blob: 8319347d812e57ef6812fe69767010365b3a2d8d [file] [log] [blame]
/*
* Copyright (c) 2017-2019 The Linux Foundation. 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 <wlan_objmgr_vdev_obj.h>
#include <wlan_objmgr_pdev_obj.h>
#include <wlan_objmgr_psoc_obj.h>
#include <qdf_list.h>
#include <qdf_status.h>
#include <wlan_utility.h>
#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;
/* 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.
*/
ser_enter();
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;
}
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;
}
ser_debug("enqueue cmd: type[%d] id[%d] high_priority[%d] blocking[%d]",
cmd->cmd_type,
cmd->cmd_id,
cmd->is_high_priority,
cmd->is_blocking);
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("VDEV queue is disabled, ser request denied");
ser_err("cmd id[%d] cmd type[%d]", 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, can't enqueue");
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");
status = WLAN_SER_CMD_DENIED_LIST_FULL;
goto error;
}
ser_debug("Global pool node: %pK", nnode);
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 to active/pending queue");
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:
ser_exit();
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;
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;
}
/*
* 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,
wlan_vdev_get_id(cmd_list->cmd.vdev));
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("cmd cb: type[%d] id[%d] : reason: %s",
cmd_list->cmd.cmd_type,
cmd_list->cmd.cmd_id,
"WLAN_SER_CB_ACTIVATE_CMD");
cmd_list->cmd.activation_reason = ser_reason;
status = cmd_list->cmd.cmd_cb(&cmd_list->cmd,
WLAN_SER_CB_ACTIVATE_CMD);
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);
timer_failed:
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));
ser_debug("active cmd_type[%d] cmd_id[%d] allowed: %d",
cmd->cmd_type,
cmd->cmd_id,
active_cmd_allowed);
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;
ser_enter();
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("dequeue cmd: type[%d] id[%d] high_priority[%d] blocking[%d]",
cmd->cmd_type,
cmd->cmd_id,
cmd->is_high_priority,
cmd->is_blocking);
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);
/* Call cmd cb for remove request*/
if (cmd_bkup.cmd_cb) {
/* caller should release the memory */
ser_debug("cmd cb: type[%d] id[%d]: reason: %s",
cmd_bkup.cmd_type,
cmd_bkup.cmd_id,
"WLAN_SER_CB_RELEASE_MEM_CMD");
cmd_bkup.cmd_cb(&cmd_bkup,
WLAN_SER_CB_RELEASE_MEM_CMD);
}
if (active_cmd) {
ser_status = wlan_serialization_move_pending_to_active(
cmd_bkup.cmd_type, ser_pdev_obj,
cmd_bkup.vdev,
blocking_cmd_removed);
}
if (active_cmd)
status = WLAN_SER_CMD_IN_ACTIVE_LIST;
else
status = WLAN_SER_CMD_IN_PENDING_LIST;
error:
ser_exit();
return status;
}
void wlan_serialization_generic_timer_cb(void *arg)
{
struct wlan_serialization_timer *timer = arg;
struct wlan_serialization_command *cmd = timer->cmd;
struct wlan_objmgr_vdev *vdev = NULL;
enum wlan_serialization_cmd_status status;
if (!cmd) {
ser_err("Command not found");
return;
}
vdev = cmd->vdev;
if (!vdev) {
ser_err("Invalid vdev");
return;
}
ser_err("active cmd timeout for cmd_type[%d] vdev[%d]",
cmd->cmd_type, wlan_vdev_get_id(cmd->vdev));
if (cmd->cmd_cb)
cmd->cmd_cb(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.
*/
status = wlan_serialization_dequeue_cmd(cmd, SER_TIMEOUT, true);
/* Release the ref taken before the timer was started */
if (status == WLAN_SER_CMD_IN_ACTIVE_LIST)
wlan_objmgr_vdev_release_ref(vdev, WLAN_SERIALIZATION_ID);
}
static QDF_STATUS wlan_serialization_mc_flush_noop(struct scheduler_msg *msg)
{
return QDF_STATUS_SUCCESS;
}
static void
wlan_serialization_timer_cb_mc_ctx(void *arg)
{
struct scheduler_msg msg = {0};
msg.type = SYS_MSG_ID_MC_TIMER;
msg.reserved = SYS_MSG_COOKIE;
msg.callback = wlan_serialization_generic_timer_cb;
msg.bodyptr = arg;
msg.bodyval = 0;
msg.flush_callback = wlan_serialization_mc_flush_noop;
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");
}
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->cmd_type, wlan_vdev_get_id(cmd->vdev));
wlan_serialization_timer_cb_mc_ctx(arg);
}
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_SUCCESS(status))
ser_debug("Updated the timer for cmd type:%d, id: %d",
cmd->cmd_type, cmd->cmd_id);
else
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.
*
* In case the command removal is because of timer expiry,
* the vdev is released when the timer handler completes.
*/
if (vdev && ser_reason != SER_TIMEOUT)
wlan_objmgr_vdev_release_ref(
vdev, WLAN_SERIALIZATION_ID);
break;
}
wlan_serialization_release_lock(&psoc_ser_obj->timer_lock);
if (QDF_IS_STATUS_SUCCESS(status))
ser_debug("Stopped timer for cmd_type %d cmd id %d",
cmd->cmd_type, cmd->cmd_id);
else
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 exit;
}
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)) {
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);
ser_debug("Started 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);
} 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:
exit:
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;
ser_enter();
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:
ser_exit();
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;
ser_enter();
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:
/* 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:
ser_exit();
return status;
}