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>
This commit is contained in:
Azael Avalos 2015-05-03 17:42:07 -06:00 committed by Darren Hart
parent 84c0691e51
commit 7ee8cd3319
2 changed files with 69 additions and 9 deletions

View File

@ -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

View File

@ -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 */ bt_dev->rfk = rfkill_alloc("Toshiba Bluetooth",
result = toshiba_bluetooth_enable(device->handle); &device->dev,
if (result) RFKILL_TYPE_BLUETOOTH,
&rfk_ops,
bt_dev);
if (!bt_dev->rfk) {
pr_err("Unable to allocate rfkill device\n");
kfree(bt_dev); 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);
}
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);