diff options
author | Ike Panhc <ike.pan@canonical.com> | 2010-12-13 05:00:38 -0500 |
---|---|---|
committer | Matthew Garrett <mjg@redhat.com> | 2011-01-07 17:03:46 -0500 |
commit | f63409ae91ff94e2192dafbeb00c278ba299f80e (patch) | |
tree | 905c847ca636d8d15ae0fbfd4bde4d587c28ea12 /drivers/platform/x86/ideapad-laptop.c | |
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/x86/ideapad-laptop.c')
-rw-r--r-- | drivers/platform/x86/ideapad-laptop.c | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 49f207fc61d2..04b4f5b3114d 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c | |||
@@ -28,6 +28,8 @@ | |||
28 | #include <acpi/acpi_drivers.h> | 28 | #include <acpi/acpi_drivers.h> |
29 | #include <linux/rfkill.h> | 29 | #include <linux/rfkill.h> |
30 | #include <linux/platform_device.h> | 30 | #include <linux/platform_device.h> |
31 | #include <linux/input.h> | ||
32 | #include <linux/input/sparse-keymap.h> | ||
31 | 33 | ||
32 | #define IDEAPAD_DEV_CAMERA 0 | 34 | #define IDEAPAD_DEV_CAMERA 0 |
33 | #define IDEAPAD_DEV_WLAN 1 | 35 | #define IDEAPAD_DEV_WLAN 1 |
@@ -39,6 +41,7 @@ struct ideapad_private { | |||
39 | acpi_handle handle; | 41 | acpi_handle handle; |
40 | struct rfkill *rfk[5]; | 42 | struct rfkill *rfk[5]; |
41 | struct platform_device *platform_device; | 43 | struct platform_device *platform_device; |
44 | struct input_dev *inputdev; | ||
42 | } *ideapad_priv; | 45 | } *ideapad_priv; |
43 | 46 | ||
44 | static struct { | 47 | static struct { |
@@ -325,6 +328,66 @@ static void ideapad_platform_exit(void) | |||
325 | } | 328 | } |
326 | /* the above is platform device */ | 329 | /* the above is platform device */ |
327 | 330 | ||
331 | /* | ||
332 | * input device | ||
333 | */ | ||
334 | static const struct key_entry ideapad_keymap[] = { | ||
335 | { KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } }, | ||
336 | { KE_KEY, 0x0D, { KEY_WLAN } }, | ||
337 | { KE_END, 0 }, | ||
338 | }; | ||
339 | |||
340 | static int __devinit ideapad_input_init(void) | ||
341 | { | ||
342 | struct input_dev *inputdev; | ||
343 | int error; | ||
344 | |||
345 | inputdev = input_allocate_device(); | ||
346 | if (!inputdev) { | ||
347 | pr_info("Unable to allocate input device\n"); | ||
348 | return -ENOMEM; | ||
349 | } | ||
350 | |||
351 | inputdev->name = "Ideapad extra buttons"; | ||
352 | inputdev->phys = "ideapad/input0"; | ||
353 | inputdev->id.bustype = BUS_HOST; | ||
354 | inputdev->dev.parent = &ideapad_priv->platform_device->dev; | ||
355 | |||
356 | error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL); | ||
357 | if (error) { | ||
358 | pr_err("Unable to setup input device keymap\n"); | ||
359 | goto err_free_dev; | ||
360 | } | ||
361 | |||
362 | error = input_register_device(inputdev); | ||
363 | if (error) { | ||
364 | pr_err("Unable to register input device\n"); | ||
365 | goto err_free_keymap; | ||
366 | } | ||
367 | |||
368 | ideapad_priv->inputdev = inputdev; | ||
369 | return 0; | ||
370 | |||
371 | err_free_keymap: | ||
372 | sparse_keymap_free(inputdev); | ||
373 | err_free_dev: | ||
374 | input_free_device(inputdev); | ||
375 | return error; | ||
376 | } | ||
377 | |||
378 | static void __devexit ideapad_input_exit(void) | ||
379 | { | ||
380 | sparse_keymap_free(ideapad_priv->inputdev); | ||
381 | input_unregister_device(ideapad_priv->inputdev); | ||
382 | ideapad_priv->inputdev = NULL; | ||
383 | } | ||
384 | |||
385 | static void ideapad_input_report(unsigned long scancode) | ||
386 | { | ||
387 | sparse_keymap_report_event(ideapad_priv->inputdev, scancode, 1, true); | ||
388 | } | ||
389 | /* the above is input device */ | ||
390 | |||
328 | static const struct acpi_device_id ideapad_device_ids[] = { | 391 | static const struct acpi_device_id ideapad_device_ids[] = { |
329 | { "VPC2004", 0}, | 392 | { "VPC2004", 0}, |
330 | { "", 0}, | 393 | { "", 0}, |
@@ -350,6 +413,10 @@ static int ideapad_acpi_add(struct acpi_device *adevice) | |||
350 | if (ret) | 413 | if (ret) |
351 | goto platform_failed; | 414 | goto platform_failed; |
352 | 415 | ||
416 | ret = ideapad_input_init(); | ||
417 | if (ret) | ||
418 | goto input_failed; | ||
419 | |||
353 | for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) { | 420 | for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) { |
354 | if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) | 421 | if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) |
355 | ideapad_register_rfkill(adevice, i); | 422 | ideapad_register_rfkill(adevice, i); |
@@ -358,6 +425,8 @@ static int ideapad_acpi_add(struct acpi_device *adevice) | |||
358 | 425 | ||
359 | return 0; | 426 | return 0; |
360 | 427 | ||
428 | input_failed: | ||
429 | ideapad_platform_exit(); | ||
361 | platform_failed: | 430 | platform_failed: |
362 | kfree(priv); | 431 | kfree(priv); |
363 | return ret; | 432 | return ret; |
@@ -370,6 +439,7 @@ static int ideapad_acpi_remove(struct acpi_device *adevice, int type) | |||
370 | 439 | ||
371 | for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) | 440 | for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) |
372 | ideapad_unregister_rfkill(adevice, i); | 441 | ideapad_unregister_rfkill(adevice, i); |
442 | ideapad_input_exit(); | ||
373 | ideapad_platform_exit(); | 443 | ideapad_platform_exit(); |
374 | dev_set_drvdata(&adevice->dev, NULL); | 444 | dev_set_drvdata(&adevice->dev, NULL); |
375 | kfree(priv); | 445 | kfree(priv); |
@@ -392,6 +462,8 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) | |||
392 | if (test_bit(vpc_bit, &vpc1)) { | 462 | if (test_bit(vpc_bit, &vpc1)) { |
393 | if (vpc_bit == 9) | 463 | if (vpc_bit == 9) |
394 | ideapad_sync_rfk_state(adevice); | 464 | ideapad_sync_rfk_state(adevice); |
465 | else | ||
466 | ideapad_input_report(vpc_bit); | ||
395 | } | 467 | } |
396 | } | 468 | } |
397 | } | 469 | } |