xref: /OK3568_Linux_fs/external/rkwifibt/drivers/rtl8852bs/core/rtw_wnm.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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