Merge branch 'master' of ssh://master.kernel.org/pub/scm/linux/kernel/git/linville...
authorDavid S. Miller <davem@davemloft.net>
Mon, 15 Feb 2010 01:45:59 +0000 (17:45 -0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 15 Feb 2010 01:45:59 +0000 (17:45 -0800)
79 files changed:
Documentation/DocBook/mac80211.tmpl
drivers/net/wireless/adm8211.c
drivers/net/wireless/ath/ar9170/ar9170.h
drivers/net/wireless/ath/ar9170/main.c
drivers/net/wireless/ath/ath5k/ath5k.h
drivers/net/wireless/ath/ath5k/base.c
drivers/net/wireless/ath/ath5k/base.h
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/beacon.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/regd.c
drivers/net/wireless/b43/b43.h
drivers/net/wireless/b43/dma.c
drivers/net/wireless/b43/dma.h
drivers/net/wireless/b43/main.c
drivers/net/wireless/b43/phy_n.c
drivers/net/wireless/b43/phy_n.h
drivers/net/wireless/b43/pio.c
drivers/net/wireless/b43/pio.h
drivers/net/wireless/b43legacy/dma.c
drivers/net/wireless/b43legacy/dma.h
drivers/net/wireless/b43legacy/main.c
drivers/net/wireless/b43legacy/pio.c
drivers/net/wireless/b43legacy/pio.h
drivers/net/wireless/iwlwifi/iwl-1000.c
drivers/net/wireless/iwlwifi/iwl-5000.c
drivers/net/wireless/iwlwifi/iwl-6000.c
drivers/net/wireless/iwlwifi/iwl-agn.c
drivers/net/wireless/iwlwifi/iwl-core.c
drivers/net/wireless/iwlwifi/iwl-core.h
drivers/net/wireless/iwlwifi/iwl-scan.c
drivers/net/wireless/iwlwifi/iwl3945-base.c
drivers/net/wireless/libertas/cmdresp.c
drivers/net/wireless/libertas/dev.h
drivers/net/wireless/libertas/if_spi.c
drivers/net/wireless/libertas/main.c
drivers/net/wireless/libertas_tf/main.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwl8k.c
drivers/net/wireless/p54/main.c
drivers/net/wireless/p54/p54.h
drivers/net/wireless/p54/txrx.c
drivers/net/wireless/rt2x00/rt2400pci.c
drivers/net/wireless/rt2x00/rt2500pci.c
drivers/net/wireless/rt2x00/rt2500usb.c
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rt2x00/rt61pci.c
drivers/net/wireless/rt2x00/rt73usb.c
drivers/net/wireless/wl12xx/wl1251_main.c
drivers/ssb/driver_mipscore.c
drivers/ssb/main.c
include/net/cfg80211.h
include/net/ieee80211_radiotap.h
include/net/mac80211.h
net/mac80211/agg-tx.c
net/mac80211/cfg.c
net/mac80211/debugfs.c
net/mac80211/driver-ops.h
net/mac80211/driver-trace.h
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/pm.c
net/mac80211/rate.c
net/mac80211/rate.h
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/status.c
net/mac80211/tx.c
net/mac80211/util.c
net/wireless/radiotap.c

index 971d1c0..affb15a 100644 (file)
@@ -234,7 +234,6 @@ usage should require reading the full document.
       <title>Multiple queues and QoS support</title>
       <para>TBD</para>
 !Finclude/net/mac80211.h ieee80211_tx_queue_params
-!Finclude/net/mac80211.h ieee80211_tx_queue_stats
     </chapter>
 
     <chapter id="AP">
index e6ca3eb..547912e 100644 (file)
@@ -302,18 +302,6 @@ static int adm8211_get_stats(struct ieee80211_hw *dev,
        return 0;
 }
 
-static int adm8211_get_tx_stats(struct ieee80211_hw *dev,
-                               struct ieee80211_tx_queue_stats *stats)
-{
-       struct adm8211_priv *priv = dev->priv;
-
-       stats[0].len = priv->cur_tx - priv->dirty_tx;
-       stats[0].limit = priv->tx_ring_size - 2;
-       stats[0].count = priv->dirty_tx;
-
-       return 0;
-}
-
 static void adm8211_interrupt_tci(struct ieee80211_hw *dev)
 {
        struct adm8211_priv *priv = dev->priv;
@@ -1773,7 +1761,6 @@ static const struct ieee80211_ops adm8211_ops = {
        .prepare_multicast      = adm8211_prepare_multicast,
        .configure_filter       = adm8211_configure_filter,
        .get_stats              = adm8211_get_stats,
-       .get_tx_stats           = adm8211_get_tx_stats,
        .get_tsf                = adm8211_get_tsft
 };
 
index b99a8c2..8c8ce67 100644 (file)
@@ -144,6 +144,12 @@ struct ar9170_sta_tid {
        bool active;
 };
 
+struct ar9170_tx_queue_stats {
+       unsigned int len;
+       unsigned int limit;
+       unsigned int count;
+};
+
 #define AR9170_QUEUE_TIMEOUT           64
 #define AR9170_TX_TIMEOUT              8
 #define AR9170_BA_TIMEOUT              4
@@ -211,7 +217,7 @@ struct ar9170 {
 
        /* qos queue settings */
        spinlock_t tx_stats_lock;
-       struct ieee80211_tx_queue_stats tx_stats[5];
+       struct ar9170_tx_queue_stats tx_stats[5];
        struct ieee80211_tx_queue_params edcf[5];
 
        spinlock_t cmdlock;
index 4d27f7f..91797cb 100644 (file)
@@ -2396,18 +2396,6 @@ static int ar9170_get_stats(struct ieee80211_hw *hw,
        return 0;
 }
 
-static int ar9170_get_tx_stats(struct ieee80211_hw *hw,
-                              struct ieee80211_tx_queue_stats *tx_stats)
-{
-       struct ar9170 *ar = hw->priv;
-
-       spin_lock_bh(&ar->tx_stats_lock);
-       memcpy(tx_stats, ar->tx_stats, sizeof(tx_stats[0]) * hw->queues);
-       spin_unlock_bh(&ar->tx_stats_lock);
-
-       return 0;
-}
-
 static int ar9170_conf_tx(struct ieee80211_hw *hw, u16 queue,
                          const struct ieee80211_tx_queue_params *param)
 {
@@ -2509,7 +2497,6 @@ static const struct ieee80211_ops ar9170_ops = {
        .set_key                = ar9170_set_key,
        .sta_notify             = ar9170_sta_notify,
        .get_stats              = ar9170_get_stats,
-       .get_tx_stats           = ar9170_get_tx_stats,
        .ampdu_action           = ar9170_ampdu_action,
 };
 
index ad4d446..ac67f02 100644 (file)
@@ -541,7 +541,6 @@ struct ath5k_txq_info {
 /*
  * Transmit packet types.
  * used on tx control descriptor
- * TODO: Use them inside base.c corectly
  */
 enum ath5k_pkt_type {
        AR5K_PKT_TYPE_NORMAL            = 0,
index edb6c90..8dce007 100644 (file)
@@ -241,8 +241,6 @@ static int ath5k_set_key(struct ieee80211_hw *hw,
                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);
@@ -269,7 +267,6 @@ static const struct ieee80211_ops ath5k_hw_ops = {
        .set_key        = ath5k_set_key,
        .get_stats      = ath5k_get_stats,
        .conf_tx        = NULL,
-       .get_tx_stats   = ath5k_get_tx_stats,
        .get_tsf        = ath5k_get_tsf,
        .set_tsf        = ath5k_set_tsf,
        .reset_tsf      = ath5k_reset_tsf,
@@ -1249,6 +1246,29 @@ ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
        return 0;
 }
 
+static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr;
+       enum ath5k_pkt_type htype;
+       __le16 fc;
+
+       hdr = (struct ieee80211_hdr *)skb->data;
+       fc = hdr->frame_control;
+
+       if (ieee80211_is_beacon(fc))
+               htype = AR5K_PKT_TYPE_BEACON;
+       else if (ieee80211_is_probe_resp(fc))
+               htype = AR5K_PKT_TYPE_PROBE_RESP;
+       else if (ieee80211_is_atim(fc))
+               htype = AR5K_PKT_TYPE_ATIM;
+       else if (ieee80211_is_pspoll(fc))
+               htype = AR5K_PKT_TYPE_PSPOLL;
+       else
+               htype = AR5K_PKT_TYPE_NORMAL;
+
+       return htype;
+}
+
 static int
 ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
                  struct ath5k_txq *txq)
@@ -1303,7 +1323,8 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
                        sc->vif, pktlen, info));
        }
        ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
-               ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL,
+               ieee80211_get_hdrlen_from_skb(skb),
+               get_hw_packet_type(skb),
                (sc->power_level * 2),
                hw_rate,
                info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags,
@@ -1332,7 +1353,6 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf,
 
        spin_lock_bh(&txq->lock);
        list_add_tail(&bf->list, &txq->q);
-       sc->tx_stats[txq->qnum].len++;
        if (txq->link == NULL) /* is this first packet? */
                ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr);
        else /* no, so only link it */
@@ -1581,7 +1601,6 @@ ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq)
                ath5k_txbuf_free(sc, bf);
 
                spin_lock_bh(&sc->txbuflock);
-               sc->tx_stats[txq->qnum].len--;
                list_move_tail(&bf->list, &sc->txbuf);
                sc->txbuf_len++;
                spin_unlock_bh(&sc->txbuflock);
@@ -2011,10 +2030,8 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
                }
 
                ieee80211_tx_status(sc->hw, skb);
-               sc->tx_stats[txq->qnum].count++;
 
                spin_lock(&sc->txbuflock);
-               sc->tx_stats[txq->qnum].len--;
                list_move_tail(&bf->list, &sc->txbuf);
                sc->txbuf_len++;
                spin_unlock(&sc->txbuflock);
@@ -3116,17 +3133,6 @@ ath5k_get_stats(struct ieee80211_hw *hw,
        return 0;
 }
 
-static int
-ath5k_get_tx_stats(struct ieee80211_hw *hw,
-               struct ieee80211_tx_queue_stats *stats)
-{
-       struct ath5k_softc *sc = hw->priv;
-
-       memcpy(stats, &sc->tx_stats, sizeof(sc->tx_stats));
-
-       return 0;
-}
-
 static u64
 ath5k_get_tsf(struct ieee80211_hw *hw)
 {
index 952b3a2..7e1a88a 100644 (file)
@@ -117,7 +117,6 @@ struct ath5k_softc {
        struct pci_dev          *pdev;          /* for dma mapping */
        void __iomem            *iobase;        /* address of the device */
        struct mutex            lock;           /* dev-level lock */
-       struct ieee80211_tx_queue_stats tx_stats[AR5K_NUM_TX_QUEUES];
        struct ieee80211_low_level_stats ll_stats;
        struct ieee80211_hw     *hw;            /* IEEE 802.11 common */
        struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
index 0ea340f..83c7ea4 100644 (file)
@@ -267,6 +267,7 @@ void ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
                       u16 tid, u16 *ssn);
 void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
 void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
+void ath9k_enable_ps(struct ath_softc *sc);
 
 /********/
 /* VIFs */
index 422454f..d088ebf 100644 (file)
@@ -577,6 +577,13 @@ static void ath_beacon_config_sta(struct ath_softc *sc,
        u64 tsf;
        int num_beacons, offset, dtim_dec_count, cfp_dec_count;
 
+       /* No need to configure beacon if we are not associated */
+       if (!common->curaid) {
+               ath_print(common, ATH_DBG_BEACON,
+                        "STA is not yet associated..skipping beacon config\n");
+               return;
+       }
+
        memset(&bs, 0, sizeof(bs));
        intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
 
@@ -739,7 +746,6 @@ void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
        enum nl80211_iftype iftype;
 
        /* Setup the beacon configuration parameters */
-
        if (vif) {
                struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
 
index f15fee7..f00f5c7 100644 (file)
@@ -1217,6 +1217,17 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
        /* As defined by IEEE 802.11-2007 17.3.8.6 */
        slottime = ah->slottime + 3 * ah->coverage_class;
        acktimeout = slottime + sifstime;
+
+       /*
+        * Workaround for early ACK timeouts, add an offset to match the
+        * initval's 64us ack timeout value.
+        * This was initially only meant to work around an issue with delayed
+        * BA frames in some implementations, but it has been found to fix ACK
+        * timeout issues in other cases as well.
+        */
+       if (conf->channel && conf->channel->band == IEEE80211_BAND_2GHZ)
+               acktimeout += 64 - sifstime - ah->slottime;
+
        ath9k_hw_setslottime(ah, slottime);
        ath9k_hw_set_ack_timeout(ah, acktimeout);
        ath9k_hw_set_cts_timeout(ah, acktimeout);
index 4b5e548..623c2f8 100644 (file)
@@ -622,7 +622,8 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
                IEEE80211_HW_SIGNAL_DBM |
                IEEE80211_HW_SUPPORTS_PS |
                IEEE80211_HW_PS_NULLFUNC_STACK |
-               IEEE80211_HW_SPECTRUM_MGMT;
+               IEEE80211_HW_SPECTRUM_MGMT |
+               IEEE80211_HW_REPORTS_TX_ACK_STATUS;
 
        if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT)
                 hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
index 6796d5c..9c8f925 100644 (file)
@@ -809,6 +809,7 @@ static void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf
 
        clear_bit(key->hw_key_idx + 64, common->keymap);
        if (common->splitmic) {
+               ath9k_hw_keyreset(ah, key->hw_key_idx + 32);
                clear_bit(key->hw_key_idx + 32, common->keymap);
                clear_bit(key->hw_key_idx + 64 + 32, common->keymap);
        }
@@ -1492,6 +1493,19 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
        mutex_unlock(&sc->mutex);
 }
 
+void ath9k_enable_ps(struct ath_softc *sc)
+{
+       sc->ps_enabled = true;
+       if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
+               if ((sc->imask & ATH9K_INT_TIM_TIMER) == 0) {
+                       sc->imask |= ATH9K_INT_TIM_TIMER;
+                       ath9k_hw_set_interrupts(sc->sc_ah,
+                                       sc->imask);
+               }
+       }
+       ath9k_hw_setrxabort(sc->sc_ah, 1);
+}
+
 static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
 {
        struct ath_wiphy *aphy = hw->priv;
@@ -1546,22 +1560,13 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
        if (changed & IEEE80211_CONF_CHANGE_PS) {
                if (conf->flags & IEEE80211_CONF_PS) {
                        sc->ps_flags |= PS_ENABLED;
-                       if (!(ah->caps.hw_caps &
-                             ATH9K_HW_CAP_AUTOSLEEP)) {
-                               if ((sc->imask & ATH9K_INT_TIM_TIMER) == 0) {
-                                       sc->imask |= ATH9K_INT_TIM_TIMER;
-                                       ath9k_hw_set_interrupts(sc->sc_ah,
-                                                       sc->imask);
-                               }
-                       }
                        /*
                         * At this point we know hardware has received an ACK
                         * of a previously sent null data frame.
                         */
                        if ((sc->ps_flags & PS_NULLFUNC_COMPLETED)) {
                                sc->ps_flags &= ~PS_NULLFUNC_COMPLETED;
-                               sc->ps_enabled = true;
-                               ath9k_hw_setrxabort(sc->sc_ah, 1);
+                               ath9k_enable_ps(sc);
                         }
                } else {
                        sc->ps_enabled = false;
index 3c790a4..f5cbbcb 100644 (file)
@@ -2048,10 +2048,9 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
                 */
                if (bf->bf_isnullfunc &&
                    (ds->ds_txstat.ts_status & ATH9K_TX_ACKED)) {
-                       if ((sc->ps_flags & PS_ENABLED)) {
-                               sc->ps_enabled = true;
-                               ath9k_hw_setrxabort(sc->sc_ah, 1);
-                       } else
+                       if ((sc->ps_flags & PS_ENABLED))
+                               ath9k_enable_ps(sc);
+                       else
                                sc->ps_flags |= PS_NULLFUNC_COMPLETED;
                }
 
index 039ac49..04abd1f 100644 (file)
@@ -110,8 +110,9 @@ static const struct ieee80211_regdomain ath_world_regdom_67_68_6A = {
 
 static inline bool is_wwr_sku(u16 regd)
 {
-       return ((regd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) ||
-               (regd == WORLD);
+       return ((regd & COUNTRY_ERD_FLAG) != COUNTRY_ERD_FLAG) &&
+               (((regd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) ||
+               (regd == WORLD));
 }
 
 static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg)
index 54d6085..6a6ab0f 100644 (file)
 #define B43_MMIO_TSF_2                 0x636   /* core rev < 3 only */
 #define B43_MMIO_TSF_3                 0x638   /* core rev < 3 only */
 #define B43_MMIO_RNG                   0x65A
+#define B43_MMIO_IFSSLOT               0x684   /* Interframe slot time */
 #define B43_MMIO_IFSCTL                        0x688 /* Interframe space control */
 #define  B43_MMIO_IFSCTL_USE_EDCF      0x0004
 #define B43_MMIO_POWERUP_DELAY         0x6A8
index 615af22..be7abf8 100644 (file)
@@ -1369,7 +1369,6 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
                b43err(dev->wl, "DMA tx mapping failure\n");
                goto out;
        }
-       ring->nr_tx_packets++;
        if ((free_slots(ring) < TX_SLOTS_PER_FRAME) ||
            should_inject_overflow(ring)) {
                /* This TX ring is full. */
@@ -1500,22 +1499,6 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
        }
 }
 
-void b43_dma_get_tx_stats(struct b43_wldev *dev,
-                         struct ieee80211_tx_queue_stats *stats)
-{
-       const int nr_queues = dev->wl->hw->queues;
-       struct b43_dmaring *ring;
-       int i;
-
-       for (i = 0; i < nr_queues; i++) {
-               ring = select_ring_by_priority(dev, i);
-
-               stats[i].len = ring->used_slots / TX_SLOTS_PER_FRAME;
-               stats[i].limit = ring->nr_slots / TX_SLOTS_PER_FRAME;
-               stats[i].count = ring->nr_tx_packets;
-       }
-}
-
 static void dma_rx(struct b43_dmaring *ring, int *slot)
 {
        const struct b43_dma_ops *ops = ring->ops;
index f7ab37c..dc91944 100644 (file)
@@ -228,8 +228,6 @@ struct b43_dmaring {
        int used_slots;
        /* Currently used slot in the ring. */
        int current_slot;
-       /* Total number of packets sent. Statistics only. */
-       unsigned int nr_tx_packets;
        /* Frameoffset in octets. */
        u32 frameoffset;
        /* Descriptor buffer size. */
@@ -278,9 +276,6 @@ void b43_dma_free(struct b43_wldev *dev);
 void b43_dma_tx_suspend(struct b43_wldev *dev);
 void b43_dma_tx_resume(struct b43_wldev *dev);
 
-void b43_dma_get_tx_stats(struct b43_wldev *dev,
-                         struct ieee80211_tx_queue_stats *stats);
-
 int b43_dma_tx(struct b43_wldev *dev,
               struct sk_buff *skb);
 void b43_dma_handle_txstatus(struct b43_wldev *dev,
index 316a913..aa33d74 100644 (file)
@@ -637,10 +637,17 @@ static void b43_upload_card_macaddress(struct b43_wldev *dev)
 static void b43_set_slot_time(struct b43_wldev *dev, u16 slot_time)
 {
        /* slot_time is in usec. */
-       if (dev->phy.type != B43_PHYTYPE_G)
+       /* This test used to exit for all but a G PHY. */
+       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
                return;
-       b43_write16(dev, 0x684, 510 + slot_time);
-       b43_shm_write16(dev, B43_SHM_SHARED, 0x0010, slot_time);
+       b43_write16(dev, B43_MMIO_IFSSLOT, 510 + slot_time);
+       /* Shared memory location 0x0010 is the slot time and should be
+        * set to slot_time; however, this register is initially 0 and changing
+        * the value adversely affects the transmit rate for BCM4311
+        * devices. Until this behavior is unterstood, delete this step
+        *
+        * b43_shm_write16(dev, B43_SHM_SHARED, 0x0010, slot_time);
+        */
 }
 
 static void b43_short_slot_timing_enable(struct b43_wldev *dev)
@@ -3349,27 +3356,6 @@ out_unlock:
        return err;
 }
 
-static int b43_op_get_tx_stats(struct ieee80211_hw *hw,
-                              struct ieee80211_tx_queue_stats *stats)
-{
-       struct b43_wl *wl = hw_to_b43_wl(hw);
-       struct b43_wldev *dev;
-       int err = -ENODEV;
-
-       mutex_lock(&wl->mutex);
-       dev = wl->current_dev;
-       if (dev && b43_status(dev) >= B43_STAT_STARTED) {
-               if (b43_using_pio_transfers(dev))
-                       b43_pio_get_tx_stats(dev, stats);
-               else
-                       b43_dma_get_tx_stats(dev, stats);
-               err = 0;
-       }
-       mutex_unlock(&wl->mutex);
-
-       return err;
-}
-
 static int b43_op_get_stats(struct ieee80211_hw *hw,
                            struct ieee80211_low_level_stats *stats)
 {
@@ -3980,6 +3966,7 @@ static int b43_wireless_core_start(struct b43_wldev *dev)
        }
 
        /* We are ready to run. */
+       ieee80211_wake_queues(dev->wl->hw);
        b43_set_status(dev, B43_STAT_STARTED);
 
        /* Start data flow (TX/RX). */
@@ -4389,8 +4376,6 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
 
        ieee80211_wake_queues(dev->wl->hw);
 
-       ieee80211_wake_queues(dev->wl->hw);
-
        b43_set_status(dev, B43_STAT_INITIALIZED);
 
 out:
@@ -4596,7 +4581,6 @@ static const struct ieee80211_ops b43_hw_ops = {
        .set_key                = b43_op_set_key,
        .update_tkip_key        = b43_op_update_tkip_key,
        .get_stats              = b43_op_get_stats,
-       .get_tx_stats           = b43_op_get_tx_stats,
        .get_tsf                = b43_op_get_tsf,
        .set_tsf                = b43_op_set_tsf,
        .start                  = b43_op_start,
index 6392da2..795bb1e 100644 (file)
@@ -68,6 +68,10 @@ static void b43_nphy_set_rf_sequence(struct b43_wldev *dev, u8 cmd,
                                        u8 *events, u8 *delays, u8 length);
 static void b43_nphy_force_rf_sequence(struct b43_wldev *dev,
                                       enum b43_nphy_rf_sequence seq);
+static void b43_nphy_rf_control_override(struct b43_wldev *dev, u16 field,
+                                               u16 value, u8 core, bool off);
+static void b43_nphy_rf_control_intc_override(struct b43_wldev *dev, u8 field,
+                                               u16 value, u8 core);
 
 void b43_nphy_set_rxantenna(struct b43_wldev *dev, int antenna)
 {//TODO
@@ -498,8 +502,8 @@ static void b43_nphy_rx_cal_phy_setup(struct b43_wldev *dev, u8 core)
                b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x0007);
        }
 
-       /* TODO: Call N PHY RF Ctrl Intc Override with 2, 0, 3 as arguments */
-       /* TODO: Call N PHY RF Intc Override with 8, 0, 3, 0 as arguments */
+       b43_nphy_rf_control_intc_override(dev, 2, 0, 3);
+       b43_nphy_rf_control_override(dev, 8, 0, 3, false);
        b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX);
 
        if (core == 0) {
@@ -509,9 +513,8 @@ static void b43_nphy_rx_cal_phy_setup(struct b43_wldev *dev, u8 core)
                rxval = 4;
                txval = 2;
        }
-
-       /* TODO: Call N PHY RF Ctrl Intc Override with 1, rxval, (core + 1) */
-       /* TODO: Call N PHY RF Ctrl Intc Override with 1, txval, (2 - core) */
+       b43_nphy_rf_control_intc_override(dev, 1, rxval, (core + 1));
+       b43_nphy_rf_control_intc_override(dev, 1, txval, (2 - core));
 }
 
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalcRxIqComp */
@@ -714,6 +717,67 @@ static void b43_nphy_stop_playback(struct b43_wldev *dev)
                b43_nphy_stay_in_carrier_search(dev, 0);
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SpurWar */
+static void b43_nphy_spur_workaround(struct b43_wldev *dev)
+{
+       struct b43_phy_n *nphy = dev->phy.n;
+
+       unsigned int channel;
+       int tone[2] = { 57, 58 };
+       u32 noise[2] = { 0x3FF, 0x3FF };
+
+       B43_WARN_ON(dev->phy.rev < 3);
+
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, 1);
+
+       /* FIXME: channel = radio_chanspec */
+
+       if (nphy->gband_spurwar_en) {
+               /* TODO: N PHY Adjust Analog Pfbw (7) */
+               if (channel == 11 && dev->phy.is_40mhz)
+                       ; /* TODO: N PHY Adjust Min Noise Var(2, tone, noise)*/
+               else
+                       ; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/
+               /* TODO: N PHY Adjust CRS Min Power (0x1E) */
+       }
+
+       if (nphy->aband_spurwar_en) {
+               if (channel == 54) {
+                       tone[0] = 0x20;
+                       noise[0] = 0x25F;
+               } else if (channel == 38 || channel == 102 || channel == 118) {
+                       if (0 /* FIXME */) {
+                               tone[0] = 0x20;
+                               noise[0] = 0x21F;
+                       } else {
+                               tone[0] = 0;
+                               noise[0] = 0;
+                       }
+               } else if (channel == 134) {
+                       tone[0] = 0x20;
+                       noise[0] = 0x21F;
+               } else if (channel == 151) {
+                       tone[0] = 0x10;
+                       noise[0] = 0x23F;
+               } else if (channel == 153 || channel == 161) {
+                       tone[0] = 0x30;
+                       noise[0] = 0x23F;
+               } else {
+                       tone[0] = 0;
+                       noise[0] = 0;
+               }
+
+               if (!tone[0] && !noise[0])
+                       ; /* TODO: N PHY Adjust Min Noise Var(1, tone, noise)*/
+               else
+                       ; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/
+       }
+
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, 0);
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/WorkaroundsGainCtrl */
 static void b43_nphy_gain_crtl_workarounds(struct b43_wldev *dev)
 {
@@ -953,6 +1017,33 @@ static void b43_nphy_workarounds(struct b43_wldev *dev)
                b43_nphy_stay_in_carrier_search(dev, 0);
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/LoadSampleTable */
+static int b43_nphy_load_samples(struct b43_wldev *dev,
+                                       struct b43_c32 *samples, u16 len) {
+       struct b43_phy_n *nphy = dev->phy.n;
+       u16 i;
+       u32 *data;
+
+       data = kzalloc(len * sizeof(u32), GFP_KERNEL);
+       if (!data) {
+               b43err(dev->wl, "allocation for samples loading failed\n");
+               return -ENOMEM;
+       }
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, 1);
+
+       for (i = 0; i < len; i++) {
+               data[i] = (samples[i].i & 0x3FF << 10);
+               data[i] |= samples[i].q & 0x3FF;
+       }
+       b43_ntab_write_bulk(dev, B43_NTAB32(17, 0), len, data);
+
+       kfree(data);
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, 0);
+       return 0;
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GenLoadSamples */
 static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
                                        bool test)
@@ -978,6 +1069,10 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
        }
 
        samples = kzalloc(len * sizeof(struct b43_c32), GFP_KERNEL);
+       if (!samples) {
+               b43err(dev->wl, "allocation for samples generation failed\n");
+               return 0;
+       }
        rot = (((freq * 36) / bw) << 16) / 100;
        angle = 0;
 
@@ -988,9 +1083,9 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
                samples[i].i = CORDIC_CONVERT(samples[i].i * max);
        }
 
-       /* TODO: Call N PHY Load Sample Table with buffer, len as arguments */
+       i = b43_nphy_load_samples(dev, samples, len);
        kfree(samples);
-       return len;
+       return (i < 0) ? 0 : len;
 }
 
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RunSamples */
@@ -1264,6 +1359,104 @@ static void b43_nphy_rf_control_override(struct b43_wldev *dev, u16 field,
        }
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlIntcOverride */
+static void b43_nphy_rf_control_intc_override(struct b43_wldev *dev, u8 field,
+                                               u16 value, u8 core)
+{
+       u8 i, j;
+       u16 reg, tmp, val;
+
+       B43_WARN_ON(dev->phy.rev < 3);
+       B43_WARN_ON(field > 4);
+
+       for (i = 0; i < 2; i++) {
+               if ((core == 1 && i == 1) || (core == 2 && !i))
+                       continue;
+
+               reg = (i == 0) ?
+                       B43_NPHY_RFCTL_INTC1 : B43_NPHY_RFCTL_INTC2;
+               b43_phy_mask(dev, reg, 0xFBFF);
+
+               switch (field) {
+               case 0:
+                       b43_phy_write(dev, reg, 0);
+                       b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX);
+                       break;
+               case 1:
+                       if (!i) {
+                               b43_phy_maskset(dev, B43_NPHY_RFCTL_INTC1,
+                                               0xFC3F, (value << 6));
+                               b43_phy_maskset(dev, B43_NPHY_TXF_40CO_B1S1,
+                                               0xFFFE, 1);
+                               b43_phy_set(dev, B43_NPHY_RFCTL_CMD,
+                                               B43_NPHY_RFCTL_CMD_START);
+                               for (j = 0; j < 100; j++) {
+                                       if (b43_phy_read(dev, B43_NPHY_RFCTL_CMD) & B43_NPHY_RFCTL_CMD_START) {
+                                               j = 0;
+                                               break;
+                                       }
+                                       udelay(10);
+                               }
+                               if (j)
+                                       b43err(dev->wl,
+                                               "intc override timeout\n");
+                               b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S1,
+                                               0xFFFE);
+                       } else {
+                               b43_phy_maskset(dev, B43_NPHY_RFCTL_INTC2,
+                                               0xFC3F, (value << 6));
+                               b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER,
+                                               0xFFFE, 1);
+                               b43_phy_set(dev, B43_NPHY_RFCTL_CMD,
+                                               B43_NPHY_RFCTL_CMD_RXTX);
+                               for (j = 0; j < 100; j++) {
+                                       if (b43_phy_read(dev, B43_NPHY_RFCTL_CMD) & B43_NPHY_RFCTL_CMD_RXTX) {
+                                               j = 0;
+                                               break;
+                                       }
+                                       udelay(10);
+                               }
+                               if (j)
+                                       b43err(dev->wl,
+                                               "intc override timeout\n");
+                               b43_phy_mask(dev, B43_NPHY_RFCTL_OVER,
+                                               0xFFFE);
+                       }
+                       break;
+               case 2:
+                       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                               tmp = 0x0020;
+                               val = value << 5;
+                       } else {
+                               tmp = 0x0010;
+                               val = value << 4;
+                       }
+                       b43_phy_maskset(dev, reg, ~tmp, val);
+                       break;
+               case 3:
+                       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                               tmp = 0x0001;
+                               val = value;
+                       } else {
+                               tmp = 0x0004;
+                               val = value << 2;
+                       }
+                       b43_phy_maskset(dev, reg, ~tmp, val);
+                       break;
+               case 4:
+                       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                               tmp = 0x0002;
+                               val = value << 1;
+                       } else {
+                               tmp = 0x0008;
+                               val = value << 3;
+                       }
+                       b43_phy_maskset(dev, reg, ~tmp, val);
+                       break;
+               }
+       }
+}
+
 static void b43_nphy_bphy_init(struct b43_wldev *dev)
 {
        unsigned int i;
@@ -2161,9 +2354,9 @@ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev)
                regs[7] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1);
                regs[8] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2);
 
-               /* TODO: Call N PHY RF Ctrl Intc Override with 2, 1, 3 */
-               /* TODO: Call N PHY RF Ctrl Intc Override with 1, 2, 1 */
-               /* TODO: Call N PHY RF Ctrl Intc Override with 1, 8, 2 */
+               b43_nphy_rf_control_intc_override(dev, 2, 1, 3);
+               b43_nphy_rf_control_intc_override(dev, 1, 2, 1);
+               b43_nphy_rf_control_intc_override(dev, 1, 8, 2);
 
                regs[9] = b43_phy_read(dev, B43_NPHY_PAPD_EN0);
                regs[10] = b43_phy_read(dev, B43_NPHY_PAPD_EN1);
@@ -2194,6 +2387,55 @@ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev)
        }
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SaveCal */
+static void b43_nphy_save_cal(struct b43_wldev *dev)
+{
+       struct b43_phy_n *nphy = dev->phy.n;
+
+       struct b43_phy_n_iq_comp *rxcal_coeffs = NULL;
+       u16 *txcal_radio_regs = NULL;
+       u8 *iqcal_chanspec;
+       u16 *table = NULL;
+
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, 1);
+
+       if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+               rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_2G;
+               txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_2G;
+               iqcal_chanspec = &nphy->iqcal_chanspec_2G;
+               table = nphy->cal_cache.txcal_coeffs_2G;
+       } else {
+               rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_5G;
+               txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_5G;
+               iqcal_chanspec = &nphy->iqcal_chanspec_5G;
+               table = nphy->cal_cache.txcal_coeffs_5G;
+       }
+
+       b43_nphy_rx_iq_coeffs(dev, false, rxcal_coeffs);
+       /* TODO use some definitions */
+       if (dev->phy.rev >= 3) {
+               txcal_radio_regs[0] = b43_radio_read(dev, 0x2021);
+               txcal_radio_regs[1] = b43_radio_read(dev, 0x2022);
+               txcal_radio_regs[2] = b43_radio_read(dev, 0x3021);
+               txcal_radio_regs[3] = b43_radio_read(dev, 0x3022);
+               txcal_radio_regs[4] = b43_radio_read(dev, 0x2023);
+               txcal_radio_regs[5] = b43_radio_read(dev, 0x2024);
+               txcal_radio_regs[6] = b43_radio_read(dev, 0x3023);
+               txcal_radio_regs[7] = b43_radio_read(dev, 0x3024);
+       } else {
+               txcal_radio_regs[0] = b43_radio_read(dev, 0x8B);
+               txcal_radio_regs[1] = b43_radio_read(dev, 0xBA);
+               txcal_radio_regs[2] = b43_radio_read(dev, 0x8D);
+               txcal_radio_regs[3] = b43_radio_read(dev, 0xBC);
+       }
+       *iqcal_chanspec = nphy->radio_chanspec;
+       b43_ntab_write_bulk(dev, B43_NTAB16(15, 80), 8, table);
+
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, 0);
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreCal */
 static void b43_nphy_restore_cal(struct b43_wldev *dev)
 {
@@ -2486,6 +2728,39 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
        return error;
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ReapplyTxCalCoeffs */
+static void b43_nphy_reapply_tx_cal_coeffs(struct b43_wldev *dev)
+{
+       struct b43_phy_n *nphy = dev->phy.n;
+       u8 i;
+       u16 buffer[7];
+       bool equal = true;
+
+       if (!nphy->txiqlocal_coeffsvalid || 1 /* FIXME */)
+               return;
+
+       b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer);
+       for (i = 0; i < 4; i++) {
+               if (buffer[i] != nphy->txiqlocal_bestc[i]) {
+                       equal = false;
+                       break;
+               }
+       }
+
+       if (!equal) {
+               b43_ntab_write_bulk(dev, B43_NTAB16(15, 80), 4,
+                                       nphy->txiqlocal_bestc);
+               for (i = 0; i < 4; i++)
+                       buffer[i] = 0;
+               b43_ntab_write_bulk(dev, B43_NTAB16(15, 88), 4,
+                                       buffer);
+               b43_ntab_write_bulk(dev, B43_NTAB16(15, 85), 2,
+                                       &nphy->txiqlocal_bestc[5]);
+               b43_ntab_write_bulk(dev, B43_NTAB16(15, 93), 2,
+                                       &nphy->txiqlocal_bestc[5]);
+       }
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalRxIqRev2 */
 static int b43_nphy_rev2_cal_rx_iq(struct b43_wldev *dev,
                        struct nphy_txgains target, u8 type, bool debug)
@@ -2516,7 +2791,7 @@ static int b43_nphy_rev2_cal_rx_iq(struct b43_wldev *dev,
        b43_nphy_stay_in_carrier_search(dev, 1);
 
        if (dev->phy.rev < 2)
-               ;/* TODO: Call N PHY Reapply TX Cal Coeffs */
+               b43_nphy_reapply_tx_cal_coeffs(dev);
        b43_ntab_read_bulk(dev, B43_NTAB16(7, 0x110), 2, gain_save);
        for (i = 0; i < 2; i++) {
                b43_nphy_iq_cal_gain_params(dev, i, target, &cal_params[i]);
@@ -2858,7 +3133,7 @@ int b43_phy_initn(struct b43_wldev *dev)
 
        if (!b43_nphy_cal_tx_iq_lo(dev, target, true, false)) {
                if (b43_nphy_cal_rx_iq(dev, target, 2, 0) == 0)
-                       ;/* Call N PHY Save Cal */
+                       b43_nphy_save_cal(dev);
                else if (nphy->mphase_cal_phase_id == 0)
                        ;/* N PHY Periodic Calibration with argument 3 */
        } else {
@@ -2872,7 +3147,8 @@ int b43_phy_initn(struct b43_wldev *dev)
        if (phy->rev >= 3 && phy->rev <= 6)
                b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x0014);
        b43_nphy_tx_lp_fbw(dev);
-       /* TODO N PHY Spur Workaround */
+       if (phy->rev >= 3)
+               b43_nphy_spur_workaround(dev);
 
        b43err(dev->wl, "IEEE 802.11n devices are not supported, yet.\n");
        return 0;
index ae82f0f..403aad3 100644 (file)
@@ -975,6 +975,7 @@ struct b43_phy_n {
        u16 papd_epsilon_offset[2];
        s32 preamble_override;
        u32 bb_mult_save;
+       u16 radio_chanspec;
 
        bool gain_boost;
        bool elna_gain_config;
@@ -1001,6 +1002,9 @@ struct b43_phy_n {
        u16 classifier_state;
        u16 clip_state[2];
 
+       bool aband_spurwar_en;
+       bool gband_spurwar_en;
+
        bool ipa2g_on;
        u8 iqcal_chanspec_2G;
        u8 rssical_chanspec_2G;
index c01b8e0..a6062c3 100644 (file)
@@ -559,7 +559,6 @@ int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb)
                b43err(dev->wl, "PIO transmission failure\n");
                goto out;
        }
-       q->nr_tx_packets++;
 
        B43_WARN_ON(q->buffer_used > q->buffer_size);
        if (((q->buffer_size - q->buffer_used) < roundup(2 + 2 + 6, 4)) ||
@@ -605,22 +604,6 @@ void b43_pio_handle_txstatus(struct b43_wldev *dev,
        }
 }
 
-void b43_pio_get_tx_stats(struct b43_wldev *dev,
-                         struct ieee80211_tx_queue_stats *stats)
-{
-       const int nr_queues = dev->wl->hw->queues;
-       struct b43_pio_txqueue *q;
-       int i;
-
-       for (i = 0; i < nr_queues; i++) {
-               q = select_queue_by_priority(dev, i);
-
-               stats[i].len = B43_PIO_MAX_NR_TXPACKETS - q->free_packet_slots;
-               stats[i].limit = B43_PIO_MAX_NR_TXPACKETS;
-               stats[i].count = q->nr_tx_packets;
-       }
-}
-
 /* Returns whether we should fetch another frame. */
 static bool pio_rx_frame(struct b43_pio_rxqueue *q)
 {
index 7b3c42f..1e51614 100644 (file)
@@ -90,9 +90,6 @@ struct b43_pio_txqueue {
        struct b43_pio_txpacket packets[B43_PIO_MAX_NR_TXPACKETS];
        struct list_head packets_list;
 
-       /* Total number of transmitted packets. */
-       unsigned int nr_tx_packets;
-
        /* Shortcut to the 802.11 core revision. This is to
         * avoid horrible pointer dereferencing in the fastpaths. */
        u8 rev;
@@ -160,8 +157,6 @@ void b43_pio_free(struct b43_wldev *dev);
 int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb);
 void b43_pio_handle_txstatus(struct b43_wldev *dev,
                             const struct b43_txstatus *status);
-void b43_pio_get_tx_stats(struct b43_wldev *dev,
-                         struct ieee80211_tx_queue_stats *stats);
 void b43_pio_rx(struct b43_pio_rxqueue *q);
 
 void b43_pio_tx_suspend(struct b43_wldev *dev);
index 0a86bdf..8b9387c 100644 (file)
@@ -1411,7 +1411,6 @@ int b43legacy_dma_tx(struct b43legacy_wldev *dev,
                b43legacyerr(dev->wl, "DMA tx mapping failure\n");
                goto out_unlock;
        }
-       ring->nr_tx_packets++;
        if ((free_slots(ring) < SLOTS_PER_PACKET) ||
            should_inject_overflow(ring)) {
                /* This TX ring is full. */
@@ -1527,25 +1526,6 @@ void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev,
        spin_unlock(&ring->lock);
 }
 
-void b43legacy_dma_get_tx_stats(struct b43legacy_wldev *dev,
-                             struct ieee80211_tx_queue_stats *stats)
-{
-       const int nr_queues = dev->wl->hw->queues;
-       struct b43legacy_dmaring *ring;
-       unsigned long flags;
-       int i;
-
-       for (i = 0; i < nr_queues; i++) {
-               ring = priority_to_txring(dev, i);
-
-               spin_lock_irqsave(&ring->lock, flags);
-               stats[i].len = ring->used_slots / SLOTS_PER_PACKET;
-               stats[i].limit = ring->nr_slots / SLOTS_PER_PACKET;
-               stats[i].count = ring->nr_tx_packets;
-               spin_unlock_irqrestore(&ring->lock, flags);
-       }
-}
-
 static void dma_rx(struct b43legacy_dmaring *ring,
                   int *slot)
 {
index 2f18600..f968104 100644 (file)
@@ -243,8 +243,6 @@ struct b43legacy_dmaring {
        int used_slots;
        /* Currently used slot in the ring. */
        int current_slot;
-       /* Total number of packets sent. Statistics only. */
-       unsigned int nr_tx_packets;
        /* Frameoffset in octets. */
        u32 frameoffset;
        /* Descriptor buffer size. */
@@ -292,9 +290,6 @@ void b43legacy_dma_free(struct b43legacy_wldev *dev);
 void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev);
 void b43legacy_dma_tx_resume(struct b43legacy_wldev *dev);
 
-void b43legacy_dma_get_tx_stats(struct b43legacy_wldev *dev,
-                               struct ieee80211_tx_queue_stats *stats);
-
 int b43legacy_dma_tx(struct b43legacy_wldev *dev,
                     struct sk_buff *skb);
 void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev,
@@ -315,11 +310,6 @@ void b43legacy_dma_free(struct b43legacy_wldev *dev)
 {
 }
 static inline
-void b43legacy_dma_get_tx_stats(struct b43legacy_wldev *dev,
-                               struct ieee80211_tx_queue_stats *stats)
-{
-}
-static inline
 int b43legacy_dma_tx(struct b43legacy_wldev *dev,
                     struct sk_buff *skb)
 {
index 874a64a..1d070be 100644 (file)
@@ -2446,29 +2446,6 @@ static int b43legacy_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
        return 0;
 }
 
-static int b43legacy_op_get_tx_stats(struct ieee80211_hw *hw,
-                                    struct ieee80211_tx_queue_stats *stats)
-{
-       struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
-       struct b43legacy_wldev *dev = wl->current_dev;
-       unsigned long flags;
-       int err = -ENODEV;
-
-       if (!dev)
-               goto out;
-       spin_lock_irqsave(&wl->irq_lock, flags);
-       if (likely(b43legacy_status(dev) >= B43legacy_STAT_STARTED)) {
-               if (b43legacy_using_pio(dev))
-                       b43legacy_pio_get_tx_stats(dev, stats);
-               else
-                       b43legacy_dma_get_tx_stats(dev, stats);
-               err = 0;
-       }
-       spin_unlock_irqrestore(&wl->irq_lock, flags);
-out:
-       return err;
-}
-
 static int b43legacy_op_get_stats(struct ieee80211_hw *hw,
                                  struct ieee80211_low_level_stats *stats)
 {
@@ -2923,6 +2900,7 @@ static int b43legacy_wireless_core_start(struct b43legacy_wldev *dev)
                goto out;
        }
        /* We are ready to run. */
+       ieee80211_wake_queues(dev->wl->hw);
        b43legacy_set_status(dev, B43legacy_STAT_STARTED);
 
        /* Start data flow (TX/RX) */
@@ -3343,6 +3321,7 @@ static int b43legacy_wireless_core_init(struct b43legacy_wldev *dev)
        b43legacy_security_init(dev);
        b43legacy_rng_init(wl);
 
+       ieee80211_wake_queues(dev->wl->hw);
        b43legacy_set_status(dev, B43legacy_STAT_INITIALIZED);
 
        b43legacy_leds_init(dev);
@@ -3511,7 +3490,6 @@ static const struct ieee80211_ops b43legacy_hw_ops = {
        .bss_info_changed       = b43legacy_op_bss_info_changed,
        .configure_filter       = b43legacy_op_configure_filter,
        .get_stats              = b43legacy_op_get_stats,
-       .get_tx_stats           = b43legacy_op_get_tx_stats,
        .start                  = b43legacy_op_start,
        .stop                   = b43legacy_op_stop,
        .set_tim                = b43legacy_op_beacon_set_tim,
index 51866c9..017c0e9 100644 (file)
@@ -477,7 +477,6 @@ int b43legacy_pio_tx(struct b43legacy_wldev *dev,
 
        list_move_tail(&packet->list, &queue->txqueue);
        queue->nr_txfree--;
-       queue->nr_tx_packets++;
        B43legacy_WARN_ON(queue->nr_txfree >= B43legacy_PIO_MAXTXPACKETS);
 
        tasklet_schedule(&queue->txtask);
@@ -546,18 +545,6 @@ void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev,
                tasklet_schedule(&queue->txtask);
 }
 
-void b43legacy_pio_get_tx_stats(struct b43legacy_wldev *dev,
-                               struct ieee80211_tx_queue_stats *stats)
-{
-       struct b43legacy_pio *pio = &dev->pio;
-       struct b43legacy_pioqueue *queue;
-
-       queue = pio->queue1;
-       stats[0].len = B43legacy_PIO_MAXTXPACKETS - queue->nr_txfree;
-       stats[0].limit = B43legacy_PIO_MAXTXPACKETS;
-       stats[0].count = queue->nr_tx_packets;
-}
-
 static void pio_rx_error(struct b43legacy_pioqueue *queue,
                         int clear_buffers,
                         const char *error)
index 464fec0..8e6773e 100644 (file)
@@ -74,10 +74,6 @@ struct b43legacy_pioqueue {
         * posted to the device. We are waiting for the txstatus.
         */
        struct list_head txrunning;
-       /* Total number or packets sent.
-        * (This counter can obviously wrap).
-        */
-       unsigned int nr_tx_packets;
        struct tasklet_struct txtask;
        struct b43legacy_pio_txpacket
                         tx_packets_cache[B43legacy_PIO_MAXTXPACKETS];
@@ -106,8 +102,6 @@ int b43legacy_pio_tx(struct b43legacy_wldev *dev,
                   struct sk_buff *skb);
 void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev,
                                 const struct b43legacy_txstatus *status);
-void b43legacy_pio_get_tx_stats(struct b43legacy_wldev *dev,
-                             struct ieee80211_tx_queue_stats *stats);
 void b43legacy_pio_rx(struct b43legacy_pioqueue *queue);
 
 /* Suspend TX queue in hardware. */
@@ -140,11 +134,6 @@ void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev,
 {
 }
 static inline
-void b43legacy_pio_get_tx_stats(struct b43legacy_wldev *dev,
-                             struct ieee80211_tx_queue_stats *stats)
-{
-}
-static inline
 void b43legacy_pio_rx(struct b43legacy_pioqueue *queue)
 {
 }
index 9d18206..694ceef 100644 (file)
@@ -247,6 +247,7 @@ struct iwl_cfg iwl1000_bgn_cfg = {
        .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
        .support_ct_kill_exit = true,
        .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
 };
 
 struct iwl_cfg iwl1000_bg_cfg = {
@@ -274,6 +275,7 @@ struct iwl_cfg iwl1000_bg_cfg = {
        .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
        .support_ct_kill_exit = true,
        .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
 };
 
 MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_MAX));
index 6d59889..f3d662c 100644 (file)
@@ -179,14 +179,24 @@ static void iwl5000_gain_computation(struct iwl_priv *priv,
                        data->delta_gain_code[i] = 0;
                        continue;
                }
-               delta_g = (1000 * ((s32)average_noise[default_chain] -
+
+               delta_g = (priv->cfg->chain_noise_scale *
+                       ((s32)average_noise[default_chain] -
                        (s32)average_noise[i])) / 1500;
+
                /* bound gain by 2 bits value max, 3rd bit is sign */
                data->delta_gain_code[i] =
                        min(abs(delta_g), (long) CHAIN_NOISE_MAX_DELTA_GAIN_CODE);
 
                if (delta_g < 0)
-                       /* set negative sign */
+                       /*
+                        * set negative sign ...
+                        * note to Intel developers:  This is uCode API format,
+                        *   not the format of any internal device registers.
+                        *   Do not change this format for e.g. 6050 or similar
+                        *   devices.  Change format only if more resolution
+                        *   (i.e. more than 2 bits magnitude) is needed.
+                        */
                        data->delta_gain_code[i] |= (1 << 2);
        }
 
@@ -1587,6 +1597,7 @@ struct iwl_cfg iwl5300_agn_cfg = {
        .use_rts_for_ht = true, /* use rts/cts protection */
        .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
        .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
 };
 
 struct iwl_cfg iwl5100_bgn_cfg = {
@@ -1612,6 +1623,7 @@ struct iwl_cfg iwl5100_bgn_cfg = {
        .use_rts_for_ht = true, /* use rts/cts protection */
        .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
        .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
 };
 
 struct iwl_cfg iwl5100_abg_cfg = {
@@ -1635,6 +1647,7 @@ struct iwl_cfg iwl5100_abg_cfg = {
        .led_compensation = 51,
        .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
        .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
 };
 
 struct iwl_cfg iwl5100_agn_cfg = {
@@ -1660,6 +1673,7 @@ struct iwl_cfg iwl5100_agn_cfg = {
        .use_rts_for_ht = true, /* use rts/cts protection */
        .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
        .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
 };
 
 struct iwl_cfg iwl5350_agn_cfg = {
@@ -1685,6 +1699,7 @@ struct iwl_cfg iwl5350_agn_cfg = {
        .use_rts_for_ht = true, /* use rts/cts protection */
        .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
        .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
 };
 
 struct iwl_cfg iwl5150_agn_cfg = {
@@ -1710,6 +1725,7 @@ struct iwl_cfg iwl5150_agn_cfg = {
        .use_rts_for_ht = true, /* use rts/cts protection */
        .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
        .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
 };
 
 struct iwl_cfg iwl5150_abg_cfg = {
@@ -1733,6 +1749,7 @@ struct iwl_cfg iwl5150_abg_cfg = {
        .led_compensation = 51,
        .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
        .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
 };
 
 MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX));
index a9f8551..782e23a 100644 (file)
@@ -277,21 +277,6 @@ static const struct iwl_ops iwl6000_ops = {
        .led = &iwlagn_led_ops,
 };
 
-static struct iwl_hcmd_utils_ops iwl6050_hcmd_utils = {
-       .get_hcmd_size = iwl5000_get_hcmd_size,
-       .build_addsta_hcmd = iwl5000_build_addsta_hcmd,
-       .rts_tx_cmd_flag = iwl5000_rts_tx_cmd_flag,
-       .calc_rssi = iwl5000_calc_rssi,
-};
-
-static const struct iwl_ops iwl6050_ops = {
-       .ucode = &iwl5000_ucode,
-       .lib = &iwl6000_lib,
-       .hcmd = &iwl5000_hcmd,
-       .utils = &iwl6050_hcmd_utils,
-       .led = &iwlagn_led_ops,
-};
-
 /*
  * "i": Internal configuration, use internal Power Amplifier
  */
@@ -324,6 +309,7 @@ struct iwl_cfg iwl6000i_2agn_cfg = {
        .adv_thermal_throttle = true,
        .support_ct_kill_exit = true,
        .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
 };
 
 struct iwl_cfg iwl6000i_2abg_cfg = {
@@ -354,6 +340,7 @@ struct iwl_cfg iwl6000i_2abg_cfg = {
        .adv_thermal_throttle = true,
        .support_ct_kill_exit = true,
        .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
 };
 
 struct iwl_cfg iwl6000i_2bg_cfg = {
@@ -384,6 +371,7 @@ struct iwl_cfg iwl6000i_2bg_cfg = {
        .adv_thermal_throttle = true,
        .support_ct_kill_exit = true,
        .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
 };
 
 struct iwl_cfg iwl6050_2agn_cfg = {
@@ -392,7 +380,7 @@ struct iwl_cfg iwl6050_2agn_cfg = {
        .ucode_api_max = IWL6050_UCODE_API_MAX,
        .ucode_api_min = IWL6050_UCODE_API_MIN,
        .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
-       .ops = &iwl6050_ops,
+       .ops = &iwl6000_ops,
        .eeprom_size = OTP_LOW_IMAGE_SIZE,
        .eeprom_ver = EEPROM_6050_EEPROM_VERSION,
        .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
@@ -415,6 +403,7 @@ struct iwl_cfg iwl6050_2agn_cfg = {
        .adv_thermal_throttle = true,
        .support_ct_kill_exit = true,
        .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1500,
 };
 
 struct iwl_cfg iwl6050_2abg_cfg = {
@@ -423,7 +412,7 @@ struct iwl_cfg iwl6050_2abg_cfg = {
        .ucode_api_max = IWL6050_UCODE_API_MAX,
        .ucode_api_min = IWL6050_UCODE_API_MIN,
        .sku = IWL_SKU_A|IWL_SKU_G,
-       .ops = &iwl6050_ops,
+       .ops = &iwl6000_ops,
        .eeprom_size = OTP_LOW_IMAGE_SIZE,
        .eeprom_ver = EEPROM_6050_EEPROM_VERSION,
        .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
@@ -445,6 +434,7 @@ struct iwl_cfg iwl6050_2abg_cfg = {
        .adv_thermal_throttle = true,
        .support_ct_kill_exit = true,
        .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1500,
 };
 
 struct iwl_cfg iwl6000_3agn_cfg = {
@@ -476,6 +466,7 @@ struct iwl_cfg iwl6000_3agn_cfg = {
        .adv_thermal_throttle = true,
        .support_ct_kill_exit = true,
        .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
 };
 
 MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX));
index d026828..1854c72 100644 (file)
@@ -2631,7 +2631,7 @@ static int iwl_mac_setup_register(struct iwl_priv *priv)
         */
        hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
 
-       hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
+       hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX + 1;
        /* we create the 802.11 header and a zero-length SSID element */
        hw->wiphy->max_scan_ie_len = IWL_MAX_PROBE_REQUEST - 24 - 2;
 
@@ -3440,7 +3440,6 @@ static struct ieee80211_ops iwl_hw_ops = {
        .set_key = iwl_mac_set_key,
        .update_tkip_key = iwl_mac_update_tkip_key,
        .get_stats = iwl_mac_get_stats,
-       .get_tx_stats = iwl_mac_get_tx_stats,
        .conf_tx = iwl_mac_conf_tx,
        .reset_tsf = iwl_mac_reset_tsf,
        .bss_info_changed = iwl_bss_info_changed,
index 02bf17e..d390eef 100644 (file)
@@ -2787,6 +2787,7 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
                if ((le16_to_cpu(priv->staging_rxon.channel) != ch))
                        priv->staging_rxon.flags = 0;
 
+               iwl_set_rxon_ht(priv, ht_conf);
                iwl_set_rxon_channel(priv, conf->channel);
 
                iwl_set_flags_for_band(priv, conf->channel->band);
@@ -2850,42 +2851,6 @@ out:
 }
 EXPORT_SYMBOL(iwl_mac_config);
 
-int iwl_mac_get_tx_stats(struct ieee80211_hw *hw,
-                        struct ieee80211_tx_queue_stats *stats)
-{
-       struct iwl_priv *priv = hw->priv;
-       int i, avail;
-       struct iwl_tx_queue *txq;
-       struct iwl_queue *q;
-       unsigned long flags;
-
-       IWL_DEBUG_MAC80211(priv, "enter\n");
-
-       if (!iwl_is_ready_rf(priv)) {
-               IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n");
-               return -EIO;
-       }
-
-       spin_lock_irqsave(&priv->lock, flags);
-
-       for (i = 0; i < AC_NUM; i++) {
-               txq = &priv->txq[i];
-               q = &txq->q;
-               avail = iwl_queue_space(q);
-
-               stats[i].len = q->n_window - avail;
-               stats[i].limit = q->n_window - q->high_mark;
-               stats[i].count = q->n_window;
-
-       }
-       spin_unlock_irqrestore(&priv->lock, flags);
-
-       IWL_DEBUG_MAC80211(priv, "leave\n");
-
-       return 0;
-}
-EXPORT_SYMBOL(iwl_mac_get_tx_stats);
-
 void iwl_mac_reset_tsf(struct ieee80211_hw *hw)
 {
        struct iwl_priv *priv = hw->priv;
index ec1fe1d..8f0c564 100644 (file)
@@ -293,6 +293,7 @@ struct iwl_cfg {
        bool support_ct_kill_exit;
        const bool support_wimax_coexist;
        u8 plcp_delta_threshold;
+       s32 chain_noise_scale;
 };
 
 /***************************
@@ -341,8 +342,6 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
                              struct ieee80211_vif *vif);
 int iwl_mac_config(struct ieee80211_hw *hw, u32 changed);
 void iwl_config_ap(struct iwl_priv *priv);
-int iwl_mac_get_tx_stats(struct ieee80211_hw *hw,
-                        struct ieee80211_tx_queue_stats *stats);
 void iwl_mac_reset_tsf(struct ieee80211_hw *hw);
 int iwl_alloc_txq_mem(struct iwl_priv *priv);
 void iwl_free_txq_mem(struct iwl_priv *priv);
index 08faafa..f786a40 100644 (file)
@@ -651,9 +651,20 @@ u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame,
        if (left < 0)
                return 0;
        *pos++ = WLAN_EID_SSID;
-       *pos++ = 0;
-
-       len += 2;
+       if (!priv->is_internal_short_scan &&
+           priv->scan_request->n_ssids) {
+               struct cfg80211_ssid *ssid =
+                       priv->scan_request->ssids;
+
+               /* Broadcast if ssid_len is 0 */
+               *pos++ = ssid->ssid_len;
+               memcpy(pos, ssid->ssid, ssid->ssid_len);
+               pos += ssid->ssid_len;
+               len += 2 + ssid->ssid_len;
+       } else {
+               *pos++ = 0;
+               len += 2;
+       }
 
        if (WARN_ON(left < ie_len))
                return len;
@@ -782,20 +793,26 @@ static void iwl_bg_request_scan(struct work_struct *data)
        if (priv->is_internal_short_scan) {
                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++;
+               /*
+                * The first SSID to scan is stuffed into the probe request
+                * template and the remaining ones are handled through the
+                * direct_scan array.
+                */
+               if (priv->scan_request->n_ssids > 1) {
+                       int i, p = 0;
+                       for (i = 1; i < priv->scan_request->n_ssids; i++) {
+                               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
index 119da54..eac2b9a 100644 (file)
@@ -3835,7 +3835,6 @@ static struct ieee80211_ops iwl3945_hw_ops = {
        .config = iwl_mac_config,
        .configure_filter = iwl_configure_filter,
        .set_key = iwl3945_mac_set_key,
-       .get_tx_stats = iwl_mac_get_tx_stats,
        .conf_tx = iwl_mac_conf_tx,
        .reset_tsf = iwl_mac_reset_tsf,
        .bss_info_changed = iwl_bss_info_changed,
index 0334a58..e747044 100644 (file)
@@ -240,11 +240,6 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
        /* Now we got response from FW, cancel the command timer */
        del_timer(&priv->command_timer);
        priv->cmd_timed_out = 0;
-       if (priv->nr_retries) {
-               lbs_pr_info("Received result %x to command %x after %d retries\n",
-                           result, curcmd, priv->nr_retries);
-               priv->nr_retries = 0;
-       }
 
        /* Store the response code to cur_cmd_retcode. */
        priv->cur_cmd_retcode = result;
index c348aff..6977ee8 100644 (file)
@@ -109,7 +109,6 @@ struct lbs_private {
        struct list_head cmdpendingq; /* pending command buffers */
        wait_queue_head_t cmd_pending;
        struct timer_list command_timer;
-       int nr_retries;
        int cmd_timed_out;
 
        /* Command responses sent from the hardware to the driver */
index bf4bfba..3ea03f2 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/kthread.h>
 #include <linux/list.h>
 #include <linux/netdevice.h>
+#include <linux/semaphore.h>
 #include <linux/spi/libertas_spi.h>
 #include <linux/spi/spi.h>
 
index 60bde12..cd8ed7f 100644 (file)
@@ -536,31 +536,14 @@ static int lbs_thread(void *data)
                if (priv->cmd_timed_out && priv->cur_cmd) {
                        struct cmd_ctrl_node *cmdnode = priv->cur_cmd;
 
-                       if (++priv->nr_retries > 3) {
-                               lbs_pr_info("Excessive timeouts submitting "
-                                       "command 0x%04x\n",
-                                       le16_to_cpu(cmdnode->cmdbuf->command));
-                               lbs_complete_command(priv, cmdnode, -ETIMEDOUT);
-                               priv->nr_retries = 0;
-                               if (priv->reset_card)
-                                       priv->reset_card(priv);
-                       } else {
-                               priv->cur_cmd = NULL;
-                               priv->dnld_sent = DNLD_RES_RECEIVED;
-                               lbs_pr_info("requeueing command 0x%04x due "
-                                       "to timeout (#%d)\n",
-                                       le16_to_cpu(cmdnode->cmdbuf->command),
-                                       priv->nr_retries);
-
-                               /* Stick it back at the _top_ of the pending queue
-                                  for immediate resubmission */
-                               list_add(&cmdnode->list, &priv->cmdpendingq);
-                       }
+                       lbs_pr_info("Timeout submitting command 0x%04x\n",
+                               le16_to_cpu(cmdnode->cmdbuf->command));
+                       lbs_complete_command(priv, cmdnode, -ETIMEDOUT);
+                       if (priv->reset_card)
+                               priv->reset_card(priv);
                }
                priv->cmd_timed_out = 0;
 
-
-
                if (!priv->fw_ready)
                        continue;
 
@@ -732,7 +715,7 @@ done:
  *  This function handles the timeout of command sending.
  *  It will re-send the same command again.
  */
-static void command_timer_fn(unsigned long data)
+static void lbs_cmd_timeout_handler(unsigned long data)
 {
        struct lbs_private *priv = (struct lbs_private *)data;
        unsigned long flags;
@@ -851,7 +834,7 @@ static int lbs_init_adapter(struct lbs_private *priv)
 
        mutex_init(&priv->lock);
 
-       setup_timer(&priv->command_timer, command_timer_fn,
+       setup_timer(&priv->command_timer, lbs_cmd_timeout_handler,
                (unsigned long)priv);
        setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn,
                        (unsigned long)priv);
index ba3eb01..6ab3003 100644 (file)
@@ -555,6 +555,9 @@ struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev)
        priv->band.n_channels = ARRAY_SIZE(lbtf_channels);
        priv->band.channels = priv->channels;
        hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
+       hw->wiphy->interface_modes =
+               BIT(NL80211_IFTYPE_STATION) |
+               BIT(NL80211_IFTYPE_ADHOC);
        skb_queue_head_init(&priv->bc_ps_buf);
 
        SET_IEEE80211_DEV(hw, dmdev);
index 0dbda8d..00ffe6d 100644 (file)
@@ -32,6 +32,10 @@ static int radios = 2;
 module_param(radios, int, 0444);
 MODULE_PARM_DESC(radios, "Number of simulated radios");
 
+static bool fake_hw_scan;
+module_param(fake_hw_scan, bool, 0444);
+MODULE_PARM_DESC(fake_hw_scan, "Install fake (no-op) hw-scan handler");
+
 /**
  * enum hwsim_regtest - the type of regulatory tests we offer
  *
@@ -908,8 +912,43 @@ static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop)
         */
 }
 
+struct hw_scan_done {
+       struct delayed_work w;
+       struct ieee80211_hw *hw;
+};
 
-static const struct ieee80211_ops mac80211_hwsim_ops =
+static void hw_scan_done(struct work_struct *work)
+{
+       struct hw_scan_done *hsd =
+               container_of(work, struct hw_scan_done, w.work);
+
+       ieee80211_scan_completed(hsd->hw, false);
+       kfree(hsd);
+}
+
+static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw,
+                                 struct cfg80211_scan_request *req)
+{
+       struct hw_scan_done *hsd = kzalloc(sizeof(*hsd), GFP_KERNEL);
+       int i;
+
+       if (!hsd)
+               return -ENOMEM;
+
+       hsd->hw = hw;
+       INIT_DELAYED_WORK(&hsd->w, hw_scan_done);
+
+       printk(KERN_DEBUG "hwsim scan request\n");
+       for (i = 0; i < req->n_channels; i++)
+               printk(KERN_DEBUG "hwsim scan freq %d\n",
+                       req->channels[i]->center_freq);
+
+       ieee80211_queue_delayed_work(hw, &hsd->w, 2 * HZ);
+
+       return 0;
+}
+
+static struct ieee80211_ops mac80211_hwsim_ops =
 {
        .tx = mac80211_hwsim_tx,
        .start = mac80211_hwsim_start,
@@ -1119,6 +1158,9 @@ static int __init init_mac80211_hwsim(void)
        if (radios < 1 || radios > 100)
                return -EINVAL;
 
+       if (fake_hw_scan)
+               mac80211_hwsim_ops.hw_scan = mac80211_hwsim_hw_scan;
+
        spin_lock_init(&hwsim_radio_lock);
        INIT_LIST_HEAD(&hwsim_radios);
 
index f0f08f3..0cfdb9d 100644 (file)
@@ -119,7 +119,7 @@ struct mwl8k_tx_queue {
        /* sw appends here */
        int tail;
 
-       struct ieee80211_tx_queue_stats stats;
+       unsigned int len;
        struct mwl8k_tx_desc *txd;
        dma_addr_t txd_dma;
        struct sk_buff **skb;
@@ -1136,8 +1136,7 @@ static int mwl8k_txq_init(struct ieee80211_hw *hw, int index)
        int size;
        int i;
 
-       memset(&txq->stats, 0, sizeof(struct ieee80211_tx_queue_stats));
-       txq->stats.limit = MWL8K_TX_DESCS;
+       txq->len = 0;
        txq->head = 0;
        txq->tail = 0;
 
@@ -1213,7 +1212,7 @@ static void mwl8k_dump_tx_rings(struct ieee80211_hw *hw)
                printk(KERN_ERR "%s: txq[%d] len=%d head=%d tail=%d "
                       "fw_owned=%d drv_owned=%d unused=%d\n",
                       wiphy_name(hw->wiphy), i,
-                      txq->stats.len, txq->head, txq->tail,
+                      txq->len, txq->head, txq->tail,
                       fw_owned, drv_owned, unused);
        }
 }
@@ -1299,7 +1298,7 @@ mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force)
        int processed;
 
        processed = 0;
-       while (txq->stats.len > 0 && limit--) {
+       while (txq->len > 0 && limit--) {
                int tx;
                struct mwl8k_tx_desc *tx_desc;
                unsigned long addr;
@@ -1321,8 +1320,8 @@ mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force)
                }
 
                txq->head = (tx + 1) % MWL8K_TX_DESCS;
-               BUG_ON(txq->stats.len == 0);
-               txq->stats.len--;
+               BUG_ON(txq->len == 0);
+               txq->len--;
                priv->pending_tx_pkts--;
 
                addr = le32_to_cpu(tx_desc->pkt_phys_addr);
@@ -1454,8 +1453,7 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)
        wmb();
        tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus);
 
-       txq->stats.count++;
-       txq->stats.len++;
+       txq->len++;
        priv->pending_tx_pkts++;
 
        txq->tail++;
@@ -3818,24 +3816,6 @@ static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue,
        return rc;
 }
 
-static int mwl8k_get_tx_stats(struct ieee80211_hw *hw,
-                             struct ieee80211_tx_queue_stats *stats)
-{
-       struct mwl8k_priv *priv = hw->priv;
-       struct mwl8k_tx_queue *txq;
-       int index;
-
-       spin_lock_bh(&priv->tx_lock);
-       for (index = 0; index < MWL8K_TX_QUEUES; index++) {
-               txq = priv->txq + index;
-               memcpy(&stats[index], &txq->stats,
-                       sizeof(struct ieee80211_tx_queue_stats));
-       }
-       spin_unlock_bh(&priv->tx_lock);
-
-       return 0;
-}
-
 static int mwl8k_get_stats(struct ieee80211_hw *hw,
                           struct ieee80211_low_level_stats *stats)
 {
@@ -3871,7 +3851,6 @@ static const struct ieee80211_ops mwl8k_ops = {
        .set_rts_threshold      = mwl8k_set_rts_threshold,
        .sta_notify             = mwl8k_sta_notify,
        .conf_tx                = mwl8k_conf_tx,
-       .get_tx_stats           = mwl8k_get_tx_stats,
        .get_stats              = mwl8k_get_stats,
        .ampdu_action           = mwl8k_ampdu_action,
 };
index 26428e4..3fe6366 100644 (file)
@@ -358,16 +358,6 @@ static int p54_get_stats(struct ieee80211_hw *dev,
        return 0;
 }
 
-static int p54_get_tx_stats(struct ieee80211_hw *dev,
-                           struct ieee80211_tx_queue_stats *stats)
-{
-       struct p54_common *priv = dev->priv;
-
-       memcpy(stats, &priv->tx_stats[P54_QUEUE_DATA],
-              sizeof(stats[0]) * dev->queues);
-       return 0;
-}
-
 static void p54_bss_info_changed(struct ieee80211_hw *dev,
                                 struct ieee80211_vif *vif,
                                 struct ieee80211_bss_conf *info,
@@ -522,7 +512,6 @@ static const struct ieee80211_ops p54_ops = {
        .configure_filter       = p54_configure_filter,
        .conf_tx                = p54_conf_tx,
        .get_stats              = p54_get_stats,
-       .get_tx_stats           = p54_get_tx_stats
 };
 
 struct ieee80211_hw *p54_init_common(size_t priv_data_len)
index 1afc394..43a3b2e 100644 (file)
@@ -157,6 +157,12 @@ struct p54_led_dev {
 
 #endif /* CONFIG_P54_LEDS */
 
+struct p54_tx_queue_stats {
+       unsigned int len;
+       unsigned int limit;
+       unsigned int count;
+};
+
 struct p54_common {
        struct ieee80211_hw *hw;
        struct ieee80211_vif *vif;
@@ -183,7 +189,7 @@ struct p54_common {
        /* (e)DCF / QOS state */
        bool use_short_slot;
        spinlock_t tx_stats_lock;
-       struct ieee80211_tx_queue_stats tx_stats[8];
+       struct p54_tx_queue_stats tx_stats[8];
        struct p54_edcf_queue_param qos_params[8];
 
        /* Radio data */
index b6dda2b..0e8f694 100644 (file)
@@ -183,7 +183,7 @@ static int p54_tx_qos_accounting_alloc(struct p54_common *priv,
                                       struct sk_buff *skb,
                                       const u16 p54_queue)
 {
-       struct ieee80211_tx_queue_stats *queue;
+       struct p54_tx_queue_stats *queue;
        unsigned long flags;
 
        if (WARN_ON(p54_queue > P54_QUEUE_NUM))
index aa579eb..1089827 100644 (file)
@@ -1561,7 +1561,6 @@ static const struct ieee80211_ops rt2400pci_mac80211_ops = {
        .get_stats              = rt2x00mac_get_stats,
        .bss_info_changed       = rt2x00mac_bss_info_changed,
        .conf_tx                = rt2400pci_conf_tx,
-       .get_tx_stats           = rt2x00mac_get_tx_stats,
        .get_tsf                = rt2400pci_get_tsf,
        .tx_last_beacon         = rt2400pci_tx_last_beacon,
        .rfkill_poll            = rt2x00mac_rfkill_poll,
index 77ee1df..f6440bb 100644 (file)
@@ -1859,7 +1859,6 @@ static const struct ieee80211_ops rt2500pci_mac80211_ops = {
        .get_stats              = rt2x00mac_get_stats,
        .bss_info_changed       = rt2x00mac_bss_info_changed,
        .conf_tx                = rt2x00mac_conf_tx,
-       .get_tx_stats           = rt2x00mac_get_tx_stats,
        .get_tsf                = rt2500pci_get_tsf,
        .tx_last_beacon         = rt2500pci_tx_last_beacon,
        .rfkill_poll            = rt2x00mac_rfkill_poll,
index 9e6f865..81ca4ec 100644 (file)
@@ -1761,7 +1761,6 @@ static const struct ieee80211_ops rt2500usb_mac80211_ops = {
        .get_stats              = rt2x00mac_get_stats,
        .bss_info_changed       = rt2x00mac_bss_info_changed,
        .conf_tx                = rt2x00mac_conf_tx,
-       .get_tx_stats           = rt2x00mac_get_tx_stats,
        .rfkill_poll            = rt2x00mac_rfkill_poll,
 };
 
index 529a373..a45e027 100644 (file)
@@ -2297,7 +2297,6 @@ const struct ieee80211_ops rt2800_mac80211_ops = {
        .set_rts_threshold      = rt2800_set_rts_threshold,
        .bss_info_changed       = rt2x00mac_bss_info_changed,
        .conf_tx                = rt2800_conf_tx,
-       .get_tx_stats           = rt2x00mac_get_tx_stats,
        .get_tsf                = rt2800_get_tsf,
        .rfkill_poll            = rt2x00mac_rfkill_poll,
 };
index 096da85..43b70c6 100644 (file)
@@ -1044,8 +1044,6 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 #endif /* CONFIG_RT2X00_LIB_CRYPTO */
 int rt2x00mac_get_stats(struct ieee80211_hw *hw,
                        struct ieee80211_low_level_stats *stats);
-int rt2x00mac_get_tx_stats(struct ieee80211_hw *hw,
-                          struct ieee80211_tx_queue_stats *stats);
 void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
                                struct ieee80211_vif *vif,
                                struct ieee80211_bss_conf *bss_conf,
index 00f1f93..abbd857 100644 (file)
@@ -555,22 +555,6 @@ int rt2x00mac_get_stats(struct ieee80211_hw *hw,
 }
 EXPORT_SYMBOL_GPL(rt2x00mac_get_stats);
 
-int rt2x00mac_get_tx_stats(struct ieee80211_hw *hw,
-                          struct ieee80211_tx_queue_stats *stats)
-{
-       struct rt2x00_dev *rt2x00dev = hw->priv;
-       unsigned int i;
-
-       for (i = 0; i < rt2x00dev->ops->tx_queues; i++) {
-               stats[i].len = rt2x00dev->tx[i].length;
-               stats[i].limit = rt2x00dev->tx[i].limit;
-               stats[i].count = rt2x00dev->tx[i].count;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rt2x00mac_get_tx_stats);
-
 void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
                                struct ieee80211_vif *vif,
                                struct ieee80211_bss_conf *bss_conf,
index 1f97a79..74de53e 100644 (file)
@@ -2730,7 +2730,6 @@ static const struct ieee80211_ops rt61pci_mac80211_ops = {
        .get_stats              = rt2x00mac_get_stats,
        .bss_info_changed       = rt2x00mac_bss_info_changed,
        .conf_tx                = rt61pci_conf_tx,
-       .get_tx_stats           = rt2x00mac_get_tx_stats,
        .get_tsf                = rt61pci_get_tsf,
        .rfkill_poll            = rt2x00mac_rfkill_poll,
 };
index a026912..3781eb7 100644 (file)
@@ -2245,7 +2245,6 @@ static const struct ieee80211_ops rt73usb_mac80211_ops = {
        .get_stats              = rt2x00mac_get_stats,
        .bss_info_changed       = rt2x00mac_bss_info_changed,
        .conf_tx                = rt73usb_conf_tx,
-       .get_tx_stats           = rt2x00mac_get_tx_stats,
        .get_tsf                = rt73usb_get_tsf,
        .rfkill_poll            = rt2x00mac_rfkill_poll,
 };
index a717dde..24ae6a3 100644 (file)
@@ -1144,9 +1144,10 @@ static int wl1251_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
        if (ret < 0)
                goto out;
 
+       /* mac80211 uses units of 32 usec */
        ret = wl1251_acx_ac_cfg(wl, wl1251_tx_get_queue(queue),
                                params->cw_min, params->cw_max,
-                               params->aifs, params->txop);
+                               params->aifs, params->txop * 32);
        if (ret < 0)
                goto out_sleep;
 
index 3c6feed..97efce1 100644 (file)
@@ -270,7 +270,6 @@ void ssb_mipscore_init(struct ssb_mipscore *mcore)
                                set_irq(dev, irq++);
                        }
                        break;
-                       /* fallthrough */
                case SSB_DEV_PCI:
                case SSB_DEV_ETHERNET:
                case SSB_DEV_ETHERNET_GBIT:
@@ -281,6 +280,10 @@ void ssb_mipscore_init(struct ssb_mipscore *mcore)
                                set_irq(dev, irq++);
                                break;
                        }
+                       /* fallthrough */
+               case SSB_DEV_EXTIF:
+                       set_irq(dev, 0);
+                       break;
                }
        }
        ssb_dprintk(KERN_INFO PFX "after irq reconfiguration\n");
index 5681ebe..03dfd27 100644 (file)
@@ -494,8 +494,7 @@ static int ssb_devices_register(struct ssb_bus *bus)
 #endif
                        break;
                case SSB_BUSTYPE_SDIO:
-#ifdef CONFIG_SSB_SDIO
-                       sdev->irq = bus->host_sdio->dev.irq;
+#ifdef CONFIG_SSB_SDIOHOST
                        dev->parent = &bus->host_sdio->dev;
 #endif
                        break;
index a3f0a7e..5b3569b 100644 (file)
@@ -1561,37 +1561,82 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
  * Documentation in Documentation/networking/radiotap-headers.txt
  */
 
+struct radiotap_align_size {
+       uint8_t align:4, size:4;
+};
+
+struct ieee80211_radiotap_namespace {
+       const struct radiotap_align_size *align_size;
+       int n_bits;
+       uint32_t oui;
+       uint8_t subns;
+};
+
+struct ieee80211_radiotap_vendor_namespaces {
+       const struct ieee80211_radiotap_namespace *ns;
+       int n_ns;
+};
+
 /**
  * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args
- * @rtheader: pointer to the radiotap header we are walking through
- * @max_length: length of radiotap header in cpu byte ordering
- * @this_arg_index: IEEE80211_RADIOTAP_... index of current arg
- * @this_arg: pointer to current radiotap arg
- * @arg_index: internal next argument index
- * @arg: internal next argument pointer
- * @next_bitmap: internal pointer to next present u32
- * @bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present
+ * @this_arg_index: index of current arg, valid after each successful call
+ *     to ieee80211_radiotap_iterator_next()
+ * @this_arg: pointer to current radiotap arg; it is valid after each
+ *     call to ieee80211_radiotap_iterator_next() but also after
+ *     ieee80211_radiotap_iterator_init() where it will point to
+ *     the beginning of the actual data portion
+ * @this_arg_size: length of the current arg, for convenience
+ * @current_namespace: pointer to the current namespace definition
+ *     (or internally %NULL if the current namespace is unknown)
+ * @is_radiotap_ns: indicates whether the current namespace is the default
+ *     radiotap namespace or not
+ *
+ * @overrides: override standard radiotap fields
+ * @n_overrides: number of overrides
+ *
+ * @_rtheader: pointer to the radiotap header we are walking through
+ * @_max_length: length of radiotap header in cpu byte ordering
+ * @_arg_index: next argument index
+ * @_arg: next argument pointer
+ * @_next_bitmap: internal pointer to next present u32
+ * @_bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present
+ * @_vns: vendor namespace definitions
+ * @_next_ns_data: beginning of the next namespace's data
+ * @_reset_on_ext: internal; reset the arg index to 0 when going to the
+ *     next bitmap word
+ *
+ * Describes the radiotap parser state. Fields prefixed with an underscore
+ * must not be used by users of the parser, only by the parser internally.
  */
 
 struct ieee80211_radiotap_iterator {
-       struct ieee80211_radiotap_header *rtheader;
-       int max_length;
+       struct ieee80211_radiotap_header *_rtheader;
+       const struct ieee80211_radiotap_vendor_namespaces *_vns;
+       const struct ieee80211_radiotap_namespace *current_namespace;
+
+       unsigned char *_arg, *_next_ns_data;
+       uint32_t *_next_bitmap;
+
+       unsigned char *this_arg;
        int this_arg_index;
-       u8 *this_arg;
+       int this_arg_size;
 
-       int arg_index;
-       u8 *arg;
-       __le32 *next_bitmap;
-       u32 bitmap_shifter;
+       int is_radiotap_ns;
+
+       int _max_length;
+       int _arg_index;
+       uint32_t _bitmap_shifter;
+       int _reset_on_ext;
 };
 
 extern int ieee80211_radiotap_iterator_init(
-   struct ieee80211_radiotap_iterator *iterator,
-   struct ieee80211_radiotap_header *radiotap_header,
-   int max_length);
+       struct ieee80211_radiotap_iterator *iterator,
+       struct ieee80211_radiotap_header *radiotap_header,
+       int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns);
 
 extern int ieee80211_radiotap_iterator_next(
-   struct ieee80211_radiotap_iterator *iterator);
+       struct ieee80211_radiotap_iterator *iterator);
+
 
 extern const unsigned char rfc1042_header[6];
 extern const unsigned char bridge_tunnel_header[6];
index 9d3d86a..af49f8a 100644 (file)
@@ -198,6 +198,10 @@ enum ieee80211_radiotap_type {
        IEEE80211_RADIOTAP_TX_FLAGS = 15,
        IEEE80211_RADIOTAP_RTS_RETRIES = 16,
        IEEE80211_RADIOTAP_DATA_RETRIES = 17,
+
+       /* valid in every it_present bitmap, even vendor namespaces */
+       IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29,
+       IEEE80211_RADIOTAP_VENDOR_NAMESPACE = 30,
        IEEE80211_RADIOTAP_EXT = 31
 };
 
index 74ccf30..314e981 100644 (file)
@@ -117,19 +117,6 @@ struct ieee80211_tx_queue_params {
        bool uapsd;
 };
 
-/**
- * struct ieee80211_tx_queue_stats - transmit queue statistics
- *
- * @len: number of packets in queue
- * @limit: queue length limit
- * @count: number of frames sent
- */
-struct ieee80211_tx_queue_stats {
-       unsigned int len;
-       unsigned int limit;
-       unsigned int count;
-};
-
 struct ieee80211_low_level_stats {
        unsigned int dot11ACKFailureCount;
        unsigned int dot11RTSFailureCount;
@@ -814,7 +801,7 @@ enum set_key_cmd {
  * mac80211, any ieee80211_sta pointer you get access to must
  * either be protected by rcu_read_lock() explicitly or implicitly,
  * or you must take good care to not use such a pointer after a
- * call to your sta_notify callback that removed it.
+ * call to your sta_remove callback that removed it.
  *
  * @addr: MAC address
  * @aid: AID we assigned to the station if we're an AP
@@ -840,8 +827,8 @@ struct ieee80211_sta {
  * indicates addition and removal of a station to station table,
  * or if a associated station made a power state transition.
  *
- * @STA_NOTIFY_ADD: a station was added to the station table
- * @STA_NOTIFY_REMOVE: a station being removed from the station table
+ * @STA_NOTIFY_ADD: (DEPRECATED) a station was added to the station table
+ * @STA_NOTIFY_REMOVE: (DEPRECATED) a station being removed from the station table
  * @STA_NOTIFY_SLEEP: a station is now sleeping
  * @STA_NOTIFY_AWAKE: a sleeping station woke up
  */
@@ -958,6 +945,11 @@ enum ieee80211_tkip_key_type {
  *     Hardware supports Unscheduled Automatic Power Save Delivery
  *     (U-APSD) in managed mode. The mode is configured with
  *     conf_tx() operation.
+ *
+ * @IEEE80211_HW_REPORTS_TX_ACK_STATUS:
+ *     Hardware can provide ack status reports of Tx frames to
+ *     the stack.
+ *
  */
 enum ieee80211_hw_flags {
        IEEE80211_HW_HAS_RATE_CONTROL                   = 1<<0,
@@ -978,6 +970,7 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_SUPPORTS_STATIC_SMPS               = 1<<15,
        IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS              = 1<<16,
        IEEE80211_HW_SUPPORTS_UAPSD                     = 1<<17,
+       IEEE80211_HW_REPORTS_TX_ACK_STATUS              = 1<<18,
 };
 
 /**
@@ -1534,22 +1527,20 @@ enum ieee80211_ampdu_mlme_action {
  * @set_rts_threshold: Configuration of RTS threshold (if device needs it)
  *     The callback can sleep.
  *
- * @sta_notify: Notifies low level driver about addition, removal or power
- *     state transition of an associated station, AP,  IBSS/WDS/mesh peer etc.
- *     Must be atomic.
+ * @sta_add: Notifies low level driver about addition of an associated station,
+ *     AP, IBSS/WDS/mesh peer etc. This callback can sleep.
+ *
+ * @sta_remove: Notifies low level driver about removal of an associated
+ *     station, AP, IBSS/WDS/mesh peer etc. This callback can sleep.
+ *
+ * @sta_notify: Notifies low level driver about power state transition of an
+ *     associated station, AP,  IBSS/WDS/mesh peer etc. Must be atomic.
  *
  * @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
  *     bursting) for a hardware TX queue.
  *     Returns a negative error code on failure.
  *     The callback can sleep.
  *
- * @get_tx_stats: Get statistics of the current TX queue status. This is used
- *     to get number of currently queued packets (queue length), maximum queue
- *     size (limit), and total number of packets sent using each TX queue
- *     (count). The 'stats' pointer points to an array that has hw->queues
- *     items.
- *     The callback must be atomic.
- *
  * @get_tsf: Get the current TSF timer value from firmware/hardware. Currently,
  *     this is only used for IBSS mode BSSID merging and debugging. Is not a
  *     required function.
@@ -1635,12 +1626,14 @@ struct ieee80211_ops {
        void (*get_tkip_seq)(struct ieee80211_hw *hw, u8 hw_key_idx,
                             u32 *iv32, u16 *iv16);
        int (*set_rts_threshold)(struct ieee80211_hw *hw, u32 value);
+       int (*sta_add)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                      struct ieee80211_sta *sta);
+       int (*sta_remove)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                         struct ieee80211_sta *sta);
        void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        enum sta_notify_cmd, struct ieee80211_sta *sta);
        int (*conf_tx)(struct ieee80211_hw *hw, u16 queue,
                       const struct ieee80211_tx_queue_params *params);
-       int (*get_tx_stats)(struct ieee80211_hw *hw,
-                           struct ieee80211_tx_queue_stats *stats);
        u64 (*get_tsf)(struct ieee80211_hw *hw);
        void (*set_tsf)(struct ieee80211_hw *hw, u64 tsf);
        void (*reset_tsf)(struct ieee80211_hw *hw);
index 718fbcf..5538e1b 100644 (file)
@@ -237,6 +237,14 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
            sdata->vif.type != NL80211_IFTYPE_AP)
                return -EINVAL;
 
+       if (test_sta_flags(sta, WLAN_STA_DISASSOC)) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               printk(KERN_DEBUG "Disassociation is in progress. "
+                      "Denying BA session request\n");
+#endif
+               return -EINVAL;
+       }
+
        if (test_sta_flags(sta, WLAN_STA_SUSPEND)) {
 #ifdef CONFIG_MAC80211_HT_DEBUG
                printk(KERN_DEBUG "Suspend in progress. "
index facf233..e1731b7 100644 (file)
@@ -515,6 +515,8 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
                if (old)
                        memcpy(new->tail, old->tail, new_tail_len);
 
+       sdata->vif.bss_conf.dtim_period = new->dtim_period;
+
        rcu_assign_pointer(sdata->u.ap.beacon, new);
 
        synchronize_rcu();
@@ -747,9 +749,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
        layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
                sdata->vif.type == NL80211_IFTYPE_AP;
 
-       rcu_read_lock();
-
-       err = sta_info_insert(sta);
+       err = sta_info_insert_rcu(sta);
        if (err) {
                rcu_read_unlock();
                return err;
@@ -768,26 +768,13 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
 {
        struct ieee80211_local *local = wiphy_priv(wiphy);
        struct ieee80211_sub_if_data *sdata;
-       struct sta_info *sta;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (mac) {
-               rcu_read_lock();
-
-               sta = sta_info_get_bss(sdata, mac);
-               if (!sta) {
-                       rcu_read_unlock();
-                       return -ENOENT;
-               }
-
-               sta_info_unlink(&sta);
-               rcu_read_unlock();
-
-               sta_info_destroy(sta);
-       } else
-               sta_info_flush(local, sdata);
+       if (mac)
+               return sta_info_destroy_addr_bss(sdata, mac);
 
+       sta_info_flush(local, sdata);
        return 0;
 }
 
index b3bc32b..637929b 100644 (file)
@@ -250,6 +250,38 @@ static const struct file_operations uapsd_max_sp_len_ops = {
        .open = mac80211_open_file_generic
 };
 
+static ssize_t channel_type_read(struct file *file, char __user *user_buf,
+                      size_t count, loff_t *ppos)
+{
+       struct ieee80211_local *local = file->private_data;
+       const char *buf;
+
+       switch (local->hw.conf.channel_type) {
+       case NL80211_CHAN_NO_HT:
+               buf = "no ht\n";
+               break;
+       case NL80211_CHAN_HT20:
+               buf = "ht20\n";
+               break;
+       case NL80211_CHAN_HT40MINUS:
+               buf = "ht40-\n";
+               break;
+       case NL80211_CHAN_HT40PLUS:
+               buf = "ht40+\n";
+               break;
+       default:
+               buf = "???";
+               break;
+       }
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
+}
+
+static const struct file_operations channel_type_ops = {
+       .read = channel_type_read,
+       .open = mac80211_open_file_generic
+};
+
 static ssize_t queues_read(struct file *file, char __user *user_buf,
                           size_t count, loff_t *ppos)
 {
@@ -408,6 +440,7 @@ void debugfs_hw_add(struct ieee80211_local *local)
        DEBUGFS_ADD(noack);
        DEBUGFS_ADD(uapsd_queues);
        DEBUGFS_ADD(uapsd_max_sp_len);
+       DEBUGFS_ADD(channel_type);
 
        statsd = debugfs_create_dir("statistics", phyd);
 
index 6c31f38..c3d8440 100644 (file)
@@ -243,6 +243,40 @@ static inline void drv_sta_notify(struct ieee80211_local *local,
        trace_drv_sta_notify(local, sdata, cmd, sta);
 }
 
+static inline int drv_sta_add(struct ieee80211_local *local,
+                             struct ieee80211_sub_if_data *sdata,
+                             struct ieee80211_sta *sta)
+{
+       int ret = 0;
+
+       might_sleep();
+
+       if (local->ops->sta_add)
+               ret = local->ops->sta_add(&local->hw, &sdata->vif, sta);
+       else if (local->ops->sta_notify)
+               local->ops->sta_notify(&local->hw, &sdata->vif,
+                                       STA_NOTIFY_ADD, sta);
+
+       trace_drv_sta_add(local, sdata, sta, ret);
+
+       return ret;
+}
+
+static inline void drv_sta_remove(struct ieee80211_local *local,
+                                 struct ieee80211_sub_if_data *sdata,
+                                 struct ieee80211_sta *sta)
+{
+       might_sleep();
+
+       if (local->ops->sta_remove)
+               local->ops->sta_remove(&local->hw, &sdata->vif, sta);
+       else if (local->ops->sta_notify)
+               local->ops->sta_notify(&local->hw, &sdata->vif,
+                                       STA_NOTIFY_REMOVE, sta);
+
+       trace_drv_sta_remove(local, sdata, sta);
+}
+
 static inline int drv_conf_tx(struct ieee80211_local *local, u16 queue,
                              const struct ieee80211_tx_queue_params *params)
 {
@@ -256,14 +290,6 @@ static inline int drv_conf_tx(struct ieee80211_local *local, u16 queue,
        return ret;
 }
 
-static inline int drv_get_tx_stats(struct ieee80211_local *local,
-                                  struct ieee80211_tx_queue_stats *stats)
-{
-       int ret = local->ops->get_tx_stats(&local->hw, stats);
-       trace_drv_get_tx_stats(local, stats, ret);
-       return ret;
-}
-
 static inline u64 drv_get_tsf(struct ieee80211_local *local)
 {
        u64 ret = -1ULL;
index 502424b..41baf73 100644 (file)
@@ -545,59 +545,88 @@ TRACE_EVENT(drv_sta_notify,
        )
 );
 
-TRACE_EVENT(drv_conf_tx,
-       TP_PROTO(struct ieee80211_local *local, u16 queue,
-                const struct ieee80211_tx_queue_params *params,
-                int ret),
+TRACE_EVENT(drv_sta_add,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
+                struct ieee80211_sta *sta, int ret),
 
-       TP_ARGS(local, queue, params, ret),
+       TP_ARGS(local, sdata, sta, ret),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
-               __field(u16, queue)
-               __field(u16, txop)
-               __field(u16, cw_min)
-               __field(u16, cw_max)
-               __field(u8, aifs)
+               VIF_ENTRY
+               STA_ENTRY
                __field(int, ret)
        ),
 
        TP_fast_assign(
                LOCAL_ASSIGN;
-               __entry->queue = queue;
+               VIF_ASSIGN;
+               STA_ASSIGN;
                __entry->ret = ret;
-               __entry->txop = params->txop;
-               __entry->cw_max = params->cw_max;
-               __entry->cw_min = params->cw_min;
-               __entry->aifs = params->aifs;
        ),
 
        TP_printk(
-               LOCAL_PR_FMT " queue:%d ret:%d",
-               LOCAL_PR_ARG, __entry->queue, __entry->ret
+               LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT " ret:%d",
+               LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->ret
        )
 );
 
-TRACE_EVENT(drv_get_tx_stats,
+TRACE_EVENT(drv_sta_remove,
        TP_PROTO(struct ieee80211_local *local,
-                struct ieee80211_tx_queue_stats *stats,
+                struct ieee80211_sub_if_data *sdata,
+                struct ieee80211_sta *sta),
+
+       TP_ARGS(local, sdata, sta),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               VIF_ENTRY
+               STA_ENTRY
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               VIF_ASSIGN;
+               STA_ASSIGN;
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT,
+               LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG
+       )
+);
+
+TRACE_EVENT(drv_conf_tx,
+       TP_PROTO(struct ieee80211_local *local, u16 queue,
+                const struct ieee80211_tx_queue_params *params,
                 int ret),
 
-       TP_ARGS(local, stats, ret),
+       TP_ARGS(local, queue, params, ret),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
+               __field(u16, queue)
+               __field(u16, txop)
+               __field(u16, cw_min)
+               __field(u16, cw_max)
+               __field(u8, aifs)
                __field(int, ret)
        ),
 
        TP_fast_assign(
                LOCAL_ASSIGN;
+               __entry->queue = queue;
                __entry->ret = ret;
+               __entry->txop = params->txop;
+               __entry->cw_max = params->cw_max;
+               __entry->cw_min = params->cw_min;
+               __entry->aifs = params->aifs;
        ),
 
        TP_printk(
-               LOCAL_PR_FMT " ret:%d",
-               LOCAL_PR_ARG, __entry->ret
+               LOCAL_PR_FMT " queue:%d ret:%d",
+               LOCAL_PR_ARG, __entry->queue, __entry->ret
        )
 );
 
index f95750b..f3e9424 100644 (file)
@@ -275,10 +275,12 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                                    (unsigned long long) supp_rates,
                                    (unsigned long long) sta->sta.supp_rates[band]);
 #endif
-               } else
-                       ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);
-
-               rcu_read_unlock();
+                       rcu_read_unlock();
+               } else {
+                       rcu_read_unlock();
+                       ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
+                                              supp_rates, GFP_KERNEL);
+               }
        }
 
        bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
@@ -368,7 +370,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                       sdata->name, mgmt->bssid);
 #endif
                ieee80211_sta_join_ibss(sdata, bss);
-               ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);
+               ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
+                                      supp_rates, GFP_KERNEL);
        }
 
  put_bss:
@@ -381,7 +384,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
  * must be callable in atomic context.
  */
 struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
-                                       u8 *bssid,u8 *addr, u32 supp_rates)
+                                       u8 *bssid,u8 *addr, u32 supp_rates,
+                                       gfp_t gfp)
 {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
        struct ieee80211_local *local = sdata->local;
@@ -410,7 +414,7 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
               wiphy_name(local->hw.wiphy), addr, sdata->name);
 #endif
 
-       sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
+       sta = sta_info_alloc(sdata, addr, gfp);
        if (!sta)
                return NULL;
 
@@ -422,9 +426,9 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
 
        rate_control_rate_init(sta);
 
+       /* If it fails, maybe we raced another insertion? */
        if (sta_info_insert(sta))
-               return NULL;
-
+               return sta_info_get(sdata, addr);
        return sta;
 }
 
@@ -652,7 +656,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
        }
        if (pos[1] != 0 &&
            (pos[1] != ifibss->ssid_len ||
-            !memcmp(pos + 2, ifibss->ssid, ifibss->ssid_len))) {
+            memcmp(pos + 2, ifibss->ssid, ifibss->ssid_len))) {
                /* Ignore ProbeReq for foreign SSID */
                return;
        }
index 3067fbd..9dd98b6 100644 (file)
@@ -316,6 +316,7 @@ enum ieee80211_sta_flags {
        IEEE80211_STA_CSA_RECEIVED      = BIT(5),
        IEEE80211_STA_MFP_ENABLED       = BIT(6),
        IEEE80211_STA_UAPSD_ENABLED     = BIT(7),
+       IEEE80211_STA_NULLFUNC_ACKED    = BIT(8),
 };
 
 struct ieee80211_if_managed {
@@ -688,15 +689,18 @@ struct ieee80211_local {
 
        /* Station data */
        /*
-        * The lock only protects the list, hash, timer and counter
-        * against manipulation, reads are done in RCU. Additionally,
-        * the lock protects each BSS's TIM bitmap.
+        * The mutex only protects the list and counter,
+        * reads are done in RCU.
+        * Additionally, the lock protects the hash table,
+        * the pending list and each BSS's TIM bitmap.
         */
+       struct mutex sta_mtx;
        spinlock_t sta_lock;
        unsigned long num_sta;
-       struct list_head sta_list;
+       struct list_head sta_list, sta_pending_list;
        struct sta_info *sta_hash[STA_HASH_SIZE];
        struct timer_list sta_cleanup;
+       struct work_struct sta_finish_work;
        int sta_generation;
 
        struct sk_buff_head pending[IEEE80211_MAX_QUEUES];
@@ -770,10 +774,6 @@ struct ieee80211_local {
             assoc_led_name[32], radio_led_name[32];
 #endif
 
-#ifdef CONFIG_MAC80211_DEBUGFS
-       struct work_struct sta_debugfs_add;
-#endif
-
 #ifdef CONFIG_MAC80211_DEBUG_COUNTERS
        /* TX/RX handler statistics */
        unsigned int tx_handlers_drop;
@@ -985,7 +985,8 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata);
 ieee80211_rx_result
 ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
 struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
-                                       u8 *bssid, u8 *addr, u32 supp_rates);
+                                       u8 *bssid, u8 *addr, u32 supp_rates,
+                                       gfp_t gfp);
 int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
                        struct cfg80211_ibss_params *params);
 int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata);
index 7985e51..bc4e20e 100644 (file)
@@ -102,7 +102,7 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
        if (local->num_sta >= MESH_MAX_PLINKS)
                return NULL;
 
-       sta = sta_info_alloc(sdata, hw_addr, GFP_ATOMIC);
+       sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
        if (!sta)
                return NULL;
 
@@ -236,12 +236,12 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data
 
        sta = sta_info_get(sdata, hw_addr);
        if (!sta) {
+               rcu_read_unlock();
+
                sta = mesh_plink_alloc(sdata, hw_addr, rates);
-               if (!sta) {
-                       rcu_read_unlock();
+               if (!sta)
                        return;
-               }
-               if (sta_info_insert(sta)) {
+               if (sta_info_insert_rcu(sta)) {
                        rcu_read_unlock();
                        return;
                }
@@ -485,9 +485,11 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
        } else if (!sta) {
                /* ftype == PLINK_OPEN */
                u32 rates;
+
+               rcu_read_unlock();
+
                if (!mesh_plink_free_count(sdata)) {
                        mpl_dbg("Mesh plink error: no more free plinks\n");
-                       rcu_read_unlock();
                        return;
                }
 
@@ -495,10 +497,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
                sta = mesh_plink_alloc(sdata, mgmt->sa, rates);
                if (!sta) {
                        mpl_dbg("Mesh plink error: plink table full\n");
-                       rcu_read_unlock();
                        return;
                }
-               if (sta_info_insert(sta)) {
+               if (sta_info_insert_rcu(sta)) {
                        rcu_read_unlock();
                        return;
                }
index 86c6ad1..bfc4a50 100644 (file)
 #include "rate.h"
 #include "led.h"
 
-#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
-#define IEEE80211_AUTH_MAX_TRIES 3
-#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
-#define IEEE80211_ASSOC_MAX_TRIES 3
 #define IEEE80211_MAX_PROBE_TRIES 5
 
 /*
@@ -438,8 +434,11 @@ static void ieee80211_enable_ps(struct ieee80211_local *local,
        } else {
                if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
                        ieee80211_send_nullfunc(local, sdata, 1);
-               conf->flags |= IEEE80211_CONF_PS;
-               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+
+               if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
+                       conf->flags |= IEEE80211_CONF_PS;
+                       ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+               }
        }
 }
 
@@ -545,6 +544,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
                container_of(work, struct ieee80211_local,
                             dynamic_ps_enable_work);
        struct ieee80211_sub_if_data *sdata = local->ps_sdata;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
        /* can only happen when PS was just disabled anyway */
        if (!sdata)
@@ -553,11 +553,16 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
        if (local->hw.conf.flags & IEEE80211_CONF_PS)
                return;
 
-       if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
+       if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
+           (!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)))
                ieee80211_send_nullfunc(local, sdata, 1);
 
-       local->hw.conf.flags |= IEEE80211_CONF_PS;
-       ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+       if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) ||
+           (ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) {
+               ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
+               local->hw.conf.flags |= IEEE80211_CONF_PS;
+               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+       }
 }
 
 void ieee80211_dynamic_ps_timer(unsigned long data)
@@ -792,8 +797,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
 
        rcu_read_lock();
        sta = sta_info_get(sdata, bssid);
-       if (sta)
+       if (sta) {
+               set_sta_flags(sta, WLAN_STA_DISASSOC);
                ieee80211_sta_tear_down_BA_sessions(sta);
+       }
        rcu_read_unlock();
 
        changed |= ieee80211_reset_erp_info(sdata);
@@ -826,19 +833,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
        changed |= BSS_CHANGED_BSSID;
        ieee80211_bss_info_change_notify(sdata, changed);
 
-       rcu_read_lock();
-
-       sta = sta_info_get(sdata, bssid);
-       if (!sta) {
-               rcu_read_unlock();
-               return;
-       }
-
-       sta_info_unlink(&sta);
-
-       rcu_read_unlock();
-
-       sta_info_destroy(sta);
+       sta_info_destroy_addr(sdata, bssid);
 }
 
 void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
@@ -1844,7 +1839,11 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
        wk->probe_auth.algorithm = auth_alg;
        wk->probe_auth.privacy = req->bss->capability & WLAN_CAPABILITY_PRIVACY;
 
-       wk->type = IEEE80211_WORK_DIRECT_PROBE;
+       /* if we already have a probe, don't probe again */
+       if (req->bss->proberesp_ies)
+               wk->type = IEEE80211_WORK_AUTH;
+       else
+               wk->type = IEEE80211_WORK_DIRECT_PROBE;
        wk->chan = req->bss->channel;
        wk->sdata = sdata;
        wk->done = ieee80211_probe_auth_done;
@@ -1904,6 +1903,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                return -ENOMEM;
 
        ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
+       ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
 
        for (i = 0; i < req->crypto.n_ciphers_pairwise; i++)
                if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
@@ -2007,12 +2007,18 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
 
                mutex_lock(&local->work_mtx);
                list_for_each_entry(wk, &local->work_list, list) {
-                       if (wk->type != IEEE80211_WORK_DIRECT_PROBE)
+                       if (wk->sdata != sdata)
+                               continue;
+
+                       if (wk->type != IEEE80211_WORK_DIRECT_PROBE &&
+                           wk->type != IEEE80211_WORK_AUTH)
                                continue;
+
                        if (memcmp(req->bss->bssid, wk->filter_ta, ETH_ALEN))
                                continue;
-                       not_auth_yet = true;
-                       list_del(&wk->list);
+
+                       not_auth_yet = wk->type == IEEE80211_WORK_DIRECT_PROBE;
+                       list_del_rcu(&wk->list);
                        free_work(wk);
                        break;
                }
index 47f8189..0e64484 100644 (file)
@@ -11,7 +11,6 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_sub_if_data *sdata;
        struct sta_info *sta;
-       unsigned long flags;
 
        ieee80211_scan_cancel(local);
 
@@ -55,22 +54,21 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
        rcu_read_unlock();
 
        /* remove STAs */
-       spin_lock_irqsave(&local->sta_lock, flags);
+       mutex_lock(&local->sta_mtx);
        list_for_each_entry(sta, &local->sta_list, list) {
-               if (local->ops->sta_notify) {
+               if (sta->uploaded) {
                        sdata = sta->sdata;
                        if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
                                sdata = container_of(sdata->bss,
                                             struct ieee80211_sub_if_data,
                                             u.ap);
 
-                       drv_sta_notify(local, sdata, STA_NOTIFY_REMOVE,
-                                      &sta->sta);
+                       drv_sta_remove(local, sdata, &sta->sta);
                }
 
                mesh_plink_quiesce(sta);
        }
-       spin_unlock_irqrestore(&local->sta_lock, flags);
+       mutex_unlock(&local->sta_mtx);
 
        /* remove all interfaces */
        list_for_each_entry(sdata, &local->interfaces, list) {
index c74b7c8..99ab24c 100644 (file)
@@ -145,7 +145,7 @@ static const struct file_operations rcname_ops = {
 };
 #endif
 
-struct rate_control_ref *rate_control_alloc(const char *name,
+static struct rate_control_ref *rate_control_alloc(const char *name,
                                            struct ieee80211_local *local)
 {
        struct dentry *debugfsdir = NULL;
index 998cf7a..b6108bc 100644 (file)
@@ -26,10 +26,6 @@ struct rate_control_ref {
        struct kref kref;
 };
 
-/* Get a reference to the rate control algorithm. If `name' is NULL, get the
- * first available algorithm. */
-struct rate_control_ref *rate_control_alloc(const char *name,
-                                           struct ieee80211_local *local);
 void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
                           struct sta_info *sta,
                           struct ieee80211_tx_rate_control *txrc);
@@ -116,7 +112,8 @@ static inline void rate_control_remove_sta_debugfs(struct sta_info *sta)
 #endif
 }
 
-/* functions for rate control related to a device */
+/* Get a reference to the rate control algorithm. If `name' is NULL, get the
+ * first available algorithm. */
 int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
                                 const char *name);
 void rate_control_deinitialize(struct ieee80211_local *local);
index 5709307..c9755f3 100644 (file)
@@ -1719,6 +1719,7 @@ static ieee80211_rx_result debug_noinline
 ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
 {
        struct ieee80211_sub_if_data *sdata = rx->sdata;
+       struct ieee80211_local *local = rx->local;
        struct net_device *dev = sdata->dev;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
        __le16 fc = hdr->frame_control;
@@ -1750,6 +1751,13 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
        dev->stats.rx_packets++;
        dev->stats.rx_bytes += rx->skb->len;
 
+       if (ieee80211_is_data(hdr->frame_control) &&
+           !is_multicast_ether_addr(hdr->addr1) &&
+           local->hw.conf.dynamic_ps_timeout > 0 && local->ps_sdata) {
+                       mod_timer(&local->dynamic_ps_timer, jiffies +
+                        msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
+       }
+
        ieee80211_deliver_skb(rx);
 
        return RX_QUEUED;
@@ -2244,8 +2252,8 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
                                rate_idx = 0; /* TODO: HT rates */
                        else
                                rate_idx = status->rate_idx;
-                       rx->sta = ieee80211_ibss_add_sta(sdata, bssid, hdr->addr2,
-                               BIT(rate_idx));
+                       rx->sta = ieee80211_ibss_add_sta(sdata, bssid,
+                                       hdr->addr2, BIT(rate_idx), GFP_ATOMIC);
                }
                break;
        case NL80211_IFTYPE_MESH_POINT:
index bc061f6..b822dce 100644 (file)
@@ -345,6 +345,13 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
        if (local->scan_req)
                return -EBUSY;
 
+       if (!list_empty(&local->work_list)) {
+               /* wait for the work to finish/time out */
+               local->scan_req = req;
+               local->scan_sdata = sdata;
+               return 0;
+       }
+
        if (local->ops->hw_scan) {
                u8 *ies;
 
@@ -364,29 +371,33 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
                local->hw_scan_req->ie = ies;
 
                local->hw_scan_band = 0;
+
+               /*
+                * After allocating local->hw_scan_req, we must
+                * go through until ieee80211_prep_hw_scan(), so
+                * anything that might be changed here and leave
+                * this function early must not go after this
+                * allocation.
+                */
        }
 
        local->scan_req = req;
        local->scan_sdata = sdata;
 
-       if (!list_empty(&local->work_list)) {
-               /* wait for the work to finish/time out */
-               return 0;
-       }
-
        if (local->ops->hw_scan)
                __set_bit(SCAN_HW_SCANNING, &local->scanning);
        else
                __set_bit(SCAN_SW_SCANNING, &local->scanning);
+
        /*
         * Kicking off the scan need not be protected,
         * only the scan variable stuff, since now
         * local->scan_req is assigned and other callers
         * will abort their scan attempts.
         *
-        * This avoids getting a scan_mtx -> iflist_mtx
-        * dependency, so that the scan completed calls
-        * have more locking freedom.
+        * This avoids too many locking dependencies
+        * so that the scan completed calls have more
+        * locking freedom.
         */
 
        ieee80211_recalc_idle(local);
index f735826..211c475 100644 (file)
  * for faster lookup and a list for iteration. They are managed using
  * RCU, i.e. access to the list and hash table is protected by RCU.
  *
- * Upon allocating a STA info structure with sta_info_alloc(), the caller owns
- * that structure. It must then either destroy it using sta_info_destroy()
- * (which is pretty useless) or insert it into the hash table using
- * sta_info_insert() which demotes the reference from ownership to a regular
- * RCU-protected reference; if the function is called without protection by an
- * RCU critical section the reference is instantly invalidated. Note that the
- * caller may not do much with the STA info before inserting it, in particular,
- * it may not start any mesh peer link management or add encryption keys.
+ * Upon allocating a STA info structure with sta_info_alloc(), the caller
+ * owns that structure. It must then insert it into the hash table using
+ * either sta_info_insert() or sta_info_insert_rcu(); only in the latter
+ * case (which acquires an rcu read section but must not be called from
+ * within one) will the pointer still be valid after the call. Note that
+ * the caller may not do much with the STA info before inserting it, in
+ * particular, it may not start any mesh peer link management or add
+ * encryption keys.
  *
  * When the insertion fails (sta_info_insert()) returns non-zero), the
  * structure will have been freed by sta_info_insert()!
  *
- * sta entries are added by mac80211 when you establish a link with a
+ * Station entries are added by mac80211 when you establish a link with a
  * peer. This means different things for the different type of interfaces
  * we support. For a regular station this mean we add the AP sta when we
  * receive an assocation response from the AP. For IBSS this occurs when
- * we receive a probe response or a beacon from target IBSS network. For
- * WDS we add the sta for the peer imediately upon device open. When using
- * AP mode we add stations for each respective station upon request from
- * userspace through nl80211.
+ * get to know about a peer on the same IBSS. For WDS we add the sta for
+ * the peer imediately upon device open. When using AP mode we add stations
+ * for each respective station upon request from userspace through nl80211.
  *
- * Because there are debugfs entries for each station, and adding those
- * must be able to sleep, it is also possible to "pin" a station entry,
- * that means it can be removed from the hash table but not be freed.
- * See the comment in __sta_info_unlink() for more information, this is
- * an internal capability only.
+ * In order to remove a STA info structure, various sta_info_destroy_*()
+ * calls are available.
  *
- * In order to remove a STA info structure, the caller needs to first
- * unlink it (sta_info_unlink()) from the list and hash tables and
- * then destroy it; sta_info_destroy() will wait for an RCU grace period
- * to elapse before actually freeing it. Due to the pinning and the
- * possibility of multiple callers trying to remove the same STA info at
- * the same time, sta_info_unlink() can clear the STA info pointer it is
- * passed to indicate that the STA info is owned by somebody else now.
- *
- * If sta_info_unlink() did not clear the pointer then the caller owns
- * the STA info structure now and is responsible of destroying it with
- * a call to sta_info_destroy().
- *
- * In all other cases, there is no concept of ownership on a STA entry,
- * each structure is owned by the global hash table/list until it is
- * removed. All users of the structure need to be RCU protected so that
- * the structure won't be freed before they are done using it.
+ * There is no concept of ownership on a STA entry, each structure is
+ * owned by the global hash table/list until it is removed. All users of
+ * the structure need to be RCU protected so that the structure won't be
+ * freed before they are done using it.
  */
 
 /* Caller must hold local->sta_lock */
@@ -185,101 +169,6 @@ static void __sta_info_free(struct ieee80211_local *local,
        kfree(sta);
 }
 
-void sta_info_destroy(struct sta_info *sta)
-{
-       struct ieee80211_local *local;
-       struct sk_buff *skb;
-       int i;
-
-       might_sleep();
-
-       if (!sta)
-               return;
-
-       local = sta->local;
-
-       cancel_work_sync(&sta->drv_unblock_wk);
-
-       rate_control_remove_sta_debugfs(sta);
-       ieee80211_sta_debugfs_remove(sta);
-
-#ifdef CONFIG_MAC80211_MESH
-       if (ieee80211_vif_is_mesh(&sta->sdata->vif))
-               mesh_plink_deactivate(sta);
-#endif
-
-       /*
-        * We have only unlinked the key, and actually destroying it
-        * may mean it is removed from hardware which requires that
-        * the key->sta pointer is still valid, so flush the key todo
-        * list here.
-        *
-        * ieee80211_key_todo() will synchronize_rcu() so after this
-        * nothing can reference this sta struct any more.
-        */
-       ieee80211_key_todo();
-
-#ifdef CONFIG_MAC80211_MESH
-       if (ieee80211_vif_is_mesh(&sta->sdata->vif))
-               del_timer_sync(&sta->plink_timer);
-#endif
-
-       while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
-               local->total_ps_buffered--;
-               dev_kfree_skb_any(skb);
-       }
-
-       while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL)
-               dev_kfree_skb_any(skb);
-
-       for (i = 0; i <  STA_TID_NUM; i++) {
-               struct tid_ampdu_rx *tid_rx;
-               struct tid_ampdu_tx *tid_tx;
-
-               spin_lock_bh(&sta->lock);
-               tid_rx = sta->ampdu_mlme.tid_rx[i];
-               /* Make sure timer won't free the tid_rx struct, see below */
-               if (tid_rx)
-                       tid_rx->shutdown = true;
-
-               spin_unlock_bh(&sta->lock);
-
-               /*
-                * Outside spinlock - shutdown is true now so that the timer
-                * won't free tid_rx, we have to do that now. Can't let the
-                * timer do it because we have to sync the timer outside the
-                * lock that it takes itself.
-                */
-               if (tid_rx) {
-                       del_timer_sync(&tid_rx->session_timer);
-                       kfree(tid_rx);
-               }
-
-               /*
-                * No need to do such complications for TX agg sessions, the
-                * path leading to freeing the tid_tx struct goes via a call
-                * from the driver, and thus needs to look up the sta struct
-                * again, which cannot be found when we get here. Hence, we
-                * just need to delete the timer and free the aggregation
-                * info; we won't be telling the peer about it then but that
-                * doesn't matter if we're not talking to it again anyway.
-                */
-               tid_tx = sta->ampdu_mlme.tid_tx[i];
-               if (tid_tx) {
-                       del_timer_sync(&tid_tx->addba_resp_timer);
-                       /*
-                        * STA removed while aggregation session being
-                        * started? Bit odd, but purge frames anyway.
-                        */
-                       skb_queue_purge(&tid_tx->pending);
-                       kfree(tid_tx);
-               }
-       }
-
-       __sta_info_free(local, sta);
-}
-
-
 /* Caller must hold local->sta_lock */
 static void sta_info_hash_add(struct ieee80211_local *local,
                              struct sta_info *sta)
@@ -376,7 +265,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        return sta;
 }
 
-int sta_info_insert(struct sta_info *sta)
+static int sta_info_finish_insert(struct sta_info *sta, bool async)
 {
        struct ieee80211_local *local = sta->local;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
@@ -384,6 +273,91 @@ int sta_info_insert(struct sta_info *sta)
        unsigned long flags;
        int err = 0;
 
+       WARN_ON(!mutex_is_locked(&local->sta_mtx));
+
+       /* notify driver */
+       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+               sdata = container_of(sdata->bss,
+                                    struct ieee80211_sub_if_data,
+                                    u.ap);
+       err = drv_sta_add(local, sdata, &sta->sta);
+       if (err) {
+               if (!async)
+                       return err;
+               printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to driver (%d)"
+                                 " - keeping it anyway.\n",
+                      sdata->name, sta->sta.addr, err);
+       } else {
+               sta->uploaded = true;
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+               if (async)
+                       printk(KERN_DEBUG "%s: Finished adding IBSS STA %pM\n",
+                              wiphy_name(local->hw.wiphy), sta->sta.addr);
+#endif
+       }
+
+       sdata = sta->sdata;
+
+       if (!async) {
+               local->num_sta++;
+               local->sta_generation++;
+               smp_mb();
+
+               /* make the station visible */
+               spin_lock_irqsave(&local->sta_lock, flags);
+               sta_info_hash_add(local, sta);
+               spin_unlock_irqrestore(&local->sta_lock, flags);
+       }
+
+       list_add(&sta->list, &local->sta_list);
+
+       ieee80211_sta_debugfs_add(sta);
+       rate_control_add_sta_debugfs(sta);
+
+       sinfo.filled = 0;
+       sinfo.generation = local->sta_generation;
+       cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL);
+
+
+       return 0;
+}
+
+static void sta_info_finish_pending(struct ieee80211_local *local)
+{
+       struct sta_info *sta;
+       unsigned long flags;
+
+       spin_lock_irqsave(&local->sta_lock, flags);
+       while (!list_empty(&local->sta_pending_list)) {
+               sta = list_first_entry(&local->sta_pending_list,
+                                      struct sta_info, list);
+               list_del(&sta->list);
+               spin_unlock_irqrestore(&local->sta_lock, flags);
+
+               sta_info_finish_insert(sta, true);
+
+               spin_lock_irqsave(&local->sta_lock, flags);
+       }
+       spin_unlock_irqrestore(&local->sta_lock, flags);
+}
+
+static void sta_info_finish_work(struct work_struct *work)
+{
+       struct ieee80211_local *local =
+               container_of(work, struct ieee80211_local, sta_finish_work);
+
+       mutex_lock(&local->sta_mtx);
+       sta_info_finish_pending(local);
+       mutex_unlock(&local->sta_mtx);
+}
+
+int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
+{
+       struct ieee80211_local *local = sta->local;
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       unsigned long flags;
+       int err = 0;
+
        /*
         * Can't be a WARN_ON because it can be triggered through a race:
         * something inserts a STA (on one CPU) without holding the RTNL
@@ -391,36 +365,87 @@ int sta_info_insert(struct sta_info *sta)
         */
        if (unlikely(!ieee80211_sdata_running(sdata))) {
                err = -ENETDOWN;
+               rcu_read_lock();
                goto out_free;
        }
 
        if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->vif.addr) == 0 ||
                    is_multicast_ether_addr(sta->sta.addr))) {
                err = -EINVAL;
+               rcu_read_lock();
                goto out_free;
        }
 
+       /*
+        * In ad-hoc mode, we sometimes need to insert stations
+        * from tasklet context from the RX path. To avoid races,
+        * always do so in that case -- see the comment below.
+        */
+       if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+               spin_lock_irqsave(&local->sta_lock, flags);
+               /* check if STA exists already */
+               if (sta_info_get_bss(sdata, sta->sta.addr)) {
+                       spin_unlock_irqrestore(&local->sta_lock, flags);
+                       rcu_read_lock();
+                       err = -EEXIST;
+                       goto out_free;
+               }
+
+               local->num_sta++;
+               local->sta_generation++;
+               smp_mb();
+               sta_info_hash_add(local, sta);
+
+               list_add_tail(&sta->list, &local->sta_pending_list);
+
+               rcu_read_lock();
+               spin_unlock_irqrestore(&local->sta_lock, flags);
+
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+               printk(KERN_DEBUG "%s: Added IBSS STA %pM\n",
+                      wiphy_name(local->hw.wiphy), sta->sta.addr);
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
+
+               ieee80211_queue_work(&local->hw, &local->sta_finish_work);
+
+               return 0;
+       }
+
+       /*
+        * On first glance, this will look racy, because the code
+        * below this point, which inserts a station with sleeping,
+        * unlocks the sta_lock between checking existence in the
+        * hash table and inserting into it.
+        *
+        * However, it is not racy against itself because it keeps
+        * the mutex locked. It still seems to race against the
+        * above code that atomically inserts the station... That,
+        * however, is not true because the above code can only
+        * be invoked for IBSS interfaces, and the below code will
+        * not be -- and the two do not race against each other as
+        * the hash table also keys off the interface.
+        */
+
+       might_sleep();
+
+       mutex_lock(&local->sta_mtx);
+
        spin_lock_irqsave(&local->sta_lock, flags);
        /* check if STA exists already */
-       if (sta_info_get(sdata, sta->sta.addr)) {
+       if (sta_info_get_bss(sdata, sta->sta.addr)) {
                spin_unlock_irqrestore(&local->sta_lock, flags);
+               rcu_read_lock();
                err = -EEXIST;
                goto out_free;
        }
-       list_add(&sta->list, &local->sta_list);
-       local->sta_generation++;
-       local->num_sta++;
-       sta_info_hash_add(local, sta);
 
-       /* notify driver */
-       if (local->ops->sta_notify) {
-               if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
-                       sdata = container_of(sdata->bss,
-                                            struct ieee80211_sub_if_data,
-                                            u.ap);
+       spin_unlock_irqrestore(&local->sta_lock, flags);
 
-               drv_sta_notify(local, sdata, STA_NOTIFY_ADD, &sta->sta);
-               sdata = sta->sdata;
+       err = sta_info_finish_insert(sta, false);
+       if (err) {
+               mutex_unlock(&local->sta_mtx);
+               rcu_read_lock();
+               goto out_free;
        }
 
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
@@ -428,22 +453,9 @@ int sta_info_insert(struct sta_info *sta)
               wiphy_name(local->hw.wiphy), sta->sta.addr);
 #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 
-       spin_unlock_irqrestore(&local->sta_lock, flags);
-
-       sinfo.filled = 0;
-       sinfo.generation = local->sta_generation;
-       cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_ATOMIC);
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       /*
-        * Debugfs entry adding might sleep, so schedule process
-        * context task for adding entry for STAs that do not yet
-        * have one.
-        * NOTE: due to auto-freeing semantics this may only be done
-        *       if the insertion is successful!
-        */
-       schedule_work(&local->sta_debugfs_add);
-#endif
+       /* move reference to rcu-protected */
+       rcu_read_lock();
+       mutex_unlock(&local->sta_mtx);
 
        if (ieee80211_vif_is_mesh(&sdata->vif))
                mesh_accept_plinks_update(sdata);
@@ -455,6 +467,15 @@ int sta_info_insert(struct sta_info *sta)
        return err;
 }
 
+int sta_info_insert(struct sta_info *sta)
+{
+       int err = sta_info_insert_rcu(sta);
+
+       rcu_read_unlock();
+
+       return err;
+}
+
 static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
 {
        /*
@@ -523,108 +544,6 @@ void sta_info_clear_tim_bit(struct sta_info *sta)
        spin_unlock_irqrestore(&sta->local->sta_lock, flags);
 }
 
-static void __sta_info_unlink(struct sta_info **sta)
-{
-       struct ieee80211_local *local = (*sta)->local;
-       struct ieee80211_sub_if_data *sdata = (*sta)->sdata;
-       /*
-        * pull caller's reference if we're already gone.
-        */
-       if (sta_info_hash_del(local, *sta)) {
-               *sta = NULL;
-               return;
-       }
-
-       if ((*sta)->key) {
-               ieee80211_key_free((*sta)->key);
-               WARN_ON((*sta)->key);
-       }
-
-       list_del(&(*sta)->list);
-       (*sta)->dead = true;
-
-       if (test_and_clear_sta_flags(*sta,
-                               WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) {
-               BUG_ON(!sdata->bss);
-
-               atomic_dec(&sdata->bss->num_sta_ps);
-               __sta_info_clear_tim_bit(sdata->bss, *sta);
-       }
-
-       local->num_sta--;
-       local->sta_generation++;
-
-       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
-               rcu_assign_pointer(sdata->u.vlan.sta, NULL);
-
-       if (local->ops->sta_notify) {
-               if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
-                       sdata = container_of(sdata->bss,
-                                            struct ieee80211_sub_if_data,
-                                            u.ap);
-
-               drv_sta_notify(local, sdata, STA_NOTIFY_REMOVE,
-                              &(*sta)->sta);
-               sdata = (*sta)->sdata;
-       }
-
-       if (ieee80211_vif_is_mesh(&sdata->vif)) {
-               mesh_accept_plinks_update(sdata);
-#ifdef CONFIG_MAC80211_MESH
-               del_timer(&(*sta)->plink_timer);
-#endif
-       }
-
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-       printk(KERN_DEBUG "%s: Removed STA %pM\n",
-              wiphy_name(local->hw.wiphy), (*sta)->sta.addr);
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
-
-       /*
-        * Finally, pull caller's reference if the STA is pinned by the
-        * task that is adding the debugfs entries. In that case, we
-        * leave the STA "to be freed".
-        *
-        * The rules are not trivial, but not too complex either:
-        *  (1) pin_status is only modified under the sta_lock
-        *  (2) STAs may only be pinned under the RTNL so that
-        *      sta_info_flush() is guaranteed to actually destroy
-        *      all STAs that are active for a given interface, this
-        *      is required for correctness because otherwise we
-        *      could notify a driver that an interface is going
-        *      away and only after that (!) notify it about a STA
-        *      on that interface going away.
-        *  (3) sta_info_debugfs_add_work() will set the status
-        *      to PINNED when it found an item that needs a new
-        *      debugfs directory created. In that case, that item
-        *      must not be freed although all *RCU* users are done
-        *      with it. Hence, we tell the caller of _unlink()
-        *      that the item is already gone (as can happen when
-        *      two tasks try to unlink/destroy at the same time)
-        *  (4) We set the pin_status to DESTROY here when we
-        *      find such an item.
-        *  (5) sta_info_debugfs_add_work() will reset the pin_status
-        *      from PINNED to NORMAL when it is done with the item,
-        *      but will check for DESTROY before resetting it in
-        *      which case it will free the item.
-        */
-       if ((*sta)->pin_status == STA_INFO_PIN_STAT_PINNED) {
-               (*sta)->pin_status = STA_INFO_PIN_STAT_DESTROY;
-               *sta = NULL;
-               return;
-       }
-}
-
-void sta_info_unlink(struct sta_info **sta)
-{
-       struct ieee80211_local *local = (*sta)->local;
-       unsigned long flags;
-
-       spin_lock_irqsave(&local->sta_lock, flags);
-       __sta_info_unlink(sta);
-       spin_unlock_irqrestore(&local->sta_lock, flags);
-}
-
 static int sta_info_buffer_expired(struct sta_info *sta,
                                   struct sk_buff *skb)
 {
@@ -681,109 +600,209 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
        }
 }
 
-
-static void sta_info_cleanup(unsigned long data)
+static int __must_check __sta_info_destroy(struct sta_info *sta)
 {
-       struct ieee80211_local *local = (struct ieee80211_local *) data;
-       struct sta_info *sta;
+       struct ieee80211_local *local;
+       struct ieee80211_sub_if_data *sdata;
+       struct sk_buff *skb;
+       unsigned long flags;
+       int ret, i;
 
-       rcu_read_lock();
-       list_for_each_entry_rcu(sta, &local->sta_list, list)
-               sta_info_cleanup_expire_buffered(local, sta);
-       rcu_read_unlock();
+       might_sleep();
 
-       if (local->quiescing)
-               return;
+       if (!sta)
+               return -ENOENT;
 
-       local->sta_cleanup.expires =
-               round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
-       add_timer(&local->sta_cleanup);
-}
+       local = sta->local;
+       sdata = sta->sdata;
 
-#ifdef CONFIG_MAC80211_DEBUGFS
-/*
- * See comment in __sta_info_unlink,
- * caller must hold local->sta_lock.
- */
-static void __sta_info_pin(struct sta_info *sta)
-{
-       WARN_ON(sta->pin_status != STA_INFO_PIN_STAT_NORMAL);
-       sta->pin_status = STA_INFO_PIN_STAT_PINNED;
+       spin_lock_irqsave(&local->sta_lock, flags);
+       ret = sta_info_hash_del(local, sta);
+       /* this might still be the pending list ... which is fine */
+       if (!ret)
+               list_del(&sta->list);
+       spin_unlock_irqrestore(&local->sta_lock, flags);
+       if (ret)
+               return ret;
+
+       if (sta->key) {
+               ieee80211_key_free(sta->key);
+               /*
+                * We have only unlinked the key, and actually destroying it
+                * may mean it is removed from hardware which requires that
+                * the key->sta pointer is still valid, so flush the key todo
+                * list here.
+                *
+                * ieee80211_key_todo() will synchronize_rcu() so after this
+                * nothing can reference this sta struct any more.
+                */
+               ieee80211_key_todo();
+
+               WARN_ON(sta->key);
+       }
+
+       sta->dead = true;
+
+       if (test_and_clear_sta_flags(sta,
+                               WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) {
+               BUG_ON(!sdata->bss);
+
+               atomic_dec(&sdata->bss->num_sta_ps);
+               __sta_info_clear_tim_bit(sdata->bss, sta);
+       }
+
+       local->num_sta--;
+       local->sta_generation++;
+
+       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+               rcu_assign_pointer(sdata->u.vlan.sta, NULL);
+
+       if (sta->uploaded) {
+               if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+                       sdata = container_of(sdata->bss,
+                                            struct ieee80211_sub_if_data,
+                                            u.ap);
+               drv_sta_remove(local, sdata, &sta->sta);
+               sdata = sta->sdata;
+       }
+
+#ifdef CONFIG_MAC80211_MESH
+       if (ieee80211_vif_is_mesh(&sdata->vif)) {
+               mesh_accept_plinks_update(sdata);
+               del_timer(&sta->plink_timer);
+       }
+#endif
+
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+       printk(KERN_DEBUG "%s: Removed STA %pM\n",
+              wiphy_name(local->hw.wiphy), sta->sta.addr);
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
+       cancel_work_sync(&sta->drv_unblock_wk);
+
+       rate_control_remove_sta_debugfs(sta);
+       ieee80211_sta_debugfs_remove(sta);
+
+#ifdef CONFIG_MAC80211_MESH
+       if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {
+               mesh_plink_deactivate(sta);
+               del_timer_sync(&sta->plink_timer);
+       }
+#endif
+
+       while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
+               local->total_ps_buffered--;
+               dev_kfree_skb_any(skb);
+       }
+
+       while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL)
+               dev_kfree_skb_any(skb);
+
+       for (i = 0; i <  STA_TID_NUM; i++) {
+               struct tid_ampdu_rx *tid_rx;
+               struct tid_ampdu_tx *tid_tx;
+
+               spin_lock_bh(&sta->lock);
+               tid_rx = sta->ampdu_mlme.tid_rx[i];
+               /* Make sure timer won't free the tid_rx struct, see below */
+               if (tid_rx)
+                       tid_rx->shutdown = true;
+
+               spin_unlock_bh(&sta->lock);
+
+               /*
+                * Outside spinlock - shutdown is true now so that the timer
+                * won't free tid_rx, we have to do that now. Can't let the
+                * timer do it because we have to sync the timer outside the
+                * lock that it takes itself.
+                */
+               if (tid_rx) {
+                       del_timer_sync(&tid_rx->session_timer);
+                       kfree(tid_rx);
+               }
+
+               /*
+                * No need to do such complications for TX agg sessions, the
+                * path leading to freeing the tid_tx struct goes via a call
+                * from the driver, and thus needs to look up the sta struct
+                * again, which cannot be found when we get here. Hence, we
+                * just need to delete the timer and free the aggregation
+                * info; we won't be telling the peer about it then but that
+                * doesn't matter if we're not talking to it again anyway.
+                */
+               tid_tx = sta->ampdu_mlme.tid_tx[i];
+               if (tid_tx) {
+                       del_timer_sync(&tid_tx->addba_resp_timer);
+                       /*
+                        * STA removed while aggregation session being
+                        * started? Bit odd, but purge frames anyway.
+                        */
+                       skb_queue_purge(&tid_tx->pending);
+                       kfree(tid_tx);
+               }
+       }
+
+       __sta_info_free(local, sta);
+
+       return 0;
 }
 
-/*
- * See comment in __sta_info_unlink, returns sta if it
- * needs to be destroyed.
- */
-static struct sta_info *__sta_info_unpin(struct sta_info *sta)
+int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata, const u8 *addr)
 {
-       struct sta_info *ret = NULL;
-       unsigned long flags;
+       struct sta_info *sta;
+       int ret;
 
-       spin_lock_irqsave(&sta->local->sta_lock, flags);
-       WARN_ON(sta->pin_status != STA_INFO_PIN_STAT_DESTROY &&
-               sta->pin_status != STA_INFO_PIN_STAT_PINNED);
-       if (sta->pin_status == STA_INFO_PIN_STAT_DESTROY)
-               ret = sta;
-       sta->pin_status = STA_INFO_PIN_STAT_NORMAL;
-       spin_unlock_irqrestore(&sta->local->sta_lock, flags);
+       mutex_lock(&sdata->local->sta_mtx);
+       sta = sta_info_get(sdata, addr);
+       ret = __sta_info_destroy(sta);
+       mutex_unlock(&sdata->local->sta_mtx);
 
        return ret;
 }
 
-static void sta_info_debugfs_add_work(struct work_struct *work)
+int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata,
+                             const u8 *addr)
 {
-       struct ieee80211_local *local =
-               container_of(work, struct ieee80211_local, sta_debugfs_add);
-       struct sta_info *sta, *tmp;
-       unsigned long flags;
+       struct sta_info *sta;
+       int ret;
 
-       /* We need to keep the RTNL across the whole pinned status. */
-       rtnl_lock();
-       while (1) {
-               sta = NULL;
+       mutex_lock(&sdata->local->sta_mtx);
+       sta = sta_info_get_bss(sdata, addr);
+       ret = __sta_info_destroy(sta);
+       mutex_unlock(&sdata->local->sta_mtx);
 
-               spin_lock_irqsave(&local->sta_lock, flags);
-               list_for_each_entry(tmp, &local->sta_list, list) {
-                       /*
-                        * debugfs.add_has_run will be set by
-                        * ieee80211_sta_debugfs_add regardless
-                        * of what else it does.
-                        */
-                       if (!tmp->debugfs.add_has_run) {
-                               sta = tmp;
-                               __sta_info_pin(sta);
-                               break;
-                       }
-               }
-               spin_unlock_irqrestore(&local->sta_lock, flags);
+       return ret;
+}
 
-               if (!sta)
-                       break;
+static void sta_info_cleanup(unsigned long data)
+{
+       struct ieee80211_local *local = (struct ieee80211_local *) data;
+       struct sta_info *sta;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(sta, &local->sta_list, list)
+               sta_info_cleanup_expire_buffered(local, sta);
+       rcu_read_unlock();
 
-               ieee80211_sta_debugfs_add(sta);
-               rate_control_add_sta_debugfs(sta);
+       if (local->quiescing)
+               return;
 
-               sta = __sta_info_unpin(sta);
-               sta_info_destroy(sta);
-       }
-       rtnl_unlock();
+       local->sta_cleanup.expires =
+               round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
+       add_timer(&local->sta_cleanup);
 }
-#endif
 
 void sta_info_init(struct ieee80211_local *local)
 {
        spin_lock_init(&local->sta_lock);
+       mutex_init(&local->sta_mtx);
        INIT_LIST_HEAD(&local->sta_list);
+       INIT_LIST_HEAD(&local->sta_pending_list);
+       INIT_WORK(&local->sta_finish_work, sta_info_finish_work);
 
        setup_timer(&local->sta_cleanup, sta_info_cleanup,
                    (unsigned long)local);
        local->sta_cleanup.expires =
                round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       INIT_WORK(&local->sta_debugfs_add, sta_info_debugfs_add_work);
-#endif
 }
 
 int sta_info_start(struct ieee80211_local *local)
@@ -795,16 +814,6 @@ int sta_info_start(struct ieee80211_local *local)
 void sta_info_stop(struct ieee80211_local *local)
 {
        del_timer(&local->sta_cleanup);
-#ifdef CONFIG_MAC80211_DEBUGFS
-       /*
-        * Make sure the debugfs adding work isn't pending after this
-        * because we're about to be destroyed. It doesn't matter
-        * whether it ran or not since we're going to flush all STAs
-        * anyway.
-        */
-       cancel_work_sync(&local->sta_debugfs_add);
-#endif
-
        sta_info_flush(local, NULL);
 }
 
@@ -820,26 +829,19 @@ int sta_info_flush(struct ieee80211_local *local,
                   struct ieee80211_sub_if_data *sdata)
 {
        struct sta_info *sta, *tmp;
-       LIST_HEAD(tmp_list);
        int ret = 0;
-       unsigned long flags;
 
        might_sleep();
 
-       spin_lock_irqsave(&local->sta_lock, flags);
+       mutex_lock(&local->sta_mtx);
+
+       sta_info_finish_pending(local);
+
        list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
-               if (!sdata || sdata == sta->sdata) {
-                       __sta_info_unlink(&sta);
-                       if (sta) {
-                               list_add_tail(&sta->list, &tmp_list);
-                               ret++;
-                       }
-               }
+               if (!sdata || sdata == sta->sdata)
+                       WARN_ON(__sta_info_destroy(sta));
        }
-       spin_unlock_irqrestore(&local->sta_lock, flags);
-
-       list_for_each_entry_safe(sta, tmp, &tmp_list, list)
-               sta_info_destroy(sta);
+       mutex_unlock(&local->sta_mtx);
 
        return ret;
 }
@@ -849,24 +851,17 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct sta_info *sta, *tmp;
-       LIST_HEAD(tmp_list);
-       unsigned long flags;
 
-       spin_lock_irqsave(&local->sta_lock, flags);
+       mutex_lock(&local->sta_mtx);
        list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
                if (time_after(jiffies, sta->last_rx + exp_time)) {
 #ifdef CONFIG_MAC80211_IBSS_DEBUG
                        printk(KERN_DEBUG "%s: expiring inactive STA %pM\n",
                               sdata->name, sta->sta.addr);
 #endif
-                       __sta_info_unlink(&sta);
-                       if (sta)
-                               list_add(&sta->list, &tmp_list);
+                       WARN_ON(__sta_info_destroy(sta));
                }
-       spin_unlock_irqrestore(&local->sta_lock, flags);
-
-       list_for_each_entry_safe(sta, tmp, &tmp_list, list)
-               sta_info_destroy(sta);
+       mutex_unlock(&local->sta_mtx);
 }
 
 struct ieee80211_sta *ieee80211_find_sta_by_hw(struct ieee80211_hw *hw,
index 6f79bba..822d845 100644 (file)
@@ -42,6 +42,9 @@
  *     be in the queues
  * @WLAN_STA_PSPOLL: Station sent PS-poll while driver was keeping
  *     station in power-save mode, reply when the driver unblocks.
+ * @WLAN_STA_DISASSOC: Disassociation in progress.
+ *     This is used to reject TX BA session requests when disassociation
+ *     is in progress.
  */
 enum ieee80211_sta_info_flags {
        WLAN_STA_AUTH           = 1<<0,
@@ -57,6 +60,7 @@ enum ieee80211_sta_info_flags {
        WLAN_STA_SUSPEND        = 1<<11,
        WLAN_STA_PS_DRIVER      = 1<<12,
        WLAN_STA_PSPOLL         = 1<<13,
+       WLAN_STA_DISASSOC       = 1<<14,
 };
 
 #define STA_TID_NUM 16
@@ -162,11 +166,6 @@ struct sta_ampdu_mlme {
 };
 
 
-/* see __sta_info_unlink */
-#define STA_INFO_PIN_STAT_NORMAL       0
-#define STA_INFO_PIN_STAT_PINNED       1
-#define STA_INFO_PIN_STAT_DESTROY      2
-
 /**
  * struct sta_info - STA information
  *
@@ -187,7 +186,6 @@ struct sta_ampdu_mlme {
  * @flaglock: spinlock for flags accesses
  * @drv_unblock_wk: used for driver PS unblocking
  * @listen_interval: listen interval of this station, when we're acting as AP
- * @pin_status: used internally for pinning a STA struct into memory
  * @flags: STA flags, see &enum ieee80211_sta_info_flags
  * @ps_tx_buf: buffer of frames to transmit to this station
  *     when it leaves power saving state
@@ -226,6 +224,7 @@ struct sta_ampdu_mlme {
  * @debugfs: debug filesystem info
  * @sta: station information we share with the driver
  * @dead: set to true when sta is unlinked
+ * @uploaded: set to true when sta is uploaded to the driver
  */
 struct sta_info {
        /* General information, mostly static */
@@ -245,11 +244,7 @@ struct sta_info {
 
        bool dead;
 
-       /*
-        * for use by the internal lifetime management,
-        * see __sta_info_unlink
-        */
-       u8 pin_status;
+       bool uploaded;
 
        /*
         * frequently updated, locked with own spinlock (flaglock),
@@ -449,18 +444,19 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
  * Insert STA info into hash table/list, returns zero or a
  * -EEXIST if (if the same MAC address is already present).
  *
- * Calling this without RCU protection makes the caller
- * relinquish its reference to @sta.
+ * Calling the non-rcu version makes the caller relinquish,
+ * the _rcu version calls read_lock_rcu() and must be called
+ * without it held.
  */
 int sta_info_insert(struct sta_info *sta);
-/*
- * Unlink a STA info from the hash table/list.
- * This can NULL the STA pointer if somebody else
- * has already unlinked it.
- */
-void sta_info_unlink(struct sta_info **sta);
+int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU);
+int sta_info_insert_atomic(struct sta_info *sta);
+
+int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata,
+                         const u8 *addr);
+int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata,
+                             const u8 *addr);
 
-void sta_info_destroy(struct sta_info *sta);
 void sta_info_set_tim_bit(struct sta_info *sta);
 void sta_info_clear_tim_bit(struct sta_info *sta);
 
index e57ad6b..ded9873 100644 (file)
@@ -188,6 +188,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
        rcu_read_lock();
 
        sband = local->hw.wiphy->bands[info->band];
+       fc = hdr->frame_control;
 
        for_each_sta_info(local, hdr->addr1, sta, tmp) {
                /* skip wrong virtual interface */
@@ -205,8 +206,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                        return;
                }
 
-               fc = hdr->frame_control;
-
                if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) &&
                    (ieee80211_is_data_qos(fc))) {
                        u16 tid, ssn;
@@ -275,6 +274,20 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                        local->dot11FailedCount++;
        }
 
+       if (ieee80211_is_nullfunc(fc) && ieee80211_has_pm(fc) &&
+           (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) &&
+           !(info->flags & IEEE80211_TX_CTL_INJECTED) &&
+           local->ps_sdata && !(local->scanning)) {
+               if (info->flags & IEEE80211_TX_STAT_ACK) {
+                       local->ps_sdata->u.mgd.flags |=
+                                       IEEE80211_STA_NULLFUNC_ACKED;
+                       ieee80211_queue_work(&local->hw,
+                                       &local->dynamic_ps_enable_work);
+               } else
+                       mod_timer(&local->dynamic_ps_timer, jiffies +
+                                       msecs_to_jiffies(10));
+       }
+
        /* this was a transmitted frame, but now we want to reuse it */
        skb_orphan(skb);
 
index 85e382a..cbe53ed 100644 (file)
@@ -571,7 +571,7 @@ ieee80211_tx_h_sta(struct ieee80211_tx_data *tx)
 {
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
 
-       if (tx->sta)
+       if (tx->sta && tx->sta->uploaded)
                info->control.sta = &tx->sta->sta;
 
        return TX_CONTINUE;
@@ -1010,7 +1010,8 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
                (struct ieee80211_radiotap_header *) skb->data;
        struct ieee80211_supported_band *sband;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-       int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
+       int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len,
+                                                  NULL);
 
        sband = tx->local->hw.wiphy->bands[tx->channel->band];
 
@@ -1046,7 +1047,7 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
                                 * because it will be recomputed and added
                                 * on transmission
                                 */
-                               if (skb->len < (iterator.max_length + FCS_LEN))
+                               if (skb->len < (iterator._max_length + FCS_LEN))
                                        return false;
 
                                skb_trim(skb, skb->len - FCS_LEN);
@@ -1073,10 +1074,10 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
 
        /*
         * remove the radiotap header
-        * iterator->max_length was sanity-checked against
+        * iterator->_max_length was sanity-checked against
         * skb->len by iterator init
         */
-       skb_pull(skb, iterator.max_length);
+       skb_pull(skb, iterator._max_length);
 
        return true;
 }
index ca170b4..c453226 100644 (file)
@@ -1082,7 +1082,6 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        struct ieee80211_hw *hw = &local->hw;
        struct ieee80211_sub_if_data *sdata;
        struct sta_info *sta;
-       unsigned long flags;
        int res;
 
        if (local->suspended)
@@ -1116,20 +1115,19 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        }
 
        /* add STAs back */
-       if (local->ops->sta_notify) {
-               spin_lock_irqsave(&local->sta_lock, flags);
-               list_for_each_entry(sta, &local->sta_list, list) {
+       mutex_lock(&local->sta_mtx);
+       list_for_each_entry(sta, &local->sta_list, list) {
+               if (sta->uploaded) {
                        sdata = sta->sdata;
                        if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
                                sdata = container_of(sdata->bss,
                                             struct ieee80211_sub_if_data,
                                             u.ap);
 
-                       drv_sta_notify(local, sdata, STA_NOTIFY_ADD,
-                                      &sta->sta);
+                       WARN_ON(drv_sta_add(local, sdata, &sta->sta));
                }
-               spin_unlock_irqrestore(&local->sta_lock, flags);
        }
+       mutex_unlock(&local->sta_mtx);
 
        /* Clear Suspend state so that ADDBA requests can be processed */
 
@@ -1180,6 +1178,14 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                }
        }
 
+       rcu_read_lock();
+       if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
+               list_for_each_entry_rcu(sta, &local->sta_list, list) {
+                       ieee80211_sta_tear_down_BA_sessions(sta);
+               }
+       }
+       rcu_read_unlock();
+
        /* add back keys */
        list_for_each_entry(sdata, &local->interfaces, list)
                if (ieee80211_sdata_running(sdata))
@@ -1219,10 +1225,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
 
        add_timer(&local->sta_cleanup);
 
-       spin_lock_irqsave(&local->sta_lock, flags);
+       mutex_lock(&local->sta_mtx);
        list_for_each_entry(sta, &local->sta_list, list)
                mesh_plink_restart(sta);
-       spin_unlock_irqrestore(&local->sta_lock, flags);
+       mutex_unlock(&local->sta_mtx);
 #else
        WARN_ON(1);
 #endif
index f591871..1332c44 100644 (file)
@@ -2,6 +2,16 @@
  * Radiotap parser
  *
  * Copyright 2007              Andy Green <andy@warmcat.com>
+ * Copyright 2009              Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See COPYING for more details.
  */
 
 #include <net/cfg80211.h>
 
 /* function prototypes and related defs are in include/net/cfg80211.h */
 
+static const struct radiotap_align_size rtap_namespace_sizes[] = {
+       [IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, },
+       [IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, },
+       [IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, },
+       [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, },
+       [IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, },
+       [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, },
+       [IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, },
+       [IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, },
+       [IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, },
+       /*
+        * add more here as they are defined in radiotap.h
+        */
+};
+
+static const struct ieee80211_radiotap_namespace radiotap_ns = {
+       .n_bits = sizeof(rtap_namespace_sizes) / sizeof(rtap_namespace_sizes[0]),
+       .align_size = rtap_namespace_sizes,
+};
+
 /**
  * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization
  * @iterator: radiotap_iterator to initialize
@@ -50,9 +89,9 @@
  */
 
 int ieee80211_radiotap_iterator_init(
-    struct ieee80211_radiotap_iterator *iterator,
-    struct ieee80211_radiotap_header *radiotap_header,
-    int max_length)
+       struct ieee80211_radiotap_iterator *iterator,
+       struct ieee80211_radiotap_header *radiotap_header,
+       int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns)
 {
        /* Linux only supports version 0 radiotap format */
        if (radiotap_header->it_version)
@@ -62,19 +101,24 @@ int ieee80211_radiotap_iterator_init(
        if (max_length < get_unaligned_le16(&radiotap_header->it_len))
                return -EINVAL;
 
-       iterator->rtheader = radiotap_header;
-       iterator->max_length = get_unaligned_le16(&radiotap_header->it_len);
-       iterator->arg_index = 0;
-       iterator->bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present);
-       iterator->arg = (u8 *)radiotap_header + sizeof(*radiotap_header);
-       iterator->this_arg = NULL;
+       iterator->_rtheader = radiotap_header;
+       iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len);
+       iterator->_arg_index = 0;
+       iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present);
+       iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header);
+       iterator->_reset_on_ext = 0;
+       iterator->_next_bitmap = &radiotap_header->it_present;
+       iterator->_next_bitmap++;
+       iterator->_vns = vns;
+       iterator->current_namespace = &radiotap_ns;
+       iterator->is_radiotap_ns = 1;
 
        /* find payload start allowing for extended bitmap(s) */
 
-       if (unlikely(iterator->bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT))) {
-               while (get_unaligned_le32(iterator->arg) &
-                      (1 << IEEE80211_RADIOTAP_EXT)) {
-                       iterator->arg += sizeof(u32);
+       if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) {
+               while (get_unaligned_le32(iterator->_arg) &
+                                       (1 << IEEE80211_RADIOTAP_EXT)) {
+                       iterator->_arg += sizeof(uint32_t);
 
                        /*
                         * check for insanity where the present bitmaps
@@ -82,12 +126,13 @@ int ieee80211_radiotap_iterator_init(
                         * stated radiotap header length
                         */
 
-                       if (((ulong)iterator->arg -
-                            (ulong)iterator->rtheader) > iterator->max_length)
+                       if ((unsigned long)iterator->_arg -
+                           (unsigned long)iterator->_rtheader >
+                           (unsigned long)iterator->_max_length)
                                return -EINVAL;
                }
 
-               iterator->arg += sizeof(u32);
+               iterator->_arg += sizeof(uint32_t);
 
                /*
                 * no need to check again for blowing past stated radiotap
@@ -96,12 +141,36 @@ int ieee80211_radiotap_iterator_init(
                 */
        }
 
+       iterator->this_arg = iterator->_arg;
+
        /* we are all initialized happily */
 
        return 0;
 }
 EXPORT_SYMBOL(ieee80211_radiotap_iterator_init);
 
+static void find_ns(struct ieee80211_radiotap_iterator *iterator,
+                   uint32_t oui, uint8_t subns)
+{
+       int i;
+
+       iterator->current_namespace = NULL;
+
+       if (!iterator->_vns)
+               return;
+
+       for (i = 0; i < iterator->_vns->n_ns; i++) {
+               if (iterator->_vns->ns[i].oui != oui)
+                       continue;
+               if (iterator->_vns->ns[i].subns != subns)
+                       continue;
+
+               iterator->current_namespace = &iterator->_vns->ns[i];
+               break;
+       }
+}
+
+
 
 /**
  * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg
@@ -127,99 +196,80 @@ EXPORT_SYMBOL(ieee80211_radiotap_iterator_init);
  */
 
 int ieee80211_radiotap_iterator_next(
-    struct ieee80211_radiotap_iterator *iterator)
+       struct ieee80211_radiotap_iterator *iterator)
 {
-
-       /*
-        * small length lookup table for all radiotap types we heard of
-        * starting from b0 in the bitmap, so we can walk the payload
-        * area of the radiotap header
-        *
-        * There is a requirement to pad args, so that args
-        * of a given length must begin at a boundary of that length
-        * -- but note that compound args are allowed (eg, 2 x u16
-        * for IEEE80211_RADIOTAP_CHANNEL) so total arg length is not
-        * a reliable indicator of alignment requirement.
-        *
-        * upper nybble: content alignment for arg
-        * lower nybble: content length for arg
-        */
-
-       static const u8 rt_sizes[] = {
-               [IEEE80211_RADIOTAP_TSFT] = 0x88,
-               [IEEE80211_RADIOTAP_FLAGS] = 0x11,
-               [IEEE80211_RADIOTAP_RATE] = 0x11,
-               [IEEE80211_RADIOTAP_CHANNEL] = 0x24,
-               [IEEE80211_RADIOTAP_FHSS] = 0x22,
-               [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 0x11,
-               [IEEE80211_RADIOTAP_DBM_ANTNOISE] = 0x11,
-               [IEEE80211_RADIOTAP_LOCK_QUALITY] = 0x22,
-               [IEEE80211_RADIOTAP_TX_ATTENUATION] = 0x22,
-               [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 0x22,
-               [IEEE80211_RADIOTAP_DBM_TX_POWER] = 0x11,
-               [IEEE80211_RADIOTAP_ANTENNA] = 0x11,
-               [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 0x11,
-               [IEEE80211_RADIOTAP_DB_ANTNOISE] = 0x11,
-               [IEEE80211_RADIOTAP_RX_FLAGS] = 0x22,
-               [IEEE80211_RADIOTAP_TX_FLAGS] = 0x22,
-               [IEEE80211_RADIOTAP_RTS_RETRIES] = 0x11,
-               [IEEE80211_RADIOTAP_DATA_RETRIES] = 0x11,
-               /*
-                * add more here as they are defined in
-                * include/net/ieee80211_radiotap.h
-                */
-       };
-
-       /*
-        * for every radiotap entry we can at
-        * least skip (by knowing the length)...
-        */
-
-       while (iterator->arg_index < sizeof(rt_sizes)) {
+       while (1) {
                int hit = 0;
-               int pad;
+               int pad, align, size, subns, vnslen;
+               uint32_t oui;
 
-               if (!(iterator->bitmap_shifter & 1))
+               /* if no more EXT bits, that's it */
+               if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT &&
+                   !(iterator->_bitmap_shifter & 1))
+                       return -ENOENT;
+
+               if (!(iterator->_bitmap_shifter & 1))
                        goto next_entry; /* arg not present */
 
+               /* get alignment/size of data */
+               switch (iterator->_arg_index % 32) {
+               case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
+               case IEEE80211_RADIOTAP_EXT:
+                       align = 1;
+                       size = 0;
+                       break;
+               case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
+                       align = 2;
+                       size = 6;
+                       break;
+               default:
+                       if (!iterator->current_namespace ||
+                           iterator->_arg_index >= iterator->current_namespace->n_bits) {
+                               if (iterator->current_namespace == &radiotap_ns)
+                                       return -ENOENT;
+                               align = 0;
+                       } else {
+                               align = iterator->current_namespace->align_size[iterator->_arg_index].align;
+                               size = iterator->current_namespace->align_size[iterator->_arg_index].size;
+                       }
+                       if (!align) {
+                               /* skip all subsequent data */
+                               iterator->_arg = iterator->_next_ns_data;
+                               /* give up on this namespace */
+                               iterator->current_namespace = NULL;
+                               goto next_entry;
+                       }
+                       break;
+               }
+
                /*
                 * arg is present, account for alignment padding
-                *  8-bit args can be at any alignment
-                * 16-bit args must start on 16-bit boundary
-                * 32-bit args must start on 32-bit boundary
-                * 64-bit args must start on 64-bit boundary
                 *
-                * note that total arg size can differ from alignment of
-                * elements inside arg, so we use upper nybble of length
-                * table to base alignment on
-                *
-                * also note: these alignments are ** relative to the
-                * start of the radiotap header **.  There is no guarantee
+                * Note that these alignments are relative to the start
+                * of the radiotap header.  There is no guarantee
                 * that the radiotap header itself is aligned on any
                 * kind of boundary.
                 *
-                * the above is why get_unaligned() is used to dereference
-                * multibyte elements from the radiotap area
+                * The above is why get_unaligned() is used to dereference
+                * multibyte elements from the radiotap area.
                 */
 
-               pad = (((ulong)iterator->arg) -
-                       ((ulong)iterator->rtheader)) &
-                       ((rt_sizes[iterator->arg_index] >> 4) - 1);
+               pad = ((unsigned long)iterator->_arg -
+                      (unsigned long)iterator->_rtheader) & (align - 1);
 
                if (pad)
-                       iterator->arg +=
-                               (rt_sizes[iterator->arg_index] >> 4) - pad;
+                       iterator->_arg += align - pad;
 
                /*
                 * this is what we will return to user, but we need to
                 * move on first so next call has something fresh to test
                 */
-               iterator->this_arg_index = iterator->arg_index;
-               iterator->this_arg = iterator->arg;
-               hit = 1;
+               iterator->this_arg_index = iterator->_arg_index;
+               iterator->this_arg = iterator->_arg;
+               iterator->this_arg_size = size;
 
                /* internally move on the size of this arg */
-               iterator->arg += rt_sizes[iterator->arg_index] & 0x0f;
+               iterator->_arg += size;
 
                /*
                 * check for insanity where we are given a bitmap that
@@ -228,32 +278,73 @@ int ieee80211_radiotap_iterator_next(
                 * max_length on the last arg, never exceeding it.
                 */
 
-               if (((ulong)iterator->arg - (ulong)iterator->rtheader) >
-                   iterator->max_length)
+               if ((unsigned long)iterator->_arg -
+                   (unsigned long)iterator->_rtheader >
+                   (unsigned long)iterator->_max_length)
                        return -EINVAL;
 
-       next_entry:
-               iterator->arg_index++;
-               if (unlikely((iterator->arg_index & 31) == 0)) {
-                       /* completed current u32 bitmap */
-                       if (iterator->bitmap_shifter & 1) {
-                               /* b31 was set, there is more */
-                               /* move to next u32 bitmap */
-                               iterator->bitmap_shifter =
-                                   get_unaligned_le32(iterator->next_bitmap);
-                               iterator->next_bitmap++;
-                       } else
-                               /* no more bitmaps: end */
-                               iterator->arg_index = sizeof(rt_sizes);
-               } else /* just try the next bit */
-                       iterator->bitmap_shifter >>= 1;
+               /* these special ones are valid in each bitmap word */
+               switch (iterator->_arg_index % 32) {
+               case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
+                       iterator->_bitmap_shifter >>= 1;
+                       iterator->_arg_index++;
+
+                       iterator->_reset_on_ext = 1;
+
+                       vnslen = get_unaligned_le16(iterator->this_arg + 4);
+                       iterator->_next_ns_data = iterator->_arg + vnslen;
+                       oui = (*iterator->this_arg << 16) |
+                               (*(iterator->this_arg + 1) << 8) |
+                               *(iterator->this_arg + 2);
+                       subns = *(iterator->this_arg + 3);
+
+                       find_ns(iterator, oui, subns);
+
+                       iterator->is_radiotap_ns = 0;
+                       /* allow parsers to show this information */
+                       iterator->this_arg_index =
+                               IEEE80211_RADIOTAP_VENDOR_NAMESPACE;
+                       iterator->this_arg_size += vnslen;
+                       if ((unsigned long)iterator->this_arg +
+                           iterator->this_arg_size -
+                           (unsigned long)iterator->_rtheader >
+                           (unsigned long)(unsigned long)iterator->_max_length)
+                               return -EINVAL;
+                       hit = 1;
+                       break;
+               case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
+                       iterator->_bitmap_shifter >>= 1;
+                       iterator->_arg_index++;
+
+                       iterator->_reset_on_ext = 1;
+                       iterator->current_namespace = &radiotap_ns;
+                       iterator->is_radiotap_ns = 1;
+                       break;
+               case IEEE80211_RADIOTAP_EXT:
+                       /*
+                        * bit 31 was set, there is more
+                        * -- move to next u32 bitmap
+                        */
+                       iterator->_bitmap_shifter =
+                               get_unaligned_le32(iterator->_next_bitmap);
+                       iterator->_next_bitmap++;
+                       if (iterator->_reset_on_ext)
+                               iterator->_arg_index = 0;
+                       else
+                               iterator->_arg_index++;
+                       iterator->_reset_on_ext = 0;
+                       break;
+               default:
+                       /* we've got a hit! */
+                       hit = 1;
+ next_entry:
+                       iterator->_bitmap_shifter >>= 1;
+                       iterator->_arg_index++;
+               }
 
                /* if we found a valid arg earlier, return it now */
                if (hit)
                        return 0;
        }
-
-       /* we don't know how to handle any more args, we're done */
-       return -ENOENT;
 }
 EXPORT_SYMBOL(ieee80211_radiotap_iterator_next);