blob: 7706541f9f79d0c77fa39623e4e12fb436bb7753 [file] [log] [blame]
Ivo van Doorncf4328c2007-05-07 00:34:20 -07001/*
2 * Input layer to RF Kill interface connector
3 *
4 * Copyright (c) 2007 Dmitry Torokhov
5 */
6
7/*
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License version 2 as published
10 * by the Free Software Foundation.
11 */
12
13#include <linux/module.h>
14#include <linux/input.h>
15#include <linux/slab.h>
16#include <linux/workqueue.h>
17#include <linux/init.h>
18#include <linux/rfkill.h>
Geert Uytterhoeven56f26f72008-10-13 21:59:03 +020019#include <linux/sched.h>
Ivo van Doorncf4328c2007-05-07 00:34:20 -070020
Ivo van Doornfe242cf2007-09-27 14:57:05 -070021#include "rfkill-input.h"
22
Ivo van Doorncf4328c2007-05-07 00:34:20 -070023MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
24MODULE_DESCRIPTION("Input layer to RF switch connector");
25MODULE_LICENSE("GPL");
26
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -030027enum rfkill_input_master_mode {
28 RFKILL_INPUT_MASTER_DONOTHING = 0,
29 RFKILL_INPUT_MASTER_RESTORE = 1,
30 RFKILL_INPUT_MASTER_UNBLOCKALL = 2,
31 RFKILL_INPUT_MASTER_MAX, /* marker */
32};
33
34static enum rfkill_input_master_mode rfkill_master_switch_mode =
35 RFKILL_INPUT_MASTER_UNBLOCKALL;
36module_param_named(master_switch_mode, rfkill_master_switch_mode, uint, 0);
37MODULE_PARM_DESC(master_switch_mode,
38 "SW_RFKILL_ALL ON should: 0=do nothing; 1=restore; 2=unblock all");
39
40enum rfkill_global_sched_op {
41 RFKILL_GLOBAL_OP_EPO = 0,
42 RFKILL_GLOBAL_OP_RESTORE,
43 RFKILL_GLOBAL_OP_UNLOCK,
44 RFKILL_GLOBAL_OP_UNBLOCK,
45};
46
47/*
48 * Currently, the code marked with RFKILL_NEED_SWSET is inactive.
49 * If handling of EV_SW SW_WLAN/WWAN/BLUETOOTH/etc is needed in the
50 * future, when such events are added, that code will be necessary.
51 */
52
Ivo van Doorncf4328c2007-05-07 00:34:20 -070053struct rfkill_task {
54 struct work_struct work;
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -030055
56 /* ensures that task is serialized */
57 struct mutex mutex;
58
59 /* protects everything below */
60 spinlock_t lock;
61
62 /* pending regular switch operations (1=pending) */
63 unsigned long sw_pending[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
64
65#ifdef RFKILL_NEED_SWSET
66 /* set operation pending (1=pending) */
67 unsigned long sw_setpending[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
68
69 /* desired state for pending set operation (1=unblock) */
70 unsigned long sw_newstate[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
71#endif
72
73 /* should the state be complemented (1=yes) */
74 unsigned long sw_togglestate[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
75
76 bool global_op_pending;
77 enum rfkill_global_sched_op op;
Ivo van Doorncf4328c2007-05-07 00:34:20 -070078};
79
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -030080static void __rfkill_handle_global_op(enum rfkill_global_sched_op op)
81{
82 unsigned int i;
83
84 switch (op) {
85 case RFKILL_GLOBAL_OP_EPO:
86 rfkill_epo();
87 break;
88 case RFKILL_GLOBAL_OP_RESTORE:
89 rfkill_restore_states();
90 break;
91 case RFKILL_GLOBAL_OP_UNLOCK:
92 rfkill_remove_epo_lock();
93 break;
94 case RFKILL_GLOBAL_OP_UNBLOCK:
95 rfkill_remove_epo_lock();
96 for (i = 0; i < RFKILL_TYPE_MAX; i++)
97 rfkill_switch_all(i, RFKILL_STATE_UNBLOCKED);
98 break;
99 default:
100 /* memory corruption or bug, fail safely */
101 rfkill_epo();
102 WARN(1, "Unknown requested operation %d! "
103 "rfkill Emergency Power Off activated\n",
104 op);
105 }
106}
107
108#ifdef RFKILL_NEED_SWSET
109static void __rfkill_handle_normal_op(const enum rfkill_type type,
110 const bool sp, const bool s, const bool c)
111{
112 enum rfkill_state state;
113
114 if (sp)
115 state = (s) ? RFKILL_STATE_UNBLOCKED :
116 RFKILL_STATE_SOFT_BLOCKED;
117 else
118 state = rfkill_get_global_state(type);
119
120 if (c)
121 state = rfkill_state_complement(state);
122
123 rfkill_switch_all(type, state);
124}
125#else
126static void __rfkill_handle_normal_op(const enum rfkill_type type,
127 const bool c)
128{
129 enum rfkill_state state;
130
131 state = rfkill_get_global_state(type);
132 if (c)
133 state = rfkill_state_complement(state);
134
135 rfkill_switch_all(type, state);
136}
137#endif
138
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700139static void rfkill_task_handler(struct work_struct *work)
140{
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300141 struct rfkill_task *task =
142 container_of(work, struct rfkill_task, work);
143 bool doit = true;
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700144
145 mutex_lock(&task->mutex);
146
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300147 spin_lock_irq(&task->lock);
148 while (doit) {
149 if (task->global_op_pending) {
150 enum rfkill_global_sched_op op = task->op;
151 task->global_op_pending = false;
152 memset(task->sw_pending, 0, sizeof(task->sw_pending));
153 spin_unlock_irq(&task->lock);
154
155 __rfkill_handle_global_op(op);
156
157 /* make sure we do at least one pass with
158 * !task->global_op_pending */
159 spin_lock_irq(&task->lock);
160 continue;
161 } else if (!rfkill_is_epo_lock_active()) {
162 unsigned int i = 0;
163
164 while (!task->global_op_pending &&
165 i < RFKILL_TYPE_MAX) {
166 if (test_and_clear_bit(i, task->sw_pending)) {
167 bool c;
168#ifdef RFKILL_NEED_SWSET
169 bool sp, s;
170 sp = test_and_clear_bit(i,
171 task->sw_setpending);
172 s = test_bit(i, task->sw_newstate);
173#endif
174 c = test_and_clear_bit(i,
175 task->sw_togglestate);
176 spin_unlock_irq(&task->lock);
177
178#ifdef RFKILL_NEED_SWSET
179 __rfkill_handle_normal_op(i, sp, s, c);
180#else
181 __rfkill_handle_normal_op(i, c);
182#endif
183
184 spin_lock_irq(&task->lock);
185 }
186 i++;
187 }
188 }
189 doit = task->global_op_pending;
190 }
191 spin_unlock_irq(&task->lock);
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700192
193 mutex_unlock(&task->mutex);
194}
195
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300196static struct rfkill_task rfkill_task = {
197 .work = __WORK_INITIALIZER(rfkill_task.work,
198 rfkill_task_handler),
199 .mutex = __MUTEX_INITIALIZER(rfkill_task.mutex),
200 .lock = __SPIN_LOCK_UNLOCKED(rfkill_task.lock),
201};
202
203static void rfkill_schedule_global_op(enum rfkill_global_sched_op op)
Henrique de Moraes Holschuh4081f002008-06-23 17:23:07 -0300204{
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300205 unsigned long flags;
206
207 spin_lock_irqsave(&rfkill_task.lock, flags);
208 rfkill_task.op = op;
209 rfkill_task.global_op_pending = true;
210 schedule_work(&rfkill_task.work);
211 spin_unlock_irqrestore(&rfkill_task.lock, flags);
Henrique de Moraes Holschuh4081f002008-06-23 17:23:07 -0300212}
213
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300214#ifdef RFKILL_NEED_SWSET
215/* Use this if you need to add EV_SW SW_WLAN/WWAN/BLUETOOTH/etc handling */
Henrique de Moraes Holschuh4081f002008-06-23 17:23:07 -0300216
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300217static void rfkill_schedule_set(enum rfkill_type type,
Henrique de Moraes Holschuh28f089c2008-06-23 17:22:58 -0300218 enum rfkill_state desired_state)
219{
220 unsigned long flags;
221
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300222 if (rfkill_is_epo_lock_active())
Henrique de Moraes Holschuh4081f002008-06-23 17:23:07 -0300223 return;
224
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300225 spin_lock_irqsave(&rfkill_task.lock, flags);
226 if (!rfkill_task.global_op_pending) {
227 set_bit(type, rfkill_task.sw_pending);
228 set_bit(type, rfkill_task.sw_setpending);
229 clear_bit(type, rfkill_task.sw_togglestate);
230 if (desired_state)
231 set_bit(type, rfkill_task.sw_newstate);
232 else
233 clear_bit(type, rfkill_task.sw_newstate);
234 schedule_work(&rfkill_task.work);
Henrique de Moraes Holschuh28f089c2008-06-23 17:22:58 -0300235 }
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300236 spin_unlock_irqrestore(&rfkill_task.lock, flags);
Henrique de Moraes Holschuh28f089c2008-06-23 17:22:58 -0300237}
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300238#endif
Henrique de Moraes Holschuh28f089c2008-06-23 17:22:58 -0300239
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300240static void rfkill_schedule_toggle(enum rfkill_type type)
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700241{
Ingo Molnare6c91162007-07-14 18:50:15 -0700242 unsigned long flags;
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700243
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300244 if (rfkill_is_epo_lock_active())
Henrique de Moraes Holschuh4081f002008-06-23 17:23:07 -0300245 return;
246
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300247 spin_lock_irqsave(&rfkill_task.lock, flags);
248 if (!rfkill_task.global_op_pending) {
249 set_bit(type, rfkill_task.sw_pending);
250 change_bit(type, rfkill_task.sw_togglestate);
251 schedule_work(&rfkill_task.work);
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700252 }
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300253 spin_unlock_irqrestore(&rfkill_task.lock, flags);
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700254}
255
Henrique de Moraes Holschuh6e28fbe2008-07-31 10:53:57 -0300256static void rfkill_schedule_evsw_rfkillall(int state)
257{
Henrique de Moraes Holschuh6e28fbe2008-07-31 10:53:57 -0300258 if (state) {
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300259 switch (rfkill_master_switch_mode) {
260 case RFKILL_INPUT_MASTER_UNBLOCKALL:
261 rfkill_schedule_global_op(RFKILL_GLOBAL_OP_UNBLOCK);
262 break;
263 case RFKILL_INPUT_MASTER_RESTORE:
264 rfkill_schedule_global_op(RFKILL_GLOBAL_OP_RESTORE);
265 break;
266 case RFKILL_INPUT_MASTER_DONOTHING:
267 rfkill_schedule_global_op(RFKILL_GLOBAL_OP_UNLOCK);
268 break;
269 default:
270 /* memory corruption or driver bug! fail safely */
271 rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO);
272 WARN(1, "Unknown rfkill_master_switch_mode (%d), "
273 "driver bug or memory corruption detected!\n",
274 rfkill_master_switch_mode);
275 break;
276 }
Henrique de Moraes Holschuh6e28fbe2008-07-31 10:53:57 -0300277 } else
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300278 rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO);
Henrique de Moraes Holschuh6e28fbe2008-07-31 10:53:57 -0300279}
280
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700281static void rfkill_event(struct input_handle *handle, unsigned int type,
Henrique de Moraes Holschuh28f089c2008-06-23 17:22:58 -0300282 unsigned int code, int data)
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700283{
Henrique de Moraes Holschuh28f089c2008-06-23 17:22:58 -0300284 if (type == EV_KEY && data == 1) {
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300285 enum rfkill_type t;
286
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700287 switch (code) {
288 case KEY_WLAN:
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300289 t = RFKILL_TYPE_WLAN;
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700290 break;
291 case KEY_BLUETOOTH:
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300292 t = RFKILL_TYPE_BLUETOOTH;
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700293 break;
Ivo van Doorne0665482007-09-13 09:21:31 +0200294 case KEY_UWB:
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300295 t = RFKILL_TYPE_UWB;
Ivo van Doorne0665482007-09-13 09:21:31 +0200296 break;
Iñaky Pérez-González303d9bf2008-01-23 13:40:27 -0800297 case KEY_WIMAX:
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300298 t = RFKILL_TYPE_WIMAX;
Iñaky Pérez-González303d9bf2008-01-23 13:40:27 -0800299 break;
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700300 default:
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300301 return;
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700302 }
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300303 rfkill_schedule_toggle(t);
304 return;
Henrique de Moraes Holschuh28f089c2008-06-23 17:22:58 -0300305 } else if (type == EV_SW) {
306 switch (code) {
307 case SW_RFKILL_ALL:
Henrique de Moraes Holschuh6e28fbe2008-07-31 10:53:57 -0300308 rfkill_schedule_evsw_rfkillall(data);
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300309 return;
Henrique de Moraes Holschuh28f089c2008-06-23 17:22:58 -0300310 default:
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300311 return;
Henrique de Moraes Holschuh28f089c2008-06-23 17:22:58 -0300312 }
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700313 }
314}
315
316static int rfkill_connect(struct input_handler *handler, struct input_dev *dev,
317 const struct input_device_id *id)
318{
319 struct input_handle *handle;
320 int error;
321
322 handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
323 if (!handle)
324 return -ENOMEM;
325
326 handle->dev = dev;
327 handle->handler = handler;
328 handle->name = "rfkill";
329
Henrique de Moraes Holschuh6e28fbe2008-07-31 10:53:57 -0300330 /* causes rfkill_start() to be called */
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700331 error = input_register_handle(handle);
332 if (error)
333 goto err_free_handle;
334
335 error = input_open_device(handle);
336 if (error)
337 goto err_unregister_handle;
338
339 return 0;
340
341 err_unregister_handle:
342 input_unregister_handle(handle);
343 err_free_handle:
344 kfree(handle);
345 return error;
346}
347
Henrique de Moraes Holschuh6e28fbe2008-07-31 10:53:57 -0300348static void rfkill_start(struct input_handle *handle)
349{
350 /* Take event_lock to guard against configuration changes, we
351 * should be able to deal with concurrency with rfkill_event()
352 * just fine (which event_lock will also avoid). */
353 spin_lock_irq(&handle->dev->event_lock);
354
355 if (test_bit(EV_SW, handle->dev->evbit)) {
356 if (test_bit(SW_RFKILL_ALL, handle->dev->swbit))
357 rfkill_schedule_evsw_rfkillall(test_bit(SW_RFKILL_ALL,
358 handle->dev->sw));
359 /* add resync for further EV_SW events here */
360 }
361
362 spin_unlock_irq(&handle->dev->event_lock);
363}
364
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700365static void rfkill_disconnect(struct input_handle *handle)
366{
367 input_close_device(handle);
368 input_unregister_handle(handle);
369 kfree(handle);
370}
371
372static const struct input_device_id rfkill_ids[] = {
373 {
374 .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
Jiri Slaby7b19ada2007-10-18 23:40:32 -0700375 .evbit = { BIT_MASK(EV_KEY) },
376 .keybit = { [BIT_WORD(KEY_WLAN)] = BIT_MASK(KEY_WLAN) },
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700377 },
378 {
379 .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
Jiri Slaby7b19ada2007-10-18 23:40:32 -0700380 .evbit = { BIT_MASK(EV_KEY) },
381 .keybit = { [BIT_WORD(KEY_BLUETOOTH)] = BIT_MASK(KEY_BLUETOOTH) },
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700382 },
Ivo van Doorne0665482007-09-13 09:21:31 +0200383 {
384 .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
Jiri Slaby7b19ada2007-10-18 23:40:32 -0700385 .evbit = { BIT_MASK(EV_KEY) },
386 .keybit = { [BIT_WORD(KEY_UWB)] = BIT_MASK(KEY_UWB) },
Ivo van Doorne0665482007-09-13 09:21:31 +0200387 },
Iñaky Pérez-González303d9bf2008-01-23 13:40:27 -0800388 {
389 .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
390 .evbit = { BIT_MASK(EV_KEY) },
391 .keybit = { [BIT_WORD(KEY_WIMAX)] = BIT_MASK(KEY_WIMAX) },
392 },
Henrique de Moraes Holschuh28f089c2008-06-23 17:22:58 -0300393 {
394 .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT,
395 .evbit = { BIT(EV_SW) },
396 .swbit = { [BIT_WORD(SW_RFKILL_ALL)] = BIT_MASK(SW_RFKILL_ALL) },
397 },
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700398 { }
399};
400
401static struct input_handler rfkill_handler = {
402 .event = rfkill_event,
403 .connect = rfkill_connect,
404 .disconnect = rfkill_disconnect,
Henrique de Moraes Holschuh6e28fbe2008-07-31 10:53:57 -0300405 .start = rfkill_start,
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700406 .name = "rfkill",
407 .id_table = rfkill_ids,
408};
409
410static int __init rfkill_handler_init(void)
411{
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300412 if (rfkill_master_switch_mode >= RFKILL_INPUT_MASTER_MAX)
413 return -EINVAL;
414
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700415 return input_register_handler(&rfkill_handler);
416}
417
418static void __exit rfkill_handler_exit(void)
419{
420 input_unregister_handler(&rfkill_handler);
421 flush_scheduled_work();
Henrique de Moraes Holschuhd0039222008-10-09 21:49:33 -0300422 rfkill_remove_epo_lock();
Ivo van Doorncf4328c2007-05-07 00:34:20 -0700423}
424
425module_init(rfkill_handler_init);
426module_exit(rfkill_handler_exit);