Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[linux-2.6.git] / drivers / net / wireless / ath / ath5k / base.c
index d70856a..93005f1 100644 (file)
@@ -50,6 +50,7 @@
 #include <linux/pci.h>
 #include <linux/ethtool.h>
 #include <linux/uaccess.h>
+#include <linux/slab.h>
 
 #include <net/ieee80211_radiotap.h>
 
@@ -58,8 +59,8 @@
 #include "base.h"
 #include "reg.h"
 #include "debug.h"
+#include "ani.h"
 
-static int ath5k_calinterval = 10; /* Calibrate PHY every 10 secs (TODO: Fixme) */
 static int modparam_nohwcrypt;
 module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
 MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
@@ -83,25 +84,25 @@ MODULE_VERSION("0.6.0 (EXPERIMENTAL)");
 
 
 /* Known PCI ids */
-static const struct pci_device_id ath5k_pci_id_table[] = {
-       { PCI_VDEVICE(ATHEROS, 0x0207), .driver_data = AR5K_AR5210 }, /* 5210 early */
-       { PCI_VDEVICE(ATHEROS, 0x0007), .driver_data = AR5K_AR5210 }, /* 5210 */
-       { PCI_VDEVICE(ATHEROS, 0x0011), .driver_data = AR5K_AR5211 }, /* 5311 - this is on AHB bus !*/
-       { PCI_VDEVICE(ATHEROS, 0x0012), .driver_data = AR5K_AR5211 }, /* 5211 */
-       { PCI_VDEVICE(ATHEROS, 0x0013), .driver_data = AR5K_AR5212 }, /* 5212 */
-       { PCI_VDEVICE(3COM_2,  0x0013), .driver_data = AR5K_AR5212 }, /* 3com 5212 */
-       { PCI_VDEVICE(3COM,    0x0013), .driver_data = AR5K_AR5212 }, /* 3com 3CRDAG675 5212 */
-       { PCI_VDEVICE(ATHEROS, 0x1014), .driver_data = AR5K_AR5212 }, /* IBM minipci 5212 */
-       { PCI_VDEVICE(ATHEROS, 0x0014), .driver_data = AR5K_AR5212 }, /* 5212 combatible */
-       { PCI_VDEVICE(ATHEROS, 0x0015), .driver_data = AR5K_AR5212 }, /* 5212 combatible */
-       { PCI_VDEVICE(ATHEROS, 0x0016), .driver_data = AR5K_AR5212 }, /* 5212 combatible */
-       { PCI_VDEVICE(ATHEROS, 0x0017), .driver_data = AR5K_AR5212 }, /* 5212 combatible */
-       { PCI_VDEVICE(ATHEROS, 0x0018), .driver_data = AR5K_AR5212 }, /* 5212 combatible */
-       { PCI_VDEVICE(ATHEROS, 0x0019), .driver_data = AR5K_AR5212 }, /* 5212 combatible */
-       { PCI_VDEVICE(ATHEROS, 0x001a), .driver_data = AR5K_AR5212 }, /* 2413 Griffin-lite */
-       { PCI_VDEVICE(ATHEROS, 0x001b), .driver_data = AR5K_AR5212 }, /* 5413 Eagle */
-       { PCI_VDEVICE(ATHEROS, 0x001c), .driver_data = AR5K_AR5212 }, /* PCI-E cards */
-       { PCI_VDEVICE(ATHEROS, 0x001d), .driver_data = AR5K_AR5212 }, /* 2417 Nala */
+static DEFINE_PCI_DEVICE_TABLE(ath5k_pci_id_table) = {
+       { PCI_VDEVICE(ATHEROS, 0x0207) }, /* 5210 early */
+       { PCI_VDEVICE(ATHEROS, 0x0007) }, /* 5210 */
+       { PCI_VDEVICE(ATHEROS, 0x0011) }, /* 5311 - this is on AHB bus !*/
+       { PCI_VDEVICE(ATHEROS, 0x0012) }, /* 5211 */
+       { PCI_VDEVICE(ATHEROS, 0x0013) }, /* 5212 */
+       { PCI_VDEVICE(3COM_2,  0x0013) }, /* 3com 5212 */
+       { PCI_VDEVICE(3COM,    0x0013) }, /* 3com 3CRDAG675 5212 */
+       { PCI_VDEVICE(ATHEROS, 0x1014) }, /* IBM minipci 5212 */
+       { PCI_VDEVICE(ATHEROS, 0x0014) }, /* 5212 combatible */
+       { PCI_VDEVICE(ATHEROS, 0x0015) }, /* 5212 combatible */
+       { PCI_VDEVICE(ATHEROS, 0x0016) }, /* 5212 combatible */
+       { PCI_VDEVICE(ATHEROS, 0x0017) }, /* 5212 combatible */
+       { PCI_VDEVICE(ATHEROS, 0x0018) }, /* 5212 combatible */
+       { PCI_VDEVICE(ATHEROS, 0x0019) }, /* 5212 combatible */
+       { PCI_VDEVICE(ATHEROS, 0x001a) }, /* 2413 Griffin-lite */
+       { PCI_VDEVICE(ATHEROS, 0x001b) }, /* 5413 Eagle */
+       { PCI_VDEVICE(ATHEROS, 0x001c) }, /* PCI-E cards */
+       { PCI_VDEVICE(ATHEROS, 0x001d) }, /* 2417 Nala */
        { 0 }
 };
 MODULE_DEVICE_TABLE(pci, ath5k_pci_id_table);
@@ -195,12 +196,13 @@ 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
-static int             ath5k_pci_suspend(struct pci_dev *pdev,
-                                       pm_message_t state);
-static int             ath5k_pci_resume(struct pci_dev *pdev);
+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_pci_suspend NULL
-#define ath5k_pci_resume NULL
+#define ATH5K_PM_OPS   NULL
 #endif /* CONFIG_PM */
 
 static struct pci_driver ath5k_pci_driver = {
@@ -208,8 +210,7 @@ static struct pci_driver ath5k_pci_driver = {
        .id_table       = ath5k_pci_id_table,
        .probe          = ath5k_pci_probe,
        .remove         = __devexit_p(ath5k_pci_remove),
-       .suspend        = ath5k_pci_suspend,
-       .resume         = ath5k_pci_resume,
+       .driver.pm      = ATH5K_PM_OPS,
 };
 
 
@@ -218,36 +219,42 @@ static struct pci_driver ath5k_pci_driver = {
  * Prototypes - MAC 802.11 stack related functions
  */
 static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
-static int ath5k_reset(struct ath5k_softc *sc, bool stop, bool change_channel);
+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_reset_wake(struct ath5k_softc *sc);
 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_if_init_conf *conf);
+               struct ieee80211_vif *vif);
 static void ath5k_remove_interface(struct ieee80211_hw *hw,
-               struct ieee80211_if_init_conf *conf);
+               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,
-               int mc_count, struct dev_mc_list *mclist);
+               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_tx_stats(struct ieee80211_hw *hw,
-               struct ieee80211_tx_queue_stats *stats);
 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 ath5k_softc *sc,
-               struct sk_buff *skb);
+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,
@@ -256,15 +263,18 @@ static const struct ieee80211_ops ath5k_hw_ops = {
        .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,
        .conf_tx        = NULL,
-       .get_tx_stats   = ath5k_get_tx_stats,
        .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,
 };
 
 /*
@@ -297,7 +307,8 @@ static void ath5k_desc_free(struct ath5k_softc *sc,
 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_buf *bf,
+                               struct ath5k_txq *txq, int padsize);
 static inline void ath5k_txbuf_free(struct ath5k_softc *sc,
                                struct ath5k_buf *bf)
 {
@@ -313,10 +324,13 @@ static inline void ath5k_txbuf_free(struct ath5k_softc *sc,
 static inline void ath5k_rxbuf_free(struct ath5k_softc *sc,
                                struct ath5k_buf *bf)
 {
+       struct ath5k_hw *ah = sc->ah;
+       struct ath_common *common = ath5k_hw_common(ah);
+
        BUG_ON(!bf);
        if (!bf->skb)
                return;
-       pci_unmap_single(sc->pdev, bf->skbaddr, sc->rxbufsize,
+       pci_unmap_single(sc->pdev, bf->skbaddr, common->rx_bufsize,
                        PCI_DMA_FROMDEVICE);
        dev_kfree_skb_any(bf->skb);
        bf->skb = NULL;
@@ -351,6 +365,7 @@ 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)
 {
@@ -369,7 +384,7 @@ static int  ath5k_stop_hw(struct ath5k_softc *sc);
 static irqreturn_t ath5k_intr(int irq, void *dev_id);
 static void    ath5k_tasklet_reset(unsigned long data);
 
-static void    ath5k_calibrate(unsigned long data);
+static void    ath5k_tasklet_calibrate(unsigned long data);
 
 /*
  * Module init/exit functions
@@ -427,6 +442,22 @@ ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val)
 
        return name;
 }
+static unsigned int ath5k_ioread32(void *hw_priv, u32 reg_offset)
+{
+       struct ath5k_hw *ah = (struct ath5k_hw *) hw_priv;
+       return ath5k_hw_reg_read(ah, reg_offset);
+}
+
+static void ath5k_iowrite32(void *hw_priv, u32 val, u32 reg_offset)
+{
+       struct ath5k_hw *ah = (struct ath5k_hw *) hw_priv;
+       ath5k_hw_reg_write(ah, val, reg_offset);
+}
+
+static const struct ath_ops ath5k_common_ops = {
+       .read = ath5k_ioread32,
+       .write = ath5k_iowrite32,
+};
 
 static int __devinit
 ath5k_pci_probe(struct pci_dev *pdev,
@@ -434,6 +465,7 @@ ath5k_pci_probe(struct pci_dev *pdev,
 {
        void __iomem *mem;
        struct ath5k_softc *sc;
+       struct ath_common *common;
        struct ieee80211_hw *hw;
        int ret;
        u8 csz;
@@ -464,7 +496,7 @@ ath5k_pci_probe(struct pci_dev *pdev,
                 * DMA to work so force a reasonable value here if it
                 * comes up zero.
                 */
-               csz = L1_CACHE_BYTES / sizeof(u32);
+               csz = L1_CACHE_BYTES >> 2;
                pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz);
        }
        /*
@@ -512,10 +544,12 @@ ath5k_pci_probe(struct pci_dev *pdev,
        /* 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 |
                    IEEE80211_HW_NOISE_DBM;
 
        hw->wiphy->interface_modes =
+               BIT(NL80211_IFTYPE_AP) |
                BIT(NL80211_IFTYPE_STATION) |
                BIT(NL80211_IFTYPE_ADHOC) |
                BIT(NL80211_IFTYPE_MESH_POINT);
@@ -535,8 +569,8 @@ ath5k_pci_probe(struct pci_dev *pdev,
        __set_bit(ATH_STAT_INVALID, sc->status);
 
        sc->iobase = mem; /* So we can unmap it on detach */
-       sc->cachelsz = csz * sizeof(u32); /* convert to bytes */
        sc->opmode = NL80211_IFTYPE_STATION;
+       sc->bintval = 1000;
        mutex_init(&sc->lock);
        spin_lock_init(&sc->rxbuflock);
        spin_lock_init(&sc->txbuflock);
@@ -552,13 +586,28 @@ ath5k_pci_probe(struct pci_dev *pdev,
                goto err_free;
        }
 
-       /* Initialize device */
-       sc->ah = ath5k_hw_attach(sc, id->driver_data);
-       if (IS_ERR(sc->ah)) {
-               ret = PTR_ERR(sc->ah);
+       /*If we passed the test malloc a 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;
@@ -627,6 +676,8 @@ err_ah:
        ath5k_hw_detach(sc->ah);
 err_irq:
        free_irq(pdev->irq, sc);
+err_free_ah:
+       kfree(sc->ah);
 err_free:
        ieee80211_free_hw(hw);
 err_map:
@@ -648,6 +699,7 @@ ath5k_pci_remove(struct pci_dev *pdev)
        ath5k_debug_finish_device(sc);
        ath5k_detach(pdev, hw);
        ath5k_hw_detach(sc->ah);
+       kfree(sc->ah);
        free_irq(pdev->irq, sc);
        pci_iounmap(pdev, sc->iobase);
        pci_release_region(pdev, 0);
@@ -656,47 +708,30 @@ ath5k_pci_remove(struct pci_dev *pdev)
 }
 
 #ifdef CONFIG_PM
-static int
-ath5k_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+static int ath5k_pci_suspend(struct device *dev)
 {
-       struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+       struct ieee80211_hw *hw = pci_get_drvdata(to_pci_dev(dev));
        struct ath5k_softc *sc = hw->priv;
 
        ath5k_led_off(sc);
-
-       free_irq(pdev->irq, sc);
-       pci_save_state(pdev);
-       pci_disable_device(pdev);
-       pci_set_power_state(pdev, PCI_D3hot);
-
        return 0;
 }
 
-static int
-ath5k_pci_resume(struct pci_dev *pdev)
+static int ath5k_pci_resume(struct device *dev)
 {
+       struct pci_dev *pdev = to_pci_dev(dev);
        struct ieee80211_hw *hw = pci_get_drvdata(pdev);
        struct ath5k_softc *sc = hw->priv;
-       int err;
 
-       pci_restore_state(pdev);
-
-       err = pci_enable_device(pdev);
-       if (err)
-               return err;
-
-       err = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc);
-       if (err) {
-               ATH5K_ERR(sc, "request_irq failed\n");
-               goto err_no_irq;
-       }
+       /*
+        * 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
+        */
+       pci_write_config_byte(pdev, 0x41, 0);
 
        ath5k_led_enable(sc);
        return 0;
-
-err_no_irq:
-       pci_disable_device(pdev);
-       return err;
 }
 #endif /* CONFIG_PM */
 
@@ -709,9 +744,9 @@ static int ath5k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *re
 {
        struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
        struct ath5k_softc *sc = hw->priv;
-       struct ath_regulatory *reg = &sc->ah->ah_regulatory;
+       struct ath_regulatory *regulatory = ath5k_hw_regulatory(sc->ah);
 
-       return ath_reg_notifier_apply(wiphy, request, reg);
+       return ath_reg_notifier_apply(wiphy, request, regulatory);
 }
 
 static int
@@ -719,6 +754,7 @@ 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;
 
@@ -776,19 +812,26 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
                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;
+       }
 
        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_bhal;
+               goto err_queues;
        }
 
        tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc);
        tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc);
        tasklet_init(&sc->restq, ath5k_tasklet_reset, (unsigned long)sc);
+       tasklet_init(&sc->calib, ath5k_tasklet_calibrate, (unsigned long)sc);
        tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc);
-       setup_timer(&sc->calib_tim, ath5k_calibrate, (unsigned long)sc);
+       tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc);
 
        ret = ath5k_eeprom_read_mac(ah, mac);
        if (ret) {
@@ -799,12 +842,11 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
 
        SET_IEEE80211_PERM_ADDR(hw, mac);
        /* All MAC address bits matter for ACKs */
-       memset(sc->bssidmask, 0xff, ETH_ALEN);
+       memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN);
        ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask);
 
-       ah->ah_regulatory.current_rd =
-               ah->ah_capabilities.cap_eeprom.ee_regdomain;
-       ret = ath_regd_init(&ah->ah_regulatory, hw->wiphy, ath5k_reg_notifier);
+       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;
@@ -816,8 +858,8 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
                goto err_queues;
        }
 
-       if (!ath_is_world_regd(&sc->ah->ah_regulatory))
-               regulatory_hint(hw->wiphy, sc->ah->ah_regulatory.alpha2);
+       if (!ath_is_world_regd(regulatory))
+               regulatory_hint(hw->wiphy, regulatory->alpha2);
 
        ath5k_init_leds(sc);
 
@@ -1059,10 +1101,9 @@ ath5k_setup_bands(struct ieee80211_hw *hw)
 }
 
 /*
- * Set/change channels.  If the channel is really being changed,
- * it's done by reseting the chip.  To accomplish this we must
- * first cleanup any pending DMA, then restart stuff after a la
- * ath5k_init.
+ * 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.
  */
@@ -1072,22 +1113,13 @@ ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan)
        ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "(%u MHz) -> (%u MHz)\n",
                sc->curchan->center_freq, chan->center_freq);
 
-       if (chan->center_freq != sc->curchan->center_freq ||
-               chan->hw_value != sc->curchan->hw_value) {
-
-               sc->curchan = chan;
-               sc->curband = &sc->sbands[chan->band];
-
-               /*
-                * 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, true, true);
-       }
-
-       return 0;
+       /*
+        * 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
@@ -1116,9 +1148,9 @@ ath5k_mode_setup(struct ath5k_softc *sc)
                ath5k_hw_set_bssid_mask(ah, sc->bssidmask);
 
        /* configure operational mode */
-       ath5k_hw_set_opmode(ah);
+       ath5k_hw_set_opmode(ah, sc->opmode);
 
-       ath5k_hw_set_mcast_filter(ah, 0, 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);
 }
 
@@ -1146,31 +1178,26 @@ ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix)
 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;
-       unsigned int off;
 
        /*
         * Allocate buffer with headroom_needed space for the
         * fake physical layer header at the start.
         */
-       skb = dev_alloc_skb(sc->rxbufsize + sc->cachelsz - 1);
+       skb = ath_rxbuf_alloc(common,
+                             common->rx_bufsize,
+                             GFP_ATOMIC);
 
        if (!skb) {
                ATH5K_ERR(sc, "can't alloc skbuff of size %u\n",
-                               sc->rxbufsize + sc->cachelsz - 1);
+                               common->rx_bufsize);
                return NULL;
        }
-       /*
-        * Cache-line-align.  This is important (for the
-        * 5210 at least) as not doing so causes bogus data
-        * in rx'd frames.
-        */
-       off = ((unsigned long)skb->data) % sc->cachelsz;
-       if (off != 0)
-               skb_reserve(skb, sc->cachelsz - off);
 
        *skb_addr = pci_map_single(sc->pdev,
-               skb->data, sc->rxbufsize, PCI_DMA_FROMDEVICE);
+                                  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);
@@ -1221,11 +1248,34 @@ ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
        return 0;
 }
 
+static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr;
+       enum ath5k_pkt_type htype;
+       __le16 fc;
+
+       hdr = (struct ieee80211_hdr *)skb->data;
+       fc = hdr->frame_control;
+
+       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;
+
+       return htype;
+}
+
 static int
-ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
+ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
+                 struct ath5k_txq *txq, int padsize)
 {
        struct ath5k_hw *ah = sc->ah;
-       struct ath5k_txq *txq = sc->txq;
        struct ath5k_desc *ds = bf->desc;
        struct sk_buff *skb = bf->skb;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1275,10 +1325,11 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
                        sc->vif, pktlen, info));
        }
        ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
-               ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL,
+               ieee80211_get_hdrlen_from_skb(skb), padsize,
+               get_hw_packet_type(skb),
                (sc->power_level * 2),
                hw_rate,
-               info->control.rates[0].count, keyidx, 0, flags,
+               info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags,
                cts_rate, duration);
        if (ret)
                goto err_unmap;
@@ -1304,7 +1355,6 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
 
        spin_lock_bh(&txq->lock);
        list_add_tail(&bf->list, &txq->q);
-       sc->tx_stats[txq->qnum].len++;
        if (txq->link == NULL) /* is this first packet? */
                ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr);
        else /* no, so only link it */
@@ -1488,7 +1538,8 @@ ath5k_beaconq_config(struct ath5k_softc *sc)
 
        ret = ath5k_hw_get_tx_queueprops(ah, sc->bhalq, &qi);
        if (ret)
-               return ret;
+               goto err;
+
        if (sc->opmode == NL80211_IFTYPE_AP ||
                sc->opmode == NL80211_IFTYPE_MESH_POINT) {
                /*
@@ -1515,10 +1566,25 @@ ath5k_beaconq_config(struct ath5k_softc *sc)
        if (ret) {
                ATH5K_ERR(sc, "%s: unable to update parameters for beacon "
                        "hardware queue!\n", __func__);
-               return ret;
+               goto err;
        }
+       ret = ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */
+       if (ret)
+               goto err;
 
-       return ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */;
+       /* 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
@@ -1537,7 +1603,6 @@ ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq)
                ath5k_txbuf_free(sc, bf);
 
                spin_lock_bh(&sc->txbuflock);
-               sc->tx_stats[txq->qnum].len--;
                list_move_tail(&bf->list, &sc->txbuf);
                sc->txbuf_len++;
                spin_unlock_bh(&sc->txbuflock);
@@ -1572,7 +1637,6 @@ ath5k_txq_cleanup(struct ath5k_softc *sc)
                                        sc->txqs[i].link);
                        }
        }
-       ieee80211_wake_queues(sc->hw); /* XXX move to callers */
 
        for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
                if (sc->txqs[i].setup)
@@ -1606,13 +1670,14 @@ 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;
 
-       sc->rxbufsize = roundup(IEEE80211_MAX_LEN, sc->cachelsz);
+       common->rx_bufsize = roundup(IEEE80211_MAX_LEN, common->cachelsz);
 
-       ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rxbufsize %u\n",
-               sc->cachelsz, sc->rxbufsize);
+       ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n",
+                 common->cachelsz, common->rx_bufsize);
 
        spin_lock_bh(&sc->rxbuflock);
        sc->rxlink = NULL;
@@ -1657,6 +1722,8 @@ static unsigned int
 ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds,
                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;
 
@@ -1673,7 +1740,7 @@ ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds,
            skb->len >= hlen + 4) {
                keyix = skb->data[hlen + 3] >> 6;
 
-               if (test_bit(keyix, sc->keymap))
+               if (test_bit(keyix, common->keymap))
                        return RX_FLAG_DECRYPTED;
        }
 
@@ -1685,13 +1752,14 @@ 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, sc->ah->ah_bssid, ETH_ALEN) == 0) {
+           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
@@ -1738,48 +1806,100 @@ ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb,
        }
 }
 
-static void ath5k_tasklet_beacon(unsigned long data)
+static void
+ath5k_update_beacon_rssi(struct ath5k_softc *sc, struct sk_buff *skb, int rssi)
 {
-       struct ath5k_softc *sc = (struct ath5k_softc *) data;
+       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+       struct ath5k_hw *ah = sc->ah;
+       struct ath_common *common = ath5k_hw_common(ah);
 
-       /*
-        * Software beacon alert--time to send a beacon.
-        *
-        * In IBSS mode we use this interrupt just to
-        * keep track of the next TBTT (target beacon
-        * transmission time) in order to detect wether
-        * automatic TSF updates happened.
-        */
-       if (sc->opmode == NL80211_IFTYPE_ADHOC) {
-               /* XXX: only if VEOL suppported */
-               u64 tsf = ath5k_hw_get_tsf64(sc->ah);
-               sc->nexttbtt += sc->bintval;
-               ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
-                               "SWBA nexttbtt: %x hw_tu: %x "
-                               "TSF: %llx\n",
-                               sc->nexttbtt,
-                               TSF_TO_TU(tsf),
-                               (unsigned long long) tsf);
-       } else {
-               spin_lock(&sc->block);
-               ath5k_beacon_send(sc);
-               spin_unlock(&sc->block);
+       /* 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 contains 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 a 802.11 frame and returns the number of
+ * bytes added, or -1 if we don't have enought 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;
+}
+
+/*
+ * This function expects a 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_tasklet_rx(unsigned long data)
 {
-       struct ieee80211_rx_status rxs = {};
+       struct ieee80211_rx_status *rxs;
        struct ath5k_rx_status rs = {};
        struct sk_buff *skb, *next_skb;
        dma_addr_t next_skb_addr;
        struct ath5k_softc *sc = (void *)data;
+       struct ath5k_hw *ah = sc->ah;
+       struct ath_common *common = ath5k_hw_common(ah);
        struct ath5k_buf *bf;
        struct ath5k_desc *ds;
        int ret;
-       int hdrlen;
-       int padsize;
+       int rx_flag;
 
        spin_lock(&sc->rxbuflock);
        if (list_empty(&sc->rxbuf)) {
@@ -1787,7 +1907,7 @@ ath5k_tasklet_rx(unsigned long data)
                goto unlock;
        }
        do {
-               rxs.flag = 0;
+               rx_flag = 0;
 
                bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list);
                BUG_ON(bf->skb == NULL);
@@ -1803,18 +1923,30 @@ ath5k_tasklet_rx(unsigned long data)
                        break;
                else if (unlikely(ret)) {
                        ATH5K_ERR(sc, "error in processing rx descriptor\n");
+                       sc->stats.rxerr_proc++;
                        spin_unlock(&sc->rxbuflock);
                        return;
                }
 
+               sc->stats.rx_all_count++;
+
                if (unlikely(rs.rs_more)) {
                        ATH5K_WARN(sc, "unsupported jumbo\n");
+                       sc->stats.rxerr_jumbo++;
                        goto next;
                }
 
                if (unlikely(rs.rs_status)) {
-                       if (rs.rs_status & AR5K_RXERR_PHY)
+                       if (rs.rs_status & AR5K_RXERR_CRC)
+                               sc->stats.rxerr_crc++;
+                       if (rs.rs_status & AR5K_RXERR_FIFO)
+                               sc->stats.rxerr_fifo++;
+                       if (rs.rs_status & AR5K_RXERR_PHY) {
+                               sc->stats.rxerr_phy++;
+                               if (rs.rs_phyerr > 0 && rs.rs_phyerr < 32)
+                                       sc->stats.rxerr_phy_code[rs.rs_phyerr]++;
                                goto next;
+                       }
                        if (rs.rs_status & AR5K_RXERR_DECRYPT) {
                                /*
                                 * Decrypt error.  If the error occurred
@@ -1826,12 +1958,14 @@ ath5k_tasklet_rx(unsigned long data)
                                 *
                                 * XXX do key cache faulting
                                 */
+                               sc->stats.rxerr_decrypt++;
                                if (rs.rs_keyix == AR5K_RXKEYIX_INVALID &&
                                    !(rs.rs_status & AR5K_RXERR_CRC))
                                        goto accept;
                        }
                        if (rs.rs_status & AR5K_RXERR_MIC) {
-                               rxs.flag |= RX_FLAG_MMIC_ERROR;
+                               rx_flag |= RX_FLAG_MMIC_ERROR;
+                               sc->stats.rxerr_mic++;
                                goto accept;
                        }
 
@@ -1851,7 +1985,7 @@ accept:
                if (!next_skb)
                        goto next;
 
-               pci_unmap_single(sc->pdev, bf->skbaddr, sc->rxbufsize,
+               pci_unmap_single(sc->pdev, bf->skbaddr, common->rx_bufsize,
                                PCI_DMA_FROMDEVICE);
                skb_put(skb, rs.rs_datalen);
 
@@ -1863,12 +1997,9 @@ accept:
                 * bytes and we can optimize this a bit. In addition, we must
                 * not try to remove padding from short control frames that do
                 * not have payload. */
-               hdrlen = ieee80211_get_hdrlen_from_skb(skb);
-               padsize = ath5k_pad_size(hdrlen);
-               if (padsize) {
-                       memmove(skb->data + padsize, skb->data, hdrlen);
-                       skb_pull(skb, padsize);
-               }
+               ath5k_remove_padding(skb);
+
+               rxs = IEEE80211_SKB_RXCB(skb);
 
                /*
                 * always extend the mac timestamp, since this information is
@@ -1890,41 +2021,38 @@ accept:
                 * 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->mactime = ath5k_extend_tsf(sc->ah, rs.rs_tstamp);
+               rxs->flag = rx_flag | RX_FLAG_TSFT;
 
-               rxs.freq = sc->curchan->center_freq;
-               rxs.band = sc->curband->band;
+               rxs->freq = sc->curchan->center_freq;
+               rxs->band = sc->curband->band;
 
-               rxs.noise = sc->ah->ah_noise_floor;
-               rxs.signal = rxs.noise + rs.rs_rssi;
+               rxs->noise = sc->ah->ah_noise_floor;
+               rxs->signal = rxs->noise + rs.rs_rssi;
 
-               /* An rssi of 35 indicates you should be able use
-                * 54 Mbps reliably. A more elaborate scheme can be used
-                * here but it requires a map of SNR/throughput for each
-                * possible mode used */
-               rxs.qual = rs.rs_rssi * 100 / 35;
+               rxs->antenna = rs.rs_antenna;
 
-               /* rssi can be more than 35 though, anything above that
-                * should be considered at 100% */
-               if (rxs.qual > 100)
-                       rxs.qual = 100;
+               if (rs.rs_antenna > 0 && rs.rs_antenna < 5)
+                       sc->stats.antenna_rx[rs.rs_antenna]++;
+               else
+                       sc->stats.antenna_rx[0]++; /* invalid */
 
-               rxs.antenna = rs.rs_antenna;
-               rxs.rate_idx = ath5k_hw_to_driver_rix(sc, rs.rs_rate);
-               rxs.flag |= ath5k_rx_decrypted(sc, ds, skb, &rs);
+               rxs->rate_idx = ath5k_hw_to_driver_rix(sc, rs.rs_rate);
+               rxs->flag |= ath5k_rx_decrypted(sc, ds, skb, &rs);
 
-               if (rxs.rate_idx >= 0 && rs.rs_rate ==
-                   sc->curband->bitrates[rxs.rate_idx].hw_value_short)
-                       rxs.flag |= RX_FLAG_SHORTPRE;
+               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);
+                       ath5k_check_ibss_tsf(sc, skb, rxs);
 
-               __ieee80211_rx(sc->hw, skb, &rxs);
+               ieee80211_rx(sc->hw, skb);
 
                bf->skb = next_skb;
                bf->skbaddr = next_skb_addr;
@@ -1956,6 +2084,17 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
        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;
@@ -1965,6 +2104,7 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
                        break;
                }
 
+               sc->stats.tx_all_count++;
                skb = bf->skb;
                info = IEEE80211_SKB_CB(skb);
                bf->skb = NULL;
@@ -1990,19 +2130,34 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
                info->status.rates[ts.ts_final_idx].count++;
 
                if (unlikely(ts.ts_status)) {
-                       sc->ll_stats.dot11ACKFailureCount++;
-                       if (ts.ts_status & AR5K_TXERR_FILT)
+                       sc->stats.ack_fail++;
+                       if (ts.ts_status & AR5K_TXERR_FILT) {
                                info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
+                               sc->stats.txerr_filt++;
+                       }
+                       if (ts.ts_status & AR5K_TXERR_XRETRY)
+                               sc->stats.txerr_retry++;
+                       if (ts.ts_status & AR5K_TXERR_FIFO)
+                               sc->stats.txerr_fifo++;
                } else {
                        info->flags |= IEEE80211_TX_STAT_ACK;
                        info->status.ack_signal = ts.ts_rssi;
                }
 
+               /*
+                * Remove MAC header padding before giving the frame
+                * back to mac80211.
+                */
+               ath5k_remove_padding(skb);
+
+               if (ts.ts_antenna > 0 && ts.ts_antenna < 5)
+                       sc->stats.antenna_tx[ts.ts_antenna]++;
+               else
+                       sc->stats.antenna_tx[0]++; /* invalid */
+
                ieee80211_tx_status(sc->hw, skb);
-               sc->tx_stats[txq->qnum].count++;
 
                spin_lock(&sc->txbuflock);
-               sc->tx_stats[txq->qnum].len--;
                list_move_tail(&bf->list, &sc->txbuf);
                sc->txbuf_len++;
                spin_unlock(&sc->txbuflock);
@@ -2017,9 +2172,12 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
 static void
 ath5k_tasklet_tx(unsigned long data)
 {
+       int i;
        struct ath5k_softc *sc = (void *)data;
 
-       ath5k_tx_processq(sc, sc->txq);
+       for (i=0; i < AR5K_NUM_TX_QUEUES; i++)
+               if (sc->txqs[i].setup && (sc->ah->ah_txq_isr & BIT(i)))
+                       ath5k_tx_processq(sc, &sc->txqs[i]);
 }
 
 
@@ -2037,8 +2195,10 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
        struct  ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ath5k_hw *ah = sc->ah;
        struct ath5k_desc *ds;
-       int ret, antenna = 0;
+       int ret = 0;
+       u8 antenna;
        u32 flags;
+       const int padsize = 0;
 
        bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len,
                        PCI_DMA_TODEVICE);
@@ -2051,30 +2211,42 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
        }
 
        ds = bf->desc;
+       antenna = ah->ah_tx_ant;
 
        flags = AR5K_TXDESC_NOACK;
        if (sc->opmode == NL80211_IFTYPE_ADHOC && ath5k_hw_hasveol(ah)) {
                ds->ds_link = bf->daddr;        /* self-linked */
                flags |= AR5K_TXDESC_VEOL;
-               /*
-                * Let hardware handle antenna switching if txantenna is not set
-                */
-       } else {
+       } else
                ds->ds_link = 0;
-               /*
-                * Switch antenna every 4 beacons if txantenna is not set
-                * XXX assumes two antennas
-                */
-               if (antenna == 0)
-                       antenna = sc->bsent & 4 ? 2 : 1;
-       }
+
+       /*
+        * If we use multiple antennas on AP and use
+        * the Sectored AP scenario, switch antenna every
+        * 4 beacons to make sure everybody hears our AP.
+        * When a client tries to associate, hw will keep
+        * track of the tx antenna to be used for this client
+        * automaticaly, based on ACKed packets.
+        *
+        * Note: AP still listens and transmits RTS on the
+        * default antenna which is supposed to be an omni.
+        *
+        * Note2: On sectored scenarios it's possible to have
+        * multiple antennas (1omni -the default- and 14 sectors)
+        * so if we choose to actually support this mode we need
+        * to allow user to set how many antennas we have and tweak
+        * the code below to send beacons on all of them.
+        */
+       if (ah->ah_ant_mode == AR5K_ANTMODE_SECTOR_AP)
+               antenna = sc->bsent & 4 ? 2 : 1;
+
 
        /* 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) */
        ds->ds_data = bf->skbaddr;
        ret = ah->ah_setup_tx_desc(ah, ds, skb->len,
-                       ieee80211_get_hdrlen_from_skb(skb),
+                       ieee80211_get_hdrlen_from_skb(skb), padsize,
                        AR5K_PKT_TYPE_BEACON, (sc->power_level * 2),
                        ieee80211_get_tx_rate(sc->hw, info)->hw_value,
                        1, AR5K_TXKEYIX_INVALID,
@@ -2101,6 +2273,7 @@ ath5k_beacon_send(struct ath5k_softc *sc)
 {
        struct ath5k_buf *bf = sc->bbuf;
        struct ath5k_hw *ah = sc->ah;
+       struct sk_buff *skb;
 
        ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "in beacon_send\n");
 
@@ -2120,7 +2293,7 @@ ath5k_beacon_send(struct ath5k_softc *sc)
                sc->bmisscount++;
                ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
                        "missed %u consecutive beacons\n", sc->bmisscount);
-               if (sc->bmisscount > 3) {               /* NB: 3 is a guess */
+               if (sc->bmisscount > 10) {      /* NB: 10 is a guess */
                        ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
                                "stuck beacon time (%u missed)\n",
                                sc->bmisscount);
@@ -2141,15 +2314,25 @@ ath5k_beacon_send(struct ath5k_softc *sc)
         * are still pending on the queue.
         */
        if (unlikely(ath5k_hw_stop_tx_dma(ah, sc->bhalq))) {
-               ATH5K_WARN(sc, "beacon queue %u didn't stop?\n", sc->bhalq);
+               ATH5K_WARN(sc, "beacon queue %u didn't start/stop ?\n", sc->bhalq);
                /* NB: hw still stops DMA, so proceed */
        }
 
+       /* refresh the beacon for AP mode */
+       if (sc->opmode == NL80211_IFTYPE_AP)
+               ath5k_beacon_update(sc->hw, sc->vif);
+
        ath5k_hw_set_txdp(ah, sc->bhalq, bf->daddr);
        ath5k_hw_start_tx_dma(ah, sc->bhalq);
        ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n",
                sc->bhalq, (unsigned long long)bf->daddr, bf->desc);
 
+       skb = ieee80211_get_buffered_bc(sc->hw, sc->vif);
+       while (skb) {
+               ath5k_tx_queue(sc->hw, skb, sc->cabq);
+               skb = ieee80211_get_buffered_bc(sc->hw, sc->vif);
+       }
+
        sc->bsent++;
 }
 
@@ -2270,13 +2453,11 @@ ath5k_beacon_config(struct ath5k_softc *sc)
        struct ath5k_hw *ah = sc->ah;
        unsigned long flags;
 
-       ath5k_hw_set_imr(ah, 0);
+       spin_lock_irqsave(&sc->block, flags);
        sc->bmisscount = 0;
        sc->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA);
 
-       if (sc->opmode == NL80211_IFTYPE_ADHOC ||
-                       sc->opmode == NL80211_IFTYPE_MESH_POINT ||
-                       sc->opmode == NL80211_IFTYPE_AP) {
+       if (sc->enable_beacon) {
                /*
                 * In IBSS mode we use a self-linked tx descriptor and let the
                 * hardware send the beacons automatically. We have to load it
@@ -2289,16 +2470,46 @@ ath5k_beacon_config(struct ath5k_softc *sc)
                sc->imask |= AR5K_INT_SWBA;
 
                if (sc->opmode == NL80211_IFTYPE_ADHOC) {
-                       if (ath5k_hw_hasveol(ah)) {
-                               spin_lock_irqsave(&sc->block, flags);
+                       if (ath5k_hw_hasveol(ah))
                                ath5k_beacon_send(sc);
-                               spin_unlock_irqrestore(&sc->block, flags);
-                       }
                } else
                        ath5k_beacon_update_timers(sc, -1);
+       } else {
+               ath5k_hw_stop_tx_dma(sc->ah, sc->bhalq);
        }
 
        ath5k_hw_set_imr(ah, sc->imask);
+       mmiowb();
+       spin_unlock_irqrestore(&sc->block, flags);
+}
+
+static void ath5k_tasklet_beacon(unsigned long data)
+{
+       struct ath5k_softc *sc = (struct ath5k_softc *) data;
+
+       /*
+        * Software beacon alert--time to send a beacon.
+        *
+        * In IBSS mode we use this interrupt just to
+        * keep track of the next TBTT (target beacon
+        * transmission time) in order to detect wether
+        * automatic TSF updates happened.
+        */
+       if (sc->opmode == NL80211_IFTYPE_ADHOC) {
+               /* XXX: only if VEOL suppported */
+               u64 tsf = ath5k_hw_get_tsf64(sc->ah);
+               sc->nexttbtt += sc->bintval;
+               ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
+                               "SWBA nexttbtt: %x hw_tu: %x "
+                               "TSF: %llx\n",
+                               sc->nexttbtt,
+                               TSF_TO_TU(tsf),
+                               (unsigned long long) tsf);
+       } else {
+               spin_lock(&sc->block);
+               ath5k_beacon_send(sc);
+               spin_unlock(&sc->block);
+       }
 }
 
 
@@ -2333,11 +2544,14 @@ ath5k_init(struct ath5k_softc *sc)
        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;
-       ret = ath5k_reset(sc, false, false);
+               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.
@@ -2345,12 +2559,7 @@ ath5k_init(struct ath5k_softc *sc)
        for (i = 0; i < AR5K_KEYTABLE_SIZE; i++)
                ath5k_hw_reset_key(ah, i);
 
-       /* Set ack to be sent at low bit-rates */
-       ath5k_hw_set_ack_bitrate_high(ah, false);
-
-       mod_timer(&sc->calib_tim, round_jiffies(jiffies +
-                       msecs_to_jiffies(ath5k_calinterval * 1000)));
-
+       ath5k_hw_set_ack_bitrate_high(ah, true);
        ret = 0;
 done:
        mmiowb();
@@ -2413,42 +2622,67 @@ ath5k_stop_hw(struct ath5k_softc *sc)
        ret = ath5k_stop_locked(sc);
        if (ret == 0 && !test_bit(ATH_STAT_INVALID, sc->status)) {
                /*
-                * Set the chip in full sleep mode.  Note that we are
-                * careful to do this only when bringing the interface
-                * completely to a stop.  When the chip is in this state
-                * it must be carefully woken up or references to
-                * registers in the PCI clock domain may freeze the bus
-                * (and system).  This varies by chip and is mostly an
-                * issue with newer parts that go to sleep more quickly.
-                */
-               if (sc->ah->ah_mac_srev >= 0x78) {
-                       /*
-                        * XXX
-                        * don't put newer MAC revisions > 7.8 to sleep because
-                        * of the above mentioned problems
-                        */
-                       ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mac version > 7.8, "
-                               "not putting device to sleep\n");
-               } else {
-                       ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
-                               "putting device to full sleep\n");
-                       ath5k_hw_set_power(sc->ah, AR5K_PM_FULL_SLEEP, true, 0);
-               }
+                * Don't set the card in full sleep mode!
+                *
+                * a) When the device is in this state it must be carefully
+                * woken up or references to registers in the PCI clock
+                * domain may freeze the bus (and system).  This varies
+                * by chip and is mostly an issue with newer parts
+                * (madwifi sources mentioned srev >= 0x78) that go to
+                * sleep more quickly.
+                *
+                * b) On older chips full sleep results a weird behaviour
+                * during wakeup. I tested various cards with srev < 0x78
+                * and they don't wake up after module reload, a second
+                * module reload is needed to bring the card up again.
+                *
+                * Until we figure out what's going on don't enable
+                * full chip reset on any chip (this is what Legacy HAL
+                * and Sam's HAL do anyway). Instead Perform a full reset
+                * on the device (same as initial state after attach) and
+                * leave it idle (keep MAC/BB on warm reset) */
+               ret = ath5k_hw_on_hold(sc->ah);
+
+               ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
+                               "putting device to sleep\n");
        }
        ath5k_txbuf_free(sc, sc->bbuf);
 
        mmiowb();
        mutex_unlock(&sc->lock);
 
-       del_timer_sync(&sc->calib_tim);
        tasklet_kill(&sc->rxtq);
        tasklet_kill(&sc->txtq);
        tasklet_kill(&sc->restq);
+       tasklet_kill(&sc->calib);
        tasklet_kill(&sc->beacontq);
+       tasklet_kill(&sc->ani_tasklet);
+
+       ath5k_rfkill_hw_stop(sc->ah);
 
        return ret;
 }
 
+static void
+ath5k_intr_calibration_poll(struct ath5k_hw *ah)
+{
+       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);
+
+       } 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); */
+}
+
 static irqreturn_t
 ath5k_intr(int irq, void *dev_id)
 {
@@ -2472,7 +2706,20 @@ ath5k_intr(int irq, void *dev_id)
                         */
                        tasklet_schedule(&sc->restq);
                } else if (unlikely(status & AR5K_INT_RXORN)) {
-                       tasklet_schedule(&sc->restq);
+                       /*
+                        * 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)
+                               tasklet_schedule(&sc->restq);
+                       else
+                               tasklet_schedule(&sc->rxtq);
                } else {
                        if (status & AR5K_INT_SWBA) {
                                tasklet_hi_schedule(&sc->beacontq);
@@ -2498,18 +2745,21 @@ ath5k_intr(int irq, void *dev_id)
                                /* TODO */
                        }
                        if (status & AR5K_INT_MIB) {
-                               /*
-                                * These stats are also used for ANI i think
-                                * so how about updating them more often ?
-                                */
-                               ath5k_hw_update_mib_counters(ah, &sc->ll_stats);
+                               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);
+
                }
        } while (ath5k_hw_is_intr_pending(ah) && --counter > 0);
 
        if (unlikely(!counter))
                ATH5K_WARN(sc, "too many interrupts, giving up for now\n");
 
+       ath5k_intr_calibration_poll(ah);
+
        return IRQ_HANDLED;
 }
 
@@ -2526,11 +2776,18 @@ ath5k_tasklet_reset(unsigned long data)
  * for temperature/environment changes.
  */
 static void
-ath5k_calibrate(unsigned long data)
+ath5k_tasklet_calibrate(unsigned long data)
 {
        struct ath5k_softc *sc = (void *)data;
        struct ath5k_hw *ah = sc->ah;
 
+       /* Only full calibration for now */
+       ah->ah_cal_mask |= AR5K_CALIBRATION_FULL;
+
+       /* Stop queues so that calibration
+        * doesn't interfere with tx */
+       ieee80211_stop_queues(sc->hw);
+
        ATH5K_DBG(sc, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n",
                ieee80211_frequency_to_channel(sc->curchan->center_freq),
                sc->curchan->hw_value);
@@ -2541,15 +2798,29 @@ ath5k_calibrate(unsigned long data)
                 * to load new gain values.
                 */
                ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "calibration, resetting\n");
-               ath5k_reset_wake(sc);
+               ath5k_reset(sc, sc->curchan);
        }
        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));
 
-       mod_timer(&sc->calib_tim, round_jiffies(jiffies +
-                       msecs_to_jiffies(ath5k_calinterval * 1000)));
+       /* Wake queues */
+       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;
 }
 
 
@@ -2561,9 +2832,16 @@ 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_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 hdrlen;
        int padsize;
 
        ath5k_debug_dump_skb(sc, skb, "TX  ", 1);
@@ -2575,17 +2853,11 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
         * the hardware expects the header padded to 4 byte boundaries
         * if this is not the case we add the padding after the header
         */
-       hdrlen = ieee80211_get_hdrlen_from_skb(skb);
-       padsize = ath5k_pad_size(hdrlen);
-       if (padsize) {
-
-               if (skb_headroom(skb) < padsize) {
-                       ATH5K_ERR(sc, "tx hdrlen not %%4: %d not enough"
-                                 " headroom to pad %d\n", hdrlen, padsize);
-                       goto drop_packet;
-               }
-               skb_push(skb, padsize);
-               memmove(skb->data, skb->data+padsize, hdrlen);
+       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);
@@ -2604,7 +2876,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 
        bf->skb = skb;
 
-       if (ath5k_txbuf_setup(sc, bf)) {
+       if (ath5k_txbuf_setup(sc, bf, txq, padsize)) {
                bf->skb = NULL;
                spin_lock_irqsave(&sc->txbuflock, flags);
                list_add_tail(&bf->list, &sc->txbuf);
@@ -2619,20 +2891,27 @@ drop_packet:
        return NETDEV_TX_OK;
 }
 
+/*
+ * Reset the hardware.  If chan is not NULL, then also pause rx/tx
+ * and change to the given channel.
+ */
 static int
-ath5k_reset(struct ath5k_softc *sc, bool stop, bool change_channel)
+ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan)
 {
        struct ath5k_hw *ah = sc->ah;
        int ret;
 
        ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "resetting\n");
 
-       if (stop) {
+       if (chan) {
                ath5k_hw_set_imr(ah, 0);
                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, true);
+       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;
@@ -2644,6 +2923,8 @@ ath5k_reset(struct ath5k_softc *sc, bool stop, bool change_channel)
                goto err;
        }
 
+       ath5k_ani_init(ah, ah->ah_sc->ani_state.ani_mode);
+
        /*
         * Change channels and update the h/w rate map if we're switching;
         * e.g. 11a to 11b/g.
@@ -2668,7 +2949,7 @@ ath5k_reset_wake(struct ath5k_softc *sc)
 {
        int ret;
 
-       ret = ath5k_reset(sc, true, true);
+       ret = ath5k_reset(sc, sc->curchan);
        if (!ret)
                ieee80211_wake_queues(sc->hw);
 
@@ -2686,7 +2967,7 @@ static void ath5k_stop(struct ieee80211_hw *hw)
 }
 
 static int ath5k_add_interface(struct ieee80211_hw *hw,
-               struct ieee80211_if_init_conf *conf)
+               struct ieee80211_vif *vif)
 {
        struct ath5k_softc *sc = hw->priv;
        int ret;
@@ -2697,25 +2978,25 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
                goto end;
        }
 
-       sc->vif = conf->vif;
+       sc->vif = vif;
 
-       switch (conf->type) {
+       switch (vif->type) {
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_ADHOC:
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_MONITOR:
-               sc->opmode = conf->type;
+               sc->opmode = vif->type;
                break;
        default:
                ret = -EOPNOTSUPP;
                goto end;
        }
 
-       /* Set to a reasonable value. Note that this will
-        * be set to mac80211's value at ath5k_config(). */
-       sc->bintval = 1000;
-       ath5k_hw_set_lladdr(sc->ah, conf->mac_addr);
+       ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "add interface mode %d\n", sc->opmode);
+
+       ath5k_hw_set_lladdr(sc->ah, vif->addr);
+       ath5k_mode_setup(sc);
 
        ret = 0;
 end:
@@ -2725,13 +3006,13 @@ end:
 
 static void
 ath5k_remove_interface(struct ieee80211_hw *hw,
-                       struct ieee80211_if_init_conf *conf)
+                       struct ieee80211_vif *vif)
 {
        struct ath5k_softc *sc = hw->priv;
        u8 mac[ETH_ALEN] = {};
 
        mutex_lock(&sc->lock);
-       if (sc->vif != conf->vif)
+       if (sc->vif != vif)
                goto end;
 
        ath5k_hw_set_lladdr(sc->ah, mac);
@@ -2749,11 +3030,15 @@ ath5k_config(struct ieee80211_hw *hw, u32 changed)
        struct ath5k_softc *sc = hw->priv;
        struct ath5k_hw *ah = sc->ah;
        struct ieee80211_conf *conf = &hw->conf;
-       int ret;
+       int ret = 0;
 
        mutex_lock(&sc->lock);
 
-       sc->bintval = conf->beacon_int;
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+               ret = ath5k_chan_set(sc, conf->channel);
+               if (ret < 0)
+                       goto unlock;
+       }
 
        if ((changed & IEEE80211_CONF_CHANGE_POWER) &&
        (sc->power_level != conf->power_level)) {
@@ -2763,12 +3048,58 @@ ath5k_config(struct ieee80211_hw *hw, u32 changed)
                ath5k_hw_set_txpower_limit(ah, (conf->power_level * 2));
        }
 
-       ret = ath5k_chan_set(sc, conf->channel);
+       /* TODO:
+        * 1) Move this on config_interface and handle each case
+        * separately eg. when we have only one STA vif, use
+        * AR5K_ANTMODE_SINGLE_AP
+        *
+        * 2) Allow the user to change antenna mode eg. when only
+        * one antenna is present
+        *
+        * 3) Allow the user to set default/tx antenna when possible
+        *
+        * 4) Default mode should handle 90% of the cases, together
+        * with fixed a/b and single AP modes we should be able to
+        * handle 99%. Sectored modes are extreme cases and i still
+        * haven't found a usage for them. If we decide to support them,
+        * then we must allow the user to set how many tx antennas we
+        * have available
+        */
+       ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode);
 
+unlock:
        mutex_unlock(&sc->lock);
        return ret;
 }
 
+static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw,
+                                  struct netdev_hw_addr_list *mc_list)
+{
+       u32 mfilt[2], val;
+       u8 pos;
+       struct netdev_hw_addr *ha;
+
+       mfilt[0] = 0;
+       mfilt[1] = 1;
+
+       netdev_hw_addr_list_for_each(ha, mc_list) {
+               /* calculate XOR of eight 6-bit values */
+               val = get_unaligned_le32(ha->addr + 0);
+               pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
+               val = get_unaligned_le32(ha->addr + 3);
+               pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
+               pos &= 0x3f;
+               mfilt[pos / 32] |= (1 << (pos % 32));
+               /* XXX: we might be able to just do this instead,
+               * but not sure, needs testing, if we do use this we'd
+               * neet to inform below to not reset the mcast */
+               /* ath5k_hw_set_mcast_filterindex(ah,
+                *      ha->addr[5]); */
+       }
+
+       return ((u64)(mfilt[1]) << 32) | mfilt[0];
+}
+
 #define SUPPORTED_FIF_FLAGS \
        FIF_PROMISC_IN_BSS |  FIF_ALLMULTI | FIF_FCSFAIL | \
        FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \
@@ -2794,16 +3125,16 @@ ath5k_config(struct ieee80211_hw *hw, u32 changed)
 static void ath5k_configure_filter(struct ieee80211_hw *hw,
                unsigned int changed_flags,
                unsigned int *new_flags,
-               int mc_count, struct dev_mc_list *mclist)
+               u64 multicast)
 {
        struct ath5k_softc *sc = hw->priv;
        struct ath5k_hw *ah = sc->ah;
-       u32 mfilt[2], val, rfilt;
-       u8 pos;
-       int i;
+       u32 mfilt[2], rfilt;
 
-       mfilt[0] = 0;
-       mfilt[1] = 0;
+       mutex_lock(&sc->lock);
+
+       mfilt[0] = multicast;
+       mfilt[1] = multicast >> 32;
 
        /* Only deal with supported flags */
        changed_flags &= SUPPORTED_FIF_FLAGS;
@@ -2829,24 +3160,6 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
        if (*new_flags & FIF_ALLMULTI) {
                mfilt[0] =  ~0;
                mfilt[1] =  ~0;
-       } else {
-               for (i = 0; i < mc_count; i++) {
-                       if (!mclist)
-                               break;
-                       /* calculate XOR of eight 6-bit values */
-                       val = get_unaligned_le32(mclist->dmi_addr + 0);
-                       pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
-                       val = get_unaligned_le32(mclist->dmi_addr + 3);
-                       pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
-                       pos &= 0x3f;
-                       mfilt[pos / 32] |= (1 << (pos % 32));
-                       /* XXX: we might be able to just do this instead,
-                       * but not sure, needs testing, if we do use this we'd
-                       * neet to inform below to not reset the mcast */
-                       /* ath5k_hw_set_mcast_filterindex(ah,
-                        *      mclist->dmi_addr[5]); */
-                       mclist = mclist->next;
-               }
        }
 
        /* This is the best we can do */
@@ -2870,22 +3183,25 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
 
        /* XXX move these to mac80211, and add a beacon IFF flag to mac80211 */
 
-       if (sc->opmode == NL80211_IFTYPE_MONITOR)
-               rfilt |= AR5K_RX_FILTER_CONTROL | AR5K_RX_FILTER_BEACON |
-                       AR5K_RX_FILTER_PROBEREQ | AR5K_RX_FILTER_PROM;
-       if (sc->opmode != NL80211_IFTYPE_STATION)
-               rfilt |= AR5K_RX_FILTER_PROBEREQ;
-       if (sc->opmode != NL80211_IFTYPE_AP &&
-               sc->opmode != NL80211_IFTYPE_MESH_POINT &&
-               test_bit(ATH_STAT_PROMISC, sc->status))
-               rfilt |= AR5K_RX_FILTER_PROM;
-       if ((sc->opmode == NL80211_IFTYPE_STATION && sc->assoc) ||
-               sc->opmode == NL80211_IFTYPE_ADHOC ||
-               sc->opmode == NL80211_IFTYPE_AP)
-               rfilt |= AR5K_RX_FILTER_BEACON;
-       if (sc->opmode == NL80211_IFTYPE_MESH_POINT)
-               rfilt |= AR5K_RX_FILTER_CONTROL | AR5K_RX_FILTER_BEACON |
-                       AR5K_RX_FILTER_PROBEREQ | AR5K_RX_FILTER_PROM;
+       switch (sc->opmode) {
+       case NL80211_IFTYPE_MESH_POINT:
+       case NL80211_IFTYPE_MONITOR:
+               rfilt |= AR5K_RX_FILTER_CONTROL |
+                        AR5K_RX_FILTER_BEACON |
+                        AR5K_RX_FILTER_PROBEREQ |
+                        AR5K_RX_FILTER_PROM;
+               break;
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_ADHOC:
+               rfilt |= AR5K_RX_FILTER_PROBEREQ |
+                        AR5K_RX_FILTER_BEACON;
+               break;
+       case NL80211_IFTYPE_STATION:
+               if (sc->assoc)
+                       rfilt |= AR5K_RX_FILTER_BEACON;
+       default:
+               break;
+       }
 
        /* Set filters */
        ath5k_hw_set_rx_filter(ah, rfilt);
@@ -2895,6 +3211,8 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
        /* Set the cached hw filter flags, this will alter actually
         * be set in HW */
        sc->filter_flags = rfilt;
+
+       mutex_unlock(&sc->lock);
 }
 
 static int
@@ -2903,16 +3221,24 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
              struct ieee80211_key_conf *key)
 {
        struct ath5k_softc *sc = hw->priv;
+       struct ath5k_hw *ah = sc->ah;
+       struct ath_common *common = ath5k_hw_common(ah);
        int ret = 0;
 
        if (modparam_nohwcrypt)
                return -EOPNOTSUPP;
 
+       if (sc->opmode == NL80211_IFTYPE_AP)
+               return -EOPNOTSUPP;
+
        switch (key->alg) {
        case ALG_WEP:
        case ALG_TKIP:
                break;
        case ALG_CCMP:
+               if (sc->ah->ah_aes_support)
+                       break;
+
                return -EOPNOTSUPP;
        default:
                WARN_ON(1);
@@ -2929,14 +3255,14 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                        ATH5K_ERR(sc, "can't set the key\n");
                        goto unlock;
                }
-               __set_bit(key->keyidx, sc->keymap);
+               __set_bit(key->keyidx, common->keymap);
                key->hw_key_idx = key->keyidx;
                key->flags |= (IEEE80211_KEY_FLAG_GENERATE_IV |
                               IEEE80211_KEY_FLAG_GENERATE_MMIC);
                break;
        case DISABLE_KEY:
                ath5k_hw_reset_key(sc->ah, key->keyidx);
-               __clear_bit(key->keyidx, sc->keymap);
+               __clear_bit(key->keyidx, common->keymap);
                break;
        default:
                ret = -EINVAL;
@@ -2954,23 +3280,14 @@ ath5k_get_stats(struct ieee80211_hw *hw,
                struct ieee80211_low_level_stats *stats)
 {
        struct ath5k_softc *sc = hw->priv;
-       struct ath5k_hw *ah = sc->ah;
 
        /* Force update */
-       ath5k_hw_update_mib_counters(ah, &sc->ll_stats);
+       ath5k_hw_update_mib_counters(sc->ah);
 
-       memcpy(stats, &sc->ll_stats, sizeof(sc->ll_stats));
-
-       return 0;
-}
-
-static int
-ath5k_get_tx_stats(struct ieee80211_hw *hw,
-               struct ieee80211_tx_queue_stats *stats)
-{
-       struct ath5k_softc *sc = hw->priv;
-
-       memcpy(stats, &sc->tx_stats, sizeof(sc->tx_stats));
+       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;
 }
@@ -3006,28 +3323,43 @@ ath5k_reset_tsf(struct ieee80211_hw *hw)
                ath5k_hw_reset_tsf(sc->ah);
 }
 
+/*
+ * 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 ath5k_softc *sc, struct sk_buff *skb)
+ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
-       unsigned long flags;
        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);
 
-       spin_lock_irqsave(&sc->block, flags);
        ath5k_txbuf_free(sc, sc->bbuf);
        sc->bbuf->skb = skb;
        ret = ath5k_beacon_setup(sc, sc->bbuf);
        if (ret)
                sc->bbuf->skb = NULL;
-       spin_unlock_irqrestore(&sc->block, flags);
-       if (!ret) {
-               ath5k_beacon_config(sc);
-               mmiowb();
-       }
-
+out:
        return ret;
 }
+
 static void
 set_beacon_filter(struct ieee80211_hw *hw, bool enable)
 {
@@ -3050,6 +3382,8 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
 {
        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))
@@ -3057,10 +3391,9 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
 
        if (changes & BSS_CHANGED_BSSID) {
                /* Cache for later use during resets */
-               memcpy(ah->ah_bssid, bss_conf->bssid, ETH_ALEN);
-               /* XXX: assoc id is set to 0 for now, mac80211 doesn't have
-                * a clean way of letting us retrieve this yet. */
-               ath5k_hw_set_associd(ah, ah->ah_bssid, 0);
+               memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
+               common->curaid = 0;
+               ath5k_hw_set_associd(ah);
                mmiowb();
        }
 
@@ -3071,18 +3404,64 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
                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_associd(ah);
+                       /* Once ANI is available you would start it here */
+               }
        }
 
-       if (changes & BSS_CHANGED_BEACON &&
-           (vif->type == NL80211_IFTYPE_ADHOC ||
-            vif->type == NL80211_IFTYPE_MESH_POINT ||
-            vif->type == NL80211_IFTYPE_AP)) {
-               struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
-
-               if (beacon)
-                       ath5k_beacon_update(sc, beacon);
+       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);
+}