1/* 2 * Toshiba Bluetooth Enable Driver 3 * 4 * Copyright (C) 2009 Jes Sorensen <Jes.Sorensen@gmail.com> 5 * Copyright (C) 2015 Azael Avalos <coproscefalo@gmail.com> 6 * 7 * Thanks to Matthew Garrett for background info on ACPI innards which 8 * normal people aren't meant to understand :-) 9 * 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 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 */ 20 21#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 22 23#include <linux/kernel.h> 24#include <linux/module.h> 25#include <linux/init.h> 26#include <linux/types.h> 27#include <linux/acpi.h> 28 29#define BT_KILLSWITCH_MASK 0x01 30#define BT_PLUGGED_MASK 0x40 31#define BT_POWER_MASK 0x80 32 33MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@gmail.com>"); 34MODULE_DESCRIPTION("Toshiba Laptop ACPI Bluetooth Enable Driver"); 35MODULE_LICENSE("GPL"); 36 37static int toshiba_bt_rfkill_add(struct acpi_device *device); 38static int toshiba_bt_rfkill_remove(struct acpi_device *device); 39static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event); 40 41static const struct acpi_device_id bt_device_ids[] = { 42 { "TOS6205", 0}, 43 { "", 0}, 44}; 45MODULE_DEVICE_TABLE(acpi, bt_device_ids); 46 47#ifdef CONFIG_PM_SLEEP 48static int toshiba_bt_resume(struct device *dev); 49#endif 50static SIMPLE_DEV_PM_OPS(toshiba_bt_pm, NULL, toshiba_bt_resume); 51 52static struct acpi_driver toshiba_bt_rfkill_driver = { 53 .name = "Toshiba BT", 54 .class = "Toshiba", 55 .ids = bt_device_ids, 56 .ops = { 57 .add = toshiba_bt_rfkill_add, 58 .remove = toshiba_bt_rfkill_remove, 59 .notify = toshiba_bt_rfkill_notify, 60 }, 61 .owner = THIS_MODULE, 62 .drv.pm = &toshiba_bt_pm, 63}; 64 65static int toshiba_bluetooth_present(acpi_handle handle) 66{ 67 acpi_status result; 68 u64 bt_present; 69 70 /* 71 * Some Toshiba laptops may have a fake TOS6205 device in 72 * their ACPI BIOS, so query the _STA method to see if there 73 * is really anything there. 74 */ 75 result = acpi_evaluate_integer(handle, "_STA", NULL, &bt_present); 76 if (ACPI_FAILURE(result)) { 77 pr_err("ACPI call to query Bluetooth presence failed"); 78 return -ENXIO; 79 } else if (!bt_present) { 80 pr_info("Bluetooth device not present\n"); 81 return -ENODEV; 82 } 83 84 return 0; 85} 86 87static int toshiba_bluetooth_status(acpi_handle handle) 88{ 89 acpi_status result; 90 u64 status; 91 92 result = acpi_evaluate_integer(handle, "BTST", NULL, &status); 93 if (ACPI_FAILURE(result)) { 94 pr_err("Could not get Bluetooth device status\n"); 95 return -ENXIO; 96 } 97 98 pr_info("Bluetooth status %llu\n", status); 99 100 return status; 101} 102 103static int toshiba_bluetooth_enable(acpi_handle handle) 104{ 105 acpi_status result; 106 bool killswitch; 107 bool powered; 108 bool plugged; 109 int status; 110 111 /* 112 * Query ACPI to verify RFKill switch is set to 'on'. 113 * If not, we return silently, no need to report it as 114 * an error. 115 */ 116 status = toshiba_bluetooth_status(handle); 117 if (status < 0) 118 return status; 119 120 killswitch = (status & BT_KILLSWITCH_MASK) ? true : false; 121 powered = (status & BT_POWER_MASK) ? true : false; 122 plugged = (status & BT_PLUGGED_MASK) ? true : false; 123 124 if (!killswitch) 125 return 0; 126 /* 127 * This check ensures to only enable the device if it is powered 128 * off or detached, as some recent devices somehow pass the killswitch 129 * test, causing a loop enabling/disabling the device, see bug 93911. 130 */ 131 if (powered || plugged) 132 return 0; 133 134 result = acpi_evaluate_object(handle, "AUSB", NULL, NULL); 135 if (ACPI_FAILURE(result)) { 136 pr_err("Could not attach USB Bluetooth device\n"); 137 return -ENXIO; 138 } 139 140 result = acpi_evaluate_object(handle, "BTPO", NULL, NULL); 141 if (ACPI_FAILURE(result)) { 142 pr_err("Could not power ON Bluetooth device\n"); 143 return -ENXIO; 144 } 145 146 return 0; 147} 148 149static int toshiba_bluetooth_disable(acpi_handle handle) 150{ 151 acpi_status result; 152 153 result = acpi_evaluate_object(handle, "BTPF", NULL, NULL); 154 if (ACPI_FAILURE(result)) { 155 pr_err("Could not power OFF Bluetooth device\n"); 156 return -ENXIO; 157 } 158 159 result = acpi_evaluate_object(handle, "DUSB", NULL, NULL); 160 if (ACPI_FAILURE(result)) { 161 pr_err("Could not detach USB Bluetooth device\n"); 162 return -ENXIO; 163 } 164 165 return 0; 166} 167 168static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event) 169{ 170 toshiba_bluetooth_enable(device->handle); 171} 172 173#ifdef CONFIG_PM_SLEEP 174static int toshiba_bt_resume(struct device *dev) 175{ 176 return toshiba_bluetooth_enable(to_acpi_device(dev)->handle); 177} 178#endif 179 180static int toshiba_bt_rfkill_add(struct acpi_device *device) 181{ 182 int result; 183 184 result = toshiba_bluetooth_present(device->handle); 185 if (result) 186 return result; 187 188 pr_info("Toshiba ACPI Bluetooth device driver\n"); 189 190 /* Enable the BT device */ 191 result = toshiba_bluetooth_enable(device->handle); 192 if (result) 193 return result; 194 195 return result; 196} 197 198static int toshiba_bt_rfkill_remove(struct acpi_device *device) 199{ 200 /* clean up */ 201 return toshiba_bluetooth_disable(device->handle); 202} 203 204module_acpi_driver(toshiba_bt_rfkill_driver); 205