Search Linux Wireless

[PATCH] ath5k: added initial RFKILL support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

Am Mittwoch, 20. Mai 2009 22:29:42 schrieb Bob Copeland:
> I'm definitely interested in seeing rfkill gpio support getting in,
> it's overdue.  So I say repost it and we can fasttrack that.  Digging
> up your last patchset was already on my todo list.
Here we go. I attached a patch (against wireless-testing) which represents my 
latest work on RFKILL support for ath5k. It still lacks testing with a real 
physical rfkill switch (for I don't have one with my NC10) but everything else 
(SW rfkill) works fine after testing various scenarios. Furthermore I think I 
addressed all remarks in the last review by Bob Copeland.

Regards,

Toby
From 0dfab8fbd3223747e2a7948296606e661899630b Mon Sep 17 00:00:00 2001
From: Tobias Doerffel <tobias.doerffel@xxxxxxxxx>
Date: Tue, 26 May 2009 00:52:20 +0200
Subject: [PATCH] ath5k: added initial RFKILL support

This patch introduces initial RFKILL support for the ath5k driver.
All RFKILL related code is separated into newly created rfkill.c.

Changes to existing code are minimal:

- added a new data structure ath5k_rfkill to the ath5k_softc structure
- new function ath5k_eeprom_read_rfkill() for retrieving RFKILL related
  parameters from EEPROM
- inserted calls to init/deinit routines for both the SW and HW part of
  RFKILL support
- ath5k_intr() has been extended to handle AR5K_INT_GPIO interrupts
---
 drivers/net/wireless/ath/ath5k/Kconfig  |    9 +
 drivers/net/wireless/ath/ath5k/Makefile |    1 +
 drivers/net/wireless/ath/ath5k/ath5k.h  |   22 +++
 drivers/net/wireless/ath/ath5k/base.c   |   20 ++-
 drivers/net/wireless/ath/ath5k/base.h   |   26 +++
 drivers/net/wireless/ath/ath5k/eeprom.c |   16 ++
 drivers/net/wireless/ath/ath5k/reset.c  |   17 --
 drivers/net/wireless/ath/ath5k/rfkill.c |  268 +++++++++++++++++++++++++++++++
 8 files changed, 358 insertions(+), 21 deletions(-)
 create mode 100644 drivers/net/wireless/ath/ath5k/rfkill.c

diff --git a/drivers/net/wireless/ath/ath5k/Kconfig b/drivers/net/wireless/ath/ath5k/Kconfig
index 509b6f9..618a879 100644
--- a/drivers/net/wireless/ath/ath5k/Kconfig
+++ b/drivers/net/wireless/ath/ath5k/Kconfig
@@ -39,3 +39,12 @@ config ATH5K_DEBUG
 
 	  modprobe ath5k debug=0x00000400
 
+config ATH5K_RFKILL
+	bool "Atheros 5xxx rfkill support"
+	select RFKILL
+	depends on ATH5K
+	default y
+	---help---
+	  Include support for enabling/disabling WiFi via rfkill switch
+	  with Atheros 5xxx cards
+
diff --git a/drivers/net/wireless/ath/ath5k/Makefile b/drivers/net/wireless/ath/ath5k/Makefile
index 84a74c5..f1e281c 100644
--- a/drivers/net/wireless/ath/ath5k/Makefile
+++ b/drivers/net/wireless/ath/ath5k/Makefile
@@ -12,4 +12,5 @@ ath5k-y				+= attach.o
 ath5k-y				+= base.o
 ath5k-y				+= led.o
 ath5k-$(CONFIG_ATH5K_DEBUG)	+= debug.o
+ath5k-$(CONFIG_ATH5K_RFKILL)	+= rfkill.o
 obj-$(CONFIG_ATH5K)		+= ath5k.o
diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
index 8137182..0dadcbb 100644
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -1162,6 +1162,10 @@ extern void ath5k_led_enable(struct ath5k_softc *sc);
 extern void ath5k_led_off(struct ath5k_softc *sc);
 extern void ath5k_unregister_leds(struct ath5k_softc *sc);
 
+/* Start/Stop Functions */
+extern int ath5k_init(struct ath5k_softc *sc);
+extern int ath5k_stop_hw(struct ath5k_softc *sc);
+
 /* Reset Functions */
 extern int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial);
 extern int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, struct ieee80211_channel *channel, bool change_channel);
@@ -1190,6 +1194,7 @@ extern void ath5k_hw_update_mib_counters(struct ath5k_hw *ah, struct ieee80211_l
 extern int ath5k_eeprom_init(struct ath5k_hw *ah);
 extern void ath5k_eeprom_detach(struct ath5k_hw *ah);
 extern int ath5k_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac);
+extern void ath5k_eeprom_read_rfkill(struct ath5k_hw *ah, u16 *gpio, u8 *polarity);
 extern bool ath5k_eeprom_is_hb63(struct ath5k_hw *ah);
 
 /* Protocol Control Unit Functions */
@@ -1256,6 +1261,23 @@ extern u32 ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio);
 extern int ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val);
 extern void ath5k_hw_set_gpio_intr(struct ath5k_hw *ah, unsigned int gpio, u32 interrupt_level);
 
+/* rfkill Functions */
+#ifdef CONFIG_ATH5K_RFKILL
+extern int ath5k_rfkill_init(struct ath5k_softc *sc);
+extern void ath5k_rfkill_deinit(struct ath5k_softc *sc);
+extern void ath5k_rfkill_hw_start(struct ath5k_hw *ah);
+extern void ath5k_rfkill_hw_stop(struct ath5k_hw *ah);
+#else
+static inline int ath5k_rfkill_init(struct ath5k_softc *sc)
+{
+	return 0;
+}
+
+static inline void ath5k_rfkill_deinit(struct ath5k_softc *sc) {}
+static inline void ath5k_rfkill_hw_start(struct ath5k_hw *ah) {}
+static inline void ath5k_rfkill_hw_stop(struct ath5k_hw *ah) {}
+#endif
+
 /* Misc functions */
 int ath5k_hw_set_capabilities(struct ath5k_hw *ah);
 extern int ath5k_hw_get_capability(struct ath5k_hw *ah, enum ath5k_capability_type cap_type, u32 capability, u32 *result);
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index fb51937..aa9446e 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -363,9 +363,7 @@ static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
 }
 
 /* Interrupt handling */
-static int 	ath5k_init(struct ath5k_softc *sc);
 static int 	ath5k_stop_locked(struct ath5k_softc *sc);
-static int 	ath5k_stop_hw(struct ath5k_softc *sc);
 static irqreturn_t ath5k_intr(int irq, void *dev_id);
 static void 	ath5k_tasklet_reset(unsigned long data);
 
@@ -822,6 +820,9 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
 
 	ath5k_init_leds(sc);
 
+	/* Initialize rfkill subsystem */
+	ath5k_rfkill_init(sc);
+
 	return 0;
 err_queues:
 	ath5k_txq_release(sc);
@@ -856,6 +857,7 @@ ath5k_detach(struct pci_dev *pdev, struct ieee80211_hw *hw)
 	ath5k_txq_release(sc);
 	ath5k_hw_release_tx_queue(sc->ah, sc->bhalq);
 	ath5k_unregister_leds(sc);
+	ath5k_rfkill_deinit(sc);
 
 	/*
 	 * NB: can't reclaim these until after ieee80211_ifdetach
@@ -2321,7 +2323,7 @@ static void ath5k_tasklet_beacon(unsigned long data)
 * Interrupt handling *
 \********************/
 
-static int
+int
 ath5k_init(struct ath5k_softc *sc)
 {
 	struct ath5k_hw *ah = sc->ah;
@@ -2353,6 +2355,8 @@ ath5k_init(struct ath5k_softc *sc)
 	if (ret)
 		goto done;
 
+	ath5k_rfkill_hw_start(ah);
+
 	/*
 	 * Reset the key cache since some parts do not reset the
 	 * contents on initial power up or resume from suspend.
@@ -2419,7 +2423,7 @@ ath5k_stop_locked(struct ath5k_softc *sc)
  * if another thread does a system call and the thread doing the
  * stop is preempted).
  */
-static int
+int
 ath5k_stop_hw(struct ath5k_softc *sc)
 {
 	int ret;
@@ -2461,6 +2465,8 @@ ath5k_stop_hw(struct ath5k_softc *sc)
 	tasklet_kill(&sc->restq);
 	tasklet_kill(&sc->beacontq);
 
+	ath5k_rfkill_hw_stop(sc->ah);
+
 	return ret;
 }
 
@@ -2519,6 +2525,12 @@ ath5k_intr(int irq, void *dev_id)
 				 */
 				ath5k_hw_update_mib_counters(ah, &sc->ll_stats);
 			}
+#ifdef CONFIG_ATH5K_RFKILL
+			if (status & AR5K_INT_GPIO)
+			{
+				tasklet_schedule(&sc->rf_kill.toggleq);
+			}
+#endif
 		}
 	} while (ath5k_hw_is_intr_pending(ah) && --counter > 0);
 
diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h
index 852b2c1..88c6929 100644
--- a/drivers/net/wireless/ath/ath5k/base.h
+++ b/drivers/net/wireless/ath/ath5k/base.h
@@ -46,6 +46,7 @@
 #include <linux/wireless.h>
 #include <linux/if_ether.h>
 #include <linux/leds.h>
+#include <linux/rfkill.h>
 
 #include "ath5k.h"
 #include "debug.h"
@@ -91,6 +92,27 @@ struct ath5k_led
 	struct led_classdev led_dev;		/* led classdev */
 };
 
+/* Rfkill */
+#define ATH5K_RFKILL_FLAG_HW_STARTED	BIT(0)
+#define ATH5K_RFKILL_FLAG_SW_BLOCKED	BIT(1)
+#define ATH5K_RFKILL_FLAG_HW_BLOCKED	BIT(2)
+
+struct ath5k_rfkill {
+	/* GPIO PIN for rfkill */
+	u16 gpio;
+	/* polarity of rfkill GPIO PIN */
+	u8 polarity;
+	/* The RFKILL subsystem data structure */
+	struct rfkill *rfkill;
+	/* The unique name of this rfkill switch */
+	char rfkill_name[32];
+	/* Flags indicating SW RFKILL status */
+	u32 flags;
+	/* RFKILL toggle tasklet */
+	struct tasklet_struct toggleq;
+	/* mutex for RFKILL related operations */
+	struct mutex lock;
+};
 
 #if CHAN_DEBUG
 #define ATH_CHAN_MAX	(26+26+26+200+200)
@@ -167,6 +189,10 @@ struct ath5k_softc {
 	struct tasklet_struct	txtq;		/* tx intr tasklet */
 	struct ath5k_led	tx_led;		/* tx led */
 
+#ifdef CONFIG_ATH5K_RFKILL
+	struct ath5k_rfkill	rf_kill;
+#endif
+
 	spinlock_t		block;		/* protects beacon */
 	struct tasklet_struct	beacontq;	/* beacon intr tasklet */
 	struct ath5k_buf	*bbuf;		/* beacon buffer */
diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c
index c56b494..ff00502 100644
--- a/drivers/net/wireless/ath/ath5k/eeprom.c
+++ b/drivers/net/wireless/ath/ath5k/eeprom.c
@@ -1800,3 +1800,19 @@ int ath5k_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac)
 
 	return 0;
 }
+
+
+/*
+ * Read RFKILL GPIO configuration from eeprom
+ */
+void ath5k_eeprom_read_rfkill(struct ath5k_hw *ah, u16 *gpio, u8 *polarity)
+{
+	u16 data;
+
+	ath5k_hw_eeprom_read(ah, AR5K_EEPROM_RFKILL_GPIO_SEL, &data);
+	*gpio = AR5K_REG_MS(data, AR5K_EEPROM_RFKILL_GPIO_SEL);
+
+	ath5k_hw_eeprom_read(ah, AR5K_EEPROM_RFKILL_POLARITY, &data);
+	*polarity = AR5K_REG_MS(data, AR5K_EEPROM_RFKILL_POLARITY);
+}
+
diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c
index bd1940e..8077f0a 100644
--- a/drivers/net/wireless/ath/ath5k/reset.c
+++ b/drivers/net/wireless/ath/ath5k/reset.c
@@ -1305,23 +1305,6 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
 	if (ah->ah_version != AR5K_AR5210)
 		ath5k_hw_set_imr(ah, ah->ah_imr);
 
-	/*
-	 * Setup RFKill interrupt if rfkill flag is set on eeprom.
-	 * TODO: Use gpio pin and polarity infos from eeprom
-	 * TODO: Handle this in ath5k_intr because it'll result
-	 * 	 a nasty interrupt storm.
-	 */
-#if 0
-	if (AR5K_EEPROM_HDR_RFKILL(ah->ah_capabilities.cap_eeprom.ee_header)) {
-		ath5k_hw_set_gpio_input(ah, 0);
-		ah->ah_gpio[0] = ath5k_hw_get_gpio(ah, 0);
-		if (ah->ah_gpio[0] == 0)
-			ath5k_hw_set_gpio_intr(ah, 0, 1);
-		else
-			ath5k_hw_set_gpio_intr(ah, 0, 0);
-	}
-#endif
-
 	/* Enable 32KHz clock function for AR5212+ chips
 	 * Set clocks to 32KHz operation and use an
 	 * external 32KHz crystal when sleeping if one
diff --git a/drivers/net/wireless/ath/ath5k/rfkill.c b/drivers/net/wireless/ath/ath5k/rfkill.c
new file mode 100644
index 0000000..2ae6961
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/rfkill.c
@@ -0,0 +1,268 @@
+/*
+ * RFKILL support for ath5k
+ *
+ * Copyright (c) 2009 Tobias Doerffel <tobias.doerffel@xxxxxxxxx>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "base.h"
+
+
+static inline void ath5k_rfkill_disable(struct ath5k_softc *sc)
+{
+	ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "rfkill disable (gpio:%d polarity:%d)\n",
+		sc->rf_kill.gpio, sc->rf_kill.polarity);
+	ath5k_hw_set_gpio_output(sc->ah, sc->rf_kill.gpio);
+	ath5k_hw_set_gpio(sc->ah, sc->rf_kill.gpio, !sc->rf_kill.polarity);
+}
+
+
+static inline void ath5k_rfkill_enable(struct ath5k_softc *sc)
+{
+	ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "rfkill enable (gpio:%d polarity:%d)\n",
+		sc->rf_kill.gpio, sc->rf_kill.polarity);
+	ath5k_hw_set_gpio_output(sc->ah, sc->rf_kill.gpio);
+	ath5k_hw_set_gpio(sc->ah, sc->rf_kill.gpio, sc->rf_kill.polarity);
+}
+
+static inline void ath5k_rfkill_set_intr(struct ath5k_softc *sc, bool enable)
+{
+	struct ath5k_hw *ah = sc->ah;
+	ath5k_hw_set_gpio_input(ah, sc->rf_kill.gpio);
+	ah->ah_gpio[0] = ath5k_hw_get_gpio(ah, sc->rf_kill.gpio);
+	ath5k_hw_set_gpio_intr(ah, sc->rf_kill.gpio, enable ?
+					!!ah->ah_gpio[0] : !ah->ah_gpio[0]);
+}
+
+static void
+ath5k_radio_enable(struct ath5k_softc *sc)
+{
+	ath5k_init(sc);
+
+	ieee80211_wake_queues(sc->hw);
+}
+
+
+static void
+ath5k_radio_disable(struct ath5k_softc *sc)
+{
+	ieee80211_stop_queues(sc->hw);
+
+	/* shutdown hardware */
+	ath5k_stop_hw(sc);
+}
+
+
+static bool
+ath5k_is_rfkill_set(struct ath5k_softc *sc)
+{
+	/* configuring GPIO for input for some reason disables rfkill */
+	/*ath5k_hw_set_gpio_input(sc->ah, sc->rf_kill.gpio);*/
+	return ath5k_hw_get_gpio(sc->ah, sc->rf_kill.gpio) ==
+							sc->rf_kill.polarity;
+}
+
+
+static void
+ath5k_tasklet_rfkill_toggle(unsigned long data)
+{
+	struct ath5k_softc *sc = (void *)data;
+	bool radio_on;
+
+	mutex_lock(&sc->rf_kill.lock);
+
+	radio_on = !ath5k_is_rfkill_set(sc);
+
+	/*
+	 * enable/disable radio only when there is a
+	 * state change in RF switch
+	 */
+	if (radio_on == !!(sc->rf_kill.flags & ATH5K_RFKILL_FLAG_HW_BLOCKED)) {
+		enum rfkill_state state;
+
+		if (sc->rf_kill.flags & ATH5K_RFKILL_FLAG_SW_BLOCKED) {
+			state = radio_on ? RFKILL_STATE_SOFT_BLOCKED
+				: RFKILL_STATE_HARD_BLOCKED;
+		} else if (radio_on) {
+			ath5k_radio_enable(sc);
+			state = RFKILL_STATE_UNBLOCKED;
+		} else {
+			ath5k_radio_disable(sc);
+			state = RFKILL_STATE_HARD_BLOCKED;
+		}
+
+		if (state == RFKILL_STATE_HARD_BLOCKED)
+			sc->rf_kill.flags |= ATH5K_RFKILL_FLAG_HW_BLOCKED;
+		else
+			sc->rf_kill.flags &= ~ATH5K_RFKILL_FLAG_HW_BLOCKED;
+
+		rfkill_force_state(sc->rf_kill.rfkill, state);
+	}
+	mutex_unlock(&sc->rf_kill.lock);
+
+}
+
+
+/* s/w rfkill handler */
+static int
+ath5k_rfkill_soft_toggle(void *data, enum rfkill_state state)
+{
+	struct ath5k_softc *sc = data;
+	mutex_lock(&sc->rf_kill.lock);
+	/* RFKILL framework might call this toggle callback even if HW is
+	   not started (i.e. interface not up) - depending on the current
+	   SW RFKILL state this could cause calling ath5k_radio_disable()
+           leading to kernel panic due to un-initialized HW state */
+	if (!(sc->rf_kill.flags & ATH5K_RFKILL_FLAG_HW_STARTED)) {
+		mutex_unlock(&sc->rf_kill.lock);
+		return -EINVAL;
+	}
+
+	switch (state) {
+	case RFKILL_STATE_SOFT_BLOCKED:
+		if (!(sc->rf_kill.flags & (ATH5K_RFKILL_FLAG_HW_BLOCKED |
+						ATH5K_RFKILL_FLAG_SW_BLOCKED)))
+			ath5k_radio_disable(sc);
+		sc->rf_kill.flags |= ATH5K_RFKILL_FLAG_SW_BLOCKED;
+		mutex_unlock(&sc->rf_kill.lock);
+		return 0;
+	case RFKILL_STATE_UNBLOCKED:
+		if ((sc->rf_kill.flags & ATH5K_RFKILL_FLAG_SW_BLOCKED)) {
+			sc->rf_kill.flags &= ~ATH5K_RFKILL_FLAG_SW_BLOCKED;
+			if (sc->rf_kill.flags & ATH5K_RFKILL_FLAG_HW_BLOCKED) {
+				ATH5K_ERR(sc, "Can't turn on the"
+					"radio as it is disabled by h/w\n");
+				mutex_unlock(&sc->rf_kill.lock);
+				return -EBUSY;
+			}
+			ath5k_radio_enable(sc);
+		}
+		mutex_unlock(&sc->rf_kill.lock);
+		return 0;
+	default:
+		break;
+	}
+	mutex_unlock(&sc->rf_kill.lock);
+	return -EINVAL;
+}
+
+
+/* Init s/w rfkill */
+int
+ath5k_rfkill_init(struct ath5k_softc *sc)
+{
+	int err;
+
+	mutex_init(&sc->rf_kill.lock);
+
+	sc->rf_kill.rfkill = rfkill_allocate(wiphy_dev(sc->hw->wiphy),
+					     RFKILL_TYPE_WLAN);
+	if (!sc->rf_kill.rfkill) {
+		ATH5K_ERR(sc, "Failed to allocate rfkill\n");
+		return -ENOMEM;
+	}
+
+	snprintf(sc->rf_kill.rfkill_name, sizeof(sc->rf_kill.rfkill_name),
+		"ath5k-%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 = ath5k_rfkill_soft_toggle;
+	sc->rf_kill.rfkill->state = RFKILL_STATE_UNBLOCKED;
+
+	err = rfkill_register(sc->rf_kill.rfkill);
+	if (err) {
+		sc->rf_kill.rfkill = NULL;
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/* Deinitialize s/w rfkill */
+void
+ath5k_rfkill_deinit(struct ath5k_softc *sc)
+{
+	if (sc->rf_kill.rfkill != NULL) {
+		rfkill_unregister(sc->rf_kill.rfkill);
+		sc->rf_kill.rfkill = NULL;
+	}
+}
+
+
+void
+ath5k_rfkill_hw_start(struct ath5k_hw *ah)
+{
+	struct ath5k_softc *sc = ah->ah_sc;
+
+	if ((sc->rf_kill.flags & ATH5K_RFKILL_FLAG_HW_STARTED) ||
+		(sc->rf_kill.flags & ATH5K_RFKILL_FLAG_SW_BLOCKED))
+		return;
+
+	/* read rfkill GPIO configuration from EEPROM */
+	ath5k_eeprom_read_rfkill(ah, &sc->rf_kill.gpio, &sc->rf_kill.polarity);
+
+	tasklet_init(&sc->rf_kill.toggleq, ath5k_tasklet_rfkill_toggle,
+		(unsigned long)sc);
+
+	if (!(sc->rf_kill.flags & ATH5K_RFKILL_FLAG_HW_BLOCKED))
+		ath5k_rfkill_disable(sc);
+
+	/* enable interrupt for rfkill switch */
+	if (AR5K_EEPROM_HDR_RFKILL(ah->ah_capabilities.cap_eeprom.ee_header)) {
+		ath5k_rfkill_set_intr(sc, true);
+	}
+
+	sc->rf_kill.flags |= ATH5K_RFKILL_FLAG_HW_STARTED;
+}
+
+
+void
+ath5k_rfkill_hw_stop(struct ath5k_hw *ah)
+{
+	struct ath5k_softc *sc = ah->ah_sc;
+
+	if (sc->rf_kill.flags & ATH5K_RFKILL_FLAG_HW_STARTED) {
+		/* disable interrupt for rfkill switch */
+		if (AR5K_EEPROM_HDR_RFKILL(
+				ah->ah_capabilities.cap_eeprom.ee_header)) {
+			ath5k_rfkill_set_intr(sc, false);
+		}
+
+		tasklet_kill(&sc->rf_kill.toggleq);
+
+		/* enable RFKILL when stopping HW so Wifi LED is turned off */
+		ath5k_rfkill_enable(sc);
+
+		sc->rf_kill.flags &= ~ATH5K_RFKILL_FLAG_HW_STARTED;
+	}
+}
+
-- 
1.6.3.1

Attachment: signature.asc
Description: This is a digitally signed message part.


[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux