]>
Commit | Line | Data |
---|---|---|
cf4328cd | 1 | /* |
fe242cfd | 2 | * Copyright (C) 2006 - 2007 Ivo van Doorn |
cf4328cd ID |
3 | * Copyright (C) 2007 Dmitry Torokhov |
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 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the | |
17 | * Free Software Foundation, Inc., | |
18 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
19 | */ | |
20 | ||
21 | #include <linux/kernel.h> | |
22 | #include <linux/module.h> | |
23 | #include <linux/init.h> | |
24 | #include <linux/workqueue.h> | |
25 | #include <linux/capability.h> | |
26 | #include <linux/list.h> | |
27 | #include <linux/mutex.h> | |
28 | #include <linux/rfkill.h> | |
29 | ||
27366223 MB |
30 | /* Get declaration of rfkill_switch_all() to shut up sparse. */ |
31 | #include "rfkill-input.h" | |
32 | ||
33 | ||
cf4328cd ID |
34 | MODULE_AUTHOR("Ivo van Doorn <IvDoorn@gmail.com>"); |
35 | MODULE_VERSION("1.0"); | |
36 | MODULE_DESCRIPTION("RF switch support"); | |
37 | MODULE_LICENSE("GPL"); | |
38 | ||
39 | static LIST_HEAD(rfkill_list); /* list of registered rf switches */ | |
40 | static DEFINE_MUTEX(rfkill_mutex); | |
41 | ||
5005657c | 42 | static unsigned int rfkill_default_state = RFKILL_STATE_UNBLOCKED; |
e954b0b8 HMH |
43 | module_param_named(default_state, rfkill_default_state, uint, 0444); |
44 | MODULE_PARM_DESC(default_state, | |
45 | "Default initial state for all radio types, 0 = radio off"); | |
46 | ||
cf4328cd ID |
47 | static enum rfkill_state rfkill_states[RFKILL_TYPE_MAX]; |
48 | ||
79399a8d HMH |
49 | static BLOCKING_NOTIFIER_HEAD(rfkill_notifier_list); |
50 | ||
51 | ||
52 | /** | |
53 | * register_rfkill_notifier - Add notifier to rfkill notifier chain | |
54 | * @nb: pointer to the new entry to add to the chain | |
55 | * | |
56 | * See blocking_notifier_chain_register() for return value and further | |
57 | * observations. | |
58 | * | |
59 | * Adds a notifier to the rfkill notifier chain. The chain will be | |
60 | * called with a pointer to the relevant rfkill structure as a parameter, | |
61 | * refer to include/linux/rfkill.h for the possible events. | |
62 | * | |
63 | * Notifiers added to this chain are to always return NOTIFY_DONE. This | |
64 | * chain is a blocking notifier chain: notifiers can sleep. | |
65 | * | |
66 | * Calls to this chain may have been done through a workqueue. One must | |
67 | * assume unordered asynchronous behaviour, there is no way to know if | |
68 | * actions related to the event that generated the notification have been | |
69 | * carried out already. | |
70 | */ | |
71 | int register_rfkill_notifier(struct notifier_block *nb) | |
72 | { | |
73 | return blocking_notifier_chain_register(&rfkill_notifier_list, nb); | |
74 | } | |
75 | EXPORT_SYMBOL_GPL(register_rfkill_notifier); | |
76 | ||
77 | /** | |
78 | * unregister_rfkill_notifier - remove notifier from rfkill notifier chain | |
79 | * @nb: pointer to the entry to remove from the chain | |
80 | * | |
81 | * See blocking_notifier_chain_unregister() for return value and further | |
82 | * observations. | |
83 | * | |
84 | * Removes a notifier from the rfkill notifier chain. | |
85 | */ | |
86 | int unregister_rfkill_notifier(struct notifier_block *nb) | |
87 | { | |
88 | return blocking_notifier_chain_unregister(&rfkill_notifier_list, nb); | |
89 | } | |
90 | EXPORT_SYMBOL_GPL(unregister_rfkill_notifier); | |
91 | ||
135900c1 MB |
92 | |
93 | static void rfkill_led_trigger(struct rfkill *rfkill, | |
94 | enum rfkill_state state) | |
95 | { | |
96 | #ifdef CONFIG_RFKILL_LEDS | |
97 | struct led_trigger *led = &rfkill->led_trigger; | |
98 | ||
99 | if (!led->name) | |
100 | return; | |
5005657c | 101 | if (state != RFKILL_STATE_UNBLOCKED) |
135900c1 MB |
102 | led_trigger_event(led, LED_OFF); |
103 | else | |
104 | led_trigger_event(led, LED_FULL); | |
105 | #endif /* CONFIG_RFKILL_LEDS */ | |
106 | } | |
107 | ||
96185664 DES |
108 | #ifdef CONFIG_RFKILL_LEDS |
109 | static void rfkill_led_trigger_activate(struct led_classdev *led) | |
110 | { | |
111 | struct rfkill *rfkill = container_of(led->trigger, | |
112 | struct rfkill, led_trigger); | |
113 | ||
114 | rfkill_led_trigger(rfkill, rfkill->state); | |
115 | } | |
116 | #endif /* CONFIG_RFKILL_LEDS */ | |
117 | ||
79399a8d HMH |
118 | static void notify_rfkill_state_change(struct rfkill *rfkill) |
119 | { | |
120 | blocking_notifier_call_chain(&rfkill_notifier_list, | |
121 | RFKILL_STATE_CHANGED, | |
122 | rfkill); | |
123 | } | |
124 | ||
801e49af HMH |
125 | static void update_rfkill_state(struct rfkill *rfkill) |
126 | { | |
79399a8d | 127 | enum rfkill_state newstate, oldstate; |
801e49af HMH |
128 | |
129 | if (rfkill->get_state) { | |
130 | mutex_lock(&rfkill->mutex); | |
79399a8d HMH |
131 | if (!rfkill->get_state(rfkill->data, &newstate)) { |
132 | oldstate = rfkill->state; | |
801e49af | 133 | rfkill->state = newstate; |
79399a8d HMH |
134 | if (oldstate != newstate) |
135 | notify_rfkill_state_change(rfkill); | |
136 | } | |
801e49af HMH |
137 | mutex_unlock(&rfkill->mutex); |
138 | } | |
139 | } | |
140 | ||
5005657c HMH |
141 | /** |
142 | * rfkill_toggle_radio - wrapper for toggle_radio hook | |
5005657c HMH |
143 | * @rfkill: the rfkill struct to use |
144 | * @force: calls toggle_radio even if cache says it is not needed, | |
145 | * and also makes sure notifications of the state will be | |
146 | * sent even if it didn't change | |
147 | * @state: the new state to call toggle_radio() with | |
148 | * | |
0f687e9a HMH |
149 | * Calls rfkill->toggle_radio, enforcing the API for toggle_radio |
150 | * calls and handling all the red tape such as issuing notifications | |
151 | * if the call is successful. | |
152 | * | |
e10e0dfe HMH |
153 | * Suspended devices are not touched at all, and -EAGAIN is returned. |
154 | * | |
435307a3 HMH |
155 | * Note that the @force parameter cannot override a (possibly cached) |
156 | * state of RFKILL_STATE_HARD_BLOCKED. Any device making use of | |
5005657c HMH |
157 | * RFKILL_STATE_HARD_BLOCKED implements either get_state() or |
158 | * rfkill_force_state(), so the cache either is bypassed or valid. | |
159 | * | |
160 | * Note that we do call toggle_radio for RFKILL_STATE_SOFT_BLOCKED | |
161 | * even if the radio is in RFKILL_STATE_HARD_BLOCKED state, so as to | |
162 | * give the driver a hint that it should double-BLOCK the transmitter. | |
163 | * | |
064af111 | 164 | * Caller must have acquired rfkill->mutex. |
5005657c | 165 | */ |
cf4328cd | 166 | static int rfkill_toggle_radio(struct rfkill *rfkill, |
526324b6 HMH |
167 | enum rfkill_state state, |
168 | int force) | |
cf4328cd | 169 | { |
7f4c5341 | 170 | int retval = 0; |
801e49af HMH |
171 | enum rfkill_state oldstate, newstate; |
172 | ||
e10e0dfe HMH |
173 | if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP)) |
174 | return -EBUSY; | |
175 | ||
801e49af HMH |
176 | oldstate = rfkill->state; |
177 | ||
526324b6 | 178 | if (rfkill->get_state && !force && |
801e49af HMH |
179 | !rfkill->get_state(rfkill->data, &newstate)) |
180 | rfkill->state = newstate; | |
cf4328cd | 181 | |
5005657c HMH |
182 | switch (state) { |
183 | case RFKILL_STATE_HARD_BLOCKED: | |
184 | /* typically happens when refreshing hardware state, | |
185 | * such as on resume */ | |
186 | state = RFKILL_STATE_SOFT_BLOCKED; | |
187 | break; | |
188 | case RFKILL_STATE_UNBLOCKED: | |
189 | /* force can't override this, only rfkill_force_state() can */ | |
190 | if (rfkill->state == RFKILL_STATE_HARD_BLOCKED) | |
191 | return -EPERM; | |
192 | break; | |
193 | case RFKILL_STATE_SOFT_BLOCKED: | |
194 | /* nothing to do, we want to give drivers the hint to double | |
195 | * BLOCK even a transmitter that is already in state | |
196 | * RFKILL_STATE_HARD_BLOCKED */ | |
197 | break; | |
198 | } | |
199 | ||
526324b6 | 200 | if (force || state != rfkill->state) { |
cf4328cd | 201 | retval = rfkill->toggle_radio(rfkill->data, state); |
5005657c HMH |
202 | /* never allow a HARD->SOFT downgrade! */ |
203 | if (!retval && rfkill->state != RFKILL_STATE_HARD_BLOCKED) | |
cf4328cd ID |
204 | rfkill->state = state; |
205 | } | |
206 | ||
79399a8d | 207 | if (force || rfkill->state != oldstate) { |
801e49af | 208 | rfkill_led_trigger(rfkill, rfkill->state); |
79399a8d HMH |
209 | notify_rfkill_state_change(rfkill); |
210 | } | |
801e49af | 211 | |
cf4328cd ID |
212 | return retval; |
213 | } | |
214 | ||
215 | /** | |
216 | * rfkill_switch_all - Toggle state of all switches of given type | |
435307a3 | 217 | * @type: type of interfaces to be affected |
cf4328cd ID |
218 | * @state: the new state |
219 | * | |
435307a3 HMH |
220 | * This function toggles the state of all switches of given type, |
221 | * unless a specific switch is claimed by userspace (in which case, | |
e10e0dfe | 222 | * that switch is left alone) or suspended. |
cf4328cd | 223 | */ |
cf4328cd ID |
224 | void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) |
225 | { | |
226 | struct rfkill *rfkill; | |
227 | ||
228 | mutex_lock(&rfkill_mutex); | |
229 | ||
230 | rfkill_states[type] = state; | |
231 | ||
232 | list_for_each_entry(rfkill, &rfkill_list, node) { | |
064af111 HMH |
233 | if ((!rfkill->user_claim) && (rfkill->type == type)) { |
234 | mutex_lock(&rfkill->mutex); | |
526324b6 | 235 | rfkill_toggle_radio(rfkill, state, 0); |
064af111 HMH |
236 | mutex_unlock(&rfkill->mutex); |
237 | } | |
cf4328cd ID |
238 | } |
239 | ||
240 | mutex_unlock(&rfkill_mutex); | |
241 | } | |
242 | EXPORT_SYMBOL(rfkill_switch_all); | |
243 | ||
4081f00d HMH |
244 | /** |
245 | * rfkill_epo - emergency power off all transmitters | |
246 | * | |
e10e0dfe HMH |
247 | * This kicks all non-suspended rfkill devices to RFKILL_STATE_SOFT_BLOCKED, |
248 | * ignoring everything in its path but rfkill_mutex and rfkill->mutex. | |
4081f00d HMH |
249 | */ |
250 | void rfkill_epo(void) | |
251 | { | |
252 | struct rfkill *rfkill; | |
253 | ||
254 | mutex_lock(&rfkill_mutex); | |
255 | list_for_each_entry(rfkill, &rfkill_list, node) { | |
064af111 | 256 | mutex_lock(&rfkill->mutex); |
5005657c | 257 | rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); |
064af111 | 258 | mutex_unlock(&rfkill->mutex); |
4081f00d HMH |
259 | } |
260 | mutex_unlock(&rfkill_mutex); | |
261 | } | |
262 | EXPORT_SYMBOL_GPL(rfkill_epo); | |
263 | ||
801e49af HMH |
264 | /** |
265 | * rfkill_force_state - Force the internal rfkill radio state | |
266 | * @rfkill: pointer to the rfkill class to modify. | |
267 | * @state: the current radio state the class should be forced to. | |
268 | * | |
269 | * This function updates the internal state of the radio cached | |
270 | * by the rfkill class. It should be used when the driver gets | |
271 | * a notification by the firmware/hardware of the current *real* | |
272 | * state of the radio rfkill switch. | |
273 | * | |
2fd9b221 HMH |
274 | * Devices which are subject to external changes on their rfkill |
275 | * state (such as those caused by a hardware rfkill line) MUST | |
276 | * have their driver arrange to call rfkill_force_state() as soon | |
277 | * as possible after such a change. | |
278 | * | |
279 | * This function may not be called from an atomic context. | |
801e49af HMH |
280 | */ |
281 | int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) | |
282 | { | |
79399a8d HMH |
283 | enum rfkill_state oldstate; |
284 | ||
5005657c HMH |
285 | if (state != RFKILL_STATE_SOFT_BLOCKED && |
286 | state != RFKILL_STATE_UNBLOCKED && | |
287 | state != RFKILL_STATE_HARD_BLOCKED) | |
801e49af HMH |
288 | return -EINVAL; |
289 | ||
290 | mutex_lock(&rfkill->mutex); | |
79399a8d HMH |
291 | |
292 | oldstate = rfkill->state; | |
801e49af | 293 | rfkill->state = state; |
79399a8d HMH |
294 | |
295 | if (state != oldstate) | |
296 | notify_rfkill_state_change(rfkill); | |
297 | ||
801e49af HMH |
298 | mutex_unlock(&rfkill->mutex); |
299 | ||
300 | return 0; | |
301 | } | |
302 | EXPORT_SYMBOL(rfkill_force_state); | |
303 | ||
cf4328cd ID |
304 | static ssize_t rfkill_name_show(struct device *dev, |
305 | struct device_attribute *attr, | |
306 | char *buf) | |
307 | { | |
308 | struct rfkill *rfkill = to_rfkill(dev); | |
309 | ||
310 | return sprintf(buf, "%s\n", rfkill->name); | |
311 | } | |
312 | ||
99c632e5 | 313 | static const char *rfkill_get_type_str(enum rfkill_type type) |
cf4328cd | 314 | { |
99c632e5 | 315 | switch (type) { |
cf4328cd | 316 | case RFKILL_TYPE_WLAN: |
99c632e5 | 317 | return "wlan"; |
cf4328cd | 318 | case RFKILL_TYPE_BLUETOOTH: |
99c632e5 | 319 | return "bluetooth"; |
e0665486 | 320 | case RFKILL_TYPE_UWB: |
99c632e5 | 321 | return "ultrawideband"; |
303d9bf6 | 322 | case RFKILL_TYPE_WIMAX: |
99c632e5 | 323 | return "wimax"; |
477576a0 | 324 | case RFKILL_TYPE_WWAN: |
99c632e5 | 325 | return "wwan"; |
cf4328cd ID |
326 | default: |
327 | BUG(); | |
328 | } | |
99c632e5 HMH |
329 | } |
330 | ||
331 | static ssize_t rfkill_type_show(struct device *dev, | |
332 | struct device_attribute *attr, | |
333 | char *buf) | |
334 | { | |
335 | struct rfkill *rfkill = to_rfkill(dev); | |
cf4328cd | 336 | |
99c632e5 | 337 | return sprintf(buf, "%s\n", rfkill_get_type_str(rfkill->type)); |
cf4328cd ID |
338 | } |
339 | ||
340 | static ssize_t rfkill_state_show(struct device *dev, | |
341 | struct device_attribute *attr, | |
342 | char *buf) | |
343 | { | |
344 | struct rfkill *rfkill = to_rfkill(dev); | |
345 | ||
801e49af | 346 | update_rfkill_state(rfkill); |
cf4328cd ID |
347 | return sprintf(buf, "%d\n", rfkill->state); |
348 | } | |
349 | ||
350 | static ssize_t rfkill_state_store(struct device *dev, | |
351 | struct device_attribute *attr, | |
352 | const char *buf, size_t count) | |
353 | { | |
354 | struct rfkill *rfkill = to_rfkill(dev); | |
355 | unsigned int state = simple_strtoul(buf, NULL, 0); | |
356 | int error; | |
357 | ||
358 | if (!capable(CAP_NET_ADMIN)) | |
359 | return -EPERM; | |
360 | ||
5005657c HMH |
361 | /* RFKILL_STATE_HARD_BLOCKED is illegal here... */ |
362 | if (state != RFKILL_STATE_UNBLOCKED && | |
363 | state != RFKILL_STATE_SOFT_BLOCKED) | |
364 | return -EINVAL; | |
365 | ||
7f4c5341 MB |
366 | if (mutex_lock_interruptible(&rfkill->mutex)) |
367 | return -ERESTARTSYS; | |
5005657c | 368 | error = rfkill_toggle_radio(rfkill, state, 0); |
7f4c5341 | 369 | mutex_unlock(&rfkill->mutex); |
cf4328cd | 370 | |
7f4c5341 | 371 | return error ? error : count; |
cf4328cd ID |
372 | } |
373 | ||
374 | static ssize_t rfkill_claim_show(struct device *dev, | |
375 | struct device_attribute *attr, | |
376 | char *buf) | |
377 | { | |
378 | struct rfkill *rfkill = to_rfkill(dev); | |
379 | ||
988b02f1 | 380 | return sprintf(buf, "%d\n", rfkill->user_claim); |
cf4328cd ID |
381 | } |
382 | ||
383 | static ssize_t rfkill_claim_store(struct device *dev, | |
384 | struct device_attribute *attr, | |
385 | const char *buf, size_t count) | |
386 | { | |
387 | struct rfkill *rfkill = to_rfkill(dev); | |
388 | bool claim = !!simple_strtoul(buf, NULL, 0); | |
389 | int error; | |
390 | ||
391 | if (!capable(CAP_NET_ADMIN)) | |
392 | return -EPERM; | |
393 | ||
064af111 HMH |
394 | if (rfkill->user_claim_unsupported) |
395 | return -EOPNOTSUPP; | |
396 | ||
cf4328cd ID |
397 | /* |
398 | * Take the global lock to make sure the kernel is not in | |
399 | * the middle of rfkill_switch_all | |
400 | */ | |
401 | error = mutex_lock_interruptible(&rfkill_mutex); | |
402 | if (error) | |
403 | return error; | |
404 | ||
405 | if (rfkill->user_claim != claim) { | |
064af111 HMH |
406 | if (!claim) { |
407 | mutex_lock(&rfkill->mutex); | |
cf4328cd | 408 | rfkill_toggle_radio(rfkill, |
526324b6 HMH |
409 | rfkill_states[rfkill->type], |
410 | 0); | |
064af111 HMH |
411 | mutex_unlock(&rfkill->mutex); |
412 | } | |
cf4328cd ID |
413 | rfkill->user_claim = claim; |
414 | } | |
415 | ||
416 | mutex_unlock(&rfkill_mutex); | |
417 | ||
20405c08 | 418 | return error ? error : count; |
cf4328cd ID |
419 | } |
420 | ||
421 | static struct device_attribute rfkill_dev_attrs[] = { | |
422 | __ATTR(name, S_IRUGO, rfkill_name_show, NULL), | |
423 | __ATTR(type, S_IRUGO, rfkill_type_show, NULL), | |
c81de6ad | 424 | __ATTR(state, S_IRUGO|S_IWUSR, rfkill_state_show, rfkill_state_store), |
cf4328cd ID |
425 | __ATTR(claim, S_IRUGO|S_IWUSR, rfkill_claim_show, rfkill_claim_store), |
426 | __ATTR_NULL | |
427 | }; | |
428 | ||
429 | static void rfkill_release(struct device *dev) | |
430 | { | |
431 | struct rfkill *rfkill = to_rfkill(dev); | |
432 | ||
433 | kfree(rfkill); | |
434 | module_put(THIS_MODULE); | |
435 | } | |
436 | ||
437 | #ifdef CONFIG_PM | |
438 | static int rfkill_suspend(struct device *dev, pm_message_t state) | |
439 | { | |
440 | struct rfkill *rfkill = to_rfkill(dev); | |
441 | ||
442 | if (dev->power.power_state.event != state.event) { | |
3a2d5b70 | 443 | if (state.event & PM_EVENT_SLEEP) { |
526324b6 HMH |
444 | /* Stop transmitter, keep state, no notifies */ |
445 | update_rfkill_state(rfkill); | |
cf4328cd | 446 | |
526324b6 | 447 | mutex_lock(&rfkill->mutex); |
5005657c HMH |
448 | rfkill->toggle_radio(rfkill->data, |
449 | RFKILL_STATE_SOFT_BLOCKED); | |
cf4328cd ID |
450 | mutex_unlock(&rfkill->mutex); |
451 | } | |
452 | ||
453 | dev->power.power_state = state; | |
454 | } | |
455 | ||
456 | return 0; | |
457 | } | |
458 | ||
459 | static int rfkill_resume(struct device *dev) | |
460 | { | |
461 | struct rfkill *rfkill = to_rfkill(dev); | |
462 | ||
463 | if (dev->power.power_state.event != PM_EVENT_ON) { | |
464 | mutex_lock(&rfkill->mutex); | |
465 | ||
e10e0dfe HMH |
466 | dev->power.power_state.event = PM_EVENT_ON; |
467 | ||
526324b6 HMH |
468 | /* restore radio state AND notify everybody */ |
469 | rfkill_toggle_radio(rfkill, rfkill->state, 1); | |
cf4328cd ID |
470 | |
471 | mutex_unlock(&rfkill->mutex); | |
472 | } | |
473 | ||
cf4328cd ID |
474 | return 0; |
475 | } | |
476 | #else | |
477 | #define rfkill_suspend NULL | |
478 | #define rfkill_resume NULL | |
479 | #endif | |
480 | ||
ffb67c34 HMH |
481 | static int rfkill_blocking_uevent_notifier(struct notifier_block *nb, |
482 | unsigned long eventid, | |
483 | void *data) | |
484 | { | |
485 | struct rfkill *rfkill = (struct rfkill *)data; | |
486 | ||
487 | switch (eventid) { | |
488 | case RFKILL_STATE_CHANGED: | |
489 | kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE); | |
490 | break; | |
491 | default: | |
492 | break; | |
493 | } | |
494 | ||
495 | return NOTIFY_DONE; | |
496 | } | |
497 | ||
498 | static struct notifier_block rfkill_blocking_uevent_nb = { | |
499 | .notifier_call = rfkill_blocking_uevent_notifier, | |
500 | .priority = 0, | |
501 | }; | |
502 | ||
503 | static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env) | |
504 | { | |
505 | struct rfkill *rfkill = to_rfkill(dev); | |
506 | int error; | |
507 | ||
508 | error = add_uevent_var(env, "RFKILL_NAME=%s", rfkill->name); | |
509 | if (error) | |
510 | return error; | |
511 | error = add_uevent_var(env, "RFKILL_TYPE=%s", | |
512 | rfkill_get_type_str(rfkill->type)); | |
513 | if (error) | |
514 | return error; | |
515 | error = add_uevent_var(env, "RFKILL_STATE=%d", rfkill->state); | |
516 | return error; | |
517 | } | |
518 | ||
cf4328cd ID |
519 | static struct class rfkill_class = { |
520 | .name = "rfkill", | |
521 | .dev_release = rfkill_release, | |
522 | .dev_attrs = rfkill_dev_attrs, | |
523 | .suspend = rfkill_suspend, | |
524 | .resume = rfkill_resume, | |
ffb67c34 | 525 | .dev_uevent = rfkill_dev_uevent, |
cf4328cd ID |
526 | }; |
527 | ||
528 | static int rfkill_add_switch(struct rfkill *rfkill) | |
529 | { | |
7319f1e6 | 530 | mutex_lock(&rfkill_mutex); |
cf4328cd | 531 | |
fd4484af HMH |
532 | rfkill_toggle_radio(rfkill, rfkill_states[rfkill->type], 0); |
533 | ||
534 | list_add_tail(&rfkill->node, &rfkill_list); | |
cf4328cd | 535 | |
cf4328cd | 536 | mutex_unlock(&rfkill_mutex); |
7319f1e6 | 537 | |
fd4484af | 538 | return 0; |
cf4328cd ID |
539 | } |
540 | ||
541 | static void rfkill_remove_switch(struct rfkill *rfkill) | |
542 | { | |
543 | mutex_lock(&rfkill_mutex); | |
544 | list_del_init(&rfkill->node); | |
cf4328cd | 545 | mutex_unlock(&rfkill_mutex); |
064af111 HMH |
546 | |
547 | mutex_lock(&rfkill->mutex); | |
548 | rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); | |
549 | mutex_unlock(&rfkill->mutex); | |
cf4328cd ID |
550 | } |
551 | ||
552 | /** | |
553 | * rfkill_allocate - allocate memory for rfkill structure. | |
554 | * @parent: device that has rf switch on it | |
234a0ca6 | 555 | * @type: type of the switch (RFKILL_TYPE_*) |
cf4328cd ID |
556 | * |
557 | * This function should be called by the network driver when it needs | |
435307a3 HMH |
558 | * rfkill structure. Once the structure is allocated the driver should |
559 | * finish its initialization by setting the name, private data, enable_radio | |
cf4328cd | 560 | * and disable_radio methods and then register it with rfkill_register(). |
435307a3 | 561 | * |
cf4328cd ID |
562 | * NOTE: If registration fails the structure shoudl be freed by calling |
563 | * rfkill_free() otherwise rfkill_unregister() should be used. | |
564 | */ | |
565 | struct rfkill *rfkill_allocate(struct device *parent, enum rfkill_type type) | |
566 | { | |
567 | struct rfkill *rfkill; | |
568 | struct device *dev; | |
569 | ||
570 | rfkill = kzalloc(sizeof(struct rfkill), GFP_KERNEL); | |
d007da1f | 571 | if (!rfkill) |
cf4328cd ID |
572 | return NULL; |
573 | ||
574 | mutex_init(&rfkill->mutex); | |
575 | INIT_LIST_HEAD(&rfkill->node); | |
576 | rfkill->type = type; | |
577 | ||
578 | dev = &rfkill->dev; | |
579 | dev->class = &rfkill_class; | |
580 | dev->parent = parent; | |
581 | device_initialize(dev); | |
582 | ||
583 | __module_get(THIS_MODULE); | |
584 | ||
585 | return rfkill; | |
586 | } | |
587 | EXPORT_SYMBOL(rfkill_allocate); | |
588 | ||
589 | /** | |
590 | * rfkill_free - Mark rfkill structure for deletion | |
591 | * @rfkill: rfkill structure to be destroyed | |
592 | * | |
435307a3 | 593 | * Decrements reference count of the rfkill structure so it is destroyed. |
cf4328cd ID |
594 | * Note that rfkill_free() should _not_ be called after rfkill_unregister(). |
595 | */ | |
596 | void rfkill_free(struct rfkill *rfkill) | |
597 | { | |
598 | if (rfkill) | |
599 | put_device(&rfkill->dev); | |
600 | } | |
601 | EXPORT_SYMBOL(rfkill_free); | |
602 | ||
135900c1 MB |
603 | static void rfkill_led_trigger_register(struct rfkill *rfkill) |
604 | { | |
605 | #ifdef CONFIG_RFKILL_LEDS | |
606 | int error; | |
607 | ||
7c4f4578 DES |
608 | if (!rfkill->led_trigger.name) |
609 | rfkill->led_trigger.name = rfkill->dev.bus_id; | |
96185664 DES |
610 | if (!rfkill->led_trigger.activate) |
611 | rfkill->led_trigger.activate = rfkill_led_trigger_activate; | |
135900c1 MB |
612 | error = led_trigger_register(&rfkill->led_trigger); |
613 | if (error) | |
614 | rfkill->led_trigger.name = NULL; | |
615 | #endif /* CONFIG_RFKILL_LEDS */ | |
616 | } | |
617 | ||
618 | static void rfkill_led_trigger_unregister(struct rfkill *rfkill) | |
619 | { | |
620 | #ifdef CONFIG_RFKILL_LEDS | |
37f55e9d | 621 | if (rfkill->led_trigger.name) { |
135900c1 | 622 | led_trigger_unregister(&rfkill->led_trigger); |
37f55e9d HMH |
623 | rfkill->led_trigger.name = NULL; |
624 | } | |
135900c1 MB |
625 | #endif |
626 | } | |
627 | ||
cf4328cd ID |
628 | /** |
629 | * rfkill_register - Register a rfkill structure. | |
630 | * @rfkill: rfkill structure to be registered | |
631 | * | |
632 | * This function should be called by the network driver when the rfkill | |
633 | * structure needs to be registered. Immediately from registration the | |
634 | * switch driver should be able to service calls to toggle_radio. | |
635 | */ | |
636 | int rfkill_register(struct rfkill *rfkill) | |
637 | { | |
638 | static atomic_t rfkill_no = ATOMIC_INIT(0); | |
639 | struct device *dev = &rfkill->dev; | |
640 | int error; | |
641 | ||
642 | if (!rfkill->toggle_radio) | |
643 | return -EINVAL; | |
7319f1e6 MB |
644 | if (rfkill->type >= RFKILL_TYPE_MAX) |
645 | return -EINVAL; | |
cf4328cd | 646 | |
8a8f1c04 MB |
647 | snprintf(dev->bus_id, sizeof(dev->bus_id), |
648 | "rfkill%ld", (long)atomic_inc_return(&rfkill_no) - 1); | |
649 | ||
650 | rfkill_led_trigger_register(rfkill); | |
651 | ||
cf4328cd | 652 | error = rfkill_add_switch(rfkill); |
632041f3 EP |
653 | if (error) { |
654 | rfkill_led_trigger_unregister(rfkill); | |
cf4328cd | 655 | return error; |
632041f3 | 656 | } |
cf4328cd | 657 | |
cf4328cd ID |
658 | error = device_add(dev); |
659 | if (error) { | |
660 | rfkill_remove_switch(rfkill); | |
37f55e9d | 661 | rfkill_led_trigger_unregister(rfkill); |
cf4328cd ID |
662 | return error; |
663 | } | |
664 | ||
665 | return 0; | |
666 | } | |
667 | EXPORT_SYMBOL(rfkill_register); | |
668 | ||
669 | /** | |
c8fcd905 | 670 | * rfkill_unregister - Unregister a rfkill structure. |
cf4328cd ID |
671 | * @rfkill: rfkill structure to be unregistered |
672 | * | |
673 | * This function should be called by the network driver during device | |
674 | * teardown to destroy rfkill structure. Note that rfkill_free() should | |
675 | * _not_ be called after rfkill_unregister(). | |
676 | */ | |
677 | void rfkill_unregister(struct rfkill *rfkill) | |
678 | { | |
679 | device_del(&rfkill->dev); | |
680 | rfkill_remove_switch(rfkill); | |
8a8f1c04 | 681 | rfkill_led_trigger_unregister(rfkill); |
cf4328cd ID |
682 | put_device(&rfkill->dev); |
683 | } | |
684 | EXPORT_SYMBOL(rfkill_unregister); | |
685 | ||
686 | /* | |
687 | * Rfkill module initialization/deinitialization. | |
688 | */ | |
689 | static int __init rfkill_init(void) | |
690 | { | |
691 | int error; | |
692 | int i; | |
693 | ||
5005657c HMH |
694 | /* RFKILL_STATE_HARD_BLOCKED is illegal here... */ |
695 | if (rfkill_default_state != RFKILL_STATE_SOFT_BLOCKED && | |
696 | rfkill_default_state != RFKILL_STATE_UNBLOCKED) | |
e954b0b8 HMH |
697 | return -EINVAL; |
698 | ||
cf4328cd | 699 | for (i = 0; i < ARRAY_SIZE(rfkill_states); i++) |
e954b0b8 | 700 | rfkill_states[i] = rfkill_default_state; |
cf4328cd ID |
701 | |
702 | error = class_register(&rfkill_class); | |
703 | if (error) { | |
704 | printk(KERN_ERR "rfkill: unable to register rfkill class\n"); | |
705 | return error; | |
706 | } | |
707 | ||
ffb67c34 HMH |
708 | register_rfkill_notifier(&rfkill_blocking_uevent_nb); |
709 | ||
cf4328cd ID |
710 | return 0; |
711 | } | |
712 | ||
713 | static void __exit rfkill_exit(void) | |
714 | { | |
ffb67c34 | 715 | unregister_rfkill_notifier(&rfkill_blocking_uevent_nb); |
cf4328cd ID |
716 | class_unregister(&rfkill_class); |
717 | } | |
718 | ||
2bf236d5 | 719 | subsys_initcall(rfkill_init); |
cf4328cd | 720 | module_exit(rfkill_exit); |