2009-03-29 17:53:35

by Johannes Berg

[permalink] [raw]
Subject: [RFC] rfkill: rewrite

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

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

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

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

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

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

* rfkill-input pointlessly is a separate module

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

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

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

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

Signed-off-by: Johannes Berg <[email protected]>
---
RFC only because I haven't fixed all drivers yet, still missing are
toshiba, thinkpad, wimax and tosa.

Let the flames begin! (or if you like it feel free to chime in too)

Documentation/rfkill.txt | 637 +++-------------------
MAINTAINERS | 4
drivers/net/wireless/ath9k/ath9k.h | 7
drivers/net/wireless/ath9k/main.c | 115 ----
drivers/net/wireless/ath9k/pci.c | 15
drivers/net/wireless/b43/Kconfig | 2
drivers/net/wireless/b43/leds.c | 2
drivers/net/wireless/b43/main.c | 4
drivers/net/wireless/b43/phy_a.c | 4
drivers/net/wireless/b43/phy_common.c | 17
drivers/net/wireless/b43/phy_common.h | 4
drivers/net/wireless/b43/phy_g.c | 4
drivers/net/wireless/b43/phy_lp.c | 2
drivers/net/wireless/b43/phy_n.c | 2
drivers/net/wireless/b43/rfkill.c | 60 --
drivers/net/wireless/b43/rfkill.h | 2
drivers/net/wireless/b43legacy/Kconfig | 2
drivers/net/wireless/b43legacy/leds.c | 3
drivers/net/wireless/b43legacy/rfkill.c | 55 -
drivers/net/wireless/b43legacy/rfkill.h | 2
drivers/net/wireless/iwlwifi/Kconfig | 5
drivers/net/wireless/iwlwifi/iwl-rfkill.c | 70 --
drivers/platform/x86/Kconfig | 6
drivers/platform/x86/acer-wmi.c | 53 -
drivers/platform/x86/dell-laptop.c | 103 +--
drivers/platform/x86/eeepc-laptop.c | 91 ---
drivers/platform/x86/hp-wmi.c | 104 +--
drivers/platform/x86/toshiba_acpi.c | 28
include/linux/Kbuild | 1
include/linux/rfkill.h | 290 +++++++---
net/rfkill/Kconfig | 6
net/rfkill/Makefile | 6
net/rfkill/core.c | 754 ++++++++++++++++++++++++++
net/rfkill/input.c | 384 +++++++++++++
net/rfkill/rfkill-input.c | 392 -------------
net/rfkill/rfkill-input.h | 21
net/rfkill/rfkill.c | 843 ------------------------------
net/rfkill/rfkill.h | 27
38 files changed, 1763 insertions(+), 2364 deletions(-)

--- wireless-testing.orig/include/linux/rfkill.h 2009-03-28 20:54:23.000000000 +0100
+++ wireless-testing/include/linux/rfkill.h 2009-03-29 18:59:51.000000000 +0200
@@ -4,6 +4,7 @@
/*
* Copyright (C) 2006 - 2007 Ivo van Doorn
* Copyright (C) 2007 Dmitry Torokhov
+ * Copyright 2009 Johannes Berg <[email protected]>
*
* 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,11 +49,13 @@

/**
* 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 ,
@@ -42,97 +63,226 @@ enum rfkill_type {
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 */
-};
+#define RFKILL_BLOCK_HW_BIT 0
+#define RFKILL_BLOCK_SW_BIT 1

/**
- * 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.
- *
- * This structure represents a RF switch located on a network device.
- */
-struct rfkill {
- const char *name;
- enum rfkill_type type;
-
- /* the mutex serializes callbacks and also protects
- * the state */
- struct mutex mutex;
- enum rfkill_state state;
- void *data;
- int (*toggle_radio)(void *data, enum rfkill_state state);
- int (*get_state)(void *data, enum rfkill_state *state);
-
-#ifdef CONFIG_RFKILL_LEDS
- struct led_trigger led_trigger;
+ * struct rfkill_ops - rfkill driver methods
+ *
+ * @poll_hw_block: poll the rfkill hardware block state (return true
+ * for blocked) -- only assign this method when you cannot
+ * do without polling
+ * @query_state: query the rfkill hardware block state (return true
+ * for blocked) -- assign this method if software events might
+ * cause hardware state changes
+ * @set_block: turn the transmitter on (blocked == false) or off
+ * (blocked == true) -- this is called only while the transmitter
+ * is not hard-blocked. This must be assigned.
+ */
+struct rfkill_ops {
+#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
+ bool (*poll_hw_block)(void *data);
+ bool (*query_state)(void *data);
+ void (*set_block)(void *data, bool blocked);
#endif
-
- struct device dev;
- struct list_head node;
- enum rfkill_state state_for_resume;
};
-#define to_rfkill(d) container_of(d, struct rfkill, dev)

-struct rfkill * __must_check rfkill_allocate(struct device *parent,
- enum rfkill_type type);
-void rfkill_free(struct rfkill *rfkill);
+/* this is opaque */
+struct rfkill;
+
+#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
+/**
+ * rfkill_alloc - allocate rfkill structure
+ * @name: name of the struct -- the string is not copied internally
+ * @parent: device that has rf switch on it
+ * @type: type of the switch (RFKILL_TYPE_*)
+ * @ops: rfkill methods
+ * @ops_data: data passed to each method
+ *
+ * This function should be called by the transmitter driver to allocate an
+ * rfkill structure.
+ */
+struct rfkill * __must_check rfkill_alloc(const char *name,
+ struct device *parent,
+ const enum rfkill_type type,
+ const struct rfkill_ops *ops,
+ void *ops_data);
+
+/**
+ * rfkill_register - Register a rfkill structure.
+ * @rfkill: rfkill structure to be registered
+ *
+ * This function should be called by the network driver when the rfkill
+ * structure needs to be registered. Before calling this function the
+ * driver needs to be ready to service method calls from rfkill.
+ */
int __must_check rfkill_register(struct rfkill *rfkill);
+
+/**
+ * rfkill_pause_polling(struct rfkill *rfkill)
+ *
+ * Pause polling -- say transmitter is off for other reasons.
+ * NOTE: not necessary for suspend/resume -- in that case the
+ * core stops polling anyway
+ */
+void rfkill_pause_polling(struct rfkill *rfkill);
+
+/**
+ * rfkill_resume_polling(struct rfkill *rfkill)
+ *
+ * Pause polling -- say transmitter is off for other reasons.
+ * NOTE: not necessary for suspend/resume -- in that case the
+ * core stops polling anyway
+ */
+void rfkill_resume_polling(struct rfkill *rfkill);
+
+
+/**
+ * rfkill_unregister - Unregister a rfkill structure.
+ * @rfkill: rfkill structure to be unregistered
+ *
+ * This function should be called by the network driver during device
+ * teardown to destroy rfkill structure. Until it returns, the driver
+ * needs to be able to service method calls.
+ */
void rfkill_unregister(struct rfkill *rfkill);

-int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state);
-int rfkill_set_default(enum rfkill_type type, enum rfkill_state state);
+/**
+ * rfkill_destroy - free rfkill structure
+ * @rfkill: rfkill structure to be destroyed
+ *
+ * Destroys the rfkill structure.
+ */
+void rfkill_destroy(struct rfkill *rfkill);

/**
- * rfkill_state_complement - return complementar state
- * @state: state to return the complement of
+ * 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.
*
- * Returns RFKILL_STATE_SOFT_BLOCKED if @state is RFKILL_STATE_UNBLOCKED,
- * returns RFKILL_STATE_UNBLOCKED otherwise.
+ * You need not (but may) call this function if poll_state is assigned.
+ *
+ * This function can be called in any context.
+ *
+ * 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 cannot anyway because while hard-blocked
+ * they do not receive soft-block events.
*/
-static inline enum rfkill_state rfkill_state_complement(enum rfkill_state state)
+bool 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.
+ *
+ * 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_sw_default - set initial value for a switch type
+ * @type - the type of switch to set the default state of
+ * @blocked - the new default state for that group of switches
+ *
+ * Sets the initial state rfkill should use for a given type.
+ *
+ * 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_sw_default(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;
+}
+#endif /* RFKILL || RFKILL_MODULE */
+
+
+
/**
* rfkill_get_led_name - Get the LED trigger name for the button's LED.
* This function might return a NULL pointer if registering of the
* LED trigger failed.
* Use this as "default_trigger" for the LED.
*/
-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_name(struct rfkill *rfkill);
#else
+static inline const char *rfkill_get_led_name(struct rfkill *rfkill)
+{
return NULL;
-#endif
}
+#endif
+
+#endif /* __KERNEL__ */

#endif /* RFKILL_H */
--- wireless-testing.orig/drivers/net/wireless/ath9k/main.c 2009-03-28 20:54:23.000000000 +0100
+++ wireless-testing/drivers/net/wireless/ath9k/main.c 2009-03-29 10:38:53.000000000 +0200
@@ -1181,120 +1181,67 @@ static bool ath_is_rfkill_set(struct ath
ah->rfkill_polarity;
}

-/* h/w rfkill poll function */
-static void ath_rfkill_poll(struct work_struct *work)
+/* s/w rfkill handlers */
+static void 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;
-
- rfkill_force_state(sc->rf_kill.rfkill, state);
- }
+ struct ath_softc *sc = data;

- queue_delayed_work(sc->hw->workqueue, &sc->rf_kill.rfkill_poll,
- msecs_to_jiffies(ATH_RFKILL_POLL_INTERVAL));
+ if (blocked)
+ ath_radio_disable(sc);
+ else
+ ath_radio_enable(sc);
}

-/* s/w rfkill handler */
-static int ath_sw_toggle_radio(void *data, enum rfkill_state state)
+static bool ath_rfkill_poll_state(void *data)
{
struct ath_softc *sc = data;
+ bool ret = !!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 (ret)
+ ath_radio_disable(sc);
+
+ return ret;
}

/* 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_state = 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);
@@ -1655,10 +1602,6 @@ int ath_attach(u16 devid, struct ath_sof
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)
@@ -2170,10 +2113,8 @@ static void ath9k_stop(struct ieee80211_
} 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);
--- wireless-testing.orig/drivers/platform/x86/eeepc-laptop.c 2009-03-28 20:54:23.000000000 +0100
+++ wireless-testing/drivers/platform/x86/eeepc-laptop.c 2009-03-29 17:30:22.000000000 +0200
@@ -296,39 +296,15 @@ static int update_bl_status(struct backl
* Rfkill helpers
*/

-static int eeepc_wlan_rfkill_set(void *data, enum rfkill_state state)
+static void eeepc_rfkill_set(void *data, bool blocked)
{
- if (state == RFKILL_STATE_SOFT_BLOCKED)
- return set_acpi(CM_ASL_WLAN, 0);
- else
- return set_acpi(CM_ASL_WLAN, 1);
-}
-
-static int eeepc_wlan_rfkill_state(void *data, enum rfkill_state *state)
-{
- if (get_acpi(CM_ASL_WLAN) == 1)
- *state = RFKILL_STATE_UNBLOCKED;
- else
- *state = RFKILL_STATE_SOFT_BLOCKED;
- return 0;
+ unsigned long asl = (unsigned long)data;
+ set_acpi(asl, !blocked);
}

-static int eeepc_bluetooth_rfkill_set(void *data, enum rfkill_state state)
-{
- if (state == RFKILL_STATE_SOFT_BLOCKED)
- return set_acpi(CM_ASL_BLUETOOTH, 0);
- else
- return set_acpi(CM_ASL_BLUETOOTH, 1);
-}
-
-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 struct rfkill_ops eeepc_rfkill_ops = {
+ .set_block = eeepc_rfkill_set,
+};

/*
* Sys helpers
@@ -650,26 +626,17 @@ static int eeepc_hotk_add(struct acpi_de
printk(EEEPC_ERR "Error installing notify handler\n");

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_sw_state(ehotk->eeepc_wlan_rfkill,
+ get_acpi(CM_ASL_WLAN) != 1);
result = rfkill_register(ehotk->eeepc_wlan_rfkill);
if (result)
goto wlan_fail;
@@ -677,28 +644,17 @@ static int eeepc_hotk_add(struct acpi_de

if (get_acpi(CM_ASL_BLUETOOTH) != -1) {
ehotk->eeepc_bluetooth_rfkill =
- rfkill_allocate(&device->dev, RFKILL_TYPE_BLUETOOTH);
+ rfkill_alloc("eeepc-wlan",
+ &device->dev,
+ RFKILL_TYPE_WLAN,
+ &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_sw_state(ehotk->eeepc_bluetooth_rfkill,
+ get_acpi(CM_ASL_BLUETOOTH) != 1);
result = rfkill_register(ehotk->eeepc_bluetooth_rfkill);
if (result)
goto bluetooth_fail;
@@ -710,13 +666,10 @@ static int eeepc_hotk_add(struct acpi_de
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);
ehotk_fail:
kfree(ehotk);
ehotk = NULL;
--- wireless-testing.orig/net/rfkill/rfkill.c 2009-03-28 20:54:22.000000000 +0100
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,843 +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 <[email protected]>");
-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);
-}
-#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_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);
-
- 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_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);
-}
-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);
-
- 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);
- }
-
- 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);
--- wireless-testing.orig/net/rfkill/rfkill-input.h 2009-03-28 20:54:22.000000000 +0100
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2007 Ivo van Doorn
- */
-
-/*
- * 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.
- */
-
-#ifndef __RFKILL_INPUT_H
-#define __RFKILL_INPUT_H
-
-void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state);
-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);
-
-#endif /* __RFKILL_INPUT_H */
--- wireless-testing.orig/net/rfkill/rfkill-input.c 2009-03-28 20:54:22.000000000 +0100
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,392 +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.
- *
- * If you ever run into a situation in which you have a SW_ type rfkill
- * input device, then you can revive code that was removed in the patch
- * "rfkill-input: remove unused code".
- */
-
-#include <linux/module.h>
-#include <linux/input.h>
-#include <linux/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 <[email protected]>");
-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);
--- wireless-testing.orig/drivers/net/wireless/b43/main.c 2009-03-28 20:54:23.000000000 +0100
+++ wireless-testing/drivers/net/wireless/b43/main.c 2009-03-28 20:54:23.000000000 +0100
@@ -3492,7 +3492,7 @@ static int b43_op_config(struct ieee8021

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 "
@@ -3500,7 +3500,7 @@ static int b43_op_config(struct ieee8021
"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");
}
}
--- wireless-testing.orig/drivers/net/wireless/b43/phy_a.c 2009-03-28 20:54:22.000000000 +0100
+++ wireless-testing/drivers/net/wireless/b43/phy_a.c 2009-03-28 20:54:23.000000000 +0100
@@ -480,11 +480,11 @@ static bool b43_aphy_op_supports_hwpctl(
}

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);
--- wireless-testing.orig/drivers/net/wireless/b43/phy_common.c 2009-03-28 20:54:22.000000000 +0100
+++ wireless-testing/drivers/net/wireless/b43/phy_common.c 2009-03-28 20:54:23.000000000 +0100
@@ -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);
}
@@ -287,18 +287,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);
}

--- wireless-testing.orig/drivers/net/wireless/b43/phy_common.h 2009-03-28 20:54:22.000000000 +0100
+++ wireless-testing/drivers/net/wireless/b43/phy_common.h 2009-03-28 20:54:23.000000000 +0100
@@ -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);
@@ -362,7 +362,7 @@ int b43_switch_channel(struct b43_wldev
/**
* 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.
--- wireless-testing.orig/drivers/net/wireless/b43/phy_g.c 2009-03-28 20:54:23.000000000 +0100
+++ wireless-testing/drivers/net/wireless/b43/phy_g.c 2009-03-28 20:54:23.000000000 +0100
@@ -2592,7 +2592,7 @@ static bool b43_gphy_op_supports_hwpctl(
}

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(

might_sleep();

- if (state == RFKILL_STATE_UNBLOCKED) {
+ if (!blocked) {
/* Turn radio ON */
if (phy->radio_on)
return;
--- wireless-testing.orig/drivers/net/wireless/b43/phy_n.c 2009-03-28 20:54:23.000000000 +0100
+++ wireless-testing/drivers/net/wireless/b43/phy_n.c 2009-03-28 20:54:23.000000000 +0100
@@ -579,7 +579,7 @@ static void b43_nphy_op_radio_write(stru
}

static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
- enum rfkill_state state)
+ bool blocked)
{//TODO
}

--- wireless-testing.orig/drivers/net/wireless/b43/rfkill.c 2009-03-28 20:54:22.000000000 +0100
+++ wireless-testing/drivers/net/wireless/b43/rfkill.c 2009-03-29 11:15:45.000000000 +0200
@@ -75,45 +75,29 @@ static void b43_rfkill_poll(struct input
}

/* Called when the RFKILL toggled in software. */
-static int b43_rfkill_soft_toggle(void *data, enum rfkill_state state)
+static void b43_rfkill_soft_set(void *data, bool blocked)
{
struct b43_wldev *dev = data;
struct b43_wl *wl = dev->wl;
- int err = -EBUSY;

- if (!wl->rfkill.registered)
- return 0;
+ if (WARN_ON(!wl->rfkill.registered))
+ return;

mutex_lock(&wl->mutex);
+
if (b43_status(dev) < B43_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)
- 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;
- }
+
+ if (WARN_ON(!dev->radio_hw_enable && !blocked))
+ goto out_unlock;
+
+ if (!blocked != dev->phy.radio_on)
+ b43_software_rfkill(dev, blocked);
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);

@@ -122,6 +106,10 @@ char *b43_rfkill_led_name(struct b43_wld
return rfkill_get_led_name(rfk->rfkill);
}

+static struct rfkill_ops b43_rfkill_ops = {
+ .set_block = b43_rfkill_soft_set,
+};
+
void b43_rfkill_init(struct b43_wldev *dev)
{
struct b43_wl *wl = dev->wl;
@@ -130,19 +118,19 @@ void b43_rfkill_init(struct b43_wldev *d

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->rfkill = rfkill_alloc(rfk->name,
+ dev->dev->dev,
+ RFKILL_TYPE_WLAN,
+ &b43_rfkill_ops, dev);
+ if (!rfk->rfkill)
+ goto out_error;

rfk->poll_dev = input_allocate_polled_device();
if (!rfk->poll_dev) {
- rfkill_free(rfk->rfkill);
+ rfkill_destroy(rfk->rfkill);
goto err_freed_rfk;
}

@@ -183,6 +171,7 @@ void b43_rfkill_init(struct b43_wldev *d
return;
err_unreg_rfk:
rfkill_unregister(rfk->rfkill);
+ rfkill_destroy(rfk->rfkill);
err_free_polldev:
input_free_polled_device(rfk->poll_dev);
rfk->poll_dev = NULL;
@@ -203,6 +192,7 @@ void b43_rfkill_exit(struct b43_wldev *d

input_unregister_polled_device(rfk->poll_dev);
rfkill_unregister(rfk->rfkill);
+ rfkill_destroy(rfk->rfkill);
input_free_polled_device(rfk->poll_dev);
rfk->poll_dev = NULL;
rfk->rfkill = NULL;
--- wireless-testing.orig/drivers/net/wireless/b43legacy/rfkill.c 2009-03-28 20:54:23.000000000 +0100
+++ wireless-testing/drivers/net/wireless/b43legacy/rfkill.c 2009-03-29 11:18:30.000000000 +0200
@@ -76,47 +76,33 @@ static void b43legacy_rfkill_poll(struct

/* 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 void b43legacy_rfkill_soft_set(void *data, bool blocked)
{
struct b43legacy_wldev *dev = data;
struct b43legacy_wl *wl = dev->wl;
- int err = -EBUSY;

if (!wl->rfkill.registered)
- return 0;
+ return;

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 (WARN_ON(!blocked && !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;
}

out_unlock:
mutex_unlock(&wl->mutex);
-
- return err;
}

-char *b43legacy_rfkill_led_name(struct b43legacy_wldev *dev)
+const char *b43legacy_rfkill_led_name(struct b43legacy_wldev *dev)
{
struct b43legacy_rfkill *rfk = &(dev->wl->rfkill);

@@ -125,6 +111,10 @@ char *b43legacy_rfkill_led_name(struct b
return rfkill_get_led_name(rfk->rfkill);
}

+static struct rfkill_ops b43legacy_rfkill_ops = {
+ .set_block = b43legacy_rfkill_soft_set,
+};
+
void b43legacy_rfkill_init(struct b43legacy_wldev *dev)
{
struct b43legacy_wl *wl = dev->wl;
@@ -133,19 +123,18 @@ void b43legacy_rfkill_init(struct b43leg

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->rfkill = rfkill_alloc(rfk->name,
+ dev->dev->dev,
+ RFKILL_TYPE_WLAN,
+ &b43legacy_rfkill_ops, dev);
+ if (!rfk->rfkill)
+ goto out_error;

rfk->poll_dev = input_allocate_polled_device();
if (!rfk->poll_dev) {
- rfkill_free(rfk->rfkill);
+ rfkill_destroy(rfk->rfkill);
goto err_freed_rfk;
}

@@ -181,6 +170,7 @@ void b43legacy_rfkill_init(struct b43leg
return;
err_unreg_rfk:
rfkill_unregister(rfk->rfkill);
+ rfkill_destroy(rfk->rfkill);
err_free_polldev:
input_free_polled_device(rfk->poll_dev);
rfk->poll_dev = NULL;
@@ -201,6 +191,7 @@ void b43legacy_rfkill_exit(struct b43leg

input_unregister_polled_device(rfk->poll_dev);
rfkill_unregister(rfk->rfkill);
+ rfkill_destroy(rfk->rfkill);
input_free_polled_device(rfk->poll_dev);
rfk->poll_dev = NULL;
rfk->rfkill = NULL;
--- wireless-testing.orig/drivers/net/wireless/iwlwifi/iwl-rfkill.c 2009-03-28 20:54:23.000000000 +0100
+++ wireless-testing/drivers/net/wireless/iwlwifi/iwl-rfkill.c 2009-03-29 11:41:14.000000000 +0200
@@ -36,42 +36,36 @@
#include "iwl-core.h"

/* software rf-kill from user */
-static int iwl_rfkill_soft_rf_kill(void *data, enum rfkill_state state)
+static void iwl_rfkill_soft_rf_kill(void *data, bool blocked)
{
struct iwl_priv *priv = data;
- int err = 0;

if (!priv->rfkill)
- return 0;
+ return;

if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return 0;
+ return;
+
+ IWL_DEBUG_RF_KILL(priv, "received soft RFKILL: block=%d\n", blocked);

- IWL_DEBUG_RF_KILL(priv, "we received soft RFKILL set to state %d\n", state);
mutex_lock(&priv->mutex);

- switch (state) {
- case RFKILL_STATE_UNBLOCKED:
- if (iwl_is_rfkill_hw(priv)) {
- err = -EBUSY;
- goto out_unlock;
- }
+ if (WARN_ON(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;
}

+static 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 +74,16 @@ int iwl_rfkill_init(struct iwl_priv *pri
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 +91,10 @@ int iwl_rfkill_init(struct iwl_priv *pri
}

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 +106,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 +121,10 @@ void iwl_rfkill_set_hw_state(struct iwl_
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);
--- wireless-testing.orig/Documentation/rfkill.txt 2009-03-28 20:54:22.000000000 +0100
+++ wireless-testing/Documentation/rfkill.txt 2009-03-29 11:34:37.000000000 +0200
@@ -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:
-
-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.
-
-Note that disabling the signal output means that the the transmitter is to be
-made to not emit any energy when "blocked". rfkill is not about blocking data
-transmissions, it is about blocking energy emission.
-
-The rfkill subsystem offers support for keys and switches often found on
-laptops to enable wireless devices like WiFi and Bluetooth, so that these keys
-and switches actually perform an action in all wireless devices of a given type
-attached to the system.
-
-The buttons to enable and disable the wireless transmitters are important in
-situations where the user is for example using his laptop on a location where
-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.
+1. Introduction
+2. Implementation details
+3. Kernel driver guidelines
+4. Kernel API
+5. Userspace support

-===============================================================================
-2: Implementation details
+
+1. Introduction
+
+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.
+
+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.
+
+
+
+2. Implementation details

The rfkill subsystem is composed of various components: the rfkill class, the
rfkill-input module (an input layer handler), and some specific input layer
events.

-The rfkill class provides kernel drivers with an interface that allows them to
-know when they should enable or disable a wireless network device transmitter.
-This is enabled by the CONFIG_RFKILL Kconfig option.
-
-The rfkill class support makes sure userspace will be notified of all state
-changes on rfkill devices through uevents. It provides a notification chain
-for interested parties in the kernel to also get notified of rfkill state
-changes in other drivers. It creates several sysfs entries which can be used
-by userspace. See section "Userspace support".
-
-The rfkill-input module provides the kernel with the ability to implement a
-basic response when the user presses a key or button (or toggles a switch)
-related to rfkill functionality. It is an in-kernel implementation of default
-policy of reacting to rfkill-related input events and neither mandatory nor
-required for wireless drivers to operate. It is enabled by the
-CONFIG_RFKILL_INPUT Kconfig option.
-
-rfkill-input is a rfkill-related events input layer handler. This handler will
-listen to all rfkill key events and will change the rfkill state of the
-wireless devices accordingly. With this option enabled userspace could either
-do nothing or simply perform monitoring tasks.
-
-The rfkill-input module also provides EPO (emergency power-off) functionality
-for all wireless transmitters. This function cannot be overridden, and it is
-always active. rfkill EPO is related to *_RFKILL_ALL input layer events.
-
-
-Important terms for the rfkill subsystem:
-
-In order to avoid confusion, we avoid the term "switch" in rfkill when it is
-referring to an electronic control circuit that enables or disables a
-transmitter. We reserve it for the physical device a human manipulates
-(which is an input device, by the way):
-
-rfkill switch:
-
- A physical device a human manipulates. Its state can be perceived by
- the kernel either directly (through a GPIO pin, ACPI GPE) or by its
- effect on a rfkill line of a wireless device.
-
-rfkill controller:
-
- A hardware circuit that controls the state of a rfkill line, which a
- kernel driver can interact with *to modify* that state (i.e. it has
- either write-only or read/write access).
-
-rfkill line:
-
- An input channel (hardware or software) of a wireless device, which
- causes a wireless transmitter to stop emitting energy (BLOCK) when it
- is active. Point of view is extremely important here: rfkill lines are
- always seen from the PoV of a wireless device (and its driver).
-
-soft rfkill line/software rfkill line:
-
- A rfkill line the wireless device driver can directly change the state
- of. Related to rfkill_state RFKILL_STATE_SOFT_BLOCKED.
-
-hard rfkill line/hardware rfkill line:
-
- A rfkill line that works fully in hardware or firmware, and that cannot
- be overridden by the kernel driver. The hardware device or the
- firmware just exports its status to the driver, but it is read-only.
- Related to rfkill_state RFKILL_STATE_HARD_BLOCKED.
-
-The enum rfkill_state describes the rfkill state of a transmitter:
-
-When a rfkill line or rfkill controller is in the RFKILL_STATE_UNBLOCKED state,
-the wireless transmitter (radio TX circuit for example) is *enabled*. When the
-it is in the RFKILL_STATE_SOFT_BLOCKED or RFKILL_STATE_HARD_BLOCKED, the
-wireless transmitter is to be *blocked* from operating.
-
-RFKILL_STATE_SOFT_BLOCKED indicates that a call to toggle_radio() can change
-that state. RFKILL_STATE_HARD_BLOCKED indicates that a call to toggle_radio()
-will not be able to change the state and will return with a suitable error if
-attempts are made to set the state to RFKILL_STATE_UNBLOCKED.
-
-RFKILL_STATE_HARD_BLOCKED is used by drivers to signal that the device is
-locked in the BLOCKED state by a hardwire rfkill line (typically an input pin
-that, when active, forces the transmitter to be disabled) which the driver
-CANNOT override.
-
-Full rfkill functionality requires two different subsystems to cooperate: the
-input layer and the rfkill class. The input layer issues *commands* to the
-entire system requesting that devices registered to the rfkill class change
-state. The way this interaction happens is not complex, but it is not obvious
-either:
-
-Kernel Input layer:
-
- * Generates KEY_WWAN, KEY_WLAN, KEY_BLUETOOTH, SW_RFKILL_ALL, and
- other such events when the user presses certain keys, buttons, or
- toggles certain physical switches.
-
- THE INPUT LAYER IS NEVER USED TO PROPAGATE STATUS, NOTIFICATIONS OR THE
- KIND OF STUFF AN ON-SCREEN-DISPLAY APPLICATION WOULD REPORT. It is
- used to issue *commands* for the system to change behaviour, and these
- commands may or may not be carried out by some kernel driver or
- userspace application. It follows that doing user feedback based only
- on input events is broken, as there is no guarantee that an input event
- will be acted upon.
-
- Most wireless communication device drivers implementing rfkill
- functionality MUST NOT generate these events, and have no reason to
- register themselves with the input layer. Doing otherwise is a common
- misconception. There is an API to propagate rfkill status change
- information, and it is NOT the input layer.
-
-rfkill class:
-
- * Calls a hook in a driver to effectively change the wireless
- transmitter state;
- * Keeps track of the wireless transmitter state (with help from
- the driver);
- * Generates userspace notifications (uevents) and a call to a
- notification chain (kernel) when there is a wireless transmitter
- state change;
- * Connects a wireless communications driver with the common rfkill
- control system, which, for example, allows actions such as
- "switch all bluetooth devices offline" to be carried out by
- userspace or by rfkill-input.
-
- THE RFKILL CLASS NEVER ISSUES INPUT EVENTS. THE RFKILL CLASS DOES
- NOT LISTEN TO INPUT EVENTS. NO DRIVER USING THE RFKILL CLASS SHALL
- EVER LISTEN TO, OR ACT ON RFKILL INPUT EVENTS. Doing otherwise is
- a layering violation.
-
- Most wireless data communication drivers in the kernel have just to
- implement the rfkill class API to work properly. Interfacing to the
- input layer is not often required (and is very often a *bug*) on
- wireless drivers.
-
- Platform drivers often have to attach to the input layer to *issue*
- (but never to listen to) rfkill events for rfkill switches, and also to
- the rfkill class to export a control interface for the platform rfkill
- controllers to the rfkill subsystem. This does NOT mean the rfkill
- switch is attached to a rfkill class (doing so is almost always wrong).
- It just means the same kernel module is the driver for different
- devices (rfkill switches and rfkill controllers).
-
-
-Userspace input handlers (uevents) or kernel input handlers (rfkill-input):
-
- * Implements the policy of what should happen when one of the input
- layer events related to rfkill operation is received.
- * Uses the sysfs interface (userspace) or private rfkill API calls
- to tell the devices registered with the rfkill class to change
- their state (i.e. translates the input layer event into real
- action).
-
- * rfkill-input implements EPO by handling EV_SW SW_RFKILL_ALL 0
- (power off all transmitters) in a special way: it ignores any
- overrides and local state cache and forces all transmitters to the
- RFKILL_STATE_SOFT_BLOCKED state (including those which are already
- supposed to be BLOCKED).
- * rfkill EPO will remain active until rfkill-input receives an
- EV_SW SW_RFKILL_ALL 1 event. While the EPO is active, transmitters
- are locked in the blocked state (rfkill will refuse to unblock them).
- * rfkill-input implements different policies that the user can
- select for handling EV_SW SW_RFKILL_ALL 1. It will unlock rfkill,
- and either do nothing (leave transmitters blocked, but now unlocked),
- restore the transmitters to their state before the EPO, or unblock
- them all.
-
-Userspace uevent handler or kernel platform-specific drivers hooked to the
-rfkill notifier chain:
-
- * Taps into the rfkill notifier chain or to KOBJ_CHANGE uevents,
- in order to know when a device that is registered with the rfkill
- class changes state;
- * Issues feedback notifications to the user;
- * In the rare platforms where this is required, synthesizes an input
- event to command all *OTHER* rfkill devices to also change their
- statues when a specific rfkill device changes state.
-
-
-===============================================================================
-3: Kernel driver guidelines
-
-Remember: point-of-view is everything for a driver that connects to the rfkill
-subsystem. All the details below must be measured/perceived from the point of
-view of the specific driver being modified.
-
-The first thing one needs to know is whether his driver should be talking to
-the rfkill class or to the input layer. In rare cases (platform drivers), it
-could happen that you need to do both, as platform drivers often handle a
-variety of devices in the same driver.
-
-Do not mistake input devices for rfkill controllers. The only type of "rfkill
-switch" device that is to be registered with the rfkill class are those
-directly controlling the circuits that cause a wireless transmitter to stop
-working (or the software equivalent of them), i.e. what we call a rfkill
-controller. Every other kind of "rfkill switch" is just an input device and
-MUST NOT be registered with the rfkill class.
-
-A driver should register a device with the rfkill class when ALL of the
-following conditions are met (they define a rfkill controller):
-
-1. The device is/controls a data communications wireless transmitter;
-
-2. The kernel can interact with the hardware/firmware to CHANGE the wireless
- transmitter state (block/unblock TX operation);
-
-3. The transmitter can be made to not emit any energy when "blocked":
- rfkill is not about blocking data transmissions, it is about blocking
- energy emission;
-
-A driver should register a device with the input subsystem to issue
-rfkill-related events (KEY_WLAN, KEY_BLUETOOTH, KEY_WWAN, KEY_WIMAX,
-SW_RFKILL_ALL, etc) when ALL of the folowing conditions are met:
-
-1. It is directly related to some physical device the user interacts with, to
- command the O.S./firmware/hardware to enable/disable a data communications
- wireless transmitter.
-
- Examples of the physical device are: buttons, keys and switches the user
- will press/touch/slide/switch to enable or disable the wireless
- communication device.
-
-2. It is NOT slaved to another device, i.e. there is no other device that
- issues rfkill-related input events in preference to this one.
-
- Please refer to the corner cases and examples section for more details.
-
-When in doubt, do not issue input events. For drivers that should generate
-input events in some platforms, but not in others (e.g. b43), the best solution
-is to NEVER generate input events in the first place. That work should be
-deferred to a platform-specific kernel module (which will know when to generate
-events through the rfkill notifier chain) or to userspace. This avoids the
-usual maintenance problems with DMI whitelisting.
-
-
-Corner cases and examples:
-====================================
-
-1. If the device is an input device that, because of hardware or firmware,
-causes wireless transmitters to be blocked regardless of the kernel's will, it
-is still just an input device, and NOT to be registered with the rfkill class.
-
-2. If the wireless transmitter switch control is read-only, it is an input
-device and not to be registered with the rfkill class (and maybe not to be made
-an input layer event source either, see below).
-
-3. If there is some other device driver *closer* to the actual hardware the
-user interacted with (the button/switch/key) to issue an input event, THAT is
-the device driver that should be issuing input events.
-
-E.g:
- [RFKILL slider switch] -- [GPIO hardware] -- [WLAN card rf-kill input]
- (platform driver) (wireless card driver)
-
-The user is closer to the RFKILL slide switch plaform driver, so the driver
-which must issue input events is the platform driver looking at the GPIO
-hardware, and NEVER the wireless card driver (which is just a slave). It is
-very likely that there are other leaves than just the WLAN card rf-kill input
-(e.g. a bluetooth card, etc)...
-
-On the other hand, some embedded devices do this:
-
- [RFKILL slider switch] -- [WLAN card rf-kill input]
- (wireless card driver)
-
-In this situation, the wireless card driver *could* register itself as an input
-device and issue rf-kill related input events... but in order to AVOID the need
-for DMI whitelisting, the wireless card driver does NOT do it. Userspace (HAL)
-or a platform driver (that exists only on these embedded devices) will do the
-dirty job of issuing the input events.
-
-
-COMMON MISTAKES in kernel drivers, related to rfkill:
-====================================
-
-1. NEVER confuse input device keys and buttons with input device switches.
-
- 1a. Switches are always set or reset. They report the current state
- (on position or off position).
-
- 1b. Keys and buttons are either in the pressed or not-pressed state, and
- that's it. A "button" that latches down when you press it, and
- unlatches when you press it again is in fact a switch as far as input
- devices go.
-
-Add the SW_* events you need for switches, do NOT try to emulate a button using
-KEY_* events just because there is no such SW_* event yet. Do NOT try to use,
-for example, KEY_BLUETOOTH when you should be using SW_BLUETOOTH instead.
-
-2. Input device switches (sources of EV_SW events) DO store their current state
-(so you *must* initialize it by issuing a gratuitous input layer event on
-driver start-up and also when resuming from sleep), and that state CAN be
-queried from userspace through IOCTLs. There is no sysfs interface for this,
-but that doesn't mean you should break things trying to hook it to the rfkill
-class to get a sysfs interface :-)
-
-3. Do not issue *_RFKILL_ALL events by default, unless you are sure it is the
-correct event for your switch/button. These events are emergency power-off
-events when they are trying to turn the transmitters off. An example of an
-input device which SHOULD generate *_RFKILL_ALL events is the wireless-kill
-switch in a laptop which is NOT a hotkey, but a real sliding/rocker switch.
-An example of an input device which SHOULD NOT generate *_RFKILL_ALL events by
-default, is any sort of hot key that is type-specific (e.g. the one for WLAN).
-
-
-3.1 Guidelines for wireless device drivers
-------------------------------------------
-
-(in this text, rfkill->foo means the foo field of struct rfkill).
-
-1. Each independent transmitter in a wireless device (usually there is only one
-transmitter per device) should have a SINGLE rfkill class attached to it.
-
-2. If the device does not have any sort of hardware assistance to allow the
-driver to rfkill the device, the driver should emulate it by taking all actions
-required to silence the transmitter.
-
-3. If it is impossible to silence the transmitter (i.e. it still emits energy,
-even if it is just in brief pulses, when there is no data to transmit and there
-is no hardware support to turn it off) do NOT lie to the users. Do not attach
-it to a rfkill class. The rfkill subsystem does not deal with data
-transmission, it deals with energy emission. If the transmitter is emitting
-energy, it is not blocked in rfkill terms.
-
-4. It doesn't matter if the device has multiple rfkill input lines affecting
-the same transmitter, their combined state is to be exported as a single state
-per transmitter (see rule 1).
-
-This rule exists because users of the rfkill subsystem expect to get (and set,
-when possible) the overall transmitter rfkill state, not of a particular rfkill
-line.
-
-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.
+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".

-3. Deactivates the soft rfkill line if none of the hardware rfkill lines are
-active and the caller asked for RFKILL_STATE_UNBLOCKED.
+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.

-===============================================================================
-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.
+The rfkill-input code implements a basic response to rfkill buttons -- it
+implements turning on/off all devices of a certain class (or all).

-The 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).
+When the device is hard-blocked (either by a call to rfkill_set_hw_state()
+or from query_state) then set_block() will not be invoked. When the device
+is hard-unblocked again, rfkill drivers need to take into account the return
+value from rfkill_set_hw_state() -- it indicates whether the device is still
+soft-blocked or not.

-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.
+The entire functionality is spread over more than one subsystem:

-===============================================================================
-5: Userspace support
+ * The kernel input layer generates KEY_WWAN, KEY_WLAN etc. and
+ SW_RFKILL_ALL -- when the user presses a button. Drivers for radio
+ transmitters generally do not register to the input layer, unless the
+ device really provides an input device (i.e. a button that has no
+ effect other than generating a button press event)

-rfkill devices issue uevents (with an action of "change"), with the following
-environment variables set:
+ * The rfkill-input code hooks up to these events and switches the soft-block
+ of the various radio transmitters, depending on the button type.

-RFKILL_NAME
-RFKILL_STATE
-RFKILL_TYPE
+ * The rfkill drivers turn off/on their transmitters as requested.

-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.
+ * The rfkill class will generate userspace notifications (uevents) to tell
+ userspace what the current state is.

-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.
+3. Kernel driver guidelines

-Userspace cannot assume it is the only source of control for rfkill switches.
-Their state can change due to firmware actions, direct user actions, and the
-rfkill-input EPO override for *_RFKILL_ALL.

-When rfkill-input is not active, userspace must initiate a rfkill status
-change by writing to the "state" attribute in order for anything to happen.
+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.

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

-The following sysfs entries will be created:
+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.
+
+
+
+4. Kernel API
+
+To build a driver with rfkill subsystem support, the driver should depend on
+(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).
+
+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_state() callback (then the rfkill core will poll the device).
+Don't do this unless you cannot get the event in any other way.
+
+
+
+5. Userspace support
+
+The following sysfs entries exist for every rfkill device:

name: Name assigned by driver to this key (interface or driver name).
type: Name of the key type ("wlan", "bluetooth", etc).
state: Current state of the transmitter
0: RFKILL_STATE_SOFT_BLOCKED
- transmitter is forced off, but one can override it
- by a write to the state attribute;
+ transmitter is turned off by software
1: RFKILL_STATE_UNBLOCKED
- transmiter is NOT forced off, and may operate if
- all other conditions for such operation are met
- (such as interface is up and configured, etc);
+ transmiter is (potentially) active
2: RFKILL_STATE_HARD_BLOCKED
transmitter is forced off by something outside of
- the driver's control. One cannot set a device to
- this state through writes to the state attribute;
- claim: 1: Userspace handles events, 0: Kernel handles events
-
-Both the "state" and "claim" entries are also writable. For the "state" entry
-this means that when 1 or 0 is written, the device rfkill state (if not yet in
-the requested state), will be will be toggled accordingly.
-
-For the "claim" entry writing 1 to it means that the kernel no longer handles
-key events even though RFKILL_INPUT input was enabled. When "claim" has been
-set to 0, userspace should make sure that it listens for the input events or
-check the sysfs "state" entry regularly to correctly perform the required tasks
-when the rkfill key is pressed.
-
-A note about input devices and EV_SW events:
-
-In order to know the current state of an input device switch (like
-SW_RFKILL_ALL), you will need to use an IOCTL. That information is not
-available through sysfs in a generic way at this time, and it is not available
-through the rfkill class AT ALL.
+ the driver's control.
+ claim: 0: Kernel handles events (currently always reads that value)
+
+rfkill devices also issue uevents (with an action of "change"), with the
+following environment variables set:
+
+RFKILL_NAME
+RFKILL_STATE
+RFKILL_TYPE
+
+The contents of these variables corresponds to the "name", "state" and
+"type" sysfs files explained above.
--- wireless-testing.orig/net/rfkill/Kconfig 2009-03-28 20:54:22.000000000 +0100
+++ wireless-testing/net/rfkill/Kconfig 2009-03-28 20:54:23.000000000 +0100
@@ -11,8 +11,9 @@ menuconfig RFKILL
module will be called rfkill.

config RFKILL_INPUT
- tristate "Input layer to RF switch connector"
+ bool "Input layer to RF switch connector" if EMBEDDED
depends on RFKILL && INPUT
+ default y
help
Say Y here if you want kernel automatically toggle state
of RF switches on and off when user presses appropriate
@@ -20,9 +21,6 @@ config RFKILL_INPUT
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
--- wireless-testing.orig/net/rfkill/Makefile 2009-03-28 20:54:22.000000000 +0100
+++ wireless-testing/net/rfkill/Makefile 2009-03-28 20:54:23.000000000 +0100
@@ -2,5 +2,7 @@
# 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
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/rfkill/core.c 2009-03-29 19:00:22.000000000 +0200
@@ -0,0 +1,754 @@
+/*
+ * Copyright (C) 2006 - 2007 Ivo van Doorn
+ * Copyright (C) 2007 Dmitry Torokhov
+ * Copyright 2009 Johannes Berg <[email protected]>
+ *
+ * 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 "rfkill.h"
+
+#define POLL_INTERVAL (5 * HZ)
+
+struct rfkill {
+ const char *name;
+ enum rfkill_type type;
+
+ unsigned long blocked;
+
+ const struct rfkill_ops *ops;
+ void *data;
+
+#ifdef CONFIG_RFKILL_LEDS
+ struct led_trigger led_trigger;
+#endif
+
+ struct device dev;
+ struct list_head node;
+
+ struct delayed_work poll_work;
+ struct work_struct uevent_work;
+};
+#define to_rfkill(d) container_of(d, struct rfkill, dev)
+
+
+
+MODULE_AUTHOR("Ivo van Doorn <[email protected]>");
+MODULE_AUTHOR("Johannes Berg <[email protected]>");
+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 = 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_activate(struct led_classdev *led)
+{
+ struct rfkill *rfkill;
+ struct led_trigger *trigger;
+
+ rfkill = container_of(led->trigger, struct rfkill, led_trigger);
+ trigger = &rfkill->led_trigger;
+
+ if (!led->name)
+ return;
+
+ if (rfkill->blocked)
+ led_trigger_event(trigger, LED_OFF);
+ else
+ led_trigger_event(trigger, LED_FULL);
+}
+#endif /* CONFIG_RFKILL_LEDS */
+
+static void rfkill_uevent(struct rfkill *rfkill)
+{
+ kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE);
+}
+
+bool __rfkill_set_hw_state(struct rfkill *rfkill, bool blocked, bool *change)
+{
+ bool prev;
+
+ BUG_ON(!rfkill);
+
+ if (blocked)
+ prev = !!test_and_set_bit(RFKILL_BLOCK_HW_BIT,
+ &rfkill->blocked);
+ else
+ prev = !!test_and_clear_bit(RFKILL_BLOCK_HW_BIT,
+ &rfkill->blocked);
+
+ *change = prev != blocked;
+
+ return blocked || !!test_bit(RFKILL_BLOCK_SW_BIT, &rfkill->blocked);
+}
+
+/**
+ * rfkill_set_radio - wrapper for set_radio hook
+ *
+ * @rfkill: the rfkill struct to use
+ * @blocked: the new software state
+ *
+ * Calls rfkill->set_radio, enforcing the API for set_radio
+ * calls and handling all the red tape such as issuing notifications
+ * if the call is successful.
+ *
+ * Suspended devices are not touched, only software updated.
+ */
+static void rfkill_set_radio(struct rfkill *rfkill, bool blocked)
+{
+ bool prev, change;
+
+ /*
+ * 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_state) {
+ __rfkill_set_hw_state(rfkill,
+ rfkill->ops->query_state(rfkill->data),
+ &change);
+ if (change)
+ rfkill_uevent(rfkill);
+ }
+
+ if (blocked)
+ prev = !!test_and_set_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+ else
+ prev = !!test_and_clear_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+
+ /* HW already blocked, so nothing changes */
+ if (test_bit(RFKILL_BLOCK_HW_BIT, &rfkill->blocked))
+ return;
+
+ if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP))
+ return;
+
+ rfkill->ops->set_block(rfkill->data, blocked);
+
+ 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_radio(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);
+}
+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)
+ rfkill_set_radio(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;
+}
+
+bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked)
+{
+ bool ret, change;
+
+ ret = __rfkill_set_hw_state(rfkill, blocked, &change);
+
+ if (change)
+ schedule_work(&rfkill->uevent_work);
+
+ return ret;
+}
+EXPORT_SYMBOL(rfkill_set_hw_state);
+
+bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
+{
+ bool prev, hwblock;
+
+ BUG_ON(!rfkill);
+
+ if (blocked)
+ prev = !!test_and_set_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+ else
+ prev = !!test_and_clear_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+
+ hwblock = !!test_bit(RFKILL_BLOCK_HW_BIT, &rfkill->blocked);
+
+ if (prev != blocked && !hwblock)
+ schedule_work(&rfkill->uevent_work);
+
+ return blocked || hwblock;
+}
+EXPORT_SYMBOL(rfkill_set_sw_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();
+ }
+
+ 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 *blocked)
+{
+ /* backward compat ... just hard-code */
+ if (test_bit(RFKILL_BLOCK_HW_BIT, blocked))
+ return RFKILL_USER_STATE_HARD_BLOCKED;
+ if (test_bit(RFKILL_BLOCK_SW_BIT, blocked))
+ 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);
+
+ return sprintf(buf, "%d\n", user_state_from_blocked(&rfkill->blocked));
+}
+
+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);
+ 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",
+ user_state_from_blocked(&rfkill->blocked));
+ return error;
+}
+
+void rfkill_pause_polling(struct rfkill *rfkill)
+{
+ BUG_ON(!rfkill);
+
+ if (!rfkill->ops->poll_hw_block)
+ 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_hw_block)
+ return;
+
+ schedule_delayed_work(&rfkill->poll_work,
+ round_jiffies_relative(POLL_INTERVAL));
+}
+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);
+
+ return 0;
+}
+
+static int rfkill_resume(struct device *dev)
+{
+ struct rfkill *rfkill = to_rfkill(dev);
+
+ schedule_work(&rfkill->poll_work.work);
+
+ 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,
+};
+
+
+/* caller must hold rfkill_global_mutex */
+static int __rfkill_add_switch(struct rfkill *rfkill)
+{
+ struct rfkill *tmp;
+
+ list_for_each_entry(tmp, &rfkill_list, node)
+ if (WARN_ON(tmp == rfkill))
+ return -EEXIST;
+
+ 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;
+ }
+
+ /* XXX: schedule work to set default state */
+
+ list_add_tail(&rfkill->node, &rfkill_list);
+
+ return 0;
+}
+
+/* caller must hold rfkill_global_mutex */
+static void __rfkill_remove_switch(struct rfkill *rfkill)
+{
+ list_del_init(&rfkill->node);
+}
+
+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;
+
+ 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 int rfkill_led_trigger_register(struct rfkill *rfkill)
+{
+ int error = 0;
+#ifdef CONFIG_RFKILL_LEDS
+ rfkill->led_trigger.name = dev_name(&rfkill->dev);
+ rfkill->led_trigger.activate = rfkill_led_trigger_activate;
+ error = led_trigger_register(&rfkill->led_trigger);
+#endif
+
+ return error;
+}
+
+static void rfkill_led_trigger_unregister(struct rfkill *rfkill)
+{
+#ifdef CONFIG_RFKILL_LEDS
+ led_trigger_unregister(&rfkill->led_trigger);
+#endif
+}
+
+static void rfkill_poll(struct work_struct *work)
+{
+ struct rfkill *rfkill;
+ bool change, hwblocked;
+
+ rfkill = container_of(work, struct rfkill, poll_work.work);
+
+ /* poll hardware state */
+ hwblocked = rfkill->ops->poll_hw_block(rfkill->data);
+
+ /*
+ * Set hardware state and tell driver to unblock if
+ * it wasn't software-blocked -- driver doesn't need
+ * to keep track of the current software block state
+ * that way.
+ */
+ if (!__rfkill_set_hw_state(rfkill, hwblocked, &change))
+ rfkill->ops->set_block(rfkill->data, false);
+
+ if (change)
+ rfkill_uevent(rfkill);
+
+ 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);
+}
+
+int __must_check rfkill_register(struct rfkill *rfkill)
+{
+ static unsigned long rfkill_no = 0;
+ struct device *dev = &rfkill->dev;
+ int error;
+
+ BUG_ON(!rfkill);
+
+ mutex_lock(&rfkill_global_mutex);
+
+ dev_set_name(dev, "rfkill%lu", rfkill_no);
+ rfkill_no++;
+
+ error = __rfkill_add_switch(rfkill);
+ if (error)
+ goto unlock;
+
+ error = device_add(dev);
+ if (error)
+ goto remove;
+
+ error = rfkill_led_trigger_register(rfkill);
+ if (error)
+ goto devdel;
+
+ if (rfkill->ops->poll_hw_block) {
+ 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);
+
+ return 0;
+
+ devdel:
+ device_del(&rfkill->dev);
+ remove:
+ __rfkill_remove_switch(rfkill);
+ 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_hw_block)
+ cancel_delayed_work_sync(&rfkill->poll_work);
+
+ cancel_work_sync(&rfkill->uevent_work);
+
+ device_del(&rfkill->dev);
+
+ mutex_lock(&rfkill_global_mutex);
+ __rfkill_remove_switch(rfkill);
+ 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);
+
+const char *rfkill_get_led_name(struct rfkill *rfkill)
+{
+ return rfkill->led_trigger.name;
+}
+EXPORT_SYMBOL(rfkill_get_led_name);
+
+int rfkill_set_sw_default(enum rfkill_type type, bool blocked)
+{
+ int error;
+
+ mutex_lock(&rfkill_global_mutex);
+
+ if (rfkill_states_default_locked & BIT(type)) {
+ error = -EPERM;
+ } else {
+ rfkill_global_states[type].def = blocked;
+ rfkill_global_states[type].cur = blocked;
+ error = 0;
+ }
+
+ mutex_unlock(&rfkill_global_mutex);
+
+ return error;
+}
+
+
+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;
+
+ error = rfkill_handler_init();
+ if (error)
+ class_unregister(&rfkill_class);
+
+ out:
+ return error;
+}
+subsys_initcall(rfkill_init);
+
+static void __exit rfkill_exit(void)
+{
+ rfkill_handler_exit();
+ class_unregister(&rfkill_class);
+}
+module_exit(rfkill_exit);
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/rfkill/input.c 2009-03-29 19:38:35.000000000 +0200
@@ -0,0 +1,384 @@
+/*
+ * Input layer to RF Kill interface connector
+ *
+ * Copyright (c) 2007 Dmitry Torokhov
+ * Copyright 2009 Johannes Berg <[email protected]>
+ *
+ * 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_DONOTHING = 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; 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(NUM_RFKILL_TYPES)];
+
+ /* should the state be complemented (1=yes) */
+ unsigned long sw_togglestate[BITS_TO_LONGS(NUM_RFKILL_TYPES)];
+
+ 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 < 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_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 < NUM_RFKILL_TYPES) {
+ 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,
+};
+
+int __init rfkill_handler_init(void)
+{
+ if (rfkill_master_switch_mode >= NUM_RFKILL_INPUT_MASTER_MODES)
+ 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);
+}
+
+void __exit rfkill_handler_exit(void)
+{
+ input_unregister_handler(&rfkill_handler);
+ cancel_delayed_work_sync(&rfkill_task.dwork);
+}
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/rfkill/rfkill.h 2009-03-28 21:13:24.000000000 +0100
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2007 Ivo van Doorn
+ * Copyright 2009 Johannes Berg <[email protected]>
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef __RFKILL_INPUT_H
+#define __RFKILL_INPUT_H
+
+/* 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);
+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 */
--- wireless-testing.orig/MAINTAINERS 2009-03-28 21:12:25.000000000 +0100
+++ wireless-testing/MAINTAINERS 2009-03-28 21:12:38.000000000 +0100
@@ -3667,8 +3667,8 @@ L: [email protected]
S: Supported

RFKILL
-P: Ivo van Doorn
-M: [email protected]
+P: Johannes Berg
+M: [email protected]
L: [email protected]
S: Maintained
F: net/rfkill
--- wireless-testing.orig/include/linux/Kbuild 2009-03-28 22:37:22.000000000 +0100
+++ wireless-testing/include/linux/Kbuild 2009-03-28 22:37:45.000000000 +0100
@@ -310,6 +310,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
--- wireless-testing.orig/drivers/net/wireless/ath9k/ath9k.h 2009-03-29 10:20:49.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/ath9k.h 2009-03-29 10:32:57.000000000 +0200
@@ -486,12 +486,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];
};

@@ -536,8 +533,6 @@ struct ath_rfkill {
#define SC_OP_RXFLUSH BIT(8)
#define SC_OP_LED_ASSOCIATED BIT(9)
#define SC_OP_RFKILL_REGISTERED BIT(10)
-#define SC_OP_RFKILL_SW_BLOCKED BIT(11)
-#define SC_OP_RFKILL_HW_BLOCKED BIT(12)
#define SC_OP_WAIT_FOR_BEACON BIT(13)
#define SC_OP_LED_ON BIT(14)
#define SC_OP_SCANNING BIT(15)
--- wireless-testing.orig/drivers/net/wireless/ath9k/pci.c 2009-03-29 10:30:17.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/pci.c 2009-03-29 10:30:45.000000000 +0200
@@ -227,11 +227,6 @@ static int ath_pci_suspend(struct pci_de

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
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;
}

--- wireless-testing.orig/drivers/net/wireless/b43/Kconfig 2009-03-29 11:11:37.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/Kconfig 2009-03-29 11:12:46.000000000 +0200
@@ -103,7 +103,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) && (INPUT_POLLDEV = y || INPUT_POLLDEV = B43)
default y

config B43_DEBUG
--- wireless-testing.orig/drivers/net/wireless/b43/rfkill.h 2009-03-29 11:13:32.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/rfkill.h 2009-03-29 11:13:38.000000000 +0200
@@ -26,7 +26,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 */
--- wireless-testing.orig/drivers/net/wireless/b43legacy/Kconfig 2009-03-29 11:16:39.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/Kconfig 2009-03-29 11:16:49.000000000 +0200
@@ -48,7 +48,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) && (INPUT_POLLDEV = y || INPUT_POLLDEV = B43LEGACY)
default y

config B43LEGACY_DEBUG
--- wireless-testing.orig/drivers/net/wireless/b43legacy/leds.c 2009-03-29 11:18:46.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/leds.c 2009-03-29 11:19:23.000000000 +0200
@@ -86,7 +86,8 @@ static void b43legacy_led_brightness_set

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;
--- wireless-testing.orig/drivers/net/wireless/b43legacy/rfkill.h 2009-03-29 11:16:58.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/rfkill.h 2009-03-29 11:17:02.000000000 +0200
@@ -27,7 +27,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 */
--- wireless-testing.orig/drivers/net/wireless/b43/leds.c 2009-03-29 11:19:55.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/leds.c 2009-03-29 11:20:02.000000000 +0200
@@ -87,7 +87,7 @@ static void b43_led_brightness_set(struc
}

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;
--- wireless-testing.orig/drivers/net/wireless/iwlwifi/Kconfig 2009-03-29 11:35:20.000000000 +0200
+++ wireless-testing/drivers/net/wireless/iwlwifi/Kconfig 2009-03-29 11:35:49.000000000 +0200
@@ -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"
--- wireless-testing.orig/drivers/net/wireless/b43/phy_lp.c 2009-03-29 12:31:46.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_lp.c 2009-03-29 12:32:08.000000000 +0200
@@ -488,7 +488,7 @@ static void b43_lpphy_op_radio_write(str
}

static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev,
- enum rfkill_state state)
+ bool blocked)
{
//TODO
}
--- wireless-testing.orig/drivers/platform/x86/Kconfig 2009-03-29 17:21:14.000000000 +0200
+++ wireless-testing/drivers/platform/x86/Kconfig 2009-03-29 17:21:37.000000000 +0200
@@ -21,7 +21,6 @@ config ACER_WMI
depends on NEW_LEDS
depends on BACKLIGHT_CLASS_DEVICE
depends on SERIO_I8042
- depends on RFKILL
select ACPI_WMI
---help---
This is a driver for newer Acer (and Wistron) laptops. It adds
@@ -60,7 +59,6 @@ config DELL_LAPTOP
depends on DCDBAS
depends on EXPERIMENTAL
depends on BACKLIGHT_CLASS_DEVICE
- depends on RFKILL
depends on POWER_SUPPLY
default n
---help---
@@ -107,7 +105,6 @@ config HP_WMI
tristate "HP WMI extras"
depends on ACPI_WMI
depends on INPUT
- depends on RFKILL
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.
@@ -192,7 +189,6 @@ config THINKPAD_ACPI
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
@@ -305,7 +301,6 @@ config EEEPC_LAPTOP
depends on EXPERIMENTAL
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.
@@ -372,7 +367,6 @@ config ACPI_TOSHIBA
depends on INPUT
select INPUT_POLLDEV
select NET
- select RFKILL
select BACKLIGHT_CLASS_DEVICE
---help---
This driver adds support for access to certain system settings
--- wireless-testing.orig/drivers/platform/x86/acer-wmi.c 2009-03-29 17:42:25.000000000 +0200
+++ wireless-testing/drivers/platform/x86/acer-wmi.c 2009-03-29 17:58:37.000000000 +0200
@@ -939,58 +939,49 @@ static void acer_rfkill_update(struct wo

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 void 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;
+ printk(KERN_ERR "acer-wmi: failed to set rfkill\n");
}

-static struct rfkill * acer_rfkill_register(struct device *dev,
-enum rfkill_type type, char *name, u32 cap)
+static 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;
@@ -1008,8 +999,8 @@ static int acer_rfkill_init(struct devic
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);
}
}
@@ -1022,11 +1013,13 @@ static int acer_rfkill_init(struct devic
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;
}
--- wireless-testing.orig/drivers/platform/x86/dell-laptop.c 2009-03-29 17:30:36.000000000 +0200
+++ wireless-testing/drivers/platform/x86/dell-laptop.c 2009-03-29 17:48:13.000000000 +0200
@@ -174,68 +174,37 @@ dell_send_request(struct calling_interfa
result[3]: NVRAM format version number
*/

-static int dell_rfkill_set(int radio, enum rfkill_state state)
+static void 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));
dell_send_request(&buffer, 17, 11);
-
- 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 bool dell_rfkill_poll(void *data)
{
struct calling_interface_buffer buffer;
int status;
- int new_state = RFKILL_STATE_HARD_BLOCKED;
+ int bit = (unsigned long)data + 16;

memset(&buffer, 0, sizeof(struct calling_interface_buffer));
dell_send_request(&buffer, 17, 11);
status = buffer.output[1];

- if (status & (1<<16))
- new_state = RFKILL_STATE_SOFT_BLOCKED;
-
- if (status & (1<<bit))
- *state = new_state;
- else
- *state = RFKILL_STATE_UNBLOCKED;
-
- return 0;
-}
+ if (status & BIT(bit))
+ return !!(status & BIT(16));

-static int dell_wifi_get(void *data, enum rfkill_state *state)
-{
- return dell_rfkill_get(17, state);
+ return false;
}

-static int dell_bluetooth_get(void *data, enum rfkill_state *state)
-{
- return dell_rfkill_get(18, state);
-}
-
-static int dell_wwan_get(void *data, enum rfkill_state *state)
-{
- return dell_rfkill_get(19, state);
-}
+static struct rfkill_ops dell_rfkill_ops = {
+ .set_block = dell_rfkill_set,
+ .poll_hw_block = dell_rfkill_poll,
+};

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;
}
--- wireless-testing.orig/drivers/platform/x86/hp-wmi.c 2009-03-29 17:49:43.000000000 +0200
+++ wireless-testing/drivers/platform/x86/hp-wmi.c 2009-03-29 18:09:28.000000000 +0200
@@ -152,58 +152,47 @@ 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 void 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);
+ if (hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, query))
+ printk(KERN_ERR "hp-wmi: failed to set rfkill\n");
}

-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 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,
@@ -345,14 +334,14 @@ static void hp_wmi_notify(u32 value, voi
}
} 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);
@@ -428,31 +417,34 @@ static int __init hp_wmi_bios_setup(stru
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;
@@ -460,11 +452,15 @@ static int __init hp_wmi_bios_setup(stru

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;
@@ -474,12 +470,18 @@ static int __exit hp_wmi_bios_remove(str
{
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;
}
--- wireless-testing.orig/drivers/platform/x86/toshiba_acpi.c 2009-03-29 19:12:47.000000000 +0200
+++ wireless-testing/drivers/platform/x86/toshiba_acpi.c 2009-03-29 19:18:44.000000000 +0200
@@ -311,38 +311,30 @@ static u32 hci_get_radio_state(bool *rad
return hci_result;
}

-static int bt_rfkill_toggle_radio(void *data, enum rfkill_state state)
+static void bt_rfkill_set_block(void *data, bool blocked)
{
u32 result1, result2;
u32 value;
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;
-
- switch (state) {
- case RFKILL_STATE_UNBLOCKED:
- if (!radio_state)
- return -EPERM;
- break;
- case RFKILL_STATE_SOFT_BLOCKED:
- break;
- default:
- return -EINVAL;
+ if (hci_get_radio_state(&radio_state) != HCI_SUCCESS) {
+ printk(KERN_ERR "toshiba-acpi: rfkill fault\n");
+ return;
}

+ if (WARN_ON(!blocked && !radio_state))
+ return;
+
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;
+ printk(KERN_ERR "toshiba-acpi: failed to set BT rfkill\n");
}

static void bt_poll_rfkill(struct input_polled_dev *poll_dev)
@@ -365,7 +357,7 @@ static void bt_poll_rfkill(struct input_
mutex_unlock(&dev->mutex);

if (unlikely(state_changed)) {
- rfkill_force_state(dev->rfk_dev,
+ rfkill_set_hw_state(dev->rfk_dev,
new_rfk_state ?
RFKILL_STATE_SOFT_BLOCKED :
RFKILL_STATE_HARD_BLOCKED);




2009-03-31 18:21:08

by Larry Finger

[permalink] [raw]
Subject: Re: [RFC/RFT v2] rfkill: rewrite

Johannes Berg wrote:
> This patch completely rewrites the rfkill core to address
> the following deficiencies:

-- snip --

> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ wireless-testing/net/rfkill/core.c 2009-03-31 00:04:25.000000000 +0200
> @@ -0,0 +1,752 @@
> +/*

-- snip --

> +int __must_check rfkill_register(struct rfkill *rfkill)
> +{
> + static unsigned long rfkill_no = 0;
> + 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;
> + }
> +
> + /* XXX: schedule work to set default state */
> +
> + 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_hw_block) {
> + 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);
> +
> + return 0;

The locking looks funny here. The normal return leaves the rfkill_global_mutex
locked. Did you mean "goto unlock" instead of "return 0"?

> +
> + devdel:
> + device_del(&rfkill->dev);
> + remove:
> + list_del_init(&rfkill->node);
> + unlock:
> + mutex_unlock(&rfkill_global_mutex);
> + return error;
> +}

I was alerted to this problem when I was unable to shut down or reboot normally.
Those problems are fixed with the patch

Index: wireless-testing/net/rfkill/core.c
===================================================================
--- wireless-testing.orig/net/rfkill/core.c
+++ wireless-testing/net/rfkill/core.c
@@ -676,7 +676,7 @@ int __must_check rfkill_register(struct

INIT_WORK(&rfkill->uevent_work, rfkill_uevent_work);

- return 0;
+ goto unlock;

devdel:
device_del(&rfkill->dev);


Larry

2009-03-29 18:16:11

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Sunday 29 March 2009, Johannes Berg wrote:
> This patch completely rewrites the rfkill core to address
> the following deficiencies:
>
> * all rfkill drivers need to implement polling where necessary
> rather than having one central implementation
>
> * updating the rfkill state cannot be done from any context,
> forcing drivers to use schedule_work and requiring lots of
> code
>
> * rfkill drivers need to keep track of soft/hard blocked
> internally -- the core should do this
>
> * the rfkill API has many unexpected quirks, for example being
> asymmetric wrt. alloc/free and register/unregister
>
> * rfkill can call back into a driver from within a function the
> driver called -- this is prone to deadlocks and generally
> should be avoided
>
> * rfkill-input pointlessly is a separate module
>
> * drivers need to #ifdef rfkill functions (unless they want to
> depend on or select RFKILL) -- rfkill should provide inlines
> that do nothing if it isn't compiled in
>
> * the rfkill structure, despite containing almost only internal
> variables, is not opaque -- and drivers need to initialise it
> correctly (lots of sanity checking code required) -- instead
> force drivers to pass the right variables to rfkill_alloc()
>
> * the documentation is hard to read because it always assumes
> the reader is completely clueless and contains way to many CAPS
>
> * the rfkill code needlessly uses a lot of locks and atomic
> operations in locked sections
>
> Signed-off-by: Johannes Berg <[email protected]>
> ---
> RFC only because I haven't fixed all drivers yet, still missing are
> toshiba, thinkpad, wimax and tosa.
>
> Let the flames begin! (or if you like it feel free to chime in too)

Just for the record, as long as you (or somebody else) takes over maintainership
over rfkill after these changes, I have no objections against the changes. Luckily
you already updated the Maintainers entry, so I'm happy. :)

I know rfkill was poorly designed and I simply lacked the time (and perhaps
some interest as well) to continue with it. The changes you propose sound
reasonable so I hope it will turn rfkill really into something which it was supposed
to be from the start.

Ivo

2009-03-30 10:04:20

by Matthew Garrett

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Mon, Mar 30, 2009 at 11:40:57AM +0200, Johannes Berg wrote:
> TODO:
>
> * toshiba_acpi.c
> -> need help, seems to abuse input polldev and report it as both
> input and rfkill block state? at very least need info about what
> hw actually does

I'm looking at reworking some of this today.

--
Matthew Garrett | [email protected]

Subject: Re: [RFC] rfkill: rewrite

On Mon, 30 Mar 2009, Johannes Berg wrote:
> * thinkpad_acpi.c
> -> need help, convoluted code I don't understand at all, looks like
> another candidate for rewriting

Heh. I will take care of it, obviously. As for rewriting, well, a cleanup
is in the queue.

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

2009-03-31 19:12:14

by Johannes Berg

[permalink] [raw]
Subject: [RFC v3] rfkill: rewrite

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

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

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

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

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

* rfkill-input pointlessly is a separate module

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

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

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

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

Signed-off-by: Johannes Berg <[email protected]>
---
v3: * fix locking bugs
* fix bug in input.c that made it ignore things
* allow setting LED trigger name
* apply default state while registering
* convert HSO

TODO
* convert toshiba, thinkpad
* (compile) test tosa conversion
* test

Documentation/rfkill.txt | 639 +++-------------------
MAINTAINERS | 6
arch/arm/mach-pxa/tosa-bt.c | 30 -
arch/arm/mach-pxa/tosa.c | 1
drivers/net/usb/hso.c | 42 -
drivers/net/wireless/ath9k/ath9k.h | 7
drivers/net/wireless/ath9k/main.c | 115 +---
drivers/net/wireless/ath9k/pci.c | 15
drivers/net/wireless/b43/Kconfig | 2
drivers/net/wireless/b43/leds.c | 2
drivers/net/wireless/b43/main.c | 4
drivers/net/wireless/b43/phy_a.c | 4
drivers/net/wireless/b43/phy_common.c | 17
drivers/net/wireless/b43/phy_common.h | 4
drivers/net/wireless/b43/phy_g.c | 4
drivers/net/wireless/b43/phy_lp.c | 2
drivers/net/wireless/b43/phy_n.c | 2
drivers/net/wireless/b43/rfkill.c | 122 +---
drivers/net/wireless/b43/rfkill.h | 5
drivers/net/wireless/b43legacy/Kconfig | 2
drivers/net/wireless/b43legacy/leds.c | 3
drivers/net/wireless/b43legacy/rfkill.c | 118 +---
drivers/net/wireless/b43legacy/rfkill.h | 6
drivers/net/wireless/iwlwifi/Kconfig | 5
drivers/net/wireless/iwlwifi/iwl-rfkill.c | 69 --
drivers/platform/x86/Kconfig | 6
drivers/platform/x86/acer-wmi.c | 50 -
drivers/platform/x86/dell-laptop.c | 101 +--
drivers/platform/x86/eeepc-laptop.c | 91 ---
drivers/platform/x86/hp-wmi.c | 103 +--
drivers/platform/x86/toshiba_acpi.c | 23
include/linux/Kbuild | 1
include/linux/rfkill.h | 330 +++++++++--
include/net/wimax.h | 8
net/rfkill/Kconfig | 14
net/rfkill/Makefile | 4
net/rfkill/core.c | 780 +++++++++++++++++++++++++++
net/rfkill/input.c | 342 ++++++++++++
net/rfkill/rfkill-input.c | 392 -------------
net/rfkill/rfkill-input.h | 21
net/rfkill/rfkill.c | 843 ------------------------------
net/rfkill/rfkill.h | 27
net/wimax/Kconfig | 14
net/wimax/op-rfkill.c | 125 ----
44 files changed, 1865 insertions(+), 2636 deletions(-)

--- wireless-testing.orig/include/linux/rfkill.h 2009-03-31 11:48:54.000000000 +0200
+++ wireless-testing/include/linux/rfkill.h 2009-03-31 20:35:04.000000000 +0200
@@ -4,6 +4,7 @@
/*
* Copyright (C) 2006 - 2007 Ivo van Doorn
* Copyright (C) 2007 Dmitry Torokhov
+ * Copyright 2009 Johannes Berg <[email protected]>
*
* 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,11 +49,13 @@

/**
* 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 ,
@@ -42,97 +63,260 @@ enum rfkill_type {
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 */
+#define RFKILL_BLOCK_HW_BIT 0
+#define RFKILL_BLOCK_SW_BIT 1
+
+/**
+ * struct rfkill_ops - rfkill driver methods
+ *
+ * @poll_hw_block: poll the rfkill hardware block state (return true
+ * for blocked) -- only assign this method when you cannot
+ * do without polling
+ * @query_hw_block: query the rfkill hardware block state (return true
+ * for blocked) -- assign this method if input events can cause
+ * hardware state changes
+ * @set_block: turn the transmitter on (blocked == false) or off
+ * (blocked == true) -- this is called only while the transmitter
+ * is not hard-blocked. This must be assigned.
+ */
+struct rfkill_ops {
+ bool (*poll_hw_block)(void *data);
+ bool (*query_hw_block)(void *data);
+ int (*set_block)(void *data, bool blocked);
};

+/* this is opaque */
+struct rfkill;
+
+#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.
- *
- * This structure represents a RF switch located on a network device.
- */
-struct rfkill {
- const char *name;
- enum rfkill_type type;
-
- /* the mutex serializes callbacks and also protects
- * the state */
- struct mutex mutex;
- enum rfkill_state state;
- void *data;
- int (*toggle_radio)(void *data, enum rfkill_state state);
- int (*get_state)(void *data, enum rfkill_state *state);
+ * rfkill_alloc - allocate rfkill structure
+ * @name: name of the struct -- the string is not copied internally
+ * @parent: device that has rf switch on it
+ * @type: type of the switch (RFKILL_TYPE_*)
+ * @ops: rfkill methods
+ * @ops_data: data passed to each method
+ *
+ * This function should be called by the transmitter driver to allocate an
+ * rfkill structure.
+ */
+struct rfkill * __must_check rfkill_alloc(const char *name,
+ struct device *parent,
+ const enum rfkill_type type,
+ const struct rfkill_ops *ops,
+ void *ops_data);

-#ifdef CONFIG_RFKILL_LEDS
- struct led_trigger led_trigger;
-#endif
+/**
+ * rfkill_has_sw_block_memory -- turn on calling set_block unconditionally
+ * @rfkill: rfkill struct
+ *
+ * This function tells the rfkill core that the device is capable of
+ * remembering soft blocks (which it is notified of via the set_block
+ * method) -- this means that the driver may ignore the return value
+ * from rfkill_set_hw_state().
+ *
+ * This function must be called before rfkill_register().
+ */
+void rfkill_has_sw_block_memory(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_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);

-struct rfkill * __must_check rfkill_allocate(struct device *parent,
- enum rfkill_type type);
-void rfkill_free(struct rfkill *rfkill);
+/**
+ * rfkill_register - Register a rfkill structure.
+ * @rfkill: rfkill structure to be registered
+ *
+ * This function should be called by the transmitter driver to register
+ * the rfkill structure needs to be registered. Before calling this function
+ * the driver needs to be ready to service method calls from rfkill.
+ */
int __must_check rfkill_register(struct rfkill *rfkill);
+
+/**
+ * rfkill_pause_polling(struct rfkill *rfkill)
+ *
+ * Pause polling -- say transmitter is off for other reasons.
+ * NOTE: not necessary for suspend/resume -- in that case the
+ * core stops polling anyway
+ */
+void rfkill_pause_polling(struct rfkill *rfkill);
+
+/**
+ * rfkill_resume_polling(struct rfkill *rfkill)
+ *
+ * Pause polling -- say transmitter is off for other reasons.
+ * NOTE: not necessary for suspend/resume -- in that case the
+ * core stops polling anyway
+ */
+void rfkill_resume_polling(struct rfkill *rfkill);
+
+
+/**
+ * rfkill_unregister - Unregister a rfkill structure.
+ * @rfkill: rfkill structure to be unregistered
+ *
+ * This function should be called by the network driver during device
+ * teardown to destroy rfkill structure. Until it returns, the driver
+ * needs to be able to service method calls.
+ */
void rfkill_unregister(struct rfkill *rfkill);

-int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state);
-int rfkill_set_default(enum rfkill_type type, enum rfkill_state state);
+/**
+ * rfkill_destroy - free rfkill structure
+ * @rfkill: rfkill structure to be destroyed
+ *
+ * Destroys the rfkill structure.
+ */
+void rfkill_destroy(struct rfkill *rfkill);

/**
- * rfkill_state_complement - return complementar state
- * @state: state to return the complement of
+ * 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.
*
- * Returns RFKILL_STATE_SOFT_BLOCKED if @state is RFKILL_STATE_UNBLOCKED,
- * returns RFKILL_STATE_UNBLOCKED otherwise.
+ * 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.
+ *
+ * If the device is able to, call rfkill_has_sw_block_memory() and
+ * ignore the return value of this function.
*/
-static inline enum rfkill_state rfkill_state_complement(enum rfkill_state state)
+bool 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.
+ *
+ * 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_global_sw_state - set global sw block default
+ * @type: rfkill type to set default for
+ * @blocked: default to set
+ *
+ * This function sets the global default -- use at boot if your platform has
+ * an rfkill switch. If not early enough this call may be ignored.
+ *
+ * XXX: instead of ignoring -- how about just updating all currently
+ * registered drivers?
+ */
+void rfkill_set_global_sw_state(const enum rfkill_type type, bool blocked);
+#else /* !RFKILL */
+static inline struct rfkill * __must_check
+rfkill_alloc(const char *name,
+ struct device *parent,
+ const enum rfkill_type type,
+ const struct rfkill_ops *ops,
+ void *ops_data)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+static inline void rfkill_has_sw_block_memory(struct rfkill *rfkill)
+{
+}
+
+static inline int __must_check rfkill_register(struct rfkill *rfkill)
+{
+ if (rfkill == ERR_PTR(-ENODEV))
+ return 0;
+ return -EINVAL;
+}
+
+static inline void rfkill_pause_polling(struct rfkill *rfkill)
+{
+}
+
+static inline void rfkill_resume_polling(struct rfkill *rfkill)
+{
+}
+
+static inline void rfkill_unregister(struct rfkill *rfkill)
+{
+}
+
+static inline void rfkill_destroy(struct rfkill *rfkill)
{
- return (state == RFKILL_STATE_UNBLOCKED) ?
- RFKILL_STATE_SOFT_BLOCKED : RFKILL_STATE_UNBLOCKED;
}

+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_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
}

+void rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name)
+{
+}
+#endif
+
+#endif /* __KERNEL__ */
+
#endif /* RFKILL_H */
--- wireless-testing.orig/drivers/net/wireless/ath9k/main.c 2009-03-31 11:48:54.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/main.c 2009-03-31 11:48:55.000000000 +0200
@@ -1181,120 +1181,69 @@ static bool ath_is_rfkill_set(struct ath
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 bool ath_rfkill_poll_state(void *data)
{
struct ath_softc *sc = data;
+ bool ret = !!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 (ret)
+ ath_radio_disable(sc);
+
+ return ret;
}

/* 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_hw_block = 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);
@@ -1655,10 +1604,6 @@ int ath_attach(u16 devid, struct ath_sof
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)
@@ -2170,10 +2115,8 @@ static void ath9k_stop(struct ieee80211_
} 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);
--- wireless-testing.orig/drivers/platform/x86/eeepc-laptop.c 2009-03-31 11:13:09.000000000 +0200
+++ wireless-testing/drivers/platform/x86/eeepc-laptop.c 2009-03-31 11:48:55.000000000 +0200
@@ -296,39 +296,15 @@ static int update_bl_status(struct backl
* Rfkill helpers
*/

-static int eeepc_wlan_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_WLAN, 0);
- else
- return set_acpi(CM_ASL_WLAN, 1);
-}
-
-static int eeepc_wlan_rfkill_state(void *data, enum rfkill_state *state)
-{
- if (get_acpi(CM_ASL_WLAN) == 1)
- *state = RFKILL_STATE_UNBLOCKED;
- else
- *state = RFKILL_STATE_SOFT_BLOCKED;
- return 0;
+ unsigned long asl = (unsigned long)data;
+ return set_acpi(asl, !blocked);
}

-static int eeepc_bluetooth_rfkill_set(void *data, enum rfkill_state state)
-{
- if (state == RFKILL_STATE_SOFT_BLOCKED)
- return set_acpi(CM_ASL_BLUETOOTH, 0);
- else
- return set_acpi(CM_ASL_BLUETOOTH, 1);
-}
-
-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
@@ -650,26 +626,17 @@ static int eeepc_hotk_add(struct acpi_de
printk(EEEPC_ERR "Error installing notify handler\n");

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_sw_state(ehotk->eeepc_wlan_rfkill,
+ get_acpi(CM_ASL_WLAN) != 1);
result = rfkill_register(ehotk->eeepc_wlan_rfkill);
if (result)
goto wlan_fail;
@@ -677,28 +644,17 @@ static int eeepc_hotk_add(struct acpi_de

if (get_acpi(CM_ASL_BLUETOOTH) != -1) {
ehotk->eeepc_bluetooth_rfkill =
- rfkill_allocate(&device->dev, RFKILL_TYPE_BLUETOOTH);
+ rfkill_alloc("eeepc-wlan",
+ &device->dev,
+ RFKILL_TYPE_WLAN,
+ &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_sw_state(ehotk->eeepc_bluetooth_rfkill,
+ get_acpi(CM_ASL_BLUETOOTH) != 1);
result = rfkill_register(ehotk->eeepc_bluetooth_rfkill);
if (result)
goto bluetooth_fail;
@@ -710,13 +666,10 @@ static int eeepc_hotk_add(struct acpi_de
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);
ehotk_fail:
kfree(ehotk);
ehotk = NULL;
--- wireless-testing.orig/net/rfkill/rfkill.c 2009-03-31 11:48:54.000000000 +0200
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,843 +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 <[email protected]>");
-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);
-}
-#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_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);
-
- 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_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);
-}
-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);
-
- 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);
- }
-
- 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);
--- wireless-testing.orig/net/rfkill/rfkill-input.h 2009-03-31 11:13:08.000000000 +0200
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2007 Ivo van Doorn
- */
-
-/*
- * 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.
- */
-
-#ifndef __RFKILL_INPUT_H
-#define __RFKILL_INPUT_H
-
-void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state);
-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);
-
-#endif /* __RFKILL_INPUT_H */
--- wireless-testing.orig/net/rfkill/rfkill-input.c 2009-03-31 11:48:53.000000000 +0200
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,392 +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.
- *
- * If you ever run into a situation in which you have a SW_ type rfkill
- * input device, then you can revive code that was removed in the patch
- * "rfkill-input: remove unused code".
- */
-
-#include <linux/module.h>
-#include <linux/input.h>
-#include <linux/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 <[email protected]>");
-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);
--- wireless-testing.orig/drivers/net/wireless/b43/main.c 2009-03-31 11:13:08.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/main.c 2009-03-31 11:48:55.000000000 +0200
@@ -3492,7 +3492,7 @@ static int b43_op_config(struct ieee8021

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 "
@@ -3500,7 +3500,7 @@ static int b43_op_config(struct ieee8021
"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");
}
}
--- wireless-testing.orig/drivers/net/wireless/b43/phy_a.c 2009-03-31 11:13:08.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_a.c 2009-03-31 11:48:55.000000000 +0200
@@ -480,11 +480,11 @@ static bool b43_aphy_op_supports_hwpctl(
}

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);
--- wireless-testing.orig/drivers/net/wireless/b43/phy_common.c 2009-03-31 11:13:08.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_common.c 2009-03-31 11:48:55.000000000 +0200
@@ -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);
}
@@ -287,18 +287,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);
}

--- wireless-testing.orig/drivers/net/wireless/b43/phy_common.h 2009-03-31 11:13:08.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_common.h 2009-03-31 11:48:55.000000000 +0200
@@ -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);
@@ -362,7 +362,7 @@ int b43_switch_channel(struct b43_wldev
/**
* 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.
--- wireless-testing.orig/drivers/net/wireless/b43/phy_g.c 2009-03-31 11:13:08.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_g.c 2009-03-31 11:48:55.000000000 +0200
@@ -2592,7 +2592,7 @@ static bool b43_gphy_op_supports_hwpctl(
}

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(

might_sleep();

- if (state == RFKILL_STATE_UNBLOCKED) {
+ if (!blocked) {
/* Turn radio ON */
if (phy->radio_on)
return;
--- wireless-testing.orig/drivers/net/wireless/b43/phy_n.c 2009-03-31 11:13:08.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_n.c 2009-03-31 11:48:55.000000000 +0200
@@ -579,7 +579,7 @@ static void b43_nphy_op_radio_write(stru
}

static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
- enum rfkill_state state)
+ bool blocked)
{//TODO
}

--- wireless-testing.orig/drivers/net/wireless/b43/rfkill.c 2009-03-31 11:48:54.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/rfkill.c 2009-03-31 16:06:43.000000000 +0200
@@ -45,83 +45,68 @@ static bool b43_is_hw_radio_enabled(stru
}

/* The poll callback for the hardware button. */
-static void b43_rfkill_poll(struct input_polled_dev *poll_dev)
+static bool b43_rfkill_poll(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)) {
mutex_unlock(&wl->mutex);
- return;
+ return false;
}
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");
}
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);
- }
+ return !enabled;
}

/* 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 (WARN_ON(!dev->radio_hw_enable && !blocked))
+ 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_hw_block = b43_rfkill_poll,
+};
+
void b43_rfkill_init(struct b43_wldev *dev)
{
struct b43_wl *wl = dev->wl;
@@ -130,65 +115,26 @@ void b43_rfkill_init(struct b43_wldev *d

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 +147,7 @@ void b43_rfkill_exit(struct b43_wldev *d
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;
}
--- wireless-testing.orig/drivers/net/wireless/b43legacy/rfkill.c 2009-03-31 11:48:54.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/rfkill.c 2009-03-31 16:06:52.000000000 +0200
@@ -45,86 +45,73 @@ static bool b43legacy_is_hw_radio_enable
}

/* The poll callback for the hardware button. */
-static void b43legacy_rfkill_poll(struct input_polled_dev *poll_dev)
+static bool b43legacy_rfkill_poll(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)) {
mutex_unlock(&wl->mutex);
- return;
+ return false;
}
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");
}
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);
- }
+ return !enabled;
}

/* 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 (WARN_ON(!blocked && !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_hw_block = b43legacy_rfkill_poll,
+};
+
void b43legacy_rfkill_init(struct b43legacy_wldev *dev)
{
struct b43legacy_wl *wl = dev->wl;
@@ -133,60 +120,25 @@ void b43legacy_rfkill_init(struct b43leg

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 +151,8 @@ void b43legacy_rfkill_exit(struct b43leg
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;
}

--- wireless-testing.orig/drivers/net/wireless/iwlwifi/iwl-rfkill.c 2009-03-31 11:48:54.000000000 +0200
+++ wireless-testing/drivers/net/wireless/iwlwifi/iwl-rfkill.c 2009-03-31 11:48:55.000000000 +0200
@@ -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 (WARN_ON(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 *pri
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 *pri
}

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_
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);
--- wireless-testing.orig/Documentation/rfkill.txt 2009-03-31 11:48:54.000000000 +0200
+++ wireless-testing/Documentation/rfkill.txt 2009-03-31 20:36:33.000000000 +0200
@@ -1,571 +1,132 @@
-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:
-
-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.
-
-Note that disabling the signal output means that the the transmitter is to be
-made to not emit any energy when "blocked". rfkill is not about blocking data
-transmissions, it is about blocking energy emission.
-
-The rfkill subsystem offers support for keys and switches often found on
-laptops to enable wireless devices like WiFi and Bluetooth, so that these keys
-and switches actually perform an action in all wireless devices of a given type
-attached to the system.
-
-The buttons to enable and disable the wireless transmitters are important in
-situations where the user is for example using his laptop on a location where
-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.
+1. Introduction
+2. Implementation details
+3. Kernel driver guidelines
+4. Kernel API
+5. Userspace support

-===============================================================================
-2: Implementation details
+
+1. Introduction
+
+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.
+
+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.
+
+
+
+2. Implementation details

The rfkill subsystem is composed of various components: the rfkill class, the
rfkill-input module (an input layer handler), and some specific input layer
events.

-The rfkill class provides kernel drivers with an interface that allows them to
-know when they should enable or disable a wireless network device transmitter.
-This is enabled by the CONFIG_RFKILL Kconfig option.
-
-The rfkill class support makes sure userspace will be notified of all state
-changes on rfkill devices through uevents. It provides a notification chain
-for interested parties in the kernel to also get notified of rfkill state
-changes in other drivers. It creates several sysfs entries which can be used
-by userspace. See section "Userspace support".
-
-The rfkill-input module provides the kernel with the ability to implement a
-basic response when the user presses a key or button (or toggles a switch)
-related to rfkill functionality. It is an in-kernel implementation of default
-policy of reacting to rfkill-related input events and neither mandatory nor
-required for wireless drivers to operate. It is enabled by the
-CONFIG_RFKILL_INPUT Kconfig option.
-
-rfkill-input is a rfkill-related events input layer handler. This handler will
-listen to all rfkill key events and will change the rfkill state of the
-wireless devices accordingly. With this option enabled userspace could either
-do nothing or simply perform monitoring tasks.
-
-The rfkill-input module also provides EPO (emergency power-off) functionality
-for all wireless transmitters. This function cannot be overridden, and it is
-always active. rfkill EPO is related to *_RFKILL_ALL input layer events.
-
-
-Important terms for the rfkill subsystem:
-
-In order to avoid confusion, we avoid the term "switch" in rfkill when it is
-referring to an electronic control circuit that enables or disables a
-transmitter. We reserve it for the physical device a human manipulates
-(which is an input device, by the way):
-
-rfkill switch:
-
- A physical device a human manipulates. Its state can be perceived by
- the kernel either directly (through a GPIO pin, ACPI GPE) or by its
- effect on a rfkill line of a wireless device.
-
-rfkill controller:
-
- A hardware circuit that controls the state of a rfkill line, which a
- kernel driver can interact with *to modify* that state (i.e. it has
- either write-only or read/write access).
-
-rfkill line:
-
- An input channel (hardware or software) of a wireless device, which
- causes a wireless transmitter to stop emitting energy (BLOCK) when it
- is active. Point of view is extremely important here: rfkill lines are
- always seen from the PoV of a wireless device (and its driver).
-
-soft rfkill line/software rfkill line:
-
- A rfkill line the wireless device driver can directly change the state
- of. Related to rfkill_state RFKILL_STATE_SOFT_BLOCKED.
-
-hard rfkill line/hardware rfkill line:
-
- A rfkill line that works fully in hardware or firmware, and that cannot
- be overridden by the kernel driver. The hardware device or the
- firmware just exports its status to the driver, but it is read-only.
- Related to rfkill_state RFKILL_STATE_HARD_BLOCKED.
-
-The enum rfkill_state describes the rfkill state of a transmitter:
-
-When a rfkill line or rfkill controller is in the RFKILL_STATE_UNBLOCKED state,
-the wireless transmitter (radio TX circuit for example) is *enabled*. When the
-it is in the RFKILL_STATE_SOFT_BLOCKED or RFKILL_STATE_HARD_BLOCKED, the
-wireless transmitter is to be *blocked* from operating.
-
-RFKILL_STATE_SOFT_BLOCKED indicates that a call to toggle_radio() can change
-that state. RFKILL_STATE_HARD_BLOCKED indicates that a call to toggle_radio()
-will not be able to change the state and will return with a suitable error if
-attempts are made to set the state to RFKILL_STATE_UNBLOCKED.
-
-RFKILL_STATE_HARD_BLOCKED is used by drivers to signal that the device is
-locked in the BLOCKED state by a hardwire rfkill line (typically an input pin
-that, when active, forces the transmitter to be disabled) which the driver
-CANNOT override.
-
-Full rfkill functionality requires two different subsystems to cooperate: the
-input layer and the rfkill class. The input layer issues *commands* to the
-entire system requesting that devices registered to the rfkill class change
-state. The way this interaction happens is not complex, but it is not obvious
-either:
-
-Kernel Input layer:
-
- * Generates KEY_WWAN, KEY_WLAN, KEY_BLUETOOTH, SW_RFKILL_ALL, and
- other such events when the user presses certain keys, buttons, or
- toggles certain physical switches.
-
- THE INPUT LAYER IS NEVER USED TO PROPAGATE STATUS, NOTIFICATIONS OR THE
- KIND OF STUFF AN ON-SCREEN-DISPLAY APPLICATION WOULD REPORT. It is
- used to issue *commands* for the system to change behaviour, and these
- commands may or may not be carried out by some kernel driver or
- userspace application. It follows that doing user feedback based only
- on input events is broken, as there is no guarantee that an input event
- will be acted upon.
-
- Most wireless communication device drivers implementing rfkill
- functionality MUST NOT generate these events, and have no reason to
- register themselves with the input layer. Doing otherwise is a common
- misconception. There is an API to propagate rfkill status change
- information, and it is NOT the input layer.
-
-rfkill class:
-
- * Calls a hook in a driver to effectively change the wireless
- transmitter state;
- * Keeps track of the wireless transmitter state (with help from
- the driver);
- * Generates userspace notifications (uevents) and a call to a
- notification chain (kernel) when there is a wireless transmitter
- state change;
- * Connects a wireless communications driver with the common rfkill
- control system, which, for example, allows actions such as
- "switch all bluetooth devices offline" to be carried out by
- userspace or by rfkill-input.
-
- THE RFKILL CLASS NEVER ISSUES INPUT EVENTS. THE RFKILL CLASS DOES
- NOT LISTEN TO INPUT EVENTS. NO DRIVER USING THE RFKILL CLASS SHALL
- EVER LISTEN TO, OR ACT ON RFKILL INPUT EVENTS. Doing otherwise is
- a layering violation.
-
- Most wireless data communication drivers in the kernel have just to
- implement the rfkill class API to work properly. Interfacing to the
- input layer is not often required (and is very often a *bug*) on
- wireless drivers.
-
- Platform drivers often have to attach to the input layer to *issue*
- (but never to listen to) rfkill events for rfkill switches, and also to
- the rfkill class to export a control interface for the platform rfkill
- controllers to the rfkill subsystem. This does NOT mean the rfkill
- switch is attached to a rfkill class (doing so is almost always wrong).
- It just means the same kernel module is the driver for different
- devices (rfkill switches and rfkill controllers).
-
-
-Userspace input handlers (uevents) or kernel input handlers (rfkill-input):
-
- * Implements the policy of what should happen when one of the input
- layer events related to rfkill operation is received.
- * Uses the sysfs interface (userspace) or private rfkill API calls
- to tell the devices registered with the rfkill class to change
- their state (i.e. translates the input layer event into real
- action).
-
- * rfkill-input implements EPO by handling EV_SW SW_RFKILL_ALL 0
- (power off all transmitters) in a special way: it ignores any
- overrides and local state cache and forces all transmitters to the
- RFKILL_STATE_SOFT_BLOCKED state (including those which are already
- supposed to be BLOCKED).
- * rfkill EPO will remain active until rfkill-input receives an
- EV_SW SW_RFKILL_ALL 1 event. While the EPO is active, transmitters
- are locked in the blocked state (rfkill will refuse to unblock them).
- * rfkill-input implements different policies that the user can
- select for handling EV_SW SW_RFKILL_ALL 1. It will unlock rfkill,
- and either do nothing (leave transmitters blocked, but now unlocked),
- restore the transmitters to their state before the EPO, or unblock
- them all.
-
-Userspace uevent handler or kernel platform-specific drivers hooked to the
-rfkill notifier chain:
-
- * Taps into the rfkill notifier chain or to KOBJ_CHANGE uevents,
- in order to know when a device that is registered with the rfkill
- class changes state;
- * Issues feedback notifications to the user;
- * In the rare platforms where this is required, synthesizes an input
- event to command all *OTHER* rfkill devices to also change their
- statues when a specific rfkill device changes state.
-
-
-===============================================================================
-3: Kernel driver guidelines
-
-Remember: point-of-view is everything for a driver that connects to the rfkill
-subsystem. All the details below must be measured/perceived from the point of
-view of the specific driver being modified.
-
-The first thing one needs to know is whether his driver should be talking to
-the rfkill class or to the input layer. In rare cases (platform drivers), it
-could happen that you need to do both, as platform drivers often handle a
-variety of devices in the same driver.
-
-Do not mistake input devices for rfkill controllers. The only type of "rfkill
-switch" device that is to be registered with the rfkill class are those
-directly controlling the circuits that cause a wireless transmitter to stop
-working (or the software equivalent of them), i.e. what we call a rfkill
-controller. Every other kind of "rfkill switch" is just an input device and
-MUST NOT be registered with the rfkill class.
-
-A driver should register a device with the rfkill class when ALL of the
-following conditions are met (they define a rfkill controller):
-
-1. The device is/controls a data communications wireless transmitter;
-
-2. The kernel can interact with the hardware/firmware to CHANGE the wireless
- transmitter state (block/unblock TX operation);
-
-3. The transmitter can be made to not emit any energy when "blocked":
- rfkill is not about blocking data transmissions, it is about blocking
- energy emission;
-
-A driver should register a device with the input subsystem to issue
-rfkill-related events (KEY_WLAN, KEY_BLUETOOTH, KEY_WWAN, KEY_WIMAX,
-SW_RFKILL_ALL, etc) when ALL of the folowing conditions are met:
-
-1. It is directly related to some physical device the user interacts with, to
- command the O.S./firmware/hardware to enable/disable a data communications
- wireless transmitter.
-
- Examples of the physical device are: buttons, keys and switches the user
- will press/touch/slide/switch to enable or disable the wireless
- communication device.
-
-2. It is NOT slaved to another device, i.e. there is no other device that
- issues rfkill-related input events in preference to this one.
-
- Please refer to the corner cases and examples section for more details.
-
-When in doubt, do not issue input events. For drivers that should generate
-input events in some platforms, but not in others (e.g. b43), the best solution
-is to NEVER generate input events in the first place. That work should be
-deferred to a platform-specific kernel module (which will know when to generate
-events through the rfkill notifier chain) or to userspace. This avoids the
-usual maintenance problems with DMI whitelisting.
-
-
-Corner cases and examples:
-====================================
-
-1. If the device is an input device that, because of hardware or firmware,
-causes wireless transmitters to be blocked regardless of the kernel's will, it
-is still just an input device, and NOT to be registered with the rfkill class.
-
-2. If the wireless transmitter switch control is read-only, it is an input
-device and not to be registered with the rfkill class (and maybe not to be made
-an input layer event source either, see below).
-
-3. If there is some other device driver *closer* to the actual hardware the
-user interacted with (the button/switch/key) to issue an input event, THAT is
-the device driver that should be issuing input events.
-
-E.g:
- [RFKILL slider switch] -- [GPIO hardware] -- [WLAN card rf-kill input]
- (platform driver) (wireless card driver)
-
-The user is closer to the RFKILL slide switch plaform driver, so the driver
-which must issue input events is the platform driver looking at the GPIO
-hardware, and NEVER the wireless card driver (which is just a slave). It is
-very likely that there are other leaves than just the WLAN card rf-kill input
-(e.g. a bluetooth card, etc)...
-
-On the other hand, some embedded devices do this:
-
- [RFKILL slider switch] -- [WLAN card rf-kill input]
- (wireless card driver)
-
-In this situation, the wireless card driver *could* register itself as an input
-device and issue rf-kill related input events... but in order to AVOID the need
-for DMI whitelisting, the wireless card driver does NOT do it. Userspace (HAL)
-or a platform driver (that exists only on these embedded devices) will do the
-dirty job of issuing the input events.
-
-
-COMMON MISTAKES in kernel drivers, related to rfkill:
-====================================
-
-1. NEVER confuse input device keys and buttons with input device switches.
-
- 1a. Switches are always set or reset. They report the current state
- (on position or off position).
-
- 1b. Keys and buttons are either in the pressed or not-pressed state, and
- that's it. A "button" that latches down when you press it, and
- unlatches when you press it again is in fact a switch as far as input
- devices go.
-
-Add the SW_* events you need for switches, do NOT try to emulate a button using
-KEY_* events just because there is no such SW_* event yet. Do NOT try to use,
-for example, KEY_BLUETOOTH when you should be using SW_BLUETOOTH instead.
-
-2. Input device switches (sources of EV_SW events) DO store their current state
-(so you *must* initialize it by issuing a gratuitous input layer event on
-driver start-up and also when resuming from sleep), and that state CAN be
-queried from userspace through IOCTLs. There is no sysfs interface for this,
-but that doesn't mean you should break things trying to hook it to the rfkill
-class to get a sysfs interface :-)
-
-3. Do not issue *_RFKILL_ALL events by default, unless you are sure it is the
-correct event for your switch/button. These events are emergency power-off
-events when they are trying to turn the transmitters off. An example of an
-input device which SHOULD generate *_RFKILL_ALL events is the wireless-kill
-switch in a laptop which is NOT a hotkey, but a real sliding/rocker switch.
-An example of an input device which SHOULD NOT generate *_RFKILL_ALL events by
-default, is any sort of hot key that is type-specific (e.g. the one for WLAN).
-
-
-3.1 Guidelines for wireless device drivers
-------------------------------------------
-
-(in this text, rfkill->foo means the foo field of struct rfkill).
-
-1. Each independent transmitter in a wireless device (usually there is only one
-transmitter per device) should have a SINGLE rfkill class attached to it.
-
-2. If the device does not have any sort of hardware assistance to allow the
-driver to rfkill the device, the driver should emulate it by taking all actions
-required to silence the transmitter.
-
-3. If it is impossible to silence the transmitter (i.e. it still emits energy,
-even if it is just in brief pulses, when there is no data to transmit and there
-is no hardware support to turn it off) do NOT lie to the users. Do not attach
-it to a rfkill class. The rfkill subsystem does not deal with data
-transmission, it deals with energy emission. If the transmitter is emitting
-energy, it is not blocked in rfkill terms.
-
-4. It doesn't matter if the device has multiple rfkill input lines affecting
-the same transmitter, their combined state is to be exported as a single state
-per transmitter (see rule 1).
-
-This rule exists because users of the rfkill subsystem expect to get (and set,
-when possible) the overall transmitter rfkill state, not of a particular rfkill
-line.
-
-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.
+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".

-3. Deactivates the soft rfkill line if none of the hardware rfkill lines are
-active and the caller asked for RFKILL_STATE_UNBLOCKED.
+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.

-===============================================================================
-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.
+The rfkill-input code implements a basic response to rfkill buttons -- it
+implements turning on/off all devices of a certain class (or all).

-The 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).
+When the device is hard-blocked (either by a call to rfkill_set_hw_state()
+or from query_hw_block) then set_block() will not be invoked. When the device
+is hard-unblocked again, rfkill drivers need to take into account the return
+value from rfkill_set_hw_state() -- it indicates whether the device is still
+soft-blocked or not. Some devices can actually keep track of soft blocks,
+for those you can call rfkill_has_sw_block_memory() and will then get soft
+block notifications while hard-blocked.

-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.
+The entire functionality is spread over more than one subsystem:

-===============================================================================
-5: Userspace support
+ * The kernel input layer generates KEY_WWAN, KEY_WLAN etc. and
+ SW_RFKILL_ALL -- when the user presses a button. Drivers for radio
+ transmitters generally do not register to the input layer, unless the
+ device really provides an input device (i.e. a button that has no
+ effect other than generating a button press event)

-rfkill devices issue uevents (with an action of "change"), with the following
-environment variables set:
+ * The rfkill-input code hooks up to these events and switches the soft-block
+ of the various radio transmitters, depending on the button type.

-RFKILL_NAME
-RFKILL_STATE
-RFKILL_TYPE
+ * The rfkill drivers turn off/on their transmitters as requested.

-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.
+ * The rfkill class will generate userspace notifications (uevents) to tell
+ userspace what the current state is.

-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.
+3. Kernel driver guidelines

-Userspace cannot assume it is the only source of control for rfkill switches.
-Their state can change due to firmware actions, direct user actions, and the
-rfkill-input EPO override for *_RFKILL_ALL.

-When rfkill-input is not active, userspace must initiate a rfkill status
-change by writing to the "state" attribute in order for anything to happen.
+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.

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

-The following sysfs entries will be created:
+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.
+
+
+
+4. Kernel API
+
+To build a driver with rfkill subsystem support, the driver should depend on
+(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).
+
+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.
+
+
+
+5. Userspace support
+
+The following sysfs entries exist for every rfkill device:

name: Name assigned by driver to this key (interface or driver name).
type: Name of the key type ("wlan", "bluetooth", etc).
state: Current state of the transmitter
0: RFKILL_STATE_SOFT_BLOCKED
- transmitter is forced off, but one can override it
- by a write to the state attribute;
+ transmitter is turned off by software
1: RFKILL_STATE_UNBLOCKED
- transmiter is NOT forced off, and may operate if
- all other conditions for such operation are met
- (such as interface is up and configured, etc);
+ transmiter is (potentially) active
2: RFKILL_STATE_HARD_BLOCKED
transmitter is forced off by something outside of
- the driver's control. One cannot set a device to
- this state through writes to the state attribute;
- claim: 1: Userspace handles events, 0: Kernel handles events
-
-Both the "state" and "claim" entries are also writable. For the "state" entry
-this means that when 1 or 0 is written, the device rfkill state (if not yet in
-the requested state), will be will be toggled accordingly.
-
-For the "claim" entry writing 1 to it means that the kernel no longer handles
-key events even though RFKILL_INPUT input was enabled. When "claim" has been
-set to 0, userspace should make sure that it listens for the input events or
-check the sysfs "state" entry regularly to correctly perform the required tasks
-when the rkfill key is pressed.
-
-A note about input devices and EV_SW events:
-
-In order to know the current state of an input device switch (like
-SW_RFKILL_ALL), you will need to use an IOCTL. That information is not
-available through sysfs in a generic way at this time, and it is not available
-through the rfkill class AT ALL.
+ the driver's control.
+ claim: 0: Kernel handles events (currently always reads that value)
+
+rfkill devices also issue uevents (with an action of "change"), with the
+following environment variables set:
+
+RFKILL_NAME
+RFKILL_STATE
+RFKILL_TYPE
+
+The contents of these variables corresponds to the "name", "state" and
+"type" sysfs files explained above.
--- wireless-testing.orig/net/rfkill/Kconfig 2009-03-31 11:13:07.000000000 +0200
+++ wireless-testing/net/rfkill/Kconfig 2009-03-31 11:48:55.000000000 +0200
@@ -10,22 +10,8 @@ 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
default y
-
--- wireless-testing.orig/net/rfkill/Makefile 2009-03-31 11:13:07.000000000 +0200
+++ wireless-testing/net/rfkill/Makefile 2009-03-31 11:48:55.000000000 +0200
@@ -2,5 +2,5 @@
# Makefile for the RF switch subsystem.
#

-obj-$(CONFIG_RFKILL) += rfkill.o
-obj-$(CONFIG_RFKILL_INPUT) += rfkill-input.o
+rfkill-y += core.o input.o
+obj-$(CONFIG_RFKILL) += rfkill.o
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/rfkill/core.c 2009-03-31 20:59:29.000000000 +0200
@@ -0,0 +1,780 @@
+/*
+ * Copyright (C) 2006 - 2007 Ivo van Doorn
+ * Copyright (C) 2007 Dmitry Torokhov
+ * Copyright 2009 Johannes Berg <[email protected]>
+ *
+ * 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 "rfkill.h"
+
+#define POLL_INTERVAL (5 * HZ)
+
+struct rfkill {
+ const char *name;
+ enum rfkill_type type;
+
+ unsigned long blocked;
+
+ bool registered;
+ bool has_sw_block_memory;
+
+ 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;
+};
+#define to_rfkill(d) container_of(d, struct rfkill, dev)
+
+
+
+MODULE_AUTHOR("Ivo van Doorn <[email protected]>");
+MODULE_AUTHOR("Johannes Berg <[email protected]>");
+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 = 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_activate(struct led_classdev *led)
+{
+ struct rfkill *rfkill;
+ struct led_trigger *trigger;
+
+ rfkill = container_of(led->trigger, struct rfkill, led_trigger);
+ trigger = &rfkill->led_trigger;
+
+ if (rfkill->blocked)
+ led_trigger_event(trigger, LED_OFF);
+ else
+ led_trigger_event(trigger, LED_FULL);
+}
+
+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 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)
+ return;
+
+ kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE);
+}
+
+static bool __rfkill_set_hw_state(struct rfkill *rfkill,
+ bool blocked, bool *change)
+{
+ bool prev;
+
+ BUG_ON(!rfkill);
+
+ if (blocked)
+ prev = !!test_and_set_bit(RFKILL_BLOCK_HW_BIT,
+ &rfkill->blocked);
+ else
+ prev = !!test_and_clear_bit(RFKILL_BLOCK_HW_BIT,
+ &rfkill->blocked);
+
+ *change = prev != blocked;
+
+ return blocked || !!test_bit(RFKILL_BLOCK_SW_BIT, &rfkill->blocked);
+}
+
+/**
+ * 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)
+{
+ bool prev, change;
+
+ /*
+ * 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_hw_block) {
+ __rfkill_set_hw_state(
+ rfkill,
+ rfkill->ops->query_hw_block(rfkill->data),
+ &change);
+ if (change)
+ rfkill_uevent(rfkill);
+ }
+
+ if (blocked)
+ prev = !!test_and_set_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+ else
+ prev = !!test_and_clear_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+
+ /*
+ * HW already blocked, so nothing changes for now
+ * (unless device has memory for software blocks)
+ */
+ if (!rfkill->has_sw_block_memory &&
+ test_bit(RFKILL_BLOCK_HW_BIT, &rfkill->blocked))
+ return;
+
+ if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP))
+ return;
+
+ if (rfkill->ops->set_block(rfkill->data, blocked)) {
+ /* failed -- reset status */
+ if (prev)
+ set_bit(RFKILL_BLOCK_SW_BIT, &rfkill->blocked);
+ else
+ clear_bit(RFKILL_BLOCK_SW_BIT, &rfkill->blocked);
+ }
+
+ 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);
+}
+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)
+ 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 (change)
+ schedule_work(&rfkill->uevent_work);
+
+ return ret;
+}
+EXPORT_SYMBOL(rfkill_set_hw_state);
+
+bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
+{
+ bool prev, hwblock;
+
+ BUG_ON(!rfkill);
+
+ if (blocked)
+ prev = !!test_and_set_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+ else
+ prev = !!test_and_clear_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+
+ hwblock = !!test_bit(RFKILL_BLOCK_HW_BIT, &rfkill->blocked);
+
+ if (prev != blocked && !hwblock)
+ schedule_work(&rfkill->uevent_work);
+
+ return blocked || hwblock;
+}
+EXPORT_SYMBOL(rfkill_set_sw_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();
+ }
+
+ 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 *blocked)
+{
+ if (test_bit(RFKILL_BLOCK_HW_BIT, blocked))
+ return RFKILL_USER_STATE_HARD_BLOCKED;
+ if (test_bit(RFKILL_BLOCK_SW_BIT, blocked))
+ 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);
+
+ return sprintf(buf, "%d\n", user_state_from_blocked(&rfkill->blocked));
+}
+
+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);
+ 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",
+ user_state_from_blocked(&rfkill->blocked));
+ return error;
+}
+
+void rfkill_pause_polling(struct rfkill *rfkill)
+{
+ BUG_ON(!rfkill);
+
+ if (!rfkill->ops->poll_hw_block)
+ 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_hw_block)
+ 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);
+
+ return 0;
+}
+
+static int rfkill_resume(struct device *dev)
+{
+ struct rfkill *rfkill = to_rfkill(dev);
+
+ 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;
+
+ 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;
+ bool change, hwblocked;
+
+ rfkill = container_of(work, struct rfkill, poll_work.work);
+
+ /* poll hardware state */
+ hwblocked = rfkill->ops->poll_hw_block(rfkill->data);
+
+ /*
+ * Set hardware state and tell driver to unblock if
+ * it wasn't software-blocked -- driver doesn't need
+ * to keep track of the current software block state
+ * that way.
+ * If that fails set the software block bit.
+ */
+ if (!__rfkill_set_hw_state(rfkill, hwblocked, &change))
+ if (rfkill->ops->set_block(rfkill->data, false)) {
+ change = true;
+ set_bit(RFKILL_BLOCK_SW_BIT, &rfkill->blocked);
+ }
+
+ if (change)
+ rfkill_uevent(rfkill);
+
+ 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);
+}
+
+void rfkill_has_sw_block_memory(struct rfkill *rfkill)
+{
+ BUG_ON(!rfkill);
+ BUG_ON(rfkill->registered);
+
+ rfkill->has_sw_block_memory = true;
+}
+EXPORT_SYMBOL(rfkill_has_sw_block_memory);
+
+int __must_check rfkill_register(struct rfkill *rfkill)
+{
+ static unsigned long rfkill_no = 0;
+ 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_hw_block) {
+ 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);
+
+ rfkill_set_block(rfkill, rfkill_global_states[rfkill->type].cur);
+
+ 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_hw_block)
+ cancel_delayed_work_sync(&rfkill->poll_work);
+
+ cancel_work_sync(&rfkill->uevent_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;
+
+ error = rfkill_handler_init();
+ if (error)
+ class_unregister(&rfkill_class);
+
+ out:
+ return error;
+}
+subsys_initcall(rfkill_init);
+
+static void __exit rfkill_exit(void)
+{
+ rfkill_handler_exit();
+ class_unregister(&rfkill_class);
+}
+module_exit(rfkill_exit);
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/rfkill/input.c 2009-03-31 15:28:20.000000000 +0200
@@ -0,0 +1,342 @@
+/*
+ * Input layer to RF Kill interface connector
+ *
+ * Copyright (c) 2007 Dmitry Torokhov
+ * Copyright 2009 Johannes Berg <[email protected]>
+ *
+ * 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))
+ break;
+ 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);
+}
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/rfkill/rfkill.h 2009-03-31 11:48:55.000000000 +0200
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2007 Ivo van Doorn
+ * Copyright 2009 Johannes Berg <[email protected]>
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef __RFKILL_INPUT_H
+#define __RFKILL_INPUT_H
+
+/* 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);
+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 */
--- wireless-testing.orig/MAINTAINERS 2009-03-31 11:13:09.000000000 +0200
+++ wireless-testing/MAINTAINERS 2009-03-31 11:48:55.000000000 +0200
@@ -3667,9 +3667,9 @@ L: [email protected]
S: Supported

RFKILL
-P: Ivo van Doorn
-M: [email protected]
-L: [email protected]
+P: Johannes Berg
+M: [email protected]
+L: [email protected]
S: Maintained
F: net/rfkill

--- wireless-testing.orig/include/linux/Kbuild 2009-03-31 11:13:09.000000000 +0200
+++ wireless-testing/include/linux/Kbuild 2009-03-31 11:48:55.000000000 +0200
@@ -310,6 +310,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
--- wireless-testing.orig/drivers/net/wireless/ath9k/ath9k.h 2009-03-31 11:13:09.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/ath9k.h 2009-03-31 11:48:55.000000000 +0200
@@ -486,12 +486,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];
};

@@ -536,8 +533,6 @@ struct ath_rfkill {
#define SC_OP_RXFLUSH BIT(8)
#define SC_OP_LED_ASSOCIATED BIT(9)
#define SC_OP_RFKILL_REGISTERED BIT(10)
-#define SC_OP_RFKILL_SW_BLOCKED BIT(11)
-#define SC_OP_RFKILL_HW_BLOCKED BIT(12)
#define SC_OP_WAIT_FOR_BEACON BIT(13)
#define SC_OP_LED_ON BIT(14)
#define SC_OP_SCANNING BIT(15)
--- wireless-testing.orig/drivers/net/wireless/ath9k/pci.c 2009-03-31 11:13:09.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/pci.c 2009-03-31 11:48:55.000000000 +0200
@@ -227,11 +227,6 @@ static int ath_pci_suspend(struct pci_de

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
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;
}

--- wireless-testing.orig/drivers/net/wireless/b43/Kconfig 2009-03-31 11:48:45.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/Kconfig 2009-03-31 11:48:55.000000000 +0200
@@ -103,7 +103,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

config B43_DEBUG
--- wireless-testing.orig/drivers/net/wireless/b43/rfkill.h 2009-03-31 11:13:08.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/rfkill.h 2009-03-31 11:48:55.000000000 +0200
@@ -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 */
--- wireless-testing.orig/drivers/net/wireless/b43legacy/Kconfig 2009-03-31 11:13:08.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/Kconfig 2009-03-31 11:48:55.000000000 +0200
@@ -48,7 +48,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

config B43LEGACY_DEBUG
--- wireless-testing.orig/drivers/net/wireless/b43legacy/leds.c 2009-03-31 11:13:08.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/leds.c 2009-03-31 11:48:55.000000000 +0200
@@ -86,7 +86,8 @@ static void b43legacy_led_brightness_set

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;
--- wireless-testing.orig/drivers/net/wireless/b43legacy/rfkill.h 2009-03-31 11:13:09.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/rfkill.h 2009-03-31 11:48:55.000000000 +0200
@@ -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 */
--- wireless-testing.orig/drivers/net/wireless/b43/leds.c 2009-03-31 11:13:08.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/leds.c 2009-03-31 11:48:55.000000000 +0200
@@ -87,7 +87,7 @@ static void b43_led_brightness_set(struc
}

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;
--- wireless-testing.orig/drivers/net/wireless/iwlwifi/Kconfig 2009-03-31 11:13:09.000000000 +0200
+++ wireless-testing/drivers/net/wireless/iwlwifi/Kconfig 2009-03-31 11:48:55.000000000 +0200
@@ -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"
--- wireless-testing.orig/drivers/net/wireless/b43/phy_lp.c 2009-03-31 11:13:08.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_lp.c 2009-03-31 11:48:55.000000000 +0200
@@ -488,7 +488,7 @@ static void b43_lpphy_op_radio_write(str
}

static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev,
- enum rfkill_state state)
+ bool blocked)
{
//TODO
}
--- wireless-testing.orig/drivers/platform/x86/Kconfig 2009-03-31 11:13:09.000000000 +0200
+++ wireless-testing/drivers/platform/x86/Kconfig 2009-03-31 11:48:55.000000000 +0200
@@ -21,7 +21,6 @@ config ACER_WMI
depends on NEW_LEDS
depends on BACKLIGHT_CLASS_DEVICE
depends on SERIO_I8042
- depends on RFKILL
select ACPI_WMI
---help---
This is a driver for newer Acer (and Wistron) laptops. It adds
@@ -60,7 +59,6 @@ config DELL_LAPTOP
depends on DCDBAS
depends on EXPERIMENTAL
depends on BACKLIGHT_CLASS_DEVICE
- depends on RFKILL
depends on POWER_SUPPLY
default n
---help---
@@ -107,7 +105,6 @@ config HP_WMI
tristate "HP WMI extras"
depends on ACPI_WMI
depends on INPUT
- depends on RFKILL
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.
@@ -192,7 +189,6 @@ config THINKPAD_ACPI
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
@@ -305,7 +301,6 @@ config EEEPC_LAPTOP
depends on EXPERIMENTAL
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.
@@ -372,7 +367,6 @@ config ACPI_TOSHIBA
depends on INPUT
select INPUT_POLLDEV
select NET
- select RFKILL
select BACKLIGHT_CLASS_DEVICE
---help---
This driver adds support for access to certain system settings
--- wireless-testing.orig/drivers/platform/x86/acer-wmi.c 2009-03-31 11:48:54.000000000 +0200
+++ wireless-testing/drivers/platform/x86/acer-wmi.c 2009-03-31 11:48:55.000000000 +0200
@@ -939,58 +939,50 @@ static void acer_rfkill_update(struct wo

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;
@@ -1008,8 +1000,8 @@ static int acer_rfkill_init(struct devic
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);
}
}
@@ -1022,11 +1014,13 @@ static int acer_rfkill_init(struct devic
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;
}
--- wireless-testing.orig/drivers/platform/x86/dell-laptop.c 2009-03-31 11:13:09.000000000 +0200
+++ wireless-testing/drivers/platform/x86/dell-laptop.c 2009-03-31 20:37:43.000000000 +0200
@@ -174,10 +174,11 @@ dell_send_request(struct calling_interfa
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,26 @@ static int dell_rfkill_set(int radio, en
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 bool dell_rfkill_query(void *data)
{
struct calling_interface_buffer buffer;
int status;
- int new_state = RFKILL_STATE_HARD_BLOCKED;
+ int bit = (unsigned long)data + 16;

memset(&buffer, 0, sizeof(struct calling_interface_buffer));
dell_send_request(&buffer, 17, 11);
status = buffer.output[1];

- if (status & (1<<16))
- new_state = RFKILL_STATE_SOFT_BLOCKED;
-
- if (status & (1<<bit))
- *state = new_state;
- else
- *state = RFKILL_STATE_UNBLOCKED;
-
- return 0;
-}
+ if (status & BIT(bit))
+ return !!(status & BIT(16));

-static int dell_wifi_get(void *data, enum rfkill_state *state)
-{
- return dell_rfkill_get(17, state);
+ return false;
}

-static int dell_bluetooth_get(void *data, enum rfkill_state *state)
-{
- return dell_rfkill_get(18, state);
-}
-
-static int dell_wwan_get(void *data, enum rfkill_state *state)
-{
- return dell_rfkill_get(19, state);
-}
+static const struct rfkill_ops dell_rfkill_ops = {
+ .set_block = dell_rfkill_set,
+ .query_hw_block = dell_rfkill_query,
+};

static int dell_setup_rfkill(void)
{
@@ -248,36 +219,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 +257,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;
}
--- wireless-testing.orig/drivers/platform/x86/hp-wmi.c 2009-03-31 11:48:54.000000000 +0200
+++ wireless-testing/drivers/platform/x86/hp-wmi.c 2009-03-31 11:48:55.000000000 +0200
@@ -152,58 +152,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,
@@ -345,14 +333,14 @@ static void hp_wmi_notify(u32 value, voi
}
} 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);
@@ -428,31 +416,34 @@ static int __init hp_wmi_bios_setup(stru
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;
@@ -460,11 +451,15 @@ static int __init hp_wmi_bios_setup(stru

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;
@@ -474,12 +469,18 @@ static int __exit hp_wmi_bios_remove(str
{
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;
}
--- wireless-testing.orig/drivers/platform/x86/toshiba_acpi.c 2009-03-31 11:48:54.000000000 +0200
+++ wireless-testing/drivers/platform/x86/toshiba_acpi.c 2009-03-31 11:48:55.000000000 +0200
@@ -311,28 +311,20 @@ static u32 hci_get_radio_state(bool *rad
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)
{
u32 result1, result2;
u32 value;
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;
+ return -EBUSY;

- switch (state) {
- case RFKILL_STATE_UNBLOCKED:
- if (!radio_state)
- return -EPERM;
- break;
- case RFKILL_STATE_SOFT_BLOCKED:
- break;
- default:
- return -EINVAL;
- }
+ if (WARN_ON(!blocked && !radio_state))
+ return;

mutex_lock(&dev->mutex);
hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1);
@@ -340,8 +332,7 @@ static int bt_rfkill_toggle_radio(void *
mutex_unlock(&dev->mutex);

if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS)
- return -EFAULT;
-
+ return -EBUSY;
return 0;
}

@@ -365,7 +356,7 @@ static void bt_poll_rfkill(struct input_
mutex_unlock(&dev->mutex);

if (unlikely(state_changed)) {
- rfkill_force_state(dev->rfk_dev,
+ rfkill_set_hw_state(dev->rfk_dev,
new_rfk_state ?
RFKILL_STATE_SOFT_BLOCKED :
RFKILL_STATE_HARD_BLOCKED);
--- wireless-testing.orig/arch/arm/mach-pxa/tosa-bt.c 2009-03-31 11:48:53.000000000 +0200
+++ wireless-testing/arch/arm/mach-pxa/tosa-bt.c 2009-03-31 16:02:49.000000000 +0200
@@ -35,21 +35,25 @@ static void tosa_bt_off(struct tosa_bt_d
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
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
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(stru

platform_set_drvdata(dev, NULL);

- if (rfk)
+ if (rfk) {
rfkill_unregister(rfk);
+ rfkill_destroy(rfk);
+ }
rfk = NULL;

tosa_bt_off(data);
--- wireless-testing.orig/arch/arm/mach-pxa/tosa.c 2009-03-31 11:13:09.000000000 +0200
+++ wireless-testing/arch/arm/mach-pxa/tosa.c 2009-03-31 16:02:18.000000000 +0200
@@ -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>
--- wireless-testing.orig/include/net/wimax.h 2009-03-31 11:13:09.000000000 +0200
+++ wireless-testing/include/net/wimax.h 2009-03-31 11:48:55.000000000 +0200
@@ -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)
--- wireless-testing.orig/net/wimax/op-rfkill.c 2009-03-31 11:48:54.000000000 +0200
+++ wireless-testing/net/wimax/op-rfkill.c 2009-03-31 11:48:55.000000000 +0200
@@ -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
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

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
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
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,22 @@ int wimax_rfkill_add(struct wimax_dev *w
{
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);
-
- 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);
+ d_printf(1, dev, "rfkill %p\n", rfkill);
+
+ rfkill_has_sw_block_memory(rfkill);
+
+ 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 +367,8 @@ int wimax_rfkill_add(struct wimax_dev *w
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 +387,12 @@ void wimax_rfkill_rm(struct wimax_dev *w
{
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
*
--- wireless-testing.orig/net/wimax/Kconfig 2009-03-31 11:13:08.000000000 +0200
+++ wireless-testing/net/wimax/Kconfig 2009-03-31 11:48:55.000000000 +0200
@@ -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 appropiate 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
--- wireless-testing.orig/drivers/net/usb/hso.c 2009-03-31 11:48:53.000000000 +0200
+++ wireless-testing/drivers/net/usb/hso.c 2009-03-31 11:48:55.000000000 +0200
@@ -2474,10 +2474,10 @@ static int add_net_device(struct 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);
@@ -2491,6 +2491,10 @@ static int hso_radio_toggle(void *data,
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)
@@ -2499,29 +2503,25 @@ static void hso_create_rfkill(struct hso
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;
@@ -3160,8 +3160,10 @@ static void hso_free_interface(struct us
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]);
}
}



Subject: Re: [RFC] rfkill: rewrite

So, far, it looks quite good. A few initial comments that I want to get out
ASAP (I am bound to have many more after I take a first try at convert
thinkpad-acpi to the new API).

On Sun, 29 Mar 2009, Johannes Berg wrote:
> + * struct rfkill_ops - rfkill driver methods
> + *
> + * @poll_hw_block: poll the rfkill hardware block state (return true
> + * for blocked) -- only assign this method when you cannot
> + * do without polling
> + * @query_state: query the rfkill hardware block state (return true
> + * for blocked) -- assign this method if software events might
> + * cause hardware state changes
> + * @set_block: turn the transmitter on (blocked == false) or off
> + * (blocked == true) -- this is called only while the transmitter
> + * is not hard-blocked. This must be assigned.
> + */
> +struct rfkill_ops {
> +#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
> + bool (*poll_hw_block)(void *data);
> + bool (*query_state)(void *data);
> + void (*set_block)(void *data, bool blocked);
> #endif

Missing error handling on set_block... could you change that to:

int (*set_block)(void *data, bool blocked)

with the usual kernel error semathics (0 = no error, < 0 = specific
error)? And of course, do the error handling on the core?

I don't know about wireless network drivers, but platform drivers want
to be able to return errors, they _do_ happen when you are doing ACPI
dances: EBUSY, EIO, ENOMEM...

If something in userspace wants to block a transmitter, it better get
an error back if the transmitter could not be blocked. The opposite
(unblock) is less critical in the safety sense, but no less critical
in the usability sense, so it wants proper error feedback as well.

> +/**
> + * rfkill_register - Register a rfkill structure.
> + * @rfkill: rfkill structure to be registered
> + *
> + * This function should be called by the network driver when the rfkill

minor nit: s/network/rfkill controller/

> -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.

Are you sure you want to do away with the above definitions?

The use of 'rfkill switch' for fundamentally different things caused a
lot of confusion in the past. That's why I had to come up with
'rfkill controller', and 'rfkill line'. Maybe you can do away with
the 'rfkill line' now, but controller/switch is something that is
helpful to avoid getting input devices and non input devices mixed up.

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

2009-03-30 21:26:53

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Mon, 2009-03-30 at 18:15 -0300, Henrique de Moraes Holschuh wrote:

> > +struct rfkill_ops {
> > +#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
> > + bool (*poll_hw_block)(void *data);
> > + bool (*query_state)(void *data);
> > + void (*set_block)(void *data, bool blocked);
> > #endif
>
> Missing error handling on set_block... could you change that to:
>
> int (*set_block)(void *data, bool blocked)
>
> with the usual kernel error semathics (0 = no error, < 0 = specific
> error)? And of course, do the error handling on the core?
>
> I don't know about wireless network drivers, but platform drivers want
> to be able to return errors, they _do_ happen when you are doing ACPI
> dances: EBUSY, EIO, ENOMEM...
>
> If something in userspace wants to block a transmitter, it better get
> an error back if the transmitter could not be blocked. The opposite
> (unblock) is less critical in the safety sense, but no less critical
> in the usability sense, so it wants proper error feedback as well.

I was considering that, but the current users of the API have no chance
to act on the error. It _might_ make sense to put it back when we add
the userspace API back, but what should userspace do in that case?

OTOH if it does return an error the sw block bit probably shouldn't be
set, so I guess I'll change that.

> > +/**
> > + * rfkill_register - Register a rfkill structure.
> > + * @rfkill: rfkill structure to be registered
> > + *
> > + * This function should be called by the network driver when the rfkill
>
> minor nit: s/network/rfkill controller/

something like that maybe.

> > -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.
>
> Are you sure you want to do away with the above definitions?
>
> The use of 'rfkill switch' for fundamentally different things caused a
> lot of confusion in the past. That's why I had to come up with
> 'rfkill controller', and 'rfkill line'. Maybe you can do away with
> the 'rfkill line' now, but controller/switch is something that is
> helpful to avoid getting input devices and non input devices mixed up.

This isn't actually helpful since the terms aren't very clearly used...

I think describing it as a "transmitter driver" is much better anyway.
And we don't need any sort of terms whether it's an rfkill line, or
whatever, it just doesn't matter -- all we need to know it's
hardware-off.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-03-30 22:09:27

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Mon, 2009-03-30 at 11:40 +0200, Johannes Berg wrote:

> * tosa - done now in new patch, but not even compile-tested
> (http://johannes.sipsolutions.net/patches/kernel/all/LATEST/NNN-rfkill-rewrite.patch)

One problem here remains -- the LED trigger name was forced to be
"tosa-bt" in that code -- that is no longer possible now that struct
rfkill is opaque. I think we should simply standardise the name to
something like
"<rfkill-name>-radio"

Thoughts? I don't want to call it <..>-rfkill or rfkill-<..> because
that would seem to imply inverted polarity.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-03-30 21:27:25

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Mon, 2009-03-30 at 18:15 -0300, Henrique de Moraes Holschuh wrote:
> So, far, it looks quite good. A few initial comments that I want to get out
> ASAP (I am bound to have many more after I take a first try at convert
> thinkpad-acpi to the new API).

Eh, btw, I've been working on it -- look at
http://johannes.sipsolutions.net/patches/kernel/all/LATEST/NNN-rfkill-rewrite.patch (updated frequently)

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-03-30 21:20:57

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Mon, 2009-03-30 at 18:02 -0300, Henrique de Moraes Holschuh wrote:

> > > It is probably a good idea to have the rfkill core sanitize the
> > > attempt to set the default state, and force it to "block" if EPO is
> > > active (I don't recall if I checked for this failure mode in the old
> > > code).
> >
> > No, but it rejects any calls to set the default state when any rfkill
> > struct has been registered already. So this isn't a concern.
>
> Suppose something entirely different (an input device) issued SW_RFKILL_ALL,
> and EPO is in effect. It happened right at boot because the user turned the
> laptop on with the rfkill switch active, and that driver loaded first.
>
> Now another driver is loading, and is registering the first rfkill struct of
> type foo (say, for an USB 'foo' dongle that has no idea whatsoever of any
> platform rfkill switches), and that USB 'foo' dongle is a really well-made
> device that even has NVRAM with lots of state pertaining to 'foo', including
> a bit used to keep persistent on/off state.
>
> It will try to set the default state for type 'foo' (which might well be
> unblock) while EPO is active. This attempt to set the default for 'foo' to
> ubblock can succeed or not (whatever is easier on the rfkill core code), but
> either way it must _not_ cause any switches to be unblocked, and that
> includes any subsequent rfkill_register (because EPO _is_ active)...

Interesting scenario -- I'll have to think about that, but you're
probably right and it needs some stuff to check for epo when setting
default.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part
Subject: Re: [RFC] rfkill: rewrite

On Mon, 30 Mar 2009, Johannes Berg wrote:
> On Mon, 2009-03-30 at 10:39 -0700, Inaky Perez-Gonzalez wrote:
> > On Monday 30 March 2009, Johannes Berg wrote:
> > >
> > > * wimax
> > > -> need help, seems to report rfkill states to input device?
> > > don't understand
> >
> > Not really.
> >
> > What it does is if the device exposes a hw rfkill key, export that
> > key as an input device, as well as using it to report the state
> > change.
> >
> > So there are three main entry points:
> >
> > wimax_report_rfkill_hw() -- device driver report to stack
> >
> > device reports a change in the hw rfkill key; switch the radio to
> > whichever state AND report a key event through the input layer
>
> But reporting the key through the input layer is wrong, afaict.

Reporting the key through the input layer is correct if, and _only_ if, it
is REALLY a button/key, AND nothing else is doing it.

It is NOT to be done in anything that could be, for example, an input pin
that is tied to a GPIO output in the embedded controller of a laptop that
will be driven by a platform driver.

And if it could be both and you can't be certain, you need dmi whitelisting
or a module parameter to know which one. And default to NOT issuing input
events.

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

2009-03-30 17:41:52

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

[remove memebeam.org from CC, it doesn't have an MX]

Hi Henrique,

> There are three independent rfkill controllers in a thinkpad: UWB, WWAN,
> bluetooth. They're hierarchically slaved to the master radio-kill switch in
> the standard EPO way: if the master switch is active, all rfkill
> controllers are forced active (all radios are killed). If the master switch
> is not active, each rfkill controller can be independently set to kill or
> not their respective radio.

[snip]

Thanks for the explanation. I guess that kinda makes sense. One thing
I'm entirely sure about -- I had removed the sw default state setter --
I guess this code still requires then? I was thinking the sw default
state should be set for the devices it applies to, but it seems that if
the master rfkill switch is set to "transmitter off" then one needs to
set the default state (for all USB transmitters etc) to off, correct?

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-03-30 20:39:29

by Inaky Perez-Gonzalez

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Monday 30 March 2009, Johannes Berg wrote:
> On Mon, 2009-03-30 at 19:54 +0200, Ivo van Doorn wrote:
> > > > wimax_report_rfkill_hw() -- device driver report to stack
> > > >
> > > > device reports a change in the hw rfkill key; switch the radio to
> > > > whichever state AND report a key event through the input layer
> > >
> > > But reporting the key through the input layer is wrong, afaict.
> >
> > Why because it combines it with switching the radio?
> > In rt2x00 all key events are going through the input layer as well,
> > because it has no influence on the radio state of the device.
> > (In other words, you can still happily send and receive all
> > the data you want regardless of the key state).
>
> Yes, but my understanding is (and I've just rewritten the code, heh)
> that if the button switches the hardware kill directly then we don't
> report an input event.
>
> Except in very limited circumstances (like the thinkpad-acpi platform
> driver Henrique explained) I think doing _both_
> rfkill_force_state(hard-blocked) [in new API terms:
> rfkill_set_hw_block(true)] _and_ input_report() is incorrect.

I guess there is both history to both; Johannes, you are right, in this
case the hw rfkill kills the radio in HW and *we* send the input event.

Reason it sends the input event is because I used the rt2x00 code as
reference...

So, if you guys all agree that passing this to the input layer is not
needed,feel free to yank it.

--
Inaky


Subject: Re: [RFC] rfkill: rewrite

Hi Johannes!

On Mon, 30 Mar 2009, Johannes Berg wrote:

> On Mon, 2009-03-30 at 17:48 -0300, Henrique de Moraes Holschuh wrote:
>
> > Yes, thinkpad-acpi wants it. It uses that to make sure the bluethooth
> > and wwan rfkill _global_ state is kept across machine shutdown and
> > reboots (I can't do that for UWB because Lenovo didn't add persistence
> > for UWB for some reason).
>
> Yeah, I've added it back already :)
>
> > 1. thinkpad-acpi loads
> > 1a. reads radio states from firmware, and stores to rfkill core
> > default state for the specific radio type
> > 1b. registers the rfkill switch. The core will cause the just
> > registered rfkill switch state to be changed to match the
> > current global state for that switch type... which probably
> > is what thinkpad-acpi tried to set in 1a.
>
> Yeah -- I need to add back 1b; I removed it for now because it goes like
> this:
>
> driver - rfkill_register
> rfkill - rfkill_register
> rfkill - set radio
> driver - set radio <--- deadlock if this needs lock
>
> This I need to schedule that away.
>
> > > state should be set for the devices it applies to, but it seems that if
> > > the master rfkill switch is set to "transmitter off" then one needs to
> > > set the default state (for all USB transmitters etc) to off, correct?
> >
> > Well, the default is applied to a _type_, so it is valid for all
> > switches of that type.
>
> Right.
>
> > It is probably a good idea to have the rfkill core sanitize the
> > attempt to set the default state, and force it to "block" if EPO is
> > active (I don't recall if I checked for this failure mode in the old
> > code).
>
> No, but it rejects any calls to set the default state when any rfkill
> struct has been registered already. So this isn't a concern.

Suppose something entirely different (an input device) issued SW_RFKILL_ALL,
and EPO is in effect. It happened right at boot because the user turned the
laptop on with the rfkill switch active, and that driver loaded first.

Now another driver is loading, and is registering the first rfkill struct of
type foo (say, for an USB 'foo' dongle that has no idea whatsoever of any
platform rfkill switches), and that USB 'foo' dongle is a really well-made
device that even has NVRAM with lots of state pertaining to 'foo', including
a bit used to keep persistent on/off state.

It will try to set the default state for type 'foo' (which might well be
unblock) while EPO is active. This attempt to set the default for 'foo' to
ubblock can succeed or not (whatever is easier on the rfkill core code), but
either way it must _not_ cause any switches to be unblocked, and that
includes any subsequent rfkill_register (because EPO _is_ active)...

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

2009-03-30 09:06:33

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Sun, 2009-03-29 at 19:53 +0200, Johannes Berg wrote:
> This patch completely rewrites the rfkill core to address
> the following deficiencies:
>
> * all rfkill drivers need to implement polling where necessary
> rather than having one central implementation

One thing I was wondering about -- should we just implement polling via
a single work struct that iterates all those rfkill drivers that need
polling? I've given them each an own right now, but that seems
unnecessary.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-03-30 15:19:27

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Mon, 2009-03-30 at 10:29 -0300, Henrique de Moraes Holschuh wrote:
> On Mon, 30 Mar 2009, Johannes Berg wrote:
> > * thinkpad_acpi.c
> > -> need help, convoluted code I don't understand at all, looks like
> > another candidate for rewriting
>
> Heh. I will take care of it, obviously. As for rewriting, well, a cleanup
> is in the queue.

Heh, thanks. I appreciate any help even if the golden rule still is that
those who change the API get to fix users :)

Thinkpad in particular confuses me due to the way it uses force_state
and input. Is it like Dell in that the button press causes a soft block?

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part
Subject: Re: [RFC] rfkill: rewrite

On Mon, 30 Mar 2009, Johannes Berg wrote:
> Thanks for the explanation. I guess that kinda makes sense. One thing
> I'm entirely sure about -- I had removed the sw default state setter --
> I guess this code still requires then? I was thinking the sw default

Yes, thinkpad-acpi wants it. It uses that to make sure the bluethooth
and wwan rfkill _global_ state is kept across machine shutdown and
reboots (I can't do that for UWB because Lenovo didn't add persistence
for UWB for some reason).

Here's how it works:

0. Platform boots with the radio states that were in NVRAM

1. thinkpad-acpi loads
1a. reads radio states from firmware, and stores to rfkill core
default state for the specific radio type
1b. registers the rfkill switch. The core will cause the just
registered rfkill switch state to be changed to match the
current global state for that switch type... which probably
is what thinkpad-acpi tried to set in 1a.

(normal system lifecycle)

2. thinkpad-acpi unload (or machine shutdown via platform bus
shutdown)
2a. commands firmware to store current radio state to NVRAM


So, if thinkpad-acpi is the first rfkill driver of a certain radio
type to load, it will cause the state for that radio type to be
persistent. If it is not the first driver to load, well, there is
nothing we can do, and the radio state will be whatever is the current
global state for that radio.

The rfkill core tries to keep global states, that are per type. That's
why the default is per type (and not per rfkill controller).

Without it, all rfkill types will initally be either blocked or
unblocked, depending on a module parameter for the rfkill core.

> state should be set for the devices it applies to, but it seems that if
> the master rfkill switch is set to "transmitter off" then one needs to
> set the default state (for all USB transmitters etc) to off, correct?

Well, the default is applied to a _type_, so it is valid for all
switches of that type.

It is probably a good idea to have the rfkill core sanitize the
attempt to set the default state, and force it to "block" if EPO is
active (I don't recall if I checked for this failure mode in the old
code).

On a _thinkpad_, the state stored in NVRAM will be overriden by the
master switch, because the driver gets that information very
indirectly (the firmware sets the initial rfkill controller state from
NVRAM, and the driver just reads the rfkill controlled state at
startup -- and if the master switch is active, the driver will read
all rfkill controllers as blocked). A driver that could get the
persistent state directly from NVRAM might not be so limited.

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

2009-03-30 10:39:35

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Mon, 2009-03-30 at 11:57 +0200, Johannes Berg wrote:

> > > One thing I was wondering about -- should we just implement polling via
> > > a single work struct that iterates all those rfkill drivers that need
> > > polling? I've given them each an own right now, but that seems
> > > unnecessary.
> >
> > If it doesn't add new locking problems (global locks, etc...) that'd be OK.
>
> Hm, indeed, it probably would -- rfkill_register needs a global lock
> while being called from the driver, and the polling would need the same
> lock while calling into the driver.

We can work around that by cancelling the work whenever we want to
modify the list -- that way the work doesn't need to take any locks. Not
sure it's worth it though, that makes the code harder to follow.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-03-31 18:32:34

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC/RFT v2] rfkill: rewrite

On Tue, 2009-03-31 at 13:20 -0500, Larry Finger wrote:

> I was alerted to this problem when I was unable to shut down or reboot normally.
> Those problems are fixed with the patch

Indeed, thanks. There's another, similar, bug in another function --
I've updated the patch on my website. I'm going to implement the
"initial state" thing now and then resend.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-03-30 22:07:41

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Mon, 2009-03-30 at 11:40 +0200, Johannes Berg wrote:
> TODO:

> * wimax
> -> need help, seems to report rfkill states to input device?
> don't understand

Ok, done.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-03-30 23:21:24

by Larry Finger

[permalink] [raw]
Subject: Re: [RFC/RFT v2] rfkill: rewrite

Johannes Berg wrote:
> This patch completely rewrites the rfkill core to address
> the following deficiencies:
>
> * all rfkill drivers need to implement polling where necessary
> rather than having one central implementation
>
> * updating the rfkill state cannot be done from arbitrary
> contexts, forcing drivers to use schedule_work and requiring
> lots of code
>
> * rfkill drivers need to keep track of soft/hard blocked
> internally -- the core should do this
>
> * the rfkill API has many unexpected quirks, for example being
> asymmetric wrt. alloc/free and register/unregister
>
> * rfkill can call back into a driver from within a function the
> driver called -- this is prone to deadlocks and generally
> should be avoided
>
> * rfkill-input pointlessly is a separate module
>
> * drivers need to #ifdef rfkill functions (unless they want to
> depend on or select RFKILL) -- rfkill should provide inlines
> that do nothing if it isn't compiled in
>
> * the rfkill structure is not opaque -- drivers need to initialise
> it correctly (lots of sanity checking code required) -- instead
> force drivers to pass the right variables to rfkill_alloc()
>
> * the documentation is hard to read because it always assumes the
> reader is completely clueless and contains way TOO MANY CAPS
>
> * the rfkill code needlessly uses a lot of locks and atomic
> operations in locked sections
>
> Signed-off-by: Johannes Berg <[email protected]>
> ---
> TODO
> * convert toshiba, thinkpad
> * compile-test tosa conversion
> * figure out tosa-default LED trigger thing
> * apply default state to devices after being registered
> * test
>
> Requires the other rfkill patches -- which you can also get at
> http://johannes.sipsolutions.net/patches/kernel/all/LATEST/

Initial testing shows at least two problems with b43legacy (and probably b43 as
well):

(1) On bootup with my radio switch OFF, the driver ignored that switch and
associated with my AP. Only when the switch was turned on and off again did the
driver log the expected "switch off" message".

(2) The radio LED is on no matter the state of the switch.

Larry

Subject: Re: [RFC] rfkill: rewrite

On Sun, 29 Mar 2009, Ivo van Doorn wrote:
> Just for the record, as long as you (or somebody else) takes over maintainership
> over rfkill after these changes, I have no objections against the changes. Luckily
> you already updated the Maintainers entry, so I'm happy. :)
>
> I know rfkill was poorly designed and I simply lacked the time (and perhaps
> some interest as well) to continue with it. The changes you propose sound
> reasonable so I hope it will turn rfkill really into something which it was supposed
> to be from the start.

Agreed. I never wanted to do so much work on rfkill, either... I just
wanted something I could use in thinkpad-acpi, but that proved to be a lot
of work, as once you start, you want to finish it.

I will go over the new proposed rfkill to check if it is in a state that is
comfortable for drivers like thinkpad-acpi to use, and offer comments about
it.

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

2009-03-30 09:41:01

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

TODO:

* toshiba_acpi.c
-> need help, seems to abuse input polldev and report it as both
input and rfkill block state? at very least need info about what
hw actually does

* thinkpad_acpi.c
-> need help, convoluted code I don't understand at all, looks like
another candidate for rewriting

* wimax
-> need help, seems to report rfkill states to input device?
don't understand

* b43/b43legacy
-> compiles now, but should remove input polldev and use hw block
polling -- later

* tosa - done now in new patch, but not even compile-tested
(http://johannes.sipsolutions.net/patches/kernel/all/LATEST/NNN-rfkill-rewrite.patch)

* test
-> need help, I don't have any hardware with rfkill buttons, will see
if I can come up with a uinput rfkill "button"

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-03-31 08:11:49

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC/RFT v2] rfkill: rewrite

[dropping CCs, this is relevant to b43legacy only]

On Tue, 2009-03-31 at 10:02 +0200, Johannes Berg wrote:

> > (2) The radio LED is on no matter the state of the switch.
>
> That's strange. I'll take a look.

Hmm, has that really worked before? I'd be surprised if it has -- the
radio LED seems to be tied to mac80211's radio LED trigger, not the
rfkill LED trigger, and mac80211 knows nothing about rfkill at this
point (which I'll fix too, eventually).

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-03-30 22:17:10

by Johannes Berg

[permalink] [raw]
Subject: [RFC/RFT v2] rfkill: rewrite

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

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

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

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

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

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

* rfkill-input pointlessly is a separate module

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

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

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

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

Signed-off-by: Johannes Berg <[email protected]>
---
TODO
* convert toshiba, thinkpad
* compile-test tosa conversion
* figure out tosa-default LED trigger thing
* apply default state to devices after being registered
* test

Requires the other rfkill patches -- which you can also get at
http://johannes.sipsolutions.net/patches/kernel/all/LATEST/

Documentation/rfkill.txt | 637 +++-------------------
MAINTAINERS | 6
arch/arm/mach-pxa/tosa-bt.c | 30 -
arch/arm/mach-pxa/tosa.c | 1
drivers/net/wireless/ath9k/ath9k.h | 7
drivers/net/wireless/ath9k/main.c | 115 +---
drivers/net/wireless/ath9k/pci.c | 15
drivers/net/wireless/b43/Kconfig | 2
drivers/net/wireless/b43/leds.c | 2
drivers/net/wireless/b43/main.c | 4
drivers/net/wireless/b43/phy_a.c | 4
drivers/net/wireless/b43/phy_common.c | 17
drivers/net/wireless/b43/phy_common.h | 4
drivers/net/wireless/b43/phy_g.c | 4
drivers/net/wireless/b43/phy_lp.c | 2
drivers/net/wireless/b43/phy_n.c | 2
drivers/net/wireless/b43/rfkill.c | 120 +---
drivers/net/wireless/b43/rfkill.h | 5
drivers/net/wireless/b43legacy/Kconfig | 2
drivers/net/wireless/b43legacy/leds.c | 3
drivers/net/wireless/b43legacy/rfkill.c | 116 +---
drivers/net/wireless/b43legacy/rfkill.h | 6
drivers/net/wireless/iwlwifi/Kconfig | 5
drivers/net/wireless/iwlwifi/iwl-rfkill.c | 69 --
drivers/platform/x86/Kconfig | 6
drivers/platform/x86/acer-wmi.c | 50 -
drivers/platform/x86/dell-laptop.c | 101 +--
drivers/platform/x86/eeepc-laptop.c | 91 ---
drivers/platform/x86/hp-wmi.c | 103 +--
drivers/platform/x86/toshiba_acpi.c | 23
include/linux/Kbuild | 1
include/linux/rfkill.h | 281 +++++++---
include/net/wimax.h | 8
net/rfkill/Kconfig | 14
net/rfkill/Makefile | 4
net/rfkill/core.c | 752 ++++++++++++++++++++++++++
net/rfkill/input.c | 337 +++++++++++
net/rfkill/rfkill-input.c | 392 -------------
net/rfkill/rfkill-input.h | 21
net/rfkill/rfkill.c | 843 ------------------------------
net/rfkill/rfkill.h | 27
net/wimax/op-rfkill.c | 82 --
42 files changed, 1753 insertions(+), 2561 deletions(-)

--- wireless-testing.orig/include/linux/rfkill.h 2009-03-30 11:33:53.000000000 +0200
+++ wireless-testing/include/linux/rfkill.h 2009-03-30 23:47:45.000000000 +0200
@@ -4,6 +4,7 @@
/*
* Copyright (C) 2006 - 2007 Ivo van Doorn
* Copyright (C) 2007 Dmitry Torokhov
+ * Copyright 2009 Johannes Berg <[email protected]>
*
* 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,11 +49,13 @@

/**
* 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 ,
@@ -42,97 +63,217 @@ enum rfkill_type {
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 */
-};
+#define RFKILL_BLOCK_HW_BIT 0
+#define RFKILL_BLOCK_SW_BIT 1

/**
- * 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.
- *
- * This structure represents a RF switch located on a network device.
- */
-struct rfkill {
- const char *name;
- enum rfkill_type type;
-
- /* the mutex serializes callbacks and also protects
- * the state */
- struct mutex mutex;
- enum rfkill_state state;
- void *data;
- int (*toggle_radio)(void *data, enum rfkill_state state);
- int (*get_state)(void *data, enum rfkill_state *state);
+ * struct rfkill_ops - rfkill driver methods
+ *
+ * @poll_hw_block: poll the rfkill hardware block state (return true
+ * for blocked) -- only assign this method when you cannot
+ * do without polling
+ * @query_state: query the rfkill hardware block state (return true
+ * for blocked) -- assign this method if software events might
+ * cause hardware state changes
+ * @set_block: turn the transmitter on (blocked == false) or off
+ * (blocked == true) -- this is called only while the transmitter
+ * is not hard-blocked. This must be assigned.
+ */
+struct rfkill_ops {
+ bool (*poll_hw_block)(void *data);
+ bool (*query_state)(void *data);
+ int (*set_block)(void *data, bool blocked);
+};

-#ifdef CONFIG_RFKILL_LEDS
- struct led_trigger led_trigger;
-#endif
+/* this is opaque */
+struct rfkill;

- struct device dev;
- struct list_head node;
- enum rfkill_state state_for_resume;
-};
-#define to_rfkill(d) container_of(d, struct rfkill, dev)
+#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
+/**
+ * rfkill_alloc - allocate rfkill structure
+ * @name: name of the struct -- the string is not copied internally
+ * @parent: device that has rf switch on it
+ * @type: type of the switch (RFKILL_TYPE_*)
+ * @ops: rfkill methods
+ * @ops_data: data passed to each method
+ *
+ * This function should be called by the transmitter driver to allocate an
+ * rfkill structure.
+ */
+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 * __must_check rfkill_allocate(struct device *parent,
- enum rfkill_type type);
-void rfkill_free(struct rfkill *rfkill);
+/**
+ * rfkill_register - Register a rfkill structure.
+ * @rfkill: rfkill structure to be registered
+ *
+ * This function should be called by the transmitter driver to register
+ * the rfkill structure needs to be registered. Before calling this function
+ * the driver needs to be ready to service method calls from rfkill.
+ */
int __must_check rfkill_register(struct rfkill *rfkill);
+
+/**
+ * rfkill_pause_polling(struct rfkill *rfkill)
+ *
+ * Pause polling -- say transmitter is off for other reasons.
+ * NOTE: not necessary for suspend/resume -- in that case the
+ * core stops polling anyway
+ */
+void rfkill_pause_polling(struct rfkill *rfkill);
+
+/**
+ * rfkill_resume_polling(struct rfkill *rfkill)
+ *
+ * Pause polling -- say transmitter is off for other reasons.
+ * NOTE: not necessary for suspend/resume -- in that case the
+ * core stops polling anyway
+ */
+void rfkill_resume_polling(struct rfkill *rfkill);
+
+
+/**
+ * rfkill_unregister - Unregister a rfkill structure.
+ * @rfkill: rfkill structure to be unregistered
+ *
+ * This function should be called by the network driver during device
+ * teardown to destroy rfkill structure. Until it returns, the driver
+ * needs to be able to service method calls.
+ */
void rfkill_unregister(struct rfkill *rfkill);

-int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state);
-int rfkill_set_default(enum rfkill_type type, enum rfkill_state state);
+/**
+ * rfkill_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.
+ *
+ * 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 cannot anyway because while hard-blocked
+ * they do not receive soft-block events.
+ */
+bool 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.
+ *
+ * The function returns the combined block state (true if transmitter
+ * should be blocked).
+ */
+bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked);

/**
- * 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)
+{
+}
+
+static inline void rfkill_unregister(struct rfkill *rfkill)
{
- return (state == RFKILL_STATE_UNBLOCKED) ?
- RFKILL_STATE_SOFT_BLOCKED : RFKILL_STATE_UNBLOCKED;
}

+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_global_sw_state(const enum rfkill_type type,
+ bool blocked)
+{
+}
+#endif /* RFKILL || RFKILL_MODULE */
+
+
+
/**
* rfkill_get_led_name - Get the LED trigger name for the button's LED.
* This function might return a NULL pointer if registering of the
* LED trigger failed.
* Use this as "default_trigger" for the LED.
*/
-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_name(struct rfkill *rfkill);
#else
+static inline const char *rfkill_get_led_name(struct rfkill *rfkill)
+{
return NULL;
-#endif
}
+#endif
+
+#endif /* __KERNEL__ */

#endif /* RFKILL_H */
--- wireless-testing.orig/drivers/net/wireless/ath9k/main.c 2009-03-30 11:33:53.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/main.c 2009-03-30 23:28:55.000000000 +0200
@@ -1181,120 +1181,69 @@ static bool ath_is_rfkill_set(struct ath
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 bool ath_rfkill_poll_state(void *data)
{
struct ath_softc *sc = data;
+ bool ret = !!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 (ret)
+ ath_radio_disable(sc);
+
+ return ret;
}

/* 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_hw_block = 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);
@@ -1655,10 +1604,6 @@ int ath_attach(u16 devid, struct ath_sof
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)
@@ -2170,10 +2115,8 @@ static void ath9k_stop(struct ieee80211_
} 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);
--- wireless-testing.orig/drivers/platform/x86/eeepc-laptop.c 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/drivers/platform/x86/eeepc-laptop.c 2009-03-30 23:34:32.000000000 +0200
@@ -296,39 +296,15 @@ static int update_bl_status(struct backl
* Rfkill helpers
*/

-static int eeepc_wlan_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_WLAN, 0);
- else
- return set_acpi(CM_ASL_WLAN, 1);
-}
-
-static int eeepc_wlan_rfkill_state(void *data, enum rfkill_state *state)
-{
- if (get_acpi(CM_ASL_WLAN) == 1)
- *state = RFKILL_STATE_UNBLOCKED;
- else
- *state = RFKILL_STATE_SOFT_BLOCKED;
- return 0;
+ unsigned long asl = (unsigned long)data;
+ return set_acpi(asl, !blocked);
}

-static int eeepc_bluetooth_rfkill_set(void *data, enum rfkill_state state)
-{
- if (state == RFKILL_STATE_SOFT_BLOCKED)
- return set_acpi(CM_ASL_BLUETOOTH, 0);
- else
- return set_acpi(CM_ASL_BLUETOOTH, 1);
-}
-
-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
@@ -650,26 +626,17 @@ static int eeepc_hotk_add(struct acpi_de
printk(EEEPC_ERR "Error installing notify handler\n");

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_sw_state(ehotk->eeepc_wlan_rfkill,
+ get_acpi(CM_ASL_WLAN) != 1);
result = rfkill_register(ehotk->eeepc_wlan_rfkill);
if (result)
goto wlan_fail;
@@ -677,28 +644,17 @@ static int eeepc_hotk_add(struct acpi_de

if (get_acpi(CM_ASL_BLUETOOTH) != -1) {
ehotk->eeepc_bluetooth_rfkill =
- rfkill_allocate(&device->dev, RFKILL_TYPE_BLUETOOTH);
+ rfkill_alloc("eeepc-wlan",
+ &device->dev,
+ RFKILL_TYPE_WLAN,
+ &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_sw_state(ehotk->eeepc_bluetooth_rfkill,
+ get_acpi(CM_ASL_BLUETOOTH) != 1);
result = rfkill_register(ehotk->eeepc_bluetooth_rfkill);
if (result)
goto bluetooth_fail;
@@ -710,13 +666,10 @@ static int eeepc_hotk_add(struct acpi_de
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);
ehotk_fail:
kfree(ehotk);
ehotk = NULL;
--- wireless-testing.orig/net/rfkill/rfkill.c 2009-03-30 11:33:53.000000000 +0200
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,843 +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 <[email protected]>");
-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);
-}
-#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_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);
-
- 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_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);
-}
-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);
-
- 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);
- }
-
- 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);
--- wireless-testing.orig/net/rfkill/rfkill-input.h 2009-03-30 11:33:36.000000000 +0200
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2007 Ivo van Doorn
- */
-
-/*
- * 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.
- */
-
-#ifndef __RFKILL_INPUT_H
-#define __RFKILL_INPUT_H
-
-void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state);
-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);
-
-#endif /* __RFKILL_INPUT_H */
--- wireless-testing.orig/net/rfkill/rfkill-input.c 2009-03-30 11:33:51.000000000 +0200
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,392 +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.
- *
- * If you ever run into a situation in which you have a SW_ type rfkill
- * input device, then you can revive code that was removed in the patch
- * "rfkill-input: remove unused code".
- */
-
-#include <linux/module.h>
-#include <linux/input.h>
-#include <linux/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 <[email protected]>");
-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);
--- wireless-testing.orig/drivers/net/wireless/b43/main.c 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/main.c 2009-03-30 11:33:53.000000000 +0200
@@ -3492,7 +3492,7 @@ static int b43_op_config(struct ieee8021

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 "
@@ -3500,7 +3500,7 @@ static int b43_op_config(struct ieee8021
"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");
}
}
--- wireless-testing.orig/drivers/net/wireless/b43/phy_a.c 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_a.c 2009-03-30 11:33:53.000000000 +0200
@@ -480,11 +480,11 @@ static bool b43_aphy_op_supports_hwpctl(
}

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);
--- wireless-testing.orig/drivers/net/wireless/b43/phy_common.c 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_common.c 2009-03-30 11:33:53.000000000 +0200
@@ -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);
}
@@ -287,18 +287,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);
}

--- wireless-testing.orig/drivers/net/wireless/b43/phy_common.h 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_common.h 2009-03-30 11:33:53.000000000 +0200
@@ -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);
@@ -362,7 +362,7 @@ int b43_switch_channel(struct b43_wldev
/**
* 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.
--- wireless-testing.orig/drivers/net/wireless/b43/phy_g.c 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_g.c 2009-03-30 11:33:53.000000000 +0200
@@ -2592,7 +2592,7 @@ static bool b43_gphy_op_supports_hwpctl(
}

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(

might_sleep();

- if (state == RFKILL_STATE_UNBLOCKED) {
+ if (!blocked) {
/* Turn radio ON */
if (phy->radio_on)
return;
--- wireless-testing.orig/drivers/net/wireless/b43/phy_n.c 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_n.c 2009-03-30 11:33:53.000000000 +0200
@@ -579,7 +579,7 @@ static void b43_nphy_op_radio_write(stru
}

static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
- enum rfkill_state state)
+ bool blocked)
{//TODO
}

--- wireless-testing.orig/drivers/net/wireless/b43/rfkill.c 2009-03-30 11:33:53.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/rfkill.c 2009-03-30 23:30:38.000000000 +0200
@@ -45,75 +45,55 @@ static bool b43_is_hw_radio_enabled(stru
}

/* The poll callback for the hardware button. */
-static void b43_rfkill_poll(struct input_polled_dev *poll_dev)
+static bool b43_rfkill_poll(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)) {
mutex_unlock(&wl->mutex);
- return;
+ return false;
}
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");
}
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);
- }
+ return !enabled;
}

/* 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 (WARN_ON(!dev->radio_hw_enable && !blocked))
+ 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);

@@ -122,6 +102,11 @@ char *b43_rfkill_led_name(struct b43_wld
return rfkill_get_led_name(rfk->rfkill);
}

+static const struct rfkill_ops b43_rfkill_ops = {
+ .set_block = b43_rfkill_soft_set,
+ .poll_hw_block = b43_rfkill_poll,
+};
+
void b43_rfkill_init(struct b43_wldev *dev)
{
struct b43_wl *wl = dev->wl;
@@ -130,65 +115,26 @@ void b43_rfkill_init(struct b43_wldev *d

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 +147,7 @@ void b43_rfkill_exit(struct b43_wldev *d
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;
}
--- wireless-testing.orig/drivers/net/wireless/b43legacy/rfkill.c 2009-03-30 11:33:53.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/rfkill.c 2009-03-30 23:31:18.000000000 +0200
@@ -45,78 +45,60 @@ static bool b43legacy_is_hw_radio_enable
}

/* The poll callback for the hardware button. */
-static void b43legacy_rfkill_poll(struct input_polled_dev *poll_dev)
+static bool b43legacy_rfkill_poll(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)) {
mutex_unlock(&wl->mutex);
- return;
+ return false;
}
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");
}
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);
- }
+ return !enabled;
}

/* 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 (WARN_ON(!blocked && !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);

@@ -125,6 +107,11 @@ char *b43legacy_rfkill_led_name(struct b
return rfkill_get_led_name(rfk->rfkill);
}

+static const struct rfkill_ops b43legacy_rfkill_ops = {
+ .set_block = b43legacy_rfkill_soft_set,
+ .poll_hw_block = b43legacy_rfkill_poll,
+};
+
void b43legacy_rfkill_init(struct b43legacy_wldev *dev)
{
struct b43legacy_wl *wl = dev->wl;
@@ -133,60 +120,25 @@ void b43legacy_rfkill_init(struct b43leg

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 +151,8 @@ void b43legacy_rfkill_exit(struct b43leg
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;
}

--- wireless-testing.orig/drivers/net/wireless/iwlwifi/iwl-rfkill.c 2009-03-30 11:33:53.000000000 +0200
+++ wireless-testing/drivers/net/wireless/iwlwifi/iwl-rfkill.c 2009-03-30 23:32:52.000000000 +0200
@@ -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 (WARN_ON(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 *pri
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 *pri
}

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_
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);
--- wireless-testing.orig/Documentation/rfkill.txt 2009-03-30 11:33:53.000000000 +0200
+++ wireless-testing/Documentation/rfkill.txt 2009-03-30 11:33:53.000000000 +0200
@@ -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:
-
-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.
-
-Note that disabling the signal output means that the the transmitter is to be
-made to not emit any energy when "blocked". rfkill is not about blocking data
-transmissions, it is about blocking energy emission.
-
-The rfkill subsystem offers support for keys and switches often found on
-laptops to enable wireless devices like WiFi and Bluetooth, so that these keys
-and switches actually perform an action in all wireless devices of a given type
-attached to the system.
-
-The buttons to enable and disable the wireless transmitters are important in
-situations where the user is for example using his laptop on a location where
-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.
+1. Introduction
+2. Implementation details
+3. Kernel driver guidelines
+4. Kernel API
+5. Userspace support

-===============================================================================
-2: Implementation details
+
+1. Introduction
+
+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.
+
+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.
+
+
+
+2. Implementation details

The rfkill subsystem is composed of various components: the rfkill class, the
rfkill-input module (an input layer handler), and some specific input layer
events.

-The rfkill class provides kernel drivers with an interface that allows them to
-know when they should enable or disable a wireless network device transmitter.
-This is enabled by the CONFIG_RFKILL Kconfig option.
-
-The rfkill class support makes sure userspace will be notified of all state
-changes on rfkill devices through uevents. It provides a notification chain
-for interested parties in the kernel to also get notified of rfkill state
-changes in other drivers. It creates several sysfs entries which can be used
-by userspace. See section "Userspace support".
-
-The rfkill-input module provides the kernel with the ability to implement a
-basic response when the user presses a key or button (or toggles a switch)
-related to rfkill functionality. It is an in-kernel implementation of default
-policy of reacting to rfkill-related input events and neither mandatory nor
-required for wireless drivers to operate. It is enabled by the
-CONFIG_RFKILL_INPUT Kconfig option.
-
-rfkill-input is a rfkill-related events input layer handler. This handler will
-listen to all rfkill key events and will change the rfkill state of the
-wireless devices accordingly. With this option enabled userspace could either
-do nothing or simply perform monitoring tasks.
-
-The rfkill-input module also provides EPO (emergency power-off) functionality
-for all wireless transmitters. This function cannot be overridden, and it is
-always active. rfkill EPO is related to *_RFKILL_ALL input layer events.
-
-
-Important terms for the rfkill subsystem:
-
-In order to avoid confusion, we avoid the term "switch" in rfkill when it is
-referring to an electronic control circuit that enables or disables a
-transmitter. We reserve it for the physical device a human manipulates
-(which is an input device, by the way):
-
-rfkill switch:
-
- A physical device a human manipulates. Its state can be perceived by
- the kernel either directly (through a GPIO pin, ACPI GPE) or by its
- effect on a rfkill line of a wireless device.
-
-rfkill controller:
-
- A hardware circuit that controls the state of a rfkill line, which a
- kernel driver can interact with *to modify* that state (i.e. it has
- either write-only or read/write access).
-
-rfkill line:
-
- An input channel (hardware or software) of a wireless device, which
- causes a wireless transmitter to stop emitting energy (BLOCK) when it
- is active. Point of view is extremely important here: rfkill lines are
- always seen from the PoV of a wireless device (and its driver).
-
-soft rfkill line/software rfkill line:
-
- A rfkill line the wireless device driver can directly change the state
- of. Related to rfkill_state RFKILL_STATE_SOFT_BLOCKED.
-
-hard rfkill line/hardware rfkill line:
-
- A rfkill line that works fully in hardware or firmware, and that cannot
- be overridden by the kernel driver. The hardware device or the
- firmware just exports its status to the driver, but it is read-only.
- Related to rfkill_state RFKILL_STATE_HARD_BLOCKED.
-
-The enum rfkill_state describes the rfkill state of a transmitter:
-
-When a rfkill line or rfkill controller is in the RFKILL_STATE_UNBLOCKED state,
-the wireless transmitter (radio TX circuit for example) is *enabled*. When the
-it is in the RFKILL_STATE_SOFT_BLOCKED or RFKILL_STATE_HARD_BLOCKED, the
-wireless transmitter is to be *blocked* from operating.
-
-RFKILL_STATE_SOFT_BLOCKED indicates that a call to toggle_radio() can change
-that state. RFKILL_STATE_HARD_BLOCKED indicates that a call to toggle_radio()
-will not be able to change the state and will return with a suitable error if
-attempts are made to set the state to RFKILL_STATE_UNBLOCKED.
-
-RFKILL_STATE_HARD_BLOCKED is used by drivers to signal that the device is
-locked in the BLOCKED state by a hardwire rfkill line (typically an input pin
-that, when active, forces the transmitter to be disabled) which the driver
-CANNOT override.
-
-Full rfkill functionality requires two different subsystems to cooperate: the
-input layer and the rfkill class. The input layer issues *commands* to the
-entire system requesting that devices registered to the rfkill class change
-state. The way this interaction happens is not complex, but it is not obvious
-either:
-
-Kernel Input layer:
-
- * Generates KEY_WWAN, KEY_WLAN, KEY_BLUETOOTH, SW_RFKILL_ALL, and
- other such events when the user presses certain keys, buttons, or
- toggles certain physical switches.
-
- THE INPUT LAYER IS NEVER USED TO PROPAGATE STATUS, NOTIFICATIONS OR THE
- KIND OF STUFF AN ON-SCREEN-DISPLAY APPLICATION WOULD REPORT. It is
- used to issue *commands* for the system to change behaviour, and these
- commands may or may not be carried out by some kernel driver or
- userspace application. It follows that doing user feedback based only
- on input events is broken, as there is no guarantee that an input event
- will be acted upon.
-
- Most wireless communication device drivers implementing rfkill
- functionality MUST NOT generate these events, and have no reason to
- register themselves with the input layer. Doing otherwise is a common
- misconception. There is an API to propagate rfkill status change
- information, and it is NOT the input layer.
-
-rfkill class:
-
- * Calls a hook in a driver to effectively change the wireless
- transmitter state;
- * Keeps track of the wireless transmitter state (with help from
- the driver);
- * Generates userspace notifications (uevents) and a call to a
- notification chain (kernel) when there is a wireless transmitter
- state change;
- * Connects a wireless communications driver with the common rfkill
- control system, which, for example, allows actions such as
- "switch all bluetooth devices offline" to be carried out by
- userspace or by rfkill-input.
-
- THE RFKILL CLASS NEVER ISSUES INPUT EVENTS. THE RFKILL CLASS DOES
- NOT LISTEN TO INPUT EVENTS. NO DRIVER USING THE RFKILL CLASS SHALL
- EVER LISTEN TO, OR ACT ON RFKILL INPUT EVENTS. Doing otherwise is
- a layering violation.
-
- Most wireless data communication drivers in the kernel have just to
- implement the rfkill class API to work properly. Interfacing to the
- input layer is not often required (and is very often a *bug*) on
- wireless drivers.
-
- Platform drivers often have to attach to the input layer to *issue*
- (but never to listen to) rfkill events for rfkill switches, and also to
- the rfkill class to export a control interface for the platform rfkill
- controllers to the rfkill subsystem. This does NOT mean the rfkill
- switch is attached to a rfkill class (doing so is almost always wrong).
- It just means the same kernel module is the driver for different
- devices (rfkill switches and rfkill controllers).
-
-
-Userspace input handlers (uevents) or kernel input handlers (rfkill-input):
-
- * Implements the policy of what should happen when one of the input
- layer events related to rfkill operation is received.
- * Uses the sysfs interface (userspace) or private rfkill API calls
- to tell the devices registered with the rfkill class to change
- their state (i.e. translates the input layer event into real
- action).
-
- * rfkill-input implements EPO by handling EV_SW SW_RFKILL_ALL 0
- (power off all transmitters) in a special way: it ignores any
- overrides and local state cache and forces all transmitters to the
- RFKILL_STATE_SOFT_BLOCKED state (including those which are already
- supposed to be BLOCKED).
- * rfkill EPO will remain active until rfkill-input receives an
- EV_SW SW_RFKILL_ALL 1 event. While the EPO is active, transmitters
- are locked in the blocked state (rfkill will refuse to unblock them).
- * rfkill-input implements different policies that the user can
- select for handling EV_SW SW_RFKILL_ALL 1. It will unlock rfkill,
- and either do nothing (leave transmitters blocked, but now unlocked),
- restore the transmitters to their state before the EPO, or unblock
- them all.
-
-Userspace uevent handler or kernel platform-specific drivers hooked to the
-rfkill notifier chain:
-
- * Taps into the rfkill notifier chain or to KOBJ_CHANGE uevents,
- in order to know when a device that is registered with the rfkill
- class changes state;
- * Issues feedback notifications to the user;
- * In the rare platforms where this is required, synthesizes an input
- event to command all *OTHER* rfkill devices to also change their
- statues when a specific rfkill device changes state.
-
-
-===============================================================================
-3: Kernel driver guidelines
-
-Remember: point-of-view is everything for a driver that connects to the rfkill
-subsystem. All the details below must be measured/perceived from the point of
-view of the specific driver being modified.
-
-The first thing one needs to know is whether his driver should be talking to
-the rfkill class or to the input layer. In rare cases (platform drivers), it
-could happen that you need to do both, as platform drivers often handle a
-variety of devices in the same driver.
-
-Do not mistake input devices for rfkill controllers. The only type of "rfkill
-switch" device that is to be registered with the rfkill class are those
-directly controlling the circuits that cause a wireless transmitter to stop
-working (or the software equivalent of them), i.e. what we call a rfkill
-controller. Every other kind of "rfkill switch" is just an input device and
-MUST NOT be registered with the rfkill class.
-
-A driver should register a device with the rfkill class when ALL of the
-following conditions are met (they define a rfkill controller):
-
-1. The device is/controls a data communications wireless transmitter;
-
-2. The kernel can interact with the hardware/firmware to CHANGE the wireless
- transmitter state (block/unblock TX operation);
-
-3. The transmitter can be made to not emit any energy when "blocked":
- rfkill is not about blocking data transmissions, it is about blocking
- energy emission;
-
-A driver should register a device with the input subsystem to issue
-rfkill-related events (KEY_WLAN, KEY_BLUETOOTH, KEY_WWAN, KEY_WIMAX,
-SW_RFKILL_ALL, etc) when ALL of the folowing conditions are met:
-
-1. It is directly related to some physical device the user interacts with, to
- command the O.S./firmware/hardware to enable/disable a data communications
- wireless transmitter.
-
- Examples of the physical device are: buttons, keys and switches the user
- will press/touch/slide/switch to enable or disable the wireless
- communication device.
-
-2. It is NOT slaved to another device, i.e. there is no other device that
- issues rfkill-related input events in preference to this one.
-
- Please refer to the corner cases and examples section for more details.
-
-When in doubt, do not issue input events. For drivers that should generate
-input events in some platforms, but not in others (e.g. b43), the best solution
-is to NEVER generate input events in the first place. That work should be
-deferred to a platform-specific kernel module (which will know when to generate
-events through the rfkill notifier chain) or to userspace. This avoids the
-usual maintenance problems with DMI whitelisting.
-
-
-Corner cases and examples:
-====================================
-
-1. If the device is an input device that, because of hardware or firmware,
-causes wireless transmitters to be blocked regardless of the kernel's will, it
-is still just an input device, and NOT to be registered with the rfkill class.
-
-2. If the wireless transmitter switch control is read-only, it is an input
-device and not to be registered with the rfkill class (and maybe not to be made
-an input layer event source either, see below).
-
-3. If there is some other device driver *closer* to the actual hardware the
-user interacted with (the button/switch/key) to issue an input event, THAT is
-the device driver that should be issuing input events.
-
-E.g:
- [RFKILL slider switch] -- [GPIO hardware] -- [WLAN card rf-kill input]
- (platform driver) (wireless card driver)
-
-The user is closer to the RFKILL slide switch plaform driver, so the driver
-which must issue input events is the platform driver looking at the GPIO
-hardware, and NEVER the wireless card driver (which is just a slave). It is
-very likely that there are other leaves than just the WLAN card rf-kill input
-(e.g. a bluetooth card, etc)...
-
-On the other hand, some embedded devices do this:
-
- [RFKILL slider switch] -- [WLAN card rf-kill input]
- (wireless card driver)
-
-In this situation, the wireless card driver *could* register itself as an input
-device and issue rf-kill related input events... but in order to AVOID the need
-for DMI whitelisting, the wireless card driver does NOT do it. Userspace (HAL)
-or a platform driver (that exists only on these embedded devices) will do the
-dirty job of issuing the input events.
-
-
-COMMON MISTAKES in kernel drivers, related to rfkill:
-====================================
-
-1. NEVER confuse input device keys and buttons with input device switches.
-
- 1a. Switches are always set or reset. They report the current state
- (on position or off position).
-
- 1b. Keys and buttons are either in the pressed or not-pressed state, and
- that's it. A "button" that latches down when you press it, and
- unlatches when you press it again is in fact a switch as far as input
- devices go.
-
-Add the SW_* events you need for switches, do NOT try to emulate a button using
-KEY_* events just because there is no such SW_* event yet. Do NOT try to use,
-for example, KEY_BLUETOOTH when you should be using SW_BLUETOOTH instead.
-
-2. Input device switches (sources of EV_SW events) DO store their current state
-(so you *must* initialize it by issuing a gratuitous input layer event on
-driver start-up and also when resuming from sleep), and that state CAN be
-queried from userspace through IOCTLs. There is no sysfs interface for this,
-but that doesn't mean you should break things trying to hook it to the rfkill
-class to get a sysfs interface :-)
-
-3. Do not issue *_RFKILL_ALL events by default, unless you are sure it is the
-correct event for your switch/button. These events are emergency power-off
-events when they are trying to turn the transmitters off. An example of an
-input device which SHOULD generate *_RFKILL_ALL events is the wireless-kill
-switch in a laptop which is NOT a hotkey, but a real sliding/rocker switch.
-An example of an input device which SHOULD NOT generate *_RFKILL_ALL events by
-default, is any sort of hot key that is type-specific (e.g. the one for WLAN).
-
-
-3.1 Guidelines for wireless device drivers
-------------------------------------------
-
-(in this text, rfkill->foo means the foo field of struct rfkill).
-
-1. Each independent transmitter in a wireless device (usually there is only one
-transmitter per device) should have a SINGLE rfkill class attached to it.
-
-2. If the device does not have any sort of hardware assistance to allow the
-driver to rfkill the device, the driver should emulate it by taking all actions
-required to silence the transmitter.
-
-3. If it is impossible to silence the transmitter (i.e. it still emits energy,
-even if it is just in brief pulses, when there is no data to transmit and there
-is no hardware support to turn it off) do NOT lie to the users. Do not attach
-it to a rfkill class. The rfkill subsystem does not deal with data
-transmission, it deals with energy emission. If the transmitter is emitting
-energy, it is not blocked in rfkill terms.
-
-4. It doesn't matter if the device has multiple rfkill input lines affecting
-the same transmitter, their combined state is to be exported as a single state
-per transmitter (see rule 1).
-
-This rule exists because users of the rfkill subsystem expect to get (and set,
-when possible) the overall transmitter rfkill state, not of a particular rfkill
-line.
-
-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.
+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".

-3. Deactivates the soft rfkill line if none of the hardware rfkill lines are
-active and the caller asked for RFKILL_STATE_UNBLOCKED.
+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.

-===============================================================================
-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.
+The rfkill-input code implements a basic response to rfkill buttons -- it
+implements turning on/off all devices of a certain class (or all).

-The 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).
+When the device is hard-blocked (either by a call to rfkill_set_hw_state()
+or from query_state) then set_block() will not be invoked. When the device
+is hard-unblocked again, rfkill drivers need to take into account the return
+value from rfkill_set_hw_state() -- it indicates whether the device is still
+soft-blocked or not.

-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.
+The entire functionality is spread over more than one subsystem:

-===============================================================================
-5: Userspace support
+ * The kernel input layer generates KEY_WWAN, KEY_WLAN etc. and
+ SW_RFKILL_ALL -- when the user presses a button. Drivers for radio
+ transmitters generally do not register to the input layer, unless the
+ device really provides an input device (i.e. a button that has no
+ effect other than generating a button press event)

-rfkill devices issue uevents (with an action of "change"), with the following
-environment variables set:
+ * The rfkill-input code hooks up to these events and switches the soft-block
+ of the various radio transmitters, depending on the button type.

-RFKILL_NAME
-RFKILL_STATE
-RFKILL_TYPE
+ * The rfkill drivers turn off/on their transmitters as requested.

-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.
+ * The rfkill class will generate userspace notifications (uevents) to tell
+ userspace what the current state is.

-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.
+3. Kernel driver guidelines

-Userspace cannot assume it is the only source of control for rfkill switches.
-Their state can change due to firmware actions, direct user actions, and the
-rfkill-input EPO override for *_RFKILL_ALL.

-When rfkill-input is not active, userspace must initiate a rfkill status
-change by writing to the "state" attribute in order for anything to happen.
+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.

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

-The following sysfs entries will be created:
+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.
+
+
+
+4. Kernel API
+
+To build a driver with rfkill subsystem support, the driver should depend on
+(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).
+
+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.
+
+
+
+5. Userspace support
+
+The following sysfs entries exist for every rfkill device:

name: Name assigned by driver to this key (interface or driver name).
type: Name of the key type ("wlan", "bluetooth", etc).
state: Current state of the transmitter
0: RFKILL_STATE_SOFT_BLOCKED
- transmitter is forced off, but one can override it
- by a write to the state attribute;
+ transmitter is turned off by software
1: RFKILL_STATE_UNBLOCKED
- transmiter is NOT forced off, and may operate if
- all other conditions for such operation are met
- (such as interface is up and configured, etc);
+ transmiter is (potentially) active
2: RFKILL_STATE_HARD_BLOCKED
transmitter is forced off by something outside of
- the driver's control. One cannot set a device to
- this state through writes to the state attribute;
- claim: 1: Userspace handles events, 0: Kernel handles events
-
-Both the "state" and "claim" entries are also writable. For the "state" entry
-this means that when 1 or 0 is written, the device rfkill state (if not yet in
-the requested state), will be will be toggled accordingly.
-
-For the "claim" entry writing 1 to it means that the kernel no longer handles
-key events even though RFKILL_INPUT input was enabled. When "claim" has been
-set to 0, userspace should make sure that it listens for the input events or
-check the sysfs "state" entry regularly to correctly perform the required tasks
-when the rkfill key is pressed.
-
-A note about input devices and EV_SW events:
-
-In order to know the current state of an input device switch (like
-SW_RFKILL_ALL), you will need to use an IOCTL. That information is not
-available through sysfs in a generic way at this time, and it is not available
-through the rfkill class AT ALL.
+ the driver's control.
+ claim: 0: Kernel handles events (currently always reads that value)
+
+rfkill devices also issue uevents (with an action of "change"), with the
+following environment variables set:
+
+RFKILL_NAME
+RFKILL_STATE
+RFKILL_TYPE
+
+The contents of these variables corresponds to the "name", "state" and
+"type" sysfs files explained above.
--- wireless-testing.orig/net/rfkill/Kconfig 2009-03-30 11:33:35.000000000 +0200
+++ wireless-testing/net/rfkill/Kconfig 2009-03-30 11:43:21.000000000 +0200
@@ -10,22 +10,8 @@ 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
default y
-
--- wireless-testing.orig/net/rfkill/Makefile 2009-03-30 11:33:35.000000000 +0200
+++ wireless-testing/net/rfkill/Makefile 2009-03-30 11:43:37.000000000 +0200
@@ -2,5 +2,5 @@
# Makefile for the RF switch subsystem.
#

-obj-$(CONFIG_RFKILL) += rfkill.o
-obj-$(CONFIG_RFKILL_INPUT) += rfkill-input.o
+rfkill-y += core.o input.o
+obj-$(CONFIG_RFKILL) += rfkill.o
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/rfkill/core.c 2009-03-31 00:04:25.000000000 +0200
@@ -0,0 +1,752 @@
+/*
+ * Copyright (C) 2006 - 2007 Ivo van Doorn
+ * Copyright (C) 2007 Dmitry Torokhov
+ * Copyright 2009 Johannes Berg <[email protected]>
+ *
+ * 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 "rfkill.h"
+
+#define POLL_INTERVAL (5 * HZ)
+
+struct rfkill {
+ const char *name;
+ enum rfkill_type type;
+
+ unsigned long blocked;
+
+ bool registered;
+
+ const struct rfkill_ops *ops;
+ void *data;
+
+#ifdef CONFIG_RFKILL_LEDS
+ struct led_trigger led_trigger;
+#endif
+
+ struct device dev;
+ struct list_head node;
+
+ struct delayed_work poll_work;
+ struct work_struct uevent_work;
+};
+#define to_rfkill(d) container_of(d, struct rfkill, dev)
+
+
+
+MODULE_AUTHOR("Ivo van Doorn <[email protected]>");
+MODULE_AUTHOR("Johannes Berg <[email protected]>");
+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 = 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_activate(struct led_classdev *led)
+{
+ struct rfkill *rfkill;
+ struct led_trigger *trigger;
+
+ rfkill = container_of(led->trigger, struct rfkill, led_trigger);
+ trigger = &rfkill->led_trigger;
+
+ if (!led->name)
+ return;
+
+ if (rfkill->blocked)
+ led_trigger_event(trigger, LED_OFF);
+ else
+ led_trigger_event(trigger, LED_FULL);
+}
+#endif /* CONFIG_RFKILL_LEDS */
+
+static void rfkill_uevent(struct rfkill *rfkill)
+{
+ if (!rfkill->registered)
+ return;
+
+ kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE);
+}
+
+bool __rfkill_set_hw_state(struct rfkill *rfkill, bool blocked, bool *change)
+{
+ bool prev;
+
+ BUG_ON(!rfkill);
+
+ if (blocked)
+ prev = !!test_and_set_bit(RFKILL_BLOCK_HW_BIT,
+ &rfkill->blocked);
+ else
+ prev = !!test_and_clear_bit(RFKILL_BLOCK_HW_BIT,
+ &rfkill->blocked);
+
+ *change = prev != blocked;
+
+ return blocked || !!test_bit(RFKILL_BLOCK_SW_BIT, &rfkill->blocked);
+}
+
+/**
+ * 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)
+{
+ bool prev, change;
+
+ /*
+ * 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_state) {
+ __rfkill_set_hw_state(rfkill,
+ rfkill->ops->query_state(rfkill->data),
+ &change);
+ if (change)
+ rfkill_uevent(rfkill);
+ }
+
+ if (blocked)
+ prev = !!test_and_set_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+ else
+ prev = !!test_and_clear_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+
+ /* HW already blocked, so nothing changes */
+ if (test_bit(RFKILL_BLOCK_HW_BIT, &rfkill->blocked))
+ return;
+
+ if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP))
+ return;
+
+ if (rfkill->ops->set_block(rfkill->data, blocked)) {
+ /* failed -- reset status */
+ if (prev)
+ set_bit(RFKILL_BLOCK_SW_BIT, &rfkill->blocked);
+ else
+ clear_bit(RFKILL_BLOCK_SW_BIT, &rfkill->blocked);
+ }
+
+ 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);
+}
+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)
+ 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)
+ return;
+
+ /* too late */
+ if (rfkill_states_default_locked & BIT(type))
+ return;
+
+ rfkill_states_default_locked |= BIT(type);
+
+ rfkill_global_states[type].cur = blocked;
+ rfkill_global_states[type].def = blocked;
+
+ 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 (change)
+ schedule_work(&rfkill->uevent_work);
+
+ return ret;
+}
+EXPORT_SYMBOL(rfkill_set_hw_state);
+
+bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
+{
+ bool prev, hwblock;
+
+ BUG_ON(!rfkill);
+
+ if (blocked)
+ prev = !!test_and_set_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+ else
+ prev = !!test_and_clear_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+
+ hwblock = !!test_bit(RFKILL_BLOCK_HW_BIT, &rfkill->blocked);
+
+ if (prev != blocked && !hwblock)
+ schedule_work(&rfkill->uevent_work);
+
+ return blocked || hwblock;
+}
+EXPORT_SYMBOL(rfkill_set_sw_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();
+ }
+
+ 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 *blocked)
+{
+ if (test_bit(RFKILL_BLOCK_HW_BIT, blocked))
+ return RFKILL_USER_STATE_HARD_BLOCKED;
+ if (test_bit(RFKILL_BLOCK_SW_BIT, blocked))
+ 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);
+
+ return sprintf(buf, "%d\n", user_state_from_blocked(&rfkill->blocked));
+}
+
+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);
+ 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",
+ user_state_from_blocked(&rfkill->blocked));
+ return error;
+}
+
+void rfkill_pause_polling(struct rfkill *rfkill)
+{
+ BUG_ON(!rfkill);
+
+ if (!rfkill->ops->poll_hw_block)
+ 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_hw_block)
+ 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);
+
+ return 0;
+}
+
+static int rfkill_resume(struct device *dev)
+{
+ struct rfkill *rfkill = to_rfkill(dev);
+
+ 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;
+
+ 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 int rfkill_led_trigger_register(struct rfkill *rfkill)
+{
+#ifdef CONFIG_RFKILL_LEDS
+ rfkill->led_trigger.name = dev_name(&rfkill->dev);
+ rfkill->led_trigger.activate = rfkill_led_trigger_activate;
+ return led_trigger_register(&rfkill->led_trigger);
+#endif
+ return 0;
+}
+
+static void rfkill_led_trigger_unregister(struct rfkill *rfkill)
+{
+#ifdef CONFIG_RFKILL_LEDS
+ led_trigger_unregister(&rfkill->led_trigger);
+#endif
+}
+
+static void rfkill_poll(struct work_struct *work)
+{
+ struct rfkill *rfkill;
+ bool change, hwblocked;
+
+ rfkill = container_of(work, struct rfkill, poll_work.work);
+
+ /* poll hardware state */
+ hwblocked = rfkill->ops->poll_hw_block(rfkill->data);
+
+ /*
+ * Set hardware state and tell driver to unblock if
+ * it wasn't software-blocked -- driver doesn't need
+ * to keep track of the current software block state
+ * that way.
+ * If that fails set the software block bit.
+ */
+ if (!__rfkill_set_hw_state(rfkill, hwblocked, &change))
+ if (rfkill->ops->set_block(rfkill->data, false)) {
+ change = true;
+ set_bit(RFKILL_BLOCK_SW_BIT, &rfkill->blocked);
+ }
+
+ if (change)
+ rfkill_uevent(rfkill);
+
+ 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);
+}
+
+int __must_check rfkill_register(struct rfkill *rfkill)
+{
+ static unsigned long rfkill_no = 0;
+ 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;
+ }
+
+ /* XXX: schedule work to set default state */
+
+ 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_hw_block) {
+ 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);
+
+ 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_hw_block)
+ cancel_delayed_work_sync(&rfkill->poll_work);
+
+ cancel_work_sync(&rfkill->uevent_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);
+
+const char *rfkill_get_led_name(struct rfkill *rfkill)
+{
+ return rfkill->led_trigger.name;
+}
+EXPORT_SYMBOL(rfkill_get_led_name);
+
+
+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;
+
+ error = rfkill_handler_init();
+ if (error)
+ class_unregister(&rfkill_class);
+
+ out:
+ return error;
+}
+subsys_initcall(rfkill_init);
+
+static void __exit rfkill_exit(void)
+{
+ rfkill_handler_exit();
+ class_unregister(&rfkill_class);
+}
+module_exit(rfkill_exit);
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/rfkill/input.c 2009-03-30 20:31:14.000000000 +0200
@@ -0,0 +1,337 @@
+/*
+ * Input layer to RF Kill interface connector
+ *
+ * Copyright (c) 2007 Dmitry Torokhov
+ * Copyright 2009 Johannes Berg <[email protected]>
+ *
+ * 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);
+ }
+
+ if (rfkill_is_epo_lock_active())
+ continue;
+
+ for (i = 0; i < NUM_RFKILL_TYPES; i++) {
+ if (!rfkill_op_pending)
+ break;
+ if (!__test_and_clear_bit(i, rfkill_sw_pending))
+ break;
+ 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);
+}
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/rfkill/rfkill.h 2009-03-30 11:33:53.000000000 +0200
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2007 Ivo van Doorn
+ * Copyright 2009 Johannes Berg <[email protected]>
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef __RFKILL_INPUT_H
+#define __RFKILL_INPUT_H
+
+/* 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);
+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 */
--- wireless-testing.orig/MAINTAINERS 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/MAINTAINERS 2009-03-30 12:26:40.000000000 +0200
@@ -3667,9 +3667,9 @@ L: [email protected]
S: Supported

RFKILL
-P: Ivo van Doorn
-M: [email protected]
-L: [email protected]
+P: Johannes Berg
+M: [email protected]
+L: [email protected]
S: Maintained
F: net/rfkill

--- wireless-testing.orig/include/linux/Kbuild 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/include/linux/Kbuild 2009-03-30 11:33:53.000000000 +0200
@@ -310,6 +310,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
--- wireless-testing.orig/drivers/net/wireless/ath9k/ath9k.h 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/ath9k.h 2009-03-30 11:33:53.000000000 +0200
@@ -486,12 +486,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];
};

@@ -536,8 +533,6 @@ struct ath_rfkill {
#define SC_OP_RXFLUSH BIT(8)
#define SC_OP_LED_ASSOCIATED BIT(9)
#define SC_OP_RFKILL_REGISTERED BIT(10)
-#define SC_OP_RFKILL_SW_BLOCKED BIT(11)
-#define SC_OP_RFKILL_HW_BLOCKED BIT(12)
#define SC_OP_WAIT_FOR_BEACON BIT(13)
#define SC_OP_LED_ON BIT(14)
#define SC_OP_SCANNING BIT(15)
--- wireless-testing.orig/drivers/net/wireless/ath9k/pci.c 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath9k/pci.c 2009-03-30 11:33:53.000000000 +0200
@@ -227,11 +227,6 @@ static int ath_pci_suspend(struct pci_de

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
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;
}

--- wireless-testing.orig/drivers/net/wireless/b43/Kconfig 2009-03-30 11:33:43.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/Kconfig 2009-03-30 11:33:53.000000000 +0200
@@ -103,7 +103,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) && (INPUT_POLLDEV = y || INPUT_POLLDEV = B43)
default y

config B43_DEBUG
--- wireless-testing.orig/drivers/net/wireless/b43/rfkill.h 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/rfkill.h 2009-03-30 20:55:47.000000000 +0200
@@ -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 */
--- wireless-testing.orig/drivers/net/wireless/b43legacy/Kconfig 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/Kconfig 2009-03-30 11:33:53.000000000 +0200
@@ -48,7 +48,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) && (INPUT_POLLDEV = y || INPUT_POLLDEV = B43LEGACY)
default y

config B43LEGACY_DEBUG
--- wireless-testing.orig/drivers/net/wireless/b43legacy/leds.c 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/leds.c 2009-03-30 11:33:53.000000000 +0200
@@ -86,7 +86,8 @@ static void b43legacy_led_brightness_set

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;
--- wireless-testing.orig/drivers/net/wireless/b43legacy/rfkill.h 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/rfkill.h 2009-03-30 20:55:55.000000000 +0200
@@ -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 */
--- wireless-testing.orig/drivers/net/wireless/b43/leds.c 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/leds.c 2009-03-30 11:33:53.000000000 +0200
@@ -87,7 +87,7 @@ static void b43_led_brightness_set(struc
}

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;
--- wireless-testing.orig/drivers/net/wireless/iwlwifi/Kconfig 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/iwlwifi/Kconfig 2009-03-30 11:33:53.000000000 +0200
@@ -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"
--- wireless-testing.orig/drivers/net/wireless/b43/phy_lp.c 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_lp.c 2009-03-30 11:33:53.000000000 +0200
@@ -488,7 +488,7 @@ static void b43_lpphy_op_radio_write(str
}

static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev,
- enum rfkill_state state)
+ bool blocked)
{
//TODO
}
--- wireless-testing.orig/drivers/platform/x86/Kconfig 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/drivers/platform/x86/Kconfig 2009-03-30 11:33:53.000000000 +0200
@@ -21,7 +21,6 @@ config ACER_WMI
depends on NEW_LEDS
depends on BACKLIGHT_CLASS_DEVICE
depends on SERIO_I8042
- depends on RFKILL
select ACPI_WMI
---help---
This is a driver for newer Acer (and Wistron) laptops. It adds
@@ -60,7 +59,6 @@ config DELL_LAPTOP
depends on DCDBAS
depends on EXPERIMENTAL
depends on BACKLIGHT_CLASS_DEVICE
- depends on RFKILL
depends on POWER_SUPPLY
default n
---help---
@@ -107,7 +105,6 @@ config HP_WMI
tristate "HP WMI extras"
depends on ACPI_WMI
depends on INPUT
- depends on RFKILL
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.
@@ -192,7 +189,6 @@ config THINKPAD_ACPI
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
@@ -305,7 +301,6 @@ config EEEPC_LAPTOP
depends on EXPERIMENTAL
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.
@@ -372,7 +367,6 @@ config ACPI_TOSHIBA
depends on INPUT
select INPUT_POLLDEV
select NET
- select RFKILL
select BACKLIGHT_CLASS_DEVICE
---help---
This driver adds support for access to certain system settings
--- wireless-testing.orig/drivers/platform/x86/acer-wmi.c 2009-03-30 11:33:53.000000000 +0200
+++ wireless-testing/drivers/platform/x86/acer-wmi.c 2009-03-30 23:33:32.000000000 +0200
@@ -939,58 +939,50 @@ static void acer_rfkill_update(struct wo

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;
@@ -1008,8 +1000,8 @@ static int acer_rfkill_init(struct devic
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);
}
}
@@ -1022,11 +1014,13 @@ static int acer_rfkill_init(struct devic
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;
}
--- wireless-testing.orig/drivers/platform/x86/dell-laptop.c 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/drivers/platform/x86/dell-laptop.c 2009-03-30 23:34:06.000000000 +0200
@@ -174,10 +174,11 @@ dell_send_request(struct calling_interfa
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,26 @@ static int dell_rfkill_set(int radio, en
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 bool dell_rfkill_poll(void *data)
{
struct calling_interface_buffer buffer;
int status;
- int new_state = RFKILL_STATE_HARD_BLOCKED;
+ int bit = (unsigned long)data + 16;

memset(&buffer, 0, sizeof(struct calling_interface_buffer));
dell_send_request(&buffer, 17, 11);
status = buffer.output[1];

- if (status & (1<<16))
- new_state = RFKILL_STATE_SOFT_BLOCKED;
-
- if (status & (1<<bit))
- *state = new_state;
- else
- *state = RFKILL_STATE_UNBLOCKED;
-
- return 0;
-}
+ if (status & BIT(bit))
+ return !!(status & BIT(16));

-static int dell_wifi_get(void *data, enum rfkill_state *state)
-{
- return dell_rfkill_get(17, state);
+ return false;
}

-static int dell_bluetooth_get(void *data, enum rfkill_state *state)
-{
- return dell_rfkill_get(18, state);
-}
-
-static int dell_wwan_get(void *data, enum rfkill_state *state)
-{
- return dell_rfkill_get(19, state);
-}
+static const struct rfkill_ops dell_rfkill_ops = {
+ .set_block = dell_rfkill_set,
+ .poll_hw_block = dell_rfkill_poll,
+};

static int dell_setup_rfkill(void)
{
@@ -248,36 +219,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 +257,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;
}
--- wireless-testing.orig/drivers/platform/x86/hp-wmi.c 2009-03-30 11:33:53.000000000 +0200
+++ wireless-testing/drivers/platform/x86/hp-wmi.c 2009-03-30 23:34:46.000000000 +0200
@@ -152,58 +152,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,
@@ -345,14 +333,14 @@ static void hp_wmi_notify(u32 value, voi
}
} 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);
@@ -428,31 +416,34 @@ static int __init hp_wmi_bios_setup(stru
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;
@@ -460,11 +451,15 @@ static int __init hp_wmi_bios_setup(stru

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;
@@ -474,12 +469,18 @@ static int __exit hp_wmi_bios_remove(str
{
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;
}
--- wireless-testing.orig/drivers/platform/x86/toshiba_acpi.c 2009-03-30 11:33:53.000000000 +0200
+++ wireless-testing/drivers/platform/x86/toshiba_acpi.c 2009-03-30 23:36:43.000000000 +0200
@@ -311,28 +311,20 @@ static u32 hci_get_radio_state(bool *rad
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)
{
u32 result1, result2;
u32 value;
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;
+ return -EBUSY;

- switch (state) {
- case RFKILL_STATE_UNBLOCKED:
- if (!radio_state)
- return -EPERM;
- break;
- case RFKILL_STATE_SOFT_BLOCKED:
- break;
- default:
- return -EINVAL;
- }
+ if (WARN_ON(!blocked && !radio_state))
+ return;

mutex_lock(&dev->mutex);
hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1);
@@ -340,8 +332,7 @@ static int bt_rfkill_toggle_radio(void *
mutex_unlock(&dev->mutex);

if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS)
- return -EFAULT;
-
+ return -EBUSY;
return 0;
}

@@ -365,7 +356,7 @@ static void bt_poll_rfkill(struct input_
mutex_unlock(&dev->mutex);

if (unlikely(state_changed)) {
- rfkill_force_state(dev->rfk_dev,
+ rfkill_set_hw_state(dev->rfk_dev,
new_rfk_state ?
RFKILL_STATE_SOFT_BLOCKED :
RFKILL_STATE_HARD_BLOCKED);
--- wireless-testing.orig/arch/arm/mach-pxa/tosa-bt.c 2009-03-30 11:33:52.000000000 +0200
+++ wireless-testing/arch/arm/mach-pxa/tosa-bt.c 2009-03-30 23:28:01.000000000 +0200
@@ -35,21 +35,25 @@ static void tosa_bt_off(struct tosa_bt_d
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,19 +74,13 @@ static int tosa_bt_probe(struct platform
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
-
rc = rfkill_register(rfk);
if (rc)
goto err_rfkill;
@@ -92,9 +90,7 @@ static int tosa_bt_probe(struct platform
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 +109,10 @@ static int __devexit tosa_bt_remove(stru

platform_set_drvdata(dev, NULL);

- if (rfk)
+ if (rfk) {
rfkill_unregister(rfk);
+ rfkill_destroy(rfk);
+ }
rfk = NULL;

tosa_bt_off(data);
--- wireless-testing.orig/arch/arm/mach-pxa/tosa.c 2009-03-30 11:33:36.000000000 +0200
+++ wireless-testing/arch/arm/mach-pxa/tosa.c 2009-03-30 11:33:53.000000000 +0200
@@ -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>
--- wireless-testing.orig/include/net/wimax.h 2009-03-30 23:05:02.000000000 +0200
+++ wireless-testing/include/net/wimax.h 2009-03-30 23:05:31.000000000 +0200
@@ -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)
--- wireless-testing.orig/net/wimax/op-rfkill.c 2009-03-30 23:03:46.000000000 +0200
+++ wireless-testing/net/wimax/op-rfkill.c 2009-03-30 23:41:39.000000000 +0200
@@ -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,7 +65,6 @@
#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
@@ -99,7 +98,6 @@ void wimax_report_rfkill_hw(struct wimax
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 +110,13 @@ void wimax_report_rfkill_hw(struct wimax

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 +169,7 @@ void wimax_report_rfkill_sw(struct wimax
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 +245,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 +313,7 @@ int wimax_rfkill(struct wimax_dev *wimax
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 +341,19 @@ int wimax_rfkill_add(struct wimax_dev *w
{
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);
-
- 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);
+ d_printf(1, dev, "rfkill %p\n", 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 *w
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,8 +385,8 @@ void wimax_rfkill_rm(struct wimax_dev *w
{
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);
}




2009-03-30 17:46:17

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Mon, 2009-03-30 at 10:39 -0700, Inaky Perez-Gonzalez wrote:
> On Monday 30 March 2009, Johannes Berg wrote:
> >
> > * wimax
> > -> need help, seems to report rfkill states to input device?
> > don't understand
>
> Not really.
>
> What it does is if the device exposes a hw rfkill key, export that
> key as an input device, as well as using it to report the state
> change.
>
> So there are three main entry points:
>
> wimax_report_rfkill_hw() -- device driver report to stack
>
> device reports a change in the hw rfkill key; switch the radio to
> whichever state AND report a key event through the input layer

But reporting the key through the input layer is wrong, afaict.

> wimax_report_rfkill_sw() -- device driver report to stack
>
> device reports a change in the sw rfkill state; this is needed for
> cases where the radio is shared amongst different technologies (wifi
> and wimax, for example, in the case of the 5150). When you turn on
> Wifi, WiMAX switches off (and vice versa).
>
> As well, if from SW you flip it off, this gets called back but does
> nothing as the state is updated already.

Shouldn't that be a hard block then? Or can the software turn it back on
(and consequently turn off wifi)?

> wimax_rfkill / wimax_rfkill_toggle_radio -- user interface
>
> The user wants to flip the SW switch on/off

Right.

So let's go back to my first assertion:
"seems to report rfkill states to input device"

wimax_report_rfkill_hw:

enum rfkill_state rfkill_state;

...
input_report_key(wimax_dev->rfkill_input, KEY_WIMAX,
rfkill_state);

??

I don't see why it should have an input device at all, to be honest.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-03-30 20:44:19

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Mon, 2009-03-30 at 13:36 -0700, Inaky Perez-Gonzalez wrote:

> I guess there is both history to both; Johannes, you are right, in this
> case the hw rfkill kills the radio in HW and *we* send the input event.

Ok.

> Reason it sends the input event is because I used the rt2x00 code as
> reference...

Right, but rt2x00 doesn't kill the radio in HW.

> So, if you guys all agree that passing this to the input layer is not
> needed,feel free to yank it.

Ok. Except I don't know how to handle wimax_report_rfkill_sw() yet.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part
Subject: Re: [RFC] rfkill: rewrite

Hi Johannes!

On Mon, 30 Mar 2009, Johannes Berg wrote:

> On Mon, 2009-03-30 at 10:29 -0300, Henrique de Moraes Holschuh wrote:
> > On Mon, 30 Mar 2009, Johannes Berg wrote:
> > > * thinkpad_acpi.c
> > > -> need help, convoluted code I don't understand at all, looks like
> > > another candidate for rewriting
> >
> > Heh. I will take care of it, obviously. As for rewriting, well, a cleanup
> > is in the queue.
>
> Heh, thanks. I appreciate any help even if the golden rule still is that
> those who change the API get to fix users :)
>
> Thinkpad in particular confuses me due to the way it uses force_state
> and input. Is it like Dell in that the button press causes a soft block?

There are three independent rfkill controllers in a thinkpad: UWB, WWAN,
bluetooth. They're hierarchically slaved to the master radio-kill switch in
the standard EPO way: if the master switch is active, all rfkill
controllers are forced active (all radios are killed). If the master switch
is not active, each rfkill controller can be independently set to kill or
not their respective radio.

When the user moves the phisical radio-kill switch, the thinkpad embedded
controller hard-kills every USB-attached radio (as in: cut power and kick
off-bus), raises the hardware rfkill line on the mini-PCI/mini-PCIe slots
(which will hard-block Intel wireless devices. I think the Atheros ones
ignore it). And sends us an event over ACPI (HKEY event 7000).

That event is used by the driver as a hint to check the state of all rfkill
controllers, and sync the core (thus, it causes calls to
rfkill_force_state()). It is also sent to userspace as an input event (so
that rfkill-input can go and block other radios, such as USB dongles, etc).

It is also possible to request that hard-kill in the BIOS: the thinkpad
boots with all radios rfkilled (the BIOS tells the EC to radio-kill
everything and to keep them killed), and there is absolutely _nothing_ one
can do to unblock them, until a reboot and BIOS reconfig is done.

Anyway, thinkpad-acpi is a _hybrid_ driver as far as rfkill is concerned.
It has rfkill controllers, but it has an input device side too.

There is some highly paranoid logic in thinkpad-acpi that basically enforces
the radio-kill switch even if the firmware goes rogue on us and decides not
to do it. This is just to guarantee that all thinkpads will behave the same
way, if there is one or two models out there with a weird firmware. You can
ignore it when trying to understand the information flow in the driver, if I
ripped it out, most (all?) thinkpads would behave just the same.

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

2009-03-30 09:55:36

by Michael Büsch

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Monday 30 March 2009 11:06:27 Johannes Berg wrote:
> On Sun, 2009-03-29 at 19:53 +0200, Johannes Berg wrote:
> > This patch completely rewrites the rfkill core to address
> > the following deficiencies:
> >
> > * all rfkill drivers need to implement polling where necessary
> > rather than having one central implementation
>
> One thing I was wondering about -- should we just implement polling via
> a single work struct that iterates all those rfkill drivers that need
> polling? I've given them each an own right now, but that seems
> unnecessary.

If it doesn't add new locking problems (global locks, etc...) that'd be OK.

--
Greetings, Michael.

2009-03-30 19:01:37

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Mon, 2009-03-30 at 11:40 +0200, Johannes Berg wrote:

> * b43/b43legacy
> -> compiles now, but should remove input polldev and use hw block
> polling -- later

done

> * tosa - done now in new patch, but not even compile-tested
> (http://johannes.sipsolutions.net/patches/kernel/all/LATEST/NNN-rfkill-rewrite.patch)

need to take care of LED default trigger thing here

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-03-31 12:16:49

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC/RFT v2] rfkill: rewrite

On Tue, 2009-03-31 at 00:16 +0200, Johannes Berg wrote:
> This patch completely rewrites the rfkill core to address
> the following deficiencies:

[snip]

This one has a few stupid locking bugs, will send an updated version
later today, for now at the well-known URL.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-03-30 17:40:07

by Inaky Perez-Gonzalez

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Monday 30 March 2009, Johannes Berg wrote:
>
> * wimax
> -> need help, seems to report rfkill states to input device?
> don't understand

Not really.

What it does is if the device exposes a hw rfkill key, export that
key as an input device, as well as using it to report the state
change.

So there are three main entry points:

wimax_report_rfkill_hw() -- device driver report to stack

device reports a change in the hw rfkill key; switch the radio to
whichever state AND report a key event through the input layer

wimax_report_rfkill_sw() -- device driver report to stack

device reports a change in the sw rfkill state; this is needed for
cases where the radio is shared amongst different technologies (wifi
and wimax, for example, in the case of the 5150). When you turn on
Wifi, WiMAX switches off (and vice versa).

As well, if from SW you flip it off, this gets called back but does
nothing as the state is updated already.

wimax_rfkill / wimax_rfkill_toggle_radio -- user interface

The user wants to flip the SW switch on/off

--
Inaky

2009-03-30 18:01:33

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Mon, 2009-03-30 at 19:54 +0200, Ivo van Doorn wrote:

> > > wimax_report_rfkill_hw() -- device driver report to stack
> > >
> > > device reports a change in the hw rfkill key; switch the radio to
> > > whichever state AND report a key event through the input layer
> >
> > But reporting the key through the input layer is wrong, afaict.
>
> Why because it combines it with switching the radio?
> In rt2x00 all key events are going through the input layer as well,
> because it has no influence on the radio state of the device.
> (In other words, you can still happily send and receive all
> the data you want regardless of the key state).

Yes, but my understanding is (and I've just rewritten the code, heh)
that if the button switches the hardware kill directly then we don't
report an input event.

Except in very limited circumstances (like the thinkpad-acpi platform
driver Henrique explained) I think doing _both_
rfkill_force_state(hard-blocked) [in new API terms:
rfkill_set_hw_block(true)] _and_ input_report() is incorrect.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-03-30 09:57:29

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Mon, 2009-03-30 at 11:54 +0200, Michael Buesch wrote:
> On Monday 30 March 2009 11:06:27 Johannes Berg wrote:
> > On Sun, 2009-03-29 at 19:53 +0200, Johannes Berg wrote:
> > > This patch completely rewrites the rfkill core to address
> > > the following deficiencies:
> > >
> > > * all rfkill drivers need to implement polling where necessary
> > > rather than having one central implementation
> >
> > One thing I was wondering about -- should we just implement polling via
> > a single work struct that iterates all those rfkill drivers that need
> > polling? I've given them each an own right now, but that seems
> > unnecessary.
>
> If it doesn't add new locking problems (global locks, etc...) that'd be OK.

Hm, indeed, it probably would -- rfkill_register needs a global lock
while being called from the driver, and the polling would need the same
lock while calling into the driver.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-03-31 08:03:35

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC/RFT v2] rfkill: rewrite

On Mon, 2009-03-30 at 18:20 -0500, Larry Finger wrote:

> Initial testing shows at least two problems with b43legacy (and probably b43 as
> well):

Thanks for testing!

> (1) On bootup with my radio switch OFF, the driver ignored that switch and
> associated with my AP. Only when the switch was turned on and off again did the
> driver log the expected "switch off" message".

I think that's expected because I'm missing some code to set the default
state when a driver registers.

> (2) The radio LED is on no matter the state of the switch.

That's strange. I'll take a look.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-03-30 20:52:52

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Mon, 2009-03-30 at 17:48 -0300, Henrique de Moraes Holschuh wrote:

> Yes, thinkpad-acpi wants it. It uses that to make sure the bluethooth
> and wwan rfkill _global_ state is kept across machine shutdown and
> reboots (I can't do that for UWB because Lenovo didn't add persistence
> for UWB for some reason).

Yeah, I've added it back already :)

> 1. thinkpad-acpi loads
> 1a. reads radio states from firmware, and stores to rfkill core
> default state for the specific radio type
> 1b. registers the rfkill switch. The core will cause the just
> registered rfkill switch state to be changed to match the
> current global state for that switch type... which probably
> is what thinkpad-acpi tried to set in 1a.

Yeah -- I need to add back 1b; I removed it for now because it goes like
this:

driver - rfkill_register
rfkill - rfkill_register
rfkill - set radio
driver - set radio <--- deadlock if this needs lock

This I need to schedule that away.

> > state should be set for the devices it applies to, but it seems that if
> > the master rfkill switch is set to "transmitter off" then one needs to
> > set the default state (for all USB transmitters etc) to off, correct?
>
> Well, the default is applied to a _type_, so it is valid for all
> switches of that type.

Right.

> It is probably a good idea to have the rfkill core sanitize the
> attempt to set the default state, and force it to "block" if EPO is
> active (I don't recall if I checked for this failure mode in the old
> code).

No, but it rejects any calls to set the default state when any rfkill
struct has been registered already. So this isn't a concern.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-03-30 17:54:11

by Ivo Van Doorn

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

On Monday 30 March 2009, Johannes Berg wrote:
> On Mon, 2009-03-30 at 10:39 -0700, Inaky Perez-Gonzalez wrote:
> > On Monday 30 March 2009, Johannes Berg wrote:
> > >
> > > * wimax
> > > -> need help, seems to report rfkill states to input device?
> > > don't understand
> >
> > Not really.
> >
> > What it does is if the device exposes a hw rfkill key, export that
> > key as an input device, as well as using it to report the state
> > change.
> >
> > So there are three main entry points:
> >
> > wimax_report_rfkill_hw() -- device driver report to stack
> >
> > device reports a change in the hw rfkill key; switch the radio to
> > whichever state AND report a key event through the input layer
>
> But reporting the key through the input layer is wrong, afaict.

Why because it combines it with switching the radio?
In rt2x00 all key events are going through the input layer as well,
because it has no influence on the radio state of the device.
(In other words, you can still happily send and receive all
the data you want regardless of the key state).

Ivo

Subject: Re: [RFC] rfkill: rewrite

On Mon, 30 Mar 2009, Henrique de Moraes Holschuh wrote:
> So, far, it looks quite good. A few initial comments that I want to get out
> ASAP (I am bound to have many more after I take a first try at convert
> thinkpad-acpi to the new API).

And it would have been better if I had looked at it for an extra five
minutes :-(

> > + * @poll_hw_block: poll the rfkill hardware block state (return true
> > + * for blocked) -- only assign this method when you cannot
> > + * do without polling
> > + * @query_state: query the rfkill hardware block state (return true
> > + * for blocked) -- assign this method if software events might
> > + * cause hardware state changes
> > + * @set_block: turn the transmitter on (blocked == false) or off
> > + * (blocked == true) -- this is called only while the transmitter
> > + * is not hard-blocked. This must be assigned.
> > + */
> > +struct rfkill_ops {
> > +#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
> > + bool (*poll_hw_block)(void *data);
> > + bool (*query_state)(void *data);
> > + void (*set_block)(void *data, bool blocked);
> > #endif
>
> Missing error handling on set_block... could you change that to:

And also on the rest of the hooks. I should have noticed it sooner!

As I said, ACPI platform drivers _do_ get errors when calling the ACPI
functions, or trying to do an EC transaction. That applies to reads
just as well as it applies to writes.

I.e. I can get AE_BUSY or something else while querying the thinkpad
firmware, and then, what should I do to return the data on the
query_state (and, for that matter, poll_hw_block) hooks?

Since there can be a clear error path from userspace to the rfkill
driver for reads and writes in many situations (e.g. on all sysfs
accesses to that driver's rfkill struct), this would be a good reason to
change the hooks to allow for that error path.

There is a second reason as well to change it: the policy on what to do
when the driver cannot query the up-to-date state of the rfkill
controller should not be defined by the driver itself, it is best
defined by the rfkill core so that all drivers behave the same.

Without that, I'd have, for example, to return false (unblocked) on any
errors from thinkpad-acpi, for safety reasons (a blocked radio is never
a hazard, but an unblocked one can be). This is the kind of stuff that
a driver shouldn't need to decide by itself...


On a related note, it was not clear to me from looking only at
rfkill.txt and the linux/rfkill.h headers, how am I supposed to deal
with the software controlled component of the rfkill controller (the
hardware rfkill line is quite obvious, though). I know there is a way,
but I haven't finished reading the core code to know which one, and the
docs didn't help, so they need an extra paragraph somewhere...


Anyway, I suggest changing the hook protypes to this (which does _NOT_
change the fact that you now track hw and sw separately in the core, but
lets the rfkill driver deliver both in a single call if it needs to):

int (*poll_block_state)(void *data,
bool *is_sw_block_active,
bool *is_hw_block_active);
int (*query_block_state)(void *data,
bool *is_sw_block_active,
bool *is_hw_block_active);
int (*set_block_state)(void *data, bool do_sw_block);

With the following semanthics:

function return status: 0 if OK, <0 in case of error (i.e. standard
error semanthics).

is_sw_block_active: leave untouched if there is no sw blocking support,
otherwise change it to the up-to-date state of the sw blocking flag
(true=blocked)

is_hw_block_active: leave untouched if there is no hw blocking,
otherwise change it to the up-to-date state of the hw blocking flag
(true=blocked)

The core should ignore the is_* stuff if an error status is returned,
as a defensive approach against driver bugs: doing it that way is less
programmer-error-prone than telling people to not touch is_* in case of
an error.

do_sw_block: true if the driver should software block, false if it
should attempt to software unblock.

BTW: it is probably a good idea to document what the driver should do if
the core tries this on a radio that is hardware blocked, or if the
driver discovers the radio is hardware blocked the instant it tries to
software unblock it...


It would be nice to have the core export a way to "force" both sw and hw
states at once, as well. This lets the core optimize the issuing of
events and other internal changes and avoids bad mojo if the driver does
sw/hw force calls in the wrong order (which might cause a unblock-block
fast sequence, etc).

This would let drivers that have the worst case software blocking (it
exists, hw blocking also exists and is separate, and both sw and hw
blocking state could change behind the driver's back) work well, without
causing any extra complexity to the other drivers.


IF you don't want the hooks to return both sw and hw states, you can
just change the return type from bool to int, and explicitly document
tha the return status is:

status < 0: error
status = 0: false, implies no error
status > 0: true, implies no error

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

2009-04-30 08:54:15

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC v6] rfkill: rewrite

Hi,

On Thu, 2009-04-30 at 00:19 -0300, Henrique de Moraes Holschuh wrote:
> I am halfway on the thinkpad-acpi conversion. It is going slow because
> I have to cleanup the entire thing to have the code look even remotely
> sane now that HW and SW states are to be tracked separately by the
> backend driver, and I lost the RFKILL_STATE_* stuff.

Hmm. I didn't think it was that much of a problem for the other drivers
at least.

> Anyway, in the process (using v7 of the patch, taken from the URL you
> posted sometime ago), I noticed this:
>
> The return values for rfkill_alloc() are not the same on the stub and
> real functions. The stub function uses ERR_PTR, while the real
> function uses NULL for any errors. Please make them do the same
> thing, and it is probably a damn good idea to document in the
> kerneldoc what kind of return values one should expect when you return
> a pointer and the function can fail (i.e. NULL or ERR_PTR).

Oops, yes, will fix that. I think I intended to use ERR_PTR but then
thought it was too much of an API change. Don't really remember though.

> Also, I have a question: are hooks ever called in atomic/interrupt
> context? Just about every ACPI and I2C driver needs to sleep to do
> their magic, so I sure hope the answer is "no", otherwise life
> suddenly got a whole lot more complicated for thinkpad-acpi..

The answer is "no" unless there's a bug, this didn't really change,
they're called only from the input handler.

> Also, due to races, it is _impossible_ to assure the backend driver
> that the set_block hook will never be called when the radio is
> hard-blocked. Best to change the description of that hook to make it
> clear that it could happen, but that the rfkill core will try to
> optimize away such changes. Here's the race:
>
> A B
> rfkill_set_block
> saves state in "prev"
> ... rfkill_set_hw_state(true)
> call set_block hook
> but radio IS hard-blocked
> and the core WAS informed
> of this

Good point. I wonder what's more important -- it would be almost trivial
to change this by using a spinlock instead of atomic bit operations.

> Also, it looks like the SW state rollback done by rfkill_set_block can
> sw-unblock a radio when it shouldn't on races very similar as the one
> above, but involving sw-blocking.
>
> I don't see a way to do safe state rollback in a lockless rfkill core
> if set_sw_state can happen at any time. Please enlighten me if I am
> wrong (and I hope I am) :-(
>
> If I am not wrong, one could change rfkill_set_sw_state and
> rfkill_set_states to return an error if rfkill_set_block is on the way
> (that can be done lockless). For rfkill_set_states, document that the
> hw state WILL be updated regardless, but that the sw state might not
> be.
>
> Or one could kick the problem to the backend driver (which will have
> any required locking anyway), by giving the set_block hook the duty of
> calling set_sw_state when it succeeds. The return status might be
> kept should we ever start passing it down to other layers like
> userspace, or you can remove it.

Thanks for your analysis. It does seem like a problem, and even locking
in rfkill can't really avoid the problem [1]. However, why would we
require the driver to postpone that? Your idea is sound, but I think we
can do it in the core. It might require a spinlock there, but that's
acceptable, I didn't use atomic operations to make it faster but to make
it simpler.

* rfkill_set_block can set a flag "I'm in an update"
* a concurrent rfkill_set_sw_state will check that flag, and update the
"prev" variable instead of the real variable
* on errors, rfkill_set_block will then revert to whatever the last
set_sw_state said

That way, if the driver returns an error from ops->set_block, we will
really revert to what the driver last told us with set_sw_state. It
would seem to get this prev/in-update handling correct we need to add a
spinlock, but concurrent rfkill_set_block()s are already prevented by
the mutex so no additional locking would be needed there.

Does that sound correct/acceptable?

johannes

[1] unless we want to use a mutex or require ops->set_block to be atomic
but then we can't really do either


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part
Subject: Re: [RFC v6] rfkill: rewrite

On Thu, 30 Apr 2009, Johannes Berg wrote:
> > The return values for rfkill_alloc() are not the same on the stub and
> > real functions. The stub function uses ERR_PTR, while the real
> > function uses NULL for any errors. Please make them do the same
> > thing, and it is probably a damn good idea to document in the
> > kerneldoc what kind of return values one should expect when you return
> > a pointer and the function can fail (i.e. NULL or ERR_PTR).
>
> Actually, I was going to clean this up, and then I noticed you're
> wrong. :) Returning ERR_PTR(-ENODEV) is intentional, in the non-RFKILL
> case. Imagine a driver like this:

Well, that's confusing as all heck... document it, please? I still think in
that case, we'd be better off if rfkill_alloc always uses the ERR_PTR
convention.

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

Subject: Re: [RFC v6] rfkill: rewrite

On Thu, 30 Apr 2009, Johannes Berg wrote:
> On Thu, 2009-04-30 at 00:19 -0300, Henrique de Moraes Holschuh wrote:
> > I am halfway on the thinkpad-acpi conversion. It is going slow because
> > I have to cleanup the entire thing to have the code look even remotely
> > sane now that HW and SW states are to be tracked separately by the
> > backend driver, and I lost the RFKILL_STATE_* stuff.
>
> Hmm. I didn't think it was that much of a problem for the other drivers
> at least.

I will cope, it just slows things down. What is really annoying me is that
I have to find a way to get the new rfkill running on 2.6.28 to be able to
test the code properly. Fortunately, that just means net/rfkill, as the
only module I need that connects to rfkill is thinkpad-acpi itself.

> > Also, due to races, it is _impossible_ to assure the backend driver
> > that the set_block hook will never be called when the radio is
> > hard-blocked. Best to change the description of that hook to make it
> > clear that it could happen, but that the rfkill core will try to
> > optimize away such changes. Here's the race:
> >
> > A B
> > rfkill_set_block
> > saves state in "prev"
> > ... rfkill_set_hw_state(true)
> > call set_block hook
> > but radio IS hard-blocked
> > and the core WAS informed
> > of this
>
> Good point. I wonder what's more important -- it would be almost trivial
> to change this by using a spinlock instead of atomic bit operations.

Yeah, as long as it is a spinlock variant that can be used safely in any
context, it should work fine, as contention would be quite rare (unless
there is a pathological case in one driver that makes it always call some
set_foo just when the core is going to call it back...)

> > If I am not wrong, one could change rfkill_set_sw_state and
> > rfkill_set_states to return an error if rfkill_set_block is on the way
> > (that can be done lockless). For rfkill_set_states, document that the
> > hw state WILL be updated regardless, but that the sw state might not
> > be.
> >
> > Or one could kick the problem to the backend driver (which will have
> > any required locking anyway), by giving the set_block hook the duty of
> > calling set_sw_state when it succeeds. The return status might be
> > kept should we ever start passing it down to other layers like
> > userspace, or you can remove it.
>
> Thanks for your analysis. It does seem like a problem, and even locking
> in rfkill can't really avoid the problem [1]. However, why would we
> require the driver to postpone that? Your idea is sound, but I think we
> can do it in the core. It might require a spinlock there, but that's
> acceptable, I didn't use atomic operations to make it faster but to make
> it simpler.

Well, as long as the race is closed, and the kernel doesn't stay around
spinning with interrupts disabled for long periods, I'm fine :-)

> * rfkill_set_block can set a flag "I'm in an update"
> * a concurrent rfkill_set_sw_state will check that flag, and update the
> "prev" variable instead of the real variable
> * on errors, rfkill_set_block will then revert to whatever the last
> set_sw_state said

That could work. It is still required to make it clear that the set_block
hook can be called with a hardware block active, even if it normally won't
be, so that backend drivers are aware of the possibility.

> That way, if the driver returns an error from ops->set_block, we will
> really revert to what the driver last told us with set_sw_state. It
> would seem to get this prev/in-update handling correct we need to add a
> spinlock, but concurrent rfkill_set_block()s are already prevented by
> the mutex so no additional locking would be needed there.
>
> Does that sound correct/acceptable?

I think so, but I'd need to look at the finished patch to be even remotely
sure. I am not very good at Linux locking yet, in particular I am quite weak
at the more esotheric (but really useful) readers/writers locks, RCU, and
other such stuff.

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

2009-04-14 23:08:40

by Johannes Berg

[permalink] [raw]
Subject: [RFC v6] rfkill: rewrite

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

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

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

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

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

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

* rfkill-input pointlessly is a separate module

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

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

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

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

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

Signed-off-by: Johannes Berg <[email protected]>
---
v3: * fix locking bugs
* fix bug in input.c that made it ignore things
* allow setting LED trigger name
* apply default state while registering
* convert HSO
v4: * LED triggering
v5: * convert sony
* introduce rfkill_set_states function
* change query/poll methods to require calls to
rfkill_set{_sw,_hw}_state/rfkill_set_states
* do not call driver from registration
* remove tested-by since it changed a lot
v6: * fix stupid sync work bugs

TODO
* convert toshiba, thinkpad

Documentation/rfkill.txt | 639 +++-------------------
MAINTAINERS | 6
arch/arm/mach-pxa/tosa-bt.c | 30 -
arch/arm/mach-pxa/tosa.c | 1
drivers/net/usb/hso.c | 42 -
drivers/net/wireless/ath/ath9k/ath9k.h | 7
drivers/net/wireless/ath/ath9k/main.c | 115 +---
drivers/net/wireless/ath/ath9k/pci.c | 15
drivers/net/wireless/b43/Kconfig | 2
drivers/net/wireless/b43/leds.c | 2
drivers/net/wireless/b43/main.c | 4
drivers/net/wireless/b43/phy_a.c | 4
drivers/net/wireless/b43/phy_common.c | 17
drivers/net/wireless/b43/phy_common.h | 4
drivers/net/wireless/b43/phy_g.c | 4
drivers/net/wireless/b43/phy_lp.c | 2
drivers/net/wireless/b43/phy_n.c | 2
drivers/net/wireless/b43/rfkill.c | 123 +---
drivers/net/wireless/b43/rfkill.h | 5
drivers/net/wireless/b43legacy/Kconfig | 2
drivers/net/wireless/b43legacy/leds.c | 3
drivers/net/wireless/b43legacy/rfkill.c | 123 +---
drivers/net/wireless/b43legacy/rfkill.h | 6
drivers/net/wireless/iwlwifi/Kconfig | 5
drivers/net/wireless/iwlwifi/iwl-rfkill.c | 69 --
drivers/platform/x86/Kconfig | 6
drivers/platform/x86/acer-wmi.c | 50 -
drivers/platform/x86/dell-laptop.c | 101 +--
drivers/platform/x86/eeepc-laptop.c | 91 ---
drivers/platform/x86/hp-wmi.c | 103 +--
drivers/platform/x86/sony-laptop.c | 115 ++--
drivers/platform/x86/toshiba_acpi.c | 23
include/linux/Kbuild | 1
include/linux/rfkill.h | 344 +++++++++---
include/net/wimax.h | 8
net/rfkill/Kconfig | 14
net/rfkill/Makefile | 4
net/rfkill/core.c | 838 +++++++++++++++++++++++++++++
net/rfkill/input.c | 342 ++++++++++++
net/rfkill/rfkill-input.c | 390 -------------
net/rfkill/rfkill-input.h | 21
net/rfkill/rfkill.c | 843 ------------------------------
net/rfkill/rfkill.h | 27
net/wimax/Kconfig | 14
net/wimax/op-rfkill.c | 125 ----
45 files changed, 2000 insertions(+), 2692 deletions(-)

--- wireless-testing.orig/include/linux/rfkill.h 2009-04-15 00:50:15.000000000 +0200
+++ wireless-testing/include/linux/rfkill.h 2009-04-15 00:53:57.000000000 +0200
@@ -4,6 +4,7 @@
/*
* Copyright (C) 2006 - 2007 Ivo van Doorn
* Copyright (C) 2007 Dmitry Torokhov
+ * Copyright 2009 Johannes Berg <[email protected]>
*
* 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,11 +49,13 @@

/**
* 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 ,
@@ -42,97 +63,274 @@ enum rfkill_type {
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 hardware block state (return true
+ * for blocked) -- 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. This 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.
- *
- * This structure represents a RF switch located on a network device.
- */
-struct rfkill {
- const char *name;
- enum rfkill_type type;
-
- /* the mutex serializes callbacks and also protects
- * the state */
- struct mutex mutex;
- enum rfkill_state state;
- void *data;
- int (*toggle_radio)(void *data, enum rfkill_state state);
- int (*get_state)(void *data, enum rfkill_state *state);
+ * rfkill_alloc - allocate rfkill structure
+ * @name: name of the struct -- the string is not copied internally
+ * @parent: device that has rf switch on it
+ * @type: type of the switch (RFKILL_TYPE_*)
+ * @ops: rfkill methods
+ * @ops_data: data passed to each method
+ *
+ * This function should be called by the transmitter driver to allocate an
+ * rfkill structure.
+ */
+struct rfkill * __must_check rfkill_alloc(const char *name,
+ struct device *parent,
+ const enum rfkill_type type,
+ const struct rfkill_ops *ops,
+ void *ops_data);

-#ifdef CONFIG_RFKILL_LEDS
- struct led_trigger led_trigger;
-#endif
+/**
+ * rfkill_has_sw_block_memory -- turn on calling set_block unconditionally
+ * @rfkill: rfkill struct
+ *
+ * This function tells the rfkill core that the device is capable of
+ * remembering soft blocks (which it is notified of via the set_block
+ * method) -- this means that the driver may ignore the return value
+ * from rfkill_set_hw_state().
+ *
+ * This function must be called before rfkill_register().
+ */
+void rfkill_has_sw_block_memory(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_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);

-struct rfkill * __must_check rfkill_allocate(struct device *parent,
- enum rfkill_type type);
-void rfkill_free(struct rfkill *rfkill);
+/**
+ * rfkill_register - Register a rfkill structure.
+ * @rfkill: rfkill structure to be registered
+ *
+ * This function should be called by the transmitter driver to register
+ * the rfkill structure needs to be registered. Before calling this function
+ * the driver needs to be ready to service method calls from rfkill.
+ */
int __must_check rfkill_register(struct rfkill *rfkill);
+
+/**
+ * rfkill_pause_polling(struct rfkill *rfkill)
+ *
+ * Pause polling -- say transmitter is off for other reasons.
+ * NOTE: not necessary for suspend/resume -- in that case the
+ * core stops polling anyway
+ */
+void rfkill_pause_polling(struct rfkill *rfkill);
+
+/**
+ * rfkill_resume_polling(struct rfkill *rfkill)
+ *
+ * Pause polling -- say transmitter is off for other reasons.
+ * NOTE: not necessary for suspend/resume -- in that case the
+ * core stops polling anyway
+ */
+void rfkill_resume_polling(struct rfkill *rfkill);
+
+
+/**
+ * rfkill_unregister - Unregister a rfkill structure.
+ * @rfkill: rfkill structure to be unregistered
+ *
+ * This function should be called by the network driver during device
+ * teardown to destroy rfkill structure. Until it returns, the driver
+ * needs to be able to service method calls.
+ */
void rfkill_unregister(struct rfkill *rfkill);

-int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state);
-int rfkill_set_default(enum rfkill_type type, enum rfkill_state state);
+/**
+ * rfkill_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.
+ *
+ * If the device is able to, call rfkill_has_sw_block_memory() and
+ * ignore the return value of this function.
+ */
+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 __must_check 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
*
- * Returns RFKILL_STATE_SOFT_BLOCKED if @state is RFKILL_STATE_UNBLOCKED,
- * returns RFKILL_STATE_UNBLOCKED otherwise.
+ * This function sets the global default -- use at boot if your platform has
+ * an rfkill switch. If not early enough this call may be ignored.
+ *
+ * XXX: instead of ignoring -- how about just updating all currently
+ * registered drivers?
*/
-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 void rfkill_has_sw_block_memory(struct rfkill *rfkill)
+{
+}
+
+static inline int __must_check rfkill_register(struct rfkill *rfkill)
+{
+ if (rfkill == ERR_PTR(-ENODEV))
+ return 0;
+ return -EINVAL;
+}
+
+static inline void rfkill_pause_polling(struct rfkill *rfkill)
+{
+}
+
+static inline void rfkill_resume_polling(struct rfkill *rfkill)
+{
+}
+
+static inline void rfkill_unregister(struct rfkill *rfkill)
+{
+}
+
+static inline void rfkill_destroy(struct rfkill *rfkill)
+{
+}
+
+static inline bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked)
+{
+ return blocked;
+}
+
+static inline bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
{
- return (state == RFKILL_STATE_UNBLOCKED) ?
- RFKILL_STATE_SOFT_BLOCKED : RFKILL_STATE_UNBLOCKED;
+ return blocked;
}

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

+void rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name)
+{
+}
+#endif
+
+#endif /* __KERNEL__ */
+
#endif /* RFKILL_H */
--- wireless-testing.orig/drivers/net/wireless/ath/ath9k/main.c 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath/ath9k/main.c 2009-04-15 00:53:57.000000000 +0200
@@ -1172,120 +1172,69 @@ static bool ath_is_rfkill_set(struct ath
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);
@@ -1654,10 +1603,6 @@ int ath_attach(u16 devid, struct ath_sof
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)
@@ -2150,10 +2095,8 @@ static void ath9k_stop(struct ieee80211_
} 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);
--- wireless-testing.orig/drivers/platform/x86/eeepc-laptop.c 2009-04-15 00:50:15.000000000 +0200
+++ wireless-testing/drivers/platform/x86/eeepc-laptop.c 2009-04-15 00:53:57.000000000 +0200
@@ -296,39 +296,15 @@ static int update_bl_status(struct backl
* Rfkill helpers
*/

-static int eeepc_wlan_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_WLAN, 0);
- else
- return set_acpi(CM_ASL_WLAN, 1);
-}
-
-static int eeepc_wlan_rfkill_state(void *data, enum rfkill_state *state)
-{
- if (get_acpi(CM_ASL_WLAN) == 1)
- *state = RFKILL_STATE_UNBLOCKED;
- else
- *state = RFKILL_STATE_SOFT_BLOCKED;
- return 0;
+ unsigned long asl = (unsigned long)data;
+ return set_acpi(asl, !blocked);
}

-static int eeepc_bluetooth_rfkill_set(void *data, enum rfkill_state state)
-{
- if (state == RFKILL_STATE_SOFT_BLOCKED)
- return set_acpi(CM_ASL_BLUETOOTH, 0);
- else
- return set_acpi(CM_ASL_BLUETOOTH, 1);
-}
-
-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
@@ -650,26 +626,17 @@ static int eeepc_hotk_add(struct acpi_de
printk(EEEPC_ERR "Error installing notify handler\n");

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_sw_state(ehotk->eeepc_wlan_rfkill,
+ get_acpi(CM_ASL_WLAN) != 1);
result = rfkill_register(ehotk->eeepc_wlan_rfkill);
if (result)
goto wlan_fail;
@@ -677,28 +644,17 @@ static int eeepc_hotk_add(struct acpi_de

if (get_acpi(CM_ASL_BLUETOOTH) != -1) {
ehotk->eeepc_bluetooth_rfkill =
- rfkill_allocate(&device->dev, RFKILL_TYPE_BLUETOOTH);
+ rfkill_alloc("eeepc-wlan",
+ &device->dev,
+ RFKILL_TYPE_WLAN,
+ &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_sw_state(ehotk->eeepc_bluetooth_rfkill,
+ get_acpi(CM_ASL_BLUETOOTH) != 1);
result = rfkill_register(ehotk->eeepc_bluetooth_rfkill);
if (result)
goto bluetooth_fail;
@@ -710,13 +666,10 @@ static int eeepc_hotk_add(struct acpi_de
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);
ehotk_fail:
kfree(ehotk);
ehotk = NULL;
--- wireless-testing.orig/net/rfkill/rfkill.c 2009-04-15 00:50:14.000000000 +0200
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,843 +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 <[email protected]>");
-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);
-}
-#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_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);
-
- 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_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);
-}
-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);
-
- 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);
- }
-
- 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);
--- wireless-testing.orig/net/rfkill/rfkill-input.h 2009-04-15 00:50:14.000000000 +0200
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2007 Ivo van Doorn
- */
-
-/*
- * 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.
- */
-
-#ifndef __RFKILL_INPUT_H
-#define __RFKILL_INPUT_H
-
-void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state);
-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);
-
-#endif /* __RFKILL_INPUT_H */
--- wireless-testing.orig/net/rfkill/rfkill-input.c 2009-04-15 00:50:14.000000000 +0200
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -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 <[email protected]>");
-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);
--- wireless-testing.orig/drivers/net/wireless/b43/main.c 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/main.c 2009-04-15 00:53:57.000000000 +0200
@@ -3498,7 +3498,7 @@ static int b43_op_config(struct ieee8021

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 "
@@ -3506,7 +3506,7 @@ static int b43_op_config(struct ieee8021
"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");
}
}
--- wireless-testing.orig/drivers/net/wireless/b43/phy_a.c 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_a.c 2009-04-15 00:53:57.000000000 +0200
@@ -480,11 +480,11 @@ static bool b43_aphy_op_supports_hwpctl(
}

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);
--- wireless-testing.orig/drivers/net/wireless/b43/phy_common.c 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_common.c 2009-04-15 00:53:57.000000000 +0200
@@ -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);
}
@@ -287,18 +287,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);
}

--- wireless-testing.orig/drivers/net/wireless/b43/phy_common.h 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_common.h 2009-04-15 00:53:57.000000000 +0200
@@ -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);
@@ -362,7 +362,7 @@ int b43_switch_channel(struct b43_wldev
/**
* 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.
--- wireless-testing.orig/drivers/net/wireless/b43/phy_g.c 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_g.c 2009-04-15 00:53:57.000000000 +0200
@@ -2592,7 +2592,7 @@ static bool b43_gphy_op_supports_hwpctl(
}

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(

might_sleep();

- if (state == RFKILL_STATE_UNBLOCKED) {
+ if (!blocked) {
/* Turn radio ON */
if (phy->radio_on)
return;
--- wireless-testing.orig/drivers/net/wireless/b43/phy_n.c 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_n.c 2009-04-15 00:53:57.000000000 +0200
@@ -579,7 +579,7 @@ static void b43_nphy_op_radio_write(stru
}

static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
- enum rfkill_state state)
+ bool blocked)
{//TODO
}

--- wireless-testing.orig/drivers/net/wireless/b43/rfkill.c 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/rfkill.c 2009-04-15 00:53:57.000000000 +0200
@@ -45,12 +45,11 @@ static bool b43_is_hw_radio_enabled(stru
}

/* 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
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 (WARN_ON(!dev->radio_hw_enable && !blocked))
+ 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 *d

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 *d
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;
}
--- wireless-testing.orig/drivers/net/wireless/b43legacy/rfkill.c 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/rfkill.c 2009-04-15 00:53:57.000000000 +0200
@@ -45,12 +45,11 @@ static bool b43legacy_is_hw_radio_enable
}

/* 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
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 (WARN_ON(!blocked && !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 b43leg

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 b43leg
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;
}

--- wireless-testing.orig/drivers/net/wireless/iwlwifi/iwl-rfkill.c 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/iwlwifi/iwl-rfkill.c 2009-04-15 00:53:57.000000000 +0200
@@ -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 (WARN_ON(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 *pri
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 *pri
}

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_
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);
--- wireless-testing.orig/Documentation/rfkill.txt 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/Documentation/rfkill.txt 2009-04-15 00:53:57.000000000 +0200
@@ -1,571 +1,132 @@
-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:
-
-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.
-
-Note that disabling the signal output means that the the transmitter is to be
-made to not emit any energy when "blocked". rfkill is not about blocking data
-transmissions, it is about blocking energy emission.
-
-The rfkill subsystem offers support for keys and switches often found on
-laptops to enable wireless devices like WiFi and Bluetooth, so that these keys
-and switches actually perform an action in all wireless devices of a given type
-attached to the system.
-
-The buttons to enable and disable the wireless transmitters are important in
-situations where the user is for example using his laptop on a location where
-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.
+1. Introduction
+2. Implementation details
+3. Kernel driver guidelines
+4. Kernel API
+5. Userspace support

-===============================================================================
-2: Implementation details
+
+1. Introduction
+
+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.
+
+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.
+
+
+
+2. Implementation details

The rfkill subsystem is composed of various components: the rfkill class, the
rfkill-input module (an input layer handler), and some specific input layer
events.

-The rfkill class provides kernel drivers with an interface that allows them to
-know when they should enable or disable a wireless network device transmitter.
-This is enabled by the CONFIG_RFKILL Kconfig option.
-
-The rfkill class support makes sure userspace will be notified of all state
-changes on rfkill devices through uevents. It provides a notification chain
-for interested parties in the kernel to also get notified of rfkill state
-changes in other drivers. It creates several sysfs entries which can be used
-by userspace. See section "Userspace support".
-
-The rfkill-input module provides the kernel with the ability to implement a
-basic response when the user presses a key or button (or toggles a switch)
-related to rfkill functionality. It is an in-kernel implementation of default
-policy of reacting to rfkill-related input events and neither mandatory nor
-required for wireless drivers to operate. It is enabled by the
-CONFIG_RFKILL_INPUT Kconfig option.
-
-rfkill-input is a rfkill-related events input layer handler. This handler will
-listen to all rfkill key events and will change the rfkill state of the
-wireless devices accordingly. With this option enabled userspace could either
-do nothing or simply perform monitoring tasks.
-
-The rfkill-input module also provides EPO (emergency power-off) functionality
-for all wireless transmitters. This function cannot be overridden, and it is
-always active. rfkill EPO is related to *_RFKILL_ALL input layer events.
-
-
-Important terms for the rfkill subsystem:
-
-In order to avoid confusion, we avoid the term "switch" in rfkill when it is
-referring to an electronic control circuit that enables or disables a
-transmitter. We reserve it for the physical device a human manipulates
-(which is an input device, by the way):
-
-rfkill switch:
-
- A physical device a human manipulates. Its state can be perceived by
- the kernel either directly (through a GPIO pin, ACPI GPE) or by its
- effect on a rfkill line of a wireless device.
-
-rfkill controller:
-
- A hardware circuit that controls the state of a rfkill line, which a
- kernel driver can interact with *to modify* that state (i.e. it has
- either write-only or read/write access).
-
-rfkill line:
-
- An input channel (hardware or software) of a wireless device, which
- causes a wireless transmitter to stop emitting energy (BLOCK) when it
- is active. Point of view is extremely important here: rfkill lines are
- always seen from the PoV of a wireless device (and its driver).
-
-soft rfkill line/software rfkill line:
-
- A rfkill line the wireless device driver can directly change the state
- of. Related to rfkill_state RFKILL_STATE_SOFT_BLOCKED.
-
-hard rfkill line/hardware rfkill line:
-
- A rfkill line that works fully in hardware or firmware, and that cannot
- be overridden by the kernel driver. The hardware device or the
- firmware just exports its status to the driver, but it is read-only.
- Related to rfkill_state RFKILL_STATE_HARD_BLOCKED.
-
-The enum rfkill_state describes the rfkill state of a transmitter:
-
-When a rfkill line or rfkill controller is in the RFKILL_STATE_UNBLOCKED state,
-the wireless transmitter (radio TX circuit for example) is *enabled*. When the
-it is in the RFKILL_STATE_SOFT_BLOCKED or RFKILL_STATE_HARD_BLOCKED, the
-wireless transmitter is to be *blocked* from operating.
-
-RFKILL_STATE_SOFT_BLOCKED indicates that a call to toggle_radio() can change
-that state. RFKILL_STATE_HARD_BLOCKED indicates that a call to toggle_radio()
-will not be able to change the state and will return with a suitable error if
-attempts are made to set the state to RFKILL_STATE_UNBLOCKED.
-
-RFKILL_STATE_HARD_BLOCKED is used by drivers to signal that the device is
-locked in the BLOCKED state by a hardwire rfkill line (typically an input pin
-that, when active, forces the transmitter to be disabled) which the driver
-CANNOT override.
-
-Full rfkill functionality requires two different subsystems to cooperate: the
-input layer and the rfkill class. The input layer issues *commands* to the
-entire system requesting that devices registered to the rfkill class change
-state. The way this interaction happens is not complex, but it is not obvious
-either:
-
-Kernel Input layer:
-
- * Generates KEY_WWAN, KEY_WLAN, KEY_BLUETOOTH, SW_RFKILL_ALL, and
- other such events when the user presses certain keys, buttons, or
- toggles certain physical switches.
-
- THE INPUT LAYER IS NEVER USED TO PROPAGATE STATUS, NOTIFICATIONS OR THE
- KIND OF STUFF AN ON-SCREEN-DISPLAY APPLICATION WOULD REPORT. It is
- used to issue *commands* for the system to change behaviour, and these
- commands may or may not be carried out by some kernel driver or
- userspace application. It follows that doing user feedback based only
- on input events is broken, as there is no guarantee that an input event
- will be acted upon.
-
- Most wireless communication device drivers implementing rfkill
- functionality MUST NOT generate these events, and have no reason to
- register themselves with the input layer. Doing otherwise is a common
- misconception. There is an API to propagate rfkill status change
- information, and it is NOT the input layer.
-
-rfkill class:
-
- * Calls a hook in a driver to effectively change the wireless
- transmitter state;
- * Keeps track of the wireless transmitter state (with help from
- the driver);
- * Generates userspace notifications (uevents) and a call to a
- notification chain (kernel) when there is a wireless transmitter
- state change;
- * Connects a wireless communications driver with the common rfkill
- control system, which, for example, allows actions such as
- "switch all bluetooth devices offline" to be carried out by
- userspace or by rfkill-input.
-
- THE RFKILL CLASS NEVER ISSUES INPUT EVENTS. THE RFKILL CLASS DOES
- NOT LISTEN TO INPUT EVENTS. NO DRIVER USING THE RFKILL CLASS SHALL
- EVER LISTEN TO, OR ACT ON RFKILL INPUT EVENTS. Doing otherwise is
- a layering violation.
-
- Most wireless data communication drivers in the kernel have just to
- implement the rfkill class API to work properly. Interfacing to the
- input layer is not often required (and is very often a *bug*) on
- wireless drivers.
-
- Platform drivers often have to attach to the input layer to *issue*
- (but never to listen to) rfkill events for rfkill switches, and also to
- the rfkill class to export a control interface for the platform rfkill
- controllers to the rfkill subsystem. This does NOT mean the rfkill
- switch is attached to a rfkill class (doing so is almost always wrong).
- It just means the same kernel module is the driver for different
- devices (rfkill switches and rfkill controllers).
-
-
-Userspace input handlers (uevents) or kernel input handlers (rfkill-input):
-
- * Implements the policy of what should happen when one of the input
- layer events related to rfkill operation is received.
- * Uses the sysfs interface (userspace) or private rfkill API calls
- to tell the devices registered with the rfkill class to change
- their state (i.e. translates the input layer event into real
- action).
-
- * rfkill-input implements EPO by handling EV_SW SW_RFKILL_ALL 0
- (power off all transmitters) in a special way: it ignores any
- overrides and local state cache and forces all transmitters to the
- RFKILL_STATE_SOFT_BLOCKED state (including those which are already
- supposed to be BLOCKED).
- * rfkill EPO will remain active until rfkill-input receives an
- EV_SW SW_RFKILL_ALL 1 event. While the EPO is active, transmitters
- are locked in the blocked state (rfkill will refuse to unblock them).
- * rfkill-input implements different policies that the user can
- select for handling EV_SW SW_RFKILL_ALL 1. It will unlock rfkill,
- and either do nothing (leave transmitters blocked, but now unlocked),
- restore the transmitters to their state before the EPO, or unblock
- them all.
-
-Userspace uevent handler or kernel platform-specific drivers hooked to the
-rfkill notifier chain:
-
- * Taps into the rfkill notifier chain or to KOBJ_CHANGE uevents,
- in order to know when a device that is registered with the rfkill
- class changes state;
- * Issues feedback notifications to the user;
- * In the rare platforms where this is required, synthesizes an input
- event to command all *OTHER* rfkill devices to also change their
- statues when a specific rfkill device changes state.
-
-
-===============================================================================
-3: Kernel driver guidelines
-
-Remember: point-of-view is everything for a driver that connects to the rfkill
-subsystem. All the details below must be measured/perceived from the point of
-view of the specific driver being modified.
-
-The first thing one needs to know is whether his driver should be talking to
-the rfkill class or to the input layer. In rare cases (platform drivers), it
-could happen that you need to do both, as platform drivers often handle a
-variety of devices in the same driver.
-
-Do not mistake input devices for rfkill controllers. The only type of "rfkill
-switch" device that is to be registered with the rfkill class are those
-directly controlling the circuits that cause a wireless transmitter to stop
-working (or the software equivalent of them), i.e. what we call a rfkill
-controller. Every other kind of "rfkill switch" is just an input device and
-MUST NOT be registered with the rfkill class.
-
-A driver should register a device with the rfkill class when ALL of the
-following conditions are met (they define a rfkill controller):
-
-1. The device is/controls a data communications wireless transmitter;
-
-2. The kernel can interact with the hardware/firmware to CHANGE the wireless
- transmitter state (block/unblock TX operation);
-
-3. The transmitter can be made to not emit any energy when "blocked":
- rfkill is not about blocking data transmissions, it is about blocking
- energy emission;
-
-A driver should register a device with the input subsystem to issue
-rfkill-related events (KEY_WLAN, KEY_BLUETOOTH, KEY_WWAN, KEY_WIMAX,
-SW_RFKILL_ALL, etc) when ALL of the folowing conditions are met:
-
-1. It is directly related to some physical device the user interacts with, to
- command the O.S./firmware/hardware to enable/disable a data communications
- wireless transmitter.
-
- Examples of the physical device are: buttons, keys and switches the user
- will press/touch/slide/switch to enable or disable the wireless
- communication device.
-
-2. It is NOT slaved to another device, i.e. there is no other device that
- issues rfkill-related input events in preference to this one.
-
- Please refer to the corner cases and examples section for more details.
-
-When in doubt, do not issue input events. For drivers that should generate
-input events in some platforms, but not in others (e.g. b43), the best solution
-is to NEVER generate input events in the first place. That work should be
-deferred to a platform-specific kernel module (which will know when to generate
-events through the rfkill notifier chain) or to userspace. This avoids the
-usual maintenance problems with DMI whitelisting.
-
-
-Corner cases and examples:
-====================================
-
-1. If the device is an input device that, because of hardware or firmware,
-causes wireless transmitters to be blocked regardless of the kernel's will, it
-is still just an input device, and NOT to be registered with the rfkill class.
-
-2. If the wireless transmitter switch control is read-only, it is an input
-device and not to be registered with the rfkill class (and maybe not to be made
-an input layer event source either, see below).
-
-3. If there is some other device driver *closer* to the actual hardware the
-user interacted with (the button/switch/key) to issue an input event, THAT is
-the device driver that should be issuing input events.
-
-E.g:
- [RFKILL slider switch] -- [GPIO hardware] -- [WLAN card rf-kill input]
- (platform driver) (wireless card driver)
-
-The user is closer to the RFKILL slide switch plaform driver, so the driver
-which must issue input events is the platform driver looking at the GPIO
-hardware, and NEVER the wireless card driver (which is just a slave). It is
-very likely that there are other leaves than just the WLAN card rf-kill input
-(e.g. a bluetooth card, etc)...
-
-On the other hand, some embedded devices do this:
-
- [RFKILL slider switch] -- [WLAN card rf-kill input]
- (wireless card driver)
-
-In this situation, the wireless card driver *could* register itself as an input
-device and issue rf-kill related input events... but in order to AVOID the need
-for DMI whitelisting, the wireless card driver does NOT do it. Userspace (HAL)
-or a platform driver (that exists only on these embedded devices) will do the
-dirty job of issuing the input events.
-
-
-COMMON MISTAKES in kernel drivers, related to rfkill:
-====================================
-
-1. NEVER confuse input device keys and buttons with input device switches.
-
- 1a. Switches are always set or reset. They report the current state
- (on position or off position).
-
- 1b. Keys and buttons are either in the pressed or not-pressed state, and
- that's it. A "button" that latches down when you press it, and
- unlatches when you press it again is in fact a switch as far as input
- devices go.
-
-Add the SW_* events you need for switches, do NOT try to emulate a button using
-KEY_* events just because there is no such SW_* event yet. Do NOT try to use,
-for example, KEY_BLUETOOTH when you should be using SW_BLUETOOTH instead.
-
-2. Input device switches (sources of EV_SW events) DO store their current state
-(so you *must* initialize it by issuing a gratuitous input layer event on
-driver start-up and also when resuming from sleep), and that state CAN be
-queried from userspace through IOCTLs. There is no sysfs interface for this,
-but that doesn't mean you should break things trying to hook it to the rfkill
-class to get a sysfs interface :-)
-
-3. Do not issue *_RFKILL_ALL events by default, unless you are sure it is the
-correct event for your switch/button. These events are emergency power-off
-events when they are trying to turn the transmitters off. An example of an
-input device which SHOULD generate *_RFKILL_ALL events is the wireless-kill
-switch in a laptop which is NOT a hotkey, but a real sliding/rocker switch.
-An example of an input device which SHOULD NOT generate *_RFKILL_ALL events by
-default, is any sort of hot key that is type-specific (e.g. the one for WLAN).
-
-
-3.1 Guidelines for wireless device drivers
-------------------------------------------
-
-(in this text, rfkill->foo means the foo field of struct rfkill).
-
-1. Each independent transmitter in a wireless device (usually there is only one
-transmitter per device) should have a SINGLE rfkill class attached to it.
-
-2. If the device does not have any sort of hardware assistance to allow the
-driver to rfkill the device, the driver should emulate it by taking all actions
-required to silence the transmitter.
-
-3. If it is impossible to silence the transmitter (i.e. it still emits energy,
-even if it is just in brief pulses, when there is no data to transmit and there
-is no hardware support to turn it off) do NOT lie to the users. Do not attach
-it to a rfkill class. The rfkill subsystem does not deal with data
-transmission, it deals with energy emission. If the transmitter is emitting
-energy, it is not blocked in rfkill terms.
-
-4. It doesn't matter if the device has multiple rfkill input lines affecting
-the same transmitter, their combined state is to be exported as a single state
-per transmitter (see rule 1).
-
-This rule exists because users of the rfkill subsystem expect to get (and set,
-when possible) the overall transmitter rfkill state, not of a particular rfkill
-line.
-
-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.
+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".

-3. Deactivates the soft rfkill line if none of the hardware rfkill lines are
-active and the caller asked for RFKILL_STATE_UNBLOCKED.
+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.

-===============================================================================
-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.
+The rfkill-input code implements a basic response to rfkill buttons -- it
+implements turning on/off all devices of a certain class (or all).

-The 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).
+When the device is hard-blocked (either by a call to rfkill_set_hw_state()
+or from query_hw_block) then set_block() will not be invoked. When the device
+is hard-unblocked again, rfkill drivers need to take into account the return
+value from rfkill_set_hw_state() -- it indicates whether the device is still
+soft-blocked or not. Some devices can actually keep track of soft blocks,
+for those you can call rfkill_has_sw_block_memory() and will then get soft
+block notifications while hard-blocked.

-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.
+The entire functionality is spread over more than one subsystem:

-===============================================================================
-5: Userspace support
+ * The kernel input layer generates KEY_WWAN, KEY_WLAN etc. and
+ SW_RFKILL_ALL -- when the user presses a button. Drivers for radio
+ transmitters generally do not register to the input layer, unless the
+ device really provides an input device (i.e. a button that has no
+ effect other than generating a button press event)

-rfkill devices issue uevents (with an action of "change"), with the following
-environment variables set:
+ * The rfkill-input code hooks up to these events and switches the soft-block
+ of the various radio transmitters, depending on the button type.

-RFKILL_NAME
-RFKILL_STATE
-RFKILL_TYPE
+ * The rfkill drivers turn off/on their transmitters as requested.

-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.
+ * The rfkill class will generate userspace notifications (uevents) to tell
+ userspace what the current state is.

-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.
+3. Kernel driver guidelines

-Userspace cannot assume it is the only source of control for rfkill switches.
-Their state can change due to firmware actions, direct user actions, and the
-rfkill-input EPO override for *_RFKILL_ALL.

-When rfkill-input is not active, userspace must initiate a rfkill status
-change by writing to the "state" attribute in order for anything to happen.
+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.

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

-The following sysfs entries will be created:
+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.
+
+
+
+4. Kernel API
+
+To build a driver with rfkill subsystem support, the driver should depend on
+(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).
+
+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.
+
+
+
+5. Userspace support
+
+The following sysfs entries exist for every rfkill device:

name: Name assigned by driver to this key (interface or driver name).
type: Name of the key type ("wlan", "bluetooth", etc).
state: Current state of the transmitter
0: RFKILL_STATE_SOFT_BLOCKED
- transmitter is forced off, but one can override it
- by a write to the state attribute;
+ transmitter is turned off by software
1: RFKILL_STATE_UNBLOCKED
- transmiter is NOT forced off, and may operate if
- all other conditions for such operation are met
- (such as interface is up and configured, etc);
+ transmiter is (potentially) active
2: RFKILL_STATE_HARD_BLOCKED
transmitter is forced off by something outside of
- the driver's control. One cannot set a device to
- this state through writes to the state attribute;
- claim: 1: Userspace handles events, 0: Kernel handles events
-
-Both the "state" and "claim" entries are also writable. For the "state" entry
-this means that when 1 or 0 is written, the device rfkill state (if not yet in
-the requested state), will be will be toggled accordingly.
-
-For the "claim" entry writing 1 to it means that the kernel no longer handles
-key events even though RFKILL_INPUT input was enabled. When "claim" has been
-set to 0, userspace should make sure that it listens for the input events or
-check the sysfs "state" entry regularly to correctly perform the required tasks
-when the rkfill key is pressed.
-
-A note about input devices and EV_SW events:
-
-In order to know the current state of an input device switch (like
-SW_RFKILL_ALL), you will need to use an IOCTL. That information is not
-available through sysfs in a generic way at this time, and it is not available
-through the rfkill class AT ALL.
+ the driver's control.
+ claim: 0: Kernel handles events (currently always reads that value)
+
+rfkill devices also issue uevents (with an action of "change"), with the
+following environment variables set:
+
+RFKILL_NAME
+RFKILL_STATE
+RFKILL_TYPE
+
+The contents of these variables corresponds to the "name", "state" and
+"type" sysfs files explained above.
--- wireless-testing.orig/net/rfkill/Kconfig 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/net/rfkill/Kconfig 2009-04-15 00:53:57.000000000 +0200
@@ -10,22 +10,8 @@ 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
default y
-
--- wireless-testing.orig/net/rfkill/Makefile 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/net/rfkill/Makefile 2009-04-15 00:53:57.000000000 +0200
@@ -2,5 +2,5 @@
# Makefile for the RF switch subsystem.
#

-obj-$(CONFIG_RFKILL) += rfkill.o
-obj-$(CONFIG_RFKILL_INPUT) += rfkill-input.o
+rfkill-y += core.o input.o
+obj-$(CONFIG_RFKILL) += rfkill.o
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/rfkill/core.c 2009-04-15 01:01:51.000000000 +0200
@@ -0,0 +1,838 @@
+/*
+ * Copyright (C) 2006 - 2007 Ivo van Doorn
+ * Copyright (C) 2007 Dmitry Torokhov
+ * Copyright 2009 Johannes Berg <[email protected]>
+ *
+ * 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 "rfkill.h"
+
+#define POLL_INTERVAL (5 * HZ)
+
+#define RFKILL_BLOCK_HW_BIT 0
+#define RFKILL_BLOCK_SW_BIT 1
+
+struct rfkill {
+ const char *name;
+ enum rfkill_type type;
+
+ unsigned long blocked;
+
+ bool registered;
+ bool has_sw_block_memory;
+
+ 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 <[email protected]>");
+MODULE_AUTHOR("Johannes Berg <[email protected]>");
+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->blocked)
+ 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)
+ return;
+
+ kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE);
+}
+
+static bool __rfkill_set_hw_state(struct rfkill *rfkill,
+ bool blocked, bool *change)
+{
+ bool prev;
+
+ BUG_ON(!rfkill);
+
+ if (blocked)
+ prev = !!test_and_set_bit(RFKILL_BLOCK_HW_BIT,
+ &rfkill->blocked);
+ else
+ prev = !!test_and_clear_bit(RFKILL_BLOCK_HW_BIT,
+ &rfkill->blocked);
+
+ *change = prev != blocked;
+
+ rfkill_led_trigger_event(rfkill);
+
+ return blocked || !!test_bit(RFKILL_BLOCK_SW_BIT, &rfkill->blocked);
+}
+
+/**
+ * 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)
+{
+ bool prev;
+
+ /*
+ * 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);
+
+ if (blocked)
+ prev = !!test_and_set_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+ else
+ prev = !!test_and_clear_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+
+ /*
+ * HW already blocked, so nothing changes for now
+ * (unless device has memory for software blocks)
+ */
+ if (!rfkill->has_sw_block_memory &&
+ test_bit(RFKILL_BLOCK_HW_BIT, &rfkill->blocked))
+ return;
+
+ if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP))
+ return;
+
+ if (rfkill->ops->set_block(rfkill->data, blocked)) {
+ /* failed -- reset status */
+ if (prev)
+ set_bit(RFKILL_BLOCK_SW_BIT, &rfkill->blocked);
+ else
+ clear_bit(RFKILL_BLOCK_SW_BIT, &rfkill->blocked);
+ }
+
+ 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);
+}
+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)
+ 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 (change)
+ schedule_work(&rfkill->uevent_work);
+
+ return ret;
+}
+EXPORT_SYMBOL(rfkill_set_hw_state);
+
+bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
+{
+ bool prev, hwblock;
+
+ BUG_ON(!rfkill);
+
+ if (blocked)
+ prev = !!test_and_set_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+ else
+ prev = !!test_and_clear_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+
+ hwblock = !!test_bit(RFKILL_BLOCK_HW_BIT, &rfkill->blocked);
+
+ if (prev != blocked && !hwblock)
+ schedule_work(&rfkill->uevent_work);
+
+ rfkill_led_trigger_event(rfkill);
+
+ return blocked || hwblock;
+}
+EXPORT_SYMBOL(rfkill_set_sw_state);
+
+void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw)
+{
+ bool swprev, hwprev;
+
+ BUG_ON(!rfkill);
+
+ if (sw)
+ swprev = !!test_and_set_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+ else
+ swprev = !!test_and_clear_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+
+ if (hw)
+ hwprev = !!test_and_set_bit(RFKILL_BLOCK_HW_BIT,
+ &rfkill->blocked);
+ else
+ hwprev = !!test_and_clear_bit(RFKILL_BLOCK_HW_BIT,
+ &rfkill->blocked);
+
+ 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 *blocked)
+{
+ if (test_bit(RFKILL_BLOCK_HW_BIT, blocked))
+ return RFKILL_USER_STATE_HARD_BLOCKED;
+ if (test_bit(RFKILL_BLOCK_SW_BIT, blocked))
+ 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);
+
+ return sprintf(buf, "%d\n", user_state_from_blocked(&rfkill->blocked));
+}
+
+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);
+ 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",
+ user_state_from_blocked(&rfkill->blocked));
+ 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);
+
+ return 0;
+}
+
+static int rfkill_resume(struct device *dev)
+{
+ struct rfkill *rfkill = to_rfkill(dev);
+
+ 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;
+
+ 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;
+ mutex_unlock(&rfkill_global_mutex);
+
+ rfkill_set_block(rfkill, cur);
+}
+
+void rfkill_has_sw_block_memory(struct rfkill *rfkill)
+{
+ BUG_ON(!rfkill);
+ BUG_ON(rfkill->registered);
+
+ rfkill->has_sw_block_memory = true;
+}
+EXPORT_SYMBOL(rfkill_has_sw_block_memory);
+
+int __must_check rfkill_register(struct rfkill *rfkill)
+{
+ static unsigned long rfkill_no = 0;
+ 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;
+
+ error = rfkill_handler_init();
+ if (error)
+ class_unregister(&rfkill_class);
+
+ out:
+ return error;
+}
+subsys_initcall(rfkill_init);
+
+static void __exit rfkill_exit(void)
+{
+ rfkill_handler_exit();
+ class_unregister(&rfkill_class);
+}
+module_exit(rfkill_exit);
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/rfkill/input.c 2009-04-15 00:53:57.000000000 +0200
@@ -0,0 +1,342 @@
+/*
+ * Input layer to RF Kill interface connector
+ *
+ * Copyright (c) 2007 Dmitry Torokhov
+ * Copyright 2009 Johannes Berg <[email protected]>
+ *
+ * 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))
+ break;
+ 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);
+}
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/rfkill/rfkill.h 2009-04-15 00:53:57.000000000 +0200
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2007 Ivo van Doorn
+ * Copyright 2009 Johannes Berg <[email protected]>
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef __RFKILL_INPUT_H
+#define __RFKILL_INPUT_H
+
+/* 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);
+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 */
--- wireless-testing.orig/MAINTAINERS 2009-04-15 00:50:15.000000000 +0200
+++ wireless-testing/MAINTAINERS 2009-04-15 00:53:57.000000000 +0200
@@ -3762,9 +3762,9 @@ L: [email protected]
S: Supported

RFKILL
-P: Ivo van Doorn
-M: [email protected]
-L: [email protected]
+P: Johannes Berg
+M: [email protected]
+L: [email protected]
S: Maintained
F: net/rfkill

--- wireless-testing.orig/include/linux/Kbuild 2009-04-15 00:50:15.000000000 +0200
+++ wireless-testing/include/linux/Kbuild 2009-04-15 00:53:57.000000000 +0200
@@ -310,6 +310,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
--- wireless-testing.orig/drivers/net/wireless/ath/ath9k/ath9k.h 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath/ath9k/ath9k.h 2009-04-15 00:53:57.000000000 +0200
@@ -464,12 +464,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];
};

@@ -513,8 +510,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)
--- wireless-testing.orig/drivers/net/wireless/ath/ath9k/pci.c 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath/ath9k/pci.c 2009-04-15 00:53:57.000000000 +0200
@@ -227,11 +227,6 @@ static int ath_pci_suspend(struct pci_de

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
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;
}

--- wireless-testing.orig/drivers/net/wireless/b43/Kconfig 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/Kconfig 2009-04-15 00:53:57.000000000 +0200
@@ -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,
--- wireless-testing.orig/drivers/net/wireless/b43/rfkill.h 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/rfkill.h 2009-04-15 00:53:57.000000000 +0200
@@ -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 */
--- wireless-testing.orig/drivers/net/wireless/b43legacy/Kconfig 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/Kconfig 2009-04-15 00:53:57.000000000 +0200
@@ -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,
--- wireless-testing.orig/drivers/net/wireless/b43legacy/leds.c 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/leds.c 2009-04-15 00:53:57.000000000 +0200
@@ -86,7 +86,8 @@ static void b43legacy_led_brightness_set

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;
--- wireless-testing.orig/drivers/net/wireless/b43legacy/rfkill.h 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/rfkill.h 2009-04-15 00:53:57.000000000 +0200
@@ -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 */
--- wireless-testing.orig/drivers/net/wireless/b43/leds.c 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/leds.c 2009-04-15 00:53:57.000000000 +0200
@@ -87,7 +87,7 @@ static void b43_led_brightness_set(struc
}

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;
--- wireless-testing.orig/drivers/net/wireless/iwlwifi/Kconfig 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/iwlwifi/Kconfig 2009-04-15 00:53:57.000000000 +0200
@@ -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"
--- wireless-testing.orig/drivers/net/wireless/b43/phy_lp.c 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_lp.c 2009-04-15 00:53:57.000000000 +0200
@@ -488,7 +488,7 @@ static void b43_lpphy_op_radio_write(str
}

static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev,
- enum rfkill_state state)
+ bool blocked)
{
//TODO
}
--- wireless-testing.orig/drivers/platform/x86/Kconfig 2009-04-15 00:50:15.000000000 +0200
+++ wireless-testing/drivers/platform/x86/Kconfig 2009-04-15 00:53:57.000000000 +0200
@@ -21,7 +21,6 @@ config ACER_WMI
depends on NEW_LEDS
depends on BACKLIGHT_CLASS_DEVICE
depends on SERIO_I8042
- depends on RFKILL
select ACPI_WMI
---help---
This is a driver for newer Acer (and Wistron) laptops. It adds
@@ -60,7 +59,6 @@ config DELL_LAPTOP
depends on DCDBAS
depends on EXPERIMENTAL
depends on BACKLIGHT_CLASS_DEVICE
- depends on RFKILL
depends on POWER_SUPPLY
default n
---help---
@@ -117,7 +115,6 @@ config HP_WMI
tristate "HP WMI extras"
depends on ACPI_WMI
depends on INPUT
- depends on RFKILL
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.
@@ -203,7 +200,6 @@ config THINKPAD_ACPI
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
@@ -340,7 +336,6 @@ config EEEPC_LAPTOP
depends on EXPERIMENTAL
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.
@@ -407,7 +402,6 @@ config ACPI_TOSHIBA
depends on INPUT
select INPUT_POLLDEV
select NET
- select RFKILL
select BACKLIGHT_CLASS_DEVICE
---help---
This driver adds support for access to certain system settings
--- wireless-testing.orig/drivers/platform/x86/acer-wmi.c 2009-04-15 00:50:15.000000000 +0200
+++ wireless-testing/drivers/platform/x86/acer-wmi.c 2009-04-15 00:53:57.000000000 +0200
@@ -958,58 +958,50 @@ static void acer_rfkill_update(struct wo

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 devic
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 devic
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;
}
--- wireless-testing.orig/drivers/platform/x86/dell-laptop.c 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/platform/x86/dell-laptop.c 2009-04-15 00:53:57.000000000 +0200
@@ -174,10 +174,11 @@ dell_send_request(struct calling_interfa
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, en
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;
}
--- wireless-testing.orig/drivers/platform/x86/hp-wmi.c 2009-04-15 00:50:15.000000000 +0200
+++ wireless-testing/drivers/platform/x86/hp-wmi.c 2009-04-15 00:53:57.000000000 +0200
@@ -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, voi
}
} 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(stru
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(stru

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(str
{
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;
}
--- wireless-testing.orig/drivers/platform/x86/toshiba_acpi.c 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/platform/x86/toshiba_acpi.c 2009-04-15 00:53:57.000000000 +0200
@@ -311,28 +311,20 @@ static u32 hci_get_radio_state(bool *rad
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)
{
u32 result1, result2;
u32 value;
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;
+ return -EBUSY;

- switch (state) {
- case RFKILL_STATE_UNBLOCKED:
- if (!radio_state)
- return -EPERM;
- break;
- case RFKILL_STATE_SOFT_BLOCKED:
- break;
- default:
- return -EINVAL;
- }
+ if (WARN_ON(!blocked && !radio_state))
+ return;

mutex_lock(&dev->mutex);
hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1);
@@ -340,8 +332,7 @@ static int bt_rfkill_toggle_radio(void *
mutex_unlock(&dev->mutex);

if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS)
- return -EFAULT;
-
+ return -EBUSY;
return 0;
}

@@ -365,7 +356,7 @@ static void bt_poll_rfkill(struct input_
mutex_unlock(&dev->mutex);

if (unlikely(state_changed)) {
- rfkill_force_state(dev->rfk_dev,
+ rfkill_set_hw_state(dev->rfk_dev,
new_rfk_state ?
RFKILL_STATE_SOFT_BLOCKED :
RFKILL_STATE_HARD_BLOCKED);
--- wireless-testing.orig/arch/arm/mach-pxa/tosa-bt.c 2009-04-15 00:50:15.000000000 +0200
+++ wireless-testing/arch/arm/mach-pxa/tosa-bt.c 2009-04-15 00:53:57.000000000 +0200
@@ -35,21 +35,25 @@ static void tosa_bt_off(struct tosa_bt_d
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
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
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(stru

platform_set_drvdata(dev, NULL);

- if (rfk)
+ if (rfk) {
rfkill_unregister(rfk);
+ rfkill_destroy(rfk);
+ }
rfk = NULL;

tosa_bt_off(data);
--- wireless-testing.orig/arch/arm/mach-pxa/tosa.c 2009-04-15 00:50:15.000000000 +0200
+++ wireless-testing/arch/arm/mach-pxa/tosa.c 2009-04-15 00:53:57.000000000 +0200
@@ -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>
--- wireless-testing.orig/include/net/wimax.h 2009-04-15 00:50:15.000000000 +0200
+++ wireless-testing/include/net/wimax.h 2009-04-15 00:53:57.000000000 +0200
@@ -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)
--- wireless-testing.orig/net/wimax/op-rfkill.c 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/net/wimax/op-rfkill.c 2009-04-15 00:53:57.000000000 +0200
@@ -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
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

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
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
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,22 @@ int wimax_rfkill_add(struct wimax_dev *w
{
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);
-
- 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);
+ d_printf(1, dev, "rfkill %p\n", rfkill);
+
+ rfkill_has_sw_block_memory(rfkill);
+
+ 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 +367,8 @@ int wimax_rfkill_add(struct wimax_dev *w
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 +387,12 @@ void wimax_rfkill_rm(struct wimax_dev *w
{
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
*
--- wireless-testing.orig/net/wimax/Kconfig 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/net/wimax/Kconfig 2009-04-15 00:53:57.000000000 +0200
@@ -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
--- wireless-testing.orig/drivers/net/usb/hso.c 2009-04-15 00:50:14.000000000 +0200
+++ wireless-testing/drivers/net/usb/hso.c 2009-04-15 00:53:57.000000000 +0200
@@ -2481,10 +2481,10 @@ static int add_net_device(struct 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,
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
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 us
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]);
}
}
--- wireless-testing.orig/drivers/platform/x86/sony-laptop.c 2009-04-15 00:50:15.000000000 +0200
+++ wireless-testing/drivers/platform/x86/sony-laptop.c 2009-04-15 00:53:57.000000000 +0200
@@ -1051,56 +1051,45 @@ static void sony_nc_rfkill_cleanup(void)
int i;

for (i = 0; i < SONY_RFKILL_MAX; i++) {
- if (sony_rfkill_devices[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 const struct rfkill_ops sony_nc_rfkill_ops = {
+ .set_block = sony_nc_rfkill_set,
+};
+
+/* XXX: this code duplication is very stupid */
+
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);
+ sony_wifi_rfkill = rfkill_alloc("sony-wifi", &device->dev,
+ RFKILL_TYPE_WLAN,
+ &sony_nc_rfkill_ops,
+ (void *)SONY_WIFI);
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;
+ return -ENOMEM;
+
err = rfkill_register(sony_wifi_rfkill);
if (err)
- rfkill_free(sony_wifi_rfkill);
+ rfkill_destroy(sony_wifi_rfkill);
else
sony_rfkill_devices[SONY_WIFI] = sony_wifi_rfkill;
return err;
@@ -1111,17 +1100,16 @@ static int sony_nc_setup_bluetooth_rfkil
int err = 0;
struct rfkill *sony_bluetooth_rfkill;

- sony_bluetooth_rfkill = rfkill_allocate(&device->dev,
- RFKILL_TYPE_BLUETOOTH);
+ sony_bluetooth_rfkill = rfkill_alloc("sony-bluetooth", &device->dev,
+ RFKILL_TYPE_BLUETOOTH,
+ &sony_nc_rfkill_ops,
+ (void *)SONY_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;
+ return -ENOMEM;
+
err = rfkill_register(sony_bluetooth_rfkill);
if (err)
- rfkill_free(sony_bluetooth_rfkill);
+ rfkill_destroy(sony_bluetooth_rfkill);
else
sony_rfkill_devices[SONY_BLUETOOTH] = sony_bluetooth_rfkill;
return err;
@@ -1132,16 +1120,16 @@ static int sony_nc_setup_wwan_rfkill(str
int err = 0;
struct rfkill *sony_wwan_rfkill;

- sony_wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN);
+ sony_wwan_rfkill = rfkill_alloc("sony-wwan", &device->dev,
+ RFKILL_TYPE_WWAN,
+ &sony_nc_rfkill_ops,
+ (void *)SONY_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;
+ return -ENOMEM;
+
err = rfkill_register(sony_wwan_rfkill);
if (err)
- rfkill_free(sony_wwan_rfkill);
+ rfkill_destroy(sony_wwan_rfkill);
else
sony_rfkill_devices[SONY_WWAN] = sony_wwan_rfkill;
return err;
@@ -1152,16 +1140,16 @@ static int sony_nc_setup_wimax_rfkill(st
int err = 0;
struct rfkill *sony_wimax_rfkill;

- sony_wimax_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WIMAX);
+ sony_wimax_rfkill = rfkill_alloc("sony-wimax", &device->dev,
+ RFKILL_TYPE_WIMAX,
+ &sony_nc_rfkill_ops,
+ (void *)SONY_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;
+ return -ENOMEM;
+
err = rfkill_register(sony_wimax_rfkill);
if (err)
- rfkill_free(sony_wimax_rfkill);
+ rfkill_destroy(sony_wimax_rfkill);
else
sony_rfkill_devices[SONY_WIMAX] = sony_wimax_rfkill;
return err;
@@ -1169,15 +1157,28 @@ static int sony_nc_setup_wimax_rfkill(st

static void sony_nc_rfkill_update()
{
- int i;
- enum rfkill_state state;
+ int result, 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);
+ sony_call_snc_handle(0x124, 0x200, &result);
+ if (result & 0x1) {
+ int addr = sony_rfkill_address[i];
+
+ sony_call_snc_handle(0x124, addr, &result);
+ if (result & 0xf)
+ rfkill_set_sw_state(
+ sony_rfkill_devices[i],
+ false);
+ else
+ rfkill_set_sw_state(
+ sony_rfkill_devices[i],
+ true);
+ } else {
+ rfkill_set_hw_state(
+ sony_rfkill_devices[i],
+ true);
+ }
}
}
}



2009-04-30 16:09:40

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC v6] rfkill: rewrite

On Thu, 2009-04-30 at 12:53 -0300, Henrique de Moraes Holschuh wrote:

> > Actually, I was going to clean this up, and then I noticed you're
> > wrong. :) Returning ERR_PTR(-ENODEV) is intentional, in the non-RFKILL
> > case. Imagine a driver like this:
>
> Well, that's confusing as all heck... document it, please?

Well, ok. I don't really think it's all that confusing since you don't
need to think about the non-RFKILL code, you can just write the code as
though rfkill was always enabled.

> I still think in
> that case, we'd be better off if rfkill_alloc always uses the ERR_PTR
> convention.

Umm, no, think again. If we do that, the driver won't do
if (!rf)
but
if (IS_ERR(rf))
and the advantage is lost.

This is exactly what debugfs does too.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-04-14 21:49:13

by Johannes Berg

[permalink] [raw]
Subject: [RFC v5] rfkill: rewrite

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

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

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

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

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

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

* rfkill-input pointlessly is a separate module

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

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

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

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

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

Signed-off-by: Johannes Berg <[email protected]>
---
v3: * fix locking bugs
* fix bug in input.c that made it ignore things
* allow setting LED trigger name
* apply default state while registering
* convert HSO
v4: * LED triggering
v5: * convert sony
* introduce rfkill_set_states function
* change query/poll methods to require calls to
rfkill_set{_sw,_hw}_state/rfkill_set_states
* do not call driver from registration
* remove tested-by since it changed a lot...

TODO
* convert toshiba, thinkpad

Documentation/rfkill.txt | 639 +++-------------------
MAINTAINERS | 6
arch/arm/mach-pxa/tosa-bt.c | 30 -
arch/arm/mach-pxa/tosa.c | 1
drivers/net/usb/hso.c | 42 -
drivers/net/wireless/ath/ath9k/ath9k.h | 7
drivers/net/wireless/ath/ath9k/main.c | 115 +---
drivers/net/wireless/ath/ath9k/pci.c | 15
drivers/net/wireless/b43/Kconfig | 2
drivers/net/wireless/b43/leds.c | 2
drivers/net/wireless/b43/main.c | 4
drivers/net/wireless/b43/phy_a.c | 4
drivers/net/wireless/b43/phy_common.c | 17
drivers/net/wireless/b43/phy_common.h | 4
drivers/net/wireless/b43/phy_g.c | 4
drivers/net/wireless/b43/phy_lp.c | 2
drivers/net/wireless/b43/phy_n.c | 2
drivers/net/wireless/b43/rfkill.c | 123 +---
drivers/net/wireless/b43/rfkill.h | 5
drivers/net/wireless/b43legacy/Kconfig | 2
drivers/net/wireless/b43legacy/leds.c | 3
drivers/net/wireless/b43legacy/rfkill.c | 123 +---
drivers/net/wireless/b43legacy/rfkill.h | 6
drivers/net/wireless/iwlwifi/Kconfig | 5
drivers/net/wireless/iwlwifi/iwl-rfkill.c | 69 --
drivers/platform/x86/Kconfig | 6
drivers/platform/x86/acer-wmi.c | 50 -
drivers/platform/x86/dell-laptop.c | 101 +--
drivers/platform/x86/eeepc-laptop.c | 91 ---
drivers/platform/x86/hp-wmi.c | 103 +--
drivers/platform/x86/sony-laptop.c | 115 ++--
drivers/platform/x86/toshiba_acpi.c | 23
include/linux/Kbuild | 1
include/linux/rfkill.h | 344 +++++++++---
include/net/wimax.h | 8
net/rfkill/Kconfig | 14
net/rfkill/Makefile | 4
net/rfkill/core.c | 837 +++++++++++++++++++++++++++++
net/rfkill/input.c | 342 ++++++++++++
net/rfkill/rfkill-input.c | 390 -------------
net/rfkill/rfkill-input.h | 21
net/rfkill/rfkill.c | 843 ------------------------------
net/rfkill/rfkill.h | 27
net/wimax/Kconfig | 14
net/wimax/op-rfkill.c | 125 ----
45 files changed, 1999 insertions(+), 2692 deletions(-)

--- wireless-testing.orig/include/linux/rfkill.h 2009-04-14 21:59:08.000000000 +0200
+++ wireless-testing/include/linux/rfkill.h 2009-04-14 23:27:58.000000000 +0200
@@ -4,6 +4,7 @@
/*
* Copyright (C) 2006 - 2007 Ivo van Doorn
* Copyright (C) 2007 Dmitry Torokhov
+ * Copyright 2009 Johannes Berg <[email protected]>
*
* 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,11 +49,13 @@

/**
* 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 ,
@@ -42,97 +63,274 @@ enum rfkill_type {
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 hardware block state (return true
+ * for blocked) -- 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. This 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.
- *
- * This structure represents a RF switch located on a network device.
- */
-struct rfkill {
- const char *name;
- enum rfkill_type type;
-
- /* the mutex serializes callbacks and also protects
- * the state */
- struct mutex mutex;
- enum rfkill_state state;
- void *data;
- int (*toggle_radio)(void *data, enum rfkill_state state);
- int (*get_state)(void *data, enum rfkill_state *state);
+ * rfkill_alloc - allocate rfkill structure
+ * @name: name of the struct -- the string is not copied internally
+ * @parent: device that has rf switch on it
+ * @type: type of the switch (RFKILL_TYPE_*)
+ * @ops: rfkill methods
+ * @ops_data: data passed to each method
+ *
+ * This function should be called by the transmitter driver to allocate an
+ * rfkill structure.
+ */
+struct rfkill * __must_check rfkill_alloc(const char *name,
+ struct device *parent,
+ const enum rfkill_type type,
+ const struct rfkill_ops *ops,
+ void *ops_data);

-#ifdef CONFIG_RFKILL_LEDS
- struct led_trigger led_trigger;
-#endif
+/**
+ * rfkill_has_sw_block_memory -- turn on calling set_block unconditionally
+ * @rfkill: rfkill struct
+ *
+ * This function tells the rfkill core that the device is capable of
+ * remembering soft blocks (which it is notified of via the set_block
+ * method) -- this means that the driver may ignore the return value
+ * from rfkill_set_hw_state().
+ *
+ * This function must be called before rfkill_register().
+ */
+void rfkill_has_sw_block_memory(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_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);

-struct rfkill * __must_check rfkill_allocate(struct device *parent,
- enum rfkill_type type);
-void rfkill_free(struct rfkill *rfkill);
+/**
+ * rfkill_register - Register a rfkill structure.
+ * @rfkill: rfkill structure to be registered
+ *
+ * This function should be called by the transmitter driver to register
+ * the rfkill structure needs to be registered. Before calling this function
+ * the driver needs to be ready to service method calls from rfkill.
+ */
int __must_check rfkill_register(struct rfkill *rfkill);
+
+/**
+ * rfkill_pause_polling(struct rfkill *rfkill)
+ *
+ * Pause polling -- say transmitter is off for other reasons.
+ * NOTE: not necessary for suspend/resume -- in that case the
+ * core stops polling anyway
+ */
+void rfkill_pause_polling(struct rfkill *rfkill);
+
+/**
+ * rfkill_resume_polling(struct rfkill *rfkill)
+ *
+ * Pause polling -- say transmitter is off for other reasons.
+ * NOTE: not necessary for suspend/resume -- in that case the
+ * core stops polling anyway
+ */
+void rfkill_resume_polling(struct rfkill *rfkill);
+
+
+/**
+ * rfkill_unregister - Unregister a rfkill structure.
+ * @rfkill: rfkill structure to be unregistered
+ *
+ * This function should be called by the network driver during device
+ * teardown to destroy rfkill structure. Until it returns, the driver
+ * needs to be able to service method calls.
+ */
void rfkill_unregister(struct rfkill *rfkill);

-int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state);
-int rfkill_set_default(enum rfkill_type type, enum rfkill_state state);
+/**
+ * rfkill_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.
+ *
+ * If the device is able to, call rfkill_has_sw_block_memory() and
+ * ignore the return value of this function.
+ */
+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 __must_check 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
*
- * Returns RFKILL_STATE_SOFT_BLOCKED if @state is RFKILL_STATE_UNBLOCKED,
- * returns RFKILL_STATE_UNBLOCKED otherwise.
+ * This function sets the global default -- use at boot if your platform has
+ * an rfkill switch. If not early enough this call may be ignored.
+ *
+ * XXX: instead of ignoring -- how about just updating all currently
+ * registered drivers?
*/
-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 void rfkill_has_sw_block_memory(struct rfkill *rfkill)
+{
+}
+
+static inline int __must_check rfkill_register(struct rfkill *rfkill)
+{
+ if (rfkill == ERR_PTR(-ENODEV))
+ return 0;
+ return -EINVAL;
+}
+
+static inline void rfkill_pause_polling(struct rfkill *rfkill)
+{
+}
+
+static inline void rfkill_resume_polling(struct rfkill *rfkill)
+{
+}
+
+static inline void rfkill_unregister(struct rfkill *rfkill)
+{
+}
+
+static inline void rfkill_destroy(struct rfkill *rfkill)
+{
+}
+
+static inline bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked)
+{
+ return blocked;
+}
+
+static inline bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
{
- return (state == RFKILL_STATE_UNBLOCKED) ?
- RFKILL_STATE_SOFT_BLOCKED : RFKILL_STATE_UNBLOCKED;
+ return blocked;
}

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

+void rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name)
+{
+}
+#endif
+
+#endif /* __KERNEL__ */
+
#endif /* RFKILL_H */
--- wireless-testing.orig/drivers/net/wireless/ath/ath9k/main.c 2009-04-14 21:59:08.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath/ath9k/main.c 2009-04-14 23:20:19.000000000 +0200
@@ -1172,120 +1172,69 @@ static bool ath_is_rfkill_set(struct ath
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);
@@ -1654,10 +1603,6 @@ int ath_attach(u16 devid, struct ath_sof
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)
@@ -2150,10 +2095,8 @@ static void ath9k_stop(struct ieee80211_
} 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);
--- wireless-testing.orig/drivers/platform/x86/eeepc-laptop.c 2009-04-14 21:59:08.000000000 +0200
+++ wireless-testing/drivers/platform/x86/eeepc-laptop.c 2009-04-14 22:14:13.000000000 +0200
@@ -296,39 +296,15 @@ static int update_bl_status(struct backl
* Rfkill helpers
*/

-static int eeepc_wlan_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_WLAN, 0);
- else
- return set_acpi(CM_ASL_WLAN, 1);
-}
-
-static int eeepc_wlan_rfkill_state(void *data, enum rfkill_state *state)
-{
- if (get_acpi(CM_ASL_WLAN) == 1)
- *state = RFKILL_STATE_UNBLOCKED;
- else
- *state = RFKILL_STATE_SOFT_BLOCKED;
- return 0;
+ unsigned long asl = (unsigned long)data;
+ return set_acpi(asl, !blocked);
}

-static int eeepc_bluetooth_rfkill_set(void *data, enum rfkill_state state)
-{
- if (state == RFKILL_STATE_SOFT_BLOCKED)
- return set_acpi(CM_ASL_BLUETOOTH, 0);
- else
- return set_acpi(CM_ASL_BLUETOOTH, 1);
-}
-
-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
@@ -650,26 +626,17 @@ static int eeepc_hotk_add(struct acpi_de
printk(EEEPC_ERR "Error installing notify handler\n");

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_sw_state(ehotk->eeepc_wlan_rfkill,
+ get_acpi(CM_ASL_WLAN) != 1);
result = rfkill_register(ehotk->eeepc_wlan_rfkill);
if (result)
goto wlan_fail;
@@ -677,28 +644,17 @@ static int eeepc_hotk_add(struct acpi_de

if (get_acpi(CM_ASL_BLUETOOTH) != -1) {
ehotk->eeepc_bluetooth_rfkill =
- rfkill_allocate(&device->dev, RFKILL_TYPE_BLUETOOTH);
+ rfkill_alloc("eeepc-wlan",
+ &device->dev,
+ RFKILL_TYPE_WLAN,
+ &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_sw_state(ehotk->eeepc_bluetooth_rfkill,
+ get_acpi(CM_ASL_BLUETOOTH) != 1);
result = rfkill_register(ehotk->eeepc_bluetooth_rfkill);
if (result)
goto bluetooth_fail;
@@ -710,13 +666,10 @@ static int eeepc_hotk_add(struct acpi_de
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);
ehotk_fail:
kfree(ehotk);
ehotk = NULL;
--- wireless-testing.orig/net/rfkill/rfkill.c 2009-04-14 21:59:06.000000000 +0200
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,843 +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 <[email protected]>");
-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);
-}
-#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_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);
-
- 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_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);
-}
-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);
-
- 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);
- }
-
- 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);
--- wireless-testing.orig/net/rfkill/rfkill-input.h 2009-04-14 21:59:06.000000000 +0200
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2007 Ivo van Doorn
- */
-
-/*
- * 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.
- */
-
-#ifndef __RFKILL_INPUT_H
-#define __RFKILL_INPUT_H
-
-void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state);
-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);
-
-#endif /* __RFKILL_INPUT_H */
--- wireless-testing.orig/net/rfkill/rfkill-input.c 2009-04-14 21:59:06.000000000 +0200
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -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 <[email protected]>");
-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);
--- wireless-testing.orig/drivers/net/wireless/b43/main.c 2009-04-14 21:59:07.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/main.c 2009-04-14 22:14:14.000000000 +0200
@@ -3498,7 +3498,7 @@ static int b43_op_config(struct ieee8021

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 "
@@ -3506,7 +3506,7 @@ static int b43_op_config(struct ieee8021
"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");
}
}
--- wireless-testing.orig/drivers/net/wireless/b43/phy_a.c 2009-04-14 21:59:06.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_a.c 2009-04-14 22:14:14.000000000 +0200
@@ -480,11 +480,11 @@ static bool b43_aphy_op_supports_hwpctl(
}

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);
--- wireless-testing.orig/drivers/net/wireless/b43/phy_common.c 2009-04-14 21:59:06.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_common.c 2009-04-14 22:14:14.000000000 +0200
@@ -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);
}
@@ -287,18 +287,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);
}

--- wireless-testing.orig/drivers/net/wireless/b43/phy_common.h 2009-04-14 21:59:06.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_common.h 2009-04-14 22:14:14.000000000 +0200
@@ -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);
@@ -362,7 +362,7 @@ int b43_switch_channel(struct b43_wldev
/**
* 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.
--- wireless-testing.orig/drivers/net/wireless/b43/phy_g.c 2009-04-14 21:59:07.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_g.c 2009-04-14 22:14:14.000000000 +0200
@@ -2592,7 +2592,7 @@ static bool b43_gphy_op_supports_hwpctl(
}

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(

might_sleep();

- if (state == RFKILL_STATE_UNBLOCKED) {
+ if (!blocked) {
/* Turn radio ON */
if (phy->radio_on)
return;
--- wireless-testing.orig/drivers/net/wireless/b43/phy_n.c 2009-04-14 21:59:07.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_n.c 2009-04-14 22:14:14.000000000 +0200
@@ -579,7 +579,7 @@ static void b43_nphy_op_radio_write(stru
}

static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
- enum rfkill_state state)
+ bool blocked)
{//TODO
}

--- wireless-testing.orig/drivers/net/wireless/b43/rfkill.c 2009-04-14 21:59:07.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/rfkill.c 2009-04-14 23:25:51.000000000 +0200
@@ -45,12 +45,11 @@ static bool b43_is_hw_radio_enabled(stru
}

/* 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
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 (WARN_ON(!dev->radio_hw_enable && !blocked))
+ 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 *d

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 *d
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;
}
--- wireless-testing.orig/drivers/net/wireless/b43legacy/rfkill.c 2009-04-14 21:59:07.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/rfkill.c 2009-04-14 23:26:06.000000000 +0200
@@ -45,12 +45,11 @@ static bool b43legacy_is_hw_radio_enable
}

/* 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
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 (WARN_ON(!blocked && !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 b43leg

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 b43leg
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;
}

--- wireless-testing.orig/drivers/net/wireless/iwlwifi/iwl-rfkill.c 2009-04-14 21:59:07.000000000 +0200
+++ wireless-testing/drivers/net/wireless/iwlwifi/iwl-rfkill.c 2009-04-14 22:14:14.000000000 +0200
@@ -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 (WARN_ON(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 *pri
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 *pri
}

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_
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);
--- wireless-testing.orig/Documentation/rfkill.txt 2009-04-14 21:59:06.000000000 +0200
+++ wireless-testing/Documentation/rfkill.txt 2009-04-14 22:14:15.000000000 +0200
@@ -1,571 +1,132 @@
-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:
-
-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.
-
-Note that disabling the signal output means that the the transmitter is to be
-made to not emit any energy when "blocked". rfkill is not about blocking data
-transmissions, it is about blocking energy emission.
-
-The rfkill subsystem offers support for keys and switches often found on
-laptops to enable wireless devices like WiFi and Bluetooth, so that these keys
-and switches actually perform an action in all wireless devices of a given type
-attached to the system.
-
-The buttons to enable and disable the wireless transmitters are important in
-situations where the user is for example using his laptop on a location where
-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.
+1. Introduction
+2. Implementation details
+3. Kernel driver guidelines
+4. Kernel API
+5. Userspace support

-===============================================================================
-2: Implementation details
+
+1. Introduction
+
+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.
+
+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.
+
+
+
+2. Implementation details

The rfkill subsystem is composed of various components: the rfkill class, the
rfkill-input module (an input layer handler), and some specific input layer
events.

-The rfkill class provides kernel drivers with an interface that allows them to
-know when they should enable or disable a wireless network device transmitter.
-This is enabled by the CONFIG_RFKILL Kconfig option.
-
-The rfkill class support makes sure userspace will be notified of all state
-changes on rfkill devices through uevents. It provides a notification chain
-for interested parties in the kernel to also get notified of rfkill state
-changes in other drivers. It creates several sysfs entries which can be used
-by userspace. See section "Userspace support".
-
-The rfkill-input module provides the kernel with the ability to implement a
-basic response when the user presses a key or button (or toggles a switch)
-related to rfkill functionality. It is an in-kernel implementation of default
-policy of reacting to rfkill-related input events and neither mandatory nor
-required for wireless drivers to operate. It is enabled by the
-CONFIG_RFKILL_INPUT Kconfig option.
-
-rfkill-input is a rfkill-related events input layer handler. This handler will
-listen to all rfkill key events and will change the rfkill state of the
-wireless devices accordingly. With this option enabled userspace could either
-do nothing or simply perform monitoring tasks.
-
-The rfkill-input module also provides EPO (emergency power-off) functionality
-for all wireless transmitters. This function cannot be overridden, and it is
-always active. rfkill EPO is related to *_RFKILL_ALL input layer events.
-
-
-Important terms for the rfkill subsystem:
-
-In order to avoid confusion, we avoid the term "switch" in rfkill when it is
-referring to an electronic control circuit that enables or disables a
-transmitter. We reserve it for the physical device a human manipulates
-(which is an input device, by the way):
-
-rfkill switch:
-
- A physical device a human manipulates. Its state can be perceived by
- the kernel either directly (through a GPIO pin, ACPI GPE) or by its
- effect on a rfkill line of a wireless device.
-
-rfkill controller:
-
- A hardware circuit that controls the state of a rfkill line, which a
- kernel driver can interact with *to modify* that state (i.e. it has
- either write-only or read/write access).
-
-rfkill line:
-
- An input channel (hardware or software) of a wireless device, which
- causes a wireless transmitter to stop emitting energy (BLOCK) when it
- is active. Point of view is extremely important here: rfkill lines are
- always seen from the PoV of a wireless device (and its driver).
-
-soft rfkill line/software rfkill line:
-
- A rfkill line the wireless device driver can directly change the state
- of. Related to rfkill_state RFKILL_STATE_SOFT_BLOCKED.
-
-hard rfkill line/hardware rfkill line:
-
- A rfkill line that works fully in hardware or firmware, and that cannot
- be overridden by the kernel driver. The hardware device or the
- firmware just exports its status to the driver, but it is read-only.
- Related to rfkill_state RFKILL_STATE_HARD_BLOCKED.
-
-The enum rfkill_state describes the rfkill state of a transmitter:
-
-When a rfkill line or rfkill controller is in the RFKILL_STATE_UNBLOCKED state,
-the wireless transmitter (radio TX circuit for example) is *enabled*. When the
-it is in the RFKILL_STATE_SOFT_BLOCKED or RFKILL_STATE_HARD_BLOCKED, the
-wireless transmitter is to be *blocked* from operating.
-
-RFKILL_STATE_SOFT_BLOCKED indicates that a call to toggle_radio() can change
-that state. RFKILL_STATE_HARD_BLOCKED indicates that a call to toggle_radio()
-will not be able to change the state and will return with a suitable error if
-attempts are made to set the state to RFKILL_STATE_UNBLOCKED.
-
-RFKILL_STATE_HARD_BLOCKED is used by drivers to signal that the device is
-locked in the BLOCKED state by a hardwire rfkill line (typically an input pin
-that, when active, forces the transmitter to be disabled) which the driver
-CANNOT override.
-
-Full rfkill functionality requires two different subsystems to cooperate: the
-input layer and the rfkill class. The input layer issues *commands* to the
-entire system requesting that devices registered to the rfkill class change
-state. The way this interaction happens is not complex, but it is not obvious
-either:
-
-Kernel Input layer:
-
- * Generates KEY_WWAN, KEY_WLAN, KEY_BLUETOOTH, SW_RFKILL_ALL, and
- other such events when the user presses certain keys, buttons, or
- toggles certain physical switches.
-
- THE INPUT LAYER IS NEVER USED TO PROPAGATE STATUS, NOTIFICATIONS OR THE
- KIND OF STUFF AN ON-SCREEN-DISPLAY APPLICATION WOULD REPORT. It is
- used to issue *commands* for the system to change behaviour, and these
- commands may or may not be carried out by some kernel driver or
- userspace application. It follows that doing user feedback based only
- on input events is broken, as there is no guarantee that an input event
- will be acted upon.
-
- Most wireless communication device drivers implementing rfkill
- functionality MUST NOT generate these events, and have no reason to
- register themselves with the input layer. Doing otherwise is a common
- misconception. There is an API to propagate rfkill status change
- information, and it is NOT the input layer.
-
-rfkill class:
-
- * Calls a hook in a driver to effectively change the wireless
- transmitter state;
- * Keeps track of the wireless transmitter state (with help from
- the driver);
- * Generates userspace notifications (uevents) and a call to a
- notification chain (kernel) when there is a wireless transmitter
- state change;
- * Connects a wireless communications driver with the common rfkill
- control system, which, for example, allows actions such as
- "switch all bluetooth devices offline" to be carried out by
- userspace or by rfkill-input.
-
- THE RFKILL CLASS NEVER ISSUES INPUT EVENTS. THE RFKILL CLASS DOES
- NOT LISTEN TO INPUT EVENTS. NO DRIVER USING THE RFKILL CLASS SHALL
- EVER LISTEN TO, OR ACT ON RFKILL INPUT EVENTS. Doing otherwise is
- a layering violation.
-
- Most wireless data communication drivers in the kernel have just to
- implement the rfkill class API to work properly. Interfacing to the
- input layer is not often required (and is very often a *bug*) on
- wireless drivers.
-
- Platform drivers often have to attach to the input layer to *issue*
- (but never to listen to) rfkill events for rfkill switches, and also to
- the rfkill class to export a control interface for the platform rfkill
- controllers to the rfkill subsystem. This does NOT mean the rfkill
- switch is attached to a rfkill class (doing so is almost always wrong).
- It just means the same kernel module is the driver for different
- devices (rfkill switches and rfkill controllers).
-
-
-Userspace input handlers (uevents) or kernel input handlers (rfkill-input):
-
- * Implements the policy of what should happen when one of the input
- layer events related to rfkill operation is received.
- * Uses the sysfs interface (userspace) or private rfkill API calls
- to tell the devices registered with the rfkill class to change
- their state (i.e. translates the input layer event into real
- action).
-
- * rfkill-input implements EPO by handling EV_SW SW_RFKILL_ALL 0
- (power off all transmitters) in a special way: it ignores any
- overrides and local state cache and forces all transmitters to the
- RFKILL_STATE_SOFT_BLOCKED state (including those which are already
- supposed to be BLOCKED).
- * rfkill EPO will remain active until rfkill-input receives an
- EV_SW SW_RFKILL_ALL 1 event. While the EPO is active, transmitters
- are locked in the blocked state (rfkill will refuse to unblock them).
- * rfkill-input implements different policies that the user can
- select for handling EV_SW SW_RFKILL_ALL 1. It will unlock rfkill,
- and either do nothing (leave transmitters blocked, but now unlocked),
- restore the transmitters to their state before the EPO, or unblock
- them all.
-
-Userspace uevent handler or kernel platform-specific drivers hooked to the
-rfkill notifier chain:
-
- * Taps into the rfkill notifier chain or to KOBJ_CHANGE uevents,
- in order to know when a device that is registered with the rfkill
- class changes state;
- * Issues feedback notifications to the user;
- * In the rare platforms where this is required, synthesizes an input
- event to command all *OTHER* rfkill devices to also change their
- statues when a specific rfkill device changes state.
-
-
-===============================================================================
-3: Kernel driver guidelines
-
-Remember: point-of-view is everything for a driver that connects to the rfkill
-subsystem. All the details below must be measured/perceived from the point of
-view of the specific driver being modified.
-
-The first thing one needs to know is whether his driver should be talking to
-the rfkill class or to the input layer. In rare cases (platform drivers), it
-could happen that you need to do both, as platform drivers often handle a
-variety of devices in the same driver.
-
-Do not mistake input devices for rfkill controllers. The only type of "rfkill
-switch" device that is to be registered with the rfkill class are those
-directly controlling the circuits that cause a wireless transmitter to stop
-working (or the software equivalent of them), i.e. what we call a rfkill
-controller. Every other kind of "rfkill switch" is just an input device and
-MUST NOT be registered with the rfkill class.
-
-A driver should register a device with the rfkill class when ALL of the
-following conditions are met (they define a rfkill controller):
-
-1. The device is/controls a data communications wireless transmitter;
-
-2. The kernel can interact with the hardware/firmware to CHANGE the wireless
- transmitter state (block/unblock TX operation);
-
-3. The transmitter can be made to not emit any energy when "blocked":
- rfkill is not about blocking data transmissions, it is about blocking
- energy emission;
-
-A driver should register a device with the input subsystem to issue
-rfkill-related events (KEY_WLAN, KEY_BLUETOOTH, KEY_WWAN, KEY_WIMAX,
-SW_RFKILL_ALL, etc) when ALL of the folowing conditions are met:
-
-1. It is directly related to some physical device the user interacts with, to
- command the O.S./firmware/hardware to enable/disable a data communications
- wireless transmitter.
-
- Examples of the physical device are: buttons, keys and switches the user
- will press/touch/slide/switch to enable or disable the wireless
- communication device.
-
-2. It is NOT slaved to another device, i.e. there is no other device that
- issues rfkill-related input events in preference to this one.
-
- Please refer to the corner cases and examples section for more details.
-
-When in doubt, do not issue input events. For drivers that should generate
-input events in some platforms, but not in others (e.g. b43), the best solution
-is to NEVER generate input events in the first place. That work should be
-deferred to a platform-specific kernel module (which will know when to generate
-events through the rfkill notifier chain) or to userspace. This avoids the
-usual maintenance problems with DMI whitelisting.
-
-
-Corner cases and examples:
-====================================
-
-1. If the device is an input device that, because of hardware or firmware,
-causes wireless transmitters to be blocked regardless of the kernel's will, it
-is still just an input device, and NOT to be registered with the rfkill class.
-
-2. If the wireless transmitter switch control is read-only, it is an input
-device and not to be registered with the rfkill class (and maybe not to be made
-an input layer event source either, see below).
-
-3. If there is some other device driver *closer* to the actual hardware the
-user interacted with (the button/switch/key) to issue an input event, THAT is
-the device driver that should be issuing input events.
-
-E.g:
- [RFKILL slider switch] -- [GPIO hardware] -- [WLAN card rf-kill input]
- (platform driver) (wireless card driver)
-
-The user is closer to the RFKILL slide switch plaform driver, so the driver
-which must issue input events is the platform driver looking at the GPIO
-hardware, and NEVER the wireless card driver (which is just a slave). It is
-very likely that there are other leaves than just the WLAN card rf-kill input
-(e.g. a bluetooth card, etc)...
-
-On the other hand, some embedded devices do this:
-
- [RFKILL slider switch] -- [WLAN card rf-kill input]
- (wireless card driver)
-
-In this situation, the wireless card driver *could* register itself as an input
-device and issue rf-kill related input events... but in order to AVOID the need
-for DMI whitelisting, the wireless card driver does NOT do it. Userspace (HAL)
-or a platform driver (that exists only on these embedded devices) will do the
-dirty job of issuing the input events.
-
-
-COMMON MISTAKES in kernel drivers, related to rfkill:
-====================================
-
-1. NEVER confuse input device keys and buttons with input device switches.
-
- 1a. Switches are always set or reset. They report the current state
- (on position or off position).
-
- 1b. Keys and buttons are either in the pressed or not-pressed state, and
- that's it. A "button" that latches down when you press it, and
- unlatches when you press it again is in fact a switch as far as input
- devices go.
-
-Add the SW_* events you need for switches, do NOT try to emulate a button using
-KEY_* events just because there is no such SW_* event yet. Do NOT try to use,
-for example, KEY_BLUETOOTH when you should be using SW_BLUETOOTH instead.
-
-2. Input device switches (sources of EV_SW events) DO store their current state
-(so you *must* initialize it by issuing a gratuitous input layer event on
-driver start-up and also when resuming from sleep), and that state CAN be
-queried from userspace through IOCTLs. There is no sysfs interface for this,
-but that doesn't mean you should break things trying to hook it to the rfkill
-class to get a sysfs interface :-)
-
-3. Do not issue *_RFKILL_ALL events by default, unless you are sure it is the
-correct event for your switch/button. These events are emergency power-off
-events when they are trying to turn the transmitters off. An example of an
-input device which SHOULD generate *_RFKILL_ALL events is the wireless-kill
-switch in a laptop which is NOT a hotkey, but a real sliding/rocker switch.
-An example of an input device which SHOULD NOT generate *_RFKILL_ALL events by
-default, is any sort of hot key that is type-specific (e.g. the one for WLAN).
-
-
-3.1 Guidelines for wireless device drivers
-------------------------------------------
-
-(in this text, rfkill->foo means the foo field of struct rfkill).
-
-1. Each independent transmitter in a wireless device (usually there is only one
-transmitter per device) should have a SINGLE rfkill class attached to it.
-
-2. If the device does not have any sort of hardware assistance to allow the
-driver to rfkill the device, the driver should emulate it by taking all actions
-required to silence the transmitter.
-
-3. If it is impossible to silence the transmitter (i.e. it still emits energy,
-even if it is just in brief pulses, when there is no data to transmit and there
-is no hardware support to turn it off) do NOT lie to the users. Do not attach
-it to a rfkill class. The rfkill subsystem does not deal with data
-transmission, it deals with energy emission. If the transmitter is emitting
-energy, it is not blocked in rfkill terms.
-
-4. It doesn't matter if the device has multiple rfkill input lines affecting
-the same transmitter, their combined state is to be exported as a single state
-per transmitter (see rule 1).
-
-This rule exists because users of the rfkill subsystem expect to get (and set,
-when possible) the overall transmitter rfkill state, not of a particular rfkill
-line.
-
-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.
+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".

-3. Deactivates the soft rfkill line if none of the hardware rfkill lines are
-active and the caller asked for RFKILL_STATE_UNBLOCKED.
+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.

-===============================================================================
-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.
+The rfkill-input code implements a basic response to rfkill buttons -- it
+implements turning on/off all devices of a certain class (or all).

-The 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).
+When the device is hard-blocked (either by a call to rfkill_set_hw_state()
+or from query_hw_block) then set_block() will not be invoked. When the device
+is hard-unblocked again, rfkill drivers need to take into account the return
+value from rfkill_set_hw_state() -- it indicates whether the device is still
+soft-blocked or not. Some devices can actually keep track of soft blocks,
+for those you can call rfkill_has_sw_block_memory() and will then get soft
+block notifications while hard-blocked.

-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.
+The entire functionality is spread over more than one subsystem:

-===============================================================================
-5: Userspace support
+ * The kernel input layer generates KEY_WWAN, KEY_WLAN etc. and
+ SW_RFKILL_ALL -- when the user presses a button. Drivers for radio
+ transmitters generally do not register to the input layer, unless the
+ device really provides an input device (i.e. a button that has no
+ effect other than generating a button press event)

-rfkill devices issue uevents (with an action of "change"), with the following
-environment variables set:
+ * The rfkill-input code hooks up to these events and switches the soft-block
+ of the various radio transmitters, depending on the button type.

-RFKILL_NAME
-RFKILL_STATE
-RFKILL_TYPE
+ * The rfkill drivers turn off/on their transmitters as requested.

-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.
+ * The rfkill class will generate userspace notifications (uevents) to tell
+ userspace what the current state is.

-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.
+3. Kernel driver guidelines

-Userspace cannot assume it is the only source of control for rfkill switches.
-Their state can change due to firmware actions, direct user actions, and the
-rfkill-input EPO override for *_RFKILL_ALL.

-When rfkill-input is not active, userspace must initiate a rfkill status
-change by writing to the "state" attribute in order for anything to happen.
+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.

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

-The following sysfs entries will be created:
+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.
+
+
+
+4. Kernel API
+
+To build a driver with rfkill subsystem support, the driver should depend on
+(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).
+
+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.
+
+
+
+5. Userspace support
+
+The following sysfs entries exist for every rfkill device:

name: Name assigned by driver to this key (interface or driver name).
type: Name of the key type ("wlan", "bluetooth", etc).
state: Current state of the transmitter
0: RFKILL_STATE_SOFT_BLOCKED
- transmitter is forced off, but one can override it
- by a write to the state attribute;
+ transmitter is turned off by software
1: RFKILL_STATE_UNBLOCKED
- transmiter is NOT forced off, and may operate if
- all other conditions for such operation are met
- (such as interface is up and configured, etc);
+ transmiter is (potentially) active
2: RFKILL_STATE_HARD_BLOCKED
transmitter is forced off by something outside of
- the driver's control. One cannot set a device to
- this state through writes to the state attribute;
- claim: 1: Userspace handles events, 0: Kernel handles events
-
-Both the "state" and "claim" entries are also writable. For the "state" entry
-this means that when 1 or 0 is written, the device rfkill state (if not yet in
-the requested state), will be will be toggled accordingly.
-
-For the "claim" entry writing 1 to it means that the kernel no longer handles
-key events even though RFKILL_INPUT input was enabled. When "claim" has been
-set to 0, userspace should make sure that it listens for the input events or
-check the sysfs "state" entry regularly to correctly perform the required tasks
-when the rkfill key is pressed.
-
-A note about input devices and EV_SW events:
-
-In order to know the current state of an input device switch (like
-SW_RFKILL_ALL), you will need to use an IOCTL. That information is not
-available through sysfs in a generic way at this time, and it is not available
-through the rfkill class AT ALL.
+ the driver's control.
+ claim: 0: Kernel handles events (currently always reads that value)
+
+rfkill devices also issue uevents (with an action of "change"), with the
+following environment variables set:
+
+RFKILL_NAME
+RFKILL_STATE
+RFKILL_TYPE
+
+The contents of these variables corresponds to the "name", "state" and
+"type" sysfs files explained above.
--- wireless-testing.orig/net/rfkill/Kconfig 2009-04-14 21:59:06.000000000 +0200
+++ wireless-testing/net/rfkill/Kconfig 2009-04-14 22:14:15.000000000 +0200
@@ -10,22 +10,8 @@ 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
default y
-
--- wireless-testing.orig/net/rfkill/Makefile 2009-04-14 21:59:06.000000000 +0200
+++ wireless-testing/net/rfkill/Makefile 2009-04-14 22:14:15.000000000 +0200
@@ -2,5 +2,5 @@
# Makefile for the RF switch subsystem.
#

-obj-$(CONFIG_RFKILL) += rfkill.o
-obj-$(CONFIG_RFKILL_INPUT) += rfkill-input.o
+rfkill-y += core.o input.o
+obj-$(CONFIG_RFKILL) += rfkill.o
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/rfkill/core.c 2009-04-14 23:44:01.000000000 +0200
@@ -0,0 +1,837 @@
+/*
+ * Copyright (C) 2006 - 2007 Ivo van Doorn
+ * Copyright (C) 2007 Dmitry Torokhov
+ * Copyright 2009 Johannes Berg <[email protected]>
+ *
+ * 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 "rfkill.h"
+
+#define POLL_INTERVAL (5 * HZ)
+
+#define RFKILL_BLOCK_HW_BIT 0
+#define RFKILL_BLOCK_SW_BIT 1
+
+struct rfkill {
+ const char *name;
+ enum rfkill_type type;
+
+ unsigned long blocked;
+
+ bool registered;
+ bool has_sw_block_memory;
+
+ 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 <[email protected]>");
+MODULE_AUTHOR("Johannes Berg <[email protected]>");
+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->blocked)
+ 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)
+ return;
+
+ kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE);
+}
+
+static bool __rfkill_set_hw_state(struct rfkill *rfkill,
+ bool blocked, bool *change)
+{
+ bool prev;
+
+ BUG_ON(!rfkill);
+
+ if (blocked)
+ prev = !!test_and_set_bit(RFKILL_BLOCK_HW_BIT,
+ &rfkill->blocked);
+ else
+ prev = !!test_and_clear_bit(RFKILL_BLOCK_HW_BIT,
+ &rfkill->blocked);
+
+ *change = prev != blocked;
+
+ rfkill_led_trigger_event(rfkill);
+
+ return blocked || !!test_bit(RFKILL_BLOCK_SW_BIT, &rfkill->blocked);
+}
+
+/**
+ * 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)
+{
+ bool prev;
+
+ /*
+ * 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);
+
+ if (blocked)
+ prev = !!test_and_set_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+ else
+ prev = !!test_and_clear_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+
+ /*
+ * HW already blocked, so nothing changes for now
+ * (unless device has memory for software blocks)
+ */
+ if (!rfkill->has_sw_block_memory &&
+ test_bit(RFKILL_BLOCK_HW_BIT, &rfkill->blocked))
+ return;
+
+ if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP))
+ return;
+
+ if (rfkill->ops->set_block(rfkill->data, blocked)) {
+ /* failed -- reset status */
+ if (prev)
+ set_bit(RFKILL_BLOCK_SW_BIT, &rfkill->blocked);
+ else
+ clear_bit(RFKILL_BLOCK_SW_BIT, &rfkill->blocked);
+ }
+
+ 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);
+}
+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)
+ 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 (change)
+ schedule_work(&rfkill->uevent_work);
+
+ return ret;
+}
+EXPORT_SYMBOL(rfkill_set_hw_state);
+
+bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
+{
+ bool prev, hwblock;
+
+ BUG_ON(!rfkill);
+
+ if (blocked)
+ prev = !!test_and_set_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+ else
+ prev = !!test_and_clear_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+
+ hwblock = !!test_bit(RFKILL_BLOCK_HW_BIT, &rfkill->blocked);
+
+ if (prev != blocked && !hwblock)
+ schedule_work(&rfkill->uevent_work);
+
+ rfkill_led_trigger_event(rfkill);
+
+ return blocked || hwblock;
+}
+EXPORT_SYMBOL(rfkill_set_sw_state);
+
+void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw)
+{
+ bool swprev, hwprev;
+
+ BUG_ON(!rfkill);
+
+ if (sw)
+ swprev = !!test_and_set_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+ else
+ swprev = !!test_and_clear_bit(RFKILL_BLOCK_SW_BIT,
+ &rfkill->blocked);
+
+ if (hw)
+ hwprev = !!test_and_set_bit(RFKILL_BLOCK_HW_BIT,
+ &rfkill->blocked);
+ else
+ hwprev = !!test_and_clear_bit(RFKILL_BLOCK_HW_BIT,
+ &rfkill->blocked);
+
+ 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 *blocked)
+{
+ if (test_bit(RFKILL_BLOCK_HW_BIT, blocked))
+ return RFKILL_USER_STATE_HARD_BLOCKED;
+ if (test_bit(RFKILL_BLOCK_SW_BIT, blocked))
+ 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);
+
+ return sprintf(buf, "%d\n", user_state_from_blocked(&rfkill->blocked));
+}
+
+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);
+ 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",
+ user_state_from_blocked(&rfkill->blocked));
+ 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);
+
+ return 0;
+}
+
+static int rfkill_resume(struct device *dev)
+{
+ struct rfkill *rfkill = to_rfkill(dev);
+
+ 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;
+
+ 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, uevent_work);
+
+ mutex_lock(&rfkill_global_mutex);
+ cur = rfkill_global_states[rfkill->type].cur;
+ mutex_unlock(&rfkill_global_mutex);
+
+ rfkill_set_block(rfkill, cur);
+}
+
+void rfkill_has_sw_block_memory(struct rfkill *rfkill)
+{
+ BUG_ON(!rfkill);
+ BUG_ON(rfkill->registered);
+
+ rfkill->has_sw_block_memory = true;
+}
+EXPORT_SYMBOL(rfkill_has_sw_block_memory);
+
+int __must_check rfkill_register(struct rfkill *rfkill)
+{
+ static unsigned long rfkill_no = 0;
+ 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);
+
+ 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;
+
+ error = rfkill_handler_init();
+ if (error)
+ class_unregister(&rfkill_class);
+
+ out:
+ return error;
+}
+subsys_initcall(rfkill_init);
+
+static void __exit rfkill_exit(void)
+{
+ rfkill_handler_exit();
+ class_unregister(&rfkill_class);
+}
+module_exit(rfkill_exit);
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/rfkill/input.c 2009-04-14 22:14:15.000000000 +0200
@@ -0,0 +1,342 @@
+/*
+ * Input layer to RF Kill interface connector
+ *
+ * Copyright (c) 2007 Dmitry Torokhov
+ * Copyright 2009 Johannes Berg <[email protected]>
+ *
+ * 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))
+ break;
+ 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);
+}
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/rfkill/rfkill.h 2009-04-14 22:14:15.000000000 +0200
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2007 Ivo van Doorn
+ * Copyright 2009 Johannes Berg <[email protected]>
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef __RFKILL_INPUT_H
+#define __RFKILL_INPUT_H
+
+/* 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);
+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 */
--- wireless-testing.orig/MAINTAINERS 2009-04-14 21:59:08.000000000 +0200
+++ wireless-testing/MAINTAINERS 2009-04-14 22:14:15.000000000 +0200
@@ -3762,9 +3762,9 @@ L: [email protected]
S: Supported

RFKILL
-P: Ivo van Doorn
-M: [email protected]
-L: [email protected]
+P: Johannes Berg
+M: [email protected]
+L: [email protected]
S: Maintained
F: net/rfkill

--- wireless-testing.orig/include/linux/Kbuild 2009-04-14 21:59:08.000000000 +0200
+++ wireless-testing/include/linux/Kbuild 2009-04-14 22:14:15.000000000 +0200
@@ -310,6 +310,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
--- wireless-testing.orig/drivers/net/wireless/ath/ath9k/ath9k.h 2009-04-14 21:59:07.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath/ath9k/ath9k.h 2009-04-14 22:14:15.000000000 +0200
@@ -464,12 +464,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];
};

@@ -513,8 +510,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)
--- wireless-testing.orig/drivers/net/wireless/ath/ath9k/pci.c 2009-04-14 21:59:08.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath/ath9k/pci.c 2009-04-14 22:14:15.000000000 +0200
@@ -227,11 +227,6 @@ static int ath_pci_suspend(struct pci_de

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
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;
}

--- wireless-testing.orig/drivers/net/wireless/b43/Kconfig 2009-04-14 22:03:31.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/Kconfig 2009-04-14 22:14:15.000000000 +0200
@@ -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,
--- wireless-testing.orig/drivers/net/wireless/b43/rfkill.h 2009-04-14 21:59:07.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/rfkill.h 2009-04-14 22:14:15.000000000 +0200
@@ -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 */
--- wireless-testing.orig/drivers/net/wireless/b43legacy/Kconfig 2009-04-14 21:59:07.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/Kconfig 2009-04-14 22:14:15.000000000 +0200
@@ -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,
--- wireless-testing.orig/drivers/net/wireless/b43legacy/leds.c 2009-04-14 21:59:07.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/leds.c 2009-04-14 22:14:15.000000000 +0200
@@ -86,7 +86,8 @@ static void b43legacy_led_brightness_set

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;
--- wireless-testing.orig/drivers/net/wireless/b43legacy/rfkill.h 2009-04-14 21:59:07.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/rfkill.h 2009-04-14 22:14:15.000000000 +0200
@@ -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 */
--- wireless-testing.orig/drivers/net/wireless/b43/leds.c 2009-04-14 21:59:07.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/leds.c 2009-04-14 22:14:15.000000000 +0200
@@ -87,7 +87,7 @@ static void b43_led_brightness_set(struc
}

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;
--- wireless-testing.orig/drivers/net/wireless/iwlwifi/Kconfig 2009-04-14 21:59:07.000000000 +0200
+++ wireless-testing/drivers/net/wireless/iwlwifi/Kconfig 2009-04-14 22:14:15.000000000 +0200
@@ -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"
--- wireless-testing.orig/drivers/net/wireless/b43/phy_lp.c 2009-04-14 21:59:07.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_lp.c 2009-04-14 22:14:15.000000000 +0200
@@ -488,7 +488,7 @@ static void b43_lpphy_op_radio_write(str
}

static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev,
- enum rfkill_state state)
+ bool blocked)
{
//TODO
}
--- wireless-testing.orig/drivers/platform/x86/Kconfig 2009-04-14 21:59:08.000000000 +0200
+++ wireless-testing/drivers/platform/x86/Kconfig 2009-04-14 22:14:15.000000000 +0200
@@ -21,7 +21,6 @@ config ACER_WMI
depends on NEW_LEDS
depends on BACKLIGHT_CLASS_DEVICE
depends on SERIO_I8042
- depends on RFKILL
select ACPI_WMI
---help---
This is a driver for newer Acer (and Wistron) laptops. It adds
@@ -60,7 +59,6 @@ config DELL_LAPTOP
depends on DCDBAS
depends on EXPERIMENTAL
depends on BACKLIGHT_CLASS_DEVICE
- depends on RFKILL
depends on POWER_SUPPLY
default n
---help---
@@ -117,7 +115,6 @@ config HP_WMI
tristate "HP WMI extras"
depends on ACPI_WMI
depends on INPUT
- depends on RFKILL
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.
@@ -203,7 +200,6 @@ config THINKPAD_ACPI
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
@@ -340,7 +336,6 @@ config EEEPC_LAPTOP
depends on EXPERIMENTAL
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.
@@ -407,7 +402,6 @@ config ACPI_TOSHIBA
depends on INPUT
select INPUT_POLLDEV
select NET
- select RFKILL
select BACKLIGHT_CLASS_DEVICE
---help---
This driver adds support for access to certain system settings
--- wireless-testing.orig/drivers/platform/x86/acer-wmi.c 2009-04-14 21:59:08.000000000 +0200
+++ wireless-testing/drivers/platform/x86/acer-wmi.c 2009-04-14 22:14:15.000000000 +0200
@@ -958,58 +958,50 @@ static void acer_rfkill_update(struct wo

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 devic
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 devic
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;
}
--- wireless-testing.orig/drivers/platform/x86/dell-laptop.c 2009-04-14 21:59:08.000000000 +0200
+++ wireless-testing/drivers/platform/x86/dell-laptop.c 2009-04-14 23:10:21.000000000 +0200
@@ -174,10 +174,11 @@ dell_send_request(struct calling_interfa
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, en
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;
}
--- wireless-testing.orig/drivers/platform/x86/hp-wmi.c 2009-04-14 21:59:08.000000000 +0200
+++ wireless-testing/drivers/platform/x86/hp-wmi.c 2009-04-14 22:14:15.000000000 +0200
@@ -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, voi
}
} 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(stru
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(stru

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(str
{
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;
}
--- wireless-testing.orig/drivers/platform/x86/toshiba_acpi.c 2009-04-14 21:59:08.000000000 +0200
+++ wireless-testing/drivers/platform/x86/toshiba_acpi.c 2009-04-14 22:14:15.000000000 +0200
@@ -311,28 +311,20 @@ static u32 hci_get_radio_state(bool *rad
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)
{
u32 result1, result2;
u32 value;
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;
+ return -EBUSY;

- switch (state) {
- case RFKILL_STATE_UNBLOCKED:
- if (!radio_state)
- return -EPERM;
- break;
- case RFKILL_STATE_SOFT_BLOCKED:
- break;
- default:
- return -EINVAL;
- }
+ if (WARN_ON(!blocked && !radio_state))
+ return;

mutex_lock(&dev->mutex);
hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1);
@@ -340,8 +332,7 @@ static int bt_rfkill_toggle_radio(void *
mutex_unlock(&dev->mutex);

if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS)
- return -EFAULT;
-
+ return -EBUSY;
return 0;
}

@@ -365,7 +356,7 @@ static void bt_poll_rfkill(struct input_
mutex_unlock(&dev->mutex);

if (unlikely(state_changed)) {
- rfkill_force_state(dev->rfk_dev,
+ rfkill_set_hw_state(dev->rfk_dev,
new_rfk_state ?
RFKILL_STATE_SOFT_BLOCKED :
RFKILL_STATE_HARD_BLOCKED);
--- wireless-testing.orig/arch/arm/mach-pxa/tosa-bt.c 2009-04-14 21:59:08.000000000 +0200
+++ wireless-testing/arch/arm/mach-pxa/tosa-bt.c 2009-04-14 22:14:15.000000000 +0200
@@ -35,21 +35,25 @@ static void tosa_bt_off(struct tosa_bt_d
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
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
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(stru

platform_set_drvdata(dev, NULL);

- if (rfk)
+ if (rfk) {
rfkill_unregister(rfk);
+ rfkill_destroy(rfk);
+ }
rfk = NULL;

tosa_bt_off(data);
--- wireless-testing.orig/arch/arm/mach-pxa/tosa.c 2009-04-14 21:59:08.000000000 +0200
+++ wireless-testing/arch/arm/mach-pxa/tosa.c 2009-04-14 22:14:15.000000000 +0200
@@ -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>
--- wireless-testing.orig/include/net/wimax.h 2009-04-14 21:59:08.000000000 +0200
+++ wireless-testing/include/net/wimax.h 2009-04-14 22:14:15.000000000 +0200
@@ -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)
--- wireless-testing.orig/net/wimax/op-rfkill.c 2009-04-14 21:59:06.000000000 +0200
+++ wireless-testing/net/wimax/op-rfkill.c 2009-04-14 22:14:16.000000000 +0200
@@ -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
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

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
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
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,22 @@ int wimax_rfkill_add(struct wimax_dev *w
{
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);
-
- 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);
+ d_printf(1, dev, "rfkill %p\n", rfkill);
+
+ rfkill_has_sw_block_memory(rfkill);
+
+ 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 +367,8 @@ int wimax_rfkill_add(struct wimax_dev *w
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 +387,12 @@ void wimax_rfkill_rm(struct wimax_dev *w
{
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
*
--- wireless-testing.orig/net/wimax/Kconfig 2009-04-14 21:59:06.000000000 +0200
+++ wireless-testing/net/wimax/Kconfig 2009-04-14 22:14:16.000000000 +0200
@@ -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
--- wireless-testing.orig/drivers/net/usb/hso.c 2009-04-14 21:59:08.000000000 +0200
+++ wireless-testing/drivers/net/usb/hso.c 2009-04-14 22:14:16.000000000 +0200
@@ -2481,10 +2481,10 @@ static int add_net_device(struct 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,
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
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 us
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]);
}
}
--- wireless-testing.orig/drivers/platform/x86/sony-laptop.c 2009-04-14 22:25:32.000000000 +0200
+++ wireless-testing/drivers/platform/x86/sony-laptop.c 2009-04-14 22:42:26.000000000 +0200
@@ -1051,56 +1051,45 @@ static void sony_nc_rfkill_cleanup(void)
int i;

for (i = 0; i < SONY_RFKILL_MAX; i++) {
- if (sony_rfkill_devices[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 const struct rfkill_ops sony_nc_rfkill_ops = {
+ .set_block = sony_nc_rfkill_set,
+};
+
+/* XXX: this code duplication is very stupid */
+
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);
+ sony_wifi_rfkill = rfkill_alloc("sony-wifi", &device->dev,
+ RFKILL_TYPE_WLAN,
+ &sony_nc_rfkill_ops,
+ (void *)SONY_WIFI);
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;
+ return -ENOMEM;
+
err = rfkill_register(sony_wifi_rfkill);
if (err)
- rfkill_free(sony_wifi_rfkill);
+ rfkill_destroy(sony_wifi_rfkill);
else
sony_rfkill_devices[SONY_WIFI] = sony_wifi_rfkill;
return err;
@@ -1111,17 +1100,16 @@ static int sony_nc_setup_bluetooth_rfkil
int err = 0;
struct rfkill *sony_bluetooth_rfkill;

- sony_bluetooth_rfkill = rfkill_allocate(&device->dev,
- RFKILL_TYPE_BLUETOOTH);
+ sony_bluetooth_rfkill = rfkill_alloc("sony-bluetooth", &device->dev,
+ RFKILL_TYPE_BLUETOOTH,
+ &sony_nc_rfkill_ops,
+ (void *)SONY_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;
+ return -ENOMEM;
+
err = rfkill_register(sony_bluetooth_rfkill);
if (err)
- rfkill_free(sony_bluetooth_rfkill);
+ rfkill_destroy(sony_bluetooth_rfkill);
else
sony_rfkill_devices[SONY_BLUETOOTH] = sony_bluetooth_rfkill;
return err;
@@ -1132,16 +1120,16 @@ static int sony_nc_setup_wwan_rfkill(str
int err = 0;
struct rfkill *sony_wwan_rfkill;

- sony_wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN);
+ sony_wwan_rfkill = rfkill_alloc("sony-wwan", &device->dev,
+ RFKILL_TYPE_WWAN,
+ &sony_nc_rfkill_ops,
+ (void *)SONY_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;
+ return -ENOMEM;
+
err = rfkill_register(sony_wwan_rfkill);
if (err)
- rfkill_free(sony_wwan_rfkill);
+ rfkill_destroy(sony_wwan_rfkill);
else
sony_rfkill_devices[SONY_WWAN] = sony_wwan_rfkill;
return err;
@@ -1152,16 +1140,16 @@ static int sony_nc_setup_wimax_rfkill(st
int err = 0;
struct rfkill *sony_wimax_rfkill;

- sony_wimax_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WIMAX);
+ sony_wimax_rfkill = rfkill_alloc("sony-wimax", &device->dev,
+ RFKILL_TYPE_WIMAX,
+ &sony_nc_rfkill_ops,
+ (void *)SONY_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;
+ return -ENOMEM;
+
err = rfkill_register(sony_wimax_rfkill);
if (err)
- rfkill_free(sony_wimax_rfkill);
+ rfkill_destroy(sony_wimax_rfkill);
else
sony_rfkill_devices[SONY_WIMAX] = sony_wimax_rfkill;
return err;
@@ -1169,15 +1157,28 @@ static int sony_nc_setup_wimax_rfkill(st

static void sony_nc_rfkill_update()
{
- int i;
- enum rfkill_state state;
+ int result, 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);
+ sony_call_snc_handle(0x124, 0x200, &result);
+ if (result & 0x1) {
+ int addr = sony_rfkill_address[i];
+
+ sony_call_snc_handle(0x124, addr, &result);
+ if (result & 0xf)
+ rfkill_set_sw_state(
+ sony_rfkill_devices[i],
+ false);
+ else
+ rfkill_set_sw_state(
+ sony_rfkill_devices[i],
+ true);
+ } else {
+ rfkill_set_hw_state(
+ sony_rfkill_devices[i],
+ true);
+ }
}
}
}



2009-04-15 04:34:29

by Larry Finger

[permalink] [raw]
Subject: Re: [RFC v6] rfkill: rewrite

Johannes Berg wrote:
> This patch completely rewrites the rfkill core to address
> the following deficiencies:

> Signed-off-by: Johannes Berg <[email protected]>
> ---
> v3: * fix locking bugs
> * fix bug in input.c that made it ignore things
> * allow setting LED trigger name
> * apply default state while registering
> * convert HSO
> v4: * LED triggering
> v5: * convert sony
> * introduce rfkill_set_states function
> * change query/poll methods to require calls to
> rfkill_set{_sw,_hw}_state/rfkill_set_states
> * do not call driver from registration
> * remove tested-by since it changed a lot
> v6: * fix stupid sync work bugs

V6 works with b43legacy.

Tested-by: Larry Finger <[email protected]>

2009-04-07 12:59:34

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

Hi!

> > > +struct rfkill_ops {
> > > +#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
> > > + bool (*poll_hw_block)(void *data);
> > > + bool (*query_state)(void *data);
> > > + void (*set_block)(void *data, bool blocked);
> > > #endif
> >
> > Missing error handling on set_block... could you change that to:
>
> And also on the rest of the hooks. I should have noticed it sooner!
>
> As I said, ACPI platform drivers _do_ get errors when calling the ACPI
> functions, or trying to do an EC transaction. That applies to reads
> just as well as it applies to writes.

And it also doesn't really matter. Yes, the current poll API isn't
prepared to treat errors, but see below for a suggestion.

> I.e. I can get AE_BUSY or something else while querying the thinkpad
> firmware, and then, what should I do to return the data on the
> query_state (and, for that matter, poll_hw_block) hooks?
>
> Since there can be a clear error path from userspace to the rfkill
> driver for reads and writes in many situations (e.g. on all sysfs
> accesses to that driver's rfkill struct), this would be a good reason to
> change the hooks to allow for that error path.

Totally bogus argument -- 99.9% of users will be using rfkill's input
handler which has no way of reacting to errors, and neither does
userspace. And it's wrong too -- the sysfs files don't actually call
into rfkill, they just report the current status.

> Without that, I'd have, for example, to return false (unblocked) on any
> errors from thinkpad-acpi, for safety reasons (a blocked radio is never
> a hazard, but an unblocked one can be). This is the kind of stuff that
> a driver shouldn't need to decide by itself...

Eh? Your first half sentence is correct (up to "on any errors"), the
rest is garbage. Yes, you'd have to return false so that the core
assumes it can do something, but no, it has nothing to do with hazards.

> On a related note, it was not clear to me from looking only at
> rfkill.txt and the linux/rfkill.h headers, how am I supposed to deal
> with the software controlled component of the rfkill controller (the
> hardware rfkill line is quite obvious, though). I know there is a way,
> but I haven't finished reading the core code to know which one, and the
> docs didn't help, so they need an extra paragraph somewhere...

There's a function for that -- but no poll yet.

> Anyway, I suggest changing the hook protypes to this (which does _NOT_
> change the fact that you now track hw and sw separately in the core, but
> lets the rfkill driver deliver both in a single call if it needs to):
>
> int (*poll_block_state)(void *data,
> bool *is_sw_block_active,
> bool *is_hw_block_active);
> int (*query_block_state)(void *data,
> bool *is_sw_block_active,
> bool *is_hw_block_active);
> int (*set_block_state)(void *data, bool do_sw_block);

No way.

I can be convinced to change the prototype of poll_hw_block to

void (*poll_hw_block)(void *data, bool *blocked);

so that drivers can say
"*shrug*, I dunno right now, keep whatever I told you last".
Returning an error, however, is completely pointless.

> The core should ignore the is_* stuff if an error status is returned,
> as a defensive approach against driver bugs: doing it that way is less
> programmer-error-prone than telling people to not touch is_* in case of
> an error.

Bugs happen, and can be fixed; your defensive programming mantra just
makes for messy code that has multiple ways of working "correctly". I
quite obviously disagree -- if you can't read the status don't touch the
variables.

> BTW: it is probably a good idea to document what the driver should do if
> the core tries this on a radio that is hardware blocked, or if the
> driver discovers the radio is hardware blocked the instant it tries to
> software unblock it...

The first case doesn't happen... And it says so in the header file:

* @set_block: turn the transmitter on (blocked == false) or off
* (blocked == true) -- this is called only while the transmitter
* is not hard-blocked. This must be assigned.

The second case _shouldn't_ happen (but can, due to race conditions);
Then you just call set_hw_state before returning.

The third case, which you forgot, works the same as the second and needs
no extra code either.

> It would be nice to have the core export a way to "force" both sw and hw
> states at once, as well. This lets the core optimize the issuing of
> events and other internal changes and avoids bad mojo if the driver does
> sw/hw force calls in the wrong order (which might cause a unblock-block
> fast sequence, etc).
>
> This would let drivers that have the worst case software blocking (it
> exists, hw blocking also exists and is separate, and both sw and hw
> blocking state could change behind the driver's back) work well, without
> causing any extra complexity to the other drivers.

There may be some value in that, do you need polling for both though?
Otherwise you'd need
if (hw_blocked)
set_hw_state(hw_blocked)
set_sw_state(sw_blocked)
else
set_sw_state(sw_blocked)
set_hw_state(hw_blocked)

in the driver which I agree isn't nice and would be better with a
combined function.

> IF you don't want the hooks to return both sw and hw states, you can
> just change the return type from bool to int, and explicitly document
> tha the return status is:
>
> status < 0: error
> status = 0: false, implies no error
> status > 0: true, implies no error

Ick.


If you really do need polling for the sw state as well as the hw state,
I can be convinced to change poll_hw_block to

void (*poll_block)(void *data, bool *hw_blocked, bool *sw_blocked);

You need much better reasons, however, to convince me to add any error
return, I see absolutely no value in that. Neither rfkill's input
handler nor the polling nor userspace has any means to react to errors.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part
Subject: Re: [RFC] rfkill: rewrite

On Tue, 07 Apr 2009, Johannes Berg wrote:
> > As I said, ACPI platform drivers _do_ get errors when calling the ACPI
> > functions, or trying to do an EC transaction. That applies to reads
> > just as well as it applies to writes.
>
> And it also doesn't really matter. Yes, the current poll API isn't
> prepared to treat errors, but see below for a suggestion.

Ok. The older one allowed for it, so that's where the divergence lies.

> > I.e. I can get AE_BUSY or something else while querying the thinkpad
> > firmware, and then, what should I do to return the data on the
> > query_state (and, for that matter, poll_hw_block) hooks?
> >
> > Since there can be a clear error path from userspace to the rfkill
> > driver for reads and writes in many situations (e.g. on all sysfs
> > accesses to that driver's rfkill struct), this would be a good reason
> > to change the hooks to allow for that error path.
>
> Totally bogus argument -- 99.9% of users will be using rfkill's input
> handler which has no way of reacting to errors, and neither does

Well, network manager would be using sysfs, and that's something that can
report errors properly to the user, if it gets them from the kernel.
While I am happy with rfkill-input, I can easily imagine someone wanting
to have all rfkill-related input events to go to network manager to get
some unified GUI treatment there, and a proper error path would be nice to
have in that case.

> userspace. And it's wrong too -- the sysfs files don't actually call
> into rfkill, they just report the current status.

Indeed, I just looked at the code. I don't know why I had this wrong
recollection that I had added a proper error path there.

Well, I'd still prefer the API to have the error return, and if the core
doesn't have much use for it, it can consume it. Fixing the core later to
propagate errors back to userspace if needed would be much easier than
fixing all users of the API.

> > Without that, I'd have, for example, to return false (unblocked) on
> > any errors from thinkpad-acpi, for safety reasons (a blocked radio is
> > never a hazard, but an unblocked one can be). This is the kind of
> > stuff that a driver shouldn't need to decide by itself...
>
> Eh? Your first half sentence is correct (up to "on any errors"), the
> rest is garbage. Yes, you'd have to return false so that the core
> assumes it can do something, but no, it has nothing to do with hazards.

Hmm? Yes, it does. The choice to return false (transmiter is not
blocked) when you don't know jack about its state is rooted to the fact
that you cannot tell the user (or the system) that a transmitter is
'secured' (i.e. blocked) when it is not, while the opposite is not that
bad.

The reason is the same reason why one _cannot_ implement the rfkill class
in hardware that doesn't give guarantees that the transmission will be
blocked from emmiting energy.

And all of this happens because a transmitter emitting energy at the wrong
place and time can cause problems. Immediate examples are: undue
interference to sensitive equipment and sensor arrays, safety hazards
(unblock a laser while someone is cleaning the lens, unblock a microwave
radio when someone is in direct contact with the antenna), lightning, fire
and explosion hazard (emitting microwaves in highly explosive atmosphere
or in a highly 'charged' environment is not very safe), etc. Yes, it _is_
far-fetched, but that's what is behind the regulations that brought rfkill
to life.

I know (now) that the old core was consuming the error status instead of
passing it through -- I wasn't aware of it anymore or I'd have tried to
fix it (and it _is_ probable that I was the one that wrecked it to begin
with).

If the 'return it is unblocked in case of doubt' also happens to be a
better choice for other reasons, that's good.

Anyway, please document that in case of errors one is to return "false" in
the kerneldoc if you won't add the error status to the API.

> > On a related note, it was not clear to me from looking only at
> > rfkill.txt and the linux/rfkill.h headers, how am I supposed to deal
> > with the software controlled component of the rfkill controller (the
> > hardware rfkill line is quite obvious, though). I know there is a way,
> > but I haven't finished reading the core code to know which one, and the
> > docs didn't help, so they need an extra paragraph somewhere...
>
> There's a function for that -- but no poll yet.

I don't really need a poll function for thinkpad-acpi (I get events), but
see below.

> I can be convinced to change the prototype of poll_hw_block to
>
> void (*poll_hw_block)(void *data, bool *blocked);
>
> so that drivers can say
> "*shrug*, I dunno right now, keep whatever I told you last".
> Returning an error, however, is completely pointless.

See my arguments above about why I like the error status in the hook API.
I think we will just have to agree to disagree on that.

And I don't personally care much about the constant poll hook (poll_*),
just about the opportunistic poll hook (get_status), and the hook to set
radio state.

> Bugs happen, and can be fixed; your defensive programming mantra just
> makes for messy code that has multiple ways of working "correctly". I
> quite obviously disagree -- if you can't read the status don't touch the
> variables.

I am fine with that, I know *I* do read the kerneldoc entries for
everything I use, and often I look at the code too, since one cannot trust
kerneldoc entries too much.

Still, I just ask that you document these requirements explicitly in the
kerneldoc entry for the hooks, as well as the expected behaviour the hooks
should take on error conditions ("return false", "don't touch the state",
whatever).

> > BTW: it is probably a good idea to document what the driver should do if
> > the core tries this on a radio that is hardware blocked, or if the
> > driver discovers the radio is hardware blocked the instant it tries to
> > software unblock it...
>
> The first case doesn't happen... And it says so in the header file:
>
> * @set_block: turn the transmitter on (blocked == false) or off
> * (blocked == true) -- this is called only while the transmitter
> * is not hard-blocked. This must be assigned.
>
> The second case _shouldn't_ happen (but can, due to race conditions);
> Then you just call set_hw_state before returning.

That is the sort of stuff that belongs in a doc somewhere for sure,
although one could infer it since the set_hw_state function makes it clear
that it can be called from anywhere, anytime... but it is not obvious.

> The third case, which you forgot, works the same as the second and needs
> no extra code either.

Ok.

> > It would be nice to have the core export a way to "force" both sw and hw
> > states at once, as well. This lets the core optimize the issuing of
> > events and other internal changes and avoids bad mojo if the driver does
> > sw/hw force calls in the wrong order (which might cause a unblock-block
> > fast sequence, etc).
> >
> > This would let drivers that have the worst case software blocking (it
> > exists, hw blocking also exists and is separate, and both sw and hw
> > blocking state could change behind the driver's back) work well, without
> > causing any extra complexity to the other drivers.
>
> There may be some value in that, do you need polling for both though?
> Otherwise you'd need
> if (hw_blocked)
> set_hw_state(hw_blocked)
> set_sw_state(sw_blocked)
> else
> set_sw_state(sw_blocked)
> set_hw_state(hw_blocked)

Well, *I* have full events, so I can disable polling altoghether, and do
the above.

However, I liked the old rfkill core 'opportunistic polling' through the
get_state hook, which the new core could also do, because it GREATLY
reduced the time window where a state could be wrong because of some sort
of misdelivery of events (you can NEVER trust ACPI-based crap too much).

And to use the opportunistic pooling, I'd need to be able to return both
states, so I'd need the query_state hook to let me return both states at
once.

So, I'd really appreciate a "void get_state(void *data, bool *sw, bool
*hw)" hook (since you won't give me error status).

Or maybe even change the get_state and poll_hw hooks to a "void
(hook)(void *data)" thing, where the driver is expected to use
set_hw_state and friends to do the update instead of returning values.

As for the small code snippet above, I ask that you add a
set_hw_sw_state() function. It would make my life as a driver writer
easier, it would be simpler to understand, and it would give the rfkill
core better optimization oportunities to avoid transient spurious
blocks/unblocks.

> If you really do need polling for the sw state as well as the hw state,
> I can be convinced to change poll_hw_block to
>
> void (*poll_block)(void *data, bool *hw_blocked, bool *sw_blocked);

Do that to get_state() as well, and give me set_hw_sw_state(bool sw, bool
hw), and we have a deal.

Or change the poll and get_state hooks to void (*hook)(void *data), and
give us set_hw_state, set_sw_state and set_hw_sw_state functions to call.
I find this to be easier to use, but either choice would make me happy.

> You need much better reasons, however, to convince me to add any error
> return, I see absolutely no value in that. Neither rfkill's input
> handler nor the polling nor userspace has any means to react to errors.

Userspace can react to errors, by notifying the user. rfkill-input can't
do anything right now, but we COULD (if the userspace people wanted) issue
uevents to get notifications sent to the user. My point is that we can
certainly propagate the errors to someone who could do something about it
if we wanted, as long as we have the errors to propagate.

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

2009-04-30 15:07:27

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC v6] rfkill: rewrite

On Thu, 2009-04-30 at 00:19 -0300, Henrique de Moraes Holschuh wrote:

> The return values for rfkill_alloc() are not the same on the stub and
> real functions. The stub function uses ERR_PTR, while the real
> function uses NULL for any errors. Please make them do the same
> thing, and it is probably a damn good idea to document in the
> kerneldoc what kind of return values one should expect when you return
> a pointer and the function can fail (i.e. NULL or ERR_PTR).

Actually, I was going to clean this up, and then I noticed you're
wrong. :) Returning ERR_PTR(-ENODEV) is intentional, in the non-RFKILL
case. Imagine a driver like this:

ASDF
rf = rfkill_alloc(...)
if (!rf)
return -ENOMEM;
err = rfkill_register(rf);
if (err) {
rfkill_destroy(rf);
return err;
}
GHJK

Now, in the non-RFKILL case, this boils down to:
rf = ERR_PTR(-ENODEV);
...

and given compiler optimisations will end up like this:
ASDF
GHJK

Which is good :)

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part
Subject: Re: [RFC v6] rfkill: rewrite

I am halfway on the thinkpad-acpi conversion. It is going slow because
I have to cleanup the entire thing to have the code look even remotely
sane now that HW and SW states are to be tracked separately by the
backend driver, and I lost the RFKILL_STATE_* stuff.

Anyway, in the process (using v7 of the patch, taken from the URL you
posted sometime ago), I noticed this:

The return values for rfkill_alloc() are not the same on the stub and
real functions. The stub function uses ERR_PTR, while the real
function uses NULL for any errors. Please make them do the same
thing, and it is probably a damn good idea to document in the
kerneldoc what kind of return values one should expect when you return
a pointer and the function can fail (i.e. NULL or ERR_PTR).

Also, I have a question: are hooks ever called in atomic/interrupt
context? Just about every ACPI and I2C driver needs to sleep to do
their magic, so I sure hope the answer is "no", otherwise life
suddenly got a whole lot more complicated for thinkpad-acpi..

Also, due to races, it is _impossible_ to assure the backend driver
that the set_block hook will never be called when the radio is
hard-blocked. Best to change the description of that hook to make it
clear that it could happen, but that the rfkill core will try to
optimize away such changes. Here's the race:

A B
rfkill_set_block
saves state in "prev"
... rfkill_set_hw_state(true)
call set_block hook
but radio IS hard-blocked
and the core WAS informed
of this

Also, it looks like the SW state rollback done by rfkill_set_block can
sw-unblock a radio when it shouldn't on races very similar as the one
above, but involving sw-blocking.

I don't see a way to do safe state rollback in a lockless rfkill core
if set_sw_state can happen at any time. Please enlighten me if I am
wrong (and I hope I am) :-(

If I am not wrong, one could change rfkill_set_sw_state and
rfkill_set_states to return an error if rfkill_set_block is on the way
(that can be done lockless). For rfkill_set_states, document that the
hw state WILL be updated regardless, but that the sw state might not
be.

Or one could kick the problem to the backend driver (which will have
any required locking anyway), by giving the set_block hook the duty of
calling set_sw_state when it succeeds. The return status might be
kept should we ever start passing it down to other layers like
userspace, or you can remove it.

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

2009-04-30 14:18:33

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC v6] rfkill: rewrite

On Thu, 2009-04-30 at 11:11 -0300, Henrique de Moraes Holschuh wrote:

> > > Also, due to races, it is _impossible_ to assure the backend driver
> > > that the set_block hook will never be called when the radio is
> > > hard-blocked. Best to change the description of that hook to make it
> > > clear that it could happen, but that the rfkill core will try to
> > > optimize away such changes. Here's the race:
> > >
> > > A B
> > > rfkill_set_block
> > > saves state in "prev"
> > > ... rfkill_set_hw_state(true)
> > > call set_block hook
> > > but radio IS hard-blocked
> > > and the core WAS informed
> > > of this
> >
> > Good point. I wonder what's more important -- it would be almost trivial
> > to change this by using a spinlock instead of atomic bit operations.
>
> Yeah, as long as it is a spinlock variant that can be used safely in any
> context, it should work fine, as contention would be quite rare (unless
> there is a pathological case in one driver that makes it always call some
> set_foo just when the core is going to call it back...)

Heh, that would be strange.

> > * rfkill_set_block can set a flag "I'm in an update"
> > * a concurrent rfkill_set_sw_state will check that flag, and update the
> > "prev" variable instead of the real variable
> > * on errors, rfkill_set_block will then revert to whatever the last
> > set_sw_state said
>
> That could work. It is still required to make it clear that the set_block
> hook can be called with a hardware block active, even if it normally won't
> be, so that backend drivers are aware of the possibility.

Right, it could still happen, if the driver has just called set_hw_state
when something decided to change the sw state. Should be relatively rare
though. The other race can also happen, but the driver needs to take
care of the return value of set_hw_state() so it doesn't matter.

I'll see if I can implement this.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-04-14 21:03:53

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] rfkill: rewrite

Hi,

Alright, finally got some spare cycles.

> > > I.e. I can get AE_BUSY or something else while querying the thinkpad
> > > firmware, and then, what should I do to return the data on the
> > > query_state (and, for that matter, poll_hw_block) hooks?
> > >
> > > Since there can be a clear error path from userspace to the rfkill
> > > driver for reads and writes in many situations (e.g. on all sysfs
> > > accesses to that driver's rfkill struct), this would be a good reason
> > > to change the hooks to allow for that error path.
> >
> > Totally bogus argument -- 99.9% of users will be using rfkill's input
> > handler which has no way of reacting to errors, and neither does
>
> Well, network manager would be using sysfs, and that's something that can
> report errors properly to the user, if it gets them from the kernel.

Actually, no. I removed actually polling the rfkill driver on sysfs
accesses -- and that was intentional. If we keep that we get into
strange situations like a sysfs read can causing a uevent, or having
some userspace app poll the sysfs file because the kernel polling isn't
working properly or ...

> > userspace. And it's wrong too -- the sysfs files don't actually call
> > into rfkill, they just report the current status.
>
> Indeed, I just looked at the code. I don't know why I had this wrong
> recollection that I had added a proper error path there.

You might have had that and I removed it? Anyway, I think it's bad, see
above.

> [snip]
> Yes, it _is_
> far-fetched, but that's what is behind the regulations that brought rfkill
> to life.

Alright, I see where you're coming from -- I still think we shouldn't
treat rfkill as a "nuclear power plant" control but rather as a "don't
want wireless" control -- your opinion clearly differs.

> Anyway, please document that in case of errors one is to return "false" in
> the kerneldoc if you won't add the error status to the API.

I'm now convinced we should just treat errors as transient and allow
poll to say "no state change". If there is a possibility that there are
errors that don't go away (are not transient) then the driver should
probably just unregister the rfkill.

> > I can be convinced to change the prototype of poll_hw_block to
> >
> > void (*poll_hw_block)(void *data, bool *blocked);
> >
> > so that drivers can say
> > "*shrug*, I dunno right now, keep whatever I told you last".
> > Returning an error, however, is completely pointless.
>
> See my arguments above about why I like the error status in the hook API.
> I think we will just have to agree to disagree on that.
>
> And I don't personally care much about the constant poll hook (poll_*),
> just about the opportunistic poll hook (get_status), and the hook to set
> radio state.

Mind you, the get_status() hook changed semantics and was renamed to
query_hw_block(); you used to call it in sysfs and some other places I
think, I now only call it for the weird case where an input button can
cause an rfkill status update... Only Dell does that weird thing afaict.

> Still, I just ask that you document these requirements explicitly in the
> kerneldoc entry for the hooks, as well as the expected behaviour the hooks
> should take on error conditions ("return false", "don't touch the state",
> whatever).

Will do.

> > > BTW: it is probably a good idea to document what the driver should do if
> > > the core tries this on a radio that is hardware blocked, or if the
> > > driver discovers the radio is hardware blocked the instant it tries to
> > > software unblock it...
> >
> > The first case doesn't happen... And it says so in the header file:
> >
> > * @set_block: turn the transmitter on (blocked == false) or off
> > * (blocked == true) -- this is called only while the transmitter
> > * is not hard-blocked. This must be assigned.
> >
> > The second case _shouldn't_ happen (but can, due to race conditions);
> > Then you just call set_hw_state before returning.
>
> That is the sort of stuff that belongs in a doc somewhere for sure,
> although one could infer it since the set_hw_state function makes it clear
> that it can be called from anywhere, anytime... but it is not obvious.

I'll add a note.

> > > It would be nice to have the core export a way to "force" both sw and hw
> > > states at once, as well. This lets the core optimize the issuing of
> > > events and other internal changes and avoids bad mojo if the driver does
> > > sw/hw force calls in the wrong order (which might cause a unblock-block
> > > fast sequence, etc).
> > >
> > > This would let drivers that have the worst case software blocking (it
> > > exists, hw blocking also exists and is separate, and both sw and hw
> > > blocking state could change behind the driver's back) work well, without
> > > causing any extra complexity to the other drivers.
> >
> > There may be some value in that, do you need polling for both though?
> > Otherwise you'd need
> > if (hw_blocked)
> > set_hw_state(hw_blocked)
> > set_sw_state(sw_blocked)
> > else
> > set_sw_state(sw_blocked)
> > set_hw_state(hw_blocked)
>
> Well, *I* have full events, so I can disable polling altoghether, and do
> the above.

I'll allow you to merge it into set_state(hw, sw) though, I think.

> However, I liked the old rfkill core 'opportunistic polling' through the
> get_state hook, which the new core could also do, because it GREATLY
> reduced the time window where a state could be wrong because of some sort
> of misdelivery of events (you can NEVER trust ACPI-based crap too much).
>
> And to use the opportunistic pooling, I'd need to be able to return both
> states, so I'd need the query_state hook to let me return both states at
> once.
>
> So, I'd really appreciate a "void get_state(void *data, bool *sw, bool
> *hw)" hook (since you won't give me error status).
>
> Or maybe even change the get_state and poll_hw hooks to a "void
> (hook)(void *data)" thing, where the driver is expected to use
> set_hw_state and friends to do the update instead of returning values.

Indeed, that would work as well and would simplify the API.

Can you explain how such a query method can reduce the time window of a
wrong state though? I honestly don't understand that.

> As for the small code snippet above, I ask that you add a
> set_hw_sw_state() function. It would make my life as a driver writer
> easier, it would be simpler to understand, and it would give the rfkill
> core better optimization oportunities to avoid transient spurious
> blocks/unblocks.

Yeah, will do.

> > If you really do need polling for the sw state as well as the hw state,
> > I can be convinced to change poll_hw_block to
> >
> > void (*poll_block)(void *data, bool *hw_blocked, bool *sw_blocked);
>
> Do that to get_state() as well, and give me set_hw_sw_state(bool sw, bool
> hw), and we have a deal.
>
> Or change the poll and get_state hooks to void (*hook)(void *data), and
> give us set_hw_state, set_sw_state and set_hw_sw_state functions to call.
> I find this to be easier to use, but either choice would make me happy.

I agree, removing the arguments seems simpler.

> > You need much better reasons, however, to convince me to add any error
> > return, I see absolutely no value in that. Neither rfkill's input
> > handler nor the polling nor userspace has any means to react to errors.
>
> Userspace can react to errors, by notifying the user. rfkill-input can't
> do anything right now, but we COULD (if the userspace people wanted) issue
> uevents to get notifications sent to the user. My point is that we can
> certainly propagate the errors to someone who could do something about it
> if we wanted, as long as we have the errors to propagate.

Sure, and then when the user was notified how can the user react? Either
the error is transient, in which case the user can only try again (but
why don't we program it to do that then?), or the error is permanent in
which case the user cannot do anything either. So what gives?

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2009-04-30 15:14:46

by Johannes Berg

[permalink] [raw]
Subject: [RFC v8] rfkill: rewrite

Ok, here's a new version.

I wonder if we should remove the optimisation of "don't call set_block
if hw-blocked since due to the race drivers must be able to handle that
anyway, if only by making it a no-op... Then we could get rid of
rfkill_has_sw_block_memory again which always seemed like a hack. I
think I'll do that for v9 after I review the drivers.

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

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

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

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

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

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

* rfkill-input pointlessly is a separate module

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

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

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

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

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

Signed-off-by: Alan Jenkins <[email protected]> [eeepc bits]
Signed-off-by: Johannes Berg <[email protected]>
---
v3: * fix locking bugs
* fix bug in input.c that made it ignore things
* allow setting LED trigger name
* apply default state while registering
* convert HSO
v4: * LED triggering
v5: * convert sony
* introduce rfkill_set_states function
* change query/poll methods to require calls to
rfkill_set{_sw,_hw}_state/rfkill_set_states
* do not call driver from registration
* remove tested-by since it changed a lot
v6: * fix stupid sync work bugs
v7: * fix setting states before registration
* make EEEPC resume properly [Alan Jenkins]
v8: * move one rfkill_set_state call under the mutex
* protect things with spinlock and take care to
close the race Henrique pointed out
* add note about set_block race
* remove tested-by again, the spinlock change is
a little too invasive to claim testing

TODO
* convert toshiba, thinkpad

Documentation/rfkill.txt | 639 +++------------------
MAINTAINERS | 6
arch/arm/mach-pxa/tosa-bt.c | 30 -
arch/arm/mach-pxa/tosa.c | 1
drivers/net/usb/hso.c | 42 -
drivers/net/wireless/ath/ath9k/ath9k.h | 7
drivers/net/wireless/ath/ath9k/main.c | 115 ---
drivers/net/wireless/ath/ath9k/pci.c | 15
drivers/net/wireless/b43/Kconfig | 2
drivers/net/wireless/b43/leds.c | 2
drivers/net/wireless/b43/main.c | 4
drivers/net/wireless/b43/phy_a.c | 4
drivers/net/wireless/b43/phy_common.c | 17
drivers/net/wireless/b43/phy_common.h | 4
drivers/net/wireless/b43/phy_g.c | 4
drivers/net/wireless/b43/phy_lp.c | 2
drivers/net/wireless/b43/phy_n.c | 2
drivers/net/wireless/b43/rfkill.c | 123 +---
drivers/net/wireless/b43/rfkill.h | 5
drivers/net/wireless/b43legacy/Kconfig | 2
drivers/net/wireless/b43legacy/leds.c | 3
drivers/net/wireless/b43legacy/rfkill.c | 123 +---
drivers/net/wireless/b43legacy/rfkill.h | 6
drivers/net/wireless/iwlwifi/Kconfig | 5
drivers/net/wireless/iwlwifi/iwl-rfkill.c | 69 --
drivers/platform/x86/Kconfig | 6
drivers/platform/x86/acer-wmi.c | 50 -
drivers/platform/x86/dell-laptop.c | 101 +--
drivers/platform/x86/eeepc-laptop.c | 122 +---
drivers/platform/x86/hp-wmi.c | 103 +--
drivers/platform/x86/sony-laptop.c | 115 +--
drivers/platform/x86/toshiba_acpi.c | 23
include/linux/Kbuild | 1
include/linux/rfkill.h | 348 +++++++++--
include/net/wimax.h | 8
net/rfkill/Kconfig | 14
net/rfkill/Makefile | 4
net/rfkill/core.c | 895 ++++++++++++++++++++++++++++++
net/rfkill/input.c | 342 +++++++++++
net/rfkill/rfkill-input.c | 390 -------------
net/rfkill/rfkill-input.h | 21
net/rfkill/rfkill.c | 855 ----------------------------
net/rfkill/rfkill.h | 27
net/wimax/Kconfig | 14
net/wimax/op-rfkill.c | 125 ----
45 files changed, 2088 insertions(+), 2708 deletions(-)

--- wireless-testing.orig/include/linux/rfkill.h 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/include/linux/rfkill.h 2009-04-30 17:12:14.000000000 +0200
@@ -4,6 +4,7 @@
/*
* Copyright (C) 2006 - 2007 Ivo van Doorn
* Copyright (C) 2007 Dmitry Torokhov
+ * Copyright 2009 Johannes Berg <[email protected]>
*
* 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,11 +49,13 @@

/**
* 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 ,
@@ -42,97 +63,278 @@ enum rfkill_type {
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 hardware block state (return true
+ * for blocked) -- 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.
- *
- * This structure represents a RF switch located on a network device.
- */
-struct rfkill {
- const char *name;
- enum rfkill_type type;
-
- /* the mutex serializes callbacks and also protects
- * the state */
- struct mutex mutex;
- enum rfkill_state state;
- void *data;
- int (*toggle_radio)(void *data, enum rfkill_state state);
- int (*get_state)(void *data, enum rfkill_state *state);
+ * rfkill_alloc - allocate rfkill structure
+ * @name: name of the struct -- the string is not copied internally
+ * @parent: device that has rf switch on it
+ * @type: type of the switch (RFKILL_TYPE_*)
+ * @ops: rfkill methods
+ * @ops_data: data passed to each method
+ *
+ * This function should be called by the transmitter driver to allocate an
+ * rfkill structure. Returns %NULL on failure.
+ */
+struct rfkill * __must_check rfkill_alloc(const char *name,
+ struct device *parent,
+ const enum rfkill_type type,
+ const struct rfkill_ops *ops,
+ void *ops_data);

-#ifdef CONFIG_RFKILL_LEDS
- struct led_trigger led_trigger;
-#endif
+/**
+ * rfkill_has_sw_block_memory -- turn on calling set_block unconditionally
+ * @rfkill: rfkill struct
+ *
+ * This function tells the rfkill core that the device is capable of
+ * remembering soft blocks (which it is notified of via the set_block
+ * method) -- this means that the driver may ignore the return value
+ * from rfkill_set_hw_state().
+ *
+ * This function must be called before rfkill_register().
+ */
+void rfkill_has_sw_block_memory(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_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);

-struct rfkill * __must_check rfkill_allocate(struct device *parent,
- enum rfkill_type type);
-void rfkill_free(struct rfkill *rfkill);
+/**
+ * rfkill_register - Register a rfkill structure.
+ * @rfkill: rfkill structure to be registered
+ *
+ * This function should be called by the transmitter driver to register
+ * the rfkill structure needs to be registered. Before calling this function
+ * the driver needs to be ready to service method calls from rfkill.
+ */
int __must_check rfkill_register(struct rfkill *rfkill);
+
+/**
+ * rfkill_pause_polling(struct rfkill *rfkill)
+ *
+ * Pause polling -- say transmitter is off for other reasons.
+ * NOTE: not necessary for suspend/resume -- in that case the
+ * core stops polling anyway
+ */
+void rfkill_pause_polling(struct rfkill *rfkill);
+
+/**
+ * rfkill_resume_polling(struct rfkill *rfkill)
+ *
+ * Pause polling -- say transmitter is off for other reasons.
+ * NOTE: not necessary for suspend/resume -- in that case the
+ * core stops polling anyway
+ */
+void rfkill_resume_polling(struct rfkill *rfkill);
+
+
+/**
+ * rfkill_unregister - Unregister a rfkill structure.
+ * @rfkill: rfkill structure to be unregistered
+ *
+ * This function should be called by the network driver during device
+ * teardown to destroy rfkill structure. Until it returns, the driver
+ * needs to be able to service method calls.
+ */
void rfkill_unregister(struct rfkill *rfkill);

-int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state);
-int rfkill_set_default(enum rfkill_type type, enum rfkill_state state);
+/**
+ * rfkill_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.
+ *
+ * If the device is able to, call rfkill_has_sw_block_memory() and
+ * ignore the return value of this function.
+ */
+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
*
- * Returns RFKILL_STATE_SOFT_BLOCKED if @state is RFKILL_STATE_UNBLOCKED,
- * returns RFKILL_STATE_UNBLOCKED otherwise.
+ * This function sets the global default -- use at boot if your platform has
+ * an rfkill switch. If not early enough this call may be ignored.
+ *
+ * XXX: instead of ignoring -- how about just updating all currently
+ * registered drivers?
*/
-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 void rfkill_has_sw_block_memory(struct rfkill *rfkill)
+{
+}
+
+static inline int __must_check rfkill_register(struct rfkill *rfkill)
+{
+ if (rfkill == ERR_PTR(-ENODEV))
+ return 0;
+ return -EINVAL;
+}
+
+static inline void rfkill_pause_polling(struct rfkill *rfkill)
+{
+}
+
+static inline void rfkill_resume_polling(struct rfkill *rfkill)
+{
+}
+
+static inline void rfkill_unregister(struct rfkill *rfkill)
+{
+}
+
+static inline void rfkill_destroy(struct rfkill *rfkill)
+{
+}
+
+static inline bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked)
+{
+ return blocked;
+}
+
+static inline bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
{
- return (state == RFKILL_STATE_UNBLOCKED) ?
- RFKILL_STATE_SOFT_BLOCKED : RFKILL_STATE_UNBLOCKED;
+ return blocked;
}

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

+void rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name)
+{
+}
+#endif
+
+#endif /* __KERNEL__ */
+
#endif /* RFKILL_H */
--- wireless-testing.orig/drivers/net/wireless/ath/ath9k/main.c 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath/ath9k/main.c 2009-04-29 10:49:38.000000000 +0200
@@ -1171,120 +1171,69 @@ static bool ath_is_rfkill_set(struct ath
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);
@@ -1654,10 +1603,6 @@ int ath_attach(u16 devid, struct ath_sof
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)
@@ -2150,10 +2095,8 @@ static void ath9k_stop(struct ieee80211_
} 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);
--- wireless-testing.orig/drivers/platform/x86/eeepc-laptop.c 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/platform/x86/eeepc-laptop.c 2009-04-29 10:49:38.000000000 +0200
@@ -177,6 +177,7 @@ static struct key_entry eeepc_keymap[] =
*/
static int eeepc_hotk_add(struct acpi_device *device);
static int eeepc_hotk_remove(struct acpi_device *device, int type);
+static int eeepc_hotk_resume(struct acpi_device *device);

static const struct acpi_device_id eeepc_device_ids[] = {
{EEEPC_HOTK_HID, 0},
@@ -191,6 +192,7 @@ static struct acpi_driver eeepc_hotk_dri
.ops = {
.add = eeepc_hotk_add,
.remove = eeepc_hotk_remove,
+ .resume = eeepc_hotk_resume,
},
};

@@ -296,39 +298,15 @@ static int update_bl_status(struct backl
* Rfkill helpers
*/

-static int eeepc_wlan_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_WLAN, 0);
- else
- return set_acpi(CM_ASL_WLAN, 1);
-}
-
-static int eeepc_wlan_rfkill_state(void *data, enum rfkill_state *state)
-{
- if (get_acpi(CM_ASL_WLAN) == 1)
- *state = RFKILL_STATE_UNBLOCKED;
- else
- *state = RFKILL_STATE_SOFT_BLOCKED;
- return 0;
+ unsigned long asl = (unsigned long)data;
+ return set_acpi(asl, !blocked);
}

-static int eeepc_bluetooth_rfkill_set(void *data, enum rfkill_state state)
-{
- if (state == RFKILL_STATE_SOFT_BLOCKED)
- return set_acpi(CM_ASL_BLUETOOTH, 0);
- else
- return set_acpi(CM_ASL_BLUETOOTH, 1);
-}
-
-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
@@ -519,14 +497,11 @@ static void notify_brn(void)
bd->props.brightness = read_brightness(bd);
}

-static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
+static void eeepc_rfkill_hotplug(void)
{
struct pci_dev *dev;
struct pci_bus *bus = pci_find_bus(0, 1);

- if (event != ACPI_NOTIFY_BUS_CHECK)
- return;
-
if (!bus) {
printk(EEEPC_WARNING "Unable to find PCI bus 1?\n");
return;
@@ -554,6 +529,14 @@ static void eeepc_rfkill_notify(acpi_han
}
}

+static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
+{
+ if (event != ACPI_NOTIFY_BUS_CHECK)
+ return;
+
+ eeepc_rfkill_hotplug();
+}
+
static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
{
static struct key_entry *key;
@@ -650,26 +633,17 @@ static int eeepc_hotk_add(struct acpi_de
printk(EEEPC_ERR "Error installing notify handler\n");

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;
@@ -677,28 +651,17 @@ static int eeepc_hotk_add(struct acpi_de

if (get_acpi(CM_ASL_BLUETOOTH) != -1) {
ehotk->eeepc_bluetooth_rfkill =
- rfkill_allocate(&device->dev, RFKILL_TYPE_BLUETOOTH);
+ rfkill_alloc("eeepc-wlan",
+ &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;
@@ -710,13 +673,10 @@ static int eeepc_hotk_add(struct acpi_de
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);
ehotk_fail:
kfree(ehotk);
ehotk = NULL;
@@ -742,6 +702,22 @@ static int eeepc_hotk_remove(struct acpi
return 0;
}

+static int eeepc_hotk_resume(struct acpi_device *device)
+{
+ if (ehotk->eeepc_wlan_rfkill) {
+ rfkill_set_sw_state(ehotk->eeepc_wlan_rfkill,
+ get_acpi(CM_ASL_WLAN) != 1);
+
+ eeepc_rfkill_hotplug();
+ }
+
+ if (ehotk->eeepc_bluetooth_rfkill)
+ rfkill_set_sw_state(ehotk->eeepc_bluetooth_rfkill,
+ get_acpi(CM_ASL_BLUETOOTH) != 1);
+
+ return 0;
+}
+
/*
* Hwmon
*/
--- wireless-testing.orig/net/rfkill/rfkill.c 2009-04-29 10:49:35.000000000 +0200
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -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 <[email protected]>");
-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);
--- wireless-testing.orig/net/rfkill/rfkill-input.h 2009-04-29 10:49:35.000000000 +0200
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2007 Ivo van Doorn
- */
-
-/*
- * 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.
- */
-
-#ifndef __RFKILL_INPUT_H
-#define __RFKILL_INPUT_H
-
-void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state);
-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);
-
-#endif /* __RFKILL_INPUT_H */
--- wireless-testing.orig/net/rfkill/rfkill-input.c 2009-04-29 10:49:35.000000000 +0200
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -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 <[email protected]>");
-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);
--- wireless-testing.orig/drivers/net/wireless/b43/main.c 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/main.c 2009-04-29 10:49:38.000000000 +0200
@@ -3470,7 +3470,7 @@ static int b43_op_config(struct ieee8021

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 ieee8021
"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");
}
}
--- wireless-testing.orig/drivers/net/wireless/b43/phy_a.c 2009-04-29 10:49:35.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_a.c 2009-04-29 10:49:38.000000000 +0200
@@ -480,11 +480,11 @@ static bool b43_aphy_op_supports_hwpctl(
}

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);
--- wireless-testing.orig/drivers/net/wireless/b43/phy_common.c 2009-04-29 10:49:35.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_common.c 2009-04-29 10:49:38.000000000 +0200
@@ -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);
}

--- wireless-testing.orig/drivers/net/wireless/b43/phy_common.h 2009-04-29 10:49:35.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_common.h 2009-04-29 10:49:38.000000000 +0200
@@ -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
/**
* 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.
--- wireless-testing.orig/drivers/net/wireless/b43/phy_g.c 2009-04-29 10:49:35.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_g.c 2009-04-29 10:49:38.000000000 +0200
@@ -2592,7 +2592,7 @@ static bool b43_gphy_op_supports_hwpctl(
}

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(

might_sleep();

- if (state == RFKILL_STATE_UNBLOCKED) {
+ if (!blocked) {
/* Turn radio ON */
if (phy->radio_on)
return;
--- wireless-testing.orig/drivers/net/wireless/b43/phy_n.c 2009-04-29 10:49:35.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_n.c 2009-04-29 10:49:38.000000000 +0200
@@ -579,7 +579,7 @@ static void b43_nphy_op_radio_write(stru
}

static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
- enum rfkill_state state)
+ bool blocked)
{//TODO
}

--- wireless-testing.orig/drivers/net/wireless/b43/rfkill.c 2009-04-29 10:49:35.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/rfkill.c 2009-04-29 10:49:38.000000000 +0200
@@ -45,12 +45,11 @@ static bool b43_is_hw_radio_enabled(stru
}

/* 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
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 (WARN_ON(!dev->radio_hw_enable && !blocked))
+ 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 *d

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 *d
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;
}
--- wireless-testing.orig/drivers/net/wireless/b43legacy/rfkill.c 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/rfkill.c 2009-04-29 10:49:38.000000000 +0200
@@ -45,12 +45,11 @@ static bool b43legacy_is_hw_radio_enable
}

/* 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
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 (WARN_ON(!blocked && !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 b43leg

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 b43leg
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;
}

--- wireless-testing.orig/drivers/net/wireless/iwlwifi/iwl-rfkill.c 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/iwlwifi/iwl-rfkill.c 2009-04-29 10:49:38.000000000 +0200
@@ -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 (WARN_ON(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 *pri
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 *pri
}

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_
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);
--- wireless-testing.orig/Documentation/rfkill.txt 2009-04-29 10:49:35.000000000 +0200
+++ wireless-testing/Documentation/rfkill.txt 2009-04-29 10:49:38.000000000 +0200
@@ -1,571 +1,132 @@
-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:
-
-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.
-
-Note that disabling the signal output means that the the transmitter is to be
-made to not emit any energy when "blocked". rfkill is not about blocking data
-transmissions, it is about blocking energy emission.
-
-The rfkill subsystem offers support for keys and switches often found on
-laptops to enable wireless devices like WiFi and Bluetooth, so that these keys
-and switches actually perform an action in all wireless devices of a given type
-attached to the system.
-
-The buttons to enable and disable the wireless transmitters are important in
-situations where the user is for example using his laptop on a location where
-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.
+1. Introduction
+2. Implementation details
+3. Kernel driver guidelines
+4. Kernel API
+5. Userspace support

-===============================================================================
-2: Implementation details
+
+1. Introduction
+
+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.
+
+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.
+
+
+
+2. Implementation details

The rfkill subsystem is composed of various components: the rfkill class, the
rfkill-input module (an input layer handler), and some specific input layer
events.

-The rfkill class provides kernel drivers with an interface that allows them to
-know when they should enable or disable a wireless network device transmitter.
-This is enabled by the CONFIG_RFKILL Kconfig option.
-
-The rfkill class support makes sure userspace will be notified of all state
-changes on rfkill devices through uevents. It provides a notification chain
-for interested parties in the kernel to also get notified of rfkill state
-changes in other drivers. It creates several sysfs entries which can be used
-by userspace. See section "Userspace support".
-
-The rfkill-input module provides the kernel with the ability to implement a
-basic response when the user presses a key or button (or toggles a switch)
-related to rfkill functionality. It is an in-kernel implementation of default
-policy of reacting to rfkill-related input events and neither mandatory nor
-required for wireless drivers to operate. It is enabled by the
-CONFIG_RFKILL_INPUT Kconfig option.
-
-rfkill-input is a rfkill-related events input layer handler. This handler will
-listen to all rfkill key events and will change the rfkill state of the
-wireless devices accordingly. With this option enabled userspace could either
-do nothing or simply perform monitoring tasks.
-
-The rfkill-input module also provides EPO (emergency power-off) functionality
-for all wireless transmitters. This function cannot be overridden, and it is
-always active. rfkill EPO is related to *_RFKILL_ALL input layer events.
-
-
-Important terms for the rfkill subsystem:
-
-In order to avoid confusion, we avoid the term "switch" in rfkill when it is
-referring to an electronic control circuit that enables or disables a
-transmitter. We reserve it for the physical device a human manipulates
-(which is an input device, by the way):
-
-rfkill switch:
-
- A physical device a human manipulates. Its state can be perceived by
- the kernel either directly (through a GPIO pin, ACPI GPE) or by its
- effect on a rfkill line of a wireless device.
-
-rfkill controller:
-
- A hardware circuit that controls the state of a rfkill line, which a
- kernel driver can interact with *to modify* that state (i.e. it has
- either write-only or read/write access).
-
-rfkill line:
-
- An input channel (hardware or software) of a wireless device, which
- causes a wireless transmitter to stop emitting energy (BLOCK) when it
- is active. Point of view is extremely important here: rfkill lines are
- always seen from the PoV of a wireless device (and its driver).
-
-soft rfkill line/software rfkill line:
-
- A rfkill line the wireless device driver can directly change the state
- of. Related to rfkill_state RFKILL_STATE_SOFT_BLOCKED.
-
-hard rfkill line/hardware rfkill line:
-
- A rfkill line that works fully in hardware or firmware, and that cannot
- be overridden by the kernel driver. The hardware device or the
- firmware just exports its status to the driver, but it is read-only.
- Related to rfkill_state RFKILL_STATE_HARD_BLOCKED.
-
-The enum rfkill_state describes the rfkill state of a transmitter:
-
-When a rfkill line or rfkill controller is in the RFKILL_STATE_UNBLOCKED state,
-the wireless transmitter (radio TX circuit for example) is *enabled*. When the
-it is in the RFKILL_STATE_SOFT_BLOCKED or RFKILL_STATE_HARD_BLOCKED, the
-wireless transmitter is to be *blocked* from operating.
-
-RFKILL_STATE_SOFT_BLOCKED indicates that a call to toggle_radio() can change
-that state. RFKILL_STATE_HARD_BLOCKED indicates that a call to toggle_radio()
-will not be able to change the state and will return with a suitable error if
-attempts are made to set the state to RFKILL_STATE_UNBLOCKED.
-
-RFKILL_STATE_HARD_BLOCKED is used by drivers to signal that the device is
-locked in the BLOCKED state by a hardwire rfkill line (typically an input pin
-that, when active, forces the transmitter to be disabled) which the driver
-CANNOT override.
-
-Full rfkill functionality requires two different subsystems to cooperate: the
-input layer and the rfkill class. The input layer issues *commands* to the
-entire system requesting that devices registered to the rfkill class change
-state. The way this interaction happens is not complex, but it is not obvious
-either:
-
-Kernel Input layer:
-
- * Generates KEY_WWAN, KEY_WLAN, KEY_BLUETOOTH, SW_RFKILL_ALL, and
- other such events when the user presses certain keys, buttons, or
- toggles certain physical switches.
-
- THE INPUT LAYER IS NEVER USED TO PROPAGATE STATUS, NOTIFICATIONS OR THE
- KIND OF STUFF AN ON-SCREEN-DISPLAY APPLICATION WOULD REPORT. It is
- used to issue *commands* for the system to change behaviour, and these
- commands may or may not be carried out by some kernel driver or
- userspace application. It follows that doing user feedback based only
- on input events is broken, as there is no guarantee that an input event
- will be acted upon.
-
- Most wireless communication device drivers implementing rfkill
- functionality MUST NOT generate these events, and have no reason to
- register themselves with the input layer. Doing otherwise is a common
- misconception. There is an API to propagate rfkill status change
- information, and it is NOT the input layer.
-
-rfkill class:
-
- * Calls a hook in a driver to effectively change the wireless
- transmitter state;
- * Keeps track of the wireless transmitter state (with help from
- the driver);
- * Generates userspace notifications (uevents) and a call to a
- notification chain (kernel) when there is a wireless transmitter
- state change;
- * Connects a wireless communications driver with the common rfkill
- control system, which, for example, allows actions such as
- "switch all bluetooth devices offline" to be carried out by
- userspace or by rfkill-input.
-
- THE RFKILL CLASS NEVER ISSUES INPUT EVENTS. THE RFKILL CLASS DOES
- NOT LISTEN TO INPUT EVENTS. NO DRIVER USING THE RFKILL CLASS SHALL
- EVER LISTEN TO, OR ACT ON RFKILL INPUT EVENTS. Doing otherwise is
- a layering violation.
-
- Most wireless data communication drivers in the kernel have just to
- implement the rfkill class API to work properly. Interfacing to the
- input layer is not often required (and is very often a *bug*) on
- wireless drivers.
-
- Platform drivers often have to attach to the input layer to *issue*
- (but never to listen to) rfkill events for rfkill switches, and also to
- the rfkill class to export a control interface for the platform rfkill
- controllers to the rfkill subsystem. This does NOT mean the rfkill
- switch is attached to a rfkill class (doing so is almost always wrong).
- It just means the same kernel module is the driver for different
- devices (rfkill switches and rfkill controllers).
-
-
-Userspace input handlers (uevents) or kernel input handlers (rfkill-input):
-
- * Implements the policy of what should happen when one of the input
- layer events related to rfkill operation is received.
- * Uses the sysfs interface (userspace) or private rfkill API calls
- to tell the devices registered with the rfkill class to change
- their state (i.e. translates the input layer event into real
- action).
-
- * rfkill-input implements EPO by handling EV_SW SW_RFKILL_ALL 0
- (power off all transmitters) in a special way: it ignores any
- overrides and local state cache and forces all transmitters to the
- RFKILL_STATE_SOFT_BLOCKED state (including those which are already
- supposed to be BLOCKED).
- * rfkill EPO will remain active until rfkill-input receives an
- EV_SW SW_RFKILL_ALL 1 event. While the EPO is active, transmitters
- are locked in the blocked state (rfkill will refuse to unblock them).
- * rfkill-input implements different policies that the user can
- select for handling EV_SW SW_RFKILL_ALL 1. It will unlock rfkill,
- and either do nothing (leave transmitters blocked, but now unlocked),
- restore the transmitters to their state before the EPO, or unblock
- them all.
-
-Userspace uevent handler or kernel platform-specific drivers hooked to the
-rfkill notifier chain:
-
- * Taps into the rfkill notifier chain or to KOBJ_CHANGE uevents,
- in order to know when a device that is registered with the rfkill
- class changes state;
- * Issues feedback notifications to the user;
- * In the rare platforms where this is required, synthesizes an input
- event to command all *OTHER* rfkill devices to also change their
- statues when a specific rfkill device changes state.
-
-
-===============================================================================
-3: Kernel driver guidelines
-
-Remember: point-of-view is everything for a driver that connects to the rfkill
-subsystem. All the details below must be measured/perceived from the point of
-view of the specific driver being modified.
-
-The first thing one needs to know is whether his driver should be talking to
-the rfkill class or to the input layer. In rare cases (platform drivers), it
-could happen that you need to do both, as platform drivers often handle a
-variety of devices in the same driver.
-
-Do not mistake input devices for rfkill controllers. The only type of "rfkill
-switch" device that is to be registered with the rfkill class are those
-directly controlling the circuits that cause a wireless transmitter to stop
-working (or the software equivalent of them), i.e. what we call a rfkill
-controller. Every other kind of "rfkill switch" is just an input device and
-MUST NOT be registered with the rfkill class.
-
-A driver should register a device with the rfkill class when ALL of the
-following conditions are met (they define a rfkill controller):
-
-1. The device is/controls a data communications wireless transmitter;
-
-2. The kernel can interact with the hardware/firmware to CHANGE the wireless
- transmitter state (block/unblock TX operation);
-
-3. The transmitter can be made to not emit any energy when "blocked":
- rfkill is not about blocking data transmissions, it is about blocking
- energy emission;
-
-A driver should register a device with the input subsystem to issue
-rfkill-related events (KEY_WLAN, KEY_BLUETOOTH, KEY_WWAN, KEY_WIMAX,
-SW_RFKILL_ALL, etc) when ALL of the folowing conditions are met:
-
-1. It is directly related to some physical device the user interacts with, to
- command the O.S./firmware/hardware to enable/disable a data communications
- wireless transmitter.
-
- Examples of the physical device are: buttons, keys and switches the user
- will press/touch/slide/switch to enable or disable the wireless
- communication device.
-
-2. It is NOT slaved to another device, i.e. there is no other device that
- issues rfkill-related input events in preference to this one.
-
- Please refer to the corner cases and examples section for more details.
-
-When in doubt, do not issue input events. For drivers that should generate
-input events in some platforms, but not in others (e.g. b43), the best solution
-is to NEVER generate input events in the first place. That work should be
-deferred to a platform-specific kernel module (which will know when to generate
-events through the rfkill notifier chain) or to userspace. This avoids the
-usual maintenance problems with DMI whitelisting.
-
-
-Corner cases and examples:
-====================================
-
-1. If the device is an input device that, because of hardware or firmware,
-causes wireless transmitters to be blocked regardless of the kernel's will, it
-is still just an input device, and NOT to be registered with the rfkill class.
-
-2. If the wireless transmitter switch control is read-only, it is an input
-device and not to be registered with the rfkill class (and maybe not to be made
-an input layer event source either, see below).
-
-3. If there is some other device driver *closer* to the actual hardware the
-user interacted with (the button/switch/key) to issue an input event, THAT is
-the device driver that should be issuing input events.
-
-E.g:
- [RFKILL slider switch] -- [GPIO hardware] -- [WLAN card rf-kill input]
- (platform driver) (wireless card driver)
-
-The user is closer to the RFKILL slide switch plaform driver, so the driver
-which must issue input events is the platform driver looking at the GPIO
-hardware, and NEVER the wireless card driver (which is just a slave). It is
-very likely that there are other leaves than just the WLAN card rf-kill input
-(e.g. a bluetooth card, etc)...
-
-On the other hand, some embedded devices do this:
-
- [RFKILL slider switch] -- [WLAN card rf-kill input]
- (wireless card driver)
-
-In this situation, the wireless card driver *could* register itself as an input
-device and issue rf-kill related input events... but in order to AVOID the need
-for DMI whitelisting, the wireless card driver does NOT do it. Userspace (HAL)
-or a platform driver (that exists only on these embedded devices) will do the
-dirty job of issuing the input events.
-
-
-COMMON MISTAKES in kernel drivers, related to rfkill:
-====================================
-
-1. NEVER confuse input device keys and buttons with input device switches.
-
- 1a. Switches are always set or reset. They report the current state
- (on position or off position).
-
- 1b. Keys and buttons are either in the pressed or not-pressed state, and
- that's it. A "button" that latches down when you press it, and
- unlatches when you press it again is in fact a switch as far as input
- devices go.
-
-Add the SW_* events you need for switches, do NOT try to emulate a button using
-KEY_* events just because there is no such SW_* event yet. Do NOT try to use,
-for example, KEY_BLUETOOTH when you should be using SW_BLUETOOTH instead.
-
-2. Input device switches (sources of EV_SW events) DO store their current state
-(so you *must* initialize it by issuing a gratuitous input layer event on
-driver start-up and also when resuming from sleep), and that state CAN be
-queried from userspace through IOCTLs. There is no sysfs interface for this,
-but that doesn't mean you should break things trying to hook it to the rfkill
-class to get a sysfs interface :-)
-
-3. Do not issue *_RFKILL_ALL events by default, unless you are sure it is the
-correct event for your switch/button. These events are emergency power-off
-events when they are trying to turn the transmitters off. An example of an
-input device which SHOULD generate *_RFKILL_ALL events is the wireless-kill
-switch in a laptop which is NOT a hotkey, but a real sliding/rocker switch.
-An example of an input device which SHOULD NOT generate *_RFKILL_ALL events by
-default, is any sort of hot key that is type-specific (e.g. the one for WLAN).
-
-
-3.1 Guidelines for wireless device drivers
-------------------------------------------
-
-(in this text, rfkill->foo means the foo field of struct rfkill).
-
-1. Each independent transmitter in a wireless device (usually there is only one
-transmitter per device) should have a SINGLE rfkill class attached to it.
-
-2. If the device does not have any sort of hardware assistance to allow the
-driver to rfkill the device, the driver should emulate it by taking all actions
-required to silence the transmitter.
-
-3. If it is impossible to silence the transmitter (i.e. it still emits energy,
-even if it is just in brief pulses, when there is no data to transmit and there
-is no hardware support to turn it off) do NOT lie to the users. Do not attach
-it to a rfkill class. The rfkill subsystem does not deal with data
-transmission, it deals with energy emission. If the transmitter is emitting
-energy, it is not blocked in rfkill terms.
-
-4. It doesn't matter if the device has multiple rfkill input lines affecting
-the same transmitter, their combined state is to be exported as a single state
-per transmitter (see rule 1).
-
-This rule exists because users of the rfkill subsystem expect to get (and set,
-when possible) the overall transmitter rfkill state, not of a particular rfkill
-line.
-
-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.
+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".

-3. Deactivates the soft rfkill line if none of the hardware rfkill lines are
-active and the caller asked for RFKILL_STATE_UNBLOCKED.
+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.

-===============================================================================
-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.
+The rfkill-input code implements a basic response to rfkill buttons -- it
+implements turning on/off all devices of a certain class (or all).

-The 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).
+When the device is hard-blocked (either by a call to rfkill_set_hw_state()
+or from query_hw_block) then set_block() will not be invoked. When the device
+is hard-unblocked again, rfkill drivers need to take into account the return
+value from rfkill_set_hw_state() -- it indicates whether the device is still
+soft-blocked or not. Some devices can actually keep track of soft blocks,
+for those you can call rfkill_has_sw_block_memory() and will then get soft
+block notifications while hard-blocked.

-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.
+The entire functionality is spread over more than one subsystem:

-===============================================================================
-5: Userspace support
+ * The kernel input layer generates KEY_WWAN, KEY_WLAN etc. and
+ SW_RFKILL_ALL -- when the user presses a button. Drivers for radio
+ transmitters generally do not register to the input layer, unless the
+ device really provides an input device (i.e. a button that has no
+ effect other than generating a button press event)

-rfkill devices issue uevents (with an action of "change"), with the following
-environment variables set:
+ * The rfkill-input code hooks up to these events and switches the soft-block
+ of the various radio transmitters, depending on the button type.

-RFKILL_NAME
-RFKILL_STATE
-RFKILL_TYPE
+ * The rfkill drivers turn off/on their transmitters as requested.

-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.
+ * The rfkill class will generate userspace notifications (uevents) to tell
+ userspace what the current state is.

-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.
+3. Kernel driver guidelines

-Userspace cannot assume it is the only source of control for rfkill switches.
-Their state can change due to firmware actions, direct user actions, and the
-rfkill-input EPO override for *_RFKILL_ALL.

-When rfkill-input is not active, userspace must initiate a rfkill status
-change by writing to the "state" attribute in order for anything to happen.
+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.

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

-The following sysfs entries will be created:
+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.
+
+
+
+4. Kernel API
+
+To build a driver with rfkill subsystem support, the driver should depend on
+(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).
+
+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.
+
+
+
+5. Userspace support
+
+The following sysfs entries exist for every rfkill device:

name: Name assigned by driver to this key (interface or driver name).
type: Name of the key type ("wlan", "bluetooth", etc).
state: Current state of the transmitter
0: RFKILL_STATE_SOFT_BLOCKED
- transmitter is forced off, but one can override it
- by a write to the state attribute;
+ transmitter is turned off by software
1: RFKILL_STATE_UNBLOCKED
- transmiter is NOT forced off, and may operate if
- all other conditions for such operation are met
- (such as interface is up and configured, etc);
+ transmiter is (potentially) active
2: RFKILL_STATE_HARD_BLOCKED
transmitter is forced off by something outside of
- the driver's control. One cannot set a device to
- this state through writes to the state attribute;
- claim: 1: Userspace handles events, 0: Kernel handles events
-
-Both the "state" and "claim" entries are also writable. For the "state" entry
-this means that when 1 or 0 is written, the device rfkill state (if not yet in
-the requested state), will be will be toggled accordingly.
-
-For the "claim" entry writing 1 to it means that the kernel no longer handles
-key events even though RFKILL_INPUT input was enabled. When "claim" has been
-set to 0, userspace should make sure that it listens for the input events or
-check the sysfs "state" entry regularly to correctly perform the required tasks
-when the rkfill key is pressed.
-
-A note about input devices and EV_SW events:
-
-In order to know the current state of an input device switch (like
-SW_RFKILL_ALL), you will need to use an IOCTL. That information is not
-available through sysfs in a generic way at this time, and it is not available
-through the rfkill class AT ALL.
+ the driver's control.
+ claim: 0: Kernel handles events (currently always reads that value)
+
+rfkill devices also issue uevents (with an action of "change"), with the
+following environment variables set:
+
+RFKILL_NAME
+RFKILL_STATE
+RFKILL_TYPE
+
+The contents of these variables corresponds to the "name", "state" and
+"type" sysfs files explained above.
--- wireless-testing.orig/net/rfkill/Kconfig 2009-04-29 10:49:35.000000000 +0200
+++ wireless-testing/net/rfkill/Kconfig 2009-04-29 10:49:38.000000000 +0200
@@ -10,22 +10,8 @@ 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
default y
-
--- wireless-testing.orig/net/rfkill/Makefile 2009-04-29 10:49:35.000000000 +0200
+++ wireless-testing/net/rfkill/Makefile 2009-04-29 10:49:38.000000000 +0200
@@ -2,5 +2,5 @@
# Makefile for the RF switch subsystem.
#

-obj-$(CONFIG_RFKILL) += rfkill.o
-obj-$(CONFIG_RFKILL_INPUT) += rfkill-input.o
+rfkill-y += core.o input.o
+obj-$(CONFIG_RFKILL) += rfkill.o
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/rfkill/core.c 2009-04-30 16:50:58.000000000 +0200
@@ -0,0 +1,895 @@
+/*
+ * Copyright (C) 2006 - 2007 Ivo van Doorn
+ * Copyright (C) 2007 Dmitry Torokhov
+ * Copyright 2009 Johannes Berg <[email protected]>
+ *
+ * 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 has_sw_block_memory;
+
+ 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 <[email protected]>");
+MODULE_AUTHOR("Johannes Berg <[email protected]>");
+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)
+ 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;
+ bool hwblock;
+
+ /*
+ * 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;
+
+ hwblock = !!(rfkill->state & RFKILL_BLOCK_HW);
+ rfkill->state |= RFKILL_BLOCK_SW_SETCALL;
+ spin_unlock_irqrestore(&rfkill->lock, flags);
+
+ /*
+ * HW already blocked, so nothing changes for now
+ * (unless device has memory for software blocks)
+ */
+ if (!rfkill->has_sw_block_memory && hwblock)
+ return;
+
+ if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP))
+ return;
+
+ if (rfkill->ops->set_block(rfkill->data, blocked)) {
+ /*
+ * 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.
+ */
+ spin_lock_irqsave(&rfkill->lock, flags);
+ if (rfkill->state & RFKILL_BLOCK_SW_PREV)
+ rfkill->state |= RFKILL_BLOCK_SW;
+ else
+ rfkill->state &= ~RFKILL_BLOCK_SW;
+ rfkill->state &= ~RFKILL_BLOCK_SW_SETCALL;
+ 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);
+}
+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)
+ 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);
+
+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);
+
+ return 0;
+}
+
+static int rfkill_resume(struct device *dev)
+{
+ struct rfkill *rfkill = to_rfkill(dev);
+
+ 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);
+}
+
+void rfkill_has_sw_block_memory(struct rfkill *rfkill)
+{
+ BUG_ON(!rfkill);
+ BUG_ON(rfkill->registered);
+
+ rfkill->has_sw_block_memory = true;
+}
+EXPORT_SYMBOL(rfkill_has_sw_block_memory);
+
+int __must_check rfkill_register(struct rfkill *rfkill)
+{
+ static unsigned long rfkill_no = 0;
+ 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;
+
+ error = rfkill_handler_init();
+ if (error)
+ class_unregister(&rfkill_class);
+
+ out:
+ return error;
+}
+subsys_initcall(rfkill_init);
+
+static void __exit rfkill_exit(void)
+{
+ rfkill_handler_exit();
+ class_unregister(&rfkill_class);
+}
+module_exit(rfkill_exit);
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/rfkill/input.c 2009-04-29 10:49:38.000000000 +0200
@@ -0,0 +1,342 @@
+/*
+ * Input layer to RF Kill interface connector
+ *
+ * Copyright (c) 2007 Dmitry Torokhov
+ * Copyright 2009 Johannes Berg <[email protected]>
+ *
+ * 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))
+ break;
+ 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);
+}
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-testing/net/rfkill/rfkill.h 2009-04-29 10:49:38.000000000 +0200
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2007 Ivo van Doorn
+ * Copyright 2009 Johannes Berg <[email protected]>
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef __RFKILL_INPUT_H
+#define __RFKILL_INPUT_H
+
+/* 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);
+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 */
--- wireless-testing.orig/MAINTAINERS 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/MAINTAINERS 2009-04-29 10:49:38.000000000 +0200
@@ -4720,9 +4720,9 @@ S: Supported
F: fs/reiserfs/

RFKILL
-P: Ivo van Doorn
-M: [email protected]
-L: [email protected]
+P: Johannes Berg
+M: [email protected]
+L: [email protected]
S: Maintained
F Documentation/rfkill.txt
F: net/rfkill/
--- wireless-testing.orig/include/linux/Kbuild 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/include/linux/Kbuild 2009-04-29 10:49:38.000000000 +0200
@@ -310,6 +310,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
--- wireless-testing.orig/drivers/net/wireless/ath/ath9k/ath9k.h 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath/ath9k/ath9k.h 2009-04-29 10:49:38.000000000 +0200
@@ -464,12 +464,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];
};

@@ -513,8 +510,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)
--- wireless-testing.orig/drivers/net/wireless/ath/ath9k/pci.c 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/ath/ath9k/pci.c 2009-04-29 10:49:38.000000000 +0200
@@ -227,11 +227,6 @@ static int ath_pci_suspend(struct pci_de

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
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;
}

--- wireless-testing.orig/drivers/net/wireless/b43/Kconfig 2009-04-29 10:49:35.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/Kconfig 2009-04-29 10:49:38.000000000 +0200
@@ -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,
--- wireless-testing.orig/drivers/net/wireless/b43/rfkill.h 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/rfkill.h 2009-04-29 10:49:38.000000000 +0200
@@ -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 */
--- wireless-testing.orig/drivers/net/wireless/b43legacy/Kconfig 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/Kconfig 2009-04-29 10:49:38.000000000 +0200
@@ -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,
--- wireless-testing.orig/drivers/net/wireless/b43legacy/leds.c 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/leds.c 2009-04-29 10:49:38.000000000 +0200
@@ -86,7 +86,8 @@ static void b43legacy_led_brightness_set

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;
--- wireless-testing.orig/drivers/net/wireless/b43legacy/rfkill.h 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43legacy/rfkill.h 2009-04-29 10:49:38.000000000 +0200
@@ -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 */
--- wireless-testing.orig/drivers/net/wireless/b43/leds.c 2009-04-29 10:49:35.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/leds.c 2009-04-29 10:49:38.000000000 +0200
@@ -87,7 +87,7 @@ static void b43_led_brightness_set(struc
}

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;
--- wireless-testing.orig/drivers/net/wireless/iwlwifi/Kconfig 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/iwlwifi/Kconfig 2009-04-29 10:49:38.000000000 +0200
@@ -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"
--- wireless-testing.orig/drivers/net/wireless/b43/phy_lp.c 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/net/wireless/b43/phy_lp.c 2009-04-29 10:49:38.000000000 +0200
@@ -488,7 +488,7 @@ static void b43_lpphy_op_radio_write(str
}

static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev,
- enum rfkill_state state)
+ bool blocked)
{
//TODO
}
--- wireless-testing.orig/drivers/platform/x86/Kconfig 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/platform/x86/Kconfig 2009-04-29 10:49:38.000000000 +0200
@@ -21,7 +21,6 @@ config ACER_WMI
depends on NEW_LEDS
depends on BACKLIGHT_CLASS_DEVICE
depends on SERIO_I8042
- depends on RFKILL
select ACPI_WMI
---help---
This is a driver for newer Acer (and Wistron) laptops. It adds
@@ -60,7 +59,6 @@ config DELL_LAPTOP
depends on DCDBAS
depends on EXPERIMENTAL
depends on BACKLIGHT_CLASS_DEVICE
- depends on RFKILL
depends on POWER_SUPPLY
default n
---help---
@@ -117,7 +115,6 @@ config HP_WMI
tristate "HP WMI extras"
depends on ACPI_WMI
depends on INPUT
- depends on RFKILL
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.
@@ -203,7 +200,6 @@ config THINKPAD_ACPI
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
@@ -340,7 +336,6 @@ config EEEPC_LAPTOP
depends on EXPERIMENTAL
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.
@@ -407,7 +402,6 @@ config ACPI_TOSHIBA
depends on INPUT
select INPUT_POLLDEV
select NET
- select RFKILL
select BACKLIGHT_CLASS_DEVICE
---help---
This driver adds support for access to certain system settings
--- wireless-testing.orig/drivers/platform/x86/acer-wmi.c 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/platform/x86/acer-wmi.c 2009-04-29 10:49:38.000000000 +0200
@@ -958,58 +958,50 @@ static void acer_rfkill_update(struct wo

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 devic
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 devic
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;
}
--- wireless-testing.orig/drivers/platform/x86/dell-laptop.c 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/platform/x86/dell-laptop.c 2009-04-29 10:49:38.000000000 +0200
@@ -174,10 +174,11 @@ dell_send_request(struct calling_interfa
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, en
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;
}
--- wireless-testing.orig/drivers/platform/x86/hp-wmi.c 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/platform/x86/hp-wmi.c 2009-04-29 10:49:38.000000000 +0200
@@ -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, voi
}
} 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(stru
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(stru

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(str
{
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;
}
--- wireless-testing.orig/drivers/platform/x86/toshiba_acpi.c 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/platform/x86/toshiba_acpi.c 2009-04-29 10:49:38.000000000 +0200
@@ -311,28 +311,20 @@ static u32 hci_get_radio_state(bool *rad
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)
{
u32 result1, result2;
u32 value;
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;
+ return -EBUSY;

- switch (state) {
- case RFKILL_STATE_UNBLOCKED:
- if (!radio_state)
- return -EPERM;
- break;
- case RFKILL_STATE_SOFT_BLOCKED:
- break;
- default:
- return -EINVAL;
- }
+ if (WARN_ON(!blocked && !radio_state))
+ return;

mutex_lock(&dev->mutex);
hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1);
@@ -340,8 +332,7 @@ static int bt_rfkill_toggle_radio(void *
mutex_unlock(&dev->mutex);

if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS)
- return -EFAULT;
-
+ return -EBUSY;
return 0;
}

@@ -365,7 +356,7 @@ static void bt_poll_rfkill(struct input_
mutex_unlock(&dev->mutex);

if (unlikely(state_changed)) {
- rfkill_force_state(dev->rfk_dev,
+ rfkill_set_hw_state(dev->rfk_dev,
new_rfk_state ?
RFKILL_STATE_SOFT_BLOCKED :
RFKILL_STATE_HARD_BLOCKED);
--- wireless-testing.orig/arch/arm/mach-pxa/tosa-bt.c 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/arch/arm/mach-pxa/tosa-bt.c 2009-04-29 10:49:38.000000000 +0200
@@ -35,21 +35,25 @@ static void tosa_bt_off(struct tosa_bt_d
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
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
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(stru

platform_set_drvdata(dev, NULL);

- if (rfk)
+ if (rfk) {
rfkill_unregister(rfk);
+ rfkill_destroy(rfk);
+ }
rfk = NULL;

tosa_bt_off(data);
--- wireless-testing.orig/arch/arm/mach-pxa/tosa.c 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/arch/arm/mach-pxa/tosa.c 2009-04-29 10:49:38.000000000 +0200
@@ -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>
--- wireless-testing.orig/include/net/wimax.h 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/include/net/wimax.h 2009-04-29 10:49:38.000000000 +0200
@@ -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)
--- wireless-testing.orig/net/wimax/op-rfkill.c 2009-04-29 10:49:35.000000000 +0200
+++ wireless-testing/net/wimax/op-rfkill.c 2009-04-29 10:49:38.000000000 +0200
@@ -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
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

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
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
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,22 @@ int wimax_rfkill_add(struct wimax_dev *w
{
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);
-
- 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);
+ d_printf(1, dev, "rfkill %p\n", rfkill);
+
+ rfkill_has_sw_block_memory(rfkill);
+
+ 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 +367,8 @@ int wimax_rfkill_add(struct wimax_dev *w
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 +387,12 @@ void wimax_rfkill_rm(struct wimax_dev *w
{
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
*
--- wireless-testing.orig/net/wimax/Kconfig 2009-04-29 10:49:35.000000000 +0200
+++ wireless-testing/net/wimax/Kconfig 2009-04-29 10:49:39.000000000 +0200
@@ -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
--- wireless-testing.orig/drivers/net/usb/hso.c 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/net/usb/hso.c 2009-04-29 10:49:39.000000000 +0200
@@ -2481,10 +2481,10 @@ static int add_net_device(struct 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,
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
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 us
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]);
}
}
--- wireless-testing.orig/drivers/platform/x86/sony-laptop.c 2009-04-29 10:49:36.000000000 +0200
+++ wireless-testing/drivers/platform/x86/sony-laptop.c 2009-04-29 10:49:39.000000000 +0200
@@ -1051,56 +1051,45 @@ static void sony_nc_rfkill_cleanup(void)
int i;

for (i = 0; i < SONY_RFKILL_MAX; i++) {
- if (sony_rfkill_devices[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 const struct rfkill_ops sony_nc_rfkill_ops = {
+ .set_block = sony_nc_rfkill_set,
+};
+
+/* XXX: this code duplication is very stupid */
+
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);
+ sony_wifi_rfkill = rfkill_alloc("sony-wifi", &device->dev,
+ RFKILL_TYPE_WLAN,
+ &sony_nc_rfkill_ops,
+ (void *)SONY_WIFI);
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;
+ return -ENOMEM;
+
err = rfkill_register(sony_wifi_rfkill);
if (err)
- rfkill_free(sony_wifi_rfkill);
+ rfkill_destroy(sony_wifi_rfkill);
else
sony_rfkill_devices[SONY_WIFI] = sony_wifi_rfkill;
return err;
@@ -1111,17 +1100,16 @@ static int sony_nc_setup_bluetooth_rfkil
int err = 0;
struct rfkill *sony_bluetooth_rfkill;

- sony_bluetooth_rfkill = rfkill_allocate(&device->dev,
- RFKILL_TYPE_BLUETOOTH);
+ sony_bluetooth_rfkill = rfkill_alloc("sony-bluetooth", &device->dev,
+ RFKILL_TYPE_BLUETOOTH,
+ &sony_nc_rfkill_ops,
+ (void *)SONY_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;
+ return -ENOMEM;
+
err = rfkill_register(sony_bluetooth_rfkill);
if (err)
- rfkill_free(sony_bluetooth_rfkill);
+ rfkill_destroy(sony_bluetooth_rfkill);
else
sony_rfkill_devices[SONY_BLUETOOTH] = sony_bluetooth_rfkill;
return err;
@@ -1132,16 +1120,16 @@ static int sony_nc_setup_wwan_rfkill(str
int err = 0;
struct rfkill *sony_wwan_rfkill;

- sony_wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN);
+ sony_wwan_rfkill = rfkill_alloc("sony-wwan", &device->dev,
+ RFKILL_TYPE_WWAN,
+ &sony_nc_rfkill_ops,
+ (void *)SONY_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;
+ return -ENOMEM;
+
err = rfkill_register(sony_wwan_rfkill);
if (err)
- rfkill_free(sony_wwan_rfkill);
+ rfkill_destroy(sony_wwan_rfkill);
else
sony_rfkill_devices[SONY_WWAN] = sony_wwan_rfkill;
return err;
@@ -1152,16 +1140,16 @@ static int sony_nc_setup_wimax_rfkill(st
int err = 0;
struct rfkill *sony_wimax_rfkill;

- sony_wimax_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WIMAX);
+ sony_wimax_rfkill = rfkill_alloc("sony-wimax", &device->dev,
+ RFKILL_TYPE_WIMAX,
+ &sony_nc_rfkill_ops,
+ (void *)SONY_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;
+ return -ENOMEM;
+
err = rfkill_register(sony_wimax_rfkill);
if (err)
- rfkill_free(sony_wimax_rfkill);
+ rfkill_destroy(sony_wimax_rfkill);
else
sony_rfkill_devices[SONY_WIMAX] = sony_wimax_rfkill;
return err;
@@ -1169,15 +1157,28 @@ static int sony_nc_setup_wimax_rfkill(st

static void sony_nc_rfkill_update()
{
- int i;
- enum rfkill_state state;
+ int result, 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);
+ sony_call_snc_handle(0x124, 0x200, &result);
+ if (result & 0x1) {
+ int addr = sony_rfkill_address[i];
+
+ sony_call_snc_handle(0x124, addr, &result);
+ if (result & 0xf)
+ rfkill_set_sw_state(
+ sony_rfkill_devices[i],
+ false);
+ else
+ rfkill_set_sw_state(
+ sony_rfkill_devices[i],
+ true);
+ } else {
+ rfkill_set_hw_state(
+ sony_rfkill_devices[i],
+ true);
+ }
}
}
}