1 /*
2 * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2022, 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: wlan_serialization_scan.c
22 * This file defines the functions which deals with
23 * serialization scan commands.
24 */
25
26 #include "wlan_serialization_utils_i.h"
27 #include "wlan_serialization_main_i.h"
28 #include "wlan_serialization_api.h"
29 #include "wlan_serialization_scan_i.h"
30 #include <wlan_objmgr_vdev_obj.h>
31 #include <wlan_objmgr_pdev_obj.h>
32 #include <qdf_mc_timer.h>
33 #include <wlan_utility.h>
34
35 void
wlan_serialization_active_scan_cmd_count_handler(struct wlan_objmgr_psoc * psoc,void * obj,void * arg)36 wlan_serialization_active_scan_cmd_count_handler(struct wlan_objmgr_psoc *psoc,
37 void *obj, void *arg)
38 {
39 struct wlan_objmgr_pdev *pdev = obj;
40 struct wlan_ser_pdev_obj *ser_pdev_obj;
41 struct wlan_serialization_pdev_queue *pdev_q;
42 uint32_t *count = arg;
43
44 if (!pdev) {
45 ser_err("invalid pdev");
46 return;
47 }
48
49 ser_pdev_obj = wlan_objmgr_pdev_get_comp_private_obj(
50 pdev, WLAN_UMAC_COMP_SERIALIZATION);
51
52 pdev_q = &ser_pdev_obj->pdev_q[SER_PDEV_QUEUE_COMP_SCAN];
53 *count += wlan_serialization_list_size(&pdev_q->active_list);
54 }
55
56 bool
wlan_serialization_is_scan_pending_queue_empty(struct wlan_serialization_command * cmd)57 wlan_serialization_is_scan_pending_queue_empty(
58 struct wlan_serialization_command *cmd)
59 {
60 struct wlan_objmgr_pdev *pdev;
61 struct wlan_ser_pdev_obj *ser_pdev_obj = NULL;
62 struct wlan_serialization_pdev_queue *pdev_q;
63 bool status = false;
64
65 pdev = wlan_serialization_get_pdev_from_cmd(cmd);
66 ser_pdev_obj = wlan_serialization_get_pdev_obj(pdev);
67
68 pdev_q = &ser_pdev_obj->pdev_q[SER_PDEV_QUEUE_COMP_SCAN];
69
70 if (qdf_list_empty(&pdev_q->pending_list))
71 status = true;
72
73 return status;
74 }
75
76 bool
wlan_serialization_is_active_scan_cmd_allowed(struct wlan_serialization_command * cmd)77 wlan_serialization_is_active_scan_cmd_allowed(
78 struct wlan_serialization_command *cmd)
79 {
80 uint32_t count = 0;
81 struct wlan_objmgr_pdev *pdev = NULL;
82 struct wlan_objmgr_psoc *psoc;
83 bool status = false;
84
85 pdev = wlan_serialization_get_pdev_from_cmd(cmd);
86 if (!pdev) {
87 ser_err("invalid pdev");
88 goto error;
89 }
90
91 psoc = wlan_pdev_get_psoc(pdev);
92 if (!psoc) {
93 ser_err("invalid psoc");
94 goto error;
95 }
96
97 wlan_objmgr_iterate_obj_list(
98 psoc, WLAN_PDEV_OP,
99 wlan_serialization_active_scan_cmd_count_handler,
100 &count, 1, WLAN_SERIALIZATION_ID);
101 if (count < ucfg_scan_get_max_active_scans(psoc))
102 status = true;
103
104 error:
105 return status;
106 }
107
wlan_ser_match_cmd_scan_id(qdf_list_node_t * nnode,struct wlan_serialization_command ** cmd,uint16_t scan_id,struct wlan_objmgr_vdev * vdev)108 bool wlan_ser_match_cmd_scan_id(
109 qdf_list_node_t *nnode,
110 struct wlan_serialization_command **cmd,
111 uint16_t scan_id, struct wlan_objmgr_vdev *vdev)
112 {
113 struct wlan_serialization_command_list *cmd_list = NULL;
114 bool match_found = false;
115
116 cmd_list = qdf_container_of(nnode,
117 struct wlan_serialization_command_list,
118 pdev_node);
119 if ((cmd_list->cmd.cmd_id == scan_id) &&
120 (cmd_list->cmd.vdev == vdev)) {
121 *cmd = &cmd_list->cmd;
122 match_found = true;
123 };
124
125 ser_debug("match found: %d", match_found);
126
127 return match_found;
128 }
129
130 enum wlan_serialization_status
wlan_ser_add_scan_cmd(struct wlan_ser_pdev_obj * ser_pdev_obj,struct wlan_serialization_command_list * cmd_list,uint8_t is_cmd_for_active_queue)131 wlan_ser_add_scan_cmd(
132 struct wlan_ser_pdev_obj *ser_pdev_obj,
133 struct wlan_serialization_command_list *cmd_list,
134 uint8_t is_cmd_for_active_queue)
135 {
136 enum wlan_serialization_status status;
137
138 status = wlan_serialization_add_cmd_to_pdev_queue(
139 ser_pdev_obj, cmd_list,
140 is_cmd_for_active_queue);
141
142 return status;
143 }
144
145 QDF_STATUS
wlan_ser_remove_scan_cmd(struct wlan_ser_pdev_obj * ser_pdev_obj,struct wlan_serialization_command_list ** pcmd_list,struct wlan_serialization_command * cmd,uint8_t is_active_cmd)146 wlan_ser_remove_scan_cmd(
147 struct wlan_ser_pdev_obj *ser_pdev_obj,
148 struct wlan_serialization_command_list **pcmd_list,
149 struct wlan_serialization_command *cmd,
150 uint8_t is_active_cmd)
151 {
152 QDF_STATUS status;
153
154 status = wlan_serialization_remove_cmd_from_pdev_queue(
155 ser_pdev_obj, pcmd_list, cmd, is_active_cmd);
156
157 return status;
158 }
159
160 enum wlan_serialization_cmd_status
wlan_ser_cancel_scan_cmd(struct wlan_ser_pdev_obj * ser_obj,struct wlan_objmgr_pdev * pdev,struct wlan_objmgr_vdev * vdev,struct wlan_serialization_command * cmd,enum wlan_serialization_cmd_type cmd_type,uint8_t is_active_queue)161 wlan_ser_cancel_scan_cmd(
162 struct wlan_ser_pdev_obj *ser_obj,
163 struct wlan_objmgr_pdev *pdev, struct wlan_objmgr_vdev *vdev,
164 struct wlan_serialization_command *cmd,
165 enum wlan_serialization_cmd_type cmd_type,
166 uint8_t is_active_queue)
167 {
168 qdf_list_t *queue;
169 struct wlan_serialization_pdev_queue *pdev_q;
170 uint32_t qsize;
171 struct wlan_serialization_command_list *cmd_list = NULL;
172 struct wlan_serialization_command cmd_bkup;
173 qdf_list_node_t *nnode = NULL, *pnode = NULL;
174 enum wlan_serialization_cmd_status status = WLAN_SER_CMD_NOT_FOUND;
175 struct wlan_objmgr_psoc *psoc = NULL;
176 QDF_STATUS qdf_status;
177
178 pdev_q = &ser_obj->pdev_q[SER_PDEV_QUEUE_COMP_SCAN];
179
180 if (is_active_queue)
181 queue = &pdev_q->active_list;
182 else
183 queue = &pdev_q->pending_list;
184
185 if (pdev)
186 psoc = wlan_pdev_get_psoc(pdev);
187 else if (vdev)
188 psoc = wlan_vdev_get_psoc(vdev);
189 else if (cmd && cmd->vdev)
190 psoc = wlan_vdev_get_psoc(cmd->vdev);
191 else
192 ser_debug("Can't find psoc");
193
194 wlan_serialization_acquire_lock(&pdev_q->pdev_queue_lock);
195
196 qsize = wlan_serialization_list_size(queue);
197 while (!wlan_serialization_list_empty(queue) && qsize--) {
198 if (wlan_serialization_get_cmd_from_queue(
199 queue, &nnode) != QDF_STATUS_SUCCESS) {
200 ser_err("can't read cmd from queue");
201 status = WLAN_SER_CMD_NOT_FOUND;
202 break;
203 }
204 cmd_list =
205 qdf_container_of(nnode,
206 struct wlan_serialization_command_list,
207 pdev_node);
208
209 if (cmd && !wlan_serialization_match_cmd_id_type(
210 nnode, cmd,
211 WLAN_SER_PDEV_NODE)) {
212 pnode = nnode;
213 continue;
214 }
215 if (vdev &&
216 !wlan_serialization_match_cmd_vdev(nnode,
217 vdev,
218 WLAN_SER_PDEV_NODE)) {
219 pnode = nnode;
220 continue;
221 }
222
223 if (pdev &&
224 !wlan_serialization_match_cmd_pdev(nnode,
225 pdev,
226 WLAN_SER_PDEV_NODE)) {
227 pnode = nnode;
228 continue;
229 }
230
231 /*
232 * active queue can't be removed directly, requester needs to
233 * wait for active command response and send remove request for
234 * active command separately
235 */
236 if (is_active_queue) {
237 if (!psoc || !cmd_list) {
238 ser_err("psoc:0x%pK, cmd_list:0x%pK",
239 psoc, cmd_list);
240 status = WLAN_SER_CMD_NOT_FOUND;
241 break;
242 }
243
244 /* Cancel request received for a cmd in active
245 * queue which has not been activated yet, we mark
246 * it as CMD_ACTIVE_MARKED_FOR_CANCEL and remove
247 * the cmd after activation
248 */
249 if (qdf_atomic_test_bit(CMD_MARKED_FOR_ACTIVATION,
250 &cmd_list->cmd_in_use)) {
251 qdf_atomic_set_bit(CMD_ACTIVE_MARKED_FOR_CANCEL,
252 &cmd_list->cmd_in_use);
253 status = WLAN_SER_CMD_MARKED_FOR_ACTIVATION;
254 continue;
255 }
256
257 qdf_status = wlan_serialization_find_and_stop_timer(
258 psoc, &cmd_list->cmd,
259 SER_CANCEL);
260 if (QDF_IS_STATUS_ERROR(qdf_status)) {
261 ser_err("Can't fix timer for active cmd");
262 status = WLAN_SER_CMD_NOT_FOUND;
263 /*
264 * This should not happen, as an active command
265 * should always have the timer.
266 */
267 QDF_BUG(0);
268 break;
269 }
270
271 status = WLAN_SER_CMD_IN_ACTIVE_LIST;
272 }
273
274 qdf_mem_copy(&cmd_bkup, &cmd_list->cmd,
275 sizeof(struct wlan_serialization_command));
276
277 qdf_status =
278 wlan_serialization_remove_node(queue,
279 &cmd_list->pdev_node);
280
281 if (qdf_status != QDF_STATUS_SUCCESS) {
282 ser_err("can't remove cmd from pdev queue");
283 status = WLAN_SER_CMD_NOT_FOUND;
284 break;
285 }
286
287 qdf_mem_zero(&cmd_list->cmd,
288 sizeof(struct wlan_serialization_command));
289 cmd_list->cmd_in_use = 0;
290 qdf_status = wlan_serialization_insert_back(
291 &pdev_q->cmd_pool_list,
292 &cmd_list->pdev_node);
293
294 if (QDF_STATUS_SUCCESS != qdf_status) {
295 ser_err("can't remove cmd from queue");
296 status = WLAN_SER_CMD_NOT_FOUND;
297 break;
298 }
299 nnode = pnode;
300
301 wlan_ser_update_cmd_history(pdev_q, &cmd_bkup,
302 SER_CANCEL, false, is_active_queue);
303
304 wlan_serialization_release_lock(&pdev_q->pdev_queue_lock);
305 /*
306 * call pending cmd's callback to notify that
307 * it is being removed
308 */
309 if (cmd_bkup.cmd_cb) {
310 ser_debug("Cancel command: type %d id %d vdev %d and Release memory",
311 cmd_bkup.cmd_type, cmd_bkup.cmd_id,
312 wlan_vdev_get_id(cmd_bkup.vdev));
313 cmd_bkup.cmd_cb(&cmd_bkup, WLAN_SER_CB_CANCEL_CMD);
314 cmd_bkup.cmd_cb(&cmd_bkup, WLAN_SER_CB_RELEASE_MEM_CMD);
315 }
316
317 wlan_serialization_acquire_lock(&pdev_q->pdev_queue_lock);
318
319 if (!is_active_queue)
320 status = WLAN_SER_CMD_IN_PENDING_LIST;
321 }
322
323 wlan_serialization_release_lock(&pdev_q->pdev_queue_lock);
324
325 return status;
326 }
327
wlan_ser_move_scan_pending_to_active(struct wlan_ser_pdev_obj * ser_pdev_obj)328 enum wlan_serialization_status wlan_ser_move_scan_pending_to_active(
329 struct wlan_ser_pdev_obj *ser_pdev_obj)
330 {
331 struct wlan_serialization_command_list *pending_cmd_list = NULL;
332 struct wlan_serialization_command_list *active_cmd_list;
333 struct wlan_serialization_command cmd_to_remove;
334 enum wlan_serialization_status status = WLAN_SER_CMD_DENIED_UNSPECIFIED;
335 QDF_STATUS qdf_status;
336 struct wlan_serialization_pdev_queue *pdev_queue;
337 qdf_list_t *pending_queue;
338 qdf_list_node_t *pending_node = NULL;
339
340 pdev_queue = &ser_pdev_obj->pdev_q[SER_PDEV_QUEUE_COMP_SCAN];
341
342 if (!ser_pdev_obj) {
343 ser_err("Can't find ser_pdev_obj");
344 goto error;
345 }
346
347 wlan_serialization_acquire_lock(&pdev_queue->pdev_queue_lock);
348
349 pending_queue = &pdev_queue->pending_list;
350
351 if (wlan_serialization_list_empty(pending_queue)) {
352 wlan_serialization_release_lock(&pdev_queue->pdev_queue_lock);
353 goto error;
354 }
355
356 qdf_status = wlan_serialization_peek_front(pending_queue,
357 &pending_node);
358 if (QDF_STATUS_SUCCESS != qdf_status) {
359 wlan_serialization_release_lock(&pdev_queue->pdev_queue_lock);
360 ser_err("can't read from pending queue");
361 goto error;
362 }
363
364 pending_cmd_list =
365 qdf_container_of(pending_node,
366 struct wlan_serialization_command_list,
367 pdev_node);
368
369 if (!pending_cmd_list) {
370 wlan_serialization_release_lock(&pdev_queue->pdev_queue_lock);
371 goto error;
372 }
373
374 qdf_mem_copy(&cmd_to_remove, &pending_cmd_list->cmd,
375 sizeof(struct wlan_serialization_command));
376
377 if (!wlan_serialization_is_active_scan_cmd_allowed(&cmd_to_remove)) {
378 ser_debug("active scan command not allowed");
379 wlan_serialization_release_lock(&pdev_queue->pdev_queue_lock);
380 goto error;
381 }
382
383 qdf_status =
384 wlan_ser_remove_scan_cmd(ser_pdev_obj,
385 &pending_cmd_list,
386 &cmd_to_remove, false);
387
388 wlan_ser_update_cmd_history(pdev_queue, &pending_cmd_list->cmd,
389 SER_PENDING_TO_ACTIVE,
390 false, false);
391
392 if (QDF_STATUS_SUCCESS != qdf_status) {
393 wlan_serialization_release_lock(&pdev_queue->pdev_queue_lock);
394 ser_err("Can't remove from pendingQ id %d type %d",
395 pending_cmd_list->cmd.cmd_id,
396 pending_cmd_list->cmd.cmd_type);
397 QDF_ASSERT(0);
398 status = WLAN_SER_CMD_DENIED_UNSPECIFIED;
399 goto error;
400 }
401
402 active_cmd_list = pending_cmd_list;
403
404 status = wlan_ser_add_scan_cmd(ser_pdev_obj,
405 active_cmd_list, true);
406
407 if (WLAN_SER_CMD_ACTIVE != status) {
408 wlan_serialization_insert_back(
409 &pdev_queue->cmd_pool_list,
410 &active_cmd_list->pdev_node);
411 wlan_serialization_release_lock(&pdev_queue->pdev_queue_lock);
412 status = WLAN_SER_CMD_DENIED_UNSPECIFIED;
413 ser_err("Can't add cmd to activeQ id-%d type-%d",
414 active_cmd_list->cmd.cmd_id,
415 active_cmd_list->cmd.cmd_type);
416 QDF_ASSERT(0);
417 goto error;
418 }
419
420 qdf_atomic_set_bit(CMD_MARKED_FOR_ACTIVATION,
421 &active_cmd_list->cmd_in_use);
422
423 wlan_ser_update_cmd_history(pdev_queue, &active_cmd_list->cmd,
424 SER_PENDING_TO_ACTIVE,
425 true, true);
426
427 wlan_serialization_release_lock(&pdev_queue->pdev_queue_lock);
428
429 wlan_serialization_activate_cmd(active_cmd_list, ser_pdev_obj,
430 SER_PENDING_TO_ACTIVE);
431 error:
432 return status;
433 }
434