xref: /OK3568_Linux_fs/external/rkwifibt/drivers/rtl8821cs/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 #include <hal_data.h>
18 
19 #ifndef RTW_WNM_DBG
20 	#define RTW_WNM_DBG	0
21 #endif
22 #if RTW_WNM_DBG
23 	#define RTW_WNM_INFO(fmt, arg...)	\
24 		RTW_INFO(fmt, arg)
25 	#define RTW_WNM_DUMP(str, data, len)	\
26 		RTW_INFO_DUMP(str, data, len)
27 #else
28 	#define RTW_WNM_INFO(fmt, arg...) do {} while (0)
29 	#define RTW_WNM_DUMP(str, data, len) do {} while (0)
30 #endif
31 
32 #ifdef CONFIG_RTW_WNM
33 
34 static u32 wnm_defualt_validity_time = 6000;
35 static u32 wnm_default_disassoc_time = 5000;
36 static u32 wnm_disassoc_wait_time = 500;
37 
38 /* for wifi test, need more validity time to wait scan done */
39 static u32 wnm_ext_validity_time = 4000;
40 
rtw_wmn_btm_cache_update(_adapter * padapter,struct btm_req_hdr * phdr)41 static void rtw_wmn_btm_cache_update(_adapter *padapter, struct btm_req_hdr *phdr)
42 {
43 	struct btm_rpt_cache *pcache = &(padapter->mlmepriv.nb_info.btm_cache);
44 
45 	pcache->dialog_token = phdr->dialog_token;
46 	pcache->req_mode = phdr->req_mode;
47 	pcache->disassoc_timer = le16_to_cpu(phdr->disassoc_timer);
48 
49 	if (phdr->validity_interval  > 0)
50 		pcache->validity_interval = phdr->validity_interval;
51 
52 	pcache->term_duration.id = phdr->term_duration.id;
53 	pcache->term_duration.len = phdr->term_duration.len;
54 	pcache->term_duration.tsf = le64_to_cpu(phdr->term_duration.tsf);
55 	pcache->term_duration.duration =  le16_to_cpu(phdr->term_duration.duration);
56 
57 	RTW_WNM_INFO("%s: req_mode(0x%02x), disassoc_timer(0x%04x), "
58 		"validity_interval(0x%02x %s), tsf(0x%llx), duration(0x%02x)\n",
59 		__func__, pcache->req_mode, pcache->disassoc_timer,
60 		pcache->validity_interval, (!phdr->validity_interval)?"default":"",
61 		pcache->term_duration.tsf,
62 		pcache->term_duration.duration);
63 
64 	if (pcache->validity_interval > 0) {
65 		pcache->validity_time = pcache->validity_interval * 100;
66 	#ifdef CONFIG_RTW_MBO
67 		if (rtw_mbo_wifi_logo_test(padapter))
68 			pcache->validity_time += wnm_ext_validity_time;
69 	#endif
70 	}
71 
72 	if (pcache->disassoc_timer > 0) {
73 		pcache->disassoc_time= pcache->disassoc_timer * 100;
74 	#ifdef CONFIG_RTW_MBO
75 		if (rtw_mbo_wifi_logo_test(padapter))
76 			pcache->disassoc_time += wnm_ext_validity_time;
77 	#endif
78 	}
79 
80 	pcache->req_stime = rtw_get_current_time();
81 
82 	RTW_WNM_INFO("%s: validity_time=%u, disassoc_time=%u\n",
83 		__func__, pcache->validity_time, pcache->disassoc_time);
84 }
85 
rtw_wnm_btm_candidate_validity(struct btm_rpt_cache * pcache,u8 flag)86 static u8 rtw_wnm_btm_candidate_validity(struct btm_rpt_cache *pcache, u8 flag)
87 {
88 	u8 is_validity =_TRUE;
89 	u32 req_validity_time = rtw_get_passing_time_ms(pcache->req_stime);
90 
91 	if ((flag & BIT(0)) && (req_validity_time > pcache->validity_time))
92 		is_validity = _FALSE;
93 
94 	if ((flag & BIT(1)) && (req_validity_time > pcache->disassoc_time))
95 		is_validity = _FALSE;
96 
97 	RTW_WNM_INFO("%s : validity=%u, rtime=%u, vtime=%u. dtime=%u\n",
98 			__func__, is_validity, req_validity_time,
99 			pcache->validity_time, pcache->disassoc_time);
100 	return is_validity;
101 }
102 
rtw_wmn_btm_rsp_reason_decision(_adapter * padapter,u8 * req_mode)103 u8 rtw_wmn_btm_rsp_reason_decision(_adapter *padapter, u8* req_mode)
104 {
105 	struct recv_priv *precvpriv = &padapter->recvpriv;
106 	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
107 	u8 reason = 0;
108 
109 	if (!rtw_wnm_btm_diff_bss(padapter)) {
110 		/* Reject - No suitable BSS transition candidates */
111 		reason = 7;
112 		goto candidate_remove;
113 	}
114 
115 #ifdef CONFIG_RTW_80211R
116 	if (rtw_ft_chk_flags(padapter, RTW_FT_BTM_ROAM)) {
117 		/* Accept */
118 		reason = 0;
119 		goto under_survey;
120 	}
121 #endif
122 
123 	if (((*req_mode) & DISASSOC_IMMINENT) == 0) {
124 		/* Reject - Unspecified reject reason */
125 		reason = 1;
126 		goto candidate_remove;
127 	}
128 
129 	if (precvpriv->signal_strength_data.avg_val >= pmlmepriv->roam_rssi_threshold) {
130 		reason = 1;
131 		RTW_WNM_INFO("%s : Reject - under high roam rssi(%u, %u) \n",
132 			__func__, precvpriv->signal_strength_data.avg_val,
133 			pmlmepriv->roam_rssi_threshold);
134 		goto candidate_remove;
135 	}
136 
137 #ifdef CONFIG_RTW_80211R
138 under_survey:
139 	if (check_fwstate(pmlmepriv, WIFI_UNDER_SURVEY)) {
140 		RTW_WNM_INFO("%s reject due to _FW_UNDER_SURVEY\n", __func__);
141 		reason = 1;
142 	}
143 #endif
144 
145 candidate_remove:
146 	if (reason !=0)
147 		rtw_wnm_reset_btm_candidate(&pmlmepriv->nb_info);
148 
149 	return reason;
150 }
151 
rtw_wnm_btm_candidates_offset_get(u8 * pframe)152 static u32 rtw_wnm_btm_candidates_offset_get(u8* pframe)
153 {
154 	u32 offset = 0;
155 
156 	if (!pframe)
157 		return 0;
158 
159 	offset += 7;
160 
161 	/* BSS Termination Duration check */
162 	if (wnm_btm_bss_term_inc(pframe))
163 		offset += 12;
164 
165 	/* Session Information URL check*/
166 	if (wnm_btm_ess_disassoc_im(pframe)) {
167 		/*URL length field + URL variable length*/
168 		offset = 1 + *(pframe + offset);
169 	}
170 
171 	RTW_WNM_INFO("%s : hdr offset=%u\n", __func__, offset);
172 	return offset;
173 }
174 
rtw_wnm_btm_req_hdr_parsing(u8 * pframe,struct btm_req_hdr * phdr)175 static void rtw_wnm_btm_req_hdr_parsing(u8* pframe, struct btm_req_hdr *phdr)
176 {
177 	u8 *pos;
178 	u32 offset = 0;
179 
180 	if (!pframe || !phdr)
181 		return;
182 
183 	_rtw_memset(phdr, 0, sizeof(struct btm_req_hdr));
184 	phdr->dialog_token = wnm_btm_dialog_token(pframe);
185 	phdr->req_mode  = wnm_btm_req_mode(pframe);
186 	phdr->disassoc_timer = wnm_btm_disassoc_timer(pframe);
187 	phdr->validity_interval = wnm_btm_valid_interval(pframe);
188 	if (wnm_btm_bss_term_inc(pframe)) {
189 		pos = wnm_btm_term_duration_offset(pframe);
190 		if (*pos == WNM_BTM_TERM_DUR_SUBEID) {
191 			phdr->term_duration.id = *pos;
192 			phdr->term_duration.len = *(pos + 1);
193 			phdr->term_duration.tsf = *((u64*)(pos + 2));
194 			phdr->term_duration.duration= *((u16*)(pos + 10));
195 		} else
196 			RTW_WNM_INFO("%s : invaild BSS Termination Duration content!\n", __func__);
197 	}
198 
199 	RTW_WNM_INFO("WNM: req_mode(0x%02x), disassoc_timer(0x%04x), validity_interval(0x%02x)\n",
200 		phdr->req_mode, phdr->disassoc_timer, phdr->validity_interval);
201 	if (wnm_btm_bss_term_inc(pframe))
202 		RTW_WNM_INFO("WNM: tsf(0x%llx), duration(0x%4x)\n",
203 			phdr->term_duration.tsf, phdr->term_duration.duration);
204 }
205 
rtw_wnm_btm_reassoc_req(_adapter * padapter)206 u8 rtw_wnm_btm_reassoc_req(_adapter *padapter)
207 {
208 	struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
209 	struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
210 	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
211 	struct roam_nb_info *pnb = &(pmlmepriv->nb_info);
212 	u8 breassoc = _FALSE;
213 
214 	if (_rtw_memcmp(get_my_bssid(&(pmlmeinfo->network)),
215 		pnb->roam_target_addr, ETH_ALEN)) {
216 		RTW_WNM_INFO("%s : bss "MAC_FMT" found in roam_target "MAC_FMT"\n",
217 			__func__, MAC_ARG(get_my_bssid(&(pmlmeinfo->network))),
218 			MAC_ARG(pnb->roam_target_addr));
219 
220 		breassoc = _TRUE;
221 	}
222 
223 	return breassoc;
224 }
225 
rtw_wnm_roam_scan_hdl(void * ctx)226 void rtw_wnm_roam_scan_hdl(void *ctx)
227 {
228 	_adapter *padapter = (_adapter *)ctx;
229 	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
230 
231 	if (rtw_is_scan_deny(padapter))
232 		RTW_WNM_INFO("%s: roam scan would abort by scan_deny!\n", __func__);
233 
234 #ifdef CONFIG_RTW_80211R
235 	if (rtw_ft_chk_flags(padapter, RTW_FT_BTM_ROAM)) {
236 		pmlmepriv->need_to_roam = _TRUE;
237 		rtw_set_to_roam(padapter, padapter->registrypriv.max_roaming_times);
238 		RTW_WNM_INFO("%s : enable roaming\n", __func__);
239 	}
240 
241 	rtw_drv_scan_by_self(padapter, RTW_AUTO_SCAN_REASON_ROAM);
242 #endif
243 }
244 
rtw_wnm_roam_scan(_adapter * padapter)245 static void rtw_wnm_roam_scan(_adapter *padapter)
246 {
247 	struct roam_nb_info *pnb = &(padapter->mlmepriv.nb_info);
248 
249 	if (rtw_is_scan_deny(padapter)) {
250 		_cancel_timer_ex(&pnb->roam_scan_timer);
251 		_set_timer(&pnb->roam_scan_timer, 1000);
252 	} else
253 		rtw_wnm_roam_scan_hdl((void *)padapter);
254 }
255 
rtw_wnm_disassoc_chk_hdl(void * ctx)256 void rtw_wnm_disassoc_chk_hdl(void *ctx)
257 {
258 	_adapter *padapter = (_adapter *)ctx;
259 	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
260 	struct roam_nb_info *pnb = &(pmlmepriv->nb_info);
261 
262 	RTW_WNM_INFO("%s : expired\n", __func__);
263 	if (pnb->disassoc_waiting <= 0 ) {
264 		RTW_WNM_INFO("%s : btm roam is interrupted by disassoc\n", __func__);
265 		return;
266 	}
267 
268 	pnb->disassoc_waiting = _FALSE;
269 	rtw_wnm_roam_scan(padapter);
270 }
271 
rtw_wnm_try_btm_roam_imnt(_adapter * padapter)272 u8 rtw_wnm_try_btm_roam_imnt(_adapter *padapter)
273 {
274 	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
275 	struct roam_nb_info *pnb = &(pmlmepriv->nb_info);
276 	struct btm_rpt_cache *pcache = &(pnb->btm_cache);
277 	u8 reason = 0, flag = 0;
278 
279 	if (!rtw_wnm_btm_preference_cap(padapter)) {
280 		RTW_WNM_INFO("%s : no btm candidate can be used!\n", __func__);
281 		return 1;
282 	}
283 
284 	flag = BIT(0) | BIT(1);
285 	if (!rtw_wnm_btm_candidate_validity(pcache, flag))
286 		return 1;
287 
288 #ifdef CONFIG_RTW_MBO
289 	if (!rtw_mbo_wifi_logo_test(padapter)
290 		&& !(pcache->req_mode & DISASSOC_IMMINENT)) {
291 		RTW_WNM_INFO("%s : non-disassoc imminet req\n",  __func__);
292 		return 1;
293 	}
294 #endif
295 
296 	RTW_WNM_INFO("%s : disassoc_waiting(%d)\n", __func__, pnb->disassoc_waiting);
297 	if (pnb->disassoc_waiting) {
298 		_cancel_timer_ex(&pnb->disassoc_chk_timer);
299 		pnb->disassoc_waiting = _FALSE;
300 		rtw_wnm_roam_scan_hdl((void *)padapter);
301 	} else if (!pnb->disassoc_waiting)
302 		RTW_WNM_INFO("%s : waiting for btm roaming start/finish\n", __func__);
303 	else
304 		reason = 1;
305 
306 	return reason;
307 }
308 
rtw_wnm_process_btm_req(_adapter * padapter,u8 * pframe,u32 frame_len)309 void rtw_wnm_process_btm_req(_adapter *padapter, u8* pframe, u32 frame_len)
310 {
311 	struct roam_nb_info *pnb = &(padapter->mlmepriv.nb_info);
312 	struct btm_req_hdr req_hdr;
313 	u8 *ptr, reason;
314 	u32 elem_len, offset;
315 
316 	rtw_wnm_btm_req_hdr_parsing(pframe, &req_hdr);
317 	offset = rtw_wnm_btm_candidates_offset_get(pframe);
318 	if (offset == 0)
319 		return;
320 
321 	if ((frame_len - offset) <= 15) {
322 		RTW_INFO("WNM : Reject - no suitable BSS transition candidates!\n");
323 		rtw_wnm_issue_action(padapter,
324 			RTW_WLAN_ACTION_WNM_BTM_RSP, 7, req_hdr.dialog_token);
325 		return;
326 	}
327 
328 	rtw_wmn_btm_cache_update(padapter, &req_hdr);
329 
330 	ptr = (pframe + offset);
331 	elem_len = (frame_len - offset);
332 	rtw_wnm_btm_candidates_survey(padapter, ptr, elem_len, _TRUE);
333 	reason = rtw_wmn_btm_rsp_reason_decision(padapter, &pframe[3]);
334 
335 #ifdef CONFIG_RTW_MBO
336 	/* for wifi-test; AP2 could power-off when BTM-req received */
337 	if ((reason > 0) && (rtw_mbo_wifi_logo_test(padapter))) {
338 		_rtw_memcpy(pnb->roam_target_addr, pnb->nb_rpt[0].bssid, ETH_ALEN);
339 		RTW_WNM_INFO("%s : used report 0 as roam_target_addr(reason=%u)\n",
340 			__func__, reason);
341 		reason = 0;
342 		pnb->preference_en = _TRUE;
343 		pnb->nb_rpt_valid = _FALSE;
344 	}
345 #endif
346 
347 	rtw_wnm_issue_action(padapter,
348 		RTW_WLAN_ACTION_WNM_BTM_RSP, reason, req_hdr.dialog_token);
349 
350 	if (reason == 0) {
351 		pnb->disassoc_waiting = _TRUE;
352 		_set_timer(&pnb->disassoc_chk_timer, wnm_disassoc_wait_time);
353 	}
354 
355 }
356 
rtw_wnm_reset_btm_candidate(struct roam_nb_info * pnb)357 void rtw_wnm_reset_btm_candidate(struct roam_nb_info *pnb)
358 {
359 	pnb->preference_en = _FALSE;
360 	_rtw_memset(pnb->roam_target_addr, 0, ETH_ALEN);
361 }
362 
rtw_wnm_reset_btm_cache(_adapter * padapter)363 void rtw_wnm_reset_btm_cache(_adapter *padapter)
364 {
365 	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
366 	struct roam_nb_info *pnb = &(pmlmepriv->nb_info);
367 	struct btm_rpt_cache *pcache = &(pnb->btm_cache);
368 	u8 flag = 0;
369 
370 	flag |= BIT(0);
371 	if (rtw_wnm_btm_candidate_validity(pcache, flag))
372 		return;
373 
374 	rtw_wnm_reset_btm_candidate(pnb);
375 	_rtw_memset(pcache, 0, sizeof(struct btm_rpt_cache));
376 	pcache->validity_time = wnm_defualt_validity_time;
377 	pcache->disassoc_time= wnm_default_disassoc_time;
378 
379 #ifdef CONFIG_RTW_80211R
380 	if (rtw_ft_chk_flags(padapter, RTW_FT_BTM_ROAM)) {
381 		pmlmepriv->need_to_roam = _FALSE;
382 		rtw_set_to_roam(padapter, 0);
383 		RTW_WNM_INFO("%s : disabled roaming\n", __func__);
384 	}
385 #endif
386 }
387 
rtw_wnm_reset_btm_state(_adapter * padapter)388 void rtw_wnm_reset_btm_state(_adapter *padapter)
389 {
390 	struct roam_nb_info *pnb = &(padapter->mlmepriv.nb_info);
391 
392 	pnb->last_nb_rpt_entries = 0;
393 	pnb->nb_rpt_is_same = _TRUE;
394 	pnb->nb_rpt_valid = _FALSE;
395 	pnb->nb_rpt_ch_list_num = 0;
396 	pnb->disassoc_waiting = -1;
397 	_rtw_memset(&pnb->nb_rpt, 0, sizeof(pnb->nb_rpt));
398 	_rtw_memset(&pnb->nb_rpt_ch_list, 0, sizeof(pnb->nb_rpt_ch_list));
399 	rtw_wnm_reset_btm_cache(padapter);
400 }
401 
rtw_wnm_btm_rsp_candidates_sz_get(_adapter * padapter,u8 * pframe,u32 frame_len)402 u32 rtw_wnm_btm_rsp_candidates_sz_get(
403 	_adapter *padapter, u8* pframe, u32 frame_len)
404 {
405 	u32 num = 0, sz = 0;
406 	u8 status;
407 	u8 *ptr;
408 
409 	if (!pframe || (frame_len <= 5))
410 		goto exit;
411 
412 	status = wnm_btm_rsp_status(pframe);
413 	if (((status != 0) && (status != 6)) || (frame_len < 23))
414 		goto exit;
415 
416 	if (status == 0)
417 		num = (frame_len - 5 - ETH_ALEN)/18;
418 	else
419 		num = (frame_len - 5)/18;
420 	sz = sizeof(struct wnm_btm_cant) * num;
421 exit:
422 	RTW_WNM_INFO("WNM: %u candidates(sz=%u) in BTM rsp\n", num, sz);
423 	return sz;
424 }
425 
rtw_wnm_process_btm_rsp(_adapter * padapter,u8 * pframe,u32 frame_len,struct btm_rsp_hdr * prsp)426 void rtw_wnm_process_btm_rsp(_adapter *padapter,
427 	u8* pframe, u32 frame_len, struct btm_rsp_hdr *prsp)
428 {
429 	prsp->dialog_token = wnm_btm_dialog_token(pframe);
430 	prsp->status = wnm_btm_rsp_status(pframe);
431 	prsp->termination_delay = wnm_btm_rsp_term_delay(pframe);
432 
433 	if ((pframe == NULL) || (frame_len == 0))
434 		return;
435 
436 	prsp->status = *(pframe + 3);
437 	prsp->termination_delay = *(pframe + 4);
438 
439 	/* no Target BSSID & Candidate in frame */
440 	if (frame_len <= 5)
441 		return;
442 
443 	/* accept */
444 	if ((prsp->status == 0) && (frame_len >= 11))
445 		_rtw_memcpy(prsp->bssid, (pframe + 5), ETH_ALEN);
446 
447 	/* STA BSS Transition Candidate List provided,
448 		and at least one NB report exist */
449 	if (((prsp->status == 0) || (prsp->status == 6)) && (frame_len >= 23)) {
450 		struct wnm_btm_cant cant;
451 		u8 *ptr, *pend;
452 		u32 idx = 0;
453 
454 		ptr = pframe + 5;
455 		if (prsp->status == 0)
456 			ptr += ETH_ALEN;
457 
458 		pend = ptr + frame_len;
459 		prsp->candidates_num = 0;
460 		while (ptr < pend) {
461 			if (*ptr != RTW_WLAN_ACTION_WNM_NB_RPT_ELEM)
462 				break;
463 			_rtw_memset(&cant, 0, sizeof(cant));
464 			cant.nb_rpt.id = *ptr;
465 			cant.nb_rpt.len = *(ptr + 1);
466 			_rtw_memcpy(cant.nb_rpt.bssid, (ptr + 2), ETH_ALEN);
467 			cant.nb_rpt.bss_info = *((u32 *)(ptr + 8));
468 			cant.nb_rpt.reg_class = *(ptr + 12);
469 			cant.nb_rpt.ch_num = *(ptr + 13);
470 			cant.nb_rpt.phy_type= *(ptr + 14);
471 
472 			if (*(ptr + 15) == WNM_BTM_CAND_PREF_SUBEID)
473 				cant.preference = *(ptr + 17);
474 			ptr = ptr + cant.nb_rpt.len + 2;
475 			if (prsp->pcandidates) {
476 				prsp->candidates_num++;
477 				_rtw_memcpy((prsp->pcandidates + sizeof(cant) * idx), &cant, sizeof(cant));
478 			}
479 
480 			idx++;
481 			RTW_WNM_INFO("WNM: btm rsp candidate bssid("MAC_FMT
482 				") ,bss_info(0x%04X), reg_class(0x%02X), ch(%d),"
483 				" phy_type(0x%02X), preference(0x%02X)\n",
484 				MAC_ARG(cant.nb_rpt.bssid), cant.nb_rpt.bss_info,
485 				cant.nb_rpt.reg_class, cant.nb_rpt.ch_num,
486 				cant.nb_rpt.phy_type, cant.preference);
487 			if ((prsp->pcandidates) && (prsp->candidates_num > 0))
488 				RTW_WNM_DUMP("WNM candidates: ", prsp->pcandidates,
489 						(sizeof(struct wnm_btm_cant) * prsp->candidates_num));
490 		}
491 	}
492 
493 }
494 
rtw_wnm_hdr_init(_adapter * padapter,struct xmit_frame * pactionframe,u8 * pmac,u8 action,u8 ** pcontent)495 void rtw_wnm_hdr_init(_adapter *padapter,
496 	struct xmit_frame *pactionframe, u8 *pmac,
497 	u8 action, u8 **pcontent)
498 {
499 	struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
500 	struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
501 	struct pkt_attrib *pattrib;
502 	struct rtw_ieee80211_hdr *pwlanhdr;
503 	u16 *pfctrl;
504 	u8 category;
505 
506 	pattrib = &(pactionframe->attrib);
507 	update_mgntframe_attrib(padapter, pattrib);
508 	_rtw_memset(pactionframe->buf_addr, 0, (WLANHDR_OFFSET + TXDESC_OFFSET));
509 
510 	*pcontent = (u8 *)(pactionframe->buf_addr + TXDESC_OFFSET);
511 	pwlanhdr = (struct rtw_ieee80211_hdr *)(*pcontent);
512 	pfctrl = &(pwlanhdr->frame_ctl);
513 	*(pfctrl) = 0;
514 
515 	_rtw_memcpy(pwlanhdr->addr1, pmac, ETH_ALEN);
516 	_rtw_memcpy(pwlanhdr->addr2, adapter_mac_addr(padapter), ETH_ALEN);
517 	_rtw_memcpy(pwlanhdr->addr3, get_my_bssid(&pmlmeinfo->network), ETH_ALEN);
518 
519 	SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
520 	pmlmeext->mgnt_seq++;
521 	set_frame_sub_type(*pcontent, WIFI_ACTION);
522 
523 	*pcontent += sizeof(struct rtw_ieee80211_hdr_3addr);
524 	pattrib->pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
525 
526 	category = RTW_WLAN_CATEGORY_WNM;
527 	*pcontent = rtw_set_fixed_ie(*pcontent, 1, &(category), &(pattrib->pktlen));
528 	*pcontent = rtw_set_fixed_ie(*pcontent, 1, &(action), &(pattrib->pktlen));
529 }
530 
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)531 void rtw_wnm_build_btm_req_ies(_adapter *padapter,
532 	u8 **pframe, struct pkt_attrib *pattrib,
533 	struct btm_req_hdr *phdr, u8 *purl, u32 url_len,
534 	u8 *pcandidates, u8 candidate_cnt)
535 {
536 	int i;
537 
538 	*pframe = rtw_set_fixed_ie(*pframe, 1,
539 				&phdr->dialog_token, &(pattrib->pktlen));
540 	*pframe = rtw_set_fixed_ie(*pframe, 1,
541 				&phdr->req_mode, &(pattrib->pktlen));
542 	*pframe = rtw_set_fixed_ie(*pframe, 2,
543 				(u8 *)&phdr->disassoc_timer, &(pattrib->pktlen));
544 	*pframe = rtw_set_fixed_ie(*pframe, 1,
545 				&phdr->validity_interval, &(pattrib->pktlen));
546 
547 	if (phdr->req_mode & BSS_TERMINATION_INCLUDED) {
548 		*pframe = rtw_set_fixed_ie(*pframe, 1,
549 					&phdr->term_duration.id, &(pattrib->pktlen));
550 		*pframe = rtw_set_fixed_ie(*pframe, 1,
551 					&phdr->term_duration.len, &(pattrib->pktlen));
552 		*pframe = rtw_set_fixed_ie(*pframe, 8,
553 					(u8 *)&phdr->term_duration.tsf, &(pattrib->pktlen));
554 		*pframe = rtw_set_fixed_ie(*pframe, 2,
555 					(u8 *)&phdr->term_duration.duration, &(pattrib->pktlen));
556 	}
557 
558 	if ((purl != NULL) && (url_len > 0) &&
559 		(phdr->req_mode & ESS_DISASSOC_IMMINENT)) {
560 		*pframe = rtw_set_fixed_ie(*pframe, 1,
561 					(u8 *)&url_len, &(pattrib->pktlen));
562 		*pframe = rtw_set_fixed_ie(*pframe,
563 					url_len, purl, &(pattrib->pktlen));
564 	}
565 
566 	if ((pcandidates != NULL) && (candidate_cnt > 0)) {
567 		for (i=0; i<candidate_cnt; i++) {
568 			struct wnm_btm_cant *pcandidate = \
569 				((struct wnm_btm_cant *)pcandidates) + i;
570 			struct nb_rpt_hdr *prpt = &(pcandidate->nb_rpt);
571 
572 			*pframe = rtw_set_fixed_ie(*pframe, 1,
573 						&pcandidate->nb_rpt.id, &(pattrib->pktlen));
574 			*pframe = rtw_set_fixed_ie(*pframe, 1,
575 						&pcandidate->nb_rpt.len, &(pattrib->pktlen));
576 			*pframe = rtw_set_fixed_ie(*pframe, ETH_ALEN,
577 						pcandidate->nb_rpt.bssid, &(pattrib->pktlen));
578 			*pframe = rtw_set_fixed_ie(*pframe, 4,
579 						(u8 *)&pcandidate->nb_rpt.bss_info, &(pattrib->pktlen));
580 			*pframe = rtw_set_fixed_ie(*pframe, 1,
581 						&pcandidate->nb_rpt.reg_class, &(pattrib->pktlen));
582 			*pframe = rtw_set_fixed_ie(*pframe, 1,
583 						&pcandidate->nb_rpt.ch_num, &(pattrib->pktlen));
584 			*pframe = rtw_set_fixed_ie(*pframe, 1,
585 						&pcandidate->nb_rpt.phy_type, &(pattrib->pktlen));
586 			*pframe = rtw_set_ie(*pframe, WNM_BTM_CAND_PREF_SUBEID, 1,
587 					(u8 *)&pcandidate->preference, &(pattrib->pktlen));
588 		}
589 	}
590 
591 }
592 
rtw_wnm_issue_btm_req(_adapter * padapter,u8 * pmac,struct btm_req_hdr * phdr,u8 * purl,u32 url_len,u8 * pcandidates,u8 candidate_cnt)593 void rtw_wnm_issue_btm_req(_adapter *padapter,
594 	u8 *pmac, struct btm_req_hdr *phdr, u8 *purl, u32 url_len,
595 	u8 *pcandidates, u8 candidate_cnt)
596 {
597 	struct roam_nb_info *pnb = &(padapter->mlmepriv.nb_info);
598 	struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
599 	struct xmit_frame *pmgntframe;
600 	struct pkt_attrib *pattrib;
601 	u8 action, *pframe, dialog_token = 0;
602 
603 	if (!pmac || is_zero_mac_addr(pmac)
604 		|| is_broadcast_mac_addr(pmac))
605 		return ;
606 
607 	if ((pmgntframe = alloc_mgtxmitframe(pxmitpriv)) == NULL)
608 		return ;
609 
610 	rtw_wnm_hdr_init(padapter, pmgntframe, pmac,
611 		RTW_WLAN_ACTION_WNM_BTM_REQ, &pframe);
612 
613 	pattrib = &(pmgntframe->attrib);
614 	rtw_wnm_build_btm_req_ies(padapter, &pframe, pattrib,
615 		phdr, purl, url_len, pcandidates, candidate_cnt);
616 
617 	if (0) {
618 		u8 *__p =  (u8 *)(pmgntframe->buf_addr + TXDESC_OFFSET);
619 		RTW_WNM_DUMP("WNM BTM REQ :", __p, pattrib->pktlen);
620 	}
621 
622 	pattrib->last_txcmdsz = pattrib->pktlen;
623 	dump_mgntframe(padapter, pmgntframe);
624 	RTW_INFO("WNM: BSS Transition Management Request sent\n");
625 }
626 
rtw_wnm_issue_action(_adapter * padapter,u8 action,u8 reason,u8 dialog)627 void rtw_wnm_issue_action(_adapter *padapter,
628 	u8 action, u8 reason, u8 dialog)
629 {
630 	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
631 	struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
632 	struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
633 	struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
634 	struct xmit_frame *pmgntframe;
635 	struct rtw_ieee80211_hdr *pwlanhdr;
636 	struct pkt_attrib *pattrib;
637 	u8 category, termination_delay, *pframe, dialog_token = 0;
638 #ifdef CONFIG_RTW_MBO
639 	u8 mbo_trans_rej_res = 1;  /* Unspecified reason */
640 	u8 mbo_notif_req_type ;
641 #endif
642 	u16 *fctrl;
643 
644 	if ((pmgntframe = alloc_mgtxmitframe(pxmitpriv)) == NULL)
645 		return ;
646 
647 	pattrib = &(pmgntframe->attrib);
648 	update_mgntframe_attrib(padapter, pattrib);
649 	_rtw_memset(pmgntframe->buf_addr, 0, (WLANHDR_OFFSET + TXDESC_OFFSET));
650 
651 	pframe = (u8 *)(pmgntframe->buf_addr + TXDESC_OFFSET);
652 	pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
653 
654 	fctrl = &(pwlanhdr->frame_ctl);
655 	*(fctrl) = 0;
656 
657 	_rtw_memcpy(pwlanhdr->addr1, get_my_bssid(&pmlmeinfo->network), ETH_ALEN);
658 	_rtw_memcpy(pwlanhdr->addr2, adapter_mac_addr(padapter), ETH_ALEN);
659 	_rtw_memcpy(pwlanhdr->addr3, get_my_bssid(&pmlmeinfo->network), ETH_ALEN);
660 
661 	SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
662 	pmlmeext->mgnt_seq++;
663 	set_frame_sub_type(pframe, WIFI_ACTION);
664 
665 	pframe += sizeof(struct rtw_ieee80211_hdr_3addr);
666 	pattrib->pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
667 
668 	category = RTW_WLAN_CATEGORY_WNM;
669 	pframe = rtw_set_fixed_ie(pframe, 1, &(category), &(pattrib->pktlen));
670 	pframe = rtw_set_fixed_ie(pframe, 1, &(action), &(pattrib->pktlen));
671 
672 	switch (action) {
673 		case RTW_WLAN_ACTION_WNM_BTM_QUERY:
674 			dialog_token++;
675 			pframe = rtw_set_fixed_ie(pframe, 1, &(dialog_token), &(pattrib->pktlen));
676 			pframe = rtw_set_fixed_ie(pframe, 1, &(reason), &(pattrib->pktlen));
677 			RTW_INFO("WNM: BSS Transition Management Query sent\n");
678 			break;
679 		case RTW_WLAN_ACTION_WNM_BTM_RSP:
680 			dialog_token = dialog;
681 			termination_delay = 0;
682 			pframe = rtw_set_fixed_ie(pframe, 1, &(dialog_token), &(pattrib->pktlen));
683 			pframe = rtw_set_fixed_ie(pframe, 1, &(reason), &(pattrib->pktlen));
684 			pframe = rtw_set_fixed_ie(pframe, 1, &(termination_delay), &(pattrib->pktlen));
685 			if (!reason && !is_zero_mac_addr(pmlmepriv->nb_info.roam_target_addr)) {
686 				pframe = rtw_set_fixed_ie(pframe, 6,
687 					pmlmepriv->nb_info.roam_target_addr, &(pattrib->pktlen));
688 			}
689 
690 #ifdef CONFIG_RTW_MBO
691 			rtw_mbo_build_trans_reject_reason_attr(padapter,
692 				&pframe, pattrib, &mbo_trans_rej_res);
693 #endif
694 
695 			RTW_INFO("WNM: BSS Transition Management Response sent(reason:%d)\n", reason);
696 			break;
697 		case RTW_WLAN_ACTION_WNM_NOTIF_REQ:
698 #ifdef CONFIG_RTW_MBO
699 			dialog_token++;
700 			mbo_notif_req_type = WLAN_EID_VENDOR_SPECIFIC;
701 			pframe = rtw_set_fixed_ie(pframe, 1, &(dialog_token), &(pattrib->pktlen));
702 			pframe = rtw_set_fixed_ie(pframe, 1, &(mbo_notif_req_type), &(pattrib->pktlen));
703 			rtw_mbo_build_wnm_notification(padapter, &pframe, pattrib);
704 			RTW_INFO("WNM: Notification request sent\n");
705 #endif
706 			break;
707 		default:
708 			goto exit;
709 	}
710 
711 	pattrib->last_txcmdsz = pattrib->pktlen;
712 	dump_mgntframe(padapter, pmgntframe);
713 
714 exit:
715 	return;
716 }
717 
718 /* argument req_ie@cfg80211_roamed()/cfg80211_connect_result()
719 	is association request IEs format. if driver used reassoc-req format,
720 	RSN IE could not be parsed @supplicant process */
rtw_wnm_update_reassoc_req_ie(_adapter * padapter)721 void rtw_wnm_update_reassoc_req_ie(_adapter *padapter)
722 {
723 	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
724 	u32 dup_len, offset;
725 	u8 *pdup;
726 
727 	if (!pmlmepriv->assoc_req || !pmlmepriv->assoc_req_len)
728 		return;
729 
730 	/* total len is assoc req len without Current AP Field*/
731 	dup_len = pmlmepriv->assoc_req_len - ETH_ALEN;
732 
733 	/* offset is a len of 80211 header +  capability(2B) + listen interval(2B) */
734 	offset =  sizeof(struct rtw_ieee80211_hdr_3addr) + 4;
735 
736 	pdup = rtw_zmalloc(dup_len);
737 	if (pdup) {
738 		/* remove Current AP Field @reassoc req IE */
739 		_rtw_memcpy(pdup, pmlmepriv->assoc_req, offset);
740 		_rtw_memcpy(pdup + offset, pmlmepriv->assoc_req + offset + ETH_ALEN,
741 				pmlmepriv->assoc_req_len - offset);
742 		rtw_buf_update(&pmlmepriv->assoc_req,
743 			&pmlmepriv->assoc_req_len, pdup, dup_len);
744 		rtw_mfree(pdup, dup_len);
745 	}
746 }
747 #endif /* CONFIG_RTW_WNM */
748 
749 #if defined(CONFIG_RTW_WNM) || defined(CONFIG_RTW_80211K)
rtw_roam_nb_info_init(_adapter * padapter)750 void rtw_roam_nb_info_init(_adapter *padapter)
751 {
752 	struct roam_nb_info *pnb = &(padapter->mlmepriv.nb_info);
753 	struct btm_rpt_cache *pcache = &(pnb->btm_cache);
754 
755 	_rtw_memset(&pnb->nb_rpt, 0, sizeof(pnb->nb_rpt));
756 	_rtw_memset(&pnb->nb_rpt_ch_list, 0, sizeof(pnb->nb_rpt_ch_list));
757 	_rtw_memset(&pnb->roam_target_addr, 0, ETH_ALEN);
758 	pnb->nb_rpt_valid = _FALSE;
759 	pnb->nb_rpt_ch_list_num = 0;
760 	pnb->preference_en = _FALSE;
761 	pnb->nb_rpt_is_same = _TRUE;
762 	pnb->last_nb_rpt_entries = 0;
763 	pnb->disassoc_waiting = -1;
764 #ifdef CONFIG_RTW_WNM
765 	pnb->features = 0;
766 	/* pnb->features |= RTW_WNM_FEATURE_BTM_REQ_EN; */
767 
768 #ifdef CONFIG_PLATFORM_CMAP_INTFS
769 	pnb->features |= RTW_WNM_FEATURE_BTM_REQ_EN;
770 #endif
771 
772 	rtw_init_timer(&pnb->roam_scan_timer,
773 		padapter, rtw_wnm_roam_scan_hdl,
774 		padapter);
775 	rtw_init_timer(&pnb->disassoc_chk_timer,
776 		padapter, rtw_wnm_disassoc_chk_hdl,
777 		padapter);
778 
779 	_rtw_memset(pcache, 0, sizeof(struct btm_rpt_cache));
780 	pcache->validity_time = wnm_defualt_validity_time;
781 	pcache->disassoc_time= wnm_default_disassoc_time ;
782 #endif
783 }
784 
rtw_roam_nb_scan_list_set(_adapter * padapter,struct sitesurvey_parm * pparm)785 u8 rtw_roam_nb_scan_list_set(
786 	_adapter *padapter, struct sitesurvey_parm *pparm)
787 {
788 	u8 ret = _FALSE;
789 	u32 i;
790 	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
791 	struct roam_nb_info *pnb = &(pmlmepriv->nb_info);
792 
793 #ifdef CONFIG_RTW_80211R
794 	if (!rtw_chk_roam_flags(padapter, RTW_ROAM_ACTIVE)
795 		&& !rtw_ft_chk_flags(padapter, RTW_FT_BTM_ROAM))
796 		return ret;
797 #endif
798 
799 	if (!pmlmepriv->need_to_roam)
800 		return ret;
801 
802 	if ((!pmlmepriv->nb_info.nb_rpt_valid) || (!pnb->nb_rpt_ch_list_num))
803 		return ret;
804 
805 	if (!pparm)
806 		return ret;
807 
808 	rtw_init_sitesurvey_parm(padapter, pparm);
809 	if (rtw_roam_busy_scan(padapter, pnb)) {
810 		pparm->ch_num = 1;
811 		pparm->ch[pmlmepriv->ch_cnt].hw_value =
812 			pnb->nb_rpt_ch_list[pmlmepriv->ch_cnt].hw_value;
813 		pmlmepriv->ch_cnt++;
814 		ret = _TRUE;
815 
816 		RTW_WNM_INFO("%s: ch_cnt=%u, (%u)hw_value=%u\n",
817 			__func__, pparm->ch_num, pmlmepriv->ch_cnt,
818 			pparm->ch[pmlmepriv->ch_cnt].hw_value);
819 
820 		if (pmlmepriv->ch_cnt == pnb->nb_rpt_ch_list_num) {
821 			pmlmepriv->nb_info.nb_rpt_valid = _FALSE;
822 			pmlmepriv->ch_cnt = 0;
823 		}
824 		goto set_bssid_list;
825 	}
826 
827 	pparm->ch_num = (pnb->nb_rpt_ch_list_num > RTW_CHANNEL_SCAN_AMOUNT)?
828 		(RTW_CHANNEL_SCAN_AMOUNT):(pnb->nb_rpt_ch_list_num);
829 	for (i=0; i<pparm->ch_num; i++) {
830 		pparm->ch[i].hw_value = pnb->nb_rpt_ch_list[i].hw_value;
831 		pparm->ch[i].flags = RTW_IEEE80211_CHAN_PASSIVE_SCAN;
832 	}
833 
834 	pmlmepriv->nb_info.nb_rpt_valid = _FALSE;
835 	pmlmepriv->ch_cnt = 0;
836 	ret = _TRUE;
837 
838 set_bssid_list:
839 	rtw_set_802_11_bssid_list_scan(padapter, pparm);
840 	return ret;
841 }
842 
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)843 static u8 rtw_wnm_nb_elem_parsing(
844 	u8* pdata, u32 data_len, u8 from_btm,
845 	u32 *nb_rpt_num, u8 *nb_rpt_is_same,
846 	struct roam_nb_info *pnb, struct wnm_btm_cant *pcandidates)
847 {
848 	u8 bfound = _FALSE, ret = _SUCCESS;
849 	u8 *ptr, *pend, *op;
850 	u32 elem_len, subelem_len, op_len;
851 	u32 i, nb_rpt_entries = 0;
852 	struct nb_rpt_hdr *pie;
853 	struct wnm_btm_cant *pcandidate;
854 
855 	if ((!pdata) || (!pnb))
856 		return _FAIL;
857 
858 	if ((from_btm) && (!pcandidates))
859 		return _FAIL;
860 
861 	ptr = pdata;
862 	pend = ptr + data_len;
863 	elem_len = data_len;
864 	subelem_len = (u32)*(pdata+1);
865 
866 	for (i=0; i < RTW_MAX_NB_RPT_NUM; i++) {
867 		if (((ptr + 7) > pend) || (elem_len < subelem_len))
868 			break;
869 
870 		if (*ptr != RTW_WLAN_ACTION_WNM_NB_RPT_ELEM) {
871 			RTW_WNM_INFO("WNM: end of data(0x%2x)!\n", *ptr);
872 			break;
873 		}
874 
875 		pie = (struct nb_rpt_hdr *)ptr;
876 		if (from_btm) {
877 			op = rtw_get_ie((u8 *)(ptr+15),
878 				WNM_BTM_CAND_PREF_SUBEID,
879 				&op_len, (subelem_len - 15));
880 		}
881 
882 		ptr = (u8 *)(ptr + subelem_len + 2);
883 		elem_len -= (subelem_len +2);
884 		subelem_len = *(ptr+1);
885 		if (from_btm) {
886 			pcandidate = (pcandidates + i);
887 			_rtw_memcpy(&pcandidate->nb_rpt, pie, sizeof(struct nb_rpt_hdr));
888 			if (op && (op_len !=0)) {
889 				pcandidate->preference = *(op + 2);
890 				bfound = _TRUE;
891 			} else
892 				pcandidate->preference = 0;
893 
894 			RTW_WNM_INFO("WNM: preference check bssid("MAC_FMT
895 				") ,bss_info(0x%04X), reg_class(0x%02X), ch(%d),"
896 				" phy_type(0x%02X), preference(0x%02X)\n",
897 				MAC_ARG(pcandidate->nb_rpt.bssid), pcandidate->nb_rpt.bss_info,
898 				pcandidate->nb_rpt.reg_class, pcandidate->nb_rpt.ch_num,
899 				pcandidate->nb_rpt.phy_type, pcandidate->preference);
900 		} else {
901 			if (_rtw_memcmp(&pnb->nb_rpt[i], pie, sizeof(struct nb_rpt_hdr)) == _FALSE)
902 				*nb_rpt_is_same = _FALSE;
903 			_rtw_memcpy(&pnb->nb_rpt[i], pie, sizeof(struct nb_rpt_hdr));
904 		}
905 		nb_rpt_entries++;
906 	}
907 
908 	if (from_btm)
909 		pnb->preference_en = (bfound)?_TRUE:_FALSE;
910 
911 	*nb_rpt_num = nb_rpt_entries;
912 	return ret;
913 }
914 
915 /* selection sorting based on preference value
916  * IN : 		nb_rpt_entries - candidate num
917  * IN/OUT :	pcandidates	- candidate list
918  * return : TRUE - means pcandidates is updated.
919  */
rtw_wnm_candidates_sorting(u32 nb_rpt_entries,struct wnm_btm_cant * pcandidates)920 static u8 rtw_wnm_candidates_sorting(
921 	u32 nb_rpt_entries, struct wnm_btm_cant *pcandidates)
922 {
923 	u8 updated = _FALSE;
924 	u32 i, j, pos;
925 	struct wnm_btm_cant swap;
926 	struct wnm_btm_cant *pcant_1, *pcant_2;
927 
928 	if ((!nb_rpt_entries) || (!pcandidates))
929 		return updated;
930 
931 	for (i=0; i < (nb_rpt_entries - 1); i++) {
932 		pos = i;
933 		for (j=(i + 1); j < nb_rpt_entries; j++) {
934 			pcant_1 = pcandidates+pos;
935 			pcant_2 = pcandidates+j;
936 			if ((pcant_1->preference) < (pcant_2->preference))
937 				pos = j;
938 		}
939 
940 		if (pos != i) {
941 			updated = _TRUE;
942 			_rtw_memcpy(&swap, (pcandidates+i), sizeof(struct wnm_btm_cant));
943 			_rtw_memcpy((pcandidates+i), (pcandidates+pos), sizeof(struct wnm_btm_cant));
944 			_rtw_memcpy((pcandidates+pos), &swap, sizeof(struct wnm_btm_cant));
945 		}
946 	}
947 	return updated;
948 }
949 
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)950 static void rtw_wnm_nb_info_update(
951 	u32 nb_rpt_entries, u8 from_btm,
952 	struct roam_nb_info *pnb, struct wnm_btm_cant *pcandidates,
953 	u8 *nb_rpt_is_same)
954 {
955 	u8 is_found;
956 	u32 i, j;
957 	struct wnm_btm_cant *pcand;
958 
959 	if (!pnb)
960 		return;
961 
962 	pnb->nb_rpt_ch_list_num = 0;
963 	for (i=0; i<nb_rpt_entries; i++) {
964 		is_found = _FALSE;
965 		if (from_btm) {
966 			pcand = (pcandidates+i);
967 			if (_rtw_memcmp(&pnb->nb_rpt[i], &pcand->nb_rpt,
968 					sizeof(struct nb_rpt_hdr)) == _FALSE)
969 				*nb_rpt_is_same = _FALSE;
970 			_rtw_memcpy(&pnb->nb_rpt[i], &pcand->nb_rpt, sizeof(struct nb_rpt_hdr));
971 		}
972 
973 		RTW_WNM_INFO("WNM: bssid(" MAC_FMT
974 			") , bss_info(0x%04X), reg_class(0x%02X), ch_num(%d), phy_type(0x%02X)\n",
975 			MAC_ARG(pnb->nb_rpt[i].bssid), pnb->nb_rpt[i].bss_info,
976 			pnb->nb_rpt[i].reg_class, pnb->nb_rpt[i].ch_num,
977 			pnb->nb_rpt[i].phy_type);
978 
979 		if (pnb->nb_rpt[i].ch_num == 0)
980 			continue;
981 
982 		for (j=0; j<nb_rpt_entries; j++) {
983 			if (pnb->nb_rpt[i].ch_num == pnb->nb_rpt_ch_list[j].hw_value) {
984 				is_found = _TRUE;
985 				break;
986 			}
987 		}
988 
989 		if (!is_found) {
990 			pnb->nb_rpt_ch_list[pnb->nb_rpt_ch_list_num].hw_value = pnb->nb_rpt[i].ch_num;
991 				pnb->nb_rpt_ch_list_num++;
992 		}
993 	}
994 }
995 
rtw_wnm_btm_candidate_select(_adapter * padapter)996 static void rtw_wnm_btm_candidate_select(_adapter *padapter)
997 {
998 	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
999 	struct roam_nb_info *pnb = &(padapter->mlmepriv.nb_info);
1000 	struct wlan_network *pnetwork;
1001 	u8 bfound = _FALSE;
1002 	u8 ignore_currrent = _FALSE;
1003 	u32 i;
1004 
1005 #ifdef CONFIG_RTW_80211R
1006 	if (rtw_ft_chk_flags(padapter, RTW_FT_BTM_ROAM))
1007 		ignore_currrent = _TRUE;
1008 #endif
1009 
1010 	for (i = 0; i < pnb->last_nb_rpt_entries; i++) {
1011 		if (ignore_currrent &&
1012 			(_rtw_memcmp(pnb->nb_rpt[i].bssid,\
1013 				padapter->mlmepriv.cur_network.network.MacAddress, ETH_ALEN))) {
1014 			RTW_WNM_INFO("WNM : ignore candidate "MAC_FMT" for it's connected(%u)!\n",
1015 					MAC_ARG(pnb->nb_rpt[i].bssid), i);
1016 			continue;
1017 		}
1018 
1019 		pnetwork = rtw_find_network(
1020 				&(pmlmepriv->scanned_queue),
1021 				pnb->nb_rpt[i].bssid);
1022 
1023 		if (pnetwork) {
1024 			bfound = _TRUE;
1025 			break;
1026 		}
1027 	}
1028 
1029 	if (bfound) {
1030 		_rtw_memcpy(pnb->roam_target_addr, pnb->nb_rpt[i].bssid, ETH_ALEN);
1031 		RTW_INFO("WNM : select btm entry(%d) - %s("MAC_FMT", ch:%u) rssi:%d\n"
1032 			, i
1033 			, pnetwork->network.Ssid.Ssid
1034 			, MAC_ARG(pnetwork->network.MacAddress)
1035 			, pnetwork->network.Configuration.DSConfig
1036 			, (int)pnetwork->network.Rssi);
1037 	} else
1038 		_rtw_memset(pnb->roam_target_addr,0, ETH_ALEN);
1039 }
1040 
rtw_wnm_btm_candidates_survey(_adapter * padapter,u8 * pframe,u32 elem_len,u8 from_btm)1041 u32 rtw_wnm_btm_candidates_survey(
1042 	_adapter *padapter, u8* pframe, u32 elem_len, u8 from_btm)
1043 {
1044 	struct roam_nb_info *pnb = &(padapter->mlmepriv.nb_info);
1045 	struct wnm_btm_cant *pcandidate_list = NULL;
1046 	u8 nb_rpt_is_same = _TRUE;
1047 	u32	ret = _FAIL;
1048 	u32 nb_rpt_entries = 0;
1049 
1050 	if (from_btm) {
1051 		u32 mlen = sizeof(struct wnm_btm_cant) * RTW_MAX_NB_RPT_NUM;
1052 		pcandidate_list = (struct wnm_btm_cant *)rtw_malloc(mlen);
1053 		if (pcandidate_list == NULL)
1054 			goto exit;
1055 	}
1056 
1057 	/*clean the status set last time*/
1058 	_rtw_memset(&pnb->nb_rpt_ch_list, 0, sizeof(pnb->nb_rpt_ch_list));
1059 	pnb->nb_rpt_valid = _FALSE;
1060 	if (!rtw_wnm_nb_elem_parsing(
1061 			pframe, elem_len, from_btm,
1062 			&nb_rpt_entries, &nb_rpt_is_same,
1063 			pnb, pcandidate_list))
1064 		goto exit;
1065 
1066 	if (nb_rpt_entries != 0) {
1067 		if ((from_btm) && (rtw_wnm_btm_preference_cap(padapter)))
1068 			rtw_wnm_candidates_sorting(nb_rpt_entries, pcandidate_list);
1069 
1070 		rtw_wnm_nb_info_update(
1071 			nb_rpt_entries, from_btm,
1072 			pnb, pcandidate_list, &nb_rpt_is_same);
1073 	}
1074 
1075 	RTW_WNM_INFO("nb_rpt_is_same = %d, nb_rpt_entries = %d, last_nb_rpt_entries = %d\n",
1076 		nb_rpt_is_same, nb_rpt_entries, pnb->last_nb_rpt_entries);
1077 	if ((nb_rpt_is_same == _TRUE) && (nb_rpt_entries == pnb->last_nb_rpt_entries))
1078 		pnb->nb_rpt_is_same = _TRUE;
1079 	else {
1080 		pnb->nb_rpt_is_same = _FALSE;
1081 		pnb->last_nb_rpt_entries = nb_rpt_entries;
1082 	}
1083 
1084 	if ((from_btm) && (nb_rpt_entries != 0))
1085 		rtw_wnm_btm_candidate_select(padapter);
1086 
1087 	pnb->nb_rpt_valid = _TRUE;
1088 	ret = _SUCCESS;
1089 
1090 exit:
1091 	if (from_btm && pcandidate_list)
1092 		rtw_mfree((u8 *)pcandidate_list, sizeof(struct wnm_btm_cant) * RTW_MAX_NB_RPT_NUM);
1093 
1094 	return ret;
1095 }
1096 
1097 #endif /*defined(CONFIG_RTW_WNM) || defined(CONFIG_RTW_80211K) */
1098 
1099