aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/work.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-12-23 07:15:37 -0500
committerJohn W. Linville <linville@tuxdriver.com>2009-12-28 16:54:57 -0500
commit77c8144ad3ee7fae834e13cb7e83f5b7c8c5329e (patch)
treef62aa278669ea2f4db5b93af1f40edb06324bff4 /net/mac80211/work.c
parent7d3a1c3b03c3a571a2c8c393b75558a5f4a7532a (diff)
mac80211: refactor association
Refactor the code to reserve an skb of the right size (instead of hoping 200 bytes are enough forever), and also put HT IE generation into an own function. Additionally, put the HT IE before the vendor-specific WMM IE. This still leaves things not quite ordered correctly, due to user-specified IEs, add a note about that for now. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/work.c')
-rw-r--r--net/mac80211/work.c239
1 files changed, 130 insertions, 109 deletions
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index 874345918e83..c03c22d5bca3 100644
--- a/net/mac80211/work.c
+++ b/net/mac80211/work.c
@@ -100,6 +100,102 @@ static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
100 100
101/* frame sending functions */ 101/* frame sending functions */
102 102
103static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie,
104 struct ieee80211_supported_band *sband,
105 struct ieee80211_channel *channel,
106 enum ieee80211_smps_mode smps)
107{
108 struct ieee80211_ht_info *ht_info;
109 u8 *pos;
110 u32 flags = channel->flags;
111 u16 cap = sband->ht_cap.cap;
112 __le16 tmp;
113
114 if (!sband->ht_cap.ht_supported)
115 return;
116
117 if (!ht_info_ie)
118 return;
119
120 if (ht_info_ie[1] < sizeof(struct ieee80211_ht_info))
121 return;
122
123 ht_info = (struct ieee80211_ht_info *)(ht_info_ie + 2);
124
125 /* determine capability flags */
126
127 if (ieee80211_disable_40mhz_24ghz &&
128 sband->band == IEEE80211_BAND_2GHZ) {
129 cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
130 cap &= ~IEEE80211_HT_CAP_SGI_40;
131 }
132
133 switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
134 case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
135 if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
136 cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
137 cap &= ~IEEE80211_HT_CAP_SGI_40;
138 }
139 break;
140 case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
141 if (flags & IEEE80211_CHAN_NO_HT40MINUS) {
142 cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
143 cap &= ~IEEE80211_HT_CAP_SGI_40;
144 }
145 break;
146 }
147
148 /* set SM PS mode properly */
149 cap &= ~IEEE80211_HT_CAP_SM_PS;
150 switch (smps) {
151 case IEEE80211_SMPS_AUTOMATIC:
152 case IEEE80211_SMPS_NUM_MODES:
153 WARN_ON(1);
154 case IEEE80211_SMPS_OFF:
155 cap |= WLAN_HT_CAP_SM_PS_DISABLED <<
156 IEEE80211_HT_CAP_SM_PS_SHIFT;
157 break;
158 case IEEE80211_SMPS_STATIC:
159 cap |= WLAN_HT_CAP_SM_PS_STATIC <<
160 IEEE80211_HT_CAP_SM_PS_SHIFT;
161 break;
162 case IEEE80211_SMPS_DYNAMIC:
163 cap |= WLAN_HT_CAP_SM_PS_DYNAMIC <<
164 IEEE80211_HT_CAP_SM_PS_SHIFT;
165 break;
166 }
167
168 /* reserve and fill IE */
169
170 pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
171 *pos++ = WLAN_EID_HT_CAPABILITY;
172 *pos++ = sizeof(struct ieee80211_ht_cap);
173 memset(pos, 0, sizeof(struct ieee80211_ht_cap));
174
175 /* capability flags */
176 tmp = cpu_to_le16(cap);
177 memcpy(pos, &tmp, sizeof(u16));
178 pos += sizeof(u16);
179
180 /* AMPDU parameters */
181 *pos++ = sband->ht_cap.ampdu_factor |
182 (sband->ht_cap.ampdu_density <<
183 IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
184
185 /* MCS set */
186 memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
187 pos += sizeof(sband->ht_cap.mcs);
188
189 /* extended capabilities */
190 pos += sizeof(__le16);
191
192 /* BF capabilities */
193 pos += sizeof(__le32);
194
195 /* antenna selection */
196 pos += sizeof(u8);
197}
198
103static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, 199static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
104 struct ieee80211_work *wk) 200 struct ieee80211_work *wk)
105{ 201{
@@ -107,15 +203,34 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
107 struct sk_buff *skb; 203 struct sk_buff *skb;
108 struct ieee80211_mgmt *mgmt; 204 struct ieee80211_mgmt *mgmt;
109 u8 *pos; 205 u8 *pos;
110 const u8 *ies, *ht_ie; 206 const u8 *ies;
111 int i, len, count, rates_len, supp_rates_len; 207 int i, len, count, rates_len, supp_rates_len;
112 u16 capab; 208 u16 capab;
113 struct ieee80211_supported_band *sband; 209 struct ieee80211_supported_band *sband;
114 u32 rates = 0; 210 u32 rates = 0;
115 211
116 skb = dev_alloc_skb(local->hw.extra_tx_headroom + 212 sband = local->hw.wiphy->bands[wk->chan->band];
117 sizeof(*mgmt) + 200 + wk->ie_len + 213
118 wk->assoc.ssid_len); 214 /*
215 * Get all rates supported by the device and the AP as
216 * some APs don't like getting a superset of their rates
217 * in the association request (e.g. D-Link DAP 1353 in
218 * b-only mode)...
219 */
220 rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates,
221 wk->assoc.supp_rates_len,
222 sband, &rates);
223
224 skb = alloc_skb(local->hw.extra_tx_headroom +
225 sizeof(*mgmt) + /* bit too much but doesn't matter */
226 2 + wk->assoc.ssid_len + /* SSID */
227 4 + rates_len + /* (extended) rates */
228 4 + /* power capability */
229 2 + 2 * sband->n_channels + /* supported channels */
230 2 + sizeof(struct ieee80211_ht_cap) + /* HT */
231 wk->ie_len + /* extra IEs */
232 9, /* WMM */
233 GFP_KERNEL);
119 if (!skb) { 234 if (!skb) {
120 printk(KERN_DEBUG "%s: failed to allocate buffer for assoc " 235 printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
121 "frame\n", sdata->name); 236 "frame\n", sdata->name);
@@ -123,8 +238,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
123 } 238 }
124 skb_reserve(skb, local->hw.extra_tx_headroom); 239 skb_reserve(skb, local->hw.extra_tx_headroom);
125 240
126 sband = local->hw.wiphy->bands[wk->chan->band];
127
128 capab = WLAN_CAPABILITY_ESS; 241 capab = WLAN_CAPABILITY_ESS;
129 242
130 if (sband->band == IEEE80211_BAND_2GHZ) { 243 if (sband->band == IEEE80211_BAND_2GHZ) {
@@ -137,16 +250,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
137 if (wk->assoc.capability & WLAN_CAPABILITY_PRIVACY) 250 if (wk->assoc.capability & WLAN_CAPABILITY_PRIVACY)
138 capab |= WLAN_CAPABILITY_PRIVACY; 251 capab |= WLAN_CAPABILITY_PRIVACY;
139 252
140 /*
141 * Get all rates supported by the device and the AP as
142 * some APs don't like getting a superset of their rates
143 * in the association request (e.g. D-Link DAP 1353 in
144 * b-only mode)...
145 */
146 rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates,
147 wk->assoc.supp_rates_len,
148 sband, &rates);
149
150 if ((wk->assoc.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) && 253 if ((wk->assoc.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
151 (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)) 254 (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
152 capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; 255 capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
@@ -220,7 +323,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
220 *pos++ = WLAN_EID_PWR_CAPABILITY; 323 *pos++ = WLAN_EID_PWR_CAPABILITY;
221 *pos++ = 2; 324 *pos++ = 2;
222 *pos++ = 0; /* min tx power */ 325 *pos++ = 0; /* min tx power */
223 *pos++ = local->hw.conf.channel->max_power; /* max tx power */ 326 *pos++ = wk->chan->max_power; /* max tx power */
224 327
225 /* 2. supported channels */ 328 /* 2. supported channels */
226 /* TODO: get this in reg domain format */ 329 /* TODO: get this in reg domain format */
@@ -234,11 +337,21 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
234 } 337 }
235 } 338 }
236 339
340 /*
341 * XXX: These IEs could contain (vendor-specified)
342 * IEs that belong after HT -- the buffer may
343 * need to be split up.
344 */
237 if (wk->ie_len && wk->ie) { 345 if (wk->ie_len && wk->ie) {
238 pos = skb_put(skb, wk->ie_len); 346 pos = skb_put(skb, wk->ie_len);
239 memcpy(pos, wk->ie, wk->ie_len); 347 memcpy(pos, wk->ie, wk->ie_len);
240 } 348 }
241 349
350 if (wk->assoc.use_11n && wk->assoc.wmm_used &&
351 local->hw.queues >= 4)
352 ieee80211_add_ht_ie(skb, wk->assoc.ht_information_ie,
353 sband, wk->chan, wk->assoc.smps);
354
242 if (wk->assoc.wmm_used && local->hw.queues >= 4) { 355 if (wk->assoc.wmm_used && local->hw.queues >= 4) {
243 pos = skb_put(skb, 9); 356 pos = skb_put(skb, 9);
244 *pos++ = WLAN_EID_VENDOR_SPECIFIC; 357 *pos++ = WLAN_EID_VENDOR_SPECIFIC;
@@ -252,98 +365,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
252 *pos++ = 0; 365 *pos++ = 0;
253 } 366 }
254 367
255 /* wmm support is a must to HT */
256 /*
257 * IEEE802.11n does not allow TKIP/WEP as pairwise
258 * ciphers in HT mode. We still associate in non-ht
259 * mode (11a/b/g) if any one of these ciphers is
260 * configured as pairwise.
261 */
262 if (wk->assoc.use_11n && wk->assoc.wmm_used &&
263 (local->hw.queues >= 4) &&
264 sband->ht_cap.ht_supported &&
265 (ht_ie = wk->assoc.ht_information_ie) &&
266 ht_ie[1] >= sizeof(struct ieee80211_ht_info)) {
267 struct ieee80211_ht_info *ht_info =
268 (struct ieee80211_ht_info *)(ht_ie + 2);
269 u16 cap = sband->ht_cap.cap;
270 __le16 tmp;
271 u32 flags = local->hw.conf.channel->flags;
272
273 /* determine capability flags */
274
275 if (ieee80211_disable_40mhz_24ghz &&
276 sband->band == IEEE80211_BAND_2GHZ) {
277 cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
278 cap &= ~IEEE80211_HT_CAP_SGI_40;
279 }
280
281 switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
282 case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
283 if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
284 cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
285 cap &= ~IEEE80211_HT_CAP_SGI_40;
286 }
287 break;
288 case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
289 if (flags & IEEE80211_CHAN_NO_HT40MINUS) {
290 cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
291 cap &= ~IEEE80211_HT_CAP_SGI_40;
292 }
293 break;
294 }
295
296 /* set SM PS mode properly */
297 cap &= ~IEEE80211_HT_CAP_SM_PS;
298 switch (wk->assoc.smps) {
299 case IEEE80211_SMPS_AUTOMATIC:
300 case IEEE80211_SMPS_NUM_MODES:
301 WARN_ON(1);
302 case IEEE80211_SMPS_OFF:
303 cap |= WLAN_HT_CAP_SM_PS_DISABLED <<
304 IEEE80211_HT_CAP_SM_PS_SHIFT;
305 break;
306 case IEEE80211_SMPS_STATIC:
307 cap |= WLAN_HT_CAP_SM_PS_STATIC <<
308 IEEE80211_HT_CAP_SM_PS_SHIFT;
309 break;
310 case IEEE80211_SMPS_DYNAMIC:
311 cap |= WLAN_HT_CAP_SM_PS_DYNAMIC <<
312 IEEE80211_HT_CAP_SM_PS_SHIFT;
313 break;
314 }
315
316 /* reserve and fill IE */
317
318 pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
319 *pos++ = WLAN_EID_HT_CAPABILITY;
320 *pos++ = sizeof(struct ieee80211_ht_cap);
321 memset(pos, 0, sizeof(struct ieee80211_ht_cap));
322
323 /* capability flags */
324 tmp = cpu_to_le16(cap);
325 memcpy(pos, &tmp, sizeof(u16));
326 pos += sizeof(u16);
327
328 /* AMPDU parameters */
329 *pos++ = sband->ht_cap.ampdu_factor |
330 (sband->ht_cap.ampdu_density <<
331 IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
332
333 /* MCS set */
334 memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
335 pos += sizeof(sband->ht_cap.mcs);
336
337 /* extended capabilities */
338 pos += sizeof(__le16);
339
340 /* BF capabilities */
341 pos += sizeof(__le32);
342
343 /* antenna selection */
344 pos += sizeof(u8);
345 }
346
347 IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; 368 IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
348 ieee80211_tx_skb(sdata, skb); 369 ieee80211_tx_skb(sdata, skb);
349} 370}