diff options
author | Ike Panhc <ike.pan@canonical.com> | 2010-12-13 18:00:38 +0800 |
---|---|---|
committer | Matthew Garrett <mjg@redhat.com> | 2011-01-07 17:03:46 -0500 |
commit | f63409ae91ff94e2192dafbeb00c278ba299f80e (patch) | |
tree | 905c847ca636d8d15ae0fbfd4bde4d587c28ea12 /drivers/platform | |
parent | c9f718d0c6b4cf8033aa0f5ac892d68ddfb865aa (diff) |
ideapad: add hotkey support
Hotkey enabled by this patch:
Fn+F3: Video mode switch
Fn+F5: software rfkill for wifi
For some ideapad when push Fn+F3, hardware generates Super-P keys, those key
will not be enabled by this patch.
Thanks for Dave Hansen report the problem. If CONFIG_INPUT_SPARSEKMAP is not
set, when building, you will have error message:
ERROR: "sparse_keymap_setup" [drivers/platform/x86/ideapad-laptop.ko] undefined!
ERROR: "sparse_keymap_free" [drivers/platform/x86/ideapad-laptop.ko] undefined!
ERROR: "sparse_keymap_report_event" [drivers/platform/x86/ideapad-laptop.ko] undefined!
To select INPUT_SPARSEKMAP solve this issue.
Ref: http://lkml.org/lkml/2010/12/2/340
Signed-off-by: Ike Panhc <ike.pan@canonical.com>
Signed-off-by: Matthew Garrett <mjg@redhat.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/Kconfig | 1 | ||||
-rw-r--r-- | drivers/platform/x86/ideapad-laptop.c | 72 |
2 files changed, 73 insertions, 0 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 4c7f8b92610..254c4ef6be3 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -226,6 +226,7 @@ config IDEAPAD_LAPTOP tristate "Lenovo IdeaPad Laptop Extras" depends on ACPI depends on RFKILL + select INPUT_SPARSEKMAP help This is a driver for the rfkill switches on Lenovo IdeaPad netbooks. diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 49f207fc61d..04b4f5b3114 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -28,6 +28,8 @@ #include <acpi/acpi_drivers.h> #include <linux/rfkill.h> #include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/input/sparse-keymap.h> #define IDEAPAD_DEV_CAMERA 0 #define IDEAPAD_DEV_WLAN 1 @@ -39,6 +41,7 @@ struct ideapad_private { acpi_handle handle; struct rfkill *rfk[5]; struct platform_device *platform_device; + struct input_dev *inputdev; } *ideapad_priv; static struct { @@ -325,6 +328,66 @@ static void ideapad_platform_exit(void) } /* the above is platform device */ +/* + * input device + */ +static const struct key_entry ideapad_keymap[] = { + { KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x0D, { KEY_WLAN } }, + { KE_END, 0 }, +}; + +static int __devinit ideapad_input_init(void) +{ + struct input_dev *inputdev; + int error; + + inputdev = input_allocate_device(); + if (!inputdev) { + pr_info("Unable to allocate input device\n"); + return -ENOMEM; + } + + inputdev->name = "Ideapad extra buttons"; + inputdev->phys = "ideapad/input0"; + inputdev->id.bustype = BUS_HOST; + inputdev->dev.parent = &ideapad_priv->platform_device->dev; + + error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL); + if (error) { + pr_err("Unable to setup input device keymap\n"); + goto err_free_dev; + } + + error = input_register_device(inputdev); + if (error) { + pr_err("Unable to register input device\n"); + goto err_free_keymap; + } + + ideapad_priv->inputdev = inputdev; + return 0; + +err_free_keymap: + sparse_keymap_free(inputdev); +err_free_dev: + input_free_device(inputdev); + return error; +} + +static void __devexit ideapad_input_exit(void) +{ + sparse_keymap_free(ideapad_priv->inputdev); + input_unregister_device(ideapad_priv->inputdev); + ideapad_priv->inputdev = NULL; +} + +static void ideapad_input_report(unsigned long scancode) +{ + sparse_keymap_report_event(ideapad_priv->inputdev, scancode, 1, true); +} +/* the above is input device */ + static const struct acpi_device_id ideapad_device_ids[] = { { "VPC2004", 0}, { "", 0}, @@ -350,6 +413,10 @@ static int ideapad_acpi_add(struct acpi_device *adevice) if (ret) goto platform_failed; + ret = ideapad_input_init(); + if (ret) + goto input_failed; + for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) { if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) ideapad_register_rfkill(adevice, i); @@ -358,6 +425,8 @@ static int ideapad_acpi_add(struct acpi_device *adevice) return 0; +input_failed: + ideapad_platform_exit(); platform_failed: kfree(priv); return ret; @@ -370,6 +439,7 @@ static int ideapad_acpi_remove(struct acpi_device *adevice, int type) for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) ideapad_unregister_rfkill(adevice, i); + ideapad_input_exit(); ideapad_platform_exit(); dev_set_drvdata(&adevice->dev, NULL); kfree(priv); @@ -392,6 +462,8 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) if (test_bit(vpc_bit, &vpc1)) { if (vpc_bit == 9) ideapad_sync_rfk_state(adevice); + else + ideapad_input_report(vpc_bit); } } } |