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