Search Linux Wireless

Re: [PATCH 1/2] rfkill: rename the rfkill_state states and add block-locked state

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

 



On Monday 23 June 2008, Henrique de Moraes Holschuh wrote:
> The current naming of rfkill_state causes a lot of confusion: not only the
> "kill" in rfkill suggests negative logic, but also the fact that rfkill cannot
> turn anything on (it can just force something off or stop forcing something
> off) is often forgotten.
> 
> Rename RFKILL_STATE_OFF to RFKILL_STATE_SOFT_BLOCKED (transmitter is blocked
> and will not operate; state can be changed by a toggle_radio request), and
> RFKILL_STATE_ON to RFKILL_STATE_UNBLOCKED (transmitter is not blocked, and may
> operate).
> 
> Also, add a new third state, RFKILL_STATE_HARD_BLOCKED (transmitter is blocked
> and will not operate; state cannot be changed through a toggle_radio request),
> which is used by drivers to indicate a wireless transmiter was blocked by a
> hardware rfkill line that accepts no overrides.
> 
> Keep the old names as #defines, but document them as deprecated.  This way,
> drivers can be converted to the new names *and* verified to actually use rfkill
> correctly one by one.
> 
> Signed-off-by: Henrique de Moraes Holschuh <hmh@xxxxxxxxxx>
> Cc: Ivo van Doorn <IvDoorn@xxxxxxxxx>

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

> ---
>  Documentation/rfkill.txt  |   56 ++++++++++++++++++++++++++++-----
>  include/linux/rfkill.h    |   32 +++++++++++++++++--
>  net/rfkill/rfkill-input.c |   29 +++++++++--------
>  net/rfkill/rfkill.c       |   75 ++++++++++++++++++++++++++++++++++++--------
>  4 files changed, 152 insertions(+), 40 deletions(-)
> 
> diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt
> index cf230c1..5316cea 100644
> --- a/Documentation/rfkill.txt
> +++ b/Documentation/rfkill.txt
> @@ -60,9 +60,20 @@ The second option provides an rfkill input handler. This handler will listen to
>  all rfkill key events and will toggle the radio accordingly.  With this option
>  enabled userspace could either do nothing or simply perform monitoring tasks.
>  
> -When a rfkill switch is in the RFKILL_STATE_ON, the wireless transmitter (radio
> -TX circuit for example) is *enabled*.  When the rfkill switch is in the
> -RFKILL_STATE_OFF, the wireless transmitter is to be *blocked* from operating.
> +When a rfkill switch is in the RFKILL_STATE_UNBLOCKED, the wireless transmitter
> +(radio TX circuit for example) is *enabled*.  When the rfkill switch is in the
> +RFKILL_STATE_SOFT_BLOCKED or RFKILL_STATE_HARD_BLOCKED, the wireless
> +transmitter is to be *blocked* from operating.
> +
> +RFKILL_STATE_SOFT_BLOCKED indicates that a call to toggle_radio() can change
> +that state.  RFKILL_STATE_HARD_BLOCKED indicates that a call to toggle_radio()
> +will not be able to change the state and will return with a suitable error if
> +attempts are made to set the state to RFKILL_STATE_UNBLOCKED.
> +
> +RFKILL_STATE_HARD_BLOCKED is used by drivers to signal that the device is
> +locked in the BLOCKED state by a hardwire rfkill line (typically an input pin
> +that, when active, forces the transmitter to be disabled) which the driver
> +CANNOT override.
>  
>  Full rfkill functionality requires two different subsystems to cooperate: the
>  input layer and the rfkill class.  The input layer issues *commands* to the
> @@ -122,10 +133,10 @@ Userspace input handlers (uevents) or kernel input handlers (rfkill-input):
>  	  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 OFF state (including those which are already supposed to be
> -	  OFF).  Note that the opposite event (power on all transmitters)
> -	  is handled normally.
> +	  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.
>  
>  Userspace uevent handler or kernel platform-specific drivers hooked to the
>  rfkill notifier chain:
> @@ -284,6 +295,19 @@ You should:
>  	  YOU CAN ACCESS state DIRECTLY)
>  	- rfkill_register()
>  
> +The only way to set a device to the RFKILL_STATE_HARD_BLOCKED state is through
> +a suitable return of get_state() or through rfkill_force_state().
> +
> +When a device is in the RFKILL_STATE_HARD_BLOCKED state, the only way to switch
> +it to a different state is through a suitable return of get_state() or through
> +rfkill_force_state().
> +
> +If toggle_radio() is called to set a device to state RFKILL_STATE_SOFT_BLOCKED
> +when that device is already at the RFKILL_STATE_HARD_BLOCKED state, it should
> +not return an error.  Instead, it should try to double-block the transmitter,
> +so that its state will change from RFKILL_STATE_HARD_BLOCKED to
> +RFKILL_STATE_SOFT_BLOCKED should the hardware blocking cease.
> +
>  Please refer to the source for more documentation.
>  
>  ===============================================================================
> @@ -322,13 +346,27 @@ change by writing to the "state" attribute in order for anything to happen.
>  
>  Take particular care to implement EV_SW SW_RFKILL_ALL properly.  When that
>  switch is set to OFF, *every* rfkill device *MUST* be immediately put into the
> -OFF state, no questions asked.
> +RFKILL_STATE_SOFT_BLOCKED state, no questions asked.
>  
>  The following sysfs entries will be created:
>  
>  	name: Name assigned by driver to this key (interface or driver name).
>  	type: Name of the key type ("wlan", "bluetooth", etc).
> -	state: Current state of the key. 1: On, 0: Off.
> +	state: Current state of the transmitter
> +		0: RFKILL_STATE_SOFT_BLOCKED
> +			transmitter is forced off, but you can override it
> +			by a write to the state attribute, or through input
> +			events (if rfkill-input is loaded).
> +		1: RFKILL_STATE_UNBLOCKED
> +			transmiter is NOT forced off, and may operate if
> +			all other conditions for such operation are met
> +			(such as interface is up and configured, etc).
> +		2: RFKILL_STATE_HARD_BLOCKED
> +			transmitter is forced off by something outside of
> +			the driver's control.
> +
> +			You cannot set a device to this state through
> +			writes to the state attribute.
>  	claim: 1: Userspace handles events, 0: Kernel handles events
>  
>  Both the "state" and "claim" entries are also writable. For the "state" entry
> diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h
> index 98667be..c5f6e54 100644
> --- a/include/linux/rfkill.h
> +++ b/include/linux/rfkill.h
> @@ -46,16 +46,25 @@ enum rfkill_type {
>  };
>  
>  enum rfkill_state {
> -	RFKILL_STATE_OFF	= 0,	/* Radio output blocked */
> -	RFKILL_STATE_ON		= 1,	/* Radio output active */
> +	RFKILL_STATE_SOFT_BLOCKED = 0,	/* Radio output blocked */
> +	RFKILL_STATE_UNBLOCKED    = 1,	/* Radio output allowed */
> +	RFKILL_STATE_HARD_BLOCKED = 2,	/* Output blocked, non-overrideable */
>  };
>  
> +/*
> + * These are DEPRECATED, drivers using them should be verified to
> + * comply with the rfkill usage guidelines in Documentation/rfkill.txt
> + * and then converted to use the new names for rfkill_state
> + */
> +#define RFKILL_STATE_OFF RFKILL_STATE_SOFT_BLOCKED
> +#define RFKILL_STATE_ON  RFKILL_STATE_UNBLOCKED
> +
>  /**
>   * struct rfkill - rfkill control structure.
>   * @name: Name of the switch.
>   * @type: Radio type which the button controls, the value stored
>   *	here should be a value from enum rfkill_type.
> - * @state: State of the switch, "ON" means radio can operate.
> + * @state: State of the switch, "UNBLOCKED" means radio can operate.
>   * @user_claim_unsupported: Whether the hardware supports exclusive
>   *	RF-kill control by userspace. Set this before registering.
>   * @user_claim: Set when the switch is controlled exlusively by userspace.
> @@ -63,8 +72,12 @@ enum rfkill_state {
>   * @data: Pointer to the RF button drivers private data which will be
>   *	passed along when toggling radio state.
>   * @toggle_radio(): Mandatory handler to control state of the radio.
> + *	only RFKILL_STATE_SOFT_BLOCKED and RFKILL_STATE_UNBLOCKED are
> + *	valid parameters.
>   * @get_state(): handler to read current radio state from hardware,
>   *      may be called from atomic context, should return 0 on success.
> + *      Either this handler OR judicious use of rfkill_force_state() is
> + *      MANDATORY for any driver capable of RFKILL_STATE_HARD_BLOCKED.
>   * @led_trigger: A LED trigger for this button's LED.
>   * @dev: Device structure integrating the switch into device tree.
>   * @node: Used to place switch into list of all switches known to the
> @@ -103,6 +116,19 @@ void rfkill_unregister(struct rfkill *rfkill);
>  int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state);
>  
>  /**
> + * rfkill_state_complement - return complementar state
> + * @state: state to return the complement of
> + *
> + * Returns RFKILL_STATE_SOFT_BLOCKED if @state is RFKILL_STATE_UNBLOCKED,
> + * returns RFKILL_STATE_UNBLOCKED otherwise.
> + */
> +static inline enum rfkill_state rfkill_state_complement(enum rfkill_state state)
> +{
> +	return (state == RFKILL_STATE_UNBLOCKED) ?
> +		RFKILL_STATE_SOFT_BLOCKED : RFKILL_STATE_UNBLOCKED;
> +}
> +
> +/**
>   * rfkill_get_led_name - Get the LED trigger name for the button's LED.
>   * This function might return a NULL pointer if registering of the
>   * LED trigger failed.
> diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c
> index 5d4c8b2..8aa8227 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)
>  	spin_lock_irqsave(&task->lock, flags);
>  
>  	if (time_after(jiffies, task->last + msecs_to_jiffies(200))) {
> -		task->desired_state = !task->desired_state;
> +		task->desired_state =
> +				rfkill_state_complement(task->desired_state);
>  		task->last = jiffies;
>  		schedule_work(&task->work);
>  	}
> @@ -92,14 +93,14 @@ static void rfkill_schedule_toggle(struct rfkill_task *task)
>  	spin_unlock_irqrestore(&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_ON,	\
> +#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);
> @@ -135,15 +136,15 @@ static void rfkill_event(struct input_handle *handle, unsigned int type,
>  			/* handle EPO (emergency power off) through shortcut */
>  			if (data) {
>  				rfkill_schedule_set(&rfkill_wwan,
> -						    RFKILL_STATE_ON);
> +						    RFKILL_STATE_UNBLOCKED);
>  				rfkill_schedule_set(&rfkill_wimax,
> -						    RFKILL_STATE_ON);
> +						    RFKILL_STATE_UNBLOCKED);
>  				rfkill_schedule_set(&rfkill_uwb,
> -						    RFKILL_STATE_ON);
> +						    RFKILL_STATE_UNBLOCKED);
>  				rfkill_schedule_set(&rfkill_bt,
> -						    RFKILL_STATE_ON);
> +						    RFKILL_STATE_UNBLOCKED);
>  				rfkill_schedule_set(&rfkill_wlan,
> -						    RFKILL_STATE_ON);
> +						    RFKILL_STATE_UNBLOCKED);
>  			} else
>  				rfkill_schedule_epo();
>  			break;
> diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c
> index 7d07175..ce0e231 100644
> --- a/net/rfkill/rfkill.c
> +++ b/net/rfkill/rfkill.c
> @@ -39,7 +39,7 @@ MODULE_LICENSE("GPL");
>  static LIST_HEAD(rfkill_list);	/* list of registered rf switches */
>  static DEFINE_MUTEX(rfkill_mutex);
>  
> -static unsigned int rfkill_default_state = RFKILL_STATE_ON;
> +static unsigned int rfkill_default_state = RFKILL_STATE_UNBLOCKED;
>  module_param_named(default_state, rfkill_default_state, uint, 0444);
>  MODULE_PARM_DESC(default_state,
>  		 "Default initial state for all radio types, 0 = radio off");
> @@ -98,7 +98,7 @@ static void rfkill_led_trigger(struct rfkill *rfkill,
>  
>  	if (!led->name)
>  		return;
> -	if (state == RFKILL_STATE_OFF)
> +	if (state != RFKILL_STATE_UNBLOCKED)
>  		led_trigger_event(led, LED_OFF);
>  	else
>  		led_trigger_event(led, LED_FULL);
> @@ -128,6 +128,28 @@ static void update_rfkill_state(struct rfkill *rfkill)
>  	}
>  }
>  
> +/**
> + * rfkill_toggle_radio - wrapper for toggle_radio hook
> + * calls toggle_radio taking into account a lot of "small"
> + * details.
> + * @rfkill: the rfkill struct to use
> + * @force: calls toggle_radio even if cache says it is not needed,
> + *	and also makes sure notifications of the state will be
> + *	sent even if it didn't change
> + * @state: the new state to call toggle_radio() with
> + *
> + * This wrappen protects and enforces the API for toggle_radio
> + * calls.  Note that @force cannot override a (possibly cached)
> + * state of RFKILL_STATE_HARD_BLOCKED.  Any device making use of
> + * RFKILL_STATE_HARD_BLOCKED implements either get_state() or
> + * rfkill_force_state(), so the cache either is bypassed or valid.
> + *
> + * Note that we do call toggle_radio for RFKILL_STATE_SOFT_BLOCKED
> + * even if the radio is in RFKILL_STATE_HARD_BLOCKED state, so as to
> + * give the driver a hint that it should double-BLOCK the transmitter.
> + *
> + * Caller must have aquired rfkill_mutex.
> + */
>  static int rfkill_toggle_radio(struct rfkill *rfkill,
>  				enum rfkill_state state,
>  				int force)
> @@ -141,9 +163,28 @@ static int rfkill_toggle_radio(struct rfkill *rfkill,
>  	    !rfkill->get_state(rfkill->data, &newstate))
>  		rfkill->state = newstate;
>  
> +	switch (state) {
> +	case RFKILL_STATE_HARD_BLOCKED:
> +		/* typically happens when refreshing hardware state,
> +		 * such as on resume */
> +		state = RFKILL_STATE_SOFT_BLOCKED;
> +		break;
> +	case RFKILL_STATE_UNBLOCKED:
> +		/* force can't override this, only rfkill_force_state() can */
> +		if (rfkill->state == RFKILL_STATE_HARD_BLOCKED)
> +			return -EPERM;
> +		break;
> +	case RFKILL_STATE_SOFT_BLOCKED:
> +		/* nothing to do, we want to give drivers the hint to double
> +		 * BLOCK even a transmitter that is already in state
> +		 * RFKILL_STATE_HARD_BLOCKED */
> +		break;
> +	}
> +
>  	if (force || state != rfkill->state) {
>  		retval = rfkill->toggle_radio(rfkill->data, state);
> -		if (!retval)
> +		/* never allow a HARD->SOFT downgrade! */
> +		if (!retval && rfkill->state != RFKILL_STATE_HARD_BLOCKED)
>  			rfkill->state = state;
>  	}
>  
> @@ -184,7 +225,7 @@ EXPORT_SYMBOL(rfkill_switch_all);
>  /**
>   * rfkill_epo - emergency power off all transmitters
>   *
> - * This kicks all rfkill devices to RFKILL_STATE_OFF, ignoring
> + * This kicks all rfkill devices to RFKILL_STATE_SOFT_BLOCKED, ignoring
>   * everything in its path but rfkill_mutex.
>   */
>  void rfkill_epo(void)
> @@ -193,7 +234,7 @@ void rfkill_epo(void)
>  
>  	mutex_lock(&rfkill_mutex);
>  	list_for_each_entry(rfkill, &rfkill_list, node) {
> -		rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF, 1);
> +		rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
>  	}
>  	mutex_unlock(&rfkill_mutex);
>  }
> @@ -215,8 +256,9 @@ int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state)
>  {
>  	enum rfkill_state oldstate;
>  
> -	if (state != RFKILL_STATE_OFF &&
> -	    state != RFKILL_STATE_ON)
> +	if (state != RFKILL_STATE_SOFT_BLOCKED &&
> +	    state != RFKILL_STATE_UNBLOCKED &&
> +	    state != RFKILL_STATE_HARD_BLOCKED)
>  		return -EINVAL;
>  
>  	mutex_lock(&rfkill->mutex);
> @@ -290,11 +332,14 @@ static ssize_t rfkill_state_store(struct device *dev,
>  	if (!capable(CAP_NET_ADMIN))
>  		return -EPERM;
>  
> +	/* RFKILL_STATE_HARD_BLOCKED is illegal here... */
> +	if (state != RFKILL_STATE_UNBLOCKED &&
> +	    state != RFKILL_STATE_SOFT_BLOCKED)
> +		return -EINVAL;
> +
>  	if (mutex_lock_interruptible(&rfkill->mutex))
>  		return -ERESTARTSYS;
> -	error = rfkill_toggle_radio(rfkill,
> -			state ? RFKILL_STATE_ON : RFKILL_STATE_OFF,
> -			0);
> +	error = rfkill_toggle_radio(rfkill, state, 0);
>  	mutex_unlock(&rfkill->mutex);
>  
>  	return error ? error : count;
> @@ -373,7 +418,8 @@ static int rfkill_suspend(struct device *dev, pm_message_t state)
>  			update_rfkill_state(rfkill);
>  
>  			mutex_lock(&rfkill->mutex);
> -			rfkill->toggle_radio(rfkill->data, RFKILL_STATE_OFF);
> +			rfkill->toggle_radio(rfkill->data,
> +						RFKILL_STATE_SOFT_BLOCKED);
>  			mutex_unlock(&rfkill->mutex);
>  		}
>  
> @@ -470,7 +516,7 @@ static void rfkill_remove_switch(struct rfkill *rfkill)
>  {
>  	mutex_lock(&rfkill_mutex);
>  	list_del_init(&rfkill->node);
> -	rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF, 1);
> +	rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
>  	mutex_unlock(&rfkill_mutex);
>  }
>  
> @@ -610,8 +656,9 @@ static int __init rfkill_init(void)
>  	int error;
>  	int i;
>  
> -	if (rfkill_default_state != RFKILL_STATE_OFF &&
> -	    rfkill_default_state != RFKILL_STATE_ON)
> +	/* RFKILL_STATE_HARD_BLOCKED is illegal here... */
> +	if (rfkill_default_state != RFKILL_STATE_SOFT_BLOCKED &&
> +	    rfkill_default_state != RFKILL_STATE_UNBLOCKED)
>  		return -EINVAL;
>  
>  	for (i = 0; i < ARRAY_SIZE(rfkill_states); i++)


--
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