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:
Rafael J. Wysocki 2014-02-16 01:51:01 +01:00
parent d6a40224a3
commit 1e2380cd14
5 changed files with 91 additions and 135 deletions

View file

@ -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;

View file

@ -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);
} }

View file

@ -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

View file

@ -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);
/* /*

View file

@ -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 */