diff options
Diffstat (limited to 'net/rfkill')
-rw-r--r-- | net/rfkill/rfkill-input.c | 29 | ||||
-rw-r--r-- | net/rfkill/rfkill.c | 75 |
2 files changed, 76 insertions, 28 deletions
diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c index 5d4c8b2446f7..8aa822730145 100644 --- a/net/rfkill/rfkill-input.c +++ b/net/rfkill/rfkill-input.c | |||
@@ -84,7 +84,8 @@ static void rfkill_schedule_toggle(struct rfkill_task *task) | |||
84 | spin_lock_irqsave(&task->lock, flags); | 84 | spin_lock_irqsave(&task->lock, flags); |
85 | 85 | ||
86 | if (time_after(jiffies, task->last + msecs_to_jiffies(200))) { | 86 | if (time_after(jiffies, task->last + msecs_to_jiffies(200))) { |
87 | task->desired_state = !task->desired_state; | 87 | task->desired_state = |
88 | rfkill_state_complement(task->desired_state); | ||
88 | task->last = jiffies; | 89 | task->last = jiffies; |
89 | schedule_work(&task->work); | 90 | schedule_work(&task->work); |
90 | } | 91 | } |
@@ -92,14 +93,14 @@ static void rfkill_schedule_toggle(struct rfkill_task *task) | |||
92 | spin_unlock_irqrestore(&task->lock, flags); | 93 | spin_unlock_irqrestore(&task->lock, flags); |
93 | } | 94 | } |
94 | 95 | ||
95 | #define DEFINE_RFKILL_TASK(n, t) \ | 96 | #define DEFINE_RFKILL_TASK(n, t) \ |
96 | struct rfkill_task n = { \ | 97 | struct rfkill_task n = { \ |
97 | .work = __WORK_INITIALIZER(n.work, \ | 98 | .work = __WORK_INITIALIZER(n.work, \ |
98 | rfkill_task_handler), \ | 99 | rfkill_task_handler), \ |
99 | .type = t, \ | 100 | .type = t, \ |
100 | .mutex = __MUTEX_INITIALIZER(n.mutex), \ | 101 | .mutex = __MUTEX_INITIALIZER(n.mutex), \ |
101 | .lock = __SPIN_LOCK_UNLOCKED(n.lock), \ | 102 | .lock = __SPIN_LOCK_UNLOCKED(n.lock), \ |
102 | .desired_state = RFKILL_STATE_ON, \ | 103 | .desired_state = RFKILL_STATE_UNBLOCKED, \ |
103 | } | 104 | } |
104 | 105 | ||
105 | static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN); | 106 | static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN); |
@@ -135,15 +136,15 @@ static void rfkill_event(struct input_handle *handle, unsigned int type, | |||
135 | /* handle EPO (emergency power off) through shortcut */ | 136 | /* handle EPO (emergency power off) through shortcut */ |
136 | if (data) { | 137 | if (data) { |
137 | rfkill_schedule_set(&rfkill_wwan, | 138 | rfkill_schedule_set(&rfkill_wwan, |
138 | RFKILL_STATE_ON); | 139 | RFKILL_STATE_UNBLOCKED); |
139 | rfkill_schedule_set(&rfkill_wimax, | 140 | rfkill_schedule_set(&rfkill_wimax, |
140 | RFKILL_STATE_ON); | 141 | RFKILL_STATE_UNBLOCKED); |
141 | rfkill_schedule_set(&rfkill_uwb, | 142 | rfkill_schedule_set(&rfkill_uwb, |
142 | RFKILL_STATE_ON); | 143 | RFKILL_STATE_UNBLOCKED); |
143 | rfkill_schedule_set(&rfkill_bt, | 144 | rfkill_schedule_set(&rfkill_bt, |
144 | RFKILL_STATE_ON); | 145 | RFKILL_STATE_UNBLOCKED); |
145 | rfkill_schedule_set(&rfkill_wlan, | 146 | rfkill_schedule_set(&rfkill_wlan, |
146 | RFKILL_STATE_ON); | 147 | RFKILL_STATE_UNBLOCKED); |
147 | } else | 148 | } else |
148 | rfkill_schedule_epo(); | 149 | rfkill_schedule_epo(); |
149 | break; | 150 | break; |
diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 7d07175c407f..ce0e23148cdd 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c | |||
@@ -39,7 +39,7 @@ MODULE_LICENSE("GPL"); | |||
39 | static LIST_HEAD(rfkill_list); /* list of registered rf switches */ | 39 | static LIST_HEAD(rfkill_list); /* list of registered rf switches */ |
40 | static DEFINE_MUTEX(rfkill_mutex); | 40 | static DEFINE_MUTEX(rfkill_mutex); |
41 | 41 | ||
42 | static unsigned int rfkill_default_state = RFKILL_STATE_ON; | 42 | static unsigned int rfkill_default_state = RFKILL_STATE_UNBLOCKED; |
43 | module_param_named(default_state, rfkill_default_state, uint, 0444); | 43 | module_param_named(default_state, rfkill_default_state, uint, 0444); |
44 | MODULE_PARM_DESC(default_state, | 44 | MODULE_PARM_DESC(default_state, |
45 | "Default initial state for all radio types, 0 = radio off"); | 45 | "Default initial state for all radio types, 0 = radio off"); |
@@ -98,7 +98,7 @@ static void rfkill_led_trigger(struct rfkill *rfkill, | |||
98 | 98 | ||
99 | if (!led->name) | 99 | if (!led->name) |
100 | return; | 100 | return; |
101 | if (state == RFKILL_STATE_OFF) | 101 | if (state != RFKILL_STATE_UNBLOCKED) |
102 | led_trigger_event(led, LED_OFF); | 102 | led_trigger_event(led, LED_OFF); |
103 | else | 103 | else |
104 | led_trigger_event(led, LED_FULL); | 104 | led_trigger_event(led, LED_FULL); |
@@ -128,6 +128,28 @@ static void update_rfkill_state(struct rfkill *rfkill) | |||
128 | } | 128 | } |
129 | } | 129 | } |
130 | 130 | ||
131 | /** | ||
132 | * rfkill_toggle_radio - wrapper for toggle_radio hook | ||
133 | * calls toggle_radio taking into account a lot of "small" | ||
134 | * details. | ||
135 | * @rfkill: the rfkill struct to use | ||
136 | * @force: calls toggle_radio even if cache says it is not needed, | ||
137 | * and also makes sure notifications of the state will be | ||
138 | * sent even if it didn't change | ||
139 | * @state: the new state to call toggle_radio() with | ||
140 | * | ||
141 | * This wrappen protects and enforces the API for toggle_radio | ||
142 | * calls. Note that @force cannot override a (possibly cached) | ||
143 | * state of RFKILL_STATE_HARD_BLOCKED. Any device making use of | ||
144 | * RFKILL_STATE_HARD_BLOCKED implements either get_state() or | ||
145 | * rfkill_force_state(), so the cache either is bypassed or valid. | ||
146 | * | ||
147 | * Note that we do call toggle_radio for RFKILL_STATE_SOFT_BLOCKED | ||
148 | * even if the radio is in RFKILL_STATE_HARD_BLOCKED state, so as to | ||
149 | * give the driver a hint that it should double-BLOCK the transmitter. | ||
150 | * | ||
151 | * Caller must have aquired rfkill_mutex. | ||
152 | */ | ||
131 | static int rfkill_toggle_radio(struct rfkill *rfkill, | 153 | static int rfkill_toggle_radio(struct rfkill *rfkill, |
132 | enum rfkill_state state, | 154 | enum rfkill_state state, |
133 | int force) | 155 | int force) |
@@ -141,9 +163,28 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, | |||
141 | !rfkill->get_state(rfkill->data, &newstate)) | 163 | !rfkill->get_state(rfkill->data, &newstate)) |
142 | rfkill->state = newstate; | 164 | rfkill->state = newstate; |
143 | 165 | ||
166 | switch (state) { | ||
167 | case RFKILL_STATE_HARD_BLOCKED: | ||
168 | /* typically happens when refreshing hardware state, | ||
169 | * such as on resume */ | ||
170 | state = RFKILL_STATE_SOFT_BLOCKED; | ||
171 | break; | ||
172 | case RFKILL_STATE_UNBLOCKED: | ||
173 | /* force can't override this, only rfkill_force_state() can */ | ||
174 | if (rfkill->state == RFKILL_STATE_HARD_BLOCKED) | ||
175 | return -EPERM; | ||
176 | break; | ||
177 | case RFKILL_STATE_SOFT_BLOCKED: | ||
178 | /* nothing to do, we want to give drivers the hint to double | ||
179 | * BLOCK even a transmitter that is already in state | ||
180 | * RFKILL_STATE_HARD_BLOCKED */ | ||
181 | break; | ||
182 | } | ||
183 | |||
144 | if (force || state != rfkill->state) { | 184 | if (force || state != rfkill->state) { |
145 | retval = rfkill->toggle_radio(rfkill->data, state); | 185 | retval = rfkill->toggle_radio(rfkill->data, state); |
146 | if (!retval) | 186 | /* never allow a HARD->SOFT downgrade! */ |
187 | if (!retval && rfkill->state != RFKILL_STATE_HARD_BLOCKED) | ||
147 | rfkill->state = state; | 188 | rfkill->state = state; |
148 | } | 189 | } |
149 | 190 | ||
@@ -184,7 +225,7 @@ EXPORT_SYMBOL(rfkill_switch_all); | |||
184 | /** | 225 | /** |
185 | * rfkill_epo - emergency power off all transmitters | 226 | * rfkill_epo - emergency power off all transmitters |
186 | * | 227 | * |
187 | * This kicks all rfkill devices to RFKILL_STATE_OFF, ignoring | 228 | * This kicks all rfkill devices to RFKILL_STATE_SOFT_BLOCKED, ignoring |
188 | * everything in its path but rfkill_mutex. | 229 | * everything in its path but rfkill_mutex. |
189 | */ | 230 | */ |
190 | void rfkill_epo(void) | 231 | void rfkill_epo(void) |
@@ -193,7 +234,7 @@ void rfkill_epo(void) | |||
193 | 234 | ||
194 | mutex_lock(&rfkill_mutex); | 235 | mutex_lock(&rfkill_mutex); |
195 | list_for_each_entry(rfkill, &rfkill_list, node) { | 236 | list_for_each_entry(rfkill, &rfkill_list, node) { |
196 | rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF, 1); | 237 | rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); |
197 | } | 238 | } |
198 | mutex_unlock(&rfkill_mutex); | 239 | mutex_unlock(&rfkill_mutex); |
199 | } | 240 | } |
@@ -215,8 +256,9 @@ int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) | |||
215 | { | 256 | { |
216 | enum rfkill_state oldstate; | 257 | enum rfkill_state oldstate; |
217 | 258 | ||
218 | if (state != RFKILL_STATE_OFF && | 259 | if (state != RFKILL_STATE_SOFT_BLOCKED && |
219 | state != RFKILL_STATE_ON) | 260 | state != RFKILL_STATE_UNBLOCKED && |
261 | state != RFKILL_STATE_HARD_BLOCKED) | ||
220 | return -EINVAL; | 262 | return -EINVAL; |
221 | 263 | ||
222 | mutex_lock(&rfkill->mutex); | 264 | mutex_lock(&rfkill->mutex); |
@@ -290,11 +332,14 @@ static ssize_t rfkill_state_store(struct device *dev, | |||
290 | if (!capable(CAP_NET_ADMIN)) | 332 | if (!capable(CAP_NET_ADMIN)) |
291 | return -EPERM; | 333 | return -EPERM; |
292 | 334 | ||
335 | /* RFKILL_STATE_HARD_BLOCKED is illegal here... */ | ||
336 | if (state != RFKILL_STATE_UNBLOCKED && | ||
337 | state != RFKILL_STATE_SOFT_BLOCKED) | ||
338 | return -EINVAL; | ||
339 | |||
293 | if (mutex_lock_interruptible(&rfkill->mutex)) | 340 | if (mutex_lock_interruptible(&rfkill->mutex)) |
294 | return -ERESTARTSYS; | 341 | return -ERESTARTSYS; |
295 | error = rfkill_toggle_radio(rfkill, | 342 | error = rfkill_toggle_radio(rfkill, state, 0); |
296 | state ? RFKILL_STATE_ON : RFKILL_STATE_OFF, | ||
297 | 0); | ||
298 | mutex_unlock(&rfkill->mutex); | 343 | mutex_unlock(&rfkill->mutex); |
299 | 344 | ||
300 | return error ? error : count; | 345 | return error ? error : count; |
@@ -373,7 +418,8 @@ static int rfkill_suspend(struct device *dev, pm_message_t state) | |||
373 | update_rfkill_state(rfkill); | 418 | update_rfkill_state(rfkill); |
374 | 419 | ||
375 | mutex_lock(&rfkill->mutex); | 420 | mutex_lock(&rfkill->mutex); |
376 | rfkill->toggle_radio(rfkill->data, RFKILL_STATE_OFF); | 421 | rfkill->toggle_radio(rfkill->data, |
422 | RFKILL_STATE_SOFT_BLOCKED); | ||
377 | mutex_unlock(&rfkill->mutex); | 423 | mutex_unlock(&rfkill->mutex); |
378 | } | 424 | } |
379 | 425 | ||
@@ -470,7 +516,7 @@ static void rfkill_remove_switch(struct rfkill *rfkill) | |||
470 | { | 516 | { |
471 | mutex_lock(&rfkill_mutex); | 517 | mutex_lock(&rfkill_mutex); |
472 | list_del_init(&rfkill->node); | 518 | list_del_init(&rfkill->node); |
473 | rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF, 1); | 519 | rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); |
474 | mutex_unlock(&rfkill_mutex); | 520 | mutex_unlock(&rfkill_mutex); |
475 | } | 521 | } |
476 | 522 | ||
@@ -610,8 +656,9 @@ static int __init rfkill_init(void) | |||
610 | int error; | 656 | int error; |
611 | int i; | 657 | int i; |
612 | 658 | ||
613 | if (rfkill_default_state != RFKILL_STATE_OFF && | 659 | /* RFKILL_STATE_HARD_BLOCKED is illegal here... */ |
614 | rfkill_default_state != RFKILL_STATE_ON) | 660 | if (rfkill_default_state != RFKILL_STATE_SOFT_BLOCKED && |
661 | rfkill_default_state != RFKILL_STATE_UNBLOCKED) | ||
615 | return -EINVAL; | 662 | return -EINVAL; |
616 | 663 | ||
617 | for (i = 0; i < ARRAY_SIZE(rfkill_states); i++) | 664 | for (i = 0; i < ARRAY_SIZE(rfkill_states); i++) |