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