Signed-off-by: Anisse Astier <anisse@xxxxxxxxx> --- Hi, This driver (intiated by http://sysmic.org/dotclear2/index.php?post/2009/06/02/83-msi-wmi-support) is aimed at supporting WMI based hotkeys on MSI Windtop all-in-one AE1900-WT. This patch contains both WMI and DMI auto loading. Once Mathew Garrett's patch for wmi autoloading is upstream (now in acpi-test), DMI auto loading could (should?) be removed. Also, Dmitry is in the process of factoring code for sparse keymaps. The second patch adds support its sparse keymap lib, removing a lot of code. These patches are based on linux-input tree, and have been tested on the hardware with and without the sparse keymap patch. drivers/platform/x86/Kconfig | 11 ++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/msi-wmi.c | 212 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 224 insertions(+), 0 deletions(-) create mode 100644 drivers/platform/x86/msi-wmi.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 1b27e4d..fe142af 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -441,4 +441,15 @@ config ACPI_TOSHIBA If you have a legacy free Toshiba laptop (such as the Libretto L1 series), say Y. + +config MSI_WMI + tristate "MSI WMI extras" + depends on ACPI_WMI + depends on INPUT + ---help--- + Say Y here if you want to support WMI-based hotkeys on MSI all-in-one + WindTop AE1900-WT. + + To compile this driver as a module, choose M here: the module will + be called msi-wmi. endif # X86_PLATFORM_DEVICES diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index d1c1621..22a0c78 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -21,3 +21,4 @@ obj-$(CONFIG_ACPI_WMI) += wmi.o obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o +obj-$(CONFIG_MSI_WMI) += msi-wmi.o diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c new file mode 100644 index 0000000..fb6d5bd --- /dev/null +++ b/drivers/platform/x86/msi-wmi.c @@ -0,0 +1,212 @@ +/* + * MSI WMI hotkeys + * + * Copyright (C) 2009 Jérôme Pouiller <jezz@xxxxxxxxxx> + * + * Portions based on hp-wmi.c: + * Copyright (C) 2008 Red Hat <mjg@xxxxxxxxxx> + * + * Portions based on wistron_btns.c: + * Copyright (C) 2005 Miloslav Trmac <mitr@xxxxxxxx> + * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@xxxxxxxxxxxx> + * Copyright (C) 2005 Dmitry Torokhov <dtor@xxxxxxx> + * + * 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 + */ + +#include <linux/kernel.h> +#include <linux/input.h> +#include <linux/acpi.h> +#include <linux/jiffies.h> + +#define MSIWMI_EVENT_GUID "b6f3eef2-3d2f-49dc-9de3-85bce18c62f2" + +struct key_entry { + u16 code; + u16 keycode; +}; + +static struct key_entry msi_wmi_keymap[] = { + /*Brightness keys should be used for a backlight driver*/ + {208, KEY_BRIGHTNESSUP}, + {209, KEY_BRIGHTNESSDOWN}, + {210, KEY_VOLUMEUP}, + {211, KEY_VOLUMEDOWN}, + {0} +}; + +static struct input_dev *msi_wmi_input_dev; +static unsigned long long msi_wmi_time_last_press; +static unsigned pression_timeout = 10; + +static struct key_entry *msi_wmi_get_entry_by_scancode(int code) +{ + struct key_entry *key; + + for (key = msi_wmi_keymap; key->code; key++) + if (code == key->code) + return key; + + return NULL; +} + +static struct key_entry *msi_wmi_get_entry_by_keycode(int keycode) +{ + struct key_entry *key; + + for (key = msi_wmi_keymap; key->code; key++) + if (keycode == key->keycode) + return key; + + return NULL; +} + +static void msi_wmi_notify(u32 value, void *context) +{ + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + static struct key_entry *key; + union acpi_object *obj; + + acpi_status ret; + ret = wmi_get_event_data(value, &response); + obj = (union acpi_object *)response.pointer; + + if (!obj || obj->type != ACPI_TYPE_INTEGER) { + printk(KERN_INFO "MSI WMI: Unknown response received\n"); + return; + } + + if (jiffies_to_msecs(get_jiffies_64() - msi_wmi_time_last_press) + > pression_timeout) { + printk(KERN_DEBUG + "MSI WMI: event correctly received: %llu\n", + obj->integer.value); + msi_wmi_time_last_press = get_jiffies_64(); + key = msi_wmi_get_entry_by_scancode(obj->integer.value); + input_report_key(msi_wmi_input_dev, key->keycode, 1); + input_sync(msi_wmi_input_dev); + input_report_key(msi_wmi_input_dev, key->keycode, 0); + input_sync(msi_wmi_input_dev); + } +} + +static int msi_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode) +{ + struct key_entry *key = msi_wmi_get_entry_by_scancode(scancode); + + if (key && key->code) { + *keycode = key->keycode; + return 0; + } + + return -EINVAL; +} + +static int msi_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode) +{ + struct key_entry *key; + int old_keycode; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + key = msi_wmi_get_entry_by_scancode(scancode); + if (key && key->code) { + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!msi_wmi_get_entry_by_keycode(old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; + } + + return -EINVAL; +} + +static int __init msi_wmi_input_setup(void) +{ + struct key_entry *key; + int err; + + msi_wmi_input_dev = input_allocate_device(); + + msi_wmi_input_dev->name = "MSI WMI hotkeys"; + msi_wmi_input_dev->phys = "wmi/input0"; + msi_wmi_input_dev->id.bustype = BUS_HOST; + msi_wmi_input_dev->getkeycode = msi_wmi_getkeycode; + msi_wmi_input_dev->setkeycode = msi_wmi_setkeycode; + + for (key = msi_wmi_keymap; key->code; key++) { + set_bit(EV_KEY, msi_wmi_input_dev->evbit); + set_bit(key->keycode, msi_wmi_input_dev->keybit); + } + + err = input_register_device(msi_wmi_input_dev); + + if (err) + input_free_device(msi_wmi_input_dev); + + return err; +} + +static int __init msi_wmi_init(void) +{ + int err; + + msi_wmi_time_last_press = get_jiffies_64(); + if (!wmi_has_guid(MSIWMI_EVENT_GUID)) { + printk(KERN_ERR + "This machine doesn't have MSI-hotkeys through WMI\n"); + goto load_error; + } + err = wmi_install_notify_handler(MSIWMI_EVENT_GUID, + msi_wmi_notify, NULL); + if (err) { + printk(KERN_ERR + "MSI WMI: Error while installing notify handler\n"); + goto load_error; + } + + msi_wmi_input_setup(); + + return 0; +load_error: + return -ENODEV; +} + +static void __exit msi_wmi_exit(void) +{ + if (wmi_has_guid(MSIWMI_EVENT_GUID)) { + wmi_remove_notify_handler(MSIWMI_EVENT_GUID); + input_unregister_device(msi_wmi_input_dev); + } +} + +module_init(msi_wmi_init); +module_exit(msi_wmi_exit); +module_param(pression_timeout, uint, 0); + +MODULE_AUTHOR("Jérôme Pouiller <jezz@xxxxxxxxxx>"); +MODULE_AUTHOR("Michael Bouchaud <michael@xxxxxxxxxxxxxx"); +MODULE_AUTHOR("Anisse Astier <anisse@xxxxxxxxx"); +MODULE_DESCRIPTION("MSI all-in-one WMI hotkeys driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("wmi:" MSIWMI_EVENT_GUID); +/*This one is useless if there is wmi-autoloading*/ +MODULE_ALIAS("dmi:bvnMICRO-STARINTERNATIONALCO.,LTD:*:svnMICRO-STARINTERNATIO" + "NALCO.,LTD:pnMS-6638:*:rnMS-7438:*:cvnMICRO-STARINTERNATIONALCO." + ",LTD:*"); +MODULE_PARM_DESC(pression_timeout, + "How much time interrupts are ignored between each pression"); -- 1.6.5.3 -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html