diff options
author | Corentin Chary <corentincj@iksaif.net> | 2009-01-20 10:17:43 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2009-01-20 14:35:06 -0500 |
commit | 034ce90a8d1051deaeb31bae7f26ff1440a5b988 (patch) | |
tree | 3d22036fe7bf5a0cae4206c34efdada7c5d3fcd8 /drivers/platform | |
parent | 2a7dc0d8c60325e9bf820900bf919430e5a419ab (diff) |
asus-laptop: hotkeys via the generic input interface
This patch is based on eeepc-laptop.c and the patchs
from Nicolas Trangez and Daniel Nascimento (mainly for the keymap).
Signed-off-by: Corentin Chary <corentincj@iksaif.net>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/asus-laptop.c | 155 |
1 files changed, 154 insertions, 1 deletions
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 1b7a28c1da73..53dbfb420627 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c | |||
@@ -46,6 +46,7 @@ | |||
46 | #include <acpi/acpi_drivers.h> | 46 | #include <acpi/acpi_drivers.h> |
47 | #include <acpi/acpi_bus.h> | 47 | #include <acpi/acpi_bus.h> |
48 | #include <asm/uaccess.h> | 48 | #include <asm/uaccess.h> |
49 | #include <linux/input.h> | ||
49 | 50 | ||
50 | #define ASUS_LAPTOP_VERSION "0.42" | 51 | #define ASUS_LAPTOP_VERSION "0.42" |
51 | 52 | ||
@@ -181,6 +182,8 @@ struct asus_hotk { | |||
181 | u8 light_level; //light sensor level | 182 | u8 light_level; //light sensor level |
182 | u8 light_switch; //light sensor switch value | 183 | u8 light_switch; //light sensor switch value |
183 | u16 event_count[128]; //count for each event TODO make this better | 184 | u16 event_count[128]; //count for each event TODO make this better |
185 | struct input_dev *inputdev; | ||
186 | u16 *keycode_map; | ||
184 | }; | 187 | }; |
185 | 188 | ||
186 | /* | 189 | /* |
@@ -250,6 +253,37 @@ ASUS_LED(rled, "record"); | |||
250 | ASUS_LED(pled, "phone"); | 253 | ASUS_LED(pled, "phone"); |
251 | ASUS_LED(gled, "gaming"); | 254 | ASUS_LED(gled, "gaming"); |
252 | 255 | ||
256 | struct key_entry { | ||
257 | char type; | ||
258 | u8 code; | ||
259 | u16 keycode; | ||
260 | }; | ||
261 | |||
262 | enum { KE_KEY, KE_END }; | ||
263 | |||
264 | static struct key_entry asus_keymap[] = { | ||
265 | {KE_KEY, 0x30, KEY_VOLUMEUP}, | ||
266 | {KE_KEY, 0x31, KEY_VOLUMEDOWN}, | ||
267 | {KE_KEY, 0x32, KEY_MUTE}, | ||
268 | {KE_KEY, 0x33, KEY_SWITCHVIDEOMODE}, | ||
269 | {KE_KEY, 0x34, KEY_SWITCHVIDEOMODE}, | ||
270 | {KE_KEY, 0x40, KEY_PREVIOUSSONG}, | ||
271 | {KE_KEY, 0x41, KEY_NEXTSONG}, | ||
272 | {KE_KEY, 0x43, KEY_STOP}, | ||
273 | {KE_KEY, 0x45, KEY_PLAYPAUSE}, | ||
274 | {KE_KEY, 0x50, KEY_EMAIL}, | ||
275 | {KE_KEY, 0x51, KEY_WWW}, | ||
276 | {KE_KEY, 0x5C, BTN_EXTRA}, /* Performance */ | ||
277 | {KE_KEY, 0x5D, KEY_WLAN}, | ||
278 | {KE_KEY, 0x61, KEY_SWITCHVIDEOMODE}, | ||
279 | {KE_KEY, 0x6B, BTN_TOUCH}, /* Lock Mouse */ | ||
280 | {KE_KEY, 0x82, KEY_CAMERA}, | ||
281 | {KE_KEY, 0x8A, KEY_TV}, | ||
282 | {KE_KEY, 0x95, KEY_MEDIA}, | ||
283 | {KE_KEY, 0x99, KEY_PHONE}, | ||
284 | {KE_END, 0}, | ||
285 | }; | ||
286 | |||
253 | /* | 287 | /* |
254 | * This function evaluates an ACPI method, given an int as parameter, the | 288 | * This function evaluates an ACPI method, given an int as parameter, the |
255 | * method is searched within the scope of the handle, can be NULL. The output | 289 | * method is searched within the scope of the handle, can be NULL. The output |
@@ -720,8 +754,68 @@ static ssize_t store_gps(struct device *dev, struct device_attribute *attr, | |||
720 | return store_status(buf, count, NULL, GPS_ON); | 754 | return store_status(buf, count, NULL, GPS_ON); |
721 | } | 755 | } |
722 | 756 | ||
757 | /* | ||
758 | * Hotkey functions | ||
759 | */ | ||
760 | static struct key_entry *asus_get_entry_by_scancode(int code) | ||
761 | { | ||
762 | struct key_entry *key; | ||
763 | |||
764 | for (key = asus_keymap; key->type != KE_END; key++) | ||
765 | if (code == key->code) | ||
766 | return key; | ||
767 | |||
768 | return NULL; | ||
769 | } | ||
770 | |||
771 | static struct key_entry *asus_get_entry_by_keycode(int code) | ||
772 | { | ||
773 | struct key_entry *key; | ||
774 | |||
775 | for (key = asus_keymap; key->type != KE_END; key++) | ||
776 | if (code == key->keycode && key->type == KE_KEY) | ||
777 | return key; | ||
778 | |||
779 | return NULL; | ||
780 | } | ||
781 | |||
782 | static int asus_getkeycode(struct input_dev *dev, int scancode, int *keycode) | ||
783 | { | ||
784 | struct key_entry *key = asus_get_entry_by_scancode(scancode); | ||
785 | |||
786 | if (key && key->type == KE_KEY) { | ||
787 | *keycode = key->keycode; | ||
788 | return 0; | ||
789 | } | ||
790 | |||
791 | return -EINVAL; | ||
792 | } | ||
793 | |||
794 | static int asus_setkeycode(struct input_dev *dev, int scancode, int keycode) | ||
795 | { | ||
796 | struct key_entry *key; | ||
797 | int old_keycode; | ||
798 | |||
799 | if (keycode < 0 || keycode > KEY_MAX) | ||
800 | return -EINVAL; | ||
801 | |||
802 | key = asus_get_entry_by_scancode(scancode); | ||
803 | if (key && key->type == KE_KEY) { | ||
804 | old_keycode = key->keycode; | ||
805 | key->keycode = keycode; | ||
806 | set_bit(keycode, dev->keybit); | ||
807 | if (!asus_get_entry_by_keycode(old_keycode)) | ||
808 | clear_bit(old_keycode, dev->keybit); | ||
809 | return 0; | ||
810 | } | ||
811 | |||
812 | return -EINVAL; | ||
813 | } | ||
814 | |||
723 | static void asus_hotk_notify(acpi_handle handle, u32 event, void *data) | 815 | static void asus_hotk_notify(acpi_handle handle, u32 event, void *data) |
724 | { | 816 | { |
817 | static struct key_entry *key; | ||
818 | |||
725 | /* TODO Find a better way to handle events count. */ | 819 | /* TODO Find a better way to handle events count. */ |
726 | if (!hotk) | 820 | if (!hotk) |
727 | return; | 821 | return; |
@@ -742,7 +836,20 @@ static void asus_hotk_notify(acpi_handle handle, u32 event, void *data) | |||
742 | dev_name(&hotk->device->dev), event, | 836 | dev_name(&hotk->device->dev), event, |
743 | hotk->event_count[event % 128]++); | 837 | hotk->event_count[event % 128]++); |
744 | 838 | ||
745 | return; | 839 | if (hotk->inputdev) { |
840 | key = asus_get_entry_by_scancode(event); | ||
841 | if (!key) | ||
842 | return ; | ||
843 | |||
844 | switch (key->type) { | ||
845 | case KE_KEY: | ||
846 | input_report_key(hotk->inputdev, key->keycode, 1); | ||
847 | input_sync(hotk->inputdev); | ||
848 | input_report_key(hotk->inputdev, key->keycode, 0); | ||
849 | input_sync(hotk->inputdev); | ||
850 | break; | ||
851 | } | ||
852 | } | ||
746 | } | 853 | } |
747 | 854 | ||
748 | #define ASUS_CREATE_DEVICE_ATTR(_name) \ | 855 | #define ASUS_CREATE_DEVICE_ATTR(_name) \ |
@@ -960,6 +1067,38 @@ static int asus_hotk_get_info(void) | |||
960 | return AE_OK; | 1067 | return AE_OK; |
961 | } | 1068 | } |
962 | 1069 | ||
1070 | static int asus_input_init(void) | ||
1071 | { | ||
1072 | const struct key_entry *key; | ||
1073 | int result; | ||
1074 | |||
1075 | hotk->inputdev = input_allocate_device(); | ||
1076 | if (!hotk->inputdev) { | ||
1077 | printk(ASUS_INFO "Unable to allocate input device\n"); | ||
1078 | return 0; | ||
1079 | } | ||
1080 | hotk->inputdev->name = "Asus Laptop extra buttons"; | ||
1081 | hotk->inputdev->phys = ASUS_HOTK_FILE "/input0"; | ||
1082 | hotk->inputdev->id.bustype = BUS_HOST; | ||
1083 | hotk->inputdev->getkeycode = asus_getkeycode; | ||
1084 | hotk->inputdev->setkeycode = asus_setkeycode; | ||
1085 | |||
1086 | for (key = asus_keymap; key->type != KE_END; key++) { | ||
1087 | switch (key->type) { | ||
1088 | case KE_KEY: | ||
1089 | set_bit(EV_KEY, hotk->inputdev->evbit); | ||
1090 | set_bit(key->keycode, hotk->inputdev->keybit); | ||
1091 | break; | ||
1092 | } | ||
1093 | } | ||
1094 | result = input_register_device(hotk->inputdev); | ||
1095 | if (result) { | ||
1096 | printk(ASUS_INFO "Unable to register input device\n"); | ||
1097 | input_free_device(hotk->inputdev); | ||
1098 | } | ||
1099 | return result; | ||
1100 | } | ||
1101 | |||
963 | static int asus_hotk_check(void) | 1102 | static int asus_hotk_check(void) |
964 | { | 1103 | { |
965 | int result = 0; | 1104 | int result = 0; |
@@ -1092,10 +1231,17 @@ static void asus_led_exit(void) | |||
1092 | ASUS_LED_UNREGISTER(gled); | 1231 | ASUS_LED_UNREGISTER(gled); |
1093 | } | 1232 | } |
1094 | 1233 | ||
1234 | static void asus_input_exit(void) | ||
1235 | { | ||
1236 | if (hotk->inputdev) | ||
1237 | input_unregister_device(hotk->inputdev); | ||
1238 | } | ||
1239 | |||
1095 | static void __exit asus_laptop_exit(void) | 1240 | static void __exit asus_laptop_exit(void) |
1096 | { | 1241 | { |
1097 | asus_backlight_exit(); | 1242 | asus_backlight_exit(); |
1098 | asus_led_exit(); | 1243 | asus_led_exit(); |
1244 | asus_input_exit(); | ||
1099 | 1245 | ||
1100 | acpi_bus_unregister_driver(&asus_hotk_driver); | 1246 | acpi_bus_unregister_driver(&asus_hotk_driver); |
1101 | sysfs_remove_group(&asuspf_device->dev.kobj, &asuspf_attribute_group); | 1247 | sysfs_remove_group(&asuspf_device->dev.kobj, &asuspf_attribute_group); |
@@ -1217,6 +1363,10 @@ static int __init asus_laptop_init(void) | |||
1217 | printk(ASUS_INFO "Brightness ignored, must be controlled by " | 1363 | printk(ASUS_INFO "Brightness ignored, must be controlled by " |
1218 | "ACPI video driver\n"); | 1364 | "ACPI video driver\n"); |
1219 | 1365 | ||
1366 | result = asus_input_init(); | ||
1367 | if (result) | ||
1368 | goto fail_input; | ||
1369 | |||
1220 | result = asus_led_init(dev); | 1370 | result = asus_led_init(dev); |
1221 | if (result) | 1371 | if (result) |
1222 | goto fail_led; | 1372 | goto fail_led; |
@@ -1256,6 +1406,9 @@ static int __init asus_laptop_init(void) | |||
1256 | asus_led_exit(); | 1406 | asus_led_exit(); |
1257 | 1407 | ||
1258 | fail_led: | 1408 | fail_led: |
1409 | asus_input_exit(); | ||
1410 | |||
1411 | fail_input: | ||
1259 | asus_backlight_exit(); | 1412 | asus_backlight_exit(); |
1260 | 1413 | ||
1261 | fail_backlight: | 1414 | fail_backlight: |