diff options
Diffstat (limited to 'net/rfkill/rfkill.c')
-rw-r--r-- | net/rfkill/rfkill.c | 183 |
1 files changed, 94 insertions, 89 deletions
diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 25ba3bd57e66..3c94f76d5525 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c | |||
@@ -51,51 +51,7 @@ struct rfkill_gsw_state { | |||
51 | 51 | ||
52 | static struct rfkill_gsw_state rfkill_global_states[RFKILL_TYPE_MAX]; | 52 | static struct rfkill_gsw_state rfkill_global_states[RFKILL_TYPE_MAX]; |
53 | static unsigned long rfkill_states_lockdflt[BITS_TO_LONGS(RFKILL_TYPE_MAX)]; | 53 | static unsigned long rfkill_states_lockdflt[BITS_TO_LONGS(RFKILL_TYPE_MAX)]; |
54 | 54 | static bool rfkill_epo_lock_active; | |
55 | static BLOCKING_NOTIFIER_HEAD(rfkill_notifier_list); | ||
56 | |||
57 | |||
58 | /** | ||
59 | * register_rfkill_notifier - Add notifier to rfkill notifier chain | ||
60 | * @nb: pointer to the new entry to add to the chain | ||
61 | * | ||
62 | * See blocking_notifier_chain_register() for return value and further | ||
63 | * observations. | ||
64 | * | ||
65 | * Adds a notifier to the rfkill notifier chain. The chain will be | ||
66 | * called with a pointer to the relevant rfkill structure as a parameter, | ||
67 | * refer to include/linux/rfkill.h for the possible events. | ||
68 | * | ||
69 | * Notifiers added to this chain are to always return NOTIFY_DONE. This | ||
70 | * chain is a blocking notifier chain: notifiers can sleep. | ||
71 | * | ||
72 | * Calls to this chain may have been done through a workqueue. One must | ||
73 | * assume unordered asynchronous behaviour, there is no way to know if | ||
74 | * actions related to the event that generated the notification have been | ||
75 | * carried out already. | ||
76 | */ | ||
77 | int register_rfkill_notifier(struct notifier_block *nb) | ||
78 | { | ||
79 | BUG_ON(!nb); | ||
80 | return blocking_notifier_chain_register(&rfkill_notifier_list, nb); | ||
81 | } | ||
82 | EXPORT_SYMBOL_GPL(register_rfkill_notifier); | ||
83 | |||
84 | /** | ||
85 | * unregister_rfkill_notifier - remove notifier from rfkill notifier chain | ||
86 | * @nb: pointer to the entry to remove from the chain | ||
87 | * | ||
88 | * See blocking_notifier_chain_unregister() for return value and further | ||
89 | * observations. | ||
90 | * | ||
91 | * Removes a notifier from the rfkill notifier chain. | ||
92 | */ | ||
93 | int unregister_rfkill_notifier(struct notifier_block *nb) | ||
94 | { | ||
95 | BUG_ON(!nb); | ||
96 | return blocking_notifier_chain_unregister(&rfkill_notifier_list, nb); | ||
97 | } | ||
98 | EXPORT_SYMBOL_GPL(unregister_rfkill_notifier); | ||
99 | 55 | ||
100 | 56 | ||
101 | static void rfkill_led_trigger(struct rfkill *rfkill, | 57 | static void rfkill_led_trigger(struct rfkill *rfkill, |
@@ -123,12 +79,9 @@ static void rfkill_led_trigger_activate(struct led_classdev *led) | |||
123 | } | 79 | } |
124 | #endif /* CONFIG_RFKILL_LEDS */ | 80 | #endif /* CONFIG_RFKILL_LEDS */ |
125 | 81 | ||
126 | static void notify_rfkill_state_change(struct rfkill *rfkill) | 82 | static void rfkill_uevent(struct rfkill *rfkill) |
127 | { | 83 | { |
128 | rfkill_led_trigger(rfkill, rfkill->state); | 84 | kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE); |
129 | blocking_notifier_call_chain(&rfkill_notifier_list, | ||
130 | RFKILL_STATE_CHANGED, | ||
131 | rfkill); | ||
132 | } | 85 | } |
133 | 86 | ||
134 | static void update_rfkill_state(struct rfkill *rfkill) | 87 | static void update_rfkill_state(struct rfkill *rfkill) |
@@ -141,7 +94,7 @@ static void update_rfkill_state(struct rfkill *rfkill) | |||
141 | oldstate = rfkill->state; | 94 | oldstate = rfkill->state; |
142 | rfkill->state = newstate; | 95 | rfkill->state = newstate; |
143 | if (oldstate != newstate) | 96 | if (oldstate != newstate) |
144 | notify_rfkill_state_change(rfkill); | 97 | rfkill_uevent(rfkill); |
145 | } | 98 | } |
146 | mutex_unlock(&rfkill->mutex); | 99 | mutex_unlock(&rfkill->mutex); |
147 | } | 100 | } |
@@ -219,7 +172,7 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, | |||
219 | } | 172 | } |
220 | 173 | ||
221 | if (force || rfkill->state != oldstate) | 174 | if (force || rfkill->state != oldstate) |
222 | notify_rfkill_state_change(rfkill); | 175 | rfkill_uevent(rfkill); |
223 | 176 | ||
224 | return retval; | 177 | return retval; |
225 | } | 178 | } |
@@ -264,11 +217,14 @@ static void __rfkill_switch_all(const enum rfkill_type type, | |||
264 | * | 217 | * |
265 | * Acquires rfkill_global_mutex and calls __rfkill_switch_all(@type, @state). | 218 | * Acquires rfkill_global_mutex and calls __rfkill_switch_all(@type, @state). |
266 | * Please refer to __rfkill_switch_all() for details. | 219 | * Please refer to __rfkill_switch_all() for details. |
220 | * | ||
221 | * Does nothing if the EPO lock is active. | ||
267 | */ | 222 | */ |
268 | void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) | 223 | void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) |
269 | { | 224 | { |
270 | mutex_lock(&rfkill_global_mutex); | 225 | mutex_lock(&rfkill_global_mutex); |
271 | __rfkill_switch_all(type, state); | 226 | if (!rfkill_epo_lock_active) |
227 | __rfkill_switch_all(type, state); | ||
272 | mutex_unlock(&rfkill_global_mutex); | 228 | mutex_unlock(&rfkill_global_mutex); |
273 | } | 229 | } |
274 | EXPORT_SYMBOL(rfkill_switch_all); | 230 | EXPORT_SYMBOL(rfkill_switch_all); |
@@ -289,6 +245,7 @@ void rfkill_epo(void) | |||
289 | 245 | ||
290 | mutex_lock(&rfkill_global_mutex); | 246 | mutex_lock(&rfkill_global_mutex); |
291 | 247 | ||
248 | rfkill_epo_lock_active = true; | ||
292 | list_for_each_entry(rfkill, &rfkill_list, node) { | 249 | list_for_each_entry(rfkill, &rfkill_list, node) { |
293 | mutex_lock(&rfkill->mutex); | 250 | mutex_lock(&rfkill->mutex); |
294 | rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); | 251 | rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); |
@@ -317,6 +274,7 @@ void rfkill_restore_states(void) | |||
317 | 274 | ||
318 | mutex_lock(&rfkill_global_mutex); | 275 | mutex_lock(&rfkill_global_mutex); |
319 | 276 | ||
277 | rfkill_epo_lock_active = false; | ||
320 | for (i = 0; i < RFKILL_TYPE_MAX; i++) | 278 | for (i = 0; i < RFKILL_TYPE_MAX; i++) |
321 | __rfkill_switch_all(i, rfkill_global_states[i].default_state); | 279 | __rfkill_switch_all(i, rfkill_global_states[i].default_state); |
322 | mutex_unlock(&rfkill_global_mutex); | 280 | mutex_unlock(&rfkill_global_mutex); |
@@ -324,6 +282,48 @@ void rfkill_restore_states(void) | |||
324 | EXPORT_SYMBOL_GPL(rfkill_restore_states); | 282 | EXPORT_SYMBOL_GPL(rfkill_restore_states); |
325 | 283 | ||
326 | /** | 284 | /** |
285 | * rfkill_remove_epo_lock - unlock state changes | ||
286 | * | ||
287 | * Used by rfkill-input manually unlock state changes, when | ||
288 | * the EPO switch is deactivated. | ||
289 | */ | ||
290 | void rfkill_remove_epo_lock(void) | ||
291 | { | ||
292 | mutex_lock(&rfkill_global_mutex); | ||
293 | rfkill_epo_lock_active = false; | ||
294 | mutex_unlock(&rfkill_global_mutex); | ||
295 | } | ||
296 | EXPORT_SYMBOL_GPL(rfkill_remove_epo_lock); | ||
297 | |||
298 | /** | ||
299 | * rfkill_is_epo_lock_active - returns true EPO is active | ||
300 | * | ||
301 | * Returns 0 (false) if there is NOT an active EPO contidion, | ||
302 | * and 1 (true) if there is an active EPO contition, which | ||
303 | * locks all radios in one of the BLOCKED states. | ||
304 | * | ||
305 | * Can be called in atomic context. | ||
306 | */ | ||
307 | bool rfkill_is_epo_lock_active(void) | ||
308 | { | ||
309 | return rfkill_epo_lock_active; | ||
310 | } | ||
311 | EXPORT_SYMBOL_GPL(rfkill_is_epo_lock_active); | ||
312 | |||
313 | /** | ||
314 | * rfkill_get_global_state - returns global state for a type | ||
315 | * @type: the type to get the global state of | ||
316 | * | ||
317 | * Returns the current global state for a given wireless | ||
318 | * device type. | ||
319 | */ | ||
320 | enum rfkill_state rfkill_get_global_state(const enum rfkill_type type) | ||
321 | { | ||
322 | return rfkill_global_states[type].current_state; | ||
323 | } | ||
324 | EXPORT_SYMBOL_GPL(rfkill_get_global_state); | ||
325 | |||
326 | /** | ||
327 | * rfkill_force_state - Force the internal rfkill radio state | 327 | * rfkill_force_state - Force the internal rfkill radio state |
328 | * @rfkill: pointer to the rfkill class to modify. | 328 | * @rfkill: pointer to the rfkill class to modify. |
329 | * @state: the current radio state the class should be forced to. | 329 | * @state: the current radio state the class should be forced to. |
@@ -357,7 +357,7 @@ int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) | |||
357 | rfkill->state = state; | 357 | rfkill->state = state; |
358 | 358 | ||
359 | if (state != oldstate) | 359 | if (state != oldstate) |
360 | notify_rfkill_state_change(rfkill); | 360 | rfkill_uevent(rfkill); |
361 | 361 | ||
362 | mutex_unlock(&rfkill->mutex); | 362 | mutex_unlock(&rfkill->mutex); |
363 | 363 | ||
@@ -431,9 +431,15 @@ static ssize_t rfkill_state_store(struct device *dev, | |||
431 | state != RFKILL_STATE_SOFT_BLOCKED) | 431 | state != RFKILL_STATE_SOFT_BLOCKED) |
432 | return -EINVAL; | 432 | return -EINVAL; |
433 | 433 | ||
434 | if (mutex_lock_interruptible(&rfkill->mutex)) | 434 | error = mutex_lock_killable(&rfkill->mutex); |
435 | return -ERESTARTSYS; | 435 | if (error) |
436 | error = rfkill_toggle_radio(rfkill, state, 0); | 436 | return error; |
437 | |||
438 | if (!rfkill_epo_lock_active) | ||
439 | error = rfkill_toggle_radio(rfkill, state, 0); | ||
440 | else | ||
441 | error = -EPERM; | ||
442 | |||
437 | mutex_unlock(&rfkill->mutex); | 443 | mutex_unlock(&rfkill->mutex); |
438 | 444 | ||
439 | return error ? error : count; | 445 | return error ? error : count; |
@@ -472,12 +478,12 @@ static ssize_t rfkill_claim_store(struct device *dev, | |||
472 | * Take the global lock to make sure the kernel is not in | 478 | * Take the global lock to make sure the kernel is not in |
473 | * the middle of rfkill_switch_all | 479 | * the middle of rfkill_switch_all |
474 | */ | 480 | */ |
475 | error = mutex_lock_interruptible(&rfkill_global_mutex); | 481 | error = mutex_lock_killable(&rfkill_global_mutex); |
476 | if (error) | 482 | if (error) |
477 | return error; | 483 | return error; |
478 | 484 | ||
479 | if (rfkill->user_claim != claim) { | 485 | if (rfkill->user_claim != claim) { |
480 | if (!claim) { | 486 | if (!claim && !rfkill_epo_lock_active) { |
481 | mutex_lock(&rfkill->mutex); | 487 | mutex_lock(&rfkill->mutex); |
482 | rfkill_toggle_radio(rfkill, | 488 | rfkill_toggle_radio(rfkill, |
483 | rfkill_global_states[rfkill->type].current_state, | 489 | rfkill_global_states[rfkill->type].current_state, |
@@ -511,24 +517,48 @@ static void rfkill_release(struct device *dev) | |||
511 | #ifdef CONFIG_PM | 517 | #ifdef CONFIG_PM |
512 | static int rfkill_suspend(struct device *dev, pm_message_t state) | 518 | static int rfkill_suspend(struct device *dev, pm_message_t state) |
513 | { | 519 | { |
520 | struct rfkill *rfkill = to_rfkill(dev); | ||
521 | |||
514 | /* mark class device as suspended */ | 522 | /* mark class device as suspended */ |
515 | if (dev->power.power_state.event != state.event) | 523 | if (dev->power.power_state.event != state.event) |
516 | dev->power.power_state = state; | 524 | dev->power.power_state = state; |
517 | 525 | ||
526 | /* store state for the resume handler */ | ||
527 | rfkill->state_for_resume = rfkill->state; | ||
528 | |||
518 | return 0; | 529 | return 0; |
519 | } | 530 | } |
520 | 531 | ||
521 | static int rfkill_resume(struct device *dev) | 532 | static int rfkill_resume(struct device *dev) |
522 | { | 533 | { |
523 | struct rfkill *rfkill = to_rfkill(dev); | 534 | struct rfkill *rfkill = to_rfkill(dev); |
535 | enum rfkill_state newstate; | ||
524 | 536 | ||
525 | if (dev->power.power_state.event != PM_EVENT_ON) { | 537 | if (dev->power.power_state.event != PM_EVENT_ON) { |
526 | mutex_lock(&rfkill->mutex); | 538 | mutex_lock(&rfkill->mutex); |
527 | 539 | ||
528 | dev->power.power_state.event = PM_EVENT_ON; | 540 | dev->power.power_state.event = PM_EVENT_ON; |
529 | 541 | ||
530 | /* restore radio state AND notify everybody */ | 542 | /* |
531 | rfkill_toggle_radio(rfkill, rfkill->state, 1); | 543 | * rfkill->state could have been modified before we got |
544 | * called, and won't be updated by rfkill_toggle_radio() | ||
545 | * in force mode. Sync it FIRST. | ||
546 | */ | ||
547 | if (rfkill->get_state && | ||
548 | !rfkill->get_state(rfkill->data, &newstate)) | ||
549 | rfkill->state = newstate; | ||
550 | |||
551 | /* | ||
552 | * If we are under EPO, kick transmitter offline, | ||
553 | * otherwise restore to pre-suspend state. | ||
554 | * | ||
555 | * Issue a notification in any case | ||
556 | */ | ||
557 | rfkill_toggle_radio(rfkill, | ||
558 | rfkill_epo_lock_active ? | ||
559 | RFKILL_STATE_SOFT_BLOCKED : | ||
560 | rfkill->state_for_resume, | ||
561 | 1); | ||
532 | 562 | ||
533 | mutex_unlock(&rfkill->mutex); | 563 | mutex_unlock(&rfkill->mutex); |
534 | } | 564 | } |
@@ -540,28 +570,6 @@ static int rfkill_resume(struct device *dev) | |||
540 | #define rfkill_resume NULL | 570 | #define rfkill_resume NULL |
541 | #endif | 571 | #endif |
542 | 572 | ||
543 | static int rfkill_blocking_uevent_notifier(struct notifier_block *nb, | ||
544 | unsigned long eventid, | ||
545 | void *data) | ||
546 | { | ||
547 | struct rfkill *rfkill = (struct rfkill *)data; | ||
548 | |||
549 | switch (eventid) { | ||
550 | case RFKILL_STATE_CHANGED: | ||
551 | kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE); | ||
552 | break; | ||
553 | default: | ||
554 | break; | ||
555 | } | ||
556 | |||
557 | return NOTIFY_DONE; | ||
558 | } | ||
559 | |||
560 | static struct notifier_block rfkill_blocking_uevent_nb = { | ||
561 | .notifier_call = rfkill_blocking_uevent_notifier, | ||
562 | .priority = 0, | ||
563 | }; | ||
564 | |||
565 | static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env) | 573 | static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env) |
566 | { | 574 | { |
567 | struct rfkill *rfkill = to_rfkill(dev); | 575 | struct rfkill *rfkill = to_rfkill(dev); |
@@ -711,7 +719,7 @@ static void rfkill_led_trigger_register(struct rfkill *rfkill) | |||
711 | int error; | 719 | int error; |
712 | 720 | ||
713 | if (!rfkill->led_trigger.name) | 721 | if (!rfkill->led_trigger.name) |
714 | rfkill->led_trigger.name = rfkill->dev.bus_id; | 722 | rfkill->led_trigger.name = dev_name(&rfkill->dev); |
715 | if (!rfkill->led_trigger.activate) | 723 | if (!rfkill->led_trigger.activate) |
716 | rfkill->led_trigger.activate = rfkill_led_trigger_activate; | 724 | rfkill->led_trigger.activate = rfkill_led_trigger_activate; |
717 | error = led_trigger_register(&rfkill->led_trigger); | 725 | error = led_trigger_register(&rfkill->led_trigger); |
@@ -752,8 +760,7 @@ int __must_check rfkill_register(struct rfkill *rfkill) | |||
752 | "badly initialized rfkill struct\n")) | 760 | "badly initialized rfkill struct\n")) |
753 | return -EINVAL; | 761 | return -EINVAL; |
754 | 762 | ||
755 | snprintf(dev->bus_id, sizeof(dev->bus_id), | 763 | dev_set_name(dev, "rfkill%ld", (long)atomic_inc_return(&rfkill_no) - 1); |
756 | "rfkill%ld", (long)atomic_inc_return(&rfkill_no) - 1); | ||
757 | 764 | ||
758 | rfkill_led_trigger_register(rfkill); | 765 | rfkill_led_trigger_register(rfkill); |
759 | 766 | ||
@@ -833,6 +840,7 @@ int rfkill_set_default(enum rfkill_type type, enum rfkill_state state) | |||
833 | 840 | ||
834 | if (!test_and_set_bit(type, rfkill_states_lockdflt)) { | 841 | if (!test_and_set_bit(type, rfkill_states_lockdflt)) { |
835 | rfkill_global_states[type].default_state = state; | 842 | rfkill_global_states[type].default_state = state; |
843 | rfkill_global_states[type].current_state = state; | ||
836 | error = 0; | 844 | error = 0; |
837 | } else | 845 | } else |
838 | error = -EPERM; | 846 | error = -EPERM; |
@@ -864,14 +872,11 @@ static int __init rfkill_init(void) | |||
864 | return error; | 872 | return error; |
865 | } | 873 | } |
866 | 874 | ||
867 | register_rfkill_notifier(&rfkill_blocking_uevent_nb); | ||
868 | |||
869 | return 0; | 875 | return 0; |
870 | } | 876 | } |
871 | 877 | ||
872 | static void __exit rfkill_exit(void) | 878 | static void __exit rfkill_exit(void) |
873 | { | 879 | { |
874 | unregister_rfkill_notifier(&rfkill_blocking_uevent_nb); | ||
875 | class_unregister(&rfkill_class); | 880 | class_unregister(&rfkill_class); |
876 | } | 881 | } |
877 | 882 | ||