Commit | Line | Data |
---|---|---|
06a05884 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 | * | |
06a05884 LF |
14 | ******************************************************************************/ |
15 | #define _IEEE80211_C | |
16 | ||
027d3efd JS |
17 | #include <linux/ieee80211.h> |
18 | ||
06a05884 | 19 | #include <drv_types.h> |
0aba3f41 | 20 | #include <osdep_intf.h> |
06a05884 LF |
21 | #include <ieee80211.h> |
22 | #include <wifi.h> | |
23 | #include <osdep_service.h> | |
24 | #include <wlan_bssdef.h> | |
06a05884 LF |
25 | |
26 | u8 RTW_WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 }; | |
06a05884 LF |
27 | u8 WPA_AUTH_KEY_MGMT_NONE[] = { 0x00, 0x50, 0xf2, 0 }; |
28 | u8 WPA_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x50, 0xf2, 1 }; | |
29 | u8 WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x50, 0xf2, 2 }; | |
30 | u8 WPA_CIPHER_SUITE_NONE[] = { 0x00, 0x50, 0xf2, 0 }; | |
31 | u8 WPA_CIPHER_SUITE_WEP40[] = { 0x00, 0x50, 0xf2, 1 }; | |
32 | u8 WPA_CIPHER_SUITE_TKIP[] = { 0x00, 0x50, 0xf2, 2 }; | |
33 | u8 WPA_CIPHER_SUITE_WRAP[] = { 0x00, 0x50, 0xf2, 3 }; | |
34 | u8 WPA_CIPHER_SUITE_CCMP[] = { 0x00, 0x50, 0xf2, 4 }; | |
35 | u8 WPA_CIPHER_SUITE_WEP104[] = { 0x00, 0x50, 0xf2, 5 }; | |
36 | ||
37 | u16 RSN_VERSION_BSD = 1; | |
38 | u8 RSN_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x0f, 0xac, 1 }; | |
39 | u8 RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x0f, 0xac, 2 }; | |
40 | u8 RSN_CIPHER_SUITE_NONE[] = { 0x00, 0x0f, 0xac, 0 }; | |
41 | u8 RSN_CIPHER_SUITE_WEP40[] = { 0x00, 0x0f, 0xac, 1 }; | |
42 | u8 RSN_CIPHER_SUITE_TKIP[] = { 0x00, 0x0f, 0xac, 2 }; | |
43 | u8 RSN_CIPHER_SUITE_WRAP[] = { 0x00, 0x0f, 0xac, 3 }; | |
44 | u8 RSN_CIPHER_SUITE_CCMP[] = { 0x00, 0x0f, 0xac, 4 }; | |
45 | u8 RSN_CIPHER_SUITE_WEP104[] = { 0x00, 0x0f, 0xac, 5 }; | |
46 | /* */ | |
47 | /* for adhoc-master to generate ie and provide supported-rate to fw */ | |
48 | /* */ | |
49 | ||
50 | static u8 WIFI_CCKRATES[] = { | |
51 | (IEEE80211_CCK_RATE_1MB | IEEE80211_BASIC_RATE_MASK), | |
52 | (IEEE80211_CCK_RATE_2MB | IEEE80211_BASIC_RATE_MASK), | |
53 | (IEEE80211_CCK_RATE_5MB | IEEE80211_BASIC_RATE_MASK), | |
54 | (IEEE80211_CCK_RATE_11MB | IEEE80211_BASIC_RATE_MASK) | |
55 | }; | |
56 | ||
57 | static u8 WIFI_OFDMRATES[] = { | |
58 | (IEEE80211_OFDM_RATE_6MB), | |
59 | (IEEE80211_OFDM_RATE_9MB), | |
60 | (IEEE80211_OFDM_RATE_12MB), | |
61 | (IEEE80211_OFDM_RATE_18MB), | |
62 | (IEEE80211_OFDM_RATE_24MB), | |
63 | IEEE80211_OFDM_RATE_36MB, | |
64 | IEEE80211_OFDM_RATE_48MB, | |
65 | IEEE80211_OFDM_RATE_54MB | |
66 | }; | |
67 | ||
68 | ||
69 | int rtw_get_bit_value_from_ieee_value(u8 val) | |
70 | { | |
71 | unsigned char dot11_rate_table[] = { | |
72 | 2, 4, 11, 22, 12, 18, 24, 36, 48, | |
73 | 72, 96, 108, 0}; /* last element must be zero!! */ | |
74 | ||
75 | int i = 0; | |
76 | while (dot11_rate_table[i] != 0) { | |
77 | if (dot11_rate_table[i] == val) | |
78 | return BIT(i); | |
79 | i++; | |
80 | } | |
81 | return 0; | |
82 | } | |
83 | ||
84 | uint rtw_is_cckrates_included(u8 *rate) | |
85 | { | |
86 | u32 i = 0; | |
87 | ||
88 | while (rate[i] != 0) { | |
89 | if ((((rate[i]) & 0x7f) == 2) || (((rate[i]) & 0x7f) == 4) || | |
90 | (((rate[i]) & 0x7f) == 11) || (((rate[i]) & 0x7f) == 22)) | |
91 | return true; | |
92 | i++; | |
93 | } | |
94 | return false; | |
95 | } | |
96 | ||
97 | uint rtw_is_cckratesonly_included(u8 *rate) | |
98 | { | |
99 | u32 i = 0; | |
100 | ||
101 | while (rate[i] != 0) { | |
102 | if ((((rate[i]) & 0x7f) != 2) && (((rate[i]) & 0x7f) != 4) && | |
103 | (((rate[i]) & 0x7f) != 11) && (((rate[i]) & 0x7f) != 22)) | |
104 | return false; | |
105 | i++; | |
106 | } | |
107 | ||
108 | return true; | |
109 | } | |
110 | ||
111 | int rtw_check_network_type(unsigned char *rate, int ratelen, int channel) | |
112 | { | |
113 | if (channel > 14) { | |
114 | if ((rtw_is_cckrates_included(rate)) == true) | |
115 | return WIRELESS_INVALID; | |
116 | else | |
117 | return WIRELESS_11A; | |
118 | } else { /* could be pure B, pure G, or B/G */ | |
119 | if ((rtw_is_cckratesonly_included(rate)) == true) | |
120 | return WIRELESS_11B; | |
121 | else if ((rtw_is_cckrates_included(rate)) == true) | |
122 | return WIRELESS_11BG; | |
123 | else | |
124 | return WIRELESS_11G; | |
125 | } | |
126 | } | |
127 | ||
3441d08c LC |
128 | u8 *rtw_set_fixed_ie(void *pbuf, unsigned int len, void *source, |
129 | unsigned int *frlen) | |
06a05884 | 130 | { |
3441d08c | 131 | memcpy(pbuf, source, len); |
06a05884 | 132 | *frlen = *frlen + len; |
3441d08c | 133 | return ((u8 *)pbuf) + len; |
06a05884 LF |
134 | } |
135 | ||
136 | /* rtw_set_ie will update frame length */ | |
137 | u8 *rtw_set_ie | |
138 | ( | |
139 | u8 *pbuf, | |
140 | int index, | |
141 | uint len, | |
142 | u8 *source, | |
143 | uint *frlen /* frame length */ | |
144 | ) | |
145 | { | |
06a05884 LF |
146 | *pbuf = (u8)index; |
147 | ||
148 | *(pbuf + 1) = (u8)len; | |
149 | ||
150 | if (len > 0) | |
151 | memcpy((void *)(pbuf + 2), (void *)source, len); | |
152 | ||
153 | *frlen = *frlen + (len + 2); | |
154 | ||
fbb57f6d | 155 | return pbuf + len + 2; |
06a05884 LF |
156 | } |
157 | ||
06a05884 LF |
158 | /*---------------------------------------------------------------------------- |
159 | index: the information element id index, limit is the limit for search | |
160 | -----------------------------------------------------------------------------*/ | |
161 | u8 *rtw_get_ie(u8 *pbuf, int index, int *len, int limit) | |
162 | { | |
163 | int tmp, i; | |
164 | u8 *p; | |
9cdddda5 | 165 | if (limit < 1) |
06a05884 | 166 | return NULL; |
06a05884 LF |
167 | |
168 | p = pbuf; | |
169 | i = 0; | |
170 | *len = 0; | |
171 | while (1) { | |
172 | if (*p == index) { | |
173 | *len = *(p + 1); | |
174 | return p; | |
175 | } else { | |
176 | tmp = *(p + 1); | |
177 | p += (tmp + 2); | |
178 | i += (tmp + 2); | |
179 | } | |
180 | if (i >= limit) | |
181 | break; | |
182 | } | |
06a05884 LF |
183 | return NULL; |
184 | } | |
185 | ||
06a05884 LF |
186 | void rtw_set_supported_rate(u8 *SupportedRates, uint mode) |
187 | { | |
06a05884 | 188 | |
1ce39848 | 189 | memset(SupportedRates, 0, NDIS_802_11_LENGTH_RATES_EX); |
06a05884 LF |
190 | |
191 | switch (mode) { | |
192 | case WIRELESS_11B: | |
193 | memcpy(SupportedRates, WIFI_CCKRATES, IEEE80211_CCK_RATE_LEN); | |
194 | break; | |
195 | case WIRELESS_11G: | |
196 | case WIRELESS_11A: | |
197 | case WIRELESS_11_5N: | |
198 | case WIRELESS_11A_5N:/* Todo: no basic rate for ofdm ? */ | |
199 | memcpy(SupportedRates, WIFI_OFDMRATES, IEEE80211_NUM_OFDM_RATESLEN); | |
200 | break; | |
201 | case WIRELESS_11BG: | |
202 | case WIRELESS_11G_24N: | |
203 | case WIRELESS_11_24N: | |
204 | case WIRELESS_11BG_24N: | |
205 | memcpy(SupportedRates, WIFI_CCKRATES, IEEE80211_CCK_RATE_LEN); | |
206 | memcpy(SupportedRates + IEEE80211_CCK_RATE_LEN, WIFI_OFDMRATES, IEEE80211_NUM_OFDM_RATESLEN); | |
207 | break; | |
208 | } | |
06a05884 LF |
209 | } |
210 | ||
211 | uint rtw_get_rateset_len(u8 *rateset) | |
212 | { | |
213 | uint i = 0; | |
06a05884 LF |
214 | while (1) { |
215 | if ((rateset[i]) == 0) | |
216 | break; | |
217 | if (i > 12) | |
218 | break; | |
219 | i++; | |
220 | } | |
06a05884 LF |
221 | return i; |
222 | } | |
223 | ||
224 | int rtw_generate_ie(struct registry_priv *pregistrypriv) | |
225 | { | |
226 | u8 wireless_mode; | |
227 | int sz = 0, rateLen; | |
228 | struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network; | |
229 | u8 *ie = pdev_network->IEs; | |
230 | ||
06a05884 LF |
231 | |
232 | /* timestamp will be inserted by hardware */ | |
233 | sz += 8; | |
234 | ie += sz; | |
235 | ||
236 | /* beacon interval : 2bytes */ | |
237 | *(__le16 *)ie = cpu_to_le16((u16)pdev_network->Configuration.BeaconPeriod);/* BCN_INTERVAL; */ | |
238 | sz += 2; | |
239 | ie += 2; | |
240 | ||
241 | /* capability info */ | |
242 | *(u16 *)ie = 0; | |
243 | ||
244 | *(__le16 *)ie |= cpu_to_le16(cap_IBSS); | |
245 | ||
246 | if (pregistrypriv->preamble == PREAMBLE_SHORT) | |
247 | *(__le16 *)ie |= cpu_to_le16(cap_ShortPremble); | |
248 | ||
249 | if (pdev_network->Privacy) | |
250 | *(__le16 *)ie |= cpu_to_le16(cap_Privacy); | |
251 | ||
252 | sz += 2; | |
253 | ie += 2; | |
254 | ||
255 | /* SSID */ | |
256 | ie = rtw_set_ie(ie, _SSID_IE_, pdev_network->Ssid.SsidLength, pdev_network->Ssid.Ssid, &sz); | |
257 | ||
258 | /* supported rates */ | |
259 | if (pregistrypriv->wireless_mode == WIRELESS_11ABGN) { | |
260 | if (pdev_network->Configuration.DSConfig > 14) | |
261 | wireless_mode = WIRELESS_11A_5N; | |
262 | else | |
263 | wireless_mode = WIRELESS_11BG_24N; | |
264 | } else { | |
265 | wireless_mode = pregistrypriv->wireless_mode; | |
266 | } | |
267 | ||
268 | rtw_set_supported_rate(pdev_network->SupportedRates, wireless_mode); | |
269 | ||
270 | rateLen = rtw_get_rateset_len(pdev_network->SupportedRates); | |
271 | ||
272 | if (rateLen > 8) { | |
273 | ie = rtw_set_ie(ie, _SUPPORTEDRATES_IE_, 8, pdev_network->SupportedRates, &sz); | |
274 | /* ie = rtw_set_ie(ie, _EXT_SUPPORTEDRATES_IE_, (rateLen - 8), (pdev_network->SupportedRates + 8), &sz); */ | |
275 | } else { | |
276 | ie = rtw_set_ie(ie, _SUPPORTEDRATES_IE_, rateLen, pdev_network->SupportedRates, &sz); | |
277 | } | |
278 | ||
279 | /* DS parameter set */ | |
280 | ie = rtw_set_ie(ie, _DSSET_IE_, 1, (u8 *)&(pdev_network->Configuration.DSConfig), &sz); | |
281 | ||
282 | /* IBSS Parameter Set */ | |
283 | ||
284 | ie = rtw_set_ie(ie, _IBSS_PARA_IE_, 2, (u8 *)&(pdev_network->Configuration.ATIMWindow), &sz); | |
285 | ||
286 | if (rateLen > 8) | |
287 | ie = rtw_set_ie(ie, _EXT_SUPPORTEDRATES_IE_, (rateLen - 8), (pdev_network->SupportedRates + 8), &sz); | |
06a05884 LF |
288 | |
289 | return sz; | |
290 | } | |
291 | ||
292 | unsigned char *rtw_get_wpa_ie(unsigned char *pie, int *wpa_ie_len, int limit) | |
293 | { | |
294 | int len; | |
295 | u16 val16; | |
296 | __le16 le_tmp; | |
297 | unsigned char wpa_oui_type[] = {0x00, 0x50, 0xf2, 0x01}; | |
298 | u8 *pbuf = pie; | |
299 | int limit_new = limit; | |
300 | ||
301 | while (1) { | |
302 | pbuf = rtw_get_ie(pbuf, _WPA_IE_ID_, &len, limit_new); | |
303 | ||
304 | if (pbuf) { | |
305 | /* check if oui matches... */ | |
f42f52aa | 306 | if (!memcmp((pbuf + 2), wpa_oui_type, sizeof(wpa_oui_type)) == false) |
06a05884 LF |
307 | goto check_next_ie; |
308 | ||
309 | /* check version... */ | |
310 | memcpy((u8 *)&le_tmp, (pbuf + 6), sizeof(val16)); | |
311 | ||
312 | val16 = le16_to_cpu(le_tmp); | |
313 | if (val16 != 0x0001) | |
314 | goto check_next_ie; | |
315 | *wpa_ie_len = *(pbuf + 1); | |
316 | return pbuf; | |
317 | } else { | |
318 | *wpa_ie_len = 0; | |
319 | return NULL; | |
320 | } | |
321 | ||
322 | check_next_ie: | |
323 | limit_new = limit - (pbuf - pie) - 2 - len; | |
324 | if (limit_new <= 0) | |
325 | break; | |
326 | pbuf += (2 + len); | |
327 | } | |
328 | *wpa_ie_len = 0; | |
329 | return NULL; | |
330 | } | |
331 | ||
332 | unsigned char *rtw_get_wpa2_ie(unsigned char *pie, int *rsn_ie_len, int limit) | |
333 | { | |
334 | ||
335 | return rtw_get_ie(pie, _WPA2_IE_ID_, rsn_ie_len, limit); | |
336 | } | |
337 | ||
338 | int rtw_get_wpa_cipher_suite(u8 *s) | |
339 | { | |
05c9bc1f | 340 | if (!memcmp(s, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN)) |
06a05884 | 341 | return WPA_CIPHER_NONE; |
05c9bc1f | 342 | if (!memcmp(s, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN)) |
06a05884 | 343 | return WPA_CIPHER_WEP40; |
05c9bc1f | 344 | if (!memcmp(s, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN)) |
06a05884 | 345 | return WPA_CIPHER_TKIP; |
05c9bc1f | 346 | if (!memcmp(s, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN)) |
06a05884 | 347 | return WPA_CIPHER_CCMP; |
05c9bc1f | 348 | if (!memcmp(s, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN)) |
06a05884 LF |
349 | return WPA_CIPHER_WEP104; |
350 | ||
351 | return 0; | |
352 | } | |
353 | ||
354 | int rtw_get_wpa2_cipher_suite(u8 *s) | |
355 | { | |
05c9bc1f | 356 | if (!memcmp(s, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN)) |
06a05884 | 357 | return WPA_CIPHER_NONE; |
05c9bc1f | 358 | if (!memcmp(s, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN)) |
06a05884 | 359 | return WPA_CIPHER_WEP40; |
05c9bc1f | 360 | if (!memcmp(s, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN)) |
06a05884 | 361 | return WPA_CIPHER_TKIP; |
05c9bc1f | 362 | if (!memcmp(s, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN)) |
06a05884 | 363 | return WPA_CIPHER_CCMP; |
05c9bc1f | 364 | if (!memcmp(s, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN)) |
06a05884 LF |
365 | return WPA_CIPHER_WEP104; |
366 | ||
367 | return 0; | |
368 | } | |
369 | ||
370 | ||
371 | int rtw_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, int *pairwise_cipher, int *is_8021x) | |
372 | { | |
373 | int i, ret = _SUCCESS; | |
374 | int left, count; | |
375 | u8 *pos; | |
376 | u8 SUITE_1X[4] = {0x00, 0x50, 0xf2, 1}; | |
377 | ||
378 | if (wpa_ie_len <= 0) { | |
379 | /* No WPA IE - fail silently */ | |
380 | return _FAIL; | |
381 | } | |
382 | ||
383 | ||
7a158635 KK |
384 | if ((*wpa_ie != _WPA_IE_ID_) || (*(wpa_ie + 1) != (u8)(wpa_ie_len - 2)) || |
385 | (memcmp(wpa_ie + 2, RTW_WPA_OUI_TYPE, WPA_SELECTOR_LEN))) | |
06a05884 LF |
386 | return _FAIL; |
387 | ||
388 | pos = wpa_ie; | |
389 | ||
390 | pos += 8; | |
391 | left = wpa_ie_len - 8; | |
392 | ||
393 | ||
394 | /* group_cipher */ | |
395 | if (left >= WPA_SELECTOR_LEN) { | |
396 | *group_cipher = rtw_get_wpa_cipher_suite(pos); | |
397 | pos += WPA_SELECTOR_LEN; | |
398 | left -= WPA_SELECTOR_LEN; | |
399 | } else if (left > 0) { | |
400 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s: ie length mismatch, %u too much", __func__, left)); | |
401 | return _FAIL; | |
402 | } | |
403 | ||
404 | /* pairwise_cipher */ | |
405 | if (left >= 2) { | |
4b49a5b3 | 406 | count = get_unaligned_le16(pos); |
06a05884 LF |
407 | pos += 2; |
408 | left -= 2; | |
409 | ||
410 | if (count == 0 || left < count * WPA_SELECTOR_LEN) { | |
411 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s: ie count botch (pairwise), " | |
412 | "count %u left %u", __func__, count, left)); | |
413 | return _FAIL; | |
414 | } | |
415 | ||
416 | for (i = 0; i < count; i++) { | |
417 | *pairwise_cipher |= rtw_get_wpa_cipher_suite(pos); | |
418 | ||
419 | pos += WPA_SELECTOR_LEN; | |
420 | left -= WPA_SELECTOR_LEN; | |
421 | } | |
422 | } else if (left == 1) { | |
423 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s: ie too short (for key mgmt)", __func__)); | |
424 | return _FAIL; | |
425 | } | |
426 | ||
427 | if (is_8021x) { | |
428 | if (left >= 6) { | |
429 | pos += 2; | |
05c9bc1f | 430 | if (!memcmp(pos, SUITE_1X, 4)) { |
06a05884 LF |
431 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("%s : there has 802.1x auth\n", __func__)); |
432 | *is_8021x = 1; | |
433 | } | |
434 | } | |
435 | } | |
436 | ||
437 | return ret; | |
438 | } | |
439 | ||
440 | int rtw_parse_wpa2_ie(u8 *rsn_ie, int rsn_ie_len, int *group_cipher, int *pairwise_cipher, int *is_8021x) | |
441 | { | |
442 | int i, ret = _SUCCESS; | |
443 | int left, count; | |
444 | u8 *pos; | |
445 | u8 SUITE_1X[4] = {0x00, 0x0f, 0xac, 0x01}; | |
446 | ||
447 | if (rsn_ie_len <= 0) { | |
448 | /* No RSN IE - fail silently */ | |
449 | return _FAIL; | |
450 | } | |
451 | ||
452 | ||
7a158635 | 453 | if ((*rsn_ie != _WPA2_IE_ID_) || (*(rsn_ie + 1) != (u8)(rsn_ie_len - 2))) |
06a05884 LF |
454 | return _FAIL; |
455 | ||
456 | pos = rsn_ie; | |
457 | pos += 4; | |
458 | left = rsn_ie_len - 4; | |
459 | ||
460 | /* group_cipher */ | |
461 | if (left >= RSN_SELECTOR_LEN) { | |
462 | *group_cipher = rtw_get_wpa2_cipher_suite(pos); | |
463 | ||
464 | pos += RSN_SELECTOR_LEN; | |
465 | left -= RSN_SELECTOR_LEN; | |
466 | ||
467 | } else if (left > 0) { | |
468 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s: ie length mismatch, %u too much", __func__, left)); | |
469 | return _FAIL; | |
470 | } | |
471 | ||
472 | /* pairwise_cipher */ | |
473 | if (left >= 2) { | |
4b49a5b3 | 474 | count = get_unaligned_le16(pos); |
06a05884 LF |
475 | pos += 2; |
476 | left -= 2; | |
477 | ||
478 | if (count == 0 || left < count * RSN_SELECTOR_LEN) { | |
479 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s: ie count botch (pairwise), " | |
480 | "count %u left %u", __func__, count, left)); | |
481 | return _FAIL; | |
482 | } | |
483 | ||
484 | for (i = 0; i < count; i++) { | |
485 | *pairwise_cipher |= rtw_get_wpa2_cipher_suite(pos); | |
486 | ||
487 | pos += RSN_SELECTOR_LEN; | |
488 | left -= RSN_SELECTOR_LEN; | |
489 | } | |
490 | ||
491 | } else if (left == 1) { | |
492 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s: ie too short (for key mgmt)", __func__)); | |
493 | ||
494 | return _FAIL; | |
495 | } | |
496 | ||
497 | if (is_8021x) { | |
498 | if (left >= 6) { | |
499 | pos += 2; | |
05c9bc1f | 500 | if (!memcmp(pos, SUITE_1X, 4)) { |
06a05884 LF |
501 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("%s (): there has 802.1x auth\n", __func__)); |
502 | *is_8021x = 1; | |
503 | } | |
504 | } | |
505 | } | |
506 | return ret; | |
507 | } | |
508 | ||
509 | int rtw_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, u8 *wpa_ie, u16 *wpa_len) | |
510 | { | |
511 | u8 authmode, sec_idx, i; | |
512 | u8 wpa_oui[4] = {0x0, 0x50, 0xf2, 0x01}; | |
513 | uint cnt; | |
514 | ||
06a05884 LF |
515 | |
516 | /* Search required WPA or WPA2 IE and copy to sec_ie[] */ | |
517 | ||
adb3d770 | 518 | cnt = _TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_; |
06a05884 LF |
519 | |
520 | sec_idx = 0; | |
521 | ||
522 | while (cnt < in_len) { | |
523 | authmode = in_ie[cnt]; | |
524 | ||
7a158635 | 525 | if ((authmode == _WPA_IE_ID_) && (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) { |
06a05884 LF |
526 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, |
527 | ("\n rtw_get_wpa_ie: sec_idx =%d in_ie[cnt+1]+2 =%d\n", | |
7a158635 | 528 | sec_idx, in_ie[cnt + 1] + 2)); |
06a05884 LF |
529 | |
530 | if (wpa_ie) { | |
7a158635 | 531 | memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); |
06a05884 | 532 | |
7a158635 | 533 | for (i = 0; i < (in_ie[cnt + 1] + 2); i += 8) { |
06a05884 LF |
534 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, |
535 | ("\n %2x,%2x,%2x,%2x,%2x,%2x,%2x,%2x\n", | |
7a158635 KK |
536 | wpa_ie[i], wpa_ie[i + 1], wpa_ie[i + 2], wpa_ie[i + 3], wpa_ie[i + 4], |
537 | wpa_ie[i + 5], wpa_ie[i + 6], wpa_ie[i + 7])); | |
06a05884 LF |
538 | } |
539 | } | |
540 | ||
7a158635 KK |
541 | *wpa_len = in_ie[cnt + 1] + 2; |
542 | cnt += in_ie[cnt + 1] + 2; /* get next */ | |
06a05884 LF |
543 | } else { |
544 | if (authmode == _WPA2_IE_ID_) { | |
545 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, | |
546 | ("\n get_rsn_ie: sec_idx =%d in_ie[cnt+1]+2 =%d\n", | |
7a158635 | 547 | sec_idx, in_ie[cnt + 1] + 2)); |
06a05884 LF |
548 | |
549 | if (rsn_ie) { | |
7a158635 | 550 | memcpy(rsn_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); |
06a05884 | 551 | |
7a158635 | 552 | for (i = 0; i < (in_ie[cnt + 1] + 2); i += 8) { |
06a05884 LF |
553 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, |
554 | ("\n %2x,%2x,%2x,%2x,%2x,%2x,%2x,%2x\n", | |
7a158635 KK |
555 | rsn_ie[i], rsn_ie[i + 1], rsn_ie[i + 2], rsn_ie[i + 3], rsn_ie[i + 4], |
556 | rsn_ie[i + 5], rsn_ie[i + 6], rsn_ie[i + 7])); | |
06a05884 LF |
557 | } |
558 | } | |
559 | ||
7a158635 KK |
560 | *rsn_len = in_ie[cnt + 1] + 2; |
561 | cnt += in_ie[cnt + 1] + 2; /* get next */ | |
06a05884 | 562 | } else { |
7a158635 | 563 | cnt += in_ie[cnt + 1] + 2; /* get next */ |
06a05884 LF |
564 | } |
565 | } | |
566 | } | |
567 | ||
06a05884 LF |
568 | |
569 | return *rsn_len + *wpa_len; | |
570 | } | |
571 | ||
572 | u8 rtw_is_wps_ie(u8 *ie_ptr, uint *wps_ielen) | |
573 | { | |
574 | u8 match = false; | |
575 | u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; | |
576 | ||
577 | if (ie_ptr == NULL) | |
578 | return match; | |
579 | ||
580 | eid = ie_ptr[0]; | |
581 | ||
f42f52aa | 582 | if ((eid == _WPA_IE_ID_) && (!memcmp(&ie_ptr[2], wps_oui, 4))) { |
7a158635 | 583 | *wps_ielen = ie_ptr[1] + 2; |
06a05884 LF |
584 | match = true; |
585 | } | |
586 | return match; | |
587 | } | |
588 | ||
589 | /** | |
590 | * rtw_get_wps_ie - Search WPS IE from a series of IEs | |
591 | * @in_ie: Address of IEs to search | |
592 | * @in_len: Length limit from in_ie | |
593 | * @wps_ie: If not NULL and WPS IE is found, WPS IE will be copied to the buf starting from wps_ie | |
594 | * @wps_ielen: If not NULL and WPS IE is found, will set to the length of the entire WPS IE | |
595 | * | |
596 | * Returns: The address of the WPS IE found, or NULL | |
597 | */ | |
598 | u8 *rtw_get_wps_ie(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen) | |
599 | { | |
600 | uint cnt; | |
601 | u8 *wpsie_ptr = NULL; | |
602 | u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; | |
603 | ||
604 | if (wps_ielen) | |
605 | *wps_ielen = 0; | |
606 | ||
607 | if (!in_ie || in_len <= 0) | |
608 | return wpsie_ptr; | |
609 | ||
610 | cnt = 0; | |
611 | ||
612 | while (cnt < in_len) { | |
613 | eid = in_ie[cnt]; | |
614 | ||
7a158635 | 615 | if ((eid == _WPA_IE_ID_) && (!memcmp(&in_ie[cnt + 2], wps_oui, 4))) { |
06a05884 LF |
616 | wpsie_ptr = &in_ie[cnt]; |
617 | ||
618 | if (wps_ie) | |
7a158635 | 619 | memcpy(wps_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); |
06a05884 LF |
620 | |
621 | if (wps_ielen) | |
7a158635 | 622 | *wps_ielen = in_ie[cnt + 1] + 2; |
06a05884 | 623 | |
7a158635 | 624 | cnt += in_ie[cnt + 1] + 2; |
06a05884 LF |
625 | |
626 | break; | |
627 | } else { | |
7a158635 | 628 | cnt += in_ie[cnt + 1] + 2; /* goto next */ |
06a05884 LF |
629 | } |
630 | } | |
631 | return wpsie_ptr; | |
632 | } | |
633 | ||
634 | /** | |
635 | * rtw_get_wps_attr - Search a specific WPS attribute from a given WPS IE | |
636 | * @wps_ie: Address of WPS IE to search | |
637 | * @wps_ielen: Length limit from wps_ie | |
638 | * @target_attr_id: The attribute ID of WPS attribute to search | |
639 | * @buf_attr: If not NULL and the WPS attribute is found, WPS attribute will be copied to the buf starting from buf_attr | |
640 | * @len_attr: If not NULL and the WPS attribute is found, will set to the length of the entire WPS attribute | |
641 | * | |
642 | * Returns: the address of the specific WPS attribute found, or NULL | |
643 | */ | |
4c0dbe0a | 644 | u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_attr, u32 *len_attr) |
06a05884 LF |
645 | { |
646 | u8 *attr_ptr = NULL; | |
647 | u8 *target_attr_ptr = NULL; | |
648 | u8 wps_oui[4] = {0x00, 0x50, 0xF2, 0x04}; | |
649 | ||
650 | if (len_attr) | |
651 | *len_attr = 0; | |
652 | ||
653 | if ((wps_ie[0] != _VENDOR_SPECIFIC_IE_) || | |
4c0dbe0a | 654 | (memcmp(wps_ie + 2, wps_oui, 4))) |
06a05884 LF |
655 | return attr_ptr; |
656 | ||
657 | /* 6 = 1(Element ID) + 1(Length) + 4(WPS OUI) */ | |
658 | attr_ptr = wps_ie + 6; /* goto first attr */ | |
659 | ||
660 | while (attr_ptr - wps_ie < wps_ielen) { | |
661 | /* 4 = 2(Attribute ID) + 2(Length) */ | |
27c8aac7 | 662 | u16 attr_id = get_unaligned_be16(attr_ptr); |
663 | u16 attr_data_len = get_unaligned_be16(attr_ptr + 2); | |
06a05884 LF |
664 | u16 attr_len = attr_data_len + 4; |
665 | ||
666 | if (attr_id == target_attr_id) { | |
667 | target_attr_ptr = attr_ptr; | |
668 | if (buf_attr) | |
669 | memcpy(buf_attr, attr_ptr, attr_len); | |
670 | if (len_attr) | |
671 | *len_attr = attr_len; | |
672 | break; | |
673 | } else { | |
674 | attr_ptr += attr_len; /* goto next */ | |
675 | } | |
676 | } | |
677 | return target_attr_ptr; | |
678 | } | |
679 | ||
680 | /** | |
681 | * rtw_get_wps_attr_content - Search a specific WPS attribute content from a given WPS IE | |
682 | * @wps_ie: Address of WPS IE to search | |
683 | * @wps_ielen: Length limit from wps_ie | |
684 | * @target_attr_id: The attribute ID of WPS attribute to search | |
685 | * @buf_content: If not NULL and the WPS attribute is found, WPS attribute content will be copied to the buf starting from buf_content | |
686 | * @len_content: If not NULL and the WPS attribute is found, will set to the length of the WPS attribute content | |
687 | * | |
688 | * Returns: the address of the specific WPS attribute content found, or NULL | |
689 | */ | |
4c0dbe0a | 690 | u8 *rtw_get_wps_attr_content(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_content, uint *len_content) |
06a05884 LF |
691 | { |
692 | u8 *attr_ptr; | |
693 | u32 attr_len; | |
694 | ||
695 | if (len_content) | |
696 | *len_content = 0; | |
697 | ||
698 | attr_ptr = rtw_get_wps_attr(wps_ie, wps_ielen, target_attr_id, NULL, &attr_len); | |
699 | ||
700 | if (attr_ptr && attr_len) { | |
701 | if (buf_content) | |
7a158635 | 702 | memcpy(buf_content, attr_ptr + 4, attr_len - 4); |
06a05884 LF |
703 | |
704 | if (len_content) | |
7a158635 | 705 | *len_content = attr_len - 4; |
06a05884 | 706 | |
7a158635 | 707 | return attr_ptr + 4; |
06a05884 LF |
708 | } |
709 | ||
710 | return NULL; | |
711 | } | |
712 | ||
713 | static int rtw_ieee802_11_parse_vendor_specific(u8 *pos, uint elen, | |
714 | struct rtw_ieee802_11_elems *elems, | |
715 | int show_errors) | |
716 | { | |
717 | unsigned int oui; | |
718 | ||
719 | /* first 3 bytes in vendor specific information element are the IEEE | |
720 | * OUI of the vendor. The following byte is used a vendor specific | |
721 | * sub-type. */ | |
722 | if (elen < 4) { | |
723 | if (show_errors) { | |
724 | DBG_88E("short vendor specific information element ignored (len=%lu)\n", | |
7be921a2 | 725 | (unsigned long)elen); |
06a05884 LF |
726 | } |
727 | return -1; | |
728 | } | |
729 | ||
730 | oui = RTW_GET_BE24(pos); | |
731 | switch (oui) { | |
732 | case OUI_MICROSOFT: | |
733 | /* Microsoft/Wi-Fi information elements are further typed and | |
734 | * subtyped */ | |
735 | switch (pos[3]) { | |
736 | case 1: | |
737 | /* Microsoft OUI (00:50:F2) with OUI Type 1: | |
738 | * real WPA information element */ | |
739 | elems->wpa_ie = pos; | |
740 | elems->wpa_ie_len = elen; | |
741 | break; | |
742 | case WME_OUI_TYPE: /* this is a Wi-Fi WME info. element */ | |
743 | if (elen < 5) { | |
744 | DBG_88E("short WME information element ignored (len=%lu)\n", | |
7be921a2 | 745 | (unsigned long)elen); |
06a05884 LF |
746 | return -1; |
747 | } | |
748 | switch (pos[4]) { | |
749 | case WME_OUI_SUBTYPE_INFORMATION_ELEMENT: | |
750 | case WME_OUI_SUBTYPE_PARAMETER_ELEMENT: | |
751 | elems->wme = pos; | |
752 | elems->wme_len = elen; | |
753 | break; | |
754 | case WME_OUI_SUBTYPE_TSPEC_ELEMENT: | |
755 | elems->wme_tspec = pos; | |
756 | elems->wme_tspec_len = elen; | |
757 | break; | |
758 | default: | |
759 | DBG_88E("unknown WME information element ignored (subtype=%d len=%lu)\n", | |
7be921a2 | 760 | pos[4], (unsigned long)elen); |
06a05884 LF |
761 | return -1; |
762 | } | |
763 | break; | |
764 | case 4: | |
765 | /* Wi-Fi Protected Setup (WPS) IE */ | |
766 | elems->wps_ie = pos; | |
767 | elems->wps_ie_len = elen; | |
768 | break; | |
769 | default: | |
770 | DBG_88E("Unknown Microsoft information element ignored (type=%d len=%lu)\n", | |
7be921a2 | 771 | pos[3], (unsigned long)elen); |
06a05884 LF |
772 | return -1; |
773 | } | |
774 | break; | |
775 | ||
776 | case OUI_BROADCOM: | |
777 | switch (pos[3]) { | |
778 | case VENDOR_HT_CAPAB_OUI_TYPE: | |
779 | elems->vendor_ht_cap = pos; | |
780 | elems->vendor_ht_cap_len = elen; | |
781 | break; | |
782 | default: | |
783 | DBG_88E("Unknown Broadcom information element ignored (type=%d len=%lu)\n", | |
7be921a2 | 784 | pos[3], (unsigned long)elen); |
06a05884 LF |
785 | return -1; |
786 | } | |
787 | break; | |
788 | default: | |
a67080ee AS |
789 | DBG_88E("unknown vendor specific information element ignored (vendor OUI %3phC len=%lu)\n", |
790 | pos, (unsigned long)elen); | |
06a05884 LF |
791 | return -1; |
792 | } | |
793 | return 0; | |
794 | } | |
795 | ||
796 | /** | |
797 | * ieee802_11_parse_elems - Parse information elements in management frames | |
798 | * @start: Pointer to the start of IEs | |
799 | * @len: Length of IE buffer in octets | |
800 | * @elems: Data structure for parsed elements | |
801 | * @show_errors: Whether to show parsing errors in debug log | |
802 | * Returns: Parsing result | |
803 | */ | |
804 | enum parse_res rtw_ieee802_11_parse_elems(u8 *start, uint len, | |
805 | struct rtw_ieee802_11_elems *elems, | |
806 | int show_errors) | |
807 | { | |
808 | uint left = len; | |
809 | u8 *pos = start; | |
810 | int unknown = 0; | |
811 | ||
1ce39848 | 812 | memset(elems, 0, sizeof(*elems)); |
06a05884 LF |
813 | |
814 | while (left >= 2) { | |
815 | u8 id, elen; | |
816 | ||
817 | id = *pos++; | |
818 | elen = *pos++; | |
819 | left -= 2; | |
820 | ||
821 | if (elen > left) { | |
822 | if (show_errors) { | |
823 | DBG_88E("IEEE 802.11 element parse failed (id=%d elen=%d left=%lu)\n", | |
7be921a2 | 824 | id, elen, (unsigned long)left); |
06a05884 LF |
825 | } |
826 | return ParseFailed; | |
827 | } | |
828 | ||
829 | switch (id) { | |
830 | case WLAN_EID_SSID: | |
831 | elems->ssid = pos; | |
832 | elems->ssid_len = elen; | |
833 | break; | |
834 | case WLAN_EID_SUPP_RATES: | |
835 | elems->supp_rates = pos; | |
836 | elems->supp_rates_len = elen; | |
837 | break; | |
838 | case WLAN_EID_FH_PARAMS: | |
839 | elems->fh_params = pos; | |
840 | elems->fh_params_len = elen; | |
841 | break; | |
842 | case WLAN_EID_DS_PARAMS: | |
843 | elems->ds_params = pos; | |
844 | elems->ds_params_len = elen; | |
845 | break; | |
846 | case WLAN_EID_CF_PARAMS: | |
847 | elems->cf_params = pos; | |
848 | elems->cf_params_len = elen; | |
849 | break; | |
850 | case WLAN_EID_TIM: | |
851 | elems->tim = pos; | |
852 | elems->tim_len = elen; | |
853 | break; | |
854 | case WLAN_EID_IBSS_PARAMS: | |
855 | elems->ibss_params = pos; | |
856 | elems->ibss_params_len = elen; | |
857 | break; | |
858 | case WLAN_EID_CHALLENGE: | |
859 | elems->challenge = pos; | |
860 | elems->challenge_len = elen; | |
861 | break; | |
862 | case WLAN_EID_ERP_INFO: | |
863 | elems->erp_info = pos; | |
864 | elems->erp_info_len = elen; | |
865 | break; | |
866 | case WLAN_EID_EXT_SUPP_RATES: | |
867 | elems->ext_supp_rates = pos; | |
868 | elems->ext_supp_rates_len = elen; | |
869 | break; | |
870 | case WLAN_EID_VENDOR_SPECIFIC: | |
871 | if (rtw_ieee802_11_parse_vendor_specific(pos, elen, elems, show_errors)) | |
872 | unknown++; | |
873 | break; | |
874 | case WLAN_EID_RSN: | |
875 | elems->rsn_ie = pos; | |
876 | elems->rsn_ie_len = elen; | |
877 | break; | |
878 | case WLAN_EID_PWR_CAPABILITY: | |
879 | elems->power_cap = pos; | |
880 | elems->power_cap_len = elen; | |
881 | break; | |
882 | case WLAN_EID_SUPPORTED_CHANNELS: | |
883 | elems->supp_channels = pos; | |
884 | elems->supp_channels_len = elen; | |
885 | break; | |
886 | case WLAN_EID_MOBILITY_DOMAIN: | |
887 | elems->mdie = pos; | |
888 | elems->mdie_len = elen; | |
889 | break; | |
890 | case WLAN_EID_FAST_BSS_TRANSITION: | |
891 | elems->ftie = pos; | |
892 | elems->ftie_len = elen; | |
893 | break; | |
894 | case WLAN_EID_TIMEOUT_INTERVAL: | |
895 | elems->timeout_int = pos; | |
896 | elems->timeout_int_len = elen; | |
897 | break; | |
35cf0b55 | 898 | case WLAN_EID_HT_CAPABILITY: |
06a05884 LF |
899 | elems->ht_capabilities = pos; |
900 | elems->ht_capabilities_len = elen; | |
901 | break; | |
902 | case WLAN_EID_HT_OPERATION: | |
903 | elems->ht_operation = pos; | |
904 | elems->ht_operation_len = elen; | |
905 | break; | |
906 | default: | |
907 | unknown++; | |
908 | if (!show_errors) | |
909 | break; | |
910 | DBG_88E("IEEE 802.11 element parse ignored unknown element (id=%d elen=%d)\n", | |
911 | id, elen); | |
912 | break; | |
913 | } | |
914 | left -= elen; | |
915 | pos += elen; | |
916 | } | |
917 | if (left) | |
918 | return ParseFailed; | |
919 | return unknown ? ParseUnknown : ParseOK; | |
920 | } | |
921 | ||
06a05884 LF |
922 | void rtw_macaddr_cfg(u8 *mac_addr) |
923 | { | |
924 | u8 mac[ETH_ALEN]; | |
79374daf | 925 | |
06a05884 LF |
926 | if (mac_addr == NULL) |
927 | return; | |
928 | ||
79374daf AS |
929 | if (rtw_initmac && mac_pton(rtw_initmac, mac)) { |
930 | /* Users specify the mac address */ | |
06a05884 | 931 | memcpy(mac_addr, mac, ETH_ALEN); |
79374daf AS |
932 | } else { |
933 | /* Use the mac address stored in the Efuse */ | |
06a05884 LF |
934 | memcpy(mac, mac_addr, ETH_ALEN); |
935 | } | |
936 | ||
937 | if (((mac[0] == 0xff) && (mac[1] == 0xff) && (mac[2] == 0xff) && | |
938 | (mac[3] == 0xff) && (mac[4] == 0xff) && (mac[5] == 0xff)) || | |
939 | ((mac[0] == 0x0) && (mac[1] == 0x0) && (mac[2] == 0x0) && | |
940 | (mac[3] == 0x0) && (mac[4] == 0x0) && (mac[5] == 0x0))) { | |
941 | mac[0] = 0x00; | |
942 | mac[1] = 0xe0; | |
943 | mac[2] = 0x4c; | |
944 | mac[3] = 0x87; | |
945 | mac[4] = 0x00; | |
946 | mac[5] = 0x00; | |
08c64f93 | 947 | /* use default mac address */ |
06a05884 LF |
948 | memcpy(mac_addr, mac, ETH_ALEN); |
949 | DBG_88E("MAC Address from efuse error, assign default one !!!\n"); | |
950 | } | |
951 | ||
952 | DBG_88E("rtw_macaddr_cfg MAC Address = %pM\n", (mac_addr)); | |
953 | } | |
954 | ||
06a05884 LF |
955 | /* Baron adds to avoid FreeBSD warning */ |
956 | int ieee80211_is_empty_essid(const char *essid, int essid_len) | |
957 | { | |
958 | /* Single white space is for Linksys APs */ | |
959 | if (essid_len == 1 && essid[0] == ' ') | |
960 | return 1; | |
961 | ||
962 | /* Otherwise, if the entire essid is 0, we assume it is hidden */ | |
963 | while (essid_len) { | |
964 | essid_len--; | |
965 | if (essid[essid_len] != '\0') | |
966 | return 0; | |
967 | } | |
968 | ||
969 | return 1; | |
970 | } | |
971 | ||
972 | int ieee80211_get_hdrlen(u16 fc) | |
973 | { | |
974 | int hdrlen = 24; | |
975 | ||
976 | switch (WLAN_FC_GET_TYPE(fc)) { | |
977 | case RTW_IEEE80211_FTYPE_DATA: | |
978 | if (fc & RTW_IEEE80211_STYPE_QOS_DATA) | |
979 | hdrlen += 2; | |
980 | if ((fc & RTW_IEEE80211_FCTL_FROMDS) && (fc & RTW_IEEE80211_FCTL_TODS)) | |
981 | hdrlen += 6; /* Addr4 */ | |
982 | break; | |
983 | case RTW_IEEE80211_FTYPE_CTL: | |
984 | switch (WLAN_FC_GET_STYPE(fc)) { | |
985 | case RTW_IEEE80211_STYPE_CTS: | |
986 | case RTW_IEEE80211_STYPE_ACK: | |
987 | hdrlen = 10; | |
988 | break; | |
989 | default: | |
990 | hdrlen = 16; | |
991 | break; | |
992 | } | |
993 | break; | |
994 | } | |
995 | ||
996 | return hdrlen; | |
997 | } | |
998 | ||
999 | static int rtw_get_cipher_info(struct wlan_network *pnetwork) | |
1000 | { | |
1001 | u32 wpa_ielen; | |
1002 | unsigned char *pbuf; | |
1003 | int group_cipher = 0, pairwise_cipher = 0, is8021x = 0; | |
1004 | int ret = _FAIL; | |
7a158635 | 1005 | pbuf = rtw_get_wpa_ie(&pnetwork->network.IEs[12], &wpa_ielen, pnetwork->network.IELength - 12); |
06a05884 LF |
1006 | |
1007 | if (pbuf && (wpa_ielen > 0)) { | |
1008 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_cipher_info: wpa_ielen: %d", wpa_ielen)); | |
7a158635 | 1009 | if (_SUCCESS == rtw_parse_wpa_ie(pbuf, wpa_ielen + 2, &group_cipher, &pairwise_cipher, &is8021x)) { |
06a05884 LF |
1010 | pnetwork->BcnInfo.pairwise_cipher = pairwise_cipher; |
1011 | pnetwork->BcnInfo.group_cipher = group_cipher; | |
1012 | pnetwork->BcnInfo.is_8021x = is8021x; | |
1013 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("%s: pnetwork->pairwise_cipher: %d, is_8021x is %d", | |
1014 | __func__, pnetwork->BcnInfo.pairwise_cipher, pnetwork->BcnInfo.is_8021x)); | |
1015 | ret = _SUCCESS; | |
1016 | } | |
1017 | } else { | |
7a158635 | 1018 | pbuf = rtw_get_wpa2_ie(&pnetwork->network.IEs[12], &wpa_ielen, pnetwork->network.IELength - 12); |
06a05884 LF |
1019 | |
1020 | if (pbuf && (wpa_ielen > 0)) { | |
1021 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("get RSN IE\n")); | |
7a158635 | 1022 | if (_SUCCESS == rtw_parse_wpa2_ie(pbuf, wpa_ielen + 2, &group_cipher, &pairwise_cipher, &is8021x)) { |
06a05884 LF |
1023 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("get RSN IE OK!!!\n")); |
1024 | pnetwork->BcnInfo.pairwise_cipher = pairwise_cipher; | |
1025 | pnetwork->BcnInfo.group_cipher = group_cipher; | |
1026 | pnetwork->BcnInfo.is_8021x = is8021x; | |
1027 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("%s: pnetwork->pairwise_cipher: %d," | |
1028 | "pnetwork->group_cipher is %d, is_8021x is %d", __func__, pnetwork->BcnInfo.pairwise_cipher, | |
1029 | pnetwork->BcnInfo.group_cipher, pnetwork->BcnInfo.is_8021x)); | |
1030 | ret = _SUCCESS; | |
1031 | } | |
1032 | } | |
1033 | } | |
1034 | ||
1035 | return ret; | |
1036 | } | |
1037 | ||
1038 | void rtw_get_bcn_info(struct wlan_network *pnetwork) | |
1039 | { | |
1040 | unsigned short cap = 0; | |
1041 | u8 bencrypt = 0; | |
1042 | __le16 le_tmp; | |
1043 | u16 wpa_len = 0, rsn_len = 0; | |
1044 | struct HT_info_element *pht_info = NULL; | |
06a05884 LF |
1045 | unsigned int len; |
1046 | unsigned char *p; | |
1047 | ||
1048 | memcpy(&le_tmp, rtw_get_capability_from_ie(pnetwork->network.IEs), 2); | |
1049 | cap = le16_to_cpu(le_tmp); | |
1050 | if (cap & WLAN_CAPABILITY_PRIVACY) { | |
1051 | bencrypt = 1; | |
1052 | pnetwork->network.Privacy = 1; | |
1053 | } else { | |
1054 | pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_OPENSYS; | |
1055 | } | |
4c0dbe0a | 1056 | rtw_get_sec_ie(pnetwork->network.IEs, pnetwork->network.IELength, NULL, &rsn_len, NULL, &wpa_len); |
06a05884 LF |
1057 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: ssid =%s\n", pnetwork->network.Ssid.Ssid)); |
1058 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: wpa_len =%d rsn_len =%d\n", wpa_len, rsn_len)); | |
1059 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: ssid =%s\n", pnetwork->network.Ssid.Ssid)); | |
1060 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: wpa_len =%d rsn_len =%d\n", wpa_len, rsn_len)); | |
1061 | ||
1062 | if (rsn_len > 0) { | |
1063 | pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WPA2; | |
1064 | } else if (wpa_len > 0) { | |
1065 | pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WPA; | |
1066 | } else { | |
1067 | if (bencrypt) | |
1068 | pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WEP; | |
1069 | } | |
1070 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: pnetwork->encryp_protocol is %x\n", | |
1071 | pnetwork->BcnInfo.encryp_protocol)); | |
1072 | RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: pnetwork->encryp_protocol is %x\n", | |
1073 | pnetwork->BcnInfo.encryp_protocol)); | |
1074 | rtw_get_cipher_info(pnetwork); | |
1075 | ||
1076 | /* get bwmode and ch_offset */ | |
1077 | /* parsing HT_CAP_IE */ | |
1078 | p = rtw_get_ie(pnetwork->network.IEs + _FIXED_IE_LENGTH_, _HT_CAPABILITY_IE_, &len, pnetwork->network.IELength - _FIXED_IE_LENGTH_); | |
1079 | if (p && len > 0) { | |
c7873d8b IS |
1080 | struct ieee80211_ht_cap *ht_cap = |
1081 | (struct ieee80211_ht_cap *)(p + 2); | |
1082 | ||
1083 | pnetwork->BcnInfo.ht_cap_info = le16_to_cpu(ht_cap->cap_info); | |
06a05884 | 1084 | } else { |
c7873d8b | 1085 | pnetwork->BcnInfo.ht_cap_info = 0; |
06a05884 LF |
1086 | } |
1087 | /* parsing HT_INFO_IE */ | |
1088 | p = rtw_get_ie(pnetwork->network.IEs + _FIXED_IE_LENGTH_, _HT_ADD_INFO_IE_, &len, pnetwork->network.IELength - _FIXED_IE_LENGTH_); | |
1089 | if (p && len > 0) { | |
1090 | pht_info = (struct HT_info_element *)(p + 2); | |
1091 | pnetwork->BcnInfo.ht_info_infos_0 = pht_info->infos[0]; | |
1092 | } else { | |
1093 | pnetwork->BcnInfo.ht_info_infos_0 = 0; | |
1094 | } | |
1095 | } | |
1096 | ||
1097 | /* show MCS rate, unit: 100Kbps */ | |
1098 | u16 rtw_mcs_rate(u8 rf_type, u8 bw_40MHz, u8 short_GI_20, u8 short_GI_40, unsigned char *MCS_rate) | |
1099 | { | |
1100 | u16 max_rate = 0; | |
1101 | ||
1102 | if (rf_type == RF_1T1R) { | |
1103 | if (MCS_rate[0] & BIT(7)) | |
1104 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 1500 : 1350) : ((short_GI_20) ? 722 : 650); | |
1105 | else if (MCS_rate[0] & BIT(6)) | |
1106 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 1350 : 1215) : ((short_GI_20) ? 650 : 585); | |
1107 | else if (MCS_rate[0] & BIT(5)) | |
1108 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 1200 : 1080) : ((short_GI_20) ? 578 : 520); | |
1109 | else if (MCS_rate[0] & BIT(4)) | |
1110 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 900 : 810) : ((short_GI_20) ? 433 : 390); | |
1111 | else if (MCS_rate[0] & BIT(3)) | |
1112 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 600 : 540) : ((short_GI_20) ? 289 : 260); | |
1113 | else if (MCS_rate[0] & BIT(2)) | |
1114 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 450 : 405) : ((short_GI_20) ? 217 : 195); | |
1115 | else if (MCS_rate[0] & BIT(1)) | |
1116 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 300 : 270) : ((short_GI_20) ? 144 : 130); | |
1117 | else if (MCS_rate[0] & BIT(0)) | |
1118 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 150 : 135) : ((short_GI_20) ? 72 : 65); | |
1119 | } else { | |
1120 | if (MCS_rate[1]) { | |
1121 | if (MCS_rate[1] & BIT(7)) | |
1122 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 3000 : 2700) : ((short_GI_20) ? 1444 : 1300); | |
1123 | else if (MCS_rate[1] & BIT(6)) | |
1124 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 2700 : 2430) : ((short_GI_20) ? 1300 : 1170); | |
1125 | else if (MCS_rate[1] & BIT(5)) | |
1126 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 2400 : 2160) : ((short_GI_20) ? 1156 : 1040); | |
1127 | else if (MCS_rate[1] & BIT(4)) | |
1128 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 1800 : 1620) : ((short_GI_20) ? 867 : 780); | |
1129 | else if (MCS_rate[1] & BIT(3)) | |
1130 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 1200 : 1080) : ((short_GI_20) ? 578 : 520); | |
1131 | else if (MCS_rate[1] & BIT(2)) | |
1132 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 900 : 810) : ((short_GI_20) ? 433 : 390); | |
1133 | else if (MCS_rate[1] & BIT(1)) | |
1134 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 600 : 540) : ((short_GI_20) ? 289 : 260); | |
1135 | else if (MCS_rate[1] & BIT(0)) | |
1136 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 300 : 270) : ((short_GI_20) ? 144 : 130); | |
1137 | } else { | |
1138 | if (MCS_rate[0] & BIT(7)) | |
1139 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 1500 : 1350) : ((short_GI_20) ? 722 : 650); | |
1140 | else if (MCS_rate[0] & BIT(6)) | |
1141 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 1350 : 1215) : ((short_GI_20) ? 650 : 585); | |
1142 | else if (MCS_rate[0] & BIT(5)) | |
1143 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 1200 : 1080) : ((short_GI_20) ? 578 : 520); | |
1144 | else if (MCS_rate[0] & BIT(4)) | |
1145 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 900 : 810) : ((short_GI_20) ? 433 : 390); | |
1146 | else if (MCS_rate[0] & BIT(3)) | |
1147 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 600 : 540) : ((short_GI_20) ? 289 : 260); | |
1148 | else if (MCS_rate[0] & BIT(2)) | |
1149 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 450 : 405) : ((short_GI_20) ? 217 : 195); | |
1150 | else if (MCS_rate[0] & BIT(1)) | |
1151 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 300 : 270) : ((short_GI_20) ? 144 : 130); | |
1152 | else if (MCS_rate[0] & BIT(0)) | |
1153 | max_rate = (bw_40MHz) ? ((short_GI_40) ? 150 : 135) : ((short_GI_20) ? 72 : 65); | |
1154 | } | |
1155 | } | |
1156 | return max_rate; | |
1157 | } |