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