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 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 	{ "", 0 },
222 };
223 
224 #ifdef CONFIG_PM_SLEEP
rbtn_clear_suspended_flag(void * context)225 static void ACPI_SYSTEM_XFACE rbtn_clear_suspended_flag(void *context)
226 {
227 	struct rbtn_data *rbtn_data = context;
228 
229 	rbtn_data->suspended = false;
230 }
231 
rbtn_suspend(struct device * dev)232 static int rbtn_suspend(struct device *dev)
233 {
234 	struct acpi_device *device = to_acpi_device(dev);
235 	struct rbtn_data *rbtn_data = acpi_driver_data(device);
236 
237 	rbtn_data->suspended = true;
238 
239 	return 0;
240 }
241 
rbtn_resume(struct device * dev)242 static int rbtn_resume(struct device *dev)
243 {
244 	struct acpi_device *device = to_acpi_device(dev);
245 	struct rbtn_data *rbtn_data = acpi_driver_data(device);
246 	acpi_status status;
247 
248 	/*
249 	 * Upon resume, some BIOSes send an ACPI notification thet triggers
250 	 * an unwanted input event. In order to ignore it, we use a flag
251 	 * that we set at suspend and clear once we have received the extra
252 	 * ACPI notification. Since ACPI notifications are delivered
253 	 * asynchronously to drivers, we clear the flag from the workqueue
254 	 * used to deliver the notifications. This should be enough
255 	 * to have the flag cleared only after we received the extra
256 	 * notification, if any.
257 	 */
258 	status = acpi_os_execute(OSL_NOTIFY_HANDLER,
259 			 rbtn_clear_suspended_flag, rbtn_data);
260 	if (ACPI_FAILURE(status))
261 		rbtn_clear_suspended_flag(rbtn_data);
262 
263 	return 0;
264 }
265 #endif
266 
267 static SIMPLE_DEV_PM_OPS(rbtn_pm_ops, rbtn_suspend, rbtn_resume);
268 
269 static struct acpi_driver rbtn_driver = {
270 	.name = "dell-rbtn",
271 	.ids = rbtn_ids,
272 	.drv.pm = &rbtn_pm_ops,
273 	.ops = {
274 		.add = rbtn_add,
275 		.remove = rbtn_remove,
276 		.notify = rbtn_notify,
277 	},
278 	.owner = THIS_MODULE,
279 };
280 
281 
282 /*
283  * notifier export functions
284  */
285 
286 static bool auto_remove_rfkill = true;
287 
288 static ATOMIC_NOTIFIER_HEAD(rbtn_chain_head);
289 
rbtn_inc_count(struct device * dev,void * data)290 static int rbtn_inc_count(struct device *dev, void *data)
291 {
292 	struct acpi_device *device = to_acpi_device(dev);
293 	struct rbtn_data *rbtn_data = device->driver_data;
294 	int *count = data;
295 
296 	if (rbtn_data->type == RBTN_SLIDER)
297 		(*count)++;
298 
299 	return 0;
300 }
301 
rbtn_switch_dev(struct device * dev,void * data)302 static int rbtn_switch_dev(struct device *dev, void *data)
303 {
304 	struct acpi_device *device = to_acpi_device(dev);
305 	struct rbtn_data *rbtn_data = device->driver_data;
306 	bool enable = data;
307 
308 	if (rbtn_data->type != RBTN_SLIDER)
309 		return 0;
310 
311 	if (enable)
312 		rbtn_rfkill_init(device);
313 	else
314 		rbtn_rfkill_exit(device);
315 
316 	return 0;
317 }
318 
dell_rbtn_notifier_register(struct notifier_block * nb)319 int dell_rbtn_notifier_register(struct notifier_block *nb)
320 {
321 	bool first;
322 	int count;
323 	int ret;
324 
325 	count = 0;
326 	ret = driver_for_each_device(&rbtn_driver.drv, NULL, &count,
327 				     rbtn_inc_count);
328 	if (ret || count == 0)
329 		return -ENODEV;
330 
331 	first = !rbtn_chain_head.head;
332 
333 	ret = atomic_notifier_chain_register(&rbtn_chain_head, nb);
334 	if (ret != 0)
335 		return ret;
336 
337 	if (auto_remove_rfkill && first)
338 		ret = driver_for_each_device(&rbtn_driver.drv, NULL,
339 					     (void *)false, rbtn_switch_dev);
340 
341 	return ret;
342 }
343 EXPORT_SYMBOL_GPL(dell_rbtn_notifier_register);
344 
dell_rbtn_notifier_unregister(struct notifier_block * nb)345 int dell_rbtn_notifier_unregister(struct notifier_block *nb)
346 {
347 	int ret;
348 
349 	ret = atomic_notifier_chain_unregister(&rbtn_chain_head, nb);
350 	if (ret != 0)
351 		return ret;
352 
353 	if (auto_remove_rfkill && !rbtn_chain_head.head)
354 		ret = driver_for_each_device(&rbtn_driver.drv, NULL,
355 					     (void *)true, rbtn_switch_dev);
356 
357 	return ret;
358 }
359 EXPORT_SYMBOL_GPL(dell_rbtn_notifier_unregister);
360 
361 
362 /*
363  * acpi driver functions
364  */
365 
rbtn_add(struct acpi_device * device)366 static int rbtn_add(struct acpi_device *device)
367 {
368 	struct rbtn_data *rbtn_data;
369 	enum rbtn_type type;
370 	int ret = 0;
371 
372 	type = rbtn_check(device);
373 	if (type == RBTN_UNKNOWN) {
374 		dev_info(&device->dev, "Unknown device type\n");
375 		return -EINVAL;
376 	}
377 
378 	ret = rbtn_acquire(device, true);
379 	if (ret < 0) {
380 		dev_err(&device->dev, "Cannot enable device\n");
381 		return ret;
382 	}
383 
384 	rbtn_data = devm_kzalloc(&device->dev, sizeof(*rbtn_data), GFP_KERNEL);
385 	if (!rbtn_data)
386 		return -ENOMEM;
387 
388 	rbtn_data->type = type;
389 	device->driver_data = rbtn_data;
390 
391 	switch (rbtn_data->type) {
392 	case RBTN_TOGGLE:
393 		ret = rbtn_input_init(rbtn_data);
394 		break;
395 	case RBTN_SLIDER:
396 		if (auto_remove_rfkill && rbtn_chain_head.head)
397 			ret = 0;
398 		else
399 			ret = rbtn_rfkill_init(device);
400 		break;
401 	default:
402 		ret = -EINVAL;
403 	}
404 
405 	return ret;
406 
407 }
408 
rbtn_remove(struct acpi_device * device)409 static int rbtn_remove(struct acpi_device *device)
410 {
411 	struct rbtn_data *rbtn_data = device->driver_data;
412 
413 	switch (rbtn_data->type) {
414 	case RBTN_TOGGLE:
415 		rbtn_input_exit(rbtn_data);
416 		break;
417 	case RBTN_SLIDER:
418 		rbtn_rfkill_exit(device);
419 		break;
420 	default:
421 		break;
422 	}
423 
424 	rbtn_acquire(device, false);
425 	device->driver_data = NULL;
426 
427 	return 0;
428 }
429 
rbtn_notify(struct acpi_device * device,u32 event)430 static void rbtn_notify(struct acpi_device *device, u32 event)
431 {
432 	struct rbtn_data *rbtn_data = device->driver_data;
433 
434 	/*
435 	 * Some BIOSes send a notification at resume.
436 	 * Ignore it to prevent unwanted input events.
437 	 */
438 	if (rbtn_data->suspended) {
439 		dev_dbg(&device->dev, "ACPI notification ignored\n");
440 		return;
441 	}
442 
443 	if (event != 0x80) {
444 		dev_info(&device->dev, "Received unknown event (0x%x)\n",
445 			 event);
446 		return;
447 	}
448 
449 	switch (rbtn_data->type) {
450 	case RBTN_TOGGLE:
451 		rbtn_input_event(rbtn_data);
452 		break;
453 	case RBTN_SLIDER:
454 		rbtn_rfkill_event(device);
455 		atomic_notifier_call_chain(&rbtn_chain_head, event, device);
456 		break;
457 	default:
458 		break;
459 	}
460 }
461 
462 
463 /*
464  * module functions
465  */
466 
467 module_acpi_driver(rbtn_driver);
468 
469 module_param(auto_remove_rfkill, bool, 0444);
470 
471 MODULE_PARM_DESC(auto_remove_rfkill, "Automatically remove rfkill devices when "
472 				     "other modules start receiving events "
473 				     "from this module and re-add them when "
474 				     "the last module stops receiving events "
475 				     "(default true)");
476 MODULE_DEVICE_TABLE(acpi, rbtn_ids);
477 MODULE_DESCRIPTION("Dell Airplane Mode Switch driver");
478 MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
479 MODULE_LICENSE("GPL");
480