Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetoot...
[cascardo/linux.git] / drivers / platform / x86 / dell-wmi.c
index 25721bf..6512a06 100644 (file)
@@ -65,10 +65,8 @@ static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
        /* Battery health status button */
        { KE_KEY, 0xe007, { KEY_BATTERY } },
 
-       /* This is actually for all radios. Although physically a
-        * switch, the notification does not provide an indication of
-        * state and so it should be reported as a key */
-       { KE_KEY, 0xe008, { KEY_WLAN } },
+       /* Radio devices state change */
+       { KE_IGNORE, 0xe008, { KEY_RFKILL } },
 
        /* The next device is at offset 6, the active devices are at
           offset 8 and the attached devices at offset 10 */
@@ -145,57 +143,154 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
 
 static struct input_dev *dell_wmi_input_dev;
 
+static void dell_wmi_process_key(int reported_key)
+{
+       const struct key_entry *key;
+
+       key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
+                                               reported_key);
+       if (!key) {
+               pr_info("Unknown key %x pressed\n", reported_key);
+               return;
+       }
+
+       pr_debug("Key %x pressed\n", reported_key);
+
+       /* Don't report brightness notifications that will also come via ACPI */
+       if ((key->keycode == KEY_BRIGHTNESSUP ||
+            key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video)
+               return;
+
+       sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
+}
+
 static void dell_wmi_notify(u32 value, void *context)
 {
        struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
        union acpi_object *obj;
        acpi_status status;
+       acpi_size buffer_size;
+       u16 *buffer_entry, *buffer_end;
+       int len, i;
 
        status = wmi_get_event_data(value, &response);
        if (status != AE_OK) {
-               pr_info("bad event status 0x%x\n", status);
+               pr_warn("bad event status 0x%x\n", status);
                return;
        }
 
        obj = (union acpi_object *)response.pointer;
+       if (!obj) {
+               pr_warn("no response\n");
+               return;
+       }
 
-       if (obj && obj->type == ACPI_TYPE_BUFFER) {
-               const struct key_entry *key;
-               int reported_key;
-               u16 *buffer_entry = (u16 *)obj->buffer.pointer;
-               int buffer_size = obj->buffer.length/2;
-
-               if (buffer_size >= 2 && dell_new_hk_type && buffer_entry[1] != 0x10) {
-                       pr_info("Received unknown WMI event (0x%x)\n",
-                               buffer_entry[1]);
-                       kfree(obj);
-                       return;
-               }
+       if (obj->type != ACPI_TYPE_BUFFER) {
+               pr_warn("bad response type %x\n", obj->type);
+               kfree(obj);
+               return;
+       }
 
-               if (buffer_size >= 3 && (dell_new_hk_type || buffer_entry[1] == 0x0))
-                       reported_key = (int)buffer_entry[2];
+       pr_debug("Received WMI event (%*ph)\n",
+               obj->buffer.length, obj->buffer.pointer);
+
+       buffer_entry = (u16 *)obj->buffer.pointer;
+       buffer_size = obj->buffer.length/2;
+
+       if (!dell_new_hk_type) {
+               if (buffer_size >= 3 && buffer_entry[1] == 0x0)
+                       dell_wmi_process_key(buffer_entry[2]);
                else if (buffer_size >= 2)
-                       reported_key = (int)buffer_entry[1] & 0xffff;
-               else {
+                       dell_wmi_process_key(buffer_entry[1]);
+               else
                        pr_info("Received unknown WMI event\n");
-                       kfree(obj);
-                       return;
+               kfree(obj);
+               return;
+       }
+
+       buffer_end = buffer_entry + buffer_size;
+
+       while (buffer_entry < buffer_end) {
+
+               len = buffer_entry[0];
+               if (len == 0)
+                       break;
+
+               len++;
+
+               if (buffer_entry + len > buffer_end) {
+                       pr_warn("Invalid length of WMI event\n");
+                       break;
                }
 
-               key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
-                                                       reported_key);
-               if (!key) {
-                       pr_info("Unknown key %x pressed\n", reported_key);
-               } else if ((key->keycode == KEY_BRIGHTNESSUP ||
-                           key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video) {
-                       /* Don't report brightness notifications that will also
-                        * come via ACPI */
-                       ;
-               } else {
-                       sparse_keymap_report_entry(dell_wmi_input_dev, key,
-                                                  1, true);
+               pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry);
+
+               switch (buffer_entry[1]) {
+               case 0x00:
+                       for (i = 2; i < len; ++i) {
+                               switch (buffer_entry[i]) {
+                               case 0xe043:
+                                       /* NIC Link is Up */
+                                       pr_debug("NIC Link is Up\n");
+                                       break;
+                               case 0xe044:
+                                       /* NIC Link is Down */
+                                       pr_debug("NIC Link is Down\n");
+                                       break;
+                               case 0xe045:
+                                       /* Unknown event but defined in DSDT */
+                               default:
+                                       /* Unknown event */
+                                       pr_info("Unknown WMI event type 0x00: "
+                                               "0x%x\n", (int)buffer_entry[i]);
+                                       break;
+                               }
+                       }
+                       break;
+               case 0x10:
+                       /* Keys pressed */
+                       for (i = 2; i < len; ++i)
+                               dell_wmi_process_key(buffer_entry[i]);
+                       break;
+               case 0x11:
+                       for (i = 2; i < len; ++i) {
+                               switch (buffer_entry[i]) {
+                               case 0xfff0:
+                                       /* Battery unplugged */
+                                       pr_debug("Battery unplugged\n");
+                                       break;
+                               case 0xfff1:
+                                       /* Battery inserted */
+                                       pr_debug("Battery inserted\n");
+                                       break;
+                               case 0x01e1:
+                               case 0x02ea:
+                               case 0x02eb:
+                               case 0x02ec:
+                               case 0x02f6:
+                                       /* Keyboard backlight level changed */
+                                       pr_debug("Keyboard backlight level "
+                                                "changed\n");
+                                       break;
+                               default:
+                                       /* Unknown event */
+                                       pr_info("Unknown WMI event type 0x11: "
+                                               "0x%x\n", (int)buffer_entry[i]);
+                                       break;
+                               }
+                       }
+                       break;
+               default:
+                       /* Unknown event */
+                       pr_info("Unknown WMI event type 0x%x\n",
+                               (int)buffer_entry[1]);
+                       break;
                }
+
+               buffer_entry += len;
+
        }
+
        kfree(obj);
 }
 
@@ -213,11 +308,16 @@ static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
        for (i = 0; i < hotkey_num; i++) {
                const struct dell_bios_keymap_entry *bios_entry =
                                        &dell_bios_hotkey_table->keymap[i];
-               keymap[i].type = KE_KEY;
-               keymap[i].code = bios_entry->scancode;
-               keymap[i].keycode = bios_entry->keycode < 256 ?
+               u16 keycode = bios_entry->keycode < 256 ?
                                    bios_to_linux_keycode[bios_entry->keycode] :
                                    KEY_RESERVED;
+
+               if (keycode == KEY_KBDILLUMTOGGLE)
+                       keymap[i].type = KE_IGNORE;
+               else
+                       keymap[i].type = KE_KEY;
+               keymap[i].code = bios_entry->scancode;
+               keymap[i].keycode = keycode;
        }
 
        keymap[hotkey_num].type = KE_END;