diff options
Diffstat (limited to 'drivers/platform/x86/wmi.c')
-rw-r--r-- | drivers/platform/x86/wmi.c | 308 |
1 files changed, 124 insertions, 184 deletions
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index b2978a04317f..104b77c87ef5 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -27,6 +27,8 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/init.h> #include <linux/types.h> @@ -44,9 +46,8 @@ MODULE_LICENSE("GPL"); #define ACPI_WMI_CLASS "wmi" -#define PREFIX "ACPI: WMI: " - static DEFINE_MUTEX(wmi_data_lock); +static LIST_HEAD(wmi_block_list); struct guid_block { char guid[16]; @@ -67,10 +68,9 @@ struct wmi_block { acpi_handle handle; wmi_notify_handler handler; void *handler_data; - struct device *dev; + struct device dev; }; -static struct wmi_block wmi_blocks; /* * If the GUID data block is marked as expensive, we must enable and @@ -110,7 +110,7 @@ static struct acpi_driver acpi_wmi_driver = { .add = acpi_wmi_add, .remove = acpi_wmi_remove, .notify = acpi_wmi_notify, - }, + }, }; /* @@ -128,30 +128,18 @@ static struct acpi_driver acpi_wmi_driver = { */ static int wmi_parse_hexbyte(const u8 *src) { - unsigned int x; /* For correct wrapping */ int h; + int value; /* high part */ - x = src[0]; - if (x - '0' <= '9' - '0') { - h = x - '0'; - } else if (x - 'a' <= 'f' - 'a') { - h = x - 'a' + 10; - } else if (x - 'A' <= 'F' - 'A') { - h = x - 'A' + 10; - } else { + h = value = hex_to_bin(src[0]); + if (value < 0) return -1; - } - h <<= 4; /* low part */ - x = src[1]; - if (x - '0' <= '9' - '0') - return h | (x - '0'); - if (x - 'a' <= 'f' - 'a') - return h | (x - 'a' + 10); - if (x - 'A' <= 'F' - 'A') - return h | (x - 'A' + 10); + value = hex_to_bin(src[1]); + if (value >= 0) + return (h << 4) | value; return -1; } @@ -232,7 +220,7 @@ static int wmi_gtoa(const char *in, char *out) for (i = 10; i <= 15; i++) out += sprintf(out, "%02X", in[i] & 0xFF); - out = '\0'; + *out = '\0'; return 0; } @@ -246,7 +234,7 @@ static bool find_guid(const char *guid_string, struct wmi_block **out) wmi_parse_guid(guid_string, tmp); wmi_swap_bytes(tmp, guid_input); - list_for_each(p, &wmi_blocks.list) { + list_for_each(p, &wmi_block_list) { wblock = list_entry(p, struct wmi_block, list); block = &wblock->gblock; @@ -487,30 +475,29 @@ const struct acpi_buffer *in) } EXPORT_SYMBOL_GPL(wmi_set_block); -static void wmi_dump_wdg(struct guid_block *g) +static void wmi_dump_wdg(const struct guid_block *g) { char guid_string[37]; wmi_gtoa(g->guid, guid_string); - printk(KERN_INFO PREFIX "%s:\n", guid_string); - printk(KERN_INFO PREFIX "\tobject_id: %c%c\n", - g->object_id[0], g->object_id[1]); - printk(KERN_INFO PREFIX "\tnotify_id: %02X\n", g->notify_id); - printk(KERN_INFO PREFIX "\treserved: %02X\n", g->reserved); - printk(KERN_INFO PREFIX "\tinstance_count: %d\n", g->instance_count); - printk(KERN_INFO PREFIX "\tflags: %#x", g->flags); + + pr_info("%s:\n", guid_string); + pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]); + pr_info("\tnotify_id: %02X\n", g->notify_id); + pr_info("\treserved: %02X\n", g->reserved); + pr_info("\tinstance_count: %d\n", g->instance_count); + pr_info("\tflags: %#x ", g->flags); if (g->flags) { - printk(" "); if (g->flags & ACPI_WMI_EXPENSIVE) - printk("ACPI_WMI_EXPENSIVE "); + pr_cont("ACPI_WMI_EXPENSIVE "); if (g->flags & ACPI_WMI_METHOD) - printk("ACPI_WMI_METHOD "); + pr_cont("ACPI_WMI_METHOD "); if (g->flags & ACPI_WMI_STRING) - printk("ACPI_WMI_STRING "); + pr_cont("ACPI_WMI_STRING "); if (g->flags & ACPI_WMI_EVENT) - printk("ACPI_WMI_EVENT "); + pr_cont("ACPI_WMI_EVENT "); } - printk("\n"); + pr_cont("\n"); } @@ -522,7 +509,7 @@ static void wmi_notify_debug(u32 value, void *context) status = wmi_get_event_data(value, &response); if (status != AE_OK) { - printk(KERN_INFO "wmi: bad event status 0x%x\n", status); + pr_info("bad event status 0x%x\n", status); return; } @@ -531,22 +518,22 @@ static void wmi_notify_debug(u32 value, void *context) if (!obj) return; - printk(KERN_INFO PREFIX "DEBUG Event "); + pr_info("DEBUG Event "); switch(obj->type) { case ACPI_TYPE_BUFFER: - printk("BUFFER_TYPE - length %d\n", obj->buffer.length); + pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length); break; case ACPI_TYPE_STRING: - printk("STRING_TYPE - %s\n", obj->string.pointer); + pr_cont("STRING_TYPE - %s\n", obj->string.pointer); break; case ACPI_TYPE_INTEGER: - printk("INTEGER_TYPE - %llu\n", obj->integer.value); + pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value); break; case ACPI_TYPE_PACKAGE: - printk("PACKAGE_TYPE - %d elements\n", obj->package.count); + pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count); break; default: - printk("object type 0x%X\n", obj->type); + pr_cont("object type 0x%X\n", obj->type); } kfree(obj); } @@ -633,7 +620,7 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) params[0].type = ACPI_TYPE_INTEGER; params[0].integer.value = event; - list_for_each(p, &wmi_blocks.list) { + list_for_each(p, &wmi_block_list) { wblock = list_entry(p, struct wmi_block, list); gblock = &wblock->gblock; @@ -662,7 +649,7 @@ EXPORT_SYMBOL_GPL(wmi_has_guid); /* * sysfs interface */ -static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { char guid_string[37]; @@ -676,7 +663,11 @@ static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, return sprintf(buf, "wmi:%s\n", guid_string); } -static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); + +static struct device_attribute wmi_dev_attrs[] = { + __ATTR_RO(modalias), + __ATTR_NULL +}; static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) { @@ -702,108 +693,71 @@ static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) static void wmi_dev_free(struct device *dev) { - kfree(dev); + struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev); + + kfree(wmi_block); } static struct class wmi_class = { .name = "wmi", .dev_release = wmi_dev_free, .dev_uevent = wmi_dev_uevent, + .dev_attrs = wmi_dev_attrs, }; -static int wmi_create_devs(void) +static struct wmi_block *wmi_create_device(const struct guid_block *gblock, + acpi_handle handle) { - int result; - char guid_string[37]; - struct guid_block *gblock; struct wmi_block *wblock; - struct list_head *p; - struct device *guid_dev; - - /* Create devices for all the GUIDs */ - list_for_each(p, &wmi_blocks.list) { - wblock = list_entry(p, struct wmi_block, list); - - guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL); - if (!guid_dev) - return -ENOMEM; - - wblock->dev = guid_dev; - - guid_dev->class = &wmi_class; - dev_set_drvdata(guid_dev, wblock); - - gblock = &wblock->gblock; - - wmi_gtoa(gblock->guid, guid_string); - dev_set_name(guid_dev, guid_string); - - result = device_register(guid_dev); - if (result) - return result; + int error; + char guid_string[37]; - result = device_create_file(guid_dev, &dev_attr_modalias); - if (result) - return result; + wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); + if (!wblock) { + error = -ENOMEM; + goto err_out; } - return 0; -} + wblock->handle = handle; + wblock->gblock = *gblock; -static void wmi_remove_devs(void) -{ - struct guid_block *gblock; - struct wmi_block *wblock; - struct list_head *p; - struct device *guid_dev; + wblock->dev.class = &wmi_class; - /* Delete devices for all the GUIDs */ - list_for_each(p, &wmi_blocks.list) { - wblock = list_entry(p, struct wmi_block, list); + wmi_gtoa(gblock->guid, guid_string); + dev_set_name(&wblock->dev, guid_string); - guid_dev = wblock->dev; - gblock = &wblock->gblock; + dev_set_drvdata(&wblock->dev, wblock); - device_remove_file(guid_dev, &dev_attr_modalias); + error = device_register(&wblock->dev); + if (error) + goto err_free; - device_unregister(guid_dev); - } -} + list_add_tail(&wblock->list, &wmi_block_list); + return wblock; -static void wmi_class_exit(void) -{ - wmi_remove_devs(); - class_unregister(&wmi_class); +err_free: + kfree(wblock); +err_out: + return ERR_PTR(error); } -static int wmi_class_init(void) +static void wmi_free_devices(void) { - int ret; - - ret = class_register(&wmi_class); - if (ret) - return ret; + struct wmi_block *wblock, *next; - ret = wmi_create_devs(); - if (ret) - wmi_class_exit(); - - return ret; + /* Delete devices for all the GUIDs */ + list_for_each_entry_safe(wblock, next, &wmi_block_list, list) + device_unregister(&wblock->dev); } static bool guid_already_parsed(const char *guid_string) { - struct guid_block *gblock; struct wmi_block *wblock; - struct list_head *p; - list_for_each(p, &wmi_blocks.list) { - wblock = list_entry(p, struct wmi_block, list); - gblock = &wblock->gblock; - - if (strncmp(gblock->guid, guid_string, 16) == 0) + list_for_each_entry(wblock, &wmi_block_list, list) + if (strncmp(wblock->gblock.guid, guid_string, 16) == 0) return true; - } + return false; } @@ -814,30 +768,29 @@ static acpi_status parse_wdg(acpi_handle handle) { struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; union acpi_object *obj; - struct guid_block *gblock; + const struct guid_block *gblock; struct wmi_block *wblock; char guid_string[37]; acpi_status status; + int retval; u32 i, total; status = acpi_evaluate_object(handle, "_WDG", NULL, &out); - if (ACPI_FAILURE(status)) - return status; + return -ENXIO; obj = (union acpi_object *) out.pointer; + if (!obj) + return -ENXIO; - if (obj->type != ACPI_TYPE_BUFFER) - return AE_ERROR; - - total = obj->buffer.length / sizeof(struct guid_block); - - gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL); - if (!gblock) { - status = AE_NO_MEMORY; + if (obj->type != ACPI_TYPE_BUFFER) { + retval = -ENXIO; goto out_free_pointer; } + gblock = (const struct guid_block *)obj->buffer.pointer; + total = obj->buffer.length / sizeof(struct guid_block); + for (i = 0; i < total; i++) { /* Some WMI devices, like those for nVidia hooks, have a @@ -848,34 +801,32 @@ static acpi_status parse_wdg(acpi_handle handle) */ if (guid_already_parsed(gblock[i].guid) == true) { wmi_gtoa(gblock[i].guid, guid_string); - printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n", - guid_string); + pr_info("Skipping duplicate GUID %s\n", guid_string); continue; } + if (debug_dump_wdg) wmi_dump_wdg(&gblock[i]); - wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); - if (!wblock) { - status = AE_NO_MEMORY; - goto out_free_gblock; + wblock = wmi_create_device(&gblock[i], handle); + if (IS_ERR(wblock)) { + retval = PTR_ERR(wblock); + wmi_free_devices(); + break; } - wblock->gblock = gblock[i]; - wblock->handle = handle; if (debug_event) { wblock->handler = wmi_notify_debug; - status = wmi_method_enable(wblock, 1); + wmi_method_enable(wblock, 1); } - list_add_tail(&wblock->list, &wmi_blocks.list); } -out_free_gblock: - kfree(gblock); + retval = 0; + out_free_pointer: kfree(out.pointer); - return status; + return retval; } /* @@ -929,7 +880,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) struct list_head *p; char guid_string[37]; - list_for_each(p, &wmi_blocks.list) { + list_for_each(p, &wmi_block_list) { wblock = list_entry(p, struct wmi_block, list); block = &wblock->gblock; @@ -939,8 +890,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) wblock->handler(event, wblock->handler_data); if (debug_event) { wmi_gtoa(wblock->gblock.guid, guid_string); - printk(KERN_INFO PREFIX "DEBUG Event GUID:" - " %s\n", guid_string); + pr_info("DEBUG Event GUID: %s\n", guid_string); } acpi_bus_generate_netlink_event( @@ -955,6 +905,7 @@ static int acpi_wmi_remove(struct acpi_device *device, int type) { acpi_remove_address_space_handler(device->handle, ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); + wmi_free_devices(); return 0; } @@ -962,68 +913,57 @@ static int acpi_wmi_remove(struct acpi_device *device, int type) static int acpi_wmi_add(struct acpi_device *device) { acpi_status status; - int result = 0; + int error; status = acpi_install_address_space_handler(device->handle, ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler, NULL, NULL); - if (ACPI_FAILURE(status)) - return -ENODEV; - - status = parse_wdg(device->handle); if (ACPI_FAILURE(status)) { - printk(KERN_ERR PREFIX "Error installing EC region handler\n"); + pr_err("Error installing EC region handler\n"); return -ENODEV; } - return result; + error = parse_wdg(device->handle); + if (error) { + acpi_remove_address_space_handler(device->handle, + ACPI_ADR_SPACE_EC, + &acpi_wmi_ec_space_handler); + pr_err("Failed to parse WDG method\n"); + return error; + } + + return 0; } static int __init acpi_wmi_init(void) { - int result; - - INIT_LIST_HEAD(&wmi_blocks.list); + int error; if (acpi_disabled) return -ENODEV; - result = acpi_bus_register_driver(&acpi_wmi_driver); + error = class_register(&wmi_class); + if (error) + return error; - if (result < 0) { - printk(KERN_INFO PREFIX "Error loading mapper\n"); - return -ENODEV; + error = acpi_bus_register_driver(&acpi_wmi_driver); + if (error) { + pr_err("Error loading mapper\n"); + class_unregister(&wmi_class); + return error; } - result = wmi_class_init(); - if (result) { - acpi_bus_unregister_driver(&acpi_wmi_driver); - return result; - } - - printk(KERN_INFO PREFIX "Mapper loaded\n"); - - return result; + pr_info("Mapper loaded\n"); + return 0; } static void __exit acpi_wmi_exit(void) { - struct list_head *p, *tmp; - struct wmi_block *wblock; - - wmi_class_exit(); - acpi_bus_unregister_driver(&acpi_wmi_driver); + class_unregister(&wmi_class); - list_for_each_safe(p, tmp, &wmi_blocks.list) { - wblock = list_entry(p, struct wmi_block, list); - - list_del(p); - kfree(wblock); - } - - printk(KERN_INFO PREFIX "Mapper unloaded\n"); + pr_info("Mapper unloaded\n"); } subsys_initcall(acpi_wmi_init); |