aboutsummaryrefslogtreecommitdiffstats
path: root/net/rfkill/rfkill.c
diff options
context:
space:
mode:
authorHenrique de Moraes Holschuh <hmh@hmh.eng.br>2008-10-09 20:49:33 -0400
committerJohn W. Linville <linville@tuxdriver.com>2008-10-31 19:00:09 -0400
commitd003922dab6a66027344a787e9846ecf35a706a8 (patch)
tree1fa27e13eafd7d2cfceaeeb7c70009e29467ccfa /net/rfkill/rfkill.c
parent68d2413bec7e1d97e906eafb05e78d925a5ca128 (diff)
rfkill: add master_switch_mode and EPO lock to rfkill and rfkill-input
Add of software-based sanity to rfkill and rfkill-input so that it can reproduce what hardware-based EPO switches do, blocking all transmitters and locking down any further attempts to unblock them until the switch is deactivated. rfkill-input is responsible for issuing the EPO control requests, like before. While an rfkill EPO is active, all transmitters are locked to one of the BLOCKED states and all attempts to change that through the rfkill API (userspace and kernel) will be either ignored or return -EPERM errors. The lock will be released upon receipt of EV_SW SW_RFKILL_ALL ON by rfkill-input, or should modular rfkill-input be unloaded. This makes rfkill and rfkill-input extend the operation of an existing wireless master kill switch to all wireless devices in the system, even those that are not under hardware or firmware control. Since the above is the expected operational behavior for the master rfkill switch, the EPO lock functionality is not optional. Also, extend rfkill-input to allow for three different behaviors when it receives an EV_SW SW_RFKILL_ALL ON input event. The user can set which behavior he wants through the master_switch_mode parameter: master_switch_mode = 0: EV_SW SW_RFKILL_ALL ON just unlocks rfkill controller state changes (so that the rfkill userspace and kernel APIs can now be used to change rfkill controller states again), but doesn't change any of their states (so they will all remain blocked). This is the safest mode of operation, as it requires explicit operator action to re-enable a transmitter. master_switch_mode = 1: EV_SW SW_RFKILL_ALL ON causes rfkill-input to attempt to restore the system to the state before the last EV_SW SW_RFKILL_ALL OFF event, or to the default global states if no EV_SW SW_RFKILL_ALL OFF ever happened. This is the recommended mode of operation for laptops. master_switch_mode = 2: tries to unblock all rfkill controllers (i.e. enable all transmitters) when an EV_SW SW_RFKILL_ALL ON event is received. This is the default mode of operation, as it mimics the previous behavior of rfkill-input. In order to implement these features in a clean way, the entire event handling of rfkill-input was refactored into a single worker function. Protection against input event DoS (repeatedly firing rfkill events for rfkill-input to process) was removed during the code refactoring. It will be added back in a future patch. Note that with these changes, rfkill-input doesn't need to explicitly handle any radio types for which KEY_<radio type> or SW_<radio type> events do not exist yet. Code to handle EV_SW SW_{WLAN,WWAN,BLUETOOTH,WIMAX,...} was added as it might be needed in the future (and its implementation is not that obvious), but is currently #ifdef'd out to avoid wasting resources. Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> Cc: Ivo van Doorn <IvDoorn@gmail.com> Cc: Dmitry Torokhov <dtor@mail.ru> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/rfkill/rfkill.c')
-rw-r--r--net/rfkill/rfkill.c46
1 files changed, 43 insertions, 3 deletions
diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c
index fdf87d2ab25e..e348eab756f3 100644
--- a/net/rfkill/rfkill.c
+++ b/net/rfkill/rfkill.c
@@ -51,6 +51,7 @@ struct rfkill_gsw_state {
51 51
52static struct rfkill_gsw_state rfkill_global_states[RFKILL_TYPE_MAX]; 52static struct rfkill_gsw_state rfkill_global_states[RFKILL_TYPE_MAX];
53static unsigned long rfkill_states_lockdflt[BITS_TO_LONGS(RFKILL_TYPE_MAX)]; 53static unsigned long rfkill_states_lockdflt[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
54static bool rfkill_epo_lock_active;
54 55
55static BLOCKING_NOTIFIER_HEAD(rfkill_notifier_list); 56static BLOCKING_NOTIFIER_HEAD(rfkill_notifier_list);
56 57
@@ -264,11 +265,14 @@ static void __rfkill_switch_all(const enum rfkill_type type,
264 * 265 *
265 * Acquires rfkill_global_mutex and calls __rfkill_switch_all(@type, @state). 266 * Acquires rfkill_global_mutex and calls __rfkill_switch_all(@type, @state).
266 * Please refer to __rfkill_switch_all() for details. 267 * Please refer to __rfkill_switch_all() for details.
268 *
269 * Does nothing if the EPO lock is active.
267 */ 270 */
268void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) 271void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state)
269{ 272{
270 mutex_lock(&rfkill_global_mutex); 273 mutex_lock(&rfkill_global_mutex);
271 __rfkill_switch_all(type, state); 274 if (!rfkill_epo_lock_active)
275 __rfkill_switch_all(type, state);
272 mutex_unlock(&rfkill_global_mutex); 276 mutex_unlock(&rfkill_global_mutex);
273} 277}
274EXPORT_SYMBOL(rfkill_switch_all); 278EXPORT_SYMBOL(rfkill_switch_all);
@@ -289,6 +293,7 @@ void rfkill_epo(void)
289 293
290 mutex_lock(&rfkill_global_mutex); 294 mutex_lock(&rfkill_global_mutex);
291 295
296 rfkill_epo_lock_active = true;
292 list_for_each_entry(rfkill, &rfkill_list, node) { 297 list_for_each_entry(rfkill, &rfkill_list, node) {
293 mutex_lock(&rfkill->mutex); 298 mutex_lock(&rfkill->mutex);
294 rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); 299 rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
@@ -317,6 +322,7 @@ void rfkill_restore_states(void)
317 322
318 mutex_lock(&rfkill_global_mutex); 323 mutex_lock(&rfkill_global_mutex);
319 324
325 rfkill_epo_lock_active = false;
320 for (i = 0; i < RFKILL_TYPE_MAX; i++) 326 for (i = 0; i < RFKILL_TYPE_MAX; i++)
321 __rfkill_switch_all(i, rfkill_global_states[i].default_state); 327 __rfkill_switch_all(i, rfkill_global_states[i].default_state);
322 mutex_unlock(&rfkill_global_mutex); 328 mutex_unlock(&rfkill_global_mutex);
@@ -324,6 +330,35 @@ void rfkill_restore_states(void)
324EXPORT_SYMBOL_GPL(rfkill_restore_states); 330EXPORT_SYMBOL_GPL(rfkill_restore_states);
325 331
326/** 332/**
333 * rfkill_remove_epo_lock - unlock state changes
334 *
335 * Used by rfkill-input manually unlock state changes, when
336 * the EPO switch is deactivated.
337 */
338void rfkill_remove_epo_lock(void)
339{
340 mutex_lock(&rfkill_global_mutex);
341 rfkill_epo_lock_active = false;
342 mutex_unlock(&rfkill_global_mutex);
343}
344EXPORT_SYMBOL_GPL(rfkill_remove_epo_lock);
345
346/**
347 * rfkill_is_epo_lock_active - returns true EPO is active
348 *
349 * Returns 0 (false) if there is NOT an active EPO contidion,
350 * and 1 (true) if there is an active EPO contition, which
351 * locks all radios in one of the BLOCKED states.
352 *
353 * Can be called in atomic context.
354 */
355bool rfkill_is_epo_lock_active(void)
356{
357 return rfkill_epo_lock_active;
358}
359EXPORT_SYMBOL_GPL(rfkill_is_epo_lock_active);
360
361/**
327 * rfkill_get_global_state - returns global state for a type 362 * rfkill_get_global_state - returns global state for a type
328 * @type: the type to get the global state of 363 * @type: the type to get the global state of
329 * 364 *
@@ -447,7 +482,12 @@ static ssize_t rfkill_state_store(struct device *dev,
447 error = mutex_lock_killable(&rfkill->mutex); 482 error = mutex_lock_killable(&rfkill->mutex);
448 if (error) 483 if (error)
449 return error; 484 return error;
450 error = rfkill_toggle_radio(rfkill, state, 0); 485
486 if (!rfkill_epo_lock_active)
487 error = rfkill_toggle_radio(rfkill, state, 0);
488 else
489 error = -EPERM;
490
451 mutex_unlock(&rfkill->mutex); 491 mutex_unlock(&rfkill->mutex);
452 492
453 return error ? error : count; 493 return error ? error : count;
@@ -491,7 +531,7 @@ static ssize_t rfkill_claim_store(struct device *dev,
491 return error; 531 return error;
492 532
493 if (rfkill->user_claim != claim) { 533 if (rfkill->user_claim != claim) {
494 if (!claim) { 534 if (!claim && !rfkill_epo_lock_active) {
495 mutex_lock(&rfkill->mutex); 535 mutex_lock(&rfkill->mutex);
496 rfkill_toggle_radio(rfkill, 536 rfkill_toggle_radio(rfkill,
497 rfkill_global_states[rfkill->type].current_state, 537 rfkill_global_states[rfkill->type].current_state,