/* * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved. * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all * copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /** * DOC: wlan_serialization_internal.c * This file defines the functions which are called * from serialization public API's and are internal * to serialization. */ #include #include #include #include #include #include #include "wlan_serialization_api.h" #include "wlan_serialization_main_i.h" #include "wlan_serialization_utils_i.h" #include "wlan_serialization_non_scan_i.h" #include "wlan_serialization_scan_i.h" #include "wlan_serialization_internal_i.h" bool wlan_serialization_is_cmd_present_queue( struct wlan_serialization_command *cmd, uint8_t is_active_queue) { qdf_list_t *queue; bool status = false; enum wlan_serialization_node node_type; struct wlan_ser_pdev_obj *ser_pdev_obj; struct wlan_ser_vdev_obj *ser_vdev_obj; enum wlan_serialization_cmd_type cmd_type; if (!cmd) { ser_err("invalid cmd"); goto error; } cmd_type = cmd->cmd_type; ser_pdev_obj = wlan_serialization_get_pdev_obj( wlan_serialization_get_pdev_from_cmd(cmd)); if (!ser_pdev_obj) { ser_err("invalid ser vdev obj"); goto error; } ser_vdev_obj = wlan_serialization_get_vdev_obj( wlan_serialization_get_vdev_from_cmd(cmd)); if (!ser_vdev_obj) { ser_err("invalid ser pdev obj"); goto error; } if (cmd_type < WLAN_SER_CMD_NONSCAN) { queue = wlan_serialization_get_list_from_pdev_queue( ser_pdev_obj, cmd_type, is_active_queue); node_type = WLAN_SER_PDEV_NODE; } else { queue = wlan_serialization_get_list_from_vdev_queue( ser_vdev_obj, cmd_type, is_active_queue); node_type = WLAN_SER_VDEV_NODE; } status = wlan_serialization_is_cmd_present_in_given_queue(queue, cmd, node_type); error: return status; } enum wlan_serialization_status wlan_serialization_enqueue_cmd(struct wlan_serialization_command *cmd, enum ser_queue_reason ser_reason) { enum wlan_serialization_status status = WLAN_SER_CMD_DENIED_UNSPECIFIED; struct wlan_serialization_command_list *cmd_list; qdf_list_node_t *nnode; struct wlan_objmgr_pdev *pdev; struct wlan_ser_pdev_obj *ser_pdev_obj; struct wlan_serialization_pdev_queue *pdev_queue; struct wlan_ser_vdev_obj *ser_vdev_obj; struct wlan_serialization_vdev_queue *vdev_queue; bool active_queue; uint8_t vdev_id; /* Enqueue process * 1) peek through command structure and see what is the command type * 2) two main types of commands to process * a) SCAN * b) NON-SCAN * 3) for each command there are separate command queues per pdev * 4) pull pdev from vdev structure and get the command queue associated * with that pdev and try to enqueue on those queue * 5) Thumb rule: * a) There could be only 1 active non-scan command at a * time including all total non-scan commands of all pdevs. * * example: pdev1 has 1 non-scan active command and * pdev2 got 1 non-scan command then that command should go to * pdev2's pending queue * * b) There could be only N number of scan commands at a time * including all total scan commands of all pdevs * * example: Let's say N=8, * pdev1's vdev1 has 5 scan command, pdev2's vdev1 has 3 * scan commands, if we get scan request on vdev2 then it will go * to pending queue of vdev2 as we reached max allowed scan active * command. */ if (!cmd) { ser_err("NULL command"); goto error; } if (!cmd->cmd_cb) { ser_err("no cmd_cb for cmd type:%d, id: %d", cmd->cmd_type, cmd->cmd_id); goto error; } pdev = wlan_serialization_get_pdev_from_cmd(cmd); if (!pdev) { ser_err("pdev is invalid"); goto error; } vdev_id = wlan_vdev_get_id(cmd->vdev); ser_pdev_obj = wlan_objmgr_pdev_get_comp_private_obj( pdev, WLAN_UMAC_COMP_SERIALIZATION); if (!ser_pdev_obj) { ser_err("Invalid ser_pdev_obj"); goto error; } pdev_queue = wlan_serialization_get_pdev_queue_obj(ser_pdev_obj, cmd->cmd_type); if (!pdev_queue) { ser_err("pdev_queue is invalid"); goto error; } wlan_serialization_acquire_lock(&pdev_queue->pdev_queue_lock); /* Before queuing any non scan command, * as part of wlan_serialization_request, * we check if the vdev queues are disabled. * * The serialization command structure has an * attribute, where after a given command is queued, * we can block the vdev queues. * * For example, after VDEV_DOWN command is queued as * part of a vdev deletion, no other commands should be queued * until the deletion is complete, so with VDEV_DOWN(in case of * vdev deletion) with pass the attribute to disable vdev queues */ if (cmd->cmd_type > WLAN_SER_CMD_SCAN && ser_reason == SER_REQUEST) { ser_vdev_obj = wlan_serialization_get_vdev_obj( wlan_serialization_get_vdev_from_cmd(cmd)); if (!ser_vdev_obj) { wlan_serialization_release_lock( &pdev_queue->pdev_queue_lock); goto error; } vdev_queue = wlan_serialization_get_vdev_queue_obj( ser_vdev_obj, cmd->cmd_type); if (!vdev_queue) { wlan_serialization_release_lock( &pdev_queue->pdev_queue_lock); goto error; } if (vdev_queue->queue_disable) { wlan_serialization_release_lock( &pdev_queue->pdev_queue_lock); ser_err_rl("VDEV %d queue is disabled, reject cmd id %d type %d", vdev_id, cmd->cmd_id, cmd->cmd_type); status = WLAN_SER_CMD_QUEUE_DISABLED; goto error; } } active_queue = wlan_serialization_is_active_cmd_allowed(cmd); if (wlan_serialization_is_cmd_present_queue(cmd, active_queue)) { wlan_serialization_release_lock(&pdev_queue->pdev_queue_lock); ser_err("duplicate command, reject cmd id %d type %d vdev %d", cmd->cmd_id, cmd->cmd_type, vdev_id); goto error; } if (wlan_serialization_remove_front( &pdev_queue->cmd_pool_list, &nnode) != QDF_STATUS_SUCCESS) { wlan_serialization_release_lock(&pdev_queue->pdev_queue_lock); ser_err("Failed to get cmd buffer from global pool cmd id %d type %d vdev %d", cmd->cmd_id, cmd->cmd_type, vdev_id); status = WLAN_SER_CMD_DENIED_LIST_FULL; goto error; } ser_debug("Type %d id %d vdev %d high_priority %d blocking %d timeout %d allowed %d", cmd->cmd_type, cmd->cmd_id, vdev_id, cmd->is_high_priority, cmd->is_blocking, cmd->cmd_timeout_duration, active_queue); cmd_list = qdf_container_of(nnode, struct wlan_serialization_command_list, pdev_node); qdf_mem_copy(&cmd_list->cmd, cmd, sizeof(struct wlan_serialization_command)); if (cmd->cmd_type < WLAN_SER_CMD_NONSCAN) { status = wlan_ser_add_scan_cmd(ser_pdev_obj, cmd_list, active_queue); } else { status = wlan_ser_add_non_scan_cmd(ser_pdev_obj, cmd_list, active_queue); } if (status != WLAN_SER_CMD_PENDING && status != WLAN_SER_CMD_ACTIVE) { qdf_mem_zero(&cmd_list->cmd, sizeof(struct wlan_serialization_command)); cmd_list->cmd_in_use = 0; wlan_serialization_insert_back( &pdev_queue->cmd_pool_list, &cmd_list->pdev_node); wlan_serialization_release_lock(&pdev_queue->pdev_queue_lock); ser_err("Failed to add cmd id %d type %d to active/pending queue", cmd->cmd_id, cmd->cmd_type); goto error; } if (WLAN_SER_CMD_ACTIVE == status) { qdf_atomic_set_bit(CMD_MARKED_FOR_ACTIVATION, &cmd_list->cmd_in_use); } wlan_ser_update_cmd_history(pdev_queue, &cmd_list->cmd, ser_reason, true, active_queue); wlan_serialization_release_lock(&pdev_queue->pdev_queue_lock); if (WLAN_SER_CMD_ACTIVE == status) wlan_serialization_activate_cmd(cmd_list, ser_pdev_obj, ser_reason); error: return status; } QDF_STATUS wlan_serialization_activate_cmd( struct wlan_serialization_command_list *cmd_list, struct wlan_ser_pdev_obj *ser_pdev_obj, enum ser_queue_reason ser_reason) { QDF_STATUS status = QDF_STATUS_E_FAILURE; struct wlan_objmgr_psoc *psoc = NULL; struct wlan_serialization_pdev_queue *pdev_queue; uint8_t vdev_id; pdev_queue = wlan_serialization_get_pdev_queue_obj( ser_pdev_obj, cmd_list->cmd.cmd_type); psoc = wlan_vdev_get_psoc(cmd_list->cmd.vdev); if (!psoc) { ser_err("invalid psoc"); goto error; } vdev_id = wlan_vdev_get_id(cmd_list->cmd.vdev); /* * command is already pushed to active queue above * now start the timer and notify requestor */ status = wlan_serialization_find_and_start_timer(psoc, &cmd_list->cmd, ser_reason); if (QDF_IS_STATUS_ERROR(status)) { ser_err("Failed to start timer cmd type %d id %d vdev %d", cmd_list->cmd.cmd_type, cmd_list->cmd.cmd_id, vdev_id); goto timer_failed; } /* * Remember that serialization module may send * this callback in same context through which it * received the serialization request. Due to which * it is caller's responsibility to ensure acquiring * and releasing its own lock appropriately. */ ser_debug("Activate type %d id %d vdev %d", cmd_list->cmd.cmd_type, cmd_list->cmd.cmd_id, vdev_id); cmd_list->cmd.activation_reason = ser_reason; status = cmd_list->cmd.cmd_cb(&cmd_list->cmd, WLAN_SER_CB_ACTIVATE_CMD); timer_failed: wlan_serialization_acquire_lock(&pdev_queue->pdev_queue_lock); qdf_atomic_clear_bit(CMD_MARKED_FOR_ACTIVATION, &cmd_list->cmd_in_use); qdf_atomic_set_bit(CMD_IS_ACTIVE, &cmd_list->cmd_in_use); wlan_serialization_release_lock(&pdev_queue->pdev_queue_lock); if (QDF_IS_STATUS_ERROR(status)) { wlan_serialization_dequeue_cmd(&cmd_list->cmd, SER_ACTIVATION_FAILED, true); return status; } /* * Cmd was marked for activation and delete or cancel * is received before activation completed, then the command * should be immediately removed after activation */ if (qdf_atomic_test_bit(CMD_ACTIVE_MARKED_FOR_REMOVAL, &cmd_list->cmd_in_use)) { wlan_serialization_dequeue_cmd(&cmd_list->cmd, SER_REMOVE, true); return status; } if (qdf_atomic_test_bit(CMD_ACTIVE_MARKED_FOR_CANCEL, &cmd_list->cmd_in_use)) wlan_serialization_cmd_cancel_handler( ser_pdev_obj, &cmd_list->cmd, NULL, NULL, cmd_list->cmd.cmd_type, WLAN_SERIALIZATION_ACTIVE_QUEUE, WLAN_SER_CMD_ATTR_NONE); error: return status; } bool wlan_serialization_is_active_cmd_allowed(struct wlan_serialization_command *cmd) { struct wlan_objmgr_pdev *pdev; bool active_cmd_allowed = 0; pdev = wlan_serialization_get_pdev_from_cmd(cmd); if (!pdev) { ser_err("NULL pdev"); goto error; } if (cmd->cmd_type < WLAN_SER_CMD_NONSCAN) active_cmd_allowed = (wlan_serialization_is_active_scan_cmd_allowed(cmd) && wlan_serialization_is_scan_pending_queue_empty(cmd)); else active_cmd_allowed = (wlan_serialization_is_active_non_scan_cmd_allowed(cmd) && wlan_serialization_is_non_scan_pending_queue_empty(cmd)); error: return active_cmd_allowed; } enum wlan_serialization_status wlan_serialization_move_pending_to_active( enum wlan_serialization_cmd_type cmd_type, struct wlan_ser_pdev_obj *ser_pdev_obj, struct wlan_objmgr_vdev *vdev, bool blocking_cmd_removed) { enum wlan_serialization_status status; if (cmd_type < WLAN_SER_CMD_NONSCAN) { status = wlan_ser_move_scan_pending_to_active( ser_pdev_obj); } else { status = wlan_ser_move_non_scan_pending_to_active( ser_pdev_obj, vdev, blocking_cmd_removed); } return status; } enum wlan_serialization_cmd_status wlan_serialization_dequeue_cmd(struct wlan_serialization_command *cmd, enum ser_queue_reason ser_reason, uint8_t active_cmd) { enum wlan_serialization_cmd_status status = WLAN_SER_CMD_NOT_FOUND; enum wlan_serialization_status ser_status = WLAN_SER_CMD_DENIED_UNSPECIFIED; QDF_STATUS qdf_status; struct wlan_objmgr_pdev *pdev; struct wlan_objmgr_psoc *psoc; struct wlan_ser_pdev_obj *ser_pdev_obj; struct wlan_serialization_command cmd_bkup; struct wlan_serialization_command_list *cmd_list; struct wlan_serialization_pdev_queue *pdev_queue; bool blocking_cmd_removed = 0; if (!cmd) { ser_err("NULL command"); goto error; } pdev = wlan_serialization_get_pdev_from_cmd(cmd); if (!pdev) { ser_err("invalid pdev"); goto error; } psoc = wlan_pdev_get_psoc(pdev); if (!psoc) { ser_err("invalid psoc"); goto error; } ser_pdev_obj = wlan_serialization_get_pdev_obj(pdev); if (!ser_pdev_obj) { ser_err("ser_pdev_obj is empty"); goto error; } pdev_queue = wlan_serialization_get_pdev_queue_obj( ser_pdev_obj, cmd->cmd_type); ser_debug("Type %d id %d vdev %d blocking %d reason %d active %d", cmd->cmd_type, cmd->cmd_id, wlan_vdev_get_id(cmd->vdev), cmd->is_blocking, ser_reason, active_cmd); wlan_serialization_acquire_lock(&pdev_queue->pdev_queue_lock); if (cmd->cmd_type < WLAN_SER_CMD_NONSCAN) qdf_status = wlan_ser_remove_scan_cmd( ser_pdev_obj, &cmd_list, cmd, active_cmd); else { qdf_status = wlan_ser_remove_non_scan_cmd( ser_pdev_obj, &cmd_list, cmd, active_cmd); } if (qdf_status == QDF_STATUS_E_PENDING) { status = WLAN_SER_CMD_MARKED_FOR_ACTIVATION; wlan_serialization_release_lock(&pdev_queue->pdev_queue_lock); goto error; } if (qdf_status != QDF_STATUS_SUCCESS) { wlan_serialization_release_lock(&pdev_queue->pdev_queue_lock); status = WLAN_SER_CMD_NOT_FOUND; goto error; } if (active_cmd) { if (cmd_list->cmd.cmd_type >= WLAN_SER_CMD_NONSCAN) blocking_cmd_removed = cmd_list->cmd.is_blocking; } if (active_cmd) wlan_serialization_find_and_stop_timer( psoc, &cmd_list->cmd, ser_reason); qdf_mem_copy(&cmd_bkup, &cmd_list->cmd, sizeof(struct wlan_serialization_command)); qdf_mem_zero(&cmd_list->cmd, sizeof(struct wlan_serialization_command)); cmd_list->cmd_in_use = 0; qdf_status = wlan_serialization_insert_back( &pdev_queue->cmd_pool_list, &cmd_list->pdev_node); wlan_ser_update_cmd_history(pdev_queue, &cmd_bkup, ser_reason, false, active_cmd); wlan_serialization_release_lock(&pdev_queue->pdev_queue_lock); if (active_cmd) { ser_status = wlan_serialization_move_pending_to_active( cmd_bkup.cmd_type, ser_pdev_obj, cmd_bkup.vdev, blocking_cmd_removed); } /* Call cmd cb for remove request*/ if (cmd_bkup.cmd_cb) { /* caller should release the memory */ ser_debug("Release memory for type %d id %d vdev %d", cmd_bkup.cmd_type, cmd_bkup.cmd_id, wlan_vdev_get_id(cmd_bkup.vdev)); cmd_bkup.cmd_cb(&cmd_bkup, WLAN_SER_CB_RELEASE_MEM_CMD); } if (active_cmd) status = WLAN_SER_CMD_IN_ACTIVE_LIST; else status = WLAN_SER_CMD_IN_PENDING_LIST; error: return status; } static void wlan_serialization_generic_timer_cb(void *arg) { struct wlan_serialization_command *timeout_cmd = arg; struct wlan_objmgr_vdev *vdev = NULL; struct wlan_objmgr_psoc *psoc; if (!timeout_cmd) { ser_err("cmd_info not found"); return; } vdev = timeout_cmd->vdev; if (!vdev) { ser_err("Invalid vdev"); qdf_mem_free(timeout_cmd); return; } psoc = wlan_vdev_get_psoc(vdev); if (!psoc) { ser_err("Psoc is NULL"); goto free; } ser_err("Active cmd timeout for cmd_type[%d] vdev[%d] cmd_id[%d]", timeout_cmd->cmd_type, wlan_vdev_get_id(vdev), timeout_cmd->cmd_id); /* * Validate if active cmnd is still present, as actual command * completion can removed it in parallel. */ if (!wlan_serialization_is_cmd_present_in_active_queue(psoc, timeout_cmd)) { ser_err("cmd_type %d vdev %d not in active queue", timeout_cmd->cmd_type, wlan_vdev_get_id(vdev)); goto free; } if (timeout_cmd->cmd_cb) timeout_cmd->cmd_cb(timeout_cmd, WLAN_SER_CB_ACTIVE_CMD_TIMEOUT); /* * dequeue cmd API will cleanup and destroy the timer. If it fails to * dequeue command then we have to destroy the timer. */ wlan_serialization_dequeue_cmd(timeout_cmd, SER_TIMEOUT, true); free: wlan_objmgr_vdev_release_ref(timeout_cmd->vdev, WLAN_SERIALIZATION_ID); qdf_mem_free(timeout_cmd); } static QDF_STATUS wlan_serialization_mc_flush(struct scheduler_msg *msg) { struct wlan_serialization_command *timeout_cmd = scheduler_qdf_mc_timer_deinit_return_data_ptr(msg->bodyptr); if (!timeout_cmd) { ser_err("Error failed to release reference for vdev objmgr"); return QDF_STATUS_E_FAILURE; } wlan_objmgr_vdev_release_ref(timeout_cmd->vdev, WLAN_SERIALIZATION_ID); qdf_mem_free(timeout_cmd); return QDF_STATUS_SUCCESS; } static void wlan_serialization_timer_cb_mc_ctx(struct wlan_serialization_command *cmd) { struct scheduler_msg msg = {0}; struct wlan_serialization_command *timeout_cmd; struct sched_qdf_mc_timer_cb_wrapper *mc_timer_wrapper; QDF_STATUS status; msg.type = SYS_MSG_ID_MC_TIMER; msg.reserved = SYS_MSG_COOKIE; status = wlan_objmgr_vdev_try_get_ref(cmd->vdev, WLAN_SERIALIZATION_ID); if (QDF_IS_STATUS_ERROR(status)) { ser_err("unable to get reference for vdev %d", wlan_vdev_get_id(cmd->vdev)); return; } timeout_cmd = qdf_mem_malloc(sizeof(*timeout_cmd)); if (!timeout_cmd) { wlan_objmgr_vdev_release_ref(cmd->vdev, WLAN_SERIALIZATION_ID); return; } qdf_mem_copy(timeout_cmd, cmd, sizeof(*timeout_cmd)); mc_timer_wrapper = scheduler_qdf_mc_timer_init(wlan_serialization_generic_timer_cb, timeout_cmd); if (!mc_timer_wrapper) { ser_err("failed to allocate sched_qdf_mc_timer_cb_wrapper"); goto failed_mc_allocation; } msg.callback = scheduler_qdf_mc_timer_callback_t_wrapper; msg.bodyptr = mc_timer_wrapper; msg.bodyval = 0; msg.flush_callback = wlan_serialization_mc_flush; if (scheduler_post_message(QDF_MODULE_ID_SERIALIZATION, QDF_MODULE_ID_SERIALIZATION, QDF_MODULE_ID_SYS, &msg) == QDF_STATUS_SUCCESS) return; ser_err("Could not enqueue timer to timer queue"); qdf_mem_free(mc_timer_wrapper); failed_mc_allocation: /* free mem and release ref on error */ wlan_objmgr_vdev_release_ref(timeout_cmd->vdev, WLAN_SERIALIZATION_ID); qdf_mem_free(timeout_cmd); } static void wlan_serialization_timer_handler(void *arg) { struct wlan_serialization_timer *timer = arg; struct wlan_serialization_command *cmd = timer->cmd; if (!cmd) { ser_err("Command not found"); return; } ser_err("Active cmd timeout for cmd_type %d vdev %d cmd id %d", cmd->cmd_type, wlan_vdev_get_id(cmd->vdev), cmd->cmd_id); wlan_serialization_timer_cb_mc_ctx(cmd); } QDF_STATUS wlan_serialization_find_and_update_timer( struct wlan_objmgr_psoc *psoc, struct wlan_serialization_command *cmd) { struct wlan_ser_psoc_obj *psoc_ser_obj; struct wlan_serialization_timer *ser_timer; QDF_STATUS status = QDF_STATUS_E_FAILURE; int i = 0; if (!psoc || !cmd) { ser_err("invalid param"); goto exit; } psoc_ser_obj = wlan_serialization_get_psoc_obj(psoc); /* * Here cmd_id and cmd_type are used to locate the timer being * associated with command. */ wlan_serialization_acquire_lock(&psoc_ser_obj->timer_lock); for (i = 0; psoc_ser_obj->max_active_cmds > i; i++) { ser_timer = &psoc_ser_obj->timers[i]; if (!(ser_timer->cmd) || (ser_timer->cmd->cmd_id != cmd->cmd_id) || (ser_timer->cmd->cmd_type != cmd->cmd_type) || (ser_timer->cmd->vdev != cmd->vdev)) continue; qdf_timer_mod(&ser_timer->timer, cmd->cmd_timeout_duration); status = QDF_STATUS_SUCCESS; break; } wlan_serialization_release_lock(&psoc_ser_obj->timer_lock); if (QDF_IS_STATUS_ERROR(status)) ser_debug("Can't find timer for cmd_type %d", cmd->cmd_type); exit: return status; } QDF_STATUS wlan_serialization_find_and_stop_timer(struct wlan_objmgr_psoc *psoc, struct wlan_serialization_command *cmd, enum ser_queue_reason ser_reason) { struct wlan_ser_psoc_obj *psoc_ser_obj; struct wlan_serialization_timer *ser_timer; QDF_STATUS status = QDF_STATUS_E_FAILURE; int i = 0; uint32_t phy_version; struct wlan_objmgr_vdev *vdev; if (!psoc || !cmd) { ser_err("invalid param"); goto exit; } if (cmd->cmd_timeout_duration == 0) { phy_version = wlan_psoc_get_nif_phy_version(psoc); if (wlan_is_emulation_platform(phy_version)) { ser_err("[SCAN-EMULATION]: Not performing timer funcs"); status = QDF_STATUS_SUCCESS; goto exit; } } psoc_ser_obj = wlan_serialization_get_psoc_obj(psoc); /* * Here cmd_id and cmd_type are used to locate the timer being * associated with command. */ wlan_serialization_acquire_lock(&psoc_ser_obj->timer_lock); for (i = 0; psoc_ser_obj->max_active_cmds > i; i++) { ser_timer = &psoc_ser_obj->timers[i]; if (!(ser_timer->cmd) || (ser_timer->cmd->cmd_id != cmd->cmd_id) || (ser_timer->cmd->cmd_type != cmd->cmd_type) || (ser_timer->cmd->vdev != cmd->vdev)) continue; vdev = ser_timer->cmd->vdev; status = wlan_serialization_stop_timer(ser_timer); /* * Release the vdev reference when the active cmd is removed * through remove/cancel request. */ wlan_objmgr_vdev_release_ref(vdev, WLAN_SERIALIZATION_ID); break; } wlan_serialization_release_lock(&psoc_ser_obj->timer_lock); if (QDF_IS_STATUS_ERROR(status)) ser_err("Can't find timer for cmd_type %d cmd id %d", cmd->cmd_type, cmd->cmd_id); exit: return status; } QDF_STATUS wlan_serialization_find_and_start_timer(struct wlan_objmgr_psoc *psoc, struct wlan_serialization_command *cmd, enum ser_queue_reason ser_reason) { QDF_STATUS status = QDF_STATUS_E_FAILURE; struct wlan_ser_psoc_obj *psoc_ser_obj; struct wlan_serialization_timer *ser_timer; int i = 0; uint32_t nif_phy_ver; if (!psoc || !cmd) { ser_err("invalid param"); goto error; } nif_phy_ver = wlan_psoc_get_nif_phy_version(psoc); if ((cmd->cmd_timeout_duration == 0) && (wlan_is_emulation_platform(nif_phy_ver))) { ser_err("[SCAN-EMULATION]: Not performing timer functions\n"); status = QDF_STATUS_SUCCESS; goto error; } psoc_ser_obj = wlan_serialization_get_psoc_obj(psoc); wlan_serialization_acquire_lock(&psoc_ser_obj->timer_lock); for (i = 0; psoc_ser_obj->max_active_cmds > i; i++) { /* Keep trying timer */ ser_timer = &psoc_ser_obj->timers[i]; if (ser_timer->cmd) continue; /* Remember timer is pointing to command */ ser_timer->cmd = cmd; status = QDF_STATUS_SUCCESS; /* * Get vdev reference before starting the timer * Remove the reference before removing the command * in any one of the cases: * 1. Active command is removed through remove/cancel request * 2. Timer expiry handler is completed. */ status = wlan_objmgr_vdev_try_get_ref(ser_timer->cmd->vdev, WLAN_SERIALIZATION_ID); if (QDF_IS_STATUS_ERROR(status)) { /* * Set cmd to null so that ref release is not tried for * vdev when timer is flushed. */ ser_timer->cmd = NULL; wlan_serialization_release_lock( &psoc_ser_obj->timer_lock); ser_err("Unbale to get vdev reference"); status = QDF_STATUS_E_FAILURE; goto error; } break; } wlan_serialization_release_lock(&psoc_ser_obj->timer_lock); if (QDF_IS_STATUS_SUCCESS(status)) { qdf_timer_init(NULL, &ser_timer->timer, wlan_serialization_timer_handler, ser_timer, QDF_TIMER_TYPE_SW); qdf_timer_mod(&ser_timer->timer, cmd->cmd_timeout_duration); } else { ser_err("Failed to start timer for cmd: type[%d] id[%d] high_priority[%d] blocking[%d]", cmd->cmd_type, cmd->cmd_id, cmd->is_high_priority, cmd->is_blocking); } error: return status; } enum wlan_serialization_cmd_status wlan_serialization_cmd_cancel_handler( struct wlan_ser_pdev_obj *ser_obj, struct wlan_serialization_command *cmd, struct wlan_objmgr_pdev *pdev, struct wlan_objmgr_vdev *vdev, enum wlan_serialization_cmd_type cmd_type, uint8_t queue_type, enum wlan_ser_cmd_attr cmd_attr) { enum wlan_serialization_cmd_status active_status = WLAN_SER_CMD_NOT_FOUND; enum wlan_serialization_cmd_status pending_status = WLAN_SER_CMD_NOT_FOUND; enum wlan_serialization_cmd_status status = WLAN_SER_CMD_NOT_FOUND; if (!ser_obj) { ser_err("invalid serial object"); goto error; } if (queue_type & WLAN_SERIALIZATION_ACTIVE_QUEUE) { if (cmd_type < WLAN_SER_CMD_NONSCAN) active_status = wlan_ser_cancel_scan_cmd( ser_obj, pdev, vdev, cmd, cmd_type, true); else active_status = wlan_ser_cancel_non_scan_cmd( ser_obj, pdev, vdev, cmd, cmd_type, true, cmd_attr); } if (queue_type & WLAN_SERIALIZATION_PENDING_QUEUE) { if (cmd_type < WLAN_SER_CMD_NONSCAN) pending_status = wlan_ser_cancel_scan_cmd( ser_obj, pdev, vdev, cmd, cmd_type, false); else pending_status = wlan_ser_cancel_non_scan_cmd( ser_obj, pdev, vdev, cmd, cmd_type, false, cmd_attr); } if (active_status == WLAN_SER_CMD_IN_ACTIVE_LIST && pending_status == WLAN_SER_CMD_IN_PENDING_LIST) status = WLAN_SER_CMDS_IN_ALL_LISTS; else if (active_status == WLAN_SER_CMD_IN_ACTIVE_LIST) status = active_status; else if (pending_status == WLAN_SER_CMD_IN_PENDING_LIST) status = pending_status; error: return status; } enum wlan_serialization_cmd_status wlan_serialization_find_and_cancel_cmd( struct wlan_serialization_command *cmd, enum wlan_serialization_cancel_type req_type, uint8_t queue_type) { enum wlan_serialization_cmd_status status = WLAN_SER_CMD_NOT_FOUND; struct wlan_ser_pdev_obj *ser_obj = NULL; struct wlan_objmgr_pdev *pdev; if (!cmd) { ser_err("Invalid cmd"); goto error; } pdev = wlan_serialization_get_pdev_from_cmd(cmd); if (!pdev) { ser_err("Invalid pdev"); goto error; } ser_obj = wlan_serialization_get_pdev_obj(pdev); if (!ser_obj) { ser_err("Invalid ser_obj"); goto error; } switch (req_type) { case WLAN_SER_CANCEL_SINGLE_SCAN: /* remove scan cmd which matches the given cmd struct */ status = wlan_serialization_cmd_cancel_handler( ser_obj, cmd, NULL, NULL, WLAN_SER_CMD_SCAN, queue_type, WLAN_SER_CMD_ATTR_NONE); break; case WLAN_SER_CANCEL_PDEV_SCANS: /* remove all scan cmds which matches the pdev object */ status = wlan_serialization_cmd_cancel_handler( ser_obj, NULL, pdev, NULL, WLAN_SER_CMD_SCAN, queue_type, WLAN_SER_CMD_ATTR_NONE); break; case WLAN_SER_CANCEL_VDEV_SCANS: case WLAN_SER_CANCEL_VDEV_HOST_SCANS: /* remove all scan cmds which matches the vdev object */ status = wlan_serialization_cmd_cancel_handler( ser_obj, NULL, NULL, cmd->vdev, WLAN_SER_CMD_SCAN, queue_type, WLAN_SER_CMD_ATTR_NONE); break; case WLAN_SER_CANCEL_NON_SCAN_CMD: /* remove nonscan cmd which matches the given cmd */ status = wlan_serialization_cmd_cancel_handler( ser_obj, cmd, NULL, NULL, WLAN_SER_CMD_NONSCAN, queue_type, WLAN_SER_CMD_ATTR_NONE); break; case WLAN_SER_CANCEL_PDEV_NON_SCAN_CMD: /* remove all non scan cmds which matches the pdev object */ status = wlan_serialization_cmd_cancel_handler( ser_obj, NULL, pdev, NULL, WLAN_SER_CMD_NONSCAN, queue_type, WLAN_SER_CMD_ATTR_NONE); break; case WLAN_SER_CANCEL_VDEV_NON_SCAN_CMD: /* remove all non scan cmds which matches the vdev object */ status = wlan_serialization_cmd_cancel_handler( ser_obj, NULL, NULL, cmd->vdev, WLAN_SER_CMD_NONSCAN, queue_type, WLAN_SER_CMD_ATTR_NONE); break; case WLAN_SER_CANCEL_VDEV_NON_SCAN_CMD_TYPE: /* * remove all non scan cmds which matches the vdev * and given cmd type */ status = wlan_serialization_cmd_cancel_handler( ser_obj, NULL, NULL, cmd->vdev, cmd->cmd_type, queue_type, WLAN_SER_CMD_ATTR_NONE); break; case WLAN_SER_CANCEL_VDEV_NON_SCAN_NB_CMD: /* * remove all non-blocking non-scan cmds which matches the given * vdev */ status = wlan_serialization_cmd_cancel_handler( ser_obj, NULL, NULL, cmd->vdev, WLAN_SER_CMD_NONSCAN, queue_type, WLAN_SER_CMD_ATTR_NONBLOCK); break; default: ser_err("Invalid request"); } error: return status; }