xref: /wlan-dirver/qca-wifi-host-cmn/umac/regulatory/core/src/reg_offload_11d_scan.c (revision dd4dc88b837a295134aa9869114a2efee0f4894b)
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 
371 	psoc_priv_obj = reg_get_psoc_obj(psoc);
372 	if (!psoc_priv_obj) {
373 		reg_err("reg psoc private obj is NULL");
374 
375 		return QDF_STATUS_E_FAILURE;
376 	}
377 
378 	pdev_id = psoc_priv_obj->def_pdev_id;
379 	psoc_priv_obj->new_11d_ctry_pending[pdev_id] = true;
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("country set handler is not present");
389 			psoc_priv_obj->new_11d_ctry_pending[pdev_id] = false;
390 			return QDF_STATUS_E_FAULT;
391 		}
392 	}
393 
394 	return QDF_STATUS_SUCCESS;
395 }
396 
397 bool reg_11d_enabled_on_host(struct wlan_objmgr_psoc *psoc)
398 {
399 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
400 
401 	psoc_priv_obj = reg_get_psoc_obj(psoc);
402 	if (!psoc_priv_obj) {
403 		reg_err("reg psoc private obj is NULL");
404 		return QDF_STATUS_E_FAILURE;
405 	}
406 
407 	return (psoc_priv_obj->enable_11d_supp &&
408 		!psoc_priv_obj->is_11d_offloaded);
409 }
410 
411 QDF_STATUS reg_set_11d_offloaded(struct wlan_objmgr_psoc *psoc, bool val)
412 {
413 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
414 
415 	psoc_priv_obj = reg_get_psoc_obj(psoc);
416 	if (!psoc_priv_obj) {
417 		reg_err("psoc reg component is NULL");
418 		return QDF_STATUS_E_FAILURE;
419 	}
420 
421 	psoc_priv_obj->is_11d_offloaded = val;
422 	reg_debug("set is_11d_offloaded %d", val);
423 	return QDF_STATUS_SUCCESS;
424 }
425 
426 bool reg_is_11d_offloaded(struct wlan_objmgr_psoc *psoc)
427 {
428 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
429 
430 	psoc_priv_obj = reg_get_psoc_obj(psoc);
431 	if (!psoc_priv_obj) {
432 		reg_err("reg psoc private obj is NULL");
433 		return false;
434 	}
435 
436 	return psoc_priv_obj->is_11d_offloaded;
437 }
438 #endif
439