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