Commit | Line | Data |
---|---|---|
cf4328cd ID |
1 | /* |
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> | |
19 | ||
fe242cfd ID |
20 | #include "rfkill-input.h" |
21 | ||
cf4328cd ID |
22 | MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>"); |
23 | MODULE_DESCRIPTION("Input layer to RF switch connector"); | |
24 | MODULE_LICENSE("GPL"); | |
25 | ||
26 | struct rfkill_task { | |
27 | struct work_struct work; | |
28 | enum rfkill_type type; | |
29 | struct mutex mutex; /* ensures that task is serialized */ | |
30 | spinlock_t lock; /* for accessing last and desired state */ | |
31 | unsigned long last; /* last schedule */ | |
32 | enum rfkill_state desired_state; /* on/off */ | |
33 | enum rfkill_state current_state; /* on/off */ | |
34 | }; | |
35 | ||
36 | static void rfkill_task_handler(struct work_struct *work) | |
37 | { | |
38 | struct rfkill_task *task = container_of(work, struct rfkill_task, work); | |
39 | enum rfkill_state state; | |
40 | ||
41 | mutex_lock(&task->mutex); | |
42 | ||
43 | /* | |
44 | * Use temp variable to fetch desired state to keep it | |
45 | * consistent even if rfkill_schedule_toggle() runs in | |
46 | * another thread or interrupts us. | |
47 | */ | |
48 | state = task->desired_state; | |
49 | ||
50 | if (state != task->current_state) { | |
51 | rfkill_switch_all(task->type, state); | |
52 | task->current_state = state; | |
53 | } | |
54 | ||
55 | mutex_unlock(&task->mutex); | |
56 | } | |
57 | ||
58 | static void rfkill_schedule_toggle(struct rfkill_task *task) | |
59 | { | |
e6c9116d | 60 | unsigned long flags; |
cf4328cd ID |
61 | |
62 | spin_lock_irqsave(&task->lock, flags); | |
63 | ||
64 | if (time_after(jiffies, task->last + msecs_to_jiffies(200))) { | |
65 | task->desired_state = !task->desired_state; | |
66 | task->last = jiffies; | |
67 | schedule_work(&task->work); | |
68 | } | |
69 | ||
70 | spin_unlock_irqrestore(&task->lock, flags); | |
71 | } | |
72 | ||
73 | #define DEFINE_RFKILL_TASK(n, t) \ | |
74 | struct rfkill_task n = { \ | |
75 | .work = __WORK_INITIALIZER(n.work, \ | |
76 | rfkill_task_handler), \ | |
77 | .type = t, \ | |
78 | .mutex = __MUTEX_INITIALIZER(n.mutex), \ | |
79 | .lock = __SPIN_LOCK_UNLOCKED(n.lock), \ | |
80 | .desired_state = RFKILL_STATE_ON, \ | |
81 | .current_state = RFKILL_STATE_ON, \ | |
82 | } | |
83 | ||
84 | static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN); | |
85 | static DEFINE_RFKILL_TASK(rfkill_bt, RFKILL_TYPE_BLUETOOTH); | |
e0665486 | 86 | static DEFINE_RFKILL_TASK(rfkill_uwb, RFKILL_TYPE_UWB); |
cf4328cd ID |
87 | |
88 | static void rfkill_event(struct input_handle *handle, unsigned int type, | |
2b81bff4 | 89 | unsigned int code, int down) |
cf4328cd ID |
90 | { |
91 | if (type == EV_KEY && down == 1) { | |
92 | switch (code) { | |
93 | case KEY_WLAN: | |
94 | rfkill_schedule_toggle(&rfkill_wlan); | |
95 | break; | |
96 | case KEY_BLUETOOTH: | |
97 | rfkill_schedule_toggle(&rfkill_bt); | |
98 | break; | |
e0665486 ID |
99 | case KEY_UWB: |
100 | rfkill_schedule_toggle(&rfkill_uwb); | |
101 | break; | |
cf4328cd ID |
102 | default: |
103 | break; | |
104 | } | |
105 | } | |
106 | } | |
107 | ||
108 | static int rfkill_connect(struct input_handler *handler, struct input_dev *dev, | |
109 | const struct input_device_id *id) | |
110 | { | |
111 | struct input_handle *handle; | |
112 | int error; | |
113 | ||
114 | handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); | |
115 | if (!handle) | |
116 | return -ENOMEM; | |
117 | ||
118 | handle->dev = dev; | |
119 | handle->handler = handler; | |
120 | handle->name = "rfkill"; | |
121 | ||
122 | error = input_register_handle(handle); | |
123 | if (error) | |
124 | goto err_free_handle; | |
125 | ||
126 | error = input_open_device(handle); | |
127 | if (error) | |
128 | goto err_unregister_handle; | |
129 | ||
130 | return 0; | |
131 | ||
132 | err_unregister_handle: | |
133 | input_unregister_handle(handle); | |
134 | err_free_handle: | |
135 | kfree(handle); | |
136 | return error; | |
137 | } | |
138 | ||
139 | static void rfkill_disconnect(struct input_handle *handle) | |
140 | { | |
141 | input_close_device(handle); | |
142 | input_unregister_handle(handle); | |
143 | kfree(handle); | |
144 | } | |
145 | ||
146 | static const struct input_device_id rfkill_ids[] = { | |
147 | { | |
148 | .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, | |
7b19ada2 JS |
149 | .evbit = { BIT_MASK(EV_KEY) }, |
150 | .keybit = { [BIT_WORD(KEY_WLAN)] = BIT_MASK(KEY_WLAN) }, | |
cf4328cd ID |
151 | }, |
152 | { | |
153 | .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, | |
7b19ada2 JS |
154 | .evbit = { BIT_MASK(EV_KEY) }, |
155 | .keybit = { [BIT_WORD(KEY_BLUETOOTH)] = BIT_MASK(KEY_BLUETOOTH) }, | |
cf4328cd | 156 | }, |
e0665486 ID |
157 | { |
158 | .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, | |
7b19ada2 JS |
159 | .evbit = { BIT_MASK(EV_KEY) }, |
160 | .keybit = { [BIT_WORD(KEY_UWB)] = BIT_MASK(KEY_UWB) }, | |
e0665486 | 161 | }, |
cf4328cd ID |
162 | { } |
163 | }; | |
164 | ||
165 | static struct input_handler rfkill_handler = { | |
166 | .event = rfkill_event, | |
167 | .connect = rfkill_connect, | |
168 | .disconnect = rfkill_disconnect, | |
169 | .name = "rfkill", | |
170 | .id_table = rfkill_ids, | |
171 | }; | |
172 | ||
173 | static int __init rfkill_handler_init(void) | |
174 | { | |
175 | return input_register_handler(&rfkill_handler); | |
176 | } | |
177 | ||
178 | static void __exit rfkill_handler_exit(void) | |
179 | { | |
180 | input_unregister_handler(&rfkill_handler); | |
181 | flush_scheduled_work(); | |
182 | } | |
183 | ||
184 | module_init(rfkill_handler_init); | |
185 | module_exit(rfkill_handler_exit); |