Create a driver to support WMI found on MSI Wind laptops. This driver allows us to receive some additional keycodes for keys that are not handled without this driver. Signed-off-by: Maxim Mikityanskiy <maxtram95@xxxxxxxxx> --- drivers/platform/x86/Kconfig | 13 +++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/msi-wind-wmi.c | 169 ++++++++++++++++++++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 drivers/platform/x86/msi-wind-wmi.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index c86bae8..46f269e5 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -561,6 +561,19 @@ config MSI_WMI To compile this driver as a module, choose M here: the module will be called msi-wmi. +config MSI_WIND_WMI + tristate "MSI Wind WMI Driver" + depends on ACPI_WMI + depends on INPUT + select INPUT_SPARSEKMAP + ---help--- + Say Y here if you want to support WMI-based hotkeys on MSI Wind + laptops. MSI Wind WMI differs from WMI found on other MSI laptops, so + say Y here if you have MSI Wind, otherwise select "MSI WMI extras". + + To compile this driver as a module, choose M here: the module will + be called msi-wind-wmi. + config TOPSTAR_LAPTOP tristate "Topstar Laptop Extras" depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index bf7e4f9..44389c0 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o obj-$(CONFIG_ACPI_WMI) += wmi.o obj-$(CONFIG_MSI_WMI) += msi-wmi.o obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o +obj-$(CONFIG_MSI_WIND_WMI) += msi-wind-wmi.o # toshiba_acpi must link after wmi to ensure that wmi devices are found # before toshiba_acpi initializes diff --git a/drivers/platform/x86/msi-wind-wmi.c b/drivers/platform/x86/msi-wind-wmi.c new file mode 100644 index 0000000..4f19434 --- /dev/null +++ b/drivers/platform/x86/msi-wind-wmi.c @@ -0,0 +1,169 @@ +/* + * MSI Wind WMI hotkeys + * + * Copyright (C) 2012 Maxim Mikityanskiy <maxtram95@xxxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/input.h> +#include <linux/input/sparse-keymap.h> +#include <linux/acpi.h> + +MODULE_AUTHOR("Maxim Mikityanskiy <maxtram95@xxxxxxxxx>"); +MODULE_DESCRIPTION("MSI Wind laptop WMI hotkeys driver"); +MODULE_LICENSE("GPL"); + +#define WMI_EVENT_GUID "5B3CC38A-40D9-7245-8AE6-1145B751BE3F" + +MODULE_ALIAS("wmi:" WMI_EVENT_GUID); + +/* Fn+F3 touchpad toggle */ +#define WIND_KEY_TOUCHPAD 0x08 +/* Fn+F11 Bluetooth toggle */ +#define WIND_KEY_BLUETOOTH 0x56 +/* Fn+F6 webcam toggle */ +#define WIND_KEY_CAMERA 0x57 +/* Fn+F11 Wi-Fi toggle */ +#define WIND_KEY_WLAN 0x5f +/* Fn+F10 turbo mode toggle */ +#define WIND_KEY_TURBO 0x60 +/* Fn+F10 ECO mode toggle */ +#define WIND_KEY_ECO 0x69 + +static struct key_entry wind_keymap[] = { + /* These keys work without WMI. Ignore them to avoid double keycodes */ + { KE_IGNORE, WIND_KEY_TOUCHPAD, { KEY_TOUCHPAD_TOGGLE } }, + { KE_IGNORE, WIND_KEY_BLUETOOTH, { KEY_BLUETOOTH } }, + { KE_IGNORE, WIND_KEY_CAMERA, { KEY_CAMERA } }, + { KE_IGNORE, WIND_KEY_WLAN, { KEY_WLAN } }, + /* These are keys that should be handled via WMI */ + { KE_KEY, WIND_KEY_TURBO, { KEY_PROG1 } }, + { KE_KEY, WIND_KEY_ECO, { KEY_PROG2 } }, + { KE_END, 0 } +}; + +static struct input_dev *wind_input_dev; + +static void wind_wmi_notify(u32 value, void *context) +{ + struct acpi_buffer response = { + .length = ACPI_ALLOCATE_BUFFER, + .pointer = NULL + }; + acpi_status status = wmi_get_event_data(value, &response); + union acpi_object *obj = response.pointer; + int code; + + if (status != AE_OK) { + pr_warn("Bad event status %#x\n", status); + return; + } + + if (!obj || obj->type != ACPI_TYPE_INTEGER) { + pr_warn("Unknown event received\n"); + goto wind_wmi_notify_free; + } + + code = obj->integer.value; + pr_debug("Event code: %#x\n", code); + + if (code == 0x00 || code == 0x62 || code == 0x63) { + /* Unknown events - drop them for now */ + goto wind_wmi_notify_free; + } + + if (!sparse_keymap_report_event(wind_input_dev, code, 1, true)) + pr_warn("Unknown key %#x pressed\n", code); + +wind_wmi_notify_free: + kfree(response.pointer); +} + +static int __init wind_input_setup(void) +{ + int err; + + wind_input_dev = input_allocate_device(); + if (!wind_input_dev) + return -ENOMEM; + + wind_input_dev->name = "MSI WMI hotkeys"; + wind_input_dev->phys = "wmi/input0"; + wind_input_dev->id.bustype = BUS_HOST; + + err = sparse_keymap_setup(wind_input_dev, wind_keymap, NULL); + if (err) + goto wind_input_setup_free_dev; + + err = input_register_device(wind_input_dev); + if (err) + goto wind_input_setup_free_keymap; + + return 0; + +wind_input_setup_free_keymap: + sparse_keymap_free(wind_input_dev); +wind_input_setup_free_dev: + input_free_device(wind_input_dev); + return err; +} + +static int __init wind_wmi_init(void) +{ + int err; + + if (!wmi_has_guid(WMI_EVENT_GUID)) { + pr_err("No MSI Wind WMI found\n"); + return -ENODEV; + } + + err = wind_input_setup(); + if (err) { + pr_err("Unable to setup input device\n"); + return err; + } + + if (ACPI_FAILURE(wmi_install_notify_handler(WMI_EVENT_GUID, + wind_wmi_notify, NULL))) { + pr_err("Unable to install WMI notify handler\n"); + err = -EIO; + goto wind_wmi_init_unregister_input; + } + + pr_debug("Event handler installed\n"); + + return 0; + +wind_wmi_init_unregister_input: + input_unregister_device(wind_input_dev); + sparse_keymap_free(wind_input_dev); + input_free_device(wind_input_dev); + return err; +} + +static void __exit wind_wmi_exit(void) +{ + wmi_remove_notify_handler(WMI_EVENT_GUID); + input_unregister_device(wind_input_dev); + sparse_keymap_free(wind_input_dev); + input_free_device(wind_input_dev); +} + +module_init(wind_wmi_init); +module_exit(wind_wmi_exit); -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe platform-driver-x86" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html