]> Git Repo - linux.git/blob - drivers/platform/x86/toshiba_bluetooth.c
Linux 6.14-rc3
[linux.git] / drivers / platform / x86 / toshiba_bluetooth.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Toshiba Bluetooth Enable Driver
4  *
5  * Copyright (C) 2009 Jes Sorensen <[email protected]>
6  * Copyright (C) 2015 Azael Avalos <[email protected]>
7  *
8  * Thanks to Matthew Garrett for background info on ACPI innards which
9  * normal people aren't meant to understand :-)
10  */
11
12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13
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      0x01
22 #define BT_PLUGGED_MASK         0x40
23 #define BT_POWER_MASK           0x80
24
25 MODULE_AUTHOR("Jes Sorensen <[email protected]>");
26 MODULE_DESCRIPTION("Toshiba Laptop ACPI Bluetooth Enable Driver");
27 MODULE_LICENSE("GPL");
28
29 struct 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
38 static int toshiba_bt_rfkill_add(struct acpi_device *device);
39 static void toshiba_bt_rfkill_remove(struct acpi_device *device);
40 static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event);
41
42 static const struct acpi_device_id bt_device_ids[] = {
43         { "TOS6205", 0},
44         { "", 0},
45 };
46 MODULE_DEVICE_TABLE(acpi, bt_device_ids);
47
48 #ifdef CONFIG_PM_SLEEP
49 static int toshiba_bt_resume(struct device *dev);
50 #endif
51 static SIMPLE_DEV_PM_OPS(toshiba_bt_pm, NULL, toshiba_bt_resume);
52
53 static 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
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\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
89 static 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
103 static 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
122 static 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 */
142 static 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 */
163 static 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         else
178                 ret = toshiba_bluetooth_enable(bt_dev->acpi_dev->handle);
179
180         return ret;
181 }
182
183 static 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 strange
192          * fish. It only provides a BT event when the switch is flipped to
193          * the 'on' position. When flipping it to 'off', the USB device is
194          * simply pulled away underneath us, without any BT event being
195          * delivered.
196          */
197         rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch);
198 }
199
200 static const struct rfkill_ops rfk_ops = {
201         .set_block = bt_rfkill_set_block,
202         .poll = bt_rfkill_poll,
203 };
204
205 /* ACPI driver functions */
206 static 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_SLEEP
217 static 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 #endif
233
234 static 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
281 static 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
296 module_acpi_driver(toshiba_bt_rfkill_driver);
This page took 0.047976 seconds and 4 git commands to generate.