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