xref: /wlan-driver/qca-wifi-host-cmn/umac/regulatory/core/src/reg_offload_11d_scan.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1 /*
2  * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
3  * Copyright (c) 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: Add 11d utility functions
22  */
23 
24 #include <wlan_cmn.h>
25 #include <reg_services_public_struct.h>
26 #include <wlan_scan_public_structs.h>
27 #include <wlan_scan_ucfg_api.h>
28 #include <wlan_objmgr_psoc_obj.h>
29 #include "reg_priv_objs.h"
30 #include "reg_utils.h"
31 #include "reg_services_common.h"
32 #include "reg_offload_11d_scan.h"
33 #include "reg_host_11d.h"
34 
35 #ifdef TARGET_11D_SCAN
36 
reg_set_11d_country(struct wlan_objmgr_pdev * pdev,uint8_t * country)37 QDF_STATUS reg_set_11d_country(struct wlan_objmgr_pdev *pdev,
38 			       uint8_t *country)
39 {
40 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
41 	struct set_country country_code;
42 	struct wlan_objmgr_psoc *psoc;
43 	struct cc_regdmn_s rd;
44 	QDF_STATUS status;
45 	struct wlan_lmac_if_reg_tx_ops *tx_ops;
46 	uint8_t pdev_id;
47 	uint8_t phy_id;
48 
49 	if (!country) {
50 		reg_err("Null country code");
51 		return QDF_STATUS_E_INVAL;
52 	}
53 
54 	pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev);
55 
56 	psoc = wlan_pdev_get_psoc(pdev);
57 	psoc_priv_obj = reg_get_psoc_obj(psoc);
58 	if (!psoc_priv_obj) {
59 		reg_err("Null psoc reg component");
60 		return QDF_STATUS_E_INVAL;
61 	}
62 
63 	if (!qdf_mem_cmp(psoc_priv_obj->cur_country, country, REG_ALPHA2_LEN)) {
64 		if (psoc_priv_obj->cc_src == SOURCE_11D) {
65 			reg_debug("same country");
66 			return QDF_STATUS_SUCCESS;
67 		}
68 	}
69 
70 	reg_info("set new 11d country:%c%c to fW",
71 		 country[0], country[1]);
72 
73 	qdf_mem_copy(country_code.country, country, REG_ALPHA2_LEN + 1);
74 	country_code.pdev_id = pdev_id;
75 
76 	tx_ops = reg_get_psoc_tx_ops(psoc);
77 	if (tx_ops->get_phy_id_from_pdev_id)
78 		tx_ops->get_phy_id_from_pdev_id(psoc, pdev_id, &phy_id);
79 	else
80 		phy_id = pdev_id;
81 
82 	psoc_priv_obj->new_11d_ctry_pending[phy_id] = true;
83 
84 	if (psoc_priv_obj->offload_enabled) {
85 		tx_ops = reg_get_psoc_tx_ops(psoc);
86 		if (tx_ops->set_country_code) {
87 			tx_ops->set_country_code(psoc, &country_code);
88 		} else {
89 			reg_err("country set fw handler not present");
90 			psoc_priv_obj->new_11d_ctry_pending[phy_id] = false;
91 			return QDF_STATUS_E_FAULT;
92 		}
93 		status = QDF_STATUS_SUCCESS;
94 	} else {
95 		qdf_mem_copy(rd.cc.alpha, country, REG_ALPHA2_LEN + 1);
96 		rd.flags = ALPHA_IS_SET;
97 		reg_program_chan_list(pdev, &rd);
98 		status = QDF_STATUS_SUCCESS;
99 	}
100 
101 	return status;
102 }
103 
104 /**
105  * reg_send_11d_flush_cbk() - release 11d psoc reference
106  * @msg: Pointer to scheduler message.
107  *
108  * Return: QDF_STATUS
109  */
reg_send_11d_flush_cbk(struct scheduler_msg * msg)110 static QDF_STATUS reg_send_11d_flush_cbk(struct scheduler_msg *msg)
111 {
112 	struct reg_11d_scan_msg *scan_msg_11d = msg->bodyptr;
113 	struct wlan_objmgr_psoc *psoc = scan_msg_11d->psoc;
114 
115 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
116 	qdf_mem_free(scan_msg_11d);
117 
118 	return QDF_STATUS_SUCCESS;
119 }
120 
121 /**
122  * reg_send_11d_msg_cbk() - Send start/stop 11d scan message.
123  * @msg: Pointer to scheduler message.
124  *
125  * Return: QDF_STATUS
126  */
reg_send_11d_msg_cbk(struct scheduler_msg * msg)127 static QDF_STATUS reg_send_11d_msg_cbk(struct scheduler_msg *msg)
128 {
129 	struct reg_11d_scan_msg *scan_msg_11d = msg->bodyptr;
130 	struct wlan_objmgr_psoc *psoc = scan_msg_11d->psoc;
131 	struct wlan_lmac_if_reg_tx_ops *tx_ops;
132 	struct reg_start_11d_scan_req start_req;
133 	struct reg_stop_11d_scan_req stop_req;
134 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
135 
136 	tx_ops = reg_get_psoc_tx_ops(psoc);
137 
138 	psoc_priv_obj = reg_get_psoc_obj(psoc);
139 	if (!psoc_priv_obj) {
140 		reg_err("Null psoc priv obj");
141 		goto end;
142 	}
143 
144 	if (psoc_priv_obj->vdev_id_for_11d_scan == INVALID_VDEV_ID) {
145 		psoc_priv_obj->enable_11d_supp = false;
146 		reg_err("Invalid vdev");
147 		goto end;
148 	}
149 
150 	if (scan_msg_11d->enable_11d_supp) {
151 		start_req.vdev_id = psoc_priv_obj->vdev_id_for_11d_scan;
152 		start_req.scan_period_msec = psoc_priv_obj->scan_11d_interval;
153 		start_req.start_interval_msec = 0;
154 		reg_debug("sending start msg");
155 		tx_ops->start_11d_scan(psoc, &start_req);
156 	} else {
157 		stop_req.vdev_id = psoc_priv_obj->vdev_id_for_11d_scan;
158 		reg_debug("sending stop msg");
159 		tx_ops->stop_11d_scan(psoc, &stop_req);
160 	}
161 
162 end:
163 	qdf_mem_free(scan_msg_11d);
164 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
165 	return QDF_STATUS_SUCCESS;
166 }
167 
168 /**
169  * reg_sched_11d_msg() - Schedules 11d scan message.
170  * @scan_msg_11d: 11d scan message
171  */
reg_sched_11d_msg(struct reg_11d_scan_msg * scan_msg_11d)172 static QDF_STATUS reg_sched_11d_msg(struct reg_11d_scan_msg *scan_msg_11d)
173 {
174 	struct scheduler_msg msg = {0};
175 	QDF_STATUS status;
176 
177 	status = wlan_objmgr_psoc_try_get_ref(scan_msg_11d->psoc,
178 					      WLAN_REGULATORY_SB_ID);
179 	if (QDF_IS_STATUS_ERROR(status)) {
180 		reg_err("error taking psoc ref cnt");
181 		return status;
182 	}
183 
184 	msg.bodyptr = scan_msg_11d;
185 	msg.callback = reg_send_11d_msg_cbk;
186 	msg.flush_callback = reg_send_11d_flush_cbk;
187 
188 	status = scheduler_post_message(QDF_MODULE_ID_REGULATORY,
189 					QDF_MODULE_ID_REGULATORY,
190 					QDF_MODULE_ID_TARGET_IF, &msg);
191 	if (QDF_IS_STATUS_ERROR(status))
192 		wlan_objmgr_psoc_release_ref(scan_msg_11d->psoc,
193 					     WLAN_REGULATORY_SB_ID);
194 
195 	return status;
196 }
197 
reg_run_11d_state_machine(struct wlan_objmgr_psoc * psoc)198 void reg_run_11d_state_machine(struct wlan_objmgr_psoc *psoc)
199 {
200 	bool temp_11d_support;
201 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
202 	bool world_mode;
203 	struct reg_11d_scan_msg *scan_msg_11d;
204 
205 	psoc_priv_obj = reg_get_psoc_obj(psoc);
206 	if (!psoc_priv_obj) {
207 		reg_err("Null reg psoc private obj");
208 		return;
209 	}
210 
211 	if (psoc_priv_obj->vdev_id_for_11d_scan == INVALID_VDEV_ID) {
212 		psoc_priv_obj->enable_11d_supp = false;
213 		reg_err("Invalid vdev");
214 		return;
215 	}
216 
217 	world_mode = reg_is_world_alpha2(psoc_priv_obj->cur_country);
218 
219 	temp_11d_support = psoc_priv_obj->enable_11d_supp;
220 	if ((psoc_priv_obj->enable_11d_in_world_mode) && (world_mode))
221 		psoc_priv_obj->enable_11d_supp = true;
222 	else if (psoc_priv_obj->user_ctry_set &&
223 		 psoc_priv_obj->user_ctry_priority)
224 		psoc_priv_obj->enable_11d_supp = false;
225 	else
226 		psoc_priv_obj->enable_11d_supp =
227 			psoc_priv_obj->enable_11d_supp_original;
228 
229 	reg_debug("inside 11d state machine:tmp %d 11d_supp %d org %d set %d pri %d vdev %d",
230 		  temp_11d_support,
231 		  psoc_priv_obj->enable_11d_supp,
232 		  psoc_priv_obj->enable_11d_supp_original,
233 		  psoc_priv_obj->user_ctry_set,
234 		  psoc_priv_obj->user_ctry_priority,
235 		  psoc_priv_obj->vdev_id_for_11d_scan);
236 
237 	if (temp_11d_support != psoc_priv_obj->enable_11d_supp) {
238 		if (psoc_priv_obj->is_11d_offloaded) {
239 			scan_msg_11d = qdf_mem_malloc(sizeof(*scan_msg_11d));
240 			if (!scan_msg_11d)
241 				return;
242 			scan_msg_11d->psoc = psoc;
243 			scan_msg_11d->enable_11d_supp =
244 						psoc_priv_obj->enable_11d_supp;
245 			reg_sched_11d_msg(scan_msg_11d);
246 		} else {
247 			reg_11d_host_scan(psoc_priv_obj);
248 		}
249 	}
250 }
251 
reg_11d_vdev_created_update(struct wlan_objmgr_vdev * vdev)252 QDF_STATUS reg_11d_vdev_created_update(struct wlan_objmgr_vdev *vdev)
253 {
254 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
255 	struct wlan_objmgr_pdev *parent_pdev;
256 	struct wlan_objmgr_psoc *parent_psoc;
257 	uint32_t vdev_id;
258 	enum QDF_OPMODE op_mode;
259 	uint8_t i;
260 
261 	op_mode = wlan_vdev_mlme_get_opmode(vdev);
262 
263 	parent_pdev = wlan_vdev_get_pdev(vdev);
264 	parent_psoc = wlan_pdev_get_psoc(parent_pdev);
265 
266 	psoc_priv_obj = reg_get_psoc_obj(parent_psoc);
267 	if (!psoc_priv_obj) {
268 		reg_err("reg psoc private obj is NULL");
269 		return QDF_STATUS_E_FAULT;
270 	}
271 
272 	if ((op_mode == QDF_STA_MODE) ||
273 	    (op_mode == QDF_P2P_DEVICE_MODE) ||
274 	    (op_mode == QDF_P2P_CLIENT_MODE)) {
275 		vdev_id = wlan_vdev_get_id(vdev);
276 		if (!psoc_priv_obj->vdev_cnt_11d) {
277 			psoc_priv_obj->vdev_id_for_11d_scan = vdev_id;
278 			reg_debug("running 11d state machine, opmode %d",
279 				  op_mode);
280 			reg_run_11d_state_machine(parent_psoc);
281 		}
282 
283 		for (i = 0; i < MAX_STA_VDEV_CNT; i++) {
284 			if (psoc_priv_obj->vdev_ids_11d[i] == INVALID_VDEV_ID) {
285 				psoc_priv_obj->vdev_ids_11d[i] = vdev_id;
286 				break;
287 			}
288 		}
289 		psoc_priv_obj->vdev_cnt_11d++;
290 	}
291 
292 	return QDF_STATUS_SUCCESS;
293 }
294 
reg_11d_vdev_delete_update(struct wlan_objmgr_psoc * psoc,enum QDF_OPMODE op_mode,uint32_t vdev_id)295 QDF_STATUS reg_11d_vdev_delete_update(struct wlan_objmgr_psoc *psoc,
296 				      enum QDF_OPMODE op_mode, uint32_t vdev_id)
297 {
298 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
299 	uint8_t i;
300 
301 	psoc_priv_obj = reg_get_psoc_obj(psoc);
302 	if (!psoc_priv_obj) {
303 		reg_err("NULL reg psoc private obj");
304 		return QDF_STATUS_E_FAULT;
305 	}
306 
307 	if ((op_mode == QDF_STA_MODE) || (op_mode == QDF_P2P_DEVICE_MODE) ||
308 	    (op_mode == QDF_P2P_CLIENT_MODE)) {
309 		for (i = 0; i < MAX_STA_VDEV_CNT; i++) {
310 			if (psoc_priv_obj->vdev_ids_11d[i] == vdev_id) {
311 				psoc_priv_obj->vdev_ids_11d[i] =
312 					INVALID_VDEV_ID;
313 				psoc_priv_obj->vdev_cnt_11d--;
314 				break;
315 			}
316 		}
317 
318 		if (psoc_priv_obj->vdev_id_for_11d_scan != vdev_id)
319 			return QDF_STATUS_SUCCESS;
320 
321 		if (!psoc_priv_obj->vdev_cnt_11d) {
322 			psoc_priv_obj->vdev_id_for_11d_scan = INVALID_VDEV_ID;
323 			psoc_priv_obj->enable_11d_supp = false;
324 			return QDF_STATUS_SUCCESS;
325 		}
326 
327 		for (i = 0; i < MAX_STA_VDEV_CNT; i++) {
328 			if (psoc_priv_obj->vdev_ids_11d[i] == INVALID_VDEV_ID)
329 				continue;
330 			psoc_priv_obj->vdev_id_for_11d_scan =
331 				psoc_priv_obj->vdev_ids_11d[i];
332 			psoc_priv_obj->enable_11d_supp = false;
333 			reg_debug("running 11d state machine, vdev %d",
334 				  psoc_priv_obj->vdev_id_for_11d_scan);
335 			reg_run_11d_state_machine(psoc);
336 			break;
337 		}
338 	}
339 
340 	return QDF_STATUS_SUCCESS;
341 }
342 
reg_is_11d_scan_inprogress(struct wlan_objmgr_psoc * psoc)343 bool reg_is_11d_scan_inprogress(struct wlan_objmgr_psoc *psoc)
344 {
345 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
346 
347 	psoc_priv_obj = reg_get_psoc_obj(psoc);
348 	if (!psoc_priv_obj) {
349 		reg_err("NULL reg psoc private obj");
350 		return false;
351 	}
352 
353 	return psoc_priv_obj->enable_11d_supp;
354 }
355 
reg_save_new_11d_country(struct wlan_objmgr_psoc * psoc,uint8_t * country)356 QDF_STATUS reg_save_new_11d_country(struct wlan_objmgr_psoc *psoc,
357 				    uint8_t *country)
358 {
359 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
360 	struct wlan_lmac_if_reg_tx_ops *tx_ops;
361 	struct set_country country_code;
362 	uint8_t pdev_id;
363 	uint8_t ctr;
364 
365 	psoc_priv_obj = reg_get_psoc_obj(psoc);
366 	if (!psoc_priv_obj) {
367 		reg_err("NULL reg psoc private obj");
368 
369 		return QDF_STATUS_E_FAILURE;
370 	}
371 
372 	/*
373 	 * Need firmware to send channel list event
374 	 * for all phys. Therefore set pdev_id to 0xFF
375 	 */
376 	pdev_id = 0xFF;
377 	for (ctr = 0; ctr < psoc_priv_obj->num_phy; ctr++)
378 		psoc_priv_obj->new_11d_ctry_pending[ctr] = true;
379 
380 	qdf_mem_copy(country_code.country, country, REG_ALPHA2_LEN + 1);
381 	country_code.pdev_id = pdev_id;
382 
383 	if (psoc_priv_obj->offload_enabled) {
384 		tx_ops = reg_get_psoc_tx_ops(psoc);
385 		if (tx_ops->set_country_code) {
386 			tx_ops->set_country_code(psoc, &country_code);
387 		} else {
388 			reg_err("NULL country set handler");
389 			for (ctr = 0; ctr < psoc_priv_obj->num_phy; ctr++)
390 				psoc_priv_obj->new_11d_ctry_pending[ctr] =
391 					false;
392 			return QDF_STATUS_E_FAULT;
393 		}
394 	}
395 
396 	return QDF_STATUS_SUCCESS;
397 }
398 
reg_11d_enabled_on_host(struct wlan_objmgr_psoc * psoc)399 bool reg_11d_enabled_on_host(struct wlan_objmgr_psoc *psoc)
400 {
401 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
402 
403 	psoc_priv_obj = reg_get_psoc_obj(psoc);
404 	if (!psoc_priv_obj) {
405 		reg_err("NULL reg psoc private obj");
406 		return QDF_STATUS_E_FAILURE;
407 	}
408 
409 	return (psoc_priv_obj->enable_11d_supp &&
410 		!psoc_priv_obj->is_11d_offloaded);
411 }
412 
reg_set_11d_offloaded(struct wlan_objmgr_psoc * psoc,bool val)413 QDF_STATUS reg_set_11d_offloaded(struct wlan_objmgr_psoc *psoc, bool val)
414 {
415 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
416 
417 	psoc_priv_obj = reg_get_psoc_obj(psoc);
418 	if (!psoc_priv_obj) {
419 		reg_err("NULL psoc reg component");
420 		return QDF_STATUS_E_FAILURE;
421 	}
422 
423 	psoc_priv_obj->is_11d_offloaded = val;
424 	reg_debug("set is_11d_offloaded %d", val);
425 	return QDF_STATUS_SUCCESS;
426 }
427 
reg_is_11d_offloaded(struct wlan_objmgr_psoc * psoc)428 bool reg_is_11d_offloaded(struct wlan_objmgr_psoc *psoc)
429 {
430 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
431 
432 	psoc_priv_obj = reg_get_psoc_obj(psoc);
433 	if (!psoc_priv_obj) {
434 		reg_err("NULL reg psoc private obj");
435 		return false;
436 	}
437 
438 	return psoc_priv_obj->is_11d_offloaded;
439 }
440 #endif
441