aboutsummaryrefslogtreecommitdiff
path: root/net/rfkill/rfkill.c
diff options
context:
space:
mode:
authorHenrique de Moraes Holschuh <hmh@hmh.eng.br>2008-10-09 21:49:33 -0300
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 fdf87d2ab25..e348eab756f 100644
--- a/net/rfkill/rfkill.c
+++ b/net/rfkill/rfkill.c
@@ -51,6 +51,7 @@ struct rfkill_gsw_state {
static struct rfkill_gsw_state rfkill_global_states[RFKILL_TYPE_MAX];
static unsigned long rfkill_states_lockdflt[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
+static bool rfkill_epo_lock_active;
static BLOCKING_NOTIFIER_HEAD(rfkill_notifier_list);
@@ -264,11 +265,14 @@ static void __rfkill_switch_all(const enum rfkill_type type,
*
* Acquires rfkill_global_mutex and calls __rfkill_switch_all(@type, @state).
* Please refer to __rfkill_switch_all() for details.
+ *
+ * Does nothing if the EPO lock is active.
*/
void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state)
{
mutex_lock(&rfkill_global_mutex);
- __rfkill_switch_all(type, state);
+ if (!rfkill_epo_lock_active)
+ __rfkill_switch_all(type, state);
mutex_unlock(&rfkill_global_mutex);
}
EXPORT_SYMBOL(rfkill_switch_all);
@@ -289,6 +293,7 @@ void rfkill_epo(void)
mutex_lock(&rfkill_global_mutex);
+ rfkill_epo_lock_active = true;
list_for_each_entry(rfkill, &rfkill_list, node) {
mutex_lock(&rfkill->mutex);
rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
@@ -317,6 +322,7 @@ void rfkill_restore_states(void)
mutex_lock(&rfkill_global_mutex);
+ rfkill_epo_lock_active = false;
for (i = 0; i < RFKILL_TYPE_MAX; i++)
__rfkill_switch_all(i, rfkill_global_states[i].default_state);
mutex_unlock(&rfkill_global_mutex);
@@ -324,6 +330,35 @@ void rfkill_restore_states(void)
EXPORT_SYMBOL_GPL(rfkill_restore_states);
/**
+ * rfkill_remove_epo_lock - unlock state changes
+ *
+ * Used by rfkill-input manually unlock state changes, when
+ * the EPO switch is deactivated.
+ */
+void rfkill_remove_epo_lock(void)
+{
+ mutex_lock(&rfkill_global_mutex);
+ rfkill_epo_lock_active = false;
+ mutex_unlock(&rfkill_global_mutex);
+}
+EXPORT_SYMBOL_GPL(rfkill_remove_epo_lock);
+
+/**
+ * rfkill_is_epo_lock_active - returns true EPO is active
+ *
+ * Returns 0 (false) if there is NOT an active EPO contidion,
+ * and 1 (true) if there is an active EPO contition, which
+ * locks all radios in one of the BLOCKED states.
+ *
+ * Can be called in atomic context.
+ */
+bool rfkill_is_epo_lock_active(void)
+{
+ return rfkill_epo_lock_active;
+}
+EXPORT_SYMBOL_GPL(rfkill_is_epo_lock_active);
+
+/**
* rfkill_get_global_state - returns global state for a type
* @type: the type to get the global state of
*
@@ -447,7 +482,12 @@ static ssize_t rfkill_state_store(struct device *dev,
error = mutex_lock_killable(&rfkill->mutex);
if (error)
return error;
- error = rfkill_toggle_radio(rfkill, state, 0);
+
+ if (!rfkill_epo_lock_active)
+ error = rfkill_toggle_radio(rfkill, state, 0);
+ else
+ error = -EPERM;
+
mutex_unlock(&rfkill->mutex);
return error ? error : count;
@@ -491,7 +531,7 @@ static ssize_t rfkill_claim_store(struct device *dev,
return error;
if (rfkill->user_claim != claim) {
- if (!claim) {
+ if (!claim && !rfkill_epo_lock_active) {
mutex_lock(&rfkill->mutex);
rfkill_toggle_radio(rfkill,
rfkill_global_states[rfkill->type].current_state,