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_issue_action(_adapter * padapter,u8 action,u8 reason,u8 dialog)402 void rtw_wnm_issue_action(_adapter *padapter, u8 action, u8 reason, u8 dialog)
403 {
404 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
405 struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
406 struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
407 struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
408 struct xmit_frame *pmgntframe;
409 struct rtw_ieee80211_hdr *pwlanhdr;
410 struct pkt_attrib *pattrib;
411 u8 category, termination_delay, *pframe, dialog_token = 0;
412 #ifdef CONFIG_RTW_MBO
413 u8 mbo_trans_rej_res = 1; /* Unspecified reason */
414 u8 mbo_notif_req_type ;
415 #endif
416 u16 *fctrl;
417
418 if ((pmgntframe = alloc_mgtxmitframe(pxmitpriv)) == NULL)
419 return ;
420
421 pattrib = &(pmgntframe->attrib);
422 update_mgntframe_attrib(padapter, pattrib);
423 _rtw_memset(pmgntframe->buf_addr, 0, (WLANHDR_OFFSET + TXDESC_OFFSET));
424
425 pframe = (u8 *)(pmgntframe->buf_addr + TXDESC_OFFSET);
426 pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
427
428 fctrl = &(pwlanhdr->frame_ctl);
429 *(fctrl) = 0;
430
431 _rtw_memcpy(pwlanhdr->addr1, get_my_bssid(&pmlmeinfo->network), ETH_ALEN);
432 _rtw_memcpy(pwlanhdr->addr2, adapter_mac_addr(padapter), ETH_ALEN);
433 _rtw_memcpy(pwlanhdr->addr3, get_my_bssid(&pmlmeinfo->network), ETH_ALEN);
434
435 SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
436 pmlmeext->mgnt_seq++;
437 set_frame_sub_type(pframe, WIFI_ACTION);
438
439 pframe += sizeof(struct rtw_ieee80211_hdr_3addr);
440 pattrib->pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
441
442 category = RTW_WLAN_CATEGORY_WNM;
443 pframe = rtw_set_fixed_ie(pframe, 1, &(category), &(pattrib->pktlen));
444 pframe = rtw_set_fixed_ie(pframe, 1, &(action), &(pattrib->pktlen));
445
446 switch (action) {
447 case RTW_WLAN_ACTION_WNM_BTM_QUERY:
448 dialog_token++;
449 pframe = rtw_set_fixed_ie(pframe, 1, &(dialog_token), &(pattrib->pktlen));
450 pframe = rtw_set_fixed_ie(pframe, 1, &(reason), &(pattrib->pktlen));
451 RTW_INFO("WNM: BSS Transition Management Query sent\n");
452 break;
453 case RTW_WLAN_ACTION_WNM_BTM_RSP:
454 dialog_token = dialog;
455 termination_delay = 0;
456 pframe = rtw_set_fixed_ie(pframe, 1, &(dialog_token), &(pattrib->pktlen));
457 pframe = rtw_set_fixed_ie(pframe, 1, &(reason), &(pattrib->pktlen));
458 pframe = rtw_set_fixed_ie(pframe, 1, &(termination_delay), &(pattrib->pktlen));
459 if (!reason && !is_zero_mac_addr(pmlmepriv->nb_info.roam_target_addr)) {
460 pframe = rtw_set_fixed_ie(pframe, 6,
461 pmlmepriv->nb_info.roam_target_addr, &(pattrib->pktlen));
462 }
463
464 #ifdef CONFIG_RTW_MBO
465 rtw_mbo_build_trans_reject_reason_attr(padapter,
466 &pframe, pattrib, &mbo_trans_rej_res);
467 #endif
468
469 RTW_INFO("WNM: BSS Transition Management Response sent(reason:%d)\n", reason);
470 break;
471 case RTW_WLAN_ACTION_WNM_NOTIF_REQ:
472 #ifdef CONFIG_RTW_MBO
473 dialog_token++;
474 mbo_notif_req_type = WLAN_EID_VENDOR_SPECIFIC;
475 pframe = rtw_set_fixed_ie(pframe, 1, &(dialog_token), &(pattrib->pktlen));
476 pframe = rtw_set_fixed_ie(pframe, 1, &(mbo_notif_req_type), &(pattrib->pktlen));
477 rtw_mbo_build_wnm_notification(padapter, &pframe, pattrib);
478 RTW_INFO("WNM: Notification request sent\n");
479 #endif
480 break;
481 default:
482 goto exit;
483 }
484
485 pattrib->last_txcmdsz = pattrib->pktlen;
486 dump_mgntframe(padapter, pmgntframe);
487
488 exit:
489 return;
490 }
491
492 /* argument req_ie@cfg80211_roamed()/cfg80211_connect_result()
493 is association request IEs format. if driver used reassoc-req format,
494 RSN IE could not be parsed @supplicant process */
rtw_wnm_update_reassoc_req_ie(_adapter * padapter)495 void rtw_wnm_update_reassoc_req_ie(_adapter *padapter)
496 {
497 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
498 u32 dup_len, offset;
499 u8 *pdup;
500
501 if (!pmlmepriv->assoc_req || !pmlmepriv->assoc_req_len)
502 return;
503
504 /* total len is assoc req len without Current AP Field*/
505 dup_len = pmlmepriv->assoc_req_len - ETH_ALEN;
506
507 /* offset is a len of 80211 header + capability(2B) + listen interval(2B) */
508 offset = sizeof(struct rtw_ieee80211_hdr_3addr) + 4;
509
510 pdup = rtw_zmalloc(dup_len);
511 if (pdup) {
512 /* remove Current AP Field @reassoc req IE */
513 _rtw_memcpy(pdup, pmlmepriv->assoc_req, offset);
514 _rtw_memcpy(pdup + offset, pmlmepriv->assoc_req + offset + ETH_ALEN,
515 pmlmepriv->assoc_req_len - offset);
516 rtw_buf_update(&pmlmepriv->assoc_req,
517 &pmlmepriv->assoc_req_len, pdup, dup_len);
518 rtw_mfree(pdup, dup_len);
519 }
520 }
521 #endif /* CONFIG_RTW_WNM */
522
523 #if defined(CONFIG_RTW_WNM) || defined(CONFIG_RTW_80211K)
rtw_roam_nb_info_init(_adapter * padapter)524 void rtw_roam_nb_info_init(_adapter *padapter)
525 {
526 struct roam_nb_info *pnb = &(padapter->mlmepriv.nb_info);
527 struct btm_rpt_cache *pcache = &(pnb->btm_cache);
528
529 _rtw_memset(&pnb->nb_rpt, 0, sizeof(pnb->nb_rpt));
530 _rtw_memset(&pnb->nb_rpt_ch_list, 0, sizeof(pnb->nb_rpt_ch_list));
531 _rtw_memset(&pnb->roam_target_addr, 0, ETH_ALEN);
532 pnb->nb_rpt_valid = _FALSE;
533 pnb->nb_rpt_ch_list_num = 0;
534 pnb->preference_en = _FALSE;
535 pnb->nb_rpt_is_same = _TRUE;
536 pnb->last_nb_rpt_entries = 0;
537 pnb->disassoc_waiting = -1;
538 #ifdef CONFIG_RTW_WNM
539 rtw_init_timer(&pnb->roam_scan_timer,
540 padapter, rtw_wnm_roam_scan_hdl,
541 padapter);
542 rtw_init_timer(&pnb->disassoc_chk_timer,
543 padapter, rtw_wnm_disassoc_chk_hdl,
544 padapter);
545
546 _rtw_memset(pcache, 0, sizeof(struct btm_rpt_cache));
547 pcache->validity_time = wnm_defualt_validity_time;
548 pcache->disassoc_time= wnm_default_disassoc_time ;
549 #endif
550 }
551
rtw_roam_nb_scan_list_set(_adapter * padapter,struct sitesurvey_parm * pparm)552 u8 rtw_roam_nb_scan_list_set(
553 _adapter *padapter, struct sitesurvey_parm *pparm)
554 {
555 u8 ret = _FALSE;
556 u32 i;
557 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
558 struct roam_nb_info *pnb = &(pmlmepriv->nb_info);
559
560 #ifdef CONFIG_RTW_80211R
561 if (!rtw_chk_roam_flags(padapter, RTW_ROAM_ACTIVE)
562 && !rtw_ft_chk_flags(padapter, RTW_FT_BTM_ROAM))
563 return ret;
564 #endif
565
566 if (!pmlmepriv->need_to_roam)
567 return ret;
568
569 if ((!pmlmepriv->nb_info.nb_rpt_valid) || (!pnb->nb_rpt_ch_list_num))
570 return ret;
571
572 if (!pparm)
573 return ret;
574
575 rtw_init_sitesurvey_parm(padapter, pparm);
576 if (rtw_roam_busy_scan(padapter, pnb)) {
577 pparm->ch_num = 1;
578 pparm->ch[pmlmepriv->ch_cnt].hw_value =
579 pnb->nb_rpt_ch_list[pmlmepriv->ch_cnt].hw_value;
580 pmlmepriv->ch_cnt++;
581 ret = _TRUE;
582
583 RTW_WNM_INFO("%s: ch_cnt=%u, (%u)hw_value=%u\n",
584 __func__, pparm->ch_num, pmlmepriv->ch_cnt,
585 pparm->ch[pmlmepriv->ch_cnt].hw_value);
586
587 if (pmlmepriv->ch_cnt == pnb->nb_rpt_ch_list_num) {
588 pmlmepriv->nb_info.nb_rpt_valid = _FALSE;
589 pmlmepriv->ch_cnt = 0;
590 }
591 goto set_bssid_list;
592 }
593
594 pparm->ch_num = (pnb->nb_rpt_ch_list_num > RTW_CHANNEL_SCAN_AMOUNT)?
595 (RTW_CHANNEL_SCAN_AMOUNT):(pnb->nb_rpt_ch_list_num);
596 for (i=0; i<pparm->ch_num; i++) {
597 pparm->ch[i].hw_value = pnb->nb_rpt_ch_list[i].hw_value;
598 pparm->ch[i].flags = RTW_IEEE80211_CHAN_PASSIVE_SCAN;
599 }
600
601 pmlmepriv->nb_info.nb_rpt_valid = _FALSE;
602 pmlmepriv->ch_cnt = 0;
603 ret = _TRUE;
604
605 set_bssid_list:
606 rtw_set_802_11_bssid_list_scan(padapter, pparm);
607 return ret;
608 }
609
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)610 static u8 rtw_wnm_nb_elem_parsing(
611 u8* pdata, u32 data_len, u8 from_btm,
612 u32 *nb_rpt_num, u8 *nb_rpt_is_same,
613 struct roam_nb_info *pnb, struct wnm_btm_cant *pcandidates)
614 {
615 u8 bfound = _FALSE, ret = _SUCCESS;
616 u8 *ptr, *pend, *op;
617 u32 elem_len, subelem_len, op_len;
618 u32 i, nb_rpt_entries = 0;
619 struct nb_rpt_hdr *pie;
620 struct wnm_btm_cant *pcandidate;
621
622 if ((!pdata) || (!pnb))
623 return _FAIL;
624
625 if ((from_btm) && (!pcandidates))
626 return _FAIL;
627
628 ptr = pdata;
629 pend = ptr + data_len;
630 elem_len = data_len;
631 subelem_len = (u32)*(pdata+1);
632
633 for (i=0; i < RTW_MAX_NB_RPT_NUM; i++) {
634 if (((ptr + 7) > pend) || (elem_len < subelem_len))
635 break;
636
637 if (*ptr != RTW_WLAN_ACTION_WNM_NB_RPT_ELEM) {
638 RTW_WNM_INFO("WNM: end of data(0x%2x)!\n", *ptr);
639 break;
640 }
641
642 pie = (struct nb_rpt_hdr *)ptr;
643 if (from_btm) {
644 op = rtw_get_ie((u8 *)(ptr+15),
645 WNM_BTM_CAND_PREF_SUBEID,
646 &op_len, (subelem_len - 15));
647 }
648
649 ptr = (u8 *)(ptr + subelem_len + 2);
650 elem_len -= (subelem_len +2);
651 subelem_len = *(ptr+1);
652 if (from_btm) {
653 pcandidate = (pcandidates + i);
654 _rtw_memcpy(&pcandidate->nb_rpt, pie, sizeof(struct nb_rpt_hdr));
655 if (op && (op_len !=0)) {
656 pcandidate->preference = *(op + 2);
657 bfound = _TRUE;
658 } else
659 pcandidate->preference = 0;
660
661 RTW_WNM_INFO("WNM: preference check bssid("MAC_FMT
662 ") ,bss_info(0x%04X), reg_class(0x%02X), ch(%d),"
663 " phy_type(0x%02X), preference(0x%02X)\n",
664 MAC_ARG(pcandidate->nb_rpt.bssid), pcandidate->nb_rpt.bss_info,
665 pcandidate->nb_rpt.reg_class, pcandidate->nb_rpt.ch_num,
666 pcandidate->nb_rpt.phy_type, pcandidate->preference);
667 } else {
668 if (_rtw_memcmp(&pnb->nb_rpt[i], pie, sizeof(struct nb_rpt_hdr)) == _FALSE)
669 *nb_rpt_is_same = _FALSE;
670 _rtw_memcpy(&pnb->nb_rpt[i], pie, sizeof(struct nb_rpt_hdr));
671 }
672 nb_rpt_entries++;
673 }
674
675 if (from_btm)
676 pnb->preference_en = (bfound)?_TRUE:_FALSE;
677
678 *nb_rpt_num = nb_rpt_entries;
679 return ret;
680 }
681
682 /* selection sorting based on preference value
683 * IN : nb_rpt_entries - candidate num
684 * IN/OUT : pcandidates - candidate list
685 * return : TRUE - means pcandidates is updated.
686 */
rtw_wnm_candidates_sorting(u32 nb_rpt_entries,struct wnm_btm_cant * pcandidates)687 static u8 rtw_wnm_candidates_sorting(
688 u32 nb_rpt_entries, struct wnm_btm_cant *pcandidates)
689 {
690 u8 updated = _FALSE;
691 u32 i, j, pos;
692 struct wnm_btm_cant swap;
693 struct wnm_btm_cant *pcant_1, *pcant_2;
694
695 if ((!nb_rpt_entries) || (!pcandidates))
696 return updated;
697
698 for (i=0; i < (nb_rpt_entries - 1); i++) {
699 pos = i;
700 for (j=(i + 1); j < nb_rpt_entries; j++) {
701 pcant_1 = pcandidates+pos;
702 pcant_2 = pcandidates+j;
703 if ((pcant_1->preference) < (pcant_2->preference))
704 pos = j;
705 }
706
707 if (pos != i) {
708 updated = _TRUE;
709 _rtw_memcpy(&swap, (pcandidates+i), sizeof(struct wnm_btm_cant));
710 _rtw_memcpy((pcandidates+i), (pcandidates+pos), sizeof(struct wnm_btm_cant));
711 _rtw_memcpy((pcandidates+pos), &swap, sizeof(struct wnm_btm_cant));
712 }
713 }
714 return updated;
715 }
716
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)717 static void rtw_wnm_nb_info_update(
718 u32 nb_rpt_entries, u8 from_btm,
719 struct roam_nb_info *pnb, struct wnm_btm_cant *pcandidates,
720 u8 *nb_rpt_is_same)
721 {
722 u8 is_found;
723 u32 i, j;
724 struct wnm_btm_cant *pcand;
725
726 if (!pnb)
727 return;
728
729 pnb->nb_rpt_ch_list_num = 0;
730 for (i=0; i<nb_rpt_entries; i++) {
731 is_found = _FALSE;
732 if (from_btm) {
733 pcand = (pcandidates+i);
734 if (_rtw_memcmp(&pnb->nb_rpt[i], &pcand->nb_rpt,
735 sizeof(struct nb_rpt_hdr)) == _FALSE)
736 *nb_rpt_is_same = _FALSE;
737 _rtw_memcpy(&pnb->nb_rpt[i], &pcand->nb_rpt, sizeof(struct nb_rpt_hdr));
738 }
739
740 RTW_WNM_INFO("WNM: bssid(" MAC_FMT
741 ") , bss_info(0x%04X), reg_class(0x%02X), ch_num(%d), phy_type(0x%02X)\n",
742 MAC_ARG(pnb->nb_rpt[i].bssid), pnb->nb_rpt[i].bss_info,
743 pnb->nb_rpt[i].reg_class, pnb->nb_rpt[i].ch_num,
744 pnb->nb_rpt[i].phy_type);
745
746 if (pnb->nb_rpt[i].ch_num == 0)
747 continue;
748
749 for (j=0; j<nb_rpt_entries; j++) {
750 if (pnb->nb_rpt[i].ch_num == pnb->nb_rpt_ch_list[j].hw_value) {
751 is_found = _TRUE;
752 break;
753 }
754 }
755
756 if (!is_found) {
757 pnb->nb_rpt_ch_list[pnb->nb_rpt_ch_list_num].hw_value = pnb->nb_rpt[i].ch_num;
758 pnb->nb_rpt_ch_list_num++;
759 }
760 }
761 }
762
rtw_wnm_btm_candidate_select(_adapter * padapter)763 static void rtw_wnm_btm_candidate_select(_adapter *padapter)
764 {
765 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
766 struct roam_nb_info *pnb = &(padapter->mlmepriv.nb_info);
767 struct wlan_network *pnetwork;
768 u8 bfound = _FALSE;
769 u8 ignore_currrent = _FALSE;
770 u32 i;
771
772 #ifdef CONFIG_RTW_80211R
773 if (rtw_ft_chk_flags(padapter, RTW_FT_BTM_ROAM))
774 ignore_currrent = _TRUE;
775 #endif
776
777 for (i = 0; i < pnb->last_nb_rpt_entries; i++) {
778 if (ignore_currrent &&
779 (_rtw_memcmp(pnb->nb_rpt[i].bssid,\
780 padapter->mlmepriv.cur_network.network.MacAddress, ETH_ALEN))) {
781 RTW_WNM_INFO("WNM : ignore candidate "MAC_FMT" for it's connected(%u)!\n",
782 MAC_ARG(pnb->nb_rpt[i].bssid), i);
783 continue;
784 }
785
786 pnetwork = rtw_find_network(
787 &(pmlmepriv->scanned_queue),
788 pnb->nb_rpt[i].bssid);
789
790 if (pnetwork) {
791 bfound = _TRUE;
792 break;
793 }
794 }
795
796 if (bfound) {
797 _rtw_memcpy(pnb->roam_target_addr, pnb->nb_rpt[i].bssid, ETH_ALEN);
798 RTW_INFO("WNM : select btm entry(%d) - %s("MAC_FMT", ch:%u) rssi:%d\n"
799 , i
800 , pnetwork->network.Ssid.Ssid
801 , MAC_ARG(pnetwork->network.MacAddress)
802 , pnetwork->network.Configuration.DSConfig
803 , (int)pnetwork->network.Rssi);
804 } else
805 _rtw_memset(pnb->roam_target_addr,0, ETH_ALEN);
806 }
807
rtw_wnm_btm_candidates_survey(_adapter * padapter,u8 * pframe,u32 elem_len,u8 from_btm)808 u32 rtw_wnm_btm_candidates_survey(
809 _adapter *padapter, u8* pframe, u32 elem_len, u8 from_btm)
810 {
811 struct roam_nb_info *pnb = &(padapter->mlmepriv.nb_info);
812 struct wnm_btm_cant *pcandidate_list = NULL;
813 u8 nb_rpt_is_same = _TRUE;
814 u32 ret = _FAIL;
815 u32 nb_rpt_entries = 0;
816
817 if (from_btm) {
818 u32 mlen = sizeof(struct wnm_btm_cant) * RTW_MAX_NB_RPT_NUM;
819 pcandidate_list = (struct wnm_btm_cant *)rtw_malloc(mlen);
820 if (pcandidate_list == NULL)
821 goto exit;
822 }
823
824 /*clean the status set last time*/
825 _rtw_memset(&pnb->nb_rpt_ch_list, 0, sizeof(pnb->nb_rpt_ch_list));
826 pnb->nb_rpt_valid = _FALSE;
827 if (!rtw_wnm_nb_elem_parsing(
828 pframe, elem_len, from_btm,
829 &nb_rpt_entries, &nb_rpt_is_same,
830 pnb, pcandidate_list))
831 goto exit;
832
833 if (nb_rpt_entries != 0) {
834 if ((from_btm) && (rtw_wnm_btm_preference_cap(padapter)))
835 rtw_wnm_candidates_sorting(nb_rpt_entries, pcandidate_list);
836
837 rtw_wnm_nb_info_update(
838 nb_rpt_entries, from_btm,
839 pnb, pcandidate_list, &nb_rpt_is_same);
840 }
841
842 RTW_WNM_INFO("nb_rpt_is_same = %d, nb_rpt_entries = %d, last_nb_rpt_entries = %d\n",
843 nb_rpt_is_same, nb_rpt_entries, pnb->last_nb_rpt_entries);
844 if ((nb_rpt_is_same == _TRUE) && (nb_rpt_entries == pnb->last_nb_rpt_entries))
845 pnb->nb_rpt_is_same = _TRUE;
846 else {
847 pnb->nb_rpt_is_same = _FALSE;
848 pnb->last_nb_rpt_entries = nb_rpt_entries;
849 }
850
851 if ((from_btm) && (nb_rpt_entries != 0))
852 rtw_wnm_btm_candidate_select(padapter);
853
854 pnb->nb_rpt_valid = _TRUE;
855 ret = _SUCCESS;
856
857 exit:
858 if (from_btm && pcandidate_list)
859 rtw_mfree((u8 *)pcandidate_list, sizeof(struct wnm_btm_cant) * RTW_MAX_NB_RPT_NUM);
860
861 return ret;
862 }
863
864 #endif /*defined(CONFIG_RTW_WNM) || defined(CONFIG_RTW_80211K) */
865
866