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 #define _RTW_MBO_C
16
17 #include <drv_types.h>
18
19 #ifdef CONFIG_RTW_MBO
20
21 #ifndef RTW_MBO_DBG
22 #define RTW_MBO_DBG 0
23 #endif
24 #if RTW_MBO_DBG
25 #define RTW_MBO_INFO(fmt, arg...) \
26 RTW_INFO(fmt, arg)
27 #define RTW_MBO_DUMP(str, data, len) \
28 RTW_INFO_DUMP(str, data, len)
29 #else
30 #define RTW_MBO_INFO(fmt, arg...) do {} while (0)
31 #define RTW_MBO_DUMP(str, data, len) do {} while (0)
32 #endif
33
34 /* Cellular Data Connectivity field
35 * 1 : Cellular data connection available
36 * 2 : Cellular data connection not available
37 * 3 : Not Cellular data capable
38 * otherwise : Reserved
39 */
40 int rtw_mbo_cell_data_conn = 2;
41 module_param(rtw_mbo_cell_data_conn, int, 0644);
42
43 static u8 wfa_mbo_oui[] = {0x50, 0x6F, 0x9A, 0x16};
44
45 #define rtw_mbo_get_oui(p) ((u8 *)(p) + 2)
46
47 #define rtw_mbo_get_attr_id(p) ((u8 *)(p))
48
49 #define rtw_mbo_get_disallow_res(p) ((u8 *)(p) + 3)
50
51 #define rtw_mbo_set_1byte_ie(p, v, l) \
52 rtw_set_fixed_ie((p), 1, (v), (l))
53
54 #define rtw_mbo_set_2byte_ie(p, v, l) \
55 rtw_set_fixed_ie((p), 2, (v), (l))
56
57 #define rtw_mbo_set_4byte_ie(p, v, l) \
58 rtw_set_fixed_ie((p), 4, (v), (l))
59
60 #define rtw_mbo_set_nbyte_ie(p, sz, v, l) \
61 rtw_set_fixed_ie((p), (sz), (v), (l))
62
63 #define rtw_mbo_subfield_set(p, offset, val) (*(p + offset) = val)
64
65 #define rtw_mbo_subfields_set(p, offset, buf, len) \
66 do { \
67 u32 _offset = 0; \
68 u8 *_p = p + offset; \
69 while(_offset < len) { \
70 *(_p + _offset) = *(buf + _offset); \
71 _offset++; \
72 } \
73 } while(0)
74
75
rtw_mbo_ie_init(_adapter * padapter,struct mbo_priv * mbopriv)76 void rtw_mbo_ie_init(_adapter *padapter, struct mbo_priv *mbopriv)
77 {
78 if(!mbopriv)
79 return;
80 mbopriv->assoc_disallow = 0;
81 mbopriv->cellular_aware = 0;
82 mbopriv->ch_list_num = 0;
83 mbopriv->mbo_oce_element_len = 6;
84 mbopriv->mbo_oce_element[0] = 0xdd;
85 mbopriv->mbo_oce_element[1] = mbopriv->mbo_oce_element_len;
86 mbopriv->mbo_oce_element[2] = 0x50;
87 mbopriv->mbo_oce_element[3] = 0x6f;
88 mbopriv->mbo_oce_element[4] = 0x9a;
89 mbopriv->mbo_oce_element[5] = 0x16;
90 }
91
rtw_mbo_fill_non_prefer_channel_list(_adapter * padapter,struct mbo_priv * mbopriv,const u8 * pbuf,u8 len)92 void rtw_mbo_fill_non_prefer_channel_list(_adapter *padapter, struct mbo_priv *mbopriv,
93 const u8 *pbuf, u8 len)
94 {
95 u8 op_class = 0;
96 u8 preference = 0;
97 int i;
98
99 /* invalid length */
100 if(len != 0 && len < 3)
101 return;
102
103 /* reset non-prefer channel list */
104 mbopriv->ch_list_num = 0;
105 op_class = *pbuf;
106 preference = *(pbuf + len - 2);
107
108 if (len == 3 && mbopriv->ch_list_num < MBO_CH_LIST_MAX_NUM) {
109 mbopriv->ch_list[mbopriv->ch_list_num].op_class = op_class;
110 mbopriv->ch_list[mbopriv->ch_list_num].preference = preference;
111 mbopriv->ch_list[mbopriv->ch_list_num].channel = 0;
112 mbopriv->ch_list_num += 1;
113 RTW_INFO("[%s:%d]channel = %d, preference = %d\n", __func__, __LINE__, 0, preference);
114 } else {
115 for (i = 0; i < len - 3; i++) {
116 if(mbopriv->ch_list_num >= MBO_CH_LIST_MAX_NUM)
117 break;
118 mbopriv->ch_list[mbopriv->ch_list_num].op_class = op_class;
119 mbopriv->ch_list[mbopriv->ch_list_num].preference = preference;
120 mbopriv->ch_list[mbopriv->ch_list_num].channel = *(pbuf + 1 + i);
121 mbopriv->ch_list_num += 1;
122 RTW_INFO("[%s:%d]channel = %d, preference = %d\n", __func__, __LINE__,
123 *(pbuf + 1 + i), preference);
124 }
125 }
126 }
127
rtw_mbo_ie_handler(_adapter * padapter,struct mbo_priv * mbopriv,const u8 * pbuf,uint limit_len)128 void rtw_mbo_ie_handler(_adapter *padapter, struct mbo_priv *mbopriv, const u8 *pbuf, uint limit_len)
129 {
130 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
131 uint total_len = 0;
132 u8 attribute_id = 0;
133 u8 attribute_len = 0;
134 const u8 *p = pbuf;
135
136 if(!mbopriv)
137 return;
138
139 rtw_mbo_ie_init(padapter, mbopriv);
140 _rtw_memcpy(mbopriv->mbo_oce_element + 6, pbuf, limit_len);
141 mbopriv->mbo_oce_element[1] = limit_len + 4;
142
143 while (total_len <= limit_len) {
144 attribute_id = *p;
145 attribute_len = *(p + 1);
146 total_len += attribute_len;
147 if(total_len > limit_len)
148 break;
149
150 switch (attribute_id) {
151 case MBO_AP_CAPABILITY:
152 if(attribute_len == 1){
153 RTW_INFO("[%s]Find attribute MBO_AP_CAPABILITY\n", __func__);
154 if(*(p+2) & 0x40)
155 mbopriv->cellular_aware = 1;
156 }
157 break;
158 case ASSOCIATION_DISALLOW:
159 if(attribute_len == 1){
160 RTW_INFO("[%s]Find attribute ASSOCIATION_DISALLOW\n", __func__);
161 mbopriv->assoc_disallow = *(p+2);
162 }
163 break;
164 case NON_PREFER_CHANNEL_RPT:
165 RTW_INFO("[%s]Find attribute NON_PREFER_CHANNEL_RPT\n", __func__);
166 rtw_mbo_fill_non_prefer_channel_list(padapter, mbopriv, p + 2, attribute_len);
167 break;
168 case CELLULAR_DATA_CAPABILITY:
169 case CELLULAR_DATA_CONNECT_PREFER:
170 case TRANS_REASON_CODE:
171 case TRANS_REJECT_REASON_CODE:
172 case ASSOCIATION_RETRY_DELAY:
173 break;
174 default:
175 RTW_ERR("[%s]Unknown MBO attribute %d\n", __func__, attribute_id);
176 }
177
178 p += (attribute_len + 2);
179 }
180 }
181
rtw_ap_parse_sta_mbo_element(_adapter * padapter,struct sta_info * psta,u8 * ies_buf,u16 ies_len)182 void rtw_ap_parse_sta_mbo_element(_adapter *padapter,
183 struct sta_info *psta, u8 *ies_buf, u16 ies_len)
184 {
185 uint ie_len = 0;
186 u8 *p;
187 u8 WIFI_ALLIANCE_OUI[] = {0x50, 0x6f, 0x9a};
188
189 ie_len = 0;
190 for (p = ies_buf; ; p += (ie_len + 2)) {
191 p = rtw_get_ie(p, _SSN_IE_1_, &ie_len, (ies_len - (ie_len + 2)));
192 if ((p) && (_rtw_memcmp(p + 2, WIFI_ALLIANCE_OUI, 3)) && (*(p+5) == MBO_OUI_TYPE)) {
193 /* find MBO-OCE information element */
194 psta->mbopriv.enable = _TRUE;
195 rtw_mbo_ie_handler(padapter, &psta->mbopriv, p + 6, ie_len - 4);
196 break;
197 }
198 if ((p == NULL) || (ie_len == 0))
199 break;
200 }
201 }
202
rtw_mbo_ie_get(u8 * pie,u32 * plen,u32 limit)203 static u8 *rtw_mbo_ie_get(u8 *pie, u32 *plen, u32 limit)
204 {
205 const u8 *p = pie;
206 u32 tmp, i;
207
208 if (limit <= 1)
209 return NULL;
210
211 i = 0;
212 *plen = 0;
213 while (1) {
214 if ((*p == _VENDOR_SPECIFIC_IE_) &&
215 (_rtw_memcmp(rtw_mbo_get_oui(p), wfa_mbo_oui, 4))) {
216 *plen = *(p + 1);
217 /* RTW_MBO_DUMP("VENDOR_SPECIFIC_IE MBO: ", p, *(p + 1)); */
218 return (u8 *)p;
219 } else {
220 tmp = *(p + 1);
221 p += (tmp + 2);
222 i += (tmp + 2);
223 }
224
225 if (i >= limit)
226 break;
227 }
228
229 return NULL;
230 }
231
rtw_mbo_attrs_get(u8 * pie,u32 limit,u8 attr_id,u32 * attr_len,u8 dbg)232 static u8 *rtw_mbo_attrs_get(u8 *pie,
233 u32 limit, u8 attr_id,u32 *attr_len, u8 dbg)
234 {
235 u8 *p = NULL;
236 u32 offset, plen = 0;
237
238 if ((pie == NULL) || (limit <= 1))
239 goto exit;
240
241 if ((p = rtw_mbo_ie_get(pie, &plen, limit)) == NULL)
242 goto exit;
243
244 /* shift 2 + OUI size and move to attributes content */
245 p = p + 2 + sizeof(wfa_mbo_oui);
246 plen = plen - 4;
247
248 if (dbg)
249 RTW_MBO_DUMP("Attributes contents: ", p, plen);
250
251 if ((p = rtw_get_ie(p, attr_id, attr_len, plen)) == NULL)
252 goto exit;
253
254 if (dbg) {
255 RTW_MBO_INFO("%s : id=%u(len=%u)\n",
256 __func__, attr_id, *attr_len);
257 RTW_MBO_DUMP("contents : ", (p + 2), *attr_len);
258 }
259
260 exit:
261 return p;
262
263 }
264
rtw_mbo_attr_sz_get(_adapter * padapter,u8 id)265 static u32 rtw_mbo_attr_sz_get(
266 _adapter *padapter, u8 id)
267 {
268 u32 len = 0;
269
270 switch (id) {
271 case RTW_MBO_ATTR_NPREF_CH_RPT_ID:
272 {
273 struct rf_ctl_t *prfctl = \
274 adapter_to_rfctl(padapter);
275 struct npref_ch_rtp *prpt = \
276 &(prfctl->ch_rtp);
277 struct npref_ch* pch;
278 u32 i, attr_len, offset;
279
280 for (i=0; i < prpt->nm_of_rpt; i++) {
281 pch = &prpt->ch_rpt[i];
282 /*attr_len = ch list + op class
283 + preference + reason */
284 attr_len = pch->nm_of_ch + 3;
285 /* offset = id + len field
286 + attr_len */
287 offset = attr_len + 2;
288 len += offset;
289 }
290 }
291 break;
292 case RTW_MBO_ATTR_ASSOC_RETRY_DELAY_ID:
293 len = 4;
294 break;
295 case RTW_MBO_ATTR_AP_CAP_ID:
296 case RTW_MBO_ATTR_CELL_DATA_CAP_ID:
297 case RTW_MBO_ATTR_ASSOC_DISABLED_ID:
298 case RTW_MBO_ATTR_TRANS_RES_ID:
299 case RTW_MBO_ATTR_TRANS_REJ_ID:
300 len = 3;
301 break;
302 default:
303 break;
304 }
305
306 return len;
307 }
308
rtw_mbo_build_mbo_ie_hdr(u8 ** pframe,struct pkt_attrib * pattrib,u8 payload_len)309 static void rtw_mbo_build_mbo_ie_hdr(
310 u8 **pframe, struct pkt_attrib *pattrib, u8 payload_len)
311 {
312 u8 eid = RTW_MBO_EID;
313 u8 len = payload_len + 4;
314
315 *pframe = rtw_mbo_set_1byte_ie(*pframe, &eid, &(pattrib->pktlen));
316 *pframe = rtw_mbo_set_1byte_ie(*pframe, &len, &(pattrib->pktlen));
317 *pframe = rtw_mbo_set_4byte_ie(*pframe,
318 wfa_mbo_oui, &(pattrib->pktlen));
319 }
320
rtw_mbo_build_cell_data_cap_attr(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)321 void rtw_mbo_build_cell_data_cap_attr(
322 _adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib)
323 {
324 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
325 u8 attr_id = RTW_MBO_ATTR_CELL_DATA_CAP_ID;
326 u8 attr_len = 1;
327 u8 cell_data_con = rtw_mbo_cell_data_conn;
328
329 /* used Cellular Data Capabilities from supplicant */
330 if (!rtw_mbo_wifi_logo_test(padapter) &&
331 pmlmepriv->pcell_data_cap_ie &&
332 pmlmepriv->cell_data_cap_len == 1) {
333 cell_data_con = *pmlmepriv->pcell_data_cap_ie;
334 }
335
336 *pframe = rtw_mbo_set_1byte_ie(*pframe, &attr_id, &(pattrib->pktlen));
337 *pframe = rtw_mbo_set_1byte_ie(*pframe, &attr_len, &(pattrib->pktlen));
338 *pframe = rtw_mbo_set_1byte_ie(*pframe,
339 &cell_data_con, &(pattrib->pktlen));
340 }
341
342
rtw_mbo_build_ap_cap_Indication_attr(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib,u8 cap_ind)343 static void rtw_mbo_build_ap_cap_Indication_attr(
344 _adapter *padapter, u8 **pframe,
345 struct pkt_attrib *pattrib, u8 cap_ind)
346 {
347 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
348 u8 attr_id = RTW_MBO_ATTR_AP_CAP_ID;
349 u8 attr_len = 1;
350 u8 ap_cap_ind = cap_ind;
351
352 *pframe = rtw_mbo_set_1byte_ie(*pframe, &attr_id, &(pattrib->pktlen));
353 *pframe = rtw_mbo_set_1byte_ie(*pframe, &attr_len, &(pattrib->pktlen));
354 *pframe = rtw_mbo_set_1byte_ie(*pframe,
355 &ap_cap_ind, &(pattrib->pktlen));
356 }
357
rtw_mbo_build_ap_disallowed_attr(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib,u8 code)358 static void rtw_mbo_build_ap_disallowed_attr(
359 _adapter *padapter, u8 **pframe,
360 struct pkt_attrib *pattrib, u8 code)
361 {
362 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
363 u8 attr_id = RTW_MBO_ATTR_ASSOC_DISABLED_ID;
364 u8 attr_len = 1;
365 u8 reason = 0;
366
367 if (code > 0) {
368 reason = code;
369 *pframe = rtw_mbo_set_1byte_ie(*pframe,
370 &attr_id, &(pattrib->pktlen));
371 *pframe = rtw_mbo_set_1byte_ie(*pframe,
372 &attr_len, &(pattrib->pktlen));
373 *pframe = rtw_mbo_set_1byte_ie(*pframe,
374 &reason, &(pattrib->pktlen));
375 }
376 }
377
rtw_mbo_build_ap_trans_reason_attr(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib,u8 code)378 static void rtw_mbo_build_ap_trans_reason_attr(
379 _adapter *padapter, u8 **pframe,
380 struct pkt_attrib *pattrib, u8 code)
381 {
382 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
383 u8 attr_id = RTW_MBO_ATTR_TRANS_RES_ID;
384 u8 attr_len = 1;
385 u8 reason = 0;
386
387 reason = code;
388 *pframe = rtw_mbo_set_1byte_ie(*pframe,
389 &attr_id, &(pattrib->pktlen));
390 *pframe = rtw_mbo_set_1byte_ie(*pframe,
391 &attr_len, &(pattrib->pktlen));
392 *pframe = rtw_mbo_set_1byte_ie(*pframe,
393 &reason, &(pattrib->pktlen));
394 }
395
rtw_mbo_build_ap_assoc_retry_delay_attr(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib,u16 code)396 static void rtw_mbo_build_ap_assoc_retry_delay_attr(
397 _adapter *padapter, u8 **pframe,
398 struct pkt_attrib *pattrib, u16 code)
399 {
400 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
401 u8 attr_id = RTW_MBO_ATTR_ASSOC_RETRY_DELAY_ID;
402 u8 attr_len = 2;
403 u16 delay = 0;
404
405 delay = code;
406 *pframe = rtw_mbo_set_1byte_ie(*pframe, &attr_id, &(pattrib->pktlen));
407 *pframe = rtw_mbo_set_1byte_ie(*pframe, &attr_len, &(pattrib->pktlen));
408 *pframe = rtw_mbo_set_2byte_ie(*pframe,
409 (u8 *)&delay, &(pattrib->pktlen));
410 }
411
rtw_mbo_update_cell_data_cap(_adapter * padapter,u8 * pie,u32 ie_len)412 static void rtw_mbo_update_cell_data_cap(
413 _adapter *padapter, u8 *pie, u32 ie_len)
414 {
415 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
416 u8 *mbo_attr;
417 u32 mbo_attrlen;
418
419 if ((pie == NULL) || (ie_len == 0))
420 return;
421
422 mbo_attr = rtw_mbo_attrs_get(pie, ie_len,
423 RTW_MBO_ATTR_CELL_DATA_CAP_ID, &mbo_attrlen, 0);
424
425 if ((mbo_attr == NULL) || (mbo_attrlen == 0) ) {
426 RTW_INFO("MBO : Cellular Data Capabilities not found!\n");
427 return;
428 }
429
430 rtw_buf_update(&pmlmepriv->pcell_data_cap_ie,
431 &pmlmepriv->cell_data_cap_len, (mbo_attr + 2), mbo_attrlen);
432 RTW_MBO_DUMP("rtw_mbo_update_cell_data_cap : ",
433 pmlmepriv->pcell_data_cap_ie, pmlmepriv->cell_data_cap_len);
434 }
435
rtw_mbo_update_ie_data(_adapter * padapter,u8 * pie,u32 ie_len)436 void rtw_mbo_update_ie_data(
437 _adapter *padapter, u8 *pie, u32 ie_len)
438 {
439 rtw_mbo_update_cell_data_cap(padapter, pie, ie_len);
440 }
441
rtw_mbo_current_op_class_get(_adapter * padapter)442 static u8 rtw_mbo_current_op_class_get(_adapter *padapter)
443 {
444 struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
445 struct p2p_channels *pch_list = &(prfctl->channel_list);
446 struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
447 struct p2p_reg_class *preg_class;
448 int class_idx, ch_idx;
449 u8 cur_op_class = 0;
450
451 for(class_idx =0; class_idx < pch_list->reg_classes; class_idx++) {
452 preg_class = &pch_list->reg_class[class_idx];
453 for (ch_idx = 0; ch_idx <= preg_class->channels; ch_idx++) {
454 if (pmlmeext->chandef.chan == \
455 preg_class->channel[ch_idx]) {
456 cur_op_class = preg_class->reg_class;
457 RTW_MBO_INFO("%s : current ch : %d,"
458 " op class : %d\n",
459 __func__, pmlmeext->chandef.chan,
460 cur_op_class);
461 break;
462 }
463 }
464 }
465
466 return cur_op_class;
467 }
468
rtw_mbo_supp_op_classes_get(_adapter * padapter,u8 * pclasses)469 static void rtw_mbo_supp_op_classes_get(_adapter *padapter, u8 *pclasses)
470 {
471 struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
472 struct p2p_channels *pch_list = &(prfctl->channel_list);
473 int class_idx;
474
475 if (pclasses == NULL)
476 return;
477
478 RTW_MBO_INFO("%s : support op class \n", __func__);
479 for(class_idx = 0; class_idx < pch_list->reg_classes; class_idx++) {
480 *(pclasses + class_idx) = \
481 pch_list->reg_class[class_idx].reg_class;
482 RTW_MBO_INFO("%u ,", *(pclasses + class_idx));
483 }
484
485 RTW_MBO_INFO("%s : \n", __func__);
486 }
487
rtw_mbo_build_supp_op_class_elem(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)488 void rtw_mbo_build_supp_op_class_elem(
489 _adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib)
490 {
491 struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
492 u8 payload[32] = {0};
493 u8 delimiter_130 = 130; /*0x82*/
494 u8 reg_class_nm, len;
495
496 if ((reg_class_nm = prfctl->channel_list.reg_classes) == 0)
497 return;
498
499 payload[0] = rtw_mbo_current_op_class_get(padapter);
500 rtw_mbo_supp_op_classes_get(padapter, &payload[1]);
501
502 /* IEEE 802.11 Std Current Operating Class Extension Sequence */
503 payload[reg_class_nm + 1] = delimiter_130;
504 payload[reg_class_nm + 2] = 0x00;
505
506 RTW_MBO_DUMP("op class :", payload, reg_class_nm);
507
508 /* Current Operating Class field + Operating Class field
509 + OneHundredAndThirty Delimiter field */
510 len = reg_class_nm + 3;
511 *pframe = rtw_set_ie(*pframe, EID_SupRegulatory, len ,
512 payload, &(pattrib->pktlen));
513 }
514
rtw_mbo_construct_npref_ch_rpt_attr(_adapter * padapter,u8 * pbuf,u32 buf_len,u32 * plen)515 static u8 rtw_mbo_construct_npref_ch_rpt_attr(
516 _adapter *padapter, u8 *pbuf, u32 buf_len, u32 *plen)
517 {
518 struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
519 struct npref_ch_rtp *prpt = &(prfctl->ch_rtp);
520 struct npref_ch* pch;
521 u32 attr_len, offset;
522 int i;
523 u8 *p = pbuf;
524
525 if (prpt->nm_of_rpt == 0) {
526 *plen = 0;
527 return _FALSE;
528 }
529
530 for (i=0; i < prpt->nm_of_rpt; i++) {
531 pch = &prpt->ch_rpt[i];
532 /* attr_len = ch list + op class + preference + reason */
533 attr_len = pch->nm_of_ch + 3;
534 /* offset = id + len field + attr_len */
535 offset = attr_len + 2;
536 rtw_mbo_subfield_set(p, 0, RTW_MBO_ATTR_NPREF_CH_RPT_ID);
537 rtw_mbo_subfield_set(p, 1, attr_len);
538 rtw_mbo_subfield_set(p, 2, pch->op_class);
539 rtw_mbo_subfields_set(p, 3, pch->chs, pch->nm_of_ch);
540 rtw_mbo_subfield_set(p, (offset - 2), pch->preference);
541 rtw_mbo_subfield_set(p, (offset - 1), pch->reason);
542 p += offset;
543 *plen += offset;
544
545 if (*plen >= buf_len) {
546 RTW_ERR("MBO : construct non-preferred-ch rpt fail!\n");
547 return _FALSE;
548 }
549 }
550
551 return _TRUE;
552 }
553
rtw_mbo_build_npref_ch_rpt_attr(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)554 void rtw_mbo_build_npref_ch_rpt_attr(
555 _adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib)
556 {
557 struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
558 struct npref_ch_rtp *prpt = &(prfctl->ch_rtp);
559 u32 tmp_sz = 0, body_len = 0;
560 u8 *ptmp;
561
562 tmp_sz = prpt->nm_of_rpt * sizeof(struct npref_ch);
563 ptmp = rtw_zmalloc(tmp_sz);
564 if (ptmp == NULL)
565 return;
566
567 if (rtw_mbo_construct_npref_ch_rpt_attr(
568 padapter, ptmp, tmp_sz, &body_len) == _FALSE) {
569 rtw_mfree(ptmp, tmp_sz);
570 return;
571 }
572
573 RTW_MBO_DUMP("Non-preferred Channel Report :", ptmp, body_len);
574 *pframe = rtw_mbo_set_nbyte_ie(*pframe, body_len,
575 ptmp, &(pattrib->pktlen));
576
577 rtw_mfree(ptmp, tmp_sz);
578 }
579
rtw_mbo_build_trans_reject_reason_attr(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib,u8 * pres)580 void rtw_mbo_build_trans_reject_reason_attr(
581 _adapter *padapter, u8 **pframe,
582 struct pkt_attrib *pattrib, u8 *pres)
583 {
584 u8 attr_id = RTW_MBO_ATTR_TRANS_REJ_ID;
585 u8 attr_len = 1;
586 u32 len = 0;
587
588 len = rtw_mbo_attr_sz_get(padapter, RTW_MBO_ATTR_TRANS_REJ_ID);
589 if ((len == 0) || (len > 3)) {
590 RTW_ERR("MBO : build Transition Rejection Reason"
591 " attribute fail(len=%u)\n", len);
592 return;
593 }
594
595 rtw_mbo_build_mbo_ie_hdr(pframe, pattrib, len);
596 *pframe = rtw_mbo_set_1byte_ie(*pframe, &attr_id, &(pattrib->pktlen));
597 *pframe = rtw_mbo_set_1byte_ie(*pframe, &attr_len, &(pattrib->pktlen));
598 *pframe = rtw_mbo_set_1byte_ie(*pframe, pres, &(pattrib->pktlen));
599 }
600
rtw_mbo_disallowed_network(struct wlan_network * pnetwork)601 u8 rtw_mbo_disallowed_network(struct wlan_network *pnetwork)
602 {
603 u8 *p, *attr_id, *res;
604 u32 attr_len = 0;
605 u8 disallow = _FALSE;
606
607 if ((pnetwork == NULL) || \
608 ((p = rtw_mbo_attrs_get(
609 pnetwork->network.IEs,
610 pnetwork->network.IELength,
611 RTW_MBO_ATTR_ASSOC_DISABLED_ID,
612 &attr_len, 0)) == NULL)) {
613 goto exit;
614 }
615
616 RTW_MBO_DUMP("Association Disallowed attribute :",p , attr_len + 2);
617 RTW_INFO("MBO : block "MAC_FMT" assoc disallowed reason %d\n",
618 MAC_ARG(pnetwork->network.MacAddress),
619 *(rtw_mbo_get_disallow_res(p)));
620
621 disallow = _TRUE;
622 exit:
623 return disallow;
624 }
625
rtw_mbo_build_extended_cap(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)626 void rtw_mbo_build_extended_cap(
627 _adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib)
628 {
629 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
630
631 if (!rtw_mbo_wifi_logo_test(padapter))
632 return;
633
634 rtw_wnm_add_btm_ext_cap(pmlmepriv->ext_capab_ie_data,
635 &(pmlmepriv->ext_capab_ie_len));
636 rtw_mbo_add_internw_ext_cap(pmlmepriv->ext_capab_ie_data,
637 &(pmlmepriv->ext_capab_ie_len));
638 *pframe = rtw_set_ie(*pframe,
639 WLAN_EID_EXT_CAP,
640 pmlmepriv->ext_capab_ie_len,
641 pmlmepriv->ext_capab_ie_data,
642 &(pattrib->pktlen));
643 }
644
rtw_mbo_non_pref_chans_dump(struct npref_ch * pch)645 static void rtw_mbo_non_pref_chans_dump(struct npref_ch* pch)
646 {
647 int i;
648 u8 buf[128] = {0};
649
650 for (i=0; i < pch->nm_of_ch; i++)
651 rtw_sprintf(buf, 128, "%s,%d", buf, pch->chs[i]);
652
653 RTW_MBO_INFO("%s : op_class=%01x, ch=%s, preference=%d, reason=%d\n",
654 __func__, pch->op_class, buf, pch->preference, pch->reason);
655 }
656
rtw_mbo_non_pref_chan_exist(struct npref_ch * pch,u8 ch)657 static u8 rtw_mbo_non_pref_chan_exist(struct npref_ch* pch, u8 ch)
658 {
659 u32 i;
660 u8 found = _FALSE;
661
662 for (i=0; i < pch->nm_of_ch; i++) {
663 if (pch->chs[i] == ch) {
664 found = _TRUE;
665 break;
666 }
667 }
668
669 return found;
670 }
671
rtw_mbo_non_pref_chan_get(_adapter * padapter,u8 op_class,u8 prefe,u8 res)672 static struct npref_ch* rtw_mbo_non_pref_chan_get(
673 _adapter *padapter, u8 op_class, u8 prefe, u8 res)
674 {
675 struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
676 struct npref_ch_rtp *prpt = &(prfctl->ch_rtp);
677 struct npref_ch* pch = NULL;
678 int i;
679
680 if (prpt->nm_of_rpt == 0)
681 return pch;
682
683 for (i=0; i < prpt->nm_of_rpt; i++) {
684 if ((prpt->ch_rpt[i].op_class == op_class) &&
685 (prpt->ch_rpt[i].preference == prefe) &&
686 (prpt->ch_rpt[i].reason == res)) {
687 pch = &prpt->ch_rpt[i];
688 break;
689 }
690 }
691
692 return pch;
693 }
694
rtw_mbo_non_pref_chan_set(struct npref_ch * pch,u8 op_class,u8 ch,u8 prefe,u8 res,u8 update)695 static void rtw_mbo_non_pref_chan_set(
696 struct npref_ch* pch, u8 op_class,
697 u8 ch, u8 prefe, u8 res, u8 update)
698 {
699 u32 offset = pch->nm_of_ch;
700
701 if (update) {
702 if (rtw_mbo_non_pref_chan_exist(pch, ch) == _FALSE) {
703 pch->chs[offset] = ch;
704 pch->nm_of_ch++;
705 }
706 } else {
707 pch->op_class = op_class;
708 pch->chs[0] = ch;
709 pch->preference = prefe;
710 pch->reason = res;
711 pch->nm_of_ch = 1;
712 }
713 }
714
rtw_mbo_non_pref_chans_update(_adapter * padapter,u8 op_class,u8 ch,u8 prefe,u8 res)715 static void rtw_mbo_non_pref_chans_update(
716 _adapter *padapter, u8 op_class, u8 ch, u8 prefe, u8 res)
717 {
718 struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
719 struct npref_ch_rtp *pch_rpt = &(prfctl->ch_rtp);
720 struct npref_ch* pch;
721
722 if (pch_rpt->nm_of_rpt >= RTW_MBO_MAX_CH_RPT_NUM) {
723 RTW_ERR("MBO : %d non_pref_chan entries supported!",
724 RTW_MBO_MAX_CH_RPT_NUM);
725 return;
726 }
727
728 if (pch_rpt->nm_of_rpt == 0) {
729 pch = &pch_rpt->ch_rpt[0];
730 rtw_mbo_non_pref_chan_set(pch, op_class,
731 ch, prefe, res, _FALSE);
732 pch_rpt->nm_of_rpt = 1;
733 return;
734 }
735
736 pch = rtw_mbo_non_pref_chan_get(padapter, op_class, prefe, res);
737 if (pch == NULL) {
738 pch = &pch_rpt->ch_rpt[pch_rpt->nm_of_rpt];
739 rtw_mbo_non_pref_chan_set(pch, op_class,
740 ch, prefe, res, _FALSE);
741 pch_rpt->nm_of_rpt++;
742 } else {
743 rtw_mbo_non_pref_chan_set(pch, op_class,
744 ch, prefe, res, _TRUE);
745 }
746
747 rtw_mbo_non_pref_chans_dump(pch);
748 }
749
rtw_mbo_non_pref_chans_set(_adapter * padapter,char * param,ssize_t sz)750 static void rtw_mbo_non_pref_chans_set(
751 _adapter *padapter, char *param, ssize_t sz)
752 {
753 char *pnext;
754 u32 op_class, ch, prefe, res;
755 int i = 0;
756
757 do {
758 pnext = strsep(¶m, " ");
759 if (pnext == NULL)
760 break;
761
762 sscanf(pnext, "%d:%d:%d:%d", &op_class, &ch, &prefe, &res);
763 rtw_mbo_non_pref_chans_update(padapter, op_class,
764 ch, prefe, res);
765
766 if ((i++) > 10) {
767 RTW_ERR("MBO : overflow %d \n", i);
768 break;
769 }
770
771 } while (param != (char*)'\0');
772
773 }
774
rtw_mbo_non_pref_chans_del(_adapter * padapter,char * param,ssize_t sz)775 static void rtw_mbo_non_pref_chans_del(
776 _adapter *padapter, char *param, ssize_t sz)
777 {
778 struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
779 struct npref_ch_rtp *prpt = &(prfctl->ch_rtp);
780
781 RTW_INFO("%s : delete non_pref_chan %s\n", __func__, param);
782 _rtw_memset(prpt, 0, sizeof(struct npref_ch_rtp));
783 }
784
rtw_mbo_proc_non_pref_chans_set(struct file * pfile,const char __user * buffer,size_t count,loff_t * pos,void * pdata)785 ssize_t rtw_mbo_proc_non_pref_chans_set(
786 struct file *pfile, const char __user *buffer,
787 size_t count, loff_t *pos, void *pdata)
788 {
789 struct net_device *dev = pdata;
790 _adapter *padapter = (_adapter *)rtw_netdev_priv(dev);
791 struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
792 u8 tmp[128] = {0};
793
794 if (count < 1)
795 return -EFAULT;
796
797 if (count > sizeof(tmp)) {
798 rtw_warn_on(1);
799 return -EFAULT;
800 }
801
802 if (buffer && !copy_from_user(tmp, buffer, count)) {
803 if (strncmp(tmp, "add", 3) == 0) {
804 rtw_mbo_non_pref_chans_set(padapter,
805 &tmp[4], (count - 4));
806 } else if (strncmp(tmp, "delete", 6) == 0) {
807 rtw_mbo_non_pref_chans_del(padapter,
808 &tmp[7], (count - 7));
809 } else {
810 RTW_ERR("MBO : Invalid format : echo [add|delete]"
811 " <oper_class>:<chan>:<preference>:<reason>\n");
812 return -EFAULT;
813 }
814 }
815
816 #ifdef CONFIG_RTW_WNM
817 if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE) &&
818 check_fwstate(pmlmepriv, WIFI_STATION_STATE))
819 rtw_wnm_issue_action(padapter,
820 RTW_WLAN_ACTION_WNM_NOTIF_REQ, 0, 0);
821 #endif
822
823 return count;
824 }
825
rtw_mbo_proc_non_pref_chans_get(struct seq_file * m,void * v)826 int rtw_mbo_proc_non_pref_chans_get(
827 struct seq_file *m, void *v)
828 {
829 struct net_device *dev = m->private;
830 _adapter *padapter = (_adapter *)rtw_netdev_priv(dev);
831 struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
832 struct npref_ch_rtp *prpt = &(prfctl->ch_rtp);
833 struct npref_ch* pch;
834 int i,j;
835 u8 buf[32] = {0};
836
837 RTW_PRINT_SEL(m, "op_class ch preference reason \n");
838 RTW_PRINT_SEL(m, "=======================================================\n");
839
840 if (prpt->nm_of_rpt == 0) {
841 RTW_PRINT_SEL(m, " empty table \n");
842 return 0;
843 }
844
845 for (i=0; i < prpt->nm_of_rpt; i++) {
846 pch = &prpt->ch_rpt[i];
847 buf[0]='\0';
848 for (j=0; j < pch->nm_of_ch; j++) {
849 if (j == 0)
850 rtw_sprintf(buf, 32, "%02u", pch->chs[j]);
851 else
852 rtw_sprintf(buf, 32, "%s,%02u", buf, pch->chs[j]);
853 }
854
855 RTW_PRINT_SEL(m, " %04u %20s %02u %02u\n",
856 pch->op_class, buf, pch->preference, pch->reason);
857 }
858
859 return 0;
860 }
861
rtw_mbo_proc_cell_data_set(struct file * pfile,const char __user * buffer,size_t count,loff_t * pos,void * pdata)862 ssize_t rtw_mbo_proc_cell_data_set(
863 struct file *pfile, const char __user *buffer,
864 size_t count, loff_t *pos, void *pdata)
865 {
866 struct net_device *dev = pdata;
867 _adapter *padapter = (_adapter *)rtw_netdev_priv(dev);
868 struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
869 int mbo_cell_data = 0;
870 u8 tmp[8] = {0};
871
872 if (count < 1)
873 return -EFAULT;
874
875 if (count > sizeof(tmp))
876 return -EFAULT;
877
878 if (buffer && !copy_from_user(tmp, buffer, count)) {
879 int num = sscanf(tmp, "%d", &mbo_cell_data);
880 if (num == 1) {
881 rtw_mbo_cell_data_conn = mbo_cell_data;
882 #ifdef CONFIG_RTW_WNM
883 if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE) &&
884 check_fwstate(pmlmepriv, WIFI_STATION_STATE)) {
885 rtw_wnm_issue_action(padapter,
886 RTW_WLAN_ACTION_WNM_NOTIF_REQ, 0, 0);
887 }
888 #endif
889 }
890 }
891
892 return count;
893 }
894
rtw_mbo_proc_cell_data_get(struct seq_file * m,void * v)895 int rtw_mbo_proc_cell_data_get(
896 struct seq_file *m, void *v)
897 {
898 #if 0
899 struct net_device *dev = m->private;
900 _adapter *adapter = (_adapter *)rtw_netdev_priv(dev);
901 #endif
902
903 RTW_PRINT_SEL(m, "Cellular Data Connectivity : %d\n",
904 rtw_mbo_cell_data_conn);
905 return 0;
906 }
907
908
rtw_mbo_disassoc(_adapter * padapter,u8 * da,u8 reason,u8 wait_ack)909 static void rtw_mbo_disassoc(_adapter *padapter, u8 *da,
910 u8 reason, u8 wait_ack)
911 {
912 struct xmit_frame *pmgntframe;
913 struct pkt_attrib *pattrib;
914 struct rtw_ieee80211_hdr *pwlanhdr;
915 struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
916 struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
917 struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
918 u8 *pframe;
919 u16 *fctrl;
920 int ret = _FAIL;
921
922 if (rtw_rfctl_is_tx_blocked_by_ch_waiting(adapter_to_rfctl(padapter)))
923 return;
924
925 pmgntframe = alloc_mgtxmitframe(pxmitpriv);
926 if (pmgntframe == NULL)
927 return;
928
929 /* update attribute */
930 pattrib = &pmgntframe->attrib;
931 update_mgntframe_attrib(padapter, pattrib);
932 pattrib->retry_ctrl = _FALSE;
933 pattrib->key_type = IEEE80211W_RIGHT_KEY;
934
935 _rtw_memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
936 pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
937 pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
938 fctrl = &(pwlanhdr->frame_ctl);
939 *(fctrl) = 0;
940
941 _rtw_memcpy(pwlanhdr->addr1, da, ETH_ALEN);
942 _rtw_memcpy(pwlanhdr->addr2, adapter_mac_addr(padapter), ETH_ALEN);
943 _rtw_memcpy(pwlanhdr->addr3,
944 get_my_bssid(&(pmlmeinfo->network)), ETH_ALEN);
945
946 SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
947 pmlmeext->mgnt_seq++;
948 set_frame_sub_type(pframe, WIFI_DISASSOC);
949
950 pframe += sizeof(struct rtw_ieee80211_hdr_3addr);
951 pattrib->pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
952
953 reason = cpu_to_le16(reason);
954 pframe = rtw_set_fixed_ie(pframe, _RSON_CODE_ ,
955 (unsigned char *)&reason, &(pattrib->pktlen));
956
957 pattrib->last_txcmdsz = pattrib->pktlen;
958 if (wait_ack)
959 dump_mgntframe_and_wait_ack(padapter, pmgntframe);
960 else
961 dump_mgntframe(padapter, pmgntframe);
962 RTW_MBO_INFO("%s : reason %u\n", __func__, reason);
963 }
964
rtw_mbo_construct_user_btm_req(_adapter * padapter,struct btm_req_hdr * phdr,u8 * purl,u32 url_len,struct wnm_btm_cant * pbtm_cant)965 static void rtw_mbo_construct_user_btm_req(
966 _adapter *padapter, struct btm_req_hdr *phdr,
967 u8 *purl, u32 url_len, struct wnm_btm_cant *pbtm_cant)
968 {
969 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
970 struct mbo_attr_info *pmbo_attr = &(pmlmepriv->mbo_attr);
971 struct mbo_user_btm_req_pkt *puser = &(pmbo_attr->user_raw);
972 struct wnm_btm_cant *puser_cant = NULL;
973
974 if (phdr) {
975 puser->hdr.req_mode = phdr->req_mode;
976 puser->hdr.disassoc_timer = phdr->disassoc_timer;
977 puser->hdr.validity_interval = phdr->validity_interval;
978 puser->hdr.term_duration.tsf = phdr->term_duration.tsf;
979 puser->hdr.term_duration.duration = \
980 phdr->term_duration.duration;
981 puser->hdr.term_duration.id = 0x4;
982 puser->hdr.term_duration.len = 0xa;
983 RTW_MBO_INFO("%s : req-mode=0x%x, disassoc_timer=%u, "
984 "validity_interval=%u, tsf=%llu, "
985 "duration=%u\n", __func__,
986 puser->hdr.req_mode, puser->hdr.disassoc_timer,
987 puser->hdr.validity_interval,
988 puser->hdr.term_duration.tsf,
989 puser->hdr.term_duration.duration);
990 }
991
992 if (purl && url_len) {
993 /* TODO */
994 }
995
996 if (pbtm_cant) {
997 struct wnm_btm_cant *pbtm_tb = NULL;
998 u8 i, idx, found = _FALSE;
999
1000 for (i = 0; i < RTW_MAX_NB_RPT_NUM; i++) {
1001 puser_cant = &puser->btm_cants[i];
1002 if (_rtw_memcmp(pbtm_cant->nb_rpt.bssid,
1003 puser_cant->nb_rpt.bssid, ETH_ALEN)) {
1004 puser_cant->nb_rpt.bss_info = \
1005 pbtm_cant->nb_rpt.bss_info;
1006 puser_cant->nb_rpt.reg_class = \
1007 pbtm_cant->nb_rpt.reg_class;
1008 puser_cant->nb_rpt.ch_num = \
1009 pbtm_cant->nb_rpt.ch_num;
1010 puser_cant->nb_rpt.phy_type = \
1011 pbtm_cant->nb_rpt.phy_type;
1012 puser_cant->preference = \
1013 pbtm_cant->preference;
1014 idx = i;
1015 found = _TRUE;
1016 break;
1017 }
1018 }
1019
1020 if (!found) {
1021 if (puser->candidate_cnt >= RTW_MAX_NB_RPT_NUM)
1022 puser->candidate_cnt = 0;
1023 puser_cant = &puser->btm_cants[puser->candidate_cnt];
1024 puser_cant->nb_rpt.id = \
1025 RTW_WLAN_ACTION_WNM_NB_RPT_ELEM;
1026 puser_cant->nb_rpt.len = 0x10;
1027 _rtw_memcpy(puser_cant->nb_rpt.bssid,
1028 pbtm_cant->nb_rpt.bssid, ETH_ALEN);
1029 puser_cant->nb_rpt.bss_info = \
1030 pbtm_cant->nb_rpt.bss_info;
1031 puser_cant->nb_rpt.reg_class = \
1032 pbtm_cant->nb_rpt.reg_class;
1033 puser_cant->nb_rpt.ch_num = \
1034 pbtm_cant->nb_rpt.ch_num;
1035 puser_cant->nb_rpt.phy_type = \
1036 pbtm_cant->nb_rpt.phy_type;
1037 puser_cant->preference = \
1038 pbtm_cant->preference;
1039 idx = puser->candidate_cnt;
1040 puser->candidate_cnt++;
1041 }
1042
1043 RTW_MBO_INFO("%s:%s idx=%u, bssid("MAC_FMT"),"
1044 " bss_info(0x%04X), reg_class(0x%02X),"
1045 " ch(%d), phy_type(0x%02X), preference(0x%02X)\n",
1046 __func__, (found)?"update":"new", idx,
1047 MAC_ARG(puser_cant->nb_rpt.bssid),
1048 puser_cant->nb_rpt.bss_info,
1049 puser_cant->nb_rpt.reg_class,
1050 puser_cant->nb_rpt.ch_num,
1051 puser_cant->nb_rpt.phy_type,
1052 puser_cant->preference);
1053
1054 } /* end of if (pbtm_cant) */
1055 }
1056
rtw_mbo_reset_user_btm_req_preference(_adapter * padapter)1057 static void rtw_mbo_reset_user_btm_req_preference(_adapter *padapter)
1058 {
1059 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
1060 struct mbo_attr_info *pmbo_attr = &(pmlmepriv->mbo_attr);
1061 struct mbo_user_btm_req_pkt *puser = &(pmbo_attr->user_raw);
1062 struct wnm_btm_cant *puser_cant = NULL;
1063 u8 i;
1064
1065 for (i = 0; i < RTW_MAX_NB_RPT_NUM; i++) {
1066 puser_cant = &puser->btm_cants[i];
1067 if (_rtw_memcmp(adapter_mac_addr(padapter),
1068 puser_cant->nb_rpt.bssid, ETH_ALEN)) {
1069 puser_cant->preference = 0;
1070 RTW_MBO_INFO("%s : reset "MAC_FMT" BTM preference\n",
1071 __func__, MAC_ARG(puser_cant->nb_rpt.bssid));
1072 break;
1073 }
1074 }
1075
1076 }
1077
rtw_mbo_proc_attr_set(struct file * pfile,const char __user * buffer,size_t count,loff_t * pos,void * pdata)1078 ssize_t rtw_mbo_proc_attr_set(
1079 struct file *pfile, const char __user *buffer,
1080 size_t count, loff_t *pos, void *pdata)
1081 {
1082 struct net_device *dev = pdata;
1083 _adapter *padapter = (_adapter *)rtw_netdev_priv(dev);
1084 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
1085 struct mbo_attr_info *pmbo_attr = &(pmlmepriv->mbo_attr);
1086 struct mbo_user_btm_req_pkt *puser = &(pmbo_attr->user_raw);
1087 u32 id, val;
1088 u8 tmp[64] = {0};
1089
1090 if (count < 1)
1091 return -EFAULT;
1092
1093 if (count > sizeof(tmp))
1094 return -EFAULT;
1095
1096 if (buffer && !copy_from_user(tmp, buffer, count)) {
1097 int num = sscanf(tmp, "%d %d", &id, &val);
1098 switch (id) {
1099 case RTW_MBO_ATTR_AP_CAP_ID:
1100 pmbo_attr->ap_cap_ind = val;
1101 break;
1102 case RTW_MBO_ATTR_CELL_DATA_CAP_ID:
1103 pmbo_attr->cell_data_cap = val;
1104 break;
1105 case RTW_MBO_ATTR_ASSOC_DISABLED_ID:
1106 pmbo_attr->assoc_disallow = val;
1107 break;
1108 case RTW_MBO_ATTR_TRANS_RES_ID:
1109 pmbo_attr->reason = val;
1110 break;
1111 case RTW_MBO_ATTR_ASSOC_RETRY_DELAY_ID:
1112 pmbo_attr->delay = val;
1113 break;
1114 case RTW_MBO_TEST_CMD_REST:
1115 RTW_INFO("%s : RTW_MBO_TEST_CMD_REST\n",
1116 __func__);
1117 _rtw_memset(pmbo_attr, 0,
1118 sizeof(struct mbo_attr_info));
1119 pmbo_attr->mbo_spec_test = 1;
1120 break;
1121 case RTW_MBO_TEST_CMD_BTM_REQ_SET:
1122 if (count >= 10) {
1123 struct btm_req_hdr btm_hdr;
1124 u32 disassoc_imnt, term_bit;
1125 u32 term_tsf, term_duration;
1126
1127 _rtw_memset(&btm_hdr, 0,
1128 sizeof(btm_hdr));
1129
1130 num = sscanf(tmp, "%d %u %u %u %u",
1131 &id, &disassoc_imnt,
1132 &term_bit, &term_tsf,
1133 &term_duration);
1134
1135 if (num < 5)
1136 break;
1137
1138 if (disassoc_imnt > 0)
1139 btm_hdr.req_mode |= \
1140 DISASSOC_IMMINENT;
1141
1142 if (term_bit > 0)
1143 btm_hdr.req_mode |= \
1144 BSS_TERMINATION_INCLUDED;
1145
1146 btm_hdr.term_duration.tsf = term_tsf;
1147 btm_hdr.term_duration.duration = \
1148 term_duration;
1149
1150 rtw_mbo_construct_user_btm_req(
1151 padapter, &btm_hdr, NULL, 0,
1152 NULL);
1153 }
1154 break;
1155 case RTW_MBO_TEST_CMD_BTM_REQ_SEND:
1156 if (count >= 12) {
1157 u8 mac_str[18] = {0};
1158 u8 dst_mac[ETH_ALEN] = {0};
1159 u32 cand_list = 0, disassoc_timer = 0;
1160
1161 num = sscanf(tmp, "%d %s %u %u",
1162 &id, mac_str, &cand_list,
1163 &disassoc_timer);
1164
1165 if (num < 4)
1166 break;
1167
1168 if (sscanf(mac_str, MAC_SFMT,
1169 MAC_SARG(
1170 dst_mac)) != 6) {
1171 break;
1172 }
1173
1174 puser->append_mbo_ie = _TRUE;
1175 puser->hdr.dialog_token++;
1176 puser->hdr.validity_interval = 0xf;
1177 if (cand_list > 0)
1178 puser->hdr.req_mode |= \
1179 PREFERRED_CANDIDATE_LIST_INCLUDED;
1180
1181
1182 puser->hdr.disassoc_timer = \
1183 disassoc_timer;
1184
1185 if ((puser->hdr.req_mode & \
1186 DISASSOC_IMMINENT) == \
1187 DISASSOC_IMMINENT) {
1188 rtw_mbo_reset_user_btm_req_preference(padapter);
1189 if (pmbo_attr->delay == 0)
1190 pmbo_attr->delay = 1;
1191 if (puser->hdr.disassoc_timer == 0)
1192 puser->hdr.disassoc_timer = 1000;
1193 }
1194
1195 if ((puser->hdr.req_mode & \
1196 BSS_TERMINATION_INCLUDED) == \
1197 BSS_TERMINATION_INCLUDED) {
1198 puser->append_mbo_ie = _FALSE;
1199 }
1200
1201 if (!puser->candidate_cnt) {
1202 struct wnm_btm_cant cant;
1203 _rtw_memset(&cant, 0,
1204 sizeof(cant));
1205 _rtw_memcpy(cant.nb_rpt.bssid,
1206 adapter_mac_addr(padapter),
1207 ETH_ALEN);
1208 cant.nb_rpt.reg_class = 115;
1209 cant.nb_rpt.ch_num = 36;
1210 cant.preference = 0;
1211 rtw_mbo_construct_user_btm_req(
1212 padapter, NULL,
1213 NULL, 0, &cant);
1214 }
1215
1216 rtw_wnm_issue_btm_req(padapter, dst_mac,
1217 &puser->hdr, NULL, 0,
1218 (u8 *)&puser->btm_cants,
1219 puser->candidate_cnt);
1220
1221 if ((puser->hdr.term_duration.duration)
1222 && (puser->hdr.req_mode & \
1223 BSS_TERMINATION_INCLUDED) == \
1224 BSS_TERMINATION_INCLUDED) {
1225 rtw_mbo_disassoc(padapter,
1226 dst_mac, 3, _TRUE);
1227 }
1228
1229 if ((puser->hdr.req_mode & \
1230 DISASSOC_IMMINENT) == \
1231 DISASSOC_IMMINENT) {
1232 rtw_mbo_disassoc(padapter,
1233 dst_mac, 3, _TRUE);
1234 }
1235 } /* end of if (count >= 12) */
1236
1237 break;
1238 case RTW_MBO_TEST_CMD_NB_BSS_ADD:
1239 if (count >= 12) {
1240 u8 bss_str[18];
1241 struct wnm_btm_cant cant;
1242 u32 op, op_ch, perf;
1243
1244 num = sscanf(tmp, "%d %s %u %u %u",
1245 &id, bss_str, &op, &op_ch,
1246 &perf);
1247 if (num < 5)
1248 break;
1249 _rtw_memset(&cant, 0, sizeof(cant));
1250 if (sscanf(bss_str, MAC_SFMT,
1251 MAC_SARG(
1252 cant.nb_rpt.bssid)) != 6) {
1253 break;
1254 }
1255
1256 cant.nb_rpt.reg_class = op;
1257 cant.nb_rpt.ch_num = op_ch;
1258 cant.preference = perf;
1259 rtw_mbo_construct_user_btm_req(
1260 padapter, NULL, NULL, 0,
1261 &cant);
1262 } /* end of if (count >= 12) */
1263 break;
1264 default:
1265 break;
1266 }
1267 }
1268
1269 return count;
1270 }
1271
rtw_mbo_proc_attr_get(struct seq_file * m,void * v)1272 int rtw_mbo_proc_attr_get(
1273 struct seq_file *m, void *v)
1274 {
1275 struct net_device *dev = m->private;
1276 _adapter *padapter = (_adapter *)rtw_netdev_priv(dev);
1277 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
1278 struct mbo_attr_info *pmbo_attr = &(pmlmepriv->mbo_attr);
1279
1280 RTW_PRINT_SEL(m, "AP Capability Indication : %d\n",
1281 pmbo_attr->ap_cap_ind);
1282 RTW_PRINT_SEL(m, "Cellular Data Capabilities : %d\n",
1283 pmbo_attr->cell_data_cap);
1284 RTW_PRINT_SEL(m, "Association Disallowed : %d\n",
1285 pmbo_attr->assoc_disallow);
1286 return 0;
1287 }
1288
rtw_mbo_non_pref_chan_subelem_parsing(_adapter * padapter,u8 * subelem,size_t subelem_len)1289 static void rtw_mbo_non_pref_chan_subelem_parsing(
1290 _adapter *padapter, u8 *subelem, size_t subelem_len)
1291 {
1292 u8 *pnon_pref_chans;
1293 u32 non_pref_chan_offset, op_subelem_len;
1294 u32 oui_offset = 3;
1295 /* wpa_supplicant don't apped OUI Type */
1296 u32 oui_type_offset = 0;
1297
1298 RTW_MBO_DUMP("Non-preferred Channel subelem : ",
1299 subelem , subelem_len);
1300
1301 /* Subelem :
1302 Vendor Specific | Length | WFA OUI | OUI Type | MBO Attributes */
1303 non_pref_chan_offset = 2 + oui_offset + oui_type_offset;
1304 pnon_pref_chans = subelem + non_pref_chan_offset;
1305 op_subelem_len = subelem_len - non_pref_chan_offset;
1306
1307 /* wpa_supplicant don't indicate non_pref_chan length,
1308 so we cannot get how many non_pref_chan in a wnm notification */
1309 RTW_MBO_DUMP("Non-preferred Channel : ",
1310 pnon_pref_chans, op_subelem_len);
1311 }
1312
rtw_mbo_wnm_notification_parsing(_adapter * padapter,const u8 * pdata,size_t data_len)1313 void rtw_mbo_wnm_notification_parsing(
1314 _adapter *padapter, const u8 *pdata, size_t data_len)
1315 {
1316 u8 *paction;
1317 u8 category, action, dialog, type;
1318 u32 len;
1319
1320 if ((pdata == NULL) || (data_len == 0))
1321 return;
1322
1323 RTW_MBO_DUMP("WNM notification data : ", pdata, data_len);
1324 paction = (u8 *)pdata + sizeof(struct rtw_ieee80211_hdr_3addr);
1325 category = paction[0];
1326 action = paction[1];
1327 dialog = paction[2];
1328 type = paction[3];
1329
1330 if ((action == RTW_WLAN_ACTION_WNM_NOTIF_REQ) &&
1331 (type == WLAN_EID_VENDOR_SPECIFIC)) {
1332 rtw_mbo_non_pref_chan_subelem_parsing(padapter, &paction[4],
1333 (data_len - sizeof(struct rtw_ieee80211_hdr_3addr)));
1334 }
1335 }
1336
rtw_mbo_build_wnm_notification(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)1337 void rtw_mbo_build_wnm_notification(
1338 _adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib)
1339 {
1340 struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
1341 struct npref_ch_rtp *prpt = &(prfctl->ch_rtp);
1342 struct npref_ch* pch;
1343 u8 subelem_id = WLAN_EID_VENDOR_SPECIFIC;
1344 u8 non_pref_ch_oui[] = {0x50, 0x6F, 0x9A, 0x2};
1345 u8 cell_data_cap_oui[] = {0x50, 0x6F, 0x9A, 0x3};
1346 u8 cell_data_con = rtw_mbo_cell_data_conn;
1347 u8 len, cell_data_con_len = 0, *pcont = *pframe;
1348 int i;
1349
1350 if (rtw_mbo_cell_data_conn > 0) {
1351 len = 0x5;
1352 *pframe = rtw_mbo_set_1byte_ie(*pframe,
1353 &subelem_id, &(pattrib->pktlen));
1354 *pframe = rtw_mbo_set_1byte_ie(*pframe,
1355 &len, &(pattrib->pktlen));
1356 *pframe = rtw_mbo_set_4byte_ie(*pframe,
1357 cell_data_cap_oui, &(pattrib->pktlen));
1358 *pframe = rtw_mbo_set_1byte_ie(*pframe,
1359 &cell_data_con, &(pattrib->pktlen));
1360 RTW_MBO_INFO("%s : Cellular Data Capabilities subelemen\n",
1361 __func__);
1362 RTW_MBO_DUMP(":", pcont, len + 2);
1363 pcont += len + 2 ;
1364 }
1365
1366 if (prpt->nm_of_rpt == 0) {
1367 len = 0x4;
1368 *pframe = rtw_mbo_set_1byte_ie(*pframe,
1369 &subelem_id, &(pattrib->pktlen));
1370 *pframe = rtw_mbo_set_1byte_ie(*pframe,
1371 &len, &(pattrib->pktlen));
1372 *pframe = rtw_mbo_set_4byte_ie(*pframe,
1373 non_pref_ch_oui, &(pattrib->pktlen));
1374 RTW_MBO_INFO("%s :Non-preferred Channel Report subelement"
1375 " without data\n", __func__);
1376 return;
1377 }
1378
1379 for (i=0; i < prpt->nm_of_rpt; i++) {
1380 pch = &prpt->ch_rpt[i];
1381 /* OUI(3B) + OUT-type(1B) + op-class(1B) + ch list(nB)
1382 + Preference(1B) + reason(1B) */
1383 len = pch->nm_of_ch + 7;
1384 *pframe = rtw_mbo_set_1byte_ie(*pframe,
1385 &subelem_id, &(pattrib->pktlen));
1386 *pframe = rtw_mbo_set_1byte_ie(*pframe,
1387 &len, &(pattrib->pktlen));
1388 *pframe = rtw_mbo_set_4byte_ie(*pframe,
1389 non_pref_ch_oui, &(pattrib->pktlen));
1390 *pframe = rtw_mbo_set_1byte_ie(*pframe,
1391 &pch->op_class, &(pattrib->pktlen));
1392 *pframe = rtw_mbo_set_nbyte_ie(*pframe,
1393 pch->nm_of_ch, pch->chs, &(pattrib->pktlen));
1394 *pframe = rtw_mbo_set_1byte_ie(*pframe,
1395 &pch->preference, &(pattrib->pktlen));
1396 *pframe = rtw_mbo_set_1byte_ie(*pframe,
1397 &pch->reason, &(pattrib->pktlen));
1398 RTW_MBO_INFO("%s :Non-preferred Channel Report"
1399 " subelement\n", __func__);
1400 RTW_MBO_DUMP(":", pcont, len);
1401 pcont = *pframe;
1402 }
1403 }
1404
rtw_mbo_build_probe_req_ies(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)1405 void rtw_mbo_build_probe_req_ies(
1406 _adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib)
1407 {
1408 u32 len =0;
1409
1410 rtw_mbo_build_extended_cap(padapter, pframe, pattrib);
1411
1412 len = rtw_mbo_attr_sz_get(padapter, RTW_MBO_ATTR_CELL_DATA_CAP_ID);
1413 if ((len == 0) || (len > 3)) {
1414 RTW_ERR("MBO : build Cellular Data Capabilities"
1415 " attribute fail(len=%u)\n", len);
1416 return;
1417 }
1418
1419 rtw_mbo_build_mbo_ie_hdr(pframe, pattrib, len);
1420 rtw_mbo_build_cell_data_cap_attr(padapter, pframe, pattrib);
1421 }
1422
rtw_mbo_build_assoc_req_ies(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)1423 void rtw_mbo_build_assoc_req_ies(
1424 _adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib)
1425 {
1426 u32 len = 0;
1427
1428 rtw_mbo_build_supp_op_class_elem(padapter, pframe, pattrib);
1429
1430 len += rtw_mbo_attr_sz_get(padapter, RTW_MBO_ATTR_CELL_DATA_CAP_ID);
1431 len += rtw_mbo_attr_sz_get(padapter, RTW_MBO_ATTR_NPREF_CH_RPT_ID);
1432 if ((len == 0)|| (len < 3)) {
1433 RTW_ERR("MBO : build assoc MBO IE fail(len=%u)\n", len);
1434 return;
1435 }
1436
1437 rtw_mbo_build_mbo_ie_hdr(pframe, pattrib, len);
1438 rtw_mbo_build_cell_data_cap_attr(padapter, pframe, pattrib);
1439 rtw_mbo_build_npref_ch_rpt_attr(padapter, pframe, pattrib);
1440 }
1441
rtw_mbo_build_ap_capability(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)1442 static void rtw_mbo_build_ap_capability(
1443 _adapter *padapter, u8 **pframe,
1444 struct pkt_attrib *pattrib)
1445 {
1446 struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
1447 struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
1448 WLAN_BSSID_EX *cur_network = &(pmlmeinfo->network);
1449 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
1450 struct mbo_attr_info *pmbo_attr = &(pmlmepriv->mbo_attr);
1451 u8 *pcap = NULL;
1452 u32 cap_len = 0, flen = 0;
1453
1454 if ((pcap = rtw_mbo_attrs_get(
1455 (cur_network->IEs + _FIXED_IE_LENGTH_),
1456 (cur_network->IELength - _FIXED_IE_LENGTH_),
1457 RTW_MBO_ATTR_AP_CAP_ID, &cap_len, 0)) != NULL)
1458 return;
1459
1460 flen += rtw_mbo_attr_sz_get(padapter, RTW_MBO_ATTR_AP_CAP_ID);
1461 if (pmbo_attr->assoc_disallow > 0)
1462 flen += rtw_mbo_attr_sz_get(padapter,
1463 RTW_MBO_ATTR_ASSOC_DISABLED_ID);
1464 if (flen > 0) {
1465 rtw_mbo_build_mbo_ie_hdr(pframe, pattrib, flen);
1466 rtw_mbo_build_ap_cap_Indication_attr(padapter, pframe,
1467 pattrib, pmbo_attr->ap_cap_ind);
1468
1469 if (pmbo_attr->assoc_disallow > 0) {
1470 rtw_mbo_build_ap_disallowed_attr(padapter, pframe,
1471 pattrib, pmbo_attr->assoc_disallow);
1472 }
1473 } /* end of if (flen > 0) */
1474
1475 }
1476
rtw_mbo_attr_info_init(_adapter * padapter)1477 void rtw_mbo_attr_info_init(_adapter *padapter)
1478 {
1479 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
1480 struct mbo_attr_info *pmbo_attr = &(pmlmepriv->mbo_attr);
1481
1482 _rtw_memset(pmbo_attr, 0, sizeof(struct mbo_attr_info));
1483 }
1484
rtw_mbo_process_assoc_req(_adapter * padapter,u8 * pie,int ie_len)1485 void rtw_mbo_process_assoc_req(
1486 _adapter *padapter, u8 *pie, int ie_len)
1487 {
1488 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
1489 struct mbo_attr_info *pmbo_attr = &(pmlmepriv->mbo_attr);
1490 u8 non_pref_ch = 0, ch_op_pref = 1;
1491 u8 *pattr = NULL, *ptr = NULL;
1492 u32 attr_len = 0, ch_nums = 0;
1493 int i, j;
1494
1495 if (!pie || !ie_len)
1496 return;
1497
1498 if ((pattr = rtw_mbo_attrs_get(pie, ie_len,
1499 RTW_MBO_ATTR_NPREF_CH_RPT_ID, &attr_len, 1)) == NULL)
1500 return;
1501
1502 if (attr_len < 3)
1503 return;
1504
1505 ch_nums = (attr_len - 3);
1506
1507 /* shfit to non-preferred ch rpt field */
1508 ptr = pattr + 3;
1509 RTW_MBO_DUMP("non-preferred ch rpt :", ptr, ch_nums);
1510
1511 ch_op_pref = *(ptr + ch_nums);
1512 RTW_MBO_INFO("%s : ch_op_pref=0x%02x\n", __func__, ch_op_pref);
1513
1514 if (ch_op_pref >= 2) {
1515 RTW_MBO_INFO("%s : unknow ch operating preference(0x%02x)\n",
1516 __func__, ch_op_pref);
1517 return;
1518 }
1519
1520 for (i = 0; i < ch_nums; i++) {
1521 if (i >= RTW_MBO_MAX_CH_LIST_NUM)
1522 break;
1523 non_pref_ch = *(ptr + i);
1524 RTW_MBO_INFO("%s : non-pref ch %u found in assoc-req\n",
1525 __func__, non_pref_ch);
1526
1527 if (rtw_mbo_wifi_spec_test(padapter)) {
1528 struct mbo_user_btm_req_pkt *puser_raw = \
1529 &(pmbo_attr->user_raw);
1530 struct wnm_btm_cant *pcant = NULL;
1531
1532 for (j = 0; j < RTW_MAX_NB_RPT_NUM; j++) {
1533 pcant = &puser_raw->btm_cants[j];
1534 if ((pcant->nb_rpt.ch_num == non_pref_ch) && \
1535 (ch_op_pref == 0)) {
1536 RTW_MBO_INFO("%s : reset "
1537 "preference(%u) for non-preference ch\n",
1538 __func__, pcant->preference);
1539 pcant->preference = 0;
1540 }
1541 }
1542 }
1543
1544 } /* end of for-loop ch_nums */
1545
1546 }
1547
rtw_mbo_build_beacon_ies(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)1548 void rtw_mbo_build_beacon_ies(
1549 _adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib)
1550 {
1551 rtw_mbo_build_ap_capability(padapter, pframe, pattrib);
1552 }
1553
rtw_mbo_build_probe_rsp_ies(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)1554 void rtw_mbo_build_probe_rsp_ies(
1555 _adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib)
1556 {
1557 rtw_mbo_build_ap_capability(padapter, pframe, pattrib);
1558 }
1559
rtw_mbo_build_assoc_rsp_ies(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)1560 void rtw_mbo_build_assoc_rsp_ies(
1561 _adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib)
1562 {
1563 struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
1564 struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
1565 WLAN_BSSID_EX *cur_network = &(pmlmeinfo->network);
1566 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
1567 struct mbo_attr_info *pmbo_attr = &(pmlmepriv->mbo_attr);
1568 u8 *pcap = NULL;
1569 u32 len = 0, cap_len = 0 ;
1570
1571 #if 1
1572 if ((pcap = rtw_mbo_attrs_get(
1573 (cur_network->IEs + _FIXED_IE_LENGTH_),
1574 (cur_network->IELength - _FIXED_IE_LENGTH_),
1575 RTW_MBO_ATTR_AP_CAP_ID, &cap_len, 0)) != NULL)
1576 return;
1577 #endif
1578
1579 len += rtw_mbo_attr_sz_get(padapter, RTW_MBO_ATTR_AP_CAP_ID);
1580 if (pmbo_attr->assoc_disallow > 0)
1581 len += rtw_mbo_attr_sz_get(padapter,
1582 RTW_MBO_ATTR_ASSOC_DISABLED_ID);
1583
1584 if (len > 0) {
1585 rtw_mbo_build_mbo_ie_hdr(pframe, pattrib, len);
1586 rtw_mbo_build_ap_cap_Indication_attr(
1587 padapter, pframe, pattrib, pmbo_attr->ap_cap_ind);
1588 if (pmbo_attr->assoc_disallow > 0) {
1589 rtw_mbo_build_ap_disallowed_attr(padapter, pframe,
1590 pattrib, pmbo_attr->assoc_disallow);
1591 }
1592 }
1593
1594 }
1595
rtw_mbo_build_wnm_btmreq_reason_ies(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)1596 void rtw_mbo_build_wnm_btmreq_reason_ies(
1597 _adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib)
1598 {
1599 struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
1600 struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
1601 WLAN_BSSID_EX *cur_network = &(pmlmeinfo->network);
1602 struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
1603 struct mbo_attr_info *pmbo_attr = &(pmlmepriv->mbo_attr);
1604 struct mbo_user_btm_req_pkt *puser = &(pmbo_attr->user_raw);
1605 u8 *pcap = NULL;
1606 u32 len = 0, cap_len = 0 ;
1607
1608 if (!puser->append_mbo_ie)
1609 return;
1610
1611 len += rtw_mbo_attr_sz_get(padapter, RTW_MBO_ATTR_AP_CAP_ID);
1612 len += rtw_mbo_attr_sz_get(padapter, RTW_MBO_ATTR_TRANS_RES_ID);
1613 len += rtw_mbo_attr_sz_get(padapter, RTW_MBO_ATTR_ASSOC_RETRY_DELAY_ID);
1614
1615 rtw_mbo_build_mbo_ie_hdr(pframe, pattrib, len);
1616
1617 rtw_mbo_build_ap_cap_Indication_attr(
1618 padapter, pframe, pattrib, pmbo_attr->ap_cap_ind);
1619
1620 rtw_mbo_build_ap_trans_reason_attr(padapter, pframe,
1621 pattrib, pmbo_attr->reason);
1622
1623 rtw_mbo_build_ap_assoc_retry_delay_attr(padapter, pframe,
1624 pattrib, pmbo_attr->delay);
1625 }
1626 #endif /* CONFIG_RTW_MBO */
1627