Browse Source

rfkill: rewrite

This patch completely rewrites the rfkill core to address
the following deficiencies:

 * all rfkill drivers need to implement polling where necessary
   rather than having one central implementation

 * updating the rfkill state cannot be done from arbitrary
   contexts, forcing drivers to use schedule_work and requiring
   lots of code

 * rfkill drivers need to keep track of soft/hard blocked
   internally -- the core should do this

 * the rfkill API has many unexpected quirks, for example being
   asymmetric wrt. alloc/free and register/unregister

 * rfkill can call back into a driver from within a function the
   driver called -- this is prone to deadlocks and generally
   should be avoided

 * rfkill-input pointlessly is a separate module

 * drivers need to #ifdef rfkill functions (unless they want to
   depend on or select RFKILL) -- rfkill should provide inlines
   that do nothing if it isn't compiled in

 * the rfkill structure is not opaque -- drivers need to initialise
   it correctly (lots of sanity checking code required) -- instead
   force drivers to pass the right variables to rfkill_alloc()

 * the documentation is hard to read because it always assumes the
   reader is completely clueless and contains way TOO MANY CAPS

 * the rfkill code needlessly uses a lot of locks and atomic
   operations in locked sections

 * fix LED trigger to actually change the LED when the radio state
   changes -- this wasn't done before

Tested-by: Alan Jenkins <alan-jenkins@tuffmail.co.uk>
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> [thinkpad]
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
imx_3.10.17_1.0.0_ga
Johannes Berg 11 years ago
committed by John W. Linville
parent
commit
19d337dff9
46 changed files with 2536 additions and 3273 deletions
  1. +78
    -519
      Documentation/rfkill.txt
  2. +3
    -3
      MAINTAINERS
  3. +15
    -15
      arch/arm/mach-pxa/tosa-bt.c
  4. +0
    -1
      arch/arm/mach-pxa/tosa.c
  5. +22
    -20
      drivers/net/usb/hso.c
  6. +1
    -6
      drivers/net/wireless/ath/ath9k/ath9k.h
  7. +29
    -86
      drivers/net/wireless/ath/ath9k/main.c
  8. +0
    -15
      drivers/net/wireless/ath/ath9k/pci.c
  9. +1
    -1
      drivers/net/wireless/b43/Kconfig
  10. +1
    -1
      drivers/net/wireless/b43/leds.c
  11. +2
    -2
      drivers/net/wireless/b43/main.c
  12. +2
    -2
      drivers/net/wireless/b43/phy_a.c
  13. +6
    -11
      drivers/net/wireless/b43/phy_common.c
  14. +2
    -2
      drivers/net/wireless/b43/phy_common.h
  15. +2
    -2
      drivers/net/wireless/b43/phy_g.c
  16. +1
    -1
      drivers/net/wireless/b43/phy_lp.c
  17. +1
    -1
      drivers/net/wireless/b43/phy_n.c
  18. +34
    -89
      drivers/net/wireless/b43/rfkill.c
  19. +1
    -4
      drivers/net/wireless/b43/rfkill.h
  20. +1
    -1
      drivers/net/wireless/b43legacy/Kconfig
  21. +2
    -1
      drivers/net/wireless/b43legacy/leds.c
  22. +39
    -84
      drivers/net/wireless/b43legacy/rfkill.c
  23. +1
    -5
      drivers/net/wireless/b43legacy/rfkill.h
  24. +2
    -3
      drivers/net/wireless/iwlwifi/Kconfig
  25. +28
    -41
      drivers/net/wireless/iwlwifi/iwl-rfkill.c
  26. +17
    -22
      drivers/net/wireless/iwmc3200wifi/rfkill.c
  27. +6
    -8
      drivers/platform/x86/Kconfig
  28. +22
    -28
      drivers/platform/x86/acer-wmi.c
  29. +32
    -69
      drivers/platform/x86/dell-laptop.c
  30. +29
    -70
      drivers/platform/x86/eeepc-laptop.c
  31. +52
    -51
      drivers/platform/x86/hp-wmi.c
  32. +71
    -120
      drivers/platform/x86/sony-laptop.c
  33. +452
    -421
      drivers/platform/x86/thinkpad_acpi.c
  34. +45
    -114
      drivers/platform/x86/toshiba_acpi.c
  35. +1
    -0
      include/linux/Kbuild
  36. +251
    -74
      include/linux/rfkill.h
  37. +2
    -6
      include/net/wimax.h
  38. +7
    -14
      net/rfkill/Kconfig
  39. +3
    -2
      net/rfkill/Makefile
  40. +896
    -0
      net/rfkill/core.c
  41. +342
    -0
      net/rfkill/input.c
  42. +0
    -390
      net/rfkill/rfkill-input.c
  43. +0
    -855
      net/rfkill/rfkill.c
  44. +8
    -2
      net/rfkill/rfkill.h
  45. +0
    -14
      net/wimax/Kconfig
  46. +26
    -97
      net/wimax/op-rfkill.c

+ 78
- 519
Documentation/rfkill.txt View File

@ -1,571 +1,130 @@
rfkill - RF switch subsystem support
====================================
rfkill - RF kill switch 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
1. Introduction
2. Implementation details
3. Kernel driver guidelines
4. Kernel API
5. Userspace support
1. Introduction:
1. Introduction
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 subsystem provides a generic interface to disabling any radio
transmitter in the system. When a transmitter is blocked, it shall not
radiate any power.
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 subsystem also provides the ability to react on button presses and
disable all transmitters of a certain type (or all). This is intended for
situations where transmitters need to be turned off, for example on
aircraft.
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
radio-frequency transmitters _must_ be disabled (e.g. airplanes).
Because of this requirement, userspace support for the keys should not be made
mandatory. Because userspace might want to perform some additional smarter
tasks when the key is pressed, rfkill provides userspace the possibility to
take over the task to handle the key events.
===============================================================================
2: Implementation details
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. 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 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:
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).
rfkill line:
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).
soft rfkill line/software rfkill line:
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()
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
entire system requesting that devices registered to the rfkill class change
state. The way this interaction happens is not complex, but it is not obvious
either:
Kernel Input layer:
* Generates KEY_WWAN, KEY_WLAN, KEY_BLUETOOTH, SW_RFKILL_ALL, and
other such events when the user presses certain keys, buttons, or
toggles certain physical switches.
THE INPUT LAYER IS NEVER USED TO PROPAGATE STATUS, NOTIFICATIONS OR THE
KIND OF STUFF AN ON-SCREEN-DISPLAY APPLICATION WOULD REPORT. It is
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, 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. Doing otherwise is a common
misconception. There is an API to propagate rfkill status change
information, and it is NOT the input layer.
rfkill class:
* Calls a hook in a driver to effectively change the wireless
transmitter state;
* Keeps track of the wireless transmitter state (with help from
the driver);
* Generates userspace notifications (uevents) and a call to a
notification chain (kernel) when there is a wireless transmitter
state change;
* Connects a wireless communications driver with the common rfkill
control system, which, for example, allows actions such as
"switch all bluetooth devices offline" to be carried out by
userspace or by rfkill-input.
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. 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*) 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):
* Implements the policy of what should happen when one of the input
layer events related to rfkill operation is received.
* Uses the sysfs interface (userspace) or private rfkill API calls
to tell the devices registered with the rfkill class to change
their state (i.e. translates the input layer event into real
action).
* rfkill-input implements EPO by handling EV_SW SW_RFKILL_ALL 0
(power off all transmitters) in a special way: it ignores any
overrides and local state cache and forces all transmitters to the
RFKILL_STATE_SOFT_BLOCKED state (including those which are already
supposed to be BLOCKED).
* rfkill EPO will remain active until rfkill-input receives an
EV_SW SW_RFKILL_ALL 1 event. While the EPO is active, transmitters
are locked in the blocked state (rfkill will refuse to unblock them).
* rfkill-input implements different policies that the user can
select for handling EV_SW SW_RFKILL_ALL 1. It will unlock rfkill,
and either do nothing (leave transmitters blocked, but now unlocked),
restore the transmitters to their state before the EPO, or unblock
them all.
Userspace uevent handler or kernel platform-specific drivers hooked to the
rfkill notifier chain:
* Taps into the rfkill notifier chain or to KOBJ_CHANGE uevents,
in order to know when a device that is registered with the rfkill
class changes state;
* Issues feedback notifications to the user;
* In the rare platforms where this is required, synthesizes an input
event to command all *OTHER* rfkill devices to also change their
statues when a specific rfkill device changes state.
===============================================================================
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. 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 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), 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 (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:
1. It is directly related to some physical device the user interacts with, to
command the O.S./firmware/hardware to enable/disable a data communications
wireless transmitter.
Examples of the physical device are: buttons, keys and switches the user
will press/touch/slide/switch to enable or disable the wireless
communication device.
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.
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
is to NEVER generate input events in the first place. That work should be
deferred to a platform-specific kernel module (which will know when to generate
events through the rfkill notifier chain) or to userspace. This avoids the
usual maintenance problems with DMI whitelisting.
Corner cases and examples:
====================================
1. If the device is an input device that, because of hardware or firmware,
causes wireless transmitters to be blocked regardless of the kernel's will, it
is still just an input device, and NOT to be registered with the rfkill class.
2. If the wireless transmitter switch control is read-only, it is an input
device and not to be registered with the rfkill class (and maybe not to be made
an input layer event source either, see below).
3. If there is some other device driver *closer* to the actual hardware the
user interacted with (the button/switch/key) to issue an input event, THAT is
the device driver that should be issuing input events.
E.g:
[RFKILL slider switch] -- [GPIO hardware] -- [WLAN card rf-kill input]
(platform driver) (wireless card driver)
The user is closer to the RFKILL slide switch plaform driver, so the driver
which must issue input events is the platform driver looking at the GPIO
hardware, and NEVER the wireless card driver (which is just a slave). It is
very likely that there are other leaves than just the WLAN card rf-kill input
(e.g. a bluetooth card, etc)...
On the other hand, some embedded devices do this:
[RFKILL slider switch] -- [WLAN card rf-kill input]
(wireless card driver)
In this situation, the wireless card driver *could* register itself as an input
device and issue rf-kill related input events... but in order to AVOID the need
for DMI whitelisting, the wireless card driver does NOT do it. Userspace (HAL)
or a platform driver (that exists only on these embedded devices) will do the
dirty job of issuing the input events.
COMMON MISTAKES in kernel drivers, related to rfkill:
====================================
1. NEVER confuse input device keys and buttons with input device switches.
1a. Switches are always set or reset. They report the current state
(on position or off position).
1b. Keys and buttons are either in the pressed or not-pressed state, and
that's it. A "button" that latches down when you press it, and
unlatches when you press it again is in fact a switch as far as input
devices go.
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
(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 sliding/rocker switch.
An example of an input device which SHOULD NOT generate *_RFKILL_ALL events by
default, is any sort of hot key that is type-specific (e.g. the one for WLAN).
3.1 Guidelines for wireless device drivers
------------------------------------------
(in this text, rfkill->foo means the foo field of struct rfkill).
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.
The rfkill class is provided for kernel drivers to register their radio
transmitter with the kernel, provide methods for turning it on and off and,
optionally, letting the system know about hardware-disabled states that may
be implemented on the device. This code is enabled with the CONFIG_RFKILL
Kconfig option, which drivers can "select".
5. The wireless device driver MUST NOT leave the transmitter enabled during
suspend and hibernation unless:
The rfkill class code also notifies userspace of state changes, this is
achieved via uevents. It also provides some sysfs files for userspace to
check the status of radio transmitters. See the "Userspace support" section
below.
5.1. The transmitter has to be enabled for some sort of functionality
like wake-on-wireless-packet or autonomous packed forwarding in a mesh
network, and that functionality is enabled for this suspend/hibernation
cycle.
AND
The rfkill-input code implements a basic response to rfkill buttons -- it
implements turning on/off all devices of a certain class (or all).
5.2. The device was not on a user-requested BLOCKED state before
the suspend (i.e. the driver must NOT unblock a device, not even
to support wake-on-wireless-packet or remain in the mesh).
When the device is hard-blocked (either by a call to rfkill_set_hw_state()
or from query_hw_block) set_block() will be invoked but drivers can well
ignore the method call since they can use the return value of the function
rfkill_set_hw_state() to sync the software state instead of keeping track
of calls to set_block().
In other words, there is absolutely no allowed scenario where a driver can
automatically take action to unblock a rfkill controller (obviously, this deals
with scenarios where soft-blocking or both soft and hard blocking is happening.
Scenarios where hardware rfkill lines are the only ones blocking the
transmitter are outside of this rule, since the wireless device driver does not
control its input hardware rfkill lines in the first place).
6. During resume, rfkill will try to restore its previous state.
The entire functionality is spread over more than one subsystem:
7. After a rfkill class is suspended, it will *not* call rfkill->toggle_radio
until it is resumed.
* The kernel input layer generates KEY_WWAN, KEY_WLAN etc. and
SW_RFKILL_ALL -- when the user presses a button. Drivers for radio
transmitters generally do not register to the input layer, unless the
device really provides an input device (i.e. a button that has no
effect other than generating a button press event)
* The rfkill-input code hooks up to these events and switches the soft-block
of the various radio transmitters, depending on the button type.
Example of a WLAN wireless driver connected to the rfkill subsystem:
--------------------------------------------------------------------
* The rfkill drivers turn off/on their transmitters as requested.
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 rfkill class will generate userspace notifications (uevents) to tell
userspace what the current state is.
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.
3. Kernel driver guidelines
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.
Drivers for radio transmitters normally implement only the rfkill class.
These drivers may not unblock the transmitter based on own decisions, they
should act on information provided by the rfkill class only.
Since the device has a hardware rfkill line, it IS subject to state changes
external to rfkill. Therefore, the driver must make sure that it calls
rfkill_force_state() to keep the status always up-to-date, and it must do a
rfkill_force_state() on resume from sleep.
Platform drivers might implement input devices if the rfkill button is just
that, a button. If that button influences the hardware then you need to
implement an rfkill class instead. This also applies if the platform provides
a way to turn on/off the transmitter(s).
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.
During suspend/hibernation, transmitters should only be left enabled when
wake-on wlan or similar functionality requires it and the device wasn't
blocked before suspend/hibernate. Note that it may be necessary to update
the rfkill subsystem's idea of what the current state is at resume time if
the state may have changed over suspend.
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
4. Kernel API
To build a driver with rfkill subsystem support, the driver should depend on
(or select) the Kconfig symbol RFKILL; it should _not_ depend on RKFILL_INPUT.
(or select) the Kconfig symbol RFKILL.
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
about its current state).
The rfkill class will call the get_state hook of a device every time it needs
to know the *real* current state of the hardware. This can happen often, but
it does not do any polling, so it is not enough on hardware that is subject
to state changes outside of the rfkill subsystem.
Therefore, calling rfkill_force_state() when a state change happens is
mandatory when the device has a hardware rfkill line, or when something else
like the firmware could cause its state to be changed without going through the
rfkill class.
Some hardware provides events when its status changes. In these cases, it is
best for the driver to not provide a get_state hook, and instead register the
rfkill class *already* with the correct status, and keep it updated using
rfkill_force_state() when it gets an event from the hardware.
rfkill_force_state() must be used on the device resume handlers to update the
rfkill status, should there be any chance of the device status changing during
the sleep.
There is no provision for a statically-allocated rfkill struct. You must
use rfkill_allocate() to allocate one.
You should:
- rfkill_allocate()
- modify rfkill fields (flags, name)
- modify state to the current hardware state (THIS IS THE ONLY TIME
YOU CAN ACCESS state DIRECTLY)
- rfkill_register()
Calling rfkill_set_hw_state() when a state change happens is required from
rfkill drivers that control devices that can be hard-blocked unless they also
assign the poll_hw_block() callback (then the rfkill core will poll the
device). Don't do this unless you cannot get the event in any other way.
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.
===============================================================================
5: Userspace support
rfkill devices issue uevents (with an action of "change"), with the following
environment variables set:
RFKILL_NAME
RFKILL_STATE
RFKILL_TYPE
5. Userspace support
The ABI for these variables is defined by the sysfs attributes. It is best
to take a quick look at the source to make sure of the possible values.
It is expected that HAL will trap those, and bridge them to DBUS, etc. These
events CAN and SHOULD be used to give feedback to the user about the rfkill
status of the system.
Input devices may issue events that are related to rfkill. These are the
various KEY_* events and SW_* events supported by rfkill-input.c.
Userspace may not change the state of an rfkill switch in response to an
input event, it should refrain from changing states entirely.
Userspace cannot assume it is the only source of control for rfkill switches.
Their state can 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 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
switch is set to OFF, *every* rfkill device *MUST* be immediately put into the
RFKILL_STATE_SOFT_BLOCKED state, no questions asked.
The following sysfs entries will be created:
The following sysfs entries exist for every rfkill device:
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 transmitter
0: RFKILL_STATE_SOFT_BLOCKED
transmitter is forced off, but one can override it
by a write to the state attribute;
transmitter is turned off by software
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);
transmiter is (potentially) active
2: RFKILL_STATE_HARD_BLOCKED
transmitter is forced off by something outside of
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
this means that when 1 or 0 is written, the device rfkill state (if not yet in
the requested state), will be will be toggled accordingly.
For the "claim" entry writing 1 to it means that the kernel no longer handles
key events even though RFKILL_INPUT input was enabled. When "claim" has been
set to 0, userspace should make sure that it listens for the input events or
check the sysfs "state" entry regularly to correctly perform the required tasks
when the rkfill key is pressed.
A note about input devices and EV_SW events:
In order to know the current state of an input device switch (like
SW_RFKILL_ALL), you will need to use an IOCTL. That information is not
available through sysfs in a generic way at this time, and it is not available
through the rfkill class AT ALL.
the driver's control.
claim: 0: Kernel handles events (currently always reads that value)
rfkill devices also issue uevents (with an action of "change"), with the
following environment variables set:
RFKILL_NAME
RFKILL_STATE
RFKILL_TYPE
The contents of these variables corresponds to the "name", "state" and
"type" sysfs files explained above.

+ 3
- 3
MAINTAINERS View File

@ -4753,9 +4753,9 @@ S: Supported
F: fs/reiserfs/
RFKILL
P: Ivo van Doorn
M: IvDoorn@gmail.com
L: netdev@vger.kernel.org
P: Johannes Berg
M: johannes@sipsolutions.net
L: linux-wireless@vger.kernel.org
S: Maintained
F Documentation/rfkill.txt
F: net/rfkill/


+ 15
- 15
arch/arm/mach-pxa/tosa-bt.c View File

@ -35,21 +35,25 @@ static void tosa_bt_off(struct tosa_bt_data *data)
gpio_set_value(data->gpio_reset, 0);
}
static int tosa_bt_toggle_radio(void *data, enum rfkill_state state)
static int tosa_bt_set_block(void *data, bool blocked)
{
pr_info("BT_RADIO going: %s\n",
state == RFKILL_STATE_UNBLOCKED ? "on" : "off");
pr_info("BT_RADIO going: %s\n", blocked ? "off" : "on");
if (state == RFKILL_STATE_UNBLOCKED) {
if (!blocked) {
pr_info("TOSA_BT: going ON\n");
tosa_bt_on(data);
} else {
pr_info("TOSA_BT: going OFF\n");
tosa_bt_off(data);
}
return 0;
}
static const struct rfkill_ops tosa_bt_rfkill_ops = {
.set_block = tosa_bt_set_block,
};
static int tosa_bt_probe(struct platform_device *dev)
{
int rc;
@ -70,18 +74,14 @@ static int tosa_bt_probe(struct platform_device *dev)
if (rc)
goto err_pwr_dir;
rfk = rfkill_allocate(&dev->dev, RFKILL_TYPE_BLUETOOTH);
rfk = rfkill_alloc("tosa-bt", &dev->dev, RFKILL_TYPE_BLUETOOTH,
&tosa_bt_rfkill_ops, data);
if (!rfk) {
rc = -ENOMEM;
goto err_rfk_alloc;
}
rfk->name = "tosa-bt";
rfk->toggle_radio = tosa_bt_toggle_radio;
rfk->data = data;
#ifdef CONFIG_RFKILL_LEDS
rfk->led_trigger.name = "tosa-bt";
#endif
rfkill_set_led_trigger_name(rfk, "tosa-bt");
rc = rfkill_register(rfk);
if (rc)
@ -92,9 +92,7 @@ static int tosa_bt_probe(struct platform_device *dev)
return 0;
err_rfkill:
if (rfk)
rfkill_free(rfk);
rfk = NULL;
rfkill_destroy(rfk);
err_rfk_alloc:
tosa_bt_off(data);
err_pwr_dir:
@ -113,8 +111,10 @@ static int __devexit tosa_bt_remove(struct platform_device *dev)
platform_set_drvdata(dev, NULL);
if (rfk)
if (rfk) {
rfkill_unregister(rfk);
rfkill_destroy(rfk);
}
rfk = NULL;
tosa_bt_off(data);


+ 0
- 1
arch/arm/mach-pxa/tosa.c View File

@ -31,7 +31,6 @@
#include <linux/input.h>
#include <linux/gpio.h>
#include <linux/pda_power.h>
#include <linux/rfkill.h>
#include <linux/spi/spi.h>
#include <asm/setup.h>


+ 22
- 20
drivers/net/usb/hso.c View File

@ -2481,10 +2481,10 @@ static int add_net_device(struct hso_device *hso_dev)
return 0;
}
static int hso_radio_toggle(void *data, enum rfkill_state state)
static int hso_rfkill_set_block(void *data, bool blocked)
{
struct hso_device *hso_dev = data;
int enabled = (state == RFKILL_STATE_UNBLOCKED);
int enabled = !blocked;
int rv;
mutex_lock(&hso_dev->mutex);
@ -2498,6 +2498,10 @@ static int hso_radio_toggle(void *data, enum rfkill_state state)
return rv;
}
static const struct rfkill_ops hso_rfkill_ops = {
.set_block = hso_rfkill_set_block,
};
/* Creates and sets up everything for rfkill */
static void hso_create_rfkill(struct hso_device *hso_dev,
struct usb_interface *interface)
@ -2506,29 +2510,25 @@ static void hso_create_rfkill(struct hso_device *hso_dev,
struct device *dev = &hso_net->net->dev;
char *rfkn;
hso_net->rfkill = rfkill_allocate(&interface_to_usbdev(interface)->dev,
RFKILL_TYPE_WWAN);
if (!hso_net->rfkill) {
dev_err(dev, "%s - Out of memory\n", __func__);
return;
}
rfkn = kzalloc(20, GFP_KERNEL);
if (!rfkn) {
rfkill_free(hso_net->rfkill);
hso_net->rfkill = NULL;
if (!rfkn)
dev_err(dev, "%s - Out of memory\n", __func__);
return;
}
snprintf(rfkn, 20, "hso-%d",
interface->altsetting->desc.bInterfaceNumber);
hso_net->rfkill->name = rfkn;
hso_net->rfkill->state = RFKILL_STATE_UNBLOCKED;
hso_net->rfkill->data = hso_dev;
hso_net->rfkill->toggle_radio = hso_radio_toggle;
hso_net->rfkill = rfkill_alloc(rfkn,
&interface_to_usbdev(interface)->dev,
RFKILL_TYPE_WWAN,
&hso_rfkill_ops, hso_dev);
if (!hso_net->rfkill) {
dev_err(dev, "%s - Out of memory\n", __func__);
kfree(rfkn);
return;
}
if (rfkill_register(hso_net->rfkill) < 0) {
rfkill_destroy(hso_net->rfkill);
kfree(rfkn);
hso_net->rfkill->name = NULL;
rfkill_free(hso_net->rfkill);
hso_net->rfkill = NULL;
dev_err(dev, "%s - Failed to register rfkill\n", __func__);
return;
@ -3165,8 +3165,10 @@ static void hso_free_interface(struct usb_interface *interface)
hso_stop_net_device(network_table[i]);
cancel_work_sync(&network_table[i]->async_put_intf);
cancel_work_sync(&network_table[i]->async_get_intf);
if (rfk)
if (rfk) {
rfkill_unregister(rfk);
rfkill_destroy(rfk);
}
hso_free_net_device(network_table[i]);
}
}


+ 1
- 6
drivers/net/wireless/ath/ath9k/ath9k.h View File

@ -460,12 +460,9 @@ struct ath_led {
bool registered;
};
/* Rfkill */
#define ATH_RFKILL_POLL_INTERVAL 2000 /* msecs */
struct ath_rfkill {
struct rfkill *rfkill;
struct delayed_work rfkill_poll;
struct rfkill_ops ops;
char rfkill_name[32];
};
@ -509,8 +506,6 @@ struct ath_rfkill {
#define SC_OP_RXFLUSH BIT(7)
#define SC_OP_LED_ASSOCIATED BIT(8)
#define SC_OP_RFKILL_REGISTERED BIT(9)
#define SC_OP_RFKILL_SW_BLOCKED BIT(10)
#define SC_OP_RFKILL_HW_BLOCKED BIT(11)
#define SC_OP_WAIT_FOR_BEACON BIT(12)
#define SC_OP_LED_ON BIT(13)
#define SC_OP_SCANNING BIT(14)


+ 29
- 86
drivers/net/wireless/ath/ath9k/main.c View File

@ -1192,120 +1192,69 @@ static bool ath_is_rfkill_set(struct ath_softc *sc)
ah->rfkill_polarity;
}
/* h/w rfkill poll function */
static void ath_rfkill_poll(struct work_struct *work)
/* s/w rfkill handlers */
static int ath_rfkill_set_block(void *data, bool blocked)
{
struct ath_softc *sc = container_of(work, struct ath_softc,
rf_kill.rfkill_poll.work);
bool radio_on;
if (sc->sc_flags & SC_OP_INVALID)
return;
radio_on = !ath_is_rfkill_set(sc);
/*
* enable/disable radio only when there is a
* state change in RF switch
*/
if (radio_on == !!(sc->sc_flags & SC_OP_RFKILL_HW_BLOCKED)) {
enum rfkill_state state;
if (sc->sc_flags & SC_OP_RFKILL_SW_BLOCKED) {
state = radio_on ? RFKILL_STATE_SOFT_BLOCKED
: RFKILL_STATE_HARD_BLOCKED;
} else if (radio_on) {
ath_radio_enable(sc);
state = RFKILL_STATE_UNBLOCKED;
} else {
ath_radio_disable(sc);
state = RFKILL_STATE_HARD_BLOCKED;
}
if (state == RFKILL_STATE_HARD_BLOCKED)
sc->sc_flags |= SC_OP_RFKILL_HW_BLOCKED;
else
sc->sc_flags &= ~SC_OP_RFKILL_HW_BLOCKED;
struct ath_softc *sc = data;
rfkill_force_state(sc->rf_kill.rfkill, state);
}
if (blocked)
ath_radio_disable(sc);
else
ath_radio_enable(sc);
queue_delayed_work(sc->hw->workqueue, &sc->rf_kill.rfkill_poll,
msecs_to_jiffies(ATH_RFKILL_POLL_INTERVAL));
return 0;
}
/* s/w rfkill handler */
static int ath_sw_toggle_radio(void *data, enum rfkill_state state)
static void ath_rfkill_poll_state(struct rfkill *rfkill, void *data)
{
struct ath_softc *sc = data;
bool blocked = !!ath_is_rfkill_set(sc);
switch (state) {
case RFKILL_STATE_SOFT_BLOCKED:
if (!(sc->sc_flags & (SC_OP_RFKILL_HW_BLOCKED |
SC_OP_RFKILL_SW_BLOCKED)))
ath_radio_disable(sc);
sc->sc_flags |= SC_OP_RFKILL_SW_BLOCKED;
return 0;
case RFKILL_STATE_UNBLOCKED:
if ((sc->sc_flags & SC_OP_RFKILL_SW_BLOCKED)) {
sc->sc_flags &= ~SC_OP_RFKILL_SW_BLOCKED;
if (sc->sc_flags & SC_OP_RFKILL_HW_BLOCKED) {
DPRINTF(sc, ATH_DBG_FATAL, "Can't turn on the"
"radio as it is disabled by h/w\n");
return -EPERM;
}
ath_radio_enable(sc);
}
return 0;
default:
return -EINVAL;
}
if (rfkill_set_hw_state(rfkill, blocked))
ath_radio_disable(sc);
else
ath_radio_enable(sc);
}
/* Init s/w rfkill */
static int ath_init_sw_rfkill(struct ath_softc *sc)
{
sc->rf_kill.rfkill = rfkill_allocate(wiphy_dev(sc->hw->wiphy),
RFKILL_TYPE_WLAN);
sc->rf_kill.ops.set_block = ath_rfkill_set_block;
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
sc->rf_kill.ops.poll = ath_rfkill_poll_state;
snprintf(sc->rf_kill.rfkill_name, sizeof(sc->rf_kill.rfkill_name),
"ath9k-%s::rfkill", wiphy_name(sc->hw->wiphy));
sc->rf_kill.rfkill = rfkill_alloc(sc->rf_kill.rfkill_name,
wiphy_dev(sc->hw->wiphy),
RFKILL_TYPE_WLAN,
&sc->rf_kill.ops, sc);
if (!sc->rf_kill.rfkill) {
DPRINTF(sc, ATH_DBG_FATAL, "Failed to allocate rfkill\n");
return -ENOMEM;
}
snprintf(sc->rf_kill.rfkill_name, sizeof(sc->rf_kill.rfkill_name),
"ath9k-%s::rfkill", wiphy_name(sc->hw->wiphy));
sc->rf_kill.rfkill->name = sc->rf_kill.rfkill_name;
sc->rf_kill.rfkill->data = sc;
sc->rf_kill.rfkill->toggle_radio = ath_sw_toggle_radio;
sc->rf_kill.rfkill->state = RFKILL_STATE_UNBLOCKED;
return 0;
}
/* Deinitialize rfkill */
static void ath_deinit_rfkill(struct ath_softc *sc)
{
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
if (sc->sc_flags & SC_OP_RFKILL_REGISTERED) {
rfkill_unregister(sc->rf_kill.rfkill);
rfkill_destroy(sc->rf_kill.rfkill);
sc->sc_flags &= ~SC_OP_RFKILL_REGISTERED;
sc->rf_kill.rfkill = NULL;
}
}
static int ath_start_rfkill_poll(struct ath_softc *sc)
{
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
queue_delayed_work(sc->hw->workqueue,
&sc->rf_kill.rfkill_poll, 0);
if (!(sc->sc_flags & SC_OP_RFKILL_REGISTERED)) {
if (rfkill_register(sc->rf_kill.rfkill)) {
DPRINTF(sc, ATH_DBG_FATAL,
"Unable to register rfkill\n");
rfkill_free(sc->rf_kill.rfkill);
rfkill_destroy(sc->rf_kill.rfkill);
/* Deinitialize the device */
ath_cleanup(sc);
@ -1678,10 +1627,6 @@ int ath_attach(u16 devid, struct ath_softc *sc)
goto error_attach;
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
/* Initialze h/w Rfkill */
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
INIT_DELAYED_WORK(&sc->rf_kill.rfkill_poll, ath_rfkill_poll);
/* Initialize s/w rfkill */
error = ath_init_sw_rfkill(sc);
if (error)
@ -2214,10 +2159,8 @@ static void ath9k_stop(struct ieee80211_hw *hw)
} else
sc->rx.rxlink = NULL;
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
#endif
rfkill_pause_polling(sc->rf_kill.rfkill);
/* disable HAL and put h/w to sleep */
ath9k_hw_disable(sc->sc_ah);
ath9k_hw_configpcipowersave(sc->sc_ah, 1);


+ 0
- 15
drivers/net/wireless/ath/ath9k/pci.c View File

@ -227,11 +227,6 @@ static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state)
ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
#endif
pci_save_state(pdev);
pci_disable_device(pdev);
pci_set_power_state(pdev, PCI_D3hot);
@ -256,16 +251,6 @@ static int ath_pci_resume(struct pci_dev *pdev)
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
/*
* check the h/w rfkill state on resume
* and start the rfkill poll timer
*/
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
queue_delayed_work(sc->hw->workqueue,
&sc->rf_kill.rfkill_poll, 0);
#endif
return 0;
}


+ 1
- 1
drivers/net/wireless/b43/Kconfig View File

@ -102,7 +102,7 @@ config B43_LEDS
# if it's possible.
config B43_RFKILL
bool
depends on B43 && (RFKILL = y || RFKILL = B43) && RFKILL_INPUT && (INPUT_POLLDEV = y || INPUT_POLLDEV = B43)
depends on B43 && (RFKILL = y || RFKILL = B43)
default y
# This config option automatically enables b43 HW-RNG support,


+ 1
- 1
drivers/net/wireless/b43/leds.c View File

@ -87,7 +87,7 @@ static void b43_led_brightness_set(struct led_classdev *led_dev,
}
static int b43_register_led(struct b43_wldev *dev, struct b43_led *led,
const char *name, char *default_trigger,
const char *name, const char *default_trigger,
u8 led_index, bool activelow)
{
int err;


+ 2
- 2
drivers/net/wireless/b43/main.c View File

@ -3470,7 +3470,7 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
if (!!conf->radio_enabled != phy->radio_on) {
if (conf->radio_enabled) {
b43_software_rfkill(dev, RFKILL_STATE_UNBLOCKED);
b43_software_rfkill(dev, false);
b43info(dev->wl, "Radio turned on by software\n");
if (!dev->radio_hw_enable) {
b43info(dev->wl, "The hardware RF-kill button "
@ -3478,7 +3478,7 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
"Press the button to turn it on.\n");
}
} else {
b43_software_rfkill(dev, RFKILL_STATE_SOFT_BLOCKED);
b43_software_rfkill(dev, true);
b43info(dev->wl, "Radio turned off by software\n");
}
}


+ 2
- 2
drivers/net/wireless/b43/phy_a.c View File

@ -480,11 +480,11 @@ static bool b43_aphy_op_supports_hwpctl(struct b43_wldev *dev)
}
static void b43_aphy_op_software_rfkill(struct b43_wldev *dev,
enum rfkill_state state)
bool blocked)
{
struct b43_phy *phy = &dev->phy;
if (state == RFKILL_STATE_UNBLOCKED) {
if (!blocked) {
if (phy->radio_on)
return;
b43_radio_write16(dev, 0x0004, 0x00C0);


+ 6
- 11
drivers/net/wireless/b43/phy_common.c View File

@ -84,7 +84,7 @@ int b43_phy_init(struct b43_wldev *dev)
phy->channel = ops->get_default_chan(dev);
ops->software_rfkill(dev, RFKILL_STATE_UNBLOCKED);
ops->software_rfkill(dev, false);
err = ops->init(dev);
if (err) {
b43err(dev->wl, "PHY init failed\n");
@ -104,7 +104,7 @@ err_phy_exit:
if (ops->exit)
ops->exit(dev);
err_block_rf:
ops->software_rfkill(dev, RFKILL_STATE_SOFT_BLOCKED);
ops->software_rfkill(dev, true);
return err;
}
@ -113,7 +113,7 @@ void b43_phy_exit(struct b43_wldev *dev)
{
const struct b43_phy_operations *ops = dev->phy.ops;
ops->software_rfkill(dev, RFKILL_STATE_SOFT_BLOCKED);
ops->software_rfkill(dev, true);
if (ops->exit)
ops->exit(dev);
}
@ -295,18 +295,13 @@ err_restore_cookie:
return err;
}
void b43_software_rfkill(struct b43_wldev *dev, enum rfkill_state state)
void b43_software_rfkill(struct b43_wldev *dev, bool blocked)
{
struct b43_phy *phy = &dev->phy;
if (state == RFKILL_STATE_HARD_BLOCKED) {
/* We cannot hardware-block the device */
state = RFKILL_STATE_SOFT_BLOCKED;
}
b43_mac_suspend(dev);
phy->ops->software_rfkill(dev, state);
phy->radio_on = (state == RFKILL_STATE_UNBLOCKED);
phy->ops->software_rfkill(dev, blocked);
phy->radio_on = !blocked;
b43_mac_enable(dev);
}


+ 2
- 2
drivers/net/wireless/b43/phy_common.h View File

@ -159,7 +159,7 @@ struct b43_phy_operations {
/* Radio */
bool (*supports_hwpctl)(struct b43_wldev *dev);
void (*software_rfkill)(struct b43_wldev *dev, enum rfkill_state state);
void (*software_rfkill)(struct b43_wldev *dev, bool blocked);
void (*switch_analog)(struct b43_wldev *dev, bool on);
int (*switch_channel)(struct b43_wldev *dev, unsigned int new_channel);
unsigned int (*get_default_chan)(struct b43_wldev *dev);
@ -364,7 +364,7 @@ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel);
/**
* b43_software_rfkill - Turn the radio ON or OFF in software.
*/
void b43_software_rfkill(struct b43_wldev *dev, enum rfkill_state state);
void b43_software_rfkill(struct b43_wldev *dev, bool blocked);
/**
* b43_phy_txpower_check - Check TX power output.


+ 2
- 2
drivers/net/wireless/b43/phy_g.c View File

@ -2592,7 +2592,7 @@ static bool b43_gphy_op_supports_hwpctl(struct b43_wldev *dev)
}
static void b43_gphy_op_software_rfkill(struct b43_wldev *dev,
enum rfkill_state state)
bool blocked)
{
struct b43_phy *phy = &dev->phy;
struct b43_phy_g *gphy = phy->g;
@ -2600,7 +2600,7 @@ static void b43_gphy_op_software_rfkill(struct b43_wldev *dev,
might_sleep();
if (state == RFKILL_STATE_UNBLOCKED) {
if (!blocked) {
/* Turn radio ON */
if (phy->radio_on)
return;


+ 1
- 1
drivers/net/wireless/b43/phy_lp.c View File

@ -488,7 +488,7 @@ static void b43_lpphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value)
}
static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev,
enum rfkill_state state)
bool blocked)
{
//TODO
}


+ 1
- 1
drivers/net/wireless/b43/phy_n.c View File

@ -579,7 +579,7 @@ static void b43_nphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value)
}
static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
enum rfkill_state state)
bool blocked)
{//TODO
}


+ 34
- 89
drivers/net/wireless/b43/rfkill.c View File

@ -45,12 +45,11 @@ static bool b43_is_hw_radio_enabled(struct b43_wldev *dev)
}
/* The poll callback for the hardware button. */
static void b43_rfkill_poll(struct input_polled_dev *poll_dev)
static void b43_rfkill_poll(struct rfkill *rfkill, void *data)
{
struct b43_wldev *dev = poll_dev->private;
struct b43_wldev *dev = data;
struct b43_wl *wl = dev->wl;
bool enabled;
bool report_change = 0;
mutex_lock(&wl->mutex);
if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED)) {
@ -60,68 +59,55 @@ static void b43_rfkill_poll(struct input_polled_dev *poll_dev)
enabled = b43_is_hw_radio_enabled(dev);
if (unlikely(enabled != dev->radio_hw_enable)) {
dev->radio_hw_enable = enabled;
report_change = 1;
b43info(wl, "Radio hardware status changed to %s\n",
enabled ? "ENABLED" : "DISABLED");
enabled = !rfkill_set_hw_state(rfkill, !enabled);
if (enabled != dev->phy.radio_on)
b43_software_rfkill(dev, !enabled);
}
mutex_unlock(&wl->mutex);
/* send the radio switch event to the system - note both a key press
* and a release are required */
if (unlikely(report_change)) {
input_report_key(poll_dev->input, KEY_WLAN, 1);
input_report_key(poll_dev->input, KEY_WLAN, 0);
}
}
/* Called when the RFKILL toggled in software. */
static int b43_rfkill_soft_toggle(void *data, enum rfkill_state state)
static int b43_rfkill_soft_set(void *data, bool blocked)
{
struct b43_wldev *dev = data;
struct b43_wl *wl = dev->wl;
int err = -EBUSY;
int err = -EINVAL;
if (!wl->rfkill.registered)
return 0;
if (WARN_ON(!wl->rfkill.registered))
return -EINVAL;
mutex_lock(&wl->mutex);
if (b43_status(dev) < B43_STAT_INITIALIZED)
goto out_unlock;
if (!dev->radio_hw_enable)
goto out_unlock;
if (!blocked != dev->phy.radio_on)
b43_software_rfkill(dev, blocked);
err = 0;
switch (state) {
case RFKILL_STATE_UNBLOCKED:
if (!dev->radio_hw_enable) {
/* No luck. We can't toggle the hardware RF-kill
* button from software. */
err = -EBUSY;
goto out_unlock;
}
if (!dev->phy.radio_on)
b43_software_rfkill(dev, state);
break;
case RFKILL_STATE_SOFT_BLOCKED:
if (dev->phy.radio_on)
b43_software_rfkill(dev, state);
break;
default:
b43warn(wl, "Received unexpected rfkill state %d.\n", state);
break;
}
out_unlock:
mutex_unlock(&wl->mutex);
return err;
}
char *b43_rfkill_led_name(struct b43_wldev *dev)
const char *b43_rfkill_led_name(struct b43_wldev *dev)
{
struct b43_rfkill *rfk = &(dev->wl->rfkill);
if (!rfk->registered)
return NULL;
return rfkill_get_led_name(rfk->rfkill);
return rfkill_get_led_trigger_name(rfk->rfkill);
}
static const struct rfkill_ops b43_rfkill_ops = {
.set_block = b43_rfkill_soft_set,
.poll = b43_rfkill_poll,
};
void b43_rfkill_init(struct b43_wldev *dev)
{
struct b43_wl *wl = dev->wl;
@ -130,65 +116,26 @@ void b43_rfkill_init(struct b43_wldev *dev)
rfk->registered = 0;
rfk->rfkill = rfkill_allocate(dev->dev->dev, RFKILL_TYPE_WLAN);
if (!rfk->rfkill)
goto out_error;
snprintf(rfk->name, sizeof(rfk->name),
"b43-%s", wiphy_name(wl->hw->wiphy));
rfk->rfkill->name = rfk->name;
rfk->rfkill->state = RFKILL_STATE_UNBLOCKED;
rfk->rfkill->data = dev;
rfk->rfkill->toggle_radio = b43_rfkill_soft_toggle;
rfk->poll_dev = input_allocate_polled_device();
if (!rfk->poll_dev) {
rfkill_free(rfk->rfkill);
goto err_freed_rfk;
}
rfk->poll_dev->private = dev;
rfk->poll_dev->poll = b43_rfkill_poll;
rfk->poll_dev->poll_interval = 1000; /* msecs */
rfk->poll_dev->input->name = rfk->name;
rfk->poll_dev->input->id.bustype = BUS_HOST;
rfk->poll_dev->input->id.vendor = dev->dev->bus->boardinfo.vendor;
rfk->poll_dev->input->evbit[0] = BIT(EV_KEY);
set_bit(KEY_WLAN, rfk->poll_dev->input->keybit);
rfk->rfkill = rfkill_alloc(rfk->name,
dev->dev->dev,
RFKILL_TYPE_WLAN,
&b43_rfkill_ops, dev);
if (!rfk->rfkill)
goto out_error;
err = rfkill_register(rfk->rfkill);
if (err)
goto err_free_polldev;
#ifdef CONFIG_RFKILL_INPUT_MODULE
/* B43 RF-kill isn't useful without the rfkill-input subsystem.
* Try to load the module. */
err = request_module("rfkill-input");
if (err)
b43warn(wl, "Failed to load the rfkill-input module. "
"The built-in radio LED will not work.\n");
#endif /* CONFIG_RFKILL_INPUT */
#if !defined(CONFIG_RFKILL_INPUT) && !defined(CONFIG_RFKILL_INPUT_MODULE)
b43warn(wl, "The rfkill-input subsystem is not available. "
"The built-in radio LED will not work.\n");
#endif
err = input_register_polled_device(rfk->poll_dev);
if (err)
goto err_unreg_rfk;
goto err_free;
rfk->registered = 1;
return;
err_unreg_rfk:
rfkill_unregister(rfk->rfkill);
err_free_polldev:
input_free_polled_device(rfk->poll_dev);
rfk->poll_dev = NULL;
err_freed_rfk:
rfk->rfkill = NULL;
out_error:
err_free:
rfkill_destroy(rfk->rfkill);
out_error:
rfk->registered = 0;
b43warn(wl, "RF-kill button init failed\n");
}
@ -201,9 +148,7 @@ void b43_rfkill_exit(struct b43_wldev *dev)
return;
rfk->registered = 0;
input_unregister_polled_device(rfk->poll_dev);
rfkill_unregister(rfk->rfkill);
input_free_polled_device(rfk->poll_dev);
rfk->poll_dev = NULL;
rfkill_destroy(rfk->rfkill);
rfk->rfkill = NULL;
}

+ 1
- 4
drivers/net/wireless/b43/rfkill.h View File

@ -7,14 +7,11 @@ struct b43_wldev;
#ifdef CONFIG_B43_RFKILL
#include <linux/rfkill.h>
#include <linux/input-polldev.h>
struct b43_rfkill {
/* The RFKILL subsystem data structure */
struct rfkill *rfkill;
/* The poll device for the RFKILL input button */
struct input_polled_dev *poll_dev;
/* Did initialization succeed? Used for freeing. */
bool registered;
/* The unique name of this rfkill switch */
@ -26,7 +23,7 @@ struct b43_rfkill {
void b43_rfkill_init(struct b43_wldev *dev);
void b43_rfkill_exit(struct b43_wldev *dev);
char * b43_rfkill_led_name(struct b43_wldev *dev);
const char *b43_rfkill_led_name(struct b43_wldev *dev);
#else /* CONFIG_B43_RFKILL */


+ 1
- 1
drivers/net/wireless/b43legacy/Kconfig View File

@ -47,7 +47,7 @@ config B43LEGACY_LEDS
# if it's possible.
config B43LEGACY_RFKILL
bool
depends on B43LEGACY && (RFKILL = y || RFKILL = B43LEGACY) && RFKILL_INPUT && (INPUT_POLLDEV = y || INPUT_POLLDEV = B43LEGACY)
depends on B43LEGACY && (RFKILL = y || RFKILL = B43LEGACY)
default y
# This config option automatically enables b43 HW-RNG support,


+ 2
- 1
drivers/net/wireless/b43legacy/leds.c View File

@ -86,7 +86,8 @@ static void b43legacy_led_brightness_set(struct led_classdev *led_dev,
static int b43legacy_register_led(struct b43legacy_wldev *dev,
struct b43legacy_led *led,
const char *name, char *default_trigger,
const char *name,
const char *default_trigger,
u8 led_index, bool activelow)
{
int err;


+ 39
- 84
drivers/net/wireless/b43legacy/rfkill.c View File

@ -45,12 +45,11 @@ static bool b43legacy_is_hw_radio_enabled(struct b43legacy_wldev *dev)
}
/* The poll callback for the hardware button. */
static void b43legacy_rfkill_poll(struct input_polled_dev *poll_dev)
static void b43legacy_rfkill_poll(struct rfkill *rfkill, void *data)
{
struct b43legacy_wldev *dev = poll_dev->private;
struct b43legacy_wldev *dev = data;
struct b43legacy_wl *wl = dev->wl;
bool enabled;
bool report_change = 0;
mutex_lock(&wl->mutex);
if (unlikely(b43legacy_status(dev) < B43legacy_STAT_INITIALIZED)) {
@ -60,71 +59,64 @@ static void b43legacy_rfkill_poll(struct input_polled_dev *poll_dev)
enabled = b43legacy_is_hw_radio_enabled(dev);
if (unlikely(enabled != dev->radio_hw_enable)) {
dev->radio_hw_enable = enabled;
report_change = 1;
b43legacyinfo(wl, "Radio hardware status changed to %s\n",
enabled ? "ENABLED" : "DISABLED");
enabled = !rfkill_set_hw_state(rfkill, !enabled);
if (enabled != dev->phy.radio_on) {
if (enabled)
b43legacy_radio_turn_on(dev);
else
b43legacy_radio_turn_off(dev, 0);
}
}
mutex_unlock(&wl->mutex);
/* send the radio switch event to the system - note both a key press
* and a release are required */
if (unlikely(report_change)) {
input_report_key(poll_dev->input, KEY_WLAN, 1);
input_report_key(poll_dev->input, KEY_WLAN, 0);
}
}
/* Called when the RFKILL toggled in software.
* This is called without locking. */
static int b43legacy_rfkill_soft_toggle(void *data, enum rfkill_state state)
static int b43legacy_rfkill_soft_set(void *data, bool blocked)
{
struct b43legacy_wldev *dev = data;
struct b43legacy_wl *wl = dev->wl;
int err = -EBUSY;
int ret = -EINVAL;
if (!wl->rfkill.registered)
return 0;
return -EINVAL;
mutex_lock(&wl->mutex);
if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED)
goto out_unlock;
err = 0;
switch (state) {
case RFKILL_STATE_UNBLOCKED:
if (!dev->radio_hw_enable) {
/* No luck. We can't toggle the hardware RF-kill
* button from software. */
err = -EBUSY;
goto out_unlock;
}
if (!dev->phy.radio_on)
if (!dev->radio_hw_enable)
goto out_unlock;
if (!blocked != dev->phy.radio_on) {
if (!blocked)
b43legacy_radio_turn_on(dev);
break;
case RFKILL_STATE_SOFT_BLOCKED:
if (dev->phy.radio_on)
else
b43legacy_radio_turn_off(dev, 0);
break;
default:
b43legacywarn(wl, "Received unexpected rfkill state %d.\n",
state);
break;
}
ret = 0;
out_unlock:
mutex_unlock(&wl->mutex);
return err;
return ret;
}
char *b43legacy_rfkill_led_name(struct b43legacy_wldev *dev)
const char *b43legacy_rfkill_led_name(struct b43legacy_wldev *dev)
{
struct b43legacy_rfkill *rfk = &(dev->wl->rfkill);
if (!rfk->registered)
return NULL;
return rfkill_get_led_name(rfk->rfkill);
return rfkill_get_led_trigger_name(rfk->rfkill);
}
static const struct rfkill_ops b43legacy_rfkill_ops = {<