Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/linville...
authorDavid S. Miller <davem@davemloft.net>
Tue, 15 Mar 2011 20:57:18 +0000 (13:57 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 15 Mar 2011 20:57:18 +0000 (13:57 -0700)
31 files changed:
drivers/net/wireless/ath/ath5k/base.c
drivers/net/wireless/ath/ath5k/base.h
drivers/net/wireless/ath/ath5k/mac80211-ops.c
drivers/net/wireless/ath/ath9k/ar9485_initvals.h
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/beacon.c
drivers/net/wireless/ath/ath9k/calib.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/mac.c
drivers/net/wireless/ath/ath9k/mac.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/recv.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/iwlwifi/iwl-agn-lib.c
drivers/net/wireless/iwlwifi/iwl-agn.c
drivers/net/wireless/iwlwifi/iwl-commands.h
drivers/net/wireless/iwlwifi/iwl-core.h
drivers/net/wireless/iwlwifi/iwl-dev.h
drivers/net/wireless/iwlwifi/iwl-scan.c
drivers/net/wireless/libertas/mesh.c
drivers/net/wireless/wl1251/ps.c
include/net/cfg80211.h
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/driver-ops.h
net/mac80211/driver-trace.h
net/mac80211/mlme.c
net/mac80211/rc80211_minstrel_ht.c
net/wireless/ethtool.c

index e6ff62e60a79000d5e99f3ea80a65d2012205085..4d7f21ee111cf28b3aa2120d38c4b08282c8cb8e 100644 (file)
@@ -943,6 +943,7 @@ ath5k_txq_setup(struct ath5k_softc *sc,
                spin_lock_init(&txq->lock);
                txq->setup = true;
                txq->txq_len = 0;
+               txq->txq_max = ATH5K_TXQ_LEN_MAX;
                txq->txq_poll_mark = false;
                txq->txq_stuck = 0;
        }
@@ -1534,7 +1535,7 @@ ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
                goto drop_packet;
        }
 
-       if (txq->txq_len >= ATH5K_TXQ_LEN_MAX)
+       if (txq->txq_len >= txq->txq_max)
                ieee80211_stop_queue(hw, txq->qnum);
 
        spin_lock_irqsave(&sc->txbuflock, flags);
index 8d1df1fa23511498e963efe783002cf3c7fc5559..978f1f4ac2f33b89c92dd40652faca4fc75ecb8d 100644 (file)
@@ -86,6 +86,7 @@ struct ath5k_txq {
        spinlock_t              lock;   /* lock on q and link */
        bool                    setup;
        int                     txq_len; /* number of queued buffers */
+       int                     txq_max; /* max allowed num of queued buffers */
        bool                    txq_poll_mark;
        unsigned int            txq_stuck;      /* informational counter */
 };
index c9b0b676adda2d19c61118dd7628f0c7df782650..9be29b728b1cffe2fdc5d9bf0977fe14e77fc562 100644 (file)
@@ -740,6 +740,47 @@ ath5k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
 }
 
 
+static void ath5k_get_ringparam(struct ieee80211_hw *hw,
+                               u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max)
+{
+       struct ath5k_softc *sc = hw->priv;
+
+       *tx = sc->txqs[AR5K_TX_QUEUE_ID_DATA_MIN].txq_max;
+
+       *tx_max = ATH5K_TXQ_LEN_MAX;
+       *rx = *rx_max = ATH_RXBUF;
+}
+
+
+static int ath5k_set_ringparam(struct ieee80211_hw *hw, u32 tx, u32 rx)
+{
+       struct ath5k_softc *sc = hw->priv;
+       u16 qnum;
+
+       /* only support setting tx ring size for now */
+       if (rx != ATH_RXBUF)
+               return -EINVAL;
+
+       /* restrict tx ring size min/max */
+       if (!tx || tx > ATH5K_TXQ_LEN_MAX)
+               return -EINVAL;
+
+       for (qnum = 0; qnum < ARRAY_SIZE(sc->txqs); qnum++) {
+               if (!sc->txqs[qnum].setup)
+                       continue;
+               if (sc->txqs[qnum].qnum < AR5K_TX_QUEUE_ID_DATA_MIN ||
+                   sc->txqs[qnum].qnum > AR5K_TX_QUEUE_ID_DATA_MAX)
+                       continue;
+
+               sc->txqs[qnum].txq_max = tx;
+               if (sc->txqs[qnum].txq_len >= sc->txqs[qnum].txq_max)
+                       ieee80211_stop_queue(hw, sc->txqs[qnum].qnum);
+       }
+
+       return 0;
+}
+
+
 const struct ieee80211_ops ath5k_hw_ops = {
        .tx                     = ath5k_tx,
        .start                  = ath5k_start,
@@ -778,4 +819,6 @@ const struct ieee80211_ops ath5k_hw_ops = {
        /* .napi_poll           = not implemented */
        .set_antenna            = ath5k_set_antenna,
        .get_antenna            = ath5k_get_antenna,
+       .set_ringparam          = ath5k_set_ringparam,
+       .get_ringparam          = ath5k_get_ringparam,
 };
index eac4d8526fc107ce3a9872735df394b6623f056f..71cc0a3a29fbb46ffaea21870116c757a119c763 100644 (file)
@@ -667,6 +667,7 @@ static const u32 ar9485_1_0_pcie_phy_clkreq_enable_L1[][2] = {
 
 static const u32 ar9485_1_0_soc_preamble[][2] = {
        /*   Addr     allmodes */
+       {0x00004090, 0x00aa10aa},
        {0x000040a4, 0x00a0c9c9},
        {0x00007048, 0x00000004},
 };
@@ -1708,6 +1709,7 @@ static const u32 ar9485_1_1_pcie_phy_clkreq_disable_L1[][2] = {
 static const u32 ar9485_1_1_soc_preamble[][2] = {
        /* Addr        allmodes */
        {0x00004014, 0xba280400},
+       {0x00004090, 0x00aa10aa},
        {0x000040a4, 0x00a0c9c9},
        {0x00007010, 0x00000022},
        {0x00007020, 0x00000000},
index c718ab512a979e15eafd4111c83dfc04898edcff..099bd4183ad00e42a4e4d75718261589365b36c4 100644 (file)
@@ -189,7 +189,6 @@ struct ath_txq {
        u32 axq_ampdu_depth;
        bool stopped;
        bool axq_tx_inprogress;
-       bool txq_flush_inprogress;
        struct list_head axq_acq;
        struct list_head txq_fifo[ATH_TXFIFO_DEPTH];
        struct list_head txq_fifo_pending;
index a4bdfdb043ef2da62acaaa06366381c226a3577c..6d2a545fc35e768c5a33433dd8f528c961887403 100644 (file)
@@ -373,6 +373,7 @@ void ath_beacon_tasklet(unsigned long data)
                        ath_dbg(common, ATH_DBG_BSTUCK,
                                "missed %u consecutive beacons\n",
                                sc->beacon.bmisscnt);
+                       ath9k_hw_stop_dma_queue(ah, sc->beacon.beaconq);
                        ath9k_hw_bstuck_nfcal(ah);
                } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) {
                        ath_dbg(common, ATH_DBG_BSTUCK,
@@ -450,16 +451,6 @@ void ath_beacon_tasklet(unsigned long data)
                sc->beacon.updateslot = OK;
        }
        if (bfaddr != 0) {
-               /*
-                * Stop any current dma and put the new frame(s) on the queue.
-                * This should never fail since we check above that no frames
-                * are still pending on the queue.
-                */
-               if (!ath9k_hw_stoptxdma(ah, sc->beacon.beaconq)) {
-                       ath_err(common, "beacon queue %u did not stop?\n",
-                               sc->beacon.beaconq);
-               }
-
                /* NB: cabq traffic should already be queued and primed */
                ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bfaddr);
                ath9k_hw_txstart(ah, sc->beacon.beaconq);
@@ -780,7 +771,7 @@ void ath9k_set_beaconing_status(struct ath_softc *sc, bool status)
                ah->imask &= ~ATH9K_INT_SWBA;
                ath9k_hw_set_interrupts(ah, ah->imask);
                tasklet_kill(&sc->bcon_tasklet);
-               ath9k_hw_stoptxdma(ah, sc->beacon.beaconq);
+               ath9k_hw_stop_dma_queue(ah, sc->beacon.beaconq);
        }
        ath9k_ps_restore(sc);
 }
index b4a92a4313f6f8fdda2a6afc91057c0201a2909d..8649581fa4ddcf0fb79384ee8a2a30333a9516bc 100644 (file)
@@ -262,7 +262,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
         * since 250us often results in NF load timeout and causes deaf
         * condition during stress testing 12/12/2009
         */
-       for (j = 0; j < 1000; j++) {
+       for (j = 0; j < 10000; j++) {
                if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
                     AR_PHY_AGC_CONTROL_NF) == 0)
                        break;
@@ -278,7 +278,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
         * here, the baseband nf cal will just be capped by our present
         * noisefloor until the next calibration timer.
         */
-       if (j == 1000) {
+       if (j == 10000) {
                ath_dbg(common, ATH_DBG_ANY,
                        "Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n",
                        REG_READ(ah, AR_PHY_AGC_CONTROL));
index 9a3438174f862d0bcfbffa44db6046c0d6169434..338b07502f1adc5621c60815a8f5ce8c0e0345e6 100644 (file)
@@ -701,7 +701,7 @@ static void ath9k_hw_init_pll(struct ath_hw *ah,
                              AR_CH0_DPLL3_PHASE_SHIFT, DPLL3_PHASE_SHIFT_VAL);
 
                REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c);
-               udelay(100);
+               udelay(1000);
 
                REG_WRITE(ah, AR_RTC_PLL_CONTROL2, 0x886666);
 
@@ -713,7 +713,7 @@ static void ath9k_hw_init_pll(struct ath_hw *ah,
                REG_RMW_FIELD(ah, AR_CH0_BB_DPLL3,
                              AR_CH0_DPLL3_PHASE_SHIFT, DPLL3_PHASE_SHIFT_VAL);
                REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x142c);
-               udelay(110);
+               udelay(1000);
        }
 
        pll = ath9k_hw_compute_pll_control(ah, chan);
index ef79f4c876ca9e0577b8ee440076b3aab57dd430..6650fd48415cb6f0cb2c558521aca3fc6b15f72f 100644 (file)
@@ -95,9 +95,9 @@
 #define REG_READ_FIELD(_a, _r, _f) \
        (((REG_READ(_a, _r) & _f) >> _f##_S))
 #define REG_SET_BIT(_a, _r, _f) \
-       REG_WRITE(_a, _r, REG_READ(_a, _r) | _f)
+       REG_WRITE(_a, _r, REG_READ(_a, _r) | (_f))
 #define REG_CLR_BIT(_a, _r, _f) \
-       REG_WRITE(_a, _r, REG_READ(_a, _r) & ~_f)
+       REG_WRITE(_a, _r, REG_READ(_a, _r) & ~(_f))
 
 #define DO_DELAY(x) do {                       \
                if ((++(x) % 64) == 0)          \
index 5efc869d65ff3772591203bbdc82368a49dffa42..562257ac52cfecf77ee58cd7f1318507f14d9b75 100644 (file)
@@ -143,84 +143,59 @@ bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel)
 }
 EXPORT_SYMBOL(ath9k_hw_updatetxtriglevel);
 
-bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q)
+void ath9k_hw_abort_tx_dma(struct ath_hw *ah)
 {
-#define ATH9K_TX_STOP_DMA_TIMEOUT      4000    /* usec */
-#define ATH9K_TIME_QUANTUM             100     /* usec */
-       struct ath_common *common = ath9k_hw_common(ah);
-       struct ath9k_hw_capabilities *pCap = &ah->caps;
-       struct ath9k_tx_queue_info *qi;
-       u32 tsfLow, j, wait;
-       u32 wait_time = ATH9K_TX_STOP_DMA_TIMEOUT / ATH9K_TIME_QUANTUM;
+       int i, q;
 
-       if (q >= pCap->total_queues) {
-               ath_dbg(common, ATH_DBG_QUEUE,
-                       "Stopping TX DMA, invalid queue: %u\n", q);
-               return false;
-       }
+       REG_WRITE(ah, AR_Q_TXD, AR_Q_TXD_M);
 
-       qi = &ah->txq[q];
-       if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
-               ath_dbg(common, ATH_DBG_QUEUE,
-                       "Stopping TX DMA, inactive queue: %u\n", q);
-               return false;
-       }
+       REG_SET_BIT(ah, AR_PCU_MISC, AR_PCU_FORCE_QUIET_COLL | AR_PCU_CLEAR_VMF);
+       REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH);
+       REG_SET_BIT(ah, AR_D_GBL_IFS_MISC, AR_D_GBL_IFS_MISC_IGNORE_BACKOFF);
 
-       REG_WRITE(ah, AR_Q_TXD, 1 << q);
+       for (q = 0; q < AR_NUM_QCU; q++) {
+               for (i = 0; i < 1000; i++) {
+                       if (i)
+                               udelay(5);
 
-       for (wait = wait_time; wait != 0; wait--) {
-               if (ath9k_hw_numtxpending(ah, q) == 0)
-                       break;
-               udelay(ATH9K_TIME_QUANTUM);
+                       if (!ath9k_hw_numtxpending(ah, q))
+                               break;
+               }
        }
 
-       if (ath9k_hw_numtxpending(ah, q)) {
-               ath_dbg(common, ATH_DBG_QUEUE,
-                       "%s: Num of pending TX Frames %d on Q %d\n",
-                       __func__, ath9k_hw_numtxpending(ah, q), q);
-
-               for (j = 0; j < 2; j++) {
-                       tsfLow = REG_READ(ah, AR_TSF_L32);
-                       REG_WRITE(ah, AR_QUIET2,
-                                 SM(10, AR_QUIET2_QUIET_DUR));
-                       REG_WRITE(ah, AR_QUIET_PERIOD, 100);
-                       REG_WRITE(ah, AR_NEXT_QUIET_TIMER, tsfLow >> 10);
-                       REG_SET_BIT(ah, AR_TIMER_MODE,
-                                      AR_QUIET_TIMER_EN);
-
-                       if ((REG_READ(ah, AR_TSF_L32) >> 10) == (tsfLow >> 10))
-                               break;
+       REG_CLR_BIT(ah, AR_PCU_MISC, AR_PCU_FORCE_QUIET_COLL | AR_PCU_CLEAR_VMF);
+       REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH);
+       REG_CLR_BIT(ah, AR_D_GBL_IFS_MISC, AR_D_GBL_IFS_MISC_IGNORE_BACKOFF);
 
-                       ath_dbg(common, ATH_DBG_QUEUE,
-                               "TSF has moved while trying to set quiet time TSF: 0x%08x\n",
-                               tsfLow);
-               }
+       REG_WRITE(ah, AR_Q_TXD, 0);
+}
+EXPORT_SYMBOL(ath9k_hw_abort_tx_dma);
 
-               REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH);
+bool ath9k_hw_stop_dma_queue(struct ath_hw *ah, u32 q)
+{
+#define ATH9K_TX_STOP_DMA_TIMEOUT      1000    /* usec */
+#define ATH9K_TIME_QUANTUM             100     /* usec */
+       int wait_time = ATH9K_TX_STOP_DMA_TIMEOUT / ATH9K_TIME_QUANTUM;
+       int wait;
 
-               udelay(200);
-               REG_CLR_BIT(ah, AR_TIMER_MODE, AR_QUIET_TIMER_EN);
+       REG_WRITE(ah, AR_Q_TXD, 1 << q);
 
-               wait = wait_time;
-               while (ath9k_hw_numtxpending(ah, q)) {
-                       if ((--wait) == 0) {
-                               ath_err(common,
-                                       "Failed to stop TX DMA in 100 msec after killing last frame\n");
-                               break;
-                       }
+       for (wait = wait_time; wait != 0; wait--) {
+               if (wait != wait_time)
                        udelay(ATH9K_TIME_QUANTUM);
-               }
 
-               REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH);
+               if (ath9k_hw_numtxpending(ah, q) == 0)
+                       break;
        }
 
        REG_WRITE(ah, AR_Q_TXD, 0);
+
        return wait != 0;
 
 #undef ATH9K_TX_STOP_DMA_TIMEOUT
 #undef ATH9K_TIME_QUANTUM
 }
-EXPORT_SYMBOL(ath9k_hw_stoptxdma);
+EXPORT_SYMBOL(ath9k_hw_stop_dma_queue);
 
 void ath9k_hw_gettxintrtxqs(struct ath_hw *ah, u32 *txqs)
 {
index 04d58ae923bb602602fe9bccb40fe918e7776424..b2b2ff852c328142a55396801010c46e32c72432 100644 (file)
@@ -676,7 +676,8 @@ void ath9k_hw_txstart(struct ath_hw *ah, u32 q);
 void ath9k_hw_cleartxdesc(struct ath_hw *ah, void *ds);
 u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q);
 bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel);
-bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q);
+bool ath9k_hw_stop_dma_queue(struct ath_hw *ah, u32 q);
+void ath9k_hw_abort_tx_dma(struct ath_hw *ah);
 void ath9k_hw_gettxintrtxqs(struct ath_hw *ah, u32 *txqs);
 bool ath9k_hw_set_txq_props(struct ath_hw *ah, int q,
                            const struct ath9k_tx_queue_info *qinfo);
index 2e228aada1a906fdd6298d63fb6d66f08dfc7536..115f162c617a92e56397be609b3e55d79913ac82 100644 (file)
@@ -2128,56 +2128,42 @@ static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
 
 static void ath9k_flush(struct ieee80211_hw *hw, bool drop)
 {
-#define ATH_FLUSH_TIMEOUT      60 /* ms */
        struct ath_softc *sc = hw->priv;
-       struct ath_txq *txq = NULL;
-       struct ath_hw *ah = sc->sc_ah;
-       struct ath_common *common = ath9k_hw_common(ah);
-       int i, j, npend = 0;
+       int timeout = 200; /* ms */
+       int i, j;
 
+       ath9k_ps_wakeup(sc);
        mutex_lock(&sc->mutex);
 
        cancel_delayed_work_sync(&sc->tx_complete_work);
 
-       for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
-               if (!ATH_TXQ_SETUP(sc, i))
-                       continue;
-               txq = &sc->tx.txq[i];
+       if (drop)
+               timeout = 1;
 
-               if (!drop) {
-                       for (j = 0; j < ATH_FLUSH_TIMEOUT; j++) {
-                               if (!ath9k_has_pending_frames(sc, txq))
-                                       break;
-                               usleep_range(1000, 2000);
-                       }
-               }
+       for (j = 0; j < timeout; j++) {
+               int npend = 0;
+
+               if (j)
+                       usleep_range(1000, 2000);
 
-               if (drop || ath9k_has_pending_frames(sc, txq)) {
-                       ath_dbg(common, ATH_DBG_QUEUE, "Drop frames from hw queue:%d\n",
-                               txq->axq_qnum);
-                       spin_lock_bh(&txq->axq_lock);
-                       txq->txq_flush_inprogress = true;
-                       spin_unlock_bh(&txq->axq_lock);
-
-                       ath9k_ps_wakeup(sc);
-                       ath9k_hw_stoptxdma(ah, txq->axq_qnum);
-                       npend = ath9k_hw_numtxpending(ah, txq->axq_qnum);
-                       ath9k_ps_restore(sc);
-                       if (npend)
-                               break;
-
-                       ath_draintxq(sc, txq, false);
-                       txq->txq_flush_inprogress = false;
+               for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
+                       if (!ATH_TXQ_SETUP(sc, i))
+                               continue;
+
+                       npend += ath9k_has_pending_frames(sc, &sc->tx.txq[i]);
                }
+
+               if (!npend)
+                   goto out;
        }
 
-       if (npend) {
+       if (!ath_drain_all_txq(sc, false))
                ath_reset(sc, false);
-               txq->txq_flush_inprogress = false;
-       }
 
+out:
        ieee80211_queue_delayed_work(hw, &sc->tx_complete_work, 0);
        mutex_unlock(&sc->mutex);
+       ath9k_ps_restore(sc);
 }
 
 struct ieee80211_ops ath9k_ops = {
index cb559e345b865ebb8aba346a72a6dbefe89ac3b0..a9c3f4672aa0e12736a24c8ddb4e4c280d635d29 100644 (file)
@@ -413,9 +413,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
         * mode interface or when in monitor mode. AP mode does not need this
         * since it receives all in-BSS frames anyway.
         */
-       if (((sc->sc_ah->opmode != NL80211_IFTYPE_AP) &&
-            (sc->rx.rxfilter & FIF_PROMISC_IN_BSS)) ||
-           (sc->sc_ah->is_monitoring))
+       if (sc->sc_ah->is_monitoring)
                rfilt |= ATH9K_RX_FILTER_PROM;
 
        if (sc->rx.rxfilter & FIF_CONTROL)
index e16136d617999a03ea2032febc9c465e489623ee..ef22096d40c918f7d16deb009eb13e0c83a40979 100644 (file)
@@ -166,7 +166,7 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
                fi = get_frame_info(bf->bf_mpdu);
                if (fi->retries) {
                        ath_tx_update_baw(sc, tid, fi->seqno);
-                       ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 0);
+                       ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0, 1);
                } else {
                        ath_tx_send_normal(sc, txq, NULL, &bf_head);
                }
@@ -1194,16 +1194,14 @@ bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
        if (sc->sc_flags & SC_OP_INVALID)
                return true;
 
-       /* Stop beacon queue */
-       ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
+       ath9k_hw_abort_tx_dma(ah);
 
-       /* Stop data queues */
+       /* Check if any queue remains active */
        for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
-               if (ATH_TXQ_SETUP(sc, i)) {
-                       txq = &sc->tx.txq[i];
-                       ath9k_hw_stoptxdma(ah, txq->axq_qnum);
-                       npend += ath9k_hw_numtxpending(ah, txq->axq_qnum);
-               }
+               if (!ATH_TXQ_SETUP(sc, i))
+                       continue;
+
+               npend += ath9k_hw_numtxpending(ah, sc->tx.txq[i].axq_qnum);
        }
 
        if (npend)
@@ -2014,8 +2012,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
                spin_lock_bh(&txq->axq_lock);
                if (list_empty(&txq->axq_q)) {
                        txq->axq_link = NULL;
-                       if (sc->sc_flags & SC_OP_TXAGGR &&
-                           !txq->txq_flush_inprogress)
+                       if (sc->sc_flags & SC_OP_TXAGGR)
                                ath_txq_schedule(sc, txq);
                        spin_unlock_bh(&txq->axq_lock);
                        break;
@@ -2096,7 +2093,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
 
                spin_lock_bh(&txq->axq_lock);
 
-               if (sc->sc_flags & SC_OP_TXAGGR && !txq->txq_flush_inprogress)
+               if (sc->sc_flags & SC_OP_TXAGGR)
                        ath_txq_schedule(sc, txq);
                spin_unlock_bh(&txq->axq_lock);
        }
@@ -2267,18 +2264,17 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
 
                spin_lock_bh(&txq->axq_lock);
 
-               if (!txq->txq_flush_inprogress) {
-                       if (!list_empty(&txq->txq_fifo_pending)) {
-                               INIT_LIST_HEAD(&bf_head);
-                               bf = list_first_entry(&txq->txq_fifo_pending,
-                                                     struct ath_buf, list);
-                               list_cut_position(&bf_head,
-                                                 &txq->txq_fifo_pending,
-                                                 &bf->bf_lastbf->list);
-                               ath_tx_txqaddbuf(sc, txq, &bf_head);
-                       } else if (sc->sc_flags & SC_OP_TXAGGR)
-                               ath_txq_schedule(sc, txq);
-               }
+               if (!list_empty(&txq->txq_fifo_pending)) {
+                       INIT_LIST_HEAD(&bf_head);
+                       bf = list_first_entry(&txq->txq_fifo_pending,
+                                             struct ath_buf, list);
+                       list_cut_position(&bf_head,
+                                         &txq->txq_fifo_pending,
+                                         &bf->bf_lastbf->list);
+                       ath_tx_txqaddbuf(sc, txq, &bf_head);
+               } else if (sc->sc_flags & SC_OP_TXAGGR)
+                       ath_txq_schedule(sc, txq);
+
                spin_unlock_bh(&txq->axq_lock);
        }
 }
index 25fccf9a3001374c0cadf899d3a0023eef38b693..2003c1d4295f3a480328cf9b8341a959c243a10b 100644 (file)
@@ -1115,6 +1115,18 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv,
        return added;
 }
 
+static int iwl_fill_offch_tx(struct iwl_priv *priv, void *data, size_t maxlen)
+{
+       struct sk_buff *skb = priv->_agn.offchan_tx_skb;
+
+       if (skb->len < maxlen)
+               maxlen = skb->len;
+
+       memcpy(data, skb->data, maxlen);
+
+       return maxlen;
+}
+
 int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
 {
        struct iwl_host_cmd cmd = {
@@ -1157,17 +1169,25 @@ int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
        scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH;
        scan->quiet_time = IWL_ACTIVE_QUIET_TIME;
 
-       if (iwl_is_any_associated(priv)) {
+       if (priv->scan_type != IWL_SCAN_OFFCH_TX &&
+           iwl_is_any_associated(priv)) {
                u16 interval = 0;
                u32 extra;
                u32 suspend_time = 100;
                u32 scan_suspend_time = 100;
 
                IWL_DEBUG_INFO(priv, "Scanning while associated...\n");
-               if (priv->is_internal_short_scan)
+               switch (priv->scan_type) {
+               case IWL_SCAN_OFFCH_TX:
+                       WARN_ON(1);
+                       break;
+               case IWL_SCAN_RADIO_RESET:
                        interval = 0;
-               else
+                       break;
+               case IWL_SCAN_NORMAL:
                        interval = vif->bss_conf.beacon_int;
+                       break;
+               }
 
                scan->suspend_time = 0;
                scan->max_out_time = cpu_to_le32(200 * 1024);
@@ -1180,29 +1200,41 @@ int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
                scan->suspend_time = cpu_to_le32(scan_suspend_time);
                IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n",
                               scan_suspend_time, interval);
+       } else if (priv->scan_type == IWL_SCAN_OFFCH_TX) {
+               scan->suspend_time = 0;
+               scan->max_out_time =
+                       cpu_to_le32(1024 * priv->_agn.offchan_tx_timeout);
        }
 
-       if (priv->is_internal_short_scan) {
+       switch (priv->scan_type) {
+       case IWL_SCAN_RADIO_RESET:
                IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n");
-       } else if (priv->scan_request->n_ssids) {
-               int i, p = 0;
-               IWL_DEBUG_SCAN(priv, "Kicking off active scan\n");
-               for (i = 0; i < priv->scan_request->n_ssids; i++) {
-                       /* always does wildcard anyway */
-                       if (!priv->scan_request->ssids[i].ssid_len)
-                               continue;
-                       scan->direct_scan[p].id = WLAN_EID_SSID;
-                       scan->direct_scan[p].len =
-                               priv->scan_request->ssids[i].ssid_len;
-                       memcpy(scan->direct_scan[p].ssid,
-                              priv->scan_request->ssids[i].ssid,
-                              priv->scan_request->ssids[i].ssid_len);
-                       n_probes++;
-                       p++;
-               }
-               is_active = true;
-       } else
-               IWL_DEBUG_SCAN(priv, "Start passive scan.\n");
+               break;
+       case IWL_SCAN_NORMAL:
+               if (priv->scan_request->n_ssids) {
+                       int i, p = 0;
+                       IWL_DEBUG_SCAN(priv, "Kicking off active scan\n");
+                       for (i = 0; i < priv->scan_request->n_ssids; i++) {
+                               /* always does wildcard anyway */
+                               if (!priv->scan_request->ssids[i].ssid_len)
+                                       continue;
+                               scan->direct_scan[p].id = WLAN_EID_SSID;
+                               scan->direct_scan[p].len =
+                                       priv->scan_request->ssids[i].ssid_len;
+                               memcpy(scan->direct_scan[p].ssid,
+                                      priv->scan_request->ssids[i].ssid,
+                                      priv->scan_request->ssids[i].ssid_len);
+                               n_probes++;
+                               p++;
+                       }
+                       is_active = true;
+               } else
+                       IWL_DEBUG_SCAN(priv, "Start passive scan.\n");
+               break;
+       case IWL_SCAN_OFFCH_TX:
+               IWL_DEBUG_SCAN(priv, "Start offchannel TX scan.\n");
+               break;
+       }
 
        scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
        scan->tx_cmd.sta_id = ctx->bcast_sta_id;
@@ -1300,38 +1332,77 @@ int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
        rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS;
        rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS;
        scan->rx_chain = cpu_to_le16(rx_chain);
-       if (!priv->is_internal_short_scan) {
+       switch (priv->scan_type) {
+       case IWL_SCAN_NORMAL:
                cmd_len = iwl_fill_probe_req(priv,
                                        (struct ieee80211_mgmt *)scan->data,
                                        vif->addr,
                                        priv->scan_request->ie,
                                        priv->scan_request->ie_len,
                                        IWL_MAX_SCAN_SIZE - sizeof(*scan));
-       } else {
+               break;
+       case IWL_SCAN_RADIO_RESET:
                /* use bcast addr, will not be transmitted but must be valid */
                cmd_len = iwl_fill_probe_req(priv,
                                        (struct ieee80211_mgmt *)scan->data,
                                        iwl_bcast_addr, NULL, 0,
                                        IWL_MAX_SCAN_SIZE - sizeof(*scan));
-
+               break;
+       case IWL_SCAN_OFFCH_TX:
+               cmd_len = iwl_fill_offch_tx(priv, scan->data,
+                                           IWL_MAX_SCAN_SIZE
+                                            - sizeof(*scan)
+                                            - sizeof(struct iwl_scan_channel));
+               scan->scan_flags |= IWL_SCAN_FLAGS_ACTION_FRAME_TX;
+               break;
+       default:
+               BUG();
        }
        scan->tx_cmd.len = cpu_to_le16(cmd_len);
 
        scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK |
                               RXON_FILTER_BCON_AWARE_MSK);
 
-       if (priv->is_internal_short_scan) {
+       switch (priv->scan_type) {
+       case IWL_SCAN_RADIO_RESET:
                scan->channel_count =
                        iwl_get_single_channel_for_scan(priv, vif, band,
-                               (void *)&scan->data[le16_to_cpu(
-                               scan->tx_cmd.len)]);
-       } else {
+                               (void *)&scan->data[cmd_len]);
+               break;
+       case IWL_SCAN_NORMAL:
                scan->channel_count =
                        iwl_get_channels_for_scan(priv, vif, band,
                                is_active, n_probes,
-                               (void *)&scan->data[le16_to_cpu(
-                               scan->tx_cmd.len)]);
+                               (void *)&scan->data[cmd_len]);
+               break;
+       case IWL_SCAN_OFFCH_TX: {
+               struct iwl_scan_channel *scan_ch;
+
+               scan->channel_count = 1;
+
+               scan_ch = (void *)&scan->data[cmd_len];
+               scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE;
+               scan_ch->channel =
+                       cpu_to_le16(priv->_agn.offchan_tx_chan->hw_value);
+               scan_ch->active_dwell =
+                       cpu_to_le16(priv->_agn.offchan_tx_timeout);
+               scan_ch->passive_dwell = 0;
+
+               /* Set txpower levels to defaults */
+               scan_ch->dsp_atten = 110;
+
+               /* NOTE: if we were doing 6Mb OFDM for scans we'd use
+                * power level:
+                * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3;
+                */
+               if (priv->_agn.offchan_tx_chan->band == IEEE80211_BAND_5GHZ)
+                       scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
+               else
+                       scan_ch->tx_gain = ((1 << 5) | (5 << 3));
+               }
+               break;
        }
+
        if (scan->channel_count == 0) {
                IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count);
                return -EIO;
index e0cd3e27a0d92825c0cc5048853c9beb202e7fb4..581dc9f102738a3433da6f56438ea242484eaf6c 100644 (file)
@@ -2937,6 +2937,91 @@ static void iwl_bg_rx_replenish(struct work_struct *data)
        mutex_unlock(&priv->mutex);
 }
 
+static int iwl_mac_offchannel_tx(struct ieee80211_hw *hw, struct sk_buff *skb,
+                                struct ieee80211_channel *chan,
+                                enum nl80211_channel_type channel_type,
+                                unsigned int wait)
+{
+       struct iwl_priv *priv = hw->priv;
+       int ret;
+
+       /* Not supported if we don't have PAN */
+       if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN))) {
+               ret = -EOPNOTSUPP;
+               goto free;
+       }
+
+       /* Not supported on pre-P2P firmware */
+       if (!(priv->contexts[IWL_RXON_CTX_PAN].interface_modes &
+                                       BIT(NL80211_IFTYPE_P2P_CLIENT))) {
+               ret = -EOPNOTSUPP;
+               goto free;
+       }
+
+       mutex_lock(&priv->mutex);
+
+       if (!priv->contexts[IWL_RXON_CTX_PAN].is_active) {
+               /*
+                * If the PAN context is free, use the normal
+                * way of doing remain-on-channel offload + TX.
+                */
+               ret = 1;
+               goto out;
+       }
+
+       /* TODO: queue up if scanning? */
+       if (test_bit(STATUS_SCANNING, &priv->status) ||
+           priv->_agn.offchan_tx_skb) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       /*
+        * max_scan_ie_len doesn't include the blank SSID or the header,
+        * so need to add that again here.
+        */
+       if (skb->len > hw->wiphy->max_scan_ie_len + 24 + 2) {
+               ret = -ENOBUFS;
+               goto out;
+       }
+
+       priv->_agn.offchan_tx_skb = skb;
+       priv->_agn.offchan_tx_timeout = wait;
+       priv->_agn.offchan_tx_chan = chan;
+
+       ret = iwl_scan_initiate(priv, priv->contexts[IWL_RXON_CTX_PAN].vif,
+                               IWL_SCAN_OFFCH_TX, chan->band);
+       if (ret)
+               priv->_agn.offchan_tx_skb = NULL;
+ out:
+       mutex_unlock(&priv->mutex);
+ free:
+       if (ret < 0)
+               kfree_skb(skb);
+
+       return ret;
+}
+
+static int iwl_mac_offchannel_tx_cancel_wait(struct ieee80211_hw *hw)
+{
+       struct iwl_priv *priv = hw->priv;
+       int ret;
+
+       mutex_lock(&priv->mutex);
+
+       if (!priv->_agn.offchan_tx_skb)
+               return -EINVAL;
+
+       priv->_agn.offchan_tx_skb = NULL;
+
+       ret = iwl_scan_cancel_timeout(priv, 200);
+       if (ret)
+               ret = -EIO;
+       mutex_unlock(&priv->mutex);
+
+       return ret;
+}
+
 /*****************************************************************************
  *
  * mac80211 entry point functions
@@ -3815,6 +3900,8 @@ struct ieee80211_ops iwlagn_hw_ops = {
        .tx_last_beacon = iwl_mac_tx_last_beacon,
        .remain_on_channel = iwl_mac_remain_on_channel,
        .cancel_remain_on_channel = iwl_mac_cancel_remain_on_channel,
+       .offchannel_tx = iwl_mac_offchannel_tx,
+       .offchannel_tx_cancel_wait = iwl_mac_offchannel_tx_cancel_wait,
 };
 
 static void iwl_hw_detect(struct iwl_priv *priv)
index 03cfb74da2bc41213a2de882268a1bd2bd402033..ca42ffa63ed73865596325dc2f3157964efda6e2 100644 (file)
@@ -2964,9 +2964,15 @@ struct iwl3945_scan_cmd {
        u8 data[0];
 } __packed;
 
+enum iwl_scan_flags {
+       /* BIT(0) currently unused */
+       IWL_SCAN_FLAGS_ACTION_FRAME_TX  = BIT(1),
+       /* bits 2-7 reserved */
+};
+
 struct iwl_scan_cmd {
        __le16 len;
-       u8 reserved0;
+       u8 scan_flags;          /* scan flags: see enum iwl_scan_flags */
        u8 channel_count;       /* # channels in channel list */
        __le16 quiet_time;      /* dwell only this # millisecs on quiet channel
                                 * (only for active scan) */
index af47750f8985340f3d340294c4ffc33824161421..b316d833d9a2f4edb9ed3070c0a20d4f4a40bc34 100644 (file)
@@ -63,6 +63,8 @@
 #ifndef __iwl_core_h__
 #define __iwl_core_h__
 
+#include "iwl-dev.h"
+
 /************************
  * forward declarations *
  ************************/
@@ -551,6 +553,10 @@ u16 iwl_get_passive_dwell_time(struct iwl_priv *priv,
                               struct ieee80211_vif *vif);
 void iwl_setup_scan_deferred_work(struct iwl_priv *priv);
 void iwl_cancel_scan_deferred_work(struct iwl_priv *priv);
+int __must_check iwl_scan_initiate(struct iwl_priv *priv,
+                                  struct ieee80211_vif *vif,
+                                  enum iwl_scan_type scan_type,
+                                  enum ieee80211_band band);
 
 /* For faster active scanning, scan will move to the next channel if fewer than
  * PLCP_QUIET_THRESH packets are heard on this channel within
index 6a41deba68633ac36a86c918880c3ff45ef395fb..68b953f2bdc7436e5b1135e239f0b1a5bf45ab2d 100644 (file)
@@ -1230,6 +1230,12 @@ struct iwl_rxon_context {
        } ht;
 };
 
+enum iwl_scan_type {
+       IWL_SCAN_NORMAL,
+       IWL_SCAN_RADIO_RESET,
+       IWL_SCAN_OFFCH_TX,
+};
+
 struct iwl_priv {
 
        /* ieee device used by generic ieee processing code */
@@ -1290,7 +1296,7 @@ struct iwl_priv {
        enum ieee80211_band scan_band;
        struct cfg80211_scan_request *scan_request;
        struct ieee80211_vif *scan_vif;
-       bool is_internal_short_scan;
+       enum iwl_scan_type scan_type;
        u8 scan_tx_ant[IEEE80211_NUM_BANDS];
        u8 mgmt_tx_ant;
 
@@ -1504,6 +1510,10 @@ struct iwl_priv {
                        struct delayed_work hw_roc_work;
                        enum nl80211_channel_type hw_roc_chantype;
                        int hw_roc_duration;
+
+                       struct sk_buff *offchan_tx_skb;
+                       int offchan_tx_timeout;
+                       struct ieee80211_channel *offchan_tx_chan;
                } _agn;
 #endif
        };
index faa6d34cb6586b2ac8da23f6e1fd886ddecfe802..3a4d9e6b0421fa270c57cf443d783756c448d68e 100644 (file)
@@ -101,7 +101,7 @@ static void iwl_complete_scan(struct iwl_priv *priv, bool aborted)
                ieee80211_scan_completed(priv->hw, aborted);
        }
 
-       priv->is_internal_short_scan = false;
+       priv->scan_type = IWL_SCAN_NORMAL;
        priv->scan_vif = NULL;
        priv->scan_request = NULL;
 }
@@ -339,10 +339,10 @@ void iwl_init_scan_params(struct iwl_priv *priv)
                priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx;
 }
 
-static int __must_check iwl_scan_initiate(struct iwl_priv *priv,
-                                         struct ieee80211_vif *vif,
-                                         bool internal,
-                                         enum ieee80211_band band)
+int __must_check iwl_scan_initiate(struct iwl_priv *priv,
+                                  struct ieee80211_vif *vif,
+                                  enum iwl_scan_type scan_type,
+                                  enum ieee80211_band band)
 {
        int ret;
 
@@ -370,17 +370,19 @@ static int __must_check iwl_scan_initiate(struct iwl_priv *priv,
        }
 
        IWL_DEBUG_SCAN(priv, "Starting %sscan...\n",
-                       internal ? "internal short " : "");
+                       scan_type == IWL_SCAN_NORMAL ? "" :
+                       scan_type == IWL_SCAN_OFFCH_TX ? "offchan TX " :
+                       "internal short ");
 
        set_bit(STATUS_SCANNING, &priv->status);
-       priv->is_internal_short_scan = internal;
+       priv->scan_type = scan_type;
        priv->scan_start = jiffies;
        priv->scan_band = band;
 
        ret = priv->cfg->ops->utils->request_scan(priv, vif);
        if (ret) {
                clear_bit(STATUS_SCANNING, &priv->status);
-               priv->is_internal_short_scan = false;
+               priv->scan_type = IWL_SCAN_NORMAL;
                return ret;
        }
 
@@ -405,7 +407,7 @@ int iwl_mac_hw_scan(struct ieee80211_hw *hw,
        mutex_lock(&priv->mutex);
 
        if (test_bit(STATUS_SCANNING, &priv->status) &&
-           !priv->is_internal_short_scan) {
+           priv->scan_type != IWL_SCAN_NORMAL) {
                IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
                ret = -EAGAIN;
                goto out_unlock;
@@ -419,11 +421,11 @@ int iwl_mac_hw_scan(struct ieee80211_hw *hw,
         * If an internal scan is in progress, just set
         * up the scan_request as per above.
         */
-       if (priv->is_internal_short_scan) {
+       if (priv->scan_type != IWL_SCAN_NORMAL) {
                IWL_DEBUG_SCAN(priv, "SCAN request during internal scan\n");
                ret = 0;
        } else
-               ret = iwl_scan_initiate(priv, vif, false,
+               ret = iwl_scan_initiate(priv, vif, IWL_SCAN_NORMAL,
                                        req->channels[0]->band);
 
        IWL_DEBUG_MAC80211(priv, "leave\n");
@@ -452,7 +454,7 @@ static void iwl_bg_start_internal_scan(struct work_struct *work)
 
        mutex_lock(&priv->mutex);
 
-       if (priv->is_internal_short_scan == true) {
+       if (priv->scan_type == IWL_SCAN_RADIO_RESET) {
                IWL_DEBUG_SCAN(priv, "Internal scan already in progress\n");
                goto unlock;
        }
@@ -462,7 +464,7 @@ static void iwl_bg_start_internal_scan(struct work_struct *work)
                goto unlock;
        }
 
-       if (iwl_scan_initiate(priv, NULL, true, priv->band))
+       if (iwl_scan_initiate(priv, NULL, IWL_SCAN_RADIO_RESET, priv->band))
                IWL_DEBUG_SCAN(priv, "failed to start internal short scan\n");
  unlock:
        mutex_unlock(&priv->mutex);
@@ -549,8 +551,7 @@ static void iwl_bg_scan_completed(struct work_struct *work)
            container_of(work, struct iwl_priv, scan_completed);
        bool aborted;
 
-       IWL_DEBUG_SCAN(priv, "Completed %sscan.\n",
-                      priv->is_internal_short_scan ? "internal short " : "");
+       IWL_DEBUG_SCAN(priv, "Completed scan.\n");
 
        cancel_delayed_work(&priv->scan_check);
 
@@ -565,7 +566,13 @@ static void iwl_bg_scan_completed(struct work_struct *work)
                goto out_settings;
        }
 
-       if (priv->is_internal_short_scan && !aborted) {
+       if (priv->scan_type == IWL_SCAN_OFFCH_TX && priv->_agn.offchan_tx_skb) {
+               ieee80211_tx_status_irqsafe(priv->hw,
+                                           priv->_agn.offchan_tx_skb);
+               priv->_agn.offchan_tx_skb = NULL;
+       }
+
+       if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) {
                int err;
 
                /* Check if mac80211 requested scan during our internal scan */
@@ -573,7 +580,7 @@ static void iwl_bg_scan_completed(struct work_struct *work)
                        goto out_complete;
 
                /* If so request a new scan */
-               err = iwl_scan_initiate(priv, priv->scan_vif, false,
+               err = iwl_scan_initiate(priv, priv->scan_vif, IWL_SCAN_NORMAL,
                                        priv->scan_request->channels[0]->band);
                if (err) {
                        IWL_DEBUG_SCAN(priv,
index acf3bf63ee338286a3af92225cdd0849a4828ae0..9d097b9c8005b5e22e264ddcce8c1a5fb30343ce 100644 (file)
@@ -918,7 +918,6 @@ static ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr,
                           char *buf)
 {
        struct mrvl_mesh_defaults defs;
-       int maxlen;
        int ret;
 
        ret = mesh_get_default_parameters(dev, &defs);
@@ -931,13 +930,11 @@ static ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr,
                defs.meshie.val.mesh_id_len = IEEE80211_MAX_SSID_LEN;
        }
 
-       /* SSID not null terminated: reserve room for \0 + \n */
-       maxlen = defs.meshie.val.mesh_id_len + 2;
-       maxlen = (PAGE_SIZE > maxlen) ? maxlen : PAGE_SIZE;
+       memcpy(buf, defs.meshie.val.mesh_id, defs.meshie.val.mesh_id_len);
+       buf[defs.meshie.val.mesh_id_len] = '\n';
+       buf[defs.meshie.val.mesh_id_len + 1] = '\0';
 
-       defs.meshie.val.mesh_id[defs.meshie.val.mesh_id_len] = '\0';
-
-       return snprintf(buf, maxlen, "%s\n", defs.meshie.val.mesh_id);
+       return defs.meshie.val.mesh_id_len + 1;
 }
 
 /**
index 9ba23ede51bd964c26412ad3a79b48b12654c4bb..9cc514703d2a3513ea4a8faedfd9a36c30854eab 100644 (file)
@@ -58,7 +58,6 @@ void wl1251_ps_elp_sleep(struct wl1251 *wl)
        unsigned long delay;
 
        if (wl->psm) {
-               cancel_delayed_work(&wl->elp_work);
                delay = msecs_to_jiffies(ELP_ENTRY_DELAY);
                ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, delay);
        }
@@ -69,6 +68,9 @@ int wl1251_ps_elp_wakeup(struct wl1251 *wl)
        unsigned long timeout, start;
        u32 elp_reg;
 
+       if (delayed_work_pending(&wl->elp_work))
+               cancel_delayed_work(&wl->elp_work);
+
        if (!wl->elp)
                return 0;
 
@@ -102,38 +104,6 @@ int wl1251_ps_elp_wakeup(struct wl1251 *wl)
        return 0;
 }
 
-static int wl1251_ps_set_elp(struct wl1251 *wl, bool enable)
-{
-       int ret;
-
-       if (enable) {
-               wl1251_debug(DEBUG_PSM, "sleep auth psm/elp");
-
-               ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_ELP);
-               if (ret < 0)
-                       return ret;
-
-               wl1251_ps_elp_sleep(wl);
-       } else {
-               wl1251_debug(DEBUG_PSM, "sleep auth cam");
-
-               /*
-                * When the target is in ELP, we can only
-                * access the ELP control register. Thus,
-                * we have to wake the target up before
-                * changing the power authorization.
-                */
-
-               wl1251_ps_elp_wakeup(wl);
-
-               ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_CAM);
-               if (ret < 0)
-                       return ret;
-       }
-
-       return 0;
-}
-
 int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode)
 {
        int ret;
@@ -162,7 +132,7 @@ int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode)
                if (ret < 0)
                        return ret;
 
-               ret = wl1251_ps_set_elp(wl, true);
+               ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_ELP);
                if (ret < 0)
                        return ret;
 
@@ -171,7 +141,8 @@ int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode)
        case STATION_ACTIVE_MODE:
        default:
                wl1251_debug(DEBUG_PSM, "leaving psm");
-               ret = wl1251_ps_set_elp(wl, false);
+
+               ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_CAM);
                if (ret < 0)
                        return ret;
 
index 1ac5786da14b1ecb3ab234ff7e1b86ba92f1a8d0..60f7876b6da8cc3a7d493bbf3b3d40c3c7a902ee 100644 (file)
@@ -1197,6 +1197,10 @@ struct cfg80211_pmksa {
  *     (also see nl80211.h @NL80211_ATTR_WIPHY_ANTENNA_TX).
  *
  * @get_antenna: Get current antenna configuration from device (tx_ant, rx_ant).
+ *
+ * @set_ringparam: Set tx and rx ring sizes.
+ *
+ * @get_ringparam: Get tx and rx ring current and maximum sizes.
  */
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy);
@@ -1364,6 +1368,10 @@ struct cfg80211_ops {
 
        int     (*set_antenna)(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant);
        int     (*get_antenna)(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant);
+
+       int     (*set_ringparam)(struct wiphy *wiphy, u32 tx, u32 rx);
+       void    (*get_ringparam)(struct wiphy *wiphy,
+                                u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max);
 };
 
 /*
index 2b072fa99399c3f3be161170a73861cdee1bb0d4..8650e7bf2ed021c12006f913e1f6c3ffe593bb26 100644 (file)
@@ -1804,6 +1804,10 @@ enum ieee80211_ampdu_mlme_action {
  *     return value is 1, then the @remain_on_channel will be used with a
  *     regular transmission (if supported.)
  * @offchannel_tx_cancel_wait: cancel wait associated with offchannel TX
+ *
+ * @set_ringparam: Set tx and rx ring sizes.
+ *
+ * @get_ringparam: Get tx and rx ring current and maximum sizes.
  */
 struct ieee80211_ops {
        void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb);
@@ -1888,6 +1892,9 @@ struct ieee80211_ops {
                             enum nl80211_channel_type channel_type,
                             unsigned int wait);
        int (*offchannel_tx_cancel_wait)(struct ieee80211_hw *hw);
+       int (*set_ringparam)(struct ieee80211_hw *hw, u32 tx, u32 rx);
+       void (*get_ringparam)(struct ieee80211_hw *hw,
+                             u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max);
 };
 
 /**
index 7b701dcddb50cb308d6b4f7d80b5da908583649f..334213571ad0ef9257c73a7d6ab81febb3ef9c36 100644 (file)
@@ -834,6 +834,10 @@ static int ieee80211_change_station(struct wiphy *wiphy,
 
        rcu_read_unlock();
 
+       if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+           params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))
+               ieee80211_recalc_ps(local, -1);
+
        return 0;
 }
 
@@ -2008,6 +2012,21 @@ static int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant)
        return drv_get_antenna(local, tx_ant, rx_ant);
 }
 
+static int ieee80211_set_ringparam(struct wiphy *wiphy, u32 tx, u32 rx)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+
+       return drv_set_ringparam(local, tx, rx);
+}
+
+static void ieee80211_get_ringparam(struct wiphy *wiphy,
+                                   u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max)
+{
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+
+       drv_get_ringparam(local, tx, tx_max, rx, rx_max);
+}
+
 struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
@@ -2065,4 +2084,6 @@ struct cfg80211_ops mac80211_config_ops = {
        .mgmt_frame_register = ieee80211_mgmt_frame_register,
        .set_antenna = ieee80211_set_antenna,
        .get_antenna = ieee80211_get_antenna,
+       .set_ringparam = ieee80211_set_ringparam,
+       .get_ringparam = ieee80211_get_ringparam,
 };
index 5b24740fc0b0698c65cc1141c981631160a1efc8..889c3e93e0f4a9df2a1a2007799f2a2f13e8a496 100644 (file)
@@ -77,6 +77,9 @@ bool ieee80211_set_channel_type(struct ieee80211_local *local,
                switch (tmp->vif.bss_conf.channel_type) {
                case NL80211_CHAN_NO_HT:
                case NL80211_CHAN_HT20:
+                       if (superchan > tmp->vif.bss_conf.channel_type)
+                               break;
+
                        superchan = tmp->vif.bss_conf.channel_type;
                        break;
                case NL80211_CHAN_HT40PLUS:
index 3729296f6f9580d97500f023afca38ff3004ff9f..9c0d62bb0ea3443d13edad25642ae7375a026293 100644 (file)
@@ -526,4 +526,30 @@ static inline int drv_offchannel_tx_cancel_wait(struct ieee80211_local *local)
        return ret;
 }
 
+static inline int drv_set_ringparam(struct ieee80211_local *local,
+                                   u32 tx, u32 rx)
+{
+       int ret = -ENOTSUPP;
+
+       might_sleep();
+
+       trace_drv_set_ringparam(local, tx, rx);
+       if (local->ops->set_ringparam)
+               ret = local->ops->set_ringparam(&local->hw, tx, rx);
+       trace_drv_return_int(local, ret);
+
+       return ret;
+}
+
+static inline void drv_get_ringparam(struct ieee80211_local *local,
+                                    u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max)
+{
+       might_sleep();
+
+       trace_drv_get_ringparam(local, tx, tx_max, rx, rx_max);
+       if (local->ops->get_ringparam)
+               local->ops->get_ringparam(&local->hw, tx, tx_max, rx, rx_max);
+       trace_drv_return_void(local);
+}
+
 #endif /* __MAC80211_DRIVER_OPS */
index 520fe24448931d4e320d7712ac13a87d61db29e6..45aab80738e272c3b9b1a25c71bb14201b643c14 100644 (file)
@@ -912,6 +912,58 @@ TRACE_EVENT(drv_offchannel_tx,
        )
 );
 
+TRACE_EVENT(drv_set_ringparam,
+       TP_PROTO(struct ieee80211_local *local, u32 tx, u32 rx),
+
+       TP_ARGS(local, tx, rx),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               __field(u32, tx)
+               __field(u32, rx)
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               __entry->tx = tx;
+               __entry->rx = rx;
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT " tx:%d rx %d",
+               LOCAL_PR_ARG, __entry->tx, __entry->rx
+       )
+);
+
+TRACE_EVENT(drv_get_ringparam,
+       TP_PROTO(struct ieee80211_local *local, u32 *tx, u32 *tx_max,
+                u32 *rx, u32 *rx_max),
+
+       TP_ARGS(local, tx, tx_max, rx, rx_max),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               __field(u32, tx)
+               __field(u32, tx_max)
+               __field(u32, rx)
+               __field(u32, rx_max)
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               __entry->tx = *tx;
+               __entry->tx_max = *tx_max;
+               __entry->rx = *rx;
+               __entry->rx_max = *rx_max;
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT " tx:%d tx_max %d rx %d rx_max %d",
+               LOCAL_PR_ARG,
+               __entry->tx, __entry->tx_max, __entry->rx, __entry->rx_max
+       )
+);
+
 DEFINE_EVENT(local_only_evt, drv_offchannel_tx_cancel_wait,
        TP_PROTO(struct ieee80211_local *local),
        TP_ARGS(local)
index cc984bd861cfacd7d463a56928b44666e66155db..64d92d5a7f40e93c001dabf3bc9680d0adfab4e5 100644 (file)
@@ -613,6 +613,37 @@ static void ieee80211_change_ps(struct ieee80211_local *local)
        }
 }
 
+static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_managed *mgd = &sdata->u.mgd;
+       struct sta_info *sta = NULL;
+       u32 sta_flags = 0;
+
+       if (!mgd->powersave)
+               return false;
+
+       if (!mgd->associated)
+               return false;
+
+       if (!mgd->associated->beacon_ies)
+               return false;
+
+       if (mgd->flags & (IEEE80211_STA_BEACON_POLL |
+                         IEEE80211_STA_CONNECTION_POLL))
+               return false;
+
+       rcu_read_lock();
+       sta = sta_info_get(sdata, mgd->bssid);
+       if (sta)
+               sta_flags = get_sta_flags(sta);
+       rcu_read_unlock();
+
+       if (!(sta_flags & WLAN_STA_AUTHORIZED))
+               return false;
+
+       return true;
+}
+
 /* need to hold RTNL or interface lock */
 void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
 {
@@ -647,11 +678,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
                count++;
        }
 
-       if (count == 1 && found->u.mgd.powersave &&
-           found->u.mgd.associated &&
-           found->u.mgd.associated->beacon_ies &&
-           !(found->u.mgd.flags & (IEEE80211_STA_BEACON_POLL |
-                                   IEEE80211_STA_CONNECTION_POLL))) {
+       if (count == 1 && ieee80211_powersave_allowed(found)) {
                struct ieee80211_conf *conf = &local->hw.conf;
                s32 beaconint_us;
 
index bce14fbfc3b65dbccd19fb10d3b5413e66496ee5..8212a8bebf06942fcb121ec0c6bed8c0fc3fbd60 100644 (file)
@@ -598,19 +598,46 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
                sample = true;
                minstrel_ht_set_rate(mp, mi, &ar[0], sample_idx,
                        txrc, true, false);
-               minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_tp_rate,
-                       txrc, false, false);
                info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE;
        } else {
                minstrel_ht_set_rate(mp, mi, &ar[0], mi->max_tp_rate,
                        txrc, false, false);
-               minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_tp_rate2,
-                       txrc, false, true);
        }
-       minstrel_ht_set_rate(mp, mi, &ar[2], mi->max_prob_rate, txrc, false, !sample);
 
-       ar[3].count = 0;
-       ar[3].idx = -1;
+       if (mp->hw->max_rates >= 3) {
+               /*
+                * At least 3 tx rates supported, use
+                * sample_rate -> max_tp_rate -> max_prob_rate for sampling and
+                * max_tp_rate -> max_tp_rate2 -> max_prob_rate by default.
+                */
+               if (sample_idx >= 0)
+                       minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_tp_rate,
+                               txrc, false, false);
+               else
+                       minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_tp_rate2,
+                               txrc, false, true);
+
+               minstrel_ht_set_rate(mp, mi, &ar[2], mi->max_prob_rate,
+                                    txrc, false, !sample);
+
+               ar[3].count = 0;
+               ar[3].idx = -1;
+       } else if (mp->hw->max_rates == 2) {
+               /*
+                * Only 2 tx rates supported, use
+                * sample_rate -> max_prob_rate for sampling and
+                * max_tp_rate -> max_prob_rate by default.
+                */
+               minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_prob_rate,
+                                    txrc, false, !sample);
+
+               ar[2].count = 0;
+               ar[2].idx = -1;
+       } else {
+               /* Not using MRR, only use the first rate */
+               ar[1].count = 0;
+               ar[1].idx = -1;
+       }
 
        mi->total_packets++;
 
index ca4c825be93d263687e1a4635c1352e807ed7c3e..9bde4d1d3e9b184047bf9ca577ba83ec35495178 100644 (file)
@@ -1,5 +1,6 @@
 #include <linux/utsname.h>
 #include <net/cfg80211.h>
+#include "core.h"
 #include "ethtool.h"
 
 static void cfg80211_get_drvinfo(struct net_device *dev,
@@ -37,9 +38,41 @@ static void cfg80211_get_regs(struct net_device *dev, struct ethtool_regs *regs,
        regs->len = 0;
 }
 
+static void cfg80211_get_ringparam(struct net_device *dev,
+                                  struct ethtool_ringparam *rp)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+
+       memset(rp, 0, sizeof(*rp));
+
+       if (rdev->ops->get_ringparam)
+               rdev->ops->get_ringparam(wdev->wiphy,
+                                        &rp->tx_pending, &rp->tx_max_pending,
+                                        &rp->rx_pending, &rp->rx_max_pending);
+}
+
+static int cfg80211_set_ringparam(struct net_device *dev,
+                                 struct ethtool_ringparam *rp)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+
+       if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0)
+               return -EINVAL;
+
+       if (rdev->ops->set_ringparam)
+               return rdev->ops->set_ringparam(wdev->wiphy,
+                                               rp->tx_pending, rp->rx_pending);
+
+       return -ENOTSUPP;
+}
+
 const struct ethtool_ops cfg80211_ethtool_ops = {
        .get_drvinfo = cfg80211_get_drvinfo,
        .get_regs_len = cfg80211_get_regs_len,
        .get_regs = cfg80211_get_regs,
        .get_link = ethtool_op_get_link,
+       .get_ringparam = cfg80211_get_ringparam,
+       .set_ringparam = cfg80211_set_ringparam,
 };