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