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