]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
Merge git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
authorJohn W. Linville <linville@tuxdriver.com>
Tue, 22 Jul 2014 17:49:34 +0000 (13:49 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 22 Jul 2014 17:49:34 +0000 (13:49 -0400)
19 files changed:
include/linux/ieee80211.h
include/net/mac80211.h
net/mac80211/agg-rx.c
net/mac80211/chan.c
net/mac80211/ht.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/key.c
net/mac80211/mlme.c
net/mac80211/rx.c
net/mac80211/sta_info.h
net/mac80211/tdls.c
net/mac80211/util.c
net/mac80211/vht.c
net/mac80211/wpa.c
net/wireless/Kconfig
net/wireless/genregdb.awk
net/wireless/nl80211.c

index 75d17e15da338c1bcb05d1b27283ea577050b3aa..63ab3873c5ed00ac6fa36e73892d4db709c13bf0 100644 (file)
@@ -1001,6 +1001,26 @@ struct ieee80211_vendor_ie {
        u8 oui_type;
 } __packed;
 
+struct ieee80211_wmm_ac_param {
+       u8 aci_aifsn; /* AIFSN, ACM, ACI */
+       u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
+       __le16 txop_limit;
+} __packed;
+
+struct ieee80211_wmm_param_ie {
+       u8 element_id; /* Element ID: 221 (0xdd); */
+       u8 len; /* Length: 24 */
+       /* required fields for WMM version 1 */
+       u8 oui[3]; /* 00:50:f2 */
+       u8 oui_type; /* 2 */
+       u8 oui_subtype; /* 1 */
+       u8 version; /* 1 for WMM version 1.0 */
+       u8 qos_info; /* AP/STA specific QoS info */
+       u8 reserved; /* 0 */
+       /* AC_BE, AC_BK, AC_VI, AC_VO */
+       struct ieee80211_wmm_ac_param ac[4];
+} __packed;
+
 /* Control frames */
 struct ieee80211_rts {
        __le16 frame_control;
index 9ce5cb17ed826a83979d787b9a43e0f19d14f383..dae2e24616e16bf3051af3dd31414f91f4220af9 100644 (file)
@@ -4552,6 +4552,40 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
  */
 void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn);
 
+/**
+ * ieee80211_start_rx_ba_session_offl - start a Rx BA session
+ *
+ * Some device drivers may offload part of the Rx aggregation flow including
+ * AddBa/DelBa negotiation but may otherwise be incapable of full Rx
+ * reordering.
+ *
+ * Create structures responsible for reordering so device drivers may call here
+ * when they complete AddBa negotiation.
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback
+ * @addr: station mac address
+ * @tid: the rx tid
+ */
+void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif,
+                                       const u8 *addr, u16 tid);
+
+/**
+ * ieee80211_stop_rx_ba_session_offl - stop a Rx BA session
+ *
+ * Some device drivers may offload part of the Rx aggregation flow including
+ * AddBa/DelBa negotiation but may otherwise be incapable of full Rx
+ * reordering.
+ *
+ * Destroy structures responsible for reordering so device drivers may call here
+ * when they complete DelBa negotiation.
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback
+ * @addr: station mac address
+ * @tid: the rx tid
+ */
+void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif,
+                                      const u8 *addr, u16 tid);
+
 /* Rate control API */
 
 /**
index 31bf2586fb84a59a0b8ce964c1717e2555a7ea65..f0e84bc48038932b022a0623ab1e2bf11a9fdfaf 100644 (file)
@@ -52,7 +52,7 @@ static void ieee80211_free_tid_rx(struct rcu_head *h)
        del_timer_sync(&tid_rx->reorder_timer);
 
        for (i = 0; i < tid_rx->buf_size; i++)
-               dev_kfree_skb(tid_rx->reorder_buf[i]);
+               __skb_queue_purge(&tid_rx->reorder_buf[i]);
        kfree(tid_rx->reorder_buf);
        kfree(tid_rx->reorder_time);
        kfree(tid_rx);
@@ -224,28 +224,15 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
        ieee80211_tx_skb(sdata, skb);
 }
 
-void ieee80211_process_addba_request(struct ieee80211_local *local,
-                                    struct sta_info *sta,
-                                    struct ieee80211_mgmt *mgmt,
-                                    size_t len)
+void __ieee80211_start_rx_ba_session(struct sta_info *sta,
+                                    u8 dialog_token, u16 timeout,
+                                    u16 start_seq_num, u16 ba_policy, u16 tid,
+                                    u16 buf_size, bool tx)
 {
+       struct ieee80211_local *local = sta->sdata->local;
        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;
-
-       /* 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;
+       int i, ret = -EOPNOTSUPP;
+       u16 status = WLAN_STATUS_REQUEST_DECLINED;
 
        if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
                ht_dbg(sta->sdata,
@@ -264,7 +251,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
                status = WLAN_STATUS_INVALID_QOS_PARAM;
                ht_dbg_ratelimited(sta->sdata,
                                   "AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n",
-                                  mgmt->sa, tid, ba_policy, buf_size);
+                                  sta->sta.addr, tid, ba_policy, buf_size);
                goto end_no_lock;
        }
        /* determine default buffer size */
@@ -281,7 +268,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
        if (sta->ampdu_mlme.tid_rx[tid]) {
                ht_dbg_ratelimited(sta->sdata,
                                   "unexpected AddBA Req from %pM on tid %u\n",
-                                  mgmt->sa, tid);
+                                  sta->sta.addr, tid);
 
                /* delete existing Rx BA session on the same tid */
                ___ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
@@ -308,7 +295,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
 
        /* prepare reordering buffer */
        tid_agg_rx->reorder_buf =
-               kcalloc(buf_size, sizeof(struct sk_buff *), GFP_KERNEL);
+               kcalloc(buf_size, sizeof(struct sk_buff_head), GFP_KERNEL);
        tid_agg_rx->reorder_time =
                kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL);
        if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) {
@@ -318,6 +305,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
                goto end;
        }
 
+       for (i = 0; i < buf_size; i++)
+               __skb_queue_head_init(&tid_agg_rx->reorder_buf[i]);
+
        ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START,
                               &sta->sta, tid, &start_seq_num, 0);
        ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n",
@@ -350,6 +340,74 @@ end:
        mutex_unlock(&sta->ampdu_mlme.mtx);
 
 end_no_lock:
-       ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,
-                                 dialog_token, status, 1, buf_size, timeout);
+       if (tx)
+               ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,
+                                         dialog_token, status, 1, buf_size,
+                                         timeout);
+}
+
+void ieee80211_process_addba_request(struct ieee80211_local *local,
+                                    struct sta_info *sta,
+                                    struct ieee80211_mgmt *mgmt,
+                                    size_t len)
+{
+       u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num;
+       u8 dialog_token;
+
+       /* 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;
+
+       __ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
+                                       start_seq_num, ba_policy, tid,
+                                       buf_size, true);
+}
+
+void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif,
+                                       const u8 *addr, u16 tid)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_rx_agg *rx_agg;
+       struct sk_buff *skb = dev_alloc_skb(0);
+
+       if (unlikely(!skb))
+               return;
+
+       rx_agg = (struct ieee80211_rx_agg *) &skb->cb;
+       memcpy(&rx_agg->addr, addr, ETH_ALEN);
+       rx_agg->tid = tid;
+
+       skb->pkt_type = IEEE80211_SDATA_QUEUE_RX_AGG_START;
+       skb_queue_tail(&sdata->skb_queue, skb);
+       ieee80211_queue_work(&local->hw, &sdata->work);
+}
+EXPORT_SYMBOL(ieee80211_start_rx_ba_session_offl);
+
+void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif,
+                                      const u8 *addr, u16 tid)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_rx_agg *rx_agg;
+       struct sk_buff *skb = dev_alloc_skb(0);
+
+       if (unlikely(!skb))
+               return;
+
+       rx_agg = (struct ieee80211_rx_agg *) &skb->cb;
+       memcpy(&rx_agg->addr, addr, ETH_ALEN);
+       rx_agg->tid = tid;
+
+       skb->pkt_type = IEEE80211_SDATA_QUEUE_RX_AGG_STOP;
+       skb_queue_tail(&sdata->skb_queue, skb);
+       ieee80211_queue_work(&local->hw, &sdata->work);
 }
+EXPORT_SYMBOL(ieee80211_stop_rx_ba_session_offl);
index c3fd4d275bf42dbd77ebd12f6b7212fcc5f9817c..6d537f03c0baa0450eefc5c22f496be085072cfd 100644 (file)
@@ -66,7 +66,7 @@ static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local)
 static struct ieee80211_chanctx *
 ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata)
 {
-       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_local *local __maybe_unused = sdata->local;
        struct ieee80211_chanctx_conf *conf;
 
        conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
index 15702ff64a4c89fb89541f620b9da7dcccc2a7bd..ff630be2ca750182fabfce3ea363a44cd24d7928 100644 (file)
@@ -150,13 +150,12 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
 
        /*
         * If user has specified capability over-rides, take care
-        * of that if the station we're setting up is the AP that
+        * of that if the station we're setting up is the AP or TDLS peer that
         * we advertised a restricted capability set to. Override
         * our own capabilities and then use those below.
         */
-       if ((sdata->vif.type == NL80211_IFTYPE_STATION ||
-            sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
-           !test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+       if (sdata->vif.type == NL80211_IFTYPE_STATION ||
+           sdata->vif.type == NL80211_IFTYPE_ADHOC)
                ieee80211_apply_htcap_overrides(sdata, &own_cap);
 
        /*
@@ -228,6 +227,9 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
        if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
                ht_cap.mcs.rx_mask[32/8] |= 1;
 
+       /* set Rx highest rate */
+       ht_cap.mcs.rx_highest = ht_cap_ie->mcs.rx_highest;
+
  apply:
        changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
 
index 713485f9effc01000d33369b056236307822f838..9713dc54ea4bb385abf1d48ae891ff5fceb7440f 100644 (file)
@@ -189,17 +189,8 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
                                                 chandef, 0);
        }
 
-       if (local->hw.queues >= IEEE80211_NUM_ACS) {
-               *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; /* U-APSD no in use */
-       }
+       if (local->hw.queues >= IEEE80211_NUM_ACS)
+               pos = ieee80211_add_wmm_info_ie(pos, 0); /* U-APSD not in use */
 
        presp->head_len = pos - presp->head;
        if (WARN_ON(presp->head_len > frame_len))
index 9e025e1184cc3b95fb9adfcdc768f0cecf5e35ee..ef7a089ac54647eb763b8bbb0793bef98454b48d 100644 (file)
@@ -345,7 +345,6 @@ enum ieee80211_sta_flags {
        IEEE80211_STA_CONNECTION_POLL   = BIT(1),
        IEEE80211_STA_CONTROL_PORT      = BIT(2),
        IEEE80211_STA_DISABLE_HT        = BIT(4),
-       IEEE80211_STA_CSA_RECEIVED      = BIT(5),
        IEEE80211_STA_MFP_ENABLED       = BIT(6),
        IEEE80211_STA_UAPSD_ENABLED     = BIT(7),
        IEEE80211_STA_NULLFUNC_ACKED    = BIT(8),
@@ -503,6 +502,9 @@ struct ieee80211_if_managed {
        struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */
        struct ieee80211_vht_cap vht_capa; /* configured VHT overrides */
        struct ieee80211_vht_cap vht_capa_mask; /* Valid parts of vht_capa */
+
+       u8 tdls_peer[ETH_ALEN] __aligned(2);
+       struct delayed_work tdls_peer_del_work;
 };
 
 struct ieee80211_if_ibss {
@@ -815,9 +817,6 @@ struct ieee80211_sub_if_data {
        bool radar_required;
        struct delayed_work dfs_cac_timer_work;
 
-       u8 tdls_peer[ETH_ALEN] __aligned(2);
-       struct delayed_work tdls_peer_del_work;
-
        /*
         * AP this belongs to: self in AP mode and
         * corresponding AP in VLAN mode, NULL for
@@ -926,10 +925,17 @@ ieee80211_vif_get_shift(struct ieee80211_vif *vif)
        return shift;
 }
 
+struct ieee80211_rx_agg {
+       u8 addr[ETH_ALEN];
+       u16 tid;
+};
+
 enum sdata_queue_type {
        IEEE80211_SDATA_QUEUE_TYPE_FRAME        = 0,
        IEEE80211_SDATA_QUEUE_AGG_START         = 1,
        IEEE80211_SDATA_QUEUE_AGG_STOP          = 2,
+       IEEE80211_SDATA_QUEUE_RX_AGG_START      = 3,
+       IEEE80211_SDATA_QUEUE_RX_AGG_STOP       = 4,
 };
 
 enum {
@@ -1578,6 +1584,10 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
                                     u16 initiator, u16 reason, bool stop);
 void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
                                    u16 initiator, u16 reason, bool stop);
+void __ieee80211_start_rx_ba_session(struct sta_info *sta,
+                                    u8 dialog_token, u16 timeout,
+                                    u16 start_seq_num, u16 ba_policy, u16 tid,
+                                    u16 buf_size, bool tx);
 void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
                                         enum ieee80211_agg_stop_reason reason);
 void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
@@ -1730,6 +1740,21 @@ static inline void ieee802_11_parse_elems(const u8 *start, size_t len,
        ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0);
 }
 
+static inline bool ieee80211_rx_reorder_ready(struct sk_buff_head *frames)
+{
+       struct sk_buff *tail = skb_peek_tail(frames);
+       struct ieee80211_rx_status *status;
+
+       if (!tail)
+               return false;
+
+       status = IEEE80211_SKB_RXCB(tail);
+       if (status->flag & RX_FLAG_AMSDU_MORE)
+               return false;
+
+       return true;
+}
+
 void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
 void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
 void ieee80211_dynamic_ps_timer(unsigned long data);
@@ -1824,6 +1849,7 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
 int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
                                struct sk_buff *skb, bool need_basic,
                                enum ieee80211_band band);
+u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo);
 
 /* channel management */
 void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
index bbf51b2f0651cf17e3dfec900fc2ce725b28c75d..29be8854a027556310482298c152894b34cf8d2b 100644 (file)
@@ -1140,6 +1140,7 @@ static void ieee80211_iface_work(struct work_struct *work)
        struct sk_buff *skb;
        struct sta_info *sta;
        struct ieee80211_ra_tid *ra_tid;
+       struct ieee80211_rx_agg *rx_agg;
 
        if (!ieee80211_sdata_running(sdata))
                return;
@@ -1167,6 +1168,34 @@ static void ieee80211_iface_work(struct work_struct *work)
                        ra_tid = (void *)&skb->cb;
                        ieee80211_stop_tx_ba_cb(&sdata->vif, ra_tid->ra,
                                                ra_tid->tid);
+               } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_START) {
+                       rx_agg = (void *)&skb->cb;
+                       mutex_lock(&local->sta_mtx);
+                       sta = sta_info_get_bss(sdata, rx_agg->addr);
+                       if (sta) {
+                               u16 last_seq;
+
+                               last_seq = le16_to_cpu(
+                                       sta->last_seq_ctrl[rx_agg->tid]);
+
+                               __ieee80211_start_rx_ba_session(sta,
+                                               0, 0,
+                                               ieee80211_sn_inc(last_seq),
+                                               1, rx_agg->tid,
+                                               IEEE80211_MAX_AMPDU_BUF,
+                                               false);
+                       }
+                       mutex_unlock(&local->sta_mtx);
+               } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_STOP) {
+                       rx_agg = (void *)&skb->cb;
+                       mutex_lock(&local->sta_mtx);
+                       sta = sta_info_get_bss(sdata, rx_agg->addr);
+                       if (sta)
+                               __ieee80211_stop_rx_ba_session(sta,
+                                                       rx_agg->tid,
+                                                       WLAN_BACK_RECIPIENT, 0,
+                                                       false);
+                       mutex_unlock(&local->sta_mtx);
                } else if (ieee80211_is_action(mgmt->frame_control) &&
                           mgmt->u.action.category == WLAN_CATEGORY_BACK) {
                        int len = skb->len;
@@ -1672,8 +1701,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
                          ieee80211_dfs_cac_timer_work);
        INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk,
                          ieee80211_delayed_tailroom_dec);
-       INIT_DELAYED_WORK(&sdata->tdls_peer_del_work,
-                         ieee80211_tdls_peer_del_work);
 
        for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
                struct ieee80211_supported_band *sband;
index 16d97f044a202e61020f3ef563941e4c9adace0a..d808cff8015374ddfb927d5695a3d3973bcc0428 100644 (file)
@@ -482,9 +482,6 @@ int ieee80211_key_link(struct ieee80211_key *key,
        int idx, ret;
        bool pairwise;
 
-       if (WARN_ON(!sdata || !key))
-               return -EINVAL;
-
        pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE;
        idx = key->conf.keyidx;
        key->local = sdata->local;
index 931330bbe00c589c33393c6b3869737641c9b307..31a8afaf73323bc09fb99f8e960b17c3fa2e31fd 100644 (file)
@@ -830,16 +830,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                        qos_info = 0;
                }
 
-               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++ = qos_info;
+               pos = ieee80211_add_wmm_info_ie(skb_put(skb, 9), qos_info);
        }
 
        /* add any remaining custom (i.e. vendor specific here) IEs */
@@ -1005,8 +996,6 @@ static void ieee80211_chswitch_work(struct work_struct *work)
                sdata->csa_block_tx = false;
        }
 
-       ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
-
        ieee80211_sta_reset_beacon_monitor(sdata);
        ieee80211_sta_reset_conn_monitor(sdata);
 
@@ -1064,7 +1053,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                return;
 
        /* disregard subsequent announcements if we are already processing */
-       if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
+       if (sdata->vif.csa_active)
                return;
 
        current_band = cbss->channel->band;
@@ -1091,8 +1080,6 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                return;
        }
 
-       ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
-
        mutex_lock(&local->mtx);
        mutex_lock(&local->chanctx_mtx);
        conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
@@ -2108,8 +2095,6 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
                               WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
                               true, frame_buf);
-       ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
-
        mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
        if (sdata->csa_block_tx) {
@@ -3722,6 +3707,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
        INIT_WORK(&ifmgd->csa_connection_drop_work,
                  ieee80211_csa_connection_drop_work);
        INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work);
+       INIT_DELAYED_WORK(&ifmgd->tdls_peer_del_work,
+                         ieee80211_tdls_peer_del_work);
        setup_timer(&ifmgd->timer, ieee80211_sta_timer,
                    (unsigned long) sdata);
        setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
@@ -4585,6 +4572,7 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
        cancel_work_sync(&ifmgd->request_smps_work);
        cancel_work_sync(&ifmgd->csa_connection_drop_work);
        cancel_work_sync(&ifmgd->chswitch_work);
+       cancel_delayed_work_sync(&ifmgd->tdls_peer_del_work);
 
        sdata_lock(sdata);
        if (ifmgd->assoc_data) {
index 5f572bed176100d634a64ba5fcc8af181ce543be..bd2c9b22c945669f8ec1d48e3959101fd9ce6754 100644 (file)
@@ -688,20 +688,27 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
                                            int index,
                                            struct sk_buff_head *frames)
 {
-       struct sk_buff *skb = tid_agg_rx->reorder_buf[index];
+       struct sk_buff_head *skb_list = &tid_agg_rx->reorder_buf[index];
+       struct sk_buff *skb;
        struct ieee80211_rx_status *status;
 
        lockdep_assert_held(&tid_agg_rx->reorder_lock);
 
-       if (!skb)
+       if (skb_queue_empty(skb_list))
+               goto no_frame;
+
+       if (!ieee80211_rx_reorder_ready(skb_list)) {
+               __skb_queue_purge(skb_list);
                goto no_frame;
+       }
 
-       /* release the frame from the reorder ring buffer */
+       /* release frames from the reorder ring buffer */
        tid_agg_rx->stored_mpdu_num--;
-       tid_agg_rx->reorder_buf[index] = NULL;
-       status = IEEE80211_SKB_RXCB(skb);
-       status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE;
-       __skb_queue_tail(frames, skb);
+       while ((skb = __skb_dequeue(skb_list))) {
+               status = IEEE80211_SKB_RXCB(skb);
+               status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE;
+               __skb_queue_tail(frames, skb);
+       }
 
 no_frame:
        tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num);
@@ -738,13 +745,13 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
                                          struct tid_ampdu_rx *tid_agg_rx,
                                          struct sk_buff_head *frames)
 {
-       int index, j;
+       int index, i, j;
 
        lockdep_assert_held(&tid_agg_rx->reorder_lock);
 
        /* release the buffer until next missing frame */
        index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
-       if (!tid_agg_rx->reorder_buf[index] &&
+       if (!ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index]) &&
            tid_agg_rx->stored_mpdu_num) {
                /*
                 * No buffers ready to be released, but check whether any
@@ -753,7 +760,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
                int skipped = 1;
                for (j = (index + 1) % tid_agg_rx->buf_size; j != index;
                     j = (j + 1) % tid_agg_rx->buf_size) {
-                       if (!tid_agg_rx->reorder_buf[j]) {
+                       if (!ieee80211_rx_reorder_ready(
+                                       &tid_agg_rx->reorder_buf[j])) {
                                skipped++;
                                continue;
                        }
@@ -762,6 +770,11 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
                                        HT_RX_REORDER_BUF_TIMEOUT))
                                goto set_release_timer;
 
+                       /* don't leave incomplete A-MSDUs around */
+                       for (i = (index + 1) % tid_agg_rx->buf_size; i != j;
+                            i = (i + 1) % tid_agg_rx->buf_size)
+                               __skb_queue_purge(&tid_agg_rx->reorder_buf[i]);
+
                        ht_dbg_ratelimited(sdata,
                                           "release an RX reorder frame due to timeout on earlier frames\n");
                        ieee80211_release_reorder_frame(sdata, tid_agg_rx, j,
@@ -775,7 +788,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
                                 skipped) & IEEE80211_SN_MASK;
                        skipped = 0;
                }
-       } else while (tid_agg_rx->reorder_buf[index]) {
+       } else while (ieee80211_rx_reorder_ready(
+                               &tid_agg_rx->reorder_buf[index])) {
                ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,
                                                frames);
                index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
@@ -786,7 +800,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
 
                for (; j != (index - 1) % tid_agg_rx->buf_size;
                     j = (j + 1) % tid_agg_rx->buf_size) {
-                       if (tid_agg_rx->reorder_buf[j])
+                       if (ieee80211_rx_reorder_ready(
+                                       &tid_agg_rx->reorder_buf[j]))
                                break;
                }
 
@@ -811,6 +826,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
                                             struct sk_buff_head *frames)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        u16 sc = le16_to_cpu(hdr->seq_ctrl);
        u16 mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
        u16 head_seq_num, buf_size;
@@ -845,7 +861,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
        index = mpdu_seq_num % tid_agg_rx->buf_size;
 
        /* check if we already stored this frame */
-       if (tid_agg_rx->reorder_buf[index]) {
+       if (ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index])) {
                dev_kfree_skb(skb);
                goto out;
        }
@@ -858,17 +874,20 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
         */
        if (mpdu_seq_num == tid_agg_rx->head_seq_num &&
            tid_agg_rx->stored_mpdu_num == 0) {
-               tid_agg_rx->head_seq_num =
-                       ieee80211_sn_inc(tid_agg_rx->head_seq_num);
+               if (!(status->flag & RX_FLAG_AMSDU_MORE))
+                       tid_agg_rx->head_seq_num =
+                               ieee80211_sn_inc(tid_agg_rx->head_seq_num);
                ret = false;
                goto out;
        }
 
        /* put the frame in the reordering buffer */
-       tid_agg_rx->reorder_buf[index] = skb;
-       tid_agg_rx->reorder_time[index] = jiffies;
-       tid_agg_rx->stored_mpdu_num++;
-       ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames);
+       __skb_queue_tail(&tid_agg_rx->reorder_buf[index], skb);
+       if (!(status->flag & RX_FLAG_AMSDU_MORE)) {
+               tid_agg_rx->reorder_time[index] = jiffies;
+               tid_agg_rx->stored_mpdu_num++;
+               ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames);
+       }
 
  out:
        spin_unlock(&tid_agg_rx->reorder_lock);
@@ -3129,6 +3148,14 @@ static bool prepare_for_handlers(struct ieee80211_rx_data *rx,
                        if (!ieee80211_is_beacon(hdr->frame_control))
                                return false;
                        status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
+               } else if (!ieee80211_has_tods(hdr->frame_control)) {
+                       /* ignore data frames to TDLS-peers */
+                       if (ieee80211_is_data(hdr->frame_control))
+                               return false;
+                       /* ignore action frames to TDLS-peers */
+                       if (ieee80211_is_action(hdr->frame_control) &&
+                           !ether_addr_equal(bssid, hdr->addr1))
+                               return false;
                }
                break;
        case NL80211_IFTYPE_WDS:
index 2a04361b2162308358dc5f06796b94fd34aa5c29..d411bcc8ef085215b8369fc0f066a49fd65fc6eb 100644 (file)
@@ -47,6 +47,8 @@
  * @WLAN_STA_TDLS_PEER: Station is a TDLS peer.
  * @WLAN_STA_TDLS_PEER_AUTH: This TDLS peer is authorized to send direct
  *     packets. This means the link is enabled.
+ * @WLAN_STA_TDLS_INITIATOR: We are the initiator of the TDLS link with this
+ *     station.
  * @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was
  *     keeping station in power-save mode, reply when the driver
  *     unblocks the station.
@@ -76,6 +78,7 @@ enum ieee80211_sta_info_flags {
        WLAN_STA_PSPOLL,
        WLAN_STA_TDLS_PEER,
        WLAN_STA_TDLS_PEER_AUTH,
+       WLAN_STA_TDLS_INITIATOR,
        WLAN_STA_UAPSD,
        WLAN_STA_SP,
        WLAN_STA_4ADDR_EVENT,
@@ -152,7 +155,8 @@ struct tid_ampdu_tx {
 /**
  * struct tid_ampdu_rx - TID aggregation information (Rx).
  *
- * @reorder_buf: buffer to reorder incoming aggregated MPDUs
+ * @reorder_buf: buffer to reorder incoming aggregated MPDUs. An MPDU may be an
+ *     A-MSDU with individually reported subframes.
  * @reorder_time: jiffies when skb was added
  * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value)
  * @reorder_timer: releases expired frames from the reorder buffer.
@@ -177,7 +181,7 @@ struct tid_ampdu_tx {
 struct tid_ampdu_rx {
        struct rcu_head rcu_head;
        spinlock_t reorder_lock;
-       struct sk_buff **reorder_buf;
+       struct sk_buff_head *reorder_buf;
        unsigned long *reorder_time;
        struct timer_list session_timer;
        struct timer_list reorder_timer;
index f7185338a0fad33508f17837041cbdb2c2d90d68..1b21050be174b29539a954991d0376dab76fc78f 100644 (file)
@@ -8,6 +8,7 @@
  */
 
 #include <linux/ieee80211.h>
+#include <linux/log2.h>
 #include <net/cfg80211.h>
 #include "ieee80211_i.h"
 #include "driver-ops.h"
@@ -21,14 +22,14 @@ void ieee80211_tdls_peer_del_work(struct work_struct *wk)
        struct ieee80211_local *local;
 
        sdata = container_of(wk, struct ieee80211_sub_if_data,
-                            tdls_peer_del_work.work);
+                            u.mgd.tdls_peer_del_work.work);
        local = sdata->local;
 
        mutex_lock(&local->mtx);
-       if (!is_zero_ether_addr(sdata->tdls_peer)) {
-               tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->tdls_peer);
-               sta_info_destroy_addr(sdata, sdata->tdls_peer);
-               eth_zero_addr(sdata->tdls_peer);
+       if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer)) {
+               tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->u.mgd.tdls_peer);
+               sta_info_destroy_addr(sdata, sdata->u.mgd.tdls_peer);
+               eth_zero_addr(sdata->u.mgd.tdls_peer);
        }
        mutex_unlock(&local->mtx);
 }
@@ -46,11 +47,16 @@ static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
        *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
 }
 
-static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
+static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata,
+                                       u16 status_code)
 {
        struct ieee80211_local *local = sdata->local;
        u16 capab;
 
+       /* The capability will be 0 when sending a failure code */
+       if (status_code != 0)
+               return 0;
+
        capab = 0;
        if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ)
                return capab;
@@ -63,19 +69,332 @@ static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
        return capab;
 }
 
-static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr,
-                                      const u8 *peer, const u8 *bssid)
+static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata,
+                                      struct sk_buff *skb, const u8 *peer,
+                                      bool initiator)
 {
        struct ieee80211_tdls_lnkie *lnkid;
+       const u8 *init_addr, *rsp_addr;
+
+       if (initiator) {
+               init_addr = sdata->vif.addr;
+               rsp_addr = peer;
+       } else {
+               init_addr = peer;
+               rsp_addr = sdata->vif.addr;
+       }
 
        lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
 
        lnkid->ie_type = WLAN_EID_LINK_ID;
        lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
 
-       memcpy(lnkid->bssid, bssid, ETH_ALEN);
-       memcpy(lnkid->init_sta, src_addr, ETH_ALEN);
-       memcpy(lnkid->resp_sta, peer, ETH_ALEN);
+       memcpy(lnkid->bssid, sdata->u.mgd.bssid, ETH_ALEN);
+       memcpy(lnkid->init_sta, init_addr, ETH_ALEN);
+       memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
+}
+
+/* translate numbering in the WMM parameter IE to the mac80211 notation */
+static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac)
+{
+       switch (ac) {
+       default:
+               WARN_ON_ONCE(1);
+       case 0:
+               return IEEE80211_AC_BE;
+       case 1:
+               return IEEE80211_AC_BK;
+       case 2:
+               return IEEE80211_AC_VI;
+       case 3:
+               return IEEE80211_AC_VO;
+       }
+}
+
+static u8 ieee80211_wmm_aci_aifsn(int aifsn, bool acm, int aci)
+{
+       u8 ret;
+
+       ret = aifsn & 0x0f;
+       if (acm)
+               ret |= 0x10;
+       ret |= (aci << 5) & 0x60;
+       return ret;
+}
+
+static u8 ieee80211_wmm_ecw(u16 cw_min, u16 cw_max)
+{
+       return ((ilog2(cw_min + 1) << 0x0) & 0x0f) |
+              ((ilog2(cw_max + 1) << 0x4) & 0xf0);
+}
+
+static void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata,
+                                           struct sk_buff *skb)
+{
+       struct ieee80211_wmm_param_ie *wmm;
+       struct ieee80211_tx_queue_params *txq;
+       int i;
+
+       wmm = (void *)skb_put(skb, sizeof(*wmm));
+       memset(wmm, 0, sizeof(*wmm));
+
+       wmm->element_id = WLAN_EID_VENDOR_SPECIFIC;
+       wmm->len = sizeof(*wmm) - 2;
+
+       wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */
+       wmm->oui[1] = 0x50;
+       wmm->oui[2] = 0xf2;
+       wmm->oui_type = 2; /* WME */
+       wmm->oui_subtype = 1; /* WME param */
+       wmm->version = 1; /* WME ver */
+       wmm->qos_info = 0; /* U-APSD not in use */
+
+       /*
+        * Use the EDCA parameters defined for the BSS, or default if the AP
+        * doesn't support it, as mandated by 802.11-2012 section 10.22.4
+        */
+       for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+               txq = &sdata->tx_conf[ieee80211_ac_from_wmm(i)];
+               wmm->ac[i].aci_aifsn = ieee80211_wmm_aci_aifsn(txq->aifs,
+                                                              txq->acm, i);
+               wmm->ac[i].cw = ieee80211_wmm_ecw(txq->cw_min, txq->cw_max);
+               wmm->ac[i].txop_limit = cpu_to_le16(txq->txop);
+       }
+}
+
+static void
+ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
+                                  struct sk_buff *skb, const u8 *peer,
+                                  u8 action_code, bool initiator,
+                                  const u8 *extra_ies, size_t extra_ies_len)
+{
+       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_sta_ht_cap ht_cap;
+       struct sta_info *sta = NULL;
+       size_t offset = 0, noffset;
+       u8 *pos;
+
+       rcu_read_lock();
+
+       /* we should have the peer STA if we're already responding */
+       if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
+               sta = sta_info_get(sdata, peer);
+               if (WARN_ON_ONCE(!sta)) {
+                       rcu_read_unlock();
+                       return;
+               }
+       }
+
+       ieee80211_add_srates_ie(sdata, skb, false, band);
+       ieee80211_add_ext_srates_ie(sdata, skb, false, band);
+
+       /* add any custom IEs that go before Extended Capabilities */
+       if (extra_ies_len) {
+               static const u8 before_ext_cap[] = {
+                       WLAN_EID_SUPP_RATES,
+                       WLAN_EID_COUNTRY,
+                       WLAN_EID_EXT_SUPP_RATES,
+                       WLAN_EID_SUPPORTED_CHANNELS,
+                       WLAN_EID_RSN,
+               };
+               noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+                                            before_ext_cap,
+                                            ARRAY_SIZE(before_ext_cap),
+                                            offset);
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, extra_ies + offset, noffset - offset);
+               offset = noffset;
+       }
+
+       ieee80211_tdls_add_ext_capab(skb);
+
+       /* add the QoS element if we support it */
+       if (local->hw.queues >= IEEE80211_NUM_ACS &&
+           action_code != WLAN_PUB_ACTION_TDLS_DISCOVER_RES)
+               ieee80211_add_wmm_info_ie(skb_put(skb, 9), 0); /* no U-APSD */
+
+       /* add any custom IEs that go before HT capabilities */
+       if (extra_ies_len) {
+               static const u8 before_ht_cap[] = {
+                       WLAN_EID_SUPP_RATES,
+                       WLAN_EID_COUNTRY,
+                       WLAN_EID_EXT_SUPP_RATES,
+                       WLAN_EID_SUPPORTED_CHANNELS,
+                       WLAN_EID_RSN,
+                       WLAN_EID_EXT_CAPABILITY,
+                       WLAN_EID_QOS_CAPA,
+                       WLAN_EID_FAST_BSS_TRANSITION,
+                       WLAN_EID_TIMEOUT_INTERVAL,
+                       WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+               };
+               noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+                                            before_ht_cap,
+                                            ARRAY_SIZE(before_ht_cap),
+                                            offset);
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, extra_ies + offset, noffset - offset);
+               offset = noffset;
+       }
+
+       /*
+        * with TDLS we can switch channels, and HT-caps are not necessarily
+        * the same on all bands. The specification limits the setup to a
+        * single HT-cap, so use the current band for now.
+        */
+       sband = local->hw.wiphy->bands[band];
+       memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+       if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
+            action_code == WLAN_TDLS_SETUP_RESPONSE) &&
+           ht_cap.ht_supported && (!sta || sta->sta.ht_cap.ht_supported)) {
+               if (action_code == WLAN_TDLS_SETUP_REQUEST) {
+                       ieee80211_apply_htcap_overrides(sdata, &ht_cap);
+
+                       /* disable SMPS in TDLS initiator */
+                       ht_cap.cap |= (WLAN_HT_CAP_SM_PS_DISABLED
+                                      << IEEE80211_HT_CAP_SM_PS_SHIFT);
+               } else {
+                       /* disable SMPS in TDLS responder */
+                       sta->sta.ht_cap.cap |=
+                               (WLAN_HT_CAP_SM_PS_DISABLED
+                                << IEEE80211_HT_CAP_SM_PS_SHIFT);
+
+                       /* the peer caps are already intersected with our own */
+                       memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap));
+               }
+
+               pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+               ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
+       }
+
+       rcu_read_unlock();
+
+       /* add any remaining IEs */
+       if (extra_ies_len) {
+               noffset = extra_ies_len;
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, extra_ies + offset, noffset - offset);
+       }
+
+       ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+}
+
+static void
+ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
+                                struct sk_buff *skb, const u8 *peer,
+                                bool initiator, const u8 *extra_ies,
+                                size_t extra_ies_len)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       size_t offset = 0, noffset;
+       struct sta_info *sta, *ap_sta;
+       u8 *pos;
+
+       rcu_read_lock();
+
+       sta = sta_info_get(sdata, peer);
+       ap_sta = sta_info_get(sdata, ifmgd->bssid);
+       if (WARN_ON_ONCE(!sta || !ap_sta)) {
+               rcu_read_unlock();
+               return;
+       }
+
+       /* add any custom IEs that go before the QoS IE */
+       if (extra_ies_len) {
+               static const u8 before_qos[] = {
+                       WLAN_EID_RSN,
+               };
+               noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+                                            before_qos,
+                                            ARRAY_SIZE(before_qos),
+                                            offset);
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, extra_ies + offset, noffset - offset);
+               offset = noffset;
+       }
+
+       /* add the QoS param IE if both the peer and we support it */
+       if (local->hw.queues >= IEEE80211_NUM_ACS &&
+           test_sta_flag(sta, WLAN_STA_WME))
+               ieee80211_tdls_add_wmm_param_ie(sdata, skb);
+
+       /* add any custom IEs that go before HT operation */
+       if (extra_ies_len) {
+               static const u8 before_ht_op[] = {
+                       WLAN_EID_RSN,
+                       WLAN_EID_QOS_CAPA,
+                       WLAN_EID_FAST_BSS_TRANSITION,
+                       WLAN_EID_TIMEOUT_INTERVAL,
+               };
+               noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+                                            before_ht_op,
+                                            ARRAY_SIZE(before_ht_op),
+                                            offset);
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, extra_ies + offset, noffset - offset);
+               offset = noffset;
+       }
+
+       /* if HT support is only added in TDLS, we need an HT-operation IE */
+       if (!ap_sta->sta.ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) {
+               struct ieee80211_chanctx_conf *chanctx_conf =
+                               rcu_dereference(sdata->vif.chanctx_conf);
+               if (!WARN_ON(!chanctx_conf)) {
+                       pos = skb_put(skb, 2 +
+                                     sizeof(struct ieee80211_ht_operation));
+                       /* send an empty HT operation IE */
+                       ieee80211_ie_build_ht_oper(pos, &sta->sta.ht_cap,
+                                                  &chanctx_conf->def, 0);
+               }
+       }
+
+       rcu_read_unlock();
+
+       /* add any remaining IEs */
+       if (extra_ies_len) {
+               noffset = extra_ies_len;
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, extra_ies + offset, noffset - offset);
+       }
+
+       ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+}
+
+static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
+                                  struct sk_buff *skb, const u8 *peer,
+                                  u8 action_code, u16 status_code,
+                                  bool initiator, const u8 *extra_ies,
+                                  size_t extra_ies_len)
+{
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+       case WLAN_TDLS_SETUP_RESPONSE:
+       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+               if (status_code == 0)
+                       ieee80211_tdls_add_setup_start_ies(sdata, skb, peer,
+                                                          action_code,
+                                                          initiator,
+                                                          extra_ies,
+                                                          extra_ies_len);
+               break;
+       case WLAN_TDLS_SETUP_CONFIRM:
+               if (status_code == 0)
+                       ieee80211_tdls_add_setup_cfm_ies(sdata, skb, peer,
+                                                        initiator, extra_ies,
+                                                        extra_ies_len);
+               break;
+       case WLAN_TDLS_TEARDOWN:
+       case WLAN_TDLS_DISCOVERY_REQUEST:
+               if (extra_ies_len)
+                       memcpy(skb_put(skb, extra_ies_len), extra_ies,
+                              extra_ies_len);
+               if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN)
+                       ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+               break;
+       }
+
 }
 
 static int
@@ -84,7 +403,6 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
                               u16 status_code, struct sk_buff *skb)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
        struct ieee80211_tdls_data *tf;
 
        tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
@@ -102,11 +420,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
                skb_put(skb, sizeof(tf->u.setup_req));
                tf->u.setup_req.dialog_token = dialog_token;
                tf->u.setup_req.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
-               ieee80211_add_srates_ie(sdata, skb, false, band);
-               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
-               ieee80211_tdls_add_ext_capab(skb);
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
+                                                                status_code));
                break;
        case WLAN_TDLS_SETUP_RESPONSE:
                tf->category = WLAN_CATEGORY_TDLS;
@@ -116,11 +431,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
                tf->u.setup_resp.status_code = cpu_to_le16(status_code);
                tf->u.setup_resp.dialog_token = dialog_token;
                tf->u.setup_resp.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
-               ieee80211_add_srates_ie(sdata, skb, false, band);
-               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
-               ieee80211_tdls_add_ext_capab(skb);
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
+                                                                status_code));
                break;
        case WLAN_TDLS_SETUP_CONFIRM:
                tf->category = WLAN_CATEGORY_TDLS;
@@ -157,7 +469,6 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
                           u16 status_code, struct sk_buff *skb)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
        struct ieee80211_mgmt *mgmt;
 
        mgmt = (void *)skb_put(skb, 24);
@@ -178,11 +489,8 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
                mgmt->u.action.u.tdls_discover_resp.dialog_token =
                        dialog_token;
                mgmt->u.action.u.tdls_discover_resp.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
-               ieee80211_add_srates_ie(sdata, skb, false, band);
-               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
-               ieee80211_tdls_add_ext_capab(skb);
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
+                                                                status_code));
                break;
        default:
                return -EINVAL;
@@ -202,7 +510,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_local *local = sdata->local;
        struct sk_buff *skb = NULL;
        bool send_direct;
-       const u8 *init_addr, *rsp_addr;
+       struct sta_info *sta;
        int ret;
 
        skb = dev_alloc_skb(local->hw.extra_tx_headroom +
@@ -210,6 +518,9 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
                                sizeof(struct ieee80211_tdls_data)) +
                            50 + /* supported rates */
                            7 + /* ext capab */
+                           26 + /* max(WMM-info, WMM-param) */
+                           2 + max(sizeof(struct ieee80211_ht_cap),
+                                   sizeof(struct ieee80211_ht_operation)) +
                            extra_ies_len +
                            sizeof(struct ieee80211_tdls_lnkie));
        if (!skb)
@@ -242,45 +553,48 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
        if (ret < 0)
                goto fail;
 
-       if (extra_ies_len)
-               memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
+       rcu_read_lock();
+       sta = sta_info_get(sdata, peer);
 
-       /* sanity check for initiator */
+       /* infer the initiator if we can, to support old userspace */
        switch (action_code) {
        case WLAN_TDLS_SETUP_REQUEST:
+               if (sta)
+                       set_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
+               /* fall-through */
        case WLAN_TDLS_SETUP_CONFIRM:
        case WLAN_TDLS_DISCOVERY_REQUEST:
-               if (!initiator) {
-                       ret = -EINVAL;
-                       goto fail;
-               }
+               initiator = true;
                break;
        case WLAN_TDLS_SETUP_RESPONSE:
+               /*
+                * In some testing scenarios, we send a request and response.
+                * Make the last packet sent take effect for the initiator
+                * value.
+                */
+               if (sta)
+                       clear_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
+               /* fall-through */
        case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
-               if (initiator) {
-                       ret = -EINVAL;
-                       goto fail;
-               }
+               initiator = false;
                break;
        case WLAN_TDLS_TEARDOWN:
                /* any value is ok */
                break;
        default:
                ret = -ENOTSUPP;
-               goto fail;
+               break;
        }
 
-       if (initiator) {
-               init_addr = sdata->vif.addr;
-               rsp_addr = peer;
-       } else {
-               init_addr = peer;
-               rsp_addr = sdata->vif.addr;
-       }
+       if (sta && test_sta_flag(sta, WLAN_STA_TDLS_INITIATOR))
+               initiator = true;
 
-       ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr,
-                                  sdata->u.mgd.bssid);
+       rcu_read_unlock();
+       if (ret < 0)
+               goto fail;
 
+       ieee80211_tdls_add_ies(sdata, skb, peer, action_code, status_code,
+                              initiator, extra_ies, extra_ies_len);
        if (send_direct) {
                ieee80211_tx_skb(sdata, skb);
                return 0;
@@ -327,8 +641,8 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
        mutex_lock(&local->mtx);
 
        /* we don't support concurrent TDLS peer setups */
-       if (!is_zero_ether_addr(sdata->tdls_peer) &&
-           !ether_addr_equal(sdata->tdls_peer, peer)) {
+       if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) &&
+           !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
                ret = -EBUSY;
                goto exit;
        }
@@ -336,15 +650,19 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
        /*
         * make sure we have a STA representing the peer so we drop or buffer
         * non-TDLS-setup frames to the peer. We can't send other packets
-        * during setup through the AP path
+        * during setup through the AP path.
+        * Allow error packets to be sent - sometimes we don't even add a STA
+        * before failing the setup.
         */
-       rcu_read_lock();
-       if (!sta_info_get(sdata, peer)) {
+       if (status_code == 0) {
+               rcu_read_lock();
+               if (!sta_info_get(sdata, peer)) {
+                       rcu_read_unlock();
+                       ret = -ENOLINK;
+                       goto exit;
+               }
                rcu_read_unlock();
-               ret = -ENOLINK;
-               goto exit;
        }
-       rcu_read_unlock();
 
        ieee80211_flush_queues(local, sdata);
 
@@ -355,9 +673,9 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
        if (ret < 0)
                goto exit;
 
-       memcpy(sdata->tdls_peer, peer, ETH_ALEN);
+       memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN);
        ieee80211_queue_delayed_work(&sdata->local->hw,
-                                    &sdata->tdls_peer_del_work,
+                                    &sdata->u.mgd.tdls_peer_del_work,
                                     TDLS_PEER_SETUP_TIMEOUT);
 
 exit:
@@ -513,11 +831,22 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
                set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
                rcu_read_unlock();
 
-               WARN_ON_ONCE(is_zero_ether_addr(sdata->tdls_peer) ||
-                            !ether_addr_equal(sdata->tdls_peer, peer));
+               WARN_ON_ONCE(is_zero_ether_addr(sdata->u.mgd.tdls_peer) ||
+                            !ether_addr_equal(sdata->u.mgd.tdls_peer, peer));
                ret = 0;
                break;
        case NL80211_TDLS_DISABLE_LINK:
+               /*
+                * The teardown message in ieee80211_tdls_mgmt_teardown() was
+                * created while the queues were stopped, so it might still be
+                * pending. Before flushing the queues we need to be sure the
+                * message is handled by the tasklet handling pending messages,
+                * otherwise we might start destroying the station before
+                * sending the teardown packet.
+                * Note that this only forces the tasklet to flush pendings -
+                * not to stop the tasklet from rescheduling itself.
+                */
+               tasklet_kill(&local->tx_pending_tasklet);
                /* flush a potentially queued teardown packet */
                ieee80211_flush_queues(local, sdata);
 
@@ -528,9 +857,9 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
                break;
        }
 
-       if (ret == 0 && ether_addr_equal(sdata->tdls_peer, peer)) {
-               cancel_delayed_work(&sdata->tdls_peer_del_work);
-               eth_zero_addr(sdata->tdls_peer);
+       if (ret == 0 && ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
+               cancel_delayed_work(&sdata->u.mgd.tdls_peer_del_work);
+               eth_zero_addr(sdata->u.mgd.tdls_peer);
        }
 
        mutex_unlock(&local->mtx);
index df1bb7e16cfe8e93560eaefef4aa6d69a0b55214..725af7a468d233b502a6de8a07e30d7a25d60e66 100644 (file)
@@ -3083,3 +3083,18 @@ int ieee80211_max_num_channels(struct ieee80211_local *local)
 
        return max_num_different_channels;
 }
+
+u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo)
+{
+       *buf++ = WLAN_EID_VENDOR_SPECIFIC;
+       *buf++ = 7; /* len */
+       *buf++ = 0x00; /* Microsoft OUI 00:50:F2 */
+       *buf++ = 0x50;
+       *buf++ = 0xf2;
+       *buf++ = 2; /* WME */
+       *buf++ = 0; /* WME info */
+       *buf++ = 1; /* WME ver */
+       *buf++ = qosinfo; /* U-APSD no in use */
+
+       return buf;
+}
index 9265adfdabfcf99acbdf1598067884188c500a49..671ce0d27a80b01bfa5d457ef6134a19d45ee208 100644 (file)
@@ -129,6 +129,10 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
        if (!vht_cap_ie || !sband->vht_cap.vht_supported)
                return;
 
+       /* don't support VHT for TDLS peers for now */
+       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+               return;
+
        /*
         * A VHT STA must support 40 MHz, but if we verify that here
         * then we break a few things - some APs (e.g. Netgear R6300v2
index 9b3dcc201145dd3942bf30771e1638ea973759f7..f7d4ca4c46e0bf68c6232d339d4b54f7934798ba 100644 (file)
@@ -811,7 +811,7 @@ ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx)
 ieee80211_rx_result
 ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx)
 {
-       if (rx->sta->cipher_scheme)
+       if (rx->sta && rx->sta->cipher_scheme)
                return ieee80211_crypto_cs_decrypt(rx);
 
        return RX_DROP_UNUSABLE;
index 405f3c4cf70ca3617a4101e1bad278b93d7ae1b7..29c8675f9a1189db65c185f2ad04f96a67702989 100644 (file)
@@ -162,6 +162,12 @@ config CFG80211_INTERNAL_REGDB
          and includes code to query that database.  This is an alternative
          to using CRDA for defining regulatory rules for the kernel.
 
+         Using this option requires some parsing of the db.txt at build time,
+         the parser will be upkept with the latest wireless-regdb updates but
+         older wireless-regdb formats will be ignored. The parser may later
+         be replaced to avoid issues with conflicts on versions of
+         wireless-regdb.
+
          For details see:
 
          http://wireless.kernel.org/en/developers/Regulatory
index 40c37fc5b67cb76325dcd4f0041c1ad3cf5ebf66..baf2426b555a31c7aeb2fbaa48205c9b422f0cbf 100644 (file)
@@ -51,32 +51,41 @@ function parse_country_head() {
 
 function parse_reg_rule()
 {
+       flag_starts_at = 7
+
        start = $1
        sub(/\(/, "", start)
        end = $3
        bw = $5
        sub(/\),/, "", bw)
-       gain = $6
-       sub(/\(/, "", gain)
-       sub(/,/, "", gain)
-       power = $7
-       sub(/\)/, "", power)
-       sub(/,/, "", power)
+       gain = 0
+       power = $6
        # power might be in mW...
-       units = $8
+       units = $7
+       dfs_cac = 0
+
+       sub(/\(/, "", power)
+       sub(/\),/, "", power)
+       sub(/\),/, "", units)
        sub(/\)/, "", units)
-       sub(/,/, "", units)
-       dfs_cac = $9
+
        if (units == "mW") {
+               flag_starts_at = 8
                power = 10 * log(power)/log(10)
+               if ($8 ~ /[[:digit:]]/) {
+                       flag_starts_at = 9
+                       dfs_cac = $8
+               }
        } else {
-               dfs_cac = $8
+               if ($7 ~ /[[:digit:]]/) {
+                       flag_starts_at = 8
+                       dfs_cac = $7
+               }
        }
-       sub(/,/, "", dfs_cac)
        sub(/\(/, "", dfs_cac)
-       sub(/\)/, "", dfs_cac)
+       sub(/\),/, "", dfs_cac)
        flagstr = ""
-       for (i=8; i<=NF; i++)
+       for (i=flag_starts_at; i<=NF; i++)
                flagstr = flagstr $i
        split(flagstr, flagarray, ",")
        flags = ""
index 082f5c62b8cf3c280414bb65b703ca52af13cb1b..df7b1332a1ec2fa80834b94c233f38a427a0f350 100644 (file)
@@ -3814,7 +3814,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
 {
        if (params->listen_interval != -1)
                return -EINVAL;
-       if (params->aid)
+       if (params->aid &&
+           !(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
                return -EINVAL;
 
        /* When you run into this, adjust the code below for the new flag */