Commit 820787fceb3e62c29a36423eb30e2f9f198547f7

Authored by Colin Ian King
Committed by Matthew Garrett
1 parent 8eec8a1167

Enable Dell All-In-One volume up/down keys

Enable volume up and down hotkeys on WMI events
GUID 284A0E6B-380E-472A-921F-E52786257FB4 and
GUID 02314822-307C-4F66-bf0E-48AEAEB26CC8.

Also works around a firmware bug where the _WED method
should return an integer containing the key code and in fact
the method returns the key code in element zero of a buffer.

BugLink: http://bugs.launchpad.net/bugs/701530
BugLink: http://bugs.launchpad.net/bugs/676997

Signed-off-by: Colin Ian King <colin.king@canonical.com>
Signed-off-by: Matthew Garrett <mjg@redhat.com>

Showing 3 changed files with 185 additions and 0 deletions Side-by-side Diff

drivers/platform/x86/Kconfig
... ... @@ -101,6 +101,19 @@
101 101 To compile this driver as a module, choose M here: the module will
102 102 be called dell-wmi.
103 103  
  104 +config DELL_WMI_AIO
  105 + tristate "WMI Hotkeys for Dell All-In-One series"
  106 + depends on ACPI_WMI
  107 + depends on INPUT
  108 + select INPUT_SPARSEKMAP
  109 + ---help---
  110 + Say Y here if you want to support WMI-based hotkeys on Dell
  111 + All-In-One machines.
  112 +
  113 + To compile this driver as a module, choose M here: the module will
  114 + be called dell-wmi.
  115 +
  116 +
104 117 config FUJITSU_LAPTOP
105 118 tristate "Fujitsu Laptop Extras"
106 119 depends on ACPI
drivers/platform/x86/Makefile
... ... @@ -10,6 +10,7 @@
10 10 obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
11 11 obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
12 12 obj-$(CONFIG_DELL_WMI) += dell-wmi.o
  13 +obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o
13 14 obj-$(CONFIG_ACER_WMI) += acer-wmi.o
14 15 obj-$(CONFIG_ACERHDF) += acerhdf.o
15 16 obj-$(CONFIG_HP_ACCEL) += hp_accel.o
drivers/platform/x86/dell-wmi-aio.c
  1 +/*
  2 + * WMI hotkeys support for Dell All-In-One series
  3 + *
  4 + * This program is free software; you can redistribute it and/or modify
  5 + * it under the terms of the GNU General Public License as published by
  6 + * the Free Software Foundation; either version 2 of the License, or
  7 + * (at your option) any later version.
  8 + *
  9 + * This program is distributed in the hope that it will be useful,
  10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12 + * GNU General Public License for more details.
  13 + *
  14 + * You should have received a copy of the GNU General Public License
  15 + * along with this program; if not, write to the Free Software
  16 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  17 + */
  18 +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  19 +
  20 +#include <linux/kernel.h>
  21 +#include <linux/module.h>
  22 +#include <linux/init.h>
  23 +#include <linux/types.h>
  24 +#include <linux/input.h>
  25 +#include <linux/input/sparse-keymap.h>
  26 +#include <acpi/acpi_drivers.h>
  27 +#include <linux/acpi.h>
  28 +#include <linux/string.h>
  29 +
  30 +MODULE_DESCRIPTION("WMI hotkeys driver for Dell All-In-One series");
  31 +MODULE_LICENSE("GPL");
  32 +
  33 +#define EVENT_GUID1 "284A0E6B-380E-472A-921F-E52786257FB4"
  34 +#define EVENT_GUID2 "02314822-307C-4F66-BF0E-48AEAEB26CC8"
  35 +
  36 +static const char *dell_wmi_aio_guids[] = {
  37 + EVENT_GUID1,
  38 + EVENT_GUID2,
  39 + NULL
  40 +};
  41 +
  42 +MODULE_ALIAS("wmi:"EVENT_GUID1);
  43 +MODULE_ALIAS("wmi:"EVENT_GUID2);
  44 +
  45 +static const struct key_entry dell_wmi_aio_keymap[] = {
  46 + { KE_KEY, 0xc0, { KEY_VOLUMEUP } },
  47 + { KE_KEY, 0xc1, { KEY_VOLUMEDOWN } },
  48 + { KE_END, 0 }
  49 +};
  50 +
  51 +static struct input_dev *dell_wmi_aio_input_dev;
  52 +
  53 +static void dell_wmi_aio_notify(u32 value, void *context)
  54 +{
  55 + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
  56 + union acpi_object *obj;
  57 + acpi_status status;
  58 +
  59 + status = wmi_get_event_data(value, &response);
  60 + if (status != AE_OK) {
  61 + pr_info("bad event status 0x%x\n", status);
  62 + return;
  63 + }
  64 +
  65 + obj = (union acpi_object *)response.pointer;
  66 + if (obj) {
  67 + unsigned int scancode;
  68 +
  69 + switch (obj->type) {
  70 + case ACPI_TYPE_INTEGER:
  71 + /* Most All-In-One correctly return integer scancode */
  72 + scancode = obj->integer.value;
  73 + sparse_keymap_report_event(dell_wmi_aio_input_dev,
  74 + scancode, 1, true);
  75 + break;
  76 + case ACPI_TYPE_BUFFER:
  77 + /* Broken machines return the scancode in a buffer */
  78 + if (obj->buffer.pointer && obj->buffer.length > 0) {
  79 + scancode = obj->buffer.pointer[0];
  80 + sparse_keymap_report_event(
  81 + dell_wmi_aio_input_dev,
  82 + scancode, 1, true);
  83 + }
  84 + break;
  85 + }
  86 + }
  87 + kfree(obj);
  88 +}
  89 +
  90 +static int __init dell_wmi_aio_input_setup(void)
  91 +{
  92 + int err;
  93 +
  94 + dell_wmi_aio_input_dev = input_allocate_device();
  95 +
  96 + if (!dell_wmi_aio_input_dev)
  97 + return -ENOMEM;
  98 +
  99 + dell_wmi_aio_input_dev->name = "Dell AIO WMI hotkeys";
  100 + dell_wmi_aio_input_dev->phys = "wmi/input0";
  101 + dell_wmi_aio_input_dev->id.bustype = BUS_HOST;
  102 +
  103 + err = sparse_keymap_setup(dell_wmi_aio_input_dev,
  104 + dell_wmi_aio_keymap, NULL);
  105 + if (err) {
  106 + pr_err("Unable to setup input device keymap\n");
  107 + goto err_free_dev;
  108 + }
  109 + err = input_register_device(dell_wmi_aio_input_dev);
  110 + if (err) {
  111 + pr_info("Unable to register input device\n");
  112 + goto err_free_keymap;
  113 + }
  114 + return 0;
  115 +
  116 +err_free_keymap:
  117 + sparse_keymap_free(dell_wmi_aio_input_dev);
  118 +err_free_dev:
  119 + input_free_device(dell_wmi_aio_input_dev);
  120 + return err;
  121 +}
  122 +
  123 +static const char *dell_wmi_aio_find(void)
  124 +{
  125 + int i;
  126 +
  127 + for (i = 0; dell_wmi_aio_guids[i] != NULL; i++)
  128 + if (wmi_has_guid(dell_wmi_aio_guids[i]))
  129 + return dell_wmi_aio_guids[i];
  130 +
  131 + return NULL;
  132 +}
  133 +
  134 +static int __init dell_wmi_aio_init(void)
  135 +{
  136 + int err;
  137 + const char *guid;
  138 +
  139 + guid = dell_wmi_aio_find();
  140 + if (!guid) {
  141 + pr_warning("No known WMI GUID found\n");
  142 + return -ENXIO;
  143 + }
  144 +
  145 + err = dell_wmi_aio_input_setup();
  146 + if (err)
  147 + return err;
  148 +
  149 + err = wmi_install_notify_handler(guid, dell_wmi_aio_notify, NULL);
  150 + if (err) {
  151 + pr_err("Unable to register notify handler - %d\n", err);
  152 + sparse_keymap_free(dell_wmi_aio_input_dev);
  153 + input_unregister_device(dell_wmi_aio_input_dev);
  154 + return err;
  155 + }
  156 +
  157 + return 0;
  158 +}
  159 +
  160 +static void __exit dell_wmi_aio_exit(void)
  161 +{
  162 + const char *guid;
  163 +
  164 + guid = dell_wmi_aio_find();
  165 + wmi_remove_notify_handler(guid);
  166 + sparse_keymap_free(dell_wmi_aio_input_dev);
  167 + input_unregister_device(dell_wmi_aio_input_dev);
  168 +}
  169 +
  170 +module_init(dell_wmi_aio_init);
  171 +module_exit(dell_wmi_aio_exit);