linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 2/2] rfkill: improve documentation for kernel drivers
  2008-06-22 15:51 [RFC] rfkill rfkill_state enhancements and new docs Henrique de Moraes Holschuh
@ 2008-06-22 15:51 ` Henrique de Moraes Holschuh
  0 siblings, 0 replies; 10+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-06-22 15:51 UTC (permalink / raw)
  To: linux-wireless
  Cc: Ivo van Doorn, Matthew Garrett, Dan Williams, Thomas Renninger,
	Henrique de Moraes Holschuh, Ivo van Doorn

Improve the documentation of how to use the rfkill class in kernel drivers,
based on the doubts that came up in a thread in linux-wireless.

Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Ivo van Doorn <IvDoorn@gmail.com>
---
 Documentation/rfkill.txt |  262 +++++++++++++++++++++++++++++++++++-----------
 1 files changed, 203 insertions(+), 59 deletions(-)

diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt
index 1517ad9..b117c75 100644
--- a/Documentation/rfkill.txt
+++ b/Documentation/rfkill.txt
@@ -4,6 +4,9 @@ rfkill - RF switch subsystem support
 1 Introduction
 2 Implementation details
 3 Kernel driver guidelines
+3.1 wireless device drivers
+3.2 platform/switch drivers
+3.3 input device drivers
 4 Kernel API
 5 Userspace support
 
@@ -14,9 +17,14 @@ The rfkill switch subsystem exists to add a generic interface to circuitry that
 can enable or disable the signal output of a wireless *transmitter* of any
 type.  By far, the most common use is to disable radio-frequency transmitters.
 
-The rfkill switch subsystem offers support for keys and switches often found on
-laptops to enable wireless devices like WiFi and Bluetooth to actually perform
-an action.
+Note that disabling the signal output means that the the transmitter is to be
+made to not emit any energy when "blocked".  rfkill is not about blocking data
+transmissions, it is about blocking energy emission.
+
+The rfkill subsystem offers support for keys and switches often found on
+laptops to enable wireless devices like WiFi and Bluetooth, so that these keys
+and switches actually perform an action in all wireles devices of a given type
+attached to the system.
 
 The buttons to enable and disable the wireless transmitters are important in
 situations where the user is for example using his laptop on a location where
@@ -30,39 +38,79 @@ take over the task to handle the key events.
 ===============================================================================
 2: Implementation details
 
+The rfkill subsystem is composed of various components: the rfkill class, the
+rfkill-input module (an input layer handler), and some specific input layer
+events.
+
 The rfkill class provides kernel drivers with an interface that allows them to
 know when they should enable or disable a wireless network device transmitter.
+This is enabled by the CONFIG_RFKILL Kconfig option.
+
+The rfkill class support makes sure userspace will be notified of all state
+changes on rfkill devices through uevents.  It provides a notification chain
+for interested parties in the kernel to also get notified of rfkill state
+changes in other drivers.  It creates several sysfs entries which can be used
+by userspace.  See section "Userspace support".
 
 The rfkill-input module provides the kernel with the ability to implement
 a basic response when the user presses a key or button (or toggles a switch)
 related to rfkill functionality.  This is optional, and can also be done in
-userspace.
+userspace.  It is enabled by the CONFIG_RFKILL_INPUT Kconfig option.
+
+rfkill-input is a rfkill-related events input layer handler.  This handler will
+listen to all rfkill key events and will change the rfkill state of the
+wireless devices accordingly.  With this option enabled userspace could either
+do nothing or simply perform monitoring tasks.
 
 The rfkill-input module also provides EPO (emergency power-off) functionality
 for all wireless transmitters.  This function cannot be overriden, and it is
-always active.  rfkill EPO is related to *_RFKILL_ALL input events.
+always active.  rfkill EPO is related to *_RFKILL_ALL input layer events.
+
+
+Important terms for the rfkill subsystem:
+
+In order to avoid confusion, we avoid the term "switch" in rfkill when it is
+refering to an electronic control circuit that enables or disables a
+transmitter.  We reserve it for the physical device a human manipulates
+(which is an input device, by the way):
+
+rfkill switch:
+
+	A physical device a human manipulates.  Its state can be perceived by
+	the kernel either directly (through a GPIO pin, ACPI GPE) or by its
+	effect on a rfkill line of a wireless device.
+
+rfkill controller:
 
-All state changes on rfkill devices are propagated by the rfkill class to a
-notification chain and also to userspace through uevents.
+	A hardware circuit that controls the state of a rfkill line, which a
+	kernel driver can interact with *to modify* that state (i.e. it has
+	either write-only or read/write access).
 
-The system inside the kernel has been split into 2 separate sections:
-	1 - RFKILL
-	2 - RFKILL_INPUT
+rfkill line:
 
-The first option enables rfkill support and will make sure userspace will be
-notified of any events through uevents.  It provides a notification chain for
-interested parties in the kernel to also get notified of rfkill state changes
-in other drivers.  It creates several sysfs entries which can be used by
-userspace.  See section "Userspace support".
+	An input channel (hardware or software) of a wireless device, which
+	causes a wireless transmitter to stop emitting energy (BLOCK) when it
+	is active.  Point of view is extremely important here: rfkill lines are
+	always seen from the PoV of a wireless device (and its driver).
 
-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.
+soft rfkill line/software rfkill line:
 
-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.
+	A rfkill line the wireless device driver can directly change the state
+	of.  Related to rfkill_state RFKILL_STATE_SOFT_BLOCKED.
+
+hard rfkill line/hardware rfkill line:
+
+	A rfkill line that works fully in hardware or firmware, and that cannot
+	be overriden by the kernel driver.  The hardware device or the firmware
+	just exports its status to the driver, but it is read-only.  Related to
+	rfkill_state RFKILL_STATE_HARD_BLOCKED.
+
+The enum rfkill_state describes the rfkill state of a transmitter:
+
+When a rfkill line or rfkill controller is in the RFKILL_STATE_UNBLOCKED state,
+the wireless transmitter (radio TX circuit for example) is *enabled*.  When the
+it 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()
@@ -91,12 +139,12 @@ Kernel Input layer:
 	used to issue *commands* for the system to change behaviour, and these
 	commands may or may not be carried out by some kernel driver or
 	userspace application.  It follows that doing user feedback based only
-	on input events is broken, there is no guarantee that an input event
+	on input events is broken, as there is no guarantee that an input event
 	will be acted upon.
 
 	Most wireless communication device drivers implementing rfkill
 	functionality MUST NOT generate these events, and have no reason to
-	register themselves with the input layer.  This is a common
+	register themselves with the input layer.  Doing otherwise is a common
 	misconception.  There is an API to propagate rfkill status change
 	information, and it is NOT the input layer.
 
@@ -116,11 +164,22 @@ rfkill class:
 
 	THE RFKILL CLASS NEVER ISSUES INPUT EVENTS.  THE RFKILL CLASS DOES
 	NOT LISTEN TO INPUT EVENTS.  NO DRIVER USING THE RFKILL CLASS SHALL
-	EVER LISTEN TO, OR ACT ON RFKILL INPUT EVENTS.
+	EVER LISTEN TO, OR ACT ON RFKILL INPUT EVENTS.  Doing otherwise is
+	a layering violation.
 
 	Most wireless data communication drivers in the kernel have just to
 	implement the rfkill class API to work properly.  Interfacing to the
-	input layer is not often required (and is very often a *bug*).
+	input layer is not often required (and is very often a *bug*) on
+	wireless drivers.
+
+	Platform drivers often have to attach to the input layer to *issue*
+	(but never to listen to) rfkill events for rfkill switches, and also to
+	the rfkill class to export a control interface for the platform rfkill
+	controllers to the rfkill subsystem.  This does NOT mean the rfkill
+	switch is attached to a rfkill class (doing so is almost always wrong).
+	It just means the same kernel module is the driver for different
+	devices (rfkill switches and rfkill controllers).
+
 
 Userspace input handlers (uevents) or kernel input handlers (rfkill-input):
 
@@ -152,24 +211,34 @@ rfkill notifier chain:
 ===============================================================================
 3: Kernel driver guidelines
 
+Remember: point-of-view is everything for a driver that connects to the rfkill
+subsystem.  All the details below must be measured/perceived from the point of
+view of the specific driver being modified.
+
 The first thing one needs to know is whether his driver should be talking to
-the rfkill class or to the input layer.
+the rfkill class or to the input layer.  In rare cases (platform drivers), it
+could happen that you need to do both, as platform drivers often handle a
+variety of devices in the same driver.
 
-Do not mistake input devices for rfkill devices.  The only type of "rfkill
+Do not mistake input devices for rfkill controllers.  The only type of "rfkill
 switch" device that is to be registered with the rfkill class are those
 directly controlling the circuits that cause a wireless transmitter to stop
-working (or the software equivalent of them).  Every other kind of "rfkill
-switch" is just an input device and MUST NOT be registered with the rfkill
-class.
+working (or the software equivalent of them), i.e. what we call a rfkill
+controller.  Every other kind of "rfkill switch" is just an input device and
+MUST NOT be registered with the rfkill class.
 
 A driver should register a device with the rfkill class when ALL of the
-following conditions are met:
+following conditions are met (they define a rfkill controller):
 
 1. The device is/controls a data communications wireless transmitter;
 
 2. The kernel can interact with the hardware/firmware to CHANGE the wireless
    transmitter state (block/unblock TX operation);
 
+3. The transmitter can be made to not emit any energy when "blocked":
+   rfkill is not about blocking data transmissions, it is about blocking
+   energy emission;
+
 A driver should register a device with the input subsystem to issue
 rfkill-related events (KEY_WLAN, KEY_BLUETOOTH, KEY_WWAN, KEY_WIMAX,
 SW_RFKILL_ALL, etc) when ALL of the folowing conditions are met:
@@ -185,9 +254,7 @@ SW_RFKILL_ALL, etc) when ALL of the folowing conditions are met:
 2. It is NOT slaved to another device, i.e. there is no other device that
    issues rfkill-related input events in preference to this one.
 
-   Typically, the ACPI "radio kill" switch of a laptop is the master input
-   device to issue rfkill events, and, e.g., the WLAN card is just a slave
-   device that gets disabled by its hardware radio-kill input pin.
+   Please refer to the corner cases and examples section for more details.
 
 When in doubt, do not issue input events.  For drivers that should generate
 input events in some platforms, but not in others (e.g. b43), the best solution
@@ -251,26 +318,106 @@ Add the SW_* events you need for switches, do NOT try to emulate a button using
 KEY_* events just because there is no such SW_* event yet.  Do NOT try to use,
 for example, KEY_BLUETOOTH when you should be using SW_BLUETOOTH instead.
 
-2. Input device switches (sources of EV_SW events) DO store their current
-state, and that state CAN be queried from userspace through IOCTLs.  There is
-no sysfs interface for this, but that doesn't mean you should break things
-trying to hook it to the rfkill class to get a sysfs interface :-)
+2. Input device switches (sources of EV_SW events) DO store their current state
+(so you *must* initialize it by issuing a gratuitous input layer event on
+driver startup and also when resuming from sleep), and that state CAN be
+queried from userspace through IOCTLs.  There is no sysfs interface for this,
+but that doesn't mean you should break things trying to hook it to the rfkill
+class to get a sysfs interface :-)
+
+3. Do not issue *_RFKILL_ALL events by default, unless you are sure it is the
+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).
+
 
-3. Do not issue *_RFKILL_ALL events, unless you are sure it is the 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 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).
+3.1 Guidelines for wireless device drivers
+------------------------------------------
 
+1. Each independent transmitter in a wireless device (usually there is only one
+transmitter per device) should have a SINGLE rfkill class attached to it.
+
+2. If the device does not have any sort of hardware assistance to allow the
+driver to rfkill the device, the driver should emulate it by taking all actions
+required to silence the transmitter.
+
+3. If it is impossible to silence the transmitter (i.e. it still emits energy,
+even if it is just in brief pulses, when there is no data to transmit and there
+is no hardware support to turn it off) do NOT lie to the users.  Do not attach
+it to a rfkill class.  The rfkill subsystem does not deal with data
+transmission, it deals with energy emission.  If the transmitter is emitting
+energy, it is not blocked in rfkill terms.
+
+4. It doesn't matter if the device has multiple rfkill input lines affecting
+the same transmitter, their combined state is to be exported as a single state
+per transmitter (see rule 1).
+
+This rule exists because users of the rfkill subsystem expect to get (and set,
+when possible) the overall transmitter rfkill state, not of a particular rfkill
+line.
+
+Example of a WLAN wireless driver connected to the rfkill subsystem:
+--------------------------------------------------------------------
+
+A certain WLAN card has one input pin that causes it to block the transmitter
+and makes the status of that input pin available (only for reading!) to the
+kernel driver.  This is a hard rfkill input line (it cannot be overriden by the
+kernel driver).
+
+The card also has one PCI register that, if manipulated by the driver, causes
+it to block the transmitter.  This is a soft rfkill input line.
+
+It has also a thermal protection circuitry that shuts down its transmitter if
+the card overheats, and makes the status of that protection available (only for
+reading!) to the kernel driver.  This is also a hard rfkill input line.
+
+If either one of these rfkill lines are active, the transmitter is blocked by
+the hardware and forced offline.
+
+The driver should allocate and attach to its struct device *ONE* instance of
+the rfkill class (there is only one transmitter).
+
+It can implement the get_state() hook, and return RFKILL_STATE_HARD_BLOCKED if
+either one of its two hard rfkill input lines are active.  If the two hard
+rfkill lines are inactive, it must return RFKILL_STATE_SOFT_BLOCKED if its soft
+rfkill input line is active.  Only if none of the rfkill input lines are
+active, will it return RFKILL_STATE_UNBLOCKED.
+
+If it doesn't implement the get_state() hook, it must make sure that its calls
+to rfkill_force_state() are enough to keep the status always up-to-date, and it
+must do a rfkill_force_state() on resume from sleep.
+
+Every time the driver gets a notification from the card that one of its rfkill
+lines changed state (polling might be needed on badly designed cards that don't
+generate interrupts for such events), it recomputes the rfkill state as per
+above, and calls rfkill_force_state() to update it.
+
+The driver should implement the toggle_radio() hook, that:
+
+1. Returns an error if one of the hardware rfkill lines are active, and the
+caller asked for RFKILL_STATE_UNBLOCKED.
+
+2. Activates the soft rfkill line if the caller asked for state
+RFKILL_STATE_SOFT_BLOCKED.  Note that if any of the hardware rfkill lines are
+active, the rfkill state will remain at RFKILL_STATE_HARD_BLOCKED, but no error
+is returned.
+
+3. Deactivates the soft rfkill line if none of the hardware rfkill lines are
+active and the caller asked for RFKILL_STATE_UNBLOCKED.
+
+The driver is to call rfkill_force_state() every time its rfkill state changes
+outside of the toggle_radio() code flow.
 
 ===============================================================================
 4: Kernel API
 
 To build a driver with rfkill subsystem support, the driver should depend on
-the Kconfig symbol RFKILL; it should _not_ depend on RKFILL_INPUT.
+(or select) the Kconfig symbol RFKILL; it should _not_ depend on RKFILL_INPUT.
 
 The hardware the driver talks to may be write-only (where the current state
 of the hardware is unknown), or read-write (where the hardware can be queried
@@ -335,10 +482,10 @@ is *absolute*; do NOT violate it.
 ******IMPORTANT******
 
 Userspace must not assume it is the only source of control for rfkill switches.
-Their state CAN and WILL change on its own, due to firmware actions, direct
-user actions, and the rfkill-input EPO override for *_RFKILL_ALL.
+Their state CAN and WILL change due to firmware actions, direct user actions,
+and the rfkill-input EPO override for *_RFKILL_ALL.
 
-When rfkill-input is not active, userspace must initiate an rfkill status
+When rfkill-input is not active, userspace must initiate a rfkill status
 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
@@ -351,19 +498,16 @@ The following sysfs entries will be created:
 	type: Name of the key type ("wlan", "bluetooth", etc).
 	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).
+			transmitter is forced off, but one can override it
+			by a write to the state attribute;
 		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).
+			(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.
+			the driver's control.  One 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
-- 
1.5.5.4


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [GIT PATCH] rfkill rework batch 2 (new)
@ 2008-06-23 20:46 Henrique de Moraes Holschuh
  2008-06-23 20:46 ` [PATCH 1/2] rfkill: rename the rfkill_state states and add block-locked state Henrique de Moraes Holschuh
  2008-06-23 20:46 ` [PATCH 2/2] rfkill: improve documentation for kernel drivers Henrique de Moraes Holschuh
  0 siblings, 2 replies; 10+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-06-23 20:46 UTC (permalink / raw)
  To: John Linville
  Cc: linux-wireless, Ivo van Doorn, Matthew Garrett, Dan Williams,
	Thomas Renninger, Fabien Crespel, Dmitry Torokhov


Here are two extra patches.  I sent a RFC version of them earlier
(subject rfkill rfkill_state enhancements and new docs).

Patch 1 has been tested, some issues have been found and fixed, and now
it seems to work fine.  Patch 2 has a lot of extra documentation that
further explains how rfkill works and how to properly use it.

Please comment.  They apply on top of the "rfkill rework for 2.6.27
(v3)" batch I just sent.

Ivo, can I get an ACK for them? :-)

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/2] rfkill: rename the rfkill_state states and add block-locked state
  2008-06-23 20:46 [GIT PATCH] rfkill rework batch 2 (new) Henrique de Moraes Holschuh
@ 2008-06-23 20:46 ` Henrique de Moraes Holschuh
  2008-06-23 20:55   ` Ivo van Doorn
  2008-06-27 18:23   ` Dan Williams
  2008-06-23 20:46 ` [PATCH 2/2] rfkill: improve documentation for kernel drivers Henrique de Moraes Holschuh
  1 sibling, 2 replies; 10+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-06-23 20:46 UTC (permalink / raw)
  To: John Linville
  Cc: linux-wireless, Ivo van Doorn, Matthew Garrett, Dan Williams,
	Thomas Renninger, Fabien Crespel, Dmitry Torokhov,
	Henrique de Moraes Holschuh

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@hmh.eng.br>
Cc: Ivo van Doorn <IvDoorn@gmail.com>
---
 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++)
-- 
1.5.5.4


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/2] rfkill: improve documentation for kernel drivers
  2008-06-23 20:46 [GIT PATCH] rfkill rework batch 2 (new) Henrique de Moraes Holschuh
  2008-06-23 20:46 ` [PATCH 1/2] rfkill: rename the rfkill_state states and add block-locked state Henrique de Moraes Holschuh
@ 2008-06-23 20:46 ` Henrique de Moraes Holschuh
  2008-06-23 20:58   ` Ivo van Doorn
  1 sibling, 1 reply; 10+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-06-23 20:46 UTC (permalink / raw)
  To: John Linville
  Cc: linux-wireless, Ivo van Doorn, Matthew Garrett, Dan Williams,
	Thomas Renninger, Fabien Crespel, Dmitry Torokhov,
	Henrique de Moraes Holschuh

Improve the documentation of how to use the rfkill class in kernel drivers,
based on the doubts that came up in a thread in linux-wireless.

Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Ivo van Doorn <IvDoorn@gmail.com>
---
 Documentation/rfkill.txt |  261 +++++++++++++++++++++++++++++++++++-----------
 1 files changed, 201 insertions(+), 60 deletions(-)

diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt
index 5316cea..0843ed0 100644
--- a/Documentation/rfkill.txt
+++ b/Documentation/rfkill.txt
@@ -4,6 +4,9 @@ rfkill - RF switch subsystem support
 1 Introduction
 2 Implementation details
 3 Kernel driver guidelines
+3.1 wireless device drivers
+3.2 platform/switch drivers
+3.3 input device drivers
 4 Kernel API
 5 Userspace support
 
@@ -14,9 +17,14 @@ The rfkill switch subsystem exists to add a generic interface to circuitry that
 can enable or disable the signal output of a wireless *transmitter* of any
 type.  By far, the most common use is to disable radio-frequency transmitters.
 
-The rfkill switch subsystem offers support for keys and switches often found on
-laptops to enable wireless devices like WiFi and Bluetooth to actually perform
-an action.
+Note that disabling the signal output means that the the transmitter is to be
+made to not emit any energy when "blocked".  rfkill is not about blocking data
+transmissions, it is about blocking energy emission.
+
+The rfkill subsystem offers support for keys and switches often found on
+laptops to enable wireless devices like WiFi and Bluetooth, so that these keys
+and switches actually perform an action in all wireless devices of a given type
+attached to the system.
 
 The buttons to enable and disable the wireless transmitters are important in
 situations where the user is for example using his laptop on a location where
@@ -30,40 +38,81 @@ take over the task to handle the key events.
 ===============================================================================
 2: Implementation details
 
+The rfkill subsystem is composed of various components: the rfkill class, the
+rfkill-input module (an input layer handler), and some specific input layer
+events.
+
 The rfkill class provides kernel drivers with an interface that allows them to
 know when they should enable or disable a wireless network device transmitter.
+This is enabled by the CONFIG_RFKILL Kconfig option.
+
+The rfkill class support makes sure userspace will be notified of all state
+changes on rfkill devices through uevents.  It provides a notification chain
+for interested parties in the kernel to also get notified of rfkill state
+changes in other drivers.  It creates several sysfs entries which can be used
+by userspace.  See section "Userspace support".
 
 The rfkill-input module provides the kernel with the ability to implement a
 basic response when the user presses a key or button (or toggles a switch)
 related to rfkill functionality.  It is an in-kernel implementation of default
 policy of reacting to rfkill-related input events and neither mandatory nor
-required for wireless drivers to operate.
+required for wireless drivers to operate.  It is enabled by the
+CONFIG_RFKILL_INPUT Kconfig option.
+
+rfkill-input is a rfkill-related events input layer handler.  This handler will
+listen to all rfkill key events and will change the rfkill state of the
+wireless devices accordingly.  With this option enabled userspace could either
+do nothing or simply perform monitoring tasks.
 
 The rfkill-input module also provides EPO (emergency power-off) functionality
-for all wireless transmitters.  This function cannot be overriden, and it is
-always active.  rfkill EPO is related to *_RFKILL_ALL input events.
+for all wireless transmitters.  This function cannot be overridden, and it is
+always active.  rfkill EPO is related to *_RFKILL_ALL input layer events.
+
+
+Important terms for the rfkill subsystem:
+
+In order to avoid confusion, we avoid the term "switch" in rfkill when it is
+referring to an electronic control circuit that enables or disables a
+transmitter.  We reserve it for the physical device a human manipulates
+(which is an input device, by the way):
+
+rfkill switch:
+
+	A physical device a human manipulates.  Its state can be perceived by
+	the kernel either directly (through a GPIO pin, ACPI GPE) or by its
+	effect on a rfkill line of a wireless device.
+
+rfkill controller:
 
-All state changes on rfkill devices are propagated by the rfkill class to a
-notification chain and also to userspace through uevents.
+	A hardware circuit that controls the state of a rfkill line, which a
+	kernel driver can interact with *to modify* that state (i.e. it has
+	either write-only or read/write access).
 
-The system inside the kernel has been split into 2 separate sections:
-	1 - RFKILL
-	2 - RFKILL_INPUT
+rfkill line:
 
-The first option enables rfkill support and will make sure userspace will be
-notified of any events through uevents.  It provides a notification chain for
-interested parties in the kernel to also get notified of rfkill state changes
-in other drivers.  It creates several sysfs entries which can be used by
-userspace.  See section "Userspace support".
+	An input channel (hardware or software) of a wireless device, which
+	causes a wireless transmitter to stop emitting energy (BLOCK) when it
+	is active.  Point of view is extremely important here: rfkill lines are
+	always seen from the PoV of a wireless device (and its driver).
 
-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.
+soft rfkill line/software rfkill line:
 
-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.
+	A rfkill line the wireless device driver can directly change the state
+	of.  Related to rfkill_state RFKILL_STATE_SOFT_BLOCKED.
+
+hard rfkill line/hardware rfkill line:
+
+	A rfkill line that works fully in hardware or firmware, and that cannot
+	be overridden by the kernel driver.  The hardware device or the
+	firmware just exports its status to the driver, but it is read-only.
+	Related to rfkill_state RFKILL_STATE_HARD_BLOCKED.
+
+The enum rfkill_state describes the rfkill state of a transmitter:
+
+When a rfkill line or rfkill controller is in the RFKILL_STATE_UNBLOCKED state,
+the wireless transmitter (radio TX circuit for example) is *enabled*.  When the
+it 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()
@@ -92,12 +141,12 @@ Kernel Input layer:
 	used to issue *commands* for the system to change behaviour, and these
 	commands may or may not be carried out by some kernel driver or
 	userspace application.  It follows that doing user feedback based only
-	on input events is broken, there is no guarantee that an input event
+	on input events is broken, as there is no guarantee that an input event
 	will be acted upon.
 
 	Most wireless communication device drivers implementing rfkill
 	functionality MUST NOT generate these events, and have no reason to
-	register themselves with the input layer.  This is a common
+	register themselves with the input layer.  Doing otherwise is a common
 	misconception.  There is an API to propagate rfkill status change
 	information, and it is NOT the input layer.
 
@@ -117,11 +166,22 @@ rfkill class:
 
 	THE RFKILL CLASS NEVER ISSUES INPUT EVENTS.  THE RFKILL CLASS DOES
 	NOT LISTEN TO INPUT EVENTS.  NO DRIVER USING THE RFKILL CLASS SHALL
-	EVER LISTEN TO, OR ACT ON RFKILL INPUT EVENTS.
+	EVER LISTEN TO, OR ACT ON RFKILL INPUT EVENTS.  Doing otherwise is
+	a layering violation.
 
 	Most wireless data communication drivers in the kernel have just to
 	implement the rfkill class API to work properly.  Interfacing to the
-	input layer is not often required (and is very often a *bug*).
+	input layer is not often required (and is very often a *bug*) on
+	wireless drivers.
+
+	Platform drivers often have to attach to the input layer to *issue*
+	(but never to listen to) rfkill events for rfkill switches, and also to
+	the rfkill class to export a control interface for the platform rfkill
+	controllers to the rfkill subsystem.  This does NOT mean the rfkill
+	switch is attached to a rfkill class (doing so is almost always wrong).
+	It just means the same kernel module is the driver for different
+	devices (rfkill switches and rfkill controllers).
+
 
 Userspace input handlers (uevents) or kernel input handlers (rfkill-input):
 
@@ -153,24 +213,34 @@ rfkill notifier chain:
 ===============================================================================
 3: Kernel driver guidelines
 
+Remember: point-of-view is everything for a driver that connects to the rfkill
+subsystem.  All the details below must be measured/perceived from the point of
+view of the specific driver being modified.
+
 The first thing one needs to know is whether his driver should be talking to
-the rfkill class or to the input layer.
+the rfkill class or to the input layer.  In rare cases (platform drivers), it
+could happen that you need to do both, as platform drivers often handle a
+variety of devices in the same driver.
 
-Do not mistake input devices for rfkill devices.  The only type of "rfkill
+Do not mistake input devices for rfkill controllers.  The only type of "rfkill
 switch" device that is to be registered with the rfkill class are those
 directly controlling the circuits that cause a wireless transmitter to stop
-working (or the software equivalent of them).  Every other kind of "rfkill
-switch" is just an input device and MUST NOT be registered with the rfkill
-class.
+working (or the software equivalent of them), i.e. what we call a rfkill
+controller.  Every other kind of "rfkill switch" is just an input device and
+MUST NOT be registered with the rfkill class.
 
 A driver should register a device with the rfkill class when ALL of the
-following conditions are met:
+following conditions are met (they define a rfkill controller):
 
 1. The device is/controls a data communications wireless transmitter;
 
 2. The kernel can interact with the hardware/firmware to CHANGE the wireless
    transmitter state (block/unblock TX operation);
 
+3. The transmitter can be made to not emit any energy when "blocked":
+   rfkill is not about blocking data transmissions, it is about blocking
+   energy emission;
+
 A driver should register a device with the input subsystem to issue
 rfkill-related events (KEY_WLAN, KEY_BLUETOOTH, KEY_WWAN, KEY_WIMAX,
 SW_RFKILL_ALL, etc) when ALL of the folowing conditions are met:
@@ -186,9 +256,7 @@ SW_RFKILL_ALL, etc) when ALL of the folowing conditions are met:
 2. It is NOT slaved to another device, i.e. there is no other device that
    issues rfkill-related input events in preference to this one.
 
-   Typically, the ACPI "radio kill" switch of a laptop is the master input
-   device to issue rfkill events, and, e.g., the WLAN card is just a slave
-   device that gets disabled by its hardware radio-kill input pin.
+   Please refer to the corner cases and examples section for more details.
 
 When in doubt, do not issue input events.  For drivers that should generate
 input events in some platforms, but not in others (e.g. b43), the best solution
@@ -252,26 +320,102 @@ Add the SW_* events you need for switches, do NOT try to emulate a button using
 KEY_* events just because there is no such SW_* event yet.  Do NOT try to use,
 for example, KEY_BLUETOOTH when you should be using SW_BLUETOOTH instead.
 
-2. Input device switches (sources of EV_SW events) DO store their current
-state, and that state CAN be queried from userspace through IOCTLs.  There is
-no sysfs interface for this, but that doesn't mean you should break things
-trying to hook it to the rfkill class to get a sysfs interface :-)
+2. Input device switches (sources of EV_SW events) DO store their current state
+(so you *must* initialize it by issuing a gratuitous input layer event on
+driver start-up and also when resuming from sleep), and that state CAN be
+queried from userspace through IOCTLs.  There is no sysfs interface for this,
+but that doesn't mean you should break things trying to hook it to the rfkill
+class to get a sysfs interface :-)
+
+3. Do not issue *_RFKILL_ALL events by default, unless you are sure it is the
+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).
 
-3. Do not issue *_RFKILL_ALL events, unless you are sure it is the 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 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).
 
+3.1 Guidelines for wireless device drivers
+------------------------------------------
+
+1. Each independent transmitter in a wireless device (usually there is only one
+transmitter per device) should have a SINGLE rfkill class attached to it.
+
+2. If the device does not have any sort of hardware assistance to allow the
+driver to rfkill the device, the driver should emulate it by taking all actions
+required to silence the transmitter.
+
+3. If it is impossible to silence the transmitter (i.e. it still emits energy,
+even if it is just in brief pulses, when there is no data to transmit and there
+is no hardware support to turn it off) do NOT lie to the users.  Do not attach
+it to a rfkill class.  The rfkill subsystem does not deal with data
+transmission, it deals with energy emission.  If the transmitter is emitting
+energy, it is not blocked in rfkill terms.
+
+4. It doesn't matter if the device has multiple rfkill input lines affecting
+the same transmitter, their combined state is to be exported as a single state
+per transmitter (see rule 1).
+
+This rule exists because users of the rfkill subsystem expect to get (and set,
+when possible) the overall transmitter rfkill state, not of a particular rfkill
+line.
+
+Example of a WLAN wireless driver connected to the rfkill subsystem:
+--------------------------------------------------------------------
+
+A certain WLAN card has one input pin that causes it to block the transmitter
+and makes the status of that input pin available (only for reading!) to the
+kernel driver.  This is a hard rfkill input line (it cannot be overridden by
+the kernel driver).
+
+The card also has one PCI register that, if manipulated by the driver, causes
+it to block the transmitter.  This is a soft rfkill input line.
+
+It has also a thermal protection circuitry that shuts down its transmitter if
+the card overheats, and makes the status of that protection available (only for
+reading!) to the kernel driver.  This is also a hard rfkill input line.
+
+If either one of these rfkill lines are active, the transmitter is blocked by
+the hardware and forced offline.
+
+The driver should allocate and attach to its struct device *ONE* instance of
+the rfkill class (there is only one transmitter).
+
+It can implement the get_state() hook, and return RFKILL_STATE_HARD_BLOCKED if
+either one of its two hard rfkill input lines are active.  If the two hard
+rfkill lines are inactive, it must return RFKILL_STATE_SOFT_BLOCKED if its soft
+rfkill input line is active.  Only if none of the rfkill input lines are
+active, will it return RFKILL_STATE_UNBLOCKED.
+
+If it doesn't implement the get_state() hook, it must make sure that its calls
+to rfkill_force_state() are enough to keep the status always up-to-date, and it
+must do a rfkill_force_state() on resume from sleep.
+
+Every time the driver gets a notification from the card that one of its rfkill
+lines changed state (polling might be needed on badly designed cards that don't
+generate interrupts for such events), it recomputes the rfkill state as per
+above, and calls rfkill_force_state() to update it.
+
+The driver should implement the toggle_radio() hook, that:
+
+1. Returns an error if one of the hardware rfkill lines are active, and the
+caller asked for RFKILL_STATE_UNBLOCKED.
+
+2. Activates the soft rfkill line if the caller asked for state
+RFKILL_STATE_SOFT_BLOCKED.  It should do this even if one of the hard rfkill
+lines are active, effectively double-blocking the transmitter.
+
+3. Deactivates the soft rfkill line if none of the hardware rfkill lines are
+active and the caller asked for RFKILL_STATE_UNBLOCKED.
 
 ===============================================================================
 4: Kernel API
 
 To build a driver with rfkill subsystem support, the driver should depend on
-the Kconfig symbol RFKILL; it should _not_ depend on RKFILL_INPUT.
+(or select) the Kconfig symbol RFKILL; it should _not_ depend on RKFILL_INPUT.
 
 The hardware the driver talks to may be write-only (where the current state
 of the hardware is unknown), or read-write (where the hardware can be queried
@@ -338,10 +482,10 @@ is *absolute*; do NOT violate it.
 ******IMPORTANT******
 
 Userspace must not assume it is the only source of control for rfkill switches.
-Their state CAN and WILL change on its own, due to firmware actions, direct
-user actions, and the rfkill-input EPO override for *_RFKILL_ALL.
+Their state CAN and WILL change due to firmware actions, direct user actions,
+and the rfkill-input EPO override for *_RFKILL_ALL.
 
-When rfkill-input is not active, userspace must initiate an rfkill status
+When rfkill-input is not active, userspace must initiate a rfkill status
 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
@@ -354,19 +498,16 @@ The following sysfs entries will be created:
 	type: Name of the key type ("wlan", "bluetooth", etc).
 	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).
+			transmitter is forced off, but one can override it
+			by a write to the state attribute;
 		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).
+			(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.
+			the driver's control.  One 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
-- 
1.5.5.4


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] rfkill: rename the rfkill_state states and add block-locked state
  2008-06-23 20:46 ` [PATCH 1/2] rfkill: rename the rfkill_state states and add block-locked state Henrique de Moraes Holschuh
@ 2008-06-23 20:55   ` Ivo van Doorn
  2008-06-27 18:23   ` Dan Williams
  1 sibling, 0 replies; 10+ messages in thread
From: Ivo van Doorn @ 2008-06-23 20:55 UTC (permalink / raw)
  To: Henrique de Moraes Holschuh
  Cc: John Linville, linux-wireless, Matthew Garrett, Dan Williams,
	Thomas Renninger, Fabien Crespel, Dmitry Torokhov

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@hmh.eng.br>
> Cc: Ivo van Doorn <IvDoorn@gmail.com>

Acked-by: Ivo van Doorn <IvDoorn@gmail.com>

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



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] rfkill: improve documentation for kernel drivers
  2008-06-23 20:46 ` [PATCH 2/2] rfkill: improve documentation for kernel drivers Henrique de Moraes Holschuh
@ 2008-06-23 20:58   ` Ivo van Doorn
  0 siblings, 0 replies; 10+ messages in thread
From: Ivo van Doorn @ 2008-06-23 20:58 UTC (permalink / raw)
  To: Henrique de Moraes Holschuh
  Cc: John Linville, linux-wireless, Matthew Garrett, Dan Williams,
	Thomas Renninger, Fabien Crespel, Dmitry Torokhov

On Monday 23 June 2008, Henrique de Moraes Holschuh wrote:
> Improve the documentation of how to use the rfkill class in kernel drivers,
> based on the doubts that came up in a thread in linux-wireless.
> 
> Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
> Cc: Ivo van Doorn <IvDoorn@gmail.com>

Acked-by: Ivo van Doorn <IvDoorn@gmail.com>

> ---
>  Documentation/rfkill.txt |  261 +++++++++++++++++++++++++++++++++++-----------
>  1 files changed, 201 insertions(+), 60 deletions(-)
> 
> diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt
> index 5316cea..0843ed0 100644
> --- a/Documentation/rfkill.txt
> +++ b/Documentation/rfkill.txt
> @@ -4,6 +4,9 @@ rfkill - RF switch subsystem support
>  1 Introduction
>  2 Implementation details
>  3 Kernel driver guidelines
> +3.1 wireless device drivers
> +3.2 platform/switch drivers
> +3.3 input device drivers
>  4 Kernel API
>  5 Userspace support
>  
> @@ -14,9 +17,14 @@ The rfkill switch subsystem exists to add a generic interface to circuitry that
>  can enable or disable the signal output of a wireless *transmitter* of any
>  type.  By far, the most common use is to disable radio-frequency transmitters.
>  
> -The rfkill switch subsystem offers support for keys and switches often found on
> -laptops to enable wireless devices like WiFi and Bluetooth to actually perform
> -an action.
> +Note that disabling the signal output means that the the transmitter is to be
> +made to not emit any energy when "blocked".  rfkill is not about blocking data
> +transmissions, it is about blocking energy emission.
> +
> +The rfkill subsystem offers support for keys and switches often found on
> +laptops to enable wireless devices like WiFi and Bluetooth, so that these keys
> +and switches actually perform an action in all wireless devices of a given type
> +attached to the system.
>  
>  The buttons to enable and disable the wireless transmitters are important in
>  situations where the user is for example using his laptop on a location where
> @@ -30,40 +38,81 @@ take over the task to handle the key events.
>  ===============================================================================
>  2: Implementation details
>  
> +The rfkill subsystem is composed of various components: the rfkill class, the
> +rfkill-input module (an input layer handler), and some specific input layer
> +events.
> +
>  The rfkill class provides kernel drivers with an interface that allows them to
>  know when they should enable or disable a wireless network device transmitter.
> +This is enabled by the CONFIG_RFKILL Kconfig option.
> +
> +The rfkill class support makes sure userspace will be notified of all state
> +changes on rfkill devices through uevents.  It provides a notification chain
> +for interested parties in the kernel to also get notified of rfkill state
> +changes in other drivers.  It creates several sysfs entries which can be used
> +by userspace.  See section "Userspace support".
>  
>  The rfkill-input module provides the kernel with the ability to implement a
>  basic response when the user presses a key or button (or toggles a switch)
>  related to rfkill functionality.  It is an in-kernel implementation of default
>  policy of reacting to rfkill-related input events and neither mandatory nor
> -required for wireless drivers to operate.
> +required for wireless drivers to operate.  It is enabled by the
> +CONFIG_RFKILL_INPUT Kconfig option.
> +
> +rfkill-input is a rfkill-related events input layer handler.  This handler will
> +listen to all rfkill key events and will change the rfkill state of the
> +wireless devices accordingly.  With this option enabled userspace could either
> +do nothing or simply perform monitoring tasks.
>  
>  The rfkill-input module also provides EPO (emergency power-off) functionality
> -for all wireless transmitters.  This function cannot be overriden, and it is
> -always active.  rfkill EPO is related to *_RFKILL_ALL input events.
> +for all wireless transmitters.  This function cannot be overridden, and it is
> +always active.  rfkill EPO is related to *_RFKILL_ALL input layer events.
> +
> +
> +Important terms for the rfkill subsystem:
> +
> +In order to avoid confusion, we avoid the term "switch" in rfkill when it is
> +referring to an electronic control circuit that enables or disables a
> +transmitter.  We reserve it for the physical device a human manipulates
> +(which is an input device, by the way):
> +
> +rfkill switch:
> +
> +	A physical device a human manipulates.  Its state can be perceived by
> +	the kernel either directly (through a GPIO pin, ACPI GPE) or by its
> +	effect on a rfkill line of a wireless device.
> +
> +rfkill controller:
>  
> -All state changes on rfkill devices are propagated by the rfkill class to a
> -notification chain and also to userspace through uevents.
> +	A hardware circuit that controls the state of a rfkill line, which a
> +	kernel driver can interact with *to modify* that state (i.e. it has
> +	either write-only or read/write access).
>  
> -The system inside the kernel has been split into 2 separate sections:
> -	1 - RFKILL
> -	2 - RFKILL_INPUT
> +rfkill line:
>  
> -The first option enables rfkill support and will make sure userspace will be
> -notified of any events through uevents.  It provides a notification chain for
> -interested parties in the kernel to also get notified of rfkill state changes
> -in other drivers.  It creates several sysfs entries which can be used by
> -userspace.  See section "Userspace support".
> +	An input channel (hardware or software) of a wireless device, which
> +	causes a wireless transmitter to stop emitting energy (BLOCK) when it
> +	is active.  Point of view is extremely important here: rfkill lines are
> +	always seen from the PoV of a wireless device (and its driver).
>  
> -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.
> +soft rfkill line/software rfkill line:
>  
> -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.
> +	A rfkill line the wireless device driver can directly change the state
> +	of.  Related to rfkill_state RFKILL_STATE_SOFT_BLOCKED.
> +
> +hard rfkill line/hardware rfkill line:
> +
> +	A rfkill line that works fully in hardware or firmware, and that cannot
> +	be overridden by the kernel driver.  The hardware device or the
> +	firmware just exports its status to the driver, but it is read-only.
> +	Related to rfkill_state RFKILL_STATE_HARD_BLOCKED.
> +
> +The enum rfkill_state describes the rfkill state of a transmitter:
> +
> +When a rfkill line or rfkill controller is in the RFKILL_STATE_UNBLOCKED state,
> +the wireless transmitter (radio TX circuit for example) is *enabled*.  When the
> +it 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()
> @@ -92,12 +141,12 @@ Kernel Input layer:
>  	used to issue *commands* for the system to change behaviour, and these
>  	commands may or may not be carried out by some kernel driver or
>  	userspace application.  It follows that doing user feedback based only
> -	on input events is broken, there is no guarantee that an input event
> +	on input events is broken, as there is no guarantee that an input event
>  	will be acted upon.
>  
>  	Most wireless communication device drivers implementing rfkill
>  	functionality MUST NOT generate these events, and have no reason to
> -	register themselves with the input layer.  This is a common
> +	register themselves with the input layer.  Doing otherwise is a common
>  	misconception.  There is an API to propagate rfkill status change
>  	information, and it is NOT the input layer.
>  
> @@ -117,11 +166,22 @@ rfkill class:
>  
>  	THE RFKILL CLASS NEVER ISSUES INPUT EVENTS.  THE RFKILL CLASS DOES
>  	NOT LISTEN TO INPUT EVENTS.  NO DRIVER USING THE RFKILL CLASS SHALL
> -	EVER LISTEN TO, OR ACT ON RFKILL INPUT EVENTS.
> +	EVER LISTEN TO, OR ACT ON RFKILL INPUT EVENTS.  Doing otherwise is
> +	a layering violation.
>  
>  	Most wireless data communication drivers in the kernel have just to
>  	implement the rfkill class API to work properly.  Interfacing to the
> -	input layer is not often required (and is very often a *bug*).
> +	input layer is not often required (and is very often a *bug*) on
> +	wireless drivers.
> +
> +	Platform drivers often have to attach to the input layer to *issue*
> +	(but never to listen to) rfkill events for rfkill switches, and also to
> +	the rfkill class to export a control interface for the platform rfkill
> +	controllers to the rfkill subsystem.  This does NOT mean the rfkill
> +	switch is attached to a rfkill class (doing so is almost always wrong).
> +	It just means the same kernel module is the driver for different
> +	devices (rfkill switches and rfkill controllers).
> +
>  
>  Userspace input handlers (uevents) or kernel input handlers (rfkill-input):
>  
> @@ -153,24 +213,34 @@ rfkill notifier chain:
>  ===============================================================================
>  3: Kernel driver guidelines
>  
> +Remember: point-of-view is everything for a driver that connects to the rfkill
> +subsystem.  All the details below must be measured/perceived from the point of
> +view of the specific driver being modified.
> +
>  The first thing one needs to know is whether his driver should be talking to
> -the rfkill class or to the input layer.
> +the rfkill class or to the input layer.  In rare cases (platform drivers), it
> +could happen that you need to do both, as platform drivers often handle a
> +variety of devices in the same driver.
>  
> -Do not mistake input devices for rfkill devices.  The only type of "rfkill
> +Do not mistake input devices for rfkill controllers.  The only type of "rfkill
>  switch" device that is to be registered with the rfkill class are those
>  directly controlling the circuits that cause a wireless transmitter to stop
> -working (or the software equivalent of them).  Every other kind of "rfkill
> -switch" is just an input device and MUST NOT be registered with the rfkill
> -class.
> +working (or the software equivalent of them), i.e. what we call a rfkill
> +controller.  Every other kind of "rfkill switch" is just an input device and
> +MUST NOT be registered with the rfkill class.
>  
>  A driver should register a device with the rfkill class when ALL of the
> -following conditions are met:
> +following conditions are met (they define a rfkill controller):
>  
>  1. The device is/controls a data communications wireless transmitter;
>  
>  2. The kernel can interact with the hardware/firmware to CHANGE the wireless
>     transmitter state (block/unblock TX operation);
>  
> +3. The transmitter can be made to not emit any energy when "blocked":
> +   rfkill is not about blocking data transmissions, it is about blocking
> +   energy emission;
> +
>  A driver should register a device with the input subsystem to issue
>  rfkill-related events (KEY_WLAN, KEY_BLUETOOTH, KEY_WWAN, KEY_WIMAX,
>  SW_RFKILL_ALL, etc) when ALL of the folowing conditions are met:
> @@ -186,9 +256,7 @@ SW_RFKILL_ALL, etc) when ALL of the folowing conditions are met:
>  2. It is NOT slaved to another device, i.e. there is no other device that
>     issues rfkill-related input events in preference to this one.
>  
> -   Typically, the ACPI "radio kill" switch of a laptop is the master input
> -   device to issue rfkill events, and, e.g., the WLAN card is just a slave
> -   device that gets disabled by its hardware radio-kill input pin.
> +   Please refer to the corner cases and examples section for more details.
>  
>  When in doubt, do not issue input events.  For drivers that should generate
>  input events in some platforms, but not in others (e.g. b43), the best solution
> @@ -252,26 +320,102 @@ Add the SW_* events you need for switches, do NOT try to emulate a button using
>  KEY_* events just because there is no such SW_* event yet.  Do NOT try to use,
>  for example, KEY_BLUETOOTH when you should be using SW_BLUETOOTH instead.
>  
> -2. Input device switches (sources of EV_SW events) DO store their current
> -state, and that state CAN be queried from userspace through IOCTLs.  There is
> -no sysfs interface for this, but that doesn't mean you should break things
> -trying to hook it to the rfkill class to get a sysfs interface :-)
> +2. Input device switches (sources of EV_SW events) DO store their current state
> +(so you *must* initialize it by issuing a gratuitous input layer event on
> +driver start-up and also when resuming from sleep), and that state CAN be
> +queried from userspace through IOCTLs.  There is no sysfs interface for this,
> +but that doesn't mean you should break things trying to hook it to the rfkill
> +class to get a sysfs interface :-)
> +
> +3. Do not issue *_RFKILL_ALL events by default, unless you are sure it is the
> +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).
>  
> -3. Do not issue *_RFKILL_ALL events, unless you are sure it is the 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 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).
>  
> +3.1 Guidelines for wireless device drivers
> +------------------------------------------
> +
> +1. Each independent transmitter in a wireless device (usually there is only one
> +transmitter per device) should have a SINGLE rfkill class attached to it.
> +
> +2. If the device does not have any sort of hardware assistance to allow the
> +driver to rfkill the device, the driver should emulate it by taking all actions
> +required to silence the transmitter.
> +
> +3. If it is impossible to silence the transmitter (i.e. it still emits energy,
> +even if it is just in brief pulses, when there is no data to transmit and there
> +is no hardware support to turn it off) do NOT lie to the users.  Do not attach
> +it to a rfkill class.  The rfkill subsystem does not deal with data
> +transmission, it deals with energy emission.  If the transmitter is emitting
> +energy, it is not blocked in rfkill terms.
> +
> +4. It doesn't matter if the device has multiple rfkill input lines affecting
> +the same transmitter, their combined state is to be exported as a single state
> +per transmitter (see rule 1).
> +
> +This rule exists because users of the rfkill subsystem expect to get (and set,
> +when possible) the overall transmitter rfkill state, not of a particular rfkill
> +line.
> +
> +Example of a WLAN wireless driver connected to the rfkill subsystem:
> +--------------------------------------------------------------------
> +
> +A certain WLAN card has one input pin that causes it to block the transmitter
> +and makes the status of that input pin available (only for reading!) to the
> +kernel driver.  This is a hard rfkill input line (it cannot be overridden by
> +the kernel driver).
> +
> +The card also has one PCI register that, if manipulated by the driver, causes
> +it to block the transmitter.  This is a soft rfkill input line.
> +
> +It has also a thermal protection circuitry that shuts down its transmitter if
> +the card overheats, and makes the status of that protection available (only for
> +reading!) to the kernel driver.  This is also a hard rfkill input line.
> +
> +If either one of these rfkill lines are active, the transmitter is blocked by
> +the hardware and forced offline.
> +
> +The driver should allocate and attach to its struct device *ONE* instance of
> +the rfkill class (there is only one transmitter).
> +
> +It can implement the get_state() hook, and return RFKILL_STATE_HARD_BLOCKED if
> +either one of its two hard rfkill input lines are active.  If the two hard
> +rfkill lines are inactive, it must return RFKILL_STATE_SOFT_BLOCKED if its soft
> +rfkill input line is active.  Only if none of the rfkill input lines are
> +active, will it return RFKILL_STATE_UNBLOCKED.
> +
> +If it doesn't implement the get_state() hook, it must make sure that its calls
> +to rfkill_force_state() are enough to keep the status always up-to-date, and it
> +must do a rfkill_force_state() on resume from sleep.
> +
> +Every time the driver gets a notification from the card that one of its rfkill
> +lines changed state (polling might be needed on badly designed cards that don't
> +generate interrupts for such events), it recomputes the rfkill state as per
> +above, and calls rfkill_force_state() to update it.
> +
> +The driver should implement the toggle_radio() hook, that:
> +
> +1. Returns an error if one of the hardware rfkill lines are active, and the
> +caller asked for RFKILL_STATE_UNBLOCKED.
> +
> +2. Activates the soft rfkill line if the caller asked for state
> +RFKILL_STATE_SOFT_BLOCKED.  It should do this even if one of the hard rfkill
> +lines are active, effectively double-blocking the transmitter.
> +
> +3. Deactivates the soft rfkill line if none of the hardware rfkill lines are
> +active and the caller asked for RFKILL_STATE_UNBLOCKED.
>  
>  ===============================================================================
>  4: Kernel API
>  
>  To build a driver with rfkill subsystem support, the driver should depend on
> -the Kconfig symbol RFKILL; it should _not_ depend on RKFILL_INPUT.
> +(or select) the Kconfig symbol RFKILL; it should _not_ depend on RKFILL_INPUT.
>  
>  The hardware the driver talks to may be write-only (where the current state
>  of the hardware is unknown), or read-write (where the hardware can be queried
> @@ -338,10 +482,10 @@ is *absolute*; do NOT violate it.
>  ******IMPORTANT******
>  
>  Userspace must not assume it is the only source of control for rfkill switches.
> -Their state CAN and WILL change on its own, due to firmware actions, direct
> -user actions, and the rfkill-input EPO override for *_RFKILL_ALL.
> +Their state CAN and WILL change due to firmware actions, direct user actions,
> +and the rfkill-input EPO override for *_RFKILL_ALL.
>  
> -When rfkill-input is not active, userspace must initiate an rfkill status
> +When rfkill-input is not active, userspace must initiate a rfkill status
>  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
> @@ -354,19 +498,16 @@ The following sysfs entries will be created:
>  	type: Name of the key type ("wlan", "bluetooth", etc).
>  	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).
> +			transmitter is forced off, but one can override it
> +			by a write to the state attribute;
>  		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).
> +			(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.
> +			the driver's control.  One 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



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] rfkill: rename the rfkill_state states and add block-locked state
  2008-06-23 20:46 ` [PATCH 1/2] rfkill: rename the rfkill_state states and add block-locked state Henrique de Moraes Holschuh
  2008-06-23 20:55   ` Ivo van Doorn
@ 2008-06-27 18:23   ` Dan Williams
  2008-06-27 21:35     ` Henrique de Moraes Holschuh
  1 sibling, 1 reply; 10+ messages in thread
From: Dan Williams @ 2008-06-27 18:23 UTC (permalink / raw)
  To: Henrique de Moraes Holschuh
  Cc: John Linville, linux-wireless, Ivo van Doorn, Matthew Garrett,
	Thomas Renninger, Fabien Crespel, Dmitry Torokhov

On Mon, 2008-06-23 at 17:46 -0300, 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@hmh.eng.br>
> Cc: Ivo van Doorn <IvDoorn@gmail.com>

Thanks for doing this!

So with this patch, in the case where ex. hp-wmi advertises a killswitch
and the wlan driver itself doesn't have one, when NM wants to softkill
the radio, should NM do a SIOCSIWTXPOW or softblock the hp-wmi
killswitch?  Or would the wlan driver implement an rfkill handler and
thus have /sys/class/rfkill/rfkillX as well?  My understanding of what
would happen here got buried in all the mails last time around.  If the
transmitter is not tied to a physical killswitch, but the killswitch is
provided by another module (laptop specific driver for example), do we
assume the rfkill state is authoritative or do we check each radio via
it's own specific method?

Thanks,
Dan

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


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] rfkill: rename the rfkill_state states and add block-locked state
  2008-06-27 18:23   ` Dan Williams
@ 2008-06-27 21:35     ` Henrique de Moraes Holschuh
  2008-06-27 21:39       ` Dan Williams
  0 siblings, 1 reply; 10+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-06-27 21:35 UTC (permalink / raw)
  To: Dan Williams
  Cc: John Linville, linux-wireless, Ivo van Doorn, Matthew Garrett,
	Thomas Renninger, Fabien Crespel, Dmitry Torokhov

On Fri, 27 Jun 2008, Dan Williams wrote:
> On Mon, 2008-06-23 at 17:46 -0300, 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@hmh.eng.br>
> > Cc: Ivo van Doorn <IvDoorn@gmail.com>
> 
> Thanks for doing this!
> 
> So with this patch, in the case where ex. hp-wmi advertises a killswitch
> and the wlan driver itself doesn't have one, when NM wants to softkill
> the radio, should NM do a SIOCSIWTXPOW or softblock the hp-wmi
> killswitch?  Or would the wlan driver implement an rfkill handler and

I thought a lot about it, and I personally feel it is better to keep
SIOCSIWTXPOW separate from rfkill.  This is a driver layer thing, and rfkill
doesn't care either way, but IMHO it is less confusing for the user if
iwconfig txpower doesn't change the rfkill status of a device.

This means NM would be trying to set the class/rfkill*/state to
RFKILL_STATE_SOFT_BLOCKED for the devices it wants to block.

And the way to be able to do it without going insane really is to start
adding rfkill subsystem support to all wireless network drivers, so your
WLAN devices will all have a rfkill class related to them.

> thus have /sys/class/rfkill/rfkillX as well?  My understanding of what
> would happen here got buried in all the mails last time around.  If the
> transmitter is not tied to a physical killswitch, but the killswitch is
> provided by another module (laptop specific driver for example), do we
> assume the rfkill state is authoritative or do we check each radio via
> it's own specific method?

Unless HAL can tell you about it, and you teach HAL the topology for every
laptop model (something I consider Not Doable), you really are supposed to
try to ignore as much as possible the topology of kill switches.

So, you add topology agnostic UIs to NM that let the user:
  1. Easily try to set the state of all devices of a given type
  2. Try to set the state of a particular device.
  3. Try to set the state of all devices (shortcut to "for all types, do (1)
     above).

hp-wmi's and thinkpad-acpi's softswitches would show up just like any
another device.  The user would quickly learn that changing the state of
these devices has an effect of hardblocking others.

When I get to improving the interface to the global state (i.e. add the
attributes needed to implement rfkill-input in userspace), you will be
able to do more, or to do the above in an easier way.

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] rfkill: rename the rfkill_state states and add block-locked state
  2008-06-27 21:35     ` Henrique de Moraes Holschuh
@ 2008-06-27 21:39       ` Dan Williams
  2008-06-27 22:12         ` Henrique de Moraes Holschuh
  0 siblings, 1 reply; 10+ messages in thread
From: Dan Williams @ 2008-06-27 21:39 UTC (permalink / raw)
  To: Henrique de Moraes Holschuh
  Cc: John Linville, linux-wireless, Ivo van Doorn, Matthew Garrett,
	Thomas Renninger, Fabien Crespel, Dmitry Torokhov

On Fri, 2008-06-27 at 18:35 -0300, Henrique de Moraes Holschuh wrote:
> On Fri, 27 Jun 2008, Dan Williams wrote:
> > On Mon, 2008-06-23 at 17:46 -0300, 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@hmh.eng.br>
> > > Cc: Ivo van Doorn <IvDoorn@gmail.com>
> > 
> > Thanks for doing this!
> > 
> > So with this patch, in the case where ex. hp-wmi advertises a killswitch
> > and the wlan driver itself doesn't have one, when NM wants to softkill
> > the radio, should NM do a SIOCSIWTXPOW or softblock the hp-wmi
> > killswitch?  Or would the wlan driver implement an rfkill handler and
> 
> I thought a lot about it, and I personally feel it is better to keep
> SIOCSIWTXPOW separate from rfkill.  This is a driver layer thing, and rfkill
> doesn't care either way, but IMHO it is less confusing for the user if
> iwconfig txpower doesn't change the rfkill status of a device.
> 
> This means NM would be trying to set the class/rfkill*/state to
> RFKILL_STATE_SOFT_BLOCKED for the devices it wants to block.
> 
> And the way to be able to do it without going insane really is to start
> adding rfkill subsystem support to all wireless network drivers, so your
> WLAN devices will all have a rfkill class related to them.

Right, but that's the part we don't yet have, correct?  Can I just start
adding rfkill capability with the patchset you just posted to drivers
like airo and atmel even though they don't have killswitches themselves?
That's the conceptual problem here; cards like airo and atmel don't have
killswitches, and since /sys/class/rfkill/rfkillX are _switches_ right
now, it gets confusing when a device that isn't a killswitch would start
providing rfkillX.

Dan

> > thus have /sys/class/rfkill/rfkillX as well?  My understanding of what
> > would happen here got buried in all the mails last time around.  If the
> > transmitter is not tied to a physical killswitch, but the killswitch is
> > provided by another module (laptop specific driver for example), do we
> > assume the rfkill state is authoritative or do we check each radio via
> > it's own specific method?
> 
> Unless HAL can tell you about it, and you teach HAL the topology for every
> laptop model (something I consider Not Doable), you really are supposed to
> try to ignore as much as possible the topology of kill switches.
> 
> So, you add topology agnostic UIs to NM that let the user:
>   1. Easily try to set the state of all devices of a given type
>   2. Try to set the state of a particular device.
>   3. Try to set the state of all devices (shortcut to "for all types, do (1)
>      above).
> 
> hp-wmi's and thinkpad-acpi's softswitches would show up just like any
> another device.  The user would quickly learn that changing the state of
> these devices has an effect of hardblocking others.
> 
> When I get to improving the interface to the global state (i.e. add the
> attributes needed to implement rfkill-input in userspace), you will be
> able to do more, or to do the above in an easier way.
> 


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] rfkill: rename the rfkill_state states and add block-locked state
  2008-06-27 21:39       ` Dan Williams
@ 2008-06-27 22:12         ` Henrique de Moraes Holschuh
  0 siblings, 0 replies; 10+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-06-27 22:12 UTC (permalink / raw)
  To: Dan Williams
  Cc: John Linville, linux-wireless, Ivo van Doorn, Matthew Garrett,
	Thomas Renninger, Fabien Crespel, Dmitry Torokhov

On Fri, 27 Jun 2008, Dan Williams wrote:
> On Fri, 2008-06-27 at 18:35 -0300, Henrique de Moraes Holschuh wrote:
> > On Fri, 27 Jun 2008, Dan Williams wrote:
> > > So with this patch, in the case where ex. hp-wmi advertises a killswitch
> > > and the wlan driver itself doesn't have one, when NM wants to softkill
> > > the radio, should NM do a SIOCSIWTXPOW or softblock the hp-wmi
> > > killswitch?  Or would the wlan driver implement an rfkill handler and
> > 
> > I thought a lot about it, and I personally feel it is better to keep
> > SIOCSIWTXPOW separate from rfkill.  This is a driver layer thing, and rfkill
> > doesn't care either way, but IMHO it is less confusing for the user if
> > iwconfig txpower doesn't change the rfkill status of a device.
> > 
> > This means NM would be trying to set the class/rfkill*/state to
> > RFKILL_STATE_SOFT_BLOCKED for the devices it wants to block.
> > 
> > And the way to be able to do it without going insane really is to start
> > adding rfkill subsystem support to all wireless network drivers, so your
> > WLAN devices will all have a rfkill class related to them.
> 
> Right, but that's the part we don't yet have, correct?  Can I just start

Correct, not all wireless device drivers support the rfkill class, and those
which do need to be revised to make sure they're doing it right.

> adding rfkill capability with the patchset you just posted to drivers
> like airo and atmel even though they don't have killswitches themselves?

Yes, *provided* that you do so by *adding* a rfkill controller (software
based) to them.  And that, of course, requires you to be able to get the
devices to stop emitting energy in software somehow.

By *definition*, you did not rfkill something if it still keeps emmiting
radio waves, or light, or whatever (even if data transfer itself is not
happening).  For some devices this is easy.  For others, you might find out
you need to put them in D3, or power down their USB port... and for others
it will be just plain impossible.

I don't know the airo and atmel devices you mention, so I have no idea how
easy (or difficult) it is to make them stop emitting energy.


Here is a weird example (with a happy ending) to make it clear:

[PS: I don't know if bluetooth keeps emitting energy when there is no data
to transmit. I will assume it does, since the laptop vendor added expensive
circuitry to make damn sure it could be powered off].

The thinkpad builtin bluetooth device is *impossible* to rfkill as far as
the bluetooth driver is concerned.  So, whatever bluetooth driver takes care
of the builtin thinkpad USB dongle here can't do anything.  It CANNOT
support rfkill.

  * Therefore, the bluetooth driver does not connect that device with a
    rfkill class, because it is impossible for that dongle hardware.  Even
    if the driver were to ignore data received by the device and halt data
    transmissions to it, it would still be emmiting radio signals so it
    would be a LIE to say it was rfkilled.

=> if the user depended only on the bluetooth device driver, he would NOT
have rfkill capabilities for bluetooth at all on the thinkpad.  In fact, he
will not get a "RF kill this radio" control in NM on this device.

Lenovo, however, added a hard rfkill hardware to make up for the lack of
native rfkill support on the bluetooth dongle: it has firmware and hardware
that powers off the dongle(!).  thinkpad-acpi knows about this hard rfkill
controller, and exports it through the rfkill class.

=> the user gets a rfkill class that says it is of type bluetooth, but it is
not connected to a bluetooth device (it is connected to the platform, i.e.
the computer itself).  Now, it is not the bluetooth device, but NM lets him
set it to RFKILL_STATE_SOFT_BLOCKED.  And when he tries it... the other
bluetooth device that had no "RF kill this radio" control goes away! (it has
been hot-unplugged, as power was cut off and that makes it disappear from
the USB bus).  How quaint!

It is not perfect beauty, but it works.  Maybe NM can use some assumptions
to make it better (example: if there is a single bluetooth platform rfkill
controller, and a single bluetooth device with no rfkill controller, assume
the platform device one will control the device), but it might be a wrong
assumption in some platform out there.

Maybe we can even teach HAL about these assumptions for some machines, if it
is deemed important enough (rfkill certainly works without it and won't want
to know about it, but GUIs might present a better interface for the user
with that information).

And I sure hope it is just bluetooth and WWAN that will be annoying like
this.

> That's the conceptual problem here; cards like airo and atmel don't have
> killswitches, and since /sys/class/rfkill/rfkillX are _switches_ right
> now, it gets confusing when a device that isn't a killswitch would start
> providing rfkillX.

Let's avoid the term "switches", please.  Look at the new documentation, and
use rfkill controller, hard rfkill line, soft rfkill line and input device,
please :-)

I hope I got what you meant with "switches" correctly, I know nothing of the
atmel and airo.

-- 
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2008-06-27 22:12 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-06-23 20:46 [GIT PATCH] rfkill rework batch 2 (new) Henrique de Moraes Holschuh
2008-06-23 20:46 ` [PATCH 1/2] rfkill: rename the rfkill_state states and add block-locked state Henrique de Moraes Holschuh
2008-06-23 20:55   ` Ivo van Doorn
2008-06-27 18:23   ` Dan Williams
2008-06-27 21:35     ` Henrique de Moraes Holschuh
2008-06-27 21:39       ` Dan Williams
2008-06-27 22:12         ` Henrique de Moraes Holschuh
2008-06-23 20:46 ` [PATCH 2/2] rfkill: improve documentation for kernel drivers Henrique de Moraes Holschuh
2008-06-23 20:58   ` Ivo van Doorn
  -- strict thread matches above, loose matches on Subject: below --
2008-06-22 15:51 [RFC] rfkill rfkill_state enhancements and new docs Henrique de Moraes Holschuh
2008-06-22 15:51 ` [PATCH 2/2] rfkill: improve documentation for kernel drivers Henrique de Moraes Holschuh

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).