diff options
author | Lee, Chun-Yi <joeyli.kernel@gmail.com> | 2010-12-06 21:29:20 -0500 |
---|---|---|
committer | Matthew Garrett <mjg@redhat.com> | 2011-01-07 17:03:47 -0500 |
commit | 3fdca87d10f1d45b1c034da343e68beb082f9b84 (patch) | |
tree | d22ecdf62d93a3e67c6830574b7d0c3eba84a827 /drivers/platform/x86 | |
parent | e98062ed6dc46ed3270350e1040e19d44150d1d1 (diff) |
acer-wmi: Add acer wmi hotkey events support
Add acer wmi hotkey event support. Install a wmi notify handler to
transfer wmi event key to key code, then send out keycode through acer
wmi input device to userland.
Signed-off-by: Lee, Chun-Yi <jlee@novell.com>
Acked-by: Dmitry Torokhov <dtor@mail.ru>
Acked-by: Thomas Renninger <trenn@suse.de>
Acked-by: Jiri Benc <jbenc@suse.cz>
Signed-off-by: Carlos Corbacho <carlos@strangeworlds.co.uk>
Cc: Corentin Chary <corentincj@iksaif.net>
Signed-off-by: Matthew Garrett <mjg@redhat.com>
Diffstat (limited to 'drivers/platform/x86')
-rw-r--r-- | drivers/platform/x86/Kconfig | 2 | ||||
-rw-r--r-- | drivers/platform/x86/acer-wmi.c | 139 |
2 files changed, 141 insertions, 0 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index fbee997d2c24..a122a0dac981 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
@@ -22,8 +22,10 @@ config ACER_WMI | |||
22 | depends on NEW_LEDS | 22 | depends on NEW_LEDS |
23 | depends on BACKLIGHT_CLASS_DEVICE | 23 | depends on BACKLIGHT_CLASS_DEVICE |
24 | depends on SERIO_I8042 | 24 | depends on SERIO_I8042 |
25 | depends on INPUT | ||
25 | depends on RFKILL || RFKILL = n | 26 | depends on RFKILL || RFKILL = n |
26 | depends on ACPI_WMI | 27 | depends on ACPI_WMI |
28 | select INPUT_SPARSEKMAP | ||
27 | ---help--- | 29 | ---help--- |
28 | This is a driver for newer Acer (and Wistron) laptops. It adds | 30 | This is a driver for newer Acer (and Wistron) laptops. It adds |
29 | wireless radio and bluetooth control, and on some laptops, | 31 | wireless radio and bluetooth control, and on some laptops, |
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index c8c65375bfe2..a1c6141f463b 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c | |||
@@ -37,6 +37,8 @@ | |||
37 | #include <linux/workqueue.h> | 37 | #include <linux/workqueue.h> |
38 | #include <linux/debugfs.h> | 38 | #include <linux/debugfs.h> |
39 | #include <linux/slab.h> | 39 | #include <linux/slab.h> |
40 | #include <linux/input.h> | ||
41 | #include <linux/input/sparse-keymap.h> | ||
40 | 42 | ||
41 | #include <acpi/acpi_drivers.h> | 43 | #include <acpi/acpi_drivers.h> |
42 | 44 | ||
@@ -48,6 +50,7 @@ MODULE_LICENSE("GPL"); | |||
48 | #define ACER_ERR KERN_ERR ACER_LOGPREFIX | 50 | #define ACER_ERR KERN_ERR ACER_LOGPREFIX |
49 | #define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX | 51 | #define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX |
50 | #define ACER_INFO KERN_INFO ACER_LOGPREFIX | 52 | #define ACER_INFO KERN_INFO ACER_LOGPREFIX |
53 | #define ACER_WARNING KERN_WARNING ACER_LOGPREFIX | ||
51 | 54 | ||
52 | /* | 55 | /* |
53 | * Magic Number | 56 | * Magic Number |
@@ -83,8 +86,39 @@ MODULE_LICENSE("GPL"); | |||
83 | #define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3" | 86 | #define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3" |
84 | #define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A" | 87 | #define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A" |
85 | 88 | ||
89 | /* | ||
90 | * Acer ACPI event GUIDs | ||
91 | */ | ||
92 | #define ACERWMID_EVENT_GUID "676AA15E-6A47-4D9F-A2CC-1E6D18D14026" | ||
93 | |||
86 | MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB"); | 94 | MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB"); |
87 | MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"); | 95 | MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"); |
96 | MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026"); | ||
97 | |||
98 | enum acer_wmi_event_ids { | ||
99 | WMID_HOTKEY_EVENT = 0x1, | ||
100 | }; | ||
101 | |||
102 | static const struct key_entry acer_wmi_keymap[] = { | ||
103 | {KE_KEY, 0x01, {KEY_WLAN} }, /* WiFi */ | ||
104 | {KE_KEY, 0x12, {KEY_BLUETOOTH} }, /* BT */ | ||
105 | {KE_KEY, 0x21, {KEY_PROG1} }, /* Backup */ | ||
106 | {KE_KEY, 0x22, {KEY_PROG2} }, /* Arcade */ | ||
107 | {KE_KEY, 0x23, {KEY_PROG3} }, /* P_Key */ | ||
108 | {KE_KEY, 0x24, {KEY_PROG4} }, /* Social networking_Key */ | ||
109 | {KE_KEY, 0x64, {KEY_SWITCHVIDEOMODE} }, /* Display Switch */ | ||
110 | {KE_KEY, 0x82, {KEY_F22} }, /* Touch Pad On/Off */ | ||
111 | {KE_END, 0} | ||
112 | }; | ||
113 | |||
114 | static struct input_dev *acer_wmi_input_dev; | ||
115 | |||
116 | struct event_return_value { | ||
117 | u8 function; | ||
118 | u8 key_num; | ||
119 | u16 device_state; | ||
120 | u32 reserved; | ||
121 | } __attribute__((packed)); | ||
88 | 122 | ||
89 | /* | 123 | /* |
90 | * Interface capability flags | 124 | * Interface capability flags |
@@ -1085,6 +1119,99 @@ static ssize_t show_interface(struct device *dev, struct device_attribute *attr, | |||
1085 | 1119 | ||
1086 | static DEVICE_ATTR(interface, S_IRUGO, show_interface, NULL); | 1120 | static DEVICE_ATTR(interface, S_IRUGO, show_interface, NULL); |
1087 | 1121 | ||
1122 | static void acer_wmi_notify(u32 value, void *context) | ||
1123 | { | ||
1124 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1125 | union acpi_object *obj; | ||
1126 | struct event_return_value return_value; | ||
1127 | acpi_status status; | ||
1128 | |||
1129 | status = wmi_get_event_data(value, &response); | ||
1130 | if (status != AE_OK) { | ||
1131 | printk(ACER_WARNING "bad event status 0x%x\n", status); | ||
1132 | return; | ||
1133 | } | ||
1134 | |||
1135 | obj = (union acpi_object *)response.pointer; | ||
1136 | |||
1137 | if (!obj) | ||
1138 | return; | ||
1139 | if (obj->type != ACPI_TYPE_BUFFER) { | ||
1140 | printk(ACER_WARNING "Unknown response received %d\n", | ||
1141 | obj->type); | ||
1142 | kfree(obj); | ||
1143 | return; | ||
1144 | } | ||
1145 | if (obj->buffer.length != 8) { | ||
1146 | printk(ACER_WARNING "Unknown buffer length %d\n", | ||
1147 | obj->buffer.length); | ||
1148 | kfree(obj); | ||
1149 | return; | ||
1150 | } | ||
1151 | |||
1152 | return_value = *((struct event_return_value *)obj->buffer.pointer); | ||
1153 | kfree(obj); | ||
1154 | |||
1155 | switch (return_value.function) { | ||
1156 | case WMID_HOTKEY_EVENT: | ||
1157 | if (!sparse_keymap_report_event(acer_wmi_input_dev, | ||
1158 | return_value.key_num, 1, true)) | ||
1159 | printk(ACER_WARNING "Unknown key number - 0x%x\n", | ||
1160 | return_value.key_num); | ||
1161 | break; | ||
1162 | default: | ||
1163 | printk(ACER_WARNING "Unknown function number - %d - %d\n", | ||
1164 | return_value.function, return_value.key_num); | ||
1165 | break; | ||
1166 | } | ||
1167 | } | ||
1168 | |||
1169 | static int __init acer_wmi_input_setup(void) | ||
1170 | { | ||
1171 | acpi_status status; | ||
1172 | int err; | ||
1173 | |||
1174 | acer_wmi_input_dev = input_allocate_device(); | ||
1175 | if (!acer_wmi_input_dev) | ||
1176 | return -ENOMEM; | ||
1177 | |||
1178 | acer_wmi_input_dev->name = "Acer WMI hotkeys"; | ||
1179 | acer_wmi_input_dev->phys = "wmi/input0"; | ||
1180 | acer_wmi_input_dev->id.bustype = BUS_HOST; | ||
1181 | |||
1182 | err = sparse_keymap_setup(acer_wmi_input_dev, acer_wmi_keymap, NULL); | ||
1183 | if (err) | ||
1184 | goto err_free_dev; | ||
1185 | |||
1186 | status = wmi_install_notify_handler(ACERWMID_EVENT_GUID, | ||
1187 | acer_wmi_notify, NULL); | ||
1188 | if (ACPI_FAILURE(status)) { | ||
1189 | err = -EIO; | ||
1190 | goto err_free_keymap; | ||
1191 | } | ||
1192 | |||
1193 | err = input_register_device(acer_wmi_input_dev); | ||
1194 | if (err) | ||
1195 | goto err_uninstall_notifier; | ||
1196 | |||
1197 | return 0; | ||
1198 | |||
1199 | err_uninstall_notifier: | ||
1200 | wmi_remove_notify_handler(ACERWMID_EVENT_GUID); | ||
1201 | err_free_keymap: | ||
1202 | sparse_keymap_free(acer_wmi_input_dev); | ||
1203 | err_free_dev: | ||
1204 | input_free_device(acer_wmi_input_dev); | ||
1205 | return err; | ||
1206 | } | ||
1207 | |||
1208 | static void acer_wmi_input_destroy(void) | ||
1209 | { | ||
1210 | wmi_remove_notify_handler(ACERWMID_EVENT_GUID); | ||
1211 | sparse_keymap_free(acer_wmi_input_dev); | ||
1212 | input_unregister_device(acer_wmi_input_dev); | ||
1213 | } | ||
1214 | |||
1088 | /* | 1215 | /* |
1089 | * debugfs functions | 1216 | * debugfs functions |
1090 | */ | 1217 | */ |
@@ -1327,6 +1454,12 @@ static int __init acer_wmi_init(void) | |||
1327 | "generic video driver\n"); | 1454 | "generic video driver\n"); |
1328 | } | 1455 | } |
1329 | 1456 | ||
1457 | if (wmi_has_guid(ACERWMID_EVENT_GUID)) { | ||
1458 | err = acer_wmi_input_setup(); | ||
1459 | if (err) | ||
1460 | return err; | ||
1461 | } | ||
1462 | |||
1330 | err = platform_driver_register(&acer_platform_driver); | 1463 | err = platform_driver_register(&acer_platform_driver); |
1331 | if (err) { | 1464 | if (err) { |
1332 | printk(ACER_ERR "Unable to register platform driver.\n"); | 1465 | printk(ACER_ERR "Unable to register platform driver.\n"); |
@@ -1368,11 +1501,17 @@ error_device_add: | |||
1368 | error_device_alloc: | 1501 | error_device_alloc: |
1369 | platform_driver_unregister(&acer_platform_driver); | 1502 | platform_driver_unregister(&acer_platform_driver); |
1370 | error_platform_register: | 1503 | error_platform_register: |
1504 | if (wmi_has_guid(ACERWMID_EVENT_GUID)) | ||
1505 | acer_wmi_input_destroy(); | ||
1506 | |||
1371 | return err; | 1507 | return err; |
1372 | } | 1508 | } |
1373 | 1509 | ||
1374 | static void __exit acer_wmi_exit(void) | 1510 | static void __exit acer_wmi_exit(void) |
1375 | { | 1511 | { |
1512 | if (wmi_has_guid(ACERWMID_EVENT_GUID)) | ||
1513 | acer_wmi_input_destroy(); | ||
1514 | |||
1376 | remove_sysfs(acer_platform_device); | 1515 | remove_sysfs(acer_platform_device); |
1377 | remove_debugfs(); | 1516 | remove_debugfs(); |
1378 | platform_device_unregister(acer_platform_device); | 1517 | platform_device_unregister(acer_platform_device); |