diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 4c7f8b92610365ab0d025d682b50d8d8839bc8b1..254c4ef6be35052b9e74dc46a517b2adfc4f1e43 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 49f207fc61d2b1389fe25c73dbf7410a4f234411..04b4f5b3114d6648c2bf0bd4fba2c75e46346582 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #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); } } }