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
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:
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.
===============================================================================
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
};
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.
* @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
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.
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);
}
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);
/* 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;
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");
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);
}
}
+/**
+ * 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)
!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;
}
/**
* 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)
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);
}
{
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);
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;
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);
}
{
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);
}
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++)