1 /*
2 * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2024 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: contains scan south bound interface definitions
22 */
23
24 #include <wlan_cmn.h>
25 #include <qdf_list.h>
26 #include "../../core/src/wlan_scan_main.h"
27 #include <wlan_scan_utils_api.h>
28 #include <wlan_scan_ucfg_api.h>
29 #include <wlan_scan_tgt_api.h>
30 #include <wlan_objmgr_cmn.h>
31 #include <wlan_lmac_if_def.h>
32 #include <wlan_objmgr_psoc_obj.h>
33 #include <wlan_objmgr_pdev_obj.h>
34 #include <wlan_objmgr_vdev_obj.h>
35 #include <../../core/src/wlan_scan_manager.h>
36
37 static inline struct wlan_lmac_if_scan_tx_ops *
wlan_psoc_get_scan_txops(struct wlan_objmgr_psoc * psoc)38 wlan_psoc_get_scan_txops(struct wlan_objmgr_psoc *psoc)
39 {
40 struct wlan_lmac_if_tx_ops *tx_ops;
41
42 tx_ops = wlan_psoc_get_lmac_if_txops(psoc);
43 if (!tx_ops) {
44 scm_err("tx_ops is NULL");
45 return NULL;
46 }
47
48 return &tx_ops->scan;
49 }
50
51 static inline struct wlan_lmac_if_scan_tx_ops *
wlan_vdev_get_scan_txops(struct wlan_objmgr_vdev * vdev)52 wlan_vdev_get_scan_txops(struct wlan_objmgr_vdev *vdev)
53 {
54 struct wlan_objmgr_psoc *psoc = NULL;
55
56 psoc = wlan_vdev_get_psoc(vdev);
57 if (!psoc) {
58 scm_err("NULL psoc");
59 return NULL;
60 }
61
62 return wlan_psoc_get_scan_txops(psoc);
63 }
64
65 static inline struct wlan_lmac_if_scan_rx_ops *
wlan_vdev_get_scan_rxops(struct wlan_objmgr_vdev * vdev)66 wlan_vdev_get_scan_rxops(struct wlan_objmgr_vdev *vdev)
67 {
68 struct wlan_objmgr_psoc *psoc = NULL;
69 struct wlan_lmac_if_rx_ops *rx_ops;
70
71 psoc = wlan_vdev_get_psoc(vdev);
72 if (!psoc) {
73 scm_err("NULL psoc");
74 return NULL;
75 }
76
77 rx_ops = wlan_psoc_get_lmac_if_rxops(psoc);
78 if (!rx_ops) {
79 scm_err("rx_ops is NULL");
80 return NULL;
81 }
82
83 return &rx_ops->scan;
84 }
85
86 #ifdef FEATURE_WLAN_SCAN_PNO
87
tgt_scan_pno_start(struct wlan_objmgr_vdev * vdev,struct pno_scan_req_params * req)88 QDF_STATUS tgt_scan_pno_start(struct wlan_objmgr_vdev *vdev,
89 struct pno_scan_req_params *req)
90 {
91 struct wlan_lmac_if_scan_tx_ops *scan_ops;
92 struct wlan_objmgr_psoc *psoc;
93
94 psoc = wlan_vdev_get_psoc(vdev);
95
96 if (!psoc) {
97 scm_err("NULL PSOC");
98 return QDF_STATUS_E_FAILURE;
99 }
100 scan_ops = wlan_psoc_get_scan_txops(psoc);
101 if (!scan_ops) {
102 scm_err("NULL scan_ops");
103 return QDF_STATUS_E_FAILURE;
104 }
105 /* invoke wmi_unified_pno_start_cmd() */
106 QDF_ASSERT(scan_ops->pno_start);
107 if (scan_ops->pno_start)
108 return scan_ops->pno_start(psoc, req);
109
110 return QDF_STATUS_SUCCESS;
111 }
112
tgt_scan_pno_stop(struct wlan_objmgr_vdev * vdev,uint8_t vdev_id)113 QDF_STATUS tgt_scan_pno_stop(struct wlan_objmgr_vdev *vdev,
114 uint8_t vdev_id)
115 {
116 struct wlan_lmac_if_scan_tx_ops *scan_ops;
117 struct wlan_objmgr_psoc *psoc;
118
119 psoc = wlan_vdev_get_psoc(vdev);
120
121 if (!psoc) {
122 scm_err("NULL PSOC");
123 return QDF_STATUS_E_FAILURE;
124 }
125 scan_ops = wlan_psoc_get_scan_txops(psoc);
126 if (!scan_ops) {
127 scm_err("NULL scan_ops");
128 return QDF_STATUS_E_FAILURE;
129 }
130 /* invoke wmi_unified_pno_stop_cmd() */
131 QDF_ASSERT(scan_ops->pno_stop);
132 if (scan_ops->pno_stop)
133 return scan_ops->pno_stop(psoc, vdev_id);
134
135 return QDF_STATUS_SUCCESS;
136 }
137 #endif
138
tgt_scan_obss_disable(struct wlan_objmgr_vdev * vdev)139 QDF_STATUS tgt_scan_obss_disable(struct wlan_objmgr_vdev *vdev)
140 {
141 struct wlan_lmac_if_scan_tx_ops *scan_ops;
142 struct wlan_objmgr_psoc *psoc;
143 uint8_t vdev_id;
144
145 psoc = wlan_vdev_get_psoc(vdev);
146
147 if (!psoc) {
148 scm_err("NULL PSOC");
149 return QDF_STATUS_E_FAILURE;
150 }
151 scan_ops = wlan_psoc_get_scan_txops(psoc);
152 if (!scan_ops) {
153 scm_err("NULL scan_ops");
154 return QDF_STATUS_E_FAILURE;
155 }
156
157 vdev_id = wlan_vdev_get_id(vdev);
158
159 /* invoke wmi_unified_obss_disable_cmd() */
160 QDF_ASSERT(scan_ops->obss_disable);
161 if (scan_ops->obss_disable)
162 return scan_ops->obss_disable(psoc, vdev_id);
163
164 return QDF_STATUS_SUCCESS;
165 }
166
167 QDF_STATUS
tgt_scan_start(struct scan_start_request * req)168 tgt_scan_start(struct scan_start_request *req)
169 {
170 struct wlan_lmac_if_scan_tx_ops *scan_ops;
171 struct wlan_objmgr_psoc *psoc;
172 struct wlan_objmgr_pdev *pdev;
173 struct wlan_objmgr_vdev *vdev = req->vdev;
174
175 if (!vdev) {
176 scm_err("vdev is NULL");
177 return QDF_STATUS_E_NULL_VALUE;
178 }
179
180 psoc = wlan_vdev_get_psoc(vdev);
181 pdev = wlan_vdev_get_pdev(vdev);
182 if (!psoc || !pdev) {
183 scm_err("psoc: 0x%pK or pdev: 0x%pK is NULL", psoc, pdev);
184 return QDF_STATUS_E_NULL_VALUE;
185 }
186
187 scan_ops = wlan_psoc_get_scan_txops(psoc);
188 if (!scan_ops) {
189 scm_err("NULL scan_ops");
190 return QDF_STATUS_E_NULL_VALUE;
191 }
192
193 /* invoke wmi_unified_scan_start_cmd_send() */
194 QDF_ASSERT(scan_ops->scan_start);
195 if (scan_ops->scan_start)
196 return scan_ops->scan_start(pdev, req);
197 else
198 return QDF_STATUS_SUCCESS;
199 }
200
201
202 QDF_STATUS
tgt_scan_cancel(struct scan_cancel_request * req)203 tgt_scan_cancel(struct scan_cancel_request *req)
204 {
205 struct wlan_lmac_if_scan_tx_ops *scan_ops;
206 struct wlan_objmgr_psoc *psoc;
207 struct wlan_objmgr_pdev *pdev;
208 struct wlan_objmgr_vdev *vdev = req->vdev;
209
210 if (!vdev) {
211 scm_err("vdev is NULL");
212 return QDF_STATUS_E_NULL_VALUE;
213 }
214 psoc = wlan_vdev_get_psoc(vdev);
215 pdev = wlan_vdev_get_pdev(vdev);
216 if (!psoc || !pdev) {
217 scm_err("psoc: 0x%pK or pdev: 0x%pK is NULL", psoc, pdev);
218 return QDF_STATUS_E_NULL_VALUE;
219 }
220 scan_ops = wlan_psoc_get_scan_txops(psoc);
221 if (!scan_ops) {
222 scm_err("NULL scan_ops");
223 return QDF_STATUS_E_NULL_VALUE;
224 }
225
226 /* invoke wmi_unified_scan_stop_cmd_send() */
227 QDF_ASSERT(scan_ops->scan_cancel);
228 if (scan_ops->scan_cancel)
229 return scan_ops->scan_cancel(pdev, &req->cancel_req);
230 else
231 return QDF_STATUS_SUCCESS;
232 }
233
234 QDF_STATUS
tgt_scan_register_ev_handler(struct wlan_objmgr_psoc * psoc)235 tgt_scan_register_ev_handler(struct wlan_objmgr_psoc *psoc)
236 {
237 struct wlan_lmac_if_scan_tx_ops *scan_ops = NULL;
238
239 scan_ops = wlan_psoc_get_scan_txops(psoc);
240 if (!scan_ops) {
241 scm_err("NULL scan_ops");
242 return QDF_STATUS_E_FAILURE;
243 }
244
245 /* invoke wmi_unified_register_event_handler()
246 * since event id, handler function and context is
247 * already known to offload lmac, passing NULL as argument.
248 * DA can pass necessary arguments by clubing then into
249 * some structure.
250 */
251 QDF_ASSERT(scan_ops->scan_reg_ev_handler);
252 if (scan_ops->scan_reg_ev_handler)
253 return scan_ops->scan_reg_ev_handler(psoc, NULL);
254 else
255 return QDF_STATUS_SUCCESS;
256 }
257
258 QDF_STATUS
tgt_scan_unregister_ev_handler(struct wlan_objmgr_psoc * psoc)259 tgt_scan_unregister_ev_handler(struct wlan_objmgr_psoc *psoc)
260 {
261 struct wlan_lmac_if_scan_tx_ops *scan_ops = NULL;
262
263 scan_ops = wlan_psoc_get_scan_txops(psoc);
264 if (!scan_ops) {
265 scm_err("NULL scan_ops");
266 return QDF_STATUS_E_FAILURE;
267 }
268
269 /* invoke wmi_unified_register_event_handler()
270 * since event id, handler function and context is
271 * already known to offload lmac, passing NULL as argument.
272 * DA can pass necessary arguments by clubing then into
273 * some structure.
274 */
275 QDF_ASSERT(scan_ops->scan_unreg_ev_handler);
276 if (scan_ops->scan_unreg_ev_handler)
277 return scan_ops->scan_unreg_ev_handler(psoc, NULL);
278 else
279 return QDF_STATUS_SUCCESS;
280 }
281
282 QDF_STATUS
tgt_scan_event_handler(struct wlan_objmgr_psoc * psoc,struct scan_event_info * event_info)283 tgt_scan_event_handler(struct wlan_objmgr_psoc *psoc,
284 struct scan_event_info *event_info)
285 {
286 struct scheduler_msg msg = {0};
287 struct scan_event *event = &event_info->event;
288 uint8_t vdev_id = event->vdev_id;
289 QDF_STATUS status;
290
291 if (!psoc || !event_info) {
292 scm_err("psoc: 0x%pK, event_info: 0x%pK", psoc, event_info);
293 return QDF_STATUS_E_NULL_VALUE;
294 }
295
296 event_info->vdev =
297 wlan_objmgr_get_vdev_by_id_from_psoc(psoc,
298 vdev_id, WLAN_SCAN_ID);
299 if (!event_info->vdev) {
300 scm_err("null vdev, vdev_id: %d, psoc: 0x%pK", vdev_id, psoc);
301 return QDF_STATUS_E_INVAL;
302 }
303 msg.bodyptr = event_info;
304 msg.callback = scm_scan_event_handler;
305 msg.flush_callback = scm_scan_event_flush_callback;
306
307 status = scheduler_post_message(QDF_MODULE_ID_SCAN,
308 QDF_MODULE_ID_SCAN,
309 QDF_MODULE_ID_SCAN, &msg);
310 if (QDF_IS_STATUS_ERROR(status)) {
311 wlan_objmgr_vdev_release_ref(event_info->vdev, WLAN_SCAN_ID);
312 }
313
314 return status;
315 }
316
tgt_scan_bcn_probe_rx_callback(struct wlan_objmgr_psoc * psoc,struct wlan_objmgr_peer * peer,qdf_nbuf_t buf,struct mgmt_rx_event_params * rx_param,enum mgmt_frame_type frm_type)317 QDF_STATUS tgt_scan_bcn_probe_rx_callback(struct wlan_objmgr_psoc *psoc,
318 struct wlan_objmgr_peer *peer, qdf_nbuf_t buf,
319 struct mgmt_rx_event_params *rx_param,
320 enum mgmt_frame_type frm_type)
321 {
322 struct scheduler_msg msg = {0};
323 struct scan_bcn_probe_event *bcn = NULL;
324 QDF_STATUS status;
325 uint32_t scan_queue_size = 0;
326
327 if ((frm_type != MGMT_PROBE_RESP) &&
328 (frm_type != MGMT_BEACON)) {
329 scm_err("frame is not beacon or probe resp");
330 status = QDF_STATUS_E_INVAL;
331 goto free;
332 }
333
334 bcn = qdf_mem_malloc_atomic(sizeof(*bcn));
335 if (!bcn) {
336 status = QDF_STATUS_E_NOMEM;
337 goto free;
338 }
339 bcn->rx_data =
340 qdf_mem_malloc_atomic(sizeof(*rx_param));
341 if (!bcn->rx_data) {
342 status = QDF_STATUS_E_NOMEM;
343 goto free;
344 }
345
346 if (frm_type == MGMT_PROBE_RESP)
347 bcn->frm_type = MGMT_SUBTYPE_PROBE_RESP;
348 else
349 bcn->frm_type = MGMT_SUBTYPE_BEACON;
350
351 /* Check if the beacon/probe frame can be posted in the scan queue */
352 status = scheduler_get_queue_size(QDF_MODULE_ID_SCAN, &scan_queue_size);
353 if (!QDF_IS_STATUS_SUCCESS(status) ||
354 scan_queue_size > MAX_BCN_PROBE_IN_SCAN_QUEUE) {
355 scm_debug_rl("Dropping beacon/probe frame, queue size %d",
356 scan_queue_size);
357 status = QDF_STATUS_E_FAILURE;
358 goto free;
359 }
360
361 status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_SCAN_ID);
362 if (QDF_IS_STATUS_ERROR(status)) {
363 scm_info("unable to get reference");
364 goto free;
365 }
366
367 bcn->psoc = psoc;
368 bcn->buf = buf;
369 bcn->save_rnr_info = false;
370 qdf_mem_copy(bcn->rx_data, rx_param, sizeof(*rx_param));
371
372 msg.bodyptr = bcn;
373 msg.callback = scm_handle_bcn_probe;
374 msg.flush_callback = scm_bcn_probe_flush_callback;
375
376 status = scheduler_post_message(QDF_MODULE_ID_SCAN,
377 QDF_MODULE_ID_SCAN,
378 QDF_MODULE_ID_SCAN, &msg);
379
380 if (QDF_IS_STATUS_SUCCESS(status))
381 return status;
382
383 wlan_objmgr_psoc_release_ref(psoc, WLAN_SCAN_ID);
384
385 free:
386 if (bcn && bcn->rx_data)
387 qdf_mem_free(bcn->rx_data);
388 if (bcn)
389 qdf_mem_free(bcn);
390 if (buf)
391 qdf_nbuf_free(buf);
392
393 return status;
394 }
395
396 QDF_STATUS
tgt_scan_set_max_active_scans(struct wlan_objmgr_psoc * psoc,uint32_t max_active_scans)397 tgt_scan_set_max_active_scans(struct wlan_objmgr_psoc *psoc,
398 uint32_t max_active_scans)
399 {
400 struct scan_default_params *scan_params = NULL;
401
402 if (!psoc) {
403 scm_err("null psoc");
404 return QDF_STATUS_E_NULL_VALUE;
405 }
406
407 scan_params = wlan_scan_psoc_get_def_params(psoc);
408 if (!scan_params) {
409 scm_err("wlan_scan_psoc_get_def_params returned NULL");
410 return QDF_STATUS_E_NULL_VALUE;
411 }
412
413 scan_params->max_active_scans_allowed = max_active_scans;
414
415 return QDF_STATUS_SUCCESS;
416 }
417