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