Add patches accepted for 2.6.27-rc1
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / releases / upstream / 2.6.27-rc1 / 0005-ACPI-thinkpad-acpi-add-bluetooth-and-WWAN-rfkill-s.patch
blob73f0cc0822022123557e5d43b6d04e83964f283e
1 From 0e74dc2646db04b644faa8ea10ff4f408d55cf90 Mon Sep 17 00:00:00 2001
2 From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
3 Date: Mon, 21 Jul 2008 09:15:51 -0300
4 Subject: ACPI: thinkpad-acpi: add bluetooth and WWAN rfkill support
6 Add a read/write rfkill interface to the bluetooth radio switch on the
7 bluetooth submodule, and one for the wireless wan radio switch to the wan
8 submodule.
10 Since rfkill does care for when a switch changes state, use WLSW
11 notifications to also check if the WWAN or Bluetooth switches did not
12 change state (due to them being slaves of WLSW in firmware/hardware, but
13 that reality not being always properly exported by the thinkpad firmware).
15 Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
16 Cc: Ivo van Doorn <IvDoorn@gmail.com>
17 Cc: John W. Linville <linville@tuxdriver.com>
18 ---
19 Documentation/laptops/thinkpad-acpi.txt | 22 +++-
20 drivers/misc/Kconfig | 2 +
21 drivers/misc/thinkpad_acpi.c | 208 +++++++++++++++++++++++++++----
22 3 files changed, 200 insertions(+), 32 deletions(-)
24 diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt
25 index 64b3f14..1c1c021 100644
26 --- a/Documentation/laptops/thinkpad-acpi.txt
27 +++ b/Documentation/laptops/thinkpad-acpi.txt
28 @@ -621,7 +621,8 @@ Bluetooth
29 ---------
31 procfs: /proc/acpi/ibm/bluetooth
32 -sysfs device attribute: bluetooth_enable
33 +sysfs device attribute: bluetooth_enable (deprecated)
34 +sysfs rfkill class: switch "tpacpi_bluetooth_sw"
36 This feature shows the presence and current state of a ThinkPad
37 Bluetooth device in the internal ThinkPad CDC slot.
38 @@ -643,8 +644,12 @@ Sysfs notes:
39 0: disables Bluetooth / Bluetooth is disabled
40 1: enables Bluetooth / Bluetooth is enabled.
42 - Note: this interface will be probably be superseded by the
43 - generic rfkill class, so it is NOT to be considered stable yet.
44 + Note: this interface has been superseded by the generic rfkill
45 + class. It has been deprecated, and it will be removed in year
46 + 2010.
48 + rfkill controller switch "tpacpi_bluetooth_sw": refer to
49 + Documentation/rfkill.txt for details.
51 Video output control -- /proc/acpi/ibm/video
52 --------------------------------------------
53 @@ -1374,7 +1379,8 @@ EXPERIMENTAL: WAN
54 -----------------
56 procfs: /proc/acpi/ibm/wan
57 -sysfs device attribute: wwan_enable
58 +sysfs device attribute: wwan_enable (deprecated)
59 +sysfs rfkill class: switch "tpacpi_wwan_sw"
61 This feature is marked EXPERIMENTAL because the implementation
62 directly accesses hardware registers and may not work as expected. USE
63 @@ -1404,8 +1410,12 @@ Sysfs notes:
64 0: disables WWAN card / WWAN card is disabled
65 1: enables WWAN card / WWAN card is enabled.
67 - Note: this interface will be probably be superseded by the
68 - generic rfkill class, so it is NOT to be considered stable yet.
69 + Note: this interface has been superseded by the generic rfkill
70 + class. It has been deprecated, and it will be removed in year
71 + 2010.
73 + rfkill controller switch "tpacpi_wwan_sw": refer to
74 + Documentation/rfkill.txt for details.
76 Multiple Commands, Module Parameters
77 ------------------------------------
78 diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
79 index 1921b8d..b27ca91 100644
80 --- a/drivers/misc/Kconfig
81 +++ b/drivers/misc/Kconfig
82 @@ -279,6 +279,8 @@ config THINKPAD_ACPI
83 select INPUT
84 select NEW_LEDS
85 select LEDS_CLASS
86 + select NET
87 + select RFKILL
88 ---help---
89 This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
90 support for Fn-Fx key combinations, Bluetooth control, video
91 diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
92 index 202d63e..dc8d00a 100644
93 --- a/drivers/misc/thinkpad_acpi.c
94 +++ b/drivers/misc/thinkpad_acpi.c
95 @@ -68,6 +68,7 @@
96 #include <linux/hwmon-sysfs.h>
97 #include <linux/input.h>
98 #include <linux/leds.h>
99 +#include <linux/rfkill.h>
100 #include <asm/uaccess.h>
102 #include <linux/dmi.h>
103 @@ -144,6 +145,12 @@ enum {
105 #define TPACPI_MAX_ACPI_ARGS 3
107 +/* rfkill switches */
108 +enum {
109 + TPACPI_RFK_BLUETOOTH_SW_ID = 0,
110 + TPACPI_RFK_WWAN_SW_ID,
113 /* Debugging */
114 #define TPACPI_LOG TPACPI_FILE ": "
115 #define TPACPI_ERR KERN_ERR TPACPI_LOG
116 @@ -905,6 +912,43 @@ static int __init tpacpi_check_std_acpi_brightness_support(void)
117 return 0;
120 +static int __init tpacpi_new_rfkill(const unsigned int id,
121 + struct rfkill **rfk,
122 + const enum rfkill_type rfktype,
123 + const char *name,
124 + int (*toggle_radio)(void *, enum rfkill_state),
125 + int (*get_state)(void *, enum rfkill_state *))
127 + int res;
128 + enum rfkill_state initial_state;
130 + *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype);
131 + if (!*rfk) {
132 + printk(TPACPI_ERR
133 + "failed to allocate memory for rfkill class\n");
134 + return -ENOMEM;
137 + (*rfk)->name = name;
138 + (*rfk)->get_state = get_state;
139 + (*rfk)->toggle_radio = toggle_radio;
141 + if (!get_state(NULL, &initial_state))
142 + (*rfk)->state = initial_state;
144 + res = rfkill_register(*rfk);
145 + if (res < 0) {
146 + printk(TPACPI_ERR
147 + "failed to register %s rfkill switch: %d\n",
148 + name, res);
149 + rfkill_free(*rfk);
150 + *rfk = NULL;
151 + return res;
154 + return 0;
157 /*************************************************************************
158 * thinkpad-acpi driver attributes
160 @@ -1906,10 +1950,18 @@ static struct attribute *hotkey_mask_attributes[] __initdata = {
161 &dev_attr_hotkey_wakeup_hotunplug_complete.attr,
164 +static void bluetooth_update_rfk(void);
165 +static void wan_update_rfk(void);
166 static void tpacpi_send_radiosw_update(void)
168 int wlsw;
170 + /* Sync these BEFORE sending any rfkill events */
171 + if (tp_features.bluetooth)
172 + bluetooth_update_rfk();
173 + if (tp_features.wan)
174 + wan_update_rfk();
176 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) {
177 mutex_lock(&tpacpi_inputdev_send_mutex);
179 @@ -2581,6 +2633,8 @@ enum {
180 TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */
183 +static struct rfkill *tpacpi_bluetooth_rfkill;
185 static int bluetooth_get_radiosw(void)
187 int status;
188 @@ -2590,15 +2644,29 @@ static int bluetooth_get_radiosw(void)
190 /* WLSW overrides bluetooth in firmware/hardware, reflect that */
191 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
192 - return 0;
193 + return RFKILL_STATE_HARD_BLOCKED;
195 if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
196 return -EIO;
198 - return (status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0;
199 + return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ?
200 + RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
203 -static int bluetooth_set_radiosw(int radio_on)
204 +static void bluetooth_update_rfk(void)
206 + int status;
208 + if (!tpacpi_bluetooth_rfkill)
209 + return;
211 + status = bluetooth_get_radiosw();
212 + if (status < 0)
213 + return;
214 + rfkill_force_state(tpacpi_bluetooth_rfkill, status);
217 +static int bluetooth_set_radiosw(int radio_on, int update_rfk)
219 int status;
221 @@ -2620,6 +2688,9 @@ static int bluetooth_set_radiosw(int radio_on)
222 if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
223 return -EIO;
225 + if (update_rfk)
226 + bluetooth_update_rfk();
228 return 0;
231 @@ -2634,7 +2705,8 @@ static ssize_t bluetooth_enable_show(struct device *dev,
232 if (status < 0)
233 return status;
235 - return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0);
236 + return snprintf(buf, PAGE_SIZE, "%d\n",
237 + (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
240 static ssize_t bluetooth_enable_store(struct device *dev,
241 @@ -2647,7 +2719,7 @@ static ssize_t bluetooth_enable_store(struct device *dev,
242 if (parse_strtoul(buf, 1, &t))
243 return -EINVAL;
245 - res = bluetooth_set_radiosw(t);
246 + res = bluetooth_set_radiosw(t, 1);
248 return (res) ? res : count;
250 @@ -2667,8 +2739,27 @@ static const struct attribute_group bluetooth_attr_group = {
251 .attrs = bluetooth_attributes,
254 +static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state)
256 + int bts = bluetooth_get_radiosw();
258 + if (bts < 0)
259 + return bts;
261 + *state = bts;
262 + return 0;
265 +static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state)
267 + return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
270 static void bluetooth_exit(void)
272 + if (tpacpi_bluetooth_rfkill)
273 + rfkill_unregister(tpacpi_bluetooth_rfkill);
275 sysfs_remove_group(&tpacpi_pdev->dev.kobj,
276 &bluetooth_attr_group);
278 @@ -2699,14 +2790,26 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)
279 "bluetooth hardware not installed\n");
282 - if (tp_features.bluetooth) {
283 - res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
284 + if (!tp_features.bluetooth)
285 + return 1;
287 + res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
288 &bluetooth_attr_group);
289 - if (res)
290 - return res;
291 + if (res)
292 + return res;
294 + res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID,
295 + &tpacpi_bluetooth_rfkill,
296 + RFKILL_TYPE_BLUETOOTH,
297 + "tpacpi_bluetooth_sw",
298 + tpacpi_bluetooth_rfk_set,
299 + tpacpi_bluetooth_rfk_get);
300 + if (res) {
301 + bluetooth_exit();
302 + return res;
305 - return (tp_features.bluetooth)? 0 : 1;
306 + return 0;
309 /* procfs -------------------------------------------------------------- */
310 @@ -2719,7 +2822,8 @@ static int bluetooth_read(char *p)
311 len += sprintf(p + len, "status:\t\tnot supported\n");
312 else {
313 len += sprintf(p + len, "status:\t\t%s\n",
314 - (status)? "enabled" : "disabled");
315 + (status == RFKILL_STATE_UNBLOCKED) ?
316 + "enabled" : "disabled");
317 len += sprintf(p + len, "commands:\tenable, disable\n");
320 @@ -2735,9 +2839,9 @@ static int bluetooth_write(char *buf)
322 while ((cmd = next_cmd(&buf))) {
323 if (strlencmp(cmd, "enable") == 0) {
324 - bluetooth_set_radiosw(1);
325 + bluetooth_set_radiosw(1, 1);
326 } else if (strlencmp(cmd, "disable") == 0) {
327 - bluetooth_set_radiosw(0);
328 + bluetooth_set_radiosw(0, 1);
329 } else
330 return -EINVAL;
332 @@ -2763,6 +2867,8 @@ enum {
333 TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */
336 +static struct rfkill *tpacpi_wan_rfkill;
338 static int wan_get_radiosw(void)
340 int status;
341 @@ -2772,15 +2878,29 @@ static int wan_get_radiosw(void)
343 /* WLSW overrides WWAN in firmware/hardware, reflect that */
344 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
345 - return 0;
346 + return RFKILL_STATE_HARD_BLOCKED;
348 if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
349 return -EIO;
351 - return (status & TP_ACPI_WANCARD_RADIOSSW) != 0;
352 + return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ?
353 + RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
356 -static int wan_set_radiosw(int radio_on)
357 +static void wan_update_rfk(void)
359 + int status;
361 + if (!tpacpi_wan_rfkill)
362 + return;
364 + status = wan_get_radiosw();
365 + if (status < 0)
366 + return;
367 + rfkill_force_state(tpacpi_wan_rfkill, status);
370 +static int wan_set_radiosw(int radio_on, int update_rfk)
372 int status;
374 @@ -2802,6 +2922,9 @@ static int wan_set_radiosw(int radio_on)
375 if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
376 return -EIO;
378 + if (update_rfk)
379 + wan_update_rfk();
381 return 0;
384 @@ -2816,7 +2939,8 @@ static ssize_t wan_enable_show(struct device *dev,
385 if (status < 0)
386 return status;
388 - return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0);
389 + return snprintf(buf, PAGE_SIZE, "%d\n",
390 + (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
393 static ssize_t wan_enable_store(struct device *dev,
394 @@ -2829,7 +2953,7 @@ static ssize_t wan_enable_store(struct device *dev,
395 if (parse_strtoul(buf, 1, &t))
396 return -EINVAL;
398 - res = wan_set_radiosw(t);
399 + res = wan_set_radiosw(t, 1);
401 return (res) ? res : count;
403 @@ -2849,8 +2973,27 @@ static const struct attribute_group wan_attr_group = {
404 .attrs = wan_attributes,
407 +static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state)
409 + int wans = wan_get_radiosw();
411 + if (wans < 0)
412 + return wans;
414 + *state = wans;
415 + return 0;
418 +static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state)
420 + return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
423 static void wan_exit(void)
425 + if (tpacpi_wan_rfkill)
426 + rfkill_unregister(tpacpi_wan_rfkill);
428 sysfs_remove_group(&tpacpi_pdev->dev.kobj,
429 &wan_attr_group);
431 @@ -2879,14 +3022,26 @@ static int __init wan_init(struct ibm_init_struct *iibm)
432 "wan hardware not installed\n");
435 - if (tp_features.wan) {
436 - res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
437 + if (!tp_features.wan)
438 + return 1;
440 + res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
441 &wan_attr_group);
442 - if (res)
443 - return res;
444 + if (res)
445 + return res;
447 + res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID,
448 + &tpacpi_wan_rfkill,
449 + RFKILL_TYPE_WWAN,
450 + "tpacpi_wwan_sw",
451 + tpacpi_wan_rfk_set,
452 + tpacpi_wan_rfk_get);
453 + if (res) {
454 + wan_exit();
455 + return res;
458 - return (tp_features.wan)? 0 : 1;
459 + return 0;
462 /* procfs -------------------------------------------------------------- */
463 @@ -2899,7 +3054,8 @@ static int wan_read(char *p)
464 len += sprintf(p + len, "status:\t\tnot supported\n");
465 else {
466 len += sprintf(p + len, "status:\t\t%s\n",
467 - (status)? "enabled" : "disabled");
468 + (status == RFKILL_STATE_UNBLOCKED) ?
469 + "enabled" : "disabled");
470 len += sprintf(p + len, "commands:\tenable, disable\n");
473 @@ -2915,9 +3071,9 @@ static int wan_write(char *buf)
475 while ((cmd = next_cmd(&buf))) {
476 if (strlencmp(cmd, "enable") == 0) {
477 - wan_set_radiosw(1);
478 + wan_set_radiosw(1, 1);
479 } else if (strlencmp(cmd, "disable") == 0) {
480 - wan_set_radiosw(0);
481 + wan_set_radiosw(0, 1);
482 } else
483 return -EINVAL;
486 1.5.6.2