xref: /OK3568_Linux_fs/external/rkwifibt/drivers/rtl8188fu/core/rtw_mbo.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 #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_4byte_ie(p, v, l)	\
55 	rtw_set_fixed_ie((p), 4, (v), (l))
56 
57 #define rtw_mbo_set_nbyte_ie(p, sz, v, l)	\
58 	rtw_set_fixed_ie((p), (sz), (v), (l))
59 
60 #define rtw_mbo_subfield_set(p, offset, val) (*(p + offset) = val)
61 
62 #define rtw_mbo_subfields_set(p, offset, buf, len)	\
63 	do {	\
64 		u32 _offset = 0;	\
65 		u8 *_p = p + offset;	\
66 		while(_offset < len) {	\
67 			*(_p + _offset) = *(buf + _offset);	\
68 			_offset++;	\
69 		}	\
70 	} while(0)
71 
rtw_mbo_ie_get(u8 * pie,u32 * plen,u32 limit)72 static u8 *rtw_mbo_ie_get(u8 *pie, u32 *plen, u32 limit)
73 {
74 	const u8 *p = pie;
75 	u32 tmp, i;
76 
77 	if (limit <= 1)
78 		return NULL;
79 
80 	i = 0;
81 	*plen = 0;
82 	while (1) {
83 		if ((*p == _VENDOR_SPECIFIC_IE_) &&
84 			(_rtw_memcmp(rtw_mbo_get_oui(p), wfa_mbo_oui, 4))) {
85 			*plen = *(p + 1);
86 			RTW_MBO_DUMP("VENDOR_SPECIFIC_IE MBO: ", p, *(p + 1));
87 			return (u8 *)p;
88 		} else {
89 			tmp = *(p + 1);
90 			p += (tmp + 2);
91 			i += (tmp + 2);
92 		}
93 
94 		if (i >= limit)
95 			break;
96 	}
97 
98 	return NULL;
99 }
100 
rtw_mbo_attrs_get(u8 * pie,u32 limit,u8 attr_id,u32 * attr_len)101 static u8 *rtw_mbo_attrs_get(u8 *pie, u32 limit, u8 attr_id, u32 *attr_len)
102 {
103 	u8 *p = NULL;
104 	u32 offset, plen = 0;
105 
106 	if ((pie == NULL) || (limit <= 1))
107 		goto exit;
108 
109 	if ((p = rtw_mbo_ie_get(pie, &plen, limit)) == NULL)
110 		goto exit;
111 
112 	/* shift 2 + OUI size and move to attributes content */
113 	p = p + 2 + sizeof(wfa_mbo_oui);
114 	plen = plen - 4;
115 	RTW_MBO_DUMP("Attributes contents: ", p, plen);
116 
117 	if ((p = rtw_get_ie(p, attr_id, attr_len, plen)) == NULL)
118 		goto exit;
119 
120 	RTW_MBO_INFO("%s : id=%u(len=%u)\n", __func__, attr_id, *attr_len);
121 	RTW_MBO_DUMP("contents : ", p, *attr_len);
122 
123 exit:
124 	return p;
125 
126 }
127 
rtw_mbo_attr_sz_get(_adapter * padapter,u8 id)128 static u32 rtw_mbo_attr_sz_get(
129 	_adapter *padapter, u8 id)
130 {
131 	u32 len = 0;
132 
133 	switch (id) {
134 		case RTW_MBO_ATTR_NPREF_CH_RPT_ID:
135 			{
136 				struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
137 				struct npref_ch_rtp *prpt = &(prfctl->ch_rtp);
138 				struct npref_ch* pch;
139 				u32 i, attr_len, offset;
140 
141 				for (i=0; i < prpt->nm_of_rpt; i++) {
142 					pch = &prpt->ch_rpt[i];
143 					/*attr_len = ch list + op class + preference + reason */
144 					attr_len = pch->nm_of_ch + 3;
145 					/* offset = id + len field + attr_len */
146 					offset = attr_len + 2;
147 					len += offset;
148 				}
149 			}
150 			break;
151 		case RTW_MBO_ATTR_CELL_DATA_CAP_ID:
152 		case RTW_MBO_ATTR_TRANS_REJ_ID:
153 			len = 3;
154 			break;
155 		default:
156 			break;
157 	}
158 
159 	return len;
160 }
161 
rtw_mbo_build_mbo_ie_hdr(u8 ** pframe,struct pkt_attrib * pattrib,u8 payload_len)162 static void rtw_mbo_build_mbo_ie_hdr(
163 	u8 **pframe, struct pkt_attrib *pattrib, u8 payload_len)
164 {
165 	u8 eid = RTW_MBO_EID;
166 	u8 len = payload_len + 4;
167 
168 	*pframe = rtw_mbo_set_1byte_ie(*pframe, &eid, &(pattrib->pktlen));
169 	*pframe = rtw_mbo_set_1byte_ie(*pframe, &len, &(pattrib->pktlen));
170 	*pframe = rtw_mbo_set_4byte_ie(*pframe, wfa_mbo_oui, &(pattrib->pktlen));
171 }
172 
rtw_mbo_build_cell_data_cap_attr(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)173 void rtw_mbo_build_cell_data_cap_attr(
174 	_adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib)
175 {
176 	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
177 	u8 attr_id = RTW_MBO_ATTR_CELL_DATA_CAP_ID;
178 	u8 attr_len = 1;
179 	u8 cell_data_con = rtw_mbo_cell_data_conn;
180 
181 	/* used Cellular Data Capabilities from supplicant */
182 	if (!rtw_mbo_wifi_logo_test(padapter) &&
183 		pmlmepriv->pcell_data_cap_ie && pmlmepriv->cell_data_cap_len == 1) {
184 		cell_data_con = *pmlmepriv->pcell_data_cap_ie;
185 		RTW_MBO_INFO("%s : used Cellular Data Capabilities(%u) from supplicant!\n",
186 			__func__, *pmlmepriv->pcell_data_cap_ie);
187 	}
188 
189 	*pframe = rtw_mbo_set_1byte_ie(*pframe, &attr_id, &(pattrib->pktlen));
190 	*pframe = rtw_mbo_set_1byte_ie(*pframe, &attr_len, &(pattrib->pktlen));
191 	*pframe = rtw_mbo_set_1byte_ie(*pframe, &cell_data_con, &(pattrib->pktlen));
192 }
193 
rtw_mbo_update_cell_data_cap(_adapter * padapter,u8 * pie,u32 ie_len)194 static void rtw_mbo_update_cell_data_cap(
195 	_adapter *padapter, u8 *pie, u32 ie_len)
196 {
197 	struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
198 	u8 *mbo_attr;
199 	u32	mbo_attrlen;
200 
201 	if ((pie == NULL) || (ie_len == 0))
202 		return;
203 
204 	mbo_attr = rtw_mbo_attrs_get(pie, ie_len,
205 		RTW_MBO_ATTR_CELL_DATA_CAP_ID, &mbo_attrlen);
206 
207 	if ((mbo_attr == NULL) || (mbo_attrlen == 0) ) {
208 		RTW_INFO("MBO : Cellular Data Capabilities not found!\n");
209 		return;
210 	}
211 
212 	rtw_buf_update(&pmlmepriv->pcell_data_cap_ie,
213 		&pmlmepriv->cell_data_cap_len, (mbo_attr + 2), mbo_attrlen);
214 	RTW_MBO_DUMP("rtw_mbo_update_cell_data_cap : ",
215 		pmlmepriv->pcell_data_cap_ie, pmlmepriv->cell_data_cap_len);
216 }
217 
rtw_mbo_update_ie_data(_adapter * padapter,u8 * pie,u32 ie_len)218 void rtw_mbo_update_ie_data(
219 	_adapter *padapter, u8 *pie, u32 ie_len)
220 {
221 	rtw_mbo_update_cell_data_cap(padapter, pie, ie_len);
222 }
223 
rtw_mbo_current_op_class_get(_adapter * padapter)224 static u8 rtw_mbo_current_op_class_get(_adapter *padapter)
225 {
226 	struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
227 	struct p2p_channels *pch_list =  &(prfctl->channel_list);
228 	struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
229 	struct p2p_reg_class *preg_class;
230 	int class_idx, ch_idx;
231 	u8 cur_op_class = 0;
232 
233 	for(class_idx =0; class_idx < pch_list->reg_classes; class_idx++) {
234 		preg_class =  &pch_list->reg_class[class_idx];
235 		for (ch_idx = 0; ch_idx <= preg_class->channels; ch_idx++) {
236 			if (pmlmeext->cur_channel ==  preg_class->channel[ch_idx]) {
237 				cur_op_class = preg_class->reg_class;
238 				RTW_MBO_INFO("%s : current ch : %d, op class : %d\n",
239 					__func__, pmlmeext->cur_channel, cur_op_class);
240 				break;
241 			}
242 		}
243 	}
244 
245 	return cur_op_class;
246 }
247 
rtw_mbo_supp_op_classes_get(_adapter * padapter,u8 * pclasses)248 static void rtw_mbo_supp_op_classes_get(_adapter *padapter, u8 *pclasses)
249 {
250 	struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
251 	struct p2p_channels *pch_list =  &(prfctl->channel_list);
252 	int class_idx;
253 
254 	if (pclasses == NULL)
255 		return;
256 
257 	RTW_MBO_INFO("%s : support op class \n", __func__);
258 	for(class_idx = 0; class_idx < pch_list->reg_classes; class_idx++) {
259 		*(pclasses + class_idx) = pch_list->reg_class[class_idx].reg_class;
260 		RTW_MBO_INFO("%u ,", *(pclasses + class_idx));
261 	}
262 
263 	RTW_MBO_INFO("%s : \n", __func__);
264 }
265 
rtw_mbo_build_supp_op_class_elem(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)266 void rtw_mbo_build_supp_op_class_elem(
267 	_adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib)
268 {
269 	struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
270 	u8 payload[32] = {0};
271 	u8 delimiter_130 = 130;	/*0x82*/
272 	u8 reg_class_nm, len;
273 
274 	if ((reg_class_nm = prfctl->channel_list.reg_classes) == 0)
275 		return;
276 
277 	payload[0] = rtw_mbo_current_op_class_get(padapter);
278 	rtw_mbo_supp_op_classes_get(padapter, &payload[1]);
279 
280 	/* IEEE 802.11 Std Current Operating Class Extension Sequence */
281 	payload[reg_class_nm + 1] = delimiter_130;
282 	payload[reg_class_nm + 2] = 0x00;
283 
284 	RTW_MBO_DUMP("op class :", payload, reg_class_nm);
285 
286 	/* Current Operating Class field + Operating Class field
287 		+ OneHundredAndThirty Delimiter field */
288 	len = reg_class_nm + 3;
289 	*pframe = rtw_set_ie(*pframe, EID_SupRegulatory, len ,
290 					payload, &(pattrib->pktlen));
291 }
292 
rtw_mbo_construct_npref_ch_rpt_attr(_adapter * padapter,u8 * pbuf,u32 buf_len,u32 * plen)293 static u8 rtw_mbo_construct_npref_ch_rpt_attr(
294 	_adapter *padapter, u8 *pbuf, u32 buf_len, u32 *plen)
295 {
296 	struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
297 	struct npref_ch_rtp *prpt = &(prfctl->ch_rtp);
298 	struct npref_ch* pch;
299 	u32 attr_len, offset;
300 	int i;
301 	u8 *p = pbuf;
302 
303 	if (prpt->nm_of_rpt == 0) {
304 		*plen = 0;
305 		return _FALSE;
306 	}
307 
308 	for (i=0; i < prpt->nm_of_rpt; i++) {
309 		pch = &prpt->ch_rpt[i];
310 		/* attr_len = ch list + op class + preference + reason */
311 		attr_len = pch->nm_of_ch + 3;
312 		/* offset = id + len field + attr_len */
313 		offset = attr_len + 2;
314 		rtw_mbo_subfield_set(p, 0, RTW_MBO_ATTR_NPREF_CH_RPT_ID);
315 		rtw_mbo_subfield_set(p, 1, attr_len);
316 		rtw_mbo_subfield_set(p, 2, pch->op_class);
317 		rtw_mbo_subfields_set(p, 3, pch->chs, pch->nm_of_ch);
318 		rtw_mbo_subfield_set(p, (offset - 2), pch->preference);
319 		rtw_mbo_subfield_set(p, (offset - 1), pch->reason);
320 		p +=  offset;
321 		*plen += offset;
322 
323 		if (*plen >=  buf_len) {
324 			RTW_ERR("MBO : construct non-preferred channel report fail!\n");
325 			return _FALSE;
326 		}
327 	}
328 
329 	return _TRUE;
330 }
331 
rtw_mbo_build_npref_ch_rpt_attr(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)332 void rtw_mbo_build_npref_ch_rpt_attr(
333 	_adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib)
334 {
335 	struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
336 	struct npref_ch_rtp *prpt = &(prfctl->ch_rtp);
337 	u32 tmp_sz = 0, body_len = 0;
338 	u8 *ptmp;
339 
340 	tmp_sz = prpt->nm_of_rpt * sizeof(struct npref_ch);
341 	ptmp = rtw_zmalloc(tmp_sz);
342 	if (ptmp == NULL)
343 		return;
344 
345 	if (rtw_mbo_construct_npref_ch_rpt_attr(padapter, ptmp, tmp_sz, &body_len) == _FALSE) {
346 		rtw_mfree(ptmp, tmp_sz);
347 		return;
348 	}
349 
350 	RTW_MBO_DUMP("Non-preferred Channel Report :", ptmp, body_len);
351 	*pframe = rtw_mbo_set_nbyte_ie(*pframe, body_len, ptmp, &(pattrib->pktlen));
352 
353 	rtw_mfree(ptmp, tmp_sz);
354 }
355 
rtw_mbo_build_trans_reject_reason_attr(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib,u8 * pres)356 void rtw_mbo_build_trans_reject_reason_attr(
357 	_adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib, u8 *pres)
358 {
359 	u8 attr_id = RTW_MBO_ATTR_TRANS_REJ_ID;
360 	u8 attr_len = 1;
361 	u32 len = 0;
362 
363 	len = rtw_mbo_attr_sz_get(padapter, RTW_MBO_ATTR_TRANS_REJ_ID);
364 	if ((len == 0) || (len > 3)) {
365 		RTW_ERR("MBO : build Transition Rejection Reason  attribute fail(len=%u)\n", len);
366 		return;
367 	}
368 
369 	rtw_mbo_build_mbo_ie_hdr(pframe, pattrib, len);
370 	*pframe = rtw_mbo_set_1byte_ie(*pframe, &attr_id, &(pattrib->pktlen));
371 	*pframe = rtw_mbo_set_1byte_ie(*pframe, &attr_len, &(pattrib->pktlen));
372 	*pframe = rtw_mbo_set_1byte_ie(*pframe, pres, &(pattrib->pktlen));
373 }
374 
rtw_mbo_disallowed_network(struct wlan_network * pnetwork)375 u8 rtw_mbo_disallowed_network(struct wlan_network *pnetwork)
376 {
377 	u8 *p, *attr_id, *res;
378 	u32 attr_len = 0;
379 	u8 disallow = _FALSE;
380 
381 	if (pnetwork == NULL)
382 		goto exit;
383 
384 	p = rtw_mbo_attrs_get(pnetwork->network.IEs,
385 		pnetwork->network.IELength,
386 		RTW_MBO_ATTR_ASSOC_DISABLED_ID,
387 		&attr_len);
388 
389 	if (p == NULL) {
390 		RTW_MBO_INFO("%s :Assoc Disallowed attribute not found!\n",__func__);
391 		goto exit;
392 	}
393 
394 	RTW_MBO_DUMP("Association Disallowed attribute :",p , attr_len + 2);
395 	RTW_INFO("MBO : block "MAC_FMT" assoc disallowed reason %d\n",
396 		MAC_ARG(pnetwork->network.MacAddress), *(rtw_mbo_get_disallow_res(p)));
397 
398 	disallow = _TRUE;
399 exit:
400 	return disallow;
401 }
402 
rtw_mbo_build_exented_cap(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)403 void rtw_mbo_build_exented_cap(
404 	_adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib)
405 {
406 	u8 content[8] = { 0 };
407 
408 	rtw_wnm_set_ext_cap_btm(content, 1);
409 	rtw_mbo_set_ext_cap_internw(content, 1);
410 	*pframe = rtw_set_ie(*pframe,
411 				EID_EXTCapability,
412 				8,
413 				content,
414 				&(pattrib->pktlen));
415 }
416 
rtw_mbo_non_pref_chans_dump(struct npref_ch * pch)417 static void rtw_mbo_non_pref_chans_dump(struct npref_ch* pch)
418 {
419 	int i;
420 	u8 buf[128] = {0};
421 
422 	for (i=0; i < pch->nm_of_ch; i++)
423 		rtw_sprintf(buf, 128, "%s,%d", buf, pch->chs[i]);
424 
425 	RTW_MBO_INFO("%s : op_class=%01x, ch=%s, preference=%d, reason=%d\n",
426 		__func__, pch->op_class, buf, pch->preference, pch->reason);
427 }
428 
rtw_mbo_non_pref_chan_exist(struct npref_ch * pch,u8 ch)429 static u8 rtw_mbo_non_pref_chan_exist(struct npref_ch* pch, u8 ch)
430 {
431 	u32 i;
432 	u8 found = _FALSE;
433 
434 	for (i=0; i < pch->nm_of_ch; i++) {
435 		if (pch->chs[i] == ch) {
436 			found = _TRUE;
437 			break;
438 		}
439 	}
440 
441 	return found;
442 }
443 
rtw_mbo_non_pref_chan_get(_adapter * padapter,u8 op_class,u8 prefe,u8 res)444 static struct npref_ch* rtw_mbo_non_pref_chan_get(
445 	_adapter *padapter, u8 op_class, u8  prefe, u8  res)
446 {
447 	struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
448 	struct npref_ch_rtp *prpt = &(prfctl->ch_rtp);
449 	struct npref_ch* pch = NULL;
450 	int i;
451 
452 	if (prpt->nm_of_rpt == 0)
453 		return pch;
454 
455 	for (i=0; i < prpt->nm_of_rpt; i++) {
456 		if ((prpt->ch_rpt[i].op_class == op_class) &&
457 			(prpt->ch_rpt[i].preference == prefe) &&
458 			(prpt->ch_rpt[i].reason == res)) {
459 			pch = &prpt->ch_rpt[i];
460 			break;
461 		}
462 	}
463 
464 	return pch;
465 }
466 
rtw_mbo_non_pref_chan_set(struct npref_ch * pch,u8 op_class,u8 ch,u8 prefe,u8 res,u8 update)467 static void rtw_mbo_non_pref_chan_set(
468 	struct npref_ch* pch, u8 op_class, u8 ch, u8  prefe, u8  res, u8 update)
469 {
470 	u32 offset = pch->nm_of_ch;
471 
472 	if (update) {
473 		if (rtw_mbo_non_pref_chan_exist(pch, ch) == _FALSE) {
474 			pch->chs[offset] = ch;
475 			pch->nm_of_ch++;
476 		}
477 	} else {
478 		pch->op_class = op_class;
479 		pch->chs[0] = ch;
480 		pch->preference = prefe;
481 		pch->reason = res;
482 		pch->nm_of_ch = 1;
483 	}
484 }
485 
rtw_mbo_non_pref_chans_update(_adapter * padapter,u8 op_class,u8 ch,u8 prefe,u8 res)486 static void  rtw_mbo_non_pref_chans_update(
487 	_adapter *padapter, u8 op_class, u8 ch, u8  prefe, u8  res)
488 {
489 	struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
490 	struct npref_ch_rtp *pch_rpt = &(prfctl->ch_rtp);
491 	struct npref_ch* pch;
492 
493 	if (pch_rpt->nm_of_rpt >= RTW_MBO_MAX_CH_RPT_NUM) {
494 		RTW_ERR("MBO : %d non_pref_chan entries supported!",
495 			RTW_MBO_MAX_CH_RPT_NUM);
496 		return;
497 	}
498 
499 	if (pch_rpt->nm_of_rpt == 0) {
500 		pch = &pch_rpt->ch_rpt[0];
501 		rtw_mbo_non_pref_chan_set(pch, op_class, ch, prefe, res, _FALSE);
502 		pch_rpt->nm_of_rpt = 1;
503 		return;
504 	}
505 
506 	pch = rtw_mbo_non_pref_chan_get(padapter, op_class, prefe, res);
507 	if (pch == NULL) {
508 		pch = &pch_rpt->ch_rpt[pch_rpt->nm_of_rpt];
509 		rtw_mbo_non_pref_chan_set(pch, op_class, ch, prefe, res, _FALSE);
510 		pch_rpt->nm_of_rpt++;
511 	} else
512 		rtw_mbo_non_pref_chan_set(pch, op_class, ch, prefe, res, _TRUE);
513 
514 	rtw_mbo_non_pref_chans_dump(pch);
515 }
516 
rtw_mbo_non_pref_chans_set(_adapter * padapter,char * param,ssize_t sz)517 static void  rtw_mbo_non_pref_chans_set(
518 	_adapter *padapter, char *param, ssize_t sz)
519 {
520 	char *pnext;
521 	u32 op_class, ch, prefe, res;
522 	int i = 0;
523 
524 	do {
525 		pnext = strsep(&param, " ");
526 		if (pnext == NULL)
527 			break;
528 
529 		sscanf(pnext, "%d:%d:%d:%d", &op_class, &ch, &prefe, &res);
530 		rtw_mbo_non_pref_chans_update(padapter, op_class, ch, prefe, res);
531 
532 		if ((i++) > 10) {
533 			RTW_ERR("MBO : overflow %d \n", i);
534 			break;
535 		}
536 
537 	} while(param != '\0');
538 
539 }
540 
rtw_mbo_non_pref_chans_del(_adapter * padapter,char * param,ssize_t sz)541 static void  rtw_mbo_non_pref_chans_del(
542 	_adapter *padapter, char *param, ssize_t sz)
543 {
544 	struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
545 	struct npref_ch_rtp *prpt = &(prfctl->ch_rtp);
546 
547 	RTW_INFO("%s : delete non_pref_chan %s\n", __func__, param);
548 	_rtw_memset(prpt, 0, sizeof(struct npref_ch_rtp));
549 }
550 
rtw_mbo_proc_non_pref_chans_set(struct file * pfile,const char __user * buffer,size_t count,loff_t * pos,void * pdata)551 ssize_t rtw_mbo_proc_non_pref_chans_set(
552 	struct file *pfile, const char __user *buffer,
553 	size_t count, loff_t *pos, void *pdata)
554 {
555 	struct net_device *dev = pdata;
556 	_adapter *padapter = (_adapter *)rtw_netdev_priv(dev);
557 	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
558 	u8 tmp[128] = {0};
559 
560 	if (count < 1)
561 		return -EFAULT;
562 
563 	if (count > sizeof(tmp)) {
564 		rtw_warn_on(1);
565 		return -EFAULT;
566 	}
567 
568 	if (buffer && !copy_from_user(tmp, buffer, count)) {
569 		if (strncmp(tmp, "add", 3) == 0)
570 			rtw_mbo_non_pref_chans_set(padapter, &tmp[4], (count - 4));
571 		else if (strncmp(tmp, "delete", 6) == 0)
572 			rtw_mbo_non_pref_chans_del(padapter, &tmp[7], (count - 7));
573 		else {
574 			RTW_ERR("MBO : Invalid format : echo [add|delete] <oper_class>:<chan>:<preference>:<reason>\n");
575 			return -EFAULT;
576 		}
577 	}
578 
579 #ifdef CONFIG_RTW_WNM
580 	if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE) &&
581 		check_fwstate(pmlmepriv, WIFI_STATION_STATE))
582 		rtw_wnm_issue_action(padapter, RTW_WLAN_ACTION_WNM_NOTIF_REQ, 0, 0);
583 #endif
584 
585 	return count;
586 }
587 
rtw_mbo_proc_non_pref_chans_get(struct seq_file * m,void * v)588 int rtw_mbo_proc_non_pref_chans_get(
589 	struct seq_file *m, void *v)
590 {
591 	struct net_device *dev = m->private;
592 	_adapter *padapter = (_adapter *)rtw_netdev_priv(dev);
593 	struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
594 	struct npref_ch_rtp *prpt = &(prfctl->ch_rtp);
595 	struct npref_ch* pch;
596 	int i,j;
597 	u8 buf[32] = {0};
598 
599 	RTW_PRINT_SEL(m, "op_class                     ch    preference    reason \n");
600 	RTW_PRINT_SEL(m, "=======================================================\n");
601 
602 
603 	if (prpt->nm_of_rpt == 0) {
604 		RTW_PRINT_SEL(m, " empty table \n");
605 		return 0;
606 	}
607 
608 	for (i=0; i < prpt->nm_of_rpt; i++) {
609 		pch = &prpt->ch_rpt[i];
610 		buf[0]='\0';
611 		for (j=0; j < pch->nm_of_ch; j++) {
612 			if (j == 0)
613 				rtw_sprintf(buf, 32, "%02u", pch->chs[j]);
614 			else
615 				rtw_sprintf(buf, 32, "%s,%02u", buf, pch->chs[j]);
616 		}
617 
618 		RTW_PRINT_SEL(m, "    %04u    %20s           %02u        %02u\n",
619 			pch->op_class, buf, pch->preference, pch->reason);
620 	}
621 
622 	return 0;
623 }
624 
rtw_mbo_proc_cell_data_set(struct file * pfile,const char __user * buffer,size_t count,loff_t * pos,void * pdata)625 ssize_t rtw_mbo_proc_cell_data_set(
626 	struct file *pfile, const char __user *buffer,
627 	size_t count, loff_t *pos, void *pdata)
628 {
629 	struct net_device *dev = pdata;
630 	_adapter *padapter = (_adapter *)rtw_netdev_priv(dev);
631 	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
632 	int mbo_cell_data = 0;
633 	u8 tmp[8] = {0};
634 
635 	if (count < 1)
636 		return -EFAULT;
637 
638 	if (count > sizeof(tmp))
639 		return -EFAULT;
640 
641 	if (buffer && !copy_from_user(tmp, buffer, count)) {
642 		int num = sscanf(tmp, "%d", &mbo_cell_data);
643 		if (num == 1) {
644 			rtw_mbo_cell_data_conn = mbo_cell_data;
645 		#ifdef CONFIG_RTW_WNM
646 			if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE) &&
647 				check_fwstate(pmlmepriv, WIFI_STATION_STATE))
648 				rtw_wnm_issue_action(padapter, RTW_WLAN_ACTION_WNM_NOTIF_REQ, 0, 0);
649 		#endif
650 		}
651 	}
652 
653 
654 	return count;
655 }
656 
rtw_mbo_proc_cell_data_get(struct seq_file * m,void * v)657 int rtw_mbo_proc_cell_data_get(
658 	struct seq_file *m, void *v)
659 {
660 #if 0
661 	struct net_device *dev = m->private;
662 	_adapter *adapter = (_adapter *)rtw_netdev_priv(dev);
663 #endif
664 
665 	RTW_PRINT_SEL(m, "Cellular Data Connectivity : %d\n", rtw_mbo_cell_data_conn);
666 	return 0;
667 }
668 
rtw_mbo_non_pref_chan_subelem_parsing(_adapter * padapter,u8 * subelem,size_t subelem_len)669 static void rtw_mbo_non_pref_chan_subelem_parsing(
670 	_adapter *padapter, u8 *subelem, size_t subelem_len)
671 {
672 	u8 *pnon_pref_chans;
673 	u32 non_pref_chan_offset, op_subelem_len;
674 	u32 oui_offset = 3;
675 	/* wpa_supplicant don't apped OUI Type */
676 	u32 oui_type_offset = 0;
677 
678 	RTW_MBO_DUMP("Non-preferred Channel subelem : ", subelem , subelem_len);
679 
680 	/* Subelem :
681 		Vendor Specific | Length | WFA OUI | OUI Type | MBO Attributes */
682 	non_pref_chan_offset = 2 + oui_offset + oui_type_offset;
683 	pnon_pref_chans = subelem + non_pref_chan_offset;
684 	op_subelem_len = subelem_len - non_pref_chan_offset;
685 
686 	/* wpa_supplicant don't indicate non_pref_chan length,
687 		so we cannot get how many non_pref_chan in a wnm notification */
688 	RTW_MBO_DUMP("Non-preferred Channel : ", pnon_pref_chans, op_subelem_len);
689 }
690 
rtw_mbo_wnm_notification_parsing(_adapter * padapter,const u8 * pdata,size_t data_len)691 void rtw_mbo_wnm_notification_parsing(
692 	_adapter *padapter, const u8 *pdata, size_t data_len)
693 {
694 	u8 *paction;
695 	u8 category, action, dialog, type;
696 	u32 len;
697 
698 	if ((pdata == NULL) || (data_len == 0))
699 		return;
700 
701 	RTW_MBO_DUMP("WNM notification data : ", pdata, data_len);
702 	paction = (u8 *)pdata + sizeof(struct rtw_ieee80211_hdr_3addr);
703 	category = paction[0];
704 	action = paction[1];
705 	dialog = paction[2];
706 	type = paction[3];
707 
708 	if ((action == RTW_WLAN_ACTION_WNM_NOTIF_REQ) &&
709 		(type == WLAN_EID_VENDOR_SPECIFIC)) {
710 		rtw_mbo_non_pref_chan_subelem_parsing(padapter, &paction[4],
711 			(data_len - sizeof(struct rtw_ieee80211_hdr_3addr)));
712 	}
713 
714 }
715 
rtw_mbo_build_wnm_notification(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)716 void rtw_mbo_build_wnm_notification(
717 	_adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib)
718 {
719 	struct rf_ctl_t *prfctl = adapter_to_rfctl(padapter);
720 	struct npref_ch_rtp *prpt = &(prfctl->ch_rtp);
721 	struct npref_ch* pch;
722 	u8 subelem_id = WLAN_EID_VENDOR_SPECIFIC;
723 	u8 non_pref_ch_oui[] = {0x50, 0x6F, 0x9A, 0x2};
724 	u8 cell_data_cap_oui[] = {0x50, 0x6F, 0x9A, 0x3};
725 	u8 cell_data_con = rtw_mbo_cell_data_conn;
726 	u8 len, cell_data_con_len = 0, *pcont = *pframe;
727 	int i;
728 
729 	if (rtw_mbo_cell_data_conn > 0) {
730 		len = 0x5;
731 		*pframe = rtw_mbo_set_1byte_ie(*pframe, &subelem_id, &(pattrib->pktlen));
732 		*pframe = rtw_mbo_set_1byte_ie(*pframe, &len, &(pattrib->pktlen));
733 		*pframe = rtw_mbo_set_4byte_ie(*pframe, cell_data_cap_oui, &(pattrib->pktlen));
734 		*pframe = rtw_mbo_set_1byte_ie(*pframe, &cell_data_con, &(pattrib->pktlen));
735 		RTW_MBO_INFO("%s : Cellular Data Capabilities subelemen\n", __func__);
736 		RTW_MBO_DUMP(":", pcont, len + 2);
737 		pcont += len + 2 ;
738 	}
739 
740 	if (prpt->nm_of_rpt == 0) {
741 		len = 0x4;
742 		*pframe = rtw_mbo_set_1byte_ie(*pframe, &subelem_id, &(pattrib->pktlen));
743 		*pframe = rtw_mbo_set_1byte_ie(*pframe, &len, &(pattrib->pktlen));
744 		*pframe = rtw_mbo_set_4byte_ie(*pframe, non_pref_ch_oui, &(pattrib->pktlen));
745 		RTW_MBO_INFO("%s :Non-preferred Channel Report subelement without data\n", __func__);
746 		return;
747 	}
748 
749 	for (i=0; i < prpt->nm_of_rpt; i++) {
750 		pch = &prpt->ch_rpt[i];
751 		/* OUI(3B)  + OUT-type(1B) + op-class(1B) + ch list(nB)
752 			+ Preference(1B) + reason(1B) */
753 		len = pch->nm_of_ch + 7;
754 		*pframe = rtw_mbo_set_1byte_ie(*pframe, &subelem_id, &(pattrib->pktlen));
755 		*pframe = rtw_mbo_set_1byte_ie(*pframe, &len, &(pattrib->pktlen));
756 		*pframe = rtw_mbo_set_4byte_ie(*pframe, non_pref_ch_oui, &(pattrib->pktlen));
757 		*pframe = rtw_mbo_set_1byte_ie(*pframe, &pch->op_class, &(pattrib->pktlen));
758 		*pframe = rtw_mbo_set_nbyte_ie(*pframe, pch->nm_of_ch, pch->chs, &(pattrib->pktlen));
759 		*pframe = rtw_mbo_set_1byte_ie(*pframe, &pch->preference, &(pattrib->pktlen));
760 		*pframe = rtw_mbo_set_1byte_ie(*pframe, &pch->reason, &(pattrib->pktlen));
761 		RTW_MBO_INFO("%s :Non-preferred Channel Report subelement\n", __func__);
762 		RTW_MBO_DUMP(":", pcont, len);
763 		pcont = *pframe;
764 	}
765 }
766 
rtw_mbo_build_probe_req_ies(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)767 void rtw_mbo_build_probe_req_ies(
768 	_adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib)
769 {
770 	u32 len =0;
771 
772 	rtw_mbo_build_exented_cap(padapter, pframe, pattrib);
773 
774 	len = rtw_mbo_attr_sz_get(padapter, RTW_MBO_ATTR_CELL_DATA_CAP_ID);
775 	if ((len == 0) || (len > 3)) {
776 		RTW_ERR("MBO : build Cellular Data Capabilities attribute fail(len=%u)\n", len);
777 		return;
778 	}
779 
780 	rtw_mbo_build_mbo_ie_hdr(pframe, pattrib, len);
781 	rtw_mbo_build_cell_data_cap_attr(padapter, pframe, pattrib);
782 }
783 
rtw_mbo_build_assoc_req_ies(_adapter * padapter,u8 ** pframe,struct pkt_attrib * pattrib)784 void rtw_mbo_build_assoc_req_ies(
785 	_adapter *padapter, u8 **pframe, struct pkt_attrib *pattrib)
786 {
787 	u32 len = 0;
788 
789 	rtw_mbo_build_supp_op_class_elem(padapter, pframe, pattrib);
790 
791 	len += rtw_mbo_attr_sz_get(padapter, RTW_MBO_ATTR_CELL_DATA_CAP_ID);
792 	len += rtw_mbo_attr_sz_get(padapter, RTW_MBO_ATTR_NPREF_CH_RPT_ID);
793 	if ((len == 0)|| (len < 3)) {
794 		RTW_ERR("MBO : build assoc MBO IE fail(len=%u)\n", len);
795 		return;
796 	}
797 
798 	rtw_mbo_build_mbo_ie_hdr(pframe, pattrib, len);
799 	rtw_mbo_build_cell_data_cap_attr(padapter, pframe, pattrib);
800 	rtw_mbo_build_npref_ch_rpt_attr(padapter, pframe, pattrib);
801 }
802 
803 #endif /* CONFIG_RTW_MBO */
804