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