diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-02-26 20:20:18 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-02-26 20:20:18 -0500 |
commit | 98723153dc32106e5be701da15551853c9f785a9 (patch) | |
tree | a04631cbeb50703474ac4f5f20310f7ef53b2b38 /drivers | |
parent | 4cbd55188fe01f22783815cbb6d4f55a0ebf5969 (diff) | |
parent | 6335e4d56681f6f08f24f4b812a72d402793d393 (diff) |
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86:
toshiba_acpi: Add full hotkey support
hp-wmi: Add support for tablet rotation key
dell-laptop: Add another Dell laptop to the DMI whitelist
classmate-laptop: use a single MODULE_DEVICE_TABLE to get correct aliases
dell-laptop: Pay attention to which devices the hardware switch controls
dell-laptop: Use buffer with 32-bit physical address
dell-laptop: Blacklist machines not supporting dell-laptop
dell-laptop: Block software state changes when rfkill hard blocked
dell-laptop: Fix small memory leak
dell-laptop: Fix platform device unregistration
dell-laptop: Update rfkill state on kill switch
compal-laptop: Replace sysfs support with rfkill support
compal-laptop: Add support for known Compal made Dell laptops
MAINTAINERS: update drivers/platform/x86 information
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/platform/x86/Kconfig | 1 | ||||
-rw-r--r-- | drivers/platform/x86/classmate-laptop.c | 31 | ||||
-rw-r--r-- | drivers/platform/x86/compal-laptop.c | 247 | ||||
-rw-r--r-- | drivers/platform/x86/dell-laptop.c | 256 | ||||
-rw-r--r-- | drivers/platform/x86/hp-wmi.c | 1 | ||||
-rw-r--r-- | drivers/platform/x86/toshiba_acpi.c | 206 |
6 files changed, 550 insertions, 192 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index f526e735c5ab..6848f213eb53 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
@@ -79,6 +79,7 @@ config DELL_LAPTOP | |||
79 | depends on BACKLIGHT_CLASS_DEVICE | 79 | depends on BACKLIGHT_CLASS_DEVICE |
80 | depends on RFKILL || RFKILL = n | 80 | depends on RFKILL || RFKILL = n |
81 | depends on POWER_SUPPLY | 81 | depends on POWER_SUPPLY |
82 | depends on SERIO_I8042 | ||
82 | default n | 83 | default n |
83 | ---help--- | 84 | ---help--- |
84 | This driver adds support for rfkill and backlight control to Dell | 85 | This driver adds support for rfkill and backlight control to Dell |
diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index ed90082cdf1d..8cb20e45bad6 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c | |||
@@ -34,6 +34,11 @@ struct cmpc_accel { | |||
34 | #define CMPC_ACCEL_SENSITIVITY_DEFAULT 5 | 34 | #define CMPC_ACCEL_SENSITIVITY_DEFAULT 5 |
35 | 35 | ||
36 | 36 | ||
37 | #define CMPC_ACCEL_HID "ACCE0000" | ||
38 | #define CMPC_TABLET_HID "TBLT0000" | ||
39 | #define CMPC_BL_HID "IPML200" | ||
40 | #define CMPC_KEYS_HID "FnBT0000" | ||
41 | |||
37 | /* | 42 | /* |
38 | * Generic input device code. | 43 | * Generic input device code. |
39 | */ | 44 | */ |
@@ -282,10 +287,9 @@ static int cmpc_accel_remove(struct acpi_device *acpi, int type) | |||
282 | } | 287 | } |
283 | 288 | ||
284 | static const struct acpi_device_id cmpc_accel_device_ids[] = { | 289 | static const struct acpi_device_id cmpc_accel_device_ids[] = { |
285 | {"ACCE0000", 0}, | 290 | {CMPC_ACCEL_HID, 0}, |
286 | {"", 0} | 291 | {"", 0} |
287 | }; | 292 | }; |
288 | MODULE_DEVICE_TABLE(acpi, cmpc_accel_device_ids); | ||
289 | 293 | ||
290 | static struct acpi_driver cmpc_accel_acpi_driver = { | 294 | static struct acpi_driver cmpc_accel_acpi_driver = { |
291 | .owner = THIS_MODULE, | 295 | .owner = THIS_MODULE, |
@@ -366,10 +370,9 @@ static int cmpc_tablet_resume(struct acpi_device *acpi) | |||
366 | } | 370 | } |
367 | 371 | ||
368 | static const struct acpi_device_id cmpc_tablet_device_ids[] = { | 372 | static const struct acpi_device_id cmpc_tablet_device_ids[] = { |
369 | {"TBLT0000", 0}, | 373 | {CMPC_TABLET_HID, 0}, |
370 | {"", 0} | 374 | {"", 0} |
371 | }; | 375 | }; |
372 | MODULE_DEVICE_TABLE(acpi, cmpc_tablet_device_ids); | ||
373 | 376 | ||
374 | static struct acpi_driver cmpc_tablet_acpi_driver = { | 377 | static struct acpi_driver cmpc_tablet_acpi_driver = { |
375 | .owner = THIS_MODULE, | 378 | .owner = THIS_MODULE, |
@@ -477,17 +480,16 @@ static int cmpc_bl_remove(struct acpi_device *acpi, int type) | |||
477 | return 0; | 480 | return 0; |
478 | } | 481 | } |
479 | 482 | ||
480 | static const struct acpi_device_id cmpc_device_ids[] = { | 483 | static const struct acpi_device_id cmpc_bl_device_ids[] = { |
481 | {"IPML200", 0}, | 484 | {CMPC_BL_HID, 0}, |
482 | {"", 0} | 485 | {"", 0} |
483 | }; | 486 | }; |
484 | MODULE_DEVICE_TABLE(acpi, cmpc_device_ids); | ||
485 | 487 | ||
486 | static struct acpi_driver cmpc_bl_acpi_driver = { | 488 | static struct acpi_driver cmpc_bl_acpi_driver = { |
487 | .owner = THIS_MODULE, | 489 | .owner = THIS_MODULE, |
488 | .name = "cmpc", | 490 | .name = "cmpc", |
489 | .class = "cmpc", | 491 | .class = "cmpc", |
490 | .ids = cmpc_device_ids, | 492 | .ids = cmpc_bl_device_ids, |
491 | .ops = { | 493 | .ops = { |
492 | .add = cmpc_bl_add, | 494 | .add = cmpc_bl_add, |
493 | .remove = cmpc_bl_remove | 495 | .remove = cmpc_bl_remove |
@@ -540,10 +542,9 @@ static int cmpc_keys_remove(struct acpi_device *acpi, int type) | |||
540 | } | 542 | } |
541 | 543 | ||
542 | static const struct acpi_device_id cmpc_keys_device_ids[] = { | 544 | static const struct acpi_device_id cmpc_keys_device_ids[] = { |
543 | {"FnBT0000", 0}, | 545 | {CMPC_KEYS_HID, 0}, |
544 | {"", 0} | 546 | {"", 0} |
545 | }; | 547 | }; |
546 | MODULE_DEVICE_TABLE(acpi, cmpc_keys_device_ids); | ||
547 | 548 | ||
548 | static struct acpi_driver cmpc_keys_acpi_driver = { | 549 | static struct acpi_driver cmpc_keys_acpi_driver = { |
549 | .owner = THIS_MODULE, | 550 | .owner = THIS_MODULE, |
@@ -607,3 +608,13 @@ static void cmpc_exit(void) | |||
607 | 608 | ||
608 | module_init(cmpc_init); | 609 | module_init(cmpc_init); |
609 | module_exit(cmpc_exit); | 610 | module_exit(cmpc_exit); |
611 | |||
612 | static const struct acpi_device_id cmpc_device_ids[] = { | ||
613 | {CMPC_ACCEL_HID, 0}, | ||
614 | {CMPC_TABLET_HID, 0}, | ||
615 | {CMPC_BL_HID, 0}, | ||
616 | {CMPC_KEYS_HID, 0}, | ||
617 | {"", 0} | ||
618 | }; | ||
619 | |||
620 | MODULE_DEVICE_TABLE(acpi, cmpc_device_ids); | ||
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 1a387e79f719..2740b40aad9b 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c | |||
@@ -26,17 +26,8 @@ | |||
26 | /* | 26 | /* |
27 | * comapl-laptop.c - Compal laptop support. | 27 | * comapl-laptop.c - Compal laptop support. |
28 | * | 28 | * |
29 | * This driver exports a few files in /sys/devices/platform/compal-laptop/: | 29 | * The driver registers itself with the rfkill subsystem and |
30 | * | 30 | * the Linux backlight control subsystem. |
31 | * wlan - wlan subsystem state: contains 0 or 1 (rw) | ||
32 | * | ||
33 | * bluetooth - Bluetooth subsystem state: contains 0 or 1 (rw) | ||
34 | * | ||
35 | * raw - raw value taken from embedded controller register (ro) | ||
36 | * | ||
37 | * In addition to these platform device attributes the driver | ||
38 | * registers itself in the Linux backlight control subsystem and is | ||
39 | * available to userspace under /sys/class/backlight/compal-laptop/. | ||
40 | * | 31 | * |
41 | * This driver might work on other laptops produced by Compal. If you | 32 | * This driver might work on other laptops produced by Compal. If you |
42 | * want to try it you can pass force=1 as argument to the module which | 33 | * want to try it you can pass force=1 as argument to the module which |
@@ -51,6 +42,7 @@ | |||
51 | #include <linux/dmi.h> | 42 | #include <linux/dmi.h> |
52 | #include <linux/backlight.h> | 43 | #include <linux/backlight.h> |
53 | #include <linux/platform_device.h> | 44 | #include <linux/platform_device.h> |
45 | #include <linux/rfkill.h> | ||
54 | 46 | ||
55 | #define COMPAL_DRIVER_VERSION "0.2.6" | 47 | #define COMPAL_DRIVER_VERSION "0.2.6" |
56 | 48 | ||
@@ -63,6 +55,10 @@ | |||
63 | #define WLAN_MASK 0x01 | 55 | #define WLAN_MASK 0x01 |
64 | #define BT_MASK 0x02 | 56 | #define BT_MASK 0x02 |
65 | 57 | ||
58 | static struct rfkill *wifi_rfkill; | ||
59 | static struct rfkill *bt_rfkill; | ||
60 | static struct platform_device *compal_device; | ||
61 | |||
66 | static int force; | 62 | static int force; |
67 | module_param(force, bool, 0); | 63 | module_param(force, bool, 0); |
68 | MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); | 64 | MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); |
@@ -88,65 +84,75 @@ static int get_lcd_level(void) | |||
88 | return (int) result; | 84 | return (int) result; |
89 | } | 85 | } |
90 | 86 | ||
91 | static int set_wlan_state(int state) | 87 | static int compal_rfkill_set(void *data, bool blocked) |
92 | { | 88 | { |
89 | unsigned long radio = (unsigned long) data; | ||
93 | u8 result, value; | 90 | u8 result, value; |
94 | 91 | ||
95 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | 92 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); |
96 | 93 | ||
97 | if ((result & KILLSWITCH_MASK) == 0) | 94 | if (!blocked) |
98 | return -EINVAL; | 95 | value = (u8) (result | radio); |
99 | else { | 96 | else |
100 | if (state) | 97 | value = (u8) (result & ~radio); |
101 | value = (u8) (result | WLAN_MASK); | 98 | ec_write(COMPAL_EC_COMMAND_WIRELESS, value); |
102 | else | ||
103 | value = (u8) (result & ~WLAN_MASK); | ||
104 | ec_write(COMPAL_EC_COMMAND_WIRELESS, value); | ||
105 | } | ||
106 | 99 | ||
107 | return 0; | 100 | return 0; |
108 | } | 101 | } |
109 | 102 | ||
110 | static int set_bluetooth_state(int state) | 103 | static void compal_rfkill_poll(struct rfkill *rfkill, void *data) |
111 | { | 104 | { |
112 | u8 result, value; | 105 | u8 result; |
106 | bool hw_blocked; | ||
113 | 107 | ||
114 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | 108 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); |
115 | 109 | ||
116 | if ((result & KILLSWITCH_MASK) == 0) | 110 | hw_blocked = !(result & KILLSWITCH_MASK); |
117 | return -EINVAL; | 111 | rfkill_set_hw_state(rfkill, hw_blocked); |
118 | else { | ||
119 | if (state) | ||
120 | value = (u8) (result | BT_MASK); | ||
121 | else | ||
122 | value = (u8) (result & ~BT_MASK); | ||
123 | ec_write(COMPAL_EC_COMMAND_WIRELESS, value); | ||
124 | } | ||
125 | |||
126 | return 0; | ||
127 | } | 112 | } |
128 | 113 | ||
129 | static int get_wireless_state(int *wlan, int *bluetooth) | 114 | static const struct rfkill_ops compal_rfkill_ops = { |
115 | .poll = compal_rfkill_poll, | ||
116 | .set_block = compal_rfkill_set, | ||
117 | }; | ||
118 | |||
119 | static int setup_rfkill(void) | ||
130 | { | 120 | { |
131 | u8 result; | 121 | int ret; |
132 | 122 | ||
133 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | 123 | wifi_rfkill = rfkill_alloc("compal-wifi", &compal_device->dev, |
124 | RFKILL_TYPE_WLAN, &compal_rfkill_ops, | ||
125 | (void *) WLAN_MASK); | ||
126 | if (!wifi_rfkill) | ||
127 | return -ENOMEM; | ||
134 | 128 | ||
135 | if (wlan) { | 129 | ret = rfkill_register(wifi_rfkill); |
136 | if ((result & KILLSWITCH_MASK) == 0) | 130 | if (ret) |
137 | *wlan = 0; | 131 | goto err_wifi; |
138 | else | ||
139 | *wlan = result & WLAN_MASK; | ||
140 | } | ||
141 | 132 | ||
142 | if (bluetooth) { | 133 | bt_rfkill = rfkill_alloc("compal-bluetooth", &compal_device->dev, |
143 | if ((result & KILLSWITCH_MASK) == 0) | 134 | RFKILL_TYPE_BLUETOOTH, &compal_rfkill_ops, |
144 | *bluetooth = 0; | 135 | (void *) BT_MASK); |
145 | else | 136 | if (!bt_rfkill) { |
146 | *bluetooth = (result & BT_MASK) >> 1; | 137 | ret = -ENOMEM; |
138 | goto err_allocate_bt; | ||
147 | } | 139 | } |
140 | ret = rfkill_register(bt_rfkill); | ||
141 | if (ret) | ||
142 | goto err_register_bt; | ||
148 | 143 | ||
149 | return 0; | 144 | return 0; |
145 | |||
146 | err_register_bt: | ||
147 | rfkill_destroy(bt_rfkill); | ||
148 | |||
149 | err_allocate_bt: | ||
150 | rfkill_unregister(wifi_rfkill); | ||
151 | |||
152 | err_wifi: | ||
153 | rfkill_destroy(wifi_rfkill); | ||
154 | |||
155 | return ret; | ||
150 | } | 156 | } |
151 | 157 | ||
152 | /* Backlight device stuff */ | 158 | /* Backlight device stuff */ |
@@ -169,86 +175,6 @@ static struct backlight_ops compalbl_ops = { | |||
169 | 175 | ||
170 | static struct backlight_device *compalbl_device; | 176 | static struct backlight_device *compalbl_device; |
171 | 177 | ||
172 | /* Platform device */ | ||
173 | |||
174 | static ssize_t show_wlan(struct device *dev, | ||
175 | struct device_attribute *attr, char *buf) | ||
176 | { | ||
177 | int ret, enabled; | ||
178 | |||
179 | ret = get_wireless_state(&enabled, NULL); | ||
180 | if (ret < 0) | ||
181 | return ret; | ||
182 | |||
183 | return sprintf(buf, "%i\n", enabled); | ||
184 | } | ||
185 | |||
186 | static ssize_t show_raw(struct device *dev, | ||
187 | struct device_attribute *attr, char *buf) | ||
188 | { | ||
189 | u8 result; | ||
190 | |||
191 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | ||
192 | |||
193 | return sprintf(buf, "%i\n", result); | ||
194 | } | ||
195 | |||
196 | static ssize_t show_bluetooth(struct device *dev, | ||
197 | struct device_attribute *attr, char *buf) | ||
198 | { | ||
199 | int ret, enabled; | ||
200 | |||
201 | ret = get_wireless_state(NULL, &enabled); | ||
202 | if (ret < 0) | ||
203 | return ret; | ||
204 | |||
205 | return sprintf(buf, "%i\n", enabled); | ||
206 | } | ||
207 | |||
208 | static ssize_t store_wlan_state(struct device *dev, | ||
209 | struct device_attribute *attr, const char *buf, size_t count) | ||
210 | { | ||
211 | int state, ret; | ||
212 | |||
213 | if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1)) | ||
214 | return -EINVAL; | ||
215 | |||
216 | ret = set_wlan_state(state); | ||
217 | if (ret < 0) | ||
218 | return ret; | ||
219 | |||
220 | return count; | ||
221 | } | ||
222 | |||
223 | static ssize_t store_bluetooth_state(struct device *dev, | ||
224 | struct device_attribute *attr, const char *buf, size_t count) | ||
225 | { | ||
226 | int state, ret; | ||
227 | |||
228 | if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1)) | ||
229 | return -EINVAL; | ||
230 | |||
231 | ret = set_bluetooth_state(state); | ||
232 | if (ret < 0) | ||
233 | return ret; | ||
234 | |||
235 | return count; | ||
236 | } | ||
237 | |||
238 | static DEVICE_ATTR(bluetooth, 0644, show_bluetooth, store_bluetooth_state); | ||
239 | static DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan_state); | ||
240 | static DEVICE_ATTR(raw, 0444, show_raw, NULL); | ||
241 | |||
242 | static struct attribute *compal_attributes[] = { | ||
243 | &dev_attr_bluetooth.attr, | ||
244 | &dev_attr_wlan.attr, | ||
245 | &dev_attr_raw.attr, | ||
246 | NULL | ||
247 | }; | ||
248 | |||
249 | static struct attribute_group compal_attribute_group = { | ||
250 | .attrs = compal_attributes | ||
251 | }; | ||
252 | 178 | ||
253 | static struct platform_driver compal_driver = { | 179 | static struct platform_driver compal_driver = { |
254 | .driver = { | 180 | .driver = { |
@@ -257,8 +183,6 @@ static struct platform_driver compal_driver = { | |||
257 | } | 183 | } |
258 | }; | 184 | }; |
259 | 185 | ||
260 | static struct platform_device *compal_device; | ||
261 | |||
262 | /* Initialization */ | 186 | /* Initialization */ |
263 | 187 | ||
264 | static int dmi_check_cb(const struct dmi_system_id *id) | 188 | static int dmi_check_cb(const struct dmi_system_id *id) |
@@ -310,6 +234,47 @@ static struct dmi_system_id __initdata compal_dmi_table[] = { | |||
310 | }, | 234 | }, |
311 | .callback = dmi_check_cb | 235 | .callback = dmi_check_cb |
312 | }, | 236 | }, |
237 | { | ||
238 | .ident = "Dell Mini 9", | ||
239 | .matches = { | ||
240 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
241 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"), | ||
242 | }, | ||
243 | .callback = dmi_check_cb | ||
244 | }, | ||
245 | { | ||
246 | .ident = "Dell Mini 10", | ||
247 | .matches = { | ||
248 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
249 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"), | ||
250 | }, | ||
251 | .callback = dmi_check_cb | ||
252 | }, | ||
253 | { | ||
254 | .ident = "Dell Mini 10v", | ||
255 | .matches = { | ||
256 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
257 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"), | ||
258 | }, | ||
259 | .callback = dmi_check_cb | ||
260 | }, | ||
261 | { | ||
262 | .ident = "Dell Inspiron 11z", | ||
263 | .matches = { | ||
264 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
265 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"), | ||
266 | }, | ||
267 | .callback = dmi_check_cb | ||
268 | }, | ||
269 | { | ||
270 | .ident = "Dell Mini 12", | ||
271 | .matches = { | ||
272 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
273 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"), | ||
274 | }, | ||
275 | .callback = dmi_check_cb | ||
276 | }, | ||
277 | |||
313 | { } | 278 | { } |
314 | }; | 279 | }; |
315 | 280 | ||
@@ -348,23 +313,21 @@ static int __init compal_init(void) | |||
348 | 313 | ||
349 | ret = platform_device_add(compal_device); | 314 | ret = platform_device_add(compal_device); |
350 | if (ret) | 315 | if (ret) |
351 | goto fail_platform_device1; | 316 | goto fail_platform_device; |
352 | 317 | ||
353 | ret = sysfs_create_group(&compal_device->dev.kobj, | 318 | ret = setup_rfkill(); |
354 | &compal_attribute_group); | ||
355 | if (ret) | 319 | if (ret) |
356 | goto fail_platform_device2; | 320 | goto fail_rfkill; |
357 | 321 | ||
358 | printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION | 322 | printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION |
359 | " successfully loaded.\n"); | 323 | " successfully loaded.\n"); |
360 | 324 | ||
361 | return 0; | 325 | return 0; |
362 | 326 | ||
363 | fail_platform_device2: | 327 | fail_rfkill: |
364 | |||
365 | platform_device_del(compal_device); | 328 | platform_device_del(compal_device); |
366 | 329 | ||
367 | fail_platform_device1: | 330 | fail_platform_device: |
368 | 331 | ||
369 | platform_device_put(compal_device); | 332 | platform_device_put(compal_device); |
370 | 333 | ||
@@ -382,10 +345,13 @@ fail_backlight: | |||
382 | static void __exit compal_cleanup(void) | 345 | static void __exit compal_cleanup(void) |
383 | { | 346 | { |
384 | 347 | ||
385 | sysfs_remove_group(&compal_device->dev.kobj, &compal_attribute_group); | ||
386 | platform_device_unregister(compal_device); | 348 | platform_device_unregister(compal_device); |
387 | platform_driver_unregister(&compal_driver); | 349 | platform_driver_unregister(&compal_driver); |
388 | backlight_device_unregister(compalbl_device); | 350 | backlight_device_unregister(compalbl_device); |
351 | rfkill_unregister(wifi_rfkill); | ||
352 | rfkill_destroy(wifi_rfkill); | ||
353 | rfkill_unregister(bt_rfkill); | ||
354 | rfkill_destroy(bt_rfkill); | ||
389 | 355 | ||
390 | printk(KERN_INFO "compal-laptop: driver unloaded.\n"); | 356 | printk(KERN_INFO "compal-laptop: driver unloaded.\n"); |
391 | } | 357 | } |
@@ -403,3 +369,8 @@ MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*"); | |||
403 | MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*"); | 369 | MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*"); |
404 | MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*"); | 370 | MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*"); |
405 | MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*"); | 371 | MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*"); |
372 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron910:*"); | ||
373 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1010:*"); | ||
374 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1011:*"); | ||
375 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1110:*"); | ||
376 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1210:*"); | ||
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 3780994dc8f2..b7f4d2705916 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c | |||
@@ -22,6 +22,8 @@ | |||
22 | #include <linux/rfkill.h> | 22 | #include <linux/rfkill.h> |
23 | #include <linux/power_supply.h> | 23 | #include <linux/power_supply.h> |
24 | #include <linux/acpi.h> | 24 | #include <linux/acpi.h> |
25 | #include <linux/mm.h> | ||
26 | #include <linux/i8042.h> | ||
25 | #include "../../firmware/dcdbas.h" | 27 | #include "../../firmware/dcdbas.h" |
26 | 28 | ||
27 | #define BRIGHTNESS_TOKEN 0x7d | 29 | #define BRIGHTNESS_TOKEN 0x7d |
@@ -79,9 +81,73 @@ static const struct dmi_system_id __initdata dell_device_table[] = { | |||
79 | DMI_MATCH(DMI_CHASSIS_TYPE, "8"), | 81 | DMI_MATCH(DMI_CHASSIS_TYPE, "8"), |
80 | }, | 82 | }, |
81 | }, | 83 | }, |
84 | { | ||
85 | .ident = "Dell Computer Corporation", | ||
86 | .matches = { | ||
87 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), | ||
88 | DMI_MATCH(DMI_CHASSIS_TYPE, "8"), | ||
89 | }, | ||
90 | }, | ||
82 | { } | 91 | { } |
83 | }; | 92 | }; |
84 | 93 | ||
94 | static struct dmi_system_id __devinitdata dell_blacklist[] = { | ||
95 | /* Supported by compal-laptop */ | ||
96 | { | ||
97 | .ident = "Dell Mini 9", | ||
98 | .matches = { | ||
99 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
100 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"), | ||
101 | }, | ||
102 | }, | ||
103 | { | ||
104 | .ident = "Dell Mini 10", | ||
105 | .matches = { | ||
106 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
107 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"), | ||
108 | }, | ||
109 | }, | ||
110 | { | ||
111 | .ident = "Dell Mini 10v", | ||
112 | .matches = { | ||
113 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
114 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"), | ||
115 | }, | ||
116 | }, | ||
117 | { | ||
118 | .ident = "Dell Inspiron 11z", | ||
119 | .matches = { | ||
120 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
121 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"), | ||
122 | }, | ||
123 | }, | ||
124 | { | ||
125 | .ident = "Dell Mini 12", | ||
126 | .matches = { | ||
127 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
128 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"), | ||
129 | }, | ||
130 | }, | ||
131 | {} | ||
132 | }; | ||
133 | |||
134 | static struct calling_interface_buffer *buffer; | ||
135 | struct page *bufferpage; | ||
136 | DEFINE_MUTEX(buffer_mutex); | ||
137 | |||
138 | static int hwswitch_state; | ||
139 | |||
140 | static void get_buffer(void) | ||
141 | { | ||
142 | mutex_lock(&buffer_mutex); | ||
143 | memset(buffer, 0, sizeof(struct calling_interface_buffer)); | ||
144 | } | ||
145 | |||
146 | static void release_buffer(void) | ||
147 | { | ||
148 | mutex_unlock(&buffer_mutex); | ||
149 | } | ||
150 | |||
85 | static void __init parse_da_table(const struct dmi_header *dm) | 151 | static void __init parse_da_table(const struct dmi_header *dm) |
86 | { | 152 | { |
87 | /* Final token is a terminator, so we don't want to copy it */ | 153 | /* Final token is a terminator, so we don't want to copy it */ |
@@ -160,6 +226,8 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, | |||
160 | /* Derived from information in DellWirelessCtl.cpp: | 226 | /* Derived from information in DellWirelessCtl.cpp: |
161 | Class 17, select 11 is radio control. It returns an array of 32-bit values. | 227 | Class 17, select 11 is radio control. It returns an array of 32-bit values. |
162 | 228 | ||
229 | Input byte 0 = 0: Wireless information | ||
230 | |||
163 | result[0]: return code | 231 | result[0]: return code |
164 | result[1]: | 232 | result[1]: |
165 | Bit 0: Hardware switch supported | 233 | Bit 0: Hardware switch supported |
@@ -180,33 +248,62 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, | |||
180 | Bits 20-31: Reserved | 248 | Bits 20-31: Reserved |
181 | result[2]: NVRAM size in bytes | 249 | result[2]: NVRAM size in bytes |
182 | result[3]: NVRAM format version number | 250 | result[3]: NVRAM format version number |
251 | |||
252 | Input byte 0 = 2: Wireless switch configuration | ||
253 | result[0]: return code | ||
254 | result[1]: | ||
255 | Bit 0: Wifi controlled by switch | ||
256 | Bit 1: Bluetooth controlled by switch | ||
257 | Bit 2: WWAN controlled by switch | ||
258 | Bits 3-6: Reserved | ||
259 | Bit 7: Wireless switch config locked | ||
260 | Bit 8: Wifi locator enabled | ||
261 | Bits 9-14: Reserved | ||
262 | Bit 15: Wifi locator setting locked | ||
263 | Bits 16-31: Reserved | ||
183 | */ | 264 | */ |
184 | 265 | ||
185 | static int dell_rfkill_set(void *data, bool blocked) | 266 | static int dell_rfkill_set(void *data, bool blocked) |
186 | { | 267 | { |
187 | struct calling_interface_buffer buffer; | ||
188 | int disable = blocked ? 1 : 0; | 268 | int disable = blocked ? 1 : 0; |
189 | unsigned long radio = (unsigned long)data; | 269 | unsigned long radio = (unsigned long)data; |
270 | int hwswitch_bit = (unsigned long)data - 1; | ||
271 | int ret = 0; | ||
272 | |||
273 | get_buffer(); | ||
274 | dell_send_request(buffer, 17, 11); | ||
275 | |||
276 | /* If the hardware switch controls this radio, and the hardware | ||
277 | switch is disabled, don't allow changing the software state */ | ||
278 | if ((hwswitch_state & BIT(hwswitch_bit)) && | ||
279 | !(buffer->output[1] & BIT(16))) { | ||
280 | ret = -EINVAL; | ||
281 | goto out; | ||
282 | } | ||
190 | 283 | ||
191 | memset(&buffer, 0, sizeof(struct calling_interface_buffer)); | 284 | buffer->input[0] = (1 | (radio<<8) | (disable << 16)); |
192 | buffer.input[0] = (1 | (radio<<8) | (disable << 16)); | 285 | dell_send_request(buffer, 17, 11); |
193 | dell_send_request(&buffer, 17, 11); | ||
194 | 286 | ||
195 | return 0; | 287 | out: |
288 | release_buffer(); | ||
289 | return ret; | ||
196 | } | 290 | } |
197 | 291 | ||
198 | static void dell_rfkill_query(struct rfkill *rfkill, void *data) | 292 | static void dell_rfkill_query(struct rfkill *rfkill, void *data) |
199 | { | 293 | { |
200 | struct calling_interface_buffer buffer; | ||
201 | int status; | 294 | int status; |
202 | int bit = (unsigned long)data + 16; | 295 | int bit = (unsigned long)data + 16; |
296 | int hwswitch_bit = (unsigned long)data - 1; | ||
203 | 297 | ||
204 | memset(&buffer, 0, sizeof(struct calling_interface_buffer)); | 298 | get_buffer(); |
205 | dell_send_request(&buffer, 17, 11); | 299 | dell_send_request(buffer, 17, 11); |
206 | status = buffer.output[1]; | 300 | status = buffer->output[1]; |
301 | release_buffer(); | ||
207 | 302 | ||
208 | rfkill_set_sw_state(rfkill, !!(status & BIT(bit))); | 303 | rfkill_set_sw_state(rfkill, !!(status & BIT(bit))); |
209 | rfkill_set_hw_state(rfkill, !(status & BIT(16))); | 304 | |
305 | if (hwswitch_state & (BIT(hwswitch_bit))) | ||
306 | rfkill_set_hw_state(rfkill, !(status & BIT(16))); | ||
210 | } | 307 | } |
211 | 308 | ||
212 | static const struct rfkill_ops dell_rfkill_ops = { | 309 | static const struct rfkill_ops dell_rfkill_ops = { |
@@ -214,15 +311,36 @@ static const struct rfkill_ops dell_rfkill_ops = { | |||
214 | .query = dell_rfkill_query, | 311 | .query = dell_rfkill_query, |
215 | }; | 312 | }; |
216 | 313 | ||
314 | static void dell_update_rfkill(struct work_struct *ignored) | ||
315 | { | ||
316 | if (wifi_rfkill) | ||
317 | dell_rfkill_query(wifi_rfkill, (void *)1); | ||
318 | if (bluetooth_rfkill) | ||
319 | dell_rfkill_query(bluetooth_rfkill, (void *)2); | ||
320 | if (wwan_rfkill) | ||
321 | dell_rfkill_query(wwan_rfkill, (void *)3); | ||
322 | } | ||
323 | static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill); | ||
324 | |||
325 | |||
217 | static int __init dell_setup_rfkill(void) | 326 | static int __init dell_setup_rfkill(void) |
218 | { | 327 | { |
219 | struct calling_interface_buffer buffer; | ||
220 | int status; | 328 | int status; |
221 | int ret; | 329 | int ret; |
222 | 330 | ||
223 | memset(&buffer, 0, sizeof(struct calling_interface_buffer)); | 331 | if (dmi_check_system(dell_blacklist)) { |
224 | dell_send_request(&buffer, 17, 11); | 332 | printk(KERN_INFO "dell-laptop: Blacklisted hardware detected - " |
225 | status = buffer.output[1]; | 333 | "not enabling rfkill\n"); |
334 | return 0; | ||
335 | } | ||
336 | |||
337 | get_buffer(); | ||
338 | dell_send_request(buffer, 17, 11); | ||
339 | status = buffer->output[1]; | ||
340 | buffer->input[0] = 0x2; | ||
341 | dell_send_request(buffer, 17, 11); | ||
342 | hwswitch_state = buffer->output[1]; | ||
343 | release_buffer(); | ||
226 | 344 | ||
227 | if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { | 345 | if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { |
228 | wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev, | 346 | wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev, |
@@ -298,39 +416,49 @@ static void dell_cleanup_rfkill(void) | |||
298 | 416 | ||
299 | static int dell_send_intensity(struct backlight_device *bd) | 417 | static int dell_send_intensity(struct backlight_device *bd) |
300 | { | 418 | { |
301 | struct calling_interface_buffer buffer; | 419 | int ret = 0; |
302 | 420 | ||
303 | memset(&buffer, 0, sizeof(struct calling_interface_buffer)); | 421 | get_buffer(); |
304 | buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN); | 422 | buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); |
305 | buffer.input[1] = bd->props.brightness; | 423 | buffer->input[1] = bd->props.brightness; |
306 | 424 | ||
307 | if (buffer.input[0] == -1) | 425 | if (buffer->input[0] == -1) { |
308 | return -ENODEV; | 426 | ret = -ENODEV; |
427 | goto out; | ||
428 | } | ||
309 | 429 | ||
310 | if (power_supply_is_system_supplied() > 0) | 430 | if (power_supply_is_system_supplied() > 0) |
311 | dell_send_request(&buffer, 1, 2); | 431 | dell_send_request(buffer, 1, 2); |
312 | else | 432 | else |
313 | dell_send_request(&buffer, 1, 1); | 433 | dell_send_request(buffer, 1, 1); |
314 | 434 | ||
435 | out: | ||
436 | release_buffer(); | ||
315 | return 0; | 437 | return 0; |
316 | } | 438 | } |
317 | 439 | ||
318 | static int dell_get_intensity(struct backlight_device *bd) | 440 | static int dell_get_intensity(struct backlight_device *bd) |
319 | { | 441 | { |
320 | struct calling_interface_buffer buffer; | 442 | int ret = 0; |
321 | 443 | ||
322 | memset(&buffer, 0, sizeof(struct calling_interface_buffer)); | 444 | get_buffer(); |
323 | buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN); | 445 | buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); |
324 | 446 | ||
325 | if (buffer.input[0] == -1) | 447 | if (buffer->input[0] == -1) { |
326 | return -ENODEV; | 448 | ret = -ENODEV; |
449 | goto out; | ||
450 | } | ||
327 | 451 | ||
328 | if (power_supply_is_system_supplied() > 0) | 452 | if (power_supply_is_system_supplied() > 0) |
329 | dell_send_request(&buffer, 0, 2); | 453 | dell_send_request(buffer, 0, 2); |
330 | else | 454 | else |
331 | dell_send_request(&buffer, 0, 1); | 455 | dell_send_request(buffer, 0, 1); |
332 | 456 | ||
333 | return buffer.output[1]; | 457 | out: |
458 | release_buffer(); | ||
459 | if (ret) | ||
460 | return ret; | ||
461 | return buffer->output[1]; | ||
334 | } | 462 | } |
335 | 463 | ||
336 | static struct backlight_ops dell_ops = { | 464 | static struct backlight_ops dell_ops = { |
@@ -338,9 +466,32 @@ static struct backlight_ops dell_ops = { | |||
338 | .update_status = dell_send_intensity, | 466 | .update_status = dell_send_intensity, |
339 | }; | 467 | }; |
340 | 468 | ||
469 | bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, | ||
470 | struct serio *port) | ||
471 | { | ||
472 | static bool extended; | ||
473 | |||
474 | if (str & 0x20) | ||
475 | return false; | ||
476 | |||
477 | if (unlikely(data == 0xe0)) { | ||
478 | extended = true; | ||
479 | return false; | ||
480 | } else if (unlikely(extended)) { | ||
481 | switch (data) { | ||
482 | case 0x8: | ||
483 | schedule_delayed_work(&dell_rfkill_work, | ||
484 | round_jiffies_relative(HZ)); | ||
485 | break; | ||
486 | } | ||
487 | extended = false; | ||
488 | } | ||
489 | |||
490 | return false; | ||
491 | } | ||
492 | |||
341 | static int __init dell_init(void) | 493 | static int __init dell_init(void) |
342 | { | 494 | { |
343 | struct calling_interface_buffer buffer; | ||
344 | int max_intensity = 0; | 495 | int max_intensity = 0; |
345 | int ret; | 496 | int ret; |
346 | 497 | ||
@@ -366,6 +517,17 @@ static int __init dell_init(void) | |||
366 | if (ret) | 517 | if (ret) |
367 | goto fail_platform_device2; | 518 | goto fail_platform_device2; |
368 | 519 | ||
520 | /* | ||
521 | * Allocate buffer below 4GB for SMI data--only 32-bit physical addr | ||
522 | * is passed to SMI handler. | ||
523 | */ | ||
524 | bufferpage = alloc_page(GFP_KERNEL | GFP_DMA32); | ||
525 | |||
526 | if (!bufferpage) | ||
527 | goto fail_buffer; | ||
528 | buffer = page_address(bufferpage); | ||
529 | mutex_init(&buffer_mutex); | ||
530 | |||
369 | ret = dell_setup_rfkill(); | 531 | ret = dell_setup_rfkill(); |
370 | 532 | ||
371 | if (ret) { | 533 | if (ret) { |
@@ -373,6 +535,13 @@ static int __init dell_init(void) | |||
373 | goto fail_rfkill; | 535 | goto fail_rfkill; |
374 | } | 536 | } |
375 | 537 | ||
538 | ret = i8042_install_filter(dell_laptop_i8042_filter); | ||
539 | if (ret) { | ||
540 | printk(KERN_WARNING | ||
541 | "dell-laptop: Unable to install key filter\n"); | ||
542 | goto fail_filter; | ||
543 | } | ||
544 | |||
376 | #ifdef CONFIG_ACPI | 545 | #ifdef CONFIG_ACPI |
377 | /* In the event of an ACPI backlight being available, don't | 546 | /* In the event of an ACPI backlight being available, don't |
378 | * register the platform controller. | 547 | * register the platform controller. |
@@ -381,13 +550,13 @@ static int __init dell_init(void) | |||
381 | return 0; | 550 | return 0; |
382 | #endif | 551 | #endif |
383 | 552 | ||
384 | memset(&buffer, 0, sizeof(struct calling_interface_buffer)); | 553 | get_buffer(); |
385 | buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN); | 554 | buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); |
386 | 555 | if (buffer->input[0] != -1) { | |
387 | if (buffer.input[0] != -1) { | 556 | dell_send_request(buffer, 0, 2); |
388 | dell_send_request(&buffer, 0, 2); | 557 | max_intensity = buffer->output[3]; |
389 | max_intensity = buffer.output[3]; | ||
390 | } | 558 | } |
559 | release_buffer(); | ||
391 | 560 | ||
392 | if (max_intensity) { | 561 | if (max_intensity) { |
393 | dell_backlight_device = backlight_device_register( | 562 | dell_backlight_device = backlight_device_register( |
@@ -410,8 +579,12 @@ static int __init dell_init(void) | |||
410 | return 0; | 579 | return 0; |
411 | 580 | ||
412 | fail_backlight: | 581 | fail_backlight: |
582 | i8042_remove_filter(dell_laptop_i8042_filter); | ||
583 | fail_filter: | ||
413 | dell_cleanup_rfkill(); | 584 | dell_cleanup_rfkill(); |
414 | fail_rfkill: | 585 | fail_rfkill: |
586 | free_page((unsigned long)bufferpage); | ||
587 | fail_buffer: | ||
415 | platform_device_del(platform_device); | 588 | platform_device_del(platform_device); |
416 | fail_platform_device2: | 589 | fail_platform_device2: |
417 | platform_device_put(platform_device); | 590 | platform_device_put(platform_device); |
@@ -424,8 +597,16 @@ fail_platform_driver: | |||
424 | 597 | ||
425 | static void __exit dell_exit(void) | 598 | static void __exit dell_exit(void) |
426 | { | 599 | { |
600 | cancel_delayed_work_sync(&dell_rfkill_work); | ||
601 | i8042_remove_filter(dell_laptop_i8042_filter); | ||
427 | backlight_device_unregister(dell_backlight_device); | 602 | backlight_device_unregister(dell_backlight_device); |
428 | dell_cleanup_rfkill(); | 603 | dell_cleanup_rfkill(); |
604 | if (platform_device) { | ||
605 | platform_device_del(platform_device); | ||
606 | platform_driver_unregister(&platform_driver); | ||
607 | } | ||
608 | kfree(da_tokens); | ||
609 | free_page((unsigned long)buffer); | ||
429 | } | 610 | } |
430 | 611 | ||
431 | module_init(dell_init); | 612 | module_init(dell_init); |
@@ -435,3 +616,4 @@ MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); | |||
435 | MODULE_DESCRIPTION("Dell laptop driver"); | 616 | MODULE_DESCRIPTION("Dell laptop driver"); |
436 | MODULE_LICENSE("GPL"); | 617 | MODULE_LICENSE("GPL"); |
437 | MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*"); | 618 | MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*"); |
619 | MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*"); | ||
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index ad4c414dbfbc..3aa57da8b43b 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c | |||
@@ -89,6 +89,7 @@ static struct key_entry hp_wmi_keymap[] = { | |||
89 | {KE_KEY, 0x20e6, KEY_PROG1}, | 89 | {KE_KEY, 0x20e6, KEY_PROG1}, |
90 | {KE_KEY, 0x2142, KEY_MEDIA}, | 90 | {KE_KEY, 0x2142, KEY_MEDIA}, |
91 | {KE_KEY, 0x213b, KEY_INFO}, | 91 | {KE_KEY, 0x213b, KEY_INFO}, |
92 | {KE_KEY, 0x2169, KEY_DIRECTION}, | ||
92 | {KE_KEY, 0x231b, KEY_HELP}, | 93 | {KE_KEY, 0x231b, KEY_HELP}, |
93 | {KE_END, 0} | 94 | {KE_END, 0} |
94 | }; | 95 | }; |
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 77bf5d8f893a..26c211724acf 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c | |||
@@ -46,6 +46,7 @@ | |||
46 | #include <linux/backlight.h> | 46 | #include <linux/backlight.h> |
47 | #include <linux/platform_device.h> | 47 | #include <linux/platform_device.h> |
48 | #include <linux/rfkill.h> | 48 | #include <linux/rfkill.h> |
49 | #include <linux/input.h> | ||
49 | 50 | ||
50 | #include <asm/uaccess.h> | 51 | #include <asm/uaccess.h> |
51 | 52 | ||
@@ -62,9 +63,10 @@ MODULE_LICENSE("GPL"); | |||
62 | 63 | ||
63 | /* Toshiba ACPI method paths */ | 64 | /* Toshiba ACPI method paths */ |
64 | #define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM" | 65 | #define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM" |
65 | #define METHOD_HCI_1 "\\_SB_.VALD.GHCI" | 66 | #define TOSH_INTERFACE_1 "\\_SB_.VALD" |
66 | #define METHOD_HCI_2 "\\_SB_.VALZ.GHCI" | 67 | #define TOSH_INTERFACE_2 "\\_SB_.VALZ" |
67 | #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" | 68 | #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" |
69 | #define GHCI_METHOD ".GHCI" | ||
68 | 70 | ||
69 | /* Toshiba HCI interface definitions | 71 | /* Toshiba HCI interface definitions |
70 | * | 72 | * |
@@ -116,6 +118,36 @@ static const struct acpi_device_id toshiba_device_ids[] = { | |||
116 | }; | 118 | }; |
117 | MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); | 119 | MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); |
118 | 120 | ||
121 | struct key_entry { | ||
122 | char type; | ||
123 | u16 code; | ||
124 | u16 keycode; | ||
125 | }; | ||
126 | |||
127 | enum {KE_KEY, KE_END}; | ||
128 | |||
129 | static struct key_entry toshiba_acpi_keymap[] = { | ||
130 | {KE_KEY, 0x101, KEY_MUTE}, | ||
131 | {KE_KEY, 0x13b, KEY_COFFEE}, | ||
132 | {KE_KEY, 0x13c, KEY_BATTERY}, | ||
133 | {KE_KEY, 0x13d, KEY_SLEEP}, | ||
134 | {KE_KEY, 0x13e, KEY_SUSPEND}, | ||
135 | {KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE}, | ||
136 | {KE_KEY, 0x140, KEY_BRIGHTNESSDOWN}, | ||
137 | {KE_KEY, 0x141, KEY_BRIGHTNESSUP}, | ||
138 | {KE_KEY, 0x142, KEY_WLAN}, | ||
139 | {KE_KEY, 0x143, KEY_PROG1}, | ||
140 | {KE_KEY, 0xb05, KEY_PROG2}, | ||
141 | {KE_KEY, 0xb06, KEY_WWW}, | ||
142 | {KE_KEY, 0xb07, KEY_MAIL}, | ||
143 | {KE_KEY, 0xb30, KEY_STOP}, | ||
144 | {KE_KEY, 0xb31, KEY_PREVIOUSSONG}, | ||
145 | {KE_KEY, 0xb32, KEY_NEXTSONG}, | ||
146 | {KE_KEY, 0xb33, KEY_PLAYPAUSE}, | ||
147 | {KE_KEY, 0xb5a, KEY_MEDIA}, | ||
148 | {KE_END, 0, 0}, | ||
149 | }; | ||
150 | |||
119 | /* utility | 151 | /* utility |
120 | */ | 152 | */ |
121 | 153 | ||
@@ -251,6 +283,8 @@ static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result) | |||
251 | struct toshiba_acpi_dev { | 283 | struct toshiba_acpi_dev { |
252 | struct platform_device *p_dev; | 284 | struct platform_device *p_dev; |
253 | struct rfkill *bt_rfk; | 285 | struct rfkill *bt_rfk; |
286 | struct input_dev *hotkey_dev; | ||
287 | acpi_handle handle; | ||
254 | 288 | ||
255 | const char *bt_name; | 289 | const char *bt_name; |
256 | 290 | ||
@@ -711,8 +745,159 @@ static struct backlight_ops toshiba_backlight_data = { | |||
711 | .update_status = set_lcd_status, | 745 | .update_status = set_lcd_status, |
712 | }; | 746 | }; |
713 | 747 | ||
748 | static struct key_entry *toshiba_acpi_get_entry_by_scancode(int code) | ||
749 | { | ||
750 | struct key_entry *key; | ||
751 | |||
752 | for (key = toshiba_acpi_keymap; key->type != KE_END; key++) | ||
753 | if (code == key->code) | ||
754 | return key; | ||
755 | |||
756 | return NULL; | ||
757 | } | ||
758 | |||
759 | static struct key_entry *toshiba_acpi_get_entry_by_keycode(int code) | ||
760 | { | ||
761 | struct key_entry *key; | ||
762 | |||
763 | for (key = toshiba_acpi_keymap; key->type != KE_END; key++) | ||
764 | if (code == key->keycode && key->type == KE_KEY) | ||
765 | return key; | ||
766 | |||
767 | return NULL; | ||
768 | } | ||
769 | |||
770 | static int toshiba_acpi_getkeycode(struct input_dev *dev, int scancode, | ||
771 | int *keycode) | ||
772 | { | ||
773 | struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode); | ||
774 | |||
775 | if (key && key->type == KE_KEY) { | ||
776 | *keycode = key->keycode; | ||
777 | return 0; | ||
778 | } | ||
779 | |||
780 | return -EINVAL; | ||
781 | } | ||
782 | |||
783 | static int toshiba_acpi_setkeycode(struct input_dev *dev, int scancode, | ||
784 | int keycode) | ||
785 | { | ||
786 | struct key_entry *key; | ||
787 | int old_keycode; | ||
788 | |||
789 | if (keycode < 0 || keycode > KEY_MAX) | ||
790 | return -EINVAL; | ||
791 | |||
792 | key = toshiba_acpi_get_entry_by_scancode(scancode); | ||
793 | if (key && key->type == KE_KEY) { | ||
794 | old_keycode = key->keycode; | ||
795 | key->keycode = keycode; | ||
796 | set_bit(keycode, dev->keybit); | ||
797 | if (!toshiba_acpi_get_entry_by_keycode(old_keycode)) | ||
798 | clear_bit(old_keycode, dev->keybit); | ||
799 | return 0; | ||
800 | } | ||
801 | |||
802 | return -EINVAL; | ||
803 | } | ||
804 | |||
805 | static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) | ||
806 | { | ||
807 | u32 hci_result, value; | ||
808 | struct key_entry *key; | ||
809 | |||
810 | if (event != 0x80) | ||
811 | return; | ||
812 | do { | ||
813 | hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result); | ||
814 | if (hci_result == HCI_SUCCESS) { | ||
815 | if (value == 0x100) | ||
816 | continue; | ||
817 | else if (value & 0x80) { | ||
818 | key = toshiba_acpi_get_entry_by_scancode | ||
819 | (value & ~0x80); | ||
820 | if (!key) { | ||
821 | printk(MY_INFO "Unknown key %x\n", | ||
822 | value & ~0x80); | ||
823 | continue; | ||
824 | } | ||
825 | input_report_key(toshiba_acpi.hotkey_dev, | ||
826 | key->keycode, 1); | ||
827 | input_sync(toshiba_acpi.hotkey_dev); | ||
828 | input_report_key(toshiba_acpi.hotkey_dev, | ||
829 | key->keycode, 0); | ||
830 | input_sync(toshiba_acpi.hotkey_dev); | ||
831 | } | ||
832 | } else if (hci_result == HCI_NOT_SUPPORTED) { | ||
833 | /* This is a workaround for an unresolved issue on | ||
834 | * some machines where system events sporadically | ||
835 | * become disabled. */ | ||
836 | hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); | ||
837 | printk(MY_NOTICE "Re-enabled hotkeys\n"); | ||
838 | } | ||
839 | } while (hci_result != HCI_EMPTY); | ||
840 | } | ||
841 | |||
842 | static int toshiba_acpi_setup_keyboard(char *device) | ||
843 | { | ||
844 | acpi_status status; | ||
845 | acpi_handle handle; | ||
846 | int result; | ||
847 | const struct key_entry *key; | ||
848 | |||
849 | status = acpi_get_handle(NULL, device, &handle); | ||
850 | if (ACPI_FAILURE(status)) { | ||
851 | printk(MY_INFO "Unable to get notification device\n"); | ||
852 | return -ENODEV; | ||
853 | } | ||
854 | |||
855 | toshiba_acpi.handle = handle; | ||
856 | |||
857 | status = acpi_evaluate_object(handle, "ENAB", NULL, NULL); | ||
858 | if (ACPI_FAILURE(status)) { | ||
859 | printk(MY_INFO "Unable to enable hotkeys\n"); | ||
860 | return -ENODEV; | ||
861 | } | ||
862 | |||
863 | status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY, | ||
864 | toshiba_acpi_notify, NULL); | ||
865 | if (ACPI_FAILURE(status)) { | ||
866 | printk(MY_INFO "Unable to install hotkey notification\n"); | ||
867 | return -ENODEV; | ||
868 | } | ||
869 | |||
870 | toshiba_acpi.hotkey_dev = input_allocate_device(); | ||
871 | if (!toshiba_acpi.hotkey_dev) { | ||
872 | printk(MY_INFO "Unable to register input device\n"); | ||
873 | return -ENOMEM; | ||
874 | } | ||
875 | |||
876 | toshiba_acpi.hotkey_dev->name = "Toshiba input device"; | ||
877 | toshiba_acpi.hotkey_dev->phys = device; | ||
878 | toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST; | ||
879 | toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode; | ||
880 | toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode; | ||
881 | |||
882 | for (key = toshiba_acpi_keymap; key->type != KE_END; key++) { | ||
883 | set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit); | ||
884 | set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit); | ||
885 | } | ||
886 | |||
887 | result = input_register_device(toshiba_acpi.hotkey_dev); | ||
888 | if (result) { | ||
889 | printk(MY_INFO "Unable to register input device\n"); | ||
890 | return result; | ||
891 | } | ||
892 | |||
893 | return 0; | ||
894 | } | ||
895 | |||
714 | static void toshiba_acpi_exit(void) | 896 | static void toshiba_acpi_exit(void) |
715 | { | 897 | { |
898 | if (toshiba_acpi.hotkey_dev) | ||
899 | input_unregister_device(toshiba_acpi.hotkey_dev); | ||
900 | |||
716 | if (toshiba_acpi.bt_rfk) { | 901 | if (toshiba_acpi.bt_rfk) { |
717 | rfkill_unregister(toshiba_acpi.bt_rfk); | 902 | rfkill_unregister(toshiba_acpi.bt_rfk); |
718 | rfkill_destroy(toshiba_acpi.bt_rfk); | 903 | rfkill_destroy(toshiba_acpi.bt_rfk); |
@@ -726,6 +911,9 @@ static void toshiba_acpi_exit(void) | |||
726 | if (toshiba_proc_dir) | 911 | if (toshiba_proc_dir) |
727 | remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); | 912 | remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); |
728 | 913 | ||
914 | acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY, | ||
915 | toshiba_acpi_notify); | ||
916 | |||
729 | platform_device_unregister(toshiba_acpi.p_dev); | 917 | platform_device_unregister(toshiba_acpi.p_dev); |
730 | 918 | ||
731 | return; | 919 | return; |
@@ -742,11 +930,15 @@ static int __init toshiba_acpi_init(void) | |||
742 | return -ENODEV; | 930 | return -ENODEV; |
743 | 931 | ||
744 | /* simple device detection: look for HCI method */ | 932 | /* simple device detection: look for HCI method */ |
745 | if (is_valid_acpi_path(METHOD_HCI_1)) | 933 | if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) { |
746 | method_hci = METHOD_HCI_1; | 934 | method_hci = TOSH_INTERFACE_1 GHCI_METHOD; |
747 | else if (is_valid_acpi_path(METHOD_HCI_2)) | 935 | if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1)) |
748 | method_hci = METHOD_HCI_2; | 936 | printk(MY_INFO "Unable to activate hotkeys\n"); |
749 | else | 937 | } else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) { |
938 | method_hci = TOSH_INTERFACE_2 GHCI_METHOD; | ||
939 | if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2)) | ||
940 | printk(MY_INFO "Unable to activate hotkeys\n"); | ||
941 | } else | ||
750 | return -ENODEV; | 942 | return -ENODEV; |
751 | 943 | ||
752 | printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n", | 944 | printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n", |