Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
authorDavid S. Miller <davem@davemloft.net>
Thu, 11 Sep 2008 22:46:02 +0000 (15:46 -0700)
committerDavid S. Miller <davem@davemloft.net>
Thu, 11 Sep 2008 22:46:02 +0000 (15:46 -0700)
51 files changed:
drivers/net/wireless/ath9k/main.c
drivers/net/wireless/b43/b43.h
drivers/net/wireless/b43/main.c
drivers/net/wireless/b43/phy_g.c
drivers/net/wireless/b43legacy/phy.c
drivers/net/wireless/b43legacy/xmit.c
drivers/net/wireless/iwlwifi/iwl-3945.c
drivers/net/wireless/iwlwifi/iwl-4965.c
drivers/net/wireless/iwlwifi/iwl-agn-rs.c
drivers/net/wireless/iwlwifi/iwl-agn-rs.h
drivers/net/wireless/iwlwifi/iwl-agn.c
drivers/net/wireless/iwlwifi/iwl-calib.c
drivers/net/wireless/iwlwifi/iwl-core.c
drivers/net/wireless/iwlwifi/iwl-core.h
drivers/net/wireless/iwlwifi/iwl-dev.h
drivers/net/wireless/iwlwifi/iwl-power.c
drivers/net/wireless/iwlwifi/iwl-power.h
drivers/net/wireless/iwlwifi/iwl-scan.c
drivers/net/wireless/iwlwifi/iwl-sta.c
drivers/net/wireless/iwlwifi/iwl-tx.c
drivers/net/wireless/libertas/cmd.c
drivers/net/wireless/libertas/cmd.h
drivers/net/wireless/libertas/defs.h
drivers/net/wireless/libertas/host.h
drivers/net/wireless/libertas/hostcmd.h
drivers/net/wireless/libertas/main.c
drivers/net/wireless/libertas/wext.c
drivers/net/wireless/p54/p54.h
drivers/net/wireless/p54/p54common.c
drivers/net/wireless/p54/p54common.h
drivers/net/wireless/rt2x00/Kconfig
drivers/net/wireless/rt2x00/rt2400pci.c
drivers/net/wireless/rt2x00/rt2500pci.c
drivers/net/wireless/rt2x00/rt2500usb.c
drivers/net/wireless/rt2x00/rt2x00rfkill.c
drivers/net/wireless/rt2x00/rt61pci.c
drivers/net/wireless/rt2x00/rt73usb.c
include/linux/ieee80211.h
include/net/mac80211.h
net/mac80211/Makefile
net/mac80211/ht.c [new file with mode: 0644]
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/main.c
net/mac80211/mesh_hwmp.c
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/rx.c
net/mac80211/scan.c [new file with mode: 0644]
net/mac80211/spectmgmt.c [new file with mode: 0644]
net/mac80211/util.c

index 245b7308a9ad9980fe2f01df84eefb2caff34e57..57d7cc87cb0ffe5205039dde287b7c30b4e1ff09 100644 (file)
@@ -224,7 +224,7 @@ static void setup_ht_cap(struct ieee80211_ht_info *ht_info)
 
        ht_info->ht_supported = 1;
        ht_info->cap = (u16)IEEE80211_HT_CAP_SUP_WIDTH
-                       |(u16)IEEE80211_HT_CAP_MIMO_PS
+                       |(u16)IEEE80211_HT_CAP_SM_PS
                        |(u16)IEEE80211_HT_CAP_SGI_40
                        |(u16)IEEE80211_HT_CAP_DSSSCCK40;
 
index f9c8161671d9bd07a6ce98e97052abf4f8e08133..07d2825458abef9e06a129597299cb03553edac2 100644 (file)
@@ -585,8 +585,6 @@ enum {
 struct b43_qos_params {
        /* The QOS parameters */
        struct ieee80211_tx_queue_params p;
-       /* Does this need to get uploaded to hardware? */
-       bool need_hw_update;
 };
 
 struct b43_wldev;
@@ -648,11 +646,8 @@ struct b43_wl {
        bool beacon_templates_virgin; /* Never wrote the templates? */
        struct work_struct beacon_update_trigger;
 
-       /* The current QOS parameters for the 4 queues.
-        * This is protected by the irq_lock. */
+       /* The current QOS parameters for the 4 queues. */
        struct b43_qos_params qos_params[4];
-       /* Workqueue for updating QOS parameters in hardware. */
-       struct work_struct qos_update_work;
 
        /* Work for adjustment of the transmission power.
         * This is scheduled when we determine that the actual TX output
index c836beceb10d6272c276e204d0f730b5659945eb..d4a356b1163689be72b7b8adf80cd9d6d7725048 100644 (file)
@@ -3059,36 +3059,31 @@ static void b43_qos_params_upload(struct b43_wldev *dev,
        }
 }
 
-/* Update the QOS parameters in hardware. */
-static void b43_qos_update(struct b43_wldev *dev)
+/* Mapping of mac80211 queue numbers to b43 QoS SHM offsets. */
+static const u16 b43_qos_shm_offsets[] = {
+       /* [mac80211-queue-nr] = SHM_OFFSET, */
+       [0] = B43_QOS_VOICE,
+       [1] = B43_QOS_VIDEO,
+       [2] = B43_QOS_BESTEFFORT,
+       [3] = B43_QOS_BACKGROUND,
+};
+
+/* Update all QOS parameters in hardware. */
+static void b43_qos_upload_all(struct b43_wldev *dev)
 {
        struct b43_wl *wl = dev->wl;
        struct b43_qos_params *params;
-       unsigned long flags;
        unsigned int i;
 
-       /* Mapping of mac80211 queues to b43 SHM offsets. */
-       static const u16 qos_shm_offsets[] = {
-               [0] = B43_QOS_VOICE,
-               [1] = B43_QOS_VIDEO,
-               [2] = B43_QOS_BESTEFFORT,
-               [3] = B43_QOS_BACKGROUND,
-       };
-       BUILD_BUG_ON(ARRAY_SIZE(qos_shm_offsets) != ARRAY_SIZE(wl->qos_params));
+       BUILD_BUG_ON(ARRAY_SIZE(b43_qos_shm_offsets) !=
+                    ARRAY_SIZE(wl->qos_params));
 
        b43_mac_suspend(dev);
-       spin_lock_irqsave(&wl->irq_lock, flags);
-
        for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) {
                params = &(wl->qos_params[i]);
-               if (params->need_hw_update) {
-                       b43_qos_params_upload(dev, &(params->p),
-                                             qos_shm_offsets[i]);
-                       params->need_hw_update = 0;
-               }
+               b43_qos_params_upload(dev, &(params->p),
+                                     b43_qos_shm_offsets[i]);
        }
-
-       spin_unlock_irqrestore(&wl->irq_lock, flags);
        b43_mac_enable(dev);
 }
 
@@ -3097,25 +3092,50 @@ static void b43_qos_clear(struct b43_wl *wl)
        struct b43_qos_params *params;
        unsigned int i;
 
+       /* Initialize QoS parameters to sane defaults. */
+
+       BUILD_BUG_ON(ARRAY_SIZE(b43_qos_shm_offsets) !=
+                    ARRAY_SIZE(wl->qos_params));
+
        for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) {
                params = &(wl->qos_params[i]);
 
-               memset(&(params->p), 0, sizeof(params->p));
-               params->p.aifs = -1;
-               params->need_hw_update = 1;
+               switch (b43_qos_shm_offsets[i]) {
+               case B43_QOS_VOICE:
+                       params->p.txop = 0;
+                       params->p.aifs = 2;
+                       params->p.cw_min = 0x0001;
+                       params->p.cw_max = 0x0001;
+                       break;
+               case B43_QOS_VIDEO:
+                       params->p.txop = 0;
+                       params->p.aifs = 2;
+                       params->p.cw_min = 0x0001;
+                       params->p.cw_max = 0x0001;
+                       break;
+               case B43_QOS_BESTEFFORT:
+                       params->p.txop = 0;
+                       params->p.aifs = 3;
+                       params->p.cw_min = 0x0001;
+                       params->p.cw_max = 0x03FF;
+                       break;
+               case B43_QOS_BACKGROUND:
+                       params->p.txop = 0;
+                       params->p.aifs = 7;
+                       params->p.cw_min = 0x0001;
+                       params->p.cw_max = 0x03FF;
+                       break;
+               default:
+                       B43_WARN_ON(1);
+               }
        }
 }
 
 /* Initialize the core's QOS capabilities */
 static void b43_qos_init(struct b43_wldev *dev)
 {
-       struct b43_wl *wl = dev->wl;
-       unsigned int i;
-
        /* Upload the current QOS parameters. */
-       for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++)
-               wl->qos_params[i].need_hw_update = 1;
-       b43_qos_update(dev);
+       b43_qos_upload_all(dev);
 
        /* Enable QOS support. */
        b43_hf_write(dev, b43_hf_read(dev) | B43_HF_EDCF);
@@ -3124,25 +3144,13 @@ static void b43_qos_init(struct b43_wldev *dev)
                    | B43_MMIO_IFSCTL_USE_EDCF);
 }
 
-static void b43_qos_update_work(struct work_struct *work)
-{
-       struct b43_wl *wl = container_of(work, struct b43_wl, qos_update_work);
-       struct b43_wldev *dev;
-
-       mutex_lock(&wl->mutex);
-       dev = wl->current_dev;
-       if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED)))
-               b43_qos_update(dev);
-       mutex_unlock(&wl->mutex);
-}
-
 static int b43_op_conf_tx(struct ieee80211_hw *hw, u16 _queue,
                          const struct ieee80211_tx_queue_params *params)
 {
        struct b43_wl *wl = hw_to_b43_wl(hw);
-       unsigned long flags;
+       struct b43_wldev *dev;
        unsigned int queue = (unsigned int)_queue;
-       struct b43_qos_params *p;
+       int err = -ENODEV;
 
        if (queue >= ARRAY_SIZE(wl->qos_params)) {
                /* Queue not available or don't support setting
@@ -3150,16 +3158,25 @@ static int b43_op_conf_tx(struct ieee80211_hw *hw, u16 _queue,
                 * confuse mac80211. */
                return 0;
        }
+       BUILD_BUG_ON(ARRAY_SIZE(b43_qos_shm_offsets) !=
+                    ARRAY_SIZE(wl->qos_params));
 
-       spin_lock_irqsave(&wl->irq_lock, flags);
-       p = &(wl->qos_params[queue]);
-       memcpy(&(p->p), params, sizeof(p->p));
-       p->need_hw_update = 1;
-       spin_unlock_irqrestore(&wl->irq_lock, flags);
+       mutex_lock(&wl->mutex);
+       dev = wl->current_dev;
+       if (unlikely(!dev || (b43_status(dev) < B43_STAT_INITIALIZED)))
+               goto out_unlock;
 
-       queue_work(hw->workqueue, &wl->qos_update_work);
+       memcpy(&(wl->qos_params[queue].p), params, sizeof(*params));
+       b43_mac_suspend(dev);
+       b43_qos_params_upload(dev, &(wl->qos_params[queue].p),
+                             b43_qos_shm_offsets[queue]);
+       b43_mac_enable(dev);
+       err = 0;
 
-       return 0;
+out_unlock:
+       mutex_unlock(&wl->mutex);
+
+       return err;
 }
 
 static int b43_op_get_tx_stats(struct ieee80211_hw *hw,
@@ -4186,7 +4203,6 @@ static void b43_op_stop(struct ieee80211_hw *hw)
        struct b43_wldev *dev = wl->current_dev;
 
        b43_rfkill_exit(dev);
-       cancel_work_sync(&(wl->qos_update_work));
        cancel_work_sync(&(wl->beacon_update_trigger));
 
        mutex_lock(&wl->mutex);
@@ -4585,7 +4601,6 @@ static int b43_wireless_init(struct ssb_device *dev)
        spin_lock_init(&wl->shm_lock);
        mutex_init(&wl->mutex);
        INIT_LIST_HEAD(&wl->devlist);
-       INIT_WORK(&wl->qos_update_work, b43_qos_update_work);
        INIT_WORK(&wl->beacon_update_trigger, b43_beacon_update_trigger_work);
        INIT_WORK(&wl->txpower_adjust_work, b43_phy_txpower_adjust_work);
 
index e8c012c9abb027679a4800a803f0a9346301397d..232181f6333c453eb6d0f030c07d60da1d0f2c73 100644 (file)
@@ -386,7 +386,6 @@ static void b43_set_original_gains(struct b43_wldev *dev)
 void b43_nrssi_hw_write(struct b43_wldev *dev, u16 offset, s16 val)
 {
        b43_phy_write(dev, B43_PHY_NRSSILT_CTRL, offset);
-       mmiowb();
        b43_phy_write(dev, B43_PHY_NRSSILT_DATA, (u16) val);
 }
 
index 768cccb9b1bafbb4a85d5c2ff77d71900e6b5a0b..bed67a54250c0b2981def6e54a6b5256a6405ff4 100644 (file)
@@ -595,12 +595,14 @@ static void b43legacy_phy_initb5(struct b43legacy_wldev *dev)
                                    0x0035) & 0xFFC0) | 0x0064);
                b43legacy_phy_write(dev, 0x005D, (b43legacy_phy_read(dev,
                                    0x005D) & 0xFF80) | 0x000A);
+               b43legacy_phy_write(dev, 0x5B, 0x0000);
+               b43legacy_phy_write(dev, 0x5C, 0x0000);
        }
 
        if (dev->bad_frames_preempt)
                b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD,
                                    b43legacy_phy_read(dev,
-                                   B43legacy_PHY_RADIO_BITFIELD) | (1 << 11));
+                                   B43legacy_PHY_RADIO_BITFIELD) | (1 << 12));
 
        if (phy->analog == 1) {
                b43legacy_phy_write(dev, 0x0026, 0xCE00);
@@ -753,7 +755,7 @@ static void b43legacy_phy_initb6(struct b43legacy_wldev *dev)
                b43legacy_radio_write16(dev, 0x0050, 0x0020);
        }
        if (phy->radio_rev <= 2) {
-               b43legacy_radio_write16(dev, 0x007C, 0x0020);
+               b43legacy_radio_write16(dev, 0x0050, 0x0020);
                b43legacy_radio_write16(dev, 0x005A, 0x0070);
                b43legacy_radio_write16(dev, 0x005B, 0x007B);
                b43legacy_radio_write16(dev, 0x005C, 0x00B0);
@@ -771,7 +773,7 @@ static void b43legacy_phy_initb6(struct b43legacy_wldev *dev)
                b43legacy_phy_write(dev, 0x002A, 0x8AC0);
        b43legacy_phy_write(dev, 0x0038, 0x0668);
        b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF);
-       if (phy->radio_rev <= 5)
+       if (phy->radio_rev == 4 || phy->radio_rev == 5)
                b43legacy_phy_write(dev, 0x005D, (b43legacy_phy_read(dev,
                                    0x005D) & 0xFF80) | 0x0003);
        if (phy->radio_rev <= 2)
@@ -1010,7 +1012,7 @@ static void b43legacy_phy_initg(struct b43legacy_wldev *dev)
                b43legacy_phy_initb5(dev);
        else
                b43legacy_phy_initb6(dev);
-       if (phy->rev >= 2 || phy->gmode)
+       if (phy->rev >= 2 && phy->gmode)
                b43legacy_phy_inita(dev);
 
        if (phy->rev >= 2) {
@@ -1025,18 +1027,22 @@ static void b43legacy_phy_initg(struct b43legacy_wldev *dev)
                b43legacy_phy_write(dev, 0x0811, 0x0400);
                b43legacy_phy_write(dev, 0x0015, 0x00C0);
        }
-       if (phy->rev >= 2 || phy->gmode) {
+       if (phy->gmode) {
                tmp = b43legacy_phy_read(dev, 0x0400) & 0xFF;
-               if (tmp == 3 || tmp == 5) {
+               if (tmp == 3) {
+                       b43legacy_phy_write(dev, 0x04C2, 0x1816);
+                       b43legacy_phy_write(dev, 0x04C3, 0x8606);
+               }
+               if (tmp == 4 || tmp == 5) {
                        b43legacy_phy_write(dev, 0x04C2, 0x1816);
                        b43legacy_phy_write(dev, 0x04C3, 0x8006);
-                       if (tmp == 5)
-                               b43legacy_phy_write(dev, 0x04CC,
-                                                   (b43legacy_phy_read(dev,
-                                                    0x04CC) & 0x00FF) |
-                                                    0x1F00);
+                       b43legacy_phy_write(dev, 0x04CC,
+                                           (b43legacy_phy_read(dev,
+                                            0x04CC) & 0x00FF) |
+                                            0x1F00);
                }
-               b43legacy_phy_write(dev, 0x047E, 0x0078);
+               if (phy->rev >= 2)
+                       b43legacy_phy_write(dev, 0x047E, 0x0078);
        }
        if (phy->radio_rev == 8) {
                b43legacy_phy_write(dev, 0x0801, b43legacy_phy_read(dev, 0x0801)
@@ -1078,7 +1084,7 @@ static void b43legacy_phy_initg(struct b43legacy_wldev *dev)
                else
                        b43legacy_phy_write(dev, 0x002F, 0x0202);
        }
-       if (phy->gmode || phy->rev >= 2) {
+       if (phy->gmode) {
                b43legacy_phy_lo_adjust(dev, 0);
                b43legacy_phy_write(dev, 0x080F, 0x8078);
        }
index c5ca72aa59e7eb1c08791334866fe796a4885790..6835064758fbfd67c4b5e391ddcca70898eb0285 100644 (file)
@@ -624,7 +624,7 @@ void b43legacy_handle_hwtxstatus(struct b43legacy_wldev *dev,
        tmp = hw->count;
        status.frame_count = (tmp >> 4);
        status.rts_count = (tmp & 0x0F);
-       tmp = hw->flags;
+       tmp = hw->flags << 1;
        status.supp_reason = ((tmp & 0x1C) >> 2);
        status.pm_indicated = !!(tmp & 0x80);
        status.intermediate = !!(tmp & 0x40);
index 1377c8190ecb1ce3035f076751640c0f20647ab9..3d100e8982499be45a42290bde00d617bf41758c 100644 (file)
@@ -681,19 +681,7 @@ static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv,
                priv->last_rx_noise = rx_status.noise;
        }
 
-       if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) {
-               iwl3945_pass_packet_to_mac80211(priv, rxb, &rx_status);
-               return;
-       }
-
-       switch (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FTYPE) {
-       case IEEE80211_FTYPE_MGMT:
-       case IEEE80211_FTYPE_DATA:
-               /* fall through */
-       default:
-               iwl3945_pass_packet_to_mac80211(priv, rxb, &rx_status);
-               break;
-       }
+       iwl3945_pass_packet_to_mac80211(priv, rxb, &rx_status);
 }
 
 int iwl3945_hw_txq_attach_buf_to_tfd(struct iwl3945_priv *priv, void *ptr,
index d9c4fdbf7f770f9d8655dddced36af46f16518bb..9838de5f436918762c0fafdca82102ea01993094 100644 (file)
@@ -1607,8 +1607,8 @@ static int iwl4965_send_rxon_assoc(struct iwl_priv *priv)
        return ret;
 }
 
-
-int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel)
+#ifdef IEEE80211_CONF_CHANNEL_SWITCH
+static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel)
 {
        int rc;
        u8 band = 0;
@@ -1648,6 +1648,7 @@ int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel)
        rc = iwl_send_cmd_pdu(priv, REPLY_CHANNEL_SWITCH, sizeof(cmd), &cmd);
        return rc;
 }
+#endif
 
 static int iwl4965_shared_mem_rx_idx(struct iwl_priv *priv)
 {
index 98f2c843b99e9e4fbe05b4665e3736c63f956a3b..c293e5b6cbb55cd8b1a3fc61490e75a326c07bfa 100644 (file)
@@ -436,7 +436,7 @@ static int rs_collect_tx_data(struct iwl_rate_scale_data *windows,
                /* Shift bitmap by one frame (throw away oldest history),
                 * OR in "1", and increment "success" if this
                 * frame was successful. */
-               window->data <<= 1;;
+               window->data <<= 1;
                if (successes > 0) {
                        window->success_counter++;
                        window->data |= 0x1;
@@ -1128,6 +1128,7 @@ static s32 rs_get_best_rate(struct iwl_priv *priv,
 
                        /* Higher rate not available, use the original */
                        } else {
+                               new_rate = rate;
                                break;
                        }
                }
@@ -1153,8 +1154,8 @@ static int rs_switch_to_mimo2(struct iwl_priv *priv,
            !sta->ht_info.ht_supported)
                return -1;
 
-       if (((sta->ht_info.cap & IEEE80211_HT_CAP_MIMO_PS) >> 2)
-                                               == IWL_MIMO_PS_STATIC)
+       if (((sta->ht_info.cap & IEEE80211_HT_CAP_SM_PS) >> 2)
+                                               == WLAN_HT_CAP_SM_PS_STATIC)
                return -1;
 
        /* Need both Tx chains/antennas to support MIMO */
@@ -1281,15 +1282,23 @@ static int rs_move_legacy_other(struct iwl_priv *priv,
                  (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
        u8 start_action = tbl->action;
        u8 valid_tx_ant = priv->hw_params.valid_tx_ant;
+       u8 tx_chains_num = priv->hw_params.tx_chains_num;
        int ret = 0;
 
        for (; ;) {
                switch (tbl->action) {
-               case IWL_LEGACY_SWITCH_ANTENNA:
+               case IWL_LEGACY_SWITCH_ANTENNA1:
+               case IWL_LEGACY_SWITCH_ANTENNA2:
                        IWL_DEBUG_RATE("LQ: Legacy toggle Antenna\n");
 
                        lq_sta->action_counter++;
 
+                       if ((tbl->action == IWL_LEGACY_SWITCH_ANTENNA1 &&
+                                                       tx_chains_num <= 1) ||
+                           (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2 &&
+                                                       tx_chains_num <= 2))
+                               break;
+
                        /* Don't change antenna if success has been great */
                        if (window->success_ratio >= IWL_RS_GOOD_RATIO)
                                break;
@@ -1299,7 +1308,7 @@ static int rs_move_legacy_other(struct iwl_priv *priv,
 
                        if (rs_toggle_antenna(valid_tx_ant,
                                &search_tbl->current_rate, search_tbl)) {
-                               lq_sta->search_better_tbl = 1;
+                               rs_set_expected_tpt_table(lq_sta, search_tbl);
                                goto out;
                        }
                        break;
@@ -1312,43 +1321,54 @@ static int rs_move_legacy_other(struct iwl_priv *priv,
                        ret = rs_switch_to_siso(priv, lq_sta, conf, sta,
                                                 search_tbl, index);
                        if (!ret) {
-                               lq_sta->search_better_tbl = 1;
                                lq_sta->action_counter = 0;
                                goto out;
                        }
 
                        break;
-               case IWL_LEGACY_SWITCH_MIMO2:
+               case IWL_LEGACY_SWITCH_MIMO2_AB:
+               case IWL_LEGACY_SWITCH_MIMO2_AC:
+               case IWL_LEGACY_SWITCH_MIMO2_BC:
                        IWL_DEBUG_RATE("LQ: Legacy switch to MIMO2\n");
 
                        /* Set up search table to try MIMO */
                        memcpy(search_tbl, tbl, sz);
                        search_tbl->is_SGI = 0;
-                       search_tbl->ant_type = ANT_AB;/*FIXME:RS*/
-                               /*FIXME:RS:need to check ant validity*/
+
+                       if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AB)
+                               search_tbl->ant_type = ANT_AB;
+                       else if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AC)
+                               search_tbl->ant_type = ANT_AC;
+                       else
+                               search_tbl->ant_type = ANT_BC;
+
+                       if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
+                               break;
+
                        ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta,
                                                 search_tbl, index);
                        if (!ret) {
-                               lq_sta->search_better_tbl = 1;
                                lq_sta->action_counter = 0;
                                goto out;
                        }
                        break;
                }
                tbl->action++;
-               if (tbl->action > IWL_LEGACY_SWITCH_MIMO2)
-                       tbl->action = IWL_LEGACY_SWITCH_ANTENNA;
+               if (tbl->action > IWL_LEGACY_SWITCH_MIMO2_BC)
+                       tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
 
                if (tbl->action == start_action)
                        break;
 
        }
+       search_tbl->lq_type = LQ_NONE;
        return 0;
 
- out:
+out:
+       lq_sta->search_better_tbl = 1;
        tbl->action++;
-       if (tbl->action > IWL_LEGACY_SWITCH_MIMO2)
-               tbl->action = IWL_LEGACY_SWITCH_ANTENNA;
+       if (tbl->action > IWL_LEGACY_SWITCH_MIMO2_BC)
+               tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
        return 0;
 
 }
@@ -1370,34 +1390,51 @@ static int rs_move_siso_to_other(struct iwl_priv *priv,
                  (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
        u8 start_action = tbl->action;
        u8 valid_tx_ant = priv->hw_params.valid_tx_ant;
+       u8 tx_chains_num = priv->hw_params.tx_chains_num;
        int ret;
 
        for (;;) {
                lq_sta->action_counter++;
                switch (tbl->action) {
-               case IWL_SISO_SWITCH_ANTENNA:
+               case IWL_SISO_SWITCH_ANTENNA1:
+               case IWL_SISO_SWITCH_ANTENNA2:
                        IWL_DEBUG_RATE("LQ: SISO toggle Antenna\n");
+
+                       if ((tbl->action == IWL_SISO_SWITCH_ANTENNA1 &&
+                                                       tx_chains_num <= 1) ||
+                           (tbl->action == IWL_SISO_SWITCH_ANTENNA2 &&
+                                                       tx_chains_num <= 2))
+                               break;
+
                        if (window->success_ratio >= IWL_RS_GOOD_RATIO)
                                break;
 
                        memcpy(search_tbl, tbl, sz);
                        if (rs_toggle_antenna(valid_tx_ant,
-                                      &search_tbl->current_rate, search_tbl)) {
-                               lq_sta->search_better_tbl = 1;
+                                      &search_tbl->current_rate, search_tbl))
                                goto out;
-                       }
                        break;
-               case IWL_SISO_SWITCH_MIMO2:
+               case IWL_SISO_SWITCH_MIMO2_AB:
+               case IWL_SISO_SWITCH_MIMO2_AC:
+               case IWL_SISO_SWITCH_MIMO2_BC:
                        IWL_DEBUG_RATE("LQ: SISO switch to MIMO2\n");
                        memcpy(search_tbl, tbl, sz);
                        search_tbl->is_SGI = 0;
-                       search_tbl->ant_type = ANT_AB; /*FIXME:RS*/
+
+                       if (tbl->action == IWL_SISO_SWITCH_MIMO2_AB)
+                               search_tbl->ant_type = ANT_AB;
+                       else if (tbl->action == IWL_SISO_SWITCH_MIMO2_AC)
+                               search_tbl->ant_type = ANT_AC;
+                       else
+                               search_tbl->ant_type = ANT_BC;
+
+                       if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
+                               break;
+
                        ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta,
                                                 search_tbl, index);
-                       if (!ret) {
-                               lq_sta->search_better_tbl = 1;
+                       if (!ret)
                                goto out;
-                       }
                        break;
                case IWL_SISO_SWITCH_GI:
                        if (!tbl->is_fat &&
@@ -1427,22 +1464,23 @@ static int rs_move_siso_to_other(struct iwl_priv *priv,
                        }
                        search_tbl->current_rate = rate_n_flags_from_tbl(
                                                search_tbl, index, is_green);
-                       lq_sta->search_better_tbl = 1;
                        goto out;
                }
                tbl->action++;
                if (tbl->action > IWL_SISO_SWITCH_GI)
-                       tbl->action = IWL_SISO_SWITCH_ANTENNA;
+                       tbl->action = IWL_SISO_SWITCH_ANTENNA1;
 
                if (tbl->action == start_action)
                        break;
        }
+       search_tbl->lq_type = LQ_NONE;
        return 0;
 
  out:
+       lq_sta->search_better_tbl = 1;
        tbl->action++;
        if (tbl->action > IWL_SISO_SWITCH_GI)
-               tbl->action = IWL_SISO_SWITCH_ANTENNA;
+               tbl->action = IWL_SISO_SWITCH_ANTENNA1;
        return 0;
 }
 
@@ -1458,37 +1496,58 @@ static int rs_move_mimo_to_other(struct iwl_priv *priv,
        struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
        struct iwl_scale_tbl_info *search_tbl =
                                &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+       struct iwl_rate_scale_data *window = &(tbl->win[index]);
        u32 sz = (sizeof(struct iwl_scale_tbl_info) -
                  (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
        u8 start_action = tbl->action;
-       /*u8 valid_tx_ant = priv->hw_params.valid_tx_ant;*/
+       u8 valid_tx_ant = priv->hw_params.valid_tx_ant;
+       u8 tx_chains_num = priv->hw_params.tx_chains_num;
        int ret;
 
        for (;;) {
                lq_sta->action_counter++;
                switch (tbl->action) {
-               case IWL_MIMO_SWITCH_ANTENNA_A:
-               case IWL_MIMO_SWITCH_ANTENNA_B:
+               case IWL_MIMO2_SWITCH_ANTENNA1:
+               case IWL_MIMO2_SWITCH_ANTENNA2:
+                       IWL_DEBUG_RATE("LQ: MIMO toggle Antennas\n");
+
+                       if (tx_chains_num <= 2)
+                               break;
+
+                       if (window->success_ratio >= IWL_RS_GOOD_RATIO)
+                               break;
+
+                       memcpy(search_tbl, tbl, sz);
+                       if (rs_toggle_antenna(valid_tx_ant,
+                                      &search_tbl->current_rate, search_tbl))
+                               goto out;
+                       break;
+               case IWL_MIMO2_SWITCH_SISO_A:
+               case IWL_MIMO2_SWITCH_SISO_B:
+               case IWL_MIMO2_SWITCH_SISO_C:
                        IWL_DEBUG_RATE("LQ: MIMO2 switch to SISO\n");
 
                        /* Set up new search table for SISO */
                        memcpy(search_tbl, tbl, sz);
 
-                       /*FIXME:RS:need to check ant validity + C*/
-                       if (tbl->action == IWL_MIMO_SWITCH_ANTENNA_A)
+                       if (tbl->action == IWL_MIMO2_SWITCH_SISO_A)
                                search_tbl->ant_type = ANT_A;
-                       else
+                       else if (tbl->action == IWL_MIMO2_SWITCH_SISO_B)
                                search_tbl->ant_type = ANT_B;
+                       else
+                               search_tbl->ant_type = ANT_C;
+
+                       if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type))
+                               break;
 
                        ret = rs_switch_to_siso(priv, lq_sta, conf, sta,
                                                 search_tbl, index);
-                       if (!ret) {
-                               lq_sta->search_better_tbl = 1;
+                       if (!ret)
                                goto out;
-                       }
+
                        break;
 
-               case IWL_MIMO_SWITCH_GI:
+               case IWL_MIMO2_SWITCH_GI:
                        if (!tbl->is_fat &&
                                !(priv->current_ht_config.sgf &
                                                HT_SHORT_GI_20MHZ))
@@ -1517,23 +1576,23 @@ static int rs_move_mimo_to_other(struct iwl_priv *priv,
                        }
                        search_tbl->current_rate = rate_n_flags_from_tbl(
                                                search_tbl, index, is_green);
-                       lq_sta->search_better_tbl = 1;
                        goto out;
 
                }
                tbl->action++;
-               if (tbl->action > IWL_MIMO_SWITCH_GI)
-                       tbl->action = IWL_MIMO_SWITCH_ANTENNA_A;
+               if (tbl->action > IWL_MIMO2_SWITCH_GI)
+                       tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
 
                if (tbl->action == start_action)
                        break;
        }
-
+       search_tbl->lq_type = LQ_NONE;
        return 0;
  out:
+       lq_sta->search_better_tbl = 1;
        tbl->action++;
-       if (tbl->action > IWL_MIMO_SWITCH_GI)
-               tbl->action = IWL_MIMO_SWITCH_ANTENNA_A;
+       if (tbl->action > IWL_MIMO2_SWITCH_GI)
+               tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
        return 0;
 
 }
@@ -1748,19 +1807,13 @@ static void rs_rate_scale_perform(struct iwl_priv *priv,
                rs_stay_in_table(lq_sta);
 
                goto out;
+       }
 
        /* Else we have enough samples; calculate estimate of
         * actual average throughput */
-       } else {
-               /*FIXME:RS remove this else if we don't get this error*/
-               if (window->average_tpt != ((window->success_ratio *
-                               tbl->expected_tpt[index] + 64) / 128)) {
-                       IWL_ERROR("expected_tpt should have been calculated"
-                                                               " by now\n");
-                       window->average_tpt = ((window->success_ratio *
-                                       tbl->expected_tpt[index] + 64) / 128);
-               }
-       }
+
+       BUG_ON(window->average_tpt != ((window->success_ratio *
+                       tbl->expected_tpt[index] + 64) / 128));
 
        /* If we are searching for better modulation mode, check success. */
        if (lq_sta->search_better_tbl) {
@@ -1770,7 +1823,7 @@ static void rs_rate_scale_perform(struct iwl_priv *priv,
                 * continuing to use the setup that we've been trying. */
                if (window->average_tpt > lq_sta->last_tpt) {
 
-                       IWL_DEBUG_RATE("LQ: SWITCHING TO CURRENT TABLE "
+                       IWL_DEBUG_RATE("LQ: SWITCHING TO NEW TABLE "
                                        "suc=%d cur-tpt=%d old-tpt=%d\n",
                                        window->success_ratio,
                                        window->average_tpt,
@@ -2183,7 +2236,7 @@ static void rs_rate_init(void *priv_rate, void *priv_sta,
                for (i = 0; i < IWL_RATE_COUNT; i++)
                        rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
 
-       IWL_DEBUG_RATE("LQ: *** rate scale global init ***\n");
+       IWL_DEBUG_RATE("LQ: *** rate scale station global init ***\n");
        /* TODO: what is a good starting rate for STA? About middle? Maybe not
         * the lowest or the highest rate.. Could consider using RSSI from
         * previous packets? Need to have IEEE 802.1X auth succeed immediately
index 84d4d1e33755bffd62578b884f17137632c847ef..d148d73635eb5ddec4669fc1fe0bf185959a5d04 100644 (file)
@@ -206,21 +206,28 @@ enum {
 #define IWL_RATE_DECREASE_TH           1920    /*  15% */
 
 /* possible actions when in legacy mode */
-#define IWL_LEGACY_SWITCH_ANTENNA      0
-#define IWL_LEGACY_SWITCH_SISO         1
-#define IWL_LEGACY_SWITCH_MIMO2                2
+#define IWL_LEGACY_SWITCH_ANTENNA1      0
+#define IWL_LEGACY_SWITCH_ANTENNA2      1
+#define IWL_LEGACY_SWITCH_SISO          2
+#define IWL_LEGACY_SWITCH_MIMO2_AB      3
+#define IWL_LEGACY_SWITCH_MIMO2_AC      4
+#define IWL_LEGACY_SWITCH_MIMO2_BC      5
 
 /* possible actions when in siso mode */
-#define IWL_SISO_SWITCH_ANTENNA                0
-#define IWL_SISO_SWITCH_MIMO2          1
-#define IWL_SISO_SWITCH_GI             2
+#define IWL_SISO_SWITCH_ANTENNA1        0
+#define IWL_SISO_SWITCH_ANTENNA2        1
+#define IWL_SISO_SWITCH_MIMO2_AB        2
+#define IWL_SISO_SWITCH_MIMO2_AC        3
+#define IWL_SISO_SWITCH_MIMO2_BC        4
+#define IWL_SISO_SWITCH_GI              5
 
 /* possible actions when in mimo mode */
-#define IWL_MIMO_SWITCH_ANTENNA_A      0
-#define IWL_MIMO_SWITCH_ANTENNA_B      1
-#define IWL_MIMO_SWITCH_GI             2
-
-/*FIXME:RS:separate MIMO2/3 transitions*/
+#define IWL_MIMO2_SWITCH_ANTENNA1       0
+#define IWL_MIMO2_SWITCH_ANTENNA2       1
+#define IWL_MIMO2_SWITCH_SISO_A         2
+#define IWL_MIMO2_SWITCH_SISO_B         3
+#define IWL_MIMO2_SWITCH_SISO_C         4
+#define IWL_MIMO2_SWITCH_GI             5
 
 /*FIXME:RS:add posible acctions for MIMO3*/
 
index 1547122e66fa9728e3c7a48151d6d044d3b901da..31ea288588969eca54449b712c176107cec41535 100644 (file)
@@ -485,7 +485,7 @@ static u8 iwl4965_rate_get_lowest_plcp(struct iwl_priv *priv)
                return IWL_RATE_6M_PLCP;
 }
 
-unsigned int iwl4965_hw_get_beacon_cmd(struct iwl_priv *priv,
+static unsigned int iwl4965_hw_get_beacon_cmd(struct iwl_priv *priv,
                                       struct iwl_frame *frame, u8 rate)
 {
        struct iwl_tx_beacon_cmd *tx_beacon_cmd;
@@ -564,8 +564,6 @@ static void iwl4965_ht_conf(struct iwl_priv *priv,
        if (!iwl_conf->is_ht)
                return;
 
-       priv->ps_mode = (u8)((ht_conf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2);
-
        if (ht_conf->cap & IEEE80211_HT_CAP_SGI_20)
                iwl_conf->sgf |= HT_SHORT_GI_20MHZ;
        if (ht_conf->cap & IEEE80211_HT_CAP_SGI_40)
@@ -586,6 +584,8 @@ static void iwl4965_ht_conf(struct iwl_priv *priv,
                iwl_conf->supported_chan_width = 0;
        }
 
+       iwl_conf->sm_ps = (u8)((ht_conf->cap & IEEE80211_HT_CAP_SM_PS) >> 2);
+
        memcpy(iwl_conf->supp_mcs_set, ht_conf->supp_mcs_set, 16);
 
        iwl_conf->control_channel = ht_bss_conf->primary_channel;
@@ -2558,7 +2558,11 @@ static void iwl4965_post_associate(struct iwl_priv *priv)
        iwl_activate_qos(priv, 0);
        spin_unlock_irqrestore(&priv->lock, flags);
 
-       iwl_power_enable_management(priv);
+       /* the chain noise calibration will enabled PM upon completion
+        * If chain noise has already been run, then we need to enable
+        * power management here */
+       if (priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE)
+               iwl_power_enable_management(priv);
 
        /* Enable Rx differential gain and sensitivity calibrations */
        iwl_chain_noise_reset(priv);
index 35fb4a4f737daba64f8c3a06eb6c7cb470343968..72fbf47229db7da9832c3eb05a6c2722df651b7e 100644 (file)
@@ -808,13 +808,11 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv,
                }
        }
 
+       /* Save for use within RXON, TX, SCAN commands, etc. */
+       priv->chain_noise_data.active_chains = active_chains;
        IWL_DEBUG_CALIB("active_chains (bitwise) = 0x%x\n",
                        active_chains);
 
-       /* Save for use within RXON, TX, SCAN commands, etc. */
-       /*priv->valid_antenna = active_chains;*/
-       /*FIXME: should be reflected in RX chains in RXON */
-
        /* Analyze noise for rx balance */
        average_noise[0] = ((data->chain_noise_a)/CAL_NUM_OF_BEACONS);
        average_noise[1] = ((data->chain_noise_b)/CAL_NUM_OF_BEACONS);
@@ -839,6 +837,15 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv,
 
        priv->cfg->ops->utils->gain_computation(priv, average_noise,
                min_average_noise_antenna_i, min_average_noise);
+
+       /* Some power changes may have been made during the calibration.
+        * Update and commit the RXON
+        */
+       if (priv->cfg->ops->lib->update_chain_flags)
+               priv->cfg->ops->lib->update_chain_flags(priv);
+
+       data->state = IWL_CHAIN_NOISE_DONE;
+       iwl_power_enable_management(priv);
 }
 EXPORT_SYMBOL(iwl_chain_noise_calibration);
 
index a0b86af25c839858802f80b1d008f4f28009121a..36d08b0eec4373f13d87275b2a77bb65916d5e7f 100644 (file)
@@ -399,8 +399,8 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv,
 
        ht_info->cap |= (u16)IEEE80211_HT_CAP_GRN_FLD;
        ht_info->cap |= (u16)IEEE80211_HT_CAP_SGI_20;
-       ht_info->cap |= (u16)(IEEE80211_HT_CAP_MIMO_PS &
-                            (IWL_MIMO_PS_NONE << 2));
+       ht_info->cap |= (u16)(IEEE80211_HT_CAP_SM_PS &
+                            (WLAN_HT_CAP_SM_PS_DISABLED << 2));
 
        max_bit_rate = MAX_BIT_RATE_20_MHZ;
        if (priv->hw_params.fat_channel & BIT(band)) {
@@ -709,7 +709,8 @@ static int iwl_get_active_rx_chain_count(struct iwl_priv *priv)
        bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status);
 
        /* # of Rx chains to use when expecting MIMO. */
-       if (is_single || (!is_cam && (priv->ps_mode == IWL_MIMO_PS_STATIC)))
+       if (is_single || (!is_cam && (priv->current_ht_config.sm_ps ==
+                                                WLAN_HT_CAP_SM_PS_STATIC)))
                return 2;
        else
                return 3;
@@ -720,17 +721,18 @@ static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt)
        int idle_cnt;
        bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status);
        /* # Rx chains when idling and maybe trying to save power */
-       switch (priv->ps_mode) {
-       case IWL_MIMO_PS_STATIC:
-       case IWL_MIMO_PS_DYNAMIC:
+       switch (priv->current_ht_config.sm_ps) {
+       case WLAN_HT_CAP_SM_PS_STATIC:
+       case WLAN_HT_CAP_SM_PS_DYNAMIC:
                idle_cnt = (is_cam) ? 2 : 1;
                break;
-       case IWL_MIMO_PS_NONE:
+       case WLAN_HT_CAP_SM_PS_DISABLED:
                idle_cnt = (is_cam) ? active_cnt : 1;
                break;
-       case IWL_MIMO_PS_INVALID:
+       case WLAN_HT_CAP_SM_PS_INVALID:
        default:
-               IWL_ERROR("invalide mimo ps mode %d\n", priv->ps_mode);
+               IWL_ERROR("invalide mimo ps mode %d\n",
+                          priv->current_ht_config.sm_ps);
                WARN_ON(1);
                idle_cnt = -1;
                break;
@@ -738,6 +740,17 @@ static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt)
        return idle_cnt;
 }
 
+/* up to 4 chains */
+static u8 iwl_count_chain_bitmap(u32 chain_bitmap)
+{
+       u8 res;
+       res = (chain_bitmap & BIT(0)) >> 0;
+       res += (chain_bitmap & BIT(1)) >> 1;
+       res += (chain_bitmap & BIT(2)) >> 2;
+       res += (chain_bitmap & BIT(4)) >> 4;
+       return res;
+}
+
 /**
  * iwl_set_rxon_chain - Set up Rx chain usage in "staging" RXON image
  *
@@ -748,25 +761,35 @@ void iwl_set_rxon_chain(struct iwl_priv *priv)
 {
        bool is_single = is_single_rx_stream(priv);
        bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status);
-       u8 idle_rx_cnt, active_rx_cnt;
+       u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt;
+       u32 active_chains;
        u16 rx_chain;
 
        /* Tell uCode which antennas are actually connected.
         * Before first association, we assume all antennas are connected.
         * Just after first association, iwl_chain_noise_calibration()
         *    checks which antennas actually *are* connected. */
-       rx_chain = priv->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS;
+        if (priv->chain_noise_data.active_chains)
+               active_chains = priv->chain_noise_data.active_chains;
+       else
+               active_chains = priv->hw_params.valid_rx_ant;
+
+       rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS;
 
        /* How many receivers should we use? */
        active_rx_cnt = iwl_get_active_rx_chain_count(priv);
        idle_rx_cnt = iwl_get_idle_rx_chain_count(priv, active_rx_cnt);
 
-       /* correct rx chain count accoridng hw settings */
-       if (priv->hw_params.rx_chains_num < active_rx_cnt)
-               active_rx_cnt = priv->hw_params.rx_chains_num;
 
-       if (priv->hw_params.rx_chains_num < idle_rx_cnt)
-               idle_rx_cnt = priv->hw_params.rx_chains_num;
+       /* correct rx chain count according hw settings
+        * and chain noise calibration
+        */
+       valid_rx_cnt = iwl_count_chain_bitmap(active_chains);
+       if (valid_rx_cnt < active_rx_cnt)
+               active_rx_cnt = valid_rx_cnt;
+
+       if (valid_rx_cnt < idle_rx_cnt)
+               idle_rx_cnt = valid_rx_cnt;
 
        rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS;
        rx_chain |= idle_rx_cnt  << RXON_RX_CHAIN_CNT_POS;
@@ -778,7 +801,7 @@ void iwl_set_rxon_chain(struct iwl_priv *priv)
        else
                priv->staging_rxon.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK;
 
-       IWL_DEBUG_ASSOC("rx_chain=0x%Xi active=%d idle=%d\n",
+       IWL_DEBUG_ASSOC("rx_chain=0x%X active=%d idle=%d\n",
                        priv->staging_rxon.rx_chain,
                        active_rx_cnt, idle_rx_cnt);
 
@@ -912,7 +935,7 @@ int iwl_init_drv(struct iwl_priv *priv)
        priv->iw_mode = IEEE80211_IF_TYPE_STA;
 
        priv->use_ant_b_for_management_frame = 1; /* start with ant B */
-       priv->ps_mode = IWL_MIMO_PS_NONE;
+       priv->current_ht_config.sm_ps = WLAN_HT_CAP_SM_PS_DISABLED;
 
        /* Choose which receivers/antennas to use */
        iwl_set_rxon_chain(priv);
@@ -1135,7 +1158,6 @@ int iwl_verify_ucode(struct iwl_priv *priv)
 }
 EXPORT_SYMBOL(iwl_verify_ucode);
 
-
 static const char *desc_lookup(int i)
 {
        switch (i) {
@@ -1216,9 +1238,9 @@ EXPORT_SYMBOL(iwl_dump_nic_error_log);
 /**
  * iwl_print_event_log - Dump error event log to syslog
  *
- * NOTE: Must be called with iwl4965_grab_nic_access() already obtained!
+ * NOTE: Must be called with iwl_grab_nic_access() already obtained!
  */
-void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
+static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
                                u32 num_events, u32 mode)
 {
        u32 i;
@@ -1259,8 +1281,6 @@ void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
                }
        }
 }
-EXPORT_SYMBOL(iwl_print_event_log);
-
 
 void iwl_dump_nic_event_log(struct iwl_priv *priv)
 {
index b5db050b22d1aea7cc990759b43e87c600763376..55a4b584ce0779af1eb33ed3b48b1cdc2138d9ce 100644 (file)
@@ -184,7 +184,6 @@ struct iwl_cfg {
 struct ieee80211_hw *iwl_alloc_all(struct iwl_cfg *cfg,
                struct ieee80211_ops *hw_ops);
 void iwl_hw_detect(struct iwl_priv *priv);
-
 void iwl_clear_stations_table(struct iwl_priv *priv);
 void iwl_reset_qos(struct iwl_priv *priv);
 void iwl_set_rxon_chain(struct iwl_priv *priv);
@@ -215,7 +214,6 @@ void iwl_rx_replenish(struct iwl_priv *priv);
 int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
 int iwl_rx_agg_start(struct iwl_priv *priv, const u8 *addr, int tid, u16 ssn);
 int iwl_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid);
-/* FIXME: remove when TX is moved to iwl core */
 int iwl_rx_queue_restock(struct iwl_priv *priv);
 int iwl_rx_queue_space(const struct iwl_rx_queue *q);
 void iwl_rx_allocate(struct iwl_priv *priv);
@@ -234,11 +232,7 @@ void iwl_rx_statistics(struct iwl_priv *priv,
 ******************************************************/
 int iwl_txq_ctx_reset(struct iwl_priv *priv);
 int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb);
-/* FIXME: remove when free Tx is fully merged into iwlcore */
-int iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq);
 void iwl_hw_txq_ctx_free(struct iwl_priv *priv);
-int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *tfd,
-                                       dma_addr_t addr, u16 len);
 int iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq);
 int iwl_tx_agg_start(struct iwl_priv *priv, const u8 *ra, u16 tid, u16 *ssn);
 int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid);
@@ -253,6 +247,7 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force);
  * RF -Kill - here and not in iwl-rfkill.h to be available when
  * RF-kill subsystem is not compiled.
  ****************************************************/
+void iwl_rf_kill(struct iwl_priv *priv);
 void iwl_radio_kill_sw_disable_radio(struct iwl_priv *priv);
 int iwl_radio_kill_sw_enable_radio(struct iwl_priv *priv);
 
@@ -283,7 +278,6 @@ static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags)
 void iwl_init_scan_params(struct iwl_priv *priv);
 int iwl_scan_cancel(struct iwl_priv *priv);
 int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
-const char *iwl_escape_essid(const char *essid, u8 essid_len);
 int iwl_scan_initiate(struct iwl_priv *priv);
 void iwl_setup_rx_scan_handlers(struct iwl_priv *priv);
 void iwl_setup_scan_deferred_work(struct iwl_priv *priv);
@@ -316,8 +310,6 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd);
 /*****************************************************
 *  Error Handling Debugging
 ******************************************************/
-void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
-                        u32 num_events, u32 mode);
 void iwl_dump_nic_error_log(struct iwl_priv *priv);
 void iwl_dump_nic_event_log(struct iwl_priv *priv);
 
index 640ceea913c7bf50d70851403bab72e35c3d8f71..1823687e5820ce0257e0768eb79d531b3cafb0c3 100644 (file)
@@ -406,6 +406,7 @@ struct iwl_ht_info {
        /* self configuration data */
        u8 is_ht;
        u8 supported_chan_width;
+       u8 sm_ps;
        u8 is_green_field;
        u8 sgf;                 /* HT_SHORT_GI_* short guard interval */
        u8 max_amsdu_size;
@@ -564,50 +565,31 @@ struct iwl_hw_params {
 #define IWL_RX_STATS(x) (&x->u.rx_frame.stats)
 #define IWL_RX_DATA(x) (IWL_RX_HDR(x)->payload)
 
-
-/******************************************************************************
- *
- * Functions implemented in iwl-base.c which are forward declared here
- * for use by iwl-*.c
- *
- *****************************************************************************/
-struct iwl_addsta_cmd;
-extern int iwl_send_add_sta(struct iwl_priv *priv,
-                           struct iwl_addsta_cmd *sta, u8 flags);
-u8 iwl_add_station_flags(struct iwl_priv *priv, const u8 *addr, int is_ap,
-                        u8 flags, struct ieee80211_ht_info *ht_info);
-extern unsigned int iwl4965_fill_beacon_frame(struct iwl_priv *priv,
-                                       struct ieee80211_hdr *hdr,
-                                       const u8 *dest, int left);
-extern void iwl4965_update_chain_flags(struct iwl_priv *priv);
-int iwl4965_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src);
-extern int iwl4965_set_power(struct iwl_priv *priv, void *cmd);
-
-extern const u8 iwl_bcast_addr[ETH_ALEN];
-
 /******************************************************************************
  *
- * Functions implemented in iwl-[34]*.c which are forward declared here
- * for use by iwl-base.c
+ * Functions implemented in core module which are forward declared here
+ * for use by iwl-[4-5].c
  *
- * NOTE:  The implementation of these functions are hardware specific
- * which is why they are in the hardware specific files (vs. iwl-base.c)
+ * NOTE:  The implementation of these functions are not hardware specific
+ * which is why they are in the core module files.
  *
  * Naming convention --
- * iwl4965_         <-- Its part of iwlwifi (should be changed to iwl4965_)
- * iwl4965_hw_      <-- Hardware specific (implemented in iwl-XXXX.c by all HW)
+ * iwl_         <-- Is part of iwlwifi
  * iwlXXXX_     <-- Hardware specific (implemented in iwl-XXXX.c for XXXX)
  * iwl4965_bg_      <-- Called from work queue context
  * iwl4965_mac_     <-- mac80211 callback
  *
  ****************************************************************************/
+struct iwl_addsta_cmd;
+extern int iwl_send_add_sta(struct iwl_priv *priv,
+                           struct iwl_addsta_cmd *sta, u8 flags);
+extern u8 iwl_add_station_flags(struct iwl_priv *priv, const u8 *addr,
+                       int is_ap, u8 flags, struct ieee80211_ht_info *ht_info);
+extern void iwl4965_update_chain_flags(struct iwl_priv *priv);
+extern int iwl4965_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src);
+extern const u8 iwl_bcast_addr[ETH_ALEN];
 extern int iwl_rxq_stop(struct iwl_priv *priv);
 extern void iwl_txq_ctx_stop(struct iwl_priv *priv);
-extern unsigned int iwl4965_hw_get_beacon_cmd(struct iwl_priv *priv,
-                                struct iwl_frame *frame, u8 rate);
-extern void iwl4965_disable_events(struct iwl_priv *priv);
-
-extern int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel);
 extern int iwl_queue_space(const struct iwl_queue *q);
 static inline int iwl_queue_used(const struct iwl_queue *q, int i)
 {
@@ -644,11 +626,6 @@ struct iwl_kw {
 #define IWL_CHANNEL_WIDTH_20MHZ   0
 #define IWL_CHANNEL_WIDTH_40MHZ   1
 
-#define IWL_MIMO_PS_STATIC        0
-#define IWL_MIMO_PS_NONE          3
-#define IWL_MIMO_PS_DYNAMIC       1
-#define IWL_MIMO_PS_INVALID       2
-
 #define IWL_OPERATION_MODE_AUTO     0
 #define IWL_OPERATION_MODE_HT_ONLY  1
 #define IWL_OPERATION_MODE_MIXED    2
@@ -703,8 +680,9 @@ enum iwl4965_false_alarm_state {
 
 enum iwl4965_chain_noise_state {
        IWL_CHAIN_NOISE_ALIVE = 0,  /* must be 0 */
-       IWL_CHAIN_NOISE_ACCUMULATE = 1,
-       IWL_CHAIN_NOISE_CALIBRATED = 2,
+       IWL_CHAIN_NOISE_ACCUMULATE,
+       IWL_CHAIN_NOISE_CALIBRATED,
+       IWL_CHAIN_NOISE_DONE,
 };
 
 enum iwl4965_calib_enabled_state {
@@ -762,17 +740,18 @@ struct iwl_sensitivity_data {
 
 /* Chain noise (differential Rx gain) calib data */
 struct iwl_chain_noise_data {
-       u8 state;
-       u16 beacon_count;
+       u32 active_chains;
        u32 chain_noise_a;
        u32 chain_noise_b;
        u32 chain_noise_c;
        u32 chain_signal_a;
        u32 chain_signal_b;
        u32 chain_signal_c;
+       u16 beacon_count;
        u8 disconn_array[NUM_RX_CHAINS];
        u8 delta_gain_code[NUM_RX_CHAINS];
        u8 radio_write;
+       u8 state;
 };
 
 #define        EEPROM_SEM_TIMEOUT 10           /* milliseconds */
@@ -995,7 +974,6 @@ struct iwl_priv {
         * hardware */
        u16 assoc_id;
        u16 assoc_capability;
-       u8 ps_mode;
 
        struct iwl_qos_info qos_data;
 
index 16f834d0c486cc64df1e88eaf0f7808a0bf2ac88..55ec31ec9e1599f318a2f9e191b072a20cb531c4 100644 (file)
@@ -250,17 +250,26 @@ static int iwl_update_power_command(struct iwl_priv *priv,
 
 
 /*
- * calucaute the final power mode index
+ * compute the final power mode index
  */
-int iwl_power_update_mode(struct iwl_priv *priv, u8 refresh)
+int iwl_power_update_mode(struct iwl_priv *priv, bool force)
 {
        struct iwl_power_mgr *setting = &(priv->power_data);
        int ret = 0;
        u16 uninitialized_var(final_mode);
 
-       /* If on battery, set to 3,
-       * if plugged into AC power, set to CAM ("continuously aware mode"),
-       * else user level */
+       /* Don't update the RX chain when chain noise calibration is running */
+       if (priv->chain_noise_data.state != IWL_CHAIN_NOISE_DONE &&
+           priv->chain_noise_data.state != IWL_CHAIN_NOISE_ALIVE) {
+               IWL_DEBUG_POWER("Cannot update the power, chain noise "
+                       "calibration running: %d\n",
+                       priv->chain_noise_data.state);
+               return -EAGAIN;
+       }
+
+       /* If on battery, set to 3,
+        * if plugged into AC power, set to CAM ("continuously aware mode"),
+        * else user level */
 
        switch (setting->system_power_setting) {
        case IWL_POWER_SYS_AUTO:
@@ -285,7 +294,7 @@ int iwl_power_update_mode(struct iwl_priv *priv, u8 refresh)
                final_mode = IWL_POWER_MODE_CAM;
 
        if (!iwl_is_rfkill(priv) && !setting->power_disabled &&
-           ((setting->power_mode != final_mode) || refresh)) {
+           ((setting->power_mode != final_mode) || force)) {
                struct iwl_powertable_cmd cmd;
 
                if (final_mode != IWL_POWER_MODE_CAM)
@@ -359,35 +368,26 @@ EXPORT_SYMBOL(iwl_power_enable_management);
 /* set user_power_setting */
 int iwl_power_set_user_mode(struct iwl_priv *priv, u16 mode)
 {
-       int ret = 0;
-
        if (mode > IWL_POWER_LIMIT)
                return -EINVAL;
 
        priv->power_data.user_power_setting = mode;
 
-       ret = iwl_power_update_mode(priv, 0);
-
-       return ret;
+       return iwl_power_update_mode(priv, 0);
 }
 EXPORT_SYMBOL(iwl_power_set_user_mode);
 
-
 /* set system_power_setting. This should be set by over all
  * PM application.
  */
 int iwl_power_set_system_mode(struct iwl_priv *priv, u16 mode)
 {
-       int ret = 0;
-
        if (mode > IWL_POWER_LIMIT)
                return -EINVAL;
 
        priv->power_data.system_power_setting = mode;
 
-       ret = iwl_power_update_mode(priv, 0);
-
-       return ret;
+       return iwl_power_update_mode(priv, 0);
 }
 EXPORT_SYMBOL(iwl_power_set_system_mode);
 
index aa99f3647def8907166df4a54847dde985f9a000..df484a90ae64f521e798243ebb751eef2ffd0bf1 100644 (file)
@@ -72,7 +72,7 @@ struct iwl_power_mgr {
        /* final power level that used to calculate final power command */
        u8 power_mode;
        u8 user_power_setting; /* set by user through mac80211 or sysfs */
-       u8 system_power_setting; /* set by kernel syatem tools */
+       u8 system_power_setting; /* set by kernel system tools */
        u8 critical_power_setting; /* set if driver over heated */
        u8 is_battery_active; /* DC/AC power */
        u8 power_disabled; /* flag to disable using power saving level */
@@ -80,7 +80,7 @@ struct iwl_power_mgr {
 
 void iwl_setup_power_deferred_work(struct iwl_priv *priv);
 void iwl_power_cancel_timeout(struct iwl_priv *priv);
-int iwl_power_update_mode(struct iwl_priv *priv, u8 refresh);
+int iwl_power_update_mode(struct iwl_priv *priv, bool force);
 int iwl_power_disable_management(struct iwl_priv *priv, u32 ms);
 int iwl_power_enable_management(struct iwl_priv *priv);
 int iwl_power_set_user_mode(struct iwl_priv *priv, u16 mode);
index 6c8ac3a87d547f5ca2c4ad1543ec530e180dd467..d026aaf6233570cd715fd8139ecdbab6300cd4c2 100644 (file)
@@ -88,7 +88,7 @@ static int iwl_is_empty_essid(const char *essid, int essid_len)
 
 
 
-const char *iwl_escape_essid(const char *essid, u8 essid_len)
+static const char *iwl_escape_essid(const char *essid, u8 essid_len)
 {
        static char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
        const char *s = essid;
@@ -111,7 +111,6 @@ const char *iwl_escape_essid(const char *essid, u8 essid_len)
        *d = '\0';
        return escaped;
 }
-EXPORT_SYMBOL(iwl_escape_essid);
 
 /**
  * iwl_scan_cancel - Cancel any currently executing HW scan
index 5b7b05c8773f02be6dc6b37c741e03fd022e01fd..a72569f1acb590fcec26fce256c3f12f4b40ecbc 100644 (file)
@@ -191,20 +191,20 @@ static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index,
        if (!sta_ht_inf || !sta_ht_inf->ht_supported)
                goto done;
 
-       mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2;
+       mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_SM_PS) >> 2;
 
        sta_flags = priv->stations[index].sta.station_flags;
 
        sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK);
 
        switch (mimo_ps_mode) {
-       case WLAN_HT_CAP_MIMO_PS_STATIC:
+       case WLAN_HT_CAP_SM_PS_STATIC:
                sta_flags |= STA_FLG_MIMO_DIS_MSK;
                break;
-       case WLAN_HT_CAP_MIMO_PS_DYNAMIC:
+       case WLAN_HT_CAP_SM_PS_DYNAMIC:
                sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK;
                break;
-       case WLAN_HT_CAP_MIMO_PS_DISABLED:
+       case WLAN_HT_CAP_SM_PS_DISABLED:
                break;
        default:
                IWL_WARNING("Invalid MIMO PS mode %d\n", mimo_ps_mode);
index 9d485aadef96cda09d8ed1060e87b12544e668fc..9d5bcf46cbe91585affc687ac97e6b64215ff722 100644 (file)
@@ -63,7 +63,7 @@ static const u16 default_tid_to_tx_fifo[] = {
  * Does NOT advance any TFD circular buffer read/write indexes
  * Does NOT free the TFD itself (which is within circular buffer)
  */
-int iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq)
+static int iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq)
 {
        struct iwl_tfd_frame *bd_tmp = (struct iwl_tfd_frame *)&txq->bd[0];
        struct iwl_tfd_frame *bd = &bd_tmp[txq->q.read_ptr];
@@ -115,10 +115,8 @@ int iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq)
        }
        return 0;
 }
-EXPORT_SYMBOL(iwl_hw_txq_free_tfd);
 
-
-int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *ptr,
+static int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *ptr,
                                 dma_addr_t addr, u16 len)
 {
        int index, is_odd;
@@ -151,7 +149,6 @@ int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *ptr,
 
        return 0;
 }
-EXPORT_SYMBOL(iwl_hw_txq_attach_buf_to_tfd);
 
 /**
  * iwl_txq_update_write_ptr - Send new write index to hardware
@@ -478,7 +475,6 @@ void iwl_hw_txq_ctx_free(struct iwl_priv *priv)
 }
 EXPORT_SYMBOL(iwl_hw_txq_ctx_free);
 
-
 /**
  * iwl_txq_ctx_reset - Reset TX queue context
  * Destroys all DMA structures and initialise them again
@@ -545,6 +541,7 @@ int iwl_txq_ctx_reset(struct iwl_priv *priv)
  error_kw:
        return ret;
 }
+
 /**
  * iwl_txq_ctx_stop - Stop all Tx DMA channels, free Tx queue memory
  */
index 802547e7967190e8f2c3bdb2735a1c0e9fedeb1c..5fef05f3cd008774699199f0ac77578277ae43ab 100644 (file)
@@ -1971,6 +1971,70 @@ void lbs_ps_confirm_sleep(struct lbs_private *priv)
 }
 
 
+/**
+ * @brief Configures the transmission power control functionality.
+ *
+ * @param priv         A pointer to struct lbs_private structure
+ * @param enable       Transmission power control enable
+ * @param p0           Power level when link quality is good (dBm).
+ * @param p1           Power level when link quality is fair (dBm).
+ * @param p2           Power level when link quality is poor (dBm).
+ * @param usesnr       Use Signal to Noise Ratio in TPC
+ *
+ * @return 0 on success
+ */
+int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1,
+               int8_t p2, int usesnr)
+{
+       struct cmd_ds_802_11_tpc_cfg cmd;
+       int ret;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+       cmd.action = cpu_to_le16(CMD_ACT_SET);
+       cmd.enable = !!enable;
+       cmd.usesnr = !!enable;
+       cmd.P0 = p0;
+       cmd.P1 = p1;
+       cmd.P2 = p2;
+
+       ret = lbs_cmd_with_response(priv, CMD_802_11_TPC_CFG, &cmd);
+
+       return ret;
+}
+
+/**
+ * @brief Configures the power adaptation settings.
+ *
+ * @param priv         A pointer to struct lbs_private structure
+ * @param enable       Power adaptation enable
+ * @param p0           Power level for 1, 2, 5.5 and 11 Mbps (dBm).
+ * @param p1           Power level for 6, 9, 12, 18, 22, 24 and 36 Mbps (dBm).
+ * @param p2           Power level for 48 and 54 Mbps (dBm).
+ *
+ * @return 0 on Success
+ */
+
+int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
+               int8_t p1, int8_t p2)
+{
+       struct cmd_ds_802_11_pa_cfg cmd;
+       int ret;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+       cmd.action = cpu_to_le16(CMD_ACT_SET);
+       cmd.enable = !!enable;
+       cmd.P0 = p0;
+       cmd.P1 = p1;
+       cmd.P2 = p2;
+
+       ret = lbs_cmd_with_response(priv, CMD_802_11_PA_CFG , &cmd);
+
+       return ret;
+}
+
+
 static struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
        uint16_t command, struct cmd_header *in_cmd, int in_cmd_size,
        int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
index 11ac996e895ed2d136370b42837d20869c9b38ec..336a181d857a0a15d31a2e44c31f74de2b3da089 100644 (file)
@@ -26,6 +26,12 @@ int __lbs_cmd(struct lbs_private *priv, uint16_t command,
              int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
              unsigned long callback_arg);
 
+int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
+               int8_t p1, int8_t p2);
+
+int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1,
+               int8_t p2, int usesnr);
+
 int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra,
                     struct cmd_header *resp);
 
index 4b2428ac2223805843c498c186b10004633dc220..c89d7a1041a83ffa9e2b6e3a7e5b0d62e0efc2ca 100644 (file)
@@ -189,6 +189,15 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in
 #define MRVDRV_CMD_UPLD_RDY            0x0008
 #define MRVDRV_CARDEVENT               0x0010
 
+
+/* Automatic TX control default levels */
+#define POW_ADAPT_DEFAULT_P0 13
+#define POW_ADAPT_DEFAULT_P1 15
+#define POW_ADAPT_DEFAULT_P2 18
+#define TPC_DEFAULT_P0 5
+#define TPC_DEFAULT_P1 10
+#define TPC_DEFAULT_P2 13
+
 /** TxPD status */
 
 /*     Station firmware use TxPD status field to report final Tx transmit
index da618fc997cec132eb4613590ed1ea8bacf53fbb..a916bb9bd5dacdb410e98699dd20a04d4333b5e5 100644 (file)
@@ -83,6 +83,7 @@
 #define CMD_802_11_INACTIVITY_TIMEOUT          0x0067
 #define CMD_802_11_SLEEP_PERIOD                        0x0068
 #define CMD_802_11_TPC_CFG                     0x0072
+#define CMD_802_11_PA_CFG                      0x0073
 #define CMD_802_11_FW_WAKE_METHOD              0x0074
 #define CMD_802_11_SUBSCRIBE_EVENT             0x0075
 #define CMD_802_11_RATE_ADAPT_RATESET          0x0076
index d27c276b219182b86b06fa4e7db536ab0d03f639..630b79967560902193feeba03557ba102440cc49 100644 (file)
@@ -607,14 +607,28 @@ struct cmd_ds_802_11_eeprom_access {
 } __attribute__ ((packed));
 
 struct cmd_ds_802_11_tpc_cfg {
+       struct cmd_header hdr;
+
        __le16 action;
-       u8 enable;
-       s8 P0;
-       s8 P1;
-       s8 P2;
-       u8 usesnr;
+       uint8_t enable;
+       int8_t P0;
+       int8_t P1;
+       int8_t P2;
+       uint8_t usesnr;
 } __attribute__ ((packed));
 
+
+struct cmd_ds_802_11_pa_cfg {
+       struct cmd_header hdr;
+
+       __le16 action;
+       uint8_t enable;
+       int8_t P0;
+       int8_t P1;
+       int8_t P2;
+} __attribute__ ((packed));
+
+
 struct cmd_ds_802_11_led_ctrl {
        __le16 action;
        __le16 numled;
index 2436634b6b7e39c04450e0f2c62d0054ee6c7ce4..73dc8c72402a93fbfa85d1477869d370454c4cd2 100644 (file)
@@ -1205,7 +1205,13 @@ void lbs_remove_card(struct lbs_private *priv)
        cancel_delayed_work_sync(&priv->scan_work);
        cancel_delayed_work_sync(&priv->assoc_work);
        cancel_work_sync(&priv->mcast_work);
+
+       /* worker thread destruction blocks on the in-flight command which
+        * should have been cleared already in lbs_stop_card().
+        */
+       lbs_deb_main("destroying worker thread\n");
        destroy_workqueue(priv->work_thread);
+       lbs_deb_main("done destroying worker thread\n");
 
        if (priv->psmode == LBS802_11POWERMODEMAX_PSP) {
                priv->psmode = LBS802_11POWERMODECAM;
@@ -1323,14 +1329,26 @@ void lbs_stop_card(struct lbs_private *priv)
                device_remove_file(&dev->dev, &dev_attr_lbs_rtap);
        }
 
-       /* Flush pending command nodes */
+       /* Delete the timeout of the currently processing command */
        del_timer_sync(&priv->command_timer);
+
+       /* Flush pending command nodes */
        spin_lock_irqsave(&priv->driver_lock, flags);
+       lbs_deb_main("clearing pending commands\n");
        list_for_each_entry(cmdnode, &priv->cmdpendingq, list) {
                cmdnode->result = -ENOENT;
                cmdnode->cmdwaitqwoken = 1;
                wake_up_interruptible(&cmdnode->cmdwait_q);
        }
+
+       /* Flush the command the card is currently processing */
+       if (priv->cur_cmd) {
+               lbs_deb_main("clearing current command\n");
+               priv->cur_cmd->result = -ENOENT;
+               priv->cur_cmd->cmdwaitqwoken = 1;
+               wake_up_interruptible(&priv->cur_cmd->cmdwait_q);
+       }
+       lbs_deb_main("done clearing commands\n");
        spin_unlock_irqrestore(&priv->driver_lock, flags);
 
        unregister_netdev(dev);
index 426f1fe3bb4298aadc6d2ec3d52060ba91d2ff94..e8cadad2c863acd8e11334287a93dd395121c527 100644 (file)
@@ -1820,7 +1820,21 @@ static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
        }
 
        if (vwrq->fixed == 0) {
-               /* Auto power control */
+               /* User requests automatic tx power control, however there are
+                * many auto tx settings.  For now use firmware defaults until
+                * we come up with a good way to expose these to the user. */
+               if (priv->fwrelease < 0x09000000) {
+                       ret = lbs_set_power_adapt_cfg(priv, 1,
+                                       POW_ADAPT_DEFAULT_P0,
+                                       POW_ADAPT_DEFAULT_P1,
+                                       POW_ADAPT_DEFAULT_P2);
+                       if (ret)
+                               goto out;
+               }
+               ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1,
+                               TPC_DEFAULT_P2, 1);
+               if (ret)
+                       goto out;
                dbm = priv->txpower_max;
        } else {
                /* Userspace check in iwrange if it should use dBm or mW,
@@ -1830,7 +1844,8 @@ static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
                        goto out;
                }
 
-               /* Validate requested power level against firmware allowed levels */
+               /* Validate requested power level against firmware allowed
+                * levels */
                if (priv->txpower_min && (dbm < priv->txpower_min)) {
                        ret = -EINVAL;
                        goto out;
@@ -1840,6 +1855,18 @@ static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
                        ret = -EINVAL;
                        goto out;
                }
+               if (priv->fwrelease < 0x09000000) {
+                       ret = lbs_set_power_adapt_cfg(priv, 0,
+                                       POW_ADAPT_DEFAULT_P0,
+                                       POW_ADAPT_DEFAULT_P1,
+                                       POW_ADAPT_DEFAULT_P2);
+                       if (ret)
+                               goto out;
+               }
+               ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1,
+                               TPC_DEFAULT_P2, 1);
+               if (ret)
+                       goto out;
        }
 
        /* If the radio was off, turn it on */
index 98d4f8e7d84d1cfdb0468e0d43d09b64e5721910..1d0704fe146fb72ed0691271a7bd8491096481b2 100644 (file)
@@ -80,6 +80,7 @@ struct p54_common {
        struct pda_channel_output_limit *output_limit;
        unsigned int output_limit_len;
        struct pda_pa_curve_data *curve_data;
+       unsigned int filter_flags;
        u16 rxhw;
        u8 version;
        u8 rx_antenna;
@@ -87,7 +88,15 @@ struct p54_common {
        void *cached_vdcf;
        unsigned int fw_var;
        unsigned int fw_interface;
+       unsigned int output_power;
+       u32 tsf_low32;
+       u32 tsf_high32;
        struct ieee80211_tx_queue_stats tx_stats[8];
+       struct ieee80211_low_level_stats stats;
+       struct timer_list stats_timer;
+       struct completion stats_comp;
+       void *cached_stats;
+       int noise;
        void *eeprom;
        struct completion eeprom_comp;
 };
index f96f7c7e6af59d48457b82e9d21dafd06088b1ca..da51786254dc66b7a9a0203a29f0df07bd2907db 100644 (file)
@@ -27,7 +27,7 @@ MODULE_DESCRIPTION("Softmac Prism54 common code");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("prism54common");
 
-static struct ieee80211_rate p54_rates[] = {
+static struct ieee80211_rate p54_bgrates[] = {
        { .bitrate = 10, .hw_value = 0, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
        { .bitrate = 20, .hw_value = 1, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
        { .bitrate = 55, .hw_value = 2, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
@@ -42,7 +42,7 @@ static struct ieee80211_rate p54_rates[] = {
        { .bitrate = 540, .hw_value = 11, },
 };
 
-static struct ieee80211_channel p54_channels[] = {
+static struct ieee80211_channel p54_bgchannels[] = {
        { .center_freq = 2412, .hw_value = 1, },
        { .center_freq = 2417, .hw_value = 2, },
        { .center_freq = 2422, .hw_value = 3, },
@@ -60,10 +60,66 @@ static struct ieee80211_channel p54_channels[] = {
 };
 
 static struct ieee80211_supported_band band_2GHz = {
-       .channels = p54_channels,
-       .n_channels = ARRAY_SIZE(p54_channels),
-       .bitrates = p54_rates,
-       .n_bitrates = ARRAY_SIZE(p54_rates),
+       .channels = p54_bgchannels,
+       .n_channels = ARRAY_SIZE(p54_bgchannels),
+       .bitrates = p54_bgrates,
+       .n_bitrates = ARRAY_SIZE(p54_bgrates),
+};
+
+static struct ieee80211_rate p54_arates[] = {
+       { .bitrate = 60, .hw_value = 4, },
+       { .bitrate = 90, .hw_value = 5, },
+       { .bitrate = 120, .hw_value = 6, },
+       { .bitrate = 180, .hw_value = 7, },
+       { .bitrate = 240, .hw_value = 8, },
+       { .bitrate = 360, .hw_value = 9, },
+       { .bitrate = 480, .hw_value = 10, },
+       { .bitrate = 540, .hw_value = 11, },
+};
+
+static struct ieee80211_channel p54_achannels[] = {
+       { .center_freq = 4920 },
+       { .center_freq = 4940 },
+       { .center_freq = 4960 },
+       { .center_freq = 4980 },
+       { .center_freq = 5040 },
+       { .center_freq = 5060 },
+       { .center_freq = 5080 },
+       { .center_freq = 5170 },
+       { .center_freq = 5180 },
+       { .center_freq = 5190 },
+       { .center_freq = 5200 },
+       { .center_freq = 5210 },
+       { .center_freq = 5220 },
+       { .center_freq = 5230 },
+       { .center_freq = 5240 },
+       { .center_freq = 5260 },
+       { .center_freq = 5280 },
+       { .center_freq = 5300 },
+       { .center_freq = 5320 },
+       { .center_freq = 5500 },
+       { .center_freq = 5520 },
+       { .center_freq = 5540 },
+       { .center_freq = 5560 },
+       { .center_freq = 5580 },
+       { .center_freq = 5600 },
+       { .center_freq = 5620 },
+       { .center_freq = 5640 },
+       { .center_freq = 5660 },
+       { .center_freq = 5680 },
+       { .center_freq = 5700 },
+       { .center_freq = 5745 },
+       { .center_freq = 5765 },
+       { .center_freq = 5785 },
+       { .center_freq = 5805 },
+       { .center_freq = 5825 },
+};
+
+static struct ieee80211_supported_band band_5GHz = {
+       .channels = p54_achannels,
+       .n_channels = ARRAY_SIZE(p54_achannels),
+       .bitrates = p54_arates,
+       .n_bitrates = ARRAY_SIZE(p54_arates),
 };
 
 int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
@@ -252,6 +308,7 @@ static int p54_convert_rev1(struct ieee80211_hw *dev,
 
 const char* p54_rf_chips[] = { "NULL", "Indigo?", "Duette",
                               "Frisbee", "Xbow", "Longbow" };
+static int p54_init_xbow_synth(struct ieee80211_hw *dev);
 
 int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
 {
@@ -371,20 +428,20 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
        }
 
        switch (priv->rxhw) {
-               case 4: /* XBow */
-               case 1: /* Indigo? */
-               case 2: /* Duette */
-                       /* TODO: 5GHz initialization goes here */
-
-               case 3: /* Frisbee */
-               case 5: /* Longbow */
-                       dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &band_2GHz;
-                       break;
-               default:
-                       printk(KERN_ERR "%s: unsupported RF-Chip\n",
-                               wiphy_name(dev->wiphy));
-                       err = -EINVAL;
-                       goto err;
+       case 4: /* XBow */
+               p54_init_xbow_synth(dev);
+       case 1: /* Indigo? */
+       case 2: /* Duette */
+               dev->wiphy->bands[IEEE80211_BAND_5GHZ] = &band_5GHz;
+       case 3: /* Frisbee */
+       case 5: /* Longbow */
+               dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &band_2GHz;
+               break;
+       default:
+               printk(KERN_ERR "%s: unsupported RF-Chip\n",
+                       wiphy_name(dev->wiphy));
+               err = -EINVAL;
+               goto err;
        }
 
        if (!is_valid_ether_addr(dev->wiphy->perm_addr)) {
@@ -424,21 +481,43 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
 }
 EXPORT_SYMBOL_GPL(p54_parse_eeprom);
 
+static int p54_rssi_to_dbm(struct ieee80211_hw *dev, int rssi)
+{
+       /* TODO: get the rssi_add & rssi_mul data from the eeprom */
+       return ((rssi * 0x83) / 64 - 400) / 4;
+}
+
 static int p54_rx_data(struct ieee80211_hw *dev, struct sk_buff *skb)
 {
+       struct p54_common *priv = dev->priv;
        struct p54_rx_hdr *hdr = (struct p54_rx_hdr *) skb->data;
        struct ieee80211_rx_status rx_status = {0};
        u16 freq = le16_to_cpu(hdr->freq);
        size_t header_len = sizeof(*hdr);
+       u32 tsf32;
 
-       rx_status.signal = hdr->rssi;
+       if (!(hdr->magic & cpu_to_le16(0x0001))) {
+               if (priv->filter_flags & FIF_FCSFAIL)
+                       rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
+               else
+                       return 0;
+       }
+
+       rx_status.signal = p54_rssi_to_dbm(dev, hdr->rssi);
+       rx_status.noise = priv->noise;
        /* XX correct? */
        rx_status.qual = (100 * hdr->rssi) / 127;
        rx_status.rate_idx = hdr->rate & 0xf;
        rx_status.freq = freq;
        rx_status.band = IEEE80211_BAND_2GHZ;
        rx_status.antenna = hdr->antenna;
-       rx_status.mactime = le64_to_cpu(hdr->timestamp);
+
+       tsf32 = le32_to_cpu(hdr->tsf32);
+       if (tsf32 < priv->tsf_low32)
+               priv->tsf_high32++;
+       rx_status.mactime = ((u64)priv->tsf_high32) << 32 | tsf32;
+       priv->tsf_low32 = tsf32;
+
        rx_status.flag |= RX_FLAG_TSFT;
 
        if (hdr->magic & cpu_to_le16(0x4000))
@@ -511,7 +590,8 @@ static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb)
                                        info->status.excessive_retries = 1;
                        }
                        info->status.retry_count = payload->retries - 1;
-                       info->status.ack_signal = le16_to_cpu(payload->ack_rssi);
+                       info->status.ack_signal = p54_rssi_to_dbm(dev,
+                                       le16_to_cpu(payload->ack_rssi));
                        skb_pull(entry, sizeof(*hdr) + pad + sizeof(*entry_data));
                        ieee80211_tx_status_irqsafe(dev, entry);
                        goto out;
@@ -542,6 +622,27 @@ static void p54_rx_eeprom_readback(struct ieee80211_hw *dev,
        complete(&priv->eeprom_comp);
 }
 
+static void p54_rx_stats(struct ieee80211_hw *dev, struct sk_buff *skb)
+{
+       struct p54_common *priv = dev->priv;
+       struct p54_control_hdr *hdr = (struct p54_control_hdr *) skb->data;
+       struct p54_statistics *stats = (struct p54_statistics *) hdr->data;
+       u32 tsf32 = le32_to_cpu(stats->tsf32);
+
+       if (tsf32 < priv->tsf_low32)
+               priv->tsf_high32++;
+       priv->tsf_low32 = tsf32;
+
+       priv->stats.dot11RTSFailureCount = le32_to_cpu(stats->rts_fail);
+       priv->stats.dot11RTSSuccessCount = le32_to_cpu(stats->rts_success);
+       priv->stats.dot11FCSErrorCount = le32_to_cpu(stats->rx_bad_fcs);
+
+       priv->noise = p54_rssi_to_dbm(dev, le32_to_cpu(stats->noise));
+       complete(&priv->stats_comp);
+
+       mod_timer(&priv->stats_timer, jiffies + 5 * HZ);
+}
+
 static int p54_rx_control(struct ieee80211_hw *dev, struct sk_buff *skb)
 {
        struct p54_control_hdr *hdr = (struct p54_control_hdr *) skb->data;
@@ -552,6 +653,9 @@ static int p54_rx_control(struct ieee80211_hw *dev, struct sk_buff *skb)
                break;
        case P54_CONTROL_TYPE_BBP:
                break;
+       case P54_CONTROL_TYPE_STAT_READBACK:
+               p54_rx_stats(dev, skb);
+               break;
        case P54_CONTROL_TYPE_EEPROM_READBACK:
                p54_rx_eeprom_readback(dev, skb);
                break;
@@ -753,7 +857,7 @@ static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
        txhdr->hw_queue = skb_get_queue_mapping(skb) + 4;
        txhdr->tx_antenna = (info->antenna_sel_tx == 0) ?
                2 : info->antenna_sel_tx - 1;
-       txhdr->output_power = 0x7f; // HW Maximum
+       txhdr->output_power = priv->output_power;
        txhdr->cts_rate = (info->flags & IEEE80211_TX_CTL_NO_ACK) ?
                          0 : cts_rate;
        if (padding)
@@ -1021,12 +1125,25 @@ static int p54_start(struct ieee80211_hw *dev)
                        return -ENOMEM;
        }
 
+       if (!priv->cached_stats) {
+               priv->cached_stats = kzalloc(sizeof(struct p54_statistics) +
+                       priv->tx_hdr_len + sizeof(struct p54_control_hdr),
+                       GFP_KERNEL);
+
+               if (!priv->cached_stats) {
+                       kfree(priv->cached_vdcf);
+                       priv->cached_vdcf = NULL;
+                       return -ENOMEM;
+               }
+       }
+
        err = priv->open(dev);
        if (!err)
                priv->mode = IEEE80211_IF_TYPE_MNTR;
 
        p54_init_vdcf(dev);
 
+       mod_timer(&priv->stats_timer, jiffies + HZ);
        return err;
 }
 
@@ -1034,9 +1151,12 @@ static void p54_stop(struct ieee80211_hw *dev)
 {
        struct p54_common *priv = dev->priv;
        struct sk_buff *skb;
+
+       del_timer(&priv->stats_timer);
        while ((skb = skb_dequeue(&priv->tx_queue)))
                kfree_skb(skb);
        priv->stop(dev);
+       priv->tsf_high32 = priv->tsf_low32 = 0;
        priv->mode = IEEE80211_IF_TYPE_INVALID;
 }
 
@@ -1091,6 +1211,7 @@ static int p54_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf)
        mutex_lock(&priv->conf_mutex);
        priv->rx_antenna = (conf->antenna_sel_rx == 0) ?
                2 : conf->antenna_sel_tx - 1;
+       priv->output_power = conf->power_level << 2;
        ret = p54_set_freq(dev, cpu_to_le16(conf->channel->center_freq));
        p54_set_vdcf(dev);
        mutex_unlock(&priv->conf_mutex);
@@ -1118,13 +1239,26 @@ static void p54_configure_filter(struct ieee80211_hw *dev,
 {
        struct p54_common *priv = dev->priv;
 
-       *total_flags &= FIF_BCN_PRBRESP_PROMISC;
+       *total_flags &= FIF_BCN_PRBRESP_PROMISC |
+                       FIF_PROMISC_IN_BSS |
+                       FIF_FCSFAIL;
+
+       priv->filter_flags = *total_flags;
 
        if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
                if (*total_flags & FIF_BCN_PRBRESP_PROMISC)
-                       p54_set_filter(dev, 0, NULL);
+                       p54_set_filter(dev, priv->filter_type, NULL);
                else
-                       p54_set_filter(dev, 0, priv->bssid);
+                       p54_set_filter(dev, priv->filter_type, priv->bssid);
+       }
+
+       if (changed_flags & FIF_PROMISC_IN_BSS) {
+               if (*total_flags & FIF_PROMISC_IN_BSS)
+                       p54_set_filter(dev, priv->filter_type |
+                               cpu_to_le16(0x8), NULL);
+               else
+                       p54_set_filter(dev, priv->filter_type &
+                               ~cpu_to_le16(0x8), priv->bssid);
        }
 }
 
@@ -1148,10 +1282,67 @@ static int p54_conf_tx(struct ieee80211_hw *dev, u16 queue,
        return 0;
 }
 
+static int p54_init_xbow_synth(struct ieee80211_hw *dev)
+{
+       struct p54_common *priv = dev->priv;
+       struct p54_control_hdr *hdr;
+       struct p54_tx_control_xbow_synth *xbow;
+
+       hdr = kzalloc(sizeof(*hdr) + sizeof(*xbow) +
+                     priv->tx_hdr_len, GFP_KERNEL);
+       if (!hdr)
+               return -ENOMEM;
+
+       hdr = (void *)hdr + priv->tx_hdr_len;
+       hdr->magic1 = cpu_to_le16(0x8001);
+       hdr->len = cpu_to_le16(sizeof(*xbow));
+       hdr->type = cpu_to_le16(P54_CONTROL_TYPE_XBOW_SYNTH_CFG);
+       p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + sizeof(*xbow));
+
+       xbow = (struct p54_tx_control_xbow_synth *) hdr->data;
+       xbow->magic1 = cpu_to_le16(0x1);
+       xbow->magic2 = cpu_to_le16(0x2);
+       xbow->freq = cpu_to_le16(5390);
+
+       priv->tx(dev, hdr, sizeof(*hdr) + sizeof(*xbow), 1);
+
+       return 0;
+}
+
+static void p54_statistics_timer(unsigned long data)
+{
+       struct ieee80211_hw *dev = (struct ieee80211_hw *) data;
+       struct p54_common *priv = dev->priv;
+       struct p54_control_hdr *hdr;
+       struct p54_statistics *stats;
+
+       BUG_ON(!priv->cached_stats);
+
+       hdr = (void *)priv->cached_stats + priv->tx_hdr_len;
+       hdr->magic1 = cpu_to_le16(0x8000);
+       hdr->len = cpu_to_le16(sizeof(*stats));
+       hdr->type = cpu_to_le16(P54_CONTROL_TYPE_STAT_READBACK);
+       p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + sizeof(*stats));
+
+       priv->tx(dev, hdr, sizeof(*hdr) + sizeof(*stats), 0);
+}
+
 static int p54_get_stats(struct ieee80211_hw *dev,
                         struct ieee80211_low_level_stats *stats)
 {
-       /* TODO */
+       struct p54_common *priv = dev->priv;
+
+       del_timer(&priv->stats_timer);
+       p54_statistics_timer((unsigned long)dev);
+
+       if (!wait_for_completion_interruptible_timeout(&priv->stats_comp, HZ)) {
+               printk(KERN_ERR "%s: device does not respond!\n",
+                       wiphy_name(dev->wiphy));
+               return -EBUSY;
+       }
+
+       memcpy(stats, &priv->stats, sizeof(*stats));
+
        return 0;
 }
 
@@ -1193,12 +1384,12 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
        skb_queue_head_init(&priv->tx_queue);
        dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | /* not sure */
                     IEEE80211_HW_RX_INCLUDES_FCS |
-                    IEEE80211_HW_SIGNAL_UNSPEC;
+                    IEEE80211_HW_SIGNAL_DBM |
+                    IEEE80211_HW_NOISE_DBM;
 
        dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
 
        dev->channel_change_time = 1000;        /* TODO: find actual value */
-       dev->max_signal = 127;
 
        priv->tx_stats[0].limit = 1;
        priv->tx_stats[1].limit = 1;
@@ -1206,11 +1397,15 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
        priv->tx_stats[3].limit = 1;
        priv->tx_stats[4].limit = 5;
        dev->queues = 1;
+       priv->noise = -94;
        dev->extra_tx_headroom = sizeof(struct p54_control_hdr) + 4 +
                                 sizeof(struct p54_tx_control_allocdata);
 
        mutex_init(&priv->conf_mutex);
        init_completion(&priv->eeprom_comp);
+       init_completion(&priv->stats_comp);
+       setup_timer(&priv->stats_timer, p54_statistics_timer,
+               (unsigned long)dev);
 
        return dev;
 }
@@ -1219,6 +1414,7 @@ EXPORT_SYMBOL_GPL(p54_init_common);
 void p54_free_common(struct ieee80211_hw *dev)
 {
        struct p54_common *priv = dev->priv;
+       kfree(priv->cached_stats);
        kfree(priv->iq_autocal);
        kfree(priv->output_limit);
        kfree(priv->curve_data);
index 73a9a2c923dd7e12695ae24ba1bf95eb8274fb48..4da736c789ac0c0125e111a9aa9f581d70f924db 100644 (file)
@@ -185,7 +185,8 @@ struct p54_rx_hdr {
        u8 rssi;
        u8 quality;
        u16 unknown2;
-       __le64 timestamp;
+       __le32 tsf32;
+       __le32 unalloc0;
        u8 align[0];
 } __attribute__ ((packed));
 
@@ -300,4 +301,24 @@ struct p54_tx_control_vdcf {
        __le16 frameburst;
 } __attribute__ ((packed));
 
+struct p54_statistics {
+       __le32 rx_success;
+       __le32 rx_bad_fcs;
+       __le32 rx_abort;
+       __le32 rx_abort_phy;
+       __le32 rts_success;
+       __le32 rts_fail;
+       __le32 tsf32;
+       __le32 airtime;
+       __le32 noise;
+       __le32 unkn[10]; /* CCE / CCA / RADAR */
+} __attribute__ ((packed));
+
+struct p54_tx_control_xbow_synth {
+       __le16 magic1;
+       __le16 magic2;
+       __le16 freq;
+       u32 padding[5];
+} __attribute__ ((packed));
+
 #endif /* P54COMMON_H */
index 11f590d63affe146e697b8c3c4d88243cd0469ee..b686dc45483e2a4a88bf192617d6385461a6879c 100644 (file)
@@ -40,11 +40,15 @@ config RT2X00_LIB_CRYPTO
 config RT2X00_LIB_RFKILL
        boolean
        depends on RT2X00_LIB
-       select RFKILL
+       depends on RFKILL
+       default y
 
 config RT2X00_LIB_LEDS
        boolean
-       depends on RT2X00_LIB && NEW_LEDS
+       depends on RT2X00_LIB
+       depends on NEW_LEDS
+       depends on LEDS_CLASS
+       default y
 
 config RT2400PCI
        tristate "Ralink rt2400 (PCI/PCMCIA) support"
@@ -57,23 +61,6 @@ config RT2400PCI
 
          When compiled as a module, this driver will be called "rt2400pci.ko".
 
-config RT2400PCI_RFKILL
-       bool "Ralink rt2400 rfkill support"
-       depends on RT2400PCI
-       select RT2X00_LIB_RFKILL
-       ---help---
-         This adds support for integrated rt2400 hardware that features a
-         hardware button to control the radio state.
-         This feature depends on the RF switch subsystem rfkill.
-
-config RT2400PCI_LEDS
-       bool "Ralink rt2400 leds support"
-       depends on RT2400PCI && NEW_LEDS
-       select LEDS_CLASS
-       select RT2X00_LIB_LEDS
-       ---help---
-         This adds support for led triggers provided my mac80211.
-
 config RT2500PCI
        tristate "Ralink rt2500 (PCI/PCMCIA) support"
        depends on PCI
@@ -85,23 +72,6 @@ config RT2500PCI
 
          When compiled as a module, this driver will be called "rt2500pci.ko".
 
-config RT2500PCI_RFKILL
-       bool "Ralink rt2500 rfkill support"
-       depends on RT2500PCI
-       select RT2X00_LIB_RFKILL
-       ---help---
-         This adds support for integrated rt2500 hardware that features a
-         hardware button to control the radio state.
-         This feature depends on the RF switch subsystem rfkill.
-
-config RT2500PCI_LEDS
-       bool "Ralink rt2500 leds support"
-       depends on RT2500PCI && NEW_LEDS
-       select LEDS_CLASS
-       select RT2X00_LIB_LEDS
-       ---help---
-         This adds support for led triggers provided my mac80211.
-
 config RT61PCI
        tristate "Ralink rt2501/rt61 (PCI/PCMCIA) support"
        depends on PCI
@@ -116,23 +86,6 @@ config RT61PCI
 
          When compiled as a module, this driver will be called "rt61pci.ko".
 
-config RT61PCI_RFKILL
-       bool "Ralink rt2501/rt61 rfkill support"
-       depends on RT61PCI
-       select RT2X00_LIB_RFKILL
-       ---help---
-         This adds support for integrated rt61 hardware that features a
-         hardware button to control the radio state.
-         This feature depends on the RF switch subsystem rfkill.
-
-config RT61PCI_LEDS
-       bool "Ralink rt2501/rt61 leds support"
-       depends on RT61PCI && NEW_LEDS
-       select LEDS_CLASS
-       select RT2X00_LIB_LEDS
-       ---help---
-         This adds support for led triggers provided my mac80211.
-
 config RT2500USB
        tristate "Ralink rt2500 (USB) support"
        depends on USB
@@ -143,14 +96,6 @@ config RT2500USB
 
          When compiled as a module, this driver will be called "rt2500usb.ko".
 
-config RT2500USB_LEDS
-       bool "Ralink rt2500 leds support"
-       depends on RT2500USB && NEW_LEDS
-       select LEDS_CLASS
-       select RT2X00_LIB_LEDS
-       ---help---
-         This adds support for led triggers provided my mac80211.
-
 config RT73USB
        tristate "Ralink rt2501/rt73 (USB) support"
        depends on USB
@@ -164,14 +109,6 @@ config RT73USB
 
          When compiled as a module, this driver will be called "rt73usb.ko".
 
-config RT73USB_LEDS
-       bool "Ralink rt2501/rt73 leds support"
-       depends on RT73USB && NEW_LEDS
-       select LEDS_CLASS
-       select RT2X00_LIB_LEDS
-       ---help---
-         This adds support for led triggers provided my mac80211.
-
 config RT2X00_LIB_DEBUGFS
        bool "Ralink debugfs support"
        depends on RT2X00_LIB && MAC80211_DEBUGFS
index 18b703c3fc2c4d8e6af845f39e9d017d147a8793..08cb9eec16a6bca16c3e5dc30c467db1e600a3af 100644 (file)
@@ -231,7 +231,7 @@ static const struct rt2x00debug rt2400pci_rt2x00debug = {
 };
 #endif /* CONFIG_RT2X00_LIB_DEBUGFS */
 
-#ifdef CONFIG_RT2400PCI_RFKILL
+#ifdef CONFIG_RT2X00_LIB_RFKILL
 static int rt2400pci_rfkill_poll(struct rt2x00_dev *rt2x00dev)
 {
        u32 reg;
@@ -241,9 +241,9 @@ static int rt2400pci_rfkill_poll(struct rt2x00_dev *rt2x00dev)
 }
 #else
 #define rt2400pci_rfkill_poll  NULL
-#endif /* CONFIG_RT2400PCI_RFKILL */
+#endif /* CONFIG_RT2X00_LIB_RFKILL */
 
-#ifdef CONFIG_RT2400PCI_LEDS
+#ifdef CONFIG_RT2X00_LIB_LEDS
 static void rt2400pci_brightness_set(struct led_classdev *led_cdev,
                                     enum led_brightness brightness)
 {
@@ -288,7 +288,7 @@ static void rt2400pci_init_led(struct rt2x00_dev *rt2x00dev,
        led->led_dev.blink_set = rt2400pci_blink_set;
        led->flags = LED_INITIALIZED;
 }
-#endif /* CONFIG_RT2400PCI_LEDS */
+#endif /* CONFIG_RT2X00_LIB_LEDS */
 
 /*
  * Configuration handlers.
@@ -1374,22 +1374,22 @@ static int rt2400pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
        /*
         * Store led mode, for correct led behaviour.
         */
-#ifdef CONFIG_RT2400PCI_LEDS
+#ifdef CONFIG_RT2X00_LIB_LEDS
        value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE);
 
        rt2400pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO);
        if (value == LED_MODE_TXRX_ACTIVITY)
                rt2400pci_init_led(rt2x00dev, &rt2x00dev->led_qual,
                                   LED_TYPE_ACTIVITY);
-#endif /* CONFIG_RT2400PCI_LEDS */
+#endif /* CONFIG_RT2X00_LIB_LEDS */
 
        /*
         * Detect if this device has an hardware controlled radio.
         */
-#ifdef CONFIG_RT2400PCI_RFKILL
+#ifdef CONFIG_RT2X00_LIB_RFKILL
        if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO))
                __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags);
-#endif /* CONFIG_RT2400PCI_RFKILL */
+#endif /* CONFIG_RT2X00_LIB_RFKILL */
 
        /*
         * Check if the BBP tuning should be enabled.
index 2a96a011f2ad0c7924828ea1a300bef1c5d6d34d..ef42cc04a2d736428d112bdc28caa4af58aeeca4 100644 (file)
@@ -231,7 +231,7 @@ static const struct rt2x00debug rt2500pci_rt2x00debug = {
 };
 #endif /* CONFIG_RT2X00_LIB_DEBUGFS */
 
-#ifdef CONFIG_RT2500PCI_RFKILL
+#ifdef CONFIG_RT2X00_LIB_RFKILL
 static int rt2500pci_rfkill_poll(struct rt2x00_dev *rt2x00dev)
 {
        u32 reg;
@@ -241,9 +241,9 @@ static int rt2500pci_rfkill_poll(struct rt2x00_dev *rt2x00dev)
 }
 #else
 #define rt2500pci_rfkill_poll  NULL
-#endif /* CONFIG_RT2500PCI_RFKILL */
+#endif /* CONFIG_RT2X00_LIB_RFKILL */
 
-#ifdef CONFIG_RT2500PCI_LEDS
+#ifdef CONFIG_RT2X00_LIB_LEDS
 static void rt2500pci_brightness_set(struct led_classdev *led_cdev,
                                     enum led_brightness brightness)
 {
@@ -288,7 +288,7 @@ static void rt2500pci_init_led(struct rt2x00_dev *rt2x00dev,
        led->led_dev.blink_set = rt2500pci_blink_set;
        led->flags = LED_INITIALIZED;
 }
-#endif /* CONFIG_RT2500PCI_LEDS */
+#endif /* CONFIG_RT2X00_LIB_LEDS */
 
 /*
  * Configuration handlers.
@@ -1533,22 +1533,22 @@ static int rt2500pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
        /*
         * Store led mode, for correct led behaviour.
         */
-#ifdef CONFIG_RT2500PCI_LEDS
+#ifdef CONFIG_RT2X00_LIB_LEDS
        value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE);
 
        rt2500pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO);
        if (value == LED_MODE_TXRX_ACTIVITY)
                rt2500pci_init_led(rt2x00dev, &rt2x00dev->led_qual,
                                   LED_TYPE_ACTIVITY);
-#endif /* CONFIG_RT2500PCI_LEDS */
+#endif /* CONFIG_RT2X00_LIB_LEDS */
 
        /*
         * Detect if this device has an hardware controlled radio.
         */
-#ifdef CONFIG_RT2500PCI_RFKILL
+#ifdef CONFIG_RT2X00_LIB_RFKILL
        if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO))
                __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags);
-#endif /* CONFIG_RT2500PCI_RFKILL */
+#endif /* CONFIG_RT2X00_LIB_RFKILL */
 
        /*
         * Check if the BBP tuning should be enabled.
index 0e008b606f70401101bb360d1e38c9a521217e4f..cb5f2d01a9c3cefb92defa23292b682ca1e6de69 100644 (file)
@@ -288,7 +288,7 @@ static const struct rt2x00debug rt2500usb_rt2x00debug = {
 };
 #endif /* CONFIG_RT2X00_LIB_DEBUGFS */
 
-#ifdef CONFIG_RT2500USB_LEDS
+#ifdef CONFIG_RT2X00_LIB_LEDS
 static void rt2500usb_brightness_set(struct led_classdev *led_cdev,
                                     enum led_brightness brightness)
 {
@@ -333,7 +333,7 @@ static void rt2500usb_init_led(struct rt2x00_dev *rt2x00dev,
        led->led_dev.blink_set = rt2500usb_blink_set;
        led->flags = LED_INITIALIZED;
 }
-#endif /* CONFIG_RT2500USB_LEDS */
+#endif /* CONFIG_RT2X00_LIB_LEDS */
 
 /*
  * Configuration handlers.
@@ -1133,7 +1133,6 @@ static void rt2500usb_write_beacon(struct queue_entry *entry)
        int pipe = usb_sndbulkpipe(usb_dev, 1);
        int length;
        u16 reg;
-       u32 word, len;
 
        /*
         * Add the descriptor in front of the skb.
@@ -1142,17 +1141,6 @@ static void rt2500usb_write_beacon(struct queue_entry *entry)
        memcpy(entry->skb->data, skbdesc->desc, skbdesc->desc_len);
        skbdesc->desc = entry->skb->data;
 
-       /*
-        * Adjust the beacon databyte count. The current number is
-        * calculated before this function gets called, but falsely
-        * assumes that the descriptor was already present in the SKB.
-        */
-       rt2x00_desc_read(skbdesc->desc, 0, &word);
-       len  = rt2x00_get_field32(word, TXD_W0_DATABYTE_COUNT);
-       len += skbdesc->desc_len;
-       rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, len);
-       rt2x00_desc_write(skbdesc->desc, 0, word);
-
        /*
         * Disable beaconing while we are reloading the beacon data,
         * otherwise we might be sending out invalid data.
@@ -1485,14 +1473,14 @@ static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev)
        /*
         * Store led mode, for correct led behaviour.
         */
-#ifdef CONFIG_RT2500USB_LEDS
+#ifdef CONFIG_RT2X00_LIB_LEDS
        value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE);
 
        rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO);
        if (value == LED_MODE_TXRX_ACTIVITY)
                rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_qual,
                                   LED_TYPE_ACTIVITY);
-#endif /* CONFIG_RT2500USB_LEDS */
+#endif /* CONFIG_RT2X00_LIB_LEDS */
 
        /*
         * Check if the BBP tuning should be disabled.
index 8a2fefb365b784b2673c036a1fde2d7757a7c90c..55eff58f1889d523182813326d8886e72c40ee4c 100644 (file)
@@ -45,16 +45,15 @@ static int rt2x00rfkill_toggle_radio(void *data, enum rfkill_state state)
                return 0;
 
        if (state == RFKILL_STATE_UNBLOCKED) {
-               INFO(rt2x00dev, "Hardware button pressed, enabling radio.\n");
+               INFO(rt2x00dev, "RFKILL event: enabling radio.\n");
                clear_bit(DEVICE_STATE_DISABLED_RADIO_HW, &rt2x00dev->flags);
                retval = rt2x00lib_enable_radio(rt2x00dev);
        } else if (state == RFKILL_STATE_SOFT_BLOCKED) {
-               INFO(rt2x00dev, "Hardware button pressed, disabling radio.\n");
+               INFO(rt2x00dev, "RFKILL event: disabling radio.\n");
                set_bit(DEVICE_STATE_DISABLED_RADIO_HW, &rt2x00dev->flags);
                rt2x00lib_disable_radio(rt2x00dev);
        } else {
-               WARNING(rt2x00dev, "Received unexpected rfkill state %d.\n",
-                       state);
+               WARNING(rt2x00dev, "RFKILL event: unknown state %d.\n", state);
        }
 
        return retval;
@@ -64,7 +63,12 @@ static int rt2x00rfkill_get_state(void *data, enum rfkill_state *state)
 {
        struct rt2x00_dev *rt2x00dev = data;
 
-       *state = rt2x00dev->rfkill->state;
+       /*
+        * rfkill_poll reports 1 when the key has been pressed and the
+        * radio should be blocked.
+        */
+       *state = rt2x00dev->ops->lib->rfkill_poll(rt2x00dev) ?
+           RFKILL_STATE_SOFT_BLOCKED : RFKILL_STATE_UNBLOCKED;
 
        return 0;
 }
@@ -73,19 +77,18 @@ static void rt2x00rfkill_poll(struct work_struct *work)
 {
        struct rt2x00_dev *rt2x00dev =
            container_of(work, struct rt2x00_dev, rfkill_work.work);
-       int state;
+       enum rfkill_state state;
 
-       if (!test_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state))
+       if (!test_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state) ||
+           !test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags))
                return;
 
        /*
-        * rfkill_poll reports 1 when the key has been pressed and the
-        * radio should be blocked.
+        * Poll latest state and report it to rfkill who should sort
+        * out if the state should be toggled or not.
         */
-       state = !rt2x00dev->ops->lib->rfkill_poll(rt2x00dev) ?
-           RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
-
-       rfkill_force_state(rt2x00dev->rfkill, state);
+       if (!rt2x00rfkill_get_state(rt2x00dev, &state))
+               rfkill_force_state(rt2x00dev->rfkill, state);
 
        queue_delayed_work(rt2x00dev->hw->workqueue,
                           &rt2x00dev->rfkill_work, RFKILL_POLL_INTERVAL);
@@ -93,8 +96,8 @@ static void rt2x00rfkill_poll(struct work_struct *work)
 
 void rt2x00rfkill_register(struct rt2x00_dev *rt2x00dev)
 {
-       if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags) ||
-           !test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state))
+       if (!test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state) ||
+           test_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state))
                return;
 
        if (rfkill_register(rt2x00dev->rfkill)) {
@@ -114,7 +117,7 @@ void rt2x00rfkill_register(struct rt2x00_dev *rt2x00dev)
 
 void rt2x00rfkill_unregister(struct rt2x00_dev *rt2x00dev)
 {
-       if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags) ||
+       if (!test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state) ||
            !test_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state))
                return;
 
@@ -127,21 +130,25 @@ void rt2x00rfkill_unregister(struct rt2x00_dev *rt2x00dev)
 
 void rt2x00rfkill_allocate(struct rt2x00_dev *rt2x00dev)
 {
-       if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags))
+       struct device *dev = wiphy_dev(rt2x00dev->hw->wiphy);
+
+       if (test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state))
                return;
 
-       rt2x00dev->rfkill =
-           rfkill_allocate(wiphy_dev(rt2x00dev->hw->wiphy), RFKILL_TYPE_WLAN);
+       rt2x00dev->rfkill = rfkill_allocate(dev, RFKILL_TYPE_WLAN);
        if (!rt2x00dev->rfkill) {
                ERROR(rt2x00dev, "Failed to allocate rfkill handler.\n");
                return;
        }
 
+       __set_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state);
+
        rt2x00dev->rfkill->name = rt2x00dev->ops->name;
        rt2x00dev->rfkill->data = rt2x00dev;
        rt2x00dev->rfkill->state = -1;
        rt2x00dev->rfkill->toggle_radio = rt2x00rfkill_toggle_radio;
-       rt2x00dev->rfkill->get_state = rt2x00rfkill_get_state;
+       if (test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags))
+               rt2x00dev->rfkill->get_state = rt2x00rfkill_get_state;
 
        INIT_DELAYED_WORK(&rt2x00dev->rfkill_work, rt2x00rfkill_poll);
 
@@ -150,8 +157,7 @@ void rt2x00rfkill_allocate(struct rt2x00_dev *rt2x00dev)
 
 void rt2x00rfkill_free(struct rt2x00_dev *rt2x00dev)
 {
-       if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags) ||
-           !test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state))
+       if (!test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->flags))
                return;
 
        cancel_delayed_work_sync(&rt2x00dev->rfkill_work);
index d740f560ccd0cce742ac336d60f02230629796db..2c36b91ff4c79da5525e6163c604fbdd22f6a65b 100644 (file)
@@ -163,7 +163,7 @@ rf_write:
        rt2x00_rf_write(rt2x00dev, word, value);
 }
 
-#ifdef CONFIG_RT61PCI_LEDS
+#ifdef CONFIG_RT2X00_LIB_LEDS
 /*
  * This function is only called from rt61pci_led_brightness()
  * make gcc happy by placing this function inside the
@@ -195,7 +195,7 @@ static void rt61pci_mcu_request(struct rt2x00_dev *rt2x00dev,
        rt2x00_set_field32(&reg, HOST_CMD_CSR_INTERRUPT_MCU, 1);
        rt2x00pci_register_write(rt2x00dev, HOST_CMD_CSR, reg);
 }
-#endif /* CONFIG_RT61PCI_LEDS */
+#endif /* CONFIG_RT2X00_LIB_LEDS */
 
 static void rt61pci_eepromregister_read(struct eeprom_93cx6 *eeprom)
 {
@@ -271,7 +271,7 @@ static const struct rt2x00debug rt61pci_rt2x00debug = {
 };
 #endif /* CONFIG_RT2X00_LIB_DEBUGFS */
 
-#ifdef CONFIG_RT61PCI_RFKILL
+#ifdef CONFIG_RT2X00_LIB_RFKILL
 static int rt61pci_rfkill_poll(struct rt2x00_dev *rt2x00dev)
 {
        u32 reg;
@@ -281,9 +281,9 @@ static int rt61pci_rfkill_poll(struct rt2x00_dev *rt2x00dev)
 }
 #else
 #define rt61pci_rfkill_poll    NULL
-#endif /* CONFIG_RT61PCI_RFKILL */
+#endif /* CONFIG_RT2X00_LIB_RFKILL */
 
-#ifdef CONFIG_RT61PCI_LEDS
+#ifdef CONFIG_RT2X00_LIB_LEDS
 static void rt61pci_brightness_set(struct led_classdev *led_cdev,
                                   enum led_brightness brightness)
 {
@@ -348,7 +348,7 @@ static void rt61pci_init_led(struct rt2x00_dev *rt2x00dev,
        led->led_dev.blink_set = rt61pci_blink_set;
        led->flags = LED_INITIALIZED;
 }
-#endif /* CONFIG_RT61PCI_LEDS */
+#endif /* CONFIG_RT2X00_LIB_LEDS */
 
 /*
  * Configuration handlers.
@@ -2313,10 +2313,10 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
        /*
         * Detect if this device has an hardware controlled radio.
         */
-#ifdef CONFIG_RT61PCI_RFKILL
+#ifdef CONFIG_RT2X00_LIB_RFKILL
        if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO))
                __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags);
-#endif /* CONFIG_RT61PCI_RFKILL */
+#endif /* CONFIG_RT2X00_LIB_RFKILL */
 
        /*
         * Read frequency offset and RF programming sequence.
@@ -2374,7 +2374,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
         * If the eeprom value is invalid,
         * switch to default led mode.
         */
-#ifdef CONFIG_RT61PCI_LEDS
+#ifdef CONFIG_RT2X00_LIB_LEDS
        rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &eeprom);
        value = rt2x00_get_field16(eeprom, EEPROM_LED_LED_MODE);
 
@@ -2408,7 +2408,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
        rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_A,
                           rt2x00_get_field16(eeprom,
                                              EEPROM_LED_POLARITY_RDY_A));
-#endif /* CONFIG_RT61PCI_LEDS */
+#endif /* CONFIG_RT2X00_LIB_LEDS */
 
        return 0;
 }
index e698ae0efbce0f5408685195f6a0c01be95cfe1d..27dde3e34603705a5fdc56cdf1a5fe8fbc8debd4 100644 (file)
@@ -292,7 +292,7 @@ static const struct rt2x00debug rt73usb_rt2x00debug = {
 };
 #endif /* CONFIG_RT2X00_LIB_DEBUGFS */
 
-#ifdef CONFIG_RT73USB_LEDS
+#ifdef CONFIG_RT2X00_LIB_LEDS
 static void rt73usb_brightness_set(struct led_classdev *led_cdev,
                                   enum led_brightness brightness)
 {
@@ -359,7 +359,7 @@ static void rt73usb_init_led(struct rt2x00_dev *rt2x00dev,
        led->led_dev.blink_set = rt73usb_blink_set;
        led->flags = LED_INITIALIZED;
 }
-#endif /* CONFIG_RT73USB_LEDS */
+#endif /* CONFIG_RT2X00_LIB_LEDS */
 
 /*
  * Configuration handlers.
@@ -1572,7 +1572,6 @@ static void rt73usb_write_beacon(struct queue_entry *entry)
        struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
        unsigned int beacon_base;
        u32 reg;
-       u32 word, len;
 
        /*
         * Add the descriptor in front of the skb.
@@ -1581,17 +1580,6 @@ static void rt73usb_write_beacon(struct queue_entry *entry)
        memcpy(entry->skb->data, skbdesc->desc, skbdesc->desc_len);
        skbdesc->desc = entry->skb->data;
 
-       /*
-        * Adjust the beacon databyte count. The current number is
-        * calculated before this function gets called, but falsely
-        * assumes that the descriptor was already present in the SKB.
-        */
-       rt2x00_desc_read(skbdesc->desc, 0, &word);
-       len  = rt2x00_get_field32(word, TXD_W0_DATABYTE_COUNT);
-       len += skbdesc->desc_len;
-       rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, len);
-       rt2x00_desc_write(skbdesc->desc, 0, word);
-
        /*
         * Disable beaconing while we are reloading the beacon data,
         * otherwise we might be sending out invalid data.
@@ -1944,7 +1932,7 @@ static int rt73usb_init_eeprom(struct rt2x00_dev *rt2x00dev)
        /*
         * Store led settings, for correct led behaviour.
         */
-#ifdef CONFIG_RT73USB_LEDS
+#ifdef CONFIG_RT2X00_LIB_LEDS
        rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &eeprom);
 
        rt73usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO);
@@ -1977,7 +1965,7 @@ static int rt73usb_init_eeprom(struct rt2x00_dev *rt2x00dev)
        rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_A,
                           rt2x00_get_field16(eeprom,
                                              EEPROM_LED_POLARITY_RDY_A));
-#endif /* CONFIG_RT73USB_LEDS */
+#endif /* CONFIG_RT2X00_LIB_LEDS */
 
        return 0;
 }
index be456450cd2efc9eb692a3873430dd41ad964e5f..abc1abc63bf093d8b3a377449d59b6e054031ed0 100644 (file)
@@ -643,6 +643,9 @@ struct ieee80211_mgmt {
        } u;
 } __attribute__ ((packed));
 
+/* mgmt header + 1 byte category code */
+#define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u)
+
 
 /* Control frames */
 struct ieee80211_rts {
@@ -708,7 +711,7 @@ struct ieee80211_ht_addt_info {
 
 /* 802.11n HT capabilities masks */
 #define IEEE80211_HT_CAP_SUP_WIDTH             0x0002
-#define IEEE80211_HT_CAP_MIMO_PS               0x000C
+#define IEEE80211_HT_CAP_SM_PS                 0x000C
 #define IEEE80211_HT_CAP_GRN_FLD               0x0010
 #define IEEE80211_HT_CAP_SGI_20                        0x0020
 #define IEEE80211_HT_CAP_SGI_40                        0x0040
@@ -737,11 +740,26 @@ struct ieee80211_ht_addt_info {
 #define IEEE80211_HT_IE_NON_GF_STA_PRSNT       0x0004
 #define IEEE80211_HT_IE_NON_HT_STA_PRSNT       0x0010
 
-/* MIMO Power Save Modes */
-#define WLAN_HT_CAP_MIMO_PS_STATIC     0
-#define WLAN_HT_CAP_MIMO_PS_DYNAMIC    1
-#define WLAN_HT_CAP_MIMO_PS_INVALID    2
-#define WLAN_HT_CAP_MIMO_PS_DISABLED   3
+/* block-ack parameters */
+#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002
+#define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C
+#define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFA0
+#define IEEE80211_DELBA_PARAM_TID_MASK 0xF000
+#define IEEE80211_DELBA_PARAM_INITIATOR_MASK 0x0800
+
+/*
+ * A-PMDU buffer sizes
+ * According to IEEE802.11n spec size varies from 8K to 64K (in powers of 2)
+ */
+#define IEEE80211_MIN_AMPDU_BUF 0x8
+#define IEEE80211_MAX_AMPDU_BUF 0x40
+
+
+/* Spatial Multiplexing Power Save Modes */
+#define WLAN_HT_CAP_SM_PS_STATIC       0
+#define WLAN_HT_CAP_SM_PS_DYNAMIC      1
+#define WLAN_HT_CAP_SM_PS_INVALID      2
+#define WLAN_HT_CAP_SM_PS_DISABLED     3
 
 /* Authentication algorithms */
 #define WLAN_AUTH_OPEN 0
index 7c399a9c11da630478133ee949f45b3d78af3c21..fb9e62211c34f9e942765ac7e6af2ae69d4125b2 100644 (file)
@@ -1142,7 +1142,7 @@ enum ieee80211_ampdu_mlme_action {
  *     of assocaited station or AP.
  *
  * @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
- *     bursting) for a hardware TX queue. Must be atomic.
+ *     bursting) for a hardware TX queue.
  *
  * @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
index a169b0201d617cab4bfd1363eeb6b94c21b3c8dc..2dc8f2bff27ba13851e0bea47efa7601d7c163a7 100644 (file)
@@ -7,6 +7,8 @@ mac80211-y := \
        sta_info.o \
        wep.o \
        wpa.o \
+       scan.o \
+       ht.o \
        mlme.o \
        iface.o \
        rate.o \
@@ -15,6 +17,7 @@ mac80211-y := \
        aes_ccm.o \
        cfg.o \
        rx.o \
+       spectmgmt.o \
        tx.o \
        key.o \
        util.o \
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
new file mode 100644 (file)
index 0000000..4dc35c9
--- /dev/null
@@ -0,0 +1,992 @@
+/*
+ * HT handling
+ *
+ * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright 2002-2005, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
+ * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2007-2008, Intel Corporation
+ *
+ * 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.
+ */
+
+#include <linux/ieee80211.h>
+#include <net/wireless.h>
+#include <net/mac80211.h>
+#include "ieee80211_i.h"
+#include "sta_info.h"
+#include "wme.h"
+
+int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
+                                  struct ieee80211_ht_info *ht_info)
+{
+
+       if (ht_info == NULL)
+               return -EINVAL;
+
+       memset(ht_info, 0, sizeof(*ht_info));
+
+       if (ht_cap_ie) {
+               u8 ampdu_info = ht_cap_ie->ampdu_params_info;
+
+               ht_info->ht_supported = 1;
+               ht_info->cap = le16_to_cpu(ht_cap_ie->cap_info);
+               ht_info->ampdu_factor =
+                       ampdu_info & IEEE80211_HT_CAP_AMPDU_FACTOR;
+               ht_info->ampdu_density =
+                       (ampdu_info & IEEE80211_HT_CAP_AMPDU_DENSITY) >> 2;
+               memcpy(ht_info->supp_mcs_set, ht_cap_ie->supp_mcs_set, 16);
+       } else
+               ht_info->ht_supported = 0;
+
+       return 0;
+}
+
+int ieee80211_ht_addt_info_ie_to_ht_bss_info(
+                       struct ieee80211_ht_addt_info *ht_add_info_ie,
+                       struct ieee80211_ht_bss_info *bss_info)
+{
+       if (bss_info == NULL)
+               return -EINVAL;
+
+       memset(bss_info, 0, sizeof(*bss_info));
+
+       if (ht_add_info_ie) {
+               u16 op_mode;
+               op_mode = le16_to_cpu(ht_add_info_ie->operation_mode);
+
+               bss_info->primary_channel = ht_add_info_ie->control_chan;
+               bss_info->bss_cap = ht_add_info_ie->ht_param;
+               bss_info->bss_op_mode = (u8)(op_mode & 0xff);
+       }
+
+       return 0;
+}
+
+static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
+                                        const u8 *da, u16 tid,
+                                        u8 dialog_token, u16 start_seq_num,
+                                        u16 agg_size, u16 timeout)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *mgmt;
+       u16 capab;
+
+       skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
+
+       if (!skb) {
+               printk(KERN_ERR "%s: failed to allocate buffer "
+                               "for addba request frame\n", sdata->dev->name);
+               return;
+       }
+       skb_reserve(skb, local->hw.extra_tx_headroom);
+       mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+       memset(mgmt, 0, 24);
+       memcpy(mgmt->da, da, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+       if (sdata->vif.type == IEEE80211_IF_TYPE_AP)
+               memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
+       else
+               memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+
+       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                         IEEE80211_STYPE_ACTION);
+
+       skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_req));
+
+       mgmt->u.action.category = WLAN_CATEGORY_BACK;
+       mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ;
+
+       mgmt->u.action.u.addba_req.dialog_token = dialog_token;
+       capab = (u16)(1 << 1);          /* bit 1 aggregation policy */
+       capab |= (u16)(tid << 2);       /* bit 5:2 TID number */
+       capab |= (u16)(agg_size << 6);  /* bit 15:6 max size of aggergation */
+
+       mgmt->u.action.u.addba_req.capab = cpu_to_le16(capab);
+
+       mgmt->u.action.u.addba_req.timeout = cpu_to_le16(timeout);
+       mgmt->u.action.u.addba_req.start_seq_num =
+                                       cpu_to_le16(start_seq_num << 4);
+
+       ieee80211_tx_skb(sdata, skb, 0);
+}
+
+static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *da, u16 tid,
+                                     u8 dialog_token, u16 status, u16 policy,
+                                     u16 buf_size, u16 timeout)
+{
+       struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+       struct ieee80211_local *local = sdata->local;
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *mgmt;
+       u16 capab;
+
+       skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
+
+       if (!skb) {
+               printk(KERN_DEBUG "%s: failed to allocate buffer "
+                      "for addba resp frame\n", sdata->dev->name);
+               return;
+       }
+
+       skb_reserve(skb, local->hw.extra_tx_headroom);
+       mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+       memset(mgmt, 0, 24);
+       memcpy(mgmt->da, da, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+       if (sdata->vif.type == IEEE80211_IF_TYPE_AP)
+               memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
+       else
+               memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                         IEEE80211_STYPE_ACTION);
+
+       skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_resp));
+       mgmt->u.action.category = WLAN_CATEGORY_BACK;
+       mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP;
+       mgmt->u.action.u.addba_resp.dialog_token = dialog_token;
+
+       capab = (u16)(policy << 1);     /* bit 1 aggregation policy */
+       capab |= (u16)(tid << 2);       /* bit 5:2 TID number */
+       capab |= (u16)(buf_size << 6);  /* bit 15:6 max size of aggregation */
+
+       mgmt->u.action.u.addba_resp.capab = cpu_to_le16(capab);
+       mgmt->u.action.u.addba_resp.timeout = cpu_to_le16(timeout);
+       mgmt->u.action.u.addba_resp.status = cpu_to_le16(status);
+
+       ieee80211_tx_skb(sdata, skb, 0);
+}
+
+static void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
+                                const u8 *da, u16 tid,
+                                u16 initiator, u16 reason_code)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *mgmt;
+       u16 params;
+
+       skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
+
+       if (!skb) {
+               printk(KERN_ERR "%s: failed to allocate buffer "
+                                       "for delba frame\n", sdata->dev->name);
+               return;
+       }
+
+       skb_reserve(skb, local->hw.extra_tx_headroom);
+       mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+       memset(mgmt, 0, 24);
+       memcpy(mgmt->da, da, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+       if (sdata->vif.type == IEEE80211_IF_TYPE_AP)
+               memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
+       else
+               memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                         IEEE80211_STYPE_ACTION);
+
+       skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba));
+
+       mgmt->u.action.category = WLAN_CATEGORY_BACK;
+       mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA;
+       params = (u16)(initiator << 11);        /* bit 11 initiator */
+       params |= (u16)(tid << 12);             /* bit 15:12 TID number */
+
+       mgmt->u.action.u.delba.params = cpu_to_le16(params);
+       mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code);
+
+       ieee80211_tx_skb(sdata, skb, 0);
+}
+
+void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct sk_buff *skb;
+       struct ieee80211_bar *bar;
+       u16 bar_control = 0;
+
+       skb = dev_alloc_skb(sizeof(*bar) + local->hw.extra_tx_headroom);
+       if (!skb) {
+               printk(KERN_ERR "%s: failed to allocate buffer for "
+                       "bar frame\n", sdata->dev->name);
+               return;
+       }
+       skb_reserve(skb, local->hw.extra_tx_headroom);
+       bar = (struct ieee80211_bar *)skb_put(skb, sizeof(*bar));
+       memset(bar, 0, sizeof(*bar));
+       bar->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
+                                        IEEE80211_STYPE_BACK_REQ);
+       memcpy(bar->ra, ra, ETH_ALEN);
+       memcpy(bar->ta, sdata->dev->dev_addr, ETH_ALEN);
+       bar_control |= (u16)IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL;
+       bar_control |= (u16)IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA;
+       bar_control |= (u16)(tid << 12);
+       bar->control = cpu_to_le16(bar_control);
+       bar->start_seq_num = cpu_to_le16(ssn);
+
+       ieee80211_tx_skb(sdata, skb, 0);
+}
+
+void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid,
+                                       u16 initiator, u16 reason)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_hw *hw = &local->hw;
+       struct sta_info *sta;
+       int ret, i;
+       DECLARE_MAC_BUF(mac);
+
+       rcu_read_lock();
+
+       sta = sta_info_get(local, ra);
+       if (!sta) {
+               rcu_read_unlock();
+               return;
+       }
+
+       /* check if TID is in operational state */
+       spin_lock_bh(&sta->lock);
+       if (sta->ampdu_mlme.tid_state_rx[tid]
+                               != HT_AGG_STATE_OPERATIONAL) {
+               spin_unlock_bh(&sta->lock);
+               rcu_read_unlock();
+               return;
+       }
+       sta->ampdu_mlme.tid_state_rx[tid] =
+               HT_AGG_STATE_REQ_STOP_BA_MSK |
+               (initiator << HT_AGG_STATE_INITIATOR_SHIFT);
+       spin_unlock_bh(&sta->lock);
+
+       /* stop HW Rx aggregation. ampdu_action existence
+        * already verified in session init so we add the BUG_ON */
+       BUG_ON(!local->ops->ampdu_action);
+
+#ifdef CONFIG_MAC80211_HT_DEBUG
+       printk(KERN_DEBUG "Rx BA session stop requested for %s tid %u\n",
+                               print_mac(mac, ra), tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+
+       ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_STOP,
+                                       ra, tid, NULL);
+       if (ret)
+               printk(KERN_DEBUG "HW problem - can not stop rx "
+                               "aggregation for tid %d\n", tid);
+
+       /* shutdown timer has not expired */
+       if (initiator != WLAN_BACK_TIMER)
+               del_timer_sync(&sta->ampdu_mlme.tid_rx[tid]->session_timer);
+
+       /* check if this is a self generated aggregation halt */
+       if (initiator == WLAN_BACK_RECIPIENT || initiator == WLAN_BACK_TIMER)
+               ieee80211_send_delba(sdata, ra, tid, 0, reason);
+
+       /* free the reordering buffer */
+       for (i = 0; i < sta->ampdu_mlme.tid_rx[tid]->buf_size; i++) {
+               if (sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]) {
+                       /* release the reordered frames */
+                       dev_kfree_skb(sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]);
+                       sta->ampdu_mlme.tid_rx[tid]->stored_mpdu_num--;
+                       sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i] = NULL;
+               }
+       }
+       /* free resources */
+       kfree(sta->ampdu_mlme.tid_rx[tid]->reorder_buf);
+       kfree(sta->ampdu_mlme.tid_rx[tid]);
+       sta->ampdu_mlme.tid_rx[tid] = NULL;
+       sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_IDLE;
+
+       rcu_read_unlock();
+}
+
+
+/*
+ * After sending add Block Ack request we activated a timer until
+ * add Block Ack response will arrive from the recipient.
+ * If this timer expires sta_addba_resp_timer_expired will be executed.
+ */
+static void sta_addba_resp_timer_expired(unsigned long data)
+{
+       /* not an elegant detour, but there is no choice as the timer passes
+        * only one argument, and both sta_info and TID are needed, so init
+        * flow in sta_info_create gives the TID as data, while the timer_to_id
+        * array gives the sta through container_of */
+       u16 tid = *(u8 *)data;
+       struct sta_info *temp_sta = container_of((void *)data,
+               struct sta_info, timer_to_tid[tid]);
+
+       struct ieee80211_local *local = temp_sta->local;
+       struct ieee80211_hw *hw = &local->hw;
+       struct sta_info *sta;
+       u8 *state;
+
+       rcu_read_lock();
+
+       sta = sta_info_get(local, temp_sta->addr);
+       if (!sta) {
+               rcu_read_unlock();
+               return;
+       }
+
+       state = &sta->ampdu_mlme.tid_state_tx[tid];
+       /* check if the TID waits for addBA response */
+       spin_lock_bh(&sta->lock);
+       if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
+               spin_unlock_bh(&sta->lock);
+               *state = HT_AGG_STATE_IDLE;
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               printk(KERN_DEBUG "timer expired on tid %d but we are not "
+                               "expecting addBA response there", tid);
+#endif
+               goto timer_expired_exit;
+       }
+
+#ifdef CONFIG_MAC80211_HT_DEBUG
+       printk(KERN_DEBUG "addBA response timer expired on tid %d\n", tid);
+#endif
+
+       /* go through the state check in stop_BA_session */
+       *state = HT_AGG_STATE_OPERATIONAL;
+       spin_unlock_bh(&sta->lock);
+       ieee80211_stop_tx_ba_session(hw, temp_sta->addr, tid,
+                                    WLAN_BACK_INITIATOR);
+
+timer_expired_exit:
+       rcu_read_unlock();
+}
+
+void ieee80211_sta_tear_down_BA_sessions(struct ieee80211_sub_if_data *sdata, u8 *addr)
+{
+       struct ieee80211_local *local = sdata->local;
+       int i;
+
+       for (i = 0; i <  STA_TID_NUM; i++) {
+               ieee80211_stop_tx_ba_session(&local->hw, addr, i,
+                                            WLAN_BACK_INITIATOR);
+               ieee80211_sta_stop_rx_ba_session(sdata, addr, i,
+                                                WLAN_BACK_RECIPIENT,
+                                                WLAN_REASON_QSTA_LEAVE_QBSS);
+       }
+}
+
+int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct sta_info *sta;
+       struct ieee80211_sub_if_data *sdata;
+       u16 start_seq_num;
+       u8 *state;
+       int ret;
+       DECLARE_MAC_BUF(mac);
+
+       if (tid >= STA_TID_NUM)
+               return -EINVAL;
+
+#ifdef CONFIG_MAC80211_HT_DEBUG
+       printk(KERN_DEBUG "Open BA session requested for %s tid %u\n",
+                               print_mac(mac, ra), tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+
+       rcu_read_lock();
+
+       sta = sta_info_get(local, ra);
+       if (!sta) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               printk(KERN_DEBUG "Could not find the station\n");
+#endif
+               ret = -ENOENT;
+               goto exit;
+       }
+
+       spin_lock_bh(&sta->lock);
+
+       /* we have tried too many times, receiver does not want A-MPDU */
+       if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) {
+               ret = -EBUSY;
+               goto err_unlock_sta;
+       }
+
+       state = &sta->ampdu_mlme.tid_state_tx[tid];
+       /* check if the TID is not in aggregation flow already */
+       if (*state != HT_AGG_STATE_IDLE) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               printk(KERN_DEBUG "BA request denied - session is not "
+                                "idle on tid %u\n", tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+               ret = -EAGAIN;
+               goto err_unlock_sta;
+       }
+
+       /* prepare A-MPDU MLME for Tx aggregation */
+       sta->ampdu_mlme.tid_tx[tid] =
+                       kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC);
+       if (!sta->ampdu_mlme.tid_tx[tid]) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               if (net_ratelimit())
+                       printk(KERN_ERR "allocate tx mlme to tid %d failed\n",
+                                       tid);
+#endif
+               ret = -ENOMEM;
+               goto err_unlock_sta;
+       }
+       /* Tx timer */
+       sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function =
+                       sta_addba_resp_timer_expired;
+       sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.data =
+                       (unsigned long)&sta->timer_to_tid[tid];
+       init_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
+
+       /* create a new queue for this aggregation */
+       ret = ieee80211_ht_agg_queue_add(local, sta, tid);
+
+       /* case no queue is available to aggregation
+        * don't switch to aggregation */
+       if (ret) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               printk(KERN_DEBUG "BA request denied - queue unavailable for"
+                                       " tid %d\n", tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+               goto err_unlock_queue;
+       }
+       sdata = sta->sdata;
+
+       /* Ok, the Addba frame hasn't been sent yet, but if the driver calls the
+        * call back right away, it must see that the flow has begun */
+       *state |= HT_ADDBA_REQUESTED_MSK;
+
+       /* This is slightly racy because the queue isn't stopped */
+       start_seq_num = sta->tid_seq[tid];
+
+       if (local->ops->ampdu_action)
+               ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_START,
+                                               ra, tid, &start_seq_num);
+
+       if (ret) {
+               /* No need to requeue the packets in the agg queue, since we
+                * held the tx lock: no packet could be enqueued to the newly
+                * allocated queue */
+               ieee80211_ht_agg_queue_remove(local, sta, tid, 0);
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               printk(KERN_DEBUG "BA request denied - HW unavailable for"
+                                       " tid %d\n", tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+               *state = HT_AGG_STATE_IDLE;
+               goto err_unlock_queue;
+       }
+
+       /* Will put all the packets in the new SW queue */
+       ieee80211_requeue(local, ieee802_1d_to_ac[tid]);
+       spin_unlock_bh(&sta->lock);
+
+       /* send an addBA request */
+       sta->ampdu_mlme.dialog_token_allocator++;
+       sta->ampdu_mlme.tid_tx[tid]->dialog_token =
+                       sta->ampdu_mlme.dialog_token_allocator;
+       sta->ampdu_mlme.tid_tx[tid]->ssn = start_seq_num;
+
+
+       ieee80211_send_addba_request(sta->sdata, ra, tid,
+                        sta->ampdu_mlme.tid_tx[tid]->dialog_token,
+                        sta->ampdu_mlme.tid_tx[tid]->ssn,
+                        0x40, 5000);
+       /* activate the timer for the recipient's addBA response */
+       sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.expires =
+                               jiffies + ADDBA_RESP_INTERVAL;
+       add_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
+#ifdef CONFIG_MAC80211_HT_DEBUG
+       printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid);
+#endif
+       goto exit;
+
+err_unlock_queue:
+       kfree(sta->ampdu_mlme.tid_tx[tid]);
+       sta->ampdu_mlme.tid_tx[tid] = NULL;
+       ret = -EBUSY;
+err_unlock_sta:
+       spin_unlock_bh(&sta->lock);
+exit:
+       rcu_read_unlock();
+       return ret;
+}
+EXPORT_SYMBOL(ieee80211_start_tx_ba_session);
+
+int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
+                                u8 *ra, u16 tid,
+                                enum ieee80211_back_parties initiator)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct sta_info *sta;
+       u8 *state;
+       int ret = 0;
+       DECLARE_MAC_BUF(mac);
+
+       if (tid >= STA_TID_NUM)
+               return -EINVAL;
+
+       rcu_read_lock();
+       sta = sta_info_get(local, ra);
+       if (!sta) {
+               rcu_read_unlock();
+               return -ENOENT;
+       }
+
+       /* check if the TID is in aggregation */
+       state = &sta->ampdu_mlme.tid_state_tx[tid];
+       spin_lock_bh(&sta->lock);
+
+       if (*state != HT_AGG_STATE_OPERATIONAL) {
+               ret = -ENOENT;
+               goto stop_BA_exit;
+       }
+
+#ifdef CONFIG_MAC80211_HT_DEBUG
+       printk(KERN_DEBUG "Tx BA session stop requested for %s tid %u\n",
+                               print_mac(mac, ra), tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+
+       ieee80211_stop_queue(hw, sta->tid_to_tx_q[tid]);
+
+       *state = HT_AGG_STATE_REQ_STOP_BA_MSK |
+               (initiator << HT_AGG_STATE_INITIATOR_SHIFT);
+
+       if (local->ops->ampdu_action)
+               ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_STOP,
+                                               ra, tid, NULL);
+
+       /* case HW denied going back to legacy */
+       if (ret) {
+               WARN_ON(ret != -EBUSY);
+               *state = HT_AGG_STATE_OPERATIONAL;
+               ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+               goto stop_BA_exit;
+       }
+
+stop_BA_exit:
+       spin_unlock_bh(&sta->lock);
+       rcu_read_unlock();
+       return ret;
+}
+EXPORT_SYMBOL(ieee80211_stop_tx_ba_session);
+
+void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct sta_info *sta;
+       u8 *state;
+       DECLARE_MAC_BUF(mac);
+
+       if (tid >= STA_TID_NUM) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n",
+                               tid, STA_TID_NUM);
+#endif
+               return;
+       }
+
+       rcu_read_lock();
+       sta = sta_info_get(local, ra);
+       if (!sta) {
+               rcu_read_unlock();
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               printk(KERN_DEBUG "Could not find station: %s\n",
+                               print_mac(mac, ra));
+#endif
+               return;
+       }
+
+       state = &sta->ampdu_mlme.tid_state_tx[tid];
+       spin_lock_bh(&sta->lock);
+
+       if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               printk(KERN_DEBUG "addBA was not requested yet, state is %d\n",
+                               *state);
+#endif
+               spin_unlock_bh(&sta->lock);
+               rcu_read_unlock();
+               return;
+       }
+
+       WARN_ON_ONCE(*state & HT_ADDBA_DRV_READY_MSK);
+
+       *state |= HT_ADDBA_DRV_READY_MSK;
+
+       if (*state == HT_AGG_STATE_OPERATIONAL) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
+#endif
+               ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+       }
+       spin_unlock_bh(&sta->lock);
+       rcu_read_unlock();
+}
+EXPORT_SYMBOL(ieee80211_start_tx_ba_cb);
+
+void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct sta_info *sta;
+       u8 *state;
+       int agg_queue;
+       DECLARE_MAC_BUF(mac);
+
+       if (tid >= STA_TID_NUM) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n",
+                               tid, STA_TID_NUM);
+#endif
+               return;
+       }
+
+#ifdef CONFIG_MAC80211_HT_DEBUG
+       printk(KERN_DEBUG "Stopping Tx BA session for %s tid %d\n",
+                               print_mac(mac, ra), tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+
+       rcu_read_lock();
+       sta = sta_info_get(local, ra);
+       if (!sta) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               printk(KERN_DEBUG "Could not find station: %s\n",
+                               print_mac(mac, ra));
+#endif
+               rcu_read_unlock();
+               return;
+       }
+       state = &sta->ampdu_mlme.tid_state_tx[tid];
+
+       /* NOTE: no need to use sta->lock in this state check, as
+        * ieee80211_stop_tx_ba_session will let only one stop call to
+        * pass through per sta/tid
+        */
+       if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n");
+#endif
+               rcu_read_unlock();
+               return;
+       }
+
+       if (*state & HT_AGG_STATE_INITIATOR_MSK)
+               ieee80211_send_delba(sta->sdata, ra, tid,
+                       WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
+
+       agg_queue = sta->tid_to_tx_q[tid];
+
+       ieee80211_ht_agg_queue_remove(local, sta, tid, 1);
+
+       /* We just requeued the all the frames that were in the
+        * removed queue, and since we might miss a softirq we do
+        * netif_schedule_queue.  ieee80211_wake_queue is not used
+        * here as this queue is not necessarily stopped
+        */
+       netif_schedule_queue(netdev_get_tx_queue(local->mdev, agg_queue));
+       spin_lock_bh(&sta->lock);
+       *state = HT_AGG_STATE_IDLE;
+       sta->ampdu_mlme.addba_req_num[tid] = 0;
+       kfree(sta->ampdu_mlme.tid_tx[tid]);
+       sta->ampdu_mlme.tid_tx[tid] = NULL;
+       spin_unlock_bh(&sta->lock);
+
+       rcu_read_unlock();
+}
+EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb);
+
+void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
+                                     const u8 *ra, u16 tid)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct ieee80211_ra_tid *ra_tid;
+       struct sk_buff *skb = dev_alloc_skb(0);
+
+       if (unlikely(!skb)) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               if (net_ratelimit())
+                       printk(KERN_WARNING "%s: Not enough memory, "
+                              "dropping start BA session", skb->dev->name);
+#endif
+               return;
+       }
+       ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
+       memcpy(&ra_tid->ra, ra, ETH_ALEN);
+       ra_tid->tid = tid;
+
+       skb->pkt_type = IEEE80211_ADDBA_MSG;
+       skb_queue_tail(&local->skb_queue, skb);
+       tasklet_schedule(&local->tasklet);
+}
+EXPORT_SYMBOL(ieee80211_start_tx_ba_cb_irqsafe);
+
+void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
+                                    const u8 *ra, u16 tid)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct ieee80211_ra_tid *ra_tid;
+       struct sk_buff *skb = dev_alloc_skb(0);
+
+       if (unlikely(!skb)) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               if (net_ratelimit())
+                       printk(KERN_WARNING "%s: Not enough memory, "
+                              "dropping stop BA session", skb->dev->name);
+#endif
+               return;
+       }
+       ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
+       memcpy(&ra_tid->ra, ra, ETH_ALEN);
+       ra_tid->tid = tid;
+
+       skb->pkt_type = IEEE80211_DELBA_MSG;
+       skb_queue_tail(&local->skb_queue, skb);
+       tasklet_schedule(&local->tasklet);
+}
+EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb_irqsafe);
+
+/*
+ * After accepting the AddBA Request we activated a timer,
+ * resetting it after each frame that arrives from the originator.
+ * if this timer expires ieee80211_sta_stop_rx_ba_session will be executed.
+ */
+static void sta_rx_agg_session_timer_expired(unsigned long data)
+{
+       /* not an elegant detour, but there is no choice as the timer passes
+        * only one argument, and various sta_info are needed here, so init
+        * flow in sta_info_create gives the TID as data, while the timer_to_id
+        * array gives the sta through container_of */
+       u8 *ptid = (u8 *)data;
+       u8 *timer_to_id = ptid - *ptid;
+       struct sta_info *sta = container_of(timer_to_id, struct sta_info,
+                                        timer_to_tid[0]);
+
+#ifdef CONFIG_MAC80211_HT_DEBUG
+       printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid);
+#endif
+       ieee80211_sta_stop_rx_ba_session(sta->sdata, sta->addr,
+                                        (u16)*ptid, WLAN_BACK_TIMER,
+                                        WLAN_REASON_QSTA_TIMEOUT);
+}
+
+void ieee80211_process_addba_request(struct ieee80211_local *local,
+                                    struct sta_info *sta,
+                                    struct ieee80211_mgmt *mgmt,
+                                    size_t len)
+{
+       struct ieee80211_hw *hw = &local->hw;
+       struct ieee80211_conf *conf = &hw->conf;
+       struct tid_ampdu_rx *tid_agg_rx;
+       u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status;
+       u8 dialog_token;
+       int ret = -EOPNOTSUPP;
+       DECLARE_MAC_BUF(mac);
+
+       /* extract session parameters from addba request frame */
+       dialog_token = mgmt->u.action.u.addba_req.dialog_token;
+       timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
+       start_seq_num =
+               le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
+
+       capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
+       ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
+       tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
+       buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
+
+       status = WLAN_STATUS_REQUEST_DECLINED;
+
+       /* sanity check for incoming parameters:
+        * check if configuration can support the BA policy
+        * and if buffer size does not exceeds max value */
+       if (((ba_policy != 1)
+               && (!(conf->ht_conf.cap & IEEE80211_HT_CAP_DELAY_BA)))
+               || (buf_size > IEEE80211_MAX_AMPDU_BUF)) {
+               status = WLAN_STATUS_INVALID_QOS_PARAM;
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               if (net_ratelimit())
+                       printk(KERN_DEBUG "AddBA Req with bad params from "
+                               "%s on tid %u. policy %d, buffer size %d\n",
+                               print_mac(mac, mgmt->sa), tid, ba_policy,
+                               buf_size);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+               goto end_no_lock;
+       }
+       /* determine default buffer size */
+       if (buf_size == 0) {
+               struct ieee80211_supported_band *sband;
+
+               sband = local->hw.wiphy->bands[conf->channel->band];
+               buf_size = IEEE80211_MIN_AMPDU_BUF;
+               buf_size = buf_size << sband->ht_info.ampdu_factor;
+       }
+
+
+       /* examine state machine */
+       spin_lock_bh(&sta->lock);
+
+       if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_IDLE) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               if (net_ratelimit())
+                       printk(KERN_DEBUG "unexpected AddBA Req from "
+                               "%s on tid %u\n",
+                               print_mac(mac, mgmt->sa), tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+               goto end;
+       }
+
+       /* prepare A-MPDU MLME for Rx aggregation */
+       sta->ampdu_mlme.tid_rx[tid] =
+                       kmalloc(sizeof(struct tid_ampdu_rx), GFP_ATOMIC);
+       if (!sta->ampdu_mlme.tid_rx[tid]) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               if (net_ratelimit())
+                       printk(KERN_ERR "allocate rx mlme to tid %d failed\n",
+                                       tid);
+#endif
+               goto end;
+       }
+       /* rx timer */
+       sta->ampdu_mlme.tid_rx[tid]->session_timer.function =
+                               sta_rx_agg_session_timer_expired;
+       sta->ampdu_mlme.tid_rx[tid]->session_timer.data =
+                               (unsigned long)&sta->timer_to_tid[tid];
+       init_timer(&sta->ampdu_mlme.tid_rx[tid]->session_timer);
+
+       tid_agg_rx = sta->ampdu_mlme.tid_rx[tid];
+
+       /* prepare reordering buffer */
+       tid_agg_rx->reorder_buf =
+               kmalloc(buf_size * sizeof(struct sk_buff *), GFP_ATOMIC);
+       if (!tid_agg_rx->reorder_buf) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               if (net_ratelimit())
+                       printk(KERN_ERR "can not allocate reordering buffer "
+                              "to tid %d\n", tid);
+#endif
+               kfree(sta->ampdu_mlme.tid_rx[tid]);
+               goto end;
+       }
+       memset(tid_agg_rx->reorder_buf, 0,
+               buf_size * sizeof(struct sk_buff *));
+
+       if (local->ops->ampdu_action)
+               ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_START,
+                                              sta->addr, tid, &start_seq_num);
+#ifdef CONFIG_MAC80211_HT_DEBUG
+       printk(KERN_DEBUG "Rx A-MPDU request on tid %d result %d\n", tid, ret);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+
+       if (ret) {
+               kfree(tid_agg_rx->reorder_buf);
+               kfree(tid_agg_rx);
+               sta->ampdu_mlme.tid_rx[tid] = NULL;
+               goto end;
+       }
+
+       /* change state and send addba resp */
+       sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_OPERATIONAL;
+       tid_agg_rx->dialog_token = dialog_token;
+       tid_agg_rx->ssn = start_seq_num;
+       tid_agg_rx->head_seq_num = start_seq_num;
+       tid_agg_rx->buf_size = buf_size;
+       tid_agg_rx->timeout = timeout;
+       tid_agg_rx->stored_mpdu_num = 0;
+       status = WLAN_STATUS_SUCCESS;
+end:
+       spin_unlock_bh(&sta->lock);
+
+end_no_lock:
+       ieee80211_send_addba_resp(sta->sdata, sta->addr, tid,
+                                 dialog_token, status, 1, buf_size, timeout);
+}
+
+void ieee80211_process_addba_resp(struct ieee80211_local *local,
+                                 struct sta_info *sta,
+                                 struct ieee80211_mgmt *mgmt,
+                                 size_t len)
+{
+       struct ieee80211_hw *hw = &local->hw;
+       u16 capab;
+       u16 tid;
+       u8 *state;
+
+       capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
+       tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
+
+       state = &sta->ampdu_mlme.tid_state_tx[tid];
+
+       spin_lock_bh(&sta->lock);
+
+       if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
+               spin_unlock_bh(&sta->lock);
+               return;
+       }
+
+       if (mgmt->u.action.u.addba_resp.dialog_token !=
+               sta->ampdu_mlme.tid_tx[tid]->dialog_token) {
+               spin_unlock_bh(&sta->lock);
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               printk(KERN_DEBUG "wrong addBA response token, tid %d\n", tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+               return;
+       }
+
+       del_timer_sync(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
+#ifdef CONFIG_MAC80211_HT_DEBUG
+       printk(KERN_DEBUG "switched off addBA timer for tid %d \n", tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+       if (le16_to_cpu(mgmt->u.action.u.addba_resp.status)
+                       == WLAN_STATUS_SUCCESS) {
+               *state |= HT_ADDBA_RECEIVED_MSK;
+               sta->ampdu_mlme.addba_req_num[tid] = 0;
+
+               if (*state == HT_AGG_STATE_OPERATIONAL)
+                       ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+
+               spin_unlock_bh(&sta->lock);
+       } else {
+               sta->ampdu_mlme.addba_req_num[tid]++;
+               /* this will allow the state check in stop_BA_session */
+               *state = HT_AGG_STATE_OPERATIONAL;
+               spin_unlock_bh(&sta->lock);
+               ieee80211_stop_tx_ba_session(hw, sta->addr, tid,
+                                            WLAN_BACK_INITIATOR);
+       }
+}
+
+void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
+                            struct sta_info *sta,
+                            struct ieee80211_mgmt *mgmt, size_t len)
+{
+       struct ieee80211_local *local = sdata->local;
+       u16 tid, params;
+       u16 initiator;
+       DECLARE_MAC_BUF(mac);
+
+       params = le16_to_cpu(mgmt->u.action.u.delba.params);
+       tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12;
+       initiator = (params & IEEE80211_DELBA_PARAM_INITIATOR_MASK) >> 11;
+
+#ifdef CONFIG_MAC80211_HT_DEBUG
+       if (net_ratelimit())
+               printk(KERN_DEBUG "delba from %s (%s) tid %d reason code %d\n",
+                       print_mac(mac, mgmt->sa),
+                       initiator ? "initiator" : "recipient", tid,
+                       mgmt->u.action.u.delba.reason_code);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+
+       if (initiator == WLAN_BACK_INITIATOR)
+               ieee80211_sta_stop_rx_ba_session(sdata, sta->addr, tid,
+                                                WLAN_BACK_INITIATOR, 0);
+       else { /* WLAN_BACK_RECIPIENT */
+               spin_lock_bh(&sta->lock);
+               sta->ampdu_mlme.tid_state_tx[tid] =
+                               HT_AGG_STATE_OPERATIONAL;
+               spin_unlock_bh(&sta->lock);
+               ieee80211_stop_tx_ba_session(&local->hw, sta->addr, tid,
+                                            WLAN_BACK_RECIPIENT);
+       }
+}
index c68d4df11196528f68430f0213608284fa9f27e9..6f334e4c3d66c6191ca52ad2331dcbeaae52d492 100644 (file)
@@ -53,6 +53,12 @@ struct ieee80211_local;
  * increased memory use (about 2 kB of RAM per entry). */
 #define IEEE80211_FRAGMENT_MAX 4
 
+/*
+ * Time after which we ignore scan results and no longer report/use
+ * them in any way.
+ */
+#define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ)
+
 struct ieee80211_fragment_entry {
        unsigned long first_frag_time;
        unsigned int seq;
@@ -636,7 +642,7 @@ struct ieee80211_local {
        enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
        unsigned long last_scan_completed;
        struct delayed_work scan_work;
-       struct net_device *scan_dev;
+       struct ieee80211_sub_if_data *scan_sdata;
        struct ieee80211_channel *oper_channel, *scan_channel;
        u8 scan_ssid[IEEE80211_MAX_SSID_LEN];
        size_t scan_ssid_len;
@@ -903,29 +909,31 @@ int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason);
 void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
                                      u32 changed);
 u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata);
-int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
-                                  struct ieee80211_ht_info *ht_info);
-int ieee80211_ht_addt_info_ie_to_ht_bss_info(
-                       struct ieee80211_ht_addt_info *ht_add_info_ie,
-                       struct ieee80211_ht_bss_info *bss_info);
-void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, const u8 *da,
-                                 u16 tid, u8 dialog_token, u16 start_seq_num,
-                                 u16 agg_size, u16 timeout);
-void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, const u8 *da, u16 tid,
-                               u16 initiator, u16 reason_code);
-void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn);
-
-void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *da,
-                               u16 tid, u16 initiator, u16 reason);
-void sta_addba_resp_timer_expired(unsigned long data);
-void ieee80211_sta_tear_down_BA_sessions(struct ieee80211_sub_if_data *sdata, u8 *addr);
 u64 ieee80211_sta_get_rates(struct ieee80211_local *local,
                            struct ieee802_11_elems *elems,
                            enum ieee80211_band band);
-void ieee80211_sta_tx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
-               int encrypt);
+void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
+                             u8 *ssid, size_t ssid_len);
 void ieee802_11_parse_elems(u8 *start, size_t len,
-                                  struct ieee802_11_elems *elems);
+                           struct ieee802_11_elems *elems);
+void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local);
+int ieee80211_sta_start_scan(struct ieee80211_sub_if_data *scan_sdata,
+                            u8 *ssid, size_t ssid_len);
+struct ieee80211_sta_bss *
+ieee80211_bss_info_update(struct ieee80211_local *local,
+                         struct ieee80211_rx_status *rx_status,
+                         struct ieee80211_mgmt *mgmt,
+                         size_t len,
+                         struct ieee802_11_elems *elems,
+                         int freq, bool beacon);
+struct ieee80211_sta_bss *
+ieee80211_rx_bss_add(struct ieee80211_local *local, u8 *bssid, int freq,
+                    u8 *ssid, u8 ssid_len);
+struct ieee80211_sta_bss *
+ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq,
+                    u8 *ssid, u8 ssid_len);
+void ieee80211_rx_bss_put(struct ieee80211_local *local,
+                         struct ieee80211_sta_bss *bss);
 
 #ifdef CONFIG_MAC80211_MESH
 void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata);
@@ -951,6 +959,34 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev);
 int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
 int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
 
+/* HT */
+int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
+                                  struct ieee80211_ht_info *ht_info);
+int ieee80211_ht_addt_info_ie_to_ht_bss_info(
+                       struct ieee80211_ht_addt_info *ht_add_info_ie,
+                       struct ieee80211_ht_bss_info *bss_info);
+void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn);
+
+void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *da,
+                               u16 tid, u16 initiator, u16 reason);
+void ieee80211_sta_tear_down_BA_sessions(struct ieee80211_sub_if_data *sdata, u8 *addr);
+void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
+                            struct sta_info *sta,
+                            struct ieee80211_mgmt *mgmt, size_t len);
+void ieee80211_process_addba_resp(struct ieee80211_local *local,
+                                 struct sta_info *sta,
+                                 struct ieee80211_mgmt *mgmt,
+                                 size_t len);
+void ieee80211_process_addba_request(struct ieee80211_local *local,
+                                    struct sta_info *sta,
+                                    struct ieee80211_mgmt *mgmt,
+                                    size_t len);
+
+/* Spectrum management */
+void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
+                                      struct ieee80211_mgmt *mgmt,
+                                      size_t len);
+
 /* utility functions/constants */
 extern void *mac80211_wiphy_privid; /* for wiphy privid */
 extern const unsigned char rfc1042_header[6];
@@ -961,6 +997,9 @@ int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
                             int rate, int erp, int short_preamble);
 void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,
                                     struct ieee80211_hdr *hdr);
+void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata);
+void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
+                     int encrypt);
 
 #ifdef CONFIG_MAC80211_NOINLINE
 #define debug_noinline noinline
index 4a623b8e91fd532f4f5c5a44ce070ede32776219..672cec60a2fbe8173e259b5f27349e60d6859b0c 100644 (file)
@@ -31,11 +31,11 @@ static void ieee80211_teardown_sdata(struct net_device *dev)
        int flushed;
        int i;
 
-       ieee80211_debugfs_remove_netdev(sdata);
-
        /* free extra data */
        ieee80211_free_keys(sdata);
 
+       ieee80211_debugfs_remove_netdev(sdata);
+
        for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
                __skb_queue_purge(&sdata->fragments[i].skb_list);
        sdata->fragment_next = 0;
index 396cfb2d0f4646fdd93623131309e9b385f45e44..6a7f4fae18c2841291d368be769e9ba0cb2c422a 100644 (file)
@@ -399,8 +399,15 @@ static int ieee80211_open(struct net_device *dev)
                atomic_inc(&local->iff_promiscs);
 
        local->open_count++;
-       if (need_hw_reconfig)
+       if (need_hw_reconfig) {
                ieee80211_hw_config(local);
+               /*
+                * set default queue parameters so drivers don't
+                * need to initialise the hardware if the hardware
+                * doesn't start up with sane defaults
+                */
+               ieee80211_set_wmm_default(sdata);
+       }
 
        /*
         * ieee80211_sta_work is disabled while network interface
@@ -551,7 +558,7 @@ static int ieee80211_stop(struct net_device *dev)
                synchronize_rcu();
                skb_queue_purge(&sdata->u.sta.skb_queue);
 
-               if (local->scan_dev == sdata->dev) {
+               if (local->scan_sdata == sdata) {
                        if (!local->ops->hw_scan) {
                                local->sta_sw_scanning = 0;
                                cancel_delayed_work(&local->scan_work);
@@ -593,379 +600,6 @@ static int ieee80211_stop(struct net_device *dev)
        return 0;
 }
 
-int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
-{
-       struct ieee80211_local *local = hw_to_local(hw);
-       struct sta_info *sta;
-       struct ieee80211_sub_if_data *sdata;
-       u16 start_seq_num;
-       u8 *state;
-       int ret;
-       DECLARE_MAC_BUF(mac);
-
-       if (tid >= STA_TID_NUM)
-               return -EINVAL;
-
-#ifdef CONFIG_MAC80211_HT_DEBUG
-       printk(KERN_DEBUG "Open BA session requested for %s tid %u\n",
-                               print_mac(mac, ra), tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-
-       rcu_read_lock();
-
-       sta = sta_info_get(local, ra);
-       if (!sta) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               printk(KERN_DEBUG "Could not find the station\n");
-#endif
-               ret = -ENOENT;
-               goto exit;
-       }
-
-       spin_lock_bh(&sta->lock);
-
-       /* we have tried too many times, receiver does not want A-MPDU */
-       if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) {
-               ret = -EBUSY;
-               goto err_unlock_sta;
-       }
-
-       state = &sta->ampdu_mlme.tid_state_tx[tid];
-       /* check if the TID is not in aggregation flow already */
-       if (*state != HT_AGG_STATE_IDLE) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               printk(KERN_DEBUG "BA request denied - session is not "
-                                "idle on tid %u\n", tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-               ret = -EAGAIN;
-               goto err_unlock_sta;
-       }
-
-       /* prepare A-MPDU MLME for Tx aggregation */
-       sta->ampdu_mlme.tid_tx[tid] =
-                       kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC);
-       if (!sta->ampdu_mlme.tid_tx[tid]) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               if (net_ratelimit())
-                       printk(KERN_ERR "allocate tx mlme to tid %d failed\n",
-                                       tid);
-#endif
-               ret = -ENOMEM;
-               goto err_unlock_sta;
-       }
-       /* Tx timer */
-       sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function =
-                       sta_addba_resp_timer_expired;
-       sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.data =
-                       (unsigned long)&sta->timer_to_tid[tid];
-       init_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
-
-       /* create a new queue for this aggregation */
-       ret = ieee80211_ht_agg_queue_add(local, sta, tid);
-
-       /* case no queue is available to aggregation
-        * don't switch to aggregation */
-       if (ret) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               printk(KERN_DEBUG "BA request denied - queue unavailable for"
-                                       " tid %d\n", tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-               goto err_unlock_queue;
-       }
-       sdata = sta->sdata;
-
-       /* Ok, the Addba frame hasn't been sent yet, but if the driver calls the
-        * call back right away, it must see that the flow has begun */
-       *state |= HT_ADDBA_REQUESTED_MSK;
-
-       /* This is slightly racy because the queue isn't stopped */
-       start_seq_num = sta->tid_seq[tid];
-
-       if (local->ops->ampdu_action)
-               ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_START,
-                                               ra, tid, &start_seq_num);
-
-       if (ret) {
-               /* No need to requeue the packets in the agg queue, since we
-                * held the tx lock: no packet could be enqueued to the newly
-                * allocated queue */
-               ieee80211_ht_agg_queue_remove(local, sta, tid, 0);
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               printk(KERN_DEBUG "BA request denied - HW unavailable for"
-                                       " tid %d\n", tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-               *state = HT_AGG_STATE_IDLE;
-               goto err_unlock_queue;
-       }
-
-       /* Will put all the packets in the new SW queue */
-       ieee80211_requeue(local, ieee802_1d_to_ac[tid]);
-       spin_unlock_bh(&sta->lock);
-
-       /* send an addBA request */
-       sta->ampdu_mlme.dialog_token_allocator++;
-       sta->ampdu_mlme.tid_tx[tid]->dialog_token =
-                       sta->ampdu_mlme.dialog_token_allocator;
-       sta->ampdu_mlme.tid_tx[tid]->ssn = start_seq_num;
-
-
-       ieee80211_send_addba_request(sta->sdata, ra, tid,
-                        sta->ampdu_mlme.tid_tx[tid]->dialog_token,
-                        sta->ampdu_mlme.tid_tx[tid]->ssn,
-                        0x40, 5000);
-       /* activate the timer for the recipient's addBA response */
-       sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.expires =
-                               jiffies + ADDBA_RESP_INTERVAL;
-       add_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
-#ifdef CONFIG_MAC80211_HT_DEBUG
-       printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid);
-#endif
-       goto exit;
-
-err_unlock_queue:
-       kfree(sta->ampdu_mlme.tid_tx[tid]);
-       sta->ampdu_mlme.tid_tx[tid] = NULL;
-       ret = -EBUSY;
-err_unlock_sta:
-       spin_unlock_bh(&sta->lock);
-exit:
-       rcu_read_unlock();
-       return ret;
-}
-EXPORT_SYMBOL(ieee80211_start_tx_ba_session);
-
-int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
-                                u8 *ra, u16 tid,
-                                enum ieee80211_back_parties initiator)
-{
-       struct ieee80211_local *local = hw_to_local(hw);
-       struct sta_info *sta;
-       u8 *state;
-       int ret = 0;
-       DECLARE_MAC_BUF(mac);
-
-       if (tid >= STA_TID_NUM)
-               return -EINVAL;
-
-       rcu_read_lock();
-       sta = sta_info_get(local, ra);
-       if (!sta) {
-               rcu_read_unlock();
-               return -ENOENT;
-       }
-
-       /* check if the TID is in aggregation */
-       state = &sta->ampdu_mlme.tid_state_tx[tid];
-       spin_lock_bh(&sta->lock);
-
-       if (*state != HT_AGG_STATE_OPERATIONAL) {
-               ret = -ENOENT;
-               goto stop_BA_exit;
-       }
-
-#ifdef CONFIG_MAC80211_HT_DEBUG
-       printk(KERN_DEBUG "Tx BA session stop requested for %s tid %u\n",
-                               print_mac(mac, ra), tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-
-       ieee80211_stop_queue(hw, sta->tid_to_tx_q[tid]);
-
-       *state = HT_AGG_STATE_REQ_STOP_BA_MSK |
-               (initiator << HT_AGG_STATE_INITIATOR_SHIFT);
-
-       if (local->ops->ampdu_action)
-               ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_STOP,
-                                               ra, tid, NULL);
-
-       /* case HW denied going back to legacy */
-       if (ret) {
-               WARN_ON(ret != -EBUSY);
-               *state = HT_AGG_STATE_OPERATIONAL;
-               ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
-               goto stop_BA_exit;
-       }
-
-stop_BA_exit:
-       spin_unlock_bh(&sta->lock);
-       rcu_read_unlock();
-       return ret;
-}
-EXPORT_SYMBOL(ieee80211_stop_tx_ba_session);
-
-void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
-{
-       struct ieee80211_local *local = hw_to_local(hw);
-       struct sta_info *sta;
-       u8 *state;
-       DECLARE_MAC_BUF(mac);
-
-       if (tid >= STA_TID_NUM) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n",
-                               tid, STA_TID_NUM);
-#endif
-               return;
-       }
-
-       rcu_read_lock();
-       sta = sta_info_get(local, ra);
-       if (!sta) {
-               rcu_read_unlock();
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               printk(KERN_DEBUG "Could not find station: %s\n",
-                               print_mac(mac, ra));
-#endif
-               return;
-       }
-
-       state = &sta->ampdu_mlme.tid_state_tx[tid];
-       spin_lock_bh(&sta->lock);
-
-       if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               printk(KERN_DEBUG "addBA was not requested yet, state is %d\n",
-                               *state);
-#endif
-               spin_unlock_bh(&sta->lock);
-               rcu_read_unlock();
-               return;
-       }
-
-       WARN_ON_ONCE(*state & HT_ADDBA_DRV_READY_MSK);
-
-       *state |= HT_ADDBA_DRV_READY_MSK;
-
-       if (*state == HT_AGG_STATE_OPERATIONAL) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
-#endif
-               ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
-       }
-       spin_unlock_bh(&sta->lock);
-       rcu_read_unlock();
-}
-EXPORT_SYMBOL(ieee80211_start_tx_ba_cb);
-
-void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
-{
-       struct ieee80211_local *local = hw_to_local(hw);
-       struct sta_info *sta;
-       u8 *state;
-       int agg_queue;
-       DECLARE_MAC_BUF(mac);
-
-       if (tid >= STA_TID_NUM) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n",
-                               tid, STA_TID_NUM);
-#endif
-               return;
-       }
-
-#ifdef CONFIG_MAC80211_HT_DEBUG
-       printk(KERN_DEBUG "Stopping Tx BA session for %s tid %d\n",
-                               print_mac(mac, ra), tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-
-       rcu_read_lock();
-       sta = sta_info_get(local, ra);
-       if (!sta) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               printk(KERN_DEBUG "Could not find station: %s\n",
-                               print_mac(mac, ra));
-#endif
-               rcu_read_unlock();
-               return;
-       }
-       state = &sta->ampdu_mlme.tid_state_tx[tid];
-
-       /* NOTE: no need to use sta->lock in this state check, as
-        * ieee80211_stop_tx_ba_session will let only one stop call to
-        * pass through per sta/tid
-        */
-       if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n");
-#endif
-               rcu_read_unlock();
-               return;
-       }
-
-       if (*state & HT_AGG_STATE_INITIATOR_MSK)
-               ieee80211_send_delba(sta->sdata, ra, tid,
-                       WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
-
-       agg_queue = sta->tid_to_tx_q[tid];
-
-       ieee80211_ht_agg_queue_remove(local, sta, tid, 1);
-
-       /* We just requeued the all the frames that were in the
-        * removed queue, and since we might miss a softirq we do
-        * netif_schedule_queue.  ieee80211_wake_queue is not used
-        * here as this queue is not necessarily stopped
-        */
-       netif_schedule_queue(netdev_get_tx_queue(local->mdev, agg_queue));
-       spin_lock_bh(&sta->lock);
-       *state = HT_AGG_STATE_IDLE;
-       sta->ampdu_mlme.addba_req_num[tid] = 0;
-       kfree(sta->ampdu_mlme.tid_tx[tid]);
-       sta->ampdu_mlme.tid_tx[tid] = NULL;
-       spin_unlock_bh(&sta->lock);
-
-       rcu_read_unlock();
-}
-EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb);
-
-void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
-                                     const u8 *ra, u16 tid)
-{
-       struct ieee80211_local *local = hw_to_local(hw);
-       struct ieee80211_ra_tid *ra_tid;
-       struct sk_buff *skb = dev_alloc_skb(0);
-
-       if (unlikely(!skb)) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               if (net_ratelimit())
-                       printk(KERN_WARNING "%s: Not enough memory, "
-                              "dropping start BA session", skb->dev->name);
-#endif
-               return;
-       }
-       ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
-       memcpy(&ra_tid->ra, ra, ETH_ALEN);
-       ra_tid->tid = tid;
-
-       skb->pkt_type = IEEE80211_ADDBA_MSG;
-       skb_queue_tail(&local->skb_queue, skb);
-       tasklet_schedule(&local->tasklet);
-}
-EXPORT_SYMBOL(ieee80211_start_tx_ba_cb_irqsafe);
-
-void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
-                                    const u8 *ra, u16 tid)
-{
-       struct ieee80211_local *local = hw_to_local(hw);
-       struct ieee80211_ra_tid *ra_tid;
-       struct sk_buff *skb = dev_alloc_skb(0);
-
-       if (unlikely(!skb)) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               if (net_ratelimit())
-                       printk(KERN_WARNING "%s: Not enough memory, "
-                              "dropping stop BA session", skb->dev->name);
-#endif
-               return;
-       }
-       ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
-       memcpy(&ra_tid->ra, ra, ETH_ALEN);
-       ra_tid->tid = tid;
-
-       skb->pkt_type = IEEE80211_DELBA_MSG;
-       skb_queue_tail(&local->skb_queue, skb);
-       tasklet_schedule(&local->tasklet);
-}
-EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb_irqsafe);
-
 static void ieee80211_set_multicast_list(struct net_device *dev)
 {
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
@@ -1140,8 +774,8 @@ u32 ieee80211_handle_ht(struct ieee80211_local *local, int enable_ht,
        ht_conf.ht_supported = 1;
 
        ht_conf.cap = req_ht_cap->cap & sband->ht_info.cap;
-       ht_conf.cap &= ~(IEEE80211_HT_CAP_MIMO_PS);
-       ht_conf.cap |= sband->ht_info.cap & IEEE80211_HT_CAP_MIMO_PS;
+       ht_conf.cap &= ~(IEEE80211_HT_CAP_SM_PS);
+       ht_conf.cap |= sband->ht_info.cap & IEEE80211_HT_CAP_SM_PS;
        ht_bss_conf.primary_channel = req_bss_cap->primary_channel;
        ht_bss_conf.bss_cap = req_bss_cap->bss_cap;
        ht_bss_conf.bss_op_mode = req_bss_cap->bss_op_mode;
index eeb0ce2d5d377295d936fd9a8b2b7b40bf76ee7b..210d6b852406bb7ee0c7d44cd979190edf627ce7 100644 (file)
@@ -149,7 +149,7 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
        pos += ETH_ALEN;
        memcpy(pos, &dst_dsn, 4);
 
-       ieee80211_sta_tx(sdata, skb, 0);
+       ieee80211_tx_skb(sdata, skb, 0);
        return 0;
 }
 
@@ -198,7 +198,7 @@ int mesh_path_error_tx(u8 *dst, __le32 dst_dsn, u8 *ra,
        pos += ETH_ALEN;
        memcpy(pos, &dst_dsn, 4);
 
-       ieee80211_sta_tx(sdata, skb, 0);
+       ieee80211_tx_skb(sdata, skb, 0);
        return 0;
 }
 
@@ -581,6 +581,10 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
        size_t baselen;
        u32 last_hop_metric;
 
+       /* need action_code */
+       if (len < IEEE80211_MIN_ACTION_SIZE + 1)
+               return;
+
        baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt;
        ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable,
                        len - baselen, &elems);
index 7714b0e6e4d767dc7c3b0d7d805fcbe10404559c..7356462dee964686d48ac397810eaf969353cb44 100644 (file)
@@ -217,7 +217,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
                memcpy(pos, &reason, 2);
        }
 
-       ieee80211_sta_tx(sdata, skb, 0);
+       ieee80211_tx_skb(sdata, skb, 0);
        return 0;
 }
 
@@ -421,6 +421,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
        DECLARE_MAC_BUF(mac);
 #endif
 
+       /* need action_code, aux */
+       if (len < IEEE80211_MIN_ACTION_SIZE + 3)
+               return;
+
        if (is_multicast_ether_addr(mgmt->da)) {
                mpl_dbg("Mesh plink: ignore frame from multicast address");
                return;
index df12e746b03e24d5988ca99a85b85888adae205f..2c06f6965b7ddf987dc172f755f6a2b57ddcc022 100644 (file)
  * published by the Free Software Foundation.
  */
 
-/* TODO:
- * order BSS list by RSSI(?) ("quality of AP")
- * scan result table filtering (by capability (privacy, IBSS/BSS, WPA/RSN IE,
- *    SSID)
- */
 #include <linux/delay.h>
 #include <linux/if_ether.h>
 #include <linux/skbuff.h>
@@ -26,9 +21,8 @@
 #include <linux/etherdevice.h>
 #include <linux/rtnetlink.h>
 #include <net/iw_handler.h>
-#include <asm/types.h>
-
 #include <net/mac80211.h>
+
 #include "ieee80211_i.h"
 #include "rate.h"
 #include "led.h"
 #define IEEE80211_SCAN_INTERVAL_SLOW (15 * HZ)
 #define IEEE80211_IBSS_JOIN_TIMEOUT (7 * HZ)
 
-#define IEEE80211_PROBE_DELAY (HZ / 33)
-#define IEEE80211_CHANNEL_TIME (HZ / 33)
-#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 5)
-#define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ)
 #define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ)
 #define IEEE80211_IBSS_INACTIVITY_LIMIT (60 * HZ)
 #define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ)
 #define IEEE80211_IBSS_MAX_STA_ENTRIES 128
 
 
-#define ERP_INFO_USE_PROTECTION BIT(1)
-
-/* mgmt header + 1 byte action code */
-#define IEEE80211_MIN_ACTION_SIZE (24 + 1)
-
-#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002
-#define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C
-#define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFA0
-#define IEEE80211_DELBA_PARAM_TID_MASK 0xF000
-#define IEEE80211_DELBA_PARAM_INITIATOR_MASK 0x0800
-
-/* next values represent the buffer size for A-MPDU frame.
- * According to IEEE802.11n spec size varies from 8K to 64K (in powers of 2) */
-#define IEEE80211_MIN_AMPDU_BUF 0x8
-#define IEEE80211_MAX_AMPDU_BUF 0x40
-
-static void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
-                                    u8 *ssid, size_t ssid_len);
-static struct ieee80211_sta_bss *
-ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq,
-                    u8 *ssid, u8 ssid_len);
-static void ieee80211_rx_bss_put(struct ieee80211_local *local,
-                                struct ieee80211_sta_bss *bss);
-static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
-                                  struct ieee80211_if_sta *ifsta);
-static int ieee80211_sta_wep_configured(struct ieee80211_sub_if_data *sdata);
-static int ieee80211_sta_start_scan(struct ieee80211_sub_if_data *sdata,
-                                   u8 *ssid, size_t ssid_len);
-static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata,
-                                    struct ieee80211_if_sta *ifsta);
-static void sta_rx_agg_session_timer_expired(unsigned long data);
-
-
-void ieee802_11_parse_elems(u8 *start, size_t len,
-                           struct ieee802_11_elems *elems)
+/* utils */
+static int ecw2cw(int ecw)
 {
-       size_t left = len;
-       u8 *pos = start;
-
-       memset(elems, 0, sizeof(*elems));
-       elems->ie_start = start;
-       elems->total_len = len;
-
-       while (left >= 2) {
-               u8 id, elen;
-
-               id = *pos++;
-               elen = *pos++;
-               left -= 2;
-
-               if (elen > left)
-                       return;
-
-               switch (id) {
-               case WLAN_EID_SSID:
-                       elems->ssid = pos;
-                       elems->ssid_len = elen;
-                       break;
-               case WLAN_EID_SUPP_RATES:
-                       elems->supp_rates = pos;
-                       elems->supp_rates_len = elen;
-                       break;
-               case WLAN_EID_FH_PARAMS:
-                       elems->fh_params = pos;
-                       elems->fh_params_len = elen;
-                       break;
-               case WLAN_EID_DS_PARAMS:
-                       elems->ds_params = pos;
-                       elems->ds_params_len = elen;
-                       break;
-               case WLAN_EID_CF_PARAMS:
-                       elems->cf_params = pos;
-                       elems->cf_params_len = elen;
-                       break;
-               case WLAN_EID_TIM:
-                       elems->tim = pos;
-                       elems->tim_len = elen;
-                       break;
-               case WLAN_EID_IBSS_PARAMS:
-                       elems->ibss_params = pos;
-                       elems->ibss_params_len = elen;
-                       break;
-               case WLAN_EID_CHALLENGE:
-                       elems->challenge = pos;
-                       elems->challenge_len = elen;
-                       break;
-               case WLAN_EID_WPA:
-                       if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 &&
-                           pos[2] == 0xf2) {
-                               /* Microsoft OUI (00:50:F2) */
-                               if (pos[3] == 1) {
-                                       /* OUI Type 1 - WPA IE */
-                                       elems->wpa = pos;
-                                       elems->wpa_len = elen;
-                               } else if (elen >= 5 && pos[3] == 2) {
-                                       if (pos[4] == 0) {
-                                               elems->wmm_info = pos;
-                                               elems->wmm_info_len = elen;
-                                       } else if (pos[4] == 1) {
-                                               elems->wmm_param = pos;
-                                               elems->wmm_param_len = elen;
-                                       }
-                               }
-                       }
-                       break;
-               case WLAN_EID_RSN:
-                       elems->rsn = pos;
-                       elems->rsn_len = elen;
-                       break;
-               case WLAN_EID_ERP_INFO:
-                       elems->erp_info = pos;
-                       elems->erp_info_len = elen;
-                       break;
-               case WLAN_EID_EXT_SUPP_RATES:
-                       elems->ext_supp_rates = pos;
-                       elems->ext_supp_rates_len = elen;
-                       break;
-               case WLAN_EID_HT_CAPABILITY:
-                       elems->ht_cap_elem = pos;
-                       elems->ht_cap_elem_len = elen;
-                       break;
-               case WLAN_EID_HT_EXTRA_INFO:
-                       elems->ht_info_elem = pos;
-                       elems->ht_info_elem_len = elen;
-                       break;
-               case WLAN_EID_MESH_ID:
-                       elems->mesh_id = pos;
-                       elems->mesh_id_len = elen;
-                       break;
-               case WLAN_EID_MESH_CONFIG:
-                       elems->mesh_config = pos;
-                       elems->mesh_config_len = elen;
-                       break;
-               case WLAN_EID_PEER_LINK:
-                       elems->peer_link = pos;
-                       elems->peer_link_len = elen;
-                       break;
-               case WLAN_EID_PREQ:
-                       elems->preq = pos;
-                       elems->preq_len = elen;
-                       break;
-               case WLAN_EID_PREP:
-                       elems->prep = pos;
-                       elems->prep_len = elen;
-                       break;
-               case WLAN_EID_PERR:
-                       elems->perr = pos;
-                       elems->perr_len = elen;
-                       break;
-               case WLAN_EID_CHANNEL_SWITCH:
-                       elems->ch_switch_elem = pos;
-                       elems->ch_switch_elem_len = elen;
-                       break;
-               case WLAN_EID_QUIET:
-                       if (!elems->quiet_elem) {
-                               elems->quiet_elem = pos;
-                               elems->quiet_elem_len = elen;
-                       }
-                       elems->num_of_quiet_elem++;
-                       break;
-               case WLAN_EID_COUNTRY:
-                       elems->country_elem = pos;
-                       elems->country_elem_len = elen;
-                       break;
-               case WLAN_EID_PWR_CONSTRAINT:
-                       elems->pwr_constr_elem = pos;
-                       elems->pwr_constr_elem_len = elen;
-                       break;
-               default:
-                       break;
-               }
-
-               left -= elen;
-               pos += elen;
-       }
+       return (1 << ecw) - 1;
 }
 
-
-static u8 * ieee80211_bss_get_ie(struct ieee80211_sta_bss *bss, u8 ie)
+static u8 *ieee80211_bss_get_ie(struct ieee80211_sta_bss *bss, u8 ie)
 {
        u8 *end, *pos;
 
@@ -256,109 +74,439 @@ static u8 * ieee80211_bss_get_ie(struct ieee80211_sta_bss *bss, u8 ie)
        return NULL;
 }
 
-
-static int ecw2cw(int ecw)
+static int ieee80211_compatible_rates(struct ieee80211_sta_bss *bss,
+                                     struct ieee80211_supported_band *sband,
+                                     u64 *rates)
 {
-       return (1 << ecw) - 1;
-}
+       int i, j, count;
+       *rates = 0;
+       count = 0;
+       for (i = 0; i < bss->supp_rates_len; i++) {
+               int rate = (bss->supp_rates[i] & 0x7F) * 5;
+
+               for (j = 0; j < sband->n_bitrates; j++)
+                       if (sband->bitrates[j].bitrate == rate) {
+                               *rates |= BIT(j);
+                               count++;
+                               break;
+                       }
+       }
 
+       return count;
+}
 
-static void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
-                                        struct ieee80211_sta_bss *bss,
-                                        int ibss)
+/* frame sending functions */
+static void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
+                               struct ieee80211_if_sta *ifsta,
+                               int transaction, u8 *extra, size_t extra_len,
+                               int encrypt)
 {
        struct ieee80211_local *local = sdata->local;
-       int i, have_higher_than_11mbit = 0;
-
-
-       /* cf. IEEE 802.11 9.2.12 */
-       for (i = 0; i < bss->supp_rates_len; i++)
-               if ((bss->supp_rates[i] & 0x7f) * 5 > 110)
-                       have_higher_than_11mbit = 1;
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *mgmt;
 
-       if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
-           have_higher_than_11mbit)
-               sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
-       else
-               sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+                           sizeof(*mgmt) + 6 + extra_len);
+       if (!skb) {
+               printk(KERN_DEBUG "%s: failed to allocate buffer for auth "
+                      "frame\n", sdata->dev->name);
+               return;
+       }
+       skb_reserve(skb, local->hw.extra_tx_headroom);
 
+       mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6);
+       memset(mgmt, 0, 24 + 6);
+       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                         IEEE80211_STYPE_AUTH);
+       if (encrypt)
+               mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+       memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+       mgmt->u.auth.auth_alg = cpu_to_le16(ifsta->auth_alg);
+       mgmt->u.auth.auth_transaction = cpu_to_le16(transaction);
+       ifsta->auth_transaction = transaction + 1;
+       mgmt->u.auth.status_code = cpu_to_le16(0);
+       if (extra)
+               memcpy(skb_put(skb, extra_len), extra, extra_len);
 
-       if (local->ops->conf_tx) {
-               struct ieee80211_tx_queue_params qparam;
+       ieee80211_tx_skb(sdata, skb, encrypt);
+}
 
-               memset(&qparam, 0, sizeof(qparam));
+void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
+                             u8 *ssid, size_t ssid_len)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_supported_band *sband;
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *mgmt;
+       u8 *pos, *supp_rates, *esupp_rates = NULL;
+       int i;
 
-               qparam.aifs = 2;
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200);
+       if (!skb) {
+               printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
+                      "request\n", sdata->dev->name);
+               return;
+       }
+       skb_reserve(skb, local->hw.extra_tx_headroom);
 
-               if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
-                   !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE))
-                       qparam.cw_min = 31;
-               else
-                       qparam.cw_min = 15;
+       mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+       memset(mgmt, 0, 24);
+       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                         IEEE80211_STYPE_PROBE_REQ);
+       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+       if (dst) {
+               memcpy(mgmt->da, dst, ETH_ALEN);
+               memcpy(mgmt->bssid, dst, ETH_ALEN);
+       } else {
+               memset(mgmt->da, 0xff, ETH_ALEN);
+               memset(mgmt->bssid, 0xff, ETH_ALEN);
+       }
+       pos = skb_put(skb, 2 + ssid_len);
+       *pos++ = WLAN_EID_SSID;
+       *pos++ = ssid_len;
+       memcpy(pos, ssid, ssid_len);
 
-               qparam.cw_max = 1023;
-               qparam.txop = 0;
+       supp_rates = skb_put(skb, 2);
+       supp_rates[0] = WLAN_EID_SUPP_RATES;
+       supp_rates[1] = 0;
+       sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 
-               for (i = 0; i < local_to_hw(local)->queues; i++)
-                       local->ops->conf_tx(local_to_hw(local), i, &qparam);
+       for (i = 0; i < sband->n_bitrates; i++) {
+               struct ieee80211_rate *rate = &sband->bitrates[i];
+               if (esupp_rates) {
+                       pos = skb_put(skb, 1);
+                       esupp_rates[1]++;
+               } else if (supp_rates[1] == 8) {
+                       esupp_rates = skb_put(skb, 3);
+                       esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES;
+                       esupp_rates[1] = 1;
+                       pos = &esupp_rates[2];
+               } else {
+                       pos = skb_put(skb, 1);
+                       supp_rates[1]++;
+               }
+               *pos = rate->bitrate / 5;
        }
+
+       ieee80211_tx_skb(sdata, skb, 0);
 }
 
-static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
-                                    struct ieee80211_if_sta *ifsta,
-                                    u8 *wmm_param, size_t wmm_param_len)
+static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
+                                struct ieee80211_if_sta *ifsta)
 {
-       struct ieee80211_tx_queue_params params;
-       size_t left;
-       int count;
-       u8 *pos;
+       struct ieee80211_local *local = sdata->local;
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *mgmt;
+       u8 *pos, *ies, *ht_add_ie;
+       int i, len, count, rates_len, supp_rates_len;
+       u16 capab;
+       struct ieee80211_sta_bss *bss;
+       int wmm = 0;
+       struct ieee80211_supported_band *sband;
+       u64 rates = 0;
 
-       if (!(ifsta->flags & IEEE80211_STA_WMM_ENABLED))
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+                           sizeof(*mgmt) + 200 + ifsta->extra_ie_len +
+                           ifsta->ssid_len);
+       if (!skb) {
+               printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
+                      "frame\n", sdata->dev->name);
                return;
+       }
+       skb_reserve(skb, local->hw.extra_tx_headroom);
 
-       if (!wmm_param)
-               return;
+       sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 
-       if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1)
-               return;
-       count = wmm_param[6] & 0x0f;
-       if (count == ifsta->wmm_last_param_set)
-               return;
-       ifsta->wmm_last_param_set = count;
+       capab = ifsta->capab;
 
-       pos = wmm_param + 8;
-       left = wmm_param_len - 8;
+       if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ) {
+               if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
+                       capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+               if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
+                       capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+       }
 
-       memset(&params, 0, sizeof(params));
+       bss = ieee80211_rx_bss_get(local, ifsta->bssid,
+                                  local->hw.conf.channel->center_freq,
+                                  ifsta->ssid, ifsta->ssid_len);
+       if (bss) {
+               if (bss->capability & WLAN_CAPABILITY_PRIVACY)
+                       capab |= WLAN_CAPABILITY_PRIVACY;
+               if (bss->wmm_used)
+                       wmm = 1;
 
-       if (!local->ops->conf_tx)
-               return;
+               /* get all rates supported by the device and the AP as
+                * some APs don't like getting a superset of their rates
+                * in the association request (e.g. D-Link DAP 1353 in
+                * b-only mode) */
+               rates_len = ieee80211_compatible_rates(bss, sband, &rates);
 
-       local->wmm_acm = 0;
-       for (; left >= 4; left -= 4, pos += 4) {
-               int aci = (pos[0] >> 5) & 0x03;
-               int acm = (pos[0] >> 4) & 0x01;
-               int queue;
+               if ((bss->capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
+                   (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
+                       capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
 
-               switch (aci) {
-               case 1:
-                       queue = 3;
-                       if (acm)
-                               local->wmm_acm |= BIT(0) | BIT(3);
-                       break;
-               case 2:
-                       queue = 1;
-                       if (acm)
-                               local->wmm_acm |= BIT(4) | BIT(5);
-                       break;
-               case 3:
-                       queue = 0;
-                       if (acm)
-                               local->wmm_acm |= BIT(6) | BIT(7);
-                       break;
-               case 0:
-               default:
-                       queue = 2;
+               ieee80211_rx_bss_put(local, bss);
+       } else {
+               rates = ~0;
+               rates_len = sband->n_bitrates;
+       }
+
+       mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+       memset(mgmt, 0, 24);
+       memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+
+       if (ifsta->flags & IEEE80211_STA_PREV_BSSID_SET) {
+               skb_put(skb, 10);
+               mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                                 IEEE80211_STYPE_REASSOC_REQ);
+               mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab);
+               mgmt->u.reassoc_req.listen_interval =
+                               cpu_to_le16(local->hw.conf.listen_interval);
+               memcpy(mgmt->u.reassoc_req.current_ap, ifsta->prev_bssid,
+                      ETH_ALEN);
+       } else {
+               skb_put(skb, 4);
+               mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                                 IEEE80211_STYPE_ASSOC_REQ);
+               mgmt->u.assoc_req.capab_info = cpu_to_le16(capab);
+               mgmt->u.reassoc_req.listen_interval =
+                               cpu_to_le16(local->hw.conf.listen_interval);
+       }
+
+       /* SSID */
+       ies = pos = skb_put(skb, 2 + ifsta->ssid_len);
+       *pos++ = WLAN_EID_SSID;
+       *pos++ = ifsta->ssid_len;
+       memcpy(pos, ifsta->ssid, ifsta->ssid_len);
+
+       /* add all rates which were marked to be used above */
+       supp_rates_len = rates_len;
+       if (supp_rates_len > 8)
+               supp_rates_len = 8;
+
+       len = sband->n_bitrates;
+       pos = skb_put(skb, supp_rates_len + 2);
+       *pos++ = WLAN_EID_SUPP_RATES;
+       *pos++ = supp_rates_len;
+
+       count = 0;
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if (BIT(i) & rates) {
+                       int rate = sband->bitrates[i].bitrate;
+                       *pos++ = (u8) (rate / 5);
+                       if (++count == 8)
+                               break;
+               }
+       }
+
+       if (rates_len > count) {
+               pos = skb_put(skb, rates_len - count + 2);
+               *pos++ = WLAN_EID_EXT_SUPP_RATES;
+               *pos++ = rates_len - count;
+
+               for (i++; i < sband->n_bitrates; i++) {
+                       if (BIT(i) & rates) {
+                               int rate = sband->bitrates[i].bitrate;
+                               *pos++ = (u8) (rate / 5);
+                       }
+               }
+       }
+
+       if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) {
+               /* 1. power capabilities */
+               pos = skb_put(skb, 4);
+               *pos++ = WLAN_EID_PWR_CAPABILITY;
+               *pos++ = 2;
+               *pos++ = 0; /* min tx power */
+               *pos++ = local->hw.conf.channel->max_power; /* max tx power */
+
+               /* 2. supported channels */
+               /* TODO: get this in reg domain format */
+               pos = skb_put(skb, 2 * sband->n_channels + 2);
+               *pos++ = WLAN_EID_SUPPORTED_CHANNELS;
+               *pos++ = 2 * sband->n_channels;
+               for (i = 0; i < sband->n_channels; i++) {
+                       *pos++ = ieee80211_frequency_to_channel(
+                                       sband->channels[i].center_freq);
+                       *pos++ = 1; /* one channel in the subband*/
+               }
+       }
+
+       if (ifsta->extra_ie) {
+               pos = skb_put(skb, ifsta->extra_ie_len);
+               memcpy(pos, ifsta->extra_ie, ifsta->extra_ie_len);
+       }
+
+       if (wmm && (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) {
+               pos = skb_put(skb, 9);
+               *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+               *pos++ = 7; /* len */
+               *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
+               *pos++ = 0x50;
+               *pos++ = 0xf2;
+               *pos++ = 2; /* WME */
+               *pos++ = 0; /* WME info */
+               *pos++ = 1; /* WME ver */
+               *pos++ = 0;
+       }
+
+       /* wmm support is a must to HT */
+       if (wmm && (ifsta->flags & IEEE80211_STA_WMM_ENABLED) &&
+           sband->ht_info.ht_supported &&
+           (ht_add_ie = ieee80211_bss_get_ie(bss, WLAN_EID_HT_EXTRA_INFO))) {
+               struct ieee80211_ht_addt_info *ht_add_info =
+                       (struct ieee80211_ht_addt_info *)ht_add_ie;
+               u16 cap = sband->ht_info.cap;
+               __le16 tmp;
+               u32 flags = local->hw.conf.channel->flags;
+
+               switch (ht_add_info->ht_param & IEEE80211_HT_IE_CHA_SEC_OFFSET) {
+               case IEEE80211_HT_IE_CHA_SEC_ABOVE:
+                       if (flags & IEEE80211_CHAN_NO_FAT_ABOVE) {
+                               cap &= ~IEEE80211_HT_CAP_SUP_WIDTH;
+                               cap &= ~IEEE80211_HT_CAP_SGI_40;
+                       }
+                       break;
+               case IEEE80211_HT_IE_CHA_SEC_BELOW:
+                       if (flags & IEEE80211_CHAN_NO_FAT_BELOW) {
+                               cap &= ~IEEE80211_HT_CAP_SUP_WIDTH;
+                               cap &= ~IEEE80211_HT_CAP_SGI_40;
+                       }
+                       break;
+               }
+
+               tmp = cpu_to_le16(cap);
+               pos = skb_put(skb, sizeof(struct ieee80211_ht_cap)+2);
+               *pos++ = WLAN_EID_HT_CAPABILITY;
+               *pos++ = sizeof(struct ieee80211_ht_cap);
+               memset(pos, 0, sizeof(struct ieee80211_ht_cap));
+               memcpy(pos, &tmp, sizeof(u16));
+               pos += sizeof(u16);
+               /* TODO: needs a define here for << 2 */
+               *pos++ = sband->ht_info.ampdu_factor |
+                        (sband->ht_info.ampdu_density << 2);
+               memcpy(pos, sband->ht_info.supp_mcs_set, 16);
+       }
+
+       kfree(ifsta->assocreq_ies);
+       ifsta->assocreq_ies_len = (skb->data + skb->len) - ies;
+       ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_KERNEL);
+       if (ifsta->assocreq_ies)
+               memcpy(ifsta->assocreq_ies, ies, ifsta->assocreq_ies_len);
+
+       ieee80211_tx_skb(sdata, skb, 0);
+}
+
+
+static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
+                                          u16 stype, u16 reason)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *mgmt;
+
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt));
+       if (!skb) {
+               printk(KERN_DEBUG "%s: failed to allocate buffer for "
+                      "deauth/disassoc frame\n", sdata->dev->name);
+               return;
+       }
+       skb_reserve(skb, local->hw.extra_tx_headroom);
+
+       mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+       memset(mgmt, 0, 24);
+       memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
+       skb_put(skb, 2);
+       /* u.deauth.reason_code == u.disassoc.reason_code */
+       mgmt->u.deauth.reason_code = cpu_to_le16(reason);
+
+       ieee80211_tx_skb(sdata, skb, 0);
+}
+
+/* MLME */
+static void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
+                                        struct ieee80211_sta_bss *bss)
+{
+       struct ieee80211_local *local = sdata->local;
+       int i, have_higher_than_11mbit = 0;
+
+       /* cf. IEEE 802.11 9.2.12 */
+       for (i = 0; i < bss->supp_rates_len; i++)
+               if ((bss->supp_rates[i] & 0x7f) * 5 > 110)
+                       have_higher_than_11mbit = 1;
+
+       if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
+           have_higher_than_11mbit)
+               sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
+       else
+               sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
+
+       ieee80211_set_wmm_default(sdata);
+}
+
+static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
+                                    struct ieee80211_if_sta *ifsta,
+                                    u8 *wmm_param, size_t wmm_param_len)
+{
+       struct ieee80211_tx_queue_params params;
+       size_t left;
+       int count;
+       u8 *pos;
+
+       if (!(ifsta->flags & IEEE80211_STA_WMM_ENABLED))
+               return;
+
+       if (!wmm_param)
+               return;
+
+       if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1)
+               return;
+       count = wmm_param[6] & 0x0f;
+       if (count == ifsta->wmm_last_param_set)
+               return;
+       ifsta->wmm_last_param_set = count;
+
+       pos = wmm_param + 8;
+       left = wmm_param_len - 8;
+
+       memset(&params, 0, sizeof(params));
+
+       if (!local->ops->conf_tx)
+               return;
+
+       local->wmm_acm = 0;
+       for (; left >= 4; left -= 4, pos += 4) {
+               int aci = (pos[0] >> 5) & 0x03;
+               int acm = (pos[0] >> 4) & 0x01;
+               int queue;
+
+               switch (aci) {
+               case 1:
+                       queue = 3;
+                       if (acm)
+                               local->wmm_acm |= BIT(0) | BIT(3);
+                       break;
+               case 2:
+                       queue = 1;
+                       if (acm)
+                               local->wmm_acm |= BIT(4) | BIT(5);
+                       break;
+               case 3:
+                       queue = 0;
+                       if (acm)
+                               local->wmm_acm |= BIT(6) | BIT(7);
+                       break;
+               case 0:
+               default:
+                       queue = 2;
                        if (acm)
                                local->wmm_acm |= BIT(1) | BIT(2);
                        break;
@@ -451,51 +599,16 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
        return changed;
 }
 
-int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
-                                  struct ieee80211_ht_info *ht_info)
+static void ieee80211_sta_send_apinfo(struct ieee80211_sub_if_data *sdata,
+                                       struct ieee80211_if_sta *ifsta)
 {
-
-       if (ht_info == NULL)
-               return -EINVAL;
-
-       memset(ht_info, 0, sizeof(*ht_info));
-
-       if (ht_cap_ie) {
-               u8 ampdu_info = ht_cap_ie->ampdu_params_info;
-
-               ht_info->ht_supported = 1;
-               ht_info->cap = le16_to_cpu(ht_cap_ie->cap_info);
-               ht_info->ampdu_factor =
-                       ampdu_info & IEEE80211_HT_CAP_AMPDU_FACTOR;
-               ht_info->ampdu_density =
-                       (ampdu_info & IEEE80211_HT_CAP_AMPDU_DENSITY) >> 2;
-               memcpy(ht_info->supp_mcs_set, ht_cap_ie->supp_mcs_set, 16);
-       } else
-               ht_info->ht_supported = 0;
-
-       return 0;
-}
-
-int ieee80211_ht_addt_info_ie_to_ht_bss_info(
-                       struct ieee80211_ht_addt_info *ht_add_info_ie,
-                       struct ieee80211_ht_bss_info *bss_info)
-{
-       if (bss_info == NULL)
-               return -EINVAL;
-
-       memset(bss_info, 0, sizeof(*bss_info));
-
-       if (ht_add_info_ie) {
-               u16 op_mode;
-               op_mode = le16_to_cpu(ht_add_info_ie->operation_mode);
-
-               bss_info->primary_channel = ht_add_info_ie->control_chan;
-               bss_info->bss_cap = ht_add_info_ie->ht_param;
-               bss_info->bss_op_mode = (u8)(op_mode & 0xff);
-       }
-
-       return 0;
-}
+       union iwreq_data wrqu;
+       memset(&wrqu, 0, sizeof(wrqu));
+       if (ifsta->flags & IEEE80211_STA_ASSOCIATED)
+               memcpy(wrqu.ap_addr.sa_data, sdata->u.sta.bssid, ETH_ALEN);
+       wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+       wireless_send_event(sdata->dev, SIOCGIWAP, &wrqu, NULL);
+}
 
 static void ieee80211_sta_send_associnfo(struct ieee80211_sub_if_data *sdata,
                                         struct ieee80211_if_sta *ifsta)
@@ -518,134 +631,54 @@ static void ieee80211_sta_send_associnfo(struct ieee80211_sub_if_data *sdata,
 
 
 static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
-                                    struct ieee80211_if_sta *ifsta,
-                                    bool assoc)
+                                    struct ieee80211_if_sta *ifsta)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_conf *conf = &local_to_hw(local)->conf;
-       union iwreq_data wrqu;
        u32 changed = BSS_CHANGED_ASSOC;
 
-       if (assoc) {
-               struct ieee80211_sta_bss *bss;
-
-               ifsta->flags |= IEEE80211_STA_ASSOCIATED;
-
-               if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
-                       return;
-
-               bss = ieee80211_rx_bss_get(local, ifsta->bssid,
-                                          conf->channel->center_freq,
-                                          ifsta->ssid, ifsta->ssid_len);
-               if (bss) {
-                       /* set timing information */
-                       sdata->bss_conf.beacon_int = bss->beacon_int;
-                       sdata->bss_conf.timestamp = bss->timestamp;
-                       sdata->bss_conf.dtim_period = bss->dtim_period;
-
-                       changed |= ieee80211_handle_bss_capability(sdata, bss);
+       struct ieee80211_sta_bss *bss;
 
-                       ieee80211_rx_bss_put(local, bss);
-               }
+       ifsta->flags |= IEEE80211_STA_ASSOCIATED;
 
-               if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) {
-                       changed |= BSS_CHANGED_HT;
-                       sdata->bss_conf.assoc_ht = 1;
-                       sdata->bss_conf.ht_conf = &conf->ht_conf;
-                       sdata->bss_conf.ht_bss_conf = &conf->ht_bss_conf;
-               }
+       if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
+               return;
 
-               ifsta->flags |= IEEE80211_STA_PREV_BSSID_SET;
-               memcpy(ifsta->prev_bssid, sdata->u.sta.bssid, ETH_ALEN);
-               memcpy(wrqu.ap_addr.sa_data, sdata->u.sta.bssid, ETH_ALEN);
-               ieee80211_sta_send_associnfo(sdata, ifsta);
-       } else {
-               netif_carrier_off(sdata->dev);
-               ieee80211_sta_tear_down_BA_sessions(sdata, ifsta->bssid);
-               ifsta->flags &= ~IEEE80211_STA_ASSOCIATED;
-               changed |= ieee80211_reset_erp_info(sdata);
+       bss = ieee80211_rx_bss_get(local, ifsta->bssid,
+                                  conf->channel->center_freq,
+                                  ifsta->ssid, ifsta->ssid_len);
+       if (bss) {
+               /* set timing information */
+               sdata->bss_conf.beacon_int = bss->beacon_int;
+               sdata->bss_conf.timestamp = bss->timestamp;
+               sdata->bss_conf.dtim_period = bss->dtim_period;
 
-               sdata->bss_conf.assoc_ht = 0;
-               sdata->bss_conf.ht_conf = NULL;
-               sdata->bss_conf.ht_bss_conf = NULL;
+               changed |= ieee80211_handle_bss_capability(sdata, bss);
 
-               memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
+               ieee80211_rx_bss_put(local, bss);
        }
-       ifsta->last_probe = jiffies;
-       ieee80211_led_assoc(local, assoc);
-
-       sdata->bss_conf.assoc = assoc;
-       ieee80211_bss_info_change_notify(sdata, changed);
-
-       if (assoc)
-               netif_carrier_on(sdata->dev);
-
-       wrqu.ap_addr.sa_family = ARPHRD_ETHER;
-       wireless_send_event(sdata->dev, SIOCGIWAP, &wrqu, NULL);
-}
 
-static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
-                                  struct ieee80211_if_sta *ifsta, int deauth)
-{
-       if (deauth) {
-               ifsta->direct_probe_tries = 0;
-               ifsta->auth_tries = 0;
+       if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) {
+               changed |= BSS_CHANGED_HT;
+               sdata->bss_conf.assoc_ht = 1;
+               sdata->bss_conf.ht_conf = &conf->ht_conf;
+               sdata->bss_conf.ht_bss_conf = &conf->ht_bss_conf;
        }
-       ifsta->assoc_scan_tries = 0;
-       ifsta->assoc_tries = 0;
-       ieee80211_set_associated(sdata, ifsta, 0);
-}
-
-void ieee80211_sta_tx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
-                     int encrypt)
-{
-       skb->dev = sdata->local->mdev;
-       skb_set_mac_header(skb, 0);
-       skb_set_network_header(skb, 0);
-       skb_set_transport_header(skb, 0);
-
-       skb->iif = sdata->dev->ifindex;
-       skb->do_not_encrypt = !encrypt;
-
-       dev_queue_xmit(skb);
-}
 
+       ifsta->flags |= IEEE80211_STA_PREV_BSSID_SET;
+       memcpy(ifsta->prev_bssid, sdata->u.sta.bssid, ETH_ALEN);
+       ieee80211_sta_send_associnfo(sdata, ifsta);
 
-static void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
-                               struct ieee80211_if_sta *ifsta,
-                               int transaction, u8 *extra, size_t extra_len,
-                               int encrypt)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct sk_buff *skb;
-       struct ieee80211_mgmt *mgmt;
+       ifsta->last_probe = jiffies;
+       ieee80211_led_assoc(local, 1);
 
-       skb = dev_alloc_skb(local->hw.extra_tx_headroom +
-                           sizeof(*mgmt) + 6 + extra_len);
-       if (!skb) {
-               printk(KERN_DEBUG "%s: failed to allocate buffer for auth "
-                      "frame\n", sdata->dev->name);
-               return;
-       }
-       skb_reserve(skb, local->hw.extra_tx_headroom);
+       sdata->bss_conf.assoc = 1;
+       ieee80211_bss_info_change_notify(sdata, changed);
 
-       mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6);
-       memset(mgmt, 0, 24 + 6);
-       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                         IEEE80211_STYPE_AUTH);
-       if (encrypt)
-               mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
-       memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
-       memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
-       mgmt->u.auth.auth_alg = cpu_to_le16(ifsta->auth_alg);
-       mgmt->u.auth.auth_transaction = cpu_to_le16(transaction);
-       ifsta->auth_transaction = transaction + 1;
-       mgmt->u.auth.status_code = cpu_to_le16(0);
-       if (extra)
-               memcpy(skb_put(skb, extra_len), extra, extra_len);
+       netif_tx_start_all_queues(sdata->dev);
+       netif_carrier_on(sdata->dev);
 
-       ieee80211_sta_tx(sdata, skb, encrypt);
+       ieee80211_sta_send_apinfo(sdata, ifsta);
 }
 
 static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata,
@@ -702,294 +735,76 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata,
        mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
 }
 
-static int ieee80211_compatible_rates(struct ieee80211_sta_bss *bss,
-                                     struct ieee80211_supported_band *sband,
-                                     u64 *rates)
-{
-       int i, j, count;
-       *rates = 0;
-       count = 0;
-       for (i = 0; i < bss->supp_rates_len; i++) {
-               int rate = (bss->supp_rates[i] & 0x7F) * 5;
-
-               for (j = 0; j < sband->n_bitrates; j++)
-                       if (sband->bitrates[j].bitrate == rate) {
-                               *rates |= BIT(j);
-                               count++;
-                               break;
-                       }
-       }
-
-       return count;
-}
-
-static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
-                                struct ieee80211_if_sta *ifsta)
+static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
+                                  struct ieee80211_if_sta *ifsta, bool deauth,
+                                  bool self_disconnected, u16 reason)
 {
        struct ieee80211_local *local = sdata->local;
-       struct sk_buff *skb;
-       struct ieee80211_mgmt *mgmt;
-       u8 *pos, *ies, *ht_add_ie;
-       int i, len, count, rates_len, supp_rates_len;
-       u16 capab;
-       struct ieee80211_sta_bss *bss;
-       int wmm = 0;
-       struct ieee80211_supported_band *sband;
-       u64 rates = 0;
+       struct sta_info *sta;
+       u32 changed = BSS_CHANGED_ASSOC;
 
-       skb = dev_alloc_skb(local->hw.extra_tx_headroom +
-                           sizeof(*mgmt) + 200 + ifsta->extra_ie_len +
-                           ifsta->ssid_len);
-       if (!skb) {
-               printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
-                      "frame\n", sdata->dev->name);
+       rcu_read_lock();
+
+       sta = sta_info_get(local, ifsta->bssid);
+       if (!sta) {
+               rcu_read_unlock();
                return;
        }
-       skb_reserve(skb, local->hw.extra_tx_headroom);
 
-       sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
-
-       capab = ifsta->capab;
-
-       if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ) {
-               if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
-                       capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
-               if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
-                       capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+       if (deauth) {
+               ifsta->direct_probe_tries = 0;
+               ifsta->auth_tries = 0;
        }
+       ifsta->assoc_scan_tries = 0;
+       ifsta->assoc_tries = 0;
 
-       bss = ieee80211_rx_bss_get(local, ifsta->bssid,
-                                  local->hw.conf.channel->center_freq,
-                                  ifsta->ssid, ifsta->ssid_len);
-       if (bss) {
-               if (bss->capability & WLAN_CAPABILITY_PRIVACY)
-                       capab |= WLAN_CAPABILITY_PRIVACY;
-               if (bss->wmm_used)
-                       wmm = 1;
-
-               /* get all rates supported by the device and the AP as
-                * some APs don't like getting a superset of their rates
-                * in the association request (e.g. D-Link DAP 1353 in
-                * b-only mode) */
-               rates_len = ieee80211_compatible_rates(bss, sband, &rates);
+       netif_tx_stop_all_queues(sdata->dev);
+       netif_carrier_off(sdata->dev);
 
-               if ((bss->capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
-                   (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
-                       capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
+       ieee80211_sta_tear_down_BA_sessions(sdata, sta->addr);
 
-               ieee80211_rx_bss_put(local, bss);
-       } else {
-               rates = ~0;
-               rates_len = sband->n_bitrates;
+       if (self_disconnected) {
+               if (deauth)
+                       ieee80211_send_deauth_disassoc(sdata,
+                               IEEE80211_STYPE_DEAUTH, reason);
+               else
+                       ieee80211_send_deauth_disassoc(sdata,
+                               IEEE80211_STYPE_DISASSOC, reason);
        }
 
-       mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
-       memset(mgmt, 0, 24);
-       memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
-       memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+       ifsta->flags &= ~IEEE80211_STA_ASSOCIATED;
+       changed |= ieee80211_reset_erp_info(sdata);
 
-       if (ifsta->flags & IEEE80211_STA_PREV_BSSID_SET) {
-               skb_put(skb, 10);
-               mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                                 IEEE80211_STYPE_REASSOC_REQ);
-               mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab);
-               mgmt->u.reassoc_req.listen_interval =
-                               cpu_to_le16(local->hw.conf.listen_interval);
-               memcpy(mgmt->u.reassoc_req.current_ap, ifsta->prev_bssid,
-                      ETH_ALEN);
-       } else {
-               skb_put(skb, 4);
-               mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                                 IEEE80211_STYPE_ASSOC_REQ);
-               mgmt->u.assoc_req.capab_info = cpu_to_le16(capab);
-               mgmt->u.reassoc_req.listen_interval =
-                               cpu_to_le16(local->hw.conf.listen_interval);
-       }
+       if (sdata->bss_conf.assoc_ht)
+               changed |= BSS_CHANGED_HT;
 
-       /* SSID */
-       ies = pos = skb_put(skb, 2 + ifsta->ssid_len);
-       *pos++ = WLAN_EID_SSID;
-       *pos++ = ifsta->ssid_len;
-       memcpy(pos, ifsta->ssid, ifsta->ssid_len);
+       sdata->bss_conf.assoc_ht = 0;
+       sdata->bss_conf.ht_conf = NULL;
+       sdata->bss_conf.ht_bss_conf = NULL;
 
-       /* add all rates which were marked to be used above */
-       supp_rates_len = rates_len;
-       if (supp_rates_len > 8)
-               supp_rates_len = 8;
+       ieee80211_led_assoc(local, 0);
+       sdata->bss_conf.assoc = 0;
 
-       len = sband->n_bitrates;
-       pos = skb_put(skb, supp_rates_len + 2);
-       *pos++ = WLAN_EID_SUPP_RATES;
-       *pos++ = supp_rates_len;
+       ieee80211_sta_send_apinfo(sdata, ifsta);
 
-       count = 0;
-       for (i = 0; i < sband->n_bitrates; i++) {
-               if (BIT(i) & rates) {
-                       int rate = sband->bitrates[i].bitrate;
-                       *pos++ = (u8) (rate / 5);
-                       if (++count == 8)
-                               break;
-               }
-       }
+       if (self_disconnected)
+               ifsta->state = IEEE80211_STA_MLME_DISABLED;
 
-       if (rates_len > count) {
-               pos = skb_put(skb, rates_len - count + 2);
-               *pos++ = WLAN_EID_EXT_SUPP_RATES;
-               *pos++ = rates_len - count;
+       sta_info_unlink(&sta);
 
-               for (i++; i < sband->n_bitrates; i++) {
-                       if (BIT(i) & rates) {
-                               int rate = sband->bitrates[i].bitrate;
-                               *pos++ = (u8) (rate / 5);
-                       }
-               }
-       }
+       rcu_read_unlock();
 
-       if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) {
-               /* 1. power capabilities */
-               pos = skb_put(skb, 4);
-               *pos++ = WLAN_EID_PWR_CAPABILITY;
-               *pos++ = 2;
-               *pos++ = 0; /* min tx power */
-               *pos++ = local->hw.conf.channel->max_power; /* max tx power */
+       sta_info_destroy(sta);
+}
 
-               /* 2. supported channels */
-               /* TODO: get this in reg domain format */
-               pos = skb_put(skb, 2 * sband->n_channels + 2);
-               *pos++ = WLAN_EID_SUPPORTED_CHANNELS;
-               *pos++ = 2 * sband->n_channels;
-               for (i = 0; i < sband->n_channels; i++) {
-                       *pos++ = ieee80211_frequency_to_channel(
-                                       sband->channels[i].center_freq);
-                       *pos++ = 1; /* one channel in the subband*/
-               }
-       }
-
-       if (ifsta->extra_ie) {
-               pos = skb_put(skb, ifsta->extra_ie_len);
-               memcpy(pos, ifsta->extra_ie, ifsta->extra_ie_len);
-       }
-
-       if (wmm && (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) {
-               pos = skb_put(skb, 9);
-               *pos++ = WLAN_EID_VENDOR_SPECIFIC;
-               *pos++ = 7; /* len */
-               *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
-               *pos++ = 0x50;
-               *pos++ = 0xf2;
-               *pos++ = 2; /* WME */
-               *pos++ = 0; /* WME info */
-               *pos++ = 1; /* WME ver */
-               *pos++ = 0;
-       }
-
-       /* wmm support is a must to HT */
-       if (wmm && (ifsta->flags & IEEE80211_STA_WMM_ENABLED) &&
-           sband->ht_info.ht_supported &&
-           (ht_add_ie = ieee80211_bss_get_ie(bss, WLAN_EID_HT_EXTRA_INFO))) {
-               struct ieee80211_ht_addt_info *ht_add_info =
-                       (struct ieee80211_ht_addt_info *)ht_add_ie;
-               u16 cap = sband->ht_info.cap;
-               __le16 tmp;
-               u32 flags = local->hw.conf.channel->flags;
-
-               switch (ht_add_info->ht_param & IEEE80211_HT_IE_CHA_SEC_OFFSET) {
-               case IEEE80211_HT_IE_CHA_SEC_ABOVE:
-                       if (flags & IEEE80211_CHAN_NO_FAT_ABOVE) {
-                               cap &= ~IEEE80211_HT_CAP_SUP_WIDTH;
-                               cap &= ~IEEE80211_HT_CAP_SGI_40;
-                       }
-                       break;
-               case IEEE80211_HT_IE_CHA_SEC_BELOW:
-                       if (flags & IEEE80211_CHAN_NO_FAT_BELOW) {
-                               cap &= ~IEEE80211_HT_CAP_SUP_WIDTH;
-                               cap &= ~IEEE80211_HT_CAP_SGI_40;
-                       }
-                       break;
-               }
-
-               tmp = cpu_to_le16(cap);
-               pos = skb_put(skb, sizeof(struct ieee80211_ht_cap)+2);
-               *pos++ = WLAN_EID_HT_CAPABILITY;
-               *pos++ = sizeof(struct ieee80211_ht_cap);
-               memset(pos, 0, sizeof(struct ieee80211_ht_cap));
-               memcpy(pos, &tmp, sizeof(u16));
-               pos += sizeof(u16);
-               /* TODO: needs a define here for << 2 */
-               *pos++ = sband->ht_info.ampdu_factor |
-                        (sband->ht_info.ampdu_density << 2);
-               memcpy(pos, sband->ht_info.supp_mcs_set, 16);
-       }
-
-       kfree(ifsta->assocreq_ies);
-       ifsta->assocreq_ies_len = (skb->data + skb->len) - ies;
-       ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_KERNEL);
-       if (ifsta->assocreq_ies)
-               memcpy(ifsta->assocreq_ies, ies, ifsta->assocreq_ies_len);
-
-       ieee80211_sta_tx(sdata, skb, 0);
-}
-
-
-static void ieee80211_send_deauth(struct ieee80211_sub_if_data *sdata,
-                                 struct ieee80211_if_sta *ifsta, u16 reason)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct sk_buff *skb;
-       struct ieee80211_mgmt *mgmt;
-
-       skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt));
-       if (!skb) {
-               printk(KERN_DEBUG "%s: failed to allocate buffer for deauth "
-                      "frame\n", sdata->dev->name);
-               return;
-       }
-       skb_reserve(skb, local->hw.extra_tx_headroom);
-
-       mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
-       memset(mgmt, 0, 24);
-       memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
-       memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
-       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                         IEEE80211_STYPE_DEAUTH);
-       skb_put(skb, 2);
-       mgmt->u.deauth.reason_code = cpu_to_le16(reason);
-
-       ieee80211_sta_tx(sdata, skb, 0);
-}
-
-
-static void ieee80211_send_disassoc(struct ieee80211_sub_if_data *sdata,
-                                   struct ieee80211_if_sta *ifsta, u16 reason)
+static int ieee80211_sta_wep_configured(struct ieee80211_sub_if_data *sdata)
 {
-       struct ieee80211_local *local = sdata->local;
-       struct sk_buff *skb;
-       struct ieee80211_mgmt *mgmt;
-
-       skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt));
-       if (!skb) {
-               printk(KERN_DEBUG "%s: failed to allocate buffer for disassoc "
-                      "frame\n", sdata->dev->name);
-               return;
-       }
-       skb_reserve(skb, local->hw.extra_tx_headroom);
-
-       mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
-       memset(mgmt, 0, 24);
-       memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
-       memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
-       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                         IEEE80211_STYPE_DISASSOC);
-       skb_put(skb, 2);
-       mgmt->u.disassoc.reason_code = cpu_to_le16(reason);
-
-       ieee80211_sta_tx(sdata, skb, 0);
+       if (!sdata || !sdata->default_key ||
+           sdata->default_key->conf.alg != ALG_WEP)
+               return 0;
+       return 1;
 }
 
-
 static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata,
                                      struct ieee80211_if_sta *ifsta)
 {
@@ -1014,849 +829,127 @@ static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_rx_bss_put(local, bss);
 
-       if ((bss_privacy == wep_privacy) || (bss_privacy == privacy_invoked))
-               return 0;
-
-       return 1;
-}
-
-
-static void ieee80211_associate(struct ieee80211_sub_if_data *sdata,
-                               struct ieee80211_if_sta *ifsta)
-{
-       DECLARE_MAC_BUF(mac);
-
-       ifsta->assoc_tries++;
-       if (ifsta->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
-               printk(KERN_DEBUG "%s: association with AP %s"
-                      " timed out\n",
-                      sdata->dev->name, print_mac(mac, ifsta->bssid));
-               ifsta->state = IEEE80211_STA_MLME_DISABLED;
-               return;
-       }
-
-       ifsta->state = IEEE80211_STA_MLME_ASSOCIATE;
-       printk(KERN_DEBUG "%s: associate with AP %s\n",
-              sdata->dev->name, print_mac(mac, ifsta->bssid));
-       if (ieee80211_privacy_mismatch(sdata, ifsta)) {
-               printk(KERN_DEBUG "%s: mismatch in privacy configuration and "
-                      "mixed-cell disabled - abort association\n", sdata->dev->name);
-               ifsta->state = IEEE80211_STA_MLME_DISABLED;
-               return;
-       }
-
-       ieee80211_send_assoc(sdata, ifsta);
-
-       mod_timer(&ifsta->timer, jiffies + IEEE80211_ASSOC_TIMEOUT);
-}
-
-
-static void ieee80211_associated(struct ieee80211_sub_if_data *sdata,
-                                struct ieee80211_if_sta *ifsta)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct sta_info *sta;
-       int disassoc;
-       DECLARE_MAC_BUF(mac);
-
-       /* TODO: start monitoring current AP signal quality and number of
-        * missed beacons. Scan other channels every now and then and search
-        * for better APs. */
-       /* TODO: remove expired BSSes */
-
-       ifsta->state = IEEE80211_STA_MLME_ASSOCIATED;
-
-       rcu_read_lock();
-
-       sta = sta_info_get(local, ifsta->bssid);
-       if (!sta) {
-               printk(KERN_DEBUG "%s: No STA entry for own AP %s\n",
-                      sdata->dev->name, print_mac(mac, ifsta->bssid));
-               disassoc = 1;
-       } else {
-               disassoc = 0;
-               if (time_after(jiffies,
-                              sta->last_rx + IEEE80211_MONITORING_INTERVAL)) {
-                       if (ifsta->flags & IEEE80211_STA_PROBEREQ_POLL) {
-                               printk(KERN_DEBUG "%s: No ProbeResp from "
-                                      "current AP %s - assume out of "
-                                      "range\n",
-                                      sdata->dev->name, print_mac(mac, ifsta->bssid));
-                               disassoc = 1;
-                               sta_info_unlink(&sta);
-                       } else
-                               ieee80211_send_probe_req(sdata, ifsta->bssid,
-                                                        local->scan_ssid,
-                                                        local->scan_ssid_len);
-                       ifsta->flags ^= IEEE80211_STA_PROBEREQ_POLL;
-               } else {
-                       ifsta->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
-                       if (time_after(jiffies, ifsta->last_probe +
-                                      IEEE80211_PROBE_INTERVAL)) {
-                               ifsta->last_probe = jiffies;
-                               ieee80211_send_probe_req(sdata, ifsta->bssid,
-                                                        ifsta->ssid,
-                                                        ifsta->ssid_len);
-                       }
-               }
-       }
-
-       rcu_read_unlock();
-
-       if (disassoc && sta)
-               sta_info_destroy(sta);
-
-       if (disassoc) {
-               ifsta->state = IEEE80211_STA_MLME_DISABLED;
-               ieee80211_set_associated(sdata, ifsta, 0);
-       } else {
-               mod_timer(&ifsta->timer, jiffies +
-                                     IEEE80211_MONITORING_INTERVAL);
-       }
-}
-
-
-static void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
-                                    u8 *ssid, size_t ssid_len)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_supported_band *sband;
-       struct sk_buff *skb;
-       struct ieee80211_mgmt *mgmt;
-       u8 *pos, *supp_rates, *esupp_rates = NULL;
-       int i;
-
-       skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200);
-       if (!skb) {
-               printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
-                      "request\n", sdata->dev->name);
-               return;
-       }
-       skb_reserve(skb, local->hw.extra_tx_headroom);
-
-       mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
-       memset(mgmt, 0, 24);
-       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                         IEEE80211_STYPE_PROBE_REQ);
-       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
-       if (dst) {
-               memcpy(mgmt->da, dst, ETH_ALEN);
-               memcpy(mgmt->bssid, dst, ETH_ALEN);
-       } else {
-               memset(mgmt->da, 0xff, ETH_ALEN);
-               memset(mgmt->bssid, 0xff, ETH_ALEN);
-       }
-       pos = skb_put(skb, 2 + ssid_len);
-       *pos++ = WLAN_EID_SSID;
-       *pos++ = ssid_len;
-       memcpy(pos, ssid, ssid_len);
-
-       supp_rates = skb_put(skb, 2);
-       supp_rates[0] = WLAN_EID_SUPP_RATES;
-       supp_rates[1] = 0;
-       sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
-
-       for (i = 0; i < sband->n_bitrates; i++) {
-               struct ieee80211_rate *rate = &sband->bitrates[i];
-               if (esupp_rates) {
-                       pos = skb_put(skb, 1);
-                       esupp_rates[1]++;
-               } else if (supp_rates[1] == 8) {
-                       esupp_rates = skb_put(skb, 3);
-                       esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES;
-                       esupp_rates[1] = 1;
-                       pos = &esupp_rates[2];
-               } else {
-                       pos = skb_put(skb, 1);
-                       supp_rates[1]++;
-               }
-               *pos = rate->bitrate / 5;
-       }
-
-       ieee80211_sta_tx(sdata, skb, 0);
-}
-
-
-static int ieee80211_sta_wep_configured(struct ieee80211_sub_if_data *sdata)
-{
-       if (!sdata || !sdata->default_key ||
-           sdata->default_key->conf.alg != ALG_WEP)
-               return 0;
-       return 1;
-}
-
-
-static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata,
-                                    struct ieee80211_if_sta *ifsta)
-{
-       printk(KERN_DEBUG "%s: authenticated\n", sdata->dev->name);
-       ifsta->flags |= IEEE80211_STA_AUTHENTICATED;
-       ieee80211_associate(sdata, ifsta);
-}
-
-
-static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
-                                    struct ieee80211_if_sta *ifsta,
-                                    struct ieee80211_mgmt *mgmt,
-                                    size_t len)
-{
-       u8 *pos;
-       struct ieee802_11_elems elems;
-
-       pos = mgmt->u.auth.variable;
-       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
-       if (!elems.challenge)
-               return;
-       ieee80211_send_auth(sdata, ifsta, 3, elems.challenge - 2,
-                           elems.challenge_len + 2, 1);
-}
-
-static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *da, u16 tid,
-                                       u8 dialog_token, u16 status, u16 policy,
-                                       u16 buf_size, u16 timeout)
-{
-       struct ieee80211_if_sta *ifsta = &sdata->u.sta;
-       struct ieee80211_local *local = sdata->local;
-       struct sk_buff *skb;
-       struct ieee80211_mgmt *mgmt;
-       u16 capab;
-
-       skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
-
-       if (!skb) {
-               printk(KERN_DEBUG "%s: failed to allocate buffer "
-                      "for addba resp frame\n", sdata->dev->name);
-               return;
-       }
-
-       skb_reserve(skb, local->hw.extra_tx_headroom);
-       mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
-       memset(mgmt, 0, 24);
-       memcpy(mgmt->da, da, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
-       if (sdata->vif.type == IEEE80211_IF_TYPE_AP)
-               memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
-       else
-               memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
-       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                         IEEE80211_STYPE_ACTION);
-
-       skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_resp));
-       mgmt->u.action.category = WLAN_CATEGORY_BACK;
-       mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP;
-       mgmt->u.action.u.addba_resp.dialog_token = dialog_token;
-
-       capab = (u16)(policy << 1);     /* bit 1 aggregation policy */
-       capab |= (u16)(tid << 2);       /* bit 5:2 TID number */
-       capab |= (u16)(buf_size << 6);  /* bit 15:6 max size of aggregation */
-
-       mgmt->u.action.u.addba_resp.capab = cpu_to_le16(capab);
-       mgmt->u.action.u.addba_resp.timeout = cpu_to_le16(timeout);
-       mgmt->u.action.u.addba_resp.status = cpu_to_le16(status);
-
-       ieee80211_sta_tx(sdata, skb, 0);
-
-       return;
-}
-
-void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, const u8 *da,
-                               u16 tid, u8 dialog_token, u16 start_seq_num,
-                               u16 agg_size, u16 timeout)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_if_sta *ifsta = &sdata->u.sta;
-       struct sk_buff *skb;
-       struct ieee80211_mgmt *mgmt;
-       u16 capab;
-
-       skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
-
-       if (!skb) {
-               printk(KERN_ERR "%s: failed to allocate buffer "
-                               "for addba request frame\n", sdata->dev->name);
-               return;
-       }
-       skb_reserve(skb, local->hw.extra_tx_headroom);
-       mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
-       memset(mgmt, 0, 24);
-       memcpy(mgmt->da, da, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
-       if (sdata->vif.type == IEEE80211_IF_TYPE_AP)
-               memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
-       else
-               memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
-
-       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                         IEEE80211_STYPE_ACTION);
-
-       skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_req));
-
-       mgmt->u.action.category = WLAN_CATEGORY_BACK;
-       mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ;
-
-       mgmt->u.action.u.addba_req.dialog_token = dialog_token;
-       capab = (u16)(1 << 1);          /* bit 1 aggregation policy */
-       capab |= (u16)(tid << 2);       /* bit 5:2 TID number */
-       capab |= (u16)(agg_size << 6);  /* bit 15:6 max size of aggergation */
-
-       mgmt->u.action.u.addba_req.capab = cpu_to_le16(capab);
-
-       mgmt->u.action.u.addba_req.timeout = cpu_to_le16(timeout);
-       mgmt->u.action.u.addba_req.start_seq_num =
-                                       cpu_to_le16(start_seq_num << 4);
-
-       ieee80211_sta_tx(sdata, skb, 0);
-}
-
-static void ieee80211_sta_process_addba_request(struct ieee80211_local *local,
-                                               struct ieee80211_mgmt *mgmt,
-                                               size_t len)
-{
-       struct ieee80211_hw *hw = &local->hw;
-       struct ieee80211_conf *conf = &hw->conf;
-       struct sta_info *sta;
-       struct tid_ampdu_rx *tid_agg_rx;
-       u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status;
-       u8 dialog_token;
-       int ret = -EOPNOTSUPP;
-       DECLARE_MAC_BUF(mac);
-
-       rcu_read_lock();
-
-       sta = sta_info_get(local, mgmt->sa);
-       if (!sta) {
-               rcu_read_unlock();
-               return;
-       }
-
-       /* extract session parameters from addba request frame */
-       dialog_token = mgmt->u.action.u.addba_req.dialog_token;
-       timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
-       start_seq_num =
-               le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
-
-       capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
-       ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
-       tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
-       buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
-
-       status = WLAN_STATUS_REQUEST_DECLINED;
-
-       /* sanity check for incoming parameters:
-        * check if configuration can support the BA policy
-        * and if buffer size does not exceeds max value */
-       if (((ba_policy != 1)
-               && (!(conf->ht_conf.cap & IEEE80211_HT_CAP_DELAY_BA)))
-               || (buf_size > IEEE80211_MAX_AMPDU_BUF)) {
-               status = WLAN_STATUS_INVALID_QOS_PARAM;
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               if (net_ratelimit())
-                       printk(KERN_DEBUG "AddBA Req with bad params from "
-                               "%s on tid %u. policy %d, buffer size %d\n",
-                               print_mac(mac, mgmt->sa), tid, ba_policy,
-                               buf_size);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-               goto end_no_lock;
-       }
-       /* determine default buffer size */
-       if (buf_size == 0) {
-               struct ieee80211_supported_band *sband;
-
-               sband = local->hw.wiphy->bands[conf->channel->band];
-               buf_size = IEEE80211_MIN_AMPDU_BUF;
-               buf_size = buf_size << sband->ht_info.ampdu_factor;
-       }
-
-
-       /* examine state machine */
-       spin_lock_bh(&sta->lock);
-
-       if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_IDLE) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               if (net_ratelimit())
-                       printk(KERN_DEBUG "unexpected AddBA Req from "
-                               "%s on tid %u\n",
-                               print_mac(mac, mgmt->sa), tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-               goto end;
-       }
-
-       /* prepare A-MPDU MLME for Rx aggregation */
-       sta->ampdu_mlme.tid_rx[tid] =
-                       kmalloc(sizeof(struct tid_ampdu_rx), GFP_ATOMIC);
-       if (!sta->ampdu_mlme.tid_rx[tid]) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               if (net_ratelimit())
-                       printk(KERN_ERR "allocate rx mlme to tid %d failed\n",
-                                       tid);
-#endif
-               goto end;
-       }
-       /* rx timer */
-       sta->ampdu_mlme.tid_rx[tid]->session_timer.function =
-                               sta_rx_agg_session_timer_expired;
-       sta->ampdu_mlme.tid_rx[tid]->session_timer.data =
-                               (unsigned long)&sta->timer_to_tid[tid];
-       init_timer(&sta->ampdu_mlme.tid_rx[tid]->session_timer);
-
-       tid_agg_rx = sta->ampdu_mlme.tid_rx[tid];
-
-       /* prepare reordering buffer */
-       tid_agg_rx->reorder_buf =
-               kmalloc(buf_size * sizeof(struct sk_buff *), GFP_ATOMIC);
-       if (!tid_agg_rx->reorder_buf) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               if (net_ratelimit())
-                       printk(KERN_ERR "can not allocate reordering buffer "
-                              "to tid %d\n", tid);
-#endif
-               kfree(sta->ampdu_mlme.tid_rx[tid]);
-               goto end;
-       }
-       memset(tid_agg_rx->reorder_buf, 0,
-               buf_size * sizeof(struct sk_buff *));
-
-       if (local->ops->ampdu_action)
-               ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_START,
-                                              sta->addr, tid, &start_seq_num);
-#ifdef CONFIG_MAC80211_HT_DEBUG
-       printk(KERN_DEBUG "Rx A-MPDU request on tid %d result %d\n", tid, ret);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-
-       if (ret) {
-               kfree(tid_agg_rx->reorder_buf);
-               kfree(tid_agg_rx);
-               sta->ampdu_mlme.tid_rx[tid] = NULL;
-               goto end;
-       }
-
-       /* change state and send addba resp */
-       sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_OPERATIONAL;
-       tid_agg_rx->dialog_token = dialog_token;
-       tid_agg_rx->ssn = start_seq_num;
-       tid_agg_rx->head_seq_num = start_seq_num;
-       tid_agg_rx->buf_size = buf_size;
-       tid_agg_rx->timeout = timeout;
-       tid_agg_rx->stored_mpdu_num = 0;
-       status = WLAN_STATUS_SUCCESS;
-end:
-       spin_unlock_bh(&sta->lock);
-
-end_no_lock:
-       ieee80211_send_addba_resp(sta->sdata, sta->addr, tid,
-                                 dialog_token, status, 1, buf_size, timeout);
-       rcu_read_unlock();
-}
-
-static void ieee80211_sta_process_addba_resp(struct ieee80211_local *local,
-                                            struct ieee80211_mgmt *mgmt,
-                                            size_t len)
-{
-       struct ieee80211_hw *hw = &local->hw;
-       struct sta_info *sta;
-       u16 capab;
-       u16 tid;
-       u8 *state;
-
-       rcu_read_lock();
-
-       sta = sta_info_get(local, mgmt->sa);
-       if (!sta) {
-               rcu_read_unlock();
-               return;
-       }
-
-       capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
-       tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
-
-       state = &sta->ampdu_mlme.tid_state_tx[tid];
-
-       spin_lock_bh(&sta->lock);
-
-       if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
-               spin_unlock_bh(&sta->lock);
-               goto addba_resp_exit;
-       }
-
-       if (mgmt->u.action.u.addba_resp.dialog_token !=
-               sta->ampdu_mlme.tid_tx[tid]->dialog_token) {
-               spin_unlock_bh(&sta->lock);
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               printk(KERN_DEBUG "wrong addBA response token, tid %d\n", tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-               goto addba_resp_exit;
-       }
-
-       del_timer_sync(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
-#ifdef CONFIG_MAC80211_HT_DEBUG
-       printk(KERN_DEBUG "switched off addBA timer for tid %d \n", tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-       if (le16_to_cpu(mgmt->u.action.u.addba_resp.status)
-                       == WLAN_STATUS_SUCCESS) {
-               *state |= HT_ADDBA_RECEIVED_MSK;
-               sta->ampdu_mlme.addba_req_num[tid] = 0;
-
-               if (*state == HT_AGG_STATE_OPERATIONAL)
-                       ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
-
-               spin_unlock_bh(&sta->lock);
-       } else {
-               sta->ampdu_mlme.addba_req_num[tid]++;
-               /* this will allow the state check in stop_BA_session */
-               *state = HT_AGG_STATE_OPERATIONAL;
-               spin_unlock_bh(&sta->lock);
-               ieee80211_stop_tx_ba_session(hw, sta->addr, tid,
-                                            WLAN_BACK_INITIATOR);
-       }
-
-addba_resp_exit:
-       rcu_read_unlock();
-}
-
-void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, const u8 *da, u16 tid,
-                         u16 initiator, u16 reason_code)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_if_sta *ifsta = &sdata->u.sta;
-       struct sk_buff *skb;
-       struct ieee80211_mgmt *mgmt;
-       u16 params;
-
-       skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
-
-       if (!skb) {
-               printk(KERN_ERR "%s: failed to allocate buffer "
-                                       "for delba frame\n", sdata->dev->name);
-               return;
-       }
-
-       skb_reserve(skb, local->hw.extra_tx_headroom);
-       mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
-       memset(mgmt, 0, 24);
-       memcpy(mgmt->da, da, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
-       if (sdata->vif.type == IEEE80211_IF_TYPE_AP)
-               memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
-       else
-               memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
-       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                         IEEE80211_STYPE_ACTION);
-
-       skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba));
-
-       mgmt->u.action.category = WLAN_CATEGORY_BACK;
-       mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA;
-       params = (u16)(initiator << 11);        /* bit 11 initiator */
-       params |= (u16)(tid << 12);             /* bit 15:12 TID number */
-
-       mgmt->u.action.u.delba.params = cpu_to_le16(params);
-       mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code);
-
-       ieee80211_sta_tx(sdata, skb, 0);
-}
-
-void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct sk_buff *skb;
-       struct ieee80211_bar *bar;
-       u16 bar_control = 0;
-
-       skb = dev_alloc_skb(sizeof(*bar) + local->hw.extra_tx_headroom);
-       if (!skb) {
-               printk(KERN_ERR "%s: failed to allocate buffer for "
-                       "bar frame\n", sdata->dev->name);
-               return;
-       }
-       skb_reserve(skb, local->hw.extra_tx_headroom);
-       bar = (struct ieee80211_bar *)skb_put(skb, sizeof(*bar));
-       memset(bar, 0, sizeof(*bar));
-       bar->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
-                                        IEEE80211_STYPE_BACK_REQ);
-       memcpy(bar->ra, ra, ETH_ALEN);
-       memcpy(bar->ta, sdata->dev->dev_addr, ETH_ALEN);
-       bar_control |= (u16)IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL;
-       bar_control |= (u16)IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA;
-       bar_control |= (u16)(tid << 12);
-       bar->control = cpu_to_le16(bar_control);
-       bar->start_seq_num = cpu_to_le16(ssn);
-
-       ieee80211_sta_tx(sdata, skb, 0);
+       if ((bss_privacy == wep_privacy) || (bss_privacy == privacy_invoked))
+               return 0;
+
+       return 1;
 }
 
-void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid,
-                                       u16 initiator, u16 reason)
+static void ieee80211_associate(struct ieee80211_sub_if_data *sdata,
+                               struct ieee80211_if_sta *ifsta)
 {
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_hw *hw = &local->hw;
-       struct sta_info *sta;
-       int ret, i;
        DECLARE_MAC_BUF(mac);
 
-       rcu_read_lock();
-
-       sta = sta_info_get(local, ra);
-       if (!sta) {
-               rcu_read_unlock();
+       ifsta->assoc_tries++;
+       if (ifsta->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
+               printk(KERN_DEBUG "%s: association with AP %s"
+                      " timed out\n",
+                      sdata->dev->name, print_mac(mac, ifsta->bssid));
+               ifsta->state = IEEE80211_STA_MLME_DISABLED;
                return;
        }
 
-       /* check if TID is in operational state */
-       spin_lock_bh(&sta->lock);
-       if (sta->ampdu_mlme.tid_state_rx[tid]
-                               != HT_AGG_STATE_OPERATIONAL) {
-               spin_unlock_bh(&sta->lock);
-               rcu_read_unlock();
+       ifsta->state = IEEE80211_STA_MLME_ASSOCIATE;
+       printk(KERN_DEBUG "%s: associate with AP %s\n",
+              sdata->dev->name, print_mac(mac, ifsta->bssid));
+       if (ieee80211_privacy_mismatch(sdata, ifsta)) {
+               printk(KERN_DEBUG "%s: mismatch in privacy configuration and "
+                      "mixed-cell disabled - abort association\n", sdata->dev->name);
+               ifsta->state = IEEE80211_STA_MLME_DISABLED;
                return;
        }
-       sta->ampdu_mlme.tid_state_rx[tid] =
-               HT_AGG_STATE_REQ_STOP_BA_MSK |
-               (initiator << HT_AGG_STATE_INITIATOR_SHIFT);
-       spin_unlock_bh(&sta->lock);
-
-       /* stop HW Rx aggregation. ampdu_action existence
-        * already verified in session init so we add the BUG_ON */
-       BUG_ON(!local->ops->ampdu_action);
-
-#ifdef CONFIG_MAC80211_HT_DEBUG
-       printk(KERN_DEBUG "Rx BA session stop requested for %s tid %u\n",
-                               print_mac(mac, ra), tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-
-       ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_STOP,
-                                       ra, tid, NULL);
-       if (ret)
-               printk(KERN_DEBUG "HW problem - can not stop rx "
-                               "aggregation for tid %d\n", tid);
-
-       /* shutdown timer has not expired */
-       if (initiator != WLAN_BACK_TIMER)
-               del_timer_sync(&sta->ampdu_mlme.tid_rx[tid]->session_timer);
-
-       /* check if this is a self generated aggregation halt */
-       if (initiator == WLAN_BACK_RECIPIENT || initiator == WLAN_BACK_TIMER)
-               ieee80211_send_delba(sdata, ra, tid, 0, reason);
-
-       /* free the reordering buffer */
-       for (i = 0; i < sta->ampdu_mlme.tid_rx[tid]->buf_size; i++) {
-               if (sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]) {
-                       /* release the reordered frames */
-                       dev_kfree_skb(sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]);
-                       sta->ampdu_mlme.tid_rx[tid]->stored_mpdu_num--;
-                       sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i] = NULL;
-               }
-       }
-       /* free resources */
-       kfree(sta->ampdu_mlme.tid_rx[tid]->reorder_buf);
-       kfree(sta->ampdu_mlme.tid_rx[tid]);
-       sta->ampdu_mlme.tid_rx[tid] = NULL;
-       sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_IDLE;
 
-       rcu_read_unlock();
+       ieee80211_send_assoc(sdata, ifsta);
+
+       mod_timer(&ifsta->timer, jiffies + IEEE80211_ASSOC_TIMEOUT);
 }
 
 
-static void ieee80211_sta_process_delba(struct ieee80211_sub_if_data *sdata,
-                       struct ieee80211_mgmt *mgmt, size_t len)
+static void ieee80211_associated(struct ieee80211_sub_if_data *sdata,
+                                struct ieee80211_if_sta *ifsta)
 {
        struct ieee80211_local *local = sdata->local;
        struct sta_info *sta;
-       u16 tid, params;
-       u16 initiator;
+       int disassoc;
        DECLARE_MAC_BUF(mac);
 
-       rcu_read_lock();
-
-       sta = sta_info_get(local, mgmt->sa);
-       if (!sta) {
-               rcu_read_unlock();
-               return;
-       }
-
-       params = le16_to_cpu(mgmt->u.action.u.delba.params);
-       tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12;
-       initiator = (params & IEEE80211_DELBA_PARAM_INITIATOR_MASK) >> 11;
-
-#ifdef CONFIG_MAC80211_HT_DEBUG
-       if (net_ratelimit())
-               printk(KERN_DEBUG "delba from %s (%s) tid %d reason code %d\n",
-                       print_mac(mac, mgmt->sa),
-                       initiator ? "initiator" : "recipient", tid,
-                       mgmt->u.action.u.delba.reason_code);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-
-       if (initiator == WLAN_BACK_INITIATOR)
-               ieee80211_sta_stop_rx_ba_session(sdata, sta->addr, tid,
-                                                WLAN_BACK_INITIATOR, 0);
-       else { /* WLAN_BACK_RECIPIENT */
-               spin_lock_bh(&sta->lock);
-               sta->ampdu_mlme.tid_state_tx[tid] =
-                               HT_AGG_STATE_OPERATIONAL;
-               spin_unlock_bh(&sta->lock);
-               ieee80211_stop_tx_ba_session(&local->hw, sta->addr, tid,
-                                            WLAN_BACK_RECIPIENT);
-       }
-       rcu_read_unlock();
-}
+       /* TODO: start monitoring current AP signal quality and number of
+        * missed beacons. Scan other channels every now and then and search
+        * for better APs. */
+       /* TODO: remove expired BSSes */
 
-/*
- * After sending add Block Ack request we activated a timer until
- * add Block Ack response will arrive from the recipient.
- * If this timer expires sta_addba_resp_timer_expired will be executed.
- */
-void sta_addba_resp_timer_expired(unsigned long data)
-{
-       /* not an elegant detour, but there is no choice as the timer passes
-        * only one argument, and both sta_info and TID are needed, so init
-        * flow in sta_info_create gives the TID as data, while the timer_to_id
-        * array gives the sta through container_of */
-       u16 tid = *(u8 *)data;
-       struct sta_info *temp_sta = container_of((void *)data,
-               struct sta_info, timer_to_tid[tid]);
-
-       struct ieee80211_local *local = temp_sta->local;
-       struct ieee80211_hw *hw = &local->hw;
-       struct sta_info *sta;
-       u8 *state;
+       ifsta->state = IEEE80211_STA_MLME_ASSOCIATED;
 
        rcu_read_lock();
 
-       sta = sta_info_get(local, temp_sta->addr);
+       sta = sta_info_get(local, ifsta->bssid);
        if (!sta) {
-               rcu_read_unlock();
-               return;
-       }
-
-       state = &sta->ampdu_mlme.tid_state_tx[tid];
-       /* check if the TID waits for addBA response */
-       spin_lock_bh(&sta->lock);
-       if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
-               spin_unlock_bh(&sta->lock);
-               *state = HT_AGG_STATE_IDLE;
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               printk(KERN_DEBUG "timer expired on tid %d but we are not "
-                               "expecting addBA response there", tid);
-#endif
-               goto timer_expired_exit;
+               printk(KERN_DEBUG "%s: No STA entry for own AP %s\n",
+                      sdata->dev->name, print_mac(mac, ifsta->bssid));
+               disassoc = 1;
+       } else {
+               disassoc = 0;
+               if (time_after(jiffies,
+                              sta->last_rx + IEEE80211_MONITORING_INTERVAL)) {
+                       if (ifsta->flags & IEEE80211_STA_PROBEREQ_POLL) {
+                               printk(KERN_DEBUG "%s: No ProbeResp from "
+                                      "current AP %s - assume out of "
+                                      "range\n",
+                                      sdata->dev->name, print_mac(mac, ifsta->bssid));
+                               disassoc = 1;
+                       } else
+                               ieee80211_send_probe_req(sdata, ifsta->bssid,
+                                                        local->scan_ssid,
+                                                        local->scan_ssid_len);
+                       ifsta->flags ^= IEEE80211_STA_PROBEREQ_POLL;
+               } else {
+                       ifsta->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
+                       if (time_after(jiffies, ifsta->last_probe +
+                                      IEEE80211_PROBE_INTERVAL)) {
+                               ifsta->last_probe = jiffies;
+                               ieee80211_send_probe_req(sdata, ifsta->bssid,
+                                                        ifsta->ssid,
+                                                        ifsta->ssid_len);
+                       }
+               }
        }
 
-#ifdef CONFIG_MAC80211_HT_DEBUG
-       printk(KERN_DEBUG "addBA response timer expired on tid %d\n", tid);
-#endif
-
-       /* go through the state check in stop_BA_session */
-       *state = HT_AGG_STATE_OPERATIONAL;
-       spin_unlock_bh(&sta->lock);
-       ieee80211_stop_tx_ba_session(hw, temp_sta->addr, tid,
-                                    WLAN_BACK_INITIATOR);
-
-timer_expired_exit:
        rcu_read_unlock();
-}
 
-/*
- * After accepting the AddBA Request we activated a timer,
- * resetting it after each frame that arrives from the originator.
- * if this timer expires ieee80211_sta_stop_rx_ba_session will be executed.
- */
-static void sta_rx_agg_session_timer_expired(unsigned long data)
-{
-       /* not an elegant detour, but there is no choice as the timer passes
-        * only one argument, and various sta_info are needed here, so init
-        * flow in sta_info_create gives the TID as data, while the timer_to_id
-        * array gives the sta through container_of */
-       u8 *ptid = (u8 *)data;
-       u8 *timer_to_id = ptid - *ptid;
-       struct sta_info *sta = container_of(timer_to_id, struct sta_info,
-                                        timer_to_tid[0]);
-
-#ifdef CONFIG_MAC80211_HT_DEBUG
-       printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid);
-#endif
-       ieee80211_sta_stop_rx_ba_session(sta->sdata, sta->addr,
-                                        (u16)*ptid, WLAN_BACK_TIMER,
-                                        WLAN_REASON_QSTA_TIMEOUT);
+       if (disassoc)
+               ieee80211_set_disassoc(sdata, ifsta, true, true,
+                                       WLAN_REASON_PREV_AUTH_NOT_VALID);
+       else
+               mod_timer(&ifsta->timer, jiffies +
+                                     IEEE80211_MONITORING_INTERVAL);
 }
 
-void ieee80211_sta_tear_down_BA_sessions(struct ieee80211_sub_if_data *sdata, u8 *addr)
-{
-       struct ieee80211_local *local = sdata->local;
-       int i;
 
-       for (i = 0; i <  STA_TID_NUM; i++) {
-               ieee80211_stop_tx_ba_session(&local->hw, addr, i,
-                                            WLAN_BACK_INITIATOR);
-               ieee80211_sta_stop_rx_ba_session(sdata, addr, i,
-                                                WLAN_BACK_RECIPIENT,
-                                                WLAN_REASON_QSTA_LEAVE_QBSS);
-       }
+static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata,
+                                    struct ieee80211_if_sta *ifsta)
+{
+       printk(KERN_DEBUG "%s: authenticated\n", sdata->dev->name);
+       ifsta->flags |= IEEE80211_STA_AUTHENTICATED;
+       ieee80211_associate(sdata, ifsta);
 }
 
-static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata,
-                                       struct ieee80211_msrment_ie *request_ie,
-                                       const u8 *da, const u8 *bssid,
-                                       u8 dialog_token)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct sk_buff *skb;
-       struct ieee80211_mgmt *msr_report;
 
-       skb = dev_alloc_skb(sizeof(*msr_report) + local->hw.extra_tx_headroom +
-                               sizeof(struct ieee80211_msrment_ie));
+static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
+                                    struct ieee80211_if_sta *ifsta,
+                                    struct ieee80211_mgmt *mgmt,
+                                    size_t len)
+{
+       u8 *pos;
+       struct ieee802_11_elems elems;
 
-       if (!skb) {
-               printk(KERN_ERR "%s: failed to allocate buffer for "
-                               "measurement report frame\n", sdata->dev->name);
+       pos = mgmt->u.auth.variable;
+       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
+       if (!elems.challenge)
                return;
-       }
-
-       skb_reserve(skb, local->hw.extra_tx_headroom);
-       msr_report = (struct ieee80211_mgmt *)skb_put(skb, 24);
-       memset(msr_report, 0, 24);
-       memcpy(msr_report->da, da, ETH_ALEN);
-       memcpy(msr_report->sa, sdata->dev->dev_addr, ETH_ALEN);
-       memcpy(msr_report->bssid, bssid, ETH_ALEN);
-       msr_report->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                               IEEE80211_STYPE_ACTION);
-
-       skb_put(skb, 1 + sizeof(msr_report->u.action.u.measurement));
-       msr_report->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
-       msr_report->u.action.u.measurement.action_code =
-                               WLAN_ACTION_SPCT_MSR_RPRT;
-       msr_report->u.action.u.measurement.dialog_token = dialog_token;
-
-       msr_report->u.action.u.measurement.element_id = WLAN_EID_MEASURE_REPORT;
-       msr_report->u.action.u.measurement.length =
-                       sizeof(struct ieee80211_msrment_ie);
-
-       memset(&msr_report->u.action.u.measurement.msr_elem, 0,
-               sizeof(struct ieee80211_msrment_ie));
-       msr_report->u.action.u.measurement.msr_elem.token = request_ie->token;
-       msr_report->u.action.u.measurement.msr_elem.mode |=
-                       IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED;
-       msr_report->u.action.u.measurement.msr_elem.type = request_ie->type;
-
-       ieee80211_sta_tx(sdata, skb, 0);
-}
-
-static void ieee80211_sta_process_measurement_req(struct ieee80211_sub_if_data *sdata,
-                                               struct ieee80211_mgmt *mgmt,
-                                               size_t len)
-{
-       /*
-        * Ignoring measurement request is spec violation.
-        * Mandatory measurements must be reported optional
-        * measurements might be refused or reported incapable
-        * For now just refuse
-        * TODO: Answer basic measurement as unmeasured
-        */
-       ieee80211_send_refuse_measurement_request(sdata,
-                       &mgmt->u.action.u.measurement.msr_elem,
-                       mgmt->sa, mgmt->bssid,
-                       mgmt->u.action.u.measurement.dialog_token);
+       ieee80211_send_auth(sdata, ifsta, 3, elems.challenge - 2,
+                           elems.challenge_len + 2, 1);
 }
 
-
 static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
                                   struct ieee80211_if_sta *ifsta,
                                   struct ieee80211_mgmt *mgmt,
@@ -1977,7 +1070,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
                                      IEEE80211_RETRY_AUTH_INTERVAL);
        }
 
-       ieee80211_set_disassoc(sdata, ifsta, 1);
+       ieee80211_set_disassoc(sdata, ifsta, true, false, 0);
        ifsta->flags &= ~IEEE80211_STA_AUTHENTICATED;
 }
 
@@ -2007,7 +1100,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
                                      IEEE80211_RETRY_AUTH_INTERVAL);
        }
 
-       ieee80211_set_disassoc(sdata, ifsta, 0);
+       ieee80211_set_disassoc(sdata, ifsta, false, false, 0);
 }
 
 
@@ -2143,264 +1236,67 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                if (rate > 110)
                        have_higher_than_11mbit = true;
 
-               for (j = 0; j < sband->n_bitrates; j++) {
-                       if (sband->bitrates[j].bitrate == rate)
-                               rates |= BIT(j);
-                       if (elems.supp_rates[i] & 0x80)
-                               basic_rates |= BIT(j);
-               }
-       }
-
-       for (i = 0; i < elems.ext_supp_rates_len; i++) {
-               int rate = (elems.ext_supp_rates[i] & 0x7f) * 5;
-
-               if (rate > 110)
-                       have_higher_than_11mbit = true;
-
-               for (j = 0; j < sband->n_bitrates; j++) {
-                       if (sband->bitrates[j].bitrate == rate)
-                               rates |= BIT(j);
-                       if (elems.ext_supp_rates[i] & 0x80)
-                               basic_rates |= BIT(j);
-               }
-       }
-
-       sta->supp_rates[local->hw.conf.channel->band] = rates;
-       sdata->basic_rates = basic_rates;
-
-       /* cf. IEEE 802.11 9.2.12 */
-       if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
-           have_higher_than_11mbit)
-               sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
-       else
-               sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
-
-       if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param &&
-           (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) {
-               struct ieee80211_ht_bss_info bss_info;
-               ieee80211_ht_cap_ie_to_ht_info(
-                               (struct ieee80211_ht_cap *)
-                               elems.ht_cap_elem, &sta->ht_info);
-               ieee80211_ht_addt_info_ie_to_ht_bss_info(
-                               (struct ieee80211_ht_addt_info *)
-                               elems.ht_info_elem, &bss_info);
-               ieee80211_handle_ht(local, 1, &sta->ht_info, &bss_info);
-       }
-
-       rate_control_rate_init(sta, local);
-
-       if (elems.wmm_param) {
-               set_sta_flags(sta, WLAN_STA_WME);
-               rcu_read_unlock();
-               ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param,
-                                        elems.wmm_param_len);
-       } else
-               rcu_read_unlock();
-
-       /* set AID and assoc capability,
-        * ieee80211_set_associated() will tell the driver */
-       bss_conf->aid = aid;
-       bss_conf->assoc_capability = capab_info;
-       ieee80211_set_associated(sdata, ifsta, 1);
-
-       ieee80211_associated(sdata, ifsta);
-}
-
-
-/* Caller must hold local->sta_bss_lock */
-static void __ieee80211_rx_bss_hash_add(struct ieee80211_local *local,
-                                       struct ieee80211_sta_bss *bss)
-{
-       u8 hash_idx;
-
-       if (bss_mesh_cfg(bss))
-               hash_idx = mesh_id_hash(bss_mesh_id(bss),
-                                       bss_mesh_id_len(bss));
-       else
-               hash_idx = STA_HASH(bss->bssid);
-
-       bss->hnext = local->sta_bss_hash[hash_idx];
-       local->sta_bss_hash[hash_idx] = bss;
-}
-
-
-/* Caller must hold local->sta_bss_lock */
-static void __ieee80211_rx_bss_hash_del(struct ieee80211_local *local,
-                                       struct ieee80211_sta_bss *bss)
-{
-       struct ieee80211_sta_bss *b, *prev = NULL;
-       b = local->sta_bss_hash[STA_HASH(bss->bssid)];
-       while (b) {
-               if (b == bss) {
-                       if (!prev)
-                               local->sta_bss_hash[STA_HASH(bss->bssid)] =
-                                       bss->hnext;
-                       else
-                               prev->hnext = bss->hnext;
-                       break;
-               }
-               prev = b;
-               b = b->hnext;
-       }
-}
-
-
-static struct ieee80211_sta_bss *
-ieee80211_rx_bss_add(struct ieee80211_sub_if_data *sdata, u8 *bssid, int freq,
-                    u8 *ssid, u8 ssid_len)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_sta_bss *bss;
-
-       bss = kzalloc(sizeof(*bss), GFP_ATOMIC);
-       if (!bss)
-               return NULL;
-       atomic_inc(&bss->users);
-       atomic_inc(&bss->users);
-       memcpy(bss->bssid, bssid, ETH_ALEN);
-       bss->freq = freq;
-       if (ssid && ssid_len <= IEEE80211_MAX_SSID_LEN) {
-               memcpy(bss->ssid, ssid, ssid_len);
-               bss->ssid_len = ssid_len;
-       }
-
-       spin_lock_bh(&local->sta_bss_lock);
-       /* TODO: order by RSSI? */
-       list_add_tail(&bss->list, &local->sta_bss_list);
-       __ieee80211_rx_bss_hash_add(local, bss);
-       spin_unlock_bh(&local->sta_bss_lock);
-       return bss;
-}
-
-static struct ieee80211_sta_bss *
-ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq,
-                    u8 *ssid, u8 ssid_len)
-{
-       struct ieee80211_sta_bss *bss;
-
-       spin_lock_bh(&local->sta_bss_lock);
-       bss = local->sta_bss_hash[STA_HASH(bssid)];
-       while (bss) {
-               if (!bss_mesh_cfg(bss) &&
-                   !memcmp(bss->bssid, bssid, ETH_ALEN) &&
-                   bss->freq == freq &&
-                   bss->ssid_len == ssid_len &&
-                   (ssid_len == 0 || !memcmp(bss->ssid, ssid, ssid_len))) {
-                       atomic_inc(&bss->users);
-                       break;
-               }
-               bss = bss->hnext;
-       }
-       spin_unlock_bh(&local->sta_bss_lock);
-       return bss;
-}
-
-#ifdef CONFIG_MAC80211_MESH
-static struct ieee80211_sta_bss *
-ieee80211_rx_mesh_bss_get(struct ieee80211_local *local, u8 *mesh_id, int mesh_id_len,
-                         u8 *mesh_cfg, int freq)
-{
-       struct ieee80211_sta_bss *bss;
-
-       spin_lock_bh(&local->sta_bss_lock);
-       bss = local->sta_bss_hash[mesh_id_hash(mesh_id, mesh_id_len)];
-       while (bss) {
-               if (bss_mesh_cfg(bss) &&
-                   !memcmp(bss_mesh_cfg(bss), mesh_cfg, MESH_CFG_CMP_LEN) &&
-                   bss->freq == freq &&
-                   mesh_id_len == bss->mesh_id_len &&
-                   (mesh_id_len == 0 || !memcmp(bss->mesh_id, mesh_id,
-                                                mesh_id_len))) {
-                       atomic_inc(&bss->users);
-                       break;
-               }
-               bss = bss->hnext;
-       }
-       spin_unlock_bh(&local->sta_bss_lock);
-       return bss;
-}
-
-static struct ieee80211_sta_bss *
-ieee80211_rx_mesh_bss_add(struct ieee80211_local *local, u8 *mesh_id, int mesh_id_len,
-                         u8 *mesh_cfg, int mesh_config_len, int freq)
-{
-       struct ieee80211_sta_bss *bss;
-
-       if (mesh_config_len != MESH_CFG_LEN)
-               return NULL;
-
-       bss = kzalloc(sizeof(*bss), GFP_ATOMIC);
-       if (!bss)
-               return NULL;
-
-       bss->mesh_cfg = kmalloc(MESH_CFG_CMP_LEN, GFP_ATOMIC);
-       if (!bss->mesh_cfg) {
-               kfree(bss);
-               return NULL;
-       }
-
-       if (mesh_id_len && mesh_id_len <= IEEE80211_MAX_MESH_ID_LEN) {
-               bss->mesh_id = kmalloc(mesh_id_len, GFP_ATOMIC);
-               if (!bss->mesh_id) {
-                       kfree(bss->mesh_cfg);
-                       kfree(bss);
-                       return NULL;
+               for (j = 0; j < sband->n_bitrates; j++) {
+                       if (sband->bitrates[j].bitrate == rate)
+                               rates |= BIT(j);
+                       if (elems.supp_rates[i] & 0x80)
+                               basic_rates |= BIT(j);
                }
-               memcpy(bss->mesh_id, mesh_id, mesh_id_len);
        }
 
-       atomic_inc(&bss->users);
-       atomic_inc(&bss->users);
-       memcpy(bss->mesh_cfg, mesh_cfg, MESH_CFG_CMP_LEN);
-       bss->mesh_id_len = mesh_id_len;
-       bss->freq = freq;
-       spin_lock_bh(&local->sta_bss_lock);
-       /* TODO: order by RSSI? */
-       list_add_tail(&bss->list, &local->sta_bss_list);
-       __ieee80211_rx_bss_hash_add(local, bss);
-       spin_unlock_bh(&local->sta_bss_lock);
-       return bss;
-}
-#endif
-
-static void ieee80211_rx_bss_free(struct ieee80211_sta_bss *bss)
-{
-       kfree(bss->ies);
-       kfree(bss_mesh_id(bss));
-       kfree(bss_mesh_cfg(bss));
-       kfree(bss);
-}
+       for (i = 0; i < elems.ext_supp_rates_len; i++) {
+               int rate = (elems.ext_supp_rates[i] & 0x7f) * 5;
 
+               if (rate > 110)
+                       have_higher_than_11mbit = true;
 
-static void ieee80211_rx_bss_put(struct ieee80211_local *local,
-                                struct ieee80211_sta_bss *bss)
-{
-       local_bh_disable();
-       if (!atomic_dec_and_lock(&bss->users, &local->sta_bss_lock)) {
-               local_bh_enable();
-               return;
+               for (j = 0; j < sband->n_bitrates; j++) {
+                       if (sband->bitrates[j].bitrate == rate)
+                               rates |= BIT(j);
+                       if (elems.ext_supp_rates[i] & 0x80)
+                               basic_rates |= BIT(j);
+               }
        }
 
-       __ieee80211_rx_bss_hash_del(local, bss);
-       list_del(&bss->list);
-       spin_unlock_bh(&local->sta_bss_lock);
-       ieee80211_rx_bss_free(bss);
-}
+       sta->supp_rates[local->hw.conf.channel->band] = rates;
+       sdata->basic_rates = basic_rates;
+
+       /* cf. IEEE 802.11 9.2.12 */
+       if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
+           have_higher_than_11mbit)
+               sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
+       else
+               sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
 
+       if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param &&
+           (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) {
+               struct ieee80211_ht_bss_info bss_info;
+               ieee80211_ht_cap_ie_to_ht_info(
+                               (struct ieee80211_ht_cap *)
+                               elems.ht_cap_elem, &sta->ht_info);
+               ieee80211_ht_addt_info_ie_to_ht_bss_info(
+                               (struct ieee80211_ht_addt_info *)
+                               elems.ht_info_elem, &bss_info);
+               ieee80211_handle_ht(local, 1, &sta->ht_info, &bss_info);
+       }
 
-void ieee80211_rx_bss_list_init(struct ieee80211_local *local)
-{
-       spin_lock_init(&local->sta_bss_lock);
-       INIT_LIST_HEAD(&local->sta_bss_list);
-}
+       rate_control_rate_init(sta, local);
 
+       if (elems.wmm_param) {
+               set_sta_flags(sta, WLAN_STA_WME);
+               rcu_read_unlock();
+               ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param,
+                                        elems.wmm_param_len);
+       } else
+               rcu_read_unlock();
 
-void ieee80211_rx_bss_list_deinit(struct ieee80211_local *local)
-{
-       struct ieee80211_sta_bss *bss, *tmp;
+       /* set AID and assoc capability,
+        * ieee80211_set_associated() will tell the driver */
+       bss_conf->aid = aid;
+       bss_conf->assoc_capability = capab_info;
+       ieee80211_set_associated(sdata, ifsta);
 
-       list_for_each_entry_safe(bss, tmp, &local->sta_bss_list, list)
-               ieee80211_rx_bss_put(local, bss);
+       ieee80211_associated(sdata, ifsta);
 }
 
 
@@ -2508,7 +1404,7 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        }
        ifsta->supp_rates_bits[local->hw.conf.channel->band] = rates;
 
-       ieee80211_sta_def_wmm_params(sdata, bss, 1);
+       ieee80211_sta_def_wmm_params(sdata, bss);
 
        ifsta->state = IEEE80211_STA_MLME_IBSS_JOINED;
        mod_timer(&ifsta->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL);
@@ -2588,21 +1484,29 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                                  struct ieee80211_mgmt *mgmt,
                                  size_t len,
                                  struct ieee80211_rx_status *rx_status,
-                                 struct ieee802_11_elems *elems)
+                                 struct ieee802_11_elems *elems,
+                                 bool beacon)
 {
        struct ieee80211_local *local = sdata->local;
-       int freq, clen;
+       int freq;
        struct ieee80211_sta_bss *bss;
        struct sta_info *sta;
        struct ieee80211_channel *channel;
        u64 beacon_timestamp, rx_timestamp;
        u64 supp_rates = 0;
-       bool beacon = ieee80211_is_beacon(mgmt->frame_control);
        enum ieee80211_band band = rx_status->band;
        DECLARE_MAC_BUF(mac);
        DECLARE_MAC_BUF(mac2);
 
-       beacon_timestamp = le64_to_cpu(mgmt->u.beacon.timestamp);
+       if (elems->ds_params && elems->ds_params_len == 1)
+               freq = ieee80211_channel_to_frequency(elems->ds_params[0]);
+       else
+               freq = rx_status->freq;
+
+       channel = ieee80211_get_channel(local->hw.wiphy, freq);
+
+       if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
+               return;
 
        if (ieee80211_vif_is_mesh(&sdata->vif) && elems->mesh_id &&
            elems->mesh_config && mesh_matches_local(elems, sdata)) {
@@ -2612,13 +1516,12 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                                      mesh_peer_accepts_plinks(elems));
        }
 
-       rcu_read_lock();
-
        if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS && elems->supp_rates &&
            memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) {
-
                supp_rates = ieee80211_sta_get_rates(local, elems, band);
 
+               rcu_read_lock();
+
                sta = sta_info_get(local, mgmt->sa);
                if (sta) {
                        u64 prev_rates;
@@ -2642,95 +1545,18 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                        ieee80211_ibss_add_sta(sdata, NULL, mgmt->bssid,
                                               mgmt->sa, supp_rates);
                }
-       }
-
-       rcu_read_unlock();
-
-       if (elems->ds_params && elems->ds_params_len == 1)
-               freq = ieee80211_channel_to_frequency(elems->ds_params[0]);
-       else
-               freq = rx_status->freq;
-
-       channel = ieee80211_get_channel(local->hw.wiphy, freq);
-
-       if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
-               return;
-
-#ifdef CONFIG_MAC80211_MESH
-       if (elems->mesh_config)
-               bss = ieee80211_rx_mesh_bss_get(local, elems->mesh_id,
-                               elems->mesh_id_len, elems->mesh_config, freq);
-       else
-#endif
-               bss = ieee80211_rx_bss_get(local, mgmt->bssid, freq,
-                                          elems->ssid, elems->ssid_len);
-       if (!bss) {
-#ifdef CONFIG_MAC80211_MESH
-               if (elems->mesh_config)
-                       bss = ieee80211_rx_mesh_bss_add(local, elems->mesh_id,
-                               elems->mesh_id_len, elems->mesh_config,
-                               elems->mesh_config_len, freq);
-               else
-#endif
-                       bss = ieee80211_rx_bss_add(sdata, mgmt->bssid, freq,
-                                                 elems->ssid, elems->ssid_len);
-               if (!bss)
-                       return;
-       } else {
-#if 0
-               /* TODO: order by RSSI? */
-               spin_lock_bh(&local->sta_bss_lock);
-               list_move_tail(&bss->list, &local->sta_bss_list);
-               spin_unlock_bh(&local->sta_bss_lock);
-#endif
-       }
-
-       /* save the ERP value so that it is available at association time */
-       if (elems->erp_info && elems->erp_info_len >= 1) {
-               bss->erp_value = elems->erp_info[0];
-               bss->has_erp_value = 1;
-       }
-
-       bss->beacon_int = le16_to_cpu(mgmt->u.beacon.beacon_int);
-       bss->capability = le16_to_cpu(mgmt->u.beacon.capab_info);
 
-       if (elems->tim) {
-               struct ieee80211_tim_ie *tim_ie =
-                       (struct ieee80211_tim_ie *)elems->tim;
-               bss->dtim_period = tim_ie->dtim_period;
+               rcu_read_unlock();
        }
 
-       /* set default value for buggy APs */
-       if (!elems->tim || bss->dtim_period == 0)
-               bss->dtim_period = 1;
-
-       bss->supp_rates_len = 0;
-       if (elems->supp_rates) {
-               clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
-               if (clen > elems->supp_rates_len)
-                       clen = elems->supp_rates_len;
-               memcpy(&bss->supp_rates[bss->supp_rates_len], elems->supp_rates,
-                      clen);
-               bss->supp_rates_len += clen;
-       }
-       if (elems->ext_supp_rates) {
-               clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
-               if (clen > elems->ext_supp_rates_len)
-                       clen = elems->ext_supp_rates_len;
-               memcpy(&bss->supp_rates[bss->supp_rates_len],
-                      elems->ext_supp_rates, clen);
-               bss->supp_rates_len += clen;
-       }
+       bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
+                                       freq, beacon);
+       if (!bss)
+               return;
 
-       bss->band = band;
+       /* was just updated in ieee80211_bss_info_update */
+       beacon_timestamp = bss->timestamp;
 
-       bss->timestamp = beacon_timestamp;
-       bss->last_update = jiffies;
-       bss->signal = rx_status->signal;
-       bss->noise = rx_status->noise;
-       bss->qual = rx_status->qual;
-       if (!beacon)
-               bss->last_probe_resp = jiffies;
        /*
         * In STA mode, the remaining parameters should not be overridden
         * by beacons because they're not necessarily accurate there.
@@ -2741,21 +1567,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                return;
        }
 
-       if (bss->ies == NULL || bss->ies_len < elems->total_len) {
-               kfree(bss->ies);
-               bss->ies = kmalloc(elems->total_len, GFP_ATOMIC);
-       }
-       if (bss->ies) {
-               memcpy(bss->ies, elems->ie_start, elems->total_len);
-               bss->ies_len = elems->total_len;
-       } else
-               bss->ies_len = 0;
-
-       bss->wmm_used = elems->wmm_param || elems->wmm_info;
-
        /* check if we need to merge IBSS */
        if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS && beacon &&
-           !local->sta_sw_scanning && !local->sta_hw_scanning &&
            bss->capability & WLAN_CAPABILITY_IBSS &&
            bss->freq == local->oper_channel->center_freq &&
            elems->ssid_len == sdata->u.sta.ssid_len &&
@@ -2832,7 +1645,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
        ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
                                &elems);
 
-       ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
+       ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false);
 
        /* direct probe may be part of the association flow */
        if (test_and_clear_bit(IEEE80211_STA_REQ_DIRECT_PROBE,
@@ -2863,7 +1676,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 
        ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems);
 
-       ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
+       ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, true);
 
        if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
                return;
@@ -2876,12 +1689,6 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param,
                                 elems.wmm_param_len);
 
-       /* Do not send changes to driver if we are scanning. This removes
-        * requirement that driver's bss_info_changed function needs to be
-        * atomic. */
-       if (local->sta_sw_scanning || local->sta_hw_scanning)
-               return;
-
        if (elems.erp_info && elems.erp_info_len >= 1)
                changed |= ieee80211_handle_erp_ie(sdata, elems.erp_info[0]);
        else {
@@ -2975,7 +1782,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
        printk(KERN_DEBUG "%s: Sending ProbeResp to %s\n",
               sdata->dev->name, print_mac(mac, resp->da));
 #endif /* CONFIG_MAC80211_IBSS_DEBUG */
-       ieee80211_sta_tx(sdata, skb, 0);
+       ieee80211_tx_skb(sdata, skb, 0);
 }
 
 static void ieee80211_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
@@ -2984,53 +1791,16 @@ static void ieee80211_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
                                     size_t len,
                                     struct ieee80211_rx_status *rx_status)
 {
-       struct ieee80211_local *local = sdata->local;
-
-       if (len < IEEE80211_MIN_ACTION_SIZE)
+       /* currently we only handle mesh interface action frames here */
+       if (!ieee80211_vif_is_mesh(&sdata->vif))
                return;
 
        switch (mgmt->u.action.category) {
-       case WLAN_CATEGORY_SPECTRUM_MGMT:
-               if (local->hw.conf.channel->band != IEEE80211_BAND_5GHZ)
-                       break;
-               switch (mgmt->u.action.u.chan_switch.action_code) {
-               case WLAN_ACTION_SPCT_MSR_REQ:
-                       if (len < (IEEE80211_MIN_ACTION_SIZE +
-                                  sizeof(mgmt->u.action.u.measurement)))
-                               break;
-                       ieee80211_sta_process_measurement_req(sdata, mgmt, len);
-                       break;
-               }
-               break;
-       case WLAN_CATEGORY_BACK:
-               switch (mgmt->u.action.u.addba_req.action_code) {
-               case WLAN_ACTION_ADDBA_REQ:
-                       if (len < (IEEE80211_MIN_ACTION_SIZE +
-                                  sizeof(mgmt->u.action.u.addba_req)))
-                               break;
-                       ieee80211_sta_process_addba_request(local, mgmt, len);
-                       break;
-               case WLAN_ACTION_ADDBA_RESP:
-                       if (len < (IEEE80211_MIN_ACTION_SIZE +
-                                  sizeof(mgmt->u.action.u.addba_resp)))
-                               break;
-                       ieee80211_sta_process_addba_resp(local, mgmt, len);
-                       break;
-               case WLAN_ACTION_DELBA:
-                       if (len < (IEEE80211_MIN_ACTION_SIZE +
-                                  sizeof(mgmt->u.action.u.delba)))
-                               break;
-                       ieee80211_sta_process_delba(sdata, mgmt, len);
-                       break;
-               }
-               break;
        case PLINK_CATEGORY:
-               if (ieee80211_vif_is_mesh(&sdata->vif))
-                       mesh_rx_plink_frame(sdata, mgmt, len, rx_status);
+               mesh_rx_plink_frame(sdata, mgmt, len, rx_status);
                break;
        case MESH_PATH_SEL_CATEGORY:
-               if (ieee80211_vif_is_mesh(&sdata->vif))
-                       mesh_rx_path_sel_frame(sdata, mgmt, len);
+               mesh_rx_path_sel_frame(sdata, mgmt, len);
                break;
        }
 }
@@ -3120,41 +1890,6 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
 }
 
 
-ieee80211_rx_result
-ieee80211_sta_rx_scan(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
-                     struct ieee80211_rx_status *rx_status)
-{
-       struct ieee80211_mgmt *mgmt;
-       __le16 fc;
-
-       if (skb->len < 2)
-               return RX_DROP_UNUSABLE;
-
-       mgmt = (struct ieee80211_mgmt *) skb->data;
-       fc = mgmt->frame_control;
-
-       if (ieee80211_is_ctl(fc))
-               return RX_CONTINUE;
-
-       if (skb->len < 24)
-               return RX_DROP_MONITOR;
-
-       if (ieee80211_is_probe_resp(fc)) {
-               ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len, rx_status);
-               dev_kfree_skb(skb);
-               return RX_QUEUED;
-       }
-
-       if (ieee80211_is_beacon(fc)) {
-               ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, rx_status);
-               dev_kfree_skb(skb);
-               return RX_QUEUED;
-       }
-
-       return RX_CONTINUE;
-}
-
-
 static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_local *local = sdata->local;
@@ -3230,123 +1965,34 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata,
 
        free_plinks = mesh_plink_availables(sdata);
        if (free_plinks != sdata->u.sta.accepting_plinks)
-               ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
-
-       mod_timer(&ifsta->timer, jiffies +
-                       IEEE80211_MESH_HOUSEKEEPING_INTERVAL);
-}
-
-
-void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_if_sta *ifsta;
-       ifsta = &sdata->u.sta;
-       ifsta->state = IEEE80211_STA_MLME_MESH_UP;
-       ieee80211_sta_timer((unsigned long)sdata);
-       ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
-}
-#endif
-
-
-void ieee80211_sta_timer(unsigned long data)
-{
-       struct ieee80211_sub_if_data *sdata =
-               (struct ieee80211_sub_if_data *) data;
-       struct ieee80211_if_sta *ifsta = &sdata->u.sta;
-       struct ieee80211_local *local = sdata->local;
-
-       set_bit(IEEE80211_STA_REQ_RUN, &ifsta->request);
-       queue_work(local->hw.workqueue, &ifsta->work);
-}
-
-void ieee80211_sta_work(struct work_struct *work)
-{
-       struct ieee80211_sub_if_data *sdata =
-               container_of(work, struct ieee80211_sub_if_data, u.sta.work);
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_if_sta *ifsta;
-       struct sk_buff *skb;
-
-       if (!netif_running(sdata->dev))
-               return;
-
-       if (local->sta_sw_scanning || local->sta_hw_scanning)
-               return;
-
-       if (WARN_ON(sdata->vif.type != IEEE80211_IF_TYPE_STA &&
-                   sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
-                   sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT))
-               return;
-       ifsta = &sdata->u.sta;
-
-       while ((skb = skb_dequeue(&ifsta->skb_queue)))
-               ieee80211_sta_rx_queued_mgmt(sdata, skb);
-
-#ifdef CONFIG_MAC80211_MESH
-       if (ifsta->preq_queue_len &&
-           time_after(jiffies,
-                      ifsta->last_preq + msecs_to_jiffies(ifsta->mshcfg.dot11MeshHWMPpreqMinInterval)))
-               mesh_path_start_discovery(sdata);
-#endif
-
-       if (ifsta->state != IEEE80211_STA_MLME_DIRECT_PROBE &&
-           ifsta->state != IEEE80211_STA_MLME_AUTHENTICATE &&
-           ifsta->state != IEEE80211_STA_MLME_ASSOCIATE &&
-           test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request)) {
-               if (ifsta->scan_ssid_len)
-                       ieee80211_sta_start_scan(sdata, ifsta->scan_ssid, ifsta->scan_ssid_len);
-               else
-                       ieee80211_sta_start_scan(sdata, NULL, 0);
-               return;
-       }
-
-       if (test_and_clear_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request)) {
-               if (ieee80211_sta_config_auth(sdata, ifsta))
-                       return;
-               clear_bit(IEEE80211_STA_REQ_RUN, &ifsta->request);
-       } else if (!test_and_clear_bit(IEEE80211_STA_REQ_RUN, &ifsta->request))
-               return;
-
-       switch (ifsta->state) {
-       case IEEE80211_STA_MLME_DISABLED:
-               break;
-       case IEEE80211_STA_MLME_DIRECT_PROBE:
-               ieee80211_direct_probe(sdata, ifsta);
-               break;
-       case IEEE80211_STA_MLME_AUTHENTICATE:
-               ieee80211_authenticate(sdata, ifsta);
-               break;
-       case IEEE80211_STA_MLME_ASSOCIATE:
-               ieee80211_associate(sdata, ifsta);
-               break;
-       case IEEE80211_STA_MLME_ASSOCIATED:
-               ieee80211_associated(sdata, ifsta);
-               break;
-       case IEEE80211_STA_MLME_IBSS_SEARCH:
-               ieee80211_sta_find_ibss(sdata, ifsta);
-               break;
-       case IEEE80211_STA_MLME_IBSS_JOINED:
-               ieee80211_sta_merge_ibss(sdata, ifsta);
-               break;
-#ifdef CONFIG_MAC80211_MESH
-       case IEEE80211_STA_MLME_MESH_UP:
-               ieee80211_mesh_housekeeping(sdata, ifsta);
-               break;
-#endif
-       default:
-               WARN_ON(1);
-               break;
-       }
+               ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
 
-       if (ieee80211_privacy_mismatch(sdata, ifsta)) {
-               printk(KERN_DEBUG "%s: privacy configuration mismatch and "
-                      "mixed-cell disabled - disassociate\n", sdata->dev->name);
+       mod_timer(&ifsta->timer, jiffies +
+                       IEEE80211_MESH_HOUSEKEEPING_INTERVAL);
+}
 
-               ieee80211_send_disassoc(sdata, ifsta, WLAN_REASON_UNSPECIFIED);
-               ieee80211_set_disassoc(sdata, ifsta, 0);
-       }
+
+void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_sta *ifsta;
+       ifsta = &sdata->u.sta;
+       ifsta->state = IEEE80211_STA_MLME_MESH_UP;
+       ieee80211_sta_timer((unsigned long)sdata);
+       ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
 }
+#endif
+
+
+void ieee80211_sta_timer(unsigned long data)
+{
+       struct ieee80211_sub_if_data *sdata =
+               (struct ieee80211_sub_if_data *) data;
+       struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+       struct ieee80211_local *local = sdata->local;
 
+       set_bit(IEEE80211_STA_REQ_RUN, &ifsta->request);
+       queue_work(local->hw.workqueue, &ifsta->work);
+}
 
 static void ieee80211_sta_reset_auth(struct ieee80211_sub_if_data *sdata,
                                     struct ieee80211_if_sta *ifsta)
@@ -3375,6 +2021,7 @@ static void ieee80211_sta_reset_auth(struct ieee80211_sub_if_data *sdata,
        ifsta->direct_probe_tries = 0;
        ifsta->auth_tries = 0;
        ifsta->assoc_tries = 0;
+       netif_tx_stop_all_queues(sdata->dev);
        netif_carrier_off(sdata->dev);
 }
 
@@ -3388,9 +2035,14 @@ void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata,
                return;
 
        if ((ifsta->flags & (IEEE80211_STA_BSSID_SET |
-                               IEEE80211_STA_AUTO_BSSID_SEL)) &&
+                            IEEE80211_STA_AUTO_BSSID_SEL)) &&
            (ifsta->flags & (IEEE80211_STA_SSID_SET |
-                               IEEE80211_STA_AUTO_SSID_SEL))) {
+                            IEEE80211_STA_AUTO_SSID_SEL))) {
+
+               if (ifsta->state == IEEE80211_STA_MLME_ASSOCIATED)
+                       ieee80211_set_disassoc(sdata, ifsta, true, true,
+                                              WLAN_REASON_DEAUTH_LEAVING);
+
                set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request);
                queue_work(local->hw.workqueue, &ifsta->work);
        }
@@ -3426,85 +2078,6 @@ static int ieee80211_sta_match_ssid(struct ieee80211_if_sta *ifsta,
        return 0;
 }
 
-static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata,
-                                    struct ieee80211_if_sta *ifsta)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_sta_bss *bss, *selected = NULL;
-       int top_rssi = 0, freq;
-
-       spin_lock_bh(&local->sta_bss_lock);
-       freq = local->oper_channel->center_freq;
-       list_for_each_entry(bss, &local->sta_bss_list, list) {
-               if (!(bss->capability & WLAN_CAPABILITY_ESS))
-                       continue;
-
-               if ((ifsta->flags & (IEEE80211_STA_AUTO_SSID_SEL |
-                       IEEE80211_STA_AUTO_BSSID_SEL |
-                       IEEE80211_STA_AUTO_CHANNEL_SEL)) &&
-                   (!!(bss->capability & WLAN_CAPABILITY_PRIVACY) ^
-                    !!sdata->default_key))
-                       continue;
-
-               if (!(ifsta->flags & IEEE80211_STA_AUTO_CHANNEL_SEL) &&
-                   bss->freq != freq)
-                       continue;
-
-               if (!(ifsta->flags & IEEE80211_STA_AUTO_BSSID_SEL) &&
-                   memcmp(bss->bssid, ifsta->bssid, ETH_ALEN))
-                       continue;
-
-               if (!(ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL) &&
-                   !ieee80211_sta_match_ssid(ifsta, bss->ssid, bss->ssid_len))
-                       continue;
-
-               if (!selected || top_rssi < bss->signal) {
-                       selected = bss;
-                       top_rssi = bss->signal;
-               }
-       }
-       if (selected)
-               atomic_inc(&selected->users);
-       spin_unlock_bh(&local->sta_bss_lock);
-
-       if (selected) {
-               ieee80211_set_freq(sdata, selected->freq);
-               if (!(ifsta->flags & IEEE80211_STA_SSID_SET))
-                       ieee80211_sta_set_ssid(sdata, selected->ssid,
-                                              selected->ssid_len);
-               ieee80211_sta_set_bssid(sdata, selected->bssid);
-               ieee80211_sta_def_wmm_params(sdata, selected, 0);
-
-               /* Send out direct probe if no probe resp was received or
-                * the one we have is outdated
-                */
-               if (!selected->last_probe_resp ||
-                   time_after(jiffies, selected->last_probe_resp
-                                       + IEEE80211_SCAN_RESULT_EXPIRE))
-                       ifsta->state = IEEE80211_STA_MLME_DIRECT_PROBE;
-               else
-                       ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE;
-
-               ieee80211_rx_bss_put(local, selected);
-               ieee80211_sta_reset_auth(sdata, ifsta);
-               return 0;
-       } else {
-               if (ifsta->assoc_scan_tries < IEEE80211_ASSOC_SCANS_MAX_TRIES) {
-                       ifsta->assoc_scan_tries++;
-                       if (ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL)
-                               ieee80211_sta_start_scan(sdata, NULL, 0);
-                       else
-                               ieee80211_sta_start_scan(sdata, ifsta->ssid,
-                                                        ifsta->ssid_len);
-                       ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE;
-                       set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request);
-               } else
-                       ifsta->state = IEEE80211_STA_MLME_DISABLED;
-       }
-       return -1;
-}
-
-
 static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata,
                                     struct ieee80211_if_sta *ifsta)
 {
@@ -3533,7 +2106,7 @@ static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata,
        printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %s\n",
               sdata->dev->name, print_mac(mac, bssid));
 
-       bss = ieee80211_rx_bss_add(sdata, bssid,
+       bss = ieee80211_rx_bss_add(local, bssid,
                                   local->hw.conf.channel->center_freq,
                                   sdata->u.sta.ssid, sdata->u.sta.ssid_len);
        if (!bss)
@@ -3701,602 +2274,64 @@ int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size
                        res = ieee80211_if_config(sdata, IEEE80211_IFCC_SSID);
                if (res) {
                        printk(KERN_DEBUG "%s: Failed to config new SSID to "
-                              "the low-level driver\n", sdata->dev->name);
-                       return res;
-               }
-       }
-
-       if (len)
-               ifsta->flags |= IEEE80211_STA_SSID_SET;
-       else
-               ifsta->flags &= ~IEEE80211_STA_SSID_SET;
-
-       if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS &&
-           !(ifsta->flags & IEEE80211_STA_BSSID_SET)) {
-               ifsta->ibss_join_req = jiffies;
-               ifsta->state = IEEE80211_STA_MLME_IBSS_SEARCH;
-               return ieee80211_sta_find_ibss(sdata, ifsta);
-       }
-
-       return 0;
-}
-
-
-int ieee80211_sta_get_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t *len)
-{
-       struct ieee80211_if_sta *ifsta = &sdata->u.sta;
-       memcpy(ssid, ifsta->ssid, ifsta->ssid_len);
-       *len = ifsta->ssid_len;
-       return 0;
-}
-
-
-int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid)
-{
-       struct ieee80211_if_sta *ifsta;
-       int res;
-
-       ifsta = &sdata->u.sta;
-
-       if (memcmp(ifsta->bssid, bssid, ETH_ALEN) != 0) {
-               memcpy(ifsta->bssid, bssid, ETH_ALEN);
-               res = 0;
-               /*
-                * Hack! See also ieee80211_sta_set_ssid.
-                */
-               if (netif_running(sdata->dev))
-                       res = ieee80211_if_config(sdata, IEEE80211_IFCC_BSSID);
-               if (res) {
-                       printk(KERN_DEBUG "%s: Failed to config new BSSID to "
-                              "the low-level driver\n", sdata->dev->name);
-                       return res;
-               }
-       }
-
-       if (is_valid_ether_addr(bssid))
-               ifsta->flags |= IEEE80211_STA_BSSID_SET;
-       else
-               ifsta->flags &= ~IEEE80211_STA_BSSID_SET;
-
-       return 0;
-}
-
-
-static void ieee80211_send_nullfunc(struct ieee80211_local *local,
-                                   struct ieee80211_sub_if_data *sdata,
-                                   int powersave)
-{
-       struct sk_buff *skb;
-       struct ieee80211_hdr *nullfunc;
-       __le16 fc;
-
-       skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24);
-       if (!skb) {
-               printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc "
-                      "frame\n", sdata->dev->name);
-               return;
-       }
-       skb_reserve(skb, local->hw.extra_tx_headroom);
-
-       nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24);
-       memset(nullfunc, 0, 24);
-       fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
-                        IEEE80211_FCTL_TODS);
-       if (powersave)
-               fc |= cpu_to_le16(IEEE80211_FCTL_PM);
-       nullfunc->frame_control = fc;
-       memcpy(nullfunc->addr1, sdata->u.sta.bssid, ETH_ALEN);
-       memcpy(nullfunc->addr2, sdata->dev->dev_addr, ETH_ALEN);
-       memcpy(nullfunc->addr3, sdata->u.sta.bssid, ETH_ALEN);
-
-       ieee80211_sta_tx(sdata, skb, 0);
-}
-
-
-static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
-{
-       if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
-           ieee80211_vif_is_mesh(&sdata->vif))
-               ieee80211_sta_timer((unsigned long)sdata);
-}
-
-void ieee80211_scan_completed(struct ieee80211_hw *hw)
-{
-       struct ieee80211_local *local = hw_to_local(hw);
-       struct net_device *dev = local->scan_dev;
-       struct ieee80211_sub_if_data *sdata;
-       union iwreq_data wrqu;
-
-       local->last_scan_completed = jiffies;
-       memset(&wrqu, 0, sizeof(wrqu));
-       wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
-
-       if (local->sta_hw_scanning) {
-               local->sta_hw_scanning = 0;
-               if (ieee80211_hw_config(local))
-                       printk(KERN_DEBUG "%s: failed to restore operational "
-                              "channel after scan\n", dev->name);
-               /* Restart STA timer for HW scan case */
-               rcu_read_lock();
-               list_for_each_entry_rcu(sdata, &local->interfaces, list)
-                       ieee80211_restart_sta_timer(sdata);
-               rcu_read_unlock();
-
-               goto done;
-       }
-
-       local->sta_sw_scanning = 0;
-       if (ieee80211_hw_config(local))
-               printk(KERN_DEBUG "%s: failed to restore operational "
-                      "channel after scan\n", dev->name);
-
-
-       netif_tx_lock_bh(local->mdev);
-       netif_addr_lock(local->mdev);
-       local->filter_flags &= ~FIF_BCN_PRBRESP_PROMISC;
-       local->ops->configure_filter(local_to_hw(local),
-                                    FIF_BCN_PRBRESP_PROMISC,
-                                    &local->filter_flags,
-                                    local->mdev->mc_count,
-                                    local->mdev->mc_list);
-
-       netif_addr_unlock(local->mdev);
-       netif_tx_unlock_bh(local->mdev);
-
-       rcu_read_lock();
-       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-               /* Tell AP we're back */
-               if (sdata->vif.type == IEEE80211_IF_TYPE_STA &&
-                   sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED)
-                       ieee80211_send_nullfunc(local, sdata, 0);
-
-               ieee80211_restart_sta_timer(sdata);
-
-               netif_wake_queue(sdata->dev);
-       }
-       rcu_read_unlock();
-
-done:
-       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
-               struct ieee80211_if_sta *ifsta = &sdata->u.sta;
-               if (!(ifsta->flags & IEEE80211_STA_BSSID_SET) ||
-                   (!(ifsta->state == IEEE80211_STA_MLME_IBSS_JOINED) &&
-                   !ieee80211_sta_active_ibss(sdata)))
-                       ieee80211_sta_find_ibss(sdata, ifsta);
-       }
-}
-EXPORT_SYMBOL(ieee80211_scan_completed);
-
-void ieee80211_sta_scan_work(struct work_struct *work)
-{
-       struct ieee80211_local *local =
-               container_of(work, struct ieee80211_local, scan_work.work);
-       struct net_device *dev = local->scan_dev;
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       struct ieee80211_supported_band *sband;
-       struct ieee80211_channel *chan;
-       int skip;
-       unsigned long next_delay = 0;
-
-       if (!local->sta_sw_scanning)
-               return;
-
-       switch (local->scan_state) {
-       case SCAN_SET_CHANNEL:
-               /*
-                * Get current scan band. scan_band may be IEEE80211_NUM_BANDS
-                * after we successfully scanned the last channel of the last
-                * band (and the last band is supported by the hw)
-                */
-               if (local->scan_band < IEEE80211_NUM_BANDS)
-                       sband = local->hw.wiphy->bands[local->scan_band];
-               else
-                       sband = NULL;
-
-               /*
-                * If we are at an unsupported band and have more bands
-                * left to scan, advance to the next supported one.
-                */
-               while (!sband && local->scan_band < IEEE80211_NUM_BANDS - 1) {
-                       local->scan_band++;
-                       sband = local->hw.wiphy->bands[local->scan_band];
-                       local->scan_channel_idx = 0;
-               }
-
-               /* if no more bands/channels left, complete scan */
-               if (!sband || local->scan_channel_idx >= sband->n_channels) {
-                       ieee80211_scan_completed(local_to_hw(local));
-                       return;
-               }
-               skip = 0;
-               chan = &sband->channels[local->scan_channel_idx];
-
-               if (chan->flags & IEEE80211_CHAN_DISABLED ||
-                   (sdata->vif.type == IEEE80211_IF_TYPE_IBSS &&
-                    chan->flags & IEEE80211_CHAN_NO_IBSS))
-                       skip = 1;
-
-               if (!skip) {
-                       local->scan_channel = chan;
-                       if (ieee80211_hw_config(local)) {
-                               printk(KERN_DEBUG "%s: failed to set freq to "
-                                      "%d MHz for scan\n", dev->name,
-                                      chan->center_freq);
-                               skip = 1;
-                       }
-               }
-
-               /* advance state machine to next channel/band */
-               local->scan_channel_idx++;
-               if (local->scan_channel_idx >= sband->n_channels) {
-                       /*
-                        * scan_band may end up == IEEE80211_NUM_BANDS, but
-                        * we'll catch that case above and complete the scan
-                        * if that is the case.
-                        */
-                       local->scan_band++;
-                       local->scan_channel_idx = 0;
-               }
-
-               if (skip)
-                       break;
-
-               next_delay = IEEE80211_PROBE_DELAY +
-                            usecs_to_jiffies(local->hw.channel_change_time);
-               local->scan_state = SCAN_SEND_PROBE;
-               break;
-       case SCAN_SEND_PROBE:
-               next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
-               local->scan_state = SCAN_SET_CHANNEL;
-
-               if (local->scan_channel->flags & IEEE80211_CHAN_PASSIVE_SCAN)
-                       break;
-               ieee80211_send_probe_req(sdata, NULL, local->scan_ssid,
-                                        local->scan_ssid_len);
-               next_delay = IEEE80211_CHANNEL_TIME;
-               break;
-       }
-
-       if (local->sta_sw_scanning)
-               queue_delayed_work(local->hw.workqueue, &local->scan_work,
-                                  next_delay);
-}
-
-
-static int ieee80211_sta_start_scan(struct ieee80211_sub_if_data *scan_sdata,
-                                   u8 *ssid, size_t ssid_len)
-{
-       struct ieee80211_local *local = scan_sdata->local;
-       struct ieee80211_sub_if_data *sdata;
-
-       if (ssid_len > IEEE80211_MAX_SSID_LEN)
-               return -EINVAL;
-
-       /* MLME-SCAN.request (page 118)  page 144 (11.1.3.1)
-        * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS
-        * BSSID: MACAddress
-        * SSID
-        * ScanType: ACTIVE, PASSIVE
-        * ProbeDelay: delay (in microseconds) to be used prior to transmitting
-        *    a Probe frame during active scanning
-        * ChannelList
-        * MinChannelTime (>= ProbeDelay), in TU
-        * MaxChannelTime: (>= MinChannelTime), in TU
-        */
-
-        /* MLME-SCAN.confirm
-         * BSSDescriptionSet
-         * ResultCode: SUCCESS, INVALID_PARAMETERS
-        */
-
-       if (local->sta_sw_scanning || local->sta_hw_scanning) {
-               if (local->scan_dev == scan_sdata->dev)
-                       return 0;
-               return -EBUSY;
-       }
-
-       if (local->ops->hw_scan) {
-               int rc = local->ops->hw_scan(local_to_hw(local),
-                                            ssid, ssid_len);
-               if (!rc) {
-                       local->sta_hw_scanning = 1;
-                       local->scan_dev = scan_sdata->dev;
+                              "the low-level driver\n", sdata->dev->name);
+                       return res;
                }
-               return rc;
        }
 
-       local->sta_sw_scanning = 1;
+       if (len)
+               ifsta->flags |= IEEE80211_STA_SSID_SET;
+       else
+               ifsta->flags &= ~IEEE80211_STA_SSID_SET;
 
-       rcu_read_lock();
-       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-               netif_stop_queue(sdata->dev);
-               if (sdata->vif.type == IEEE80211_IF_TYPE_STA &&
-                   (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED))
-                       ieee80211_send_nullfunc(local, sdata, 1);
+       if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS &&
+           !(ifsta->flags & IEEE80211_STA_BSSID_SET)) {
+               ifsta->ibss_join_req = jiffies;
+               ifsta->state = IEEE80211_STA_MLME_IBSS_SEARCH;
+               return ieee80211_sta_find_ibss(sdata, ifsta);
        }
-       rcu_read_unlock();
-
-       if (ssid) {
-               local->scan_ssid_len = ssid_len;
-               memcpy(local->scan_ssid, ssid, ssid_len);
-       } else
-               local->scan_ssid_len = 0;
-       local->scan_state = SCAN_SET_CHANNEL;
-       local->scan_channel_idx = 0;
-       local->scan_band = IEEE80211_BAND_2GHZ;
-       local->scan_dev = scan_sdata->dev;
-
-       netif_addr_lock_bh(local->mdev);
-       local->filter_flags |= FIF_BCN_PRBRESP_PROMISC;
-       local->ops->configure_filter(local_to_hw(local),
-                                    FIF_BCN_PRBRESP_PROMISC,
-                                    &local->filter_flags,
-                                    local->mdev->mc_count,
-                                    local->mdev->mc_list);
-       netif_addr_unlock_bh(local->mdev);
-
-       /* TODO: start scan as soon as all nullfunc frames are ACKed */
-       queue_delayed_work(local->hw.workqueue, &local->scan_work,
-                          IEEE80211_CHANNEL_TIME);
 
        return 0;
 }
 
 
-int ieee80211_sta_req_scan(struct ieee80211_sub_if_data *sdata, u8 *ssid, size_t ssid_len)
+int ieee80211_sta_get_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t *len)
 {
        struct ieee80211_if_sta *ifsta = &sdata->u.sta;
-       struct ieee80211_local *local = sdata->local;
-
-       if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
-               return ieee80211_sta_start_scan(sdata, ssid, ssid_len);
-
-       if (local->sta_sw_scanning || local->sta_hw_scanning) {
-               if (local->scan_dev == sdata->dev)
-                       return 0;
-               return -EBUSY;
-       }
-
-       ifsta->scan_ssid_len = ssid_len;
-       if (ssid_len)
-               memcpy(ifsta->scan_ssid, ssid, ssid_len);
-       set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request);
-       queue_work(local->hw.workqueue, &ifsta->work);
+       memcpy(ssid, ifsta->ssid, ifsta->ssid_len);
+       *len = ifsta->ssid_len;
        return 0;
 }
 
 
-static void ieee80211_sta_add_scan_ies(struct iw_request_info *info,
-                                      struct ieee80211_sta_bss *bss,
-                                      char **current_ev, char *end_buf)
-{
-       u8 *pos, *end, *next;
-       struct iw_event iwe;
-
-       if (bss == NULL || bss->ies == NULL)
-               return;
-
-       /*
-        * If needed, fragment the IEs buffer (at IE boundaries) into short
-        * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
-        */
-       pos = bss->ies;
-       end = pos + bss->ies_len;
-
-       while (end - pos > IW_GENERIC_IE_MAX) {
-               next = pos + 2 + pos[1];
-               while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX)
-                       next = next + 2 + next[1];
-
-               memset(&iwe, 0, sizeof(iwe));
-               iwe.cmd = IWEVGENIE;
-               iwe.u.data.length = next - pos;
-               *current_ev = iwe_stream_add_point(info, *current_ev,
-                                                  end_buf, &iwe, pos);
-
-               pos = next;
-       }
-
-       if (end > pos) {
-               memset(&iwe, 0, sizeof(iwe));
-               iwe.cmd = IWEVGENIE;
-               iwe.u.data.length = end - pos;
-               *current_ev = iwe_stream_add_point(info, *current_ev,
-                                                  end_buf, &iwe, pos);
-       }
-}
-
-
-static char *
-ieee80211_sta_scan_result(struct ieee80211_local *local,
-                         struct iw_request_info *info,
-                         struct ieee80211_sta_bss *bss,
-                         char *current_ev, char *end_buf)
+int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid)
 {
-       struct iw_event iwe;
-
-       if (time_after(jiffies,
-                      bss->last_update + IEEE80211_SCAN_RESULT_EXPIRE))
-               return current_ev;
-
-       memset(&iwe, 0, sizeof(iwe));
-       iwe.cmd = SIOCGIWAP;
-       iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
-       memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
-       current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
-                                         IW_EV_ADDR_LEN);
-
-       memset(&iwe, 0, sizeof(iwe));
-       iwe.cmd = SIOCGIWESSID;
-       if (bss_mesh_cfg(bss)) {
-               iwe.u.data.length = bss_mesh_id_len(bss);
-               iwe.u.data.flags = 1;
-               current_ev = iwe_stream_add_point(info, current_ev, end_buf,
-                                                 &iwe, bss_mesh_id(bss));
-       } else {
-               iwe.u.data.length = bss->ssid_len;
-               iwe.u.data.flags = 1;
-               current_ev = iwe_stream_add_point(info, current_ev, end_buf,
-                                                 &iwe, bss->ssid);
-       }
-
-       if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)
-           || bss_mesh_cfg(bss)) {
-               memset(&iwe, 0, sizeof(iwe));
-               iwe.cmd = SIOCGIWMODE;
-               if (bss_mesh_cfg(bss))
-                       iwe.u.mode = IW_MODE_MESH;
-               else if (bss->capability & WLAN_CAPABILITY_ESS)
-                       iwe.u.mode = IW_MODE_MASTER;
-               else
-                       iwe.u.mode = IW_MODE_ADHOC;
-               current_ev = iwe_stream_add_event(info, current_ev, end_buf,
-                                                 &iwe, IW_EV_UINT_LEN);
-       }
-
-       memset(&iwe, 0, sizeof(iwe));
-       iwe.cmd = SIOCGIWFREQ;
-       iwe.u.freq.m = ieee80211_frequency_to_channel(bss->freq);
-       iwe.u.freq.e = 0;
-       current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
-                                         IW_EV_FREQ_LEN);
-
-       memset(&iwe, 0, sizeof(iwe));
-       iwe.cmd = SIOCGIWFREQ;
-       iwe.u.freq.m = bss->freq;
-       iwe.u.freq.e = 6;
-       current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
-                                         IW_EV_FREQ_LEN);
-       memset(&iwe, 0, sizeof(iwe));
-       iwe.cmd = IWEVQUAL;
-       iwe.u.qual.qual = bss->qual;
-       iwe.u.qual.level = bss->signal;
-       iwe.u.qual.noise = bss->noise;
-       iwe.u.qual.updated = local->wstats_flags;
-       current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
-                                         IW_EV_QUAL_LEN);
-
-       memset(&iwe, 0, sizeof(iwe));
-       iwe.cmd = SIOCGIWENCODE;
-       if (bss->capability & WLAN_CAPABILITY_PRIVACY)
-               iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
-       else
-               iwe.u.data.flags = IW_ENCODE_DISABLED;
-       iwe.u.data.length = 0;
-       current_ev = iwe_stream_add_point(info, current_ev, end_buf,
-                                         &iwe, "");
-
-       ieee80211_sta_add_scan_ies(info, bss, &current_ev, end_buf);
-
-       if (bss && bss->supp_rates_len > 0) {
-               /* display all supported rates in readable format */
-               char *p = current_ev + iwe_stream_lcp_len(info);
-               int i;
-
-               memset(&iwe, 0, sizeof(iwe));
-               iwe.cmd = SIOCGIWRATE;
-               /* Those two flags are ignored... */
-               iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
-
-               for (i = 0; i < bss->supp_rates_len; i++) {
-                       iwe.u.bitrate.value = ((bss->supp_rates[i] &
-                                                       0x7f) * 500000);
-                       p = iwe_stream_add_value(info, current_ev, p,
-                                       end_buf, &iwe, IW_EV_PARAM_LEN);
-               }
-               current_ev = p;
-       }
+       struct ieee80211_if_sta *ifsta;
+       int res;
 
-       if (bss) {
-               char *buf;
-               buf = kmalloc(30, GFP_ATOMIC);
-               if (buf) {
-                       memset(&iwe, 0, sizeof(iwe));
-                       iwe.cmd = IWEVCUSTOM;
-                       sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->timestamp));
-                       iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
-                       memset(&iwe, 0, sizeof(iwe));
-                       iwe.cmd = IWEVCUSTOM;
-                       sprintf(buf, " Last beacon: %dms ago",
-                               jiffies_to_msecs(jiffies - bss->last_update));
-                       iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf, &iwe, buf);
-                       kfree(buf);
-               }
-       }
+       ifsta = &sdata->u.sta;
 
-       if (bss_mesh_cfg(bss)) {
-               char *buf;
-               u8 *cfg = bss_mesh_cfg(bss);
-               buf = kmalloc(50, GFP_ATOMIC);
-               if (buf) {
-                       memset(&iwe, 0, sizeof(iwe));
-                       iwe.cmd = IWEVCUSTOM;
-                       sprintf(buf, "Mesh network (version %d)", cfg[0]);
-                       iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
-                       sprintf(buf, "Path Selection Protocol ID: "
-                               "0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3],
-                                                       cfg[4]);
-                       iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
-                       sprintf(buf, "Path Selection Metric ID: "
-                               "0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7],
-                                                       cfg[8]);
-                       iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
-                       sprintf(buf, "Congestion Control Mode ID: "
-                               "0x%02X%02X%02X%02X", cfg[9], cfg[10],
-                                                       cfg[11], cfg[12]);
-                       iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
-                       sprintf(buf, "Channel Precedence: "
-                               "0x%02X%02X%02X%02X", cfg[13], cfg[14],
-                                                       cfg[15], cfg[16]);
-                       iwe.u.data.length = strlen(buf);
-                       current_ev = iwe_stream_add_point(info, current_ev,
-                                                         end_buf,
-                                                         &iwe, buf);
-                       kfree(buf);
+       if (memcmp(ifsta->bssid, bssid, ETH_ALEN) != 0) {
+               memcpy(ifsta->bssid, bssid, ETH_ALEN);
+               res = 0;
+               /*
+                * Hack! See also ieee80211_sta_set_ssid.
+                */
+               if (netif_running(sdata->dev))
+                       res = ieee80211_if_config(sdata, IEEE80211_IFCC_BSSID);
+               if (res) {
+                       printk(KERN_DEBUG "%s: Failed to config new BSSID to "
+                              "the low-level driver\n", sdata->dev->name);
+                       return res;
                }
        }
 
-       return current_ev;
-}
-
-
-int ieee80211_sta_scan_results(struct ieee80211_local *local,
-                              struct iw_request_info *info,
-                              char *buf, size_t len)
-{
-       char *current_ev = buf;
-       char *end_buf = buf + len;
-       struct ieee80211_sta_bss *bss;
+       if (is_valid_ether_addr(bssid))
+               ifsta->flags |= IEEE80211_STA_BSSID_SET;
+       else
+               ifsta->flags &= ~IEEE80211_STA_BSSID_SET;
 
-       spin_lock_bh(&local->sta_bss_lock);
-       list_for_each_entry(bss, &local->sta_bss_list, list) {
-               if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
-                       spin_unlock_bh(&local->sta_bss_lock);
-                       return -E2BIG;
-               }
-               current_ev = ieee80211_sta_scan_result(local, info, bss,
-                                                      current_ev, end_buf);
-       }
-       spin_unlock_bh(&local->sta_bss_lock);
-       return current_ev - buf;
+       return 0;
 }
 
 
@@ -4367,6 +2402,85 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
 }
 
 
+static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata,
+                                    struct ieee80211_if_sta *ifsta)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_sta_bss *bss, *selected = NULL;
+       int top_rssi = 0, freq;
+
+       spin_lock_bh(&local->sta_bss_lock);
+       freq = local->oper_channel->center_freq;
+       list_for_each_entry(bss, &local->sta_bss_list, list) {
+               if (!(bss->capability & WLAN_CAPABILITY_ESS))
+                       continue;
+
+               if ((ifsta->flags & (IEEE80211_STA_AUTO_SSID_SEL |
+                       IEEE80211_STA_AUTO_BSSID_SEL |
+                       IEEE80211_STA_AUTO_CHANNEL_SEL)) &&
+                   (!!(bss->capability & WLAN_CAPABILITY_PRIVACY) ^
+                    !!sdata->default_key))
+                       continue;
+
+               if (!(ifsta->flags & IEEE80211_STA_AUTO_CHANNEL_SEL) &&
+                   bss->freq != freq)
+                       continue;
+
+               if (!(ifsta->flags & IEEE80211_STA_AUTO_BSSID_SEL) &&
+                   memcmp(bss->bssid, ifsta->bssid, ETH_ALEN))
+                       continue;
+
+               if (!(ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL) &&
+                   !ieee80211_sta_match_ssid(ifsta, bss->ssid, bss->ssid_len))
+                       continue;
+
+               if (!selected || top_rssi < bss->signal) {
+                       selected = bss;
+                       top_rssi = bss->signal;
+               }
+       }
+       if (selected)
+               atomic_inc(&selected->users);
+       spin_unlock_bh(&local->sta_bss_lock);
+
+       if (selected) {
+               ieee80211_set_freq(sdata, selected->freq);
+               if (!(ifsta->flags & IEEE80211_STA_SSID_SET))
+                       ieee80211_sta_set_ssid(sdata, selected->ssid,
+                                              selected->ssid_len);
+               ieee80211_sta_set_bssid(sdata, selected->bssid);
+               ieee80211_sta_def_wmm_params(sdata, selected);
+
+               /* Send out direct probe if no probe resp was received or
+                * the one we have is outdated
+                */
+               if (!selected->last_probe_resp ||
+                   time_after(jiffies, selected->last_probe_resp
+                                       + IEEE80211_SCAN_RESULT_EXPIRE))
+                       ifsta->state = IEEE80211_STA_MLME_DIRECT_PROBE;
+               else
+                       ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE;
+
+               ieee80211_rx_bss_put(local, selected);
+               ieee80211_sta_reset_auth(sdata, ifsta);
+               return 0;
+       } else {
+               if (ifsta->assoc_scan_tries < IEEE80211_ASSOC_SCANS_MAX_TRIES) {
+                       ifsta->assoc_scan_tries++;
+                       if (ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL)
+                               ieee80211_sta_start_scan(sdata, NULL, 0);
+                       else
+                               ieee80211_sta_start_scan(sdata, ifsta->ssid,
+                                                        ifsta->ssid_len);
+                       ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE;
+                       set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request);
+               } else
+                       ifsta->state = IEEE80211_STA_MLME_DISABLED;
+       }
+       return -1;
+}
+
+
 int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason)
 {
        struct ieee80211_if_sta *ifsta = &sdata->u.sta;
@@ -4378,8 +2492,7 @@ int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason
            sdata->vif.type != IEEE80211_IF_TYPE_IBSS)
                return -EINVAL;
 
-       ieee80211_send_deauth(sdata, ifsta, reason);
-       ieee80211_set_disassoc(sdata, ifsta, 1);
+       ieee80211_set_disassoc(sdata, ifsta, true, true, reason);
        return 0;
 }
 
@@ -4397,8 +2510,7 @@ int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason)
        if (!(ifsta->flags & IEEE80211_STA_ASSOCIATED))
                return -1;
 
-       ieee80211_send_disassoc(sdata, ifsta, reason);
-       ieee80211_set_disassoc(sdata, ifsta, 0);
+       ieee80211_set_disassoc(sdata, ifsta, false, true, reason);
        return 0;
 }
 
@@ -4422,3 +2534,102 @@ void ieee80211_notify_mac(struct ieee80211_hw *hw,
        }
 }
 EXPORT_SYMBOL(ieee80211_notify_mac);
+
+void ieee80211_sta_work(struct work_struct *work)
+{
+       struct ieee80211_sub_if_data *sdata =
+               container_of(work, struct ieee80211_sub_if_data, u.sta.work);
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_sta *ifsta;
+       struct sk_buff *skb;
+
+       if (!netif_running(sdata->dev))
+               return;
+
+       if (local->sta_sw_scanning || local->sta_hw_scanning)
+               return;
+
+       if (WARN_ON(sdata->vif.type != IEEE80211_IF_TYPE_STA &&
+                   sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
+                   sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT))
+               return;
+       ifsta = &sdata->u.sta;
+
+       while ((skb = skb_dequeue(&ifsta->skb_queue)))
+               ieee80211_sta_rx_queued_mgmt(sdata, skb);
+
+#ifdef CONFIG_MAC80211_MESH
+       if (ifsta->preq_queue_len &&
+           time_after(jiffies,
+                      ifsta->last_preq + msecs_to_jiffies(ifsta->mshcfg.dot11MeshHWMPpreqMinInterval)))
+               mesh_path_start_discovery(sdata);
+#endif
+
+       if (ifsta->state != IEEE80211_STA_MLME_DIRECT_PROBE &&
+           ifsta->state != IEEE80211_STA_MLME_AUTHENTICATE &&
+           ifsta->state != IEEE80211_STA_MLME_ASSOCIATE &&
+           test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request)) {
+               ieee80211_sta_start_scan(sdata, ifsta->scan_ssid, ifsta->scan_ssid_len);
+               return;
+       }
+
+       if (test_and_clear_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request)) {
+               if (ieee80211_sta_config_auth(sdata, ifsta))
+                       return;
+               clear_bit(IEEE80211_STA_REQ_RUN, &ifsta->request);
+       } else if (!test_and_clear_bit(IEEE80211_STA_REQ_RUN, &ifsta->request))
+               return;
+
+       switch (ifsta->state) {
+       case IEEE80211_STA_MLME_DISABLED:
+               break;
+       case IEEE80211_STA_MLME_DIRECT_PROBE:
+               ieee80211_direct_probe(sdata, ifsta);
+               break;
+       case IEEE80211_STA_MLME_AUTHENTICATE:
+               ieee80211_authenticate(sdata, ifsta);
+               break;
+       case IEEE80211_STA_MLME_ASSOCIATE:
+               ieee80211_associate(sdata, ifsta);
+               break;
+       case IEEE80211_STA_MLME_ASSOCIATED:
+               ieee80211_associated(sdata, ifsta);
+               break;
+       case IEEE80211_STA_MLME_IBSS_SEARCH:
+               ieee80211_sta_find_ibss(sdata, ifsta);
+               break;
+       case IEEE80211_STA_MLME_IBSS_JOINED:
+               ieee80211_sta_merge_ibss(sdata, ifsta);
+               break;
+#ifdef CONFIG_MAC80211_MESH
+       case IEEE80211_STA_MLME_MESH_UP:
+               ieee80211_mesh_housekeeping(sdata, ifsta);
+               break;
+#endif
+       default:
+               WARN_ON(1);
+               break;
+       }
+
+       if (ieee80211_privacy_mismatch(sdata, ifsta)) {
+               printk(KERN_DEBUG "%s: privacy configuration mismatch and "
+                      "mixed-cell disabled - disassociate\n", sdata->dev->name);
+
+               ieee80211_set_disassoc(sdata, ifsta, false, true,
+                                       WLAN_REASON_UNSPECIFIED);
+       }
+}
+
+void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
+{
+       struct ieee80211_sub_if_data *sdata = local->scan_sdata;
+       struct ieee80211_if_sta *ifsta;
+
+       if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
+               ifsta = &sdata->u.sta;
+               if (!(ifsta->flags & IEEE80211_STA_BSSID_SET) ||
+                   (!(ifsta->state == IEEE80211_STA_MLME_IBSS_JOINED) &&
+                   !ieee80211_sta_active_ibss(sdata)))
+                       ieee80211_sta_find_ibss(sdata, ifsta);
+       }
+}
index 7e09b30dd3937508968c1864bb2c414da63aebc7..d0803797902be43d353f7449366279a6c65c46c6 100644 (file)
@@ -1510,23 +1510,96 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
        return RX_CONTINUE;
 }
 
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
+{
+       struct ieee80211_local *local = rx->local;
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
+       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
+       int len = rx->skb->len;
+
+       if (!ieee80211_is_action(mgmt->frame_control))
+               return RX_CONTINUE;
+
+       if (!rx->sta)
+               return RX_DROP_MONITOR;
+
+       if (!(rx->flags & IEEE80211_RX_RA_MATCH))
+               return RX_DROP_MONITOR;
+
+       /* all categories we currently handle have action_code */
+       if (len < IEEE80211_MIN_ACTION_SIZE + 1)
+               return RX_DROP_MONITOR;
+
+       /*
+        * FIXME: revisit this, I'm sure we should handle most
+        *        of these frames in other modes as well!
+        */
+       if (sdata->vif.type != IEEE80211_IF_TYPE_STA &&
+           sdata->vif.type != IEEE80211_IF_TYPE_IBSS)
+               return RX_DROP_MONITOR;
+
+       switch (mgmt->u.action.category) {
+       case WLAN_CATEGORY_BACK:
+               switch (mgmt->u.action.u.addba_req.action_code) {
+               case WLAN_ACTION_ADDBA_REQ:
+                       if (len < (IEEE80211_MIN_ACTION_SIZE +
+                                  sizeof(mgmt->u.action.u.addba_req)))
+                               return RX_DROP_MONITOR;
+                       ieee80211_process_addba_request(local, rx->sta, mgmt, len);
+                       break;
+               case WLAN_ACTION_ADDBA_RESP:
+                       if (len < (IEEE80211_MIN_ACTION_SIZE +
+                                  sizeof(mgmt->u.action.u.addba_resp)))
+                               return RX_DROP_MONITOR;
+                       ieee80211_process_addba_resp(local, rx->sta, mgmt, len);
+                       break;
+               case WLAN_ACTION_DELBA:
+                       if (len < (IEEE80211_MIN_ACTION_SIZE +
+                                  sizeof(mgmt->u.action.u.delba)))
+                               return RX_DROP_MONITOR;
+                       ieee80211_process_delba(sdata, rx->sta, mgmt, len);
+                       break;
+               }
+               break;
+       case WLAN_CATEGORY_SPECTRUM_MGMT:
+               if (local->hw.conf.channel->band != IEEE80211_BAND_5GHZ)
+                       return RX_DROP_MONITOR;
+               switch (mgmt->u.action.u.measurement.action_code) {
+               case WLAN_ACTION_SPCT_MSR_REQ:
+                       if (len < (IEEE80211_MIN_ACTION_SIZE +
+                                  sizeof(mgmt->u.action.u.measurement)))
+                               return RX_DROP_MONITOR;
+                       ieee80211_process_measurement_req(sdata, mgmt, len);
+                       break;
+               }
+               break;
+       default:
+               return RX_CONTINUE;
+       }
+
+       rx->sta->rx_packets++;
+       dev_kfree_skb(rx->skb);
+       return RX_QUEUED;
+}
+
 static ieee80211_rx_result debug_noinline
 ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
 {
-       struct ieee80211_sub_if_data *sdata;
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
 
        if (!(rx->flags & IEEE80211_RX_RA_MATCH))
                return RX_DROP_MONITOR;
 
-       sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
-       if ((sdata->vif.type == IEEE80211_IF_TYPE_STA ||
-            sdata->vif.type == IEEE80211_IF_TYPE_IBSS ||
-            sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) &&
-           !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
-               ieee80211_sta_rx_mgmt(sdata, rx->skb, rx->status);
-       else
+       if (sdata->vif.type != IEEE80211_IF_TYPE_STA &&
+           sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
+           sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
+               return RX_DROP_MONITOR;
+
+       if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)
                return RX_DROP_MONITOR;
 
+       ieee80211_sta_rx_mgmt(sdata, rx->skb, rx->status);
        return RX_QUEUED;
 }
 
@@ -1689,6 +1762,7 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_sub_if_data *sdata,
                CALL_RXH(ieee80211_rx_h_mesh_fwding);
        CALL_RXH(ieee80211_rx_h_data)
        CALL_RXH(ieee80211_rx_h_ctrl)
+       CALL_RXH(ieee80211_rx_h_action)
        CALL_RXH(ieee80211_rx_h_mgmt)
 
 #undef CALL_RXH
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
new file mode 100644 (file)
index 0000000..010781b
--- /dev/null
@@ -0,0 +1,933 @@
+/*
+ * Scanning implementation
+ *
+ * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright 2004, Instant802 Networks, Inc.
+ * Copyright 2005, Devicescape Software, Inc.
+ * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
+ * Copyright 2007, Michael Wu <flamingice@sourmilk.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.
+ */
+
+/* TODO:
+ * order BSS list by RSSI(?) ("quality of AP")
+ * scan result table filtering (by capability (privacy, IBSS/BSS, WPA/RSN IE,
+ *    SSID)
+ */
+
+#include <linux/wireless.h>
+#include <linux/if_arp.h>
+#include <net/mac80211.h>
+#include <net/iw_handler.h>
+
+#include "ieee80211_i.h"
+#include "mesh.h"
+
+#define IEEE80211_PROBE_DELAY (HZ / 33)
+#define IEEE80211_CHANNEL_TIME (HZ / 33)
+#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 5)
+
+void ieee80211_rx_bss_list_init(struct ieee80211_local *local)
+{
+       spin_lock_init(&local->sta_bss_lock);
+       INIT_LIST_HEAD(&local->sta_bss_list);
+}
+
+void ieee80211_rx_bss_list_deinit(struct ieee80211_local *local)
+{
+       struct ieee80211_sta_bss *bss, *tmp;
+
+       list_for_each_entry_safe(bss, tmp, &local->sta_bss_list, list)
+               ieee80211_rx_bss_put(local, bss);
+}
+
+struct ieee80211_sta_bss *
+ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq,
+                    u8 *ssid, u8 ssid_len)
+{
+       struct ieee80211_sta_bss *bss;
+
+       spin_lock_bh(&local->sta_bss_lock);
+       bss = local->sta_bss_hash[STA_HASH(bssid)];
+       while (bss) {
+               if (!bss_mesh_cfg(bss) &&
+                   !memcmp(bss->bssid, bssid, ETH_ALEN) &&
+                   bss->freq == freq &&
+                   bss->ssid_len == ssid_len &&
+                   (ssid_len == 0 || !memcmp(bss->ssid, ssid, ssid_len))) {
+                       atomic_inc(&bss->users);
+                       break;
+               }
+               bss = bss->hnext;
+       }
+       spin_unlock_bh(&local->sta_bss_lock);
+       return bss;
+}
+
+/* Caller must hold local->sta_bss_lock */
+static void __ieee80211_rx_bss_hash_add(struct ieee80211_local *local,
+                                       struct ieee80211_sta_bss *bss)
+{
+       u8 hash_idx;
+
+       if (bss_mesh_cfg(bss))
+               hash_idx = mesh_id_hash(bss_mesh_id(bss),
+                                       bss_mesh_id_len(bss));
+       else
+               hash_idx = STA_HASH(bss->bssid);
+
+       bss->hnext = local->sta_bss_hash[hash_idx];
+       local->sta_bss_hash[hash_idx] = bss;
+}
+
+/* Caller must hold local->sta_bss_lock */
+static void __ieee80211_rx_bss_hash_del(struct ieee80211_local *local,
+                                       struct ieee80211_sta_bss *bss)
+{
+       struct ieee80211_sta_bss *b, *prev = NULL;
+       b = local->sta_bss_hash[STA_HASH(bss->bssid)];
+       while (b) {
+               if (b == bss) {
+                       if (!prev)
+                               local->sta_bss_hash[STA_HASH(bss->bssid)] =
+                                       bss->hnext;
+                       else
+                               prev->hnext = bss->hnext;
+                       break;
+               }
+               prev = b;
+               b = b->hnext;
+       }
+}
+
+struct ieee80211_sta_bss *
+ieee80211_rx_bss_add(struct ieee80211_local *local, u8 *bssid, int freq,
+                    u8 *ssid, u8 ssid_len)
+{
+       struct ieee80211_sta_bss *bss;
+
+       bss = kzalloc(sizeof(*bss), GFP_ATOMIC);
+       if (!bss)
+               return NULL;
+       atomic_set(&bss->users, 2);
+       memcpy(bss->bssid, bssid, ETH_ALEN);
+       bss->freq = freq;
+       if (ssid && ssid_len <= IEEE80211_MAX_SSID_LEN) {
+               memcpy(bss->ssid, ssid, ssid_len);
+               bss->ssid_len = ssid_len;
+       }
+
+       spin_lock_bh(&local->sta_bss_lock);
+       /* TODO: order by RSSI? */
+       list_add_tail(&bss->list, &local->sta_bss_list);
+       __ieee80211_rx_bss_hash_add(local, bss);
+       spin_unlock_bh(&local->sta_bss_lock);
+       return bss;
+}
+
+#ifdef CONFIG_MAC80211_MESH
+static struct ieee80211_sta_bss *
+ieee80211_rx_mesh_bss_get(struct ieee80211_local *local, u8 *mesh_id, int mesh_id_len,
+                         u8 *mesh_cfg, int freq)
+{
+       struct ieee80211_sta_bss *bss;
+
+       spin_lock_bh(&local->sta_bss_lock);
+       bss = local->sta_bss_hash[mesh_id_hash(mesh_id, mesh_id_len)];
+       while (bss) {
+               if (bss_mesh_cfg(bss) &&
+                   !memcmp(bss_mesh_cfg(bss), mesh_cfg, MESH_CFG_CMP_LEN) &&
+                   bss->freq == freq &&
+                   mesh_id_len == bss->mesh_id_len &&
+                   (mesh_id_len == 0 || !memcmp(bss->mesh_id, mesh_id,
+                                                mesh_id_len))) {
+                       atomic_inc(&bss->users);
+                       break;
+               }
+               bss = bss->hnext;
+       }
+       spin_unlock_bh(&local->sta_bss_lock);
+       return bss;
+}
+
+static struct ieee80211_sta_bss *
+ieee80211_rx_mesh_bss_add(struct ieee80211_local *local, u8 *mesh_id, int mesh_id_len,
+                         u8 *mesh_cfg, int mesh_config_len, int freq)
+{
+       struct ieee80211_sta_bss *bss;
+
+       if (mesh_config_len != MESH_CFG_LEN)
+               return NULL;
+
+       bss = kzalloc(sizeof(*bss), GFP_ATOMIC);
+       if (!bss)
+               return NULL;
+
+       bss->mesh_cfg = kmalloc(MESH_CFG_CMP_LEN, GFP_ATOMIC);
+       if (!bss->mesh_cfg) {
+               kfree(bss);
+               return NULL;
+       }
+
+       if (mesh_id_len && mesh_id_len <= IEEE80211_MAX_MESH_ID_LEN) {
+               bss->mesh_id = kmalloc(mesh_id_len, GFP_ATOMIC);
+               if (!bss->mesh_id) {
+                       kfree(bss->mesh_cfg);
+                       kfree(bss);
+                       return NULL;
+               }
+               memcpy(bss->mesh_id, mesh_id, mesh_id_len);
+       }
+
+       atomic_set(&bss->users, 2);
+       memcpy(bss->mesh_cfg, mesh_cfg, MESH_CFG_CMP_LEN);
+       bss->mesh_id_len = mesh_id_len;
+       bss->freq = freq;
+       spin_lock_bh(&local->sta_bss_lock);
+       /* TODO: order by RSSI? */
+       list_add_tail(&bss->list, &local->sta_bss_list);
+       __ieee80211_rx_bss_hash_add(local, bss);
+       spin_unlock_bh(&local->sta_bss_lock);
+       return bss;
+}
+#endif
+
+static void ieee80211_rx_bss_free(struct ieee80211_sta_bss *bss)
+{
+       kfree(bss->ies);
+       kfree(bss_mesh_id(bss));
+       kfree(bss_mesh_cfg(bss));
+       kfree(bss);
+}
+
+void ieee80211_rx_bss_put(struct ieee80211_local *local,
+                         struct ieee80211_sta_bss *bss)
+{
+       local_bh_disable();
+       if (!atomic_dec_and_lock(&bss->users, &local->sta_bss_lock)) {
+               local_bh_enable();
+               return;
+       }
+
+       __ieee80211_rx_bss_hash_del(local, bss);
+       list_del(&bss->list);
+       spin_unlock_bh(&local->sta_bss_lock);
+       ieee80211_rx_bss_free(bss);
+}
+
+struct ieee80211_sta_bss *
+ieee80211_bss_info_update(struct ieee80211_local *local,
+                         struct ieee80211_rx_status *rx_status,
+                         struct ieee80211_mgmt *mgmt,
+                         size_t len,
+                         struct ieee802_11_elems *elems,
+                         int freq, bool beacon)
+{
+       struct ieee80211_sta_bss *bss;
+       int clen;
+
+#ifdef CONFIG_MAC80211_MESH
+       if (elems->mesh_config)
+               bss = ieee80211_rx_mesh_bss_get(local, elems->mesh_id,
+                               elems->mesh_id_len, elems->mesh_config, freq);
+       else
+#endif
+               bss = ieee80211_rx_bss_get(local, mgmt->bssid, freq,
+                                          elems->ssid, elems->ssid_len);
+       if (!bss) {
+#ifdef CONFIG_MAC80211_MESH
+               if (elems->mesh_config)
+                       bss = ieee80211_rx_mesh_bss_add(local, elems->mesh_id,
+                               elems->mesh_id_len, elems->mesh_config,
+                               elems->mesh_config_len, freq);
+               else
+#endif
+                       bss = ieee80211_rx_bss_add(local, mgmt->bssid, freq,
+                                                 elems->ssid, elems->ssid_len);
+               if (!bss)
+                       return NULL;
+       } else {
+#if 0
+               /* TODO: order by RSSI? */
+               spin_lock_bh(&local->sta_bss_lock);
+               list_move_tail(&bss->list, &local->sta_bss_list);
+               spin_unlock_bh(&local->sta_bss_lock);
+#endif
+       }
+
+       /* save the ERP value so that it is available at association time */
+       if (elems->erp_info && elems->erp_info_len >= 1) {
+               bss->erp_value = elems->erp_info[0];
+               bss->has_erp_value = 1;
+       }
+
+       bss->beacon_int = le16_to_cpu(mgmt->u.beacon.beacon_int);
+       bss->capability = le16_to_cpu(mgmt->u.beacon.capab_info);
+
+       if (elems->tim) {
+               struct ieee80211_tim_ie *tim_ie =
+                       (struct ieee80211_tim_ie *)elems->tim;
+               bss->dtim_period = tim_ie->dtim_period;
+       }
+
+       /* set default value for buggy APs */
+       if (!elems->tim || bss->dtim_period == 0)
+               bss->dtim_period = 1;
+
+       bss->supp_rates_len = 0;
+       if (elems->supp_rates) {
+               clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
+               if (clen > elems->supp_rates_len)
+                       clen = elems->supp_rates_len;
+               memcpy(&bss->supp_rates[bss->supp_rates_len], elems->supp_rates,
+                      clen);
+               bss->supp_rates_len += clen;
+       }
+       if (elems->ext_supp_rates) {
+               clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
+               if (clen > elems->ext_supp_rates_len)
+                       clen = elems->ext_supp_rates_len;
+               memcpy(&bss->supp_rates[bss->supp_rates_len],
+                      elems->ext_supp_rates, clen);
+               bss->supp_rates_len += clen;
+       }
+
+       bss->band = rx_status->band;
+
+       bss->timestamp = le64_to_cpu(mgmt->u.beacon.timestamp);
+       bss->last_update = jiffies;
+       bss->signal = rx_status->signal;
+       bss->noise = rx_status->noise;
+       bss->qual = rx_status->qual;
+       bss->wmm_used = elems->wmm_param || elems->wmm_info;
+
+       if (!beacon)
+               bss->last_probe_resp = jiffies;
+
+       /*
+        * For probe responses, or if we don't have any information yet,
+        * use the IEs from the beacon.
+        */
+       if (!bss->ies || !beacon) {
+               if (bss->ies == NULL || bss->ies_len < elems->total_len) {
+                       kfree(bss->ies);
+                       bss->ies = kmalloc(elems->total_len, GFP_ATOMIC);
+               }
+               if (bss->ies) {
+                       memcpy(bss->ies, elems->ie_start, elems->total_len);
+                       bss->ies_len = elems->total_len;
+               } else
+                       bss->ies_len = 0;
+       }
+
+       return bss;
+}
+
+ieee80211_rx_result
+ieee80211_sta_rx_scan(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
+                     struct ieee80211_rx_status *rx_status)
+{
+       struct ieee80211_mgmt *mgmt;
+       struct ieee80211_sta_bss *bss;
+       u8 *elements;
+       struct ieee80211_channel *channel;
+       size_t baselen;
+       int freq;
+       __le16 fc;
+       bool presp, beacon = false;
+       struct ieee802_11_elems elems;
+
+       if (skb->len < 2)
+               return RX_DROP_UNUSABLE;
+
+       mgmt = (struct ieee80211_mgmt *) skb->data;
+       fc = mgmt->frame_control;
+
+       if (ieee80211_is_ctl(fc))
+               return RX_CONTINUE;
+
+       if (skb->len < 24)
+               return RX_DROP_MONITOR;
+
+       presp = ieee80211_is_probe_resp(fc);
+       if (presp) {
+               /* ignore ProbeResp to foreign address */
+               if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN))
+                       return RX_DROP_MONITOR;
+
+               presp = true;
+               elements = mgmt->u.probe_resp.variable;
+               baselen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+       } else {
+               beacon = ieee80211_is_beacon(fc);
+               baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+               elements = mgmt->u.beacon.variable;
+       }
+
+       if (!presp && !beacon)
+               return RX_CONTINUE;
+
+       if (baselen > skb->len)
+               return RX_DROP_MONITOR;
+
+       ieee802_11_parse_elems(elements, skb->len - baselen, &elems);
+
+       if (elems.ds_params && elems.ds_params_len == 1)
+               freq = ieee80211_channel_to_frequency(elems.ds_params[0]);
+       else
+               freq = rx_status->freq;
+
+       channel = ieee80211_get_channel(sdata->local->hw.wiphy, freq);
+
+       if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
+               return RX_DROP_MONITOR;
+
+       bss = ieee80211_bss_info_update(sdata->local, rx_status,
+                                       mgmt, skb->len, &elems,
+                                       freq, beacon);
+       ieee80211_rx_bss_put(sdata->local, bss);
+
+       dev_kfree_skb(skb);
+       return RX_QUEUED;
+}
+
+static void ieee80211_send_nullfunc(struct ieee80211_local *local,
+                                   struct ieee80211_sub_if_data *sdata,
+                                   int powersave)
+{
+       struct sk_buff *skb;
+       struct ieee80211_hdr *nullfunc;
+       __le16 fc;
+
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24);
+       if (!skb) {
+               printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc "
+                      "frame\n", sdata->dev->name);
+               return;
+       }
+       skb_reserve(skb, local->hw.extra_tx_headroom);
+
+       nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24);
+       memset(nullfunc, 0, 24);
+       fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
+                        IEEE80211_FCTL_TODS);
+       if (powersave)
+               fc |= cpu_to_le16(IEEE80211_FCTL_PM);
+       nullfunc->frame_control = fc;
+       memcpy(nullfunc->addr1, sdata->u.sta.bssid, ETH_ALEN);
+       memcpy(nullfunc->addr2, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(nullfunc->addr3, sdata->u.sta.bssid, ETH_ALEN);
+
+       ieee80211_tx_skb(sdata, skb, 0);
+}
+
+static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
+{
+       if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
+           ieee80211_vif_is_mesh(&sdata->vif))
+               ieee80211_sta_timer((unsigned long)sdata);
+}
+
+void ieee80211_scan_completed(struct ieee80211_hw *hw)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct ieee80211_sub_if_data *sdata;
+       union iwreq_data wrqu;
+
+       local->last_scan_completed = jiffies;
+       memset(&wrqu, 0, sizeof(wrqu));
+       wireless_send_event(local->scan_sdata->dev, SIOCGIWSCAN, &wrqu, NULL);
+
+       if (local->sta_hw_scanning) {
+               local->sta_hw_scanning = 0;
+               if (ieee80211_hw_config(local))
+                       printk(KERN_DEBUG "%s: failed to restore operational "
+                              "channel after scan\n", wiphy_name(local->hw.wiphy));
+               /* Restart STA timer for HW scan case */
+               rcu_read_lock();
+               list_for_each_entry_rcu(sdata, &local->interfaces, list)
+                       ieee80211_restart_sta_timer(sdata);
+               rcu_read_unlock();
+
+               goto done;
+       }
+
+       local->sta_sw_scanning = 0;
+       if (ieee80211_hw_config(local))
+               printk(KERN_DEBUG "%s: failed to restore operational "
+                      "channel after scan\n", wiphy_name(local->hw.wiphy));
+
+
+       netif_tx_lock_bh(local->mdev);
+       netif_addr_lock(local->mdev);
+       local->filter_flags &= ~FIF_BCN_PRBRESP_PROMISC;
+       local->ops->configure_filter(local_to_hw(local),
+                                    FIF_BCN_PRBRESP_PROMISC,
+                                    &local->filter_flags,
+                                    local->mdev->mc_count,
+                                    local->mdev->mc_list);
+
+       netif_addr_unlock(local->mdev);
+       netif_tx_unlock_bh(local->mdev);
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+               /* Tell AP we're back */
+               if (sdata->vif.type == IEEE80211_IF_TYPE_STA) {
+                       if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
+                               ieee80211_send_nullfunc(local, sdata, 0);
+                               netif_tx_wake_all_queues(sdata->dev);
+                       }
+               } else
+                       netif_tx_wake_all_queues(sdata->dev);
+
+               ieee80211_restart_sta_timer(sdata);
+       }
+       rcu_read_unlock();
+
+ done:
+       ieee80211_mlme_notify_scan_completed(local);
+}
+EXPORT_SYMBOL(ieee80211_scan_completed);
+
+
+void ieee80211_sta_scan_work(struct work_struct *work)
+{
+       struct ieee80211_local *local =
+               container_of(work, struct ieee80211_local, scan_work.work);
+       struct ieee80211_sub_if_data *sdata = local->scan_sdata;
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_channel *chan;
+       int skip;
+       unsigned long next_delay = 0;
+
+       if (!local->sta_sw_scanning)
+               return;
+
+       switch (local->scan_state) {
+       case SCAN_SET_CHANNEL:
+               /*
+                * Get current scan band. scan_band may be IEEE80211_NUM_BANDS
+                * after we successfully scanned the last channel of the last
+                * band (and the last band is supported by the hw)
+                */
+               if (local->scan_band < IEEE80211_NUM_BANDS)
+                       sband = local->hw.wiphy->bands[local->scan_band];
+               else
+                       sband = NULL;
+
+               /*
+                * If we are at an unsupported band and have more bands
+                * left to scan, advance to the next supported one.
+                */
+               while (!sband && local->scan_band < IEEE80211_NUM_BANDS - 1) {
+                       local->scan_band++;
+                       sband = local->hw.wiphy->bands[local->scan_band];
+                       local->scan_channel_idx = 0;
+               }
+
+               /* if no more bands/channels left, complete scan */
+               if (!sband || local->scan_channel_idx >= sband->n_channels) {
+                       ieee80211_scan_completed(local_to_hw(local));
+                       return;
+               }
+               skip = 0;
+               chan = &sband->channels[local->scan_channel_idx];
+
+               if (chan->flags & IEEE80211_CHAN_DISABLED ||
+                   (sdata->vif.type == IEEE80211_IF_TYPE_IBSS &&
+                    chan->flags & IEEE80211_CHAN_NO_IBSS))
+                       skip = 1;
+
+               if (!skip) {
+                       local->scan_channel = chan;
+                       if (ieee80211_hw_config(local)) {
+                               printk(KERN_DEBUG "%s: failed to set freq to "
+                                      "%d MHz for scan\n", wiphy_name(local->hw.wiphy),
+                                      chan->center_freq);
+                               skip = 1;
+                       }
+               }
+
+               /* advance state machine to next channel/band */
+               local->scan_channel_idx++;
+               if (local->scan_channel_idx >= sband->n_channels) {
+                       /*
+                        * scan_band may end up == IEEE80211_NUM_BANDS, but
+                        * we'll catch that case above and complete the scan
+                        * if that is the case.
+                        */
+                       local->scan_band++;
+                       local->scan_channel_idx = 0;
+               }
+
+               if (skip)
+                       break;
+
+               next_delay = IEEE80211_PROBE_DELAY +
+                            usecs_to_jiffies(local->hw.channel_change_time);
+               local->scan_state = SCAN_SEND_PROBE;
+               break;
+       case SCAN_SEND_PROBE:
+               next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
+               local->scan_state = SCAN_SET_CHANNEL;
+
+               if (local->scan_channel->flags & IEEE80211_CHAN_PASSIVE_SCAN)
+                       break;
+               ieee80211_send_probe_req(sdata, NULL, local->scan_ssid,
+                                        local->scan_ssid_len);
+               next_delay = IEEE80211_CHANNEL_TIME;
+               break;
+       }
+
+       if (local->sta_sw_scanning)
+               queue_delayed_work(local->hw.workqueue, &local->scan_work,
+                                  next_delay);
+}
+
+
+int ieee80211_sta_start_scan(struct ieee80211_sub_if_data *scan_sdata,
+                            u8 *ssid, size_t ssid_len)
+{
+       struct ieee80211_local *local = scan_sdata->local;
+       struct ieee80211_sub_if_data *sdata;
+
+       if (ssid_len > IEEE80211_MAX_SSID_LEN)
+               return -EINVAL;
+
+       /* MLME-SCAN.request (page 118)  page 144 (11.1.3.1)
+        * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS
+        * BSSID: MACAddress
+        * SSID
+        * ScanType: ACTIVE, PASSIVE
+        * ProbeDelay: delay (in microseconds) to be used prior to transmitting
+        *    a Probe frame during active scanning
+        * ChannelList
+        * MinChannelTime (>= ProbeDelay), in TU
+        * MaxChannelTime: (>= MinChannelTime), in TU
+        */
+
+        /* MLME-SCAN.confirm
+         * BSSDescriptionSet
+         * ResultCode: SUCCESS, INVALID_PARAMETERS
+        */
+
+       if (local->sta_sw_scanning || local->sta_hw_scanning) {
+               if (local->scan_sdata == scan_sdata)
+                       return 0;
+               return -EBUSY;
+       }
+
+       if (local->ops->hw_scan) {
+               int rc = local->ops->hw_scan(local_to_hw(local),
+                                            ssid, ssid_len);
+               if (!rc) {
+                       local->sta_hw_scanning = 1;
+                       local->scan_sdata = scan_sdata;
+               }
+               return rc;
+       }
+
+       local->sta_sw_scanning = 1;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+               if (sdata->vif.type == IEEE80211_IF_TYPE_STA) {
+                       if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
+                               netif_tx_stop_all_queues(sdata->dev);
+                               ieee80211_send_nullfunc(local, sdata, 1);
+                       }
+               } else
+                       netif_tx_stop_all_queues(sdata->dev);
+       }
+       rcu_read_unlock();
+
+       if (ssid) {
+               local->scan_ssid_len = ssid_len;
+               memcpy(local->scan_ssid, ssid, ssid_len);
+       } else
+               local->scan_ssid_len = 0;
+       local->scan_state = SCAN_SET_CHANNEL;
+       local->scan_channel_idx = 0;
+       local->scan_band = IEEE80211_BAND_2GHZ;
+       local->scan_sdata = scan_sdata;
+
+       netif_addr_lock_bh(local->mdev);
+       local->filter_flags |= FIF_BCN_PRBRESP_PROMISC;
+       local->ops->configure_filter(local_to_hw(local),
+                                    FIF_BCN_PRBRESP_PROMISC,
+                                    &local->filter_flags,
+                                    local->mdev->mc_count,
+                                    local->mdev->mc_list);
+       netif_addr_unlock_bh(local->mdev);
+
+       /* TODO: start scan as soon as all nullfunc frames are ACKed */
+       queue_delayed_work(local->hw.workqueue, &local->scan_work,
+                          IEEE80211_CHANNEL_TIME);
+
+       return 0;
+}
+
+
+int ieee80211_sta_req_scan(struct ieee80211_sub_if_data *sdata, u8 *ssid, size_t ssid_len)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_sta *ifsta;
+
+       if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
+               return ieee80211_sta_start_scan(sdata, ssid, ssid_len);
+
+       /*
+        * STA has a state machine that might need to defer scanning
+        * while it's trying to associate/authenticate, therefore we
+        * queue it up to the state machine in that case.
+        */
+
+       if (local->sta_sw_scanning || local->sta_hw_scanning) {
+               if (local->scan_sdata == sdata)
+                       return 0;
+               return -EBUSY;
+       }
+
+       ifsta = &sdata->u.sta;
+
+       ifsta->scan_ssid_len = ssid_len;
+       if (ssid_len)
+               memcpy(ifsta->scan_ssid, ssid, ssid_len);
+       set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request);
+       queue_work(local->hw.workqueue, &ifsta->work);
+
+       return 0;
+}
+
+
+static void ieee80211_sta_add_scan_ies(struct iw_request_info *info,
+                                      struct ieee80211_sta_bss *bss,
+                                      char **current_ev, char *end_buf)
+{
+       u8 *pos, *end, *next;
+       struct iw_event iwe;
+
+       if (bss == NULL || bss->ies == NULL)
+               return;
+
+       /*
+        * If needed, fragment the IEs buffer (at IE boundaries) into short
+        * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
+        */
+       pos = bss->ies;
+       end = pos + bss->ies_len;
+
+       while (end - pos > IW_GENERIC_IE_MAX) {
+               next = pos + 2 + pos[1];
+               while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX)
+                       next = next + 2 + next[1];
+
+               memset(&iwe, 0, sizeof(iwe));
+               iwe.cmd = IWEVGENIE;
+               iwe.u.data.length = next - pos;
+               *current_ev = iwe_stream_add_point(info, *current_ev,
+                                                  end_buf, &iwe, pos);
+
+               pos = next;
+       }
+
+       if (end > pos) {
+               memset(&iwe, 0, sizeof(iwe));
+               iwe.cmd = IWEVGENIE;
+               iwe.u.data.length = end - pos;
+               *current_ev = iwe_stream_add_point(info, *current_ev,
+                                                  end_buf, &iwe, pos);
+       }
+}
+
+
+static char *
+ieee80211_sta_scan_result(struct ieee80211_local *local,
+                         struct iw_request_info *info,
+                         struct ieee80211_sta_bss *bss,
+                         char *current_ev, char *end_buf)
+{
+       struct iw_event iwe;
+       char *buf;
+
+       if (time_after(jiffies,
+                      bss->last_update + IEEE80211_SCAN_RESULT_EXPIRE))
+               return current_ev;
+
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = SIOCGIWAP;
+       iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+       memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
+       current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
+                                         IW_EV_ADDR_LEN);
+
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = SIOCGIWESSID;
+       if (bss_mesh_cfg(bss)) {
+               iwe.u.data.length = bss_mesh_id_len(bss);
+               iwe.u.data.flags = 1;
+               current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+                                                 &iwe, bss_mesh_id(bss));
+       } else {
+               iwe.u.data.length = bss->ssid_len;
+               iwe.u.data.flags = 1;
+               current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+                                                 &iwe, bss->ssid);
+       }
+
+       if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)
+           || bss_mesh_cfg(bss)) {
+               memset(&iwe, 0, sizeof(iwe));
+               iwe.cmd = SIOCGIWMODE;
+               if (bss_mesh_cfg(bss))
+                       iwe.u.mode = IW_MODE_MESH;
+               else if (bss->capability & WLAN_CAPABILITY_ESS)
+                       iwe.u.mode = IW_MODE_MASTER;
+               else
+                       iwe.u.mode = IW_MODE_ADHOC;
+               current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+                                                 &iwe, IW_EV_UINT_LEN);
+       }
+
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = SIOCGIWFREQ;
+       iwe.u.freq.m = ieee80211_frequency_to_channel(bss->freq);
+       iwe.u.freq.e = 0;
+       current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
+                                         IW_EV_FREQ_LEN);
+
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = SIOCGIWFREQ;
+       iwe.u.freq.m = bss->freq;
+       iwe.u.freq.e = 6;
+       current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
+                                         IW_EV_FREQ_LEN);
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = IWEVQUAL;
+       iwe.u.qual.qual = bss->qual;
+       iwe.u.qual.level = bss->signal;
+       iwe.u.qual.noise = bss->noise;
+       iwe.u.qual.updated = local->wstats_flags;
+       current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
+                                         IW_EV_QUAL_LEN);
+
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = SIOCGIWENCODE;
+       if (bss->capability & WLAN_CAPABILITY_PRIVACY)
+               iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+       else
+               iwe.u.data.flags = IW_ENCODE_DISABLED;
+       iwe.u.data.length = 0;
+       current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+                                         &iwe, "");
+
+       ieee80211_sta_add_scan_ies(info, bss, &current_ev, end_buf);
+
+       if (bss->supp_rates_len > 0) {
+               /* display all supported rates in readable format */
+               char *p = current_ev + iwe_stream_lcp_len(info);
+               int i;
+
+               memset(&iwe, 0, sizeof(iwe));
+               iwe.cmd = SIOCGIWRATE;
+               /* Those two flags are ignored... */
+               iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+
+               for (i = 0; i < bss->supp_rates_len; i++) {
+                       iwe.u.bitrate.value = ((bss->supp_rates[i] &
+                                                       0x7f) * 500000);
+                       p = iwe_stream_add_value(info, current_ev, p,
+                                       end_buf, &iwe, IW_EV_PARAM_LEN);
+               }
+               current_ev = p;
+       }
+
+       buf = kmalloc(30, GFP_ATOMIC);
+       if (buf) {
+               memset(&iwe, 0, sizeof(iwe));
+               iwe.cmd = IWEVCUSTOM;
+               sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->timestamp));
+               iwe.u.data.length = strlen(buf);
+               current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+                                                 &iwe, buf);
+               memset(&iwe, 0, sizeof(iwe));
+               iwe.cmd = IWEVCUSTOM;
+               sprintf(buf, " Last beacon: %dms ago",
+                       jiffies_to_msecs(jiffies - bss->last_update));
+               iwe.u.data.length = strlen(buf);
+               current_ev = iwe_stream_add_point(info, current_ev,
+                                                 end_buf, &iwe, buf);
+               kfree(buf);
+       }
+
+       if (bss_mesh_cfg(bss)) {
+               u8 *cfg = bss_mesh_cfg(bss);
+               buf = kmalloc(50, GFP_ATOMIC);
+               if (buf) {
+                       memset(&iwe, 0, sizeof(iwe));
+                       iwe.cmd = IWEVCUSTOM;
+                       sprintf(buf, "Mesh network (version %d)", cfg[0]);
+                       iwe.u.data.length = strlen(buf);
+                       current_ev = iwe_stream_add_point(info, current_ev,
+                                                         end_buf,
+                                                         &iwe, buf);
+                       sprintf(buf, "Path Selection Protocol ID: "
+                               "0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3],
+                                                       cfg[4]);
+                       iwe.u.data.length = strlen(buf);
+                       current_ev = iwe_stream_add_point(info, current_ev,
+                                                         end_buf,
+                                                         &iwe, buf);
+                       sprintf(buf, "Path Selection Metric ID: "
+                               "0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7],
+                                                       cfg[8]);
+                       iwe.u.data.length = strlen(buf);
+                       current_ev = iwe_stream_add_point(info, current_ev,
+                                                         end_buf,
+                                                         &iwe, buf);
+                       sprintf(buf, "Congestion Control Mode ID: "
+                               "0x%02X%02X%02X%02X", cfg[9], cfg[10],
+                                                       cfg[11], cfg[12]);
+                       iwe.u.data.length = strlen(buf);
+                       current_ev = iwe_stream_add_point(info, current_ev,
+                                                         end_buf,
+                                                         &iwe, buf);
+                       sprintf(buf, "Channel Precedence: "
+                               "0x%02X%02X%02X%02X", cfg[13], cfg[14],
+                                                       cfg[15], cfg[16]);
+                       iwe.u.data.length = strlen(buf);
+                       current_ev = iwe_stream_add_point(info, current_ev,
+                                                         end_buf,
+                                                         &iwe, buf);
+                       kfree(buf);
+               }
+       }
+
+       return current_ev;
+}
+
+
+int ieee80211_sta_scan_results(struct ieee80211_local *local,
+                              struct iw_request_info *info,
+                              char *buf, size_t len)
+{
+       char *current_ev = buf;
+       char *end_buf = buf + len;
+       struct ieee80211_sta_bss *bss;
+
+       spin_lock_bh(&local->sta_bss_lock);
+       list_for_each_entry(bss, &local->sta_bss_list, list) {
+               if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
+                       spin_unlock_bh(&local->sta_bss_lock);
+                       return -E2BIG;
+               }
+               current_ev = ieee80211_sta_scan_result(local, info, bss,
+                                                      current_ev, end_buf);
+       }
+       spin_unlock_bh(&local->sta_bss_lock);
+       return current_ev - buf;
+}
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
new file mode 100644 (file)
index 0000000..f72bad6
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * spectrum management
+ *
+ * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright 2002-2005, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright 2006-2007  Jiri Benc <jbenc@suse.cz>
+ * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2007-2008, Intel Corporation
+ * Copyright 2008, 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.
+ */
+
+#include <linux/ieee80211.h>
+#include <net/wireless.h>
+#include <net/mac80211.h>
+#include "ieee80211_i.h"
+#include "sta_info.h"
+#include "wme.h"
+
+static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata,
+                                       struct ieee80211_msrment_ie *request_ie,
+                                       const u8 *da, const u8 *bssid,
+                                       u8 dialog_token)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *msr_report;
+
+       skb = dev_alloc_skb(sizeof(*msr_report) + local->hw.extra_tx_headroom +
+                               sizeof(struct ieee80211_msrment_ie));
+
+       if (!skb) {
+               printk(KERN_ERR "%s: failed to allocate buffer for "
+                               "measurement report frame\n", sdata->dev->name);
+               return;
+       }
+
+       skb_reserve(skb, local->hw.extra_tx_headroom);
+       msr_report = (struct ieee80211_mgmt *)skb_put(skb, 24);
+       memset(msr_report, 0, 24);
+       memcpy(msr_report->da, da, ETH_ALEN);
+       memcpy(msr_report->sa, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(msr_report->bssid, bssid, ETH_ALEN);
+       msr_report->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                               IEEE80211_STYPE_ACTION);
+
+       skb_put(skb, 1 + sizeof(msr_report->u.action.u.measurement));
+       msr_report->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
+       msr_report->u.action.u.measurement.action_code =
+                               WLAN_ACTION_SPCT_MSR_RPRT;
+       msr_report->u.action.u.measurement.dialog_token = dialog_token;
+
+       msr_report->u.action.u.measurement.element_id = WLAN_EID_MEASURE_REPORT;
+       msr_report->u.action.u.measurement.length =
+                       sizeof(struct ieee80211_msrment_ie);
+
+       memset(&msr_report->u.action.u.measurement.msr_elem, 0,
+               sizeof(struct ieee80211_msrment_ie));
+       msr_report->u.action.u.measurement.msr_elem.token = request_ie->token;
+       msr_report->u.action.u.measurement.msr_elem.mode |=
+                       IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED;
+       msr_report->u.action.u.measurement.msr_elem.type = request_ie->type;
+
+       ieee80211_tx_skb(sdata, skb, 0);
+}
+
+void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
+                                      struct ieee80211_mgmt *mgmt,
+                                      size_t len)
+{
+       /*
+        * Ignoring measurement request is spec violation.
+        * Mandatory measurements must be reported optional
+        * measurements might be refused or reported incapable
+        * For now just refuse
+        * TODO: Answer basic measurement as unmeasured
+        */
+       ieee80211_send_refuse_measurement_request(sdata,
+                       &mgmt->u.action.u.measurement.msr_elem,
+                       mgmt->sa, mgmt->bssid,
+                       mgmt->u.action.u.measurement.dialog_token);
+}
index f40c060341aebd3fd82e85061980702f447857cc..c3a22ab2ad2e46fe87a22153c82e3962177f7f7c 100644 (file)
@@ -428,3 +428,187 @@ void ieee80211_iterate_active_interfaces_atomic(
        rcu_read_unlock();
 }
 EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
+
+void ieee802_11_parse_elems(u8 *start, size_t len,
+                           struct ieee802_11_elems *elems)
+{
+       size_t left = len;
+       u8 *pos = start;
+
+       memset(elems, 0, sizeof(*elems));
+       elems->ie_start = start;
+       elems->total_len = len;
+
+       while (left >= 2) {
+               u8 id, elen;
+
+               id = *pos++;
+               elen = *pos++;
+               left -= 2;
+
+               if (elen > left)
+                       return;
+
+               switch (id) {
+               case WLAN_EID_SSID:
+                       elems->ssid = pos;
+                       elems->ssid_len = elen;
+                       break;
+               case WLAN_EID_SUPP_RATES:
+                       elems->supp_rates = pos;
+                       elems->supp_rates_len = elen;
+                       break;
+               case WLAN_EID_FH_PARAMS:
+                       elems->fh_params = pos;
+                       elems->fh_params_len = elen;
+                       break;
+               case WLAN_EID_DS_PARAMS:
+                       elems->ds_params = pos;
+                       elems->ds_params_len = elen;
+                       break;
+               case WLAN_EID_CF_PARAMS:
+                       elems->cf_params = pos;
+                       elems->cf_params_len = elen;
+                       break;
+               case WLAN_EID_TIM:
+                       elems->tim = pos;
+                       elems->tim_len = elen;
+                       break;
+               case WLAN_EID_IBSS_PARAMS:
+                       elems->ibss_params = pos;
+                       elems->ibss_params_len = elen;
+                       break;
+               case WLAN_EID_CHALLENGE:
+                       elems->challenge = pos;
+                       elems->challenge_len = elen;
+                       break;
+               case WLAN_EID_WPA:
+                       if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 &&
+                           pos[2] == 0xf2) {
+                               /* Microsoft OUI (00:50:F2) */
+                               if (pos[3] == 1) {
+                                       /* OUI Type 1 - WPA IE */
+                                       elems->wpa = pos;
+                                       elems->wpa_len = elen;
+                               } else if (elen >= 5 && pos[3] == 2) {
+                                       if (pos[4] == 0) {
+                                               elems->wmm_info = pos;
+                                               elems->wmm_info_len = elen;
+                                       } else if (pos[4] == 1) {
+                                               elems->wmm_param = pos;
+                                               elems->wmm_param_len = elen;
+                                       }
+                               }
+                       }
+                       break;
+               case WLAN_EID_RSN:
+                       elems->rsn = pos;
+                       elems->rsn_len = elen;
+                       break;
+               case WLAN_EID_ERP_INFO:
+                       elems->erp_info = pos;
+                       elems->erp_info_len = elen;
+                       break;
+               case WLAN_EID_EXT_SUPP_RATES:
+                       elems->ext_supp_rates = pos;
+                       elems->ext_supp_rates_len = elen;
+                       break;
+               case WLAN_EID_HT_CAPABILITY:
+                       elems->ht_cap_elem = pos;
+                       elems->ht_cap_elem_len = elen;
+                       break;
+               case WLAN_EID_HT_EXTRA_INFO:
+                       elems->ht_info_elem = pos;
+                       elems->ht_info_elem_len = elen;
+                       break;
+               case WLAN_EID_MESH_ID:
+                       elems->mesh_id = pos;
+                       elems->mesh_id_len = elen;
+                       break;
+               case WLAN_EID_MESH_CONFIG:
+                       elems->mesh_config = pos;
+                       elems->mesh_config_len = elen;
+                       break;
+               case WLAN_EID_PEER_LINK:
+                       elems->peer_link = pos;
+                       elems->peer_link_len = elen;
+                       break;
+               case WLAN_EID_PREQ:
+                       elems->preq = pos;
+                       elems->preq_len = elen;
+                       break;
+               case WLAN_EID_PREP:
+                       elems->prep = pos;
+                       elems->prep_len = elen;
+                       break;
+               case WLAN_EID_PERR:
+                       elems->perr = pos;
+                       elems->perr_len = elen;
+                       break;
+               case WLAN_EID_CHANNEL_SWITCH:
+                       elems->ch_switch_elem = pos;
+                       elems->ch_switch_elem_len = elen;
+                       break;
+               case WLAN_EID_QUIET:
+                       if (!elems->quiet_elem) {
+                               elems->quiet_elem = pos;
+                               elems->quiet_elem_len = elen;
+                       }
+                       elems->num_of_quiet_elem++;
+                       break;
+               case WLAN_EID_COUNTRY:
+                       elems->country_elem = pos;
+                       elems->country_elem_len = elen;
+                       break;
+               case WLAN_EID_PWR_CONSTRAINT:
+                       elems->pwr_constr_elem = pos;
+                       elems->pwr_constr_elem_len = elen;
+                       break;
+               default:
+                       break;
+               }
+
+               left -= elen;
+               pos += elen;
+       }
+}
+
+void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_tx_queue_params qparam;
+       int i;
+
+       if (!local->ops->conf_tx)
+               return;
+
+       memset(&qparam, 0, sizeof(qparam));
+
+       qparam.aifs = 2;
+
+       if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
+           !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE))
+               qparam.cw_min = 31;
+       else
+               qparam.cw_min = 15;
+
+       qparam.cw_max = 1023;
+       qparam.txop = 0;
+
+       for (i = 0; i < local_to_hw(local)->queues; i++)
+               local->ops->conf_tx(local_to_hw(local), i, &qparam);
+}
+
+void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
+                     int encrypt)
+{
+       skb->dev = sdata->local->mdev;
+       skb_set_mac_header(skb, 0);
+       skb_set_network_header(skb, 0);
+       skb_set_transport_header(skb, 0);
+
+       skb->iif = sdata->dev->ifindex;
+       skb->do_not_encrypt = !encrypt;
+
+       dev_queue_xmit(skb);
+}
This page took 0.148921 seconds and 5 git commands to generate.