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