Commit | Line | Data |
---|---|---|
5e93f352 LF |
1 | /****************************************************************************** |
2 | * | |
3 | * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of version 2 of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | * more details. | |
13 | * | |
14 | ******************************************************************************/ | |
15 | #define _IEEE80211_C | |
16 | ||
17 | #include <drv_types.h> | |
18 | #include <linux/ieee80211.h> | |
19 | #include <ieee80211.h> | |
20 | #include <wifi.h> | |
21 | #include <osdep_service.h> | |
22 | #include <wlan_bssdef.h> | |
23 | ||
24 | u8 RTW_WPA_OUI23A_TYPE[] = { 0x00, 0x50, 0xf2, 1 }; | |
25 | u16 RTW_WPA_VERSION23A = 1; | |
26 | u8 WPA_AUTH_KEY_MGMT_NONE23A[] = { 0x00, 0x50, 0xf2, 0 }; | |
27 | u8 WPA_AUTH_KEY_MGMT_UNSPEC_802_1X23A[] = { 0x00, 0x50, 0xf2, 1 }; | |
28 | u8 WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X23A[] = { 0x00, 0x50, 0xf2, 2 }; | |
29 | u8 WPA_CIPHER_SUITE_NONE23A[] = { 0x00, 0x50, 0xf2, 0 }; | |
30 | u8 WPA_CIPHER_SUITE_WEP4023A[] = { 0x00, 0x50, 0xf2, 1 }; | |
31 | u8 WPA_CIPHER_SUITE_TKIP23A[] = { 0x00, 0x50, 0xf2, 2 }; | |
32 | u8 WPA_CIPHER_SUITE_WRAP23A[] = { 0x00, 0x50, 0xf2, 3 }; | |
33 | u8 WPA_CIPHER_SUITE_CCMP23A[] = { 0x00, 0x50, 0xf2, 4 }; | |
34 | u8 WPA_CIPHER_SUITE_WEP10423A[] = { 0x00, 0x50, 0xf2, 5 }; | |
35 | ||
36 | u16 RSN_VERSION_BSD23A = 1; | |
37 | u8 RSN_AUTH_KEY_MGMT_UNSPEC_802_1X23A[] = { 0x00, 0x0f, 0xac, 1 }; | |
38 | u8 RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X23A[] = { 0x00, 0x0f, 0xac, 2 }; | |
39 | u8 RSN_CIPHER_SUITE_NONE23A[] = { 0x00, 0x0f, 0xac, 0 }; | |
40 | u8 RSN_CIPHER_SUITE_WEP4023A[] = { 0x00, 0x0f, 0xac, 1 }; | |
41 | u8 RSN_CIPHER_SUITE_TKIP23A[] = { 0x00, 0x0f, 0xac, 2 }; | |
42 | u8 RSN_CIPHER_SUITE_WRAP23A[] = { 0x00, 0x0f, 0xac, 3 }; | |
43 | u8 RSN_CIPHER_SUITE_CCMP23A[] = { 0x00, 0x0f, 0xac, 4 }; | |
44 | u8 RSN_CIPHER_SUITE_WEP10423A[] = { 0x00, 0x0f, 0xac, 5 }; | |
45 | /* */ | |
46 | /* for adhoc-master to generate ie and provide supported-rate to fw */ | |
47 | /* */ | |
48 | ||
49 | static u8 WIFI_CCKRATES[] = | |
50 | {(IEEE80211_CCK_RATE_1MB | IEEE80211_BASIC_RATE_MASK), | |
51 | (IEEE80211_CCK_RATE_2MB | IEEE80211_BASIC_RATE_MASK), | |
52 | (IEEE80211_CCK_RATE_5MB | IEEE80211_BASIC_RATE_MASK), | |
53 | (IEEE80211_CCK_RATE_11MB | IEEE80211_BASIC_RATE_MASK)}; | |
54 | ||
55 | static u8 WIFI_OFDMRATES[] = | |
56 | {(IEEE80211_OFDM_RATE_6MB), | |
57 | (IEEE80211_OFDM_RATE_9MB), | |
58 | (IEEE80211_OFDM_RATE_12MB), | |
59 | (IEEE80211_OFDM_RATE_18MB), | |
60 | (IEEE80211_OFDM_RATE_24MB), | |
61 | IEEE80211_OFDM_RATE_36MB, | |
62 | IEEE80211_OFDM_RATE_48MB, | |
63 | IEEE80211_OFDM_RATE_54MB}; | |
64 | ||
65 | int rtw_get_bit_value_from_ieee_value23a(u8 val) | |
66 | { | |
67 | unsigned char dot11_rate_table[]= | |
68 | {2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108, 0}; | |
69 | ||
70 | int i = 0; | |
71 | while (dot11_rate_table[i] != 0) { | |
72 | if (dot11_rate_table[i] == val) | |
73 | return BIT(i); | |
74 | i++; | |
75 | } | |
76 | return 0; | |
77 | } | |
78 | ||
79 | uint rtw_is_cckrates_included23a(u8 *rate) | |
80 | { | |
81 | u32 i = 0; | |
82 | ||
83 | while (rate[i] != 0) { | |
84 | if ((((rate[i]) & 0x7f) == 2) || (((rate[i]) & 0x7f) == 4) || | |
85 | (((rate[i]) & 0x7f) == 11) || (((rate[i]) & 0x7f) == 22)) | |
86 | return true; | |
87 | i++; | |
88 | } | |
89 | ||
90 | return false; | |
91 | } | |
92 | ||
93 | uint rtw_is_cckratesonly_included23a(u8 *rate) | |
94 | { | |
95 | u32 i = 0; | |
96 | ||
97 | while (rate[i] != 0) { | |
98 | if ((((rate[i]) & 0x7f) != 2) && (((rate[i]) & 0x7f) != 4) && | |
99 | (((rate[i]) & 0x7f) != 11) && (((rate[i]) & 0x7f) != 22)) | |
100 | return false; | |
101 | ||
102 | i++; | |
103 | } | |
104 | ||
105 | return true; | |
106 | } | |
107 | ||
108 | int rtw_check_network_type23a(unsigned char *rate, int ratelen, int channel) | |
109 | { | |
110 | if (channel > 14) { | |
111 | if ((rtw_is_cckrates_included23a(rate)) == true) | |
112 | return WIRELESS_INVALID; | |
113 | else | |
114 | return WIRELESS_11A; | |
115 | } else { /* could be pure B, pure G, or B/G */ | |
116 | if ((rtw_is_cckratesonly_included23a(rate)) == true) | |
117 | return WIRELESS_11B; | |
118 | else if ((rtw_is_cckrates_included23a(rate)) == true) | |
119 | return WIRELESS_11BG; | |
120 | else | |
121 | return WIRELESS_11G; | |
122 | } | |
123 | } | |
124 | ||
125 | u8 *rtw_set_fixed_ie23a(unsigned char *pbuf, unsigned int len, | |
126 | unsigned char *source, unsigned int *frlen) | |
127 | { | |
128 | memcpy((void *)pbuf, (void *)source, len); | |
129 | *frlen = *frlen + len; | |
130 | return pbuf + len; | |
131 | } | |
132 | ||
133 | /* rtw_set_ie23a will update frame length */ | |
134 | u8 *rtw_set_ie23a(u8 *pbuf, int index, uint len, u8 *source, uint *frlen) | |
135 | { | |
136 | ||
137 | *pbuf = (u8)index; | |
138 | ||
139 | *(pbuf + 1) = (u8)len; | |
140 | ||
141 | if (len > 0) | |
142 | memcpy((void *)(pbuf + 2), (void *)source, len); | |
143 | ||
144 | *frlen = *frlen + (len + 2); | |
145 | ||
146 | ||
147 | return pbuf + len + 2; | |
148 | } | |
149 | ||
150 | inline u8 *rtw_set_ie23a_ch_switch (u8 *buf, u32 *buf_len, u8 ch_switch_mode, | |
151 | u8 new_ch, u8 ch_switch_cnt) | |
152 | { | |
153 | u8 ie_data[3]; | |
154 | ||
155 | ie_data[0] = ch_switch_mode; | |
156 | ie_data[1] = new_ch; | |
157 | ie_data[2] = ch_switch_cnt; | |
158 | return rtw_set_ie23a(buf, WLAN_EID_CHANNEL_SWITCH, 3, ie_data, buf_len); | |
159 | } | |
160 | ||
161 | inline u8 secondary_ch_offset_to_hal_ch_offset23a(u8 ch_offset) | |
162 | { | |
163 | if (ch_offset == SCN) | |
164 | return HAL_PRIME_CHNL_OFFSET_DONT_CARE; | |
165 | else if (ch_offset == SCA) | |
166 | return HAL_PRIME_CHNL_OFFSET_UPPER; | |
167 | else if (ch_offset == SCB) | |
168 | return HAL_PRIME_CHNL_OFFSET_LOWER; | |
169 | ||
170 | return HAL_PRIME_CHNL_OFFSET_DONT_CARE; | |
171 | } | |
172 | ||
173 | inline u8 hal_ch_offset_to_secondary_ch_offset23a(u8 ch_offset) | |
174 | { | |
175 | if (ch_offset == HAL_PRIME_CHNL_OFFSET_DONT_CARE) | |
176 | return SCN; | |
177 | else if (ch_offset == HAL_PRIME_CHNL_OFFSET_LOWER) | |
178 | return SCB; | |
179 | else if (ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER) | |
180 | return SCA; | |
181 | ||
182 | return SCN; | |
183 | } | |
184 | ||
185 | inline u8 *rtw_set_ie23a_secondary_ch_offset(u8 *buf, u32 *buf_len, | |
186 | u8 secondary_ch_offset) | |
187 | { | |
188 | return rtw_set_ie23a(buf, WLAN_EID_SECONDARY_CHANNEL_OFFSET, | |
189 | 1, &secondary_ch_offset, buf_len); | |
190 | } | |
191 | ||
192 | inline u8 *rtw_set_ie23a_mesh_ch_switch_parm(u8 *buf, u32 *buf_len, u8 ttl, | |
193 | u8 flags, u16 reason, u16 precedence) | |
194 | { | |
195 | u8 ie_data[6]; | |
196 | ||
197 | ie_data[0] = ttl; | |
198 | ie_data[1] = flags; | |
c17416ef LF |
199 | put_unaligned_le16(reason, (u8*)&ie_data[2]); |
200 | put_unaligned_le16(precedence, (u8*)&ie_data[4]); | |
5e93f352 LF |
201 | |
202 | return rtw_set_ie23a(buf, 0x118, 6, ie_data, buf_len); | |
203 | } | |
204 | ||
205 | /*---------------------------------------------------------------------------- | |
206 | index: the information element id index, limit is the limit for search | |
207 | -----------------------------------------------------------------------------*/ | |
208 | u8 *rtw_get_ie23a(u8 *pbuf, int index, int *len, int limit) | |
209 | { | |
210 | int tmp, i; | |
211 | u8 *p; | |
212 | ||
213 | if (limit < 1) { | |
214 | ||
215 | return NULL; | |
216 | } | |
217 | ||
218 | p = pbuf; | |
219 | i = 0; | |
220 | *len = 0; | |
221 | while (1) { | |
222 | if (*p == index) { | |
223 | *len = *(p + 1); | |
224 | return p; | |
225 | } else { | |
226 | tmp = *(p + 1); | |
227 | p += (tmp + 2); | |
228 | i += (tmp + 2); | |
229 | } | |
230 | if (i >= limit) | |
231 | break; | |
232 | } | |
233 | ||
234 | return NULL; | |
235 | } | |
236 | ||
237 | /** | |
238 | * rtw_get_ie23a_ex - Search specific IE from a series of IEs | |
239 | * @in_ie: Address of IEs to search | |
240 | * @in_len: Length limit from in_ie | |
241 | * @eid: Element ID to match | |
242 | * @oui: OUI to match | |
243 | * @oui_len: OUI length | |
244 | * @ie: If not NULL and the specific IE is found, the IE will be copied | |
245 | * to the buf starting from the specific IE | |
246 | * @ielen: If not NULL and the specific IE is found, will set to the length | |
247 | * of the entire IE | |
248 | * | |
249 | * Returns: The address of the specific IE found, or NULL | |
250 | */ | |
251 | u8 *rtw_get_ie23a_ex(u8 *in_ie, uint in_len, u8 eid, u8 *oui, u8 oui_len, | |
252 | u8 *ie, uint *ielen) | |
253 | { | |
254 | uint cnt; | |
255 | u8 *target_ie = NULL; | |
256 | ||
257 | if (ielen) | |
258 | *ielen = 0; | |
259 | ||
260 | if (!in_ie || in_len <= 0) | |
261 | return target_ie; | |
262 | ||
263 | cnt = 0; | |
264 | ||
265 | while (cnt < in_len) { | |
266 | if (eid == in_ie[cnt] && | |
267 | (!oui || !memcmp(&in_ie[cnt+2], oui, oui_len))) { | |
268 | target_ie = &in_ie[cnt]; | |
269 | ||
270 | if (ie) | |
271 | memcpy(ie, &in_ie[cnt], in_ie[cnt+1]+2); | |
272 | ||
273 | if (ielen) | |
274 | *ielen = in_ie[cnt+1]+2; | |
275 | break; | |
276 | } else { | |
277 | cnt += in_ie[cnt + 1] + 2; /* goto next */ | |
278 | } | |
279 | } | |
280 | ||
281 | return target_ie; | |
282 | } | |
283 | ||
284 | /** | |
285 | * rtw_ies_remove_ie23a - Find matching IEs and remove | |
286 | * @ies: Address of IEs to search | |
287 | * @ies_len: Pointer of length of ies, will update to new length | |
288 | * @offset: The offset to start scarch | |
289 | * @eid: Element ID to match | |
290 | * @oui: OUI to match | |
291 | * @oui_len: OUI length | |
292 | * | |
293 | * Returns: _SUCCESS: ies is updated, _FAIL: not updated | |
294 | */ | |
295 | int rtw_ies_remove_ie23a(u8 *ies, uint *ies_len, uint offset, u8 eid, | |
296 | u8 *oui, u8 oui_len) | |
297 | { | |
298 | int ret = _FAIL; | |
299 | u8 *target_ie; | |
300 | u32 target_ielen; | |
301 | u8 *start; | |
302 | uint search_len; | |
303 | ||
304 | if (!ies || !ies_len || *ies_len <= offset) | |
305 | goto exit; | |
306 | ||
307 | start = ies + offset; | |
308 | search_len = *ies_len - offset; | |
309 | ||
310 | while (1) { | |
311 | target_ie = rtw_get_ie23a_ex(start, search_len, eid, oui, oui_len, | |
312 | NULL, &target_ielen); | |
313 | if (target_ie && target_ielen) { | |
314 | u8 buf[MAX_IE_SZ] = {0}; | |
315 | u8 *remain_ies = target_ie + target_ielen; | |
316 | uint remain_len = search_len - (remain_ies - start); | |
317 | ||
318 | memcpy(buf, remain_ies, remain_len); | |
319 | memcpy(target_ie, buf, remain_len); | |
320 | *ies_len = *ies_len - target_ielen; | |
321 | ret = _SUCCESS; | |
322 | ||
323 | start = target_ie; | |
324 | search_len = remain_len; | |
325 | } else { | |
326 | break; | |
327 | } | |
328 | } | |
329 | exit: | |
330 | return ret; | |
331 | } | |
332 | ||
333 | void rtw_set_supported_rate23a(u8* SupportedRates, uint mode) | |
334 | { | |
335 | ||
336 | ||
337 | memset(SupportedRates, 0, NDIS_802_11_LENGTH_RATES_EX); | |
338 | ||
339 | switch (mode) | |
340 | { | |
341 | case WIRELESS_11B: | |
342 | memcpy(SupportedRates, WIFI_CCKRATES, IEEE80211_CCK_RATE_LEN); | |
343 | break; | |
344 | ||
345 | case WIRELESS_11G: | |
346 | case WIRELESS_11A: | |
347 | case WIRELESS_11_5N: | |
348 | case WIRELESS_11A_5N:/* Todo: no basic rate for ofdm ? */ | |
349 | memcpy(SupportedRates, WIFI_OFDMRATES, | |
350 | IEEE80211_NUM_OFDM_RATESLEN); | |
351 | break; | |
352 | ||
353 | case WIRELESS_11BG: | |
354 | case WIRELESS_11G_24N: | |
355 | case WIRELESS_11_24N: | |
356 | case WIRELESS_11BG_24N: | |
357 | memcpy(SupportedRates, WIFI_CCKRATES, IEEE80211_CCK_RATE_LEN); | |
358 | memcpy(SupportedRates + IEEE80211_CCK_RATE_LEN, WIFI_OFDMRATES, | |
359 | IEEE80211_NUM_OFDM_RATESLEN); | |
360 | break; | |
361 | } | |
362 | ||
363 | } | |
364 | ||
365 | uint rtw_get_rateset_len23a(u8 *rateset) | |
366 | { | |
367 | uint i = 0; | |
368 | ||
369 | while(1) { | |
370 | if ((rateset[i]) == 0) | |
371 | break; | |
372 | ||
373 | if (i > 12) | |
374 | break; | |
375 | ||
376 | i++; | |
377 | } | |
378 | ||
379 | return i; | |
380 | } | |
381 | ||
382 | int rtw_generate_ie23a(struct registry_priv *pregistrypriv) | |
383 | { | |
384 | u8 wireless_mode; | |
385 | int sz = 0, rateLen; | |
386 | struct wlan_bssid_ex* pdev_network = &pregistrypriv->dev_network; | |
387 | u8* ie = pdev_network->IEs; | |
388 | ||
389 | ||
390 | ||
391 | /* timestamp will be inserted by hardware */ | |
392 | sz += 8; | |
393 | ie += sz; | |
394 | ||
395 | /* beacon interval : 2bytes */ | |
396 | /* BCN_INTERVAL; */ | |
397 | *(u16*)ie = cpu_to_le16((u16)pdev_network->Configuration.BeaconPeriod); | |
398 | sz += 2; | |
399 | ie += 2; | |
400 | ||
401 | /* capability info */ | |
402 | *(u16*)ie = 0; | |
403 | ||
404 | *(u16*)ie |= cpu_to_le16(cap_IBSS); | |
405 | ||
406 | if (pregistrypriv->preamble == PREAMBLE_SHORT) | |
407 | *(u16*)ie |= cpu_to_le16(cap_ShortPremble); | |
408 | ||
409 | if (pdev_network->Privacy) | |
410 | *(u16*)ie |= cpu_to_le16(cap_Privacy); | |
411 | ||
412 | sz += 2; | |
413 | ie += 2; | |
414 | ||
415 | /* SSID */ | |
416 | ie = rtw_set_ie23a(ie, _SSID_IE_, pdev_network->Ssid.ssid_len, | |
417 | pdev_network->Ssid.ssid, &sz); | |
418 | ||
419 | /* supported rates */ | |
420 | if (pregistrypriv->wireless_mode == WIRELESS_11ABGN) { | |
421 | if (pdev_network->Configuration.DSConfig > 14) | |
422 | wireless_mode = WIRELESS_11A_5N; | |
423 | else | |
424 | wireless_mode = WIRELESS_11BG_24N; | |
425 | } else { | |
426 | wireless_mode = pregistrypriv->wireless_mode; | |
427 | } | |
428 | ||
429 | rtw_set_supported_rate23a(pdev_network->SupportedRates, wireless_mode) ; | |
430 | ||
431 | rateLen = rtw_get_rateset_len23a(pdev_network->SupportedRates); | |
432 | ||
433 | if (rateLen > 8) { | |
434 | ie = rtw_set_ie23a(ie, _SUPPORTEDRATES_IE_, 8, | |
435 | pdev_network->SupportedRates, &sz); | |
436 | /* ie = rtw_set_ie23a(ie, _EXT_SUPPORTEDRATES_IE_, (rateLen - 8), (pdev_network->SupportedRates + 8), &sz); */ | |
437 | } else { | |
438 | ie = rtw_set_ie23a(ie, _SUPPORTEDRATES_IE_, rateLen, | |
439 | pdev_network->SupportedRates, &sz); | |
440 | } | |
441 | ||
442 | /* DS parameter set */ | |
443 | ie = rtw_set_ie23a(ie, _DSSET_IE_, 1, | |
444 | (u8 *)&pdev_network->Configuration.DSConfig, &sz); | |
445 | ||
446 | /* IBSS Parameter Set */ | |
447 | ||
448 | ie = rtw_set_ie23a(ie, _IBSS_PARA_IE_, 2, | |
449 | (u8 *)&pdev_network->Configuration.ATIMWindow, &sz); | |
450 | ||
451 | if (rateLen > 8) { | |
452 | ie = rtw_set_ie23a(ie, _EXT_SUPPORTEDRATES_IE_, (rateLen - 8), | |
453 | (pdev_network->SupportedRates + 8), &sz); | |
454 | } | |
455 | ||
456 | ||
457 | ||
458 | /* return _SUCCESS; */ | |
459 | ||
460 | return sz; | |
461 | } | |
462 | ||
463 | unsigned char *rtw_get_wpa_ie23a(unsigned char *pie, int *wpa_ie_len, int limit) | |
464 | { | |
465 | int len; | |
466 | u16 val16; | |
467 | unsigned char wpa_oui_type[] = {0x00, 0x50, 0xf2, 0x01}; | |
468 | u8 *pbuf = pie; | |
469 | int limit_new = limit; | |
470 | ||
471 | while(1) { | |
472 | pbuf = rtw_get_ie23a(pbuf, _WPA_IE_ID_, &len, limit_new); | |
473 | ||
474 | if (pbuf) { | |
475 | /* check if oui matches... */ | |
476 | if (memcmp((pbuf + 2), wpa_oui_type, | |
477 | sizeof(wpa_oui_type))) { | |
478 | goto check_next_ie; | |
479 | } | |
480 | ||
481 | /* check version... */ | |
482 | memcpy((u8 *)&val16, (pbuf + 6), sizeof(val16)); | |
483 | ||
484 | val16 = le16_to_cpu(val16); | |
485 | if (val16 != 0x0001) | |
486 | goto check_next_ie; | |
487 | ||
488 | *wpa_ie_len = *(pbuf + 1); | |
489 | ||
490 | return pbuf; | |
491 | } else { | |
492 | *wpa_ie_len = 0; | |
493 | return NULL; | |
494 | } | |
495 | ||
496 | check_next_ie: | |
497 | ||
498 | limit_new = limit - (pbuf - pie) - 2 - len; | |
499 | ||
500 | if (limit_new <= 0) | |
501 | break; | |
502 | ||
503 | pbuf += (2 + len); | |
504 | } | |
505 | ||
506 | *wpa_ie_len = 0; | |
507 | ||
508 | return NULL; | |
509 | } | |
510 | ||
511 | unsigned char *rtw_get_wpa2_ie23a(unsigned char *pie, int *rsn_ie_len, int limit) | |
512 | { | |
513 | return rtw_get_ie23a(pie, _WPA2_IE_ID_, rsn_ie_len, limit); | |
514 | } | |
515 | ||
516 | int rtw_get_wpa_cipher_suite23a(u8 *s) | |
517 | { | |
518 | if (!memcmp(s, WPA_CIPHER_SUITE_NONE23A, WPA_SELECTOR_LEN)) | |
519 | return WPA_CIPHER_NONE; | |
520 | if (!memcmp(s, WPA_CIPHER_SUITE_WEP4023A, WPA_SELECTOR_LEN)) | |
521 | return WPA_CIPHER_WEP40; | |
522 | if (!memcmp(s, WPA_CIPHER_SUITE_TKIP23A, WPA_SELECTOR_LEN)) | |
523 | return WPA_CIPHER_TKIP; | |
524 | if (!memcmp(s, WPA_CIPHER_SUITE_CCMP23A, WPA_SELECTOR_LEN)) | |
525 | return WPA_CIPHER_CCMP; | |
526 | if (!memcmp(s, WPA_CIPHER_SUITE_WEP10423A, WPA_SELECTOR_LEN)) | |
527 | return WPA_CIPHER_WEP104; | |
528 | ||
529 | return 0; | |
530 | } | |
531 | ||
532 | int rtw_get_wpa2_cipher_suite23a(u8 *s) | |
533 | { | |
534 | if (!memcmp(s, RSN_CIPHER_SUITE_NONE23A, RSN_SELECTOR_LEN)) | |
535 | return WPA_CIPHER_NONE; | |
536 | if (!memcmp(s, RSN_CIPHER_SUITE_WEP4023A, RSN_SELECTOR_LEN)) | |
537 | return WPA_CIPHER_WEP40; | |
538 | if (!memcmp(s, RSN_CIPHER_SUITE_TKIP23A, RSN_SELECTOR_LEN)) | |
539 | return WPA_CIPHER_TKIP; | |
540 | if (!memcmp(s, RSN_CIPHER_SUITE_CCMP23A, RSN_SELECTOR_LEN)) | |
541 | return WPA_CIPHER_CCMP; | |
542 | if (!memcmp(s, RSN_CIPHER_SUITE_WEP10423A, RSN_SELECTOR_LEN)) | |
543 | return WPA_CIPHER_WEP104; | |
544 | ||
545 | return 0; | |
546 | } | |
547 | ||
548 | int rtw_parse_wpa_ie23a(u8* wpa_ie, int wpa_ie_len, int *group_cipher, int *pairwise_cipher, int *is_8021x) | |
549 | { | |
550 | int i, ret = _SUCCESS; | |
551 | int left, count; | |
552 | u8 *pos; | |
553 | u8 SUITE_1X[4] = {0x00, 0x50, 0xf2, 1}; | |
554 | ||
555 | if (wpa_ie_len <= 0) { | |
556 | /* No WPA IE - fail silently */ | |
557 | return _FAIL; | |
558 | } | |
559 | ||
560 | if ((*wpa_ie != _WPA_IE_ID_) || (*(wpa_ie+1) != (u8)(wpa_ie_len - 2)) || | |
561 | memcmp(wpa_ie + 2, RTW_WPA_OUI23A_TYPE, WPA_SELECTOR_LEN)) { | |
562 | return _FAIL; | |
563 | } | |
564 | ||
565 | pos = wpa_ie; | |
566 | ||
567 | pos += 8; | |
568 | left = wpa_ie_len - 8; | |
569 | ||
570 | /* group_cipher */ | |
571 | if (left >= WPA_SELECTOR_LEN) { | |
572 | ||
573 | *group_cipher = rtw_get_wpa_cipher_suite23a(pos); | |
574 | ||
575 | pos += WPA_SELECTOR_LEN; | |
576 | left -= WPA_SELECTOR_LEN; | |
577 | } else if (left > 0) { | |
578 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, | |
579 | ("%s: ie length mismatch, %u too much", | |
580 | __func__, left)); | |
581 | ||
582 | return _FAIL; | |
583 | } | |
584 | ||
585 | /* pairwise_cipher */ | |
586 | if (left >= 2) { | |
587 | /* count = le16_to_cpu(*(u16*)pos); */ | |
c17416ef | 588 | count = get_unaligned_le16(pos); |
5e93f352 LF |
589 | pos += 2; |
590 | left -= 2; | |
591 | ||
592 | if (count == 0 || left < count * WPA_SELECTOR_LEN) { | |
593 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, | |
594 | ("%s: ie count botch (pairwise), " | |
595 | "count %u left %u", __func__, | |
596 | count, left)); | |
597 | return _FAIL; | |
598 | } | |
599 | ||
600 | for (i = 0; i < count; i++) { | |
601 | *pairwise_cipher |= rtw_get_wpa_cipher_suite23a(pos); | |
602 | ||
603 | pos += WPA_SELECTOR_LEN; | |
604 | left -= WPA_SELECTOR_LEN; | |
605 | } | |
606 | } else if (left == 1) { | |
607 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, | |
608 | ("%s: ie too short (for key mgmt)", __func__)); | |
609 | return _FAIL; | |
610 | } | |
611 | ||
612 | if (is_8021x) { | |
613 | if (left >= 6) { | |
614 | pos += 2; | |
615 | if (!memcmp(pos, SUITE_1X, 4)) { | |
616 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
617 | ("%s : there has 802.1x auth\n", | |
618 | __func__)); | |
619 | *is_8021x = 1; | |
620 | } | |
621 | } | |
622 | } | |
623 | ||
624 | return ret; | |
625 | } | |
626 | ||
627 | int rtw_parse_wpa2_ie23a(u8* rsn_ie, int rsn_ie_len, int *group_cipher, | |
628 | int *pairwise_cipher, int *is_8021x) | |
629 | { | |
630 | int i, ret = _SUCCESS; | |
631 | int left, count; | |
632 | u8 *pos; | |
633 | u8 SUITE_1X[4] = {0x00, 0x0f, 0xac, 0x01}; | |
634 | ||
635 | if (rsn_ie_len <= 0) { | |
636 | /* No RSN IE - fail silently */ | |
637 | return _FAIL; | |
638 | } | |
639 | ||
640 | if ((*rsn_ie!= _WPA2_IE_ID_) || (*(rsn_ie+1) != (u8)(rsn_ie_len - 2))) { | |
641 | return _FAIL; | |
642 | } | |
643 | ||
644 | pos = rsn_ie; | |
645 | pos += 4; | |
646 | left = rsn_ie_len - 4; | |
647 | ||
648 | /* group_cipher */ | |
649 | if (left >= RSN_SELECTOR_LEN) { | |
650 | *group_cipher = rtw_get_wpa2_cipher_suite23a(pos); | |
651 | ||
652 | pos += RSN_SELECTOR_LEN; | |
653 | left -= RSN_SELECTOR_LEN; | |
654 | } else if (left > 0) { | |
655 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, | |
656 | ("%s: ie length mismatch, %u too much", | |
657 | __func__, left)); | |
658 | return _FAIL; | |
659 | } | |
660 | ||
661 | /* pairwise_cipher */ | |
662 | if (left >= 2) { | |
663 | /* count = le16_to_cpu(*(u16*)pos); */ | |
c17416ef | 664 | count = get_unaligned_le16(pos); |
5e93f352 LF |
665 | pos += 2; |
666 | left -= 2; | |
667 | ||
668 | if (count == 0 || left < count * RSN_SELECTOR_LEN) { | |
669 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, | |
670 | ("%s: ie count botch (pairwise), " | |
671 | "count %u left %u", | |
672 | __func__, count, left)); | |
673 | return _FAIL; | |
674 | } | |
675 | ||
676 | for (i = 0; i < count; i++) { | |
677 | *pairwise_cipher |= rtw_get_wpa2_cipher_suite23a(pos); | |
678 | ||
679 | pos += RSN_SELECTOR_LEN; | |
680 | left -= RSN_SELECTOR_LEN; | |
681 | } | |
682 | } else if (left == 1) { | |
683 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, | |
684 | ("%s: ie too short (for key mgmt)", __func__)); | |
685 | ||
686 | return _FAIL; | |
687 | } | |
688 | ||
689 | if (is_8021x) { | |
690 | if (left >= 6) { | |
691 | pos += 2; | |
692 | if (!memcmp(pos, SUITE_1X, 4)) { | |
693 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
694 | ("%s (): there has 802.1x auth\n", | |
695 | __func__)); | |
696 | *is_8021x = 1; | |
697 | } | |
698 | } | |
699 | } | |
700 | ||
701 | return ret; | |
702 | } | |
703 | ||
704 | int rtw_get_sec_ie23a(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, | |
705 | u8 *wpa_ie, u16 *wpa_len) | |
706 | { | |
707 | u8 authmode, sec_idx, i; | |
708 | u8 wpa_oui[4] = {0x0, 0x50, 0xf2, 0x01}; | |
709 | uint cnt; | |
710 | ||
711 | ||
712 | ||
713 | /* Search required WPA or WPA2 IE and copy to sec_ie[ ] */ | |
714 | ||
715 | cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_); | |
716 | ||
717 | sec_idx = 0; | |
718 | ||
719 | while(cnt < in_len) { | |
720 | authmode = in_ie[cnt]; | |
721 | ||
722 | if ((authmode == _WPA_IE_ID_) && | |
723 | !memcmp(&in_ie[cnt+2], &wpa_oui[0], 4)) { | |
724 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
725 | ("\n rtw_get_wpa_ie23a: sec_idx =%d " | |
726 | "in_ie[cnt+1]+2 =%d\n", | |
727 | sec_idx, in_ie[cnt + 1] + 2)); | |
728 | ||
729 | if (wpa_ie) { | |
730 | memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt+1]+2); | |
731 | ||
732 | for (i = 0; i < (in_ie[cnt + 1] + 2); i = i + 8) { | |
733 | RT_TRACE(_module_rtl871x_mlme_c_, | |
734 | _drv_info_, | |
735 | ("\n %2x,%2x,%2x,%2x,%2x,%2x," | |
736 | "%2x,%2x\n", wpa_ie[i], | |
737 | wpa_ie[i + 1], wpa_ie[i + 2], | |
738 | wpa_ie[i + 3], wpa_ie[i + 4], | |
739 | wpa_ie[i + 5], wpa_ie[i + 6], | |
740 | wpa_ie[i + 7])); | |
741 | } | |
742 | } | |
743 | ||
744 | *wpa_len = in_ie[cnt + 1] + 2; | |
745 | cnt += in_ie[cnt + 1] + 2; /* get next */ | |
746 | } else { | |
747 | if (authmode == _WPA2_IE_ID_) { | |
748 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
749 | ("\n get_rsn_ie: sec_idx =%d in_ie" | |
750 | "[cnt+1]+2 =%d\n", sec_idx, | |
751 | in_ie[cnt + 1] + 2)); | |
752 | ||
753 | if (rsn_ie) { | |
754 | memcpy(rsn_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); | |
755 | ||
756 | for (i = 0; i < (in_ie[cnt + 1] + 2); i = i + 8) { | |
757 | RT_TRACE(_module_rtl871x_mlme_c_, | |
758 | _drv_info_, | |
759 | ("\n %2x,%2x,%2x,%2x,%2x,%2x," | |
760 | "%2x,%2x\n", rsn_ie[i], | |
761 | rsn_ie[i + 1], rsn_ie[i + 2], | |
762 | rsn_ie[i + 3], rsn_ie[i + 4], | |
763 | rsn_ie[i + 5], rsn_ie[i + 6], | |
764 | rsn_ie[i + 7])); | |
765 | } | |
766 | } | |
767 | ||
768 | *rsn_len = in_ie[cnt + 1] + 2; | |
769 | cnt += in_ie[cnt + 1] + 2; /* get next */ | |
770 | } else { | |
771 | cnt += in_ie[cnt + 1] + 2; /* get next */ | |
772 | } | |
773 | } | |
774 | } | |
775 | ||
776 | ||
777 | ||
778 | return *rsn_len + *wpa_len; | |
779 | } | |
780 | ||
781 | u8 rtw_is_wps_ie23a(u8 *ie_ptr, uint *wps_ielen) | |
782 | { | |
783 | u8 match = false; | |
784 | u8 eid, wps_oui[4]= {0x0, 0x50, 0xf2, 0x04}; | |
785 | ||
786 | if (!ie_ptr) | |
787 | return match; | |
788 | ||
789 | eid = ie_ptr[0]; | |
790 | ||
791 | if ((eid == _WPA_IE_ID_) && !memcmp(&ie_ptr[2], wps_oui, 4)) { | |
792 | /* DBG_8723A("==> found WPS_IE.....\n"); */ | |
793 | *wps_ielen = ie_ptr[1] + 2; | |
794 | match = true; | |
795 | } | |
796 | return match; | |
797 | } | |
798 | ||
799 | /** | |
800 | * rtw_get_wps_ie23a - Search WPS IE from a series of IEs | |
801 | * @in_ie: Address of IEs to search | |
802 | * @in_len: Length limit from in_ie | |
803 | * @wps_ie: If not NULL and WPS IE is found, WPS IE will be copied to the | |
804 | * buf starting from wps_ie | |
805 | * @wps_ielen: If not NULL and WPS IE is found, will set to the length of | |
806 | * the entire WPS IE | |
807 | * | |
808 | * Returns: The address of the WPS IE found, or NULL | |
809 | */ | |
810 | u8 *rtw_get_wps_ie23a(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen) | |
811 | { | |
812 | uint cnt; | |
813 | u8 *wpsie_ptr = NULL; | |
814 | u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; | |
815 | ||
816 | if (wps_ielen) | |
817 | *wps_ielen = 0; | |
818 | ||
819 | if (!in_ie || in_len <= 0) | |
820 | return wpsie_ptr; | |
821 | ||
822 | cnt = 0; | |
823 | ||
824 | while (cnt < in_len) { | |
825 | eid = in_ie[cnt]; | |
826 | ||
827 | if ((eid == _WPA_IE_ID_) && !memcmp(&in_ie[cnt+2], wps_oui, 4)) { | |
828 | wpsie_ptr = &in_ie[cnt]; | |
829 | ||
830 | if (wps_ie) | |
831 | memcpy(wps_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); | |
832 | ||
833 | if (wps_ielen) | |
834 | *wps_ielen = in_ie[cnt + 1] + 2; | |
835 | ||
836 | cnt += in_ie[cnt + 1] + 2; | |
837 | ||
838 | break; | |
839 | } else { | |
840 | cnt += in_ie[cnt + 1] + 2; /* goto next */ | |
841 | } | |
842 | } | |
843 | ||
844 | return wpsie_ptr; | |
845 | } | |
846 | ||
847 | /** | |
848 | * rtw_get_wps_attr23a - Search a specific WPS attribute from a given WPS IE | |
849 | * @wps_ie: Address of WPS IE to search | |
850 | * @wps_ielen: Length limit from wps_ie | |
851 | * @target_attr_id: The attribute ID of WPS attribute to search | |
852 | * @buf_attr: If not NULL and the WPS attribute is found, WPS attribute | |
853 | * will be copied to the buf starting from buf_attr | |
854 | * @len_attr: If not NULL and the WPS attribute is found, will set to the | |
855 | * length of the entire WPS attribute | |
856 | * | |
857 | * Returns: the address of the specific WPS attribute found, or NULL | |
858 | */ | |
859 | u8 *rtw_get_wps_attr23a(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, | |
860 | u8 *buf_attr, u32 *len_attr) | |
861 | { | |
862 | u8 *attr_ptr = NULL; | |
863 | u8 * target_attr_ptr = NULL; | |
864 | u8 wps_oui[4] = {0x00, 0x50, 0xF2, 0x04}; | |
865 | ||
866 | if (len_attr) | |
867 | *len_attr = 0; | |
868 | ||
869 | if ((wps_ie[0] != _VENDOR_SPECIFIC_IE_) || | |
870 | memcmp(wps_ie + 2, wps_oui, 4)) { | |
871 | return attr_ptr; | |
872 | } | |
873 | ||
874 | /* 6 = 1(Element ID) + 1(Length) + 4(WPS OUI) */ | |
875 | attr_ptr = wps_ie + 6; /* goto first attr */ | |
876 | ||
877 | while (attr_ptr - wps_ie < wps_ielen) { | |
878 | /* 4 = 2(Attribute ID) + 2(Length) */ | |
c17416ef LF |
879 | u16 attr_id = get_unaligned_be16(attr_ptr); |
880 | u16 attr_data_len = get_unaligned_be16(attr_ptr + 2); | |
5e93f352 LF |
881 | u16 attr_len = attr_data_len + 4; |
882 | ||
883 | /* DBG_8723A("%s attr_ptr:%p, id:%u, length:%u\n", __func__, attr_ptr, attr_id, attr_data_len); */ | |
884 | if (attr_id == target_attr_id) { | |
885 | target_attr_ptr = attr_ptr; | |
886 | ||
887 | if (buf_attr) | |
888 | memcpy(buf_attr, attr_ptr, attr_len); | |
889 | ||
890 | if (len_attr) | |
891 | *len_attr = attr_len; | |
892 | ||
893 | break; | |
894 | } else { | |
895 | attr_ptr += attr_len; /* goto next */ | |
896 | } | |
897 | } | |
898 | ||
899 | return target_attr_ptr; | |
900 | } | |
901 | ||
902 | /** | |
903 | * rtw_get_wps_attr_content23a - Search a specific WPS attribute content | |
904 | * from a given WPS IE | |
905 | * @wps_ie: Address of WPS IE to search | |
906 | * @wps_ielen: Length limit from wps_ie | |
907 | * @target_attr_id: The attribute ID of WPS attribute to search | |
908 | * @buf_content: If not NULL and the WPS attribute is found, WPS attribute | |
909 | * content will be copied to the buf starting from buf_content | |
910 | * @len_content: If not NULL and the WPS attribute is found, will set to the | |
911 | * length of the WPS attribute content | |
912 | * | |
913 | * Returns: the address of the specific WPS attribute content found, or NULL | |
914 | */ | |
915 | u8 *rtw_get_wps_attr_content23a(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, | |
916 | u8 *buf_content, uint *len_content) | |
917 | { | |
918 | u8 *attr_ptr; | |
919 | u32 attr_len; | |
920 | ||
921 | if (len_content) | |
922 | *len_content = 0; | |
923 | ||
924 | attr_ptr = rtw_get_wps_attr23a(wps_ie, wps_ielen, target_attr_id, | |
925 | NULL, &attr_len); | |
926 | ||
927 | if (attr_ptr && attr_len) { | |
928 | if (buf_content) | |
929 | memcpy(buf_content, attr_ptr + 4, attr_len - 4); | |
930 | ||
931 | if (len_content) | |
932 | *len_content = attr_len - 4; | |
933 | ||
934 | return attr_ptr + 4; | |
935 | } | |
936 | ||
937 | return NULL; | |
938 | } | |
939 | ||
940 | static int | |
941 | rtw_ieee802_11_parse_vendor_specific(u8 *pos, uint elen, | |
942 | struct rtw_ieee802_11_elems *elems, | |
943 | int show_errors) | |
944 | { | |
945 | unsigned int oui; | |
946 | ||
947 | /* first 3 bytes in vendor specific information element are the IEEE | |
948 | * OUI of the vendor. The following byte is used a vendor specific | |
949 | * sub-type. */ | |
950 | if (elen < 4) { | |
951 | if (show_errors) { | |
952 | DBG_8723A("short vendor specific " | |
953 | "information element ignored (len =%lu)\n", | |
954 | (unsigned long) elen); | |
955 | } | |
956 | return -1; | |
957 | } | |
958 | ||
959 | oui = RTW_GET_BE24(pos); | |
960 | switch (oui) { | |
961 | case WLAN_OUI_MICROSOFT: | |
962 | /* Microsoft/Wi-Fi information elements are further typed and | |
963 | * subtyped */ | |
964 | switch (pos[3]) { | |
965 | case 1: | |
966 | /* Microsoft OUI (00:50:F2) with OUI Type 1: | |
967 | * real WPA information element */ | |
968 | elems->wpa_ie = pos; | |
969 | elems->wpa_ie_len = elen; | |
970 | break; | |
971 | case WME_OUI_TYPE: /* this is a Wi-Fi WME info. element */ | |
972 | if (elen < 5) { | |
973 | DBG_8723A("short WME " | |
974 | "information element ignored " | |
975 | "(len =%lu)\n", | |
976 | (unsigned long) elen); | |
977 | return -1; | |
978 | } | |
979 | switch (pos[4]) { | |
980 | case WME_OUI_SUBTYPE_INFORMATION_ELEMENT: | |
981 | case WME_OUI_SUBTYPE_PARAMETER_ELEMENT: | |
982 | elems->wme = pos; | |
983 | elems->wme_len = elen; | |
984 | break; | |
985 | case WME_OUI_SUBTYPE_TSPEC_ELEMENT: | |
986 | elems->wme_tspec = pos; | |
987 | elems->wme_tspec_len = elen; | |
988 | break; | |
989 | default: | |
990 | DBG_8723A("unknown WME " | |
991 | "information element ignored " | |
992 | "(subtype =%d len =%lu)\n", | |
993 | pos[4], (unsigned long) elen); | |
994 | return -1; | |
995 | } | |
996 | break; | |
997 | case 4: | |
998 | /* Wi-Fi Protected Setup (WPS) IE */ | |
999 | elems->wps_ie = pos; | |
1000 | elems->wps_ie_len = elen; | |
1001 | break; | |
1002 | default: | |
1003 | DBG_8723A("Unknown Microsoft " | |
1004 | "information element ignored " | |
1005 | "(type =%d len =%lu)\n", | |
1006 | pos[3], (unsigned long) elen); | |
1007 | return -1; | |
1008 | } | |
1009 | break; | |
1010 | ||
1011 | case OUI_BROADCOM: | |
1012 | switch (pos[3]) { | |
1013 | case VENDOR_HT_CAPAB_OUI_TYPE: | |
1014 | elems->vendor_ht_cap = pos; | |
1015 | elems->vendor_ht_cap_len = elen; | |
1016 | break; | |
1017 | default: | |
1018 | DBG_8723A("Unknown Broadcom " | |
1019 | "information element ignored " | |
1020 | "(type =%d len =%lu)\n", | |
1021 | pos[3], (unsigned long) elen); | |
1022 | return -1; | |
1023 | } | |
1024 | break; | |
1025 | ||
1026 | default: | |
1027 | DBG_8723A("unknown vendor specific information " | |
1028 | "element ignored (vendor OUI %02x:%02x:%02x " | |
1029 | "len =%lu)\n", | |
1030 | pos[0], pos[1], pos[2], (unsigned long) elen); | |
1031 | return -1; | |
1032 | } | |
1033 | ||
1034 | return 0; | |
1035 | } | |
1036 | ||
1037 | /** | |
1038 | * ieee802_11_parse_elems - Parse information elements in management frames | |
1039 | * @start: Pointer to the start of IEs | |
1040 | * @len: Length of IE buffer in octets | |
1041 | * @elems: Data structure for parsed elements | |
1042 | * @show_errors: Whether to show parsing errors in debug log | |
1043 | * Returns: Parsing result | |
1044 | */ | |
1045 | enum parse_res rtw_ieee802_11_parse_elems23a(u8 *start, uint len, | |
1046 | struct rtw_ieee802_11_elems *elems, | |
1047 | int show_errors) | |
1048 | { | |
1049 | uint left = len; | |
1050 | u8 *pos = start; | |
1051 | int unknown = 0; | |
1052 | ||
1053 | memset(elems, 0, sizeof(*elems)); | |
1054 | ||
1055 | while (left >= 2) { | |
1056 | u8 id, elen; | |
1057 | ||
1058 | id = *pos++; | |
1059 | elen = *pos++; | |
1060 | left -= 2; | |
1061 | ||
1062 | if (elen > left) { | |
1063 | if (show_errors) { | |
1064 | DBG_8723A("IEEE 802.11 element " | |
1065 | "parse failed (id =%d elen =%d " | |
1066 | "left =%lu)\n", | |
1067 | id, elen, (unsigned long) left); | |
1068 | } | |
1069 | return ParseFailed; | |
1070 | } | |
1071 | ||
1072 | switch (id) { | |
1073 | case WLAN_EID_SSID: | |
1074 | elems->ssid = pos; | |
1075 | elems->ssid_len = elen; | |
1076 | break; | |
1077 | case WLAN_EID_SUPP_RATES: | |
1078 | elems->supp_rates = pos; | |
1079 | elems->supp_rates_len = elen; | |
1080 | break; | |
1081 | case WLAN_EID_FH_PARAMS: | |
1082 | elems->fh_params = pos; | |
1083 | elems->fh_params_len = elen; | |
1084 | break; | |
1085 | case WLAN_EID_DS_PARAMS: | |
1086 | elems->ds_params = pos; | |
1087 | elems->ds_params_len = elen; | |
1088 | break; | |
1089 | case WLAN_EID_CF_PARAMS: | |
1090 | elems->cf_params = pos; | |
1091 | elems->cf_params_len = elen; | |
1092 | break; | |
1093 | case WLAN_EID_TIM: | |
1094 | elems->tim = pos; | |
1095 | elems->tim_len = elen; | |
1096 | break; | |
1097 | case WLAN_EID_IBSS_PARAMS: | |
1098 | elems->ibss_params = pos; | |
1099 | elems->ibss_params_len = elen; | |
1100 | break; | |
1101 | case WLAN_EID_CHALLENGE: | |
1102 | elems->challenge = pos; | |
1103 | elems->challenge_len = elen; | |
1104 | break; | |
1105 | case WLAN_EID_ERP_INFO: | |
1106 | elems->erp_info = pos; | |
1107 | elems->erp_info_len = elen; | |
1108 | break; | |
1109 | case WLAN_EID_EXT_SUPP_RATES: | |
1110 | elems->ext_supp_rates = pos; | |
1111 | elems->ext_supp_rates_len = elen; | |
1112 | break; | |
1113 | case WLAN_EID_VENDOR_SPECIFIC: | |
1114 | if (rtw_ieee802_11_parse_vendor_specific(pos, elen, | |
1115 | elems, | |
1116 | show_errors)) | |
1117 | unknown++; | |
1118 | break; | |
1119 | case WLAN_EID_RSN: | |
1120 | elems->rsn_ie = pos; | |
1121 | elems->rsn_ie_len = elen; | |
1122 | break; | |
1123 | case WLAN_EID_PWR_CAPABILITY: | |
1124 | elems->power_cap = pos; | |
1125 | elems->power_cap_len = elen; | |
1126 | break; | |
1127 | case WLAN_EID_SUPPORTED_CHANNELS: | |
1128 | elems->supp_channels = pos; | |
1129 | elems->supp_channels_len = elen; | |
1130 | break; | |
1131 | case WLAN_EID_MOBILITY_DOMAIN: | |
1132 | elems->mdie = pos; | |
1133 | elems->mdie_len = elen; | |
1134 | break; | |
1135 | case WLAN_EID_FAST_BSS_TRANSITION: | |
1136 | elems->ftie = pos; | |
1137 | elems->ftie_len = elen; | |
1138 | break; | |
1139 | case WLAN_EID_TIMEOUT_INTERVAL: | |
1140 | elems->timeout_int = pos; | |
1141 | elems->timeout_int_len = elen; | |
1142 | break; | |
1143 | case WLAN_EID_HT_CAPABILITY: | |
1144 | elems->ht_capabilities = pos; | |
1145 | elems->ht_capabilities_len = elen; | |
1146 | break; | |
1147 | case WLAN_EID_HT_OPERATION: | |
1148 | elems->ht_operation = pos; | |
1149 | elems->ht_operation_len = elen; | |
1150 | break; | |
1151 | default: | |
1152 | unknown++; | |
1153 | if (!show_errors) | |
1154 | break; | |
1155 | DBG_8723A("IEEE 802.11 element parse " | |
1156 | "ignored unknown element (id =%d elen =%d)\n", | |
1157 | id, elen); | |
1158 | break; | |
1159 | } | |
1160 | ||
1161 | left -= elen; | |
1162 | pos += elen; | |
1163 | } | |
1164 | ||
1165 | if (left) | |
1166 | return ParseFailed; | |
1167 | ||
1168 | return unknown ? ParseUnknown : ParseOK; | |
1169 | } | |
1170 | ||
1171 | static u8 key_char2num(u8 ch) | |
1172 | { | |
1173 | if ((ch >= '0') && (ch <= '9')) | |
1174 | return ch - '0'; | |
1175 | else if ((ch >= 'a') && (ch <= 'f')) | |
1176 | return ch - 'a' + 10; | |
1177 | else if ((ch >= 'A') && (ch <= 'F')) | |
1178 | return ch - 'A' + 10; | |
1179 | else | |
1180 | return 0xff; | |
1181 | } | |
1182 | ||
1183 | u8 str_2char2num23a(u8 hch, u8 lch) | |
1184 | { | |
1185 | return (key_char2num(hch) * 10) + key_char2num(lch); | |
1186 | } | |
1187 | ||
1188 | u8 key_2char2num23a(u8 hch, u8 lch) | |
1189 | { | |
1190 | return (key_char2num(hch) << 4) | key_char2num(lch); | |
1191 | } | |
1192 | ||
1193 | void rtw_macaddr_cfg23a(u8 *mac_addr) | |
1194 | { | |
1195 | u8 mac[ETH_ALEN]; | |
1196 | if (!mac_addr) | |
1197 | return; | |
1198 | ||
1199 | memcpy(mac, mac_addr, ETH_ALEN); | |
1200 | ||
1201 | if (is_broadcast_ether_addr(mac) || is_zero_ether_addr(mac)) { | |
1202 | mac[0] = 0x00; | |
1203 | mac[1] = 0xe0; | |
1204 | mac[2] = 0x4c; | |
1205 | mac[3] = 0x87; | |
1206 | mac[4] = 0x00; | |
1207 | mac[5] = 0x00; | |
1208 | /* use default mac addresss */ | |
1209 | memcpy(mac_addr, mac, ETH_ALEN); | |
1210 | DBG_8723A("MAC Address from efuse error, assign default " | |
1211 | "one !!!\n"); | |
1212 | } | |
1213 | DBG_8723A("rtw_macaddr_cfg23a MAC Address = "MAC_FMT"\n", | |
1214 | MAC_ARG(mac_addr)); | |
1215 | } | |
1216 | ||
1217 | void dump_ies23a(u8 *buf, u32 buf_len) { | |
1218 | u8* pos = (u8*)buf; | |
1219 | u8 id, len; | |
1220 | ||
1221 | while (pos-buf <= buf_len) { | |
1222 | id = *pos; | |
1223 | len = *(pos + 1); | |
1224 | ||
1225 | DBG_8723A("%s ID:%u, LEN:%u\n", __func__, id, len); | |
1226 | #ifdef CONFIG_8723AU_P2P | |
1227 | dump_p2p_ie23a(pos, len); | |
1228 | #endif | |
1229 | dump_wps_ie23a(pos, len); | |
1230 | ||
1231 | pos += (2 + len); | |
1232 | } | |
1233 | } | |
1234 | ||
1235 | void dump_wps_ie23a(u8 *ie, u32 ie_len) { | |
1236 | u8* pos = (u8*)ie; | |
1237 | u16 id; | |
1238 | u16 len; | |
1239 | ||
1240 | u8 *wps_ie; | |
1241 | uint wps_ielen; | |
1242 | ||
1243 | wps_ie = rtw_get_wps_ie23a(ie, ie_len, NULL, &wps_ielen); | |
1244 | if (wps_ie != ie || wps_ielen == 0) | |
1245 | return; | |
1246 | ||
1247 | pos+= 6; | |
1248 | while (pos-ie < ie_len) { | |
c17416ef LF |
1249 | id = get_unaligned_be16(pos); |
1250 | len = get_unaligned_be16(pos + 2); | |
5e93f352 LF |
1251 | |
1252 | DBG_8723A("%s ID:0x%04x, LEN:%u\n", __func__, id, len); | |
1253 | ||
1254 | pos += (4 + len); | |
1255 | } | |
1256 | } | |
1257 | ||
1258 | #ifdef CONFIG_8723AU_P2P | |
1259 | void dump_p2p_ie23a(u8 *ie, u32 ie_len) { | |
1260 | u8* pos = (u8*)ie; | |
1261 | u8 id; | |
1262 | u16 len; | |
1263 | ||
1264 | u8 *p2p_ie; | |
1265 | uint p2p_ielen; | |
1266 | ||
1267 | p2p_ie = rtw_get_p2p_ie23a(ie, ie_len, NULL, &p2p_ielen); | |
1268 | if (p2p_ie != ie || p2p_ielen == 0) | |
1269 | return; | |
1270 | ||
1271 | pos += 6; | |
1272 | while (pos-ie < ie_len) { | |
1273 | id = *pos; | |
c17416ef | 1274 | len = get_unaligned_le16(pos+1); |
5e93f352 LF |
1275 | |
1276 | DBG_8723A("%s ID:%u, LEN:%u\n", __func__, id, len); | |
1277 | ||
1278 | pos+= (3+len); | |
1279 | } | |
1280 | } | |
1281 | ||
1282 | /** | |
1283 | * rtw_get_p2p_ie23a - Search P2P IE from a series of IEs | |
1284 | * @in_ie: Address of IEs to search | |
1285 | * @in_len: Length limit from in_ie | |
1286 | * @p2p_ie: If not NULL and P2P IE is found, P2P IE will be copied to the | |
1287 | * buf starting from p2p_ie | |
1288 | * @p2p_ielen: If not NULL and P2P IE is found, will set to the length of | |
1289 | * the entire P2P IE | |
1290 | * | |
1291 | * Returns: The address of the P2P IE found, or NULL | |
1292 | */ | |
1293 | u8 *rtw_get_p2p_ie23a(u8 *in_ie, int in_len, u8 *p2p_ie, uint *p2p_ielen) | |
1294 | { | |
1295 | uint cnt = 0; | |
1296 | u8 *p2p_ie_ptr; | |
1297 | u8 eid, p2p_oui[4]={0x50, 0x6F, 0x9A, 0x09}; | |
1298 | ||
1299 | if (p2p_ielen) | |
1300 | *p2p_ielen = 0; | |
1301 | ||
1302 | while (cnt<in_len) { | |
1303 | eid = in_ie[cnt]; | |
1304 | if ((in_len < 0) || (cnt > MAX_IE_SZ)) { | |
1305 | dump_stack(); | |
1306 | return NULL; | |
1307 | } | |
1308 | if ((eid == _VENDOR_SPECIFIC_IE_) && | |
1309 | !memcmp(&in_ie[cnt + 2], p2p_oui, 4)) { | |
1310 | p2p_ie_ptr = in_ie + cnt; | |
1311 | ||
1312 | if (p2p_ie != NULL) { | |
1313 | memcpy(p2p_ie, &in_ie[cnt], | |
1314 | in_ie[cnt + 1] + 2); | |
1315 | } | |
1316 | ||
1317 | if (p2p_ielen != NULL) { | |
1318 | *p2p_ielen = in_ie[cnt + 1] + 2; | |
1319 | } | |
1320 | ||
1321 | return p2p_ie_ptr; | |
1322 | ||
1323 | break; | |
1324 | } else { | |
1325 | cnt += in_ie[cnt + 1] + 2; /* goto next */ | |
1326 | } | |
1327 | } | |
1328 | ||
1329 | return NULL; | |
1330 | } | |
1331 | ||
1332 | /** | |
1333 | * rtw_get_p2p_attr23a - Search a specific P2P attribute from a given P2P IE | |
1334 | * @p2p_ie: Address of P2P IE to search | |
1335 | * @p2p_ielen: Length limit from p2p_ie | |
1336 | * @target_attr_id: The attribute ID of P2P attribute to search | |
1337 | * @buf_attr: If not NULL and the P2P attribute is found, P2P attribute will | |
1338 | * be copied to the buf starting from buf_attr | |
1339 | * @len_attr: If not NULL and the P2P attribute is found, will set to the | |
1340 | * length of the entire P2P attribute | |
1341 | * | |
1342 | * Returns: the address of the specific WPS attribute found, or NULL | |
1343 | */ | |
1344 | u8 *rtw_get_p2p_attr23a(u8 *p2p_ie, uint p2p_ielen, u8 target_attr_id, | |
1345 | u8 *buf_attr, u32 *len_attr) | |
1346 | { | |
1347 | u8 *attr_ptr = NULL; | |
1348 | u8 *target_attr_ptr = NULL; | |
1349 | u8 p2p_oui[4]={0x50, 0x6F, 0x9A, 0x09}; | |
1350 | ||
1351 | if (len_attr) | |
1352 | *len_attr = 0; | |
1353 | ||
1354 | if (!p2p_ie || (p2p_ie[0] != _VENDOR_SPECIFIC_IE_) || | |
1355 | memcmp(p2p_ie + 2, p2p_oui, 4)) { | |
1356 | return attr_ptr; | |
1357 | } | |
1358 | ||
1359 | /* 6 = 1(Element ID) + 1(Length) + 3 (OUI) + 1(OUI Type) */ | |
1360 | attr_ptr = p2p_ie + 6; /* goto first attr */ | |
1361 | ||
1362 | while (attr_ptr - p2p_ie < p2p_ielen) { | |
1363 | /* 3 = 1(Attribute ID) + 2(Length) */ | |
1364 | u8 attr_id = *attr_ptr; | |
c17416ef | 1365 | u16 attr_data_len = get_unaligned_le16(attr_ptr + 1); |
5e93f352 LF |
1366 | u16 attr_len = attr_data_len + 3; |
1367 | ||
1368 | /* DBG_8723A("%s attr_ptr:%p, id:%u, length:%u\n", __func__, attr_ptr, attr_id, attr_data_len); */ | |
1369 | if (attr_id == target_attr_id) { | |
1370 | target_attr_ptr = attr_ptr; | |
1371 | ||
1372 | if (buf_attr) | |
1373 | memcpy(buf_attr, attr_ptr, attr_len); | |
1374 | ||
1375 | if (len_attr) | |
1376 | *len_attr = attr_len; | |
1377 | ||
1378 | break; | |
1379 | } else { | |
1380 | attr_ptr += attr_len; /* goto next */ | |
1381 | } | |
1382 | } | |
1383 | ||
1384 | return target_attr_ptr; | |
1385 | } | |
1386 | ||
1387 | /** | |
1388 | * rtw_get_p2p_attr23a_content - Search a specific P2P attribute content from | |
1389 | * a given P2P IE | |
1390 | * @p2p_ie: Address of P2P IE to search | |
1391 | * @p2p_ielen: Length limit from p2p_ie | |
1392 | * @target_attr_id: The attribute ID of P2P attribute to search | |
1393 | * @buf_content: If not NULL and the P2P attribute is found, P2P attribute | |
1394 | * content will be copied to the buf starting from buf_content | |
1395 | * @len_content: If not NULL and the P2P attribute is found, will set to the | |
1396 | * length of the P2P attribute content | |
1397 | * | |
1398 | * Returns: the address of the specific P2P attribute content found, or NULL | |
1399 | */ | |
1400 | u8 *rtw_get_p2p_attr23a_content(u8 *p2p_ie, uint p2p_ielen, u8 target_attr_id, | |
1401 | u8 *buf_content, uint *len_content) | |
1402 | { | |
1403 | u8 *attr_ptr; | |
1404 | u32 attr_len; | |
1405 | ||
1406 | if (len_content) | |
1407 | *len_content = 0; | |
1408 | ||
1409 | attr_ptr = rtw_get_p2p_attr23a(p2p_ie, p2p_ielen, target_attr_id, | |
1410 | NULL, &attr_len); | |
1411 | ||
1412 | if (attr_ptr && attr_len) { | |
1413 | if (buf_content) | |
1414 | memcpy(buf_content, attr_ptr + 3, attr_len - 3); | |
1415 | ||
1416 | if (len_content) | |
1417 | *len_content = attr_len - 3; | |
1418 | ||
1419 | return attr_ptr+3; | |
1420 | } | |
1421 | ||
1422 | return NULL; | |
1423 | } | |
1424 | ||
1425 | u32 rtw_set_p2p_attr_content23a(u8 *pbuf, u8 attr_id, u16 attr_len, u8 *pdata_attr) | |
1426 | { | |
1427 | u32 a_len; | |
1428 | ||
1429 | *pbuf = attr_id; | |
1430 | ||
1431 | /* u16*)(pbuf + 1) = cpu_to_le16(attr_len); */ | |
c17416ef | 1432 | put_unaligned_le16(attr_len, pbuf + 1); |
5e93f352 LF |
1433 | |
1434 | if (pdata_attr) | |
1435 | memcpy(pbuf + 3, pdata_attr, attr_len); | |
1436 | ||
1437 | a_len = attr_len + 3; | |
1438 | ||
1439 | return a_len; | |
1440 | } | |
1441 | ||
1442 | static uint rtw_p2p_attr_remove(u8 *ie, uint ielen_ori, u8 attr_id) | |
1443 | { | |
1444 | u8 *target_attr; | |
1445 | u32 target_attr_len; | |
1446 | uint ielen = ielen_ori; | |
1447 | ||
1448 | while(1) { | |
1449 | target_attr = rtw_get_p2p_attr23a(ie, ielen, attr_id, NULL, | |
1450 | &target_attr_len); | |
1451 | if (target_attr && target_attr_len) { | |
1452 | u8 *next_attr = target_attr+target_attr_len; | |
1453 | uint remain_len = ielen-(next_attr-ie); | |
1454 | /* dump_ies23a(ie, ielen); */ | |
1455 | ||
1456 | memset(target_attr, 0, target_attr_len); | |
1457 | memcpy(target_attr, next_attr, remain_len); | |
1458 | memset(target_attr+remain_len, 0, target_attr_len); | |
1459 | *(ie + 1) -= target_attr_len; | |
1460 | ielen -= target_attr_len; | |
1461 | } else { | |
1462 | /* if (index>0) */ | |
1463 | /* dump_ies23a(ie, ielen); */ | |
1464 | break; | |
1465 | } | |
1466 | } | |
1467 | ||
1468 | return ielen; | |
1469 | } | |
1470 | ||
1471 | void rtw_wlan_bssid_ex_remove_p2p_attr23a(struct wlan_bssid_ex *bss_ex, u8 attr_id) | |
1472 | { | |
1473 | u8 *p2p_ie; | |
1474 | uint p2p_ielen, p2p_ielen_ori; | |
1475 | ||
1476 | if ((p2p_ie = rtw_get_p2p_ie23a(bss_ex->IEs + _FIXED_IE_LENGTH_, | |
1477 | bss_ex->IELength - _FIXED_IE_LENGTH_, | |
1478 | NULL, &p2p_ielen_ori))) { | |
1479 | p2p_ielen = rtw_p2p_attr_remove(p2p_ie, p2p_ielen_ori, attr_id); | |
1480 | if (p2p_ielen != p2p_ielen_ori) { | |
1481 | u8 *next_ie_ori = p2p_ie+p2p_ielen_ori; | |
1482 | u8 *next_ie = p2p_ie+p2p_ielen; | |
1483 | uint remain_len; | |
1484 | remain_len = bss_ex->IELength-(next_ie_ori-bss_ex->IEs); | |
1485 | ||
1486 | memcpy(next_ie, next_ie_ori, remain_len); | |
1487 | memset(next_ie+remain_len, 0, p2p_ielen_ori-p2p_ielen); | |
1488 | bss_ex->IELength -= p2p_ielen_ori-p2p_ielen; | |
1489 | } | |
1490 | } | |
1491 | } | |
1492 | ||
1493 | #endif /* CONFIG_8723AU_P2P */ | |
1494 | ||
1495 | #ifdef CONFIG_8723AU_P2P | |
1496 | int rtw_get_wfd_ie(u8 *in_ie, int in_len, u8 *wfd_ie, uint *wfd_ielen) | |
1497 | { | |
1498 | int match; | |
f5d197b6 | 1499 | const u8 *ie; |
5e93f352 | 1500 | |
f5d197b6 | 1501 | match = 0; |
5e93f352 | 1502 | |
f5d197b6 | 1503 | if (in_len < 0) |
5e93f352 | 1504 | return match; |
5e93f352 | 1505 | |
f5d197b6 JS |
1506 | ie = cfg80211_find_vendor_ie(0x506F9A, 0x0A, in_ie, in_len); |
1507 | if (ie && (ie[1] <= (MAX_WFD_IE_LEN - 2))) { | |
1508 | if (wfd_ie) { | |
1509 | *wfd_ielen = ie[1] + 2; | |
1510 | memcpy(wfd_ie, ie, ie[1] + 2); | |
1511 | } else | |
1512 | if (wfd_ielen) | |
1513 | *wfd_ielen = 0; | |
5e93f352 | 1514 | |
f5d197b6 | 1515 | match = 1; |
5e93f352 LF |
1516 | } |
1517 | ||
1518 | return match; | |
1519 | } | |
1520 | ||
1521 | /* attr_content: The output buffer, contains the "body field" of | |
1522 | WFD attribute. */ | |
1523 | /* attr_contentlen: The data length of the "body field" of WFD | |
1524 | attribute. */ | |
1525 | int rtw_get_wfd_attr_content(u8 *wfd_ie, uint wfd_ielen, u8 target_attr_id, | |
1526 | u8 *attr_content, uint *attr_contentlen) | |
1527 | { | |
1528 | int match; | |
1529 | uint cnt = 0; | |
1530 | u8 attr_id, wfd_oui[4] = {0x50, 0x6F, 0x9A, 0x0A}; | |
1531 | ||
1532 | match = false; | |
1533 | ||
1534 | if ((wfd_ie[0] != _VENDOR_SPECIFIC_IE_) || | |
1535 | memcmp(wfd_ie + 2, wfd_oui, 4)) { | |
1536 | return match; | |
1537 | } | |
1538 | ||
1539 | /* 1 (WFD IE) + 1 (Length) + 3 (OUI) + 1 (OUI Type) */ | |
1540 | cnt = 6; | |
1541 | while (cnt < wfd_ielen) { | |
c17416ef | 1542 | u16 attrlen = get_unaligned_be16(wfd_ie + cnt + 1); |
5e93f352 LF |
1543 | |
1544 | attr_id = wfd_ie[cnt]; | |
1545 | if (attr_id == target_attr_id) { | |
1546 | /* 3 -> 1 byte for attribute ID field, 2 | |
1547 | bytes for length field */ | |
1548 | if (attr_content) | |
1549 | memcpy(attr_content, &wfd_ie[cnt + 3], attrlen); | |
1550 | ||
1551 | if (attr_contentlen) | |
1552 | *attr_contentlen = attrlen; | |
1553 | ||
1554 | cnt += attrlen + 3; | |
1555 | ||
1556 | match = true; | |
1557 | break; | |
1558 | } else { | |
1559 | cnt += attrlen + 3; /* goto next */ | |
1560 | } | |
1561 | } | |
1562 | ||
1563 | return match; | |
1564 | } | |
1565 | #endif /* CONFIG_8723AU_P2P */ | |
1566 | ||
1567 | /* Baron adds to avoid FreeBSD warning */ | |
1568 | int ieee80211_is_empty_essid23a(const char *essid, int essid_len) | |
1569 | { | |
1570 | /* Single white space is for Linksys APs */ | |
1571 | if (essid_len == 1 && essid[0] == ' ') | |
1572 | return 1; | |
1573 | ||
1574 | /* Otherwise, if the entire essid is 0, we assume it is hidden */ | |
1575 | while (essid_len) { | |
1576 | essid_len--; | |
1577 | if (essid[essid_len] != '\0') | |
1578 | return 0; | |
1579 | } | |
1580 | ||
1581 | return 1; | |
1582 | } | |
1583 | ||
1584 | static int rtw_get_cipher_info(struct wlan_network *pnetwork) | |
1585 | { | |
1586 | u32 wpa_ielen; | |
1587 | unsigned char *pbuf; | |
1588 | int group_cipher = 0, pairwise_cipher = 0, is8021x = 0; | |
1589 | int ret = _FAIL; | |
1590 | int r; | |
1591 | pbuf = rtw_get_wpa_ie23a(&pnetwork->network.IEs[12], &wpa_ielen, | |
1592 | pnetwork->network.IELength - 12); | |
1593 | ||
1594 | if (pbuf && (wpa_ielen > 0)) { | |
1595 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
1596 | ("rtw_get_cipher_info: wpa_ielen: %d", wpa_ielen)); | |
1597 | r = rtw_parse_wpa_ie23a(pbuf, wpa_ielen + 2, &group_cipher, | |
1598 | &pairwise_cipher, &is8021x); | |
1599 | if (r == _SUCCESS) { | |
1600 | pnetwork->BcnInfo.pairwise_cipher = pairwise_cipher; | |
1601 | pnetwork->BcnInfo.group_cipher = group_cipher; | |
1602 | pnetwork->BcnInfo.is_8021x = is8021x; | |
1603 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
1604 | ("%s: pnetwork->pairwise_cipher: %d, is_" | |
1605 | "8021x is %d", __func__, | |
1606 | pnetwork->BcnInfo.pairwise_cipher, | |
1607 | pnetwork->BcnInfo.is_8021x)); | |
1608 | ret = _SUCCESS; | |
1609 | } | |
1610 | } else { | |
1611 | pbuf = rtw_get_wpa2_ie23a(&pnetwork->network.IEs[12], &wpa_ielen, | |
1612 | pnetwork->network.IELength - 12); | |
1613 | ||
1614 | if (pbuf && (wpa_ielen > 0)) { | |
1615 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
1616 | ("get RSN IE\n")); | |
1617 | r = rtw_parse_wpa2_ie23a(pbuf, wpa_ielen + 2, | |
1618 | &group_cipher, &pairwise_cipher, | |
1619 | &is8021x); | |
1620 | if (r == _SUCCESS) { | |
1621 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
1622 | ("get RSN IE OK!!!\n")); | |
1623 | pnetwork->BcnInfo.pairwise_cipher = | |
1624 | pairwise_cipher; | |
1625 | pnetwork->BcnInfo.group_cipher = group_cipher; | |
1626 | pnetwork->BcnInfo.is_8021x = is8021x; | |
1627 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
1628 | ("%s: pnetwork->pairwise_cipher: %d," | |
1629 | "pnetwork->group_cipher is %d, " | |
1630 | "is_8021x is %d", __func__, | |
1631 | pnetwork->BcnInfo.pairwise_cipher, | |
1632 | pnetwork->BcnInfo.group_cipher, | |
1633 | pnetwork->BcnInfo.is_8021x)); | |
1634 | ret = _SUCCESS; | |
1635 | } | |
1636 | } | |
1637 | } | |
1638 | ||
1639 | return ret; | |
1640 | } | |
1641 | ||
1642 | void rtw_get_bcn_info23a(struct wlan_network *pnetwork) | |
1643 | { | |
1644 | unsigned short cap = 0; | |
1645 | u8 bencrypt = 0; | |
1646 | /* u8 wpa_ie[255], rsn_ie[255]; */ | |
1647 | u16 wpa_len = 0, rsn_len = 0; | |
1648 | struct HT_info_element *pht_info = NULL; | |
1649 | struct ieee80211_ht_cap *pht_cap = NULL; | |
1650 | unsigned int len; | |
1651 | unsigned char *p; | |
1652 | ||
1653 | memcpy(&cap, rtw_get_capability23a_from_ie(pnetwork->network.IEs), 2); | |
1654 | cap = le16_to_cpu(cap); | |
1655 | if (cap & WLAN_CAPABILITY_PRIVACY) { | |
1656 | bencrypt = 1; | |
1657 | pnetwork->network.Privacy = 1; | |
1658 | } else { | |
1659 | pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_OPENSYS; | |
1660 | } | |
1661 | rtw_get_sec_ie23a(pnetwork->network.IEs, pnetwork->network.IELength, | |
1662 | NULL, &rsn_len, NULL, &wpa_len); | |
1663 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
1664 | ("rtw_get_bcn_info23a: ssid =%s\n", pnetwork->network.Ssid.ssid)); | |
1665 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
1666 | ("rtw_get_bcn_info23a: wpa_len =%d rsn_len =%d\n", | |
1667 | wpa_len, rsn_len)); | |
1668 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
1669 | ("rtw_get_bcn_info23a: ssid =%s\n", pnetwork->network.Ssid.ssid)); | |
1670 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
1671 | ("rtw_get_bcn_info23a: wpa_len =%d rsn_len =%d\n", | |
1672 | wpa_len, rsn_len)); | |
1673 | ||
1674 | if (rsn_len > 0) { | |
1675 | pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WPA2; | |
1676 | } else if (wpa_len > 0) { | |
1677 | pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WPA; | |
1678 | } else { | |
1679 | if (bencrypt) | |
1680 | pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WEP; | |
1681 | } | |
1682 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
1683 | ("rtw_get_bcn_info23a: pnetwork->encryp_protocol is %x\n", | |
1684 | pnetwork->BcnInfo.encryp_protocol)); | |
1685 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
1686 | ("rtw_get_bcn_info23a: pnetwork->encryp_protocol is %x\n", | |
1687 | pnetwork->BcnInfo.encryp_protocol)); | |
1688 | rtw_get_cipher_info(pnetwork); | |
1689 | ||
1690 | /* get bwmode and ch_offset */ | |
1691 | /* parsing HT_CAP_IE */ | |
1692 | p = rtw_get_ie23a(pnetwork->network.IEs + _FIXED_IE_LENGTH_, | |
1693 | _HT_CAPABILITY_IE_, &len, | |
1694 | pnetwork->network.IELength - _FIXED_IE_LENGTH_); | |
1695 | if (p && len > 0) { | |
1696 | pht_cap = (struct ieee80211_ht_cap *)(p + 2); | |
1697 | pnetwork->BcnInfo.ht_cap_info = pht_cap->cap_info; | |
1698 | } else { | |
1699 | pnetwork->BcnInfo.ht_cap_info = 0; | |
1700 | } | |
1701 | /* parsing HT_INFO_IE */ | |
1702 | p = rtw_get_ie23a(pnetwork->network.IEs + _FIXED_IE_LENGTH_, | |
1703 | _HT_ADD_INFO_IE_, &len, | |
1704 | pnetwork->network.IELength - _FIXED_IE_LENGTH_); | |
1705 | if (p && len > 0) { | |
1706 | pht_info = (struct HT_info_element *)(p + 2); | |
1707 | pnetwork->BcnInfo.ht_info_infos_0 = pht_info->infos[0]; | |
1708 | } else { | |
1709 | pnetwork->BcnInfo.ht_info_infos_0 = 0; | |
1710 | } | |
1711 | } | |
1712 | ||
1713 | /* show MCS rate, unit: 100Kbps */ | |
1714 | u16 rtw_mcs_rate23a(u8 rf_type, u8 bw_40MHz, u8 short_GI_20, u8 short_GI_40, | |
1715 | unsigned char * MCS_rate) | |
1716 | { | |
1717 | u16 max_rate = 0; | |
1718 | ||
1719 | if (rf_type == RF_1T1R) { | |
1720 | if (MCS_rate[0] & BIT(7)) | |
1721 | max_rate = (bw_40MHz) ? ((short_GI_40)?1500:1350): | |
1722 | ((short_GI_20)?722:650); | |
1723 | else if (MCS_rate[0] & BIT(6)) | |
1724 | max_rate = (bw_40MHz) ? ((short_GI_40)?1350:1215): | |
1725 | ((short_GI_20)?650:585); | |
1726 | else if (MCS_rate[0] & BIT(5)) | |
1727 | max_rate = (bw_40MHz) ? ((short_GI_40)?1200:1080): | |
1728 | ((short_GI_20)?578:520); | |
1729 | else if (MCS_rate[0] & BIT(4)) | |
1730 | max_rate = (bw_40MHz) ? ((short_GI_40)?900:810): | |
1731 | ((short_GI_20)?433:390); | |
1732 | else if (MCS_rate[0] & BIT(3)) | |
1733 | max_rate = (bw_40MHz) ? ((short_GI_40)?600:540): | |
1734 | ((short_GI_20)?289:260); | |
1735 | else if (MCS_rate[0] & BIT(2)) | |
1736 | max_rate = (bw_40MHz) ? ((short_GI_40)?450:405): | |
1737 | ((short_GI_20)?217:195); | |
1738 | else if (MCS_rate[0] & BIT(1)) | |
1739 | max_rate = (bw_40MHz) ? ((short_GI_40)?300:270): | |
1740 | ((short_GI_20)?144:130); | |
1741 | else if (MCS_rate[0] & BIT(0)) | |
1742 | max_rate = (bw_40MHz) ? ((short_GI_40)?150:135): | |
1743 | ((short_GI_20)?72:65); | |
1744 | } else { | |
1745 | if (MCS_rate[1]) { | |
1746 | if (MCS_rate[1] & BIT(7)) | |
1747 | max_rate = (bw_40MHz) ? ((short_GI_40)?3000:2700):((short_GI_20)?1444:1300); | |
1748 | else if (MCS_rate[1] & BIT(6)) | |
1749 | max_rate = (bw_40MHz) ? ((short_GI_40)?2700:2430):((short_GI_20)?1300:1170); | |
1750 | else if (MCS_rate[1] & BIT(5)) | |
1751 | max_rate = (bw_40MHz) ? ((short_GI_40)?2400:2160):((short_GI_20)?1156:1040); | |
1752 | else if (MCS_rate[1] & BIT(4)) | |
1753 | max_rate = (bw_40MHz) ? ((short_GI_40)?1800:1620):((short_GI_20)?867:780); | |
1754 | else if (MCS_rate[1] & BIT(3)) | |
1755 | max_rate = (bw_40MHz) ? ((short_GI_40)?1200:1080):((short_GI_20)?578:520); | |
1756 | else if (MCS_rate[1] & BIT(2)) | |
1757 | max_rate = (bw_40MHz) ? ((short_GI_40)?900:810):((short_GI_20)?433:390); | |
1758 | else if (MCS_rate[1] & BIT(1)) | |
1759 | max_rate = (bw_40MHz) ? ((short_GI_40)?600:540):((short_GI_20)?289:260); | |
1760 | else if (MCS_rate[1] & BIT(0)) | |
1761 | max_rate = (bw_40MHz) ? ((short_GI_40)?300:270):((short_GI_20)?144:130); | |
1762 | } else { | |
1763 | if (MCS_rate[0] & BIT(7)) | |
1764 | max_rate = (bw_40MHz) ? ((short_GI_40)?1500:1350):((short_GI_20)?722:650); | |
1765 | else if (MCS_rate[0] & BIT(6)) | |
1766 | max_rate = (bw_40MHz) ? ((short_GI_40)?1350:1215):((short_GI_20)?650:585); | |
1767 | else if (MCS_rate[0] & BIT(5)) | |
1768 | max_rate = (bw_40MHz) ? ((short_GI_40)?1200:1080):((short_GI_20)?578:520); | |
1769 | else if (MCS_rate[0] & BIT(4)) | |
1770 | max_rate = (bw_40MHz) ? ((short_GI_40)?900:810):((short_GI_20)?433:390); | |
1771 | else if (MCS_rate[0] & BIT(3)) | |
1772 | max_rate = (bw_40MHz) ? ((short_GI_40)?600:540):((short_GI_20)?289:260); | |
1773 | else if (MCS_rate[0] & BIT(2)) | |
1774 | max_rate = (bw_40MHz) ? ((short_GI_40)?450:405):((short_GI_20)?217:195); | |
1775 | else if (MCS_rate[0] & BIT(1)) | |
1776 | max_rate = (bw_40MHz) ? ((short_GI_40)?300:270):((short_GI_20)?144:130); | |
1777 | else if (MCS_rate[0] & BIT(0)) | |
1778 | max_rate = (bw_40MHz) ? ((short_GI_40)?150:135):((short_GI_20)?72:65); | |
1779 | } | |
1780 | } | |
1781 | return max_rate; | |
1782 | } | |
1783 | ||
1784 | int rtw_action_frame_parse23a(const u8 *frame, u32 frame_len, u8* category, | |
1785 | u8 *action) | |
1786 | { | |
1787 | const u8 *frame_body = frame + sizeof(struct ieee80211_hdr_3addr); | |
1788 | u16 fc; | |
1789 | u8 c, a = 0; | |
1790 | ||
1791 | fc = le16_to_cpu(((struct ieee80211_hdr_3addr *)frame)->frame_control); | |
1792 | ||
1793 | if ((fc & (IEEE80211_FCTL_FTYPE|IEEE80211_FCTL_STYPE)) != | |
1794 | (IEEE80211_FTYPE_MGMT|IEEE80211_STYPE_ACTION)) { | |
1795 | return false; | |
1796 | } | |
1797 | ||
1798 | c = frame_body[0]; | |
1799 | ||
1800 | switch (c) { | |
1801 | case WLAN_CATEGORY_VENDOR_SPECIFIC: /* vendor-specific */ | |
1802 | break; | |
1803 | default: | |
1804 | a = frame_body[1]; | |
1805 | } | |
1806 | ||
1807 | if (category) | |
1808 | *category = c; | |
1809 | if (action) | |
1810 | *action = a; | |
1811 | ||
1812 | return true; | |
1813 | } | |
1814 | ||
1815 | static const char *_action_public_str23a[] = { | |
1816 | "ACT_PUB_BSSCOEXIST", | |
1817 | "ACT_PUB_DSE_ENABLE", | |
1818 | "ACT_PUB_DSE_DEENABLE", | |
1819 | "ACT_PUB_DSE_REG_LOCATION", | |
1820 | "ACT_PUB_EXT_CHL_SWITCH", | |
1821 | "ACT_PUB_DSE_MSR_REQ", | |
1822 | "ACT_PUB_DSE_MSR_RPRT", | |
1823 | "ACT_PUB_MP", | |
1824 | "ACT_PUB_DSE_PWR_CONSTRAINT", | |
1825 | "ACT_PUB_VENDOR", | |
1826 | "ACT_PUB_GAS_INITIAL_REQ", | |
1827 | "ACT_PUB_GAS_INITIAL_RSP", | |
1828 | "ACT_PUB_GAS_COMEBACK_REQ", | |
1829 | "ACT_PUB_GAS_COMEBACK_RSP", | |
1830 | "ACT_PUB_TDLS_DISCOVERY_RSP", | |
1831 | "ACT_PUB_LOCATION_TRACK", | |
1832 | "ACT_PUB_RSVD", | |
1833 | }; | |
1834 | ||
1835 | const char *action_public_str23a(u8 action) | |
1836 | { | |
1837 | action = (action >= ACT_PUBLIC_MAX) ? ACT_PUBLIC_MAX : action; | |
1838 | return _action_public_str23a[action]; | |
1839 | } |