All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] ath5k: reorder base.c to remove fwd decls
@ 2010-09-11 19:06 Bob Copeland
  2010-09-11 19:16 ` [ath5k-devel] " Bob Copeland
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Bob Copeland @ 2010-09-11 19:06 UTC (permalink / raw)
  To: linville, jirislaby, mickflemm, lrodriguez, br1
  Cc: linux-wireless, ath5k-devel, Bob Copeland

This change reorganizes the main ath5k file in order to re-group
related functions and remove most of the forward declarations
(from 61 down to 3).  This is, unfortunately, a lot of churn, but
there should be no functional changes.

Signed-off-by: Bob Copeland <me@bobcopeland.com>
---

Worth the churn?  Is there any way to do this kind of patch that
doesn't suck?

 drivers/net/wireless/ath/ath5k/base.c | 3902 ++++++++++++++++-----------------
 1 files changed, 1884 insertions(+), 2018 deletions(-)

diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index f8c699d..9e4636f 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -70,11 +70,6 @@ static int modparam_all_channels;
 module_param_named(all_channels, modparam_all_channels, bool, S_IRUGO);
 MODULE_PARM_DESC(all_channels, "Expose all channels the device can use.");
 
-
-/******************\
-* Internal defines *
-\******************/
-
 /* Module info */
 MODULE_AUTHOR("Jiri Slaby");
 MODULE_AUTHOR("Nick Kossifidis");
@@ -83,6 +78,10 @@ MODULE_SUPPORTED_DEVICE("Atheros 5xxx WLAN cards");
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_VERSION("0.6.0 (EXPERIMENTAL)");
 
+static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan);
+static int ath5k_beacon_update(struct ieee80211_hw *hw,
+		struct ieee80211_vif *vif);
+static void ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf);
 
 /* Known PCI ids */
 static DEFINE_PCI_DEVICE_TABLE(ath5k_pci_id_table) = {
@@ -190,129 +189,6 @@ static const struct ieee80211_rate ath5k_rates[] = {
 	/* XR missing */
 };
 
-/*
- * Prototypes - PCI stack related functions
- */
-static int __devinit	ath5k_pci_probe(struct pci_dev *pdev,
-				const struct pci_device_id *id);
-static void __devexit	ath5k_pci_remove(struct pci_dev *pdev);
-#ifdef CONFIG_PM_SLEEP
-static int		ath5k_pci_suspend(struct device *dev);
-static int		ath5k_pci_resume(struct device *dev);
-
-static SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend, ath5k_pci_resume);
-#define ATH5K_PM_OPS	(&ath5k_pm_ops)
-#else
-#define ATH5K_PM_OPS	NULL
-#endif /* CONFIG_PM_SLEEP */
-
-static struct pci_driver ath5k_pci_driver = {
-	.name		= KBUILD_MODNAME,
-	.id_table	= ath5k_pci_id_table,
-	.probe		= ath5k_pci_probe,
-	.remove		= __devexit_p(ath5k_pci_remove),
-	.driver.pm	= ATH5K_PM_OPS,
-};
-
-
-
-/*
- * Prototypes - MAC 802.11 stack related functions
- */
-static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
-static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
-		struct ath5k_txq *txq);
-static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan);
-static int ath5k_start(struct ieee80211_hw *hw);
-static void ath5k_stop(struct ieee80211_hw *hw);
-static int ath5k_add_interface(struct ieee80211_hw *hw,
-		struct ieee80211_vif *vif);
-static void ath5k_remove_interface(struct ieee80211_hw *hw,
-		struct ieee80211_vif *vif);
-static int ath5k_config(struct ieee80211_hw *hw, u32 changed);
-static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw,
-				   struct netdev_hw_addr_list *mc_list);
-static void ath5k_configure_filter(struct ieee80211_hw *hw,
-		unsigned int changed_flags,
-		unsigned int *new_flags,
-		u64 multicast);
-static int ath5k_set_key(struct ieee80211_hw *hw,
-		enum set_key_cmd cmd,
-		struct ieee80211_vif *vif, struct ieee80211_sta *sta,
-		struct ieee80211_key_conf *key);
-static int ath5k_get_stats(struct ieee80211_hw *hw,
-		struct ieee80211_low_level_stats *stats);
-static int ath5k_get_survey(struct ieee80211_hw *hw,
-		int idx, struct survey_info *survey);
-static u64 ath5k_get_tsf(struct ieee80211_hw *hw);
-static void ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf);
-static void ath5k_reset_tsf(struct ieee80211_hw *hw);
-static int ath5k_beacon_update(struct ieee80211_hw *hw,
-		struct ieee80211_vif *vif);
-static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
-		struct ieee80211_vif *vif,
-		struct ieee80211_bss_conf *bss_conf,
-		u32 changes);
-static void ath5k_sw_scan_start(struct ieee80211_hw *hw);
-static void ath5k_sw_scan_complete(struct ieee80211_hw *hw);
-static void ath5k_set_coverage_class(struct ieee80211_hw *hw,
-		u8 coverage_class);
-
-static const struct ieee80211_ops ath5k_hw_ops = {
-	.tx 		= ath5k_tx,
-	.start 		= ath5k_start,
-	.stop 		= ath5k_stop,
-	.add_interface 	= ath5k_add_interface,
-	.remove_interface = ath5k_remove_interface,
-	.config 	= ath5k_config,
-	.prepare_multicast = ath5k_prepare_multicast,
-	.configure_filter = ath5k_configure_filter,
-	.set_key 	= ath5k_set_key,
-	.get_stats 	= ath5k_get_stats,
-	.get_survey	= ath5k_get_survey,
-	.conf_tx 	= NULL,
-	.get_tsf 	= ath5k_get_tsf,
-	.set_tsf 	= ath5k_set_tsf,
-	.reset_tsf 	= ath5k_reset_tsf,
-	.bss_info_changed = ath5k_bss_info_changed,
-	.sw_scan_start	= ath5k_sw_scan_start,
-	.sw_scan_complete = ath5k_sw_scan_complete,
-	.set_coverage_class = ath5k_set_coverage_class,
-};
-
-/*
- * Prototypes - Internal functions
- */
-/* Attach detach */
-static int 	ath5k_attach(struct pci_dev *pdev,
-			struct ieee80211_hw *hw);
-static void 	ath5k_detach(struct pci_dev *pdev,
-			struct ieee80211_hw *hw);
-/* Channel/mode setup */
-static inline short ath5k_ieee2mhz(short chan);
-static unsigned int ath5k_copy_channels(struct ath5k_hw *ah,
-				struct ieee80211_channel *channels,
-				unsigned int mode,
-				unsigned int max);
-static int 	ath5k_setup_bands(struct ieee80211_hw *hw);
-static int 	ath5k_chan_set(struct ath5k_softc *sc,
-				struct ieee80211_channel *chan);
-static void	ath5k_setcurmode(struct ath5k_softc *sc,
-				unsigned int mode);
-static void	ath5k_mode_setup(struct ath5k_softc *sc);
-
-/* Descriptor setup */
-static int	ath5k_desc_alloc(struct ath5k_softc *sc,
-				struct pci_dev *pdev);
-static void	ath5k_desc_free(struct ath5k_softc *sc,
-				struct pci_dev *pdev);
-/* Buffers setup */
-static int 	ath5k_rxbuf_setup(struct ath5k_softc *sc,
-				struct ath5k_buf *bf);
-static int 	ath5k_txbuf_setup(struct ath5k_softc *sc,
-				struct ath5k_buf *bf,
-				struct ath5k_txq *txq, int padsize);
-
 static inline void ath5k_txbuf_free_skb(struct ath5k_softc *sc,
 				struct ath5k_buf *bf)
 {
@@ -345,35 +221,6 @@ static inline void ath5k_rxbuf_free_skb(struct ath5k_softc *sc,
 }
 
 
-/* Queues setup */
-static struct 	ath5k_txq *ath5k_txq_setup(struct ath5k_softc *sc,
-				int qtype, int subtype);
-static int 	ath5k_beaconq_setup(struct ath5k_hw *ah);
-static int 	ath5k_beaconq_config(struct ath5k_softc *sc);
-static void 	ath5k_txq_drainq(struct ath5k_softc *sc,
-				struct ath5k_txq *txq);
-static void 	ath5k_txq_cleanup(struct ath5k_softc *sc);
-static void 	ath5k_txq_release(struct ath5k_softc *sc);
-/* Rx handling */
-static int 	ath5k_rx_start(struct ath5k_softc *sc);
-static void 	ath5k_rx_stop(struct ath5k_softc *sc);
-static unsigned int ath5k_rx_decrypted(struct ath5k_softc *sc,
-					struct sk_buff *skb,
-					struct ath5k_rx_status *rs);
-static void 	ath5k_tasklet_rx(unsigned long data);
-/* Tx handling */
-static void 	ath5k_tx_processq(struct ath5k_softc *sc,
-				struct ath5k_txq *txq);
-static void 	ath5k_tasklet_tx(unsigned long data);
-/* Beacon handling */
-static int 	ath5k_beacon_setup(struct ath5k_softc *sc,
-					struct ath5k_buf *bf);
-static void 	ath5k_beacon_send(struct ath5k_softc *sc);
-static void 	ath5k_beacon_config(struct ath5k_softc *sc);
-static void	ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf);
-static void	ath5k_tasklet_beacon(unsigned long data);
-static void	ath5k_tasklet_ani(unsigned long data);
-
 static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
 {
 	u64 tsf = ath5k_hw_get_tsf64(ah);
@@ -384,50 +231,6 @@ static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
 	return (tsf & ~0x7fff) | 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_reset_work(struct work_struct *work);
-
-static void 	ath5k_tasklet_calibrate(unsigned long data);
-
-/*
- * Module init/exit functions
- */
-static int __init
-init_ath5k_pci(void)
-{
-	int ret;
-
-	ath5k_debug_init();
-
-	ret = pci_register_driver(&ath5k_pci_driver);
-	if (ret) {
-		printk(KERN_ERR "ath5k_pci: can't register pci driver\n");
-		return ret;
-	}
-
-	return 0;
-}
-
-static void __exit
-exit_ath5k_pci(void)
-{
-	pci_unregister_driver(&ath5k_pci_driver);
-
-	ath5k_debug_finish();
-}
-
-module_init(init_ath5k_pci);
-module_exit(exit_ath5k_pci);
-
-
-/********************\
-* PCI Initialization *
-\********************/
-
 static const char *
 ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val)
 {
@@ -466,1546 +269,1084 @@ static const struct ath_ops ath5k_common_ops = {
 	.write = ath5k_iowrite32,
 };
 
-static int __devinit
-ath5k_pci_probe(struct pci_dev *pdev,
-		const struct pci_device_id *id)
+/***********************\
+* Driver Initialization *
+\***********************/
+
+static int ath5k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
 {
-	void __iomem *mem;
-	struct ath5k_softc *sc;
-	struct ath_common *common;
-	struct ieee80211_hw *hw;
-	int ret;
-	u8 csz;
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct ath5k_softc *sc = hw->priv;
+	struct ath_regulatory *regulatory = ath5k_hw_regulatory(sc->ah);
 
-	/*
-	 * L0s needs to be disabled on all ath5k cards.
-	 *
-	 * For distributions shipping with CONFIG_PCIEASPM (this will be enabled
-	 * by default in the future in 2.6.36) this will also mean both L1 and
-	 * L0s will be disabled when a pre 1.1 PCIe device is detected. We do
-	 * know L1 works correctly even for all ath5k pre 1.1 PCIe devices
-	 * though but cannot currently undue the effect of a blacklist, for
-	 * details you can read pcie_aspm_sanity_check() and see how it adjusts
-	 * the device link capability.
-	 *
-	 * It may be possible in the future to implement some PCI API to allow
-	 * drivers to override blacklists for pre 1.1 PCIe but for now it is
-	 * best to accept that both L0s and L1 will be disabled completely for
-	 * distributions shipping with CONFIG_PCIEASPM rather than having this
-	 * issue present. Motivation for adding this new API will be to help
-	 * with power consumption for some of these devices.
-	 */
-	pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S);
+	return ath_reg_notifier_apply(wiphy, request, regulatory);
+}
 
-	ret = pci_enable_device(pdev);
-	if (ret) {
-		dev_err(&pdev->dev, "can't enable device\n");
-		goto err;
-	}
+/********************\
+* Channel/mode setup *
+\********************/
 
-	/* XXX 32-bit addressing only */
-	ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
-	if (ret) {
-		dev_err(&pdev->dev, "32-bit DMA not available\n");
-		goto err_dis;
-	}
+/*
+ * Convert IEEE channel number to MHz frequency.
+ */
+static inline short
+ath5k_ieee2mhz(short chan)
+{
+	if (chan <= 14 || chan >= 27)
+		return ieee80211chan2mhz(chan);
+	else
+		return 2212 + chan * 20;
+}
 
-	/*
-	 * Cache line size is used to size and align various
-	 * structures used to communicate with the hardware.
-	 */
-	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz);
-	if (csz == 0) {
-		/*
-		 * Linux 2.4.18 (at least) writes the cache line size
-		 * register as a 16-bit wide register which is wrong.
-		 * We must have this setup properly for rx buffer
-		 * DMA to work so force a reasonable value here if it
-		 * comes up zero.
-		 */
-		csz = L1_CACHE_BYTES >> 2;
-		pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz);
-	}
-	/*
-	 * The default setting of latency timer yields poor results,
-	 * set it to the value used by other systems.  It may be worth
-	 * tweaking this setting more.
-	 */
-	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8);
+/*
+ * Returns true for the channel numbers used without all_channels modparam.
+ */
+static bool ath5k_is_standard_channel(short chan)
+{
+	return ((chan <= 14) ||
+		/* UNII 1,2 */
+		((chan & 3) == 0 && chan >= 36 && chan <= 64) ||
+		/* midband */
+		((chan & 3) == 0 && chan >= 100 && chan <= 140) ||
+		/* UNII-3 */
+		((chan & 3) == 1 && chan >= 149 && chan <= 165));
+}
 
-	/* Enable bus mastering */
-	pci_set_master(pdev);
+static unsigned int
+ath5k_copy_channels(struct ath5k_hw *ah,
+		struct ieee80211_channel *channels,
+		unsigned int mode,
+		unsigned int max)
+{
+	unsigned int i, count, size, chfreq, freq, ch;
 
-	/*
-	 * Disable the RETRY_TIMEOUT register (0x41) to keep
-	 * PCI Tx retries from interfering with C3 CPU state.
-	 */
-	pci_write_config_byte(pdev, 0x41, 0);
+	if (!test_bit(mode, ah->ah_modes))
+		return 0;
 
-	ret = pci_request_region(pdev, 0, "ath5k");
-	if (ret) {
-		dev_err(&pdev->dev, "cannot reserve PCI memory region\n");
-		goto err_dis;
+	switch (mode) {
+	case AR5K_MODE_11A:
+	case AR5K_MODE_11A_TURBO:
+		/* 1..220, but 2GHz frequencies are filtered by check_channel */
+		size = 220 ;
+		chfreq = CHANNEL_5GHZ;
+		break;
+	case AR5K_MODE_11B:
+	case AR5K_MODE_11G:
+	case AR5K_MODE_11G_TURBO:
+		size = 26;
+		chfreq = CHANNEL_2GHZ;
+		break;
+	default:
+		ATH5K_WARN(ah->ah_sc, "bad mode, not copying channels\n");
+		return 0;
 	}
 
-	mem = pci_iomap(pdev, 0, 0);
-	if (!mem) {
-		dev_err(&pdev->dev, "cannot remap PCI memory region\n") ;
-		ret = -EIO;
-		goto err_reg;
-	}
+	for (i = 0, count = 0; i < size && max > 0; i++) {
+		ch = i + 1 ;
+		freq = ath5k_ieee2mhz(ch);
 
-	/*
-	 * Allocate hw (mac80211 main struct)
-	 * and hw->priv (driver private data)
-	 */
-	hw = ieee80211_alloc_hw(sizeof(*sc), &ath5k_hw_ops);
-	if (hw == NULL) {
-		dev_err(&pdev->dev, "cannot allocate ieee80211_hw\n");
-		ret = -ENOMEM;
-		goto err_map;
-	}
-
-	dev_info(&pdev->dev, "registered as '%s'\n", wiphy_name(hw->wiphy));
-
-	/* Initialize driver private data */
-	SET_IEEE80211_DEV(hw, &pdev->dev);
-	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
-		    IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
-		    IEEE80211_HW_SIGNAL_DBM;
+		/* Check if channel is supported by the chipset */
+		if (!ath5k_channel_ok(ah, freq, chfreq))
+			continue;
 
-	hw->wiphy->interface_modes =
-		BIT(NL80211_IFTYPE_AP) |
-		BIT(NL80211_IFTYPE_STATION) |
-		BIT(NL80211_IFTYPE_ADHOC) |
-		BIT(NL80211_IFTYPE_MESH_POINT);
+		if (!modparam_all_channels && !ath5k_is_standard_channel(ch))
+			continue;
 
-	hw->extra_tx_headroom = 2;
-	hw->channel_change_time = 5000;
-	sc = hw->priv;
-	sc->hw = hw;
-	sc->pdev = pdev;
+		/* Write channel info and increment counter */
+		channels[count].center_freq = freq;
+		channels[count].band = (chfreq == CHANNEL_2GHZ) ?
+			IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+		switch (mode) {
+		case AR5K_MODE_11A:
+		case AR5K_MODE_11G:
+			channels[count].hw_value = chfreq | CHANNEL_OFDM;
+			break;
+		case AR5K_MODE_11A_TURBO:
+		case AR5K_MODE_11G_TURBO:
+			channels[count].hw_value = chfreq |
+				CHANNEL_OFDM | CHANNEL_TURBO;
+			break;
+		case AR5K_MODE_11B:
+			channels[count].hw_value = CHANNEL_B;
+		}
 
-	ath5k_debug_init_device(sc);
+		count++;
+		max--;
+	}
 
-	/*
-	 * Mark the device as detached to avoid processing
-	 * interrupts until setup is complete.
-	 */
-	__set_bit(ATH_STAT_INVALID, sc->status);
+	return count;
+}
 
-	sc->iobase = mem; /* So we can unmap it on detach */
-	sc->opmode = NL80211_IFTYPE_STATION;
-	sc->bintval = 1000;
-	mutex_init(&sc->lock);
-	spin_lock_init(&sc->rxbuflock);
-	spin_lock_init(&sc->txbuflock);
-	spin_lock_init(&sc->block);
+static void
+ath5k_setup_rate_idx(struct ath5k_softc *sc, struct ieee80211_supported_band *b)
+{
+	u8 i;
 
-	/* Set private data */
-	pci_set_drvdata(pdev, sc);
+	for (i = 0; i < AR5K_MAX_RATES; i++)
+		sc->rate_idx[b->band][i] = -1;
 
-	/* Setup interrupt handler */
-	ret = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc);
-	if (ret) {
-		ATH5K_ERR(sc, "request_irq failed\n");
-		goto err_free;
+	for (i = 0; i < b->n_bitrates; i++) {
+		sc->rate_idx[b->band][b->bitrates[i].hw_value] = i;
+		if (b->bitrates[i].hw_value_short)
+			sc->rate_idx[b->band][b->bitrates[i].hw_value_short] = i;
 	}
+}
 
-	/* If we passed the test, malloc an ath5k_hw struct */
-	sc->ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL);
-	if (!sc->ah) {
-		ret = -ENOMEM;
-		ATH5K_ERR(sc, "out of memory\n");
-		goto err_irq;
-	}
+static int
+ath5k_setup_bands(struct ieee80211_hw *hw)
+{
+	struct ath5k_softc *sc = hw->priv;
+	struct ath5k_hw *ah = sc->ah;
+	struct ieee80211_supported_band *sband;
+	int max_c, count_c = 0;
+	int i;
 
-	sc->ah->ah_sc = sc;
-	sc->ah->ah_iobase = sc->iobase;
-	common = ath5k_hw_common(sc->ah);
-	common->ops = &ath5k_common_ops;
-	common->ah = sc->ah;
-	common->hw = hw;
-	common->cachelsz = csz << 2; /* convert to bytes */
+	BUILD_BUG_ON(ARRAY_SIZE(sc->sbands) < IEEE80211_NUM_BANDS);
+	max_c = ARRAY_SIZE(sc->channels);
 
-	/* Initialize device */
-	ret = ath5k_hw_attach(sc);
-	if (ret) {
-		goto err_free_ah;
-	}
+	/* 2GHz band */
+	sband = &sc->sbands[IEEE80211_BAND_2GHZ];
+	sband->band = IEEE80211_BAND_2GHZ;
+	sband->bitrates = &sc->rates[IEEE80211_BAND_2GHZ][0];
 
-	/* set up multi-rate retry capabilities */
-	if (sc->ah->ah_version == AR5K_AR5212) {
-		hw->max_rates = 4;
-		hw->max_rate_tries = 11;
-	}
+	if (test_bit(AR5K_MODE_11G, sc->ah->ah_capabilities.cap_mode)) {
+		/* G mode */
+		memcpy(sband->bitrates, &ath5k_rates[0],
+		       sizeof(struct ieee80211_rate) * 12);
+		sband->n_bitrates = 12;
 
-	/* Finish private driver data initialization */
-	ret = ath5k_attach(pdev, hw);
-	if (ret)
-		goto err_ah;
+		sband->channels = sc->channels;
+		sband->n_channels = ath5k_copy_channels(ah, sband->channels,
+					AR5K_MODE_11G, max_c);
 
-	ATH5K_INFO(sc, "Atheros AR%s chip found (MAC: 0x%x, PHY: 0x%x)\n",
-			ath5k_chip_name(AR5K_VERSION_MAC, sc->ah->ah_mac_srev),
-					sc->ah->ah_mac_srev,
-					sc->ah->ah_phy_revision);
+		hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband;
+		count_c = sband->n_channels;
+		max_c -= count_c;
+	} else if (test_bit(AR5K_MODE_11B, sc->ah->ah_capabilities.cap_mode)) {
+		/* B mode */
+		memcpy(sband->bitrates, &ath5k_rates[0],
+		       sizeof(struct ieee80211_rate) * 4);
+		sband->n_bitrates = 4;
 
-	if (!sc->ah->ah_single_chip) {
-		/* Single chip radio (!RF5111) */
-		if (sc->ah->ah_radio_5ghz_revision &&
-			!sc->ah->ah_radio_2ghz_revision) {
-			/* No 5GHz support -> report 2GHz radio */
-			if (!test_bit(AR5K_MODE_11A,
-				sc->ah->ah_capabilities.cap_mode)) {
-				ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n",
-					ath5k_chip_name(AR5K_VERSION_RAD,
-						sc->ah->ah_radio_5ghz_revision),
-						sc->ah->ah_radio_5ghz_revision);
-			/* No 2GHz support (5110 and some
-			 * 5Ghz only cards) -> report 5Ghz radio */
-			} else if (!test_bit(AR5K_MODE_11B,
-				sc->ah->ah_capabilities.cap_mode)) {
-				ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n",
-					ath5k_chip_name(AR5K_VERSION_RAD,
-						sc->ah->ah_radio_5ghz_revision),
-						sc->ah->ah_radio_5ghz_revision);
-			/* Multiband radio */
-			} else {
-				ATH5K_INFO(sc, "RF%s multiband radio found"
-					" (0x%x)\n",
-					ath5k_chip_name(AR5K_VERSION_RAD,
-						sc->ah->ah_radio_5ghz_revision),
-						sc->ah->ah_radio_5ghz_revision);
+		/* 5211 only supports B rates and uses 4bit rate codes
+		 * (e.g normally we have 0x1B for 1M, but on 5211 we have 0x0B)
+		 * fix them up here:
+		 */
+		if (ah->ah_version == AR5K_AR5211) {
+			for (i = 0; i < 4; i++) {
+				sband->bitrates[i].hw_value =
+					sband->bitrates[i].hw_value & 0xF;
+				sband->bitrates[i].hw_value_short =
+					sband->bitrates[i].hw_value_short & 0xF;
 			}
 		}
-		/* Multi chip radio (RF5111 - RF2111) ->
-		 * report both 2GHz/5GHz radios */
-		else if (sc->ah->ah_radio_5ghz_revision &&
-				sc->ah->ah_radio_2ghz_revision){
-			ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n",
-				ath5k_chip_name(AR5K_VERSION_RAD,
-					sc->ah->ah_radio_5ghz_revision),
-					sc->ah->ah_radio_5ghz_revision);
-			ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n",
-				ath5k_chip_name(AR5K_VERSION_RAD,
-					sc->ah->ah_radio_2ghz_revision),
-					sc->ah->ah_radio_2ghz_revision);
-		}
-	}
 
+		sband->channels = sc->channels;
+		sband->n_channels = ath5k_copy_channels(ah, sband->channels,
+					AR5K_MODE_11B, max_c);
 
-	/* ready to process interrupts */
-	__clear_bit(ATH_STAT_INVALID, sc->status);
+		hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband;
+		count_c = sband->n_channels;
+		max_c -= count_c;
+	}
+	ath5k_setup_rate_idx(sc, sband);
 
-	return 0;
-err_ah:
-	ath5k_hw_detach(sc->ah);
-err_free_ah:
-	kfree(sc->ah);
-err_irq:
-	free_irq(pdev->irq, sc);
-err_free:
-	ieee80211_free_hw(hw);
-err_map:
-	pci_iounmap(pdev, mem);
-err_reg:
-	pci_release_region(pdev, 0);
-err_dis:
-	pci_disable_device(pdev);
-err:
-	return ret;
-}
+	/* 5GHz band, A mode */
+	if (test_bit(AR5K_MODE_11A, sc->ah->ah_capabilities.cap_mode)) {
+		sband = &sc->sbands[IEEE80211_BAND_5GHZ];
+		sband->band = IEEE80211_BAND_5GHZ;
+		sband->bitrates = &sc->rates[IEEE80211_BAND_5GHZ][0];
 
-static void __devexit
-ath5k_pci_remove(struct pci_dev *pdev)
-{
-	struct ath5k_softc *sc = pci_get_drvdata(pdev);
+		memcpy(sband->bitrates, &ath5k_rates[4],
+		       sizeof(struct ieee80211_rate) * 8);
+		sband->n_bitrates = 8;
 
-	ath5k_debug_finish_device(sc);
-	ath5k_detach(pdev, sc->hw);
-	ath5k_hw_detach(sc->ah);
-	kfree(sc->ah);
-	free_irq(pdev->irq, sc);
-	pci_iounmap(pdev, sc->iobase);
-	pci_release_region(pdev, 0);
-	pci_disable_device(pdev);
-	ieee80211_free_hw(sc->hw);
-}
+		sband->channels = &sc->channels[count_c];
+		sband->n_channels = ath5k_copy_channels(ah, sband->channels,
+					AR5K_MODE_11A, max_c);
 
-#ifdef CONFIG_PM_SLEEP
-static int ath5k_pci_suspend(struct device *dev)
-{
-	struct ath5k_softc *sc = pci_get_drvdata(to_pci_dev(dev));
+		hw->wiphy->bands[IEEE80211_BAND_5GHZ] = sband;
+	}
+	ath5k_setup_rate_idx(sc, sband);
+
+	ath5k_debug_dump_bands(sc);
 
-	ath5k_led_off(sc);
 	return 0;
 }
 
-static int ath5k_pci_resume(struct device *dev)
-{
-	struct pci_dev *pdev = to_pci_dev(dev);
-	struct ath5k_softc *sc = pci_get_drvdata(pdev);
-
+/*
+ * Set/change channels. We always reset the chip.
+ * To accomplish this we must first cleanup any pending DMA,
+ * then restart stuff after a la  ath5k_init.
+ *
+ * Called with sc->lock.
+ */
+static int
+ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan)
+{
+	ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
+		  "channel set, resetting (%u -> %u MHz)\n",
+		  sc->curchan->center_freq, chan->center_freq);
+
 	/*
-	 * Suspend/Resume resets the PCI configuration space, so we have to
-	 * re-disable the RETRY_TIMEOUT register (0x41) to keep
-	 * PCI Tx retries from interfering with C3 CPU state
+	 * To switch channels clear any pending DMA operations;
+	 * wait long enough for the RX fifo to drain, reset the
+	 * hardware at the new frequency, and then re-enable
+	 * the relevant bits of the h/w.
 	 */
-	pci_write_config_byte(pdev, 0x41, 0);
-
-	ath5k_led_enable(sc);
-	return 0;
+	return ath5k_reset(sc, chan);
 }
-#endif /* CONFIG_PM_SLEEP */
-
-
-/***********************\
-* Driver Initialization *
-\***********************/
 
-static int ath5k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
+static void
+ath5k_setcurmode(struct ath5k_softc *sc, unsigned int mode)
 {
-	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
-	struct ath5k_softc *sc = hw->priv;
-	struct ath_regulatory *regulatory = ath5k_hw_regulatory(sc->ah);
+	sc->curmode = mode;
 
-	return ath_reg_notifier_apply(wiphy, request, regulatory);
+	if (mode == AR5K_MODE_11A) {
+		sc->curband = &sc->sbands[IEEE80211_BAND_5GHZ];
+	} else {
+		sc->curband = &sc->sbands[IEEE80211_BAND_2GHZ];
+	}
 }
 
-static int
-ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
+static void
+ath5k_mode_setup(struct ath5k_softc *sc)
 {
-	struct ath5k_softc *sc = hw->priv;
 	struct ath5k_hw *ah = sc->ah;
-	struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah);
-	u8 mac[ETH_ALEN] = {};
-	int ret;
+	u32 rfilt;
 
-	ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "devid 0x%x\n", pdev->device);
+	/* configure rx filter */
+	rfilt = sc->filter_flags;
+	ath5k_hw_set_rx_filter(ah, rfilt);
 
-	/*
-	 * Check if the MAC has multi-rate retry support.
-	 * We do this by trying to setup a fake extended
-	 * descriptor.  MACs that don't have support will
-	 * return false w/o doing anything.  MACs that do
-	 * support it will return true w/o doing anything.
-	 */
-	ret = ath5k_hw_setup_mrr_tx_desc(ah, NULL, 0, 0, 0, 0, 0, 0);
+	if (ath5k_hw_hasbssidmask(ah))
+		ath5k_hw_set_bssid_mask(ah, sc->bssidmask);
 
-	if (ret < 0)
-		goto err;
-	if (ret > 0)
-		__set_bit(ATH_STAT_MRRETRY, sc->status);
+	/* configure operational mode */
+	ath5k_hw_set_opmode(ah, sc->opmode);
 
-	/*
-	 * Collect the channel list.  The 802.11 layer
-	 * is resposible for filtering this list based
-	 * on settings like the phy mode and regulatory
-	 * domain restrictions.
-	 */
-	ret = ath5k_setup_bands(hw);
-	if (ret) {
-		ATH5K_ERR(sc, "can't get channels\n");
-		goto err;
-	}
+	ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "mode setup opmode %d\n", sc->opmode);
+	ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt);
+}
 
-	/* NB: setup here so ath5k_rate_update is happy */
-	if (test_bit(AR5K_MODE_11A, ah->ah_modes))
-		ath5k_setcurmode(sc, AR5K_MODE_11A);
-	else
-		ath5k_setcurmode(sc, AR5K_MODE_11B);
+static inline int
+ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix)
+{
+	int rix;
 
-	/*
-	 * Allocate tx+rx descriptors and populate the lists.
-	 */
-	ret = ath5k_desc_alloc(sc, pdev);
-	if (ret) {
-		ATH5K_ERR(sc, "can't allocate descriptors\n");
-		goto err;
-	}
+	/* return base rate on errors */
+	if (WARN(hw_rix < 0 || hw_rix >= AR5K_MAX_RATES,
+			"hw_rix out of bounds: %x\n", hw_rix))
+		return 0;
+
+	rix = sc->rate_idx[sc->curband->band][hw_rix];
+	if (WARN(rix < 0, "invalid hw_rix: %x\n", hw_rix))
+		rix = 0;
+
+	return rix;
+}
+
+/***************\
+* Buffers setup *
+\***************/
+
+static
+struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_softc *sc, dma_addr_t *skb_addr)
+{
+	struct ath_common *common = ath5k_hw_common(sc->ah);
+	struct sk_buff *skb;
 
 	/*
-	 * Allocate hardware transmit queues: one queue for
-	 * beacon frames and one data queue for each QoS
-	 * priority.  Note that hw functions handle resetting
-	 * these queues at the needed time.
+	 * Allocate buffer with headroom_needed space for the
+	 * fake physical layer header at the start.
 	 */
-	ret = ath5k_beaconq_setup(ah);
-	if (ret < 0) {
-		ATH5K_ERR(sc, "can't setup a beacon xmit queue\n");
-		goto err_desc;
-	}
-	sc->bhalq = ret;
-	sc->cabq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_CAB, 0);
-	if (IS_ERR(sc->cabq)) {
-		ATH5K_ERR(sc, "can't setup cab queue\n");
-		ret = PTR_ERR(sc->cabq);
-		goto err_bhal;
-	}
+	skb = ath_rxbuf_alloc(common,
+			      common->rx_bufsize,
+			      GFP_ATOMIC);
 
-	sc->txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK);
-	if (IS_ERR(sc->txq)) {
-		ATH5K_ERR(sc, "can't setup xmit queue\n");
-		ret = PTR_ERR(sc->txq);
-		goto err_queues;
+	if (!skb) {
+		ATH5K_ERR(sc, "can't alloc skbuff of size %u\n",
+				common->rx_bufsize);
+		return NULL;
 	}
 
-	tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc);
-	tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc);
-	tasklet_init(&sc->calib, ath5k_tasklet_calibrate, (unsigned long)sc);
-	tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc);
-	tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc);
-
-	INIT_WORK(&sc->reset_work, ath5k_reset_work);
-
-	ret = ath5k_eeprom_read_mac(ah, mac);
-	if (ret) {
-		ATH5K_ERR(sc, "unable to read address from EEPROM: 0x%04x\n",
-			sc->pdev->device);
-		goto err_queues;
+	*skb_addr = pci_map_single(sc->pdev,
+				   skb->data, common->rx_bufsize,
+				   PCI_DMA_FROMDEVICE);
+	if (unlikely(pci_dma_mapping_error(sc->pdev, *skb_addr))) {
+		ATH5K_ERR(sc, "%s: DMA mapping failed\n", __func__);
+		dev_kfree_skb(skb);
+		return NULL;
 	}
+	return skb;
+}
 
-	SET_IEEE80211_PERM_ADDR(hw, mac);
-	/* All MAC address bits matter for ACKs */
-	memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN);
-	ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask);
+static int
+ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
+{
+	struct ath5k_hw *ah = sc->ah;
+	struct sk_buff *skb = bf->skb;
+	struct ath5k_desc *ds;
+	int ret;
 
-	regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain;
-	ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier);
-	if (ret) {
-		ATH5K_ERR(sc, "can't initialize regulatory system\n");
-		goto err_queues;
+	if (!skb) {
+		skb = ath5k_rx_skb_alloc(sc, &bf->skbaddr);
+		if (!skb)
+			return -ENOMEM;
+		bf->skb = skb;
 	}
 
-	ret = ieee80211_register_hw(hw);
+	/*
+	 * Setup descriptors.  For receive we always terminate
+	 * the descriptor list with a self-linked entry so we'll
+	 * not get overrun under high load (as can happen with a
+	 * 5212 when ANI processing enables PHY error frames).
+	 *
+	 * To ensure the last descriptor is self-linked we create
+	 * each descriptor as self-linked and add it to the end.  As
+	 * each additional descriptor is added the previous self-linked
+	 * entry is "fixed" naturally.  This should be safe even
+	 * if DMA is happening.  When processing RX interrupts we
+	 * never remove/process the last, self-linked, entry on the
+	 * descriptor list.  This ensures the hardware always has
+	 * someplace to write a new frame.
+	 */
+	ds = bf->desc;
+	ds->ds_link = bf->daddr;	/* link to self */
+	ds->ds_data = bf->skbaddr;
+	ret = ath5k_hw_setup_rx_desc(ah, ds, ah->common.rx_bufsize, 0);
 	if (ret) {
-		ATH5K_ERR(sc, "can't register ieee80211 hw\n");
-		goto err_queues;
+		ATH5K_ERR(sc, "%s: could not setup RX desc\n", __func__);
+		return ret;
 	}
 
-	if (!ath_is_world_regd(regulatory))
-		regulatory_hint(hw->wiphy, regulatory->alpha2);
-
-	ath5k_init_leds(sc);
-
-	ath5k_sysfs_register(sc);
-
+	if (sc->rxlink != NULL)
+		*sc->rxlink = bf->daddr;
+	sc->rxlink = &ds->ds_link;
 	return 0;
-err_queues:
-	ath5k_txq_release(sc);
-err_bhal:
-	ath5k_hw_release_tx_queue(ah, sc->bhalq);
-err_desc:
-	ath5k_desc_free(sc, pdev);
-err:
-	return ret;
 }
 
-static void
-ath5k_detach(struct pci_dev *pdev, struct ieee80211_hw *hw)
+static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb)
 {
-	struct ath5k_softc *sc = hw->priv;
+	struct ieee80211_hdr *hdr;
+	enum ath5k_pkt_type htype;
+	__le16 fc;
 
-	/*
-	 * NB: the order of these is important:
-	 * o call the 802.11 layer before detaching ath5k_hw to
-	 *   ensure callbacks into the driver to delete global
-	 *   key cache entries can be handled
-	 * o reclaim the tx queue data structures after calling
-	 *   the 802.11 layer as we'll get called back to reclaim
-	 *   node state and potentially want to use them
-	 * o to cleanup the tx queues the hal is called, so detach
-	 *   it last
-	 * XXX: ??? detach ath5k_hw ???
-	 * Other than that, it's straightforward...
-	 */
-	ieee80211_unregister_hw(hw);
-	ath5k_desc_free(sc, pdev);
-	ath5k_txq_release(sc);
-	ath5k_hw_release_tx_queue(sc->ah, sc->bhalq);
-	ath5k_unregister_leds(sc);
-
-	ath5k_sysfs_unregister(sc);
-	/*
-	 * NB: can't reclaim these until after ieee80211_ifdetach
-	 * returns because we'll get called back to reclaim node
-	 * state and potentially want to use them.
-	 */
-}
-
-
-
-
-/********************\
-* Channel/mode setup *
-\********************/
+	hdr = (struct ieee80211_hdr *)skb->data;
+	fc = hdr->frame_control;
 
-/*
- * Convert IEEE channel number to MHz frequency.
- */
-static inline short
-ath5k_ieee2mhz(short chan)
-{
-	if (chan <= 14 || chan >= 27)
-		return ieee80211chan2mhz(chan);
+	if (ieee80211_is_beacon(fc))
+		htype = AR5K_PKT_TYPE_BEACON;
+	else if (ieee80211_is_probe_resp(fc))
+		htype = AR5K_PKT_TYPE_PROBE_RESP;
+	else if (ieee80211_is_atim(fc))
+		htype = AR5K_PKT_TYPE_ATIM;
+	else if (ieee80211_is_pspoll(fc))
+		htype = AR5K_PKT_TYPE_PSPOLL;
 	else
-		return 2212 + chan * 20;
-}
+		htype = AR5K_PKT_TYPE_NORMAL;
 
-/*
- * Returns true for the channel numbers used without all_channels modparam.
- */
-static bool ath5k_is_standard_channel(short chan)
-{
-	return ((chan <= 14) ||
-		/* UNII 1,2 */
-		((chan & 3) == 0 && chan >= 36 && chan <= 64) ||
-		/* midband */
-		((chan & 3) == 0 && chan >= 100 && chan <= 140) ||
-		/* UNII-3 */
-		((chan & 3) == 1 && chan >= 149 && chan <= 165));
+	return htype;
 }
 
-static unsigned int
-ath5k_copy_channels(struct ath5k_hw *ah,
-		struct ieee80211_channel *channels,
-		unsigned int mode,
-		unsigned int max)
+static int
+ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
+		  struct ath5k_txq *txq, int padsize)
 {
-	unsigned int i, count, size, chfreq, freq, ch;
+	struct ath5k_hw *ah = sc->ah;
+	struct ath5k_desc *ds = bf->desc;
+	struct sk_buff *skb = bf->skb;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	unsigned int pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID;
+	struct ieee80211_rate *rate;
+	unsigned int mrr_rate[3], mrr_tries[3];
+	int i, ret;
+	u16 hw_rate;
+	u16 cts_rate = 0;
+	u16 duration = 0;
+	u8 rc_flags;
 
-	if (!test_bit(mode, ah->ah_modes))
-		return 0;
+	flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK;
 
-	switch (mode) {
-	case AR5K_MODE_11A:
-	case AR5K_MODE_11A_TURBO:
-		/* 1..220, but 2GHz frequencies are filtered by check_channel */
-		size = 220 ;
-		chfreq = CHANNEL_5GHZ;
-		break;
-	case AR5K_MODE_11B:
-	case AR5K_MODE_11G:
-	case AR5K_MODE_11G_TURBO:
-		size = 26;
-		chfreq = CHANNEL_2GHZ;
-		break;
-	default:
-		ATH5K_WARN(ah->ah_sc, "bad mode, not copying channels\n");
-		return 0;
+	/* XXX endianness */
+	bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len,
+			PCI_DMA_TODEVICE);
+
+	rate = ieee80211_get_tx_rate(sc->hw, info);
+	if (!rate) {
+		ret = -EINVAL;
+		goto err_unmap;
 	}
 
-	for (i = 0, count = 0; i < size && max > 0; i++) {
-		ch = i + 1 ;
-		freq = ath5k_ieee2mhz(ch);
+	if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+		flags |= AR5K_TXDESC_NOACK;
 
-		/* Check if channel is supported by the chipset */
-		if (!ath5k_channel_ok(ah, freq, chfreq))
-			continue;
+	rc_flags = info->control.rates[0].flags;
+	hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ?
+		rate->hw_value_short : rate->hw_value;
 
-		if (!modparam_all_channels && !ath5k_is_standard_channel(ch))
-			continue;
+	pktlen = skb->len;
 
-		/* Write channel info and increment counter */
-		channels[count].center_freq = freq;
-		channels[count].band = (chfreq == CHANNEL_2GHZ) ?
-			IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
-		switch (mode) {
-		case AR5K_MODE_11A:
-		case AR5K_MODE_11G:
-			channels[count].hw_value = chfreq | CHANNEL_OFDM;
-			break;
-		case AR5K_MODE_11A_TURBO:
-		case AR5K_MODE_11G_TURBO:
-			channels[count].hw_value = chfreq |
-				CHANNEL_OFDM | CHANNEL_TURBO;
+	/* FIXME: If we are in g mode and rate is a CCK rate
+	 * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta
+	 * from tx power (value is in dB units already) */
+	if (info->control.hw_key) {
+		keyidx = info->control.hw_key->hw_key_idx;
+		pktlen += info->control.hw_key->icv_len;
+	}
+	if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
+		flags |= AR5K_TXDESC_RTSENA;
+		cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value;
+		duration = le16_to_cpu(ieee80211_rts_duration(sc->hw,
+			sc->vif, pktlen, info));
+	}
+	if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
+		flags |= AR5K_TXDESC_CTSENA;
+		cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value;
+		duration = le16_to_cpu(ieee80211_ctstoself_duration(sc->hw,
+			sc->vif, pktlen, info));
+	}
+	ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
+		ieee80211_get_hdrlen_from_skb(skb), padsize,
+		get_hw_packet_type(skb),
+		(sc->power_level * 2),
+		hw_rate,
+		info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags,
+		cts_rate, duration);
+	if (ret)
+		goto err_unmap;
+
+	memset(mrr_rate, 0, sizeof(mrr_rate));
+	memset(mrr_tries, 0, sizeof(mrr_tries));
+	for (i = 0; i < 3; i++) {
+		rate = ieee80211_get_alt_retry_rate(sc->hw, info, i);
+		if (!rate)
 			break;
-		case AR5K_MODE_11B:
-			channels[count].hw_value = CHANNEL_B;
-		}
 
-		count++;
-		max--;
+		mrr_rate[i] = rate->hw_value;
+		mrr_tries[i] = info->control.rates[i + 1].count;
 	}
 
-	return count;
-}
+	ath5k_hw_setup_mrr_tx_desc(ah, ds,
+		mrr_rate[0], mrr_tries[0],
+		mrr_rate[1], mrr_tries[1],
+		mrr_rate[2], mrr_tries[2]);
 
-static void
-ath5k_setup_rate_idx(struct ath5k_softc *sc, struct ieee80211_supported_band *b)
-{
-	u8 i;
+	ds->ds_link = 0;
+	ds->ds_data = bf->skbaddr;
 
-	for (i = 0; i < AR5K_MAX_RATES; i++)
-		sc->rate_idx[b->band][i] = -1;
+	spin_lock_bh(&txq->lock);
+	list_add_tail(&bf->list, &txq->q);
+	if (txq->link == NULL) /* is this first packet? */
+		ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr);
+	else /* no, so only link it */
+		*txq->link = bf->daddr;
 
-	for (i = 0; i < b->n_bitrates; i++) {
-		sc->rate_idx[b->band][b->bitrates[i].hw_value] = i;
-		if (b->bitrates[i].hw_value_short)
-			sc->rate_idx[b->band][b->bitrates[i].hw_value_short] = i;
-	}
+	txq->link = &ds->ds_link;
+	ath5k_hw_start_tx_dma(ah, txq->qnum);
+	mmiowb();
+	spin_unlock_bh(&txq->lock);
+
+	return 0;
+err_unmap:
+	pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE);
+	return ret;
 }
 
+/*******************\
+* Descriptors setup *
+\*******************/
+
 static int
-ath5k_setup_bands(struct ieee80211_hw *hw)
+ath5k_desc_alloc(struct ath5k_softc *sc, struct pci_dev *pdev)
 {
-	struct ath5k_softc *sc = hw->priv;
-	struct ath5k_hw *ah = sc->ah;
-	struct ieee80211_supported_band *sband;
-	int max_c, count_c = 0;
-	int i;
+	struct ath5k_desc *ds;
+	struct ath5k_buf *bf;
+	dma_addr_t da;
+	unsigned int i;
+	int ret;
 
-	BUILD_BUG_ON(ARRAY_SIZE(sc->sbands) < IEEE80211_NUM_BANDS);
-	max_c = ARRAY_SIZE(sc->channels);
+	/* allocate descriptors */
+	sc->desc_len = sizeof(struct ath5k_desc) *
+			(ATH_TXBUF + ATH_RXBUF + ATH_BCBUF + 1);
+	sc->desc = pci_alloc_consistent(pdev, sc->desc_len, &sc->desc_daddr);
+	if (sc->desc == NULL) {
+		ATH5K_ERR(sc, "can't allocate descriptors\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+	ds = sc->desc;
+	da = sc->desc_daddr;
+	ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "DMA map: %p (%zu) -> %llx\n",
+		ds, sc->desc_len, (unsigned long long)sc->desc_daddr);
 
-	/* 2GHz band */
-	sband = &sc->sbands[IEEE80211_BAND_2GHZ];
-	sband->band = IEEE80211_BAND_2GHZ;
-	sband->bitrates = &sc->rates[IEEE80211_BAND_2GHZ][0];
+	bf = kcalloc(1 + ATH_TXBUF + ATH_RXBUF + ATH_BCBUF,
+			sizeof(struct ath5k_buf), GFP_KERNEL);
+	if (bf == NULL) {
+		ATH5K_ERR(sc, "can't allocate bufptr\n");
+		ret = -ENOMEM;
+		goto err_free;
+	}
+	sc->bufptr = bf;
 
-	if (test_bit(AR5K_MODE_11G, sc->ah->ah_capabilities.cap_mode)) {
-		/* G mode */
-		memcpy(sband->bitrates, &ath5k_rates[0],
-		       sizeof(struct ieee80211_rate) * 12);
-		sband->n_bitrates = 12;
-
-		sband->channels = sc->channels;
-		sband->n_channels = ath5k_copy_channels(ah, sband->channels,
-					AR5K_MODE_11G, max_c);
-
-		hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband;
-		count_c = sband->n_channels;
-		max_c -= count_c;
-	} else if (test_bit(AR5K_MODE_11B, sc->ah->ah_capabilities.cap_mode)) {
-		/* B mode */
-		memcpy(sband->bitrates, &ath5k_rates[0],
-		       sizeof(struct ieee80211_rate) * 4);
-		sband->n_bitrates = 4;
-
-		/* 5211 only supports B rates and uses 4bit rate codes
-		 * (e.g normally we have 0x1B for 1M, but on 5211 we have 0x0B)
-		 * fix them up here:
-		 */
-		if (ah->ah_version == AR5K_AR5211) {
-			for (i = 0; i < 4; i++) {
-				sband->bitrates[i].hw_value =
-					sband->bitrates[i].hw_value & 0xF;
-				sband->bitrates[i].hw_value_short =
-					sband->bitrates[i].hw_value_short & 0xF;
-			}
-		}
-
-		sband->channels = sc->channels;
-		sband->n_channels = ath5k_copy_channels(ah, sband->channels,
-					AR5K_MODE_11B, max_c);
-
-		hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband;
-		count_c = sband->n_channels;
-		max_c -= count_c;
+	INIT_LIST_HEAD(&sc->rxbuf);
+	for (i = 0; i < ATH_RXBUF; i++, bf++, ds++, da += sizeof(*ds)) {
+		bf->desc = ds;
+		bf->daddr = da;
+		list_add_tail(&bf->list, &sc->rxbuf);
 	}
-	ath5k_setup_rate_idx(sc, sband);
-
-	/* 5GHz band, A mode */
-	if (test_bit(AR5K_MODE_11A, sc->ah->ah_capabilities.cap_mode)) {
-		sband = &sc->sbands[IEEE80211_BAND_5GHZ];
-		sband->band = IEEE80211_BAND_5GHZ;
-		sband->bitrates = &sc->rates[IEEE80211_BAND_5GHZ][0];
-
-		memcpy(sband->bitrates, &ath5k_rates[4],
-		       sizeof(struct ieee80211_rate) * 8);
-		sband->n_bitrates = 8;
-
-		sband->channels = &sc->channels[count_c];
-		sband->n_channels = ath5k_copy_channels(ah, sband->channels,
-					AR5K_MODE_11A, max_c);
 
-		hw->wiphy->bands[IEEE80211_BAND_5GHZ] = sband;
+	INIT_LIST_HEAD(&sc->txbuf);
+	sc->txbuf_len = ATH_TXBUF;
+	for (i = 0; i < ATH_TXBUF; i++, bf++, ds++,
+			da += sizeof(*ds)) {
+		bf->desc = ds;
+		bf->daddr = da;
+		list_add_tail(&bf->list, &sc->txbuf);
 	}
-	ath5k_setup_rate_idx(sc, sband);
 
-	ath5k_debug_dump_bands(sc);
+	/* beacon buffer */
+	bf->desc = ds;
+	bf->daddr = da;
+	sc->bbuf = bf;
 
 	return 0;
-}
-
-/*
- * Set/change channels. We always reset the chip.
- * To accomplish this we must first cleanup any pending DMA,
- * then restart stuff after a la  ath5k_init.
- *
- * Called with sc->lock.
- */
-static int
-ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan)
-{
-	ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
-		  "channel set, resetting (%u -> %u MHz)\n",
-		  sc->curchan->center_freq, chan->center_freq);
-
-	/*
-	 * To switch channels clear any pending DMA operations;
-	 * wait long enough for the RX fifo to drain, reset the
-	 * hardware at the new frequency, and then re-enable
-	 * the relevant bits of the h/w.
-	 */
-	return ath5k_reset(sc, chan);
-}
-
-static void
-ath5k_setcurmode(struct ath5k_softc *sc, unsigned int mode)
-{
-	sc->curmode = mode;
-
-	if (mode == AR5K_MODE_11A) {
-		sc->curband = &sc->sbands[IEEE80211_BAND_5GHZ];
-	} else {
-		sc->curband = &sc->sbands[IEEE80211_BAND_2GHZ];
-	}
+err_free:
+	pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
+err:
+	sc->desc = NULL;
+	return ret;
 }
 
 static void
-ath5k_mode_setup(struct ath5k_softc *sc)
+ath5k_desc_free(struct ath5k_softc *sc, struct pci_dev *pdev)
 {
-	struct ath5k_hw *ah = sc->ah;
-	u32 rfilt;
-
-	/* configure rx filter */
-	rfilt = sc->filter_flags;
-	ath5k_hw_set_rx_filter(ah, rfilt);
+	struct ath5k_buf *bf;
 
-	if (ath5k_hw_hasbssidmask(ah))
-		ath5k_hw_set_bssid_mask(ah, sc->bssidmask);
+	ath5k_txbuf_free_skb(sc, sc->bbuf);
+	list_for_each_entry(bf, &sc->txbuf, list)
+		ath5k_txbuf_free_skb(sc, bf);
+	list_for_each_entry(bf, &sc->rxbuf, list)
+		ath5k_rxbuf_free_skb(sc, bf);
 
-	/* configure operational mode */
-	ath5k_hw_set_opmode(ah, sc->opmode);
+	/* Free memory associated with all descriptors */
+	pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
+	sc->desc = NULL;
+	sc->desc_daddr = 0;
 
-	ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "mode setup opmode %d\n", sc->opmode);
-	ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt);
+	kfree(sc->bufptr);
+	sc->bufptr = NULL;
+	sc->bbuf = NULL;
 }
 
-static inline int
-ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix)
-{
-	int rix;
-
-	/* return base rate on errors */
-	if (WARN(hw_rix < 0 || hw_rix >= AR5K_MAX_RATES,
-			"hw_rix out of bounds: %x\n", hw_rix))
-		return 0;
-
-	rix = sc->rate_idx[sc->curband->band][hw_rix];
-	if (WARN(rix < 0, "invalid hw_rix: %x\n", hw_rix))
-		rix = 0;
-
-	return rix;
-}
 
-/***************\
-* Buffers setup *
-\***************/
+/**************\
+* Queues setup *
+\**************/
 
-static
-struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_softc *sc, dma_addr_t *skb_addr)
+static struct ath5k_txq *
+ath5k_txq_setup(struct ath5k_softc *sc,
+		int qtype, int subtype)
 {
-	struct ath_common *common = ath5k_hw_common(sc->ah);
-	struct sk_buff *skb;
+	struct ath5k_hw *ah = sc->ah;
+	struct ath5k_txq *txq;
+	struct ath5k_txq_info qi = {
+		.tqi_subtype = subtype,
+		.tqi_aifs = AR5K_TXQ_USEDEFAULT,
+		.tqi_cw_min = AR5K_TXQ_USEDEFAULT,
+		.tqi_cw_max = AR5K_TXQ_USEDEFAULT
+	};
+	int qnum;
 
 	/*
-	 * Allocate buffer with headroom_needed space for the
-	 * fake physical layer header at the start.
+	 * Enable interrupts only for EOL and DESC conditions.
+	 * We mark tx descriptors to receive a DESC interrupt
+	 * when a tx queue gets deep; otherwise we wait for the
+	 * EOL to reap descriptors.  Note that this is done to
+	 * reduce interrupt load and this only defers reaping
+	 * descriptors, never transmitting frames.  Aside from
+	 * reducing interrupts this also permits more concurrency.
+	 * The only potential downside is if the tx queue backs
+	 * up in which case the top half of the kernel may backup
+	 * due to a lack of tx descriptors.
 	 */
-	skb = ath_rxbuf_alloc(common,
-			      common->rx_bufsize,
-			      GFP_ATOMIC);
-
-	if (!skb) {
-		ATH5K_ERR(sc, "can't alloc skbuff of size %u\n",
-				common->rx_bufsize);
-		return NULL;
+	qi.tqi_flags = AR5K_TXQ_FLAG_TXEOLINT_ENABLE |
+				AR5K_TXQ_FLAG_TXDESCINT_ENABLE;
+	qnum = ath5k_hw_setup_tx_queue(ah, qtype, &qi);
+	if (qnum < 0) {
+		/*
+		 * NB: don't print a message, this happens
+		 * normally on parts with too few tx queues
+		 */
+		return ERR_PTR(qnum);
 	}
-
-	*skb_addr = pci_map_single(sc->pdev,
-				   skb->data, common->rx_bufsize,
-				   PCI_DMA_FROMDEVICE);
-	if (unlikely(pci_dma_mapping_error(sc->pdev, *skb_addr))) {
-		ATH5K_ERR(sc, "%s: DMA mapping failed\n", __func__);
-		dev_kfree_skb(skb);
-		return NULL;
+	if (qnum >= ARRAY_SIZE(sc->txqs)) {
+		ATH5K_ERR(sc, "hw qnum %u out of range, max %tu!\n",
+			qnum, ARRAY_SIZE(sc->txqs));
+		ath5k_hw_release_tx_queue(ah, qnum);
+		return ERR_PTR(-EINVAL);
 	}
-	return skb;
+	txq = &sc->txqs[qnum];
+	if (!txq->setup) {
+		txq->qnum = qnum;
+		txq->link = NULL;
+		INIT_LIST_HEAD(&txq->q);
+		spin_lock_init(&txq->lock);
+		txq->setup = true;
+	}
+	return &sc->txqs[qnum];
 }
 
 static int
-ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
+ath5k_beaconq_setup(struct ath5k_hw *ah)
+{
+	struct ath5k_txq_info qi = {
+		.tqi_aifs = AR5K_TXQ_USEDEFAULT,
+		.tqi_cw_min = AR5K_TXQ_USEDEFAULT,
+		.tqi_cw_max = AR5K_TXQ_USEDEFAULT,
+		/* NB: for dynamic turbo, don't enable any other interrupts */
+		.tqi_flags = AR5K_TXQ_FLAG_TXDESCINT_ENABLE
+	};
+
+	return ath5k_hw_setup_tx_queue(ah, AR5K_TX_QUEUE_BEACON, &qi);
+}
+
+static int
+ath5k_beaconq_config(struct ath5k_softc *sc)
 {
 	struct ath5k_hw *ah = sc->ah;
-	struct sk_buff *skb = bf->skb;
-	struct ath5k_desc *ds;
+	struct ath5k_txq_info qi;
 	int ret;
 
-	if (!skb) {
-		skb = ath5k_rx_skb_alloc(sc, &bf->skbaddr);
-		if (!skb)
-			return -ENOMEM;
-		bf->skb = skb;
-	}
+	ret = ath5k_hw_get_tx_queueprops(ah, sc->bhalq, &qi);
+	if (ret)
+		goto err;
 
-	/*
-	 * Setup descriptors.  For receive we always terminate
-	 * the descriptor list with a self-linked entry so we'll
-	 * not get overrun under high load (as can happen with a
-	 * 5212 when ANI processing enables PHY error frames).
-	 *
-	 * To ensure the last descriptor is self-linked we create
-	 * each descriptor as self-linked and add it to the end.  As
-	 * each additional descriptor is added the previous self-linked
-	 * entry is "fixed" naturally.  This should be safe even
-	 * if DMA is happening.  When processing RX interrupts we
-	 * never remove/process the last, self-linked, entry on the
-	 * descriptor list.  This ensures the hardware always has
-	 * someplace to write a new frame.
-	 */
-	ds = bf->desc;
-	ds->ds_link = bf->daddr;	/* link to self */
-	ds->ds_data = bf->skbaddr;
-	ret = ath5k_hw_setup_rx_desc(ah, ds, ah->common.rx_bufsize, 0);
+	if (sc->opmode == NL80211_IFTYPE_AP ||
+		sc->opmode == NL80211_IFTYPE_MESH_POINT) {
+		/*
+		 * Always burst out beacon and CAB traffic
+		 * (aifs = cwmin = cwmax = 0)
+		 */
+		qi.tqi_aifs = 0;
+		qi.tqi_cw_min = 0;
+		qi.tqi_cw_max = 0;
+	} else if (sc->opmode == NL80211_IFTYPE_ADHOC) {
+		/*
+		 * Adhoc mode; backoff between 0 and (2 * cw_min).
+		 */
+		qi.tqi_aifs = 0;
+		qi.tqi_cw_min = 0;
+		qi.tqi_cw_max = 2 * ah->ah_cw_min;
+	}
+
+	ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
+		"beacon queueprops tqi_aifs:%d tqi_cw_min:%d tqi_cw_max:%d\n",
+		qi.tqi_aifs, qi.tqi_cw_min, qi.tqi_cw_max);
+
+	ret = ath5k_hw_set_tx_queueprops(ah, sc->bhalq, &qi);
 	if (ret) {
-		ATH5K_ERR(sc, "%s: could not setup RX desc\n", __func__);
-		return ret;
+		ATH5K_ERR(sc, "%s: unable to update parameters for beacon "
+			"hardware queue!\n", __func__);
+		goto err;
 	}
+	ret = ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */
+	if (ret)
+		goto err;
 
-	if (sc->rxlink != NULL)
-		*sc->rxlink = bf->daddr;
-	sc->rxlink = &ds->ds_link;
-	return 0;
+	/* reconfigure cabq with ready time to 80% of beacon_interval */
+	ret = ath5k_hw_get_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);
+	if (ret)
+		goto err;
+
+	qi.tqi_ready_time = (sc->bintval * 80) / 100;
+	ret = ath5k_hw_set_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);
+	if (ret)
+		goto err;
+
+	ret = ath5k_hw_reset_tx_queue(ah, AR5K_TX_QUEUE_ID_CAB);
+err:
+	return ret;
 }
 
-static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb)
+static void
+ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq)
 {
-	struct ieee80211_hdr *hdr;
-	enum ath5k_pkt_type htype;
-	__le16 fc;
+	struct ath5k_buf *bf, *bf0;
 
-	hdr = (struct ieee80211_hdr *)skb->data;
-	fc = hdr->frame_control;
+	/*
+	 * NB: this assumes output has been stopped and
+	 *     we do not need to block ath5k_tx_tasklet
+	 */
+	spin_lock_bh(&txq->lock);
+	list_for_each_entry_safe(bf, bf0, &txq->q, list) {
+		ath5k_debug_printtxbuf(sc, bf);
 
-	if (ieee80211_is_beacon(fc))
-		htype = AR5K_PKT_TYPE_BEACON;
-	else if (ieee80211_is_probe_resp(fc))
-		htype = AR5K_PKT_TYPE_PROBE_RESP;
-	else if (ieee80211_is_atim(fc))
-		htype = AR5K_PKT_TYPE_ATIM;
-	else if (ieee80211_is_pspoll(fc))
-		htype = AR5K_PKT_TYPE_PSPOLL;
-	else
-		htype = AR5K_PKT_TYPE_NORMAL;
+		ath5k_txbuf_free_skb(sc, bf);
 
-	return htype;
+		spin_lock_bh(&sc->txbuflock);
+		list_move_tail(&bf->list, &sc->txbuf);
+		sc->txbuf_len++;
+		spin_unlock_bh(&sc->txbuflock);
+	}
+	txq->link = NULL;
+	spin_unlock_bh(&txq->lock);
 }
 
-static int
-ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
-		  struct ath5k_txq *txq, int padsize)
+/*
+ * Drain the transmit queues and reclaim resources.
+ */
+static void
+ath5k_txq_cleanup(struct ath5k_softc *sc)
 {
 	struct ath5k_hw *ah = sc->ah;
-	struct ath5k_desc *ds = bf->desc;
-	struct sk_buff *skb = bf->skb;
-	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-	unsigned int pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID;
-	struct ieee80211_rate *rate;
-	unsigned int mrr_rate[3], mrr_tries[3];
-	int i, ret;
-	u16 hw_rate;
-	u16 cts_rate = 0;
-	u16 duration = 0;
-	u8 rc_flags;
-
-	flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK;
-
-	/* XXX endianness */
-	bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len,
-			PCI_DMA_TODEVICE);
+	unsigned int i;
 
-	rate = ieee80211_get_tx_rate(sc->hw, info);
-	if (!rate) {
-		ret = -EINVAL;
-		goto err_unmap;
+	/* XXX return value */
+	if (likely(!test_bit(ATH_STAT_INVALID, sc->status))) {
+		/* don't touch the hardware if marked invalid */
+		ath5k_hw_stop_tx_dma(ah, sc->bhalq);
+		ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "beacon queue %x\n",
+			ath5k_hw_get_txdp(ah, sc->bhalq));
+		for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
+			if (sc->txqs[i].setup) {
+				ath5k_hw_stop_tx_dma(ah, sc->txqs[i].qnum);
+				ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "txq [%u] %x, "
+					"link %p\n",
+					sc->txqs[i].qnum,
+					ath5k_hw_get_txdp(ah,
+							sc->txqs[i].qnum),
+					sc->txqs[i].link);
+			}
 	}
 
-	if (info->flags & IEEE80211_TX_CTL_NO_ACK)
-		flags |= AR5K_TXDESC_NOACK;
+	for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
+		if (sc->txqs[i].setup)
+			ath5k_txq_drainq(sc, &sc->txqs[i]);
+}
 
-	rc_flags = info->control.rates[0].flags;
-	hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ?
-		rate->hw_value_short : rate->hw_value;
+static void
+ath5k_txq_release(struct ath5k_softc *sc)
+{
+	struct ath5k_txq *txq = sc->txqs;
+	unsigned int i;
 
-	pktlen = skb->len;
+	for (i = 0; i < ARRAY_SIZE(sc->txqs); i++, txq++)
+		if (txq->setup) {
+			ath5k_hw_release_tx_queue(sc->ah, txq->qnum);
+			txq->setup = false;
+		}
+}
 
-	/* FIXME: If we are in g mode and rate is a CCK rate
-	 * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta
-	 * from tx power (value is in dB units already) */
-	if (info->control.hw_key) {
-		keyidx = info->control.hw_key->hw_key_idx;
-		pktlen += info->control.hw_key->icv_len;
-	}
-	if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
-		flags |= AR5K_TXDESC_RTSENA;
-		cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value;
-		duration = le16_to_cpu(ieee80211_rts_duration(sc->hw,
-			sc->vif, pktlen, info));
-	}
-	if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
-		flags |= AR5K_TXDESC_CTSENA;
-		cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value;
-		duration = le16_to_cpu(ieee80211_ctstoself_duration(sc->hw,
-			sc->vif, pktlen, info));
-	}
-	ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
-		ieee80211_get_hdrlen_from_skb(skb), padsize,
-		get_hw_packet_type(skb),
-		(sc->power_level * 2),
-		hw_rate,
-		info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags,
-		cts_rate, duration);
-	if (ret)
-		goto err_unmap;
 
-	memset(mrr_rate, 0, sizeof(mrr_rate));
-	memset(mrr_tries, 0, sizeof(mrr_tries));
-	for (i = 0; i < 3; i++) {
-		rate = ieee80211_get_alt_retry_rate(sc->hw, info, i);
-		if (!rate)
-			break;
+/*************\
+* RX Handling *
+\*************/
 
-		mrr_rate[i] = rate->hw_value;
-		mrr_tries[i] = info->control.rates[i + 1].count;
-	}
+/*
+ * Enable the receive h/w following a reset.
+ */
+static int
+ath5k_rx_start(struct ath5k_softc *sc)
+{
+	struct ath5k_hw *ah = sc->ah;
+	struct ath_common *common = ath5k_hw_common(ah);
+	struct ath5k_buf *bf;
+	int ret;
 
-	ath5k_hw_setup_mrr_tx_desc(ah, ds,
-		mrr_rate[0], mrr_tries[0],
-		mrr_rate[1], mrr_tries[1],
-		mrr_rate[2], mrr_tries[2]);
+	common->rx_bufsize = roundup(IEEE80211_MAX_FRAME_LEN, common->cachelsz);
 
-	ds->ds_link = 0;
-	ds->ds_data = bf->skbaddr;
+	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n",
+		  common->cachelsz, common->rx_bufsize);
 
-	spin_lock_bh(&txq->lock);
-	list_add_tail(&bf->list, &txq->q);
-	if (txq->link == NULL) /* is this first packet? */
-		ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr);
-	else /* no, so only link it */
-		*txq->link = bf->daddr;
+	spin_lock_bh(&sc->rxbuflock);
+	sc->rxlink = NULL;
+	list_for_each_entry(bf, &sc->rxbuf, list) {
+		ret = ath5k_rxbuf_setup(sc, bf);
+		if (ret != 0) {
+			spin_unlock_bh(&sc->rxbuflock);
+			goto err;
+		}
+	}
+	bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list);
+	ath5k_hw_set_rxdp(ah, bf->daddr);
+	spin_unlock_bh(&sc->rxbuflock);
 
-	txq->link = &ds->ds_link;
-	ath5k_hw_start_tx_dma(ah, txq->qnum);
-	mmiowb();
-	spin_unlock_bh(&txq->lock);
+	ath5k_hw_start_rx_dma(ah);	/* enable recv descriptors */
+	ath5k_mode_setup(sc);		/* set filters, etc. */
+	ath5k_hw_start_rx_pcu(ah);	/* re-enable PCU/DMA engine */
 
 	return 0;
-err_unmap:
-	pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE);
+err:
 	return ret;
 }
 
-/*******************\
-* Descriptors setup *
-\*******************/
-
-static int
-ath5k_desc_alloc(struct ath5k_softc *sc, struct pci_dev *pdev)
+/*
+ * Disable the receive h/w in preparation for a reset.
+ */
+static void
+ath5k_rx_stop(struct ath5k_softc *sc)
 {
-	struct ath5k_desc *ds;
-	struct ath5k_buf *bf;
-	dma_addr_t da;
-	unsigned int i;
-	int ret;
+	struct ath5k_hw *ah = sc->ah;
 
-	/* allocate descriptors */
-	sc->desc_len = sizeof(struct ath5k_desc) *
-			(ATH_TXBUF + ATH_RXBUF + ATH_BCBUF + 1);
-	sc->desc = pci_alloc_consistent(pdev, sc->desc_len, &sc->desc_daddr);
-	if (sc->desc == NULL) {
-		ATH5K_ERR(sc, "can't allocate descriptors\n");
-		ret = -ENOMEM;
-		goto err;
-	}
-	ds = sc->desc;
-	da = sc->desc_daddr;
-	ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "DMA map: %p (%zu) -> %llx\n",
-		ds, sc->desc_len, (unsigned long long)sc->desc_daddr);
+	ath5k_hw_stop_rx_pcu(ah);	/* disable PCU */
+	ath5k_hw_set_rx_filter(ah, 0);	/* clear recv filter */
+	ath5k_hw_stop_rx_dma(ah);	/* disable DMA engine */
 
-	bf = kcalloc(1 + ATH_TXBUF + ATH_RXBUF + ATH_BCBUF,
-			sizeof(struct ath5k_buf), GFP_KERNEL);
-	if (bf == NULL) {
-		ATH5K_ERR(sc, "can't allocate bufptr\n");
-		ret = -ENOMEM;
-		goto err_free;
-	}
-	sc->bufptr = bf;
+	ath5k_debug_printrxbuffs(sc, ah);
+}
 
-	INIT_LIST_HEAD(&sc->rxbuf);
-	for (i = 0; i < ATH_RXBUF; i++, bf++, ds++, da += sizeof(*ds)) {
-		bf->desc = ds;
-		bf->daddr = da;
-		list_add_tail(&bf->list, &sc->rxbuf);
-	}
+static unsigned int
+ath5k_rx_decrypted(struct ath5k_softc *sc, struct sk_buff *skb,
+		   struct ath5k_rx_status *rs)
+{
+	struct ath5k_hw *ah = sc->ah;
+	struct ath_common *common = ath5k_hw_common(ah);
+	struct ieee80211_hdr *hdr = (void *)skb->data;
+	unsigned int keyix, hlen;
 
-	INIT_LIST_HEAD(&sc->txbuf);
-	sc->txbuf_len = ATH_TXBUF;
-	for (i = 0; i < ATH_TXBUF; i++, bf++, ds++,
-			da += sizeof(*ds)) {
-		bf->desc = ds;
-		bf->daddr = da;
-		list_add_tail(&bf->list, &sc->txbuf);
-	}
+	if (!(rs->rs_status & AR5K_RXERR_DECRYPT) &&
+			rs->rs_keyix != AR5K_RXKEYIX_INVALID)
+		return RX_FLAG_DECRYPTED;
 
-	/* beacon buffer */
-	bf->desc = ds;
-	bf->daddr = da;
-	sc->bbuf = bf;
+	/* Apparently when a default key is used to decrypt the packet
+	   the hw does not set the index used to decrypt.  In such cases
+	   get the index from the packet. */
+	hlen = ieee80211_hdrlen(hdr->frame_control);
+	if (ieee80211_has_protected(hdr->frame_control) &&
+	    !(rs->rs_status & AR5K_RXERR_DECRYPT) &&
+	    skb->len >= hlen + 4) {
+		keyix = skb->data[hlen + 3] >> 6;
+
+		if (test_bit(keyix, common->keymap))
+			return RX_FLAG_DECRYPTED;
+	}
 
 	return 0;
-err_free:
-	pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
-err:
-	sc->desc = NULL;
-	return ret;
 }
 
+
 static void
-ath5k_desc_free(struct ath5k_softc *sc, struct pci_dev *pdev)
+ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb,
+		     struct ieee80211_rx_status *rxs)
 {
-	struct ath5k_buf *bf;
+	struct ath_common *common = ath5k_hw_common(sc->ah);
+	u64 tsf, bc_tstamp;
+	u32 hw_tu;
+	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
 
-	ath5k_txbuf_free_skb(sc, sc->bbuf);
-	list_for_each_entry(bf, &sc->txbuf, list)
-		ath5k_txbuf_free_skb(sc, bf);
-	list_for_each_entry(bf, &sc->rxbuf, list)
-		ath5k_rxbuf_free_skb(sc, bf);
+	if (ieee80211_is_beacon(mgmt->frame_control) &&
+	    le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS &&
+	    memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) == 0) {
+		/*
+		 * Received an IBSS beacon with the same BSSID. Hardware *must*
+		 * have updated the local TSF. We have to work around various
+		 * hardware bugs, though...
+		 */
+		tsf = ath5k_hw_get_tsf64(sc->ah);
+		bc_tstamp = le64_to_cpu(mgmt->u.beacon.timestamp);
+		hw_tu = TSF_TO_TU(tsf);
 
-	/* Free memory associated with all descriptors */
-	pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
-	sc->desc = NULL;
-	sc->desc_daddr = 0;
+		ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+			"beacon %llx mactime %llx (diff %lld) tsf now %llx\n",
+			(unsigned long long)bc_tstamp,
+			(unsigned long long)rxs->mactime,
+			(unsigned long long)(rxs->mactime - bc_tstamp),
+			(unsigned long long)tsf);
 
-	kfree(sc->bufptr);
-	sc->bufptr = NULL;
-	sc->bbuf = NULL;
-}
+		/*
+		 * Sometimes the HW will give us a wrong tstamp in the rx
+		 * status, causing the timestamp extension to go wrong.
+		 * (This seems to happen especially with beacon frames bigger
+		 * than 78 byte (incl. FCS))
+		 * But we know that the receive timestamp must be later than the
+		 * timestamp of the beacon since HW must have synced to that.
+		 *
+		 * NOTE: here we assume mactime to be after the frame was
+		 * received, not like mac80211 which defines it at the start.
+		 */
+		if (bc_tstamp > rxs->mactime) {
+			ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+				"fixing mactime from %llx to %llx\n",
+				(unsigned long long)rxs->mactime,
+				(unsigned long long)tsf);
+			rxs->mactime = tsf;
+		}
 
+		/*
+		 * Local TSF might have moved higher than our beacon timers,
+		 * in that case we have to update them to continue sending
+		 * beacons. This also takes care of synchronizing beacon sending
+		 * times with other stations.
+		 */
+		if (hw_tu >= sc->nexttbtt)
+			ath5k_beacon_update_timers(sc, bc_tstamp);
+	}
+}
 
+static void
+ath5k_update_beacon_rssi(struct ath5k_softc *sc, struct sk_buff *skb, int rssi)
+{
+	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+	struct ath5k_hw *ah = sc->ah;
+	struct ath_common *common = ath5k_hw_common(ah);
 
+	/* only beacons from our BSSID */
+	if (!ieee80211_is_beacon(mgmt->frame_control) ||
+	    memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) != 0)
+		return;
 
+	ah->ah_beacon_rssi_avg = ath5k_moving_average(ah->ah_beacon_rssi_avg,
+						      rssi);
 
-/**************\
-* Queues setup *
-\**************/
+	/* in IBSS mode we should keep RSSI statistics per neighbour */
+	/* le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS */
+}
 
-static struct ath5k_txq *
-ath5k_txq_setup(struct ath5k_softc *sc,
-		int qtype, int subtype)
+/*
+ * Compute padding position. skb must contain an IEEE 802.11 frame
+ */
+static int ath5k_common_padpos(struct sk_buff *skb)
 {
-	struct ath5k_hw *ah = sc->ah;
-	struct ath5k_txq *txq;
-	struct ath5k_txq_info qi = {
-		.tqi_subtype = subtype,
-		.tqi_aifs = AR5K_TXQ_USEDEFAULT,
-		.tqi_cw_min = AR5K_TXQ_USEDEFAULT,
-		.tqi_cw_max = AR5K_TXQ_USEDEFAULT
-	};
-	int qnum;
+	struct ieee80211_hdr * hdr = (struct ieee80211_hdr *)skb->data;
+	__le16 frame_control = hdr->frame_control;
+	int padpos = 24;
 
-	/*
-	 * Enable interrupts only for EOL and DESC conditions.
-	 * We mark tx descriptors to receive a DESC interrupt
-	 * when a tx queue gets deep; otherwise we wait for the
-	 * EOL to reap descriptors.  Note that this is done to
-	 * reduce interrupt load and this only defers reaping
-	 * descriptors, never transmitting frames.  Aside from
-	 * reducing interrupts this also permits more concurrency.
-	 * The only potential downside is if the tx queue backs
-	 * up in which case the top half of the kernel may backup
-	 * due to a lack of tx descriptors.
-	 */
-	qi.tqi_flags = AR5K_TXQ_FLAG_TXEOLINT_ENABLE |
-				AR5K_TXQ_FLAG_TXDESCINT_ENABLE;
-	qnum = ath5k_hw_setup_tx_queue(ah, qtype, &qi);
-	if (qnum < 0) {
-		/*
-		 * NB: don't print a message, this happens
-		 * normally on parts with too few tx queues
-		 */
-		return ERR_PTR(qnum);
-	}
-	if (qnum >= ARRAY_SIZE(sc->txqs)) {
-		ATH5K_ERR(sc, "hw qnum %u out of range, max %tu!\n",
-			qnum, ARRAY_SIZE(sc->txqs));
-		ath5k_hw_release_tx_queue(ah, qnum);
-		return ERR_PTR(-EINVAL);
+	if (ieee80211_has_a4(frame_control)) {
+		padpos += ETH_ALEN;
 	}
-	txq = &sc->txqs[qnum];
-	if (!txq->setup) {
-		txq->qnum = qnum;
-		txq->link = NULL;
-		INIT_LIST_HEAD(&txq->q);
-		spin_lock_init(&txq->lock);
-		txq->setup = true;
+	if (ieee80211_is_data_qos(frame_control)) {
+		padpos += IEEE80211_QOS_CTL_LEN;
 	}
-	return &sc->txqs[qnum];
-}
-
-static int
-ath5k_beaconq_setup(struct ath5k_hw *ah)
-{
-	struct ath5k_txq_info qi = {
-		.tqi_aifs = AR5K_TXQ_USEDEFAULT,
-		.tqi_cw_min = AR5K_TXQ_USEDEFAULT,
-		.tqi_cw_max = AR5K_TXQ_USEDEFAULT,
-		/* NB: for dynamic turbo, don't enable any other interrupts */
-		.tqi_flags = AR5K_TXQ_FLAG_TXDESCINT_ENABLE
-	};
 
-	return ath5k_hw_setup_tx_queue(ah, AR5K_TX_QUEUE_BEACON, &qi);
+	return padpos;
 }
 
-static int
-ath5k_beaconq_config(struct ath5k_softc *sc)
+/*
+ * This function expects an 802.11 frame and returns the number of
+ * bytes added, or -1 if we don't have enough header room.
+ */
+static int ath5k_add_padding(struct sk_buff *skb)
 {
-	struct ath5k_hw *ah = sc->ah;
-	struct ath5k_txq_info qi;
-	int ret;
+	int padpos = ath5k_common_padpos(skb);
+	int padsize = padpos & 3;
 
-	ret = ath5k_hw_get_tx_queueprops(ah, sc->bhalq, &qi);
-	if (ret)
-		goto err;
-
-	if (sc->opmode == NL80211_IFTYPE_AP ||
-		sc->opmode == NL80211_IFTYPE_MESH_POINT) {
-		/*
-		 * Always burst out beacon and CAB traffic
-		 * (aifs = cwmin = cwmax = 0)
-		 */
-		qi.tqi_aifs = 0;
-		qi.tqi_cw_min = 0;
-		qi.tqi_cw_max = 0;
-	} else if (sc->opmode == NL80211_IFTYPE_ADHOC) {
-		/*
-		 * Adhoc mode; backoff between 0 and (2 * cw_min).
-		 */
-		qi.tqi_aifs = 0;
-		qi.tqi_cw_min = 0;
-		qi.tqi_cw_max = 2 * ah->ah_cw_min;
-	}
+	if (padsize && skb->len>padpos) {
 
-	ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
-		"beacon queueprops tqi_aifs:%d tqi_cw_min:%d tqi_cw_max:%d\n",
-		qi.tqi_aifs, qi.tqi_cw_min, qi.tqi_cw_max);
+		if (skb_headroom(skb) < padsize)
+			return -1;
 
-	ret = ath5k_hw_set_tx_queueprops(ah, sc->bhalq, &qi);
-	if (ret) {
-		ATH5K_ERR(sc, "%s: unable to update parameters for beacon "
-			"hardware queue!\n", __func__);
-		goto err;
+		skb_push(skb, padsize);
+		memmove(skb->data, skb->data+padsize, padpos);
+		return padsize;
 	}
-	ret = ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */
-	if (ret)
-		goto err;
-
-	/* reconfigure cabq with ready time to 80% of beacon_interval */
-	ret = ath5k_hw_get_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);
-	if (ret)
-		goto err;
-
-	qi.tqi_ready_time = (sc->bintval * 80) / 100;
-	ret = ath5k_hw_set_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);
-	if (ret)
-		goto err;
-
-	ret = ath5k_hw_reset_tx_queue(ah, AR5K_TX_QUEUE_ID_CAB);
-err:
-	return ret;
-}
-
-static void
-ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq)
-{
-	struct ath5k_buf *bf, *bf0;
-
-	/*
-	 * NB: this assumes output has been stopped and
-	 *     we do not need to block ath5k_tx_tasklet
-	 */
-	spin_lock_bh(&txq->lock);
-	list_for_each_entry_safe(bf, bf0, &txq->q, list) {
-		ath5k_debug_printtxbuf(sc, bf);
-
-		ath5k_txbuf_free_skb(sc, bf);
 
-		spin_lock_bh(&sc->txbuflock);
-		list_move_tail(&bf->list, &sc->txbuf);
-		sc->txbuf_len++;
-		spin_unlock_bh(&sc->txbuflock);
-	}
-	txq->link = NULL;
-	spin_unlock_bh(&txq->lock);
+	return 0;
 }
 
 /*
- * Drain the transmit queues and reclaim resources.
+ * The MAC header is padded to have 32-bit boundary if the
+ * packet payload is non-zero. The general calculation for
+ * padsize would take into account odd header lengths:
+ * padsize = 4 - (hdrlen & 3); however, since only
+ * even-length headers are used, padding can only be 0 or 2
+ * bytes and we can optimize this a bit.  We must not try to
+ * remove padding from short control frames that do not have a
+ * payload.
+ *
+ * This function expects an 802.11 frame and returns the number of
+ * bytes removed.
  */
-static void
-ath5k_txq_cleanup(struct ath5k_softc *sc)
+static int ath5k_remove_padding(struct sk_buff *skb)
 {
-	struct ath5k_hw *ah = sc->ah;
-	unsigned int i;
+	int padpos = ath5k_common_padpos(skb);
+	int padsize = padpos & 3;
 
-	/* XXX return value */
-	if (likely(!test_bit(ATH_STAT_INVALID, sc->status))) {
-		/* don't touch the hardware if marked invalid */
-		ath5k_hw_stop_tx_dma(ah, sc->bhalq);
-		ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "beacon queue %x\n",
-			ath5k_hw_get_txdp(ah, sc->bhalq));
-		for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
-			if (sc->txqs[i].setup) {
-				ath5k_hw_stop_tx_dma(ah, sc->txqs[i].qnum);
-				ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "txq [%u] %x, "
-					"link %p\n",
-					sc->txqs[i].qnum,
-					ath5k_hw_get_txdp(ah,
-							sc->txqs[i].qnum),
-					sc->txqs[i].link);
-			}
+	if (padsize && skb->len>=padpos+padsize) {
+		memmove(skb->data + padsize, skb->data, padpos);
+		skb_pull(skb, padsize);
+		return padsize;
 	}
 
-	for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
-		if (sc->txqs[i].setup)
-			ath5k_txq_drainq(sc, &sc->txqs[i]);
+	return 0;
 }
 
 static void
-ath5k_txq_release(struct ath5k_softc *sc)
+ath5k_receive_frame(struct ath5k_softc *sc, struct sk_buff *skb,
+		    struct ath5k_rx_status *rs)
 {
-	struct ath5k_txq *txq = sc->txqs;
-	unsigned int i;
+	struct ieee80211_rx_status *rxs;
 
-	for (i = 0; i < ARRAY_SIZE(sc->txqs); i++, txq++)
-		if (txq->setup) {
-			ath5k_hw_release_tx_queue(sc->ah, txq->qnum);
-			txq->setup = false;
-		}
-}
+	ath5k_remove_padding(skb);
 
+	rxs = IEEE80211_SKB_RXCB(skb);
 
+	rxs->flag = 0;
+	if (unlikely(rs->rs_status & AR5K_RXERR_MIC))
+		rxs->flag |= RX_FLAG_MMIC_ERROR;
 
+	/*
+	 * always extend the mac timestamp, since this information is
+	 * also needed for proper IBSS merging.
+	 *
+	 * XXX: it might be too late to do it here, since rs_tstamp is
+	 * 15bit only. that means TSF extension has to be done within
+	 * 32768usec (about 32ms). it might be necessary to move this to
+	 * the interrupt handler, like it is done in madwifi.
+	 *
+	 * Unfortunately we don't know when the hardware takes the rx
+	 * timestamp (beginning of phy frame, data frame, end of rx?).
+	 * The only thing we know is that it is hardware specific...
+	 * On AR5213 it seems the rx timestamp is at the end of the
+	 * frame, but i'm not sure.
+	 *
+	 * NOTE: mac80211 defines mactime at the beginning of the first
+	 * data symbol. Since we don't have any time references it's
+	 * impossible to comply to that. This affects IBSS merge only
+	 * right now, so it's not too bad...
+	 */
+	rxs->mactime = ath5k_extend_tsf(sc->ah, rs->rs_tstamp);
+	rxs->flag |= RX_FLAG_TSFT;
 
-/*************\
-* RX Handling *
-\*************/
+	rxs->freq = sc->curchan->center_freq;
+	rxs->band = sc->curband->band;
 
-/*
- * Enable the receive h/w following a reset.
- */
-static int
-ath5k_rx_start(struct ath5k_softc *sc)
-{
-	struct ath5k_hw *ah = sc->ah;
-	struct ath_common *common = ath5k_hw_common(ah);
-	struct ath5k_buf *bf;
-	int ret;
+	rxs->signal = sc->ah->ah_noise_floor + rs->rs_rssi;
 
-	common->rx_bufsize = roundup(IEEE80211_MAX_FRAME_LEN, common->cachelsz);
+	rxs->antenna = rs->rs_antenna;
 
-	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n",
-		  common->cachelsz, common->rx_bufsize);
+	if (rs->rs_antenna > 0 && rs->rs_antenna < 5)
+		sc->stats.antenna_rx[rs->rs_antenna]++;
+	else
+		sc->stats.antenna_rx[0]++; /* invalid */
 
-	spin_lock_bh(&sc->rxbuflock);
-	sc->rxlink = NULL;
-	list_for_each_entry(bf, &sc->rxbuf, list) {
-		ret = ath5k_rxbuf_setup(sc, bf);
-		if (ret != 0) {
-			spin_unlock_bh(&sc->rxbuflock);
-			goto err;
-		}
-	}
-	bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list);
-	ath5k_hw_set_rxdp(ah, bf->daddr);
-	spin_unlock_bh(&sc->rxbuflock);
+	rxs->rate_idx = ath5k_hw_to_driver_rix(sc, rs->rs_rate);
+	rxs->flag |= ath5k_rx_decrypted(sc, skb, rs);
 
-	ath5k_hw_start_rx_dma(ah);	/* enable recv descriptors */
-	ath5k_mode_setup(sc);		/* set filters, etc. */
-	ath5k_hw_start_rx_pcu(ah);	/* re-enable PCU/DMA engine */
+	if (rxs->rate_idx >= 0 && rs->rs_rate ==
+	    sc->curband->bitrates[rxs->rate_idx].hw_value_short)
+		rxs->flag |= RX_FLAG_SHORTPRE;
 
-	return 0;
-err:
-	return ret;
-}
+	ath5k_debug_dump_skb(sc, skb, "RX  ", 0);
 
-/*
- * Disable the receive h/w in preparation for a reset.
- */
-static void
-ath5k_rx_stop(struct ath5k_softc *sc)
-{
-	struct ath5k_hw *ah = sc->ah;
+	ath5k_update_beacon_rssi(sc, skb, rs->rs_rssi);
 
-	ath5k_hw_stop_rx_pcu(ah);	/* disable PCU */
-	ath5k_hw_set_rx_filter(ah, 0);	/* clear recv filter */
-	ath5k_hw_stop_rx_dma(ah);	/* disable DMA engine */
+	/* check beacons in IBSS mode */
+	if (sc->opmode == NL80211_IFTYPE_ADHOC)
+		ath5k_check_ibss_tsf(sc, skb, rxs);
 
-	ath5k_debug_printrxbuffs(sc, ah);
+	ieee80211_rx(sc->hw, skb);
 }
 
-static unsigned int
-ath5k_rx_decrypted(struct ath5k_softc *sc, struct sk_buff *skb,
-		   struct ath5k_rx_status *rs)
+/** ath5k_frame_receive_ok() - Do we want to receive this frame or not?
+ *
+ * Check if we want to further process this frame or not. Also update
+ * statistics. Return true if we want this frame, false if not.
+ */
+static bool
+ath5k_receive_frame_ok(struct ath5k_softc *sc, struct ath5k_rx_status *rs)
 {
-	struct ath5k_hw *ah = sc->ah;
-	struct ath_common *common = ath5k_hw_common(ah);
-	struct ieee80211_hdr *hdr = (void *)skb->data;
-	unsigned int keyix, hlen;
-
-	if (!(rs->rs_status & AR5K_RXERR_DECRYPT) &&
-			rs->rs_keyix != AR5K_RXKEYIX_INVALID)
-		return RX_FLAG_DECRYPTED;
-
-	/* Apparently when a default key is used to decrypt the packet
-	   the hw does not set the index used to decrypt.  In such cases
-	   get the index from the packet. */
-	hlen = ieee80211_hdrlen(hdr->frame_control);
-	if (ieee80211_has_protected(hdr->frame_control) &&
-	    !(rs->rs_status & AR5K_RXERR_DECRYPT) &&
-	    skb->len >= hlen + 4) {
-		keyix = skb->data[hlen + 3] >> 6;
-
-		if (test_bit(keyix, common->keymap))
-			return RX_FLAG_DECRYPTED;
-	}
-
-	return 0;
-}
-
-
-static void
-ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb,
-		     struct ieee80211_rx_status *rxs)
-{
-	struct ath_common *common = ath5k_hw_common(sc->ah);
-	u64 tsf, bc_tstamp;
-	u32 hw_tu;
-	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
-
-	if (ieee80211_is_beacon(mgmt->frame_control) &&
-	    le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS &&
-	    memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) == 0) {
-		/*
-		 * Received an IBSS beacon with the same BSSID. Hardware *must*
-		 * have updated the local TSF. We have to work around various
-		 * hardware bugs, though...
-		 */
-		tsf = ath5k_hw_get_tsf64(sc->ah);
-		bc_tstamp = le64_to_cpu(mgmt->u.beacon.timestamp);
-		hw_tu = TSF_TO_TU(tsf);
-
-		ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
-			"beacon %llx mactime %llx (diff %lld) tsf now %llx\n",
-			(unsigned long long)bc_tstamp,
-			(unsigned long long)rxs->mactime,
-			(unsigned long long)(rxs->mactime - bc_tstamp),
-			(unsigned long long)tsf);
-
-		/*
-		 * Sometimes the HW will give us a wrong tstamp in the rx
-		 * status, causing the timestamp extension to go wrong.
-		 * (This seems to happen especially with beacon frames bigger
-		 * than 78 byte (incl. FCS))
-		 * But we know that the receive timestamp must be later than the
-		 * timestamp of the beacon since HW must have synced to that.
-		 *
-		 * NOTE: here we assume mactime to be after the frame was
-		 * received, not like mac80211 which defines it at the start.
-		 */
-		if (bc_tstamp > rxs->mactime) {
-			ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
-				"fixing mactime from %llx to %llx\n",
-				(unsigned long long)rxs->mactime,
-				(unsigned long long)tsf);
-			rxs->mactime = tsf;
-		}
-
-		/*
-		 * Local TSF might have moved higher than our beacon timers,
-		 * in that case we have to update them to continue sending
-		 * beacons. This also takes care of synchronizing beacon sending
-		 * times with other stations.
-		 */
-		if (hw_tu >= sc->nexttbtt)
-			ath5k_beacon_update_timers(sc, bc_tstamp);
-	}
-}
-
-static void
-ath5k_update_beacon_rssi(struct ath5k_softc *sc, struct sk_buff *skb, int rssi)
-{
-	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
-	struct ath5k_hw *ah = sc->ah;
-	struct ath_common *common = ath5k_hw_common(ah);
-
-	/* only beacons from our BSSID */
-	if (!ieee80211_is_beacon(mgmt->frame_control) ||
-	    memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) != 0)
-		return;
-
-	ah->ah_beacon_rssi_avg = ath5k_moving_average(ah->ah_beacon_rssi_avg,
-						      rssi);
-
-	/* in IBSS mode we should keep RSSI statistics per neighbour */
-	/* le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS */
-}
-
-/*
- * Compute padding position. skb must contain an IEEE 802.11 frame
- */
-static int ath5k_common_padpos(struct sk_buff *skb)
-{
-	struct ieee80211_hdr * hdr = (struct ieee80211_hdr *)skb->data;
-	__le16 frame_control = hdr->frame_control;
-	int padpos = 24;
-
-	if (ieee80211_has_a4(frame_control)) {
-		padpos += ETH_ALEN;
-	}
-	if (ieee80211_is_data_qos(frame_control)) {
-		padpos += IEEE80211_QOS_CTL_LEN;
-	}
-
-	return padpos;
-}
-
-/*
- * This function expects an 802.11 frame and returns the number of
- * bytes added, or -1 if we don't have enough header room.
- */
-static int ath5k_add_padding(struct sk_buff *skb)
-{
-	int padpos = ath5k_common_padpos(skb);
-	int padsize = padpos & 3;
-
-	if (padsize && skb->len>padpos) {
-
-		if (skb_headroom(skb) < padsize)
-			return -1;
-
-		skb_push(skb, padsize);
-		memmove(skb->data, skb->data+padsize, padpos);
-		return padsize;
-	}
-
-	return 0;
-}
-
-/*
- * The MAC header is padded to have 32-bit boundary if the
- * packet payload is non-zero. The general calculation for
- * padsize would take into account odd header lengths:
- * padsize = 4 - (hdrlen & 3); however, since only
- * even-length headers are used, padding can only be 0 or 2
- * bytes and we can optimize this a bit.  We must not try to
- * remove padding from short control frames that do not have a
- * payload.
- *
- * This function expects an 802.11 frame and returns the number of
- * bytes removed.
- */
-static int ath5k_remove_padding(struct sk_buff *skb)
-{
-	int padpos = ath5k_common_padpos(skb);
-	int padsize = padpos & 3;
-
-	if (padsize && skb->len>=padpos+padsize) {
-		memmove(skb->data + padsize, skb->data, padpos);
-		skb_pull(skb, padsize);
-		return padsize;
-	}
-
-	return 0;
-}
-
-static void
-ath5k_receive_frame(struct ath5k_softc *sc, struct sk_buff *skb,
-		    struct ath5k_rx_status *rs)
-{
-	struct ieee80211_rx_status *rxs;
-
-	ath5k_remove_padding(skb);
-
-	rxs = IEEE80211_SKB_RXCB(skb);
-
-	rxs->flag = 0;
-	if (unlikely(rs->rs_status & AR5K_RXERR_MIC))
-		rxs->flag |= RX_FLAG_MMIC_ERROR;
-
-	/*
-	 * always extend the mac timestamp, since this information is
-	 * also needed for proper IBSS merging.
-	 *
-	 * XXX: it might be too late to do it here, since rs_tstamp is
-	 * 15bit only. that means TSF extension has to be done within
-	 * 32768usec (about 32ms). it might be necessary to move this to
-	 * the interrupt handler, like it is done in madwifi.
-	 *
-	 * Unfortunately we don't know when the hardware takes the rx
-	 * timestamp (beginning of phy frame, data frame, end of rx?).
-	 * The only thing we know is that it is hardware specific...
-	 * On AR5213 it seems the rx timestamp is at the end of the
-	 * frame, but i'm not sure.
-	 *
-	 * NOTE: mac80211 defines mactime at the beginning of the first
-	 * data symbol. Since we don't have any time references it's
-	 * impossible to comply to that. This affects IBSS merge only
-	 * right now, so it's not too bad...
-	 */
-	rxs->mactime = ath5k_extend_tsf(sc->ah, rs->rs_tstamp);
-	rxs->flag |= RX_FLAG_TSFT;
-
-	rxs->freq = sc->curchan->center_freq;
-	rxs->band = sc->curband->band;
-
-	rxs->signal = sc->ah->ah_noise_floor + rs->rs_rssi;
-
-	rxs->antenna = rs->rs_antenna;
-
-	if (rs->rs_antenna > 0 && rs->rs_antenna < 5)
-		sc->stats.antenna_rx[rs->rs_antenna]++;
-	else
-		sc->stats.antenna_rx[0]++; /* invalid */
-
-	rxs->rate_idx = ath5k_hw_to_driver_rix(sc, rs->rs_rate);
-	rxs->flag |= ath5k_rx_decrypted(sc, skb, rs);
-
-	if (rxs->rate_idx >= 0 && rs->rs_rate ==
-	    sc->curband->bitrates[rxs->rate_idx].hw_value_short)
-		rxs->flag |= RX_FLAG_SHORTPRE;
-
-	ath5k_debug_dump_skb(sc, skb, "RX  ", 0);
-
-	ath5k_update_beacon_rssi(sc, skb, rs->rs_rssi);
-
-	/* check beacons in IBSS mode */
-	if (sc->opmode == NL80211_IFTYPE_ADHOC)
-		ath5k_check_ibss_tsf(sc, skb, rxs);
-
-	ieee80211_rx(sc->hw, skb);
-}
-
-/** ath5k_frame_receive_ok() - Do we want to receive this frame or not?
- *
- * Check if we want to further process this frame or not. Also update
- * statistics. Return true if we want this frame, false if not.
- */
-static bool
-ath5k_receive_frame_ok(struct ath5k_softc *sc, struct ath5k_rx_status *rs)
-{
-	sc->stats.rx_all_count++;
+	sc->stats.rx_all_count++;
 
 	if (unlikely(rs->rs_status)) {
 		if (rs->rs_status & AR5K_RXERR_CRC)
@@ -2121,33 +1462,86 @@ unlock:
 * TX Handling *
 \*************/
 
-static void
-ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
+static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
+			  struct ath5k_txq *txq)
 {
-	struct ath5k_tx_status ts = {};
-	struct ath5k_buf *bf, *bf0;
-	struct ath5k_desc *ds;
-	struct sk_buff *skb;
-	struct ieee80211_tx_info *info;
-	int i, ret;
-
-	spin_lock(&txq->lock);
-	list_for_each_entry_safe(bf, bf0, &txq->q, list) {
-		ds = bf->desc;
+	struct ath5k_softc *sc = hw->priv;
+	struct ath5k_buf *bf;
+	unsigned long flags;
+	int padsize;
 
-		/*
-		 * It's possible that the hardware can say the buffer is
-		 * completed when it hasn't yet loaded the ds_link from
-		 * host memory and moved on.  If there are more TX
-		 * descriptors in the queue, wait for TXDP to change
-		 * before processing this one.
-		 */
-		if (ath5k_hw_get_txdp(sc->ah, txq->qnum) == bf->daddr &&
-		    !list_is_last(&bf->list, &txq->q))
-			break;
+	ath5k_debug_dump_skb(sc, skb, "TX  ", 1);
 
-		ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts);
-		if (unlikely(ret == -EINPROGRESS))
+	/*
+	 * The hardware expects the header padded to 4 byte boundaries.
+	 * If this is not the case, we add the padding after the header.
+	 */
+	padsize = ath5k_add_padding(skb);
+	if (padsize < 0) {
+		ATH5K_ERR(sc, "tx hdrlen not %%4: not enough"
+			  " headroom to pad");
+		goto drop_packet;
+	}
+
+	spin_lock_irqsave(&sc->txbuflock, flags);
+	if (list_empty(&sc->txbuf)) {
+		ATH5K_ERR(sc, "no further txbuf available, dropping packet\n");
+		spin_unlock_irqrestore(&sc->txbuflock, flags);
+		ieee80211_stop_queue(hw, skb_get_queue_mapping(skb));
+		goto drop_packet;
+	}
+	bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list);
+	list_del(&bf->list);
+	sc->txbuf_len--;
+	if (list_empty(&sc->txbuf))
+		ieee80211_stop_queues(hw);
+	spin_unlock_irqrestore(&sc->txbuflock, flags);
+
+	bf->skb = skb;
+
+	if (ath5k_txbuf_setup(sc, bf, txq, padsize)) {
+		bf->skb = NULL;
+		spin_lock_irqsave(&sc->txbuflock, flags);
+		list_add_tail(&bf->list, &sc->txbuf);
+		sc->txbuf_len++;
+		spin_unlock_irqrestore(&sc->txbuflock, flags);
+		goto drop_packet;
+	}
+	return NETDEV_TX_OK;
+
+drop_packet:
+	dev_kfree_skb_any(skb);
+	return NETDEV_TX_OK;
+}
+
+
+static void
+ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
+{
+	struct ath5k_tx_status ts = {};
+	struct ath5k_buf *bf, *bf0;
+	struct ath5k_desc *ds;
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	int i, ret;
+
+	spin_lock(&txq->lock);
+	list_for_each_entry_safe(bf, bf0, &txq->q, list) {
+		ds = bf->desc;
+
+		/*
+		 * It's possible that the hardware can say the buffer is
+		 * completed when it hasn't yet loaded the ds_link from
+		 * host memory and moved on.  If there are more TX
+		 * descriptors in the queue, wait for TXDP to change
+		 * before processing this one.
+		 */
+		if (ath5k_hw_get_txdp(sc->ah, txq->qnum) == bf->daddr &&
+		    !list_is_last(&bf->list, &txq->q))
+			break;
+
+		ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts);
+		if (unlikely(ret == -EINPROGRESS))
 			break;
 		else if (unlikely(ret)) {
 			ATH5K_ERR(sc, "error %d while processing queue %u\n",
@@ -2313,6 +1707,43 @@ err_unmap:
 }
 
 /*
+ * Updates the beacon that is sent by ath5k_beacon_send.  For adhoc,
+ * this is called only once at config_bss time, for AP we do it every
+ * SWBA interrupt so that the TIM will reflect buffered frames.
+ *
+ * Called with the beacon lock.
+ */
+static int
+ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+	int ret;
+	struct ath5k_softc *sc = hw->priv;
+	struct sk_buff *skb;
+
+	if (WARN_ON(!vif)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	skb = ieee80211_beacon_get(hw, vif);
+
+	if (!skb) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ath5k_debug_dump_skb(sc, skb, "BC  ", 1);
+
+	ath5k_txbuf_free_skb(sc, sc->bbuf);
+	sc->bbuf->skb = skb;
+	ret = ath5k_beacon_setup(sc, sc->bbuf);
+	if (ret)
+		sc->bbuf->skb = NULL;
+out:
+	return ret;
+}
+
+/*
  * Transmit a beacon frame at SWBA.  Dynamic updates to the
  * frame contents are done as needed and the slot time is
  * also adjusted based on current state.
@@ -2389,7 +1820,6 @@ ath5k_beacon_send(struct ath5k_softc *sc)
 	sc->bsent++;
 }
 
-
 /**
  * ath5k_beacon_update_timers - update beacon timers
  *
@@ -2491,7 +1921,6 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf)
 		intval & AR5K_BEACON_RESET_TSF ? "AR5K_BEACON_RESET_TSF" : "");
 }
 
-
 /**
  * ath5k_beacon_config - Configure the beacon queues and interrupts
  *
@@ -2570,66 +1999,181 @@ static void ath5k_tasklet_beacon(unsigned long data)
 * Interrupt handling *
 \********************/
 
-static int
-ath5k_init(struct ath5k_softc *sc)
+static void
+ath5k_intr_calibration_poll(struct ath5k_hw *ah)
 {
-	struct ath5k_hw *ah = sc->ah;
-	struct ath_common *common = ath5k_hw_common(ah);
-	int ret, i;
+	if (time_is_before_eq_jiffies(ah->ah_cal_next_ani) &&
+	    !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL)) {
+		/* run ANI only when full calibration is not active */
+		ah->ah_cal_next_ani = jiffies +
+			msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI);
+		tasklet_schedule(&ah->ah_sc->ani_tasklet);
 
-	mutex_lock(&sc->lock);
+	} else if (time_is_before_eq_jiffies(ah->ah_cal_next_full)) {
+		ah->ah_cal_next_full = jiffies +
+			msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL);
+		tasklet_schedule(&ah->ah_sc->calib);
+	}
+	/* we could use SWI to generate enough interrupts to meet our
+	 * calibration interval requirements, if necessary:
+	 * AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); */
+}
 
-	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mode %d\n", sc->opmode);
+static irqreturn_t
+ath5k_intr(int irq, void *dev_id)
+{
+	struct ath5k_softc *sc = dev_id;
+	struct ath5k_hw *ah = sc->ah;
+	enum ath5k_int status;
+	unsigned int counter = 1000;
 
-	/*
-	 * Stop anything previously setup.  This is safe
-	 * no matter this is the first time through or not.
-	 */
-	ath5k_stop_locked(sc);
+	if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) ||
+				!ath5k_hw_is_intr_pending(ah)))
+		return IRQ_NONE;
 
-	/*
-	 * The basic interface to setting the hardware in a good
-	 * state is ``reset''.  On return the hardware is known to
-	 * be powered up and with interrupts disabled.  This must
-	 * be followed by initialization of the appropriate bits
-	 * and then setup of the interrupt mask.
-	 */
-	sc->curchan = sc->hw->conf.channel;
-	sc->curband = &sc->sbands[sc->curchan->band];
-	sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL |
-		AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL |
-		AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB;
+	do {
+		ath5k_hw_get_isr(ah, &status);		/* NB: clears IRQ too */
+		ATH5K_DBG(sc, ATH5K_DEBUG_INTR, "status 0x%x/0x%x\n",
+				status, sc->imask);
+		if (unlikely(status & AR5K_INT_FATAL)) {
+			/*
+			 * Fatal errors are unrecoverable.
+			 * Typically these are caused by DMA errors.
+			 */
+			ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
+				  "fatal int, resetting\n");
+			ieee80211_queue_work(sc->hw, &sc->reset_work);
+		} else if (unlikely(status & AR5K_INT_RXORN)) {
+			/*
+			 * Receive buffers are full. Either the bus is busy or
+			 * the CPU is not fast enough to process all received
+			 * frames.
+			 * Older chipsets need a reset to come out of this
+			 * condition, but we treat it as RX for newer chips.
+			 * We don't know exactly which versions need a reset -
+			 * this guess is copied from the HAL.
+			 */
+			sc->stats.rxorn_intr++;
+			if (ah->ah_mac_srev < AR5K_SREV_AR5212) {
+				ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
+					  "rx overrun, resetting\n");
+				ieee80211_queue_work(sc->hw, &sc->reset_work);
+			}
+			else
+				tasklet_schedule(&sc->rxtq);
+		} else {
+			if (status & AR5K_INT_SWBA) {
+				tasklet_hi_schedule(&sc->beacontq);
+			}
+			if (status & AR5K_INT_RXEOL) {
+				/*
+				* NB: the hardware should re-read the link when
+				*     RXE bit is written, but it doesn't work at
+				*     least on older hardware revs.
+				*/
+				sc->stats.rxeol_intr++;
+			}
+			if (status & AR5K_INT_TXURN) {
+				/* bump tx trigger level */
+				ath5k_hw_update_tx_triglevel(ah, true);
+			}
+			if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR))
+				tasklet_schedule(&sc->rxtq);
+			if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC
+					| AR5K_INT_TXERR | AR5K_INT_TXEOL))
+				tasklet_schedule(&sc->txtq);
+			if (status & AR5K_INT_BMISS) {
+				/* TODO */
+			}
+			if (status & AR5K_INT_MIB) {
+				sc->stats.mib_intr++;
+				ath5k_hw_update_mib_counters(ah);
+				ath5k_ani_mib_intr(ah);
+			}
+			if (status & AR5K_INT_GPIO)
+				tasklet_schedule(&sc->rf_kill.toggleq);
 
-	ret = ath5k_reset(sc, NULL);
-	if (ret)
-		goto done;
+		}
+	} while (ath5k_hw_is_intr_pending(ah) && --counter > 0);
 
-	ath5k_rfkill_hw_start(ah);
+	if (unlikely(!counter))
+		ATH5K_WARN(sc, "too many interrupts, giving up for now\n");
 
-	/*
-	 * Reset the key cache since some parts do not reset the
-	 * contents on initial power up or resume from suspend.
-	 */
-	for (i = 0; i < common->keymax; i++)
-		ath_hw_keyreset(common, (u16)i);
+	ath5k_intr_calibration_poll(ah);
 
-	ath5k_hw_set_ack_bitrate_high(ah, true);
-	ret = 0;
-done:
-	mmiowb();
-	mutex_unlock(&sc->lock);
-	return ret;
+	return IRQ_HANDLED;
 }
 
-static int
-ath5k_stop_locked(struct ath5k_softc *sc)
+/*
+ * Periodically recalibrate the PHY to account
+ * for temperature/environment changes.
+ */
+static void
+ath5k_tasklet_calibrate(unsigned long data)
 {
+	struct ath5k_softc *sc = (void *)data;
 	struct ath5k_hw *ah = sc->ah;
 
-	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "invalid %u\n",
-			test_bit(ATH_STAT_INVALID, sc->status));
+	/* Only full calibration for now */
+	ah->ah_cal_mask |= AR5K_CALIBRATION_FULL;
 
-	/*
+	ATH5K_DBG(sc, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n",
+		ieee80211_frequency_to_channel(sc->curchan->center_freq),
+		sc->curchan->hw_value);
+
+	if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) {
+		/*
+		 * Rfgain is out of bounds, reset the chip
+		 * to load new gain values.
+		 */
+		ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "calibration, resetting\n");
+		ieee80211_queue_work(sc->hw, &sc->reset_work);
+	}
+	if (ath5k_hw_phy_calibrate(ah, sc->curchan))
+		ATH5K_ERR(sc, "calibration of channel %u failed\n",
+			ieee80211_frequency_to_channel(
+				sc->curchan->center_freq));
+
+	/* Noise floor calibration interrupts rx/tx path while I/Q calibration
+	 * doesn't. We stop the queues so that calibration doesn't interfere
+	 * with TX and don't run it as often */
+	if (time_is_before_eq_jiffies(ah->ah_cal_next_nf)) {
+		ah->ah_cal_next_nf = jiffies +
+			msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_NF);
+		ieee80211_stop_queues(sc->hw);
+		ath5k_hw_update_noise_floor(ah);
+		ieee80211_wake_queues(sc->hw);
+	}
+
+	ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL;
+}
+
+
+static void
+ath5k_tasklet_ani(unsigned long data)
+{
+	struct ath5k_softc *sc = (void *)data;
+	struct ath5k_hw *ah = sc->ah;
+
+	ah->ah_cal_mask |= AR5K_CALIBRATION_ANI;
+	ath5k_ani_calibration(ah);
+	ah->ah_cal_mask &= ~AR5K_CALIBRATION_ANI;
+}
+
+
+/*************************\
+* Initialization routines *
+\*************************/
+
+static int
+ath5k_stop_locked(struct ath5k_softc *sc)
+{
+	struct ath5k_hw *ah = sc->ah;
+
+	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "invalid %u\n",
+			test_bit(ATH_STAT_INVALID, sc->status));
+
+	/*
 	 * Shutdown the hardware and driver:
 	 *    stop output from above
 	 *    disable interrupts
@@ -2660,6 +2204,57 @@ ath5k_stop_locked(struct ath5k_softc *sc)
 	return 0;
 }
 
+static int
+ath5k_init(struct ath5k_softc *sc)
+{
+	struct ath5k_hw *ah = sc->ah;
+	struct ath_common *common = ath5k_hw_common(ah);
+	int ret, i;
+
+	mutex_lock(&sc->lock);
+
+	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mode %d\n", sc->opmode);
+
+	/*
+	 * Stop anything previously setup.  This is safe
+	 * no matter this is the first time through or not.
+	 */
+	ath5k_stop_locked(sc);
+
+	/*
+	 * The basic interface to setting the hardware in a good
+	 * state is ``reset''.  On return the hardware is known to
+	 * be powered up and with interrupts disabled.  This must
+	 * be followed by initialization of the appropriate bits
+	 * and then setup of the interrupt mask.
+	 */
+	sc->curchan = sc->hw->conf.channel;
+	sc->curband = &sc->sbands[sc->curchan->band];
+	sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL |
+		AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL |
+		AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB;
+
+	ret = ath5k_reset(sc, NULL);
+	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.
+	 */
+	for (i = 0; i < common->keymax; i++)
+		ath_hw_keyreset(common, (u16) i);
+
+	ath5k_hw_set_ack_bitrate_high(ah, true);
+	ret = 0;
+done:
+	mmiowb();
+	mutex_unlock(&sc->lock);
+	return ret;
+}
+
 static void stop_tasklets(struct ath5k_softc *sc)
 {
 	tasklet_kill(&sc->rxtq);
@@ -2720,310 +2315,257 @@ ath5k_stop_hw(struct ath5k_softc *sc)
 	return ret;
 }
 
-static void
-ath5k_intr_calibration_poll(struct ath5k_hw *ah)
+/*
+ * Reset the hardware.  If chan is not NULL, then also pause rx/tx
+ * and change to the given channel.
+ *
+ * This should be called with sc->lock.
+ */
+static int
+ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan)
 {
-	if (time_is_before_eq_jiffies(ah->ah_cal_next_ani) &&
-	    !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL)) {
-		/* run ANI only when full calibration is not active */
-		ah->ah_cal_next_ani = jiffies +
-			msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI);
-		tasklet_schedule(&ah->ah_sc->ani_tasklet);
+	struct ath5k_hw *ah = sc->ah;
+	int ret;
 
-	} else if (time_is_before_eq_jiffies(ah->ah_cal_next_full)) {
-		ah->ah_cal_next_full = jiffies +
-			msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL);
-		tasklet_schedule(&ah->ah_sc->calib);
+	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "resetting\n");
+
+	ath5k_hw_set_imr(ah, 0);
+	synchronize_irq(sc->pdev->irq);
+	stop_tasklets(sc);
+
+	if (chan) {
+		ath5k_txq_cleanup(sc);
+		ath5k_rx_stop(sc);
+
+		sc->curchan = chan;
+		sc->curband = &sc->sbands[chan->band];
+	}
+	ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, chan != NULL);
+	if (ret) {
+		ATH5K_ERR(sc, "can't reset hardware (%d)\n", ret);
+		goto err;
 	}
-	/* we could use SWI to generate enough interrupts to meet our
-	 * calibration interval requirements, if necessary:
-	 * AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); */
-}
 
-static irqreturn_t
-ath5k_intr(int irq, void *dev_id)
-{
-	struct ath5k_softc *sc = dev_id;
-	struct ath5k_hw *ah = sc->ah;
-	enum ath5k_int status;
-	unsigned int counter = 1000;
+	ret = ath5k_rx_start(sc);
+	if (ret) {
+		ATH5K_ERR(sc, "can't start recv logic\n");
+		goto err;
+	}
 
-	if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) ||
-				!ath5k_hw_is_intr_pending(ah)))
-		return IRQ_NONE;
+	ath5k_ani_init(ah, ah->ah_sc->ani_state.ani_mode);
 
-	do {
-		ath5k_hw_get_isr(ah, &status);		/* NB: clears IRQ too */
-		ATH5K_DBG(sc, ATH5K_DEBUG_INTR, "status 0x%x/0x%x\n",
-				status, sc->imask);
-		if (unlikely(status & AR5K_INT_FATAL)) {
-			/*
-			 * Fatal errors are unrecoverable.
-			 * Typically these are caused by DMA errors.
-			 */
-			ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
-				  "fatal int, resetting\n");
-			ieee80211_queue_work(sc->hw, &sc->reset_work);
-		} else if (unlikely(status & AR5K_INT_RXORN)) {
-			/*
-			 * Receive buffers are full. Either the bus is busy or
-			 * the CPU is not fast enough to process all received
-			 * frames.
-			 * Older chipsets need a reset to come out of this
-			 * condition, but we treat it as RX for newer chips.
-			 * We don't know exactly which versions need a reset -
-			 * this guess is copied from the HAL.
-			 */
-			sc->stats.rxorn_intr++;
-			if (ah->ah_mac_srev < AR5K_SREV_AR5212) {
-				ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
-					  "rx overrun, resetting\n");
-				ieee80211_queue_work(sc->hw, &sc->reset_work);
-			}
-			else
-				tasklet_schedule(&sc->rxtq);
-		} else {
-			if (status & AR5K_INT_SWBA) {
-				tasklet_hi_schedule(&sc->beacontq);
-			}
-			if (status & AR5K_INT_RXEOL) {
-				/*
-				* NB: the hardware should re-read the link when
-				*     RXE bit is written, but it doesn't work at
-				*     least on older hardware revs.
-				*/
-				sc->stats.rxeol_intr++;
-			}
-			if (status & AR5K_INT_TXURN) {
-				/* bump tx trigger level */
-				ath5k_hw_update_tx_triglevel(ah, true);
-			}
-			if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR))
-				tasklet_schedule(&sc->rxtq);
-			if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC
-					| AR5K_INT_TXERR | AR5K_INT_TXEOL))
-				tasklet_schedule(&sc->txtq);
-			if (status & AR5K_INT_BMISS) {
-				/* TODO */
-			}
-			if (status & AR5K_INT_MIB) {
-				sc->stats.mib_intr++;
-				ath5k_hw_update_mib_counters(ah);
-				ath5k_ani_mib_intr(ah);
-			}
-			if (status & AR5K_INT_GPIO)
-				tasklet_schedule(&sc->rf_kill.toggleq);
+	ah->ah_cal_next_full = jiffies;
+	ah->ah_cal_next_ani = jiffies;
+	ah->ah_cal_next_nf = jiffies;
 
-		}
-	} while (ath5k_hw_is_intr_pending(ah) && --counter > 0);
+	/*
+	 * Change channels and update the h/w rate map if we're switching;
+	 * e.g. 11a to 11b/g.
+	 *
+	 * We may be doing a reset in response to an ioctl that changes the
+	 * channel so update any state that might change as a result.
+	 *
+	 * XXX needed?
+	 */
+/*	ath5k_chan_change(sc, c); */
 
-	if (unlikely(!counter))
-		ATH5K_WARN(sc, "too many interrupts, giving up for now\n");
+	ath5k_beacon_config(sc);
+	/* intrs are enabled by ath5k_beacon_config */
 
-	ath5k_intr_calibration_poll(ah);
+	ieee80211_wake_queues(sc->hw);
 
-	return IRQ_HANDLED;
+	return 0;
+err:
+	return ret;
 }
 
-/*
- * Periodically recalibrate the PHY to account
- * for temperature/environment changes.
- */
-static void
-ath5k_tasklet_calibrate(unsigned long data)
+static void ath5k_reset_work(struct work_struct *work)
 {
-	struct ath5k_softc *sc = (void *)data;
-	struct ath5k_hw *ah = sc->ah;
-
-	/* Only full calibration for now */
-	ah->ah_cal_mask |= AR5K_CALIBRATION_FULL;
-
-	ATH5K_DBG(sc, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n",
-		ieee80211_frequency_to_channel(sc->curchan->center_freq),
-		sc->curchan->hw_value);
-
-	if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) {
-		/*
-		 * Rfgain is out of bounds, reset the chip
-		 * to load new gain values.
-		 */
-		ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "calibration, resetting\n");
-		ieee80211_queue_work(sc->hw, &sc->reset_work);
-	}
-	if (ath5k_hw_phy_calibrate(ah, sc->curchan))
-		ATH5K_ERR(sc, "calibration of channel %u failed\n",
-			ieee80211_frequency_to_channel(
-				sc->curchan->center_freq));
-
-	/* Noise floor calibration interrupts rx/tx path while I/Q calibration
-	 * doesn't. We stop the queues so that calibration doesn't interfere
-	 * with TX and don't run it as often */
-	if (time_is_before_eq_jiffies(ah->ah_cal_next_nf)) {
-		ah->ah_cal_next_nf = jiffies +
-			msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_NF);
-		ieee80211_stop_queues(sc->hw);
-		ath5k_hw_update_noise_floor(ah);
-		ieee80211_wake_queues(sc->hw);
-	}
-
-	ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL;
-}
-
-
-static void
-ath5k_tasklet_ani(unsigned long data)
-{
-	struct ath5k_softc *sc = (void *)data;
-	struct ath5k_hw *ah = sc->ah;
+	struct ath5k_softc *sc = container_of(work, struct ath5k_softc,
+		reset_work);
 
-	ah->ah_cal_mask |= AR5K_CALIBRATION_ANI;
-	ath5k_ani_calibration(ah);
-	ah->ah_cal_mask &= ~AR5K_CALIBRATION_ANI;
+	mutex_lock(&sc->lock);
+	ath5k_reset(sc, sc->curchan);
+	mutex_unlock(&sc->lock);
 }
 
-
-/********************\
-* Mac80211 functions *
-\********************/
-
 static int
-ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
 {
 	struct ath5k_softc *sc = hw->priv;
+	struct ath5k_hw *ah = sc->ah;
+	struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah);
+	u8 mac[ETH_ALEN] = {};
+	int ret;
 
-	return ath5k_tx_queue(hw, skb, sc->txq);
-}
+	ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "devid 0x%x\n", pdev->device);
 
-static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
-			  struct ath5k_txq *txq)
-{
-	struct ath5k_softc *sc = hw->priv;
-	struct ath5k_buf *bf;
-	unsigned long flags;
-	int padsize;
+	/*
+	 * Check if the MAC has multi-rate retry support.
+	 * We do this by trying to setup a fake extended
+	 * descriptor.  MACs that don't have support will
+	 * return false w/o doing anything.  MACs that do
+	 * support it will return true w/o doing anything.
+	 */
+	ret = ath5k_hw_setup_mrr_tx_desc(ah, NULL, 0, 0, 0, 0, 0, 0);
 
-	ath5k_debug_dump_skb(sc, skb, "TX  ", 1);
+	if (ret < 0)
+		goto err;
+	if (ret > 0)
+		__set_bit(ATH_STAT_MRRETRY, sc->status);
 
 	/*
-	 * The hardware expects the header padded to 4 byte boundaries.
-	 * If this is not the case, we add the padding after the header.
+	 * Collect the channel list.  The 802.11 layer
+	 * is resposible for filtering this list based
+	 * on settings like the phy mode and regulatory
+	 * domain restrictions.
 	 */
-	padsize = ath5k_add_padding(skb);
-	if (padsize < 0) {
-		ATH5K_ERR(sc, "tx hdrlen not %%4: not enough"
-			  " headroom to pad");
-		goto drop_packet;
+	ret = ath5k_setup_bands(hw);
+	if (ret) {
+		ATH5K_ERR(sc, "can't get channels\n");
+		goto err;
 	}
 
-	spin_lock_irqsave(&sc->txbuflock, flags);
-	if (list_empty(&sc->txbuf)) {
-		ATH5K_ERR(sc, "no further txbuf available, dropping packet\n");
-		spin_unlock_irqrestore(&sc->txbuflock, flags);
-		ieee80211_stop_queue(hw, skb_get_queue_mapping(skb));
-		goto drop_packet;
-	}
-	bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list);
-	list_del(&bf->list);
-	sc->txbuf_len--;
-	if (list_empty(&sc->txbuf))
-		ieee80211_stop_queues(hw);
-	spin_unlock_irqrestore(&sc->txbuflock, flags);
+	/* NB: setup here so ath5k_rate_update is happy */
+	if (test_bit(AR5K_MODE_11A, ah->ah_modes))
+		ath5k_setcurmode(sc, AR5K_MODE_11A);
+	else
+		ath5k_setcurmode(sc, AR5K_MODE_11B);
 
-	bf->skb = skb;
+	/*
+	 * Allocate tx+rx descriptors and populate the lists.
+	 */
+	ret = ath5k_desc_alloc(sc, pdev);
+	if (ret) {
+		ATH5K_ERR(sc, "can't allocate descriptors\n");
+		goto err;
+	}
 
-	if (ath5k_txbuf_setup(sc, bf, txq, padsize)) {
-		bf->skb = NULL;
-		spin_lock_irqsave(&sc->txbuflock, flags);
-		list_add_tail(&bf->list, &sc->txbuf);
-		sc->txbuf_len++;
-		spin_unlock_irqrestore(&sc->txbuflock, flags);
-		goto drop_packet;
+	/*
+	 * Allocate hardware transmit queues: one queue for
+	 * beacon frames and one data queue for each QoS
+	 * priority.  Note that hw functions handle resetting
+	 * these queues at the needed time.
+	 */
+	ret = ath5k_beaconq_setup(ah);
+	if (ret < 0) {
+		ATH5K_ERR(sc, "can't setup a beacon xmit queue\n");
+		goto err_desc;
+	}
+	sc->bhalq = ret;
+	sc->cabq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_CAB, 0);
+	if (IS_ERR(sc->cabq)) {
+		ATH5K_ERR(sc, "can't setup cab queue\n");
+		ret = PTR_ERR(sc->cabq);
+		goto err_bhal;
 	}
-	return NETDEV_TX_OK;
 
-drop_packet:
-	dev_kfree_skb_any(skb);
-	return NETDEV_TX_OK;
-}
+	sc->txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK);
+	if (IS_ERR(sc->txq)) {
+		ATH5K_ERR(sc, "can't setup xmit queue\n");
+		ret = PTR_ERR(sc->txq);
+		goto err_queues;
+	}
 
-/*
- * Reset the hardware.  If chan is not NULL, then also pause rx/tx
- * and change to the given channel.
- *
- * This should be called with sc->lock.
- */
-static int
-ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan)
-{
-	struct ath5k_hw *ah = sc->ah;
-	int ret;
+	tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc);
+	tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc);
+	tasklet_init(&sc->calib, ath5k_tasklet_calibrate, (unsigned long)sc);
+	tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc);
+	tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc);
 
-	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "resetting\n");
+	INIT_WORK(&sc->reset_work, ath5k_reset_work);
 
-	ath5k_hw_set_imr(ah, 0);
-	synchronize_irq(sc->pdev->irq);
-	stop_tasklets(sc);
+	ret = ath5k_eeprom_read_mac(ah, mac);
+	if (ret) {
+		ATH5K_ERR(sc, "unable to read address from EEPROM: 0x%04x\n",
+			sc->pdev->device);
+		goto err_queues;
+	}
 
-	if (chan) {
-		ath5k_txq_cleanup(sc);
-		ath5k_rx_stop(sc);
+	SET_IEEE80211_PERM_ADDR(hw, mac);
+	/* All MAC address bits matter for ACKs */
+	memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN);
+	ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask);
 
-		sc->curchan = chan;
-		sc->curband = &sc->sbands[chan->band];
-	}
-	ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, chan != NULL);
+	regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain;
+	ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier);
 	if (ret) {
-		ATH5K_ERR(sc, "can't reset hardware (%d)\n", ret);
-		goto err;
+		ATH5K_ERR(sc, "can't initialize regulatory system\n");
+		goto err_queues;
 	}
 
-	ret = ath5k_rx_start(sc);
+	ret = ieee80211_register_hw(hw);
 	if (ret) {
-		ATH5K_ERR(sc, "can't start recv logic\n");
-		goto err;
+		ATH5K_ERR(sc, "can't register ieee80211 hw\n");
+		goto err_queues;
 	}
 
-	ath5k_ani_init(ah, ah->ah_sc->ani_state.ani_mode);
-
-	ah->ah_cal_next_full = jiffies;
-	ah->ah_cal_next_ani = jiffies;
-	ah->ah_cal_next_nf = jiffies;
-
-	/*
-	 * Change channels and update the h/w rate map if we're switching;
-	 * e.g. 11a to 11b/g.
-	 *
-	 * We may be doing a reset in response to an ioctl that changes the
-	 * channel so update any state that might change as a result.
-	 *
-	 * XXX needed?
-	 */
-/*	ath5k_chan_change(sc, c); */
+	if (!ath_is_world_regd(regulatory))
+		regulatory_hint(hw->wiphy, regulatory->alpha2);
 
-	ath5k_beacon_config(sc);
-	/* intrs are enabled by ath5k_beacon_config */
+	ath5k_init_leds(sc);
 
-	ieee80211_wake_queues(sc->hw);
+	ath5k_sysfs_register(sc);
 
 	return 0;
+err_queues:
+	ath5k_txq_release(sc);
+err_bhal:
+	ath5k_hw_release_tx_queue(ah, sc->bhalq);
+err_desc:
+	ath5k_desc_free(sc, pdev);
 err:
 	return ret;
 }
 
-static void ath5k_reset_work(struct work_struct *work)
+static void
+ath5k_detach(struct pci_dev *pdev, struct ieee80211_hw *hw)
 {
-	struct ath5k_softc *sc = container_of(work, struct ath5k_softc,
-		reset_work);
-
-	mutex_lock(&sc->lock);
-	ath5k_reset(sc, sc->curchan);
-	mutex_unlock(&sc->lock);
-}
+	struct ath5k_softc *sc = hw->priv;
 
-static int ath5k_start(struct ieee80211_hw *hw)
-{
-	return ath5k_init(hw->priv);
-}
+	/*
+	 * NB: the order of these is important:
+	 * o call the 802.11 layer before detaching ath5k_hw to
+	 *   ensure callbacks into the driver to delete global
+	 *   key cache entries can be handled
+	 * o reclaim the tx queue data structures after calling
+	 *   the 802.11 layer as we'll get called back to reclaim
+	 *   node state and potentially want to use them
+	 * o to cleanup the tx queues the hal is called, so detach
+	 *   it last
+	 * XXX: ??? detach ath5k_hw ???
+	 * Other than that, it's straightforward...
+	 */
+	ieee80211_unregister_hw(hw);
+	ath5k_desc_free(sc, pdev);
+	ath5k_txq_release(sc);
+	ath5k_hw_release_tx_queue(sc->ah, sc->bhalq);
+	ath5k_unregister_leds(sc);
+
+	ath5k_sysfs_unregister(sc);
+	/*
+	 * NB: can't reclaim these until after ieee80211_ifdetach
+	 * returns because we'll get called back to reclaim node
+	 * state and potentially want to use them.
+	 */
+}
+
+/********************\
+* Mac80211 functions *
+\********************/
+
+static int
+ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+	struct ath5k_softc *sc = hw->priv;
+
+	return ath5k_tx_queue(hw, skb, sc->txq);
+}
+
+static int ath5k_start(struct ieee80211_hw *hw)
+{
+	return ath5k_init(hw->priv);
+}
 
 static void ath5k_stop(struct ieee80211_hw *hw)
 {
@@ -3329,214 +2871,538 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 		ret = -EINVAL;
 	}
 
-	mmiowb();
-	mutex_unlock(&sc->lock);
-	return ret;
-}
-
-static int
-ath5k_get_stats(struct ieee80211_hw *hw,
-		struct ieee80211_low_level_stats *stats)
-{
-	struct ath5k_softc *sc = hw->priv;
-
-	/* Force update */
-	ath5k_hw_update_mib_counters(sc->ah);
-
-	stats->dot11ACKFailureCount = sc->stats.ack_fail;
-	stats->dot11RTSFailureCount = sc->stats.rts_fail;
-	stats->dot11RTSSuccessCount = sc->stats.rts_ok;
-	stats->dot11FCSErrorCount = sc->stats.fcs_error;
-
-	return 0;
-}
-
-static int ath5k_get_survey(struct ieee80211_hw *hw, int idx,
-		struct survey_info *survey)
-{
-	struct ath5k_softc *sc = hw->priv;
-	struct ieee80211_conf *conf = &hw->conf;
-
-	 if (idx != 0)
-		return -ENOENT;
+	mmiowb();
+	mutex_unlock(&sc->lock);
+	return ret;
+}
+
+static int
+ath5k_get_stats(struct ieee80211_hw *hw,
+		struct ieee80211_low_level_stats *stats)
+{
+	struct ath5k_softc *sc = hw->priv;
+
+	/* Force update */
+	ath5k_hw_update_mib_counters(sc->ah);
+
+	stats->dot11ACKFailureCount = sc->stats.ack_fail;
+	stats->dot11RTSFailureCount = sc->stats.rts_fail;
+	stats->dot11RTSSuccessCount = sc->stats.rts_ok;
+	stats->dot11FCSErrorCount = sc->stats.fcs_error;
+
+	return 0;
+}
+
+static int ath5k_get_survey(struct ieee80211_hw *hw, int idx,
+		struct survey_info *survey)
+{
+	struct ath5k_softc *sc = hw->priv;
+	struct ieee80211_conf *conf = &hw->conf;
+
+	 if (idx != 0)
+		return -ENOENT;
+
+	survey->channel = conf->channel;
+	survey->filled = SURVEY_INFO_NOISE_DBM;
+	survey->noise = sc->ah->ah_noise_floor;
+
+	return 0;
+}
+
+static u64
+ath5k_get_tsf(struct ieee80211_hw *hw)
+{
+	struct ath5k_softc *sc = hw->priv;
+
+	return ath5k_hw_get_tsf64(sc->ah);
+}
+
+static void
+ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf)
+{
+	struct ath5k_softc *sc = hw->priv;
+
+	ath5k_hw_set_tsf64(sc->ah, tsf);
+}
+
+static void
+ath5k_reset_tsf(struct ieee80211_hw *hw)
+{
+	struct ath5k_softc *sc = hw->priv;
+
+	/*
+	 * in IBSS mode we need to update the beacon timers too.
+	 * this will also reset the TSF if we call it with 0
+	 */
+	if (sc->opmode == NL80211_IFTYPE_ADHOC)
+		ath5k_beacon_update_timers(sc, 0);
+	else
+		ath5k_hw_reset_tsf(sc->ah);
+}
+
+static void
+set_beacon_filter(struct ieee80211_hw *hw, bool enable)
+{
+	struct ath5k_softc *sc = hw->priv;
+	struct ath5k_hw *ah = sc->ah;
+	u32 rfilt;
+	rfilt = ath5k_hw_get_rx_filter(ah);
+	if (enable)
+		rfilt |= AR5K_RX_FILTER_BEACON;
+	else
+		rfilt &= ~AR5K_RX_FILTER_BEACON;
+	ath5k_hw_set_rx_filter(ah, rfilt);
+	sc->filter_flags = rfilt;
+}
+
+static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    struct ieee80211_bss_conf *bss_conf,
+				    u32 changes)
+{
+	struct ath5k_softc *sc = hw->priv;
+	struct ath5k_hw *ah = sc->ah;
+	struct ath_common *common = ath5k_hw_common(ah);
+	unsigned long flags;
+
+	mutex_lock(&sc->lock);
+	if (WARN_ON(sc->vif != vif))
+		goto unlock;
+
+	if (changes & BSS_CHANGED_BSSID) {
+		/* Cache for later use during resets */
+		memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
+		common->curaid = 0;
+		ath5k_hw_set_bssid(ah);
+		mmiowb();
+	}
+
+	if (changes & BSS_CHANGED_BEACON_INT)
+		sc->bintval = bss_conf->beacon_int;
+
+	if (changes & BSS_CHANGED_ASSOC) {
+		sc->assoc = bss_conf->assoc;
+		if (sc->opmode == NL80211_IFTYPE_STATION)
+			set_beacon_filter(hw, sc->assoc);
+		ath5k_hw_set_ledstate(sc->ah, sc->assoc ?
+			AR5K_LED_ASSOC : AR5K_LED_INIT);
+		if (bss_conf->assoc) {
+			ATH5K_DBG(sc, ATH5K_DEBUG_ANY,
+				  "Bss Info ASSOC %d, bssid: %pM\n",
+				  bss_conf->aid, common->curbssid);
+			common->curaid = bss_conf->aid;
+			ath5k_hw_set_bssid(ah);
+			/* Once ANI is available you would start it here */
+		}
+	}
+
+	if (changes & BSS_CHANGED_BEACON) {
+		spin_lock_irqsave(&sc->block, flags);
+		ath5k_beacon_update(hw, vif);
+		spin_unlock_irqrestore(&sc->block, flags);
+	}
+
+	if (changes & BSS_CHANGED_BEACON_ENABLED)
+		sc->enable_beacon = bss_conf->enable_beacon;
+
+	if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED |
+		       BSS_CHANGED_BEACON_INT))
+		ath5k_beacon_config(sc);
+
+ unlock:
+	mutex_unlock(&sc->lock);
+}
+
+static void ath5k_sw_scan_start(struct ieee80211_hw *hw)
+{
+	struct ath5k_softc *sc = hw->priv;
+	if (!sc->assoc)
+		ath5k_hw_set_ledstate(sc->ah, AR5K_LED_SCAN);
+}
+
+static void ath5k_sw_scan_complete(struct ieee80211_hw *hw)
+{
+	struct ath5k_softc *sc = hw->priv;
+	ath5k_hw_set_ledstate(sc->ah, sc->assoc ?
+		AR5K_LED_ASSOC : AR5K_LED_INIT);
+}
+
+/**
+ * ath5k_set_coverage_class - Set IEEE 802.11 coverage class
+ *
+ * @hw: struct ieee80211_hw pointer
+ * @coverage_class: IEEE 802.11 coverage class number
+ *
+ * Mac80211 callback. Sets slot time, ACK timeout and CTS timeout for given
+ * coverage class. The values are persistent, they are restored after device
+ * reset.
+ */
+static void ath5k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
+{
+	struct ath5k_softc *sc = hw->priv;
+
+	mutex_lock(&sc->lock);
+	ath5k_hw_set_coverage_class(sc->ah, coverage_class);
+	mutex_unlock(&sc->lock);
+}
+
+static const struct ieee80211_ops ath5k_hw_ops = {
+	.tx 		= ath5k_tx,
+	.start 		= ath5k_start,
+	.stop 		= ath5k_stop,
+	.add_interface 	= ath5k_add_interface,
+	.remove_interface = ath5k_remove_interface,
+	.config 	= ath5k_config,
+	.prepare_multicast = ath5k_prepare_multicast,
+	.configure_filter = ath5k_configure_filter,
+	.set_key 	= ath5k_set_key,
+	.get_stats 	= ath5k_get_stats,
+	.get_survey	= ath5k_get_survey,
+	.conf_tx 	= NULL,
+	.get_tsf 	= ath5k_get_tsf,
+	.set_tsf 	= ath5k_set_tsf,
+	.reset_tsf 	= ath5k_reset_tsf,
+	.bss_info_changed = ath5k_bss_info_changed,
+	.sw_scan_start	= ath5k_sw_scan_start,
+	.sw_scan_complete = ath5k_sw_scan_complete,
+	.set_coverage_class = ath5k_set_coverage_class,
+};
+
+/********************\
+* PCI Initialization *
+\********************/
+
+static int __devinit
+ath5k_pci_probe(struct pci_dev *pdev,
+		const struct pci_device_id *id)
+{
+	void __iomem *mem;
+	struct ath5k_softc *sc;
+	struct ath_common *common;
+	struct ieee80211_hw *hw;
+	int ret;
+	u8 csz;
+
+	/*
+	 * L0s needs to be disabled on all ath5k cards.
+	 *
+	 * For distributions shipping with CONFIG_PCIEASPM (this will be enabled
+	 * by default in the future in 2.6.36) this will also mean both L1 and
+	 * L0s will be disabled when a pre 1.1 PCIe device is detected. We do
+	 * know L1 works correctly even for all ath5k pre 1.1 PCIe devices
+	 * though but cannot currently undue the effect of a blacklist, for
+	 * details you can read pcie_aspm_sanity_check() and see how it adjusts
+	 * the device link capability.
+	 *
+	 * It may be possible in the future to implement some PCI API to allow
+	 * drivers to override blacklists for pre 1.1 PCIe but for now it is
+	 * best to accept that both L0s and L1 will be disabled completely for
+	 * distributions shipping with CONFIG_PCIEASPM rather than having this
+	 * issue present. Motivation for adding this new API will be to help
+	 * with power consumption for some of these devices.
+	 */
+	pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S);
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "can't enable device\n");
+		goto err;
+	}
+
+	/* XXX 32-bit addressing only */
+	ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+	if (ret) {
+		dev_err(&pdev->dev, "32-bit DMA not available\n");
+		goto err_dis;
+	}
+
+	/*
+	 * Cache line size is used to size and align various
+	 * structures used to communicate with the hardware.
+	 */
+	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz);
+	if (csz == 0) {
+		/*
+		 * Linux 2.4.18 (at least) writes the cache line size
+		 * register as a 16-bit wide register which is wrong.
+		 * We must have this setup properly for rx buffer
+		 * DMA to work so force a reasonable value here if it
+		 * comes up zero.
+		 */
+		csz = L1_CACHE_BYTES >> 2;
+		pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz);
+	}
+	/*
+	 * The default setting of latency timer yields poor results,
+	 * set it to the value used by other systems.  It may be worth
+	 * tweaking this setting more.
+	 */
+	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8);
+
+	/* Enable bus mastering */
+	pci_set_master(pdev);
+
+	/*
+	 * Disable the RETRY_TIMEOUT register (0x41) to keep
+	 * PCI Tx retries from interfering with C3 CPU state.
+	 */
+	pci_write_config_byte(pdev, 0x41, 0);
+
+	ret = pci_request_region(pdev, 0, "ath5k");
+	if (ret) {
+		dev_err(&pdev->dev, "cannot reserve PCI memory region\n");
+		goto err_dis;
+	}
+
+	mem = pci_iomap(pdev, 0, 0);
+	if (!mem) {
+		dev_err(&pdev->dev, "cannot remap PCI memory region\n") ;
+		ret = -EIO;
+		goto err_reg;
+	}
+
+	/*
+	 * Allocate hw (mac80211 main struct)
+	 * and hw->priv (driver private data)
+	 */
+	hw = ieee80211_alloc_hw(sizeof(*sc), &ath5k_hw_ops);
+	if (hw == NULL) {
+		dev_err(&pdev->dev, "cannot allocate ieee80211_hw\n");
+		ret = -ENOMEM;
+		goto err_map;
+	}
+
+	dev_info(&pdev->dev, "registered as '%s'\n", wiphy_name(hw->wiphy));
+
+	/* Initialize driver private data */
+	SET_IEEE80211_DEV(hw, &pdev->dev);
+	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
+		    IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
+		    IEEE80211_HW_SIGNAL_DBM;
+
+	hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_AP) |
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC) |
+		BIT(NL80211_IFTYPE_MESH_POINT);
+
+	hw->extra_tx_headroom = 2;
+	hw->channel_change_time = 5000;
+	sc = hw->priv;
+	sc->hw = hw;
+	sc->pdev = pdev;
+
+	ath5k_debug_init_device(sc);
+
+	/*
+	 * Mark the device as detached to avoid processing
+	 * interrupts until setup is complete.
+	 */
+	__set_bit(ATH_STAT_INVALID, sc->status);
+
+	sc->iobase = mem; /* So we can unmap it on detach */
+	sc->opmode = NL80211_IFTYPE_STATION;
+	sc->bintval = 1000;
+	mutex_init(&sc->lock);
+	spin_lock_init(&sc->rxbuflock);
+	spin_lock_init(&sc->txbuflock);
+	spin_lock_init(&sc->block);
+
+	/* Set private data */
+	pci_set_drvdata(pdev, sc);
+
+	/* Setup interrupt handler */
+	ret = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc);
+	if (ret) {
+		ATH5K_ERR(sc, "request_irq failed\n");
+		goto err_free;
+	}
+
+	/* If we passed the test, malloc an ath5k_hw struct */
+	sc->ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL);
+	if (!sc->ah) {
+		ret = -ENOMEM;
+		ATH5K_ERR(sc, "out of memory\n");
+		goto err_irq;
+	}
+
+	sc->ah->ah_sc = sc;
+	sc->ah->ah_iobase = sc->iobase;
+	common = ath5k_hw_common(sc->ah);
+	common->ops = &ath5k_common_ops;
+	common->ah = sc->ah;
+	common->hw = hw;
+	common->cachelsz = csz << 2; /* convert to bytes */
+
+	/* Initialize device */
+	ret = ath5k_hw_attach(sc);
+	if (ret) {
+		goto err_free_ah;
+	}
+
+	/* set up multi-rate retry capabilities */
+	if (sc->ah->ah_version == AR5K_AR5212) {
+		hw->max_rates = 4;
+		hw->max_rate_tries = 11;
+	}
+
+	/* Finish private driver data initialization */
+	ret = ath5k_attach(pdev, hw);
+	if (ret)
+		goto err_ah;
+
+	ATH5K_INFO(sc, "Atheros AR%s chip found (MAC: 0x%x, PHY: 0x%x)\n",
+			ath5k_chip_name(AR5K_VERSION_MAC, sc->ah->ah_mac_srev),
+					sc->ah->ah_mac_srev,
+					sc->ah->ah_phy_revision);
+
+	if (!sc->ah->ah_single_chip) {
+		/* Single chip radio (!RF5111) */
+		if (sc->ah->ah_radio_5ghz_revision &&
+			!sc->ah->ah_radio_2ghz_revision) {
+			/* No 5GHz support -> report 2GHz radio */
+			if (!test_bit(AR5K_MODE_11A,
+				sc->ah->ah_capabilities.cap_mode)) {
+				ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n",
+					ath5k_chip_name(AR5K_VERSION_RAD,
+						sc->ah->ah_radio_5ghz_revision),
+						sc->ah->ah_radio_5ghz_revision);
+			/* No 2GHz support (5110 and some
+			 * 5Ghz only cards) -> report 5Ghz radio */
+			} else if (!test_bit(AR5K_MODE_11B,
+				sc->ah->ah_capabilities.cap_mode)) {
+				ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n",
+					ath5k_chip_name(AR5K_VERSION_RAD,
+						sc->ah->ah_radio_5ghz_revision),
+						sc->ah->ah_radio_5ghz_revision);
+			/* Multiband radio */
+			} else {
+				ATH5K_INFO(sc, "RF%s multiband radio found"
+					" (0x%x)\n",
+					ath5k_chip_name(AR5K_VERSION_RAD,
+						sc->ah->ah_radio_5ghz_revision),
+						sc->ah->ah_radio_5ghz_revision);
+			}
+		}
+		/* Multi chip radio (RF5111 - RF2111) ->
+		 * report both 2GHz/5GHz radios */
+		else if (sc->ah->ah_radio_5ghz_revision &&
+				sc->ah->ah_radio_2ghz_revision){
+			ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n",
+				ath5k_chip_name(AR5K_VERSION_RAD,
+					sc->ah->ah_radio_5ghz_revision),
+					sc->ah->ah_radio_5ghz_revision);
+			ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n",
+				ath5k_chip_name(AR5K_VERSION_RAD,
+					sc->ah->ah_radio_2ghz_revision),
+					sc->ah->ah_radio_2ghz_revision);
+		}
+	}
+
 
-	survey->channel = conf->channel;
-	survey->filled = SURVEY_INFO_NOISE_DBM;
-	survey->noise = sc->ah->ah_noise_floor;
+	/* ready to process interrupts */
+	__clear_bit(ATH_STAT_INVALID, sc->status);
 
 	return 0;
+err_ah:
+	ath5k_hw_detach(sc->ah);
+err_free_ah:
+	kfree(sc->ah);
+err_irq:
+	free_irq(pdev->irq, sc);
+err_free:
+	ieee80211_free_hw(hw);
+err_map:
+	pci_iounmap(pdev, mem);
+err_reg:
+	pci_release_region(pdev, 0);
+err_dis:
+	pci_disable_device(pdev);
+err:
+	return ret;
 }
 
-static u64
-ath5k_get_tsf(struct ieee80211_hw *hw)
+static void __devexit
+ath5k_pci_remove(struct pci_dev *pdev)
 {
-	struct ath5k_softc *sc = hw->priv;
+	struct ath5k_softc *sc = pci_get_drvdata(pdev);
 
-	return ath5k_hw_get_tsf64(sc->ah);
+	ath5k_debug_finish_device(sc);
+	ath5k_detach(pdev, sc->hw);
+	ath5k_hw_detach(sc->ah);
+	kfree(sc->ah);
+	free_irq(pdev->irq, sc);
+	pci_iounmap(pdev, sc->iobase);
+	pci_release_region(pdev, 0);
+	pci_disable_device(pdev);
+	ieee80211_free_hw(sc->hw);
 }
 
-static void
-ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf)
+#ifdef CONFIG_PM_SLEEP
+static int ath5k_pci_suspend(struct device *dev)
 {
-	struct ath5k_softc *sc = hw->priv;
+	struct ath5k_softc *sc = pci_get_drvdata(to_pci_dev(dev));
 
-	ath5k_hw_set_tsf64(sc->ah, tsf);
+	ath5k_led_off(sc);
+	return 0;
 }
 
-static void
-ath5k_reset_tsf(struct ieee80211_hw *hw)
+static int ath5k_pci_resume(struct device *dev)
 {
-	struct ath5k_softc *sc = hw->priv;
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct ath5k_softc *sc = pci_get_drvdata(pdev);
 
 	/*
-	 * in IBSS mode we need to update the beacon timers too.
-	 * this will also reset the TSF if we call it with 0
+	 * Suspend/Resume resets the PCI configuration space, so we have to
+	 * re-disable the RETRY_TIMEOUT register (0x41) to keep
+	 * PCI Tx retries from interfering with C3 CPU state
 	 */
-	if (sc->opmode == NL80211_IFTYPE_ADHOC)
-		ath5k_beacon_update_timers(sc, 0);
-	else
-		ath5k_hw_reset_tsf(sc->ah);
+	pci_write_config_byte(pdev, 0x41, 0);
+
+	ath5k_led_enable(sc);
+	return 0;
 }
 
+static SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend, ath5k_pci_resume);
+#define ATH5K_PM_OPS	(&ath5k_pm_ops)
+#else
+#define ATH5K_PM_OPS	NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct pci_driver ath5k_pci_driver = {
+	.name		= KBUILD_MODNAME,
+	.id_table	= ath5k_pci_id_table,
+	.probe		= ath5k_pci_probe,
+	.remove		= __devexit_p(ath5k_pci_remove),
+	.driver.pm	= ATH5K_PM_OPS,
+};
+
 /*
- * Updates the beacon that is sent by ath5k_beacon_send.  For adhoc,
- * this is called only once at config_bss time, for AP we do it every
- * SWBA interrupt so that the TIM will reflect buffered frames.
- *
- * Called with the beacon lock.
+ * Module init/exit functions
  */
-static int
-ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+static int __init
+init_ath5k_pci(void)
 {
 	int ret;
-	struct ath5k_softc *sc = hw->priv;
-	struct sk_buff *skb;
-
-	if (WARN_ON(!vif)) {
-		ret = -EINVAL;
-		goto out;
-	}
-
-	skb = ieee80211_beacon_get(hw, vif);
-
-	if (!skb) {
-		ret = -ENOMEM;
-		goto out;
-	}
-
-	ath5k_debug_dump_skb(sc, skb, "BC  ", 1);
-
-	ath5k_txbuf_free_skb(sc, sc->bbuf);
-	sc->bbuf->skb = skb;
-	ret = ath5k_beacon_setup(sc, sc->bbuf);
-	if (ret)
-		sc->bbuf->skb = NULL;
-out:
-	return ret;
-}
-
-static void
-set_beacon_filter(struct ieee80211_hw *hw, bool enable)
-{
-	struct ath5k_softc *sc = hw->priv;
-	struct ath5k_hw *ah = sc->ah;
-	u32 rfilt;
-	rfilt = ath5k_hw_get_rx_filter(ah);
-	if (enable)
-		rfilt |= AR5K_RX_FILTER_BEACON;
-	else
-		rfilt &= ~AR5K_RX_FILTER_BEACON;
-	ath5k_hw_set_rx_filter(ah, rfilt);
-	sc->filter_flags = rfilt;
-}
-
-static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
-				    struct ieee80211_vif *vif,
-				    struct ieee80211_bss_conf *bss_conf,
-				    u32 changes)
-{
-	struct ath5k_softc *sc = hw->priv;
-	struct ath5k_hw *ah = sc->ah;
-	struct ath_common *common = ath5k_hw_common(ah);
-	unsigned long flags;
-
-	mutex_lock(&sc->lock);
-	if (WARN_ON(sc->vif != vif))
-		goto unlock;
-
-	if (changes & BSS_CHANGED_BSSID) {
-		/* Cache for later use during resets */
-		memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
-		common->curaid = 0;
-		ath5k_hw_set_bssid(ah);
-		mmiowb();
-	}
-
-	if (changes & BSS_CHANGED_BEACON_INT)
-		sc->bintval = bss_conf->beacon_int;
 
-	if (changes & BSS_CHANGED_ASSOC) {
-		sc->assoc = bss_conf->assoc;
-		if (sc->opmode == NL80211_IFTYPE_STATION)
-			set_beacon_filter(hw, sc->assoc);
-		ath5k_hw_set_ledstate(sc->ah, sc->assoc ?
-			AR5K_LED_ASSOC : AR5K_LED_INIT);
-		if (bss_conf->assoc) {
-			ATH5K_DBG(sc, ATH5K_DEBUG_ANY,
-				  "Bss Info ASSOC %d, bssid: %pM\n",
-				  bss_conf->aid, common->curbssid);
-			common->curaid = bss_conf->aid;
-			ath5k_hw_set_bssid(ah);
-			/* Once ANI is available you would start it here */
-		}
-	}
+	ath5k_debug_init();
 
-	if (changes & BSS_CHANGED_BEACON) {
-		spin_lock_irqsave(&sc->block, flags);
-		ath5k_beacon_update(hw, vif);
-		spin_unlock_irqrestore(&sc->block, flags);
+	ret = pci_register_driver(&ath5k_pci_driver);
+	if (ret) {
+		printk(KERN_ERR "ath5k_pci: can't register pci driver\n");
+		return ret;
 	}
 
-	if (changes & BSS_CHANGED_BEACON_ENABLED)
-		sc->enable_beacon = bss_conf->enable_beacon;
-
-	if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED |
-		       BSS_CHANGED_BEACON_INT))
-		ath5k_beacon_config(sc);
-
- unlock:
-	mutex_unlock(&sc->lock);
+	return 0;
 }
 
-static void ath5k_sw_scan_start(struct ieee80211_hw *hw)
+static void __exit
+exit_ath5k_pci(void)
 {
-	struct ath5k_softc *sc = hw->priv;
-	if (!sc->assoc)
-		ath5k_hw_set_ledstate(sc->ah, AR5K_LED_SCAN);
-}
+	pci_unregister_driver(&ath5k_pci_driver);
 
-static void ath5k_sw_scan_complete(struct ieee80211_hw *hw)
-{
-	struct ath5k_softc *sc = hw->priv;
-	ath5k_hw_set_ledstate(sc->ah, sc->assoc ?
-		AR5K_LED_ASSOC : AR5K_LED_INIT);
+	ath5k_debug_finish();
 }
 
-/**
- * ath5k_set_coverage_class - Set IEEE 802.11 coverage class
- *
- * @hw: struct ieee80211_hw pointer
- * @coverage_class: IEEE 802.11 coverage class number
- *
- * Mac80211 callback. Sets slot time, ACK timeout and CTS timeout for given
- * coverage class. The values are persistent, they are restored after device
- * reset.
- */
-static void ath5k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
-{
-	struct ath5k_softc *sc = hw->priv;
-
-	mutex_lock(&sc->lock);
-	ath5k_hw_set_coverage_class(sc->ah, coverage_class);
-	mutex_unlock(&sc->lock);
-}
+module_init(init_ath5k_pci);
+module_exit(exit_ath5k_pci);
-- 
1.7.1.1



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [ath5k-devel] [PATCH] ath5k: reorder base.c to remove fwd decls
  2010-09-11 19:06 [PATCH] ath5k: reorder base.c to remove fwd decls Bob Copeland
@ 2010-09-11 19:16 ` Bob Copeland
  2010-09-13  1:33 ` Bruno Randolf
  2010-09-16 19:36 ` John W. Linville
  2 siblings, 0 replies; 6+ messages in thread
From: Bob Copeland @ 2010-09-11 19:16 UTC (permalink / raw)
  To: linville, jirislaby, mickflemm, lrodriguez, br1
  Cc: ath5k-devel, linux-wireless

On Sat, Sep 11, 2010 at 3:06 PM, Bob Copeland <me@bobcopeland.com> wrote:
> This change reorganizes the main ath5k file in order to re-group
> related functions and remove most of the forward declarations
> (from 61 down to 3).  This is, unfortunately, a lot of churn, but
> there should be no functional changes.

Forgot to mention, this is based on Bruno's ath?k crypto series.

-- 
Bob Copeland %% www.bobcopeland.com

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] ath5k: reorder base.c to remove fwd decls
  2010-09-11 19:06 [PATCH] ath5k: reorder base.c to remove fwd decls Bob Copeland
  2010-09-11 19:16 ` [ath5k-devel] " Bob Copeland
@ 2010-09-13  1:33 ` Bruno Randolf
  2010-09-16 19:36 ` John W. Linville
  2 siblings, 0 replies; 6+ messages in thread
From: Bruno Randolf @ 2010-09-13  1:33 UTC (permalink / raw)
  To: Bob Copeland
  Cc: linville, jirislaby, mickflemm, lrodriguez, linux-wireless, ath5k-devel

On Sun September 12 2010 04:06:36 Bob Copeland wrote:
> This change reorganizes the main ath5k file in order to re-group
> related functions and remove most of the forward declarations
> (from 61 down to 3).  This is, unfortunately, a lot of churn, but
> there should be no functional changes.
> 
> Signed-off-by: Bob Copeland <me@bobcopeland.com>
> ---
> 
> Worth the churn?  Is there any way to do this kind of patch that
> doesn't suck?

yeah, it's good!

i have a few pending patches which i'll have to re-do but that's fine with me. 
also let's fix the crypto flags in a separate patch after this.

Acked-by: Bruno Randolf <br1@einfach.org>

>  drivers/net/wireless/ath/ath5k/base.c | 3902
> ++++++++++++++++----------------- 1 files changed, 1884 insertions(+),
> 2018 deletions(-)
> 
> diff --git a/drivers/net/wireless/ath/ath5k/base.c
> b/drivers/net/wireless/ath/ath5k/base.c index f8c699d..9e4636f 100644
> --- a/drivers/net/wireless/ath/ath5k/base.c
> +++ b/drivers/net/wireless/ath/ath5k/base.c
> @@ -70,11 +70,6 @@ static int modparam_all_channels;
>  module_param_named(all_channels, modparam_all_channels, bool, S_IRUGO);
>  MODULE_PARM_DESC(all_channels, "Expose all channels the device can use.");
> 
> -
> -/******************\
> -* Internal defines *
> -\******************/
> -
>  /* Module info */
>  MODULE_AUTHOR("Jiri Slaby");
>  MODULE_AUTHOR("Nick Kossifidis");
> @@ -83,6 +78,10 @@ MODULE_SUPPORTED_DEVICE("Atheros 5xxx WLAN cards");
>  MODULE_LICENSE("Dual BSD/GPL");
>  MODULE_VERSION("0.6.0 (EXPERIMENTAL)");
> 
> +static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel
> *chan); +static int ath5k_beacon_update(struct ieee80211_hw *hw,
> +		struct ieee80211_vif *vif);
> +static void ath5k_beacon_update_timers(struct ath5k_softc *sc, u64
> bc_tsf);
> 
>  /* Known PCI ids */
>  static DEFINE_PCI_DEVICE_TABLE(ath5k_pci_id_table) = {
> @@ -190,129 +189,6 @@ static const struct ieee80211_rate ath5k_rates[] = {
>  	/* XR missing */
>  };
> 
> -/*
> - * Prototypes - PCI stack related functions
> - */
> -static int __devinit	ath5k_pci_probe(struct pci_dev *pdev,
> -				const struct pci_device_id *id);
> -static void __devexit	ath5k_pci_remove(struct pci_dev *pdev);
> -#ifdef CONFIG_PM_SLEEP
> -static int		ath5k_pci_suspend(struct device *dev);
> -static int		ath5k_pci_resume(struct device *dev);
> -
> -static SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend,
> ath5k_pci_resume); -#define ATH5K_PM_OPS	(&ath5k_pm_ops)
> -#else
> -#define ATH5K_PM_OPS	NULL
> -#endif /* CONFIG_PM_SLEEP */
> -
> -static struct pci_driver ath5k_pci_driver = {
> -	.name		= KBUILD_MODNAME,
> -	.id_table	= ath5k_pci_id_table,
> -	.probe		= ath5k_pci_probe,
> -	.remove		= __devexit_p(ath5k_pci_remove),
> -	.driver.pm	= ATH5K_PM_OPS,
> -};
> -
> -
> -
> -/*
> - * Prototypes - MAC 802.11 stack related functions
> - */
> -static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
> -static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
> -		struct ath5k_txq *txq);
> -static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel
> *chan); -static int ath5k_start(struct ieee80211_hw *hw);
> -static void ath5k_stop(struct ieee80211_hw *hw);
> -static int ath5k_add_interface(struct ieee80211_hw *hw,
> -		struct ieee80211_vif *vif);
> -static void ath5k_remove_interface(struct ieee80211_hw *hw,
> -		struct ieee80211_vif *vif);
> -static int ath5k_config(struct ieee80211_hw *hw, u32 changed);
> -static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw,
> -				   struct netdev_hw_addr_list *mc_list);
> -static void ath5k_configure_filter(struct ieee80211_hw *hw,
> -		unsigned int changed_flags,
> -		unsigned int *new_flags,
> -		u64 multicast);
> -static int ath5k_set_key(struct ieee80211_hw *hw,
> -		enum set_key_cmd cmd,
> -		struct ieee80211_vif *vif, struct ieee80211_sta *sta,
> -		struct ieee80211_key_conf *key);
> -static int ath5k_get_stats(struct ieee80211_hw *hw,
> -		struct ieee80211_low_level_stats *stats);
> -static int ath5k_get_survey(struct ieee80211_hw *hw,
> -		int idx, struct survey_info *survey);
> -static u64 ath5k_get_tsf(struct ieee80211_hw *hw);
> -static void ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf);
> -static void ath5k_reset_tsf(struct ieee80211_hw *hw);
> -static int ath5k_beacon_update(struct ieee80211_hw *hw,
> -		struct ieee80211_vif *vif);
> -static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
> -		struct ieee80211_vif *vif,
> -		struct ieee80211_bss_conf *bss_conf,
> -		u32 changes);
> -static void ath5k_sw_scan_start(struct ieee80211_hw *hw);
> -static void ath5k_sw_scan_complete(struct ieee80211_hw *hw);
> -static void ath5k_set_coverage_class(struct ieee80211_hw *hw,
> -		u8 coverage_class);
> -
> -static const struct ieee80211_ops ath5k_hw_ops = {
> -	.tx 		= ath5k_tx,
> -	.start 		= ath5k_start,
> -	.stop 		= ath5k_stop,
> -	.add_interface 	= ath5k_add_interface,
> -	.remove_interface = ath5k_remove_interface,
> -	.config 	= ath5k_config,
> -	.prepare_multicast = ath5k_prepare_multicast,
> -	.configure_filter = ath5k_configure_filter,
> -	.set_key 	= ath5k_set_key,
> -	.get_stats 	= ath5k_get_stats,
> -	.get_survey	= ath5k_get_survey,
> -	.conf_tx 	= NULL,
> -	.get_tsf 	= ath5k_get_tsf,
> -	.set_tsf 	= ath5k_set_tsf,
> -	.reset_tsf 	= ath5k_reset_tsf,
> -	.bss_info_changed = ath5k_bss_info_changed,
> -	.sw_scan_start	= ath5k_sw_scan_start,
> -	.sw_scan_complete = ath5k_sw_scan_complete,
> -	.set_coverage_class = ath5k_set_coverage_class,
> -};
> -
> -/*
> - * Prototypes - Internal functions
> - */
> -/* Attach detach */
> -static int 	ath5k_attach(struct pci_dev *pdev,
> -			struct ieee80211_hw *hw);
> -static void 	ath5k_detach(struct pci_dev *pdev,
> -			struct ieee80211_hw *hw);
> -/* Channel/mode setup */
> -static inline short ath5k_ieee2mhz(short chan);
> -static unsigned int ath5k_copy_channels(struct ath5k_hw *ah,
> -				struct ieee80211_channel *channels,
> -				unsigned int mode,
> -				unsigned int max);
> -static int 	ath5k_setup_bands(struct ieee80211_hw *hw);
> -static int 	ath5k_chan_set(struct ath5k_softc *sc,
> -				struct ieee80211_channel *chan);
> -static void	ath5k_setcurmode(struct ath5k_softc *sc,
> -				unsigned int mode);
> -static void	ath5k_mode_setup(struct ath5k_softc *sc);
> -
> -/* Descriptor setup */
> -static int	ath5k_desc_alloc(struct ath5k_softc *sc,
> -				struct pci_dev *pdev);
> -static void	ath5k_desc_free(struct ath5k_softc *sc,
> -				struct pci_dev *pdev);
> -/* Buffers setup */
> -static int 	ath5k_rxbuf_setup(struct ath5k_softc *sc,
> -				struct ath5k_buf *bf);
> -static int 	ath5k_txbuf_setup(struct ath5k_softc *sc,
> -				struct ath5k_buf *bf,
> -				struct ath5k_txq *txq, int padsize);
> -
>  static inline void ath5k_txbuf_free_skb(struct ath5k_softc *sc,
>  				struct ath5k_buf *bf)
>  {
> @@ -345,35 +221,6 @@ static inline void ath5k_rxbuf_free_skb(struct
> ath5k_softc *sc, }
> 
> 
> -/* Queues setup */
> -static struct 	ath5k_txq *ath5k_txq_setup(struct ath5k_softc *sc,
> -				int qtype, int subtype);
> -static int 	ath5k_beaconq_setup(struct ath5k_hw *ah);
> -static int 	ath5k_beaconq_config(struct ath5k_softc *sc);
> -static void 	ath5k_txq_drainq(struct ath5k_softc *sc,
> -				struct ath5k_txq *txq);
> -static void 	ath5k_txq_cleanup(struct ath5k_softc *sc);
> -static void 	ath5k_txq_release(struct ath5k_softc *sc);
> -/* Rx handling */
> -static int 	ath5k_rx_start(struct ath5k_softc *sc);
> -static void 	ath5k_rx_stop(struct ath5k_softc *sc);
> -static unsigned int ath5k_rx_decrypted(struct ath5k_softc *sc,
> -					struct sk_buff *skb,
> -					struct ath5k_rx_status *rs);
> -static void 	ath5k_tasklet_rx(unsigned long data);
> -/* Tx handling */
> -static void 	ath5k_tx_processq(struct ath5k_softc *sc,
> -				struct ath5k_txq *txq);
> -static void 	ath5k_tasklet_tx(unsigned long data);
> -/* Beacon handling */
> -static int 	ath5k_beacon_setup(struct ath5k_softc *sc,
> -					struct ath5k_buf *bf);
> -static void 	ath5k_beacon_send(struct ath5k_softc *sc);
> -static void 	ath5k_beacon_config(struct ath5k_softc *sc);
> -static void	ath5k_beacon_update_timers(struct ath5k_softc *sc, u64
> bc_tsf); -static void	ath5k_tasklet_beacon(unsigned long data);
> -static void	ath5k_tasklet_ani(unsigned long data);
> -
>  static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
>  {
>  	u64 tsf = ath5k_hw_get_tsf64(ah);
> @@ -384,50 +231,6 @@ static inline u64 ath5k_extend_tsf(struct ath5k_hw
> *ah, u32 rstamp) return (tsf & ~0x7fff) | 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_reset_work(struct work_struct *work);
> -
> -static void 	ath5k_tasklet_calibrate(unsigned long data);
> -
> -/*
> - * Module init/exit functions
> - */
> -static int __init
> -init_ath5k_pci(void)
> -{
> -	int ret;
> -
> -	ath5k_debug_init();
> -
> -	ret = pci_register_driver(&ath5k_pci_driver);
> -	if (ret) {
> -		printk(KERN_ERR "ath5k_pci: can't register pci driver\n");
> -		return ret;
> -	}
> -
> -	return 0;
> -}
> -
> -static void __exit
> -exit_ath5k_pci(void)
> -{
> -	pci_unregister_driver(&ath5k_pci_driver);
> -
> -	ath5k_debug_finish();
> -}
> -
> -module_init(init_ath5k_pci);
> -module_exit(exit_ath5k_pci);
> -
> -
> -/********************\
> -* PCI Initialization *
> -\********************/
> -
>  static const char *
>  ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val)
>  {
> @@ -466,1546 +269,1084 @@ static const struct ath_ops ath5k_common_ops = {
>  	.write = ath5k_iowrite32,
>  };
> 
> -static int __devinit
> -ath5k_pci_probe(struct pci_dev *pdev,
> -		const struct pci_device_id *id)
> +/***********************\
> +* Driver Initialization *
> +\***********************/
> +
> +static int ath5k_reg_notifier(struct wiphy *wiphy, struct
> regulatory_request *request) {
> -	void __iomem *mem;
> -	struct ath5k_softc *sc;
> -	struct ath_common *common;
> -	struct ieee80211_hw *hw;
> -	int ret;
> -	u8 csz;
> +	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
> +	struct ath5k_softc *sc = hw->priv;
> +	struct ath_regulatory *regulatory = ath5k_hw_regulatory(sc->ah);
> 
> -	/*
> -	 * L0s needs to be disabled on all ath5k cards.
> -	 *
> -	 * For distributions shipping with CONFIG_PCIEASPM (this will be enabled
> -	 * by default in the future in 2.6.36) this will also mean both L1 and
> -	 * L0s will be disabled when a pre 1.1 PCIe device is detected. We do
> -	 * know L1 works correctly even for all ath5k pre 1.1 PCIe devices
> -	 * though but cannot currently undue the effect of a blacklist, for
> -	 * details you can read pcie_aspm_sanity_check() and see how it adjusts
> -	 * the device link capability.
> -	 *
> -	 * It may be possible in the future to implement some PCI API to allow
> -	 * drivers to override blacklists for pre 1.1 PCIe but for now it is
> -	 * best to accept that both L0s and L1 will be disabled completely for
> -	 * distributions shipping with CONFIG_PCIEASPM rather than having this
> -	 * issue present. Motivation for adding this new API will be to help
> -	 * with power consumption for some of these devices.
> -	 */
> -	pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S);
> +	return ath_reg_notifier_apply(wiphy, request, regulatory);
> +}
> 
> -	ret = pci_enable_device(pdev);
> -	if (ret) {
> -		dev_err(&pdev->dev, "can't enable device\n");
> -		goto err;
> -	}
> +/********************\
> +* Channel/mode setup *
> +\********************/
> 
> -	/* XXX 32-bit addressing only */
> -	ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
> -	if (ret) {
> -		dev_err(&pdev->dev, "32-bit DMA not available\n");
> -		goto err_dis;
> -	}
> +/*
> + * Convert IEEE channel number to MHz frequency.
> + */
> +static inline short
> +ath5k_ieee2mhz(short chan)
> +{
> +	if (chan <= 14 || chan >= 27)
> +		return ieee80211chan2mhz(chan);
> +	else
> +		return 2212 + chan * 20;
> +}
> 
> -	/*
> -	 * Cache line size is used to size and align various
> -	 * structures used to communicate with the hardware.
> -	 */
> -	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz);
> -	if (csz == 0) {
> -		/*
> -		 * Linux 2.4.18 (at least) writes the cache line size
> -		 * register as a 16-bit wide register which is wrong.
> -		 * We must have this setup properly for rx buffer
> -		 * DMA to work so force a reasonable value here if it
> -		 * comes up zero.
> -		 */
> -		csz = L1_CACHE_BYTES >> 2;
> -		pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz);
> -	}
> -	/*
> -	 * The default setting of latency timer yields poor results,
> -	 * set it to the value used by other systems.  It may be worth
> -	 * tweaking this setting more.
> -	 */
> -	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8);
> +/*
> + * Returns true for the channel numbers used without all_channels
> modparam. + */
> +static bool ath5k_is_standard_channel(short chan)
> +{
> +	return ((chan <= 14) ||
> +		/* UNII 1,2 */
> +		((chan & 3) == 0 && chan >= 36 && chan <= 64) ||
> +		/* midband */
> +		((chan & 3) == 0 && chan >= 100 && chan <= 140) ||
> +		/* UNII-3 */
> +		((chan & 3) == 1 && chan >= 149 && chan <= 165));
> +}
> 
> -	/* Enable bus mastering */
> -	pci_set_master(pdev);
> +static unsigned int
> +ath5k_copy_channels(struct ath5k_hw *ah,
> +		struct ieee80211_channel *channels,
> +		unsigned int mode,
> +		unsigned int max)
> +{
> +	unsigned int i, count, size, chfreq, freq, ch;
> 
> -	/*
> -	 * Disable the RETRY_TIMEOUT register (0x41) to keep
> -	 * PCI Tx retries from interfering with C3 CPU state.
> -	 */
> -	pci_write_config_byte(pdev, 0x41, 0);
> +	if (!test_bit(mode, ah->ah_modes))
> +		return 0;
> 
> -	ret = pci_request_region(pdev, 0, "ath5k");
> -	if (ret) {
> -		dev_err(&pdev->dev, "cannot reserve PCI memory region\n");
> -		goto err_dis;
> +	switch (mode) {
> +	case AR5K_MODE_11A:
> +	case AR5K_MODE_11A_TURBO:
> +		/* 1..220, but 2GHz frequencies are filtered by check_channel */
> +		size = 220 ;
> +		chfreq = CHANNEL_5GHZ;
> +		break;
> +	case AR5K_MODE_11B:
> +	case AR5K_MODE_11G:
> +	case AR5K_MODE_11G_TURBO:
> +		size = 26;
> +		chfreq = CHANNEL_2GHZ;
> +		break;
> +	default:
> +		ATH5K_WARN(ah->ah_sc, "bad mode, not copying channels\n");
> +		return 0;
>  	}
> 
> -	mem = pci_iomap(pdev, 0, 0);
> -	if (!mem) {
> -		dev_err(&pdev->dev, "cannot remap PCI memory region\n") ;
> -		ret = -EIO;
> -		goto err_reg;
> -	}
> +	for (i = 0, count = 0; i < size && max > 0; i++) {
> +		ch = i + 1 ;
> +		freq = ath5k_ieee2mhz(ch);
> 
> -	/*
> -	 * Allocate hw (mac80211 main struct)
> -	 * and hw->priv (driver private data)
> -	 */
> -	hw = ieee80211_alloc_hw(sizeof(*sc), &ath5k_hw_ops);
> -	if (hw == NULL) {
> -		dev_err(&pdev->dev, "cannot allocate ieee80211_hw\n");
> -		ret = -ENOMEM;
> -		goto err_map;
> -	}
> -
> -	dev_info(&pdev->dev, "registered as '%s'\n", wiphy_name(hw->wiphy));
> -
> -	/* Initialize driver private data */
> -	SET_IEEE80211_DEV(hw, &pdev->dev);
> -	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
> -		    IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
> -		    IEEE80211_HW_SIGNAL_DBM;
> +		/* Check if channel is supported by the chipset */
> +		if (!ath5k_channel_ok(ah, freq, chfreq))
> +			continue;
> 
> -	hw->wiphy->interface_modes =
> -		BIT(NL80211_IFTYPE_AP) |
> -		BIT(NL80211_IFTYPE_STATION) |
> -		BIT(NL80211_IFTYPE_ADHOC) |
> -		BIT(NL80211_IFTYPE_MESH_POINT);
> +		if (!modparam_all_channels && !ath5k_is_standard_channel(ch))
> +			continue;
> 
> -	hw->extra_tx_headroom = 2;
> -	hw->channel_change_time = 5000;
> -	sc = hw->priv;
> -	sc->hw = hw;
> -	sc->pdev = pdev;
> +		/* Write channel info and increment counter */
> +		channels[count].center_freq = freq;
> +		channels[count].band = (chfreq == CHANNEL_2GHZ) ?
> +			IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
> +		switch (mode) {
> +		case AR5K_MODE_11A:
> +		case AR5K_MODE_11G:
> +			channels[count].hw_value = chfreq | CHANNEL_OFDM;
> +			break;
> +		case AR5K_MODE_11A_TURBO:
> +		case AR5K_MODE_11G_TURBO:
> +			channels[count].hw_value = chfreq |
> +				CHANNEL_OFDM | CHANNEL_TURBO;
> +			break;
> +		case AR5K_MODE_11B:
> +			channels[count].hw_value = CHANNEL_B;
> +		}
> 
> -	ath5k_debug_init_device(sc);
> +		count++;
> +		max--;
> +	}
> 
> -	/*
> -	 * Mark the device as detached to avoid processing
> -	 * interrupts until setup is complete.
> -	 */
> -	__set_bit(ATH_STAT_INVALID, sc->status);
> +	return count;
> +}
> 
> -	sc->iobase = mem; /* So we can unmap it on detach */
> -	sc->opmode = NL80211_IFTYPE_STATION;
> -	sc->bintval = 1000;
> -	mutex_init(&sc->lock);
> -	spin_lock_init(&sc->rxbuflock);
> -	spin_lock_init(&sc->txbuflock);
> -	spin_lock_init(&sc->block);
> +static void
> +ath5k_setup_rate_idx(struct ath5k_softc *sc, struct
> ieee80211_supported_band *b) +{
> +	u8 i;
> 
> -	/* Set private data */
> -	pci_set_drvdata(pdev, sc);
> +	for (i = 0; i < AR5K_MAX_RATES; i++)
> +		sc->rate_idx[b->band][i] = -1;
> 
> -	/* Setup interrupt handler */
> -	ret = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc);
> -	if (ret) {
> -		ATH5K_ERR(sc, "request_irq failed\n");
> -		goto err_free;
> +	for (i = 0; i < b->n_bitrates; i++) {
> +		sc->rate_idx[b->band][b->bitrates[i].hw_value] = i;
> +		if (b->bitrates[i].hw_value_short)
> +			sc->rate_idx[b->band][b->bitrates[i].hw_value_short] = i;
>  	}
> +}
> 
> -	/* If we passed the test, malloc an ath5k_hw struct */
> -	sc->ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL);
> -	if (!sc->ah) {
> -		ret = -ENOMEM;
> -		ATH5K_ERR(sc, "out of memory\n");
> -		goto err_irq;
> -	}
> +static int
> +ath5k_setup_bands(struct ieee80211_hw *hw)
> +{
> +	struct ath5k_softc *sc = hw->priv;
> +	struct ath5k_hw *ah = sc->ah;
> +	struct ieee80211_supported_band *sband;
> +	int max_c, count_c = 0;
> +	int i;
> 
> -	sc->ah->ah_sc = sc;
> -	sc->ah->ah_iobase = sc->iobase;
> -	common = ath5k_hw_common(sc->ah);
> -	common->ops = &ath5k_common_ops;
> -	common->ah = sc->ah;
> -	common->hw = hw;
> -	common->cachelsz = csz << 2; /* convert to bytes */
> +	BUILD_BUG_ON(ARRAY_SIZE(sc->sbands) < IEEE80211_NUM_BANDS);
> +	max_c = ARRAY_SIZE(sc->channels);
> 
> -	/* Initialize device */
> -	ret = ath5k_hw_attach(sc);
> -	if (ret) {
> -		goto err_free_ah;
> -	}
> +	/* 2GHz band */
> +	sband = &sc->sbands[IEEE80211_BAND_2GHZ];
> +	sband->band = IEEE80211_BAND_2GHZ;
> +	sband->bitrates = &sc->rates[IEEE80211_BAND_2GHZ][0];
> 
> -	/* set up multi-rate retry capabilities */
> -	if (sc->ah->ah_version == AR5K_AR5212) {
> -		hw->max_rates = 4;
> -		hw->max_rate_tries = 11;
> -	}
> +	if (test_bit(AR5K_MODE_11G, sc->ah->ah_capabilities.cap_mode)) {
> +		/* G mode */
> +		memcpy(sband->bitrates, &ath5k_rates[0],
> +		       sizeof(struct ieee80211_rate) * 12);
> +		sband->n_bitrates = 12;
> 
> -	/* Finish private driver data initialization */
> -	ret = ath5k_attach(pdev, hw);
> -	if (ret)
> -		goto err_ah;
> +		sband->channels = sc->channels;
> +		sband->n_channels = ath5k_copy_channels(ah, sband->channels,
> +					AR5K_MODE_11G, max_c);
> 
> -	ATH5K_INFO(sc, "Atheros AR%s chip found (MAC: 0x%x, PHY: 0x%x)\n",
> -			ath5k_chip_name(AR5K_VERSION_MAC, sc->ah->ah_mac_srev),
> -					sc->ah->ah_mac_srev,
> -					sc->ah->ah_phy_revision);
> +		hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband;
> +		count_c = sband->n_channels;
> +		max_c -= count_c;
> +	} else if (test_bit(AR5K_MODE_11B, sc->ah->ah_capabilities.cap_mode)) {
> +		/* B mode */
> +		memcpy(sband->bitrates, &ath5k_rates[0],
> +		       sizeof(struct ieee80211_rate) * 4);
> +		sband->n_bitrates = 4;
> 
> -	if (!sc->ah->ah_single_chip) {
> -		/* Single chip radio (!RF5111) */
> -		if (sc->ah->ah_radio_5ghz_revision &&
> -			!sc->ah->ah_radio_2ghz_revision) {
> -			/* No 5GHz support -> report 2GHz radio */
> -			if (!test_bit(AR5K_MODE_11A,
> -				sc->ah->ah_capabilities.cap_mode)) {
> -				ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n",
> -					ath5k_chip_name(AR5K_VERSION_RAD,
> -						sc->ah->ah_radio_5ghz_revision),
> -						sc->ah->ah_radio_5ghz_revision);
> -			/* No 2GHz support (5110 and some
> -			 * 5Ghz only cards) -> report 5Ghz radio */
> -			} else if (!test_bit(AR5K_MODE_11B,
> -				sc->ah->ah_capabilities.cap_mode)) {
> -				ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n",
> -					ath5k_chip_name(AR5K_VERSION_RAD,
> -						sc->ah->ah_radio_5ghz_revision),
> -						sc->ah->ah_radio_5ghz_revision);
> -			/* Multiband radio */
> -			} else {
> -				ATH5K_INFO(sc, "RF%s multiband radio found"
> -					" (0x%x)\n",
> -					ath5k_chip_name(AR5K_VERSION_RAD,
> -						sc->ah->ah_radio_5ghz_revision),
> -						sc->ah->ah_radio_5ghz_revision);
> +		/* 5211 only supports B rates and uses 4bit rate codes
> +		 * (e.g normally we have 0x1B for 1M, but on 5211 we have 0x0B)
> +		 * fix them up here:
> +		 */
> +		if (ah->ah_version == AR5K_AR5211) {
> +			for (i = 0; i < 4; i++) {
> +				sband->bitrates[i].hw_value =
> +					sband->bitrates[i].hw_value & 0xF;
> +				sband->bitrates[i].hw_value_short =
> +					sband->bitrates[i].hw_value_short & 0xF;
>  			}
>  		}
> -		/* Multi chip radio (RF5111 - RF2111) ->
> -		 * report both 2GHz/5GHz radios */
> -		else if (sc->ah->ah_radio_5ghz_revision &&
> -				sc->ah->ah_radio_2ghz_revision){
> -			ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n",
> -				ath5k_chip_name(AR5K_VERSION_RAD,
> -					sc->ah->ah_radio_5ghz_revision),
> -					sc->ah->ah_radio_5ghz_revision);
> -			ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n",
> -				ath5k_chip_name(AR5K_VERSION_RAD,
> -					sc->ah->ah_radio_2ghz_revision),
> -					sc->ah->ah_radio_2ghz_revision);
> -		}
> -	}
> 
> +		sband->channels = sc->channels;
> +		sband->n_channels = ath5k_copy_channels(ah, sband->channels,
> +					AR5K_MODE_11B, max_c);
> 
> -	/* ready to process interrupts */
> -	__clear_bit(ATH_STAT_INVALID, sc->status);
> +		hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband;
> +		count_c = sband->n_channels;
> +		max_c -= count_c;
> +	}
> +	ath5k_setup_rate_idx(sc, sband);
> 
> -	return 0;
> -err_ah:
> -	ath5k_hw_detach(sc->ah);
> -err_free_ah:
> -	kfree(sc->ah);
> -err_irq:
> -	free_irq(pdev->irq, sc);
> -err_free:
> -	ieee80211_free_hw(hw);
> -err_map:
> -	pci_iounmap(pdev, mem);
> -err_reg:
> -	pci_release_region(pdev, 0);
> -err_dis:
> -	pci_disable_device(pdev);
> -err:
> -	return ret;
> -}
> +	/* 5GHz band, A mode */
> +	if (test_bit(AR5K_MODE_11A, sc->ah->ah_capabilities.cap_mode)) {
> +		sband = &sc->sbands[IEEE80211_BAND_5GHZ];
> +		sband->band = IEEE80211_BAND_5GHZ;
> +		sband->bitrates = &sc->rates[IEEE80211_BAND_5GHZ][0];
> 
> -static void __devexit
> -ath5k_pci_remove(struct pci_dev *pdev)
> -{
> -	struct ath5k_softc *sc = pci_get_drvdata(pdev);
> +		memcpy(sband->bitrates, &ath5k_rates[4],
> +		       sizeof(struct ieee80211_rate) * 8);
> +		sband->n_bitrates = 8;
> 
> -	ath5k_debug_finish_device(sc);
> -	ath5k_detach(pdev, sc->hw);
> -	ath5k_hw_detach(sc->ah);
> -	kfree(sc->ah);
> -	free_irq(pdev->irq, sc);
> -	pci_iounmap(pdev, sc->iobase);
> -	pci_release_region(pdev, 0);
> -	pci_disable_device(pdev);
> -	ieee80211_free_hw(sc->hw);
> -}
> +		sband->channels = &sc->channels[count_c];
> +		sband->n_channels = ath5k_copy_channels(ah, sband->channels,
> +					AR5K_MODE_11A, max_c);
> 
> -#ifdef CONFIG_PM_SLEEP
> -static int ath5k_pci_suspend(struct device *dev)
> -{
> -	struct ath5k_softc *sc = pci_get_drvdata(to_pci_dev(dev));
> +		hw->wiphy->bands[IEEE80211_BAND_5GHZ] = sband;
> +	}
> +	ath5k_setup_rate_idx(sc, sband);
> +
> +	ath5k_debug_dump_bands(sc);
> 
> -	ath5k_led_off(sc);
>  	return 0;
>  }
> 
> -static int ath5k_pci_resume(struct device *dev)
> -{
> -	struct pci_dev *pdev = to_pci_dev(dev);
> -	struct ath5k_softc *sc = pci_get_drvdata(pdev);
> -
> +/*
> + * Set/change channels. We always reset the chip.
> + * To accomplish this we must first cleanup any pending DMA,
> + * then restart stuff after a la  ath5k_init.
> + *
> + * Called with sc->lock.
> + */
> +static int
> +ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan)
> +{
> +	ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
> +		  "channel set, resetting (%u -> %u MHz)\n",
> +		  sc->curchan->center_freq, chan->center_freq);
> +
>  	/*
> -	 * Suspend/Resume resets the PCI configuration space, so we have to
> -	 * re-disable the RETRY_TIMEOUT register (0x41) to keep
> -	 * PCI Tx retries from interfering with C3 CPU state
> +	 * To switch channels clear any pending DMA operations;
> +	 * wait long enough for the RX fifo to drain, reset the
> +	 * hardware at the new frequency, and then re-enable
> +	 * the relevant bits of the h/w.
>  	 */
> -	pci_write_config_byte(pdev, 0x41, 0);
> -
> -	ath5k_led_enable(sc);
> -	return 0;
> +	return ath5k_reset(sc, chan);
>  }
> -#endif /* CONFIG_PM_SLEEP */
> -
> -
> -/***********************\
> -* Driver Initialization *
> -\***********************/
> 
> -static int ath5k_reg_notifier(struct wiphy *wiphy, struct
> regulatory_request *request) +static void
> +ath5k_setcurmode(struct ath5k_softc *sc, unsigned int mode)
>  {
> -	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
> -	struct ath5k_softc *sc = hw->priv;
> -	struct ath_regulatory *regulatory = ath5k_hw_regulatory(sc->ah);
> +	sc->curmode = mode;
> 
> -	return ath_reg_notifier_apply(wiphy, request, regulatory);
> +	if (mode == AR5K_MODE_11A) {
> +		sc->curband = &sc->sbands[IEEE80211_BAND_5GHZ];
> +	} else {
> +		sc->curband = &sc->sbands[IEEE80211_BAND_2GHZ];
> +	}
>  }
> 
> -static int
> -ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
> +static void
> +ath5k_mode_setup(struct ath5k_softc *sc)
>  {
> -	struct ath5k_softc *sc = hw->priv;
>  	struct ath5k_hw *ah = sc->ah;
> -	struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah);
> -	u8 mac[ETH_ALEN] = {};
> -	int ret;
> +	u32 rfilt;
> 
> -	ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "devid 0x%x\n", pdev->device);
> +	/* configure rx filter */
> +	rfilt = sc->filter_flags;
> +	ath5k_hw_set_rx_filter(ah, rfilt);
> 
> -	/*
> -	 * Check if the MAC has multi-rate retry support.
> -	 * We do this by trying to setup a fake extended
> -	 * descriptor.  MACs that don't have support will
> -	 * return false w/o doing anything.  MACs that do
> -	 * support it will return true w/o doing anything.
> -	 */
> -	ret = ath5k_hw_setup_mrr_tx_desc(ah, NULL, 0, 0, 0, 0, 0, 0);
> +	if (ath5k_hw_hasbssidmask(ah))
> +		ath5k_hw_set_bssid_mask(ah, sc->bssidmask);
> 
> -	if (ret < 0)
> -		goto err;
> -	if (ret > 0)
> -		__set_bit(ATH_STAT_MRRETRY, sc->status);
> +	/* configure operational mode */
> +	ath5k_hw_set_opmode(ah, sc->opmode);
> 
> -	/*
> -	 * Collect the channel list.  The 802.11 layer
> -	 * is resposible for filtering this list based
> -	 * on settings like the phy mode and regulatory
> -	 * domain restrictions.
> -	 */
> -	ret = ath5k_setup_bands(hw);
> -	if (ret) {
> -		ATH5K_ERR(sc, "can't get channels\n");
> -		goto err;
> -	}
> +	ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "mode setup opmode %d\n", sc->opmode);
> +	ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt);
> +}
> 
> -	/* NB: setup here so ath5k_rate_update is happy */
> -	if (test_bit(AR5K_MODE_11A, ah->ah_modes))
> -		ath5k_setcurmode(sc, AR5K_MODE_11A);
> -	else
> -		ath5k_setcurmode(sc, AR5K_MODE_11B);
> +static inline int
> +ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix)
> +{
> +	int rix;
> 
> -	/*
> -	 * Allocate tx+rx descriptors and populate the lists.
> -	 */
> -	ret = ath5k_desc_alloc(sc, pdev);
> -	if (ret) {
> -		ATH5K_ERR(sc, "can't allocate descriptors\n");
> -		goto err;
> -	}
> +	/* return base rate on errors */
> +	if (WARN(hw_rix < 0 || hw_rix >= AR5K_MAX_RATES,
> +			"hw_rix out of bounds: %x\n", hw_rix))
> +		return 0;
> +
> +	rix = sc->rate_idx[sc->curband->band][hw_rix];
> +	if (WARN(rix < 0, "invalid hw_rix: %x\n", hw_rix))
> +		rix = 0;
> +
> +	return rix;
> +}
> +
> +/***************\
> +* Buffers setup *
> +\***************/
> +
> +static
> +struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_softc *sc, dma_addr_t
> *skb_addr) +{
> +	struct ath_common *common = ath5k_hw_common(sc->ah);
> +	struct sk_buff *skb;
> 
>  	/*
> -	 * Allocate hardware transmit queues: one queue for
> -	 * beacon frames and one data queue for each QoS
> -	 * priority.  Note that hw functions handle resetting
> -	 * these queues at the needed time.
> +	 * Allocate buffer with headroom_needed space for the
> +	 * fake physical layer header at the start.
>  	 */
> -	ret = ath5k_beaconq_setup(ah);
> -	if (ret < 0) {
> -		ATH5K_ERR(sc, "can't setup a beacon xmit queue\n");
> -		goto err_desc;
> -	}
> -	sc->bhalq = ret;
> -	sc->cabq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_CAB, 0);
> -	if (IS_ERR(sc->cabq)) {
> -		ATH5K_ERR(sc, "can't setup cab queue\n");
> -		ret = PTR_ERR(sc->cabq);
> -		goto err_bhal;
> -	}
> +	skb = ath_rxbuf_alloc(common,
> +			      common->rx_bufsize,
> +			      GFP_ATOMIC);
> 
> -	sc->txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK);
> -	if (IS_ERR(sc->txq)) {
> -		ATH5K_ERR(sc, "can't setup xmit queue\n");
> -		ret = PTR_ERR(sc->txq);
> -		goto err_queues;
> +	if (!skb) {
> +		ATH5K_ERR(sc, "can't alloc skbuff of size %u\n",
> +				common->rx_bufsize);
> +		return NULL;
>  	}
> 
> -	tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc);
> -	tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc);
> -	tasklet_init(&sc->calib, ath5k_tasklet_calibrate, (unsigned long)sc);
> -	tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc);
> -	tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc);
> -
> -	INIT_WORK(&sc->reset_work, ath5k_reset_work);
> -
> -	ret = ath5k_eeprom_read_mac(ah, mac);
> -	if (ret) {
> -		ATH5K_ERR(sc, "unable to read address from EEPROM: 0x%04x\n",
> -			sc->pdev->device);
> -		goto err_queues;
> +	*skb_addr = pci_map_single(sc->pdev,
> +				   skb->data, common->rx_bufsize,
> +				   PCI_DMA_FROMDEVICE);
> +	if (unlikely(pci_dma_mapping_error(sc->pdev, *skb_addr))) {
> +		ATH5K_ERR(sc, "%s: DMA mapping failed\n", __func__);
> +		dev_kfree_skb(skb);
> +		return NULL;
>  	}
> +	return skb;
> +}
> 
> -	SET_IEEE80211_PERM_ADDR(hw, mac);
> -	/* All MAC address bits matter for ACKs */
> -	memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN);
> -	ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask);
> +static int
> +ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
> +{
> +	struct ath5k_hw *ah = sc->ah;
> +	struct sk_buff *skb = bf->skb;
> +	struct ath5k_desc *ds;
> +	int ret;
> 
> -	regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain;
> -	ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier);
> -	if (ret) {
> -		ATH5K_ERR(sc, "can't initialize regulatory system\n");
> -		goto err_queues;
> +	if (!skb) {
> +		skb = ath5k_rx_skb_alloc(sc, &bf->skbaddr);
> +		if (!skb)
> +			return -ENOMEM;
> +		bf->skb = skb;
>  	}
> 
> -	ret = ieee80211_register_hw(hw);
> +	/*
> +	 * Setup descriptors.  For receive we always terminate
> +	 * the descriptor list with a self-linked entry so we'll
> +	 * not get overrun under high load (as can happen with a
> +	 * 5212 when ANI processing enables PHY error frames).
> +	 *
> +	 * To ensure the last descriptor is self-linked we create
> +	 * each descriptor as self-linked and add it to the end.  As
> +	 * each additional descriptor is added the previous self-linked
> +	 * entry is "fixed" naturally.  This should be safe even
> +	 * if DMA is happening.  When processing RX interrupts we
> +	 * never remove/process the last, self-linked, entry on the
> +	 * descriptor list.  This ensures the hardware always has
> +	 * someplace to write a new frame.
> +	 */
> +	ds = bf->desc;
> +	ds->ds_link = bf->daddr;	/* link to self */
> +	ds->ds_data = bf->skbaddr;
> +	ret = ath5k_hw_setup_rx_desc(ah, ds, ah->common.rx_bufsize, 0);
>  	if (ret) {
> -		ATH5K_ERR(sc, "can't register ieee80211 hw\n");
> -		goto err_queues;
> +		ATH5K_ERR(sc, "%s: could not setup RX desc\n", __func__);
> +		return ret;
>  	}
> 
> -	if (!ath_is_world_regd(regulatory))
> -		regulatory_hint(hw->wiphy, regulatory->alpha2);
> -
> -	ath5k_init_leds(sc);
> -
> -	ath5k_sysfs_register(sc);
> -
> +	if (sc->rxlink != NULL)
> +		*sc->rxlink = bf->daddr;
> +	sc->rxlink = &ds->ds_link;
>  	return 0;
> -err_queues:
> -	ath5k_txq_release(sc);
> -err_bhal:
> -	ath5k_hw_release_tx_queue(ah, sc->bhalq);
> -err_desc:
> -	ath5k_desc_free(sc, pdev);
> -err:
> -	return ret;
>  }
> 
> -static void
> -ath5k_detach(struct pci_dev *pdev, struct ieee80211_hw *hw)
> +static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb)
>  {
> -	struct ath5k_softc *sc = hw->priv;
> +	struct ieee80211_hdr *hdr;
> +	enum ath5k_pkt_type htype;
> +	__le16 fc;
> 
> -	/*
> -	 * NB: the order of these is important:
> -	 * o call the 802.11 layer before detaching ath5k_hw to
> -	 *   ensure callbacks into the driver to delete global
> -	 *   key cache entries can be handled
> -	 * o reclaim the tx queue data structures after calling
> -	 *   the 802.11 layer as we'll get called back to reclaim
> -	 *   node state and potentially want to use them
> -	 * o to cleanup the tx queues the hal is called, so detach
> -	 *   it last
> -	 * XXX: ??? detach ath5k_hw ???
> -	 * Other than that, it's straightforward...
> -	 */
> -	ieee80211_unregister_hw(hw);
> -	ath5k_desc_free(sc, pdev);
> -	ath5k_txq_release(sc);
> -	ath5k_hw_release_tx_queue(sc->ah, sc->bhalq);
> -	ath5k_unregister_leds(sc);
> -
> -	ath5k_sysfs_unregister(sc);
> -	/*
> -	 * NB: can't reclaim these until after ieee80211_ifdetach
> -	 * returns because we'll get called back to reclaim node
> -	 * state and potentially want to use them.
> -	 */
> -}
> -
> -
> -
> -
> -/********************\
> -* Channel/mode setup *
> -\********************/
> +	hdr = (struct ieee80211_hdr *)skb->data;
> +	fc = hdr->frame_control;
> 
> -/*
> - * Convert IEEE channel number to MHz frequency.
> - */
> -static inline short
> -ath5k_ieee2mhz(short chan)
> -{
> -	if (chan <= 14 || chan >= 27)
> -		return ieee80211chan2mhz(chan);
> +	if (ieee80211_is_beacon(fc))
> +		htype = AR5K_PKT_TYPE_BEACON;
> +	else if (ieee80211_is_probe_resp(fc))
> +		htype = AR5K_PKT_TYPE_PROBE_RESP;
> +	else if (ieee80211_is_atim(fc))
> +		htype = AR5K_PKT_TYPE_ATIM;
> +	else if (ieee80211_is_pspoll(fc))
> +		htype = AR5K_PKT_TYPE_PSPOLL;
>  	else
> -		return 2212 + chan * 20;
> -}
> +		htype = AR5K_PKT_TYPE_NORMAL;
> 
> -/*
> - * Returns true for the channel numbers used without all_channels
> modparam. - */
> -static bool ath5k_is_standard_channel(short chan)
> -{
> -	return ((chan <= 14) ||
> -		/* UNII 1,2 */
> -		((chan & 3) == 0 && chan >= 36 && chan <= 64) ||
> -		/* midband */
> -		((chan & 3) == 0 && chan >= 100 && chan <= 140) ||
> -		/* UNII-3 */
> -		((chan & 3) == 1 && chan >= 149 && chan <= 165));
> +	return htype;
>  }
> 
> -static unsigned int
> -ath5k_copy_channels(struct ath5k_hw *ah,
> -		struct ieee80211_channel *channels,
> -		unsigned int mode,
> -		unsigned int max)
> +static int
> +ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
> +		  struct ath5k_txq *txq, int padsize)
>  {
> -	unsigned int i, count, size, chfreq, freq, ch;
> +	struct ath5k_hw *ah = sc->ah;
> +	struct ath5k_desc *ds = bf->desc;
> +	struct sk_buff *skb = bf->skb;
> +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> +	unsigned int pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID;
> +	struct ieee80211_rate *rate;
> +	unsigned int mrr_rate[3], mrr_tries[3];
> +	int i, ret;
> +	u16 hw_rate;
> +	u16 cts_rate = 0;
> +	u16 duration = 0;
> +	u8 rc_flags;
> 
> -	if (!test_bit(mode, ah->ah_modes))
> -		return 0;
> +	flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK;
> 
> -	switch (mode) {
> -	case AR5K_MODE_11A:
> -	case AR5K_MODE_11A_TURBO:
> -		/* 1..220, but 2GHz frequencies are filtered by check_channel */
> -		size = 220 ;
> -		chfreq = CHANNEL_5GHZ;
> -		break;
> -	case AR5K_MODE_11B:
> -	case AR5K_MODE_11G:
> -	case AR5K_MODE_11G_TURBO:
> -		size = 26;
> -		chfreq = CHANNEL_2GHZ;
> -		break;
> -	default:
> -		ATH5K_WARN(ah->ah_sc, "bad mode, not copying channels\n");
> -		return 0;
> +	/* XXX endianness */
> +	bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len,
> +			PCI_DMA_TODEVICE);
> +
> +	rate = ieee80211_get_tx_rate(sc->hw, info);
> +	if (!rate) {
> +		ret = -EINVAL;
> +		goto err_unmap;
>  	}
> 
> -	for (i = 0, count = 0; i < size && max > 0; i++) {
> -		ch = i + 1 ;
> -		freq = ath5k_ieee2mhz(ch);
> +	if (info->flags & IEEE80211_TX_CTL_NO_ACK)
> +		flags |= AR5K_TXDESC_NOACK;
> 
> -		/* Check if channel is supported by the chipset */
> -		if (!ath5k_channel_ok(ah, freq, chfreq))
> -			continue;
> +	rc_flags = info->control.rates[0].flags;
> +	hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ?
> +		rate->hw_value_short : rate->hw_value;
> 
> -		if (!modparam_all_channels && !ath5k_is_standard_channel(ch))
> -			continue;
> +	pktlen = skb->len;
> 
> -		/* Write channel info and increment counter */
> -		channels[count].center_freq = freq;
> -		channels[count].band = (chfreq == CHANNEL_2GHZ) ?
> -			IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
> -		switch (mode) {
> -		case AR5K_MODE_11A:
> -		case AR5K_MODE_11G:
> -			channels[count].hw_value = chfreq | CHANNEL_OFDM;
> -			break;
> -		case AR5K_MODE_11A_TURBO:
> -		case AR5K_MODE_11G_TURBO:
> -			channels[count].hw_value = chfreq |
> -				CHANNEL_OFDM | CHANNEL_TURBO;
> +	/* FIXME: If we are in g mode and rate is a CCK rate
> +	 * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta
> +	 * from tx power (value is in dB units already) */
> +	if (info->control.hw_key) {
> +		keyidx = info->control.hw_key->hw_key_idx;
> +		pktlen += info->control.hw_key->icv_len;
> +	}
> +	if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
> +		flags |= AR5K_TXDESC_RTSENA;
> +		cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value;
> +		duration = le16_to_cpu(ieee80211_rts_duration(sc->hw,
> +			sc->vif, pktlen, info));
> +	}
> +	if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
> +		flags |= AR5K_TXDESC_CTSENA;
> +		cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value;
> +		duration = le16_to_cpu(ieee80211_ctstoself_duration(sc->hw,
> +			sc->vif, pktlen, info));
> +	}
> +	ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
> +		ieee80211_get_hdrlen_from_skb(skb), padsize,
> +		get_hw_packet_type(skb),
> +		(sc->power_level * 2),
> +		hw_rate,
> +		info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags,
> +		cts_rate, duration);
> +	if (ret)
> +		goto err_unmap;
> +
> +	memset(mrr_rate, 0, sizeof(mrr_rate));
> +	memset(mrr_tries, 0, sizeof(mrr_tries));
> +	for (i = 0; i < 3; i++) {
> +		rate = ieee80211_get_alt_retry_rate(sc->hw, info, i);
> +		if (!rate)
>  			break;
> -		case AR5K_MODE_11B:
> -			channels[count].hw_value = CHANNEL_B;
> -		}
> 
> -		count++;
> -		max--;
> +		mrr_rate[i] = rate->hw_value;
> +		mrr_tries[i] = info->control.rates[i + 1].count;
>  	}
> 
> -	return count;
> -}
> +	ath5k_hw_setup_mrr_tx_desc(ah, ds,
> +		mrr_rate[0], mrr_tries[0],
> +		mrr_rate[1], mrr_tries[1],
> +		mrr_rate[2], mrr_tries[2]);
> 
> -static void
> -ath5k_setup_rate_idx(struct ath5k_softc *sc, struct
> ieee80211_supported_band *b) -{
> -	u8 i;
> +	ds->ds_link = 0;
> +	ds->ds_data = bf->skbaddr;
> 
> -	for (i = 0; i < AR5K_MAX_RATES; i++)
> -		sc->rate_idx[b->band][i] = -1;
> +	spin_lock_bh(&txq->lock);
> +	list_add_tail(&bf->list, &txq->q);
> +	if (txq->link == NULL) /* is this first packet? */
> +		ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr);
> +	else /* no, so only link it */
> +		*txq->link = bf->daddr;
> 
> -	for (i = 0; i < b->n_bitrates; i++) {
> -		sc->rate_idx[b->band][b->bitrates[i].hw_value] = i;
> -		if (b->bitrates[i].hw_value_short)
> -			sc->rate_idx[b->band][b->bitrates[i].hw_value_short] = i;
> -	}
> +	txq->link = &ds->ds_link;
> +	ath5k_hw_start_tx_dma(ah, txq->qnum);
> +	mmiowb();
> +	spin_unlock_bh(&txq->lock);
> +
> +	return 0;
> +err_unmap:
> +	pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE);
> +	return ret;
>  }
> 
> +/*******************\
> +* Descriptors setup *
> +\*******************/
> +
>  static int
> -ath5k_setup_bands(struct ieee80211_hw *hw)
> +ath5k_desc_alloc(struct ath5k_softc *sc, struct pci_dev *pdev)
>  {
> -	struct ath5k_softc *sc = hw->priv;
> -	struct ath5k_hw *ah = sc->ah;
> -	struct ieee80211_supported_band *sband;
> -	int max_c, count_c = 0;
> -	int i;
> +	struct ath5k_desc *ds;
> +	struct ath5k_buf *bf;
> +	dma_addr_t da;
> +	unsigned int i;
> +	int ret;
> 
> -	BUILD_BUG_ON(ARRAY_SIZE(sc->sbands) < IEEE80211_NUM_BANDS);
> -	max_c = ARRAY_SIZE(sc->channels);
> +	/* allocate descriptors */
> +	sc->desc_len = sizeof(struct ath5k_desc) *
> +			(ATH_TXBUF + ATH_RXBUF + ATH_BCBUF + 1);
> +	sc->desc = pci_alloc_consistent(pdev, sc->desc_len, &sc->desc_daddr);
> +	if (sc->desc == NULL) {
> +		ATH5K_ERR(sc, "can't allocate descriptors\n");
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +	ds = sc->desc;
> +	da = sc->desc_daddr;
> +	ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "DMA map: %p (%zu) -> %llx\n",
> +		ds, sc->desc_len, (unsigned long long)sc->desc_daddr);
> 
> -	/* 2GHz band */
> -	sband = &sc->sbands[IEEE80211_BAND_2GHZ];
> -	sband->band = IEEE80211_BAND_2GHZ;
> -	sband->bitrates = &sc->rates[IEEE80211_BAND_2GHZ][0];
> +	bf = kcalloc(1 + ATH_TXBUF + ATH_RXBUF + ATH_BCBUF,
> +			sizeof(struct ath5k_buf), GFP_KERNEL);
> +	if (bf == NULL) {
> +		ATH5K_ERR(sc, "can't allocate bufptr\n");
> +		ret = -ENOMEM;
> +		goto err_free;
> +	}
> +	sc->bufptr = bf;
> 
> -	if (test_bit(AR5K_MODE_11G, sc->ah->ah_capabilities.cap_mode)) {
> -		/* G mode */
> -		memcpy(sband->bitrates, &ath5k_rates[0],
> -		       sizeof(struct ieee80211_rate) * 12);
> -		sband->n_bitrates = 12;
> -
> -		sband->channels = sc->channels;
> -		sband->n_channels = ath5k_copy_channels(ah, sband->channels,
> -					AR5K_MODE_11G, max_c);
> -
> -		hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband;
> -		count_c = sband->n_channels;
> -		max_c -= count_c;
> -	} else if (test_bit(AR5K_MODE_11B, sc->ah->ah_capabilities.cap_mode)) {
> -		/* B mode */
> -		memcpy(sband->bitrates, &ath5k_rates[0],
> -		       sizeof(struct ieee80211_rate) * 4);
> -		sband->n_bitrates = 4;
> -
> -		/* 5211 only supports B rates and uses 4bit rate codes
> -		 * (e.g normally we have 0x1B for 1M, but on 5211 we have 0x0B)
> -		 * fix them up here:
> -		 */
> -		if (ah->ah_version == AR5K_AR5211) {
> -			for (i = 0; i < 4; i++) {
> -				sband->bitrates[i].hw_value =
> -					sband->bitrates[i].hw_value & 0xF;
> -				sband->bitrates[i].hw_value_short =
> -					sband->bitrates[i].hw_value_short & 0xF;
> -			}
> -		}
> -
> -		sband->channels = sc->channels;
> -		sband->n_channels = ath5k_copy_channels(ah, sband->channels,
> -					AR5K_MODE_11B, max_c);
> -
> -		hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband;
> -		count_c = sband->n_channels;
> -		max_c -= count_c;
> +	INIT_LIST_HEAD(&sc->rxbuf);
> +	for (i = 0; i < ATH_RXBUF; i++, bf++, ds++, da += sizeof(*ds)) {
> +		bf->desc = ds;
> +		bf->daddr = da;
> +		list_add_tail(&bf->list, &sc->rxbuf);
>  	}
> -	ath5k_setup_rate_idx(sc, sband);
> -
> -	/* 5GHz band, A mode */
> -	if (test_bit(AR5K_MODE_11A, sc->ah->ah_capabilities.cap_mode)) {
> -		sband = &sc->sbands[IEEE80211_BAND_5GHZ];
> -		sband->band = IEEE80211_BAND_5GHZ;
> -		sband->bitrates = &sc->rates[IEEE80211_BAND_5GHZ][0];
> -
> -		memcpy(sband->bitrates, &ath5k_rates[4],
> -		       sizeof(struct ieee80211_rate) * 8);
> -		sband->n_bitrates = 8;
> -
> -		sband->channels = &sc->channels[count_c];
> -		sband->n_channels = ath5k_copy_channels(ah, sband->channels,
> -					AR5K_MODE_11A, max_c);
> 
> -		hw->wiphy->bands[IEEE80211_BAND_5GHZ] = sband;
> +	INIT_LIST_HEAD(&sc->txbuf);
> +	sc->txbuf_len = ATH_TXBUF;
> +	for (i = 0; i < ATH_TXBUF; i++, bf++, ds++,
> +			da += sizeof(*ds)) {
> +		bf->desc = ds;
> +		bf->daddr = da;
> +		list_add_tail(&bf->list, &sc->txbuf);
>  	}
> -	ath5k_setup_rate_idx(sc, sband);
> 
> -	ath5k_debug_dump_bands(sc);
> +	/* beacon buffer */
> +	bf->desc = ds;
> +	bf->daddr = da;
> +	sc->bbuf = bf;
> 
>  	return 0;
> -}
> -
> -/*
> - * Set/change channels. We always reset the chip.
> - * To accomplish this we must first cleanup any pending DMA,
> - * then restart stuff after a la  ath5k_init.
> - *
> - * Called with sc->lock.
> - */
> -static int
> -ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan)
> -{
> -	ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
> -		  "channel set, resetting (%u -> %u MHz)\n",
> -		  sc->curchan->center_freq, chan->center_freq);
> -
> -	/*
> -	 * To switch channels clear any pending DMA operations;
> -	 * wait long enough for the RX fifo to drain, reset the
> -	 * hardware at the new frequency, and then re-enable
> -	 * the relevant bits of the h/w.
> -	 */
> -	return ath5k_reset(sc, chan);
> -}
> -
> -static void
> -ath5k_setcurmode(struct ath5k_softc *sc, unsigned int mode)
> -{
> -	sc->curmode = mode;
> -
> -	if (mode == AR5K_MODE_11A) {
> -		sc->curband = &sc->sbands[IEEE80211_BAND_5GHZ];
> -	} else {
> -		sc->curband = &sc->sbands[IEEE80211_BAND_2GHZ];
> -	}
> +err_free:
> +	pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
> +err:
> +	sc->desc = NULL;
> +	return ret;
>  }
> 
>  static void
> -ath5k_mode_setup(struct ath5k_softc *sc)
> +ath5k_desc_free(struct ath5k_softc *sc, struct pci_dev *pdev)
>  {
> -	struct ath5k_hw *ah = sc->ah;
> -	u32 rfilt;
> -
> -	/* configure rx filter */
> -	rfilt = sc->filter_flags;
> -	ath5k_hw_set_rx_filter(ah, rfilt);
> +	struct ath5k_buf *bf;
> 
> -	if (ath5k_hw_hasbssidmask(ah))
> -		ath5k_hw_set_bssid_mask(ah, sc->bssidmask);
> +	ath5k_txbuf_free_skb(sc, sc->bbuf);
> +	list_for_each_entry(bf, &sc->txbuf, list)
> +		ath5k_txbuf_free_skb(sc, bf);
> +	list_for_each_entry(bf, &sc->rxbuf, list)
> +		ath5k_rxbuf_free_skb(sc, bf);
> 
> -	/* configure operational mode */
> -	ath5k_hw_set_opmode(ah, sc->opmode);
> +	/* Free memory associated with all descriptors */
> +	pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
> +	sc->desc = NULL;
> +	sc->desc_daddr = 0;
> 
> -	ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "mode setup opmode %d\n", sc->opmode);
> -	ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt);
> +	kfree(sc->bufptr);
> +	sc->bufptr = NULL;
> +	sc->bbuf = NULL;
>  }
> 
> -static inline int
> -ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix)
> -{
> -	int rix;
> -
> -	/* return base rate on errors */
> -	if (WARN(hw_rix < 0 || hw_rix >= AR5K_MAX_RATES,
> -			"hw_rix out of bounds: %x\n", hw_rix))
> -		return 0;
> -
> -	rix = sc->rate_idx[sc->curband->band][hw_rix];
> -	if (WARN(rix < 0, "invalid hw_rix: %x\n", hw_rix))
> -		rix = 0;
> -
> -	return rix;
> -}
> 
> -/***************\
> -* Buffers setup *
> -\***************/
> +/**************\
> +* Queues setup *
> +\**************/
> 
> -static
> -struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_softc *sc, dma_addr_t
> *skb_addr) +static struct ath5k_txq *
> +ath5k_txq_setup(struct ath5k_softc *sc,
> +		int qtype, int subtype)
>  {
> -	struct ath_common *common = ath5k_hw_common(sc->ah);
> -	struct sk_buff *skb;
> +	struct ath5k_hw *ah = sc->ah;
> +	struct ath5k_txq *txq;
> +	struct ath5k_txq_info qi = {
> +		.tqi_subtype = subtype,
> +		.tqi_aifs = AR5K_TXQ_USEDEFAULT,
> +		.tqi_cw_min = AR5K_TXQ_USEDEFAULT,
> +		.tqi_cw_max = AR5K_TXQ_USEDEFAULT
> +	};
> +	int qnum;
> 
>  	/*
> -	 * Allocate buffer with headroom_needed space for the
> -	 * fake physical layer header at the start.
> +	 * Enable interrupts only for EOL and DESC conditions.
> +	 * We mark tx descriptors to receive a DESC interrupt
> +	 * when a tx queue gets deep; otherwise we wait for the
> +	 * EOL to reap descriptors.  Note that this is done to
> +	 * reduce interrupt load and this only defers reaping
> +	 * descriptors, never transmitting frames.  Aside from
> +	 * reducing interrupts this also permits more concurrency.
> +	 * The only potential downside is if the tx queue backs
> +	 * up in which case the top half of the kernel may backup
> +	 * due to a lack of tx descriptors.
>  	 */
> -	skb = ath_rxbuf_alloc(common,
> -			      common->rx_bufsize,
> -			      GFP_ATOMIC);
> -
> -	if (!skb) {
> -		ATH5K_ERR(sc, "can't alloc skbuff of size %u\n",
> -				common->rx_bufsize);
> -		return NULL;
> +	qi.tqi_flags = AR5K_TXQ_FLAG_TXEOLINT_ENABLE |
> +				AR5K_TXQ_FLAG_TXDESCINT_ENABLE;
> +	qnum = ath5k_hw_setup_tx_queue(ah, qtype, &qi);
> +	if (qnum < 0) {
> +		/*
> +		 * NB: don't print a message, this happens
> +		 * normally on parts with too few tx queues
> +		 */
> +		return ERR_PTR(qnum);
>  	}
> -
> -	*skb_addr = pci_map_single(sc->pdev,
> -				   skb->data, common->rx_bufsize,
> -				   PCI_DMA_FROMDEVICE);
> -	if (unlikely(pci_dma_mapping_error(sc->pdev, *skb_addr))) {
> -		ATH5K_ERR(sc, "%s: DMA mapping failed\n", __func__);
> -		dev_kfree_skb(skb);
> -		return NULL;
> +	if (qnum >= ARRAY_SIZE(sc->txqs)) {
> +		ATH5K_ERR(sc, "hw qnum %u out of range, max %tu!\n",
> +			qnum, ARRAY_SIZE(sc->txqs));
> +		ath5k_hw_release_tx_queue(ah, qnum);
> +		return ERR_PTR(-EINVAL);
>  	}
> -	return skb;
> +	txq = &sc->txqs[qnum];
> +	if (!txq->setup) {
> +		txq->qnum = qnum;
> +		txq->link = NULL;
> +		INIT_LIST_HEAD(&txq->q);
> +		spin_lock_init(&txq->lock);
> +		txq->setup = true;
> +	}
> +	return &sc->txqs[qnum];
>  }
> 
>  static int
> -ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
> +ath5k_beaconq_setup(struct ath5k_hw *ah)
> +{
> +	struct ath5k_txq_info qi = {
> +		.tqi_aifs = AR5K_TXQ_USEDEFAULT,
> +		.tqi_cw_min = AR5K_TXQ_USEDEFAULT,
> +		.tqi_cw_max = AR5K_TXQ_USEDEFAULT,
> +		/* NB: for dynamic turbo, don't enable any other interrupts */
> +		.tqi_flags = AR5K_TXQ_FLAG_TXDESCINT_ENABLE
> +	};
> +
> +	return ath5k_hw_setup_tx_queue(ah, AR5K_TX_QUEUE_BEACON, &qi);
> +}
> +
> +static int
> +ath5k_beaconq_config(struct ath5k_softc *sc)
>  {
>  	struct ath5k_hw *ah = sc->ah;
> -	struct sk_buff *skb = bf->skb;
> -	struct ath5k_desc *ds;
> +	struct ath5k_txq_info qi;
>  	int ret;
> 
> -	if (!skb) {
> -		skb = ath5k_rx_skb_alloc(sc, &bf->skbaddr);
> -		if (!skb)
> -			return -ENOMEM;
> -		bf->skb = skb;
> -	}
> +	ret = ath5k_hw_get_tx_queueprops(ah, sc->bhalq, &qi);
> +	if (ret)
> +		goto err;
> 
> -	/*
> -	 * Setup descriptors.  For receive we always terminate
> -	 * the descriptor list with a self-linked entry so we'll
> -	 * not get overrun under high load (as can happen with a
> -	 * 5212 when ANI processing enables PHY error frames).
> -	 *
> -	 * To ensure the last descriptor is self-linked we create
> -	 * each descriptor as self-linked and add it to the end.  As
> -	 * each additional descriptor is added the previous self-linked
> -	 * entry is "fixed" naturally.  This should be safe even
> -	 * if DMA is happening.  When processing RX interrupts we
> -	 * never remove/process the last, self-linked, entry on the
> -	 * descriptor list.  This ensures the hardware always has
> -	 * someplace to write a new frame.
> -	 */
> -	ds = bf->desc;
> -	ds->ds_link = bf->daddr;	/* link to self */
> -	ds->ds_data = bf->skbaddr;
> -	ret = ath5k_hw_setup_rx_desc(ah, ds, ah->common.rx_bufsize, 0);
> +	if (sc->opmode == NL80211_IFTYPE_AP ||
> +		sc->opmode == NL80211_IFTYPE_MESH_POINT) {
> +		/*
> +		 * Always burst out beacon and CAB traffic
> +		 * (aifs = cwmin = cwmax = 0)
> +		 */
> +		qi.tqi_aifs = 0;
> +		qi.tqi_cw_min = 0;
> +		qi.tqi_cw_max = 0;
> +	} else if (sc->opmode == NL80211_IFTYPE_ADHOC) {
> +		/*
> +		 * Adhoc mode; backoff between 0 and (2 * cw_min).
> +		 */
> +		qi.tqi_aifs = 0;
> +		qi.tqi_cw_min = 0;
> +		qi.tqi_cw_max = 2 * ah->ah_cw_min;
> +	}
> +
> +	ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
> +		"beacon queueprops tqi_aifs:%d tqi_cw_min:%d tqi_cw_max:%d\n",
> +		qi.tqi_aifs, qi.tqi_cw_min, qi.tqi_cw_max);
> +
> +	ret = ath5k_hw_set_tx_queueprops(ah, sc->bhalq, &qi);
>  	if (ret) {
> -		ATH5K_ERR(sc, "%s: could not setup RX desc\n", __func__);
> -		return ret;
> +		ATH5K_ERR(sc, "%s: unable to update parameters for beacon "
> +			"hardware queue!\n", __func__);
> +		goto err;
>  	}
> +	ret = ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */
> +	if (ret)
> +		goto err;
> 
> -	if (sc->rxlink != NULL)
> -		*sc->rxlink = bf->daddr;
> -	sc->rxlink = &ds->ds_link;
> -	return 0;
> +	/* reconfigure cabq with ready time to 80% of beacon_interval */
> +	ret = ath5k_hw_get_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);
> +	if (ret)
> +		goto err;
> +
> +	qi.tqi_ready_time = (sc->bintval * 80) / 100;
> +	ret = ath5k_hw_set_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);
> +	if (ret)
> +		goto err;
> +
> +	ret = ath5k_hw_reset_tx_queue(ah, AR5K_TX_QUEUE_ID_CAB);
> +err:
> +	return ret;
>  }
> 
> -static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb)
> +static void
> +ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq)
>  {
> -	struct ieee80211_hdr *hdr;
> -	enum ath5k_pkt_type htype;
> -	__le16 fc;
> +	struct ath5k_buf *bf, *bf0;
> 
> -	hdr = (struct ieee80211_hdr *)skb->data;
> -	fc = hdr->frame_control;
> +	/*
> +	 * NB: this assumes output has been stopped and
> +	 *     we do not need to block ath5k_tx_tasklet
> +	 */
> +	spin_lock_bh(&txq->lock);
> +	list_for_each_entry_safe(bf, bf0, &txq->q, list) {
> +		ath5k_debug_printtxbuf(sc, bf);
> 
> -	if (ieee80211_is_beacon(fc))
> -		htype = AR5K_PKT_TYPE_BEACON;
> -	else if (ieee80211_is_probe_resp(fc))
> -		htype = AR5K_PKT_TYPE_PROBE_RESP;
> -	else if (ieee80211_is_atim(fc))
> -		htype = AR5K_PKT_TYPE_ATIM;
> -	else if (ieee80211_is_pspoll(fc))
> -		htype = AR5K_PKT_TYPE_PSPOLL;
> -	else
> -		htype = AR5K_PKT_TYPE_NORMAL;
> +		ath5k_txbuf_free_skb(sc, bf);
> 
> -	return htype;
> +		spin_lock_bh(&sc->txbuflock);
> +		list_move_tail(&bf->list, &sc->txbuf);
> +		sc->txbuf_len++;
> +		spin_unlock_bh(&sc->txbuflock);
> +	}
> +	txq->link = NULL;
> +	spin_unlock_bh(&txq->lock);
>  }
> 
> -static int
> -ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
> -		  struct ath5k_txq *txq, int padsize)
> +/*
> + * Drain the transmit queues and reclaim resources.
> + */
> +static void
> +ath5k_txq_cleanup(struct ath5k_softc *sc)
>  {
>  	struct ath5k_hw *ah = sc->ah;
> -	struct ath5k_desc *ds = bf->desc;
> -	struct sk_buff *skb = bf->skb;
> -	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> -	unsigned int pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID;
> -	struct ieee80211_rate *rate;
> -	unsigned int mrr_rate[3], mrr_tries[3];
> -	int i, ret;
> -	u16 hw_rate;
> -	u16 cts_rate = 0;
> -	u16 duration = 0;
> -	u8 rc_flags;
> -
> -	flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK;
> -
> -	/* XXX endianness */
> -	bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len,
> -			PCI_DMA_TODEVICE);
> +	unsigned int i;
> 
> -	rate = ieee80211_get_tx_rate(sc->hw, info);
> -	if (!rate) {
> -		ret = -EINVAL;
> -		goto err_unmap;
> +	/* XXX return value */
> +	if (likely(!test_bit(ATH_STAT_INVALID, sc->status))) {
> +		/* don't touch the hardware if marked invalid */
> +		ath5k_hw_stop_tx_dma(ah, sc->bhalq);
> +		ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "beacon queue %x\n",
> +			ath5k_hw_get_txdp(ah, sc->bhalq));
> +		for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
> +			if (sc->txqs[i].setup) {
> +				ath5k_hw_stop_tx_dma(ah, sc->txqs[i].qnum);
> +				ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "txq [%u] %x, "
> +					"link %p\n",
> +					sc->txqs[i].qnum,
> +					ath5k_hw_get_txdp(ah,
> +							sc->txqs[i].qnum),
> +					sc->txqs[i].link);
> +			}
>  	}
> 
> -	if (info->flags & IEEE80211_TX_CTL_NO_ACK)
> -		flags |= AR5K_TXDESC_NOACK;
> +	for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
> +		if (sc->txqs[i].setup)
> +			ath5k_txq_drainq(sc, &sc->txqs[i]);
> +}
> 
> -	rc_flags = info->control.rates[0].flags;
> -	hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ?
> -		rate->hw_value_short : rate->hw_value;
> +static void
> +ath5k_txq_release(struct ath5k_softc *sc)
> +{
> +	struct ath5k_txq *txq = sc->txqs;
> +	unsigned int i;
> 
> -	pktlen = skb->len;
> +	for (i = 0; i < ARRAY_SIZE(sc->txqs); i++, txq++)
> +		if (txq->setup) {
> +			ath5k_hw_release_tx_queue(sc->ah, txq->qnum);
> +			txq->setup = false;
> +		}
> +}
> 
> -	/* FIXME: If we are in g mode and rate is a CCK rate
> -	 * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta
> -	 * from tx power (value is in dB units already) */
> -	if (info->control.hw_key) {
> -		keyidx = info->control.hw_key->hw_key_idx;
> -		pktlen += info->control.hw_key->icv_len;
> -	}
> -	if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
> -		flags |= AR5K_TXDESC_RTSENA;
> -		cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value;
> -		duration = le16_to_cpu(ieee80211_rts_duration(sc->hw,
> -			sc->vif, pktlen, info));
> -	}
> -	if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
> -		flags |= AR5K_TXDESC_CTSENA;
> -		cts_rate = ieee80211_get_rts_cts_rate(sc->hw, info)->hw_value;
> -		duration = le16_to_cpu(ieee80211_ctstoself_duration(sc->hw,
> -			sc->vif, pktlen, info));
> -	}
> -	ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
> -		ieee80211_get_hdrlen_from_skb(skb), padsize,
> -		get_hw_packet_type(skb),
> -		(sc->power_level * 2),
> -		hw_rate,
> -		info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags,
> -		cts_rate, duration);
> -	if (ret)
> -		goto err_unmap;
> 
> -	memset(mrr_rate, 0, sizeof(mrr_rate));
> -	memset(mrr_tries, 0, sizeof(mrr_tries));
> -	for (i = 0; i < 3; i++) {
> -		rate = ieee80211_get_alt_retry_rate(sc->hw, info, i);
> -		if (!rate)
> -			break;
> +/*************\
> +* RX Handling *
> +\*************/
> 
> -		mrr_rate[i] = rate->hw_value;
> -		mrr_tries[i] = info->control.rates[i + 1].count;
> -	}
> +/*
> + * Enable the receive h/w following a reset.
> + */
> +static int
> +ath5k_rx_start(struct ath5k_softc *sc)
> +{
> +	struct ath5k_hw *ah = sc->ah;
> +	struct ath_common *common = ath5k_hw_common(ah);
> +	struct ath5k_buf *bf;
> +	int ret;
> 
> -	ath5k_hw_setup_mrr_tx_desc(ah, ds,
> -		mrr_rate[0], mrr_tries[0],
> -		mrr_rate[1], mrr_tries[1],
> -		mrr_rate[2], mrr_tries[2]);
> +	common->rx_bufsize = roundup(IEEE80211_MAX_FRAME_LEN, common->cachelsz);
> 
> -	ds->ds_link = 0;
> -	ds->ds_data = bf->skbaddr;
> +	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n",
> +		  common->cachelsz, common->rx_bufsize);
> 
> -	spin_lock_bh(&txq->lock);
> -	list_add_tail(&bf->list, &txq->q);
> -	if (txq->link == NULL) /* is this first packet? */
> -		ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr);
> -	else /* no, so only link it */
> -		*txq->link = bf->daddr;
> +	spin_lock_bh(&sc->rxbuflock);
> +	sc->rxlink = NULL;
> +	list_for_each_entry(bf, &sc->rxbuf, list) {
> +		ret = ath5k_rxbuf_setup(sc, bf);
> +		if (ret != 0) {
> +			spin_unlock_bh(&sc->rxbuflock);
> +			goto err;
> +		}
> +	}
> +	bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list);
> +	ath5k_hw_set_rxdp(ah, bf->daddr);
> +	spin_unlock_bh(&sc->rxbuflock);
> 
> -	txq->link = &ds->ds_link;
> -	ath5k_hw_start_tx_dma(ah, txq->qnum);
> -	mmiowb();
> -	spin_unlock_bh(&txq->lock);
> +	ath5k_hw_start_rx_dma(ah);	/* enable recv descriptors */
> +	ath5k_mode_setup(sc);		/* set filters, etc. */
> +	ath5k_hw_start_rx_pcu(ah);	/* re-enable PCU/DMA engine */
> 
>  	return 0;
> -err_unmap:
> -	pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE);
> +err:
>  	return ret;
>  }
> 
> -/*******************\
> -* Descriptors setup *
> -\*******************/
> -
> -static int
> -ath5k_desc_alloc(struct ath5k_softc *sc, struct pci_dev *pdev)
> +/*
> + * Disable the receive h/w in preparation for a reset.
> + */
> +static void
> +ath5k_rx_stop(struct ath5k_softc *sc)
>  {
> -	struct ath5k_desc *ds;
> -	struct ath5k_buf *bf;
> -	dma_addr_t da;
> -	unsigned int i;
> -	int ret;
> +	struct ath5k_hw *ah = sc->ah;
> 
> -	/* allocate descriptors */
> -	sc->desc_len = sizeof(struct ath5k_desc) *
> -			(ATH_TXBUF + ATH_RXBUF + ATH_BCBUF + 1);
> -	sc->desc = pci_alloc_consistent(pdev, sc->desc_len, &sc->desc_daddr);
> -	if (sc->desc == NULL) {
> -		ATH5K_ERR(sc, "can't allocate descriptors\n");
> -		ret = -ENOMEM;
> -		goto err;
> -	}
> -	ds = sc->desc;
> -	da = sc->desc_daddr;
> -	ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "DMA map: %p (%zu) -> %llx\n",
> -		ds, sc->desc_len, (unsigned long long)sc->desc_daddr);
> +	ath5k_hw_stop_rx_pcu(ah);	/* disable PCU */
> +	ath5k_hw_set_rx_filter(ah, 0);	/* clear recv filter */
> +	ath5k_hw_stop_rx_dma(ah);	/* disable DMA engine */
> 
> -	bf = kcalloc(1 + ATH_TXBUF + ATH_RXBUF + ATH_BCBUF,
> -			sizeof(struct ath5k_buf), GFP_KERNEL);
> -	if (bf == NULL) {
> -		ATH5K_ERR(sc, "can't allocate bufptr\n");
> -		ret = -ENOMEM;
> -		goto err_free;
> -	}
> -	sc->bufptr = bf;
> +	ath5k_debug_printrxbuffs(sc, ah);
> +}
> 
> -	INIT_LIST_HEAD(&sc->rxbuf);
> -	for (i = 0; i < ATH_RXBUF; i++, bf++, ds++, da += sizeof(*ds)) {
> -		bf->desc = ds;
> -		bf->daddr = da;
> -		list_add_tail(&bf->list, &sc->rxbuf);
> -	}
> +static unsigned int
> +ath5k_rx_decrypted(struct ath5k_softc *sc, struct sk_buff *skb,
> +		   struct ath5k_rx_status *rs)
> +{
> +	struct ath5k_hw *ah = sc->ah;
> +	struct ath_common *common = ath5k_hw_common(ah);
> +	struct ieee80211_hdr *hdr = (void *)skb->data;
> +	unsigned int keyix, hlen;
> 
> -	INIT_LIST_HEAD(&sc->txbuf);
> -	sc->txbuf_len = ATH_TXBUF;
> -	for (i = 0; i < ATH_TXBUF; i++, bf++, ds++,
> -			da += sizeof(*ds)) {
> -		bf->desc = ds;
> -		bf->daddr = da;
> -		list_add_tail(&bf->list, &sc->txbuf);
> -	}
> +	if (!(rs->rs_status & AR5K_RXERR_DECRYPT) &&
> +			rs->rs_keyix != AR5K_RXKEYIX_INVALID)
> +		return RX_FLAG_DECRYPTED;
> 
> -	/* beacon buffer */
> -	bf->desc = ds;
> -	bf->daddr = da;
> -	sc->bbuf = bf;
> +	/* Apparently when a default key is used to decrypt the packet
> +	   the hw does not set the index used to decrypt.  In such cases
> +	   get the index from the packet. */
> +	hlen = ieee80211_hdrlen(hdr->frame_control);
> +	if (ieee80211_has_protected(hdr->frame_control) &&
> +	    !(rs->rs_status & AR5K_RXERR_DECRYPT) &&
> +	    skb->len >= hlen + 4) {
> +		keyix = skb->data[hlen + 3] >> 6;
> +
> +		if (test_bit(keyix, common->keymap))
> +			return RX_FLAG_DECRYPTED;
> +	}
> 
>  	return 0;
> -err_free:
> -	pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
> -err:
> -	sc->desc = NULL;
> -	return ret;
>  }
> 
> +
>  static void
> -ath5k_desc_free(struct ath5k_softc *sc, struct pci_dev *pdev)
> +ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb,
> +		     struct ieee80211_rx_status *rxs)
>  {
> -	struct ath5k_buf *bf;
> +	struct ath_common *common = ath5k_hw_common(sc->ah);
> +	u64 tsf, bc_tstamp;
> +	u32 hw_tu;
> +	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
> 
> -	ath5k_txbuf_free_skb(sc, sc->bbuf);
> -	list_for_each_entry(bf, &sc->txbuf, list)
> -		ath5k_txbuf_free_skb(sc, bf);
> -	list_for_each_entry(bf, &sc->rxbuf, list)
> -		ath5k_rxbuf_free_skb(sc, bf);
> +	if (ieee80211_is_beacon(mgmt->frame_control) &&
> +	    le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS &&
> +	    memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) == 0) {
> +		/*
> +		 * Received an IBSS beacon with the same BSSID. Hardware *must*
> +		 * have updated the local TSF. We have to work around various
> +		 * hardware bugs, though...
> +		 */
> +		tsf = ath5k_hw_get_tsf64(sc->ah);
> +		bc_tstamp = le64_to_cpu(mgmt->u.beacon.timestamp);
> +		hw_tu = TSF_TO_TU(tsf);
> 
> -	/* Free memory associated with all descriptors */
> -	pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
> -	sc->desc = NULL;
> -	sc->desc_daddr = 0;
> +		ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
> +			"beacon %llx mactime %llx (diff %lld) tsf now %llx\n",
> +			(unsigned long long)bc_tstamp,
> +			(unsigned long long)rxs->mactime,
> +			(unsigned long long)(rxs->mactime - bc_tstamp),
> +			(unsigned long long)tsf);
> 
> -	kfree(sc->bufptr);
> -	sc->bufptr = NULL;
> -	sc->bbuf = NULL;
> -}
> +		/*
> +		 * Sometimes the HW will give us a wrong tstamp in the rx
> +		 * status, causing the timestamp extension to go wrong.
> +		 * (This seems to happen especially with beacon frames bigger
> +		 * than 78 byte (incl. FCS))
> +		 * But we know that the receive timestamp must be later than the
> +		 * timestamp of the beacon since HW must have synced to that.
> +		 *
> +		 * NOTE: here we assume mactime to be after the frame was
> +		 * received, not like mac80211 which defines it at the start.
> +		 */
> +		if (bc_tstamp > rxs->mactime) {
> +			ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
> +				"fixing mactime from %llx to %llx\n",
> +				(unsigned long long)rxs->mactime,
> +				(unsigned long long)tsf);
> +			rxs->mactime = tsf;
> +		}
> 
> +		/*
> +		 * Local TSF might have moved higher than our beacon timers,
> +		 * in that case we have to update them to continue sending
> +		 * beacons. This also takes care of synchronizing beacon sending
> +		 * times with other stations.
> +		 */
> +		if (hw_tu >= sc->nexttbtt)
> +			ath5k_beacon_update_timers(sc, bc_tstamp);
> +	}
> +}
> 
> +static void
> +ath5k_update_beacon_rssi(struct ath5k_softc *sc, struct sk_buff *skb, int
> rssi) +{
> +	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
> +	struct ath5k_hw *ah = sc->ah;
> +	struct ath_common *common = ath5k_hw_common(ah);
> 
> +	/* only beacons from our BSSID */
> +	if (!ieee80211_is_beacon(mgmt->frame_control) ||
> +	    memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) != 0)
> +		return;
> 
> +	ah->ah_beacon_rssi_avg = ath5k_moving_average(ah->ah_beacon_rssi_avg,
> +						      rssi);
> 
> -/**************\
> -* Queues setup *
> -\**************/
> +	/* in IBSS mode we should keep RSSI statistics per neighbour */
> +	/* le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS */
> +}
> 
> -static struct ath5k_txq *
> -ath5k_txq_setup(struct ath5k_softc *sc,
> -		int qtype, int subtype)
> +/*
> + * Compute padding position. skb must contain an IEEE 802.11 frame
> + */
> +static int ath5k_common_padpos(struct sk_buff *skb)
>  {
> -	struct ath5k_hw *ah = sc->ah;
> -	struct ath5k_txq *txq;
> -	struct ath5k_txq_info qi = {
> -		.tqi_subtype = subtype,
> -		.tqi_aifs = AR5K_TXQ_USEDEFAULT,
> -		.tqi_cw_min = AR5K_TXQ_USEDEFAULT,
> -		.tqi_cw_max = AR5K_TXQ_USEDEFAULT
> -	};
> -	int qnum;
> +	struct ieee80211_hdr * hdr = (struct ieee80211_hdr *)skb->data;
> +	__le16 frame_control = hdr->frame_control;
> +	int padpos = 24;
> 
> -	/*
> -	 * Enable interrupts only for EOL and DESC conditions.
> -	 * We mark tx descriptors to receive a DESC interrupt
> -	 * when a tx queue gets deep; otherwise we wait for the
> -	 * EOL to reap descriptors.  Note that this is done to
> -	 * reduce interrupt load and this only defers reaping
> -	 * descriptors, never transmitting frames.  Aside from
> -	 * reducing interrupts this also permits more concurrency.
> -	 * The only potential downside is if the tx queue backs
> -	 * up in which case the top half of the kernel may backup
> -	 * due to a lack of tx descriptors.
> -	 */
> -	qi.tqi_flags = AR5K_TXQ_FLAG_TXEOLINT_ENABLE |
> -				AR5K_TXQ_FLAG_TXDESCINT_ENABLE;
> -	qnum = ath5k_hw_setup_tx_queue(ah, qtype, &qi);
> -	if (qnum < 0) {
> -		/*
> -		 * NB: don't print a message, this happens
> -		 * normally on parts with too few tx queues
> -		 */
> -		return ERR_PTR(qnum);
> -	}
> -	if (qnum >= ARRAY_SIZE(sc->txqs)) {
> -		ATH5K_ERR(sc, "hw qnum %u out of range, max %tu!\n",
> -			qnum, ARRAY_SIZE(sc->txqs));
> -		ath5k_hw_release_tx_queue(ah, qnum);
> -		return ERR_PTR(-EINVAL);
> +	if (ieee80211_has_a4(frame_control)) {
> +		padpos += ETH_ALEN;
>  	}
> -	txq = &sc->txqs[qnum];
> -	if (!txq->setup) {
> -		txq->qnum = qnum;
> -		txq->link = NULL;
> -		INIT_LIST_HEAD(&txq->q);
> -		spin_lock_init(&txq->lock);
> -		txq->setup = true;
> +	if (ieee80211_is_data_qos(frame_control)) {
> +		padpos += IEEE80211_QOS_CTL_LEN;
>  	}
> -	return &sc->txqs[qnum];
> -}
> -
> -static int
> -ath5k_beaconq_setup(struct ath5k_hw *ah)
> -{
> -	struct ath5k_txq_info qi = {
> -		.tqi_aifs = AR5K_TXQ_USEDEFAULT,
> -		.tqi_cw_min = AR5K_TXQ_USEDEFAULT,
> -		.tqi_cw_max = AR5K_TXQ_USEDEFAULT,
> -		/* NB: for dynamic turbo, don't enable any other interrupts */
> -		.tqi_flags = AR5K_TXQ_FLAG_TXDESCINT_ENABLE
> -	};
> 
> -	return ath5k_hw_setup_tx_queue(ah, AR5K_TX_QUEUE_BEACON, &qi);
> +	return padpos;
>  }
> 
> -static int
> -ath5k_beaconq_config(struct ath5k_softc *sc)
> +/*
> + * This function expects an 802.11 frame and returns the number of
> + * bytes added, or -1 if we don't have enough header room.
> + */
> +static int ath5k_add_padding(struct sk_buff *skb)
>  {
> -	struct ath5k_hw *ah = sc->ah;
> -	struct ath5k_txq_info qi;
> -	int ret;
> +	int padpos = ath5k_common_padpos(skb);
> +	int padsize = padpos & 3;
> 
> -	ret = ath5k_hw_get_tx_queueprops(ah, sc->bhalq, &qi);
> -	if (ret)
> -		goto err;
> -
> -	if (sc->opmode == NL80211_IFTYPE_AP ||
> -		sc->opmode == NL80211_IFTYPE_MESH_POINT) {
> -		/*
> -		 * Always burst out beacon and CAB traffic
> -		 * (aifs = cwmin = cwmax = 0)
> -		 */
> -		qi.tqi_aifs = 0;
> -		qi.tqi_cw_min = 0;
> -		qi.tqi_cw_max = 0;
> -	} else if (sc->opmode == NL80211_IFTYPE_ADHOC) {
> -		/*
> -		 * Adhoc mode; backoff between 0 and (2 * cw_min).
> -		 */
> -		qi.tqi_aifs = 0;
> -		qi.tqi_cw_min = 0;
> -		qi.tqi_cw_max = 2 * ah->ah_cw_min;
> -	}
> +	if (padsize && skb->len>padpos) {
> 
> -	ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
> -		"beacon queueprops tqi_aifs:%d tqi_cw_min:%d tqi_cw_max:%d\n",
> -		qi.tqi_aifs, qi.tqi_cw_min, qi.tqi_cw_max);
> +		if (skb_headroom(skb) < padsize)
> +			return -1;
> 
> -	ret = ath5k_hw_set_tx_queueprops(ah, sc->bhalq, &qi);
> -	if (ret) {
> -		ATH5K_ERR(sc, "%s: unable to update parameters for beacon "
> -			"hardware queue!\n", __func__);
> -		goto err;
> +		skb_push(skb, padsize);
> +		memmove(skb->data, skb->data+padsize, padpos);
> +		return padsize;
>  	}
> -	ret = ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */
> -	if (ret)
> -		goto err;
> -
> -	/* reconfigure cabq with ready time to 80% of beacon_interval */
> -	ret = ath5k_hw_get_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);
> -	if (ret)
> -		goto err;
> -
> -	qi.tqi_ready_time = (sc->bintval * 80) / 100;
> -	ret = ath5k_hw_set_tx_queueprops(ah, AR5K_TX_QUEUE_ID_CAB, &qi);
> -	if (ret)
> -		goto err;
> -
> -	ret = ath5k_hw_reset_tx_queue(ah, AR5K_TX_QUEUE_ID_CAB);
> -err:
> -	return ret;
> -}
> -
> -static void
> -ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq)
> -{
> -	struct ath5k_buf *bf, *bf0;
> -
> -	/*
> -	 * NB: this assumes output has been stopped and
> -	 *     we do not need to block ath5k_tx_tasklet
> -	 */
> -	spin_lock_bh(&txq->lock);
> -	list_for_each_entry_safe(bf, bf0, &txq->q, list) {
> -		ath5k_debug_printtxbuf(sc, bf);
> -
> -		ath5k_txbuf_free_skb(sc, bf);
> 
> -		spin_lock_bh(&sc->txbuflock);
> -		list_move_tail(&bf->list, &sc->txbuf);
> -		sc->txbuf_len++;
> -		spin_unlock_bh(&sc->txbuflock);
> -	}
> -	txq->link = NULL;
> -	spin_unlock_bh(&txq->lock);
> +	return 0;
>  }
> 
>  /*
> - * Drain the transmit queues and reclaim resources.
> + * The MAC header is padded to have 32-bit boundary if the
> + * packet payload is non-zero. The general calculation for
> + * padsize would take into account odd header lengths:
> + * padsize = 4 - (hdrlen & 3); however, since only
> + * even-length headers are used, padding can only be 0 or 2
> + * bytes and we can optimize this a bit.  We must not try to
> + * remove padding from short control frames that do not have a
> + * payload.
> + *
> + * This function expects an 802.11 frame and returns the number of
> + * bytes removed.
>   */
> -static void
> -ath5k_txq_cleanup(struct ath5k_softc *sc)
> +static int ath5k_remove_padding(struct sk_buff *skb)
>  {
> -	struct ath5k_hw *ah = sc->ah;
> -	unsigned int i;
> +	int padpos = ath5k_common_padpos(skb);
> +	int padsize = padpos & 3;
> 
> -	/* XXX return value */
> -	if (likely(!test_bit(ATH_STAT_INVALID, sc->status))) {
> -		/* don't touch the hardware if marked invalid */
> -		ath5k_hw_stop_tx_dma(ah, sc->bhalq);
> -		ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "beacon queue %x\n",
> -			ath5k_hw_get_txdp(ah, sc->bhalq));
> -		for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
> -			if (sc->txqs[i].setup) {
> -				ath5k_hw_stop_tx_dma(ah, sc->txqs[i].qnum);
> -				ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "txq [%u] %x, "
> -					"link %p\n",
> -					sc->txqs[i].qnum,
> -					ath5k_hw_get_txdp(ah,
> -							sc->txqs[i].qnum),
> -					sc->txqs[i].link);
> -			}
> +	if (padsize && skb->len>=padpos+padsize) {
> +		memmove(skb->data + padsize, skb->data, padpos);
> +		skb_pull(skb, padsize);
> +		return padsize;
>  	}
> 
> -	for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
> -		if (sc->txqs[i].setup)
> -			ath5k_txq_drainq(sc, &sc->txqs[i]);
> +	return 0;
>  }
> 
>  static void
> -ath5k_txq_release(struct ath5k_softc *sc)
> +ath5k_receive_frame(struct ath5k_softc *sc, struct sk_buff *skb,
> +		    struct ath5k_rx_status *rs)
>  {
> -	struct ath5k_txq *txq = sc->txqs;
> -	unsigned int i;
> +	struct ieee80211_rx_status *rxs;
> 
> -	for (i = 0; i < ARRAY_SIZE(sc->txqs); i++, txq++)
> -		if (txq->setup) {
> -			ath5k_hw_release_tx_queue(sc->ah, txq->qnum);
> -			txq->setup = false;
> -		}
> -}
> +	ath5k_remove_padding(skb);
> 
> +	rxs = IEEE80211_SKB_RXCB(skb);
> 
> +	rxs->flag = 0;
> +	if (unlikely(rs->rs_status & AR5K_RXERR_MIC))
> +		rxs->flag |= RX_FLAG_MMIC_ERROR;
> 
> +	/*
> +	 * always extend the mac timestamp, since this information is
> +	 * also needed for proper IBSS merging.
> +	 *
> +	 * XXX: it might be too late to do it here, since rs_tstamp is
> +	 * 15bit only. that means TSF extension has to be done within
> +	 * 32768usec (about 32ms). it might be necessary to move this to
> +	 * the interrupt handler, like it is done in madwifi.
> +	 *
> +	 * Unfortunately we don't know when the hardware takes the rx
> +	 * timestamp (beginning of phy frame, data frame, end of rx?).
> +	 * The only thing we know is that it is hardware specific...
> +	 * On AR5213 it seems the rx timestamp is at the end of the
> +	 * frame, but i'm not sure.
> +	 *
> +	 * NOTE: mac80211 defines mactime at the beginning of the first
> +	 * data symbol. Since we don't have any time references it's
> +	 * impossible to comply to that. This affects IBSS merge only
> +	 * right now, so it's not too bad...
> +	 */
> +	rxs->mactime = ath5k_extend_tsf(sc->ah, rs->rs_tstamp);
> +	rxs->flag |= RX_FLAG_TSFT;
> 
> -/*************\
> -* RX Handling *
> -\*************/
> +	rxs->freq = sc->curchan->center_freq;
> +	rxs->band = sc->curband->band;
> 
> -/*
> - * Enable the receive h/w following a reset.
> - */
> -static int
> -ath5k_rx_start(struct ath5k_softc *sc)
> -{
> -	struct ath5k_hw *ah = sc->ah;
> -	struct ath_common *common = ath5k_hw_common(ah);
> -	struct ath5k_buf *bf;
> -	int ret;
> +	rxs->signal = sc->ah->ah_noise_floor + rs->rs_rssi;
> 
> -	common->rx_bufsize = roundup(IEEE80211_MAX_FRAME_LEN, common->cachelsz);
> +	rxs->antenna = rs->rs_antenna;
> 
> -	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n",
> -		  common->cachelsz, common->rx_bufsize);
> +	if (rs->rs_antenna > 0 && rs->rs_antenna < 5)
> +		sc->stats.antenna_rx[rs->rs_antenna]++;
> +	else
> +		sc->stats.antenna_rx[0]++; /* invalid */
> 
> -	spin_lock_bh(&sc->rxbuflock);
> -	sc->rxlink = NULL;
> -	list_for_each_entry(bf, &sc->rxbuf, list) {
> -		ret = ath5k_rxbuf_setup(sc, bf);
> -		if (ret != 0) {
> -			spin_unlock_bh(&sc->rxbuflock);
> -			goto err;
> -		}
> -	}
> -	bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list);
> -	ath5k_hw_set_rxdp(ah, bf->daddr);
> -	spin_unlock_bh(&sc->rxbuflock);
> +	rxs->rate_idx = ath5k_hw_to_driver_rix(sc, rs->rs_rate);
> +	rxs->flag |= ath5k_rx_decrypted(sc, skb, rs);
> 
> -	ath5k_hw_start_rx_dma(ah);	/* enable recv descriptors */
> -	ath5k_mode_setup(sc);		/* set filters, etc. */
> -	ath5k_hw_start_rx_pcu(ah);	/* re-enable PCU/DMA engine */
> +	if (rxs->rate_idx >= 0 && rs->rs_rate ==
> +	    sc->curband->bitrates[rxs->rate_idx].hw_value_short)
> +		rxs->flag |= RX_FLAG_SHORTPRE;
> 
> -	return 0;
> -err:
> -	return ret;
> -}
> +	ath5k_debug_dump_skb(sc, skb, "RX  ", 0);
> 
> -/*
> - * Disable the receive h/w in preparation for a reset.
> - */
> -static void
> -ath5k_rx_stop(struct ath5k_softc *sc)
> -{
> -	struct ath5k_hw *ah = sc->ah;
> +	ath5k_update_beacon_rssi(sc, skb, rs->rs_rssi);
> 
> -	ath5k_hw_stop_rx_pcu(ah);	/* disable PCU */
> -	ath5k_hw_set_rx_filter(ah, 0);	/* clear recv filter */
> -	ath5k_hw_stop_rx_dma(ah);	/* disable DMA engine */
> +	/* check beacons in IBSS mode */
> +	if (sc->opmode == NL80211_IFTYPE_ADHOC)
> +		ath5k_check_ibss_tsf(sc, skb, rxs);
> 
> -	ath5k_debug_printrxbuffs(sc, ah);
> +	ieee80211_rx(sc->hw, skb);
>  }
> 
> -static unsigned int
> -ath5k_rx_decrypted(struct ath5k_softc *sc, struct sk_buff *skb,
> -		   struct ath5k_rx_status *rs)
> +/** ath5k_frame_receive_ok() - Do we want to receive this frame or not?
> + *
> + * Check if we want to further process this frame or not. Also update
> + * statistics. Return true if we want this frame, false if not.
> + */
> +static bool
> +ath5k_receive_frame_ok(struct ath5k_softc *sc, struct ath5k_rx_status *rs)
>  {
> -	struct ath5k_hw *ah = sc->ah;
> -	struct ath_common *common = ath5k_hw_common(ah);
> -	struct ieee80211_hdr *hdr = (void *)skb->data;
> -	unsigned int keyix, hlen;
> -
> -	if (!(rs->rs_status & AR5K_RXERR_DECRYPT) &&
> -			rs->rs_keyix != AR5K_RXKEYIX_INVALID)
> -		return RX_FLAG_DECRYPTED;
> -
> -	/* Apparently when a default key is used to decrypt the packet
> -	   the hw does not set the index used to decrypt.  In such cases
> -	   get the index from the packet. */
> -	hlen = ieee80211_hdrlen(hdr->frame_control);
> -	if (ieee80211_has_protected(hdr->frame_control) &&
> -	    !(rs->rs_status & AR5K_RXERR_DECRYPT) &&
> -	    skb->len >= hlen + 4) {
> -		keyix = skb->data[hlen + 3] >> 6;
> -
> -		if (test_bit(keyix, common->keymap))
> -			return RX_FLAG_DECRYPTED;
> -	}
> -
> -	return 0;
> -}
> -
> -
> -static void
> -ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb,
> -		     struct ieee80211_rx_status *rxs)
> -{
> -	struct ath_common *common = ath5k_hw_common(sc->ah);
> -	u64 tsf, bc_tstamp;
> -	u32 hw_tu;
> -	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
> -
> -	if (ieee80211_is_beacon(mgmt->frame_control) &&
> -	    le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS &&
> -	    memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) == 0) {
> -		/*
> -		 * Received an IBSS beacon with the same BSSID. Hardware *must*
> -		 * have updated the local TSF. We have to work around various
> -		 * hardware bugs, though...
> -		 */
> -		tsf = ath5k_hw_get_tsf64(sc->ah);
> -		bc_tstamp = le64_to_cpu(mgmt->u.beacon.timestamp);
> -		hw_tu = TSF_TO_TU(tsf);
> -
> -		ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
> -			"beacon %llx mactime %llx (diff %lld) tsf now %llx\n",
> -			(unsigned long long)bc_tstamp,
> -			(unsigned long long)rxs->mactime,
> -			(unsigned long long)(rxs->mactime - bc_tstamp),
> -			(unsigned long long)tsf);
> -
> -		/*
> -		 * Sometimes the HW will give us a wrong tstamp in the rx
> -		 * status, causing the timestamp extension to go wrong.
> -		 * (This seems to happen especially with beacon frames bigger
> -		 * than 78 byte (incl. FCS))
> -		 * But we know that the receive timestamp must be later than the
> -		 * timestamp of the beacon since HW must have synced to that.
> -		 *
> -		 * NOTE: here we assume mactime to be after the frame was
> -		 * received, not like mac80211 which defines it at the start.
> -		 */
> -		if (bc_tstamp > rxs->mactime) {
> -			ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
> -				"fixing mactime from %llx to %llx\n",
> -				(unsigned long long)rxs->mactime,
> -				(unsigned long long)tsf);
> -			rxs->mactime = tsf;
> -		}
> -
> -		/*
> -		 * Local TSF might have moved higher than our beacon timers,
> -		 * in that case we have to update them to continue sending
> -		 * beacons. This also takes care of synchronizing beacon sending
> -		 * times with other stations.
> -		 */
> -		if (hw_tu >= sc->nexttbtt)
> -			ath5k_beacon_update_timers(sc, bc_tstamp);
> -	}
> -}
> -
> -static void
> -ath5k_update_beacon_rssi(struct ath5k_softc *sc, struct sk_buff *skb, int
> rssi) -{
> -	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
> -	struct ath5k_hw *ah = sc->ah;
> -	struct ath_common *common = ath5k_hw_common(ah);
> -
> -	/* only beacons from our BSSID */
> -	if (!ieee80211_is_beacon(mgmt->frame_control) ||
> -	    memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) != 0)
> -		return;
> -
> -	ah->ah_beacon_rssi_avg = ath5k_moving_average(ah->ah_beacon_rssi_avg,
> -						      rssi);
> -
> -	/* in IBSS mode we should keep RSSI statistics per neighbour */
> -	/* le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS */
> -}
> -
> -/*
> - * Compute padding position. skb must contain an IEEE 802.11 frame
> - */
> -static int ath5k_common_padpos(struct sk_buff *skb)
> -{
> -	struct ieee80211_hdr * hdr = (struct ieee80211_hdr *)skb->data;
> -	__le16 frame_control = hdr->frame_control;
> -	int padpos = 24;
> -
> -	if (ieee80211_has_a4(frame_control)) {
> -		padpos += ETH_ALEN;
> -	}
> -	if (ieee80211_is_data_qos(frame_control)) {
> -		padpos += IEEE80211_QOS_CTL_LEN;
> -	}
> -
> -	return padpos;
> -}
> -
> -/*
> - * This function expects an 802.11 frame and returns the number of
> - * bytes added, or -1 if we don't have enough header room.
> - */
> -static int ath5k_add_padding(struct sk_buff *skb)
> -{
> -	int padpos = ath5k_common_padpos(skb);
> -	int padsize = padpos & 3;
> -
> -	if (padsize && skb->len>padpos) {
> -
> -		if (skb_headroom(skb) < padsize)
> -			return -1;
> -
> -		skb_push(skb, padsize);
> -		memmove(skb->data, skb->data+padsize, padpos);
> -		return padsize;
> -	}
> -
> -	return 0;
> -}
> -
> -/*
> - * The MAC header is padded to have 32-bit boundary if the
> - * packet payload is non-zero. The general calculation for
> - * padsize would take into account odd header lengths:
> - * padsize = 4 - (hdrlen & 3); however, since only
> - * even-length headers are used, padding can only be 0 or 2
> - * bytes and we can optimize this a bit.  We must not try to
> - * remove padding from short control frames that do not have a
> - * payload.
> - *
> - * This function expects an 802.11 frame and returns the number of
> - * bytes removed.
> - */
> -static int ath5k_remove_padding(struct sk_buff *skb)
> -{
> -	int padpos = ath5k_common_padpos(skb);
> -	int padsize = padpos & 3;
> -
> -	if (padsize && skb->len>=padpos+padsize) {
> -		memmove(skb->data + padsize, skb->data, padpos);
> -		skb_pull(skb, padsize);
> -		return padsize;
> -	}
> -
> -	return 0;
> -}
> -
> -static void
> -ath5k_receive_frame(struct ath5k_softc *sc, struct sk_buff *skb,
> -		    struct ath5k_rx_status *rs)
> -{
> -	struct ieee80211_rx_status *rxs;
> -
> -	ath5k_remove_padding(skb);
> -
> -	rxs = IEEE80211_SKB_RXCB(skb);
> -
> -	rxs->flag = 0;
> -	if (unlikely(rs->rs_status & AR5K_RXERR_MIC))
> -		rxs->flag |= RX_FLAG_MMIC_ERROR;
> -
> -	/*
> -	 * always extend the mac timestamp, since this information is
> -	 * also needed for proper IBSS merging.
> -	 *
> -	 * XXX: it might be too late to do it here, since rs_tstamp is
> -	 * 15bit only. that means TSF extension has to be done within
> -	 * 32768usec (about 32ms). it might be necessary to move this to
> -	 * the interrupt handler, like it is done in madwifi.
> -	 *
> -	 * Unfortunately we don't know when the hardware takes the rx
> -	 * timestamp (beginning of phy frame, data frame, end of rx?).
> -	 * The only thing we know is that it is hardware specific...
> -	 * On AR5213 it seems the rx timestamp is at the end of the
> -	 * frame, but i'm not sure.
> -	 *
> -	 * NOTE: mac80211 defines mactime at the beginning of the first
> -	 * data symbol. Since we don't have any time references it's
> -	 * impossible to comply to that. This affects IBSS merge only
> -	 * right now, so it's not too bad...
> -	 */
> -	rxs->mactime = ath5k_extend_tsf(sc->ah, rs->rs_tstamp);
> -	rxs->flag |= RX_FLAG_TSFT;
> -
> -	rxs->freq = sc->curchan->center_freq;
> -	rxs->band = sc->curband->band;
> -
> -	rxs->signal = sc->ah->ah_noise_floor + rs->rs_rssi;
> -
> -	rxs->antenna = rs->rs_antenna;
> -
> -	if (rs->rs_antenna > 0 && rs->rs_antenna < 5)
> -		sc->stats.antenna_rx[rs->rs_antenna]++;
> -	else
> -		sc->stats.antenna_rx[0]++; /* invalid */
> -
> -	rxs->rate_idx = ath5k_hw_to_driver_rix(sc, rs->rs_rate);
> -	rxs->flag |= ath5k_rx_decrypted(sc, skb, rs);
> -
> -	if (rxs->rate_idx >= 0 && rs->rs_rate ==
> -	    sc->curband->bitrates[rxs->rate_idx].hw_value_short)
> -		rxs->flag |= RX_FLAG_SHORTPRE;
> -
> -	ath5k_debug_dump_skb(sc, skb, "RX  ", 0);
> -
> -	ath5k_update_beacon_rssi(sc, skb, rs->rs_rssi);
> -
> -	/* check beacons in IBSS mode */
> -	if (sc->opmode == NL80211_IFTYPE_ADHOC)
> -		ath5k_check_ibss_tsf(sc, skb, rxs);
> -
> -	ieee80211_rx(sc->hw, skb);
> -}
> -
> -/** ath5k_frame_receive_ok() - Do we want to receive this frame or not?
> - *
> - * Check if we want to further process this frame or not. Also update
> - * statistics. Return true if we want this frame, false if not.
> - */
> -static bool
> -ath5k_receive_frame_ok(struct ath5k_softc *sc, struct ath5k_rx_status *rs)
> -{
> -	sc->stats.rx_all_count++;
> +	sc->stats.rx_all_count++;
> 
>  	if (unlikely(rs->rs_status)) {
>  		if (rs->rs_status & AR5K_RXERR_CRC)
> @@ -2121,33 +1462,86 @@ unlock:
>  * TX Handling *
>  \*************/
> 
> -static void
> -ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
> +static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
> +			  struct ath5k_txq *txq)
>  {
> -	struct ath5k_tx_status ts = {};
> -	struct ath5k_buf *bf, *bf0;
> -	struct ath5k_desc *ds;
> -	struct sk_buff *skb;
> -	struct ieee80211_tx_info *info;
> -	int i, ret;
> -
> -	spin_lock(&txq->lock);
> -	list_for_each_entry_safe(bf, bf0, &txq->q, list) {
> -		ds = bf->desc;
> +	struct ath5k_softc *sc = hw->priv;
> +	struct ath5k_buf *bf;
> +	unsigned long flags;
> +	int padsize;
> 
> -		/*
> -		 * It's possible that the hardware can say the buffer is
> -		 * completed when it hasn't yet loaded the ds_link from
> -		 * host memory and moved on.  If there are more TX
> -		 * descriptors in the queue, wait for TXDP to change
> -		 * before processing this one.
> -		 */
> -		if (ath5k_hw_get_txdp(sc->ah, txq->qnum) == bf->daddr &&
> -		    !list_is_last(&bf->list, &txq->q))
> -			break;
> +	ath5k_debug_dump_skb(sc, skb, "TX  ", 1);
> 
> -		ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts);
> -		if (unlikely(ret == -EINPROGRESS))
> +	/*
> +	 * The hardware expects the header padded to 4 byte boundaries.
> +	 * If this is not the case, we add the padding after the header.
> +	 */
> +	padsize = ath5k_add_padding(skb);
> +	if (padsize < 0) {
> +		ATH5K_ERR(sc, "tx hdrlen not %%4: not enough"
> +			  " headroom to pad");
> +		goto drop_packet;
> +	}
> +
> +	spin_lock_irqsave(&sc->txbuflock, flags);
> +	if (list_empty(&sc->txbuf)) {
> +		ATH5K_ERR(sc, "no further txbuf available, dropping packet\n");
> +		spin_unlock_irqrestore(&sc->txbuflock, flags);
> +		ieee80211_stop_queue(hw, skb_get_queue_mapping(skb));
> +		goto drop_packet;
> +	}
> +	bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list);
> +	list_del(&bf->list);
> +	sc->txbuf_len--;
> +	if (list_empty(&sc->txbuf))
> +		ieee80211_stop_queues(hw);
> +	spin_unlock_irqrestore(&sc->txbuflock, flags);
> +
> +	bf->skb = skb;
> +
> +	if (ath5k_txbuf_setup(sc, bf, txq, padsize)) {
> +		bf->skb = NULL;
> +		spin_lock_irqsave(&sc->txbuflock, flags);
> +		list_add_tail(&bf->list, &sc->txbuf);
> +		sc->txbuf_len++;
> +		spin_unlock_irqrestore(&sc->txbuflock, flags);
> +		goto drop_packet;
> +	}
> +	return NETDEV_TX_OK;
> +
> +drop_packet:
> +	dev_kfree_skb_any(skb);
> +	return NETDEV_TX_OK;
> +}
> +
> +
> +static void
> +ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
> +{
> +	struct ath5k_tx_status ts = {};
> +	struct ath5k_buf *bf, *bf0;
> +	struct ath5k_desc *ds;
> +	struct sk_buff *skb;
> +	struct ieee80211_tx_info *info;
> +	int i, ret;
> +
> +	spin_lock(&txq->lock);
> +	list_for_each_entry_safe(bf, bf0, &txq->q, list) {
> +		ds = bf->desc;
> +
> +		/*
> +		 * It's possible that the hardware can say the buffer is
> +		 * completed when it hasn't yet loaded the ds_link from
> +		 * host memory and moved on.  If there are more TX
> +		 * descriptors in the queue, wait for TXDP to change
> +		 * before processing this one.
> +		 */
> +		if (ath5k_hw_get_txdp(sc->ah, txq->qnum) == bf->daddr &&
> +		    !list_is_last(&bf->list, &txq->q))
> +			break;
> +
> +		ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts);
> +		if (unlikely(ret == -EINPROGRESS))
>  			break;
>  		else if (unlikely(ret)) {
>  			ATH5K_ERR(sc, "error %d while processing queue %u\n",
> @@ -2313,6 +1707,43 @@ err_unmap:
>  }
> 
>  /*
> + * Updates the beacon that is sent by ath5k_beacon_send.  For adhoc,
> + * this is called only once at config_bss time, for AP we do it every
> + * SWBA interrupt so that the TIM will reflect buffered frames.
> + *
> + * Called with the beacon lock.
> + */
> +static int
> +ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
> +{
> +	int ret;
> +	struct ath5k_softc *sc = hw->priv;
> +	struct sk_buff *skb;
> +
> +	if (WARN_ON(!vif)) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	skb = ieee80211_beacon_get(hw, vif);
> +
> +	if (!skb) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	ath5k_debug_dump_skb(sc, skb, "BC  ", 1);
> +
> +	ath5k_txbuf_free_skb(sc, sc->bbuf);
> +	sc->bbuf->skb = skb;
> +	ret = ath5k_beacon_setup(sc, sc->bbuf);
> +	if (ret)
> +		sc->bbuf->skb = NULL;
> +out:
> +	return ret;
> +}
> +
> +/*
>   * Transmit a beacon frame at SWBA.  Dynamic updates to the
>   * frame contents are done as needed and the slot time is
>   * also adjusted based on current state.
> @@ -2389,7 +1820,6 @@ ath5k_beacon_send(struct ath5k_softc *sc)
>  	sc->bsent++;
>  }
> 
> -
>  /**
>   * ath5k_beacon_update_timers - update beacon timers
>   *
> @@ -2491,7 +1921,6 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc,
> u64 bc_tsf) intval & AR5K_BEACON_RESET_TSF ? "AR5K_BEACON_RESET_TSF" :
> ""); }
> 
> -
>  /**
>   * ath5k_beacon_config - Configure the beacon queues and interrupts
>   *
> @@ -2570,66 +1999,181 @@ static void ath5k_tasklet_beacon(unsigned long
> data) * Interrupt handling *
>  \********************/
> 
> -static int
> -ath5k_init(struct ath5k_softc *sc)
> +static void
> +ath5k_intr_calibration_poll(struct ath5k_hw *ah)
>  {
> -	struct ath5k_hw *ah = sc->ah;
> -	struct ath_common *common = ath5k_hw_common(ah);
> -	int ret, i;
> +	if (time_is_before_eq_jiffies(ah->ah_cal_next_ani) &&
> +	    !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL)) {
> +		/* run ANI only when full calibration is not active */
> +		ah->ah_cal_next_ani = jiffies +
> +			msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI);
> +		tasklet_schedule(&ah->ah_sc->ani_tasklet);
> 
> -	mutex_lock(&sc->lock);
> +	} else if (time_is_before_eq_jiffies(ah->ah_cal_next_full)) {
> +		ah->ah_cal_next_full = jiffies +
> +			msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL);
> +		tasklet_schedule(&ah->ah_sc->calib);
> +	}
> +	/* we could use SWI to generate enough interrupts to meet our
> +	 * calibration interval requirements, if necessary:
> +	 * AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); */
> +}
> 
> -	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mode %d\n", sc->opmode);
> +static irqreturn_t
> +ath5k_intr(int irq, void *dev_id)
> +{
> +	struct ath5k_softc *sc = dev_id;
> +	struct ath5k_hw *ah = sc->ah;
> +	enum ath5k_int status;
> +	unsigned int counter = 1000;
> 
> -	/*
> -	 * Stop anything previously setup.  This is safe
> -	 * no matter this is the first time through or not.
> -	 */
> -	ath5k_stop_locked(sc);
> +	if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) ||
> +				!ath5k_hw_is_intr_pending(ah)))
> +		return IRQ_NONE;
> 
> -	/*
> -	 * The basic interface to setting the hardware in a good
> -	 * state is ``reset''.  On return the hardware is known to
> -	 * be powered up and with interrupts disabled.  This must
> -	 * be followed by initialization of the appropriate bits
> -	 * and then setup of the interrupt mask.
> -	 */
> -	sc->curchan = sc->hw->conf.channel;
> -	sc->curband = &sc->sbands[sc->curchan->band];
> -	sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL |
> -		AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL |
> -		AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB;
> +	do {
> +		ath5k_hw_get_isr(ah, &status);		/* NB: clears IRQ too */
> +		ATH5K_DBG(sc, ATH5K_DEBUG_INTR, "status 0x%x/0x%x\n",
> +				status, sc->imask);
> +		if (unlikely(status & AR5K_INT_FATAL)) {
> +			/*
> +			 * Fatal errors are unrecoverable.
> +			 * Typically these are caused by DMA errors.
> +			 */
> +			ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
> +				  "fatal int, resetting\n");
> +			ieee80211_queue_work(sc->hw, &sc->reset_work);
> +		} else if (unlikely(status & AR5K_INT_RXORN)) {
> +			/*
> +			 * Receive buffers are full. Either the bus is busy or
> +			 * the CPU is not fast enough to process all received
> +			 * frames.
> +			 * Older chipsets need a reset to come out of this
> +			 * condition, but we treat it as RX for newer chips.
> +			 * We don't know exactly which versions need a reset -
> +			 * this guess is copied from the HAL.
> +			 */
> +			sc->stats.rxorn_intr++;
> +			if (ah->ah_mac_srev < AR5K_SREV_AR5212) {
> +				ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
> +					  "rx overrun, resetting\n");
> +				ieee80211_queue_work(sc->hw, &sc->reset_work);
> +			}
> +			else
> +				tasklet_schedule(&sc->rxtq);
> +		} else {
> +			if (status & AR5K_INT_SWBA) {
> +				tasklet_hi_schedule(&sc->beacontq);
> +			}
> +			if (status & AR5K_INT_RXEOL) {
> +				/*
> +				* NB: the hardware should re-read the link when
> +				*     RXE bit is written, but it doesn't work at
> +				*     least on older hardware revs.
> +				*/
> +				sc->stats.rxeol_intr++;
> +			}
> +			if (status & AR5K_INT_TXURN) {
> +				/* bump tx trigger level */
> +				ath5k_hw_update_tx_triglevel(ah, true);
> +			}
> +			if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR))
> +				tasklet_schedule(&sc->rxtq);
> +			if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC
> +					| AR5K_INT_TXERR | AR5K_INT_TXEOL))
> +				tasklet_schedule(&sc->txtq);
> +			if (status & AR5K_INT_BMISS) {
> +				/* TODO */
> +			}
> +			if (status & AR5K_INT_MIB) {
> +				sc->stats.mib_intr++;
> +				ath5k_hw_update_mib_counters(ah);
> +				ath5k_ani_mib_intr(ah);
> +			}
> +			if (status & AR5K_INT_GPIO)
> +				tasklet_schedule(&sc->rf_kill.toggleq);
> 
> -	ret = ath5k_reset(sc, NULL);
> -	if (ret)
> -		goto done;
> +		}
> +	} while (ath5k_hw_is_intr_pending(ah) && --counter > 0);
> 
> -	ath5k_rfkill_hw_start(ah);
> +	if (unlikely(!counter))
> +		ATH5K_WARN(sc, "too many interrupts, giving up for now\n");
> 
> -	/*
> -	 * Reset the key cache since some parts do not reset the
> -	 * contents on initial power up or resume from suspend.
> -	 */
> -	for (i = 0; i < common->keymax; i++)
> -		ath_hw_keyreset(common, (u16)i);
> +	ath5k_intr_calibration_poll(ah);
> 
> -	ath5k_hw_set_ack_bitrate_high(ah, true);
> -	ret = 0;
> -done:
> -	mmiowb();
> -	mutex_unlock(&sc->lock);
> -	return ret;
> +	return IRQ_HANDLED;
>  }
> 
> -static int
> -ath5k_stop_locked(struct ath5k_softc *sc)
> +/*
> + * Periodically recalibrate the PHY to account
> + * for temperature/environment changes.
> + */
> +static void
> +ath5k_tasklet_calibrate(unsigned long data)
>  {
> +	struct ath5k_softc *sc = (void *)data;
>  	struct ath5k_hw *ah = sc->ah;
> 
> -	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "invalid %u\n",
> -			test_bit(ATH_STAT_INVALID, sc->status));
> +	/* Only full calibration for now */
> +	ah->ah_cal_mask |= AR5K_CALIBRATION_FULL;
> 
> -	/*
> +	ATH5K_DBG(sc, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n",
> +		ieee80211_frequency_to_channel(sc->curchan->center_freq),
> +		sc->curchan->hw_value);
> +
> +	if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) {
> +		/*
> +		 * Rfgain is out of bounds, reset the chip
> +		 * to load new gain values.
> +		 */
> +		ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "calibration, resetting\n");
> +		ieee80211_queue_work(sc->hw, &sc->reset_work);
> +	}
> +	if (ath5k_hw_phy_calibrate(ah, sc->curchan))
> +		ATH5K_ERR(sc, "calibration of channel %u failed\n",
> +			ieee80211_frequency_to_channel(
> +				sc->curchan->center_freq));
> +
> +	/* Noise floor calibration interrupts rx/tx path while I/Q calibration
> +	 * doesn't. We stop the queues so that calibration doesn't interfere
> +	 * with TX and don't run it as often */
> +	if (time_is_before_eq_jiffies(ah->ah_cal_next_nf)) {
> +		ah->ah_cal_next_nf = jiffies +
> +			msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_NF);
> +		ieee80211_stop_queues(sc->hw);
> +		ath5k_hw_update_noise_floor(ah);
> +		ieee80211_wake_queues(sc->hw);
> +	}
> +
> +	ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL;
> +}
> +
> +
> +static void
> +ath5k_tasklet_ani(unsigned long data)
> +{
> +	struct ath5k_softc *sc = (void *)data;
> +	struct ath5k_hw *ah = sc->ah;
> +
> +	ah->ah_cal_mask |= AR5K_CALIBRATION_ANI;
> +	ath5k_ani_calibration(ah);
> +	ah->ah_cal_mask &= ~AR5K_CALIBRATION_ANI;
> +}
> +
> +
> +/*************************\
> +* Initialization routines *
> +\*************************/
> +
> +static int
> +ath5k_stop_locked(struct ath5k_softc *sc)
> +{
> +	struct ath5k_hw *ah = sc->ah;
> +
> +	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "invalid %u\n",
> +			test_bit(ATH_STAT_INVALID, sc->status));
> +
> +	/*
>  	 * Shutdown the hardware and driver:
>  	 *    stop output from above
>  	 *    disable interrupts
> @@ -2660,6 +2204,57 @@ ath5k_stop_locked(struct ath5k_softc *sc)
>  	return 0;
>  }
> 
> +static int
> +ath5k_init(struct ath5k_softc *sc)
> +{
> +	struct ath5k_hw *ah = sc->ah;
> +	struct ath_common *common = ath5k_hw_common(ah);
> +	int ret, i;
> +
> +	mutex_lock(&sc->lock);
> +
> +	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mode %d\n", sc->opmode);
> +
> +	/*
> +	 * Stop anything previously setup.  This is safe
> +	 * no matter this is the first time through or not.
> +	 */
> +	ath5k_stop_locked(sc);
> +
> +	/*
> +	 * The basic interface to setting the hardware in a good
> +	 * state is ``reset''.  On return the hardware is known to
> +	 * be powered up and with interrupts disabled.  This must
> +	 * be followed by initialization of the appropriate bits
> +	 * and then setup of the interrupt mask.
> +	 */
> +	sc->curchan = sc->hw->conf.channel;
> +	sc->curband = &sc->sbands[sc->curchan->band];
> +	sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL |
> +		AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL |
> +		AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB;
> +
> +	ret = ath5k_reset(sc, NULL);
> +	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.
> +	 */
> +	for (i = 0; i < common->keymax; i++)
> +		ath_hw_keyreset(common, (u16) i);
> +
> +	ath5k_hw_set_ack_bitrate_high(ah, true);
> +	ret = 0;
> +done:
> +	mmiowb();
> +	mutex_unlock(&sc->lock);
> +	return ret;
> +}
> +
>  static void stop_tasklets(struct ath5k_softc *sc)
>  {
>  	tasklet_kill(&sc->rxtq);
> @@ -2720,310 +2315,257 @@ ath5k_stop_hw(struct ath5k_softc *sc)
>  	return ret;
>  }
> 
> -static void
> -ath5k_intr_calibration_poll(struct ath5k_hw *ah)
> +/*
> + * Reset the hardware.  If chan is not NULL, then also pause rx/tx
> + * and change to the given channel.
> + *
> + * This should be called with sc->lock.
> + */
> +static int
> +ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan)
>  {
> -	if (time_is_before_eq_jiffies(ah->ah_cal_next_ani) &&
> -	    !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL)) {
> -		/* run ANI only when full calibration is not active */
> -		ah->ah_cal_next_ani = jiffies +
> -			msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI);
> -		tasklet_schedule(&ah->ah_sc->ani_tasklet);
> +	struct ath5k_hw *ah = sc->ah;
> +	int ret;
> 
> -	} else if (time_is_before_eq_jiffies(ah->ah_cal_next_full)) {
> -		ah->ah_cal_next_full = jiffies +
> -			msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL);
> -		tasklet_schedule(&ah->ah_sc->calib);
> +	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "resetting\n");
> +
> +	ath5k_hw_set_imr(ah, 0);
> +	synchronize_irq(sc->pdev->irq);
> +	stop_tasklets(sc);
> +
> +	if (chan) {
> +		ath5k_txq_cleanup(sc);
> +		ath5k_rx_stop(sc);
> +
> +		sc->curchan = chan;
> +		sc->curband = &sc->sbands[chan->band];
> +	}
> +	ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, chan != NULL);
> +	if (ret) {
> +		ATH5K_ERR(sc, "can't reset hardware (%d)\n", ret);
> +		goto err;
>  	}
> -	/* we could use SWI to generate enough interrupts to meet our
> -	 * calibration interval requirements, if necessary:
> -	 * AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); */
> -}
> 
> -static irqreturn_t
> -ath5k_intr(int irq, void *dev_id)
> -{
> -	struct ath5k_softc *sc = dev_id;
> -	struct ath5k_hw *ah = sc->ah;
> -	enum ath5k_int status;
> -	unsigned int counter = 1000;
> +	ret = ath5k_rx_start(sc);
> +	if (ret) {
> +		ATH5K_ERR(sc, "can't start recv logic\n");
> +		goto err;
> +	}
> 
> -	if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) ||
> -				!ath5k_hw_is_intr_pending(ah)))
> -		return IRQ_NONE;
> +	ath5k_ani_init(ah, ah->ah_sc->ani_state.ani_mode);
> 
> -	do {
> -		ath5k_hw_get_isr(ah, &status);		/* NB: clears IRQ too */
> -		ATH5K_DBG(sc, ATH5K_DEBUG_INTR, "status 0x%x/0x%x\n",
> -				status, sc->imask);
> -		if (unlikely(status & AR5K_INT_FATAL)) {
> -			/*
> -			 * Fatal errors are unrecoverable.
> -			 * Typically these are caused by DMA errors.
> -			 */
> -			ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
> -				  "fatal int, resetting\n");
> -			ieee80211_queue_work(sc->hw, &sc->reset_work);
> -		} else if (unlikely(status & AR5K_INT_RXORN)) {
> -			/*
> -			 * Receive buffers are full. Either the bus is busy or
> -			 * the CPU is not fast enough to process all received
> -			 * frames.
> -			 * Older chipsets need a reset to come out of this
> -			 * condition, but we treat it as RX for newer chips.
> -			 * We don't know exactly which versions need a reset -
> -			 * this guess is copied from the HAL.
> -			 */
> -			sc->stats.rxorn_intr++;
> -			if (ah->ah_mac_srev < AR5K_SREV_AR5212) {
> -				ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
> -					  "rx overrun, resetting\n");
> -				ieee80211_queue_work(sc->hw, &sc->reset_work);
> -			}
> -			else
> -				tasklet_schedule(&sc->rxtq);
> -		} else {
> -			if (status & AR5K_INT_SWBA) {
> -				tasklet_hi_schedule(&sc->beacontq);
> -			}
> -			if (status & AR5K_INT_RXEOL) {
> -				/*
> -				* NB: the hardware should re-read the link when
> -				*     RXE bit is written, but it doesn't work at
> -				*     least on older hardware revs.
> -				*/
> -				sc->stats.rxeol_intr++;
> -			}
> -			if (status & AR5K_INT_TXURN) {
> -				/* bump tx trigger level */
> -				ath5k_hw_update_tx_triglevel(ah, true);
> -			}
> -			if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR))
> -				tasklet_schedule(&sc->rxtq);
> -			if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC
> -					| AR5K_INT_TXERR | AR5K_INT_TXEOL))
> -				tasklet_schedule(&sc->txtq);
> -			if (status & AR5K_INT_BMISS) {
> -				/* TODO */
> -			}
> -			if (status & AR5K_INT_MIB) {
> -				sc->stats.mib_intr++;
> -				ath5k_hw_update_mib_counters(ah);
> -				ath5k_ani_mib_intr(ah);
> -			}
> -			if (status & AR5K_INT_GPIO)
> -				tasklet_schedule(&sc->rf_kill.toggleq);
> +	ah->ah_cal_next_full = jiffies;
> +	ah->ah_cal_next_ani = jiffies;
> +	ah->ah_cal_next_nf = jiffies;
> 
> -		}
> -	} while (ath5k_hw_is_intr_pending(ah) && --counter > 0);
> +	/*
> +	 * Change channels and update the h/w rate map if we're switching;
> +	 * e.g. 11a to 11b/g.
> +	 *
> +	 * We may be doing a reset in response to an ioctl that changes the
> +	 * channel so update any state that might change as a result.
> +	 *
> +	 * XXX needed?
> +	 */
> +/*	ath5k_chan_change(sc, c); */
> 
> -	if (unlikely(!counter))
> -		ATH5K_WARN(sc, "too many interrupts, giving up for now\n");
> +	ath5k_beacon_config(sc);
> +	/* intrs are enabled by ath5k_beacon_config */
> 
> -	ath5k_intr_calibration_poll(ah);
> +	ieee80211_wake_queues(sc->hw);
> 
> -	return IRQ_HANDLED;
> +	return 0;
> +err:
> +	return ret;
>  }
> 
> -/*
> - * Periodically recalibrate the PHY to account
> - * for temperature/environment changes.
> - */
> -static void
> -ath5k_tasklet_calibrate(unsigned long data)
> +static void ath5k_reset_work(struct work_struct *work)
>  {
> -	struct ath5k_softc *sc = (void *)data;
> -	struct ath5k_hw *ah = sc->ah;
> -
> -	/* Only full calibration for now */
> -	ah->ah_cal_mask |= AR5K_CALIBRATION_FULL;
> -
> -	ATH5K_DBG(sc, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n",
> -		ieee80211_frequency_to_channel(sc->curchan->center_freq),
> -		sc->curchan->hw_value);
> -
> -	if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) {
> -		/*
> -		 * Rfgain is out of bounds, reset the chip
> -		 * to load new gain values.
> -		 */
> -		ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "calibration, resetting\n");
> -		ieee80211_queue_work(sc->hw, &sc->reset_work);
> -	}
> -	if (ath5k_hw_phy_calibrate(ah, sc->curchan))
> -		ATH5K_ERR(sc, "calibration of channel %u failed\n",
> -			ieee80211_frequency_to_channel(
> -				sc->curchan->center_freq));
> -
> -	/* Noise floor calibration interrupts rx/tx path while I/Q calibration
> -	 * doesn't. We stop the queues so that calibration doesn't interfere
> -	 * with TX and don't run it as often */
> -	if (time_is_before_eq_jiffies(ah->ah_cal_next_nf)) {
> -		ah->ah_cal_next_nf = jiffies +
> -			msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_NF);
> -		ieee80211_stop_queues(sc->hw);
> -		ath5k_hw_update_noise_floor(ah);
> -		ieee80211_wake_queues(sc->hw);
> -	}
> -
> -	ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL;
> -}
> -
> -
> -static void
> -ath5k_tasklet_ani(unsigned long data)
> -{
> -	struct ath5k_softc *sc = (void *)data;
> -	struct ath5k_hw *ah = sc->ah;
> +	struct ath5k_softc *sc = container_of(work, struct ath5k_softc,
> +		reset_work);
> 
> -	ah->ah_cal_mask |= AR5K_CALIBRATION_ANI;
> -	ath5k_ani_calibration(ah);
> -	ah->ah_cal_mask &= ~AR5K_CALIBRATION_ANI;
> +	mutex_lock(&sc->lock);
> +	ath5k_reset(sc, sc->curchan);
> +	mutex_unlock(&sc->lock);
>  }
> 
> -
> -/********************\
> -* Mac80211 functions *
> -\********************/
> -
>  static int
> -ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
> +ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
>  {
>  	struct ath5k_softc *sc = hw->priv;
> +	struct ath5k_hw *ah = sc->ah;
> +	struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah);
> +	u8 mac[ETH_ALEN] = {};
> +	int ret;
> 
> -	return ath5k_tx_queue(hw, skb, sc->txq);
> -}
> +	ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "devid 0x%x\n", pdev->device);
> 
> -static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
> -			  struct ath5k_txq *txq)
> -{
> -	struct ath5k_softc *sc = hw->priv;
> -	struct ath5k_buf *bf;
> -	unsigned long flags;
> -	int padsize;
> +	/*
> +	 * Check if the MAC has multi-rate retry support.
> +	 * We do this by trying to setup a fake extended
> +	 * descriptor.  MACs that don't have support will
> +	 * return false w/o doing anything.  MACs that do
> +	 * support it will return true w/o doing anything.
> +	 */
> +	ret = ath5k_hw_setup_mrr_tx_desc(ah, NULL, 0, 0, 0, 0, 0, 0);
> 
> -	ath5k_debug_dump_skb(sc, skb, "TX  ", 1);
> +	if (ret < 0)
> +		goto err;
> +	if (ret > 0)
> +		__set_bit(ATH_STAT_MRRETRY, sc->status);
> 
>  	/*
> -	 * The hardware expects the header padded to 4 byte boundaries.
> -	 * If this is not the case, we add the padding after the header.
> +	 * Collect the channel list.  The 802.11 layer
> +	 * is resposible for filtering this list based
> +	 * on settings like the phy mode and regulatory
> +	 * domain restrictions.
>  	 */
> -	padsize = ath5k_add_padding(skb);
> -	if (padsize < 0) {
> -		ATH5K_ERR(sc, "tx hdrlen not %%4: not enough"
> -			  " headroom to pad");
> -		goto drop_packet;
> +	ret = ath5k_setup_bands(hw);
> +	if (ret) {
> +		ATH5K_ERR(sc, "can't get channels\n");
> +		goto err;
>  	}
> 
> -	spin_lock_irqsave(&sc->txbuflock, flags);
> -	if (list_empty(&sc->txbuf)) {
> -		ATH5K_ERR(sc, "no further txbuf available, dropping packet\n");
> -		spin_unlock_irqrestore(&sc->txbuflock, flags);
> -		ieee80211_stop_queue(hw, skb_get_queue_mapping(skb));
> -		goto drop_packet;
> -	}
> -	bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list);
> -	list_del(&bf->list);
> -	sc->txbuf_len--;
> -	if (list_empty(&sc->txbuf))
> -		ieee80211_stop_queues(hw);
> -	spin_unlock_irqrestore(&sc->txbuflock, flags);
> +	/* NB: setup here so ath5k_rate_update is happy */
> +	if (test_bit(AR5K_MODE_11A, ah->ah_modes))
> +		ath5k_setcurmode(sc, AR5K_MODE_11A);
> +	else
> +		ath5k_setcurmode(sc, AR5K_MODE_11B);
> 
> -	bf->skb = skb;
> +	/*
> +	 * Allocate tx+rx descriptors and populate the lists.
> +	 */
> +	ret = ath5k_desc_alloc(sc, pdev);
> +	if (ret) {
> +		ATH5K_ERR(sc, "can't allocate descriptors\n");
> +		goto err;
> +	}
> 
> -	if (ath5k_txbuf_setup(sc, bf, txq, padsize)) {
> -		bf->skb = NULL;
> -		spin_lock_irqsave(&sc->txbuflock, flags);
> -		list_add_tail(&bf->list, &sc->txbuf);
> -		sc->txbuf_len++;
> -		spin_unlock_irqrestore(&sc->txbuflock, flags);
> -		goto drop_packet;
> +	/*
> +	 * Allocate hardware transmit queues: one queue for
> +	 * beacon frames and one data queue for each QoS
> +	 * priority.  Note that hw functions handle resetting
> +	 * these queues at the needed time.
> +	 */
> +	ret = ath5k_beaconq_setup(ah);
> +	if (ret < 0) {
> +		ATH5K_ERR(sc, "can't setup a beacon xmit queue\n");
> +		goto err_desc;
> +	}
> +	sc->bhalq = ret;
> +	sc->cabq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_CAB, 0);
> +	if (IS_ERR(sc->cabq)) {
> +		ATH5K_ERR(sc, "can't setup cab queue\n");
> +		ret = PTR_ERR(sc->cabq);
> +		goto err_bhal;
>  	}
> -	return NETDEV_TX_OK;
> 
> -drop_packet:
> -	dev_kfree_skb_any(skb);
> -	return NETDEV_TX_OK;
> -}
> +	sc->txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK);
> +	if (IS_ERR(sc->txq)) {
> +		ATH5K_ERR(sc, "can't setup xmit queue\n");
> +		ret = PTR_ERR(sc->txq);
> +		goto err_queues;
> +	}
> 
> -/*
> - * Reset the hardware.  If chan is not NULL, then also pause rx/tx
> - * and change to the given channel.
> - *
> - * This should be called with sc->lock.
> - */
> -static int
> -ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan)
> -{
> -	struct ath5k_hw *ah = sc->ah;
> -	int ret;
> +	tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc);
> +	tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc);
> +	tasklet_init(&sc->calib, ath5k_tasklet_calibrate, (unsigned long)sc);
> +	tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc);
> +	tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc);
> 
> -	ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "resetting\n");
> +	INIT_WORK(&sc->reset_work, ath5k_reset_work);
> 
> -	ath5k_hw_set_imr(ah, 0);
> -	synchronize_irq(sc->pdev->irq);
> -	stop_tasklets(sc);
> +	ret = ath5k_eeprom_read_mac(ah, mac);
> +	if (ret) {
> +		ATH5K_ERR(sc, "unable to read address from EEPROM: 0x%04x\n",
> +			sc->pdev->device);
> +		goto err_queues;
> +	}
> 
> -	if (chan) {
> -		ath5k_txq_cleanup(sc);
> -		ath5k_rx_stop(sc);
> +	SET_IEEE80211_PERM_ADDR(hw, mac);
> +	/* All MAC address bits matter for ACKs */
> +	memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN);
> +	ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask);
> 
> -		sc->curchan = chan;
> -		sc->curband = &sc->sbands[chan->band];
> -	}
> -	ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, chan != NULL);
> +	regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain;
> +	ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier);
>  	if (ret) {
> -		ATH5K_ERR(sc, "can't reset hardware (%d)\n", ret);
> -		goto err;
> +		ATH5K_ERR(sc, "can't initialize regulatory system\n");
> +		goto err_queues;
>  	}
> 
> -	ret = ath5k_rx_start(sc);
> +	ret = ieee80211_register_hw(hw);
>  	if (ret) {
> -		ATH5K_ERR(sc, "can't start recv logic\n");
> -		goto err;
> +		ATH5K_ERR(sc, "can't register ieee80211 hw\n");
> +		goto err_queues;
>  	}
> 
> -	ath5k_ani_init(ah, ah->ah_sc->ani_state.ani_mode);
> -
> -	ah->ah_cal_next_full = jiffies;
> -	ah->ah_cal_next_ani = jiffies;
> -	ah->ah_cal_next_nf = jiffies;
> -
> -	/*
> -	 * Change channels and update the h/w rate map if we're switching;
> -	 * e.g. 11a to 11b/g.
> -	 *
> -	 * We may be doing a reset in response to an ioctl that changes the
> -	 * channel so update any state that might change as a result.
> -	 *
> -	 * XXX needed?
> -	 */
> -/*	ath5k_chan_change(sc, c); */
> +	if (!ath_is_world_regd(regulatory))
> +		regulatory_hint(hw->wiphy, regulatory->alpha2);
> 
> -	ath5k_beacon_config(sc);
> -	/* intrs are enabled by ath5k_beacon_config */
> +	ath5k_init_leds(sc);
> 
> -	ieee80211_wake_queues(sc->hw);
> +	ath5k_sysfs_register(sc);
> 
>  	return 0;
> +err_queues:
> +	ath5k_txq_release(sc);
> +err_bhal:
> +	ath5k_hw_release_tx_queue(ah, sc->bhalq);
> +err_desc:
> +	ath5k_desc_free(sc, pdev);
>  err:
>  	return ret;
>  }
> 
> -static void ath5k_reset_work(struct work_struct *work)
> +static void
> +ath5k_detach(struct pci_dev *pdev, struct ieee80211_hw *hw)
>  {
> -	struct ath5k_softc *sc = container_of(work, struct ath5k_softc,
> -		reset_work);
> -
> -	mutex_lock(&sc->lock);
> -	ath5k_reset(sc, sc->curchan);
> -	mutex_unlock(&sc->lock);
> -}
> +	struct ath5k_softc *sc = hw->priv;
> 
> -static int ath5k_start(struct ieee80211_hw *hw)
> -{
> -	return ath5k_init(hw->priv);
> -}
> +	/*
> +	 * NB: the order of these is important:
> +	 * o call the 802.11 layer before detaching ath5k_hw to
> +	 *   ensure callbacks into the driver to delete global
> +	 *   key cache entries can be handled
> +	 * o reclaim the tx queue data structures after calling
> +	 *   the 802.11 layer as we'll get called back to reclaim
> +	 *   node state and potentially want to use them
> +	 * o to cleanup the tx queues the hal is called, so detach
> +	 *   it last
> +	 * XXX: ??? detach ath5k_hw ???
> +	 * Other than that, it's straightforward...
> +	 */
> +	ieee80211_unregister_hw(hw);
> +	ath5k_desc_free(sc, pdev);
> +	ath5k_txq_release(sc);
> +	ath5k_hw_release_tx_queue(sc->ah, sc->bhalq);
> +	ath5k_unregister_leds(sc);
> +
> +	ath5k_sysfs_unregister(sc);
> +	/*
> +	 * NB: can't reclaim these until after ieee80211_ifdetach
> +	 * returns because we'll get called back to reclaim node
> +	 * state and potentially want to use them.
> +	 */
> +}
> +
> +/********************\
> +* Mac80211 functions *
> +\********************/
> +
> +static int
> +ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
> +{
> +	struct ath5k_softc *sc = hw->priv;
> +
> +	return ath5k_tx_queue(hw, skb, sc->txq);
> +}
> +
> +static int ath5k_start(struct ieee80211_hw *hw)
> +{
> +	return ath5k_init(hw->priv);
> +}
> 
>  static void ath5k_stop(struct ieee80211_hw *hw)
>  {
> @@ -3329,214 +2871,538 @@ ath5k_set_key(struct ieee80211_hw *hw, enum
> set_key_cmd cmd, ret = -EINVAL;
>  	}
> 
> -	mmiowb();
> -	mutex_unlock(&sc->lock);
> -	return ret;
> -}
> -
> -static int
> -ath5k_get_stats(struct ieee80211_hw *hw,
> -		struct ieee80211_low_level_stats *stats)
> -{
> -	struct ath5k_softc *sc = hw->priv;
> -
> -	/* Force update */
> -	ath5k_hw_update_mib_counters(sc->ah);
> -
> -	stats->dot11ACKFailureCount = sc->stats.ack_fail;
> -	stats->dot11RTSFailureCount = sc->stats.rts_fail;
> -	stats->dot11RTSSuccessCount = sc->stats.rts_ok;
> -	stats->dot11FCSErrorCount = sc->stats.fcs_error;
> -
> -	return 0;
> -}
> -
> -static int ath5k_get_survey(struct ieee80211_hw *hw, int idx,
> -		struct survey_info *survey)
> -{
> -	struct ath5k_softc *sc = hw->priv;
> -	struct ieee80211_conf *conf = &hw->conf;
> -
> -	 if (idx != 0)
> -		return -ENOENT;
> +	mmiowb();
> +	mutex_unlock(&sc->lock);
> +	return ret;
> +}
> +
> +static int
> +ath5k_get_stats(struct ieee80211_hw *hw,
> +		struct ieee80211_low_level_stats *stats)
> +{
> +	struct ath5k_softc *sc = hw->priv;
> +
> +	/* Force update */
> +	ath5k_hw_update_mib_counters(sc->ah);
> +
> +	stats->dot11ACKFailureCount = sc->stats.ack_fail;
> +	stats->dot11RTSFailureCount = sc->stats.rts_fail;
> +	stats->dot11RTSSuccessCount = sc->stats.rts_ok;
> +	stats->dot11FCSErrorCount = sc->stats.fcs_error;
> +
> +	return 0;
> +}
> +
> +static int ath5k_get_survey(struct ieee80211_hw *hw, int idx,
> +		struct survey_info *survey)
> +{
> +	struct ath5k_softc *sc = hw->priv;
> +	struct ieee80211_conf *conf = &hw->conf;
> +
> +	 if (idx != 0)
> +		return -ENOENT;
> +
> +	survey->channel = conf->channel;
> +	survey->filled = SURVEY_INFO_NOISE_DBM;
> +	survey->noise = sc->ah->ah_noise_floor;
> +
> +	return 0;
> +}
> +
> +static u64
> +ath5k_get_tsf(struct ieee80211_hw *hw)
> +{
> +	struct ath5k_softc *sc = hw->priv;
> +
> +	return ath5k_hw_get_tsf64(sc->ah);
> +}
> +
> +static void
> +ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf)
> +{
> +	struct ath5k_softc *sc = hw->priv;
> +
> +	ath5k_hw_set_tsf64(sc->ah, tsf);
> +}
> +
> +static void
> +ath5k_reset_tsf(struct ieee80211_hw *hw)
> +{
> +	struct ath5k_softc *sc = hw->priv;
> +
> +	/*
> +	 * in IBSS mode we need to update the beacon timers too.
> +	 * this will also reset the TSF if we call it with 0
> +	 */
> +	if (sc->opmode == NL80211_IFTYPE_ADHOC)
> +		ath5k_beacon_update_timers(sc, 0);
> +	else
> +		ath5k_hw_reset_tsf(sc->ah);
> +}
> +
> +static void
> +set_beacon_filter(struct ieee80211_hw *hw, bool enable)
> +{
> +	struct ath5k_softc *sc = hw->priv;
> +	struct ath5k_hw *ah = sc->ah;
> +	u32 rfilt;
> +	rfilt = ath5k_hw_get_rx_filter(ah);
> +	if (enable)
> +		rfilt |= AR5K_RX_FILTER_BEACON;
> +	else
> +		rfilt &= ~AR5K_RX_FILTER_BEACON;
> +	ath5k_hw_set_rx_filter(ah, rfilt);
> +	sc->filter_flags = rfilt;
> +}
> +
> +static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
> +				    struct ieee80211_vif *vif,
> +				    struct ieee80211_bss_conf *bss_conf,
> +				    u32 changes)
> +{
> +	struct ath5k_softc *sc = hw->priv;
> +	struct ath5k_hw *ah = sc->ah;
> +	struct ath_common *common = ath5k_hw_common(ah);
> +	unsigned long flags;
> +
> +	mutex_lock(&sc->lock);
> +	if (WARN_ON(sc->vif != vif))
> +		goto unlock;
> +
> +	if (changes & BSS_CHANGED_BSSID) {
> +		/* Cache for later use during resets */
> +		memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
> +		common->curaid = 0;
> +		ath5k_hw_set_bssid(ah);
> +		mmiowb();
> +	}
> +
> +	if (changes & BSS_CHANGED_BEACON_INT)
> +		sc->bintval = bss_conf->beacon_int;
> +
> +	if (changes & BSS_CHANGED_ASSOC) {
> +		sc->assoc = bss_conf->assoc;
> +		if (sc->opmode == NL80211_IFTYPE_STATION)
> +			set_beacon_filter(hw, sc->assoc);
> +		ath5k_hw_set_ledstate(sc->ah, sc->assoc ?
> +			AR5K_LED_ASSOC : AR5K_LED_INIT);
> +		if (bss_conf->assoc) {
> +			ATH5K_DBG(sc, ATH5K_DEBUG_ANY,
> +				  "Bss Info ASSOC %d, bssid: %pM\n",
> +				  bss_conf->aid, common->curbssid);
> +			common->curaid = bss_conf->aid;
> +			ath5k_hw_set_bssid(ah);
> +			/* Once ANI is available you would start it here */
> +		}
> +	}
> +
> +	if (changes & BSS_CHANGED_BEACON) {
> +		spin_lock_irqsave(&sc->block, flags);
> +		ath5k_beacon_update(hw, vif);
> +		spin_unlock_irqrestore(&sc->block, flags);
> +	}
> +
> +	if (changes & BSS_CHANGED_BEACON_ENABLED)
> +		sc->enable_beacon = bss_conf->enable_beacon;
> +
> +	if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED |
> +		       BSS_CHANGED_BEACON_INT))
> +		ath5k_beacon_config(sc);
> +
> + unlock:
> +	mutex_unlock(&sc->lock);
> +}
> +
> +static void ath5k_sw_scan_start(struct ieee80211_hw *hw)
> +{
> +	struct ath5k_softc *sc = hw->priv;
> +	if (!sc->assoc)
> +		ath5k_hw_set_ledstate(sc->ah, AR5K_LED_SCAN);
> +}
> +
> +static void ath5k_sw_scan_complete(struct ieee80211_hw *hw)
> +{
> +	struct ath5k_softc *sc = hw->priv;
> +	ath5k_hw_set_ledstate(sc->ah, sc->assoc ?
> +		AR5K_LED_ASSOC : AR5K_LED_INIT);
> +}
> +
> +/**
> + * ath5k_set_coverage_class - Set IEEE 802.11 coverage class
> + *
> + * @hw: struct ieee80211_hw pointer
> + * @coverage_class: IEEE 802.11 coverage class number
> + *
> + * Mac80211 callback. Sets slot time, ACK timeout and CTS timeout for
> given + * coverage class. The values are persistent, they are restored
> after device + * reset.
> + */
> +static void ath5k_set_coverage_class(struct ieee80211_hw *hw, u8
> coverage_class) +{
> +	struct ath5k_softc *sc = hw->priv;
> +
> +	mutex_lock(&sc->lock);
> +	ath5k_hw_set_coverage_class(sc->ah, coverage_class);
> +	mutex_unlock(&sc->lock);
> +}
> +
> +static const struct ieee80211_ops ath5k_hw_ops = {
> +	.tx 		= ath5k_tx,
> +	.start 		= ath5k_start,
> +	.stop 		= ath5k_stop,
> +	.add_interface 	= ath5k_add_interface,
> +	.remove_interface = ath5k_remove_interface,
> +	.config 	= ath5k_config,
> +	.prepare_multicast = ath5k_prepare_multicast,
> +	.configure_filter = ath5k_configure_filter,
> +	.set_key 	= ath5k_set_key,
> +	.get_stats 	= ath5k_get_stats,
> +	.get_survey	= ath5k_get_survey,
> +	.conf_tx 	= NULL,
> +	.get_tsf 	= ath5k_get_tsf,
> +	.set_tsf 	= ath5k_set_tsf,
> +	.reset_tsf 	= ath5k_reset_tsf,
> +	.bss_info_changed = ath5k_bss_info_changed,
> +	.sw_scan_start	= ath5k_sw_scan_start,
> +	.sw_scan_complete = ath5k_sw_scan_complete,
> +	.set_coverage_class = ath5k_set_coverage_class,
> +};
> +
> +/********************\
> +* PCI Initialization *
> +\********************/
> +
> +static int __devinit
> +ath5k_pci_probe(struct pci_dev *pdev,
> +		const struct pci_device_id *id)
> +{
> +	void __iomem *mem;
> +	struct ath5k_softc *sc;
> +	struct ath_common *common;
> +	struct ieee80211_hw *hw;
> +	int ret;
> +	u8 csz;
> +
> +	/*
> +	 * L0s needs to be disabled on all ath5k cards.
> +	 *
> +	 * For distributions shipping with CONFIG_PCIEASPM (this will be enabled
> +	 * by default in the future in 2.6.36) this will also mean both L1 and
> +	 * L0s will be disabled when a pre 1.1 PCIe device is detected. We do
> +	 * know L1 works correctly even for all ath5k pre 1.1 PCIe devices
> +	 * though but cannot currently undue the effect of a blacklist, for
> +	 * details you can read pcie_aspm_sanity_check() and see how it adjusts
> +	 * the device link capability.
> +	 *
> +	 * It may be possible in the future to implement some PCI API to allow
> +	 * drivers to override blacklists for pre 1.1 PCIe but for now it is
> +	 * best to accept that both L0s and L1 will be disabled completely for
> +	 * distributions shipping with CONFIG_PCIEASPM rather than having this
> +	 * issue present. Motivation for adding this new API will be to help
> +	 * with power consumption for some of these devices.
> +	 */
> +	pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S);
> +
> +	ret = pci_enable_device(pdev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can't enable device\n");
> +		goto err;
> +	}
> +
> +	/* XXX 32-bit addressing only */
> +	ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
> +	if (ret) {
> +		dev_err(&pdev->dev, "32-bit DMA not available\n");
> +		goto err_dis;
> +	}
> +
> +	/*
> +	 * Cache line size is used to size and align various
> +	 * structures used to communicate with the hardware.
> +	 */
> +	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz);
> +	if (csz == 0) {
> +		/*
> +		 * Linux 2.4.18 (at least) writes the cache line size
> +		 * register as a 16-bit wide register which is wrong.
> +		 * We must have this setup properly for rx buffer
> +		 * DMA to work so force a reasonable value here if it
> +		 * comes up zero.
> +		 */
> +		csz = L1_CACHE_BYTES >> 2;
> +		pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz);
> +	}
> +	/*
> +	 * The default setting of latency timer yields poor results,
> +	 * set it to the value used by other systems.  It may be worth
> +	 * tweaking this setting more.
> +	 */
> +	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8);
> +
> +	/* Enable bus mastering */
> +	pci_set_master(pdev);
> +
> +	/*
> +	 * Disable the RETRY_TIMEOUT register (0x41) to keep
> +	 * PCI Tx retries from interfering with C3 CPU state.
> +	 */
> +	pci_write_config_byte(pdev, 0x41, 0);
> +
> +	ret = pci_request_region(pdev, 0, "ath5k");
> +	if (ret) {
> +		dev_err(&pdev->dev, "cannot reserve PCI memory region\n");
> +		goto err_dis;
> +	}
> +
> +	mem = pci_iomap(pdev, 0, 0);
> +	if (!mem) {
> +		dev_err(&pdev->dev, "cannot remap PCI memory region\n") ;
> +		ret = -EIO;
> +		goto err_reg;
> +	}
> +
> +	/*
> +	 * Allocate hw (mac80211 main struct)
> +	 * and hw->priv (driver private data)
> +	 */
> +	hw = ieee80211_alloc_hw(sizeof(*sc), &ath5k_hw_ops);
> +	if (hw == NULL) {
> +		dev_err(&pdev->dev, "cannot allocate ieee80211_hw\n");
> +		ret = -ENOMEM;
> +		goto err_map;
> +	}
> +
> +	dev_info(&pdev->dev, "registered as '%s'\n", wiphy_name(hw->wiphy));
> +
> +	/* Initialize driver private data */
> +	SET_IEEE80211_DEV(hw, &pdev->dev);
> +	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
> +		    IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
> +		    IEEE80211_HW_SIGNAL_DBM;
> +
> +	hw->wiphy->interface_modes =
> +		BIT(NL80211_IFTYPE_AP) |
> +		BIT(NL80211_IFTYPE_STATION) |
> +		BIT(NL80211_IFTYPE_ADHOC) |
> +		BIT(NL80211_IFTYPE_MESH_POINT);
> +
> +	hw->extra_tx_headroom = 2;
> +	hw->channel_change_time = 5000;
> +	sc = hw->priv;
> +	sc->hw = hw;
> +	sc->pdev = pdev;
> +
> +	ath5k_debug_init_device(sc);
> +
> +	/*
> +	 * Mark the device as detached to avoid processing
> +	 * interrupts until setup is complete.
> +	 */
> +	__set_bit(ATH_STAT_INVALID, sc->status);
> +
> +	sc->iobase = mem; /* So we can unmap it on detach */
> +	sc->opmode = NL80211_IFTYPE_STATION;
> +	sc->bintval = 1000;
> +	mutex_init(&sc->lock);
> +	spin_lock_init(&sc->rxbuflock);
> +	spin_lock_init(&sc->txbuflock);
> +	spin_lock_init(&sc->block);
> +
> +	/* Set private data */
> +	pci_set_drvdata(pdev, sc);
> +
> +	/* Setup interrupt handler */
> +	ret = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc);
> +	if (ret) {
> +		ATH5K_ERR(sc, "request_irq failed\n");
> +		goto err_free;
> +	}
> +
> +	/* If we passed the test, malloc an ath5k_hw struct */
> +	sc->ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL);
> +	if (!sc->ah) {
> +		ret = -ENOMEM;
> +		ATH5K_ERR(sc, "out of memory\n");
> +		goto err_irq;
> +	}
> +
> +	sc->ah->ah_sc = sc;
> +	sc->ah->ah_iobase = sc->iobase;
> +	common = ath5k_hw_common(sc->ah);
> +	common->ops = &ath5k_common_ops;
> +	common->ah = sc->ah;
> +	common->hw = hw;
> +	common->cachelsz = csz << 2; /* convert to bytes */
> +
> +	/* Initialize device */
> +	ret = ath5k_hw_attach(sc);
> +	if (ret) {
> +		goto err_free_ah;
> +	}
> +
> +	/* set up multi-rate retry capabilities */
> +	if (sc->ah->ah_version == AR5K_AR5212) {
> +		hw->max_rates = 4;
> +		hw->max_rate_tries = 11;
> +	}
> +
> +	/* Finish private driver data initialization */
> +	ret = ath5k_attach(pdev, hw);
> +	if (ret)
> +		goto err_ah;
> +
> +	ATH5K_INFO(sc, "Atheros AR%s chip found (MAC: 0x%x, PHY: 0x%x)\n",
> +			ath5k_chip_name(AR5K_VERSION_MAC, sc->ah->ah_mac_srev),
> +					sc->ah->ah_mac_srev,
> +					sc->ah->ah_phy_revision);
> +
> +	if (!sc->ah->ah_single_chip) {
> +		/* Single chip radio (!RF5111) */
> +		if (sc->ah->ah_radio_5ghz_revision &&
> +			!sc->ah->ah_radio_2ghz_revision) {
> +			/* No 5GHz support -> report 2GHz radio */
> +			if (!test_bit(AR5K_MODE_11A,
> +				sc->ah->ah_capabilities.cap_mode)) {
> +				ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n",
> +					ath5k_chip_name(AR5K_VERSION_RAD,
> +						sc->ah->ah_radio_5ghz_revision),
> +						sc->ah->ah_radio_5ghz_revision);
> +			/* No 2GHz support (5110 and some
> +			 * 5Ghz only cards) -> report 5Ghz radio */
> +			} else if (!test_bit(AR5K_MODE_11B,
> +				sc->ah->ah_capabilities.cap_mode)) {
> +				ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n",
> +					ath5k_chip_name(AR5K_VERSION_RAD,
> +						sc->ah->ah_radio_5ghz_revision),
> +						sc->ah->ah_radio_5ghz_revision);
> +			/* Multiband radio */
> +			} else {
> +				ATH5K_INFO(sc, "RF%s multiband radio found"
> +					" (0x%x)\n",
> +					ath5k_chip_name(AR5K_VERSION_RAD,
> +						sc->ah->ah_radio_5ghz_revision),
> +						sc->ah->ah_radio_5ghz_revision);
> +			}
> +		}
> +		/* Multi chip radio (RF5111 - RF2111) ->
> +		 * report both 2GHz/5GHz radios */
> +		else if (sc->ah->ah_radio_5ghz_revision &&
> +				sc->ah->ah_radio_2ghz_revision){
> +			ATH5K_INFO(sc, "RF%s 5GHz radio found (0x%x)\n",
> +				ath5k_chip_name(AR5K_VERSION_RAD,
> +					sc->ah->ah_radio_5ghz_revision),
> +					sc->ah->ah_radio_5ghz_revision);
> +			ATH5K_INFO(sc, "RF%s 2GHz radio found (0x%x)\n",
> +				ath5k_chip_name(AR5K_VERSION_RAD,
> +					sc->ah->ah_radio_2ghz_revision),
> +					sc->ah->ah_radio_2ghz_revision);
> +		}
> +	}
> +
> 
> -	survey->channel = conf->channel;
> -	survey->filled = SURVEY_INFO_NOISE_DBM;
> -	survey->noise = sc->ah->ah_noise_floor;
> +	/* ready to process interrupts */
> +	__clear_bit(ATH_STAT_INVALID, sc->status);
> 
>  	return 0;
> +err_ah:
> +	ath5k_hw_detach(sc->ah);
> +err_free_ah:
> +	kfree(sc->ah);
> +err_irq:
> +	free_irq(pdev->irq, sc);
> +err_free:
> +	ieee80211_free_hw(hw);
> +err_map:
> +	pci_iounmap(pdev, mem);
> +err_reg:
> +	pci_release_region(pdev, 0);
> +err_dis:
> +	pci_disable_device(pdev);
> +err:
> +	return ret;
>  }
> 
> -static u64
> -ath5k_get_tsf(struct ieee80211_hw *hw)
> +static void __devexit
> +ath5k_pci_remove(struct pci_dev *pdev)
>  {
> -	struct ath5k_softc *sc = hw->priv;
> +	struct ath5k_softc *sc = pci_get_drvdata(pdev);
> 
> -	return ath5k_hw_get_tsf64(sc->ah);
> +	ath5k_debug_finish_device(sc);
> +	ath5k_detach(pdev, sc->hw);
> +	ath5k_hw_detach(sc->ah);
> +	kfree(sc->ah);
> +	free_irq(pdev->irq, sc);
> +	pci_iounmap(pdev, sc->iobase);
> +	pci_release_region(pdev, 0);
> +	pci_disable_device(pdev);
> +	ieee80211_free_hw(sc->hw);
>  }
> 
> -static void
> -ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf)
> +#ifdef CONFIG_PM_SLEEP
> +static int ath5k_pci_suspend(struct device *dev)
>  {
> -	struct ath5k_softc *sc = hw->priv;
> +	struct ath5k_softc *sc = pci_get_drvdata(to_pci_dev(dev));
> 
> -	ath5k_hw_set_tsf64(sc->ah, tsf);
> +	ath5k_led_off(sc);
> +	return 0;
>  }
> 
> -static void
> -ath5k_reset_tsf(struct ieee80211_hw *hw)
> +static int ath5k_pci_resume(struct device *dev)
>  {
> -	struct ath5k_softc *sc = hw->priv;
> +	struct pci_dev *pdev = to_pci_dev(dev);
> +	struct ath5k_softc *sc = pci_get_drvdata(pdev);
> 
>  	/*
> -	 * in IBSS mode we need to update the beacon timers too.
> -	 * this will also reset the TSF if we call it with 0
> +	 * Suspend/Resume resets the PCI configuration space, so we have to
> +	 * re-disable the RETRY_TIMEOUT register (0x41) to keep
> +	 * PCI Tx retries from interfering with C3 CPU state
>  	 */
> -	if (sc->opmode == NL80211_IFTYPE_ADHOC)
> -		ath5k_beacon_update_timers(sc, 0);
> -	else
> -		ath5k_hw_reset_tsf(sc->ah);
> +	pci_write_config_byte(pdev, 0x41, 0);
> +
> +	ath5k_led_enable(sc);
> +	return 0;
>  }
> 
> +static SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend,
> ath5k_pci_resume); +#define ATH5K_PM_OPS	(&ath5k_pm_ops)
> +#else
> +#define ATH5K_PM_OPS	NULL
> +#endif /* CONFIG_PM_SLEEP */
> +
> +static struct pci_driver ath5k_pci_driver = {
> +	.name		= KBUILD_MODNAME,
> +	.id_table	= ath5k_pci_id_table,
> +	.probe		= ath5k_pci_probe,
> +	.remove		= __devexit_p(ath5k_pci_remove),
> +	.driver.pm	= ATH5K_PM_OPS,
> +};
> +
>  /*
> - * Updates the beacon that is sent by ath5k_beacon_send.  For adhoc,
> - * this is called only once at config_bss time, for AP we do it every
> - * SWBA interrupt so that the TIM will reflect buffered frames.
> - *
> - * Called with the beacon lock.
> + * Module init/exit functions
>   */
> -static int
> -ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
> +static int __init
> +init_ath5k_pci(void)
>  {
>  	int ret;
> -	struct ath5k_softc *sc = hw->priv;
> -	struct sk_buff *skb;
> -
> -	if (WARN_ON(!vif)) {
> -		ret = -EINVAL;
> -		goto out;
> -	}
> -
> -	skb = ieee80211_beacon_get(hw, vif);
> -
> -	if (!skb) {
> -		ret = -ENOMEM;
> -		goto out;
> -	}
> -
> -	ath5k_debug_dump_skb(sc, skb, "BC  ", 1);
> -
> -	ath5k_txbuf_free_skb(sc, sc->bbuf);
> -	sc->bbuf->skb = skb;
> -	ret = ath5k_beacon_setup(sc, sc->bbuf);
> -	if (ret)
> -		sc->bbuf->skb = NULL;
> -out:
> -	return ret;
> -}
> -
> -static void
> -set_beacon_filter(struct ieee80211_hw *hw, bool enable)
> -{
> -	struct ath5k_softc *sc = hw->priv;
> -	struct ath5k_hw *ah = sc->ah;
> -	u32 rfilt;
> -	rfilt = ath5k_hw_get_rx_filter(ah);
> -	if (enable)
> -		rfilt |= AR5K_RX_FILTER_BEACON;
> -	else
> -		rfilt &= ~AR5K_RX_FILTER_BEACON;
> -	ath5k_hw_set_rx_filter(ah, rfilt);
> -	sc->filter_flags = rfilt;
> -}
> -
> -static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
> -				    struct ieee80211_vif *vif,
> -				    struct ieee80211_bss_conf *bss_conf,
> -				    u32 changes)
> -{
> -	struct ath5k_softc *sc = hw->priv;
> -	struct ath5k_hw *ah = sc->ah;
> -	struct ath_common *common = ath5k_hw_common(ah);
> -	unsigned long flags;
> -
> -	mutex_lock(&sc->lock);
> -	if (WARN_ON(sc->vif != vif))
> -		goto unlock;
> -
> -	if (changes & BSS_CHANGED_BSSID) {
> -		/* Cache for later use during resets */
> -		memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
> -		common->curaid = 0;
> -		ath5k_hw_set_bssid(ah);
> -		mmiowb();
> -	}
> -
> -	if (changes & BSS_CHANGED_BEACON_INT)
> -		sc->bintval = bss_conf->beacon_int;
> 
> -	if (changes & BSS_CHANGED_ASSOC) {
> -		sc->assoc = bss_conf->assoc;
> -		if (sc->opmode == NL80211_IFTYPE_STATION)
> -			set_beacon_filter(hw, sc->assoc);
> -		ath5k_hw_set_ledstate(sc->ah, sc->assoc ?
> -			AR5K_LED_ASSOC : AR5K_LED_INIT);
> -		if (bss_conf->assoc) {
> -			ATH5K_DBG(sc, ATH5K_DEBUG_ANY,
> -				  "Bss Info ASSOC %d, bssid: %pM\n",
> -				  bss_conf->aid, common->curbssid);
> -			common->curaid = bss_conf->aid;
> -			ath5k_hw_set_bssid(ah);
> -			/* Once ANI is available you would start it here */
> -		}
> -	}
> +	ath5k_debug_init();
> 
> -	if (changes & BSS_CHANGED_BEACON) {
> -		spin_lock_irqsave(&sc->block, flags);
> -		ath5k_beacon_update(hw, vif);
> -		spin_unlock_irqrestore(&sc->block, flags);
> +	ret = pci_register_driver(&ath5k_pci_driver);
> +	if (ret) {
> +		printk(KERN_ERR "ath5k_pci: can't register pci driver\n");
> +		return ret;
>  	}
> 
> -	if (changes & BSS_CHANGED_BEACON_ENABLED)
> -		sc->enable_beacon = bss_conf->enable_beacon;
> -
> -	if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED |
> -		       BSS_CHANGED_BEACON_INT))
> -		ath5k_beacon_config(sc);
> -
> - unlock:
> -	mutex_unlock(&sc->lock);
> +	return 0;
>  }
> 
> -static void ath5k_sw_scan_start(struct ieee80211_hw *hw)
> +static void __exit
> +exit_ath5k_pci(void)
>  {
> -	struct ath5k_softc *sc = hw->priv;
> -	if (!sc->assoc)
> -		ath5k_hw_set_ledstate(sc->ah, AR5K_LED_SCAN);
> -}
> +	pci_unregister_driver(&ath5k_pci_driver);
> 
> -static void ath5k_sw_scan_complete(struct ieee80211_hw *hw)
> -{
> -	struct ath5k_softc *sc = hw->priv;
> -	ath5k_hw_set_ledstate(sc->ah, sc->assoc ?
> -		AR5K_LED_ASSOC : AR5K_LED_INIT);
> +	ath5k_debug_finish();
>  }
> 
> -/**
> - * ath5k_set_coverage_class - Set IEEE 802.11 coverage class
> - *
> - * @hw: struct ieee80211_hw pointer
> - * @coverage_class: IEEE 802.11 coverage class number
> - *
> - * Mac80211 callback. Sets slot time, ACK timeout and CTS timeout for
> given - * coverage class. The values are persistent, they are restored
> after device - * reset.
> - */
> -static void ath5k_set_coverage_class(struct ieee80211_hw *hw, u8
> coverage_class) -{
> -	struct ath5k_softc *sc = hw->priv;
> -
> -	mutex_lock(&sc->lock);
> -	ath5k_hw_set_coverage_class(sc->ah, coverage_class);
> -	mutex_unlock(&sc->lock);
> -}
> +module_init(init_ath5k_pci);
> +module_exit(exit_ath5k_pci);

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] ath5k: reorder base.c to remove fwd decls
  2010-09-11 19:06 [PATCH] ath5k: reorder base.c to remove fwd decls Bob Copeland
  2010-09-11 19:16 ` [ath5k-devel] " Bob Copeland
  2010-09-13  1:33 ` Bruno Randolf
@ 2010-09-16 19:36 ` John W. Linville
  2010-09-16 23:18   ` Bob Copeland
  2 siblings, 1 reply; 6+ messages in thread
From: John W. Linville @ 2010-09-16 19:36 UTC (permalink / raw)
  To: Bob Copeland
  Cc: jirislaby, mickflemm, lrodriguez, br1, linux-wireless, ath5k-devel

On Sat, Sep 11, 2010 at 03:06:36PM -0400, Bob Copeland wrote:
> This change reorganizes the main ath5k file in order to re-group
> related functions and remove most of the forward declarations
> (from 61 down to 3).  This is, unfortunately, a lot of churn, but
> there should be no functional changes.
> 
> Signed-off-by: Bob Copeland <me@bobcopeland.com>
> ---
> 
> Worth the churn?  Is there any way to do this kind of patch that
> doesn't suck?

Apparently not...

Well, I hate to ask for a rebase -- but this doesn't apply anymore!
I would just fix it up, but that offending hunk is 2500 lines or so...

John
-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] ath5k: reorder base.c to remove fwd decls
  2010-09-16 19:36 ` John W. Linville
@ 2010-09-16 23:18   ` Bob Copeland
  2010-09-17  2:37     ` Bruno Randolf
  0 siblings, 1 reply; 6+ messages in thread
From: Bob Copeland @ 2010-09-16 23:18 UTC (permalink / raw)
  To: John W. Linville
  Cc: jirislaby, mickflemm, lrodriguez, br1, linux-wireless, ath5k-devel

On Thu, Sep 16, 2010 at 03:36:52PM -0400, John W. Linville wrote:
> > Worth the churn?  Is there any way to do this kind of patch that
> > doesn't suck?
> 
> Apparently not...
> 
> Well, I hate to ask for a rebase -- but this doesn't apply anymore!
> I would just fix it up, but that offending hunk is 2500 lines or so...

(for Bruno's benefit since I said as much on irc) -- I'll redo this
one as soon as the crypto series is pushed out in w-t.

-- 
Bob Copeland %% www.bobcopeland.com


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] ath5k: reorder base.c to remove fwd decls
  2010-09-16 23:18   ` Bob Copeland
@ 2010-09-17  2:37     ` Bruno Randolf
  0 siblings, 0 replies; 6+ messages in thread
From: Bruno Randolf @ 2010-09-17  2:37 UTC (permalink / raw)
  To: Bob Copeland
  Cc: John W. Linville, jirislaby, mickflemm, lrodriguez,
	linux-wireless, ath5k-devel

On Fri September 17 2010 08:18:46 Bob Copeland wrote:
> On Thu, Sep 16, 2010 at 03:36:52PM -0400, John W. Linville wrote:
> > > Worth the churn?  Is there any way to do this kind of patch that
> > > doesn't suck?
> > 
> > Apparently not...
> > 
> > Well, I hate to ask for a rebase -- but this doesn't apply anymore!
> > I would just fix it up, but that offending hunk is 2500 lines or so...
> 
> (for Bruno's benefit since I said as much on irc) -- I'll redo this
> one as soon as the crypto series is pushed out in w-t.

just in case you missed it - i have rebased it and sent it out in my last 
series.

bruno

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2010-09-17  2:37 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-09-11 19:06 [PATCH] ath5k: reorder base.c to remove fwd decls Bob Copeland
2010-09-11 19:16 ` [ath5k-devel] " Bob Copeland
2010-09-13  1:33 ` Bruno Randolf
2010-09-16 19:36 ` John W. Linville
2010-09-16 23:18   ` Bob Copeland
2010-09-17  2:37     ` Bruno Randolf

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.