]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - drivers/platform/x86/wmi.c
wmi: use memcmp instead of strncmp to compare GUIDs
[mirror_ubuntu-zesty-kernel.git] / drivers / platform / x86 / wmi.c
index b2978a04317f9de32755a719b4c7abe215e588b6..aecd9a9b549f004d692976ce2a911644e2c56eb7 100644 (file)
@@ -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 (memcmp(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);