diff options
46 files changed, 2527 insertions, 3264 deletions
diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt index 40c3a3f1081..de941e309d4 100644 --- a/Documentation/rfkill.txt +++ b/Documentation/rfkill.txt @@ -1,571 +1,130 @@ -rfkill - RF switch subsystem support -==================================== +rfkill - RF kill switch support +=============================== -1 Introduction -2 Implementation details -3 Kernel driver guidelines -3.1 wireless device drivers -3.2 platform/switch drivers -3.3 input device drivers -4 Kernel API -5 Userspace support +1. Introduction +2. Implementation details +3. Kernel driver guidelines +4. Kernel API +5. Userspace support -1. Introduction: +1. Introduction -The rfkill switch subsystem exists to add a generic interface to circuitry that -can enable or disable the signal output of a wireless *transmitter* of any -type.  By far, the most common use is to disable radio-frequency transmitters. +The rfkill subsystem provides a generic interface to disabling any radio +transmitter in the system. When a transmitter is blocked, it shall not +radiate any power. -Note that disabling the signal output means that the the transmitter is to be -made to not emit any energy when "blocked".  rfkill is not about blocking data -transmissions, it is about blocking energy emission. +The subsystem also provides the ability to react on button presses and +disable all transmitters of a certain type (or all). This is intended for +situations where transmitters need to be turned off, for example on +aircraft. -The rfkill subsystem offers support for keys and switches often found on -laptops to enable wireless devices like WiFi and Bluetooth, so that these keys -and switches actually perform an action in all wireless devices of a given type -attached to the system. -The buttons to enable and disable the wireless transmitters are important in -situations where the user is for example using his laptop on a location where -radio-frequency transmitters _must_ be disabled (e.g. airplanes). -Because of this requirement, userspace support for the keys should not be made -mandatory.  Because userspace might want to perform some additional smarter -tasks when the key is pressed, rfkill provides userspace the possibility to -take over the task to handle the key events. - -=============================================================================== -2: Implementation details +2. Implementation details  The rfkill subsystem is composed of various components: the rfkill class, the  rfkill-input module (an input layer handler), and some specific input layer  events. -The rfkill class provides kernel drivers with an interface that allows them to -know when they should enable or disable a wireless network device transmitter. -This is enabled by the CONFIG_RFKILL Kconfig option. - -The rfkill class support makes sure userspace will be notified of all state -changes on rfkill devices through uevents.  It provides a notification chain -for interested parties in the kernel to also get notified of rfkill state -changes in other drivers.  It creates several sysfs entries which can be used -by userspace.  See section "Userspace support". - -The rfkill-input module provides the kernel with the ability to implement a -basic response when the user presses a key or button (or toggles a switch) -related to rfkill functionality.  It is an in-kernel implementation of default -policy of reacting to rfkill-related input events and neither mandatory nor -required for wireless drivers to operate.  It is enabled by the -CONFIG_RFKILL_INPUT Kconfig option. - -rfkill-input is a rfkill-related events input layer handler.  This handler will -listen to all rfkill key events and will change the rfkill state of the -wireless devices accordingly.  With this option enabled userspace could either -do nothing or simply perform monitoring tasks. - -The rfkill-input module also provides EPO (emergency power-off) functionality -for all wireless transmitters.  This function cannot be overridden, and it is -always active.  rfkill EPO is related to *_RFKILL_ALL input layer events. - - -Important terms for the rfkill subsystem: - -In order to avoid confusion, we avoid the term "switch" in rfkill when it is -referring to an electronic control circuit that enables or disables a -transmitter.  We reserve it for the physical device a human manipulates -(which is an input device, by the way): - -rfkill switch: - -	A physical device a human manipulates.  Its state can be perceived by -	the kernel either directly (through a GPIO pin, ACPI GPE) or by its -	effect on a rfkill line of a wireless device. - -rfkill controller: - -	A hardware circuit that controls the state of a rfkill line, which a -	kernel driver can interact with *to modify* that state (i.e. it has -	either write-only or read/write access). - -rfkill line: - -	An input channel (hardware or software) of a wireless device, which -	causes a wireless transmitter to stop emitting energy (BLOCK) when it -	is active.  Point of view is extremely important here: rfkill lines are -	always seen from the PoV of a wireless device (and its driver). - -soft rfkill line/software rfkill line: - -	A rfkill line the wireless device driver can directly change the state -	of.  Related to rfkill_state RFKILL_STATE_SOFT_BLOCKED. - -hard rfkill line/hardware rfkill line: - -	A rfkill line that works fully in hardware or firmware, and that cannot -	be overridden by the kernel driver.  The hardware device or the -	firmware just exports its status to the driver, but it is read-only. -	Related to rfkill_state RFKILL_STATE_HARD_BLOCKED. - -The enum rfkill_state describes the rfkill state of a transmitter: - -When a rfkill line or rfkill controller is in the RFKILL_STATE_UNBLOCKED state, -the wireless transmitter (radio TX circuit for example) is *enabled*.  When the -it is in the RFKILL_STATE_SOFT_BLOCKED or RFKILL_STATE_HARD_BLOCKED, the -wireless transmitter is to be *blocked* from operating. - -RFKILL_STATE_SOFT_BLOCKED indicates that a call to toggle_radio() can change -that state.  RFKILL_STATE_HARD_BLOCKED indicates that a call to toggle_radio() -will not be able to change the state and will return with a suitable error if -attempts are made to set the state to RFKILL_STATE_UNBLOCKED. - -RFKILL_STATE_HARD_BLOCKED is used by drivers to signal that the device is -locked in the BLOCKED state by a hardwire rfkill line (typically an input pin -that, when active, forces the transmitter to be disabled) which the driver -CANNOT override. - -Full rfkill functionality requires two different subsystems to cooperate: the -input layer and the rfkill class.  The input layer issues *commands* to the -entire system requesting that devices registered to the rfkill class change -state.  The way this interaction happens is not complex, but it is not obvious -either: - -Kernel Input layer: - -	* Generates KEY_WWAN, KEY_WLAN, KEY_BLUETOOTH, SW_RFKILL_ALL, and -	  other such events when the user presses certain keys, buttons, or -	  toggles certain physical switches. - -	THE INPUT LAYER IS NEVER USED TO PROPAGATE STATUS, NOTIFICATIONS OR THE -	KIND OF STUFF AN ON-SCREEN-DISPLAY APPLICATION WOULD REPORT.  It is -	used to issue *commands* for the system to change behaviour, and these -	commands may or may not be carried out by some kernel driver or -	userspace application.  It follows that doing user feedback based only -	on input events is broken, as there is no guarantee that an input event -	will be acted upon. - -	Most wireless communication device drivers implementing rfkill -	functionality MUST NOT generate these events, and have no reason to -	register themselves with the input layer.  Doing otherwise is a common -	misconception.  There is an API to propagate rfkill status change -	information, and it is NOT the input layer. - -rfkill class: - -	* Calls a hook in a driver to effectively change the wireless -	  transmitter state; -	* Keeps track of the wireless transmitter state (with help from -	  the driver); -	* Generates userspace notifications (uevents) and a call to a -	  notification chain (kernel) when there is a wireless transmitter -	  state change; -	* Connects a wireless communications driver with the common rfkill -	  control system, which, for example, allows actions such as -	  "switch all bluetooth devices offline" to be carried out by -	  userspace or by rfkill-input. - -	THE RFKILL CLASS NEVER ISSUES INPUT EVENTS.  THE RFKILL CLASS DOES -	NOT LISTEN TO INPUT EVENTS.  NO DRIVER USING THE RFKILL CLASS SHALL -	EVER LISTEN TO, OR ACT ON RFKILL INPUT EVENTS.  Doing otherwise is -	a layering violation. - -	Most wireless data communication drivers in the kernel have just to -	implement the rfkill class API to work properly.  Interfacing to the -	input layer is not often required (and is very often a *bug*) on -	wireless drivers. - -	Platform drivers often have to attach to the input layer to *issue* -	(but never to listen to) rfkill events for rfkill switches, and also to -	the rfkill class to export a control interface for the platform rfkill -	controllers to the rfkill subsystem.  This does NOT mean the rfkill -	switch is attached to a rfkill class (doing so is almost always wrong). -	It just means the same kernel module is the driver for different -	devices (rfkill switches and rfkill controllers). - - -Userspace input handlers (uevents) or kernel input handlers (rfkill-input): - -	* Implements the policy of what should happen when one of the input -	  layer events related to rfkill operation is received. -	* Uses the sysfs interface (userspace) or private rfkill API calls -	  to tell the devices registered with the rfkill class to change -	  their state (i.e. translates the input layer event into real -	  action). - -	* rfkill-input implements EPO by handling EV_SW SW_RFKILL_ALL 0 -	  (power off all transmitters) in a special way: it ignores any -	  overrides and local state cache and forces all transmitters to the -	  RFKILL_STATE_SOFT_BLOCKED state (including those which are already -	  supposed to be BLOCKED). -	* rfkill EPO will remain active until rfkill-input receives an -	  EV_SW SW_RFKILL_ALL 1 event.  While the EPO is active, transmitters -	  are locked in the blocked state (rfkill will refuse to unblock them). -	* rfkill-input implements different policies that the user can -	  select for handling EV_SW SW_RFKILL_ALL 1.  It will unlock rfkill, -	  and either do nothing (leave transmitters blocked, but now unlocked), -	  restore the transmitters to their state before the EPO, or unblock -	  them all. - -Userspace uevent handler or kernel platform-specific drivers hooked to the -rfkill notifier chain: - -	* Taps into the rfkill notifier chain or to KOBJ_CHANGE uevents, -	  in order to know when a device that is registered with the rfkill -	  class changes state; -	* Issues feedback notifications to the user; -	* In the rare platforms where this is required, synthesizes an input -	  event to command all *OTHER* rfkill devices to also change their -	  statues when a specific rfkill device changes state. - - -=============================================================================== -3: Kernel driver guidelines - -Remember: point-of-view is everything for a driver that connects to the rfkill -subsystem.  All the details below must be measured/perceived from the point of -view of the specific driver being modified. - -The first thing one needs to know is whether his driver should be talking to -the rfkill class or to the input layer.  In rare cases (platform drivers), it -could happen that you need to do both, as platform drivers often handle a -variety of devices in the same driver. - -Do not mistake input devices for rfkill controllers.  The only type of "rfkill -switch" device that is to be registered with the rfkill class are those -directly controlling the circuits that cause a wireless transmitter to stop -working (or the software equivalent of them), i.e. what we call a rfkill -controller.  Every other kind of "rfkill switch" is just an input device and -MUST NOT be registered with the rfkill class. - -A driver should register a device with the rfkill class when ALL of the -following conditions are met (they define a rfkill controller): - -1. The device is/controls a data communications wireless transmitter; - -2. The kernel can interact with the hardware/firmware to CHANGE the wireless -   transmitter state (block/unblock TX operation); - -3. The transmitter can be made to not emit any energy when "blocked": -   rfkill is not about blocking data transmissions, it is about blocking -   energy emission; - -A driver should register a device with the input subsystem to issue -rfkill-related events (KEY_WLAN, KEY_BLUETOOTH, KEY_WWAN, KEY_WIMAX, -SW_RFKILL_ALL, etc) when ALL of the folowing conditions are met: - -1. It is directly related to some physical device the user interacts with, to -   command the O.S./firmware/hardware to enable/disable a data communications -   wireless transmitter. - -   Examples of the physical device are: buttons, keys and switches the user -   will press/touch/slide/switch to enable or disable the wireless -   communication device. - -2. It is NOT slaved to another device, i.e. there is no other device that -   issues rfkill-related input events in preference to this one. - -   Please refer to the corner cases and examples section for more details. +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". -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. +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. -Corner cases and examples: -==================================== +The rfkill-input code implements a basic response to rfkill buttons -- it +implements turning on/off all devices of a certain class (or all). -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. +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(). -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. +The entire functionality is spread over more than one subsystem: -E.g: -  [RFKILL slider switch] -- [GPIO hardware] -- [WLAN card rf-kill input] -                           (platform driver)    (wireless card driver) + * The kernel input layer generates KEY_WWAN, KEY_WLAN etc. and +   SW_RFKILL_ALL -- when the user presses a button. Drivers for radio +   transmitters generally do not register to the input layer, unless the +   device really provides an input device (i.e. a button that has no +   effect other than generating a button press event) -The 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)... + * The rfkill-input code hooks up to these events and switches the soft-block +   of the various radio transmitters, depending on the button type. -On the other hand, some embedded devices do this: + * The rfkill drivers turn off/on their transmitters as requested. -  [RFKILL slider switch] -- [WLAN card rf-kill input] -                             (wireless card driver) + * The rfkill class will generate userspace notifications (uevents) to tell +   userspace what the current state is. -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: -==================================== +3. Kernel driver guidelines -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). +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. -  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. +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). -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. +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. -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. - -Therefore, calling rfkill_force_state() when a state change happens is -mandatory when the device has a hardware rfkill line, or when something else -like the firmware could cause its state to be changed without going through the -rfkill class. - -Some hardware provides events when its status changes.  In these cases, it is -best for the driver to not provide a get_state hook, and instead register the -rfkill class *already* with the correct status, and keep it updated using -rfkill_force_state() when it gets an event from the hardware. - -rfkill_force_state() must be used on the device resume handlers to update the -rfkill status, should there be any chance of the device status changing during -the sleep. - -There is no provision for a statically-allocated rfkill struct.  You must -use rfkill_allocate() to allocate one. - -You should: -	- rfkill_allocate() -	- modify rfkill fields (flags, name) -	- modify state to the current hardware state (THIS IS THE ONLY TIME -	  YOU CAN ACCESS state DIRECTLY) -	- rfkill_register() - -The only way to set a device to the RFKILL_STATE_HARD_BLOCKED state is through -a suitable return of get_state() or through rfkill_force_state(). - -When a device is in the RFKILL_STATE_HARD_BLOCKED state, the only way to switch -it to a different state is through a suitable return of get_state() or through -rfkill_force_state(). - -If toggle_radio() is called to set a device to state RFKILL_STATE_SOFT_BLOCKED -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 +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. -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. +5. Userspace support -When rfkill-input is not active, userspace must initiate a rfkill status -change by writing to the "state" attribute in order for anything to happen. - -Take particular care to implement EV_SW SW_RFKILL_ALL properly.  When that -switch is set to OFF, *every* rfkill device *MUST* be immediately put into the -RFKILL_STATE_SOFT_BLOCKED state, no questions asked. - -The following sysfs entries will be created: +The following sysfs entries exist for every rfkill device:  	name: Name assigned by driver to this key (interface or driver name).  	type: Name of the key type ("wlan", "bluetooth", etc).  	state: Current state of the transmitter  		0: RFKILL_STATE_SOFT_BLOCKED -			transmitter is forced off, but one can override it -			by a write to the state attribute; +			transmitter is turned off by software  		1: RFKILL_STATE_UNBLOCKED -			transmiter is NOT forced off, and may operate if -			all other conditions for such operation are met -			(such as interface is up and configured, etc); +			transmiter is (potentially) active  		2: RFKILL_STATE_HARD_BLOCKED  			transmitter is forced off by something outside of -			the driver's control.  One cannot set a device to -			this state through writes to the state attribute; -	claim: 1: Userspace handles events, 0: Kernel handles events - -Both the "state" and "claim" entries are also writable. For the "state" entry -this means that when 1 or 0 is written, the device rfkill state (if not yet in -the requested state), will be will be toggled accordingly. +			the driver's control. +	claim: 0: Kernel handles events (currently always reads that value) -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 devices also issue uevents (with an action of "change"), with the +following environment variables set: -A note about input devices and EV_SW events: +RFKILL_NAME +RFKILL_STATE +RFKILL_TYPE -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. diff --git a/MAINTAINERS b/MAINTAINERS index e18baa410b5..2f6a8fcfb1f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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/ diff --git a/arch/arm/mach-pxa/tosa-bt.c b/arch/arm/mach-pxa/tosa-bt.c index bde42aa2937..c31e601eb49 100644 --- a/arch/arm/mach-pxa/tosa-bt.c +++ b/arch/arm/mach-pxa/tosa-bt.c @@ -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); diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c index afac5b6d3d7..58ce807fe44 100644 --- a/arch/arm/mach-pxa/tosa.c +++ b/arch/arm/mach-pxa/tosa.c @@ -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> diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 837135f0390..5ddd8c4f901 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -2481,10 +2481,10 @@ static int add_net_device(struct hso_device *hso_dev)  	return 0;  } -static int hso_radio_toggle(void *data, enum rfkill_state state) +static int hso_rfkill_set_block(void *data, bool blocked)  {  	struct hso_device *hso_dev = data; -	int enabled = (state == RFKILL_STATE_UNBLOCKED); +	int enabled = !blocked;  	int rv;  	mutex_lock(&hso_dev->mutex); @@ -2498,6 +2498,10 @@ static int hso_radio_toggle(void *data, enum rfkill_state state)  	return rv;  } +static const struct rfkill_ops hso_rfkill_ops = { +	.set_block = hso_rfkill_set_block, +}; +  /* Creates and sets up everything for rfkill */  static void hso_create_rfkill(struct hso_device *hso_dev,  			     struct usb_interface *interface) @@ -2506,29 +2510,25 @@ static void hso_create_rfkill(struct hso_device *hso_dev,  	struct device *dev = &hso_net->net->dev;  	char *rfkn; -	hso_net->rfkill = rfkill_allocate(&interface_to_usbdev(interface)->dev, -				 RFKILL_TYPE_WWAN); -	if (!hso_net->rfkill) { -		dev_err(dev, "%s - Out of memory\n", __func__); -		return; -	}  	rfkn = kzalloc(20, GFP_KERNEL); -	if (!rfkn) { -		rfkill_free(hso_net->rfkill); -		hso_net->rfkill = NULL; +	if (!rfkn)  		dev_err(dev, "%s - Out of memory\n", __func__); -		return; -	} +  	snprintf(rfkn, 20, "hso-%d",  		 interface->altsetting->desc.bInterfaceNumber); -	hso_net->rfkill->name = rfkn; -	hso_net->rfkill->state = RFKILL_STATE_UNBLOCKED; -	hso_net->rfkill->data = hso_dev; -	hso_net->rfkill->toggle_radio = hso_radio_toggle; + +	hso_net->rfkill = rfkill_alloc(rfkn, +				       &interface_to_usbdev(interface)->dev, +				       RFKILL_TYPE_WWAN, +				       &hso_rfkill_ops, hso_dev); +	if (!hso_net->rfkill) { +		dev_err(dev, "%s - Out of memory\n", __func__); +		kfree(rfkn); +		return; +	}  	if (rfkill_register(hso_net->rfkill) < 0) { +		rfkill_destroy(hso_net->rfkill);  		kfree(rfkn); -		hso_net->rfkill->name = NULL; -		rfkill_free(hso_net->rfkill);  		hso_net->rfkill = NULL;  		dev_err(dev, "%s - Failed to register rfkill\n", __func__);  		return; @@ -3165,8 +3165,10 @@ static void hso_free_interface(struct usb_interface *interface)  			hso_stop_net_device(network_table[i]);  			cancel_work_sync(&network_table[i]->async_put_intf);  			cancel_work_sync(&network_table[i]->async_get_intf); -			if (rfk) +			if (rfk) {  				rfkill_unregister(rfk); +				rfkill_destroy(rfk); +			}  			hso_free_net_device(network_table[i]);  		}  	} diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 796a3adffea..515880aa211 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -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) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 61da08a1648..f7baa406918 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1192,120 +1192,69 @@ static bool ath_is_rfkill_set(struct ath_softc *sc)  				  ah->rfkill_polarity;  } -/* h/w rfkill poll function */ -static void ath_rfkill_poll(struct work_struct *work) +/* s/w rfkill handlers */ +static int ath_rfkill_set_block(void *data, bool blocked)  { -	struct ath_softc *sc = container_of(work, struct ath_softc, -					    rf_kill.rfkill_poll.work); -	bool radio_on; - -	if (sc->sc_flags & SC_OP_INVALID) -		return; - -	radio_on = !ath_is_rfkill_set(sc); - -	/* -	 * enable/disable radio only when there is a -	 * state change in RF switch -	 */ -	if (radio_on == !!(sc->sc_flags & SC_OP_RFKILL_HW_BLOCKED)) { -		enum rfkill_state state; - -		if (sc->sc_flags & SC_OP_RFKILL_SW_BLOCKED) { -			state = radio_on ? RFKILL_STATE_SOFT_BLOCKED -				: RFKILL_STATE_HARD_BLOCKED; -		} else if (radio_on) { -			ath_radio_enable(sc); -			state = RFKILL_STATE_UNBLOCKED; -		} else { -			ath_radio_disable(sc); -			state = RFKILL_STATE_HARD_BLOCKED; -		} - -		if (state == RFKILL_STATE_HARD_BLOCKED) -			sc->sc_flags |= SC_OP_RFKILL_HW_BLOCKED; -		else -			sc->sc_flags &= ~SC_OP_RFKILL_HW_BLOCKED; +	struct ath_softc *sc = data; -		rfkill_force_state(sc->rf_kill.rfkill, state); -	} +	if (blocked) +		ath_radio_disable(sc); +	else +		ath_radio_enable(sc); -	queue_delayed_work(sc->hw->workqueue, &sc->rf_kill.rfkill_poll, -			   msecs_to_jiffies(ATH_RFKILL_POLL_INTERVAL)); +	return 0;  } -/* s/w rfkill handler */ -static int ath_sw_toggle_radio(void *data, enum rfkill_state state) +static void ath_rfkill_poll_state(struct rfkill *rfkill, void *data)  {  	struct ath_softc *sc = data; +	bool blocked = !!ath_is_rfkill_set(sc); -	switch (state) { -	case RFKILL_STATE_SOFT_BLOCKED: -		if (!(sc->sc_flags & (SC_OP_RFKILL_HW_BLOCKED | -		    SC_OP_RFKILL_SW_BLOCKED))) -			ath_radio_disable(sc); -		sc->sc_flags |= SC_OP_RFKILL_SW_BLOCKED; -		return 0; -	case RFKILL_STATE_UNBLOCKED: -		if ((sc->sc_flags & SC_OP_RFKILL_SW_BLOCKED)) { -			sc->sc_flags &= ~SC_OP_RFKILL_SW_BLOCKED; -			if (sc->sc_flags & SC_OP_RFKILL_HW_BLOCKED) { -				DPRINTF(sc, ATH_DBG_FATAL, "Can't turn on the" -					"radio as it is disabled by h/w\n"); -				return -EPERM; -			} -			ath_radio_enable(sc); -		} -		return 0; -	default: -		return -EINVAL; -	} +	if (rfkill_set_hw_state(rfkill, blocked)) +		ath_radio_disable(sc); +	else +		ath_radio_enable(sc);  }  /* Init s/w rfkill */  static int ath_init_sw_rfkill(struct ath_softc *sc)  { -	sc->rf_kill.rfkill = rfkill_allocate(wiphy_dev(sc->hw->wiphy), -					     RFKILL_TYPE_WLAN); +	sc->rf_kill.ops.set_block = ath_rfkill_set_block; +	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) +		sc->rf_kill.ops.poll = ath_rfkill_poll_state; + +	snprintf(sc->rf_kill.rfkill_name, sizeof(sc->rf_kill.rfkill_name), +		"ath9k-%s::rfkill", wiphy_name(sc->hw->wiphy)); + +	sc->rf_kill.rfkill = rfkill_alloc(sc->rf_kill.rfkill_name, +					  wiphy_dev(sc->hw->wiphy), +					  RFKILL_TYPE_WLAN, +					  &sc->rf_kill.ops, sc);  	if (!sc->rf_kill.rfkill) {  		DPRINTF(sc, ATH_DBG_FATAL, "Failed to allocate rfkill\n");  		return -ENOMEM;  	} -	snprintf(sc->rf_kill.rfkill_name, sizeof(sc->rf_kill.rfkill_name), -		"ath9k-%s::rfkill", wiphy_name(sc->hw->wiphy)); -	sc->rf_kill.rfkill->name = sc->rf_kill.rfkill_name; -	sc->rf_kill.rfkill->data = sc; -	sc->rf_kill.rfkill->toggle_radio = ath_sw_toggle_radio; -	sc->rf_kill.rfkill->state = RFKILL_STATE_UNBLOCKED; -  	return 0;  }  /* Deinitialize rfkill */  static void ath_deinit_rfkill(struct ath_softc *sc)  { -	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) -		cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll); -  	if (sc->sc_flags & SC_OP_RFKILL_REGISTERED) {  		rfkill_unregister(sc->rf_kill.rfkill); +		rfkill_destroy(sc->rf_kill.rfkill);  		sc->sc_flags &= ~SC_OP_RFKILL_REGISTERED; -		sc->rf_kill.rfkill = NULL;  	}  }  static int ath_start_rfkill_poll(struct ath_softc *sc)  { -	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) -		queue_delayed_work(sc->hw->workqueue, -				   &sc->rf_kill.rfkill_poll, 0); -  	if (!(sc->sc_flags & SC_OP_RFKILL_REGISTERED)) {  		if (rfkill_register(sc->rf_kill.rfkill)) {  			DPRINTF(sc, ATH_DBG_FATAL,  				"Unable to register rfkill\n"); -			rfkill_free(sc->rf_kill.rfkill); +			rfkill_destroy(sc->rf_kill.rfkill);  			/* Deinitialize the device */  			ath_cleanup(sc); @@ -1678,10 +1627,6 @@ int ath_attach(u16 devid, struct ath_softc *sc)  		goto error_attach;  #if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) -	/* Initialze h/w Rfkill */ -	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) -		INIT_DELAYED_WORK(&sc->rf_kill.rfkill_poll, ath_rfkill_poll); -  	/* Initialize s/w rfkill */  	error = ath_init_sw_rfkill(sc);  	if (error) @@ -2214,10 +2159,8 @@ static void ath9k_stop(struct ieee80211_hw *hw)  	} else  		sc->rx.rxlink = NULL; -#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) -	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) -		cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll); -#endif +	rfkill_pause_polling(sc->rf_kill.rfkill); +  	/* disable HAL and put h/w to sleep */  	ath9k_hw_disable(sc->sc_ah);  	ath9k_hw_configpcipowersave(sc->sc_ah, 1); diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 168411d322a..ccdf20a2e9b 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -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;  } diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index 21572e40b79..07a99e3faf9 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -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, diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/b43/leds.c index 76f4c7bad8b..9a498d3fc65 100644 --- a/drivers/net/wireless/b43/leds.c +++ b/drivers/net/wireless/b43/leds.c @@ -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; diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index cb4a8712946..1d3e40095ad 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -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");  		}  	} diff --git a/drivers/net/wireless/b43/phy_a.c b/drivers/net/wireless/b43/phy_a.c index c836c077d51..816e028a262 100644 --- a/drivers/net/wireless/b43/phy_a.c +++ b/drivers/net/wireless/b43/phy_a.c @@ -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); diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c index e176b6e0d9c..6d241622210 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -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);  } diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h index b2d99101947..f4c2d79cbc8 100644 --- a/drivers/net/wireless/b43/phy_common.h +++ b/drivers/net/wireless/b43/phy_common.h @@ -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. diff --git a/drivers/net/wireless/b43/phy_g.c b/drivers/net/wireless/b43/phy_g.c index e7b98f013b0..5300232449f 100644 --- a/drivers/net/wireless/b43/phy_g.c +++ b/drivers/net/wireless/b43/phy_g.c @@ -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; diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c index 58e319d6b1e..ea0d3a3a6a6 100644 --- a/drivers/net/wireless/b43/phy_lp.c +++ b/drivers/net/wireless/b43/phy_lp.c @@ -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  } diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 8bcfda5f3f0..be7b5604947 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -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  } diff --git a/drivers/net/wireless/b43/rfkill.c b/drivers/net/wireless/b43/rfkill.c index 9e1d00bc24d..96047843cd5 100644 --- a/drivers/net/wireless/b43/rfkill.c +++ b/drivers/net/wireless/b43/rfkill.c @@ -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;  } diff --git a/drivers/net/wireless/b43/rfkill.h b/drivers/net/wireless/b43/rfkill.h index adacf936d81..da497e01bbb 100644 --- a/drivers/net/wireless/b43/rfkill.h +++ b/drivers/net/wireless/b43/rfkill.h @@ -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 */ diff --git a/drivers/net/wireless/b43legacy/Kconfig b/drivers/net/wireless/b43legacy/Kconfig index d4f628a74bb..6893f439df7 100644 --- a/drivers/net/wireless/b43legacy/Kconfig +++ b/drivers/net/wireless/b43legacy/Kconfig @@ -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, diff --git a/drivers/net/wireless/b43legacy/leds.c b/drivers/net/wireless/b43legacy/leds.c index 3ea55b18c70..538d3117594 100644 --- a/drivers/net/wireless/b43legacy/leds.c +++ b/drivers/net/wireless/b43legacy/leds.c @@ -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; diff --git a/drivers/net/wireless/b43legacy/rfkill.c b/drivers/net/wireless/b43legacy/rfkill.c index 4b0c7d27a51..c6230a64505 100644 --- a/drivers/net/wireless/b43legacy/rfkill.c +++ b/drivers/net/wireless/b43legacy/rfkill.c @@ -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;  } diff --git a/drivers/net/wireless/b43legacy/rfkill.h b/drivers/net/wireless/b43legacy/rfkill.h index 11150a8032f..adffc503a6a 100644 --- a/drivers/net/wireless/b43legacy/rfkill.h +++ b/drivers/net/wireless/b43legacy/rfkill.h @@ -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 */ diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index 8304f6406a1..6fe259fcfb8 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -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" diff --git a/drivers/net/wireless/iwlwifi/iwl-rfkill.c b/drivers/net/wireless/iwlwifi/iwl-rfkill.c index 65605ad44e4..13149936fd2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rfkill.c +++ b/drivers/net/wireless/iwlwifi/iwl-rfkill.c @@ -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); diff --git a/drivers/net/wireless/iwmc3200wifi/rfkill.c b/drivers/net/wireless/iwmc3200wifi/rfkill.c index 4ca8b495f82..8ee2c3c09a0 100644 --- a/drivers/net/wireless/iwmc3200wifi/rfkill.c +++ b/drivers/net/wireless/iwmc3200wifi/rfkill.c @@ -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;  } diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 284ebaca6e4..c682ac53641 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -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 diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 62d02b3c998..b618fa51db2 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -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;  } diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index af9f4302117..2faf0e14f05 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -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; -} - -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); +	if (status & BIT(bit)) +		rfkill_set_hw_state(rfkill, !!(status & BIT(16)));  } -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_destroy(wwan_rfkill); +	if (bluetooth_rfkill)  		rfkill_unregister(bluetooth_rfkill); -		bluetooth_rfkill = NULL; -	}  err_bluetooth: -	if (bluetooth_rfkill) -		rfkill_free(bluetooth_rfkill); -	if (wifi_rfkill) { +	rfkill_destroy(bluetooth_rfkill); +	if (wifi_rfkill)  		rfkill_unregister(wifi_rfkill); -		wifi_rfkill = NULL; -	}  err_wifi: -	if (wifi_rfkill) -		rfkill_free(wifi_rfkill); +	rfkill_destroy(wifi_rfkill);  	return ret;  } diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 353a898c369..1208d0cedd1 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -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: diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index fe171fad12c..8d931145cbf 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -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); -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); +	return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, query);  } -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 const struct rfkill_ops hp_wmi_rfkill_ops = { +	.set_block = hp_wmi_set_block, +}; -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;  } diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index f1963b05175..aec0b27fd77 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -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) -{ -	int err = 0; -	struct rfkill *sony_wifi_rfkill; - -	sony_wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN); -	if (!sony_wifi_rfkill) -		return -1; -	sony_wifi_rfkill->name = "sony-wifi"; -	sony_wifi_rfkill->toggle_radio = sony_nc_rfkill_set; -	sony_wifi_rfkill->get_state = sony_nc_rfkill_get; -	sony_wifi_rfkill->data = (void *)SONY_WIFI; -	err = rfkill_register(sony_wifi_rfkill); -	if (err) -		rfkill_free(sony_wifi_rfkill); -	else { -		sony_rfkill_devices[SONY_WIFI] = sony_wifi_rfkill; -		sony_nc_rfkill_set(sony_wifi_rfkill->data, -				RFKILL_STATE_UNBLOCKED); -	} -	return err; -} +static const struct rfkill_ops sony_rfkill_ops = { +	.set_block = sony_nc_rfkill_set, +}; -static int sony_nc_setup_bluetooth_rfkill(struct acpi_device *device) +static int sony_nc_setup_rfkill(struct acpi_device *device, +				enum sony_nc_rfkill nc_type)  {  	int err = 0; -	struct rfkill *sony_bluetooth_rfkill; +	struct rfkill *rfk; +	enum rfkill_type type; +	const char *name; -	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); +	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_wwan_rfkill(struct acpi_device *device) -{ -	int err = 0; -	struct rfkill *sony_wwan_rfkill; +	rfk = rfkill_alloc(name, &device->dev, type, +			   &sony_rfkill_ops, (void *)nc_type); +	if (!rfk) +		return -ENOMEM; -	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); +	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 int sony_nc_setup_wimax_rfkill(struct acpi_device *device) +static void sony_nc_rfkill_update()  { -	int err = 0; -	struct rfkill *sony_wimax_rfkill; +	enum sony_nc_rfkill i; +	int result; +	bool hwblock; -	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); -	} -	return err; -} +	sony_call_snc_handle(0x124, 0x200, &result); +	hwblock = !(result & 0x1); -static void sony_nc_rfkill_update() -{ -	int i; -	enum rfkill_state state; +	for (i = 0; i < N_SONY_RFKILL; i++) { +		int argument = sony_rfkill_address[i]; -	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); +		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;  } diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 912be65b626..cfcafa4e947 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -166,13 +166,6 @@ enum {  #define TPACPI_MAX_ACPI_ARGS 3 -/* rfkill switches */ -enum { -	TPACPI_RFK_BLUETOOTH_SW_ID = 0, -	TPACPI_RFK_WWAN_SW_ID, -	TPACPI_RFK_UWB_SW_ID, -}; -  /* printk headers */  #define TPACPI_LOG TPACPI_FILE ": "  #define TPACPI_EMERG	KERN_EMERG	TPACPI_LOG @@ -1005,67 +998,237 @@ static int __init tpacpi_check_std_acpi_brightness_support(void)  	return 0;  } -static int __init tpacpi_new_rfkill(const unsigned int id, -			struct rfkill **rfk, +static void printk_deprecated_attribute(const char * const what, +					const char * const details) +{ +	tpacpi_log_usertask("deprecated sysfs attribute"); +	printk(TPACPI_WARN "WARNING: sysfs attribute %s is deprecated and " +		"will be removed. %s\n", +		what, details); +} + +/************************************************************************* + * rfkill and radio control support helpers + */ + +/* + * ThinkPad-ACPI firmware handling model: + * + * WLSW (master wireless switch) is event-driven, and is common to all + * firmware-controlled radios.  It cannot be controlled, just monitored, + * as expected.  It overrides all radio state in firmware + * + * The kernel, a masked-off hotkey, and WLSW can change the radio state + * (TODO: verify how WLSW interacts with the returned radio state). + * + * The only time there are shadow radio state changes, is when + * masked-off hotkeys are used. + */ + +/* + * Internal driver API for radio state: + * + * int: < 0 = error, otherwise enum tpacpi_rfkill_state + * bool: true means radio blocked (off) + */ +enum tpacpi_rfkill_state { +	TPACPI_RFK_RADIO_OFF = 0, +	TPACPI_RFK_RADIO_ON +}; + +/* rfkill switches */ +enum tpacpi_rfk_id { +	TPACPI_RFK_BLUETOOTH_SW_ID = 0, +	TPACPI_RFK_WWAN_SW_ID, +	TPACPI_RFK_UWB_SW_ID, +	TPACPI_RFK_SW_MAX +}; + +static const char *tpacpi_rfkill_names[] = { +	[TPACPI_RFK_BLUETOOTH_SW_ID] = "bluetooth", +	[TPACPI_RFK_WWAN_SW_ID] = "wwan", +	[TPACPI_RFK_UWB_SW_ID] = "uwb", +	[TPACPI_RFK_SW_MAX] = NULL +}; + +/* ThinkPad-ACPI rfkill subdriver */ +struct tpacpi_rfk { +	struct rfkill *rfkill; +	enum tpacpi_rfk_id id; +	const struct tpacpi_rfk_ops *ops; +}; + +struct tpacpi_rfk_ops { +	/* firmware interface */ +	int (*get_status)(void); +	int (*set_status)(const enum tpacpi_rfkill_state); +}; + +static struct tpacpi_rfk *tpacpi_rfkill_switches[TPACPI_RFK_SW_MAX]; + +/* Query FW and update rfkill sw state for a given rfkill switch */ +static int tpacpi_rfk_update_swstate(const struct tpacpi_rfk *tp_rfk) +{ +	int status; + +	if (!tp_rfk) +		return -ENODEV; + +	status = (tp_rfk->ops->get_status)(); +	if (status < 0) +		return status; + +	rfkill_set_sw_state(tp_rfk->rfkill, +			    (status == TPACPI_RFK_RADIO_OFF)); + +	return status; +} + +/* Query FW and update rfkill sw state for all rfkill switches */ +static void tpacpi_rfk_update_swstate_all(void) +{ +	unsigned int i; + +	for (i = 0; i < TPACPI_RFK_SW_MAX; i++) +		tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[i]); +} + +/* + * Sync the HW-blocking state of all rfkill switches, + * do notice it causes the rfkill core to schedule uevents + */ +static void tpacpi_rfk_update_hwblock_state(bool blocked) +{ +	unsigned int i; +	struct tpacpi_rfk *tp_rfk; + +	for (i = 0; i < TPACPI_RFK_SW_MAX; i++) { +		tp_rfk = tpacpi_rfkill_switches[i]; +		if (tp_rfk) { +			if (rfkill_set_hw_state(tp_rfk->rfkill, +						blocked)) { +				/* ignore -- we track sw block */ +			} +		} +	} +} + +/* Call to get the WLSW state from the firmware */ +static int hotkey_get_wlsw(void); + +/* Call to query WLSW state and update all rfkill switches */ +static bool tpacpi_rfk_check_hwblock_state(void) +{ +	int res = hotkey_get_wlsw(); +	int hw_blocked; + +	/* When unknown or unsupported, we have to assume it is unblocked */ +	if (res < 0) +		return false; + +	hw_blocked = (res == TPACPI_RFK_RADIO_OFF); +	tpacpi_rfk_update_hwblock_state(hw_blocked); + +	return hw_blocked; +} + +static int tpacpi_rfk_hook_set_block(void *data, bool blocked) +{ +	struct tpacpi_rfk *tp_rfk = data; +	int res; + +	dbg_printk(TPACPI_DBG_RFKILL, +		   "request to change radio state to %s\n", +		   blocked ? "blocked" : "unblocked"); + +	/* try to set radio state */ +	res = (tp_rfk->ops->set_status)(blocked ? +				TPACPI_RFK_RADIO_OFF : TPACPI_RFK_RADIO_ON); + +	/* and update the rfkill core with whatever the FW really did */ +	tpacpi_rfk_update_swstate(tp_rfk); + +	return (res < 0) ? res : 0; +} + +static const struct rfkill_ops tpacpi_rfk_rfkill_ops = { +	.set_block = tpacpi_rfk_hook_set_block, +}; + +static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, +			const struct tpacpi_rfk_ops *tp_rfkops,  			const enum rfkill_type rfktype,  			const char *name, -			const bool set_default, -			int (*toggle_radio)(void *, enum rfkill_state), -			int (*get_state)(void *, enum rfkill_state *)) +			const bool set_default)  { +	struct tpacpi_rfk *atp_rfk;  	int res; -	enum rfkill_state initial_state = RFKILL_STATE_SOFT_BLOCKED; +	bool initial_sw_state = false; +	int initial_sw_status; -	res = get_state(NULL, &initial_state); -	if (res < 0) { +	BUG_ON(id >= TPACPI_RFK_SW_MAX || tpacpi_rfkill_switches[id]); + +	initial_sw_status = (tp_rfkops->get_status)(); +	if (initial_sw_status < 0) {  		printk(TPACPI_ERR  			"failed to read initial state for %s, error %d; " -			"will turn radio off\n", name, res); -	} else if (set_default) { -		/* try to set the initial state as the default for the rfkill -		 * type, since we ask the firmware to preserve it across S5 in -		 * NVRAM */ -		if (rfkill_set_default(rfktype, -				(initial_state == RFKILL_STATE_UNBLOCKED) ? -					RFKILL_STATE_UNBLOCKED : -					RFKILL_STATE_SOFT_BLOCKED) == -EPERM) -			vdbg_printk(TPACPI_DBG_RFKILL, -				    "Default state for %s cannot be changed\n", -				    name); +			"will turn radio off\n", name, initial_sw_status); +	} else { +		initial_sw_state = (initial_sw_status == TPACPI_RFK_RADIO_OFF); +		if (set_default) { +			/* try to set the initial state as the default for the +			 * rfkill type, since we ask the firmware to preserve +			 * it across S5 in NVRAM */ +			rfkill_set_global_sw_state(rfktype, initial_sw_state); +		}  	} -	*rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype); -	if (!*rfk) { +	atp_rfk = kzalloc(sizeof(struct tpacpi_rfk), GFP_KERNEL); +	if (atp_rfk) +		atp_rfk->rfkill = rfkill_alloc(name, +						&tpacpi_pdev->dev, +						rfktype, +						&tpacpi_rfk_rfkill_ops, +						atp_rfk); +	if (!atp_rfk || !atp_rfk->rfkill) {  		printk(TPACPI_ERR  			"failed to allocate memory for rfkill class\n"); +		kfree(atp_rfk);  		return -ENOMEM;  	} -	(*rfk)->name = name; -	(*rfk)->get_state = get_state; -	(*rfk)->toggle_radio = toggle_radio; -	(*rfk)->state = initial_state; +	atp_rfk->id = id; +	atp_rfk->ops = tp_rfkops; -	res = rfkill_register(*rfk); +	rfkill_set_states(atp_rfk->rfkill, initial_sw_state, +				tpacpi_rfk_check_hwblock_state()); + +	res = rfkill_register(atp_rfk->rfkill);  	if (res < 0) {  		printk(TPACPI_ERR  			"failed to register %s rfkill switch: %d\n",  			name, res); -		rfkill_free(*rfk); -		*rfk = NULL; +		rfkill_destroy(atp_rfk->rfkill); +		kfree(atp_rfk);  		return res;  	} +	tpacpi_rfkill_switches[id] = atp_rfk;  	return 0;  } -static void printk_deprecated_attribute(const char * const what, -					const char * const details) +static void tpacpi_destroy_rfkill(const enum tpacpi_rfk_id id)  { -	tpacpi_log_usertask("deprecated sysfs attribute"); -	printk(TPACPI_WARN "WARNING: sysfs attribute %s is deprecated and " -		"will be removed. %s\n", -		what, details); +	struct tpacpi_rfk *tp_rfk; + +	BUG_ON(id >= TPACPI_RFK_SW_MAX); + +	tp_rfk = tpacpi_rfkill_switches[id]; +	if (tp_rfk) { +		rfkill_unregister(tp_rfk->rfkill); +		tpacpi_rfkill_switches[id] = NULL; +		kfree(tp_rfk); +	}  }  static void printk_deprecated_rfkill_attribute(const char * const what) @@ -1074,6 +1237,112 @@ static void printk_deprecated_rfkill_attribute(const char * const what)  			"Please switch to generic rfkill before year 2010");  } +/* sysfs <radio> enable ------------------------------------------------ */ +static ssize_t tpacpi_rfk_sysfs_enable_show(const enum tpacpi_rfk_id id, +					    struct device_attribute *attr, +					    char *buf) +{ +	int status; + +	printk_deprecated_rfkill_attribute(attr->attr.name); + +	/* This is in the ABI... */ +	if (tpacpi_rfk_check_hwblock_state()) { +		status = TPACPI_RFK_RADIO_OFF; +	} else { +		status = tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]); +		if (status < 0) +			return status; +	} + +	return snprintf(buf, PAGE_SIZE, "%d\n", +			(status == TPACPI_RFK_RADIO_ON) ? 1 : 0); +} + +static ssize_t tpacpi_rfk_sysfs_enable_store(const enum tpacpi_rfk_id id, +			    struct device_attribute *attr, +			    const char *buf, size_t count) +{ +	unsigned long t; +	int res; + +	printk_deprecated_rfkill_attribute(attr->attr.name); + +	if (parse_strtoul(buf, 1, &t)) +		return -EINVAL; + +	tpacpi_disclose_usertask(attr->attr.name, "set to %ld\n", t); + +	/* This is in the ABI... */ +	if (tpacpi_rfk_check_hwblock_state() && !!t) +		return -EPERM; + +	res = tpacpi_rfkill_switches[id]->ops->set_status((!!t) ? +				TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF); +	tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]); + +	return (res < 0) ? res : count; +} + +/* procfs -------------------------------------------------------------- */ +static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, char *p) +{ +	int len = 0; + +	if (id >= TPACPI_RFK_SW_MAX) +		len += sprintf(p + len, "status:\t\tnot supported\n"); +	else { +		int status; + +		/* This is in the ABI... */ +		if (tpacpi_rfk_check_hwblock_state()) { +			status = TPACPI_RFK_RADIO_OFF; +		} else { +			status = tpacpi_rfk_update_swstate( +						tpacpi_rfkill_switches[id]); +			if (status < 0) +				return status; +		} + +		len += sprintf(p + len, "status:\t\t%s\n", +				(status == TPACPI_RFK_RADIO_ON) ? +					"enabled" : "disabled"); +		len += sprintf(p + len, "commands:\tenable, disable\n"); +	} + +	return len; +} + +static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf) +{ +	char *cmd; +	int status = -1; +	int res = 0; + +	if (id >= TPACPI_RFK_SW_MAX) +		return -ENODEV; + +	while ((cmd = next_cmd(&buf))) { +		if (strlencmp(cmd, "enable") == 0) +			status = TPACPI_RFK_RADIO_ON; +		else if (strlencmp(cmd, "disable") == 0) +			status = TPACPI_RFK_RADIO_OFF; +		else +			return -EINVAL; +	} + +	if (status != -1) { +		tpacpi_disclose_usertask("procfs", "attempt to %s %s\n", +				(status == TPACPI_RFK_RADIO_ON) ? +						"enable" : "disable", +				tpacpi_rfkill_names[id]); +		res = (tpacpi_rfkill_switches[id]->ops->set_status)(status); +		tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]); +	} + +	return res; +} +  /*************************************************************************   * thinkpad-acpi driver attributes   */ @@ -1127,8 +1396,6 @@ static DRIVER_ATTR(version, S_IRUGO,  #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES -static void tpacpi_send_radiosw_update(void); -  /* wlsw_emulstate ------------------------------------------------------ */  static ssize_t tpacpi_driver_wlsw_emulstate_show(struct device_driver *drv,  						char *buf) @@ -1144,11 +1411,10 @@ static ssize_t tpacpi_driver_wlsw_emulstate_store(struct device_driver *drv,  	if (parse_strtoul(buf, 1, &t))  		return -EINVAL; -	if (tpacpi_wlsw_emulstate != t) { -		tpacpi_wlsw_emulstate = !!t; -		tpacpi_send_radiosw_update(); -	} else +	if (tpacpi_wlsw_emulstate != !!t) {  		tpacpi_wlsw_emulstate = !!t; +		tpacpi_rfk_update_hwblock_state(!t);	/* negative logic */ +	}  	return count;  } @@ -1463,17 +1729,23 @@ static struct attribute_set *hotkey_dev_attributes;  /* HKEY.MHKG() return bits */  #define TP_HOTKEY_TABLET_MASK (1 << 3) -static int hotkey_get_wlsw(int *status) +static int hotkey_get_wlsw(void)  { +	int status; + +	if (!tp_features.hotkey_wlsw) +		return -ENODEV; +  #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES -	if (dbg_wlswemul) { -		*status = !!tpacpi_wlsw_emulstate; -		return 0; -	} +	if (dbg_wlswemul) +		return (tpacpi_wlsw_emulstate) ? +				TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;  #endif -	if (!acpi_evalf(hkey_handle, status, "WLSW", "d")) + +	if (!acpi_evalf(hkey_handle, &status, "WLSW", "d"))  		return -EIO; -	return 0; + +	return (status) ? TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;  }  static int hotkey_get_tablet_mode(int *status) @@ -2107,12 +2379,16 @@ static ssize_t hotkey_radio_sw_show(struct device *dev,  			   struct device_attribute *attr,  			   char *buf)  { -	int res, s; -	res = hotkey_get_wlsw(&s); +	int res; +	res = hotkey_get_wlsw();  	if (res < 0)  		return res; -	return snprintf(buf, PAGE_SIZE, "%d\n", !!s); +	/* Opportunistic update */ +	tpacpi_rfk_update_hwblock_state((res == TPACPI_RFK_RADIO_OFF)); + +	return snprintf(buf, PAGE_SIZE, "%d\n", +			(res == TPACPI_RFK_RADIO_OFF) ? 0 : 1);  }  static struct device_attribute dev_attr_hotkey_radio_sw = @@ -2223,30 +2499,52 @@ static struct attribute *hotkey_mask_attributes[] __initdata = {  	&dev_attr_hotkey_wakeup_hotunplug_complete.attr,  }; -static void bluetooth_update_rfk(void); -static void wan_update_rfk(void); -static void uwb_update_rfk(void); +/* + * Sync both the hw and sw blocking state of all switches + */  static void tpacpi_send_radiosw_update(void)  {  	int wlsw; -	/* Sync these BEFORE sending any rfkill events */ -	if (tp_features.bluetooth) -		bluetooth_update_rfk(); -	if (tp_features.wan) -		wan_update_rfk(); -	if (tp_features.uwb) -		uwb_update_rfk(); +	/* +	 * We must sync all rfkill controllers *before* issuing any +	 * rfkill input events, or we will race the rfkill core input +	 * handler. +	 * +	 * tpacpi_inputdev_send_mutex works as a syncronization point +	 * for the above. +	 * +	 * We optimize to avoid numerous calls to hotkey_get_wlsw. +	 */ -	if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { +	wlsw = hotkey_get_wlsw(); + +	/* Sync hw blocking state first if it is hw-blocked */ +	if (wlsw == TPACPI_RFK_RADIO_OFF) +		tpacpi_rfk_update_hwblock_state(true); + +	/* Sync sw blocking state */ +	tpacpi_rfk_update_swstate_all(); + +	/* Sync hw blocking state last if it is hw-unblocked */ +	if (wlsw == TPACPI_RFK_RADIO_ON) +		tpacpi_rfk_update_hwblock_state(false); + +	/* Issue rfkill input event for WLSW switch */ +	if (!(wlsw < 0)) {  		mutex_lock(&tpacpi_inputdev_send_mutex);  		input_report_switch(tpacpi_inputdev, -				    SW_RFKILL_ALL, !!wlsw); +				    SW_RFKILL_ALL, (wlsw > 0));  		input_sync(tpacpi_inputdev);  		mutex_unlock(&tpacpi_inputdev_send_mutex);  	} + +	/* +	 * this can be unconditional, as we will poll state again +	 * if userspace uses the notify to read data +	 */  	hotkey_radio_sw_notify_change();  } @@ -3056,8 +3354,6 @@ enum {  #define TPACPI_RFK_BLUETOOTH_SW_NAME	"tpacpi_bluetooth_sw" -static struct rfkill *tpacpi_bluetooth_rfkill; -  static void bluetooth_suspend(pm_message_t state)  {  	/* Try to make sure radio will resume powered off */ @@ -3067,83 +3363,47 @@ static void bluetooth_suspend(pm_message_t state)  			"bluetooth power down on resume request failed\n");  } -static int bluetooth_get_radiosw(void) +static int bluetooth_get_status(void)  {  	int status; -	if (!tp_features.bluetooth) -		return -ENODEV; - -	/* WLSW overrides bluetooth in firmware/hardware, reflect that */ -	if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) -		return RFKILL_STATE_HARD_BLOCKED; -  #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES  	if (dbg_bluetoothemul)  		return (tpacpi_bluetooth_emulstate) ? -			RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; +		       TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;  #endif  	if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))  		return -EIO;  	return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ? -		RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; -} - -static void bluetooth_update_rfk(void) -{ -	int status; - -	if (!tpacpi_bluetooth_rfkill) -		return; - -	status = bluetooth_get_radiosw(); -	if (status < 0) -		return; -	rfkill_force_state(tpacpi_bluetooth_rfkill, status); - -	vdbg_printk(TPACPI_DBG_RFKILL, -		"forced rfkill state to %d\n", -		status); +			TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;  } -static int bluetooth_set_radiosw(int radio_on, int update_rfk) +static int bluetooth_set_status(enum tpacpi_rfkill_state state)  {  	int status; -	if (!tp_features.bluetooth) -		return -ENODEV; - -	/* WLSW overrides bluetooth in firmware/hardware, but there is no -	 * reason to risk weird behaviour. */ -	if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status -	    && radio_on) -		return -EPERM; -  	vdbg_printk(TPACPI_DBG_RFKILL, -		"will %s bluetooth\n", radio_on ? "enable" : "disable"); +		"will attempt to %s bluetooth\n", +		(state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable");  #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES  	if (dbg_bluetoothemul) { -		tpacpi_bluetooth_emulstate = !!radio_on; -		if (update_rfk) -			bluetooth_update_rfk(); +		tpacpi_bluetooth_emulstate = (state == TPACPI_RFK_RADIO_ON);  		return 0;  	}  #endif  	/* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */ -	if (radio_on) +	if (state == TPACPI_RFK_RADIO_ON)  		status = TP_ACPI_BLUETOOTH_RADIOSSW;  	else  		status = 0; +  	if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))  		return -EIO; -	if (update_rfk) -		bluetooth_update_rfk(); -  	return 0;  } @@ -3152,35 +3412,16 @@ static ssize_t bluetooth_enable_show(struct device *dev,  			   struct device_attribute *attr,  			   char *buf)  { -	int status; - -	printk_deprecated_rfkill_attribute("bluetooth_enable"); - -	status = bluetooth_get_radiosw(); -	if (status < 0) -		return status; - -	return snprintf(buf, PAGE_SIZE, "%d\n", -			(status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); +	return tpacpi_rfk_sysfs_enable_show(TPACPI_RFK_BLUETOOTH_SW_ID, +			attr, buf);  }  static ssize_t bluetooth_enable_store(struct device *dev,  			    struct device_attribute *attr,  			    const char *buf, size_t count)  { -	unsigned long t; -	int res; - -	printk_deprecated_rfkill_attribute("bluetooth_enable"); - -	if (parse_strtoul(buf, 1, &t)) -		return -EINVAL; - -	tpacpi_disclose_usertask("bluetooth_enable", "set to %ld\n", t); - -	res = bluetooth_set_radiosw(t, 1); - -	return (res) ? res : count; +	return tpacpi_rfk_sysfs_enable_store(TPACPI_RFK_BLUETOOTH_SW_ID, +				attr, buf, count);  }  static struct device_attribute dev_attr_bluetooth_enable = @@ -3198,23 +3439,10 @@ static const struct attribute_group bluetooth_attr_group = {  	.attrs = bluetooth_attributes,  }; -static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state) -{ -	int bts = bluetooth_get_radiosw(); - -	if (bts < 0) -		return bts; - -	*state = bts; -	return 0; -} - -static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state) -{ -	dbg_printk(TPACPI_DBG_RFKILL, -		   "request to change radio state to %d\n", state); -	return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); -} +static const struct tpacpi_rfk_ops bluetooth_tprfk_ops = { +	.get_status = bluetooth_get_status, +	.set_status = bluetooth_set_status, +};  static void bluetooth_shutdown(void)  { @@ -3230,13 +3458,12 @@ static void bluetooth_shutdown(void)  static void bluetooth_exit(void)  { -	bluetooth_shutdown(); - -	if (tpacpi_bluetooth_rfkill) -		rfkill_unregister(tpacpi_bluetooth_rfkill); -  	sysfs_remove_group(&tpacpi_pdev->dev.kobj,  			&bluetooth_attr_group); + +	tpacpi_destroy_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID); + +	bluetooth_shutdown();  }  static int __init bluetooth_init(struct ibm_init_struct *iibm) @@ -3277,20 +3504,18 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)  	if (!tp_features.bluetooth)  		return 1; -	res = sysfs_create_group(&tpacpi_pdev->dev.kobj, -				&bluetooth_attr_group); -	if (res) -		return res; -  	res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID, -				&tpacpi_bluetooth_rfkill, +				&bluetooth_tprfk_ops,  				RFKILL_TYPE_BLUETOOTH,  				TPACPI_RFK_BLUETOOTH_SW_NAME, -				true, -				tpacpi_bluetooth_rfk_set, -				tpacpi_bluetooth_rfk_get); +				true); +	if (res) +		return res; + +	res = sysfs_create_group(&tpacpi_pdev->dev.kobj, +				&bluetooth_attr_group);  	if (res) { -		bluetooth_exit(); +		tpacpi_destroy_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID);  		return res;  	} @@ -3300,46 +3525,12 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)  /* procfs -------------------------------------------------------------- */  static int bluetooth_read(char *p)  { -	int len = 0; -	int status = bluetooth_get_radiosw(); - -	if (!tp_features.bluetooth) -		len += sprintf(p + len, "status:\t\tnot supported\n"); -	else { -		len += sprintf(p + len, "status:\t\t%s\n", -				(status == RFKILL_STATE_UNBLOCKED) ? -					"enabled" : "disabled"); -		len += sprintf(p + len, "commands:\tenable, disable\n"); -	} - -	return len; +	return tpacpi_rfk_procfs_read(TPACPI_RFK_BLUETOOTH_SW_ID, p);  }  static int bluetooth_write(char *buf)  { -	char *cmd; -	int state = -1; - -	if (!tp_features.bluetooth) -		return -ENODEV; - -	while ((cmd = next_cmd(&buf))) { -		if (strlencmp(cmd, "enable") == 0) { -			state = 1; -		} else if (strlencmp(cmd, "disable") == 0) { -			state = 0; -		} else -			return -EINVAL; -	} - -	if (state != -1) { -		tpacpi_disclose_usertask("procfs bluetooth", -			"attempt to %s\n", -			state ? "enable" : "disable"); -		bluetooth_set_radiosw(state, 1); -	} - -	return 0; +	return tpacpi_rfk_procfs_write(TPACPI_RFK_BLUETOOTH_SW_ID, buf);  }  static struct ibm_struct bluetooth_driver_data = { @@ -3365,8 +3556,6 @@ enum {  #define TPACPI_RFK_WWAN_SW_NAME		"tpacpi_wwan_sw" -static struct rfkill *tpacpi_wan_rfkill; -  static void wan_suspend(pm_message_t state)  {  	/* Try to make sure radio will resume powered off */ @@ -3376,83 +3565,47 @@ static void wan_suspend(pm_message_t state)  			"WWAN power down on resume request failed\n");  } -static int wan_get_radiosw(void) +static int wan_get_status(void)  {  	int status; -	if (!tp_features.wan) -		return -ENODEV; - -	/* WLSW overrides WWAN in firmware/hardware, reflect that */ -	if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) -		return RFKILL_STATE_HARD_BLOCKED; -  #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES  	if (dbg_wwanemul)  		return (tpacpi_wwan_emulstate) ? -			RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; +		       TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;  #endif  	if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))  		return -EIO;  	return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ? -		RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; -} - -static void wan_update_rfk(void) -{ -	int status; - -	if (!tpacpi_wan_rfkill) -		return; - -	status = wan_get_radiosw(); -	if (status < 0) -		return; -	rfkill_force_state(tpacpi_wan_rfkill, status); - -	vdbg_printk(TPACPI_DBG_RFKILL, -		"forced rfkill state to %d\n", -		status); +			TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;  } -static int wan_set_radiosw(int radio_on, int update_rfk) +static int wan_set_status(enum tpacpi_rfkill_state state)  {  	int status; -	if (!tp_features.wan) -		return -ENODEV; - -	/* WLSW overrides bluetooth in firmware/hardware, but there is no -	 * reason to risk weird behaviour. */ -	if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status -	    && radio_on) -		return -EPERM; -  	vdbg_printk(TPACPI_DBG_RFKILL, -		"will %s WWAN\n", radio_on ? "enable" : "disable"); +		"will attempt to %s wwan\n", +		(state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable");  #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES  	if (dbg_wwanemul) { -		tpacpi_wwan_emulstate = !!radio_on; -		if (update_rfk) -			wan_update_rfk(); +		tpacpi_wwan_emulstate = (state == TPACPI_RFK_RADIO_ON);  		return 0;  	}  #endif  	/* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */ -	if (radio_on) +	if (state == TPACPI_RFK_RADIO_ON)  		status = TP_ACPI_WANCARD_RADIOSSW;  	else  		status = 0; +  	if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))  		return -EIO; -	if (update_rfk) -		wan_update_rfk(); -  	return 0;  } @@ -3461,35 +3614,16 @@ static ssize_t wan_enable_show(struct device *dev,  			   struct device_attribute *attr,  			   char *buf)  { -	int status; - -	printk_deprecated_rfkill_attribute("wwan_enable"); - -	status = wan_get_radiosw(); -	if (status < 0) -		return status; - -	return snprintf(buf, PAGE_SIZE, "%d\n", -			(status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); +	return tpacpi_rfk_sysfs_enable_show(TPACPI_RFK_WWAN_SW_ID, +			attr, buf);  }  static ssize_t wan_enable_store(struct device *dev,  			    struct device_attribute *attr,  			    const char *buf, size_t count)  { -	unsigned long t; -	int res; - -	printk_deprecated_rfkill_attribute("wwan_enable"); - -	if (parse_strtoul(buf, 1, &t)) -		return -EINVAL; - -	tpacpi_disclose_usertask("wwan_enable", "set to %ld\n", t); - -	res = wan_set_radiosw(t, 1); - -	return (res) ? res : count; +	return tpacpi_rfk_sysfs_enable_store(TPACPI_RFK_WWAN_SW_ID, +			attr, buf, count);  }  static struct device_attribute dev_attr_wan_enable = @@ -3507,23 +3641,10 @@ static const struct attribute_group wan_attr_group = {  	.attrs = wan_attributes,  }; -static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state) -{ -	int wans = wan_get_radiosw(); - -	if (wans < 0) -		return wans; - -	*state = wans; -	return 0; -} - -static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state) -{ -	dbg_printk(TPACPI_DBG_RFKILL, -		   "request to change radio state to %d\n", state); -	return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); -} +static const struct tpacpi_rfk_ops wan_tprfk_ops = { +	.get_status = wan_get_status, +	.set_status = wan_set_status, +};  static void wan_shutdown(void)  { @@ -3539,13 +3660,12 @@ static void wan_shutdown(void)  static void wan_exit(void)  { -	wan_shutdown(); - -	if (tpacpi_wan_rfkill) -		rfkill_unregister(tpacpi_wan_rfkill); -  	sysfs_remove_group(&tpacpi_pdev->dev.kobj,  		&wan_attr_group); + +	tpacpi_destroy_rfkill(TPACPI_RFK_WWAN_SW_ID); + +	wan_shutdown();  }  static int __init wan_init(struct ibm_init_struct *iibm) @@ -3584,20 +3704,19 @@ static int __init wan_init(struct ibm_init_struct *iibm)  	if (!tp_features.wan)  		return 1; -	res = sysfs_create_group(&tpacpi_pdev->dev.kobj, -				&wan_attr_group); -	if (res) -		return res; -  	res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID, -				&tpacpi_wan_rfkill, +				&wan_tprfk_ops,  				RFKILL_TYPE_WWAN,  				TPACPI_RFK_WWAN_SW_NAME, -				true, -				tpacpi_wan_rfk_set, -				tpacpi_wan_rfk_get); +				true); +	if (res) +		return res; + +	res = sysfs_create_group(&tpacpi_pdev->dev.kobj, +				&wan_attr_group); +  	if (res) { -		wan_exit(); +		tpacpi_destroy_rfkill(TPACPI_RFK_WWAN_SW_ID);  		return res;  	} @@ -3607,48 +3726,12 @@ static int __init wan_init(struct ibm_init_struct *iibm)  /* procfs -------------------------------------------------------------- */  static int wan_read(char *p)  { -	int len = 0; -	int status = wan_get_radiosw(); - -	tpacpi_disclose_usertask("procfs wan", "read"); - -	if (!tp_features.wan) -		len += sprintf(p + len, "status:\t\tnot supported\n"); -	else { -		len += sprintf(p + len, "status:\t\t%s\n", -				(status == RFKILL_STATE_UNBLOCKED) ? -					"enabled" : "disabled"); -		len += sprintf(p + len, "commands:\tenable, disable\n"); -	} - -	return len; +	return tpacpi_rfk_procfs_read(TPACPI_RFK_WWAN_SW_ID, p);  }  static int wan_write(char *buf)  { -	char *cmd; -	int state = -1; - -	if (!tp_features.wan) -		return -ENODEV; - -	while ((cmd = next_cmd(&buf))) { -		if (strlencmp(cmd, "enable") == 0) { -			state = 1; -		} else if (strlencmp(cmd, "disable") == 0) { -			state = 0; -		} else -			return -EINVAL; -	} - -	if (state != -1) { -		tpacpi_disclose_usertask("procfs wan", -			"attempt to %s\n", -			state ? "enable" : "disable"); -		wan_set_radiosw(state, 1); -	} - -	return 0; +	return tpacpi_rfk_procfs_write(TPACPI_RFK_WWAN_SW_ID, buf);  }  static struct ibm_struct wan_driver_data = { @@ -3672,108 +3755,59 @@ enum {  #define TPACPI_RFK_UWB_SW_NAME	"tpacpi_uwb_sw" -static struct rfkill *tpacpi_uwb_rfkill; - -static int uwb_get_radiosw(void) +static int uwb_get_status(void)  {  	int status; -	if (!tp_features.uwb) -		return -ENODEV; - -	/* WLSW overrides UWB in firmware/hardware, reflect that */ -	if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) -		return RFKILL_STATE_HARD_BLOCKED; -  #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES  	if (dbg_uwbemul)  		return (tpacpi_uwb_emulstate) ? -			RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; +		       TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;  #endif  	if (!acpi_evalf(hkey_handle, &status, "GUWB", "d"))  		return -EIO;  	return ((status & TP_ACPI_UWB_RADIOSSW) != 0) ? -		RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; +			TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;  } -static void uwb_update_rfk(void) +static int uwb_set_status(enum tpacpi_rfkill_state state)  {  	int status; -	if (!tpacpi_uwb_rfkill) -		return; - -	status = uwb_get_radiosw(); -	if (status < 0) -		return; -	rfkill_force_state(tpacpi_uwb_rfkill, status); -  	vdbg_printk(TPACPI_DBG_RFKILL, -		"forced rfkill state to %d\n", -		status); -} - -static int uwb_set_radiosw(int radio_on, int update_rfk) -{ -	int status; - -	if (!tp_features.uwb) -		return -ENODEV; - -	/* WLSW overrides UWB in firmware/hardware, but there is no -	 * reason to risk weird behaviour. */ -	if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status -	    && radio_on) -		return -EPERM; - -	vdbg_printk(TPACPI_DBG_RFKILL, -			"will %s UWB\n", radio_on ? "enable" : "disable"); +		"will attempt to %s UWB\n", +		(state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable");  #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES  	if (dbg_uwbemul) { -		tpacpi_uwb_emulstate = !!radio_on; -		if (update_rfk) -			uwb_update_rfk(); +		tpacpi_uwb_emulstate = (state == TPACPI_RFK_RADIO_ON);  		return 0;  	}  #endif -	status = (radio_on) ? TP_ACPI_UWB_RADIOSSW : 0; +	if (state == TPACPI_RFK_RADIO_ON) +		status = TP_ACPI_UWB_RADIOSSW; +	else +		status = 0; +  	if (!acpi_evalf(hkey_handle, NULL, "SUWB", "vd", status))  		return -EIO; -	if (update_rfk) -		uwb_update_rfk(); -  	return 0;  }  /* --------------------------------------------------------------------- */ -static int tpacpi_uwb_rfk_get(void *data, enum rfkill_state *state) -{ -	int uwbs = uwb_get_radiosw(); - -	if (uwbs < 0) -		return uwbs; - -	*state = uwbs; -	return 0; -} - -static int tpacpi_uwb_rfk_set(void *data, enum rfkill_state state) -{ -	dbg_printk(TPACPI_DBG_RFKILL, -		   "request to change radio state to %d\n", state); -	return uwb_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); -} +static const struct tpacpi_rfk_ops uwb_tprfk_ops = { +	.get_status = uwb_get_status, +	.set_status = uwb_set_status, +};  static void uwb_exit(void)  { -	if (tpacpi_uwb_rfkill) -		rfkill_unregister(tpacpi_uwb_rfkill); +	tpacpi_destroy_rfkill(TPACPI_RFK_UWB_SW_ID);  }  static int __init uwb_init(struct ibm_init_struct *iibm) @@ -3813,13 +3847,10 @@ static int __init uwb_init(struct ibm_init_struct *iibm)  		return 1;  	res = tpacpi_new_rfkill(TPACPI_RFK_UWB_SW_ID, -				&tpacpi_uwb_rfkill, +				&uwb_tprfk_ops,  				RFKILL_TYPE_UWB,  				TPACPI_RFK_UWB_SW_NAME, -				false, -				tpacpi_uwb_rfk_set, -				tpacpi_uwb_rfk_get); - +				false);  	return res;  } diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 4345089f517..81d31ea507d 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -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); +	value = (blocked == false); -	if (hci_get_radio_state(&radio_state) != HCI_SUCCESS) -		return -EFAULT; +	mutex_lock(&dev->mutex); +	if (hci_get_radio_state(&radio_state) != HCI_SUCCESS) { +		err = -EBUSY; +		goto out; +	} -	switch (state) { -	case RFKILL_STATE_UNBLOCKED: -		if (!radio_state) -			return -EPERM; -		break; -	case RFKILL_STATE_SOFT_BLOCKED: -		break; -	default: -		return -EINVAL; +	if (!radio_state) { +		err = 0; +		goto out;  	} -	mutex_lock(&dev->mutex);  	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;  		} diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 3f0eaa397ef..7e09c5c1ed0 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -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 diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index de18ef227e0..090852c8de7 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.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_ops - rfkill driver methods + * + * @poll: poll the rfkill block state(s) -- only assign this method + *	when you need polling. When called, simply call one of the + *	rfkill_set{,_hw,_sw}_state family of functions. If the hw + *	is getting unblocked you need to take into account the return + *	value of those functions to make sure the software block is + *	properly used. + * @query: query the rfkill block state(s) and call exactly one of the + *	rfkill_set{,_hw,_sw}_state family of functions. Assign this + *	method if input events can cause hardware state changes to make + *	the rfkill core query your driver before setting a requested + *	block. + * @set_block: turn the transmitter on (blocked == false) or off + *	(blocked == true) -- this is called only while the transmitter + *	is not hard-blocked, but note that the core's view of whether + *	the transmitter is hard-blocked might differ from your driver's + *	view due to race conditions, so it is possible that it is still + *	called at the same time as you are calling rfkill_set_hw_state(). + *	This callback must be assigned. + */ +struct rfkill_ops { +	void	(*poll)(struct rfkill *rfkill, void *data); +	void	(*query)(struct rfkill *rfkill, void *data); +	int	(*set_block)(void *data, bool blocked);  }; +#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)  /** - * struct rfkill - rfkill control structure. - * @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. + * 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 structure represents a RF switch located on a network device. + * This function should be called by the transmitter driver to allocate an + * rfkill structure. Returns %NULL on failure.   */ -struct rfkill { -	const char *name; -	enum rfkill_type type; +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); -	/* 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); +/** + * 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); -#ifdef CONFIG_RFKILL_LEDS -	struct led_trigger led_trigger; -#endif +/** + * 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); -	struct device dev; -	struct list_head node; -	enum rfkill_state state_for_resume; -}; -#define to_rfkill(d)	container_of(d, struct rfkill, dev) +/** + * 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); -struct rfkill * __must_check rfkill_allocate(struct device *parent, -					     enum rfkill_type type); -void rfkill_free(struct rfkill *rfkill); -int __must_check rfkill_register(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_destroy - free rfkill structure + * @rfkill: rfkill structure to be destroyed + * + * Destroys the rfkill structure. + */ +void rfkill_destroy(struct rfkill *rfkill); + +/** + * rfkill_set_hw_state - Set the internal rfkill hardware block state + * @rfkill: pointer to the rfkill class to modify. + * @state: the current hardware block state to set + * + * rfkill drivers that get events when the hard-blocked state changes + * use this function to notify the rfkill core (and through that also + * userspace) of the current state -- they should also use this after + * resume if the state could have changed. + * + * You need not (but may) call this function if poll_state is assigned. + * + * This function can be called in any context, even from within rfkill + * callbacks. + * + * The function returns the combined block state (true if transmitter + * should be blocked) so that drivers need not keep track of the soft + * block state -- which they might not be able to. + */ +bool __must_check rfkill_set_hw_state(struct rfkill *rfkill, bool blocked); + +/** + * rfkill_set_sw_state - Set the internal rfkill software block state + * @rfkill: pointer to the rfkill class to modify. + * @state: the current software block state to set + * + * rfkill drivers that get events when the soft-blocked state changes + * (yes, some platforms directly act on input but allow changing again) + * use this function to notify the rfkill core (and through that also + * userspace) of the current state -- they should also use this after + * resume if the state could have changed. + * + * This function can be called in any context, even from within rfkill + * callbacks. + * + * The function returns the combined block state (true if transmitter + * should be blocked). + */ +bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked); + +/** + * rfkill_set_states - Set the internal rfkill block states + * @rfkill: pointer to the rfkill class to modify. + * @sw: the current software block state to set + * @hw: the current hardware block state to set + * + * This function can be called in any context, even from within rfkill + * callbacks. + */ +void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw);  /** - * rfkill_state_complement - return complementar state - * @state: state to return the complement of + * 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.   * - * Returns RFKILL_STATE_SOFT_BLOCKED if @state is RFKILL_STATE_UNBLOCKED, - * returns RFKILL_STATE_UNBLOCKED otherwise. + * XXX: instead of ignoring -- how about just updating all currently + *	registered drivers?   */ -static inline enum rfkill_state rfkill_state_complement(enum rfkill_state state) +void rfkill_set_global_sw_state(const enum rfkill_type type, bool blocked); +#else /* !RFKILL */ +static inline struct rfkill * __must_check +rfkill_alloc(const char *name, +	     struct device *parent, +	     const enum rfkill_type type, +	     const struct rfkill_ops *ops, +	     void *ops_data) +{ +	return ERR_PTR(-ENODEV); +} + +static inline int __must_check rfkill_register(struct rfkill *rfkill) +{ +	if (rfkill == ERR_PTR(-ENODEV)) +		return 0; +	return -EINVAL; +} + +static inline void rfkill_pause_polling(struct rfkill *rfkill) +{ +} + +static inline void rfkill_resume_polling(struct rfkill *rfkill)  { -	return (state == RFKILL_STATE_UNBLOCKED) ? -		RFKILL_STATE_SOFT_BLOCKED : RFKILL_STATE_UNBLOCKED;  } +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_name - Get the LED trigger name for the button's LED. + * rfkill_get_led_trigger_name - Get the LED trigger name for the button's LED.   * This function might return a NULL pointer if registering of the - * LED trigger failed. - * Use this as "default_trigger" for the LED. + * LED trigger failed. Use this as "default_trigger" for the LED.   */ -static inline char *rfkill_get_led_name(struct rfkill *rfkill) -{ -#ifdef CONFIG_RFKILL_LEDS -	return (char *)(rfkill->led_trigger.name); +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; -#endif  } +static inline void +rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name) +{ +} +#endif + +#endif /* __KERNEL__ */ +  #endif /* RFKILL_H */ diff --git a/include/net/wimax.h b/include/net/wimax.h index 6b3824edb39..2af7bf839f2 100644 --- a/include/net/wimax.h +++ b/include/net/wimax.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) diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig index 7f807b30cfb..b47f72fae05 100644 --- a/net/rfkill/Kconfig +++ b/net/rfkill/Kconfig @@ -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 diff --git a/net/rfkill/Makefile b/net/rfkill/Makefile index b38c430be05..66210535269 100644 --- a/net/rfkill/Makefile +++ b/net/rfkill/Makefile @@ -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 diff --git a/net/rfkill/core.c b/net/rfkill/core.c new file mode 100644 index 00000000000..30a6f8d819b --- /dev/null +++ b/net/rfkill/core.c @@ -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); diff --git a/net/rfkill/input.c b/net/rfkill/input.c new file mode 100644 index 00000000000..a7295ad5f9c --- /dev/null +++ b/net/rfkill/input.c @@ -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); +} diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c deleted file mode 100644 index 60a34f3b5f6..00000000000 --- a/net/rfkill/rfkill-input.c +++ /dev/null @@ -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); diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c deleted file mode 100644 index 4f5a83183c9..00000000000 --- a/net/rfkill/rfkill.c +++ /dev/null @@ -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); diff --git a/net/rfkill/rfkill-input.h b/net/rfkill/rfkill.h index fe8df6b5b93..d1117cb6e4d 100644 --- a/net/rfkill/rfkill-input.h +++ b/net/rfkill/rfkill.h @@ -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 */ diff --git a/net/wimax/Kconfig b/net/wimax/Kconfig index 1b46747a5f5..0bdbb692820 100644 --- a/net/wimax/Kconfig +++ b/net/wimax/Kconfig @@ -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 diff --git a/net/wimax/op-rfkill.c b/net/wimax/op-rfkill.c index a3616e2ccb8..bb102e4aa3e 100644 --- a/net/wimax/op-rfkill.c +++ b/net/wimax/op-rfkill.c @@ -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; -	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); +	d_printf(1, dev, "rfkill %p\n", rfkill); -	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); +	wimax_dev->rfkill = rfkill; -	/* 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   *  |