Search Linux Wireless

Re: [PATCH 3/5] rfkill: add master_switch_mode and EPO lock to rfkill and rfkill-input

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Friday 10 October 2008, Henrique de Moraes Holschuh wrote:
> 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@xxxxxxxxxx>
> Cc: Ivo van Doorn <IvDoorn@xxxxxxxxx>
> Cc: Dmitry Torokhov <dtor@xxxxxxx>

Although I think RFKILL_NEED_SWSET could be removed entirely if it isn't used anyway,
we might do that later.

Acked-by: Ivo van Doorn <IvDoorn@xxxxxxxxx>

> ---
>  Here, typos fixed...
> 
>  Documentation/rfkill.txt  |   20 ++-
>  net/rfkill/rfkill-input.c |  301 ++++++++++++++++++++++++++++++++++-----------
>  net/rfkill/rfkill-input.h |    2 +
>  net/rfkill/rfkill.c       |   46 +++++++-
>  4 files changed, 287 insertions(+), 82 deletions(-)
> 
> diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt
> index b65f079..4d3ee31 100644
> --- a/Documentation/rfkill.txt
> +++ b/Documentation/rfkill.txt
> @@ -191,12 +191,20 @@ Userspace input handlers (uevents) or kernel input handlers (rfkill-input):
>  	  to tell the devices registered with the rfkill class to change
>  	  their state (i.e. translates the input layer event into real
>  	  action).
> +
>  	* rfkill-input implements EPO by handling EV_SW SW_RFKILL_ALL 0
>  	  (power off all transmitters) in a special way: it ignores any
>  	  overrides and local state cache and forces all transmitters to the
>  	  RFKILL_STATE_SOFT_BLOCKED state (including those which are already
> -	  supposed to be BLOCKED).  Note that the opposite event (power on all
> -	  transmitters) is handled normally.
> +	  supposed to be BLOCKED).
> +	* rfkill EPO will remain active until rfkill-input receives an
> +	  EV_SW SW_RFKILL_ALL 1 event.  While the EPO is active, transmitters
> +	  are locked in the blocked state (rfkill will refuse to unblock them).
> +	* rfkill-input implements different policies that the user can
> +	  select for handling EV_SW SW_RFKILL_ALL 1.  It will unlock rfkill,
> +	  and either do nothing (leave transmitters blocked, but now unlocked),
> +	  restore the transmitters to their state before the EPO, or unblock
> +	  them all.
>  
>  Userspace uevent handler or kernel platform-specific drivers hooked to the
>  rfkill notifier chain:
> @@ -331,11 +339,9 @@ class to get a sysfs interface :-)
>  correct event for your switch/button.  These events are emergency power-off
>  events when they are trying to turn the transmitters off.  An example of an
>  input device which SHOULD generate *_RFKILL_ALL events is the wireless-kill
> -switch in a laptop which is NOT a hotkey, but a real switch that kills radios
> -in hardware, even if the O.S. has gone to lunch.  An example of an input device
> -which SHOULD NOT generate *_RFKILL_ALL events by default, is any sort of hot
> -key that does nothing by itself, as well as any hot key that is type-specific
> -(e.g. the one for WLAN).
> +switch in a laptop which is NOT a hotkey, but a real sliding/rocker switch.
> +An example of an input device which SHOULD NOT generate *_RFKILL_ALL events by
> +default, is any sort of hot key that is type-specific (e.g. the one for WLAN).
>  
>  
>  3.1 Guidelines for wireless device drivers
> diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c
> index e5b6955..4ea4e68 100644
> --- a/net/rfkill/rfkill-input.c
> +++ b/net/rfkill/rfkill-input.c
> @@ -23,138 +23,291 @@ MODULE_AUTHOR("Dmitry Torokhov <dtor@xxxxxxx>");
>  MODULE_DESCRIPTION("Input layer to RF switch connector");
>  MODULE_LICENSE("GPL");
>  
> +enum rfkill_input_master_mode {
> +	RFKILL_INPUT_MASTER_DONOTHING = 0,
> +	RFKILL_INPUT_MASTER_RESTORE = 1,
> +	RFKILL_INPUT_MASTER_UNBLOCKALL = 2,
> +	RFKILL_INPUT_MASTER_MAX,	/* marker */
> +};
> +
> +static enum rfkill_input_master_mode rfkill_master_switch_mode =
> +					RFKILL_INPUT_MASTER_UNBLOCKALL;
> +module_param_named(master_switch_mode, rfkill_master_switch_mode, uint, 0);
> +MODULE_PARM_DESC(master_switch_mode,
> +	"SW_RFKILL_ALL ON should: 0=do nothing; 1=restore; 2=unblock all");
> +
> +enum rfkill_global_sched_op {
> +	RFKILL_GLOBAL_OP_EPO = 0,
> +	RFKILL_GLOBAL_OP_RESTORE,
> +	RFKILL_GLOBAL_OP_UNLOCK,
> +	RFKILL_GLOBAL_OP_UNBLOCK,
> +};
> +
> +/*
> + * Currently, the code marked with RFKILL_NEED_SWSET is inactive.
> + * If handling of EV_SW SW_WLAN/WWAN/BLUETOOTH/etc is needed in the
> + * future, when such events are added, that code will be necessary.
> + */
> +
>  struct rfkill_task {
>  	struct work_struct work;
> -	enum rfkill_type type;
> -	struct mutex mutex; /* ensures that task is serialized */
> -	spinlock_t lock; /* for accessing last and desired state */
> -	unsigned long last; /* last schedule */
> -	enum rfkill_state desired_state; /* on/off */
> +
> +	/* ensures that task is serialized */
> +	struct mutex mutex;
> +
> +	/* protects everything below */
> +	spinlock_t lock;
> +
> +	/* pending regular switch operations (1=pending) */
> +	unsigned long sw_pending[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
> +
> +#ifdef RFKILL_NEED_SWSET
> +	/* set operation pending (1=pending) */
> +	unsigned long sw_setpending[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
> +
> +	/* desired state for pending set operation (1=unblock) */
> +	unsigned long sw_newstate[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
> +#endif
> +
> +	/* should the state be complemented (1=yes) */
> +	unsigned long sw_togglestate[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
> +
> +	bool global_op_pending;
> +	enum rfkill_global_sched_op op;
>  };
>  
> -static void rfkill_task_handler(struct work_struct *work)
> +static void __rfkill_handle_global_op(enum rfkill_global_sched_op op)
>  {
> -	struct rfkill_task *task = container_of(work, struct rfkill_task, work);
> +	unsigned int i;
> +
> +	switch (op) {
> +	case RFKILL_GLOBAL_OP_EPO:
> +		rfkill_epo();
> +		break;
> +	case RFKILL_GLOBAL_OP_RESTORE:
> +		rfkill_restore_states();
> +		break;
> +	case RFKILL_GLOBAL_OP_UNLOCK:
> +		rfkill_remove_epo_lock();
> +		break;
> +	case RFKILL_GLOBAL_OP_UNBLOCK:
> +		rfkill_remove_epo_lock();
> +		for (i = 0; i < RFKILL_TYPE_MAX; i++)
> +			rfkill_switch_all(i, RFKILL_STATE_UNBLOCKED);
> +		break;
> +	default:
> +		/* memory corruption or bug, fail safely */
> +		rfkill_epo();
> +		WARN(1, "Unknown requested operation %d! "
> +			"rfkill Emergency Power Off activated\n",
> +			op);
> +	}
> +}
>  
> -	mutex_lock(&task->mutex);
> +#ifdef RFKILL_NEED_SWSET
> +static void __rfkill_handle_normal_op(const enum rfkill_type type,
> +			const bool sp, const bool s, const bool c)
> +{
> +	enum rfkill_state state;
>  
> -	rfkill_switch_all(task->type, task->desired_state);
> +	if (sp)
> +		state = (s) ? RFKILL_STATE_UNBLOCKED :
> +			      RFKILL_STATE_SOFT_BLOCKED;
> +	else
> +		state = rfkill_get_global_state(type);
>  
> -	mutex_unlock(&task->mutex);
> +	if (c)
> +		state = rfkill_state_complement(state);
> +
> +	rfkill_switch_all(type, state);
>  }
> +#else
> +static void __rfkill_handle_normal_op(const enum rfkill_type type,
> +			const bool c)
> +{
> +	enum rfkill_state state;
> +
> +	state = rfkill_get_global_state(type);
> +	if (c)
> +		state = rfkill_state_complement(state);
> +
> +	rfkill_switch_all(type, state);
> +}
> +#endif
>  
> -static void rfkill_task_epo_handler(struct work_struct *work)
> +static void rfkill_task_handler(struct work_struct *work)
>  {
> -	rfkill_epo();
> +	struct rfkill_task *task =
> +			container_of(work, struct rfkill_task, work);
> +	bool doit = true;
> +
> +	mutex_lock(&task->mutex);
> +
> +	spin_lock_irq(&task->lock);
> +	while (doit) {
> +		if (task->global_op_pending) {
> +			enum rfkill_global_sched_op op = task->op;
> +			task->global_op_pending = false;
> +			memset(task->sw_pending, 0, sizeof(task->sw_pending));
> +			spin_unlock_irq(&task->lock);
> +
> +			__rfkill_handle_global_op(op);
> +
> +			/* make sure we do at least one pass with
> +			 * !task->global_op_pending */
> +			spin_lock_irq(&task->lock);
> +			continue;
> +		} else if (!rfkill_is_epo_lock_active()) {
> +			unsigned int i = 0;
> +
> +			while (!task->global_op_pending &&
> +						i < RFKILL_TYPE_MAX) {
> +				if (test_and_clear_bit(i, task->sw_pending)) {
> +					bool c;
> +#ifdef RFKILL_NEED_SWSET
> +					bool sp, s;
> +					sp = test_and_clear_bit(i,
> +							task->sw_setpending);
> +					s = test_bit(i, task->sw_newstate);
> +#endif
> +					c = test_and_clear_bit(i,
> +							task->sw_togglestate);
> +					spin_unlock_irq(&task->lock);
> +
> +#ifdef RFKILL_NEED_SWSET
> +					__rfkill_handle_normal_op(i, sp, s, c);
> +#else
> +					__rfkill_handle_normal_op(i, c);
> +#endif
> +
> +					spin_lock_irq(&task->lock);
> +				}
> +				i++;
> +			}
> +		}
> +		doit = task->global_op_pending;
> +	}
> +	spin_unlock_irq(&task->lock);
> +
> +	mutex_unlock(&task->mutex);
>  }
>  
> -static DECLARE_WORK(epo_work, rfkill_task_epo_handler);
> +static struct rfkill_task rfkill_task = {
> +	.work = __WORK_INITIALIZER(rfkill_task.work,
> +				rfkill_task_handler),
> +	.mutex = __MUTEX_INITIALIZER(rfkill_task.mutex),
> +	.lock = __SPIN_LOCK_UNLOCKED(rfkill_task.lock),
> +};
>  
> -static void rfkill_schedule_epo(void)
> +static void rfkill_schedule_global_op(enum rfkill_global_sched_op op)
>  {
> -	schedule_work(&epo_work);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&rfkill_task.lock, flags);
> +	rfkill_task.op = op;
> +	rfkill_task.global_op_pending = true;
> +	schedule_work(&rfkill_task.work);
> +	spin_unlock_irqrestore(&rfkill_task.lock, flags);
>  }
>  
> -static void rfkill_schedule_set(struct rfkill_task *task,
> +#ifdef RFKILL_NEED_SWSET
> +/* Use this if you need to add EV_SW SW_WLAN/WWAN/BLUETOOTH/etc handling */
> +
> +static void rfkill_schedule_set(enum rfkill_type type,
>  				enum rfkill_state desired_state)
>  {
>  	unsigned long flags;
>  
> -	if (unlikely(work_pending(&epo_work)))
> +	if (rfkill_is_epo_lock_active())
>  		return;
>  
> -	spin_lock_irqsave(&task->lock, flags);
> -
> -	if (time_after(jiffies, task->last + msecs_to_jiffies(200))) {
> -		task->desired_state = desired_state;
> -		task->last = jiffies;
> -		schedule_work(&task->work);
> +	spin_lock_irqsave(&rfkill_task.lock, flags);
> +	if (!rfkill_task.global_op_pending) {
> +		set_bit(type, rfkill_task.sw_pending);
> +		set_bit(type, rfkill_task.sw_setpending);
> +		clear_bit(type, rfkill_task.sw_togglestate);
> +		if (desired_state)
> +			set_bit(type,  rfkill_task.sw_newstate);
> +		else
> +			clear_bit(type, rfkill_task.sw_newstate);
> +		schedule_work(&rfkill_task.work);
>  	}
> -
> -	spin_unlock_irqrestore(&task->lock, flags);
> +	spin_unlock_irqrestore(&rfkill_task.lock, flags);
>  }
> +#endif
>  
> -static void rfkill_schedule_toggle(struct rfkill_task *task)
> +static void rfkill_schedule_toggle(enum rfkill_type type)
>  {
>  	unsigned long flags;
>  
> -	if (unlikely(work_pending(&epo_work)))
> +	if (rfkill_is_epo_lock_active())
>  		return;
>  
> -	spin_lock_irqsave(&task->lock, flags);
> -
> -	if (time_after(jiffies, task->last + msecs_to_jiffies(200))) {
> -		task->desired_state =
> -				rfkill_state_complement(task->desired_state);
> -		task->last = jiffies;
> -		schedule_work(&task->work);
> +	spin_lock_irqsave(&rfkill_task.lock, flags);
> +	if (!rfkill_task.global_op_pending) {
> +		set_bit(type, rfkill_task.sw_pending);
> +		change_bit(type, rfkill_task.sw_togglestate);
> +		schedule_work(&rfkill_task.work);
>  	}
> -
> -	spin_unlock_irqrestore(&task->lock, flags);
> +	spin_unlock_irqrestore(&rfkill_task.lock, flags);
>  }
>  
> -#define DEFINE_RFKILL_TASK(n, t)				\
> -	struct rfkill_task n = {				\
> -		.work = __WORK_INITIALIZER(n.work,		\
> -				rfkill_task_handler),		\
> -		.type = t,					\
> -		.mutex = __MUTEX_INITIALIZER(n.mutex),		\
> -		.lock = __SPIN_LOCK_UNLOCKED(n.lock),		\
> -		.desired_state = RFKILL_STATE_UNBLOCKED,	\
> -	}
> -
> -static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN);
> -static DEFINE_RFKILL_TASK(rfkill_bt, RFKILL_TYPE_BLUETOOTH);
> -static DEFINE_RFKILL_TASK(rfkill_uwb, RFKILL_TYPE_UWB);
> -static DEFINE_RFKILL_TASK(rfkill_wimax, RFKILL_TYPE_WIMAX);
> -static DEFINE_RFKILL_TASK(rfkill_wwan, RFKILL_TYPE_WWAN);
> -
>  static void rfkill_schedule_evsw_rfkillall(int state)
>  {
> -	/* EVERY radio type. state != 0 means radios ON */
> -	/* handle EPO (emergency power off) through shortcut */
>  	if (state) {
> -		rfkill_schedule_set(&rfkill_wwan,
> -				    RFKILL_STATE_UNBLOCKED);
> -		rfkill_schedule_set(&rfkill_wimax,
> -				    RFKILL_STATE_UNBLOCKED);
> -		rfkill_schedule_set(&rfkill_uwb,
> -				    RFKILL_STATE_UNBLOCKED);
> -		rfkill_schedule_set(&rfkill_bt,
> -				    RFKILL_STATE_UNBLOCKED);
> -		rfkill_schedule_set(&rfkill_wlan,
> -				    RFKILL_STATE_UNBLOCKED);
> +		switch (rfkill_master_switch_mode) {
> +		case RFKILL_INPUT_MASTER_UNBLOCKALL:
> +			rfkill_schedule_global_op(RFKILL_GLOBAL_OP_UNBLOCK);
> +			break;
> +		case RFKILL_INPUT_MASTER_RESTORE:
> +			rfkill_schedule_global_op(RFKILL_GLOBAL_OP_RESTORE);
> +			break;
> +		case RFKILL_INPUT_MASTER_DONOTHING:
> +			rfkill_schedule_global_op(RFKILL_GLOBAL_OP_UNLOCK);
> +			break;
> +		default:
> +			/* memory corruption or driver bug! fail safely */
> +			rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO);
> +			WARN(1, "Unknown rfkill_master_switch_mode (%d), "
> +				"driver bug or memory corruption detected!\n",
> +				rfkill_master_switch_mode);
> +			break;
> +		}
>  	} else
> -		rfkill_schedule_epo();
> +		rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO);
>  }
>  
>  static void rfkill_event(struct input_handle *handle, unsigned int type,
>  			unsigned int code, int data)
>  {
>  	if (type == EV_KEY && data == 1) {
> +		enum rfkill_type t;
> +
>  		switch (code) {
>  		case KEY_WLAN:
> -			rfkill_schedule_toggle(&rfkill_wlan);
> +			t = RFKILL_TYPE_WLAN;
>  			break;
>  		case KEY_BLUETOOTH:
> -			rfkill_schedule_toggle(&rfkill_bt);
> +			t = RFKILL_TYPE_BLUETOOTH;
>  			break;
>  		case KEY_UWB:
> -			rfkill_schedule_toggle(&rfkill_uwb);
> +			t = RFKILL_TYPE_UWB;
>  			break;
>  		case KEY_WIMAX:
> -			rfkill_schedule_toggle(&rfkill_wimax);
> +			t = RFKILL_TYPE_WIMAX;
>  			break;
>  		default:
> -			break;
> +			return;
>  		}
> +		rfkill_schedule_toggle(t);
> +		return;
>  	} else if (type == EV_SW) {
>  		switch (code) {
>  		case SW_RFKILL_ALL:
>  			rfkill_schedule_evsw_rfkillall(data);
> -			break;
> +			return;
>  		default:
> -			break;
> +			return;
>  		}
>  	}
>  }
> @@ -255,6 +408,9 @@ static struct input_handler rfkill_handler = {
>  
>  static int __init rfkill_handler_init(void)
>  {
> +	if (rfkill_master_switch_mode >= RFKILL_INPUT_MASTER_MAX)
> +		return -EINVAL;
> +
>  	return input_register_handler(&rfkill_handler);
>  }
>  
> @@ -262,6 +418,7 @@ static void __exit rfkill_handler_exit(void)
>  {
>  	input_unregister_handler(&rfkill_handler);
>  	flush_scheduled_work();
> +	rfkill_remove_epo_lock();
>  }
>  
>  module_init(rfkill_handler_init);
> diff --git a/net/rfkill/rfkill-input.h b/net/rfkill/rfkill-input.h
> index d1e03e8..fe8df6b 100644
> --- a/net/rfkill/rfkill-input.h
> +++ b/net/rfkill/rfkill-input.h
> @@ -14,6 +14,8 @@
>  void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state);
>  void rfkill_epo(void);
>  void rfkill_restore_states(void);
> +void rfkill_remove_epo_lock(void);
> +bool rfkill_is_epo_lock_active(void);
>  enum rfkill_state rfkill_get_global_state(const enum rfkill_type type);
>  
>  #endif /* __RFKILL_INPUT_H */
> diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c
> index fdf87d2..e348eab 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,
> -- 
> 1.5.6.5
> 


--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux