Переглянути джерело

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>
Azael Avalos 10 роки тому
батько
коміт
7ee8cd3319
2 змінених файлів з 69 додано та 9 видалено
  1. 1 0
      drivers/platform/x86/Kconfig
  2. 68 9
      drivers/platform/x86/toshiba_bluetooth.c

+ 1 - 0
drivers/platform/x86/Kconfig

@@ -642,6 +642,7 @@ config ACPI_TOSHIBA
 config TOSHIBA_BT_RFKILL
 config TOSHIBA_BT_RFKILL
 	tristate "Toshiba Bluetooth RFKill switch support"
 	tristate "Toshiba Bluetooth RFKill switch support"
 	depends on ACPI
 	depends on ACPI
+	depends on RFKILL || RFKILL = n
 	---help---
 	---help---
 	  This driver adds support for Bluetooth events for the RFKill
 	  This driver adds support for Bluetooth events for the RFKill
 	  switch on modern Toshiba laptops with full ACPI support and
 	  switch on modern Toshiba laptops with full ACPI support and

+ 68 - 9
drivers/platform/x86/toshiba_bluetooth.c

@@ -10,12 +10,6 @@
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  * published by the Free Software Foundation.
- *
- * Note the Toshiba Bluetooth RFKill switch seems to be a strange
- * fish. It only provides a BT event when the switch is flipped to
- * the 'on' position. When flipping it to 'off', the USB device is
- * simply pulled away underneath us, without any BT event being
- * delivered.
  */
  */
 
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -25,6 +19,7 @@
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/types.h>
 #include <linux/types.h>
 #include <linux/acpi.h>
 #include <linux/acpi.h>
+#include <linux/rfkill.h>
 
 
 #define BT_KILLSWITCH_MASK	0x01
 #define BT_KILLSWITCH_MASK	0x01
 #define BT_PLUGGED_MASK		0x40
 #define BT_PLUGGED_MASK		0x40
@@ -36,6 +31,7 @@ MODULE_LICENSE("GPL");
 
 
 struct toshiba_bluetooth_dev {
 struct toshiba_bluetooth_dev {
 	struct acpi_device *acpi_dev;
 	struct acpi_device *acpi_dev;
+	struct rfkill *rfk;
 
 
 	bool killswitch;
 	bool killswitch;
 	bool plugged;
 	bool plugged;
@@ -191,6 +187,49 @@ static int toshiba_bluetooth_sync_status(struct toshiba_bluetooth_dev *bt_dev)
 	return 0;
 	return 0;
 }
 }
 
 
+/* RFKill handlers */
+static int bt_rfkill_set_block(void *data, bool blocked)
+{
+	struct toshiba_bluetooth_dev *bt_dev = data;
+	int ret;
+
+	ret = toshiba_bluetooth_sync_status(bt_dev);
+	if (ret)
+		return ret;
+
+	if (!bt_dev->killswitch)
+		return 0;
+
+	if (blocked)
+		ret = toshiba_bluetooth_disable(bt_dev->acpi_dev->handle);
+	else
+		ret = toshiba_bluetooth_enable(bt_dev->acpi_dev->handle);
+
+	return ret;
+}
+
+static void bt_rfkill_poll(struct rfkill *rfkill, void *data)
+{
+	struct toshiba_bluetooth_dev *bt_dev = data;
+
+	if (toshiba_bluetooth_sync_status(bt_dev))
+		return;
+
+	/*
+	 * Note the Toshiba Bluetooth RFKill switch seems to be a strange
+	 * fish. It only provides a BT event when the switch is flipped to
+	 * the 'on' position. When flipping it to 'off', the USB device is
+	 * simply pulled away underneath us, without any BT event being
+	 * delivered.
+	 */
+	rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch);
+}
+
+static const struct rfkill_ops rfk_ops = {
+	.set_block = bt_rfkill_set_block,
+	.poll = bt_rfkill_poll,
+};
+
 /* ACPI driver functions */
 /* ACPI driver functions */
 static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event)
 static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event)
 {
 {
@@ -228,10 +267,25 @@ static int toshiba_bt_rfkill_add(struct acpi_device *device)
 		return result;
 		return result;
 	}
 	}
 
 
-	/* Enable the BT device */
-	result = toshiba_bluetooth_enable(device->handle);
-	if (result)
+	bt_dev->rfk = rfkill_alloc("Toshiba Bluetooth",
+				   &device->dev,
+				   RFKILL_TYPE_BLUETOOTH,
+				   &rfk_ops,
+				   bt_dev);
+	if (!bt_dev->rfk) {
+		pr_err("Unable to allocate rfkill device\n");
+		kfree(bt_dev);
+		return -ENOMEM;
+	}
+
+	rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch);
+
+	result = rfkill_register(bt_dev->rfk);
+	if (result) {
+		pr_err("Unable to register rfkill device\n");
+		rfkill_destroy(bt_dev->rfk);
 		kfree(bt_dev);
 		kfree(bt_dev);
+	}
 
 
 	return result;
 	return result;
 }
 }
@@ -241,6 +295,11 @@ static int toshiba_bt_rfkill_remove(struct acpi_device *device)
 	struct toshiba_bluetooth_dev *bt_dev = acpi_driver_data(device);
 	struct toshiba_bluetooth_dev *bt_dev = acpi_driver_data(device);
 
 
 	/* clean up */
 	/* clean up */
+	if (bt_dev->rfk) {
+		rfkill_unregister(bt_dev->rfk);
+		rfkill_destroy(bt_dev->rfk);
+	}
+
 	kfree(bt_dev);
 	kfree(bt_dev);
 
 
 	return toshiba_bluetooth_disable(device->handle);
 	return toshiba_bluetooth_disable(device->handle);