diff options
Diffstat (limited to 'drivers/acpi/dock.c')
-rw-r--r-- | drivers/acpi/dock.c | 398 |
1 files changed, 125 insertions, 273 deletions
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 826560753389..b527c1bd8bb2 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -51,8 +51,6 @@ MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to " " the driver to wait for userspace to write the undock sysfs file " " before undocking"); -static struct atomic_notifier_head dock_notifier_list; - static const struct acpi_device_id dock_device_ids[] = { {"LNXDOCK", 0}, {"", 0}, @@ -63,8 +61,6 @@ struct dock_station { acpi_handle handle; unsigned long last_dock_time; u32 flags; - spinlock_t dd_lock; - struct mutex hp_lock; struct list_head dependent_devices; struct list_head sibling; @@ -91,6 +87,12 @@ struct dock_dependent_device { #define DOCK_EVENT 3 #define UNDOCK_EVENT 2 +enum dock_callback_type { + DOCK_CALL_HANDLER, + DOCK_CALL_FIXUP, + DOCK_CALL_UEVENT, +}; + /***************************************************************************** * Dock Dependent device functions * *****************************************************************************/ @@ -101,7 +103,7 @@ struct dock_dependent_device { * * Add the dependent device to the dock's dependent device list. */ -static int +static int __init add_dock_dependent_device(struct dock_station *ds, acpi_handle handle) { struct dock_dependent_device *dd; @@ -112,14 +114,21 @@ add_dock_dependent_device(struct dock_station *ds, acpi_handle handle) dd->handle = handle; INIT_LIST_HEAD(&dd->list); - - spin_lock(&ds->dd_lock); list_add_tail(&dd->list, &ds->dependent_devices); - spin_unlock(&ds->dd_lock); return 0; } +static void remove_dock_dependent_devices(struct dock_station *ds) +{ + struct dock_dependent_device *dd, *aux; + + list_for_each_entry_safe(dd, aux, &ds->dependent_devices, list) { + list_del(&dd->list); + kfree(dd); + } +} + /** * dock_init_hotplug - Initialize a hotplug device on a docking station. * @dd: Dock-dependent device. @@ -135,19 +144,16 @@ static int dock_init_hotplug(struct dock_dependent_device *dd, int ret = 0; mutex_lock(&hotplug_lock); - - if (dd->hp_context) { + if (WARN_ON(dd->hp_context)) { ret = -EEXIST; } else { dd->hp_refcount = 1; dd->hp_ops = ops; dd->hp_context = context; dd->hp_release = release; + if (init) + init(context); } - - if (!WARN_ON(ret) && init) - init(context); - mutex_unlock(&hotplug_lock); return ret; } @@ -162,27 +168,22 @@ static int dock_init_hotplug(struct dock_dependent_device *dd, */ static void dock_release_hotplug(struct dock_dependent_device *dd) { - void (*release)(void *) = NULL; - void *context = NULL; - mutex_lock(&hotplug_lock); - if (dd->hp_context && !--dd->hp_refcount) { + void (*release)(void *) = dd->hp_release; + void *context = dd->hp_context; + dd->hp_ops = NULL; - context = dd->hp_context; dd->hp_context = NULL; - release = dd->hp_release; dd->hp_release = NULL; + if (release) + release(context); } - - if (release && context) - release(context); - mutex_unlock(&hotplug_lock); } static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event, - bool uevent) + enum dock_callback_type cb_type) { acpi_notify_handler cb = NULL; bool run = false; @@ -192,8 +193,18 @@ static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event, if (dd->hp_context) { run = true; dd->hp_refcount++; - if (dd->hp_ops) - cb = uevent ? dd->hp_ops->uevent : dd->hp_ops->handler; + if (dd->hp_ops) { + switch (cb_type) { + case DOCK_CALL_FIXUP: + cb = dd->hp_ops->fixup; + break; + case DOCK_CALL_UEVENT: + cb = dd->hp_ops->uevent; + break; + default: + cb = dd->hp_ops->handler; + } + } } mutex_unlock(&hotplug_lock); @@ -220,63 +231,17 @@ find_dock_dependent_device(struct dock_station *ds, acpi_handle handle) { struct dock_dependent_device *dd; - spin_lock(&ds->dd_lock); - list_for_each_entry(dd, &ds->dependent_devices, list) { - if (handle == dd->handle) { - spin_unlock(&ds->dd_lock); + list_for_each_entry(dd, &ds->dependent_devices, list) + if (handle == dd->handle) return dd; - } - } - spin_unlock(&ds->dd_lock); + return NULL; } /***************************************************************************** * Dock functions * *****************************************************************************/ -/** - * is_dock - see if a device is a dock station - * @handle: acpi handle of the device - * - * If an acpi object has a _DCK method, then it is by definition a dock - * station, so return true. - */ -static int is_dock(acpi_handle handle) -{ - acpi_status status; - acpi_handle tmp; - - status = acpi_get_handle(handle, "_DCK", &tmp); - if (ACPI_FAILURE(status)) - return 0; - return 1; -} - -static int is_ejectable(acpi_handle handle) -{ - acpi_status status; - acpi_handle tmp; - - status = acpi_get_handle(handle, "_EJ0", &tmp); - if (ACPI_FAILURE(status)) - return 0; - return 1; -} - -static int is_ata(acpi_handle handle) -{ - acpi_handle tmp; - - if ((ACPI_SUCCESS(acpi_get_handle(handle, "_GTF", &tmp))) || - (ACPI_SUCCESS(acpi_get_handle(handle, "_GTM", &tmp))) || - (ACPI_SUCCESS(acpi_get_handle(handle, "_STM", &tmp))) || - (ACPI_SUCCESS(acpi_get_handle(handle, "_SDD", &tmp)))) - return 1; - - return 0; -} - -static int is_battery(acpi_handle handle) +static int __init is_battery(acpi_handle handle) { struct acpi_device_info *info; int ret = 1; @@ -292,17 +257,13 @@ static int is_battery(acpi_handle handle) return ret; } -static int is_ejectable_bay(acpi_handle handle) +/* Check whether ACPI object is an ejectable battery or disk bay */ +static bool __init is_ejectable_bay(acpi_handle handle) { - acpi_handle phandle; + if (acpi_has_method(handle, "_EJ0") && is_battery(handle)) + return true; - if (!is_ejectable(handle)) - return 0; - if (is_battery(handle) || is_ata(handle)) - return 1; - if (!acpi_get_parent(handle, &phandle) && is_ata(phandle)) - return 1; - return 0; + return acpi_bay_match(handle); } /** @@ -320,7 +281,7 @@ int is_dock_device(acpi_handle handle) if (!dock_station_count) return 0; - if (is_dock(handle)) + if (acpi_dock_match(handle)) return 1; list_for_each_entry(dock_station, &dock_stations, sibling) @@ -359,10 +320,8 @@ static int dock_present(struct dock_station *ds) * handle if one does not exist already. This should cause * acpi to scan for drivers for the given devices, and call * matching driver's add routine. - * - * Returns a pointer to the acpi_device corresponding to the handle. */ -static struct acpi_device * dock_create_acpi_device(acpi_handle handle) +static void dock_create_acpi_device(acpi_handle handle) { struct acpi_device *device; int ret; @@ -375,10 +334,7 @@ static struct acpi_device * dock_create_acpi_device(acpi_handle handle) ret = acpi_bus_scan(handle); if (ret) pr_debug("error adding bus, %x\n", -ret); - - acpi_bus_get_device(handle, &device); } - return device; } /** @@ -397,9 +353,29 @@ static void dock_remove_acpi_device(acpi_handle handle) } /** - * hotplug_dock_devices - insert or remove devices on the dock station + * hot_remove_dock_devices - Remove dock station devices. + * @ds: Dock station. + */ +static void hot_remove_dock_devices(struct dock_station *ds) +{ + struct dock_dependent_device *dd; + + /* + * Walk the list in reverse order so that devices that have been added + * last are removed first (in case there are some indirect dependencies + * between them). + */ + list_for_each_entry_reverse(dd, &ds->dependent_devices, list) + dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST, false); + + list_for_each_entry_reverse(dd, &ds->dependent_devices, list) + dock_remove_acpi_device(dd->handle); +} + +/** + * hotplug_dock_devices - Insert devices on a dock station. * @ds: the dock station - * @event: either bus check or eject request + * @event: either bus check or device check request * * Some devices on the dock station need to have drivers called * to perform hotplug operations after a dock event has occurred. @@ -410,27 +386,21 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event) { struct dock_dependent_device *dd; - mutex_lock(&ds->hp_lock); + /* Call driver specific post-dock fixups. */ + list_for_each_entry(dd, &ds->dependent_devices, list) + dock_hotplug_event(dd, event, DOCK_CALL_FIXUP); - /* - * First call driver specific hotplug functions - */ + /* Call driver specific hotplug functions. */ list_for_each_entry(dd, &ds->dependent_devices, list) - dock_hotplug_event(dd, event, false); + dock_hotplug_event(dd, event, DOCK_CALL_HANDLER); /* - * Now make sure that an acpi_device is created for each - * dependent device, or removed if this is an eject request. - * This will cause acpi_drivers to be stopped/started if they - * exist + * Now make sure that an acpi_device is created for each dependent + * device. That will cause scan handlers to be attached to device + * objects or acpi_drivers to be stopped/started if they are present. */ - list_for_each_entry(dd, &ds->dependent_devices, list) { - if (event == ACPI_NOTIFY_EJECT_REQUEST) - dock_remove_acpi_device(dd->handle); - else - dock_create_acpi_device(dd->handle); - } - mutex_unlock(&ds->hp_lock); + list_for_each_entry(dd, &ds->dependent_devices, list) + dock_create_acpi_device(dd->handle); } static void dock_event(struct dock_station *ds, u32 event, int num) @@ -453,44 +423,13 @@ static void dock_event(struct dock_station *ds, u32 event, int num) kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); list_for_each_entry(dd, &ds->dependent_devices, list) - dock_hotplug_event(dd, event, true); + dock_hotplug_event(dd, event, DOCK_CALL_UEVENT); if (num != DOCK_EVENT) kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); } /** - * eject_dock - respond to a dock eject request - * @ds: the dock station - * - * This is called after _DCK is called, to execute the dock station's - * _EJ0 method. - */ -static void eject_dock(struct dock_station *ds) -{ - struct acpi_object_list arg_list; - union acpi_object arg; - acpi_status status; - acpi_handle tmp; - - /* all dock devices should have _EJ0, but check anyway */ - status = acpi_get_handle(ds->handle, "_EJ0", &tmp); - if (ACPI_FAILURE(status)) { - pr_debug("No _EJ0 support for dock device\n"); - return; - } - - arg_list.count = 1; - arg_list.pointer = &arg; - arg.type = ACPI_TYPE_INTEGER; - arg.integer.value = 1; - - status = acpi_evaluate_object(ds->handle, "_EJ0", &arg_list, NULL); - if (ACPI_FAILURE(status)) - pr_debug("Failed to evaluate _EJ0!\n"); -} - -/** * handle_dock - handle a dock event * @ds: the dock station * @dock: to dock, or undock - that is the question @@ -550,27 +489,6 @@ static inline void complete_undock(struct dock_station *ds) ds->flags &= ~(DOCK_UNDOCKING); } -static void dock_lock(struct dock_station *ds, int lock) -{ - struct acpi_object_list arg_list; - union acpi_object arg; - acpi_status status; - - arg_list.count = 1; - arg_list.pointer = &arg; - arg.type = ACPI_TYPE_INTEGER; - arg.integer.value = !!lock; - status = acpi_evaluate_object(ds->handle, "_LCK", &arg_list, NULL); - if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { - if (lock) - acpi_handle_warn(ds->handle, - "Locking device failed (0x%x)\n", status); - else - acpi_handle_warn(ds->handle, - "Unlocking device failed (0x%x)\n", status); - } -} - /** * dock_in_progress - see if we are in the middle of handling a dock event * @ds: the dock station @@ -588,37 +506,6 @@ static int dock_in_progress(struct dock_station *ds) } /** - * register_dock_notifier - add yourself to the dock notifier list - * @nb: the callers notifier block - * - * If a driver wishes to be notified about dock events, they can - * use this function to put a notifier block on the dock notifier list. - * this notifier call chain will be called after a dock event, but - * before hotplugging any new devices. - */ -int register_dock_notifier(struct notifier_block *nb) -{ - if (!dock_station_count) - return -ENODEV; - - return atomic_notifier_chain_register(&dock_notifier_list, nb); -} -EXPORT_SYMBOL_GPL(register_dock_notifier); - -/** - * unregister_dock_notifier - remove yourself from the dock notifier list - * @nb: the callers notifier block - */ -void unregister_dock_notifier(struct notifier_block *nb) -{ - if (!dock_station_count) - return; - - atomic_notifier_chain_unregister(&dock_notifier_list, nb); -} -EXPORT_SYMBOL_GPL(unregister_dock_notifier); - -/** * register_hotplug_dock_device - register a hotplug function * @handle: the handle of the device * @ops: handlers to call after docking @@ -703,10 +590,10 @@ static int handle_eject_request(struct dock_station *ds, u32 event) */ dock_event(ds, event, UNDOCK_EVENT); - hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST); + hot_remove_dock_devices(ds); undock(ds); - dock_lock(ds, 0); - eject_dock(ds); + acpi_evaluate_lck(ds->handle, 0); + acpi_evaluate_ej0(ds->handle); if (dock_present(ds)) { acpi_handle_err(ds->handle, "Unable to undock!\n"); return -EBUSY; @@ -717,18 +604,17 @@ static int handle_eject_request(struct dock_station *ds, u32 event) /** * dock_notify - act upon an acpi dock notification - * @handle: the dock station handle + * @ds: dock station * @event: the acpi event - * @data: our driver data struct * * 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, * and then hotplug and devices that may need hotplugging. */ -static void dock_notify(acpi_handle handle, u32 event, void *data) +static void dock_notify(struct dock_station *ds, u32 event) { - struct dock_station *ds = data; - struct acpi_device *tmp; + acpi_handle handle = ds->handle; + struct acpi_device *ad; int surprise_removal = 0; /* @@ -751,8 +637,7 @@ static void dock_notify(acpi_handle handle, u32 event, void *data) switch (event) { case ACPI_NOTIFY_BUS_CHECK: case ACPI_NOTIFY_DEVICE_CHECK: - if (!dock_in_progress(ds) && acpi_bus_get_device(ds->handle, - &tmp)) { + if (!dock_in_progress(ds) && acpi_bus_get_device(handle, &ad)) { begin_dock(ds); dock(ds); if (!dock_present(ds)) { @@ -760,12 +645,10 @@ static void dock_notify(acpi_handle handle, u32 event, void *data) complete_dock(ds); break; } - atomic_notifier_call_chain(&dock_notifier_list, - event, NULL); hotplug_dock_devices(ds, event); complete_dock(ds); dock_event(ds, event, DOCK_EVENT); - dock_lock(ds, 1); + acpi_evaluate_lck(ds->handle, 1); acpi_update_all_gpes(); break; } @@ -789,9 +672,8 @@ static void dock_notify(acpi_handle handle, u32 event, void *data) } struct dock_data { - acpi_handle handle; - unsigned long event; struct dock_station *ds; + u32 event; }; static void acpi_dock_deferred_cb(void *context) @@ -799,52 +681,31 @@ static void acpi_dock_deferred_cb(void *context) struct dock_data *data = context; acpi_scan_lock_acquire(); - dock_notify(data->handle, data->event, data->ds); + dock_notify(data->ds, data->event); acpi_scan_lock_release(); kfree(data); } -static int acpi_dock_notifier_call(struct notifier_block *this, - unsigned long event, void *data) +static void dock_notify_handler(acpi_handle handle, u32 event, void *data) { - struct dock_station *dock_station; - acpi_handle handle = data; + struct dock_data *dd; if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK && event != ACPI_NOTIFY_EJECT_REQUEST) - return 0; - - acpi_scan_lock_acquire(); - - list_for_each_entry(dock_station, &dock_stations, sibling) { - if (dock_station->handle == handle) { - struct dock_data *dd; - acpi_status status; - - dd = kmalloc(sizeof(*dd), GFP_KERNEL); - if (!dd) - break; + return; - dd->handle = handle; - dd->event = event; - dd->ds = dock_station; - status = acpi_os_hotplug_execute(acpi_dock_deferred_cb, - dd); - if (ACPI_FAILURE(status)) - kfree(dd); + dd = kmalloc(sizeof(*dd), GFP_KERNEL); + if (dd) { + acpi_status status; - break; - } + dd->ds = data; + dd->event = event; + status = acpi_os_hotplug_execute(acpi_dock_deferred_cb, dd); + if (ACPI_FAILURE(status)) + kfree(dd); } - - acpi_scan_lock_release(); - return 0; } -static struct notifier_block dock_acpi_notifier = { - .notifier_call = acpi_dock_notifier_call, -}; - /** * find_dock_devices - find devices on the dock station * @handle: the handle of the device we are examining @@ -856,29 +717,16 @@ static struct notifier_block dock_acpi_notifier = { * 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 -find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv) +static acpi_status __init find_dock_devices(acpi_handle handle, u32 lvl, + void *context, void **rv) { - acpi_status status; - acpi_handle tmp, parent; struct dock_station *ds = context; + acpi_handle ejd = NULL; - status = acpi_bus_get_ejd(handle, &tmp); - if (ACPI_FAILURE(status)) { - /* try the parent device as well */ - status = acpi_get_parent(handle, &parent); - if (ACPI_FAILURE(status)) - goto fdd_out; - /* see if parent is dependent on dock */ - status = acpi_bus_get_ejd(parent, &tmp); - if (ACPI_FAILURE(status)) - goto fdd_out; - } - - if (tmp == ds->handle) + acpi_bus_get_ejd(handle, &ejd); + if (ejd == ds->handle) add_dock_dependent_device(ds, handle); -fdd_out: return AE_OK; } @@ -988,13 +836,13 @@ static struct attribute_group dock_attribute_group = { */ static int __init dock_add(acpi_handle handle) { - int ret, id; - struct dock_station ds, *dock_station; + struct dock_station *dock_station, ds = { NULL, }; struct platform_device *dd; + acpi_status status; + int ret; - id = dock_station_count; - memset(&ds, 0, sizeof(ds)); - dd = platform_device_register_data(NULL, "dock", id, &ds, sizeof(ds)); + dd = platform_device_register_data(NULL, "dock", dock_station_count, + &ds, sizeof(ds)); if (IS_ERR(dd)) return PTR_ERR(dd); @@ -1004,18 +852,15 @@ static int __init dock_add(acpi_handle handle) dock_station->dock_device = dd; dock_station->last_dock_time = jiffies - HZ; - mutex_init(&dock_station->hp_lock); - spin_lock_init(&dock_station->dd_lock); INIT_LIST_HEAD(&dock_station->sibling); - ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); INIT_LIST_HEAD(&dock_station->dependent_devices); /* we want the dock device to send uevents */ dev_set_uevent_suppress(&dd->dev, 0); - if (is_dock(handle)) + if (acpi_dock_match(handle)) dock_station->flags |= DOCK_IS_DOCK; - if (is_ata(handle)) + if (acpi_ata_match(handle)) dock_station->flags |= DOCK_IS_ATA; if (is_battery(handle)) dock_station->flags |= DOCK_IS_BAT; @@ -1034,11 +879,19 @@ static int __init dock_add(acpi_handle handle) if (ret) 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++; list_add(&dock_station->sibling, &dock_stations); return 0; err_rmgroup: + remove_dock_dependent_devices(dock_station); sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group); err_unregister: platform_device_unregister(dd); @@ -1058,7 +911,7 @@ err_unregister: static __init acpi_status find_dock_and_bay(acpi_handle handle, u32 lvl, void *context, void **rv) { - if (is_dock(handle) || is_ejectable_bay(handle)) + if (acpi_dock_match(handle) || is_ejectable_bay(handle)) dock_add(handle); return AE_OK; @@ -1078,7 +931,6 @@ void __init acpi_dock_init(void) return; } - register_acpi_bus_notifier(&dock_acpi_notifier); pr_info(PREFIX "%s: %d docks/bays found\n", ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count); } |