提交 5005657c 编写于 作者: H Henrique de Moraes Holschuh 提交者: John W. Linville

rfkill: rename the rfkill_state states and add block-locked state

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: NHenrique de Moraes Holschuh <hmh@hmh.eng.br>
Acked-by: NIvo van Doorn <IvDoorn@gmail.com>
Signed-off-by: NJohn W. Linville <linville@tuxdriver.com>
上级 dc288520
...@@ -60,9 +60,20 @@ The second option provides an rfkill input handler. This handler will listen to ...@@ -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 all rfkill key events and will toggle the radio accordingly. With this option
enabled userspace could either do nothing or simply perform monitoring tasks. 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 When a rfkill switch is in the RFKILL_STATE_UNBLOCKED, the wireless transmitter
TX circuit for example) is *enabled*. When the rfkill switch is in the (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. 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 Full rfkill functionality requires two different subsystems to cooperate: the
input layer and the rfkill class. The input layer issues *commands* to 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): ...@@ -122,10 +133,10 @@ Userspace input handlers (uevents) or kernel input handlers (rfkill-input):
action). action).
* rfkill-input implements EPO by handling EV_SW SW_RFKILL_ALL 0 * rfkill-input implements EPO by handling EV_SW SW_RFKILL_ALL 0
(power off all transmitters) in a special way: it ignores any (power off all transmitters) in a special way: it ignores any
overrides and local state cache and forces all transmitters to overrides and local state cache and forces all transmitters to the
the OFF state (including those which are already supposed to be RFKILL_STATE_SOFT_BLOCKED state (including those which are already
OFF). Note that the opposite event (power on all transmitters) supposed to be BLOCKED). Note that the opposite event (power on all
is handled normally. transmitters) is handled normally.
Userspace uevent handler or kernel platform-specific drivers hooked to the Userspace uevent handler or kernel platform-specific drivers hooked to the
rfkill notifier chain: rfkill notifier chain:
...@@ -284,6 +295,19 @@ You should: ...@@ -284,6 +295,19 @@ You should:
YOU CAN ACCESS state DIRECTLY) YOU CAN ACCESS state DIRECTLY)
- rfkill_register() - 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. 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. ...@@ -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 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 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: The following sysfs entries will be created:
name: Name assigned by driver to this key (interface or driver name). name: Name assigned by driver to this key (interface or driver name).
type: Name of the key type ("wlan", "bluetooth", etc). 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 claim: 1: Userspace handles events, 0: Kernel handles events
Both the "state" and "claim" entries are also writable. For the "state" entry Both the "state" and "claim" entries are also writable. For the "state" entry
......
...@@ -46,16 +46,25 @@ enum rfkill_type { ...@@ -46,16 +46,25 @@ enum rfkill_type {
}; };
enum rfkill_state { enum rfkill_state {
RFKILL_STATE_OFF = 0, /* Radio output blocked */ RFKILL_STATE_SOFT_BLOCKED = 0, /* Radio output blocked */
RFKILL_STATE_ON = 1, /* Radio output active */ 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. * struct rfkill - rfkill control structure.
* @name: Name of the switch. * @name: Name of the switch.
* @type: Radio type which the button controls, the value stored * @type: Radio type which the button controls, the value stored
* here should be a value from enum rfkill_type. * 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 * @user_claim_unsupported: Whether the hardware supports exclusive
* RF-kill control by userspace. Set this before registering. * RF-kill control by userspace. Set this before registering.
* @user_claim: Set when the switch is controlled exlusively by userspace. * @user_claim: Set when the switch is controlled exlusively by userspace.
...@@ -63,8 +72,12 @@ enum rfkill_state { ...@@ -63,8 +72,12 @@ enum rfkill_state {
* @data: Pointer to the RF button drivers private data which will be * @data: Pointer to the RF button drivers private data which will be
* passed along when toggling radio state. * passed along when toggling radio state.
* @toggle_radio(): Mandatory handler to control state of the radio. * @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, * @get_state(): handler to read current radio state from hardware,
* may be called from atomic context, should return 0 on success. * 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. * @led_trigger: A LED trigger for this button's LED.
* @dev: Device structure integrating the switch into device tree. * @dev: Device structure integrating the switch into device tree.
* @node: Used to place switch into list of all switches known to the * @node: Used to place switch into list of all switches known to the
...@@ -102,6 +115,19 @@ void rfkill_unregister(struct rfkill *rfkill); ...@@ -102,6 +115,19 @@ void rfkill_unregister(struct rfkill *rfkill);
int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state); 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. * 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 * This function might return a NULL pointer if registering of the
......
...@@ -84,7 +84,8 @@ static void rfkill_schedule_toggle(struct rfkill_task *task) ...@@ -84,7 +84,8 @@ static void rfkill_schedule_toggle(struct rfkill_task *task)
spin_lock_irqsave(&task->lock, flags); spin_lock_irqsave(&task->lock, flags);
if (time_after(jiffies, task->last + msecs_to_jiffies(200))) { 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; task->last = jiffies;
schedule_work(&task->work); schedule_work(&task->work);
} }
...@@ -99,7 +100,7 @@ static void rfkill_schedule_toggle(struct rfkill_task *task) ...@@ -99,7 +100,7 @@ static void rfkill_schedule_toggle(struct rfkill_task *task)
.type = t, \ .type = t, \
.mutex = __MUTEX_INITIALIZER(n.mutex), \ .mutex = __MUTEX_INITIALIZER(n.mutex), \
.lock = __SPIN_LOCK_UNLOCKED(n.lock), \ .lock = __SPIN_LOCK_UNLOCKED(n.lock), \
.desired_state = RFKILL_STATE_ON, \ .desired_state = RFKILL_STATE_UNBLOCKED, \
} }
static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN); 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,15 +136,15 @@ static void rfkill_event(struct input_handle *handle, unsigned int type,
/* handle EPO (emergency power off) through shortcut */ /* handle EPO (emergency power off) through shortcut */
if (data) { if (data) {
rfkill_schedule_set(&rfkill_wwan, rfkill_schedule_set(&rfkill_wwan,
RFKILL_STATE_ON); RFKILL_STATE_UNBLOCKED);
rfkill_schedule_set(&rfkill_wimax, rfkill_schedule_set(&rfkill_wimax,
RFKILL_STATE_ON); RFKILL_STATE_UNBLOCKED);
rfkill_schedule_set(&rfkill_uwb, rfkill_schedule_set(&rfkill_uwb,
RFKILL_STATE_ON); RFKILL_STATE_UNBLOCKED);
rfkill_schedule_set(&rfkill_bt, rfkill_schedule_set(&rfkill_bt,
RFKILL_STATE_ON); RFKILL_STATE_UNBLOCKED);
rfkill_schedule_set(&rfkill_wlan, rfkill_schedule_set(&rfkill_wlan,
RFKILL_STATE_ON); RFKILL_STATE_UNBLOCKED);
} else } else
rfkill_schedule_epo(); rfkill_schedule_epo();
break; break;
......
...@@ -39,7 +39,7 @@ MODULE_LICENSE("GPL"); ...@@ -39,7 +39,7 @@ MODULE_LICENSE("GPL");
static LIST_HEAD(rfkill_list); /* list of registered rf switches */ static LIST_HEAD(rfkill_list); /* list of registered rf switches */
static DEFINE_MUTEX(rfkill_mutex); 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_param_named(default_state, rfkill_default_state, uint, 0444);
MODULE_PARM_DESC(default_state, MODULE_PARM_DESC(default_state,
"Default initial state for all radio types, 0 = radio off"); "Default initial state for all radio types, 0 = radio off");
...@@ -98,7 +98,7 @@ static void rfkill_led_trigger(struct rfkill *rfkill, ...@@ -98,7 +98,7 @@ static void rfkill_led_trigger(struct rfkill *rfkill,
if (!led->name) if (!led->name)
return; return;
if (state == RFKILL_STATE_OFF) if (state != RFKILL_STATE_UNBLOCKED)
led_trigger_event(led, LED_OFF); led_trigger_event(led, LED_OFF);
else else
led_trigger_event(led, LED_FULL); led_trigger_event(led, LED_FULL);
...@@ -128,6 +128,28 @@ static void update_rfkill_state(struct rfkill *rfkill) ...@@ -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, static int rfkill_toggle_radio(struct rfkill *rfkill,
enum rfkill_state state, enum rfkill_state state,
int force) int force)
...@@ -141,9 +163,28 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, ...@@ -141,9 +163,28 @@ static int rfkill_toggle_radio(struct rfkill *rfkill,
!rfkill->get_state(rfkill->data, &newstate)) !rfkill->get_state(rfkill->data, &newstate))
rfkill->state = 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) { if (force || state != rfkill->state) {
retval = rfkill->toggle_radio(rfkill->data, 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; rfkill->state = state;
} }
...@@ -184,7 +225,7 @@ EXPORT_SYMBOL(rfkill_switch_all); ...@@ -184,7 +225,7 @@ EXPORT_SYMBOL(rfkill_switch_all);
/** /**
* rfkill_epo - emergency power off all transmitters * 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. * everything in its path but rfkill_mutex.
*/ */
void rfkill_epo(void) void rfkill_epo(void)
...@@ -193,7 +234,7 @@ void rfkill_epo(void) ...@@ -193,7 +234,7 @@ void rfkill_epo(void)
mutex_lock(&rfkill_mutex); mutex_lock(&rfkill_mutex);
list_for_each_entry(rfkill, &rfkill_list, node) { 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); mutex_unlock(&rfkill_mutex);
} }
...@@ -215,8 +256,9 @@ int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) ...@@ -215,8 +256,9 @@ int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state)
{ {
enum rfkill_state oldstate; enum rfkill_state oldstate;
if (state != RFKILL_STATE_OFF && if (state != RFKILL_STATE_SOFT_BLOCKED &&
state != RFKILL_STATE_ON) state != RFKILL_STATE_UNBLOCKED &&
state != RFKILL_STATE_HARD_BLOCKED)
return -EINVAL; return -EINVAL;
mutex_lock(&rfkill->mutex); mutex_lock(&rfkill->mutex);
...@@ -290,11 +332,14 @@ static ssize_t rfkill_state_store(struct device *dev, ...@@ -290,11 +332,14 @@ static ssize_t rfkill_state_store(struct device *dev,
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN))
return -EPERM; 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)) if (mutex_lock_interruptible(&rfkill->mutex))
return -ERESTARTSYS; return -ERESTARTSYS;
error = rfkill_toggle_radio(rfkill, error = rfkill_toggle_radio(rfkill, state, 0);
state ? RFKILL_STATE_ON : RFKILL_STATE_OFF,
0);
mutex_unlock(&rfkill->mutex); mutex_unlock(&rfkill->mutex);
return error ? error : count; return error ? error : count;
...@@ -373,7 +418,8 @@ static int rfkill_suspend(struct device *dev, pm_message_t state) ...@@ -373,7 +418,8 @@ static int rfkill_suspend(struct device *dev, pm_message_t state)
update_rfkill_state(rfkill); update_rfkill_state(rfkill);
mutex_lock(&rfkill->mutex); 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); mutex_unlock(&rfkill->mutex);
} }
...@@ -470,7 +516,7 @@ static void rfkill_remove_switch(struct rfkill *rfkill) ...@@ -470,7 +516,7 @@ static void rfkill_remove_switch(struct rfkill *rfkill)
{ {
mutex_lock(&rfkill_mutex); mutex_lock(&rfkill_mutex);
list_del_init(&rfkill->node); 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); mutex_unlock(&rfkill_mutex);
} }
...@@ -610,8 +656,9 @@ static int __init rfkill_init(void) ...@@ -610,8 +656,9 @@ static int __init rfkill_init(void)
int error; int error;
int i; int i;
if (rfkill_default_state != RFKILL_STATE_OFF && /* RFKILL_STATE_HARD_BLOCKED is illegal here... */
rfkill_default_state != RFKILL_STATE_ON) if (rfkill_default_state != RFKILL_STATE_SOFT_BLOCKED &&
rfkill_default_state != RFKILL_STATE_UNBLOCKED)
return -EINVAL; return -EINVAL;
for (i = 0; i < ARRAY_SIZE(rfkill_states); i++) for (i = 0; i < ARRAY_SIZE(rfkill_states); i++)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册