提交 19d337df 编写于 作者: J Johannes Berg 提交者: John W. Linville

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: NAlan Jenkins <alan-jenkins@tuffmail.co.uk>
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> [thinkpad]
Signed-off-by: NJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: NJohn W. Linville <linville@tuxdriver.com>
上级 0f6399c4
rfkill - RF switch subsystem support rfkill - RF kill switch support
==================================== ===============================
1 Introduction 1. Introduction
2 Implementation details 2. Implementation details
3 Kernel driver guidelines 3. Kernel driver guidelines
3.1 wireless device drivers 4. Kernel API
3.2 platform/switch drivers 5. Userspace support
3.3 input device drivers
4 Kernel API
5 Userspace support
1. Introduction: 1. Introduction
The rfkill switch subsystem exists to add a generic interface to circuitry that The rfkill subsystem provides a generic interface to disabling any radio
can enable or disable the signal output of a wireless *transmitter* of any transmitter in the system. When a transmitter is blocked, it shall not
type. By far, the most common use is to disable radio-frequency transmitters. radiate any power.
Note that disabling the signal output means that the the transmitter is to be The subsystem also provides the ability to react on button presses and
made to not emit any energy when "blocked". rfkill is not about blocking data disable all transmitters of a certain type (or all). This is intended for
transmissions, it is about blocking energy emission. 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 2. Implementation details
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
The rfkill subsystem is composed of various components: the rfkill class, the The rfkill subsystem is composed of various components: the rfkill class, the
rfkill-input module (an input layer handler), and some specific input layer rfkill-input module (an input layer handler), and some specific input layer
events. events.
The rfkill class provides kernel drivers with an interface that allows them to The rfkill class is provided for kernel drivers to register their radio
know when they should enable or disable a wireless network device transmitter. transmitter with the kernel, provide methods for turning it on and off and,
This is enabled by the CONFIG_RFKILL Kconfig option. optionally, letting the system know about hardware-disabled states that may
be implemented on the device. This code is enabled with the CONFIG_RFKILL
The rfkill class support makes sure userspace will be notified of all state Kconfig option, which drivers can "select".
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.
5. The wireless device driver MUST NOT leave the transmitter enabled during The rfkill class code also notifies userspace of state changes, this is
suspend and hibernation unless: 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 When the device is hard-blocked (either by a call to rfkill_set_hw_state()
the suspend (i.e. the driver must NOT unblock a device, not even or from query_hw_block) set_block() will be invoked but drivers can well
to support wake-on-wireless-packet or remain in the mesh). 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 * The kernel input layer generates KEY_WWAN, KEY_WLAN etc. and
until it is resumed. 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 * The rfkill class will generate userspace notifications (uevents) to tell
and makes the status of that input pin available (only for reading!) to the userspace what the current state is.
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 3. Kernel driver guidelines
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 Drivers for radio transmitters normally implement only the rfkill class.
either one of its two hard rfkill input lines are active. If the two hard These drivers may not unblock the transmitter based on own decisions, they
rfkill lines are inactive, it must return RFKILL_STATE_SOFT_BLOCKED if its soft should act on information provided by the rfkill class only.
rfkill input line is active. Only if none of the rfkill input lines are
active, will it return RFKILL_STATE_UNBLOCKED.
Since the device has a hardware rfkill line, it IS subject to state changes Platform drivers might implement input devices if the rfkill button is just
external to rfkill. Therefore, the driver must make sure that it calls that, a button. If that button influences the hardware then you need to
rfkill_force_state() to keep the status always up-to-date, and it must do a implement an rfkill class instead. This also applies if the platform provides
rfkill_force_state() on resume from sleep. a way to turn on/off the transmitter(s).
Every time the driver gets a notification from the card that one of its rfkill During suspend/hibernation, transmitters should only be left enabled when
lines changed state (polling might be needed on badly designed cards that don't wake-on wlan or similar functionality requires it and the device wasn't
generate interrupts for such events), it recomputes the rfkill state as per blocked before suspend/hibernate. Note that it may be necessary to update
above, and calls rfkill_force_state() to update it. 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 4. Kernel API
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 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 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 of the hardware is unknown), or read-write (where the hardware can be queried
about its current state). about its current state).
The rfkill class will call the get_state hook of a device every time it needs Calling rfkill_set_hw_state() when a state change happens is required from
to know the *real* current state of the hardware. This can happen often, but rfkill drivers that control devices that can be hard-blocked unless they also
it does not do any polling, so it is not enough on hardware that is subject assign the poll_hw_block() callback (then the rfkill core will poll the
to state changes outside of the rfkill subsystem. device). Don't do this unless you cannot get the event in any other way.
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()
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 5. Userspace support
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
The ABI for these variables is defined by the sysfs attributes. It is best The following sysfs entries exist for every rfkill device:
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:
name: Name assigned by driver to this key (interface or driver name). name: Name assigned by driver to this key (interface or driver name).
type: Name of the key type ("wlan", "bluetooth", etc). type: Name of the key type ("wlan", "bluetooth", etc).
state: Current state of the transmitter state: Current state of the transmitter
0: RFKILL_STATE_SOFT_BLOCKED 0: RFKILL_STATE_SOFT_BLOCKED
transmitter is forced off, but one can override it transmitter is turned off by software
by a write to the state attribute;
1: RFKILL_STATE_UNBLOCKED 1: RFKILL_STATE_UNBLOCKED
transmiter is NOT forced off, and may operate if transmiter is (potentially) active
all other conditions for such operation are met
(such as interface is up and configured, etc);
2: RFKILL_STATE_HARD_BLOCKED 2: RFKILL_STATE_HARD_BLOCKED
transmitter is forced off by something outside of transmitter is forced off by something outside of
the driver's control. One cannot set a device to the driver's control.
this state through writes to the state attribute; claim: 0: Kernel handles events (currently always reads that value)
claim: 1: Userspace handles events, 0: Kernel handles events
rfkill devices also issue uevents (with an action of "change"), with the
Both the "state" and "claim" entries are also writable. For the "state" entry following environment variables set:
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. RFKILL_NAME
RFKILL_STATE
For the "claim" entry writing 1 to it means that the kernel no longer handles RFKILL_TYPE
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 The contents of these variables corresponds to the "name", "state" and
check the sysfs "state" entry regularly to correctly perform the required tasks "type" sysfs files explained above.
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.
...@@ -4753,9 +4753,9 @@ S: Supported ...@@ -4753,9 +4753,9 @@ S: Supported
F: fs/reiserfs/ F: fs/reiserfs/
RFKILL RFKILL
P: Ivo van Doorn P: Johannes Berg
M: IvDoorn@gmail.com M: johannes@sipsolutions.net
L: netdev@vger.kernel.org L: linux-wireless@vger.kernel.org
S: Maintained S: Maintained
F Documentation/rfkill.txt F Documentation/rfkill.txt
F: net/rfkill/ F: net/rfkill/
......
...@@ -35,21 +35,25 @@ static void tosa_bt_off(struct tosa_bt_data *data) ...@@ -35,21 +35,25 @@ static void tosa_bt_off(struct tosa_bt_data *data)
gpio_set_value(data->gpio_reset, 0); 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", pr_info("BT_RADIO going: %s\n", blocked ? "off" : "on");
state == RFKILL_STATE_UNBLOCKED ? "on" : "off");
if (state == RFKILL_STATE_UNBLOCKED) { if (!blocked) {
pr_info("TOSA_BT: going ON\n"); pr_info("TOSA_BT: going ON\n");
tosa_bt_on(data); tosa_bt_on(data);
} else { } else {
pr_info("TOSA_BT: going OFF\n"); pr_info("TOSA_BT: going OFF\n");
tosa_bt_off(data); tosa_bt_off(data);
} }
return 0; 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) static int tosa_bt_probe(struct platform_device *dev)
{ {
int rc; int rc;
...@@ -70,18 +74,14 @@ static int tosa_bt_probe(struct platform_device *dev) ...@@ -70,18 +74,14 @@ static int tosa_bt_probe(struct platform_device *dev)
if (rc) if (rc)
goto err_pwr_dir; 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) { if (!rfk) {
rc = -ENOMEM; rc = -ENOMEM;
goto err_rfk_alloc; goto err_rfk_alloc;
} }
rfk->name = "tosa-bt"; rfkill_set_led_trigger_name(rfk, "tosa-bt");
rfk->toggle_radio = tosa_bt_toggle_radio;
rfk->data = data;
#ifdef CONFIG_RFKILL_LEDS
rfk->led_trigger.name = "tosa-bt";
#endif
rc = rfkill_register(rfk); rc = rfkill_register(rfk);
if (rc) if (rc)
...@@ -92,9 +92,7 @@ static int tosa_bt_probe(struct platform_device *dev) ...@@ -92,9 +92,7 @@ static int tosa_bt_probe(struct platform_device *dev)
return 0; return 0;
err_rfkill: err_rfkill:
if (rfk) rfkill_destroy(rfk);
rfkill_free(rfk);
rfk = NULL;
err_rfk_alloc: err_rfk_alloc:
tosa_bt_off(data); tosa_bt_off(data);
err_pwr_dir: err_pwr_dir:
...@@ -113,8 +111,10 @@ static int __devexit tosa_bt_remove(struct platform_device *dev) ...@@ -113,8 +111,10 @@ static int __devexit tosa_bt_remove(struct platform_device *dev)
platform_set_drvdata(dev, NULL); platform_set_drvdata(dev, NULL);
if (rfk) if (rfk) {
rfkill_unregister(rfk); rfkill_unregister(rfk);
rfkill_destroy(rfk);
}
rfk = NULL; rfk = NULL;
tosa_bt_off(data); tosa_bt_off(data);
......
...@@ -31,7 +31,6 @@ ...@@ -31,7 +31,6 @@
#include <linux/input.h> #include <linux/input.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/pda_power.h> #include <linux/pda_power.h>
#include <linux/rfkill.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <asm/setup.h> #include <asm/setup.h>
......
...@@ -2481,10 +2481,10 @@ static int add_net_device(struct hso_device *hso_dev) ...@@ -2481,10 +2481,10 @@ static int add_net_device(struct hso_device *hso_dev)
return 0; 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; struct hso_device *hso_dev = data;
int enabled = (state == RFKILL_STATE_UNBLOCKED); int enabled = !blocked;
int rv; int rv;
mutex_lock(&hso_dev->mutex); mutex_lock(&hso_dev->mutex);
...@@ -2498,6 +2498,10 @@ static int hso_radio_toggle(void *data, enum rfkill_state state) ...@@ -2498,6 +2498,10 @@ static int hso_radio_toggle(void *data, enum rfkill_state state)
return rv; return rv;
} }
static const struct rfkill_ops hso_rfkill_ops = {
.set_block = hso_rfkill_set_block,
};
/* Creates and sets up everything for rfkill */ /* Creates and sets up everything for rfkill */
static void hso_create_rfkill(struct hso_device *hso_dev, static void hso_create_rfkill(struct hso_device *hso_dev,
struct usb_interface *interface) struct usb_interface *interface)
...@@ -2506,29 +2510,25 @@ static void hso_create_rfkill(struct hso_device *hso_dev, ...@@ -2506,29 +2510,25 @@ static void hso_create_rfkill(struct hso_device *hso_dev,
struct device *dev = &hso_net->net->dev; struct device *dev = &hso_net->net->dev;
char *rfkn; 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); rfkn = kzalloc(20, GFP_KERNEL);
if (!rfkn) { if (!rfkn)
rfkill_free(hso_net->rfkill);
hso_net->rfkill = NULL;
dev_err(dev, "%s - Out of memory\n", __func__); dev_err(dev, "%s - Out of memory\n", __func__);
return;
}
snprintf(rfkn, 20, "hso-%d", snprintf(rfkn, 20, "hso-%d",
interface->altsetting->desc.bInterfaceNumber); interface->altsetting->desc.bInterfaceNumber);
hso_net->rfkill->name = rfkn;
hso_net->rfkill->state = RFKILL_STATE_UNBLOCKED; hso_net->rfkill = rfkill_alloc(rfkn,
hso_net->rfkill->data = hso_dev; &interface_to_usbdev(interface)->dev,
hso_net->rfkill->toggle_radio = hso_radio_toggle; 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) { if (rfkill_register(hso_net->rfkill) < 0) {
rfkill_destroy(hso_net->rfkill);
kfree(rfkn); kfree(rfkn);
hso_net->rfkill->name = NULL;
rfkill_free(hso_net->rfkill);
hso_net->rfkill = NULL; hso_net->rfkill = NULL;
dev_err(dev, "%s - Failed to register rfkill\n", __func__); dev_err(dev, "%s - Failed to register rfkill\n", __func__);
return; return;
...@@ -3165,8 +3165,10 @@ static void hso_free_interface(struct usb_interface *interface) ...@@ -3165,8 +3165,10 @@ static void hso_free_interface(struct usb_interface *interface)
hso_stop_net_device(network_table[i]); hso_stop_net_device(network_table[i]);
cancel_work_sync(&network_table[i]->async_put_intf); cancel_work_sync(&network_table[i]->async_put_intf);
cancel_work_sync(&network_table[i]->async_get_intf); cancel_work_sync(&network_table[i]->async_get_intf);
if (rfk) if (rfk) {
rfkill_unregister(rfk); rfkill_unregister(rfk);
rfkill_destroy(rfk);
}
hso_free_net_device(network_table[i]); hso_free_net_device(network_table[i]);
} }
} }
......
...@@ -460,12 +460,9 @@ struct ath_led { ...@@ -460,12 +460,9 @@ struct ath_led {
bool registered; bool registered;
}; };
/* Rfkill */
#define ATH_RFKILL_POLL_INTERVAL 2000 /* msecs */
struct ath_rfkill { struct ath_rfkill {
struct rfkill *rfkill; struct rfkill *rfkill;
struct delayed_work rfkill_poll; struct rfkill_ops ops;
char rfkill_name[32]; char rfkill_name[32];
}; };
...@@ -509,8 +506,6 @@ struct ath_rfkill { ...@@ -509,8 +506,6 @@ struct ath_rfkill {
#define SC_OP_RXFLUSH BIT(7) #define SC_OP_RXFLUSH BIT(7)
#define SC_OP_LED_ASSOCIATED BIT(8) #define SC_OP_LED_ASSOCIATED BIT(8)
#define SC_OP_RFKILL_REGISTERED BIT(9) #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_WAIT_FOR_BEACON BIT(12)
#define SC_OP_LED_ON BIT(13) #define SC_OP_LED_ON BIT(13)
#define SC_OP_SCANNING BIT(14) #define SC_OP_SCANNING BIT(14)
......
...@@ -1192,120 +1192,69 @@ static bool ath_is_rfkill_set(struct ath_softc *sc) ...@@ -1192,120 +1192,69 @@ static bool ath_is_rfkill_set(struct ath_softc *sc)
ah->rfkill_polarity; ah->rfkill_polarity;
} }
/* h/w rfkill poll function */ /* s/w rfkill handlers */
static void ath_rfkill_poll(struct work_struct *work) static int ath_rfkill_set_block(void *data, bool blocked)
{ {
struct ath_softc *sc = container_of(work, struct ath_softc, struct ath_softc *sc = data;
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) { if (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); ath_radio_disable(sc);
state = RFKILL_STATE_HARD_BLOCKED;
}
if (state == RFKILL_STATE_HARD_BLOCKED)
sc->sc_flags |= SC_OP_RFKILL_HW_BLOCKED;
else else
sc->sc_flags &= ~SC_OP_RFKILL_HW_BLOCKED; ath_radio_enable(sc);
rfkill_force_state(sc->rf_kill.rfkill, state);
}
queue_delayed_work(sc->hw->workqueue, &sc->rf_kill.rfkill_poll, return 0;
msecs_to_jiffies(ATH_RFKILL_POLL_INTERVAL));
} }
/* s/w rfkill handler */ static void ath_rfkill_poll_state(struct rfkill *rfkill, void *data)
static int ath_sw_toggle_radio(void *data, enum rfkill_state state)
{ {
struct ath_softc *sc = data; struct ath_softc *sc = data;
bool blocked = !!ath_is_rfkill_set(sc);
switch (state) { if (rfkill_set_hw_state(rfkill, blocked))
case RFKILL_STATE_SOFT_BLOCKED:
if (!(sc->sc_flags & (SC_OP_RFKILL_HW_BLOCKED |
SC_OP_RFKILL_SW_BLOCKED)))
ath_radio_disable(sc); ath_radio_disable(sc);
sc->sc_flags |= SC_OP_RFKILL_SW_BLOCKED; else
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); ath_radio_enable(sc);
}
return 0;
default:
return -EINVAL;
}
} }
/* Init s/w rfkill */ /* Init s/w rfkill */
static int ath_init_sw_rfkill(struct ath_softc *sc) static int ath_init_sw_rfkill(struct ath_softc *sc)
{ {
sc->rf_kill.rfkill = rfkill_allocate(wiphy_dev(sc->hw->wiphy), sc->rf_kill.ops.set_block = ath_rfkill_set_block;
RFKILL_TYPE_WLAN); 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) { if (!sc->rf_kill.rfkill) {
DPRINTF(sc, ATH_DBG_FATAL, "Failed to allocate rfkill\n"); DPRINTF(sc, ATH_DBG_FATAL, "Failed to allocate rfkill\n");
return -ENOMEM; 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; return 0;
} }
/* Deinitialize rfkill */ /* Deinitialize rfkill */
static void ath_deinit_rfkill(struct ath_softc *sc) 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) { if (sc->sc_flags & SC_OP_RFKILL_REGISTERED) {
rfkill_unregister(sc->rf_kill.rfkill); rfkill_unregister(sc->rf_kill.rfkill);
rfkill_destroy(sc->rf_kill.rfkill);
sc->sc_flags &= ~SC_OP_RFKILL_REGISTERED; sc->sc_flags &= ~SC_OP_RFKILL_REGISTERED;
sc->rf_kill.rfkill = NULL;
} }
} }
static int ath_start_rfkill_poll(struct ath_softc *sc) 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 (!(sc->sc_flags & SC_OP_RFKILL_REGISTERED)) {
if (rfkill_register(sc->rf_kill.rfkill)) { if (rfkill_register(sc->rf_kill.rfkill)) {
DPRINTF(sc, ATH_DBG_FATAL, DPRINTF(sc, ATH_DBG_FATAL,
"Unable to register rfkill\n"); "Unable to register rfkill\n");
rfkill_free(sc->rf_kill.rfkill); rfkill_destroy(sc->rf_kill.rfkill);
/* Deinitialize the device */ /* Deinitialize the device */
ath_cleanup(sc); ath_cleanup(sc);
...@@ -1678,10 +1627,6 @@ int ath_attach(u16 devid, struct ath_softc *sc) ...@@ -1678,10 +1627,6 @@ int ath_attach(u16 devid, struct ath_softc *sc)
goto error_attach; goto error_attach;
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) #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 */ /* Initialize s/w rfkill */
error = ath_init_sw_rfkill(sc); error = ath_init_sw_rfkill(sc);
if (error) if (error)
...@@ -2214,10 +2159,8 @@ static void ath9k_stop(struct ieee80211_hw *hw) ...@@ -2214,10 +2159,8 @@ static void ath9k_stop(struct ieee80211_hw *hw)
} else } else
sc->rx.rxlink = NULL; sc->rx.rxlink = NULL;
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) rfkill_pause_polling(sc->rf_kill.rfkill);
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
#endif
/* disable HAL and put h/w to sleep */ /* disable HAL and put h/w to sleep */
ath9k_hw_disable(sc->sc_ah); ath9k_hw_disable(sc->sc_ah);
ath9k_hw_configpcipowersave(sc->sc_ah, 1); ath9k_hw_configpcipowersave(sc->sc_ah, 1);
......
...@@ -227,11 +227,6 @@ static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state) ...@@ -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); 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_save_state(pdev);
pci_disable_device(pdev); pci_disable_device(pdev);
pci_set_power_state(pdev, PCI_D3hot); pci_set_power_state(pdev, PCI_D3hot);
...@@ -256,16 +251,6 @@ static int ath_pci_resume(struct pci_dev *pdev) ...@@ -256,16 +251,6 @@ static int ath_pci_resume(struct pci_dev *pdev)
AR_GPIO_OUTPUT_MUX_AS_OUTPUT); AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); 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; return 0;
} }
......
...@@ -102,7 +102,7 @@ config B43_LEDS ...@@ -102,7 +102,7 @@ config B43_LEDS
# if it's possible. # if it's possible.
config B43_RFKILL config B43_RFKILL
bool 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 default y
# This config option automatically enables b43 HW-RNG support, # This config option automatically enables b43 HW-RNG support,
......
...@@ -87,7 +87,7 @@ static void b43_led_brightness_set(struct led_classdev *led_dev, ...@@ -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, 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) u8 led_index, bool activelow)
{ {
int err; int err;
......
...@@ -3470,7 +3470,7 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) ...@@ -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 != phy->radio_on) {
if (conf->radio_enabled) { 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"); b43info(dev->wl, "Radio turned on by software\n");
if (!dev->radio_hw_enable) { if (!dev->radio_hw_enable) {
b43info(dev->wl, "The hardware RF-kill button " b43info(dev->wl, "The hardware RF-kill button "
...@@ -3478,7 +3478,7 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) ...@@ -3478,7 +3478,7 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
"Press the button to turn it on.\n"); "Press the button to turn it on.\n");
} }
} else { } else {
b43_software_rfkill(dev, RFKILL_STATE_SOFT_BLOCKED); b43_software_rfkill(dev, true);
b43info(dev->wl, "Radio turned off by software\n"); b43info(dev->wl, "Radio turned off by software\n");
} }
} }
......
...@@ -480,11 +480,11 @@ static bool b43_aphy_op_supports_hwpctl(struct b43_wldev *dev) ...@@ -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, static void b43_aphy_op_software_rfkill(struct b43_wldev *dev,
enum rfkill_state state) bool blocked)
{ {
struct b43_phy *phy = &dev->phy; struct b43_phy *phy = &dev->phy;
if (state == RFKILL_STATE_UNBLOCKED) { if (!blocked) {
if (phy->radio_on) if (phy->radio_on)
return; return;
b43_radio_write16(dev, 0x0004, 0x00C0); b43_radio_write16(dev, 0x0004, 0x00C0);
......
...@@ -84,7 +84,7 @@ int b43_phy_init(struct b43_wldev *dev) ...@@ -84,7 +84,7 @@ int b43_phy_init(struct b43_wldev *dev)
phy->channel = ops->get_default_chan(dev); phy->channel = ops->get_default_chan(dev);
ops->software_rfkill(dev, RFKILL_STATE_UNBLOCKED); ops->software_rfkill(dev, false);
err = ops->init(dev); err = ops->init(dev);
if (err) { if (err) {
b43err(dev->wl, "PHY init failed\n"); b43err(dev->wl, "PHY init failed\n");
...@@ -104,7 +104,7 @@ int b43_phy_init(struct b43_wldev *dev) ...@@ -104,7 +104,7 @@ int b43_phy_init(struct b43_wldev *dev)
if (ops->exit) if (ops->exit)
ops->exit(dev); ops->exit(dev);
err_block_rf: err_block_rf:
ops->software_rfkill(dev, RFKILL_STATE_SOFT_BLOCKED); ops->software_rfkill(dev, true);
return err; return err;
} }
...@@ -113,7 +113,7 @@ void b43_phy_exit(struct b43_wldev *dev) ...@@ -113,7 +113,7 @@ void b43_phy_exit(struct b43_wldev *dev)
{ {
const struct b43_phy_operations *ops = dev->phy.ops; 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) if (ops->exit)
ops->exit(dev); ops->exit(dev);
} }
...@@ -295,18 +295,13 @@ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel) ...@@ -295,18 +295,13 @@ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel)
return err; 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; 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); b43_mac_suspend(dev);
phy->ops->software_rfkill(dev, state); phy->ops->software_rfkill(dev, blocked);
phy->radio_on = (state == RFKILL_STATE_UNBLOCKED); phy->radio_on = !blocked;
b43_mac_enable(dev); b43_mac_enable(dev);
} }
......
...@@ -159,7 +159,7 @@ struct b43_phy_operations { ...@@ -159,7 +159,7 @@ struct b43_phy_operations {
/* Radio */ /* Radio */
bool (*supports_hwpctl)(struct b43_wldev *dev); 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); void (*switch_analog)(struct b43_wldev *dev, bool on);
int (*switch_channel)(struct b43_wldev *dev, unsigned int new_channel); int (*switch_channel)(struct b43_wldev *dev, unsigned int new_channel);
unsigned int (*get_default_chan)(struct b43_wldev *dev); 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); ...@@ -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. * 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. * b43_phy_txpower_check - Check TX power output.
......
...@@ -2592,7 +2592,7 @@ static bool b43_gphy_op_supports_hwpctl(struct b43_wldev *dev) ...@@ -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, 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 *phy = &dev->phy;
struct b43_phy_g *gphy = phy->g; struct b43_phy_g *gphy = phy->g;
...@@ -2600,7 +2600,7 @@ static void b43_gphy_op_software_rfkill(struct b43_wldev *dev, ...@@ -2600,7 +2600,7 @@ static void b43_gphy_op_software_rfkill(struct b43_wldev *dev,
might_sleep(); might_sleep();
if (state == RFKILL_STATE_UNBLOCKED) { if (!blocked) {
/* Turn radio ON */ /* Turn radio ON */
if (phy->radio_on) if (phy->radio_on)
return; return;
......
...@@ -488,7 +488,7 @@ static void b43_lpphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) ...@@ -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, static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev,
enum rfkill_state state) bool blocked)
{ {
//TODO //TODO
} }
......
...@@ -579,7 +579,7 @@ static void b43_nphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) ...@@ -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, static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
enum rfkill_state state) bool blocked)
{//TODO {//TODO
} }
......
...@@ -45,12 +45,11 @@ static bool b43_is_hw_radio_enabled(struct b43_wldev *dev) ...@@ -45,12 +45,11 @@ static bool b43_is_hw_radio_enabled(struct b43_wldev *dev)
} }
/* The poll callback for the hardware button. */ /* 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; struct b43_wl *wl = dev->wl;
bool enabled; bool enabled;
bool report_change = 0;
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED)) { if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED)) {
...@@ -60,68 +59,55 @@ static void b43_rfkill_poll(struct input_polled_dev *poll_dev) ...@@ -60,68 +59,55 @@ static void b43_rfkill_poll(struct input_polled_dev *poll_dev)
enabled = b43_is_hw_radio_enabled(dev); enabled = b43_is_hw_radio_enabled(dev);
if (unlikely(enabled != dev->radio_hw_enable)) { if (unlikely(enabled != dev->radio_hw_enable)) {
dev->radio_hw_enable = enabled; dev->radio_hw_enable = enabled;
report_change = 1;
b43info(wl, "Radio hardware status changed to %s\n", b43info(wl, "Radio hardware status changed to %s\n",
enabled ? "ENABLED" : "DISABLED"); 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); 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. */ /* 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_wldev *dev = data;
struct b43_wl *wl = dev->wl; struct b43_wl *wl = dev->wl;
int err = -EBUSY; int err = -EINVAL;
if (!wl->rfkill.registered) if (WARN_ON(!wl->rfkill.registered))
return 0; return -EINVAL;
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
if (b43_status(dev) < B43_STAT_INITIALIZED) if (b43_status(dev) < B43_STAT_INITIALIZED)
goto out_unlock; goto out_unlock;
err = 0;
switch (state) { if (!dev->radio_hw_enable)
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; goto out_unlock;
}
if (!dev->phy.radio_on) if (!blocked != dev->phy.radio_on)
b43_software_rfkill(dev, state); b43_software_rfkill(dev, blocked);
break; err = 0;
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: out_unlock:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
return err; 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); struct b43_rfkill *rfk = &(dev->wl->rfkill);
if (!rfk->registered) if (!rfk->registered)
return NULL; 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) void b43_rfkill_init(struct b43_wldev *dev)
{ {
struct b43_wl *wl = dev->wl; struct b43_wl *wl = dev->wl;
...@@ -130,65 +116,26 @@ void b43_rfkill_init(struct b43_wldev *dev) ...@@ -130,65 +116,26 @@ void b43_rfkill_init(struct b43_wldev *dev)
rfk->registered = 0; 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), snprintf(rfk->name, sizeof(rfk->name),
"b43-%s", wiphy_name(wl->hw->wiphy)); "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->rfkill = rfkill_alloc(rfk->name,
rfk->poll_dev->input->id.bustype = BUS_HOST; dev->dev->dev,
rfk->poll_dev->input->id.vendor = dev->dev->bus->boardinfo.vendor; RFKILL_TYPE_WLAN,
rfk->poll_dev->input->evbit[0] = BIT(EV_KEY); &b43_rfkill_ops, dev);
set_bit(KEY_WLAN, rfk->poll_dev->input->keybit); if (!rfk->rfkill)
goto out_error;
err = rfkill_register(rfk->rfkill); err = rfkill_register(rfk->rfkill);
if (err) if (err)
goto err_free_polldev; goto err_free;
#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;
rfk->registered = 1; rfk->registered = 1;
return; return;
err_unreg_rfk: err_free:
rfkill_unregister(rfk->rfkill); rfkill_destroy(rfk->rfkill);
err_free_polldev: out_error:
input_free_polled_device(rfk->poll_dev);
rfk->poll_dev = NULL;
err_freed_rfk:
rfk->rfkill = NULL;
out_error:
rfk->registered = 0; rfk->registered = 0;
b43warn(wl, "RF-kill button init failed\n"); b43warn(wl, "RF-kill button init failed\n");
} }
...@@ -201,9 +148,7 @@ void b43_rfkill_exit(struct b43_wldev *dev) ...@@ -201,9 +148,7 @@ void b43_rfkill_exit(struct b43_wldev *dev)
return; return;
rfk->registered = 0; rfk->registered = 0;
input_unregister_polled_device(rfk->poll_dev);
rfkill_unregister(rfk->rfkill); rfkill_unregister(rfk->rfkill);
input_free_polled_device(rfk->poll_dev); rfkill_destroy(rfk->rfkill);
rfk->poll_dev = NULL;
rfk->rfkill = NULL; rfk->rfkill = NULL;
} }
...@@ -7,14 +7,11 @@ struct b43_wldev; ...@@ -7,14 +7,11 @@ struct b43_wldev;
#ifdef CONFIG_B43_RFKILL #ifdef CONFIG_B43_RFKILL
#include <linux/rfkill.h> #include <linux/rfkill.h>
#include <linux/input-polldev.h>
struct b43_rfkill { struct b43_rfkill {
/* The RFKILL subsystem data structure */ /* The RFKILL subsystem data structure */
struct rfkill *rfkill; struct rfkill *rfkill;
/* The poll device for the RFKILL input button */
struct input_polled_dev *poll_dev;
/* Did initialization succeed? Used for freeing. */ /* Did initialization succeed? Used for freeing. */
bool registered; bool registered;
/* The unique name of this rfkill switch */ /* The unique name of this rfkill switch */
...@@ -26,7 +23,7 @@ struct b43_rfkill { ...@@ -26,7 +23,7 @@ struct b43_rfkill {
void b43_rfkill_init(struct b43_wldev *dev); void b43_rfkill_init(struct b43_wldev *dev);
void b43_rfkill_exit(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 */ #else /* CONFIG_B43_RFKILL */
......
...@@ -47,7 +47,7 @@ config B43LEGACY_LEDS ...@@ -47,7 +47,7 @@ config B43LEGACY_LEDS
# if it's possible. # if it's possible.
config B43LEGACY_RFKILL config B43LEGACY_RFKILL
bool 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 default y
# This config option automatically enables b43 HW-RNG support, # This config option automatically enables b43 HW-RNG support,
......
...@@ -86,7 +86,8 @@ static void b43legacy_led_brightness_set(struct led_classdev *led_dev, ...@@ -86,7 +86,8 @@ static void b43legacy_led_brightness_set(struct led_classdev *led_dev,
static int b43legacy_register_led(struct b43legacy_wldev *dev, static int b43legacy_register_led(struct b43legacy_wldev *dev,
struct b43legacy_led *led, struct b43legacy_led *led,
const char *name, char *default_trigger, const char *name,
const char *default_trigger,
u8 led_index, bool activelow) u8 led_index, bool activelow)
{ {
int err; int err;
......
...@@ -45,12 +45,11 @@ static bool b43legacy_is_hw_radio_enabled(struct b43legacy_wldev *dev) ...@@ -45,12 +45,11 @@ static bool b43legacy_is_hw_radio_enabled(struct b43legacy_wldev *dev)
} }
/* The poll callback for the hardware button. */ /* 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; struct b43legacy_wl *wl = dev->wl;
bool enabled; bool enabled;
bool report_change = 0;
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
if (unlikely(b43legacy_status(dev) < B43legacy_STAT_INITIALIZED)) { if (unlikely(b43legacy_status(dev) < B43legacy_STAT_INITIALIZED)) {
...@@ -60,71 +59,64 @@ static void b43legacy_rfkill_poll(struct input_polled_dev *poll_dev) ...@@ -60,71 +59,64 @@ static void b43legacy_rfkill_poll(struct input_polled_dev *poll_dev)
enabled = b43legacy_is_hw_radio_enabled(dev); enabled = b43legacy_is_hw_radio_enabled(dev);
if (unlikely(enabled != dev->radio_hw_enable)) { if (unlikely(enabled != dev->radio_hw_enable)) {
dev->radio_hw_enable = enabled; dev->radio_hw_enable = enabled;
report_change = 1;
b43legacyinfo(wl, "Radio hardware status changed to %s\n", b43legacyinfo(wl, "Radio hardware status changed to %s\n",
enabled ? "ENABLED" : "DISABLED"); 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);
} }
mutex_unlock(&wl->mutex);
} }
/* Called when the RFKILL toggled in software. /* Called when the RFKILL toggled in software.
* This is called without locking. */ * 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_wldev *dev = data;
struct b43legacy_wl *wl = dev->wl; struct b43legacy_wl *wl = dev->wl;
int err = -EBUSY; int ret = -EINVAL;
if (!wl->rfkill.registered) if (!wl->rfkill.registered)
return 0; return -EINVAL;
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED)
goto out_unlock; goto out_unlock;
err = 0;
switch (state) { if (!dev->radio_hw_enable)
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; goto out_unlock;
}
if (!dev->phy.radio_on) if (!blocked != dev->phy.radio_on) {
if (!blocked)
b43legacy_radio_turn_on(dev); b43legacy_radio_turn_on(dev);
break; else
case RFKILL_STATE_SOFT_BLOCKED:
if (dev->phy.radio_on)
b43legacy_radio_turn_off(dev, 0); b43legacy_radio_turn_off(dev, 0);
break;
default:
b43legacywarn(wl, "Received unexpected rfkill state %d.\n",
state);
break;
} }
ret = 0;
out_unlock: out_unlock:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
return ret;
return err;
} }
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); struct b43legacy_rfkill *rfk = &(dev->wl->rfkill);
if (!rfk->registered) if (!rfk->registered)
return NULL; 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 = {
.set_block = b43legacy_rfkill_soft_set,
.poll = b43legacy_rfkill_poll,
};
void b43legacy_rfkill_init(struct b43legacy_wldev *dev) void b43legacy_rfkill_init(struct b43legacy_wldev *dev)
{ {
struct b43legacy_wl *wl = dev->wl; struct b43legacy_wl *wl = dev->wl;
...@@ -133,60 +125,25 @@ void b43legacy_rfkill_init(struct b43legacy_wldev *dev) ...@@ -133,60 +125,25 @@ void b43legacy_rfkill_init(struct b43legacy_wldev *dev)
rfk->registered = 0; 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), snprintf(rfk->name, sizeof(rfk->name),
"b43legacy-%s", wiphy_name(wl->hw->wiphy)); "b43legacy-%s", wiphy_name(wl->hw->wiphy));
rfk->rfkill->name = rfk->name; rfk->rfkill = rfkill_alloc(rfk->name,
rfk->rfkill->state = RFKILL_STATE_UNBLOCKED; dev->dev->dev,
rfk->rfkill->data = dev; RFKILL_TYPE_WLAN,
rfk->rfkill->toggle_radio = b43legacy_rfkill_soft_toggle; &b43legacy_rfkill_ops, dev);
if (!rfk->rfkill)
rfk->poll_dev = input_allocate_polled_device(); goto out_error;
if (!rfk->poll_dev) {
rfkill_free(rfk->rfkill);
goto err_freed_rfk;
}
rfk->poll_dev->private = dev;
rfk->poll_dev->poll = b43legacy_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);
err = rfkill_register(rfk->rfkill); err = rfkill_register(rfk->rfkill);
if (err) if (err)
goto err_free_polldev; goto err_free;
#ifdef CONFIG_RFKILL_INPUT_MODULE
/* B43legacy RF-kill isn't useful without the rfkill-input subsystem.
* Try to load the module. */
err = request_module("rfkill-input");
if (err)
b43legacywarn(wl, "Failed to load the rfkill-input module."
"The built-in radio LED will not work.\n");
#endif /* CONFIG_RFKILL_INPUT */
err = input_register_polled_device(rfk->poll_dev);
if (err)
goto err_unreg_rfk;
rfk->registered = 1; rfk->registered = 1;
return; return;
err_unreg_rfk: err_free:
rfkill_unregister(rfk->rfkill); rfkill_destroy(rfk->rfkill);
err_free_polldev: out_error:
input_free_polled_device(rfk->poll_dev);
rfk->poll_dev = NULL;
err_freed_rfk:
rfk->rfkill = NULL;
out_error:
rfk->registered = 0; rfk->registered = 0;
b43legacywarn(wl, "RF-kill button init failed\n"); b43legacywarn(wl, "RF-kill button init failed\n");
} }
...@@ -199,10 +156,8 @@ void b43legacy_rfkill_exit(struct b43legacy_wldev *dev) ...@@ -199,10 +156,8 @@ void b43legacy_rfkill_exit(struct b43legacy_wldev *dev)
return; return;
rfk->registered = 0; rfk->registered = 0;
input_unregister_polled_device(rfk->poll_dev);
rfkill_unregister(rfk->rfkill); rfkill_unregister(rfk->rfkill);
input_free_polled_device(rfk->poll_dev); rfkill_destroy(rfk->rfkill);
rfk->poll_dev = NULL;
rfk->rfkill = NULL; rfk->rfkill = NULL;
} }
...@@ -6,16 +6,12 @@ struct b43legacy_wldev; ...@@ -6,16 +6,12 @@ struct b43legacy_wldev;
#ifdef CONFIG_B43LEGACY_RFKILL #ifdef CONFIG_B43LEGACY_RFKILL
#include <linux/rfkill.h> #include <linux/rfkill.h>
#include <linux/workqueue.h>
#include <linux/input-polldev.h>
struct b43legacy_rfkill { struct b43legacy_rfkill {
/* The RFKILL subsystem data structure */ /* The RFKILL subsystem data structure */
struct rfkill *rfkill; struct rfkill *rfkill;
/* The poll device for the RFKILL input button */
struct input_polled_dev *poll_dev;
/* Did initialization succeed? Used for freeing. */ /* Did initialization succeed? Used for freeing. */
bool registered; bool registered;
/* The unique name of this rfkill switch */ /* The unique name of this rfkill switch */
...@@ -27,7 +23,7 @@ struct b43legacy_rfkill { ...@@ -27,7 +23,7 @@ struct b43legacy_rfkill {
void b43legacy_rfkill_init(struct b43legacy_wldev *dev); void b43legacy_rfkill_init(struct b43legacy_wldev *dev);
void b43legacy_rfkill_exit(struct b43legacy_wldev *dev); void b43legacy_rfkill_exit(struct b43legacy_wldev *dev);
char *b43legacy_rfkill_led_name(struct b43legacy_wldev *dev); const char *b43legacy_rfkill_led_name(struct b43legacy_wldev *dev);
#else /* CONFIG_B43LEGACY_RFKILL */ #else /* CONFIG_B43LEGACY_RFKILL */
......
...@@ -5,15 +5,14 @@ config IWLWIFI ...@@ -5,15 +5,14 @@ config IWLWIFI
select FW_LOADER select FW_LOADER
select MAC80211_LEDS if IWLWIFI_LEDS select MAC80211_LEDS if IWLWIFI_LEDS
select LEDS_CLASS if IWLWIFI_LEDS select LEDS_CLASS if IWLWIFI_LEDS
select RFKILL if IWLWIFI_RFKILL
config IWLWIFI_LEDS config IWLWIFI_LEDS
bool "Enable LED support in iwlagn and iwl3945 drivers" bool "Enable LED support in iwlagn and iwl3945 drivers"
depends on IWLWIFI depends on IWLWIFI
config IWLWIFI_RFKILL config IWLWIFI_RFKILL
bool "Enable RF kill support in iwlagn and iwl3945 drivers" def_bool y
depends on IWLWIFI depends on IWLWIFI && RFKILL
config IWLWIFI_SPECTRUM_MEASUREMENT config IWLWIFI_SPECTRUM_MEASUREMENT
bool "Enable Spectrum Measurement in iwlagn driver" bool "Enable Spectrum Measurement in iwlagn driver"
......
...@@ -36,42 +36,37 @@ ...@@ -36,42 +36,37 @@
#include "iwl-core.h" #include "iwl-core.h"
/* software rf-kill from user */ /* software rf-kill from user */
static int iwl_rfkill_soft_rf_kill(void *data, enum rfkill_state state) static int iwl_rfkill_soft_rf_kill(void *data, bool blocked)
{ {
struct iwl_priv *priv = data; struct iwl_priv *priv = data;
int err = 0;
if (!priv->rfkill) if (!priv->rfkill)
return 0; return -EINVAL;
if (test_bit(STATUS_EXIT_PENDING, &priv->status)) if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return 0; return 0;
IWL_DEBUG_RF_KILL(priv, "we received soft RFKILL set to state %d\n", state); IWL_DEBUG_RF_KILL(priv, "received soft RFKILL: block=%d\n", blocked);
mutex_lock(&priv->mutex); mutex_lock(&priv->mutex);
switch (state) { if (iwl_is_rfkill_hw(priv))
case RFKILL_STATE_UNBLOCKED:
if (iwl_is_rfkill_hw(priv)) {
err = -EBUSY;
goto out_unlock; goto out_unlock;
}
if (!blocked)
iwl_radio_kill_sw_enable_radio(priv); iwl_radio_kill_sw_enable_radio(priv);
break; else
case RFKILL_STATE_SOFT_BLOCKED:
iwl_radio_kill_sw_disable_radio(priv); iwl_radio_kill_sw_disable_radio(priv);
break;
default:
IWL_WARN(priv, "we received unexpected RFKILL state %d\n",
state);
break;
}
out_unlock: out_unlock:
mutex_unlock(&priv->mutex); mutex_unlock(&priv->mutex);
return 0;
return err;
} }
static const struct rfkill_ops iwl_rfkill_ops = {
.set_block = iwl_rfkill_soft_rf_kill,
};
int iwl_rfkill_init(struct iwl_priv *priv) int iwl_rfkill_init(struct iwl_priv *priv)
{ {
struct device *device = wiphy_dev(priv->hw->wiphy); struct device *device = wiphy_dev(priv->hw->wiphy);
...@@ -80,21 +75,16 @@ int iwl_rfkill_init(struct iwl_priv *priv) ...@@ -80,21 +75,16 @@ int iwl_rfkill_init(struct iwl_priv *priv)
BUG_ON(device == NULL); BUG_ON(device == NULL);
IWL_DEBUG_RF_KILL(priv, "Initializing RFKILL.\n"); IWL_DEBUG_RF_KILL(priv, "Initializing RFKILL.\n");
priv->rfkill = rfkill_allocate(device, RFKILL_TYPE_WLAN); priv->rfkill = rfkill_alloc(priv->cfg->name,
device,
RFKILL_TYPE_WLAN,
&iwl_rfkill_ops, priv);
if (!priv->rfkill) { if (!priv->rfkill) {
IWL_ERR(priv, "Unable to allocate RFKILL device.\n"); IWL_ERR(priv, "Unable to allocate RFKILL device.\n");
ret = -ENOMEM; ret = -ENOMEM;
goto error; goto error;
} }
priv->rfkill->name = priv->cfg->name;
priv->rfkill->data = priv;
priv->rfkill->state = RFKILL_STATE_UNBLOCKED;
priv->rfkill->toggle_radio = iwl_rfkill_soft_rf_kill;
priv->rfkill->dev.class->suspend = NULL;
priv->rfkill->dev.class->resume = NULL;
ret = rfkill_register(priv->rfkill); ret = rfkill_register(priv->rfkill);
if (ret) { if (ret) {
IWL_ERR(priv, "Unable to register RFKILL: %d\n", ret); IWL_ERR(priv, "Unable to register RFKILL: %d\n", ret);
...@@ -102,11 +92,10 @@ int iwl_rfkill_init(struct iwl_priv *priv) ...@@ -102,11 +92,10 @@ int iwl_rfkill_init(struct iwl_priv *priv)
} }
IWL_DEBUG_RF_KILL(priv, "RFKILL initialization complete.\n"); IWL_DEBUG_RF_KILL(priv, "RFKILL initialization complete.\n");
return ret; return 0;
free_rfkill: free_rfkill:
if (priv->rfkill != NULL) rfkill_destroy(priv->rfkill);
rfkill_free(priv->rfkill);
priv->rfkill = NULL; priv->rfkill = NULL;
error: error:
...@@ -118,8 +107,10 @@ EXPORT_SYMBOL(iwl_rfkill_init); ...@@ -118,8 +107,10 @@ EXPORT_SYMBOL(iwl_rfkill_init);
void iwl_rfkill_unregister(struct iwl_priv *priv) void iwl_rfkill_unregister(struct iwl_priv *priv)
{ {
if (priv->rfkill) if (priv->rfkill) {
rfkill_unregister(priv->rfkill); rfkill_unregister(priv->rfkill);
rfkill_destroy(priv->rfkill);
}
priv->rfkill = NULL; priv->rfkill = NULL;
} }
...@@ -131,14 +122,10 @@ void iwl_rfkill_set_hw_state(struct iwl_priv *priv) ...@@ -131,14 +122,10 @@ void iwl_rfkill_set_hw_state(struct iwl_priv *priv)
if (!priv->rfkill) if (!priv->rfkill)
return; return;
if (iwl_is_rfkill_hw(priv)) { if (rfkill_set_hw_state(priv->rfkill,
rfkill_force_state(priv->rfkill, RFKILL_STATE_HARD_BLOCKED); !!iwl_is_rfkill_hw(priv)))
return; iwl_radio_kill_sw_disable_radio(priv);
}
if (!iwl_is_rfkill_sw(priv))
rfkill_force_state(priv->rfkill, RFKILL_STATE_UNBLOCKED);
else else
rfkill_force_state(priv->rfkill, RFKILL_STATE_SOFT_BLOCKED); iwl_radio_kill_sw_enable_radio(priv);
} }
EXPORT_SYMBOL(iwl_rfkill_set_hw_state); EXPORT_SYMBOL(iwl_rfkill_set_hw_state);
...@@ -25,47 +25,42 @@ ...@@ -25,47 +25,42 @@
#include "iwm.h" #include "iwm.h"
static int iwm_rfkill_soft_toggle(void *data, enum rfkill_state state) static int iwm_rfkill_set_block(void *data, bool blocked)
{ {
struct iwm_priv *iwm = data; struct iwm_priv *iwm = data;
switch (state) { if (!blocked) {
case RFKILL_STATE_UNBLOCKED:
if (test_bit(IWM_RADIO_RFKILL_HW, &iwm->radio)) if (test_bit(IWM_RADIO_RFKILL_HW, &iwm->radio))
return -EBUSY; return -EBUSY;
if (test_and_clear_bit(IWM_RADIO_RFKILL_SW, &iwm->radio) && if (test_and_clear_bit(IWM_RADIO_RFKILL_SW, &iwm->radio) &&
(iwm_to_ndev(iwm)->flags & IFF_UP)) (iwm_to_ndev(iwm)->flags & IFF_UP))
iwm_up(iwm); return iwm_up(iwm);
} else {
break;
case RFKILL_STATE_SOFT_BLOCKED:
if (!test_and_set_bit(IWM_RADIO_RFKILL_SW, &iwm->radio)) if (!test_and_set_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
iwm_down(iwm); return iwm_down(iwm);
break;
default:
break;
} }
return 0; return 0;
} }
static const struct rfkill_ops iwm_rfkill_ops = {
.set_block = iwm_rfkill_set_block,
};
int iwm_rfkill_init(struct iwm_priv *iwm) int iwm_rfkill_init(struct iwm_priv *iwm)
{ {
int ret; int ret;
iwm->rfkill = rfkill_allocate(iwm_to_dev(iwm), RFKILL_TYPE_WLAN); iwm->rfkill = rfkill_alloc(KBUILD_MODNAME,
iwm_to_dev(iwm),
RFKILL_TYPE_WLAN,
&iwm_rfkill_ops, iwm);
if (!iwm->rfkill) { if (!iwm->rfkill) {
IWM_ERR(iwm, "Unable to allocate rfkill device\n"); IWM_ERR(iwm, "Unable to allocate rfkill device\n");
return -ENOMEM; return -ENOMEM;
} }
iwm->rfkill->name = KBUILD_MODNAME;
iwm->rfkill->data = iwm;
iwm->rfkill->state = RFKILL_STATE_UNBLOCKED;
iwm->rfkill->toggle_radio = iwm_rfkill_soft_toggle;
ret = rfkill_register(iwm->rfkill); ret = rfkill_register(iwm->rfkill);
if (ret) { if (ret) {
IWM_ERR(iwm, "Failed to register rfkill device\n"); IWM_ERR(iwm, "Failed to register rfkill device\n");
...@@ -74,15 +69,15 @@ int iwm_rfkill_init(struct iwm_priv *iwm) ...@@ -74,15 +69,15 @@ int iwm_rfkill_init(struct iwm_priv *iwm)
return 0; return 0;
fail: fail:
rfkill_free(iwm->rfkill); rfkill_destroy(iwm->rfkill);
return ret; return ret;
} }
void iwm_rfkill_exit(struct iwm_priv *iwm) void iwm_rfkill_exit(struct iwm_priv *iwm)
{ {
if (iwm->rfkill) if (iwm->rfkill) {
rfkill_unregister(iwm->rfkill); rfkill_unregister(iwm->rfkill);
rfkill_destroy(iwm->rfkill);
rfkill_free(iwm->rfkill); }
iwm->rfkill = NULL; iwm->rfkill = NULL;
} }
...@@ -21,7 +21,7 @@ config ACER_WMI ...@@ -21,7 +21,7 @@ config ACER_WMI
depends on NEW_LEDS depends on NEW_LEDS
depends on BACKLIGHT_CLASS_DEVICE depends on BACKLIGHT_CLASS_DEVICE
depends on SERIO_I8042 depends on SERIO_I8042
depends on RFKILL depends on RFKILL || RFKILL = n
select ACPI_WMI select ACPI_WMI
---help--- ---help---
This is a driver for newer Acer (and Wistron) laptops. It adds This is a driver for newer Acer (and Wistron) laptops. It adds
...@@ -60,7 +60,7 @@ config DELL_LAPTOP ...@@ -60,7 +60,7 @@ config DELL_LAPTOP
depends on DCDBAS depends on DCDBAS
depends on EXPERIMENTAL depends on EXPERIMENTAL
depends on BACKLIGHT_CLASS_DEVICE depends on BACKLIGHT_CLASS_DEVICE
depends on RFKILL depends on RFKILL || RFKILL = n
depends on POWER_SUPPLY depends on POWER_SUPPLY
default n default n
---help--- ---help---
...@@ -117,7 +117,7 @@ config HP_WMI ...@@ -117,7 +117,7 @@ config HP_WMI
tristate "HP WMI extras" tristate "HP WMI extras"
depends on ACPI_WMI depends on ACPI_WMI
depends on INPUT depends on INPUT
depends on RFKILL depends on RFKILL || RFKILL = n
help help
Say Y here if you want to support WMI-based hotkeys on HP laptops and Say Y here if you want to support WMI-based hotkeys on HP laptops and
to read data from WMI such as docking or ambient light sensor state. to read data from WMI such as docking or ambient light sensor state.
...@@ -196,14 +196,13 @@ config THINKPAD_ACPI ...@@ -196,14 +196,13 @@ config THINKPAD_ACPI
tristate "ThinkPad ACPI Laptop Extras" tristate "ThinkPad ACPI Laptop Extras"
depends on ACPI depends on ACPI
depends on INPUT depends on INPUT
depends on RFKILL || RFKILL = n
select BACKLIGHT_LCD_SUPPORT select BACKLIGHT_LCD_SUPPORT
select BACKLIGHT_CLASS_DEVICE select BACKLIGHT_CLASS_DEVICE
select HWMON select HWMON
select NVRAM select NVRAM
select NEW_LEDS select NEW_LEDS
select LEDS_CLASS select LEDS_CLASS
select NET
select RFKILL
---help--- ---help---
This is a driver for the IBM and Lenovo ThinkPad laptops. It adds This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
support for Fn-Fx key combinations, Bluetooth control, video support for Fn-Fx key combinations, Bluetooth control, video
...@@ -338,9 +337,9 @@ config EEEPC_LAPTOP ...@@ -338,9 +337,9 @@ config EEEPC_LAPTOP
depends on ACPI depends on ACPI
depends on INPUT depends on INPUT
depends on EXPERIMENTAL depends on EXPERIMENTAL
depends on RFKILL || RFKILL = n
select BACKLIGHT_CLASS_DEVICE select BACKLIGHT_CLASS_DEVICE
select HWMON select HWMON
select RFKILL
---help--- ---help---
This driver supports the Fn-Fx keys on Eee PC laptops. This driver supports the Fn-Fx keys on Eee PC laptops.
It also adds the ability to switch camera/wlan on/off. It also adds the ability to switch camera/wlan on/off.
...@@ -405,9 +404,8 @@ config ACPI_TOSHIBA ...@@ -405,9 +404,8 @@ config ACPI_TOSHIBA
tristate "Toshiba Laptop Extras" tristate "Toshiba Laptop Extras"
depends on ACPI depends on ACPI
depends on INPUT depends on INPUT
depends on RFKILL || RFKILL = n
select INPUT_POLLDEV select INPUT_POLLDEV
select NET
select RFKILL
select BACKLIGHT_CLASS_DEVICE select BACKLIGHT_CLASS_DEVICE
---help--- ---help---
This driver adds support for access to certain system settings This driver adds support for access to certain system settings
......
...@@ -958,58 +958,50 @@ static void acer_rfkill_update(struct work_struct *ignored) ...@@ -958,58 +958,50 @@ static void acer_rfkill_update(struct work_struct *ignored)
status = get_u32(&state, ACER_CAP_WIRELESS); status = get_u32(&state, ACER_CAP_WIRELESS);
if (ACPI_SUCCESS(status)) if (ACPI_SUCCESS(status))
rfkill_force_state(wireless_rfkill, state ? rfkill_set_sw_state(wireless_rfkill, !!state);
RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED);
if (has_cap(ACER_CAP_BLUETOOTH)) { if (has_cap(ACER_CAP_BLUETOOTH)) {
status = get_u32(&state, ACER_CAP_BLUETOOTH); status = get_u32(&state, ACER_CAP_BLUETOOTH);
if (ACPI_SUCCESS(status)) if (ACPI_SUCCESS(status))
rfkill_force_state(bluetooth_rfkill, state ? rfkill_set_sw_state(bluetooth_rfkill, !!state);
RFKILL_STATE_UNBLOCKED :
RFKILL_STATE_SOFT_BLOCKED);
} }
schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
} }
static int acer_rfkill_set(void *data, enum rfkill_state state) static int acer_rfkill_set(void *data, bool blocked)
{ {
acpi_status status; acpi_status status;
u32 *cap = data; u32 cap = (unsigned long)data;
status = set_u32((u32) (state == RFKILL_STATE_UNBLOCKED), *cap); status = set_u32(!!blocked, cap);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return -ENODEV; return -ENODEV;
return 0; return 0;
} }
static struct rfkill * acer_rfkill_register(struct device *dev, static const struct rfkill_ops acer_rfkill_ops = {
enum rfkill_type type, char *name, u32 cap) .set_block = acer_rfkill_set,
};
static struct rfkill *acer_rfkill_register(struct device *dev,
enum rfkill_type type,
char *name, u32 cap)
{ {
int err; int err;
u32 state; u32 state;
u32 *data;
struct rfkill *rfkill_dev; struct rfkill *rfkill_dev;
rfkill_dev = rfkill_allocate(dev, type); rfkill_dev = rfkill_alloc(name, dev, type,
&acer_rfkill_ops,
(void *)(unsigned long)cap);
if (!rfkill_dev) if (!rfkill_dev)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
rfkill_dev->name = name;
get_u32(&state, cap); get_u32(&state, cap);
rfkill_dev->state = state ? RFKILL_STATE_UNBLOCKED : rfkill_set_sw_state(rfkill_dev, !state);
RFKILL_STATE_SOFT_BLOCKED;
data = kzalloc(sizeof(u32), GFP_KERNEL);
if (!data) {
rfkill_free(rfkill_dev);
return ERR_PTR(-ENOMEM);
}
*data = cap;
rfkill_dev->data = data;
rfkill_dev->toggle_radio = acer_rfkill_set;
err = rfkill_register(rfkill_dev); err = rfkill_register(rfkill_dev);
if (err) { if (err) {
kfree(rfkill_dev->data); rfkill_destroy(rfkill_dev);
rfkill_free(rfkill_dev);
return ERR_PTR(err); return ERR_PTR(err);
} }
return rfkill_dev; return rfkill_dev;
...@@ -1027,8 +1019,8 @@ static int acer_rfkill_init(struct device *dev) ...@@ -1027,8 +1019,8 @@ static int acer_rfkill_init(struct device *dev)
RFKILL_TYPE_BLUETOOTH, "acer-bluetooth", RFKILL_TYPE_BLUETOOTH, "acer-bluetooth",
ACER_CAP_BLUETOOTH); ACER_CAP_BLUETOOTH);
if (IS_ERR(bluetooth_rfkill)) { if (IS_ERR(bluetooth_rfkill)) {
kfree(wireless_rfkill->data);
rfkill_unregister(wireless_rfkill); rfkill_unregister(wireless_rfkill);
rfkill_destroy(wireless_rfkill);
return PTR_ERR(bluetooth_rfkill); return PTR_ERR(bluetooth_rfkill);
} }
} }
...@@ -1041,11 +1033,13 @@ static int acer_rfkill_init(struct device *dev) ...@@ -1041,11 +1033,13 @@ static int acer_rfkill_init(struct device *dev)
static void acer_rfkill_exit(void) static void acer_rfkill_exit(void)
{ {
cancel_delayed_work_sync(&acer_rfkill_work); cancel_delayed_work_sync(&acer_rfkill_work);
kfree(wireless_rfkill->data);
rfkill_unregister(wireless_rfkill); rfkill_unregister(wireless_rfkill);
rfkill_destroy(wireless_rfkill);
if (has_cap(ACER_CAP_BLUETOOTH)) { if (has_cap(ACER_CAP_BLUETOOTH)) {
kfree(bluetooth_rfkill->data);
rfkill_unregister(bluetooth_rfkill); rfkill_unregister(bluetooth_rfkill);
rfkill_destroy(bluetooth_rfkill);
} }
return; return;
} }
......
...@@ -174,10 +174,11 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, ...@@ -174,10 +174,11 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,
result[3]: NVRAM format version number result[3]: NVRAM format version number
*/ */
static int dell_rfkill_set(int radio, enum rfkill_state state) static int dell_rfkill_set(void *data, bool blocked)
{ {
struct calling_interface_buffer buffer; struct calling_interface_buffer buffer;
int disable = (state == RFKILL_STATE_UNBLOCKED) ? 0 : 1; int disable = blocked ? 0 : 1;
unsigned long radio = (unsigned long)data;
memset(&buffer, 0, sizeof(struct calling_interface_buffer)); memset(&buffer, 0, sizeof(struct calling_interface_buffer));
buffer.input[0] = (1 | (radio<<8) | (disable << 16)); buffer.input[0] = (1 | (radio<<8) | (disable << 16));
...@@ -186,56 +187,24 @@ static int dell_rfkill_set(int radio, enum rfkill_state state) ...@@ -186,56 +187,24 @@ static int dell_rfkill_set(int radio, enum rfkill_state state)
return 0; return 0;
} }
static int dell_wifi_set(void *data, enum rfkill_state state) static void dell_rfkill_query(struct rfkill *rfkill, void *data)
{
return dell_rfkill_set(1, state);
}
static int dell_bluetooth_set(void *data, enum rfkill_state state)
{
return dell_rfkill_set(2, state);
}
static int dell_wwan_set(void *data, enum rfkill_state state)
{
return dell_rfkill_set(3, state);
}
static int dell_rfkill_get(int bit, enum rfkill_state *state)
{ {
struct calling_interface_buffer buffer; struct calling_interface_buffer buffer;
int status; int status;
int new_state = RFKILL_STATE_HARD_BLOCKED; int bit = (unsigned long)data + 16;
memset(&buffer, 0, sizeof(struct calling_interface_buffer)); memset(&buffer, 0, sizeof(struct calling_interface_buffer));
dell_send_request(&buffer, 17, 11); dell_send_request(&buffer, 17, 11);
status = buffer.output[1]; status = buffer.output[1];
if (status & (1<<16)) if (status & BIT(bit))
new_state = RFKILL_STATE_SOFT_BLOCKED; rfkill_set_hw_state(rfkill, !!(status & BIT(16)));
if (status & (1<<bit))
*state = new_state;
else
*state = RFKILL_STATE_UNBLOCKED;
return 0;
}
static int dell_wifi_get(void *data, enum rfkill_state *state)
{
return dell_rfkill_get(17, state);
}
static int dell_bluetooth_get(void *data, enum rfkill_state *state)
{
return dell_rfkill_get(18, state);
} }
static int dell_wwan_get(void *data, enum rfkill_state *state) static const struct rfkill_ops dell_rfkill_ops = {
{ .set_block = dell_rfkill_set,
return dell_rfkill_get(19, state); .query = dell_rfkill_query,
} };
static int dell_setup_rfkill(void) static int dell_setup_rfkill(void)
{ {
...@@ -248,36 +217,37 @@ static int dell_setup_rfkill(void) ...@@ -248,36 +217,37 @@ static int dell_setup_rfkill(void)
status = buffer.output[1]; status = buffer.output[1];
if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
wifi_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_WLAN); wifi_rfkill = rfkill_alloc("dell-wifi", NULL, RFKILL_TYPE_WLAN,
if (!wifi_rfkill) &dell_rfkill_ops, (void *) 1);
if (!wifi_rfkill) {
ret = -ENOMEM;
goto err_wifi; goto err_wifi;
wifi_rfkill->name = "dell-wifi"; }
wifi_rfkill->toggle_radio = dell_wifi_set;
wifi_rfkill->get_state = dell_wifi_get;
ret = rfkill_register(wifi_rfkill); ret = rfkill_register(wifi_rfkill);
if (ret) if (ret)
goto err_wifi; goto err_wifi;
} }
if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) { if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
bluetooth_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_BLUETOOTH); bluetooth_rfkill = rfkill_alloc("dell-bluetooth", NULL,
if (!bluetooth_rfkill) RFKILL_TYPE_BLUETOOTH,
&dell_rfkill_ops, (void *) 2);
if (!bluetooth_rfkill) {
ret = -ENOMEM;
goto err_bluetooth; goto err_bluetooth;
bluetooth_rfkill->name = "dell-bluetooth"; }
bluetooth_rfkill->toggle_radio = dell_bluetooth_set;
bluetooth_rfkill->get_state = dell_bluetooth_get;
ret = rfkill_register(bluetooth_rfkill); ret = rfkill_register(bluetooth_rfkill);
if (ret) if (ret)
goto err_bluetooth; goto err_bluetooth;
} }
if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) { if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
wwan_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_WWAN); wwan_rfkill = rfkill_alloc("dell-wwan", NULL, RFKILL_TYPE_WWAN,
if (!wwan_rfkill) &dell_rfkill_ops, (void *) 3);
if (!wwan_rfkill) {
ret = -ENOMEM;
goto err_wwan; goto err_wwan;
wwan_rfkill->name = "dell-wwan"; }
wwan_rfkill->toggle_radio = dell_wwan_set;
wwan_rfkill->get_state = dell_wwan_get;
ret = rfkill_register(wwan_rfkill); ret = rfkill_register(wwan_rfkill);
if (ret) if (ret)
goto err_wwan; goto err_wwan;
...@@ -285,22 +255,15 @@ static int dell_setup_rfkill(void) ...@@ -285,22 +255,15 @@ static int dell_setup_rfkill(void)
return 0; return 0;
err_wwan: err_wwan:
if (wwan_rfkill) rfkill_destroy(wwan_rfkill);
rfkill_free(wwan_rfkill); if (bluetooth_rfkill)
if (bluetooth_rfkill) {
rfkill_unregister(bluetooth_rfkill); rfkill_unregister(bluetooth_rfkill);
bluetooth_rfkill = NULL;
}
err_bluetooth: err_bluetooth:
if (bluetooth_rfkill) rfkill_destroy(bluetooth_rfkill);
rfkill_free(bluetooth_rfkill); if (wifi_rfkill)
if (wifi_rfkill) {
rfkill_unregister(wifi_rfkill); rfkill_unregister(wifi_rfkill);
wifi_rfkill = NULL;
}
err_wifi: err_wifi:
if (wifi_rfkill) rfkill_destroy(wifi_rfkill);
rfkill_free(wifi_rfkill);
return ret; return ret;
} }
......
...@@ -299,39 +299,22 @@ static int update_bl_status(struct backlight_device *bd) ...@@ -299,39 +299,22 @@ static int update_bl_status(struct backlight_device *bd)
* Rfkill helpers * Rfkill helpers
*/ */
static int eeepc_wlan_rfkill_set(void *data, enum rfkill_state state) static bool eeepc_wlan_rfkill_blocked(void)
{
if (state == RFKILL_STATE_SOFT_BLOCKED)
return set_acpi(CM_ASL_WLAN, 0);
else
return set_acpi(CM_ASL_WLAN, 1);
}
static int eeepc_wlan_rfkill_state(void *data, enum rfkill_state *state)
{ {
if (get_acpi(CM_ASL_WLAN) == 1) if (get_acpi(CM_ASL_WLAN) == 1)
*state = RFKILL_STATE_UNBLOCKED; return false;
else return true;
*state = RFKILL_STATE_SOFT_BLOCKED;
return 0;
} }
static int eeepc_bluetooth_rfkill_set(void *data, enum rfkill_state state) static int eeepc_rfkill_set(void *data, bool blocked)
{ {
if (state == RFKILL_STATE_SOFT_BLOCKED) unsigned long asl = (unsigned long)data;
return set_acpi(CM_ASL_BLUETOOTH, 0); return set_acpi(asl, !blocked);
else
return set_acpi(CM_ASL_BLUETOOTH, 1);
} }
static int eeepc_bluetooth_rfkill_state(void *data, enum rfkill_state *state) static const struct rfkill_ops eeepc_rfkill_ops = {
{ .set_block = eeepc_rfkill_set,
if (get_acpi(CM_ASL_BLUETOOTH) == 1) };
*state = RFKILL_STATE_UNBLOCKED;
else
*state = RFKILL_STATE_SOFT_BLOCKED;
return 0;
}
/* /*
* Sys helpers * Sys helpers
...@@ -531,9 +514,9 @@ static int notify_brn(void) ...@@ -531,9 +514,9 @@ static int notify_brn(void)
static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
{ {
enum rfkill_state state;
struct pci_dev *dev; struct pci_dev *dev;
struct pci_bus *bus = pci_find_bus(0, 1); struct pci_bus *bus = pci_find_bus(0, 1);
bool blocked;
if (event != ACPI_NOTIFY_BUS_CHECK) if (event != ACPI_NOTIFY_BUS_CHECK)
return; return;
...@@ -543,9 +526,8 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) ...@@ -543,9 +526,8 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
return; return;
} }
eeepc_wlan_rfkill_state(ehotk->eeepc_wlan_rfkill, &state); blocked = eeepc_wlan_rfkill_blocked();
if (!blocked) {
if (state == RFKILL_STATE_UNBLOCKED) {
dev = pci_get_slot(bus, 0); dev = pci_get_slot(bus, 0);
if (dev) { if (dev) {
/* Device already present */ /* Device already present */
...@@ -566,7 +548,7 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) ...@@ -566,7 +548,7 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
} }
} }
rfkill_force_state(ehotk->eeepc_wlan_rfkill, state); rfkill_set_sw_state(ehotk->eeepc_wlan_rfkill, blocked);
} }
static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data) static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
...@@ -684,26 +666,17 @@ static int eeepc_hotk_add(struct acpi_device *device) ...@@ -684,26 +666,17 @@ static int eeepc_hotk_add(struct acpi_device *device)
eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7"); eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
if (get_acpi(CM_ASL_WLAN) != -1) { if (get_acpi(CM_ASL_WLAN) != -1) {
ehotk->eeepc_wlan_rfkill = rfkill_allocate(&device->dev, ehotk->eeepc_wlan_rfkill = rfkill_alloc("eeepc-wlan",
RFKILL_TYPE_WLAN); &device->dev,
RFKILL_TYPE_WLAN,
&eeepc_rfkill_ops,
(void *)CM_ASL_WLAN);
if (!ehotk->eeepc_wlan_rfkill) if (!ehotk->eeepc_wlan_rfkill)
goto wlan_fail; goto wlan_fail;
ehotk->eeepc_wlan_rfkill->name = "eeepc-wlan"; rfkill_set_global_sw_state(RFKILL_TYPE_WLAN,
ehotk->eeepc_wlan_rfkill->toggle_radio = eeepc_wlan_rfkill_set; get_acpi(CM_ASL_WLAN) != 1);
ehotk->eeepc_wlan_rfkill->get_state = eeepc_wlan_rfkill_state;
if (get_acpi(CM_ASL_WLAN) == 1) {
ehotk->eeepc_wlan_rfkill->state =
RFKILL_STATE_UNBLOCKED;
rfkill_set_default(RFKILL_TYPE_WLAN,
RFKILL_STATE_UNBLOCKED);
} else {
ehotk->eeepc_wlan_rfkill->state =
RFKILL_STATE_SOFT_BLOCKED;
rfkill_set_default(RFKILL_TYPE_WLAN,
RFKILL_STATE_SOFT_BLOCKED);
}
result = rfkill_register(ehotk->eeepc_wlan_rfkill); result = rfkill_register(ehotk->eeepc_wlan_rfkill);
if (result) if (result)
goto wlan_fail; goto wlan_fail;
...@@ -711,28 +684,17 @@ static int eeepc_hotk_add(struct acpi_device *device) ...@@ -711,28 +684,17 @@ static int eeepc_hotk_add(struct acpi_device *device)
if (get_acpi(CM_ASL_BLUETOOTH) != -1) { if (get_acpi(CM_ASL_BLUETOOTH) != -1) {
ehotk->eeepc_bluetooth_rfkill = ehotk->eeepc_bluetooth_rfkill =
rfkill_allocate(&device->dev, RFKILL_TYPE_BLUETOOTH); rfkill_alloc("eeepc-bluetooth",
&device->dev,
RFKILL_TYPE_BLUETOOTH,
&eeepc_rfkill_ops,
(void *)CM_ASL_BLUETOOTH);
if (!ehotk->eeepc_bluetooth_rfkill) if (!ehotk->eeepc_bluetooth_rfkill)
goto bluetooth_fail; goto bluetooth_fail;
ehotk->eeepc_bluetooth_rfkill->name = "eeepc-bluetooth"; rfkill_set_global_sw_state(RFKILL_TYPE_BLUETOOTH,
ehotk->eeepc_bluetooth_rfkill->toggle_radio = get_acpi(CM_ASL_BLUETOOTH) != 1);
eeepc_bluetooth_rfkill_set;
ehotk->eeepc_bluetooth_rfkill->get_state =
eeepc_bluetooth_rfkill_state;
if (get_acpi(CM_ASL_BLUETOOTH) == 1) {
ehotk->eeepc_bluetooth_rfkill->state =
RFKILL_STATE_UNBLOCKED;
rfkill_set_default(RFKILL_TYPE_BLUETOOTH,
RFKILL_STATE_UNBLOCKED);
} else {
ehotk->eeepc_bluetooth_rfkill->state =
RFKILL_STATE_SOFT_BLOCKED;
rfkill_set_default(RFKILL_TYPE_BLUETOOTH,
RFKILL_STATE_SOFT_BLOCKED);
}
result = rfkill_register(ehotk->eeepc_bluetooth_rfkill); result = rfkill_register(ehotk->eeepc_bluetooth_rfkill);
if (result) if (result)
goto bluetooth_fail; goto bluetooth_fail;
...@@ -741,13 +703,10 @@ static int eeepc_hotk_add(struct acpi_device *device) ...@@ -741,13 +703,10 @@ static int eeepc_hotk_add(struct acpi_device *device)
return 0; return 0;
bluetooth_fail: bluetooth_fail:
if (ehotk->eeepc_bluetooth_rfkill) rfkill_destroy(ehotk->eeepc_bluetooth_rfkill);
rfkill_free(ehotk->eeepc_bluetooth_rfkill);
rfkill_unregister(ehotk->eeepc_wlan_rfkill); rfkill_unregister(ehotk->eeepc_wlan_rfkill);
ehotk->eeepc_wlan_rfkill = NULL;
wlan_fail: wlan_fail:
if (ehotk->eeepc_wlan_rfkill) rfkill_destroy(ehotk->eeepc_wlan_rfkill);
rfkill_free(ehotk->eeepc_wlan_rfkill);
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6"); eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7"); eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
ehotk_fail: ehotk_fail:
......
...@@ -154,58 +154,46 @@ static int hp_wmi_dock_state(void) ...@@ -154,58 +154,46 @@ static int hp_wmi_dock_state(void)
return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0); return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0);
} }
static int hp_wmi_wifi_set(void *data, enum rfkill_state state) static int hp_wmi_set_block(void *data, bool blocked)
{ {
if (state) unsigned long b = (unsigned long) data;
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x101); int query = BIT(b + 8) | ((!!blocked) << b);
else
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x100);
}
static int hp_wmi_bluetooth_set(void *data, enum rfkill_state state) return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, query);
{
if (state)
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x202);
else
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x200);
} }
static int hp_wmi_wwan_set(void *data, enum rfkill_state state) static const struct rfkill_ops hp_wmi_rfkill_ops = {
{ .set_block = hp_wmi_set_block,
if (state) };
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x404);
else
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x400);
}
static int hp_wmi_wifi_state(void) static bool hp_wmi_wifi_state(void)
{ {
int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
if (wireless & 0x100) if (wireless & 0x100)
return RFKILL_STATE_UNBLOCKED; return false;
else else
return RFKILL_STATE_SOFT_BLOCKED; return true;
} }
static int hp_wmi_bluetooth_state(void) static bool hp_wmi_bluetooth_state(void)
{ {
int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
if (wireless & 0x10000) if (wireless & 0x10000)
return RFKILL_STATE_UNBLOCKED; return false;
else else
return RFKILL_STATE_SOFT_BLOCKED; return true;
} }
static int hp_wmi_wwan_state(void) static bool hp_wmi_wwan_state(void)
{ {
int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
if (wireless & 0x1000000) if (wireless & 0x1000000)
return RFKILL_STATE_UNBLOCKED; return false;
else else
return RFKILL_STATE_SOFT_BLOCKED; return true;
} }
static ssize_t show_display(struct device *dev, struct device_attribute *attr, static ssize_t show_display(struct device *dev, struct device_attribute *attr,
...@@ -347,13 +335,13 @@ static void hp_wmi_notify(u32 value, void *context) ...@@ -347,13 +335,13 @@ static void hp_wmi_notify(u32 value, void *context)
} }
} else if (eventcode == 0x5) { } else if (eventcode == 0x5) {
if (wifi_rfkill) if (wifi_rfkill)
rfkill_force_state(wifi_rfkill, rfkill_set_sw_state(wifi_rfkill,
hp_wmi_wifi_state()); hp_wmi_wifi_state());
if (bluetooth_rfkill) if (bluetooth_rfkill)
rfkill_force_state(bluetooth_rfkill, rfkill_set_sw_state(bluetooth_rfkill,
hp_wmi_bluetooth_state()); hp_wmi_bluetooth_state());
if (wwan_rfkill) if (wwan_rfkill)
rfkill_force_state(wwan_rfkill, rfkill_set_sw_state(wwan_rfkill,
hp_wmi_wwan_state()); hp_wmi_wwan_state());
} else } else
printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n", printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n",
...@@ -430,31 +418,34 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) ...@@ -430,31 +418,34 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
goto add_sysfs_error; goto add_sysfs_error;
if (wireless & 0x1) { if (wireless & 0x1) {
wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN); wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
wifi_rfkill->name = "hp-wifi"; RFKILL_TYPE_WLAN,
wifi_rfkill->state = hp_wmi_wifi_state(); &hp_wmi_rfkill_ops,
wifi_rfkill->toggle_radio = hp_wmi_wifi_set; (void *) 0);
rfkill_set_sw_state(wifi_rfkill, hp_wmi_wifi_state());
err = rfkill_register(wifi_rfkill); err = rfkill_register(wifi_rfkill);
if (err) if (err)
goto add_sysfs_error; goto register_wifi_error;
} }
if (wireless & 0x2) { if (wireless & 0x2) {
bluetooth_rfkill = rfkill_allocate(&device->dev, bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev,
RFKILL_TYPE_BLUETOOTH); RFKILL_TYPE_BLUETOOTH,
bluetooth_rfkill->name = "hp-bluetooth"; &hp_wmi_rfkill_ops,
bluetooth_rfkill->state = hp_wmi_bluetooth_state(); (void *) 1);
bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set; rfkill_set_sw_state(bluetooth_rfkill,
hp_wmi_bluetooth_state());
err = rfkill_register(bluetooth_rfkill); err = rfkill_register(bluetooth_rfkill);
if (err) if (err)
goto register_bluetooth_error; goto register_bluetooth_error;
} }
if (wireless & 0x4) { if (wireless & 0x4) {
wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN); wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev,
wwan_rfkill->name = "hp-wwan"; RFKILL_TYPE_WWAN,
wwan_rfkill->state = hp_wmi_wwan_state(); &hp_wmi_rfkill_ops,
wwan_rfkill->toggle_radio = hp_wmi_wwan_set; (void *) 2);
rfkill_set_sw_state(wwan_rfkill, hp_wmi_wwan_state());
err = rfkill_register(wwan_rfkill); err = rfkill_register(wwan_rfkill);
if (err) if (err)
goto register_wwan_err; goto register_wwan_err;
...@@ -462,11 +453,15 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) ...@@ -462,11 +453,15 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
return 0; return 0;
register_wwan_err: register_wwan_err:
rfkill_destroy(wwan_rfkill);
if (bluetooth_rfkill) if (bluetooth_rfkill)
rfkill_unregister(bluetooth_rfkill); rfkill_unregister(bluetooth_rfkill);
register_bluetooth_error: register_bluetooth_error:
rfkill_destroy(bluetooth_rfkill);
if (wifi_rfkill) if (wifi_rfkill)
rfkill_unregister(wifi_rfkill); rfkill_unregister(wifi_rfkill);
register_wifi_error:
rfkill_destroy(wifi_rfkill);
add_sysfs_error: add_sysfs_error:
cleanup_sysfs(device); cleanup_sysfs(device);
return err; return err;
...@@ -476,12 +471,18 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device) ...@@ -476,12 +471,18 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device)
{ {
cleanup_sysfs(device); cleanup_sysfs(device);
if (wifi_rfkill) if (wifi_rfkill) {
rfkill_unregister(wifi_rfkill); rfkill_unregister(wifi_rfkill);
if (bluetooth_rfkill) rfkill_destroy(wifi_rfkill);
}
if (bluetooth_rfkill) {
rfkill_unregister(bluetooth_rfkill); rfkill_unregister(bluetooth_rfkill);
if (wwan_rfkill) rfkill_destroy(wifi_rfkill);
}
if (wwan_rfkill) {
rfkill_unregister(wwan_rfkill); rfkill_unregister(wwan_rfkill);
rfkill_destroy(wwan_rfkill);
}
return 0; return 0;
} }
......
...@@ -128,11 +128,11 @@ enum sony_nc_rfkill { ...@@ -128,11 +128,11 @@ enum sony_nc_rfkill {
SONY_BLUETOOTH, SONY_BLUETOOTH,
SONY_WWAN, SONY_WWAN,
SONY_WIMAX, SONY_WIMAX,
SONY_RFKILL_MAX, N_SONY_RFKILL,
}; };
static struct rfkill *sony_rfkill_devices[SONY_RFKILL_MAX]; static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL];
static int sony_rfkill_address[SONY_RFKILL_MAX] = {0x300, 0x500, 0x700, 0x900}; static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900};
static void sony_nc_rfkill_update(void); static void sony_nc_rfkill_update(void);
/*********** Input Devices ***********/ /*********** Input Devices ***********/
...@@ -1051,147 +1051,98 @@ static void sony_nc_rfkill_cleanup(void) ...@@ -1051,147 +1051,98 @@ static void sony_nc_rfkill_cleanup(void)
{ {
int i; int i;
for (i = 0; i < SONY_RFKILL_MAX; i++) { for (i = 0; i < N_SONY_RFKILL; i++) {
if (sony_rfkill_devices[i]) if (sony_rfkill_devices[i]) {
rfkill_unregister(sony_rfkill_devices[i]); rfkill_unregister(sony_rfkill_devices[i]);
rfkill_destroy(sony_rfkill_devices[i]);
} }
}
static int sony_nc_rfkill_get(void *data, enum rfkill_state *state)
{
int result;
int argument = sony_rfkill_address[(long) data];
sony_call_snc_handle(0x124, 0x200, &result);
if (result & 0x1) {
sony_call_snc_handle(0x124, argument, &result);
if (result & 0xf)
*state = RFKILL_STATE_UNBLOCKED;
else
*state = RFKILL_STATE_SOFT_BLOCKED;
} else {
*state = RFKILL_STATE_HARD_BLOCKED;
} }
return 0;
} }
static int sony_nc_rfkill_set(void *data, enum rfkill_state state) static int sony_nc_rfkill_set(void *data, bool blocked)
{ {
int result; int result;
int argument = sony_rfkill_address[(long) data] + 0x100; int argument = sony_rfkill_address[(long) data] + 0x100;
if (state == RFKILL_STATE_UNBLOCKED) if (!blocked)
argument |= 0xff0000; argument |= 0xff0000;
return sony_call_snc_handle(0x124, argument, &result); return sony_call_snc_handle(0x124, argument, &result);
} }
static int sony_nc_setup_wifi_rfkill(struct acpi_device *device) static const struct rfkill_ops sony_rfkill_ops = {
{ .set_block = sony_nc_rfkill_set,
int err = 0; };
struct rfkill *sony_wifi_rfkill;
sony_wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN);
if (!sony_wifi_rfkill)
return -1;
sony_wifi_rfkill->name = "sony-wifi";
sony_wifi_rfkill->toggle_radio = sony_nc_rfkill_set;
sony_wifi_rfkill->get_state = sony_nc_rfkill_get;
sony_wifi_rfkill->data = (void *)SONY_WIFI;
err = rfkill_register(sony_wifi_rfkill);
if (err)
rfkill_free(sony_wifi_rfkill);
else {
sony_rfkill_devices[SONY_WIFI] = sony_wifi_rfkill;
sony_nc_rfkill_set(sony_wifi_rfkill->data,
RFKILL_STATE_UNBLOCKED);
}
return err;
}
static int sony_nc_setup_bluetooth_rfkill(struct acpi_device *device) static int sony_nc_setup_rfkill(struct acpi_device *device,
enum sony_nc_rfkill nc_type)
{ {
int err = 0; int err = 0;
struct rfkill *sony_bluetooth_rfkill; struct rfkill *rfk;
enum rfkill_type type;
sony_bluetooth_rfkill = rfkill_allocate(&device->dev, const char *name;
RFKILL_TYPE_BLUETOOTH);
if (!sony_bluetooth_rfkill) switch (nc_type) {
return -1; case SONY_WIFI:
sony_bluetooth_rfkill->name = "sony-bluetooth"; type = RFKILL_TYPE_WLAN;
sony_bluetooth_rfkill->toggle_radio = sony_nc_rfkill_set; name = "sony-wifi";
sony_bluetooth_rfkill->get_state = sony_nc_rfkill_get; break;
sony_bluetooth_rfkill->data = (void *)SONY_BLUETOOTH; case SONY_BLUETOOTH:
err = rfkill_register(sony_bluetooth_rfkill); type = RFKILL_TYPE_BLUETOOTH;
if (err) name = "sony-bluetooth";
rfkill_free(sony_bluetooth_rfkill); break;
else { case SONY_WWAN:
sony_rfkill_devices[SONY_BLUETOOTH] = sony_bluetooth_rfkill; type = RFKILL_TYPE_WWAN;
sony_nc_rfkill_set(sony_bluetooth_rfkill->data, name = "sony-wwan";
RFKILL_STATE_UNBLOCKED); break;
case SONY_WIMAX:
type = RFKILL_TYPE_WIMAX;
name = "sony-wimax";
break;
default:
return -EINVAL;
} }
return err;
}
static int sony_nc_setup_wwan_rfkill(struct acpi_device *device) rfk = rfkill_alloc(name, &device->dev, type,
{ &sony_rfkill_ops, (void *)nc_type);
int err = 0; if (!rfk)
struct rfkill *sony_wwan_rfkill; return -ENOMEM;
sony_wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN); err = rfkill_register(rfk);
if (!sony_wwan_rfkill) if (err) {
return -1; rfkill_destroy(rfk);
sony_wwan_rfkill->name = "sony-wwan";
sony_wwan_rfkill->toggle_radio = sony_nc_rfkill_set;
sony_wwan_rfkill->get_state = sony_nc_rfkill_get;
sony_wwan_rfkill->data = (void *)SONY_WWAN;
err = rfkill_register(sony_wwan_rfkill);
if (err)
rfkill_free(sony_wwan_rfkill);
else {
sony_rfkill_devices[SONY_WWAN] = sony_wwan_rfkill;
sony_nc_rfkill_set(sony_wwan_rfkill->data,
RFKILL_STATE_UNBLOCKED);
}
return err; return err;
}
static int sony_nc_setup_wimax_rfkill(struct acpi_device *device)
{
int err = 0;
struct rfkill *sony_wimax_rfkill;
sony_wimax_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WIMAX);
if (!sony_wimax_rfkill)
return -1;
sony_wimax_rfkill->name = "sony-wimax";
sony_wimax_rfkill->toggle_radio = sony_nc_rfkill_set;
sony_wimax_rfkill->get_state = sony_nc_rfkill_get;
sony_wimax_rfkill->data = (void *)SONY_WIMAX;
err = rfkill_register(sony_wimax_rfkill);
if (err)
rfkill_free(sony_wimax_rfkill);
else {
sony_rfkill_devices[SONY_WIMAX] = sony_wimax_rfkill;
sony_nc_rfkill_set(sony_wimax_rfkill->data,
RFKILL_STATE_UNBLOCKED);
} }
sony_rfkill_devices[nc_type] = rfk;
sony_nc_rfkill_set((void *)nc_type, false);
return err; return err;
} }
static void sony_nc_rfkill_update() static void sony_nc_rfkill_update()
{ {
int i; enum sony_nc_rfkill i;
enum rfkill_state state; int result;
bool hwblock;
for (i = 0; i < SONY_RFKILL_MAX; i++) { sony_call_snc_handle(0x124, 0x200, &result);
if (sony_rfkill_devices[i]) { hwblock = !(result & 0x1);
sony_rfkill_devices[i]->
get_state(sony_rfkill_devices[i]->data, for (i = 0; i < N_SONY_RFKILL; i++) {
&state); int argument = sony_rfkill_address[i];
rfkill_force_state(sony_rfkill_devices[i], state);
if (!sony_rfkill_devices[i])
continue;
if (hwblock) {
if (rfkill_set_hw_state(sony_rfkill_devices[i], true))
sony_nc_rfkill_set(sony_rfkill_devices[i],
true);
continue;
} }
sony_call_snc_handle(0x124, argument, &result);
rfkill_set_states(sony_rfkill_devices[i],
!(result & 0xf), false);
} }
} }
...@@ -1210,13 +1161,13 @@ static int sony_nc_rfkill_setup(struct acpi_device *device) ...@@ -1210,13 +1161,13 @@ static int sony_nc_rfkill_setup(struct acpi_device *device)
} }
if (result & 0x1) if (result & 0x1)
sony_nc_setup_wifi_rfkill(device); sony_nc_setup_rfkill(device, SONY_WIFI);
if (result & 0x2) if (result & 0x2)
sony_nc_setup_bluetooth_rfkill(device); sony_nc_setup_rfkill(device, SONY_BLUETOOTH);
if (result & 0x1c) if (result & 0x1c)
sony_nc_setup_wwan_rfkill(device); sony_nc_setup_rfkill(device, SONY_WWAN);
if (result & 0x20) if (result & 0x20)
sony_nc_setup_wimax_rfkill(device); sony_nc_setup_rfkill(device, SONY_WIMAX);
return 0; return 0;
} }
......
...@@ -166,13 +166,6 @@ enum { ...@@ -166,13 +166,6 @@ enum {
#define TPACPI_MAX_ACPI_ARGS 3 #define TPACPI_MAX_ACPI_ARGS 3
/* rfkill switches */
enum {
TPACPI_RFK_BLUETOOTH_SW_ID = 0,
TPACPI_RFK_WWAN_SW_ID,
TPACPI_RFK_UWB_SW_ID,
};
/* printk headers */ /* printk headers */
#define TPACPI_LOG TPACPI_FILE ": " #define TPACPI_LOG TPACPI_FILE ": "
#define TPACPI_EMERG KERN_EMERG TPACPI_LOG #define TPACPI_EMERG KERN_EMERG TPACPI_LOG
...@@ -1005,67 +998,237 @@ static int __init tpacpi_check_std_acpi_brightness_support(void) ...@@ -1005,67 +998,237 @@ static int __init tpacpi_check_std_acpi_brightness_support(void)
return 0; return 0;
} }
static int __init tpacpi_new_rfkill(const unsigned int id, static void printk_deprecated_attribute(const char * const what,
struct rfkill **rfk, const char * const details)
{
tpacpi_log_usertask("deprecated sysfs attribute");
printk(TPACPI_WARN "WARNING: sysfs attribute %s is deprecated and "
"will be removed. %s\n",
what, details);
}
/*************************************************************************
* rfkill and radio control support helpers
*/
/*
* ThinkPad-ACPI firmware handling model:
*
* WLSW (master wireless switch) is event-driven, and is common to all
* firmware-controlled radios. It cannot be controlled, just monitored,
* as expected. It overrides all radio state in firmware
*
* The kernel, a masked-off hotkey, and WLSW can change the radio state
* (TODO: verify how WLSW interacts with the returned radio state).
*
* The only time there are shadow radio state changes, is when
* masked-off hotkeys are used.
*/
/*
* Internal driver API for radio state:
*
* int: < 0 = error, otherwise enum tpacpi_rfkill_state
* bool: true means radio blocked (off)
*/
enum tpacpi_rfkill_state {
TPACPI_RFK_RADIO_OFF = 0,
TPACPI_RFK_RADIO_ON
};
/* rfkill switches */
enum tpacpi_rfk_id {
TPACPI_RFK_BLUETOOTH_SW_ID = 0,
TPACPI_RFK_WWAN_SW_ID,
TPACPI_RFK_UWB_SW_ID,
TPACPI_RFK_SW_MAX
};
static const char *tpacpi_rfkill_names[] = {
[TPACPI_RFK_BLUETOOTH_SW_ID] = "bluetooth",
[TPACPI_RFK_WWAN_SW_ID] = "wwan",
[TPACPI_RFK_UWB_SW_ID] = "uwb",
[TPACPI_RFK_SW_MAX] = NULL
};
/* ThinkPad-ACPI rfkill subdriver */
struct tpacpi_rfk {
struct rfkill *rfkill;
enum tpacpi_rfk_id id;
const struct tpacpi_rfk_ops *ops;
};
struct tpacpi_rfk_ops {
/* firmware interface */
int (*get_status)(void);
int (*set_status)(const enum tpacpi_rfkill_state);
};
static struct tpacpi_rfk *tpacpi_rfkill_switches[TPACPI_RFK_SW_MAX];
/* Query FW and update rfkill sw state for a given rfkill switch */
static int tpacpi_rfk_update_swstate(const struct tpacpi_rfk *tp_rfk)
{
int status;
if (!tp_rfk)
return -ENODEV;
status = (tp_rfk->ops->get_status)();
if (status < 0)
return status;
rfkill_set_sw_state(tp_rfk->rfkill,
(status == TPACPI_RFK_RADIO_OFF));
return status;
}
/* Query FW and update rfkill sw state for all rfkill switches */
static void tpacpi_rfk_update_swstate_all(void)
{
unsigned int i;
for (i = 0; i < TPACPI_RFK_SW_MAX; i++)
tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[i]);
}
/*
* Sync the HW-blocking state of all rfkill switches,
* do notice it causes the rfkill core to schedule uevents
*/
static void tpacpi_rfk_update_hwblock_state(bool blocked)
{
unsigned int i;
struct tpacpi_rfk *tp_rfk;
for (i = 0; i < TPACPI_RFK_SW_MAX; i++) {
tp_rfk = tpacpi_rfkill_switches[i];
if (tp_rfk) {
if (rfkill_set_hw_state(tp_rfk->rfkill,
blocked)) {
/* ignore -- we track sw block */
}
}
}
}
/* Call to get the WLSW state from the firmware */
static int hotkey_get_wlsw(void);
/* Call to query WLSW state and update all rfkill switches */
static bool tpacpi_rfk_check_hwblock_state(void)
{
int res = hotkey_get_wlsw();
int hw_blocked;
/* When unknown or unsupported, we have to assume it is unblocked */
if (res < 0)
return false;
hw_blocked = (res == TPACPI_RFK_RADIO_OFF);
tpacpi_rfk_update_hwblock_state(hw_blocked);
return hw_blocked;
}
static int tpacpi_rfk_hook_set_block(void *data, bool blocked)
{
struct tpacpi_rfk *tp_rfk = data;
int res;
dbg_printk(TPACPI_DBG_RFKILL,
"request to change radio state to %s\n",
blocked ? "blocked" : "unblocked");
/* try to set radio state */
res = (tp_rfk->ops->set_status)(blocked ?
TPACPI_RFK_RADIO_OFF : TPACPI_RFK_RADIO_ON);
/* and update the rfkill core with whatever the FW really did */
tpacpi_rfk_update_swstate(tp_rfk);
return (res < 0) ? res : 0;
}
static const struct rfkill_ops tpacpi_rfk_rfkill_ops = {
.set_block = tpacpi_rfk_hook_set_block,
};
static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id,
const struct tpacpi_rfk_ops *tp_rfkops,
const enum rfkill_type rfktype, const enum rfkill_type rfktype,
const char *name, const char *name,
const bool set_default, const bool set_default)
int (*toggle_radio)(void *, enum rfkill_state),
int (*get_state)(void *, enum rfkill_state *))
{ {
struct tpacpi_rfk *atp_rfk;
int res; int res;
enum rfkill_state initial_state = RFKILL_STATE_SOFT_BLOCKED; bool initial_sw_state = false;
int initial_sw_status;
res = get_state(NULL, &initial_state); BUG_ON(id >= TPACPI_RFK_SW_MAX || tpacpi_rfkill_switches[id]);
if (res < 0) {
initial_sw_status = (tp_rfkops->get_status)();
if (initial_sw_status < 0) {
printk(TPACPI_ERR printk(TPACPI_ERR
"failed to read initial state for %s, error %d; " "failed to read initial state for %s, error %d; "
"will turn radio off\n", name, res); "will turn radio off\n", name, initial_sw_status);
} else if (set_default) { } else {
/* try to set the initial state as the default for the rfkill initial_sw_state = (initial_sw_status == TPACPI_RFK_RADIO_OFF);
* type, since we ask the firmware to preserve it across S5 in if (set_default) {
* NVRAM */ /* try to set the initial state as the default for the
if (rfkill_set_default(rfktype, * rfkill type, since we ask the firmware to preserve
(initial_state == RFKILL_STATE_UNBLOCKED) ? * it across S5 in NVRAM */
RFKILL_STATE_UNBLOCKED : rfkill_set_global_sw_state(rfktype, initial_sw_state);
RFKILL_STATE_SOFT_BLOCKED) == -EPERM) }
vdbg_printk(TPACPI_DBG_RFKILL, }
"Default state for %s cannot be changed\n",
name); atp_rfk = kzalloc(sizeof(struct tpacpi_rfk), GFP_KERNEL);
} if (atp_rfk)
atp_rfk->rfkill = rfkill_alloc(name,
*rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype); &tpacpi_pdev->dev,
if (!*rfk) { rfktype,
&tpacpi_rfk_rfkill_ops,
atp_rfk);
if (!atp_rfk || !atp_rfk->rfkill) {
printk(TPACPI_ERR printk(TPACPI_ERR
"failed to allocate memory for rfkill class\n"); "failed to allocate memory for rfkill class\n");
kfree(atp_rfk);
return -ENOMEM; return -ENOMEM;
} }
(*rfk)->name = name; atp_rfk->id = id;
(*rfk)->get_state = get_state; atp_rfk->ops = tp_rfkops;
(*rfk)->toggle_radio = toggle_radio;
(*rfk)->state = initial_state;
res = rfkill_register(*rfk); rfkill_set_states(atp_rfk->rfkill, initial_sw_state,
tpacpi_rfk_check_hwblock_state());
res = rfkill_register(atp_rfk->rfkill);
if (res < 0) { if (res < 0) {
printk(TPACPI_ERR printk(TPACPI_ERR
"failed to register %s rfkill switch: %d\n", "failed to register %s rfkill switch: %d\n",
name, res); name, res);
rfkill_free(*rfk); rfkill_destroy(atp_rfk->rfkill);
*rfk = NULL; kfree(atp_rfk);
return res; return res;
} }
tpacpi_rfkill_switches[id] = atp_rfk;
return 0; return 0;
} }
static void printk_deprecated_attribute(const char * const what, static void tpacpi_destroy_rfkill(const enum tpacpi_rfk_id id)
const char * const details)
{ {
tpacpi_log_usertask("deprecated sysfs attribute"); struct tpacpi_rfk *tp_rfk;
printk(TPACPI_WARN "WARNING: sysfs attribute %s is deprecated and "
"will be removed. %s\n", BUG_ON(id >= TPACPI_RFK_SW_MAX);
what, details);
tp_rfk = tpacpi_rfkill_switches[id];
if (tp_rfk) {
rfkill_unregister(tp_rfk->rfkill);
tpacpi_rfkill_switches[id] = NULL;
kfree(tp_rfk);
}
} }
static void printk_deprecated_rfkill_attribute(const char * const what) static void printk_deprecated_rfkill_attribute(const char * const what)
...@@ -1074,6 +1237,112 @@ static void printk_deprecated_rfkill_attribute(const char * const what) ...@@ -1074,6 +1237,112 @@ static void printk_deprecated_rfkill_attribute(const char * const what)
"Please switch to generic rfkill before year 2010"); "Please switch to generic rfkill before year 2010");
} }
/* sysfs <radio> enable ------------------------------------------------ */
static ssize_t tpacpi_rfk_sysfs_enable_show(const enum tpacpi_rfk_id id,
struct device_attribute *attr,
char *buf)
{
int status;
printk_deprecated_rfkill_attribute(attr->attr.name);
/* This is in the ABI... */
if (tpacpi_rfk_check_hwblock_state()) {
status = TPACPI_RFK_RADIO_OFF;
} else {
status = tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]);
if (status < 0)
return status;
}
return snprintf(buf, PAGE_SIZE, "%d\n",
(status == TPACPI_RFK_RADIO_ON) ? 1 : 0);
}
static ssize_t tpacpi_rfk_sysfs_enable_store(const enum tpacpi_rfk_id id,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long t;
int res;
printk_deprecated_rfkill_attribute(attr->attr.name);
if (parse_strtoul(buf, 1, &t))
return -EINVAL;
tpacpi_disclose_usertask(attr->attr.name, "set to %ld\n", t);
/* This is in the ABI... */
if (tpacpi_rfk_check_hwblock_state() && !!t)
return -EPERM;
res = tpacpi_rfkill_switches[id]->ops->set_status((!!t) ?
TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF);
tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]);
return (res < 0) ? res : count;
}
/* procfs -------------------------------------------------------------- */
static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, char *p)
{
int len = 0;
if (id >= TPACPI_RFK_SW_MAX)
len += sprintf(p + len, "status:\t\tnot supported\n");
else {
int status;
/* This is in the ABI... */
if (tpacpi_rfk_check_hwblock_state()) {
status = TPACPI_RFK_RADIO_OFF;
} else {
status = tpacpi_rfk_update_swstate(
tpacpi_rfkill_switches[id]);
if (status < 0)
return status;
}
len += sprintf(p + len, "status:\t\t%s\n",
(status == TPACPI_RFK_RADIO_ON) ?
"enabled" : "disabled");
len += sprintf(p + len, "commands:\tenable, disable\n");
}
return len;
}
static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf)
{
char *cmd;
int status = -1;
int res = 0;
if (id >= TPACPI_RFK_SW_MAX)
return -ENODEV;
while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "enable") == 0)
status = TPACPI_RFK_RADIO_ON;
else if (strlencmp(cmd, "disable") == 0)
status = TPACPI_RFK_RADIO_OFF;
else
return -EINVAL;
}
if (status != -1) {
tpacpi_disclose_usertask("procfs", "attempt to %s %s\n",
(status == TPACPI_RFK_RADIO_ON) ?
"enable" : "disable",
tpacpi_rfkill_names[id]);
res = (tpacpi_rfkill_switches[id]->ops->set_status)(status);
tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]);
}
return res;
}
/************************************************************************* /*************************************************************************
* thinkpad-acpi driver attributes * thinkpad-acpi driver attributes
*/ */
...@@ -1127,8 +1396,6 @@ static DRIVER_ATTR(version, S_IRUGO, ...@@ -1127,8 +1396,6 @@ static DRIVER_ATTR(version, S_IRUGO,
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
static void tpacpi_send_radiosw_update(void);
/* wlsw_emulstate ------------------------------------------------------ */ /* wlsw_emulstate ------------------------------------------------------ */
static ssize_t tpacpi_driver_wlsw_emulstate_show(struct device_driver *drv, static ssize_t tpacpi_driver_wlsw_emulstate_show(struct device_driver *drv,
char *buf) char *buf)
...@@ -1144,11 +1411,10 @@ static ssize_t tpacpi_driver_wlsw_emulstate_store(struct device_driver *drv, ...@@ -1144,11 +1411,10 @@ static ssize_t tpacpi_driver_wlsw_emulstate_store(struct device_driver *drv,
if (parse_strtoul(buf, 1, &t)) if (parse_strtoul(buf, 1, &t))
return -EINVAL; return -EINVAL;
if (tpacpi_wlsw_emulstate != t) { if (tpacpi_wlsw_emulstate != !!t) {
tpacpi_wlsw_emulstate = !!t;
tpacpi_send_radiosw_update();
} else
tpacpi_wlsw_emulstate = !!t; tpacpi_wlsw_emulstate = !!t;
tpacpi_rfk_update_hwblock_state(!t); /* negative logic */
}
return count; return count;
} }
...@@ -1463,17 +1729,23 @@ static struct attribute_set *hotkey_dev_attributes; ...@@ -1463,17 +1729,23 @@ static struct attribute_set *hotkey_dev_attributes;
/* HKEY.MHKG() return bits */ /* HKEY.MHKG() return bits */
#define TP_HOTKEY_TABLET_MASK (1 << 3) #define TP_HOTKEY_TABLET_MASK (1 << 3)
static int hotkey_get_wlsw(int *status) static int hotkey_get_wlsw(void)
{ {
int status;
if (!tp_features.hotkey_wlsw)
return -ENODEV;
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if (dbg_wlswemul) { if (dbg_wlswemul)
*status = !!tpacpi_wlsw_emulstate; return (tpacpi_wlsw_emulstate) ?
return 0; TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;
}
#endif #endif
if (!acpi_evalf(hkey_handle, status, "WLSW", "d"))
if (!acpi_evalf(hkey_handle, &status, "WLSW", "d"))
return -EIO; return -EIO;
return 0;
return (status) ? TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;
} }
static int hotkey_get_tablet_mode(int *status) static int hotkey_get_tablet_mode(int *status)
...@@ -2107,12 +2379,16 @@ static ssize_t hotkey_radio_sw_show(struct device *dev, ...@@ -2107,12 +2379,16 @@ static ssize_t hotkey_radio_sw_show(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
int res, s; int res;
res = hotkey_get_wlsw(&s); res = hotkey_get_wlsw();
if (res < 0) if (res < 0)
return res; return res;
return snprintf(buf, PAGE_SIZE, "%d\n", !!s); /* Opportunistic update */
tpacpi_rfk_update_hwblock_state((res == TPACPI_RFK_RADIO_OFF));
return snprintf(buf, PAGE_SIZE, "%d\n",
(res == TPACPI_RFK_RADIO_OFF) ? 0 : 1);
} }
static struct device_attribute dev_attr_hotkey_radio_sw = static struct device_attribute dev_attr_hotkey_radio_sw =
...@@ -2223,30 +2499,52 @@ static struct attribute *hotkey_mask_attributes[] __initdata = { ...@@ -2223,30 +2499,52 @@ static struct attribute *hotkey_mask_attributes[] __initdata = {
&dev_attr_hotkey_wakeup_hotunplug_complete.attr, &dev_attr_hotkey_wakeup_hotunplug_complete.attr,
}; };
static void bluetooth_update_rfk(void); /*
static void wan_update_rfk(void); * Sync both the hw and sw blocking state of all switches
static void uwb_update_rfk(void); */
static void tpacpi_send_radiosw_update(void) static void tpacpi_send_radiosw_update(void)
{ {
int wlsw; int wlsw;
/* Sync these BEFORE sending any rfkill events */ /*
if (tp_features.bluetooth) * We must sync all rfkill controllers *before* issuing any
bluetooth_update_rfk(); * rfkill input events, or we will race the rfkill core input
if (tp_features.wan) * handler.
wan_update_rfk(); *
if (tp_features.uwb) * tpacpi_inputdev_send_mutex works as a syncronization point
uwb_update_rfk(); * for the above.
*
* We optimize to avoid numerous calls to hotkey_get_wlsw.
*/
wlsw = hotkey_get_wlsw();
/* Sync hw blocking state first if it is hw-blocked */
if (wlsw == TPACPI_RFK_RADIO_OFF)
tpacpi_rfk_update_hwblock_state(true);
/* Sync sw blocking state */
tpacpi_rfk_update_swstate_all();
if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { /* Sync hw blocking state last if it is hw-unblocked */
if (wlsw == TPACPI_RFK_RADIO_ON)
tpacpi_rfk_update_hwblock_state(false);
/* Issue rfkill input event for WLSW switch */
if (!(wlsw < 0)) {
mutex_lock(&tpacpi_inputdev_send_mutex); mutex_lock(&tpacpi_inputdev_send_mutex);
input_report_switch(tpacpi_inputdev, input_report_switch(tpacpi_inputdev,
SW_RFKILL_ALL, !!wlsw); SW_RFKILL_ALL, (wlsw > 0));
input_sync(tpacpi_inputdev); input_sync(tpacpi_inputdev);
mutex_unlock(&tpacpi_inputdev_send_mutex); mutex_unlock(&tpacpi_inputdev_send_mutex);
} }
/*
* this can be unconditional, as we will poll state again
* if userspace uses the notify to read data
*/
hotkey_radio_sw_notify_change(); hotkey_radio_sw_notify_change();
} }
...@@ -3056,8 +3354,6 @@ enum { ...@@ -3056,8 +3354,6 @@ enum {
#define TPACPI_RFK_BLUETOOTH_SW_NAME "tpacpi_bluetooth_sw" #define TPACPI_RFK_BLUETOOTH_SW_NAME "tpacpi_bluetooth_sw"
static struct rfkill *tpacpi_bluetooth_rfkill;
static void bluetooth_suspend(pm_message_t state) static void bluetooth_suspend(pm_message_t state)
{ {
/* Try to make sure radio will resume powered off */ /* Try to make sure radio will resume powered off */
...@@ -3067,83 +3363,47 @@ static void bluetooth_suspend(pm_message_t state) ...@@ -3067,83 +3363,47 @@ static void bluetooth_suspend(pm_message_t state)
"bluetooth power down on resume request failed\n"); "bluetooth power down on resume request failed\n");
} }
static int bluetooth_get_radiosw(void) static int bluetooth_get_status(void)
{ {
int status; int status;
if (!tp_features.bluetooth)
return -ENODEV;
/* WLSW overrides bluetooth in firmware/hardware, reflect that */
if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
return RFKILL_STATE_HARD_BLOCKED;
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if (dbg_bluetoothemul) if (dbg_bluetoothemul)
return (tpacpi_bluetooth_emulstate) ? return (tpacpi_bluetooth_emulstate) ?
RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;
#endif #endif
if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
return -EIO; return -EIO;
return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ? return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ?
RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;
}
static void bluetooth_update_rfk(void)
{
int status;
if (!tpacpi_bluetooth_rfkill)
return;
status = bluetooth_get_radiosw();
if (status < 0)
return;
rfkill_force_state(tpacpi_bluetooth_rfkill, status);
vdbg_printk(TPACPI_DBG_RFKILL,
"forced rfkill state to %d\n",
status);
} }
static int bluetooth_set_radiosw(int radio_on, int update_rfk) static int bluetooth_set_status(enum tpacpi_rfkill_state state)
{ {
int status; int status;
if (!tp_features.bluetooth)
return -ENODEV;
/* WLSW overrides bluetooth in firmware/hardware, but there is no
* reason to risk weird behaviour. */
if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status
&& radio_on)
return -EPERM;
vdbg_printk(TPACPI_DBG_RFKILL, vdbg_printk(TPACPI_DBG_RFKILL,
"will %s bluetooth\n", radio_on ? "enable" : "disable"); "will attempt to %s bluetooth\n",
(state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable");
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if (dbg_bluetoothemul) { if (dbg_bluetoothemul) {
tpacpi_bluetooth_emulstate = !!radio_on; tpacpi_bluetooth_emulstate = (state == TPACPI_RFK_RADIO_ON);
if (update_rfk)
bluetooth_update_rfk();
return 0; return 0;
} }
#endif #endif
/* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */ /* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */
if (radio_on) if (state == TPACPI_RFK_RADIO_ON)
status = TP_ACPI_BLUETOOTH_RADIOSSW; status = TP_ACPI_BLUETOOTH_RADIOSSW;
else else
status = 0; status = 0;
if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
return -EIO; return -EIO;
if (update_rfk)
bluetooth_update_rfk();
return 0; return 0;
} }
...@@ -3152,35 +3412,16 @@ static ssize_t bluetooth_enable_show(struct device *dev, ...@@ -3152,35 +3412,16 @@ static ssize_t bluetooth_enable_show(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
int status; return tpacpi_rfk_sysfs_enable_show(TPACPI_RFK_BLUETOOTH_SW_ID,
attr, buf);
printk_deprecated_rfkill_attribute("bluetooth_enable");
status = bluetooth_get_radiosw();
if (status < 0)
return status;
return snprintf(buf, PAGE_SIZE, "%d\n",
(status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
} }
static ssize_t bluetooth_enable_store(struct device *dev, static ssize_t bluetooth_enable_store(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
unsigned long t; return tpacpi_rfk_sysfs_enable_store(TPACPI_RFK_BLUETOOTH_SW_ID,
int res; attr, buf, count);
printk_deprecated_rfkill_attribute("bluetooth_enable");
if (parse_strtoul(buf, 1, &t))
return -EINVAL;
tpacpi_disclose_usertask("bluetooth_enable", "set to %ld\n", t);
res = bluetooth_set_radiosw(t, 1);
return (res) ? res : count;
} }
static struct device_attribute dev_attr_bluetooth_enable = static struct device_attribute dev_attr_bluetooth_enable =
...@@ -3198,23 +3439,10 @@ static const struct attribute_group bluetooth_attr_group = { ...@@ -3198,23 +3439,10 @@ static const struct attribute_group bluetooth_attr_group = {
.attrs = bluetooth_attributes, .attrs = bluetooth_attributes,
}; };
static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state) static const struct tpacpi_rfk_ops bluetooth_tprfk_ops = {
{ .get_status = bluetooth_get_status,
int bts = bluetooth_get_radiosw(); .set_status = bluetooth_set_status,
};
if (bts < 0)
return bts;
*state = bts;
return 0;
}
static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state)
{
dbg_printk(TPACPI_DBG_RFKILL,
"request to change radio state to %d\n", state);
return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
}
static void bluetooth_shutdown(void) static void bluetooth_shutdown(void)
{ {
...@@ -3230,13 +3458,12 @@ static void bluetooth_shutdown(void) ...@@ -3230,13 +3458,12 @@ static void bluetooth_shutdown(void)
static void bluetooth_exit(void) static void bluetooth_exit(void)
{ {
bluetooth_shutdown();
if (tpacpi_bluetooth_rfkill)
rfkill_unregister(tpacpi_bluetooth_rfkill);
sysfs_remove_group(&tpacpi_pdev->dev.kobj, sysfs_remove_group(&tpacpi_pdev->dev.kobj,
&bluetooth_attr_group); &bluetooth_attr_group);
tpacpi_destroy_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID);
bluetooth_shutdown();
} }
static int __init bluetooth_init(struct ibm_init_struct *iibm) static int __init bluetooth_init(struct ibm_init_struct *iibm)
...@@ -3277,20 +3504,18 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) ...@@ -3277,20 +3504,18 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)
if (!tp_features.bluetooth) if (!tp_features.bluetooth)
return 1; return 1;
res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
&bluetooth_attr_group);
if (res)
return res;
res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID, res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID,
&tpacpi_bluetooth_rfkill, &bluetooth_tprfk_ops,
RFKILL_TYPE_BLUETOOTH, RFKILL_TYPE_BLUETOOTH,
TPACPI_RFK_BLUETOOTH_SW_NAME, TPACPI_RFK_BLUETOOTH_SW_NAME,
true, true);
tpacpi_bluetooth_rfk_set, if (res)
tpacpi_bluetooth_rfk_get); return res;
res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
&bluetooth_attr_group);
if (res) { if (res) {
bluetooth_exit(); tpacpi_destroy_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID);
return res; return res;
} }
...@@ -3300,46 +3525,12 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) ...@@ -3300,46 +3525,12 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)
/* procfs -------------------------------------------------------------- */ /* procfs -------------------------------------------------------------- */
static int bluetooth_read(char *p) static int bluetooth_read(char *p)
{ {
int len = 0; return tpacpi_rfk_procfs_read(TPACPI_RFK_BLUETOOTH_SW_ID, p);
int status = bluetooth_get_radiosw();
if (!tp_features.bluetooth)
len += sprintf(p + len, "status:\t\tnot supported\n");
else {
len += sprintf(p + len, "status:\t\t%s\n",
(status == RFKILL_STATE_UNBLOCKED) ?
"enabled" : "disabled");
len += sprintf(p + len, "commands:\tenable, disable\n");
}
return len;
} }
static int bluetooth_write(char *buf) static int bluetooth_write(char *buf)
{ {
char *cmd; return tpacpi_rfk_procfs_write(TPACPI_RFK_BLUETOOTH_SW_ID, buf);
int state = -1;
if (!tp_features.bluetooth)
return -ENODEV;
while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "enable") == 0) {
state = 1;
} else if (strlencmp(cmd, "disable") == 0) {
state = 0;
} else
return -EINVAL;
}
if (state != -1) {
tpacpi_disclose_usertask("procfs bluetooth",
"attempt to %s\n",
state ? "enable" : "disable");
bluetooth_set_radiosw(state, 1);
}
return 0;
} }
static struct ibm_struct bluetooth_driver_data = { static struct ibm_struct bluetooth_driver_data = {
...@@ -3365,8 +3556,6 @@ enum { ...@@ -3365,8 +3556,6 @@ enum {
#define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw" #define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw"
static struct rfkill *tpacpi_wan_rfkill;
static void wan_suspend(pm_message_t state) static void wan_suspend(pm_message_t state)
{ {
/* Try to make sure radio will resume powered off */ /* Try to make sure radio will resume powered off */
...@@ -3376,83 +3565,47 @@ static void wan_suspend(pm_message_t state) ...@@ -3376,83 +3565,47 @@ static void wan_suspend(pm_message_t state)
"WWAN power down on resume request failed\n"); "WWAN power down on resume request failed\n");
} }
static int wan_get_radiosw(void) static int wan_get_status(void)
{ {
int status; int status;
if (!tp_features.wan)
return -ENODEV;
/* WLSW overrides WWAN in firmware/hardware, reflect that */
if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
return RFKILL_STATE_HARD_BLOCKED;
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if (dbg_wwanemul) if (dbg_wwanemul)
return (tpacpi_wwan_emulstate) ? return (tpacpi_wwan_emulstate) ?
RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;
#endif #endif
if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
return -EIO; return -EIO;
return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ? return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ?
RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;
} }
static void wan_update_rfk(void) static int wan_set_status(enum tpacpi_rfkill_state state)
{ {
int status; int status;
if (!tpacpi_wan_rfkill)
return;
status = wan_get_radiosw();
if (status < 0)
return;
rfkill_force_state(tpacpi_wan_rfkill, status);
vdbg_printk(TPACPI_DBG_RFKILL, vdbg_printk(TPACPI_DBG_RFKILL,
"forced rfkill state to %d\n", "will attempt to %s wwan\n",
status); (state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable");
}
static int wan_set_radiosw(int radio_on, int update_rfk)
{
int status;
if (!tp_features.wan)
return -ENODEV;
/* WLSW overrides bluetooth in firmware/hardware, but there is no
* reason to risk weird behaviour. */
if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status
&& radio_on)
return -EPERM;
vdbg_printk(TPACPI_DBG_RFKILL,
"will %s WWAN\n", radio_on ? "enable" : "disable");
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if (dbg_wwanemul) { if (dbg_wwanemul) {
tpacpi_wwan_emulstate = !!radio_on; tpacpi_wwan_emulstate = (state == TPACPI_RFK_RADIO_ON);
if (update_rfk)
wan_update_rfk();
return 0; return 0;
} }
#endif #endif
/* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */ /* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */
if (radio_on) if (state == TPACPI_RFK_RADIO_ON)
status = TP_ACPI_WANCARD_RADIOSSW; status = TP_ACPI_WANCARD_RADIOSSW;
else else
status = 0; status = 0;
if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
return -EIO; return -EIO;
if (update_rfk)
wan_update_rfk();
return 0; return 0;
} }
...@@ -3461,35 +3614,16 @@ static ssize_t wan_enable_show(struct device *dev, ...@@ -3461,35 +3614,16 @@ static ssize_t wan_enable_show(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
int status; return tpacpi_rfk_sysfs_enable_show(TPACPI_RFK_WWAN_SW_ID,
attr, buf);
printk_deprecated_rfkill_attribute("wwan_enable");
status = wan_get_radiosw();
if (status < 0)
return status;
return snprintf(buf, PAGE_SIZE, "%d\n",
(status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
} }
static ssize_t wan_enable_store(struct device *dev, static ssize_t wan_enable_store(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
unsigned long t; return tpacpi_rfk_sysfs_enable_store(TPACPI_RFK_WWAN_SW_ID,
int res; attr, buf, count);
printk_deprecated_rfkill_attribute("wwan_enable");
if (parse_strtoul(buf, 1, &t))
return -EINVAL;
tpacpi_disclose_usertask("wwan_enable", "set to %ld\n", t);
res = wan_set_radiosw(t, 1);
return (res) ? res : count;
} }
static struct device_attribute dev_attr_wan_enable = static struct device_attribute dev_attr_wan_enable =
...@@ -3507,23 +3641,10 @@ static const struct attribute_group wan_attr_group = { ...@@ -3507,23 +3641,10 @@ static const struct attribute_group wan_attr_group = {
.attrs = wan_attributes, .attrs = wan_attributes,
}; };
static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state) static const struct tpacpi_rfk_ops wan_tprfk_ops = {
{ .get_status = wan_get_status,
int wans = wan_get_radiosw(); .set_status = wan_set_status,
};
if (wans < 0)
return wans;
*state = wans;
return 0;
}
static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state)
{
dbg_printk(TPACPI_DBG_RFKILL,
"request to change radio state to %d\n", state);
return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
}
static void wan_shutdown(void) static void wan_shutdown(void)
{ {
...@@ -3539,13 +3660,12 @@ static void wan_shutdown(void) ...@@ -3539,13 +3660,12 @@ static void wan_shutdown(void)
static void wan_exit(void) static void wan_exit(void)
{ {
wan_shutdown();
if (tpacpi_wan_rfkill)
rfkill_unregister(tpacpi_wan_rfkill);
sysfs_remove_group(&tpacpi_pdev->dev.kobj, sysfs_remove_group(&tpacpi_pdev->dev.kobj,
&wan_attr_group); &wan_attr_group);
tpacpi_destroy_rfkill(TPACPI_RFK_WWAN_SW_ID);
wan_shutdown();
} }
static int __init wan_init(struct ibm_init_struct *iibm) static int __init wan_init(struct ibm_init_struct *iibm)
...@@ -3584,20 +3704,19 @@ static int __init wan_init(struct ibm_init_struct *iibm) ...@@ -3584,20 +3704,19 @@ static int __init wan_init(struct ibm_init_struct *iibm)
if (!tp_features.wan) if (!tp_features.wan)
return 1; return 1;
res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
&wan_attr_group);
if (res)
return res;
res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID, res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID,
&tpacpi_wan_rfkill, &wan_tprfk_ops,
RFKILL_TYPE_WWAN, RFKILL_TYPE_WWAN,
TPACPI_RFK_WWAN_SW_NAME, TPACPI_RFK_WWAN_SW_NAME,
true, true);
tpacpi_wan_rfk_set, if (res)
tpacpi_wan_rfk_get); return res;
res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
&wan_attr_group);
if (res) { if (res) {
wan_exit(); tpacpi_destroy_rfkill(TPACPI_RFK_WWAN_SW_ID);
return res; return res;
} }
...@@ -3607,48 +3726,12 @@ static int __init wan_init(struct ibm_init_struct *iibm) ...@@ -3607,48 +3726,12 @@ static int __init wan_init(struct ibm_init_struct *iibm)
/* procfs -------------------------------------------------------------- */ /* procfs -------------------------------------------------------------- */
static int wan_read(char *p) static int wan_read(char *p)
{ {
int len = 0; return tpacpi_rfk_procfs_read(TPACPI_RFK_WWAN_SW_ID, p);
int status = wan_get_radiosw();
tpacpi_disclose_usertask("procfs wan", "read");
if (!tp_features.wan)
len += sprintf(p + len, "status:\t\tnot supported\n");
else {
len += sprintf(p + len, "status:\t\t%s\n",
(status == RFKILL_STATE_UNBLOCKED) ?
"enabled" : "disabled");
len += sprintf(p + len, "commands:\tenable, disable\n");
}
return len;
} }
static int wan_write(char *buf) static int wan_write(char *buf)
{ {
char *cmd; return tpacpi_rfk_procfs_write(TPACPI_RFK_WWAN_SW_ID, buf);
int state = -1;
if (!tp_features.wan)
return -ENODEV;
while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "enable") == 0) {
state = 1;
} else if (strlencmp(cmd, "disable") == 0) {
state = 0;
} else
return -EINVAL;
}
if (state != -1) {
tpacpi_disclose_usertask("procfs wan",
"attempt to %s\n",
state ? "enable" : "disable");
wan_set_radiosw(state, 1);
}
return 0;
} }
static struct ibm_struct wan_driver_data = { static struct ibm_struct wan_driver_data = {
...@@ -3672,108 +3755,59 @@ enum { ...@@ -3672,108 +3755,59 @@ enum {
#define TPACPI_RFK_UWB_SW_NAME "tpacpi_uwb_sw" #define TPACPI_RFK_UWB_SW_NAME "tpacpi_uwb_sw"
static struct rfkill *tpacpi_uwb_rfkill; static int uwb_get_status(void)
static int uwb_get_radiosw(void)
{ {
int status; int status;
if (!tp_features.uwb)
return -ENODEV;
/* WLSW overrides UWB in firmware/hardware, reflect that */
if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
return RFKILL_STATE_HARD_BLOCKED;
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if (dbg_uwbemul) if (dbg_uwbemul)
return (tpacpi_uwb_emulstate) ? return (tpacpi_uwb_emulstate) ?
RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;
#endif #endif
if (!acpi_evalf(hkey_handle, &status, "GUWB", "d")) if (!acpi_evalf(hkey_handle, &status, "GUWB", "d"))
return -EIO; return -EIO;
return ((status & TP_ACPI_UWB_RADIOSSW) != 0) ? return ((status & TP_ACPI_UWB_RADIOSSW) != 0) ?
RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;
}
static void uwb_update_rfk(void)
{
int status;
if (!tpacpi_uwb_rfkill)
return;
status = uwb_get_radiosw();
if (status < 0)
return;
rfkill_force_state(tpacpi_uwb_rfkill, status);
vdbg_printk(TPACPI_DBG_RFKILL,
"forced rfkill state to %d\n",
status);
} }
static int uwb_set_radiosw(int radio_on, int update_rfk) static int uwb_set_status(enum tpacpi_rfkill_state state)
{ {
int status; int status;
if (!tp_features.uwb)
return -ENODEV;
/* WLSW overrides UWB in firmware/hardware, but there is no
* reason to risk weird behaviour. */
if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status
&& radio_on)
return -EPERM;
vdbg_printk(TPACPI_DBG_RFKILL, vdbg_printk(TPACPI_DBG_RFKILL,
"will %s UWB\n", radio_on ? "enable" : "disable"); "will attempt to %s UWB\n",
(state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable");
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if (dbg_uwbemul) { if (dbg_uwbemul) {
tpacpi_uwb_emulstate = !!radio_on; tpacpi_uwb_emulstate = (state == TPACPI_RFK_RADIO_ON);
if (update_rfk)
uwb_update_rfk();
return 0; return 0;
} }
#endif #endif
status = (radio_on) ? TP_ACPI_UWB_RADIOSSW : 0; if (state == TPACPI_RFK_RADIO_ON)
status = TP_ACPI_UWB_RADIOSSW;
else
status = 0;
if (!acpi_evalf(hkey_handle, NULL, "SUWB", "vd", status)) if (!acpi_evalf(hkey_handle, NULL, "SUWB", "vd", status))
return -EIO; return -EIO;
if (update_rfk)
uwb_update_rfk();
return 0; return 0;
} }
/* --------------------------------------------------------------------- */ /* --------------------------------------------------------------------- */
static int tpacpi_uwb_rfk_get(void *data, enum rfkill_state *state) static const struct tpacpi_rfk_ops uwb_tprfk_ops = {
{ .get_status = uwb_get_status,
int uwbs = uwb_get_radiosw(); .set_status = uwb_set_status,
};
if (uwbs < 0)
return uwbs;
*state = uwbs;
return 0;
}
static int tpacpi_uwb_rfk_set(void *data, enum rfkill_state state)
{
dbg_printk(TPACPI_DBG_RFKILL,
"request to change radio state to %d\n", state);
return uwb_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
}
static void uwb_exit(void) static void uwb_exit(void)
{ {
if (tpacpi_uwb_rfkill) tpacpi_destroy_rfkill(TPACPI_RFK_UWB_SW_ID);
rfkill_unregister(tpacpi_uwb_rfkill);
} }
static int __init uwb_init(struct ibm_init_struct *iibm) static int __init uwb_init(struct ibm_init_struct *iibm)
...@@ -3813,13 +3847,10 @@ static int __init uwb_init(struct ibm_init_struct *iibm) ...@@ -3813,13 +3847,10 @@ static int __init uwb_init(struct ibm_init_struct *iibm)
return 1; return 1;
res = tpacpi_new_rfkill(TPACPI_RFK_UWB_SW_ID, res = tpacpi_new_rfkill(TPACPI_RFK_UWB_SW_ID,
&tpacpi_uwb_rfkill, &uwb_tprfk_ops,
RFKILL_TYPE_UWB, RFKILL_TYPE_UWB,
TPACPI_RFK_UWB_SW_NAME, TPACPI_RFK_UWB_SW_NAME,
false, false);
tpacpi_uwb_rfk_set,
tpacpi_uwb_rfk_get);
return res; return res;
} }
......
...@@ -45,7 +45,6 @@ ...@@ -45,7 +45,6 @@
#include <linux/backlight.h> #include <linux/backlight.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/rfkill.h> #include <linux/rfkill.h>
#include <linux/input-polldev.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -250,21 +249,15 @@ static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result) ...@@ -250,21 +249,15 @@ static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result)
struct toshiba_acpi_dev { struct toshiba_acpi_dev {
struct platform_device *p_dev; struct platform_device *p_dev;
struct rfkill *rfk_dev; struct rfkill *bt_rfk;
struct input_polled_dev *poll_dev;
const char *bt_name; const char *bt_name;
const char *rfk_name;
bool last_rfk_state;
struct mutex mutex; struct mutex mutex;
}; };
static struct toshiba_acpi_dev toshiba_acpi = { static struct toshiba_acpi_dev toshiba_acpi = {
.bt_name = "Toshiba Bluetooth", .bt_name = "Toshiba Bluetooth",
.rfk_name = "Toshiba RFKill Switch",
.last_rfk_state = false,
}; };
/* Bluetooth rfkill handlers */ /* Bluetooth rfkill handlers */
...@@ -283,21 +276,6 @@ static u32 hci_get_bt_present(bool *present) ...@@ -283,21 +276,6 @@ static u32 hci_get_bt_present(bool *present)
return hci_result; return hci_result;
} }
static u32 hci_get_bt_on(bool *on)
{
u32 hci_result;
u32 value, value2;
value = 0;
value2 = 0x0001;
hci_read2(HCI_WIRELESS, &value, &value2, &hci_result);
if (hci_result == HCI_SUCCESS)
*on = (value & HCI_WIRELESS_BT_POWER) &&
(value & HCI_WIRELESS_BT_ATTACH);
return hci_result;
}
static u32 hci_get_radio_state(bool *radio_state) static u32 hci_get_radio_state(bool *radio_state)
{ {
u32 hci_result; u32 hci_result;
...@@ -311,70 +289,67 @@ static u32 hci_get_radio_state(bool *radio_state) ...@@ -311,70 +289,67 @@ static u32 hci_get_radio_state(bool *radio_state)
return hci_result; return hci_result;
} }
static int bt_rfkill_toggle_radio(void *data, enum rfkill_state state) static int bt_rfkill_set_block(void *data, bool blocked)
{ {
struct toshiba_acpi_dev *dev = data;
u32 result1, result2; u32 result1, result2;
u32 value; u32 value;
int err;
bool radio_state; bool radio_state;
struct toshiba_acpi_dev *dev = data;
value = (state == RFKILL_STATE_UNBLOCKED); value = (blocked == false);
if (hci_get_radio_state(&radio_state) != HCI_SUCCESS) mutex_lock(&dev->mutex);
return -EFAULT; if (hci_get_radio_state(&radio_state) != HCI_SUCCESS) {
err = -EBUSY;
goto out;
}
switch (state) { if (!radio_state) {
case RFKILL_STATE_UNBLOCKED: err = 0;
if (!radio_state) goto out;
return -EPERM;
break;
case RFKILL_STATE_SOFT_BLOCKED:
break;
default:
return -EINVAL;
} }
mutex_lock(&dev->mutex);
hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1); hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1);
hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2); hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2);
mutex_unlock(&dev->mutex);
if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS) if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS)
return -EFAULT; err = -EBUSY;
else
return 0; err = 0;
out:
mutex_unlock(&dev->mutex);
return err;
} }
static void bt_poll_rfkill(struct input_polled_dev *poll_dev) static void bt_rfkill_poll(struct rfkill *rfkill, void *data)
{ {
bool state_changed;
bool new_rfk_state; bool new_rfk_state;
bool value; bool value;
u32 hci_result; u32 hci_result;
struct toshiba_acpi_dev *dev = poll_dev->private; struct toshiba_acpi_dev *dev = data;
mutex_lock(&dev->mutex);
hci_result = hci_get_radio_state(&value); hci_result = hci_get_radio_state(&value);
if (hci_result != HCI_SUCCESS) if (hci_result != HCI_SUCCESS) {
return; /* Can't do anything useful */ /* Can't do anything useful */
mutex_unlock(&dev->mutex);
}
new_rfk_state = value; new_rfk_state = value;
mutex_lock(&dev->mutex);
state_changed = new_rfk_state != dev->last_rfk_state;
dev->last_rfk_state = new_rfk_state;
mutex_unlock(&dev->mutex); mutex_unlock(&dev->mutex);
if (unlikely(state_changed)) { if (rfkill_set_hw_state(rfkill, !new_rfk_state))
rfkill_force_state(dev->rfk_dev, bt_rfkill_set_block(data, true);
new_rfk_state ?
RFKILL_STATE_SOFT_BLOCKED :
RFKILL_STATE_HARD_BLOCKED);
input_report_switch(poll_dev->input, SW_RFKILL_ALL,
new_rfk_state);
input_sync(poll_dev->input);
}
} }
static const struct rfkill_ops toshiba_rfk_ops = {
.set_block = bt_rfkill_set_block,
.poll = bt_rfkill_poll,
};
static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
static struct backlight_device *toshiba_backlight_device; static struct backlight_device *toshiba_backlight_device;
static int force_fan; static int force_fan;
...@@ -702,14 +677,11 @@ static struct backlight_ops toshiba_backlight_data = { ...@@ -702,14 +677,11 @@ static struct backlight_ops toshiba_backlight_data = {
static void toshiba_acpi_exit(void) static void toshiba_acpi_exit(void)
{ {
if (toshiba_acpi.poll_dev) { if (toshiba_acpi.bt_rfk) {
input_unregister_polled_device(toshiba_acpi.poll_dev); rfkill_unregister(toshiba_acpi.bt_rfk);
input_free_polled_device(toshiba_acpi.poll_dev); rfkill_destroy(toshiba_acpi.bt_rfk);
} }
if (toshiba_acpi.rfk_dev)
rfkill_unregister(toshiba_acpi.rfk_dev);
if (toshiba_backlight_device) if (toshiba_backlight_device)
backlight_device_unregister(toshiba_backlight_device); backlight_device_unregister(toshiba_backlight_device);
...@@ -728,8 +700,6 @@ static int __init toshiba_acpi_init(void) ...@@ -728,8 +700,6 @@ static int __init toshiba_acpi_init(void)
acpi_status status = AE_OK; acpi_status status = AE_OK;
u32 hci_result; u32 hci_result;
bool bt_present; bool bt_present;
bool bt_on;
bool radio_on;
int ret = 0; int ret = 0;
if (acpi_disabled) if (acpi_disabled)
...@@ -793,60 +763,21 @@ static int __init toshiba_acpi_init(void) ...@@ -793,60 +763,21 @@ static int __init toshiba_acpi_init(void)
/* Register rfkill switch for Bluetooth */ /* Register rfkill switch for Bluetooth */
if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) { if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) {
toshiba_acpi.rfk_dev = rfkill_allocate(&toshiba_acpi.p_dev->dev, toshiba_acpi.bt_rfk = rfkill_alloc(toshiba_acpi.bt_name,
RFKILL_TYPE_BLUETOOTH); &toshiba_acpi.p_dev->dev,
if (!toshiba_acpi.rfk_dev) { RFKILL_TYPE_BLUETOOTH,
&toshiba_rfk_ops,
&toshiba_acpi);
if (!toshiba_acpi.bt_rfk) {
printk(MY_ERR "unable to allocate rfkill device\n"); printk(MY_ERR "unable to allocate rfkill device\n");
toshiba_acpi_exit(); toshiba_acpi_exit();
return -ENOMEM; return -ENOMEM;
} }
toshiba_acpi.rfk_dev->name = toshiba_acpi.bt_name; ret = rfkill_register(toshiba_acpi.bt_rfk);
toshiba_acpi.rfk_dev->toggle_radio = bt_rfkill_toggle_radio;
toshiba_acpi.rfk_dev->data = &toshiba_acpi;
if (hci_get_bt_on(&bt_on) == HCI_SUCCESS && bt_on) {
toshiba_acpi.rfk_dev->state = RFKILL_STATE_UNBLOCKED;
} else if (hci_get_radio_state(&radio_on) == HCI_SUCCESS &&
radio_on) {
toshiba_acpi.rfk_dev->state = RFKILL_STATE_SOFT_BLOCKED;
} else {
toshiba_acpi.rfk_dev->state = RFKILL_STATE_HARD_BLOCKED;
}
ret = rfkill_register(toshiba_acpi.rfk_dev);
if (ret) { if (ret) {
printk(MY_ERR "unable to register rfkill device\n"); printk(MY_ERR "unable to register rfkill device\n");
toshiba_acpi_exit(); rfkill_destroy(toshiba_acpi.bt_rfk);
return -ENOMEM;
}
/* Register input device for kill switch */
toshiba_acpi.poll_dev = input_allocate_polled_device();
if (!toshiba_acpi.poll_dev) {
printk(MY_ERR
"unable to allocate kill-switch input device\n");
toshiba_acpi_exit();
return -ENOMEM;
}
toshiba_acpi.poll_dev->private = &toshiba_acpi;
toshiba_acpi.poll_dev->poll = bt_poll_rfkill;
toshiba_acpi.poll_dev->poll_interval = 1000; /* msecs */
toshiba_acpi.poll_dev->input->name = toshiba_acpi.rfk_name;
toshiba_acpi.poll_dev->input->id.bustype = BUS_HOST;
/* Toshiba USB ID */
toshiba_acpi.poll_dev->input->id.vendor = 0x0930;
set_bit(EV_SW, toshiba_acpi.poll_dev->input->evbit);
set_bit(SW_RFKILL_ALL, toshiba_acpi.poll_dev->input->swbit);
input_report_switch(toshiba_acpi.poll_dev->input,
SW_RFKILL_ALL, TRUE);
input_sync(toshiba_acpi.poll_dev->input);
ret = input_register_polled_device(toshiba_acpi.poll_dev);
if (ret) {
printk(MY_ERR
"unable to register kill-switch input device\n");
toshiba_acpi_exit(); toshiba_acpi_exit();
return ret; return ret;
} }
......
...@@ -311,6 +311,7 @@ unifdef-y += ptrace.h ...@@ -311,6 +311,7 @@ unifdef-y += ptrace.h
unifdef-y += qnx4_fs.h unifdef-y += qnx4_fs.h
unifdef-y += quota.h unifdef-y += quota.h
unifdef-y += random.h unifdef-y += random.h
unifdef-y += rfkill.h
unifdef-y += irqnr.h unifdef-y += irqnr.h
unifdef-y += reboot.h unifdef-y += reboot.h
unifdef-y += reiserfs_fs.h unifdef-y += reiserfs_fs.h
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
/* /*
* Copyright (C) 2006 - 2007 Ivo van Doorn * Copyright (C) 2006 - 2007 Ivo van Doorn
* Copyright (C) 2007 Dmitry Torokhov * Copyright (C) 2007 Dmitry Torokhov
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -21,6 +22,24 @@ ...@@ -21,6 +22,24 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
/* define userspace visible states */
#define RFKILL_STATE_SOFT_BLOCKED 0
#define RFKILL_STATE_UNBLOCKED 1
#define RFKILL_STATE_HARD_BLOCKED 2
/* and that's all userspace gets */
#ifdef __KERNEL__
/* don't allow anyone to use these in the kernel */
enum rfkill_user_states {
RFKILL_USER_STATE_SOFT_BLOCKED = RFKILL_STATE_SOFT_BLOCKED,
RFKILL_USER_STATE_UNBLOCKED = RFKILL_STATE_UNBLOCKED,
RFKILL_USER_STATE_HARD_BLOCKED = RFKILL_STATE_HARD_BLOCKED,
};
#undef RFKILL_STATE_SOFT_BLOCKED
#undef RFKILL_STATE_UNBLOCKED
#undef RFKILL_STATE_HARD_BLOCKED
#include <linux/types.h> #include <linux/types.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/list.h> #include <linux/list.h>
...@@ -30,109 +49,267 @@ ...@@ -30,109 +49,267 @@
/** /**
* enum rfkill_type - type of rfkill switch. * enum rfkill_type - type of rfkill switch.
* RFKILL_TYPE_WLAN: switch is on a 802.11 wireless network device. *
* RFKILL_TYPE_BLUETOOTH: switch is on a bluetooth device. * @RFKILL_TYPE_WLAN: switch is on a 802.11 wireless network device.
* RFKILL_TYPE_UWB: switch is on a ultra wideband device. * @RFKILL_TYPE_BLUETOOTH: switch is on a bluetooth device.
* RFKILL_TYPE_WIMAX: switch is on a WiMAX device. * @RFKILL_TYPE_UWB: switch is on a ultra wideband device.
* RFKILL_TYPE_WWAN: switch is on a wireless WAN device. * @RFKILL_TYPE_WIMAX: switch is on a WiMAX device.
* @RFKILL_TYPE_WWAN: switch is on a wireless WAN device.
* @NUM_RFKILL_TYPES: number of defined rfkill types
*/ */
enum rfkill_type { enum rfkill_type {
RFKILL_TYPE_WLAN , RFKILL_TYPE_WLAN,
RFKILL_TYPE_BLUETOOTH, RFKILL_TYPE_BLUETOOTH,
RFKILL_TYPE_UWB, RFKILL_TYPE_UWB,
RFKILL_TYPE_WIMAX, RFKILL_TYPE_WIMAX,
RFKILL_TYPE_WWAN, RFKILL_TYPE_WWAN,
RFKILL_TYPE_MAX, NUM_RFKILL_TYPES,
}; };
enum rfkill_state { /* this is opaque */
RFKILL_STATE_SOFT_BLOCKED = 0, /* Radio output blocked */ struct rfkill;
RFKILL_STATE_UNBLOCKED = 1, /* Radio output allowed */
RFKILL_STATE_HARD_BLOCKED = 2, /* Output blocked, non-overrideable */ /**
RFKILL_STATE_MAX, /* marker for last valid state */ * struct rfkill_ops - rfkill driver methods
*
* @poll: poll the rfkill block state(s) -- only assign this method
* when you need polling. When called, simply call one of the
* rfkill_set{,_hw,_sw}_state family of functions. If the hw
* is getting unblocked you need to take into account the return
* value of those functions to make sure the software block is
* properly used.
* @query: query the rfkill block state(s) and call exactly one of the
* rfkill_set{,_hw,_sw}_state family of functions. Assign this
* method if input events can cause hardware state changes to make
* the rfkill core query your driver before setting a requested
* block.
* @set_block: turn the transmitter on (blocked == false) or off
* (blocked == true) -- this is called only while the transmitter
* is not hard-blocked, but note that the core's view of whether
* the transmitter is hard-blocked might differ from your driver's
* view due to race conditions, so it is possible that it is still
* called at the same time as you are calling rfkill_set_hw_state().
* This callback must be assigned.
*/
struct rfkill_ops {
void (*poll)(struct rfkill *rfkill, void *data);
void (*query)(struct rfkill *rfkill, void *data);
int (*set_block)(void *data, bool blocked);
}; };
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
/** /**
* struct rfkill - rfkill control structure. * rfkill_alloc - allocate rfkill structure
* @name: Name of the switch. * @name: name of the struct -- the string is not copied internally
* @type: Radio type which the button controls, the value stored * @parent: device that has rf switch on it
* here should be a value from enum rfkill_type. * @type: type of the switch (RFKILL_TYPE_*)
* @state: State of the switch, "UNBLOCKED" means radio can operate. * @ops: rfkill methods
* @mutex: Guards switch state transitions. It serializes callbacks * @ops_data: data passed to each method
* and also protects the state. *
* @data: Pointer to the RF button drivers private data which will be * This function should be called by the transmitter driver to allocate an
* passed along when toggling radio state. * rfkill structure. Returns %NULL on failure.
* @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
* the system.
*
* This structure represents a RF switch located on a network device.
*/ */
struct rfkill { struct rfkill * __must_check rfkill_alloc(const char *name,
const char *name; struct device *parent,
enum rfkill_type type; const enum rfkill_type type,
const struct rfkill_ops *ops,
/* the mutex serializes callbacks and also protects void *ops_data);
* the state */
struct mutex mutex;
enum rfkill_state state;
void *data;
int (*toggle_radio)(void *data, enum rfkill_state state);
int (*get_state)(void *data, enum rfkill_state *state);
#ifdef CONFIG_RFKILL_LEDS /**
struct led_trigger led_trigger; * rfkill_register - Register a rfkill structure.
#endif * @rfkill: rfkill structure to be registered
*
* This function should be called by the transmitter driver to register
* the rfkill structure needs to be registered. Before calling this function
* the driver needs to be ready to service method calls from rfkill.
*/
int __must_check rfkill_register(struct rfkill *rfkill);
struct device dev; /**
struct list_head node; * rfkill_pause_polling(struct rfkill *rfkill)
enum rfkill_state state_for_resume; *
}; * Pause polling -- say transmitter is off for other reasons.
#define to_rfkill(d) container_of(d, struct rfkill, dev) * NOTE: not necessary for suspend/resume -- in that case the
* core stops polling anyway
*/
void rfkill_pause_polling(struct rfkill *rfkill);
struct rfkill * __must_check rfkill_allocate(struct device *parent, /**
enum rfkill_type type); * rfkill_resume_polling(struct rfkill *rfkill)
void rfkill_free(struct rfkill *rfkill); *
int __must_check rfkill_register(struct rfkill *rfkill); * Pause polling -- say transmitter is off for other reasons.
* NOTE: not necessary for suspend/resume -- in that case the
* core stops polling anyway
*/
void rfkill_resume_polling(struct rfkill *rfkill);
/**
* rfkill_unregister - Unregister a rfkill structure.
* @rfkill: rfkill structure to be unregistered
*
* This function should be called by the network driver during device
* teardown to destroy rfkill structure. Until it returns, the driver
* needs to be able to service method calls.
*/
void rfkill_unregister(struct rfkill *rfkill); void rfkill_unregister(struct rfkill *rfkill);
int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state); /**
int rfkill_set_default(enum rfkill_type type, enum rfkill_state state); * rfkill_destroy - free rfkill structure
* @rfkill: rfkill structure to be destroyed
*
* Destroys the rfkill structure.
*/
void rfkill_destroy(struct rfkill *rfkill);
/**
* rfkill_set_hw_state - Set the internal rfkill hardware block state
* @rfkill: pointer to the rfkill class to modify.
* @state: the current hardware block state to set
*
* rfkill drivers that get events when the hard-blocked state changes
* use this function to notify the rfkill core (and through that also
* userspace) of the current state -- they should also use this after
* resume if the state could have changed.
*
* You need not (but may) call this function if poll_state is assigned.
*
* This function can be called in any context, even from within rfkill
* callbacks.
*
* The function returns the combined block state (true if transmitter
* should be blocked) so that drivers need not keep track of the soft
* block state -- which they might not be able to.
*/
bool __must_check rfkill_set_hw_state(struct rfkill *rfkill, bool blocked);
/**
* rfkill_set_sw_state - Set the internal rfkill software block state
* @rfkill: pointer to the rfkill class to modify.
* @state: the current software block state to set
*
* rfkill drivers that get events when the soft-blocked state changes
* (yes, some platforms directly act on input but allow changing again)
* use this function to notify the rfkill core (and through that also
* userspace) of the current state -- they should also use this after
* resume if the state could have changed.
*
* This function can be called in any context, even from within rfkill
* callbacks.
*
* The function returns the combined block state (true if transmitter
* should be blocked).
*/
bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked);
/**
* rfkill_set_states - Set the internal rfkill block states
* @rfkill: pointer to the rfkill class to modify.
* @sw: the current software block state to set
* @hw: the current hardware block state to set
*
* This function can be called in any context, even from within rfkill
* callbacks.
*/
void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw);
/** /**
* rfkill_state_complement - return complementar state * rfkill_set_global_sw_state - set global sw block default
* @state: state to return the complement of * @type: rfkill type to set default for
* @blocked: default to set
* *
* Returns RFKILL_STATE_SOFT_BLOCKED if @state is RFKILL_STATE_UNBLOCKED, * This function sets the global default -- use at boot if your platform has
* returns RFKILL_STATE_UNBLOCKED otherwise. * an rfkill switch. If not early enough this call may be ignored.
*
* XXX: instead of ignoring -- how about just updating all currently
* registered drivers?
*/ */
static inline enum rfkill_state rfkill_state_complement(enum rfkill_state state) void rfkill_set_global_sw_state(const enum rfkill_type type, bool blocked);
#else /* !RFKILL */
static inline struct rfkill * __must_check
rfkill_alloc(const char *name,
struct device *parent,
const enum rfkill_type type,
const struct rfkill_ops *ops,
void *ops_data)
{
return ERR_PTR(-ENODEV);
}
static inline int __must_check rfkill_register(struct rfkill *rfkill)
{
if (rfkill == ERR_PTR(-ENODEV))
return 0;
return -EINVAL;
}
static inline void rfkill_pause_polling(struct rfkill *rfkill)
{
}
static inline void rfkill_resume_polling(struct rfkill *rfkill)
{
}
static inline void rfkill_unregister(struct rfkill *rfkill)
{
}
static inline void rfkill_destroy(struct rfkill *rfkill)
{
}
static inline bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked)
{
return blocked;
}
static inline bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
{
return blocked;
}
static inline void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw)
{
}
static inline void rfkill_set_global_sw_state(const enum rfkill_type type,
bool blocked)
{ {
return (state == RFKILL_STATE_UNBLOCKED) ?
RFKILL_STATE_SOFT_BLOCKED : RFKILL_STATE_UNBLOCKED;
} }
#endif /* RFKILL || RFKILL_MODULE */
#ifdef CONFIG_RFKILL_LEDS
/** /**
* rfkill_get_led_name - Get the LED trigger name for the button's LED. * rfkill_get_led_trigger_name - Get the LED trigger name for the button's LED.
* This function might return a NULL pointer if registering of the * This function might return a NULL pointer if registering of the
* LED trigger failed. * LED trigger failed. Use this as "default_trigger" for the LED.
* Use this as "default_trigger" for the LED.
*/ */
static inline char *rfkill_get_led_name(struct rfkill *rfkill) const char *rfkill_get_led_trigger_name(struct rfkill *rfkill);
{
#ifdef CONFIG_RFKILL_LEDS /**
return (char *)(rfkill->led_trigger.name); * rfkill_set_led_trigger_name -- set the LED trigger name
* @rfkill: rfkill struct
* @name: LED trigger name
*
* This function sets the LED trigger name of the radio LED
* trigger that rfkill creates. It is optional, but if called
* must be called before rfkill_register() to be effective.
*/
void rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name);
#else #else
static inline const char *rfkill_get_led_trigger_name(struct rfkill *rfkill)
{
return NULL; return NULL;
#endif
} }
static inline void
rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name)
{
}
#endif
#endif /* __KERNEL__ */
#endif /* RFKILL_H */ #endif /* RFKILL_H */
...@@ -253,7 +253,6 @@ ...@@ -253,7 +253,6 @@
struct net_device; struct net_device;
struct genl_info; struct genl_info;
struct wimax_dev; struct wimax_dev;
struct input_dev;
/** /**
* struct wimax_dev - Generic WiMAX device * struct wimax_dev - Generic WiMAX device
...@@ -293,8 +292,8 @@ struct input_dev; ...@@ -293,8 +292,8 @@ struct input_dev;
* See wimax_reset()'s documentation. * See wimax_reset()'s documentation.
* *
* @name: [fill] A way to identify this device. We need to register a * @name: [fill] A way to identify this device. We need to register a
* name with many subsystems (input for RFKILL, workqueue * name with many subsystems (rfkill, workqueue creation, etc).
* creation, etc). We can't use the network device name as that * We can't use the network device name as that
* might change and in some instances we don't know it yet (until * might change and in some instances we don't know it yet (until
* we don't call register_netdev()). So we generate an unique one * we don't call register_netdev()). So we generate an unique one
* using the driver name and device bus id, place it here and use * using the driver name and device bus id, place it here and use
...@@ -316,9 +315,6 @@ struct input_dev; ...@@ -316,9 +315,6 @@ struct input_dev;
* *
* @rfkill: [private] integration into the RF-Kill infrastructure. * @rfkill: [private] integration into the RF-Kill infrastructure.
* *
* @rfkill_input: [private] virtual input device to process the
* hardware RF Kill switches.
*
* @rf_sw: [private] State of the software radio switch (OFF/ON) * @rf_sw: [private] State of the software radio switch (OFF/ON)
* *
* @rf_hw: [private] State of the hardware radio switch (OFF/ON) * @rf_hw: [private] State of the hardware radio switch (OFF/ON)
......
...@@ -10,22 +10,15 @@ menuconfig RFKILL ...@@ -10,22 +10,15 @@ menuconfig RFKILL
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called rfkill. module will be called rfkill.
config RFKILL_INPUT
tristate "Input layer to RF switch connector"
depends on RFKILL && INPUT
help
Say Y here if you want kernel automatically toggle state
of RF switches on and off when user presses appropriate
button or a key on the keyboard. Without this module you
need a some kind of userspace application to control
state of the switches.
To compile this driver as a module, choose M here: the
module will be called rfkill-input.
# LED trigger support # LED trigger support
config RFKILL_LEDS config RFKILL_LEDS
bool bool
depends on RFKILL && LEDS_TRIGGERS depends on RFKILL
depends on LEDS_TRIGGERS = y || RFKILL = LEDS_TRIGGERS
default y default y
config RFKILL_INPUT
bool
depends on RFKILL
depends on INPUT = y || RFKILL = INPUT
default y
...@@ -2,5 +2,6 @@ ...@@ -2,5 +2,6 @@
# Makefile for the RF switch subsystem. # Makefile for the RF switch subsystem.
# #
rfkill-y += core.o
rfkill-$(CONFIG_RFKILL_INPUT) += input.o
obj-$(CONFIG_RFKILL) += rfkill.o obj-$(CONFIG_RFKILL) += rfkill.o
obj-$(CONFIG_RFKILL_INPUT) += rfkill-input.o
/* /*
* Copyright (C) 2006 - 2007 Ivo van Doorn * Copyright (C) 2006 - 2007 Ivo van Doorn
* Copyright (C) 2007 Dmitry Torokhov * Copyright (C) 2007 Dmitry Torokhov
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -26,161 +27,240 @@ ...@@ -26,161 +27,240 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/rfkill.h> #include <linux/rfkill.h>
#include <linux/spinlock.h>
#include "rfkill.h"
#define POLL_INTERVAL (5 * HZ)
#define RFKILL_BLOCK_HW BIT(0)
#define RFKILL_BLOCK_SW BIT(1)
#define RFKILL_BLOCK_SW_PREV BIT(2)
#define RFKILL_BLOCK_ANY (RFKILL_BLOCK_HW |\
RFKILL_BLOCK_SW |\
RFKILL_BLOCK_SW_PREV)
#define RFKILL_BLOCK_SW_SETCALL BIT(31)
struct rfkill {
spinlock_t lock;
const char *name;
enum rfkill_type type;
unsigned long state;
bool registered;
bool suspended;
const struct rfkill_ops *ops;
void *data;
#ifdef CONFIG_RFKILL_LEDS
struct led_trigger led_trigger;
const char *ledtrigname;
#endif
struct device dev;
struct list_head node;
struct delayed_work poll_work;
struct work_struct uevent_work;
struct work_struct sync_work;
};
#define to_rfkill(d) container_of(d, struct rfkill, dev)
/* Get declaration of rfkill_switch_all() to shut up sparse. */
#include "rfkill-input.h"
MODULE_AUTHOR("Ivo van Doorn <IvDoorn@gmail.com>"); MODULE_AUTHOR("Ivo van Doorn <IvDoorn@gmail.com>");
MODULE_VERSION("1.0"); MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
MODULE_DESCRIPTION("RF switch support"); MODULE_DESCRIPTION("RF switch support");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
/*
* The locking here should be made much smarter, we currently have
* a bit of a stupid situation because drivers might want to register
* the rfkill struct under their own lock, and take this lock during
* rfkill method calls -- which will cause an AB-BA deadlock situation.
*
* To fix that, we need to rework this code here to be mostly lock-free
* and only use the mutex for list manipulations, not to protect the
* various other global variables. Then we can avoid holding the mutex
* around driver operations, and all is happy.
*/
static LIST_HEAD(rfkill_list); /* list of registered rf switches */ static LIST_HEAD(rfkill_list); /* list of registered rf switches */
static DEFINE_MUTEX(rfkill_global_mutex); static DEFINE_MUTEX(rfkill_global_mutex);
static unsigned int rfkill_default_state = RFKILL_STATE_UNBLOCKED; static unsigned int rfkill_default_state = 1;
module_param_named(default_state, rfkill_default_state, uint, 0444); module_param_named(default_state, rfkill_default_state, uint, 0444);
MODULE_PARM_DESC(default_state, MODULE_PARM_DESC(default_state,
"Default initial state for all radio types, 0 = radio off"); "Default initial state for all radio types, 0 = radio off");
struct rfkill_gsw_state { static struct {
enum rfkill_state current_state; bool cur, def;
enum rfkill_state default_state; } rfkill_global_states[NUM_RFKILL_TYPES];
};
static unsigned long rfkill_states_default_locked;
static struct rfkill_gsw_state rfkill_global_states[RFKILL_TYPE_MAX];
static unsigned long rfkill_states_lockdflt[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
static bool rfkill_epo_lock_active; static bool rfkill_epo_lock_active;
#ifdef CONFIG_RFKILL_LEDS #ifdef CONFIG_RFKILL_LEDS
static void rfkill_led_trigger(struct rfkill *rfkill, static void rfkill_led_trigger_event(struct rfkill *rfkill)
enum rfkill_state state)
{ {
struct led_trigger *led = &rfkill->led_trigger; struct led_trigger *trigger;
if (!led->name) if (!rfkill->registered)
return; return;
if (state != RFKILL_STATE_UNBLOCKED)
led_trigger_event(led, LED_OFF); trigger = &rfkill->led_trigger;
if (rfkill->state & RFKILL_BLOCK_ANY)
led_trigger_event(trigger, LED_OFF);
else else
led_trigger_event(led, LED_FULL); led_trigger_event(trigger, LED_FULL);
} }
static void rfkill_led_trigger_activate(struct led_classdev *led) static void rfkill_led_trigger_activate(struct led_classdev *led)
{ {
struct rfkill *rfkill = container_of(led->trigger, struct rfkill *rfkill;
struct rfkill, led_trigger);
rfkill = container_of(led->trigger, struct rfkill, led_trigger);
rfkill_led_trigger_event(rfkill);
}
rfkill_led_trigger(rfkill, rfkill->state); const char *rfkill_get_led_trigger_name(struct rfkill *rfkill)
{
return rfkill->led_trigger.name;
}
EXPORT_SYMBOL(rfkill_get_led_trigger_name);
void rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name)
{
BUG_ON(!rfkill);
rfkill->ledtrigname = name;
}
EXPORT_SYMBOL(rfkill_set_led_trigger_name);
static int rfkill_led_trigger_register(struct rfkill *rfkill)
{
rfkill->led_trigger.name = rfkill->ledtrigname
? : dev_name(&rfkill->dev);
rfkill->led_trigger.activate = rfkill_led_trigger_activate;
return led_trigger_register(&rfkill->led_trigger);
}
static void rfkill_led_trigger_unregister(struct rfkill *rfkill)
{
led_trigger_unregister(&rfkill->led_trigger);
} }
#else #else
static inline void rfkill_led_trigger(struct rfkill *rfkill, static void rfkill_led_trigger_event(struct rfkill *rfkill)
enum rfkill_state state) {
}
static inline int rfkill_led_trigger_register(struct rfkill *rfkill)
{
return 0;
}
static inline void rfkill_led_trigger_unregister(struct rfkill *rfkill)
{ {
} }
#endif /* CONFIG_RFKILL_LEDS */ #endif /* CONFIG_RFKILL_LEDS */
static void rfkill_uevent(struct rfkill *rfkill) static void rfkill_uevent(struct rfkill *rfkill)
{ {
if (!rfkill->registered || rfkill->suspended)
return;
kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE); kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE);
} }
static void update_rfkill_state(struct rfkill *rfkill) static bool __rfkill_set_hw_state(struct rfkill *rfkill,
bool blocked, bool *change)
{ {
enum rfkill_state newstate, oldstate; unsigned long flags;
bool prev, any;
if (rfkill->get_state) { BUG_ON(!rfkill);
mutex_lock(&rfkill->mutex);
if (!rfkill->get_state(rfkill->data, &newstate)) { spin_lock_irqsave(&rfkill->lock, flags);
oldstate = rfkill->state; prev = !!(rfkill->state & RFKILL_BLOCK_HW);
rfkill->state = newstate; if (blocked)
if (oldstate != newstate) rfkill->state |= RFKILL_BLOCK_HW;
rfkill_uevent(rfkill); else
} rfkill->state &= ~RFKILL_BLOCK_HW;
mutex_unlock(&rfkill->mutex); *change = prev != blocked;
} any = rfkill->state & RFKILL_BLOCK_ANY;
rfkill_led_trigger(rfkill, rfkill->state); spin_unlock_irqrestore(&rfkill->lock, flags);
rfkill_led_trigger_event(rfkill);
return any;
} }
/** /**
* rfkill_toggle_radio - wrapper for toggle_radio hook * rfkill_set_block - wrapper for set_block method
* @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
*
* Calls rfkill->toggle_radio, enforcing the API for toggle_radio
* calls and handling all the red tape such as issuing notifications
* if the call is successful.
* *
* Suspended devices are not touched at all, and -EAGAIN is returned. * @rfkill: the rfkill struct to use
* * @blocked: the new software state
* Note that the @force parameter 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 acquired rfkill->mutex. * Calls the set_block method (when applicable) and handles notifications
* etc. as well.
*/ */
static int rfkill_toggle_radio(struct rfkill *rfkill, static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
enum rfkill_state state,
int force)
{ {
int retval = 0; unsigned long flags;
enum rfkill_state oldstate, newstate; int err;
if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP)) /*
return -EBUSY; * Some platforms (...!) generate input events which affect the
* _hard_ kill state -- whenever something tries to change the
* current software state query the hardware state too.
*/
if (rfkill->ops->query)
rfkill->ops->query(rfkill, rfkill->data);
oldstate = rfkill->state; spin_lock_irqsave(&rfkill->lock, flags);
if (rfkill->state & RFKILL_BLOCK_SW)
rfkill->state |= RFKILL_BLOCK_SW_PREV;
else
rfkill->state &= ~RFKILL_BLOCK_SW_PREV;
if (rfkill->get_state && !force && if (blocked)
!rfkill->get_state(rfkill->data, &newstate)) { rfkill->state |= RFKILL_BLOCK_SW;
rfkill->state = newstate; else
} rfkill->state &= ~RFKILL_BLOCK_SW;
switch (state) { rfkill->state |= RFKILL_BLOCK_SW_SETCALL;
case RFKILL_STATE_HARD_BLOCKED: spin_unlock_irqrestore(&rfkill->lock, flags);
/* typically happens when refreshing hardware state,
* such as on resume */ if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP))
state = RFKILL_STATE_SOFT_BLOCKED; return;
break;
case RFKILL_STATE_UNBLOCKED: err = rfkill->ops->set_block(rfkill->data, blocked);
/* 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;
default:
WARN(1, KERN_WARNING
"rfkill: illegal state %d passed as parameter "
"to rfkill_toggle_radio\n", state);
return -EINVAL;
}
if (force || state != rfkill->state) { spin_lock_irqsave(&rfkill->lock, flags);
retval = rfkill->toggle_radio(rfkill->data, state); if (err) {
/* never allow a HARD->SOFT downgrade! */ /*
if (!retval && rfkill->state != RFKILL_STATE_HARD_BLOCKED) * Failed -- reset status to _prev, this may be different
rfkill->state = state; * from what set set _PREV to earlier in this function
* if rfkill_set_sw_state was invoked.
*/
if (rfkill->state & RFKILL_BLOCK_SW_PREV)
rfkill->state |= RFKILL_BLOCK_SW;
else
rfkill->state &= ~RFKILL_BLOCK_SW;
} }
rfkill->state &= ~RFKILL_BLOCK_SW_SETCALL;
rfkill->state &= ~RFKILL_BLOCK_SW_PREV;
spin_unlock_irqrestore(&rfkill->lock, flags);
if (force || rfkill->state != oldstate) rfkill_led_trigger_event(rfkill);
rfkill_uevent(rfkill); rfkill_uevent(rfkill);
rfkill_led_trigger(rfkill, rfkill->state);
return retval;
} }
/** /**
...@@ -188,32 +268,22 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, ...@@ -188,32 +268,22 @@ static int rfkill_toggle_radio(struct rfkill *rfkill,
* @type: type of interfaces to be affected * @type: type of interfaces to be affected
* @state: the new state * @state: the new state
* *
* This function toggles the state of all switches of given type, * This function sets the state of all switches of given type,
* unless a specific switch is claimed by userspace (in which case, * unless a specific switch is claimed by userspace (in which case,
* that switch is left alone) or suspended. * that switch is left alone) or suspended.
* *
* Caller must have acquired rfkill_global_mutex. * Caller must have acquired rfkill_global_mutex.
*/ */
static void __rfkill_switch_all(const enum rfkill_type type, static void __rfkill_switch_all(const enum rfkill_type type, bool blocked)
const enum rfkill_state state)
{ {
struct rfkill *rfkill; struct rfkill *rfkill;
if (WARN((state >= RFKILL_STATE_MAX || type >= RFKILL_TYPE_MAX), rfkill_global_states[type].cur = blocked;
KERN_WARNING
"rfkill: illegal state %d or type %d "
"passed as parameter to __rfkill_switch_all\n",
state, type))
return;
rfkill_global_states[type].current_state = state;
list_for_each_entry(rfkill, &rfkill_list, node) { list_for_each_entry(rfkill, &rfkill_list, node) {
if (rfkill->type == type) { if (rfkill->type != type)
mutex_lock(&rfkill->mutex); continue;
rfkill_toggle_radio(rfkill, state, 0);
mutex_unlock(&rfkill->mutex); rfkill_set_block(rfkill, blocked);
rfkill_led_trigger(rfkill, rfkill->state);
}
} }
} }
...@@ -227,14 +297,15 @@ static void __rfkill_switch_all(const enum rfkill_type type, ...@@ -227,14 +297,15 @@ static void __rfkill_switch_all(const enum rfkill_type type,
* *
* Does nothing if the EPO lock is active. * Does nothing if the EPO lock is active.
*/ */
void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) void rfkill_switch_all(enum rfkill_type type, bool blocked)
{ {
mutex_lock(&rfkill_global_mutex); mutex_lock(&rfkill_global_mutex);
if (!rfkill_epo_lock_active) if (!rfkill_epo_lock_active)
__rfkill_switch_all(type, state); __rfkill_switch_all(type, blocked);
mutex_unlock(&rfkill_global_mutex); mutex_unlock(&rfkill_global_mutex);
} }
EXPORT_SYMBOL(rfkill_switch_all);
/** /**
* rfkill_epo - emergency power off all transmitters * rfkill_epo - emergency power off all transmitters
...@@ -253,21 +324,15 @@ void rfkill_epo(void) ...@@ -253,21 +324,15 @@ void rfkill_epo(void)
mutex_lock(&rfkill_global_mutex); mutex_lock(&rfkill_global_mutex);
rfkill_epo_lock_active = true; rfkill_epo_lock_active = true;
list_for_each_entry(rfkill, &rfkill_list, node) { list_for_each_entry(rfkill, &rfkill_list, node)
mutex_lock(&rfkill->mutex); rfkill_set_block(rfkill, true);
rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
mutex_unlock(&rfkill->mutex); for (i = 0; i < NUM_RFKILL_TYPES; i++) {
} rfkill_global_states[i].def = rfkill_global_states[i].cur;
for (i = 0; i < RFKILL_TYPE_MAX; i++) { rfkill_global_states[i].cur = true;
rfkill_global_states[i].default_state =
rfkill_global_states[i].current_state;
rfkill_global_states[i].current_state =
RFKILL_STATE_SOFT_BLOCKED;
} }
mutex_unlock(&rfkill_global_mutex); mutex_unlock(&rfkill_global_mutex);
rfkill_led_trigger(rfkill, rfkill->state);
} }
EXPORT_SYMBOL_GPL(rfkill_epo);
/** /**
* rfkill_restore_states - restore global states * rfkill_restore_states - restore global states
...@@ -283,11 +348,10 @@ void rfkill_restore_states(void) ...@@ -283,11 +348,10 @@ void rfkill_restore_states(void)
mutex_lock(&rfkill_global_mutex); mutex_lock(&rfkill_global_mutex);
rfkill_epo_lock_active = false; rfkill_epo_lock_active = false;
for (i = 0; i < RFKILL_TYPE_MAX; i++) for (i = 0; i < NUM_RFKILL_TYPES; i++)
__rfkill_switch_all(i, rfkill_global_states[i].default_state); __rfkill_switch_all(i, rfkill_global_states[i].def);
mutex_unlock(&rfkill_global_mutex); mutex_unlock(&rfkill_global_mutex);
} }
EXPORT_SYMBOL_GPL(rfkill_restore_states);
/** /**
* rfkill_remove_epo_lock - unlock state changes * rfkill_remove_epo_lock - unlock state changes
...@@ -301,7 +365,6 @@ void rfkill_remove_epo_lock(void) ...@@ -301,7 +365,6 @@ void rfkill_remove_epo_lock(void)
rfkill_epo_lock_active = false; rfkill_epo_lock_active = false;
mutex_unlock(&rfkill_global_mutex); mutex_unlock(&rfkill_global_mutex);
} }
EXPORT_SYMBOL_GPL(rfkill_remove_epo_lock);
/** /**
* rfkill_is_epo_lock_active - returns true EPO is active * rfkill_is_epo_lock_active - returns true EPO is active
...@@ -316,63 +379,125 @@ bool rfkill_is_epo_lock_active(void) ...@@ -316,63 +379,125 @@ bool rfkill_is_epo_lock_active(void)
{ {
return rfkill_epo_lock_active; return rfkill_epo_lock_active;
} }
EXPORT_SYMBOL_GPL(rfkill_is_epo_lock_active);
/** /**
* rfkill_get_global_state - returns global state for a type * rfkill_get_global_sw_state - returns global state for a type
* @type: the type to get the global state of * @type: the type to get the global state of
* *
* Returns the current global state for a given wireless * Returns the current global state for a given wireless
* device type. * device type.
*/ */
enum rfkill_state rfkill_get_global_state(const enum rfkill_type type) bool rfkill_get_global_sw_state(const enum rfkill_type type)
{ {
return rfkill_global_states[type].current_state; return rfkill_global_states[type].cur;
} }
EXPORT_SYMBOL_GPL(rfkill_get_global_state);
/** void rfkill_set_global_sw_state(const enum rfkill_type type, bool blocked)
* rfkill_force_state - Force the internal rfkill radio state {
* @rfkill: pointer to the rfkill class to modify. mutex_lock(&rfkill_global_mutex);
* @state: the current radio state the class should be forced to.
* /* don't allow unblock when epo */
* This function updates the internal state of the radio cached if (rfkill_epo_lock_active && !blocked)
* by the rfkill class. It should be used when the driver gets goto out;
* a notification by the firmware/hardware of the current *real*
* state of the radio rfkill switch. /* too late */
* if (rfkill_states_default_locked & BIT(type))
* Devices which are subject to external changes on their rfkill goto out;
* state (such as those caused by a hardware rfkill line) MUST
* have their driver arrange to call rfkill_force_state() as soon rfkill_states_default_locked |= BIT(type);
* as possible after such a change.
* rfkill_global_states[type].cur = blocked;
* This function may not be called from an atomic context. rfkill_global_states[type].def = blocked;
*/ out:
int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) mutex_unlock(&rfkill_global_mutex);
}
EXPORT_SYMBOL(rfkill_set_global_sw_state);
bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked)
{ {
enum rfkill_state oldstate; bool ret, change;
ret = __rfkill_set_hw_state(rfkill, blocked, &change);
if (!rfkill->registered)
return ret;
if (change)
schedule_work(&rfkill->uevent_work);
return ret;
}
EXPORT_SYMBOL(rfkill_set_hw_state);
static void __rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
{
u32 bit = RFKILL_BLOCK_SW;
/* if in a ops->set_block right now, use other bit */
if (rfkill->state & RFKILL_BLOCK_SW_SETCALL)
bit = RFKILL_BLOCK_SW_PREV;
if (blocked)
rfkill->state |= bit;
else
rfkill->state &= ~bit;
}
bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
{
unsigned long flags;
bool prev, hwblock;
BUG_ON(!rfkill); BUG_ON(!rfkill);
if (WARN((state >= RFKILL_STATE_MAX),
KERN_WARNING
"rfkill: illegal state %d passed as parameter "
"to rfkill_force_state\n", state))
return -EINVAL;
mutex_lock(&rfkill->mutex); spin_lock_irqsave(&rfkill->lock, flags);
prev = !!(rfkill->state & RFKILL_BLOCK_SW);
__rfkill_set_sw_state(rfkill, blocked);
hwblock = !!(rfkill->state & RFKILL_BLOCK_HW);
blocked = blocked || hwblock;
spin_unlock_irqrestore(&rfkill->lock, flags);
oldstate = rfkill->state; if (!rfkill->registered)
rfkill->state = state; return blocked;
if (state != oldstate) if (prev != blocked && !hwblock)
rfkill_uevent(rfkill); schedule_work(&rfkill->uevent_work);
mutex_unlock(&rfkill->mutex); rfkill_led_trigger_event(rfkill);
rfkill_led_trigger(rfkill, rfkill->state);
return 0; return blocked;
} }
EXPORT_SYMBOL(rfkill_force_state); EXPORT_SYMBOL(rfkill_set_sw_state);
void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw)
{
unsigned long flags;
bool swprev, hwprev;
BUG_ON(!rfkill);
spin_lock_irqsave(&rfkill->lock, flags);
/*
* No need to care about prev/setblock ... this is for uevent only
* and that will get triggered by rfkill_set_block anyway.
*/
swprev = !!(rfkill->state & RFKILL_BLOCK_SW);
hwprev = !!(rfkill->state & RFKILL_BLOCK_HW);
__rfkill_set_sw_state(rfkill, sw);
spin_unlock_irqrestore(&rfkill->lock, flags);
if (!rfkill->registered)
return;
if (swprev != sw || hwprev != hw)
schedule_work(&rfkill->uevent_work);
rfkill_led_trigger_event(rfkill);
}
EXPORT_SYMBOL(rfkill_set_states);
static ssize_t rfkill_name_show(struct device *dev, static ssize_t rfkill_name_show(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
...@@ -399,6 +524,8 @@ static const char *rfkill_get_type_str(enum rfkill_type type) ...@@ -399,6 +524,8 @@ static const char *rfkill_get_type_str(enum rfkill_type type)
default: default:
BUG(); BUG();
} }
BUILD_BUG_ON(NUM_RFKILL_TYPES != RFKILL_TYPE_WWAN + 1);
} }
static ssize_t rfkill_type_show(struct device *dev, static ssize_t rfkill_type_show(struct device *dev,
...@@ -410,48 +537,44 @@ static ssize_t rfkill_type_show(struct device *dev, ...@@ -410,48 +537,44 @@ static ssize_t rfkill_type_show(struct device *dev,
return sprintf(buf, "%s\n", rfkill_get_type_str(rfkill->type)); return sprintf(buf, "%s\n", rfkill_get_type_str(rfkill->type));
} }
static u8 user_state_from_blocked(unsigned long state)
{
if (state & RFKILL_BLOCK_HW)
return RFKILL_USER_STATE_HARD_BLOCKED;
if (state & RFKILL_BLOCK_SW)
return RFKILL_USER_STATE_SOFT_BLOCKED;
return RFKILL_USER_STATE_UNBLOCKED;
}
static ssize_t rfkill_state_show(struct device *dev, static ssize_t rfkill_state_show(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
struct rfkill *rfkill = to_rfkill(dev); struct rfkill *rfkill = to_rfkill(dev);
unsigned long flags;
u32 state;
update_rfkill_state(rfkill); spin_lock_irqsave(&rfkill->lock, flags);
return sprintf(buf, "%d\n", rfkill->state); state = rfkill->state;
spin_unlock_irqrestore(&rfkill->lock, flags);
return sprintf(buf, "%d\n", user_state_from_blocked(state));
} }
static ssize_t rfkill_state_store(struct device *dev, static ssize_t rfkill_state_store(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct rfkill *rfkill = to_rfkill(dev); /*
unsigned long state; * The intention was that userspace can only take control over
int error; * a given device when/if rfkill-input doesn't control it due
* to user_claim. Since user_claim is currently unsupported,
* we never support changing the state from userspace -- this
* can be implemented again later.
*/
if (!capable(CAP_NET_ADMIN))
return -EPERM; return -EPERM;
error = strict_strtoul(buf, 0, &state);
if (error)
return error;
/* RFKILL_STATE_HARD_BLOCKED is illegal here... */
if (state != RFKILL_STATE_UNBLOCKED &&
state != RFKILL_STATE_SOFT_BLOCKED)
return -EINVAL;
error = mutex_lock_killable(&rfkill->mutex);
if (error)
return error;
if (!rfkill_epo_lock_active)
error = rfkill_toggle_radio(rfkill, state, 0);
else
error = -EPERM;
mutex_unlock(&rfkill->mutex);
return error ? error : count;
} }
static ssize_t rfkill_claim_show(struct device *dev, static ssize_t rfkill_claim_show(struct device *dev,
...@@ -481,69 +604,13 @@ static void rfkill_release(struct device *dev) ...@@ -481,69 +604,13 @@ static void rfkill_release(struct device *dev)
struct rfkill *rfkill = to_rfkill(dev); struct rfkill *rfkill = to_rfkill(dev);
kfree(rfkill); kfree(rfkill);
module_put(THIS_MODULE);
}
#ifdef CONFIG_PM
static int rfkill_suspend(struct device *dev, pm_message_t state)
{
struct rfkill *rfkill = to_rfkill(dev);
/* mark class device as suspended */
if (dev->power.power_state.event != state.event)
dev->power.power_state = state;
/* store state for the resume handler */
rfkill->state_for_resume = rfkill->state;
return 0;
}
static int rfkill_resume(struct device *dev)
{
struct rfkill *rfkill = to_rfkill(dev);
enum rfkill_state newstate;
if (dev->power.power_state.event != PM_EVENT_ON) {
mutex_lock(&rfkill->mutex);
dev->power.power_state.event = PM_EVENT_ON;
/*
* rfkill->state could have been modified before we got
* called, and won't be updated by rfkill_toggle_radio()
* in force mode. Sync it FIRST.
*/
if (rfkill->get_state &&
!rfkill->get_state(rfkill->data, &newstate))
rfkill->state = newstate;
/*
* If we are under EPO, kick transmitter offline,
* otherwise restore to pre-suspend state.
*
* Issue a notification in any case
*/
rfkill_toggle_radio(rfkill,
rfkill_epo_lock_active ?
RFKILL_STATE_SOFT_BLOCKED :
rfkill->state_for_resume,
1);
mutex_unlock(&rfkill->mutex);
rfkill_led_trigger(rfkill, rfkill->state);
}
return 0;
} }
#else
#define rfkill_suspend NULL
#define rfkill_resume NULL
#endif
static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env) static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
{ {
struct rfkill *rfkill = to_rfkill(dev); struct rfkill *rfkill = to_rfkill(dev);
unsigned long flags;
u32 state;
int error; int error;
error = add_uevent_var(env, "RFKILL_NAME=%s", rfkill->name); error = add_uevent_var(env, "RFKILL_NAME=%s", rfkill->name);
...@@ -553,303 +620,277 @@ static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env) ...@@ -553,303 +620,277 @@ static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
rfkill_get_type_str(rfkill->type)); rfkill_get_type_str(rfkill->type));
if (error) if (error)
return error; return error;
error = add_uevent_var(env, "RFKILL_STATE=%d", rfkill->state); spin_lock_irqsave(&rfkill->lock, flags);
state = rfkill->state;
spin_unlock_irqrestore(&rfkill->lock, flags);
error = add_uevent_var(env, "RFKILL_STATE=%d",
user_state_from_blocked(state));
return error; return error;
} }
static struct class rfkill_class = { void rfkill_pause_polling(struct rfkill *rfkill)
.name = "rfkill",
.dev_release = rfkill_release,
.dev_attrs = rfkill_dev_attrs,
.suspend = rfkill_suspend,
.resume = rfkill_resume,
.dev_uevent = rfkill_dev_uevent,
};
static int rfkill_check_duplicity(const struct rfkill *rfkill)
{ {
struct rfkill *p; BUG_ON(!rfkill);
unsigned long seen[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
memset(seen, 0, sizeof(seen));
list_for_each_entry(p, &rfkill_list, node) { if (!rfkill->ops->poll)
if (WARN((p == rfkill), KERN_WARNING return;
"rfkill: illegal attempt to register "
"an already registered rfkill struct\n"))
return -EEXIST;
set_bit(p->type, seen);
}
/* 0: first switch of its kind */ cancel_delayed_work_sync(&rfkill->poll_work);
return (test_bit(rfkill->type, seen)) ? 1 : 0;
} }
EXPORT_SYMBOL(rfkill_pause_polling);
static int rfkill_add_switch(struct rfkill *rfkill) void rfkill_resume_polling(struct rfkill *rfkill)
{ {
int error; BUG_ON(!rfkill);
mutex_lock(&rfkill_global_mutex);
error = rfkill_check_duplicity(rfkill); if (!rfkill->ops->poll)
if (error < 0) return;
goto unlock_out;
if (!error) { schedule_work(&rfkill->poll_work.work);
/* lock default after first use */ }
set_bit(rfkill->type, rfkill_states_lockdflt); EXPORT_SYMBOL(rfkill_resume_polling);
rfkill_global_states[rfkill->type].current_state =
rfkill_global_states[rfkill->type].default_state;
}
rfkill_toggle_radio(rfkill, static int rfkill_suspend(struct device *dev, pm_message_t state)
rfkill_global_states[rfkill->type].current_state, {
0); struct rfkill *rfkill = to_rfkill(dev);
list_add_tail(&rfkill->node, &rfkill_list); rfkill_pause_polling(rfkill);
error = 0; rfkill->suspended = true;
unlock_out:
mutex_unlock(&rfkill_global_mutex);
return error; return 0;
} }
static void rfkill_remove_switch(struct rfkill *rfkill) static int rfkill_resume(struct device *dev)
{ {
struct rfkill *rfkill = to_rfkill(dev);
bool cur;
mutex_lock(&rfkill_global_mutex); mutex_lock(&rfkill_global_mutex);
list_del_init(&rfkill->node); cur = rfkill_global_states[rfkill->type].cur;
rfkill_set_block(rfkill, cur);
mutex_unlock(&rfkill_global_mutex); mutex_unlock(&rfkill_global_mutex);
mutex_lock(&rfkill->mutex); rfkill->suspended = false;
rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
mutex_unlock(&rfkill->mutex); schedule_work(&rfkill->uevent_work);
rfkill_resume_polling(rfkill);
return 0;
} }
/** static struct class rfkill_class = {
* rfkill_allocate - allocate memory for rfkill structure. .name = "rfkill",
* @parent: device that has rf switch on it .dev_release = rfkill_release,
* @type: type of the switch (RFKILL_TYPE_*) .dev_attrs = rfkill_dev_attrs,
* .dev_uevent = rfkill_dev_uevent,
* This function should be called by the network driver when it needs .suspend = rfkill_suspend,
* rfkill structure. Once the structure is allocated the driver should .resume = rfkill_resume,
* finish its initialization by setting the name, private data, enable_radio };
* and disable_radio methods and then register it with rfkill_register().
*
* NOTE: If registration fails the structure shoudl be freed by calling struct rfkill * __must_check rfkill_alloc(const char *name,
* rfkill_free() otherwise rfkill_unregister() should be used. struct device *parent,
*/ const enum rfkill_type type,
struct rfkill * __must_check rfkill_allocate(struct device *parent, const struct rfkill_ops *ops,
enum rfkill_type type) void *ops_data)
{ {
struct rfkill *rfkill; struct rfkill *rfkill;
struct device *dev; struct device *dev;
if (WARN((type >= RFKILL_TYPE_MAX), if (WARN_ON(!ops))
KERN_WARNING
"rfkill: illegal type %d passed as parameter "
"to rfkill_allocate\n", type))
return NULL; return NULL;
rfkill = kzalloc(sizeof(struct rfkill), GFP_KERNEL); if (WARN_ON(!ops->set_block))
return NULL;
if (WARN_ON(!name))
return NULL;
if (WARN_ON(type >= NUM_RFKILL_TYPES))
return NULL;
rfkill = kzalloc(sizeof(*rfkill), GFP_KERNEL);
if (!rfkill) if (!rfkill)
return NULL; return NULL;
mutex_init(&rfkill->mutex); spin_lock_init(&rfkill->lock);
INIT_LIST_HEAD(&rfkill->node); INIT_LIST_HEAD(&rfkill->node);
rfkill->type = type; rfkill->type = type;
rfkill->name = name;
rfkill->ops = ops;
rfkill->data = ops_data;
dev = &rfkill->dev; dev = &rfkill->dev;
dev->class = &rfkill_class; dev->class = &rfkill_class;
dev->parent = parent; dev->parent = parent;
device_initialize(dev); device_initialize(dev);
__module_get(THIS_MODULE);
return rfkill; return rfkill;
} }
EXPORT_SYMBOL(rfkill_allocate); EXPORT_SYMBOL(rfkill_alloc);
/** static void rfkill_poll(struct work_struct *work)
* rfkill_free - Mark rfkill structure for deletion
* @rfkill: rfkill structure to be destroyed
*
* Decrements reference count of the rfkill structure so it is destroyed.
* Note that rfkill_free() should _not_ be called after rfkill_unregister().
*/
void rfkill_free(struct rfkill *rfkill)
{ {
if (rfkill) struct rfkill *rfkill;
put_device(&rfkill->dev);
rfkill = container_of(work, struct rfkill, poll_work.work);
/*
* Poll hardware state -- driver will use one of the
* rfkill_set{,_hw,_sw}_state functions and use its
* return value to update the current status.
*/
rfkill->ops->poll(rfkill, rfkill->data);
schedule_delayed_work(&rfkill->poll_work,
round_jiffies_relative(POLL_INTERVAL));
} }
EXPORT_SYMBOL(rfkill_free);
static void rfkill_led_trigger_register(struct rfkill *rfkill) static void rfkill_uevent_work(struct work_struct *work)
{ {
#ifdef CONFIG_RFKILL_LEDS struct rfkill *rfkill;
int error;
if (!rfkill->led_trigger.name) rfkill = container_of(work, struct rfkill, uevent_work);
rfkill->led_trigger.name = dev_name(&rfkill->dev);
if (!rfkill->led_trigger.activate) rfkill_uevent(rfkill);
rfkill->led_trigger.activate = rfkill_led_trigger_activate;
error = led_trigger_register(&rfkill->led_trigger);
if (error)
rfkill->led_trigger.name = NULL;
#endif /* CONFIG_RFKILL_LEDS */
} }
static void rfkill_led_trigger_unregister(struct rfkill *rfkill) static void rfkill_sync_work(struct work_struct *work)
{ {
#ifdef CONFIG_RFKILL_LEDS struct rfkill *rfkill;
if (rfkill->led_trigger.name) { bool cur;
led_trigger_unregister(&rfkill->led_trigger);
rfkill->led_trigger.name = NULL; rfkill = container_of(work, struct rfkill, sync_work);
}
#endif mutex_lock(&rfkill_global_mutex);
cur = rfkill_global_states[rfkill->type].cur;
rfkill_set_block(rfkill, cur);
mutex_unlock(&rfkill_global_mutex);
} }
/**
* rfkill_register - Register a rfkill structure.
* @rfkill: rfkill structure to be registered
*
* This function should be called by the network driver when the rfkill
* structure needs to be registered. Immediately from registration the
* switch driver should be able to service calls to toggle_radio.
*/
int __must_check rfkill_register(struct rfkill *rfkill) int __must_check rfkill_register(struct rfkill *rfkill)
{ {
static atomic_t rfkill_no = ATOMIC_INIT(0); static unsigned long rfkill_no;
struct device *dev = &rfkill->dev; struct device *dev = &rfkill->dev;
int error; int error;
if (WARN((!rfkill || !rfkill->toggle_radio || BUG_ON(!rfkill);
rfkill->type >= RFKILL_TYPE_MAX ||
rfkill->state >= RFKILL_STATE_MAX),
KERN_WARNING
"rfkill: attempt to register a "
"badly initialized rfkill struct\n"))
return -EINVAL;
dev_set_name(dev, "rfkill%ld", (long)atomic_inc_return(&rfkill_no) - 1); mutex_lock(&rfkill_global_mutex);
rfkill_led_trigger_register(rfkill); if (rfkill->registered) {
error = -EALREADY;
goto unlock;
}
error = rfkill_add_switch(rfkill); dev_set_name(dev, "rfkill%lu", rfkill_no);
if (error) { rfkill_no++;
rfkill_led_trigger_unregister(rfkill);
return error; if (!(rfkill_states_default_locked & BIT(rfkill->type))) {
/* first of its kind */
BUILD_BUG_ON(NUM_RFKILL_TYPES >
sizeof(rfkill_states_default_locked) * 8);
rfkill_states_default_locked |= BIT(rfkill->type);
rfkill_global_states[rfkill->type].cur =
rfkill_global_states[rfkill->type].def;
} }
list_add_tail(&rfkill->node, &rfkill_list);
error = device_add(dev); error = device_add(dev);
if (error) { if (error)
rfkill_remove_switch(rfkill); goto remove;
rfkill_led_trigger_unregister(rfkill);
return error; error = rfkill_led_trigger_register(rfkill);
if (error)
goto devdel;
rfkill->registered = true;
if (rfkill->ops->poll) {
INIT_DELAYED_WORK(&rfkill->poll_work, rfkill_poll);
schedule_delayed_work(&rfkill->poll_work,
round_jiffies_relative(POLL_INTERVAL));
} }
INIT_WORK(&rfkill->uevent_work, rfkill_uevent_work);
INIT_WORK(&rfkill->sync_work, rfkill_sync_work);
schedule_work(&rfkill->sync_work);
mutex_unlock(&rfkill_global_mutex);
return 0; return 0;
devdel:
device_del(&rfkill->dev);
remove:
list_del_init(&rfkill->node);
unlock:
mutex_unlock(&rfkill_global_mutex);
return error;
} }
EXPORT_SYMBOL(rfkill_register); EXPORT_SYMBOL(rfkill_register);
/**
* rfkill_unregister - Unregister a rfkill structure.
* @rfkill: rfkill structure to be unregistered
*
* This function should be called by the network driver during device
* teardown to destroy rfkill structure. Note that rfkill_free() should
* _not_ be called after rfkill_unregister().
*/
void rfkill_unregister(struct rfkill *rfkill) void rfkill_unregister(struct rfkill *rfkill)
{ {
BUG_ON(!rfkill); BUG_ON(!rfkill);
device_del(&rfkill->dev);
rfkill_remove_switch(rfkill);
rfkill_led_trigger_unregister(rfkill);
put_device(&rfkill->dev);
}
EXPORT_SYMBOL(rfkill_unregister);
/** if (rfkill->ops->poll)
* rfkill_set_default - set initial value for a switch type cancel_delayed_work_sync(&rfkill->poll_work);
* @type - the type of switch to set the default state of
* @state - the new default state for that group of switches
*
* Sets the initial state rfkill should use for a given type.
* The following initial states are allowed: RFKILL_STATE_SOFT_BLOCKED
* and RFKILL_STATE_UNBLOCKED.
*
* This function is meant to be used by platform drivers for platforms
* that can save switch state across power down/reboot.
*
* The default state for each switch type can be changed exactly once.
* After a switch of that type is registered, the default state cannot
* be changed anymore. This guards against multiple drivers it the
* same platform trying to set the initial switch default state, which
* is not allowed.
*
* Returns -EPERM if the state has already been set once or is in use,
* so drivers likely want to either ignore or at most printk(KERN_NOTICE)
* if this function returns -EPERM.
*
* Returns 0 if the new default state was set, or an error if it
* could not be set.
*/
int rfkill_set_default(enum rfkill_type type, enum rfkill_state state)
{
int error;
if (WARN((type >= RFKILL_TYPE_MAX || cancel_work_sync(&rfkill->uevent_work);
(state != RFKILL_STATE_SOFT_BLOCKED && cancel_work_sync(&rfkill->sync_work);
state != RFKILL_STATE_UNBLOCKED)),
KERN_WARNING
"rfkill: illegal state %d or type %d passed as "
"parameter to rfkill_set_default\n", state, type))
return -EINVAL;
mutex_lock(&rfkill_global_mutex); rfkill->registered = false;
if (!test_and_set_bit(type, rfkill_states_lockdflt)) { device_del(&rfkill->dev);
rfkill_global_states[type].default_state = state;
rfkill_global_states[type].current_state = state;
error = 0;
} else
error = -EPERM;
mutex_lock(&rfkill_global_mutex);
list_del_init(&rfkill->node);
mutex_unlock(&rfkill_global_mutex); mutex_unlock(&rfkill_global_mutex);
return error;
rfkill_led_trigger_unregister(rfkill);
} }
EXPORT_SYMBOL_GPL(rfkill_set_default); EXPORT_SYMBOL(rfkill_unregister);
void rfkill_destroy(struct rfkill *rfkill)
{
if (rfkill)
put_device(&rfkill->dev);
}
EXPORT_SYMBOL(rfkill_destroy);
/*
* Rfkill module initialization/deinitialization.
*/
static int __init rfkill_init(void) static int __init rfkill_init(void)
{ {
int error; int error;
int i; int i;
/* RFKILL_STATE_HARD_BLOCKED is illegal here... */ for (i = 0; i < NUM_RFKILL_TYPES; i++)
if (rfkill_default_state != RFKILL_STATE_SOFT_BLOCKED && rfkill_global_states[i].def = !rfkill_default_state;
rfkill_default_state != RFKILL_STATE_UNBLOCKED)
return -EINVAL;
for (i = 0; i < RFKILL_TYPE_MAX; i++)
rfkill_global_states[i].default_state = rfkill_default_state;
error = class_register(&rfkill_class); error = class_register(&rfkill_class);
if (error) { if (error)
printk(KERN_ERR "rfkill: unable to register rfkill class\n"); goto out;
return error;
}
return 0; #ifdef CONFIG_RFKILL_INPUT
error = rfkill_handler_init();
if (error)
class_unregister(&rfkill_class);
#endif
out:
return error;
} }
subsys_initcall(rfkill_init);
static void __exit rfkill_exit(void) static void __exit rfkill_exit(void)
{ {
#ifdef CONFIG_RFKILL_INPUT
rfkill_handler_exit();
#endif
class_unregister(&rfkill_class); class_unregister(&rfkill_class);
} }
subsys_initcall(rfkill_init);
module_exit(rfkill_exit); module_exit(rfkill_exit);
...@@ -2,15 +2,17 @@ ...@@ -2,15 +2,17 @@
* Input layer to RF Kill interface connector * Input layer to RF Kill interface connector
* *
* Copyright (c) 2007 Dmitry Torokhov * Copyright (c) 2007 Dmitry Torokhov
*/ * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
*
/*
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published * under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation. * by the Free Software Foundation.
*
* If you ever run into a situation in which you have a SW_ type rfkill
* input device, then you can revive code that was removed in the patch
* "rfkill-input: remove unused code".
*/ */
#include <linux/module.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
...@@ -18,17 +20,13 @@ ...@@ -18,17 +20,13 @@
#include <linux/rfkill.h> #include <linux/rfkill.h>
#include <linux/sched.h> #include <linux/sched.h>
#include "rfkill-input.h" #include "rfkill.h"
MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
MODULE_DESCRIPTION("Input layer to RF switch connector");
MODULE_LICENSE("GPL");
enum rfkill_input_master_mode { enum rfkill_input_master_mode {
RFKILL_INPUT_MASTER_DONOTHING = 0, RFKILL_INPUT_MASTER_UNLOCK = 0,
RFKILL_INPUT_MASTER_RESTORE = 1, RFKILL_INPUT_MASTER_RESTORE = 1,
RFKILL_INPUT_MASTER_UNBLOCKALL = 2, RFKILL_INPUT_MASTER_UNBLOCKALL = 2,
RFKILL_INPUT_MASTER_MAX, /* marker */ NUM_RFKILL_INPUT_MASTER_MODES
}; };
/* Delay (in ms) between consecutive switch ops */ /* Delay (in ms) between consecutive switch ops */
...@@ -38,38 +36,24 @@ static enum rfkill_input_master_mode rfkill_master_switch_mode = ...@@ -38,38 +36,24 @@ static enum rfkill_input_master_mode rfkill_master_switch_mode =
RFKILL_INPUT_MASTER_UNBLOCKALL; RFKILL_INPUT_MASTER_UNBLOCKALL;
module_param_named(master_switch_mode, rfkill_master_switch_mode, uint, 0); module_param_named(master_switch_mode, rfkill_master_switch_mode, uint, 0);
MODULE_PARM_DESC(master_switch_mode, MODULE_PARM_DESC(master_switch_mode,
"SW_RFKILL_ALL ON should: 0=do nothing; 1=restore; 2=unblock all"); "SW_RFKILL_ALL ON should: 0=do nothing (only unlock); 1=restore; 2=unblock all");
static spinlock_t rfkill_op_lock;
static bool rfkill_op_pending;
static unsigned long rfkill_sw_pending[BITS_TO_LONGS(NUM_RFKILL_TYPES)];
static unsigned long rfkill_sw_state[BITS_TO_LONGS(NUM_RFKILL_TYPES)];
enum rfkill_global_sched_op { enum rfkill_sched_op {
RFKILL_GLOBAL_OP_EPO = 0, RFKILL_GLOBAL_OP_EPO = 0,
RFKILL_GLOBAL_OP_RESTORE, RFKILL_GLOBAL_OP_RESTORE,
RFKILL_GLOBAL_OP_UNLOCK, RFKILL_GLOBAL_OP_UNLOCK,
RFKILL_GLOBAL_OP_UNBLOCK, RFKILL_GLOBAL_OP_UNBLOCK,
}; };
struct rfkill_task { static enum rfkill_sched_op rfkill_master_switch_op;
struct delayed_work dwork; static enum rfkill_sched_op rfkill_op;
/* ensures that task is serialized */
struct mutex mutex;
/* protects everything below */
spinlock_t lock;
/* pending regular switch operations (1=pending) */ static void __rfkill_handle_global_op(enum rfkill_sched_op op)
unsigned long sw_pending[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
/* should the state be complemented (1=yes) */
unsigned long sw_togglestate[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
bool global_op_pending;
enum rfkill_global_sched_op op;
/* last time it was scheduled */
unsigned long last_scheduled;
};
static void __rfkill_handle_global_op(enum rfkill_global_sched_op op)
{ {
unsigned int i; unsigned int i;
...@@ -85,8 +69,8 @@ static void __rfkill_handle_global_op(enum rfkill_global_sched_op op) ...@@ -85,8 +69,8 @@ static void __rfkill_handle_global_op(enum rfkill_global_sched_op op)
break; break;
case RFKILL_GLOBAL_OP_UNBLOCK: case RFKILL_GLOBAL_OP_UNBLOCK:
rfkill_remove_epo_lock(); rfkill_remove_epo_lock();
for (i = 0; i < RFKILL_TYPE_MAX; i++) for (i = 0; i < NUM_RFKILL_TYPES; i++)
rfkill_switch_all(i, RFKILL_STATE_UNBLOCKED); rfkill_switch_all(i, false);
break; break;
default: default:
/* memory corruption or bug, fail safely */ /* memory corruption or bug, fail safely */
...@@ -98,70 +82,62 @@ static void __rfkill_handle_global_op(enum rfkill_global_sched_op op) ...@@ -98,70 +82,62 @@ static void __rfkill_handle_global_op(enum rfkill_global_sched_op op)
} }
static void __rfkill_handle_normal_op(const enum rfkill_type type, static void __rfkill_handle_normal_op(const enum rfkill_type type,
const bool c) const bool complement)
{ {
enum rfkill_state state; bool blocked;
state = rfkill_get_global_state(type); blocked = rfkill_get_global_sw_state(type);
if (c) if (complement)
state = rfkill_state_complement(state); blocked = !blocked;
rfkill_switch_all(type, state); rfkill_switch_all(type, blocked);
} }
static void rfkill_task_handler(struct work_struct *work) static void rfkill_op_handler(struct work_struct *work)
{ {
struct rfkill_task *task = container_of(work, unsigned int i;
struct rfkill_task, dwork.work); bool c;
bool doit = true;
mutex_lock(&task->mutex);
spin_lock_irq(&task->lock); spin_lock_irq(&rfkill_op_lock);
while (doit) { do {
if (task->global_op_pending) { if (rfkill_op_pending) {
enum rfkill_global_sched_op op = task->op; enum rfkill_sched_op op = rfkill_op;
task->global_op_pending = false; rfkill_op_pending = false;
memset(task->sw_pending, 0, sizeof(task->sw_pending)); memset(rfkill_sw_pending, 0,
spin_unlock_irq(&task->lock); sizeof(rfkill_sw_pending));
spin_unlock_irq(&rfkill_op_lock);
__rfkill_handle_global_op(op); __rfkill_handle_global_op(op);
/* make sure we do at least one pass with spin_lock_irq(&rfkill_op_lock);
* !task->global_op_pending */
spin_lock_irq(&task->lock); /*
* handle global ops first -- during unlocked period
* we might have gotten a new global op.
*/
if (rfkill_op_pending)
continue; continue;
} else if (!rfkill_is_epo_lock_active()) { }
unsigned int i = 0;
while (!task->global_op_pending && if (rfkill_is_epo_lock_active())
i < RFKILL_TYPE_MAX) { continue;
if (test_and_clear_bit(i, task->sw_pending)) {
bool c; for (i = 0; i < NUM_RFKILL_TYPES; i++) {
c = test_and_clear_bit(i, if (__test_and_clear_bit(i, rfkill_sw_pending)) {
task->sw_togglestate); c = __test_and_clear_bit(i, rfkill_sw_state);
spin_unlock_irq(&task->lock); spin_unlock_irq(&rfkill_op_lock);
__rfkill_handle_normal_op(i, c); __rfkill_handle_normal_op(i, c);
spin_lock_irq(&task->lock); spin_lock_irq(&rfkill_op_lock);
}
i++;
}
} }
doit = task->global_op_pending;
} }
spin_unlock_irq(&task->lock); } while (rfkill_op_pending);
spin_unlock_irq(&rfkill_op_lock);
mutex_unlock(&task->mutex);
} }
static struct rfkill_task rfkill_task = { static DECLARE_DELAYED_WORK(rfkill_op_work, rfkill_op_handler);
.dwork = __DELAYED_WORK_INITIALIZER(rfkill_task.dwork, static unsigned long rfkill_last_scheduled;
rfkill_task_handler),
.mutex = __MUTEX_INITIALIZER(rfkill_task.mutex),
.lock = __SPIN_LOCK_UNLOCKED(rfkill_task.lock),
};
static unsigned long rfkill_ratelimit(const unsigned long last) static unsigned long rfkill_ratelimit(const unsigned long last)
{ {
...@@ -171,28 +147,28 @@ static unsigned long rfkill_ratelimit(const unsigned long last) ...@@ -171,28 +147,28 @@ static unsigned long rfkill_ratelimit(const unsigned long last)
static void rfkill_schedule_ratelimited(void) static void rfkill_schedule_ratelimited(void)
{ {
if (!delayed_work_pending(&rfkill_task.dwork)) { if (delayed_work_pending(&rfkill_op_work))
schedule_delayed_work(&rfkill_task.dwork, return;
rfkill_ratelimit(rfkill_task.last_scheduled)); schedule_delayed_work(&rfkill_op_work,
rfkill_task.last_scheduled = jiffies; rfkill_ratelimit(rfkill_last_scheduled));
} rfkill_last_scheduled = jiffies;
} }
static void rfkill_schedule_global_op(enum rfkill_global_sched_op op) static void rfkill_schedule_global_op(enum rfkill_sched_op op)
{ {
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&rfkill_task.lock, flags); spin_lock_irqsave(&rfkill_op_lock, flags);
rfkill_task.op = op; rfkill_op = op;
rfkill_task.global_op_pending = true; rfkill_op_pending = true;
if (op == RFKILL_GLOBAL_OP_EPO && !rfkill_is_epo_lock_active()) { if (op == RFKILL_GLOBAL_OP_EPO && !rfkill_is_epo_lock_active()) {
/* bypass the limiter for EPO */ /* bypass the limiter for EPO */
cancel_delayed_work(&rfkill_task.dwork); cancel_delayed_work(&rfkill_op_work);
schedule_delayed_work(&rfkill_task.dwork, 0); schedule_delayed_work(&rfkill_op_work, 0);
rfkill_task.last_scheduled = jiffies; rfkill_last_scheduled = jiffies;
} else } else
rfkill_schedule_ratelimited(); rfkill_schedule_ratelimited();
spin_unlock_irqrestore(&rfkill_task.lock, flags); spin_unlock_irqrestore(&rfkill_op_lock, flags);
} }
static void rfkill_schedule_toggle(enum rfkill_type type) static void rfkill_schedule_toggle(enum rfkill_type type)
...@@ -202,37 +178,20 @@ static void rfkill_schedule_toggle(enum rfkill_type type) ...@@ -202,37 +178,20 @@ static void rfkill_schedule_toggle(enum rfkill_type type)
if (rfkill_is_epo_lock_active()) if (rfkill_is_epo_lock_active())
return; return;
spin_lock_irqsave(&rfkill_task.lock, flags); spin_lock_irqsave(&rfkill_op_lock, flags);
if (!rfkill_task.global_op_pending) { if (!rfkill_op_pending) {
set_bit(type, rfkill_task.sw_pending); __set_bit(type, rfkill_sw_pending);
change_bit(type, rfkill_task.sw_togglestate); __change_bit(type, rfkill_sw_state);
rfkill_schedule_ratelimited(); rfkill_schedule_ratelimited();
} }
spin_unlock_irqrestore(&rfkill_task.lock, flags); spin_unlock_irqrestore(&rfkill_op_lock, flags);
} }
static void rfkill_schedule_evsw_rfkillall(int state) static void rfkill_schedule_evsw_rfkillall(int state)
{ {
if (state) { if (state)
switch (rfkill_master_switch_mode) { rfkill_schedule_global_op(rfkill_master_switch_op);
case RFKILL_INPUT_MASTER_UNBLOCKALL: else
rfkill_schedule_global_op(RFKILL_GLOBAL_OP_UNBLOCK);
break;
case RFKILL_INPUT_MASTER_RESTORE:
rfkill_schedule_global_op(RFKILL_GLOBAL_OP_RESTORE);
break;
case RFKILL_INPUT_MASTER_DONOTHING:
rfkill_schedule_global_op(RFKILL_GLOBAL_OP_UNLOCK);
break;
default:
/* memory corruption or driver bug! fail safely */
rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO);
WARN(1, "Unknown rfkill_master_switch_mode (%d), "
"driver bug or memory corruption detected!\n",
rfkill_master_switch_mode);
break;
}
} else
rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO); rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO);
} }
...@@ -240,35 +199,22 @@ static void rfkill_event(struct input_handle *handle, unsigned int type, ...@@ -240,35 +199,22 @@ static void rfkill_event(struct input_handle *handle, unsigned int type,
unsigned int code, int data) unsigned int code, int data)
{ {
if (type == EV_KEY && data == 1) { if (type == EV_KEY && data == 1) {
enum rfkill_type t;
switch (code) { switch (code) {
case KEY_WLAN: case KEY_WLAN:
t = RFKILL_TYPE_WLAN; rfkill_schedule_toggle(RFKILL_TYPE_WLAN);
break; break;
case KEY_BLUETOOTH: case KEY_BLUETOOTH:
t = RFKILL_TYPE_BLUETOOTH; rfkill_schedule_toggle(RFKILL_TYPE_BLUETOOTH);
break; break;
case KEY_UWB: case KEY_UWB:
t = RFKILL_TYPE_UWB; rfkill_schedule_toggle(RFKILL_TYPE_UWB);
break; break;
case KEY_WIMAX: case KEY_WIMAX:
t = RFKILL_TYPE_WIMAX; rfkill_schedule_toggle(RFKILL_TYPE_WIMAX);
break; break;
default:
return;
} }
rfkill_schedule_toggle(t); } else if (type == EV_SW && code == SW_RFKILL_ALL)
return;
} else if (type == EV_SW) {
switch (code) {
case SW_RFKILL_ALL:
rfkill_schedule_evsw_rfkillall(data); rfkill_schedule_evsw_rfkillall(data);
return;
default:
return;
}
}
} }
static int rfkill_connect(struct input_handler *handler, struct input_dev *dev, static int rfkill_connect(struct input_handler *handler, struct input_dev *dev,
...@@ -305,17 +251,17 @@ static int rfkill_connect(struct input_handler *handler, struct input_dev *dev, ...@@ -305,17 +251,17 @@ static int rfkill_connect(struct input_handler *handler, struct input_dev *dev,
static void rfkill_start(struct input_handle *handle) static void rfkill_start(struct input_handle *handle)
{ {
/* Take event_lock to guard against configuration changes, we /*
* Take event_lock to guard against configuration changes, we
* should be able to deal with concurrency with rfkill_event() * should be able to deal with concurrency with rfkill_event()
* just fine (which event_lock will also avoid). */ * just fine (which event_lock will also avoid).
*/
spin_lock_irq(&handle->dev->event_lock); spin_lock_irq(&handle->dev->event_lock);
if (test_bit(EV_SW, handle->dev->evbit)) { if (test_bit(EV_SW, handle->dev->evbit) &&
if (test_bit(SW_RFKILL_ALL, handle->dev->swbit)) test_bit(SW_RFKILL_ALL, handle->dev->swbit))
rfkill_schedule_evsw_rfkillall(test_bit(SW_RFKILL_ALL, rfkill_schedule_evsw_rfkillall(test_bit(SW_RFKILL_ALL,
handle->dev->sw)); handle->dev->sw));
/* add resync for further EV_SW events here */
}
spin_unlock_irq(&handle->dev->event_lock); spin_unlock_irq(&handle->dev->event_lock);
} }
...@@ -357,34 +303,40 @@ static const struct input_device_id rfkill_ids[] = { ...@@ -357,34 +303,40 @@ static const struct input_device_id rfkill_ids[] = {
}; };
static struct input_handler rfkill_handler = { static struct input_handler rfkill_handler = {
.name = "rfkill",
.event = rfkill_event, .event = rfkill_event,
.connect = rfkill_connect, .connect = rfkill_connect,
.disconnect = rfkill_disconnect,
.start = rfkill_start, .start = rfkill_start,
.name = "rfkill", .disconnect = rfkill_disconnect,
.id_table = rfkill_ids, .id_table = rfkill_ids,
}; };
static int __init rfkill_handler_init(void) int __init rfkill_handler_init(void)
{ {
if (rfkill_master_switch_mode >= RFKILL_INPUT_MASTER_MAX) switch (rfkill_master_switch_mode) {
case RFKILL_INPUT_MASTER_UNBLOCKALL:
rfkill_master_switch_op = RFKILL_GLOBAL_OP_UNBLOCK;
break;
case RFKILL_INPUT_MASTER_RESTORE:
rfkill_master_switch_op = RFKILL_GLOBAL_OP_RESTORE;
break;
case RFKILL_INPUT_MASTER_UNLOCK:
rfkill_master_switch_op = RFKILL_GLOBAL_OP_UNLOCK;
break;
default:
return -EINVAL; return -EINVAL;
}
/* spin_lock_init(&rfkill_op_lock);
* The penalty to not doing this is a possible RFKILL_OPS_DELAY delay
* at the first use. Acceptable, but if we can avoid it, why not? /* Avoid delay at first schedule */
*/ rfkill_last_scheduled =
rfkill_task.last_scheduled =
jiffies - msecs_to_jiffies(RFKILL_OPS_DELAY) - 1; jiffies - msecs_to_jiffies(RFKILL_OPS_DELAY) - 1;
return input_register_handler(&rfkill_handler); return input_register_handler(&rfkill_handler);
} }
static void __exit rfkill_handler_exit(void) void __exit rfkill_handler_exit(void)
{ {
input_unregister_handler(&rfkill_handler); input_unregister_handler(&rfkill_handler);
cancel_delayed_work_sync(&rfkill_task.dwork); cancel_delayed_work_sync(&rfkill_op_work);
rfkill_remove_epo_lock();
} }
module_init(rfkill_handler_init);
module_exit(rfkill_handler_exit);
/* /*
* Copyright (C) 2007 Ivo van Doorn * Copyright (C) 2007 Ivo van Doorn
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
*/ */
/* /*
...@@ -11,11 +12,16 @@ ...@@ -11,11 +12,16 @@
#ifndef __RFKILL_INPUT_H #ifndef __RFKILL_INPUT_H
#define __RFKILL_INPUT_H #define __RFKILL_INPUT_H
void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state); /* core code */
void rfkill_switch_all(const enum rfkill_type type, bool blocked);
void rfkill_epo(void); void rfkill_epo(void);
void rfkill_restore_states(void); void rfkill_restore_states(void);
void rfkill_remove_epo_lock(void); void rfkill_remove_epo_lock(void);
bool rfkill_is_epo_lock_active(void); bool rfkill_is_epo_lock_active(void);
enum rfkill_state rfkill_get_global_state(const enum rfkill_type type); bool rfkill_get_global_sw_state(const enum rfkill_type type);
/* input handler */
int rfkill_handler_init(void);
void rfkill_handler_exit(void);
#endif /* __RFKILL_INPUT_H */ #endif /* __RFKILL_INPUT_H */
# #
# WiMAX LAN device configuration # WiMAX LAN device configuration
# #
# Note the ugly 'depends on' on WIMAX: that disallows RFKILL to be a
# module if WIMAX is to be linked in. The WiMAX code is done in such a
# way that it doesn't require and explicit dependency on RFKILL in
# case an embedded system wants to rip it out.
#
# As well, enablement of the RFKILL code means we need the INPUT layer
# support to inject events coming from hw rfkill switches. That
# dependency could be killed if input.h provided appropriate means to
# work when input is disabled.
comment "WiMAX Wireless Broadband support requires CONFIG_INPUT enabled"
depends on INPUT = n && RFKILL != n
menuconfig WIMAX menuconfig WIMAX
tristate "WiMAX Wireless Broadband support" tristate "WiMAX Wireless Broadband support"
depends on (y && RFKILL != m) || m
depends on (INPUT && RFKILL != n) || RFKILL = n
help help
Select to configure support for devices that provide Select to configure support for devices that provide
......
...@@ -29,8 +29,8 @@ ...@@ -29,8 +29,8 @@
* A non-polled generic rfkill device is embedded into the WiMAX * A non-polled generic rfkill device is embedded into the WiMAX
* subsystem's representation of a device. * subsystem's representation of a device.
* *
* FIXME: Need polled support? use a timer or add the implementation * FIXME: Need polled support? Let drivers provide a poll routine
* to the stack. * and hand it to rfkill ops then?
* *
* All device drivers have to do is after wimax_dev_init(), call * All device drivers have to do is after wimax_dev_init(), call
* wimax_report_rfkill_hw() and wimax_report_rfkill_sw() to update * wimax_report_rfkill_hw() and wimax_report_rfkill_sw() to update
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
* wimax_rfkill() Kernel calling wimax_rfkill() * wimax_rfkill() Kernel calling wimax_rfkill()
* __wimax_rf_toggle_radio() * __wimax_rf_toggle_radio()
* *
* wimax_rfkill_toggle_radio() RF-Kill subsytem calling * wimax_rfkill_set_radio_block() RF-Kill subsytem calling
* __wimax_rf_toggle_radio() * __wimax_rf_toggle_radio()
* *
* __wimax_rf_toggle_radio() * __wimax_rf_toggle_radio()
...@@ -65,15 +65,11 @@ ...@@ -65,15 +65,11 @@
#include <linux/wimax.h> #include <linux/wimax.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/rfkill.h> #include <linux/rfkill.h>
#include <linux/input.h>
#include "wimax-internal.h" #include "wimax-internal.h"
#define D_SUBMODULE op_rfkill #define D_SUBMODULE op_rfkill
#include "debug-levels.h" #include "debug-levels.h"
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
/** /**
* wimax_report_rfkill_hw - Reports changes in the hardware RF switch * wimax_report_rfkill_hw - Reports changes in the hardware RF switch
* *
...@@ -99,7 +95,6 @@ void wimax_report_rfkill_hw(struct wimax_dev *wimax_dev, ...@@ -99,7 +95,6 @@ void wimax_report_rfkill_hw(struct wimax_dev *wimax_dev,
int result; int result;
struct device *dev = wimax_dev_to_dev(wimax_dev); struct device *dev = wimax_dev_to_dev(wimax_dev);
enum wimax_st wimax_state; enum wimax_st wimax_state;
enum rfkill_state rfkill_state;
d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state); d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state);
BUG_ON(state == WIMAX_RF_QUERY); BUG_ON(state == WIMAX_RF_QUERY);
...@@ -112,16 +107,15 @@ void wimax_report_rfkill_hw(struct wimax_dev *wimax_dev, ...@@ -112,16 +107,15 @@ void wimax_report_rfkill_hw(struct wimax_dev *wimax_dev,
if (state != wimax_dev->rf_hw) { if (state != wimax_dev->rf_hw) {
wimax_dev->rf_hw = state; wimax_dev->rf_hw = state;
rfkill_state = state == WIMAX_RF_ON ?
RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
if (wimax_dev->rf_hw == WIMAX_RF_ON if (wimax_dev->rf_hw == WIMAX_RF_ON
&& wimax_dev->rf_sw == WIMAX_RF_ON) && wimax_dev->rf_sw == WIMAX_RF_ON)
wimax_state = WIMAX_ST_READY; wimax_state = WIMAX_ST_READY;
else else
wimax_state = WIMAX_ST_RADIO_OFF; wimax_state = WIMAX_ST_RADIO_OFF;
rfkill_set_hw_state(wimax_dev->rfkill, state == WIMAX_RF_OFF);
__wimax_state_change(wimax_dev, wimax_state); __wimax_state_change(wimax_dev, wimax_state);
input_report_key(wimax_dev->rfkill_input, KEY_WIMAX,
rfkill_state);
} }
error_not_ready: error_not_ready:
mutex_unlock(&wimax_dev->mutex); mutex_unlock(&wimax_dev->mutex);
...@@ -174,6 +168,7 @@ void wimax_report_rfkill_sw(struct wimax_dev *wimax_dev, ...@@ -174,6 +168,7 @@ void wimax_report_rfkill_sw(struct wimax_dev *wimax_dev,
else else
wimax_state = WIMAX_ST_RADIO_OFF; wimax_state = WIMAX_ST_RADIO_OFF;
__wimax_state_change(wimax_dev, wimax_state); __wimax_state_change(wimax_dev, wimax_state);
rfkill_set_sw_state(wimax_dev->rfkill, state == WIMAX_RF_OFF);
} }
error_not_ready: error_not_ready:
mutex_unlock(&wimax_dev->mutex); mutex_unlock(&wimax_dev->mutex);
...@@ -249,36 +244,31 @@ int __wimax_rf_toggle_radio(struct wimax_dev *wimax_dev, ...@@ -249,36 +244,31 @@ int __wimax_rf_toggle_radio(struct wimax_dev *wimax_dev,
* *
* NOTE: This call will block until the operation is completed. * NOTE: This call will block until the operation is completed.
*/ */
static static int wimax_rfkill_set_radio_block(void *data, bool blocked)
int wimax_rfkill_toggle_radio(void *data, enum rfkill_state state)
{ {
int result; int result;
struct wimax_dev *wimax_dev = data; struct wimax_dev *wimax_dev = data;
struct device *dev = wimax_dev_to_dev(wimax_dev); struct device *dev = wimax_dev_to_dev(wimax_dev);
enum wimax_rf_state rf_state; enum wimax_rf_state rf_state;
d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state); d_fnstart(3, dev, "(wimax_dev %p blocked %u)\n", wimax_dev, blocked);
switch (state) {
case RFKILL_STATE_SOFT_BLOCKED:
rf_state = WIMAX_RF_OFF;
break;
case RFKILL_STATE_UNBLOCKED:
rf_state = WIMAX_RF_ON; rf_state = WIMAX_RF_ON;
break; if (blocked)
default: rf_state = WIMAX_RF_OFF;
BUG();
}
mutex_lock(&wimax_dev->mutex); mutex_lock(&wimax_dev->mutex);
if (wimax_dev->state <= __WIMAX_ST_QUIESCING) if (wimax_dev->state <= __WIMAX_ST_QUIESCING)
result = 0; /* just pretend it didn't happen */ result = 0;
else else
result = __wimax_rf_toggle_radio(wimax_dev, rf_state); result = __wimax_rf_toggle_radio(wimax_dev, rf_state);
mutex_unlock(&wimax_dev->mutex); mutex_unlock(&wimax_dev->mutex);
d_fnend(3, dev, "(wimax_dev %p state %u) = %d\n", d_fnend(3, dev, "(wimax_dev %p blocked %u) = %d\n",
wimax_dev, state, result); wimax_dev, blocked, result);
return result; return result;
} }
static const struct rfkill_ops wimax_rfkill_ops = {
.set_block = wimax_rfkill_set_radio_block,
};
/** /**
* wimax_rfkill - Set the software RF switch state for a WiMAX device * wimax_rfkill - Set the software RF switch state for a WiMAX device
...@@ -322,6 +312,7 @@ int wimax_rfkill(struct wimax_dev *wimax_dev, enum wimax_rf_state state) ...@@ -322,6 +312,7 @@ int wimax_rfkill(struct wimax_dev *wimax_dev, enum wimax_rf_state state)
result = __wimax_rf_toggle_radio(wimax_dev, state); result = __wimax_rf_toggle_radio(wimax_dev, state);
if (result < 0) if (result < 0)
goto error; goto error;
rfkill_set_sw_state(wimax_dev->rfkill, state == WIMAX_RF_OFF);
break; break;
case WIMAX_RF_QUERY: case WIMAX_RF_QUERY:
break; break;
...@@ -349,40 +340,20 @@ int wimax_rfkill_add(struct wimax_dev *wimax_dev) ...@@ -349,40 +340,20 @@ int wimax_rfkill_add(struct wimax_dev *wimax_dev)
{ {
int result; int result;
struct rfkill *rfkill; struct rfkill *rfkill;
struct input_dev *input_dev;
struct device *dev = wimax_dev_to_dev(wimax_dev); struct device *dev = wimax_dev_to_dev(wimax_dev);
d_fnstart(3, dev, "(wimax_dev %p)\n", wimax_dev); d_fnstart(3, dev, "(wimax_dev %p)\n", wimax_dev);
/* Initialize RF Kill */ /* Initialize RF Kill */
result = -ENOMEM; result = -ENOMEM;
rfkill = rfkill_allocate(dev, RFKILL_TYPE_WIMAX); rfkill = rfkill_alloc(wimax_dev->name, dev, RFKILL_TYPE_WIMAX,
&wimax_rfkill_ops, wimax_dev);
if (rfkill == NULL) if (rfkill == NULL)
goto error_rfkill_allocate; goto error_rfkill_allocate;
d_printf(1, dev, "rfkill %p\n", rfkill);
wimax_dev->rfkill = rfkill; wimax_dev->rfkill = rfkill;
rfkill->name = wimax_dev->name;
rfkill->state = RFKILL_STATE_UNBLOCKED;
rfkill->data = wimax_dev;
rfkill->toggle_radio = wimax_rfkill_toggle_radio;
/* Initialize the input device for the hw key */
input_dev = input_allocate_device();
if (input_dev == NULL)
goto error_input_allocate;
wimax_dev->rfkill_input = input_dev;
d_printf(1, dev, "rfkill %p input %p\n", rfkill, input_dev);
input_dev->name = wimax_dev->name;
/* FIXME: get a real device bus ID and stuff? do we care? */
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0xffff;
input_dev->evbit[0] = BIT(EV_KEY);
set_bit(KEY_WIMAX, input_dev->keybit);
/* Register both */
result = input_register_device(wimax_dev->rfkill_input);
if (result < 0)
goto error_input_register;
result = rfkill_register(wimax_dev->rfkill); result = rfkill_register(wimax_dev->rfkill);
if (result < 0) if (result < 0)
goto error_rfkill_register; goto error_rfkill_register;
...@@ -394,17 +365,8 @@ int wimax_rfkill_add(struct wimax_dev *wimax_dev) ...@@ -394,17 +365,8 @@ int wimax_rfkill_add(struct wimax_dev *wimax_dev)
d_fnend(3, dev, "(wimax_dev %p) = 0\n", wimax_dev); d_fnend(3, dev, "(wimax_dev %p) = 0\n", wimax_dev);
return 0; return 0;
/* if rfkill_register() suceeds, can't use rfkill_free() any
* more, only rfkill_unregister() [it owns the refcount]; with
* the input device we have the same issue--hence the if. */
error_rfkill_register: error_rfkill_register:
input_unregister_device(wimax_dev->rfkill_input); rfkill_destroy(wimax_dev->rfkill);
wimax_dev->rfkill_input = NULL;
error_input_register:
if (wimax_dev->rfkill_input)
input_free_device(wimax_dev->rfkill_input);
error_input_allocate:
rfkill_free(wimax_dev->rfkill);
error_rfkill_allocate: error_rfkill_allocate:
d_fnend(3, dev, "(wimax_dev %p) = %d\n", wimax_dev, result); d_fnend(3, dev, "(wimax_dev %p) = %d\n", wimax_dev, result);
return result; return result;
...@@ -423,45 +385,12 @@ void wimax_rfkill_rm(struct wimax_dev *wimax_dev) ...@@ -423,45 +385,12 @@ void wimax_rfkill_rm(struct wimax_dev *wimax_dev)
{ {
struct device *dev = wimax_dev_to_dev(wimax_dev); struct device *dev = wimax_dev_to_dev(wimax_dev);
d_fnstart(3, dev, "(wimax_dev %p)\n", wimax_dev); d_fnstart(3, dev, "(wimax_dev %p)\n", wimax_dev);
rfkill_unregister(wimax_dev->rfkill); /* frees */ rfkill_unregister(wimax_dev->rfkill);
input_unregister_device(wimax_dev->rfkill_input); rfkill_destroy(wimax_dev->rfkill);
d_fnend(3, dev, "(wimax_dev %p)\n", wimax_dev); d_fnend(3, dev, "(wimax_dev %p)\n", wimax_dev);
} }
#else /* #ifdef CONFIG_RFKILL */
void wimax_report_rfkill_hw(struct wimax_dev *wimax_dev,
enum wimax_rf_state state)
{
}
EXPORT_SYMBOL_GPL(wimax_report_rfkill_hw);
void wimax_report_rfkill_sw(struct wimax_dev *wimax_dev,
enum wimax_rf_state state)
{
}
EXPORT_SYMBOL_GPL(wimax_report_rfkill_sw);
int wimax_rfkill(struct wimax_dev *wimax_dev,
enum wimax_rf_state state)
{
return WIMAX_RF_ON << 1 | WIMAX_RF_ON;
}
EXPORT_SYMBOL_GPL(wimax_rfkill);
int wimax_rfkill_add(struct wimax_dev *wimax_dev)
{
return 0;
}
void wimax_rfkill_rm(struct wimax_dev *wimax_dev)
{
}
#endif /* #ifdef CONFIG_RFKILL */
/* /*
* Exporting to user space over generic netlink * Exporting to user space over generic netlink
* *
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册