1 /******************************************************************************
2 *
3 * Copyright(c) 2007 - 2017 Realtek Corporation.
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
16 #include <drv_types.h>
17
18 #ifndef RTW_WNM_DBG
19 #define RTW_WNM_DBG 0
20 #endif
21 #if RTW_WNM_DBG
22 #define RTW_WNM_INFO(fmt, arg...) \
23 RTW_INFO(fmt, arg)
24 #define RTW_WNM_DUMP(str, data, len) \
25 RTW_INFO_DUMP(str, data, len)
26 #else
27 #define RTW_WNM_INFO(fmt, arg...) do {} while (0)
28 #define RTW_WNM_DUMP(str, data, len) do {} while (0)
29 #endif
30
31 #ifdef CONFIG_RTW_WNM
32
33 static u32 wnm_defualt_validity_time = 6000;
34 static u32 wnm_default_disassoc_time = 5000;
35 static u32 wnm_disassoc_wait_time = 500;
36
37 /* for wifi test, need more validity time to wait scan done */
38 static u32 wnm_ext_validity_time = 4000;
39
40 static u8 wnm_non_pref_ch_oui[] = {0x50, 0x6F, 0x9A, 0x2};
41
42 #define rtw_wnm_get_non_pref_ch_oui(p) ((u8 *)(p) + 2)
43
rtw_wmn_btm_cache_update(_adapter * padapter,struct btm_req_hdr * phdr)44 static void rtw_wmn_btm_cache_update(
45 _adapter *padapter, struct btm_req_hdr *phdr)
46 {
47 struct btm_rpt_cache *pcache = \
48 &(padapter->mlmepriv.nb_info.btm_cache);
49
50 pcache->dialog_token = phdr->dialog_token;
51 pcache->req_mode = phdr->req_mode;
52 pcache->disassoc_timer = le16_to_cpu(phdr->disassoc_timer);
53
54 if (phdr->validity_interval > 0)
55 pcache->validity_interval = phdr->validity_interval;
56
57 pcache->term_duration.id = phdr->term_duration.id;
58 pcache->term_duration.len = phdr->term_duration.len;
59 pcache->term_duration.tsf = le64_to_cpu(phdr->term_duration.tsf);
60 pcache->term_duration.duration = le16_to_cpu(phdr->term_duration.duration);
61
62 RTW_WNM_INFO("%s: req_mode(0x%02x), disassoc_timer(0x%04x), "
63 "validity_interval(0x%02x %s), tsf(0x%llx), duration(0x%02x)\n",
64 __func__, pcache->req_mode, pcache->disassoc_timer,
65 pcache->validity_interval, (!phdr->validity_interval)?
66 "default":"", pcache->term_duration.tsf,
67 pcache->term_duration.duration);
68
69 if (pcache->validity_interval > 0) {
70 pcache->validity_time = pcache->validity_interval * 100;
71 #ifdef CONFIG_RTW_MBO
72 if (rtw_mbo_wifi_logo_test(padapter))
73 pcache->validity_time += wnm_ext_validity_time;
74 #endif
75 }
76
77 if (pcache->disassoc_timer > 0) {
78 pcache->disassoc_time= pcache->disassoc_timer * 100;
79 #ifdef CONFIG_RTW_MBO
80 if (rtw_mbo_wifi_logo_test(padapter))
81 pcache->disassoc_time += wnm_ext_validity_time;
82 #endif
83 }
84
85 pcache->req_stime = rtw_get_current_time();
86
87 RTW_WNM_INFO("%s: validity_time=%u, disassoc_time=%u\n",
88 __func__, pcache->validity_time, pcache->disassoc_time);
89 }
90
rtw_wnm_btm_candidate_validity(struct btm_rpt_cache * pcache,u8 flag)91 static u8 rtw_wnm_btm_candidate_validity(
92 struct btm_rpt_cache *pcache, u8 flag)
93 {
94 u8 is_validity =_TRUE;
95 u32 req_validity_time = rtw_get_passing_time_ms(pcache->req_stime);
96
97 if ((flag & BIT(0)) && (req_validity_time > pcache->validity_time))
98 is_validity = _FALSE;
99
100 if ((flag & BIT(1)) && (req_validity_time > pcache->disassoc_time))
101 is_validity = _FALSE;
102
103 RTW_WNM_INFO("%s : validity=%u, rtime=%u, vtime=%u. dtime=%u\n",
104 __func__, is_validity, req_validity_time,
105 pcache->validity_time, pcache->disassoc_time);
106 return is_validity;
107 }
108
rtw_wmn_btm_rsp_reason_decision(_adapter * padapter,u8 * req_mode)109 u8 rtw_wmn_btm_rsp_reason_decision(_adapter *padapter, u8* req_mode)
110 {
111 struct recv_info *recvinfo = &(padapter->recvinfo);
112 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
113 u8 reason = 0;
114
115 if (!rtw_wnm_btm_diff_bss(padapter)) {
116 /* Reject - No suitable BSS transition candidates */
117 reason = 7;
118 goto candidate_remove;
119 }
120
121 #ifdef CONFIG_RTW_80211R
122 if (rtw_ft_chk_flags(padapter, RTW_FT_BTM_ROAM)) {
123 /* Accept */
124 reason = 0;
125 goto under_survey;
126 }
127 #endif
128
129 if (((*req_mode) & DISASSOC_IMMINENT) == 0) {
130 /* Reject - Unspecified reject reason */
131 reason = 1;
132 goto candidate_remove;
133 }
134
135 if (recvinfo->signal_strength_data.avg_val >=
136 pmlmepriv->roam_rssi_threshold) {
137 reason = 1;
138 RTW_WNM_INFO("%s : Reject - under high roam rssi(%u, %u) \n",
139 __func__, recvinfo->signal_strength_data.avg_val,
140 pmlmepriv->roam_rssi_threshold);
141 goto candidate_remove;
142 }
143
144 #ifdef CONFIG_RTW_80211R
145 under_survey:
146 if (check_fwstate(pmlmepriv, WIFI_UNDER_SURVEY)) {
147 RTW_WNM_INFO("%s reject due to _FW_UNDER_SURVEY\n", __func__);
148 reason = 1;
149 }
150 #endif
151
152 candidate_remove:
153 if (reason !=0)
154 rtw_wnm_reset_btm_candidate(&pmlmepriv->nb_info);
155
156 return reason;
157 }
158
rtw_wnm_btm_candidates_offset_get(u8 * pframe)159 static u32 rtw_wnm_btm_candidates_offset_get(u8* pframe)
160 {
161 u32 offset = 0;
162
163 if (!pframe)
164 return 0;
165
166 offset += 7;
167
168 /* BSS Termination Duration check */
169 if (wnm_btm_bss_term_inc(pframe))
170 offset += 12;
171
172 /* Session Information URL check*/
173 if (wnm_btm_ess_disassoc_im(pframe)) {
174 /*URL length field + URL variable length*/
175 offset = 1 + *(pframe + offset);
176 }
177
178 RTW_WNM_INFO("%s : hdr offset=%u\n", __func__, offset);
179 return offset;
180 }
181
rtw_wnm_btm_req_hdr_parsing(u8 * pframe,struct btm_req_hdr * phdr)182 static void rtw_wnm_btm_req_hdr_parsing(u8* pframe, struct btm_req_hdr *phdr)
183 {
184 u8 *pos;
185 u32 offset = 0;
186
187 if (!pframe || !phdr)
188 return;
189
190 _rtw_memset(phdr, 0, sizeof(struct btm_req_hdr));
191 phdr->dialog_token = wnm_btm_dialog_token(pframe);
192 phdr->req_mode = wnm_btm_req_mode(pframe);
193 phdr->disassoc_timer = wnm_btm_disassoc_timer(pframe);
194 phdr->validity_interval = wnm_btm_valid_interval(pframe);
195 if (wnm_btm_bss_term_inc(pframe)) {
196 pos = wnm_btm_term_duration_offset(pframe);
197 if (*pos == WNM_BTM_TERM_DUR_SUBEID) {
198 phdr->term_duration.id = *pos;
199 phdr->term_duration.len = *(pos + 1);
200 phdr->term_duration.tsf = *((u64*)(pos + 2));
201 phdr->term_duration.duration= *((u16*)(pos + 10));
202 } else
203 RTW_WNM_INFO("%s : invaild BSS Termination Duration"
204 " content!\n", __func__);
205 }
206
207 RTW_WNM_INFO("WNM: req_mode(0x%02x), disassoc_timer(0x%04x),"
208 " validity_interval(0x%02x)\n",
209 phdr->req_mode, phdr->disassoc_timer,
210 phdr->validity_interval);
211
212 if (wnm_btm_bss_term_inc(pframe)) {
213 RTW_WNM_INFO("WNM: tsf(0x%llx), duration(0x%4x)\n",
214 phdr->term_duration.tsf,
215 phdr->term_duration.duration);
216 }
217 }
218
rtw_wnm_btm_reassoc_req(_adapter * padapter)219 u8 rtw_wnm_btm_reassoc_req(_adapter *padapter)
220 {
221 struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
222 struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
223 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
224 struct roam_nb_info *pnb = &(pmlmepriv->nb_info);
225 u8 breassoc = _FALSE;
226
227 if (_rtw_memcmp(get_my_bssid(&(pmlmeinfo->network)),
228 pnb->roam_target_addr, ETH_ALEN)) {
229 RTW_WNM_INFO("%s : bss "MAC_FMT" found in roam_target "
230 MAC_FMT"\n", __func__,
231 MAC_ARG(get_my_bssid(&(pmlmeinfo->network))),
232 MAC_ARG(pnb->roam_target_addr));
233 breassoc = _TRUE;
234 }
235
236 return breassoc;
237 }
238
rtw_wnm_roam_scan_hdl(void * ctx)239 void rtw_wnm_roam_scan_hdl(void *ctx)
240 {
241 _adapter *padapter = (_adapter *)ctx;
242 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
243
244 if (rtw_is_scan_deny(padapter)) {
245 RTW_WNM_INFO("%s: roam scan would abort by scan_deny!\n",
246 __func__);
247 }
248
249 #ifdef CONFIG_RTW_80211R
250 if (rtw_ft_chk_flags(padapter, RTW_FT_BTM_ROAM)) {
251 pmlmepriv->need_to_roam = _TRUE;
252 rtw_set_to_roam(padapter,
253 padapter->registrypriv.max_roaming_times);
254 RTW_WNM_INFO("%s : enable roaming\n", __func__);
255 }
256
257 rtw_drv_scan_by_self(padapter, RTW_AUTO_SCAN_REASON_ROAM);
258 #endif
259 }
260
rtw_wnm_roam_scan(_adapter * padapter)261 static void rtw_wnm_roam_scan(_adapter *padapter)
262 {
263 struct roam_nb_info *pnb = &(padapter->mlmepriv.nb_info);
264
265 if (rtw_is_scan_deny(padapter)) {
266 _cancel_timer_ex(&pnb->roam_scan_timer);
267 _set_timer(&pnb->roam_scan_timer, 1000);
268 } else
269 rtw_wnm_roam_scan_hdl((void *)padapter);
270 }
271
rtw_wnm_disassoc_chk_hdl(void * ctx)272 void rtw_wnm_disassoc_chk_hdl(void *ctx)
273 {
274 _adapter *padapter = (_adapter *)ctx;
275 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
276 struct roam_nb_info *pnb = &(pmlmepriv->nb_info);
277
278 RTW_WNM_INFO("%s : expired\n", __func__);
279 if (pnb->disassoc_waiting <= 0 ) {
280 RTW_WNM_INFO("%s : btm roam is interrupted by disassoc\n",
281 __func__);
282 return;
283 }
284
285 pnb->disassoc_waiting = _FALSE;
286 rtw_wnm_roam_scan(padapter);
287 }
288
rtw_wnm_try_btm_roam_imnt(_adapter * padapter)289 u8 rtw_wnm_try_btm_roam_imnt(_adapter *padapter)
290 {
291 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
292 struct roam_nb_info *pnb = &(pmlmepriv->nb_info);
293 struct btm_rpt_cache *pcache = &(pnb->btm_cache);
294 u8 reason = 0, flag = 0;
295
296 if (!rtw_wnm_btm_preference_cap(padapter)) {
297 RTW_WNM_INFO("%s : no btm candidate can be used!\n",
298 __func__);
299 return 1;
300 }
301
302 flag = BIT(0) | BIT(1);
303 if (!rtw_wnm_btm_candidate_validity(pcache, flag))
304 return 1;
305
306 #ifdef CONFIG_RTW_MBO
307 if (!rtw_mbo_wifi_logo_test(padapter)
308 && !(pcache->req_mode & DISASSOC_IMMINENT)) {
309 RTW_WNM_INFO("%s : non-disassoc imminet req\n", __func__);
310 return 1;
311 }
312 #endif
313
314 RTW_WNM_INFO("%s : disassoc_waiting(%d)\n",
315 __func__, pnb->disassoc_waiting);
316 if (pnb->disassoc_waiting) {
317 _cancel_timer_ex(&pnb->disassoc_chk_timer);
318 pnb->disassoc_waiting = _FALSE;
319 rtw_wnm_roam_scan_hdl((void *)padapter);
320 } else if (!pnb->disassoc_waiting) {
321 RTW_WNM_INFO("%s : waiting for btm roaming start/finish\n",
322 __func__);
323 } else
324 reason = 1;
325
326 return reason;
327 }
328
rtw_wnm_process_btm_req(_adapter * padapter,u8 * pframe,u32 frame_len)329 void rtw_wnm_process_btm_req(_adapter *padapter, u8* pframe, u32 frame_len)
330 {
331 struct roam_nb_info *pnb = &(padapter->mlmepriv.nb_info);
332 struct btm_req_hdr req_hdr;
333 u8 *ptr, reason;
334 u32 elem_len, offset;
335
336 rtw_wnm_btm_req_hdr_parsing(pframe, &req_hdr);
337 offset = rtw_wnm_btm_candidates_offset_get(pframe);
338 if (offset == 0)
339 return;
340
341 if ((frame_len - offset) <= 15) {
342 RTW_INFO("WNM : Reject - "
343 "no suitable BSS transition candidates!\n");
344 rtw_wnm_issue_action(padapter,
345 RTW_WLAN_ACTION_WNM_BTM_RSP, 7, req_hdr.dialog_token);
346 return;
347 }
348
349 rtw_wmn_btm_cache_update(padapter, &req_hdr);
350
351 ptr = (pframe + offset);
352 elem_len = (frame_len - offset);
353 rtw_wnm_btm_candidates_survey(padapter, ptr, elem_len, _TRUE);
354 reason = rtw_wmn_btm_rsp_reason_decision(padapter, &pframe[3]);
355
356 #ifdef CONFIG_RTW_MBO
357 /* for wifi-test; AP2 could power-off when BTM-req received */
358 if ((reason > 0) && (rtw_mbo_wifi_logo_test(padapter))) {
359 _rtw_memcpy(pnb->roam_target_addr,
360 pnb->nb_rpt[0].bssid, ETH_ALEN);
361 RTW_WNM_INFO("%s : used report 0 as roam_target_addr"
362 "(reason=%u)\n", __func__, reason);
363 reason = 0;
364 pnb->preference_en = _TRUE;
365 pnb->nb_rpt_valid = _FALSE;
366 }
367 #endif
368
369 rtw_wnm_issue_action(padapter,
370 RTW_WLAN_ACTION_WNM_BTM_RSP, reason, req_hdr.dialog_token);
371
372 if (reason == 0) {
373 pnb->disassoc_waiting = _TRUE;
374 _set_timer(&pnb->disassoc_chk_timer, wnm_disassoc_wait_time);
375 }
376
377 }
378
rtw_wnm_reset_btm_candidate(struct roam_nb_info * pnb)379 void rtw_wnm_reset_btm_candidate(struct roam_nb_info *pnb)
380 {
381 pnb->preference_en = _FALSE;
382 _rtw_memset(pnb->roam_target_addr, 0, ETH_ALEN);
383 }
384
rtw_wnm_reset_btm_cache(_adapter * padapter)385 void rtw_wnm_reset_btm_cache(_adapter *padapter)
386 {
387 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
388 struct roam_nb_info *pnb = &(pmlmepriv->nb_info);
389 struct btm_rpt_cache *pcache = &(pnb->btm_cache);
390 u8 flag = 0;
391
392 flag |= BIT(0);
393 if (rtw_wnm_btm_candidate_validity(pcache, flag))
394 return;
395
396 rtw_wnm_reset_btm_candidate(pnb);
397 _rtw_memset(pcache, 0, sizeof(struct btm_rpt_cache));
398 pcache->validity_time = wnm_defualt_validity_time;
399 pcache->disassoc_time= wnm_default_disassoc_time;
400
401 #ifdef CONFIG_RTW_80211R
402 if (rtw_ft_chk_flags(padapter, RTW_FT_BTM_ROAM)) {
403 pmlmepriv->need_to_roam = _FALSE;
404 rtw_set_to_roam(padapter, 0);
405 RTW_WNM_INFO("%s : disabled roaming\n", __func__);
406 }
407 #endif
408 }
409
rtw_wnm_reset_btm_state(_adapter * padapter)410 void rtw_wnm_reset_btm_state(_adapter *padapter)
411 {
412 struct roam_nb_info *pnb = &(padapter->mlmepriv.nb_info);
413
414 pnb->last_nb_rpt_entries = 0;
415 pnb->nb_rpt_is_same = _TRUE;
416 pnb->nb_rpt_valid = _FALSE;
417 pnb->nb_rpt_ch_list_num = 0;
418 pnb->disassoc_waiting = -1;
419 _rtw_memset(&pnb->nb_rpt, 0, sizeof(pnb->nb_rpt));
420 _rtw_memset(&pnb->nb_rpt_ch_list, 0, sizeof(pnb->nb_rpt_ch_list));
421 rtw_wnm_reset_btm_cache(padapter);
422 }
423
rtw_wnm_btm_rsp_candidates_sz_get(_adapter * padapter,u8 * pframe,u32 frame_len)424 u32 rtw_wnm_btm_rsp_candidates_sz_get(
425 _adapter *padapter, u8* pframe, u32 frame_len)
426 {
427 u32 num = 0, sz = 0;
428 u8 status;
429 u8 *ptr;
430
431 if (!pframe || (frame_len <= 5))
432 goto exit;
433
434 status = wnm_btm_rsp_status(pframe);
435 if (((status != 0) && (status != 6)) || (frame_len < 23))
436 goto exit;
437
438 if (status == 0)
439 num = (frame_len - 5 - ETH_ALEN)/18;
440 else
441 num = (frame_len - 5)/18;
442 sz = sizeof(struct wnm_btm_cant) * num;
443 exit:
444 RTW_WNM_INFO("WNM: %u candidates(sz=%u) in BTM rsp\n", num, sz);
445 return sz;
446 }
447
rtw_wnm_process_btm_rsp(_adapter * padapter,u8 * pframe,u32 frame_len,struct btm_rsp_hdr * prsp)448 void rtw_wnm_process_btm_rsp(_adapter *padapter,
449 u8* pframe, u32 frame_len, struct btm_rsp_hdr *prsp)
450 {
451 prsp->dialog_token = wnm_btm_dialog_token(pframe);
452 prsp->status = wnm_btm_rsp_status(pframe);
453 prsp->termination_delay = wnm_btm_rsp_term_delay(pframe);
454
455 if ((pframe == NULL) || (frame_len == 0))
456 return;
457
458 prsp->status = *(pframe + 3);
459 prsp->termination_delay = *(pframe + 4);
460
461 /* no Target BSSID & Candidate in frame */
462 if (frame_len <= 5)
463 return;
464
465 /* accept */
466 if ((prsp->status == 0) && (frame_len >= 11))
467 _rtw_memcpy(prsp->bssid, (pframe + 5), ETH_ALEN);
468
469 /* STA BSS Transition Candidate List provided,
470 and at least one NB report exist */
471 if (((prsp->status == 0) || (prsp->status == 6)) \
472 && (frame_len >= 23)) {
473 struct wnm_btm_cant cant;
474 u8 *ptr, *pend;
475 u32 idx = 0;
476
477 ptr = pframe + 5;
478 if (prsp->status == 0)
479 ptr += ETH_ALEN;
480
481 pend = ptr + frame_len;
482 prsp->candidates_num = 0;
483 while (ptr < pend) {
484 if (*ptr != RTW_WLAN_ACTION_WNM_NB_RPT_ELEM)
485 break;
486 _rtw_memset(&cant, 0, sizeof(cant));
487 cant.nb_rpt.id = *ptr;
488 cant.nb_rpt.len = *(ptr + 1);
489 _rtw_memcpy(cant.nb_rpt.bssid, (ptr + 2), ETH_ALEN);
490 cant.nb_rpt.bss_info = *((u32 *)(ptr + 8));
491 cant.nb_rpt.reg_class = *(ptr + 12);
492 cant.nb_rpt.ch_num = *(ptr + 13);
493 cant.nb_rpt.phy_type= *(ptr + 14);
494
495 if (*(ptr + 15) == WNM_BTM_CAND_PREF_SUBEID)
496 cant.preference = *(ptr + 17);
497 ptr = ptr + cant.nb_rpt.len + 2;
498 if (prsp->pcandidates) {
499 prsp->candidates_num++;
500 _rtw_memcpy((prsp->pcandidates + \
501 sizeof(cant) * idx), &cant, sizeof(cant));
502 }
503
504 idx++;
505 RTW_WNM_INFO("WNM: btm rsp candidate bssid("MAC_FMT
506 ") ,bss_info(0x%04X), reg_class(0x%02X), ch(%d),"
507 " phy_type(0x%02X), preference(0x%02X)\n",
508 MAC_ARG(cant.nb_rpt.bssid), cant.nb_rpt.bss_info,
509 cant.nb_rpt.reg_class, cant.nb_rpt.ch_num,
510 cant.nb_rpt.phy_type, cant.preference);
511 if ((prsp->pcandidates) && \
512 (prsp->candidates_num > 0)) {
513 RTW_WNM_DUMP("WNM candidates: ", prsp->pcandidates,
514 (sizeof(struct wnm_btm_cant) * prsp->candidates_num));
515 }
516 }
517 }
518
519 }
520
rtw_wnm_hdr_init(_adapter * padapter,struct xmit_frame * pactionframe,u8 * pmac,u8 action,u8 ** pcontent)521 void rtw_wnm_hdr_init(_adapter *padapter,
522 struct xmit_frame *pactionframe, u8 *pmac,
523 u8 action, u8 **pcontent)
524 {
525 struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
526 struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
527 struct pkt_attrib *pattrib;
528 struct rtw_ieee80211_hdr *pwlanhdr;
529 u16 *pfctrl;
530 u8 category;
531
532 pattrib = &(pactionframe->attrib);
533 update_mgntframe_attrib(padapter, pattrib);
534 _rtw_memset(pactionframe->buf_addr, 0,
535 (WLANHDR_OFFSET + TXDESC_OFFSET));
536
537 *pcontent = (u8 *)(pactionframe->buf_addr + TXDESC_OFFSET);
538 pwlanhdr = (struct rtw_ieee80211_hdr *)(*pcontent);
539 pfctrl = &(pwlanhdr->frame_ctl);
540 *(pfctrl) = 0;
541
542 _rtw_memcpy(pwlanhdr->addr1, pmac, ETH_ALEN);
543 _rtw_memcpy(pwlanhdr->addr2, adapter_mac_addr(padapter), ETH_ALEN);
544 _rtw_memcpy(pwlanhdr->addr3,
545 get_my_bssid(&pmlmeinfo->network), ETH_ALEN);
546
547 SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
548 pmlmeext->mgnt_seq++;
549 set_frame_sub_type(*pcontent, WIFI_ACTION);
550
551 *pcontent += sizeof(struct rtw_ieee80211_hdr_3addr);
552 pattrib->pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
553
554 category = RTW_WLAN_CATEGORY_WNM;
555 *pcontent = rtw_set_fixed_ie(*pcontent, 1,
556 &(category), &(pattrib->pktlen));
557 *pcontent = rtw_set_fixed_ie(*pcontent, 1,
558 &(action), &(pattrib->pktlen));
559 }
560
rtw_wnm_build_btm_req_ies(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib,struct btm_req_hdr * phdr,u8 * purl,u32 url_len,u8 * pcandidates,u8 candidate_cnt)561 void rtw_wnm_build_btm_req_ies(_adapter *padapter,
562 u8 **pframe, struct pkt_attrib *pattrib,
563 struct btm_req_hdr *phdr, u8 *purl, u32 url_len,
564 u8 *pcandidates, u8 candidate_cnt)
565 {
566 int i;
567
568 *pframe = rtw_set_fixed_ie(*pframe, 1,
569 &phdr->dialog_token, &(pattrib->pktlen));
570 *pframe = rtw_set_fixed_ie(*pframe, 1,
571 &phdr->req_mode, &(pattrib->pktlen));
572 *pframe = rtw_set_fixed_ie(*pframe, 2,
573 (u8 *)&phdr->disassoc_timer, &(pattrib->pktlen));
574 *pframe = rtw_set_fixed_ie(*pframe, 1,
575 &phdr->validity_interval, &(pattrib->pktlen));
576
577 if (phdr->req_mode & BSS_TERMINATION_INCLUDED) {
578 *pframe = rtw_set_fixed_ie(*pframe, 1,
579 &phdr->term_duration.id, &(pattrib->pktlen));
580 *pframe = rtw_set_fixed_ie(*pframe, 1,
581 &phdr->term_duration.len, &(pattrib->pktlen));
582 *pframe = rtw_set_fixed_ie(*pframe, 8,
583 (u8 *)&phdr->term_duration.tsf,
584 &(pattrib->pktlen));
585 *pframe = rtw_set_fixed_ie(*pframe, 2,
586 (u8 *)&phdr->term_duration.duration,
587 &(pattrib->pktlen));
588 }
589
590 if ((purl != NULL) && (url_len > 0) &&
591 (phdr->req_mode & ESS_DISASSOC_IMMINENT)) {
592 *pframe = rtw_set_fixed_ie(*pframe, 1,
593 (u8 *)&url_len, &(pattrib->pktlen));
594 *pframe = rtw_set_fixed_ie(*pframe,
595 url_len, purl, &(pattrib->pktlen));
596 }
597
598 if ((pcandidates != NULL) && (candidate_cnt > 0)) {
599 for (i=0; i<candidate_cnt; i++) {
600 struct wnm_btm_cant *pcandidate = \
601 ((struct wnm_btm_cant *)pcandidates) + i;
602 struct nb_rpt_hdr *prpt = &(pcandidate->nb_rpt);
603
604 *pframe = rtw_set_fixed_ie(*pframe, 1,
605 &pcandidate->nb_rpt.id,
606 &(pattrib->pktlen));
607 *pframe = rtw_set_fixed_ie(*pframe, 1,
608 &pcandidate->nb_rpt.len,
609 &(pattrib->pktlen));
610 *pframe = rtw_set_fixed_ie(*pframe, ETH_ALEN,
611 pcandidate->nb_rpt.bssid,
612 &(pattrib->pktlen));
613 *pframe = rtw_set_fixed_ie(*pframe, 4,
614 (u8 *)&pcandidate->nb_rpt.bss_info,
615 &(pattrib->pktlen));
616 *pframe = rtw_set_fixed_ie(*pframe, 1,
617 &pcandidate->nb_rpt.reg_class,
618 &(pattrib->pktlen));
619 *pframe = rtw_set_fixed_ie(*pframe, 1,
620 &pcandidate->nb_rpt.ch_num,
621 &(pattrib->pktlen));
622 *pframe = rtw_set_fixed_ie(*pframe, 1,
623 &pcandidate->nb_rpt.phy_type,
624 &(pattrib->pktlen));
625 *pframe = rtw_set_ie(*pframe, WNM_BTM_CAND_PREF_SUBEID,
626 1, (u8 *)&pcandidate->preference,
627 &(pattrib->pktlen));
628 }
629 }
630
631
632 #ifdef CONFIG_RTW_MBO
633 rtw_mbo_build_wnm_btmreq_reason_ies(padapter, pframe, pattrib);
634 #endif
635 }
636
rtw_wnm_issue_btm_req(_adapter * padapter,u8 * pmac,struct btm_req_hdr * phdr,u8 * purl,u32 url_len,u8 * pcandidates,u8 candidate_cnt)637 void rtw_wnm_issue_btm_req(_adapter *padapter,
638 u8 *pmac, struct btm_req_hdr *phdr,
639 u8 *purl, u32 url_len,
640 u8 *pcandidates, u8 candidate_cnt)
641 {
642 struct roam_nb_info *pnb = &(padapter->mlmepriv.nb_info);
643 struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
644 struct xmit_frame *pmgntframe;
645 struct pkt_attrib *pattrib;
646 u8 action, *pframe, dialog_token = 0;
647
648 if (!pmac || is_zero_mac_addr(pmac)
649 || is_broadcast_mac_addr(pmac))
650 return ;
651
652 if ((pmgntframe = alloc_mgtxmitframe(pxmitpriv)) == NULL)
653 return ;
654
655 rtw_wnm_hdr_init(padapter, pmgntframe, pmac,
656 RTW_WLAN_ACTION_WNM_BTM_REQ, &pframe);
657
658 pattrib = &(pmgntframe->attrib);
659 rtw_wnm_build_btm_req_ies(padapter, &pframe, pattrib,
660 phdr, purl, url_len, pcandidates, candidate_cnt);
661
662 if (0) {
663 u8 *__p = (u8 *)(pmgntframe->buf_addr + TXDESC_OFFSET);
664 RTW_WNM_DUMP("WNM BTM REQ :", __p, pattrib->pktlen);
665 }
666
667 pattrib->last_txcmdsz = pattrib->pktlen;
668 dump_mgntframe(padapter, pmgntframe);
669 /* dump_mgntframe_and_wait_ack(padapter, pmgntframe); */
670 RTW_INFO("WNM: BSS Transition Management Request sent\n");
671 }
672
rtw_wnm_issue_action(_adapter * padapter,u8 action,u8 reason,u8 dialog)673 void rtw_wnm_issue_action(_adapter *padapter,
674 u8 action, u8 reason, u8 dialog)
675 {
676 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
677 struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
678 struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
679 struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
680 struct xmit_frame *pmgntframe;
681 struct rtw_ieee80211_hdr *pwlanhdr;
682 struct pkt_attrib *pattrib;
683 u8 category, termination_delay, *pframe, dialog_token = 0;
684 #ifdef CONFIG_RTW_MBO
685 u8 mbo_trans_rej_res = 1; /* Unspecified reason */
686 u8 mbo_notif_req_type ;
687 #endif
688 u16 *fctrl;
689
690 if ((pmgntframe = alloc_mgtxmitframe(pxmitpriv)) == NULL)
691 return ;
692
693 pattrib = &(pmgntframe->attrib);
694 update_mgntframe_attrib(padapter, pattrib);
695 _rtw_memset(pmgntframe->buf_addr, 0, (WLANHDR_OFFSET + TXDESC_OFFSET));
696
697 pframe = (u8 *)(pmgntframe->buf_addr + TXDESC_OFFSET);
698 pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
699
700 fctrl = &(pwlanhdr->frame_ctl);
701 *(fctrl) = 0;
702
703 _rtw_memcpy(pwlanhdr->addr1,
704 get_my_bssid(&pmlmeinfo->network), ETH_ALEN);
705 _rtw_memcpy(pwlanhdr->addr2,
706 adapter_mac_addr(padapter), ETH_ALEN);
707 _rtw_memcpy(pwlanhdr->addr3,
708 get_my_bssid(&pmlmeinfo->network), ETH_ALEN);
709
710 SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
711 pmlmeext->mgnt_seq++;
712 set_frame_sub_type(pframe, WIFI_ACTION);
713
714 pframe += sizeof(struct rtw_ieee80211_hdr_3addr);
715 pattrib->pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
716
717 category = RTW_WLAN_CATEGORY_WNM;
718 pframe = rtw_set_fixed_ie(pframe, 1, &(category), &(pattrib->pktlen));
719 pframe = rtw_set_fixed_ie(pframe, 1, &(action), &(pattrib->pktlen));
720
721 switch (action) {
722 case RTW_WLAN_ACTION_WNM_BTM_QUERY:
723 dialog_token++;
724 pframe = rtw_set_fixed_ie(pframe, 1,
725 &(dialog_token), &(pattrib->pktlen));
726 pframe = rtw_set_fixed_ie(pframe, 1,
727 &(reason), &(pattrib->pktlen));
728 RTW_INFO("WNM: BSS Transition Management Query sent\n");
729 break;
730 case RTW_WLAN_ACTION_WNM_BTM_RSP:
731 dialog_token = dialog;
732 termination_delay = 0;
733 pframe = rtw_set_fixed_ie(pframe, 1,
734 &(dialog_token), &(pattrib->pktlen));
735 pframe = rtw_set_fixed_ie(pframe, 1,
736 &(reason), &(pattrib->pktlen));
737 pframe = rtw_set_fixed_ie(pframe, 1,
738 &(termination_delay),
739 &(pattrib->pktlen));
740 if (!reason && !is_zero_mac_addr(
741 pmlmepriv->nb_info.roam_target_addr)) {
742 pframe = rtw_set_fixed_ie(pframe, 6,
743 pmlmepriv->nb_info.roam_target_addr,
744 &(pattrib->pktlen));
745 }
746
747 #ifdef CONFIG_RTW_MBO
748 rtw_mbo_build_trans_reject_reason_attr(padapter,
749 &pframe, pattrib, &mbo_trans_rej_res);
750 #endif
751
752 RTW_INFO("WNM: BSS Transition Management Response"
753 " sent(reason:%d)\n", reason);
754 break;
755 case RTW_WLAN_ACTION_WNM_NOTIF_REQ:
756 #ifdef CONFIG_RTW_MBO
757 dialog_token++;
758 mbo_notif_req_type = WLAN_EID_VENDOR_SPECIFIC;
759 pframe = rtw_set_fixed_ie(pframe, 1,
760 &(dialog_token),
761 &(pattrib->pktlen));
762 pframe = rtw_set_fixed_ie(pframe, 1,
763 &(mbo_notif_req_type),
764 &(pattrib->pktlen));
765 rtw_mbo_build_wnm_notification(padapter,
766 &pframe, pattrib);
767 RTW_INFO("WNM: Notification request sent\n");
768 #endif
769 break;
770 case RTW_WLAN_ACTION_WNM_NOTIF_RSP:
771 #ifdef CONFIG_RTW_MBO
772 dialog_token = dialog;
773 pframe = rtw_set_fixed_ie(pframe, 1,
774 &(dialog_token), &(pattrib->pktlen));
775 pframe = rtw_set_fixed_ie(pframe, 1,
776 &(reason), &(pattrib->pktlen));
777 RTW_INFO("WNM: Notification Response sent\n");
778 #endif
779 break;
780 default:
781 goto exit;
782 }
783
784 pattrib->last_txcmdsz = pattrib->pktlen;
785 dump_mgntframe(padapter, pmgntframe);
786 /* dump_mgntframe_and_wait_ack(padapter, pmgntframe); */
787
788 exit:
789 return;
790 }
791
792 /* argument req_ie@cfg80211_roamed()/cfg80211_connect_result()
793 is association request IEs format. if driver used reassoc-req format,
794 RSN IE could not be parsed @supplicant process */
rtw_wnm_update_reassoc_req_ie(_adapter * padapter)795 void rtw_wnm_update_reassoc_req_ie(_adapter *padapter)
796 {
797 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
798 u32 dup_len, offset;
799 u8 *pdup;
800
801 if (!pmlmepriv->assoc_req || !pmlmepriv->assoc_req_len)
802 return;
803
804 /* total len is assoc req len without Current AP Field*/
805 dup_len = pmlmepriv->assoc_req_len - ETH_ALEN;
806
807 /* offset is a len of 80211 header +
808 capability(2B) + listen interval(2B) */
809 offset = sizeof(struct rtw_ieee80211_hdr_3addr) + 4;
810
811 pdup = rtw_zmalloc(dup_len);
812 if (pdup) {
813 /* remove Current AP Field @reassoc req IE */
814 _rtw_memcpy(pdup, pmlmepriv->assoc_req, offset);
815 _rtw_memcpy(pdup + offset,
816 pmlmepriv->assoc_req + offset + ETH_ALEN,
817 pmlmepriv->assoc_req_len - offset);
818 rtw_buf_update(&pmlmepriv->assoc_req,
819 &pmlmepriv->assoc_req_len, pdup, dup_len);
820 rtw_mfree(pdup, dup_len);
821 }
822 }
823 #endif /* CONFIG_RTW_WNM */
824
825 #if defined(CONFIG_RTW_WNM) || defined(CONFIG_RTW_80211K)
rtw_roam_nb_info_init(_adapter * padapter)826 void rtw_roam_nb_info_init(_adapter *padapter)
827 {
828 struct roam_nb_info *pnb = &(padapter->mlmepriv.nb_info);
829 struct btm_rpt_cache *pcache = &(pnb->btm_cache);
830
831 _rtw_memset(&pnb->nb_rpt, 0, sizeof(pnb->nb_rpt));
832 _rtw_memset(&pnb->nb_rpt_ch_list, 0, sizeof(pnb->nb_rpt_ch_list));
833 _rtw_memset(&pnb->roam_target_addr, 0, ETH_ALEN);
834 pnb->nb_rpt_valid = _FALSE;
835 pnb->nb_rpt_ch_list_num = 0;
836 pnb->preference_en = _FALSE;
837 pnb->nb_rpt_is_same = _TRUE;
838 pnb->last_nb_rpt_entries = 0;
839 pnb->disassoc_waiting = -1;
840 #ifdef CONFIG_RTW_WNM
841 pnb->features = 0;
842 /* pnb->features |= RTW_WNM_FEATURE_BTM_REQ_EN; */
843
844 rtw_init_timer(&pnb->roam_scan_timer,
845 rtw_wnm_roam_scan_hdl,
846 padapter);
847 rtw_init_timer(&pnb->disassoc_chk_timer,
848 rtw_wnm_disassoc_chk_hdl,
849 padapter);
850
851 _rtw_memset(pcache, 0, sizeof(struct btm_rpt_cache));
852 pcache->validity_time = wnm_defualt_validity_time;
853 pcache->disassoc_time= wnm_default_disassoc_time ;
854 #endif
855 }
856
rtw_roam_nb_scan_list_set(_adapter * padapter,struct sitesurvey_parm * pparm)857 u8 rtw_roam_nb_scan_list_set(
858 _adapter *padapter, struct sitesurvey_parm *pparm)
859 {
860 u8 ret = _FALSE;
861 u32 i;
862 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
863 struct roam_nb_info *pnb = &(pmlmepriv->nb_info);
864
865 #ifdef CONFIG_RTW_80211R
866 if (!rtw_chk_roam_flags(padapter, RTW_ROAM_ACTIVE)
867 && !rtw_ft_chk_flags(padapter, RTW_FT_BTM_ROAM))
868 return ret;
869 #endif
870
871 if (!pmlmepriv->need_to_roam)
872 return ret;
873
874 if ((!pmlmepriv->nb_info.nb_rpt_valid) || (!pnb->nb_rpt_ch_list_num))
875 return ret;
876
877 if (!pparm)
878 return ret;
879
880 rtw_init_sitesurvey_parm(padapter, pparm);
881 if (rtw_roam_busy_scan(padapter, pnb)) {
882 pparm->ch_num = 1;
883 pparm->ch[pmlmepriv->ch_cnt].hw_value =
884 pnb->nb_rpt_ch_list[pmlmepriv->ch_cnt].hw_value;
885 pmlmepriv->ch_cnt++;
886 ret = _TRUE;
887
888 RTW_WNM_INFO("%s: ch_cnt=%u, (%u)hw_value=%u\n",
889 __func__, pparm->ch_num, pmlmepriv->ch_cnt,
890 pparm->ch[pmlmepriv->ch_cnt].hw_value);
891
892 if (pmlmepriv->ch_cnt == pnb->nb_rpt_ch_list_num) {
893 pmlmepriv->nb_info.nb_rpt_valid = _FALSE;
894 pmlmepriv->ch_cnt = 0;
895 }
896 goto set_bssid_list;
897 }
898
899 pparm->ch_num = (pnb->nb_rpt_ch_list_num > RTW_CHANNEL_SCAN_AMOUNT)?
900 (RTW_CHANNEL_SCAN_AMOUNT):(pnb->nb_rpt_ch_list_num);
901 for (i=0; i<pparm->ch_num; i++) {
902 pparm->ch[i].hw_value = pnb->nb_rpt_ch_list[i].hw_value;
903 pparm->ch[i].flags = RTW_IEEE80211_CHAN_PASSIVE_SCAN;
904 }
905
906 pmlmepriv->nb_info.nb_rpt_valid = _FALSE;
907 pmlmepriv->ch_cnt = 0;
908 ret = _TRUE;
909
910 set_bssid_list:
911 rtw_sitesurvey_cmd(padapter, pparm);
912 return ret;
913 }
914
rtw_wnm_nb_elem_parsing(u8 * pdata,u32 data_len,u8 from_btm,u32 * nb_rpt_num,u8 * nb_rpt_is_same,struct roam_nb_info * pnb,struct wnm_btm_cant * pcandidates)915 static u8 rtw_wnm_nb_elem_parsing(
916 u8* pdata, u32 data_len, u8 from_btm,
917 u32 *nb_rpt_num, u8 *nb_rpt_is_same,
918 struct roam_nb_info *pnb, struct wnm_btm_cant *pcandidates)
919 {
920 u8 bfound = _FALSE, ret = _SUCCESS;
921 u8 *ptr, *pend, *op;
922 u32 elem_len, subelem_len, op_len;
923 u32 i, nb_rpt_entries = 0;
924 struct nb_rpt_hdr *pie;
925 struct wnm_btm_cant *pcandidate;
926
927 if ((!pdata) || (!pnb))
928 return _FAIL;
929
930 if ((from_btm) && (!pcandidates))
931 return _FAIL;
932
933 ptr = pdata;
934 pend = ptr + data_len;
935 elem_len = data_len;
936 subelem_len = (u32)*(pdata+1);
937
938 for (i=0; i < RTW_MAX_NB_RPT_NUM; i++) {
939 if (((ptr + 7) > pend) || (elem_len < subelem_len))
940 break;
941
942 if (*ptr != RTW_WLAN_ACTION_WNM_NB_RPT_ELEM) {
943 RTW_WNM_INFO("WNM: end of data(0x%2x)!\n", *ptr);
944 break;
945 }
946
947 pie = (struct nb_rpt_hdr *)ptr;
948 if (from_btm) {
949 op = rtw_get_ie((u8 *)(ptr+15),
950 WNM_BTM_CAND_PREF_SUBEID,
951 &op_len, (subelem_len - 15));
952 }
953
954 ptr = (u8 *)(ptr + subelem_len + 2);
955 elem_len -= (subelem_len +2);
956 subelem_len = *(ptr+1);
957 if (from_btm) {
958 pcandidate = (pcandidates + i);
959 _rtw_memcpy(&pcandidate->nb_rpt, pie,
960 sizeof(struct nb_rpt_hdr));
961 if (op && (op_len !=0)) {
962 pcandidate->preference = *(op + 2);
963 bfound = _TRUE;
964 } else
965 pcandidate->preference = 0;
966
967 RTW_WNM_INFO("WNM: preference check bssid("MAC_FMT
968 ") ,bss_info(0x%04X), reg_class(0x%02X), ch(%d),"
969 " phy_type(0x%02X), preference(0x%02X)\n",
970 MAC_ARG(pcandidate->nb_rpt.bssid),
971 pcandidate->nb_rpt.bss_info,
972 pcandidate->nb_rpt.reg_class,
973 pcandidate->nb_rpt.ch_num,
974 pcandidate->nb_rpt.phy_type,
975 pcandidate->preference);
976 } else {
977 if (_rtw_memcmp(&pnb->nb_rpt[i], pie,
978 sizeof(struct nb_rpt_hdr)) == _FALSE)
979 *nb_rpt_is_same = _FALSE;
980 _rtw_memcpy(&pnb->nb_rpt[i], pie,
981 sizeof(struct nb_rpt_hdr));
982 }
983 nb_rpt_entries++;
984 }
985
986 if (from_btm)
987 pnb->preference_en = (bfound)?_TRUE:_FALSE;
988
989 *nb_rpt_num = nb_rpt_entries;
990 return ret;
991 }
992
993 /* selection sorting based on preference value
994 * IN : nb_rpt_entries - candidate num
995 * IN/OUT : pcandidates - candidate list
996 * return : TRUE - means pcandidates is updated.
997 */
rtw_wnm_candidates_sorting(u32 nb_rpt_entries,struct wnm_btm_cant * pcandidates)998 static u8 rtw_wnm_candidates_sorting(
999 u32 nb_rpt_entries, struct wnm_btm_cant *pcandidates)
1000 {
1001 u8 updated = _FALSE;
1002 u32 i, j, pos;
1003 struct wnm_btm_cant swap;
1004 struct wnm_btm_cant *pcant_1, *pcant_2;
1005
1006 if ((!nb_rpt_entries) || (!pcandidates))
1007 return updated;
1008
1009 for (i=0; i < (nb_rpt_entries - 1); i++) {
1010 pos = i;
1011 for (j=(i + 1); j < nb_rpt_entries; j++) {
1012 pcant_1 = pcandidates+pos;
1013 pcant_2 = pcandidates+j;
1014 if ((pcant_1->preference) < (pcant_2->preference))
1015 pos = j;
1016 }
1017
1018 if (pos != i) {
1019 updated = _TRUE;
1020 _rtw_memcpy(&swap, (pcandidates+i),
1021 sizeof(struct wnm_btm_cant));
1022 _rtw_memcpy((pcandidates+i), (pcandidates+pos),
1023 sizeof(struct wnm_btm_cant));
1024 _rtw_memcpy((pcandidates+pos), &swap,
1025 sizeof(struct wnm_btm_cant));
1026 }
1027 }
1028 return updated;
1029 }
1030
rtw_wnm_nb_info_update(u32 nb_rpt_entries,u8 from_btm,struct roam_nb_info * pnb,struct wnm_btm_cant * pcandidates,u8 * nb_rpt_is_same)1031 static void rtw_wnm_nb_info_update(
1032 u32 nb_rpt_entries, u8 from_btm,
1033 struct roam_nb_info *pnb,
1034 struct wnm_btm_cant *pcandidates,
1035 u8 *nb_rpt_is_same)
1036 {
1037 u8 is_found;
1038 u32 i, j;
1039 struct wnm_btm_cant *pcand;
1040
1041 if (!pnb)
1042 return;
1043
1044 pnb->nb_rpt_ch_list_num = 0;
1045 for (i=0; i<nb_rpt_entries; i++) {
1046 is_found = _FALSE;
1047 if (from_btm) {
1048 pcand = (pcandidates+i);
1049 if (_rtw_memcmp(&pnb->nb_rpt[i], &pcand->nb_rpt,
1050 sizeof(struct nb_rpt_hdr)) == _FALSE)
1051 *nb_rpt_is_same = _FALSE;
1052 _rtw_memcpy(&pnb->nb_rpt[i], &pcand->nb_rpt,
1053 sizeof(struct nb_rpt_hdr));
1054 }
1055
1056 RTW_WNM_INFO("WNM: bssid(" MAC_FMT
1057 ") , bss_info(0x%04X), reg_class(0x%02X),"
1058 " ch_num(%d), phy_type(0x%02X)\n",
1059 MAC_ARG(pnb->nb_rpt[i].bssid), pnb->nb_rpt[i].bss_info,
1060 pnb->nb_rpt[i].reg_class, pnb->nb_rpt[i].ch_num,
1061 pnb->nb_rpt[i].phy_type);
1062
1063 if (pnb->nb_rpt[i].ch_num == 0)
1064 continue;
1065
1066 for (j=0; j<nb_rpt_entries; j++) {
1067 if (pnb->nb_rpt[i].ch_num ==
1068 pnb->nb_rpt_ch_list[j].hw_value) {
1069 is_found = _TRUE;
1070 break;
1071 }
1072 }
1073
1074 if (!is_found) {
1075 pnb->nb_rpt_ch_list[pnb->nb_rpt_ch_list_num].hw_value =\
1076 pnb->nb_rpt[i].ch_num;
1077 pnb->nb_rpt_ch_list_num++;
1078 }
1079 }
1080 }
1081
rtw_wnm_btm_candidate_select(_adapter * padapter)1082 static void rtw_wnm_btm_candidate_select(_adapter *padapter)
1083 {
1084 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
1085 struct roam_nb_info *pnb = &(padapter->mlmepriv.nb_info);
1086 struct wlan_network *pnetwork;
1087 u8 bfound = _FALSE;
1088 u8 ignore_currrent = _FALSE;
1089 u32 i;
1090
1091 #ifdef CONFIG_RTW_80211R
1092 if (rtw_ft_chk_flags(padapter, RTW_FT_BTM_ROAM))
1093 ignore_currrent = _TRUE;
1094 #endif
1095
1096 for (i = 0; i < pnb->last_nb_rpt_entries; i++) {
1097 if (ignore_currrent &&
1098 (_rtw_memcmp(pnb->nb_rpt[i].bssid,\
1099 padapter->mlmepriv.cur_network.network.MacAddress,
1100 ETH_ALEN))) {
1101 RTW_WNM_INFO("WNM : ignore candidate "MAC_FMT
1102 " for it's connected(%u)!\n",
1103 MAC_ARG(pnb->nb_rpt[i].bssid), i);
1104 continue;
1105 }
1106
1107 pnetwork = rtw_find_network(
1108 &(pmlmepriv->scanned_queue),
1109 pnb->nb_rpt[i].bssid);
1110
1111 if (pnetwork) {
1112 bfound = _TRUE;
1113 break;
1114 }
1115 }
1116
1117 if (bfound) {
1118 _rtw_memcpy(pnb->roam_target_addr,
1119 pnb->nb_rpt[i].bssid, ETH_ALEN);
1120 RTW_INFO("WNM : select btm entry(%d) -"
1121 " %s("MAC_FMT", ch:%u) rssi:%d\n"
1122 , i
1123 , pnetwork->network.Ssid.Ssid
1124 , MAC_ARG(pnetwork->network.MacAddress)
1125 , pnetwork->network.Configuration.DSConfig
1126 , (int)pnetwork->network.PhyInfo.rssi);
1127 } else
1128 _rtw_memset(pnb->roam_target_addr,0, ETH_ALEN);
1129 }
1130
rtw_wnm_btm_candidates_survey(_adapter * padapter,u8 * pframe,u32 elem_len,u8 from_btm)1131 u32 rtw_wnm_btm_candidates_survey(
1132 _adapter *padapter, u8* pframe, u32 elem_len, u8 from_btm)
1133 {
1134 struct roam_nb_info *pnb = &(padapter->mlmepriv.nb_info);
1135 struct wnm_btm_cant *pcandidate_list = NULL;
1136 u8 nb_rpt_is_same = _TRUE;
1137 u32 ret = _FAIL;
1138 u32 nb_rpt_entries = 0;
1139
1140 if (from_btm) {
1141 u32 mlen = sizeof(struct wnm_btm_cant) * RTW_MAX_NB_RPT_NUM;
1142 pcandidate_list = (struct wnm_btm_cant *)rtw_malloc(mlen);
1143 if (pcandidate_list == NULL)
1144 goto exit;
1145 }
1146
1147 /*clean the status set last time*/
1148 _rtw_memset(&pnb->nb_rpt_ch_list, 0, sizeof(pnb->nb_rpt_ch_list));
1149 pnb->nb_rpt_valid = _FALSE;
1150 if (!rtw_wnm_nb_elem_parsing(
1151 pframe, elem_len, from_btm,
1152 &nb_rpt_entries, &nb_rpt_is_same,
1153 pnb, pcandidate_list))
1154 goto exit;
1155
1156 if (nb_rpt_entries != 0) {
1157 if ((from_btm) && (rtw_wnm_btm_preference_cap(padapter))) {
1158 rtw_wnm_candidates_sorting(
1159 nb_rpt_entries, pcandidate_list);
1160 }
1161
1162 rtw_wnm_nb_info_update(
1163 nb_rpt_entries, from_btm,
1164 pnb, pcandidate_list, &nb_rpt_is_same);
1165 }
1166
1167 RTW_WNM_INFO("nb_rpt_is_same = %d, nb_rpt_entries = %d,"
1168 " last_nb_rpt_entries = %d\n",
1169 nb_rpt_is_same, nb_rpt_entries,
1170 pnb->last_nb_rpt_entries);
1171 if ((nb_rpt_is_same == _TRUE) &&
1172 (nb_rpt_entries == pnb->last_nb_rpt_entries)) {
1173 pnb->nb_rpt_is_same = _TRUE;
1174 } else {
1175 pnb->nb_rpt_is_same = _FALSE;
1176 pnb->last_nb_rpt_entries = nb_rpt_entries;
1177 }
1178
1179 if ((from_btm) && (nb_rpt_entries != 0))
1180 rtw_wnm_btm_candidate_select(padapter);
1181
1182 pnb->nb_rpt_valid = _TRUE;
1183 ret = _SUCCESS;
1184
1185 exit:
1186 if (from_btm && pcandidate_list) {
1187 rtw_mfree((u8 *)pcandidate_list,
1188 sizeof(struct wnm_btm_cant) * RTW_MAX_NB_RPT_NUM);
1189 }
1190
1191 return ret;
1192 }
1193
rtw_wnm_process_btm_query(_adapter * padapter,u8 * pframe,u32 frame_len)1194 void rtw_wnm_process_btm_query(_adapter *padapter, u8* pframe, u32 frame_len)
1195 {
1196 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
1197 struct nb_rpt_hdr *pie;
1198 u8 *ptr, *pend, *op;
1199 u32 elem_len, subelem_len, op_len;
1200 u32 i, nb_rpt_entries = 0;
1201 if (!pframe || !frame_len)
1202 return;
1203
1204 /* no preferred bss transition candidate list include */
1205 if (wnm_btm_query_reason(pframe) != 0x13)
1206 return;
1207
1208 ptr = pframe + 4;
1209 elem_len = frame_len - 4;
1210 pend = ptr + elem_len;
1211 subelem_len = (u32)*(ptr + 1);
1212
1213 RTW_WNM_DUMP("BTM QUERY :", ptr, elem_len);
1214 for (i = 0; i < RTW_MAX_NB_RPT_NUM; i++) {
1215 if (((ptr + 7) > pend) || (elem_len < subelem_len))
1216 break;
1217 if (*ptr != RTW_WLAN_ACTION_WNM_NB_RPT_ELEM)
1218 break;
1219 pie = (struct nb_rpt_hdr *)ptr;
1220 op = rtw_get_ie((u8 *)(ptr+15), WNM_BTM_CAND_PREF_SUBEID,
1221 &op_len, (subelem_len - 15));
1222
1223 #ifdef CONFIG_RTW_MBO
1224 /* for debug only */
1225 #if 0
1226 if (rtw_mbo_wifi_logo_test(padapter) &&
1227 check_fwstate(pmlmepriv, WIFI_AP_STATE) == _TRUE) {
1228 struct mbo_attr_info *pmbo_attr = \
1229 &(pmlmepriv->mbo_attr);
1230 struct mbo_user_btm_req_pkt *puser = \
1231 &(pmbo_attr->user_raw);
1232 struct wnm_btm_cant *puser_cant;
1233 u8 j = 0, idx = 0, found = _FALSE;
1234
1235 for (j = 0; j < RTW_MAX_NB_RPT_NUM; j++) {
1236 puser_cant = &puser->btm_cants[j];
1237 if (_rtw_memcmp(pie->bssid,
1238 puser_cant->nb_rpt.bssid, ETH_ALEN)) {
1239 puser_cant->nb_rpt.bss_info = \
1240 pie->bss_info;
1241 puser_cant->nb_rpt.reg_class = \
1242 pie->reg_class;
1243 puser_cant->nb_rpt.ch_num = \
1244 pie->ch_num;
1245 puser_cant->nb_rpt.phy_type = \
1246 pie->phy_type;
1247 puser_cant->preference = \
1248 *(op + 2);
1249 idx = j;
1250 found = _TRUE;
1251 break;
1252 }
1253 }
1254
1255 if (!found) {
1256 if (puser->candidate_cnt >= RTW_MAX_NB_RPT_NUM)
1257 puser->candidate_cnt = 0;
1258 puser_cant = \
1259 &puser->btm_cants[puser->candidate_cnt];
1260 puser_cant->nb_rpt.id = \
1261 RTW_WLAN_ACTION_WNM_NB_RPT_ELEM;
1262 puser_cant->nb_rpt.len = 0x10;
1263 _rtw_memcpy(puser_cant->nb_rpt.bssid,
1264 pie->bssid, ETH_ALEN);
1265 puser_cant->nb_rpt.bss_info = \
1266 pie->bss_info;
1267 puser_cant->nb_rpt.reg_class = \
1268 pie->reg_class;
1269 puser_cant->nb_rpt.ch_num = \
1270 pie->ch_num;
1271 puser_cant->nb_rpt.phy_type = \
1272 pie->phy_type;
1273 puser_cant->preference = \
1274 *(op + 2);
1275 puser->candidate_cnt++;
1276 }
1277
1278 RTW_WNM_INFO("%s: idx=%u, bssid("MAC_FMT"),"
1279 " bss_info(0x%04X), reg_class(0x%02X),"
1280 " ch(%d), phy_type(0x%02X), preference(0x%02X)\n",
1281 __func__, (found)?idx:puser->candidate_cnt,
1282 MAC_ARG(puser_cant->nb_rpt.bssid),
1283 puser_cant->nb_rpt.bss_info,
1284 puser_cant->nb_rpt.reg_class,
1285 puser_cant->nb_rpt.ch_num,
1286 puser_cant->nb_rpt.phy_type,
1287 puser_cant->preference);
1288 }
1289 #endif
1290 #endif /* end of CONFIG_RTW_MBO */
1291
1292 ptr = (u8 *)(ptr + subelem_len + 2);
1293 elem_len -= (subelem_len +2);
1294 subelem_len = *(ptr+1);
1295 } /* end of for-loop RTW_MAX_NB_RPT_NUM */
1296
1297 }
1298
rtw_wnm_non_pref_ch_attr_get(u8 * pie,u32 * plen,u32 limit)1299 static u8 *rtw_wnm_non_pref_ch_attr_get(u8 *pie, u32 *plen, u32 limit)
1300 {
1301 const u8 *p = pie;
1302 u32 tmp, i;
1303
1304 if (limit <= 1)
1305 return NULL;
1306
1307 i = 0;
1308 *plen = 0;
1309 while (1) {
1310 if ((*p == _VENDOR_SPECIFIC_IE_) &&
1311 (_rtw_memcmp(rtw_wnm_get_non_pref_ch_oui(p),
1312 wnm_non_pref_ch_oui, 4))) {
1313 *plen = *(p + 1);
1314 return (u8 *)p;
1315 } else {
1316 tmp = *(p + 1);
1317 p += (tmp + 2);
1318 i += (tmp + 2);
1319 }
1320
1321 if (i >= limit)
1322 break;
1323 }
1324
1325 return NULL;
1326 }
1327
rtw_wnm_process_notification_req(_adapter * padapter,u8 * pframe,u32 frame_len)1328 void rtw_wnm_process_notification_req(
1329 _adapter *padapter, u8 *pframe, u32 frame_len)
1330 {
1331 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
1332 #ifdef CONFIG_RTW_MBO
1333 struct mbo_attr_info *pmbo_attr = &(pmlmepriv->mbo_attr);
1334 struct mbo_user_btm_req_pkt *puser_raw = &(pmbo_attr->user_raw);
1335 #endif
1336 struct wnm_btm_cant *pcant = NULL;
1337 u8 dialog, type;
1338 u8 *psubie;
1339 u8 *ptr = NULL;
1340 u32 left_len, subie_len;
1341
1342 if (!pframe || (frame_len < 5))
1343 return;
1344
1345 /* RTW_WNM_DUMP("notify-req :", pframe, frame_len); */
1346 dialog = *(pframe + 2);
1347 type = *(pframe + 3);
1348
1349 if (type != WLAN_EID_VENDOR_SPECIFIC)
1350 return;
1351
1352 ptr = (pframe + 4);
1353 left_len = (frame_len - 4);
1354
1355 while((psubie = rtw_wnm_non_pref_ch_attr_get(
1356 ptr, &subie_len, left_len)) != NULL) {
1357
1358 /* RTW_WNM_DUMP("notify-req subie :", (psubie + 2), subie_len); */
1359
1360 ptr += (subie_len + 2);
1361 left_len -= (subie_len + 2);
1362
1363 #ifdef CONFIG_RTW_MBO
1364 if (rtw_mbo_wifi_spec_test(padapter) && \
1365 (subie_len > 7)) {
1366
1367 u8 *pload = NULL, *pclass = NULL;
1368 u8 ch_nums = 0;
1369 u8 non_pref_ch = 0;
1370 u8 ch_op_pref = 1, reason = 0;
1371 u32 i, j;
1372
1373 /* nums = sublen - oui_len -
1374 (class_len - op_len - reason_len) */
1375 ch_nums = subie_len - 4 - 3;
1376 if (ch_nums >= RTW_MBO_MAX_CH_LIST_NUM)
1377 ch_nums = RTW_MBO_MAX_CH_LIST_NUM;
1378
1379 /* skip sudid, len, oui */
1380 pclass = (psubie + 6);
1381 ch_op_pref = *(pclass + ch_nums + 1);
1382 reason = *(pclass + ch_nums + 2);
1383 RTW_WNM_INFO("%s : class=0x%02x, operating=0x%02x, "
1384 "reason=0x%02x\n", __func__, *pclass,
1385 ch_op_pref, reason);
1386
1387 for (i = 0; i < ch_nums; i++) {
1388 non_pref_ch = *(pclass + 1 + i);
1389 RTW_WNM_INFO("%s : non-pref ch %u "
1390 "found in notify-req operating 0x%02x\n",
1391 __func__, non_pref_ch, ch_op_pref);
1392
1393 for (j = 0; j < RTW_MAX_NB_RPT_NUM; j++) {
1394 pcant = &puser_raw->btm_cants[j];
1395 if (pcant->nb_rpt.ch_num == non_pref_ch) {
1396 if (ch_op_pref == 0) {
1397 RTW_WNM_INFO("%s : reset "
1398 "preference(%u) for non-preference ch\n",
1399 __func__, pcant->preference);
1400 pcant->preference = 0;
1401 } else {
1402 if (!pcant->preference)
1403 pcant->preference = 64;
1404 }
1405 }
1406
1407 } /* end of for loop RTW_MAX_NB_RPT_NUM */
1408
1409 } /* end of for loop ch_nums*/
1410
1411 }
1412 #endif /* end of CONFIG_RTW_MB*/
1413
1414 } /* end of while loop */
1415
1416 rtw_wnm_issue_action(padapter,
1417 RTW_WLAN_ACTION_WNM_NOTIF_RSP, 0, dialog);
1418
1419 }
1420 #endif /*defined(CONFIG_RTW_WNM) || defined(CONFIG_RTW_80211K) */
1421
1422