blob: 0c8cc38185ce5869319cba40b73d5b2a24753cca [file] [log] [blame]
/*
* Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* 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.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/**
* DOC: wma_dev_if.c
* This file contains vdev & peer related operations.
*/
/* Header files */
#include "wma.h"
#include "wma_api.h"
#include "cds_api.h"
#include "wmi_unified_api.h"
#include "wlan_qct_sys.h"
#include "wni_api.h"
#include "ani_global.h"
#include "wmi_unified.h"
#include "wni_cfg.h"
#include "cfg_api.h"
#include "qdf_nbuf.h"
#include "qdf_types.h"
#include "qdf_mem.h"
#include "ol_txrx_peer_find.h"
#include "wma_types.h"
#include "lim_api.h"
#include "lim_session_utils.h"
#include "cds_utils.h"
#include "cds_concurrency.h"
#if !defined(REMOVE_PKT_LOG)
#include "pktlog_ac.h"
#endif /* REMOVE_PKT_LOG */
#include "dbglog_host.h"
#include "csr_api.h"
#include "dfs.h"
#include "wma_internal.h"
#include "wma_ocb.h"
#include "cdp_txrx_cfg.h"
#include "cdp_txrx_flow_ctrl_legacy.h"
#include <cdp_txrx_peer_ops.h>
#include <cdp_txrx_cfg.h>
#include <cdp_txrx_cmn.h>
#include "ol_txrx.h"
#include "cds_concurrency.h"
#include "wma_nan_datapath.h"
/**
* wma_find_vdev_by_addr() - find vdev_id from mac address
* @wma: wma handle
* @addr: mac address
* @vdev_id: return vdev_id
*
* Return: Returns vdev handle or NULL if mac address don't match
*/
void *wma_find_vdev_by_addr(tp_wma_handle wma, uint8_t *addr,
uint8_t *vdev_id)
{
uint8_t i;
for (i = 0; i < wma->max_bssid; i++) {
if (qdf_is_macaddr_equal(
(struct qdf_mac_addr *) wma->interfaces[i].addr,
(struct qdf_mac_addr *) addr) == true) {
*vdev_id = i;
return wma->interfaces[i].handle;
}
}
return NULL;
}
/**
* wma_is_vdev_in_ap_mode() - check that vdev is in ap mode or not
* @wma: wma handle
* @vdev_id: vdev id
*
* Helper function to know whether given vdev id
* is in AP mode or not.
*
* Return: True/False
*/
bool wma_is_vdev_in_ap_mode(tp_wma_handle wma, uint8_t vdev_id)
{
struct wma_txrx_node *intf = wma->interfaces;
if (vdev_id >= wma->max_bssid) {
WMA_LOGE("%s: Invalid vdev_id %hu", __func__, vdev_id);
QDF_ASSERT(0);
return false;
}
if ((intf[vdev_id].type == WMI_VDEV_TYPE_AP) &&
((intf[vdev_id].sub_type == WMI_UNIFIED_VDEV_SUBTYPE_P2P_GO) ||
(intf[vdev_id].sub_type == 0)))
return true;
return false;
}
#ifdef QCA_IBSS_SUPPORT
/**
* wma_is_vdev_in_ibss_mode() - check that vdev is in ibss mode or not
* @wma: wma handle
* @vdev_id: vdev id
*
* Helper function to know whether given vdev id
* is in IBSS mode or not.
*
* Return: True/False
*/
bool wma_is_vdev_in_ibss_mode(tp_wma_handle wma, uint8_t vdev_id)
{
struct wma_txrx_node *intf = wma->interfaces;
if (vdev_id >= wma->max_bssid) {
WMA_LOGE("%s: Invalid vdev_id %hu", __func__, vdev_id);
QDF_ASSERT(0);
return false;
}
if (intf[vdev_id].type == WMI_VDEV_TYPE_IBSS)
return true;
return false;
}
#endif /* QCA_IBSS_SUPPORT */
/**
* wma_find_vdev_by_bssid() - Get the corresponding vdev_id from BSSID
* @wma - wma handle
* @vdev_id - vdev ID
*
* Return: fill vdev_id with appropriate vdev id and return vdev
* handle or NULL if not found.
*/
void *wma_find_vdev_by_bssid(tp_wma_handle wma, uint8_t *bssid,
uint8_t *vdev_id)
{
int i;
for (i = 0; i < wma->max_bssid; i++) {
if (qdf_is_macaddr_equal(
(struct qdf_mac_addr *) wma->interfaces[i].bssid,
(struct qdf_mac_addr *) bssid) == true) {
*vdev_id = i;
return wma->interfaces[i].handle;
}
}
return NULL;
}
/**
* wma_get_txrx_vdev_type() - return operating mode of vdev
* @type: vdev_type
*
* Return: return operating mode as enum wlan_op_mode type
*/
static enum wlan_op_mode wma_get_txrx_vdev_type(uint32_t type)
{
enum wlan_op_mode vdev_type = wlan_op_mode_unknown;
switch (type) {
case WMI_VDEV_TYPE_AP:
vdev_type = wlan_op_mode_ap;
break;
case WMI_VDEV_TYPE_STA:
vdev_type = wlan_op_mode_sta;
break;
#ifdef QCA_IBSS_SUPPORT
case WMI_VDEV_TYPE_IBSS:
vdev_type = wlan_op_mode_ibss;
break;
#endif /* QCA_IBSS_SUPPORT */
case WMI_VDEV_TYPE_OCB:
vdev_type = wlan_op_mode_ocb;
break;
case WMI_VDEV_TYPE_MONITOR:
vdev_type = wlan_op_mode_monitor;
break;
case WMI_VDEV_TYPE_NDI:
vdev_type = wlan_op_mode_ndi;
break;
default:
WMA_LOGE("Invalid vdev type %u", type);
vdev_type = wlan_op_mode_unknown;
}
return vdev_type;
}
/**
* wma_find_req() - find target request for vdev id
* @wma: wma handle
* @vdev_id: vdev id
* @type: request type
*
* Find target request for given vdev id & type of request.
* Remove that request from active list.
*
* Return: return target request if found or NULL.
*/
static struct wma_target_req *wma_find_req(tp_wma_handle wma,
uint8_t vdev_id, uint8_t type)
{
struct wma_target_req *req_msg = NULL;
bool found = false;
qdf_list_node_t *node1 = NULL, *node2 = NULL;
QDF_STATUS status;
qdf_spin_lock_bh(&wma->wma_hold_req_q_lock);
if (QDF_STATUS_SUCCESS != qdf_list_peek_front(&wma->wma_hold_req_queue,
&node2)) {
qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock);
WMA_LOGE(FL("unable to get msg node from request queue"));
return NULL;
}
do {
node1 = node2;
req_msg = qdf_container_of(node1, struct wma_target_req, node);
if (req_msg->vdev_id != vdev_id)
continue;
if (req_msg->type != type)
continue;
found = true;
status = qdf_list_remove_node(&wma->wma_hold_req_queue, node1);
if (QDF_STATUS_SUCCESS != status) {
qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock);
WMA_LOGD(FL("Failed to remove request for vdev_id %d type %d"),
vdev_id, type);
return NULL;
}
break;
} while (QDF_STATUS_SUCCESS ==
qdf_list_peek_next(&wma->wma_hold_req_queue, node1,
&node2));
qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock);
if (!found) {
WMA_LOGE(FL("target request not found for vdev_id %d type %d"),
vdev_id, type);
return NULL;
}
WMA_LOGD(FL("target request found for vdev id: %d type %d"),
vdev_id, type);
return req_msg;
}
/**
* wma_find_remove_req_msgtype() - find and remove request for vdev id
* @wma: wma handle
* @vdev_id: vdev id
* @msg_type: message request type
*
* Find target request for given vdev id & sub type of request.
* Remove the same from active list.
*
* Return: Success if request found, failure other wise
*/
static struct wma_target_req *wma_find_remove_req_msgtype(tp_wma_handle wma,
uint8_t vdev_id, uint32_t msg_type)
{
struct wma_target_req *req_msg = NULL;
bool found = false;
qdf_list_node_t *node1 = NULL, *node2 = NULL;
QDF_STATUS status;
qdf_spin_lock_bh(&wma->wma_hold_req_q_lock);
if (QDF_STATUS_SUCCESS != qdf_list_peek_front(&wma->wma_hold_req_queue,
&node2)) {
qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock);
WMA_LOGE(FL("unable to get msg node from request queue"));
return NULL;
}
do {
node1 = node2;
req_msg = qdf_container_of(node1, struct wma_target_req, node);
if (req_msg->vdev_id != vdev_id)
continue;
if (req_msg->msg_type != msg_type)
continue;
found = true;
status = qdf_list_remove_node(&wma->wma_hold_req_queue, node1);
if (QDF_STATUS_SUCCESS != status) {
qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock);
WMA_LOGD(FL("Failed to remove request. vdev_id %d type %d"),
vdev_id, msg_type);
return NULL;
}
break;
} while (QDF_STATUS_SUCCESS ==
qdf_list_peek_next(&wma->wma_hold_req_queue, node1,
&node2));
qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock);
if (!found) {
WMA_LOGE(FL("target request not found for vdev_id %d type %d"),
vdev_id, msg_type);
return NULL;
}
WMA_LOGD(FL("target request found for vdev id: %d type %d"),
vdev_id, msg_type);
return req_msg;
}
/**
* wma_find_vdev_req() - find target request for vdev id
* @wma: wma handle
* @vdev_id: vdev id
* @type: request type
* @remove_req_from_list: flag to indicate remove req or not.
*
* Return: return target request if found or NULL.
*/
static struct wma_target_req *wma_find_vdev_req(tp_wma_handle wma,
uint8_t vdev_id, uint8_t type,
bool remove_req_from_list)
{
struct wma_target_req *req_msg = NULL;
bool found = false;
qdf_list_node_t *node1 = NULL, *node2 = NULL;
QDF_STATUS status;
qdf_spin_lock_bh(&wma->vdev_respq_lock);
if (QDF_STATUS_SUCCESS != qdf_list_peek_front(&wma->vdev_resp_queue,
&node2)) {
qdf_spin_unlock_bh(&wma->vdev_respq_lock);
WMA_LOGD(FL("unable to get target req from vdev resp queue vdev_id: %d type: %d"),
vdev_id, type);
return NULL;
}
do {
node1 = node2;
req_msg = qdf_container_of(node1, struct wma_target_req, node);
if (req_msg->vdev_id != vdev_id)
continue;
if (req_msg->type != type)
continue;
found = true;
if (remove_req_from_list) {
status = qdf_list_remove_node(&wma->vdev_resp_queue,
node1);
if (QDF_STATUS_SUCCESS != status) {
qdf_spin_unlock_bh(&wma->vdev_respq_lock);
WMA_LOGD(FL(
"Failed to target req for vdev_id %d type %d"),
vdev_id, type);
return NULL;
}
}
break;
} while (QDF_STATUS_SUCCESS ==
qdf_list_peek_next(&wma->vdev_resp_queue,
node1, &node2));
qdf_spin_unlock_bh(&wma->vdev_respq_lock);
if (!found) {
WMA_LOGD(FL("target request not found for vdev_id %d type %d"),
vdev_id, type);
return NULL;
}
WMA_LOGD(FL("target request found for vdev id: %d type %d msg %d"),
vdev_id, type, req_msg->msg_type);
return req_msg;
}
/**
* wma_send_del_sta_self_resp() - send del sta self resp to Upper layer
* @param: params of del sta resp
*
* Return: none
*/
static inline void wma_send_del_sta_self_resp(struct del_sta_self_params *param)
{
cds_msg_t sme_msg = {0};
QDF_STATUS status;
sme_msg.type = eWNI_SME_DEL_STA_SELF_RSP;
sme_msg.bodyptr = param;
status = cds_mq_post_message(QDF_MODULE_ID_SME, &sme_msg);
if (!QDF_IS_STATUS_SUCCESS(status)) {
WMA_LOGE("Failed to post eWNI_SME_DEL_STA_SELF_RSP");
qdf_mem_free(param);
}
}
/**
* wma_vdev_detach_callback() - send vdev detach response to upper layer
* @ctx: txrx node ptr
*
* Return: none
*/
static void wma_vdev_detach_callback(void *ctx)
{
tp_wma_handle wma;
struct wma_txrx_node *iface = (struct wma_txrx_node *)ctx;
struct del_sta_self_params *param;
struct wma_target_req *req_msg;
wma = cds_get_context(QDF_MODULE_ID_WMA);
if (!wma || !iface->del_staself_req) {
WMA_LOGE("%s: wma %pK iface %pK", __func__, wma,
iface->del_staself_req);
return;
}
param = (struct del_sta_self_params *) iface->del_staself_req;
iface->del_staself_req = NULL;
WMA_LOGD("%s: sending eWNI_SME_DEL_STA_SELF_RSP for vdev %d",
__func__, param->session_id);
if (!WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap,
WMI_SERVICE_SYNC_DELETE_CMDS)) {
req_msg = wma_find_vdev_req(wma, param->session_id,
WMA_TARGET_REQ_TYPE_VDEV_DEL,
true);
if (req_msg) {
WMA_LOGD("%s: Found vdev request for vdev id %d",
__func__, param->session_id);
qdf_mc_timer_stop(&req_msg->event_timeout);
qdf_mc_timer_destroy(&req_msg->event_timeout);
qdf_mem_free(req_msg);
}
}
if (iface->addBssStaContext)
qdf_mem_free(iface->addBssStaContext);
if (iface->staKeyParams)
qdf_mem_free(iface->staKeyParams);
if (iface->stats_rsp)
qdf_mem_free(iface->stats_rsp);
wma_vdev_deinit(iface);
qdf_mem_zero(iface, sizeof(*iface));
wma_vdev_init(iface);
param->status = QDF_STATUS_SUCCESS;
wma_send_del_sta_self_resp(param);
}
/**
* wma_self_peer_remove() - Self peer remove handler
* @wma: wma handle
* @del_sta_self_req_param: vdev id
* @generate_vdev_rsp: request type
*
* Return: success if peer delete command sent to firmware, else failure.
*/
static QDF_STATUS wma_self_peer_remove(tp_wma_handle wma_handle,
struct del_sta_self_params *del_sta_self_req_param,
uint8_t generate_vdev_rsp)
{
ol_txrx_peer_handle peer;
ol_txrx_pdev_handle pdev;
QDF_STATUS qdf_status;
uint8_t peer_id;
uint8_t vdev_id = del_sta_self_req_param->session_id;
struct wma_target_req *msg = NULL;
struct del_sta_self_rsp_params *sta_self_wmi_rsp;
WMA_LOGD("P2P Device: removing self peer %pM",
del_sta_self_req_param->self_mac_addr);
pdev = cds_get_context(QDF_MODULE_ID_TXRX);
if (NULL == pdev) {
WMA_LOGE("%s: Failed to get pdev", __func__);
qdf_status = QDF_STATUS_E_FAULT;
goto error;
}
peer = ol_txrx_find_peer_by_addr(pdev,
del_sta_self_req_param->self_mac_addr,
&peer_id);
if (!peer) {
WMA_LOGE("%s Failed to find peer %pM", __func__,
del_sta_self_req_param->self_mac_addr);
qdf_status = QDF_STATUS_E_FAULT;
goto error;
}
wma_remove_peer(wma_handle,
del_sta_self_req_param->self_mac_addr,
vdev_id, peer, false);
if (WMI_SERVICE_IS_ENABLED(wma_handle->wmi_service_bitmap,
WMI_SERVICE_SYNC_DELETE_CMDS)) {
sta_self_wmi_rsp =
qdf_mem_malloc(sizeof(struct del_sta_self_rsp_params));
if (sta_self_wmi_rsp == NULL) {
WMA_LOGE(FL("Failed to allocate memory"));
qdf_status = QDF_STATUS_E_NOMEM;
goto error;
}
sta_self_wmi_rsp->self_sta_param = del_sta_self_req_param;
sta_self_wmi_rsp->generate_rsp = generate_vdev_rsp;
msg = wma_fill_hold_req(wma_handle, vdev_id,
WMA_DELETE_STA_REQ,
WMA_DEL_P2P_SELF_STA_RSP_START,
sta_self_wmi_rsp,
WMA_DELETE_STA_TIMEOUT);
if (!msg) {
WMA_LOGE(FL("Failed to allocate request for vdev_id %d"),
vdev_id);
wma_remove_req(wma_handle, vdev_id,
WMA_DEL_P2P_SELF_STA_RSP_START);
qdf_status = QDF_STATUS_E_FAILURE;
goto error;
}
}
return QDF_STATUS_SUCCESS;
error:
return qdf_status;
}
static void
wma_ol_txrx_vdev_detach(tp_wma_handle wma_handle,
uint8_t vdev_id)
{
struct wma_txrx_node *iface = &wma_handle->interfaces[vdev_id];
ol_txrx_vdev_detach(iface->handle, NULL, NULL);
iface->handle = NULL;
iface->is_vdev_valid = false;
}
static QDF_STATUS wma_handle_vdev_detach(tp_wma_handle wma_handle,
struct del_sta_self_params *del_sta_self_req_param,
uint8_t generate_rsp)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
uint8_t vdev_id = del_sta_self_req_param->session_id;
struct wma_txrx_node *iface = &wma_handle->interfaces[vdev_id];
struct wma_target_req *msg = NULL;
status = wmi_unified_vdev_delete_send(wma_handle->wmi_handle, vdev_id);
if (QDF_IS_STATUS_ERROR(status)) {
WMA_LOGE("Unable to remove an interface");
goto out;
}
WMA_LOGD("vdev_id:%hu vdev_hdl:%pK", vdev_id, iface->handle);
if (!generate_rsp) {
goto out;
}
iface->del_staself_req = del_sta_self_req_param;
msg = wma_fill_vdev_req(wma_handle, vdev_id, WMA_DEL_STA_SELF_REQ,
WMA_TARGET_REQ_TYPE_VDEV_DEL, iface, 6000);
if (!msg) {
WMA_LOGE("%s: Failed to fill vdev request for vdev_id %d",
__func__, vdev_id);
status = QDF_STATUS_E_NOMEM;
goto out;
}
/* Acquire wake lock only when you expect a response from firmware */
if (WMI_SERVICE_IS_ENABLED(wma_handle->wmi_service_bitmap,
WMI_SERVICE_SYNC_DELETE_CMDS)) {
wma_acquire_wakelock(&wma_handle->wmi_cmd_rsp_wake_lock,
WMA_FW_RSP_EVENT_WAKE_LOCK_DURATION);
}
WMA_LOGD("Call txrx detach with callback for vdev %d", vdev_id);
wma_ol_txrx_vdev_detach(wma_handle, vdev_id);
/*
* send the response immediately if WMI_SERVICE_SYNC_DELETE_CMDS
* service is not supported by firmware
*/
if (!WMI_SERVICE_IS_ENABLED(wma_handle->wmi_service_bitmap,
WMI_SERVICE_SYNC_DELETE_CMDS))
wma_vdev_detach_callback(iface);
return status;
out:
WMA_LOGE("Call txrx detach callback for vdev %d, generate_rsp %u",
vdev_id, generate_rsp);
wma_ol_txrx_vdev_detach(wma_handle, vdev_id);
if (iface->addBssStaContext)
qdf_mem_free(iface->addBssStaContext);
if (iface->staKeyParams)
qdf_mem_free(iface->staKeyParams);
wma_vdev_deinit(iface);
qdf_mem_zero(iface, sizeof(*iface));
wma_vdev_init(iface);
del_sta_self_req_param->status = status;
if (generate_rsp)
wma_send_del_sta_self_resp(del_sta_self_req_param);
return status;
}
/**
* wma_vdev_detach() - send vdev delete command to fw
* @wma_handle: wma handle
* @pdel_sta_self_req_param: del sta params
* @generateRsp: generate Response flag
*
* Return: QDF status
*/
QDF_STATUS wma_vdev_detach(tp_wma_handle wma_handle,
struct del_sta_self_params *pdel_sta_self_req_param,
uint8_t generateRsp)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
uint8_t vdev_id = pdel_sta_self_req_param->session_id;
struct wma_txrx_node *iface = &wma_handle->interfaces[vdev_id];
struct wma_target_req *req_msg;
if (qdf_atomic_read(&iface->bss_status) == WMA_BSS_STATUS_STARTED) {
req_msg = wma_find_vdev_req(wma_handle, vdev_id,
WMA_TARGET_REQ_TYPE_VDEV_STOP, false);
if (!req_msg)
goto send_fail_rsp_and_trigger_recovery;
if (req_msg->msg_type != WMA_DELETE_BSS_REQ)
goto send_fail_rsp_and_trigger_recovery;
WMA_LOGA("BSS is not yet stopped. Defering vdev(vdev id %x) deletion",
vdev_id);
iface->del_staself_req = pdel_sta_self_req_param;
iface->is_del_sta_defered = true;
return status;
}
iface->is_del_sta_defered = false;
if (!iface->handle) {
WMA_LOGE("handle of vdev_id %d is NULL vdev is already freed",
vdev_id);
pdel_sta_self_req_param->status = status;
if (generateRsp) {
wma_send_del_sta_self_resp(pdel_sta_self_req_param);
} else {
qdf_mem_free(pdel_sta_self_req_param);
pdel_sta_self_req_param = NULL;
}
return status;
}
if (iface->type == WMI_VDEV_TYPE_STA)
wma_pno_stop(wma_handle, vdev_id);
/* P2P Device */
if ((iface->type == WMI_VDEV_TYPE_AP) &&
(iface->sub_type == WMI_UNIFIED_VDEV_SUBTYPE_P2P_DEVICE)) {
status = wma_self_peer_remove(wma_handle,
pdel_sta_self_req_param, generateRsp);
if ((status != QDF_STATUS_SUCCESS) && generateRsp) {
WMA_LOGE("can't remove selfpeer, send rsp session: %d",
vdev_id);
if (!cds_is_driver_unloading()) {
WMA_LOGE("Trigger recovery for session: %d",
vdev_id);
goto send_fail_rsp_and_trigger_recovery;
} else {
WMA_LOGE("driver unload, free mem vdev_id: %d",
vdev_id);
goto send_fail_rsp;
}
} else if (status != QDF_STATUS_SUCCESS) {
WMA_LOGE("can't remove selfpeer, free msg session: %d",
vdev_id);
qdf_mem_free(pdel_sta_self_req_param);
pdel_sta_self_req_param = NULL;
return status;
}
if (!WMI_SERVICE_IS_ENABLED(wma_handle->wmi_service_bitmap,
WMI_SERVICE_SYNC_DELETE_CMDS))
status = wma_handle_vdev_detach(wma_handle,
pdel_sta_self_req_param, generateRsp);
} else { /* other than P2P */
status = wma_handle_vdev_detach(wma_handle,
pdel_sta_self_req_param, generateRsp);
}
if (QDF_IS_STATUS_SUCCESS(status))
iface->vdev_active = false;
return status;
send_fail_rsp_and_trigger_recovery:
if (!cds_is_driver_recovering()) {
if (cds_is_self_recovery_enabled()) {
WMA_LOGE("rcvd del_self_sta without del_bss, trigger recovery, vdev_id %d",
vdev_id);
cds_trigger_recovery(CDS_REASON_UNSPECIFIED);
} else {
WMA_LOGE("rcvd del_self_sta without del_bss, BUG_ON(), vdev_id %d",
vdev_id);
QDF_BUG(0);
}
}
send_fail_rsp:
if (generateRsp) {
pdel_sta_self_req_param->status = QDF_STATUS_E_FAILURE;
wma_send_del_sta_self_resp(pdel_sta_self_req_param);
} else {
qdf_mem_free(pdel_sta_self_req_param);
pdel_sta_self_req_param = NULL;
}
return status;
}
/**
* wma_vdev_start_rsp() - send vdev start response to upper layer
* @wma: wma handle
* @add_bss: add bss params
* @resp_event: response params
*
* Return: none
*/
static void wma_vdev_start_rsp(tp_wma_handle wma,
tpAddBssParams add_bss,
wmi_vdev_start_response_event_fixed_param *
resp_event)
{
struct beacon_info *bcn;
ol_txrx_pdev_handle pdev;
ol_txrx_peer_handle peer = NULL;
uint8_t peer_id;
#ifdef QCA_IBSS_SUPPORT
WMA_LOGD("%s: vdev start response received for %s mode", __func__,
add_bss->operMode ==
BSS_OPERATIONAL_MODE_IBSS ? "IBSS" : "non-IBSS");
#endif /* QCA_IBSS_SUPPORT */
if (resp_event->status) {
add_bss->status = QDF_STATUS_E_FAILURE;
goto send_fail_resp;
}
if ((add_bss->operMode == BSS_OPERATIONAL_MODE_AP)
#ifdef QCA_IBSS_SUPPORT
|| (add_bss->operMode == BSS_OPERATIONAL_MODE_IBSS)
#endif /* QCA_IBSS_SUPPORT */
) {
wma->interfaces[resp_event->vdev_id].beacon =
qdf_mem_malloc(sizeof(struct beacon_info));
bcn = wma->interfaces[resp_event->vdev_id].beacon;
if (!bcn) {
WMA_LOGE("%s: Failed alloc memory for beacon struct",
__func__);
add_bss->status = QDF_STATUS_E_NOMEM;
goto send_fail_resp;
}
bcn->buf = qdf_nbuf_alloc(NULL, WMA_BCN_BUF_MAX_SIZE, 0,
sizeof(uint32_t), 0);
if (!bcn->buf) {
WMA_LOGE("%s: No memory allocated for beacon buffer",
__func__);
qdf_mem_free(bcn);
add_bss->status = QDF_STATUS_E_FAILURE;
goto send_fail_resp;
}
bcn->seq_no = MIN_SW_SEQ;
qdf_spinlock_create(&bcn->lock);
qdf_atomic_set(&wma->interfaces[resp_event->vdev_id].bss_status,
WMA_BSS_STATUS_STARTED);
WMA_LOGD("%s: AP mode (type %d subtype %d) BSS is started",
__func__, wma->interfaces[resp_event->vdev_id].type,
wma->interfaces[resp_event->vdev_id].sub_type);
WMA_LOGD("%s: Allocated beacon struct %pK, template memory %pK",
__func__, bcn, bcn->buf);
}
add_bss->status = QDF_STATUS_SUCCESS;
add_bss->bssIdx = resp_event->vdev_id;
add_bss->chainMask = resp_event->chain_mask;
if ((2 != resp_event->cfgd_rx_streams) ||
(2 != resp_event->cfgd_tx_streams)) {
add_bss->nss = 1;
}
add_bss->smpsMode = host_map_smps_mode(resp_event->smps_mode);
send_fail_resp:
if (add_bss->status != QDF_STATUS_SUCCESS) {
WMA_LOGE("%s: ADD BSS failure %d", __func__, add_bss->status);
/* Send vdev stop if vdev start was success*/
if (!resp_event->status) {
if (wma_send_vdev_stop_to_fw(wma, resp_event->vdev_id))
WMA_LOGE("%s: %d Failed to send vdev stop", __func__, __LINE__);
}
pdev = cds_get_context(QDF_MODULE_ID_TXRX);
if (NULL == pdev)
WMA_LOGE("%s: Failed to get pdev", __func__);
if (pdev)
peer = ol_txrx_find_peer_by_addr(pdev,
add_bss->bssId, &peer_id);
if (!peer)
WMA_LOGE("%s Failed to find peer %pM", __func__,
add_bss->bssId);
if (peer)
wma_remove_peer(wma, add_bss->bssId,
resp_event->vdev_id, peer, false);
}
WMA_LOGD("%s: Sending add bss rsp to umac(vdev %d status %d)",
__func__, resp_event->vdev_id, add_bss->status);
wma_send_msg_high_priority(wma, WMA_ADD_BSS_RSP, (void *)add_bss, 0);
}
#ifdef FEATURE_AP_MCC_CH_AVOIDANCE
/**
* wma_find_mcc_ap() - finds if device is operating AP in MCC mode or not
* @wma: wma handle.
* @vdev_id: vdev ID of device for which MCC has to be checked
* @add: flag indicating if current device is added or deleted
*
* This function parses through all the interfaces in wma and finds if
* any of those devces are in MCC mode with AP. If such a vdev is found
* involved AP vdevs are sent WDA_UPDATE_Q2Q_IE_IND msg to update their
* beacon template to include Q2Q IE.
*
* Return: none
*/
static void wma_find_mcc_ap(tp_wma_handle wma, uint8_t vdev_id, bool add)
{
uint8_t i;
uint16_t prev_ch_freq = 0;
bool is_ap = false;
bool result = false;
uint8_t *ap_vdev_ids = NULL;
uint8_t num_ch = 0;
ap_vdev_ids = qdf_mem_malloc(wma->max_bssid);
if (!ap_vdev_ids)
return;
for (i = 0; i < wma->max_bssid; i++) {
ap_vdev_ids[i] = -1;
if (add == false && i == vdev_id)
continue;
if (wma->interfaces[i].vdev_up || (i == vdev_id && add)) {
if (wma->interfaces[i].type == WMI_VDEV_TYPE_AP) {
is_ap = true;
ap_vdev_ids[i] = i;
}
if (wma->interfaces[i].mhz != prev_ch_freq) {
num_ch++;
prev_ch_freq = wma->interfaces[i].mhz;
}
}
}
if (is_ap && (num_ch > 1))
result = true;
else
result = false;
wma_send_msg(wma, WMA_UPDATE_Q2Q_IE_IND, (void *)ap_vdev_ids, result);
}
#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */
static const wmi_channel_width mode_to_width[MODE_MAX] = {
[MODE_11A] = WMI_CHAN_WIDTH_20,
[MODE_11G] = WMI_CHAN_WIDTH_20,
[MODE_11B] = WMI_CHAN_WIDTH_20,
[MODE_11GONLY] = WMI_CHAN_WIDTH_20,
[MODE_11NA_HT20] = WMI_CHAN_WIDTH_20,
[MODE_11NG_HT20] = WMI_CHAN_WIDTH_20,
[MODE_11AC_VHT20] = WMI_CHAN_WIDTH_20,
[MODE_11AC_VHT20_2G] = WMI_CHAN_WIDTH_20,
[MODE_11NA_HT40] = WMI_CHAN_WIDTH_40,
[MODE_11NG_HT40] = WMI_CHAN_WIDTH_40,
[MODE_11AC_VHT40] = WMI_CHAN_WIDTH_40,
[MODE_11AC_VHT40_2G] = WMI_CHAN_WIDTH_40,
[MODE_11AC_VHT80] = WMI_CHAN_WIDTH_80,
[MODE_11AC_VHT80_2G] = WMI_CHAN_WIDTH_80,
#if CONFIG_160MHZ_SUPPORT
[MODE_11AC_VHT80_80] = WMI_CHAN_WIDTH_80P80,
[MODE_11AC_VHT160] = WMI_CHAN_WIDTH_160,
#endif
#if SUPPORT_11AX
[MODE_11AX_HE20] = WMI_CHAN_WIDTH_20,
[MODE_11AX_HE40] = WMI_CHAN_WIDTH_40,
[MODE_11AX_HE80] = WMI_CHAN_WIDTH_80,
[MODE_11AX_HE80_80] = WMI_CHAN_WIDTH_80P80,
[MODE_11AX_HE160] = WMI_CHAN_WIDTH_160,
[MODE_11AX_HE20_2G] = WMI_CHAN_WIDTH_20,
[MODE_11AX_HE40_2G] = WMI_CHAN_WIDTH_40,
[MODE_11AX_HE80_2G] = WMI_CHAN_WIDTH_80,
#endif
};
/**
* chanmode_to_chanwidth() - get channel width through channel mode
* @chanmode: channel phy mode
*
* Return: channel width
*/
wmi_channel_width chanmode_to_chanwidth(WLAN_PHY_MODE chanmode)
{
wmi_channel_width chan_width;
if (chanmode >= MODE_11A && chanmode < MODE_MAX)
chan_width = mode_to_width[chanmode];
else
chan_width = WMI_CHAN_WIDTH_20;
return chan_width;
}
/**
* wma_vdev_start_resp_handler() - vdev start response handler
* @handle: wma handle
* @cmd_param_info: event buffer
* @len: buffer length
*
* Return: 0 for success or error code
*/
int wma_vdev_start_resp_handler(void *handle, uint8_t *cmd_param_info,
uint32_t len)
{
tp_wma_handle wma = (tp_wma_handle) handle;
WMI_VDEV_START_RESP_EVENTID_param_tlvs *param_buf;
wmi_vdev_start_response_event_fixed_param *resp_event;
struct wma_target_req *req_msg;
struct wma_txrx_node *iface;
struct vdev_up_params param = {0};
QDF_STATUS status;
int err;
wmi_channel_width chanwidth;
#ifdef FEATURE_AP_MCC_CH_AVOIDANCE
tpAniSirGlobal mac_ctx = cds_get_context(QDF_MODULE_ID_PE);
if (NULL == mac_ctx) {
WMA_LOGE("%s: Failed to get mac_ctx", __func__);
cds_set_do_hw_mode_change_flag(false);
return -EINVAL;
}
#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */
WMA_LOGD("%s: Enter", __func__);
param_buf = (WMI_VDEV_START_RESP_EVENTID_param_tlvs *) cmd_param_info;
if (!param_buf) {
WMA_LOGE("Invalid start response event buffer");
cds_set_do_hw_mode_change_flag(false);
return -EINVAL;
}
resp_event = param_buf->fixed_param;
if (!resp_event) {
WMA_LOGE("Invalid start response event buffer");
cds_set_do_hw_mode_change_flag(false);
return -EINVAL;
}
if (resp_event->vdev_id >= wma->max_bssid) {
WMA_LOGE("Invalid vdev id received from firmware");
return -EINVAL;
}
if (wma_is_vdev_in_ap_mode(wma, resp_event->vdev_id)) {
qdf_spin_lock_bh(&wma->dfs_ic->chan_lock);
wma->dfs_ic->disable_phy_err_processing = false;
qdf_spin_unlock_bh(&wma->dfs_ic->chan_lock);
}
if (resp_event->status == QDF_STATUS_SUCCESS) {
wma->interfaces[resp_event->vdev_id].tx_streams =
resp_event->cfgd_tx_streams;
wma->interfaces[resp_event->vdev_id].rx_streams =
resp_event->cfgd_rx_streams;
wma->interfaces[resp_event->vdev_id].chain_mask =
resp_event->chain_mask;
if (wma->wlan_resource_config.use_pdev_id) {
if (resp_event->pdev_id == WMI_PDEV_ID_SOC) {
WMA_LOGE("%s: soc level id received for mac id",
__func__);
QDF_BUG(0);
return -EINVAL;
}
wma->interfaces[resp_event->vdev_id].mac_id =
WMA_PDEV_TO_MAC_MAP(resp_event->pdev_id);
} else {
wma->interfaces[resp_event->vdev_id].mac_id =
resp_event->mac_id;
}
WMA_LOGD("%s: vdev:%d tx ss=%d rx ss=%d chain mask=%d mac=%d",
__func__,
resp_event->vdev_id,
wma->interfaces[resp_event->vdev_id].tx_streams,
wma->interfaces[resp_event->vdev_id].rx_streams,
wma->interfaces[resp_event->vdev_id].chain_mask,
wma->interfaces[resp_event->vdev_id].mac_id);
}
iface = &wma->interfaces[resp_event->vdev_id];
req_msg = wma_find_vdev_req(wma, resp_event->vdev_id,
WMA_TARGET_REQ_TYPE_VDEV_START,
true);
if (!req_msg) {
WMA_LOGE("%s: Failed to lookup request message for vdev %d",
__func__, resp_event->vdev_id);
cds_set_do_hw_mode_change_flag(false);
return -EINVAL;
}
if ((resp_event->vdev_id < wma->max_bssid) &&
(qdf_atomic_read(
&wma->interfaces[resp_event->vdev_id].vdev_restart_params.hidden_ssid_restart_in_progress))
&& (wma_is_vdev_in_ap_mode(wma, resp_event->vdev_id) == true)
&& (req_msg->msg_type == WMA_HIDDEN_SSID_VDEV_RESTART)) {
tpHalHiddenSsidVdevRestart hidden_ssid_restart =
(tpHalHiddenSsidVdevRestart)req_msg->user_data;
WMA_LOGE("%s: vdev restart event recevied for hidden ssid set using IOCTL",
__func__);
param.vdev_id = resp_event->vdev_id;
param.assoc_id = 0;
qdf_atomic_set(&wma->interfaces[resp_event->vdev_id].
vdev_restart_params.
hidden_ssid_restart_in_progress, 0);
wma_send_msg(wma, WMA_HIDDEN_SSID_RESTART_RSP,
(void *)hidden_ssid_restart, 0);
/*
* Unpause TX queue in SAP case while configuring hidden ssid
* enable or disable, else the data path is paused forever
* causing data packets(starting from DHCP offer) to get stuck
*/
ol_txrx_vdev_unpause(iface->handle,
OL_TXQ_PAUSE_REASON_VDEV_STOP);
iface->pause_bitmap &= ~(1 << PAUSE_TYPE_HOST);
}
qdf_mc_timer_stop(&req_msg->event_timeout);
#ifdef FEATURE_AP_MCC_CH_AVOIDANCE
if (resp_event->status == QDF_STATUS_SUCCESS
&& mac_ctx->sap.sap_channel_avoidance)
wma_find_mcc_ap(wma, resp_event->vdev_id, true);
#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */
if (req_msg->msg_type == WMA_CHNL_SWITCH_REQ) {
tpSwitchChannelParams params =
(tpSwitchChannelParams) req_msg->user_data;
if (!params) {
WMA_LOGE("%s: channel switch params is NULL for vdev %d",
__func__, resp_event->vdev_id);
cds_set_do_hw_mode_change_flag(false);
return -EINVAL;
}
WMA_LOGD("%s: Send channel switch resp vdev %d status %d",
__func__, resp_event->vdev_id, resp_event->status);
if (QDF_IS_STATUS_ERROR(resp_event->status)) {
wma_cli_set_command(resp_event->vdev_id,
(int)WMI_VDEV_PARAM_ABG_MODE_TX_CHAIN_NUM, 0,
VDEV_CMD);
WMA_LOGD("vdev: %d WMI_VDEV_PARAM_ABG_MODE_TX_CHAIN_NUM 0",
resp_event->vdev_id);
}
params->chainMask = resp_event->chain_mask;
if ((2 != resp_event->cfgd_rx_streams) ||
(2 != resp_event->cfgd_tx_streams)) {
params->nss = 1;
}
params->smpsMode = host_map_smps_mode(resp_event->smps_mode);
params->status = resp_event->status;
if (wma->interfaces[resp_event->vdev_id].is_channel_switch) {
wma->interfaces[resp_event->vdev_id].is_channel_switch =
false;
}
if (((resp_event->resp_type == WMI_VDEV_RESTART_RESP_EVENT) &&
((iface->type == WMI_VDEV_TYPE_STA) ||
(iface->type == WMI_VDEV_TYPE_MONITOR))) ||
((resp_event->resp_type == WMI_VDEV_START_RESP_EVENT) &&
(iface->type == WMI_VDEV_TYPE_MONITOR))) {
/* for CSA case firmware expects phymode before ch_wd */
err = wma_set_peer_param(wma, iface->bssid,
WMI_PEER_PHYMODE, iface->chanmode,
resp_event->vdev_id);
WMA_LOGD("%s:vdev_id %d chanmode %d status %d",
__func__, resp_event->vdev_id,
iface->chanmode, err);
chanwidth = chanmode_to_chanwidth(iface->chanmode);
err = wma_set_peer_param(wma, iface->bssid,
WMI_PEER_CHWIDTH, chanwidth,
resp_event->vdev_id);
WMA_LOGD("%s:vdev_id %d chanwidth %d status %d",
__func__, resp_event->vdev_id,
chanwidth, err);
param.vdev_id = resp_event->vdev_id;
param.assoc_id = iface->aid;
status = wma_send_vdev_up_to_fw(wma, &param,
iface->bssid);
if (QDF_IS_STATUS_ERROR(status)) {
WMA_LOGE("%s:vdev_up failed vdev_id %d",
__func__, resp_event->vdev_id);
iface->vdev_up = false;
WMA_LOGD(FL("Setting vdev_up flag to false"));
cds_set_do_hw_mode_change_flag(false);
} else {
iface->vdev_up = true;
WMA_LOGD(FL("Setting vdev_up flag to true"));
if (iface->beacon_filter_enabled)
wma_add_beacon_filter(wma,
&iface->beacon_filter);
}
}
wma_send_msg_high_priority(wma,
WMA_SWITCH_CHANNEL_RSP, (void *)params, 0);
} else if (req_msg->msg_type == WMA_ADD_BSS_REQ) {
tpAddBssParams bssParams = (tpAddBssParams) req_msg->user_data;
qdf_mem_copy(iface->bssid, bssParams->bssId,
IEEE80211_ADDR_LEN);
wma_vdev_start_rsp(wma, bssParams, resp_event);
} else if (req_msg->msg_type == WMA_OCB_SET_CONFIG_CMD) {
param.vdev_id = resp_event->vdev_id;
param.assoc_id = iface->aid;
if (wma_send_vdev_up_to_fw(wma, &param, iface->bssid) !=
QDF_STATUS_SUCCESS) {
WMA_LOGE(FL("failed to send vdev up"));
cds_set_do_hw_mode_change_flag(false);
return -EEXIST;
}
iface->vdev_up = true;
WMA_LOGD(FL("Setting vdev_up flag to true"));
wma_ocb_start_resp_ind_cont(wma);
}
if ((wma->interfaces[resp_event->vdev_id].type == WMI_VDEV_TYPE_AP) &&
wma->interfaces[resp_event->vdev_id].vdev_up)
wma_set_sap_keepalive(wma, resp_event->vdev_id);
qdf_mc_timer_destroy(&req_msg->event_timeout);
qdf_mem_free(req_msg);
return 0;
}
bool wma_is_vdev_valid(uint32_t vdev_id)
{
tp_wma_handle wma_handle = cds_get_context(QDF_MODULE_ID_WMA);
if (!wma_handle) {
WMA_LOGD("%s: vdev_id: %d, null wma_handle", __func__, vdev_id);
return false;
}
/* No of interface are allocated based on max_bssid value */
if (vdev_id >= wma_handle->max_bssid) {
WMA_LOGD("%s: vdev_id: %d is invalid, max_bssid: %d",
__func__, vdev_id, wma_handle->max_bssid);
return false;
}
WMA_LOGD("%s: vdev_id: %d, vdev_active: %d, is_vdev_valid %d",
__func__, vdev_id, wma_handle->interfaces[vdev_id].vdev_active,
wma_handle->interfaces[vdev_id].is_vdev_valid);
return wma_handle->interfaces[vdev_id].vdev_active ||
wma_handle->interfaces[vdev_id].is_vdev_valid;
}
/**
* wma_vdev_set_param() - set per vdev params in fw
* @wmi_handle: wmi handle
* @if_if: vdev id
* @param_id: parameter id
* @param_value: parameter value
*
* Return: QDF_STATUS_SUCCESS for success or error code
*/
QDF_STATUS
wma_vdev_set_param(wmi_unified_t wmi_handle, uint32_t if_id,
uint32_t param_id, uint32_t param_value)
{
struct vdev_set_params param = {0};
if (!wma_is_vdev_valid(if_id)) {
WMA_LOGE(FL("vdev_id: %d is not active reject the req: param id %d val %d"),
if_id, param_id, param_value);
return QDF_STATUS_E_INVAL;
}
param.if_id = if_id;
param.param_id = param_id;
param.param_value = param_value;
return wmi_unified_vdev_set_param_send(wmi_handle, &param);
}
/**
* wma_set_peer_authorized_cb() - set peer authorized callback function
* @wma_Ctx: wma handle
* @auth_cb: peer authorized callback
*
* Return: none
*/
void wma_set_peer_authorized_cb(void *wma_ctx, wma_peer_authorized_fp auth_cb)
{
tp_wma_handle wma_handle = (tp_wma_handle) wma_ctx;
wma_handle->peer_authorized_cb = auth_cb;
}
/**
* wma_set_peer_param() - set peer parameter in fw
* @wma_ctx: wma handle
* @peer_addr: peer mac address
* @param_id: parameter id
* @param_value: parameter value
* @vdev_id: vdev id
*
* Return: QDF_STATUS_SUCCESS for success or error code
*/
QDF_STATUS wma_set_peer_param(void *wma_ctx, uint8_t *peer_addr,
uint32_t param_id, uint32_t param_value,
uint32_t vdev_id)
{
tp_wma_handle wma_handle = (tp_wma_handle) wma_ctx;
struct peer_set_params param = {0};
int err;
param.vdev_id = vdev_id;
param.param_value = param_value;
param.param_id = param_id;
err = wmi_set_peer_param_send(wma_handle->wmi_handle, peer_addr,
&param);
return err;
}
/**
* wma_remove_peer() - remove peer information from host driver and fw
* @wma: wma handle
* @bssid: mac address
* @vdev_id: vdev id
* @peer: peer ptr
* @roam_synch_in_progress: roam in progress flag
*
* Return: none
*/
void wma_remove_peer(tp_wma_handle wma, uint8_t *bssid,
uint8_t vdev_id, ol_txrx_peer_handle peer,
bool roam_synch_in_progress)
{
#define PEER_ALL_TID_BITMASK 0xffffffff
uint32_t peer_tid_bitmap = PEER_ALL_TID_BITMASK;
uint8_t *peer_addr = bssid;
struct peer_flush_params param = {0};
uint8_t *peer_mac_addr;
QDF_STATUS qdf_status;
bool start_peer_unmap_timer = true;
peer_mac_addr = ol_txrx_peer_get_peer_mac_addr(peer);
if (peer_mac_addr == NULL) {
WMA_LOGE("%s: peer mac addr is NULL, Can't remove peer, vdevid %d peer_count %d",
__func__, vdev_id,
wma->interfaces[vdev_id].peer_count);
return;
}
if (!wma->interfaces[vdev_id].peer_count) {
WMA_LOGE("%s: Can't remove peer with peer_addr %pM vdevid %d peer_count %d",
__func__, bssid, vdev_id,
wma->interfaces[vdev_id].peer_count);
return;
}
if (roam_synch_in_progress)
goto peer_detach;
/* Flush all TIDs except MGMT TID for this peer in Target */
peer_tid_bitmap &= ~(0x1 << WMI_MGMT_TID);
param.peer_tid_bitmap = peer_tid_bitmap;
param.vdev_id = vdev_id;
wmi_unified_peer_flush_tids_send(wma->wmi_handle, bssid,
&param);
if (wma_is_vdev_in_ibss_mode(wma, vdev_id)) {
WMA_LOGD("%s: bssid %pM peer->mac_addr %pM", __func__,
bssid, peer_mac_addr);
peer_addr = peer_mac_addr;
}
wma_peer_debug_log(vdev_id, DEBUG_PEER_DELETE_SEND,
DEBUG_INVALID_PEER_ID, peer_addr, peer,
0,
qdf_atomic_read(&peer->ref_cnt));
qdf_status = wmi_unified_peer_delete_send(wma->wmi_handle, peer_addr,
vdev_id);
if (qdf_status) {
WMA_LOGE("%s Peer delete could not be sent to firmware %d",
__func__, qdf_status);
start_peer_unmap_timer = false;
}
peer_detach:
WMA_LOGD("%s: Remove peer %pK with peer_addr %pM vdevid %d peer_count %d",
__func__, peer, bssid, vdev_id,
wma->interfaces[vdev_id].peer_count);
if (peer) {
if (roam_synch_in_progress)
ol_txrx_peer_detach_force_delete(peer);
else
ol_txrx_peer_detach(peer, start_peer_unmap_timer);
}
wma->interfaces[vdev_id].peer_count--;
#undef PEER_ALL_TID_BITMASK
}
/**
* wma_find_duplicate_peer_on_other_vdev() - Find if same peer exist
* on other vdevs
* @wma: wma handle
* @pdev: txrx pdev ptr
* @vdev_id: vdev id of vdev on which the peer
* needs to be added
* @peer_mac: peer mac addr which needs to be added
*
* Check if peer with same MAC is present on vdev other then
* the provided vdev_id
*
* Return: true if same peer is present on vdev other then vdev_id
* else return false
*/
static bool wma_find_duplicate_peer_on_other_vdev(tp_wma_handle wma,
ol_txrx_pdev_handle pdev, uint8_t vdev_id, uint8_t *peer_mac)
{
int i;
uint8_t peer_id;
for (i = 0; i < wma->max_bssid; i++) {
/* Need to check vdevs other than the vdev_id */
if (vdev_id == i ||
!wma->interfaces[i].handle)
continue;
if (ol_txrx_find_peer_by_addr_and_vdev(pdev,
wma->interfaces[i].handle, peer_mac, &peer_id)) {
WMA_LOGE("%s :Duplicate peer %pM (peer id %d) already exist on vdev %d",
__func__, peer_mac, peer_id, i);
return true;
}
}
return false;
}
/**
* wma_create_peer() - send peer create command to fw
* @wma: wma handle
* @pdev: txrx pdev ptr
* @vdev: txrx vdev ptr
* @peer_addr: peer mac addr
* @peer_type: peer type
* @vdev_id: vdev id
* @roam_synch_in_progress: roam in progress
*
* Return: QDF status
*/
QDF_STATUS wma_create_peer(tp_wma_handle wma, ol_txrx_pdev_handle pdev,
ol_txrx_vdev_handle vdev,
u8 peer_addr[IEEE80211_ADDR_LEN],
uint32_t peer_type, uint8_t vdev_id,
bool roam_synch_in_progress)
{
ol_txrx_peer_handle peer;
struct peer_create_params param = {0};
uint8_t *mac_addr_raw;
if (++wma->interfaces[vdev_id].peer_count >
wma->wlan_resource_config.num_peers) {
WMA_LOGE("%s, the peer count exceeds the limit %d", __func__,
wma->interfaces[vdev_id].peer_count - 1);
goto err;
}
/*
* Check if peer with same MAC exist on other Vdev, If so avoid
* adding this peer, as it will cause FW to crash.
*/
if (wma_find_duplicate_peer_on_other_vdev(wma, pdev,
vdev_id, peer_addr))
goto err;
peer = ol_txrx_peer_attach(vdev, peer_addr);
if (!peer) {
WMA_LOGE("%s : Unable to attach peer %pM", __func__, peer_addr);
goto err;
}
if (roam_synch_in_progress) {
WMA_LOGD("%s: LFR3: Created peer %pK with peer_addr %pM vdev_id %d, peer_count - %d",
__func__, peer, peer_addr, vdev_id,
wma->interfaces[vdev_id].peer_count);
return QDF_STATUS_SUCCESS;
}
param.peer_addr = peer_addr;
param.peer_type = peer_type;
param.vdev_id = vdev_id;
if (wmi_unified_peer_create_send(wma->wmi_handle,
&param) != QDF_STATUS_SUCCESS) {
WMA_LOGE("%s : Unable to create peer in Target", __func__);
ol_txrx_peer_detach(peer, false);
goto err;
}
WMA_LOGD("%s: Created peer %pK ref_cnt %d with peer_addr %pM vdev_id %d, peer_count - %d",
__func__, peer, qdf_atomic_read(&peer->ref_cnt),
peer_addr, vdev_id,
wma->interfaces[vdev_id].peer_count);
wma_peer_debug_log(vdev_id, DEBUG_PEER_CREATE_SEND,
DEBUG_INVALID_PEER_ID, peer_addr, peer, 0,
qdf_atomic_read(&peer->ref_cnt));
mac_addr_raw = ol_txrx_get_vdev_mac_addr(vdev);
if (mac_addr_raw == NULL) {
WMA_LOGE("%s: peer mac addr is NULL", __func__);
return QDF_STATUS_E_FAULT;
}
/* for each remote ibss peer, clear its keys */
if (wma_is_vdev_in_ibss_mode(wma, vdev_id) &&
qdf_mem_cmp(peer_addr, mac_addr_raw, IEEE80211_ADDR_LEN)) {
tSetStaKeyParams key_info;
WMA_LOGD("%s: remote ibss peer %pM key clearing\n", __func__,
peer_addr);
qdf_mem_set(&key_info, sizeof(key_info), 0);
key_info.smesessionId = vdev_id;
qdf_mem_copy(key_info.peer_macaddr.bytes, peer_addr,
IEEE80211_ADDR_LEN);
key_info.sendRsp = false;
wma_set_stakey(wma, &key_info);
}
return QDF_STATUS_SUCCESS;
err:
wma->interfaces[vdev_id].peer_count--;
return QDF_STATUS_E_FAILURE;
}
#ifdef QCA_IBSS_SUPPORT
/**
* wma_delete_all_ibss_peers() - delete all ibss peer for vdev_id
* @wma: wma handle
* @vdev_id: vdev id
*
* This function send peer delete command to fw for all
* peers in peer_list and remove ref count for peer id
* peer will actually remove from list after receving
* unmap event from firmware.
*
* Return: none
*/
static void wma_delete_all_ibss_peers(tp_wma_handle wma, A_UINT32 vdev_id)
{
ol_txrx_vdev_handle vdev;
if (!wma || vdev_id >= wma->max_bssid)
return;
vdev = wma->interfaces[vdev_id].handle;
if (!vdev)
return;
/* remove all remote peers of IBSS */
ol_txrx_remove_peers_for_vdev(vdev,
(ol_txrx_vdev_peer_remove_cb)wma_remove_peer, wma,
true);
}
#else
/**
* wma_delete_all_ibss_peers(): dummy function for when ibss is not supported
* @wma: wma handle
* @vdev_id: vdev id
*
* This function send peer delete command to fw for all
* peers in peer_list and remove ref count for peer id
* peer will actually remove from list after receving
* unmap event from firmware.
*
* Return: none
*/
static void wma_delete_all_ibss_peers(tp_wma_handle wma, A_UINT32 vdev_id)
{
}
#endif /* QCA_IBSS_SUPPORT */
/**
* wma_delete_all_ap_remote_peers() - delete all ap peer for vdev_id
* @wma: wma handle
* @vdev_id: vdev id
*
* This function send peer delete command to fw for all
* peers in peer_list and remove ref count for peer id
* peer will actually remove from list after receving
* unmap event from firmware.
*
* Return: none
*/
static void wma_delete_all_ap_remote_peers(tp_wma_handle wma, A_UINT32 vdev_id)
{
ol_txrx_vdev_handle vdev;
if (!wma || vdev_id >= wma->max_bssid)
return;
vdev = wma->interfaces[vdev_id].handle;
if (!vdev)
return;
WMA_LOGD("%s: vdev_id - %d", __func__, vdev_id);
/* remove all remote peers of SAP */
ol_txrx_remove_peers_for_vdev(vdev,
(ol_txrx_vdev_peer_remove_cb)wma_remove_peer, wma, false);
}
/**
* wma_hidden_ssid_vdev_restart_on_vdev_stop() - restart vdev to set hidden ssid
* @wma_handle: wma handle
* @sessionId: session id
*
* Return: none
*/
static void wma_hidden_ssid_vdev_restart_on_vdev_stop(tp_wma_handle wma_handle,
uint8_t sessionId)
{
struct wma_txrx_node *intr = wma_handle->interfaces;
struct hidden_ssid_vdev_restart_params params;
QDF_STATUS status;
params.session_id = sessionId;
params.ssid_len = intr[sessionId].vdev_restart_params.ssid.ssid_len;
qdf_mem_copy(params.ssid,
intr[sessionId].vdev_restart_params.ssid.ssid,
params.ssid_len);
params.flags = intr[sessionId].vdev_restart_params.flags;
if (intr[sessionId].vdev_restart_params.ssidHidden)
params.flags |= WMI_UNIFIED_VDEV_START_HIDDEN_SSID;
else
params.flags &= (0xFFFFFFFE);
params.requestor_id = intr[sessionId].vdev_restart_params.requestor_id;
params.disable_hw_ack =
intr[sessionId].vdev_restart_params.disable_hw_ack;
params.mhz = intr[sessionId].vdev_restart_params.chan.mhz;
params.band_center_freq1 =
intr[sessionId].vdev_restart_params.chan.band_center_freq1;
params.band_center_freq2 =
intr[sessionId].vdev_restart_params.chan.band_center_freq2;
params.info = intr[sessionId].vdev_restart_params.chan.info;
params.reg_info_1 = intr[sessionId].vdev_restart_params.chan.reg_info_1;
params.reg_info_2 = intr[sessionId].vdev_restart_params.chan.reg_info_2;
status = wmi_unified_hidden_ssid_vdev_restart_send(
wma_handle->wmi_handle, &params);
if (status == QDF_STATUS_E_FAILURE) {
WMA_LOGE("%s: Failed to send vdev restart command", __func__);
qdf_atomic_set(&intr[sessionId].vdev_restart_params.
hidden_ssid_restart_in_progress, 0);
}
}
/**
* wma_cleanup_target_req_param() - free param memory of target request
* @tgt_req: target request params
*
* Return: none
*/
static void wma_cleanup_target_req_param(struct wma_target_req *tgt_req)
{
if (tgt_req->msg_type == WMA_CHNL_SWITCH_REQ ||
tgt_req->msg_type == WMA_DELETE_BSS_REQ ||
tgt_req->msg_type == WMA_ADD_BSS_REQ) {
qdf_mem_free(tgt_req->user_data);
tgt_req->user_data = NULL;
}
if (tgt_req->msg_type == WMA_SET_LINK_STATE && tgt_req->user_data) {
tpLinkStateParams params =
(tpLinkStateParams) tgt_req->user_data;
qdf_mem_free(params->callbackArg);
qdf_mem_free(tgt_req->user_data);
tgt_req->user_data = NULL;
}
}
/**
* get_fw_active_apf_mode() - convert HDD APF mode to FW configurable APF
* mode
* @mode: APF mode maintained in HDD
*
* Return: FW configurable BP mode
*/
static FW_ACTIVE_BPF_MODE get_fw_active_apf_mode(enum active_apf_mode mode)
{
FW_ACTIVE_BPF_MODE fw_apf_mode;
switch (mode) {
case ACTIVE_APF_DISABLED:
fw_apf_mode = FW_ACTIVE_BPF_MODE_DISABLE;
break;
case ACTIVE_APF_ENABLED:
fw_apf_mode = FW_ACTIVE_BPF_MODE_FORCE_ENABLE;
break;
case ACTIVE_APF_ADAPTIVE:
fw_apf_mode = FW_ACTIVE_BPF_MODE_ADAPTIVE_ENABLE;
break;
default:
WMA_LOGE("Invalid Active APF Mode %d; Using 'disabled'", mode);
fw_apf_mode = FW_ACTIVE_BPF_MODE_DISABLE;
break;
}
return fw_apf_mode;
}
/**
* wma_config_active_apf_mode() - Config active APF mode in FW
* @wma: the WMA handle
* @vdev_id: the Id of the vdev for which the configuration should be applied
*
* Return: QDF status
*/
static QDF_STATUS wma_config_active_apf_mode(t_wma_handle *wma, uint8_t vdev_id)
{
FW_ACTIVE_BPF_MODE uc_mode, mcbc_mode;
uc_mode = get_fw_active_apf_mode(wma->active_uc_apf_mode);
mcbc_mode = get_fw_active_apf_mode(wma->active_mc_bc_apf_mode);
WMA_LOGD("Configuring Active APF Mode UC:%d MC/BC:%d for vdev %u",
uc_mode, mcbc_mode, vdev_id);
return wmi_unified_set_active_apf_mode_cmd(wma->wmi_handle, vdev_id,
uc_mode, mcbc_mode);
}
#ifdef FEATURE_AP_MCC_CH_AVOIDANCE
/**
* wma_check_and_find_mcc_ap() - finds if device is operating AP
* in MCC mode or not
* @wma: wma handle.
* @vdev_id: vdev ID of device for which MCC has to be checked
*
* This function internally calls wma_find_mcc_ap finds if
* device is operating AP in MCC mode or not
*
* Return: none
*/
static void
wma_check_and_find_mcc_ap(tp_wma_handle wma, uint8_t vdev_id)
{
tpAniSirGlobal mac_ctx = cds_get_context(QDF_MODULE_ID_PE);
if (NULL == mac_ctx) {
WMA_LOGE("%s: Failed to get mac_ctx", __func__);
return;
}
if (mac_ctx->sap.sap_channel_avoidance)
wma_find_mcc_ap(wma, vdev_id, false);
}
#else
static inline void
wma_check_and_find_mcc_ap(tp_wma_handle wma, uint8_t vdev_id)
{}
#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */
/**
* wma_send_del_bss_response() - send del bss resp to upper layer
* @wma: wma handle.
* @vdev_id: vdev ID of device for which MCC has to be checked
*
* This function sends del bss resp to upper layer
*
* Return: none
*/
static void
wma_send_del_bss_response(tp_wma_handle wma, struct wma_target_req *req,
uint8_t vdev_id)
{
struct wma_txrx_node *iface;
struct beacon_info *bcn;
tpDeleteBssParams params;
if (!req) {
WMA_LOGE("%s req is NULL", __func__);
return;
}
iface = &wma->interfaces[vdev_id];
if (!iface->handle) {
WMA_LOGE("%s vdev id %d is already deleted",
__func__, vdev_id);
if (req->user_data)
qdf_mem_free(req->user_data);
req->user_data = NULL;
return;
}
params = (tpDeleteBssParams)req->user_data;
if (wma_send_vdev_down_to_fw(wma, vdev_id) != QDF_STATUS_SUCCESS) {
WMA_LOGE("Failed to send vdev down cmd: vdev %d", vdev_id);
} else {
wma->interfaces[vdev_id].vdev_up = false;
WMA_LOGD(FL("Setting vdev_up flag to false"));
wma_check_and_find_mcc_ap(wma, vdev_id);
}
ol_txrx_vdev_flush(iface->handle);
WMA_LOGD("%s, vdev_id: %d, un-pausing tx_ll_queue for VDEV_STOP rsp",
__func__, vdev_id);
ol_txrx_vdev_unpause(iface->handle,
OL_TXQ_PAUSE_REASON_VDEV_STOP);
iface->pause_bitmap &= ~(1 << PAUSE_TYPE_HOST);
qdf_atomic_set(&iface->bss_status, WMA_BSS_STATUS_STOPPED);
WMA_LOGD("%s: (type %d subtype %d) BSS is stopped",
__func__, iface->type, iface->sub_type);
bcn = wma->interfaces[vdev_id].beacon;
if (bcn) {
struct ol_txrx_pdev_t *pdev;
pdev = cds_get_context(QDF_MODULE_ID_TXRX);
WMA_LOGD("%s: Freeing beacon struct %pK, template memory %pK",
__func__, bcn, bcn->buf);
if (bcn->dma_mapped && pdev)
qdf_nbuf_unmap_single(pdev->osdev, bcn->buf,
QDF_DMA_TO_DEVICE);
qdf_nbuf_free(bcn->buf);
qdf_mem_free(bcn);
wma->interfaces[vdev_id].beacon = NULL;
}
/*
* Timeout status means its WMA generated DEL BSS REQ when ADD
* BSS REQ was timed out to stop the VDEV in this case no need
* to send response to UMAC
*/
if (params->status == QDF_STATUS_FW_MSG_TIMEDOUT) {
qdf_mem_free(req->user_data);
req->user_data = NULL;
WMA_LOGE("%s: DEL BSS from ADD BSS timeout do not send resp to UMAC (vdev id %x)",
__func__, vdev_id);
} else {
params->status = QDF_STATUS_SUCCESS;
wma_send_msg_high_priority(wma,
WMA_DELETE_BSS_RSP, (void *)params, 0);
}
if (iface->del_staself_req && iface->is_del_sta_defered) {
iface->is_del_sta_defered = false;
WMA_LOGA("scheduling defered deletion (vdev id %x)",
vdev_id);
wma_vdev_detach(wma, iface->del_staself_req, 1);
}
}
static QDF_STATUS
wma_remove_peer_by_reference(ol_txrx_pdev_handle pdev,
tp_wma_handle wma,
void *params,
uint8_t *peer_id,
uint8_t *bssid,
uint8_t vdev_id,
uint8_t peer_rsp_type)
{
ol_txrx_peer_handle peer;
struct wma_target_req *del_req;
QDF_STATUS status;
status = QDF_STATUS_SUCCESS;
peer = ol_txrx_find_peer_by_addr_inc_ref(pdev,
bssid,
peer_id);
if (!peer) {
WMA_LOGD("%s Failed to find peer %pM",
__func__, bssid);
status = QDF_STATUS_E_FAULT;
return status;
}
WMA_LOGI(FL("Deleting peer %pM vdev id %d"),
bssid, vdev_id);
wma_remove_peer(wma, bssid, vdev_id,
peer, false);
if (WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap,
WMI_SERVICE_SYNC_DELETE_CMDS)) {
WMA_LOGD(FL("Wait for the peer delete. vdev_id %d"),
vdev_id);
del_req = wma_fill_hold_req(wma,
vdev_id,
WMA_DELETE_STA_REQ,
peer_rsp_type,
params,
WMA_DELETE_STA_TIMEOUT);
if (!del_req) {
WMA_LOGE(FL("Failed to allocate request. vdev_id %d"),
vdev_id);
status = QDF_STATUS_E_NOMEM;
}
}
OL_TXRX_PEER_UNREF_DELETE(peer);
return status;
}
/**
* wma_vdev_stop_resp_handler() - vdev stop response handler
* @handle: wma handle
* @cmd_param_info: event buffer
* @len: buffer length
*
* Return: 0 for success or error code
*/
int wma_vdev_stop_resp_handler(void *handle, uint8_t *cmd_param_info,
u32 len)
{
tp_wma_handle wma = (tp_wma_handle) handle;
WMI_VDEV_STOPPED_EVENTID_param_tlvs *param_buf;
wmi_vdev_stopped_event_fixed_param *resp_event;
struct wma_target_req *req_msg, *new_req_msg;
ol_txrx_pdev_handle pdev;
uint8_t peer_id;
struct wma_txrx_node *iface;
int32_t status = 0;
QDF_STATUS result;
WMA_LOGD("%s: Enter", __func__);
param_buf = (WMI_VDEV_STOPPED_EVENTID_param_tlvs *) cmd_param_info;
if (!param_buf) {
WMA_LOGE("Invalid event buffer");
return -EINVAL;
}
resp_event = param_buf->fixed_param;
if (resp_event->vdev_id >= wma->max_bssid) {
WMA_LOGE("%s: Invalid vdev_id %d from FW",
__func__, resp_event->vdev_id);
return -EINVAL;
}
iface = &wma->interfaces[resp_event->vdev_id];
/*
* Reset the rmfEnabled as there might be MGMT action frames
* sent on this vdev before the next session is established.
*/
if (iface->rmfEnabled) {
iface->rmfEnabled = 0;
WMA_LOGD(FL("Reset rmfEnabled for vdev %d"),
resp_event->vdev_id);
}
wma_release_wakelock(&iface->vdev_stop_wakelock);
req_msg = wma_find_vdev_req(wma, resp_event->vdev_id,
WMA_TARGET_REQ_TYPE_VDEV_STOP, true);
if (!req_msg) {
WMA_LOGE("%s: Failed to lookup vdev request for vdev id %d",
__func__, resp_event->vdev_id);
return -EINVAL;
}
if ((qdf_atomic_read
(&wma->interfaces[resp_event->vdev_id].vdev_restart_params.
hidden_ssid_restart_in_progress))
&& ((wma->interfaces[resp_event->vdev_id].type == WMI_VDEV_TYPE_AP)
&& (wma->interfaces[resp_event->vdev_id].sub_type == 0))) {
WMA_LOGE("%s: vdev stop event recevied for hidden ssid set using IOCTL ",
__func__);
wma->interfaces[resp_event->vdev_id].vdev_up = false;
WMA_LOGD(FL("Setting vdev_up flag to false"));
new_req_msg = wma_fill_vdev_req(wma, resp_event->vdev_id,
WMA_HIDDEN_SSID_VDEV_RESTART,
WMA_TARGET_REQ_TYPE_VDEV_START,
req_msg->user_data,
WMA_VDEV_START_REQUEST_TIMEOUT);
if (!new_req_msg) {
WMA_LOGE("%s: Failed to fill vdev request, vdev_id %d",
__func__, resp_event->vdev_id);
return -EINVAL;
}
wma_hidden_ssid_vdev_restart_on_vdev_stop(wma,
resp_event->vdev_id);
}
pdev = cds_get_context(QDF_MODULE_ID_TXRX);
if (!pdev) {
WMA_LOGE("%s: pdev is NULL", __func__);
status = -EINVAL;
wma_cleanup_target_req_param(req_msg);
qdf_mc_timer_stop(&req_msg->event_timeout);
goto free_req_msg;
}
qdf_mc_timer_stop(&req_msg->event_timeout);
if (req_msg->msg_type == WMA_DELETE_BSS_REQ) {
tpDeleteBssParams params =
(tpDeleteBssParams) req_msg->user_data;
if (iface->handle == NULL) {
WMA_LOGE("%s vdev id %d is already deleted",
__func__, resp_event->vdev_id);
wma_cleanup_target_req_param(req_msg);
status = -EINVAL;
goto free_req_msg;
}
/* CCA is required only for sta interface */
if (iface->type == WMI_VDEV_TYPE_STA)
wma_get_cca_stats(wma, resp_event->vdev_id);
/* Clear arp and ns offload cache */
qdf_mem_zero(&iface->ns_offload_req,
sizeof(iface->ns_offload_req));
qdf_mem_zero(&iface->arp_offload_req,
sizeof(iface->arp_offload_req));
if (wma_is_vdev_in_ibss_mode(wma, resp_event->vdev_id))
wma_delete_all_ibss_peers(wma, resp_event->vdev_id);
else if (WMA_IS_VDEV_IN_NDI_MODE(wma->interfaces,
resp_event->vdev_id)) {
wma_delete_all_nan_remote_peers(wma,
resp_event->vdev_id);
} else {
if (wma_is_vdev_in_ap_mode(wma, resp_event->vdev_id)) {
wma_delete_all_ap_remote_peers(wma,
resp_event->vdev_id);
}
result = wma_remove_peer_by_reference(pdev,
wma, params,
&peer_id,
params->bssid,
resp_event->vdev_id,
WMA_DELETE_PEER_RSP);
if (result == QDF_STATUS_SUCCESS)
goto free_req_msg;
}
wma_send_del_bss_response(wma, req_msg, resp_event->vdev_id);
} else if (req_msg->msg_type == WMA_SET_LINK_STATE) {
tpLinkStateParams params =
(tpLinkStateParams) req_msg->user_data;
result = wma_remove_peer_by_reference(pdev, wma, params,
&peer_id,
params->bssid,
req_msg->vdev_id,
WMA_SET_LINK_PEER_RSP);
if (result == QDF_STATUS_SUCCESS)
goto free_req_msg;
if (wma_send_vdev_down_to_fw(wma, req_msg->vdev_id) !=
QDF_STATUS_SUCCESS) {
WMA_LOGE("Failed to send vdev down cmd: vdev %d",
req_msg->vdev_id);
} else {
iface->vdev_up = false;
WMA_LOGD(FL("Setting vdev_up flag to false"));
}
wma_send_msg(wma, WMA_SET_LINK_STATE_RSP, (void *)params, 0);
}
free_req_msg:
qdf_mc_timer_destroy(&req_msg->event_timeout);
qdf_mem_free(req_msg);
return status;
}
/**
* wma_vdev_attach() - create vdev in fw
* @wma_handle: wma handle
* @self_sta_req: self sta request
* @generateRsp: generate response
*
* This function creates vdev in target and
* attach this vdev to txrx module. It also set
* vdev related params to fw.
*
* Return: txrx vdev handle
*/
ol_txrx_vdev_handle wma_vdev_attach(tp_wma_handle wma_handle,
struct add_sta_self_params *self_sta_req,
uint8_t generateRsp)
{
ol_txrx_vdev_handle txrx_vdev_handle = NULL;
ol_txrx_pdev_handle txrx_pdev = cds_get_context(QDF_MODULE_ID_TXRX);
enum wlan_op_mode txrx_vdev_type;
QDF_STATUS status = QDF_STATUS_SUCCESS;
struct sAniSirGlobal *mac = cds_get_context(QDF_MODULE_ID_PE);
uint32_t cfg_val;
uint16_t val16;
QDF_STATUS ret;
tSirMacHTCapabilityInfo *phtCapInfo;
cds_msg_t sme_msg = { 0 };
struct vdev_create_params params = { 0 };
u_int8_t vdev_id;
struct sir_set_tx_rx_aggregation_size tx_rx_aggregation_size;
WMA_LOGD("mac %pM, vdev_id %hu, type %d, sub_type %d, nss 2g %d, 5g %d",
self_sta_req->self_mac_addr, self_sta_req->session_id,
self_sta_req->type, self_sta_req->sub_type,
self_sta_req->nss_2g, self_sta_req->nss_5g);
if (NULL == mac) {
WMA_LOGE("%s: Failed to get mac", __func__);
goto end;
}
vdev_id = self_sta_req->session_id;
if (wma_is_vdev_valid(vdev_id)) {
WMA_LOGE("%s: vdev %d already active", __func__, vdev_id);
goto end;
}
params.if_id = self_sta_req->session_id;
params.type = self_sta_req->type;
params.subtype = self_sta_req->sub_type;
params.nss_2g = self_sta_req->nss_2g;
params.nss_5g = self_sta_req->nss_5g;
/* Create a vdev in target */
status = wmi_unified_vdev_create_send(wma_handle->wmi_handle,
self_sta_req->self_mac_addr,
&params);
if (QDF_IS_STATUS_ERROR(status)) {
WMA_LOGE("%s: Unable to add an interface for ath_dev",
__func__);
goto end;
}
txrx_vdev_type = wma_get_txrx_vdev_type(self_sta_req->type);
if (wlan_op_mode_unknown == txrx_vdev_type) {
WMA_LOGE("Failed to get txrx vdev type");
wmi_unified_vdev_delete_send(wma_handle->wmi_handle,
self_sta_req->session_id);
goto end;
}
txrx_vdev_handle = ol_txrx_vdev_attach(txrx_pdev,
self_sta_req->self_mac_addr,
self_sta_req->session_id,
txrx_vdev_type);
wma_handle->interfaces[self_sta_req->session_id].pause_bitmap = 0;
WMA_LOGD("vdev_id %hu, txrx_vdev_handle = %pK", self_sta_req->session_id,
txrx_vdev_handle);
if (NULL == txrx_vdev_handle) {
WMA_LOGE("%s: ol_txrx_vdev_attach failed", __func__);
status = QDF_STATUS_E_FAILURE;
wmi_unified_vdev_delete_send(wma_handle->wmi_handle,
self_sta_req->session_id);
goto end;
}
wma_handle->interfaces[self_sta_req->session_id].vdev_active = true;
wma_handle->interfaces[self_sta_req->session_id].handle =
txrx_vdev_handle;
wma_handle->interfaces[self_sta_req->session_id].ptrn_match_enable =
wma_handle->ptrn_match_enable_all_vdev ? true : false;
if (wlan_cfg_get_int(mac, WNI_CFG_WOWLAN_DEAUTH_ENABLE, &cfg_val)
!= eSIR_SUCCESS)
wma_handle->wow.deauth_enable = true;
else
wma_handle->wow.deauth_enable = cfg_val ? true : false;
if (wlan_cfg_get_int(mac, WNI_CFG_WOWLAN_DISASSOC_ENABLE, &cfg_val)
!= eSIR_SUCCESS)
wma_handle->wow.disassoc_enable = true;
else
wma_handle->wow.disassoc_enable = cfg_val ? true : false;
if (wlan_cfg_get_int(mac, WNI_CFG_WOWLAN_MAX_MISSED_BEACON, &cfg_val)
!= eSIR_SUCCESS)
wma_handle->wow.bmiss_enable = true;
else
wma_handle->wow.bmiss_enable = cfg_val ? true : false;
qdf_mem_copy(wma_handle->interfaces[self_sta_req->session_id].addr,
self_sta_req->self_mac_addr,
sizeof(wma_handle->interfaces[self_sta_req->session_id].
addr));
tx_rx_aggregation_size.tx_aggregation_size =
self_sta_req->tx_aggregation_size;
tx_rx_aggregation_size.rx_aggregation_size =
self_sta_req->rx_aggregation_size;
tx_rx_aggregation_size.vdev_id = self_sta_req->session_id;
status = wma_set_tx_rx_aggregation_size(&tx_rx_aggregation_size);
if (status != QDF_STATUS_SUCCESS)
WMA_LOGE("failed to set aggregation sizes(err=%d)", status);
switch (self_sta_req->type) {
case WMI_VDEV_TYPE_STA:
if (wlan_cfg_get_int(mac, WNI_CFG_INFRA_STA_KEEP_ALIVE_PERIOD,
&cfg_val) != eSIR_SUCCESS) {
WMA_LOGE("Failed to get value for WNI_CFG_INFRA_STA_KEEP_ALIVE_PERIOD");
cfg_val = DEFAULT_INFRA_STA_KEEP_ALIVE_PERIOD;
}
wma_set_sta_keep_alive(wma_handle,
self_sta_req->session_id,
SIR_KEEP_ALIVE_NULL_PKT,
cfg_val, NULL, NULL, NULL);
/* offload STA SA query related params to fwr */
if (WMI_SERVICE_IS_ENABLED(wma_handle->wmi_service_bitmap,
WMI_SERVICE_STA_PMF_OFFLOAD)) {
wma_set_sta_sa_query_param(wma_handle,
self_sta_req->session_id);
}
break;
}
wma_handle->interfaces[self_sta_req->session_id].type =
self_sta_req->type;
wma_handle->interfaces[self_sta_req->session_id].sub_type =
self_sta_req->sub_type;
qdf_atomic_init(&wma_handle->interfaces
[self_sta_req->session_id].bss_status);
if (((self_sta_req->type == WMI_VDEV_TYPE_AP) &&
(self_sta_req->sub_type == WMI_UNIFIED_VDEV_SUBTYPE_P2P_DEVICE)) ||
(self_sta_req->type == WMI_VDEV_TYPE_OCB) ||
(self_sta_req->type == WMI_VDEV_TYPE_MONITOR) ||
(self_sta_req->type == WMI_VDEV_TYPE_NDI)) {
status = wma_create_peer(wma_handle, txrx_pdev,
txrx_vdev_handle,
self_sta_req->self_mac_addr,
WMI_PEER_TYPE_DEFAULT,
self_sta_req->session_id, false);
if (status != QDF_STATUS_SUCCESS) {
WMA_LOGE("%s: Failed to create peer", __func__);
status = QDF_STATUS_E_FAILURE;
wmi_unified_vdev_delete_send(wma_handle->wmi_handle,
self_sta_req->session_id);
}
}
WMA_LOGD("Setting WMI_VDEV_PARAM_DISCONNECT_TH: %d",
self_sta_req->pkt_err_disconn_th);
ret = wma_vdev_set_param(wma_handle->wmi_handle,
self_sta_req->session_id,
WMI_VDEV_PARAM_DISCONNECT_TH,
self_sta_req->pkt_err_disconn_th);
if (ret)
WMA_LOGE("Failed to set WMI_VDEV_PARAM_DISCONNECT_TH");
wma_handle->interfaces[vdev_id].is_vdev_valid = true;
ret = wma_vdev_set_param(wma_handle->wmi_handle,
self_sta_req->session_id,
WMI_VDEV_PARAM_MCC_RTSCTS_PROTECTION_ENABLE,
mac->roam.configParam.mcc_rts_cts_prot_enable);
if (QDF_IS_STATUS_ERROR(ret))
WMA_LOGE("Failed to set WMI VDEV MCC_RTSCTS_PROTECTION_ENABLE");
ret = wma_vdev_set_param(wma_handle->wmi_handle,
self_sta_req->session_id,
WMI_VDEV_PARAM_MCC_BROADCAST_PROBE_ENABLE,
mac->roam.configParam.mcc_bcast_prob_resp_enable);
if (QDF_IS_STATUS_ERROR(ret))
WMA_LOGE("Failed to set WMI VDEV MCC_BROADCAST_PROBE_ENABLE");
if (wlan_cfg_get_int(mac, WNI_CFG_RTS_THRESHOLD,
&cfg_val) == eSIR_SUCCESS) {
ret = wma_vdev_set_param(wma_handle->wmi_handle,
self_sta_req->session_id,
WMI_VDEV_PARAM_RTS_THRESHOLD,
cfg_val);
if (QDF_IS_STATUS_ERROR(ret))
WMA_LOGE("Failed to set WMI_VDEV_PARAM_RTS_THRESHOLD");
} else {
WMA_LOGE("Failed to get value for WNI_CFG_RTS_THRESHOLD, leaving unchanged");
}
if (wlan_cfg_get_int(mac, WNI_CFG_FRAGMENTATION_THRESHOLD,
&cfg_val) == eSIR_SUCCESS) {
ret = wma_vdev_set_param(wma_handle->wmi_handle,
self_sta_req->session_id,
WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
cfg_val);
if (QDF_IS_STATUS_ERROR(ret))
WMA_LOGE("Failed to set WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD");
} else {
WMA_LOGE("Failed to get value for WNI_CFG_FRAGMENTATION_THRESHOLD, leaving unchanged");
}
if (wlan_cfg_get_int(mac, WNI_CFG_HT_CAP_INFO, &cfg_val) ==
eSIR_SUCCESS) {
val16 = (uint16_t) cfg_val;
phtCapInfo = (tSirMacHTCapabilityInfo *) &cfg_val;
ret = wma_vdev_set_param(wma_handle->wmi_handle,
self_sta_req->session_id,
WMI_VDEV_PARAM_TX_STBC,
phtCapInfo->txSTBC);
if (QDF_IS_STATUS_ERROR(ret))
WMA_LOGE("Failed to set WMI_VDEV_PARAM_TX_STBC");
} else {
WMA_LOGE("Failed to get value of HT_CAP, TX STBC unchanged");
}
wma_set_vdev_mgmt_rate(wma_handle, self_sta_req->session_id);
/* Initialize roaming offload state */
if ((self_sta_req->type == WMI_VDEV_TYPE_STA) &&
(self_sta_req->sub_type == 0)) {
wma_handle->roam_offload_enabled = true;
ret = wma_vdev_set_param(wma_handle->wmi_handle,
self_sta_req->session_id,
WMI_VDEV_PARAM_ROAM_FW_OFFLOAD,
(WMI_ROAM_FW_OFFLOAD_ENABLE_FLAG |
WMI_ROAM_BMISS_FINAL_SCAN_ENABLE_FLAG));
if (QDF_IS_STATUS_ERROR(ret))
WMA_LOGE("Failed to set WMI_VDEV_PARAM_ROAM_FW_OFFLOAD");
/* Pass down enable/disable bcast probe rsp to FW */
ret = wma_vdev_set_param(
wma_handle->wmi_handle,
self_sta_req->session_id,
WMI_VDEV_PARAM_ENABLE_BCAST_PROBE_RESPONSE,
self_sta_req->enable_bcast_probe_rsp);
if (QDF_IS_STATUS_ERROR(ret))
WMA_LOGE("Failed to set WMI_VDEV_PARAM_ENABLE_BCAST_PROBE_RESPONSE");
/* Pass down the FILS max channel guard time to FW */
ret = wma_vdev_set_param(
wma_handle->wmi_handle,
self_sta_req->session_id,
WMI_VDEV_PARAM_FILS_MAX_CHANNEL_GUARD_TIME,
self_sta_req->fils_max_chan_guard_time);
if (QDF_IS_STATUS_ERROR(ret))
WMA_LOGE("Failed to set WMI_VDEV_PARAM_FILS_MAX_CHANNEL_GUARD_TIME");
/* Pass down the Probe Request tx delay(in ms) to FW */
ret = wma_vdev_set_param(
wma_handle->wmi_handle,
self_sta_req->session_id,
WMI_VDEV_PARAM_PROBE_DELAY,
PROBE_REQ_TX_DELAY);
if (QDF_IS_STATUS_ERROR(ret))
WMA_LOGE("Failed to set WMI_VDEV_PARAM_PROBE_DELAY");
/* Pass down the probe request tx time gap(in ms) to FW */
ret = wma_vdev_set_param(
wma_handle->wmi_handle,
self_sta_req->session_id,
WMI_VDEV_PARAM_REPEAT_PROBE_TIME,
PROBE_REQ_TX_TIME_GAP);
if (QDF_IS_STATUS_ERROR(ret))
WMA_LOGE("Failed to set WMI_VDEV_PARAM_REPEAT_PROBE_TIME");
}
if ((self_sta_req->type == WMI_VDEV_TYPE_STA ||
self_sta_req->type == WMI_VDEV_TYPE_AP) &&
self_sta_req->sub_type == 0) {
ret = wma_vdev_set_param(wma_handle->wmi_handle,
self_sta_req->session_id,
WMI_VDEV_PARAM_ENABLE_DISABLE_OCE_FEATURES,
self_sta_req->oce_feature_bitmap);
if (QDF_IS_STATUS_ERROR(ret))
WMA_LOGE("Failed to set WMI_VDEV_PARAM_ENABLE_DISABLE_OCE_FEATURES");
}
/* Initialize BMISS parameters */
if ((self_sta_req->type == WMI_VDEV_TYPE_STA) &&
(self_sta_req->sub_type == 0))
wma_roam_scan_bmiss_cnt(wma_handle,
mac->roam.configParam.neighborRoamConfig.nRoamBmissFirstBcnt,
mac->roam.configParam.neighborRoamConfig.nRoamBmissFinalBcnt,
self_sta_req->session_id);
if (wlan_cfg_get_int(mac, WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED,
&cfg_val) == eSIR_SUCCESS) {
WMA_LOGD("%s: setting ini value for WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED: %d",
__func__, cfg_val);
ret = wma_set_enable_disable_mcc_adaptive_scheduler(cfg_val);
if (ret != QDF_STATUS_SUCCESS) {
WMA_LOGE("Failed to set WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED");
}
} else {
WMA_LOGE("Failed to get value for WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED, leaving unchanged");
}
wma_register_wow_wakeup_events(wma_handle, self_sta_req->session_id,
self_sta_req->type,
self_sta_req->sub_type);
wma_register_action_frame_patterns(wma_handle,
self_sta_req->session_id);
wma_register_wow_default_patterns(wma_handle, self_sta_req->session_id);
if (self_sta_req->type == WMI_VDEV_TYPE_STA) {
status = wma_config_active_apf_mode(wma_handle,
self_sta_req->session_id);
if (QDF_IS_STATUS_ERROR(status))
WMA_LOGE("Failed to configure active APF mode");
}
end:
self_sta_req->status = status;
#ifdef QCA_IBSS_SUPPORT
if (generateRsp)
#endif
{
sme_msg.type = eWNI_SME_ADD_STA_SELF_RSP;
sme_msg.bodyptr = self_sta_req;
sme_msg.bodyval = 0;
status = cds_mq_post_message(QDF_MODULE_ID_SME, &sme_msg);
if (!QDF_IS_STATUS_SUCCESS(status)) {
WMA_LOGE("Failed to post eWNI_SME_ADD_STA_SELF_RSP");
qdf_mem_free(self_sta_req);
}
}
return txrx_vdev_handle;
}
uint32_t wma_get_bcn_rate_code(uint16_t rate)
{
/* rate in multiples of 100 Kbps */
switch (rate) {
case WMA_BEACON_TX_RATE_1_M:
return WMI_BCN_TX_RATE_CODE_1_M;
case WMA_BEACON_TX_RATE_2_M:
return WMI_BCN_TX_RATE_CODE_2_M;
case WMA_BEACON_TX_RATE_5_5_M:
return WMI_BCN_TX_RATE_CODE_5_5_M;
case WMA_BEACON_TX_RATE_11_M:
return WMI_BCN_TX_RATE_CODE_11M;
case WMA_BEACON_TX_RATE_6_M:
return WMI_BCN_TX_RATE_CODE_6_M;
case WMA_BEACON_TX_RATE_9_M:
return WMI_BCN_TX_RATE_CODE_9_M;
case WMA_BEACON_TX_RATE_12_M:
return WMI_BCN_TX_RATE_CODE_12_M;
case WMA_BEACON_TX_RATE_18_M:
return WMI_BCN_TX_RATE_CODE_18_M;
case WMA_BEACON_TX_RATE_24_M:
return WMI_BCN_TX_RATE_CODE_24_M;
case WMA_BEACON_TX_RATE_36_M:
return WMI_BCN_TX_RATE_CODE_36_M;
case WMA_BEACON_TX_RATE_48_M:
return WMI_BCN_TX_RATE_CODE_48_M;
case WMA_BEACON_TX_RATE_54_M:
return WMI_BCN_TX_RATE_CODE_54_M;
default:
return WMI_BCN_TX_RATE_CODE_1_M;
}
}
/**
* wma_vdev_start() - send vdev start request to fw
* @wma: wma handle
* @req: vdev start params
* @isRestart: isRestart flag
*
* Return: QDF status
*/
QDF_STATUS wma_vdev_start(tp_wma_handle wma,
struct wma_vdev_start_req *req, bool isRestart)
{
struct vdev_start_params params = { 0 };
wmi_vdev_start_request_cmd_fixed_param *cmd;
struct wma_txrx_node *intr = wma->interfaces;
tpAniSirGlobal mac_ctx = NULL;
struct ath_dfs *dfs;
uint32_t temp_ssid_len = 0;
uint32_t temp_flags = 0;
uint32_t temp_chan_info = 0;
uint32_t temp_reg_info_1 = 0;
uint32_t temp_reg_info_2 = 0;
uint16_t bw_val;
struct wma_txrx_node *iface = &wma->interfaces[req->vdev_id];
struct wma_target_req *req_msg;
uint32_t chan_mode;
enum phy_ch_width ch_width;
mac_ctx = cds_get_context(QDF_MODULE_ID_PE);
if (mac_ctx == NULL) {
WMA_LOGE("%s: vdev start failed as mac_ctx is NULL", __func__);
return QDF_STATUS_E_FAILURE;
}
if (req->chan == 0) {
WMA_LOGE("%s: invalid channel: %d", __func__, req->chan);
QDF_ASSERT(0);
return QDF_STATUS_E_INVAL;
}
params.band_center_freq1 = cds_chan_to_freq(req->chan);
ch_width = req->chan_width;
bw_val = cds_bw_value(ch_width);
if (20 < bw_val) {
if (req->ch_center_freq_seg0) {
params.band_center_freq1 =
cds_chan_to_freq(req->ch_center_freq_seg0);
} else {
WMA_LOGE("%s: invalid cntr_freq for bw %d, drop to 20",
__func__, bw_val);
params.band_center_freq1 = cds_chan_to_freq(req->chan);
ch_width = CH_WIDTH_20MHZ;
bw_val = 20;
}
}
if (80 < bw_val) {
if (req->ch_center_freq_seg1) {
params.band_center_freq2 =
cds_chan_to_freq(req->ch_center_freq_seg1);
} else {
WMA_LOGE("%s: invalid cntr_freq for bw %d, drop to 80",
__func__, bw_val);
params.band_center_freq2 = 0;
ch_width = CH_WIDTH_80MHZ;
}
} else {
params.band_center_freq2 = 0;
}
chan_mode = wma_chan_phy_mode(req->chan, ch_width,
req->dot11_mode);
if (chan_mode == MODE_UNKNOWN) {
WMA_LOGE("%s: invalid phy mode!", __func__);
return QDF_STATUS_E_FAILURE;
}
if (!params.band_center_freq1) {
WMA_LOGE("%s: invalid center freq1", __func__);
return QDF_STATUS_E_FAILURE;
}
if (((ch_width == CH_WIDTH_160MHZ) || (ch_width == CH_WIDTH_80P80MHZ))
&& !params.band_center_freq2) {
WMA_LOGE("%s: invalid center freq2 for 160MHz", __func__);
return QDF_STATUS_E_FAILURE;
}
/* Fill channel info */
params.chan_freq = cds_chan_to_freq(req->chan);
params.chan_mode = chan_mode;
if (!isRestart &&
qdf_atomic_read(&iface->bss_status) == WMA_BSS_STATUS_STARTED) {
req_msg = wma_find_vdev_req(wma, req->vdev_id,
WMA_TARGET_REQ_TYPE_VDEV_STOP,
false);
if (!req_msg || req_msg->msg_type != WMA_DELETE_BSS_REQ) {
if (!cds_is_driver_recovering()) {
if (cds_is_self_recovery_enabled()) {
WMA_LOGE("BSS is in started state before vdev start, trigger recovery");
cds_trigger_recovery(
CDS_REASON_UNSPECIFIED);
} else {
WMA_LOGE("BSS is in started state before vdev start, BUG_ON()");
QDF_BUG(0);
}
}
}
}
dfs = (struct ath_dfs *)wma->dfs_ic->ic_dfs;
WMA_LOGD("%s: Enter isRestart=%d vdev=%d", __func__, isRestart,
req->vdev_id);
params.vdev_id = req->vdev_id;
intr[params.vdev_id].chanmode = params.chan_mode;
intr[params.vdev_id].ht_capable = req->ht_capable;
intr[params.vdev_id].vht_capable = req->vht_capable;
intr[params.vdev_id].config.gtx_info.gtxRTMask[0] =
CFG_TGT_DEFAULT_GTX_HT_MASK;
intr[params.vdev_id].config.gtx_info.gtxRTMask[1] =
CFG_TGT_DEFAULT_GTX_VHT_MASK;
if (wlan_cfg_get_int(mac_ctx, WNI_CFG_TGT_GTX_USR_CFG,
&intr[params.vdev_id].config.gtx_info.gtxUsrcfg) != eSIR_SUCCESS) {
intr[params.vdev_id].config.gtx_info.gtxUsrcfg =
WNI_CFG_TGT_GTX_USR_CFG_STADEF;
QDF_TRACE(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_WARN,
"Failed to get WNI_CFG_TGT_GTX_USR_CFG");
}
intr[params.vdev_id].config.gtx_info.gtxPERThreshold =
CFG_TGT_DEFAULT_GTX_PER_THRESHOLD;
intr[params.vdev_id].config.gtx_info.gtxPERMargin =
CFG_TGT_DEFAULT_GTX_PER_MARGIN;
intr[params.vdev_id].config.gtx_info.gtxTPCstep =
CFG_TGT_DEFAULT_GTX_TPC_STEP;
intr[params.vdev_id].config.gtx_info.gtxTPCMin =
CFG_TGT_DEFAULT_GTX_TPC_MIN;
intr[params.vdev_id].config.gtx_info.gtxBWMask =
CFG_TGT_DEFAULT_GTX_BW_MASK;
intr[params.vdev_id].mhz = params.chan_freq;
intr[params.vdev_id].chan_width = ch_width;
intr[params.vdev_id].channel = req->chan;
temp_chan_info &= 0xffffffc0;
temp_chan_info |= params.chan_mode;
/* Set half or quarter rate WMI flags */
params.is_half_rate = req->is_half_rate;
params.is_quarter_rate = req->is_quarter_rate;
if (req->is_half_rate)
temp_chan_info |= (1 << WMI_CHAN_FLAG_HALF_RATE);
else if (req->is_quarter_rate)
temp_chan_info |= (1 << WMI_CHAN_FLAG_QUARTER_RATE);
/* Config channel information in dfs_ic, the channel information
* is needed when processing spectral scan results
*/
wma_dfs_configure_channel(wma->dfs_ic,
params.band_center_freq1,
params.band_center_freq2, req);
params.is_dfs = req->is_dfs;
params.is_restart = isRestart;
if ((QDF_GLOBAL_MONITOR_MODE != cds_get_conparam()) && req->is_dfs) {
temp_chan_info |= (1 << WMI_CHAN_FLAG_DFS);
params.dis_hw_ack = true;
req->dfs_pri_multiplier = wma->dfs_pri_multiplier;
/*
* Configure the current operating channel
* to DFS module only if the device operating
* mode is AP.
* Enable/Disable Phyerr filtering offload
* depending on dfs_phyerr_filter_offload
* flag status as set in ini for SAP mode.
* Currently, only AP supports DFS master
* mode operation on DFS channels, P2P-GO
* does not support operation on DFS Channels.
*/
if (wma_is_vdev_in_ap_mode(wma, params.vdev_id) == true) {
/*
* If the Channel is DFS,
* set the WMI_CHAN_FLAG_DFS flag
*/
params.flag_dfs = WMI_CHAN_FLAG_DFS;
/*
* If DFS regulatory domain is invalid,
* then, DFS radar filters intialization
* will fail. So, do not configure the
* channel in to DFS modlue, do not
* indicate if phyerror filtering offload
* is enabled or not to the firmware, simply
* fail the VDEV start on the DFS channel
* early on, to protect the DFS module from
* processing phyerrors without being intialized.
*/
if (DFS_UNINIT_REGION ==
wma->dfs_ic->current_dfs_regdomain) {
WMA_LOGE("%s[%d]:DFS Configured with Invalid regdomain Failed to send VDEV START command",
__func__, __LINE__);
return QDF_STATUS_E_FAILURE;
}
if (isRestart)
wma->dfs_ic->disable_phy_err_processing = true;
/* provide the current channel to DFS */
wma_dfs_configure_channel(wma->dfs_ic,
params.band_center_freq1,
params.band_center_freq2, req);
wma_unified_dfs_phyerr_filter_offload_enable(wma);
dfs->disable_dfs_ch_switch =
mac_ctx->sap.SapDfsInfo.disable_dfs_ch_switch;
}
}
params.beacon_intval = req->beacon_intval;
params.dtim_period = req->dtim_period;
if (req->beacon_tx_rate) {
WMA_LOGD("%s: beacon tx rate [%hu * 100 Kbps]",
__func__, req->beacon_tx_rate);
temp_flags |= WMI_UNIFIED_VDEV_START_BCN_TX_RATE_PRESENT;
/*
* beacon_tx_rate is in multiples of 100 Kbps.
* Convert the data rate to hw rate code.
*/
params.bcn_tx_rate_code =
wma_get_bcn_rate_code(req->beacon_tx_rate);
}
/* FIXME: Find out min, max and regulatory power levels */
params.max_txpow = req->max_txpow;
temp_reg_info_1 &= 0xff00ffff;
temp_reg_info_1 |= ((req->max_txpow&0xff) << 16);
temp_reg_info_2 &= 0xffff00ff;
temp_reg_info_2 |= ((req->max_txpow&0xff)<<8);
/* TODO: Handle regulatory class, max antenna */
if (!isRestart) {
params.beacon_intval = req->beacon_intval;
params.dtim_period = req->dtim_period;
/* Copy the SSID */
if (req->ssid.length) {
params.ssid.length = req->ssid.length;
if (req->ssid.length < sizeof(cmd->ssid.ssid))
temp_ssid_len = req->ssid.length;
else
temp_ssid_len = sizeof(cmd->ssid.ssid);
qdf_mem_copy(params.ssid.mac_ssid, req->ssid.ssId,
temp_ssid_len);
}
params.pmf_enabled = req->pmf_enabled;
params.ldpc_rx_enabled = req->ldpc_rx_enabled;
if (req->pmf_enabled)
temp_flags |= WMI_UNIFIED_VDEV_START_PMF_ENABLED;
if (req->ldpc_rx_enabled)
temp_flags |= WMI_UNIFIED_VDEV_START_LDPC_RX_ENABLED;
}
params.hidden_ssid = req->hidden_ssid;
if (req->hidden_ssid)
temp_flags |= WMI_UNIFIED_VDEV_START_HIDDEN_SSID;
params.num_noa_descriptors = 0;
params.preferred_rx_streams = req->preferred_rx_streams;
params.preferred_tx_streams = req->preferred_tx_streams;
/* Store vdev params in SAP mode which can be used in vdev restart */
if (intr[req->vdev_id].type == WMI_VDEV_TYPE_AP &&
intr[req->vdev_id].sub_type == 0) {
intr[req->vdev_id].vdev_restart_params.vdev_id = req->vdev_id;
intr[req->vdev_id].vdev_restart_params.ssid.ssid_len =
temp_ssid_len;
qdf_mem_copy(intr[req->vdev_id].vdev_restart_params.ssid.ssid,
params.ssid.mac_ssid, temp_ssid_len);
intr[req->vdev_id].vdev_restart_params.flags = temp_flags;
intr[req->vdev_id].vdev_restart_params.requestor_id = 0;
intr[req->vdev_id].vdev_restart_params.disable_hw_ack =
params.dis_hw_ack;
intr[req->vdev_id].vdev_restart_params.chan.mhz =
params.chan_freq;
intr[req->vdev_id].vdev_restart_params.chan.band_center_freq1 =
params.band_center_freq1;
intr[req->vdev_id].vdev_restart_params.chan.band_center_freq2 =
params.band_center_freq2;
intr[req->vdev_id].vdev_restart_params.chan.info =
temp_chan_info;
intr[req->vdev_id].vdev_restart_params.chan.reg_info_1 =
temp_reg_info_1;
intr[req->vdev_id].vdev_restart_params.chan.reg_info_2 =
temp_reg_info_2;
}
if (isRestart) {
/*
* Marking the VDEV UP STATUS to false
* since, VDEV RESTART will do a VDEV DOWN
* in the firmware.
*/
intr[params.vdev_id].vdev_up = false;
WMA_LOGD(FL("Setting vdev_up flag to false"));
} else {
WMA_LOGD("%s, vdev_id: %d, unpausing tx_ll_queue at VDEV_START",
__func__, params.vdev_id);
ol_txrx_vdev_unpause(wma->interfaces[params.vdev_id].handle,
0xffffffff);
wma->interfaces[params.vdev_id].pause_bitmap = 0;
}
return wma_send_vdev_start_to_fw(wma, &params);
}
/**
* wma_peer_assoc_conf_handler() - peer assoc conf handler
* @handle: wma handle
* @cmd_param_info: event buffer
* @len: buffer length
*
* Return: 0 for success or error code
*/
int wma_peer_assoc_conf_handler(void *handle, uint8_t *cmd_param_info,
uint32_t len)
{
tp_wma_handle wma = (tp_wma_handle) handle;
WMI_PEER_ASSOC_CONF_EVENTID_param_tlvs *param_buf;
wmi_peer_assoc_conf_event_fixed_param *event;
struct wma_target_req *req_msg;
uint8_t macaddr[IEEE80211_ADDR_LEN];
int status = 0;
WMA_LOGD(FL("Enter"));
param_buf = (WMI_PEER_ASSOC_CONF_EVENTID_param_tlvs *) cmd_param_info;
if (!param_buf) {
WMA_LOGE("Invalid peer assoc conf event buffer");
return -EINVAL;
}
event = param_buf->fixed_param;
if (!event) {
WMA_LOGE("Invalid peer assoc conf event buffer");
return -EINVAL;
}
WMI_MAC_ADDR_TO_CHAR_ARRAY(&event->peer_macaddr, macaddr);
WMA_LOGD(FL("peer assoc conf for vdev:%d mac=%pM"),
event->vdev_id, macaddr);
req_msg = wma_find_req(wma, event->vdev_id,
WMA_PEER_ASSOC_CNF_START);
if (!req_msg) {
WMA_LOGE(FL("Failed to lookup request message for vdev %d"),
event->vdev_id);
return -EINVAL;
}
qdf_mc_timer_stop(&req_msg->event_timeout);
if (req_msg->msg_type == WMA_ADD_STA_REQ) {
tpAddStaParams params = (tpAddStaParams)req_msg->user_data;
if (!params) {
WMA_LOGE(FL("add STA params is NULL for vdev %d"),
event->vdev_id);
status = -EINVAL;
goto free_req_msg;
}
/* peer assoc conf event means the cmd succeeds */
params->status = QDF_STATUS_SUCCESS;
WMA_LOGD(FL("Send ADD_STA_RSP: statype %d vdev_id %d aid %d bssid %pM staIdx %d status %d"),
params->staType, params->smesessionId,
params->assocId, params->bssId, params->staIdx,
params->status);
wma_send_msg_high_priority(wma,
WMA_ADD_STA_RSP, (void *)params, 0);
} else if (req_msg->msg_type == WMA_ADD_BSS_REQ) {
tpAddBssParams params = (tpAddBssParams) req_msg->user_data;
if (!params) {
WMA_LOGE(FL("add BSS params is NULL for vdev %d"),
event->vdev_id);
status = -EINVAL;
goto free_req_msg;
}
/* peer assoc conf event means the cmd succeeds */
params->status = QDF_STATUS_SUCCESS;
WMA_LOGD(FL("Send ADD BSS RSP: opermode: %d update_bss: %d nw_type: %d bssid: %pM staIdx %d status %d"),
params->operMode,
params->updateBss, params->nwType, params->bssId,
params->staContext.staIdx, params->status);
wma_send_msg_high_priority(wma,
WMA_ADD_BSS_RSP, (void *)params, 0);
} else {
WMA_LOGE(FL("Unhandled request message type: %d"),
req_msg->msg_type);
}
free_req_msg:
qdf_mc_timer_destroy(&req_msg->event_timeout);
qdf_mem_free(req_msg);
return status;
}
/**
* wma_vdev_delete_handler() - vdev delete response handler
* @handle: wma handle
* @cmd_param_info: event buffer
* @len: buffer length
*
* Return: 0 for success or error code
*/
int wma_vdev_delete_handler(void *handle, uint8_t *cmd_param_info,
uint32_t len)
{
tp_wma_handle wma = (tp_wma_handle) handle;
WMI_VDEV_DELETE_RESP_EVENTID_param_tlvs *param_buf;
wmi_vdev_delete_cmd_fixed_param *event;
struct wma_target_req *req_msg;
int status = 0;
param_buf = (WMI_VDEV_DELETE_RESP_EVENTID_param_tlvs *)cmd_param_info;
if (!param_buf) {
WMA_LOGE("Invalid vdev delete event buffer");
return -EINVAL;
}
event = (wmi_vdev_delete_cmd_fixed_param *)param_buf->fixed_param;
if (!event) {
WMA_LOGE("Invalid vdev delete event buffer");
return -EINVAL;
}
WMA_LOGD("%s Vdev delete resp vdev id %d", __func__, event->vdev_id);
req_msg = wma_find_vdev_req(wma, event->vdev_id,
WMA_TARGET_REQ_TYPE_VDEV_DEL, true);
if (!req_msg) {
WMA_LOGD(FL("Vdev delete resp is not handled! vdev id %d"),
event->vdev_id);
return -EINVAL;
}
qdf_mc_timer_stop(&req_msg->event_timeout);
qdf_mc_timer_destroy(&req_msg->event_timeout);
wma_release_wakelock(&wma->wmi_cmd_rsp_wake_lock);
/* Send response to upper layers */
wma_vdev_detach_callback(req_msg->user_data);
qdf_mem_free(req_msg);
return status;
}
/**
* wma_peer_delete_handler() - peer delete response handler
* @handle: wma handle
* @cmd_param_info: event buffer
* @len: buffer length
*
* Return: 0 for success or error code
*/
int wma_peer_delete_handler(void *handle, uint8_t *cmd_param_info,
uint32_t len)
{
tp_wma_handle wma = (tp_wma_handle) handle;
WMI_PEER_DELETE_RESP_EVENTID_param_tlvs *param_buf;
wmi_peer_delete_cmd_fixed_param *event;
struct wma_target_req *req_msg;
tDeleteStaParams *del_sta;
uint8_t macaddr[IEEE80211_ADDR_LEN];
int status = 0;
param_buf = (WMI_PEER_DELETE_RESP_EVENTID_param_tlvs *)cmd_param_info;
if (!param_buf) {
WMA_LOGE("Invalid vdev delete event buffer");
return -EINVAL;
}
event = (wmi_peer_delete_cmd_fixed_param *)param_buf->fixed_param;
if (!event) {
WMA_LOGE("Invalid vdev delete event buffer");
return -EINVAL;
}
WMI_MAC_ADDR_TO_CHAR_ARRAY(&event->peer_macaddr, macaddr);
WMA_LOGD(FL("Peer Delete Response, vdev %d Peer %pM"),
event->vdev_id, macaddr);
wma_peer_debug_log(event->vdev_id, DEBUG_PEER_DELETE_RESP,
DEBUG_INVALID_PEER_ID, macaddr, NULL,
0,
0);
req_msg = wma_find_remove_req_msgtype(wma, event->vdev_id,
WMA_DELETE_STA_REQ);
if (!req_msg) {
WMA_LOGD("Peer Delete response is not handled");
return -EINVAL;
}
wma_release_wakelock(&wma->wmi_cmd_rsp_wake_lock);
/* Cleanup timeout handler */
qdf_mc_timer_stop(&req_msg->event_timeout);
qdf_mc_timer_destroy(&req_msg->event_timeout);
if (req_msg->type == WMA_DELETE_STA_RSP_START) {
del_sta = req_msg->user_data;
if (del_sta->respReqd) {
WMA_LOGD(FL("Sending peer del rsp to umac"));
wma_send_msg_high_priority(wma, WMA_DELETE_STA_RSP,
(void *)del_sta, QDF_STATUS_SUCCESS);
} else {
qdf_mem_free(del_sta);
}
} else if (req_msg->type == WMA_DEL_P2P_SELF_STA_RSP_START) {
struct del_sta_self_rsp_params *data;
data = (struct del_sta_self_rsp_params *)req_msg->user_data;
WMA_LOGD(FL("Calling vdev detach handler"));
wma_handle_vdev_detach(wma, data->self_sta_param,
data->generate_rsp);
qdf_mem_free(data);
} else if (req_msg->type == WMA_SET_LINK_PEER_RSP) {
tpLinkStateParams params =
(tpLinkStateParams) req_msg->user_data;
struct wma_txrx_node *iface;
iface = &wma->interfaces[req_msg->vdev_id];
if (wma_send_vdev_down_to_fw(wma, req_msg->vdev_id) !=
QDF_STATUS_SUCCESS) {
WMA_LOGE("Failed to send vdev down cmd: vdev %d",
req_msg->vdev_id);
} else {
iface->vdev_up = false;
WMA_LOGD(FL("Setting vdev_up flag to false"));
}
wma_send_msg(wma, WMA_SET_LINK_STATE_RSP, (void *)params, 0);
} else if (req_msg->type == WMA_DELETE_PEER_RSP) {
wma_send_del_bss_response(wma, req_msg, req_msg->vdev_id);
}
qdf_mem_free(req_msg);
return status;
}
static inline bool wma_crash_on_fw_timeout(bool crash_enabled)
{
/* Discard FW timeouts and dont crash during SSR */
if (cds_is_driver_recovering())
return false;
if (cds_is_driver_unloading())
return false;
/* Firmware is down send failure response */
if (cds_is_fw_down())
return false;
return crash_enabled;
}
/**
* wma_hold_req_timer() - wma hold request timeout function
* @data: target request params
*
* Return: none
*/
void wma_hold_req_timer(void *data)
{
tp_wma_handle wma;
struct wma_target_req *tgt_req = (struct wma_target_req *)data;
struct wma_target_req *msg;
wma = cds_get_context(QDF_MODULE_ID_WMA);
if (NULL == wma) {
WMA_LOGE(FL("Failed to get wma"));
return;
}
WMA_LOGA(FL("request %d is timed out for vdev_id - %d"),
tgt_req->msg_type, tgt_req->vdev_id);
msg = wma_find_req(wma, tgt_req->vdev_id, tgt_req->type);
if (!msg) {
WMA_LOGE(FL("Failed to lookup request message - %d"),
tgt_req->msg_type);
/*
* if find request failed, then firmware rsp should have
* consumed the buffer. Do not free.
*/
return;
}
if (tgt_req->msg_type == WMA_ADD_STA_REQ) {
tpAddStaParams params = (tpAddStaParams) tgt_req->user_data;
params->status = QDF_STATUS_E_TIMEOUT;
WMA_LOGA(FL("WMA_ADD_STA_REQ timed out"));
WMA_LOGD(FL("Sending add sta rsp to umac (mac:%pM, status:%d)"),
params->staMac, params->status);
if (wma_crash_on_fw_timeout(wma->fw_timeout_crash) == true)
QDF_BUG(0);
else
wma_send_msg_high_priority(wma,
WMA_ADD_STA_RSP, (void *)params, 0);
} else if (tgt_req->msg_type == WMA_ADD_BSS_REQ) {
tpAddBssParams params = (tpAddBssParams) tgt_req->user_data;
params->status = QDF_STATUS_E_TIMEOUT;
WMA_LOGA(FL("WMA_ADD_BSS_REQ timed out"));
WMA_LOGD(FL("Sending add bss rsp to umac (mac:%pM, status:%d)"),
params->selfMacAddr, params->status);
if (wma_crash_on_fw_timeout(wma->fw_timeout_crash) == true)
QDF_BUG(0);
else
wma_send_msg_high_priority(wma,
WMA_ADD_BSS_RSP, (void *)params, 0);
} else if ((tgt_req->msg_type == WMA_DELETE_STA_REQ) &&
(tgt_req->type == WMA_DELETE_STA_RSP_START)) {
tpDeleteStaParams params =
(tpDeleteStaParams) tgt_req->user_data;
params->status = QDF_STATUS_E_TIMEOUT;
WMA_LOGE(FL("WMA_DEL_STA_REQ timed out"));
WMA_LOGE(FL("Sending del sta rsp to umac (mac:%pM, status:%d)"),
params->staMac, params->status);
if (wma_crash_on_fw_timeout(wma->fw_timeout_crash) == true) {
QDF_BUG(0);
} else {
/*
* Assert in development build only.
* Send response in production builds.
*/
QDF_ASSERT(0);
wma_send_msg_high_priority(wma, WMA_DELETE_STA_RSP,
(void *)params, 0);
}
} else if ((tgt_req->msg_type == WMA_DELETE_STA_REQ) &&
(tgt_req->type == WMA_DEL_P2P_SELF_STA_RSP_START)) {
struct del_sta_self_rsp_params *del_sta;
del_sta = (struct del_sta_self_rsp_params *)tgt_req->user_data;
del_sta->self_sta_param->status = QDF_STATUS_E_TIMEOUT;
WMA_LOGA(FL("wma delete sta p2p request timed out"));
if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) {
QDF_BUG(0);
} else {
if (del_sta->generate_rsp)
wma_send_del_sta_self_resp(
del_sta->self_sta_param);
}
qdf_mem_free(tgt_req->user_data);
} else if ((tgt_req->msg_type == WMA_DELETE_STA_REQ) &&
(tgt_req->type == WMA_SET_LINK_PEER_RSP)) {
tpLinkStateParams params =
(tpLinkStateParams) tgt_req->user_data;
params->status = false;
WMA_LOGA(FL("wma delete peer for set link timed out"));
if (wma_crash_on_fw_timeout(wma->fw_timeout_crash) == true)
QDF_BUG(0);
else
wma_send_msg(wma, WMA_SET_LINK_STATE_RSP,
params, 0);
} else if ((tgt_req->msg_type == WMA_DELETE_STA_REQ) &&
(tgt_req->type == WMA_DELETE_PEER_RSP)) {
tpDeleteBssParams params =
(tpDeleteBssParams) tgt_req->user_data;
params->status = QDF_STATUS_E_TIMEOUT;
WMA_LOGE(FL("wma delete peer for del bss req timed out"));
if (wma_crash_on_fw_timeout(wma->fw_timeout_crash) == true)
QDF_BUG(0);
else
wma_send_msg_high_priority(wma,
WMA_DELETE_BSS_RSP, params, 0);
} else {
WMA_LOGE(FL("Unhandled timeout for msg_type:%d and type:%d"),
tgt_req->msg_type, tgt_req->type);
QDF_BUG(0);
}
qdf_mc_timer_destroy(&tgt_req->event_timeout);
qdf_mem_free(tgt_req);
}
/**
* wma_fill_hold_req() - fill wma request
* @wma: wma handle
* @msg_type: message type
* @type: request type
* @params: request params
* @timeout: timeout value
*
* Return: wma_target_req ptr
*/
struct wma_target_req *wma_fill_hold_req(tp_wma_handle wma,
uint8_t vdev_id,
uint32_t msg_type, uint8_t type,
void *params, uint32_t timeout)
{
struct wma_target_req *req;
QDF_STATUS status;
if (!cds_is_target_ready()) {
WMA_LOGE("target not ready, drop the request");
return NULL;
}
req = qdf_mem_malloc(sizeof(*req));
if (!req) {
WMA_LOGE(FL("Failed to allocate memory for msg %d vdev %d"),
msg_type, vdev_id);
return NULL;
}
WMA_LOGD(FL("vdev_id %d msg %d type %d"), vdev_id, msg_type, type);
qdf_spin_lock_bh(&wma->wma_hold_req_q_lock);
req->vdev_id = vdev_id;
req->msg_type = msg_type;
req->type = type;
req->user_data = params;
status = qdf_list_insert_back(&wma->wma_hold_req_queue, &req->node);
if (QDF_STATUS_SUCCESS != status) {
qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock);
WMA_LOGE(FL("Failed add request in queue"));
qdf_mem_free(req);
return NULL;
}
qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock);
qdf_mc_timer_init(&req->event_timeout, QDF_TIMER_TYPE_SW,
wma_hold_req_timer, req);
qdf_mc_timer_start(&req->event_timeout, timeout);
return req;
}
/**
* wma_remove_req() - remove request
* @wma: wma handle
* @vdev_id: vdev id
* @type: type
*
* Return: none
*/
void wma_remove_req(tp_wma_handle wma, uint8_t vdev_id,
uint8_t type)
{
struct wma_target_req *req_msg;
WMA_LOGD(FL("Remove req for vdev: %d type: %d"), vdev_id, type);
req_msg = wma_find_req(wma, vdev_id, type);
if (!req_msg) {
WMA_LOGE(FL("target req not found for vdev: %d type: %d"),
vdev_id, type);
return;
}
qdf_mc_timer_stop(&req_msg->event_timeout);
qdf_mc_timer_destroy(&req_msg->event_timeout);
qdf_mem_free(req_msg);
}
/**
* wma_vdev_resp_timer() - wma response timeout function
* @data: target request params
*
* Return: none
*/
void wma_vdev_resp_timer(void *data)
{
tp_wma_handle wma;
struct wma_target_req *tgt_req = (struct wma_target_req *)data;
ol_txrx_peer_handle peer;
ol_txrx_pdev_handle pdev;
uint8_t peer_id;
struct wma_target_req *msg;
struct wma_txrx_node *iface;
#ifdef FEATURE_AP_MCC_CH_AVOIDANCE
tpAniSirGlobal mac_ctx;
#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */
wma = cds_get_context(QDF_MODULE_ID_WMA);
if (NULL == wma) {
WMA_LOGE("%s: Failed to get wma", __func__);
wma_cleanup_target_req_param(tgt_req);
goto free_tgt_req;
}
WMA_LOGA("%s: request %d is timed out for vdev_id - %d", __func__,
tgt_req->msg_type, tgt_req->vdev_id);
msg = wma_find_vdev_req(wma, tgt_req->vdev_id, tgt_req->type, true);
if (!msg) {
WMA_LOGE("%s: Failed to lookup request message - %d",
__func__, tgt_req->msg_type);
return;
}
pdev = cds_get_context(QDF_MODULE_ID_TXRX);
if (NULL == pdev) {
WMA_LOGE("%s: Failed to get pdev", __func__);
wma_cleanup_target_req_param(tgt_req);
qdf_mc_timer_stop(&tgt_req->event_timeout);
goto free_tgt_req;
}
#ifdef FEATURE_AP_MCC_CH_AVOIDANCE
mac_ctx = cds_get_context(QDF_MODULE_ID_PE);
if (!mac_ctx) {
WMA_LOGE("%s: Failed to get mac_ctx", __func__);
wma_cleanup_target_req_param(tgt_req);
goto free_tgt_req;
}
#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */
iface = &wma->interfaces[tgt_req->vdev_id];
if (tgt_req->msg_type == WMA_CHNL_SWITCH_REQ) {
tpSwitchChannelParams params =
(tpSwitchChannelParams) tgt_req->user_data;
params->status = QDF_STATUS_E_TIMEOUT;
WMA_LOGA("%s: WMA_SWITCH_CHANNEL_REQ timedout", __func__);
wma_cli_set_command(tgt_req->vdev_id,
(int)WMI_VDEV_PARAM_ABG_MODE_TX_CHAIN_NUM, 0, VDEV_CMD);
WMA_LOGD("vdev: %d WMI_VDEV_PARAM_ABG_MODE_TX_CHAIN_NUM 0",
tgt_req->vdev_id);
/*
* Trigger host crash if the flag is set or if the timeout
* is not due to fw down
*/
if (wma_crash_on_fw_timeout(wma->fw_timeout_crash) == true)
QDF_BUG(0);
else
wma_send_msg_high_priority(wma, WMA_SWITCH_CHANNEL_RSP,
(void *)params, 0);
if (wma->interfaces[tgt_req->vdev_id].is_channel_switch) {
wma->interfaces[tgt_req->vdev_id].is_channel_switch =
false;
}
} else if (tgt_req->msg_type == WMA_DELETE_BSS_REQ) {
tpDeleteBssParams params =
(tpDeleteBssParams) tgt_req->user_data;
struct beacon_info *bcn;
struct wma_txrx_node *iface;
if (tgt_req->vdev_id >= wma->max_bssid) {
WMA_LOGE("%s: Invalid vdev_id %d", __func__,
tgt_req->vdev_id);
wma_cleanup_target_req_param(tgt_req);
qdf_mc_timer_stop(&tgt_req->event_timeout);
goto free_tgt_req;
}
iface = &wma->interfaces[tgt_req->vdev_id];
if (iface->handle == NULL) {
WMA_LOGE("%s vdev id %d is already deleted",
__func__, tgt_req->vdev_id);
wma_cleanup_target_req_param(tgt_req);
qdf_mc_timer_stop(&tgt_req->event_timeout);
goto free_tgt_req;
}
/*
* Trigger host crash if the flag is set or if the timeout
* is not due to fw down
*/
if (wma_crash_on_fw_timeout(wma->fw_timeout_crash) == true) {
QDF_BUG(0);
return;
}
if (wma_is_vdev_in_ibss_mode(wma, tgt_req->vdev_id))
wma_delete_all_ibss_peers(wma, tgt_req->vdev_id);
else {
if (wma_is_vdev_in_ap_mode(wma, tgt_req->vdev_id)) {
wma_delete_all_ap_remote_peers(wma,
tgt_req->
vdev_id);
}
peer = ol_txrx_find_peer_by_addr(pdev, params->bssid,
&peer_id);
wma_remove_peer(wma, params->bssid, tgt_req->vdev_id,
peer, false);
}
if (wma_send_vdev_down_to_fw(wma, tgt_req->vdev_id) !=
QDF_STATUS_SUCCESS) {
WMA_LOGE("Failed to send vdev down cmd: vdev %d",
tgt_req->vdev_id);
} else {
wma->interfaces[tgt_req->vdev_id].vdev_up = false;
WMA_LOGD(FL("Setting vdev_up flag to false"));
#ifdef FEATURE_AP_MCC_CH_AVOIDANCE
if (mac_ctx->sap.sap_channel_avoidance)
wma_find_mcc_ap(wma, tgt_req->vdev_id, false);
#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */
}
ol_txrx_vdev_flush(iface->handle);
WMA_LOGD("%s, vdev_id: %d, un-pausing tx_ll_queue for WDA_DELETE_BSS_REQ timeout",
__func__, tgt_req->vdev_id);
ol_txrx_vdev_unpause(iface->handle,
OL_TXQ_PAUSE_REASON_VDEV_STOP);
iface->pause_bitmap &= ~(1 << PAUSE_TYPE_HOST);
qdf_atomic_set(&iface->bss_status, WMA_BSS_STATUS_STOPPED);
WMA_LOGD("%s: (type %d subtype %d) BSS is stopped",
__func__, iface->type, iface->sub_type);
bcn = wma->interfaces[tgt_req->vdev_id].beacon;
if (bcn) {
WMA_LOGD("%s: Freeing beacon struct %pK, template memory %pK",
__func__, bcn, bcn->buf);
if (bcn->dma_mapped)
qdf_nbuf_unmap_single(pdev->osdev, bcn->buf,
QDF_DMA_TO_DEVICE);
qdf_nbuf_free(bcn->buf);
qdf_mem_free(bcn);
wma->interfaces[tgt_req->vdev_id].beacon = NULL;
}
params->status = QDF_STATUS_E_TIMEOUT;
WMA_LOGA("%s: WMA_DELETE_BSS_REQ timedout", __func__);
wma_send_msg_high_priority(wma, WMA_DELETE_BSS_RSP,
(void *)params, 0);
if (iface->del_staself_req && iface->is_del_sta_defered) {
iface->is_del_sta_defered = false;
WMA_LOGA("scheduling defered deletion(vdev id %x)",
tgt_req->vdev_id);
wma_vdev_detach(wma, iface->del_staself_req, 1);
}
} else if (tgt_req->msg_type == WMA_DEL_STA_SELF_REQ) {
struct wma_txrx_node *iface =
(struct wma_txrx_node *)tgt_req->user_data;
struct del_sta_self_params *params =
(struct del_sta_self_params *) iface->del_staself_req;
if (WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap,
WMI_SERVICE_SYNC_DELETE_CMDS)) {
wma_release_wakelock(&wma->wmi_cmd_rsp_wake_lock);
}
params->status = QDF_STATUS_E_TIMEOUT;
WMA_LOGA("%s: WMA_DEL_STA_SELF_REQ timedout", __func__);
if (wma_crash_on_fw_timeout(wma->fw_timeout_crash) == true) {
QDF_BUG(0);
} else if (!cds_is_driver_unloading() &&
(cds_is_fw_down() || cds_is_driver_recovering())) {
qdf_mem_free(iface->del_staself_req);
iface->del_staself_req = NULL;
} else {
wma_send_del_sta_self_resp(iface->del_staself_req);
}
if (iface->addBssStaContext) {
qdf_mem_free(iface->addBssStaContext);
iface->addBssStaContext = NULL;
}
if (iface->staKeyParams) {
qdf_mem_free(iface->staKeyParams);
iface->staKeyParams = NULL;
}
wma_vdev_deinit(iface);
qdf_mem_zero(iface, sizeof(*iface));
wma_vdev_init(iface);
} else if (tgt_req->msg_type == WMA_ADD_BSS_REQ) {
tpAddBssParams params = (tpAddBssParams) tgt_req->user_data;
params->status = QDF_STATUS_E_TIMEOUT;
WMA_LOGA("%s: WMA_ADD_BSS_REQ timedout", __func__);
WMA_LOGD("%s: bssid %pM vdev_id %d", __func__, params->bssId,
tgt_req->vdev_id);
if (wma_crash_on_fw_timeout(wma->fw_timeout_crash) == true) {
QDF_BUG(0);
} else {
wma_send_msg_high_priority(wma,
WMA_ADD_BSS_RSP, (void *)params, 0);
QDF_ASSERT(0);
}
goto free_tgt_req;
} else if (tgt_req->msg_type == WMA_OCB_SET_CONFIG_CMD) {
WMA_LOGE(FL("Failed to send OCB set config cmd"));
iface->vdev_up = false;
WMA_LOGD(FL("Setting vdev_up flag to false"));
wma_ocb_set_config_resp(wma, QDF_STATUS_E_TIMEOUT);
} else if (tgt_req->msg_type == WMA_HIDDEN_SSID_VDEV_RESTART) {
WMA_LOGE("Hidden ssid vdev restart Timed Out; vdev_id: %d, type = %d",
tgt_req->vdev_id, tgt_req->type);
} else if (tgt_req->msg_type == WMA_SET_LINK_STATE) {
tpLinkStateParams params =
(tpLinkStateParams) tgt_req->user_data;
peer = ol_txrx_find_peer_by_addr(pdev, params->bssid, &peer_id);
if (peer) {
WMA_LOGE(FL("Deleting peer %pM vdev id %d"),
params->bssid, tgt_req->vdev_id);
wma_remove_peer(wma, params->bssid, tgt_req->vdev_id,
peer, false);
}
if (wma_send_vdev_down_to_fw(wma, tgt_req->vdev_id) !=
QDF_STATUS_SUCCESS) {
WMA_LOGE("Failed to send vdev down cmd: vdev %d",
tgt_req->vdev_id);
} else {
iface->vdev_up = false;
WMA_LOGD(FL("Setting vdev_up flag to false"));
}
params->status = QDF_STATUS_E_TIMEOUT;
WMA_LOGA("%s: WMA_SET_LINK_STATE timedout vdev %d", __func__,
tgt_req->vdev_id);
wma_send_msg(wma, WMA_SET_LINK_STATE_RSP, (void *)params, 0);
}
free_tgt_req:
qdf_mc_timer_destroy(&tgt_req->event_timeout);
qdf_mem_free(tgt_req);
}
/**
* wma_fill_vdev_req() - fill vdev request
* @wma: wma handle
* @msg_type: message type
* @type: request type
* @params: request params
* @timeout: timeout value
*
* Return: wma_target_req ptr
*/
struct wma_target_req *wma_fill_vdev_req(tp_wma_handle wma,
uint8_t vdev_id,
uint32_t msg_type, uint8_t type,
void *params, uint32_t timeout)
{
struct wma_target_req *req;
QDF_STATUS status;
if (!cds_is_target_ready()) {
WMA_LOGE("target not ready, drop the request");
return NULL;
}
req = qdf_mem_malloc(sizeof(*req));
if (!req) {
WMA_LOGE("%s: Failed to allocate memory for msg %d vdev %d",
__func__, msg_type, vdev_id);
return NULL;
}
WMA_LOGD("%s: vdev_id %d msg %d", __func__, vdev_id, msg_type);
qdf_spin_lock_bh(&wma->vdev_respq_lock);
req->vdev_id = vdev_id;
req->msg_type = msg_type;
req->type = type;
req->user_data = params;
status = qdf_list_insert_back(&wma->vdev_resp_queue, &req->node);
if (QDF_STATUS_SUCCESS != status) {
qdf_spin_unlock_bh(&wma->vdev_respq_lock);
WMA_LOGE(FL("Failed add request in queue for vdev_id %d type %d"),
vdev_id, type);
qdf_mem_free(req);
return NULL;
}
qdf_spin_unlock_bh(&wma->vdev_respq_lock);
qdf_mc_timer_init(&req->event_timeout, QDF_TIMER_TYPE_SW,
wma_vdev_resp_timer, req);
qdf_mc_timer_start(&req->event_timeout, timeout);
return req;
}
/**
* wma_remove_vdev_req() - remove vdev request
* @wma: wma handle
* @vdev_id: vdev id
* @type: type
*
* Return: none
*/
void wma_remove_vdev_req(tp_wma_handle wma, uint8_t vdev_id,
uint8_t type)
{
struct wma_target_req *req_msg;
req_msg = wma_find_vdev_req(wma, vdev_id, type, true);
if (!req_msg)
return;
qdf_mc_timer_stop(&req_msg->event_timeout);
qdf_mc_timer_destroy(&req_msg->event_timeout);
qdf_mem_free(req_msg);
}
/**
* wma_vdev_set_bss_params() - BSS set params functions
* @wma: wma handle
* @vdev_id: vdev id
* @beaconInterval: beacon interval
* @dtimPeriod: DTIM period
* @shortSlotTimeSupported: short slot time
* @llbCoexist: llbCoexist
* @maxTxPower: max tx power
*
* Return: none
*/
static void
wma_vdev_set_bss_params(tp_wma_handle wma, int vdev_id,
tSirMacBeaconInterval beaconInterval,
uint8_t dtimPeriod, uint8_t shortSlotTimeSupported,
uint8_t llbCoexist, int8_t maxTxPower)
{
QDF_STATUS ret;
uint32_t slot_time;
struct wma_txrx_node *intr = wma->interfaces;
/* Beacon Interval setting */
ret = wma_vdev_set_param(wma->wmi_handle, vdev_id,
WMI_VDEV_PARAM_BEACON_INTERVAL,
beaconInterval);
if (QDF_IS_STATUS_ERROR(ret))
WMA_LOGE("failed to set WMI_VDEV_PARAM_BEACON_INTERVAL");
ret = wmi_unified_vdev_set_gtx_cfg_send(wma->wmi_handle, vdev_id,
&intr[vdev_id].config.gtx_info);
if (QDF_IS_STATUS_ERROR(ret))
WMA_LOGE("failed to set WMI_VDEV_PARAM_DTIM_PERIOD");
ret = wma_vdev_set_param(wma->wmi_handle, vdev_id,
WMI_VDEV_PARAM_DTIM_PERIOD,
dtimPeriod);
intr[vdev_id].dtimPeriod = dtimPeriod;
if (QDF_IS_STATUS_ERROR(ret))
WMA_LOGE("failed to set WMI_VDEV_PARAM_DTIM_PERIOD");
if (!maxTxPower)
WMA_LOGW("Setting Tx power limit to 0");
WMA_LOGD("Set maxTx pwr [WMI_VDEV_PARAM_TX_PWRLIMIT] to %d",
maxTxPower);
ret = wma_vdev_set_param(wma->wmi_handle, vdev_id,
WMI_VDEV_PARAM_TX_PWRLIMIT,
maxTxPower);
if (QDF_IS_STATUS_ERROR(ret))
WMA_LOGE("failed to set WMI_VDEV_PARAM_TX_PWRLIMIT");
else
intr[vdev_id].max_tx_power = maxTxPower;
/* Slot time */
if (shortSlotTimeSupported)
slot_time = WMI_VDEV_SLOT_TIME_SHORT;
else
slot_time = WMI_VDEV_SLOT_TIME_LONG;
ret = wma_vdev_set_param(wma->wmi_handle, vdev_id,
WMI_VDEV_PARAM_SLOT_TIME,
slot_time);
if (QDF_IS_STATUS_ERROR(ret))
WMA_LOGE("failed to set WMI_VDEV_PARAM_SLOT_TIME");
/* Initialize protection mode in case of coexistence */
wma_update_protection_mode(wma, vdev_id, llbCoexist);
}
#ifdef WLAN_FEATURE_11W
static void wma_set_mgmt_frame_protection(tp_wma_handle wma)
{
struct pdev_params param = {0};
QDF_STATUS ret;
/*
* when 802.11w PMF is enabled for hw encr/decr
* use hw MFP Qos bits 0x10
*/
param.param_id = WMI_PDEV_PARAM_PMF_QOS;
param.param_value = true;
ret = wmi_unified_pdev_param_send(wma->wmi_handle,
&param, WMA_WILDCARD_PDEV_ID);
if (QDF_IS_STATUS_ERROR(ret)) {
WMA_LOGE("%s: Failed to set QOS MFP/PMF (%d)",
__func__, ret);
} else {
WMA_LOGD("%s: QOS MFP/PMF set", __func__);
}
}
#else
static inline void wma_set_mgmt_frame_protection(tp_wma_handle wma)
{
}
#endif /* WLAN_FEATURE_11W */
/**
* wma_add_bss_ap_mode() - process add bss request in ap mode
* @wma: wma handle
* @add_bss: add bss parameters
*
* Return: none
*/
static void wma_add_bss_ap_mode(tp_wma_handle wma, tpAddBssParams add_bss)
{
ol_txrx_pdev_handle pdev;
ol_txrx_vdev_handle vdev;
struct wma_vdev_start_req req;
ol_txrx_peer_handle peer;
struct wma_target_req *msg;
uint8_t vdev_id, peer_id;
QDF_STATUS status;
int8_t maxTxPower;
struct sir_hw_mode_params hw_mode = {0};
uint32_t wow_mask[WMI_WOW_MAX_EVENT_BM_LEN] = {0};
pdev = cds_get_context(QDF_MODULE_ID_TXRX);
if (NULL == pdev) {
WMA_LOGE("%s: Failed to get pdev", __func__);
goto send_fail_resp;
}
vdev = wma_find_vdev_by_addr(wma, add_bss->bssId, &vdev_id);
if (!vdev) {
WMA_LOGE("%s: Failed to get vdev handle:"MAC_ADDRESS_STR,
__func__, MAC_ADDR_ARRAY(add_bss->bssId));
goto send_fail_resp;
}
if (SAP_WPS_DISABLED == add_bss->wps_state) {
wma_set_wow_event_bitmap(WOW_PROBE_REQ_WPS_IE_EVENT,
WMI_WOW_MAX_EVENT_BM_LEN,
wow_mask);
wma_enable_disable_wakeup_event(wma, vdev_id,
wow_mask, false);
}
wma_set_bss_rate_flags(&wma->interfaces[vdev_id], add_bss);
status = wma_create_peer(wma, pdev, vdev, add_bss->bssId,
WMI_PEER_TYPE_DEFAULT, vdev_id, false);
if (status != QDF_STATUS_SUCCESS) {
WMA_LOGE("%s: Failed to create peer", __func__);
goto send_fail_resp;
}
peer = ol_txrx_find_peer_by_addr(pdev, add_bss->bssId, &peer_id);
if (!peer) {
WMA_LOGE("%s Failed to find peer %pM", __func__,
add_bss->bssId);
goto send_fail_resp;
}
msg = wma_fill_vdev_req(wma, vdev_id, WMA_ADD_BSS_REQ,
WMA_TARGET_REQ_TYPE_VDEV_START, add_bss,
WMA_VDEV_START_REQUEST_TIMEOUT);
if (!msg) {
WMA_LOGE("%s Failed to allocate vdev request vdev_id %d",
__func__, vdev_id);
goto peer_cleanup;
}
add_bss->staContext.staIdx = ol_txrx_local_peer_id(peer);
qdf_mem_zero(&req, sizeof(req));
req.vdev_id = vdev_id;
req.chan = add_bss->currentOperChannel;
req.chan_width = add_bss->ch_width;
if (add_bss->ch_width == CH_WIDTH_10MHZ)
req.is_half_rate = 1;
else if (add_bss->ch_width == CH_WIDTH_5MHZ)
req.is_quarter_rate = 1;
req.ch_center_freq_seg0 = add_bss->ch_center_freq_seg0;
req.ch_center_freq_seg1 = add_bss->ch_center_freq_seg1;
req.vht_capable = add_bss->vhtCapable;
req.max_txpow = add_bss->maxTxPower;
maxTxPower = add_bss->maxTxPower;
if (add_bss->rmfEnabled)
wma_set_mgmt_frame_protection(wma);
req.beacon_intval = add_bss->beaconInterval;
req.dtim_period = add_bss->dtimPeriod;
req.beacon_tx_rate = add_bss->beacon_tx_rate;
req.hidden_ssid = add_bss->bHiddenSSIDEn;
req.is_dfs = add_bss->bSpectrumMgtEnabled;
req.oper_mode = BSS_OPERATIONAL_MODE_AP;
req.ssid.length = add_bss->ssId.length;
if (req.ssid.length > 0)
qdf_mem_copy(req.ssid.ssId, add_bss->ssId.ssId,
add_bss->ssId.length);
status = wma_get_current_hw_mode(&hw_mode);
if (!QDF_IS_STATUS_SUCCESS(status))
WMA_LOGE("wma_get_current_hw_mode failed");
if (add_bss->nss == 2) {
req.preferred_rx_streams = 2;
req.preferred_tx_streams = 2;
} else {
req.preferred_rx_streams = 1;
req.preferred_tx_streams = 1;
}
status = wma_vdev_start(wma, &req, false);
if (status != QDF_STATUS_SUCCESS) {
wma_remove_vdev_req(wma, vdev_id,
WMA_TARGET_REQ_TYPE_VDEV_START);
goto peer_cleanup;
}
wma_vdev_set_bss_params(wma, vdev_id,
add_bss->beaconInterval, add_bss->dtimPeriod,
add_bss->shortSlotTimeSupported,
add_bss->llbCoexist, maxTxPower);
return;
peer_cleanup:
wma_remove_peer(wma, add_bss->bssId, vdev_id, peer, false);
send_fail_resp:
add_bss->status = QDF_STATUS_E_FAILURE;
wma_send_msg_high_priority(wma, WMA_ADD_BSS_RSP, (void *)add_bss, 0);
}
#ifdef QCA_IBSS_SUPPORT
/**
* wma_add_bss_ibss_mode() - process add bss request in IBSS mode
* @wma: wma handle
* @add_bss: add bss parameters
*
* Return: none
*/
static void wma_add_bss_ibss_mode(tp_wma_handle wma, tpAddBssParams add_bss)
{
ol_txrx_pdev_handle pdev;
ol_txrx_vdev_handle vdev;
struct wma_vdev_start_req req;
ol_txrx_peer_handle peer = NULL;
struct wma_target_req *msg;
uint8_t vdev_id, peer_id;
QDF_STATUS status;
tSetBssKeyParams key_info;
struct sir_hw_mode_params hw_mode = {0};
vdev = wma_find_vdev_by_addr(wma, add_bss->selfMacAddr, &vdev_id);
if (!vdev) {
WMA_LOGE("%s: vdev not found for vdev id %d.",
__func__, vdev_id);
goto send_fail_resp;
}
WMA_LOGD("%s: add_bss->sessionId = %d", __func__, vdev_id);
pdev = cds_get_context(QDF_MODULE_ID_TXRX);
if (NULL == pdev) {
WMA_LOGE("%s: Failed to get pdev", __func__);
goto send_fail_resp;
}
wma_set_bss_rate_flags(&wma->interfaces[vdev_id], add_bss);
/* create ibss bss peer */
status = wma_create_peer(wma, pdev, vdev, add_bss->selfMacAddr,
WMI_PEER_TYPE_DEFAULT, vdev_id,
false);
if (status != QDF_STATUS_SUCCESS) {
WMA_LOGE("%s: Failed to create peer", __func__);
goto send_fail_resp;
}
WMA_LOGA("IBSS BSS peer created with mac %pM",
add_bss->selfMacAddr);
peer = ol_txrx_find_peer_by_addr(pdev, add_bss->selfMacAddr, &peer_id);
if (!peer) {
WMA_LOGE("%s Failed to find peer %pM", __func__,
add_bss->selfMacAddr);
goto send_fail_resp;
}
/* clear leftover ibss keys on bss peer */
WMA_LOGD("%s: ibss bss key clearing", __func__);
qdf_mem_set(&key_info, sizeof(key_info), 0);
key_info.smesessionId = vdev_id;
key_info.numKeys = SIR_MAC_MAX_NUM_OF_DEFAULT_KEYS;
qdf_mem_copy(&wma->ibsskey_info, &key_info, sizeof(tSetBssKeyParams));
/* start ibss vdev */
add_bss->operMode = BSS_OPERATIONAL_MODE_IBSS;
msg = wma_fill_vdev_req(wma, vdev_id, WMA_ADD_BSS_REQ,
WMA_TARGET_REQ_TYPE_VDEV_START, add_bss,
WMA_VDEV_START_REQUEST_TIMEOUT);
if (!msg) {
WMA_LOGE("%s Failed to allocate vdev request vdev_id %d",
__func__, vdev_id);
goto peer_cleanup;
}
WMA_LOGD("%s: vdev start request for IBSS enqueued", __func__);
add_bss->staContext.staIdx = ol_txrx_local_peer_id(peer);
/*
* If IBSS Power Save is supported by firmware
* set the IBSS power save params to firmware.
*/
if (WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap,
WMI_SERVICE_IBSS_PWRSAVE)) {
status = wma_set_ibss_pwrsave_params(wma, vdev_id);
if (status != QDF_STATUS_SUCCESS) {
WMA_LOGE("%s: Failed to Set IBSS Power Save Params to firmware",
__func__);
goto peer_cleanup;
}
}
qdf_mem_zero(&req, sizeof(req));
req.vdev_id = vdev_id;
req.chan = add_bss->currentOperChannel;
req.chan_width = add_bss->ch_width;
req.ch_center_freq_seg0 = add_bss->ch_center_freq_seg0;
req.ch_center_freq_seg1 = add_bss->ch_center_freq_seg1;
req.vht_capable = add_bss->vhtCapable;
#if defined WLAN_FEATURE_VOWIF
req.max_txpow = add_bss->maxTxPower;
#else
req.max_txpow = 0;
#endif /* WLAN_FEATURE_VOWIF */
req.beacon_intval = add_bss->beaconInterval;
req.dtim_period = add_bss->dtimPeriod;
req.hidden_ssid = add_bss->bHiddenSSIDEn;
req.is_dfs = add_bss->bSpectrumMgtEnabled;
req.oper_mode = BSS_OPERATIONAL_MODE_IBSS;
req.ssid.length = add_bss->ssId.length;
if (req.ssid.length > 0)
qdf_mem_copy(req.ssid.ssId, add_bss->ssId.ssId,
add_bss->ssId.length);
status = wma_get_current_hw_mode(&hw_mode);
if (!QDF_IS_STATUS_SUCCESS(status))
WMA_LOGE("wma_get_current_hw_mode failed");
if (add_bss->nss == 2) {
req.preferred_rx_streams = 2;
req.preferred_tx_streams = 2;
} else {
req.preferred_rx_streams = 1;
req.preferred_tx_streams = 1;
}
WMA_LOGD("%s: chan %d chan_width %d", __func__, req.chan,
req.chan_width);
WMA_LOGD("%s: ssid = %s", __func__, req.ssid.ssId);
status = wma_vdev_start(wma, &req, false);
if (status != QDF_STATUS_SUCCESS) {
wma_remove_vdev_req(wma, vdev_id,
WMA_TARGET_REQ_TYPE_VDEV_START);
goto peer_cleanup;
}
WMA_LOGD("%s: vdev start request for IBSS sent to target", __func__);
/* Initialize protection mode to no protection */
status = wma_vdev_set_param(wma->wmi_handle, vdev_id,
WMI_VDEV_PARAM_PROTECTION_MODE,
IEEE80211_PROT_NONE);
if (QDF_IS_STATUS_ERROR(status))
WMA_LOGE("Failed to initialize protection mode");
return;
peer_cleanup:
if (peer)
wma_remove_peer(wma, add_bss->bssId, vdev_id, peer, false);
send_fail_resp:
add_bss->status = QDF_STATUS_E_FAILURE;
wma_send_msg_high_priority(wma, WMA_ADD_BSS_RSP, (void *)add_bss, 0);
}
#endif /* QCA_IBSS_SUPPORT */
/**
* wma_add_bss_sta_mode() - process add bss request in sta mode
* @wma: wma handle
* @add_bss: add bss parameters
*
* Return: none
*/
static void wma_add_bss_sta_mode(tp_wma_handle wma, tpAddBssParams add_bss)
{
ol_txrx_pdev_handle pdev;
struct wma_vdev_start_req req;
struct wma_target_req *msg;
uint8_t peer_id;
ol_txrx_peer_handle peer = NULL;
QDF_STATUS status;
struct wma_txrx_node *iface;
int pps_val = 0;
bool roam_synch_in_progress = false;
tpAniSirGlobal pMac = cds_get_context(QDF_MODULE_ID_PE);
struct sir_hw_mode_params hw_mode = {0};
bool peer_assoc_sent = false;
uint8_t vdev_id = add_bss->staContext.smesessionId;
if (NULL == pMac) {
WMA_LOGE("%s: Unable to get PE context", __func__);
goto send_fail_resp;
}
pdev = cds_get_context(QDF_MODULE_ID_TXRX);
if (NULL == pdev) {
WMA_LOGE("%s Failed to get pdev", __func__);
goto send_fail_resp;
}
iface = &wma->interfaces[vdev_id];
wma_set_bss_rate_flags(iface, add_bss);
if (add_bss->operMode) {
/* Save parameters later needed by WMA_ADD_STA_REQ */
if (iface->addBssStaContext)
qdf_mem_free(iface->addBssStaContext);
iface->addBssStaContext = qdf_mem_malloc(sizeof(tAddStaParams));
if (!iface->addBssStaContext) {
WMA_LOGE("%s Failed to allocat memory", __func__);
goto send_fail_resp;
}
qdf_mem_copy(iface->addBssStaContext, &add_bss->staContext,
sizeof(tAddStaParams));
if (iface->staKeyParams) {
qdf_mem_free(iface->staKeyParams);
iface->staKeyParams = NULL;
}
if (add_bss->extSetStaKeyParamValid) {
iface->staKeyParams =
qdf_mem_malloc(sizeof(tSetStaKeyParams));
if (!iface->staKeyParams) {
WMA_LOGE("%s Failed to allocat memory",
__func__);
goto send_fail_resp;
}
qdf_mem_copy(iface->staKeyParams,
&add_bss->extSetStaKeyParam,
sizeof(tSetStaKeyParams));
}
/* Save parameters later needed by WMA_ADD_STA_REQ */
iface->rmfEnabled = add_bss->rmfEnabled;
iface->beaconInterval = add_bss->beaconInterval;
iface->llbCoexist = add_bss->llbCoexist;
iface->shortSlotTimeSupported = add_bss->shortSlotTimeSupported;
iface->nwType = add_bss->nwType;
if (add_bss->nonRoamReassoc) {
peer = ol_txrx_find_peer_by_addr(pdev, add_bss->bssId,
&peer_id);
if (peer) {
add_bss->staContext.staIdx =
ol_txrx_local_peer_id(peer);
goto send_bss_resp;
}
}
if (add_bss->reassocReq) {
#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || defined(QCA_LL_TX_FLOW_CONTROL_V2)
ol_txrx_vdev_handle vdev;
#endif
/* Called in preassoc state. BSSID peer is already
* added by set_linkstate
*/
peer = ol_txrx_find_peer_by_addr(pdev, add_bss->bssId,
&peer_id);
if (!peer) {
WMA_LOGE("%s Failed to find peer %pM", __func__,
add_bss->bssId);
goto send_fail_resp;
}
if (wma_is_roam_synch_in_progress(wma, vdev_id)) {
add_bss->staContext.staIdx =
ol_txrx_local_peer_id(peer);
WMA_LOGD("LFR3:%s: bssid %pM staIdx %d",
__func__, add_bss->bssId,
add_bss->staContext.staIdx);
return;
}
msg = wma_fill_vdev_req(wma, vdev_id, WMA_ADD_BSS_REQ,
WMA_TARGET_REQ_TYPE_VDEV_START,
add_bss,
WMA_VDEV_START_REQUEST_TIMEOUT);
if (!msg) {
WMA_LOGE("%s Failed to allocate vdev request vdev_id %d",
__func__, vdev_id);
goto peer_cleanup;
}
add_bss->staContext.staIdx =
ol_txrx_local_peer_id(peer);
qdf_mem_zero(&req, sizeof(req));
req.vdev_id = vdev_id;
req.chan = add_bss->currentOperChannel;
req.chan_width = add_bss->ch_width;
if (add_bss->ch_width == CH_WIDTH_10MHZ)
req.is_half_rate = 1;
else if (add_bss->ch_width == CH_WIDTH_5MHZ)
req.is_quarter_rate = 1;
req.ch_center_freq_seg0 = add_bss->ch_center_freq_seg0;
req.ch_center_freq_seg1 = add_bss->ch_center_freq_seg1;
req.max_txpow = add_bss->maxTxPower;
req.beacon_intval = add_bss->beaconInterval;
req.dtim_period = add_bss->dtimPeriod;
req.hidden_ssid = add_bss->bHiddenSSIDEn;
req.is_dfs = add_bss->bSpectrumMgtEnabled;
req.ssid.length = add_bss->ssId.length;
req.oper_mode = BSS_OPERATIONAL_MODE_STA;
if (req.ssid.length > 0)
qdf_mem_copy(req.ssid.ssId, add_bss->ssId.ssId,
add_bss->ssId.length);
status = wma_get_current_hw_mode(&hw_mode);
if (!QDF_IS_STATUS_SUCCESS(status))
WMA_LOGE("wma_get_current_hw_mode failed");
if (add_bss->nss == 2) {
req.preferred_rx_streams = 2;
req.preferred_tx_streams = 2;
} else {
req.preferred_rx_streams = 1;
req.preferred_tx_streams = 1;
}
status = wma_vdev_start(wma, &req, false);
if (status != QDF_STATUS_SUCCESS) {
wma_remove_vdev_req(wma, vdev_id,
WMA_TARGET_REQ_TYPE_VDEV_START);
goto peer_cleanup;
}
#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || defined(QCA_LL_TX_FLOW_CONTROL_V2)
vdev = wma_find_vdev_by_id(wma, vdev_id);
if (!vdev) {
WMA_LOGE("%s Invalid txrx vdev", __func__);
goto peer_cleanup;
}
ol_txrx_vdev_pause(vdev,
OL_TXQ_PAUSE_REASON_PEER_UNAUTHORIZED);
#endif
/* ADD_BSS_RESP will be deferred to completion of
* VDEV_START
*/
return;
}
if (!add_bss->updateBss)
goto send_bss_resp;
/* Update peer state */
if (add_bss->staContext.encryptType == eSIR_ED_NONE) {
WMA_LOGD("%s: Update peer(%pM) state into auth",
__func__, add_bss->bssId);
ol_txrx_peer_state_update(pdev, add_bss->bssId,
OL_TXRX_PEER_STATE_AUTH);
} else {
#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || defined(QCA_LL_TX_FLOW_CONTROL_V2)
ol_txrx_vdev_handle vdev;
#endif
WMA_LOGD("%s: Update peer(%pM) state into conn",
__func__, add_bss->bssId);
ol_txrx_peer_state_update(pdev, add_bss->bssId,
OL_TXRX_PEER_STATE_CONN);
#if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || defined(QCA_LL_TX_FLOW_CONTROL_V2)
peer = ol_txrx_find_peer_by_addr(pdev, add_bss->bssId,
&peer_id);
if (!peer) {
WMA_LOGE("%s:%d Failed to find peer %pM",
__func__, __LINE__, add_bss->bssId);
goto send_fail_resp;
}
vdev = wma_find_vdev_by_id(wma, vdev_id);
if (!vdev) {
WMA_LOGE("%s Invalid txrx vdev", __func__);
goto peer_cleanup;
}
ol_txrx_vdev_pause(vdev,
OL_TXQ_PAUSE_REASON_PEER_UNAUTHORIZED);
#endif
}
wmi_unified_send_txbf(wma, &add_bss->staContext);
pps_val = ((pMac->enable5gEBT << 31) & 0xffff0000) |
(PKT_PWR_SAVE_5G_EBT & 0xffff);
status = wma_vdev_set_param(wma->wmi_handle, vdev_id,
WMI_VDEV_PARAM_PACKET_POWERSAVE,
pps_val);
if (QDF_IS_STATUS_ERROR(status))
WMA_LOGE("Failed to send wmi packet power save cmd");
else
WMA_LOGD("Sent PKT_PWR_SAVE_5G_EBT cmd to target, val = %x, status = %d",
pps_val, status);
wma_send_peer_assoc(wma, add_bss->nwType,
&add_bss->staContext);
/* we just had peer assoc, so install key will be done later */
iface->is_waiting_for_key = true;
peer_assoc_sent = true;
if (add_bss->rmfEnabled)
wma_set_mgmt_frame_protection(wma);
wma_vdev_set_bss_params(wma, add_bss->staContext.smesessionId,
add_bss->beaconInterval,
add_bss->dtimPeriod,
add_bss->shortSlotTimeSupported,
add_bss->llbCoexist,
add_bss->maxTxPower);
/*
* Store the bssid in interface table, bssid will
* be used during group key setting sta mode.
*/
qdf_mem_copy(iface->bssid, add_bss->bssId, IEEE80211_ADDR_LEN);
}
send_bss_resp:
if (NULL == ol_txrx_find_peer_by_addr(pdev, add_bss->bssId,
&add_bss->staContext.staIdx))
add_bss->status = QDF_STATUS_E_FAILURE;
else
add_bss->status = QDF_STATUS_SUCCESS;
add_bss->bssIdx = add_bss->staContext.smesessionId;
qdf_mem_copy(add_bss->staContext.staMac, add_bss->bssId,
sizeof(add_bss->staContext.staMac));
if (!WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap,
WMI_SERVICE_PEER_ASSOC_CONF)) {
WMA_LOGE(FL("WMI_SERVICE_PEER_ASSOC_CONF not enabled"));
goto send_final_rsp;
}
/* In case of reassoc, peer assoc cmd will not be sent */
if (!peer_assoc_sent)
goto send_final_rsp;
msg = wma_fill_hold_req(wma, vdev_id, WMA_ADD_BSS_REQ,
WMA_PEER_ASSOC_CNF_START, add_bss,
WMA_PEER_ASSOC_TIMEOUT);
if (!msg) {
WMA_LOGE(FL("Failed to allocate request for vdev_id %d"),
vdev_id);
wma_remove_req(wma, vdev_id, WMA_PEER_ASSOC_CNF_START);
goto peer_cleanup;
}
return;
send_final_rsp:
WMA_LOGD("%s: opermode %d update_bss %d nw_type %d bssid %pM staIdx %d status %d",
__func__, add_bss->operMode,
add_bss->updateBss, add_bss->nwType, add_bss->bssId,
add_bss->staContext.staIdx, add_bss->status);
wma_send_msg_high_priority(wma, WMA_ADD_BSS_RSP, (void *)add_bss, 0);
return;
peer_cleanup:
wma_remove_peer(wma, add_bss->bssId, vdev_id, peer,
roam_synch_in_progress);
send_fail_resp:
add_bss->status = QDF_STATUS_E_FAILURE;
if (!wma_is_roam_synch_in_progress(wma, vdev_id))
wma_send_msg_high_priority(wma,
WMA_ADD_BSS_RSP, (void *)add_bss, 0);
}
/**
* wma_add_bss() - Add BSS request to fw as per opmode
* @wma: wma handle
* @params: add bss params
*
* Return: none
*/
void wma_add_bss(tp_wma_handle wma, tpAddBssParams params)
{
WMA_LOGD("%s: add_bss_param.halPersona = %d",
__func__, params->halPersona);
switch (params->halPersona) {
case QDF_SAP_MODE:
case QDF_P2P_GO_MODE:
/*If current bring up SAP/P2P channel matches the previous
*radar found channel then reset the last_radar_found_chan
*variable to avoid race conditions.
*/
if (params->currentOperChannel ==
wma->dfs_ic->last_radar_found_chan)
wma->dfs_ic->last_radar_found_chan = 0;
wma_add_bss_ap_mode(wma, params);
break;
#ifdef QCA_IBSS_SUPPORT
case QDF_IBSS_MODE:
wma_add_bss_ibss_mode(wma, params);
break;
#endif
case QDF_NDI_MODE:
wma_add_bss_ndi_mode(wma, params);
break;
default:
wma_add_bss_sta_mode(wma, params);
break;
}
}
/**
* wma_add_sta_req_ap_mode() - process add sta request in ap mode
* @wma: wma handle
* @add_sta: add sta params
*
* Return: none
*/
static void wma_add_sta_req_ap_mode(tp_wma_handle wma, tpAddStaParams add_sta)
{
enum ol_txrx_peer_state state = OL_TXRX_PEER_STATE_CONN;
ol_txrx_pdev_handle pdev;
ol_txrx_vdev_handle vdev;
ol_txrx_peer_handle peer;
uint8_t peer_id;
QDF_STATUS status;
int32_t ret;
struct wma_txrx_node *iface = NULL;
struct wma_target_req *msg;
bool peer_assoc_cnf = false;
uint32_t mcs_limit, i, j;
uint8_t *rate_pos;
pdev = cds_get_context(QDF_MODULE_ID_TXRX);
if (NULL == pdev) {
WMA_LOGE("%s: Failed to find pdev", __func__);
add_sta->status = QDF_STATUS_E_FAILURE;
goto send_rsp;
}
/* UMAC sends WMA_ADD_STA_REQ msg twice to WMA when the station
* associates. First WMA_ADD_STA_REQ will have staType as
* STA_ENTRY_PEER and second posting will have STA_ENTRY_SELF.
* Peer creation is done in first WMA_ADD_STA_REQ and second
* WMA_ADD_STA_REQ which has STA_ENTRY_SELF is ignored and
* send fake response with success to UMAC. Otherwise UMAC
* will get blocked.
*/
if (add_sta->staType != STA_ENTRY_PEER) {
add_sta->status = QDF_STATUS_SUCCESS;
goto send_rsp;
}
vdev = wma_find_vdev_by_id(wma, add_sta->smesessionId);
if (!vdev) {
WMA_LOGE("%s: Failed to find vdev", __func__);
add_sta->status = QDF_STATUS_E_FAILURE;
goto send_rsp;
}
iface = &wma->interfaces[add_sta->smesessionId];
peer = ol_txrx_find_peer_by_addr_and_vdev(pdev,
vdev,
add_sta->staMac, &peer_id);
if (peer) {
wma_remove_peer(wma, add_sta->staMac, add_sta->smesessionId,
peer, false);
WMA_LOGE("%s: Peer already exists, Deleted peer with peer_addr %pM",
__func__, add_sta->staMac);
}
/* The code above only checks the peer existence on its own vdev.
* Need to check whether the peer exists on other vDevs because firmware
* can't create the peer if the peer with same MAC address already
* exists on the pDev. As this peer belongs to other vDevs, just return
* here.
*/
peer = ol_txrx_find_peer_by_addr(pdev, add_sta->staMac, &peer_id);
if (peer) {
WMA_LOGE("%s: My vdev:%d, but Peer exists on other vdev with peer_addr %pM and peer_id %d",
__func__, vdev->vdev_id, add_sta->staMac, peer_id);
add_sta->status = QDF_STATUS_E_FAILURE;
goto send_rsp;
}
status = wma_create_peer(wma, pdev, vdev, add_sta->staMac,
WMI_PEER_TYPE_DEFAULT, add_sta->smesessionId,
false);
if (status != QDF_STATUS_SUCCESS) {
WMA_LOGE("%s: Failed to create peer for %pM",
__func__, add_sta->staMac);
add_sta->status = status;
goto send_rsp;
}
peer = ol_txrx_find_peer_by_addr_and_vdev(pdev,
vdev,
add_sta->staMac, &peer_id);
if (!peer) {
WMA_LOGE("%s: Failed to find peer handle using peer mac %pM",
__func__, add_sta->staMac);
add_sta->status = QDF_STATUS_E_FAILURE;
wma_remove_peer(wma, add_sta->staMac, add_sta->smesessionId,
peer, false);
goto send_rsp;
}
wmi_unified_send_txbf(wma, add_sta);
/*
* Get MCS limit from ini configure, and map it to rate parameters
* This will limit HT rate upper bound. CFG_CTRL_MASK is used to
* check whether ini config is enabled and CFG_DATA_MASK to get the
* MCS value.
*/
#define CFG_CTRL_MASK 0xFF00
#define CFG_DATA_MASK 0x00FF
if (wlan_cfg_get_int(wma->mac_context, WNI_CFG_SAP_MAX_MCS_DATA,
&mcs_limit) != eSIR_SUCCESS) {
mcs_limit = WNI_CFG_SAP_MAX_MCS_DATA_STADEF;
}
if (mcs_limit & CFG_CTRL_MASK) {
WMA_LOGD("%s: set mcs_limit %x", __func__, mcs_limit);
mcs_limit &= CFG_DATA_MASK;
rate_pos = (u_int8_t *)add_sta->supportedRates.supportedMCSSet;
for (i = 0, j = 0; i < MAX_SUPPORTED_RATES;) {
if (j < mcs_limit / 8) {
rate_pos[j] = 0xff;
j++;
i += 8;
} else if (j < mcs_limit / 8 + 1) {
if (i <= mcs_limit)
rate_pos[i / 8] |= 1 << (i % 8);
else
rate_pos[i / 8] &= ~(1 << (i % 8));
i++;
if (i >= (j + 1) * 8)
j++;
} else {
rate_pos[j++] = 0;
i += 8;
}
}
}
if (WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap,
WMI_SERVICE_PEER_ASSOC_CONF)) {
peer_assoc_cnf = true;
msg = wma_fill_hold_req(wma, add_sta->smesessionId,
WMA_ADD_STA_REQ, WMA_PEER_ASSOC_CNF_START,
add_sta, WMA_PEER_ASSOC_TIMEOUT);
if (!msg) {
WMA_LOGE(FL("Failed to alloc request for vdev_id %d"),
add_sta->smesessionId);
add_sta->status = QDF_STATUS_E_FAILURE;
wma_remove_req(wma, add_sta->smesessionId,
WMA_PEER_ASSOC_CNF_START);
wma_remove_peer(wma, add_sta->staMac,
add_sta->smesessionId, peer, false);
peer_assoc_cnf = false;
goto send_rsp;
}
} else {
WMA_LOGE(FL("WMI_SERVICE_PEER_ASSOC_CONF not enabled"));
}
ret = wma_send_peer_assoc(wma, add_sta->nwType, add_sta);
if (ret) {
add_sta->status = QDF_STATUS_E_FAILURE;
wma_remove_peer(wma, add_sta->staMac, add_sta->smesessionId,
peer, false);
goto send_rsp;
}
#ifdef QCA_IBSS_SUPPORT
/*
* In IBSS mode send the peer
* Atim Window length if IBSS
* power save is enabled by the
* firmware.
*/
if (wma_is_vdev_in_ibss_mode(wma, add_sta->smesessionId) &&
WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap,
WMI_SERVICE_IBSS_PWRSAVE)) {
/*
* If ATIM Window is present in the peer
* beacon then send it to firmware else
* configure Zero ATIM Window length to
* firmware.
*/
if (add_sta->atimIePresent) {
wma_set_peer_param(wma, add_sta->staMac,
WMI_PEER_IBSS_ATIM_WINDOW_LENGTH,
add_sta->peerAtimWindowLength,
add_sta->smesessionId);
} else {
wma_set_peer_param(wma, add_sta->staMac,
WMI_PEER_IBSS_ATIM_WINDOW_LENGTH,
0, add_sta->smesessionId);
}
}
#endif
iface->rmfEnabled = add_sta->rmfEnabled;
if (add_sta->rmfEnabled)
wma_set_mgmt_frame_protection(wma);
if (add_sta->uAPSD) {
status = wma_set_ap_peer_uapsd(wma, add_sta->smesessionId,
add_sta->staMac,
add_sta->uAPSD, add_sta->maxSPLen);
if (QDF_IS_STATUS_ERROR(status)) {
WMA_LOGE("Failed to set peer uapsd param for %pM",
add_sta->staMac);
add_sta->status = QDF_STATUS_E_FAILURE;
wma_remove_peer(wma, add_sta->staMac,
add_sta->smesessionId, peer, false);
goto send_rsp;
}
}
WMA_LOGD("%s: Moving peer %pM to state %d",
__func__, add_sta->staMac, state);
ol_txrx_peer_state_update(pdev, add_sta->staMac, state);
add_sta->staIdx = ol_txrx_local_peer_id(peer);
add_sta->nss = iface->nss;
add_sta->status = QDF_STATUS_SUCCESS;
send_rsp:
/* Do not send add stat resp when peer assoc cnf is enabled */
if (peer_assoc_cnf) {
WMA_LOGD(FL("WMI_SERVICE_PEER_ASSOC_CONF is enabled"));
return;
}
WMA_LOGD(FL("statype %d vdev_id %d aid %d bssid %pM staIdx %d status %d"),
add_sta->staType, add_sta->smesessionId,
add_sta->assocId, add_sta->bssId, add_sta->staIdx,
add_sta->status);
wma_send_msg_high_priority(wma, WMA_ADD_STA_RSP, (void *)add_sta, 0);
}
#ifdef FEATURE_WLAN_TDLS
/**
* wma_add_tdls_sta() - process add sta request in TDLS mode
* @wma: wma handle
* @add_sta: add sta params
*
* Return: none
*/
static void wma_add_tdls_sta(tp_wma_handle wma, tpAddStaParams add_sta)
{
ol_txrx_pdev_handle pdev;
ol_txrx_vdev_handle vdev;
ol_txrx_peer_handle peer;
uint8_t peer_id;
QDF_STATUS status;
int32_t ret;
tTdlsPeerStateParams *peerStateParams;
struct wma_target_req *msg;
bool peer_assoc_cnf = false;
WMA_LOGD("%s: staType: %d, staIdx: %d, updateSta: %d, bssId: %pM, staMac: %pM",
__func__, add_sta->staType, add_sta->staIdx,
add_sta->updateSta, add_sta->bssId, add_sta->staMac);
pdev = cds_get_context(QDF_MODULE_ID_TXRX);
if (NULL == pdev) {
WMA_LOGE("%s: Failed to find pdev", __func__);
add_sta->status = QDF_STATUS_E_FAILURE;
goto send_rsp;
}
vdev = wma_find_vdev_by_id(wma, add_sta->smesessionId);
if (!vdev) {
WMA_LOGE("%s: Failed to find vdev", __func__);
add_sta->status = QDF_STATUS_E_FAILURE;
goto send_rsp;
}
if (wma_is_roam_synch_in_progress(wma, add_sta->smesessionId)) {
WMA_LOGE("%s: roaming in progress, reject add sta!", __func__);
add_sta->status = QDF_STATUS_E_PERM;
goto send_rsp;
}
if (0 == add_sta->updateSta) {
/* its a add sta request * */
ol_txrx_copy_mac_addr_raw(vdev, add_sta->bssId);
WMA_LOGD("%s: addSta, calling wma_create_peer for %pM, vdev_id %hu",
__func__, add_sta->staMac, add_sta->smesessionId);
status = wma_create_peer(wma, pdev, vdev, add_sta->staMac,
WMI_PEER_TYPE_TDLS,
add_sta->smesessionId, false);
if (status != QDF_STATUS_SUCCESS) {
WMA_LOGE("%s: Failed to create peer for %pM",
__func__, add_sta->staMac);
add_sta->status = status;
goto send_rsp;
}
peer = ol_txrx_find_peer_by_addr(pdev, add_sta->staMac, &peer_id);
if (!peer) {
WMA_LOGE("%s: addSta, failed to find peer handle for mac %pM",
__func__, add_sta->staMac);
add_sta->status = QDF_STATUS_E_FAILURE;
wma_remove_peer(wma, add_sta->staMac,
add_sta->smesessionId, peer, false);
ol_txrx_add_last_real_peer(pdev, vdev, &peer_id);
goto send_rsp;
}
add_sta->staIdx = ol_txrx_local_peer_id(peer);
WMA_LOGD("%s: addSta, after calling ol_txrx_local_peer_id, staIdx: %d, staMac: %pM",
__func__, add_sta->staIdx, add_sta->staMac);
peerStateParams = qdf_mem_malloc(sizeof(tTdlsPeerStateParams));
if (!peerStateParams) {
WMA_LOGE
("%s: Failed to allocate memory for peerStateParams for %pM",
__func__, add_sta->staMac);
add_sta->status = QDF_STATUS_E_NOMEM;
goto send_rsp;
}
peerStateParams->peerState = WMI_TDLS_PEER_STATE_PEERING;
peerStateParams->vdevId = add_sta->smesessionId;
qdf_mem_copy(&peerStateParams->peerMacAddr,
&add_sta->staMac, sizeof(tSirMacAddr));
wma_update_tdls_peer_state(wma, peerStateParams);
} else {
/* its a change sta request * */
peer =
ol_txrx_find_peer_by_addr(pdev, add_sta->staMac, &peer_id);
if (!peer) {
WMA_LOGE("%s: changeSta,failed to find peer handle for mac %pM",
__func__, add_sta->staMac);
add_sta->status = QDF_STATUS_E_FAILURE;
wma_remove_peer(wma, add_sta->staMac,
add_sta->smesessionId, peer, false);
ol_txrx_add_last_real_peer(pdev, vdev, &peer_id);
goto send_rsp;
}
if (WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap,
WMI_SERVICE_PEER_ASSOC_CONF)) {
WMA_LOGE(FL("WMI_SERVICE_PEER_ASSOC_CONF is enabled"));
peer_assoc_cnf = true;
msg = wma_fill_hold_req(wma, add_sta->smesessionId,
WMA_ADD_STA_REQ, WMA_PEER_ASSOC_CNF_START,
add_sta, WMA_PEER_ASSOC_TIMEOUT);
if (!msg) {
WMA_LOGE(FL("Failed to alloc request for vdev_id %d"),
add_sta->smesessionId);
add_sta->status = QDF_STATUS_E_FAILURE;
wma_remove_req(wma, add_sta->smesessionId,
WMA_PEER_ASSOC_CNF_START);
wma_remove_peer(wma, add_sta->staMac,
add_sta->smesessionId, peer, false);
peer_assoc_cnf = false;
goto send_rsp;
}
} else {
WMA_LOGE(FL("WMI_SERVICE_PEER_ASSOC_CONF not enabled"));
}
WMA_LOGD("%s: changeSta, calling wma_send_peer_assoc",
__func__);
ret =
wma_send_peer_assoc(wma, add_sta->nwType, add_sta);
if (ret) {
add_sta->status = QDF_STATUS_E_FAILURE;
wma_remove_peer(wma, add_sta->staMac,
add_sta->smesessionId, peer, false);
ol_txrx_add_last_real_peer(pdev, vdev, &peer_id);
goto send_rsp;
}
}
send_rsp:
/* Do not send add stat resp when peer assoc cnf is enabled */
if (peer_assoc_cnf)
return;
WMA_LOGD(FL("statype %d vdev_id %d aid %d bssid %pM staIdx %d status %d"),
add_sta->staType, add_sta->smesessionId,
add_sta->assocId, add_sta->bssId, add_sta->staIdx,
add_sta->status);
wma_send_msg_high_priority(wma, WMA_ADD_STA_RSP, (void *)add_sta, 0);
}
#endif
/**
* wma_add_sta_req_sta_mode() - process add sta request in sta mode
* @wma: wma handle
* @add_sta: add sta params
*
* Return: none
*/
static void wma_add_sta_req_sta_mode(tp_wma_handle wma, tpAddStaParams params)
{
ol_txrx_pdev_handle pdev;
QDF_STATUS status = QDF_STATUS_SUCCESS;
ol_txrx_peer_handle peer;
struct wma_txrx_node *iface;
int8_t maxTxPower;
int ret = 0;
struct wma_target_req *msg;
bool peer_assoc_cnf = false;
struct vdev_up_params param = {0};
int smps_param;
#ifdef FEATURE_WLAN_TDLS
if (STA_ENTRY_TDLS_PEER == params->staType) {
wma_add_tdls_sta(wma, params);
return;
}
#endif
pdev = cds_get_context(QDF_MODULE_ID_TXRX);
if (NULL == pdev) {
WMA_LOGE("%s: Unable to get pdev", __func__);
goto out;
}
iface = &wma->interfaces[params->smesessionId];
if (params->staType != STA_ENTRY_SELF) {
WMA_LOGE("%s: unsupported station type %d",
__func__, params->staType);
goto out;
}
peer = ol_txrx_find_peer_by_addr(pdev, params->bssId, &params->staIdx);
if (peer == NULL) {
WMA_LOGE("%s: Peer is not present vdev id %d for %pM", __func__,
params->smesessionId, params->bssId);
status = QDF_STATUS_E_FAILURE;
goto out;
}
if (params->nonRoamReassoc) {
ol_txrx_peer_state_update(pdev, params->bssId,
OL_TXRX_PEER_STATE_AUTH);
qdf_atomic_set(&iface->bss_status, WMA_BSS_STATUS_STARTED);
iface->aid = params->assocId;
goto out;
}
if (wma->interfaces[params->smesessionId].vdev_up == true) {
WMA_LOGE("%s: vdev id %d is already UP for %pM", __func__,
params->smesessionId, params->bssId);
status = QDF_STATUS_E_FAILURE;
goto out;
}
if (peer != NULL &&
(ol_txrx_get_peer_state(peer) == OL_TXRX_PEER_STATE_DISC)) {
/*
* This is the case for reassociation.
* peer state update and peer_assoc is required since it
* was not done by WMA_ADD_BSS_REQ.
*/
/* Update peer state */
if (params->encryptType == eSIR_ED_NONE) {
WMA_LOGD("%s: Update peer(%pM) state into auth",
__func__, params->bssId);
ol_txrx_peer_state_update(pdev, params->bssId,
OL_TXRX_PEER_STATE_AUTH);
} else {
WMA_LOGD("%s: Update peer(%pM) state into conn",
__func__, params->bssId);
ol_txrx_peer_state_update(pdev, params->bssId,
OL_TXRX_PEER_STATE_CONN);
}
if (wma_is_roam_synch_in_progress(wma, params->smesessionId)) {
/* iface->nss = params->nss; */
/*In LFR2.0, the following operations are performed as
* part of wma_send_peer_assoc. As we are
* skipping this operation, we are just executing the
* following which are useful for LFR3.0.
*/
ol_txrx_peer_state_update(pdev, params->bssId,
OL_TXRX_PEER_STATE_AUTH);
qdf_atomic_set(&iface->bss_status,
WMA_BSS_STATUS_STARTED);
iface->aid = params->assocId;
WMA_LOGD("LFR3:statype %d vdev %d aid %d bssid %pM",
params->staType, params->smesessionId,
params->assocId, params->bssId);
return;
}
wmi_unified_send_txbf(wma, params);
if (WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap,
WMI_SERVICE_PEER_ASSOC_CONF)) {
WMA_LOGD(FL("WMI_SERVICE_PEER_ASSOC_CONF is enabled"));
peer_assoc_cnf = true;
msg = wma_fill_hold_req(wma, params->smesessionId,
WMA_ADD_STA_REQ, WMA_PEER_ASSOC_CNF_START,
params, WMA_PEER_ASSOC_TIMEOUT);
if (!msg) {
WMA_LOGD(FL("Failed to alloc request for vdev_id %d"),
params->smesessionId);
params->status = QDF_STATUS_E_FAILURE;
wma_remove_req(wma, params->smesessionId,
WMA_PEER_ASSOC_CNF_START);
wma_remove_peer(wma, params->staMac,
params->smesessionId, peer, false);
peer_assoc_cnf = false;
goto out;
}
} else {
WMA_LOGD(FL("WMI_SERVICE_PEER_ASSOC_CONF not enabled"));
}
ret = wma_send_peer_assoc(wma,
iface->nwType,
(tAddStaParams *) iface->addBssStaContext);
if (ret) {
status = QDF_STATUS_E_FAILURE;
wma_remove_peer(wma, params->bssId,
params->smesessionId, peer, false);
goto out;
}
if (params->rmfEnabled)
wma_set_mgmt_frame_protection(wma);
/*
* Set the PTK in 11r mode because we already have it.
*/
if (iface->staKeyParams) {
wma_set_stakey(wma,
(tpSetStaKeyParams) iface->staKeyParams);
}
}
maxTxPower = params->maxTxPower;
wma_vdev_set_bss_params(wma, params->smesessionId,
iface->beaconInterval, iface->dtimPeriod,
iface->shortSlotTimeSupported,
iface->llbCoexist, maxTxPower);
params->csaOffloadEnable = 0;
if (WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap,
WMI_SERVICE_CSA_OFFLOAD)) {
params->csaOffloadEnable = 1;
if (wma_unified_csa_offload_enable(wma, params->smesessionId) <
0) {
WMA_LOGE("Unable to enable CSA offload for vdev_id:%d",
params->smesessionId);
}
}
if (WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap,
WMI_SERVICE_FILTER_IPSEC_NATKEEPALIVE)) {
if (wmi_unified_nat_keepalive_enable(wma, params->smesessionId)
< 0) {
WMA_LOGE("Unable to enable NAT keepalive for vdev_id:%d",
params->smesessionId);
}
}
param.vdev_id = params->smesessionId;
param.assoc_id = params->assocId;
if (wma_send_vdev_up_to_fw(wma, &param, params->bssId) !=
QDF_STATUS_SUCCESS) {
WMA_LOGE("%s: Failed to send vdev up cmd: vdev %d bssid %pM",
__func__, params->smesessionId, params->bssId);
cds_set_do_hw_mode_change_flag(false);
status = QDF_STATUS_E_FAILURE;
} else {
wma->interfaces[params->smesessionId].vdev_up = true;
WMA_LOGD(FL("Setting vdev_up flag to true"));
wma_set_vdev_mgmt_rate(wma, params->smesessionId);
}
qdf_atomic_set(&iface->bss_status, WMA_BSS_STATUS_STARTED);
WMA_LOGD("%s: STA mode (type %d subtype %d) BSS is started",
__func__, iface->type, iface->sub_type);
/* Sta is now associated, configure various params */
/* Send SMPS force command to FW to send the required
* action frame only when SM power save is enbaled in
* from INI. In case dynamic antenna selection, the
* action frames are sent by the chain mask manager
* In addition to the action frames, The SM power save is
* published in the assoc request HT SMPS IE for both cases.
*/
if ((params->enableHtSmps) && (params->send_smps_action)) {
smps_param = wma_smps_mode_to_force_mode_param(
params->htSmpsconfig);
if (smps_param >= 0) {
WMA_LOGD("%s: Send SMPS force mode: %d",
__func__, params->htSmpsconfig);
wma_set_mimops(wma, params->smesessionId,
smps_param);
}
}
/* Partial AID match power save, enable when SU bformee */
if (params->enableVhtpAid && params->vhtTxBFCapable)
wma_set_ppsconfig(params->smesessionId,
WMA_VHT_PPS_PAID_MATCH, 1);
/* Enable AMPDU power save, if htCapable/vhtCapable */
if (params->enableAmpduPs && (params->htCapable || params->vhtCapable))
wma_set_ppsconfig(params->smesessionId,
WMA_VHT_PPS_DELIM_CRC_FAIL, 1);
if (WMI_SERVICE_EXT_IS_ENABLED(wma->wmi_service_bitmap,
wma->wmi_service_ext_bitmap,
WMI_SERVICE_LISTEN_INTERVAL_OFFLOAD_SUPPORT)) {
WMA_LOGD("%s: listen interval offload enabled, setting params",
__func__);
status = wma_vdev_set_param(wma->wmi_handle,
params->smesessionId,
WMI_VDEV_PARAM_MAX_LI_OF_MODDTIM,
wma->staMaxLIModDtim);
if (status != QDF_STATUS_SUCCESS) {
WMA_LOGE(FL("can't set MAX_LI for session: %d"),
params->smesessionId);
}
status = wma_vdev_set_param(wma->wmi_handle,
params->smesessionId,
WMI_VDEV_PARAM_DYNDTIM_CNT,
wma->staDynamicDtim);
if (status != QDF_STATUS_SUCCESS) {
WMA_LOGE(FL("can't set DYNDTIM_CNT for session: %d"),
params->smesessionId);
}
status = wma_vdev_set_param(wma->wmi_handle,
params->smesessionId,
WMI_VDEV_PARAM_MODDTIM_CNT,
wma->staModDtim);
if (status != QDF_STATUS_SUCCESS) {
WMA_LOGE(FL("can't set DTIM_CNT for session: %d"),
params->smesessionId);
}
} else {
WMA_LOGD("%s: listen interval offload is not set",
__func__);
}
iface->aid = params->assocId;
params->nss = iface->nss;
out:
/* Do not send add stat resp when peer assoc cnf is enabled */
if (peer_assoc_cnf)
return;
params->status = status;
WMA_LOGD(FL("statype %d vdev_id %d aid %d bssid %pM staIdx %d status %d"),
params->staType, params->smesessionId,
params->assocId, params->bssId, params->staIdx,
params->status);
/* Don't send a response during roam sync operation */
if (!wma_is_roam_synch_in_progress(wma, params->smesessionId))
wma_send_msg_high_priority(wma,
WMA_ADD_STA_RSP, (void *)params, 0);
}
/**
* wma_delete_sta_req_ap_mode() - proces delete sta request from UMAC in AP mode
* @wma: wma handle
* @del_sta: delete sta params
*
* Return: none
*/
static void wma_delete_sta_req_ap_mode(tp_wma_handle wma,
tpDeleteStaParams del_sta)
{
ol_txrx_pdev_handle pdev;
ol_txrx_peer_handle peer;
struct wma_target_req *msg;
uint8_t *peer_mac_addr;
pdev = cds_get_context(QDF_MODULE_ID_TXRX);
if (NULL == pdev) {
WMA_LOGE("%s: Failed to get pdev", __func__);
del_sta->status = QDF_STATUS_E_FAILURE;
goto send_del_rsp;
}
peer = ol_txrx_peer_find_by_local_id(pdev, del_sta->staIdx);
if (!peer) {
WMA_LOGE("%s: Failed to get peer handle using peer id %d",
__func__, del_sta->staIdx);
del_sta->status = QDF_STATUS_E_FAILURE;
goto send_del_rsp;
}
peer_mac_addr = ol_txrx_peer_get_peer_mac_addr(peer);
wma_remove_peer(wma, peer_mac_addr, del_sta->smesessionId, peer,
false);
del_sta->status = QDF_STATUS_SUCCESS;
if (WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap,
WMI_SERVICE_SYNC_DELETE_CMDS)) {
msg = wma_fill_hold_req(wma, del_sta->smesessionId,
WMA_DELETE_STA_REQ,
WMA_DELETE_STA_RSP_START, del_sta,
WMA_DELETE_STA_TIMEOUT);
if (!msg) {
WMA_LOGE(FL("Failed to allocate request. vdev_id %d"),
del_sta->smesessionId);
wma_remove_req(wma, del_sta->smesessionId,
WMA_DELETE_STA_RSP_START);
del_sta->status = QDF_STATUS_E_NOMEM;
goto send_del_rsp;
}
wma_acquire_wakelock(&wma->wmi_cmd_rsp_wake_lock,
WMA_FW_RSP_EVENT_WAKE_LOCK_DURATION);
return;
}
send_del_rsp:
if (del_sta->respReqd) {
WMA_LOGD("%s: Sending del rsp to umac (status: %d)",
__func__, del_sta->status);
wma_send_msg_high_priority(wma,
WMA_DELETE_STA_RSP, (void *)del_sta, 0);
}
}
#ifdef FEATURE_WLAN_TDLS
/**
* wma_del_tdls_sta() - proces delete sta request from UMAC in TDLS
* @wma: wma handle
* @del_sta: delete sta params
*
* Return: none
*/
static void wma_del_tdls_sta(tp_wma_handle wma, tpDeleteStaParams del_sta)
{
tTdlsPeerStateParams *peerStateParams;
struct wma_target_req *msg;
int status;
peerStateParams = qdf_mem_malloc(sizeof(tTdlsPeerStateParams));
if (!peerStateParams) {
WMA_LOGE("%s: Failed to allocate memory for peerStateParams for: %pM",
__func__, del_sta->staMac);
del_sta->status = QDF_STATUS_E_NOMEM;
goto send_del_rsp;
}
if (wma_is_roam_synch_in_progress(wma, del_sta->smesessionId)) {
WMA_LOGE("%s: roaming in progress, reject del sta!", __func__);
del_sta->status = QDF_STATUS_E_PERM;
goto send_del_rsp;
}
peerStateParams->peerState = WMA_TDLS_PEER_STATE_TEARDOWN;
peerStateParams->vdevId = del_sta->smesessionId;
peerStateParams->resp_reqd = del_sta->respReqd;
qdf_mem_copy(&peerStateParams->peerMacAddr,
&del_sta->staMac, sizeof(tSirMacAddr));
WMA_LOGD("%s: sending tdls_peer_state for peer mac: %pM, peerState: %d",
__func__, peerStateParams->peerMacAddr,
peerStateParams->peerState);
status = wma_update_tdls_peer_state(wma, peerStateParams);
if (status < 0) {
WMA_LOGE("%s: wma_update_tdls_peer_state returned failure",
__func__);
goto send_del_rsp;
}
if (del_sta->respReqd &&
WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap,
WMI_SERVICE_SYNC_DELETE_CMDS)) {
del_sta->status = QDF_STATUS_SUCCESS;
msg = wma_fill_hold_req(wma,
del_sta->smesessionId,
WMA_DELETE_STA_REQ,
WMA_DELETE_STA_RSP_START, del_sta,
WMA_DELETE_STA_TIMEOUT);
if (!msg) {
WMA_LOGE(FL("Failed to allocate vdev_id %d"),
del_sta->smesessionId);
wma_remove_req(wma,
del_sta->smesessionId,
WMA_DELETE_STA_RSP_START);
del_sta->status = QDF_STATUS_E_NOMEM;
goto send_del_rsp;
}
wma_acquire_wakelock(&wma->wmi_cmd_rsp_wake_lock,
WMA_FW_RSP_EVENT_WAKE_LOCK_DURATION);
}
return;
send_del_rsp:
if (del_sta->respReqd) {
WMA_LOGD("%s: Sending del rsp to umac (status: %d)",
__func__, del_sta->status);
wma_send_msg_high_priority(wma,
WMA_DELETE_STA_RSP, (void *)del_sta, 0);
}
}
#endif
/**
* wma_delete_sta_req_sta_mode() - proces delete sta request from UMAC
* @wma: wma handle
* @params: delete sta params
*
* Return: none
*/
static void wma_delete_sta_req_sta_mode(tp_wma_handle wma,
tpDeleteStaParams params)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
struct wma_txrx_node *iface;
iface = &wma->interfaces[params->smesessionId];
iface->uapsd_cached_val = 0;
if (wma_is_roam_synch_in_progress(wma, params->smesessionId))
return;
#ifdef FEATURE_WLAN_TDLS
if (STA_ENTRY_TDLS_PEER == params->staType) {
wma_del_tdls_sta(wma, params);
return;
}
#endif
params->status = status;
if (params->respReqd) {
WMA_LOGD("%s: vdev_id %d status %d", __func__,
params->smesessionId, status);
wma_send_msg_high_priority(wma,
WMA_DELETE_STA_RSP, (void *)params, 0);
}
}
/**
* wma_add_sta() - process add sta request as per opmode
* @wma: wma handle
* @add_Sta: add sta params
*
* Return: none
*/
void wma_add_sta(tp_wma_handle wma, tpAddStaParams add_sta)
{
uint8_t oper_mode = BSS_OPERATIONAL_MODE_STA;
WMA_LOGD("%s: add_sta->sessionId = %d.", __func__,
add_sta->smesessionId);
WMA_LOGD("%s: add_sta->bssId = %x:%x:%x:%x:%x:%x", __func__,
add_sta->bssId[0], add_sta->bssId[1], add_sta->bssId[2],
add_sta->bssId[3], add_sta->bssId[4], add_sta->bssId[5]);
if (wma_is_vdev_in_ap_mode(wma, add_sta->smesessionId))
oper_mode = BSS_OPERATIONAL_MODE_AP;
else if (wma_is_vdev_in_ibss_mode(wma, add_sta->smesessionId))
oper_mode = BSS_OPERATIONAL_MODE_IBSS;
if (WMA_IS_VDEV_IN_NDI_MODE(wma->interfaces, add_sta->smesessionId))
oper_mode = BSS_OPERATIONAL_MODE_NDI;
switch (oper_mode) {
case BSS_OPERATIONAL_MODE_STA:
wma_add_sta_req_sta_mode(wma, add_sta);
break;
/* IBSS should share the same code as AP mode */
case BSS_OPERATIONAL_MODE_IBSS:
case BSS_OPERATIONAL_MODE_AP:
htc_vote_link_up(wma->htc_handle);
wma_add_sta_req_ap_mode(wma, add_sta);
break;
case BSS_OPERATIONAL_MODE_NDI:
wma_add_sta_ndi_mode(wma, add_sta);
break;
}
#ifdef QCA_IBSS_SUPPORT
/* adjust heart beat thresold timer value for detecting ibss peer
* departure
*/
if (oper_mode == BSS_OPERATIONAL_MODE_IBSS)
wma_adjust_ibss_heart_beat_timer(wma, add_sta->smesessionId, 1);
#endif
}
/**
* wma_delete_sta() - process del sta request as per opmode
* @wma: wma handle
* @del_sta: delete sta params
*
* Return: none
*/
void wma_delete_sta(tp_wma_handle wma, tpDeleteStaParams del_sta)
{
uint8_t oper_mode = BSS_OPERATIONAL_MODE_STA;
uint8_t smesession_id = del_sta->smesessionId;
bool rsp_requested = del_sta->respReqd;
if (wma_is_vdev_in_ap_mode(wma, smesession_id))
oper_mode = BSS_OPERATIONAL_MODE_AP;
if (wma_is_vdev_in_ibss_mode(wma, smesession_id)) {
oper_mode = BSS_OPERATIONAL_MODE_IBSS;
WMA_LOGD("%s: to delete sta for IBSS mode", __func__);
}
if (del_sta->staType == STA_ENTRY_NDI_PEER)
oper_mode = BSS_OPERATIONAL_MODE_NDI;
WMA_LOGD(FL("oper_mode %d"), oper_mode);
switch (oper_mode) {
case BSS_OPERATIONAL_MODE_STA:
wma_delete_sta_req_sta_mode(wma, del_sta);
if (wma_is_roam_synch_in_progress(wma, smesession_id)) {
WMA_LOGD(FL("LFR3: Del STA on vdev_id %d"),
del_sta->smesessionId);
qdf_mem_free(del_sta);
return;
}
if (!rsp_requested) {
WMA_LOGD(FL("vdev_id %d status %d"),
del_sta->smesessionId, del_sta->status);
qdf_mem_free(del_sta);
}
break;
case BSS_OPERATIONAL_MODE_IBSS: /* IBSS shares AP code */
case BSS_OPERATIONAL_MODE_AP:
htc_vote_link_down(wma->htc_handle);
wma_delete_sta_req_ap_mode(wma, del_sta);
/* free the memory here only if sync feature is not enabled */
if (!rsp_requested &&
!WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap,
WMI_SERVICE_SYNC_DELETE_CMDS)) {
WMA_LOGD(FL("vdev_id %d status %d"),
del_sta->smesessionId, del_sta->status);
qdf_mem_free(del_sta);
} else if (!rsp_requested &&
(del_sta->status != QDF_STATUS_SUCCESS)) {
WMA_LOGD(FL("Release del_sta mem vdev_id %d status %d"),
del_sta->smesessionId, del_sta->status);
qdf_mem_free(del_sta);
}
break;
case BSS_OPERATIONAL_MODE_NDI:
wma_delete_sta_req_ndi_mode(wma, del_sta);
break;
default:
WMA_LOGE(FL("Incorrect oper mode %d"), oper_mode);
qdf_mem_free(del_sta);
}
#ifdef QCA_IBSS_SUPPORT
/* adjust heart beat thresold timer value for
* detecting ibss peer departure
*/
if (oper_mode == BSS_OPERATIONAL_MODE_IBSS)
wma_adjust_ibss_heart_beat_timer(wma, smesession_id, -1);
#endif
}
/**
* wma_delete_bss_ho_fail() - process delete bss request for handoff failure
* @wma: wma handle
* @params: del bss parameters
*
* Delete BSS in case of ROAM_HO_FAIL processing is handled separately in
* this routine. It needs to be done without sending any commands to firmware
* because firmware has already stopped and deleted peer and vdev is down.
* Relevent logic is aggregated from other routines. It changes the host
* data structures without sending VDEV_STOP, PEER_FLUSH_TIDS, PEER_DELETE
* and VDEV_DOWN commands to firmware.
*
* Return: none
*/
void wma_delete_bss_ho_fail(tp_wma_handle wma, tpDeleteBssParams params)
{
ol_txrx_pdev_handle pdev;
ol_txrx_peer_handle peer = NULL;
QDF_STATUS status = QDF_STATUS_SUCCESS;
uint8_t peer_id;
ol_txrx_vdev_handle txrx_vdev = NULL;
struct wma_txrx_node *iface;
pdev = cds_get_context(QDF_MODULE_ID_TXRX);
if (NULL == pdev) {
WMA_LOGE("%s:Unable to get TXRX context", __func__);
goto fail_del_bss_ho_fail;
}
peer = ol_txrx_find_peer_by_addr(pdev, params->bssid, &peer_id);
if (!peer) {
WMA_LOGE("%s: Failed to find peer %pM", __func__,
params->bssid);
status = QDF_STATUS_E_FAILURE;
goto fail_del_bss_ho_fail;
}
iface = &wma->interfaces[params->smesessionId];
if (!iface || !iface->handle) {
WMA_LOGE("%s vdev id %d is already deleted",
__func__, params->smesessionId);
goto fail_del_bss_ho_fail;
}
qdf_mem_zero(iface->bssid, IEEE80211_ADDR_LEN);
txrx_vdev = wma_find_vdev_by_id(wma, params->smesessionId);
if (!txrx_vdev) {
WMA_LOGE("%s:Invalid vdev handle", __func__);
status = QDF_STATUS_E_FAILURE;
goto fail_del_bss_ho_fail;
}
/* Free the allocated stats response buffer for the the session */
if (iface->stats_rsp) {
qdf_mem_free(iface->stats_rsp);
iface->stats_rsp = NULL;
}
if (iface->psnr_req) {
qdf_mem_free(iface->psnr_req);
iface->psnr_req = NULL;
}
if (iface->rcpi_req) {
struct sme_rcpi_req *rcpi_req = iface->rcpi_req;
iface->rcpi_req = NULL;
qdf_mem_free(rcpi_req);
}
qdf_mem_zero(&iface->ns_offload_req,
sizeof(iface->ns_offload_req));
qdf_mem_zero(&iface->arp_offload_req,
sizeof(iface->arp_offload_req));
WMA_LOGD("%s, vdev_id: %d, pausing tx_ll_queue for VDEV_STOP (del_bss)",
__func__, params->smesessionId);
ol_txrx_vdev_pause(iface->handle,
OL_TXQ_PAUSE_REASON_VDEV_STOP);
iface->pause_bitmap |= (1 << PAUSE_TYPE_HOST);
ol_txrx_vdev_flush(iface->handle);
WMA_LOGD("%s, vdev_id: %d, un-pausing tx_ll_queue for VDEV_STOP rsp",
__func__, params->smesessionId);
ol_txrx_vdev_unpause(iface->handle,
OL_TXQ_PAUSE_REASON_VDEV_STOP);
iface->pause_bitmap &= ~(1 << PAUSE_TYPE_HOST);
qdf_atomic_set(&iface->bss_status, WMA_BSS_STATUS_STOPPED);
WMA_LOGD("%s: (type %d subtype %d) BSS is stopped",
__func__, iface->type, iface->sub_type);
iface->vdev_up = false;
WMA_LOGD(FL("Setting vdev_up flag to false"));
params->status = QDF_STATUS_SUCCESS;
if (!iface->peer_count) {
WMA_LOGE("%s: Can't remove peer with peer_addr %pM vdevid %d peer_count %d",
__func__, params->bssid, params->smesessionId,
iface->peer_count);
goto fail_del_bss_ho_fail;
}
if (peer)
ol_txrx_peer_detach(peer, true);
iface->peer_count--;
WMA_LOGD("%s: Removed peer %pK with peer_addr %pM vdevid %d peer_count %d",
__func__, peer, params->bssid, params->smesessionId,
iface->peer_count);
fail_del_bss_ho_fail:
params->status = status;
wma_send_msg_high_priority(wma,
WMA_DELETE_BSS_HO_FAIL_RSP, (void *)params, 0);
}
#ifdef WLAN_FEATURE_HOST_ROAM
/**
* wma_wait_tx_complete() - Wait till tx packets are drained
* @wma: wma handle
* @session_id: vdev id
*
* Return: none
*/
static void wma_wait_tx_complete(tp_wma_handle wma,
uint32_t session_id)
{
ol_txrx_pdev_handle pdev;
uint8_t max_wait_iterations = 0;
if (!wma->interfaces[session_id].is_vdev_valid) {
WMA_LOGE("%s: Vdev is not valid: %d",
__func__, session_id);
return;
}
pdev = cds_get_context(QDF_MODULE_ID_TXRX);
if (pdev == NULL) {
WMA_LOGE("%s: pdev is not valid: %d",
__func__, session_id);
return;
}
max_wait_iterations =
wma->interfaces[session_id].delay_before_vdev_stop /
WMA_TX_Q_RECHECK_TIMER_WAIT;
while (ol_txrx_get_tx_pending(pdev) && max_wait_iterations) {
WMA_LOGW(FL("Waiting for outstanding packet to drain."));
qdf_wait_for_event_completion(&wma->tx_queue_empty_event,
WMA_TX_Q_RECHECK_TIMER_WAIT);
max_wait_iterations--;
}
}
#else
static void wma_wait_tx_complete(tp_wma_handle wma)
{
}
#endif
/**
* wma_delete_bss() - process delete bss request from upper layer
* @wma: wma handle
* @params: del bss parameters
*
* Return: none
*/
void wma_delete_bss(tp_wma_handle wma, tpDeleteBssParams params)
{
ol_txrx_pdev_handle pdev;
ol_txrx_peer_handle peer = NULL;
struct wma_target_req *msg;
QDF_STATUS status = QDF_STATUS_SUCCESS;
uint8_t peer_id;
ol_txrx_vdev_handle txrx_vdev = NULL;
bool roam_synch_in_progress = false;
struct wma_txrx_node *iface;
pdev = cds_get_context(QDF_MODULE_ID_TXRX);
if (NULL == pdev) {
WMA_LOGE("%s:Unable to get TXRX context", __func__);
goto out;
}
if (wma_is_vdev_in_ibss_mode(wma, params->smesessionId))
/* in rome ibss case, self mac is used to create the bss peer */
peer = ol_txrx_find_peer_by_addr(pdev,
wma->interfaces[params->smesessionId].addr,
&peer_id);
else if (WMA_IS_VDEV_IN_NDI_MODE(wma->interfaces,
params->smesessionId))
/* In ndi case, self mac is used to create the self peer */
peer = ol_txrx_find_peer_by_addr(pdev,
wma->interfaces[params->smesessionId].addr,
&peer_id);
else
peer = ol_txrx_find_peer_by_addr(pdev, params->bssid, &peer_id);
if (!peer) {
WMA_LOGE("%s: Failed to find peer %pM", __func__,
params->bssid);
status = QDF_STATUS_E_FAILURE;
goto out;
}
qdf_mem_zero(wma->interfaces[params->smesessionId].bssid,
IEEE80211_ADDR_LEN);
txrx_vdev = wma_find_vdev_by_id(wma, params->smesessionId);
if (!txrx_vdev) {
WMA_LOGE("%s:Invalid vdev handle", __func__);
status = QDF_STATUS_E_FAILURE;
goto out;
}
iface = &wma->interfaces[params->smesessionId];
if (!iface || !iface->handle) {
WMA_LOGE("%s vdev id %d is already deleted",
__func__, params->smesessionId);
goto out;
}
/*Free the allocated stats response buffer for the the session */
if (iface->stats_rsp) {
qdf_mem_free(iface->stats_rsp);
iface->stats_rsp = NULL;
}
if (iface->psnr_req) {
qdf_mem_free(iface->psnr_req);
iface->psnr_req = NULL;
}
if (iface->rcpi_req) {
struct sme_rcpi_req *rcpi_req = iface->rcpi_req;
iface->rcpi_req = NULL;
qdf_mem_free(rcpi_req);
}
if (wma->interfaces[params->smesessionId].action_frame_filter) {
struct action_frame_random_filter *action_frame_filter =
wma->interfaces[params->smesessionId].action_frame_filter;
wma->interfaces[params->smesessionId].action_frame_filter =
NULL;
qdf_mem_free(action_frame_filter);
}
if (wlan_op_mode_ibss == ol_txrx_get_opmode(txrx_vdev))
wma->ibss_started = 0;
if (wma_is_roam_synch_in_progress(wma, params->smesessionId)) {
roam_synch_in_progress = true;
WMA_LOGD("LFR3:%s: Setting vdev_up to FALSE for session %d",
__func__, params->smesessionId);
iface->vdev_up = false;
WMA_LOGD(FL("Setting vdev_up flag to false"));
goto detach_peer;
}
msg = wma_fill_vdev_req(wma, params->smesessionId, WMA_DELETE_BSS_REQ,
WMA_TARGET_REQ_TYPE_VDEV_STOP, params,
WMA_VDEV_STOP_REQUEST_TIMEOUT);
if (!msg) {
WMA_LOGE("%s: Failed to fill vdev request for vdev_id %d",
__func__, params->smesessionId);
status = QDF_STATUS_E_NOMEM;
goto detach_peer;
}
WMA_LOGW(FL("Outstanding msdu packets: %d"),
ol_txrx_get_tx_pending(pdev));
wma_wait_tx_complete(wma, params->smesessionId);
if (ol_txrx_get_tx_pending(pdev)) {
WMA_LOGW(FL("Outstanding msdu packets before VDEV_STOP : %d"),
ol_txrx_get_tx_pending(pdev));
}
WMA_LOGD("%s, vdev_id: %d, pausing tx_ll_queue for VDEV_STOP (del_bss)",
__func__, params->smesessionId);
ol_txrx_vdev_pause(iface->handle,
OL_TXQ_PAUSE_REASON_VDEV_STOP);
iface->pause_bitmap |= (1 << PAUSE_TYPE_HOST);
status = wma_send_vdev_stop_to_fw(wma, params->smesessionId);
wma_cli_set_command(params->smesessionId,
(int)WMI_VDEV_PARAM_ABG_MODE_TX_CHAIN_NUM, 0, VDEV_CMD);
WMA_LOGD("vdev: %d WMI_VDEV_PARAM_ABG_MODE_TX_CHAIN_NUM 0",
params->smesessionId);
if (QDF_IS_STATUS_ERROR(status)) {
WMA_LOGE("%s: %d Failed to send vdev stop", __func__, __LINE__);
wma_remove_vdev_req(wma, params->smesessionId,
WMA_TARGET_REQ_TYPE_VDEV_STOP);
goto detach_peer;
}
WMA_LOGD("%s: bssid %pM vdev_id %d",
__func__, params->bssid, params->smesessionId);
return;
detach_peer:
wma_remove_peer(wma, params->bssid, params->smesessionId, peer,
roam_synch_in_progress);
if (wma_is_roam_synch_in_progress(wma, params->smesessionId))
return;
out:
params->status = status;
wma_send_msg_high_priority(wma, WMA_DELETE_BSS_RSP, (void *)params, 0);
}
/**
* wma_find_ibss_vdev() - This function finds vdev_id based on input type
* @wma: wma handle
* @type: vdev type
*
* Return: vdev id
*/
int32_t wma_find_vdev_by_type(tp_wma_handle wma, int32_t type)
{
int32_t vdev_id = 0;
struct wma_txrx_node *intf = wma->interfaces;
for (vdev_id = 0; vdev_id < wma->max_bssid; vdev_id++) {
if (NULL != intf) {
if (intf[vdev_id].type == type)
return vdev_id;
}
}
return -EFAULT;
}
/**
* wma_set_vdev_intrabss_fwd() - set intra_fwd value to wni_in.
* @wma_handle: wma handle
* @pdis_intra_fwd: Pointer to DisableIntraBssFwd struct
*
* Return: none
*/
void wma_set_vdev_intrabss_fwd(tp_wma_handle wma_handle,
tpDisableIntraBssFwd pdis_intra_fwd)
{
ol_txrx_vdev_handle txrx_vdev;
WMA_LOGD("%s:intra_fwd:vdev(%d) intrabss_dis=%s",
__func__, pdis_intra_fwd->sessionId,
(pdis_intra_fwd->disableintrabssfwd ? "true" : "false"));
txrx_vdev = wma_handle->interfaces[pdis_intra_fwd->sessionId].handle;
ol_vdev_rx_set_intrabss_fwd(txrx_vdev,
pdis_intra_fwd->disableintrabssfwd);
}
/**
* wma_vdev_reset_beacon_interval_timer() - reset beacon interval back
* to its original value after the channel switch.
*
* @data: data
*
* Return: void
*/
static void wma_vdev_reset_beacon_interval_timer(void *data)
{
tp_wma_handle wma;
struct wma_beacon_interval_reset_req *req =
(struct wma_beacon_interval_reset_req *)data;
uint16_t beacon_interval = req->interval;
uint8_t vdev_id = req->vdev_id;
wma = (tp_wma_handle)cds_get_context(QDF_MODULE_ID_WMA);
if (NULL == wma) {
WMA_LOGE("%s: Failed to get wma", __func__);
goto end;
}
/* Change the beacon interval back to its original value */
WMA_LOGE("%s: Change beacon interval back to %d",
__func__, beacon_interval);
wma_update_beacon_interval(wma, vdev_id, beacon_interval);
end:
qdf_timer_stop(&req->event_timeout);
qdf_timer_free(&req->event_timeout);
qdf_mem_free(req);
}
int wma_fill_beacon_interval_reset_req(tp_wma_handle wma, uint8_t vdev_id,
uint16_t beacon_interval, uint32_t timeout)
{
struct wma_beacon_interval_reset_req *req;
req = qdf_mem_malloc(sizeof(*req));
if (!req) {
WMA_LOGE("%s: Failed to allocate memory for beacon_interval_reset_req vdev %d",
__func__, vdev_id);
return -ENOMEM;
}
WMA_LOGD("%s: vdev_id %d ", __func__, vdev_id);
req->vdev_id = vdev_id;
req->interval = beacon_interval;
qdf_timer_init(NULL, &req->event_timeout,
wma_vdev_reset_beacon_interval_timer, req, QDF_TIMER_TYPE_SW);
qdf_timer_start(&req->event_timeout, timeout);
return 0;
}
QDF_STATUS wma_set_wlm_latency_level(void *wma_ptr,
struct wlm_latency_level_param *latency_params)
{
QDF_STATUS ret;
tp_wma_handle wma = (tp_wma_handle)wma_ptr;
WMA_LOGD("%s: set latency level %d, flags flag 0x%x",
__func__, latency_params->wlm_latency_level,
latency_params->wlm_latency_flags);
ret = wmi_unified_wlm_latency_level_cmd(wma->wmi_handle,
latency_params);
if (QDF_IS_STATUS_ERROR(ret))
WMA_LOGW("Failed to set latency level");
return ret;
}