xref: /wlan-dirver/qca-wifi-host-cmn/umac/regulatory/core/src/reg_callbacks.c (revision 45a38684b07295822dc8eba39e293408f203eec8)
1 /*
2  * Copyright (c) 2014-2020 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  */
41 static void reg_call_chan_change_cbks(struct wlan_objmgr_psoc *psoc,
42 				      struct wlan_objmgr_pdev *pdev)
43 {
44 	struct chan_change_cbk_entry *cbk_list;
45 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
46 	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
47 	struct regulatory_channel *cur_chan_list;
48 	uint32_t ctr;
49 	struct avoid_freq_ind_data *avoid_freq_ind = NULL;
50 	reg_chan_change_callback callback;
51 
52 	psoc_priv_obj = reg_get_psoc_obj(psoc);
53 	if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) {
54 		reg_alert("psoc reg component is NULL");
55 		return;
56 	}
57 
58 	pdev_priv_obj = reg_get_pdev_obj(pdev);
59 	if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
60 		reg_alert("pdev reg component is NULL");
61 		return;
62 	}
63 
64 	cur_chan_list = qdf_mem_malloc(NUM_CHANNELS * sizeof(*cur_chan_list));
65 	if (!cur_chan_list)
66 		return;
67 
68 	qdf_mem_copy(cur_chan_list,
69 		     pdev_priv_obj->cur_chan_list,
70 		     NUM_CHANNELS *
71 		     sizeof(struct regulatory_channel));
72 
73 	if (psoc_priv_obj->ch_avoid_ind) {
74 		avoid_freq_ind = qdf_mem_malloc(sizeof(*avoid_freq_ind));
75 		if (!avoid_freq_ind)
76 			goto skip_ch_avoid_ind;
77 
78 		qdf_mem_copy(&avoid_freq_ind->freq_list,
79 			     &psoc_priv_obj->avoid_freq_list,
80 				sizeof(struct ch_avoid_ind_type));
81 		qdf_mem_copy(&avoid_freq_ind->chan_list,
82 			     &psoc_priv_obj->unsafe_chan_list,
83 				sizeof(struct unsafe_ch_list));
84 		psoc_priv_obj->ch_avoid_ind = false;
85 	}
86 
87 skip_ch_avoid_ind:
88 	cbk_list = psoc_priv_obj->cbk_list;
89 
90 	for (ctr = 0; ctr < REG_MAX_CHAN_CHANGE_CBKS; ctr++) {
91 		callback  = NULL;
92 		qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock);
93 		if (cbk_list[ctr].cbk)
94 			callback = cbk_list[ctr].cbk;
95 		qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
96 		if (callback)
97 			callback(psoc, pdev, cur_chan_list, avoid_freq_ind,
98 				 cbk_list[ctr].arg);
99 	}
100 	qdf_mem_free(cur_chan_list);
101 	if (avoid_freq_ind)
102 		qdf_mem_free(avoid_freq_ind);
103 }
104 
105 /**
106  * reg_alloc_and_fill_payload() - Alloc and fill payload structure.
107  * @psoc: Pointer to global psoc structure.
108  * @pdev: Pointer to global pdev structure.
109  */
110 static void reg_alloc_and_fill_payload(struct wlan_objmgr_psoc *psoc,
111 				       struct wlan_objmgr_pdev *pdev,
112 				       struct reg_sched_payload **payload)
113 {
114 	*payload = qdf_mem_malloc(sizeof(**payload));
115 	if (*payload) {
116 		(*payload)->psoc = psoc;
117 		(*payload)->pdev = pdev;
118 	}
119 }
120 
121 /**
122  * reg_chan_change_flush_cbk_sb() - Flush south bound channel change callbacks.
123  * @msg: Pointer to scheduler msg structure.
124  */
125 static QDF_STATUS reg_chan_change_flush_cbk_sb(struct scheduler_msg *msg)
126 {
127 	struct reg_sched_payload *load = msg->bodyptr;
128 	struct wlan_objmgr_psoc *psoc = load->psoc;
129 	struct wlan_objmgr_pdev *pdev = load->pdev;
130 
131 	wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID);
132 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
133 	qdf_mem_free(load);
134 
135 	return QDF_STATUS_SUCCESS;
136 }
137 
138 /**
139  * reg_sched_chan_change_cbks_sb() - Schedule south bound channel change
140  * callbacks.
141  * @msg: Pointer to scheduler msg structure.
142  */
143 static QDF_STATUS reg_sched_chan_change_cbks_sb(struct scheduler_msg *msg)
144 {
145 	struct reg_sched_payload *load = msg->bodyptr;
146 	struct wlan_objmgr_psoc *psoc = load->psoc;
147 	struct wlan_objmgr_pdev *pdev = load->pdev;
148 
149 	reg_call_chan_change_cbks(psoc, pdev);
150 
151 	wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID);
152 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
153 	qdf_mem_free(load);
154 
155 	return QDF_STATUS_SUCCESS;
156 }
157 
158 /**
159  * reg_chan_change_flush_cbk_nb() - Flush north bound channel change callbacks.
160  * @msg: Pointer to scheduler msg structure.
161  */
162 static QDF_STATUS reg_chan_change_flush_cbk_nb(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_NB_ID);
169 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
170 	qdf_mem_free(load);
171 
172 	return QDF_STATUS_SUCCESS;
173 }
174 
175 /**
176  * reg_sched_chan_change_cbks_nb() - Schedule north bound channel change
177  * callbacks.
178  * @msg: Pointer to scheduler msg structure.
179  */
180 static QDF_STATUS reg_sched_chan_change_cbks_nb(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);
187 
188 	wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID);
189 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
190 	qdf_mem_free(load);
191 
192 	return QDF_STATUS_SUCCESS;
193 }
194 
195 QDF_STATUS reg_send_scheduler_msg_sb(struct wlan_objmgr_psoc *psoc,
196 				     struct wlan_objmgr_pdev *pdev)
197 {
198 	struct scheduler_msg msg = {0};
199 	struct reg_sched_payload *payload;
200 	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
201 	QDF_STATUS status;
202 
203 	pdev_priv_obj = reg_get_pdev_obj(pdev);
204 	if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
205 		reg_alert("pdev reg component is NULL");
206 		return QDF_STATUS_E_FAILURE;
207 	}
208 
209 	if (!pdev_priv_obj->pdev_opened) {
210 		reg_err("hlos not initialized");
211 		return QDF_STATUS_E_FAILURE;
212 	}
213 
214 	if (!pdev_priv_obj->chan_list_recvd) {
215 		reg_err("Empty channel list");
216 		return QDF_STATUS_E_FAILURE;
217 	}
218 
219 	status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_SB_ID);
220 	if (QDF_IS_STATUS_ERROR(status)) {
221 		reg_err("error taking psoc ref cnt");
222 		return status;
223 	}
224 
225 	status = wlan_objmgr_pdev_try_get_ref(pdev, WLAN_REGULATORY_SB_ID);
226 	if (QDF_IS_STATUS_ERROR(status)) {
227 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
228 		reg_err("error taking pdev ref cnt");
229 		return status;
230 	}
231 
232 	reg_alloc_and_fill_payload(psoc, pdev, &payload);
233 	if (!payload) {
234 		reg_err("malloc failed");
235 		wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID);
236 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
237 		return QDF_STATUS_E_NOMEM;
238 	}
239 
240 	msg.bodyptr = payload;
241 	msg.callback = reg_sched_chan_change_cbks_sb;
242 	msg.flush_callback = reg_chan_change_flush_cbk_sb;
243 
244 	status = scheduler_post_message(QDF_MODULE_ID_REGULATORY,
245 					QDF_MODULE_ID_REGULATORY,
246 					QDF_MODULE_ID_TARGET_IF, &msg);
247 	if (QDF_IS_STATUS_ERROR(status)) {
248 		wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_SB_ID);
249 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
250 		qdf_mem_free(payload);
251 	}
252 
253 	return status;
254 }
255 
256 QDF_STATUS reg_send_scheduler_msg_nb(struct wlan_objmgr_psoc *psoc,
257 				     struct wlan_objmgr_pdev *pdev)
258 {
259 	struct scheduler_msg msg = {0};
260 	struct reg_sched_payload *payload;
261 	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
262 	QDF_STATUS status;
263 
264 	pdev_priv_obj = reg_get_pdev_obj(pdev);
265 	if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
266 		reg_alert("pdev reg component is NULL");
267 		return QDF_STATUS_E_FAILURE;
268 	}
269 
270 	if (!pdev_priv_obj->pdev_opened) {
271 		reg_err("hlos not initialized");
272 		return QDF_STATUS_E_FAILURE;
273 	}
274 
275 	if (!pdev_priv_obj->chan_list_recvd) {
276 		reg_err("Empty channel list");
277 		return QDF_STATUS_E_FAILURE;
278 	}
279 
280 	status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_NB_ID);
281 	if (QDF_IS_STATUS_ERROR(status)) {
282 		reg_err("error taking psoc ref cnt");
283 		return status;
284 	}
285 
286 	status = wlan_objmgr_pdev_try_get_ref(pdev, WLAN_REGULATORY_NB_ID);
287 	if (QDF_IS_STATUS_ERROR(status)) {
288 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
289 		reg_err("error taking pdev ref cnt");
290 		return status;
291 	}
292 
293 	reg_alloc_and_fill_payload(psoc, pdev, &payload);
294 	if (!payload) {
295 		reg_err("malloc failed");
296 		wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID);
297 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
298 		return QDF_STATUS_E_NOMEM;
299 	}
300 	msg.bodyptr = payload;
301 	msg.callback = reg_sched_chan_change_cbks_nb;
302 	msg.flush_callback = reg_chan_change_flush_cbk_nb;
303 
304 	status = scheduler_post_message(QDF_MODULE_ID_REGULATORY,
305 					QDF_MODULE_ID_REGULATORY,
306 					QDF_MODULE_ID_OS_IF, &msg);
307 	if (QDF_IS_STATUS_ERROR(status)) {
308 		wlan_objmgr_pdev_release_ref(pdev, WLAN_REGULATORY_NB_ID);
309 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
310 		qdf_mem_free(payload);
311 	}
312 
313 	return status;
314 }
315 
316 QDF_STATUS reg_notify_sap_event(struct wlan_objmgr_pdev *pdev,
317 				bool sap_state)
318 {
319 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
320 	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
321 	struct wlan_objmgr_psoc *psoc;
322 	QDF_STATUS status;
323 
324 	pdev_priv_obj = reg_get_pdev_obj(pdev);
325 
326 	if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
327 		reg_err("pdev reg component is NULL");
328 		return QDF_STATUS_E_INVAL;
329 	}
330 
331 	psoc = wlan_pdev_get_psoc(pdev);
332 	if (!psoc) {
333 		reg_err("psoc is NULL");
334 		return QDF_STATUS_E_INVAL;
335 	}
336 
337 	psoc_priv_obj = reg_get_psoc_obj(psoc);
338 	if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) {
339 		reg_err("psoc reg component is NULL");
340 		return QDF_STATUS_E_INVAL;
341 	}
342 
343 	reg_info("sap_state: %d", sap_state);
344 
345 	if (pdev_priv_obj->sap_state == sap_state)
346 		return QDF_STATUS_SUCCESS;
347 
348 	pdev_priv_obj->sap_state = sap_state;
349 	set_disable_channel_state(pdev_priv_obj);
350 
351 	reg_compute_pdev_current_chan_list(pdev_priv_obj);
352 	status = reg_send_scheduler_msg_sb(psoc, pdev);
353 
354 	return status;
355 }
356 
357 void reg_register_chan_change_callback(struct wlan_objmgr_psoc *psoc,
358 				       reg_chan_change_callback cbk, void *arg)
359 {
360 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
361 	uint32_t count;
362 
363 	psoc_priv_obj = reg_get_psoc_obj(psoc);
364 	if (!psoc_priv_obj) {
365 		reg_err("reg psoc private obj is NULL");
366 		return;
367 	}
368 
369 	qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock);
370 	for (count = 0; count < REG_MAX_CHAN_CHANGE_CBKS; count++)
371 		if (!psoc_priv_obj->cbk_list[count].cbk) {
372 			psoc_priv_obj->cbk_list[count].cbk = cbk;
373 			psoc_priv_obj->cbk_list[count].arg = arg;
374 			psoc_priv_obj->num_chan_change_cbks++;
375 			break;
376 		}
377 	qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
378 
379 	if (count == REG_MAX_CHAN_CHANGE_CBKS)
380 		reg_err("callback list is full");
381 }
382 
383 void reg_unregister_chan_change_callback(struct wlan_objmgr_psoc *psoc,
384 					 reg_chan_change_callback cbk)
385 {
386 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
387 	uint32_t count;
388 
389 	psoc_priv_obj = reg_get_psoc_obj(psoc);
390 	if (!psoc_priv_obj) {
391 		reg_err("reg psoc private obj is NULL");
392 		return;
393 	}
394 
395 	qdf_spin_lock_bh(&psoc_priv_obj->cbk_list_lock);
396 	for (count = 0; count < REG_MAX_CHAN_CHANGE_CBKS; count++)
397 		if (psoc_priv_obj->cbk_list[count].cbk == cbk) {
398 			psoc_priv_obj->cbk_list[count].cbk = NULL;
399 			psoc_priv_obj->num_chan_change_cbks--;
400 			break;
401 		}
402 	qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
403 
404 	if (count == REG_MAX_CHAN_CHANGE_CBKS)
405 		reg_err("callback not found in the list");
406 }
407 
408