mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 12:11:40 +00:00
rfkill: rewrite
This patch completely rewrites the rfkill core to address the following deficiencies: * all rfkill drivers need to implement polling where necessary rather than having one central implementation * updating the rfkill state cannot be done from arbitrary contexts, forcing drivers to use schedule_work and requiring lots of code * rfkill drivers need to keep track of soft/hard blocked internally -- the core should do this * the rfkill API has many unexpected quirks, for example being asymmetric wrt. alloc/free and register/unregister * rfkill can call back into a driver from within a function the driver called -- this is prone to deadlocks and generally should be avoided * rfkill-input pointlessly is a separate module * drivers need to #ifdef rfkill functions (unless they want to depend on or select RFKILL) -- rfkill should provide inlines that do nothing if it isn't compiled in * the rfkill structure is not opaque -- drivers need to initialise it correctly (lots of sanity checking code required) -- instead force drivers to pass the right variables to rfkill_alloc() * the documentation is hard to read because it always assumes the reader is completely clueless and contains way TOO MANY CAPS * the rfkill code needlessly uses a lot of locks and atomic operations in locked sections * fix LED trigger to actually change the LED when the radio state changes -- this wasn't done before Tested-by: Alan Jenkins <alan-jenkins@tuffmail.co.uk> Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> [thinkpad] Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
0f6399c4c5
commit
19d337dff9
@ -1,571 +1,130 @@
|
||||
rfkill - RF switch subsystem support
|
||||
====================================
|
||||
rfkill - RF kill switch support
|
||||
===============================
|
||||
|
||||
1 Introduction
|
||||
2 Implementation details
|
||||
3 Kernel driver guidelines
|
||||
3.1 wireless device drivers
|
||||
3.2 platform/switch drivers
|
||||
3.3 input device drivers
|
||||
4 Kernel API
|
||||
5 Userspace support
|
||||
1. Introduction
|
||||
2. Implementation details
|
||||
3. Kernel driver guidelines
|
||||
4. Kernel API
|
||||
5. Userspace support
|
||||
|
||||
|
||||
1. Introduction:
|
||||
1. Introduction
|
||||
|
||||
The rfkill switch subsystem exists to add a generic interface to circuitry that
|
||||
can enable or disable the signal output of a wireless *transmitter* of any
|
||||
type. By far, the most common use is to disable radio-frequency transmitters.
|
||||
The rfkill subsystem provides a generic interface to disabling any radio
|
||||
transmitter in the system. When a transmitter is blocked, it shall not
|
||||
radiate any power.
|
||||
|
||||
Note that disabling the signal output means that the the transmitter is to be
|
||||
made to not emit any energy when "blocked". rfkill is not about blocking data
|
||||
transmissions, it is about blocking energy emission.
|
||||
The subsystem also provides the ability to react on button presses and
|
||||
disable all transmitters of a certain type (or all). This is intended for
|
||||
situations where transmitters need to be turned off, for example on
|
||||
aircraft.
|
||||
|
||||
The rfkill subsystem offers support for keys and switches often found on
|
||||
laptops to enable wireless devices like WiFi and Bluetooth, so that these keys
|
||||
and switches actually perform an action in all wireless devices of a given type
|
||||
attached to the system.
|
||||
|
||||
The buttons to enable and disable the wireless transmitters are important in
|
||||
situations where the user is for example using his laptop on a location where
|
||||
radio-frequency transmitters _must_ be disabled (e.g. airplanes).
|
||||
|
||||
Because of this requirement, userspace support for the keys should not be made
|
||||
mandatory. Because userspace might want to perform some additional smarter
|
||||
tasks when the key is pressed, rfkill provides userspace the possibility to
|
||||
take over the task to handle the key events.
|
||||
|
||||
===============================================================================
|
||||
2: Implementation details
|
||||
2. Implementation details
|
||||
|
||||
The rfkill subsystem is composed of various components: the rfkill class, the
|
||||
rfkill-input module (an input layer handler), and some specific input layer
|
||||
events.
|
||||
|
||||
The rfkill class provides kernel drivers with an interface that allows them to
|
||||
know when they should enable or disable a wireless network device transmitter.
|
||||
This is enabled by the CONFIG_RFKILL Kconfig option.
|
||||
The rfkill class is provided for kernel drivers to register their radio
|
||||
transmitter with the kernel, provide methods for turning it on and off and,
|
||||
optionally, letting the system know about hardware-disabled states that may
|
||||
be implemented on the device. This code is enabled with the CONFIG_RFKILL
|
||||
Kconfig option, which drivers can "select".
|
||||
|
||||
The rfkill class support makes sure userspace will be notified of all state
|
||||
changes on rfkill devices through uevents. It provides a notification chain
|
||||
for interested parties in the kernel to also get notified of rfkill state
|
||||
changes in other drivers. It creates several sysfs entries which can be used
|
||||
by userspace. See section "Userspace support".
|
||||
The rfkill class code also notifies userspace of state changes, this is
|
||||
achieved via uevents. It also provides some sysfs files for userspace to
|
||||
check the status of radio transmitters. See the "Userspace support" section
|
||||
below.
|
||||
|
||||
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 code implements a basic response to rfkill buttons -- it
|
||||
implements turning on/off all devices of a certain class (or all).
|
||||
|
||||
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.
|
||||
When the device is hard-blocked (either by a call to rfkill_set_hw_state()
|
||||
or from query_hw_block) set_block() will be invoked but drivers can well
|
||||
ignore the method call since they can use the return value of the function
|
||||
rfkill_set_hw_state() to sync the software state instead of keeping track
|
||||
of calls to set_block().
|
||||
|
||||
|
||||
Important terms for the rfkill subsystem:
|
||||
The entire functionality is spread over more than one 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):
|
||||
* The kernel input layer generates KEY_WWAN, KEY_WLAN etc. and
|
||||
SW_RFKILL_ALL -- when the user presses a button. Drivers for radio
|
||||
transmitters generally do not register to the input layer, unless the
|
||||
device really provides an input device (i.e. a button that has no
|
||||
effect other than generating a button press event)
|
||||
|
||||
rfkill switch:
|
||||
* The rfkill-input code hooks up to these events and switches the soft-block
|
||||
of the various radio transmitters, depending on the button type.
|
||||
|
||||
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.
|
||||
* The rfkill drivers turn off/on their transmitters as requested.
|
||||
|
||||
rfkill controller:
|
||||
* The rfkill class will generate userspace notifications (uevents) to tell
|
||||
userspace what the current state is.
|
||||
|
||||
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).
|
||||
3. Kernel driver guidelines
|
||||
|
||||
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.
|
||||
Drivers for radio transmitters normally implement only the rfkill class.
|
||||
These drivers may not unblock the transmitter based on own decisions, they
|
||||
should act on information provided by the rfkill class only.
|
||||
|
||||
hard rfkill line/hardware rfkill line:
|
||||
Platform drivers might implement input devices if the rfkill button is just
|
||||
that, a button. If that button influences the hardware then you need to
|
||||
implement an rfkill class instead. This also applies if the platform provides
|
||||
a way to turn on/off the transmitter(s).
|
||||
|
||||
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.
|
||||
During suspend/hibernation, transmitters should only be left enabled when
|
||||
wake-on wlan or similar functionality requires it and the device wasn't
|
||||
blocked before suspend/hibernate. Note that it may be necessary to update
|
||||
the rfkill subsystem's idea of what the current state is at resume time if
|
||||
the state may have changed over suspend.
|
||||
|
||||
The 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
|
||||
suspend and hibernation unless:
|
||||
|
||||
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
|
||||
|
||||
5.2. The device was not on a user-requested BLOCKED state before
|
||||
the suspend (i.e. the driver must NOT unblock a device, not even
|
||||
to support wake-on-wireless-packet or remain in the mesh).
|
||||
|
||||
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.
|
||||
|
||||
7. After a rfkill class is suspended, it will *not* call rfkill->toggle_radio
|
||||
until it is resumed.
|
||||
|
||||
|
||||
Example of a WLAN wireless driver connected to the rfkill subsystem:
|
||||
--------------------------------------------------------------------
|
||||
|
||||
A certain WLAN card has one input pin that causes it to block the transmitter
|
||||
and makes the status of that input pin available (only for reading!) to the
|
||||
kernel driver. This is a hard rfkill input line (it cannot be overridden by
|
||||
the kernel driver).
|
||||
|
||||
The card also has one PCI register that, if manipulated by the driver, causes
|
||||
it to block the transmitter. This is a soft rfkill input line.
|
||||
|
||||
It has also a thermal protection circuitry that shuts down its transmitter if
|
||||
the card overheats, and makes the status of that protection available (only for
|
||||
reading!) to the kernel driver. This is also a hard rfkill input line.
|
||||
|
||||
If either one of these rfkill lines are active, the transmitter is blocked by
|
||||
the hardware and forced offline.
|
||||
|
||||
The driver should allocate and attach to its struct device *ONE* instance of
|
||||
the rfkill class (there is only one transmitter).
|
||||
|
||||
It can implement the get_state() hook, and return RFKILL_STATE_HARD_BLOCKED if
|
||||
either one of its two hard rfkill input lines are active. If the two hard
|
||||
rfkill lines are inactive, it must return RFKILL_STATE_SOFT_BLOCKED if its soft
|
||||
rfkill input line is active. Only if none of the rfkill input lines are
|
||||
active, will it return RFKILL_STATE_UNBLOCKED.
|
||||
|
||||
Since the device has a hardware rfkill line, it IS subject to state changes
|
||||
external to rfkill. Therefore, the driver must make sure that it calls
|
||||
rfkill_force_state() to keep the status always up-to-date, and it must do a
|
||||
rfkill_force_state() on resume from sleep.
|
||||
|
||||
Every time the driver gets a notification from the card that one of its rfkill
|
||||
lines changed state (polling might be needed on badly designed cards that don't
|
||||
generate interrupts for such events), it recomputes the rfkill state as per
|
||||
above, and calls rfkill_force_state() to update it.
|
||||
|
||||
The driver should implement the toggle_radio() hook, that:
|
||||
|
||||
1. Returns an error if one of the hardware rfkill lines are active, and the
|
||||
caller asked for RFKILL_STATE_UNBLOCKED.
|
||||
|
||||
2. Activates the soft rfkill line if the caller asked for state
|
||||
RFKILL_STATE_SOFT_BLOCKED. It should do this even if one of the hard rfkill
|
||||
lines are active, effectively double-blocking the transmitter.
|
||||
|
||||
3. Deactivates the soft rfkill line if none of the hardware rfkill lines are
|
||||
active and the caller asked for RFKILL_STATE_UNBLOCKED.
|
||||
|
||||
===============================================================================
|
||||
4: Kernel API
|
||||
4. Kernel API
|
||||
|
||||
To build a driver with rfkill subsystem support, the driver should depend on
|
||||
(or select) the Kconfig symbol RFKILL; it should _not_ depend on RKFILL_INPUT.
|
||||
(or select) the Kconfig symbol RFKILL.
|
||||
|
||||
The hardware the driver talks to may be write-only (where the current state
|
||||
of the hardware is unknown), or read-write (where the hardware can be queried
|
||||
about its current state).
|
||||
|
||||
The rfkill class will call the get_state hook of a device every time it needs
|
||||
to know the *real* current state of the hardware. This can happen often, but
|
||||
it does not do any polling, so it is not enough on hardware that is subject
|
||||
to state changes outside of the rfkill subsystem.
|
||||
Calling rfkill_set_hw_state() when a state change happens is required from
|
||||
rfkill drivers that control devices that can be hard-blocked unless they also
|
||||
assign the poll_hw_block() callback (then the rfkill core will poll the
|
||||
device). Don't do this unless you cannot get the event in any other way.
|
||||
|
||||
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.
|
||||
5. Userspace support
|
||||
|
||||
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
|
||||
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
|
||||
to take a quick look at the source to make sure of the possible values.
|
||||
|
||||
It is expected that HAL will trap those, and bridge them to DBUS, etc. These
|
||||
events CAN and SHOULD be used to give feedback to the user about the rfkill
|
||||
status of the system.
|
||||
|
||||
Input devices may issue events that are related to rfkill. These are the
|
||||
various KEY_* events and SW_* events supported by rfkill-input.c.
|
||||
|
||||
Userspace may not change the state of an rfkill switch in response to an
|
||||
input event, it should refrain from changing states entirely.
|
||||
|
||||
Userspace cannot assume it is the only source of control for rfkill switches.
|
||||
Their state can change due to firmware actions, direct user actions, and the
|
||||
rfkill-input EPO override for *_RFKILL_ALL.
|
||||
|
||||
When rfkill-input is not active, userspace must initiate a rfkill status
|
||||
change by writing to the "state" attribute in order for anything to happen.
|
||||
|
||||
Take particular care to implement EV_SW SW_RFKILL_ALL properly. When that
|
||||
switch is set to OFF, *every* rfkill device *MUST* be immediately put into the
|
||||
RFKILL_STATE_SOFT_BLOCKED state, no questions asked.
|
||||
|
||||
The following sysfs entries will be created:
|
||||
The following sysfs entries exist for every rfkill device:
|
||||
|
||||
name: Name assigned by driver to this key (interface or driver name).
|
||||
type: Name of the key type ("wlan", "bluetooth", etc).
|
||||
state: Current state of the transmitter
|
||||
0: RFKILL_STATE_SOFT_BLOCKED
|
||||
transmitter is forced off, but one can override it
|
||||
by a write to the state attribute;
|
||||
transmitter is turned off by software
|
||||
1: RFKILL_STATE_UNBLOCKED
|
||||
transmiter is NOT forced off, and may operate if
|
||||
all other conditions for such operation are met
|
||||
(such as interface is up and configured, etc);
|
||||
transmiter is (potentially) active
|
||||
2: RFKILL_STATE_HARD_BLOCKED
|
||||
transmitter is forced off by something outside of
|
||||
the driver's control. One cannot set a device to
|
||||
this state through writes to the state attribute;
|
||||
claim: 1: Userspace handles events, 0: Kernel handles events
|
||||
the driver's control.
|
||||
claim: 0: Kernel handles events (currently always reads that value)
|
||||
|
||||
Both the "state" and "claim" entries are also writable. For the "state" entry
|
||||
this means that when 1 or 0 is written, the device rfkill state (if not yet in
|
||||
the requested state), will be will be toggled accordingly.
|
||||
rfkill devices also issue uevents (with an action of "change"), with the
|
||||
following environment variables set:
|
||||
|
||||
For the "claim" entry writing 1 to it means that the kernel no longer handles
|
||||
key events even though RFKILL_INPUT input was enabled. When "claim" has been
|
||||
set to 0, userspace should make sure that it listens for the input events or
|
||||
check the sysfs "state" entry regularly to correctly perform the required tasks
|
||||
when the rkfill key is pressed.
|
||||
RFKILL_NAME
|
||||
RFKILL_STATE
|
||||
RFKILL_TYPE
|
||||
|
||||
A note about input devices and EV_SW events:
|
||||
|
||||
In order to know the current state of an input device switch (like
|
||||
SW_RFKILL_ALL), you will need to use an IOCTL. That information is not
|
||||
available through sysfs in a generic way at this time, and it is not available
|
||||
through the rfkill class AT ALL.
|
||||
The contents of these variables corresponds to the "name", "state" and
|
||||
"type" sysfs files explained above.
|
||||
|
@ -4753,9 +4753,9 @@ S: Supported
|
||||
F: fs/reiserfs/
|
||||
|
||||
RFKILL
|
||||
P: Ivo van Doorn
|
||||
M: IvDoorn@gmail.com
|
||||
L: netdev@vger.kernel.org
|
||||
P: Johannes Berg
|
||||
M: johannes@sipsolutions.net
|
||||
L: linux-wireless@vger.kernel.org
|
||||
S: Maintained
|
||||
F Documentation/rfkill.txt
|
||||
F: net/rfkill/
|
||||
|
@ -35,21 +35,25 @@ static void tosa_bt_off(struct tosa_bt_data *data)
|
||||
gpio_set_value(data->gpio_reset, 0);
|
||||
}
|
||||
|
||||
static int tosa_bt_toggle_radio(void *data, enum rfkill_state state)
|
||||
static int tosa_bt_set_block(void *data, bool blocked)
|
||||
{
|
||||
pr_info("BT_RADIO going: %s\n",
|
||||
state == RFKILL_STATE_UNBLOCKED ? "on" : "off");
|
||||
pr_info("BT_RADIO going: %s\n", blocked ? "off" : "on");
|
||||
|
||||
if (state == RFKILL_STATE_UNBLOCKED) {
|
||||
if (!blocked) {
|
||||
pr_info("TOSA_BT: going ON\n");
|
||||
tosa_bt_on(data);
|
||||
} else {
|
||||
pr_info("TOSA_BT: going OFF\n");
|
||||
tosa_bt_off(data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rfkill_ops tosa_bt_rfkill_ops = {
|
||||
.set_block = tosa_bt_set_block,
|
||||
};
|
||||
|
||||
static int tosa_bt_probe(struct platform_device *dev)
|
||||
{
|
||||
int rc;
|
||||
@ -70,18 +74,14 @@ static int tosa_bt_probe(struct platform_device *dev)
|
||||
if (rc)
|
||||
goto err_pwr_dir;
|
||||
|
||||
rfk = rfkill_allocate(&dev->dev, RFKILL_TYPE_BLUETOOTH);
|
||||
rfk = rfkill_alloc("tosa-bt", &dev->dev, RFKILL_TYPE_BLUETOOTH,
|
||||
&tosa_bt_rfkill_ops, data);
|
||||
if (!rfk) {
|
||||
rc = -ENOMEM;
|
||||
goto err_rfk_alloc;
|
||||
}
|
||||
|
||||
rfk->name = "tosa-bt";
|
||||
rfk->toggle_radio = tosa_bt_toggle_radio;
|
||||
rfk->data = data;
|
||||
#ifdef CONFIG_RFKILL_LEDS
|
||||
rfk->led_trigger.name = "tosa-bt";
|
||||
#endif
|
||||
rfkill_set_led_trigger_name(rfk, "tosa-bt");
|
||||
|
||||
rc = rfkill_register(rfk);
|
||||
if (rc)
|
||||
@ -92,9 +92,7 @@ static int tosa_bt_probe(struct platform_device *dev)
|
||||
return 0;
|
||||
|
||||
err_rfkill:
|
||||
if (rfk)
|
||||
rfkill_free(rfk);
|
||||
rfk = NULL;
|
||||
rfkill_destroy(rfk);
|
||||
err_rfk_alloc:
|
||||
tosa_bt_off(data);
|
||||
err_pwr_dir:
|
||||
@ -113,8 +111,10 @@ static int __devexit tosa_bt_remove(struct platform_device *dev)
|
||||
|
||||
platform_set_drvdata(dev, NULL);
|
||||
|
||||
if (rfk)
|
||||
if (rfk) {
|
||||
rfkill_unregister(rfk);
|
||||
rfkill_destroy(rfk);
|
||||
}
|
||||
rfk = NULL;
|
||||
|
||||
tosa_bt_off(data);
|
||||
|
@ -31,7 +31,6 @@
|
||||
#include <linux/input.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/pda_power.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <asm/setup.h>
|
||||
|
@ -2481,10 +2481,10 @@ static int add_net_device(struct hso_device *hso_dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hso_radio_toggle(void *data, enum rfkill_state state)
|
||||
static int hso_rfkill_set_block(void *data, bool blocked)
|
||||
{
|
||||
struct hso_device *hso_dev = data;
|
||||
int enabled = (state == RFKILL_STATE_UNBLOCKED);
|
||||
int enabled = !blocked;
|
||||
int rv;
|
||||
|
||||
mutex_lock(&hso_dev->mutex);
|
||||
@ -2498,6 +2498,10 @@ static int hso_radio_toggle(void *data, enum rfkill_state state)
|
||||
return rv;
|
||||
}
|
||||
|
||||
static const struct rfkill_ops hso_rfkill_ops = {
|
||||
.set_block = hso_rfkill_set_block,
|
||||
};
|
||||
|
||||
/* Creates and sets up everything for rfkill */
|
||||
static void hso_create_rfkill(struct hso_device *hso_dev,
|
||||
struct usb_interface *interface)
|
||||
@ -2506,29 +2510,25 @@ static void hso_create_rfkill(struct hso_device *hso_dev,
|
||||
struct device *dev = &hso_net->net->dev;
|
||||
char *rfkn;
|
||||
|
||||
hso_net->rfkill = rfkill_allocate(&interface_to_usbdev(interface)->dev,
|
||||
RFKILL_TYPE_WWAN);
|
||||
if (!hso_net->rfkill) {
|
||||
dev_err(dev, "%s - Out of memory\n", __func__);
|
||||
return;
|
||||
}
|
||||
rfkn = kzalloc(20, GFP_KERNEL);
|
||||
if (!rfkn) {
|
||||
rfkill_free(hso_net->rfkill);
|
||||
hso_net->rfkill = NULL;
|
||||
if (!rfkn)
|
||||
dev_err(dev, "%s - Out of memory\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(rfkn, 20, "hso-%d",
|
||||
interface->altsetting->desc.bInterfaceNumber);
|
||||
hso_net->rfkill->name = rfkn;
|
||||
hso_net->rfkill->state = RFKILL_STATE_UNBLOCKED;
|
||||
hso_net->rfkill->data = hso_dev;
|
||||
hso_net->rfkill->toggle_radio = hso_radio_toggle;
|
||||
if (rfkill_register(hso_net->rfkill) < 0) {
|
||||
|
||||
hso_net->rfkill = rfkill_alloc(rfkn,
|
||||
&interface_to_usbdev(interface)->dev,
|
||||
RFKILL_TYPE_WWAN,
|
||||
&hso_rfkill_ops, hso_dev);
|
||||
if (!hso_net->rfkill) {
|
||||
dev_err(dev, "%s - Out of memory\n", __func__);
|
||||
kfree(rfkn);
|
||||
return;
|
||||
}
|
||||
if (rfkill_register(hso_net->rfkill) < 0) {
|
||||
rfkill_destroy(hso_net->rfkill);
|
||||
kfree(rfkn);
|
||||
hso_net->rfkill->name = NULL;
|
||||
rfkill_free(hso_net->rfkill);
|
||||
hso_net->rfkill = NULL;
|
||||
dev_err(dev, "%s - Failed to register rfkill\n", __func__);
|
||||
return;
|
||||
@ -3165,8 +3165,10 @@ static void hso_free_interface(struct usb_interface *interface)
|
||||
hso_stop_net_device(network_table[i]);
|
||||
cancel_work_sync(&network_table[i]->async_put_intf);
|
||||
cancel_work_sync(&network_table[i]->async_get_intf);
|
||||
if (rfk)
|
||||
if (rfk) {
|
||||
rfkill_unregister(rfk);
|
||||
rfkill_destroy(rfk);
|
||||
}
|
||||
hso_free_net_device(network_table[i]);
|
||||
}
|
||||
}
|
||||
|
@ -460,12 +460,9 @@ struct ath_led {
|
||||
bool registered;
|
||||
};
|
||||
|
||||
/* Rfkill */
|
||||
#define ATH_RFKILL_POLL_INTERVAL 2000 /* msecs */
|
||||
|
||||
struct ath_rfkill {
|
||||
struct rfkill *rfkill;
|
||||
struct delayed_work rfkill_poll;
|
||||
struct rfkill_ops ops;
|
||||
char rfkill_name[32];
|
||||
};
|
||||
|
||||
@ -509,8 +506,6 @@ struct ath_rfkill {
|
||||
#define SC_OP_RXFLUSH BIT(7)
|
||||
#define SC_OP_LED_ASSOCIATED BIT(8)
|
||||
#define SC_OP_RFKILL_REGISTERED BIT(9)
|
||||
#define SC_OP_RFKILL_SW_BLOCKED BIT(10)
|
||||
#define SC_OP_RFKILL_HW_BLOCKED BIT(11)
|
||||
#define SC_OP_WAIT_FOR_BEACON BIT(12)
|
||||
#define SC_OP_LED_ON BIT(13)
|
||||
#define SC_OP_SCANNING BIT(14)
|
||||
|
@ -1192,120 +1192,69 @@ static bool ath_is_rfkill_set(struct ath_softc *sc)
|
||||
ah->rfkill_polarity;
|
||||
}
|
||||
|
||||
/* h/w rfkill poll function */
|
||||
static void ath_rfkill_poll(struct work_struct *work)
|
||||
{
|
||||
struct ath_softc *sc = container_of(work, struct ath_softc,
|
||||
rf_kill.rfkill_poll.work);
|
||||
bool radio_on;
|
||||
|
||||
if (sc->sc_flags & SC_OP_INVALID)
|
||||
return;
|
||||
|
||||
radio_on = !ath_is_rfkill_set(sc);
|
||||
|
||||
/*
|
||||
* enable/disable radio only when there is a
|
||||
* state change in RF switch
|
||||
*/
|
||||
if (radio_on == !!(sc->sc_flags & SC_OP_RFKILL_HW_BLOCKED)) {
|
||||
enum rfkill_state state;
|
||||
|
||||
if (sc->sc_flags & SC_OP_RFKILL_SW_BLOCKED) {
|
||||
state = radio_on ? RFKILL_STATE_SOFT_BLOCKED
|
||||
: RFKILL_STATE_HARD_BLOCKED;
|
||||
} else if (radio_on) {
|
||||
ath_radio_enable(sc);
|
||||
state = RFKILL_STATE_UNBLOCKED;
|
||||
} else {
|
||||
ath_radio_disable(sc);
|
||||
state = RFKILL_STATE_HARD_BLOCKED;
|
||||
}
|
||||
|
||||
if (state == RFKILL_STATE_HARD_BLOCKED)
|
||||
sc->sc_flags |= SC_OP_RFKILL_HW_BLOCKED;
|
||||
else
|
||||
sc->sc_flags &= ~SC_OP_RFKILL_HW_BLOCKED;
|
||||
|
||||
rfkill_force_state(sc->rf_kill.rfkill, state);
|
||||
}
|
||||
|
||||
queue_delayed_work(sc->hw->workqueue, &sc->rf_kill.rfkill_poll,
|
||||
msecs_to_jiffies(ATH_RFKILL_POLL_INTERVAL));
|
||||
}
|
||||
|
||||
/* s/w rfkill handler */
|
||||
static int ath_sw_toggle_radio(void *data, enum rfkill_state state)
|
||||
/* s/w rfkill handlers */
|
||||
static int ath_rfkill_set_block(void *data, bool blocked)
|
||||
{
|
||||
struct ath_softc *sc = data;
|
||||
|
||||
switch (state) {
|
||||
case RFKILL_STATE_SOFT_BLOCKED:
|
||||
if (!(sc->sc_flags & (SC_OP_RFKILL_HW_BLOCKED |
|
||||
SC_OP_RFKILL_SW_BLOCKED)))
|
||||
ath_radio_disable(sc);
|
||||
sc->sc_flags |= SC_OP_RFKILL_SW_BLOCKED;
|
||||
return 0;
|
||||
case RFKILL_STATE_UNBLOCKED:
|
||||
if ((sc->sc_flags & SC_OP_RFKILL_SW_BLOCKED)) {
|
||||
sc->sc_flags &= ~SC_OP_RFKILL_SW_BLOCKED;
|
||||
if (sc->sc_flags & SC_OP_RFKILL_HW_BLOCKED) {
|
||||
DPRINTF(sc, ATH_DBG_FATAL, "Can't turn on the"
|
||||
"radio as it is disabled by h/w\n");
|
||||
return -EPERM;
|
||||
}
|
||||
ath_radio_enable(sc);
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (blocked)
|
||||
ath_radio_disable(sc);
|
||||
else
|
||||
ath_radio_enable(sc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ath_rfkill_poll_state(struct rfkill *rfkill, void *data)
|
||||
{
|
||||
struct ath_softc *sc = data;
|
||||
bool blocked = !!ath_is_rfkill_set(sc);
|
||||
|
||||
if (rfkill_set_hw_state(rfkill, blocked))
|
||||
ath_radio_disable(sc);
|
||||
else
|
||||
ath_radio_enable(sc);
|
||||
}
|
||||
|
||||
/* Init s/w rfkill */
|
||||
static int ath_init_sw_rfkill(struct ath_softc *sc)
|
||||
{
|
||||
sc->rf_kill.rfkill = rfkill_allocate(wiphy_dev(sc->hw->wiphy),
|
||||
RFKILL_TYPE_WLAN);
|
||||
sc->rf_kill.ops.set_block = ath_rfkill_set_block;
|
||||
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
|
||||
sc->rf_kill.ops.poll = ath_rfkill_poll_state;
|
||||
|
||||
snprintf(sc->rf_kill.rfkill_name, sizeof(sc->rf_kill.rfkill_name),
|
||||
"ath9k-%s::rfkill", wiphy_name(sc->hw->wiphy));
|
||||
|
||||
sc->rf_kill.rfkill = rfkill_alloc(sc->rf_kill.rfkill_name,
|
||||
wiphy_dev(sc->hw->wiphy),
|
||||
RFKILL_TYPE_WLAN,
|
||||
&sc->rf_kill.ops, sc);
|
||||
if (!sc->rf_kill.rfkill) {
|
||||
DPRINTF(sc, ATH_DBG_FATAL, "Failed to allocate rfkill\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
snprintf(sc->rf_kill.rfkill_name, sizeof(sc->rf_kill.rfkill_name),
|
||||
"ath9k-%s::rfkill", wiphy_name(sc->hw->wiphy));
|
||||
sc->rf_kill.rfkill->name = sc->rf_kill.rfkill_name;
|
||||
sc->rf_kill.rfkill->data = sc;
|
||||
sc->rf_kill.rfkill->toggle_radio = ath_sw_toggle_radio;
|
||||
sc->rf_kill.rfkill->state = RFKILL_STATE_UNBLOCKED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deinitialize rfkill */
|
||||
static void ath_deinit_rfkill(struct ath_softc *sc)
|
||||
{
|
||||
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
|
||||
cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
|
||||
|
||||
if (sc->sc_flags & SC_OP_RFKILL_REGISTERED) {
|
||||
rfkill_unregister(sc->rf_kill.rfkill);
|
||||
rfkill_destroy(sc->rf_kill.rfkill);
|
||||
sc->sc_flags &= ~SC_OP_RFKILL_REGISTERED;
|
||||
sc->rf_kill.rfkill = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ath_start_rfkill_poll(struct ath_softc *sc)
|
||||
{
|
||||
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
|
||||
queue_delayed_work(sc->hw->workqueue,
|
||||
&sc->rf_kill.rfkill_poll, 0);
|
||||
|
||||
if (!(sc->sc_flags & SC_OP_RFKILL_REGISTERED)) {
|
||||
if (rfkill_register(sc->rf_kill.rfkill)) {
|
||||
DPRINTF(sc, ATH_DBG_FATAL,
|
||||
"Unable to register rfkill\n");
|
||||
rfkill_free(sc->rf_kill.rfkill);
|
||||
rfkill_destroy(sc->rf_kill.rfkill);
|
||||
|
||||
/* Deinitialize the device */
|
||||
ath_cleanup(sc);
|
||||
@ -1678,10 +1627,6 @@ int ath_attach(u16 devid, struct ath_softc *sc)
|
||||
goto error_attach;
|
||||
|
||||
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
|
||||
/* Initialze h/w Rfkill */
|
||||
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
|
||||
INIT_DELAYED_WORK(&sc->rf_kill.rfkill_poll, ath_rfkill_poll);
|
||||
|
||||
/* Initialize s/w rfkill */
|
||||
error = ath_init_sw_rfkill(sc);
|
||||
if (error)
|
||||
@ -2214,10 +2159,8 @@ static void ath9k_stop(struct ieee80211_hw *hw)
|
||||
} else
|
||||
sc->rx.rxlink = NULL;
|
||||
|
||||
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
|
||||
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
|
||||
cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
|
||||
#endif
|
||||
rfkill_pause_polling(sc->rf_kill.rfkill);
|
||||
|
||||
/* disable HAL and put h/w to sleep */
|
||||
ath9k_hw_disable(sc->sc_ah);
|
||||
ath9k_hw_configpcipowersave(sc->sc_ah, 1);
|
||||
|
@ -227,11 +227,6 @@ static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state)
|
||||
|
||||
ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
|
||||
|
||||
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
|
||||
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
|
||||
cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
|
||||
#endif
|
||||
|
||||
pci_save_state(pdev);
|
||||
pci_disable_device(pdev);
|
||||
pci_set_power_state(pdev, PCI_D3hot);
|
||||
@ -256,16 +251,6 @@ static int ath_pci_resume(struct pci_dev *pdev)
|
||||
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
|
||||
ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
|
||||
|
||||
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
|
||||
/*
|
||||
* check the h/w rfkill state on resume
|
||||
* and start the rfkill poll timer
|
||||
*/
|
||||
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
|
||||
queue_delayed_work(sc->hw->workqueue,
|
||||
&sc->rf_kill.rfkill_poll, 0);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -102,7 +102,7 @@ config B43_LEDS
|
||||
# if it's possible.
|
||||
config B43_RFKILL
|
||||
bool
|
||||
depends on B43 && (RFKILL = y || RFKILL = B43) && RFKILL_INPUT && (INPUT_POLLDEV = y || INPUT_POLLDEV = B43)
|
||||
depends on B43 && (RFKILL = y || RFKILL = B43)
|
||||
default y
|
||||
|
||||
# This config option automatically enables b43 HW-RNG support,
|
||||
|
@ -87,7 +87,7 @@ static void b43_led_brightness_set(struct led_classdev *led_dev,
|
||||
}
|
||||
|
||||
static int b43_register_led(struct b43_wldev *dev, struct b43_led *led,
|
||||
const char *name, char *default_trigger,
|
||||
const char *name, const char *default_trigger,
|
||||
u8 led_index, bool activelow)
|
||||
{
|
||||
int err;
|
||||
|
@ -3470,7 +3470,7 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
|
||||
|
||||
if (!!conf->radio_enabled != phy->radio_on) {
|
||||
if (conf->radio_enabled) {
|
||||
b43_software_rfkill(dev, RFKILL_STATE_UNBLOCKED);
|
||||
b43_software_rfkill(dev, false);
|
||||
b43info(dev->wl, "Radio turned on by software\n");
|
||||
if (!dev->radio_hw_enable) {
|
||||
b43info(dev->wl, "The hardware RF-kill button "
|
||||
@ -3478,7 +3478,7 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
|
||||
"Press the button to turn it on.\n");
|
||||
}
|
||||
} else {
|
||||
b43_software_rfkill(dev, RFKILL_STATE_SOFT_BLOCKED);
|
||||
b43_software_rfkill(dev, true);
|
||||
b43info(dev->wl, "Radio turned off by software\n");
|
||||
}
|
||||
}
|
||||
|
@ -480,11 +480,11 @@ static bool b43_aphy_op_supports_hwpctl(struct b43_wldev *dev)
|
||||
}
|
||||
|
||||
static void b43_aphy_op_software_rfkill(struct b43_wldev *dev,
|
||||
enum rfkill_state state)
|
||||
bool blocked)
|
||||
{
|
||||
struct b43_phy *phy = &dev->phy;
|
||||
|
||||
if (state == RFKILL_STATE_UNBLOCKED) {
|
||||
if (!blocked) {
|
||||
if (phy->radio_on)
|
||||
return;
|
||||
b43_radio_write16(dev, 0x0004, 0x00C0);
|
||||
|
@ -84,7 +84,7 @@ int b43_phy_init(struct b43_wldev *dev)
|
||||
|
||||
phy->channel = ops->get_default_chan(dev);
|
||||
|
||||
ops->software_rfkill(dev, RFKILL_STATE_UNBLOCKED);
|
||||
ops->software_rfkill(dev, false);
|
||||
err = ops->init(dev);
|
||||
if (err) {
|
||||
b43err(dev->wl, "PHY init failed\n");
|
||||
@ -104,7 +104,7 @@ err_phy_exit:
|
||||
if (ops->exit)
|
||||
ops->exit(dev);
|
||||
err_block_rf:
|
||||
ops->software_rfkill(dev, RFKILL_STATE_SOFT_BLOCKED);
|
||||
ops->software_rfkill(dev, true);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -113,7 +113,7 @@ void b43_phy_exit(struct b43_wldev *dev)
|
||||
{
|
||||
const struct b43_phy_operations *ops = dev->phy.ops;
|
||||
|
||||
ops->software_rfkill(dev, RFKILL_STATE_SOFT_BLOCKED);
|
||||
ops->software_rfkill(dev, true);
|
||||
if (ops->exit)
|
||||
ops->exit(dev);
|
||||
}
|
||||
@ -295,18 +295,13 @@ err_restore_cookie:
|
||||
return err;
|
||||
}
|
||||
|
||||
void b43_software_rfkill(struct b43_wldev *dev, enum rfkill_state state)
|
||||
void b43_software_rfkill(struct b43_wldev *dev, bool blocked)
|
||||
{
|
||||
struct b43_phy *phy = &dev->phy;
|
||||
|
||||
if (state == RFKILL_STATE_HARD_BLOCKED) {
|
||||
/* We cannot hardware-block the device */
|
||||
state = RFKILL_STATE_SOFT_BLOCKED;
|
||||
}
|
||||
|
||||
b43_mac_suspend(dev);
|
||||
phy->ops->software_rfkill(dev, state);
|
||||
phy->radio_on = (state == RFKILL_STATE_UNBLOCKED);
|
||||
phy->ops->software_rfkill(dev, blocked);
|
||||
phy->radio_on = !blocked;
|
||||
b43_mac_enable(dev);
|
||||
}
|
||||
|
||||
|
@ -159,7 +159,7 @@ struct b43_phy_operations {
|
||||
|
||||
/* Radio */
|
||||
bool (*supports_hwpctl)(struct b43_wldev *dev);
|
||||
void (*software_rfkill)(struct b43_wldev *dev, enum rfkill_state state);
|
||||
void (*software_rfkill)(struct b43_wldev *dev, bool blocked);
|
||||
void (*switch_analog)(struct b43_wldev *dev, bool on);
|
||||
int (*switch_channel)(struct b43_wldev *dev, unsigned int new_channel);
|
||||
unsigned int (*get_default_chan)(struct b43_wldev *dev);
|
||||
@ -364,7 +364,7 @@ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel);
|
||||
/**
|
||||
* b43_software_rfkill - Turn the radio ON or OFF in software.
|
||||
*/
|
||||
void b43_software_rfkill(struct b43_wldev *dev, enum rfkill_state state);
|
||||
void b43_software_rfkill(struct b43_wldev *dev, bool blocked);
|
||||
|
||||
/**
|
||||
* b43_phy_txpower_check - Check TX power output.
|
||||
|
@ -2592,7 +2592,7 @@ static bool b43_gphy_op_supports_hwpctl(struct b43_wldev *dev)
|
||||
}
|
||||
|
||||
static void b43_gphy_op_software_rfkill(struct b43_wldev *dev,
|
||||
enum rfkill_state state)
|
||||
bool blocked)
|
||||
{
|
||||
struct b43_phy *phy = &dev->phy;
|
||||
struct b43_phy_g *gphy = phy->g;
|
||||
@ -2600,7 +2600,7 @@ static void b43_gphy_op_software_rfkill(struct b43_wldev *dev,
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (state == RFKILL_STATE_UNBLOCKED) {
|
||||
if (!blocked) {
|
||||
/* Turn radio ON */
|
||||
if (phy->radio_on)
|
||||
return;
|
||||
|
@ -488,7 +488,7 @@ static void b43_lpphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value)
|
||||
}
|
||||
|
||||
static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev,
|
||||
enum rfkill_state state)
|
||||
bool blocked)
|
||||
{
|
||||
//TODO
|
||||
}
|
||||
|
@ -579,7 +579,7 @@ static void b43_nphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value)
|
||||
}
|
||||
|
||||
static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
|
||||
enum rfkill_state state)
|
||||
bool blocked)
|
||||
{//TODO
|
||||
}
|
||||
|
||||
|
@ -45,12 +45,11 @@ static bool b43_is_hw_radio_enabled(struct b43_wldev *dev)
|
||||
}
|
||||
|
||||
/* The poll callback for the hardware button. */
|
||||
static void b43_rfkill_poll(struct input_polled_dev *poll_dev)
|
||||
static void b43_rfkill_poll(struct rfkill *rfkill, void *data)
|
||||
{
|
||||
struct b43_wldev *dev = poll_dev->private;
|
||||
struct b43_wldev *dev = data;
|
||||
struct b43_wl *wl = dev->wl;
|
||||
bool enabled;
|
||||
bool report_change = 0;
|
||||
|
||||
mutex_lock(&wl->mutex);
|
||||
if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED)) {
|
||||
@ -60,68 +59,55 @@ static void b43_rfkill_poll(struct input_polled_dev *poll_dev)
|
||||
enabled = b43_is_hw_radio_enabled(dev);
|
||||
if (unlikely(enabled != dev->radio_hw_enable)) {
|
||||
dev->radio_hw_enable = enabled;
|
||||
report_change = 1;
|
||||
b43info(wl, "Radio hardware status changed to %s\n",
|
||||
enabled ? "ENABLED" : "DISABLED");
|
||||
enabled = !rfkill_set_hw_state(rfkill, !enabled);
|
||||
if (enabled != dev->phy.radio_on)
|
||||
b43_software_rfkill(dev, !enabled);
|
||||
}
|
||||
mutex_unlock(&wl->mutex);
|
||||
|
||||
/* send the radio switch event to the system - note both a key press
|
||||
* and a release are required */
|
||||
if (unlikely(report_change)) {
|
||||
input_report_key(poll_dev->input, KEY_WLAN, 1);
|
||||
input_report_key(poll_dev->input, KEY_WLAN, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Called when the RFKILL toggled in software. */
|
||||
static int b43_rfkill_soft_toggle(void *data, enum rfkill_state state)
|
||||
static int b43_rfkill_soft_set(void *data, bool blocked)
|
||||
{
|
||||
struct b43_wldev *dev = data;
|
||||
struct b43_wl *wl = dev->wl;
|
||||
int err = -EBUSY;
|
||||
int err = -EINVAL;
|
||||
|
||||
if (!wl->rfkill.registered)
|
||||
return 0;
|
||||
if (WARN_ON(!wl->rfkill.registered))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&wl->mutex);
|
||||
|
||||
if (b43_status(dev) < B43_STAT_INITIALIZED)
|
||||
goto out_unlock;
|
||||
|
||||
if (!dev->radio_hw_enable)
|
||||
goto out_unlock;
|
||||
|
||||
if (!blocked != dev->phy.radio_on)
|
||||
b43_software_rfkill(dev, blocked);
|
||||
err = 0;
|
||||
switch (state) {
|
||||
case RFKILL_STATE_UNBLOCKED:
|
||||
if (!dev->radio_hw_enable) {
|
||||
/* No luck. We can't toggle the hardware RF-kill
|
||||
* button from software. */
|
||||
err = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (!dev->phy.radio_on)
|
||||
b43_software_rfkill(dev, state);
|
||||
break;
|
||||
case RFKILL_STATE_SOFT_BLOCKED:
|
||||
if (dev->phy.radio_on)
|
||||
b43_software_rfkill(dev, state);
|
||||
break;
|
||||
default:
|
||||
b43warn(wl, "Received unexpected rfkill state %d.\n", state);
|
||||
break;
|
||||
}
|
||||
out_unlock:
|
||||
mutex_unlock(&wl->mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
char *b43_rfkill_led_name(struct b43_wldev *dev)
|
||||
const char *b43_rfkill_led_name(struct b43_wldev *dev)
|
||||
{
|
||||
struct b43_rfkill *rfk = &(dev->wl->rfkill);
|
||||
|
||||
if (!rfk->registered)
|
||||
return NULL;
|
||||
return rfkill_get_led_name(rfk->rfkill);
|
||||
return rfkill_get_led_trigger_name(rfk->rfkill);
|
||||
}
|
||||
|
||||
static const struct rfkill_ops b43_rfkill_ops = {
|
||||
.set_block = b43_rfkill_soft_set,
|
||||
.poll = b43_rfkill_poll,
|
||||
};
|
||||
|
||||
void b43_rfkill_init(struct b43_wldev *dev)
|
||||
{
|
||||
struct b43_wl *wl = dev->wl;
|
||||
@ -130,65 +116,26 @@ void b43_rfkill_init(struct b43_wldev *dev)
|
||||
|
||||
rfk->registered = 0;
|
||||
|
||||
rfk->rfkill = rfkill_allocate(dev->dev->dev, RFKILL_TYPE_WLAN);
|
||||
if (!rfk->rfkill)
|
||||
goto out_error;
|
||||
snprintf(rfk->name, sizeof(rfk->name),
|
||||
"b43-%s", wiphy_name(wl->hw->wiphy));
|
||||
rfk->rfkill->name = rfk->name;
|
||||
rfk->rfkill->state = RFKILL_STATE_UNBLOCKED;
|
||||
rfk->rfkill->data = dev;
|
||||
rfk->rfkill->toggle_radio = b43_rfkill_soft_toggle;
|
||||
|
||||
rfk->poll_dev = input_allocate_polled_device();
|
||||
if (!rfk->poll_dev) {
|
||||
rfkill_free(rfk->rfkill);
|
||||
goto err_freed_rfk;
|
||||
}
|
||||
|
||||
rfk->poll_dev->private = dev;
|
||||
rfk->poll_dev->poll = b43_rfkill_poll;
|
||||
rfk->poll_dev->poll_interval = 1000; /* msecs */
|
||||
|
||||
rfk->poll_dev->input->name = rfk->name;
|
||||
rfk->poll_dev->input->id.bustype = BUS_HOST;
|
||||
rfk->poll_dev->input->id.vendor = dev->dev->bus->boardinfo.vendor;
|
||||
rfk->poll_dev->input->evbit[0] = BIT(EV_KEY);
|
||||
set_bit(KEY_WLAN, rfk->poll_dev->input->keybit);
|
||||
rfk->rfkill = rfkill_alloc(rfk->name,
|
||||
dev->dev->dev,
|
||||
RFKILL_TYPE_WLAN,
|
||||
&b43_rfkill_ops, dev);
|
||||
if (!rfk->rfkill)
|
||||
goto out_error;
|
||||
|
||||
err = rfkill_register(rfk->rfkill);
|
||||
if (err)
|
||||
goto err_free_polldev;
|
||||
|
||||
#ifdef CONFIG_RFKILL_INPUT_MODULE
|
||||
/* B43 RF-kill isn't useful without the rfkill-input subsystem.
|
||||
* Try to load the module. */
|
||||
err = request_module("rfkill-input");
|
||||
if (err)
|
||||
b43warn(wl, "Failed to load the rfkill-input module. "
|
||||
"The built-in radio LED will not work.\n");
|
||||
#endif /* CONFIG_RFKILL_INPUT */
|
||||
|
||||
#if !defined(CONFIG_RFKILL_INPUT) && !defined(CONFIG_RFKILL_INPUT_MODULE)
|
||||
b43warn(wl, "The rfkill-input subsystem is not available. "
|
||||
"The built-in radio LED will not work.\n");
|
||||
#endif
|
||||
|
||||
err = input_register_polled_device(rfk->poll_dev);
|
||||
if (err)
|
||||
goto err_unreg_rfk;
|
||||
goto err_free;
|
||||
|
||||
rfk->registered = 1;
|
||||
|
||||
return;
|
||||
err_unreg_rfk:
|
||||
rfkill_unregister(rfk->rfkill);
|
||||
err_free_polldev:
|
||||
input_free_polled_device(rfk->poll_dev);
|
||||
rfk->poll_dev = NULL;
|
||||
err_freed_rfk:
|
||||
rfk->rfkill = NULL;
|
||||
out_error:
|
||||
err_free:
|
||||
rfkill_destroy(rfk->rfkill);
|
||||
out_error:
|
||||
rfk->registered = 0;
|
||||
b43warn(wl, "RF-kill button init failed\n");
|
||||
}
|
||||
@ -201,9 +148,7 @@ void b43_rfkill_exit(struct b43_wldev *dev)
|
||||
return;
|
||||
rfk->registered = 0;
|
||||
|
||||
input_unregister_polled_device(rfk->poll_dev);
|
||||
rfkill_unregister(rfk->rfkill);
|
||||
input_free_polled_device(rfk->poll_dev);
|
||||
rfk->poll_dev = NULL;
|
||||
rfkill_destroy(rfk->rfkill);
|
||||
rfk->rfkill = NULL;
|
||||
}
|
||||
|
@ -7,14 +7,11 @@ struct b43_wldev;
|
||||
#ifdef CONFIG_B43_RFKILL
|
||||
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/input-polldev.h>
|
||||
|
||||
|
||||
struct b43_rfkill {
|
||||
/* The RFKILL subsystem data structure */
|
||||
struct rfkill *rfkill;
|
||||
/* The poll device for the RFKILL input button */
|
||||
struct input_polled_dev *poll_dev;
|
||||
/* Did initialization succeed? Used for freeing. */
|
||||
bool registered;
|
||||
/* The unique name of this rfkill switch */
|
||||
@ -26,7 +23,7 @@ struct b43_rfkill {
|
||||
void b43_rfkill_init(struct b43_wldev *dev);
|
||||
void b43_rfkill_exit(struct b43_wldev *dev);
|
||||
|
||||
char * b43_rfkill_led_name(struct b43_wldev *dev);
|
||||
const char *b43_rfkill_led_name(struct b43_wldev *dev);
|
||||
|
||||
|
||||
#else /* CONFIG_B43_RFKILL */
|
||||
|
@ -47,7 +47,7 @@ config B43LEGACY_LEDS
|
||||
# if it's possible.
|
||||
config B43LEGACY_RFKILL
|
||||
bool
|
||||
depends on B43LEGACY && (RFKILL = y || RFKILL = B43LEGACY) && RFKILL_INPUT && (INPUT_POLLDEV = y || INPUT_POLLDEV = B43LEGACY)
|
||||
depends on B43LEGACY && (RFKILL = y || RFKILL = B43LEGACY)
|
||||
default y
|
||||
|
||||
# This config option automatically enables b43 HW-RNG support,
|
||||
|
@ -86,7 +86,8 @@ static void b43legacy_led_brightness_set(struct led_classdev *led_dev,
|
||||
|
||||
static int b43legacy_register_led(struct b43legacy_wldev *dev,
|
||||
struct b43legacy_led *led,
|
||||
const char *name, char *default_trigger,
|
||||
const char *name,
|
||||
const char *default_trigger,
|
||||
u8 led_index, bool activelow)
|
||||
{
|
||||
int err;
|
||||
|
@ -45,12 +45,11 @@ static bool b43legacy_is_hw_radio_enabled(struct b43legacy_wldev *dev)
|
||||
}
|
||||
|
||||
/* The poll callback for the hardware button. */
|
||||
static void b43legacy_rfkill_poll(struct input_polled_dev *poll_dev)
|
||||
static void b43legacy_rfkill_poll(struct rfkill *rfkill, void *data)
|
||||
{
|
||||
struct b43legacy_wldev *dev = poll_dev->private;
|
||||
struct b43legacy_wldev *dev = data;
|
||||
struct b43legacy_wl *wl = dev->wl;
|
||||
bool enabled;
|
||||
bool report_change = 0;
|
||||
|
||||
mutex_lock(&wl->mutex);
|
||||
if (unlikely(b43legacy_status(dev) < B43legacy_STAT_INITIALIZED)) {
|
||||
@ -60,71 +59,64 @@ static void b43legacy_rfkill_poll(struct input_polled_dev *poll_dev)
|
||||
enabled = b43legacy_is_hw_radio_enabled(dev);
|
||||
if (unlikely(enabled != dev->radio_hw_enable)) {
|
||||
dev->radio_hw_enable = enabled;
|
||||
report_change = 1;
|
||||
b43legacyinfo(wl, "Radio hardware status changed to %s\n",
|
||||
enabled ? "ENABLED" : "DISABLED");
|
||||
enabled = !rfkill_set_hw_state(rfkill, !enabled);
|
||||
if (enabled != dev->phy.radio_on) {
|
||||
if (enabled)
|
||||
b43legacy_radio_turn_on(dev);
|
||||
else
|
||||
b43legacy_radio_turn_off(dev, 0);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&wl->mutex);
|
||||
|
||||
/* send the radio switch event to the system - note both a key press
|
||||
* and a release are required */
|
||||
if (unlikely(report_change)) {
|
||||
input_report_key(poll_dev->input, KEY_WLAN, 1);
|
||||
input_report_key(poll_dev->input, KEY_WLAN, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Called when the RFKILL toggled in software.
|
||||
* This is called without locking. */
|
||||
static int b43legacy_rfkill_soft_toggle(void *data, enum rfkill_state state)
|
||||
static int b43legacy_rfkill_soft_set(void *data, bool blocked)
|
||||
{
|
||||
struct b43legacy_wldev *dev = data;
|
||||
struct b43legacy_wl *wl = dev->wl;
|
||||
int err = -EBUSY;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!wl->rfkill.registered)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&wl->mutex);
|
||||
if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED)
|
||||
goto out_unlock;
|
||||
err = 0;
|
||||
switch (state) {
|
||||
case RFKILL_STATE_UNBLOCKED:
|
||||
if (!dev->radio_hw_enable) {
|
||||
/* No luck. We can't toggle the hardware RF-kill
|
||||
* button from software. */
|
||||
err = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (!dev->phy.radio_on)
|
||||
|
||||
if (!dev->radio_hw_enable)
|
||||
goto out_unlock;
|
||||
|
||||
if (!blocked != dev->phy.radio_on) {
|
||||
if (!blocked)
|
||||
b43legacy_radio_turn_on(dev);
|
||||
break;
|
||||
case RFKILL_STATE_SOFT_BLOCKED:
|
||||
if (dev->phy.radio_on)
|
||||
else
|
||||
b43legacy_radio_turn_off(dev, 0);
|
||||
break;
|
||||
default:
|
||||
b43legacywarn(wl, "Received unexpected rfkill state %d.\n",
|
||||
state);
|
||||
break;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&wl->mutex);
|
||||
|
||||
return err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *b43legacy_rfkill_led_name(struct b43legacy_wldev *dev)
|
||||
const char *b43legacy_rfkill_led_name(struct b43legacy_wldev *dev)
|
||||
{
|
||||
struct b43legacy_rfkill *rfk = &(dev->wl->rfkill);
|
||||
|
||||
if (!rfk->registered)
|
||||
return NULL;
|
||||
return rfkill_get_led_name(rfk->rfkill);
|
||||
return rfkill_get_led_trigger_name(rfk->rfkill);
|
||||
}
|
||||
|
||||
static const struct rfkill_ops b43legacy_rfkill_ops = {
|
||||
.set_block = b43legacy_rfkill_soft_set,
|
||||
.poll = b43legacy_rfkill_poll,
|
||||
};
|
||||
|
||||
void b43legacy_rfkill_init(struct b43legacy_wldev *dev)
|
||||
{
|
||||
struct b43legacy_wl *wl = dev->wl;
|
||||
@ -133,60 +125,25 @@ void b43legacy_rfkill_init(struct b43legacy_wldev *dev)
|
||||
|
||||
rfk->registered = 0;
|
||||
|
||||
rfk->rfkill = rfkill_allocate(dev->dev->dev, RFKILL_TYPE_WLAN);
|
||||
if (!rfk->rfkill)
|
||||
goto out_error;
|
||||
snprintf(rfk->name, sizeof(rfk->name),
|
||||
"b43legacy-%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 = b43legacy_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 = 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);
|
||||
rfk->rfkill = rfkill_alloc(rfk->name,
|
||||
dev->dev->dev,
|
||||
RFKILL_TYPE_WLAN,
|
||||
&b43legacy_rfkill_ops, dev);
|
||||
if (!rfk->rfkill)
|
||||
goto out_error;
|
||||
|
||||
err = rfkill_register(rfk->rfkill);
|
||||
if (err)
|
||||
goto err_free_polldev;
|
||||
|
||||
#ifdef CONFIG_RFKILL_INPUT_MODULE
|
||||
/* 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;
|
||||
goto err_free;
|
||||
|
||||
rfk->registered = 1;
|
||||
|
||||
return;
|
||||
err_unreg_rfk:
|
||||
rfkill_unregister(rfk->rfkill);
|
||||
err_free_polldev:
|
||||
input_free_polled_device(rfk->poll_dev);
|
||||
rfk->poll_dev = NULL;
|
||||
err_freed_rfk:
|
||||
rfk->rfkill = NULL;
|
||||
out_error:
|
||||
err_free:
|
||||
rfkill_destroy(rfk->rfkill);
|
||||
out_error:
|
||||
rfk->registered = 0;
|
||||
b43legacywarn(wl, "RF-kill button init failed\n");
|
||||
}
|
||||
@ -199,10 +156,8 @@ void b43legacy_rfkill_exit(struct b43legacy_wldev *dev)
|
||||
return;
|
||||
rfk->registered = 0;
|
||||
|
||||
input_unregister_polled_device(rfk->poll_dev);
|
||||
rfkill_unregister(rfk->rfkill);
|
||||
input_free_polled_device(rfk->poll_dev);
|
||||
rfk->poll_dev = NULL;
|
||||
rfkill_destroy(rfk->rfkill);
|
||||
rfk->rfkill = NULL;
|
||||
}
|
||||
|
||||
|
@ -6,16 +6,12 @@ struct b43legacy_wldev;
|
||||
#ifdef CONFIG_B43LEGACY_RFKILL
|
||||
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/input-polldev.h>
|
||||
|
||||
|
||||
|
||||
struct b43legacy_rfkill {
|
||||
/* The RFKILL subsystem data structure */
|
||||
struct rfkill *rfkill;
|
||||
/* The poll device for the RFKILL input button */
|
||||
struct input_polled_dev *poll_dev;
|
||||
/* Did initialization succeed? Used for freeing. */
|
||||
bool registered;
|
||||
/* The unique name of this rfkill switch */
|
||||
@ -27,7 +23,7 @@ struct b43legacy_rfkill {
|
||||
void b43legacy_rfkill_init(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 */
|
||||
|
@ -5,15 +5,14 @@ config IWLWIFI
|
||||
select FW_LOADER
|
||||
select MAC80211_LEDS if IWLWIFI_LEDS
|
||||
select LEDS_CLASS if IWLWIFI_LEDS
|
||||
select RFKILL if IWLWIFI_RFKILL
|
||||
|
||||
config IWLWIFI_LEDS
|
||||
bool "Enable LED support in iwlagn and iwl3945 drivers"
|
||||
depends on IWLWIFI
|
||||
|
||||
config IWLWIFI_RFKILL
|
||||
bool "Enable RF kill support in iwlagn and iwl3945 drivers"
|
||||
depends on IWLWIFI
|
||||
def_bool y
|
||||
depends on IWLWIFI && RFKILL
|
||||
|
||||
config IWLWIFI_SPECTRUM_MEASUREMENT
|
||||
bool "Enable Spectrum Measurement in iwlagn driver"
|
||||
|
@ -36,42 +36,37 @@
|
||||
#include "iwl-core.h"
|
||||
|
||||
/* 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;
|
||||
int err = 0;
|
||||
|
||||
if (!priv->rfkill)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
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);
|
||||
|
||||
switch (state) {
|
||||
case RFKILL_STATE_UNBLOCKED:
|
||||
if (iwl_is_rfkill_hw(priv)) {
|
||||
err = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (iwl_is_rfkill_hw(priv))
|
||||
goto out_unlock;
|
||||
|
||||
if (!blocked)
|
||||
iwl_radio_kill_sw_enable_radio(priv);
|
||||
break;
|
||||
case RFKILL_STATE_SOFT_BLOCKED:
|
||||
else
|
||||
iwl_radio_kill_sw_disable_radio(priv);
|
||||
break;
|
||||
default:
|
||||
IWL_WARN(priv, "we received unexpected RFKILL state %d\n",
|
||||
state);
|
||||
break;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&priv->mutex);
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rfkill_ops iwl_rfkill_ops = {
|
||||
.set_block = iwl_rfkill_soft_rf_kill,
|
||||
};
|
||||
|
||||
int iwl_rfkill_init(struct iwl_priv *priv)
|
||||
{
|
||||
struct device *device = wiphy_dev(priv->hw->wiphy);
|
||||
@ -80,21 +75,16 @@ int iwl_rfkill_init(struct iwl_priv *priv)
|
||||
BUG_ON(device == NULL);
|
||||
|
||||
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) {
|
||||
IWL_ERR(priv, "Unable to allocate RFKILL device.\n");
|
||||
ret = -ENOMEM;
|
||||
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);
|
||||
if (ret) {
|
||||
IWL_ERR(priv, "Unable to register RFKILL: %d\n", ret);
|
||||
@ -102,11 +92,10 @@ int iwl_rfkill_init(struct iwl_priv *priv)
|
||||
}
|
||||
|
||||
IWL_DEBUG_RF_KILL(priv, "RFKILL initialization complete.\n");
|
||||
return ret;
|
||||
return 0;
|
||||
|
||||
free_rfkill:
|
||||
if (priv->rfkill != NULL)
|
||||
rfkill_free(priv->rfkill);
|
||||
rfkill_destroy(priv->rfkill);
|
||||
priv->rfkill = NULL;
|
||||
|
||||
error:
|
||||
@ -118,8 +107,10 @@ EXPORT_SYMBOL(iwl_rfkill_init);
|
||||
void iwl_rfkill_unregister(struct iwl_priv *priv)
|
||||
{
|
||||
|
||||
if (priv->rfkill)
|
||||
if (priv->rfkill) {
|
||||
rfkill_unregister(priv->rfkill);
|
||||
rfkill_destroy(priv->rfkill);
|
||||
}
|
||||
|
||||
priv->rfkill = NULL;
|
||||
}
|
||||
@ -131,14 +122,10 @@ void iwl_rfkill_set_hw_state(struct iwl_priv *priv)
|
||||
if (!priv->rfkill)
|
||||
return;
|
||||
|
||||
if (iwl_is_rfkill_hw(priv)) {
|
||||
rfkill_force_state(priv->rfkill, RFKILL_STATE_HARD_BLOCKED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!iwl_is_rfkill_sw(priv))
|
||||
rfkill_force_state(priv->rfkill, RFKILL_STATE_UNBLOCKED);
|
||||
if (rfkill_set_hw_state(priv->rfkill,
|
||||
!!iwl_is_rfkill_hw(priv)))
|
||||
iwl_radio_kill_sw_disable_radio(priv);
|
||||
else
|
||||
rfkill_force_state(priv->rfkill, RFKILL_STATE_SOFT_BLOCKED);
|
||||
iwl_radio_kill_sw_enable_radio(priv);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_rfkill_set_hw_state);
|
||||
|
@ -25,47 +25,42 @@
|
||||
|
||||
#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;
|
||||
|
||||
switch (state) {
|
||||
case RFKILL_STATE_UNBLOCKED:
|
||||
if (!blocked) {
|
||||
if (test_bit(IWM_RADIO_RFKILL_HW, &iwm->radio))
|
||||
return -EBUSY;
|
||||
|
||||
if (test_and_clear_bit(IWM_RADIO_RFKILL_SW, &iwm->radio) &&
|
||||
(iwm_to_ndev(iwm)->flags & IFF_UP))
|
||||
iwm_up(iwm);
|
||||
|
||||
break;
|
||||
case RFKILL_STATE_SOFT_BLOCKED:
|
||||
return iwm_up(iwm);
|
||||
} else {
|
||||
if (!test_and_set_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
|
||||
iwm_down(iwm);
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
return iwm_down(iwm);
|
||||
}
|
||||
|
||||
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 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) {
|
||||
IWM_ERR(iwm, "Unable to allocate rfkill device\n");
|
||||
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);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Failed to register rfkill device\n");
|
||||
@ -74,15 +69,15 @@ int iwm_rfkill_init(struct iwm_priv *iwm)
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
rfkill_free(iwm->rfkill);
|
||||
rfkill_destroy(iwm->rfkill);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void iwm_rfkill_exit(struct iwm_priv *iwm)
|
||||
{
|
||||
if (iwm->rfkill)
|
||||
if (iwm->rfkill) {
|
||||
rfkill_unregister(iwm->rfkill);
|
||||
|
||||
rfkill_free(iwm->rfkill);
|
||||
rfkill_destroy(iwm->rfkill);
|
||||
}
|
||||
iwm->rfkill = NULL;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ config ACER_WMI
|
||||
depends on NEW_LEDS
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
depends on SERIO_I8042
|
||||
depends on RFKILL
|
||||
depends on RFKILL || RFKILL = n
|
||||
select ACPI_WMI
|
||||
---help---
|
||||
This is a driver for newer Acer (and Wistron) laptops. It adds
|
||||
@ -60,7 +60,7 @@ config DELL_LAPTOP
|
||||
depends on DCDBAS
|
||||
depends on EXPERIMENTAL
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
depends on RFKILL
|
||||
depends on RFKILL || RFKILL = n
|
||||
depends on POWER_SUPPLY
|
||||
default n
|
||||
---help---
|
||||
@ -117,7 +117,7 @@ config HP_WMI
|
||||
tristate "HP WMI extras"
|
||||
depends on ACPI_WMI
|
||||
depends on INPUT
|
||||
depends on RFKILL
|
||||
depends on RFKILL || RFKILL = n
|
||||
help
|
||||
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.
|
||||
@ -196,14 +196,13 @@ config THINKPAD_ACPI
|
||||
tristate "ThinkPad ACPI Laptop Extras"
|
||||
depends on ACPI
|
||||
depends on INPUT
|
||||
depends on RFKILL || RFKILL = n
|
||||
select BACKLIGHT_LCD_SUPPORT
|
||||
select BACKLIGHT_CLASS_DEVICE
|
||||
select HWMON
|
||||
select NVRAM
|
||||
select NEW_LEDS
|
||||
select LEDS_CLASS
|
||||
select NET
|
||||
select RFKILL
|
||||
---help---
|
||||
This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
|
||||
support for Fn-Fx key combinations, Bluetooth control, video
|
||||
@ -338,9 +337,9 @@ config EEEPC_LAPTOP
|
||||
depends on ACPI
|
||||
depends on INPUT
|
||||
depends on EXPERIMENTAL
|
||||
depends on RFKILL || RFKILL = n
|
||||
select BACKLIGHT_CLASS_DEVICE
|
||||
select HWMON
|
||||
select RFKILL
|
||||
---help---
|
||||
This driver supports the Fn-Fx keys on Eee PC laptops.
|
||||
It also adds the ability to switch camera/wlan on/off.
|
||||
@ -405,9 +404,8 @@ config ACPI_TOSHIBA
|
||||
tristate "Toshiba Laptop Extras"
|
||||
depends on ACPI
|
||||
depends on INPUT
|
||||
depends on RFKILL || RFKILL = n
|
||||
select INPUT_POLLDEV
|
||||
select NET
|
||||
select RFKILL
|
||||
select BACKLIGHT_CLASS_DEVICE
|
||||
---help---
|
||||
This driver adds support for access to certain system settings
|
||||
|
@ -958,58 +958,50 @@ static void acer_rfkill_update(struct work_struct *ignored)
|
||||
|
||||
status = get_u32(&state, ACER_CAP_WIRELESS);
|
||||
if (ACPI_SUCCESS(status))
|
||||
rfkill_force_state(wireless_rfkill, state ?
|
||||
RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED);
|
||||
rfkill_set_sw_state(wireless_rfkill, !!state);
|
||||
|
||||
if (has_cap(ACER_CAP_BLUETOOTH)) {
|
||||
status = get_u32(&state, ACER_CAP_BLUETOOTH);
|
||||
if (ACPI_SUCCESS(status))
|
||||
rfkill_force_state(bluetooth_rfkill, state ?
|
||||
RFKILL_STATE_UNBLOCKED :
|
||||
RFKILL_STATE_SOFT_BLOCKED);
|
||||
rfkill_set_sw_state(bluetooth_rfkill, !!state);
|
||||
}
|
||||
|
||||
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;
|
||||
u32 *cap = data;
|
||||
status = set_u32((u32) (state == RFKILL_STATE_UNBLOCKED), *cap);
|
||||
u32 cap = (unsigned long)data;
|
||||
status = set_u32(!!blocked, cap);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rfkill * acer_rfkill_register(struct device *dev,
|
||||
enum rfkill_type type, char *name, u32 cap)
|
||||
static const struct rfkill_ops acer_rfkill_ops = {
|
||||
.set_block = acer_rfkill_set,
|
||||
};
|
||||
|
||||
static struct rfkill *acer_rfkill_register(struct device *dev,
|
||||
enum rfkill_type type,
|
||||
char *name, u32 cap)
|
||||
{
|
||||
int err;
|
||||
u32 state;
|
||||
u32 *data;
|
||||
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)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
rfkill_dev->name = name;
|
||||
get_u32(&state, cap);
|
||||
rfkill_dev->state = state ? RFKILL_STATE_UNBLOCKED :
|
||||
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;
|
||||
rfkill_set_sw_state(rfkill_dev, !state);
|
||||
|
||||
err = rfkill_register(rfkill_dev);
|
||||
if (err) {
|
||||
kfree(rfkill_dev->data);
|
||||
rfkill_free(rfkill_dev);
|
||||
rfkill_destroy(rfkill_dev);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
return rfkill_dev;
|
||||
@ -1027,8 +1019,8 @@ static int acer_rfkill_init(struct device *dev)
|
||||
RFKILL_TYPE_BLUETOOTH, "acer-bluetooth",
|
||||
ACER_CAP_BLUETOOTH);
|
||||
if (IS_ERR(bluetooth_rfkill)) {
|
||||
kfree(wireless_rfkill->data);
|
||||
rfkill_unregister(wireless_rfkill);
|
||||
rfkill_destroy(wireless_rfkill);
|
||||
return PTR_ERR(bluetooth_rfkill);
|
||||
}
|
||||
}
|
||||
@ -1041,11 +1033,13 @@ static int acer_rfkill_init(struct device *dev)
|
||||
static void acer_rfkill_exit(void)
|
||||
{
|
||||
cancel_delayed_work_sync(&acer_rfkill_work);
|
||||
kfree(wireless_rfkill->data);
|
||||
|
||||
rfkill_unregister(wireless_rfkill);
|
||||
rfkill_destroy(wireless_rfkill);
|
||||
|
||||
if (has_cap(ACER_CAP_BLUETOOTH)) {
|
||||
kfree(bluetooth_rfkill->data);
|
||||
rfkill_unregister(bluetooth_rfkill);
|
||||
rfkill_destroy(bluetooth_rfkill);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -174,10 +174,11 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,
|
||||
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;
|
||||
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));
|
||||
buffer.input[0] = (1 | (radio<<8) | (disable << 16));
|
||||
@ -186,56 +187,24 @@ static int dell_rfkill_set(int radio, enum rfkill_state state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dell_wifi_set(void *data, enum rfkill_state state)
|
||||
{
|
||||
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)
|
||||
static void dell_rfkill_query(struct rfkill *rfkill, void *data)
|
||||
{
|
||||
struct calling_interface_buffer buffer;
|
||||
int status;
|
||||
int new_state = RFKILL_STATE_HARD_BLOCKED;
|
||||
int bit = (unsigned long)data + 16;
|
||||
|
||||
memset(&buffer, 0, sizeof(struct calling_interface_buffer));
|
||||
dell_send_request(&buffer, 17, 11);
|
||||
status = buffer.output[1];
|
||||
|
||||
if (status & (1<<16))
|
||||
new_state = RFKILL_STATE_SOFT_BLOCKED;
|
||||
|
||||
if (status & (1<<bit))
|
||||
*state = new_state;
|
||||
else
|
||||
*state = RFKILL_STATE_UNBLOCKED;
|
||||
|
||||
return 0;
|
||||
if (status & BIT(bit))
|
||||
rfkill_set_hw_state(rfkill, !!(status & BIT(16)));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return dell_rfkill_get(19, state);
|
||||
}
|
||||
static const struct rfkill_ops dell_rfkill_ops = {
|
||||
.set_block = dell_rfkill_set,
|
||||
.query = dell_rfkill_query,
|
||||
};
|
||||
|
||||
static int dell_setup_rfkill(void)
|
||||
{
|
||||
@ -248,36 +217,37 @@ static int dell_setup_rfkill(void)
|
||||
status = buffer.output[1];
|
||||
|
||||
if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
|
||||
wifi_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_WLAN);
|
||||
if (!wifi_rfkill)
|
||||
wifi_rfkill = rfkill_alloc("dell-wifi", NULL, RFKILL_TYPE_WLAN,
|
||||
&dell_rfkill_ops, (void *) 1);
|
||||
if (!wifi_rfkill) {
|
||||
ret = -ENOMEM;
|
||||
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);
|
||||
if (ret)
|
||||
goto err_wifi;
|
||||
}
|
||||
|
||||
if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
|
||||
bluetooth_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_BLUETOOTH);
|
||||
if (!bluetooth_rfkill)
|
||||
bluetooth_rfkill = rfkill_alloc("dell-bluetooth", NULL,
|
||||
RFKILL_TYPE_BLUETOOTH,
|
||||
&dell_rfkill_ops, (void *) 2);
|
||||
if (!bluetooth_rfkill) {
|
||||
ret = -ENOMEM;
|
||||
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);
|
||||
if (ret)
|
||||
goto err_bluetooth;
|
||||
}
|
||||
|
||||
if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
|
||||
wwan_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_WWAN);
|
||||
if (!wwan_rfkill)
|
||||
wwan_rfkill = rfkill_alloc("dell-wwan", NULL, RFKILL_TYPE_WWAN,
|
||||
&dell_rfkill_ops, (void *) 3);
|
||||
if (!wwan_rfkill) {
|
||||
ret = -ENOMEM;
|
||||
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);
|
||||
if (ret)
|
||||
goto err_wwan;
|
||||
@ -285,22 +255,15 @@ static int dell_setup_rfkill(void)
|
||||
|
||||
return 0;
|
||||
err_wwan:
|
||||
if (wwan_rfkill)
|
||||
rfkill_free(wwan_rfkill);
|
||||
if (bluetooth_rfkill) {
|
||||
rfkill_unregister(bluetooth_rfkill);
|
||||
bluetooth_rfkill = NULL;
|
||||
}
|
||||
err_bluetooth:
|
||||
rfkill_destroy(wwan_rfkill);
|
||||
if (bluetooth_rfkill)
|
||||
rfkill_free(bluetooth_rfkill);
|
||||
if (wifi_rfkill) {
|
||||
rfkill_unregister(wifi_rfkill);
|
||||
wifi_rfkill = NULL;
|
||||
}
|
||||
err_wifi:
|
||||
rfkill_unregister(bluetooth_rfkill);
|
||||
err_bluetooth:
|
||||
rfkill_destroy(bluetooth_rfkill);
|
||||
if (wifi_rfkill)
|
||||
rfkill_free(wifi_rfkill);
|
||||
rfkill_unregister(wifi_rfkill);
|
||||
err_wifi:
|
||||
rfkill_destroy(wifi_rfkill);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -299,39 +299,22 @@ static int update_bl_status(struct backlight_device *bd)
|
||||
* Rfkill helpers
|
||||
*/
|
||||
|
||||
static int eeepc_wlan_rfkill_set(void *data, enum rfkill_state state)
|
||||
{
|
||||
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)
|
||||
static bool eeepc_wlan_rfkill_blocked(void)
|
||||
{
|
||||
if (get_acpi(CM_ASL_WLAN) == 1)
|
||||
*state = RFKILL_STATE_UNBLOCKED;
|
||||
else
|
||||
*state = RFKILL_STATE_SOFT_BLOCKED;
|
||||
return 0;
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
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)
|
||||
return set_acpi(CM_ASL_BLUETOOTH, 0);
|
||||
else
|
||||
return set_acpi(CM_ASL_BLUETOOTH, 1);
|
||||
unsigned long asl = (unsigned long)data;
|
||||
return set_acpi(asl, !blocked);
|
||||
}
|
||||
|
||||
static int eeepc_bluetooth_rfkill_state(void *data, enum rfkill_state *state)
|
||||
{
|
||||
if (get_acpi(CM_ASL_BLUETOOTH) == 1)
|
||||
*state = RFKILL_STATE_UNBLOCKED;
|
||||
else
|
||||
*state = RFKILL_STATE_SOFT_BLOCKED;
|
||||
return 0;
|
||||
}
|
||||
static const struct rfkill_ops eeepc_rfkill_ops = {
|
||||
.set_block = eeepc_rfkill_set,
|
||||
};
|
||||
|
||||
/*
|
||||
* Sys helpers
|
||||
@ -531,9 +514,9 @@ static int notify_brn(void)
|
||||
|
||||
static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
|
||||
{
|
||||
enum rfkill_state state;
|
||||
struct pci_dev *dev;
|
||||
struct pci_bus *bus = pci_find_bus(0, 1);
|
||||
bool blocked;
|
||||
|
||||
if (event != ACPI_NOTIFY_BUS_CHECK)
|
||||
return;
|
||||
@ -543,9 +526,8 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
|
||||
return;
|
||||
}
|
||||
|
||||
eeepc_wlan_rfkill_state(ehotk->eeepc_wlan_rfkill, &state);
|
||||
|
||||
if (state == RFKILL_STATE_UNBLOCKED) {
|
||||
blocked = eeepc_wlan_rfkill_blocked();
|
||||
if (!blocked) {
|
||||
dev = pci_get_slot(bus, 0);
|
||||
if (dev) {
|
||||
/* Device already present */
|
||||
@ -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)
|
||||
@ -684,26 +666,17 @@ static int eeepc_hotk_add(struct acpi_device *device)
|
||||
eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
|
||||
|
||||
if (get_acpi(CM_ASL_WLAN) != -1) {
|
||||
ehotk->eeepc_wlan_rfkill = rfkill_allocate(&device->dev,
|
||||
RFKILL_TYPE_WLAN);
|
||||
ehotk->eeepc_wlan_rfkill = rfkill_alloc("eeepc-wlan",
|
||||
&device->dev,
|
||||
RFKILL_TYPE_WLAN,
|
||||
&eeepc_rfkill_ops,
|
||||
(void *)CM_ASL_WLAN);
|
||||
|
||||
if (!ehotk->eeepc_wlan_rfkill)
|
||||
goto wlan_fail;
|
||||
|
||||
ehotk->eeepc_wlan_rfkill->name = "eeepc-wlan";
|
||||
ehotk->eeepc_wlan_rfkill->toggle_radio = eeepc_wlan_rfkill_set;
|
||||
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);
|
||||
}
|
||||
rfkill_set_global_sw_state(RFKILL_TYPE_WLAN,
|
||||
get_acpi(CM_ASL_WLAN) != 1);
|
||||
result = rfkill_register(ehotk->eeepc_wlan_rfkill);
|
||||
if (result)
|
||||
goto wlan_fail;
|
||||
@ -711,28 +684,17 @@ static int eeepc_hotk_add(struct acpi_device *device)
|
||||
|
||||
if (get_acpi(CM_ASL_BLUETOOTH) != -1) {
|
||||
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)
|
||||
goto bluetooth_fail;
|
||||
|
||||
ehotk->eeepc_bluetooth_rfkill->name = "eeepc-bluetooth";
|
||||
ehotk->eeepc_bluetooth_rfkill->toggle_radio =
|
||||
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);
|
||||
}
|
||||
|
||||
rfkill_set_global_sw_state(RFKILL_TYPE_BLUETOOTH,
|
||||
get_acpi(CM_ASL_BLUETOOTH) != 1);
|
||||
result = rfkill_register(ehotk->eeepc_bluetooth_rfkill);
|
||||
if (result)
|
||||
goto bluetooth_fail;
|
||||
@ -741,13 +703,10 @@ static int eeepc_hotk_add(struct acpi_device *device)
|
||||
return 0;
|
||||
|
||||
bluetooth_fail:
|
||||
if (ehotk->eeepc_bluetooth_rfkill)
|
||||
rfkill_free(ehotk->eeepc_bluetooth_rfkill);
|
||||
rfkill_destroy(ehotk->eeepc_bluetooth_rfkill);
|
||||
rfkill_unregister(ehotk->eeepc_wlan_rfkill);
|
||||
ehotk->eeepc_wlan_rfkill = NULL;
|
||||
wlan_fail:
|
||||
if (ehotk->eeepc_wlan_rfkill)
|
||||
rfkill_free(ehotk->eeepc_wlan_rfkill);
|
||||
rfkill_destroy(ehotk->eeepc_wlan_rfkill);
|
||||
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
|
||||
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
|
||||
ehotk_fail:
|
||||
|
@ -154,58 +154,46 @@ static int hp_wmi_dock_state(void)
|
||||
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)
|
||||
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x101);
|
||||
else
|
||||
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x100);
|
||||
unsigned long b = (unsigned long) data;
|
||||
int query = BIT(b + 8) | ((!!blocked) << b);
|
||||
|
||||
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, query);
|
||||
}
|
||||
|
||||
static int hp_wmi_bluetooth_set(void *data, enum rfkill_state state)
|
||||
{
|
||||
if (state)
|
||||
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x202);
|
||||
else
|
||||
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x200);
|
||||
}
|
||||
static const struct rfkill_ops hp_wmi_rfkill_ops = {
|
||||
.set_block = hp_wmi_set_block,
|
||||
};
|
||||
|
||||
static int hp_wmi_wwan_set(void *data, enum rfkill_state state)
|
||||
{
|
||||
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);
|
||||
|
||||
if (wireless & 0x100)
|
||||
return RFKILL_STATE_UNBLOCKED;
|
||||
return false;
|
||||
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);
|
||||
|
||||
if (wireless & 0x10000)
|
||||
return RFKILL_STATE_UNBLOCKED;
|
||||
return false;
|
||||
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);
|
||||
|
||||
if (wireless & 0x1000000)
|
||||
return RFKILL_STATE_UNBLOCKED;
|
||||
return false;
|
||||
else
|
||||
return RFKILL_STATE_SOFT_BLOCKED;
|
||||
return true;
|
||||
}
|
||||
|
||||
static ssize_t show_display(struct device *dev, struct device_attribute *attr,
|
||||
@ -347,14 +335,14 @@ static void hp_wmi_notify(u32 value, void *context)
|
||||
}
|
||||
} else if (eventcode == 0x5) {
|
||||
if (wifi_rfkill)
|
||||
rfkill_force_state(wifi_rfkill,
|
||||
hp_wmi_wifi_state());
|
||||
rfkill_set_sw_state(wifi_rfkill,
|
||||
hp_wmi_wifi_state());
|
||||
if (bluetooth_rfkill)
|
||||
rfkill_force_state(bluetooth_rfkill,
|
||||
hp_wmi_bluetooth_state());
|
||||
rfkill_set_sw_state(bluetooth_rfkill,
|
||||
hp_wmi_bluetooth_state());
|
||||
if (wwan_rfkill)
|
||||
rfkill_force_state(wwan_rfkill,
|
||||
hp_wmi_wwan_state());
|
||||
rfkill_set_sw_state(wwan_rfkill,
|
||||
hp_wmi_wwan_state());
|
||||
} else
|
||||
printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n",
|
||||
eventcode);
|
||||
@ -430,31 +418,34 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
|
||||
goto add_sysfs_error;
|
||||
|
||||
if (wireless & 0x1) {
|
||||
wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN);
|
||||
wifi_rfkill->name = "hp-wifi";
|
||||
wifi_rfkill->state = hp_wmi_wifi_state();
|
||||
wifi_rfkill->toggle_radio = hp_wmi_wifi_set;
|
||||
wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
|
||||
RFKILL_TYPE_WLAN,
|
||||
&hp_wmi_rfkill_ops,
|
||||
(void *) 0);
|
||||
rfkill_set_sw_state(wifi_rfkill, hp_wmi_wifi_state());
|
||||
err = rfkill_register(wifi_rfkill);
|
||||
if (err)
|
||||
goto add_sysfs_error;
|
||||
goto register_wifi_error;
|
||||
}
|
||||
|
||||
if (wireless & 0x2) {
|
||||
bluetooth_rfkill = rfkill_allocate(&device->dev,
|
||||
RFKILL_TYPE_BLUETOOTH);
|
||||
bluetooth_rfkill->name = "hp-bluetooth";
|
||||
bluetooth_rfkill->state = hp_wmi_bluetooth_state();
|
||||
bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set;
|
||||
bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev,
|
||||
RFKILL_TYPE_BLUETOOTH,
|
||||
&hp_wmi_rfkill_ops,
|
||||
(void *) 1);
|
||||
rfkill_set_sw_state(bluetooth_rfkill,
|
||||
hp_wmi_bluetooth_state());
|
||||
err = rfkill_register(bluetooth_rfkill);
|
||||
if (err)
|
||||
goto register_bluetooth_error;
|
||||
}
|
||||
|
||||
if (wireless & 0x4) {
|
||||
wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN);
|
||||
wwan_rfkill->name = "hp-wwan";
|
||||
wwan_rfkill->state = hp_wmi_wwan_state();
|
||||
wwan_rfkill->toggle_radio = hp_wmi_wwan_set;
|
||||
wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev,
|
||||
RFKILL_TYPE_WWAN,
|
||||
&hp_wmi_rfkill_ops,
|
||||
(void *) 2);
|
||||
rfkill_set_sw_state(wwan_rfkill, hp_wmi_wwan_state());
|
||||
err = rfkill_register(wwan_rfkill);
|
||||
if (err)
|
||||
goto register_wwan_err;
|
||||
@ -462,11 +453,15 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
|
||||
|
||||
return 0;
|
||||
register_wwan_err:
|
||||
rfkill_destroy(wwan_rfkill);
|
||||
if (bluetooth_rfkill)
|
||||
rfkill_unregister(bluetooth_rfkill);
|
||||
register_bluetooth_error:
|
||||
rfkill_destroy(bluetooth_rfkill);
|
||||
if (wifi_rfkill)
|
||||
rfkill_unregister(wifi_rfkill);
|
||||
register_wifi_error:
|
||||
rfkill_destroy(wifi_rfkill);
|
||||
add_sysfs_error:
|
||||
cleanup_sysfs(device);
|
||||
return err;
|
||||
@ -476,12 +471,18 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device)
|
||||
{
|
||||
cleanup_sysfs(device);
|
||||
|
||||
if (wifi_rfkill)
|
||||
if (wifi_rfkill) {
|
||||
rfkill_unregister(wifi_rfkill);
|
||||
if (bluetooth_rfkill)
|
||||
rfkill_destroy(wifi_rfkill);
|
||||
}
|
||||
if (bluetooth_rfkill) {
|
||||
rfkill_unregister(bluetooth_rfkill);
|
||||
if (wwan_rfkill)
|
||||
rfkill_destroy(wifi_rfkill);
|
||||
}
|
||||
if (wwan_rfkill) {
|
||||
rfkill_unregister(wwan_rfkill);
|
||||
rfkill_destroy(wwan_rfkill);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -128,11 +128,11 @@ enum sony_nc_rfkill {
|
||||
SONY_BLUETOOTH,
|
||||
SONY_WWAN,
|
||||
SONY_WIMAX,
|
||||
SONY_RFKILL_MAX,
|
||||
N_SONY_RFKILL,
|
||||
};
|
||||
|
||||
static struct rfkill *sony_rfkill_devices[SONY_RFKILL_MAX];
|
||||
static int sony_rfkill_address[SONY_RFKILL_MAX] = {0x300, 0x500, 0x700, 0x900};
|
||||
static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL];
|
||||
static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900};
|
||||
static void sony_nc_rfkill_update(void);
|
||||
|
||||
/*********** Input Devices ***********/
|
||||
@ -1051,147 +1051,98 @@ static void sony_nc_rfkill_cleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < SONY_RFKILL_MAX; i++) {
|
||||
if (sony_rfkill_devices[i])
|
||||
for (i = 0; i < N_SONY_RFKILL; i++) {
|
||||
if (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 argument = sony_rfkill_address[(long) data] + 0x100;
|
||||
|
||||
if (state == RFKILL_STATE_UNBLOCKED)
|
||||
if (!blocked)
|
||||
argument |= 0xff0000;
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
static int sony_nc_setup_rfkill(struct acpi_device *device,
|
||||
enum sony_nc_rfkill nc_type)
|
||||
{
|
||||
int err = 0;
|
||||
struct rfkill *sony_wifi_rfkill;
|
||||
struct rfkill *rfk;
|
||||
enum rfkill_type type;
|
||||
const char *name;
|
||||
|
||||
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);
|
||||
switch (nc_type) {
|
||||
case SONY_WIFI:
|
||||
type = RFKILL_TYPE_WLAN;
|
||||
name = "sony-wifi";
|
||||
break;
|
||||
case SONY_BLUETOOTH:
|
||||
type = RFKILL_TYPE_BLUETOOTH;
|
||||
name = "sony-bluetooth";
|
||||
break;
|
||||
case SONY_WWAN:
|
||||
type = RFKILL_TYPE_WWAN;
|
||||
name = "sony-wwan";
|
||||
break;
|
||||
case SONY_WIMAX:
|
||||
type = RFKILL_TYPE_WIMAX;
|
||||
name = "sony-wimax";
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sony_nc_setup_bluetooth_rfkill(struct acpi_device *device)
|
||||
{
|
||||
int err = 0;
|
||||
struct rfkill *sony_bluetooth_rfkill;
|
||||
rfk = rfkill_alloc(name, &device->dev, type,
|
||||
&sony_rfkill_ops, (void *)nc_type);
|
||||
if (!rfk)
|
||||
return -ENOMEM;
|
||||
|
||||
sony_bluetooth_rfkill = rfkill_allocate(&device->dev,
|
||||
RFKILL_TYPE_BLUETOOTH);
|
||||
if (!sony_bluetooth_rfkill)
|
||||
return -1;
|
||||
sony_bluetooth_rfkill->name = "sony-bluetooth";
|
||||
sony_bluetooth_rfkill->toggle_radio = sony_nc_rfkill_set;
|
||||
sony_bluetooth_rfkill->get_state = sony_nc_rfkill_get;
|
||||
sony_bluetooth_rfkill->data = (void *)SONY_BLUETOOTH;
|
||||
err = rfkill_register(sony_bluetooth_rfkill);
|
||||
if (err)
|
||||
rfkill_free(sony_bluetooth_rfkill);
|
||||
else {
|
||||
sony_rfkill_devices[SONY_BLUETOOTH] = sony_bluetooth_rfkill;
|
||||
sony_nc_rfkill_set(sony_bluetooth_rfkill->data,
|
||||
RFKILL_STATE_UNBLOCKED);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sony_nc_setup_wwan_rfkill(struct acpi_device *device)
|
||||
{
|
||||
int err = 0;
|
||||
struct rfkill *sony_wwan_rfkill;
|
||||
|
||||
sony_wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN);
|
||||
if (!sony_wwan_rfkill)
|
||||
return -1;
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
err = rfkill_register(rfk);
|
||||
if (err) {
|
||||
rfkill_destroy(rfk);
|
||||
return err;
|
||||
}
|
||||
sony_rfkill_devices[nc_type] = rfk;
|
||||
sony_nc_rfkill_set((void *)nc_type, false);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void sony_nc_rfkill_update()
|
||||
{
|
||||
int i;
|
||||
enum rfkill_state state;
|
||||
enum sony_nc_rfkill i;
|
||||
int result;
|
||||
bool hwblock;
|
||||
|
||||
for (i = 0; i < SONY_RFKILL_MAX; i++) {
|
||||
if (sony_rfkill_devices[i]) {
|
||||
sony_rfkill_devices[i]->
|
||||
get_state(sony_rfkill_devices[i]->data,
|
||||
&state);
|
||||
rfkill_force_state(sony_rfkill_devices[i], state);
|
||||
sony_call_snc_handle(0x124, 0x200, &result);
|
||||
hwblock = !(result & 0x1);
|
||||
|
||||
for (i = 0; i < N_SONY_RFKILL; i++) {
|
||||
int argument = sony_rfkill_address[i];
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if (result & 0x1)
|
||||
sony_nc_setup_wifi_rfkill(device);
|
||||
sony_nc_setup_rfkill(device, SONY_WIFI);
|
||||
if (result & 0x2)
|
||||
sony_nc_setup_bluetooth_rfkill(device);
|
||||
sony_nc_setup_rfkill(device, SONY_BLUETOOTH);
|
||||
if (result & 0x1c)
|
||||
sony_nc_setup_wwan_rfkill(device);
|
||||
sony_nc_setup_rfkill(device, SONY_WWAN);
|
||||
if (result & 0x20)
|
||||
sony_nc_setup_wimax_rfkill(device);
|
||||
sony_nc_setup_rfkill(device, SONY_WIMAX);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -45,7 +45,6 @@
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/input-polldev.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
@ -250,21 +249,15 @@ static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result)
|
||||
|
||||
struct toshiba_acpi_dev {
|
||||
struct platform_device *p_dev;
|
||||
struct rfkill *rfk_dev;
|
||||
struct input_polled_dev *poll_dev;
|
||||
struct rfkill *bt_rfk;
|
||||
|
||||
const char *bt_name;
|
||||
const char *rfk_name;
|
||||
|
||||
bool last_rfk_state;
|
||||
|
||||
struct mutex mutex;
|
||||
};
|
||||
|
||||
static struct toshiba_acpi_dev toshiba_acpi = {
|
||||
.bt_name = "Toshiba Bluetooth",
|
||||
.rfk_name = "Toshiba RFKill Switch",
|
||||
.last_rfk_state = false,
|
||||
};
|
||||
|
||||
/* Bluetooth rfkill handlers */
|
||||
@ -283,21 +276,6 @@ static u32 hci_get_bt_present(bool *present)
|
||||
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)
|
||||
{
|
||||
u32 hci_result;
|
||||
@ -311,70 +289,67 @@ static u32 hci_get_radio_state(bool *radio_state)
|
||||
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 value;
|
||||
int err;
|
||||
bool radio_state;
|
||||
struct toshiba_acpi_dev *dev = data;
|
||||
|
||||
value = (state == RFKILL_STATE_UNBLOCKED);
|
||||
|
||||
if (hci_get_radio_state(&radio_state) != HCI_SUCCESS)
|
||||
return -EFAULT;
|
||||
|
||||
switch (state) {
|
||||
case RFKILL_STATE_UNBLOCKED:
|
||||
if (!radio_state)
|
||||
return -EPERM;
|
||||
break;
|
||||
case RFKILL_STATE_SOFT_BLOCKED:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
value = (blocked == false);
|
||||
|
||||
mutex_lock(&dev->mutex);
|
||||
if (hci_get_radio_state(&radio_state) != HCI_SUCCESS) {
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!radio_state) {
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1);
|
||||
hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2);
|
||||
mutex_unlock(&dev->mutex);
|
||||
|
||||
if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS)
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
err = -EBUSY;
|
||||
else
|
||||
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 value;
|
||||
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);
|
||||
if (hci_result != HCI_SUCCESS)
|
||||
return; /* Can't do anything useful */
|
||||
if (hci_result != HCI_SUCCESS) {
|
||||
/* Can't do anything useful */
|
||||
mutex_unlock(&dev->mutex);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (unlikely(state_changed)) {
|
||||
rfkill_force_state(dev->rfk_dev,
|
||||
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);
|
||||
}
|
||||
if (rfkill_set_hw_state(rfkill, !new_rfk_state))
|
||||
bt_rfkill_set_block(data, true);
|
||||
}
|
||||
|
||||
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 backlight_device *toshiba_backlight_device;
|
||||
static int force_fan;
|
||||
@ -702,14 +677,11 @@ static struct backlight_ops toshiba_backlight_data = {
|
||||
|
||||
static void toshiba_acpi_exit(void)
|
||||
{
|
||||
if (toshiba_acpi.poll_dev) {
|
||||
input_unregister_polled_device(toshiba_acpi.poll_dev);
|
||||
input_free_polled_device(toshiba_acpi.poll_dev);
|
||||
if (toshiba_acpi.bt_rfk) {
|
||||
rfkill_unregister(toshiba_acpi.bt_rfk);
|
||||
rfkill_destroy(toshiba_acpi.bt_rfk);
|
||||
}
|
||||
|
||||
if (toshiba_acpi.rfk_dev)
|
||||
rfkill_unregister(toshiba_acpi.rfk_dev);
|
||||
|
||||
if (toshiba_backlight_device)
|
||||
backlight_device_unregister(toshiba_backlight_device);
|
||||
|
||||
@ -728,8 +700,6 @@ static int __init toshiba_acpi_init(void)
|
||||
acpi_status status = AE_OK;
|
||||
u32 hci_result;
|
||||
bool bt_present;
|
||||
bool bt_on;
|
||||
bool radio_on;
|
||||
int ret = 0;
|
||||
|
||||
if (acpi_disabled)
|
||||
@ -793,60 +763,21 @@ static int __init toshiba_acpi_init(void)
|
||||
|
||||
/* Register rfkill switch for Bluetooth */
|
||||
if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) {
|
||||
toshiba_acpi.rfk_dev = rfkill_allocate(&toshiba_acpi.p_dev->dev,
|
||||
RFKILL_TYPE_BLUETOOTH);
|
||||
if (!toshiba_acpi.rfk_dev) {
|
||||
toshiba_acpi.bt_rfk = rfkill_alloc(toshiba_acpi.bt_name,
|
||||
&toshiba_acpi.p_dev->dev,
|
||||
RFKILL_TYPE_BLUETOOTH,
|
||||
&toshiba_rfk_ops,
|
||||
&toshiba_acpi);
|
||||
if (!toshiba_acpi.bt_rfk) {
|
||||
printk(MY_ERR "unable to allocate rfkill device\n");
|
||||
toshiba_acpi_exit();
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
toshiba_acpi.rfk_dev->name = toshiba_acpi.bt_name;
|
||||
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);
|
||||
ret = rfkill_register(toshiba_acpi.bt_rfk);
|
||||
if (ret) {
|
||||
printk(MY_ERR "unable to register rfkill device\n");
|
||||
toshiba_acpi_exit();
|
||||
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");
|
||||
rfkill_destroy(toshiba_acpi.bt_rfk);
|
||||
toshiba_acpi_exit();
|
||||
return ret;
|
||||
}
|
||||
|
@ -311,6 +311,7 @@ unifdef-y += ptrace.h
|
||||
unifdef-y += qnx4_fs.h
|
||||
unifdef-y += quota.h
|
||||
unifdef-y += random.h
|
||||
unifdef-y += rfkill.h
|
||||
unifdef-y += irqnr.h
|
||||
unifdef-y += reboot.h
|
||||
unifdef-y += reiserfs_fs.h
|
||||
|
@ -4,6 +4,7 @@
|
||||
/*
|
||||
* Copyright (C) 2006 - 2007 Ivo van Doorn
|
||||
* 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 under the terms of the GNU General Public License as published by
|
||||
@ -21,6 +22,24 @@
|
||||
* 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/kernel.h>
|
||||
#include <linux/list.h>
|
||||
@ -30,109 +49,267 @@
|
||||
|
||||
/**
|
||||
* 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_UWB: switch is on a ultra wideband device.
|
||||
* RFKILL_TYPE_WIMAX: switch is on a WiMAX device.
|
||||
* RFKILL_TYPE_WWAN: switch is on a wireless WAN device.
|
||||
*
|
||||
* @RFKILL_TYPE_WLAN: switch is on a 802.11 wireless network device.
|
||||
* @RFKILL_TYPE_BLUETOOTH: switch is on a bluetooth device.
|
||||
* @RFKILL_TYPE_UWB: switch is on a ultra wideband 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 {
|
||||
RFKILL_TYPE_WLAN ,
|
||||
RFKILL_TYPE_WLAN,
|
||||
RFKILL_TYPE_BLUETOOTH,
|
||||
RFKILL_TYPE_UWB,
|
||||
RFKILL_TYPE_WIMAX,
|
||||
RFKILL_TYPE_WWAN,
|
||||
RFKILL_TYPE_MAX,
|
||||
NUM_RFKILL_TYPES,
|
||||
};
|
||||
|
||||
enum rfkill_state {
|
||||
RFKILL_STATE_SOFT_BLOCKED = 0, /* Radio output blocked */
|
||||
RFKILL_STATE_UNBLOCKED = 1, /* Radio output allowed */
|
||||
RFKILL_STATE_HARD_BLOCKED = 2, /* Output blocked, non-overrideable */
|
||||
RFKILL_STATE_MAX, /* marker for last valid state */
|
||||
};
|
||||
/* this is opaque */
|
||||
struct rfkill;
|
||||
|
||||
/**
|
||||
* struct rfkill - rfkill control structure.
|
||||
* @name: Name of the switch.
|
||||
* @type: Radio type which the button controls, the value stored
|
||||
* here should be a value from enum rfkill_type.
|
||||
* @state: State of the switch, "UNBLOCKED" means radio can operate.
|
||||
* @mutex: Guards switch state transitions. It serializes callbacks
|
||||
* and also protects the state.
|
||||
* @data: Pointer to the RF button drivers private data which will be
|
||||
* passed along when toggling radio state.
|
||||
* @toggle_radio(): Mandatory handler to control state of the radio.
|
||||
* only RFKILL_STATE_SOFT_BLOCKED and RFKILL_STATE_UNBLOCKED are
|
||||
* valid parameters.
|
||||
* @get_state(): handler to read current radio state from hardware,
|
||||
* may be called from atomic context, should return 0 on success.
|
||||
* Either this handler OR judicious use of rfkill_force_state() is
|
||||
* MANDATORY for any driver capable of RFKILL_STATE_HARD_BLOCKED.
|
||||
* @led_trigger: A LED trigger for this button's LED.
|
||||
* @dev: Device structure integrating the switch into device tree.
|
||||
* @node: Used to place switch into list of all switches known to the
|
||||
* the system.
|
||||
* struct rfkill_ops - rfkill driver methods
|
||||
*
|
||||
* This structure represents a RF switch located on a network device.
|
||||
* @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 {
|
||||
const char *name;
|
||||
enum rfkill_type type;
|
||||
|
||||
/* the mutex serializes callbacks and also protects
|
||||
* 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;
|
||||
#endif
|
||||
|
||||
struct device dev;
|
||||
struct list_head node;
|
||||
enum rfkill_state state_for_resume;
|
||||
struct rfkill_ops {
|
||||
void (*poll)(struct rfkill *rfkill, void *data);
|
||||
void (*query)(struct rfkill *rfkill, void *data);
|
||||
int (*set_block)(void *data, bool blocked);
|
||||
};
|
||||
#define to_rfkill(d) container_of(d, struct rfkill, dev)
|
||||
|
||||
struct rfkill * __must_check rfkill_allocate(struct device *parent,
|
||||
enum rfkill_type type);
|
||||
void rfkill_free(struct rfkill *rfkill);
|
||||
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
|
||||
/**
|
||||
* rfkill_alloc - allocate rfkill structure
|
||||
* @name: name of the struct -- the string is not copied internally
|
||||
* @parent: device that has rf switch on it
|
||||
* @type: type of the switch (RFKILL_TYPE_*)
|
||||
* @ops: rfkill methods
|
||||
* @ops_data: data passed to each method
|
||||
*
|
||||
* This function should be called by the transmitter driver to allocate an
|
||||
* rfkill structure. Returns %NULL on failure.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* rfkill_register - Register a rfkill structure.
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* rfkill_pause_polling(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_pause_polling(struct rfkill *rfkill);
|
||||
|
||||
/**
|
||||
* rfkill_resume_polling(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);
|
||||
|
||||
int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state);
|
||||
int rfkill_set_default(enum rfkill_type type, enum rfkill_state state);
|
||||
|
||||
/**
|
||||
* rfkill_state_complement - return complementar state
|
||||
* @state: state to return the complement of
|
||||
* rfkill_destroy - free rfkill structure
|
||||
* @rfkill: rfkill structure to be destroyed
|
||||
*
|
||||
* Returns RFKILL_STATE_SOFT_BLOCKED if @state is RFKILL_STATE_UNBLOCKED,
|
||||
* returns RFKILL_STATE_UNBLOCKED otherwise.
|
||||
* Destroys the rfkill structure.
|
||||
*/
|
||||
static inline enum rfkill_state rfkill_state_complement(enum rfkill_state state)
|
||||
{
|
||||
return (state == RFKILL_STATE_UNBLOCKED) ?
|
||||
RFKILL_STATE_SOFT_BLOCKED : RFKILL_STATE_UNBLOCKED;
|
||||
}
|
||||
void rfkill_destroy(struct rfkill *rfkill);
|
||||
|
||||
/**
|
||||
* rfkill_get_led_name - Get the LED trigger name for the button's LED.
|
||||
* This function might return a NULL pointer if registering of the
|
||||
* LED trigger failed.
|
||||
* Use this as "default_trigger" for the LED.
|
||||
* 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.
|
||||
*/
|
||||
static inline char *rfkill_get_led_name(struct rfkill *rfkill)
|
||||
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_set_global_sw_state - set global sw block default
|
||||
* @type: rfkill type to set default for
|
||||
* @blocked: default to set
|
||||
*
|
||||
* This function sets the global default -- use at boot if your platform has
|
||||
* an rfkill switch. If not early enough this call may be ignored.
|
||||
*
|
||||
* XXX: instead of ignoring -- how about just updating all currently
|
||||
* registered drivers?
|
||||
*/
|
||||
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)
|
||||
{
|
||||
#ifdef CONFIG_RFKILL_LEDS
|
||||
return (char *)(rfkill->led_trigger.name);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
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)
|
||||
{
|
||||
}
|
||||
#endif /* RFKILL || RFKILL_MODULE */
|
||||
|
||||
|
||||
#ifdef CONFIG_RFKILL_LEDS
|
||||
/**
|
||||
* 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
|
||||
* LED trigger failed. Use this as "default_trigger" for the LED.
|
||||
*/
|
||||
const char *rfkill_get_led_trigger_name(struct rfkill *rfkill);
|
||||
|
||||
/**
|
||||
* 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
|
||||
static inline const char *rfkill_get_led_trigger_name(struct rfkill *rfkill)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* RFKILL_H */
|
||||
|
@ -253,7 +253,6 @@
|
||||
struct net_device;
|
||||
struct genl_info;
|
||||
struct wimax_dev;
|
||||
struct input_dev;
|
||||
|
||||
/**
|
||||
* struct wimax_dev - Generic WiMAX device
|
||||
@ -293,8 +292,8 @@ struct input_dev;
|
||||
* See wimax_reset()'s documentation.
|
||||
*
|
||||
* @name: [fill] A way to identify this device. We need to register a
|
||||
* name with many subsystems (input for RFKILL, workqueue
|
||||
* creation, etc). We can't use the network device name as that
|
||||
* name with many subsystems (rfkill, workqueue creation, etc).
|
||||
* We can't use the network device name as that
|
||||
* 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
|
||||
* using the driver name and device bus id, place it here and use
|
||||
@ -316,9 +315,6 @@ struct input_dev;
|
||||
*
|
||||
* @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_hw: [private] State of the hardware radio switch (OFF/ON)
|
||||
|
@ -10,22 +10,15 @@ menuconfig RFKILL
|
||||
To compile this driver as a module, choose M here: the
|
||||
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
|
||||
config RFKILL_LEDS
|
||||
bool
|
||||
depends on RFKILL && LEDS_TRIGGERS
|
||||
depends on RFKILL
|
||||
depends on LEDS_TRIGGERS = y || RFKILL = LEDS_TRIGGERS
|
||||
default y
|
||||
|
||||
config RFKILL_INPUT
|
||||
bool
|
||||
depends on RFKILL
|
||||
depends on INPUT = y || RFKILL = INPUT
|
||||
default y
|
||||
|
@ -2,5 +2,6 @@
|
||||
# Makefile for the RF switch subsystem.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_RFKILL) += rfkill.o
|
||||
obj-$(CONFIG_RFKILL_INPUT) += rfkill-input.o
|
||||
rfkill-y += core.o
|
||||
rfkill-$(CONFIG_RFKILL_INPUT) += input.o
|
||||
obj-$(CONFIG_RFKILL) += rfkill.o
|
||||
|
896
net/rfkill/core.c
Normal file
896
net/rfkill/core.c
Normal file
@ -0,0 +1,896 @@
|
||||
/*
|
||||
* Copyright (C) 2006 - 2007 Ivo van Doorn
|
||||
* 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 under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.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)
|
||||
|
||||
|
||||
|
||||
MODULE_AUTHOR("Ivo van Doorn <IvDoorn@gmail.com>");
|
||||
MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
|
||||
MODULE_DESCRIPTION("RF switch support");
|
||||
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 DEFINE_MUTEX(rfkill_global_mutex);
|
||||
|
||||
static unsigned int rfkill_default_state = 1;
|
||||
module_param_named(default_state, rfkill_default_state, uint, 0444);
|
||||
MODULE_PARM_DESC(default_state,
|
||||
"Default initial state for all radio types, 0 = radio off");
|
||||
|
||||
static struct {
|
||||
bool cur, def;
|
||||
} rfkill_global_states[NUM_RFKILL_TYPES];
|
||||
|
||||
static unsigned long rfkill_states_default_locked;
|
||||
|
||||
static bool rfkill_epo_lock_active;
|
||||
|
||||
|
||||
#ifdef CONFIG_RFKILL_LEDS
|
||||
static void rfkill_led_trigger_event(struct rfkill *rfkill)
|
||||
{
|
||||
struct led_trigger *trigger;
|
||||
|
||||
if (!rfkill->registered)
|
||||
return;
|
||||
|
||||
trigger = &rfkill->led_trigger;
|
||||
|
||||
if (rfkill->state & RFKILL_BLOCK_ANY)
|
||||
led_trigger_event(trigger, LED_OFF);
|
||||
else
|
||||
led_trigger_event(trigger, LED_FULL);
|
||||
}
|
||||
|
||||
static void rfkill_led_trigger_activate(struct led_classdev *led)
|
||||
{
|
||||
struct rfkill *rfkill;
|
||||
|
||||
rfkill = container_of(led->trigger, struct rfkill, led_trigger);
|
||||
|
||||
rfkill_led_trigger_event(rfkill);
|
||||
}
|
||||
|
||||
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
|
||||
static void rfkill_led_trigger_event(struct rfkill *rfkill)
|
||||
{
|
||||
}
|
||||
|
||||
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 */
|
||||
|
||||
static void rfkill_uevent(struct rfkill *rfkill)
|
||||
{
|
||||
if (!rfkill->registered || rfkill->suspended)
|
||||
return;
|
||||
|
||||
kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE);
|
||||
}
|
||||
|
||||
static bool __rfkill_set_hw_state(struct rfkill *rfkill,
|
||||
bool blocked, bool *change)
|
||||
{
|
||||
unsigned long flags;
|
||||
bool prev, any;
|
||||
|
||||
BUG_ON(!rfkill);
|
||||
|
||||
spin_lock_irqsave(&rfkill->lock, flags);
|
||||
prev = !!(rfkill->state & RFKILL_BLOCK_HW);
|
||||
if (blocked)
|
||||
rfkill->state |= RFKILL_BLOCK_HW;
|
||||
else
|
||||
rfkill->state &= ~RFKILL_BLOCK_HW;
|
||||
*change = prev != blocked;
|
||||
any = rfkill->state & RFKILL_BLOCK_ANY;
|
||||
spin_unlock_irqrestore(&rfkill->lock, flags);
|
||||
|
||||
rfkill_led_trigger_event(rfkill);
|
||||
|
||||
return any;
|
||||
}
|
||||
|
||||
/**
|
||||
* rfkill_set_block - wrapper for set_block method
|
||||
*
|
||||
* @rfkill: the rfkill struct to use
|
||||
* @blocked: the new software state
|
||||
*
|
||||
* Calls the set_block method (when applicable) and handles notifications
|
||||
* etc. as well.
|
||||
*/
|
||||
static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
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 (blocked)
|
||||
rfkill->state |= RFKILL_BLOCK_SW;
|
||||
else
|
||||
rfkill->state &= ~RFKILL_BLOCK_SW;
|
||||
|
||||
rfkill->state |= RFKILL_BLOCK_SW_SETCALL;
|
||||
spin_unlock_irqrestore(&rfkill->lock, flags);
|
||||
|
||||
if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP))
|
||||
return;
|
||||
|
||||
err = rfkill->ops->set_block(rfkill->data, blocked);
|
||||
|
||||
spin_lock_irqsave(&rfkill->lock, flags);
|
||||
if (err) {
|
||||
/*
|
||||
* Failed -- reset status to _prev, this may be different
|
||||
* 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);
|
||||
|
||||
rfkill_led_trigger_event(rfkill);
|
||||
rfkill_uevent(rfkill);
|
||||
}
|
||||
|
||||
/**
|
||||
* __rfkill_switch_all - Toggle state of all switches of given type
|
||||
* @type: type of interfaces to be affected
|
||||
* @state: the new state
|
||||
*
|
||||
* This function sets the state of all switches of given type,
|
||||
* unless a specific switch is claimed by userspace (in which case,
|
||||
* that switch is left alone) or suspended.
|
||||
*
|
||||
* Caller must have acquired rfkill_global_mutex.
|
||||
*/
|
||||
static void __rfkill_switch_all(const enum rfkill_type type, bool blocked)
|
||||
{
|
||||
struct rfkill *rfkill;
|
||||
|
||||
rfkill_global_states[type].cur = blocked;
|
||||
list_for_each_entry(rfkill, &rfkill_list, node) {
|
||||
if (rfkill->type != type)
|
||||
continue;
|
||||
|
||||
rfkill_set_block(rfkill, blocked);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* rfkill_switch_all - Toggle state of all switches of given type
|
||||
* @type: type of interfaces to be affected
|
||||
* @state: the new state
|
||||
*
|
||||
* Acquires rfkill_global_mutex and calls __rfkill_switch_all(@type, @state).
|
||||
* Please refer to __rfkill_switch_all() for details.
|
||||
*
|
||||
* Does nothing if the EPO lock is active.
|
||||
*/
|
||||
void rfkill_switch_all(enum rfkill_type type, bool blocked)
|
||||
{
|
||||
mutex_lock(&rfkill_global_mutex);
|
||||
|
||||
if (!rfkill_epo_lock_active)
|
||||
__rfkill_switch_all(type, blocked);
|
||||
|
||||
mutex_unlock(&rfkill_global_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* rfkill_epo - emergency power off all transmitters
|
||||
*
|
||||
* This kicks all non-suspended rfkill devices to RFKILL_STATE_SOFT_BLOCKED,
|
||||
* ignoring everything in its path but rfkill_global_mutex and rfkill->mutex.
|
||||
*
|
||||
* The global state before the EPO is saved and can be restored later
|
||||
* using rfkill_restore_states().
|
||||
*/
|
||||
void rfkill_epo(void)
|
||||
{
|
||||
struct rfkill *rfkill;
|
||||
int i;
|
||||
|
||||
mutex_lock(&rfkill_global_mutex);
|
||||
|
||||
rfkill_epo_lock_active = true;
|
||||
list_for_each_entry(rfkill, &rfkill_list, node)
|
||||
rfkill_set_block(rfkill, true);
|
||||
|
||||
for (i = 0; i < NUM_RFKILL_TYPES; i++) {
|
||||
rfkill_global_states[i].def = rfkill_global_states[i].cur;
|
||||
rfkill_global_states[i].cur = true;
|
||||
}
|
||||
mutex_unlock(&rfkill_global_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* rfkill_restore_states - restore global states
|
||||
*
|
||||
* Restore (and sync switches to) the global state from the
|
||||
* states in rfkill_default_states. This can undo the effects of
|
||||
* a call to rfkill_epo().
|
||||
*/
|
||||
void rfkill_restore_states(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
mutex_lock(&rfkill_global_mutex);
|
||||
|
||||
rfkill_epo_lock_active = false;
|
||||
for (i = 0; i < NUM_RFKILL_TYPES; i++)
|
||||
__rfkill_switch_all(i, rfkill_global_states[i].def);
|
||||
mutex_unlock(&rfkill_global_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* rfkill_remove_epo_lock - unlock state changes
|
||||
*
|
||||
* Used by rfkill-input manually unlock state changes, when
|
||||
* the EPO switch is deactivated.
|
||||
*/
|
||||
void rfkill_remove_epo_lock(void)
|
||||
{
|
||||
mutex_lock(&rfkill_global_mutex);
|
||||
rfkill_epo_lock_active = false;
|
||||
mutex_unlock(&rfkill_global_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* rfkill_is_epo_lock_active - returns true EPO is active
|
||||
*
|
||||
* Returns 0 (false) if there is NOT an active EPO contidion,
|
||||
* and 1 (true) if there is an active EPO contition, which
|
||||
* locks all radios in one of the BLOCKED states.
|
||||
*
|
||||
* Can be called in atomic context.
|
||||
*/
|
||||
bool rfkill_is_epo_lock_active(void)
|
||||
{
|
||||
return rfkill_epo_lock_active;
|
||||
}
|
||||
|
||||
/**
|
||||
* rfkill_get_global_sw_state - returns global state for a type
|
||||
* @type: the type to get the global state of
|
||||
*
|
||||
* Returns the current global state for a given wireless
|
||||
* device type.
|
||||
*/
|
||||
bool rfkill_get_global_sw_state(const enum rfkill_type type)
|
||||
{
|
||||
return rfkill_global_states[type].cur;
|
||||
}
|
||||
|
||||
void rfkill_set_global_sw_state(const enum rfkill_type type, bool blocked)
|
||||
{
|
||||
mutex_lock(&rfkill_global_mutex);
|
||||
|
||||
/* don't allow unblock when epo */
|
||||
if (rfkill_epo_lock_active && !blocked)
|
||||
goto out;
|
||||
|
||||
/* too late */
|
||||
if (rfkill_states_default_locked & BIT(type))
|
||||
goto out;
|
||||
|
||||
rfkill_states_default_locked |= BIT(type);
|
||||
|
||||
rfkill_global_states[type].cur = blocked;
|
||||
rfkill_global_states[type].def = blocked;
|
||||
out:
|
||||
mutex_unlock(&rfkill_global_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(rfkill_set_global_sw_state);
|
||||
|
||||
|
||||
bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked)
|
||||
{
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
if (!rfkill->registered)
|
||||
return blocked;
|
||||
|
||||
if (prev != blocked && !hwblock)
|
||||
schedule_work(&rfkill->uevent_work);
|
||||
|
||||
rfkill_led_trigger_event(rfkill);
|
||||
|
||||
return blocked;
|
||||
}
|
||||
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,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rfkill *rfkill = to_rfkill(dev);
|
||||
|
||||
return sprintf(buf, "%s\n", rfkill->name);
|
||||
}
|
||||
|
||||
static const char *rfkill_get_type_str(enum rfkill_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case RFKILL_TYPE_WLAN:
|
||||
return "wlan";
|
||||
case RFKILL_TYPE_BLUETOOTH:
|
||||
return "bluetooth";
|
||||
case RFKILL_TYPE_UWB:
|
||||
return "ultrawideband";
|
||||
case RFKILL_TYPE_WIMAX:
|
||||
return "wimax";
|
||||
case RFKILL_TYPE_WWAN:
|
||||
return "wwan";
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
BUILD_BUG_ON(NUM_RFKILL_TYPES != RFKILL_TYPE_WWAN + 1);
|
||||
}
|
||||
|
||||
static ssize_t rfkill_type_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rfkill *rfkill = to_rfkill(dev);
|
||||
|
||||
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,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rfkill *rfkill = to_rfkill(dev);
|
||||
unsigned long flags;
|
||||
u32 state;
|
||||
|
||||
spin_lock_irqsave(&rfkill->lock, flags);
|
||||
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,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
/*
|
||||
* The intention was that userspace can only take control over
|
||||
* 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.
|
||||
*/
|
||||
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
static ssize_t rfkill_claim_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", 0);
|
||||
}
|
||||
|
||||
static ssize_t rfkill_claim_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static struct device_attribute rfkill_dev_attrs[] = {
|
||||
__ATTR(name, S_IRUGO, rfkill_name_show, NULL),
|
||||
__ATTR(type, S_IRUGO, rfkill_type_show, NULL),
|
||||
__ATTR(state, S_IRUGO|S_IWUSR, rfkill_state_show, rfkill_state_store),
|
||||
__ATTR(claim, S_IRUGO|S_IWUSR, rfkill_claim_show, rfkill_claim_store),
|
||||
__ATTR_NULL
|
||||
};
|
||||
|
||||
static void rfkill_release(struct device *dev)
|
||||
{
|
||||
struct rfkill *rfkill = to_rfkill(dev);
|
||||
|
||||
kfree(rfkill);
|
||||
}
|
||||
|
||||
static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
struct rfkill *rfkill = to_rfkill(dev);
|
||||
unsigned long flags;
|
||||
u32 state;
|
||||
int error;
|
||||
|
||||
error = add_uevent_var(env, "RFKILL_NAME=%s", rfkill->name);
|
||||
if (error)
|
||||
return error;
|
||||
error = add_uevent_var(env, "RFKILL_TYPE=%s",
|
||||
rfkill_get_type_str(rfkill->type));
|
||||
if (error)
|
||||
return error;
|
||||
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;
|
||||
}
|
||||
|
||||
void rfkill_pause_polling(struct rfkill *rfkill)
|
||||
{
|
||||
BUG_ON(!rfkill);
|
||||
|
||||
if (!rfkill->ops->poll)
|
||||
return;
|
||||
|
||||
cancel_delayed_work_sync(&rfkill->poll_work);
|
||||
}
|
||||
EXPORT_SYMBOL(rfkill_pause_polling);
|
||||
|
||||
void rfkill_resume_polling(struct rfkill *rfkill)
|
||||
{
|
||||
BUG_ON(!rfkill);
|
||||
|
||||
if (!rfkill->ops->poll)
|
||||
return;
|
||||
|
||||
schedule_work(&rfkill->poll_work.work);
|
||||
}
|
||||
EXPORT_SYMBOL(rfkill_resume_polling);
|
||||
|
||||
static int rfkill_suspend(struct device *dev, pm_message_t state)
|
||||
{
|
||||
struct rfkill *rfkill = to_rfkill(dev);
|
||||
|
||||
rfkill_pause_polling(rfkill);
|
||||
|
||||
rfkill->suspended = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rfkill_resume(struct device *dev)
|
||||
{
|
||||
struct rfkill *rfkill = to_rfkill(dev);
|
||||
bool cur;
|
||||
|
||||
mutex_lock(&rfkill_global_mutex);
|
||||
cur = rfkill_global_states[rfkill->type].cur;
|
||||
rfkill_set_block(rfkill, cur);
|
||||
mutex_unlock(&rfkill_global_mutex);
|
||||
|
||||
rfkill->suspended = false;
|
||||
|
||||
schedule_work(&rfkill->uevent_work);
|
||||
|
||||
rfkill_resume_polling(rfkill);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct class rfkill_class = {
|
||||
.name = "rfkill",
|
||||
.dev_release = rfkill_release,
|
||||
.dev_attrs = rfkill_dev_attrs,
|
||||
.dev_uevent = rfkill_dev_uevent,
|
||||
.suspend = rfkill_suspend,
|
||||
.resume = rfkill_resume,
|
||||
};
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
struct rfkill *rfkill;
|
||||
struct device *dev;
|
||||
|
||||
if (WARN_ON(!ops))
|
||||
return NULL;
|
||||
|
||||
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)
|
||||
return NULL;
|
||||
|
||||
spin_lock_init(&rfkill->lock);
|
||||
INIT_LIST_HEAD(&rfkill->node);
|
||||
rfkill->type = type;
|
||||
rfkill->name = name;
|
||||
rfkill->ops = ops;
|
||||
rfkill->data = ops_data;
|
||||
|
||||
dev = &rfkill->dev;
|
||||
dev->class = &rfkill_class;
|
||||
dev->parent = parent;
|
||||
device_initialize(dev);
|
||||
|
||||
return rfkill;
|
||||
}
|
||||
EXPORT_SYMBOL(rfkill_alloc);
|
||||
|
||||
static void rfkill_poll(struct work_struct *work)
|
||||
{
|
||||
struct rfkill *rfkill;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
static void rfkill_uevent_work(struct work_struct *work)
|
||||
{
|
||||
struct rfkill *rfkill;
|
||||
|
||||
rfkill = container_of(work, struct rfkill, uevent_work);
|
||||
|
||||
rfkill_uevent(rfkill);
|
||||
}
|
||||
|
||||
static void rfkill_sync_work(struct work_struct *work)
|
||||
{
|
||||
struct rfkill *rfkill;
|
||||
bool cur;
|
||||
|
||||
rfkill = container_of(work, struct rfkill, sync_work);
|
||||
|
||||
mutex_lock(&rfkill_global_mutex);
|
||||
cur = rfkill_global_states[rfkill->type].cur;
|
||||
rfkill_set_block(rfkill, cur);
|
||||
mutex_unlock(&rfkill_global_mutex);
|
||||
}
|
||||
|
||||
int __must_check rfkill_register(struct rfkill *rfkill)
|
||||
{
|
||||
static unsigned long rfkill_no;
|
||||
struct device *dev = &rfkill->dev;
|
||||
int error;
|
||||
|
||||
BUG_ON(!rfkill);
|
||||
|
||||
mutex_lock(&rfkill_global_mutex);
|
||||
|
||||
if (rfkill->registered) {
|
||||
error = -EALREADY;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
dev_set_name(dev, "rfkill%lu", rfkill_no);
|
||||
rfkill_no++;
|
||||
|
||||
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);
|
||||
if (error)
|
||||
goto remove;
|
||||
|
||||
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;
|
||||
|
||||
devdel:
|
||||
device_del(&rfkill->dev);
|
||||
remove:
|
||||
list_del_init(&rfkill->node);
|
||||
unlock:
|
||||
mutex_unlock(&rfkill_global_mutex);
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL(rfkill_register);
|
||||
|
||||
void rfkill_unregister(struct rfkill *rfkill)
|
||||
{
|
||||
BUG_ON(!rfkill);
|
||||
|
||||
if (rfkill->ops->poll)
|
||||
cancel_delayed_work_sync(&rfkill->poll_work);
|
||||
|
||||
cancel_work_sync(&rfkill->uevent_work);
|
||||
cancel_work_sync(&rfkill->sync_work);
|
||||
|
||||
rfkill->registered = false;
|
||||
|
||||
device_del(&rfkill->dev);
|
||||
|
||||
mutex_lock(&rfkill_global_mutex);
|
||||
list_del_init(&rfkill->node);
|
||||
mutex_unlock(&rfkill_global_mutex);
|
||||
|
||||
rfkill_led_trigger_unregister(rfkill);
|
||||
}
|
||||
EXPORT_SYMBOL(rfkill_unregister);
|
||||
|
||||
void rfkill_destroy(struct rfkill *rfkill)
|
||||
{
|
||||
if (rfkill)
|
||||
put_device(&rfkill->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(rfkill_destroy);
|
||||
|
||||
|
||||
static int __init rfkill_init(void)
|
||||
{
|
||||
int error;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_RFKILL_TYPES; i++)
|
||||
rfkill_global_states[i].def = !rfkill_default_state;
|
||||
|
||||
error = class_register(&rfkill_class);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
#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)
|
||||
{
|
||||
#ifdef CONFIG_RFKILL_INPUT
|
||||
rfkill_handler_exit();
|
||||
#endif
|
||||
class_unregister(&rfkill_class);
|
||||
}
|
||||
module_exit(rfkill_exit);
|
342
net/rfkill/input.c
Normal file
342
net/rfkill/input.c
Normal file
@ -0,0 +1,342 @@
|
||||
/*
|
||||
* Input layer to RF Kill interface connector
|
||||
*
|
||||
* 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
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* 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/input.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include "rfkill.h"
|
||||
|
||||
enum rfkill_input_master_mode {
|
||||
RFKILL_INPUT_MASTER_UNLOCK = 0,
|
||||
RFKILL_INPUT_MASTER_RESTORE = 1,
|
||||
RFKILL_INPUT_MASTER_UNBLOCKALL = 2,
|
||||
NUM_RFKILL_INPUT_MASTER_MODES
|
||||
};
|
||||
|
||||
/* Delay (in ms) between consecutive switch ops */
|
||||
#define RFKILL_OPS_DELAY 200
|
||||
|
||||
static enum rfkill_input_master_mode rfkill_master_switch_mode =
|
||||
RFKILL_INPUT_MASTER_UNBLOCKALL;
|
||||
module_param_named(master_switch_mode, rfkill_master_switch_mode, uint, 0);
|
||||
MODULE_PARM_DESC(master_switch_mode,
|
||||
"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_sched_op {
|
||||
RFKILL_GLOBAL_OP_EPO = 0,
|
||||
RFKILL_GLOBAL_OP_RESTORE,
|
||||
RFKILL_GLOBAL_OP_UNLOCK,
|
||||
RFKILL_GLOBAL_OP_UNBLOCK,
|
||||
};
|
||||
|
||||
static enum rfkill_sched_op rfkill_master_switch_op;
|
||||
static enum rfkill_sched_op rfkill_op;
|
||||
|
||||
static void __rfkill_handle_global_op(enum rfkill_sched_op op)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
switch (op) {
|
||||
case RFKILL_GLOBAL_OP_EPO:
|
||||
rfkill_epo();
|
||||
break;
|
||||
case RFKILL_GLOBAL_OP_RESTORE:
|
||||
rfkill_restore_states();
|
||||
break;
|
||||
case RFKILL_GLOBAL_OP_UNLOCK:
|
||||
rfkill_remove_epo_lock();
|
||||
break;
|
||||
case RFKILL_GLOBAL_OP_UNBLOCK:
|
||||
rfkill_remove_epo_lock();
|
||||
for (i = 0; i < NUM_RFKILL_TYPES; i++)
|
||||
rfkill_switch_all(i, false);
|
||||
break;
|
||||
default:
|
||||
/* memory corruption or bug, fail safely */
|
||||
rfkill_epo();
|
||||
WARN(1, "Unknown requested operation %d! "
|
||||
"rfkill Emergency Power Off activated\n",
|
||||
op);
|
||||
}
|
||||
}
|
||||
|
||||
static void __rfkill_handle_normal_op(const enum rfkill_type type,
|
||||
const bool complement)
|
||||
{
|
||||
bool blocked;
|
||||
|
||||
blocked = rfkill_get_global_sw_state(type);
|
||||
if (complement)
|
||||
blocked = !blocked;
|
||||
|
||||
rfkill_switch_all(type, blocked);
|
||||
}
|
||||
|
||||
static void rfkill_op_handler(struct work_struct *work)
|
||||
{
|
||||
unsigned int i;
|
||||
bool c;
|
||||
|
||||
spin_lock_irq(&rfkill_op_lock);
|
||||
do {
|
||||
if (rfkill_op_pending) {
|
||||
enum rfkill_sched_op op = rfkill_op;
|
||||
rfkill_op_pending = false;
|
||||
memset(rfkill_sw_pending, 0,
|
||||
sizeof(rfkill_sw_pending));
|
||||
spin_unlock_irq(&rfkill_op_lock);
|
||||
|
||||
__rfkill_handle_global_op(op);
|
||||
|
||||
spin_lock_irq(&rfkill_op_lock);
|
||||
|
||||
/*
|
||||
* handle global ops first -- during unlocked period
|
||||
* we might have gotten a new global op.
|
||||
*/
|
||||
if (rfkill_op_pending)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rfkill_is_epo_lock_active())
|
||||
continue;
|
||||
|
||||
for (i = 0; i < NUM_RFKILL_TYPES; i++) {
|
||||
if (__test_and_clear_bit(i, rfkill_sw_pending)) {
|
||||
c = __test_and_clear_bit(i, rfkill_sw_state);
|
||||
spin_unlock_irq(&rfkill_op_lock);
|
||||
|
||||
__rfkill_handle_normal_op(i, c);
|
||||
|
||||
spin_lock_irq(&rfkill_op_lock);
|
||||
}
|
||||
}
|
||||
} while (rfkill_op_pending);
|
||||
spin_unlock_irq(&rfkill_op_lock);
|
||||
}
|
||||
|
||||
static DECLARE_DELAYED_WORK(rfkill_op_work, rfkill_op_handler);
|
||||
static unsigned long rfkill_last_scheduled;
|
||||
|
||||
static unsigned long rfkill_ratelimit(const unsigned long last)
|
||||
{
|
||||
const unsigned long delay = msecs_to_jiffies(RFKILL_OPS_DELAY);
|
||||
return (time_after(jiffies, last + delay)) ? 0 : delay;
|
||||
}
|
||||
|
||||
static void rfkill_schedule_ratelimited(void)
|
||||
{
|
||||
if (delayed_work_pending(&rfkill_op_work))
|
||||
return;
|
||||
schedule_delayed_work(&rfkill_op_work,
|
||||
rfkill_ratelimit(rfkill_last_scheduled));
|
||||
rfkill_last_scheduled = jiffies;
|
||||
}
|
||||
|
||||
static void rfkill_schedule_global_op(enum rfkill_sched_op op)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&rfkill_op_lock, flags);
|
||||
rfkill_op = op;
|
||||
rfkill_op_pending = true;
|
||||
if (op == RFKILL_GLOBAL_OP_EPO && !rfkill_is_epo_lock_active()) {
|
||||
/* bypass the limiter for EPO */
|
||||
cancel_delayed_work(&rfkill_op_work);
|
||||
schedule_delayed_work(&rfkill_op_work, 0);
|
||||
rfkill_last_scheduled = jiffies;
|
||||
} else
|
||||
rfkill_schedule_ratelimited();
|
||||
spin_unlock_irqrestore(&rfkill_op_lock, flags);
|
||||
}
|
||||
|
||||
static void rfkill_schedule_toggle(enum rfkill_type type)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (rfkill_is_epo_lock_active())
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&rfkill_op_lock, flags);
|
||||
if (!rfkill_op_pending) {
|
||||
__set_bit(type, rfkill_sw_pending);
|
||||
__change_bit(type, rfkill_sw_state);
|
||||
rfkill_schedule_ratelimited();
|
||||
}
|
||||
spin_unlock_irqrestore(&rfkill_op_lock, flags);
|
||||
}
|
||||
|
||||
static void rfkill_schedule_evsw_rfkillall(int state)
|
||||
{
|
||||
if (state)
|
||||
rfkill_schedule_global_op(rfkill_master_switch_op);
|
||||
else
|
||||
rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO);
|
||||
}
|
||||
|
||||
static void rfkill_event(struct input_handle *handle, unsigned int type,
|
||||
unsigned int code, int data)
|
||||
{
|
||||
if (type == EV_KEY && data == 1) {
|
||||
switch (code) {
|
||||
case KEY_WLAN:
|
||||
rfkill_schedule_toggle(RFKILL_TYPE_WLAN);
|
||||
break;
|
||||
case KEY_BLUETOOTH:
|
||||
rfkill_schedule_toggle(RFKILL_TYPE_BLUETOOTH);
|
||||
break;
|
||||
case KEY_UWB:
|
||||
rfkill_schedule_toggle(RFKILL_TYPE_UWB);
|
||||
break;
|
||||
case KEY_WIMAX:
|
||||
rfkill_schedule_toggle(RFKILL_TYPE_WIMAX);
|
||||
break;
|
||||
}
|
||||
} else if (type == EV_SW && code == SW_RFKILL_ALL)
|
||||
rfkill_schedule_evsw_rfkillall(data);
|
||||
}
|
||||
|
||||
static int rfkill_connect(struct input_handler *handler, struct input_dev *dev,
|
||||
const struct input_device_id *id)
|
||||
{
|
||||
struct input_handle *handle;
|
||||
int error;
|
||||
|
||||
handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
|
||||
if (!handle)
|
||||
return -ENOMEM;
|
||||
|
||||
handle->dev = dev;
|
||||
handle->handler = handler;
|
||||
handle->name = "rfkill";
|
||||
|
||||
/* causes rfkill_start() to be called */
|
||||
error = input_register_handle(handle);
|
||||
if (error)
|
||||
goto err_free_handle;
|
||||
|
||||
error = input_open_device(handle);
|
||||
if (error)
|
||||
goto err_unregister_handle;
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_handle:
|
||||
input_unregister_handle(handle);
|
||||
err_free_handle:
|
||||
kfree(handle);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void rfkill_start(struct input_handle *handle)
|
||||
{
|
||||
/*
|
||||
* Take event_lock to guard against configuration changes, we
|
||||
* should be able to deal with concurrency with rfkill_event()
|
||||
* just fine (which event_lock will also avoid).
|
||||
*/
|
||||
spin_lock_irq(&handle->dev->event_lock);
|
||||
|
||||
if (test_bit(EV_SW, handle->dev->evbit) &&
|
||||
test_bit(SW_RFKILL_ALL, handle->dev->swbit))
|
||||
rfkill_schedule_evsw_rfkillall(test_bit(SW_RFKILL_ALL,
|
||||
handle->dev->sw));
|
||||
|
||||
spin_unlock_irq(&handle->dev->event_lock);
|
||||
}
|
||||
|
||||
static void rfkill_disconnect(struct input_handle *handle)
|
||||
{
|
||||
input_close_device(handle);
|
||||
input_unregister_handle(handle);
|
||||
kfree(handle);
|
||||
}
|
||||
|
||||
static const struct input_device_id rfkill_ids[] = {
|
||||
{
|
||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
|
||||
.evbit = { BIT_MASK(EV_KEY) },
|
||||
.keybit = { [BIT_WORD(KEY_WLAN)] = BIT_MASK(KEY_WLAN) },
|
||||
},
|
||||
{
|
||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
|
||||
.evbit = { BIT_MASK(EV_KEY) },
|
||||
.keybit = { [BIT_WORD(KEY_BLUETOOTH)] = BIT_MASK(KEY_BLUETOOTH) },
|
||||
},
|
||||
{
|
||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
|
||||
.evbit = { BIT_MASK(EV_KEY) },
|
||||
.keybit = { [BIT_WORD(KEY_UWB)] = BIT_MASK(KEY_UWB) },
|
||||
},
|
||||
{
|
||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
|
||||
.evbit = { BIT_MASK(EV_KEY) },
|
||||
.keybit = { [BIT_WORD(KEY_WIMAX)] = BIT_MASK(KEY_WIMAX) },
|
||||
},
|
||||
{
|
||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT,
|
||||
.evbit = { BIT(EV_SW) },
|
||||
.swbit = { [BIT_WORD(SW_RFKILL_ALL)] = BIT_MASK(SW_RFKILL_ALL) },
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct input_handler rfkill_handler = {
|
||||
.name = "rfkill",
|
||||
.event = rfkill_event,
|
||||
.connect = rfkill_connect,
|
||||
.start = rfkill_start,
|
||||
.disconnect = rfkill_disconnect,
|
||||
.id_table = rfkill_ids,
|
||||
};
|
||||
|
||||
int __init rfkill_handler_init(void)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
spin_lock_init(&rfkill_op_lock);
|
||||
|
||||
/* Avoid delay at first schedule */
|
||||
rfkill_last_scheduled =
|
||||
jiffies - msecs_to_jiffies(RFKILL_OPS_DELAY) - 1;
|
||||
return input_register_handler(&rfkill_handler);
|
||||
}
|
||||
|
||||
void __exit rfkill_handler_exit(void)
|
||||
{
|
||||
input_unregister_handler(&rfkill_handler);
|
||||
cancel_delayed_work_sync(&rfkill_op_work);
|
||||
}
|
@ -1,390 +0,0 @@
|
||||
/*
|
||||
* Input layer to RF Kill interface connector
|
||||
*
|
||||
* Copyright (c) 2007 Dmitry Torokhov
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include "rfkill-input.h"
|
||||
|
||||
MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
|
||||
MODULE_DESCRIPTION("Input layer to RF switch connector");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
enum rfkill_input_master_mode {
|
||||
RFKILL_INPUT_MASTER_DONOTHING = 0,
|
||||
RFKILL_INPUT_MASTER_RESTORE = 1,
|
||||
RFKILL_INPUT_MASTER_UNBLOCKALL = 2,
|
||||
RFKILL_INPUT_MASTER_MAX, /* marker */
|
||||
};
|
||||
|
||||
/* Delay (in ms) between consecutive switch ops */
|
||||
#define RFKILL_OPS_DELAY 200
|
||||
|
||||
static enum rfkill_input_master_mode rfkill_master_switch_mode =
|
||||
RFKILL_INPUT_MASTER_UNBLOCKALL;
|
||||
module_param_named(master_switch_mode, rfkill_master_switch_mode, uint, 0);
|
||||
MODULE_PARM_DESC(master_switch_mode,
|
||||
"SW_RFKILL_ALL ON should: 0=do nothing; 1=restore; 2=unblock all");
|
||||
|
||||
enum rfkill_global_sched_op {
|
||||
RFKILL_GLOBAL_OP_EPO = 0,
|
||||
RFKILL_GLOBAL_OP_RESTORE,
|
||||
RFKILL_GLOBAL_OP_UNLOCK,
|
||||
RFKILL_GLOBAL_OP_UNBLOCK,
|
||||
};
|
||||
|
||||
struct rfkill_task {
|
||||
struct delayed_work dwork;
|
||||
|
||||
/* ensures that task is serialized */
|
||||
struct mutex mutex;
|
||||
|
||||
/* protects everything below */
|
||||
spinlock_t lock;
|
||||
|
||||
/* pending regular switch operations (1=pending) */
|
||||
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;
|
||||
|
||||
switch (op) {
|
||||
case RFKILL_GLOBAL_OP_EPO:
|
||||
rfkill_epo();
|
||||
break;
|
||||
case RFKILL_GLOBAL_OP_RESTORE:
|
||||
rfkill_restore_states();
|
||||
break;
|
||||
case RFKILL_GLOBAL_OP_UNLOCK:
|
||||
rfkill_remove_epo_lock();
|
||||
break;
|
||||
case RFKILL_GLOBAL_OP_UNBLOCK:
|
||||
rfkill_remove_epo_lock();
|
||||
for (i = 0; i < RFKILL_TYPE_MAX; i++)
|
||||
rfkill_switch_all(i, RFKILL_STATE_UNBLOCKED);
|
||||
break;
|
||||
default:
|
||||
/* memory corruption or bug, fail safely */
|
||||
rfkill_epo();
|
||||
WARN(1, "Unknown requested operation %d! "
|
||||
"rfkill Emergency Power Off activated\n",
|
||||
op);
|
||||
}
|
||||
}
|
||||
|
||||
static void __rfkill_handle_normal_op(const enum rfkill_type type,
|
||||
const bool c)
|
||||
{
|
||||
enum rfkill_state state;
|
||||
|
||||
state = rfkill_get_global_state(type);
|
||||
if (c)
|
||||
state = rfkill_state_complement(state);
|
||||
|
||||
rfkill_switch_all(type, state);
|
||||
}
|
||||
|
||||
static void rfkill_task_handler(struct work_struct *work)
|
||||
{
|
||||
struct rfkill_task *task = container_of(work,
|
||||
struct rfkill_task, dwork.work);
|
||||
bool doit = true;
|
||||
|
||||
mutex_lock(&task->mutex);
|
||||
|
||||
spin_lock_irq(&task->lock);
|
||||
while (doit) {
|
||||
if (task->global_op_pending) {
|
||||
enum rfkill_global_sched_op op = task->op;
|
||||
task->global_op_pending = false;
|
||||
memset(task->sw_pending, 0, sizeof(task->sw_pending));
|
||||
spin_unlock_irq(&task->lock);
|
||||
|
||||
__rfkill_handle_global_op(op);
|
||||
|
||||
/* make sure we do at least one pass with
|
||||
* !task->global_op_pending */
|
||||
spin_lock_irq(&task->lock);
|
||||
continue;
|
||||
} else if (!rfkill_is_epo_lock_active()) {
|
||||
unsigned int i = 0;
|
||||
|
||||
while (!task->global_op_pending &&
|
||||
i < RFKILL_TYPE_MAX) {
|
||||
if (test_and_clear_bit(i, task->sw_pending)) {
|
||||
bool c;
|
||||
c = test_and_clear_bit(i,
|
||||
task->sw_togglestate);
|
||||
spin_unlock_irq(&task->lock);
|
||||
|
||||
__rfkill_handle_normal_op(i, c);
|
||||
|
||||
spin_lock_irq(&task->lock);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
doit = task->global_op_pending;
|
||||
}
|
||||
spin_unlock_irq(&task->lock);
|
||||
|
||||
mutex_unlock(&task->mutex);
|
||||
}
|
||||
|
||||
static struct rfkill_task rfkill_task = {
|
||||
.dwork = __DELAYED_WORK_INITIALIZER(rfkill_task.dwork,
|
||||
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)
|
||||
{
|
||||
const unsigned long delay = msecs_to_jiffies(RFKILL_OPS_DELAY);
|
||||
return (time_after(jiffies, last + delay)) ? 0 : delay;
|
||||
}
|
||||
|
||||
static void rfkill_schedule_ratelimited(void)
|
||||
{
|
||||
if (!delayed_work_pending(&rfkill_task.dwork)) {
|
||||
schedule_delayed_work(&rfkill_task.dwork,
|
||||
rfkill_ratelimit(rfkill_task.last_scheduled));
|
||||
rfkill_task.last_scheduled = jiffies;
|
||||
}
|
||||
}
|
||||
|
||||
static void rfkill_schedule_global_op(enum rfkill_global_sched_op op)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&rfkill_task.lock, flags);
|
||||
rfkill_task.op = op;
|
||||
rfkill_task.global_op_pending = true;
|
||||
if (op == RFKILL_GLOBAL_OP_EPO && !rfkill_is_epo_lock_active()) {
|
||||
/* bypass the limiter for EPO */
|
||||
cancel_delayed_work(&rfkill_task.dwork);
|
||||
schedule_delayed_work(&rfkill_task.dwork, 0);
|
||||
rfkill_task.last_scheduled = jiffies;
|
||||
} else
|
||||
rfkill_schedule_ratelimited();
|
||||
spin_unlock_irqrestore(&rfkill_task.lock, flags);
|
||||
}
|
||||
|
||||
static void rfkill_schedule_toggle(enum rfkill_type type)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (rfkill_is_epo_lock_active())
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&rfkill_task.lock, flags);
|
||||
if (!rfkill_task.global_op_pending) {
|
||||
set_bit(type, rfkill_task.sw_pending);
|
||||
change_bit(type, rfkill_task.sw_togglestate);
|
||||
rfkill_schedule_ratelimited();
|
||||
}
|
||||
spin_unlock_irqrestore(&rfkill_task.lock, flags);
|
||||
}
|
||||
|
||||
static void rfkill_schedule_evsw_rfkillall(int state)
|
||||
{
|
||||
if (state) {
|
||||
switch (rfkill_master_switch_mode) {
|
||||
case RFKILL_INPUT_MASTER_UNBLOCKALL:
|
||||
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);
|
||||
}
|
||||
|
||||
static void rfkill_event(struct input_handle *handle, unsigned int type,
|
||||
unsigned int code, int data)
|
||||
{
|
||||
if (type == EV_KEY && data == 1) {
|
||||
enum rfkill_type t;
|
||||
|
||||
switch (code) {
|
||||
case KEY_WLAN:
|
||||
t = RFKILL_TYPE_WLAN;
|
||||
break;
|
||||
case KEY_BLUETOOTH:
|
||||
t = RFKILL_TYPE_BLUETOOTH;
|
||||
break;
|
||||
case KEY_UWB:
|
||||
t = RFKILL_TYPE_UWB;
|
||||
break;
|
||||
case KEY_WIMAX:
|
||||
t = RFKILL_TYPE_WIMAX;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
rfkill_schedule_toggle(t);
|
||||
return;
|
||||
} else if (type == EV_SW) {
|
||||
switch (code) {
|
||||
case SW_RFKILL_ALL:
|
||||
rfkill_schedule_evsw_rfkillall(data);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int rfkill_connect(struct input_handler *handler, struct input_dev *dev,
|
||||
const struct input_device_id *id)
|
||||
{
|
||||
struct input_handle *handle;
|
||||
int error;
|
||||
|
||||
handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
|
||||
if (!handle)
|
||||
return -ENOMEM;
|
||||
|
||||
handle->dev = dev;
|
||||
handle->handler = handler;
|
||||
handle->name = "rfkill";
|
||||
|
||||
/* causes rfkill_start() to be called */
|
||||
error = input_register_handle(handle);
|
||||
if (error)
|
||||
goto err_free_handle;
|
||||
|
||||
error = input_open_device(handle);
|
||||
if (error)
|
||||
goto err_unregister_handle;
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_handle:
|
||||
input_unregister_handle(handle);
|
||||
err_free_handle:
|
||||
kfree(handle);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void rfkill_start(struct input_handle *handle)
|
||||
{
|
||||
/* Take event_lock to guard against configuration changes, we
|
||||
* should be able to deal with concurrency with rfkill_event()
|
||||
* just fine (which event_lock will also avoid). */
|
||||
spin_lock_irq(&handle->dev->event_lock);
|
||||
|
||||
if (test_bit(EV_SW, handle->dev->evbit)) {
|
||||
if (test_bit(SW_RFKILL_ALL, handle->dev->swbit))
|
||||
rfkill_schedule_evsw_rfkillall(test_bit(SW_RFKILL_ALL,
|
||||
handle->dev->sw));
|
||||
/* add resync for further EV_SW events here */
|
||||
}
|
||||
|
||||
spin_unlock_irq(&handle->dev->event_lock);
|
||||
}
|
||||
|
||||
static void rfkill_disconnect(struct input_handle *handle)
|
||||
{
|
||||
input_close_device(handle);
|
||||
input_unregister_handle(handle);
|
||||
kfree(handle);
|
||||
}
|
||||
|
||||
static const struct input_device_id rfkill_ids[] = {
|
||||
{
|
||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
|
||||
.evbit = { BIT_MASK(EV_KEY) },
|
||||
.keybit = { [BIT_WORD(KEY_WLAN)] = BIT_MASK(KEY_WLAN) },
|
||||
},
|
||||
{
|
||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
|
||||
.evbit = { BIT_MASK(EV_KEY) },
|
||||
.keybit = { [BIT_WORD(KEY_BLUETOOTH)] = BIT_MASK(KEY_BLUETOOTH) },
|
||||
},
|
||||
{
|
||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
|
||||
.evbit = { BIT_MASK(EV_KEY) },
|
||||
.keybit = { [BIT_WORD(KEY_UWB)] = BIT_MASK(KEY_UWB) },
|
||||
},
|
||||
{
|
||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
|
||||
.evbit = { BIT_MASK(EV_KEY) },
|
||||
.keybit = { [BIT_WORD(KEY_WIMAX)] = BIT_MASK(KEY_WIMAX) },
|
||||
},
|
||||
{
|
||||
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT,
|
||||
.evbit = { BIT(EV_SW) },
|
||||
.swbit = { [BIT_WORD(SW_RFKILL_ALL)] = BIT_MASK(SW_RFKILL_ALL) },
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct input_handler rfkill_handler = {
|
||||
.event = rfkill_event,
|
||||
.connect = rfkill_connect,
|
||||
.disconnect = rfkill_disconnect,
|
||||
.start = rfkill_start,
|
||||
.name = "rfkill",
|
||||
.id_table = rfkill_ids,
|
||||
};
|
||||
|
||||
static int __init rfkill_handler_init(void)
|
||||
{
|
||||
if (rfkill_master_switch_mode >= RFKILL_INPUT_MASTER_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* 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?
|
||||
*/
|
||||
rfkill_task.last_scheduled =
|
||||
jiffies - msecs_to_jiffies(RFKILL_OPS_DELAY) - 1;
|
||||
return input_register_handler(&rfkill_handler);
|
||||
}
|
||||
|
||||
static void __exit rfkill_handler_exit(void)
|
||||
{
|
||||
input_unregister_handler(&rfkill_handler);
|
||||
cancel_delayed_work_sync(&rfkill_task.dwork);
|
||||
rfkill_remove_epo_lock();
|
||||
}
|
||||
|
||||
module_init(rfkill_handler_init);
|
||||
module_exit(rfkill_handler_exit);
|
@ -1,855 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 - 2007 Ivo van Doorn
|
||||
* Copyright (C) 2007 Dmitry Torokhov
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/rfkill.h>
|
||||
|
||||
/* Get declaration of rfkill_switch_all() to shut up sparse. */
|
||||
#include "rfkill-input.h"
|
||||
|
||||
|
||||
MODULE_AUTHOR("Ivo van Doorn <IvDoorn@gmail.com>");
|
||||
MODULE_VERSION("1.0");
|
||||
MODULE_DESCRIPTION("RF switch support");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static LIST_HEAD(rfkill_list); /* list of registered rf switches */
|
||||
static DEFINE_MUTEX(rfkill_global_mutex);
|
||||
|
||||
static unsigned int rfkill_default_state = RFKILL_STATE_UNBLOCKED;
|
||||
module_param_named(default_state, rfkill_default_state, uint, 0444);
|
||||
MODULE_PARM_DESC(default_state,
|
||||
"Default initial state for all radio types, 0 = radio off");
|
||||
|
||||
struct rfkill_gsw_state {
|
||||
enum rfkill_state current_state;
|
||||
enum rfkill_state default_state;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
|
||||
#ifdef CONFIG_RFKILL_LEDS
|
||||
static void rfkill_led_trigger(struct rfkill *rfkill,
|
||||
enum rfkill_state state)
|
||||
{
|
||||
struct led_trigger *led = &rfkill->led_trigger;
|
||||
|
||||
if (!led->name)
|
||||
return;
|
||||
if (state != RFKILL_STATE_UNBLOCKED)
|
||||
led_trigger_event(led, LED_OFF);
|
||||
else
|
||||
led_trigger_event(led, LED_FULL);
|
||||
}
|
||||
|
||||
static void rfkill_led_trigger_activate(struct led_classdev *led)
|
||||
{
|
||||
struct rfkill *rfkill = container_of(led->trigger,
|
||||
struct rfkill, led_trigger);
|
||||
|
||||
rfkill_led_trigger(rfkill, rfkill->state);
|
||||
}
|
||||
#else
|
||||
static inline void rfkill_led_trigger(struct rfkill *rfkill,
|
||||
enum rfkill_state state)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_RFKILL_LEDS */
|
||||
|
||||
static void rfkill_uevent(struct rfkill *rfkill)
|
||||
{
|
||||
kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE);
|
||||
}
|
||||
|
||||
static void update_rfkill_state(struct rfkill *rfkill)
|
||||
{
|
||||
enum rfkill_state newstate, oldstate;
|
||||
|
||||
if (rfkill->get_state) {
|
||||
mutex_lock(&rfkill->mutex);
|
||||
if (!rfkill->get_state(rfkill->data, &newstate)) {
|
||||
oldstate = rfkill->state;
|
||||
rfkill->state = newstate;
|
||||
if (oldstate != newstate)
|
||||
rfkill_uevent(rfkill);
|
||||
}
|
||||
mutex_unlock(&rfkill->mutex);
|
||||
}
|
||||
rfkill_led_trigger(rfkill, rfkill->state);
|
||||
}
|
||||
|
||||
/**
|
||||
* rfkill_toggle_radio - wrapper for toggle_radio hook
|
||||
* @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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
static int rfkill_toggle_radio(struct rfkill *rfkill,
|
||||
enum rfkill_state state,
|
||||
int force)
|
||||
{
|
||||
int retval = 0;
|
||||
enum rfkill_state oldstate, newstate;
|
||||
|
||||
if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP))
|
||||
return -EBUSY;
|
||||
|
||||
oldstate = rfkill->state;
|
||||
|
||||
if (rfkill->get_state && !force &&
|
||||
!rfkill->get_state(rfkill->data, &newstate)) {
|
||||
rfkill->state = newstate;
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case RFKILL_STATE_HARD_BLOCKED:
|
||||
/* typically happens when refreshing hardware state,
|
||||
* such as on resume */
|
||||
state = RFKILL_STATE_SOFT_BLOCKED;
|
||||
break;
|
||||
case RFKILL_STATE_UNBLOCKED:
|
||||
/* force can't override this, only rfkill_force_state() can */
|
||||
if (rfkill->state == RFKILL_STATE_HARD_BLOCKED)
|
||||
return -EPERM;
|
||||
break;
|
||||
case RFKILL_STATE_SOFT_BLOCKED:
|
||||
/* nothing to do, we want to give drivers the hint to double
|
||||
* BLOCK even a transmitter that is already in state
|
||||
* RFKILL_STATE_HARD_BLOCKED */
|
||||
break;
|
||||
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) {
|
||||
retval = rfkill->toggle_radio(rfkill->data, state);
|
||||
/* never allow a HARD->SOFT downgrade! */
|
||||
if (!retval && rfkill->state != RFKILL_STATE_HARD_BLOCKED)
|
||||
rfkill->state = state;
|
||||
}
|
||||
|
||||
if (force || rfkill->state != oldstate)
|
||||
rfkill_uevent(rfkill);
|
||||
|
||||
rfkill_led_trigger(rfkill, rfkill->state);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* __rfkill_switch_all - Toggle state of all switches of given type
|
||||
* @type: type of interfaces to be affected
|
||||
* @state: the new state
|
||||
*
|
||||
* This function toggles the state of all switches of given type,
|
||||
* unless a specific switch is claimed by userspace (in which case,
|
||||
* that switch is left alone) or suspended.
|
||||
*
|
||||
* Caller must have acquired rfkill_global_mutex.
|
||||
*/
|
||||
static void __rfkill_switch_all(const enum rfkill_type type,
|
||||
const enum rfkill_state state)
|
||||
{
|
||||
struct rfkill *rfkill;
|
||||
|
||||
if (WARN((state >= RFKILL_STATE_MAX || type >= RFKILL_TYPE_MAX),
|
||||
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) {
|
||||
if (rfkill->type == type) {
|
||||
mutex_lock(&rfkill->mutex);
|
||||
rfkill_toggle_radio(rfkill, state, 0);
|
||||
mutex_unlock(&rfkill->mutex);
|
||||
rfkill_led_trigger(rfkill, rfkill->state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* rfkill_switch_all - Toggle state of all switches of given type
|
||||
* @type: type of interfaces to be affected
|
||||
* @state: the new state
|
||||
*
|
||||
* Acquires rfkill_global_mutex and calls __rfkill_switch_all(@type, @state).
|
||||
* Please refer to __rfkill_switch_all() for details.
|
||||
*
|
||||
* Does nothing if the EPO lock is active.
|
||||
*/
|
||||
void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state)
|
||||
{
|
||||
mutex_lock(&rfkill_global_mutex);
|
||||
if (!rfkill_epo_lock_active)
|
||||
__rfkill_switch_all(type, state);
|
||||
mutex_unlock(&rfkill_global_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(rfkill_switch_all);
|
||||
|
||||
/**
|
||||
* rfkill_epo - emergency power off all transmitters
|
||||
*
|
||||
* This kicks all non-suspended rfkill devices to RFKILL_STATE_SOFT_BLOCKED,
|
||||
* ignoring everything in its path but rfkill_global_mutex and rfkill->mutex.
|
||||
*
|
||||
* The global state before the EPO is saved and can be restored later
|
||||
* using rfkill_restore_states().
|
||||
*/
|
||||
void rfkill_epo(void)
|
||||
{
|
||||
struct rfkill *rfkill;
|
||||
int i;
|
||||
|
||||
mutex_lock(&rfkill_global_mutex);
|
||||
|
||||
rfkill_epo_lock_active = true;
|
||||
list_for_each_entry(rfkill, &rfkill_list, node) {
|
||||
mutex_lock(&rfkill->mutex);
|
||||
rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
|
||||
mutex_unlock(&rfkill->mutex);
|
||||
}
|
||||
for (i = 0; i < RFKILL_TYPE_MAX; i++) {
|
||||
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);
|
||||
rfkill_led_trigger(rfkill, rfkill->state);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rfkill_epo);
|
||||
|
||||
/**
|
||||
* rfkill_restore_states - restore global states
|
||||
*
|
||||
* Restore (and sync switches to) the global state from the
|
||||
* states in rfkill_default_states. This can undo the effects of
|
||||
* a call to rfkill_epo().
|
||||
*/
|
||||
void rfkill_restore_states(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
mutex_lock(&rfkill_global_mutex);
|
||||
|
||||
rfkill_epo_lock_active = false;
|
||||
for (i = 0; i < RFKILL_TYPE_MAX; i++)
|
||||
__rfkill_switch_all(i, rfkill_global_states[i].default_state);
|
||||
mutex_unlock(&rfkill_global_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rfkill_restore_states);
|
||||
|
||||
/**
|
||||
* rfkill_remove_epo_lock - unlock state changes
|
||||
*
|
||||
* Used by rfkill-input manually unlock state changes, when
|
||||
* the EPO switch is deactivated.
|
||||
*/
|
||||
void rfkill_remove_epo_lock(void)
|
||||
{
|
||||
mutex_lock(&rfkill_global_mutex);
|
||||
rfkill_epo_lock_active = false;
|
||||
mutex_unlock(&rfkill_global_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rfkill_remove_epo_lock);
|
||||
|
||||
/**
|
||||
* rfkill_is_epo_lock_active - returns true EPO is active
|
||||
*
|
||||
* Returns 0 (false) if there is NOT an active EPO contidion,
|
||||
* and 1 (true) if there is an active EPO contition, which
|
||||
* locks all radios in one of the BLOCKED states.
|
||||
*
|
||||
* Can be called in atomic context.
|
||||
*/
|
||||
bool rfkill_is_epo_lock_active(void)
|
||||
{
|
||||
return rfkill_epo_lock_active;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rfkill_is_epo_lock_active);
|
||||
|
||||
/**
|
||||
* rfkill_get_global_state - returns global state for a type
|
||||
* @type: the type to get the global state of
|
||||
*
|
||||
* Returns the current global state for a given wireless
|
||||
* device type.
|
||||
*/
|
||||
enum rfkill_state rfkill_get_global_state(const enum rfkill_type type)
|
||||
{
|
||||
return rfkill_global_states[type].current_state;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rfkill_get_global_state);
|
||||
|
||||
/**
|
||||
* rfkill_force_state - Force the internal rfkill radio state
|
||||
* @rfkill: pointer to the rfkill class to modify.
|
||||
* @state: the current radio state the class should be forced to.
|
||||
*
|
||||
* This function updates the internal state of the radio cached
|
||||
* by the rfkill class. It should be used when the driver gets
|
||||
* a notification by the firmware/hardware of the current *real*
|
||||
* state of the radio rfkill switch.
|
||||
*
|
||||
* Devices which are subject to external changes on their rfkill
|
||||
* state (such as those caused by a hardware rfkill line) MUST
|
||||
* have their driver arrange to call rfkill_force_state() as soon
|
||||
* as possible after such a change.
|
||||
*
|
||||
* This function may not be called from an atomic context.
|
||||
*/
|
||||
int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state)
|
||||
{
|
||||
enum rfkill_state oldstate;
|
||||
|
||||
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);
|
||||
|
||||
oldstate = rfkill->state;
|
||||
rfkill->state = state;
|
||||
|
||||
if (state != oldstate)
|
||||
rfkill_uevent(rfkill);
|
||||
|
||||
mutex_unlock(&rfkill->mutex);
|
||||
rfkill_led_trigger(rfkill, rfkill->state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rfkill_force_state);
|
||||
|
||||
static ssize_t rfkill_name_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rfkill *rfkill = to_rfkill(dev);
|
||||
|
||||
return sprintf(buf, "%s\n", rfkill->name);
|
||||
}
|
||||
|
||||
static const char *rfkill_get_type_str(enum rfkill_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case RFKILL_TYPE_WLAN:
|
||||
return "wlan";
|
||||
case RFKILL_TYPE_BLUETOOTH:
|
||||
return "bluetooth";
|
||||
case RFKILL_TYPE_UWB:
|
||||
return "ultrawideband";
|
||||
case RFKILL_TYPE_WIMAX:
|
||||
return "wimax";
|
||||
case RFKILL_TYPE_WWAN:
|
||||
return "wwan";
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t rfkill_type_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rfkill *rfkill = to_rfkill(dev);
|
||||
|
||||
return sprintf(buf, "%s\n", rfkill_get_type_str(rfkill->type));
|
||||
}
|
||||
|
||||
static ssize_t rfkill_state_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct rfkill *rfkill = to_rfkill(dev);
|
||||
|
||||
update_rfkill_state(rfkill);
|
||||
return sprintf(buf, "%d\n", rfkill->state);
|
||||
}
|
||||
|
||||
static ssize_t rfkill_state_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct rfkill *rfkill = to_rfkill(dev);
|
||||
unsigned long state;
|
||||
int error;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
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,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", 0);
|
||||
}
|
||||
|
||||
static ssize_t rfkill_claim_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static struct device_attribute rfkill_dev_attrs[] = {
|
||||
__ATTR(name, S_IRUGO, rfkill_name_show, NULL),
|
||||
__ATTR(type, S_IRUGO, rfkill_type_show, NULL),
|
||||
__ATTR(state, S_IRUGO|S_IWUSR, rfkill_state_show, rfkill_state_store),
|
||||
__ATTR(claim, S_IRUGO|S_IWUSR, rfkill_claim_show, rfkill_claim_store),
|
||||
__ATTR_NULL
|
||||
};
|
||||
|
||||
static void rfkill_release(struct device *dev)
|
||||
{
|
||||
struct rfkill *rfkill = to_rfkill(dev);
|
||||
|
||||
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)
|
||||
{
|
||||
struct rfkill *rfkill = to_rfkill(dev);
|
||||
int error;
|
||||
|
||||
error = add_uevent_var(env, "RFKILL_NAME=%s", rfkill->name);
|
||||
if (error)
|
||||
return error;
|
||||
error = add_uevent_var(env, "RFKILL_TYPE=%s",
|
||||
rfkill_get_type_str(rfkill->type));
|
||||
if (error)
|
||||
return error;
|
||||
error = add_uevent_var(env, "RFKILL_STATE=%d", rfkill->state);
|
||||
return error;
|
||||
}
|
||||
|
||||
static struct class rfkill_class = {
|
||||
.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;
|
||||
unsigned long seen[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
|
||||
|
||||
memset(seen, 0, sizeof(seen));
|
||||
|
||||
list_for_each_entry(p, &rfkill_list, node) {
|
||||
if (WARN((p == rfkill), KERN_WARNING
|
||||
"rfkill: illegal attempt to register "
|
||||
"an already registered rfkill struct\n"))
|
||||
return -EEXIST;
|
||||
set_bit(p->type, seen);
|
||||
}
|
||||
|
||||
/* 0: first switch of its kind */
|
||||
return (test_bit(rfkill->type, seen)) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int rfkill_add_switch(struct rfkill *rfkill)
|
||||
{
|
||||
int error;
|
||||
|
||||
mutex_lock(&rfkill_global_mutex);
|
||||
|
||||
error = rfkill_check_duplicity(rfkill);
|
||||
if (error < 0)
|
||||
goto unlock_out;
|
||||
|
||||
if (!error) {
|
||||
/* lock default after first use */
|
||||
set_bit(rfkill->type, rfkill_states_lockdflt);
|
||||
rfkill_global_states[rfkill->type].current_state =
|
||||
rfkill_global_states[rfkill->type].default_state;
|
||||
}
|
||||
|
||||
rfkill_toggle_radio(rfkill,
|
||||
rfkill_global_states[rfkill->type].current_state,
|
||||
0);
|
||||
|
||||
list_add_tail(&rfkill->node, &rfkill_list);
|
||||
|
||||
error = 0;
|
||||
unlock_out:
|
||||
mutex_unlock(&rfkill_global_mutex);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void rfkill_remove_switch(struct rfkill *rfkill)
|
||||
{
|
||||
mutex_lock(&rfkill_global_mutex);
|
||||
list_del_init(&rfkill->node);
|
||||
mutex_unlock(&rfkill_global_mutex);
|
||||
|
||||
mutex_lock(&rfkill->mutex);
|
||||
rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
|
||||
mutex_unlock(&rfkill->mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* rfkill_allocate - allocate memory for rfkill structure.
|
||||
* @parent: device that has rf switch on it
|
||||
* @type: type of the switch (RFKILL_TYPE_*)
|
||||
*
|
||||
* This function should be called by the network driver when it needs
|
||||
* rfkill structure. Once the structure is allocated the driver should
|
||||
* 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
|
||||
* rfkill_free() otherwise rfkill_unregister() should be used.
|
||||
*/
|
||||
struct rfkill * __must_check rfkill_allocate(struct device *parent,
|
||||
enum rfkill_type type)
|
||||
{
|
||||
struct rfkill *rfkill;
|
||||
struct device *dev;
|
||||
|
||||
if (WARN((type >= RFKILL_TYPE_MAX),
|
||||
KERN_WARNING
|
||||
"rfkill: illegal type %d passed as parameter "
|
||||
"to rfkill_allocate\n", type))
|
||||
return NULL;
|
||||
|
||||
rfkill = kzalloc(sizeof(struct rfkill), GFP_KERNEL);
|
||||
if (!rfkill)
|
||||
return NULL;
|
||||
|
||||
mutex_init(&rfkill->mutex);
|
||||
INIT_LIST_HEAD(&rfkill->node);
|
||||
rfkill->type = type;
|
||||
|
||||
dev = &rfkill->dev;
|
||||
dev->class = &rfkill_class;
|
||||
dev->parent = parent;
|
||||
device_initialize(dev);
|
||||
|
||||
__module_get(THIS_MODULE);
|
||||
|
||||
return rfkill;
|
||||
}
|
||||
EXPORT_SYMBOL(rfkill_allocate);
|
||||
|
||||
/**
|
||||
* 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)
|
||||
put_device(&rfkill->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(rfkill_free);
|
||||
|
||||
static void rfkill_led_trigger_register(struct rfkill *rfkill)
|
||||
{
|
||||
#ifdef CONFIG_RFKILL_LEDS
|
||||
int error;
|
||||
|
||||
if (!rfkill->led_trigger.name)
|
||||
rfkill->led_trigger.name = dev_name(&rfkill->dev);
|
||||
if (!rfkill->led_trigger.activate)
|
||||
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)
|
||||
{
|
||||
#ifdef CONFIG_RFKILL_LEDS
|
||||
if (rfkill->led_trigger.name) {
|
||||
led_trigger_unregister(&rfkill->led_trigger);
|
||||
rfkill->led_trigger.name = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
static atomic_t rfkill_no = ATOMIC_INIT(0);
|
||||
struct device *dev = &rfkill->dev;
|
||||
int error;
|
||||
|
||||
if (WARN((!rfkill || !rfkill->toggle_radio ||
|
||||
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);
|
||||
|
||||
rfkill_led_trigger_register(rfkill);
|
||||
|
||||
error = rfkill_add_switch(rfkill);
|
||||
if (error) {
|
||||
rfkill_led_trigger_unregister(rfkill);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = device_add(dev);
|
||||
if (error) {
|
||||
rfkill_remove_switch(rfkill);
|
||||
rfkill_led_trigger_unregister(rfkill);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
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)
|
||||
{
|
||||
BUG_ON(!rfkill);
|
||||
device_del(&rfkill->dev);
|
||||
rfkill_remove_switch(rfkill);
|
||||
rfkill_led_trigger_unregister(rfkill);
|
||||
put_device(&rfkill->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(rfkill_unregister);
|
||||
|
||||
/**
|
||||
* rfkill_set_default - set initial value for a switch type
|
||||
* @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 ||
|
||||
(state != RFKILL_STATE_SOFT_BLOCKED &&
|
||||
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);
|
||||
|
||||
if (!test_and_set_bit(type, rfkill_states_lockdflt)) {
|
||||
rfkill_global_states[type].default_state = state;
|
||||
rfkill_global_states[type].current_state = state;
|
||||
error = 0;
|
||||
} else
|
||||
error = -EPERM;
|
||||
|
||||
mutex_unlock(&rfkill_global_mutex);
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rfkill_set_default);
|
||||
|
||||
/*
|
||||
* Rfkill module initialization/deinitialization.
|
||||
*/
|
||||
static int __init rfkill_init(void)
|
||||
{
|
||||
int error;
|
||||
int i;
|
||||
|
||||
/* RFKILL_STATE_HARD_BLOCKED is illegal here... */
|
||||
if (rfkill_default_state != RFKILL_STATE_SOFT_BLOCKED &&
|
||||
rfkill_default_state != RFKILL_STATE_UNBLOCKED)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < RFKILL_TYPE_MAX; i++)
|
||||
rfkill_global_states[i].default_state = rfkill_default_state;
|
||||
|
||||
error = class_register(&rfkill_class);
|
||||
if (error) {
|
||||
printk(KERN_ERR "rfkill: unable to register rfkill class\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit rfkill_exit(void)
|
||||
{
|
||||
class_unregister(&rfkill_class);
|
||||
}
|
||||
|
||||
subsys_initcall(rfkill_init);
|
||||
module_exit(rfkill_exit);
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2007 Ivo van Doorn
|
||||
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -11,11 +12,16 @@
|
||||
#ifndef __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_restore_states(void);
|
||||
void rfkill_remove_epo_lock(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 */
|
@ -1,23 +1,9 @@
|
||||
#
|
||||
# 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
|
||||
tristate "WiMAX Wireless Broadband support"
|
||||
depends on (y && RFKILL != m) || m
|
||||
depends on (INPUT && RFKILL != n) || RFKILL = n
|
||||
help
|
||||
|
||||
Select to configure support for devices that provide
|
||||
|
@ -29,8 +29,8 @@
|
||||
* A non-polled generic rfkill device is embedded into the WiMAX
|
||||
* subsystem's representation of a device.
|
||||
*
|
||||
* FIXME: Need polled support? use a timer or add the implementation
|
||||
* to the stack.
|
||||
* FIXME: Need polled support? Let drivers provide a poll routine
|
||||
* and hand it to rfkill ops then?
|
||||
*
|
||||
* All device drivers have to do is after wimax_dev_init(), call
|
||||
* wimax_report_rfkill_hw() and wimax_report_rfkill_sw() to update
|
||||
@ -43,7 +43,7 @@
|
||||
* wimax_rfkill() Kernel calling wimax_rfkill()
|
||||
* __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()
|
||||
@ -65,15 +65,11 @@
|
||||
#include <linux/wimax.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/input.h>
|
||||
#include "wimax-internal.h"
|
||||
|
||||
#define D_SUBMODULE op_rfkill
|
||||
#include "debug-levels.h"
|
||||
|
||||
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
|
||||
|
||||
|
||||
/**
|
||||
* 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,
|
||||
int result;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
enum wimax_st wimax_state;
|
||||
enum rfkill_state rfkill_state;
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state);
|
||||
BUG_ON(state == WIMAX_RF_QUERY);
|
||||
@ -112,16 +107,15 @@ void wimax_report_rfkill_hw(struct wimax_dev *wimax_dev,
|
||||
|
||||
if (state != wimax_dev->rf_hw) {
|
||||
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
|
||||
&& wimax_dev->rf_sw == WIMAX_RF_ON)
|
||||
wimax_state = WIMAX_ST_READY;
|
||||
else
|
||||
wimax_state = WIMAX_ST_RADIO_OFF;
|
||||
|
||||
rfkill_set_hw_state(wimax_dev->rfkill, state == WIMAX_RF_OFF);
|
||||
|
||||
__wimax_state_change(wimax_dev, wimax_state);
|
||||
input_report_key(wimax_dev->rfkill_input, KEY_WIMAX,
|
||||
rfkill_state);
|
||||
}
|
||||
error_not_ready:
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
@ -174,6 +168,7 @@ void wimax_report_rfkill_sw(struct wimax_dev *wimax_dev,
|
||||
else
|
||||
wimax_state = WIMAX_ST_RADIO_OFF;
|
||||
__wimax_state_change(wimax_dev, wimax_state);
|
||||
rfkill_set_sw_state(wimax_dev->rfkill, state == WIMAX_RF_OFF);
|
||||
}
|
||||
error_not_ready:
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
@ -249,36 +244,31 @@ out_no_change:
|
||||
*
|
||||
* NOTE: This call will block until the operation is completed.
|
||||
*/
|
||||
static
|
||||
int wimax_rfkill_toggle_radio(void *data, enum rfkill_state state)
|
||||
static int wimax_rfkill_set_radio_block(void *data, bool blocked)
|
||||
{
|
||||
int result;
|
||||
struct wimax_dev *wimax_dev = data;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
enum wimax_rf_state rf_state;
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state);
|
||||
switch (state) {
|
||||
case RFKILL_STATE_SOFT_BLOCKED:
|
||||
d_fnstart(3, dev, "(wimax_dev %p blocked %u)\n", wimax_dev, blocked);
|
||||
rf_state = WIMAX_RF_ON;
|
||||
if (blocked)
|
||||
rf_state = WIMAX_RF_OFF;
|
||||
break;
|
||||
case RFKILL_STATE_UNBLOCKED:
|
||||
rf_state = WIMAX_RF_ON;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
if (wimax_dev->state <= __WIMAX_ST_QUIESCING)
|
||||
result = 0; /* just pretend it didn't happen */
|
||||
result = 0;
|
||||
else
|
||||
result = __wimax_rf_toggle_radio(wimax_dev, rf_state);
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
d_fnend(3, dev, "(wimax_dev %p state %u) = %d\n",
|
||||
wimax_dev, state, result);
|
||||
d_fnend(3, dev, "(wimax_dev %p blocked %u) = %d\n",
|
||||
wimax_dev, blocked, 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
|
||||
@ -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);
|
||||
if (result < 0)
|
||||
goto error;
|
||||
rfkill_set_sw_state(wimax_dev->rfkill, state == WIMAX_RF_OFF);
|
||||
break;
|
||||
case WIMAX_RF_QUERY:
|
||||
break;
|
||||
@ -349,40 +340,20 @@ int wimax_rfkill_add(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
int result;
|
||||
struct rfkill *rfkill;
|
||||
struct input_dev *input_dev;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p)\n", wimax_dev);
|
||||
/* Initialize RF Kill */
|
||||
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)
|
||||
goto error_rfkill_allocate;
|
||||
|
||||
d_printf(1, dev, "rfkill %p\n", 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);
|
||||
if (result < 0)
|
||||
goto error_rfkill_register;
|
||||
@ -394,17 +365,8 @@ int wimax_rfkill_add(struct wimax_dev *wimax_dev)
|
||||
d_fnend(3, dev, "(wimax_dev %p) = 0\n", wimax_dev);
|
||||
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:
|
||||
input_unregister_device(wimax_dev->rfkill_input);
|
||||
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);
|
||||
rfkill_destroy(wimax_dev->rfkill);
|
||||
error_rfkill_allocate:
|
||||
d_fnend(3, dev, "(wimax_dev %p) = %d\n", wimax_dev, result);
|
||||
return result;
|
||||
@ -423,45 +385,12 @@ void wimax_rfkill_rm(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
d_fnstart(3, dev, "(wimax_dev %p)\n", wimax_dev);
|
||||
rfkill_unregister(wimax_dev->rfkill); /* frees */
|
||||
input_unregister_device(wimax_dev->rfkill_input);
|
||||
rfkill_unregister(wimax_dev->rfkill);
|
||||
rfkill_destroy(wimax_dev->rfkill);
|
||||
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
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user