/* * 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_features.c * This file contains different features related functions like WoW, * Offloads, TDLS etc. */ /* Header files */ #include "cds_ieee80211_common.h" /* ieee80211_frame */ #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 #include #include "qdf_nbuf.h" #include "qdf_types.h" #include "qdf_mem.h" #include "qdf_util.h" #include "wma_types.h" #include "lim_api.h" #include "lim_session_utils.h" #include "cfg_ucfg_api.h" #include "cds_utils.h" #include "cfg_qos.h" #if !defined(REMOVE_PKT_LOG) #include "pktlog_ac.h" #endif /* REMOVE_PKT_LOG */ #include "dbglog_host.h" #include "csr_api.h" #include "ol_fw.h" #include "wma_internal.h" #include "wma_nan_datapath.h" #include #include "wlan_pmo_ucfg_api.h" #include #include "wlan_reg_services_api.h" #include "wlan_roam_debug.h" #include #ifdef WLAN_FEATURE_NAN #include "target_if_nan.h" #endif #include "wlan_scan_api.h" #include "spatial_reuse_api.h" #include "wlan_cm_api.h" #include #include "cdp_txrx_host_stats.h" #include "target_if_cm_roam_event.h" #include #include "hif.h" #include "wlan_cmn_ieee80211.h" #include "wlan_mlo_mgr_cmn.h" #include "wlan_mlo_mgr_peer.h" #include "wlan_mlo_mgr_sta.h" #include "wlan_cp_stats_mc_defs.h" /** * WMA_SET_VDEV_IE_SOURCE_HOST - Flag to identify the source of VDEV SET IE * command. The value is 0x0 for the VDEV SET IE WMI commands from mobile * MCL platform. */ #define WMA_SET_VDEV_IE_SOURCE_HOST 0x0 /* * Max AMPDU Tx Aggr supported size */ #define ADDBA_TXAGGR_SIZE_HELIUM 64 #define ADDBA_TXAGGR_SIZE_LITHIUM 256 #define ADDBA_TXAGGR_SIZE_512 512 #define ADDBA_TXAGGR_SIZE_BERYLLIUM 1024 static bool is_wakeup_event_console_logs_enabled = false; void wma_set_wakeup_logs_to_console(bool value) { is_wakeup_event_console_logs_enabled = value; } #if defined(FEATURE_WLAN_DIAG_SUPPORT) /** * qdf_wma_wow_wakeup_stats_event()- send wow wakeup stats * @tp_wma_handle wma: WOW wakeup packet counter * * This function sends wow wakeup stats diag event * * Return: void. */ static inline void qdf_wma_wow_wakeup_stats_event(tp_wma_handle wma) { QDF_STATUS status; struct wake_lock_stats stats = {0}; WLAN_HOST_DIAG_EVENT_DEF(wow_stats, struct host_event_wlan_powersave_wow_stats); status = ucfg_mc_cp_stats_get_psoc_wake_lock_stats(wma->psoc, &stats); if (QDF_IS_STATUS_ERROR(status)) return; qdf_mem_zero(&wow_stats, sizeof(wow_stats)); wow_stats.wow_bcast_wake_up_count = stats.bcast_wake_up_count; wow_stats.wow_ipv4_mcast_wake_up_count = stats.ipv4_mcast_wake_up_count; wow_stats.wow_ipv6_mcast_wake_up_count = stats.ipv6_mcast_wake_up_count; wow_stats.wow_ipv6_mcast_ra_stats = stats.ipv6_mcast_ra_stats; wow_stats.wow_ipv6_mcast_ns_stats = stats.ipv6_mcast_ns_stats; wow_stats.wow_ipv6_mcast_na_stats = stats.ipv6_mcast_na_stats; wow_stats.wow_pno_match_wake_up_count = stats.pno_match_wake_up_count; wow_stats.wow_pno_complete_wake_up_count = stats.pno_complete_wake_up_count; wow_stats.wow_gscan_wake_up_count = stats.gscan_wake_up_count; wow_stats.wow_low_rssi_wake_up_count = stats.low_rssi_wake_up_count; wow_stats.wow_rssi_breach_wake_up_count = stats.rssi_breach_wake_up_count; wow_stats.wow_icmpv4_count = stats.icmpv4_count; wow_stats.wow_icmpv6_count = stats.icmpv6_count; wow_stats.wow_oem_response_wake_up_count = stats.oem_response_wake_up_count; WLAN_HOST_DIAG_EVENT_REPORT(&wow_stats, EVENT_WLAN_POWERSAVE_WOW_STATS); } #else static inline void qdf_wma_wow_wakeup_stats_event(tp_wma_handle wma) { return; } #endif #ifdef FEATURE_WLAN_AUTO_SHUTDOWN /** * wma_wake_reason_auto_shutdown() - to post auto shutdown event to sme * * Return: 0 for success or error code */ static int wma_wake_reason_auto_shutdown(void) { QDF_STATUS qdf_status; struct scheduler_msg sme_msg = { 0 }; sme_msg.type = eWNI_SME_AUTO_SHUTDOWN_IND; qdf_status = scheduler_post_message(QDF_MODULE_ID_WMA, QDF_MODULE_ID_SME, QDF_MODULE_ID_SME, &sme_msg); if (!QDF_IS_STATUS_SUCCESS(qdf_status)) wma_err("Fail to post eWNI_SME_AUTO_SHUTDOWN_IND msg to SME"); return qdf_status_to_os_return(qdf_status); } #else static inline int wma_wake_reason_auto_shutdown(void) { return 0; } #endif /* FEATURE_WLAN_AUTO_SHUTDOWN */ #ifdef FEATURE_WLAN_SCAN_PNO static int wma_wake_reason_nlod(t_wma_handle *wma, uint8_t vdev_id) { wmi_nlo_event nlo_event = { .vdev_id = vdev_id }; WMI_NLO_MATCH_EVENTID_param_tlvs param = { .fixed_param = &nlo_event }; return target_if_nlo_match_event_handler(wma, (uint8_t *)¶m, sizeof(param)); } #else static inline int wma_wake_reason_nlod(t_wma_handle *wma, uint8_t vdev_id) { return 0; } #endif /* FEATURE_WLAN_SCAN_PNO */ #ifdef WLAN_FEATURE_NAN /** * wma_nan_rsp_handler_callback() - call NAN Discovery event handler * @handle: wma handle * @event: event buffer * @len: buffer length * * Return: 0 for success or error code */ static int wma_nan_rsp_handler_callback(void *handle, uint8_t *event, uint32_t len) { return target_if_nan_rsp_handler(handle, event, len); } #else static inline int wma_nan_rsp_handler_callback(void *handle, uint8_t *event, uint32_t len) { return 0; } #endif /** * wma_get_snr() - get RSSI from fw * @psnr_req: request params * * Return: QDF status */ QDF_STATUS wma_get_snr(tAniGetSnrReq *psnr_req) { tAniGetSnrReq *psnr_req_bkp; tp_wma_handle wma_handle = NULL; struct wma_txrx_node *intr; wma_handle = cds_get_context(QDF_MODULE_ID_WMA); if (!wma_handle) return QDF_STATUS_E_FAULT; intr = &wma_handle->interfaces[psnr_req->sessionId]; /* command is in progress */ if (intr->psnr_req) { wma_err("previous snr request is pending"); return QDF_STATUS_SUCCESS; } psnr_req_bkp = qdf_mem_malloc(sizeof(tAniGetSnrReq)); if (!psnr_req_bkp) return QDF_STATUS_E_NOMEM; qdf_mem_zero(psnr_req_bkp, sizeof(tAniGetSnrReq)); psnr_req_bkp->pDevContext = psnr_req->pDevContext; psnr_req_bkp->snrCallback = psnr_req->snrCallback; intr->psnr_req = (void *)psnr_req_bkp; if (wmi_unified_snr_cmd(wma_handle->wmi_handle, psnr_req->sessionId)) { wma_err("Failed to send host stats request to fw"); qdf_mem_free(psnr_req_bkp); intr->psnr_req = NULL; return QDF_STATUS_E_FAILURE; } return QDF_STATUS_SUCCESS; } void wma_get_rx_retry_cnt(struct mac_context *mac, uint8_t vdev_id, uint8_t *mac_addr) { struct cdp_peer_stats *peer_stats; QDF_STATUS status; peer_stats = qdf_mem_malloc(sizeof(*peer_stats)); if (!peer_stats) return; status = cdp_host_get_peer_stats(cds_get_context(QDF_MODULE_ID_SOC), vdev_id, mac_addr, peer_stats); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to get peer stats"); goto exit; } mac->rx_retry_cnt = peer_stats->rx.rx_retries; wma_debug("Rx retry count %d, Peer" QDF_MAC_ADDR_FMT, mac->rx_retry_cnt, QDF_MAC_ADDR_REF(mac_addr)); exit: qdf_mem_free(peer_stats); } /** * wma_process_link_status_req() - process link status request from UMAC * @wma: wma handle * @pGetLinkStatus: get link params * * Return: none */ void wma_process_link_status_req(tp_wma_handle wma, tAniGetLinkStatus *pGetLinkStatus) { struct link_status_params cmd = {0}; struct wma_txrx_node *iface = &wma->interfaces[pGetLinkStatus->sessionId]; if (iface->plink_status_req) { wma_err("previous link status request is pending,deleting the new request"); qdf_mem_free(pGetLinkStatus); return; } iface->plink_status_req = pGetLinkStatus; cmd.vdev_id = pGetLinkStatus->sessionId; if (wmi_unified_link_status_req_cmd(wma->wmi_handle, &cmd)) { wma_err("Failed to send WMI link status request to fw"); iface->plink_status_req = NULL; goto end; } return; end: wma_post_link_status(pGetLinkStatus, LINK_STATUS_LEGACY); } #ifdef WLAN_FEATURE_TSF #if defined(WLAN_FEATURE_TSF_AUTO_REPORT) || defined(QCA_GET_TSF_VIA_REG) static inline void wma_vdev_tsf_set_mac_id_tsf_id(struct stsf *ptsf, uint32_t mac_id, uint32_t mac_id_valid, uint32_t tsf_id, uint32_t tsf_id_valid) { ptsf->mac_id = mac_id; ptsf->mac_id_valid = mac_id_valid; ptsf->tsf_id = tsf_id; ptsf->tsf_id_valid = tsf_id_valid; wma_nofl_debug("mac_id %d mac_id_valid %d tsf_id %d tsf_id_valid %d", ptsf->mac_id, ptsf->mac_id_valid, ptsf->tsf_id, ptsf->tsf_id_valid); } #else /* !(WLAN_FEATURE_TSF_AUTO_REPORT || QCA_GET_TSF_VIA_REG) */ static inline void wma_vdev_tsf_set_mac_id_tsf_id(struct stsf *ptsf, uint32_t mac_id, uint32_t mac_id_valid, uint32_t tsf_id, uint32_t tsf_id_valid) { } #endif /* WLAN_FEATURE_TSF_AUTO_REPORT || QCA_GET_TSF_VIA_REG */ /** * wma_vdev_tsf_handler() - handle tsf event indicated by FW * @handle: wma context * @data: event buffer * @data len: length of event buffer * * Return: 0 on success */ int wma_vdev_tsf_handler(void *handle, uint8_t *data, uint32_t data_len) { struct scheduler_msg tsf_msg = {0}; WMI_VDEV_TSF_REPORT_EVENTID_param_tlvs *param_buf; wmi_vdev_tsf_report_event_fixed_param *tsf_event; struct stsf *ptsf; if (!data) { wma_err("invalid pointer"); return -EINVAL; } ptsf = qdf_mem_malloc(sizeof(*ptsf)); if (!ptsf) return -ENOMEM; param_buf = (WMI_VDEV_TSF_REPORT_EVENTID_param_tlvs *)data; tsf_event = param_buf->fixed_param; ptsf->vdev_id = tsf_event->vdev_id; ptsf->tsf_low = tsf_event->tsf_low; ptsf->tsf_high = tsf_event->tsf_high; ptsf->soc_timer_low = tsf_event->qtimer_low; ptsf->soc_timer_high = tsf_event->qtimer_high; ptsf->global_tsf_low = tsf_event->wlan_global_tsf_low; ptsf->global_tsf_high = tsf_event->wlan_global_tsf_high; wma_nofl_debug("receive WMI_VDEV_TSF_REPORT_EVENTID on %d, tsf: %d %d", ptsf->vdev_id, ptsf->tsf_low, ptsf->tsf_high); wma_nofl_debug("g_tsf: %d %d; soc_timer: %d %d", ptsf->global_tsf_low, ptsf->global_tsf_high, ptsf->soc_timer_low, ptsf->soc_timer_high); wma_vdev_tsf_set_mac_id_tsf_id(ptsf, tsf_event->mac_id, tsf_event->mac_id_valid, tsf_event->tsf_id, tsf_event->tsf_id_valid); tsf_msg.type = eWNI_SME_TSF_EVENT; tsf_msg.bodyptr = ptsf; tsf_msg.bodyval = 0; if (QDF_STATUS_SUCCESS != scheduler_post_message(QDF_MODULE_ID_WMA, QDF_MODULE_ID_SME, QDF_MODULE_ID_SME, &tsf_msg)) { qdf_mem_free(ptsf); return -EINVAL; } return 0; } #if defined(QCA_WIFI_3_0) || defined(WLAN_FEATURE_TSF_TIMER_SYNC) #define TSF_FW_ACTION_CMD TSF_TSTAMP_QTIMER_CAPTURE_REQ #else #define TSF_FW_ACTION_CMD TSF_TSTAMP_CAPTURE_REQ #endif /** * wma_capture_tsf() - send wmi to fw to capture tsf * @wma_handle: wma handler * @vdev_id: vdev id * * Return: wmi send state */ QDF_STATUS wma_capture_tsf(tp_wma_handle wma_handle, uint32_t vdev_id) { QDF_STATUS status; wmi_buf_t buf; wmi_vdev_tsf_tstamp_action_cmd_fixed_param *cmd; int len = sizeof(*cmd); buf = wmi_buf_alloc(wma_handle->wmi_handle, len); if (!buf) return QDF_STATUS_E_NOMEM; cmd = (wmi_vdev_tsf_tstamp_action_cmd_fixed_param *) wmi_buf_data(buf); cmd->vdev_id = vdev_id; cmd->tsf_action = TSF_FW_ACTION_CMD; wma_debug("vdev_id %u, tsf_cmd: %d", cmd->vdev_id, cmd->tsf_action); WMITLV_SET_HDR(&cmd->tlv_header, WMITLV_TAG_STRUC_wmi_vdev_tsf_tstamp_action_cmd_fixed_param, WMITLV_GET_STRUCT_TLVLEN( wmi_vdev_tsf_tstamp_action_cmd_fixed_param)); status = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len, WMI_VDEV_TSF_TSTAMP_ACTION_CMDID); if (QDF_IS_STATUS_ERROR(status)) wmi_buf_free(buf); return status; } /** * wma_reset_tsf_gpio() - send wmi to fw to reset GPIO * @wma_handle: wma handler * @vdev_id: vdev id * * Return: wmi send state */ QDF_STATUS wma_reset_tsf_gpio(tp_wma_handle wma_handle, uint32_t vdev_id) { QDF_STATUS status; wmi_buf_t buf; wmi_vdev_tsf_tstamp_action_cmd_fixed_param *cmd; int len = sizeof(*cmd); uint8_t *buf_ptr; buf = wmi_buf_alloc(wma_handle->wmi_handle, len); if (!buf) return QDF_STATUS_E_NOMEM; buf_ptr = (uint8_t *) wmi_buf_data(buf); cmd = (wmi_vdev_tsf_tstamp_action_cmd_fixed_param *) buf_ptr; cmd->vdev_id = vdev_id; cmd->tsf_action = TSF_TSTAMP_CAPTURE_RESET; wma_debug("vdev_id %u, TSF_TSTAMP_CAPTURE_RESET", cmd->vdev_id); WMITLV_SET_HDR(&cmd->tlv_header, WMITLV_TAG_STRUC_wmi_vdev_tsf_tstamp_action_cmd_fixed_param, WMITLV_GET_STRUCT_TLVLEN( wmi_vdev_tsf_tstamp_action_cmd_fixed_param)); status = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len, WMI_VDEV_TSF_TSTAMP_ACTION_CMDID); if (QDF_IS_STATUS_ERROR(status)) wmi_buf_free(buf); return status; } /** * wma_set_tsf_gpio_pin() - send wmi cmd to configure gpio pin * @handle: wma handler * @pin: GPIO pin id * * Return: QDF_STATUS */ QDF_STATUS wma_set_tsf_gpio_pin(WMA_HANDLE handle, uint32_t pin) { tp_wma_handle wma = (tp_wma_handle)handle; struct pdev_params pdev_param = {0}; int32_t ret; struct wmi_unified *wmi_handle; if (wma_validate_handle(wma)) return QDF_STATUS_E_INVAL; wmi_handle = wma->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; wma_debug("set tsf gpio pin: %d", pin); pdev_param.param_id = wmi_pdev_param_wnts_config; pdev_param.param_value = pin; ret = wmi_unified_pdev_param_send(wmi_handle, &pdev_param, WMA_WILDCARD_PDEV_ID); if (ret) { wma_err("Failed to set tsf gpio pin (status=%d)", ret); return QDF_STATUS_E_FAILURE; } return QDF_STATUS_SUCCESS; } #ifdef WLAN_FEATURE_TSF_AUTO_REPORT QDF_STATUS wma_set_tsf_auto_report(WMA_HANDLE handle, uint32_t vdev_id, uint32_t param_id, bool ena) { wmi_vdev_tsf_tstamp_action_cmd_fixed_param *cmd; tp_wma_handle wma = (tp_wma_handle)handle; struct wmi_unified *wmi_handle; int len = sizeof(*cmd); QDF_STATUS status; uint8_t *buf_ptr; wmi_buf_t buf; if (param_id != GEN_PARAM_TSF_AUTO_REPORT_ENABLE && param_id != GEN_PARAM_TSF_AUTO_REPORT_DISABLE) return QDF_STATUS_E_FAILURE; wmi_handle = wma->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; buf = wmi_buf_alloc(wmi_handle, len); if (!buf) return QDF_STATUS_E_NOMEM; buf_ptr = (uint8_t *)wmi_buf_data(buf); cmd = (wmi_vdev_tsf_tstamp_action_cmd_fixed_param *)buf_ptr; cmd->vdev_id = vdev_id; cmd->tsf_action = ena ? TSF_TSTAMP_AUTO_REPORT_ENABLE : TSF_TSTAMP_AUTO_REPORT_DISABLE; wma_debug("vdev_id %u tsf_action %d", cmd->vdev_id, cmd->tsf_action); WMITLV_SET_HDR( &cmd->tlv_header, WMITLV_TAG_STRUC_wmi_vdev_tsf_tstamp_action_cmd_fixed_param, WMITLV_GET_STRUCT_TLVLEN( wmi_vdev_tsf_tstamp_action_cmd_fixed_param)); status = wmi_unified_cmd_send(wmi_handle, buf, len, WMI_VDEV_TSF_TSTAMP_ACTION_CMDID); if (QDF_IS_STATUS_ERROR(status)) wmi_buf_free(buf); return status; } #endif /* WLAN_FEATURE_TSF_AUTO_REPORT */ #endif /** * wma_set_wisa_params(): Set WISA features related params in FW * @wma_handle: WMA handle * @wisa: Pointer to WISA param struct * * Return: CDF status */ QDF_STATUS wma_set_wisa_params(tp_wma_handle wma_handle, struct sir_wisa_params *wisa) { QDF_STATUS status; wmi_buf_t buf; wmi_vdev_wisa_cmd_fixed_param *cmd; int len = sizeof(*cmd); buf = wmi_buf_alloc(wma_handle->wmi_handle, len); if (!buf) return QDF_STATUS_E_NOMEM; cmd = (wmi_vdev_wisa_cmd_fixed_param *) wmi_buf_data(buf); cmd->wisa_mode = wisa->mode; cmd->vdev_id = wisa->vdev_id; WMITLV_SET_HDR(&cmd->tlv_header, WMITLV_TAG_STRUC_wmi_vdev_wisa_cmd_fixed_param, WMITLV_GET_STRUCT_TLVLEN( wmi_vdev_wisa_cmd_fixed_param)); status = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len, WMI_VDEV_WISA_CMDID); if (QDF_IS_STATUS_ERROR(status)) wmi_buf_free(buf); return status; } /** * wma_process_dhcp_ind() - process dhcp indication from SME * @wma_handle: wma handle * @ta_dhcp_ind: DHCP indication * * Return: QDF Status */ QDF_STATUS wma_process_dhcp_ind(WMA_HANDLE handle, tAniDHCPInd *ta_dhcp_ind) { tp_wma_handle wma_handle = (tp_wma_handle) handle; uint8_t vdev_id; wmi_peer_set_param_cmd_fixed_param peer_set_param_fp = {0}; if (wma_validate_handle(wma_handle)) return QDF_STATUS_E_FAILURE; if (!ta_dhcp_ind) { wma_err("DHCP indication is NULL"); return QDF_STATUS_E_FAILURE; } if (wma_find_vdev_id_by_addr(wma_handle, ta_dhcp_ind->adapterMacAddr.bytes, &vdev_id)) { wma_err("Failed to find vdev id for DHCP indication"); return QDF_STATUS_E_FAILURE; } wma_debug("WMA --> WMI_PEER_SET_PARAM triggered by DHCP, msgType=%s, device_mode=%d, macAddr=" QDF_MAC_ADDR_FMT, ta_dhcp_ind->msgType == WMA_DHCP_START_IND ? "WMA_DHCP_START_IND" : "WMA_DHCP_STOP_IND", ta_dhcp_ind->device_mode, QDF_MAC_ADDR_REF(ta_dhcp_ind->peerMacAddr.bytes)); /* fill in values */ peer_set_param_fp.vdev_id = vdev_id; peer_set_param_fp.param_id = WMI_HOST_PEER_CRIT_PROTO_HINT_ENABLED; if (WMA_DHCP_START_IND == ta_dhcp_ind->msgType) peer_set_param_fp.param_value = 1; else peer_set_param_fp.param_value = 0; WMI_CHAR_ARRAY_TO_MAC_ADDR(ta_dhcp_ind->peerMacAddr.bytes, &peer_set_param_fp.peer_macaddr); return wmi_unified_process_dhcp_ind(wma_handle->wmi_handle, &peer_set_param_fp); } #if defined(WLAN_FEATURE_11BE) static enum wlan_phymode wma_eht_chan_phy_mode(uint32_t freq, uint8_t dot11_mode, uint16_t bw_val, enum phy_ch_width chan_width) { if((dot11_mode == MLME_DOT11_MODE_11BE) || (dot11_mode == MLME_DOT11_MODE_11BE_ONLY)) { if (wlan_reg_is_24ghz_ch_freq(freq)) { if (bw_val == 20) return WLAN_PHYMODE_11BEG_EHT20; else if (bw_val == 40) return WLAN_PHYMODE_11BEG_EHT40; } else { if (bw_val == 20) return WLAN_PHYMODE_11BEA_EHT20; else if (bw_val == 40) return WLAN_PHYMODE_11BEA_EHT40; else if (bw_val == 80) return WLAN_PHYMODE_11BEA_EHT80; else if (chan_width == CH_WIDTH_160MHZ) return WLAN_PHYMODE_11BEA_EHT160; else if (chan_width == CH_WIDTH_320MHZ) return WLAN_PHYMODE_11BEA_EHT320; } } return WLAN_PHYMODE_AUTO; } #else static enum wlan_phymode wma_eht_chan_phy_mode(uint32_t freq, uint8_t dot11_mode, uint16_t bw_val, enum phy_ch_width chan_width) { return WLAN_PHYMODE_AUTO; } #endif enum wlan_phymode wma_chan_phy_mode(uint32_t freq, enum phy_ch_width chan_width, uint8_t dot11_mode) { enum wlan_phymode phymode = WLAN_PHYMODE_AUTO; uint16_t bw_val = wlan_reg_get_bw_value(chan_width); t_wma_handle *wma = cds_get_context(QDF_MODULE_ID_WMA); if (!wma) return WLAN_PHYMODE_AUTO; if (chan_width >= CH_WIDTH_INVALID || !bw_val || (wlan_reg_is_24ghz_ch_freq(freq) && bw_val > 40)) { wma_err_rl("Invalid channel width %d freq %d", chan_width, freq); return WLAN_PHYMODE_AUTO; } if (wlan_reg_is_24ghz_ch_freq(freq)) { if (((CH_WIDTH_5MHZ == chan_width) || (CH_WIDTH_10MHZ == chan_width)) && ((MLME_DOT11_MODE_11B == dot11_mode) || (MLME_DOT11_MODE_11G == dot11_mode) || (MLME_DOT11_MODE_11N == dot11_mode) || (MLME_DOT11_MODE_ALL == dot11_mode) || (MLME_DOT11_MODE_11AC == dot11_mode) || (MLME_DOT11_MODE_11AX == dot11_mode))) phymode = WLAN_PHYMODE_11G; else { switch (dot11_mode) { case MLME_DOT11_MODE_11B: if ((bw_val == 20) || (bw_val == 40)) phymode = WLAN_PHYMODE_11B; break; case MLME_DOT11_MODE_11G: if ((bw_val == 20) || (bw_val == 40)) phymode = WLAN_PHYMODE_11G; break; case MLME_DOT11_MODE_11G_ONLY: if ((bw_val == 20) || (bw_val == 40)) phymode = WLAN_PHYMODE_11G_ONLY; break; case MLME_DOT11_MODE_11N: case MLME_DOT11_MODE_11N_ONLY: if (bw_val == 20) phymode = WLAN_PHYMODE_11NG_HT20; else if (bw_val == 40) phymode = WLAN_PHYMODE_11NG_HT40; break; case MLME_DOT11_MODE_ALL: case MLME_DOT11_MODE_11AC: case MLME_DOT11_MODE_11AC_ONLY: if (bw_val == 20) phymode = WLAN_PHYMODE_11AC_VHT20_2G; else if (bw_val == 40) phymode = WLAN_PHYMODE_11AC_VHT40_2G; break; case MLME_DOT11_MODE_11AX: case MLME_DOT11_MODE_11AX_ONLY: if (20 == bw_val) phymode = WLAN_PHYMODE_11AXG_HE20; else if (40 == bw_val) phymode = WLAN_PHYMODE_11AXG_HE40; break; default: phymode = wma_eht_chan_phy_mode( freq, dot11_mode, bw_val, chan_width); break; } } } else if (wlan_reg_is_dsrc_freq(freq)) phymode = WLAN_PHYMODE_11A; else { if (((CH_WIDTH_5MHZ == chan_width) || (CH_WIDTH_10MHZ == chan_width)) && ((MLME_DOT11_MODE_11A == dot11_mode) || (MLME_DOT11_MODE_11N == dot11_mode) || (MLME_DOT11_MODE_ALL == dot11_mode) || (MLME_DOT11_MODE_11AC == dot11_mode) || (MLME_DOT11_MODE_11AX == dot11_mode))) phymode = WLAN_PHYMODE_11A; else { switch (dot11_mode) { case MLME_DOT11_MODE_11A: if (0 < bw_val) phymode = WLAN_PHYMODE_11A; break; case MLME_DOT11_MODE_11N: case MLME_DOT11_MODE_11N_ONLY: if (bw_val == 20) phymode = WLAN_PHYMODE_11NA_HT20; else if (40 <= bw_val) phymode = WLAN_PHYMODE_11NA_HT40; break; case MLME_DOT11_MODE_ALL: case MLME_DOT11_MODE_11AC: case MLME_DOT11_MODE_11AC_ONLY: if (bw_val == 20) phymode = WLAN_PHYMODE_11AC_VHT20; else if (bw_val == 40) phymode = WLAN_PHYMODE_11AC_VHT40; else if (bw_val == 80) phymode = WLAN_PHYMODE_11AC_VHT80; else if (chan_width == CH_WIDTH_160MHZ) phymode = WLAN_PHYMODE_11AC_VHT160; else if (chan_width == CH_WIDTH_80P80MHZ) phymode = WLAN_PHYMODE_11AC_VHT80_80; break; case MLME_DOT11_MODE_11AX: case MLME_DOT11_MODE_11AX_ONLY: if (20 == bw_val) phymode = WLAN_PHYMODE_11AXA_HE20; else if (40 == bw_val) phymode = WLAN_PHYMODE_11AXA_HE40; else if (80 == bw_val) phymode = WLAN_PHYMODE_11AXA_HE80; else if (CH_WIDTH_160MHZ == chan_width) phymode = WLAN_PHYMODE_11AXA_HE160; else if (CH_WIDTH_80P80MHZ == chan_width) phymode = WLAN_PHYMODE_11AXA_HE80_80; break; default: phymode = wma_eht_chan_phy_mode( freq, dot11_mode, bw_val, chan_width); break; } } } wma_debug("phymode %d freq %d ch_width %d dot11_mode %d", phymode, freq, chan_width, dot11_mode); QDF_ASSERT(phymode != WLAN_PHYMODE_AUTO); return phymode; } /** * wma_get_link_speed() -send command to get linkspeed * @handle: wma handle * @pLinkSpeed: link speed info * * Return: QDF status */ QDF_STATUS wma_get_link_speed(WMA_HANDLE handle, struct link_speed_info *pLinkSpeed) { tp_wma_handle wma_handle = (tp_wma_handle) handle; wmi_mac_addr peer_macaddr; struct wmi_unified *wmi_handle; if (wma_validate_handle(wma_handle)) return QDF_STATUS_E_INVAL; wmi_handle = wma_handle->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; if (!wmi_service_enabled(wmi_handle, wmi_service_estimate_linkspeed)) { wma_err("Linkspeed feature bit not enabled Sending value 0 as link speed"); wma_send_link_speed(0); return QDF_STATUS_E_FAILURE; } /* Copy the peer macaddress to the wma buffer */ WMI_CHAR_ARRAY_TO_MAC_ADDR(pLinkSpeed->peer_macaddr.bytes, &peer_macaddr); wma_debug("pLinkSpeed->peerMacAddr: "QDF_MAC_ADDR_FMT", peer_macaddr.mac_addr31to0: 0x%x, peer_macaddr.mac_addr47to32: 0x%x", QDF_MAC_ADDR_REF(pLinkSpeed->peer_macaddr.bytes), peer_macaddr.mac_addr31to0, peer_macaddr.mac_addr47to32); if (wmi_unified_get_link_speed_cmd(wmi_handle, peer_macaddr)) { return QDF_STATUS_E_FAILURE; } return QDF_STATUS_SUCCESS; } QDF_STATUS wma_get_isolation(tp_wma_handle wma) { wmi_coex_get_antenna_isolation_cmd_fixed_param *cmd; wmi_buf_t wmi_buf; uint32_t len; uint8_t *buf_ptr; struct wmi_unified *wmi_handle; wma_debug("get isolation"); if (wma_validate_handle(wma)) return QDF_STATUS_E_INVAL; wmi_handle = wma->wmi_handle; if (wmi_validate_handle(wma->wmi_handle)) return QDF_STATUS_E_INVAL; len = sizeof(wmi_coex_get_antenna_isolation_cmd_fixed_param); wmi_buf = wmi_buf_alloc(wmi_handle, len); if (!wmi_buf) { wma_err("wmi_buf_alloc failed"); return QDF_STATUS_E_NOMEM; } buf_ptr = (uint8_t *)wmi_buf_data(wmi_buf); cmd = (wmi_coex_get_antenna_isolation_cmd_fixed_param *)buf_ptr; WMITLV_SET_HDR( &cmd->tlv_header, WMITLV_TAG_STRUC_wmi_coex_get_antenna_isolation_cmd_fixed_param, WMITLV_GET_STRUCT_TLVLEN( wmi_coex_get_antenna_isolation_cmd_fixed_param)); if (wmi_unified_cmd_send(wmi_handle, wmi_buf, len, WMI_COEX_GET_ANTENNA_ISOLATION_CMDID)) { wma_err("Failed to get isolation request from fw"); wmi_buf_free(wmi_buf); return QDF_STATUS_E_FAILURE; } return QDF_STATUS_SUCCESS; } /** * wma_add_beacon_filter() - Issue WMI command to set beacon filter * @wma: wma handler * @filter_params: beacon_filter_param to set * * Return: Return QDF_STATUS */ QDF_STATUS wma_add_beacon_filter(WMA_HANDLE handle, struct beacon_filter_param *filter_params) { int i; wmi_buf_t wmi_buf; u_int8_t *buf; A_UINT32 *ie_map; int ret; struct wma_txrx_node *iface; tp_wma_handle wma = (tp_wma_handle) handle; struct wmi_unified *wmi_handle; wmi_add_bcn_filter_cmd_fixed_param *cmd; int len = sizeof(wmi_add_bcn_filter_cmd_fixed_param); len += WMI_TLV_HDR_SIZE; len += BCN_FLT_MAX_ELEMS_IE_LIST * sizeof(A_UINT32); /* for ext ie map */ len += WMI_TLV_HDR_SIZE; len += BCN_FLT_MAX_ELEMS_IE_LIST * sizeof(A_UINT32); if (wma_validate_handle(wma)) return QDF_STATUS_E_INVAL; wmi_handle = wma->wmi_handle; if (wmi_validate_handle(wma->wmi_handle)) return QDF_STATUS_E_INVAL; iface = &wma->interfaces[filter_params->vdev_id]; qdf_mem_copy(&iface->beacon_filter, filter_params, sizeof(struct beacon_filter_param)); iface->beacon_filter_enabled = true; wmi_buf = wmi_buf_alloc(wmi_handle, len); if (!wmi_buf) return QDF_STATUS_E_NOMEM; buf = (u_int8_t *) wmi_buf_data(wmi_buf); cmd = (wmi_add_bcn_filter_cmd_fixed_param *)wmi_buf_data(wmi_buf); cmd->vdev_id = filter_params->vdev_id; WMITLV_SET_HDR(&cmd->tlv_header, WMITLV_TAG_STRUC_wmi_add_bcn_filter_cmd_fixed_param, WMITLV_GET_STRUCT_TLVLEN( wmi_add_bcn_filter_cmd_fixed_param)); buf += sizeof(wmi_add_bcn_filter_cmd_fixed_param); WMITLV_SET_HDR(buf, WMITLV_TAG_ARRAY_UINT32, (BCN_FLT_MAX_ELEMS_IE_LIST * sizeof(u_int32_t))); ie_map = (A_UINT32 *)(buf + WMI_TLV_HDR_SIZE); for (i = 0; i < BCN_FLT_MAX_ELEMS_IE_LIST; i++) ie_map[i] = filter_params->ie_map[i]; wma_debug("Beacon filter ie map Hex dump:"); QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_DEBUG, (uint8_t *)ie_map, BCN_FLT_MAX_ELEMS_IE_LIST * sizeof(u_int32_t)); buf += WMI_TLV_HDR_SIZE; buf += BCN_FLT_MAX_ELEMS_IE_LIST * sizeof(A_UINT32); WMITLV_SET_HDR(buf, WMITLV_TAG_ARRAY_UINT32, (BCN_FLT_MAX_ELEMS_IE_LIST * sizeof(u_int32_t))); ie_map = (A_UINT32 *)(buf + WMI_TLV_HDR_SIZE); for (i = 0; i < BCN_FLT_MAX_ELEMS_IE_LIST; i++) ie_map[i] = filter_params->ie_map[i + 8]; wma_debug("Beacon filter ext ie map Hex dump:"); QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_DEBUG, (uint8_t *)ie_map, BCN_FLT_MAX_ELEMS_IE_LIST * sizeof(u_int32_t)); ret = wmi_unified_cmd_send(wmi_handle, wmi_buf, len, WMI_ADD_BCN_FILTER_CMDID); if (ret) { wmi_buf_free(wmi_buf); return QDF_STATUS_E_FAILURE; } return QDF_STATUS_SUCCESS; } /** * wma_remove_beacon_filter() - Issue WMI command to remove beacon filter * @wma: wma handler * @filter_params: beacon_filter_params * * Return: Return QDF_STATUS */ QDF_STATUS wma_remove_beacon_filter(WMA_HANDLE handle, struct beacon_filter_param *filter_params) { wmi_buf_t buf; tp_wma_handle wma = (tp_wma_handle) handle; wmi_rmv_bcn_filter_cmd_fixed_param *cmd; int len = sizeof(wmi_rmv_bcn_filter_cmd_fixed_param); int ret; struct wmi_unified *wmi_handle; if (wma_validate_handle(wma)) return QDF_STATUS_E_INVAL; wmi_handle = wma->wmi_handle; if (wmi_validate_handle(wma->wmi_handle)) return QDF_STATUS_E_INVAL; buf = wmi_buf_alloc(wmi_handle, len); if (!buf) return QDF_STATUS_E_NOMEM; cmd = (wmi_rmv_bcn_filter_cmd_fixed_param *)wmi_buf_data(buf); cmd->vdev_id = filter_params->vdev_id; WMITLV_SET_HDR(&cmd->tlv_header, WMITLV_TAG_STRUC_wmi_rmv_bcn_filter_cmd_fixed_param, WMITLV_GET_STRUCT_TLVLEN( wmi_rmv_bcn_filter_cmd_fixed_param)); ret = wmi_unified_cmd_send(wmi_handle, buf, len, WMI_RMV_BCN_FILTER_CMDID); if (ret) { wmi_buf_free(buf); return QDF_STATUS_E_FAILURE; } return QDF_STATUS_SUCCESS; } /** * wma_send_adapt_dwelltime_params() - send adaptive dwelltime configuration * params to firmware * @wma_handle: wma handler * @dwelltime_params: pointer to dwelltime_params * * Return: QDF_STATUS_SUCCESS on success and QDF failure reason code for failure */ QDF_STATUS wma_send_adapt_dwelltime_params(WMA_HANDLE handle, struct adaptive_dwelltime_params *dwelltime_params) { tp_wma_handle wma_handle = (tp_wma_handle) handle; struct wmi_adaptive_dwelltime_params wmi_param = {0}; int32_t err; wmi_param.is_enabled = dwelltime_params->is_enabled; wmi_param.dwelltime_mode = dwelltime_params->dwelltime_mode; wmi_param.lpf_weight = dwelltime_params->lpf_weight; wmi_param.passive_mon_intval = dwelltime_params->passive_mon_intval; wmi_param.wifi_act_threshold = dwelltime_params->wifi_act_threshold; err = wmi_unified_send_adapt_dwelltime_params_cmd(wma_handle-> wmi_handle, &wmi_param); if (err) return QDF_STATUS_E_FAILURE; return QDF_STATUS_SUCCESS; } QDF_STATUS wma_send_dbs_scan_selection_params(WMA_HANDLE handle, struct wmi_dbs_scan_sel_params *dbs_scan_params) { tp_wma_handle wma_handle = (tp_wma_handle) handle; int32_t err; err = wmi_unified_send_dbs_scan_sel_params_cmd(wma_handle-> wmi_handle, dbs_scan_params); if (err) return QDF_STATUS_E_FAILURE; return QDF_STATUS_SUCCESS; } /** * wma_unified_fw_profiling_cmd() - send FW profiling cmd to WLAN FW * @wma: wma handle * @cmd: Profiling command index * @value1: parameter1 value * @value2: parameter2 value * * Return: 0 for success else error code */ QDF_STATUS wma_unified_fw_profiling_cmd(wmi_unified_t wmi_handle, uint32_t cmd, uint32_t value1, uint32_t value2) { int ret; ret = wmi_unified_fw_profiling_data_cmd(wmi_handle, cmd, value1, value2); if (ret) { wma_err("enable cmd Failed for id %d value %d", value1, value2); return ret; } return QDF_STATUS_SUCCESS; } /** * wma_wow_set_wake_time() - set timer pattern tlv, so that firmware will wake * up host after specified time is elapsed * @wma_handle: wma handle * @vdev_id: vdev id * @cookie: value to identify reason why host set up wake call. * @time: time in ms * * Return: QDF status */ static QDF_STATUS wma_wow_set_wake_time(WMA_HANDLE wma_handle, uint8_t vdev_id, uint32_t cookie, uint32_t time) { int ret; tp_wma_handle wma = (tp_wma_handle)wma_handle; wma_debug("send timer pattern with time: %d and vdev = %d to fw", time, vdev_id); ret = wmi_unified_wow_timer_pattern_cmd(wma->wmi_handle, vdev_id, cookie, time); if (ret) { wma_err("Failed to send timer pattern to fw"); return QDF_STATUS_E_FAILURE; } return QDF_STATUS_SUCCESS; } #ifdef WLAN_POWER_MANAGEMENT_OFFLOAD /** * wma_check_and_set_wake_timer(): checks all interfaces and if any interface * has install_key pending, sets timer pattern in fw to wake up host after * specified time has elapsed. * @wma: wma handle * @time: time after which host wants to be awaken. * * Return: None */ void wma_check_and_set_wake_timer(uint32_t time) { int i; struct wma_txrx_node *iface; bool is_set_key_in_progress = false; t_wma_handle *wma = cds_get_context(QDF_MODULE_ID_WMA); if (!wma) return; if (!wmi_service_enabled(wma->wmi_handle, wmi_service_wow_wakeup_by_timer_pattern)) { wma_debug("TIME_PATTERN is not enabled"); return; } for (i = 0; i < wma->max_bssid; i++) { iface = &wma->interfaces[i]; if (iface->vdev_active && iface->is_waiting_for_key) { /* * right now cookie is dont care, since FW disregards * that. */ is_set_key_in_progress = true; wma_wow_set_wake_time((WMA_HANDLE)wma, i, 0, time); break; } } if (!is_set_key_in_progress) wma_debug("set key not in progress for any vdev"); } /** * wma_unified_csa_offload_enable() - sen CSA offload enable command * @wma: wma handle * @vdev_id: vdev id * * Return: 0 for success or error code */ int wma_unified_csa_offload_enable(tp_wma_handle wma, uint8_t vdev_id) { if (wmi_unified_csa_offload_enable(wma->wmi_handle, vdev_id)) { wma_alert("Failed to send CSA offload enable command"); return -EIO; } return 0; } #endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ static uint8_t * wma_parse_ch_switch_wrapper_ie(uint8_t *ch_wr_ie, uint8_t sub_ele_id, uint8_t ie_extn_id) { uint8_t len = 0, sub_ele_len = 0; struct ie_header *ele; struct extn_ie_header *extn_ie; ele = (struct ie_header *)ch_wr_ie; if (ele->ie_id != WLAN_ELEMID_CHAN_SWITCH_WRAP || ele->ie_len == 0) { wma_debug("Invalid len: %d", ele->ie_len); return NULL; } len = ele->ie_len; ele = (struct ie_header *)(ch_wr_ie + sizeof(struct ie_header)); while (len > 0) { sub_ele_len = sizeof(struct ie_header) + ele->ie_len; if (sub_ele_len > len) { wma_debug("invalid sub element len :%d id:%d ie len:%d", sub_ele_len, ele->ie_id, ele->ie_len); return NULL; } len -= sub_ele_len; if (ele->ie_id == sub_ele_id) { if (sub_ele_id == WLAN_ELEMID_EXTN_ELEM) { extn_ie = (struct extn_ie_header *)ele; if (extn_ie->ie_extn_id == ie_extn_id) return (uint8_t *)ele; } else { return (uint8_t *)ele; } } ele = (struct ie_header *)((uint8_t *)ele + sub_ele_len); } return NULL; } #ifdef WLAN_FEATURE_11BE /** * wma_parse_bw_indication_ie(): STA parse bandwidth indication IE of connected * AP for channel switch or puncture update. * @ie: bandwidth indication IE * @tlv_len: total length of ie tlv * @csa_event: struct to save channel switch info * * Return - QDF_STATUS. */ static QDF_STATUS wma_parse_bw_indication_ie(uint8_t *ie, uint8_t tlv_len, struct csa_offload_params *csa_event) { uint16_t puncture_bitmap = 0; uint8_t *sub_ie; struct wlan_ie_bw_ind *bw_ind; struct ie_header *ie_head; uint8_t ccfs0, ccfs1; enum phy_ch_width ch_width; QDF_STATUS status; ie_head = (struct ie_header *)ie; if (ie_head->ie_len + sizeof(struct ie_header) > tlv_len) { wma_err("Invalid ie len: %d, buffer len: %d", ie_head->ie_len, tlv_len); return QDF_STATUS_E_INVAL; } qdf_trace_hex_dump(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_DEBUG, ie, tlv_len); sub_ie = wma_parse_ch_switch_wrapper_ie(ie, WLAN_ELEMID_EXTN_ELEM, WLAN_EXTN_ELEMID_BW_IND); if (!sub_ie) { wma_err("Invalid ie"); return QDF_STATUS_E_INVAL; } bw_ind = (struct wlan_ie_bw_ind *)sub_ie; status = util_parse_bw_ind(bw_ind, &ccfs0, &ccfs1, &ch_width, &puncture_bitmap); wma_debug("ch width: %d, ccfs0: %d, ccfs1: %d, puncture: %d", ch_width, ccfs0, ccfs1, puncture_bitmap); csa_event->new_ch_width = ch_width; csa_event->new_ch_freq_seg1 = ccfs0; csa_event->new_ch_freq_seg2 = ccfs1; csa_event->new_punct_bitmap = puncture_bitmap; return QDF_STATUS_SUCCESS; } #else static inline QDF_STATUS wma_parse_bw_indication_ie(uint8_t *ie, uint8_t tlv_len, struct csa_offload_params *csa_event) { return QDF_STATUS_E_NOSUPPORT; } #endif static bool fill_csa_offload_params( wmi_csa_event_fixed_param *csa_event, struct csa_offload_params *csa_offload_event, struct wlan_objmgr_pdev *pdev) { struct ieee80211_channelswitch_ie *csa_ie; struct ieee80211_extendedchannelswitch_ie *xcsa_ie; uint8_t is_csa_ie_present = false; if (csa_event->ies_present_flag & WMI_CSA_IE_PRESENT) { csa_ie = (struct ieee80211_channelswitch_ie *) (&csa_event->csa_ie[0]); csa_offload_event->channel = csa_ie->newchannel; csa_offload_event->csa_chan_freq = wlan_reg_legacy_chan_to_freq(pdev, csa_ie->newchannel); csa_offload_event->switch_mode = csa_ie->switchmode; csa_offload_event->ies_present_flag |= MLME_CSA_IE_PRESENT; is_csa_ie_present = true; } else if (csa_event->ies_present_flag & WMI_XCSA_IE_PRESENT) { xcsa_ie = (struct ieee80211_extendedchannelswitch_ie *) (&csa_event->xcsa_ie[0]); csa_offload_event->channel = xcsa_ie->newchannel; csa_offload_event->switch_mode = xcsa_ie->switchmode; csa_offload_event->new_op_class = xcsa_ie->newClass; if (wlan_reg_is_6ghz_op_class(pdev, xcsa_ie->newClass)) { csa_offload_event->csa_chan_freq = wlan_reg_chan_band_to_freq (pdev, xcsa_ie->newchannel, BIT(REG_BAND_6G)); } else { csa_offload_event->csa_chan_freq = wlan_reg_legacy_chan_to_freq (pdev, xcsa_ie->newchannel); } csa_offload_event->ies_present_flag |= MLME_XCSA_IE_PRESENT; is_csa_ie_present = true; } return is_csa_ie_present; } #ifdef WLAN_FEATURE_11BE static bool handle_csa_standby_link(wmi_csa_event_fixed_param *csa_event, struct wlan_objmgr_psoc *psoc, struct wlan_objmgr_pdev *pdev) { struct mlo_link_info *link_info; struct wlan_mlo_dev_context *mldev; uint8_t mld_addr[QDF_MAC_ADDR_SIZE]; struct csa_offload_params csa_param = {0}; struct mlo_link_bss_params params = {0}; uint8_t is_csa_standby = false; uint8_t link_id; struct wlan_lmac_if_mlo_tx_ops *mlo_tx_ops; QDF_STATUS status = QDF_STATUS_SUCCESS; if (!psoc) { wma_err("null psoc"); return is_csa_standby; } WMI_MAC_ADDR_TO_CHAR_ARRAY(&csa_event->mld_mac_address, &mld_addr[0]); wlan_mlo_get_mlpeer_by_peer_mladdr( (struct qdf_mac_addr *)&mld_addr[0], &mldev); if (!mldev) { wma_err("NULL ml dev ctx"); return is_csa_standby; } link_id = csa_event->link_id; link_info = mlo_mgr_get_ap_link_by_link_id(mldev, link_id); if (!link_info) { wma_err("NULL link info "); return is_csa_standby; } if (link_info->vdev_id != WLAN_INVALID_VDEV_ID) { wma_debug("vdev id %d link id %d ", link_info->vdev_id, link_id); return is_csa_standby; } mlo_tx_ops = &psoc->soc_cb.tx_ops->mlo_ops; if (!mlo_tx_ops) { wma_err("tx_ops is null!"); return is_csa_standby; } if (!fill_csa_offload_params(csa_event, &csa_param, pdev)) { wma_err("CSA Event error: No CSA IE present"); return is_csa_standby; } mlo_mgr_update_csa_link_info(pdev, mldev, &csa_param, link_id); params.link_id = link_info->link_id; params.chan = qdf_mem_malloc(sizeof(struct wlan_channel)); if (!params.chan) { wma_err("no mem acquired"); return is_csa_standby; } qdf_copy_macaddr((struct qdf_mac_addr *)¶ms.ap_mld_mac[0], &link_info->ap_link_addr); params.chan->ch_freq = link_info->link_chan_info->ch_freq; params.chan->ch_cfreq1 = link_info->link_chan_info->ch_cfreq1; params.chan->ch_cfreq2 = link_info->link_chan_info->ch_cfreq2; params.chan->ch_phymode = link_info->link_chan_info->ch_phymode; mlo_debug("link id %d chan freq %d cfreq1 %d cfreq2 %d host phymode %d ap mld mac " QDF_MAC_ADDR_FMT, link_info->link_id, link_info->link_chan_info->ch_freq, link_info->link_chan_info->ch_cfreq1, link_info->link_chan_info->ch_cfreq2, link_info->link_chan_info->ch_phymode, QDF_MAC_ADDR_REF(¶ms.ap_mld_mac[0])); if (!mlo_tx_ops->send_link_set_bss_params_cmd) { wma_err("handler is not registered"); qdf_mem_free(params.chan); return is_csa_standby; } status = mlo_tx_ops->send_link_set_bss_params_cmd(psoc, ¶ms); if (QDF_IS_STATUS_ERROR(status)) { wma_err("failed to send link set bss request command to FW"); qdf_mem_free(params.chan); return is_csa_standby; } is_csa_standby = true; qdf_mem_free(params.chan); return is_csa_standby; } static int fill_peer_mac_addr(wmi_csa_event_fixed_param *csa_event, uint8_t *bssid) { QDF_STATUS status = QDF_STATUS_SUCCESS; uint8_t mld_addr[QDF_MAC_ADDR_SIZE]; uint8_t link_addr[QDF_MAC_ADDR_SIZE]; uint8_t link_id; struct mlo_link_info *link_info; struct wlan_mlo_dev_context *mldev; WMI_MAC_ADDR_TO_CHAR_ARRAY(&csa_event->mld_mac_address, &mld_addr[0]); WMI_MAC_ADDR_TO_CHAR_ARRAY(&csa_event->link_mac_address, &link_addr[0]); wlan_mlo_get_mlpeer_by_peer_mladdr( (struct qdf_mac_addr *)&mld_addr[0], &mldev); if (!mldev) { wma_err("NULL ml dev ctx"); return -EINVAL; } link_id = csa_event->link_id; link_info = mlo_mgr_get_ap_link_by_link_id(mldev, link_id); if (!link_info) { wma_err("NULL link info "); return -EINVAL; } qdf_copy_macaddr((struct qdf_mac_addr *)&bssid[0], &link_info->ap_link_addr); wma_debug("csa event link id %d vdev id %d peer mld addr" QDF_MAC_ADDR_FMT "peer link addr" QDF_MAC_ADDR_FMT "host link info ap_link_addr" QDF_MAC_ADDR_FMT, link_id, link_info->vdev_id, QDF_MAC_ADDR_REF(&mld_addr[0]), QDF_MAC_ADDR_REF(&link_addr[0]), QDF_MAC_ADDR_REF(link_info->ap_link_addr.bytes)); return status; } #else static int fill_peer_mac_addr(wmi_csa_event_fixed_param *csa_event, uint8_t *bssid) { return 0; } static bool handle_csa_standby_link(wmi_csa_event_fixed_param *csa_event, struct wlan_objmgr_psoc *psoc, struct wlan_objmgr_pdev *pdev) { return false; } #endif /** * wma_csa_offload_handler() - CSA event handler * @handle: wma handle * @event: event buffer * @len: buffer length * * This event is sent by firmware when it receives CSA IE. * * Return: 0 for success or error code */ int wma_csa_offload_handler(void *handle, uint8_t *event, uint32_t len) { tp_wma_handle wma = (tp_wma_handle) handle; WMI_CSA_HANDLING_EVENTID_param_tlvs *param_buf; wmi_csa_event_fixed_param *csa_event; uint8_t bssid[QDF_MAC_ADDR_SIZE]; uint8_t vdev_id = 0; uint8_t cur_chan = 0; struct csa_offload_params *csa_offload_event; struct ieee80211_ie_wide_bw_switch *wb_ie; struct wlan_objmgr_peer *peer; struct wlan_objmgr_vdev *vdev; QDF_STATUS status; uint8_t tlv_len; struct wlan_channel *chan; param_buf = (WMI_CSA_HANDLING_EVENTID_param_tlvs *) event; wma_debug("Enter"); if (!param_buf) { wma_err("Invalid csa event buffer"); return -EINVAL; } csa_event = param_buf->fixed_param; if (csa_event->link_id_present && csa_event->mld_mac_address_present) { status = fill_peer_mac_addr(csa_event, &bssid[0]); if (status) return -EINVAL; /* check standby link and return */ if (handle_csa_standby_link(csa_event, wma->psoc, wma->pdev)) return 0; } else { WMI_MAC_ADDR_TO_CHAR_ARRAY(&csa_event->i_addr2, &bssid[0]); } peer = wlan_objmgr_get_peer_by_mac(wma->psoc, bssid, WLAN_LEGACY_WMA_ID); if (!peer) { wma_err("Invalid peer"); return -EINVAL; } vdev_id = wlan_vdev_get_id(wlan_peer_get_vdev(peer)); wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); vdev = wlan_objmgr_get_vdev_by_id_from_psoc(wma->psoc, vdev_id, WLAN_LEGACY_WMA_ID); if (!vdev) return -EINVAL; csa_offload_event = qdf_mem_malloc(sizeof(*csa_offload_event)); if (!csa_offload_event) { wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); return -EINVAL; } if (wlan_cm_is_vdev_roaming(vdev)) { wma_err("Roaming in progress for vdev %d, ignore csa event", vdev_id); qdf_mem_free(csa_offload_event); wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); return -EINVAL; } qdf_mem_zero(csa_offload_event, sizeof(*csa_offload_event)); qdf_copy_macaddr(&csa_offload_event->bssid, (struct qdf_mac_addr *)bssid); if (!fill_csa_offload_params(csa_event, csa_offload_event, wma->pdev)) { wma_err("CSA Event error: No CSA IE present"); qdf_mem_free(csa_offload_event); wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); return -EINVAL; } if (csa_event->ies_present_flag & WMI_CSWRAP_IE_EXT_VER_2_PRESENT) { wma_debug("WMI_CSWRAP_IE_EXT_VER_2 received"); tlv_len = csa_event->num_bytes_valid_in_cswrap_ie_ext_ver2; status = wma_parse_bw_indication_ie(param_buf->cs_wrap_ie, tlv_len, csa_offload_event); if (QDF_IS_STATUS_SUCCESS(status)) { csa_offload_event->ies_present_flag |= MLME_CSWRAP_IE_EXT_V2_PRESENT; goto got_chan; } } if (csa_event->ies_present_flag & WMI_WBW_IE_PRESENT) { wb_ie = (struct ieee80211_ie_wide_bw_switch *) (&csa_event->wb_ie[0]); csa_offload_event->new_ch_freq_seg1 = wb_ie->new_ch_freq_seg1; csa_offload_event->new_ch_freq_seg2 = wb_ie->new_ch_freq_seg2; csa_offload_event->new_ch_width = wlan_mlme_convert_vht_op_bw_to_phy_ch_width(wb_ie->new_ch_width, csa_offload_event->channel, wb_ie->new_ch_freq_seg1, wb_ie->new_ch_freq_seg2); csa_offload_event->ies_present_flag |= MLME_WBW_IE_PRESENT; } else if (csa_event->ies_present_flag & WMI_CSWRAP_IE_EXTENDED_PRESENT) { wb_ie = (struct ieee80211_ie_wide_bw_switch *) wma_parse_ch_switch_wrapper_ie( (uint8_t *)&csa_event->cswrap_ie_extended, WLAN_ELEMID_WIDE_BAND_CHAN_SWITCH, 0); if (wb_ie) { csa_offload_event->new_ch_freq_seg1 = wb_ie->new_ch_freq_seg1; csa_offload_event->new_ch_freq_seg2 = wb_ie->new_ch_freq_seg2; csa_offload_event->new_ch_width = wlan_mlme_convert_vht_op_bw_to_phy_ch_width(wb_ie->new_ch_width, csa_offload_event->channel, wb_ie->new_ch_freq_seg1, wb_ie->new_ch_freq_seg2); csa_event->ies_present_flag |= WMI_WBW_IE_PRESENT; csa_offload_event->ies_present_flag |= MLME_WBW_IE_PRESENT; } csa_offload_event->ies_present_flag |= MLME_CSWRAP_IE_EXTENDED_PRESENT; } got_chan: wma_debug("CSA: BSSID "QDF_MAC_ADDR_FMT" chan %d freq %d flag 0x%x width = %d freq1 = %d freq2 = %d op class = %d", QDF_MAC_ADDR_REF(csa_offload_event->bssid.bytes), csa_offload_event->channel, csa_offload_event->csa_chan_freq, csa_offload_event->ies_present_flag, csa_offload_event->new_ch_width, csa_offload_event->new_ch_freq_seg1, csa_offload_event->new_ch_freq_seg2, csa_offload_event->new_op_class); chan = wlan_vdev_get_active_channel(vdev); if (!chan) { wmi_err("failed to get active channel"); wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); qdf_mem_free(csa_offload_event); return false; } wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); cur_chan = cds_freq_to_chan(chan->ch_freq); /* * basic sanity check: requested channel should not be 0 * and equal to home channel */ if (0 == csa_offload_event->channel) { wma_err("CSA Event with channel %d. Ignore !!", csa_offload_event->channel); qdf_mem_free(csa_offload_event); return -EINVAL; } wma_send_msg(wma, WMA_CSA_OFFLOAD_EVENT, (void *)csa_offload_event, 0); return 0; } #ifdef FEATURE_OEM_DATA_SUPPORT /** * wma_oem_data_response_handler() - OEM data response event handler * @handle: wma handle * @datap: data ptr * @len: data length * * Return: 0 for success or error code */ int wma_oem_data_response_handler(void *handle, uint8_t *datap, uint32_t len) { WMI_OEM_RESPONSE_EVENTID_param_tlvs *param_buf; uint8_t *data; uint32_t datalen; struct oem_data_rsp *oem_rsp; struct mac_context *pmac = cds_get_context(QDF_MODULE_ID_PE); if (!pmac) return -EINVAL; if (!pmac->sme.oem_data_rsp_callback) { wma_err("Callback not registered"); return -EINVAL; } param_buf = (WMI_OEM_RESPONSE_EVENTID_param_tlvs *) datap; if (!param_buf) { wma_err("Received NULL buf ptr from FW"); return -ENOMEM; } data = param_buf->data; datalen = param_buf->num_data; if (!data) { wma_err("Received NULL data from FW"); return -EINVAL; } if (datalen > OEM_DATA_RSP_SIZE) { wma_err("Received data len %d exceeds max value %d", datalen, OEM_DATA_RSP_SIZE); return -EINVAL; } oem_rsp = qdf_mem_malloc(sizeof(*oem_rsp)); if (!oem_rsp) return -ENOMEM; oem_rsp->rsp_len = datalen; if (oem_rsp->rsp_len) { oem_rsp->data = qdf_mem_malloc(oem_rsp->rsp_len); if (!oem_rsp->data) { qdf_mem_free(oem_rsp); return -ENOMEM; } } else { wma_err("Invalid rsp length: %d", oem_rsp->rsp_len); qdf_mem_free(oem_rsp); return -EINVAL; } qdf_mem_copy(oem_rsp->data, data, datalen); wma_debug("Sending OEM_DATA_RSP(len: %d) to upper layer", datalen); pmac->sme.oem_data_rsp_callback(oem_rsp); if (oem_rsp->data) qdf_mem_free(oem_rsp->data); qdf_mem_free(oem_rsp); return 0; } QDF_STATUS wma_start_oem_req_cmd(tp_wma_handle wma_handle, struct oem_data_req *oem_data_req) { QDF_STATUS ret; struct wmi_unified *wmi_handle; wma_debug("Send OEM Data Request to target"); if (!oem_data_req || !oem_data_req->data) { wma_err("oem_data_req is null"); return QDF_STATUS_E_INVAL; } if (wma_validate_handle(wma_handle)) { qdf_mem_free(oem_data_req->data); return QDF_STATUS_E_INVAL; } wmi_handle = wma_handle->wmi_handle; if (wmi_validate_handle(wmi_handle)) { qdf_mem_free(oem_data_req->data); return QDF_STATUS_E_INVAL; } /* legacy api, for oem data request case */ ret = wmi_unified_start_oem_data_cmd(wmi_handle, oem_data_req->data_len, oem_data_req->data); if (!QDF_IS_STATUS_SUCCESS(ret)) wma_err("wmi cmd send failed"); return ret; } #endif /* FEATURE_OEM_DATA_SUPPORT */ #ifdef FEATURE_OEM_DATA QDF_STATUS wma_start_oem_data_cmd(tp_wma_handle wma_handle, struct oem_data *oem_data) { QDF_STATUS ret; struct wmi_unified *wmi_handle; wma_debug("Send OEM Data to target"); if (!oem_data || !oem_data->data) { wma_err("oem_data is null"); return QDF_STATUS_E_INVAL; } if (wma_validate_handle(wma_handle)) return QDF_STATUS_E_INVAL; wmi_handle = wma_handle->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; /* common api, for oem data command case */ ret = wmi_unified_start_oemv2_data_cmd(wmi_handle, oem_data); if (!QDF_IS_STATUS_SUCCESS(ret)) wma_err("call start wmi cmd failed"); return ret; } #endif #if !defined(REMOVE_PKT_LOG) && defined(FEATURE_PKTLOG) /** * wma_pktlog_wmi_send_cmd() - send pktlog enable/disable command to target * @handle: wma handle * @params: pktlog params * * Return: QDF status */ QDF_STATUS wma_pktlog_wmi_send_cmd(WMA_HANDLE handle, struct ath_pktlog_wmi_params *params) { tp_wma_handle wma_handle = (tp_wma_handle) handle; int ret; ret = wmi_unified_pktlog_wmi_send_cmd(wma_handle->wmi_handle, params->pktlog_event, params->cmd_id, params->user_triggered); if (ret) return QDF_STATUS_E_FAILURE; return QDF_STATUS_SUCCESS; } #endif /* !REMOVE_PKT_LOG && FEATURE_PKTLOG */ /** * wma_wow_wake_reason_str() - Converts wow wakeup reason code to text format * @wake_reason - WOW wake reason * * Return: reason code in string format */ static const uint8_t *wma_wow_wake_reason_str(A_INT32 wake_reason) { switch (wake_reason) { case WOW_REASON_UNSPECIFIED: return "UNSPECIFIED"; case WOW_REASON_NLOD: return "NLOD"; case WOW_REASON_AP_ASSOC_LOST: return "AP_ASSOC_LOST"; case WOW_REASON_LOW_RSSI: return "LOW_RSSI"; case WOW_REASON_DEAUTH_RECVD: return "DEAUTH_RECVD"; case WOW_REASON_DISASSOC_RECVD: return "DISASSOC_RECVD"; case WOW_REASON_GTK_HS_ERR: return "GTK_HS_ERR"; case WOW_REASON_EAP_REQ: return "EAP_REQ"; case WOW_REASON_FOURWAY_HS_RECV: return "FOURWAY_HS_RECV"; case WOW_REASON_TIMER_INTR_RECV: return "TIMER_INTR_RECV"; case WOW_REASON_PATTERN_MATCH_FOUND: return "PATTERN_MATCH_FOUND"; case WOW_REASON_RECV_MAGIC_PATTERN: return "RECV_MAGIC_PATTERN"; case WOW_REASON_P2P_DISC: return "P2P_DISC"; case WOW_REASON_WLAN_HB: return "WLAN_HB"; case WOW_REASON_CSA_EVENT: return "CSA_EVENT"; case WOW_REASON_PROBE_REQ_WPS_IE_RECV: return "PROBE_REQ_WPS_IE_RECV"; case WOW_REASON_AUTH_REQ_RECV: return "AUTH_REQ_RECV"; case WOW_REASON_ASSOC_REQ_RECV: return "ASSOC_REQ_RECV"; case WOW_REASON_HTT_EVENT: return "HTT_EVENT"; case WOW_REASON_RA_MATCH: return "RA_MATCH"; case WOW_REASON_HOST_AUTO_SHUTDOWN: return "HOST_AUTO_SHUTDOWN"; case WOW_REASON_IOAC_MAGIC_EVENT: return "IOAC_MAGIC_EVENT"; case WOW_REASON_IOAC_SHORT_EVENT: return "IOAC_SHORT_EVENT"; case WOW_REASON_IOAC_EXTEND_EVENT: return "IOAC_EXTEND_EVENT"; case WOW_REASON_IOAC_TIMER_EVENT: return "IOAC_TIMER_EVENT"; case WOW_REASON_ROAM_HO: return "ROAM_HO"; case WOW_REASON_ROAM_PREAUTH_START: return "ROAM_PREAUTH_START_EVENT"; case WOW_REASON_DFS_PHYERR_RADADR_EVENT: return "DFS_PHYERR_RADADR_EVENT"; case WOW_REASON_BEACON_RECV: return "BEACON_RECV"; case WOW_REASON_CLIENT_KICKOUT_EVENT: return "CLIENT_KICKOUT_EVENT"; case WOW_REASON_NAN_EVENT: return "NAN_EVENT"; case WOW_REASON_EXTSCAN: return "EXTSCAN"; case WOW_REASON_RSSI_BREACH_EVENT: return "RSSI_BREACH_EVENT"; case WOW_REASON_IOAC_REV_KA_FAIL_EVENT: return "IOAC_REV_KA_FAIL_EVENT"; case WOW_REASON_IOAC_SOCK_EVENT: return "IOAC_SOCK_EVENT"; case WOW_REASON_NLO_SCAN_COMPLETE: return "NLO_SCAN_COMPLETE"; case WOW_REASON_PACKET_FILTER_MATCH: return "PACKET_FILTER_MATCH"; case WOW_REASON_ASSOC_RES_RECV: return "ASSOC_RES_RECV"; case WOW_REASON_REASSOC_REQ_RECV: return "REASSOC_REQ_RECV"; case WOW_REASON_REASSOC_RES_RECV: return "REASSOC_RES_RECV"; case WOW_REASON_ACTION_FRAME_RECV: return "ACTION_FRAME_RECV"; case WOW_REASON_BPF_ALLOW: return "BPF_ALLOW"; case WOW_REASON_NAN_DATA: return "NAN_DATA"; case WOW_REASON_OEM_RESPONSE_EVENT: return "OEM_RESPONSE_EVENT"; case WOW_REASON_TDLS_CONN_TRACKER_EVENT: return "TDLS_CONN_TRACKER_EVENT"; case WOW_REASON_CRITICAL_LOG: return "CRITICAL_LOG"; case WOW_REASON_P2P_LISTEN_OFFLOAD: return "P2P_LISTEN_OFFLOAD"; case WOW_REASON_NAN_EVENT_WAKE_HOST: return "NAN_EVENT_WAKE_HOST"; case WOW_REASON_DEBUG_TEST: return "DEBUG_TEST"; case WOW_REASON_CHIP_POWER_FAILURE_DETECT: return "CHIP_POWER_FAILURE_DETECT"; case WOW_REASON_11D_SCAN: return "11D_SCAN"; case WOW_REASON_SAP_OBSS_DETECTION: return "SAP_OBSS_DETECTION"; case WOW_REASON_BSS_COLOR_COLLISION_DETECT: return "BSS_COLOR_COLLISION_DETECT"; #ifdef WLAN_FEATURE_MOTION_DETECTION case WOW_REASON_WLAN_MD: return "MOTION_DETECT"; case WOW_REASON_WLAN_BL: return "MOTION_DETECT_BASELINE"; #endif /* WLAN_FEATURE_MOTION_DETECTION */ case WOW_REASON_PAGE_FAULT: return "PF"; case WOW_REASON_ROAM_PMKID_REQUEST: return "ROAM_PMKID_REQUEST"; case WOW_REASON_VDEV_DISCONNECT: return "VDEV_DISCONNECT"; case WOW_REASON_LOCAL_DATA_UC_DROP: return "LOCAL_DATA_UC_DROP"; case WOW_REASON_FATAL_EVENT_WAKE: return "FATAL_EVENT_WAKE"; case WOW_REASON_GENERIC_WAKE: return "GENERIC_WAKE"; case WOW_REASON_TWT: return "TWT Event"; case WOW_REASON_DCS_INT_DET: return "DCS_INT_DET"; case WOW_REASON_ROAM_STATS: return "ROAM_STATS"; case WOW_REASON_RTT_11AZ: return "WOW_REASON_RTT_11AZ"; case WOW_REASON_DELAYED_WAKEUP_HOST_CFG_TIMER_ELAPSED: return "DELAYED_WAKEUP_TIMER_ELAPSED"; case WOW_REASON_DELAYED_WAKEUP_DATA_STORE_LIST_FULL: return "DELAYED_WAKEUP_DATA_STORE_LIST_FULL"; #ifndef WLAN_SUPPORT_GAP_LL_PS_MODE case WOW_REASON_XGAP: return "XGAP"; #endif default: return "unknown"; } } static bool wma_wow_reason_has_stats(enum wake_reason_e reason) { switch (reason) { case WOW_REASON_ASSOC_REQ_RECV: case WOW_REASON_DISASSOC_RECVD: case WOW_REASON_ASSOC_RES_RECV: case WOW_REASON_REASSOC_REQ_RECV: case WOW_REASON_REASSOC_RES_RECV: case WOW_REASON_AUTH_REQ_RECV: case WOW_REASON_DEAUTH_RECVD: case WOW_REASON_ACTION_FRAME_RECV: case WOW_REASON_BPF_ALLOW: case WOW_REASON_PATTERN_MATCH_FOUND: case WOW_REASON_PACKET_FILTER_MATCH: case WOW_REASON_RA_MATCH: case WOW_REASON_NLOD: case WOW_REASON_NLO_SCAN_COMPLETE: case WOW_REASON_LOW_RSSI: case WOW_REASON_EXTSCAN: case WOW_REASON_RSSI_BREACH_EVENT: case WOW_REASON_OEM_RESPONSE_EVENT: case WOW_REASON_CHIP_POWER_FAILURE_DETECT: case WOW_REASON_11D_SCAN: case WOW_REASON_LOCAL_DATA_UC_DROP: case WOW_REASON_FATAL_EVENT_WAKE: return true; #ifdef WLAN_FEATURE_MOTION_DETECTION case WOW_REASON_WLAN_MD: case WOW_REASON_WLAN_BL: return true; #endif /* WLAN_FEATURE_MOTION_DETECTION */ default: return false; } } static void wma_inc_wow_stats(t_wma_handle *wma, WOW_EVENT_INFO_fixed_param *wake_info) { ucfg_mc_cp_stats_inc_wake_lock_stats(wma->psoc, wake_info->vdev_id, wake_info->wake_reason); } static void wma_wow_stats_display(struct wake_lock_stats *stats) { wma_conditional_log(is_wakeup_event_console_logs_enabled, "WLAN wake reason counters:"); wma_conditional_log(is_wakeup_event_console_logs_enabled, "uc:%d bc:%d v4_mc:%d v6_mc:%d ra:%d ns:%d na:%d " "icmp:%d icmpv6:%d", stats->ucast_wake_up_count, stats->bcast_wake_up_count, stats->ipv4_mcast_wake_up_count, stats->ipv6_mcast_wake_up_count, stats->ipv6_mcast_ra_stats, stats->ipv6_mcast_ns_stats, stats->ipv6_mcast_na_stats, stats->icmpv4_count, stats->icmpv6_count); wma_conditional_log(is_wakeup_event_console_logs_enabled, "assoc:%d disassoc:%d assoc_resp:%d reassoc:%d " "reassoc_resp:%d auth:%d deauth:%d action:%d", stats->mgmt_assoc, stats->mgmt_disassoc, stats->mgmt_assoc_resp, stats->mgmt_reassoc, stats->mgmt_reassoc_resp, stats->mgmt_auth, stats->mgmt_deauth, stats->mgmt_action); wma_conditional_log(is_wakeup_event_console_logs_enabled, "pno_match:%d pno_complete:%d gscan:%d low_rssi:%d" " rssi_breach:%d oem:%d ucdrop:%d scan_11d:%d" " fatal_event:%d", stats->pno_match_wake_up_count, stats->pno_complete_wake_up_count, stats->gscan_wake_up_count, stats->low_rssi_wake_up_count, stats->rssi_breach_wake_up_count, stats->oem_response_wake_up_count, stats->uc_drop_wake_up_count, stats->scan_11d, stats->fatal_event_wake_up_count); } static void wma_print_wow_stats(t_wma_handle *wma, WOW_EVENT_INFO_fixed_param *wake_info) { struct wlan_objmgr_vdev *vdev; struct wake_lock_stats stats = {0}; if (!wma_wow_reason_has_stats(wake_info->wake_reason)) return; vdev = wlan_objmgr_get_vdev_by_id_from_psoc(wma->psoc, wake_info->vdev_id, WLAN_LEGACY_WMA_ID); if (!vdev) { wma_err("vdev_id: %d, failed to get vdev from psoc", wake_info->vdev_id); return; } ucfg_mc_cp_stats_get_vdev_wake_lock_stats(vdev, &stats); wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); wma_wow_stats_display(&stats); } #ifdef FEATURE_WLAN_EXTSCAN /** * wma_extscan_get_eventid_from_tlvtag() - map tlv tag to corresponding event id * @tag: WMI TLV tag * * Return: * 0 if TLV tag is invalid * else return corresponding WMI event id */ static int wma_extscan_get_eventid_from_tlvtag(uint32_t tag) { uint32_t event_id; switch (tag) { case WMITLV_TAG_STRUC_wmi_extscan_start_stop_event_fixed_param: event_id = WMI_EXTSCAN_START_STOP_EVENTID; break; case WMITLV_TAG_STRUC_wmi_extscan_operation_event_fixed_param: event_id = WMI_EXTSCAN_OPERATION_EVENTID; break; case WMITLV_TAG_STRUC_wmi_extscan_table_usage_event_fixed_param: event_id = WMI_EXTSCAN_TABLE_USAGE_EVENTID; break; case WMITLV_TAG_STRUC_wmi_extscan_cached_results_event_fixed_param: event_id = WMI_EXTSCAN_CACHED_RESULTS_EVENTID; break; case WMITLV_TAG_STRUC_wmi_extscan_wlan_change_results_event_fixed_param: event_id = WMI_EXTSCAN_WLAN_CHANGE_RESULTS_EVENTID; break; case WMITLV_TAG_STRUC_wmi_extscan_hotlist_match_event_fixed_param: event_id = WMI_EXTSCAN_HOTLIST_MATCH_EVENTID; break; case WMITLV_TAG_STRUC_wmi_extscan_capabilities_event_fixed_param: event_id = WMI_EXTSCAN_CAPABILITIES_EVENTID; break; default: event_id = 0; wma_err("Unknown tag: %d", tag); break; } wma_info("For tag %d WMI event 0x%x", tag, event_id); return event_id; } #else static int wma_extscan_get_eventid_from_tlvtag(uint32_t tag) { return 0; } #endif /** * wow_get_wmi_eventid() - map reason or tlv tag to corresponding event id * @tag: WMI TLV tag * @reason: WOW reason * * WOW reason type is primarily used to find the ID. If there could be * multiple events that can be sent as a WOW event with same reason * then tlv tag is used to identify the corresponding event. * * Return: * 0 if TLV tag/reason is invalid * else return corresponding WMI event id */ static int wow_get_wmi_eventid(int32_t reason, uint32_t tag) { int event_id; switch (reason) { case WOW_REASON_AP_ASSOC_LOST: event_id = WMI_ROAM_EVENTID; break; case WOW_REASON_NLO_SCAN_COMPLETE: event_id = WMI_NLO_SCAN_COMPLETE_EVENTID; break; case WOW_REASON_CSA_EVENT: event_id = WMI_CSA_HANDLING_EVENTID; break; case WOW_REASON_LOW_RSSI: event_id = WMI_ROAM_EVENTID; break; case WOW_REASON_CLIENT_KICKOUT_EVENT: event_id = WMI_PEER_STA_KICKOUT_EVENTID; break; case WOW_REASON_EXTSCAN: event_id = wma_extscan_get_eventid_from_tlvtag(tag); break; case WOW_REASON_RSSI_BREACH_EVENT: event_id = WMI_RSSI_BREACH_EVENTID; break; case WOW_REASON_NAN_EVENT: event_id = WMI_NAN_EVENTID; break; case WOW_REASON_NAN_DATA: event_id = wma_ndp_get_eventid_from_tlvtag(tag); break; case WOW_REASON_TDLS_CONN_TRACKER_EVENT: event_id = WMI_TDLS_PEER_EVENTID; break; case WOW_REASON_ROAM_HO: event_id = WMI_ROAM_EVENTID; break; case WOW_REASON_11D_SCAN: event_id = WMI_11D_NEW_COUNTRY_EVENTID; break; case WOW_REASON_ROAM_PMKID_REQUEST: event_id = WMI_ROAM_PMKID_REQUEST_EVENTID; break; case WOW_REASON_VDEV_DISCONNECT: event_id = WMI_VDEV_DISCONNECT_EVENTID; break; default: wma_debug("No Event Id for WOW reason %s(%d)", wma_wow_wake_reason_str(reason), reason); event_id = 0; break; } wlan_roam_debug_log(WMA_INVALID_VDEV_ID, DEBUG_WOW_REASON, DEBUG_INVALID_PEER_ID, NULL, NULL, reason, event_id); return event_id; } /** * is_piggybacked_event() - Returns true if the given wake reason indicates * there will be piggybacked TLV event data * @reason: WOW reason * * There are three types of WoW event payloads: none, piggybacked event, and * network packet. This function returns true for wake reasons that fall into * the piggybacked event case. * * Return: true for piggybacked event data */ static bool is_piggybacked_event(int32_t reason) { switch (reason) { case WOW_REASON_AP_ASSOC_LOST: case WOW_REASON_NLO_SCAN_COMPLETE: case WOW_REASON_CSA_EVENT: case WOW_REASON_LOW_RSSI: case WOW_REASON_CLIENT_KICKOUT_EVENT: case WOW_REASON_EXTSCAN: case WOW_REASON_RSSI_BREACH_EVENT: case WOW_REASON_NAN_EVENT: case WOW_REASON_NAN_DATA: case WOW_REASON_TDLS_CONN_TRACKER_EVENT: case WOW_REASON_ROAM_HO: case WOW_REASON_ROAM_PMKID_REQUEST: case WOW_REASON_VDEV_DISCONNECT: case WOW_REASON_TWT: return true; default: return false; } } /** * wma_pkt_proto_subtype_to_string() - to convert proto subtype * of data packet to string. * @proto_subtype: proto subtype for data packet * * This function returns the string for the proto subtype of * data packet. * * Return: string for proto subtype for data packet */ static const char * wma_pkt_proto_subtype_to_string(enum qdf_proto_subtype proto_subtype) { switch (proto_subtype) { case QDF_PROTO_EAPOL_M1: return "EAPOL M1"; case QDF_PROTO_EAPOL_M2: return "EAPOL M2"; case QDF_PROTO_EAPOL_M3: return "EAPOL M3"; case QDF_PROTO_EAPOL_M4: return "EAPOL M4"; case QDF_PROTO_DHCP_DISCOVER: return "DHCP DISCOVER"; case QDF_PROTO_DHCP_REQUEST: return "DHCP REQUEST"; case QDF_PROTO_DHCP_OFFER: return "DHCP OFFER"; case QDF_PROTO_DHCP_ACK: return "DHCP ACK"; case QDF_PROTO_DHCP_NACK: return "DHCP NACK"; case QDF_PROTO_DHCP_RELEASE: return "DHCP RELEASE"; case QDF_PROTO_DHCP_INFORM: return "DHCP INFORM"; case QDF_PROTO_DHCP_DECLINE: return "DHCP DECLINE"; case QDF_PROTO_ARP_REQ: return "ARP REQUEST"; case QDF_PROTO_ARP_RES: return "ARP RESPONSE"; case QDF_PROTO_ICMP_REQ: return "ICMP REQUEST"; case QDF_PROTO_ICMP_RES: return "ICMP RESPONSE"; case QDF_PROTO_ICMPV6_REQ: return "ICMPV6 REQUEST"; case QDF_PROTO_ICMPV6_RES: return "ICMPV6 RESPONSE"; case QDF_PROTO_ICMPV6_RS: return "ICMPV6 RS"; case QDF_PROTO_ICMPV6_RA: return "ICMPV6 RA"; case QDF_PROTO_ICMPV6_NS: return "ICMPV6 NS"; case QDF_PROTO_ICMPV6_NA: return "ICMPV6 NA"; case QDF_PROTO_IPV4_UDP: return "IPV4 UDP Packet"; case QDF_PROTO_IPV4_TCP: return "IPV4 TCP Packet"; case QDF_PROTO_IPV6_UDP: return "IPV6 UDP Packet"; case QDF_PROTO_IPV6_TCP: return "IPV6 TCP Packet"; default: return NULL; } } /** * wma_wow_get_pkt_proto_subtype() - get the proto subtype of the packet. * @data: Pointer to the packet data buffer * @len: length of the packet data buffer * * Return: proto subtype of the packet. */ static enum qdf_proto_subtype wma_wow_get_pkt_proto_subtype(uint8_t *data, uint32_t len) { uint16_t eth_type; uint8_t proto_type; if (len < QDF_NBUF_TRAC_ETH_TYPE_OFFSET + 2) { wma_err("Malformed ethernet packet: length %u < %d", len, QDF_NBUF_TRAC_ETH_TYPE_OFFSET + 2); return QDF_PROTO_INVALID; } eth_type = *(uint16_t *)(data + QDF_NBUF_TRAC_ETH_TYPE_OFFSET); eth_type = qdf_cpu_to_be16(eth_type); wma_debug("Ether Type: 0x%04x", eth_type); switch (eth_type) { case QDF_NBUF_TRAC_EAPOL_ETH_TYPE: if (len < WMA_EAPOL_SUBTYPE_GET_MIN_LEN) return QDF_PROTO_INVALID; wma_debug("EAPOL Packet"); return qdf_nbuf_data_get_eapol_subtype(data); case QDF_NBUF_TRAC_ARP_ETH_TYPE: if (len < WMA_ARP_SUBTYPE_GET_MIN_LEN) return QDF_PROTO_INVALID; wma_debug("ARP Packet"); return qdf_nbuf_data_get_arp_subtype(data); case QDF_NBUF_TRAC_IPV4_ETH_TYPE: if (len < WMA_IPV4_PROTO_GET_MIN_LEN) return QDF_PROTO_INVALID; wma_debug("IPV4 Packet"); proto_type = qdf_nbuf_data_get_ipv4_proto(data); wma_debug("IPV4_proto_type: %u", proto_type); switch (proto_type) { case QDF_NBUF_TRAC_ICMP_TYPE: if (len < WMA_ICMP_SUBTYPE_GET_MIN_LEN) return QDF_PROTO_INVALID; wma_debug("ICMP Packet"); return qdf_nbuf_data_get_icmp_subtype(data); case QDF_NBUF_TRAC_UDP_TYPE: if (len < WMA_IS_DHCP_GET_MIN_LEN) return QDF_PROTO_IPV4_UDP; if (!qdf_nbuf_data_is_ipv4_dhcp_pkt(data)) return QDF_PROTO_IPV4_UDP; if (len < WMA_DHCP_SUBTYPE_GET_MIN_LEN) return QDF_PROTO_INVALID; wma_debug("DHCP Packet"); return qdf_nbuf_data_get_dhcp_subtype(data); case QDF_NBUF_TRAC_TCP_TYPE: return QDF_PROTO_IPV4_TCP; default: return QDF_PROTO_INVALID; } case QDF_NBUF_TRAC_IPV6_ETH_TYPE: if (len < WMA_IPV6_PROTO_GET_MIN_LEN) return QDF_PROTO_INVALID; wma_debug("IPV6 Packet"); proto_type = qdf_nbuf_data_get_ipv6_proto(data); wma_debug("IPV6_proto_type: %u", proto_type); switch (proto_type) { case QDF_NBUF_TRAC_ICMPV6_TYPE: if (len < WMA_ICMPV6_SUBTYPE_GET_MIN_LEN) return QDF_PROTO_INVALID; wma_debug("ICMPV6 Packet"); return qdf_nbuf_data_get_icmpv6_subtype(data); case QDF_NBUF_TRAC_UDP_TYPE: return QDF_PROTO_IPV6_UDP; case QDF_NBUF_TRAC_TCP_TYPE: return QDF_PROTO_IPV6_TCP; default: return QDF_PROTO_INVALID; } default: return QDF_PROTO_INVALID; } } static void wma_log_pkt_eapol(uint8_t *data, uint32_t length) { uint16_t pkt_len, key_len; if (length < WMA_EAPOL_INFO_GET_MIN_LEN) return; pkt_len = *(uint16_t *)(data + EAPOL_PKT_LEN_OFFSET); key_len = *(uint16_t *)(data + EAPOL_KEY_LEN_OFFSET); wma_debug("Pkt_len: %u, Key_len: %u", qdf_cpu_to_be16(pkt_len), qdf_cpu_to_be16(key_len)); } static void wma_log_pkt_dhcp(uint8_t *data, uint32_t length) { uint16_t pkt_len; uint32_t trans_id; if (length < WMA_DHCP_INFO_GET_MIN_LEN) return; pkt_len = *(uint16_t *)(data + DHCP_PKT_LEN_OFFSET); trans_id = *(uint32_t *)(data + DHCP_TRANSACTION_ID_OFFSET); wma_debug("Pkt_len: %u, Transaction_id: %u", qdf_cpu_to_be16(pkt_len), qdf_cpu_to_be16(trans_id)); } static void wma_log_pkt_icmpv4(uint8_t *data, uint32_t length) { uint16_t pkt_len, seq_num; if (length < WMA_IPV4_PKT_INFO_GET_MIN_LEN) return; pkt_len = *(uint16_t *)(data + IPV4_PKT_LEN_OFFSET); seq_num = *(uint16_t *)(data + ICMP_SEQ_NUM_OFFSET); wma_debug("Pkt_len: %u, Seq_num: %u", qdf_cpu_to_be16(pkt_len), qdf_cpu_to_be16(seq_num)); } static void wma_log_pkt_icmpv6(uint8_t *data, uint32_t length) { uint16_t pkt_len, seq_num; if (length < WMA_IPV6_PKT_INFO_GET_MIN_LEN) return; pkt_len = *(uint16_t *)(data + IPV6_PKT_LEN_OFFSET); seq_num = *(uint16_t *)(data + ICMPV6_SEQ_NUM_OFFSET); wma_debug("Pkt_len: %u, Seq_num: %u", qdf_cpu_to_be16(pkt_len), qdf_cpu_to_be16(seq_num)); } static void wma_log_pkt_ipv4(uint8_t *data, uint32_t length) { uint16_t pkt_len, src_port, dst_port; char *ip_addr; if (length < WMA_IPV4_PKT_INFO_GET_MIN_LEN) return; pkt_len = *(uint16_t *)(data + IPV4_PKT_LEN_OFFSET); ip_addr = (char *)(data + IPV4_SRC_ADDR_OFFSET); wma_nofl_debug("src addr %d:%d:%d:%d", ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3]); ip_addr = (char *)(data + IPV4_DST_ADDR_OFFSET); wma_nofl_debug("dst addr %d:%d:%d:%d", ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3]); src_port = *(uint16_t *)(data + IPV4_SRC_PORT_OFFSET); dst_port = *(uint16_t *)(data + IPV4_DST_PORT_OFFSET); wma_debug("Pkt_len: %u, src_port: %u, dst_port: %u", qdf_cpu_to_be16(pkt_len), qdf_cpu_to_be16(src_port), qdf_cpu_to_be16(dst_port)); } static void wma_log_pkt_ipv6(uint8_t *data, uint32_t length) { uint16_t pkt_len, src_port, dst_port; char *ip_addr; if (length < WMA_IPV6_PKT_INFO_GET_MIN_LEN) return; pkt_len = *(uint16_t *)(data + IPV6_PKT_LEN_OFFSET); ip_addr = (char *)(data + IPV6_SRC_ADDR_OFFSET); wma_nofl_debug("src addr "IPV6_ADDR_STR, ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3], ip_addr[4], ip_addr[5], ip_addr[6], ip_addr[7], ip_addr[8], ip_addr[9], ip_addr[10], ip_addr[11], ip_addr[12], ip_addr[13], ip_addr[14], ip_addr[15]); ip_addr = (char *)(data + IPV6_DST_ADDR_OFFSET); wma_nofl_debug("dst addr "IPV6_ADDR_STR, ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3], ip_addr[4], ip_addr[5], ip_addr[6], ip_addr[7], ip_addr[8], ip_addr[9], ip_addr[10], ip_addr[11], ip_addr[12], ip_addr[13], ip_addr[14], ip_addr[15]); src_port = *(uint16_t *)(data + IPV6_SRC_PORT_OFFSET); dst_port = *(uint16_t *)(data + IPV6_DST_PORT_OFFSET); wma_info("Pkt_len: %u, src_port: %u, dst_port: %u", qdf_cpu_to_be16(pkt_len), qdf_cpu_to_be16(src_port), qdf_cpu_to_be16(dst_port)); } static void wma_log_pkt_tcpv4(uint8_t *data, uint32_t length) { uint32_t seq_num; if (length < WMA_IPV4_PKT_INFO_GET_MIN_LEN) return; seq_num = *(uint32_t *)(data + IPV4_TCP_SEQ_NUM_OFFSET); wma_debug("TCP_seq_num: %u", qdf_cpu_to_be16(seq_num)); } static void wma_log_pkt_tcpv6(uint8_t *data, uint32_t length) { uint32_t seq_num; if (length < WMA_IPV6_PKT_INFO_GET_MIN_LEN) return; seq_num = *(uint32_t *)(data + IPV6_TCP_SEQ_NUM_OFFSET); wma_debug("TCP_seq_num: %u", qdf_cpu_to_be16(seq_num)); } static void wma_wow_inc_wake_lock_stats_by_dst_addr(t_wma_handle *wma, uint8_t vdev_id, uint8_t *dest_mac) { ucfg_mc_cp_stats_inc_wake_lock_stats_by_dst_addr(wma->psoc, vdev_id, dest_mac); } static void wma_wow_inc_wake_lock_stats_by_protocol(t_wma_handle *wma, uint8_t vdev_id, enum qdf_proto_subtype proto_subtype) { ucfg_mc_cp_stats_inc_wake_lock_stats_by_protocol(wma->psoc, vdev_id, proto_subtype); } /** * wma_wow_parse_data_pkt() - API to parse data buffer for data * packet that resulted in WOW wakeup. * @stats: per-vdev stats for tracking packet types * @data: Pointer to data buffer * @length: data buffer length * * This function parses the data buffer received (first few bytes of * skb->data) to get information like src mac addr, dst mac addr, packet * len, seq_num, etc. It also increments stats for different packet types. * * Return: void */ static void wma_wow_parse_data_pkt(t_wma_handle *wma, uint8_t vdev_id, uint8_t *data, uint32_t length) { uint8_t *src_mac; uint8_t *dest_mac; const char *proto_subtype_name; enum qdf_proto_subtype proto_subtype; wma_debug("packet length: %u", length); if (length < QDF_NBUF_TRAC_IPV4_OFFSET) return; src_mac = data + QDF_NBUF_SRC_MAC_OFFSET; dest_mac = data + QDF_NBUF_DEST_MAC_OFFSET; wma_conditional_log(is_wakeup_event_console_logs_enabled, "Src_mac: " QDF_MAC_ADDR_FMT ", Dst_mac: " QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(src_mac), QDF_MAC_ADDR_REF(dest_mac)); wma_wow_inc_wake_lock_stats_by_dst_addr(wma, vdev_id, dest_mac); proto_subtype = wma_wow_get_pkt_proto_subtype(data, length); proto_subtype_name = wma_pkt_proto_subtype_to_string(proto_subtype); if (proto_subtype_name) wma_conditional_log(is_wakeup_event_console_logs_enabled, "WOW Wakeup: %s rcvd", proto_subtype_name); switch (proto_subtype) { case QDF_PROTO_EAPOL_M1: case QDF_PROTO_EAPOL_M2: case QDF_PROTO_EAPOL_M3: case QDF_PROTO_EAPOL_M4: wma_log_pkt_eapol(data, length); break; case QDF_PROTO_DHCP_DISCOVER: case QDF_PROTO_DHCP_REQUEST: case QDF_PROTO_DHCP_OFFER: case QDF_PROTO_DHCP_ACK: case QDF_PROTO_DHCP_NACK: case QDF_PROTO_DHCP_RELEASE: case QDF_PROTO_DHCP_INFORM: case QDF_PROTO_DHCP_DECLINE: wma_log_pkt_dhcp(data, length); break; case QDF_PROTO_ICMP_REQ: case QDF_PROTO_ICMP_RES: wma_wow_inc_wake_lock_stats_by_protocol(wma, vdev_id, proto_subtype); wma_log_pkt_icmpv4(data, length); break; case QDF_PROTO_ICMPV6_REQ: case QDF_PROTO_ICMPV6_RES: case QDF_PROTO_ICMPV6_RS: case QDF_PROTO_ICMPV6_RA: case QDF_PROTO_ICMPV6_NS: case QDF_PROTO_ICMPV6_NA: wma_wow_inc_wake_lock_stats_by_protocol(wma, vdev_id, proto_subtype); wma_log_pkt_icmpv6(data, length); break; case QDF_PROTO_IPV4_UDP: wma_log_pkt_ipv4(data, length); break; case QDF_PROTO_IPV4_TCP: wma_log_pkt_ipv4(data, length); wma_log_pkt_tcpv4(data, length); break; case QDF_PROTO_IPV6_UDP: wma_log_pkt_ipv6(data, length); break; case QDF_PROTO_IPV6_TCP: wma_log_pkt_ipv6(data, length); wma_log_pkt_tcpv6(data, length); break; default: break; } } /** * wma_wow_dump_mgmt_buffer() - API to parse data buffer for mgmt. * packet that resulted in WOW wakeup. * @wow_packet_buffer: Pointer to data buffer * @buf_len: length of data buffer * * This function parses the data buffer received (802.11 header) * to get information like src mac addr, dst mac addr, seq_num, * frag_num, etc. * * Return: void */ static void wma_wow_dump_mgmt_buffer(uint8_t *wow_packet_buffer, uint32_t buf_len) { struct ieee80211_frame_addr4 *wh; wma_debug("wow_buf_pkt_len: %u", buf_len); wh = (struct ieee80211_frame_addr4 *) (wow_packet_buffer); if (buf_len >= sizeof(struct ieee80211_frame)) { uint8_t to_from_ds, frag_num; uint32_t seq_num; wma_conditional_log(is_wakeup_event_console_logs_enabled, "RA: " QDF_MAC_ADDR_FMT " TA: " QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(wh->i_addr1), QDF_MAC_ADDR_REF(wh->i_addr2)); wma_conditional_log(is_wakeup_event_console_logs_enabled, "TO_DS: %u, FROM_DS: %u", wh->i_fc[1] & IEEE80211_FC1_DIR_TODS, wh->i_fc[1] & IEEE80211_FC1_DIR_FROMDS); to_from_ds = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; switch (to_from_ds) { case IEEE80211_FC1_DIR_NODS: wma_conditional_log( is_wakeup_event_console_logs_enabled, "BSSID: " QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(wh->i_addr3)); break; case IEEE80211_FC1_DIR_TODS: wma_conditional_log( is_wakeup_event_console_logs_enabled, "DA: " QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(wh->i_addr3)); break; case IEEE80211_FC1_DIR_FROMDS: wma_conditional_log( is_wakeup_event_console_logs_enabled, "SA: " QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(wh->i_addr3)); break; case IEEE80211_FC1_DIR_DSTODS: if (buf_len >= sizeof(struct ieee80211_frame_addr4)) wma_conditional_log( is_wakeup_event_console_logs_enabled, "DA: " QDF_MAC_ADDR_FMT " SA: " QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(wh->i_addr3), QDF_MAC_ADDR_REF(wh->i_addr4)); break; } seq_num = (((*(uint16_t *)wh->i_seq) & IEEE80211_SEQ_SEQ_MASK) >> IEEE80211_SEQ_SEQ_SHIFT); frag_num = (((*(uint16_t *)wh->i_seq) & IEEE80211_SEQ_FRAG_MASK) >> IEEE80211_SEQ_FRAG_SHIFT); wma_conditional_log(is_wakeup_event_console_logs_enabled, "SEQ_NUM: %u, FRAG_NUM: %u", seq_num, frag_num); } else { wma_err("Insufficient buffer length for mgmt. packet"); } } /** * wma_acquire_wakelock() - conditionally acquires a wakelock base on wake reason * @wma: the wma handle with the wakelocks to acquire * @wake_reason: wow wakeup reason * * Return: None */ static void wma_acquire_wow_wakelock(t_wma_handle *wma, int wake_reason) { qdf_wake_lock_t *wl; uint32_t ms; switch (wake_reason) { case WOW_REASON_AUTH_REQ_RECV: wl = &wma->wow_auth_req_wl; ms = WMA_AUTH_REQ_RECV_WAKE_LOCK_TIMEOUT; break; case WOW_REASON_ASSOC_REQ_RECV: wl = &wma->wow_assoc_req_wl; ms = WMA_ASSOC_REQ_RECV_WAKE_LOCK_DURATION; break; case WOW_REASON_DEAUTH_RECVD: wl = &wma->wow_deauth_rec_wl; ms = WMA_DEAUTH_RECV_WAKE_LOCK_DURATION; break; case WOW_REASON_DISASSOC_RECVD: wl = &wma->wow_disassoc_rec_wl; ms = WMA_DISASSOC_RECV_WAKE_LOCK_DURATION; break; case WOW_REASON_AP_ASSOC_LOST: wl = &wma->wow_ap_assoc_lost_wl; ms = WMA_BMISS_EVENT_WAKE_LOCK_DURATION; break; #ifdef FEATURE_WLAN_AUTO_SHUTDOWN case WOW_REASON_HOST_AUTO_SHUTDOWN: wl = &wma->wow_auto_shutdown_wl; ms = WMA_AUTO_SHUTDOWN_WAKE_LOCK_DURATION; break; #endif case WOW_REASON_ROAM_HO: wl = &wma->roam_ho_wl; ms = WMA_ROAM_HO_WAKE_LOCK_DURATION; break; case WOW_REASON_ROAM_PREAUTH_START: wl = &wma->roam_preauth_wl; ms = WMA_ROAM_PREAUTH_WAKE_LOCK_DURATION; break; case WOW_REASON_PROBE_REQ_WPS_IE_RECV: wl = &wma->probe_req_wps_wl; ms = WMA_REASON_PROBE_REQ_WPS_IE_RECV_DURATION; break; default: return; } wma_alert("Holding %d msec wake_lock", ms); cds_host_diag_log_work(wl, ms, WIFI_POWER_EVENT_WAKELOCK_WOW); qdf_wake_lock_timeout_acquire(wl, ms); } /** * wma_wake_reason_ap_assoc_lost() - WOW_REASON_AP_ASSOC_LOST handler * @wma: Pointer to wma handle * @event: pointer to piggybacked WMI_ROAM_EVENTID_param_tlvs buffer * @len: length of the event buffer * * Return: Errno */ static int wma_wake_reason_ap_assoc_lost(t_wma_handle *wma, void *event, uint32_t len) { WMI_ROAM_EVENTID_param_tlvs *event_param; wmi_roam_event_fixed_param *roam_event; event_param = event; if (!event_param) { wma_err("AP Assoc Lost event data is null"); return -EINVAL; } roam_event = event_param->fixed_param; wma_alert("Beacon miss indication on vdev %d", roam_event->vdev_id); wma_beacon_miss_handler(wma, roam_event->vdev_id, roam_event->rssi); return 0; } static const char *wma_vdev_type_str(uint32_t vdev_type) { switch (vdev_type) { case WMI_VDEV_TYPE_AP: return "AP"; case WMI_VDEV_TYPE_STA: return "STA"; case WMI_VDEV_TYPE_IBSS: return "IBSS"; case WMI_VDEV_TYPE_MONITOR: return "MONITOR"; case WMI_VDEV_TYPE_NAN: return "NAN"; case WMI_VDEV_TYPE_OCB: return "OCB"; case WMI_VDEV_TYPE_NDI: return "NDI"; default: return "unknown"; } } static int wma_wake_event_packet( t_wma_handle *wma, WMI_WOW_WAKEUP_HOST_EVENTID_param_tlvs *event_param, uint32_t length) { WOW_EVENT_INFO_fixed_param *wake_info; struct wma_txrx_node *vdev; uint8_t *packet; uint32_t packet_len; if (event_param->num_wow_packet_buffer <= 4) { wma_err("Invalid wow packet buffer from firmware %u", event_param->num_wow_packet_buffer); return -EINVAL; } /* first 4 bytes are the length, followed by the buffer */ packet_len = *(uint32_t *)event_param->wow_packet_buffer; packet = event_param->wow_packet_buffer + 4; if (!packet_len) { wma_err("Wake event packet is empty"); return 0; } if (packet_len > (event_param->num_wow_packet_buffer - 4)) { wma_err("Invalid packet_len from firmware, packet_len: %u, num_wow_packet_buffer: %u", packet_len, event_param->num_wow_packet_buffer); return -EINVAL; } wake_info = event_param->fixed_param; wma_debug("Number of delayed packets received = %d", wake_info->delayed_pkt_count); switch (wake_info->wake_reason) { case WOW_REASON_AUTH_REQ_RECV: case WOW_REASON_ASSOC_REQ_RECV: case WOW_REASON_DEAUTH_RECVD: case WOW_REASON_DISASSOC_RECVD: case WOW_REASON_ASSOC_RES_RECV: case WOW_REASON_REASSOC_REQ_RECV: case WOW_REASON_REASSOC_RES_RECV: case WOW_REASON_BEACON_RECV: case WOW_REASON_ACTION_FRAME_RECV: /* management frame case */ wma_wow_dump_mgmt_buffer(packet, packet_len); break; case WOW_REASON_BPF_ALLOW: case WOW_REASON_PATTERN_MATCH_FOUND: case WOW_REASON_RA_MATCH: case WOW_REASON_RECV_MAGIC_PATTERN: case WOW_REASON_PACKET_FILTER_MATCH: case WOW_REASON_DELAYED_WAKEUP_HOST_CFG_TIMER_ELAPSED: case WOW_REASON_DELAYED_WAKEUP_DATA_STORE_LIST_FULL: wma_info("Wake event packet:"); qdf_trace_hex_dump(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_DEBUG, packet, packet_len); vdev = &wma->interfaces[wake_info->vdev_id]; wma_wow_parse_data_pkt(wma, wake_info->vdev_id, packet, packet_len); break; case WOW_REASON_PAGE_FAULT: /* * In case PAGE_FAULT occurs on non-DRV platform, * dump event buffer which contains more info regarding * current page fault. */ wma_info("PF occurs during suspend: packet_len %u", packet_len); qdf_trace_hex_dump(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_INFO, packet, packet_len); break; default: return -EINVAL; } return 0; } static int wma_wake_event_no_payload( t_wma_handle *wma, WMI_WOW_WAKEUP_HOST_EVENTID_param_tlvs *event_param, uint32_t length) { WOW_EVENT_INFO_fixed_param *wake_info = event_param->fixed_param; switch (wake_info->wake_reason) { case WOW_REASON_HOST_AUTO_SHUTDOWN: return wma_wake_reason_auto_shutdown(); case WOW_REASON_NLOD: return wma_wake_reason_nlod(wma, wake_info->vdev_id); case WOW_REASON_GENERIC_WAKE: case WOW_REASON_ROAM_STATS: case WOW_REASON_RTT_11AZ: wma_info("Wake reason %s", wma_wow_wake_reason_str(wake_info->wake_reason)); return 0; default: return 0; } } static int wma_wake_event_piggybacked( t_wma_handle *wma, WMI_WOW_WAKEUP_HOST_EVENTID_param_tlvs *event_param, uint32_t length) { int errno = 0; void *pb_event; uint32_t pb_event_len; uint32_t wake_reason; uint32_t event_id; uint8_t *bssid; tpDeleteStaContext del_sta_ctx; /* * There are "normal" cases where a wake reason that usually contains a * piggybacked event is empty. In these cases we just want to wake up, * and no action is needed. Bail out now if that is the case. */ if (!event_param->wow_packet_buffer || event_param->num_wow_packet_buffer <= 4) { wma_err("Invalid wow packet buffer from firmware %u", event_param->num_wow_packet_buffer); return 0; } bssid = wma_get_vdev_bssid (wma->interfaces[event_param->fixed_param->vdev_id].vdev); if (!bssid) { wma_err("Failed to get bssid for vdev_%d", event_param->fixed_param->vdev_id); return 0; } wake_reason = event_param->fixed_param->wake_reason; /* parse piggybacked event from param buffer */ { int ret_code; uint8_t *pb_event_buf; uint32_t tag; /* first 4 bytes are the length, followed by the buffer */ pb_event_len = *(uint32_t *)event_param->wow_packet_buffer; if (pb_event_len > (event_param->num_wow_packet_buffer - 4)) { wma_err("Invalid pb_event_len from firmware, pb_event_len: %u, num_wow_packet_buffer: %u", pb_event_len, event_param->num_wow_packet_buffer); return -EINVAL; } pb_event_buf = event_param->wow_packet_buffer + 4; wma_debug("piggybacked event buffer:"); qdf_trace_hex_dump(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_DEBUG, pb_event_buf, pb_event_len); tag = WMITLV_GET_TLVTAG(WMITLV_GET_HDR(pb_event_buf)); event_id = wow_get_wmi_eventid(wake_reason, tag); if (!event_id) { wma_err("Unable to find Event Id"); return -EINVAL; } ret_code = wmitlv_check_and_pad_event_tlvs(wma, pb_event_buf, pb_event_len, event_id, &pb_event); if (ret_code) { wma_err("Bad TLVs; len:%d, event_id:%d, status:%d", pb_event_len, event_id, ret_code); return -EINVAL; } } switch (wake_reason) { case WOW_REASON_AP_ASSOC_LOST: errno = wma_wake_reason_ap_assoc_lost(wma, pb_event, pb_event_len); break; #ifdef FEATURE_WLAN_SCAN_PNO case WOW_REASON_NLO_SCAN_COMPLETE: errno = target_if_nlo_complete_handler(wma, pb_event, pb_event_len); break; #endif /* FEATURE_WLAN_SCAN_PNO */ case WOW_REASON_CSA_EVENT: errno = wma_csa_offload_handler(wma, pb_event, pb_event_len); break; /* * WOW_REASON_LOW_RSSI is used for following roaming events - * WMI_ROAM_REASON_BETTER_AP, WMI_ROAM_REASON_BMISS, * WMI_ROAM_REASON_SUITABLE_AP will be handled by * wma_roam_event_callback(). * WOW_REASON_ROAM_HO is associated with * WMI_ROAM_REASON_HO_FAILED event and it will be handled by * wma_roam_event_callback(). */ case WOW_REASON_LOW_RSSI: case WOW_REASON_ROAM_HO: wlan_roam_debug_log(event_param->fixed_param->vdev_id, DEBUG_WOW_ROAM_EVENT, DEBUG_INVALID_PEER_ID, NULL, NULL, wake_reason, pb_event_len); if (pb_event_len > 0) { errno = target_if_cm_roam_event(wma, pb_event, pb_event_len); } else { /* * No wow_packet_buffer means a better AP beacon * will follow in a later event. */ wma_debug("Host woken up because of better AP beacon"); } break; case WOW_REASON_CLIENT_KICKOUT_EVENT: errno = wma_peer_sta_kickout_event_handler(wma, pb_event, pb_event_len); break; #ifdef FEATURE_WLAN_EXTSCAN case WOW_REASON_EXTSCAN: errno = wma_extscan_wow_event_callback(wma, pb_event, pb_event_len); break; #endif case WOW_REASON_RSSI_BREACH_EVENT: errno = wma_rssi_breached_event_handler(wma, pb_event, pb_event_len); break; case WOW_REASON_NAN_EVENT: errno = wma_nan_rsp_handler_callback(wma, pb_event, pb_event_len); break; case WOW_REASON_NAN_DATA: errno = wma_ndp_wow_event_callback(wma, pb_event, pb_event_len, event_id); break; #ifdef FEATURE_WLAN_TDLS case WOW_REASON_TDLS_CONN_TRACKER_EVENT: errno = wma_tdls_event_handler(wma, pb_event, pb_event_len); break; #endif case WOW_REASON_TIMER_INTR_RECV: /* * Right now firmware is not returning any cookie host has * programmed. So do not check for cookie. */ wma_err("WOW_REASON_TIMER_INTR_RECV received, indicating key exchange did not finish. Initiate disconnect"); del_sta_ctx = qdf_mem_malloc(sizeof(*del_sta_ctx)); if (!del_sta_ctx) break; del_sta_ctx->is_tdls = false; del_sta_ctx->vdev_id = event_param->fixed_param->vdev_id; qdf_mem_copy(del_sta_ctx->addr2, bssid, QDF_MAC_ADDR_SIZE); qdf_mem_copy(del_sta_ctx->bssId, bssid, QDF_MAC_ADDR_SIZE); del_sta_ctx->reasonCode = HAL_DEL_STA_REASON_CODE_KEEP_ALIVE; wma_send_msg(wma, SIR_LIM_DELETE_STA_CONTEXT_IND, del_sta_ctx, 0); break; case WOW_REASON_ROAM_PMKID_REQUEST: wma_debug("Host woken up because of PMKID request event"); errno = target_if_pmkid_request_event_handler(wma, pb_event, pb_event_len); break; case WOW_REASON_VDEV_DISCONNECT: wma_debug("Host woken up because of vdev disconnect event"); errno = target_if_cm_roam_vdev_disconnect_event_handler(wma, pb_event, pb_event_len); break; default: wma_err("Wake reason %s(%u) is not a piggybacked event", wma_wow_wake_reason_str(wake_reason), wake_reason); errno = -EINVAL; break; } wmitlv_free_allocated_event_tlvs(event_id, &pb_event); return errno; } static void wma_debug_assert_page_fault_wakeup(uint32_t reason) { /* During DRV if page fault wake up then assert */ if ((WOW_REASON_PAGE_FAULT == reason) && (qdf_is_drv_connected())) QDF_DEBUG_PANIC("Unexpected page fault wake up detected during DRV wow"); } static void wma_wake_event_log_reason(t_wma_handle *wma, WOW_EVENT_INFO_fixed_param *wake_info) { struct wma_txrx_node *vdev; /* "Unspecified" means APPS triggered wake, else firmware triggered */ if (wake_info->wake_reason != WOW_REASON_UNSPECIFIED) { vdev = &wma->interfaces[wake_info->vdev_id]; wma_nofl_info("WLAN triggered wakeup: %s (%d), vdev: %d (%s)", wma_wow_wake_reason_str(wake_info->wake_reason), wake_info->wake_reason, wake_info->vdev_id, wma_vdev_type_str(vdev->type)); wma_debug_assert_page_fault_wakeup(wake_info->wake_reason); } else if (!wmi_get_runtime_pm_inprogress(wma->wmi_handle)) { wma_nofl_info("Non-WLAN triggered wakeup: %s (%d)", wma_wow_wake_reason_str(wake_info->wake_reason), wake_info->wake_reason); } qdf_wow_wakeup_host_event(wake_info->wake_reason); qdf_wma_wow_wakeup_stats_event(wma); } static QDF_STATUS wma_wow_pagefault_action_cb(void *buf) { struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); if (!mac) { wma_err("NULL mac ptr"); return QDF_STATUS_E_INVAL; } return mac->sme.pagefault_action_cb(buf, WLAN_WMA_PF_APPS_NOTIFY_BUF_LEN); } static QDF_STATUS wma_wow_pagefault_add_sym_to_event(tp_wma_handle wma, struct wow_pf_sym *pf_sym) { uint8_t *buf_ptr = wma->wma_pf_hist.pf_notify_buf_ptr; uint32_t buf_len = wma->wma_pf_hist.pf_notify_buf_len; if (WLAN_WMA_PF_APPS_NOTIFY_BUF_LEN - buf_len < WLAN_WMA_PER_PF_SYM_NOTIFY_BUF_LEN) { wma_wow_pagefault_action_cb(buf_ptr); buf_len = 0; } /* Mem zero to send buffer with zero padding */ if (!buf_len) qdf_mem_zero(buf_ptr, WLAN_WMA_PF_APPS_NOTIFY_BUF_LEN); qdf_mem_copy(buf_ptr + buf_len, pf_sym, WLAN_WMA_PER_PF_SYM_NOTIFY_BUF_LEN); buf_len += WLAN_WMA_PER_PF_SYM_NOTIFY_BUF_LEN; wma->wma_pf_hist.pf_notify_buf_len = buf_len; return QDF_STATUS_SUCCESS; } static void wma_wow_pagefault_add_new_sym_from_event(tp_wma_handle wma, struct wow_pf_wakeup_ev_data *pf_sym_list, qdf_time_t cur_time) { uint8_t tbl_idx, ev_lst_idx, new_pf_idx, idx2; uint8_t new_idx_cnt, cur_idx_cnt, ev_sym_cnt, pf_th; uint8_t max_sym_count = WLAN_WMA_MAX_PF_SYM; qdf_time_t new_idx_last_ts, cur_idx_last_ts; qdf_time_t new_idx_old_ts, cur_idx_old_ts; struct wma_pf_sym *cur_pf_entry, *new_pf_entry; struct wma_pf_sym_hist *pf_sym_hist = &wma->wma_pf_hist; pf_th = wlan_pmo_get_min_pagefault_wakeups_for_action(wma->psoc); for (tbl_idx = 0; tbl_idx < max_sym_count; tbl_idx++) { if (!pf_sym_list->pending_pf_syms) break; new_pf_idx = tbl_idx; new_pf_entry = &pf_sym_hist->wma_pf_sym[tbl_idx]; new_idx_cnt = new_pf_entry->pf_sym.count; new_idx_last_ts = new_pf_entry->pf_ev_ts[new_idx_cnt - 1]; new_idx_old_ts = new_pf_entry->pf_ev_ts[0]; for (ev_lst_idx = 0; ev_lst_idx < pf_sym_list->num_pf_syms; ev_lst_idx++) { if (!pf_sym_list->pf_sym[ev_lst_idx].count) continue; /* Add the sym that already met threshold to the buf */ if (pf_sym_list->pf_sym[ev_lst_idx].count >= pf_th) { wma_wow_pagefault_add_sym_to_event(wma, pf_sym_list->pf_sym); pf_sym_list->pf_sym[ev_lst_idx].count = 0x0; pf_sym_list->pending_pf_syms--; continue; } ev_sym_cnt = pf_sym_list->pf_sym[ev_lst_idx].count; /* If current entry is NULL, add the symbol here */ if (!new_idx_cnt) goto add_sym; /* Replace event if count is equal as current event * is latest and don't replace symbol from current event */ if (new_idx_cnt > ev_sym_cnt || qdf_system_time_after_eq(new_idx_last_ts, cur_time)) break; for (idx2 = tbl_idx + 1; idx2 < max_sym_count; idx2++) { cur_pf_entry = &pf_sym_hist->wma_pf_sym[idx2]; cur_idx_cnt = cur_pf_entry->pf_sym.count; cur_idx_last_ts = cur_pf_entry->pf_ev_ts[cur_idx_cnt - 1]; cur_idx_old_ts = cur_pf_entry->pf_ev_ts[0]; if (cur_idx_cnt > new_idx_cnt) continue; /* Don't replace symbol from current event */ if (qdf_system_time_after_eq(cur_idx_last_ts, cur_time)) { continue; } if (cur_idx_cnt == new_idx_cnt && qdf_system_time_after_eq(cur_idx_old_ts, new_idx_old_ts)) { continue; } new_pf_idx = idx2; new_idx_cnt = cur_idx_cnt; new_idx_last_ts = cur_idx_last_ts; new_idx_old_ts = cur_idx_old_ts; } add_sym: /* Replace symbol with current event symbol */ new_pf_entry = &pf_sym_hist->wma_pf_sym[new_pf_idx]; new_pf_entry->pf_sym.symbol = pf_sym_list->pf_sym[ev_lst_idx].symbol; new_pf_entry->pf_sym.count = ev_sym_cnt; for (idx2 = 0; idx2 < ev_sym_cnt; idx2++) new_pf_entry->pf_ev_ts[idx2] = cur_time; pf_sym_list->pending_pf_syms--; pf_sym_list->pf_sym[ev_lst_idx].count = 0; break; } } } static void wma_wow_pagefault_process_existing_syms(tp_wma_handle wma, struct wma_pf_sym *pf_sym_entry, struct wow_pf_wakeup_ev_data *ev_list, qdf_time_t cur_time) { uint8_t ev_idx, add_idx; uint8_t pf_th, *tbl_sym_cnt, ev_sym_cnt; pf_th = wlan_pmo_get_min_pagefault_wakeups_for_action(wma->psoc); tbl_sym_cnt = &pf_sym_entry->pf_sym.count; for (ev_idx = 0; ev_idx < ev_list->num_pf_syms; ev_idx++) { /* Ignore if all symbols are processed */ if (!ev_list->pending_pf_syms) break; if (!ev_list->pf_sym[ev_idx].count) continue; /* Only process symbol equals current entry in the history */ if (ev_list->pf_sym[ev_idx].symbol != pf_sym_entry->pf_sym.symbol) { continue; } ev_sym_cnt = ev_list->pf_sym[ev_idx].count; /* If symbol reaches threshold, then clear the ts data */ if (*tbl_sym_cnt + ev_sym_cnt >= pf_th) { qdf_mem_zero(&pf_sym_entry->pf_ev_ts[0], (*tbl_sym_cnt * sizeof(qdf_time_t))); *tbl_sym_cnt += ev_sym_cnt; wma_wow_pagefault_add_sym_to_event(wma, &pf_sym_entry->pf_sym); *tbl_sym_cnt = 0x0; goto sym_handled; } for (add_idx = 0; add_idx < ev_sym_cnt; add_idx++) pf_sym_entry->pf_ev_ts[(*tbl_sym_cnt)++] = cur_time; sym_handled: ev_list->pending_pf_syms--; ev_list->pf_sym[ev_idx].count = 0; break; } } static void wma_wow_pagefault_flush_ageout_entries(struct wma_pf_sym *pf_sym_entry, qdf_time_t cutoff_time) { qdf_time_t entry_ts; uint8_t *cur_pf_count, pf_ts_idx; cur_pf_count = &pf_sym_entry->pf_sym.count; /* Find the count of entries which elapsed cutoff time */ for (pf_ts_idx = 0; pf_ts_idx < *cur_pf_count; pf_ts_idx++) { entry_ts = pf_sym_entry->pf_ev_ts[pf_ts_idx]; if (qdf_system_time_before(cutoff_time, entry_ts)) break; } /* Remove the entries which elapsed cutoff time */ if (pf_ts_idx > 0) { *cur_pf_count -= pf_ts_idx; qdf_mem_copy(&pf_sym_entry->pf_ev_ts[0], &pf_sym_entry->pf_ev_ts[pf_ts_idx], (*cur_pf_count * sizeof(qdf_time_t))); qdf_mem_zero(&pf_sym_entry->pf_ev_ts[*cur_pf_count], pf_ts_idx * sizeof(qdf_time_t)); } } static QDF_STATUS wma_wow_pagefault_parse_event(struct wlan_objmgr_psoc *psoc, void *ev, uint32_t ev_len, struct wow_pf_wakeup_ev_data *pf_sym_list) { WMI_WOW_WAKEUP_HOST_EVENTID_param_tlvs *event_param = ev; uint8_t *cur_list_count, *pf_sym_addr, buf_idx, sym_idx, i; uint32_t packet_len, symbol, pf_sym_count; struct wow_pf_sym tmp_pf_sym; if (event_param->num_wow_packet_buffer <= sizeof(packet_len)) { wma_err("Invalid wow packet buffer from FW %u", event_param->num_wow_packet_buffer); return QDF_STATUS_E_INVAL; } packet_len = *(uint32_t *)event_param->wow_packet_buffer; if (!packet_len) { wma_err("Wake event packet is empty"); return QDF_STATUS_E_INVAL; } if (packet_len > (event_param->num_wow_packet_buffer - sizeof(packet_len))) { wma_err("Invalid packet_len from firmware, packet_len: %u, num_wow_packet_buffer: %u", packet_len, event_param->num_wow_packet_buffer); return QDF_STATUS_E_INVAL; } pf_sym_count = packet_len / sizeof(symbol); /* First 4 bytes following packet len contains UUID */ pf_sym_count--; pf_sym_list->pf_sym = qdf_mem_malloc(pf_sym_count * sizeof(*pf_sym_list->pf_sym)); if (!pf_sym_list->pf_sym) return QDF_STATUS_E_NOMEM; /* First 4 bytes of buffer gives length and next 4 bytes for UUID */ pf_sym_addr = event_param->wow_packet_buffer + (sizeof(packet_len) * 2); cur_list_count = &pf_sym_list->num_pf_syms; for (buf_idx = 0; buf_idx < pf_sym_count; buf_idx++) { symbol = *(uint32_t *)pf_sym_addr; /* Ignore invalid symbols */ if (!symbol || symbol == (uint32_t)-1) goto iter_next_sym; for (sym_idx = 0; sym_idx < *cur_list_count; sym_idx++) { if (pf_sym_list->pf_sym[sym_idx].symbol == symbol) { pf_sym_list->pf_sym[sym_idx].count++; goto iter_next_sym; } } pf_sym_list->pf_sym[*cur_list_count].symbol = symbol; pf_sym_list->pf_sym[*cur_list_count].count++; (*cur_list_count)++; iter_next_sym: pf_sym_addr += sizeof(symbol); } pf_sym_list->pending_pf_syms = *cur_list_count; /* Reorder to prioritize syms with high frequency */ for (sym_idx = 0; sym_idx < *cur_list_count; sym_idx++) { for (i = sym_idx + 1; i < *cur_list_count; i++) { if (pf_sym_list->pf_sym[i].count <= pf_sym_list->pf_sym[sym_idx].count) { continue; } tmp_pf_sym.symbol = pf_sym_list->pf_sym[sym_idx].symbol; tmp_pf_sym.count = pf_sym_list->pf_sym[sym_idx].count; pf_sym_list->pf_sym[sym_idx].symbol = pf_sym_list->pf_sym[i].symbol; pf_sym_list->pf_sym[sym_idx].count = pf_sym_list->pf_sym[i].count; pf_sym_list->pf_sym[i].symbol = tmp_pf_sym.symbol; pf_sym_list->pf_sym[i].count = tmp_pf_sym.count; } } return QDF_STATUS_SUCCESS; } static void wma_wow_pagefault_handle_ssr_action(tp_wma_handle wma) { QDF_STATUS status; uint8_t *cur_count, pf_thresh; qdf_time_t cur_time, cutoff_time; uint32_t pf_wakeup_intv; struct wma_pf_sym *pf_sym_entry; cur_time = qdf_get_system_uptime(); pf_wakeup_intv = wlan_pmo_get_interval_for_pagefault_wakeup_counts(wma->psoc); pf_thresh = wlan_pmo_get_min_pagefault_wakeups_for_action(wma->psoc); cutoff_time = cur_time - qdf_system_msecs_to_ticks(pf_wakeup_intv); pf_sym_entry = &wma->wma_pf_hist.wma_pf_sym[0]; cur_count = &pf_sym_entry->pf_sym.count; if (cutoff_time < cur_time) { wma_wow_pagefault_flush_ageout_entries(pf_sym_entry, cutoff_time); } pf_sym_entry->pf_ev_ts[(*cur_count)++] = cur_time; if (*cur_count < pf_thresh) return; /* If SSR threshold condition fails, SSR will not be triggered, so * save current event and flush oldest entry. */ status = wma_wow_pagefault_action_cb(NULL); if (QDF_IS_STATUS_ERROR(status)) { (*cur_count)--; qdf_mem_copy(&pf_sym_entry->pf_ev_ts[0], &pf_sym_entry->pf_ev_ts[1], (*cur_count * sizeof(qdf_time_t))); qdf_mem_zero(&pf_sym_entry->pf_ev_ts[*cur_count], sizeof(qdf_time_t)); } } static void wma_wow_wakeup_pagefault_notify(tp_wma_handle wma, void *ev, uint32_t ev_len) { QDF_STATUS status; uint32_t pf_wakeup_intv; qdf_time_t cur_time, cutoff_time; struct wma_pf_sym *pf_sym_entry; struct wma_pf_sym_hist *pf_sym_hist = &wma->wma_pf_hist; struct wlan_objmgr_psoc *psoc = wma->psoc; uint8_t pf_tbl_idx; struct wow_pf_wakeup_ev_data pf_sym_list = {0}; struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); if (!mac) { wma_debug("MAC context NULL"); return; } if (wlan_pmo_no_op_on_page_fault(psoc)) return; if (wmi_get_runtime_pm_inprogress(wma->wmi_handle)) { wma_debug("Ignore run time pm wakeup"); return; } if (!mac->sme.pagefault_action_cb) { wma_debug("NULL pagefault action cb"); return; } if (wlan_pmo_enable_ssr_on_page_fault(psoc)) { wma_wow_pagefault_handle_ssr_action(wma); return; } cur_time = qdf_get_system_uptime(); pf_wakeup_intv = wlan_pmo_get_interval_for_pagefault_wakeup_counts(psoc); cutoff_time = cur_time - qdf_system_msecs_to_ticks(pf_wakeup_intv); status = wma_wow_pagefault_parse_event(psoc, ev, ev_len, &pf_sym_list); if (QDF_IS_STATUS_ERROR(status)) { wma_debug("Failed during page fault payload parse"); return; } qdf_spinlock_acquire(&pf_sym_hist->lock); for (pf_tbl_idx = 0; pf_tbl_idx < WLAN_WMA_MAX_PF_SYM; pf_tbl_idx++) { pf_sym_entry = &pf_sym_hist->wma_pf_sym[pf_tbl_idx]; if (cutoff_time < cur_time) { wma_wow_pagefault_flush_ageout_entries(pf_sym_entry, cutoff_time); } wma_wow_pagefault_process_existing_syms(wma, pf_sym_entry, &pf_sym_list, cur_time); if (!pf_sym_list.pending_pf_syms) goto send_event; } /* Process if any new symbols are present in the event */ wma_wow_pagefault_add_new_sym_from_event(wma, &pf_sym_list, cur_time); send_event: if (pf_sym_hist->pf_notify_buf_len) { wma_wow_pagefault_action_cb(pf_sym_hist->pf_notify_buf_ptr); pf_sym_hist->pf_notify_buf_len = 0; } qdf_spinlock_release(&pf_sym_hist->lock); qdf_mem_free(pf_sym_list.pf_sym); pf_sym_list.pf_sym = NULL; } /** * wma_wow_wakeup_host_event() - wakeup host event handler * @handle: wma handle * @event: event data * @len: buffer length * * Handler to catch wow wakeup host event. This event will have * reason why the firmware has woken the host. * * Return: Errno */ int wma_wow_wakeup_host_event(void *handle, uint8_t *event, uint32_t len) { int errno; t_wma_handle *wma = handle; WMI_WOW_WAKEUP_HOST_EVENTID_param_tlvs *event_param; WOW_EVENT_INFO_fixed_param *wake_info; event_param = (WMI_WOW_WAKEUP_HOST_EVENTID_param_tlvs *)event; if (!event_param) { wma_err("Wake event data is null"); return -EINVAL; } wake_info = event_param->fixed_param; if (wake_info->vdev_id >= wma->max_bssid) { wma_err("received invalid vdev_id %d", wake_info->vdev_id); return -EINVAL; } wma_wake_event_log_reason(wma, wake_info); if (wake_info->wake_reason == WOW_REASON_PAGE_FAULT) wma_wow_wakeup_pagefault_notify(wma, event, len); if (wake_info->wake_reason == WOW_REASON_LOCAL_DATA_UC_DROP) hif_rtpm_set_autosuspend_delay(WOW_LARGE_RX_RTPM_DELAY); ucfg_pmo_psoc_wakeup_host_event_received(wma->psoc); wma_print_wow_stats(wma, wake_info); /* split based on payload type */ if (is_piggybacked_event(wake_info->wake_reason)) errno = wma_wake_event_piggybacked(wma, event_param, len); else if (event_param->wow_packet_buffer) errno = wma_wake_event_packet(wma, event_param, len); else errno = wma_wake_event_no_payload(wma, event_param, len); wma_inc_wow_stats(wma, wake_info); wma_print_wow_stats(wma, wake_info); wma_acquire_wow_wakelock(wma, wake_info->wake_reason); return errno; } #ifdef FEATURE_WLAN_D0WOW /** * wma_d0_wow_disable_ack_event() - wakeup host event handler * @handle: wma handle * @event: event data * @len: buffer length * * Handler to catch D0-WOW disable ACK event. This event will have * reason why the firmware has woken the host. * This is for backward compatible with cld2.0. * * Return: 0 for success or error */ int wma_d0_wow_disable_ack_event(void *handle, uint8_t *event, uint32_t len) { tp_wma_handle wma = (tp_wma_handle)handle; WMI_D0_WOW_DISABLE_ACK_EVENTID_param_tlvs *param_buf; wmi_d0_wow_disable_ack_event_fixed_param *resp_data; param_buf = (WMI_D0_WOW_DISABLE_ACK_EVENTID_param_tlvs *)event; if (!param_buf) { wma_err("Invalid D0-WOW disable ACK event buffer!"); return -EINVAL; } resp_data = param_buf->fixed_param; ucfg_pmo_psoc_wakeup_host_event_received(wma->psoc); wma_debug("Received D0-WOW disable ACK"); return 0; } #else int wma_d0_wow_disable_ack_event(void *handle, uint8_t *event, uint32_t len) { return 0; } #endif /** * wma_pdev_resume_event_handler() - PDEV resume event handler * @handle: wma handle * @event: event data * @len: buffer length * * Return: 0 for success or error */ int wma_pdev_resume_event_handler(void *handle, uint8_t *event, uint32_t len) { tp_wma_handle wma = (tp_wma_handle) handle; wma_nofl_info("Received PDEV resume event"); ucfg_pmo_psoc_wakeup_host_event_received(wma->psoc); return 0; } /** * wma_del_ts_req() - send DELTS request to fw * @wma: wma handle * @msg: delts params * * Return: none */ void wma_del_ts_req(tp_wma_handle wma, struct del_ts_params *msg) { if (!wma_is_vdev_valid(msg->sessionId)) { wma_err("vdev id:%d is not active ", msg->sessionId); qdf_mem_free(msg); return; } if (wmi_unified_del_ts_cmd(wma->wmi_handle, msg->sessionId, TID_TO_WME_AC(msg->userPrio))) { wma_alert("Failed to send vdev DELTS command"); } #ifdef WLAN_FEATURE_ROAM_OFFLOAD if (msg->setRICparams == true) wma_set_ric_req(wma, msg, false); #endif /* WLAN_FEATURE_ROAM_OFFLOAD */ qdf_mem_free(msg); } void wma_aggr_qos_req(tp_wma_handle wma, struct aggr_add_ts_param *aggr_qos_rsp_msg) { if (!wma_is_vdev_valid(aggr_qos_rsp_msg->vdev_id)) { wma_err("vdev id:%d is not active ", aggr_qos_rsp_msg->vdev_id); return; } wmi_unified_aggr_qos_cmd(wma->wmi_handle, aggr_qos_rsp_msg); /* send response to upper layers from here only. */ wma_send_msg_high_priority(wma, WMA_AGGR_QOS_RSP, aggr_qos_rsp_msg, 0); } #ifdef FEATURE_WLAN_ESE /** * wma_set_tsm_interval() - Set TSM interval * @req: pointer to ADDTS request * * Return: QDF_STATUS_E_FAILURE or QDF_STATUS_SUCCESS */ static QDF_STATUS wma_set_tsm_interval(struct add_ts_param *req) { /* * msmt_interval is in unit called TU (1 TU = 1024 us) * max value of msmt_interval cannot make resulting * interval_milliseconds overflow 32 bit * */ uint32_t interval_milliseconds; interval_milliseconds = (req->tsm_interval * 1024) / 1000; cdp_tx_set_compute_interval(cds_get_context(QDF_MODULE_ID_SOC), WMI_PDEV_ID_SOC, interval_milliseconds); return QDF_STATUS_SUCCESS; } #else static inline QDF_STATUS wma_set_tsm_interval(struct add_ts_param *req) { return QDF_STATUS_SUCCESS; } #endif /* FEATURE_WLAN_ESE */ /** * wma_add_ts_req() - send ADDTS request to fw * @wma: wma handle * @msg: ADDTS params * * Return: none */ void wma_add_ts_req(tp_wma_handle wma, struct add_ts_param *msg) { struct add_ts_param cmd = {0}; msg->status = QDF_STATUS_SUCCESS; if (wma_set_tsm_interval(msg) == QDF_STATUS_SUCCESS) { cmd.vdev_id = msg->vdev_id; cmd.tspec.tsinfo.traffic.userPrio = TID_TO_WME_AC(msg->tspec.tsinfo.traffic.userPrio); cmd.tspec.mediumTime = msg->tspec.mediumTime; if (wmi_unified_add_ts_cmd(wma->wmi_handle, &cmd)) msg->status = QDF_STATUS_E_FAILURE; #ifdef WLAN_FEATURE_ROAM_OFFLOAD if (msg->set_ric_params) wma_set_ric_req(wma, msg, true); #endif /* WLAN_FEATURE_ROAM_OFFLOAD */ } wma_send_msg_high_priority(wma, WMA_ADD_TS_RSP, msg, 0); } #ifdef FEATURE_WLAN_ESE #define TSM_DELAY_HISTROGRAM_BINS 4 /** * wma_process_tsm_stats_req() - process tsm stats request * @wma_handler - handle to wma * @pTsmStatsMsg - TSM stats struct that needs to be populated and * passed in message. * * A parallel function to WMA_ProcessTsmStatsReq for pronto. This * function fetches stats from data path APIs and post * WMA_TSM_STATS_RSP msg back to LIM. * * Return: QDF status */ QDF_STATUS wma_process_tsm_stats_req(tp_wma_handle wma_handler, void *pTsmStatsMsg) { uint8_t counter; uint32_t queue_delay_microsec = 0; uint32_t tx_delay_microsec = 0; uint16_t packet_count = 0; uint16_t packet_loss_count = 0; tpAniTrafStrmMetrics pTsmMetric = NULL; tpAniGetTsmStatsReq pStats = (tpAniGetTsmStatsReq) pTsmStatsMsg; tpAniGetTsmStatsRsp pTsmRspParams = NULL; int tid = pStats->tid; /* * The number of histrogram bin report by data path api are different * than required by TSM, hence different (6) size array used */ uint16_t bin_values[QCA_TX_DELAY_HIST_REPORT_BINS] = { 0, }; void *soc = cds_get_context(QDF_MODULE_ID_SOC); /* get required values from data path APIs */ cdp_tx_delay(soc, WMI_PDEV_ID_SOC, &queue_delay_microsec, &tx_delay_microsec, tid); cdp_tx_delay_hist(soc, WMI_PDEV_ID_SOC, bin_values, tid); cdp_tx_packet_count(soc, WMI_PDEV_ID_SOC, &packet_count, &packet_loss_count, tid); pTsmRspParams = qdf_mem_malloc(sizeof(*pTsmRspParams)); if (!pTsmRspParams) { QDF_ASSERT(0); qdf_mem_free(pTsmStatsMsg); return QDF_STATUS_E_NOMEM; } qdf_copy_macaddr(&pTsmRspParams->bssid, &pStats->bssId); pTsmRspParams->rc = QDF_STATUS_E_FAILURE; pTsmRspParams->tsmStatsReq = pStats; pTsmMetric = &pTsmRspParams->tsmMetrics; /* populate pTsmMetric */ pTsmMetric->UplinkPktQueueDly = queue_delay_microsec; /* store only required number of bin values */ for (counter = 0; counter < TSM_DELAY_HISTROGRAM_BINS; counter++) { pTsmMetric->UplinkPktQueueDlyHist[counter] = bin_values[counter]; } pTsmMetric->UplinkPktTxDly = tx_delay_microsec; pTsmMetric->UplinkPktLoss = packet_loss_count; pTsmMetric->UplinkPktCount = packet_count; /* * No need to populate roaming delay and roaming count as they are * being populated just before sending IAPP frame out */ /* post this message to LIM/PE */ wma_send_msg(wma_handler, WMA_TSM_STATS_RSP, (void *)pTsmRspParams, 0); return QDF_STATUS_SUCCESS; } #endif /* FEATURE_WLAN_ESE */ /** * wma_process_mcbc_set_filter_req() - process mcbc set filter request * @wma_handle: wma handle * @mcbc_param: mcbc params * * Return: QDF status */ QDF_STATUS wma_process_mcbc_set_filter_req(tp_wma_handle wma_handle, tSirRcvFltMcAddrList *mcbc_param) { return QDF_STATUS_SUCCESS; } /** * wma_process_add_periodic_tx_ptrn_ind() - add periodic tx pattern * @handle: wma handle * @pattern: tx pattern params * * Return: QDF status */ QDF_STATUS wma_process_add_periodic_tx_ptrn_ind(WMA_HANDLE handle, tSirAddPeriodicTxPtrn *pattern) { tp_wma_handle wma_handle = (tp_wma_handle) handle; struct periodic_tx_pattern *params_ptr; uint8_t vdev_id; QDF_STATUS status; struct wmi_unified *wmi_handle; if (wma_validate_handle(wma_handle)) return QDF_STATUS_E_INVAL; wmi_handle = wma_handle->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; if (wma_find_vdev_id_by_addr(wma_handle, pattern->mac_address.bytes, &vdev_id)) { wma_err("Failed to find vdev id for "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(pattern->mac_address.bytes)); return QDF_STATUS_E_INVAL; } params_ptr = qdf_mem_malloc(sizeof(*params_ptr)); if (!params_ptr) return QDF_STATUS_E_NOMEM; params_ptr->ucPtrnId = pattern->ucPtrnId; params_ptr->ucPtrnSize = pattern->ucPtrnSize; params_ptr->usPtrnIntervalMs = pattern->usPtrnIntervalMs; qdf_mem_copy(¶ms_ptr->mac_address, &pattern->mac_address, sizeof(struct qdf_mac_addr)); qdf_mem_copy(params_ptr->ucPattern, pattern->ucPattern, params_ptr->ucPtrnSize); status = wmi_unified_process_add_periodic_tx_ptrn_cmd( wmi_handle, params_ptr, vdev_id); qdf_mem_free(params_ptr); return status; } /** * wma_process_del_periodic_tx_ptrn_ind - del periodic tx ptrn * @handle: wma handle * @pDelPeriodicTxPtrnParams: tx ptrn params * * Return: QDF status */ QDF_STATUS wma_process_del_periodic_tx_ptrn_ind(WMA_HANDLE handle, tSirDelPeriodicTxPtrn * pDelPeriodicTxPtrnParams) { tp_wma_handle wma_handle = (tp_wma_handle) handle; uint8_t vdev_id; struct wmi_unified *wmi_handle; if (wma_validate_handle(wma_handle)) return QDF_STATUS_E_INVAL; wmi_handle = wma_handle->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; if (wma_find_vdev_id_by_addr( wma_handle, pDelPeriodicTxPtrnParams->mac_address.bytes, &vdev_id)) { wma_err("Failed to find vdev id for "QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(pDelPeriodicTxPtrnParams->mac_address.bytes)); return QDF_STATUS_E_INVAL; } return wmi_unified_process_del_periodic_tx_ptrn_cmd( wmi_handle, vdev_id, pDelPeriodicTxPtrnParams->ucPtrnId); } #ifdef WLAN_FEATURE_STATS_EXT #ifdef WLAN_FEATURE_11BE_MLO /* * wma_stats_ext_req_vdev_id_bitmap() -API to calculate connected links bitmap * in case of MLO. * psoc: psoc common object * vdev_id: vdev_id for the stats ext request * bitmap: connected links bitmap * * Return None */ static void wma_stats_ext_req_vdev_id_bitmap(struct wlan_objmgr_psoc *psoc, uint32_t vdev_id, uint32_t *bitmap) { struct wlan_objmgr_vdev *vdev, *link_vdev; struct wlan_mlo_dev_context *mlo_dev_ctx; uint32_t i, connected_links_bitmap = 0; uint8_t connected_vdev_id; vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, WLAN_LEGACY_WMA_ID); if (!vdev) { wma_err("vdev object is NULL for vdev_id %d", vdev_id); return; } mlo_dev_ctx = vdev->mlo_dev_ctx; if (!mlo_dev_ctx) goto end; for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) { link_vdev = mlo_dev_ctx->wlan_vdev_list[i]; if (!link_vdev) continue; connected_vdev_id = wlan_vdev_get_id(link_vdev); if (wlan_cm_is_vdev_connected(link_vdev)) connected_links_bitmap |= BIT(connected_vdev_id); } wma_debug("mlo connected links bitmap[0x%x]", connected_links_bitmap); *bitmap = connected_links_bitmap; end: wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); } #else static void wma_stats_ext_req_vdev_id_bitmap(struct wlan_objmgr_psoc *psoc, uint32_t vdev_id, uint32_t *bitmap) { *bitmap = 0; } #endif QDF_STATUS wma_stats_ext_req(void *wma_ptr, tpStatsExtRequest preq) { tp_wma_handle wma = (tp_wma_handle) wma_ptr; struct stats_ext_params *params; size_t params_len; QDF_STATUS status; if (!wma) { wma_err("wma handle is NULL"); return QDF_STATUS_E_FAILURE; } params_len = sizeof(*params) + preq->request_data_len; params = qdf_mem_malloc(params_len); if (!params) return QDF_STATUS_E_NOMEM; params->vdev_id = preq->vdev_id; params->request_data_len = preq->request_data_len; if (preq->request_data_len > 0) qdf_mem_copy(params->request_data, preq->request_data, params->request_data_len); wma_stats_ext_req_vdev_id_bitmap(wma->psoc, params->vdev_id, ¶ms->vdev_id_bitmap); status = wmi_unified_stats_ext_req_cmd(wma->wmi_handle, params); qdf_mem_free(params); return status; } #endif /* WLAN_FEATURE_STATS_EXT */ #ifdef WLAN_FEATURE_EXTWOW_SUPPORT /** * wma_send_status_of_ext_wow() - send ext wow status to SME * @wma: wma handle * @status: status * * Return: none */ static void wma_send_status_of_ext_wow(tp_wma_handle wma, bool status) { tSirReadyToExtWoWInd *ready_to_extwow; QDF_STATUS vstatus; struct scheduler_msg message = {0}; uint8_t len; wma_debug("Posting ready to suspend indication to umac"); len = sizeof(tSirReadyToExtWoWInd); ready_to_extwow = qdf_mem_malloc(len); if (!ready_to_extwow) return; ready_to_extwow->mesgType = eWNI_SME_READY_TO_EXTWOW_IND; ready_to_extwow->mesgLen = len; ready_to_extwow->status = status; message.type = eWNI_SME_READY_TO_EXTWOW_IND; message.bodyptr = (void *)ready_to_extwow; message.bodyval = 0; vstatus = scheduler_post_message(QDF_MODULE_ID_WMA, QDF_MODULE_ID_SME, QDF_MODULE_ID_SME, &message); if (vstatus != QDF_STATUS_SUCCESS) qdf_mem_free(ready_to_extwow); } /** * wma_enable_ext_wow() - enable ext wow in fw * @wma: wma handle * @params: ext wow params * * Return:0 for success or error code */ QDF_STATUS wma_enable_ext_wow(tp_wma_handle wma, tpSirExtWoWParams params) { struct ext_wow_params wow_params = {0}; QDF_STATUS status; if (!wma) { wma_err("wma handle is NULL"); return QDF_STATUS_E_FAILURE; } wow_params.vdev_id = params->vdev_id; wow_params.type = (enum wmi_ext_wow_type) params->type; wow_params.wakeup_pin_num = params->wakeup_pin_num; status = wmi_unified_enable_ext_wow_cmd(wma->wmi_handle, &wow_params); if (QDF_IS_STATUS_ERROR(status)) return status; wma_send_status_of_ext_wow(wma, true); return status; } /** * wma_set_app_type1_params_in_fw() - set app type1 params in fw * @wma: wma handle * @appType1Params: app type1 params * * Return: QDF status */ int wma_set_app_type1_params_in_fw(tp_wma_handle wma, tpSirAppType1Params appType1Params) { int ret; ret = wmi_unified_app_type1_params_in_fw_cmd(wma->wmi_handle, (struct app_type1_params *)appType1Params); if (ret) { wma_err("Failed to set APP TYPE1 PARAMS"); return QDF_STATUS_E_FAILURE; } return QDF_STATUS_SUCCESS; } /** * wma_set_app_type2_params_in_fw() - set app type2 params in fw * @wma: wma handle * @appType2Params: app type2 params * * Return: QDF status */ QDF_STATUS wma_set_app_type2_params_in_fw(tp_wma_handle wma, tpSirAppType2Params appType2Params) { struct app_type2_params params = {0}; if (!wma) { wma_err("wma handle is NULL"); return QDF_STATUS_E_FAILURE; } params.vdev_id = appType2Params->vdev_id; params.rc4_key_len = appType2Params->rc4_key_len; qdf_mem_copy(params.rc4_key, appType2Params->rc4_key, 16); params.ip_id = appType2Params->ip_id; params.ip_device_ip = appType2Params->ip_device_ip; params.ip_server_ip = appType2Params->ip_server_ip; params.tcp_src_port = appType2Params->tcp_src_port; params.tcp_dst_port = appType2Params->tcp_dst_port; params.tcp_seq = appType2Params->tcp_seq; params.tcp_ack_seq = appType2Params->tcp_ack_seq; params.keepalive_init = appType2Params->keepalive_init; params.keepalive_min = appType2Params->keepalive_min; params.keepalive_max = appType2Params->keepalive_max; params.keepalive_inc = appType2Params->keepalive_inc; params.tcp_tx_timeout_val = appType2Params->tcp_tx_timeout_val; params.tcp_rx_timeout_val = appType2Params->tcp_rx_timeout_val; qdf_mem_copy(¶ms.gateway_mac, &appType2Params->gateway_mac, sizeof(struct qdf_mac_addr)); return wmi_unified_set_app_type2_params_in_fw_cmd(wma->wmi_handle, ¶ms); } #endif /* WLAN_FEATURE_EXTWOW_SUPPORT */ #ifdef FEATURE_WLAN_AUTO_SHUTDOWN /** * wma_auto_shutdown_event_handler() - process auto shutdown timer trigger * @handle: wma handle * @event: event buffer * @len: buffer length * * Return: 0 for success or error code */ int wma_auto_shutdown_event_handler(void *handle, uint8_t *event, uint32_t len) { wmi_host_auto_shutdown_event_fixed_param *wmi_auto_sh_evt; WMI_HOST_AUTO_SHUTDOWN_EVENTID_param_tlvs *param_buf = (WMI_HOST_AUTO_SHUTDOWN_EVENTID_param_tlvs *) event; if (!param_buf || !param_buf->fixed_param) { wma_err("Invalid Auto shutdown timer evt"); return -EINVAL; } wmi_auto_sh_evt = param_buf->fixed_param; if (wmi_auto_sh_evt->shutdown_reason != WMI_HOST_AUTO_SHUTDOWN_REASON_TIMER_EXPIRY) { wma_err("Invalid Auto shutdown timer evt"); return -EINVAL; } wma_debug("Auto Shutdown Evt: %d", wmi_auto_sh_evt->shutdown_reason); return wma_wake_reason_auto_shutdown(); } QDF_STATUS wma_set_auto_shutdown_timer_req(tp_wma_handle wma_handle, struct auto_shutdown_cmd *auto_sh_cmd) { if (!auto_sh_cmd) { wma_err("Invalid Autoshutdown cfg cmd"); return QDF_STATUS_E_FAILURE; } return wmi_unified_set_auto_shutdown_timer_cmd(wma_handle->wmi_handle, auto_sh_cmd->timer_val); } #endif /* FEATURE_WLAN_AUTO_SHUTDOWN */ #ifdef DHCP_SERVER_OFFLOAD QDF_STATUS wma_process_dhcpserver_offload(tp_wma_handle wma_handle, struct dhcp_offload_info_params *params) { QDF_STATUS status; wmi_unified_t wmi_handle; if (!wma_handle) { wma_err("wma handle is NULL"); return QDF_STATUS_E_FAILURE; } wmi_handle = wma_handle->wmi_handle; status = wmi_unified_process_dhcpserver_offload_cmd(wmi_handle, params); wma_debug("Set dhcp server offload to vdev %d status %d", params->vdev_id, status); return status; } #endif /* DHCP_SERVER_OFFLOAD */ #ifdef WLAN_FEATURE_GPIO_LED_FLASHING /** * wma_set_led_flashing() - set led flashing in fw * @wma_handle: wma handle * @flashing: flashing request * * Return: QDF status */ QDF_STATUS wma_set_led_flashing(tp_wma_handle wma_handle, struct flashing_req_params *flashing) { QDF_STATUS status; struct wmi_unified *wmi_handle; if (wma_validate_handle(wma_handle)) return QDF_STATUS_E_INVAL; wmi_handle = wma_handle->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; if (!flashing) { wma_err("invalid parameter: flashing"); return QDF_STATUS_E_INVAL; } status = wmi_unified_set_led_flashing_cmd(wmi_handle, flashing); return status; } #endif /* WLAN_FEATURE_GPIO_LED_FLASHING */ int wma_sar_rsp_evt_handler(ol_scn_t handle, uint8_t *event, uint32_t len) { tp_wma_handle wma_handle; wmi_unified_t wmi_handle; QDF_STATUS status; wma_debug("handle:%pK event:%pK len:%u", handle, event, len); wma_handle = handle; if (wma_validate_handle(wma_handle)) return QDF_STATUS_E_INVAL; wmi_handle = wma_handle->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; status = wmi_unified_extract_sar2_result_event(wmi_handle, event, len); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Event extract failure: %d", status); return -EINVAL; } return 0; } #ifdef FEATURE_WLAN_CH_AVOID /** * wma_process_ch_avoid_update_req() - handles channel avoid update request * @wma_handle: wma handle * @ch_avoid_update_req: channel avoid update params * * Return: QDF status */ QDF_STATUS wma_process_ch_avoid_update_req(tp_wma_handle wma_handle, tSirChAvoidUpdateReq * ch_avoid_update_req) { QDF_STATUS status; struct wmi_unified *wmi_handle; if (wma_validate_handle(wma_handle)) return QDF_STATUS_E_FAILURE; wmi_handle = wma_handle->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_FAILURE; if (!ch_avoid_update_req) { wma_err("ch_avoid_update_req is NULL"); return QDF_STATUS_E_FAILURE; } wma_debug("WMA --> WMI_CHAN_AVOID_UPDATE"); status = wmi_unified_process_ch_avoid_update_cmd(wmi_handle); if (QDF_IS_STATUS_ERROR(status)) return status; wma_debug("WMA --> WMI_CHAN_AVOID_UPDATE sent through WMI"); return status; } #endif void wma_send_regdomain_info_to_fw(uint32_t reg_dmn, uint16_t regdmn2G, uint16_t regdmn5G, uint8_t ctl2G, uint8_t ctl5G) { tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); int32_t cck_mask_val = 0; struct pdev_params pdev_param = {0}; QDF_STATUS ret = QDF_STATUS_SUCCESS; QDF_STATUS status = QDF_STATUS_SUCCESS; wma_debug("reg_dmn: %d regdmn2g: %d regdmn5g :%d ctl2g: %d ctl5g: %d", reg_dmn, regdmn2G, regdmn5G, ctl2G, ctl5G); if (!wma) return; status = wmi_unified_send_regdomain_info_to_fw_cmd(wma->wmi_handle, reg_dmn, regdmn2G, regdmn5G, ctl2G, ctl5G); if (status == QDF_STATUS_E_NOMEM) return; if ((((reg_dmn & ~CTRY_FLAG) == CTRY_JAPAN15) || ((reg_dmn & ~CTRY_FLAG) == CTRY_KOREA_ROC)) && (true == wma->tx_chain_mask_cck)) cck_mask_val = 1; cck_mask_val |= (wma->self_gen_frm_pwr << 16); pdev_param.param_id = wmi_pdev_param_tx_chain_mask_cck; pdev_param.param_value = cck_mask_val; ret = wmi_unified_pdev_param_send(wma->wmi_handle, &pdev_param, WMA_WILDCARD_PDEV_ID); if (QDF_IS_STATUS_ERROR(ret)) wma_err("failed to set PDEV tx_chain_mask_cck %d", ret); } #ifdef FEATURE_WLAN_TDLS /** * wma_tdls_event_handler() - handle TDLS event * @handle: wma handle * @event: event buffer * @len: buffer length * * Return: 0 for success or error code */ int wma_tdls_event_handler(void *handle, uint8_t *event, uint32_t len) { /* TODO update with target rx ops */ return 0; } /** * wma_update_tdls_peer_state() - update TDLS peer state * @handle: wma handle * @peer_state: TDLS peer state params * * Return: 0 for success or error code */ int wma_update_tdls_peer_state(WMA_HANDLE handle, struct tdls_peer_update_state *peer_state) { tp_wma_handle wma_handle = (tp_wma_handle) handle; uint32_t i; void *soc = cds_get_context(QDF_MODULE_ID_SOC); struct tdls_peer_params *peer_cap; int ret = 0; uint32_t *ch_mhz = NULL; size_t ch_mhz_len; bool restore_last_peer = false; QDF_STATUS qdf_status; struct wmi_unified *wmi_handle; if (wma_validate_handle(wma_handle)) { ret = -EINVAL; goto end_tdls_peer_state; } wmi_handle = wma_handle->wmi_handle; if (wmi_validate_handle(wmi_handle)) { ret = -EINVAL; goto end_tdls_peer_state; } if (!soc) { ret = -EINVAL; goto end_tdls_peer_state; } if (wlan_cm_is_roam_sync_in_progress(wma_handle->psoc, peer_state->vdev_id)) { wma_err("roaming in progress, reject peer update cmd!"); ret = -EPERM; goto end_tdls_peer_state; } if (!wma_objmgr_peer_exist(wma_handle, peer_state->peer_macaddr, NULL)) { wma_err("peer:" QDF_MAC_ADDR_FMT "doesn't exist", QDF_MAC_ADDR_REF(peer_state->peer_macaddr)); ret = -EINVAL; goto end_tdls_peer_state; } peer_cap = &peer_state->peer_cap; /* peer capability info is valid only when peer state is connected */ if (TDLS_PEER_STATE_CONNECTED != peer_state->peer_state) qdf_mem_zero(peer_cap, sizeof(*peer_cap)); if (peer_cap->peer_chanlen) { ch_mhz_len = sizeof(*ch_mhz) * peer_cap->peer_chanlen; ch_mhz = qdf_mem_malloc(ch_mhz_len); if (!ch_mhz) { ret = -ENOMEM; goto end_tdls_peer_state; } for (i = 0; i < peer_cap->peer_chanlen; ++i) ch_mhz[i] = peer_cap->peer_chan[i].ch_freq; } cdp_peer_set_tdls_offchan_enabled(soc, peer_state->vdev_id, peer_state->peer_macaddr, !!peer_cap->peer_off_chan_support); if (wmi_unified_update_tdls_peer_state_cmd(wma_handle->wmi_handle, peer_state, ch_mhz)) { wma_err("failed to send tdls peer update state command"); ret = -EIO; /* Fall through to delete TDLS peer for teardown */ } /* in case of teardown, remove peer from fw */ if (TDLS_PEER_STATE_TEARDOWN == peer_state->peer_state) { restore_last_peer = cdp_peer_is_vdev_restore_last_peer( soc, peer_state->vdev_id, peer_state->peer_macaddr); wma_debug("calling wma_remove_peer for peer " QDF_MAC_ADDR_FMT " vdevId: %d", QDF_MAC_ADDR_REF(peer_state->peer_macaddr), peer_state->vdev_id); qdf_status = wma_remove_peer(wma_handle, peer_state->peer_macaddr, peer_state->vdev_id, false); if (QDF_IS_STATUS_ERROR(qdf_status)) { wma_err("wma_remove_peer failed"); ret = -EINVAL; } cdp_peer_update_last_real_peer(soc, WMI_PDEV_ID_SOC, peer_state->vdev_id, restore_last_peer); } if (TDLS_PEER_STATE_CONNECTED == peer_state->peer_state) { cdp_peer_state_update(soc, peer_state->peer_macaddr, OL_TXRX_PEER_STATE_AUTH); } end_tdls_peer_state: if (ch_mhz) qdf_mem_free(ch_mhz); if (peer_state) qdf_mem_free(peer_state); return ret; } #endif /* FEATURE_WLAN_TDLS */ /* * wma_process_cfg_action_frm_tb_ppdu() - action frame TB PPDU cfg to firmware * @wma: Pointer to WMA handle * @cfg_info: Pointer for cfg info * * Return: QDF_STATUS_SUCCESS for success otherwise failure * */ QDF_STATUS wma_process_cfg_action_frm_tb_ppdu(tp_wma_handle wma, struct cfg_action_frm_tb_ppdu *cfg_info) { struct cfg_action_frm_tb_ppdu_param cmd = {0}; if (wma_validate_handle(wma)) return QDF_STATUS_E_FAILURE; cmd.frm_len = cfg_info->frm_len; cmd.cfg = cfg_info->cfg; cmd.data = cfg_info->data; wma_debug("cfg: %d, frm_len: %d", cfg_info->cfg, cfg_info->frm_len); return wmi_unified_cfg_action_frm_tb_ppdu_cmd(wma->wmi_handle, &cmd); } /* * wma_process_set_ie_info() - Function to send IE info to firmware * @wma: Pointer to WMA handle * @ie_data: Pointer for ie data * * This function sends IE information to firmware * * Return: QDF_STATUS_SUCCESS for success otherwise failure * */ QDF_STATUS wma_process_set_ie_info(tp_wma_handle wma, struct vdev_ie_info *ie_info) { struct wma_txrx_node *interface; struct vdev_ie_info_param cmd = {0}; if (!ie_info || !wma) { wma_err("input pointer is NULL"); return QDF_STATUS_E_FAILURE; } /* Validate the input */ if (ie_info->length <= 0) { wma_err("Invalid IE length"); return QDF_STATUS_E_INVAL; } if (!wma_is_vdev_valid(ie_info->vdev_id)) { wma_err("vdev_id: %d is not active", ie_info->vdev_id); return QDF_STATUS_E_INVAL; } interface = &wma->interfaces[ie_info->vdev_id]; cmd.vdev_id = ie_info->vdev_id; cmd.ie_id = ie_info->ie_id; cmd.length = ie_info->length; cmd.band = ie_info->band; cmd.data = ie_info->data; cmd.ie_source = WMA_SET_VDEV_IE_SOURCE_HOST; wma_debug("vdev id: %d, ie_id: %d, band: %d, len: %d", ie_info->vdev_id, ie_info->ie_id, ie_info->band, ie_info->length); QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_DEBUG, ie_info->data, ie_info->length); return wmi_unified_process_set_ie_info_cmd(wma->wmi_handle, &cmd); } #ifdef FEATURE_WLAN_APF /** * wma_get_apf_caps_event_handler() - Event handler for get apf capability * @handle: WMA global handle * @cmd_param_info: command event data * @len: Length of @cmd_param_info * * Return: 0 on Success or Errno on failure */ int wma_get_apf_caps_event_handler(void *handle, u_int8_t *cmd_param_info, u_int32_t len) { WMI_BPF_CAPABILIY_INFO_EVENTID_param_tlvs *param_buf; wmi_bpf_capability_info_evt_fixed_param *event; struct sir_apf_get_offload *apf_get_offload; struct mac_context *pmac = (struct mac_context *)cds_get_context( QDF_MODULE_ID_PE); if (!pmac) return -EINVAL; if (!pmac->sme.apf_get_offload_cb) { wma_err("Callback not registered"); return -EINVAL; } param_buf = (WMI_BPF_CAPABILIY_INFO_EVENTID_param_tlvs *)cmd_param_info; event = param_buf->fixed_param; apf_get_offload = qdf_mem_malloc(sizeof(*apf_get_offload)); if (!apf_get_offload) return -ENOMEM; apf_get_offload->apf_version = event->bpf_version; apf_get_offload->max_apf_filters = event->max_bpf_filters; apf_get_offload->max_bytes_for_apf_inst = event->max_bytes_for_bpf_inst; wma_debug("APF capabilities version: %d max apf filter size: %d", apf_get_offload->apf_version, apf_get_offload->max_bytes_for_apf_inst); wma_debug("sending apf capabilities event to hdd"); pmac->sme.apf_get_offload_cb(pmac->sme.apf_get_offload_context, apf_get_offload); qdf_mem_free(apf_get_offload); return 0; } QDF_STATUS wma_get_apf_capabilities(tp_wma_handle wma) { QDF_STATUS status = QDF_STATUS_SUCCESS; wmi_bpf_get_capability_cmd_fixed_param *cmd; wmi_buf_t wmi_buf; uint32_t len; u_int8_t *buf_ptr; struct wmi_unified *wmi_handle; if (wma_validate_handle(wma)) return QDF_STATUS_E_INVAL; wmi_handle = wma->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; if (!wmi_service_enabled(wmi_handle, wmi_service_apf_offload)) { wma_err("APF cababilities feature bit not enabled"); return QDF_STATUS_E_FAILURE; } len = sizeof(*cmd); wmi_buf = wmi_buf_alloc(wmi_handle, len); if (!wmi_buf) return QDF_STATUS_E_NOMEM; buf_ptr = (u_int8_t *) wmi_buf_data(wmi_buf); cmd = (wmi_bpf_get_capability_cmd_fixed_param *) buf_ptr; WMITLV_SET_HDR(&cmd->tlv_header, WMITLV_TAG_STRUC_wmi_bpf_get_capability_cmd_fixed_param, WMITLV_GET_STRUCT_TLVLEN( wmi_bpf_get_capability_cmd_fixed_param)); if (wmi_unified_cmd_send(wmi_handle, wmi_buf, len, WMI_BPF_GET_CAPABILITY_CMDID)) { wmi_buf_free(wmi_buf); return QDF_STATUS_E_FAILURE; } return status; } QDF_STATUS wma_set_apf_instructions(tp_wma_handle wma, struct sir_apf_set_offload *apf_set_offload) { wmi_bpf_set_vdev_instructions_cmd_fixed_param *cmd; wmi_buf_t wmi_buf; uint32_t len = 0, len_aligned = 0; u_int8_t *buf_ptr; struct wmi_unified *wmi_handle; if (wma_validate_handle(wma)) return QDF_STATUS_E_INVAL; wmi_handle = wma->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; if (!wmi_service_enabled(wmi_handle, wmi_service_apf_offload)) { wma_err("APF offload feature Disabled"); return QDF_STATUS_E_NOSUPPORT; } if (!apf_set_offload) { wma_err("Invalid APF instruction request"); return QDF_STATUS_E_INVAL; } if (apf_set_offload->session_id >= wma->max_bssid) { wma_err("Invalid vdev_id: %d", apf_set_offload->session_id); return QDF_STATUS_E_INVAL; } if (!wma_is_vdev_up(apf_set_offload->session_id)) { wma_err("vdev %d is not up skipping APF offload", apf_set_offload->session_id); return QDF_STATUS_E_INVAL; } if (apf_set_offload->total_length) { len_aligned = roundup(apf_set_offload->current_length, sizeof(A_UINT32)); len = len_aligned + WMI_TLV_HDR_SIZE; } len += sizeof(*cmd); wmi_buf = wmi_buf_alloc(wmi_handle, len); if (!wmi_buf) return QDF_STATUS_E_NOMEM; buf_ptr = (u_int8_t *) wmi_buf_data(wmi_buf); cmd = (wmi_bpf_set_vdev_instructions_cmd_fixed_param *) buf_ptr; WMITLV_SET_HDR(&cmd->tlv_header, WMITLV_TAG_STRUC_wmi_bpf_set_vdev_instructions_cmd_fixed_param, WMITLV_GET_STRUCT_TLVLEN( wmi_bpf_set_vdev_instructions_cmd_fixed_param)); cmd->vdev_id = apf_set_offload->session_id; cmd->filter_id = apf_set_offload->filter_id; cmd->total_length = apf_set_offload->total_length; cmd->current_offset = apf_set_offload->current_offset; cmd->current_length = apf_set_offload->current_length; if (apf_set_offload->total_length) { buf_ptr += sizeof(wmi_bpf_set_vdev_instructions_cmd_fixed_param); WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_BYTE, len_aligned); buf_ptr += WMI_TLV_HDR_SIZE; qdf_mem_copy(buf_ptr, apf_set_offload->program, apf_set_offload->current_length); } if (wmi_unified_cmd_send(wmi_handle, wmi_buf, len, WMI_BPF_SET_VDEV_INSTRUCTIONS_CMDID)) { wmi_buf_free(wmi_buf); return QDF_STATUS_E_FAILURE; } wma_debug("APF offload enabled in fw"); return QDF_STATUS_SUCCESS; } QDF_STATUS wma_send_apf_enable_cmd(WMA_HANDLE handle, uint8_t vdev_id, bool apf_enable) { QDF_STATUS status = QDF_STATUS_SUCCESS; tp_wma_handle wma = (tp_wma_handle) handle; struct wmi_unified *wmi_handle; if (!wma_is_vdev_valid(vdev_id)) return QDF_STATUS_E_INVAL; if (wma_validate_handle(wma)) return QDF_STATUS_E_INVAL; wmi_handle = wma->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; if (!WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap, WMI_SERVICE_BPF_OFFLOAD)) { wma_err("APF cababilities feature bit not enabled"); return QDF_STATUS_E_FAILURE; } status = wmi_unified_send_apf_enable_cmd(wmi_handle, vdev_id, apf_enable); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to send apf enable/disable cmd"); return QDF_STATUS_E_FAILURE; } if (apf_enable) wma_debug("Sent APF Enable on vdevid: %d", vdev_id); else wma_debug("Sent APF Disable on vdevid: %d", vdev_id); return status; } QDF_STATUS wma_send_apf_write_work_memory_cmd(WMA_HANDLE handle, struct wmi_apf_write_memory_params *write_params) { QDF_STATUS status = QDF_STATUS_SUCCESS; tp_wma_handle wma = (tp_wma_handle) handle; struct wmi_unified *wmi_handle; if (wma_validate_handle(wma)) return QDF_STATUS_E_INVAL; wmi_handle = wma->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; if (!WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap, WMI_SERVICE_BPF_OFFLOAD)) { wma_err("APF cababilities feature bit not enabled"); return QDF_STATUS_E_FAILURE; } if (wmi_unified_send_apf_write_work_memory_cmd(wmi_handle, write_params)) { wma_err("Failed to send APF write mem command"); return QDF_STATUS_E_FAILURE; } wma_debug("Sent APF write mem on vdevid: %d", write_params->vdev_id); return status; } int wma_apf_read_work_memory_event_handler(void *handle, uint8_t *evt_buf, uint32_t len) { tp_wma_handle wma_handle; wmi_unified_t wmi_handle; struct wmi_apf_read_memory_resp_event_params evt_params = {0}; QDF_STATUS status; struct mac_context *pmac = cds_get_context(QDF_MODULE_ID_PE); wma_debug("handle:%pK event:%pK len:%u", handle, evt_buf, len); wma_handle = handle; if (wma_validate_handle(wma_handle)) return -EINVAL; wmi_handle = wma_handle->wmi_handle; if (wmi_validate_handle(wmi_handle)) return -EINVAL; if (!pmac) return -EINVAL; if (!pmac->sme.apf_read_mem_cb) { wma_err("Callback not registered"); return -EINVAL; } status = wmi_extract_apf_read_memory_resp_event(wmi_handle, evt_buf, &evt_params); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Event extract failure: %d", status); return -EINVAL; } pmac->sme.apf_read_mem_cb(pmac->hdd_handle, &evt_params); return 0; } QDF_STATUS wma_send_apf_read_work_memory_cmd(WMA_HANDLE handle, struct wmi_apf_read_memory_params *read_params) { QDF_STATUS status = QDF_STATUS_SUCCESS; tp_wma_handle wma = (tp_wma_handle) handle; struct wmi_unified *wmi_handle; if (wma_validate_handle(wma)) return QDF_STATUS_E_INVAL; wmi_handle = wma->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; if (!WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap, WMI_SERVICE_BPF_OFFLOAD)) { wma_err("APF cababilities feature bit not enabled"); return QDF_STATUS_E_FAILURE; } if (wmi_unified_send_apf_read_work_memory_cmd(wmi_handle, read_params)) { wma_err("Failed to send APF read memory command"); return QDF_STATUS_E_FAILURE; } wma_debug("Sent APF read memory on vdevid: %d", read_params->vdev_id); return status; } #endif /* FEATURE_WLAN_APF */ QDF_STATUS wma_set_tx_rx_aggr_size(uint8_t vdev_id, uint32_t tx_size, uint32_t rx_size, wmi_vdev_custom_aggr_type_t aggr_type) { tp_wma_handle wma_handle; struct wma_txrx_node *intr; wmi_vdev_set_custom_aggr_size_cmd_fixed_param *cmd; int32_t len; wmi_buf_t buf; u_int8_t *buf_ptr; int ret; wma_handle = cds_get_context(QDF_MODULE_ID_WMA); if (!wma_handle) return QDF_STATUS_E_INVAL; intr = wma_handle->interfaces; if (!intr) { wma_err("WMA interface is invalid!"); return QDF_STATUS_E_INVAL; } if (aggr_type == WMI_VDEV_CUSTOM_AGGR_TYPE_AMPDU) { intr[vdev_id].config.tx_ampdu = tx_size; intr[vdev_id].config.rx_ampdu = rx_size; } else { intr[vdev_id].config.tx_amsdu = tx_size; intr[vdev_id].config.rx_amsdu = rx_size; } len = sizeof(*cmd); buf = wmi_buf_alloc(wma_handle->wmi_handle, len); if (!buf) return QDF_STATUS_E_NOMEM; buf_ptr = (u_int8_t *) wmi_buf_data(buf); cmd = (wmi_vdev_set_custom_aggr_size_cmd_fixed_param *) buf_ptr; qdf_mem_zero(cmd, sizeof(*cmd)); WMITLV_SET_HDR(&cmd->tlv_header, WMITLV_TAG_STRUC_wmi_vdev_set_custom_aggr_size_cmd_fixed_param, WMITLV_GET_STRUCT_TLVLEN( wmi_vdev_set_custom_aggr_size_cmd_fixed_param)); if (wmi_service_enabled(wma_handle->wmi_handle, wmi_service_ampdu_tx_buf_size_256_support)) { cmd->enable_bitmap |= (0x1 << 6); } if ((tx_size > ADDBA_TXAGGR_SIZE_HELIUM) && (tx_size != ADDBA_TXAGGR_SIZE_LITHIUM) && (tx_size != ADDBA_TXAGGR_SIZE_512) && (tx_size != ADDBA_TXAGGR_SIZE_BERYLLIUM)) { wma_err("Invalid AMPDU Size"); return QDF_STATUS_E_INVAL; } cmd->vdev_id = vdev_id; cmd->tx_aggr_size = tx_size; cmd->rx_aggr_size = rx_size; /* bit 2 (aggr_type): TX Aggregation Type (0=A-MPDU, 1=A-MSDU) */ if (aggr_type == WMI_VDEV_CUSTOM_AGGR_TYPE_AMSDU) cmd->enable_bitmap |= 0x04; wma_debug("tx aggr: %d rx aggr: %d vdev: %d enable_bitmap %d", cmd->tx_aggr_size, cmd->rx_aggr_size, cmd->vdev_id, cmd->enable_bitmap); ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len, WMI_VDEV_SET_CUSTOM_AGGR_SIZE_CMDID); if (ret) { wmi_buf_free(buf); return QDF_STATUS_E_FAILURE; } return QDF_STATUS_SUCCESS; } QDF_STATUS wma_set_tx_rx_aggr_size_per_ac(WMA_HANDLE handle, uint8_t vdev_id, struct wlan_mlme_qos *qos_aggr, wmi_vdev_custom_aggr_type_t aggr_type) { wmi_vdev_set_custom_aggr_size_cmd_fixed_param *cmd; int32_t len; wmi_buf_t buf; u_int8_t *buf_ptr; int ret; int queue_num; uint32_t tx_aggr_size[4]; tp_wma_handle wma_handle = (tp_wma_handle)handle; if (wma_validate_handle(wma_handle)) return QDF_STATUS_E_INVAL; tx_aggr_size[0] = qos_aggr->tx_aggregation_size_be; tx_aggr_size[1] = qos_aggr->tx_aggregation_size_bk; tx_aggr_size[2] = qos_aggr->tx_aggregation_size_vi; tx_aggr_size[3] = qos_aggr->tx_aggregation_size_vo; for (queue_num = 0; queue_num < 4; queue_num++) { if (tx_aggr_size[queue_num] == 0) continue; len = sizeof(*cmd); buf = wmi_buf_alloc(wma_handle->wmi_handle, len); if (!buf) return QDF_STATUS_E_NOMEM; buf_ptr = (u_int8_t *)wmi_buf_data(buf); cmd = (wmi_vdev_set_custom_aggr_size_cmd_fixed_param *)buf_ptr; qdf_mem_zero(cmd, sizeof(*cmd)); WMITLV_SET_HDR(&cmd->tlv_header, WMITLV_TAG_STRUC_wmi_vdev_set_custom_aggr_size_cmd_fixed_param, WMITLV_GET_STRUCT_TLVLEN( wmi_vdev_set_custom_aggr_size_cmd_fixed_param)); cmd->vdev_id = vdev_id; cmd->rx_aggr_size = qos_aggr->rx_aggregation_size; cmd->tx_aggr_size = tx_aggr_size[queue_num]; if (wmi_service_enabled(wma_handle->wmi_handle, wmi_service_ampdu_tx_buf_size_256_support)) { cmd->enable_bitmap |= (0x1 << 6); } if ((tx_aggr_size[queue_num] != ADDBA_TXAGGR_SIZE_LITHIUM) && (tx_aggr_size[queue_num] > ADDBA_TXAGGR_SIZE_HELIUM)) { wma_err("Invalid AMPDU Size"); return QDF_STATUS_E_INVAL; } /* bit 5: tx_ac_enable, if set, ac bitmap is valid. */ cmd->enable_bitmap |= 0x20 | queue_num; /* bit 2 (aggr_type): TX Aggregation Type (0=A-MPDU, 1=A-MSDU) */ if (aggr_type == WMI_VDEV_CUSTOM_AGGR_TYPE_AMSDU) cmd->enable_bitmap |= 0x04; wma_debug("queue_num: %d, tx aggr: %d rx aggr: %d vdev: %d, bitmap: %d", queue_num, cmd->tx_aggr_size, cmd->rx_aggr_size, cmd->vdev_id, cmd->enable_bitmap); ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len, WMI_VDEV_SET_CUSTOM_AGGR_SIZE_CMDID); if (ret) { wmi_buf_free(buf); return QDF_STATUS_E_FAILURE; } } return QDF_STATUS_SUCCESS; } static QDF_STATUS wma_set_sw_retry_by_qos( tp_wma_handle handle, uint8_t vdev_id, wmi_vdev_custom_sw_retry_type_t retry_type, wmi_traffic_ac ac_type, uint32_t sw_retry) { wmi_vdev_set_custom_sw_retry_th_cmd_fixed_param *cmd; int32_t len; wmi_buf_t buf; u_int8_t *buf_ptr; int ret; len = sizeof(*cmd); buf = wmi_buf_alloc(handle->wmi_handle, len); if (!buf) return QDF_STATUS_E_NOMEM; buf_ptr = (u_int8_t *)wmi_buf_data(buf); cmd = (wmi_vdev_set_custom_sw_retry_th_cmd_fixed_param *)buf_ptr; WMITLV_SET_HDR(&cmd->tlv_header, WMITLV_TAG_STRUC_wmi_vdev_set_custom_sw_retry_th_cmd_fixed_param, WMITLV_GET_STRUCT_TLVLEN( wmi_vdev_set_custom_sw_retry_th_cmd_fixed_param)); cmd->vdev_id = vdev_id; cmd->ac_type = ac_type; cmd->sw_retry_type = retry_type; cmd->sw_retry_th = sw_retry; wma_debug("ac_type: %d re_type: %d threshold: %d vid: %d", cmd->ac_type, cmd->sw_retry_type, cmd->sw_retry_th, cmd->vdev_id); ret = wmi_unified_cmd_send(handle->wmi_handle, buf, len, WMI_VDEV_SET_CUSTOM_SW_RETRY_TH_CMDID); if (ret) { wmi_buf_free(buf); return QDF_STATUS_E_FAILURE; } return QDF_STATUS_SUCCESS; } QDF_STATUS wma_set_vdev_sw_retry_th(uint8_t vdev_id, uint8_t sw_retry_count, wmi_vdev_custom_sw_retry_type_t retry_type) { QDF_STATUS status = QDF_STATUS_SUCCESS; tp_wma_handle wma_handle; uint32_t queue_num; wma_handle = cds_get_context(QDF_MODULE_ID_WMA); if (!wma_handle) return QDF_STATUS_E_FAILURE; for (queue_num = 0; queue_num < WMI_AC_MAX; queue_num++) { if (sw_retry_count == 0) continue; status = wma_set_sw_retry_by_qos(wma_handle, vdev_id, retry_type, queue_num, sw_retry_count); if (QDF_IS_STATUS_ERROR(status)) return status; } return QDF_STATUS_SUCCESS; } QDF_STATUS wma_set_sw_retry_threshold_per_ac(WMA_HANDLE handle, uint8_t vdev_id, struct wlan_mlme_qos *qos_aggr) { QDF_STATUS ret; int retry_type, queue_num; uint32_t tx_sw_retry[WMI_VDEV_CUSTOM_SW_RETRY_TYPE_MAX][WMI_AC_MAX]; uint32_t sw_retry; tp_wma_handle wma_handle = (tp_wma_handle)handle; if (wma_validate_handle(wma_handle)) return QDF_STATUS_E_INVAL; tx_sw_retry[WMI_VDEV_CUSTOM_SW_RETRY_TYPE_AGGR][WMI_AC_BE] = qos_aggr->tx_aggr_sw_retry_threshold_be; tx_sw_retry[WMI_VDEV_CUSTOM_SW_RETRY_TYPE_AGGR][WMI_AC_BK] = qos_aggr->tx_aggr_sw_retry_threshold_bk; tx_sw_retry[WMI_VDEV_CUSTOM_SW_RETRY_TYPE_AGGR][WMI_AC_VI] = qos_aggr->tx_aggr_sw_retry_threshold_vi; tx_sw_retry[WMI_VDEV_CUSTOM_SW_RETRY_TYPE_AGGR][WMI_AC_VO] = qos_aggr->tx_aggr_sw_retry_threshold_vo; tx_sw_retry[WMI_VDEV_CUSTOM_SW_RETRY_TYPE_NONAGGR][WMI_AC_BE] = qos_aggr->tx_non_aggr_sw_retry_threshold_be; tx_sw_retry[WMI_VDEV_CUSTOM_SW_RETRY_TYPE_NONAGGR][WMI_AC_BK] = qos_aggr->tx_non_aggr_sw_retry_threshold_bk; tx_sw_retry[WMI_VDEV_CUSTOM_SW_RETRY_TYPE_NONAGGR][WMI_AC_VI] = qos_aggr->tx_non_aggr_sw_retry_threshold_vi; tx_sw_retry[WMI_VDEV_CUSTOM_SW_RETRY_TYPE_NONAGGR][WMI_AC_VO] = qos_aggr->tx_non_aggr_sw_retry_threshold_vo; retry_type = WMI_VDEV_CUSTOM_SW_RETRY_TYPE_NONAGGR; while (retry_type < WMI_VDEV_CUSTOM_SW_RETRY_TYPE_MAX) { for (queue_num = 0; queue_num < WMI_AC_MAX; queue_num++) { if (tx_sw_retry[retry_type][queue_num] == 0) continue; sw_retry = tx_sw_retry[retry_type][queue_num]; ret = wma_set_sw_retry_by_qos(wma_handle, vdev_id, retry_type, queue_num, sw_retry); if (QDF_IS_STATUS_ERROR(ret)) return ret; } retry_type++; } return QDF_STATUS_SUCCESS; } #define MAX_PDEV_SW_RETRY_PARAMS 2 /* params being sent: * 1.wmi_pdev_param_agg_sw_retry_th * 2.wmi_pdev_param_non_agg_sw_retry_th */ QDF_STATUS wma_set_sw_retry_threshold(struct wlan_mlme_qos *qos_aggr) { uint32_t max, min, retry; struct dev_set_param setparam[MAX_PDEV_SW_RETRY_PARAMS]; QDF_STATUS ret; uint8_t index = 0; retry = qos_aggr->tx_aggr_sw_retry_threshold; max = cfg_max(CFG_TX_AGGR_SW_RETRY); min = cfg_min(CFG_TX_AGGR_SW_RETRY); retry = (retry > max) ? max : retry; retry = (retry < min) ? min : retry; ret = mlme_check_index_setparam(setparam, wmi_pdev_param_agg_sw_retry_th, retry, index++, MAX_PDEV_SW_RETRY_PARAMS); if (QDF_IS_STATUS_ERROR(ret)) { wma_debug("failed to set wmi_pdev_param_agg_sw_retry_th"); return ret; } retry = qos_aggr->tx_non_aggr_sw_retry_threshold; max = cfg_max(CFG_TX_NON_AGGR_SW_RETRY); min = cfg_min(CFG_TX_NON_AGGR_SW_RETRY); retry = (retry > max) ? max : retry; retry = (retry < min) ? min : retry; ret = mlme_check_index_setparam(setparam, wmi_pdev_param_non_agg_sw_retry_th, retry, index++, MAX_PDEV_SW_RETRY_PARAMS); if (QDF_IS_STATUS_ERROR(ret)) { wma_debug("failed to set wmi_pdev_param_non_agg_sw_retry_th"); return ret; } ret = wma_send_multi_pdev_vdev_set_params(MLME_PDEV_SETPARAM, WMI_PDEV_ID_SOC, setparam, index); return ret; } /** * wma_process_fw_test_cmd() - send unit test command to fw. * @handle: wma handle * @wma_fwtest: fw test command * * This function send fw test command to fw. * * Return: none */ QDF_STATUS wma_process_fw_test_cmd(WMA_HANDLE handle, struct set_fwtest_params *wma_fwtest) { tp_wma_handle wma_handle = (tp_wma_handle) handle; struct wmi_unified *wmi_handle; if (wma_validate_handle(wma_handle)) return QDF_STATUS_E_INVAL; wmi_handle = wma_handle->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; if (wmi_unified_fw_test_cmd(wmi_handle, (struct set_fwtest_params *)wma_fwtest)) { wma_err("Failed to issue fw test cmd"); return QDF_STATUS_E_FAILURE; } return QDF_STATUS_SUCCESS; } /** * wma_enable_disable_caevent_ind() - Issue WMI command to enable or * disable ca event indication * @wma: wma handler * @val: boolean value true or false * * Return: QDF_STATUS */ QDF_STATUS wma_enable_disable_caevent_ind(tp_wma_handle wma, uint8_t val) { WMI_CHAN_AVOID_RPT_ALLOW_CMD_fixed_param *cmd; wmi_buf_t wmi_buf; uint8_t *buf_ptr; uint32_t len; struct wmi_unified *wmi_handle; if (wma_validate_handle(wma)) return QDF_STATUS_E_INVAL; wmi_handle = wma->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; len = sizeof(*cmd); wmi_buf = wmi_buf_alloc(wmi_handle, len); if (!wmi_buf) return QDF_STATUS_E_NOMEM; buf_ptr = (uint8_t *) wmi_buf_data(wmi_buf); cmd = (WMI_CHAN_AVOID_RPT_ALLOW_CMD_fixed_param *) buf_ptr; WMITLV_SET_HDR(&cmd->tlv_header, WMITLV_TAG_STRUC_WMI_CHAN_AVOID_RPT_ALLOW_CMD_fixed_param, WMITLV_GET_STRUCT_TLVLEN( WMI_CHAN_AVOID_RPT_ALLOW_CMD_fixed_param)); cmd->rpt_allow = val; if (wmi_unified_cmd_send(wmi_handle, wmi_buf, len, WMI_CHAN_AVOID_RPT_ALLOW_CMDID)) { wmi_buf_free(wmi_buf); return QDF_STATUS_E_FAILURE; } return QDF_STATUS_SUCCESS; } static wma_sar_cb sar_callback; static void *sar_context; static int wma_sar_event_handler(void *handle, uint8_t *evt_buf, uint32_t len) { tp_wma_handle wma_handle; wmi_unified_t wmi_handle; struct sar_limit_event *event; wma_sar_cb callback; QDF_STATUS status; wma_info("handle:%pK event:%pK len:%u", handle, evt_buf, len); wma_handle = handle; if (wma_validate_handle(wma_handle)) return QDF_STATUS_E_INVAL; wmi_handle = wma_handle->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; event = qdf_mem_malloc(sizeof(*event)); if (!event) return QDF_STATUS_E_NOMEM; status = wmi_unified_extract_sar_limit_event(wmi_handle, evt_buf, event); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Event extract failure: %d", status); qdf_mem_free(event); return QDF_STATUS_E_INVAL; } callback = sar_callback; sar_callback = NULL; if (callback) callback(sar_context, event); qdf_mem_free(event); return 0; } QDF_STATUS wma_sar_register_event_handlers(WMA_HANDLE handle) { tp_wma_handle wma_handle = handle; wmi_unified_t wmi_handle; if (wma_validate_handle(wma_handle)) return QDF_STATUS_E_INVAL; wmi_handle = wma_handle->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; return wmi_unified_register_event_handler(wmi_handle, wmi_sar_get_limits_event_id, wma_sar_event_handler, WMA_RX_WORK_CTX); } QDF_STATUS wma_get_sar_limit(WMA_HANDLE handle, wma_sar_cb callback, void *context) { tp_wma_handle wma_handle = handle; wmi_unified_t wmi_handle; QDF_STATUS status; if (wma_validate_handle(wma_handle)) return QDF_STATUS_E_INVAL; wmi_handle = wma_handle->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; sar_callback = callback; sar_context = context; status = wmi_unified_get_sar_limit_cmd(wmi_handle); if (QDF_IS_STATUS_ERROR(status)) { wma_err("wmi_unified_get_sar_limit_cmd() error: %u", status); sar_callback = NULL; } return status; } QDF_STATUS wma_set_sar_limit(WMA_HANDLE handle, struct sar_limit_cmd_params *sar_limit_params) { int ret; tp_wma_handle wma = (tp_wma_handle) handle; struct wmi_unified *wmi_handle; if (wma_validate_handle(wma)) return QDF_STATUS_E_INVAL; wmi_handle = wma->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; if (!sar_limit_params) { wma_err("set sar limit ptr NULL"); return QDF_STATUS_E_INVAL; } ret = wmi_unified_send_sar_limit_cmd(wmi_handle, sar_limit_params); return ret; } QDF_STATUS wma_send_coex_config_cmd(WMA_HANDLE wma_handle, struct coex_config_params *coex_cfg_params) { tp_wma_handle wma = (tp_wma_handle)wma_handle; struct wmi_unified *wmi_handle; if (wma_validate_handle(wma)) return QDF_STATUS_E_INVAL; wmi_handle = wma->wmi_handle; if (wmi_validate_handle(wmi_handle)) return QDF_STATUS_E_INVAL; if (!coex_cfg_params) { wma_err("coex cfg params ptr NULL"); return QDF_STATUS_E_INVAL; } return wmi_unified_send_coex_config_cmd(wmi_handle, coex_cfg_params); } /** * wma_get_arp_stats_handler() - handle arp stats data * indicated by FW * @handle: wma context * @data: event buffer * @data len: length of event buffer * * Return: 0 on success */ int wma_get_arp_stats_handler(void *handle, uint8_t *data, uint32_t data_len) { WMI_VDEV_GET_ARP_STAT_EVENTID_param_tlvs *param_buf; wmi_vdev_get_arp_stats_event_fixed_param *data_event; wmi_vdev_get_connectivity_check_stats *connect_stats_event; uint8_t *buf_ptr; struct rsp_stats rsp = {0}; struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE); if (!mac) return -EINVAL; if (!mac->sme.get_arp_stats_cb) { wma_err("Callback not registered"); return -EINVAL; } if (!data) { wma_err("invalid pointer"); return -EINVAL; } param_buf = (WMI_VDEV_GET_ARP_STAT_EVENTID_param_tlvs *)data; if (!param_buf) { wma_err("Invalid get arp stats event"); return -EINVAL; } data_event = param_buf->fixed_param; if (!data_event) { wma_err("Invalid get arp stats data event"); return -EINVAL; } rsp.arp_req_enqueue = data_event->arp_req_enqueue; rsp.vdev_id = data_event->vdev_id; rsp.arp_req_tx_success = data_event->arp_req_tx_success; rsp.arp_req_tx_failure = data_event->arp_req_tx_failure; rsp.arp_rsp_recvd = data_event->arp_rsp_recvd; rsp.out_of_order_arp_rsp_drop_cnt = data_event->out_of_order_arp_rsp_drop_cnt; rsp.dad_detected = data_event->dad_detected; rsp.connect_status = data_event->connect_status; rsp.ba_session_establishment_status = data_event->ba_session_establishment_status; buf_ptr = (uint8_t *)data_event; buf_ptr = buf_ptr + sizeof(wmi_vdev_get_arp_stats_event_fixed_param) + WMI_TLV_HDR_SIZE; connect_stats_event = (wmi_vdev_get_connectivity_check_stats *)buf_ptr; if (((connect_stats_event->tlv_header & 0xFFFF0000) >> 16 == WMITLV_TAG_STRUC_wmi_vdev_get_connectivity_check_stats)) { rsp.connect_stats_present = true; rsp.tcp_ack_recvd = connect_stats_event->tcp_ack_recvd; rsp.icmpv4_rsp_recvd = connect_stats_event->icmpv4_rsp_recvd; wma_debug("tcp_ack_recvd %d icmpv4_rsp_recvd %d", connect_stats_event->tcp_ack_recvd, connect_stats_event->icmpv4_rsp_recvd); } mac->sme.get_arp_stats_cb(mac->hdd_handle, &rsp, mac->sme.get_arp_stats_context); return 0; } /** * wma_unified_power_debug_stats_event_handler() - WMA handler function to * handle Power stats event from firmware * @handle: Pointer to wma handle * @cmd_param_info: Pointer to Power stats event TLV * @len: Length of the cmd_param_info * * Return: 0 on success, error number otherwise */ #ifdef WLAN_POWER_DEBUG int wma_unified_power_debug_stats_event_handler(void *handle, uint8_t *cmd_param_info, uint32_t len) { WMI_PDEV_CHIP_POWER_STATS_EVENTID_param_tlvs *param_tlvs; struct power_stats_response *power_stats_results; wmi_pdev_chip_power_stats_event_fixed_param *param_buf; uint32_t power_stats_len, stats_registers_len, *debug_registers; struct mac_context *mac = (struct mac_context *)cds_get_context(QDF_MODULE_ID_PE); param_tlvs = (WMI_PDEV_CHIP_POWER_STATS_EVENTID_param_tlvs *) cmd_param_info; param_buf = (wmi_pdev_chip_power_stats_event_fixed_param *) param_tlvs->fixed_param; if (!mac) { wma_debug("NULL mac ptr"); return -EINVAL; } if (!param_buf) { wma_debug("NULL power stats event fixed param"); return -EINVAL; } if (param_buf->num_debug_register > ((WMI_SVC_MSG_MAX_SIZE - sizeof(wmi_pdev_chip_power_stats_event_fixed_param)) / sizeof(uint32_t)) || param_buf->num_debug_register > param_tlvs->num_debug_registers) { wma_err("excess payload: LEN num_debug_register:%u", param_buf->num_debug_register); return -EINVAL; } debug_registers = param_tlvs->debug_registers; stats_registers_len = (sizeof(uint32_t) * param_buf->num_debug_register); power_stats_len = stats_registers_len + sizeof(*power_stats_results); power_stats_results = qdf_mem_malloc(power_stats_len); if (!power_stats_results) return -ENOMEM; wma_debug("Cumulative sleep time %d cumulative total on time %d deep sleep enter counter %d last deep sleep enter tstamp ts %d debug registers fmt %d num debug register %d", param_buf->cumulative_sleep_time_ms, param_buf->cumulative_total_on_time_ms, param_buf->deep_sleep_enter_counter, param_buf->last_deep_sleep_enter_tstamp_ms, param_buf->debug_register_fmt, param_buf->num_debug_register); power_stats_results->cumulative_sleep_time_ms = param_buf->cumulative_sleep_time_ms; power_stats_results->cumulative_total_on_time_ms = param_buf->cumulative_total_on_time_ms; power_stats_results->deep_sleep_enter_counter = param_buf->deep_sleep_enter_counter; power_stats_results->last_deep_sleep_enter_tstamp_ms = param_buf->last_deep_sleep_enter_tstamp_ms; power_stats_results->debug_register_fmt = param_buf->debug_register_fmt; power_stats_results->num_debug_register = param_buf->num_debug_register; power_stats_results->debug_registers = (uint32_t *)(power_stats_results + 1); qdf_mem_copy(power_stats_results->debug_registers, debug_registers, stats_registers_len); if (mac->sme.sme_power_debug_stats_callback) mac->sme.sme_power_debug_stats_callback(mac, power_stats_results); qdf_mem_free(power_stats_results); return 0; } #endif #ifdef WLAN_FEATURE_BEACON_RECEPTION_STATS int wma_unified_beacon_debug_stats_event_handler(void *handle, uint8_t *cmd_param_info, uint32_t len) { WMI_VDEV_BCN_RECEPTION_STATS_EVENTID_param_tlvs *param_tlvs; struct bcn_reception_stats_rsp *bcn_reception_stats; wmi_vdev_bcn_recv_stats_fixed_param *param_buf; struct mac_context *mac = (struct mac_context *)cds_get_context(QDF_MODULE_ID_PE); param_tlvs = (WMI_VDEV_BCN_RECEPTION_STATS_EVENTID_param_tlvs *)cmd_param_info; if (!param_tlvs) { wma_err("Invalid stats event"); return -EINVAL; } param_buf = (wmi_vdev_bcn_recv_stats_fixed_param *) param_tlvs->fixed_param; if (!param_buf || !mac || !mac->sme.beacon_stats_resp_callback) { wma_debug("NULL mac ptr or HDD callback is null"); return -EINVAL; } if (!param_buf) { wma_debug("NULL beacon stats event fixed param"); return -EINVAL; } bcn_reception_stats = qdf_mem_malloc(sizeof(*bcn_reception_stats)); if (!bcn_reception_stats) return -ENOMEM; bcn_reception_stats->total_bcn_cnt = param_buf->total_bcn_cnt; bcn_reception_stats->total_bmiss_cnt = param_buf->total_bmiss_cnt; bcn_reception_stats->vdev_id = param_buf->vdev_id; wma_debug("Total beacon count %d total beacon miss count %d vdev_id %d", param_buf->total_bcn_cnt, param_buf->total_bmiss_cnt, param_buf->vdev_id); qdf_mem_copy(bcn_reception_stats->bmiss_bitmap, param_buf->bmiss_bitmap, MAX_BCNMISS_BITMAP * sizeof(uint32_t)); mac->sme.beacon_stats_resp_callback(bcn_reception_stats, mac->sme.beacon_stats_context); qdf_mem_free(bcn_reception_stats); return 0; } #else int wma_unified_beacon_debug_stats_event_handler(void *handle, uint8_t *cmd_param_info, uint32_t len) { return 0; } #endif #if defined(CLD_PM_QOS) && defined(WLAN_FEATURE_LL_MODE) int wma_vdev_bcn_latency_event_handler(void *handle, uint8_t *event_info, uint32_t len) { WMI_VDEV_BCN_LATENCY_EVENTID_param_tlvs *param_buf = NULL; wmi_vdev_bcn_latency_fixed_param *bcn_latency = NULL; struct mac_context *mac = (struct mac_context *)cds_get_context(QDF_MODULE_ID_PE); uint32_t latency_level; param_buf = (WMI_VDEV_BCN_LATENCY_EVENTID_param_tlvs *)event_info; if (!param_buf) { wma_err("Invalid bcn latency event"); return -EINVAL; } bcn_latency = param_buf->fixed_param; if (!bcn_latency) { wma_debug("beacon latency event fixed param is NULL"); return -EINVAL; } /* Map the latency value to the level which host expects * 1 - normal, 2 - moderate, 3 - low, 4 - ultralow */ latency_level = bcn_latency->latency_level + 1; if (latency_level < 1 || latency_level > 4) { wma_debug("invalid beacon latency level value"); return -EINVAL; } /* Call the registered sme callback */ mac->sme.beacon_latency_event_cb(latency_level); return 0; } #endif static void wma_update_scan_channel_info_buf(wmi_unified_t wmi_handle, wmi_chan_info_event_fixed_param *event, struct scan_chan_info *buf, wmi_cca_busy_subband_info *cca_info, uint32_t num_tlvs) { uint32_t i; if (cca_info && num_tlvs > 0) { buf->subband_info.num_chan = 0; for (i = 0; i < num_tlvs && i < MAX_WIDE_BAND_SCAN_CHAN; i++) { buf->subband_info.cca_busy_subband_info[i] = cca_info->rx_clear_count; wma_debug("cca_info->rx_clear_count:%d", cca_info->rx_clear_count); buf->subband_info.num_chan++; cca_info++; } buf->subband_info.is_wide_band_scan = true; buf->subband_info.vdev_id = event->vdev_id; } } static void wma_update_scan_channel_subband_info(wmi_chan_info_event_fixed_param *event, struct channel_status *channel_status, wmi_cca_busy_subband_info *cca_info, uint32_t num_tlvs) { uint32_t i; if (cca_info && num_tlvs > 0) { channel_status->subband_info.num_chan = 0; for (i = 0; i < num_tlvs && i < MAX_WIDE_BAND_SCAN_CHAN; i++) { channel_status->subband_info.cca_busy_subband_info[i] = cca_info->rx_clear_count; wma_debug("cca_info->rx_clear_count[%d]: %d", i, cca_info->rx_clear_count); channel_status->subband_info.num_chan++; cca_info++; } channel_status->subband_info.is_wide_band_scan = true; channel_status->subband_info.vdev_id = event->vdev_id; } } int wma_chan_info_event_handler(void *handle, uint8_t *event_buf, uint32_t len) { tp_wma_handle wma = (tp_wma_handle)handle; WMI_CHAN_INFO_EVENTID_param_tlvs *param_buf; wmi_chan_info_event_fixed_param *event; struct scan_chan_info buf = {0}; struct mac_context *mac = NULL; struct channel_status *channel_status; bool snr_monitor_enabled; struct scheduler_msg sme_msg = {0}; wmi_cca_busy_subband_info *cca_info = NULL; uint32_t num_tlvs = 0; bool is_cca_busy_info; QDF_STATUS qdf_status; if (wma && wma->cds_context) mac = (struct mac_context *)cds_get_context(QDF_MODULE_ID_PE); if (!mac) return -EINVAL; param_buf = (WMI_CHAN_INFO_EVENTID_param_tlvs *)event_buf; if (!param_buf) { wma_err("Invalid chan info event buffer"); return -EINVAL; } event = param_buf->fixed_param; if (!event) { wma_err("Invalid fixed param"); return -EINVAL; } /* Ignore the last channel event data whose command flag is set to 1. * It’s basically an event with empty data only to indicate scan event * completion. */ if (event->cmd_flags == WMI_CHAN_INFO_END_RESP) return 0; cca_info = param_buf->cca_busy_subband_info; num_tlvs = param_buf->num_cca_busy_subband_info; is_cca_busy_info = wmi_service_enabled(wma->wmi_handle, wmi_service_cca_busy_info_for_each_20mhz); snr_monitor_enabled = wlan_scan_is_snr_monitor_enabled(mac->psoc); if (snr_monitor_enabled && mac->chan_info_cb) { buf.tx_frame_count = event->tx_frame_cnt; buf.clock_freq = event->mac_clk_mhz; buf.cmd_flag = event->cmd_flags; buf.freq = event->freq; buf.noise_floor = event->noise_floor; buf.cycle_count = event->cycle_count; buf.rx_clear_count = event->rx_clear_count; /* wide band scan case */ if (is_cca_busy_info && num_tlvs) wma_update_scan_channel_info_buf(wma->wmi_handle, event, &buf, cca_info, num_tlvs); mac->chan_info_cb(&buf); } channel_status = qdf_mem_malloc(sizeof(*channel_status)); if (!channel_status) return -ENOMEM; channel_status->channel_freq = event->freq; channel_status->noise_floor = event->noise_floor; if (is_cca_busy_info && num_tlvs) wma_update_scan_channel_subband_info(event, channel_status, cca_info, num_tlvs); else channel_status->rx_clear_count = event->rx_clear_count; channel_status->cycle_count = event->cycle_count; channel_status->chan_tx_pwr_range = event->chan_tx_pwr_range; channel_status->chan_tx_pwr_throughput = event->chan_tx_pwr_tp; channel_status->rx_frame_count = event->rx_frame_count; channel_status->bss_rx_cycle_count = event->my_bss_rx_cycle_count; channel_status->rx_11b_mode_data_duration = event->rx_11b_mode_data_duration; channel_status->tx_frame_count = event->tx_frame_cnt; channel_status->mac_clk_mhz = event->mac_clk_mhz; channel_status->channel_id = cds_freq_to_chan(event->freq); channel_status->cmd_flags = event->cmd_flags; wma_debug("freq %d, nf %d, rcc %u, ch_rcc:%u, cc %u, tx_r %d, tx_t %d, chan_id:%d, flags:%d, cap: %d, num_tlvs:%d", event->freq, event->noise_floor, event->rx_clear_count, channel_status->rx_clear_count, event->cycle_count, event->chan_tx_pwr_range, event->chan_tx_pwr_tp, channel_status->channel_id, channel_status->cmd_flags, is_cca_busy_info, num_tlvs); sme_msg.type = eWNI_SME_CHAN_INFO_EVENT; sme_msg.bodyptr = channel_status; sme_msg.bodyval = event->vdev_id; qdf_status = scheduler_post_message(QDF_MODULE_ID_WMA, QDF_MODULE_ID_SME, QDF_MODULE_ID_SME, &sme_msg); if (QDF_IS_STATUS_ERROR(qdf_status)) qdf_mem_free(channel_status); return 0; } int wma_rx_aggr_failure_event_handler(void *handle, u_int8_t *event_buf, u_int32_t len) { WMI_REPORT_RX_AGGR_FAILURE_EVENTID_param_tlvs *param_buf; struct sir_sme_rx_aggr_hole_ind *rx_aggr_hole_event; wmi_rx_aggr_failure_event_fixed_param *rx_aggr_failure_info; wmi_rx_aggr_failure_info *hole_info; uint32_t i, alloc_len; struct mac_context *mac; mac = (struct mac_context *)cds_get_context(QDF_MODULE_ID_PE); if (!mac || !mac->sme.stats_ext2_cb) { wma_debug("NULL mac ptr or HDD callback is null"); return -EINVAL; } param_buf = (WMI_REPORT_RX_AGGR_FAILURE_EVENTID_param_tlvs *)event_buf; if (!param_buf) { wma_err("Invalid stats ext event buf"); return -EINVAL; } rx_aggr_failure_info = param_buf->fixed_param; hole_info = param_buf->failure_info; if (rx_aggr_failure_info->num_failure_info > ((WMI_SVC_MSG_MAX_SIZE - sizeof(*rx_aggr_hole_event)) / sizeof(rx_aggr_hole_event->hole_info_array[0]))) { wma_err("Excess data from WMI num_failure_info %d", rx_aggr_failure_info->num_failure_info); return -EINVAL; } alloc_len = sizeof(*rx_aggr_hole_event) + (rx_aggr_failure_info->num_failure_info)* sizeof(rx_aggr_hole_event->hole_info_array[0]); rx_aggr_hole_event = qdf_mem_malloc(alloc_len); if (!rx_aggr_hole_event) return -ENOMEM; rx_aggr_hole_event->hole_cnt = rx_aggr_failure_info->num_failure_info; if (rx_aggr_hole_event->hole_cnt > param_buf->num_failure_info) { wma_err("Invalid no of hole count: %d", rx_aggr_hole_event->hole_cnt); qdf_mem_free(rx_aggr_hole_event); return -EINVAL; } wma_debug("aggr holes_sum: %d\n", rx_aggr_failure_info->num_failure_info); for (i = 0; i < rx_aggr_hole_event->hole_cnt; i++) { rx_aggr_hole_event->hole_info_array[i] = hole_info->end_seq - hole_info->start_seq + 1; wma_nofl_debug("aggr_index: %d\tstart_seq: %d\tend_seq: %d\t" "hole_info: %d mpdu lost", i, hole_info->start_seq, hole_info->end_seq, rx_aggr_hole_event->hole_info_array[i]); hole_info++; } mac->sme.stats_ext2_cb(mac->hdd_handle, rx_aggr_hole_event); qdf_mem_free(rx_aggr_hole_event); return 0; } int wma_wlan_bt_activity_evt_handler(void *handle, uint8_t *event, uint32_t len) { wmi_coex_bt_activity_event_fixed_param *fixed_param; WMI_WLAN_COEX_BT_ACTIVITY_EVENTID_param_tlvs *param_buf = (WMI_WLAN_COEX_BT_ACTIVITY_EVENTID_param_tlvs *)event; struct scheduler_msg sme_msg = {0}; QDF_STATUS qdf_status; if (!param_buf) { wma_err("Invalid BT activity event buffer"); return -EINVAL; } fixed_param = param_buf->fixed_param; if (!fixed_param) { wma_err("Invalid BT activity event fixed param buffer"); return -EINVAL; } wma_info("Received BT activity event %u", fixed_param->coex_profile_evt); sme_msg.type = eWNI_SME_BT_ACTIVITY_INFO_IND; sme_msg.bodyptr = NULL; sme_msg.bodyval = fixed_param->coex_profile_evt; qdf_status = scheduler_post_message(QDF_MODULE_ID_WMA, QDF_MODULE_ID_SME, QDF_MODULE_ID_SME, &sme_msg); if (QDF_IS_STATUS_ERROR(qdf_status)) return -EINVAL; return 0; } int wma_pdev_div_info_evt_handler(void *handle, u_int8_t *event_buf, u_int32_t len) { WMI_PDEV_DIV_RSSI_ANTID_EVENTID_param_tlvs *param_buf; wmi_pdev_div_rssi_antid_event_fixed_param *event; struct chain_rssi_result chain_rssi_result; u_int32_t i; u_int8_t macaddr[QDF_MAC_ADDR_SIZE]; tp_wma_handle wma = (tp_wma_handle)handle; struct mac_context *pmac = (struct mac_context *)cds_get_context( QDF_MODULE_ID_PE); if (!pmac || !wma) { wma_err("Invalid pmac or wma"); return -EINVAL; } if (!pmac->sme.get_chain_rssi_cb) { wma_err("Invalid get_chain_rssi_cb"); return -EINVAL; } param_buf = (WMI_PDEV_DIV_RSSI_ANTID_EVENTID_param_tlvs *) event_buf; if (!param_buf) { wma_err("Invalid rssi antid event buffer"); return -EINVAL; } event = param_buf->fixed_param; if (!event) { wma_err("Invalid fixed param"); return -EINVAL; } if (event->num_chains_valid > CHAIN_MAX_NUM) { wma_err("Invalid num of chains"); return -EINVAL; } qdf_mem_zero(&chain_rssi_result, sizeof(chain_rssi_result)); WMI_MAC_ADDR_TO_CHAR_ARRAY(&event->macaddr, macaddr); wma_debug("macaddr: " QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(macaddr)); wma_debug("num_chains_valid: %d", event->num_chains_valid); chain_rssi_result.num_chains_valid = event->num_chains_valid; qdf_mem_copy(chain_rssi_result.chain_rssi, event->chain_rssi, sizeof(event->chain_rssi)); qdf_mem_copy(chain_rssi_result.chain_evm, event->chain_evm, sizeof(event->chain_evm)); qdf_mem_copy(chain_rssi_result.ant_id, event->ant_id, sizeof(event->ant_id)); for (i = 0; i < chain_rssi_result.num_chains_valid; i++) { wma_nofl_debug("chain_rssi: %d, chain_evm: %d,ant_id: %d", chain_rssi_result.chain_rssi[i], chain_rssi_result.chain_evm[i], chain_rssi_result.ant_id[i]); if (!wmi_service_enabled(wma->wmi_handle, wmi_service_hw_db2dbm_support)) { if (chain_rssi_result.chain_rssi[i] != WMA_INVALID_PER_CHAIN_SNR) chain_rssi_result.chain_rssi[i] += WMA_TGT_NOISE_FLOOR_DBM; else chain_rssi_result.chain_rssi[i] = WMA_INVALID_PER_CHAIN_RSSI; } } pmac->sme.get_chain_rssi_cb(pmac->sme.get_chain_rssi_context, &chain_rssi_result); return 0; } int wma_vdev_obss_detection_info_handler(void *handle, uint8_t *event, uint32_t len) { tp_wma_handle wma = (tp_wma_handle) handle; struct wmi_obss_detect_info *obss_detection; QDF_STATUS status; if (!event) { wma_err("Invalid obss_detection_info event buffer"); return -EINVAL; } obss_detection = qdf_mem_malloc(sizeof(*obss_detection)); if (!obss_detection) return -ENOMEM; status = wmi_unified_extract_obss_detection_info(wma->wmi_handle, event, obss_detection); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to extract obss info"); qdf_mem_free(obss_detection); return -EINVAL; } if (!wma_is_vdev_valid(obss_detection->vdev_id)) { wma_err("Invalid vdev id %d", obss_detection->vdev_id); qdf_mem_free(obss_detection); return -EINVAL; } wma_send_msg(wma, WMA_OBSS_DETECTION_INFO, obss_detection, 0); return 0; } static void wma_send_set_key_rsp(uint8_t vdev_id, bool pairwise, uint8_t key_index) { tSetStaKeyParams *key_info_uc; tSetBssKeyParams *key_info_mc; struct wlan_crypto_key *crypto_key; struct wlan_objmgr_vdev *vdev; tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); struct qdf_mac_addr bcast_mac = QDF_MAC_ADDR_BCAST_INIT; if (!wma) return; vdev = wlan_objmgr_get_vdev_by_id_from_psoc(wma->psoc, vdev_id, WLAN_LEGACY_WMA_ID); if (!vdev) { wma_err("VDEV object not found"); return; } crypto_key = wlan_crypto_get_key(vdev, key_index); wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_WMA_ID); if (!crypto_key) { wma_debug("crypto_key not found"); return; } if (pairwise) { key_info_uc = qdf_mem_malloc(sizeof(*key_info_uc)); if (!key_info_uc) return; key_info_uc->vdev_id = vdev_id; key_info_uc->status = QDF_STATUS_SUCCESS; key_info_uc->key_len = crypto_key->keylen; qdf_mem_copy(&key_info_uc->macaddr, &crypto_key->macaddr, QDF_MAC_ADDR_SIZE); wma_send_msg_high_priority(wma, WMA_SET_STAKEY_RSP, key_info_uc, 0); wlan_release_peer_key_wakelock(wma->pdev, crypto_key->macaddr); } else { key_info_mc = qdf_mem_malloc(sizeof(*key_info_mc)); if (!key_info_mc) return; key_info_mc->vdev_id = vdev_id; key_info_mc->status = QDF_STATUS_SUCCESS; key_info_mc->key_len = crypto_key->keylen; qdf_mem_copy(&key_info_mc->macaddr, &bcast_mac, QDF_MAC_ADDR_SIZE); wma_send_msg_high_priority(wma, WMA_SET_BSSKEY_RSP, key_info_mc, 0); } } void wma_set_peer_ucast_cipher(uint8_t *mac_addr, int32_t uc_cipher, int32_t cipher_cap) { struct wlan_objmgr_peer *peer; tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); if (!wma) return; peer = wlan_objmgr_get_peer(wma->psoc, wlan_objmgr_pdev_get_pdev_id(wma->pdev), mac_addr, WLAN_LEGACY_WMA_ID); if (!peer) { wma_err("Peer of peer_mac "QDF_MAC_ADDR_FMT" not found", QDF_MAC_ADDR_REF(mac_addr)); return; } wlan_crypto_set_peer_param(peer, WLAN_CRYPTO_PARAM_CIPHER_CAP, cipher_cap); wlan_crypto_set_peer_param(peer, WLAN_CRYPTO_PARAM_UCAST_CIPHER, uc_cipher); wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_WMA_ID); wma_debug("Set unicast cipher %x and cap %x for "QDF_MAC_ADDR_FMT, uc_cipher, cipher_cap, QDF_MAC_ADDR_REF(mac_addr)); } void wma_update_set_key(uint8_t session_id, bool pairwise, uint8_t key_index, enum wlan_crypto_cipher_type cipher_type) { tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); struct wma_txrx_node *iface; if (!wma) return; iface = &wma->interfaces[session_id]; if (!iface) { wma_err("iface not found for session id %d", session_id); return; } if (iface) iface->is_waiting_for_key = false; wma_send_set_key_rsp(session_id, pairwise, key_index); } int wma_vdev_bss_color_collision_info_handler(void *handle, uint8_t *event, uint32_t len) { tp_wma_handle wma = (tp_wma_handle) handle; struct wmi_obss_color_collision_info *obss_color_info; QDF_STATUS status; if (!event) { wma_err("Invalid obss_color_collision event buffer"); return -EINVAL; } obss_color_info = qdf_mem_malloc(sizeof(*obss_color_info)); if (!obss_color_info) return -ENOMEM; status = wmi_unified_extract_obss_color_collision_info(wma->wmi_handle, event, obss_color_info); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to extract obss color info"); qdf_mem_free(obss_color_info); return -EINVAL; } if (!wma_is_vdev_valid(obss_color_info->vdev_id)) { wma_err("Invalid vdev id %d", obss_color_info->vdev_id); qdf_mem_free(obss_color_info); return -EINVAL; } wma_send_msg(wma, WMA_OBSS_COLOR_COLLISION_INFO, obss_color_info, 0); return 0; } #ifdef FEATURE_ANI_LEVEL_REQUEST int wma_get_ani_level_evt_handler(void *handle, uint8_t *event_buf, uint32_t len) { tp_wma_handle wma = (tp_wma_handle)handle; struct wmi_host_ani_level_event *ani = NULL; uint32_t num_freqs = 0; QDF_STATUS status; struct mac_context *pmac; int ret = 0; pmac = (struct mac_context *)cds_get_context(QDF_MODULE_ID_PE); if (!pmac || !wma) { wma_err("Invalid pmac or wma"); return -EINVAL; } status = wmi_unified_extract_ani_level(wma->wmi_handle, event_buf, &ani, &num_freqs); if (QDF_IS_STATUS_ERROR(status)) { wma_err("Failed to extract ani level"); return -EINVAL; } if (!pmac->ani_params.ani_level_cb) { wma_err("Invalid ani_level_cb"); ret = -EINVAL; goto free; } pmac->ani_params.ani_level_cb(ani, num_freqs, pmac->ani_params.context); free: qdf_mem_free(ani); return ret; } #else int wma_get_ani_level_evt_handler(void *handle, uint8_t *event_buf, uint32_t len) { return 0; } #endif