From patchwork Fri Jan 9 20:17:11 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Morton X-Patchwork-Id: 1624 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n09KFI81012995 for ; Fri, 9 Jan 2009 12:15:18 -0800 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753970AbZAIUTB (ORCPT ); Fri, 9 Jan 2009 15:19:01 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1754505AbZAIUTB (ORCPT ); Fri, 9 Jan 2009 15:19:01 -0500 Received: from smtp1.linux-foundation.org ([140.211.169.13]:59295 "EHLO smtp1.linux-foundation.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753970AbZAIUTA (ORCPT ); Fri, 9 Jan 2009 15:19:00 -0500 Received: from imap1.linux-foundation.org (imap1.linux-foundation.org [140.211.169.55]) by smtp1.linux-foundation.org (8.14.2/8.13.5/Debian-3ubuntu1.1) with ESMTP id n09KHCIl021378 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Fri, 9 Jan 2009 12:17:13 -0800 Received: from localhost.localdomain (localhost [127.0.0.1]) by imap1.linux-foundation.org (8.13.5.20060308/8.13.5/Debian-3ubuntu1.1) with ESMTP id n09KHCcY014999; Fri, 9 Jan 2009 12:17:12 -0800 Message-Id: <200901092017.n09KHCcY014999@imap1.linux-foundation.org> Subject: [patch 4/6] misc: add dell-wmi driver for hotkey control To: lenb@kernel.org Cc: linux-acpi@vger.kernel.org, akpm@linux-foundation.org, mjg59@srcf.ucam.org, dtor@mail.ru, mjg@redhat.com From: akpm@linux-foundation.org Date: Fri, 09 Jan 2009 12:17:11 -0800 X-Spam-Status: No, hits=-3.411 required=5 tests=AWL, BAYES_00, OSDL_HEADER_SUBJECT_BRACKETED X-Spam-Checker-Version: SpamAssassin 3.2.4-osdl_revision__1.47__ X-MIMEDefang-Filter: lf$Revision: 1.188 $ X-Scanned-By: MIMEDefang 2.63 on 140.211.169.13 Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org From: Matthew Garrett Add a WMI driver for Dell laptops. Currently it does nothing but send a generic input event when a button with a picture of a battery on it is pressed, but maybe other uses will appear over time. Signed-off-by: Matthew Garrett Cc: Len Brown Cc: Dmitry Torokhov Signed-off-by: Andrew Morton --- MAINTAINERS | 5 drivers/misc/Kconfig | 10 + drivers/misc/Makefile | 1 drivers/misc/dell-wmi.c | 210 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 226 insertions(+) diff -puN MAINTAINERS~misc-add-dell-wmi-driver-for-hotkey-control MAINTAINERS --- a/MAINTAINERS~misc-add-dell-wmi-driver-for-hotkey-control +++ a/MAINTAINERS @@ -1376,6 +1376,11 @@ P: Doug Warzecha M: Douglas_Warzecha@dell.com S: Maintained +DELL WMI EXTRAS DRIVER +P: Matthew Garrett +M: mjg59@srcf.ucam.org +S: Maintained + DEVICE NUMBER REGISTRY P: Torben Mathiasen M: device@lanana.org diff -puN drivers/misc/Kconfig~misc-add-dell-wmi-driver-for-hotkey-control drivers/misc/Kconfig --- a/drivers/misc/Kconfig~misc-add-dell-wmi-driver-for-hotkey-control +++ a/drivers/misc/Kconfig @@ -136,6 +136,16 @@ config TIFM_7XX1 To compile this driver as a module, choose M here: the module will be called tifm_7xx1. +config DELL_WMI + tristate "Dell WMI extras" + depends on ACPI_WMI + depends on INPUT + help + Say Y here if you want to support WMI-based hotkeys on Dell laptops. + + To compile this driver as a module, choose M here: the module will + be called dell-wmi. + config ICS932S401 tristate "Integrated Circuits ICS932S401" depends on I2C && EXPERIMENTAL diff -puN drivers/misc/Makefile~misc-add-dell-wmi-driver-for-hotkey-control drivers/misc/Makefile --- a/drivers/misc/Makefile~misc-add-dell-wmi-driver-for-hotkey-control +++ a/drivers/misc/Makefile @@ -20,4 +20,5 @@ obj-$(CONFIG_KGDB_TESTS) += kgdbts.o obj-$(CONFIG_SGI_XP) += sgi-xp/ obj-$(CONFIG_SGI_GRU) += sgi-gru/ obj-$(CONFIG_HP_ILO) += hpilo.o +obj-$(CONFIG_DELL_WMI) += dell-wmi.o obj-$(CONFIG_C2PORT) += c2port/ diff -puN /dev/null drivers/misc/dell-wmi.c --- /dev/null +++ a/drivers/misc/dell-wmi.c @@ -0,0 +1,210 @@ +/* + * Dell WMI hotkeys + * + * Copyright (C) 2008 Red Hat + * + * Portions based on wistron_btns.c: + * Copyright (C) 2005 Miloslav Trmac + * Copyright (C) 2005 Bernhard Rosenkraenzer + * Copyright (C) 2005 Dmitry Torokhov + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Matthew Garrett "); +MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver"); +MODULE_LICENSE("GPL"); + +#define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492" + +MODULE_ALIAS("wmi:"DELL_EVENT_GUID); + +struct key_entry { + char type; /* See KE_* below */ + u16 code; + u16 keycode; +}; + +enum { KE_KEY, KE_SW, KE_END }; + +static struct key_entry dell_wmi_keymap[] = { + {KE_KEY, 0xe045, KEY_PROG1}, + {KE_END, 0} +}; + +static struct input_dev *dell_wmi_input_dev; + +static struct key_entry *dell_wmi_get_entry_by_scancode(int code) +{ + struct key_entry *key; + + for (key = dell_wmi_keymap; key->type != KE_END; key++) + if (code == key->code) + return key; + + return NULL; +} + +static struct key_entry *dell_wmi_get_entry_by_keycode(int keycode) +{ + struct key_entry *key; + + for (key = dell_wmi_keymap; key->type != KE_END; key++) + if (key->type == KE_KEY && keycode == key->keycode) + return key; + + return NULL; +} + +static int dell_wmi_getkeycode(struct input_dev *dev, int scancode, + int *keycode) +{ + struct key_entry *key = dell_wmi_get_entry_by_scancode(scancode); + + if (key && key->type == KE_KEY) { + *keycode = key->keycode; + return 0; + } + + return -EINVAL; +} + +static int dell_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 = dell_wmi_get_entry_by_scancode(scancode); + if (key && key->type == KE_KEY) { + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!dell_wmi_get_entry_by_keycode(old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; + } + return -EINVAL; +} + +static void dell_wmi_notify(u32 value, void *context) +{ + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + static struct key_entry *key; + union acpi_object *obj; + + wmi_get_event_data(value, &response); + + obj = (union acpi_object *)response.pointer; + + if (obj && obj->type == ACPI_TYPE_BUFFER) { + int *buffer = (int *)obj->buffer.pointer; + key = dell_wmi_get_entry_by_scancode(buffer[1]); + if (key) { + input_report_key(dell_wmi_input_dev, key->keycode, 1); + input_sync(dell_wmi_input_dev); + input_report_key(dell_wmi_input_dev, key->keycode, 0); + input_sync(dell_wmi_input_dev); + } else + printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", + buffer[1]); + } +} + +static int __init dell_wmi_input_setup(void) +{ + struct key_entry *key; + int err; + + dell_wmi_input_dev = input_allocate_device(); + + if (!dell_wmi_input_dev) + return -ENOMEM; + + dell_wmi_input_dev->name = "Dell WMI hotkeys"; + dell_wmi_input_dev->phys = "wmi/input0"; + dell_wmi_input_dev->id.bustype = BUS_HOST; + dell_wmi_input_dev->getkeycode = dell_wmi_getkeycode; + dell_wmi_input_dev->setkeycode = dell_wmi_setkeycode; + + for (key = dell_wmi_keymap; key->type != KE_END; key++) { + switch (key->type) { + case KE_KEY: + set_bit(EV_KEY, dell_wmi_input_dev->evbit); + set_bit(key->keycode, dell_wmi_input_dev->keybit); + break; + case KE_SW: + set_bit(EV_SW, dell_wmi_input_dev->evbit); + set_bit(key->keycode, dell_wmi_input_dev->swbit); + break; + } + } + + err = input_register_device(dell_wmi_input_dev); + + if (err) { + input_free_device(dell_wmi_input_dev); + return err; + } + + return 0; +} + +static int __init dell_wmi_init(void) +{ + int err; + + if (wmi_has_guid(DELL_EVENT_GUID)) { + err = dell_wmi_input_setup(); + + if (err) + return err; + + err = wmi_install_notify_handler(DELL_EVENT_GUID, + dell_wmi_notify, NULL); + if (err) { + input_unregister_device(dell_wmi_input_dev); + printk(KERN_ERR "dell-wmi: Unable to register" + " notify handler - %d\n", err); + return err; + } + + } else + printk(KERN_WARNING "dell-wmi: No known WMI GUID found\n"); + + return 0; +} + +static void __exit dell_wmi_exit(void) +{ + if (wmi_has_guid(DELL_EVENT_GUID)) { + wmi_remove_notify_handler(DELL_EVENT_GUID); + input_unregister_device(dell_wmi_input_dev); + } +} + +module_init(dell_wmi_init); +module_exit(dell_wmi_exit);