xref: /wlan-dirver/qca-wifi-host-cmn/umac/regulatory/core/src/reg_callbacks.c (revision 901120c066e139c7f8a2c8e4820561fdd83c67ef)
1 /*
2  * Copyright (c) 2014-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022 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: reg_callbacks.c
22  * This file defines regulatory callback functions
23  */
24 
25 #include <wlan_cmn.h>
26 #include <reg_services_public_struct.h>
27 #include <wlan_objmgr_psoc_obj.h>
28 #include <wlan_objmgr_pdev_obj.h>
29 #include "reg_priv_objs.h"
30 #include "reg_utils.h"
31 #include <scheduler_api.h>
32 #include "reg_callbacks.h"
33 #include "reg_services_common.h"
34 #include "reg_build_chan_list.h"
35 
36 /**
37  * reg_call_chan_change_cbks() - Call registered callback functions on channel
38  * change.
39  * @psoc: Pointer to global psoc structure.
40  * @pdev: Pointer to global pdev structure.
41  * @ch_avoid_ind: if chan avoid indicated
42  * @avoid_info: chan avoid info if @ch_avoid_ind is true
43  */
44 static void reg_call_chan_change_cbks(struct wlan_objmgr_psoc *psoc,
45 				      struct wlan_objmgr_pdev *pdev,
46 				      bool ch_avoid_ind,
47 				      struct avoid_freq_ind_data *avoid_info)
48 {
49 	struct chan_change_cbk_entry *cbk_list;
50 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
51 	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
52 	struct regulatory_channel *cur_chan_list;
53 	uint32_t ctr;
54 	struct avoid_freq_ind_data *avoid_freq_ind = NULL;
55 	reg_chan_change_callback callback;
56 	QDF_STATUS status;
57 
58 	psoc_priv_obj = reg_get_psoc_obj(psoc);
59 	if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) {
60 		reg_alert("psoc reg component is NULL");
61 		return;
62 	}
63 
64 	pdev_priv_obj = reg_get_pdev_obj(pdev);
65 	if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
66 		reg_alert("pdev reg component is NULL");
67 		return;
68 	}
69 
70 	cur_chan_list = qdf_mem_malloc(NUM_CHANNELS * sizeof(*cur_chan_list));
71 	if (!cur_chan_list)
72 		return;
73 
74 	status = reg_get_pwrmode_chan_list(pdev, cur_chan_list,
75 					   REG_CURRENT_PWR_MODE);
76 	if (!QDF_IS_STATUS_SUCCESS(status)) {
77 		qdf_mem_free(cur_chan_list);
78 		return;
79 	}
80 	if (ch_avoid_ind)
81 		avoid_freq_ind = avoid_info;
82 
83 	cbk_list = psoc_priv_obj->cbk_list;
84 
85 	for (ctr = 0; ctr < REG_MAX_CHAN_CHANGE_CBKS; ctr++) {
86 		callback  = NULL;
87 		qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock);
88 		if (cbk_list[ctr].cbk)
89 			callback = cbk_list[ctr].cbk;
90 		qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
91 		if (callback)
92 			callback(psoc, pdev, cur_chan_list, avoid_freq_ind,
93 				 cbk_list[ctr].arg);
94 	}
95 	qdf_mem_free(cur_chan_list);
96 }
97 
98 #ifdef FEATURE_WLAN_CH_AVOID_EXT
99 static inline void
100 reg_fill_freq_ext_payload(struct reg_sched_payload **payload,
101 			  struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj)
102 {
103 		(*payload)->ch_avoid_ind =
104 			!!psoc_priv_obj->ch_avoid_ext_ind;
105 		qdf_mem_copy(&(*payload)->avoid_info.freq_list,
106 			     &psoc_priv_obj->avoid_freq_ext_list,
107 			     sizeof(psoc_priv_obj->avoid_freq_ext_list));
108 
109 		psoc_priv_obj->ch_avoid_ext_ind = false;
110 }
111 #else
112 static inline void
113 reg_fill_freq_ext_payload(struct reg_sched_payload **payload,
114 			  struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj)
115 {
116 }
117 #endif
118 
119 /**
120  * reg_alloc_and_fill_payload() - Alloc and fill payload structure.
121  * @psoc: Pointer to global psoc structure.
122  * @pdev: Pointer to global pdev structure.
123  */
124 static void reg_alloc_and_fill_payload(struct wlan_objmgr_psoc *psoc,
125 				       struct wlan_objmgr_pdev *pdev,
126 				       struct reg_sched_payload **payload)
127 {
128 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
129 
130 	psoc_priv_obj = reg_get_psoc_obj(psoc);
131 	if (!psoc_priv_obj) {
132 		reg_err("reg psoc private obj is NULL");
133 		*payload = NULL;
134 		return;
135 	}
136 
137 	*payload = qdf_mem_malloc(sizeof(**payload));
138 	if (*payload) {
139 		(*payload)->psoc = psoc;
140 		(*payload)->pdev = pdev;
141 		if (reg_check_coex_unsafe_nb_user_prefer(psoc)) {
142 			reg_fill_freq_ext_payload(payload, psoc_priv_obj);
143 		} else {
144 			(*payload)->ch_avoid_ind =
145 				!!psoc_priv_obj->ch_avoid_ind;
146 			qdf_mem_copy(&(*payload)->avoid_info.freq_list,
147 				     &psoc_priv_obj->avoid_freq_list,
148 				     sizeof(psoc_priv_obj->avoid_freq_list));
149 
150 			psoc_priv_obj->ch_avoid_ind = false;
151 		}
152 		qdf_mem_copy(&(*payload)->avoid_info.chan_list,
153 			     &psoc_priv_obj->unsafe_chan_list,
154 			     sizeof(psoc_priv_obj->unsafe_chan_list));
155 	}
156 }
157 
158 /**
159  * reg_chan_change_flush_cbk_sb() - Flush south bound channel change callbacks.
160  * @msg: Pointer to scheduler msg structure.
161  */
162 static QDF_STATUS reg_chan_change_flush_cbk_sb(struct scheduler_msg *msg)
163 {
164 	struct reg_sched_payload *load = msg->bodyptr;
165 	struct wlan_objmgr_psoc *psoc = load->psoc;
166 	struct wlan_objmgr_pdev *pdev = load->pdev;
167 
168 	wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID);
169 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
170 	qdf_mem_free(load);
171 
172 	return QDF_STATUS_SUCCESS;
173 }
174 
175 /**
176  * reg_sched_chan_change_cbks_sb() - Schedule south bound channel change
177  * callbacks.
178  * @msg: Pointer to scheduler msg structure.
179  */
180 static QDF_STATUS reg_sched_chan_change_cbks_sb(struct scheduler_msg *msg)
181 {
182 	struct reg_sched_payload *load = msg->bodyptr;
183 	struct wlan_objmgr_psoc *psoc = load->psoc;
184 	struct wlan_objmgr_pdev *pdev = load->pdev;
185 
186 	reg_call_chan_change_cbks(psoc, pdev, load->ch_avoid_ind,
187 				  &load->avoid_info);
188 
189 	wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID);
190 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
191 	qdf_mem_free(load);
192 
193 	return QDF_STATUS_SUCCESS;
194 }
195 
196 /**
197  * reg_chan_change_flush_cbk_nb() - Flush north bound channel change callbacks.
198  * @msg: Pointer to scheduler msg structure.
199  */
200 static QDF_STATUS reg_chan_change_flush_cbk_nb(struct scheduler_msg *msg)
201 {
202 	struct reg_sched_payload *load = msg->bodyptr;
203 	struct wlan_objmgr_psoc *psoc = load->psoc;
204 	struct wlan_objmgr_pdev *pdev = load->pdev;
205 
206 	wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID);
207 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
208 	qdf_mem_free(load);
209 
210 	return QDF_STATUS_SUCCESS;
211 }
212 
213 /**
214  * reg_sched_chan_change_cbks_nb() - Schedule north bound channel change
215  * callbacks.
216  * @msg: Pointer to scheduler msg structure.
217  */
218 static QDF_STATUS reg_sched_chan_change_cbks_nb(struct scheduler_msg *msg)
219 {
220 	struct reg_sched_payload *load = msg->bodyptr;
221 	struct wlan_objmgr_psoc *psoc = load->psoc;
222 	struct wlan_objmgr_pdev *pdev = load->pdev;
223 
224 	reg_call_chan_change_cbks(psoc, pdev, load->ch_avoid_ind,
225 				  &load->avoid_info);
226 
227 	wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID);
228 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
229 	qdf_mem_free(load);
230 
231 	return QDF_STATUS_SUCCESS;
232 }
233 
234 QDF_STATUS reg_send_scheduler_msg_sb(struct wlan_objmgr_psoc *psoc,
235 				     struct wlan_objmgr_pdev *pdev)
236 {
237 	struct scheduler_msg msg = {0};
238 	struct reg_sched_payload *payload;
239 	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
240 	QDF_STATUS status;
241 
242 	pdev_priv_obj = reg_get_pdev_obj(pdev);
243 	if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
244 		reg_alert("pdev reg component is NULL");
245 		return QDF_STATUS_E_FAILURE;
246 	}
247 
248 	if (!pdev_priv_obj->pdev_opened) {
249 		reg_err("hlos not initialized");
250 		return QDF_STATUS_E_FAILURE;
251 	}
252 
253 	if (!pdev_priv_obj->chan_list_recvd) {
254 		reg_err("Empty channel list");
255 		return QDF_STATUS_E_FAILURE;
256 	}
257 
258 	status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_SB_ID);
259 	if (QDF_IS_STATUS_ERROR(status)) {
260 		reg_err("error taking psoc ref cnt");
261 		return status;
262 	}
263 
264 	status = wlan_objmgr_pdev_try_get_ref(pdev, WLAN_REGULATORY_SB_ID);
265 	if (QDF_IS_STATUS_ERROR(status)) {
266 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
267 		reg_err("error taking pdev ref cnt");
268 		return status;
269 	}
270 
271 	reg_alloc_and_fill_payload(psoc, pdev, &payload);
272 	if (!payload) {
273 		reg_err("malloc failed");
274 		wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID);
275 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
276 		return QDF_STATUS_E_NOMEM;
277 	}
278 
279 	msg.bodyptr = payload;
280 	msg.callback = reg_sched_chan_change_cbks_sb;
281 	msg.flush_callback = reg_chan_change_flush_cbk_sb;
282 
283 	status = scheduler_post_message(QDF_MODULE_ID_REGULATORY,
284 					QDF_MODULE_ID_REGULATORY,
285 					QDF_MODULE_ID_TARGET_IF, &msg);
286 	if (QDF_IS_STATUS_ERROR(status)) {
287 		wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID);
288 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
289 		qdf_mem_free(payload);
290 	}
291 
292 	return status;
293 }
294 
295 QDF_STATUS reg_send_scheduler_msg_nb(struct wlan_objmgr_psoc *psoc,
296 				     struct wlan_objmgr_pdev *pdev)
297 {
298 	struct scheduler_msg msg = {0};
299 	struct reg_sched_payload *payload;
300 	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
301 	QDF_STATUS status;
302 
303 	pdev_priv_obj = reg_get_pdev_obj(pdev);
304 	if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
305 		reg_alert("pdev reg component is NULL");
306 		return QDF_STATUS_E_FAILURE;
307 	}
308 
309 	if (!pdev_priv_obj->pdev_opened) {
310 		reg_err("hlos not initialized");
311 		return QDF_STATUS_E_FAILURE;
312 	}
313 
314 	if (!pdev_priv_obj->chan_list_recvd) {
315 		reg_err("Empty channel list");
316 		return QDF_STATUS_E_FAILURE;
317 	}
318 
319 	status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_NB_ID);
320 	if (QDF_IS_STATUS_ERROR(status)) {
321 		reg_err("error taking psoc ref cnt");
322 		return status;
323 	}
324 
325 	status = wlan_objmgr_pdev_try_get_ref(pdev, WLAN_REGULATORY_NB_ID);
326 	if (QDF_IS_STATUS_ERROR(status)) {
327 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
328 		reg_err("error taking pdev ref cnt");
329 		return status;
330 	}
331 
332 	reg_alloc_and_fill_payload(psoc, pdev, &payload);
333 	if (!payload) {
334 		reg_err("malloc failed");
335 		wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID);
336 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
337 		return QDF_STATUS_E_NOMEM;
338 	}
339 	msg.bodyptr = payload;
340 	msg.callback = reg_sched_chan_change_cbks_nb;
341 	msg.flush_callback = reg_chan_change_flush_cbk_nb;
342 
343 	status = scheduler_post_message(QDF_MODULE_ID_REGULATORY,
344 					QDF_MODULE_ID_REGULATORY,
345 					QDF_MODULE_ID_OS_IF, &msg);
346 	if (QDF_IS_STATUS_ERROR(status)) {
347 		wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID);
348 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
349 		qdf_mem_free(payload);
350 	}
351 
352 	return status;
353 }
354 
355 QDF_STATUS reg_notify_sap_event(struct wlan_objmgr_pdev *pdev,
356 				bool sap_state)
357 {
358 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
359 	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
360 	struct wlan_objmgr_psoc *psoc;
361 	QDF_STATUS status;
362 
363 	pdev_priv_obj = reg_get_pdev_obj(pdev);
364 
365 	if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
366 		reg_err("pdev reg component is NULL");
367 		return QDF_STATUS_E_INVAL;
368 	}
369 
370 	psoc = wlan_pdev_get_psoc(pdev);
371 	if (!psoc) {
372 		reg_err("psoc is NULL");
373 		return QDF_STATUS_E_INVAL;
374 	}
375 
376 	psoc_priv_obj = reg_get_psoc_obj(psoc);
377 	if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) {
378 		reg_err("psoc reg component is NULL");
379 		return QDF_STATUS_E_INVAL;
380 	}
381 
382 	reg_info("sap_state: %d", sap_state);
383 
384 	if (pdev_priv_obj->sap_state == sap_state)
385 		return QDF_STATUS_SUCCESS;
386 
387 	pdev_priv_obj->sap_state = sap_state;
388 
389 	reg_compute_pdev_current_chan_list(pdev_priv_obj);
390 	status = reg_send_scheduler_msg_sb(psoc, pdev);
391 
392 	return status;
393 }
394 
395 void reg_register_chan_change_callback(struct wlan_objmgr_psoc *psoc,
396 				       reg_chan_change_callback cbk, void *arg)
397 {
398 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
399 	uint32_t count;
400 
401 	psoc_priv_obj = reg_get_psoc_obj(psoc);
402 	if (!psoc_priv_obj) {
403 		reg_err("reg psoc private obj is NULL");
404 		return;
405 	}
406 
407 	qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock);
408 	for (count = 0; count < REG_MAX_CHAN_CHANGE_CBKS; count++)
409 		if (!psoc_priv_obj->cbk_list[count].cbk) {
410 			psoc_priv_obj->cbk_list[count].cbk = cbk;
411 			psoc_priv_obj->cbk_list[count].arg = arg;
412 			psoc_priv_obj->num_chan_change_cbks++;
413 			break;
414 		}
415 	qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
416 
417 	if (count == REG_MAX_CHAN_CHANGE_CBKS)
418 		reg_err("callback list is full");
419 }
420 
421 void reg_register_ctry_change_callback(struct wlan_objmgr_psoc *psoc,
422 				       reg_ctry_change_callback cbk)
423 {
424 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
425 
426 	psoc_priv_obj = reg_get_psoc_obj(psoc);
427 	if (!psoc_priv_obj) {
428 		reg_err("reg psoc private obj is NULL");
429 		return;
430 	}
431 
432 	qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock);
433 	if (!psoc_priv_obj->cc_cbk.cbk)
434 		psoc_priv_obj->cc_cbk.cbk = cbk;
435 	qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
436 }
437 
438 void reg_unregister_chan_change_callback(struct wlan_objmgr_psoc *psoc,
439 					 reg_chan_change_callback cbk)
440 {
441 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
442 	uint32_t count;
443 
444 	psoc_priv_obj = reg_get_psoc_obj(psoc);
445 	if (!psoc_priv_obj) {
446 		reg_err("reg psoc private obj is NULL");
447 		return;
448 	}
449 
450 	qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock);
451 	for (count = 0; count < REG_MAX_CHAN_CHANGE_CBKS; count++)
452 		if (psoc_priv_obj->cbk_list[count].cbk == cbk) {
453 			psoc_priv_obj->cbk_list[count].cbk = NULL;
454 			psoc_priv_obj->num_chan_change_cbks--;
455 			break;
456 		}
457 	qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
458 
459 	if (count == REG_MAX_CHAN_CHANGE_CBKS)
460 		reg_err("callback not found in the list");
461 }
462 
463 void reg_unregister_ctry_change_callback(struct wlan_objmgr_psoc *psoc,
464 					 reg_ctry_change_callback cbk)
465 {
466 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
467 
468 	psoc_priv_obj = reg_get_psoc_obj(psoc);
469 	if (!psoc_priv_obj) {
470 		reg_err("reg psoc private obj is NULL");
471 		return;
472 	}
473 
474 	qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock);
475 	if (psoc_priv_obj->cc_cbk.cbk == cbk)
476 		psoc_priv_obj->cc_cbk.cbk = NULL;
477 	qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
478 }
479