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
tristate "Toshiba Bluetooth RFKill switch support"
depends on ACPI
depends on RFKILL || RFKILL = n
---help---
This driver adds support for Bluetooth events for the RFKill
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
* it under the terms of the GNU General Public License version 2 as
* 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
@ -25,6 +19,7 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/acpi.h>
#include <linux/rfkill.h>
#define BT_KILLSWITCH_MASK 0x01
#define BT_PLUGGED_MASK 0x40
@ -36,6 +31,7 @@ MODULE_LICENSE("GPL");
struct toshiba_bluetooth_dev {
struct acpi_device *acpi_dev;
struct rfkill *rfk;
bool killswitch;
bool plugged;
@ -191,6 +187,49 @@ static int toshiba_bluetooth_sync_status(struct toshiba_bluetooth_dev *bt_dev)
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 */
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;
}
/* 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);
}
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);
/* clean up */
if (bt_dev->rfk) {
rfkill_unregister(bt_dev->rfk);
rfkill_destroy(bt_dev->rfk);
}
kfree(bt_dev);
return toshiba_bluetooth_disable(device->handle);