diff options
author | Lee, Chun-Yi <joeyli.kernel@gmail.com> | 2011-03-07 02:46:28 -0500 |
---|---|---|
committer | Matthew Garrett <mjg@redhat.com> | 2011-03-28 06:07:26 -0400 |
commit | 143a4c0284dc2378b3ce78866b3548d90121d843 (patch) | |
tree | 2c22036049bd6aa36ed7f6c0c632d324a617b0b3 /drivers/platform | |
parent | 8941178efad900e48e44000208513a6426c74368 (diff) |
msi-laptop: send out touchpad on/off key
MSI BIOS's raw behavior is send out KEY_TOUCHPAD_TOGGLE key when user
pressed touchpad hotkey.
Actually, we can capture the real touchpad status by read 0xE4 EC address
on MSI netbook/notebook. So, add msi-laptop input device for send out
KEY_TOUCHPAD_ON or KEY_TOUCHPAD_OFF key when user pressed Fn+F3 touchpad
hotkey. It leave userland applications to know the real touchpad status.
Tested on MSI netbook U-100, U-115, U160(N051), U160DX, N014, N034
Tested on MSI notebook CR620
Cc: Carlos Corbacho <carlos@strangeworlds.co.uk>
Cc: Matthew Garrett <mjg@redhat.com>
Cc: Dmitry Torokhov <dtor@mail.ru>
Cc: Corentin Chary <corentincj@iksaif.net>
Signed-off-by: Lee, Chun-Yi <jlee@novell.com>
Signed-off-by: Matthew Garrett <mjg@redhat.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/msi-laptop.c | 81 |
1 files changed, 79 insertions, 2 deletions
diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index 142d38579314..fb4da28f5906 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c | |||
@@ -60,6 +60,8 @@ | |||
60 | #include <linux/platform_device.h> | 60 | #include <linux/platform_device.h> |
61 | #include <linux/rfkill.h> | 61 | #include <linux/rfkill.h> |
62 | #include <linux/i8042.h> | 62 | #include <linux/i8042.h> |
63 | #include <linux/input.h> | ||
64 | #include <linux/input/sparse-keymap.h> | ||
63 | 65 | ||
64 | #define MSI_DRIVER_VERSION "0.5" | 66 | #define MSI_DRIVER_VERSION "0.5" |
65 | 67 | ||
@@ -78,6 +80,9 @@ | |||
78 | #define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d | 80 | #define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d |
79 | #define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0) | 81 | #define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0) |
80 | 82 | ||
83 | #define MSI_STANDARD_EC_TOUCHPAD_ADDRESS 0xe4 | ||
84 | #define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4) | ||
85 | |||
81 | static int msi_laptop_resume(struct platform_device *device); | 86 | static int msi_laptop_resume(struct platform_device *device); |
82 | 87 | ||
83 | #define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f | 88 | #define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f |
@@ -90,6 +95,14 @@ static int auto_brightness; | |||
90 | module_param(auto_brightness, int, 0); | 95 | module_param(auto_brightness, int, 0); |
91 | MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)"); | 96 | MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)"); |
92 | 97 | ||
98 | static const struct key_entry msi_laptop_keymap[] = { | ||
99 | {KE_KEY, KEY_TOUCHPAD_ON, {KEY_TOUCHPAD_ON} }, /* Touch Pad On */ | ||
100 | {KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },/* Touch Pad On */ | ||
101 | {KE_END, 0} | ||
102 | }; | ||
103 | |||
104 | static struct input_dev *msi_laptop_input_dev; | ||
105 | |||
93 | static bool old_ec_model; | 106 | static bool old_ec_model; |
94 | static int wlan_s, bluetooth_s, threeg_s; | 107 | static int wlan_s, bluetooth_s, threeg_s; |
95 | static int threeg_exists; | 108 | static int threeg_exists; |
@@ -605,6 +618,21 @@ static void msi_update_rfkill(struct work_struct *ignored) | |||
605 | } | 618 | } |
606 | static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill); | 619 | static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill); |
607 | 620 | ||
621 | static void msi_send_touchpad_key(struct work_struct *ignored) | ||
622 | { | ||
623 | u8 rdata; | ||
624 | int result; | ||
625 | |||
626 | result = ec_read(MSI_STANDARD_EC_TOUCHPAD_ADDRESS, &rdata); | ||
627 | if (result < 0) | ||
628 | return; | ||
629 | |||
630 | sparse_keymap_report_event(msi_laptop_input_dev, | ||
631 | (rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ? | ||
632 | KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true); | ||
633 | } | ||
634 | static DECLARE_DELAYED_WORK(msi_touchpad_work, msi_send_touchpad_key); | ||
635 | |||
608 | static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, | 636 | static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, |
609 | struct serio *port) | 637 | struct serio *port) |
610 | { | 638 | { |
@@ -613,12 +641,17 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, | |||
613 | if (str & 0x20) | 641 | if (str & 0x20) |
614 | return false; | 642 | return false; |
615 | 643 | ||
616 | /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan*/ | 644 | /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/ |
617 | if (unlikely(data == 0xe0)) { | 645 | if (unlikely(data == 0xe0)) { |
618 | extended = true; | 646 | extended = true; |
619 | return false; | 647 | return false; |
620 | } else if (unlikely(extended)) { | 648 | } else if (unlikely(extended)) { |
649 | extended = false; | ||
621 | switch (data) { | 650 | switch (data) { |
651 | case 0xE4: | ||
652 | schedule_delayed_work(&msi_touchpad_work, | ||
653 | round_jiffies_relative(0.5 * HZ)); | ||
654 | break; | ||
622 | case 0x54: | 655 | case 0x54: |
623 | case 0x62: | 656 | case 0x62: |
624 | case 0x76: | 657 | case 0x76: |
@@ -626,7 +659,6 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, | |||
626 | round_jiffies_relative(0.5 * HZ)); | 659 | round_jiffies_relative(0.5 * HZ)); |
627 | break; | 660 | break; |
628 | } | 661 | } |
629 | extended = false; | ||
630 | } | 662 | } |
631 | 663 | ||
632 | return false; | 664 | return false; |
@@ -731,6 +763,42 @@ static int msi_laptop_resume(struct platform_device *device) | |||
731 | return 0; | 763 | return 0; |
732 | } | 764 | } |
733 | 765 | ||
766 | static int __init msi_laptop_input_setup(void) | ||
767 | { | ||
768 | int err; | ||
769 | |||
770 | msi_laptop_input_dev = input_allocate_device(); | ||
771 | if (!msi_laptop_input_dev) | ||
772 | return -ENOMEM; | ||
773 | |||
774 | msi_laptop_input_dev->name = "MSI Laptop hotkeys"; | ||
775 | msi_laptop_input_dev->phys = "msi-laptop/input0"; | ||
776 | msi_laptop_input_dev->id.bustype = BUS_HOST; | ||
777 | |||
778 | err = sparse_keymap_setup(msi_laptop_input_dev, | ||
779 | msi_laptop_keymap, NULL); | ||
780 | if (err) | ||
781 | goto err_free_dev; | ||
782 | |||
783 | err = input_register_device(msi_laptop_input_dev); | ||
784 | if (err) | ||
785 | goto err_free_keymap; | ||
786 | |||
787 | return 0; | ||
788 | |||
789 | err_free_keymap: | ||
790 | sparse_keymap_free(msi_laptop_input_dev); | ||
791 | err_free_dev: | ||
792 | input_free_device(msi_laptop_input_dev); | ||
793 | return err; | ||
794 | } | ||
795 | |||
796 | static void msi_laptop_input_destroy(void) | ||
797 | { | ||
798 | sparse_keymap_free(msi_laptop_input_dev); | ||
799 | input_unregister_device(msi_laptop_input_dev); | ||
800 | } | ||
801 | |||
734 | static int load_scm_model_init(struct platform_device *sdev) | 802 | static int load_scm_model_init(struct platform_device *sdev) |
735 | { | 803 | { |
736 | u8 data; | 804 | u8 data; |
@@ -759,6 +827,11 @@ static int load_scm_model_init(struct platform_device *sdev) | |||
759 | if (result < 0) | 827 | if (result < 0) |
760 | goto fail_rfkill; | 828 | goto fail_rfkill; |
761 | 829 | ||
830 | /* setup input device */ | ||
831 | result = msi_laptop_input_setup(); | ||
832 | if (result) | ||
833 | goto fail_input; | ||
834 | |||
762 | result = i8042_install_filter(msi_laptop_i8042_filter); | 835 | result = i8042_install_filter(msi_laptop_i8042_filter); |
763 | if (result) { | 836 | if (result) { |
764 | printk(KERN_ERR | 837 | printk(KERN_ERR |
@@ -769,6 +842,9 @@ static int load_scm_model_init(struct platform_device *sdev) | |||
769 | return 0; | 842 | return 0; |
770 | 843 | ||
771 | fail_filter: | 844 | fail_filter: |
845 | msi_laptop_input_destroy(); | ||
846 | |||
847 | fail_input: | ||
772 | rfkill_cleanup(); | 848 | rfkill_cleanup(); |
773 | 849 | ||
774 | fail_rfkill: | 850 | fail_rfkill: |
@@ -886,6 +962,7 @@ static void __exit msi_cleanup(void) | |||
886 | { | 962 | { |
887 | if (load_scm_model) { | 963 | if (load_scm_model) { |
888 | i8042_remove_filter(msi_laptop_i8042_filter); | 964 | i8042_remove_filter(msi_laptop_i8042_filter); |
965 | msi_laptop_input_destroy(); | ||
889 | cancel_delayed_work_sync(&msi_rfkill_work); | 966 | cancel_delayed_work_sync(&msi_rfkill_work); |
890 | rfkill_cleanup(); | 967 | rfkill_cleanup(); |
891 | } | 968 | } |