/* * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved. * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all * copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /** * DOC: 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 "qdf_nbuf.h" #include "qdf_types.h" #include "qdf_mem.h" #include "wma_types.h" #include "lim_api.h" #include "lim_session_utils.h" #include "wma_pasn_peer_api.h" #include "cds_utils.h" #if !defined(REMOVE_PKT_LOG) #include "pktlog_ac.h" #endif /* REMOVE_PKT_LOG */ #include "dbglog_host.h" #include "csr_api.h" #include "wma_internal.h" #include "wma_ocb.h" #include "cdp_txrx_cfg.h" #include "cdp_txrx_flow_ctrl_legacy.h" #include #include #include #include #include #include "wlan_policy_mgr_api.h" #include "wma_nan_datapath.h" #include "wifi_pos_pasn_api.h" #if defined(CONFIG_HL_SUPPORT) #include "wlan_tgt_def_config_hl.h" #else #include "wlan_tgt_def_config.h" #endif #include #include #include "wlan_pmo_ucfg_api.h" #include "wlan_reg_services_api.h" #include #include "wma_he.h" #include "wma_eht.h" #include "wlan_roam_debug.h" #include "wlan_ocb_ucfg_api.h" #include "init_deinit_lmac.h" #include #include "wlan_policy_mgr_ucfg.h" #include "wlan_mlme_public_struct.h" #include "wlan_mlme_api.h" #include "wlan_mlme_main.h" #include "wlan_mlme_ucfg_api.h" #include "wlan_mlme_twt_api.h" #include #include "../../core/src/vdev_mgr_ops.h" #include "wlan_utility.h" #include "wlan_coex_ucfg_api.h" #include #include "wmi_unified_vdev_api.h" #include #include <../../core/src/wlan_cm_vdev_api.h> #include "wlan_nan_api.h" #include "wlan_mlo_mgr_peer.h" #include "wifi_pos_api.h" #include "wifi_pos_pasn_api.h" #ifdef DCS_INTERFERENCE_DETECTION #include #endif #ifdef FEATURE_STA_MODE_VOTE_LINK #include "wlan_ipa_ucfg_api.h" #endif #include "son_api.h" #include "wlan_vdev_mgr_tgt_if_tx_defs.h" #include "wlan_mlo_mgr_roam.h" #include "target_if_vdev_mgr_tx_ops.h" #include "wlan_fwol_ucfg_api.h" #include "wlan_vdev_mgr_utils_api.h" #include "target_if.h" #include /* * FW only supports 8 clients in SAP/GO mode for D3 WoW feature * and hence host needs to hold a wake lock after 9th client connects * and release the wake lock when 9th client disconnects */ #define SAP_D3_WOW_MAX_CLIENT_HOLD_WAKE_LOCK (9) #define SAP_D3_WOW_MAX_CLIENT_RELEASE_WAKE_LOCK (8) QDF_STATUS wma_find_vdev_id_by_addr(tp_wma_handle wma, uint8_t *addr, uint8_t *vdev_id) { uint8_t i; struct wlan_objmgr_vdev *vdev; for (i = 0; i < wma->max_bssid; i++) { vdev = wma->interfaces[i].vdev; if (!vdev) continue; if (qdf_is_macaddr_equal( (struct qdf_mac_addr *)wlan_vdev_mlme_get_macaddr(vdev), (struct qdf_mac_addr *)addr) == true) { *vdev_id = i; return QDF_STATUS_SUCCESS; } if (qdf_is_macaddr_equal((struct qdf_mac_addr *)wlan_vdev_mlme_get_mldaddr(vdev), (struct qdf_mac_addr *)addr) == true) { *vdev_id = i; return QDF_STATUS_SUCCESS; } } return QDF_STATUS_E_FAILURE; } /** * 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_err("Invalid vdev_id %hu", 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; } uint8_t *wma_get_vdev_bssid(struct wlan_objmgr_vdev *vdev) { struct vdev_mlme_obj *mlme_obj; if (!vdev) { wma_err("vdev is NULL"); return NULL; } mlme_obj = wlan_vdev_mlme_get_cmpt_obj(vdev); if (!mlme_obj) { wma_err("Failed to get mlme_obj"); return NULL; } return mlme_obj->mgmt.generic.bssid; } QDF_STATUS wma_find_vdev_id_by_bssid(tp_wma_handle wma, uint8_t *bssid, uint8_t *vdev_id) { int i; uint8_t *bssid_addr; for (i = 0; i < wma->max_bssid; i++) { if (!wma->interfaces[i].vdev) continue; bssid_addr = wma_get_vdev_bssid(wma->interfaces[i].vdev); if (!bssid_addr) continue; if (qdf_is_macaddr_equal( (struct qdf_mac_addr *)bssid_addr, (struct qdf_mac_addr *)bssid) == true) { *vdev_id = i; return QDF_STATUS_SUCCESS; } } return QDF_STATUS_E_FAILURE; } /** * wma_find_req_on_timer_expiry() - find request by address * @wma: wma handle * @req: pointer to the target request * * On timer expiry, the pointer to the req message is received from the * timer callback. Lookup the wma_hold_req_queue for the request with the * same address and return success if found. * * Return: QDF_STATUS */ static QDF_STATUS wma_find_req_on_timer_expiry(tp_wma_handle wma, struct wma_target_req *req) { struct wma_target_req *req_msg = NULL; bool found = false; qdf_list_node_t *cur_node = NULL, *next_node = 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, &next_node)) { qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock); wma_err("unable to get msg node from request queue"); return QDF_STATUS_E_FAILURE; } do { cur_node = next_node; req_msg = qdf_container_of(cur_node, struct wma_target_req, node); if (req_msg != req) continue; found = true; status = qdf_list_remove_node(&wma->wma_hold_req_queue, cur_node); if (QDF_STATUS_SUCCESS != status) { qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock); wma_debug("Failed to remove request for req %pK", req); return QDF_STATUS_E_FAILURE; } break; } while (QDF_STATUS_SUCCESS == qdf_list_peek_next(&wma->wma_hold_req_queue, cur_node, &next_node)); qdf_spin_unlock_bh(&wma->wma_hold_req_q_lock); if (!found) { wma_err("target request not found for req %pK", req); return QDF_STATUS_E_INVAL; } wma_debug("target request found for vdev id: %d type %d", req_msg->vdev_id, req_msg->type); return QDF_STATUS_SUCCESS; } /** * wma_find_req() - find target request for vdev id * @wma: wma handle * @vdev_id: vdev id * @type: request type * @peer_addr: Peer mac address * * 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 qdf_mac_addr *peer_addr) { 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_err("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; if (type == WMA_PEER_CREATE_RESPONSE && peer_addr && !qdf_is_macaddr_equal(&req_msg->addr, peer_addr)) found = false; 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_debug("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_err("target request not found for vdev_id %d type %d", vdev_id, type); return NULL; } wma_debug("target request found for vdev id: %d type %d", vdev_id, type); return req_msg; } 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_debug("unable to get msg node from request queue for vdev_id %d type %d", vdev_id, msg_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->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_debug("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_debug("target request not found for vdev_id %d type %d", vdev_id, msg_type); return NULL; } wma_debug("target request found for vdev id: %d type %d", vdev_id, msg_type); return req_msg; } QDF_STATUS wma_vdev_detach_callback(struct vdev_delete_response *rsp) { tp_wma_handle wma; struct wma_txrx_node *iface = NULL; wma = cds_get_context(QDF_MODULE_ID_WMA); if (!wma) return QDF_STATUS_E_FAILURE; /* Sanitize the vdev id*/ if (rsp->vdev_id >= wma->max_bssid) { wma_err("vdev delete response with invalid vdev_id :%d", rsp->vdev_id); QDF_BUG(0); return QDF_STATUS_E_FAILURE; } iface = &wma->interfaces[rsp->vdev_id]; wma_debug("vdev del response received for VDEV_%d", rsp->vdev_id); iface->del_staself_req = NULL; if (iface->roam_scan_stats_req) { struct sir_roam_scan_stats *roam_scan_stats_req = iface->roam_scan_stats_req; iface->roam_scan_stats_req = NULL; qdf_mem_free(roam_scan_stats_req); } wma_vdev_deinit(iface); qdf_mem_zero(iface, sizeof(*iface)); wma_vdev_init(iface); mlme_vdev_del_resp(rsp->vdev_id); return QDF_STATUS_SUCCESS; } static void wma_cdp_vdev_detach(ol_txrx_soc_handle soc, tp_wma_handle wma_handle, uint8_t vdev_id) { struct wma_txrx_node *iface = &wma_handle->interfaces[vdev_id]; struct wlan_objmgr_vdev *vdev = iface->vdev; if (!vdev) { wma_err("vdev is NULL"); return; } if (soc && wlan_vdev_get_id(vdev) != WLAN_INVALID_VDEV_ID) cdp_vdev_detach(soc, vdev_id, NULL, NULL); } /** * wma_release_vdev_ref() - Release vdev object reference count * @iface: wma interface txrx node * * Purpose of this function is to release vdev object reference count * from wma interface txrx node. * * Return: None */ static void wma_release_vdev_ref(struct wma_txrx_node *iface) { struct wlan_objmgr_vdev *vdev; vdev = iface->vdev; if (!vdev) { wma_debug("vdev context is NULL"); return; } wma_debug("vdev state: %d", vdev->obj_state); if (vdev->obj_state != WLAN_OBJ_STATE_LOGICALLY_DELETED) { wma_debug("no vdev delete"); return; } iface->vdev_active = false; iface->vdev = NULL; if (vdev) wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); } /** * wma_handle_monitor_mode_vdev_detach() - Stop and down monitor mode vdev * @wma: wma handle * @vdev_id: used to get wma interface txrx node * * Monitor mode is unconneted mode, so do explicit vdev stop and down * * Return: None */ static void wma_handle_monitor_mode_vdev_detach(tp_wma_handle wma, uint8_t vdev_id) { struct wma_txrx_node *iface; iface = &wma->interfaces[vdev_id]; wlan_vdev_mlme_sm_deliver_evt(iface->vdev, WLAN_VDEV_SM_EV_DOWN, 0, NULL); iface->vdev_active = false; } /** * wma_handle_vdev_detach() - wma vdev detach handler * @wma_handle: pointer to wma handle * @del_vdev_req_param: pointer to del req param * * Return: none. */ static QDF_STATUS wma_handle_vdev_detach(tp_wma_handle wma_handle, struct del_vdev_params *del_vdev_req_param) { QDF_STATUS status = QDF_STATUS_SUCCESS; uint8_t vdev_id = del_vdev_req_param->vdev_id; struct wma_txrx_node *iface = &wma_handle->interfaces[vdev_id]; void *soc = cds_get_context(QDF_MODULE_ID_SOC); struct wmi_mgmt_params mgmt_params = {}; if (!soc) { status = QDF_STATUS_E_FAILURE; goto rel_ref; } if ((cds_get_conparam() == QDF_GLOBAL_MONITOR_MODE) || (policy_mgr_is_sta_mon_concurrency(wma_handle->psoc) && wlan_vdev_mlme_get_opmode(iface->vdev) == QDF_MONITOR_MODE)) wma_handle_monitor_mode_vdev_detach(wma_handle, vdev_id); rel_ref: wma_cdp_vdev_detach(soc, wma_handle, vdev_id); if (qdf_is_recovering()) wlan_mgmt_txrx_vdev_drain(iface->vdev, wma_mgmt_frame_fill_peer_cb, &mgmt_params); wma_debug("Releasing wma reference for vdev:%d", vdev_id); wma_release_vdev_ref(iface); return status; } /** * wma_self_peer_remove() - Self peer remove handler * @wma_handle: wma handle * @del_vdev_req: vdev id * * 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_vdev_params *del_vdev_req) { QDF_STATUS qdf_status; uint8_t vdev_id = del_vdev_req->vdev_id; struct wma_target_req *msg = NULL; struct del_sta_self_rsp_params *sta_self_wmi_rsp = NULL; wma_debug("P2P Device: removing self peer "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(del_vdev_req->self_mac_addr)); if (wmi_service_enabled(wma_handle->wmi_handle, wmi_service_sync_delete_cmds)) { sta_self_wmi_rsp = qdf_mem_malloc(sizeof(struct del_sta_self_rsp_params)); if (!sta_self_wmi_rsp) { qdf_status = QDF_STATUS_E_NOMEM; goto error; } sta_self_wmi_rsp->self_sta_param = del_vdev_req; 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_err("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_mem_free(sta_self_wmi_rsp); qdf_status = QDF_STATUS_E_FAILURE; goto error; } } qdf_status = wma_remove_peer(wma_handle, del_vdev_req->self_mac_addr, vdev_id, false); if (QDF_IS_STATUS_ERROR(qdf_status)) { wma_err("wma_remove_peer is failed"); wma_remove_req(wma_handle, vdev_id, WMA_DEL_P2P_SELF_STA_RSP_START); if (sta_self_wmi_rsp) qdf_mem_free(sta_self_wmi_rsp); goto error; } error: return qdf_status; } #ifdef WLAN_FEATURE_DYNAMIC_MAC_ADDR_UPDATE QDF_STATUS wma_p2p_self_peer_remove(struct wlan_objmgr_vdev *vdev) { struct del_vdev_params *del_self_peer_req; tp_wma_handle wma_handle = cds_get_context(QDF_MODULE_ID_WMA); QDF_STATUS status; if (!wma_handle) return QDF_STATUS_E_INVAL; del_self_peer_req = qdf_mem_malloc(sizeof(*del_self_peer_req)); if (!del_self_peer_req) return QDF_STATUS_E_NOMEM; del_self_peer_req->vdev = vdev; del_self_peer_req->vdev_id = wlan_vdev_get_id(vdev); qdf_mem_copy(del_self_peer_req->self_mac_addr, wlan_vdev_mlme_get_macaddr(vdev), QDF_MAC_ADDR_SIZE); status = wma_self_peer_remove(wma_handle, del_self_peer_req); return status; } #endif void wma_remove_objmgr_peer(tp_wma_handle wma, struct wlan_objmgr_vdev *obj_vdev, uint8_t *peer_addr) { struct wlan_objmgr_psoc *psoc; struct wlan_objmgr_peer *obj_peer; struct wlan_objmgr_pdev *obj_pdev; uint8_t pdev_id = 0; psoc = wma->psoc; if (!psoc) { wma_err("PSOC is NULL"); return; } obj_pdev = wlan_vdev_get_pdev(obj_vdev); pdev_id = wlan_objmgr_pdev_get_pdev_id(obj_pdev); obj_peer = wlan_objmgr_get_peer(psoc, pdev_id, peer_addr, WLAN_LEGACY_WMA_ID); if (obj_peer) { wlan_objmgr_peer_obj_delete(obj_peer); /* Unref to decrement ref happened in find_peer */ wlan_objmgr_peer_release_ref(obj_peer, WLAN_LEGACY_WMA_ID); } else { wma_nofl_err("Peer "QDF_MAC_ADDR_FMT" not found", QDF_MAC_ADDR_REF(peer_addr)); } } static QDF_STATUS wma_check_for_deferred_peer_delete(tp_wma_handle wma_handle, struct del_vdev_params *pdel_vdev_req_param) { QDF_STATUS status = QDF_STATUS_SUCCESS; uint8_t vdev_id = pdel_vdev_req_param->vdev_id; struct wma_txrx_node *iface = &wma_handle->interfaces[vdev_id]; uint32_t vdev_stop_type; if (qdf_atomic_read(&iface->bss_status) == WMA_BSS_STATUS_STARTED) { status = mlme_get_vdev_stop_type(iface->vdev, &vdev_stop_type); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to get wma req msg_type for vdev_id: %d", vdev_id); status = QDF_STATUS_E_INVAL; return status; } if (vdev_stop_type != WMA_DELETE_BSS_REQ) { status = QDF_STATUS_E_INVAL; return status; } wma_debug("BSS is not yet stopped. Deferring vdev(vdev id %x) deletion", vdev_id); iface->del_staself_req = pdel_vdev_req_param; iface->is_del_sta_deferred = true; } return status; } static QDF_STATUS wma_vdev_self_peer_delete(tp_wma_handle wma_handle, struct del_vdev_params *pdel_vdev_req_param) { QDF_STATUS status = QDF_STATUS_SUCCESS; uint8_t vdev_id = pdel_vdev_req_param->vdev_id; struct wma_txrx_node *iface = &wma_handle->interfaces[vdev_id]; if (mlme_vdev_uses_self_peer(iface->type, iface->sub_type)) { status = wma_self_peer_remove(wma_handle, pdel_vdev_req_param); if (QDF_IS_STATUS_ERROR(status)) { wma_err("can't remove selfpeer, send rsp session: %d", vdev_id); wma_handle_vdev_detach(wma_handle, pdel_vdev_req_param); mlme_vdev_self_peer_delete_resp(pdel_vdev_req_param); cds_trigger_recovery(QDF_SELF_PEER_DEL_FAILED); return status; } } else if (iface->type == WMI_VDEV_TYPE_STA || iface->type == WMI_VDEV_TYPE_NAN) { wma_remove_objmgr_peer(wma_handle, iface->vdev, pdel_vdev_req_param->self_mac_addr); } return status; } QDF_STATUS wma_vdev_detach(struct del_vdev_params *pdel_vdev_req_param) { QDF_STATUS status = QDF_STATUS_SUCCESS; uint8_t vdev_id; struct wma_txrx_node *iface = NULL; tp_wma_handle wma_handle = cds_get_context(QDF_MODULE_ID_WMA); if (!wma_handle) return QDF_STATUS_E_INVAL; vdev_id = wlan_vdev_get_id(pdel_vdev_req_param->vdev); iface = &wma_handle->interfaces[vdev_id]; if (!iface->vdev) { wma_err("vdev %d is NULL", vdev_id); return status; } status = wma_check_for_deferred_peer_delete(wma_handle, pdel_vdev_req_param); if (QDF_IS_STATUS_ERROR(status)) goto send_fail_rsp; if (iface->is_del_sta_deferred) return status; iface->is_del_sta_deferred = false; iface->del_staself_req = NULL; status = wma_vdev_self_peer_delete(wma_handle, pdel_vdev_req_param); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to send self peer delete:%d", status); status = QDF_STATUS_E_INVAL; return status; } if (iface->type != WMI_VDEV_TYPE_MONITOR) iface->vdev_active = false; if (!mlme_vdev_uses_self_peer(iface->type, iface->sub_type) || !wmi_service_enabled(wma_handle->wmi_handle, wmi_service_sync_delete_cmds)) { status = wma_handle_vdev_detach(wma_handle, pdel_vdev_req_param); pdel_vdev_req_param->status = status; mlme_vdev_self_peer_delete_resp(pdel_vdev_req_param); } return status; send_fail_rsp: wma_err("rcvd del_self_sta without del_bss; vdev_id:%d", vdev_id); cds_trigger_recovery(QDF_DEL_SELF_STA_FAILED); status = QDF_STATUS_E_FAILURE; return status; } /** * wma_send_start_resp() - send vdev start response to upper layer * @wma: wma handle * @add_bss_rsp: add bss params * @rsp: response params * * Return: none */ static void wma_send_start_resp(tp_wma_handle wma, struct add_bss_rsp *add_bss_rsp, struct vdev_start_response *rsp) { struct wma_txrx_node *iface = &wma->interfaces[rsp->vdev_id]; QDF_STATUS status; if (QDF_IS_STATUS_SUCCESS(rsp->status) && QDF_IS_STATUS_SUCCESS(add_bss_rsp->status)) { status = wlan_vdev_mlme_sm_deliver_evt(iface->vdev, WLAN_VDEV_SM_EV_START_RESP, sizeof(*add_bss_rsp), add_bss_rsp); if (QDF_IS_STATUS_SUCCESS(status)) return; add_bss_rsp->status = status; } /* Send vdev stop if vdev start was success */ if (QDF_IS_STATUS_ERROR(add_bss_rsp->status) && QDF_IS_STATUS_SUCCESS(rsp->status)) { wlan_vdev_mlme_sm_deliver_evt(iface->vdev, WLAN_VDEV_SM_EV_DOWN, sizeof(*add_bss_rsp), add_bss_rsp); return; } wma_remove_bss_peer_on_failure(wma, rsp->vdev_id); wma_debug("Sending add bss rsp to umac(vdev %d status %d)", rsp->vdev_id, add_bss_rsp->status); lim_handle_add_bss_rsp(wma->mac_context, add_bss_rsp); } /** * wma_vdev_start_rsp() - send vdev start response to upper layer * @wma: wma handle * @vdev: vdev * @rsp: response params * * Return: none */ static void wma_vdev_start_rsp(tp_wma_handle wma, struct wlan_objmgr_vdev *vdev, struct vdev_start_response *rsp) { struct beacon_info *bcn; enum QDF_OPMODE opmode; struct add_bss_rsp *add_bss_rsp; opmode = wlan_vdev_mlme_get_opmode(vdev); add_bss_rsp = qdf_mem_malloc(sizeof(*add_bss_rsp)); if (!add_bss_rsp) return; add_bss_rsp->vdev_id = rsp->vdev_id; add_bss_rsp->status = rsp->status; add_bss_rsp->chain_mask = rsp->chain_mask; add_bss_rsp->smps_mode = host_map_smps_mode(rsp->smps_mode); if (rsp->status) goto send_fail_resp; if (opmode == QDF_P2P_GO_MODE || opmode == QDF_SAP_MODE) { wma->interfaces[rsp->vdev_id].beacon = qdf_mem_malloc(sizeof(struct beacon_info)); bcn = wma->interfaces[rsp->vdev_id].beacon; if (!bcn) { add_bss_rsp->status = QDF_STATUS_E_NOMEM; goto send_fail_resp; } bcn->buf = qdf_nbuf_alloc(NULL, SIR_MAX_BEACON_SIZE, 0, sizeof(uint32_t), 0); if (!bcn->buf) { qdf_mem_free(bcn); add_bss_rsp->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[rsp->vdev_id].bss_status, WMA_BSS_STATUS_STARTED); wma_debug("AP mode (type %d subtype %d) BSS is started", wma->interfaces[rsp->vdev_id].type, wma->interfaces[rsp->vdev_id].sub_type); } send_fail_resp: wma_send_start_resp(wma, add_bss_rsp, rsp); } #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_is_vdev_up(vdev_id) || (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].ch_freq != prev_ch_freq) { num_ch++; prev_ch_freq = wma->interfaces[i].ch_freq; } } } 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 */ /** * wma_handle_hidden_ssid_restart() - handle hidden ssid restart * @wma: wma handle * @iface: interface pointer * * Return: none */ static void wma_handle_hidden_ssid_restart(tp_wma_handle wma, struct wma_txrx_node *iface) { wlan_vdev_mlme_sm_deliver_evt(iface->vdev, WLAN_VDEV_SM_EV_RESTART_RESP, 0, NULL); } #ifdef WLAN_FEATURE_11BE /** * wma_get_peer_phymode() - get phy mode and eht puncture * @nw_type: wlan type * @old_peer_phymode: old peer phy mode * @vdev_chan: vdev channel * @is_eht: is eht mode * @puncture_bitmap: eht puncture bitmap * * Return: new wlan phy mode */ static enum wlan_phymode wma_get_peer_phymode(tSirNwType nw_type, enum wlan_phymode old_peer_phymode, struct wlan_channel *vdev_chan, bool *is_eht, uint16_t *puncture_bitmap) { enum wlan_phymode new_phymode; new_phymode = wma_peer_phymode(nw_type, STA_ENTRY_PEER, IS_WLAN_PHYMODE_HT(old_peer_phymode), vdev_chan->ch_width, IS_WLAN_PHYMODE_VHT(old_peer_phymode), IS_WLAN_PHYMODE_HE(old_peer_phymode), IS_WLAN_PHYMODE_EHT(old_peer_phymode)); *is_eht = IS_WLAN_PHYMODE_EHT(new_phymode); if (*is_eht) *puncture_bitmap = vdev_chan->puncture_bitmap; return new_phymode; } #else static enum wlan_phymode wma_get_peer_phymode(tSirNwType nw_type, enum wlan_phymode old_peer_phymode, struct wlan_channel *vdev_chan, bool *is_eht, uint16_t *puncture_bitmap) { enum wlan_phymode new_phymode; new_phymode = wma_peer_phymode(nw_type, STA_ENTRY_PEER, IS_WLAN_PHYMODE_HT(old_peer_phymode), vdev_chan->ch_width, IS_WLAN_PHYMODE_VHT(old_peer_phymode), IS_WLAN_PHYMODE_HE(old_peer_phymode), 0); return new_phymode; } #endif static void wma_peer_send_phymode(struct wlan_objmgr_vdev *vdev, void *object, void *arg) { struct wlan_objmgr_peer *peer = object; enum wlan_phymode old_peer_phymode; struct wlan_channel *vdev_chan; enum wlan_phymode new_phymode; tSirNwType nw_type; uint32_t fw_phymode; uint32_t max_ch_width_supported; tp_wma_handle wma; uint8_t *peer_mac_addr; uint8_t vdev_id; bool is_eht = false; uint16_t puncture_bitmap = 0; uint16_t new_puncture_bitmap = 0; uint32_t bw_puncture = 0; enum phy_ch_width new_bw; if (wlan_peer_get_peer_type(peer) == WLAN_PEER_SELF) return; wma = cds_get_context(QDF_MODULE_ID_WMA); if(!wma) return; old_peer_phymode = wlan_peer_get_phymode(peer); vdev_chan = wlan_vdev_mlme_get_des_chan(vdev); peer_mac_addr = wlan_peer_get_macaddr(peer); if (WLAN_REG_IS_24GHZ_CH_FREQ(vdev_chan->ch_freq)) { if (vdev_chan->ch_phymode == WLAN_PHYMODE_11B || old_peer_phymode == WLAN_PHYMODE_11B) nw_type = eSIR_11B_NW_TYPE; else nw_type = eSIR_11G_NW_TYPE; } else { nw_type = eSIR_11A_NW_TYPE; } new_phymode = wma_get_peer_phymode(nw_type, old_peer_phymode, vdev_chan, &is_eht, &puncture_bitmap); if (!is_eht && new_phymode == old_peer_phymode) { wma_debug("Ignore update as old %d and new %d phymode are same for mac "QDF_MAC_ADDR_FMT, old_peer_phymode, new_phymode, QDF_MAC_ADDR_REF(peer_mac_addr)); return; } wlan_peer_set_phymode(peer, new_phymode); fw_phymode = wmi_host_to_fw_phymode(new_phymode); vdev_id = wlan_vdev_get_id(vdev); max_ch_width_supported = wmi_get_ch_width_from_phy_mode(wma->wmi_handle, fw_phymode); new_bw = target_if_wmi_chan_width_to_phy_ch_width(max_ch_width_supported); if (is_eht) { wlan_reg_extract_puncture_by_bw(vdev_chan->ch_width, puncture_bitmap, vdev_chan->ch_freq, vdev_chan->ch_freq_seg2, new_bw, &new_puncture_bitmap); QDF_SET_BITS(bw_puncture, 0, 8, new_bw); QDF_SET_BITS(bw_puncture, 8, 16, new_puncture_bitmap); wlan_util_vdev_peer_set_param_send(vdev, peer_mac_addr, WLAN_MLME_PEER_BW_PUNCTURE, bw_puncture); } else { wma_set_peer_param(wma, peer_mac_addr, WMI_HOST_PEER_CHWIDTH, max_ch_width_supported, vdev_id); } wma_set_peer_param(wma, peer_mac_addr, WMI_HOST_PEER_PHYMODE, fw_phymode, vdev_id); wma_debug("FW phymode %d old phymode %d new phymode %d bw %d punct: 0x%x macaddr " QDF_MAC_ADDR_FMT, fw_phymode, old_peer_phymode, new_phymode, max_ch_width_supported, new_puncture_bitmap, QDF_MAC_ADDR_REF(peer_mac_addr)); } static void wma_update_rate_flags_after_vdev_restart(tp_wma_handle wma, struct wma_txrx_node *iface) { struct vdev_mlme_obj *vdev_mlme; uint32_t rate_flags = 0; enum wlan_phymode bss_phymode; struct wlan_channel *des_chan; if (iface->type != WMI_VDEV_TYPE_STA) return; vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(iface->vdev); if (!vdev_mlme) return; des_chan = wlan_vdev_mlme_get_des_chan(iface->vdev); bss_phymode = des_chan->ch_phymode; if (wma_is_eht_phymode_supported(bss_phymode)) { rate_flags = wma_get_eht_rate_flags(des_chan->ch_width); } else if (IS_WLAN_PHYMODE_HE(bss_phymode)) { rate_flags = wma_get_he_rate_flags(des_chan->ch_width); } else if (IS_WLAN_PHYMODE_VHT(bss_phymode)) { rate_flags = wma_get_vht_rate_flags(des_chan->ch_width); } else if (IS_WLAN_PHYMODE_HT(bss_phymode)) { rate_flags = wma_get_ht_rate_flags(des_chan->ch_width); } else { rate_flags = TX_RATE_LEGACY; } vdev_mlme->mgmt.rate_info.rate_flags = rate_flags; wma_debug("bss phymode %d rate_flags %x, ch_width %d", bss_phymode, rate_flags, des_chan->ch_width); ucfg_mc_cp_stats_set_rate_flags(iface->vdev, rate_flags); } QDF_STATUS wma_handle_channel_switch_resp(tp_wma_handle wma, struct vdev_start_response *rsp) { enum wlan_vdev_sm_evt event; struct wma_txrx_node *iface; iface = &wma->interfaces[rsp->vdev_id]; wma_debug("Send channel switch resp vdev %d status %d", rsp->vdev_id, rsp->status); /* Indicate channel switch failure to LIM */ if (QDF_IS_STATUS_ERROR(rsp->status) && (iface->type == WMI_VDEV_TYPE_MONITOR || wma_is_vdev_in_ap_mode(wma, rsp->vdev_id) || mlme_is_chan_switch_in_progress(iface->vdev))) { mlme_set_chan_switch_in_progress(iface->vdev, false); lim_process_switch_channel_rsp(wma->mac_context, rsp); return QDF_STATUS_SUCCESS; } if (QDF_IS_STATUS_SUCCESS(rsp->status) && rsp->resp_type == WMI_VDEV_RESTART_RESP_EVENT) { wlan_objmgr_iterate_peerobj_list(iface->vdev, wma_peer_send_phymode, NULL, WLAN_LEGACY_WMA_ID); wma_update_rate_flags_after_vdev_restart(wma, iface); } if (wma_is_vdev_in_ap_mode(wma, rsp->vdev_id) || mlme_is_chan_switch_in_progress(iface->vdev)) event = WLAN_VDEV_SM_EV_RESTART_RESP; else event = WLAN_VDEV_SM_EV_START_RESP; wlan_vdev_mlme_sm_deliver_evt(iface->vdev, event, sizeof(rsp), rsp); return QDF_STATUS_SUCCESS; } #ifdef DCS_INTERFERENCE_DETECTION /** * wma_dcs_clear_vdev_starting() - clear vdev starting within dcs information * @mac_ctx: mac context * @vdev_id: vdev id * * This function is used to clear vdev starting within dcs information * * Return: None */ static void wma_dcs_clear_vdev_starting(struct mac_context *mac_ctx, uint32_t vdev_id) { mac_ctx->sap.dcs_info.is_vdev_starting[vdev_id] = false; } /** * wma_dcs_wlan_interference_mitigation_enable() - enable wlan * interference mitigation * @mac_ctx: mac context * @mac_id: mac id * @rsp: vdev start response * * This function is used to enable wlan interference mitigation through * send dcs command * * Return: None */ static void wma_dcs_wlan_interference_mitigation_enable( struct mac_context *mac_ctx, uint32_t mac_id, struct vdev_start_response *rsp) { int vdev_index; uint32_t list[MAX_NUMBER_OF_CONC_CONNECTIONS]; uint32_t count; bool wlan_interference_mitigation_enable = mac_ctx->sap.dcs_info. wlan_interference_mitigation_enable[rsp->vdev_id]; count = policy_mgr_get_sap_go_count_on_mac( mac_ctx->psoc, list, mac_id); for (vdev_index = 0; vdev_index < count; vdev_index++) { if (mac_ctx->sap.dcs_info.is_vdev_starting[list[vdev_index]]) { wma_err("vdev %d: does not finish restart", list[vdev_index]); return; } wlan_interference_mitigation_enable = wlan_interference_mitigation_enable || mac_ctx->sap.dcs_info. wlan_interference_mitigation_enable[list[vdev_index]]; } if (wlan_interference_mitigation_enable) ucfg_config_dcs_event_data(mac_ctx->psoc, mac_id, true); if (rsp->resp_type == WMI_HOST_VDEV_START_RESP_EVENT) { ucfg_config_dcs_enable(mac_ctx->psoc, mac_id, WLAN_HOST_DCS_WLANIM); ucfg_wlan_dcs_cmd(mac_ctx->psoc, mac_id, true); } } #else static void wma_dcs_wlan_interference_mitigation_enable( struct mac_context *mac_ctx, uint32_t mac_id, struct vdev_start_response *rsp) { } static void wma_dcs_clear_vdev_starting(struct mac_context *mac_ctx, uint32_t vdev_id) { } #endif /* * wma_get_ratemask_type() - convert user input ratemask type to FW type * @type: User input ratemask type maintained in HDD * @fwtype: Value return arg for fw ratemask type value * * Return: FW configurable ratemask type */ static QDF_STATUS wma_get_ratemask_type(enum wlan_mlme_ratemask_type type, uint8_t *fwtype) { switch (type) { case WLAN_MLME_RATEMASK_TYPE_CCK: *fwtype = WMI_RATEMASK_TYPE_CCK; break; case WLAN_MLME_RATEMASK_TYPE_HT: *fwtype = WMI_RATEMASK_TYPE_HT; break; case WLAN_MLME_RATEMASK_TYPE_VHT: *fwtype = WMI_RATEMASK_TYPE_VHT; break; case WLAN_MLME_RATEMASK_TYPE_HE: *fwtype = WMI_RATEMASK_TYPE_HE; break; default: return QDF_STATUS_E_INVAL; } return QDF_STATUS_SUCCESS; } QDF_STATUS wma_vdev_start_resp_handler(struct vdev_mlme_obj *vdev_mlme, struct vdev_start_response *rsp) { tp_wma_handle wma; struct wma_txrx_node *iface; target_resource_config *wlan_res_cfg; struct wlan_objmgr_psoc *psoc; struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_PE); QDF_STATUS status; enum vdev_assoc_type assoc_type = VDEV_ASSOC; struct vdev_mlme_obj *mlme_obj; struct wlan_mlme_psoc_ext_obj *mlme_psoc_obj; const struct wlan_mlme_ratemask *ratemask_cfg; struct config_ratemask_params rparams = {0}; wma = cds_get_context(QDF_MODULE_ID_WMA); if (!wma) return QDF_STATUS_E_FAILURE; psoc = wma->psoc; if (!psoc) { wma_err("psoc is NULL"); return QDF_STATUS_E_FAILURE; } mlme_psoc_obj = mlme_get_psoc_ext_obj(psoc); if (!mlme_psoc_obj) { wma_err("Failed to get mlme_psoc"); return QDF_STATUS_E_FAILURE; } ratemask_cfg = &mlme_psoc_obj->cfg.ratemask_cfg; if (!mac_ctx) { wma_err("Failed to get mac_ctx"); return QDF_STATUS_E_FAILURE; } wlan_res_cfg = lmac_get_tgt_res_cfg(psoc); if (!wlan_res_cfg) { wma_err("Wlan resource config is NULL"); return QDF_STATUS_E_FAILURE; } if (rsp->vdev_id >= wma->max_bssid) { wma_err("Invalid vdev id received from firmware"); return QDF_STATUS_E_FAILURE; } if (wma_is_vdev_in_ap_mode(wma, rsp->vdev_id)) tgt_dfs_radar_enable(wma->pdev, 0, 0, true); iface = &wma->interfaces[rsp->vdev_id]; if (!iface->vdev) { wma_err("Invalid vdev"); return QDF_STATUS_E_FAILURE; } if (rsp->status == QDF_STATUS_SUCCESS) { wma->interfaces[rsp->vdev_id].tx_streams = rsp->cfgd_tx_streams; if (wlan_res_cfg->use_pdev_id) { if (rsp->mac_id == OL_TXRX_PDEV_ID) { wma_err("soc level id received for mac id"); return -QDF_STATUS_E_INVAL; } wma->interfaces[rsp->vdev_id].mac_id = WMA_PDEV_TO_MAC_MAP(rsp->mac_id); } else { wma->interfaces[rsp->vdev_id].mac_id = rsp->mac_id; } wma_debug("vdev:%d tx ss=%d rx ss=%d chain mask=%d mac=%d", rsp->vdev_id, rsp->cfgd_tx_streams, rsp->cfgd_rx_streams, rsp->chain_mask, wma->interfaces[rsp->vdev_id].mac_id); /* Fill bss_chan after vdev start */ qdf_mem_copy(iface->vdev->vdev_mlme.bss_chan, iface->vdev->vdev_mlme.des_chan, sizeof(struct wlan_channel)); } if (wma_is_vdev_in_ap_mode(wma, rsp->vdev_id)) { wma_dcs_clear_vdev_starting(mac_ctx, rsp->vdev_id); wma_dcs_wlan_interference_mitigation_enable(mac_ctx, iface->mac_id, rsp); } #ifdef FEATURE_AP_MCC_CH_AVOIDANCE if (rsp->status == QDF_STATUS_SUCCESS && mac_ctx->sap.sap_channel_avoidance) wma_find_mcc_ap(wma, rsp->vdev_id, true); #endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ if (wma_get_hidden_ssid_restart_in_progress(iface) && wma_is_vdev_in_ap_mode(wma, rsp->vdev_id)) { wma_handle_hidden_ssid_restart(wma, iface); return QDF_STATUS_SUCCESS; } mlme_obj = wlan_vdev_mlme_get_cmpt_obj(iface->vdev); if (!mlme_obj) return QDF_STATUS_E_INVAL; mlme_obj->mgmt.generic.tx_pwrlimit = rsp->max_allowed_tx_power; wma_debug("Max allowed tx power: %d", rsp->max_allowed_tx_power); if (iface->type == WMI_VDEV_TYPE_STA) assoc_type = mlme_get_assoc_type(vdev_mlme->vdev); if (mlme_is_chan_switch_in_progress(iface->vdev) || iface->type == WMI_VDEV_TYPE_MONITOR || (iface->type == WMI_VDEV_TYPE_STA && (assoc_type == VDEV_ASSOC || assoc_type == VDEV_REASSOC))) { status = wma_handle_channel_switch_resp(wma, rsp); if (QDF_IS_STATUS_ERROR(status)) return QDF_STATUS_E_FAILURE; } else if (iface->type == WMI_VDEV_TYPE_OCB) { mlme_obj->proto.sta.assoc_id = iface->aid; if (vdev_mgr_up_send(mlme_obj) != QDF_STATUS_SUCCESS) { wma_err("failed to send vdev up"); return QDF_STATUS_E_FAILURE; } ucfg_ocb_config_channel(wma->pdev); } else { struct qdf_mac_addr bss_peer; status = wlan_vdev_get_bss_peer_mac(iface->vdev, &bss_peer); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to get bssid"); return QDF_STATUS_E_INVAL; } qdf_mem_copy(mlme_obj->mgmt.generic.bssid, bss_peer.bytes, QDF_MAC_ADDR_SIZE); wma_vdev_start_rsp(wma, vdev_mlme->vdev, rsp); } if (iface->type == WMI_VDEV_TYPE_AP && wma_is_vdev_up(rsp->vdev_id)) wma_set_sap_keepalive(wma, rsp->vdev_id); /* Send ratemask to firmware */ if ((ratemask_cfg->type > WLAN_MLME_RATEMASK_TYPE_NO_MASK) && (ratemask_cfg->type < WLAN_MLME_RATEMASK_TYPE_MAX)) { struct wmi_unified *wmi_handle = wma->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; rparams.vdev_id = rsp->vdev_id; status = wma_get_ratemask_type(ratemask_cfg->type, &rparams.type); if (QDF_IS_STATUS_ERROR(status)) { wma_err(FL("unable to map ratemask")); /* don't fail, default rates will still work */ return QDF_STATUS_SUCCESS; } rparams.lower32 = ratemask_cfg->lower32; rparams.higher32 = ratemask_cfg->higher32; rparams.lower32_2 = ratemask_cfg->lower32_2; rparams.higher32_2 = ratemask_cfg->higher32_2; status = wmi_unified_vdev_config_ratemask_cmd_send(wmi_handle, &rparams); /* Only log failure. Do not abort */ if (QDF_IS_STATUS_ERROR(status)) wma_err(FL("failed to send ratemask")); } return QDF_STATUS_SUCCESS; } bool wma_is_vdev_valid(uint32_t vdev_id) { tp_wma_handle wma_handle = cds_get_context(QDF_MODULE_ID_WMA); if (!wma_handle) return false; /* No of interface are allocated based on max_bssid value */ if (vdev_id >= wma_handle->max_bssid) { wma_debug("vdev_id: %d is invalid, max_bssid: %d", vdev_id, wma_handle->max_bssid); return false; } return wma_handle->interfaces[vdev_id].vdev_active; } /** * wma_vdev_set_param() - set per vdev params in fw * @wmi_handle: wmi handle * @if_id: 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_err("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.vdev_id = if_id; param.param_id = param_id; param.param_value = param_value; return wmi_unified_vdev_set_param_send(wmi_handle, ¶m); } /** * 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}; QDF_STATUS status; param.vdev_id = vdev_id; param.param_value = param_value; param.param_id = param_id; status = wmi_set_peer_param_send(wma_handle->wmi_handle, peer_addr, ¶m); if (QDF_IS_STATUS_ERROR(status)) wma_err("vdev_id: %d peer set failed, id %d, val %d", vdev_id, param_id, param_value); return status; } /** * wma_peer_unmap_conf_send - send peer unmap conf cmnd to fw * @wma: wma handle * @msg: peer unmap conf params * * Return: QDF_STATUS */ QDF_STATUS wma_peer_unmap_conf_send(tp_wma_handle wma, struct send_peer_unmap_conf_params *msg) { QDF_STATUS qdf_status; if (!msg) { wma_err("null input params"); return QDF_STATUS_E_INVAL; } qdf_status = wmi_unified_peer_unmap_conf_send( wma->wmi_handle, msg->vdev_id, msg->peer_id_cnt, msg->peer_id_list); if (qdf_status != QDF_STATUS_SUCCESS) wma_err("peer_unmap_conf_send failed %d", qdf_status); qdf_mem_free(msg->peer_id_list); msg->peer_id_list = NULL; return qdf_status; } /** * wma_peer_unmap_conf_cb - send peer unmap conf cmnd to fw * @vdev_id: vdev id * @peer_id_cnt: no of peer id * @peer_id_list: list of peer ids * * Return: QDF_STATUS */ QDF_STATUS wma_peer_unmap_conf_cb(uint8_t vdev_id, uint32_t peer_id_cnt, uint16_t *peer_id_list) { tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); QDF_STATUS qdf_status; if (!wma) return QDF_STATUS_E_INVAL; wma_debug("peer_id_cnt: %d", peer_id_cnt); qdf_status = wmi_unified_peer_unmap_conf_send( wma->wmi_handle, vdev_id, peer_id_cnt, peer_id_list); if (qdf_status == QDF_STATUS_E_BUSY) { QDF_STATUS retcode; struct scheduler_msg msg = {0}; struct send_peer_unmap_conf_params *peer_unmap_conf_req; void *mac_ctx = cds_get_context(QDF_MODULE_ID_PE); wma_debug("post unmap_conf cmd to MC thread"); if (!mac_ctx) return QDF_STATUS_E_FAILURE; peer_unmap_conf_req = qdf_mem_malloc(sizeof( struct send_peer_unmap_conf_params)); if (!peer_unmap_conf_req) return QDF_STATUS_E_NOMEM; peer_unmap_conf_req->vdev_id = vdev_id; peer_unmap_conf_req->peer_id_cnt = peer_id_cnt; peer_unmap_conf_req->peer_id_list = qdf_mem_malloc( sizeof(uint16_t) * peer_id_cnt); if (!peer_unmap_conf_req->peer_id_list) { qdf_mem_free(peer_unmap_conf_req); peer_unmap_conf_req = NULL; return QDF_STATUS_E_NOMEM; } qdf_mem_copy(peer_unmap_conf_req->peer_id_list, peer_id_list, sizeof(uint16_t) * peer_id_cnt); msg.type = WMA_SEND_PEER_UNMAP_CONF; msg.reserved = 0; msg.bodyptr = peer_unmap_conf_req; msg.bodyval = 0; retcode = wma_post_ctrl_msg(mac_ctx, &msg); if (retcode != QDF_STATUS_SUCCESS) { wma_err("wma_post_ctrl_msg failed"); qdf_mem_free(peer_unmap_conf_req->peer_id_list); qdf_mem_free(peer_unmap_conf_req); return QDF_STATUS_E_FAILURE; } } return qdf_status; } bool wma_objmgr_peer_exist(tp_wma_handle wma, uint8_t *peer_addr, uint8_t *peer_vdev_id) { struct wlan_objmgr_peer *peer; if (!peer_addr || qdf_is_macaddr_zero((struct qdf_mac_addr *)peer_addr)) return false; peer = wlan_objmgr_get_peer_by_mac(wma->psoc, peer_addr, WLAN_LEGACY_WMA_ID); if (!peer) return false; if (peer_vdev_id) *peer_vdev_id = wlan_vdev_get_id(wlan_peer_get_vdev(peer)); wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); return true; } #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE void wma_peer_tbl_trans_add_entry(struct wlan_objmgr_peer *peer, bool is_create, struct cdp_peer_setup_info *peer_info) { QDF_STATUS status; struct wlan_objmgr_vdev *vdev; struct wlan_objmgr_psoc *psoc; struct wlan_peer_tbl_trans_entry *peer_trans_entry; uint8_t *peer_mac, *peer_mld; vdev = wlan_peer_get_vdev(peer); if (!vdev) return; psoc = wlan_vdev_get_psoc(vdev); if (!psoc) return; peer_trans_entry = qdf_mem_malloc(sizeof(*peer_trans_entry)); if (!peer_trans_entry) return; peer_trans_entry->ts = qdf_get_log_timestamp(); peer_trans_entry->vdev_id = wlan_vdev_get_id(vdev); peer_trans_entry->opmode = wlan_vdev_mlme_get_opmode(vdev); peer_mac = wlan_peer_get_macaddr(peer); peer_mld = wlan_peer_mlme_get_mldaddr(peer); qdf_ether_addr_copy(&peer_trans_entry->peer_addr.bytes[0], peer_mac); if (peer_mld) { qdf_ether_addr_copy(&peer_trans_entry->peer_mld_addr.bytes[0], peer_mld); } peer_trans_entry->is_mlo = wlan_vdev_mlme_is_mlo_vdev(vdev); peer_trans_entry->is_mlo_link = wlan_vdev_mlme_is_mlo_link_vdev(vdev); if (wlan_cm_is_vdev_roam_sync_inprogress(vdev)) { peer_trans_entry->peer_flags |= WLAN_PEER_TBL_TRANS_ROAM; peer_trans_entry->auth_status = wlan_cm_check_mlo_roam_auth_status(vdev); peer_trans_entry->num_roam_links = mlo_mgr_num_roam_links(vdev); } else if (wlan_vdev_mlme_is_mlo_link_switch_in_progress(vdev)) { peer_trans_entry->peer_flags |= WLAN_PEER_TBL_TRANS_LINK_SWITCH; } else if (is_create) { peer_trans_entry->peer_flags |= WLAN_PEER_TBL_TRANS_CONNECT; } else { peer_trans_entry->peer_flags |= WLAN_PEER_TBL_TRANS_DISCONNECT; } if (is_create) { peer_trans_entry->peer_flags |= WLAN_PEER_TBL_TRANS_CREATE; peer_trans_entry->is_primary = peer_info->is_primary_link; peer_trans_entry->is_first_link = peer_info->is_first_link; } else { peer_trans_entry->peer_flags |= WLAN_PEER_TBL_TRANS_DESTROY; } status = wlan_mlme_psoc_peer_tbl_trans_add_entry(psoc, peer_trans_entry); if (QDF_IS_STATUS_ERROR(status)) qdf_mem_free(peer_trans_entry); } #endif /** * wma_remove_peer() - remove peer information from host driver and fw * @wma: wma handle * @mac_addr: peer mac address, to be removed * @vdev_id: vdev id * @no_fw_peer_delete: If true dont send peer delete to firmware * * Return: QDF_STATUS */ QDF_STATUS wma_remove_peer(tp_wma_handle wma, uint8_t *mac_addr, uint8_t vdev_id, bool no_fw_peer_delete) { #define PEER_ALL_TID_BITMASK 0xffffffff uint32_t peer_tid_bitmap = PEER_ALL_TID_BITMASK; uint8_t peer_addr[QDF_MAC_ADDR_SIZE] = {0}; struct peer_flush_params param = {0}; void *soc = cds_get_context(QDF_MODULE_ID_SOC); QDF_STATUS qdf_status = QDF_STATUS_SUCCESS; uint32_t bitmap = 1 << CDP_PEER_DELETE_NO_SPECIAL; bool peer_unmap_conf_support_enabled; uint8_t peer_vdev_id; struct peer_delete_cmd_params del_param = {0}; struct wma_txrx_node *iface; struct wlan_objmgr_peer *peer; if (vdev_id >= WLAN_MAX_VDEVS) { wma_err("Invalid vdev_id %d", vdev_id); QDF_BUG(0); return QDF_STATUS_E_INVAL; } qdf_mem_copy(peer_addr, mac_addr, QDF_MAC_ADDR_SIZE); iface = &wma->interfaces[vdev_id]; if (!iface->peer_count) { wma_err("Can't remove peer with peer_addr "QDF_MAC_ADDR_FMT" vdevid %d peer_count %d", QDF_MAC_ADDR_REF(peer_addr), vdev_id, iface->peer_count); QDF_ASSERT(0); return QDF_STATUS_E_INVAL; } if (!soc) { QDF_BUG(0); return QDF_STATUS_E_INVAL; } peer = wlan_objmgr_get_peer_by_mac(wma->psoc, peer_addr, WLAN_LEGACY_WMA_ID); if (!peer) { wma_err("peer doesn't exist peer_addr "QDF_MAC_ADDR_FMT" vdevid %d peer_count %d", QDF_MAC_ADDR_REF(peer_addr), vdev_id, iface->peer_count); return QDF_STATUS_E_INVAL; } peer_vdev_id = wlan_vdev_get_id(wlan_peer_get_vdev(peer)); if (peer_vdev_id != vdev_id) { wma_err("peer "QDF_MAC_ADDR_FMT" is on vdev id %d but delete req on vdevid %d peer_count %d", QDF_MAC_ADDR_REF(peer_addr), peer_vdev_id, vdev_id, iface->peer_count); wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); return QDF_STATUS_E_INVAL; } wma_peer_tbl_trans_add_entry(peer, false, NULL); peer_unmap_conf_support_enabled = cdp_cfg_get_peer_unmap_conf_support(soc); cdp_peer_teardown(soc, vdev_id, peer_addr); if (no_fw_peer_delete) 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; if (!wmi_service_enabled(wma->wmi_handle, wmi_service_peer_delete_no_peer_flush_tids_cmd)) wmi_unified_peer_flush_tids_send(wma->wmi_handle, peer_addr, ¶m); /* peer->ref_cnt is not visible in WMA */ wlan_roam_debug_log(vdev_id, DEBUG_PEER_DELETE_SEND, DEBUG_INVALID_PEER_ID, peer_addr, NULL, 0, 0); del_param.vdev_id = vdev_id; del_param.is_mlo_link_switch = wlan_vdev_mlme_is_mlo_link_switch_in_progress(iface->vdev); qdf_status = wmi_unified_peer_delete_send(wma->wmi_handle, peer_addr, &del_param); if (QDF_IS_STATUS_ERROR(qdf_status)) { wma_err("Peer delete could not be sent to firmware %d", qdf_status); /* Clear default bit and set to NOT_START_UNMAP */ bitmap = 1 << CDP_PEER_DO_NOT_START_UNMAP_TIMER; qdf_status = QDF_STATUS_E_FAILURE; } peer_detach: wma_debug("vdevid %d is detaching with peer_addr "QDF_MAC_ADDR_FMT" peer_count %d", vdev_id, QDF_MAC_ADDR_REF(peer_addr), iface->peer_count); if (no_fw_peer_delete && is_cdp_peer_detach_force_delete_supported(soc)) { if (!peer_unmap_conf_support_enabled) { wma_debug("LFR3: trigger force delete for peer "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(peer_addr)); cdp_peer_detach_force_delete(soc, vdev_id, peer_addr); } else { cdp_peer_delete_sync(soc, vdev_id, peer_addr, wma_peer_unmap_conf_cb, bitmap); } } else { if (no_fw_peer_delete) wma_debug("LFR3: Delete the peer "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(peer_addr)); if (peer_unmap_conf_support_enabled) cdp_peer_delete_sync(soc, vdev_id, peer_addr, wma_peer_unmap_conf_cb, bitmap); else cdp_peer_delete(soc, vdev_id, peer_addr, bitmap); } wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); wlan_release_peer_key_wakelock(wma->pdev, peer_addr); wma_remove_objmgr_peer(wma, iface->vdev, peer_addr); iface->peer_count--; #undef PEER_ALL_TID_BITMASK return qdf_status; } /** * wma_get_obj_mgr_peer_type() - Determine the type of peer(eg. STA/AP) * @wma: wma handle * @vdev_id: vdev id * @peer_addr: peer mac address * @wma_peer_type: wma peer type * * Return: Peer type */ static int wma_get_obj_mgr_peer_type(tp_wma_handle wma, uint8_t vdev_id, uint8_t *peer_addr, uint32_t wma_peer_type) { uint32_t obj_peer_type = 0; struct wlan_objmgr_vdev *vdev; uint8_t *addr; uint8_t *mld_addr; vdev = wma->interfaces[vdev_id].vdev; if (!vdev) { wma_err("Couldn't find vdev for VDEV_%d", vdev_id); return obj_peer_type; } addr = wlan_vdev_mlme_get_macaddr(vdev); mld_addr = wlan_vdev_mlme_get_mldaddr(vdev); if (wma_peer_type == WMI_PEER_TYPE_TDLS) return WLAN_PEER_TDLS; if (wma_peer_type == WMI_PEER_TYPE_PASN) return WLAN_PEER_RTT_PASN; if (!qdf_mem_cmp(addr, peer_addr, QDF_MAC_ADDR_SIZE) || !qdf_mem_cmp(mld_addr, peer_addr, QDF_MAC_ADDR_SIZE)) { obj_peer_type = WLAN_PEER_SELF; } else if (wma->interfaces[vdev_id].type == WMI_VDEV_TYPE_STA) { if (wma->interfaces[vdev_id].sub_type == WMI_UNIFIED_VDEV_SUBTYPE_P2P_CLIENT) obj_peer_type = WLAN_PEER_P2P_GO; else obj_peer_type = WLAN_PEER_AP; } else if (wma->interfaces[vdev_id].type == WMI_VDEV_TYPE_AP) { obj_peer_type = WLAN_PEER_STA; } else if (wma->interfaces[vdev_id].type == WMI_VDEV_TYPE_IBSS) { obj_peer_type = WLAN_PEER_IBSS; } else if (wma->interfaces[vdev_id].type == WMI_VDEV_TYPE_NDI) { obj_peer_type = WLAN_PEER_NDP; } else { wma_err("Couldn't find peertype for type %d and sub type %d", wma->interfaces[vdev_id].type, wma->interfaces[vdev_id].sub_type); } return obj_peer_type; } #ifdef WLAN_FEATURE_11BE_MLO static QDF_STATUS wma_create_peer_validate_mld_address(tp_wma_handle wma, uint8_t *peer_mld_addr, struct wlan_objmgr_vdev *vdev) { uint8_t peer_vdev_id, vdev_id; struct wlan_objmgr_vdev *dup_vdev; QDF_STATUS status = QDF_STATUS_SUCCESS; struct wlan_objmgr_psoc *psoc = wma->psoc; vdev_id = wlan_vdev_get_id(vdev); /* Check if the @peer_mld_addr matches any other * peer's link address. * We may find a match if one of the peers added * has same MLD and link, in such case check if * both are in same ML dev context. */ if (wma_objmgr_peer_exist(wma, peer_mld_addr, &peer_vdev_id)) { if (peer_vdev_id != vdev_id) { dup_vdev = wlan_objmgr_get_vdev_by_id_from_psoc( psoc, peer_vdev_id, WLAN_LEGACY_WMA_ID); if (!dup_vdev) return QDF_STATUS_E_INVAL; /* If ML dev context is NULL then the matching * peer exist on non ML VDEV, so reject the peer. */ if (!dup_vdev->mlo_dev_ctx) { wlan_objmgr_vdev_release_ref( dup_vdev, WLAN_LEGACY_WMA_ID); return QDF_STATUS_E_ALREADY; } else if (dup_vdev->mlo_dev_ctx != vdev->mlo_dev_ctx) { wma_debug("Peer " QDF_MAC_ADDR_FMT " already exists on vdev %d, current vdev %d", QDF_MAC_ADDR_REF(peer_mld_addr), peer_vdev_id, vdev_id); wlan_objmgr_vdev_release_ref( dup_vdev, WLAN_LEGACY_WMA_ID); status = QDF_STATUS_E_ALREADY; } else { wlan_objmgr_vdev_release_ref( dup_vdev, WLAN_LEGACY_WMA_ID); wma_debug("Allow ML peer on same ML dev context"); status = QDF_STATUS_SUCCESS; } } else { wma_debug("ML Peer exists on same VDEV %d", vdev_id); status = QDF_STATUS_E_ALREADY; } } else if (mlo_mgr_ml_peer_exist_on_diff_ml_ctx(peer_mld_addr, &vdev_id)) { /* Reject if MLD exists on different ML dev context, */ wma_debug("ML Peer " QDF_MAC_ADDR_FMT " already exists on different ML dev context", QDF_MAC_ADDR_REF(peer_mld_addr)); status = QDF_STATUS_E_ALREADY; } return status; } #else static QDF_STATUS wma_create_peer_validate_mld_address(tp_wma_handle wma, uint8_t *peer_mld_addr, struct wlan_objmgr_vdev *vdev) { return QDF_STATUS_SUCCESS; } #endif struct wlan_objmgr_peer *wma_create_objmgr_peer(tp_wma_handle wma, uint8_t vdev_id, uint8_t *peer_addr, uint32_t wma_peer_type, uint8_t *peer_mld_addr) { QDF_STATUS status; uint8_t peer_vdev_id; uint32_t obj_peer_type; struct wlan_objmgr_vdev *obj_vdev; struct wlan_objmgr_peer *obj_peer = NULL; struct wlan_objmgr_psoc *psoc = wma->psoc; obj_vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, WLAN_LEGACY_WMA_ID); if (!obj_vdev) { wma_err("Invalid obj vdev. Unable to create peer"); return NULL; } /* * Check if peer with same MAC exist on any Vdev, If so avoid * adding this peer. */ if (wma_objmgr_peer_exist(wma, peer_addr, &peer_vdev_id)) { wma_debug("Peer " QDF_MAC_ADDR_FMT " already exists on vdev %d, current vdev %d", QDF_MAC_ADDR_REF(peer_addr), peer_vdev_id, vdev_id); goto vdev_ref; } /* Reject if same MAC exists on different ML dev context */ if (mlo_mgr_ml_peer_exist_on_diff_ml_ctx(peer_addr, &vdev_id)) { wma_debug("Peer " QDF_MAC_ADDR_FMT " already exists on different ML dev context", QDF_MAC_ADDR_REF(peer_addr)); goto vdev_ref; } status = wma_create_peer_validate_mld_address(wma, peer_mld_addr, obj_vdev); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("MLD " QDF_MAC_ADDR_FMT " matches with peer on different MLD context", QDF_MAC_ADDR_REF(peer_mld_addr)); goto vdev_ref; } obj_peer_type = wma_get_obj_mgr_peer_type(wma, vdev_id, peer_addr, wma_peer_type); if (!obj_peer_type) { wma_err("Invalid obj peer type. Unable to create peer %d", obj_peer_type); goto vdev_ref; } /* Create obj_mgr peer */ obj_peer = wlan_objmgr_peer_obj_create(obj_vdev, obj_peer_type, peer_addr); vdev_ref: wlan_objmgr_vdev_release_ref(obj_vdev, WLAN_LEGACY_WMA_ID); return obj_peer; } /** * wma_increment_peer_count() - Increment the vdev peer * count * @wma: wma handle * @vdev_id: vdev id * * Return: None */ static void wma_increment_peer_count(tp_wma_handle wma, uint8_t vdev_id) { wma->interfaces[vdev_id].peer_count++; } /** * wma_update_mlo_peer_create() - update mlo parameter for peer creation * @param: peer create param * @mlo_enable: mlo enable or not * * Return: Void */ #ifdef WLAN_FEATURE_11BE_MLO static void wma_update_mlo_peer_create(struct peer_create_params *param, bool mlo_enable) { param->mlo_enabled = mlo_enable; } #else static void wma_update_mlo_peer_create(struct peer_create_params *param, bool mlo_enable) { } #endif /** * wma_add_peer() - send peer create command to fw * @wma: wma handle * @peer_addr: peer mac addr * @peer_type: peer type * @vdev_id: vdev id * @peer_mld_addr: peer mld addr * @is_assoc_peer: is assoc peer or not * * Return: QDF status */ static QDF_STATUS wma_add_peer(tp_wma_handle wma, uint8_t peer_addr[QDF_MAC_ADDR_SIZE], uint32_t peer_type, uint8_t vdev_id, uint8_t peer_mld_addr[QDF_MAC_ADDR_SIZE], bool is_assoc_peer) { struct peer_create_params param = {0}; void *dp_soc = cds_get_context(QDF_MODULE_ID_SOC); struct wlan_objmgr_psoc *psoc = wma->psoc; target_resource_config *wlan_res_cfg; struct wlan_objmgr_peer *obj_peer = NULL; QDF_STATUS status; if (!psoc) { wma_err("psoc is NULL"); return QDF_STATUS_E_INVAL; } wlan_res_cfg = lmac_get_tgt_res_cfg(psoc); if (!wlan_res_cfg) { wma_err("psoc target res cfg is null"); return QDF_STATUS_E_INVAL; } if (wma->interfaces[vdev_id].peer_count >= wlan_res_cfg->num_peers) { wma_err("the peer count exceeds the limit %d", wma->interfaces[vdev_id].peer_count); return QDF_STATUS_E_FAILURE; } if (!dp_soc) return QDF_STATUS_E_FAILURE; if (qdf_is_macaddr_group((struct qdf_mac_addr *)peer_addr) || qdf_is_macaddr_zero((struct qdf_mac_addr *)peer_addr)) { wma_err("Invalid peer address received reject it"); return QDF_STATUS_E_FAILURE; } obj_peer = wma_create_objmgr_peer(wma, vdev_id, peer_addr, peer_type, peer_mld_addr); if (!obj_peer) return QDF_STATUS_E_FAILURE; /* The peer object should be created before sending the WMI peer * create command to firmware. This is to prevent a race condition * where the HTT peer map event is received before the peer object * is created in the data path */ if (peer_mld_addr && !qdf_is_macaddr_zero((struct qdf_mac_addr *)peer_mld_addr)) { wlan_peer_mlme_flag_ext_set(obj_peer, WLAN_PEER_FEXT_MLO); wma_debug("peer " QDF_MAC_ADDR_FMT "is_assoc_peer%d mld mac " QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(peer_addr), is_assoc_peer, QDF_MAC_ADDR_REF(peer_mld_addr)); wlan_peer_mlme_set_mldaddr(obj_peer, peer_mld_addr); wlan_peer_mlme_set_assoc_peer(obj_peer, is_assoc_peer); wma_update_mlo_peer_create(¶m, true); } status = cdp_peer_create(dp_soc, vdev_id, peer_addr); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Unable to attach peer "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(peer_addr)); wlan_objmgr_peer_obj_delete(obj_peer); return QDF_STATUS_E_FAILURE; } if (peer_type == WMI_PEER_TYPE_TDLS) cdp_peer_set_peer_as_tdls(dp_soc, vdev_id, peer_addr, true); if (wlan_cm_is_roam_sync_in_progress(wma->psoc, vdev_id) || MLME_IS_MLO_ROAM_SYNCH_IN_PROGRESS(wma->psoc, vdev_id)) { wma_debug("LFR3: Created peer "QDF_MAC_ADDR_FMT" vdev_id %d, peer_count %d", QDF_MAC_ADDR_REF(peer_addr), vdev_id, wma->interfaces[vdev_id].peer_count + 1); 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, ¶m) != QDF_STATUS_SUCCESS) { wma_err("Unable to create peer in Target"); if (cdp_cfg_get_peer_unmap_conf_support(dp_soc)) cdp_peer_delete_sync( dp_soc, vdev_id, peer_addr, wma_peer_unmap_conf_cb, 1 << CDP_PEER_DO_NOT_START_UNMAP_TIMER); else cdp_peer_delete( dp_soc, vdev_id, peer_addr, 1 << CDP_PEER_DO_NOT_START_UNMAP_TIMER); wlan_objmgr_peer_obj_delete(obj_peer); return QDF_STATUS_E_FAILURE; } wma_debug("Created peer peer_addr "QDF_MAC_ADDR_FMT" vdev_id %d, peer_count - %d", QDF_MAC_ADDR_REF(peer_addr), vdev_id, wma->interfaces[vdev_id].peer_count + 1); wlan_roam_debug_log(vdev_id, DEBUG_PEER_CREATE_SEND, DEBUG_INVALID_PEER_ID, peer_addr, NULL, 0, 0); return QDF_STATUS_SUCCESS; } #ifdef WLAN_FEATURE_11BE_MLO static void wma_peer_setup_fill_info(struct wlan_objmgr_psoc *psoc, struct wlan_objmgr_peer *peer, struct cdp_peer_setup_info *peer_info) { uint8_t vdev_id = wlan_vdev_get_id(wlan_peer_get_vdev(peer)); peer_info->mld_peer_mac = wlan_peer_mlme_get_mldaddr(peer); if (MLME_IS_MLO_ROAM_SYNCH_IN_PROGRESS(psoc, vdev_id) && wlan_vdev_mlme_get_is_mlo_link(psoc, vdev_id)) { peer_info->is_first_link = true; peer_info->is_primary_link = false; } else if (wlan_cm_is_roam_sync_in_progress(psoc, vdev_id) && wlan_vdev_mlme_get_is_mlo_vdev(psoc, vdev_id)) { if (mlo_get_single_link_ml_roaming(psoc, vdev_id)) { peer_info->is_first_link = true; peer_info->is_primary_link = true; } else { peer_info->is_first_link = false; peer_info->is_primary_link = true; } } else { peer_info->is_first_link = wlan_peer_mlme_is_assoc_peer(peer); peer_info->is_primary_link = peer_info->is_first_link; } } /** * wma_cdp_peer_setup() - provide mlo information to cdp_peer_setup * @dp_soc: dp soc * @vdev_id: vdev id * @peer: Object manager peer pointer * @peer_info: Peer setup info * * Return: VOID */ static void wma_cdp_peer_setup(ol_txrx_soc_handle dp_soc, uint8_t vdev_id, struct wlan_objmgr_peer *peer, struct cdp_peer_setup_info *peer_info) { uint8_t *mld_mac, *peer_addr; peer_addr = wlan_peer_get_macaddr(peer); mld_mac = peer_info->mld_peer_mac; if (!mld_mac || qdf_is_macaddr_zero((struct qdf_mac_addr *)mld_mac)) { cdp_peer_setup(dp_soc, vdev_id, peer_addr, NULL); return; } cdp_peer_setup(dp_soc, vdev_id, peer_addr, peer_info); } #else static inline void wma_peer_setup_fill_info(struct wlan_objmgr_psoc *psoc, struct wlan_objmgr_peer *peer, struct cdp_peer_setup_info *peer_info) { peer_info->mld_peer_mac = NULL; peer_info->is_first_link = false; peer_info->is_primary_link = false; } static void wma_cdp_peer_setup(ol_txrx_soc_handle dp_soc, uint8_t vdev_id, struct wlan_objmgr_peer *peer, struct cdp_peer_setup_info *peer_info) { uint8_t *peer_addr; peer_addr = wlan_peer_get_macaddr(peer); cdp_peer_setup(dp_soc, vdev_id, peer_addr, NULL); } #endif QDF_STATUS wma_create_peer(tp_wma_handle wma, uint8_t peer_addr[QDF_MAC_ADDR_SIZE], uint32_t peer_type, uint8_t vdev_id, uint8_t peer_mld_addr[QDF_MAC_ADDR_SIZE], bool is_assoc_peer) { void *dp_soc = cds_get_context(QDF_MODULE_ID_SOC); QDF_STATUS status; struct wlan_objmgr_peer *obj_peer; struct cdp_peer_setup_info peer_info = {0}; if (!dp_soc) return QDF_STATUS_E_FAILURE; status = wma_add_peer(wma, peer_addr, peer_type, vdev_id, peer_mld_addr, is_assoc_peer); if (QDF_IS_STATUS_ERROR(status)) return status; obj_peer = wlan_objmgr_get_peer_by_mac(wma->psoc, peer_addr, WLAN_LEGACY_WMA_ID); if (!obj_peer) return QDF_STATUS_E_FAILURE; wma_increment_peer_count(wma, vdev_id); wma_peer_setup_fill_info(wma->psoc, obj_peer, &peer_info); wma_peer_tbl_trans_add_entry(obj_peer, true, &peer_info); wma_cdp_peer_setup(dp_soc, vdev_id, obj_peer, &peer_info); wlan_objmgr_peer_release_ref(obj_peer, WLAN_LEGACY_WMA_ID); return QDF_STATUS_SUCCESS; } /** * wma_create_sta_mode_bss_peer() - send peer create command to fw * and start peer create response timer * @wma: wma handle * @peer_addr: peer mac address * @peer_type: peer type * @vdev_id: vdev id * @mld_addr: peer mld address * @is_assoc_peer: is assoc peer or not * * Return: QDF_STATUS */ static QDF_STATUS wma_create_sta_mode_bss_peer(tp_wma_handle wma, uint8_t peer_addr[QDF_MAC_ADDR_SIZE], uint32_t peer_type, uint8_t vdev_id, uint8_t mld_addr[QDF_MAC_ADDR_SIZE], bool is_assoc_peer) { struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); struct wma_target_req *msg = NULL; struct peer_create_rsp_params *peer_create_rsp = NULL; QDF_STATUS status = QDF_STATUS_E_FAILURE; bool is_tgt_peer_conf_supported = false; if (!mac) { wma_err("vdev%d: Mac context is null", vdev_id); return status; } /* * If fw doesn't advertise peer create confirm event support, * use the legacy peer create API */ is_tgt_peer_conf_supported = wlan_psoc_nif_fw_ext_cap_get(wma->psoc, WLAN_SOC_F_PEER_CREATE_RESP); if (!is_tgt_peer_conf_supported) { status = wma_create_peer(wma, peer_addr, peer_type, vdev_id, mld_addr, is_assoc_peer); goto end; } peer_create_rsp = qdf_mem_malloc(sizeof(*peer_create_rsp)); if (!peer_create_rsp) goto end; wma_acquire_wakelock(&wma->wmi_cmd_rsp_wake_lock, WMA_PEER_CREATE_RESPONSE_TIMEOUT); status = wma_add_peer(wma, peer_addr, peer_type, vdev_id, mld_addr, is_assoc_peer); if (QDF_IS_STATUS_ERROR(status)) { wma_release_wakelock(&wma->wmi_cmd_rsp_wake_lock); goto end; } wma_increment_peer_count(wma, vdev_id); qdf_mem_copy(peer_create_rsp->peer_mac.bytes, peer_addr, QDF_MAC_ADDR_SIZE); msg = wma_fill_hold_req(wma, vdev_id, WMA_PEER_CREATE_REQ, WMA_PEER_CREATE_RESPONSE, (void *)peer_create_rsp, WMA_PEER_CREATE_RESPONSE_TIMEOUT); if (!msg) { wma_err("vdev:%d failed to fill peer create req", vdev_id); wma_remove_peer_req(wma, vdev_id, WMA_PEER_CREATE_RESPONSE, (struct qdf_mac_addr *)peer_addr); wma_remove_peer(wma, peer_addr, vdev_id, false); wma_release_wakelock(&wma->wmi_cmd_rsp_wake_lock); status = QDF_STATUS_E_FAILURE; goto end; } return status; end: qdf_mem_free(peer_create_rsp); lim_send_peer_create_resp(mac, vdev_id, status, peer_addr); return status; } /** * wma_remove_bss_peer() - remove BSS peer * @wma: pointer to WMA handle * @vdev_id: vdev id on which delete BSS request was received * @vdev_stop_resp: pointer to Delete BSS response * @type: request type * * This function is called on receiving vdev stop response from FW or * vdev stop response timeout. In case of NDI, use vdev's self MAC * for removing the peer. In case of STA/SAP use bssid passed as part of * delete STA parameter. * * Return: 0 on success, ERROR code on failure */ static int wma_remove_bss_peer(tp_wma_handle wma, uint32_t vdev_id, struct del_bss_resp *vdev_stop_resp, uint8_t type) { void *soc = cds_get_context(QDF_MODULE_ID_SOC); uint8_t *mac_addr = NULL; struct wma_target_req *del_req; int ret_value = 0; QDF_STATUS qdf_status; struct qdf_mac_addr bssid; if (WMA_IS_VDEV_IN_NDI_MODE(wma->interfaces, vdev_id)) { mac_addr = cdp_get_vdev_mac_addr(soc, vdev_id); if (!mac_addr) { wma_err("mac_addr is NULL for vdev_id = %d", vdev_id); return -EINVAL; } } else { qdf_status = wlan_vdev_get_bss_peer_mac( wma->interfaces[vdev_id].vdev, &bssid); if (QDF_IS_STATUS_ERROR(qdf_status)) { wma_err("Failed to get bssid for vdev_id: %d", vdev_id); return -EINVAL; } mac_addr = bssid.bytes; } qdf_status = wma_remove_peer(wma, mac_addr, vdev_id, false); if (QDF_IS_STATUS_ERROR(qdf_status)) { wma_err("wma_remove_peer failed vdev_id:%d", vdev_id); return -EINVAL; } if (cds_is_driver_recovering()) return -EINVAL; if (wmi_service_enabled(wma->wmi_handle, wmi_service_sync_delete_cmds)) { wma_debug("Wait for the peer delete. vdev_id %d", vdev_id); del_req = wma_fill_hold_req(wma, vdev_id, WMA_DELETE_STA_REQ, type, vdev_stop_resp, WMA_DELETE_STA_TIMEOUT); if (!del_req) { wma_err("Failed to allocate request. vdev_id %d", vdev_id); vdev_stop_resp->status = QDF_STATUS_E_NOMEM; ret_value = -EINVAL; } } return ret_value; } #ifdef FEATURE_WLAN_APF /* * 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 enum wmi_host_active_apf_mode get_fw_active_apf_mode(enum active_apf_mode mode) { switch (mode) { case ACTIVE_APF_DISABLED: return WMI_HOST_ACTIVE_APF_DISABLED; case ACTIVE_APF_ENABLED: return WMI_HOST_ACTIVE_APF_ENABLED; case ACTIVE_APF_ADAPTIVE: return WMI_HOST_ACTIVE_APF_ADAPTIVE; default: wma_err("Invalid Active APF Mode %d; Using 'disabled'", mode); return WMI_HOST_ACTIVE_APF_DISABLED; } } /** * 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) { enum wmi_host_active_apf_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_debug("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); } #else /* FEATURE_WLAN_APF */ static QDF_STATUS wma_config_active_apf_mode(t_wma_handle *wma, uint8_t vdev_id) { return QDF_STATUS_SUCCESS; } #endif /* FEATURE_WLAN_APF */ #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) { struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_PE); if (!mac_ctx) 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 */ void wma_send_del_bss_response(tp_wma_handle wma, struct del_bss_resp *resp) { struct wma_txrx_node *iface; struct beacon_info *bcn; uint8_t vdev_id; void *soc = cds_get_context(QDF_MODULE_ID_SOC); if (!resp) { wma_err("req is NULL"); return; } vdev_id = resp->vdev_id; iface = &wma->interfaces[vdev_id]; if (!iface->vdev) { wma_err("vdev id %d iface->vdev is NULL", vdev_id); if (resp) qdf_mem_free(resp); return; } cdp_fc_vdev_flush(soc, vdev_id); wma_debug("vdev_id: %d, un-pausing tx_ll_queue for VDEV_STOP rsp", vdev_id); cdp_fc_vdev_unpause(soc, vdev_id, OL_TXQ_PAUSE_REASON_VDEV_STOP, 0); wma_vdev_clear_pause_bit(vdev_id, PAUSE_TYPE_HOST); qdf_atomic_set(&iface->bss_status, WMA_BSS_STATUS_STOPPED); wma_debug("(type %d subtype %d) BSS is stopped", iface->type, iface->sub_type); bcn = wma->interfaces[vdev_id].beacon; if (bcn) { wma_debug("Freeing beacon struct %pK, template memory %pK", bcn, bcn->buf); if (bcn->dma_mapped) qdf_nbuf_unmap_single(wma->qdf_dev, 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 (resp->status == QDF_STATUS_FW_MSG_TIMEDOUT) { qdf_mem_free(resp); wma_err("DEL BSS from ADD BSS timeout do not send resp to UMAC (vdev id %x)", vdev_id); } else { resp->status = QDF_STATUS_SUCCESS; wma_send_msg_high_priority(wma, WMA_DELETE_BSS_RSP, (void *)resp, 0); } if (iface->del_staself_req && iface->is_del_sta_deferred) { iface->is_del_sta_deferred = false; wma_nofl_alert("scheduling deferred deletion (vdev id %x)", vdev_id); wma_vdev_detach(iface->del_staself_req); } } QDF_STATUS wma_send_vdev_down(tp_wma_handle wma, struct del_bss_resp *resp) { uint8_t vdev_id; struct wma_txrx_node *iface = &wma->interfaces[resp->vdev_id]; uint32_t vdev_stop_type; QDF_STATUS status; if (!resp) { wma_err("resp is NULL"); return QDF_STATUS_E_NULL_VALUE; } vdev_id = resp->vdev_id; status = mlme_get_vdev_stop_type(iface->vdev, &vdev_stop_type); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to get vdev stop type"); qdf_mem_free(resp); return status; } if (vdev_stop_type != WMA_DELETE_BSS_HO_FAIL_REQ) { status = wma_send_vdev_down_to_fw(wma, vdev_id); if (QDF_IS_STATUS_ERROR(status)) wma_err("Failed to send vdev down cmd: vdev %d", vdev_id); else wma_check_and_find_mcc_ap(wma, vdev_id); } wlan_vdev_mlme_sm_deliver_evt(iface->vdev, WLAN_VDEV_SM_EV_DOWN_COMPLETE, sizeof(*resp), resp); return status; } /** * wma_send_vdev_down_req() - handle vdev down req * @wma: wma handle * @resp: pointer to vde del bss response * * Return: none */ static void wma_send_vdev_down_req(tp_wma_handle wma, struct del_bss_resp *resp) { struct wma_txrx_node *iface = &wma->interfaces[resp->vdev_id]; enum QDF_OPMODE mode; mode = wlan_vdev_mlme_get_opmode(iface->vdev); if (mode == QDF_STA_MODE || mode == QDF_P2P_CLIENT_MODE) { /* initiate MLME Down req from CM for STA/CLI */ wlan_cm_bss_peer_delete_rsp(iface->vdev, resp->status); qdf_mem_free(resp); return; } wlan_vdev_mlme_sm_deliver_evt(iface->vdev, WLAN_VDEV_SM_EV_MLME_DOWN_REQ, sizeof(*resp), resp); } #ifdef WLAN_FEATURE_11BE_MLO void wma_delete_peer_mlo(struct wlan_objmgr_psoc *psoc, uint8_t *macaddr) { struct wlan_objmgr_peer *peer = NULL; peer = wlan_objmgr_get_peer_by_mac(psoc, macaddr, WLAN_LEGACY_WMA_ID); if (peer) { wlan_mlo_link_peer_delete(peer); wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); } } #endif /* WLAN_FEATURE_11BE_MLO */ static QDF_STATUS wma_delete_peer_on_vdev_stop(tp_wma_handle wma, uint8_t vdev_id) { uint32_t vdev_stop_type; struct del_bss_resp *vdev_stop_resp; struct wma_txrx_node *iface; QDF_STATUS status; struct qdf_mac_addr bssid; iface = &wma->interfaces[vdev_id]; status = wlan_vdev_get_bss_peer_mac(iface->vdev, &bssid); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to get bssid"); return QDF_STATUS_E_INVAL; } status = mlme_get_vdev_stop_type(iface->vdev, &vdev_stop_type); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to get wma req msg type for vdev id %d", vdev_id); return QDF_STATUS_E_INVAL; } wma_delete_peer_mlo(wma->psoc, bssid.bytes); vdev_stop_resp = qdf_mem_malloc(sizeof(*vdev_stop_resp)); if (!vdev_stop_resp) return QDF_STATUS_E_NOMEM; if (vdev_stop_type == WMA_DELETE_BSS_HO_FAIL_REQ) { status = wma_remove_peer(wma, bssid.bytes, vdev_id, true); if (QDF_IS_STATUS_ERROR(status)) goto free_params; vdev_stop_resp->status = status; vdev_stop_resp->vdev_id = vdev_id; wma_send_vdev_down_req(wma, vdev_stop_resp); } else if (vdev_stop_type == WMA_DELETE_BSS_REQ || vdev_stop_type == WMA_SET_LINK_STATE) { uint8_t type; /* CCA is required only for sta interface */ if (iface->type == WMI_VDEV_TYPE_STA) wma_get_cca_stats(wma, vdev_id); if (vdev_stop_type == WMA_DELETE_BSS_REQ) type = WMA_DELETE_PEER_RSP; else type = WMA_SET_LINK_PEER_RSP; vdev_stop_resp->vdev_id = vdev_id; vdev_stop_resp->status = status; status = wma_remove_bss_peer(wma, vdev_id, vdev_stop_resp, type); if (status) { wma_err("Del bss failed vdev:%d", vdev_id); wma_send_vdev_down_req(wma, vdev_stop_resp); return status; } if (wmi_service_enabled(wma->wmi_handle, wmi_service_sync_delete_cmds)) return status; wma_send_vdev_down_req(wma, vdev_stop_resp); } return status; free_params: qdf_mem_free(vdev_stop_resp); return status; } QDF_STATUS cm_send_bss_peer_delete_req(struct wlan_objmgr_vdev *vdev) { tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); uint8_t vdev_id = wlan_vdev_get_id(vdev); if (!wma) return QDF_STATUS_E_INVAL; if (wlan_vdev_mlme_is_init_state(vdev) == QDF_STATUS_SUCCESS) { wma_remove_bss_peer_on_failure(wma, vdev_id); return QDF_STATUS_SUCCESS; } return wma_delete_peer_on_vdev_stop(wma, vdev_id); } QDF_STATUS __wma_handle_vdev_stop_rsp(struct vdev_stop_response *resp_event) { tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); struct wma_txrx_node *iface; QDF_STATUS status; struct qdf_mac_addr bssid; enum QDF_OPMODE mode; if (!wma) return QDF_STATUS_E_INVAL; /* Ignore stop_response in Monitor mode */ if (cds_get_conparam() == QDF_GLOBAL_MONITOR_MODE) return QDF_STATUS_SUCCESS; iface = &wma->interfaces[resp_event->vdev_id]; /* vdev in stopped state, no more waiting for key */ iface->is_waiting_for_key = false; /* * 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_debug("Reset rmfEnabled for vdev %d", resp_event->vdev_id); } mode = wlan_vdev_mlme_get_opmode(iface->vdev); if (mode == QDF_STA_MODE || mode == QDF_P2P_CLIENT_MODE) { status = wlan_vdev_get_bss_peer_mac(iface->vdev, &bssid); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("Failed to get bssid, peer might have got deleted already"); return wlan_cm_bss_peer_delete_rsp(iface->vdev, status); } /* initiate CM to delete bss peer */ return wlan_cm_bss_peer_delete_ind(iface->vdev, &bssid); } else if (mode == QDF_SAP_MODE) { wlan_son_deliver_vdev_stop(iface->vdev); } return wma_delete_peer_on_vdev_stop(wma, resp_event->vdev_id); } /** * wma_handle_vdev_stop_rsp() - handle vdev stop resp * @wma: wma handle * @resp_event: fw resp * * Return: QDF_STATUS */ static QDF_STATUS wma_handle_vdev_stop_rsp(tp_wma_handle wma, struct vdev_stop_response *resp_event) { struct wma_txrx_node *iface; iface = &wma->interfaces[resp_event->vdev_id]; return wlan_vdev_mlme_sm_deliver_evt(iface->vdev, WLAN_VDEV_SM_EV_STOP_RESP, sizeof(*resp_event), resp_event); } QDF_STATUS wma_vdev_stop_resp_handler(struct vdev_mlme_obj *vdev_mlme, struct vdev_stop_response *rsp) { tp_wma_handle wma; struct wma_txrx_node *iface = NULL; QDF_STATUS status = QDF_STATUS_E_FAILURE; wma = cds_get_context(QDF_MODULE_ID_WMA); if (!wma) return status; iface = &wma->interfaces[vdev_mlme->vdev->vdev_objmgr.vdev_id]; if (rsp->vdev_id >= wma->max_bssid) { wma_err("Invalid vdev_id %d from FW", rsp->vdev_id); return QDF_STATUS_E_INVAL; } status = wma_handle_vdev_stop_rsp(wma, rsp); return status; } void wma_cleanup_vdev(struct wlan_objmgr_vdev *vdev) { tp_wma_handle wma_handle; void *soc = cds_get_context(QDF_MODULE_ID_SOC); uint8_t vdev_id = wlan_vdev_get_id(vdev); struct vdev_mlme_obj *vdev_mlme; if (!soc) return; wma_handle = cds_get_context(QDF_MODULE_ID_WMA); if (!wma_handle) return; if (!wma_handle->interfaces[vdev_id].vdev) { wma_err("vdev is NULL"); return; } vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); if (!vdev_mlme) { wma_err("Failed to get vdev mlme obj for vdev id %d", vdev_id); return; } wma_cdp_vdev_detach(soc, wma_handle, vdev_id); wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); wma_handle->interfaces[vdev_id].vdev = NULL; wma_handle->interfaces[vdev_id].vdev_active = false; } QDF_STATUS wma_vdev_self_peer_create(struct vdev_mlme_obj *vdev_mlme) { struct wlan_objmgr_peer *obj_peer; QDF_STATUS status = QDF_STATUS_SUCCESS; struct wlan_objmgr_vdev *vdev = vdev_mlme->vdev; tp_wma_handle wma_handle; uint8_t peer_vdev_id, *self_peer_macaddr; wma_handle = cds_get_context(QDF_MODULE_ID_WMA); if (!wma_handle) return QDF_STATUS_E_FAILURE; if (mlme_vdev_uses_self_peer(vdev_mlme->mgmt.generic.type, vdev_mlme->mgmt.generic.subtype)) { status = wma_create_peer(wma_handle, vdev->vdev_mlme.macaddr, WMI_PEER_TYPE_DEFAULT, wlan_vdev_get_id(vdev), NULL, false); if (QDF_IS_STATUS_ERROR(status)) wma_err("Failed to create peer %d", status); } else if (vdev_mlme->mgmt.generic.type == WMI_VDEV_TYPE_STA || vdev_mlme->mgmt.generic.type == WMI_VDEV_TYPE_NAN) { if (!qdf_is_macaddr_zero( (struct qdf_mac_addr *)vdev->vdev_mlme.mldaddr)) self_peer_macaddr = vdev->vdev_mlme.mldaddr; else self_peer_macaddr = vdev->vdev_mlme.macaddr; /** * Self peer is used for the frames exchanged before * association. For ML STA, Self peer create will be triggered * for both the VDEVs, but one self peer is enough. So in case * of ML, use MLD address for the self peer and ignore self peer * creation for the partner link vdev. */ if (wma_objmgr_peer_exist(wma_handle, self_peer_macaddr, &peer_vdev_id)) return QDF_STATUS_SUCCESS; obj_peer = wma_create_objmgr_peer(wma_handle, wlan_vdev_get_id(vdev), self_peer_macaddr, WMI_PEER_TYPE_DEFAULT, vdev->vdev_mlme.macaddr); if (!obj_peer) { wma_err("Failed to create obj mgr peer for self"); status = QDF_STATUS_E_INVAL; } } return status; } #ifdef MULTI_CLIENT_LL_SUPPORT #define MAX_VDEV_LATENCY_PARAMS 10 /* params being sent: * 1.wmi_vdev_param_set_multi_client_ll_feature_config * 2.wmi_vdev_param_set_normal_latency_flags_config * 3.wmi_vdev_param_set_xr_latency_flags_config * 4.wmi_vdev_param_set_low_latency_flags_config * 5.wmi_vdev_param_set_ultra_low_latency_flags_config * 6.wmi_vdev_param_set_normal_latency_ul_dl_config * 7.wmi_vdev_param_set_xr_latency_ul_dl_config * 8.wmi_vdev_param_set_low_latency_ul_dl_config * 9.wmi_vdev_param_set_ultra_low_latency_ul_dl_config * 10.wmi_vdev_param_set_default_ll_config */ /** * wma_set_vdev_latency_level_param() - Set per vdev latency level params in FW * @wma_handle: wma handle * @mac: mac context * @vdev_id: vdev id * * Return: QDF_STATUS */ static QDF_STATUS wma_set_vdev_latency_level_param(tp_wma_handle wma_handle, struct mac_context *mac, uint8_t vdev_id) { QDF_STATUS status = QDF_STATUS_SUCCESS; bool multi_client_ll_ini_support, multi_client_ll_caps; uint32_t latency_flags; static uint32_t ll[4] = {100, 60, 40, 20}; uint32_t ul_latency, dl_latency, ul_dl_latency; uint8_t default_latency_level; struct dev_set_param setparam[MAX_VDEV_LATENCY_PARAMS]; uint8_t index = 0; multi_client_ll_ini_support = mac->mlme_cfg->wlm_config.multi_client_ll_support; multi_client_ll_caps = wlan_mlme_get_wlm_multi_client_ll_caps(mac->psoc); wma_debug("INI support: %d, fw capability:%d", multi_client_ll_ini_support, multi_client_ll_caps); /* * Multi-Client arbiter functionality is enabled only if both INI is * set, and Service bit is configured. */ if (!(multi_client_ll_ini_support && multi_client_ll_caps)) return status; status = mlme_check_index_setparam( setparam, wmi_vdev_param_set_multi_client_ll_feature_config, multi_client_ll_ini_support, index++, MAX_VDEV_LATENCY_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_err("failed to configure low latency feature"); return status; } /* wlm latency level index:0 - normal, 1 - xr, 2 - low, 3 - ultralow */ wma_debug("Setting vdev params for latency level flags"); latency_flags = mac->mlme_cfg->wlm_config.latency_flags[0]; status = mlme_check_index_setparam( setparam, wmi_vdev_param_set_normal_latency_flags_config, latency_flags, index++, MAX_VDEV_LATENCY_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_err("failed to configure normal latency feature"); return status; } latency_flags = mac->mlme_cfg->wlm_config.latency_flags[1]; status = mlme_check_index_setparam( setparam, wmi_vdev_param_set_xr_latency_flags_config, latency_flags, index++, MAX_VDEV_LATENCY_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_err("failed to configure xr latency feature"); return status; } latency_flags = mac->mlme_cfg->wlm_config.latency_flags[2]; status = mlme_check_index_setparam( setparam, wmi_vdev_param_set_low_latency_flags_config, latency_flags, index++, MAX_VDEV_LATENCY_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_err("failed to configure low latency feature"); return status; } latency_flags = mac->mlme_cfg->wlm_config.latency_flags[3]; status = mlme_check_index_setparam( setparam, wmi_vdev_param_set_ultra_low_latency_flags_config, latency_flags, index++, MAX_VDEV_LATENCY_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_err("failed to configure ultra low latency feature"); return status; } wma_debug("Setting vdev params for Latency level UL/DL flags"); /* * Latency level UL/DL * 0-15 bits: UL and 16-31 bits: DL */ dl_latency = ll[0]; ul_latency = ll[0]; ul_dl_latency = dl_latency << 16 | ul_latency; status = mlme_check_index_setparam( setparam, wmi_vdev_param_set_normal_latency_ul_dl_config, ul_dl_latency, index++, MAX_VDEV_LATENCY_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_err("failed to configure normal latency ul dl flag"); return status; } dl_latency = ll[1]; ul_latency = ll[1]; ul_dl_latency = dl_latency << 16 | ul_latency; status = mlme_check_index_setparam( setparam, wmi_vdev_param_set_xr_latency_ul_dl_config, ul_dl_latency, index++, MAX_VDEV_LATENCY_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_err("failed to configure normal latency ul dl flag"); return status; } dl_latency = ll[2]; ul_latency = ll[2]; ul_dl_latency = dl_latency << 16 | ul_latency; status = mlme_check_index_setparam( setparam, wmi_vdev_param_set_low_latency_ul_dl_config, ul_dl_latency, index++, MAX_VDEV_LATENCY_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_err("failed to configure normal latency ul dl flag"); return status; } dl_latency = ll[3]; ul_latency = ll[3]; ul_dl_latency = dl_latency << 16 | ul_latency; status = mlme_check_index_setparam( setparam, wmi_vdev_param_set_ultra_low_latency_ul_dl_config, ul_dl_latency, index++, MAX_VDEV_LATENCY_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_err("failed to configure normal latency ul dl flag"); return status; } default_latency_level = mac->mlme_cfg->wlm_config.latency_level; status = mlme_check_index_setparam( setparam, wmi_vdev_param_set_default_ll_config, default_latency_level, index++, MAX_VDEV_LATENCY_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_err("failed to configure low latency feature"); return status; } status = wma_send_multi_pdev_vdev_set_params(MLME_VDEV_SETPARAM, vdev_id, setparam, index); if (QDF_IS_STATUS_ERROR(status)) wma_err("Failed to configure vdev latency level params"); return status; } #else static QDF_STATUS wma_set_vdev_latency_level_param(tp_wma_handle wma_handle, struct mac_context *mac, uint8_t vdev_id) { return QDF_STATUS_E_FAILURE; } #endif QDF_STATUS wma_post_vdev_create_setup(struct wlan_objmgr_vdev *vdev) { QDF_STATUS status = QDF_STATUS_SUCCESS; struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); bool mcc_adapt_sch = false; QDF_STATUS ret; uint8_t vdev_id; struct wlan_mlme_qos *qos_aggr; struct vdev_mlme_obj *vdev_mlme; tp_wma_handle wma_handle; uint8_t enable_sifs_burst = 0; if (!mac) return QDF_STATUS_E_FAILURE; wma_handle = cds_get_context(QDF_MODULE_ID_WMA); if (!wma_handle) return QDF_STATUS_E_FAILURE; if (wlan_objmgr_vdev_try_get_ref(vdev, WLAN_LEGACY_WMA_ID) != QDF_STATUS_SUCCESS) return QDF_STATUS_E_FAILURE; vdev_id = wlan_vdev_get_id(vdev); wma_handle->interfaces[vdev_id].vdev = vdev; wma_handle->interfaces[vdev_id].vdev_active = true; vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); if (!vdev_mlme) { wma_err("Failed to get vdev mlme obj!"); goto end; } wma_vdev_update_pause_bitmap(vdev_id, 0); wma_handle->interfaces[vdev_id].type = vdev_mlme->mgmt.generic.type; wma_handle->interfaces[vdev_id].sub_type = vdev_mlme->mgmt.generic.subtype; qos_aggr = &mac->mlme_cfg->qos_mlme_params; if (vdev_mlme->mgmt.generic.type == WMI_VDEV_TYPE_STA) { wma_set_sta_keep_alive( wma_handle, vdev_id, SIR_KEEP_ALIVE_NULL_PKT, mac->mlme_cfg->sta.sta_keep_alive_period, NULL, NULL, NULL); /* offload STA SA query related params to fwr */ if (wmi_service_enabled(wma_handle->wmi_handle, wmi_service_sta_pmf_offload)) { wma_set_sta_sa_query_param(wma_handle, vdev_id); } status = wma_set_sw_retry_threshold(qos_aggr); if (QDF_IS_STATUS_ERROR(status)) wma_err("failed to set sw retry threshold (status = %d)", status); status = wma_set_sw_retry_threshold_per_ac(wma_handle, vdev_id, qos_aggr); if (QDF_IS_STATUS_ERROR(status)) wma_err("failed to set sw retry threshold per ac(status = %d)", status); } status = wma_vdev_create_set_param(vdev); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to setup Vdev create set param for vdev: %d", vdev_id); return status; } if (policy_mgr_get_dynamic_mcc_adaptive_sch(mac->psoc, &mcc_adapt_sch) == QDF_STATUS_SUCCESS) { wma_debug("setting ini value for WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED: %d", mcc_adapt_sch); ret = wma_set_enable_disable_mcc_adaptive_scheduler(mcc_adapt_sch); if (QDF_IS_STATUS_ERROR(ret)) { wma_err("Failed to set WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED"); } } else { wma_err("Failed to get value for WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED, leaving unchanged"); } if (vdev_mlme->mgmt.generic.type == WMI_VDEV_TYPE_STA && ucfg_pmo_is_apf_enabled(wma_handle->psoc)) { ret = wma_config_active_apf_mode(wma_handle, vdev_id); if (QDF_IS_STATUS_ERROR(ret)) wma_err("Failed to configure active APF mode"); } if (vdev_mlme->mgmt.generic.type == WMI_VDEV_TYPE_STA && vdev_mlme->mgmt.generic.subtype == 0) wma_set_vdev_latency_level_param(wma_handle, mac, vdev_id); switch (vdev_mlme->mgmt.generic.type) { case WMI_VDEV_TYPE_AP: if (vdev_mlme->mgmt.generic.subtype != WLAN_VDEV_MLME_SUBTYPE_P2P_DEVICE) break; fallthrough; case WMI_VDEV_TYPE_STA: case WMI_VDEV_TYPE_NAN: case WMI_VDEV_TYPE_OCB: case WMI_VDEV_TYPE_MONITOR: status = ucfg_get_enable_sifs_burst(wma_handle->psoc, &enable_sifs_burst); if (QDF_IS_STATUS_ERROR(status)) wma_err("Failed to get sifs burst value, use default"); status = wma_vdev_set_param(wma_handle->wmi_handle, vdev_id, WMI_PDEV_PARAM_BURST_ENABLE, enable_sifs_burst); if (QDF_IS_STATUS_ERROR(status)) wma_err("WMI_PDEV_PARAM_BURST_ENABLE set failed %d", status); break; default: break; } wma_vdev_set_data_tx_callback(vdev); return QDF_STATUS_SUCCESS; end: wma_cleanup_vdev(vdev); return QDF_STATUS_E_FAILURE; } QDF_STATUS wma_vdev_set_data_tx_callback(struct wlan_objmgr_vdev *vdev) { u_int8_t vdev_id; void *soc = cds_get_context(QDF_MODULE_ID_SOC); tp_wma_handle wma_handle = cds_get_context(QDF_MODULE_ID_WMA); if (!vdev || !wma_handle || !soc) { wma_err("null vdev, wma_handle or soc"); return QDF_STATUS_E_FAILURE; } vdev_id = wlan_vdev_get_id(vdev); cdp_data_tx_cb_set(soc, vdev_id, wma_data_tx_ack_comp_hdlr, wma_handle); return QDF_STATUS_SUCCESS; } enum mlme_bcn_tx_rate_code wma_get_bcn_rate_code(uint16_t rate) { /* rate in multiples of 100 Kbps */ switch (rate) { case WMA_BEACON_TX_RATE_1_M: return MLME_BCN_TX_RATE_CODE_1_M; case WMA_BEACON_TX_RATE_2_M: return MLME_BCN_TX_RATE_CODE_2_M; case WMA_BEACON_TX_RATE_5_5_M: return MLME_BCN_TX_RATE_CODE_5_5_M; case WMA_BEACON_TX_RATE_11_M: return MLME_BCN_TX_RATE_CODE_11M; case WMA_BEACON_TX_RATE_6_M: return MLME_BCN_TX_RATE_CODE_6_M; case WMA_BEACON_TX_RATE_9_M: return MLME_BCN_TX_RATE_CODE_9_M; case WMA_BEACON_TX_RATE_12_M: return MLME_BCN_TX_RATE_CODE_12_M; case WMA_BEACON_TX_RATE_18_M: return MLME_BCN_TX_RATE_CODE_18_M; case WMA_BEACON_TX_RATE_24_M: return MLME_BCN_TX_RATE_CODE_24_M; case WMA_BEACON_TX_RATE_36_M: return MLME_BCN_TX_RATE_CODE_36_M; case WMA_BEACON_TX_RATE_48_M: return MLME_BCN_TX_RATE_CODE_48_M; case WMA_BEACON_TX_RATE_54_M: return MLME_BCN_TX_RATE_CODE_54_M; default: return MLME_BCN_TX_RATE_CODE_1_M; } } #ifdef WLAN_BCN_RATECODE_ENABLE /** * wma_update_beacon_tx_rate_code() - Update bcn_tx_rate_code * @mlme_obj: pointer to mlme object * * Return: none */ static void wma_update_beacon_tx_rate_code(struct vdev_mlme_obj *mlme_obj) { uint8_t preamble, nss, rix; uint32_t rate_code; rate_code = mlme_obj->mgmt.rate_info.bcn_tx_rate; rix = rate_code & RATECODE_V1_RIX_MASK; nss = (rate_code >> RATECODE_V1_NSS_OFFSET) & RATECODE_V1_NSS_MASK; preamble = rate_code >> RATECODE_V1_PREAMBLE_OFFSET; mlme_obj->mgmt.rate_info.bcn_tx_rate_code = wlan_mlme_assemble_rate_code(preamble, nss, rix); } #else static void wma_update_beacon_tx_rate_code(struct vdev_mlme_obj *mlme_obj) { } #endif QDF_STATUS wma_vdev_pre_start(uint8_t vdev_id, bool restart) { tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); struct wma_txrx_node *intr; struct mac_context *mac_ctx = cds_get_context(QDF_MODULE_ID_PE); struct wlan_mlme_nss_chains *ini_cfg; struct vdev_mlme_obj *mlme_obj; struct wlan_objmgr_vdev *vdev; struct wlan_channel *des_chan; QDF_STATUS status; enum coex_btc_chain_mode btc_chain_mode; struct wlan_mlme_qos *qos_aggr; uint8_t amsdu_val; if (!wma || !mac_ctx) return QDF_STATUS_E_FAILURE; intr = wma->interfaces; if (!intr) { wma_err("Invalid interface"); return QDF_STATUS_E_FAILURE; } if (vdev_id >= WLAN_MAX_VDEVS) { wma_err("Invalid vdev id"); return QDF_STATUS_E_INVAL; } vdev = intr[vdev_id].vdev; if (!vdev) { wma_err("Invalid vdev"); return QDF_STATUS_E_FAILURE; } mlme_obj = wlan_vdev_mlme_get_cmpt_obj(vdev); if (!mlme_obj) { wma_err("vdev component object is NULL"); return QDF_STATUS_E_FAILURE; } des_chan = vdev->vdev_mlme.des_chan; ini_cfg = mlme_get_ini_vdev_config(vdev); if (!ini_cfg) { wma_err("nss chain ini config NULL"); return QDF_STATUS_E_FAILURE; } intr[vdev_id].config.gtx_info.gtxRTMask[0] = CFG_TGT_DEFAULT_GTX_HT_MASK; intr[vdev_id].config.gtx_info.gtxRTMask[1] = CFG_TGT_DEFAULT_GTX_VHT_MASK; intr[vdev_id].config.gtx_info.gtxUsrcfg = mac_ctx->mlme_cfg->sta.tgt_gtx_usr_cfg; intr[vdev_id].config.gtx_info.gtxPERThreshold = CFG_TGT_DEFAULT_GTX_PER_THRESHOLD; intr[vdev_id].config.gtx_info.gtxPERMargin = CFG_TGT_DEFAULT_GTX_PER_MARGIN; intr[vdev_id].config.gtx_info.gtxTPCstep = CFG_TGT_DEFAULT_GTX_TPC_STEP; intr[vdev_id].config.gtx_info.gtxTPCMin = CFG_TGT_DEFAULT_GTX_TPC_MIN; intr[vdev_id].config.gtx_info.gtxBWMask = CFG_TGT_DEFAULT_GTX_BW_MASK; intr[vdev_id].chan_width = des_chan->ch_width; intr[vdev_id].ch_freq = des_chan->ch_freq; intr[vdev_id].ch_flagext = des_chan->ch_flagext; /* * If the channel has DFS set, flip on radar reporting. * * It may be that this should only be done for hostap operation * as this flag may be interpreted (at some point in the future) * by the firmware as "oh, and please do radar DETECTION." * * If that is ever the case we would insert the decision whether to * enable the firmware flag here. */ if (QDF_GLOBAL_MONITOR_MODE != cds_get_conparam() && utils_is_dfs_chan_for_freq(wma->pdev, des_chan->ch_freq)) mlme_obj->mgmt.generic.disable_hw_ack = true; if (mlme_obj->mgmt.rate_info.bcn_tx_rate) { wma_debug("beacon tx rate [%u * 100 Kbps]", mlme_obj->mgmt.rate_info.bcn_tx_rate); /* * beacon_tx_rate is in multiples of 100 Kbps. * Convert the data rate to hw rate code. */ mlme_obj->mgmt.rate_info.bcn_tx_rate = wma_get_bcn_rate_code(mlme_obj->mgmt.rate_info.bcn_tx_rate); wma_update_beacon_tx_rate_code(mlme_obj); } if (!restart) { wma_debug("vdev_id: %d, unpausing tx_ll_queue at VDEV_START", vdev_id); cdp_fc_vdev_unpause(cds_get_context(QDF_MODULE_ID_SOC), vdev_id, 0xffffffff, 0); wma_vdev_update_pause_bitmap(vdev_id, 0); } /* Send the dynamic nss chain params before vdev start to fw */ if (wma->dynamic_nss_chains_support && !restart) wma_vdev_nss_chain_params_send(vdev_id, ini_cfg); status = ucfg_coex_psoc_get_btc_chain_mode(wma->psoc, &btc_chain_mode); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to get btc chain mode"); return QDF_STATUS_E_FAILURE; } if (btc_chain_mode != WLAN_COEX_BTC_CHAIN_MODE_UNSETTLED) { status = ucfg_coex_send_btc_chain_mode(vdev, btc_chain_mode); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to send btc chain mode %d", btc_chain_mode); return QDF_STATUS_E_FAILURE; } } qos_aggr = &mac_ctx->mlme_cfg->qos_mlme_params; status = wma_set_tx_rx_aggr_size(vdev_id, qos_aggr->tx_aggregation_size, qos_aggr->rx_aggregation_size, WMI_VDEV_CUSTOM_AGGR_TYPE_AMPDU); if (QDF_IS_STATUS_ERROR(status)) wma_err("failed to set aggregation sizes(status = %d)", status); if (mac_ctx->is_usr_cfg_amsdu_enabled) { status = wlan_mlme_get_max_amsdu_num(wma->psoc, &amsdu_val); if (QDF_IS_STATUS_ERROR(status)) { wma_err("failed to get amsdu aggr.size(status = %d)", status); } else { status = wma_set_tx_rx_aggr_size( vdev_id, amsdu_val, amsdu_val, WMI_VDEV_CUSTOM_AGGR_TYPE_AMSDU); if (QDF_IS_STATUS_ERROR(status)) wma_err("failed to set amsdu aggr.size(status = %d)", status); } } if (mlme_obj->mgmt.generic.type == WMI_VDEV_TYPE_STA) { status = wma_set_tx_rx_aggr_size_per_ac(wma, vdev_id, qos_aggr, WMI_VDEV_CUSTOM_AGGR_TYPE_AMPDU); if (QDF_IS_STATUS_ERROR(status)) wma_err("failed to set aggr size per ac(status = %d)", status); } return QDF_STATUS_SUCCESS; } /** * 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[QDF_MAC_ADDR_SIZE]; int status = 0; param_buf = (WMI_PEER_ASSOC_CONF_EVENTID_param_tlvs *) cmd_param_info; if (!param_buf) { wma_err("Invalid peer assoc conf event buffer"); return -EINVAL; } event = param_buf->fixed_param; if (!event) { wma_err("Invalid peer assoc conf event buffer"); return -EINVAL; } WMI_MAC_ADDR_TO_CHAR_ARRAY(&event->peer_macaddr, macaddr); wma_debug("peer assoc conf for vdev:%d mac="QDF_MAC_ADDR_FMT, event->vdev_id, QDF_MAC_ADDR_REF(macaddr)); req_msg = wma_find_req(wma, event->vdev_id, WMA_PEER_ASSOC_CNF_START, NULL); if (!req_msg) { wma_err("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_err("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 = event->status; wma_debug("Send ADD_STA_RSP: statype %d vdev_id %d aid %d bssid "QDF_MAC_ADDR_FMT" status %d", params->staType, params->smesessionId, params->assocId, QDF_MAC_ADDR_REF(params->bssId), 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) { wma_send_add_bss_resp(wma, event->vdev_id, event->status); } else { wma_err("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; } int wma_peer_create_confirm_handler(void *handle, uint8_t *evt_param_info, uint32_t len) { tp_wma_handle wma = (tp_wma_handle)handle; wmi_peer_create_conf_event_fixed_param *peer_create_rsp; WMI_PEER_CREATE_CONF_EVENTID_param_tlvs *param_buf; struct wma_target_req *req_msg = NULL; struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); struct peer_create_rsp_params *rsp_data; void *dp_soc = cds_get_context(QDF_MODULE_ID_SOC); struct qdf_mac_addr peer_mac; QDF_STATUS status = QDF_STATUS_E_FAILURE; int ret = -EINVAL; uint8_t req_msg_type; struct wlan_objmgr_peer *peer; struct cdp_peer_setup_info peer_info = {0}; param_buf = (WMI_PEER_CREATE_CONF_EVENTID_param_tlvs *)evt_param_info; if (!param_buf) { wma_err("Invalid peer create conf evt buffer"); return -EINVAL; } peer_create_rsp = param_buf->fixed_param; WMI_MAC_ADDR_TO_CHAR_ARRAY(&peer_create_rsp->peer_macaddr, peer_mac.bytes); if (qdf_is_macaddr_zero(&peer_mac) || qdf_is_macaddr_broadcast(&peer_mac) || qdf_is_macaddr_group(&peer_mac)) { wma_err("Invalid bssid"); return -EINVAL; } wma_debug("vdev:%d Peer create confirm for bssid: " QDF_MAC_ADDR_FMT, peer_create_rsp->vdev_id, QDF_MAC_ADDR_REF(peer_mac.bytes)); req_msg = wma_find_remove_req_msgtype(wma, peer_create_rsp->vdev_id, WMA_PEER_CREATE_REQ); if (!req_msg) { wma_debug("vdev:%d Failed to lookup peer create request msg", peer_create_rsp->vdev_id); return -EINVAL; } rsp_data = (struct peer_create_rsp_params *)req_msg->user_data; req_msg_type = req_msg->type; qdf_mc_timer_stop(&req_msg->event_timeout); qdf_mc_timer_destroy(&req_msg->event_timeout); qdf_mem_free(rsp_data); qdf_mem_free(req_msg); if (req_msg_type == WMA_PASN_PEER_CREATE_RESPONSE) { wma_pasn_handle_peer_create_conf(wma, &peer_mac, peer_create_rsp->status, peer_create_rsp->vdev_id); wma_release_wakelock(&wma->wmi_cmd_rsp_wake_lock); return 0; } wma_release_wakelock(&wma->wmi_cmd_rsp_wake_lock); if (!peer_create_rsp->status) { if (!dp_soc) { wma_err("DP SOC context is NULL"); goto fail; } peer = wlan_objmgr_get_peer_by_mac(wma->psoc, peer_mac.bytes, WLAN_LEGACY_WMA_ID); if (!peer) { status = QDF_STATUS_E_FAILURE; goto fail; } wma_peer_setup_fill_info(wma->psoc, peer, &peer_info); wma_peer_tbl_trans_add_entry(peer, true, &peer_info); wma_cdp_peer_setup(dp_soc, peer_create_rsp->vdev_id, peer, &peer_info); wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); status = QDF_STATUS_SUCCESS; ret = 0; } fail: if (QDF_IS_STATUS_ERROR(status)) wma_remove_peer(wma, peer_mac.bytes, peer_create_rsp->vdev_id, (peer_create_rsp->status > 0) ? true : false); if (mac) lim_send_peer_create_resp(mac, peer_create_rsp->vdev_id, status, peer_mac.bytes); return ret; } #ifdef FEATURE_WDS /* * wma_cdp_cp_peer_del_response - handle peer delete response * @psoc: psoc object pointer * @mac_addr: Mac address of the peer * @vdev_id: id of virtual device object * * when peer map v2 is enabled, cdp_peer_teardown() does not remove the AST from * hash table. Call cdp_cp_peer_del_response() when peer delete response is * received from fw to delete the AST entry from the AST hash. * * Return: None */ static void wma_cdp_cp_peer_del_response(struct wlan_objmgr_psoc *psoc, uint8_t *peer_mac, uint8_t vdev_id) { ol_txrx_soc_handle soc_txrx_handle; soc_txrx_handle = wlan_psoc_get_dp_handle(psoc); cdp_cp_peer_del_response(soc_txrx_handle, vdev_id, peer_mac); } #else static void wma_cdp_cp_peer_del_response(struct wlan_objmgr_psoc *psoc, uint8_t *peer_mac, uint8_t vdev_id) { } #endif /** * 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[QDF_MAC_ADDR_SIZE]; int status = 0; struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); if (!mac) { wma_err("mac context is null"); return -EINVAL; } param_buf = (WMI_PEER_DELETE_RESP_EVENTID_param_tlvs *)cmd_param_info; if (!param_buf) { wma_err("Invalid vdev delete event buffer"); return -EINVAL; } event = (wmi_peer_delete_cmd_fixed_param *)param_buf->fixed_param; if (!event) { wma_err("Invalid vdev delete event buffer"); return -EINVAL; } WMI_MAC_ADDR_TO_CHAR_ARRAY(&event->peer_macaddr, macaddr); wma_debug("Peer Delete Response, vdev %d Peer "QDF_MAC_ADDR_FMT, event->vdev_id, QDF_MAC_ADDR_REF(macaddr)); wlan_roam_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_debug("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_debug("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_debug("Calling vdev detach handler"); wma_handle_vdev_detach(wma, data->self_sta_param); mlme_vdev_self_peer_delete_resp(data->self_sta_param); qdf_mem_free(data); } else if (req_msg->type == WMA_SET_LINK_PEER_RSP || req_msg->type == WMA_DELETE_PEER_RSP) { wma_send_vdev_down_req(wma, req_msg->user_data); } else if (req_msg->type == WMA_DELETE_STA_CONNECT_RSP) { wma_debug("wma delete peer completed vdev %d", req_msg->vdev_id); lim_cm_send_connect_rsp(mac, NULL, req_msg->user_data, CM_GENERIC_FAILURE, QDF_STATUS_E_FAILURE, 0, false); cm_free_join_req(req_msg->user_data); } wma_cdp_cp_peer_del_response(wma->psoc, macaddr, event->vdev_id); qdf_mem_free(req_msg); return status; } static void wma_trigger_recovery_assert_on_fw_timeout(uint16_t wma_msg, enum qdf_hang_reason reason) { wma_err("%s timed out, triggering recovery", mac_trace_get_wma_msg_string(wma_msg)); qdf_trigger_self_recovery(NULL, reason); } 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; /* Firmware is down send failure response */ if (cds_is_fw_down()) return false; if (cds_is_driver_unloading()) 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 mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); QDF_STATUS status; wma = cds_get_context(QDF_MODULE_ID_WMA); if (!wma) return; status = wma_find_req_on_timer_expiry(wma, tgt_req); if (QDF_IS_STATUS_ERROR(status)) { /* * if find request failed, then firmware rsp should have * consumed the buffer. Do not free. */ wma_debug("Failed to lookup request message - %pK", tgt_req); return; } wma_alert("request %d is timed out for vdev_id - %d", tgt_req->msg_type, tgt_req->vdev_id); if (tgt_req->msg_type == WMA_ADD_STA_REQ) { tpAddStaParams params = (tpAddStaParams) tgt_req->user_data; params->status = QDF_STATUS_E_TIMEOUT; wma_alert("WMA_ADD_STA_REQ timed out"); wma_debug("Sending add sta rsp to umac (mac:"QDF_MAC_ADDR_FMT", status:%d)", QDF_MAC_ADDR_REF(params->staMac), params->status); if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) wma_trigger_recovery_assert_on_fw_timeout( WMA_ADD_STA_REQ, QDF_AP_STA_CONNECT_REQ_TIMEOUT); wma_send_msg_high_priority(wma, WMA_ADD_STA_RSP, (void *)params, 0); } else if (tgt_req->msg_type == WMA_ADD_BSS_REQ) { wma_alert("WMA_ADD_BSS_REQ timed out"); wma_debug("Sending add bss rsp to umac (vdev %d, status:%d)", tgt_req->vdev_id, QDF_STATUS_E_TIMEOUT); if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) wma_trigger_recovery_assert_on_fw_timeout( WMA_ADD_BSS_REQ, QDF_STA_AP_CONNECT_REQ_TIMEOUT); wma_send_add_bss_resp(wma, tgt_req->vdev_id, QDF_STATUS_E_TIMEOUT); } 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_err("WMA_DEL_STA_REQ timed out"); wma_debug("Sending del sta rsp to umac (mac:"QDF_MAC_ADDR_FMT", status:%d)", QDF_MAC_ADDR_REF(params->staMac), params->status); if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) wma_trigger_recovery_assert_on_fw_timeout( WMA_DELETE_STA_REQ, QDF_PEER_DELETION_TIMEDOUT); 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_alert("wma delete sta p2p request timed out"); if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) wma_trigger_recovery_assert_on_fw_timeout( WMA_DELETE_STA_REQ, QDF_PEER_DELETION_TIMEDOUT); wma_handle_vdev_detach(wma, del_sta->self_sta_param); mlme_vdev_self_peer_delete_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 || tgt_req->type == WMA_DELETE_PEER_RSP)) { struct del_bss_resp *params = (struct del_bss_resp *)tgt_req->user_data; params->status = QDF_STATUS_E_TIMEOUT; wma_err("wma delete peer for del bss req timed out"); if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) wma_trigger_recovery_assert_on_fw_timeout( WMA_DELETE_STA_REQ, QDF_PEER_DELETION_TIMEDOUT); wma_send_vdev_down_req(wma, params); } else if ((tgt_req->msg_type == WMA_DELETE_STA_REQ) && (tgt_req->type == WMA_DELETE_STA_CONNECT_RSP)) { wma_err("wma delete peer timed out vdev %d", tgt_req->vdev_id); if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) wma_trigger_recovery_assert_on_fw_timeout( WMA_DELETE_STA_REQ, QDF_PEER_DELETION_TIMEDOUT); if (!mac) { wma_err("mac: Null Pointer Error"); goto timer_destroy; } lim_cm_send_connect_rsp(mac, NULL, tgt_req->user_data, CM_GENERIC_FAILURE, QDF_STATUS_E_FAILURE, 0, false); cm_free_join_req(tgt_req->user_data); } else if ((tgt_req->msg_type == SIR_HAL_PDEV_SET_HW_MODE) && (tgt_req->type == WMA_PDEV_SET_HW_MODE_RESP)) { struct sir_set_hw_mode_resp *params = qdf_mem_malloc(sizeof(*params)); wma_err("set hw mode req timed out"); if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) wma_trigger_recovery_assert_on_fw_timeout( SIR_HAL_PDEV_SET_HW_MODE, QDF_MAC_HW_MODE_CHANGE_TIMEOUT); if (!params) goto timer_destroy; params->status = SET_HW_MODE_STATUS_ECANCELED; params->cfgd_hw_mode_index = 0; params->num_vdev_mac_entries = 0; wma_send_msg_high_priority(wma, SIR_HAL_PDEV_SET_HW_MODE_RESP, params, 0); } else if ((tgt_req->msg_type == SIR_HAL_PDEV_DUAL_MAC_CFG_REQ) && (tgt_req->type == WMA_PDEV_MAC_CFG_RESP)) { struct sir_dual_mac_config_resp *resp = qdf_mem_malloc(sizeof(*resp)); wma_err("set dual mac config timeout"); if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) wma_trigger_recovery_assert_on_fw_timeout( SIR_HAL_PDEV_DUAL_MAC_CFG_REQ, QDF_MAC_HW_MODE_CONFIG_TIMEOUT); if (!resp) goto timer_destroy; resp->status = SET_HW_MODE_STATUS_ECANCELED; wma_send_msg_high_priority(wma, SIR_HAL_PDEV_MAC_CFG_RESP, resp, 0); } else if ((tgt_req->msg_type == WMA_PEER_CREATE_REQ) && (tgt_req->type == WMA_PEER_CREATE_RESPONSE)) { struct peer_create_rsp_params *peer_create_rsp; struct qdf_mac_addr *peer_mac; if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) wma_trigger_recovery_assert_on_fw_timeout( WMA_PEER_CREATE_RESPONSE, WMA_PEER_CREATE_RESPONSE_TIMEOUT); peer_create_rsp = (struct peer_create_rsp_params *)tgt_req->user_data; peer_mac = &peer_create_rsp->peer_mac; wma_remove_peer(wma, peer_mac->bytes, tgt_req->vdev_id, false); if (!mac) { qdf_mem_free(tgt_req->user_data); goto timer_destroy; } lim_send_peer_create_resp(mac, tgt_req->vdev_id, QDF_STATUS_E_TIMEOUT, (uint8_t *)tgt_req->user_data); qdf_mem_free(tgt_req->user_data); } else if ((tgt_req->msg_type == WMA_PEER_CREATE_REQ) && (tgt_req->type == WMA_PASN_PEER_CREATE_RESPONSE)) { struct peer_create_rsp_params *peer_create_rsp; struct qdf_mac_addr *peer_mac; if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) wma_trigger_recovery_assert_on_fw_timeout( WMA_PEER_CREATE_RESPONSE, WMA_PEER_CREATE_RESPONSE_TIMEOUT); peer_create_rsp = (struct peer_create_rsp_params *)tgt_req->user_data; peer_mac = &peer_create_rsp->peer_mac; wma_pasn_handle_peer_create_conf( wma, peer_mac, QDF_STATUS_E_TIMEOUT, tgt_req->vdev_id); qdf_mem_free(tgt_req->user_data); } else if (tgt_req->msg_type == WMA_PASN_PEER_DELETE_REQUEST && tgt_req->type == WMA_PASN_PEER_DELETE_RESPONSE) { wma_err("PASN Peer delete all resp not received. vdev:%d", tgt_req->vdev_id); if (wma_crash_on_fw_timeout(wma->fw_timeout_crash)) wma_trigger_recovery_assert_on_fw_timeout( WMA_PASN_PEER_DELETE_RESPONSE, WMA_PEER_DELETE_RESPONSE_TIMEOUT); wma_resume_vdev_delete(wma, tgt_req->vdev_id); } else { wma_err("Unhandled timeout for msg_type:%d and type:%d", tgt_req->msg_type, tgt_req->type); QDF_BUG(0); } timer_destroy: qdf_mc_timer_destroy(&tgt_req->event_timeout); qdf_mem_free(tgt_req); } /** * wma_fill_hold_req() - fill wma request * @wma: wma handle * @vdev_id: vdev id * @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; req = qdf_mem_malloc(sizeof(*req)); if (!req) return NULL; wma_debug("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_err("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; } void wma_remove_peer_req(tp_wma_handle wma, uint8_t vdev_id, uint8_t type, struct qdf_mac_addr *peer_addr) { struct wma_target_req *req_msg; wma_debug("Remove req for vdev: %d type: %d", vdev_id, type); req_msg = wma_find_req(wma, vdev_id, type, peer_addr); if (!req_msg) { wma_err("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_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_debug("Remove req for vdev: %d type: %d", vdev_id, type); req_msg = wma_find_req(wma, vdev_id, type, NULL); if (!req_msg) { wma_err("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); } #define MAX_VDEV_SET_BSS_PARAMS 5 /* params being sent: * 1.wmi_vdev_param_beacon_interval * 2.wmi_vdev_param_dtim_period * 3.wmi_vdev_param_tx_pwrlimit * 4.wmi_vdev_param_slot_time * 5.wmi_vdev_param_protection_mode */ /** * 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 * @bss_max_idle_period: BSS max idle period * * Return: QDF_STATUS */ static QDF_STATUS 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, uint16_t bss_max_idle_period) { uint32_t slot_time; struct wma_txrx_node *intr = wma->interfaces; struct dev_set_param setparam[MAX_VDEV_SET_BSS_PARAMS]; uint8_t index = 0; enum ieee80211_protmode prot_mode; QDF_STATUS ret; ret = QDF_STATUS_E_FAILURE; /* Beacon Interval setting */ ret = mlme_check_index_setparam(setparam, wmi_vdev_param_beacon_interval, beaconInterval, index++, MAX_VDEV_SET_BSS_PARAMS); if (QDF_IS_STATUS_ERROR(ret)) { wma_debug("failed to send wmi_vdev_param_beacon_interval to fw"); goto error; } 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_err("failed to set vdev gtx cfg"); goto error; } ret = mlme_check_index_setparam(setparam, wmi_vdev_param_dtim_period, dtimPeriod, index++, MAX_VDEV_SET_BSS_PARAMS); if (QDF_IS_STATUS_ERROR(ret)) { wma_debug("failed to send wmi_vdev_param_dtim_period fw"); goto error; } intr[vdev_id].dtimPeriod = dtimPeriod; if (!wlan_reg_is_ext_tpc_supported(wma->psoc)) { if (!maxTxPower) wma_warn("Setting Tx power limit to 0"); wma_nofl_debug("TXP[W][set_bss_params]: %d", maxTxPower); if (maxTxPower != INVALID_TXPOWER) { ret = mlme_check_index_setparam( setparam, wmi_vdev_param_tx_pwrlimit, maxTxPower, index++, MAX_VDEV_SET_BSS_PARAMS); if (QDF_IS_STATUS_ERROR(ret)) { wma_debug("failed to send wmi_vdev_param_tx_pwrlimit to fw"); goto error; } } else { wma_err("Invalid max Tx power"); } } /* Slot time */ if (shortSlotTimeSupported) slot_time = WMI_VDEV_SLOT_TIME_SHORT; else slot_time = WMI_VDEV_SLOT_TIME_LONG; ret = mlme_check_index_setparam(setparam, wmi_vdev_param_slot_time, slot_time, index++, MAX_VDEV_SET_BSS_PARAMS); if (QDF_IS_STATUS_ERROR(ret)) { wma_debug("failed to send wmi_vdev_param_slot_time to fw"); goto error; } /* Initialize protection mode in case of coexistence */ prot_mode = llbCoexist ? IEEE80211_PROT_CTSONLY : IEEE80211_PROT_NONE; ret = mlme_check_index_setparam(setparam, wmi_vdev_param_protection_mode, prot_mode, index++, MAX_VDEV_SET_BSS_PARAMS); if (QDF_IS_STATUS_ERROR(ret)) { wma_debug("failed to send wmi_vdev_param_protection_mode to fw"); goto error; } ret = wma_send_multi_pdev_vdev_set_params(MLME_VDEV_SETPARAM, vdev_id, setparam, index); if (QDF_IS_STATUS_ERROR(ret)) { wma_err("Failed to set BEACON/DTIM_PERIOD/PWRLIMIT/SLOTTIME/PROTECTION params"); goto error; } mlme_set_max_reg_power(intr[vdev_id].vdev, maxTxPower); if (bss_max_idle_period) wma_set_sta_keep_alive( wma, vdev_id, SIR_KEEP_ALIVE_NULL_PKT, bss_max_idle_period, NULL, NULL, NULL); error: return ret; } 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, ¶m, WMA_WILDCARD_PDEV_ID); if (QDF_IS_STATUS_ERROR(ret)) { wma_err("Failed to set QOS MFP/PMF (%d)", ret); } else { wma_debug("QOS MFP/PMF set"); } } /** * wma_set_peer_pmf_status() - Get the peer and update PMF capability of it * @wma: wma handle * @peer_mac: peer mac addr * @is_pmf_enabled: Carries the status whether PMF is enabled or not * * Return: QDF_STATUS */ static QDF_STATUS wma_set_peer_pmf_status(tp_wma_handle wma, uint8_t *peer_mac, bool is_pmf_enabled) { struct wlan_objmgr_peer *peer; peer = wlan_objmgr_get_peer(wma->psoc, wlan_objmgr_pdev_get_pdev_id(wma->pdev), peer_mac, WLAN_LEGACY_WMA_ID); if (!peer) { wma_err("Peer of peer_mac "QDF_MAC_ADDR_FMT" not found", QDF_MAC_ADDR_REF(peer_mac)); return QDF_STATUS_E_INVAL; } mlme_set_peer_pmf_status(peer, is_pmf_enabled); wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); wma_debug("set is_pmf_enabled %d for "QDF_MAC_ADDR_FMT, is_pmf_enabled, QDF_MAC_ADDR_REF(peer_mac)); return QDF_STATUS_SUCCESS; } QDF_STATUS wma_pre_vdev_start_setup(uint8_t vdev_id, struct bss_params *add_bss) { QDF_STATUS status; void *soc = cds_get_context(QDF_MODULE_ID_SOC); struct wma_txrx_node *iface; tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); struct vdev_mlme_obj *mlme_obj; uint8_t *mac_addr; if (!soc || !wma) return QDF_STATUS_E_FAILURE; iface = &wma->interfaces[vdev_id]; mlme_obj = wlan_vdev_mlme_get_cmpt_obj(iface->vdev); if (!mlme_obj) { wma_err("vdev component object is NULL"); return QDF_STATUS_E_FAILURE; } wma_set_bss_rate_flags(wma, vdev_id, add_bss); if (wlan_vdev_mlme_get_opmode(iface->vdev) == QDF_NDI_MODE || wlan_vdev_mlme_get_opmode(iface->vdev) == QDF_IBSS_MODE) mac_addr = mlme_obj->mgmt.generic.bssid; else mac_addr = wlan_vdev_mlme_get_macaddr(iface->vdev); status = wma_create_peer(wma, mac_addr, WMI_PEER_TYPE_DEFAULT, vdev_id, NULL, false); if (status != QDF_STATUS_SUCCESS) { wma_err("Failed to create peer"); return status; } if (!cdp_find_peer_exist(soc, OL_TXRX_PDEV_ID, mac_addr)) { wma_err("Failed to find peer "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(mac_addr)); return QDF_STATUS_E_FAILURE; } iface->rmfEnabled = add_bss->rmfEnabled; if (add_bss->rmfEnabled) wma_set_mgmt_frame_protection(wma); return status; } QDF_STATUS wma_post_vdev_start_setup(uint8_t vdev_id) { QDF_STATUS status = QDF_STATUS_SUCCESS; tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); struct wma_txrx_node *intr; struct vdev_mlme_obj *mlme_obj; struct wlan_objmgr_vdev *vdev; uint8_t bss_power = 0; if (!wma) return QDF_STATUS_E_FAILURE; intr = &wma->interfaces[vdev_id]; if (!intr) { wma_err("Invalid interface"); return QDF_STATUS_E_FAILURE; } vdev = intr->vdev; if (!vdev) { wma_err("vdev is NULL"); return QDF_STATUS_E_FAILURE; } if (wlan_vdev_mlme_get_opmode(vdev) == QDF_NDI_MODE || wlan_vdev_mlme_get_opmode(vdev) == QDF_IBSS_MODE) { /* Initialize protection mode to no protection */ wma_vdev_set_param(wma->wmi_handle, vdev_id, wmi_vdev_param_protection_mode, IEEE80211_PROT_NONE); return status; } mlme_obj = wlan_vdev_mlme_get_cmpt_obj(vdev); if (!mlme_obj) { wma_err("vdev component object is NULL"); return QDF_STATUS_E_FAILURE; } /* Fill bss_chan after vdev start */ qdf_mem_copy(vdev->vdev_mlme.bss_chan, vdev->vdev_mlme.des_chan, sizeof(struct wlan_channel)); if (!wlan_reg_is_ext_tpc_supported(wma->psoc)) bss_power = wlan_reg_get_channel_reg_power_for_freq( wma->pdev, vdev->vdev_mlme.bss_chan->ch_freq); if (wma_vdev_set_bss_params(wma, vdev_id, mlme_obj->proto.generic.beacon_interval, mlme_obj->proto.generic.dtim_period, mlme_obj->proto.generic.slot_time, mlme_obj->proto.generic.protection_mode, bss_power, 0)) { wma_err("Failed to set wma_vdev_set_bss_params"); } wma_vdev_set_he_bss_params(wma, vdev_id, &mlme_obj->proto.he_ops_info); #if defined(WLAN_FEATURE_11BE) wma_vdev_set_eht_bss_params(wma, vdev_id, &mlme_obj->proto.eht_ops_info); #endif return status; } static QDF_STATUS wma_update_iface_params(tp_wma_handle wma, struct bss_params *add_bss) { struct wma_txrx_node *iface; uint8_t vdev_id; vdev_id = add_bss->staContext.smesessionId; iface = &wma->interfaces[vdev_id]; wma_set_bss_rate_flags(wma, vdev_id, add_bss); if (iface->addBssStaContext) qdf_mem_free(iface->addBssStaContext); iface->addBssStaContext = qdf_mem_malloc(sizeof(tAddStaParams)); if (!iface->addBssStaContext) return QDF_STATUS_E_RESOURCES; *iface->addBssStaContext = add_bss->staContext; /* Save parameters later needed by WMA_ADD_STA_REQ */ iface->rmfEnabled = add_bss->rmfEnabled; if (add_bss->rmfEnabled) wma_set_peer_pmf_status(wma, add_bss->bssId, true); iface->beaconInterval = add_bss->beaconInterval; iface->llbCoexist = add_bss->llbCoexist; iface->shortSlotTimeSupported = add_bss->shortSlotTimeSupported; iface->nwType = add_bss->nwType; iface->bss_max_idle_period = add_bss->bss_max_idle_period; return QDF_STATUS_SUCCESS; } static inline bool wma_cdp_find_peer_by_addr(uint8_t *peer_addr) { void *soc = cds_get_context(QDF_MODULE_ID_SOC); uint8_t pdev_id = OL_TXRX_PDEV_ID; if (!soc || pdev_id == OL_TXRX_INVALID_PDEV_ID) { wma_err("Failed to get pdev/soc"); return NULL; } return cdp_find_peer_exist(soc, pdev_id, peer_addr); } static QDF_STATUS wma_save_bss_params(tp_wma_handle wma, struct bss_params *add_bss) { QDF_STATUS status; wma_vdev_set_he_config(wma, add_bss->staContext.smesessionId, add_bss); if (!wma_cdp_find_peer_by_addr(add_bss->bssId)) status = QDF_STATUS_E_FAILURE; else status = QDF_STATUS_SUCCESS; qdf_mem_copy(add_bss->staContext.staMac, add_bss->bssId, sizeof(add_bss->staContext.staMac)); wma_debug("update_bss %d nw_type %d bssid "QDF_MAC_ADDR_FMT" status %d", add_bss->updateBss, add_bss->nwType, QDF_MAC_ADDR_REF(add_bss->bssId), status); return status; } QDF_STATUS wma_pre_assoc_req(struct bss_params *add_bss) { QDF_STATUS status; tp_wma_handle wma; wma = cds_get_context(QDF_MODULE_ID_WMA); if (!wma) return QDF_STATUS_E_INVAL; status = wma_update_iface_params(wma, add_bss); if (QDF_IS_STATUS_ERROR(status)) return status; status = wma_save_bss_params(wma, add_bss); return status; } void wma_add_bss_lfr3(tp_wma_handle wma, struct bss_params *add_bss) { QDF_STATUS status; status = wma_update_iface_params(wma, add_bss); if (QDF_IS_STATUS_ERROR(status)) return; if (!wma_cdp_find_peer_by_addr(add_bss->bssId)) { wma_err("Failed to find peer "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(add_bss->bssId)); return; } wma_debug("LFR3: bssid "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(add_bss->bssId)); } #if defined(QCA_LL_LEGACY_TX_FLOW_CONTROL) || defined(QCA_LL_TX_FLOW_CONTROL_V2) static QDF_STATUS wma_set_cdp_vdev_pause_reason(tp_wma_handle wma, uint8_t vdev_id) { void *soc = cds_get_context(QDF_MODULE_ID_SOC); cdp_fc_vdev_pause(soc, vdev_id, OL_TXQ_PAUSE_REASON_PEER_UNAUTHORIZED, 0); return QDF_STATUS_SUCCESS; } #else static inline QDF_STATUS wma_set_cdp_vdev_pause_reason(tp_wma_handle wma, uint8_t vdev_id) { return QDF_STATUS_SUCCESS; } #endif void wma_send_add_bss_resp(tp_wma_handle wma, uint8_t vdev_id, QDF_STATUS status) { struct add_bss_rsp *add_bss_rsp; add_bss_rsp = qdf_mem_malloc(sizeof(*add_bss_rsp)); if (!add_bss_rsp) return; add_bss_rsp->vdev_id = vdev_id; add_bss_rsp->status = status; lim_handle_add_bss_rsp(wma->mac_context, add_bss_rsp); } #ifdef WLAN_FEATURE_HOST_ROAM QDF_STATUS wma_add_bss_lfr2_vdev_start(struct wlan_objmgr_vdev *vdev, struct bss_params *add_bss) { tp_wma_handle wma; QDF_STATUS status; struct vdev_mlme_obj *mlme_obj; uint8_t vdev_id; bool peer_exist = false; wma = cds_get_context(QDF_MODULE_ID_WMA); if (!wma || !vdev) { wma_err("Invalid wma or vdev"); return QDF_STATUS_E_INVAL; } vdev_id = vdev->vdev_objmgr.vdev_id; mlme_obj = wlan_vdev_mlme_get_cmpt_obj(vdev); if (!mlme_obj) { wma_err("vdev component object is NULL"); return QDF_STATUS_E_FAILURE; } status = wma_update_iface_params(wma, add_bss); if (QDF_IS_STATUS_ERROR(status)) goto send_fail_resp; peer_exist = wma_cdp_find_peer_by_addr(mlme_obj->mgmt.generic.bssid); if (!peer_exist) { wma_err("Failed to find peer "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(mlme_obj->mgmt.generic.bssid)); goto send_fail_resp; } status = wma_vdev_pre_start(vdev_id, false); if (QDF_IS_STATUS_ERROR(status)) { wma_err("failed, status: %d", status); goto peer_cleanup; } status = vdev_mgr_start_send(mlme_obj, false); if (QDF_IS_STATUS_ERROR(status)) { wma_err("failed, status: %d", status); goto peer_cleanup; } status = wma_set_cdp_vdev_pause_reason(wma, vdev_id); if (QDF_IS_STATUS_ERROR(status)) goto peer_cleanup; /* ADD_BSS_RESP will be deferred to completion of VDEV_START */ return QDF_STATUS_SUCCESS; peer_cleanup: if (peer_exist) wma_remove_peer(wma, mlme_obj->mgmt.generic.bssid, vdev_id, false); send_fail_resp: wma_send_add_bss_resp(wma, vdev_id, QDF_STATUS_E_FAILURE); return QDF_STATUS_SUCCESS; } #endif QDF_STATUS wma_set_vdev_bw(uint8_t vdev_id, uint8_t bw) { QDF_STATUS status; tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); if (!wma) return QDF_STATUS_E_INVAL; status = wma_vdev_set_param(wma->wmi_handle, vdev_id, wmi_vdev_param_chwidth, bw); if (QDF_IS_STATUS_ERROR(status)) wma_err("failed to set vdev bw, status: %d", status); return status; } QDF_STATUS wma_send_peer_assoc_req(struct bss_params *add_bss) { struct wma_target_req *msg; tp_wma_handle wma; uint8_t vdev_id; bool peer_exist; QDF_STATUS status; struct wma_txrx_node *iface; int pps_val = 0; struct vdev_mlme_obj *mlme_obj; struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); void *soc = cds_get_context(QDF_MODULE_ID_SOC); wma = cds_get_context(QDF_MODULE_ID_WMA); if (!wma || !mac || !soc) return QDF_STATUS_E_INVAL; vdev_id = add_bss->staContext.smesessionId; iface = &wma->interfaces[vdev_id]; status = wma_update_iface_params(wma, add_bss); if (QDF_IS_STATUS_ERROR(status)) goto send_resp; peer_exist = wma_cdp_find_peer_by_addr(add_bss->bssId); if (add_bss->nonRoamReassoc && peer_exist) goto send_resp; if (!add_bss->updateBss) goto send_resp; if (!peer_exist) { wma_err("Failed to find peer "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(add_bss->bssId)); status = QDF_STATUS_E_FAILURE; goto send_resp; } if (add_bss->staContext.encryptType == eSIR_ED_NONE) { wma_debug("Update peer("QDF_MAC_ADDR_FMT") state into auth", QDF_MAC_ADDR_REF(add_bss->bssId)); cdp_peer_state_update(soc, add_bss->bssId, OL_TXRX_PEER_STATE_AUTH); } else { wma_debug("Update peer("QDF_MAC_ADDR_FMT") state into conn", QDF_MAC_ADDR_REF(add_bss->bssId)); cdp_peer_state_update(soc, add_bss->bssId, OL_TXRX_PEER_STATE_CONN); status = wma_set_cdp_vdev_pause_reason(wma, vdev_id); if (QDF_IS_STATUS_ERROR(status)) goto send_resp; } wmi_unified_send_txbf(wma, &add_bss->staContext); pps_val = ((mac->mlme_cfg->sta.enable_5g_ebt << 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_err("Failed to send wmi packet power save cmd"); else wma_debug("Sent packet power save %x", pps_val); add_bss->staContext.no_ptk_4_way = add_bss->no_ptk_4_way; status = wma_send_peer_assoc(wma, add_bss->nwType, &add_bss->staContext); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to send peer assoc status:%d", status); goto send_resp; } /* we just had peer assoc, so install key will be done later */ if (add_bss->staContext.encryptType != eSIR_ED_NONE) iface->is_waiting_for_key = true; if (add_bss->rmfEnabled) wma_set_mgmt_frame_protection(wma); if (wlan_reg_is_ext_tpc_supported(wma->psoc)) add_bss->maxTxPower = 0; if (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, add_bss->bss_max_idle_period)) { wma_err("Failed to set bss params"); } mlme_obj = wlan_vdev_mlme_get_cmpt_obj(iface->vdev); if (!mlme_obj) { wma_err("Failed to mlme obj"); status = QDF_STATUS_E_FAILURE; goto send_resp; } /* * Store the bssid in interface table, bssid will * be used during group key setting sta mode. */ qdf_mem_copy(mlme_obj->mgmt.generic.bssid, add_bss->bssId, QDF_MAC_ADDR_SIZE); wma_save_bss_params(wma, add_bss); if (!wmi_service_enabled(wma->wmi_handle, wmi_service_peer_assoc_conf)) { wma_debug("WMI_SERVICE_PEER_ASSOC_CONF not enabled"); goto send_resp; } msg = wma_fill_hold_req(wma, vdev_id, WMA_ADD_BSS_REQ, WMA_PEER_ASSOC_CNF_START, NULL, WMA_PEER_ASSOC_TIMEOUT); if (!msg) { wma_err("Failed to allocate request for vdev_id %d", vdev_id); wma_remove_req(wma, vdev_id, WMA_PEER_ASSOC_CNF_START); status = QDF_STATUS_E_FAILURE; goto send_resp; } return QDF_STATUS_SUCCESS; send_resp: wma_send_add_bss_resp(wma, vdev_id, status); return QDF_STATUS_SUCCESS; } #ifdef WLAN_FEATURE_11BE_MLO /** * wma_get_mld_info_ap() - get mld mac addr and assoc peer flag for ap * @add_sta: tpAddStaParams * @peer_mld_addr: peer mld mac addr * @is_assoc_peer: is assoc peer or not * * Return: void */ static void wma_get_mld_info_ap(tpAddStaParams add_sta, uint8_t **peer_mld_addr, bool *is_assoc_peer) { if (add_sta) { *peer_mld_addr = add_sta->mld_mac_addr; *is_assoc_peer = add_sta->is_assoc_peer; } else { peer_mld_addr = NULL; *is_assoc_peer = false; } } #else static void wma_get_mld_info_ap(tpAddStaParams add_sta, uint8_t **peer_mld_addr, bool *is_assoc_peer) { *peer_mld_addr = NULL; *is_assoc_peer = false; } #endif /** * 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; uint8_t pdev_id = OL_TXRX_PDEV_ID; QDF_STATUS status; int32_t ret; struct wma_txrx_node *iface = NULL; struct wma_target_req *msg = NULL; bool peer_assoc_cnf = false; void *soc = cds_get_context(QDF_MODULE_ID_SOC); uint32_t i, j; uint16_t mcs_limit; uint8_t *rate_pos; struct mac_context *mac = wma->mac_context; uint8_t *peer_mld_addr = NULL; bool is_assoc_peer = false; /* 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; } iface = &wma->interfaces[add_sta->smesessionId]; if (cdp_find_peer_exist_on_vdev(soc, add_sta->smesessionId, add_sta->staMac)) { wma_remove_peer(wma, add_sta->staMac, add_sta->smesessionId, false); wma_err("Peer already exists, Deleted peer with peer_addr "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(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. */ if (cdp_find_peer_exist(soc, pdev_id, add_sta->staMac)) { wma_err("My vdev id %d, but Peer exists on other vdev with peer_addr "QDF_MAC_ADDR_FMT, add_sta->smesessionId, QDF_MAC_ADDR_REF(add_sta->staMac)); add_sta->status = QDF_STATUS_E_FAILURE; goto send_rsp; } wma_delete_invalid_peer_entries(add_sta->smesessionId, add_sta->staMac); wma_get_mld_info_ap(add_sta, &peer_mld_addr, &is_assoc_peer); status = wma_create_peer(wma, add_sta->staMac, WMI_PEER_TYPE_DEFAULT, add_sta->smesessionId, peer_mld_addr, is_assoc_peer); if (status != QDF_STATUS_SUCCESS) { wma_err("Failed to create peer for "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(add_sta->staMac)); add_sta->status = status; goto send_rsp; } if (!cdp_find_peer_exist_on_vdev(soc, add_sta->smesessionId, add_sta->staMac)) { wma_err("Failed to find peer handle using peer mac "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(add_sta->staMac)); add_sta->status = QDF_STATUS_E_FAILURE; wma_remove_peer(wma, add_sta->staMac, add_sta->smesessionId, 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 mcs_limit = mac->mlme_cfg->rates.sap_max_mcs_txdata; if (mcs_limit & CFG_CTRL_MASK) { wma_debug("set mcs_limit %x", 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_enabled(wma->wmi_handle, 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_err("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, false); peer_assoc_cnf = false; goto send_rsp; } } else { wma_err("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; if (msg) { wma_remove_req(wma, add_sta->smesessionId, WMA_PEER_ASSOC_CNF_START); peer_assoc_cnf = false; } wma_remove_peer(wma, add_sta->staMac, add_sta->smesessionId, false); goto send_rsp; } if (add_sta->rmfEnabled) wma_set_peer_pmf_status(wma, add_sta->staMac, true); 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_err("Failed to set peer uapsd param for "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(add_sta->staMac)); add_sta->status = QDF_STATUS_E_FAILURE; wma_remove_peer(wma, add_sta->staMac, add_sta->smesessionId, false); goto send_rsp; } } wma_debug("Moving peer "QDF_MAC_ADDR_FMT" to state %d", QDF_MAC_ADDR_REF(add_sta->staMac), state); cdp_peer_state_update(soc, add_sta->staMac, state); add_sta->nss = wma_objmgr_get_peer_mlme_nss(wma, add_sta->staMac); 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_debug("WMI_SERVICE_PEER_ASSOC_CONF is enabled"); return; } wma_debug("statype %d vdev_id %d aid %d bssid "QDF_MAC_ADDR_FMT" status %d", add_sta->staType, add_sta->smesessionId, add_sta->assocId, QDF_MAC_ADDR_REF(add_sta->bssId), 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) { QDF_STATUS status; int32_t ret; struct tdls_peer_update_state *peer_state; struct wma_target_req *msg; bool peer_assoc_cnf = false; void *soc = cds_get_context(QDF_MODULE_ID_SOC); uint8_t pdev_id = OL_TXRX_PDEV_ID; struct wma_txrx_node *iface = &wma->interfaces[add_sta->smesessionId]; wma_debug("staType: %d, updateSta: %d, bssId: "QDF_MAC_ADDR_FMT", staMac: "QDF_MAC_ADDR_FMT, add_sta->staType, add_sta->updateSta, QDF_MAC_ADDR_REF(add_sta->bssId), QDF_MAC_ADDR_REF(add_sta->staMac)); if (iface->vdev && wlan_cm_is_vdev_roaming(iface->vdev)) { wma_err("Vdev %d roaming in progress, reject add sta!", add_sta->smesessionId); add_sta->status = QDF_STATUS_E_PERM; goto send_rsp; } if (0 == add_sta->updateSta) { /* its a add sta request * */ cdp_peer_copy_mac_addr_raw(soc, add_sta->smesessionId, add_sta->bssId); wma_debug("addSta, calling wma_create_peer for "QDF_MAC_ADDR_FMT", vdev_id %hu", QDF_MAC_ADDR_REF(add_sta->staMac), add_sta->smesessionId); status = wma_create_peer(wma, add_sta->staMac, WMI_PEER_TYPE_TDLS, add_sta->smesessionId, NULL, false); if (status != QDF_STATUS_SUCCESS) { wma_err("Failed to create peer for "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(add_sta->staMac)); add_sta->status = status; goto send_rsp; } wma_debug("addSta, after calling cdp_local_peer_id, staMac: "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(add_sta->staMac)); peer_state = qdf_mem_malloc(sizeof(*peer_state)); if (!peer_state) { add_sta->status = QDF_STATUS_E_NOMEM; goto send_rsp; } peer_state->peer_state = WMI_TDLS_PEER_STATE_PEERING; peer_state->vdev_id = add_sta->smesessionId; qdf_mem_copy(&peer_state->peer_macaddr, &add_sta->staMac, sizeof(tSirMacAddr)); wma_update_tdls_peer_state(wma, peer_state); } else { if (wmi_service_enabled(wma->wmi_handle, wmi_service_peer_assoc_conf)) { wma_err("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_err("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, false); peer_assoc_cnf = false; goto send_rsp; } } else { wma_err("WMI_SERVICE_PEER_ASSOC_CONF not enabled"); } wma_debug("changeSta, calling wma_send_peer_assoc"); if (add_sta->rmfEnabled) wma_set_peer_pmf_status(wma, add_sta->staMac, true); 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, false); cdp_peer_add_last_real_peer(soc, pdev_id, add_sta->smesessionId); wma_remove_req(wma, add_sta->smesessionId, WMA_PEER_ASSOC_CNF_START); peer_assoc_cnf = false; goto send_rsp; } } send_rsp: /* Do not send add stat resp when peer assoc cnf is enabled */ if (peer_assoc_cnf) return; wma_debug("statype %d vdev_id %d aid %d bssid "QDF_MAC_ADDR_FMT" status %d", add_sta->staType, add_sta->smesessionId, add_sta->assocId, QDF_MAC_ADDR_REF(add_sta->bssId), add_sta->status); wma_send_msg_high_priority(wma, WMA_ADD_STA_RSP, (void *)add_sta, 0); } #endif /** * wma_send_bss_color_change_enable() - send bss color change enable cmd. * @wma: wma handle * @params: add sta params * * Send bss color change command to firmware, to enable firmware to update * internally if any change in bss color in advertised by associated AP. * * Return: none */ #ifdef WLAN_FEATURE_11AX static void wma_send_bss_color_change_enable(tp_wma_handle wma, tpAddStaParams params) { QDF_STATUS status; uint32_t vdev_id = params->smesessionId; if (!params->he_capable) { wma_debug("he_capable is not set for vdev_id:%d", vdev_id); return; } status = wmi_unified_send_bss_color_change_enable_cmd(wma->wmi_handle, vdev_id, true); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to enable bss color change offload, vdev:%d", vdev_id); } return; } #else static void wma_send_bss_color_change_enable(tp_wma_handle wma, tpAddStaParams params) { } #endif #define MAX_VDEV_STA_REQ_PARAMS 5 /* params being sent: * 1.wmi_vdev_param_max_li_of_moddtim * 2.wmi_vdev_param_max_li_of_moddtim_ms * 3.wmi_vdev_param_dyndtim_cnt * 4.wmi_vdev_param_moddtim_cnt * 5.wmi_vdev_param_moddtim_cnt */ /** * wma_add_sta_req_sta_mode() - process add sta request in sta mode * @wma: wma handle * @params: add sta params * * Return: none */ static void wma_add_sta_req_sta_mode(tp_wma_handle wma, tpAddStaParams params) { QDF_STATUS status = QDF_STATUS_SUCCESS; struct wma_txrx_node *iface; int8_t maxTxPower = 0; int ret = 0; struct wma_target_req *msg; bool peer_assoc_cnf = false; int smps_param; void *soc = cds_get_context(QDF_MODULE_ID_SOC); struct dev_set_param setparam[MAX_VDEV_STA_REQ_PARAMS]; uint8_t index = 0; #ifdef FEATURE_WLAN_TDLS if (STA_ENTRY_TDLS_PEER == params->staType) { wma_add_tdls_sta(wma, params); return; } #endif iface = &wma->interfaces[params->smesessionId]; if (params->staType != STA_ENTRY_SELF) { wma_err("unsupported station type %d", params->staType); goto out; } if (params->nonRoamReassoc) { cdp_peer_state_update(soc, 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_is_vdev_up(params->smesessionId)) { wma_debug("vdev id %d is already UP for "QDF_MAC_ADDR_FMT, params->smesessionId, QDF_MAC_ADDR_REF(params->bssId)); status = QDF_STATUS_E_FAILURE; goto out; } if (cdp_peer_state_get(soc, params->smesessionId, params->bssId, true) == 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_debug("Update peer("QDF_MAC_ADDR_FMT") state into auth", QDF_MAC_ADDR_REF(params->bssId)); cdp_peer_state_update(soc, params->bssId, OL_TXRX_PEER_STATE_AUTH); } else { wma_debug("Update peer("QDF_MAC_ADDR_FMT") state into conn", QDF_MAC_ADDR_REF(params->bssId)); cdp_peer_state_update(soc, params->bssId, OL_TXRX_PEER_STATE_CONN); } if (wlan_cm_is_roam_sync_in_progress(wma->psoc, params->smesessionId) || MLME_IS_MLO_ROAM_SYNCH_IN_PROGRESS(wma->psoc, 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 */ cdp_peer_state_update(soc, params->bssId, OL_TXRX_PEER_STATE_AUTH); qdf_atomic_set(&iface->bss_status, WMA_BSS_STATUS_STARTED); iface->aid = params->assocId; wma_debug("LFR3:statype %d vdev %d aid %d bssid "QDF_MAC_ADDR_FMT, params->staType, params->smesessionId, params->assocId, QDF_MAC_ADDR_REF(params->bssId)); return; } wmi_unified_send_txbf(wma, params); if (wmi_service_enabled(wma->wmi_handle, wmi_service_peer_assoc_conf)) { 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_debug("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->bssId, params->smesessionId, false); peer_assoc_cnf = false; goto out; } } else { wma_debug("WMI_SERVICE_PEER_ASSOC_CONF not enabled"); } ((tAddStaParams *)iface->addBssStaContext)->no_ptk_4_way = params->no_ptk_4_way; qdf_mem_copy(((tAddStaParams *)iface->addBssStaContext)-> supportedRates.supportedMCSSet, params->supportedRates.supportedMCSSet, SIR_MAC_MAX_SUPPORTED_MCS_SET); 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, false); goto out; } if (params->rmfEnabled) { wma_set_mgmt_frame_protection(wma); wma_set_peer_pmf_status(wma, params->bssId, true); } } if (!wlan_reg_is_ext_tpc_supported(wma->psoc)) maxTxPower = params->maxTxPower; if (wma_vdev_set_bss_params(wma, params->smesessionId, iface->beaconInterval, iface->dtimPeriod, iface->shortSlotTimeSupported, iface->llbCoexist, maxTxPower, iface->bss_max_idle_period)) { wma_err("Failed to bss params"); } params->csaOffloadEnable = 0; if (wmi_service_enabled(wma->wmi_handle, wmi_service_csa_offload)) { params->csaOffloadEnable = 1; if (wma_unified_csa_offload_enable(wma, params->smesessionId) < 0) { wma_err("Unable to enable CSA offload for vdev_id:%d", params->smesessionId); } } if (wmi_service_enabled(wma->wmi_handle, wmi_service_filter_ipsec_natkeepalive)) { if (wmi_unified_nat_keepalive_en_cmd(wma->wmi_handle, params->smesessionId)) { wma_err("Unable to enable NAT keepalive for vdev_id:%d", params->smesessionId); } } qdf_atomic_set(&iface->bss_status, WMA_BSS_STATUS_STARTED); /* 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 enabled 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_debug("Send SMPS force mode: %d", params->htSmpsconfig); wma_set_mimops(wma, params->smesessionId, smps_param); } } wma_send_bss_color_change_enable(wma, params); /* 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_enabled(wma->wmi_handle, wmi_service_listen_interval_offload_support)) { struct wlan_objmgr_vdev *vdev = NULL; uint32_t moddtim; bool is_connection_roaming_cfg_set = 0; wma_debug("listen interval offload enabled, setting params"); status = mlme_check_index_setparam( setparam, wmi_vdev_param_max_li_of_moddtim, wma->staMaxLIModDtim, index++, MAX_VDEV_STA_REQ_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to send wmi_vdev_param_max_li_of_moddtim"); goto out; } ucfg_mlme_get_connection_roaming_ini_present( wma->psoc, &is_connection_roaming_cfg_set); if (is_connection_roaming_cfg_set) { status = mlme_check_index_setparam( setparam, wmi_vdev_param_max_li_of_moddtim_ms, wma->sta_max_li_mod_dtim_ms, index++, MAX_VDEV_STA_REQ_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to send wmi_vdev_param_max_li_of_moddtim_ms"); goto out; } } status = mlme_check_index_setparam( setparam, wmi_vdev_param_dyndtim_cnt, wma->staDynamicDtim, index++, MAX_VDEV_STA_REQ_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to send wmi_vdev_param_dyndtim_cnt"); goto out; } vdev = wlan_objmgr_get_vdev_by_id_from_psoc(wma->psoc, params->smesessionId, WLAN_LEGACY_WMA_ID); if (!vdev) { wma_debug("Invalid vdev"); goto out; } if (!ucfg_pmo_get_moddtim_user_enable(vdev)) { moddtim = wma->staModDtim; status = mlme_check_index_setparam( setparam, wmi_vdev_param_moddtim_cnt, moddtim, index++, MAX_VDEV_STA_REQ_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to send wmi_vdev_param_moddtim_cnt"); goto rel_ref; } } else if (ucfg_pmo_get_moddtim_user_enable(vdev) && !ucfg_pmo_get_moddtim_user_active(vdev)) { moddtim = ucfg_pmo_get_moddtim_user(vdev); status = mlme_check_index_setparam( setparam, wmi_vdev_param_moddtim_cnt, moddtim, index++, MAX_VDEV_STA_REQ_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to send wmi_vdev_param_moddtim_cnt"); goto rel_ref; } } status = wma_send_multi_pdev_vdev_set_params(MLME_VDEV_SETPARAM, params->smesessionId, setparam, index); if (QDF_IS_STATUS_ERROR(status)) { wma_err("failed to send DTIM vdev setparams"); } rel_ref: if (vdev) wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); } else { wma_debug("listen interval offload is not set"); } params->nss = iface->nss; out: iface->aid = params->assocId; /* Do not send add stat resp when peer assoc cnf is enabled */ if (peer_assoc_cnf) return; params->status = status; wma_debug("vdev_id %d aid %d sta mac " QDF_MAC_ADDR_FMT " status %d", params->smesessionId, iface->aid, QDF_MAC_ADDR_REF(params->bssId), params->status); /* Don't send a response during roam sync operation */ if (!wlan_cm_is_roam_sync_in_progress(wma->psoc, params->smesessionId) && !MLME_IS_MLO_ROAM_SYNCH_IN_PROGRESS(wma->psoc, params->smesessionId)) wma_send_msg_high_priority(wma, WMA_ADD_STA_RSP, (void *)params, 0); } /** * wma_delete_sta_req_ap_mode() - process 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) { struct wma_target_req *msg; QDF_STATUS qdf_status; qdf_status = wma_remove_peer(wma, del_sta->staMac, del_sta->smesessionId, false); if (QDF_IS_STATUS_ERROR(qdf_status)) { wma_err("wma_remove_peer failed"); del_sta->status = QDF_STATUS_E_FAILURE; goto send_del_rsp; } del_sta->status = QDF_STATUS_SUCCESS; if (wmi_service_enabled(wma->wmi_handle, 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_err("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_debug("Sending del rsp to umac (status: %d)", 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() - process 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) { struct tdls_peer_update_state *peer_state; struct wma_target_req *msg; int status; peer_state = qdf_mem_malloc(sizeof(*peer_state)); if (!peer_state) { del_sta->status = QDF_STATUS_E_NOMEM; goto send_del_rsp; } peer_state->peer_state = TDLS_PEER_STATE_TEARDOWN; peer_state->vdev_id = del_sta->smesessionId; peer_state->resp_reqd = del_sta->respReqd; qdf_mem_copy(&peer_state->peer_macaddr, &del_sta->staMac, sizeof(tSirMacAddr)); wma_debug("sending tdls_peer_state for peer mac: "QDF_MAC_ADDR_FMT", peerState: %d", QDF_MAC_ADDR_REF(peer_state->peer_macaddr), peer_state->peer_state); status = wma_update_tdls_peer_state(wma, peer_state); if (status < 0) { wma_err("wma_update_tdls_peer_state returned failure"); del_sta->status = QDF_STATUS_E_FAILURE; goto send_del_rsp; } if (del_sta->respReqd && wmi_service_enabled(wma->wmi_handle, 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_err("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_debug("Sending del rsp to umac (status: %d)", del_sta->status); wma_send_msg_high_priority(wma, WMA_DELETE_STA_RSP, (void *)del_sta, 0); } } #endif /** * wma_delete_sta_req_sta_mode() - process 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; if (wmi_service_enabled(wma->wmi_handle, wmi_service_listen_interval_offload_support)) { struct wlan_objmgr_vdev *vdev; vdev = wlan_objmgr_get_vdev_by_id_from_psoc(wma->psoc, params->smesessionId, WLAN_LEGACY_WMA_ID); if (vdev) { if (ucfg_pmo_get_moddtim_user_enable(vdev)) ucfg_pmo_set_moddtim_user_enable(vdev, false); wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); } } iface = &wma->interfaces[params->smesessionId]; iface->uapsd_cached_val = 0; #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_debug("vdev_id %d status %d", params->smesessionId, status); wma_send_msg_high_priority(wma, WMA_DELETE_STA_RSP, (void *)params, 0); } } static void wma_sap_prevent_runtime_pm(tp_wma_handle wma) { qdf_runtime_pm_prevent_suspend(&wma->sap_prevent_runtime_pm_lock); } static void wma_sap_allow_runtime_pm(tp_wma_handle wma) { qdf_runtime_pm_allow_suspend(&wma->sap_prevent_runtime_pm_lock); } static void wma_ndp_prevent_runtime_pm(tp_wma_handle wma) { qdf_runtime_pm_prevent_suspend(&wma->ndp_prevent_runtime_pm_lock); } static void wma_ndp_allow_runtime_pm(tp_wma_handle wma) { qdf_runtime_pm_allow_suspend(&wma->ndp_prevent_runtime_pm_lock); } #ifdef FEATURE_STA_MODE_VOTE_LINK static bool wma_add_sta_allow_sta_mode_vote_link(uint8_t oper_mode) { if (oper_mode == BSS_OPERATIONAL_MODE_STA && ucfg_ipa_is_enabled()) return true; return false; } #else /* !FEATURE_STA_MODE_VOTE_LINK */ static bool wma_add_sta_allow_sta_mode_vote_link(uint8_t oper_mode) { return false; } #endif /* FEATURE_STA_MODE_VOTE_LINK */ static bool wma_is_vdev_in_sap_mode(tp_wma_handle wma, uint8_t vdev_id) { struct wma_txrx_node *intf = wma->interfaces; if (vdev_id >= wma->max_bssid) { wma_err("Invalid vdev_id %hu", vdev_id); QDF_ASSERT(0); return false; } if ((intf[vdev_id].type == WMI_VDEV_TYPE_AP) && (intf[vdev_id].sub_type == 0)) return true; return false; } static bool wma_is_vdev_in_go_mode(tp_wma_handle wma, uint8_t vdev_id) { struct wma_txrx_node *intf = wma->interfaces; if (vdev_id >= wma->max_bssid) { wma_err("Invalid vdev_id %hu", 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)) return true; return false; } static void wma_sap_d3_wow_client_connect(tp_wma_handle wma) { uint32_t num_clients; num_clients = qdf_atomic_inc_return(&wma->sap_num_clients_connected); wmi_debug("sap d3 wow %d client connected", num_clients); if (num_clients == SAP_D3_WOW_MAX_CLIENT_HOLD_WAKE_LOCK) { wmi_info("max clients connected acquire sap d3 wow wake lock"); qdf_wake_lock_acquire(&wma->sap_d3_wow_wake_lock, WIFI_POWER_EVENT_WAKELOCK_SAP_D3_WOW); } } static void wma_sap_d3_wow_client_disconnect(tp_wma_handle wma) { uint32_t num_clients; num_clients = qdf_atomic_dec_return(&wma->sap_num_clients_connected); wmi_debug("sap d3 wow %d client connected", num_clients); if (num_clients == SAP_D3_WOW_MAX_CLIENT_RELEASE_WAKE_LOCK) { wmi_info("max clients disconnected release sap d3 wow wake lock"); qdf_wake_lock_release(&wma->sap_d3_wow_wake_lock, WIFI_POWER_EVENT_WAKELOCK_SAP_D3_WOW); } } static void wma_go_d3_wow_client_connect(tp_wma_handle wma) { uint32_t num_clients; num_clients = qdf_atomic_inc_return(&wma->go_num_clients_connected); wmi_debug("go d3 wow %d client connected", num_clients); if (num_clients == SAP_D3_WOW_MAX_CLIENT_HOLD_WAKE_LOCK) { wmi_info("max clients connected acquire go d3 wow wake lock"); qdf_wake_lock_acquire(&wma->go_d3_wow_wake_lock, WIFI_POWER_EVENT_WAKELOCK_GO_D3_WOW); } } static void wma_go_d3_wow_client_disconnect(tp_wma_handle wma) { uint32_t num_clients; num_clients = qdf_atomic_dec_return(&wma->go_num_clients_connected); wmi_debug("go d3 wow %d client connected", num_clients); if (num_clients == SAP_D3_WOW_MAX_CLIENT_RELEASE_WAKE_LOCK) { wmi_info("max clients disconnected release go d3 wow wake lock"); qdf_wake_lock_release(&wma->go_d3_wow_wake_lock, WIFI_POWER_EVENT_WAKELOCK_GO_D3_WOW); } } void wma_add_sta(tp_wma_handle wma, tpAddStaParams add_sta) { uint8_t oper_mode = BSS_OPERATIONAL_MODE_STA; void *htc_handle; QDF_STATUS status = QDF_STATUS_SUCCESS; uint8_t vdev_id = add_sta->smesessionId; htc_handle = lmac_get_htc_hdl(wma->psoc); if (!htc_handle) { wma_err("HTC handle is NULL"); return; } wma_debug("Vdev %d BSSID "QDF_MAC_ADDR_FMT, vdev_id, QDF_MAC_ADDR_REF(add_sta->bssId)); if (wma_is_vdev_in_ap_mode(wma, vdev_id)) oper_mode = BSS_OPERATIONAL_MODE_AP; if (WMA_IS_VDEV_IN_NDI_MODE(wma->interfaces, vdev_id)) oper_mode = BSS_OPERATIONAL_MODE_NDI; switch (oper_mode) { case BSS_OPERATIONAL_MODE_STA: wma_add_sta_req_sta_mode(wma, add_sta); break; case BSS_OPERATIONAL_MODE_AP: wma_add_sta_req_ap_mode(wma, add_sta); break; case BSS_OPERATIONAL_MODE_NDI: status = wma_add_sta_ndi_mode(wma, add_sta); break; } /* * not use add_sta after this to avoid use after free * as it maybe freed. */ /* handle wow for sap with 1 or more peer in same way */ if (wma_is_vdev_in_sap_mode(wma, vdev_id)) { bool is_bus_suspend_allowed_in_sap_mode = (wlan_pmo_get_sap_mode_bus_suspend(wma->psoc) && wmi_service_enabled(wma->wmi_handle, wmi_service_sap_connected_d3_wow)); if (!is_bus_suspend_allowed_in_sap_mode) { htc_vote_link_up(htc_handle, HTC_LINK_VOTE_SAP_USER_ID); wmi_info("sap d0 wow"); } else { wmi_debug("sap d3 wow"); wma_sap_d3_wow_client_connect(wma); } wma_sap_prevent_runtime_pm(wma); return; } /* handle wow for p2pgo with 1 or more peer in same way */ if (wma_is_vdev_in_go_mode(wma, vdev_id)) { bool is_bus_suspend_allowed_in_go_mode = (wlan_pmo_get_go_mode_bus_suspend(wma->psoc) && wmi_service_enabled(wma->wmi_handle, wmi_service_go_connected_d3_wow)); if (!is_bus_suspend_allowed_in_go_mode) { htc_vote_link_up(htc_handle, HTC_LINK_VOTE_GO_USER_ID); wmi_info("p2p go d0 wow"); } else { wmi_info("p2p go d3 wow"); wma_go_d3_wow_client_connect(wma); } wma_sap_prevent_runtime_pm(wma); return; } /* handle wow for nan with 1 or more peer in same way */ if (BSS_OPERATIONAL_MODE_NDI == oper_mode && QDF_IS_STATUS_SUCCESS(status)) { wma_debug("disable runtime pm and vote for link up"); htc_vote_link_up(htc_handle, HTC_LINK_VOTE_NDP_USER_ID); wma_ndp_prevent_runtime_pm(wma); } else if (wma_add_sta_allow_sta_mode_vote_link(oper_mode)) { wma_debug("vote for link up"); htc_vote_link_up(htc_handle, HTC_LINK_VOTE_STA_USER_ID); } } void wma_delete_sta(tp_wma_handle wma, tpDeleteStaParams del_sta) { uint8_t oper_mode = BSS_OPERATIONAL_MODE_STA; uint8_t vdev_id = del_sta->smesessionId; bool rsp_requested = del_sta->respReqd; void *htc_handle; QDF_STATUS status = QDF_STATUS_SUCCESS; htc_handle = lmac_get_htc_hdl(wma->psoc); if (!htc_handle) { wma_err("HTC handle is NULL"); return; } if (wma_is_vdev_in_ap_mode(wma, vdev_id)) oper_mode = BSS_OPERATIONAL_MODE_AP; if (del_sta->staType == STA_ENTRY_NDI_PEER) oper_mode = BSS_OPERATIONAL_MODE_NDI; wma_debug("vdev %d oper_mode %d", vdev_id, oper_mode); switch (oper_mode) { case BSS_OPERATIONAL_MODE_STA: if (wlan_cm_is_roam_sync_in_progress(wma->psoc, vdev_id) || MLME_IS_MLO_ROAM_SYNCH_IN_PROGRESS(wma->psoc, vdev_id) || mlo_is_roaming_in_progress(wma->psoc, vdev_id)) { wma_debug("LFR3: Del STA on vdev_id %d", vdev_id); qdf_mem_free(del_sta); return; } wma_delete_sta_req_sta_mode(wma, del_sta); if (!rsp_requested) qdf_mem_free(del_sta); break; case BSS_OPERATIONAL_MODE_AP: 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_enabled(wma->wmi_handle, wmi_service_sync_delete_cmds)) qdf_mem_free(del_sta); else if (!rsp_requested && (del_sta->status != QDF_STATUS_SUCCESS)) qdf_mem_free(del_sta); break; case BSS_OPERATIONAL_MODE_NDI: status = wma_delete_sta_req_ndi_mode(wma, del_sta); break; default: wma_err("Incorrect oper mode %d", oper_mode); qdf_mem_free(del_sta); } if (wma_is_vdev_in_sap_mode(wma, vdev_id)) { bool is_bus_suspend_allowed_in_sap_mode = (wlan_pmo_get_sap_mode_bus_suspend(wma->psoc) && wmi_service_enabled(wma->wmi_handle, wmi_service_sap_connected_d3_wow)); if (!is_bus_suspend_allowed_in_sap_mode) { htc_vote_link_down(htc_handle, HTC_LINK_VOTE_SAP_USER_ID); wmi_info("sap d0 wow"); } else { wmi_debug("sap d3 wow"); wma_sap_d3_wow_client_disconnect(wma); } wma_sap_allow_runtime_pm(wma); return; } if (wma_is_vdev_in_go_mode(wma, vdev_id)) { bool is_bus_suspend_allowed_in_go_mode = (wlan_pmo_get_go_mode_bus_suspend(wma->psoc) && wmi_service_enabled(wma->wmi_handle, wmi_service_go_connected_d3_wow)); if (!is_bus_suspend_allowed_in_go_mode) { htc_vote_link_down(htc_handle, HTC_LINK_VOTE_GO_USER_ID); wmi_info("p2p go d0 wow"); } else { wmi_info("p2p go d3 wow"); wma_go_d3_wow_client_disconnect(wma); } wma_sap_allow_runtime_pm(wma); return; } if (BSS_OPERATIONAL_MODE_NDI == oper_mode && QDF_IS_STATUS_SUCCESS(status)) { wma_debug("allow runtime pm and vote for link down"); htc_vote_link_down(htc_handle, HTC_LINK_VOTE_NDP_USER_ID); wma_ndp_allow_runtime_pm(wma); } else if (wma_add_sta_allow_sta_mode_vote_link(oper_mode)) { wma_debug("vote for link down"); htc_vote_link_down(htc_handle, HTC_LINK_VOTE_STA_USER_ID); } } void wma_delete_bss_ho_fail(tp_wma_handle wma, uint8_t vdev_id) { QDF_STATUS status = QDF_STATUS_SUCCESS; struct wma_txrx_node *iface; void *soc = cds_get_context(QDF_MODULE_ID_SOC); struct vdev_stop_response resp_event; struct del_bss_resp *vdev_stop_resp; uint8_t *bssid; iface = &wma->interfaces[vdev_id]; if (!iface) { wma_err("iface for vdev_id %d is already deleted", vdev_id); goto fail_del_bss_ho_fail; } bssid = wma_get_vdev_bssid(iface->vdev); if (!bssid) { wma_err("Invalid bssid"); status = QDF_STATUS_E_FAILURE; goto fail_del_bss_ho_fail; } qdf_mem_zero(bssid, QDF_MAC_ADDR_SIZE); 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 (iface->roam_scan_stats_req) { struct sir_roam_scan_stats *roam_scan_stats_req = iface->roam_scan_stats_req; iface->roam_scan_stats_req = NULL; qdf_mem_free(roam_scan_stats_req); } wma_debug("vdev_id: %d, pausing tx_ll_queue for VDEV_STOP (del_bss)", vdev_id); cdp_fc_vdev_pause(soc, vdev_id, OL_TXQ_PAUSE_REASON_VDEV_STOP, 0); wma_vdev_set_pause_bit(vdev_id, PAUSE_TYPE_HOST); cdp_fc_vdev_flush(soc, vdev_id); wma_debug("vdev_id: %d, un-pausing tx_ll_queue for VDEV_STOP rsp", vdev_id); cdp_fc_vdev_unpause(soc, vdev_id, OL_TXQ_PAUSE_REASON_VDEV_STOP, 0); wma_vdev_clear_pause_bit(vdev_id, PAUSE_TYPE_HOST); qdf_atomic_set(&iface->bss_status, WMA_BSS_STATUS_STOPPED); wma_debug("(type %d subtype %d) BSS is stopped", iface->type, iface->sub_type); status = mlme_set_vdev_stop_type(iface->vdev, WMA_DELETE_BSS_HO_FAIL_REQ); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to set wma req msg_type for vdev_id: %d", vdev_id); goto fail_del_bss_ho_fail; } /* Try to use the vdev stop response path */ resp_event.vdev_id = vdev_id; status = wma_handle_vdev_stop_rsp(wma, &resp_event); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to handle vdev stop rsp for vdev_id %d", vdev_id); goto fail_del_bss_ho_fail; } return; fail_del_bss_ho_fail: vdev_stop_resp = qdf_mem_malloc(sizeof(*vdev_stop_resp)); if (!vdev_stop_resp) return; vdev_stop_resp->vdev_id = vdev_id; vdev_stop_resp->status = status; wma_send_msg_high_priority(wma, WMA_DELETE_BSS_HO_FAIL_RSP, (void *)vdev_stop_resp, 0); } /** * 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) { uint8_t max_wait_iterations = 0, delay = 0; cdp_config_param_type val; void *soc = cds_get_context(QDF_MODULE_ID_SOC); QDF_STATUS status; if (!wma_is_vdev_valid(session_id)) { wma_err("Vdev is not valid: %d", session_id); return; } status = ucfg_mlme_get_delay_before_vdev_stop(wma->psoc, &delay); if (QDF_IS_STATUS_ERROR(status)) wma_err("Failed to get delay before vdev stop"); max_wait_iterations = delay / WMA_TX_Q_RECHECK_TIMER_WAIT; if (cdp_txrx_get_pdev_param(soc, wlan_objmgr_pdev_get_pdev_id(wma->pdev), CDP_TX_PENDING, &val)) return; while (val.cdp_pdev_param_tx_pending && max_wait_iterations) { wma_warn("Waiting for outstanding packet to drain"); qdf_wait_for_event_completion(&wma->tx_queue_empty_event, WMA_TX_Q_RECHECK_TIMER_WAIT); if (cdp_txrx_get_pdev_param( soc, wlan_objmgr_pdev_get_pdev_id(wma->pdev), CDP_TX_PENDING, &val)) return; max_wait_iterations--; } } void wma_delete_bss(tp_wma_handle wma, uint8_t vdev_id) { bool peer_exist = false; QDF_STATUS status = QDF_STATUS_SUCCESS; uint32_t tx_pending = 0; cdp_config_param_type val; bool roam_synch_in_progress = false; struct wma_txrx_node *iface; void *soc = cds_get_context(QDF_MODULE_ID_SOC); struct qdf_mac_addr bssid; struct del_bss_resp *params; uint8_t *addr, *bssid_addr; iface = &wma->interfaces[vdev_id]; if (!iface || !iface->vdev) { wma_err("vdev id %d is already deleted", vdev_id); goto out; } status = wlan_vdev_get_bss_peer_mac(iface->vdev, &bssid); if (QDF_IS_STATUS_ERROR(status)) { wma_err("vdev id %d : failed to get bssid", vdev_id); goto out; } addr = wlan_vdev_mlme_get_macaddr(iface->vdev); if (!addr) { wma_err("vdev id %d : failed to get macaddr", vdev_id); goto out; } if (WMA_IS_VDEV_IN_NDI_MODE(wma->interfaces, vdev_id)) /* In ndi case, self mac is used to create the self peer */ peer_exist = wma_cdp_find_peer_by_addr(addr); else peer_exist = wma_cdp_find_peer_by_addr(bssid.bytes); if (!peer_exist) { wma_err("Failed to find peer "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(bssid.bytes)); status = QDF_STATUS_E_FAILURE; goto out; } bssid_addr = wma_get_vdev_bssid(wma->interfaces[vdev_id].vdev); if (!bssid_addr) { wma_err("Failed to bssid for vdev_%d", vdev_id); status = QDF_STATUS_E_FAILURE; goto out; } qdf_mem_zero(bssid_addr, QDF_MAC_ADDR_SIZE); wma_delete_invalid_peer_entries(vdev_id, 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 (iface->roam_scan_stats_req) { struct sir_roam_scan_stats *roam_scan_stats_req = iface->roam_scan_stats_req; iface->roam_scan_stats_req = NULL; qdf_mem_free(roam_scan_stats_req); } if (wlan_cm_is_roam_sync_in_progress(wma->psoc, vdev_id) || MLME_IS_MLO_ROAM_SYNCH_IN_PROGRESS(wma->psoc, vdev_id) || mlo_is_roaming_in_progress(wma->psoc, vdev_id)) { roam_synch_in_progress = true; wma_debug("LFR3: Setting vdev_up to FALSE for vdev:%d", vdev_id); goto detach_peer; } status = mlme_set_vdev_stop_type(iface->vdev, WMA_DELETE_BSS_REQ); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to set wma req msg_type for vdev_id: %d", vdev_id); goto out; } cdp_txrx_get_pdev_param(soc, wlan_objmgr_pdev_get_pdev_id(wma->pdev), CDP_TX_PENDING, &val); tx_pending = val.cdp_pdev_param_tx_pending; wma_debug("Outstanding msdu packets: %u", tx_pending); wma_wait_tx_complete(wma, vdev_id); cdp_txrx_get_pdev_param(soc, wlan_objmgr_pdev_get_pdev_id(wma->pdev), CDP_TX_PENDING, &val); if (tx_pending) { wma_debug("Outstanding msdu packets before VDEV_STOP : %u", tx_pending); } wma_debug("vdev_id: %d, pausing tx_ll_queue for VDEV_STOP (del_bss)", vdev_id); wma_vdev_set_pause_bit(vdev_id, PAUSE_TYPE_HOST); cdp_fc_vdev_pause(soc, vdev_id, OL_TXQ_PAUSE_REASON_VDEV_STOP, 0); if (wma_send_vdev_stop_to_fw(wma, vdev_id)) { struct vdev_stop_response vdev_stop_rsp = {0}; wma_err("Failed to send vdev stop to FW, explicitly invoke vdev stop rsp"); vdev_stop_rsp.vdev_id = vdev_id; wma_handle_vdev_stop_rsp(wma, &vdev_stop_rsp); qdf_atomic_set(&iface->bss_status, WMA_BSS_STATUS_STOPPED); } wma_debug("bssid "QDF_MAC_ADDR_FMT" vdev_id %d", QDF_MAC_ADDR_REF(bssid.bytes), vdev_id); return; detach_peer: wma_remove_peer(wma, bssid.bytes, vdev_id, roam_synch_in_progress); if (roam_synch_in_progress) return; out: /* skip when legacy to mlo roam sync ongoing */ if (MLME_IS_MLO_ROAM_SYNCH_IN_PROGRESS(wma->psoc, vdev_id)) return; params = qdf_mem_malloc(sizeof(*params)); if (!params) return; params->vdev_id = vdev_id; params->status = status; wma_send_msg_high_priority(wma, WMA_DELETE_BSS_RSP, params, 0); } /** * wma_find_vdev_by_type() - 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 (intf) { if (intf[vdev_id].type == type) return vdev_id; } } return -EFAULT; } void wma_set_vdev_intrabss_fwd(tp_wma_handle wma_handle, tpDisableIntraBssFwd pdis_intra_fwd) { struct wlan_objmgr_vdev *vdev; wma_debug("intra_fwd:vdev(%d) intrabss_dis=%s", pdis_intra_fwd->sessionId, (pdis_intra_fwd->disableintrabssfwd ? "true" : "false")); vdev = wma_handle->interfaces[pdis_intra_fwd->sessionId].vdev; cdp_cfg_vdev_rx_set_intrabss_fwd(cds_get_context(QDF_MODULE_ID_SOC), pdis_intra_fwd->sessionId, pdis_intra_fwd->disableintrabssfwd); } /** * wma_get_pdev_from_scn_handle() - API to get pdev from scn handle * @scn_handle: opaque wma handle * * API to get pdev from scn handle * * Return: None */ static struct wlan_objmgr_pdev *wma_get_pdev_from_scn_handle(void *scn_handle) { tp_wma_handle wma_handle; if (!scn_handle) { wma_err("invalid scn handle"); return NULL; } wma_handle = (tp_wma_handle)scn_handle; return wma_handle->pdev; } void wma_store_pdev(void *wma_ctx, struct wlan_objmgr_pdev *pdev) { tp_wma_handle wma = (tp_wma_handle)wma_ctx; QDF_STATUS status; status = wlan_objmgr_pdev_try_get_ref(pdev, WLAN_LEGACY_WMA_ID); if (QDF_STATUS_SUCCESS != status) { wma->pdev = NULL; return; } wma->pdev = pdev; target_if_store_pdev_target_if_ctx(wma_get_pdev_from_scn_handle); target_pdev_set_wmi_handle(wma->pdev->tgt_if_handle, wma->wmi_handle); } /** * 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 (!wma) goto end; /* Change the beacon interval back to its original value */ wma_debug("Change beacon interval back to %d", 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) return -ENOMEM; wma_debug("vdev_id %d ", 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_debug("set latency level %d, fw wlm_latency_flags 0x%x", 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_warn("Failed to set latency level"); return ret; } QDF_STATUS wma_add_bss_peer_sta(uint8_t vdev_id, uint8_t *bssid, bool is_resp_required, uint8_t *mld_mac, bool is_assoc_peer) { tp_wma_handle wma; QDF_STATUS status = QDF_STATUS_E_FAILURE; wma = cds_get_context(QDF_MODULE_ID_WMA); if (!wma) goto err; if (is_resp_required) status = wma_create_sta_mode_bss_peer(wma, bssid, WMI_PEER_TYPE_DEFAULT, vdev_id, mld_mac, is_assoc_peer); else status = wma_create_peer(wma, bssid, WMI_PEER_TYPE_DEFAULT, vdev_id, mld_mac, is_assoc_peer); err: return status; } QDF_STATUS wma_send_vdev_stop(uint8_t vdev_id) { tp_wma_handle wma; void *soc = cds_get_context(QDF_MODULE_ID_SOC); QDF_STATUS status; wma = cds_get_context(QDF_MODULE_ID_WMA); if (!wma) return QDF_STATUS_E_FAILURE; wma_debug("vdev_id: %d, pausing tx_ll_queue for VDEV_STOP", vdev_id); cdp_fc_vdev_pause(soc, vdev_id, OL_TXQ_PAUSE_REASON_VDEV_STOP, 0); status = mlme_set_vdev_stop_type( wma->interfaces[vdev_id].vdev, WMA_SET_LINK_STATE); if (QDF_IS_STATUS_ERROR(status)) { wma_alert("Failed to set wma req msg_type for vdev_id %d", vdev_id); return QDF_STATUS_E_FAILURE; } wma_vdev_set_pause_bit(vdev_id, PAUSE_TYPE_HOST); status = wma_send_vdev_stop_to_fw(wma, vdev_id); if (QDF_IS_STATUS_ERROR(status)) { struct vdev_stop_response resp_event; wma_info("vdev %d Failed to send vdev stop", vdev_id); resp_event.vdev_id = vdev_id; mlme_set_connection_fail(wma->interfaces[vdev_id].vdev, false); wma_handle_vdev_stop_rsp(wma, &resp_event); } /* * Remove peer, Vdev down and sending set link * response will be handled in vdev stop response * handler */ return QDF_STATUS_SUCCESS; } #define TX_MGMT_RATE_2G_ENABLE_OFFSET 30 #define TX_MGMT_RATE_5G_ENABLE_OFFSET 31 #define TX_MGMT_RATE_2G_OFFSET 0 #define TX_MGMT_RATE_5G_OFFSET 12 /** * wma_verify_rate_code() - verify if rate code is valid. * @rate_code: rate code * @band: band information * * Return: verify result */ static bool wma_verify_rate_code(uint32_t rate_code, enum cds_band_type band) { uint8_t preamble, nss, rate; bool valid = true; preamble = (rate_code & 0xc0) >> 6; nss = (rate_code & 0x30) >> 4; rate = rate_code & 0xf; switch (preamble) { case WMI_RATE_PREAMBLE_CCK: if (nss != 0 || rate > 3 || band == CDS_BAND_5GHZ) valid = false; break; case WMI_RATE_PREAMBLE_OFDM: if (nss != 0 || rate > 7) valid = false; break; case WMI_RATE_PREAMBLE_HT: if (nss != 0 || rate > 7) valid = false; break; case WMI_RATE_PREAMBLE_VHT: if (nss != 0 || rate > 9) valid = false; break; default: break; } return valid; } /** * wma_vdev_mgmt_tx_rate() - set vdev mgmt rate. * @info: pointer to vdev set param. * * Return: return status */ static QDF_STATUS wma_vdev_mgmt_tx_rate(struct dev_set_param *info) { uint32_t cfg_val; enum cds_band_type band = 0; QDF_STATUS status; struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); if (!mac) { wma_err("Failed to get mac"); return QDF_STATUS_E_FAILURE; } cfg_val = mac->mlme_cfg->sap_cfg.rate_tx_mgmt; band = CDS_BAND_ALL; if (cfg_val == MLME_CFG_TX_MGMT_RATE_DEF || !wma_verify_rate_code(cfg_val, band)) { wma_nofl_debug("default WNI_CFG_RATE_FOR_TX_MGMT, ignore"); status = QDF_STATUS_E_FAILURE; } else { info->param_id = wmi_vdev_param_mgmt_tx_rate; info->param_value = cfg_val; status = QDF_STATUS_SUCCESS; } return status; } /** * wma_vdev_mgmt_perband_tx_rate() - set vdev mgmt perband tx rate. * @info: pointer to vdev set param * * Return: returns status */ static QDF_STATUS wma_vdev_mgmt_perband_tx_rate(struct dev_set_param *info) { uint32_t cfg_val; uint32_t per_band_mgmt_tx_rate = 0; enum cds_band_type band = 0; struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); if (!mac) { wma_err("failed to get mac"); return QDF_STATUS_E_FAILURE; } cfg_val = mac->mlme_cfg->sap_cfg.rate_tx_mgmt_2g; band = CDS_BAND_2GHZ; if (cfg_val == MLME_CFG_TX_MGMT_2G_RATE_DEF || !wma_verify_rate_code(cfg_val, band)) { wma_nofl_debug("use default 2G MGMT rate."); per_band_mgmt_tx_rate &= ~(1 << TX_MGMT_RATE_2G_ENABLE_OFFSET); } else { per_band_mgmt_tx_rate |= (1 << TX_MGMT_RATE_2G_ENABLE_OFFSET); per_band_mgmt_tx_rate |= ((cfg_val & 0x7FF) << TX_MGMT_RATE_2G_OFFSET); } cfg_val = mac->mlme_cfg->sap_cfg.rate_tx_mgmt; band = CDS_BAND_5GHZ; if (cfg_val == MLME_CFG_TX_MGMT_5G_RATE_DEF || !wma_verify_rate_code(cfg_val, band)) { wma_nofl_debug("use default 5G MGMT rate."); per_band_mgmt_tx_rate &= ~(1 << TX_MGMT_RATE_5G_ENABLE_OFFSET); } else { per_band_mgmt_tx_rate |= (1 << TX_MGMT_RATE_5G_ENABLE_OFFSET); per_band_mgmt_tx_rate |= ((cfg_val & 0x7FF) << TX_MGMT_RATE_5G_OFFSET); } info->param_id = wmi_vdev_param_per_band_mgmt_tx_rate; info->param_value = per_band_mgmt_tx_rate; return QDF_STATUS_SUCCESS; } #define MAX_VDEV_CREATE_PARAMS 22 /* params being sent: * 1.wmi_vdev_param_wmm_txop_enable * 2.wmi_vdev_param_disconnect_th * 3.wmi_vdev_param_mcc_rtscts_protection_enable * 4.wmi_vdev_param_mcc_broadcast_probe_enable * 5.wmi_vdev_param_rts_threshold * 6.wmi_vdev_param_fragmentation_threshold * 7.wmi_vdev_param_tx_stbc * 8.wmi_vdev_param_mgmt_tx_rate * 9.wmi_vdev_param_per_band_mgmt_tx_rate * 10.wmi_vdev_param_set_eht_mu_mode * 11.wmi_vdev_param_set_hemu_mode * 12.wmi_vdev_param_txbf * 13.wmi_vdev_param_enable_bcast_probe_response * 14.wmi_vdev_param_fils_max_channel_guard_time * 15.wmi_vdev_param_probe_delay * 16.wmi_vdev_param_repeat_probe_time * 17.wmi_vdev_param_enable_disable_oce_features * 18.wmi_vdev_param_bmiss_first_bcnt * 19.wmi_vdev_param_bmiss_final_bcnt * 20.wmi_vdev_param_set_sap_ps_with_twt * 21.wmi_vdev_param_disable_2g_twt * 22.wmi_vdev_param_disable_twt_info_frame */ QDF_STATUS wma_vdev_create_set_param(struct wlan_objmgr_vdev *vdev) { QDF_STATUS status; struct mlme_ht_capabilities_info *ht_cap_info; uint32_t cfg_val; struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); struct dev_set_param ext_val; wmi_vdev_txbf_en txbf_en = {0}; struct vdev_mlme_obj *vdev_mlme; uint8_t vdev_id; uint32_t hemu_mode; struct dev_set_param setparam[MAX_VDEV_CREATE_PARAMS]; uint8_t index = 0; bool is_24ghz_twt_enabled; bool disable_twt_info_frame; enum QDF_OPMODE opmode; if (!mac) return QDF_STATUS_E_FAILURE; vdev_id = wlan_vdev_get_id(vdev); vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev); if (!vdev_mlme) { wma_err("Failed to get vdev mlme obj!"); return QDF_STATUS_E_FAILURE; } status = mlme_check_index_setparam( setparam, wmi_vdev_param_wmm_txop_enable, mac->mlme_cfg->edca_params.enable_wmm_txop, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_wmm_txop_enable"); goto error; } wma_debug("Setting wmi_vdev_param_disconnect_th: %d", mac->mlme_cfg->gen.dropped_pkt_disconnect_thresh); status = mlme_check_index_setparam( setparam, wmi_vdev_param_disconnect_th, mac->mlme_cfg->gen.dropped_pkt_disconnect_thresh, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_disconnect_th"); goto error; } status = mlme_check_index_setparam( setparam, wmi_vdev_param_mcc_rtscts_protection_enable, mac->roam.configParam.mcc_rts_cts_prot_enable, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_mcc_rtscts_protection_enable"); goto error; } status = mlme_check_index_setparam( setparam, wmi_vdev_param_mcc_broadcast_probe_enable, mac->roam.configParam.mcc_bcast_prob_resp_enable, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_mcc_broadcast_probe_enable"); goto error; } if (wlan_mlme_get_rts_threshold(mac->psoc, &cfg_val) == QDF_STATUS_SUCCESS) { status = mlme_check_index_setparam( setparam, wmi_vdev_param_rts_threshold, cfg_val, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_rts_threshold"); goto error; } } else { wma_err("Fail to get val for rts threshold, leave unchanged"); } if (wlan_mlme_get_frag_threshold(mac->psoc, &cfg_val) == QDF_STATUS_SUCCESS) { status = mlme_check_index_setparam( setparam, wmi_vdev_param_fragmentation_threshold, cfg_val, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_fragmentation_threshold"); goto error; } } else { wma_err("Fail to get val for frag threshold, leave unchanged"); } ht_cap_info = &mac->mlme_cfg->ht_caps.ht_cap_info; status = mlme_check_index_setparam(setparam, wmi_vdev_param_tx_stbc, ht_cap_info->tx_stbc, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_tx_stbc"); goto error; } if (!wma_vdev_mgmt_tx_rate(&ext_val)) { status = mlme_check_index_setparam(setparam, ext_val.param_id, ext_val.param_value, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set param for MGMT RATE"); goto error; } } if (!wma_vdev_mgmt_perband_tx_rate(&ext_val)) { status = mlme_check_index_setparam(setparam, ext_val.param_id, ext_val.param_value, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set PERBAND_MGMT RATE"); goto error; } } if (IS_FEATURE_11BE_SUPPORTED_BY_FW) { uint32_t mode; status = wma_set_eht_txbf_vdev_params(mac, &mode); if (status == QDF_STATUS_SUCCESS) { wma_debug("set EHTMU_MODE (ehtmu_mode = 0x%x)", mode); status = mlme_check_index_setparam( setparam, wmi_vdev_param_set_eht_mu_mode, mode, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_set_eht_mu_mode"); goto error; } } } if (IS_FEATURE_SUPPORTED_BY_FW(DOT11AX)) { if (!wma_get_hemu_mode(&hemu_mode, mac)) { wma_debug("set HEMU_MODE (hemu_mode = 0x%x)", hemu_mode); status = mlme_check_index_setparam( setparam, wmi_vdev_param_set_hemu_mode, hemu_mode, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_set_hemu_mode"); goto error; } } } if (wlan_nan_is_beamforming_supported(mac->psoc)) { txbf_en.sutxbfee = mac->mlme_cfg->vht_caps.vht_cap_info.su_bformee; txbf_en.mutxbfee = mac->mlme_cfg->vht_caps.vht_cap_info.enable_mu_bformee; txbf_en.sutxbfer = mac->mlme_cfg->vht_caps.vht_cap_info.su_bformer; status = mlme_check_index_setparam(setparam, wmi_vdev_param_txbf, *((A_UINT8 *)&txbf_en), index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_txbf"); goto error; } } /* Initialize roaming offload state */ if (vdev_mlme->mgmt.generic.type == WMI_VDEV_TYPE_STA && vdev_mlme->mgmt.generic.subtype == 0) { /* Pass down enable/disable bcast probe rsp to FW */ status = mlme_check_index_setparam( setparam, wmi_vdev_param_enable_bcast_probe_response, mac->mlme_cfg->oce.enable_bcast_probe_rsp, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_enable_bcast_probe_response"); goto error; } /* Pass down the FILS max channel guard time to FW */ status = mlme_check_index_setparam( setparam, wmi_vdev_param_fils_max_channel_guard_time, mac->mlme_cfg->sta.fils_max_chan_guard_time, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_fils_max_channel_guard_time"); goto error; } /* Pass down the Probe Request tx delay(in ms) to FW */ status = mlme_check_index_setparam(setparam, wmi_vdev_param_probe_delay, PROBE_REQ_TX_DELAY, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_probe_delay"); goto error; } /* Pass down the probe request tx time gap_ms to FW */ status = mlme_check_index_setparam( setparam, wmi_vdev_param_repeat_probe_time, PROBE_REQ_TX_TIME_GAP, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_repeat_probe_time"); goto error; } status = mlme_check_index_setparam( setparam, wmi_vdev_param_enable_disable_oce_features, mac->mlme_cfg->oce.feature_bitmap, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_enable_disable_oce_features"); goto error; } /* Initialize BMISS parameters */ wma_debug("first_bcnt: %d, final_bcnt: %d", mac->mlme_cfg->lfr.roam_bmiss_first_bcnt, mac->mlme_cfg->lfr.roam_bmiss_final_bcnt); status = mlme_check_index_setparam( setparam, wmi_vdev_param_bmiss_first_bcnt, mac->mlme_cfg->lfr.roam_bmiss_first_bcnt, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_bmiss_first_bcnt"); goto error; } status = mlme_check_index_setparam(setparam, wmi_vdev_param_bmiss_final_bcnt, mac->mlme_cfg->lfr.roam_bmiss_final_bcnt, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_bmiss_final_bcnt"); goto error; } } if (vdev_mlme->mgmt.generic.type == WMI_VDEV_TYPE_AP && vdev_mlme->mgmt.generic.subtype == 0) { status = mlme_check_index_setparam(setparam, wmi_vdev_param_enable_disable_oce_features, mac->mlme_cfg->oce.feature_bitmap, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_enable_disable_oce_features"); goto error; } } opmode = wlan_vdev_mlme_get_opmode(vdev); if (opmode == QDF_SAP_MODE) { status = mlme_check_index_setparam( setparam, wmi_vdev_param_set_sap_ps_with_twt, wlan_mlme_get_sap_ps_with_twt(mac->psoc), index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_set_sap_ps_with_twt"); goto error; } } is_24ghz_twt_enabled = mlme_is_24ghz_twt_enabled(mac->psoc); status = mlme_check_index_setparam(setparam, wmi_vdev_param_disable_2g_twt, !is_24ghz_twt_enabled, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_disable_2g_twt"); goto error; } disable_twt_info_frame = mlme_is_twt_disable_info_frame(mac->psoc); status = mlme_check_index_setparam( setparam, wmi_vdev_param_disable_twt_info_frame, disable_twt_info_frame, index++, MAX_VDEV_CREATE_PARAMS); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("failed to set wmi_vdev_param_disable_twt_info_frame"); goto error; } status = wma_send_multi_pdev_vdev_set_params(MLME_VDEV_SETPARAM, vdev_id, setparam, index); if (QDF_IS_STATUS_ERROR(status)) { wma_err("failed to update vdev set all params"); status = QDF_STATUS_E_FAILURE; goto error; } error: return status; } static inline bool wma_tx_is_chainmask_valid(int value, struct target_psoc_info *tgt_hdl) { struct wlan_psoc_host_mac_phy_caps *mac_phy_cap; uint8_t total_mac_phy_cnt, i; mac_phy_cap = target_psoc_get_mac_phy_cap(tgt_hdl); if (!mac_phy_cap) { wma_err("Invalid MAC PHY capabilities handle"); return false; } total_mac_phy_cnt = target_psoc_get_total_mac_phy_cnt(tgt_hdl); for (i = 0; i < total_mac_phy_cnt; i++) { if (((mac_phy_cap[i].tx_chain_mask_5G) & (value))) return true; } return false; } QDF_STATUS wma_validate_txrx_chain_mask(uint32_t id, uint32_t value) { tp_wma_handle wma_handle = cds_get_context(QDF_MODULE_ID_WMA); struct target_psoc_info *tgt_hdl; if (!wma_handle) return QDF_STATUS_E_FAILURE; tgt_hdl = wlan_psoc_get_tgt_if_handle(wma_handle->psoc); if (!tgt_hdl) return QDF_STATUS_E_FAILURE; wma_debug("pdev pid %d pval %d", id, value); if (id == wmi_pdev_param_tx_chain_mask) { if (wma_check_txrx_chainmask(target_if_get_num_rf_chains( tgt_hdl), value) || !wma_tx_is_chainmask_valid(value, tgt_hdl)) { wma_err("failed in validating tx chainmask"); return QDF_STATUS_E_FAILURE; } } if (id == wmi_pdev_param_rx_chain_mask) { if (wma_check_txrx_chainmask(target_if_get_num_rf_chains( tgt_hdl), value)) { wma_err("failed in validating rtx chainmask"); return QDF_STATUS_SUCCESS; } } return QDF_STATUS_SUCCESS; } QDF_STATUS wma_send_multi_pdev_vdev_set_params(enum mlme_dev_setparam param_type, uint8_t dev_id, struct dev_set_param *param, uint8_t n_params) { struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); struct set_multiple_pdev_vdev_param params = {}; QDF_STATUS status; wmi_unified_t wmi_handle; if (!mac) return QDF_STATUS_E_FAILURE; wmi_handle = get_wmi_unified_hdl_from_psoc(mac->psoc); if (!wmi_handle) return QDF_STATUS_E_FAILURE; params.param_type = param_type; params.dev_id = dev_id; params.is_host_pdev_id = false; params.params = param; params.n_params = n_params; if (param_type == MLME_VDEV_SETPARAM) { status = wmi_unified_multiple_vdev_param_send(wmi_handle, ¶ms); if (QDF_IS_STATUS_ERROR(status)) wma_err("failed to send multi vdev set params"); } else if (param_type == MLME_PDEV_SETPARAM) { status = wmi_unified_multiple_pdev_param_send(wmi_handle, ¶ms); if (QDF_IS_STATUS_ERROR(status)) wma_err("failed to send multi pdev set params"); } else { status = QDF_STATUS_E_FAILURE; wma_err("Invalid param type"); } return status; }