diff options
Diffstat (limited to 'drivers/platform/x86/wmi.c')
-rw-r--r-- | drivers/platform/x86/wmi.c | 464 |
1 files changed, 200 insertions, 264 deletions
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 1920e115da89..e46453750d5f 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -22,7 +22,6 @@ #include <linux/device.h> #include <linux/init.h> #include <linux/kernel.h> -#include <linux/list.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/rwsem.h> @@ -37,8 +36,6 @@ MODULE_AUTHOR("Carlos Corbacho"); MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); MODULE_LICENSE("GPL"); -static LIST_HEAD(wmi_block_list); - struct guid_block { guid_t guid; union { @@ -63,7 +60,6 @@ enum { /* wmi_block flags */ struct wmi_block { struct wmi_device dev; - struct list_head list; struct guid_block gblock; struct acpi_device *acpi_device; struct rw_semaphore notify_lock; /* Protects notify callback add/remove */ @@ -73,6 +69,10 @@ struct wmi_block { unsigned long flags; }; +struct wmi_guid_count_context { + const guid_t *guid; + int count; +}; /* * If the GUID data block is marked as expensive, we must enable and @@ -91,7 +91,6 @@ static const struct acpi_device_id wmi_device_ids[] = { MODULE_DEVICE_TABLE(acpi, wmi_device_ids); #define dev_to_wblock(__dev) container_of_const(__dev, struct wmi_block, dev.dev) -#define dev_to_wdev(__dev) container_of_const(__dev, struct wmi_device, dev) /* * GUID parsing functions @@ -124,24 +123,6 @@ static const void *find_guid_context(struct wmi_block *wblock, return NULL; } -static acpi_status wmi_method_enable(struct wmi_block *wblock, bool enable) -{ - struct guid_block *block; - char method[5]; - acpi_status status; - acpi_handle handle; - - block = &wblock->gblock; - handle = wblock->acpi_device->handle; - - snprintf(method, 5, "WE%02X", block->notify_id); - status = acpi_execute_simple_method(handle, method, enable); - if (status == AE_NOT_FOUND) - return AE_OK; - - return status; -} - #define WMI_ACPI_METHOD_NAME_SIZE 5 static inline void get_acpi_method_name(const struct wmi_block *wblock, @@ -166,22 +147,6 @@ static inline acpi_object_type get_param_acpi_type(const struct wmi_block *wbloc return ACPI_TYPE_BUFFER; } -static acpi_status get_event_data(const struct wmi_block *wblock, struct acpi_buffer *out) -{ - union acpi_object param = { - .integer = { - .type = ACPI_TYPE_INTEGER, - .value = wblock->gblock.notify_id, - } - }; - struct acpi_object_list input = { - .count = 1, - .pointer = ¶m, - }; - - return acpi_evaluate_object(wblock->acpi_device->handle, "_WED", &input, out); -} - static int wmidev_match_guid(struct device *dev, const void *data) { struct wmi_block *wblock = dev_to_wblock(dev); @@ -199,25 +164,46 @@ static int wmidev_match_guid(struct device *dev, const void *data) return 0; } -static int wmidev_match_notify_id(struct device *dev, const void *data) +static const struct bus_type wmi_bus_type; + +static const struct device_type wmi_type_event; + +static const struct device_type wmi_type_method; + +static int wmi_device_enable(struct wmi_device *wdev, bool enable) { - struct wmi_block *wblock = dev_to_wblock(dev); - const u32 *notify_id = data; + struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); + char method[WMI_ACPI_METHOD_NAME_SIZE]; + acpi_handle handle; + acpi_status status; - /* Legacy GUID-based functions are restricted to only see - * a single WMI device for each GUID. + if (!(wblock->gblock.flags & ACPI_WMI_EXPENSIVE)) + return 0; + + if (wblock->dev.dev.type == &wmi_type_method) + return 0; + + if (wblock->dev.dev.type == &wmi_type_event) + snprintf(method, sizeof(method), "WE%02X", wblock->gblock.notify_id); + else + get_acpi_method_name(wblock, 'C', method); + + /* + * Not all WMI devices marked as expensive actually implement the + * necessary ACPI method. Ignore this missing ACPI method to match + * the behaviour of the Windows driver. */ - if (test_bit(WMI_GUID_DUPLICATED, &wblock->flags)) + status = acpi_get_handle(wblock->acpi_device->handle, method, &handle); + if (ACPI_FAILURE(status)) return 0; - if (wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *notify_id) - return 1; + status = acpi_execute_simple_method(handle, NULL, enable); + if (ACPI_FAILURE(status)) + return -EIO; return 0; } -static const struct bus_type wmi_bus_type; - static struct wmi_device *wmi_find_device_by_guid(const char *guid_string) { struct device *dev; @@ -232,17 +218,6 @@ static struct wmi_device *wmi_find_device_by_guid(const char *guid_string) if (!dev) return ERR_PTR(-ENODEV); - return dev_to_wdev(dev); -} - -static struct wmi_device *wmi_find_event_by_notify_id(const u32 notify_id) -{ - struct device *dev; - - dev = bus_find_device(&wmi_bus_type, NULL, ¬ify_id, wmidev_match_notify_id); - if (!dev) - return ERR_PTR(-ENODEV); - return to_wmi_device(dev); } @@ -382,10 +357,8 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance, { struct guid_block *block; acpi_handle handle; - acpi_status status, wc_status = AE_ERROR; struct acpi_object_list input; union acpi_object wq_params[1]; - char wc_method[WMI_ACPI_METHOD_NAME_SIZE]; char method[WMI_ACPI_METHOD_NAME_SIZE]; if (!out) @@ -409,40 +382,9 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance, if (instance == 0 && test_bit(WMI_READ_TAKES_NO_ARGS, &wblock->flags)) input.count = 0; - /* - * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to - * enable collection. - */ - if (block->flags & ACPI_WMI_EXPENSIVE) { - get_acpi_method_name(wblock, 'C', wc_method); - - /* - * Some GUIDs break the specification by declaring themselves - * expensive, but have no corresponding WCxx method. So we - * should not fail if this happens. - */ - wc_status = acpi_execute_simple_method(handle, wc_method, 1); - } - get_acpi_method_name(wblock, 'Q', method); - status = acpi_evaluate_object(handle, method, &input, out); - /* - * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if - * the WQxx method failed - we should disable collection anyway. - */ - if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) { - /* - * Ignore whether this WCxx call succeeds or not since - * the previously executed WQxx method call might have - * succeeded, and returning the failing status code - * of this call would throw away the result of the WQxx - * call, potentially leaking memory. - */ - acpi_execute_simple_method(handle, wc_method, 0); - } - - return status; + return acpi_evaluate_object(handle, method, &input, out); } /** @@ -466,9 +408,15 @@ acpi_status wmi_query_block(const char *guid_string, u8 instance, if (IS_ERR(wdev)) return AE_ERROR; + if (wmi_device_enable(wdev, true) < 0) + dev_warn(&wdev->dev, "Failed to enable device\n"); + wblock = container_of(wdev, struct wmi_block, dev); status = __query_block(wblock, instance, out); + if (wmi_device_enable(wdev, false) < 0) + dev_warn(&wdev->dev, "Failed to disable device\n"); + wmi_device_put(wdev); return status; @@ -515,7 +463,14 @@ acpi_status wmi_set_block(const char *guid_string, u8 instance, const struct acp if (IS_ERR(wdev)) return AE_ERROR; + if (wmi_device_enable(wdev, true) < 0) + dev_warn(&wdev->dev, "Failed to enable device\n"); + status = wmidev_block_set(wdev, instance, in); + + if (wmi_device_enable(wdev, false) < 0) + dev_warn(&wdev->dev, "Failed to disable device\n"); + wmi_device_put(wdev); return status; @@ -596,7 +551,7 @@ acpi_status wmi_install_notify_handler(const char *guid, wblock->handler = handler; wblock->handler_data = data; - if (ACPI_FAILURE(wmi_method_enable(wblock, true))) + if (wmi_device_enable(wdev, true) < 0) dev_warn(&wblock->dev.dev, "Failed to enable device\n"); status = AE_OK; @@ -633,7 +588,7 @@ acpi_status wmi_remove_notify_handler(const char *guid) if (!wblock->handler) { status = AE_NULL_ENTRY; } else { - if (ACPI_FAILURE(wmi_method_enable(wblock, false))) + if (wmi_device_enable(wdev, false) < 0) dev_warn(&wblock->dev.dev, "Failed to disable device\n"); wblock->handler = NULL; @@ -650,35 +605,6 @@ acpi_status wmi_remove_notify_handler(const char *guid) EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); /** - * wmi_get_event_data - Get WMI data associated with an event (deprecated) - * - * @event: Event to find - * @out: Buffer to hold event data - * - * Get extra data associated with an WMI event, the caller needs to free @out. - * - * Return: acpi_status signaling success or error. - */ -acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) -{ - struct wmi_block *wblock; - struct wmi_device *wdev; - acpi_status status; - - wdev = wmi_find_event_by_notify_id(event); - if (IS_ERR(wdev)) - return AE_NOT_FOUND; - - wblock = container_of(wdev, struct wmi_block, dev); - status = get_event_data(wblock, out); - - wmi_device_put(wdev); - - return status; -} -EXPORT_SYMBOL_GPL(wmi_get_event_data); - -/** * wmi_has_guid - Check if a GUID is available * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba * @@ -727,11 +653,6 @@ char *wmi_get_acpi_device_uid(const char *guid_string) } EXPORT_SYMBOL_GPL(wmi_get_acpi_device_uid); -static inline struct wmi_driver *drv_to_wdrv(struct device_driver *drv) -{ - return container_of(drv, struct wmi_driver, driver); -} - /* * sysfs interface */ @@ -772,11 +693,39 @@ static ssize_t expensive_show(struct device *dev, } static DEVICE_ATTR_RO(expensive); +static ssize_t driver_override_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct wmi_device *wdev = to_wmi_device(dev); + ssize_t ret; + + device_lock(dev); + ret = sysfs_emit(buf, "%s\n", wdev->driver_override); + device_unlock(dev); + + return ret; +} + +static ssize_t driver_override_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wmi_device *wdev = to_wmi_device(dev); + int ret; + + ret = driver_set_override(dev, &wdev->driver_override, buf, count); + if (ret < 0) + return ret; + + return count; +} +static DEVICE_ATTR_RW(driver_override); + static struct attribute *wmi_attrs[] = { &dev_attr_modalias.attr, &dev_attr_guid.attr, &dev_attr_instance_count.attr, &dev_attr_expensive.attr, + &dev_attr_driver_override.attr, NULL }; ATTRIBUTE_GROUPS(wmi); @@ -809,7 +758,7 @@ static DEVICE_ATTR_RO(object_id); static ssize_t setable_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct wmi_device *wdev = dev_to_wdev(dev); + struct wmi_device *wdev = to_wmi_device(dev); return sysfs_emit(buf, "%d\n", (int)wdev->setable); } @@ -845,15 +794,20 @@ static void wmi_dev_release(struct device *dev) { struct wmi_block *wblock = dev_to_wblock(dev); + kfree(wblock->dev.driver_override); kfree(wblock); } -static int wmi_dev_match(struct device *dev, struct device_driver *driver) +static int wmi_dev_match(struct device *dev, const struct device_driver *driver) { - struct wmi_driver *wmi_driver = drv_to_wdrv(driver); + const struct wmi_driver *wmi_driver = to_wmi_driver(driver); struct wmi_block *wblock = dev_to_wblock(dev); const struct wmi_device_id *id = wmi_driver->id_table; + /* When driver_override is set, only bind to the matching driver */ + if (wblock->dev.driver_override) + return !strcmp(wblock->dev.driver_override, driver->name); + if (id == NULL) return 0; @@ -867,11 +821,19 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver) return 0; } +static void wmi_dev_disable(void *data) +{ + struct device *dev = data; + + if (wmi_device_enable(to_wmi_device(dev), false) < 0) + dev_warn(dev, "Failed to disable device\n"); +} + static int wmi_dev_probe(struct device *dev) { struct wmi_block *wblock = dev_to_wblock(dev); - struct wmi_driver *wdriver = drv_to_wdrv(dev->driver); - int ret = 0; + struct wmi_driver *wdriver = to_wmi_driver(dev->driver); + int ret; /* Some older WMI drivers will break if instantiated multiple times, * so they are blocked from probing WMI devices with a duplicated GUID. @@ -890,18 +852,22 @@ static int wmi_dev_probe(struct device *dev) return -ENODEV; } - if (ACPI_FAILURE(wmi_method_enable(wblock, true))) + if (wmi_device_enable(to_wmi_device(dev), true) < 0) dev_warn(dev, "failed to enable device -- probing anyway\n"); + /* + * We have to make sure that all devres-managed resources are released first because + * some might still want to access the underlying WMI device. + */ + ret = devm_add_action_or_reset(dev, wmi_dev_disable, dev); + if (ret < 0) + return ret; + if (wdriver->probe) { - ret = wdriver->probe(dev_to_wdev(dev), + ret = wdriver->probe(to_wmi_device(dev), find_guid_context(wblock, wdriver)); - if (ret) { - if (ACPI_FAILURE(wmi_method_enable(wblock, false))) - dev_warn(dev, "Failed to disable device\n"); - + if (ret) return ret; - } } down_write(&wblock->notify_lock); @@ -914,17 +880,44 @@ static int wmi_dev_probe(struct device *dev) static void wmi_dev_remove(struct device *dev) { struct wmi_block *wblock = dev_to_wblock(dev); - struct wmi_driver *wdriver = drv_to_wdrv(dev->driver); + struct wmi_driver *wdriver = to_wmi_driver(dev->driver); down_write(&wblock->notify_lock); wblock->driver_ready = false; up_write(&wblock->notify_lock); if (wdriver->remove) - wdriver->remove(dev_to_wdev(dev)); + wdriver->remove(to_wmi_device(dev)); +} + +static void wmi_dev_shutdown(struct device *dev) +{ + struct wmi_driver *wdriver; + struct wmi_block *wblock; + + if (dev->driver) { + wdriver = to_wmi_driver(dev->driver); + wblock = dev_to_wblock(dev); + + /* + * Some machines return bogus WMI event data when disabling + * the WMI event. Because of this we must prevent the associated + * WMI driver from receiving new WMI events before disabling it. + */ + down_write(&wblock->notify_lock); + wblock->driver_ready = false; + up_write(&wblock->notify_lock); - if (ACPI_FAILURE(wmi_method_enable(wblock, false))) - dev_warn(dev, "failed to disable device\n"); + if (wdriver->shutdown) + wdriver->shutdown(to_wmi_device(dev)); + + /* + * We still need to disable the WMI device here since devres-managed resources + * like wmi_dev_disable() will not be release during shutdown. + */ + if (wmi_device_enable(to_wmi_device(dev), false) < 0) + dev_warn(dev, "Failed to disable device\n"); + } } static struct class wmi_bus_class = { @@ -938,6 +931,7 @@ static const struct bus_type wmi_bus_type = { .uevent = wmi_dev_uevent, .probe = wmi_dev_probe, .remove = wmi_dev_remove, + .shutdown = wmi_dev_shutdown, }; static const struct device_type wmi_type_event = { @@ -958,21 +952,30 @@ static const struct device_type wmi_type_data = { .release = wmi_dev_release, }; -/* - * _WDG is a static list that is only parsed at startup, - * so it's safe to count entries without extra protection. - */ +static int wmi_count_guids(struct device *dev, void *data) +{ + struct wmi_guid_count_context *context = data; + struct wmi_block *wblock = dev_to_wblock(dev); + + if (guid_equal(&wblock->gblock.guid, context->guid)) + context->count++; + + return 0; +} + static int guid_count(const guid_t *guid) { - struct wmi_block *wblock; - int count = 0; + struct wmi_guid_count_context context = { + .guid = guid, + .count = 0, + }; + int ret; - list_for_each_entry(wblock, &wmi_block_list, list) { - if (guid_equal(&wblock->gblock.guid, guid)) - count++; - } + ret = bus_for_each_dev(&wmi_bus_type, NULL, &context, wmi_count_guids); + if (ret < 0) + return ret; - return count; + return context.count; } static int wmi_create_device(struct device *wmi_bus_dev, @@ -983,7 +986,7 @@ static int wmi_create_device(struct device *wmi_bus_dev, struct acpi_device_info *info; acpi_handle method_handle; acpi_status status; - uint count; + int count; if (wblock->gblock.flags & ACPI_WMI_EVENT) { wblock->dev.dev.type = &wmi_type_event; @@ -1051,6 +1054,9 @@ static int wmi_create_device(struct device *wmi_bus_dev, wblock->dev.dev.parent = wmi_bus_dev; count = guid_count(&wblock->gblock.guid); + if (count < 0) + return count; + if (count) { dev_set_name(&wblock->dev.dev, "%pUL-%d", &wblock->gblock.guid, count); set_bit(WMI_GUID_DUPLICATED, &wblock->flags); @@ -1136,14 +1142,11 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) continue; } - list_add_tail(&wblock->list, &wmi_block_list); - retval = wmi_add_device(pdev, &wblock->dev); if (retval) { dev_err(wmi_bus_dev, "failed to register %pUL\n", &wblock->gblock.guid); - list_del(&wblock->list); put_device(&wblock->dev.dev); } } @@ -1153,58 +1156,22 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) return 0; } -/* - * WMI can have EmbeddedControl access regions. In which case, we just want to - * hand these off to the EC driver. - */ -static acpi_status -acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, - u32 bits, u64 *value, - void *handler_context, void *region_context) -{ - int result = 0; - u8 temp = 0; - - if ((address > 0xFF) || !value) - return AE_BAD_PARAMETER; - - if (function != ACPI_READ && function != ACPI_WRITE) - return AE_BAD_PARAMETER; - - if (bits != 8) - return AE_BAD_PARAMETER; - - if (function == ACPI_READ) { - result = ec_read(address, &temp); - *value = temp; - } else { - temp = 0xff & *value; - result = ec_write(address, temp); - } - - switch (result) { - case -EINVAL: - return AE_BAD_PARAMETER; - case -ENODEV: - return AE_NOT_FOUND; - case -ETIME: - return AE_TIME; - default: - return AE_OK; - } -} - static int wmi_get_notify_data(struct wmi_block *wblock, union acpi_object **obj) { struct acpi_buffer data = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object param = { + .integer = { + .type = ACPI_TYPE_INTEGER, + .value = wblock->gblock.notify_id, + } + }; + struct acpi_object_list input = { + .count = 1, + .pointer = ¶m, + }; acpi_status status; - if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags)) { - *obj = NULL; - return 0; - } - - status = get_event_data(wblock, &data); + status = acpi_evaluate_object(wblock->acpi_device->handle, "_WED", &input, &data); if (ACPI_FAILURE(status)) { dev_warn(&wblock->dev.dev, "Failed to get event data\n"); return -EIO; @@ -1217,7 +1184,7 @@ static int wmi_get_notify_data(struct wmi_block *wblock, union acpi_object **obj static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj) { - struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver); + struct wmi_driver *driver = to_wmi_driver(wblock->dev.dev.driver); if (!obj && !driver->no_notify_data) { dev_warn(&wblock->dev.dev, "Event contains no event data\n"); @@ -1231,47 +1198,40 @@ static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj) static int wmi_notify_device(struct device *dev, void *data) { struct wmi_block *wblock = dev_to_wblock(dev); - union acpi_object *obj; + union acpi_object *obj = NULL; u32 *event = data; int ret; if (!(wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *event)) return 0; - down_read(&wblock->notify_lock); - /* The WMI driver notify handler conflicts with the legacy WMI handler. - * Because of this the WMI driver notify handler takes precedence. + /* The ACPI WMI specification says that _WED should be + * evaluated every time an notification is received, even + * if no consumers are present. + * + * Some firmware implementations actually depend on this + * by using a queue for events which will fill up if the + * WMI driver core stops evaluating _WED due to missing + * WMI event consumers. */ - if (wblock->dev.dev.driver && wblock->driver_ready) { + if (!test_bit(WMI_NO_EVENT_DATA, &wblock->flags)) { ret = wmi_get_notify_data(wblock, &obj); - if (ret >= 0) { - wmi_notify_driver(wblock, obj); - kfree(obj); - } - } else { - if (wblock->handler) { - wblock->handler(*event, wblock->handler_data); - } else { - /* The ACPI WMI specification says that _WED should be - * evaluated every time an notification is received, even - * if no consumers are present. - * - * Some firmware implementations actually depend on this - * by using a queue for events which will fill up if the - * WMI driver core stops evaluating _WED due to missing - * WMI event consumers. - * - * Because of this we need this seemingly useless call to - * wmi_get_notify_data() which in turn evaluates _WED. - */ - ret = wmi_get_notify_data(wblock, &obj); - if (ret >= 0) - kfree(obj); - } - + if (ret < 0) + return -EIO; } + + down_read(&wblock->notify_lock); + + if (wblock->dev.dev.driver && wblock->driver_ready) + wmi_notify_driver(wblock, obj); + + if (wblock->handler) + wblock->handler(obj, wblock->handler_data); + up_read(&wblock->notify_lock); + kfree(obj); + acpi_bus_generate_netlink_event("wmi", acpi_dev_name(wblock->acpi_device), *event, 0); return -EBUSY; @@ -1286,9 +1246,6 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, void *context static int wmi_remove_device(struct device *dev, void *data) { - struct wmi_block *wblock = dev_to_wblock(dev); - - list_del(&wblock->list); device_unregister(dev); return 0; @@ -1308,14 +1265,6 @@ static void acpi_wmi_remove_notify_handler(void *data) acpi_remove_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY, acpi_wmi_notify_handler); } -static void acpi_wmi_remove_address_space_handler(void *data) -{ - struct acpi_device *acpi_device = data; - - acpi_remove_address_space_handler(acpi_device->handle, ACPI_ADR_SPACE_EC, - &acpi_wmi_ec_space_handler); -} - static void acpi_wmi_remove_bus_device(void *data) { struct device *wmi_bus_dev = data; @@ -1347,19 +1296,6 @@ static int acpi_wmi_probe(struct platform_device *device) dev_set_drvdata(&device->dev, wmi_bus_dev); - status = acpi_install_address_space_handler(acpi_device->handle, - ACPI_ADR_SPACE_EC, - &acpi_wmi_ec_space_handler, - NULL, NULL); - if (ACPI_FAILURE(status)) { - dev_err(&device->dev, "Error installing EC region handler\n"); - return -ENODEV; - } - error = devm_add_action_or_reset(&device->dev, acpi_wmi_remove_address_space_handler, - acpi_device); - if (error < 0) - return error; - status = acpi_install_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY, acpi_wmi_notify_handler, wmi_bus_dev); if (ACPI_FAILURE(status)) { @@ -1408,7 +1344,7 @@ static struct platform_driver acpi_wmi_driver = { .acpi_match_table = wmi_device_ids, }, .probe = acpi_wmi_probe, - .remove_new = acpi_wmi_remove, + .remove = acpi_wmi_remove, }; static int __init acpi_wmi_init(void) |