Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[safe/jmp/linux-2.6] / drivers / net / wireless / ath5k / base.c
index b1e9a47..bd2c580 100644 (file)
@@ -60,6 +60,9 @@
 #include "debug.h"
 
 static int ath5k_calinterval = 10; /* Calibrate PHY every 10 secs (TODO: Fixme) */
+static int modparam_nohwcrypt;
+module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
 
 
 /******************\
@@ -197,7 +200,7 @@ static int          ath5k_pci_resume(struct pci_dev *pdev);
 #endif /* CONFIG_PM */
 
 static struct pci_driver ath5k_pci_driver = {
-       .name           = "ath5k_pci",
+       .name           = KBUILD_MODNAME,
        .id_table       = ath5k_pci_id_table,
        .probe          = ath5k_pci_probe,
        .remove         = __devexit_p(ath5k_pci_remove),
@@ -229,15 +232,21 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
                int mc_count, struct dev_mc_list *mclist);
 static int ath5k_set_key(struct ieee80211_hw *hw,
                enum set_key_cmd cmd,
-               const u8 *local_addr, const u8 *addr,
+               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 ath5k_softc *sc,
+               struct sk_buff *skb);
+static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
+               struct ieee80211_vif *vif,
+               struct ieee80211_bss_conf *bss_conf,
+               u32 changes);
 
 static struct ieee80211_ops ath5k_hw_ops = {
        .tx             = ath5k_tx,
@@ -253,7 +262,9 @@ static struct ieee80211_ops ath5k_hw_ops = {
        .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,
 };
 
 /*
@@ -338,9 +349,9 @@ static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
 }
 
 /* Interrupt handling */
-static int     ath5k_init(struct ath5k_softc *sc, bool is_resume);
+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, bool is_suspend);
+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);
 
@@ -541,8 +552,8 @@ ath5k_pci_probe(struct pci_dev *pdev,
 
        /* set up multi-rate retry capabilities */
        if (sc->ah->ah_version == AR5K_AR5212) {
-               hw->max_altrates = 3;
-               hw->max_altrate_tries = 11;
+               hw->max_rates = 4;
+               hw->max_rate_tries = 11;
        }
 
        /* Finish private driver data initialization */
@@ -644,8 +655,6 @@ ath5k_pci_suspend(struct pci_dev *pdev, pm_message_t state)
 
        ath5k_led_off(sc);
 
-       ath5k_stop_hw(sc, true);
-
        free_irq(pdev->irq, sc);
        pci_save_state(pdev);
        pci_disable_device(pdev);
@@ -680,14 +689,9 @@ ath5k_pci_resume(struct pci_dev *pdev)
                goto err_no_irq;
        }
 
-       err = ath5k_init(sc, true);
-       if (err)
-               goto err_irq;
        ath5k_led_enable(sc);
-
        return 0;
-err_irq:
-       free_irq(pdev->irq, sc);
+
 err_no_irq:
        pci_disable_device(pdev);
        return err;
@@ -704,7 +708,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
 {
        struct ath5k_softc *sc = hw->priv;
        struct ath5k_hw *ah = sc->ah;
-       u8 mac[ETH_ALEN];
+       u8 mac[ETH_ALEN] = {};
        int ret;
 
        ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "devid 0x%x\n", pdev->device);
@@ -774,7 +778,13 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
        tasklet_init(&sc->restq, ath5k_tasklet_reset, (unsigned long)sc);
        setup_timer(&sc->calib_tim, ath5k_calibrate, (unsigned long)sc);
 
-       ath5k_hw_get_lladdr(ah, mac);
+       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;
+       }
+
        SET_IEEE80211_PERM_ADDR(hw, mac);
        /* All MAC address bits matter for ACKs */
        memset(sc->bssidmask, 0xff, ETH_ALEN);
@@ -1013,6 +1023,8 @@ ath5k_setup_bands(struct ieee80211_hw *hw)
  * 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.
+ *
+ * Called with sc->lock.
  */
 static int
 ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan)
@@ -1081,6 +1093,42 @@ ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix)
 * Buffers setup *
 \***************/
 
+static
+struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_softc *sc, dma_addr_t *skb_addr)
+{
+       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);
+
+       if (!skb) {
+               ATH5K_ERR(sc, "can't alloc skbuff of size %u\n",
+                               sc->rxbufsize + sc->cachelsz - 1);
+               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);
+       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;
+}
+
 static int
 ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
 {
@@ -1088,37 +1136,11 @@ ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
        struct sk_buff *skb = bf->skb;
        struct ath5k_desc *ds;
 
-       if (likely(skb == NULL)) {
-               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);
-               if (unlikely(skb == NULL)) {
-                       ATH5K_ERR(sc, "can't alloc skbuff of size %u\n",
-                                       sc->rxbufsize + sc->cachelsz - 1);
+       if (!skb) {
+               skb = ath5k_rx_skb_alloc(sc, &bf->skbaddr);
+               if (!skb)
                        return -ENOMEM;
-               }
-               /*
-                * 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);
-
                bf->skb = skb;
-               bf->skbaddr = pci_map_single(sc->pdev,
-                       skb->data, sc->rxbufsize, PCI_DMA_FROMDEVICE);
-               if (unlikely(pci_dma_mapping_error(sc->pdev, bf->skbaddr))) {
-                       ATH5K_ERR(sc, "%s: DMA mapping failed\n", __func__);
-                       dev_kfree_skb(skb);
-                       bf->skb = NULL;
-                       return -ENOMEM;
-               }
        }
 
        /*
@@ -1161,6 +1183,10 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
        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;
 
@@ -1168,11 +1194,30 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
        bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len,
                        PCI_DMA_TODEVICE);
 
+       rate = ieee80211_get_tx_rate(sc->hw, info);
+
        if (info->flags & IEEE80211_TX_CTL_NO_ACK)
                flags |= AR5K_TXDESC_NOACK;
 
+       rc_flags = info->control.rates[0].flags;
+       hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ?
+               rate->hw_value_short : rate->hw_value;
+
        pktlen = skb->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));
+       }
+
        if (info->control.hw_key) {
                keyidx = info->control.hw_key->hw_key_idx;
                pktlen += info->control.hw_key->icv_len;
@@ -1180,8 +1225,9 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
        ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
                ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL,
                (sc->power_level * 2),
-               ieee80211_get_tx_rate(sc->hw, info)->hw_value,
-               info->control.retry_limit, keyidx, 0, flags, 0, 0);
+               hw_rate,
+               info->control.rates[0].count, keyidx, 0, flags,
+               cts_rate, duration);
        if (ret)
                goto err_unmap;
 
@@ -1193,7 +1239,7 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
                        break;
 
                mrr_rate[i] = rate->hw_value;
-               mrr_tries[i] = info->control.retries[i].limit;
+               mrr_tries[i] = info->control.rates[i + 1].count;
        }
 
        ah->ah_setup_mrr_tx_desc(ah, ds,
@@ -1647,13 +1693,14 @@ ath5k_tasklet_rx(unsigned long data)
 {
        struct ieee80211_rx_status rxs = {};
        struct ath5k_rx_status rs = {};
-       struct sk_buff *skb;
+       struct sk_buff *skb, *next_skb;
+       dma_addr_t next_skb_addr;
        struct ath5k_softc *sc = (void *)data;
        struct ath5k_buf *bf, *bf_last;
        struct ath5k_desc *ds;
        int ret;
        int hdrlen;
-       int pad;
+       int padsize;
 
        spin_lock(&sc->rxbuflock);
        if (list_empty(&sc->rxbuf)) {
@@ -1732,22 +1779,32 @@ ath5k_tasklet_rx(unsigned long data)
                                goto next;
                }
 accept:
+               next_skb = ath5k_rx_skb_alloc(sc, &next_skb_addr);
+
+               /*
+                * If we can't replace bf->skb with a new skb under memory
+                * pressure, just skip this packet
+                */
+               if (!next_skb)
+                       goto next;
+
                pci_unmap_single(sc->pdev, bf->skbaddr, sc->rxbufsize,
                                PCI_DMA_FROMDEVICE);
-               bf->skb = NULL;
-
                skb_put(skb, rs.rs_datalen);
 
-               /*
-                * the hardware adds a padding to 4 byte boundaries between
-                * the header and the payload data if the header length is
-                * not multiples of 4 - remove it
-                */
+               /* 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 % 4) % 4; However, since only
+                * even-length headers are used, padding can only be 0 or 2
+                * 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);
-               if (hdrlen & 3) {
-                       pad = hdrlen % 4;
-                       memmove(skb->data + pad, skb->data, hdrlen);
-                       skb_pull(skb, pad);
+               padsize = ath5k_pad_size(hdrlen);
+               if (padsize) {
+                       memmove(skb->data + padsize, skb->data, hdrlen);
+                       skb_pull(skb, padsize);
                }
 
                /*
@@ -1805,6 +1862,9 @@ accept:
                        ath5k_check_ibss_tsf(sc, skb, &rxs);
 
                __ieee80211_rx(sc->hw, skb, &rxs);
+
+               bf->skb = next_skb;
+               bf->skbaddr = next_skb_addr;
 next:
                list_move_tail(&bf->list, &sc->rxbuf);
        } while (ath5k_rxbuf_setup(sc, bf) == 0);
@@ -1849,30 +1909,26 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
                pci_unmap_single(sc->pdev, bf->skbaddr, skb->len,
                                PCI_DMA_TODEVICE);
 
-               memset(&info->status, 0, sizeof(info->status));
-               info->tx_rate_idx = ath5k_hw_to_driver_rix(sc,
-                               ts.ts_rate[ts.ts_final_idx]);
-               info->status.retry_count = ts.ts_longretry;
-
+               ieee80211_tx_info_clear_status(info);
                for (i = 0; i < 4; i++) {
-                       struct ieee80211_tx_altrate *r =
-                               &info->status.retries[i];
+                       struct ieee80211_tx_rate *r =
+                               &info->status.rates[i];
 
                        if (ts.ts_rate[i]) {
-                               r->rate_idx = ath5k_hw_to_driver_rix(sc, ts.ts_rate[i]);
-                               r->limit = ts.ts_retry[i];
+                               r->idx = ath5k_hw_to_driver_rix(sc, ts.ts_rate[i]);
+                               r->count = ts.ts_retry[i];
                        } else {
-                               r->rate_idx = -1;
-                               r->limit = 0;
+                               r->idx = -1;
+                               r->count = 0;
                        }
                }
 
-               info->status.excessive_retries = 0;
+               /* count the successful attempt as well */
+               info->status.rates[ts.ts_final_idx].count++;
+
                if (unlikely(ts.ts_status)) {
                        sc->ll_stats.dot11ACKFailureCount++;
-                       if (ts.ts_status & AR5K_TXERR_XRETRY)
-                               info->status.excessive_retries = 1;
-                       else if (ts.ts_status & AR5K_TXERR_FILT)
+                       if (ts.ts_status & AR5K_TXERR_FILT)
                                info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
                } else {
                        info->flags |= IEEE80211_TX_STAT_ACK;
@@ -2159,6 +2215,7 @@ ath5k_beacon_config(struct ath5k_softc *sc)
        if (sc->opmode == NL80211_IFTYPE_STATION) {
                sc->imask |= AR5K_INT_BMISS;
        } else if (sc->opmode == NL80211_IFTYPE_ADHOC ||
+                       sc->opmode == NL80211_IFTYPE_MESH_POINT ||
                        sc->opmode == NL80211_IFTYPE_AP) {
                /*
                 * In IBSS mode we use a self-linked tx descriptor and let the
@@ -2190,18 +2247,13 @@ ath5k_beacon_config(struct ath5k_softc *sc)
 \********************/
 
 static int
-ath5k_init(struct ath5k_softc *sc, bool is_resume)
+ath5k_init(struct ath5k_softc *sc)
 {
        struct ath5k_hw *ah = sc->ah;
        int ret, i;
 
        mutex_lock(&sc->lock);
 
-       if (is_resume && !test_bit(ATH_STAT_STARTED, sc->status))
-               goto out_ok;
-
-       __clear_bit(ATH_STAT_STARTED, sc->status);
-
        ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mode %d\n", sc->opmode);
 
        /*
@@ -2219,9 +2271,9 @@ ath5k_init(struct ath5k_softc *sc, bool is_resume)
         */
        sc->curchan = sc->hw->conf.channel;
        sc->curband = &sc->sbands[sc->curchan->band];
-       sc->imask = AR5K_INT_RX | AR5K_INT_TX | AR5K_INT_RXEOL |
-               AR5K_INT_RXORN | AR5K_INT_FATAL | AR5K_INT_GLOBAL |
-               AR5K_INT_MIB;
+       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, false, false);
        if (ret)
                goto done;
@@ -2233,15 +2285,12 @@ ath5k_init(struct ath5k_softc *sc, bool is_resume)
        for (i = 0; i < AR5K_KEYTABLE_SIZE; i++)
                ath5k_hw_reset_key(ah, i);
 
-       __set_bit(ATH_STAT_STARTED, sc->status);
-
        /* 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)));
 
-out_ok:
        ret = 0;
 done:
        mmiowb();
@@ -2296,7 +2345,7 @@ ath5k_stop_locked(struct ath5k_softc *sc)
  * stop is preempted).
  */
 static int
-ath5k_stop_hw(struct ath5k_softc *sc, bool is_suspend)
+ath5k_stop_hw(struct ath5k_softc *sc)
 {
        int ret;
 
@@ -2327,8 +2376,6 @@ ath5k_stop_hw(struct ath5k_softc *sc, bool is_suspend)
                }
        }
        ath5k_txbuf_free(sc, sc->bbuf);
-       if (!is_suspend)
-               __clear_bit(ATH_STAT_STARTED, sc->status);
 
        mmiowb();
        mutex_unlock(&sc->lock);
@@ -2413,9 +2460,10 @@ ath5k_intr(int irq, void *dev_id)
                                /* bump tx trigger level */
                                ath5k_hw_update_tx_triglevel(ah, true);
                        }
-                       if (status & AR5K_INT_RX)
+                       if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR))
                                tasklet_schedule(&sc->rxtq);
-                       if (status & AR5K_INT_TX)
+                       if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC
+                                       | AR5K_INT_TXERR | AR5K_INT_TXEOL))
                                tasklet_schedule(&sc->txtq);
                        if (status & AR5K_INT_BMISS) {
                        }
@@ -2531,8 +2579,7 @@ ath5k_register_led(struct ath5k_softc *sc, struct ath5k_led *led,
        led->led_dev.brightness_set = ath5k_led_brightness_set;
 
        err = led_classdev_register(&sc->pdev->dev, &led->led_dev);
-       if (err)
-       {
+       if (err) {
                ATH5K_WARN(sc, "could not register LED %s\n", name);
                led->sc = NULL;
        }
@@ -2581,6 +2628,17 @@ ath5k_init_leds(struct ath5k_softc *sc)
                sc->led_pin = 1;
                sc->led_on = 1;  /* active high */
        }
+       /*
+        * Pin 3 on Foxconn chips used in Acer Aspire One (0x105b:e008) and
+        * in emachines notebooks with AMBIT subsystem.
+        */
+       if (pdev->subsystem_vendor == PCI_VENDOR_ID_FOXCONN ||
+           pdev->subsystem_vendor == PCI_VENDOR_ID_AMBIT) {
+               __set_bit(ATH_STAT_LEDSOFT, sc->status);
+               sc->led_pin = 3;
+               sc->led_on = 0;  /* active low */
+       }
+
        if (!test_bit(ATH_STAT_LEDSOFT, sc->status))
                goto out;
 
@@ -2611,7 +2669,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
        struct ath5k_buf *bf;
        unsigned long flags;
        int hdrlen;
-       int pad;
+       int padsize;
 
        ath5k_debug_dump_skb(sc, skb, "TX  ", 1);
 
@@ -2623,15 +2681,16 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
         * if this is not the case we add the padding after the header
         */
        hdrlen = ieee80211_get_hdrlen_from_skb(skb);
-       if (hdrlen & 3) {
-               pad = hdrlen % 4;
-               if (skb_headroom(skb) < pad) {
+       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, pad);
-                       return -1;
+                                 " headroom to pad %d\n", hdrlen, padsize);
+                       return NETDEV_TX_BUSY;
                }
-               skb_push(skb, pad);
-               memmove(skb->data, skb->data+pad, hdrlen);
+               skb_push(skb, padsize);
+               memmove(skb->data, skb->data+padsize, hdrlen);
        }
 
        spin_lock_irqsave(&sc->txbuflock, flags);
@@ -2639,7 +2698,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
                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));
-               return -1;
+               return NETDEV_TX_BUSY;
        }
        bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list);
        list_del(&bf->list);
@@ -2657,10 +2716,10 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
                sc->txbuf_len++;
                spin_unlock_irqrestore(&sc->txbuflock, flags);
                dev_kfree_skb_any(skb);
-               return 0;
+               return NETDEV_TX_OK;
        }
 
-       return 0;
+       return NETDEV_TX_OK;
 }
 
 static int
@@ -2727,12 +2786,12 @@ ath5k_reset_wake(struct ath5k_softc *sc)
 
 static int ath5k_start(struct ieee80211_hw *hw)
 {
-       return ath5k_init(hw->priv, false);
+       return ath5k_init(hw->priv);
 }
 
 static void ath5k_stop(struct ieee80211_hw *hw)
 {
-       ath5k_stop_hw(hw->priv, false);
+       ath5k_stop_hw(hw->priv);
 }
 
 static int ath5k_add_interface(struct ieee80211_hw *hw,
@@ -2753,6 +2812,7 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
        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;
                break;
@@ -2764,6 +2824,7 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
        /* 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);
 
        ret = 0;
 end:
@@ -2776,11 +2837,13 @@ ath5k_remove_interface(struct ieee80211_hw *hw,
                        struct ieee80211_if_init_conf *conf)
 {
        struct ath5k_softc *sc = hw->priv;
+       u8 mac[ETH_ALEN] = {};
 
        mutex_lock(&sc->lock);
        if (sc->vif != conf->vif)
                goto end;
 
+       ath5k_hw_set_lladdr(sc->ah, mac);
        sc->vif = NULL;
 end:
        mutex_unlock(&sc->lock);
@@ -2794,11 +2857,17 @@ ath5k_config(struct ieee80211_hw *hw, u32 changed)
 {
        struct ath5k_softc *sc = hw->priv;
        struct ieee80211_conf *conf = &hw->conf;
+       int ret;
+
+       mutex_lock(&sc->lock);
 
        sc->bintval = conf->beacon_int;
        sc->power_level = conf->power_level;
 
-       return ath5k_chan_set(sc, conf->channel);
+       ret = ath5k_chan_set(sc, conf->channel);
+
+       mutex_unlock(&sc->lock);
+       return ret;
 }
 
 static int
@@ -2824,6 +2893,7 @@ ath5k_config_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        }
        if (conf->changed & IEEE80211_IFCC_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) {
@@ -2891,9 +2961,9 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
                if (*new_flags & FIF_PROMISC_IN_BSS) {
                        rfilt |= AR5K_RX_FILTER_PROM;
                        __set_bit(ATH_STAT_PROMISC, sc->status);
-               }
-               else
+               } else {
                        __clear_bit(ATH_STAT_PROMISC, sc->status);
+               }
        }
 
        /* Note, AR5K_RX_FILTER_MCAST is already enabled */
@@ -2950,11 +3020,16 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
                sc->opmode != NL80211_IFTYPE_MESH_POINT &&
                test_bit(ATH_STAT_PROMISC, sc->status))
                rfilt |= AR5K_RX_FILTER_PROM;
-       if (sc->opmode == NL80211_IFTYPE_ADHOC)
+       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;
 
        /* Set filters */
-       ath5k_hw_set_rx_filter(ah,rfilt);
+       ath5k_hw_set_rx_filter(ah, rfilt);
 
        /* Set multicast bits */
        ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]);
@@ -2965,18 +3040,19 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
 
 static int
 ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
-               const u8 *local_addr, const u8 *addr,
-               struct ieee80211_key_conf *key)
+             struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+             struct ieee80211_key_conf *key)
 {
        struct ath5k_softc *sc = hw->priv;
        int ret = 0;
 
-       switch(key->alg) {
+       if (modparam_nohwcrypt)
+               return -EOPNOTSUPP;
+
+       switch (key->alg) {
        case ALG_WEP:
-       /* XXX: fix hardware encryption, its not working. For now
-        * allow software encryption */
-               /* break; */
        case ALG_TKIP:
+               break;
        case ALG_CCMP:
                return -EOPNOTSUPP;
        default:
@@ -2988,13 +3064,16 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 
        switch (cmd) {
        case SET_KEY:
-               ret = ath5k_hw_set_key(sc->ah, key->keyidx, key, addr);
+               ret = ath5k_hw_set_key(sc->ah, key->keyidx, key,
+                                      sta ? sta->addr : NULL);
                if (ret) {
                        ATH5K_ERR(sc, "can't set the key\n");
                        goto unlock;
                }
                __set_bit(key->keyidx, sc->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);
@@ -3046,6 +3125,14 @@ ath5k_get_tsf(struct ieee80211_hw *hw)
 }
 
 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;
@@ -3082,4 +3169,32 @@ ath5k_beacon_update(struct ath5k_softc *sc, struct sk_buff *skb)
 
        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;
+       if (changes & BSS_CHANGED_ASSOC) {
+               mutex_lock(&sc->lock);
+               sc->assoc = bss_conf->assoc;
+               if (sc->opmode == NL80211_IFTYPE_STATION)
+                       set_beacon_filter(hw, sc->assoc);
+               mutex_unlock(&sc->lock);
+       }
+}