297 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Toshiba Bluetooth Enable Driver4 *5 * Copyright (C) 2009 Jes Sorensen <Jes.Sorensen@gmail.com>6 * Copyright (C) 2015 Azael Avalos <coproscefalo@gmail.com>7 *8 * Thanks to Matthew Garrett for background info on ACPI innards which9 * normal people aren't meant to understand :-)10 */11 12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt13 14#include <linux/kernel.h>15#include <linux/module.h>16#include <linux/init.h>17#include <linux/types.h>18#include <linux/acpi.h>19#include <linux/rfkill.h>20 21#define BT_KILLSWITCH_MASK 0x0122#define BT_PLUGGED_MASK 0x4023#define BT_POWER_MASK 0x8024 25MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@gmail.com>");26MODULE_DESCRIPTION("Toshiba Laptop ACPI Bluetooth Enable Driver");27MODULE_LICENSE("GPL");28 29struct toshiba_bluetooth_dev {30 struct acpi_device *acpi_dev;31 struct rfkill *rfk;32 33 bool killswitch;34 bool plugged;35 bool powered;36};37 38static int toshiba_bt_rfkill_add(struct acpi_device *device);39static void toshiba_bt_rfkill_remove(struct acpi_device *device);40static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event);41 42static const struct acpi_device_id bt_device_ids[] = {43 { "TOS6205", 0},44 { "", 0},45};46MODULE_DEVICE_TABLE(acpi, bt_device_ids);47 48#ifdef CONFIG_PM_SLEEP49static int toshiba_bt_resume(struct device *dev);50#endif51static SIMPLE_DEV_PM_OPS(toshiba_bt_pm, NULL, toshiba_bt_resume);52 53static struct acpi_driver toshiba_bt_rfkill_driver = {54 .name = "Toshiba BT",55 .class = "Toshiba",56 .ids = bt_device_ids,57 .ops = {58 .add = toshiba_bt_rfkill_add,59 .remove = toshiba_bt_rfkill_remove,60 .notify = toshiba_bt_rfkill_notify,61 },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 in72 * their ACPI BIOS, so query the _STA method to see if there73 * 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\n");78 return -ENXIO;79 }80 81 if (!bt_present) {82 pr_info("Bluetooth device not present\n");83 return -ENODEV;84 }85 86 return 0;87}88 89static int toshiba_bluetooth_status(acpi_handle handle)90{91 acpi_status result;92 u64 status;93 94 result = acpi_evaluate_integer(handle, "BTST", NULL, &status);95 if (ACPI_FAILURE(result)) {96 pr_err("Could not get Bluetooth device status\n");97 return -ENXIO;98 }99 100 return status;101}102 103static int toshiba_bluetooth_enable(acpi_handle handle)104{105 acpi_status result;106 107 result = acpi_evaluate_object(handle, "AUSB", NULL, NULL);108 if (ACPI_FAILURE(result)) {109 pr_err("Could not attach USB Bluetooth device\n");110 return -ENXIO;111 }112 113 result = acpi_evaluate_object(handle, "BTPO", NULL, NULL);114 if (ACPI_FAILURE(result)) {115 pr_err("Could not power ON Bluetooth device\n");116 return -ENXIO;117 }118 119 return 0;120}121 122static int toshiba_bluetooth_disable(acpi_handle handle)123{124 acpi_status result;125 126 result = acpi_evaluate_object(handle, "BTPF", NULL, NULL);127 if (ACPI_FAILURE(result)) {128 pr_err("Could not power OFF Bluetooth device\n");129 return -ENXIO;130 }131 132 result = acpi_evaluate_object(handle, "DUSB", NULL, NULL);133 if (ACPI_FAILURE(result)) {134 pr_err("Could not detach USB Bluetooth device\n");135 return -ENXIO;136 }137 138 return 0;139}140 141/* Helper function */142static int toshiba_bluetooth_sync_status(struct toshiba_bluetooth_dev *bt_dev)143{144 int status;145 146 status = toshiba_bluetooth_status(bt_dev->acpi_dev->handle);147 if (status < 0) {148 pr_err("Could not sync bluetooth device status\n");149 return status;150 }151 152 bt_dev->killswitch = (status & BT_KILLSWITCH_MASK) ? true : false;153 bt_dev->plugged = (status & BT_PLUGGED_MASK) ? true : false;154 bt_dev->powered = (status & BT_POWER_MASK) ? true : false;155 156 pr_debug("Bluetooth status %d killswitch %d plugged %d powered %d\n",157 status, bt_dev->killswitch, bt_dev->plugged, bt_dev->powered);158 159 return 0;160}161 162/* RFKill handlers */163static int bt_rfkill_set_block(void *data, bool blocked)164{165 struct toshiba_bluetooth_dev *bt_dev = data;166 int ret;167 168 ret = toshiba_bluetooth_sync_status(bt_dev);169 if (ret)170 return ret;171 172 if (!bt_dev->killswitch)173 return 0;174 175 if (blocked)176 ret = toshiba_bluetooth_disable(bt_dev->acpi_dev->handle);177 else178 ret = toshiba_bluetooth_enable(bt_dev->acpi_dev->handle);179 180 return ret;181}182 183static void bt_rfkill_poll(struct rfkill *rfkill, void *data)184{185 struct toshiba_bluetooth_dev *bt_dev = data;186 187 if (toshiba_bluetooth_sync_status(bt_dev))188 return;189 190 /*191 * Note the Toshiba Bluetooth RFKill switch seems to be a strange192 * fish. It only provides a BT event when the switch is flipped to193 * the 'on' position. When flipping it to 'off', the USB device is194 * simply pulled away underneath us, without any BT event being195 * delivered.196 */197 rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch);198}199 200static const struct rfkill_ops rfk_ops = {201 .set_block = bt_rfkill_set_block,202 .poll = bt_rfkill_poll,203};204 205/* ACPI driver functions */206static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event)207{208 struct toshiba_bluetooth_dev *bt_dev = acpi_driver_data(device);209 210 if (toshiba_bluetooth_sync_status(bt_dev))211 return;212 213 rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch);214}215 216#ifdef CONFIG_PM_SLEEP217static int toshiba_bt_resume(struct device *dev)218{219 struct toshiba_bluetooth_dev *bt_dev;220 int ret;221 222 bt_dev = acpi_driver_data(to_acpi_device(dev));223 224 ret = toshiba_bluetooth_sync_status(bt_dev);225 if (ret)226 return ret;227 228 rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch);229 230 return 0;231}232#endif233 234static int toshiba_bt_rfkill_add(struct acpi_device *device)235{236 struct toshiba_bluetooth_dev *bt_dev;237 int result;238 239 result = toshiba_bluetooth_present(device->handle);240 if (result)241 return result;242 243 pr_info("Toshiba ACPI Bluetooth device driver\n");244 245 bt_dev = kzalloc(sizeof(*bt_dev), GFP_KERNEL);246 if (!bt_dev)247 return -ENOMEM;248 bt_dev->acpi_dev = device;249 device->driver_data = bt_dev;250 dev_set_drvdata(&device->dev, bt_dev);251 252 result = toshiba_bluetooth_sync_status(bt_dev);253 if (result) {254 kfree(bt_dev);255 return result;256 }257 258 bt_dev->rfk = rfkill_alloc("Toshiba Bluetooth",259 &device->dev,260 RFKILL_TYPE_BLUETOOTH,261 &rfk_ops,262 bt_dev);263 if (!bt_dev->rfk) {264 pr_err("Unable to allocate rfkill device\n");265 kfree(bt_dev);266 return -ENOMEM;267 }268 269 rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch);270 271 result = rfkill_register(bt_dev->rfk);272 if (result) {273 pr_err("Unable to register rfkill device\n");274 rfkill_destroy(bt_dev->rfk);275 kfree(bt_dev);276 }277 278 return result;279}280 281static void toshiba_bt_rfkill_remove(struct acpi_device *device)282{283 struct toshiba_bluetooth_dev *bt_dev = acpi_driver_data(device);284 285 /* clean up */286 if (bt_dev->rfk) {287 rfkill_unregister(bt_dev->rfk);288 rfkill_destroy(bt_dev->rfk);289 }290 291 kfree(bt_dev);292 293 toshiba_bluetooth_disable(device->handle);294}295 296module_acpi_driver(toshiba_bt_rfkill_driver);297