aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2008-07-26 11:48:49 -0400
committerIngo Molnar <mingo@elte.hu>2008-07-26 11:48:49 -0400
commitc3cc99ff5d24e2eeaf7ec2032e720681916990e3 (patch)
treec3e74171bbbd2adde9d60b9db1c440415c8d2831 /drivers/misc
parent38ffbe66d59051fd9cfcfc8545f164700e2fa3bc (diff)
parent024e8ac04453b3525448c31ef39848cf675ba6db (diff)
Merge branch 'linus' into x86/xen
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig19
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/atmel_pwm.c3
-rw-r--r--drivers/misc/hp-wmi.c494
-rw-r--r--drivers/misc/phantom.c2
-rw-r--r--drivers/misc/thinkpad_acpi.c475
6 files changed, 848 insertions, 146 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index ce67d973d349..321eb9134635 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -77,11 +77,13 @@ config IBM_ASM
77 for your IBM server. 77 for your IBM server.
78 78
79config PHANTOM 79config PHANTOM
80 tristate "Sensable PHANToM" 80 tristate "Sensable PHANToM (PCI)"
81 depends on PCI 81 depends on PCI
82 help 82 help
83 Say Y here if you want to build a driver for Sensable PHANToM device. 83 Say Y here if you want to build a driver for Sensable PHANToM device.
84 84
85 This driver is only for PCI PHANToMs.
86
85 If you choose to build module, its name will be phantom. If unsure, 87 If you choose to build module, its name will be phantom. If unsure,
86 say N here. 88 say N here.
87 89
@@ -212,6 +214,18 @@ config TC1100_WMI
212 This is a driver for the WMI extensions (wireless and bluetooth power 214 This is a driver for the WMI extensions (wireless and bluetooth power
213 control) of the HP Compaq TC1100 tablet. 215 control) of the HP Compaq TC1100 tablet.
214 216
217config HP_WMI
218 tristate "HP WMI extras"
219 depends on ACPI_WMI
220 depends on INPUT
221 depends on RFKILL
222 help
223 Say Y here if you want to support WMI-based hotkeys on HP laptops and
224 to read data from WMI such as docking or ambient light sensor state.
225
226 To compile this driver as a module, choose M here: the module will
227 be called hp-wmi.
228
215config MSI_LAPTOP 229config MSI_LAPTOP
216 tristate "MSI Laptop Extras" 230 tristate "MSI Laptop Extras"
217 depends on X86 231 depends on X86
@@ -279,6 +293,8 @@ config THINKPAD_ACPI
279 select INPUT 293 select INPUT
280 select NEW_LEDS 294 select NEW_LEDS
281 select LEDS_CLASS 295 select LEDS_CLASS
296 select NET
297 select RFKILL
282 ---help--- 298 ---help---
283 This is a driver for the IBM and Lenovo ThinkPad laptops. It adds 299 This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
284 support for Fn-Fx key combinations, Bluetooth control, video 300 support for Fn-Fx key combinations, Bluetooth control, video
@@ -422,6 +438,7 @@ config SGI_XP
422 438
423config HP_ILO 439config HP_ILO
424 tristate "Channel interface driver for HP iLO/iLO2 processor" 440 tristate "Channel interface driver for HP iLO/iLO2 processor"
441 depends on PCI
425 default n 442 default n
426 help 443 help
427 The channel interface driver allows applications to communicate 444 The channel interface driver allows applications to communicate
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 688fe76135e0..f5e273420c09 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_ACER_WMI) += acer-wmi.o
13obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o 13obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o
14obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o 14obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
15obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o 15obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
16obj-$(CONFIG_HP_WMI) += hp-wmi.o
16obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o 17obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
17obj-$(CONFIG_LKDTM) += lkdtm.o 18obj-$(CONFIG_LKDTM) += lkdtm.o
18obj-$(CONFIG_TIFM_CORE) += tifm_core.o 19obj-$(CONFIG_TIFM_CORE) += tifm_core.o
diff --git a/drivers/misc/atmel_pwm.c b/drivers/misc/atmel_pwm.c
index 5b5a14dab3d3..6aa5294dfec4 100644
--- a/drivers/misc/atmel_pwm.c
+++ b/drivers/misc/atmel_pwm.c
@@ -211,8 +211,7 @@ int pwm_clk_alloc(unsigned prescale, unsigned div)
211 if ((mr & 0xffff) == 0) { 211 if ((mr & 0xffff) == 0) {
212 mr |= val; 212 mr |= val;
213 ret = PWM_CPR_CLKA; 213 ret = PWM_CPR_CLKA;
214 } 214 } else if ((mr & (0xffff << 16)) == 0) {
215 if ((mr & (0xffff << 16)) == 0) {
216 mr |= val << 16; 215 mr |= val << 16;
217 ret = PWM_CPR_CLKB; 216 ret = PWM_CPR_CLKB;
218 } 217 }
diff --git a/drivers/misc/hp-wmi.c b/drivers/misc/hp-wmi.c
new file mode 100644
index 000000000000..1dbcbcb323a2
--- /dev/null
+++ b/drivers/misc/hp-wmi.c
@@ -0,0 +1,494 @@
1/*
2 * HP WMI hotkeys
3 *
4 * Copyright (C) 2008 Red Hat <mjg@redhat.com>
5 *
6 * Portions based on wistron_btns.c:
7 * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
8 * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
9 * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26#include <linux/kernel.h>
27#include <linux/module.h>
28#include <linux/init.h>
29#include <linux/types.h>
30#include <linux/input.h>
31#include <acpi/acpi_drivers.h>
32#include <linux/platform_device.h>
33#include <linux/acpi.h>
34#include <linux/rfkill.h>
35#include <linux/string.h>
36
37MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
38MODULE_DESCRIPTION("HP laptop WMI hotkeys driver");
39MODULE_LICENSE("GPL");
40
41MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
42MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
43
44#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
45#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
46
47#define HPWMI_DISPLAY_QUERY 0x1
48#define HPWMI_HDDTEMP_QUERY 0x2
49#define HPWMI_ALS_QUERY 0x3
50#define HPWMI_DOCK_QUERY 0x4
51#define HPWMI_WIRELESS_QUERY 0x5
52
53static int __init hp_wmi_bios_setup(struct platform_device *device);
54static int __exit hp_wmi_bios_remove(struct platform_device *device);
55
56struct bios_args {
57 u32 signature;
58 u32 command;
59 u32 commandtype;
60 u32 datasize;
61 u32 data;
62};
63
64struct bios_return {
65 u32 sigpass;
66 u32 return_code;
67 u32 value;
68};
69
70struct key_entry {
71 char type; /* See KE_* below */
72 u8 code;
73 u16 keycode;
74};
75
76enum { KE_KEY, KE_SW, KE_END };
77
78static struct key_entry hp_wmi_keymap[] = {
79 {KE_SW, 0x01, SW_DOCK},
80 {KE_KEY, 0x02, KEY_BRIGHTNESSUP},
81 {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
82 {KE_KEY, 0x04, KEY_HELP},
83 {KE_END, 0}
84};
85
86static struct input_dev *hp_wmi_input_dev;
87static struct platform_device *hp_wmi_platform_dev;
88
89static struct rfkill *wifi_rfkill;
90static struct rfkill *bluetooth_rfkill;
91static struct rfkill *wwan_rfkill;
92
93static struct platform_driver hp_wmi_driver = {
94 .driver = {
95 .name = "hp-wmi",
96 .owner = THIS_MODULE,
97 },
98 .probe = hp_wmi_bios_setup,
99 .remove = hp_wmi_bios_remove,
100};
101
102static int hp_wmi_perform_query(int query, int write, int value)
103{
104 struct bios_return bios_return;
105 acpi_status status;
106 union acpi_object *obj;
107 struct bios_args args = {
108 .signature = 0x55434553,
109 .command = write ? 0x2 : 0x1,
110 .commandtype = query,
111 .datasize = write ? 0x4 : 0,
112 .data = value,
113 };
114 struct acpi_buffer input = { sizeof(struct bios_args), &args };
115 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
116
117 status = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
118
119 obj = output.pointer;
120
121 if (!obj || obj->type != ACPI_TYPE_BUFFER)
122 return -EINVAL;
123
124 bios_return = *((struct bios_return *)obj->buffer.pointer);
125 if (bios_return.return_code > 0)
126 return bios_return.return_code * -1;
127 else
128 return bios_return.value;
129}
130
131static int hp_wmi_display_state(void)
132{
133 return hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, 0);
134}
135
136static int hp_wmi_hddtemp_state(void)
137{
138 return hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, 0);
139}
140
141static int hp_wmi_als_state(void)
142{
143 return hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, 0);
144}
145
146static int hp_wmi_dock_state(void)
147{
148 return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0);
149}
150
151static int hp_wmi_wifi_set(void *data, enum rfkill_state state)
152{
153 if (state)
154 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x101);
155 else
156 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x100);
157}
158
159static int hp_wmi_bluetooth_set(void *data, enum rfkill_state state)
160{
161 if (state)
162 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x202);
163 else
164 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x200);
165}
166
167static int hp_wmi_wwan_set(void *data, enum rfkill_state state)
168{
169 if (state)
170 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x404);
171 else
172 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x400);
173}
174
175static int hp_wmi_wifi_state(void)
176{
177 int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
178
179 if (wireless & 0x100)
180 return 1;
181 else
182 return 0;
183}
184
185static int hp_wmi_bluetooth_state(void)
186{
187 int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
188
189 if (wireless & 0x10000)
190 return 1;
191 else
192 return 0;
193}
194
195static int hp_wmi_wwan_state(void)
196{
197 int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
198
199 if (wireless & 0x1000000)
200 return 1;
201 else
202 return 0;
203}
204
205static ssize_t show_display(struct device *dev, struct device_attribute *attr,
206 char *buf)
207{
208 int value = hp_wmi_display_state();
209 if (value < 0)
210 return -EINVAL;
211 return sprintf(buf, "%d\n", value);
212}
213
214static ssize_t show_hddtemp(struct device *dev, struct device_attribute *attr,
215 char *buf)
216{
217 int value = hp_wmi_hddtemp_state();
218 if (value < 0)
219 return -EINVAL;
220 return sprintf(buf, "%d\n", value);
221}
222
223static ssize_t show_als(struct device *dev, struct device_attribute *attr,
224 char *buf)
225{
226 int value = hp_wmi_als_state();
227 if (value < 0)
228 return -EINVAL;
229 return sprintf(buf, "%d\n", value);
230}
231
232static ssize_t show_dock(struct device *dev, struct device_attribute *attr,
233 char *buf)
234{
235 int value = hp_wmi_dock_state();
236 if (value < 0)
237 return -EINVAL;
238 return sprintf(buf, "%d\n", value);
239}
240
241static ssize_t set_als(struct device *dev, struct device_attribute *attr,
242 const char *buf, size_t count)
243{
244 u32 tmp = simple_strtoul(buf, NULL, 10);
245 hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, tmp);
246 return count;
247}
248
249static DEVICE_ATTR(display, S_IRUGO, show_display, NULL);
250static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL);
251static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
252static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
253
254static struct key_entry *hp_wmi_get_entry_by_scancode(int code)
255{
256 struct key_entry *key;
257
258 for (key = hp_wmi_keymap; key->type != KE_END; key++)
259 if (code == key->code)
260 return key;
261
262 return NULL;
263}
264
265static struct key_entry *hp_wmi_get_entry_by_keycode(int keycode)
266{
267 struct key_entry *key;
268
269 for (key = hp_wmi_keymap; key->type != KE_END; key++)
270 if (key->type == KE_KEY && keycode == key->keycode)
271 return key;
272
273 return NULL;
274}
275
276static int hp_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode)
277{
278 struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode);
279
280 if (key && key->type == KE_KEY) {
281 *keycode = key->keycode;
282 return 0;
283 }
284
285 return -EINVAL;
286}
287
288static int hp_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode)
289{
290 struct key_entry *key;
291 int old_keycode;
292
293 if (keycode < 0 || keycode > KEY_MAX)
294 return -EINVAL;
295
296 key = hp_wmi_get_entry_by_scancode(scancode);
297 if (key && key->type == KE_KEY) {
298 old_keycode = key->keycode;
299 key->keycode = keycode;
300 set_bit(keycode, dev->keybit);
301 if (!hp_wmi_get_entry_by_keycode(old_keycode))
302 clear_bit(old_keycode, dev->keybit);
303 return 0;
304 }
305
306 return -EINVAL;
307}
308
309void hp_wmi_notify(u32 value, void *context)
310{
311 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
312 static struct key_entry *key;
313 union acpi_object *obj;
314
315 wmi_get_event_data(value, &response);
316
317 obj = (union acpi_object *)response.pointer;
318
319 if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == 8) {
320 int eventcode = *((u8 *) obj->buffer.pointer);
321 key = hp_wmi_get_entry_by_scancode(eventcode);
322 if (key) {
323 switch (key->type) {
324 case KE_KEY:
325 input_report_key(hp_wmi_input_dev,
326 key->keycode, 1);
327 input_sync(hp_wmi_input_dev);
328 input_report_key(hp_wmi_input_dev,
329 key->keycode, 0);
330 input_sync(hp_wmi_input_dev);
331 break;
332 case KE_SW:
333 input_report_switch(hp_wmi_input_dev,
334 key->keycode,
335 hp_wmi_dock_state());
336 input_sync(hp_wmi_input_dev);
337 break;
338 }
339 } else if (eventcode == 0x5) {
340 if (wifi_rfkill)
341 wifi_rfkill->state = hp_wmi_wifi_state();
342 if (bluetooth_rfkill)
343 bluetooth_rfkill->state =
344 hp_wmi_bluetooth_state();
345 if (wwan_rfkill)
346 wwan_rfkill->state = hp_wmi_wwan_state();
347 } else
348 printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n",
349 eventcode);
350 } else
351 printk(KERN_INFO "HP WMI: Unknown response received\n");
352}
353
354static int __init hp_wmi_input_setup(void)
355{
356 struct key_entry *key;
357 int err;
358
359 hp_wmi_input_dev = input_allocate_device();
360
361 hp_wmi_input_dev->name = "HP WMI hotkeys";
362 hp_wmi_input_dev->phys = "wmi/input0";
363 hp_wmi_input_dev->id.bustype = BUS_HOST;
364 hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode;
365 hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode;
366
367 for (key = hp_wmi_keymap; key->type != KE_END; key++) {
368 switch (key->type) {
369 case KE_KEY:
370 set_bit(EV_KEY, hp_wmi_input_dev->evbit);
371 set_bit(key->keycode, hp_wmi_input_dev->keybit);
372 break;
373 case KE_SW:
374 set_bit(EV_SW, hp_wmi_input_dev->evbit);
375 set_bit(key->keycode, hp_wmi_input_dev->swbit);
376 break;
377 }
378 }
379
380 err = input_register_device(hp_wmi_input_dev);
381
382 if (err) {
383 input_free_device(hp_wmi_input_dev);
384 return err;
385 }
386
387 return 0;
388}
389
390static void cleanup_sysfs(struct platform_device *device)
391{
392 device_remove_file(&device->dev, &dev_attr_display);
393 device_remove_file(&device->dev, &dev_attr_hddtemp);
394 device_remove_file(&device->dev, &dev_attr_als);
395 device_remove_file(&device->dev, &dev_attr_dock);
396}
397
398static int __init hp_wmi_bios_setup(struct platform_device *device)
399{
400 int err;
401
402 err = device_create_file(&device->dev, &dev_attr_display);
403 if (err)
404 goto add_sysfs_error;
405 err = device_create_file(&device->dev, &dev_attr_hddtemp);
406 if (err)
407 goto add_sysfs_error;
408 err = device_create_file(&device->dev, &dev_attr_als);
409 if (err)
410 goto add_sysfs_error;
411 err = device_create_file(&device->dev, &dev_attr_dock);
412 if (err)
413 goto add_sysfs_error;
414
415 wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN);
416 wifi_rfkill->name = "hp-wifi";
417 wifi_rfkill->state = hp_wmi_wifi_state();
418 wifi_rfkill->toggle_radio = hp_wmi_wifi_set;
419 wifi_rfkill->user_claim_unsupported = 1;
420
421 bluetooth_rfkill = rfkill_allocate(&device->dev,
422 RFKILL_TYPE_BLUETOOTH);
423 bluetooth_rfkill->name = "hp-bluetooth";
424 bluetooth_rfkill->state = hp_wmi_bluetooth_state();
425 bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set;
426 bluetooth_rfkill->user_claim_unsupported = 1;
427
428 wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WIMAX);
429 wwan_rfkill->name = "hp-wwan";
430 wwan_rfkill->state = hp_wmi_wwan_state();
431 wwan_rfkill->toggle_radio = hp_wmi_wwan_set;
432 wwan_rfkill->user_claim_unsupported = 1;
433
434 rfkill_register(wifi_rfkill);
435 rfkill_register(bluetooth_rfkill);
436 rfkill_register(wwan_rfkill);
437
438 return 0;
439add_sysfs_error:
440 cleanup_sysfs(device);
441 return err;
442}
443
444static int __exit hp_wmi_bios_remove(struct platform_device *device)
445{
446 cleanup_sysfs(device);
447
448 rfkill_unregister(wifi_rfkill);
449 rfkill_unregister(bluetooth_rfkill);
450 rfkill_unregister(wwan_rfkill);
451
452 return 0;
453}
454
455static int __init hp_wmi_init(void)
456{
457 int err;
458
459 if (wmi_has_guid(HPWMI_EVENT_GUID)) {
460 err = wmi_install_notify_handler(HPWMI_EVENT_GUID,
461 hp_wmi_notify, NULL);
462 if (!err)
463 hp_wmi_input_setup();
464 }
465
466 if (wmi_has_guid(HPWMI_BIOS_GUID)) {
467 err = platform_driver_register(&hp_wmi_driver);
468 if (err)
469 return 0;
470 hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1);
471 if (!hp_wmi_platform_dev) {
472 platform_driver_unregister(&hp_wmi_driver);
473 return 0;
474 }
475 platform_device_add(hp_wmi_platform_dev);
476 }
477
478 return 0;
479}
480
481static void __exit hp_wmi_exit(void)
482{
483 if (wmi_has_guid(HPWMI_EVENT_GUID)) {
484 wmi_remove_notify_handler(HPWMI_EVENT_GUID);
485 input_unregister_device(hp_wmi_input_dev);
486 }
487 if (hp_wmi_platform_dev) {
488 platform_device_del(hp_wmi_platform_dev);
489 platform_driver_unregister(&hp_wmi_driver);
490 }
491}
492
493module_init(hp_wmi_init);
494module_exit(hp_wmi_exit);
diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c
index 4ce3bdc2f959..daf585689ce3 100644
--- a/drivers/misc/phantom.c
+++ b/drivers/misc/phantom.c
@@ -563,6 +563,6 @@ module_init(phantom_init);
563module_exit(phantom_exit); 563module_exit(phantom_exit);
564 564
565MODULE_AUTHOR("Jiri Slaby <jirislaby@gmail.com>"); 565MODULE_AUTHOR("Jiri Slaby <jirislaby@gmail.com>");
566MODULE_DESCRIPTION("Sensable Phantom driver"); 566MODULE_DESCRIPTION("Sensable Phantom driver (PCI devices)");
567MODULE_LICENSE("GPL"); 567MODULE_LICENSE("GPL");
568MODULE_VERSION(PHANTOM_VERSION); 568MODULE_VERSION(PHANTOM_VERSION);
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index b5969298f3d3..d3eb7903c346 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -21,7 +21,7 @@
21 * 02110-1301, USA. 21 * 02110-1301, USA.
22 */ 22 */
23 23
24#define TPACPI_VERSION "0.20" 24#define TPACPI_VERSION "0.21"
25#define TPACPI_SYSFS_VERSION 0x020200 25#define TPACPI_SYSFS_VERSION 0x020200
26 26
27/* 27/*
@@ -68,6 +68,7 @@
68#include <linux/hwmon-sysfs.h> 68#include <linux/hwmon-sysfs.h>
69#include <linux/input.h> 69#include <linux/input.h>
70#include <linux/leds.h> 70#include <linux/leds.h>
71#include <linux/rfkill.h>
71#include <asm/uaccess.h> 72#include <asm/uaccess.h>
72 73
73#include <linux/dmi.h> 74#include <linux/dmi.h>
@@ -144,6 +145,12 @@ enum {
144 145
145#define TPACPI_MAX_ACPI_ARGS 3 146#define TPACPI_MAX_ACPI_ARGS 3
146 147
148/* rfkill switches */
149enum {
150 TPACPI_RFK_BLUETOOTH_SW_ID = 0,
151 TPACPI_RFK_WWAN_SW_ID,
152};
153
147/* Debugging */ 154/* Debugging */
148#define TPACPI_LOG TPACPI_FILE ": " 155#define TPACPI_LOG TPACPI_FILE ": "
149#define TPACPI_ERR KERN_ERR TPACPI_LOG 156#define TPACPI_ERR KERN_ERR TPACPI_LOG
@@ -905,6 +912,43 @@ static int __init tpacpi_check_std_acpi_brightness_support(void)
905 return 0; 912 return 0;
906} 913}
907 914
915static int __init tpacpi_new_rfkill(const unsigned int id,
916 struct rfkill **rfk,
917 const enum rfkill_type rfktype,
918 const char *name,
919 int (*toggle_radio)(void *, enum rfkill_state),
920 int (*get_state)(void *, enum rfkill_state *))
921{
922 int res;
923 enum rfkill_state initial_state;
924
925 *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype);
926 if (!*rfk) {
927 printk(TPACPI_ERR
928 "failed to allocate memory for rfkill class\n");
929 return -ENOMEM;
930 }
931
932 (*rfk)->name = name;
933 (*rfk)->get_state = get_state;
934 (*rfk)->toggle_radio = toggle_radio;
935
936 if (!get_state(NULL, &initial_state))
937 (*rfk)->state = initial_state;
938
939 res = rfkill_register(*rfk);
940 if (res < 0) {
941 printk(TPACPI_ERR
942 "failed to register %s rfkill switch: %d\n",
943 name, res);
944 rfkill_free(*rfk);
945 *rfk = NULL;
946 return res;
947 }
948
949 return 0;
950}
951
908/************************************************************************* 952/*************************************************************************
909 * thinkpad-acpi driver attributes 953 * thinkpad-acpi driver attributes
910 */ 954 */
@@ -1285,21 +1329,6 @@ static int hotkey_status_set(int status)
1285 return 0; 1329 return 0;
1286} 1330}
1287 1331
1288static void tpacpi_input_send_radiosw(void)
1289{
1290 int wlsw;
1291
1292 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) {
1293 mutex_lock(&tpacpi_inputdev_send_mutex);
1294
1295 input_report_switch(tpacpi_inputdev,
1296 SW_RFKILL_ALL, !!wlsw);
1297 input_sync(tpacpi_inputdev);
1298
1299 mutex_unlock(&tpacpi_inputdev_send_mutex);
1300 }
1301}
1302
1303static void tpacpi_input_send_tabletsw(void) 1332static void tpacpi_input_send_tabletsw(void)
1304{ 1333{
1305 int state; 1334 int state;
@@ -1921,6 +1950,30 @@ static struct attribute *hotkey_mask_attributes[] __initdata = {
1921 &dev_attr_hotkey_wakeup_hotunplug_complete.attr, 1950 &dev_attr_hotkey_wakeup_hotunplug_complete.attr,
1922}; 1951};
1923 1952
1953static void bluetooth_update_rfk(void);
1954static void wan_update_rfk(void);
1955static void tpacpi_send_radiosw_update(void)
1956{
1957 int wlsw;
1958
1959 /* Sync these BEFORE sending any rfkill events */
1960 if (tp_features.bluetooth)
1961 bluetooth_update_rfk();
1962 if (tp_features.wan)
1963 wan_update_rfk();
1964
1965 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) {
1966 mutex_lock(&tpacpi_inputdev_send_mutex);
1967
1968 input_report_switch(tpacpi_inputdev,
1969 SW_RFKILL_ALL, !!wlsw);
1970 input_sync(tpacpi_inputdev);
1971
1972 mutex_unlock(&tpacpi_inputdev_send_mutex);
1973 }
1974 hotkey_radio_sw_notify_change();
1975}
1976
1924static void hotkey_exit(void) 1977static void hotkey_exit(void)
1925{ 1978{
1926#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL 1979#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
@@ -2167,9 +2220,10 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
2167 printk(TPACPI_INFO 2220 printk(TPACPI_INFO
2168 "radio switch found; radios are %s\n", 2221 "radio switch found; radios are %s\n",
2169 enabled(status, 0)); 2222 enabled(status, 0));
2223 }
2224 if (tp_features.hotkey_wlsw)
2170 res = add_to_attr_set(hotkey_dev_attributes, 2225 res = add_to_attr_set(hotkey_dev_attributes,
2171 &dev_attr_hotkey_radio_sw.attr); 2226 &dev_attr_hotkey_radio_sw.attr);
2172 }
2173 2227
2174 /* For X41t, X60t, X61t Tablets... */ 2228 /* For X41t, X60t, X61t Tablets... */
2175 if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { 2229 if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) {
@@ -2287,7 +2341,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
2287 tpacpi_inputdev->close = &hotkey_inputdev_close; 2341 tpacpi_inputdev->close = &hotkey_inputdev_close;
2288 2342
2289 hotkey_poll_setup_safe(1); 2343 hotkey_poll_setup_safe(1);
2290 tpacpi_input_send_radiosw(); 2344 tpacpi_send_radiosw_update();
2291 tpacpi_input_send_tabletsw(); 2345 tpacpi_input_send_tabletsw();
2292 2346
2293 return 0; 2347 return 0;
@@ -2419,8 +2473,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
2419 case 7: 2473 case 7:
2420 /* 0x7000-0x7FFF: misc */ 2474 /* 0x7000-0x7FFF: misc */
2421 if (tp_features.hotkey_wlsw && hkey == 0x7000) { 2475 if (tp_features.hotkey_wlsw && hkey == 0x7000) {
2422 tpacpi_input_send_radiosw(); 2476 tpacpi_send_radiosw_update();
2423 hotkey_radio_sw_notify_change();
2424 send_acpi_ev = 0; 2477 send_acpi_ev = 0;
2425 break; 2478 break;
2426 } 2479 }
@@ -2463,8 +2516,7 @@ static void hotkey_resume(void)
2463 printk(TPACPI_ERR 2516 printk(TPACPI_ERR
2464 "error while trying to read hot key mask " 2517 "error while trying to read hot key mask "
2465 "from firmware\n"); 2518 "from firmware\n");
2466 tpacpi_input_send_radiosw(); 2519 tpacpi_send_radiosw_update();
2467 hotkey_radio_sw_notify_change();
2468 hotkey_tablet_mode_notify_change(); 2520 hotkey_tablet_mode_notify_change();
2469 hotkey_wakeup_reason_notify_change(); 2521 hotkey_wakeup_reason_notify_change();
2470 hotkey_wakeup_hotunplug_complete_notify_change(); 2522 hotkey_wakeup_hotunplug_complete_notify_change();
@@ -2581,8 +2633,66 @@ enum {
2581 TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */ 2633 TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */
2582}; 2634};
2583 2635
2584static int bluetooth_get_radiosw(void); 2636static struct rfkill *tpacpi_bluetooth_rfkill;
2585static int bluetooth_set_radiosw(int radio_on); 2637
2638static int bluetooth_get_radiosw(void)
2639{
2640 int status;
2641
2642 if (!tp_features.bluetooth)
2643 return -ENODEV;
2644
2645 /* WLSW overrides bluetooth in firmware/hardware, reflect that */
2646 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
2647 return RFKILL_STATE_HARD_BLOCKED;
2648
2649 if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
2650 return -EIO;
2651
2652 return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ?
2653 RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
2654}
2655
2656static void bluetooth_update_rfk(void)
2657{
2658 int status;
2659
2660 if (!tpacpi_bluetooth_rfkill)
2661 return;
2662
2663 status = bluetooth_get_radiosw();
2664 if (status < 0)
2665 return;
2666 rfkill_force_state(tpacpi_bluetooth_rfkill, status);
2667}
2668
2669static int bluetooth_set_radiosw(int radio_on, int update_rfk)
2670{
2671 int status;
2672
2673 if (!tp_features.bluetooth)
2674 return -ENODEV;
2675
2676 /* WLSW overrides bluetooth in firmware/hardware, but there is no
2677 * reason to risk weird behaviour. */
2678 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status
2679 && radio_on)
2680 return -EPERM;
2681
2682 if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
2683 return -EIO;
2684 if (radio_on)
2685 status |= TP_ACPI_BLUETOOTH_RADIOSSW;
2686 else
2687 status &= ~TP_ACPI_BLUETOOTH_RADIOSSW;
2688 if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
2689 return -EIO;
2690
2691 if (update_rfk)
2692 bluetooth_update_rfk();
2693
2694 return 0;
2695}
2586 2696
2587/* sysfs bluetooth enable ---------------------------------------------- */ 2697/* sysfs bluetooth enable ---------------------------------------------- */
2588static ssize_t bluetooth_enable_show(struct device *dev, 2698static ssize_t bluetooth_enable_show(struct device *dev,
@@ -2595,7 +2705,8 @@ static ssize_t bluetooth_enable_show(struct device *dev,
2595 if (status < 0) 2705 if (status < 0)
2596 return status; 2706 return status;
2597 2707
2598 return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); 2708 return snprintf(buf, PAGE_SIZE, "%d\n",
2709 (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
2599} 2710}
2600 2711
2601static ssize_t bluetooth_enable_store(struct device *dev, 2712static ssize_t bluetooth_enable_store(struct device *dev,
@@ -2608,7 +2719,7 @@ static ssize_t bluetooth_enable_store(struct device *dev,
2608 if (parse_strtoul(buf, 1, &t)) 2719 if (parse_strtoul(buf, 1, &t))
2609 return -EINVAL; 2720 return -EINVAL;
2610 2721
2611 res = bluetooth_set_radiosw(t); 2722 res = bluetooth_set_radiosw(t, 1);
2612 2723
2613 return (res) ? res : count; 2724 return (res) ? res : count;
2614} 2725}
@@ -2628,6 +2739,31 @@ static const struct attribute_group bluetooth_attr_group = {
2628 .attrs = bluetooth_attributes, 2739 .attrs = bluetooth_attributes,
2629}; 2740};
2630 2741
2742static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state)
2743{
2744 int bts = bluetooth_get_radiosw();
2745
2746 if (bts < 0)
2747 return bts;
2748
2749 *state = bts;
2750 return 0;
2751}
2752
2753static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state)
2754{
2755 return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
2756}
2757
2758static void bluetooth_exit(void)
2759{
2760 if (tpacpi_bluetooth_rfkill)
2761 rfkill_unregister(tpacpi_bluetooth_rfkill);
2762
2763 sysfs_remove_group(&tpacpi_pdev->dev.kobj,
2764 &bluetooth_attr_group);
2765}
2766
2631static int __init bluetooth_init(struct ibm_init_struct *iibm) 2767static int __init bluetooth_init(struct ibm_init_struct *iibm)
2632{ 2768{
2633 int res; 2769 int res;
@@ -2646,57 +2782,32 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)
2646 str_supported(tp_features.bluetooth), 2782 str_supported(tp_features.bluetooth),
2647 status); 2783 status);
2648 2784
2649 if (tp_features.bluetooth) { 2785 if (tp_features.bluetooth &&
2650 if (!(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { 2786 !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) {
2651 /* no bluetooth hardware present in system */ 2787 /* no bluetooth hardware present in system */
2652 tp_features.bluetooth = 0; 2788 tp_features.bluetooth = 0;
2653 dbg_printk(TPACPI_DBG_INIT, 2789 dbg_printk(TPACPI_DBG_INIT,
2654 "bluetooth hardware not installed\n"); 2790 "bluetooth hardware not installed\n");
2655 } else {
2656 res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
2657 &bluetooth_attr_group);
2658 if (res)
2659 return res;
2660 }
2661 } 2791 }
2662 2792
2663 return (tp_features.bluetooth)? 0 : 1;
2664}
2665
2666static void bluetooth_exit(void)
2667{
2668 sysfs_remove_group(&tpacpi_pdev->dev.kobj,
2669 &bluetooth_attr_group);
2670}
2671
2672static int bluetooth_get_radiosw(void)
2673{
2674 int status;
2675
2676 if (!tp_features.bluetooth) 2793 if (!tp_features.bluetooth)
2677 return -ENODEV; 2794 return 1;
2678
2679 if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
2680 return -EIO;
2681
2682 return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0);
2683}
2684
2685static int bluetooth_set_radiosw(int radio_on)
2686{
2687 int status;
2688 2795
2689 if (!tp_features.bluetooth) 2796 res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
2690 return -ENODEV; 2797 &bluetooth_attr_group);
2798 if (res)
2799 return res;
2691 2800
2692 if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) 2801 res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID,
2693 return -EIO; 2802 &tpacpi_bluetooth_rfkill,
2694 if (radio_on) 2803 RFKILL_TYPE_BLUETOOTH,
2695 status |= TP_ACPI_BLUETOOTH_RADIOSSW; 2804 "tpacpi_bluetooth_sw",
2696 else 2805 tpacpi_bluetooth_rfk_set,
2697 status &= ~TP_ACPI_BLUETOOTH_RADIOSSW; 2806 tpacpi_bluetooth_rfk_get);
2698 if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) 2807 if (res) {
2699 return -EIO; 2808 bluetooth_exit();
2809 return res;
2810 }
2700 2811
2701 return 0; 2812 return 0;
2702} 2813}
@@ -2711,7 +2822,8 @@ static int bluetooth_read(char *p)
2711 len += sprintf(p + len, "status:\t\tnot supported\n"); 2822 len += sprintf(p + len, "status:\t\tnot supported\n");
2712 else { 2823 else {
2713 len += sprintf(p + len, "status:\t\t%s\n", 2824 len += sprintf(p + len, "status:\t\t%s\n",
2714 (status)? "enabled" : "disabled"); 2825 (status == RFKILL_STATE_UNBLOCKED) ?
2826 "enabled" : "disabled");
2715 len += sprintf(p + len, "commands:\tenable, disable\n"); 2827 len += sprintf(p + len, "commands:\tenable, disable\n");
2716 } 2828 }
2717 2829
@@ -2727,9 +2839,9 @@ static int bluetooth_write(char *buf)
2727 2839
2728 while ((cmd = next_cmd(&buf))) { 2840 while ((cmd = next_cmd(&buf))) {
2729 if (strlencmp(cmd, "enable") == 0) { 2841 if (strlencmp(cmd, "enable") == 0) {
2730 bluetooth_set_radiosw(1); 2842 bluetooth_set_radiosw(1, 1);
2731 } else if (strlencmp(cmd, "disable") == 0) { 2843 } else if (strlencmp(cmd, "disable") == 0) {
2732 bluetooth_set_radiosw(0); 2844 bluetooth_set_radiosw(0, 1);
2733 } else 2845 } else
2734 return -EINVAL; 2846 return -EINVAL;
2735 } 2847 }
@@ -2755,8 +2867,66 @@ enum {
2755 TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */ 2867 TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */
2756}; 2868};
2757 2869
2758static int wan_get_radiosw(void); 2870static struct rfkill *tpacpi_wan_rfkill;
2759static int wan_set_radiosw(int radio_on); 2871
2872static int wan_get_radiosw(void)
2873{
2874 int status;
2875
2876 if (!tp_features.wan)
2877 return -ENODEV;
2878
2879 /* WLSW overrides WWAN in firmware/hardware, reflect that */
2880 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
2881 return RFKILL_STATE_HARD_BLOCKED;
2882
2883 if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
2884 return -EIO;
2885
2886 return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ?
2887 RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
2888}
2889
2890static void wan_update_rfk(void)
2891{
2892 int status;
2893
2894 if (!tpacpi_wan_rfkill)
2895 return;
2896
2897 status = wan_get_radiosw();
2898 if (status < 0)
2899 return;
2900 rfkill_force_state(tpacpi_wan_rfkill, status);
2901}
2902
2903static int wan_set_radiosw(int radio_on, int update_rfk)
2904{
2905 int status;
2906
2907 if (!tp_features.wan)
2908 return -ENODEV;
2909
2910 /* WLSW overrides bluetooth in firmware/hardware, but there is no
2911 * reason to risk weird behaviour. */
2912 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status
2913 && radio_on)
2914 return -EPERM;
2915
2916 if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
2917 return -EIO;
2918 if (radio_on)
2919 status |= TP_ACPI_WANCARD_RADIOSSW;
2920 else
2921 status &= ~TP_ACPI_WANCARD_RADIOSSW;
2922 if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
2923 return -EIO;
2924
2925 if (update_rfk)
2926 wan_update_rfk();
2927
2928 return 0;
2929}
2760 2930
2761/* sysfs wan enable ---------------------------------------------------- */ 2931/* sysfs wan enable ---------------------------------------------------- */
2762static ssize_t wan_enable_show(struct device *dev, 2932static ssize_t wan_enable_show(struct device *dev,
@@ -2769,7 +2939,8 @@ static ssize_t wan_enable_show(struct device *dev,
2769 if (status < 0) 2939 if (status < 0)
2770 return status; 2940 return status;
2771 2941
2772 return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); 2942 return snprintf(buf, PAGE_SIZE, "%d\n",
2943 (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
2773} 2944}
2774 2945
2775static ssize_t wan_enable_store(struct device *dev, 2946static ssize_t wan_enable_store(struct device *dev,
@@ -2782,7 +2953,7 @@ static ssize_t wan_enable_store(struct device *dev,
2782 if (parse_strtoul(buf, 1, &t)) 2953 if (parse_strtoul(buf, 1, &t))
2783 return -EINVAL; 2954 return -EINVAL;
2784 2955
2785 res = wan_set_radiosw(t); 2956 res = wan_set_radiosw(t, 1);
2786 2957
2787 return (res) ? res : count; 2958 return (res) ? res : count;
2788} 2959}
@@ -2802,6 +2973,31 @@ static const struct attribute_group wan_attr_group = {
2802 .attrs = wan_attributes, 2973 .attrs = wan_attributes,
2803}; 2974};
2804 2975
2976static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state)
2977{
2978 int wans = wan_get_radiosw();
2979
2980 if (wans < 0)
2981 return wans;
2982
2983 *state = wans;
2984 return 0;
2985}
2986
2987static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state)
2988{
2989 return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
2990}
2991
2992static void wan_exit(void)
2993{
2994 if (tpacpi_wan_rfkill)
2995 rfkill_unregister(tpacpi_wan_rfkill);
2996
2997 sysfs_remove_group(&tpacpi_pdev->dev.kobj,
2998 &wan_attr_group);
2999}
3000
2805static int __init wan_init(struct ibm_init_struct *iibm) 3001static int __init wan_init(struct ibm_init_struct *iibm)
2806{ 3002{
2807 int res; 3003 int res;
@@ -2818,57 +3014,32 @@ static int __init wan_init(struct ibm_init_struct *iibm)
2818 str_supported(tp_features.wan), 3014 str_supported(tp_features.wan),
2819 status); 3015 status);
2820 3016
2821 if (tp_features.wan) { 3017 if (tp_features.wan &&
2822 if (!(status & TP_ACPI_WANCARD_HWPRESENT)) { 3018 !(status & TP_ACPI_WANCARD_HWPRESENT)) {
2823 /* no wan hardware present in system */ 3019 /* no wan hardware present in system */
2824 tp_features.wan = 0; 3020 tp_features.wan = 0;
2825 dbg_printk(TPACPI_DBG_INIT, 3021 dbg_printk(TPACPI_DBG_INIT,
2826 "wan hardware not installed\n"); 3022 "wan hardware not installed\n");
2827 } else {
2828 res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
2829 &wan_attr_group);
2830 if (res)
2831 return res;
2832 }
2833 } 3023 }
2834 3024
2835 return (tp_features.wan)? 0 : 1;
2836}
2837
2838static void wan_exit(void)
2839{
2840 sysfs_remove_group(&tpacpi_pdev->dev.kobj,
2841 &wan_attr_group);
2842}
2843
2844static int wan_get_radiosw(void)
2845{
2846 int status;
2847
2848 if (!tp_features.wan) 3025 if (!tp_features.wan)
2849 return -ENODEV; 3026 return 1;
2850
2851 if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
2852 return -EIO;
2853
2854 return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0);
2855}
2856
2857static int wan_set_radiosw(int radio_on)
2858{
2859 int status;
2860 3027
2861 if (!tp_features.wan) 3028 res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
2862 return -ENODEV; 3029 &wan_attr_group);
3030 if (res)
3031 return res;
2863 3032
2864 if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) 3033 res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID,
2865 return -EIO; 3034 &tpacpi_wan_rfkill,
2866 if (radio_on) 3035 RFKILL_TYPE_WWAN,
2867 status |= TP_ACPI_WANCARD_RADIOSSW; 3036 "tpacpi_wwan_sw",
2868 else 3037 tpacpi_wan_rfk_set,
2869 status &= ~TP_ACPI_WANCARD_RADIOSSW; 3038 tpacpi_wan_rfk_get);
2870 if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) 3039 if (res) {
2871 return -EIO; 3040 wan_exit();
3041 return res;
3042 }
2872 3043
2873 return 0; 3044 return 0;
2874} 3045}
@@ -2883,7 +3054,8 @@ static int wan_read(char *p)
2883 len += sprintf(p + len, "status:\t\tnot supported\n"); 3054 len += sprintf(p + len, "status:\t\tnot supported\n");
2884 else { 3055 else {
2885 len += sprintf(p + len, "status:\t\t%s\n", 3056 len += sprintf(p + len, "status:\t\t%s\n",
2886 (status)? "enabled" : "disabled"); 3057 (status == RFKILL_STATE_UNBLOCKED) ?
3058 "enabled" : "disabled");
2887 len += sprintf(p + len, "commands:\tenable, disable\n"); 3059 len += sprintf(p + len, "commands:\tenable, disable\n");
2888 } 3060 }
2889 3061
@@ -2899,9 +3071,9 @@ static int wan_write(char *buf)
2899 3071
2900 while ((cmd = next_cmd(&buf))) { 3072 while ((cmd = next_cmd(&buf))) {
2901 if (strlencmp(cmd, "enable") == 0) { 3073 if (strlencmp(cmd, "enable") == 0) {
2902 wan_set_radiosw(1); 3074 wan_set_radiosw(1, 1);
2903 } else if (strlencmp(cmd, "disable") == 0) { 3075 } else if (strlencmp(cmd, "disable") == 0) {
2904 wan_set_radiosw(0); 3076 wan_set_radiosw(0, 1);
2905 } else 3077 } else
2906 return -EINVAL; 3078 return -EINVAL;
2907 } 3079 }
@@ -6168,13 +6340,18 @@ err_out:
6168 6340
6169/* Probing */ 6341/* Probing */
6170 6342
6171static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) 6343/* returns 0 - probe ok, or < 0 - probe error.
6344 * Probe ok doesn't mean thinkpad found.
6345 * On error, kfree() cleanup on tp->* is not performed, caller must do it */
6346static int __must_check __init get_thinkpad_model_data(
6347 struct thinkpad_id_data *tp)
6172{ 6348{
6173 const struct dmi_device *dev = NULL; 6349 const struct dmi_device *dev = NULL;
6174 char ec_fw_string[18]; 6350 char ec_fw_string[18];
6351 char const *s;
6175 6352
6176 if (!tp) 6353 if (!tp)
6177 return; 6354 return -EINVAL;
6178 6355
6179 memset(tp, 0, sizeof(*tp)); 6356 memset(tp, 0, sizeof(*tp));
6180 6357
@@ -6183,12 +6360,14 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp)
6183 else if (dmi_name_in_vendors("LENOVO")) 6360 else if (dmi_name_in_vendors("LENOVO"))
6184 tp->vendor = PCI_VENDOR_ID_LENOVO; 6361 tp->vendor = PCI_VENDOR_ID_LENOVO;
6185 else 6362 else
6186 return; 6363 return 0;
6187 6364
6188 tp->bios_version_str = kstrdup(dmi_get_system_info(DMI_BIOS_VERSION), 6365 s = dmi_get_system_info(DMI_BIOS_VERSION);
6189 GFP_KERNEL); 6366 tp->bios_version_str = kstrdup(s, GFP_KERNEL);
6367 if (s && !tp->bios_version_str)
6368 return -ENOMEM;
6190 if (!tp->bios_version_str) 6369 if (!tp->bios_version_str)
6191 return; 6370 return 0;
6192 tp->bios_model = tp->bios_version_str[0] 6371 tp->bios_model = tp->bios_version_str[0]
6193 | (tp->bios_version_str[1] << 8); 6372 | (tp->bios_version_str[1] << 8);
6194 6373
@@ -6207,21 +6386,27 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp)
6207 ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; 6386 ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;
6208 6387
6209 tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); 6388 tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL);
6389 if (!tp->ec_version_str)
6390 return -ENOMEM;
6210 tp->ec_model = ec_fw_string[0] 6391 tp->ec_model = ec_fw_string[0]
6211 | (ec_fw_string[1] << 8); 6392 | (ec_fw_string[1] << 8);
6212 break; 6393 break;
6213 } 6394 }
6214 } 6395 }
6215 6396
6216 tp->model_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_VERSION), 6397 s = dmi_get_system_info(DMI_PRODUCT_VERSION);
6217 GFP_KERNEL); 6398 if (s && !strnicmp(s, "ThinkPad", 8)) {
6218 if (tp->model_str && strnicmp(tp->model_str, "ThinkPad", 8) != 0) { 6399 tp->model_str = kstrdup(s, GFP_KERNEL);
6219 kfree(tp->model_str); 6400 if (!tp->model_str)
6220 tp->model_str = NULL; 6401 return -ENOMEM;
6221 } 6402 }
6222 6403
6223 tp->nummodel_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_NAME), 6404 s = dmi_get_system_info(DMI_PRODUCT_NAME);
6224 GFP_KERNEL); 6405 tp->nummodel_str = kstrdup(s, GFP_KERNEL);
6406 if (s && !tp->nummodel_str)
6407 return -ENOMEM;
6408
6409 return 0;
6225} 6410}
6226 6411
6227static int __init probe_for_thinkpad(void) 6412static int __init probe_for_thinkpad(void)
@@ -6484,7 +6669,13 @@ static int __init thinkpad_acpi_module_init(void)
6484 6669
6485 /* Driver-level probe */ 6670 /* Driver-level probe */
6486 6671
6487 get_thinkpad_model_data(&thinkpad_id); 6672 ret = get_thinkpad_model_data(&thinkpad_id);
6673 if (ret) {
6674 printk(TPACPI_ERR
6675 "unable to get DMI data: %d\n", ret);
6676 thinkpad_acpi_module_exit();
6677 return ret;
6678 }
6488 ret = probe_for_thinkpad(); 6679 ret = probe_for_thinkpad();
6489 if (ret) { 6680 if (ret) {
6490 thinkpad_acpi_module_exit(); 6681 thinkpad_acpi_module_exit();