xref: /linux/drivers/platform/x86/dell/dell-rbtn.c (revision 2c142b63c8ee982cdfdba49a616027c266294838)
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 = &param;
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