diff options
author | Arik Nemtsov <arik@wizery.com> | 2014-07-17 10:14:24 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2014-07-21 06:14:04 -0400 |
commit | 6f7eaa47e1de30159277f91f1145a6687f13ffd9 (patch) | |
tree | 128c600ddf57ed192ffd3d54abc8edadee9264c4 | |
parent | 40b861a0eeb06bbfa472b456482ebf89b6886926 (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.h | 20 | ||||
-rw-r--r-- | net/mac80211/tdls.c | 124 |
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 | ||
1004 | struct 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 | |||
1010 | struct 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 */ |
1005 | struct ieee80211_rts { | 1025 | struct 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 */ | ||
98 | static 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 | |||
114 | static 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 | |||
125 | static 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 | |||
131 | static 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 | |||
96 | static void | 165 | static void |
97 | ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, | 166 | ieee80211_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 | ||
237 | static void | ||
238 | ieee80211_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 | |||
168 | static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata, | 287 | static 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) |