]> Pileus Git - ~andy/linux/blobdiff - net/mac80211/ieee80211_sta.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/lethal/sh-2.6
[~andy/linux] / net / mac80211 / ieee80211_sta.c
index 87830c04a1caf4d1904da04efa169162b9060d9c..9aeed5320228bfd0e1df34d600c0c725498be7f9 100644 (file)
 #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 net_device *dev, u8 *dst,
                                     u8 *ssid, size_t ssid_len);
@@ -306,48 +313,42 @@ static void ieee80211_sta_wmm_params(struct net_device *dev,
 }
 
 
-static void ieee80211_handle_erp_ie(struct net_device *dev, u8 erp_value)
+static u32 ieee80211_handle_erp_ie(struct ieee80211_sub_if_data *sdata,
+                                  u8 erp_value)
 {
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_bss_conf *bss_conf = &sdata->bss_conf;
        struct ieee80211_if_sta *ifsta = &sdata->u.sta;
-       int use_protection = (erp_value & WLAN_ERP_USE_PROTECTION) != 0;
-       int preamble_mode = (erp_value & WLAN_ERP_BARKER_PREAMBLE) != 0;
-       u8 changes = 0;
+       bool use_protection = (erp_value & WLAN_ERP_USE_PROTECTION) != 0;
+       bool preamble_mode = (erp_value & WLAN_ERP_BARKER_PREAMBLE) != 0;
        DECLARE_MAC_BUF(mac);
+       u32 changed = 0;
 
-       if (use_protection != !!(sdata->flags & IEEE80211_SDATA_USE_PROTECTION)) {
+       if (use_protection != bss_conf->use_cts_prot) {
                if (net_ratelimit()) {
                        printk(KERN_DEBUG "%s: CTS protection %s (BSSID="
                               "%s)\n",
-                              dev->name,
+                              sdata->dev->name,
                               use_protection ? "enabled" : "disabled",
                               print_mac(mac, ifsta->bssid));
                }
-               if (use_protection)
-                       sdata->flags |= IEEE80211_SDATA_USE_PROTECTION;
-               else
-                       sdata->flags &= ~IEEE80211_SDATA_USE_PROTECTION;
-               changes |= IEEE80211_ERP_CHANGE_PROTECTION;
+               bss_conf->use_cts_prot = use_protection;
+               changed |= BSS_CHANGED_ERP_CTS_PROT;
        }
 
-       if (preamble_mode != !(sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE)) {
+       if (preamble_mode != bss_conf->use_short_preamble) {
                if (net_ratelimit()) {
                        printk(KERN_DEBUG "%s: switched to %s barker preamble"
                               " (BSSID=%s)\n",
-                              dev->name,
+                              sdata->dev->name,
                               (preamble_mode == WLAN_ERP_PREAMBLE_SHORT) ?
                                        "short" : "long",
                               print_mac(mac, ifsta->bssid));
                }
-               if (preamble_mode)
-                       sdata->flags &= ~IEEE80211_SDATA_SHORT_PREAMBLE;
-               else
-                       sdata->flags |= IEEE80211_SDATA_SHORT_PREAMBLE;
-               changes |= IEEE80211_ERP_CHANGE_PREAMBLE;
+               bss_conf->use_short_preamble = preamble_mode;
+               changed |= BSS_CHANGED_ERP_PREAMBLE;
        }
 
-       if (changes)
-               ieee80211_erp_info_change_notify(dev, changes);
+       return changed;
 }
 
 int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
@@ -451,20 +452,17 @@ static void ieee80211_set_associated(struct net_device *dev,
                                     struct ieee80211_if_sta *ifsta,
                                     bool assoc)
 {
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
        union iwreq_data wrqu;
-
-       if (!!(ifsta->flags & IEEE80211_STA_ASSOCIATED) == assoc)
-               return;
+       u32 changed = BSS_CHANGED_ASSOC;
 
        if (assoc) {
-               struct ieee80211_sub_if_data *sdata;
                struct ieee80211_sta_bss *bss;
 
                ifsta->flags |= IEEE80211_STA_ASSOCIATED;
 
-               sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-               if (sdata->type != IEEE80211_IF_TYPE_STA)
+               if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
                        return;
 
                bss = ieee80211_rx_bss_get(dev, ifsta->bssid,
@@ -472,7 +470,8 @@ static void ieee80211_set_associated(struct net_device *dev,
                                           ifsta->ssid, ifsta->ssid_len);
                if (bss) {
                        if (bss->has_erp_value)
-                               ieee80211_handle_erp_ie(dev, bss->erp_value);
+                               changed |= ieee80211_handle_erp_ie(
+                                               sdata, bss->erp_value);
                        ieee80211_rx_bss_put(dev, bss);
                }
 
@@ -492,6 +491,8 @@ static void ieee80211_set_associated(struct net_device *dev,
        wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
        ifsta->last_probe = jiffies;
        ieee80211_led_assoc(local, assoc);
+
+       ieee80211_bss_info_change_notify(sdata, changed);
 }
 
 static void ieee80211_set_disassoc(struct net_device *dev,
@@ -1005,7 +1006,8 @@ static void ieee80211_send_addba_resp(struct net_device *dev, u8 *da, u16 tid,
        struct ieee80211_mgmt *mgmt;
        u16 capab;
 
-       skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
+       skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom + 1 +
+                                       sizeof(mgmt->u.action.u.addba_resp));
        if (!skb) {
                printk(KERN_DEBUG "%s: failed to allocate buffer "
                       "for addba resp frame\n", dev->name);
@@ -1017,7 +1019,7 @@ static void ieee80211_send_addba_resp(struct net_device *dev, u8 *da, u16 tid,
        memset(mgmt, 0, 24);
        memcpy(mgmt->da, da, ETH_ALEN);
        memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
-       if (sdata->type == IEEE80211_IF_TYPE_AP)
+       if (sdata->vif.type == IEEE80211_IF_TYPE_AP)
                memcpy(mgmt->bssid, dev->dev_addr, ETH_ALEN);
        else
                memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
@@ -1047,9 +1049,14 @@ static void ieee80211_sta_process_addba_request(struct net_device *dev,
                                                size_t len)
 {
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+       struct ieee80211_hw *hw = &local->hw;
+       struct ieee80211_conf *conf = &hw->conf;
        struct sta_info *sta;
-       u16 capab, tid, timeout, ba_policy, buf_size, status;
+       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);
 
        sta = sta_info_get(local, mgmt->sa);
        if (!sta)
@@ -1058,28 +1065,252 @@ static void ieee80211_sta_process_addba_request(struct net_device *dev,
        /* 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;
 
-       /* TODO - currently aggregation is declined (A-MPDU add BA request
-       * acceptance is not obligatory by 802.11n draft), but here is
-       * the entry point for dealing with it */
-#ifdef MAC80211_HT_DEBUG
-       if (net_ratelimit())
-               printk(KERN_DEBUG "Add Block Ack request arrived,"
-                                  " currently denying it\n");
-#endif /* MAC80211_HT_DEBUG */
-
        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 "Block Ack 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_hw_mode *mode = conf->mode;
+               buf_size = IEEE80211_MIN_AMPDU_BUF;
+               buf_size = buf_size << mode->ht_info.ampdu_factor;
+       }
+
+       tid_agg_rx = &sta->ampdu_mlme.tid_rx[tid];
+
+       /* examine state machine */
+       spin_lock_bh(&sta->ampdu_mlme.ampdu_rx);
+
+       if (tid_agg_rx->state != HT_AGG_STATE_IDLE) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               if (net_ratelimit())
+                       printk(KERN_DEBUG "unexpected Block Ack Req from "
+                               "%s on tid %u\n",
+                               print_mac(mac, mgmt->sa), tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+               goto end;
+       }
+
+       /* prepare reordering buffer */
+       tid_agg_rx->reorder_buf =
+               kmalloc(buf_size * sizeof(struct sk_buf *), GFP_ATOMIC);
+       if (!tid_agg_rx->reorder_buf) {
+               if (net_ratelimit())
+                       printk(KERN_ERR "can not allocate reordering buffer "
+                              "to tid %d\n", tid);
+               goto end;
+       }
+       memset(tid_agg_rx->reorder_buf, 0,
+               buf_size * sizeof(struct sk_buf *));
+
+       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 on tid %d result %d", tid, ret);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+
+       if (ret) {
+               kfree(tid_agg_rx->reorder_buf);
+               goto end;
+       }
+
+       /* change state and send addba resp */
+       tid_agg_rx->state = 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->ampdu_mlme.ampdu_rx);
+
+end_no_lock:
        ieee80211_send_addba_resp(sta->dev, sta->addr, tid, dialog_token,
                                status, 1, buf_size, timeout);
        sta_info_put(sta);
 }
 
+static void ieee80211_send_delba(struct net_device *dev, const u8 *da, u16 tid,
+                                u16 initiator, u16 reason_code)
+{
+       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       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 + 1 +
+                                       sizeof(mgmt->u.action.u.delba));
+
+       if (!skb) {
+               printk(KERN_ERR "%s: failed to allocate buffer "
+                                       "for delba frame\n", 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, dev->dev_addr, ETH_ALEN);
+       if (sdata->vif.type == IEEE80211_IF_TYPE_AP)
+               memcpy(mgmt->bssid, dev->dev_addr, ETH_ALEN);
+       else
+               memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+       mgmt->frame_control = IEEE80211_FC(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(dev, skb, 0);
+}
+
+void ieee80211_sta_stop_rx_ba_session(struct net_device *dev, u8 *ra, u16 tid,
+                                       u16 initiator, u16 reason)
+{
+       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+       struct ieee80211_hw *hw = &local->hw;
+       struct sta_info *sta;
+       int ret, i;
+
+       sta = sta_info_get(local, ra);
+       if (!sta)
+               return;
+
+       /* check if TID is in operational state */
+       spin_lock_bh(&sta->ampdu_mlme.ampdu_rx);
+       if (sta->ampdu_mlme.tid_rx[tid].state
+                               != HT_AGG_STATE_OPERATIONAL) {
+               spin_unlock_bh(&sta->ampdu_mlme.ampdu_rx);
+               sta_info_put(sta);
+               return;
+       }
+       sta->ampdu_mlme.tid_rx[tid].state =
+               HT_AGG_STATE_REQ_STOP_BA_MSK |
+               (initiator << HT_AGG_STATE_INITIATOR_SHIFT);
+               spin_unlock_bh(&sta->ampdu_mlme.ampdu_rx);
+
+       /* stop HW Rx aggregation. ampdu_action existence
+        * already verified in session init so we add the BUG_ON */
+       BUG_ON(!local->ops->ampdu_action);
+
+       ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_STOP,
+                                       ra, tid, EINVAL);
+       if (ret)
+               printk(KERN_DEBUG "HW problem - can not stop rx "
+                               "aggergation 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(dev, 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;
+               }
+       }
+       kfree(sta->ampdu_mlme.tid_rx[tid].reorder_buf);
+
+       sta->ampdu_mlme.tid_rx[tid].state = HT_AGG_STATE_IDLE;
+       sta_info_put(sta);
+}
+
+static void ieee80211_sta_process_delba(struct net_device *dev,
+                       struct ieee80211_mgmt *mgmt, size_t len)
+{
+       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+       struct sta_info *sta;
+       u16 tid, params;
+       u16 initiator;
+       DECLARE_MAC_BUF(mac);
+
+       sta = sta_info_get(local, mgmt->sa);
+       if (!sta)
+               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 on tid %d reason code %d\n",
+                       print_mac(mac, mgmt->sa), tid,
+                       mgmt->u.action.u.delba.reason_code);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+
+       if (initiator == WLAN_BACK_INITIATOR)
+               ieee80211_sta_stop_rx_ba_session(dev, sta->addr, tid,
+                                                WLAN_BACK_INITIATOR, 0);
+       sta_info_put(sta);
+}
+
+/*
+ * After receiving Block Ack Request (BAR) we activated a
+ * timer after each frame arrives from the originator.
+ * if this timer expires ieee80211_sta_stop_rx_ba_session will be executed.
+ */
+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 verious sta_info are needed here, so init
+        * flow in sta_info_add 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]);
+
+       printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid);
+       ieee80211_sta_stop_rx_ba_session(sta->dev, sta->addr, (u16)*ptid,
+                                        WLAN_BACK_TIMER,
+                                        WLAN_REASON_QSTA_TIMEOUT);
+}
+
+
 static void ieee80211_rx_mgmt_auth(struct net_device *dev,
                                   struct ieee80211_if_sta *ifsta,
                                   struct ieee80211_mgmt *mgmt,
@@ -1090,7 +1321,7 @@ static void ieee80211_rx_mgmt_auth(struct net_device *dev,
        DECLARE_MAC_BUF(mac);
 
        if (ifsta->state != IEEE80211_AUTHENTICATE &&
-           sdata->type != IEEE80211_IF_TYPE_IBSS) {
+           sdata->vif.type != IEEE80211_IF_TYPE_IBSS) {
                printk(KERN_DEBUG "%s: authentication frame received from "
                       "%s, but not in authenticate state - ignored\n",
                       dev->name, print_mac(mac, mgmt->sa));
@@ -1104,7 +1335,7 @@ static void ieee80211_rx_mgmt_auth(struct net_device *dev,
                return;
        }
 
-       if (sdata->type != IEEE80211_IF_TYPE_IBSS &&
+       if (sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
            memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) {
                printk(KERN_DEBUG "%s: authentication frame received from "
                       "unknown AP (SA=%s BSSID=%s) - "
@@ -1113,7 +1344,7 @@ static void ieee80211_rx_mgmt_auth(struct net_device *dev,
                return;
        }
 
-       if (sdata->type != IEEE80211_IF_TYPE_IBSS &&
+       if (sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
            memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0) {
                printk(KERN_DEBUG "%s: authentication frame received from "
                       "unknown BSSID (SA=%s BSSID=%s) - "
@@ -1131,7 +1362,7 @@ static void ieee80211_rx_mgmt_auth(struct net_device *dev,
               dev->name, print_mac(mac, mgmt->sa), auth_alg,
               auth_transaction, status_code);
 
-       if (sdata->type == IEEE80211_IF_TYPE_IBSS) {
+       if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
                /* IEEE 802.11 standard does not require authentication in IBSS
                 * networks and most implementations do not seem to use it.
                 * However, try to reply to authentication attempts if someone
@@ -1297,18 +1528,20 @@ static void ieee80211_rx_mgmt_disassoc(struct net_device *dev,
 }
 
 
-static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev,
+static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                                         struct ieee80211_if_sta *ifsta,
                                         struct ieee80211_mgmt *mgmt,
                                         size_t len,
                                         int reassoc)
 {
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+       struct ieee80211_local *local = sdata->local;
+       struct net_device *dev = sdata->dev;
        struct ieee80211_hw_mode *mode;
        struct sta_info *sta;
        u32 rates;
        u16 capab_info, status_code, aid;
        struct ieee802_11_elems elems;
+       struct ieee80211_bss_conf *bss_conf = &sdata->bss_conf;
        u8 *pos;
        int i, j;
        DECLARE_MAC_BUF(mac);
@@ -1371,20 +1604,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev,
                return;
        }
 
-       /* it probably doesn't, but if the frame includes an ERP value then
-        * update our stored copy */
-       if (elems.erp_info && elems.erp_info_len >= 1) {
-               struct ieee80211_sta_bss *bss
-                       = ieee80211_rx_bss_get(dev, ifsta->bssid,
-                                              local->hw.conf.channel,
-                                              ifsta->ssid, ifsta->ssid_len);
-               if (bss) {
-                       bss->erp_value = elems.erp_info[0];
-                       bss->has_erp_value = 1;
-                       ieee80211_rx_bss_put(dev, bss);
-               }
-       }
-
        printk(KERN_DEBUG "%s: associated\n", dev->name);
        ifsta->aid = aid;
        ifsta->ap_capab = capab_info;
@@ -1395,6 +1614,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev,
        if (ifsta->assocresp_ies)
                memcpy(ifsta->assocresp_ies, pos, ifsta->assocresp_ies_len);
 
+       /* set AID, ieee80211_set_associated() will tell the driver */
+       bss_conf->aid = aid;
        ieee80211_set_associated(dev, ifsta, 1);
 
        /* Add STA entry for the AP */
@@ -1624,7 +1845,7 @@ static void ieee80211_rx_bss_info(struct net_device *dev,
 
        timestamp = le64_to_cpu(mgmt->u.beacon.timestamp);
 
-       if (sdata->type == IEEE80211_IF_TYPE_IBSS && beacon &&
+       if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS && beacon &&
            memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) {
 #ifdef CONFIG_MAC80211_IBSS_DEBUG
                static unsigned long last_tsf_debug = 0;
@@ -1649,7 +1870,7 @@ static void ieee80211_rx_bss_info(struct net_device *dev,
 
        ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems);
 
-       if (sdata->type == IEEE80211_IF_TYPE_IBSS && elems.supp_rates &&
+       if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS && elems.supp_rates &&
            memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0 &&
            (sta = sta_info_get(local, mgmt->sa))) {
                struct ieee80211_hw_mode *mode;
@@ -1874,11 +2095,12 @@ static void ieee80211_rx_mgmt_beacon(struct net_device *dev,
        struct ieee802_11_elems elems;
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct ieee80211_conf *conf = &local->hw.conf;
+       u32 changed = 0;
 
        ieee80211_rx_bss_info(dev, mgmt, len, rx_status, 1);
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       if (sdata->type != IEEE80211_IF_TYPE_STA)
+       if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
                return;
        ifsta = &sdata->u.sta;
 
@@ -1894,7 +2116,7 @@ static void ieee80211_rx_mgmt_beacon(struct net_device *dev,
        ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems);
 
        if (elems.erp_info && elems.erp_info_len >= 1)
-               ieee80211_handle_erp_ie(dev, elems.erp_info[0]);
+               changed |= ieee80211_handle_erp_ie(sdata, elems.erp_info[0]);
 
        if (elems.ht_cap_elem && elems.ht_info_elem &&
            elems.wmm_param && local->ops->conf_ht &&
@@ -1917,6 +2139,8 @@ static void ieee80211_rx_mgmt_beacon(struct net_device *dev,
                ieee80211_sta_wmm_params(dev, ifsta, elems.wmm_param,
                                         elems.wmm_param_len);
        }
+
+       ieee80211_bss_info_change_notify(sdata, changed);
 }
 
 
@@ -1938,7 +2162,7 @@ static void ieee80211_rx_mgmt_probe_req(struct net_device *dev,
        DECLARE_MAC_BUF(mac3);
 #endif
 
-       if (sdata->type != IEEE80211_IF_TYPE_IBSS ||
+       if (sdata->vif.type != IEEE80211_IF_TYPE_IBSS ||
            ifsta->state != IEEE80211_IBSS_JOINED ||
            len < 24 + 2 || !ifsta->probe_resp)
                return;
@@ -1994,10 +2218,10 @@ static void ieee80211_rx_mgmt_probe_req(struct net_device *dev,
        ieee80211_sta_tx(dev, skb, 0);
 }
 
-void ieee80211_rx_mgmt_action(struct net_device *dev,
-                            struct ieee80211_if_sta *ifsta,
-                            struct ieee80211_mgmt *mgmt,
-                            size_t len)
+static void ieee80211_rx_mgmt_action(struct net_device *dev,
+                                    struct ieee80211_if_sta *ifsta,
+                                    struct ieee80211_mgmt *mgmt,
+                                    size_t len)
 {
        if (len < IEEE80211_MIN_ACTION_SIZE)
                return;
@@ -2011,9 +2235,15 @@ void ieee80211_rx_mgmt_action(struct net_device *dev,
                                break;
                        ieee80211_sta_process_addba_request(dev, mgmt, len);
                        break;
+               case WLAN_ACTION_DELBA:
+                       if (len < (IEEE80211_MIN_ACTION_SIZE +
+                                  sizeof(mgmt->u.action.u.delba)))
+                               break;
+                       ieee80211_sta_process_delba(dev, mgmt, len);
+                       break;
                default:
                        if (net_ratelimit())
-                          printk(KERN_DEBUG "%s: received unsupported BACK\n",
+                          printk(KERN_DEBUG "%s: Rx unknown A-MPDU action\n",
                                        dev->name);
                        break;
                }
@@ -2098,10 +2328,10 @@ static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev,
                ieee80211_rx_mgmt_auth(dev, ifsta, mgmt, skb->len);
                break;
        case IEEE80211_STYPE_ASSOC_RESP:
-               ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len, 0);
+               ieee80211_rx_mgmt_assoc_resp(sdata, ifsta, mgmt, skb->len, 0);
                break;
        case IEEE80211_STYPE_REASSOC_RESP:
-               ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len, 1);
+               ieee80211_rx_mgmt_assoc_resp(sdata, ifsta, mgmt, skb->len, 1);
                break;
        case IEEE80211_STYPE_DEAUTH:
                ieee80211_rx_mgmt_deauth(dev, ifsta, mgmt, skb->len);
@@ -2243,10 +2473,10 @@ void ieee80211_sta_work(struct work_struct *work)
        if (local->sta_sw_scanning || local->sta_hw_scanning)
                return;
 
-       if (sdata->type != IEEE80211_IF_TYPE_STA &&
-           sdata->type != IEEE80211_IF_TYPE_IBSS) {
+       if (sdata->vif.type != IEEE80211_IF_TYPE_STA &&
+           sdata->vif.type != IEEE80211_IF_TYPE_IBSS) {
                printk(KERN_DEBUG "%s: ieee80211_sta_work: non-STA interface "
-                      "(type=%d)\n", dev->name, sdata->type);
+                      "(type=%d)\n", dev->name, sdata->vif.type);
                return;
        }
        ifsta = &sdata->u.sta;
@@ -2341,7 +2571,7 @@ void ieee80211_sta_req_auth(struct net_device *dev,
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->type != IEEE80211_IF_TYPE_STA)
+       if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
                return;
 
        if ((ifsta->flags & (IEEE80211_STA_BSSID_SET |
@@ -2463,9 +2693,8 @@ static int ieee80211_sta_join_ibss(struct net_device *dev,
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
        struct ieee80211_tx_control control;
-       struct ieee80211_rate *rate;
        struct ieee80211_hw_mode *mode;
-       struct rate_control_extra extra;
+       struct rate_selection ratesel;
        u8 *pos;
        struct ieee80211_sub_if_data *sdata;
 
@@ -2550,18 +2779,17 @@ static int ieee80211_sta_join_ibss(struct net_device *dev,
                }
 
                memset(&control, 0, sizeof(control));
-               memset(&extra, 0, sizeof(extra));
-               extra.mode = local->oper_hw_mode;
-               rate = rate_control_get_rate(local, dev, skb, &extra);
-               if (!rate) {
+               rate_control_get_rate(dev, local->oper_hw_mode, skb, &ratesel);
+               if (!ratesel.rate) {
                        printk(KERN_DEBUG "%s: Failed to determine TX rate "
                               "for IBSS beacon\n", dev->name);
                        break;
                }
+               control.vif = &sdata->vif;
                control.tx_rate =
-                       ((sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE) &&
-                       (rate->flags & IEEE80211_RATE_PREAMBLE2)) ?
-                       rate->val2 : rate->val;
+                       (sdata->bss_conf.use_short_preamble &&
+                       (ratesel.rate->flags & IEEE80211_RATE_PREAMBLE2)) ?
+                       ratesel.rate->val2 : ratesel.rate->val;
                control.antenna_sel_tx = local->hw.conf.antenna_sel_tx;
                control.power_level = local->hw.conf.power_level;
                control.flags |= IEEE80211_TXCTL_NO_ACK;
@@ -2811,7 +3039,7 @@ int ieee80211_sta_set_ssid(struct net_device *dev, char *ssid, size_t len)
                ifsta->flags |= IEEE80211_STA_SSID_SET;
        else
                ifsta->flags &= ~IEEE80211_STA_SSID_SET;
-       if (sdata->type == IEEE80211_IF_TYPE_IBSS &&
+       if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS &&
            !(ifsta->flags & IEEE80211_STA_BSSID_SET)) {
                ifsta->ibss_join_req = jiffies;
                ifsta->state = IEEE80211_IBSS_SEARCH;
@@ -2929,7 +3157,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
                if (sdata->dev == local->mdev)
                        continue;
 
-               if (sdata->type == IEEE80211_IF_TYPE_STA) {
+               if (sdata->vif.type == IEEE80211_IF_TYPE_STA) {
                        if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED)
                                ieee80211_send_nullfunc(local, sdata, 0);
                        ieee80211_sta_timer((unsigned long)sdata);
@@ -2941,7 +3169,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
 
 done:
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       if (sdata->type == IEEE80211_IF_TYPE_IBSS) {
+       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_IBSS_JOINED &&
@@ -2976,7 +3204,7 @@ void ieee80211_sta_scan_work(struct work_struct *work)
                skip = !(local->enabled_modes & (1 << mode->mode));
                chan = &mode->channels[local->scan_channel_idx];
                if (!(chan->flag & IEEE80211_CHAN_W_SCAN) ||
-                   (sdata->type == IEEE80211_IF_TYPE_IBSS &&
+                   (sdata->vif.type == IEEE80211_IF_TYPE_IBSS &&
                     !(chan->flag & IEEE80211_CHAN_W_IBSS)) ||
                    (local->hw_modes & local->enabled_modes &
                     (1 << MODE_IEEE80211G) && mode->mode == MODE_IEEE80211B))
@@ -3084,7 +3312,7 @@ static int ieee80211_sta_start_scan(struct net_device *dev,
                        continue;
 
                netif_stop_queue(sdata->dev);
-               if (sdata->type == IEEE80211_IF_TYPE_STA &&
+               if (sdata->vif.type == IEEE80211_IF_TYPE_STA &&
                    (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED))
                        ieee80211_send_nullfunc(local, sdata, 1);
        }
@@ -3125,7 +3353,7 @@ int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len)
        struct ieee80211_if_sta *ifsta = &sdata->u.sta;
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 
-       if (sdata->type != IEEE80211_IF_TYPE_STA)
+       if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
                return ieee80211_sta_start_scan(dev, ssid, ssid_len);
 
        if (local->sta_sw_scanning || local->sta_hw_scanning) {
@@ -3260,31 +3488,6 @@ ieee80211_sta_scan_result(struct net_device *dev,
                }
        }
 
-       do {
-               char *buf;
-
-               buf = kmalloc(100, GFP_ATOMIC);
-               if (!buf)
-                       break;
-
-               memset(&iwe, 0, sizeof(iwe));
-               iwe.cmd = IWEVCUSTOM;
-               sprintf(buf, "bcn_int=%d", bss->beacon_int);
-               iwe.u.data.length = strlen(buf);
-               current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
-                                                 buf);
-
-               memset(&iwe, 0, sizeof(iwe));
-               iwe.cmd = IWEVCUSTOM;
-               sprintf(buf, "capab=0x%04x", bss->capability);
-               iwe.u.data.length = strlen(buf);
-               current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
-                                                 buf);
-
-               kfree(buf);
-               break;
-       } while (0);
-
        return current_ev;
 }
 
@@ -3373,8 +3576,8 @@ int ieee80211_sta_deauthenticate(struct net_device *dev, u16 reason)
        printk(KERN_DEBUG "%s: deauthenticate(reason=%d)\n",
               dev->name, reason);
 
-       if (sdata->type != IEEE80211_IF_TYPE_STA &&
-           sdata->type != IEEE80211_IF_TYPE_IBSS)
+       if (sdata->vif.type != IEEE80211_IF_TYPE_STA &&
+           sdata->vif.type != IEEE80211_IF_TYPE_IBSS)
                return -EINVAL;
 
        ieee80211_send_deauth(dev, ifsta, reason);
@@ -3391,7 +3594,7 @@ int ieee80211_sta_disassociate(struct net_device *dev, u16 reason)
        printk(KERN_DEBUG "%s: disassociate(reason=%d)\n",
               dev->name, reason);
 
-       if (sdata->type != IEEE80211_IF_TYPE_STA)
+       if (sdata->vif.type != IEEE80211_IF_TYPE_STA)
                return -EINVAL;
 
        if (!(ifsta->flags & IEEE80211_STA_ASSOCIATED))