xref: /wlan-driver/qca-wifi-host-cmn/umac/regulatory/core/src/reg_callbacks.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1 /*
2  * Copyright (c) 2014-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022-2023 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  */
reg_call_chan_change_cbks(struct wlan_objmgr_psoc * psoc,struct wlan_objmgr_pdev * pdev,bool ch_avoid_ind,struct avoid_freq_ind_data * avoid_info)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
reg_fill_freq_ext_payload(struct reg_sched_payload ** payload,struct wlan_regulatory_psoc_priv_obj * psoc_priv_obj)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
reg_fill_freq_ext_payload(struct reg_sched_payload ** payload,struct wlan_regulatory_psoc_priv_obj * psoc_priv_obj)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  * @payload: output pointer to allocated payload buffer
124  */
reg_alloc_and_fill_payload(struct wlan_objmgr_psoc * psoc,struct wlan_objmgr_pdev * pdev,struct reg_sched_payload ** payload)125 static void reg_alloc_and_fill_payload(struct wlan_objmgr_psoc *psoc,
126 				       struct wlan_objmgr_pdev *pdev,
127 				       struct reg_sched_payload **payload)
128 {
129 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
130 
131 	psoc_priv_obj = reg_get_psoc_obj(psoc);
132 	if (!psoc_priv_obj) {
133 		reg_err("reg psoc private obj is NULL");
134 		*payload = NULL;
135 		return;
136 	}
137 
138 	*payload = qdf_mem_malloc(sizeof(**payload));
139 	if (*payload) {
140 		(*payload)->psoc = psoc;
141 		(*payload)->pdev = pdev;
142 		if (reg_check_coex_unsafe_nb_user_prefer(psoc)) {
143 			reg_fill_freq_ext_payload(payload, psoc_priv_obj);
144 		} else {
145 			(*payload)->ch_avoid_ind =
146 				!!psoc_priv_obj->ch_avoid_ind;
147 			qdf_mem_copy(&(*payload)->avoid_info.freq_list,
148 				     &psoc_priv_obj->avoid_freq_list,
149 				     sizeof(psoc_priv_obj->avoid_freq_list));
150 
151 			psoc_priv_obj->ch_avoid_ind = false;
152 		}
153 		qdf_mem_copy(&(*payload)->avoid_info.chan_list,
154 			     &psoc_priv_obj->unsafe_chan_list,
155 			     sizeof(psoc_priv_obj->unsafe_chan_list));
156 	}
157 }
158 
159 /**
160  * reg_chan_change_flush_cbk_sb() - Flush south bound channel change callbacks.
161  * @msg: Pointer to scheduler msg structure.
162  */
reg_chan_change_flush_cbk_sb(struct scheduler_msg * msg)163 static QDF_STATUS reg_chan_change_flush_cbk_sb(struct scheduler_msg *msg)
164 {
165 	struct reg_sched_payload *load = msg->bodyptr;
166 	struct wlan_objmgr_psoc *psoc = load->psoc;
167 	struct wlan_objmgr_pdev *pdev = load->pdev;
168 
169 	wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID);
170 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
171 	qdf_mem_free(load);
172 
173 	return QDF_STATUS_SUCCESS;
174 }
175 
176 /**
177  * reg_sched_chan_change_cbks_sb() - Schedule south bound channel change
178  * callbacks.
179  * @msg: Pointer to scheduler msg structure.
180  */
reg_sched_chan_change_cbks_sb(struct scheduler_msg * msg)181 static QDF_STATUS reg_sched_chan_change_cbks_sb(struct scheduler_msg *msg)
182 {
183 	struct reg_sched_payload *load = msg->bodyptr;
184 	struct wlan_objmgr_psoc *psoc = load->psoc;
185 	struct wlan_objmgr_pdev *pdev = load->pdev;
186 
187 	reg_call_chan_change_cbks(psoc, pdev, load->ch_avoid_ind,
188 				  &load->avoid_info);
189 
190 	wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID);
191 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
192 	qdf_mem_free(load);
193 
194 	return QDF_STATUS_SUCCESS;
195 }
196 
197 /**
198  * reg_chan_change_flush_cbk_nb() - Flush north bound channel change callbacks.
199  * @msg: Pointer to scheduler msg structure.
200  */
reg_chan_change_flush_cbk_nb(struct scheduler_msg * msg)201 static QDF_STATUS reg_chan_change_flush_cbk_nb(struct scheduler_msg *msg)
202 {
203 	struct reg_sched_payload *load = msg->bodyptr;
204 	struct wlan_objmgr_psoc *psoc = load->psoc;
205 	struct wlan_objmgr_pdev *pdev = load->pdev;
206 
207 	wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID);
208 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
209 	qdf_mem_free(load);
210 
211 	return QDF_STATUS_SUCCESS;
212 }
213 
214 /**
215  * reg_sched_chan_change_cbks_nb() - Schedule north bound channel change
216  * callbacks.
217  * @msg: Pointer to scheduler msg structure.
218  */
reg_sched_chan_change_cbks_nb(struct scheduler_msg * msg)219 static QDF_STATUS reg_sched_chan_change_cbks_nb(struct scheduler_msg *msg)
220 {
221 	struct reg_sched_payload *load = msg->bodyptr;
222 	struct wlan_objmgr_psoc *psoc = load->psoc;
223 	struct wlan_objmgr_pdev *pdev = load->pdev;
224 
225 	reg_call_chan_change_cbks(psoc, pdev, load->ch_avoid_ind,
226 				  &load->avoid_info);
227 
228 	wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID);
229 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
230 	qdf_mem_free(load);
231 
232 	return QDF_STATUS_SUCCESS;
233 }
234 
reg_send_scheduler_msg_sb(struct wlan_objmgr_psoc * psoc,struct wlan_objmgr_pdev * pdev)235 QDF_STATUS reg_send_scheduler_msg_sb(struct wlan_objmgr_psoc *psoc,
236 				     struct wlan_objmgr_pdev *pdev)
237 {
238 	struct scheduler_msg msg = {0};
239 	struct reg_sched_payload *payload;
240 	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
241 	QDF_STATUS status;
242 
243 	pdev_priv_obj = reg_get_pdev_obj(pdev);
244 	if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
245 		reg_alert("pdev reg component is NULL");
246 		return QDF_STATUS_E_FAILURE;
247 	}
248 
249 	if (!pdev_priv_obj->pdev_opened) {
250 		reg_err("hlos not initialized");
251 		return QDF_STATUS_E_FAILURE;
252 	}
253 
254 	if (!pdev_priv_obj->chan_list_recvd) {
255 		reg_err("Empty channel list");
256 		return QDF_STATUS_E_FAILURE;
257 	}
258 
259 	status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_SB_ID);
260 	if (QDF_IS_STATUS_ERROR(status)) {
261 		reg_err("error taking psoc ref cnt");
262 		return status;
263 	}
264 
265 	status = wlan_objmgr_pdev_try_get_ref(pdev, WLAN_REGULATORY_SB_ID);
266 	if (QDF_IS_STATUS_ERROR(status)) {
267 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
268 		reg_err("error taking pdev ref cnt");
269 		return status;
270 	}
271 
272 	reg_alloc_and_fill_payload(psoc, pdev, &payload);
273 	if (!payload) {
274 		reg_err("malloc failed");
275 		wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID);
276 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
277 		return QDF_STATUS_E_NOMEM;
278 	}
279 
280 	msg.bodyptr = payload;
281 	msg.callback = reg_sched_chan_change_cbks_sb;
282 	msg.flush_callback = reg_chan_change_flush_cbk_sb;
283 
284 	status = scheduler_post_message(QDF_MODULE_ID_REGULATORY,
285 					QDF_MODULE_ID_REGULATORY,
286 					QDF_MODULE_ID_TARGET_IF, &msg);
287 	if (QDF_IS_STATUS_ERROR(status)) {
288 		wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID);
289 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
290 		qdf_mem_free(payload);
291 	}
292 
293 	return status;
294 }
295 
reg_send_scheduler_msg_nb(struct wlan_objmgr_psoc * psoc,struct wlan_objmgr_pdev * pdev)296 QDF_STATUS reg_send_scheduler_msg_nb(struct wlan_objmgr_psoc *psoc,
297 				     struct wlan_objmgr_pdev *pdev)
298 {
299 	struct scheduler_msg msg = {0};
300 	struct reg_sched_payload *payload;
301 	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
302 	QDF_STATUS status;
303 
304 	pdev_priv_obj = reg_get_pdev_obj(pdev);
305 	if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
306 		reg_alert("pdev reg component is NULL");
307 		return QDF_STATUS_E_FAILURE;
308 	}
309 
310 	if (!pdev_priv_obj->pdev_opened) {
311 		reg_err("hlos not initialized");
312 		return QDF_STATUS_E_FAILURE;
313 	}
314 
315 	if (!pdev_priv_obj->chan_list_recvd) {
316 		reg_err("Empty channel list");
317 		return QDF_STATUS_E_FAILURE;
318 	}
319 
320 	status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_NB_ID);
321 	if (QDF_IS_STATUS_ERROR(status)) {
322 		reg_err("error taking psoc ref cnt");
323 		return status;
324 	}
325 
326 	status = wlan_objmgr_pdev_try_get_ref(pdev, WLAN_REGULATORY_NB_ID);
327 	if (QDF_IS_STATUS_ERROR(status)) {
328 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
329 		reg_err("error taking pdev ref cnt");
330 		return status;
331 	}
332 
333 	reg_alloc_and_fill_payload(psoc, pdev, &payload);
334 	if (!payload) {
335 		reg_err("malloc failed");
336 		wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID);
337 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
338 		return QDF_STATUS_E_NOMEM;
339 	}
340 	msg.bodyptr = payload;
341 	msg.callback = reg_sched_chan_change_cbks_nb;
342 	msg.flush_callback = reg_chan_change_flush_cbk_nb;
343 
344 	status = scheduler_post_message(QDF_MODULE_ID_REGULATORY,
345 					QDF_MODULE_ID_REGULATORY,
346 					QDF_MODULE_ID_OS_IF, &msg);
347 	if (QDF_IS_STATUS_ERROR(status)) {
348 		wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID);
349 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
350 		qdf_mem_free(payload);
351 	}
352 
353 	return status;
354 }
355 
reg_notify_sap_event(struct wlan_objmgr_pdev * pdev,bool sap_state)356 QDF_STATUS reg_notify_sap_event(struct wlan_objmgr_pdev *pdev,
357 				bool sap_state)
358 {
359 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
360 	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
361 	struct wlan_objmgr_psoc *psoc;
362 	QDF_STATUS status;
363 
364 	pdev_priv_obj = reg_get_pdev_obj(pdev);
365 
366 	if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
367 		reg_err("pdev reg component is NULL");
368 		return QDF_STATUS_E_INVAL;
369 	}
370 
371 	psoc = wlan_pdev_get_psoc(pdev);
372 	if (!psoc) {
373 		reg_err("psoc is NULL");
374 		return QDF_STATUS_E_INVAL;
375 	}
376 
377 	psoc_priv_obj = reg_get_psoc_obj(psoc);
378 	if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) {
379 		reg_err("psoc reg component is NULL");
380 		return QDF_STATUS_E_INVAL;
381 	}
382 
383 	reg_info("sap_state: %d", sap_state);
384 
385 	if (pdev_priv_obj->sap_state == sap_state)
386 		return QDF_STATUS_SUCCESS;
387 
388 	pdev_priv_obj->sap_state = sap_state;
389 
390 	reg_compute_pdev_current_chan_list(pdev_priv_obj);
391 	status = reg_send_scheduler_msg_sb(psoc, pdev);
392 
393 	return status;
394 }
395 
reg_register_chan_change_callback(struct wlan_objmgr_psoc * psoc,reg_chan_change_callback cbk,void * arg)396 void reg_register_chan_change_callback(struct wlan_objmgr_psoc *psoc,
397 				       reg_chan_change_callback cbk, void *arg)
398 {
399 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
400 	uint32_t count;
401 
402 	psoc_priv_obj = reg_get_psoc_obj(psoc);
403 	if (!psoc_priv_obj) {
404 		reg_err("reg psoc private obj is NULL");
405 		return;
406 	}
407 
408 	qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock);
409 	for (count = 0; count < REG_MAX_CHAN_CHANGE_CBKS; count++)
410 		if (!psoc_priv_obj->cbk_list[count].cbk) {
411 			psoc_priv_obj->cbk_list[count].cbk = cbk;
412 			psoc_priv_obj->cbk_list[count].arg = arg;
413 			psoc_priv_obj->num_chan_change_cbks++;
414 			break;
415 		}
416 	qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
417 
418 	if (count == REG_MAX_CHAN_CHANGE_CBKS)
419 		reg_err("callback list is full");
420 }
421 
reg_register_ctry_change_callback(struct wlan_objmgr_psoc * psoc,reg_ctry_change_callback cbk)422 void reg_register_ctry_change_callback(struct wlan_objmgr_psoc *psoc,
423 				       reg_ctry_change_callback cbk)
424 {
425 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
426 
427 	psoc_priv_obj = reg_get_psoc_obj(psoc);
428 	if (!psoc_priv_obj) {
429 		reg_err("reg psoc private obj is NULL");
430 		return;
431 	}
432 
433 	qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock);
434 	if (!psoc_priv_obj->cc_cbk.cbk)
435 		psoc_priv_obj->cc_cbk.cbk = cbk;
436 	qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
437 }
438 
reg_unregister_chan_change_callback(struct wlan_objmgr_psoc * psoc,reg_chan_change_callback cbk)439 void reg_unregister_chan_change_callback(struct wlan_objmgr_psoc *psoc,
440 					 reg_chan_change_callback cbk)
441 {
442 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
443 	uint32_t count;
444 
445 	psoc_priv_obj = reg_get_psoc_obj(psoc);
446 	if (!psoc_priv_obj) {
447 		reg_err("reg psoc private obj is NULL");
448 		return;
449 	}
450 
451 	qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock);
452 	for (count = 0; count < REG_MAX_CHAN_CHANGE_CBKS; count++)
453 		if (psoc_priv_obj->cbk_list[count].cbk == cbk) {
454 			psoc_priv_obj->cbk_list[count].cbk = NULL;
455 			psoc_priv_obj->num_chan_change_cbks--;
456 			break;
457 		}
458 	qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
459 
460 	if (count == REG_MAX_CHAN_CHANGE_CBKS)
461 		reg_err("callback not found in the list");
462 }
463 
reg_unregister_ctry_change_callback(struct wlan_objmgr_psoc * psoc,reg_ctry_change_callback cbk)464 void reg_unregister_ctry_change_callback(struct wlan_objmgr_psoc *psoc,
465 					 reg_ctry_change_callback cbk)
466 {
467 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
468 
469 	psoc_priv_obj = reg_get_psoc_obj(psoc);
470 	if (!psoc_priv_obj) {
471 		reg_err("reg psoc private obj is NULL");
472 		return;
473 	}
474 
475 	qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock);
476 	if (psoc_priv_obj->cc_cbk.cbk == cbk)
477 		psoc_priv_obj->cc_cbk.cbk = NULL;
478 	qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
479 }
480 
481 void
reg_register_is_chan_connected_callback(struct wlan_objmgr_psoc * psoc,reg_get_connected_chan_for_mode_callback cbk)482 reg_register_is_chan_connected_callback(struct wlan_objmgr_psoc *psoc,
483 				reg_get_connected_chan_for_mode_callback cbk)
484 {
485 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
486 
487 	psoc_priv_obj = reg_get_psoc_obj(psoc);
488 	if (!psoc_priv_obj) {
489 		reg_err("reg psoc private obj is NULL");
490 		return;
491 	}
492 
493 	qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock);
494 	if (!psoc_priv_obj->conn_chan_cb.cbk)
495 		psoc_priv_obj->conn_chan_cb.cbk = cbk;
496 	qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
497 }
498 
499 void
reg_unregister_is_chan_connected_callback(struct wlan_objmgr_psoc * psoc,reg_get_connected_chan_for_mode_callback cbk)500 reg_unregister_is_chan_connected_callback(struct wlan_objmgr_psoc *psoc,
501 				reg_get_connected_chan_for_mode_callback cbk)
502 {
503 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
504 
505 	psoc_priv_obj = reg_get_psoc_obj(psoc);
506 	if (!psoc_priv_obj) {
507 		reg_err("reg psoc private obj is NULL");
508 		return;
509 	}
510 
511 	qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock);
512 	if (psoc_priv_obj->conn_chan_cb.cbk == cbk)
513 		psoc_priv_obj->conn_chan_cb.cbk = NULL;
514 	qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
515 }
516