ath5k: fix CAB queue operation
[linux-2.6.git] / drivers / net / wireless / ath / ath5k / base.c
index f26a689..3a1c156 100644 (file)
@@ -218,6 +218,8 @@ 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_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);
@@ -301,7 +303,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);
 static inline void ath5k_txbuf_free(struct ath5k_softc *sc,
                                struct ath5k_buf *bf)
 {
@@ -516,6 +519,7 @@ 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;
 
@@ -670,7 +674,6 @@ ath5k_pci_suspend(struct pci_dev *pdev, pm_message_t state)
 
        ath5k_led_off(sc);
 
-       free_irq(pdev->irq, sc);
        pci_save_state(pdev);
        pci_disable_device(pdev);
        pci_set_power_state(pdev, PCI_D3hot);
@@ -698,18 +701,8 @@ ath5k_pci_resume(struct pci_dev *pdev)
         */
        pci_write_config_byte(pdev, 0x41, 0);
 
-       err = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc);
-       if (err) {
-               ATH5K_ERR(sc, "request_irq failed\n");
-               goto err_no_irq;
-       }
-
        ath5k_led_enable(sc);
        return 0;
-
-err_no_irq:
-       pci_disable_device(pdev);
-       return err;
 }
 #endif /* CONFIG_PM */
 
@@ -789,12 +782,18 @@ 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);
@@ -1118,6 +1117,8 @@ ath5k_mode_setup(struct ath5k_softc *sc)
        struct ath5k_hw *ah = sc->ah;
        u32 rfilt;
 
+       ah->ah_op_mode = sc->opmode;
+
        /* configure rx filter */
        rfilt = sc->filter_flags;
        ath5k_hw_set_rx_filter(ah, rfilt);
@@ -1232,10 +1233,10 @@ ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
 }
 
 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)
 {
        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);
@@ -1905,7 +1906,8 @@ accept:
                if (sc->opmode == NL80211_IFTYPE_ADHOC)
                        ath5k_check_ibss_tsf(sc, skb, &rxs);
 
-               __ieee80211_rx(sc->hw, skb, &rxs);
+               memcpy(IEEE80211_SKB_RXCB(skb), &rxs, sizeof(rxs));
+               ieee80211_rx(sc->hw, skb);
 
                bf->skb = next_skb;
                bf->skbaddr = next_skb_addr;
@@ -1998,9 +2000,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]);
 }
 
 
@@ -2082,13 +2087,6 @@ err_unmap:
        return ret;
 }
 
-static void ath5k_beacon_disable(struct ath5k_softc *sc)
-{
-       sc->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA);
-       ath5k_hw_set_imr(sc->ah, sc->imask);
-       ath5k_hw_stop_tx_dma(sc->ah, sc->bhalq);
-}
-
 /*
  * Transmit a beacon frame at SWBA.  Dynamic updates to the
  * frame contents are done as needed and the slot time is
@@ -2102,6 +2100,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");
 
@@ -2155,6 +2154,12 @@ ath5k_beacon_send(struct ath5k_softc *sc)
        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++;
 }
 
@@ -2275,13 +2280,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
@@ -2294,16 +2297,17 @@ 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)
@@ -2602,6 +2606,14 @@ 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;
@@ -2645,7 +2657,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)) {
                bf->skb = NULL;
                spin_lock_irqsave(&sc->txbuflock, flags);
                list_add_tail(&bf->list, &sc->txbuf);
@@ -2680,7 +2692,7 @@ ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan)
                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;
@@ -2761,6 +2773,7 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
        }
 
        ath5k_hw_set_lladdr(sc->ah, conf->mac_addr);
+       ath5k_mode_setup(sc);
 
        ret = 0;
 end:
@@ -2780,7 +2793,6 @@ ath5k_remove_interface(struct ieee80211_hw *hw,
                goto end;
 
        ath5k_hw_set_lladdr(sc->ah, mac);
-       ath5k_beacon_disable(sc);
        sc->vif = NULL;
 end:
        mutex_unlock(&sc->lock);
@@ -2974,6 +2986,9 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
        if (modparam_nohwcrypt)
                return -EOPNOTSUPP;
 
+       if (sc->opmode == NL80211_IFTYPE_AP)
+               return -EOPNOTSUPP;
+
        switch (key->alg) {
        case ALG_WEP:
        case ALG_TKIP:
@@ -3109,25 +3124,6 @@ out:
        return ret;
 }
 
-/*
- *  Update the beacon and reconfigure the beacon queues.
- */
-static void
-ath5k_beacon_reconfig(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
-{
-       int ret;
-       unsigned long flags;
-       struct ath5k_softc *sc = hw->priv;
-
-       spin_lock_irqsave(&sc->block, flags);
-       ret = ath5k_beacon_update(hw, vif);
-       spin_unlock_irqrestore(&sc->block, flags);
-       if (ret == 0) {
-               ath5k_beacon_config(sc);
-               mmiowb();
-       }
-}
-
 static void
 set_beacon_filter(struct ieee80211_hw *hw, bool enable)
 {
@@ -3150,6 +3146,7 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
 {
        struct ath5k_softc *sc = hw->priv;
        struct ath5k_hw *ah = sc->ah;
+       unsigned long flags;
 
        mutex_lock(&sc->lock);
        if (WARN_ON(sc->vif != vif))
@@ -3175,13 +3172,19 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
                        AR5K_LED_ASSOC : AR5K_LED_INIT);
        }
 
-       if (changes & BSS_CHANGED_BEACON &&
-           (vif->type == NL80211_IFTYPE_ADHOC ||
-            vif->type == NL80211_IFTYPE_MESH_POINT ||
-            vif->type == NL80211_IFTYPE_AP)) {
-               ath5k_beacon_reconfig(hw, vif);
+       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);
 }