1 /*
2  * Copyright (c) 2012-2018, 2020 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /**
21  * DOC: Implement various notification handlers which are accessed
22  * internally in action_oui component only.
23  */
24 #include "cfg_ucfg_api.h"
25 #include "wlan_action_oui_cfg.h"
26 #include "wlan_action_oui_main.h"
27 #include "wlan_action_oui_public_struct.h"
28 #include "wlan_action_oui_tgt_api.h"
29 #include "target_if_action_oui.h"
30 
31 /**
32  * action_oui_allocate() - Allocates memory for various actions.
33  * @psoc_priv: pointer to action_oui psoc priv obj
34  *
35  * This function allocates memory for all the action_oui types
36  * and initializes the respective lists to store extensions
37  * extracted from action_oui_extract().
38  *
39  * Return: QDF_STATUS
40  */
41 static QDF_STATUS
action_oui_allocate(struct action_oui_psoc_priv * psoc_priv)42 action_oui_allocate(struct action_oui_psoc_priv *psoc_priv)
43 {
44 	struct action_oui_priv *oui_priv;
45 	uint32_t i;
46 	uint32_t j;
47 
48 	for (i = 0; i < ACTION_OUI_MAXIMUM_ID; i++) {
49 		oui_priv = qdf_mem_malloc(sizeof(*oui_priv));
50 		if (!oui_priv) {
51 			action_oui_err("Mem alloc failed for oui_priv id: %u",
52 					i);
53 			goto free_mem;
54 		}
55 		oui_priv->id = i;
56 		qdf_list_create(&oui_priv->extension_list,
57 				ACTION_OUI_MAX_EXTENSIONS);
58 		qdf_mutex_create(&oui_priv->extension_lock);
59 		psoc_priv->oui_priv[i] = oui_priv;
60 	}
61 
62 	return QDF_STATUS_SUCCESS;
63 
64 free_mem:
65 	for (j = 0; j < i; j++) {
66 		oui_priv = psoc_priv->oui_priv[j];
67 		if (!oui_priv)
68 			continue;
69 
70 		qdf_list_destroy(&oui_priv->extension_list);
71 		qdf_mutex_destroy(&oui_priv->extension_lock);
72 		psoc_priv->oui_priv[j] = NULL;
73 	}
74 
75 	return QDF_STATUS_E_NOMEM;
76 }
77 
78 /**
79  * action_oui_destroy() - Deallocates memory for various actions.
80  * @psoc_priv: pointer to action_oui psoc priv obj
81  *
82  * This function Deallocates memory for all the action_oui types.
83  * As a part of deallocate, all extensions are destroyed.
84  *
85  * Return: None
86  */
87 static void
action_oui_destroy(struct action_oui_psoc_priv * psoc_priv)88 action_oui_destroy(struct action_oui_psoc_priv *psoc_priv)
89 {
90 	struct action_oui_priv *oui_priv;
91 	struct action_oui_extension_priv *ext_priv;
92 	qdf_list_t *ext_list;
93 	QDF_STATUS status;
94 	qdf_list_node_t *node = NULL;
95 	uint32_t i;
96 
97 	psoc_priv->total_extensions = 0;
98 	psoc_priv->max_extensions = 0;
99 	psoc_priv->host_only_extensions = 0;
100 
101 	for (i = 0; i < ACTION_OUI_MAXIMUM_ID; i++) {
102 		oui_priv = psoc_priv->oui_priv[i];
103 		psoc_priv->oui_priv[i] = NULL;
104 		if (!oui_priv)
105 			continue;
106 
107 		ext_list = &oui_priv->extension_list;
108 		qdf_mutex_acquire(&oui_priv->extension_lock);
109 		while (!qdf_list_empty(ext_list)) {
110 			status = qdf_list_remove_front(ext_list, &node);
111 			if (!QDF_IS_STATUS_SUCCESS(status)) {
112 				action_oui_err("Invalid delete in action: %u",
113 						oui_priv->id);
114 				break;
115 			}
116 			ext_priv = qdf_container_of(node,
117 					struct action_oui_extension_priv,
118 					item);
119 			qdf_mem_free(ext_priv);
120 			ext_priv = NULL;
121 		}
122 
123 		qdf_list_destroy(ext_list);
124 		qdf_mutex_release(&oui_priv->extension_lock);
125 		qdf_mutex_destroy(&oui_priv->extension_lock);
126 		qdf_mem_free(oui_priv);
127 		oui_priv = NULL;
128 	}
129 }
130 
action_oui_load_config(struct action_oui_psoc_priv * psoc_priv)131 static void action_oui_load_config(struct action_oui_psoc_priv *psoc_priv)
132 {
133 	struct wlan_objmgr_psoc *psoc = psoc_priv->psoc;
134 
135 	psoc_priv->action_oui_enable =
136 		cfg_get(psoc, CFG_ENABLE_ACTION_OUI);
137 
138 	qdf_str_lcopy(psoc_priv->action_oui_str[ACTION_OUI_CONNECT_1X1],
139 		      cfg_get(psoc, CFG_ACTION_OUI_CONNECT_1X1),
140 		      ACTION_OUI_MAX_STR_LEN);
141 	qdf_str_lcopy(psoc_priv->action_oui_str[ACTION_OUI_ITO_EXTENSION],
142 		      cfg_get(psoc, CFG_ACTION_OUI_ITO_EXTENSION),
143 		      ACTION_OUI_MAX_STR_LEN);
144 	qdf_str_lcopy(psoc_priv->action_oui_str[ACTION_OUI_CCKM_1X1],
145 		      cfg_get(psoc, CFG_ACTION_OUI_CCKM_1X1),
146 		      ACTION_OUI_MAX_STR_LEN);
147 	qdf_str_lcopy(psoc_priv->action_oui_str[ACTION_OUI_ITO_ALTERNATE],
148 		      cfg_get(psoc, CFG_ACTION_OUI_ITO_ALTERNATE),
149 		      ACTION_OUI_MAX_STR_LEN);
150 	qdf_str_lcopy(psoc_priv->action_oui_str[ACTION_OUI_SWITCH_TO_11N_MODE],
151 		      cfg_get(psoc, CFG_ACTION_OUI_SWITCH_TO_11N_MODE),
152 		      ACTION_OUI_MAX_STR_LEN);
153 	qdf_str_lcopy(psoc_priv->action_oui_str[ACTION_OUI_CONNECT_1X1_WITH_1_CHAIN],
154 		      cfg_get(psoc,
155 			      CFG_ACTION_OUI_CONNECT_1X1_WITH_1_CHAIN),
156 		      ACTION_OUI_MAX_STR_LEN);
157 	qdf_str_lcopy(psoc_priv->action_oui_str[ACTION_OUI_DISABLE_AGGRESSIVE_TX],
158 		      cfg_get(psoc,
159 			      CFG_ACTION_OUI_DISABLE_AGGRESSIVE_TX),
160 		      ACTION_OUI_MAX_STR_LEN);
161 	qdf_str_lcopy(psoc_priv->action_oui_str
162 					  [ACTION_OUI_DISABLE_AGGRESSIVE_EDCA],
163 		      cfg_get(psoc,
164 			      CFG_ACTION_OUI_DISABLE_AGGRESSIVE_EDCA),
165 		      ACTION_OUI_MAX_STR_LEN);
166 	qdf_str_lcopy(psoc_priv->action_oui_str[ACTION_OUI_EXTEND_WOW_ITO],
167 		      cfg_get(psoc, CFG_ACTION_OUI_EXTEND_WOW_ITO),
168 		      ACTION_OUI_MAX_STR_LEN);
169 	qdf_str_lcopy(psoc_priv->action_oui_str[ACTION_OUI_DISABLE_TWT],
170 		      cfg_get(psoc, CFG_ACTION_OUI_DISABLE_TWT),
171 		      ACTION_OUI_MAX_STR_LEN);
172 	qdf_str_lcopy(psoc_priv->action_oui_str[ACTION_OUI_HOST_RECONN],
173 		      cfg_get(psoc, CFG_ACTION_OUI_RECONN_ASSOCTIMEOUT),
174 		      ACTION_OUI_MAX_STR_LEN);
175 	qdf_str_lcopy(psoc_priv->action_oui_str[ACTION_OUI_TAKE_ALL_BAND_INFO],
176 		      cfg_get(psoc, CFG_ACTION_OUI_TAKE_ALL_BAND_INFO),
177 		      ACTION_OUI_MAX_STR_LEN);
178 	qdf_str_lcopy(psoc_priv->action_oui_str[ACTION_OUI_11BE_OUI_ALLOW],
179 		      cfg_get(psoc, CFG_ACTION_OUI_11BE_ALLOW_LIST),
180 		      ACTION_OUI_MAX_STR_LEN);
181 	qdf_str_lcopy(psoc_priv->action_oui_str
182 			[ACTION_OUI_DISABLE_DYNAMIC_QOS_NULL_TX_RATE],
183 		      cfg_get(psoc,
184 			      CFG_ACTION_OUI_DISABLE_DYNAMIC_QOS_NULL_TX_RATE),
185 		      ACTION_OUI_MAX_STR_LEN);
186 	qdf_str_lcopy(psoc_priv->action_oui_str
187 			[ACTION_OUI_ENABLE_CTS2SELF_WITH_QOS_NULL],
188 		      cfg_get(psoc,
189 			      CFG_ACTION_OUI_ENABLE_CTS2SELF_WITH_QOS_NULL),
190 		      ACTION_OUI_MAX_STR_LEN);
191 
192 	qdf_str_lcopy(psoc_priv->action_oui_str[ACTION_OUI_ENABLE_CTS2SELF],
193 		      cfg_get(psoc, CFG_ACTION_OUI_ENABLE_CTS2SELF),
194 		      ACTION_OUI_MAX_STR_LEN);
195 
196 	qdf_str_lcopy(psoc_priv->action_oui_str
197 			[ACTION_OUI_SEND_SMPS_FRAME_WITH_OMN],
198 		      cfg_get(psoc,
199 			      CFG_ACTION_OUI_SEND_SMPS_FRAME_WITH_OMN),
200 		      ACTION_OUI_MAX_STR_LEN);
201 	qdf_str_lcopy(psoc_priv->action_oui_str
202 			[ACTION_OUI_RESTRICT_MAX_MLO_LINKS],
203 		      cfg_get(psoc, CFG_ACTION_OUI_RESTRICT_MAX_MLO_LINKS),
204 		      ACTION_OUI_MAX_STR_LEN);
205 	qdf_str_lcopy(psoc_priv->action_oui_str
206 			[ACTION_OUI_AUTH_ASSOC_6MBPS_2GHZ],
207 		      cfg_get(psoc, CFG_ACTION_OUI_AUTH_ASSOC_6MBPS_2GHZ),
208 		      ACTION_OUI_MAX_STR_LEN);
209 	qdf_str_lcopy(psoc_priv->action_oui_str[ACTION_OUI_DISABLE_BFORMEE],
210 		      cfg_get(psoc, CFG_ACTION_OUI_DISABLE_BFORMEE),
211 			      ACTION_OUI_MAX_STR_LEN);
212 	qdf_str_lcopy(psoc_priv->action_oui_str[ACTION_OUI_LIMIT_BW],
213 		      cfg_get(psoc, CFG_ACTION_OUI_LIMIT_BW),
214 			      ACTION_OUI_MAX_STR_LEN);
215 }
216 
action_oui_parse_config(struct wlan_objmgr_psoc * psoc)217 static void action_oui_parse_config(struct wlan_objmgr_psoc *psoc)
218 {
219 	QDF_STATUS status;
220 	uint32_t id;
221 	uint8_t *str;
222 	struct action_oui_psoc_priv *psoc_priv;
223 
224 	if (!psoc) {
225 		action_oui_err("Invalid psoc");
226 		return;
227 	}
228 
229 	psoc_priv = action_oui_psoc_get_priv(psoc);
230 	if (!psoc_priv) {
231 		action_oui_err("psoc priv is NULL");
232 		return;
233 	}
234 	if (!psoc_priv->action_oui_enable) {
235 		action_oui_debug("action_oui is not enable");
236 		return;
237 	}
238 	for (id = 0; id < ACTION_OUI_MAXIMUM_ID; id++) {
239 		str = psoc_priv->action_oui_str[id];
240 		if (!qdf_str_len(str))
241 			continue;
242 
243 		status = action_oui_parse_string(psoc, str, id);
244 		if (!QDF_IS_STATUS_SUCCESS(status))
245 			action_oui_err("Failed to parse action_oui str: %u",
246 				       id);
247 	}
248 
249 	/* FW allocates memory for the extensions only during init time.
250 	 * Therefore, send additional legspace for configuring new
251 	 * extensions during runtime.
252 	 * The current max value is default extensions count + 10.
253 	 */
254 	psoc_priv->max_extensions = psoc_priv->total_extensions -
255 					psoc_priv->host_only_extensions +
256 					ACTION_OUI_MAX_ADDNL_EXTENSIONS;
257 	action_oui_debug("Extensions - Max: %d Total: %d host_only %d",
258 			 psoc_priv->max_extensions, psoc_priv->total_extensions,
259 			 psoc_priv->host_only_extensions);
260 }
261 
action_oui_send_config(struct wlan_objmgr_psoc * psoc)262 static QDF_STATUS action_oui_send_config(struct wlan_objmgr_psoc *psoc)
263 {
264 	struct action_oui_psoc_priv *psoc_priv;
265 	QDF_STATUS status = QDF_STATUS_E_INVAL;
266 	uint32_t id;
267 
268 	if (!psoc) {
269 		action_oui_err("psoc is NULL");
270 		goto exit;
271 	}
272 
273 	psoc_priv = action_oui_psoc_get_priv(psoc);
274 	if (!psoc_priv) {
275 		action_oui_err("psoc priv is NULL");
276 		goto exit;
277 	}
278 	if (!psoc_priv->action_oui_enable) {
279 		action_oui_debug("action_oui is not enable");
280 		return QDF_STATUS_SUCCESS;
281 	}
282 
283 	for (id = 0; id < ACTION_OUI_MAXIMUM_ID; id++) {
284 		if (id >= ACTION_OUI_HOST_ONLY)
285 			continue;
286 		status = action_oui_send(psoc_priv, id);
287 		if (!QDF_IS_STATUS_SUCCESS(status))
288 			action_oui_err("Failed to send: %u", id);
289 	}
290 
291 exit:
292 	return status;
293 }
294 
295 QDF_STATUS
action_oui_psoc_create_notification(struct wlan_objmgr_psoc * psoc,void * arg)296 action_oui_psoc_create_notification(struct wlan_objmgr_psoc *psoc, void *arg)
297 {
298 	struct action_oui_psoc_priv *psoc_priv;
299 	QDF_STATUS status;
300 
301 	ACTION_OUI_ENTER();
302 
303 	psoc_priv = qdf_mem_malloc(sizeof(*psoc_priv));
304 	if (!psoc_priv) {
305 		status = QDF_STATUS_E_NOMEM;
306 		goto exit;
307 	}
308 
309 	status = wlan_objmgr_psoc_component_obj_attach(psoc,
310 				WLAN_UMAC_COMP_ACTION_OUI,
311 				(void *)psoc_priv, QDF_STATUS_SUCCESS);
312 	if (!QDF_IS_STATUS_SUCCESS(status)) {
313 		action_oui_err("Failed to attach priv with psoc");
314 		goto free_psoc_priv;
315 	}
316 
317 	target_if_action_oui_register_tx_ops(&psoc_priv->tx_ops);
318 	psoc_priv->psoc = psoc;
319 	action_oui_load_config(psoc_priv);
320 	action_oui_debug("psoc priv attached");
321 	goto exit;
322 free_psoc_priv:
323 	qdf_mem_free(psoc_priv);
324 	status = QDF_STATUS_E_INVAL;
325 exit:
326 	ACTION_OUI_EXIT();
327 	return status;
328 }
329 
330 QDF_STATUS
action_oui_psoc_destroy_notification(struct wlan_objmgr_psoc * psoc,void * arg)331 action_oui_psoc_destroy_notification(struct wlan_objmgr_psoc *psoc, void *arg)
332 {
333 	struct action_oui_psoc_priv *psoc_priv = NULL;
334 	QDF_STATUS status = QDF_STATUS_E_FAILURE;
335 
336 	ACTION_OUI_ENTER();
337 
338 	psoc_priv = action_oui_psoc_get_priv(psoc);
339 	if (!psoc_priv) {
340 		action_oui_err("psoc priv is NULL");
341 		goto exit;
342 	}
343 
344 	status = wlan_objmgr_psoc_component_obj_detach(psoc,
345 					WLAN_UMAC_COMP_ACTION_OUI,
346 					(void *)psoc_priv);
347 	if (!QDF_IS_STATUS_SUCCESS(status))
348 		action_oui_err("Failed to detach priv with psoc");
349 
350 	qdf_mem_free(psoc_priv);
351 
352 exit:
353 	ACTION_OUI_EXIT();
354 	return status;
355 }
356 
action_oui_psoc_enable(struct wlan_objmgr_psoc * psoc)357 void action_oui_psoc_enable(struct wlan_objmgr_psoc *psoc)
358 {
359 	struct action_oui_psoc_priv *psoc_priv;
360 	QDF_STATUS status = QDF_STATUS_E_FAILURE;
361 
362 	ACTION_OUI_ENTER();
363 
364 	psoc_priv = action_oui_psoc_get_priv(psoc);
365 	if (!psoc_priv) {
366 		action_oui_err("psoc priv is NULL");
367 		goto exit;
368 	}
369 
370 	status = action_oui_allocate(psoc_priv);
371 	if (!QDF_IS_STATUS_SUCCESS(status)) {
372 		action_oui_err("Failed to alloc action_oui");
373 		goto exit;
374 	}
375 	action_oui_parse_config(psoc);
376 	action_oui_send_config(psoc);
377 exit:
378 	ACTION_OUI_EXIT();
379 }
380 
action_oui_psoc_disable(struct wlan_objmgr_psoc * psoc)381 void action_oui_psoc_disable(struct wlan_objmgr_psoc *psoc)
382 {
383 	struct action_oui_psoc_priv *psoc_priv;
384 
385 	ACTION_OUI_ENTER();
386 
387 	psoc_priv = action_oui_psoc_get_priv(psoc);
388 	if (!psoc_priv) {
389 		action_oui_err("psoc priv is NULL");
390 		goto exit;
391 	}
392 
393 	action_oui_destroy(psoc_priv);
394 exit:
395 	ACTION_OUI_EXIT();
396 }
397 
wlan_action_oui_search(struct wlan_objmgr_psoc * psoc,struct action_oui_search_attr * attr,enum action_oui_id action_id)398 bool wlan_action_oui_search(struct wlan_objmgr_psoc *psoc,
399 			    struct action_oui_search_attr *attr,
400 			    enum action_oui_id action_id)
401 {
402 	struct action_oui_psoc_priv *psoc_priv;
403 	bool found = false;
404 
405 	if (!psoc || !attr) {
406 		action_oui_err("Invalid psoc or search attrs");
407 		goto exit;
408 	}
409 
410 	if (action_id >= ACTION_OUI_MAXIMUM_ID) {
411 		action_oui_err("Invalid action_oui id: %u", action_id);
412 		goto exit;
413 	}
414 
415 	psoc_priv = action_oui_psoc_get_priv(psoc);
416 	if (!psoc_priv) {
417 		action_oui_err("psoc priv is NULL");
418 		goto exit;
419 	}
420 
421 	found = action_oui_search(psoc_priv, attr, action_id);
422 
423 exit:
424 	return found;
425 }
426 
427 QDF_STATUS
wlan_action_oui_cleanup(struct action_oui_psoc_priv * psoc_priv,enum action_oui_id action_id)428 wlan_action_oui_cleanup(struct action_oui_psoc_priv *psoc_priv,
429 			enum action_oui_id action_id)
430 {
431 	struct action_oui_priv *oui_priv;
432 	struct action_oui_extension_priv *ext_priv;
433 	qdf_list_t *ext_list;
434 	QDF_STATUS status;
435 	qdf_list_node_t *node = NULL;
436 
437 	if (action_id >= ACTION_OUI_MAXIMUM_ID)
438 		return QDF_STATUS_E_INVAL;
439 
440 	oui_priv = psoc_priv->oui_priv[action_id];
441 	if (!oui_priv)
442 		return QDF_STATUS_SUCCESS;
443 
444 	ext_list = &oui_priv->extension_list;
445 	qdf_mutex_acquire(&oui_priv->extension_lock);
446 	while (!qdf_list_empty(ext_list)) {
447 		status = qdf_list_remove_front(ext_list, &node);
448 		if (!QDF_IS_STATUS_SUCCESS(status)) {
449 			action_oui_err("Invalid delete in action: %u",
450 				       oui_priv->id);
451 			break;
452 		}
453 		ext_priv = qdf_container_of(
454 				node,
455 				struct action_oui_extension_priv,
456 				item);
457 		qdf_mem_free(ext_priv);
458 		ext_priv = NULL;
459 		if (psoc_priv->total_extensions)
460 			psoc_priv->total_extensions--;
461 		else
462 			action_oui_err("unexpected total_extensions 0");
463 
464 		if (action_id >= ACTION_OUI_HOST_ONLY) {
465 			if (!psoc_priv->host_only_extensions)
466 				action_oui_err("unexpected total host extensions");
467 			else
468 				psoc_priv->host_only_extensions--;
469 		}
470 	}
471 	qdf_mutex_release(&oui_priv->extension_lock);
472 
473 	return QDF_STATUS_SUCCESS;
474 }
475 
wlan_action_oui_is_empty(struct wlan_objmgr_psoc * psoc,enum action_oui_id action_id)476 bool wlan_action_oui_is_empty(struct wlan_objmgr_psoc *psoc,
477 			      enum action_oui_id action_id)
478 {
479 	struct action_oui_psoc_priv *psoc_priv;
480 	bool empty = true;
481 
482 	if (!psoc) {
483 		action_oui_err("Invalid psoc");
484 		goto exit;
485 	}
486 
487 	if (action_id >= ACTION_OUI_MAXIMUM_ID) {
488 		action_oui_err("Invalid action_oui id: %u", action_id);
489 		goto exit;
490 	}
491 
492 	psoc_priv = action_oui_psoc_get_priv(psoc);
493 	if (!psoc_priv) {
494 		action_oui_err("psoc priv is NULL");
495 		goto exit;
496 	}
497 
498 	empty = action_oui_is_empty(psoc_priv, action_id);
499 
500 exit:
501 	return empty;
502 }
503 
504