xref: /wlan-dirver/qca-wifi-host-cmn/umac/regulatory/core/src/reg_callbacks.c (revision 2f4b444fb7e689b83a4ab0e7b3b38f0bf4def8e0)
1 /*
2  * Copyright (c) 2014-2021 The Linux Foundation. All rights reserved.
3  *
4  * Permission to use, copy, modify, and/or distribute this software for
5  * any purpose with or without fee is hereby granted, provided that the
6  * above copyright notice and this permission notice appear in all
7  * copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16  * PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /**
20  * DOC: reg_callbacks.c
21  * This file defines regulatory callback functions
22  */
23 
24 #include <wlan_cmn.h>
25 #include <reg_services_public_struct.h>
26 #include <wlan_objmgr_psoc_obj.h>
27 #include <wlan_objmgr_pdev_obj.h>
28 #include "reg_priv_objs.h"
29 #include "reg_utils.h"
30 #include <scheduler_api.h>
31 #include "reg_callbacks.h"
32 #include "reg_services_common.h"
33 #include "reg_build_chan_list.h"
34 
35 /**
36  * reg_call_chan_change_cbks() - Call registered callback functions on channel
37  * change.
38  * @psoc: Pointer to global psoc structure.
39  * @pdev: Pointer to global pdev structure.
40  * @ch_avoid_ind: if chan avoid indicated
41  * @avoid_info: chan avoid info if @ch_avoid_ind is true
42  */
43 static void reg_call_chan_change_cbks(struct wlan_objmgr_psoc *psoc,
44 				      struct wlan_objmgr_pdev *pdev,
45 				      bool ch_avoid_ind,
46 				      struct avoid_freq_ind_data *avoid_info)
47 {
48 	struct chan_change_cbk_entry *cbk_list;
49 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
50 	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
51 	struct regulatory_channel *cur_chan_list;
52 	uint32_t ctr;
53 	struct avoid_freq_ind_data *avoid_freq_ind = NULL;
54 	reg_chan_change_callback callback;
55 
56 	psoc_priv_obj = reg_get_psoc_obj(psoc);
57 	if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) {
58 		reg_alert("psoc reg component is NULL");
59 		return;
60 	}
61 
62 	pdev_priv_obj = reg_get_pdev_obj(pdev);
63 	if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
64 		reg_alert("pdev reg component is NULL");
65 		return;
66 	}
67 
68 	cur_chan_list = qdf_mem_malloc(NUM_CHANNELS * sizeof(*cur_chan_list));
69 	if (!cur_chan_list)
70 		return;
71 
72 	qdf_mem_copy(cur_chan_list,
73 		     pdev_priv_obj->cur_chan_list,
74 		     NUM_CHANNELS *
75 		     sizeof(struct regulatory_channel));
76 
77 	if (ch_avoid_ind)
78 		avoid_freq_ind = avoid_info;
79 
80 	cbk_list = psoc_priv_obj->cbk_list;
81 
82 	for (ctr = 0; ctr < REG_MAX_CHAN_CHANGE_CBKS; ctr++) {
83 		callback  = NULL;
84 		qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock);
85 		if (cbk_list[ctr].cbk)
86 			callback = cbk_list[ctr].cbk;
87 		qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
88 		if (callback)
89 			callback(psoc, pdev, cur_chan_list, avoid_freq_ind,
90 				 cbk_list[ctr].arg);
91 	}
92 	qdf_mem_free(cur_chan_list);
93 }
94 
95 /**
96  * reg_alloc_and_fill_payload() - Alloc and fill payload structure.
97  * @psoc: Pointer to global psoc structure.
98  * @pdev: Pointer to global pdev structure.
99  */
100 static void reg_alloc_and_fill_payload(struct wlan_objmgr_psoc *psoc,
101 				       struct wlan_objmgr_pdev *pdev,
102 				       struct reg_sched_payload **payload)
103 {
104 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
105 
106 	psoc_priv_obj = reg_get_psoc_obj(psoc);
107 	if (!psoc_priv_obj) {
108 		reg_err("reg psoc private obj is NULL");
109 		*payload = NULL;
110 		return;
111 	}
112 
113 	*payload = qdf_mem_malloc(sizeof(**payload));
114 	if (*payload) {
115 		(*payload)->psoc = psoc;
116 		(*payload)->pdev = pdev;
117 		(*payload)->ch_avoid_ind = !!psoc_priv_obj->ch_avoid_ind;
118 		qdf_mem_copy(&(*payload)->avoid_info.freq_list,
119 			     &psoc_priv_obj->avoid_freq_list,
120 			     sizeof(psoc_priv_obj->avoid_freq_list));
121 		qdf_mem_copy(&(*payload)->avoid_info.chan_list,
122 			     &psoc_priv_obj->unsafe_chan_list,
123 			     sizeof(psoc_priv_obj->unsafe_chan_list));
124 
125 		psoc_priv_obj->ch_avoid_ind = false;
126 	}
127 }
128 
129 /**
130  * reg_chan_change_flush_cbk_sb() - Flush south bound channel change callbacks.
131  * @msg: Pointer to scheduler msg structure.
132  */
133 static QDF_STATUS reg_chan_change_flush_cbk_sb(struct scheduler_msg *msg)
134 {
135 	struct reg_sched_payload *load = msg->bodyptr;
136 	struct wlan_objmgr_psoc *psoc = load->psoc;
137 	struct wlan_objmgr_pdev *pdev = load->pdev;
138 
139 	wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID);
140 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
141 	qdf_mem_free(load);
142 
143 	return QDF_STATUS_SUCCESS;
144 }
145 
146 /**
147  * reg_sched_chan_change_cbks_sb() - Schedule south bound channel change
148  * callbacks.
149  * @msg: Pointer to scheduler msg structure.
150  */
151 static QDF_STATUS reg_sched_chan_change_cbks_sb(struct scheduler_msg *msg)
152 {
153 	struct reg_sched_payload *load = msg->bodyptr;
154 	struct wlan_objmgr_psoc *psoc = load->psoc;
155 	struct wlan_objmgr_pdev *pdev = load->pdev;
156 
157 	reg_call_chan_change_cbks(psoc, pdev, load->ch_avoid_ind,
158 				  &load->avoid_info);
159 
160 	wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID);
161 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
162 	qdf_mem_free(load);
163 
164 	return QDF_STATUS_SUCCESS;
165 }
166 
167 /**
168  * reg_chan_change_flush_cbk_nb() - Flush north bound channel change callbacks.
169  * @msg: Pointer to scheduler msg structure.
170  */
171 static QDF_STATUS reg_chan_change_flush_cbk_nb(struct scheduler_msg *msg)
172 {
173 	struct reg_sched_payload *load = msg->bodyptr;
174 	struct wlan_objmgr_psoc *psoc = load->psoc;
175 	struct wlan_objmgr_pdev *pdev = load->pdev;
176 
177 	wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID);
178 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
179 	qdf_mem_free(load);
180 
181 	return QDF_STATUS_SUCCESS;
182 }
183 
184 /**
185  * reg_sched_chan_change_cbks_nb() - Schedule north bound channel change
186  * callbacks.
187  * @msg: Pointer to scheduler msg structure.
188  */
189 static QDF_STATUS reg_sched_chan_change_cbks_nb(struct scheduler_msg *msg)
190 {
191 	struct reg_sched_payload *load = msg->bodyptr;
192 	struct wlan_objmgr_psoc *psoc = load->psoc;
193 	struct wlan_objmgr_pdev *pdev = load->pdev;
194 
195 	reg_call_chan_change_cbks(psoc, pdev, load->ch_avoid_ind,
196 				  &load->avoid_info);
197 
198 	wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID);
199 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
200 	qdf_mem_free(load);
201 
202 	return QDF_STATUS_SUCCESS;
203 }
204 
205 QDF_STATUS reg_send_scheduler_msg_sb(struct wlan_objmgr_psoc *psoc,
206 				     struct wlan_objmgr_pdev *pdev)
207 {
208 	struct scheduler_msg msg = {0};
209 	struct reg_sched_payload *payload;
210 	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
211 	QDF_STATUS status;
212 
213 	pdev_priv_obj = reg_get_pdev_obj(pdev);
214 	if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
215 		reg_alert("pdev reg component is NULL");
216 		return QDF_STATUS_E_FAILURE;
217 	}
218 
219 	if (!pdev_priv_obj->pdev_opened) {
220 		reg_err("hlos not initialized");
221 		return QDF_STATUS_E_FAILURE;
222 	}
223 
224 	if (!pdev_priv_obj->chan_list_recvd) {
225 		reg_err("Empty channel list");
226 		return QDF_STATUS_E_FAILURE;
227 	}
228 
229 	status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_SB_ID);
230 	if (QDF_IS_STATUS_ERROR(status)) {
231 		reg_err("error taking psoc ref cnt");
232 		return status;
233 	}
234 
235 	status = wlan_objmgr_pdev_try_get_ref(pdev, WLAN_REGULATORY_SB_ID);
236 	if (QDF_IS_STATUS_ERROR(status)) {
237 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
238 		reg_err("error taking pdev ref cnt");
239 		return status;
240 	}
241 
242 	reg_alloc_and_fill_payload(psoc, pdev, &payload);
243 	if (!payload) {
244 		reg_err("malloc failed");
245 		wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID);
246 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
247 		return QDF_STATUS_E_NOMEM;
248 	}
249 
250 	msg.bodyptr = payload;
251 	msg.callback = reg_sched_chan_change_cbks_sb;
252 	msg.flush_callback = reg_chan_change_flush_cbk_sb;
253 
254 	status = scheduler_post_message(QDF_MODULE_ID_REGULATORY,
255 					QDF_MODULE_ID_REGULATORY,
256 					QDF_MODULE_ID_TARGET_IF, &msg);
257 	if (QDF_IS_STATUS_ERROR(status)) {
258 		wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID);
259 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
260 		qdf_mem_free(payload);
261 	}
262 
263 	return status;
264 }
265 
266 QDF_STATUS reg_send_scheduler_msg_nb(struct wlan_objmgr_psoc *psoc,
267 				     struct wlan_objmgr_pdev *pdev)
268 {
269 	struct scheduler_msg msg = {0};
270 	struct reg_sched_payload *payload;
271 	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
272 	QDF_STATUS status;
273 
274 	pdev_priv_obj = reg_get_pdev_obj(pdev);
275 	if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
276 		reg_alert("pdev reg component is NULL");
277 		return QDF_STATUS_E_FAILURE;
278 	}
279 
280 	if (!pdev_priv_obj->pdev_opened) {
281 		reg_err("hlos not initialized");
282 		return QDF_STATUS_E_FAILURE;
283 	}
284 
285 	if (!pdev_priv_obj->chan_list_recvd) {
286 		reg_err("Empty channel list");
287 		return QDF_STATUS_E_FAILURE;
288 	}
289 
290 	status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_NB_ID);
291 	if (QDF_IS_STATUS_ERROR(status)) {
292 		reg_err("error taking psoc ref cnt");
293 		return status;
294 	}
295 
296 	status = wlan_objmgr_pdev_try_get_ref(pdev, WLAN_REGULATORY_NB_ID);
297 	if (QDF_IS_STATUS_ERROR(status)) {
298 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
299 		reg_err("error taking pdev ref cnt");
300 		return status;
301 	}
302 
303 	reg_alloc_and_fill_payload(psoc, pdev, &payload);
304 	if (!payload) {
305 		reg_err("malloc failed");
306 		wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID);
307 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
308 		return QDF_STATUS_E_NOMEM;
309 	}
310 	msg.bodyptr = payload;
311 	msg.callback = reg_sched_chan_change_cbks_nb;
312 	msg.flush_callback = reg_chan_change_flush_cbk_nb;
313 
314 	status = scheduler_post_message(QDF_MODULE_ID_REGULATORY,
315 					QDF_MODULE_ID_REGULATORY,
316 					QDF_MODULE_ID_OS_IF, &msg);
317 	if (QDF_IS_STATUS_ERROR(status)) {
318 		wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID);
319 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
320 		qdf_mem_free(payload);
321 	}
322 
323 	return status;
324 }
325 
326 QDF_STATUS reg_notify_sap_event(struct wlan_objmgr_pdev *pdev,
327 				bool sap_state)
328 {
329 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
330 	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
331 	struct wlan_objmgr_psoc *psoc;
332 	QDF_STATUS status;
333 
334 	pdev_priv_obj = reg_get_pdev_obj(pdev);
335 
336 	if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
337 		reg_err("pdev reg component is NULL");
338 		return QDF_STATUS_E_INVAL;
339 	}
340 
341 	psoc = wlan_pdev_get_psoc(pdev);
342 	if (!psoc) {
343 		reg_err("psoc is NULL");
344 		return QDF_STATUS_E_INVAL;
345 	}
346 
347 	psoc_priv_obj = reg_get_psoc_obj(psoc);
348 	if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) {
349 		reg_err("psoc reg component is NULL");
350 		return QDF_STATUS_E_INVAL;
351 	}
352 
353 	reg_info("sap_state: %d", sap_state);
354 
355 	if (pdev_priv_obj->sap_state == sap_state)
356 		return QDF_STATUS_SUCCESS;
357 
358 	pdev_priv_obj->sap_state = sap_state;
359 
360 	reg_compute_pdev_current_chan_list(pdev_priv_obj);
361 	status = reg_send_scheduler_msg_sb(psoc, pdev);
362 
363 	return status;
364 }
365 
366 void reg_register_chan_change_callback(struct wlan_objmgr_psoc *psoc,
367 				       reg_chan_change_callback cbk, void *arg)
368 {
369 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
370 	uint32_t count;
371 
372 	psoc_priv_obj = reg_get_psoc_obj(psoc);
373 	if (!psoc_priv_obj) {
374 		reg_err("reg psoc private obj is NULL");
375 		return;
376 	}
377 
378 	qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock);
379 	for (count = 0; count < REG_MAX_CHAN_CHANGE_CBKS; count++)
380 		if (!psoc_priv_obj->cbk_list[count].cbk) {
381 			psoc_priv_obj->cbk_list[count].cbk = cbk;
382 			psoc_priv_obj->cbk_list[count].arg = arg;
383 			psoc_priv_obj->num_chan_change_cbks++;
384 			break;
385 		}
386 	qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
387 
388 	if (count == REG_MAX_CHAN_CHANGE_CBKS)
389 		reg_err("callback list is full");
390 }
391 
392 void reg_unregister_chan_change_callback(struct wlan_objmgr_psoc *psoc,
393 					 reg_chan_change_callback cbk)
394 {
395 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
396 	uint32_t count;
397 
398 	psoc_priv_obj = reg_get_psoc_obj(psoc);
399 	if (!psoc_priv_obj) {
400 		reg_err("reg psoc private obj is NULL");
401 		return;
402 	}
403 
404 	qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock);
405 	for (count = 0; count < REG_MAX_CHAN_CHANGE_CBKS; count++)
406 		if (psoc_priv_obj->cbk_list[count].cbk == cbk) {
407 			psoc_priv_obj->cbk_list[count].cbk = NULL;
408 			psoc_priv_obj->num_chan_change_cbks--;
409 			break;
410 		}
411 	qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
412 
413 	if (count == REG_MAX_CHAN_CHANGE_CBKS)
414 		reg_err("callback not found in the list");
415 }
416 
417