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 
33 MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@gmail.com>");
34 MODULE_DESCRIPTION("Toshiba Laptop ACPI Bluetooth Enable Driver");
35 MODULE_LICENSE("GPL");
36 
37 static int toshiba_bt_rfkill_add(struct acpi_device *device);
38 static int toshiba_bt_rfkill_remove(struct acpi_device *device);
39 static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event);
40 
41 static const struct acpi_device_id bt_device_ids[] = {
42 	{ "TOS6205", 0},
43 	{ "", 0},
44 };
45 MODULE_DEVICE_TABLE(acpi, bt_device_ids);
46 
47 #ifdef CONFIG_PM_SLEEP
48 static int toshiba_bt_resume(struct device *dev);
49 #endif
50 static SIMPLE_DEV_PM_OPS(toshiba_bt_pm, NULL, toshiba_bt_resume);
51 
52 static 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 
65 static 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 
87 static 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 
103 static 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 
149 static 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 
168 static 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
174 static int toshiba_bt_resume(struct device *dev)
175 {
176 	return toshiba_bluetooth_enable(to_acpi_device(dev)->handle);
177 }
178 #endif
179 
180 static 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 
198 static int toshiba_bt_rfkill_remove(struct acpi_device *device)
199 {
200 	/* clean up */
201 	return toshiba_bluetooth_disable(device->handle);
202 }
203 
204 module_acpi_driver(toshiba_bt_rfkill_driver);
205