aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAzael Avalos <coproscefalo@gmail.com>2015-05-03 19:42:07 -0400
committerDarren Hart <dvhart@linux.intel.com>2015-05-06 18:12:39 -0400
commit7ee8cd3319d5e57b1b5e2b348f078af44e67a577 (patch)
treeac2a3071bff2516502cc829e8cc1ff7316309251
parent84c0691e514539900d0f90b1e4442ce49664da5a (diff)
toshiba_bluetooth: Add RFKill handler functions
This patch adds RFKill handler functions to the driver, allowing it to register and update the rfkill switch status. Also, a comment block was moved from the header to the poll function, as it explains why we need to poll the killswitch on older devices. Signed-off-by: Azael Avalos <coproscefalo@gmail.com> Signed-off-by: Darren Hart <dvhart@linux.intel.com>
-rw-r--r--drivers/platform/x86/Kconfig1
-rw-r--r--drivers/platform/x86/toshiba_bluetooth.c77
2 files changed, 69 insertions, 9 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 822171cb50d5..399085d79015 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -642,6 +642,7 @@ config ACPI_TOSHIBA
642config TOSHIBA_BT_RFKILL 642config TOSHIBA_BT_RFKILL
643 tristate "Toshiba Bluetooth RFKill switch support" 643 tristate "Toshiba Bluetooth RFKill switch support"
644 depends on ACPI 644 depends on ACPI
645 depends on RFKILL || RFKILL = n
645 ---help--- 646 ---help---
646 This driver adds support for Bluetooth events for the RFKill 647 This driver adds support for Bluetooth events for the RFKill
647 switch on modern Toshiba laptops with full ACPI support and 648 switch on modern Toshiba laptops with full ACPI support and
diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c
index a619ba67b9d4..a3b2d3883dd6 100644
--- a/drivers/platform/x86/toshiba_bluetooth.c
+++ b/drivers/platform/x86/toshiba_bluetooth.c
@@ -10,12 +10,6 @@
10 * This program is free software; you can redistribute it and/or modify 10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as 11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation. 12 * published by the Free Software Foundation.
13 *
14 * Note the Toshiba Bluetooth RFKill switch seems to be a strange
15 * fish. It only provides a BT event when the switch is flipped to
16 * the 'on' position. When flipping it to 'off', the USB device is
17 * simply pulled away underneath us, without any BT event being
18 * delivered.
19 */ 13 */
20 14
21#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 15#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -25,6 +19,7 @@
25#include <linux/init.h> 19#include <linux/init.h>
26#include <linux/types.h> 20#include <linux/types.h>
27#include <linux/acpi.h> 21#include <linux/acpi.h>
22#include <linux/rfkill.h>
28 23
29#define BT_KILLSWITCH_MASK 0x01 24#define BT_KILLSWITCH_MASK 0x01
30#define BT_PLUGGED_MASK 0x40 25#define BT_PLUGGED_MASK 0x40
@@ -36,6 +31,7 @@ MODULE_LICENSE("GPL");
36 31
37struct toshiba_bluetooth_dev { 32struct toshiba_bluetooth_dev {
38 struct acpi_device *acpi_dev; 33 struct acpi_device *acpi_dev;
34 struct rfkill *rfk;
39 35
40 bool killswitch; 36 bool killswitch;
41 bool plugged; 37 bool plugged;
@@ -191,6 +187,49 @@ static int toshiba_bluetooth_sync_status(struct toshiba_bluetooth_dev *bt_dev)
191 return 0; 187 return 0;
192} 188}
193 189
190/* RFKill handlers */
191static int bt_rfkill_set_block(void *data, bool blocked)
192{
193 struct toshiba_bluetooth_dev *bt_dev = data;
194 int ret;
195
196 ret = toshiba_bluetooth_sync_status(bt_dev);
197 if (ret)
198 return ret;
199
200 if (!bt_dev->killswitch)
201 return 0;
202
203 if (blocked)
204 ret = toshiba_bluetooth_disable(bt_dev->acpi_dev->handle);
205 else
206 ret = toshiba_bluetooth_enable(bt_dev->acpi_dev->handle);
207
208 return ret;
209}
210
211static void bt_rfkill_poll(struct rfkill *rfkill, void *data)
212{
213 struct toshiba_bluetooth_dev *bt_dev = data;
214
215 if (toshiba_bluetooth_sync_status(bt_dev))
216 return;
217
218 /*
219 * Note the Toshiba Bluetooth RFKill switch seems to be a strange
220 * fish. It only provides a BT event when the switch is flipped to
221 * the 'on' position. When flipping it to 'off', the USB device is
222 * simply pulled away underneath us, without any BT event being
223 * delivered.
224 */
225 rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch);
226}
227
228static const struct rfkill_ops rfk_ops = {
229 .set_block = bt_rfkill_set_block,
230 .poll = bt_rfkill_poll,
231};
232
194/* ACPI driver functions */ 233/* ACPI driver functions */
195static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event) 234static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event)
196{ 235{
@@ -228,10 +267,25 @@ static int toshiba_bt_rfkill_add(struct acpi_device *device)
228 return result; 267 return result;
229 } 268 }
230 269
231 /* Enable the BT device */ 270 bt_dev->rfk = rfkill_alloc("Toshiba Bluetooth",
232 result = toshiba_bluetooth_enable(device->handle); 271 &device->dev,
233 if (result) 272 RFKILL_TYPE_BLUETOOTH,
273 &rfk_ops,
274 bt_dev);
275 if (!bt_dev->rfk) {
276 pr_err("Unable to allocate rfkill device\n");
277 kfree(bt_dev);
278 return -ENOMEM;
279 }
280
281 rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch);
282
283 result = rfkill_register(bt_dev->rfk);
284 if (result) {
285 pr_err("Unable to register rfkill device\n");
286 rfkill_destroy(bt_dev->rfk);
234 kfree(bt_dev); 287 kfree(bt_dev);
288 }
235 289
236 return result; 290 return result;
237} 291}
@@ -241,6 +295,11 @@ static int toshiba_bt_rfkill_remove(struct acpi_device *device)
241 struct toshiba_bluetooth_dev *bt_dev = acpi_driver_data(device); 295 struct toshiba_bluetooth_dev *bt_dev = acpi_driver_data(device);
242 296
243 /* clean up */ 297 /* clean up */
298 if (bt_dev->rfk) {
299 rfkill_unregister(bt_dev->rfk);
300 rfkill_destroy(bt_dev->rfk);
301 }
302
244 kfree(bt_dev); 303 kfree(bt_dev);
245 304
246 return toshiba_bluetooth_disable(device->handle); 305 return toshiba_bluetooth_disable(device->handle);