1 From 0045c0aa7d5e787f78938e6a10927b8a516f0b83 Mon Sep 17 00:00:00 2001
2 From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
3 Date: Sun, 11 Jan 2009 03:01:03 -0200
4 Subject: ACPI: thinkpad-acpi: add UWB radio support
6 Add rfkill support for USB UWB radio devices on very recent ThinkPad
9 The new subdriver is moslty a trimmed down copy of the wwan subdriver.
11 Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
12 Cc: Ivo van Doorn <IvDoorn@gmail.com>
13 Signed-off-by: Len Brown <len.brown@intel.com>
15 Documentation/laptops/thinkpad-acpi.txt | 18 +++
16 drivers/platform/x86/thinkpad_acpi.c | 206 ++++++++++++++++++++++++++++++-
17 2 files changed, 223 insertions(+), 1 deletions(-)
19 diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt
20 index ddc371e..91c0001 100644
21 --- a/Documentation/laptops/thinkpad-acpi.txt
22 +++ b/Documentation/laptops/thinkpad-acpi.txt
23 @@ -1413,6 +1413,24 @@ Sysfs notes:
24 rfkill controller switch "tpacpi_wwan_sw": refer to
25 Documentation/rfkill.txt for details.
30 +This feature is marked EXPERIMENTAL because it has not been extensively
31 +tested and validated in various ThinkPad models yet. The feature may not
32 +work as expected. USE WITH CAUTION! To use this feature, you need to supply
33 +the experimental=1 parameter when loading the module.
35 +sysfs rfkill class: switch "tpacpi_uwb_sw"
37 +This feature exports an rfkill controller for the UWB device, if one is
38 +present and enabled in the BIOS.
42 + rfkill controller switch "tpacpi_uwb_sw": refer to
43 + Documentation/rfkill.txt for details.
45 Multiple Commands, Module Parameters
46 ------------------------------------
48 diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
49 index 27d709b..c1d4041 100644
50 --- a/drivers/platform/x86/thinkpad_acpi.c
51 +++ b/drivers/platform/x86/thinkpad_acpi.c
52 @@ -169,6 +169,7 @@ enum {
54 TPACPI_RFK_BLUETOOTH_SW_ID = 0,
55 TPACPI_RFK_WWAN_SW_ID,
56 + TPACPI_RFK_UWB_SW_ID,
60 @@ -261,6 +262,7 @@ static struct {
61 u32 bright_16levels:1;
62 u32 bright_acpimode:1;
65 u32 fan_ctrl_status_undef:1;
66 u32 input_device_registered:1;
67 u32 platform_drv_registered:1;
68 @@ -317,6 +319,8 @@ static int dbg_bluetoothemul;
69 static int tpacpi_bluetooth_emulstate;
70 static int dbg_wwanemul;
71 static int tpacpi_wwan_emulstate;
72 +static int dbg_uwbemul;
73 +static int tpacpi_uwb_emulstate;
77 @@ -967,6 +971,7 @@ static int __init tpacpi_new_rfkill(const unsigned int id,
79 const enum rfkill_type rfktype,
81 + const bool set_default,
82 int (*toggle_radio)(void *, enum rfkill_state),
83 int (*get_state)(void *, enum rfkill_state *))
85 @@ -978,7 +983,7 @@ static int __init tpacpi_new_rfkill(const unsigned int id,
87 "failed to read initial state for %s, error %d; "
88 "will turn radio off\n", name, res);
90 + } else if (set_default) {
91 /* try to set the initial state as the default for the rfkill
92 * type, since we ask the firmware to preserve it across S5 in
94 @@ -1148,6 +1153,31 @@ static DRIVER_ATTR(wwan_emulstate, S_IWUSR | S_IRUGO,
95 tpacpi_driver_wwan_emulstate_show,
96 tpacpi_driver_wwan_emulstate_store);
98 +/* uwb_emulstate ------------------------------------------------- */
99 +static ssize_t tpacpi_driver_uwb_emulstate_show(
100 + struct device_driver *drv,
103 + return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_uwb_emulstate);
106 +static ssize_t tpacpi_driver_uwb_emulstate_store(
107 + struct device_driver *drv,
108 + const char *buf, size_t count)
112 + if (parse_strtoul(buf, 1, &t))
115 + tpacpi_uwb_emulstate = !!t;
120 +static DRIVER_ATTR(uwb_emulstate, S_IWUSR | S_IRUGO,
121 + tpacpi_driver_uwb_emulstate_show,
122 + tpacpi_driver_uwb_emulstate_store);
125 /* --------------------------------------------------------------------- */
126 @@ -1175,6 +1205,8 @@ static int __init tpacpi_create_driver_attributes(struct device_driver *drv)
127 res = driver_create_file(drv, &driver_attr_bluetooth_emulstate);
128 if (!res && dbg_wwanemul)
129 res = driver_create_file(drv, &driver_attr_wwan_emulstate);
130 + if (!res && dbg_uwbemul)
131 + res = driver_create_file(drv, &driver_attr_uwb_emulstate);
135 @@ -1191,6 +1223,7 @@ static void tpacpi_remove_driver_attributes(struct device_driver *drv)
136 driver_remove_file(drv, &driver_attr_wlsw_emulstate);
137 driver_remove_file(drv, &driver_attr_bluetooth_emulstate);
138 driver_remove_file(drv, &driver_attr_wwan_emulstate);
139 + driver_remove_file(drv, &driver_attr_uwb_emulstate);
143 @@ -2125,6 +2158,7 @@ static struct attribute *hotkey_mask_attributes[] __initdata = {
145 static void bluetooth_update_rfk(void);
146 static void wan_update_rfk(void);
147 +static void uwb_update_rfk(void);
148 static void tpacpi_send_radiosw_update(void)
151 @@ -2134,6 +2168,8 @@ static void tpacpi_send_radiosw_update(void)
152 bluetooth_update_rfk();
155 + if (tp_features.uwb)
158 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) {
159 mutex_lock(&tpacpi_inputdev_send_mutex);
160 @@ -3035,6 +3071,7 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)
161 &tpacpi_bluetooth_rfkill,
162 RFKILL_TYPE_BLUETOOTH,
163 "tpacpi_bluetooth_sw",
165 tpacpi_bluetooth_rfk_set,
166 tpacpi_bluetooth_rfk_get);
168 @@ -3309,6 +3346,7 @@ static int __init wan_init(struct ibm_init_struct *iibm)
176 @@ -3366,6 +3404,162 @@ static struct ibm_struct wan_driver_data = {
179 /*************************************************************************
184 + /* ACPI GUWB/SUWB bits */
185 + TP_ACPI_UWB_HWPRESENT = 0x01, /* UWB hw available */
186 + TP_ACPI_UWB_RADIOSSW = 0x02, /* UWB radio enabled */
189 +static struct rfkill *tpacpi_uwb_rfkill;
191 +static int uwb_get_radiosw(void)
195 + if (!tp_features.uwb)
198 + /* WLSW overrides UWB in firmware/hardware, reflect that */
199 + if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
200 + return RFKILL_STATE_HARD_BLOCKED;
202 +#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
204 + return (tpacpi_uwb_emulstate) ?
205 + RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
208 + if (!acpi_evalf(hkey_handle, &status, "GUWB", "d"))
211 + return ((status & TP_ACPI_UWB_RADIOSSW) != 0) ?
212 + RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
215 +static void uwb_update_rfk(void)
219 + if (!tpacpi_uwb_rfkill)
222 + status = uwb_get_radiosw();
225 + rfkill_force_state(tpacpi_uwb_rfkill, status);
228 +static int uwb_set_radiosw(int radio_on, int update_rfk)
232 + if (!tp_features.uwb)
235 + /* WLSW overrides UWB in firmware/hardware, but there is no
236 + * reason to risk weird behaviour. */
237 + if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status
241 +#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
243 + tpacpi_uwb_emulstate = !!radio_on;
250 + status = (radio_on) ? TP_ACPI_UWB_RADIOSSW : 0;
251 + if (!acpi_evalf(hkey_handle, NULL, "SUWB", "vd", status))
260 +/* --------------------------------------------------------------------- */
262 +static int tpacpi_uwb_rfk_get(void *data, enum rfkill_state *state)
264 + int uwbs = uwb_get_radiosw();
273 +static int tpacpi_uwb_rfk_set(void *data, enum rfkill_state state)
275 + return uwb_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
278 +static void uwb_exit(void)
280 + if (tpacpi_uwb_rfkill)
281 + rfkill_unregister(tpacpi_uwb_rfkill);
284 +static int __init uwb_init(struct ibm_init_struct *iibm)
289 + vdbg_printk(TPACPI_DBG_INIT, "initializing uwb subdriver\n");
291 + TPACPI_ACPIHANDLE_INIT(hkey);
293 + tp_features.uwb = hkey_handle &&
294 + acpi_evalf(hkey_handle, &status, "GUWB", "qd");
296 + vdbg_printk(TPACPI_DBG_INIT, "uwb is %s, status 0x%02x\n",
297 + str_supported(tp_features.uwb),
300 +#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
302 + tp_features.uwb = 1;
304 + "uwb switch emulation enabled\n");
307 + if (tp_features.uwb &&
308 + !(status & TP_ACPI_UWB_HWPRESENT)) {
309 + /* no uwb hardware present in system */
310 + tp_features.uwb = 0;
311 + dbg_printk(TPACPI_DBG_INIT,
312 + "uwb hardware not installed\n");
315 + if (!tp_features.uwb)
318 + res = tpacpi_new_rfkill(TPACPI_RFK_UWB_SW_ID,
319 + &tpacpi_uwb_rfkill,
323 + tpacpi_uwb_rfk_set,
324 + tpacpi_uwb_rfk_get);
329 +static struct ibm_struct uwb_driver_data = {
332 + .flags.experimental = 1,
335 +/*************************************************************************
339 @@ -6830,6 +7024,10 @@ static struct ibm_init_struct ibms_init[] __initdata = {
341 .data = &wan_driver_data,
345 + .data = &uwb_driver_data,
347 #ifdef CONFIG_THINKPAD_ACPI_VIDEO
350 @@ -6986,6 +7184,12 @@ MODULE_PARM_DESC(dbg_wwanemul, "Enables WWAN switch emulation");
351 module_param_named(wwan_state, tpacpi_wwan_emulstate, bool, 0);
352 MODULE_PARM_DESC(wwan_state,
353 "Initial state of the emulated WWAN switch");
355 +module_param(dbg_uwbemul, uint, 0);
356 +MODULE_PARM_DESC(dbg_uwbemul, "Enables UWB switch emulation");
357 +module_param_named(uwb_state, tpacpi_uwb_emulstate, bool, 0);
358 +MODULE_PARM_DESC(uwb_state,
359 + "Initial state of the emulated UWB switch");
362 static void thinkpad_acpi_module_exit(void)