mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
ACPI / dock: Dispatch dock notifications from the global notify handler
The ACPI dock station code carries out an extra namespace scan before the main one in order to find and register all of the dock device objects. Then, it registers a notify handler for each of them for handling dock events. However, dock device objects need not be scanned for upfront. They very well can be enumerated and registered during the first phase of the main namespace scan, before attaching scan handlers and ACPI drivers to ACPI device objects. Then, the dependent devices can be added to the in the second phase. That makes it possible to drop the extra namespace scan, so do it. Moreover, it is not necessary to register notify handlers for all of the dock stations' namespace nodes, becuase notifications may be dispatched from the global notify handler for them. Do that and drop two functions used for dock notify handling, acpi_dock_deferred_cb() and dock_notify_handler(), that aren't necessary any more. Finally, some dock station objects have _HID objects matching the ACPI container scan handler which causes it to claim those objects and try to handle their hotplug, but that is not a good idea, because those objects have their own special hotplug handling anyway. For this reason, the hotplug_notify flag should not be set for ACPI device objects representing dock stations and the container scan handler should be made ignore those objects, so make that happen. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
parent
d6a40224a3
commit
1e2380cd14
5 changed files with 91 additions and 135 deletions
|
@ -68,6 +68,9 @@ static int container_device_attach(struct acpi_device *adev,
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (adev->flags.is_dock_station)
|
||||||
|
return 0;
|
||||||
|
|
||||||
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
|
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
|
||||||
if (!cdev)
|
if (!cdev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
|
@ -103,8 +103,7 @@ enum dock_callback_type {
|
||||||
*
|
*
|
||||||
* Add the dependent device to the dock's dependent device list.
|
* Add the dependent device to the dock's dependent device list.
|
||||||
*/
|
*/
|
||||||
static int __init
|
static int add_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
|
||||||
add_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
|
|
||||||
{
|
{
|
||||||
struct dock_dependent_device *dd;
|
struct dock_dependent_device *dd;
|
||||||
|
|
||||||
|
@ -218,6 +217,17 @@ static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event,
|
||||||
dock_release_hotplug(dd);
|
dock_release_hotplug(dd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct dock_station *find_dock_station(acpi_handle handle)
|
||||||
|
{
|
||||||
|
struct dock_station *ds;
|
||||||
|
|
||||||
|
list_for_each_entry(ds, &dock_stations, sibling)
|
||||||
|
if (ds->handle == handle)
|
||||||
|
return ds;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* find_dock_dependent_device - get a device dependent on this dock
|
* find_dock_dependent_device - get a device dependent on this dock
|
||||||
* @ds: the dock station
|
* @ds: the dock station
|
||||||
|
@ -238,33 +248,19 @@ find_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void register_dock_dependent_device(struct acpi_device *adev,
|
||||||
|
acpi_handle dshandle)
|
||||||
|
{
|
||||||
|
struct dock_station *ds = find_dock_station(dshandle);
|
||||||
|
acpi_handle handle = adev->handle;
|
||||||
|
|
||||||
|
if (ds && !find_dock_dependent_device(ds, handle))
|
||||||
|
add_dock_dependent_device(ds, handle);
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Dock functions *
|
* Dock functions *
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
static int __init is_battery(acpi_handle handle)
|
|
||||||
{
|
|
||||||
struct acpi_device_info *info;
|
|
||||||
int ret = 1;
|
|
||||||
|
|
||||||
if (!ACPI_SUCCESS(acpi_get_object_info(handle, &info)))
|
|
||||||
return 0;
|
|
||||||
if (!(info->valid & ACPI_VALID_HID))
|
|
||||||
ret = 0;
|
|
||||||
else
|
|
||||||
ret = !strcmp("PNP0C0A", info->hardware_id.string);
|
|
||||||
|
|
||||||
kfree(info);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check whether ACPI object is an ejectable battery or disk bay */
|
|
||||||
static bool __init is_ejectable_bay(acpi_handle handle)
|
|
||||||
{
|
|
||||||
if (acpi_has_method(handle, "_EJ0") && is_battery(handle))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return acpi_bay_match(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* is_dock_device - see if a device is on a dock station
|
* is_dock_device - see if a device is on a dock station
|
||||||
|
@ -598,20 +594,23 @@ static int handle_eject_request(struct dock_station *ds, u32 event)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* dock_notify - act upon an acpi dock notification
|
* dock_notify - Handle ACPI dock notification.
|
||||||
* @ds: dock station
|
* @adev: Dock station's ACPI device object.
|
||||||
* @event: the acpi event
|
* @event: Event code.
|
||||||
*
|
*
|
||||||
* If we are notified to dock, then check to see if the dock is
|
* If we are notified to dock, then check to see if the dock is
|
||||||
* present and then dock. Notify all drivers of the dock event,
|
* present and then dock. Notify all drivers of the dock event,
|
||||||
* and then hotplug and devices that may need hotplugging.
|
* and then hotplug and devices that may need hotplugging.
|
||||||
*/
|
*/
|
||||||
static void dock_notify(struct dock_station *ds, u32 event)
|
int dock_notify(struct acpi_device *adev, u32 event)
|
||||||
{
|
{
|
||||||
acpi_handle handle = ds->handle;
|
acpi_handle handle = adev->handle;
|
||||||
struct acpi_device *adev = NULL;
|
struct dock_station *ds = find_dock_station(handle);
|
||||||
int surprise_removal = 0;
|
int surprise_removal = 0;
|
||||||
|
|
||||||
|
if (!ds)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* According to acpi spec 3.0a, if a DEVICE_CHECK notification
|
* According to acpi spec 3.0a, if a DEVICE_CHECK notification
|
||||||
* is sent and _DCK is present, it is assumed to mean an undock
|
* is sent and _DCK is present, it is assumed to mean an undock
|
||||||
|
@ -632,7 +631,6 @@ static void dock_notify(struct dock_station *ds, u32 event)
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case ACPI_NOTIFY_BUS_CHECK:
|
case ACPI_NOTIFY_BUS_CHECK:
|
||||||
case ACPI_NOTIFY_DEVICE_CHECK:
|
case ACPI_NOTIFY_DEVICE_CHECK:
|
||||||
acpi_bus_get_device(handle, &adev);
|
|
||||||
if (!dock_in_progress(ds) && !acpi_device_enumerated(adev)) {
|
if (!dock_in_progress(ds) && !acpi_device_enumerated(adev)) {
|
||||||
begin_dock(ds);
|
begin_dock(ds);
|
||||||
dock(ds);
|
dock(ds);
|
||||||
|
@ -662,49 +660,8 @@ static void dock_notify(struct dock_station *ds, u32 event)
|
||||||
else
|
else
|
||||||
dock_event(ds, event, UNDOCK_EVENT);
|
dock_event(ds, event, UNDOCK_EVENT);
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
acpi_handle_err(handle, "Unknown dock event %d\n", event);
|
|
||||||
}
|
}
|
||||||
}
|
return 0;
|
||||||
|
|
||||||
static void acpi_dock_deferred_cb(void *data, u32 event)
|
|
||||||
{
|
|
||||||
acpi_scan_lock_acquire();
|
|
||||||
dock_notify(data, event);
|
|
||||||
acpi_scan_lock_release();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dock_notify_handler(acpi_handle handle, u32 event, void *data)
|
|
||||||
{
|
|
||||||
if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK
|
|
||||||
&& event != ACPI_NOTIFY_EJECT_REQUEST)
|
|
||||||
return;
|
|
||||||
|
|
||||||
acpi_hotplug_execute(acpi_dock_deferred_cb, data, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* find_dock_devices - find devices on the dock station
|
|
||||||
* @handle: the handle of the device we are examining
|
|
||||||
* @lvl: unused
|
|
||||||
* @context: the dock station private data
|
|
||||||
* @rv: unused
|
|
||||||
*
|
|
||||||
* This function is called by acpi_walk_namespace. It will
|
|
||||||
* check to see if an object has an _EJD method. If it does, then it
|
|
||||||
* will see if it is dependent on the dock station.
|
|
||||||
*/
|
|
||||||
static acpi_status __init find_dock_devices(acpi_handle handle, u32 lvl,
|
|
||||||
void *context, void **rv)
|
|
||||||
{
|
|
||||||
struct dock_station *ds = context;
|
|
||||||
acpi_handle ejd = NULL;
|
|
||||||
|
|
||||||
acpi_bus_get_ejd(handle, &ejd);
|
|
||||||
if (ejd == ds->handle)
|
|
||||||
add_dock_dependent_device(ds, handle);
|
|
||||||
|
|
||||||
return AE_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -803,23 +760,22 @@ static struct attribute_group dock_attribute_group = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* dock_add - add a new dock station
|
* acpi_dock_add - Add a new dock station
|
||||||
* @handle: the dock station handle
|
* @adev: Dock station ACPI device object.
|
||||||
*
|
*
|
||||||
* allocated and initialize a new dock station device. Find all devices
|
* allocated and initialize a new dock station device.
|
||||||
* that are on the dock station, and register for dock event notifications.
|
|
||||||
*/
|
*/
|
||||||
static int __init dock_add(acpi_handle handle)
|
void acpi_dock_add(struct acpi_device *adev)
|
||||||
{
|
{
|
||||||
struct dock_station *dock_station, ds = { NULL, };
|
struct dock_station *dock_station, ds = { NULL, };
|
||||||
|
acpi_handle handle = adev->handle;
|
||||||
struct platform_device *dd;
|
struct platform_device *dd;
|
||||||
acpi_status status;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
dd = platform_device_register_data(NULL, "dock", dock_station_count,
|
dd = platform_device_register_data(NULL, "dock", dock_station_count,
|
||||||
&ds, sizeof(ds));
|
&ds, sizeof(ds));
|
||||||
if (IS_ERR(dd))
|
if (IS_ERR(dd))
|
||||||
return PTR_ERR(dd);
|
return;
|
||||||
|
|
||||||
dock_station = dd->dev.platform_data;
|
dock_station = dd->dev.platform_data;
|
||||||
|
|
||||||
|
@ -837,33 +793,24 @@ static int __init dock_add(acpi_handle handle)
|
||||||
dock_station->flags |= DOCK_IS_DOCK;
|
dock_station->flags |= DOCK_IS_DOCK;
|
||||||
if (acpi_ata_match(handle))
|
if (acpi_ata_match(handle))
|
||||||
dock_station->flags |= DOCK_IS_ATA;
|
dock_station->flags |= DOCK_IS_ATA;
|
||||||
if (is_battery(handle))
|
if (acpi_device_is_battery(handle))
|
||||||
dock_station->flags |= DOCK_IS_BAT;
|
dock_station->flags |= DOCK_IS_BAT;
|
||||||
|
|
||||||
ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group);
|
ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_unregister;
|
goto err_unregister;
|
||||||
|
|
||||||
/* Find dependent devices */
|
|
||||||
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
|
||||||
ACPI_UINT32_MAX, find_dock_devices, NULL,
|
|
||||||
dock_station, NULL);
|
|
||||||
|
|
||||||
/* add the dock station as a device dependent on itself */
|
/* add the dock station as a device dependent on itself */
|
||||||
ret = add_dock_dependent_device(dock_station, handle);
|
ret = add_dock_dependent_device(dock_station, handle);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_rmgroup;
|
goto err_rmgroup;
|
||||||
|
|
||||||
status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
|
|
||||||
dock_notify_handler, dock_station);
|
|
||||||
if (ACPI_FAILURE(status)) {
|
|
||||||
ret = -ENODEV;
|
|
||||||
goto err_rmgroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
dock_station_count++;
|
dock_station_count++;
|
||||||
list_add(&dock_station->sibling, &dock_stations);
|
list_add(&dock_station->sibling, &dock_stations);
|
||||||
return 0;
|
adev->flags.is_dock_station = true;
|
||||||
|
dev_info(&adev->dev, "ACPI dock station (docks/bays count: %d)\n",
|
||||||
|
dock_station_count);
|
||||||
|
return;
|
||||||
|
|
||||||
err_rmgroup:
|
err_rmgroup:
|
||||||
remove_dock_dependent_devices(dock_station);
|
remove_dock_dependent_devices(dock_station);
|
||||||
|
@ -871,38 +818,4 @@ static int __init dock_add(acpi_handle handle)
|
||||||
err_unregister:
|
err_unregister:
|
||||||
platform_device_unregister(dd);
|
platform_device_unregister(dd);
|
||||||
acpi_handle_err(handle, "%s encountered error %d\n", __func__, ret);
|
acpi_handle_err(handle, "%s encountered error %d\n", __func__, ret);
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* find_dock_and_bay - look for dock stations and bays
|
|
||||||
* @handle: acpi handle of a device
|
|
||||||
* @lvl: unused
|
|
||||||
* @context: unused
|
|
||||||
* @rv: unused
|
|
||||||
*
|
|
||||||
* This is called by acpi_walk_namespace to look for dock stations and bays.
|
|
||||||
*/
|
|
||||||
static acpi_status __init
|
|
||||||
find_dock_and_bay(acpi_handle handle, u32 lvl, void *context, void **rv)
|
|
||||||
{
|
|
||||||
if (acpi_dock_match(handle) || is_ejectable_bay(handle))
|
|
||||||
dock_add(handle);
|
|
||||||
|
|
||||||
return AE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void __init acpi_dock_init(void)
|
|
||||||
{
|
|
||||||
/* look for dock stations and bays */
|
|
||||||
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
|
||||||
ACPI_UINT32_MAX, find_dock_and_bay, NULL, NULL, NULL);
|
|
||||||
|
|
||||||
if (!dock_station_count) {
|
|
||||||
pr_info(PREFIX "No dock devices found.\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_info(PREFIX "%s: %d docks/bays found\n",
|
|
||||||
ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,9 +37,15 @@ void acpi_container_init(void);
|
||||||
static inline void acpi_container_init(void) {}
|
static inline void acpi_container_init(void) {}
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_ACPI_DOCK
|
#ifdef CONFIG_ACPI_DOCK
|
||||||
void acpi_dock_init(void);
|
void register_dock_dependent_device(struct acpi_device *adev,
|
||||||
|
acpi_handle dshandle);
|
||||||
|
int dock_notify(struct acpi_device *adev, u32 event);
|
||||||
|
void acpi_dock_add(struct acpi_device *adev);
|
||||||
#else
|
#else
|
||||||
static inline void acpi_dock_init(void) {}
|
static inline void register_dock_dependent_device(struct acpi_device *adev,
|
||||||
|
acpi_handle dshandle) {}
|
||||||
|
static inline int dock_notify(struct acpi_device *adev, u32 event) { return -ENODEV; }
|
||||||
|
static inline void acpi_dock_add(struct acpi_device *adev) {}
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_ACPI_HOTPLUG_MEMORY
|
#ifdef CONFIG_ACPI_HOTPLUG_MEMORY
|
||||||
void acpi_memory_hotplug_init(void);
|
void acpi_memory_hotplug_init(void);
|
||||||
|
@ -91,6 +97,7 @@ void acpi_free_pnp_ids(struct acpi_device_pnp *pnp);
|
||||||
int acpi_bind_one(struct device *dev, struct acpi_device *adev);
|
int acpi_bind_one(struct device *dev, struct acpi_device *adev);
|
||||||
int acpi_unbind_one(struct device *dev);
|
int acpi_unbind_one(struct device *dev);
|
||||||
bool acpi_device_is_present(struct acpi_device *adev);
|
bool acpi_device_is_present(struct acpi_device *adev);
|
||||||
|
bool acpi_device_is_battery(acpi_handle handle);
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------
|
||||||
Power Resource
|
Power Resource
|
||||||
|
|
|
@ -487,7 +487,9 @@ void acpi_device_hotplug(void *data, u32 src)
|
||||||
if (adev->handle == INVALID_ACPI_HANDLE)
|
if (adev->handle == INVALID_ACPI_HANDLE)
|
||||||
goto err_out;
|
goto err_out;
|
||||||
|
|
||||||
if (adev->flags.hotplug_notify) {
|
if (adev->flags.is_dock_station) {
|
||||||
|
error = dock_notify(adev, src);
|
||||||
|
} else if (adev->flags.hotplug_notify) {
|
||||||
error = acpi_generic_hotplug_event(adev, src);
|
error = acpi_generic_hotplug_event(adev, src);
|
||||||
if (error == -EPERM) {
|
if (error == -EPERM) {
|
||||||
ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
|
ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
|
||||||
|
@ -1660,6 +1662,29 @@ bool acpi_bay_match(acpi_handle handle)
|
||||||
return acpi_ata_match(phandle);
|
return acpi_ata_match(phandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool acpi_device_is_battery(acpi_handle handle)
|
||||||
|
{
|
||||||
|
struct acpi_device_info *info;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
if (!ACPI_SUCCESS(acpi_get_object_info(handle, &info)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (info->valid & ACPI_VALID_HID)
|
||||||
|
ret = !strcmp("PNP0C0A", info->hardware_id.string);
|
||||||
|
|
||||||
|
kfree(info);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_ejectable_bay(acpi_handle handle)
|
||||||
|
{
|
||||||
|
if (acpi_has_method(handle, "_EJ0") && acpi_device_is_battery(handle))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return acpi_bay_match(handle);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* acpi_dock_match - see if an acpi object has a _DCK method
|
* acpi_dock_match - see if an acpi object has a _DCK method
|
||||||
*/
|
*/
|
||||||
|
@ -1964,6 +1989,10 @@ static void acpi_scan_init_hotplug(struct acpi_device *adev)
|
||||||
{
|
{
|
||||||
struct acpi_hardware_id *hwid;
|
struct acpi_hardware_id *hwid;
|
||||||
|
|
||||||
|
if (acpi_dock_match(adev->handle) || is_ejectable_bay(adev->handle)) {
|
||||||
|
acpi_dock_add(adev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
list_for_each_entry(hwid, &adev->pnp.ids, list) {
|
list_for_each_entry(hwid, &adev->pnp.ids, list) {
|
||||||
struct acpi_scan_handler *handler;
|
struct acpi_scan_handler *handler;
|
||||||
|
|
||||||
|
@ -2035,8 +2064,12 @@ static int acpi_scan_attach_handler(struct acpi_device *device)
|
||||||
static void acpi_bus_attach(struct acpi_device *device)
|
static void acpi_bus_attach(struct acpi_device *device)
|
||||||
{
|
{
|
||||||
struct acpi_device *child;
|
struct acpi_device *child;
|
||||||
|
acpi_handle ejd;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (ACPI_SUCCESS(acpi_bus_get_ejd(device->handle, &ejd)))
|
||||||
|
register_dock_dependent_device(device, ejd);
|
||||||
|
|
||||||
acpi_bus_get_status(device);
|
acpi_bus_get_status(device);
|
||||||
/* Skip devices that are not present. */
|
/* Skip devices that are not present. */
|
||||||
if (!acpi_device_is_present(device)) {
|
if (!acpi_device_is_present(device)) {
|
||||||
|
@ -2189,7 +2222,6 @@ int __init acpi_scan_init(void)
|
||||||
acpi_cmos_rtc_init();
|
acpi_cmos_rtc_init();
|
||||||
acpi_container_init();
|
acpi_container_init();
|
||||||
acpi_memory_hotplug_init();
|
acpi_memory_hotplug_init();
|
||||||
acpi_dock_init();
|
|
||||||
|
|
||||||
mutex_lock(&acpi_scan_lock);
|
mutex_lock(&acpi_scan_lock);
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -201,7 +201,8 @@ struct acpi_device_flags {
|
||||||
u32 visited:1;
|
u32 visited:1;
|
||||||
u32 no_hotplug:1;
|
u32 no_hotplug:1;
|
||||||
u32 hotplug_notify:1;
|
u32 hotplug_notify:1;
|
||||||
u32 reserved:23;
|
u32 is_dock_station:1;
|
||||||
|
u32 reserved:22;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* File System */
|
/* File System */
|
||||||
|
|
Loading…
Reference in a new issue