xref: /wlan-dirver/qca-wifi-host-cmn/umac/regulatory/core/src/reg_offload_11d_scan.c (revision 8ddef7dd9a290d4a9b1efd5d3efacf51d78a1a0d)
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 		return QDF_STATUS_E_FAILURE;
129 	}
130 
131 	if (psoc_priv_obj->enable_11d_supp) {
132 		start_req.vdev_id = psoc_priv_obj->vdev_id_for_11d_scan;
133 		start_req.scan_period_msec = psoc_priv_obj->scan_11d_interval;
134 		start_req.start_interval_msec = 0;
135 		reg_debug("sending start msg");
136 		tx_ops->start_11d_scan(psoc, &start_req);
137 	} else {
138 		stop_req.vdev_id = psoc_priv_obj->vdev_id_for_11d_scan;
139 		reg_debug("sending stop msg");
140 		tx_ops->stop_11d_scan(psoc, &stop_req);
141 	}
142 
143 	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
144 	return QDF_STATUS_SUCCESS;
145 }
146 
147 /**
148  * reg_sched_11d_msg() - Schedules 11d scan message.
149  * @psoc: soc context
150  */
151 static QDF_STATUS reg_sched_11d_msg(struct wlan_objmgr_psoc *psoc)
152 {
153 	struct scheduler_msg msg = {0};
154 	QDF_STATUS status;
155 
156 	status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_SB_ID);
157 	if (QDF_IS_STATUS_ERROR(status)) {
158 		reg_err("error taking psoc ref cnt");
159 		return status;
160 	}
161 
162 	msg.bodyptr = psoc;
163 	msg.callback = reg_send_11d_msg_cbk;
164 	msg.flush_callback = reg_send_11d_flush_cbk;
165 
166 	status = scheduler_post_message(QDF_MODULE_ID_REGULATORY,
167 					QDF_MODULE_ID_REGULATORY,
168 					QDF_MODULE_ID_TARGET_IF, &msg);
169 	if (QDF_IS_STATUS_ERROR(status)) {
170 		wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID);
171 		reg_err("scheduler msg posting failed");
172 	}
173 
174 	return status;
175 }
176 
177 void reg_run_11d_state_machine(struct wlan_objmgr_psoc *psoc)
178 {
179 	bool temp_11d_support;
180 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
181 	bool world_mode;
182 
183 	psoc_priv_obj = reg_get_psoc_obj(psoc);
184 	if (!psoc_priv_obj) {
185 		reg_err("reg psoc private obj is NULL");
186 		return;
187 	}
188 
189 	world_mode = reg_is_world_alpha2(psoc_priv_obj->cur_country);
190 
191 	temp_11d_support = psoc_priv_obj->enable_11d_supp;
192 	if ((psoc_priv_obj->enable_11d_in_world_mode) && (world_mode))
193 		psoc_priv_obj->enable_11d_supp = true;
194 	else if (((psoc_priv_obj->user_ctry_set) &&
195 		  (psoc_priv_obj->user_ctry_priority)) ||
196 		 (psoc_priv_obj->master_vdev_cnt))
197 		psoc_priv_obj->enable_11d_supp = false;
198 	else
199 		psoc_priv_obj->enable_11d_supp =
200 			psoc_priv_obj->enable_11d_supp_original;
201 
202 	reg_debug("inside 11d state machine:tmp %d 11d_supp %d org %d set %d pri %d cnt %d vdev %d",
203 		  temp_11d_support,
204 		  psoc_priv_obj->enable_11d_supp,
205 		  psoc_priv_obj->enable_11d_supp_original,
206 		  psoc_priv_obj->user_ctry_set,
207 		  psoc_priv_obj->user_ctry_priority,
208 		  psoc_priv_obj->master_vdev_cnt,
209 		  psoc_priv_obj->vdev_id_for_11d_scan);
210 
211 	if (temp_11d_support != psoc_priv_obj->enable_11d_supp) {
212 		if (psoc_priv_obj->is_11d_offloaded)
213 			reg_sched_11d_msg(psoc);
214 		else
215 			reg_11d_host_scan(psoc_priv_obj);
216 	}
217 }
218 
219 QDF_STATUS reg_11d_vdev_created_update(struct wlan_objmgr_vdev *vdev)
220 {
221 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
222 	struct wlan_objmgr_pdev *parent_pdev;
223 	struct wlan_objmgr_psoc *parent_psoc;
224 	uint32_t vdev_id;
225 	enum QDF_OPMODE op_mode;
226 	uint8_t i;
227 
228 	op_mode = wlan_vdev_mlme_get_opmode(vdev);
229 
230 	parent_pdev = wlan_vdev_get_pdev(vdev);
231 	parent_psoc = wlan_pdev_get_psoc(parent_pdev);
232 
233 	psoc_priv_obj = reg_get_psoc_obj(parent_psoc);
234 	if (!psoc_priv_obj) {
235 		reg_err("reg psoc private obj is NULL");
236 		return QDF_STATUS_E_FAULT;
237 	}
238 
239 	if ((op_mode == QDF_STA_MODE) ||
240 	    (op_mode == QDF_P2P_DEVICE_MODE) ||
241 	    (op_mode == QDF_P2P_CLIENT_MODE)) {
242 		vdev_id = wlan_vdev_get_id(vdev);
243 		if (!psoc_priv_obj->vdev_cnt_11d) {
244 			psoc_priv_obj->vdev_id_for_11d_scan = vdev_id;
245 			reg_debug("running 11d state machine, opmode %d",
246 				  op_mode);
247 			reg_run_11d_state_machine(parent_psoc);
248 		}
249 
250 		for (i = 0; i < MAX_STA_VDEV_CNT; i++) {
251 			if (psoc_priv_obj->vdev_ids_11d[i] == INVALID_VDEV_ID) {
252 				psoc_priv_obj->vdev_ids_11d[i] = vdev_id;
253 				break;
254 			}
255 		}
256 		psoc_priv_obj->vdev_cnt_11d++;
257 	}
258 
259 	if ((op_mode == QDF_P2P_GO_MODE) || (op_mode == QDF_SAP_MODE)) {
260 		reg_debug("running 11d state machine, opmode %d", op_mode);
261 		psoc_priv_obj->master_vdev_cnt++;
262 		reg_run_11d_state_machine(parent_psoc);
263 	}
264 
265 	return QDF_STATUS_SUCCESS;
266 }
267 
268 QDF_STATUS reg_11d_vdev_delete_update(struct wlan_objmgr_vdev *vdev)
269 {
270 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
271 	struct wlan_objmgr_pdev *parent_pdev;
272 	struct wlan_objmgr_psoc *parent_psoc;
273 	enum QDF_OPMODE op_mode;
274 	uint32_t vdev_id;
275 	uint8_t i;
276 
277 	if (!vdev) {
278 		reg_err("vdev is NULL");
279 		return QDF_STATUS_E_INVAL;
280 	}
281 	op_mode = wlan_vdev_mlme_get_opmode(vdev);
282 
283 	parent_pdev = wlan_vdev_get_pdev(vdev);
284 	parent_psoc = wlan_pdev_get_psoc(parent_pdev);
285 
286 	psoc_priv_obj = reg_get_psoc_obj(parent_psoc);
287 	if (!psoc_priv_obj) {
288 		reg_err("reg psoc private obj is NULL");
289 		return QDF_STATUS_E_FAULT;
290 	}
291 
292 	if ((op_mode == QDF_P2P_GO_MODE) || (op_mode == QDF_SAP_MODE)) {
293 		psoc_priv_obj->master_vdev_cnt--;
294 		reg_debug("run 11d state machine, deleted opmode %d",
295 			  op_mode);
296 		reg_run_11d_state_machine(parent_psoc);
297 		return QDF_STATUS_SUCCESS;
298 	}
299 
300 	if ((op_mode == QDF_STA_MODE) || (op_mode == QDF_P2P_DEVICE_MODE) ||
301 	    (op_mode == QDF_P2P_CLIENT_MODE)) {
302 		vdev_id = wlan_vdev_get_id(vdev);
303 		for (i = 0; i < MAX_STA_VDEV_CNT; i++) {
304 			if (psoc_priv_obj->vdev_ids_11d[i] == vdev_id) {
305 				psoc_priv_obj->vdev_ids_11d[i] =
306 					INVALID_VDEV_ID;
307 				psoc_priv_obj->vdev_cnt_11d--;
308 				break;
309 			}
310 		}
311 
312 		if ((psoc_priv_obj->vdev_id_for_11d_scan != vdev_id) ||
313 		    !psoc_priv_obj->vdev_cnt_11d)
314 			return QDF_STATUS_SUCCESS;
315 
316 		for (i = 0; i < MAX_STA_VDEV_CNT; i++) {
317 			if (psoc_priv_obj->vdev_ids_11d[i] == INVALID_VDEV_ID)
318 				continue;
319 			psoc_priv_obj->vdev_id_for_11d_scan =
320 				psoc_priv_obj->vdev_ids_11d[i];
321 			psoc_priv_obj->enable_11d_supp = false;
322 			reg_debug("running 11d state machine, vdev %d",
323 				  psoc_priv_obj->vdev_id_for_11d_scan);
324 			reg_run_11d_state_machine(parent_psoc);
325 			break;
326 		}
327 	}
328 
329 	return QDF_STATUS_SUCCESS;
330 }
331 
332 bool reg_is_11d_scan_inprogress(struct wlan_objmgr_psoc *psoc)
333 {
334 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
335 
336 	psoc_priv_obj = reg_get_psoc_obj(psoc);
337 	if (!psoc_priv_obj) {
338 		reg_err("reg psoc private obj is NULL");
339 		return false;
340 	}
341 
342 	return psoc_priv_obj->enable_11d_supp;
343 }
344 
345 QDF_STATUS reg_save_new_11d_country(struct wlan_objmgr_psoc *psoc,
346 				    uint8_t *country)
347 {
348 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
349 	struct wlan_lmac_if_reg_tx_ops *tx_ops;
350 	struct set_country country_code;
351 	uint8_t pdev_id;
352 
353 	psoc_priv_obj = reg_get_psoc_obj(psoc);
354 	if (!psoc_priv_obj) {
355 		reg_err("reg psoc private obj is NULL");
356 
357 		return QDF_STATUS_E_FAILURE;
358 	}
359 
360 	pdev_id = psoc_priv_obj->def_pdev_id;
361 	psoc_priv_obj->new_11d_ctry_pending[pdev_id] = true;
362 	qdf_mem_copy(country_code.country, country, REG_ALPHA2_LEN + 1);
363 	country_code.pdev_id = pdev_id;
364 
365 	if (psoc_priv_obj->offload_enabled) {
366 		tx_ops = reg_get_psoc_tx_ops(psoc);
367 		if (tx_ops->set_country_code) {
368 			tx_ops->set_country_code(psoc, &country_code);
369 		} else {
370 			reg_err("country set handler is not present");
371 			psoc_priv_obj->new_11d_ctry_pending[pdev_id] = false;
372 			return QDF_STATUS_E_FAULT;
373 		}
374 	}
375 
376 	return QDF_STATUS_SUCCESS;
377 }
378 
379 bool reg_11d_enabled_on_host(struct wlan_objmgr_psoc *psoc)
380 {
381 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
382 
383 	psoc_priv_obj = reg_get_psoc_obj(psoc);
384 	if (!psoc_priv_obj) {
385 		reg_err("reg psoc private obj is NULL");
386 		return QDF_STATUS_E_FAILURE;
387 	}
388 
389 	return (psoc_priv_obj->enable_11d_supp &&
390 		!psoc_priv_obj->is_11d_offloaded);
391 }
392 
393 QDF_STATUS reg_set_11d_offloaded(struct wlan_objmgr_psoc *psoc, bool val)
394 {
395 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
396 
397 	psoc_priv_obj = reg_get_psoc_obj(psoc);
398 	if (!psoc_priv_obj) {
399 		reg_err("psoc reg component is NULL");
400 		return QDF_STATUS_E_FAILURE;
401 	}
402 
403 	psoc_priv_obj->is_11d_offloaded = val;
404 	reg_debug("set is_11d_offloaded %d", val);
405 	return QDF_STATUS_SUCCESS;
406 }
407 
408 bool reg_is_11d_offloaded(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 false;
416 	}
417 
418 	return psoc_priv_obj->is_11d_offloaded;
419 }
420 #endif
421