diff options
Diffstat (limited to 'drivers/platform/x86/toshiba_bluetooth.c')
-rw-r--r-- | drivers/platform/x86/toshiba_bluetooth.c | 133 |
1 files changed, 104 insertions, 29 deletions
diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c index 2cb1ea62b4a7..249800763362 100644 --- a/drivers/platform/x86/toshiba_bluetooth.c +++ b/drivers/platform/x86/toshiba_bluetooth.c @@ -2,6 +2,7 @@ * Toshiba Bluetooth Enable Driver * * Copyright (C) 2009 Jes Sorensen <Jes.Sorensen@gmail.com> + * Copyright (C) 2015 Azael Avalos <coproscefalo@gmail.com> * * Thanks to Matthew Garrett for background info on ACPI innards which * normal people aren't meant to understand :-) @@ -25,6 +26,10 @@ #include <linux/types.h> #include <linux/acpi.h> +#define BT_KILLSWITCH_MASK 0x01 +#define BT_PLUGGED_MASK 0x40 +#define BT_POWER_MASK 0x80 + MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@gmail.com>"); MODULE_DESCRIPTION("Toshiba Laptop ACPI Bluetooth Enable Driver"); MODULE_LICENSE("GPL"); @@ -57,32 +62,107 @@ static struct acpi_driver toshiba_bt_rfkill_driver = { .drv.pm = &toshiba_bt_pm, }; +static int toshiba_bluetooth_present(acpi_handle handle) +{ + acpi_status result; + u64 bt_present; + + /* + * Some Toshiba laptops may have a fake TOS6205 device in + * their ACPI BIOS, so query the _STA method to see if there + * is really anything there. + */ + result = acpi_evaluate_integer(handle, "_STA", NULL, &bt_present); + if (ACPI_FAILURE(result)) { + pr_err("ACPI call to query Bluetooth presence failed"); + return -ENXIO; + } else if (!bt_present) { + pr_info("Bluetooth device not present\n"); + return -ENODEV; + } + + return 0; +} + +static int toshiba_bluetooth_status(acpi_handle handle) +{ + acpi_status result; + u64 status; + + result = acpi_evaluate_integer(handle, "BTST", NULL, &status); + if (ACPI_FAILURE(result)) { + pr_err("Could not get Bluetooth device status\n"); + return -ENXIO; + } + + pr_info("Bluetooth status %llu\n", status); + + return status; +} static int toshiba_bluetooth_enable(acpi_handle handle) { - acpi_status res1, res2; - u64 result; + acpi_status result; + bool killswitch; + bool powered; + bool plugged; + int status; /* * Query ACPI to verify RFKill switch is set to 'on'. * If not, we return silently, no need to report it as * an error. */ - res1 = acpi_evaluate_integer(handle, "BTST", NULL, &result); - if (ACPI_FAILURE(res1)) - return res1; - if (!(result & 0x01)) - return 0; + status = toshiba_bluetooth_status(handle); + if (status < 0) + return status; + + killswitch = (status & BT_KILLSWITCH_MASK) ? true : false; + powered = (status & BT_POWER_MASK) ? true : false; + plugged = (status & BT_PLUGGED_MASK) ? true : false; - pr_info("Re-enabling Toshiba Bluetooth\n"); - res1 = acpi_evaluate_object(handle, "AUSB", NULL, NULL); - res2 = acpi_evaluate_object(handle, "BTPO", NULL, NULL); - if (!ACPI_FAILURE(res1) || !ACPI_FAILURE(res2)) + if (!killswitch) return 0; + /* + * This check ensures to only enable the device if it is powered + * off or detached, as some recent devices somehow pass the killswitch + * test, causing a loop enabling/disabling the device, see bug 93911. + */ + if (powered || plugged) + return 0; + + result = acpi_evaluate_object(handle, "AUSB", NULL, NULL); + if (ACPI_FAILURE(result)) { + pr_err("Could not attach USB Bluetooth device\n"); + return -ENXIO; + } + + result = acpi_evaluate_object(handle, "BTPO", NULL, NULL); + if (ACPI_FAILURE(result)) { + pr_err("Could not power ON Bluetooth device\n"); + return -ENXIO; + } + + return 0; +} + +static int toshiba_bluetooth_disable(acpi_handle handle) +{ + acpi_status result; + + result = acpi_evaluate_object(handle, "BTPF", NULL, NULL); + if (ACPI_FAILURE(result)) { + pr_err("Could not power OFF Bluetooth device\n"); + return -ENXIO; + } - pr_warn("Failed to re-enable Toshiba Bluetooth\n"); + result = acpi_evaluate_object(handle, "DUSB", NULL, NULL); + if (ACPI_FAILURE(result)) { + pr_err("Could not detach USB Bluetooth device\n"); + return -ENXIO; + } - return -ENODEV; + return 0; } static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event) @@ -99,23 +179,18 @@ static int toshiba_bt_resume(struct device *dev) static int toshiba_bt_rfkill_add(struct acpi_device *device) { - acpi_status status; - u64 bt_present; - int result = -ENODEV; + int result; - /* - * Some Toshiba laptops may have a fake TOS6205 device in - * their ACPI BIOS, so query the _STA method to see if there - * is really anything there, before trying to enable it. - */ - status = acpi_evaluate_integer(device->handle, "_STA", NULL, - &bt_present); + result = toshiba_bluetooth_present(device->handle); + if (result) + return result; - if (!ACPI_FAILURE(status) && bt_present) { - pr_info("Detected Toshiba ACPI Bluetooth device - " - "installing RFKill handler\n"); - result = toshiba_bluetooth_enable(device->handle); - } + pr_info("Toshiba ACPI Bluetooth device driver\n"); + + /* Enable the BT device */ + result = toshiba_bluetooth_enable(device->handle); + if (result) + return result; return result; } @@ -123,7 +198,7 @@ static int toshiba_bt_rfkill_add(struct acpi_device *device) static int toshiba_bt_rfkill_remove(struct acpi_device *device) { /* clean up */ - return 0; + return toshiba_bluetooth_disable(device->handle); } module_acpi_driver(toshiba_bt_rfkill_driver); |