aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArik Nemtsov <arik@wizery.com>2014-07-17 10:14:24 -0400
committerJohannes Berg <johannes.berg@intel.com>2014-07-21 06:14:04 -0400
commit6f7eaa47e1de30159277f91f1145a6687f13ffd9 (patch)
tree128c600ddf57ed192ffd3d54abc8edadee9264c4
parent40b861a0eeb06bbfa472b456482ebf89b6886926 (diff)
mac80211: add TDLS QoS param IE on setup-confirm
When TDLS QoS is supported by the the peer and the local card, add the WMM parameter IE to the setup-confirm frame. Take the QoS settings from the current AP, or if unsupported, use the default values from the specification. This behavior is mandated by IEEE802.11-2012 section 10.22.4. Signed-off-by: Arik Nemtsov <arikx.nemtsov@intel.com> Reviewed-by: Liad Kaufman <liad.kaufman@intel.com> Reviewed-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--include/linux/ieee80211.h20
-rw-r--r--net/mac80211/tdls.c124
2 files changed, 144 insertions, 0 deletions
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 75d17e15da33..63ab3873c5ed 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1001,6 +1001,26 @@ struct ieee80211_vendor_ie {
1001 u8 oui_type; 1001 u8 oui_type;
1002} __packed; 1002} __packed;
1003 1003
1004struct ieee80211_wmm_ac_param {
1005 u8 aci_aifsn; /* AIFSN, ACM, ACI */
1006 u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
1007 __le16 txop_limit;
1008} __packed;
1009
1010struct ieee80211_wmm_param_ie {
1011 u8 element_id; /* Element ID: 221 (0xdd); */
1012 u8 len; /* Length: 24 */
1013 /* required fields for WMM version 1 */
1014 u8 oui[3]; /* 00:50:f2 */
1015 u8 oui_type; /* 2 */
1016 u8 oui_subtype; /* 1 */
1017 u8 version; /* 1 for WMM version 1.0 */
1018 u8 qos_info; /* AP/STA specific QoS info */
1019 u8 reserved; /* 0 */
1020 /* AC_BE, AC_BK, AC_VI, AC_VO */
1021 struct ieee80211_wmm_ac_param ac[4];
1022} __packed;
1023
1004/* Control frames */ 1024/* Control frames */
1005struct ieee80211_rts { 1025struct ieee80211_rts {
1006 __le16 frame_control; 1026 __le16 frame_control;
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index bfd8fc4a6b2f..72eebea7e60a 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -8,6 +8,7 @@
8 */ 8 */
9 9
10#include <linux/ieee80211.h> 10#include <linux/ieee80211.h>
11#include <linux/log2.h>
11#include <net/cfg80211.h> 12#include <net/cfg80211.h>
12#include "ieee80211_i.h" 13#include "ieee80211_i.h"
13#include "driver-ops.h" 14#include "driver-ops.h"
@@ -93,6 +94,74 @@ static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata,
93 memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN); 94 memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
94} 95}
95 96
97/* translate numbering in the WMM parameter IE to the mac80211 notation */
98static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac)
99{
100 switch (ac) {
101 default:
102 WARN_ON_ONCE(1);
103 case 0:
104 return IEEE80211_AC_BE;
105 case 1:
106 return IEEE80211_AC_BK;
107 case 2:
108 return IEEE80211_AC_VI;
109 case 3:
110 return IEEE80211_AC_VO;
111 }
112}
113
114static u8 ieee80211_wmm_aci_aifsn(int aifsn, bool acm, int aci)
115{
116 u8 ret;
117
118 ret = aifsn & 0x0f;
119 if (acm)
120 ret |= 0x10;
121 ret |= (aci << 5) & 0x60;
122 return ret;
123}
124
125static u8 ieee80211_wmm_ecw(u16 cw_min, u16 cw_max)
126{
127 return ((ilog2(cw_min + 1) << 0x0) & 0x0f) |
128 ((ilog2(cw_max + 1) << 0x4) & 0xf0);
129}
130
131static void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata,
132 struct sk_buff *skb)
133{
134 struct ieee80211_wmm_param_ie *wmm;
135 struct ieee80211_tx_queue_params *txq;
136 int i;
137
138 wmm = (void *)skb_put(skb, sizeof(*wmm));
139 memset(wmm, 0, sizeof(*wmm));
140
141 wmm->element_id = WLAN_EID_VENDOR_SPECIFIC;
142 wmm->len = sizeof(*wmm) - 2;
143
144 wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */
145 wmm->oui[1] = 0x50;
146 wmm->oui[2] = 0xf2;
147 wmm->oui_type = 2; /* WME */
148 wmm->oui_subtype = 1; /* WME param */
149 wmm->version = 1; /* WME ver */
150 wmm->qos_info = 0; /* U-APSD not in use */
151
152 /*
153 * Use the EDCA parameters defined for the BSS, or default if the AP
154 * doesn't support it, as mandated by 802.11-2012 section 10.22.4
155 */
156 for (i = 0; i < IEEE80211_NUM_ACS; i++) {
157 txq = &sdata->tx_conf[ieee80211_ac_from_wmm(i)];
158 wmm->ac[i].aci_aifsn = ieee80211_wmm_aci_aifsn(txq->aifs,
159 txq->acm, i);
160 wmm->ac[i].cw = ieee80211_wmm_ecw(txq->cw_min, txq->cw_max);
161 wmm->ac[i].txop_limit = cpu_to_le16(txq->txop);
162 }
163}
164
96static void 165static void
97ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, 166ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
98 struct sk_buff *skb, const u8 *peer, 167 struct sk_buff *skb, const u8 *peer,
@@ -165,6 +234,56 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
165 ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); 234 ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
166} 235}
167 236
237static void
238ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
239 struct sk_buff *skb, const u8 *peer,
240 bool initiator, const u8 *extra_ies,
241 size_t extra_ies_len)
242{
243 struct ieee80211_local *local = sdata->local;
244 size_t offset = 0, noffset;
245 struct sta_info *sta;
246 u8 *pos;
247
248 rcu_read_lock();
249
250 sta = sta_info_get(sdata, peer);
251 if (WARN_ON_ONCE(!sta)) {
252 rcu_read_unlock();
253 return;
254 }
255
256 /* add any custom IEs that go before the QoS IE */
257 if (extra_ies_len) {
258 static const u8 before_qos[] = {
259 WLAN_EID_RSN,
260 };
261 noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
262 before_qos,
263 ARRAY_SIZE(before_qos),
264 offset);
265 pos = skb_put(skb, noffset - offset);
266 memcpy(pos, extra_ies + offset, noffset - offset);
267 offset = noffset;
268 }
269
270 /* add the QoS param IE if both the peer and we support it */
271 if (local->hw.queues >= IEEE80211_NUM_ACS &&
272 test_sta_flag(sta, WLAN_STA_WME))
273 ieee80211_tdls_add_wmm_param_ie(sdata, skb);
274
275 /* add any remaining IEs */
276 if (extra_ies_len) {
277 noffset = extra_ies_len;
278 pos = skb_put(skb, noffset - offset);
279 memcpy(pos, extra_ies + offset, noffset - offset);
280 }
281
282 ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
283
284 rcu_read_unlock();
285}
286
168static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata, 287static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
169 struct sk_buff *skb, const u8 *peer, 288 struct sk_buff *skb, const u8 *peer,
170 u8 action_code, u16 status_code, 289 u8 action_code, u16 status_code,
@@ -183,6 +302,11 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
183 extra_ies_len); 302 extra_ies_len);
184 break; 303 break;
185 case WLAN_TDLS_SETUP_CONFIRM: 304 case WLAN_TDLS_SETUP_CONFIRM:
305 if (status_code == 0)
306 ieee80211_tdls_add_setup_cfm_ies(sdata, skb, peer,
307 initiator, extra_ies,
308 extra_ies_len);
309 break;
186 case WLAN_TDLS_TEARDOWN: 310 case WLAN_TDLS_TEARDOWN:
187 case WLAN_TDLS_DISCOVERY_REQUEST: 311 case WLAN_TDLS_DISCOVERY_REQUEST:
188 if (extra_ies_len) 312 if (extra_ies_len)