1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 Dell Airplane Mode Switch driver
4 Copyright (C) 2014-2015 Pali Rohár <pali@kernel.org>
5
6 */
7
8 #include <linux/module.h>
9 #include <linux/acpi.h>
10 #include <linux/rfkill.h>
11 #include <linux/input.h>
12 #include <linux/platform_device.h>
13
14 #include "dell-rbtn.h"
15
16 enum rbtn_type {
17 RBTN_UNKNOWN,
18 RBTN_TOGGLE,
19 RBTN_SLIDER,
20 };
21
22 struct rbtn_data {
23 enum rbtn_type type;
24 struct rfkill *rfkill;
25 struct input_dev *input_dev;
26 bool suspended;
27 };
28
29
30 /*
31 * acpi functions
32 */
33
rbtn_check(struct acpi_device * device)34 static enum rbtn_type rbtn_check(struct acpi_device *device)
35 {
36 unsigned long long output;
37 acpi_status status;
38
39 status = acpi_evaluate_integer(device->handle, "CRBT", NULL, &output);
40 if (ACPI_FAILURE(status))
41 return RBTN_UNKNOWN;
42
43 switch (output) {
44 case 0:
45 case 1:
46 return RBTN_TOGGLE;
47 case 2:
48 case 3:
49 return RBTN_SLIDER;
50 default:
51 return RBTN_UNKNOWN;
52 }
53 }
54
rbtn_get(struct acpi_device * device)55 static int rbtn_get(struct acpi_device *device)
56 {
57 unsigned long long output;
58 acpi_status status;
59
60 status = acpi_evaluate_integer(device->handle, "GRBT", NULL, &output);
61 if (ACPI_FAILURE(status))
62 return -EINVAL;
63
64 return !output;
65 }
66
rbtn_acquire(struct acpi_device * device,bool enable)67 static int rbtn_acquire(struct acpi_device *device, bool enable)
68 {
69 struct acpi_object_list input;
70 union acpi_object param;
71 acpi_status status;
72
73 param.type = ACPI_TYPE_INTEGER;
74 param.integer.value = enable;
75 input.count = 1;
76 input.pointer = ¶m;
77
78 status = acpi_evaluate_object(device->handle, "ARBT", &input, NULL);
79 if (ACPI_FAILURE(status))
80 return -EINVAL;
81
82 return 0;
83 }
84
85
86 /*
87 * rfkill device
88 */
89
rbtn_rfkill_query(struct rfkill * rfkill,void * data)90 static void rbtn_rfkill_query(struct rfkill *rfkill, void *data)
91 {
92 struct acpi_device *device = data;
93 int state;
94
95 state = rbtn_get(device);
96 if (state < 0)
97 return;
98
99 rfkill_set_states(rfkill, state, state);
100 }
101
rbtn_rfkill_set_block(void * data,bool blocked)102 static int rbtn_rfkill_set_block(void *data, bool blocked)
103 {
104 /* NOTE: setting soft rfkill state is not supported */
105 return -EINVAL;
106 }
107
108 static const struct rfkill_ops rbtn_ops = {
109 .query = rbtn_rfkill_query,
110 .set_block = rbtn_rfkill_set_block,
111 };
112
rbtn_rfkill_init(struct device * dev)113 static int rbtn_rfkill_init(struct device *dev)
114 {
115 struct rbtn_data *rbtn_data = dev_get_drvdata(dev);
116 int ret;
117
118 if (rbtn_data->rfkill)
119 return 0;
120
121 /*
122 * NOTE: rbtn controls all radio devices, not only WLAN
123 * but rfkill interface does not support "ANY" type
124 * so "WLAN" type is used
125 */
126 rbtn_data->rfkill = rfkill_alloc("dell-rbtn", dev, RFKILL_TYPE_WLAN,
127 &rbtn_ops, ACPI_COMPANION(dev));
128 if (!rbtn_data->rfkill)
129 return -ENOMEM;
130
131 ret = rfkill_register(rbtn_data->rfkill);
132 if (ret) {
133 rfkill_destroy(rbtn_data->rfkill);
134 rbtn_data->rfkill = NULL;
135 return ret;
136 }
137
138 return 0;
139 }
140
rbtn_rfkill_exit(struct device * dev)141 static void rbtn_rfkill_exit(struct device *dev)
142 {
143 struct rbtn_data *rbtn_data = dev_get_drvdata(dev);
144
145 if (!rbtn_data->rfkill)
146 return;
147
148 rfkill_unregister(rbtn_data->rfkill);
149 rfkill_destroy(rbtn_data->rfkill);
150 rbtn_data->rfkill = NULL;
151 }
152
rbtn_rfkill_event(struct device * dev)153 static void rbtn_rfkill_event(struct device *dev)
154 {
155 struct rbtn_data *rbtn_data = dev_get_drvdata(dev);
156
157 if (rbtn_data->rfkill)
158 rbtn_rfkill_query(rbtn_data->rfkill, ACPI_COMPANION(dev));
159 }
160
161
162 /*
163 * input device
164 */
165
rbtn_input_init(struct rbtn_data * rbtn_data)166 static int rbtn_input_init(struct rbtn_data *rbtn_data)
167 {
168 int ret;
169
170 rbtn_data->input_dev = input_allocate_device();
171 if (!rbtn_data->input_dev)
172 return -ENOMEM;
173
174 rbtn_data->input_dev->name = "DELL Wireless hotkeys";
175 rbtn_data->input_dev->phys = "dellabce/input0";
176 rbtn_data->input_dev->id.bustype = BUS_HOST;
177 rbtn_data->input_dev->evbit[0] = BIT(EV_KEY);
178 set_bit(KEY_RFKILL, rbtn_data->input_dev->keybit);
179
180 ret = input_register_device(rbtn_data->input_dev);
181 if (ret) {
182 input_free_device(rbtn_data->input_dev);
183 rbtn_data->input_dev = NULL;
184 return ret;
185 }
186
187 return 0;
188 }
189
rbtn_input_exit(struct rbtn_data * rbtn_data)190 static void rbtn_input_exit(struct rbtn_data *rbtn_data)
191 {
192 input_unregister_device(rbtn_data->input_dev);
193 rbtn_data->input_dev = NULL;
194 }
195
rbtn_input_event(struct rbtn_data * rbtn_data)196 static void rbtn_input_event(struct rbtn_data *rbtn_data)
197 {
198 input_report_key(rbtn_data->input_dev, KEY_RFKILL, 1);
199 input_sync(rbtn_data->input_dev);
200 input_report_key(rbtn_data->input_dev, KEY_RFKILL, 0);
201 input_sync(rbtn_data->input_dev);
202 }
203
204
205 /*
206 * acpi driver
207 */
208
209 static int rbtn_probe(struct platform_device *pdev);
210 static void rbtn_remove(struct platform_device *pdev);
211 static void rbtn_notify(acpi_handle handle, u32 event, void *data);
212
213 static const struct acpi_device_id rbtn_ids[] = {
214 { "DELRBTN", 0 },
215 { "DELLABCE", 0 },
216
217 /*
218 * This driver can also handle the "DELLABC6" device that
219 * appears on the XPS 13 9350, but that device is disabled by
220 * the DSDT unless booted with acpi_osi="!Windows 2012"
221 * acpi_osi="!Windows 2013".
222 *
223 * According to Mario at Dell:
224 *
225 * DELLABC6 is a custom interface that was created solely to
226 * have airplane mode support for Windows 7. For Windows 10
227 * the proper interface is to use that which is handled by
228 * intel-hid. A OEM airplane mode driver is not used.
229 *
230 * Since the kernel doesn't identify as Windows 7 it would be
231 * incorrect to do attempt to use that interface.
232 *
233 * Even if we override _OSI and bind to DELLABC6, we end up with
234 * inconsistent behavior in which userspace can get out of sync
235 * with the rfkill state as it conflicts with events from
236 * intel-hid.
237 *
238 * The upshot is that it is better to just ignore DELLABC6
239 * devices.
240 */
241
242 { "", 0 },
243 };
244
245 #ifdef CONFIG_PM_SLEEP
rbtn_clear_suspended_flag(void * context)246 static void ACPI_SYSTEM_XFACE rbtn_clear_suspended_flag(void *context)
247 {
248 struct rbtn_data *rbtn_data = context;
249
250 rbtn_data->suspended = false;
251 }
252
rbtn_suspend(struct device * dev)253 static int rbtn_suspend(struct device *dev)
254 {
255 struct rbtn_data *rbtn_data = dev_get_drvdata(dev);
256
257 rbtn_data->suspended = true;
258
259 return 0;
260 }
261
rbtn_resume(struct device * dev)262 static int rbtn_resume(struct device *dev)
263 {
264 struct rbtn_data *rbtn_data = dev_get_drvdata(dev);
265 acpi_status status;
266
267 /*
268 * Upon resume, some BIOSes send an ACPI notification thet triggers
269 * an unwanted input event. In order to ignore it, we use a flag
270 * that we set at suspend and clear once we have received the extra
271 * ACPI notification. Since ACPI notifications are delivered
272 * asynchronously to drivers, we clear the flag from the workqueue
273 * used to deliver the notifications. This should be enough
274 * to have the flag cleared only after we received the extra
275 * notification, if any.
276 */
277 status = acpi_os_execute(OSL_NOTIFY_HANDLER,
278 rbtn_clear_suspended_flag, rbtn_data);
279 if (ACPI_FAILURE(status))
280 rbtn_clear_suspended_flag(rbtn_data);
281
282 return 0;
283 }
284 #endif
285
286 static SIMPLE_DEV_PM_OPS(rbtn_pm_ops, rbtn_suspend, rbtn_resume);
287
288 static struct platform_driver rbtn_driver = {
289 .probe = rbtn_probe,
290 .remove = rbtn_remove,
291 .driver = {
292 .name = "dell-rbtn",
293 .acpi_match_table = rbtn_ids,
294 .pm = &rbtn_pm_ops,
295 },
296 };
297
298
299 /*
300 * notifier export functions
301 */
302
303 static bool auto_remove_rfkill = true;
304
305 static ATOMIC_NOTIFIER_HEAD(rbtn_chain_head);
306
rbtn_inc_count(struct device * dev,void * data)307 static int rbtn_inc_count(struct device *dev, void *data)
308 {
309 struct rbtn_data *rbtn_data = dev_get_drvdata(dev);
310 int *count = data;
311
312 if (rbtn_data->type == RBTN_SLIDER)
313 (*count)++;
314
315 return 0;
316 }
317
rbtn_switch_dev(struct device * dev,void * data)318 static int rbtn_switch_dev(struct device *dev, void *data)
319 {
320 struct rbtn_data *rbtn_data = dev_get_drvdata(dev);
321 bool enable = data;
322
323 if (rbtn_data->type != RBTN_SLIDER)
324 return 0;
325
326 if (enable)
327 rbtn_rfkill_init(dev);
328 else
329 rbtn_rfkill_exit(dev);
330
331 return 0;
332 }
333
dell_rbtn_notifier_register(struct notifier_block * nb)334 int dell_rbtn_notifier_register(struct notifier_block *nb)
335 {
336 bool first;
337 int count;
338 int ret;
339
340 count = 0;
341 ret = driver_for_each_device(&rbtn_driver.driver, NULL, &count,
342 rbtn_inc_count);
343 if (ret || count == 0)
344 return -ENODEV;
345
346 first = !rbtn_chain_head.head;
347
348 ret = atomic_notifier_chain_register(&rbtn_chain_head, nb);
349 if (ret != 0)
350 return ret;
351
352 if (auto_remove_rfkill && first)
353 ret = driver_for_each_device(&rbtn_driver.driver, NULL,
354 (void *)false, rbtn_switch_dev);
355
356 return ret;
357 }
358 EXPORT_SYMBOL_GPL(dell_rbtn_notifier_register);
359
dell_rbtn_notifier_unregister(struct notifier_block * nb)360 int dell_rbtn_notifier_unregister(struct notifier_block *nb)
361 {
362 int ret;
363
364 ret = atomic_notifier_chain_unregister(&rbtn_chain_head, nb);
365 if (ret != 0)
366 return ret;
367
368 if (auto_remove_rfkill && !rbtn_chain_head.head)
369 ret = driver_for_each_device(&rbtn_driver.driver, NULL,
370 (void *)true, rbtn_switch_dev);
371
372 return ret;
373 }
374 EXPORT_SYMBOL_GPL(dell_rbtn_notifier_unregister);
375
376
377 /*
378 * acpi driver functions
379 */
380
rbtn_cleanup(struct device * dev)381 static void rbtn_cleanup(struct device *dev)
382 {
383 struct rbtn_data *rbtn_data = dev_get_drvdata(dev);
384
385 switch (rbtn_data->type) {
386 case RBTN_TOGGLE:
387 rbtn_input_exit(rbtn_data);
388 break;
389 case RBTN_SLIDER:
390 rbtn_rfkill_exit(dev);
391 break;
392 default:
393 break;
394 }
395 }
396
rbtn_probe(struct platform_device * pdev)397 static int rbtn_probe(struct platform_device *pdev)
398 {
399 struct rbtn_data *rbtn_data;
400 struct acpi_device *device;
401 enum rbtn_type type;
402 int ret = 0;
403
404 device = ACPI_COMPANION(&pdev->dev);
405 if (!device)
406 return -ENODEV;
407
408 type = rbtn_check(device);
409 if (type == RBTN_UNKNOWN) {
410 dev_info(&pdev->dev, "Unknown device type\n");
411 return -EINVAL;
412 }
413
414 rbtn_data = devm_kzalloc(&pdev->dev, sizeof(*rbtn_data), GFP_KERNEL);
415 if (!rbtn_data)
416 return -ENOMEM;
417
418 ret = rbtn_acquire(device, true);
419 if (ret < 0) {
420 dev_err(&pdev->dev, "Cannot enable device\n");
421 return ret;
422 }
423
424 platform_set_drvdata(pdev, rbtn_data);
425
426 rbtn_data->type = type;
427
428 switch (rbtn_data->type) {
429 case RBTN_TOGGLE:
430 ret = rbtn_input_init(rbtn_data);
431 break;
432 case RBTN_SLIDER:
433 if (auto_remove_rfkill && rbtn_chain_head.head)
434 ret = 0;
435 else
436 ret = rbtn_rfkill_init(&pdev->dev);
437 break;
438 default:
439 ret = -EINVAL;
440 break;
441 }
442 if (ret)
443 goto err;
444
445 ret = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY,
446 rbtn_notify, &pdev->dev);
447 if (ret)
448 goto err_cleanup;
449
450 return 0;
451
452 err_cleanup:
453 rbtn_cleanup(&pdev->dev);
454 err:
455 rbtn_acquire(device, false);
456 return ret;
457 }
458
rbtn_remove(struct platform_device * pdev)459 static void rbtn_remove(struct platform_device *pdev)
460 {
461 struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
462
463 acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, rbtn_notify);
464 rbtn_cleanup(&pdev->dev);
465 rbtn_acquire(device, false);
466 }
467
rbtn_notify(acpi_handle handle,u32 event,void * data)468 static void rbtn_notify(acpi_handle handle, u32 event, void *data)
469 {
470 struct device *dev = data;
471 struct rbtn_data *rbtn_data = dev_get_drvdata(dev);
472
473 /*
474 * Some BIOSes send a notification at resume.
475 * Ignore it to prevent unwanted input events.
476 */
477 if (rbtn_data->suspended) {
478 dev_dbg(dev, "ACPI notification ignored\n");
479 return;
480 }
481
482 if (event != 0x80) {
483 dev_info(dev, "Received unknown event (0x%x)\n",
484 event);
485 return;
486 }
487
488 switch (rbtn_data->type) {
489 case RBTN_TOGGLE:
490 rbtn_input_event(rbtn_data);
491 break;
492 case RBTN_SLIDER:
493 rbtn_rfkill_event(dev);
494 atomic_notifier_call_chain(&rbtn_chain_head, event, NULL);
495 break;
496 default:
497 break;
498 }
499 }
500
501 module_platform_driver(rbtn_driver);
502
503 module_param(auto_remove_rfkill, bool, 0444);
504
505 MODULE_PARM_DESC(auto_remove_rfkill, "Automatically remove rfkill devices when "
506 "other modules start receiving events "
507 "from this module and re-add them when "
508 "the last module stops receiving events "
509 "(default true)");
510 MODULE_DEVICE_TABLE(acpi, rbtn_ids);
511 MODULE_DESCRIPTION("Dell Airplane Mode Switch driver");
512 MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
513 MODULE_LICENSE("GPL");
514