brintos

brintos / linux-shallow public Read only

0
0
Text · 6.8 KiB · dad2c3e Raw
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