/* * Copyright (c) 2021, The Linux Foundation. All rights reserved. * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * DOC: target_if_mgmt_txrx_rx_reo.c * This file contains definitions of management rx re-ordering related APIs. */ #include #include #include #include #include #include #include #include #include /** * target_if_mgmt_rx_reo_fw_consumed_event_handler() - WMI event handler to * process MGMT Rx FW consumed event handler * @scn: Pointer to scn object * @data_buf: Pointer to event buffer * @data_len: Length of event buffer * * Return: 0 for success, else failure */ static int target_if_mgmt_rx_reo_fw_consumed_event_handler( ol_scn_t scn, uint8_t *data, uint32_t datalen) { struct wlan_objmgr_psoc *psoc; struct wlan_objmgr_pdev *pdev; struct wmi_unified *wmi_handle; QDF_STATUS status; struct mgmt_rx_reo_params params; struct wlan_lmac_if_mgmt_rx_reo_rx_ops *mgmt_rx_reo_rx_ops; psoc = target_if_get_psoc_from_scn_hdl(scn); if (!psoc) { mgmt_rx_reo_err("null psoc"); return -EINVAL; } wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); if (!wmi_handle) { mgmt_rx_reo_err("wmi_handle is NULL"); return -EINVAL; } status = wmi_extract_mgmt_rx_fw_consumed(wmi_handle, data, ¶ms); if (QDF_IS_STATUS_ERROR(status)) { mgmt_rx_reo_err("Failed to extract mgmt rx params"); return -EINVAL; } mgmt_rx_reo_rx_ops = target_if_mgmt_rx_reo_get_rx_ops(psoc); if (!mgmt_rx_reo_rx_ops) { mgmt_rx_reo_err("rx_ops of MGMT Rx REO module is NULL"); return -EINVAL; } if (!mgmt_rx_reo_rx_ops->fw_consumed_event_handler) { mgmt_rx_reo_err("FW consumed event handler is NULL"); return -EINVAL; } /* Take the pdev reference */ pdev = wlan_objmgr_get_pdev_by_id(psoc, params.pdev_id, WLAN_MGMT_SB_ID); if (!pdev) { mgmt_rx_reo_err("Couldn't get pdev for pdev_id: %d" "on psoc: %pK", params.pdev_id, psoc); return -EINVAL; } status = mgmt_rx_reo_rx_ops->fw_consumed_event_handler(pdev, ¶ms); if (QDF_IS_STATUS_ERROR(status)) { mgmt_rx_reo_err("FW consumed event handling failed"); wlan_objmgr_pdev_release_ref(pdev, WLAN_MGMT_SB_ID); return -EINVAL; } wlan_objmgr_pdev_release_ref(pdev, WLAN_MGMT_SB_ID); return 0; } QDF_STATUS target_if_mgmt_rx_reo_register_event_handlers(struct wlan_objmgr_psoc *psoc) { struct wmi_unified *wmi_handle; QDF_STATUS status; wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); if (!wmi_handle) { mgmt_rx_reo_err("Invalid WMI handle"); return QDF_STATUS_E_INVAL; } status = wmi_unified_register_event_handler( wmi_handle, wmi_mgmt_rx_fw_consumed_eventid, target_if_mgmt_rx_reo_fw_consumed_event_handler, WMI_RX_UMAC_CTX); if (QDF_IS_STATUS_ERROR(status)) { mgmt_rx_reo_err("Register Rx FW consumed event cb errcode %d", status); if (status == QDF_STATUS_E_NOSUPPORT) status = QDF_STATUS_SUCCESS; } return status; } QDF_STATUS target_if_mgmt_rx_reo_unregister_event_handlers(struct wlan_objmgr_psoc *psoc) { struct wmi_unified *wmi_handle; QDF_STATUS status; wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); if (!wmi_handle) { mgmt_rx_reo_err("Invalid WMI handle"); return QDF_STATUS_E_INVAL; } status = wmi_unified_unregister_event_handler( wmi_handle, wmi_mgmt_rx_fw_consumed_eventid); if (QDF_IS_STATUS_ERROR(status)) { mgmt_rx_reo_err("Unregister Rx FW consumed event cb errcode %d", status); if (status == QDF_STATUS_E_NOSUPPORT) status = QDF_STATUS_SUCCESS; } return status; } /** * target_if_mgmt_rx_reo_get_num_active_hw_links() - Get number of active MLO HW * links * @psoc: Pointer to psoc object * @num_active_hw_links: pointer to number of active MLO HW links * * Get number of active MLO HW links from the MLO global shared memory arena. * * Return: QDF_STATUS */ static QDF_STATUS target_if_mgmt_rx_reo_get_num_active_hw_links(struct wlan_objmgr_psoc *psoc, int8_t *num_active_hw_links) { struct wlan_lmac_if_mgmt_rx_reo_low_level_ops *low_level_ops; uint8_t grp_id; if (!psoc) { mgmt_rx_reo_err("psoc is null"); return QDF_STATUS_E_NULL_VALUE; } if (!num_active_hw_links) { mgmt_rx_reo_err("Pointer to num_active_hw_links is null"); return QDF_STATUS_E_NULL_VALUE; } if (!mlo_psoc_get_grp_id(psoc, &grp_id)) { mgmt_rx_reo_err("Failed to get valid MLO Group id"); return QDF_STATUS_E_INVAL; } low_level_ops = target_if_get_mgmt_rx_reo_low_level_ops(psoc); if (!low_level_ops) { mgmt_rx_reo_err("Low level ops of MGMT Rx REO is null"); return QDF_STATUS_E_NULL_VALUE; } qdf_assert_always(low_level_ops->implemented); *num_active_hw_links = low_level_ops->get_num_links(grp_id); return QDF_STATUS_SUCCESS; } /** * target_if_mgmt_rx_reo_get_valid_hw_link_bitmap() - Get valid MLO HW link * bitmap * @psoc: Pointer to psoc object * @valid_hw_link_bitmap: Pointer to valid MLO HW link bitmap * * Get valid MLO HW link bitmap from the MLO global shared memory arena. * * Return: QDF_STATUS */ QDF_STATUS target_if_mgmt_rx_reo_get_valid_hw_link_bitmap(struct wlan_objmgr_psoc *psoc, uint16_t *valid_hw_link_bitmap) { struct wlan_lmac_if_mgmt_rx_reo_low_level_ops *low_level_ops; uint8_t grp_id; if (!psoc) { mgmt_rx_reo_err("psoc is null"); return QDF_STATUS_E_NULL_VALUE; } if (!valid_hw_link_bitmap) { mgmt_rx_reo_err("Pointer to valid_hw_link_bitmap is null"); return QDF_STATUS_E_NULL_VALUE; } if (!mlo_psoc_get_grp_id(psoc, &grp_id)) { mgmt_rx_reo_err("Failed to get valid MLO Group id"); return QDF_STATUS_E_INVAL; } low_level_ops = target_if_get_mgmt_rx_reo_low_level_ops(psoc); if (!low_level_ops) { mgmt_rx_reo_err("Low level ops of MGMT Rx REO is null"); return QDF_STATUS_E_NULL_VALUE; } qdf_assert_always(low_level_ops->implemented); *valid_hw_link_bitmap = low_level_ops->get_valid_link_bitmap(grp_id); return QDF_STATUS_SUCCESS; } /** * target_if_mgmt_rx_reo_read_snapshot_raw() - Read raw value of management * rx-reorder snapshot * @snapshot_address: snapshot address * @mgmt_rx_reo_snapshot_low: Pointer to lower 32 bits of snapshot value * @mgmt_rx_reo_snapshot_high: Pointer to higher 32 bits of snapshot value * @snapshot_version: snapshot version * * Read raw value of management rx-reorder snapshots. * * Return: QDF_STATUS */ static QDF_STATUS target_if_mgmt_rx_reo_read_snapshot_raw (struct mgmt_rx_reo_shared_snapshot *snapshot_address, uint32_t *mgmt_rx_reo_snapshot_low, uint32_t *mgmt_rx_reo_snapshot_high, uint8_t snapshot_version, struct mgmt_rx_reo_shared_snapshot *raw_snapshot) { uint32_t prev_snapshot_low; uint32_t prev_snapshot_high; uint32_t cur_snapshot_low; uint32_t cur_snapshot_high; uint8_t retry_count = 0; if (snapshot_version == 1) { *mgmt_rx_reo_snapshot_low = snapshot_address->mgmt_rx_reo_snapshot_low; *mgmt_rx_reo_snapshot_high = snapshot_address->mgmt_rx_reo_snapshot_high; raw_snapshot->mgmt_rx_reo_snapshot_low = *mgmt_rx_reo_snapshot_low; raw_snapshot->mgmt_rx_reo_snapshot_high = *mgmt_rx_reo_snapshot_high; return QDF_STATUS_SUCCESS; } prev_snapshot_low = snapshot_address->mgmt_rx_reo_snapshot_low; prev_snapshot_high = snapshot_address->mgmt_rx_reo_snapshot_high; raw_snapshot->mgmt_rx_reo_snapshot_low = prev_snapshot_low; raw_snapshot->mgmt_rx_reo_snapshot_high = prev_snapshot_high; for (; retry_count < (MGMT_RX_REO_SNAPSHOT_B2B_READ_SWAR_RETRY_LIMIT - 1); retry_count++) { cur_snapshot_low = snapshot_address->mgmt_rx_reo_snapshot_low; cur_snapshot_high = snapshot_address->mgmt_rx_reo_snapshot_high; raw_snapshot[retry_count + 1].mgmt_rx_reo_snapshot_low = cur_snapshot_low; raw_snapshot[retry_count + 1].mgmt_rx_reo_snapshot_high = cur_snapshot_high; if (prev_snapshot_low == cur_snapshot_low && prev_snapshot_high == cur_snapshot_high) break; prev_snapshot_low = cur_snapshot_low; prev_snapshot_high = cur_snapshot_high; } qdf_assert_always(retry_count != (MGMT_RX_REO_SNAPSHOT_B2B_READ_SWAR_RETRY_LIMIT - 1)); *mgmt_rx_reo_snapshot_low = cur_snapshot_low; *mgmt_rx_reo_snapshot_high = cur_snapshot_high; return QDF_STATUS_SUCCESS; } /** * target_if_mgmt_rx_reo_read_snapshot() - Read management rx-reorder snapshot * @pdev: pdev pointer * @snapshot_info: Snapshot info * @id: Snapshot ID * @snapshot_value: Pointer to snapshot value * * Read management rx-reorder snapshots from target. * * Return: QDF_STATUS */ static QDF_STATUS target_if_mgmt_rx_reo_read_snapshot( struct wlan_objmgr_pdev *pdev, struct mgmt_rx_reo_snapshot_info *snapshot_info, enum mgmt_rx_reo_shared_snapshot_id id, struct mgmt_rx_reo_snapshot_params *snapshot_value, struct mgmt_rx_reo_shared_snapshot (*raw_snapshot) [MGMT_RX_REO_SNAPSHOT_B2B_READ_SWAR_RETRY_LIMIT]) { bool snapshot_valid; uint16_t mgmt_pkt_ctr; uint32_t global_timestamp; uint32_t mgmt_rx_reo_snapshot_low; uint32_t mgmt_rx_reo_snapshot_high; uint8_t retry_count; QDF_STATUS status; struct wlan_lmac_if_mgmt_rx_reo_low_level_ops *low_level_ops; struct mgmt_rx_reo_shared_snapshot *snapshot_address; uint8_t snapshot_version; if (!snapshot_info) { mgmt_rx_reo_err("Mgmt Rx REO snapshot info null"); return QDF_STATUS_E_INVAL; } snapshot_address = snapshot_info->address; if (!snapshot_address) { mgmt_rx_reo_err("Mgmt Rx REO snapshot address null"); return QDF_STATUS_E_INVAL; } snapshot_version = snapshot_info->version; if (!snapshot_value) { mgmt_rx_reo_err("Mgmt Rx REO snapshot null"); return QDF_STATUS_E_INVAL; } qdf_mem_zero(snapshot_value, sizeof(*snapshot_value)); low_level_ops = target_if_get_mgmt_rx_reo_low_level_ops( wlan_pdev_get_psoc(pdev)); if (!low_level_ops) { mgmt_rx_reo_err("Low level ops of MGMT Rx REO is null"); return QDF_STATUS_E_FAILURE; } /* Make sure that function pointers are populated */ qdf_assert_always(low_level_ops->implemented); switch (id) { case MGMT_RX_REO_SHARED_SNAPSHOT_MAC_HW: case MGMT_RX_REO_SHARED_SNAPSHOT_FW_CONSUMED: case MGMT_RX_REO_SHARED_SNAPSHOT_FW_FORWARDED: retry_count = 0; for (; retry_count < MGMT_RX_REO_SNAPSHOT_READ_RETRY_LIMIT; retry_count++) { status = target_if_mgmt_rx_reo_read_snapshot_raw (snapshot_address, &mgmt_rx_reo_snapshot_low, &mgmt_rx_reo_snapshot_high, snapshot_version, raw_snapshot[retry_count]); if (QDF_IS_STATUS_ERROR(status)) { mgmt_rx_reo_err("Failed to read snapshot %d", id); return QDF_STATUS_E_FAILURE; } snapshot_valid = low_level_ops->snapshot_is_valid( mgmt_rx_reo_snapshot_low, snapshot_version); if (!snapshot_valid) { mgmt_rx_reo_info("Invalid REO snapshot value"); snapshot_value->valid = false; snapshot_value->mgmt_pkt_ctr = low_level_ops->snapshot_get_mgmt_pkt_ctr (mgmt_rx_reo_snapshot_low, snapshot_version); snapshot_value->global_timestamp = low_level_ops->snapshot_get_global_timestamp (mgmt_rx_reo_snapshot_low, mgmt_rx_reo_snapshot_high, snapshot_version); snapshot_value->retry_count = retry_count + 1; return QDF_STATUS_SUCCESS; } if (low_level_ops->snapshot_is_consistent (mgmt_rx_reo_snapshot_low, mgmt_rx_reo_snapshot_high, snapshot_version)) { global_timestamp = low_level_ops->snapshot_get_global_timestamp (mgmt_rx_reo_snapshot_low, mgmt_rx_reo_snapshot_high, snapshot_version); mgmt_pkt_ctr = low_level_ops->snapshot_get_mgmt_pkt_ctr (mgmt_rx_reo_snapshot_low, snapshot_version); break; } mgmt_rx_reo_info("Inconsistent snapshot %d, version=%u, low=0x%x, high=0x%x, retry=%u", id, snapshot_version, mgmt_rx_reo_snapshot_low, mgmt_rx_reo_snapshot_high, retry_count); } if (retry_count == MGMT_RX_REO_SNAPSHOT_READ_RETRY_LIMIT) { mgmt_rx_reo_err("Read retry limit, id = %d, ver = %u", id, snapshot_version); snapshot_value->valid = false; snapshot_value->mgmt_pkt_ctr = 0xFFFF; snapshot_value->global_timestamp = 0xFFFFFFFF; snapshot_value->retry_count = retry_count; qdf_assert_always(0); return QDF_STATUS_E_FAILURE; } snapshot_value->valid = true; snapshot_value->mgmt_pkt_ctr = mgmt_pkt_ctr; snapshot_value->global_timestamp = global_timestamp; snapshot_value->retry_count = retry_count + 1; status = QDF_STATUS_SUCCESS; break; default: mgmt_rx_reo_err("Invalid snapshot id %d", id); status = QDF_STATUS_E_INVAL; break; } return status; } /** * target_if_mgmt_rx_reo_get_snapshot_info() - Get information related to * management rx-reorder snapshot * @pdev: Pointer to pdev object * @id: Snapshot ID * @snapshot_info: Pointer to snapshot info * * Return: QDF_STATUS */ static QDF_STATUS target_if_mgmt_rx_reo_get_snapshot_info (struct wlan_objmgr_pdev *pdev, enum mgmt_rx_reo_shared_snapshot_id id, struct mgmt_rx_reo_snapshot_info *snapshot_info) { struct wlan_lmac_if_mgmt_rx_reo_low_level_ops *low_level_ops; int8_t link_id; int8_t snapshot_version; uint8_t grp_id; struct wlan_objmgr_psoc *psoc; if (!pdev) { mgmt_rx_reo_err("pdev is null"); return QDF_STATUS_E_NULL_VALUE; } psoc = wlan_pdev_get_psoc(pdev); if (!psoc) { mgmt_rx_reo_err("psoc is null"); return QDF_STATUS_E_NULL_VALUE; } if (id >= MGMT_RX_REO_SHARED_SNAPSHOT_MAX) { mgmt_rx_reo_err("Mgmt RX REO snapshot id invalid %d", id); return QDF_STATUS_E_INVAL; } if (!snapshot_info) { mgmt_rx_reo_err("Ref to mgmt RX REO snapshot info is null"); return QDF_STATUS_E_NULL_VALUE; } if (!mlo_psoc_get_grp_id(psoc, &grp_id)) { mgmt_rx_reo_err("Failed to get valid MLO Group id"); return QDF_STATUS_E_INVAL; } low_level_ops = target_if_get_mgmt_rx_reo_low_level_ops(psoc); if (!low_level_ops) { mgmt_rx_reo_err("Low level ops of MGMT Rx REO is null"); return QDF_STATUS_E_FAILURE; } qdf_assert_always(low_level_ops->implemented); link_id = wlan_get_mlo_link_id_from_pdev(pdev); qdf_assert_always(link_id >= 0); snapshot_info->address = low_level_ops->get_snapshot_address(grp_id, link_id, id); snapshot_version = low_level_ops->get_snapshot_version(grp_id, id); if (snapshot_version < 0) { mgmt_rx_reo_err("Invalid snapshot version %d MLO Group id %d", snapshot_version, grp_id); return QDF_STATUS_E_INVAL; } snapshot_info->version = snapshot_version; return QDF_STATUS_SUCCESS; } /** * target_if_mgmt_rx_reo_filter_config() - Configure MGMT Rx REO filter * @pdev: Pointer to pdev objmgr * @filter: Pointer to MGMT Rx REO filter * * Return: QDF_STATUS_SUCCESS for success or error code */ static QDF_STATUS target_if_mgmt_rx_reo_filter_config( struct wlan_objmgr_pdev *pdev, struct mgmt_rx_reo_filter *filter) { QDF_STATUS status; struct wmi_unified *wmi_handle; uint8_t pdev_id; wmi_handle = lmac_get_pdev_wmi_handle(pdev); if (!wmi_handle) { mgmt_rx_reo_err("Invalid WMI handle"); return QDF_STATUS_E_INVAL; } pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev); status = wmi_unified_mgmt_rx_reo_filter_config_cmd(wmi_handle, pdev_id, filter); if (QDF_IS_STATUS_ERROR(status)) mgmt_rx_reo_err("Unable to send MGMT Rx REO Filter config cmd"); return status; } QDF_STATUS target_if_mgmt_rx_reo_extract_reo_params( wmi_unified_t wmi_handle, void *evt_buf, struct mgmt_rx_event_params *params) { struct wlan_objmgr_psoc *psoc; if (!wmi_handle) { mgmt_rx_reo_err("wmi_handle is null"); return QDF_STATUS_E_NULL_VALUE; } psoc = target_if_get_psoc_from_scn_hdl(wmi_handle->scn_handle); if (!psoc) { mgmt_rx_reo_err("null psoc"); return QDF_STATUS_E_NULL_VALUE; } /* If REO feature is not enabled in FW, no need to extract REO params */ if (!wlan_psoc_nif_feat_cap_get(psoc, WLAN_SOC_F_MGMT_RX_REO_CAPABLE)) return QDF_STATUS_SUCCESS; if (!params) { mgmt_rx_reo_err("MGMT Rx event parameters is NULL"); return QDF_STATUS_E_NULL_VALUE; } return wmi_extract_mgmt_rx_reo_params(wmi_handle, evt_buf, params->reo_params); } QDF_STATUS target_if_mgmt_rx_reo_tx_ops_register( struct wlan_lmac_if_mgmt_txrx_tx_ops *mgmt_txrx_tx_ops) { struct wlan_lmac_if_mgmt_rx_reo_tx_ops *mgmt_rx_reo_tx_ops; if (!mgmt_txrx_tx_ops) { mgmt_rx_reo_err("mgmt_txrx txops NULL"); return QDF_STATUS_E_FAILURE; } mgmt_rx_reo_tx_ops = &mgmt_txrx_tx_ops->mgmt_rx_reo_tx_ops; mgmt_rx_reo_tx_ops->get_num_active_hw_links = target_if_mgmt_rx_reo_get_num_active_hw_links; mgmt_rx_reo_tx_ops->get_valid_hw_link_bitmap = target_if_mgmt_rx_reo_get_valid_hw_link_bitmap; mgmt_rx_reo_tx_ops->read_mgmt_rx_reo_snapshot = target_if_mgmt_rx_reo_read_snapshot; mgmt_rx_reo_tx_ops->get_mgmt_rx_reo_snapshot_info = target_if_mgmt_rx_reo_get_snapshot_info; mgmt_rx_reo_tx_ops->mgmt_rx_reo_filter_config = target_if_mgmt_rx_reo_filter_config; return QDF_STATUS_SUCCESS; } QDF_STATUS target_if_mgmt_rx_reo_host_drop_handler(struct wlan_objmgr_pdev *pdev, struct mgmt_rx_event_params *params) { struct wlan_lmac_if_mgmt_rx_reo_rx_ops *mgmt_rx_reo_rx_ops; if (!pdev) { mgmt_rx_reo_err("pdev is null"); return QDF_STATUS_E_NULL_VALUE; } if (!params) { mgmt_rx_reo_err("mgmt rx event params are null"); return QDF_STATUS_E_NULL_VALUE; } mgmt_rx_reo_rx_ops = target_if_mgmt_rx_reo_get_rx_ops( wlan_pdev_get_psoc(pdev)); if (!mgmt_rx_reo_rx_ops) { mgmt_rx_reo_err("rx_ops of MGMT Rx REO module is NULL"); return QDF_STATUS_E_NULL_VALUE; } return mgmt_rx_reo_rx_ops->host_drop_handler(pdev, params->reo_params); }