new file mode 100644
@@ -0,0 +1,349 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include "tx/tx.h"
+#include "sounding.h"
+#include "fw/msg_rx.h"
+#include "fw/msg_tx.h"
+#include "fw/msg_cfm.h"
+#include "stats.h"
+#include "rssi.h"
+#include "fw/fw_dbg.h"
+#include "utils/utils.h"
+#include "hw_assert.h"
+#include "rate_ctrl.h"
+#include "bus/pci/ipc.h"
+#include "rsrc_mgmt.h"
+#ifdef TRACE_SUPPORT
+#include "trace.h"
+#endif
+
+static inline void rx_mm_start_cfm(struct cl_hw *cl_hw, struct cl_ipc_e2a_msg *msg)
+{
+ cl_msg_cfm_clear(cl_hw, msg);
+
+ /* Send indication to the embedded that a new rxbuffer element are ready */
+ cl_hw->ipc_host2xmac_trigger_set(cl_hw->chip, IPC_IRQ_A2E_RXBUF_BACK);
+}
+
+static inline void rx_mm_ba_add_cfm(struct cl_hw *cl_hw, struct cl_ipc_e2a_msg *msg)
+{
+ if (le16_to_cpu(msg->id) == MM_BA_ADD_TX_CFM)
+ cl_msg_cfm_assign_and_clear(cl_hw, msg);
+ else
+ cl_msg_cfm_clear(cl_hw, msg);
+}
+
+static inline void rx_mm_rsrc_mgmt_cfm(struct cl_hw *cl_hw, struct cl_ipc_e2a_msg *msg)
+{
+ struct mm_rsrc_mgmt_cfm *cfm = (struct mm_rsrc_mgmt_cfm *)msg->param;
+
+ cl_rsrc_mgmt_process_cfm(cl_hw, cfm);
+ cl_msg_cfm_clear(cl_hw, msg);
+}
+
+static inline void rx_mm_agg_tx_report_ind(struct cl_hw *cl_hw, struct cl_ipc_e2a_msg *msg)
+{
+ struct cl_agg_tx_report *agg_report = (struct cl_agg_tx_report *)msg->param;
+ struct cl_sta *cl_sta;
+ union cl_rate_ctrl_info rate_ctrl_info;
+
+ /*
+ * Take care of endianness and update gi and format_mod fields of rate
+ * ctrl info in agg_report for the sake of any function that needs to
+ * use them
+ */
+ agg_report->rate_cntrl_info = le32_to_cpu(agg_report->rate_cntrl_info);
+ agg_report->rate_cntrl_info_he = le32_to_cpu(agg_report->rate_cntrl_info_he);
+
+ rate_ctrl_info.word = agg_report->rate_cntrl_info;
+
+ cl_rate_ctrl_convert(&rate_ctrl_info);
+ agg_report->rate_cntrl_info = rate_ctrl_info.word;
+
+ cl_sta_lock(cl_hw);
+ cl_sta = cl_sta_get(cl_hw, agg_report->sta_idx);
+
+ if (cl_sta) {
+ /* TX stats */
+ cl_agg_tx_report_handler(cl_hw, cl_sta, (void *)agg_report);
+ cl_stats_update_tx_agg(cl_hw, cl_sta, agg_report);
+
+ /* RSSI stats */
+ if (!agg_report->ba_not_received)
+ cl_rssi_block_ack_handler(cl_hw, cl_sta, agg_report);
+
+ /*
+ * TODO: Do we need to notify upper layer at agg_report->success?
+ * Ageout may need to reset ageout counter if at least one
+ * frame was success.
+ * May be needed when sending UDP downlink because BA's are not
+ * forwarded to driver.
+ */
+ }
+
+ cl_sta_unlock(cl_hw);
+}
+
+static inline void rx_mm_sounding_ind(struct cl_hw *cl_hw, struct cl_ipc_e2a_msg *msg)
+{
+ struct mm_sounding_ind *ind = (struct mm_sounding_ind *)msg->param;
+
+ cl_sounding_indication(cl_hw, ind);
+}
+
+static inline void rx_mm_fw_error_ind(struct cl_hw *cl_hw, struct cl_ipc_e2a_msg *msg)
+{
+ struct mm_fw_error_ind *ind = (struct mm_fw_error_ind *)msg->param;
+
+ switch (ind->error_type) {
+ case MM_FW_ERROR_TYPE_MU_OFDMA_SLOW_SECONDARY:
+ break;
+ case MM_FW_ERROR_TYPE_MAX:
+ default:
+ cl_dbg_err(cl_hw, "Invalid fw error type %u\n", ind->error_type);
+ break;
+ }
+}
+
+static inline void rx_mm_idle_async_ind(struct cl_hw *cl_hw, struct cl_ipc_e2a_msg *msg)
+{
+ cl_hw->idle_async_set = false;
+
+ cl_dbg_trace(cl_hw, "Clear MM_IDLE_ASYNC_IND\n");
+}
+
+static inline void rx_mm_rsrc_mgmt_ind(struct cl_hw *cl_hw, struct cl_ipc_e2a_msg *msg)
+{
+ cl_rsrc_mgmt_process_ind(cl_hw, (struct mm_rsrc_mgmt_ind *)msg->param);
+}
+
+static inline void rx_dbg_print_ind(struct cl_hw *cl_hw, struct cl_ipc_e2a_msg *msg)
+{
+ cl_hw_assert_print(cl_hw, msg);
+}
+
+static inline void rx_dbg_info_ind(struct cl_hw *cl_hw, struct cl_ipc_e2a_msg *msg)
+{
+ cl_fw_dbg_handler(cl_hw);
+}
+
+static void (*mm_hdlrs[])(struct cl_hw *cl_hw, struct cl_ipc_e2a_msg *msg) = {
+ [MM_RESET_CFM] = cl_msg_cfm_clear,
+ [MM_START_CFM] = rx_mm_start_cfm,
+ [MM_VERSION_CFM] = cl_msg_cfm_assign_and_clear,
+ [MM_ADD_IF_CFM] = cl_msg_cfm_assign_and_clear,
+ [MM_REMOVE_IF_CFM] = cl_msg_cfm_clear,
+ [MM_STA_ADD_CFM] = cl_msg_cfm_assign_and_clear,
+ [MM_STA_DEL_CFM] = cl_msg_cfm_clear,
+ [MM_SET_FILTER_CFM] = cl_msg_cfm_clear,
+ [MM_SET_CHANNEL_CFM] = cl_msg_cfm_clear,
+ [MM_SET_DTIM_CFM] = cl_msg_cfm_clear,
+ [MM_SET_BEACON_INT_CFM] = cl_msg_cfm_clear,
+ [MM_SET_BASIC_RATES_CFM] = cl_msg_cfm_clear,
+ [MM_SET_BSSID_CFM] = cl_msg_cfm_clear,
+ [MM_SET_EDCA_CFM] = cl_msg_cfm_clear,
+ [MM_SET_ASSOCIATED_CFM] = cl_msg_cfm_clear,
+ [MM_SET_SLOTTIME_CFM] = cl_msg_cfm_clear,
+ [MM_SET_IDLE_CFM] = cl_msg_cfm_clear,
+ [MM_KEY_ADD_CFM] = cl_msg_cfm_assign_and_clear,
+ [MM_KEY_DEL_CFM] = cl_msg_cfm_clear,
+ [MM_BA_ADD_TX_CFM] = rx_mm_ba_add_cfm,
+ [MM_BA_ADD_RX_CFM] = rx_mm_ba_add_cfm,
+ [MM_BA_DEL_CFM] = cl_msg_cfm_assign_and_clear,
+ [MM_AVAILABLE_BA_TXQ_CFM] = cl_msg_cfm_assign_and_clear,
+ [MM_UPDATE_RATE_DL_CFM] = cl_msg_cfm_clear,
+ [MM_SET_VNS_CFM] = cl_msg_cfm_clear,
+ [MM_SET_TX_BF_CFM] = cl_msg_cfm_clear,
+ [MM_PHY_RESET_CFM] = cl_msg_cfm_clear,
+ [MM_CONFIG_CCA_CFM] = cl_msg_cfm_clear,
+ [MM_SET_DFS_CFM] = cl_msg_cfm_clear,
+ [MM_SET_ANT_BITMAP_CFM] = cl_msg_cfm_clear,
+ [MM_NDP_TX_CONTROL_CFM] = cl_msg_cfm_clear,
+ [MM_REG_WRITE_CFM] = cl_msg_cfm_clear,
+ [MM_PROT_MODE_CFM] = cl_msg_cfm_clear,
+ [MM_GOTO_POWER_REDUCTION_CFM] = cl_msg_cfm_clear,
+ [MM_SOUNDING_CFM] = cl_msg_cfm_assign_and_clear,
+ [MM_SOUNDING_PAIRING_CFM] = cl_msg_cfm_clear,
+ [MM_SOUNDING_INTERVAL_CFM] = cl_msg_cfm_assign_and_clear,
+ [MM_SOUNDING_STA_SWITCH_CFM] = cl_msg_cfm_assign_and_clear,
+ [MM_BACKUP_BCN_EN_CFM] = cl_msg_cfm_clear,
+ [MM_START_PERIODIC_TX_TIME_CFM] = cl_msg_cfm_clear,
+ [MM_ANAMON_READ_CFM] = cl_msg_cfm_assign_and_clear,
+ [MM_REFRESH_PWR_CFM] = cl_msg_cfm_clear,
+ [MM_SET_ANT_PWR_OFFSET_CFM] = cl_msg_cfm_clear,
+ [MM_SET_RATE_FALLBACK_CFM] = cl_msg_cfm_clear,
+ [MM_TWT_SETUP_CFM] = cl_msg_cfm_clear,
+ [MM_TWT_TEARDOWN_CFM] = cl_msg_cfm_clear,
+ [MM_RSRC_MGMT_CFM] = rx_mm_rsrc_mgmt_cfm,
+ [MM_SET_FREQ_OFFSET_CFM] = cl_msg_cfm_clear,
+ [MM_AGG_TX_REPORT_IND] = rx_mm_agg_tx_report_ind,
+ [MM_SOUNDING_IND] = rx_mm_sounding_ind,
+ [MM_FW_ERROR_IND] = rx_mm_fw_error_ind,
+ [MM_IDLE_ASYNC_IND] = rx_mm_idle_async_ind,
+ [MM_RSRC_MGMT_IND] = rx_mm_rsrc_mgmt_ind,
+ [MM_MAX] = NULL,
+};
+
+#define DBG_MSG_SHIFT(id) ((id) - FIRST_MSG(TASK_DBG))
+
+static void (*dbg_hdlrs[])(struct cl_hw *cl_hw, struct cl_ipc_e2a_msg *msg) = {
+ [DBG_MSG_SHIFT(DBG_SET_MOD_FILTER_CFM)] = cl_msg_cfm_clear,
+ [DBG_MSG_SHIFT(DBG_SET_SEV_FILTER_CFM)] = cl_msg_cfm_clear,
+ [DBG_MSG_SHIFT(DBG_CE_SET_MOD_FILTER_CFM)] = cl_msg_cfm_clear,
+ [DBG_MSG_SHIFT(DBG_BEAMFORMING_TX_CFM)] = cl_msg_cfm_clear,
+ [DBG_MSG_SHIFT(DBG_GET_E2W_STATS_CFM)] = cl_msg_cfm_assign_and_clear,
+ [DBG_MSG_SHIFT(DBG_SET_LA_MPIF_MASK_CFM)] = cl_msg_cfm_clear,
+ [DBG_MSG_SHIFT(DBG_SET_LA_TRIG_POINT_CFM)] = cl_msg_cfm_clear,
+ [DBG_MSG_SHIFT(DBG_SET_LA_MPIF_DEBUG_MODE_CFM)] = cl_msg_cfm_clear,
+ [DBG_MSG_SHIFT(DBG_SET_LA_TRIG_RULE_CFM)] = cl_msg_cfm_clear,
+ [DBG_MSG_SHIFT(DBG_TX_TRACE_DEBUG_FLAG_CFM)] = cl_msg_cfm_clear,
+ [DBG_MSG_SHIFT(DBG_PRINT_STATS_CFM)] = cl_msg_cfm_clear,
+ [DBG_MSG_SHIFT(DBG_TRIGGER_CFM)] = cl_msg_cfm_clear,
+ [DBG_MSG_SHIFT(DBG_TEST_MODE_CFM)] = cl_msg_cfm_clear,
+ [DBG_MSG_SHIFT(DBG_SOUNDING_CMD_CFM)] = cl_msg_cfm_clear,
+ [DBG_MSG_SHIFT(DBG_PRINT_IND)] = rx_dbg_print_ind,
+ [DBG_MSG_SHIFT(DBG_INFO_IND)] = rx_dbg_info_ind,
+ [DBG_MSG_SHIFT(DBG_MAX)] = NULL,
+};
+
+static bool is_dbg_msg(u16 msg_id)
+{
+ return (msg_id >= FIRST_MSG(TASK_DBG) && msg_id < DBG_MAX);
+}
+
+static void cl_msg_rx_run_mm(struct cl_hw *cl_hw, struct cl_ipc_e2a_msg *msg, u16 msg_id)
+{
+ if (msg_id < MM_REQ_CFM_MAX)
+ cl_dbg_trace(cl_hw, "%s\n", msg2str[msg_id]);
+
+ mm_hdlrs[msg_id](cl_hw, msg);
+}
+
+static int cl_msg_rx_run_dbg(struct cl_hw *cl_hw, struct cl_ipc_e2a_msg *msg, u16 msg_id)
+{
+ u16 dbg_id = DBG_MSG_SHIFT(msg_id);
+
+ if (dbg_hdlrs[dbg_id]) {
+ if (msg_id < DBG_REQ_CFM_MAX) {
+ u16 str_id = DBG_STR_SHIFT(msg_id);
+
+ cl_dbg_trace(cl_hw, "%s\n", msg2str[str_id]);
+ }
+
+ dbg_hdlrs[dbg_id](cl_hw, msg);
+ return 0;
+ }
+
+ return -1;
+}
+
+static int cl_msg_rx_run(struct cl_hw *cl_hw, struct cl_ipc_e2a_msg *msg)
+{
+ u16 msg_id = le16_to_cpu(msg->id);
+
+ if (msg_id < MM_MAX && mm_hdlrs[msg_id]) {
+ cl_msg_rx_run_mm(cl_hw, msg, msg_id);
+ return 0;
+ }
+
+ if (is_dbg_msg(msg_id))
+ return cl_msg_rx_run_dbg(cl_hw, msg, msg_id);
+
+ return -1;
+}
+
+static bool is_cfm_msg(u16 msg_id)
+{
+ /* A confirmation must be an odd id */
+ if ((msg_id & 0x1) == 0)
+ return false;
+
+ return ((msg_id < MM_FIRST_IND) || is_dbg_msg(msg_id));
+}
+
+static int cl_msg_rx_handler(struct cl_hw *cl_hw, struct cl_ipc_e2a_msg *msg)
+{
+ int ret = 0;
+ u16 msg_id = le16_to_cpu(msg->id);
+
+#ifdef TRACE_SUPPORT
+ trace_cl_trace_msg_rx_handler_start(cl_hw->idx, msg_id,
+ le32_to_cpu(msg->pattern), cl_hw->cfm_flags);
+#endif
+ /* Relay further actions to the msg parser */
+ ret = cl_msg_rx_run(cl_hw, msg);
+
+ if (ret) {
+ cl_dbg_err(cl_hw, "Unexpected msg (%u)\n", msg_id);
+ } else {
+ /* Wake up the queue in case the msg is a confirmation */
+ if (is_cfm_msg(msg_id))
+ wake_up(&cl_hw->wait_queue);
+ }
+
+#ifdef TRACE_SUPPORT
+ trace_cl_trace_msg_rx_handler_end(cl_hw->idx, cl_hw->cfm_flags);
+#endif
+
+ return ret;
+}
+
+void cl_msg_rx_tasklet(unsigned long data)
+{
+ struct cl_hw *cl_hw = (struct cl_hw *)data;
+ struct cl_ipc_host_env *ipc_env = cl_hw->ipc_env;
+ struct cl_e2a_msg_elem *msg_elem = NULL;
+ struct cl_ipc_e2a_msg *msg = NULL;
+ int msg_handled = 0;
+ u8 idx;
+
+#ifdef TRACE_SUPPORT
+ trace_cl_trace_msg_rx_tasklet_start(cl_hw->idx);
+#endif
+
+ while (1) {
+ idx = ipc_env->e2a_msg_host_idx;
+ msg_elem = (struct cl_e2a_msg_elem *)(ipc_env->e2a_msg_hostbuf_array[idx].hostid);
+ msg = msg_elem->msgbuf_ptr;
+
+ /* Look for pattern which means that this hostbuf has been used for a MSG */
+ if (le32_to_cpu(msg->pattern) != IPC_E2A_MSG_VALID_PATTERN)
+ break;
+
+ cl_msg_rx_handler(cl_hw, msg);
+ msg_handled++;
+
+ /* Reset the msg element and re-use it */
+ msg->pattern = 0;
+
+ /* Make sure memory is written before push to HW */
+ wmb();
+
+ /* Push back the buffer */
+ cl_ipc_msgbuf_push(ipc_env, (ptrdiff_t)msg_elem, msg_elem->dma_addr);
+ }
+
+#ifdef TRACE_SUPPORT
+ trace_cl_trace_msg_rx_tasklet_end(cl_hw->idx, msg_handled);
+#endif
+}
+
+void cl_msg_rx_flush_all(struct cl_hw *cl_hw)
+{
+ int i = 0;
+
+ for (i = FIRST_MSG(TASK_MM); i < MM_MAX; i++) {
+ if (cl_hw->msg_cfm_params[i]) {
+ cl_dbg_verbose(cl_hw, "free MM msg_cfm_params %d\n", i);
+ cl_msg_tx_free_cfm_params(cl_hw, i);
+ }
+ }
+
+ for (i = FIRST_MSG(TASK_DBG); i < DBG_MAX; i++) {
+ if (cl_hw->msg_cfm_params[i]) {
+ cl_dbg_verbose(cl_hw, "free DBG msg_cfm_params %d\n", i);
+ cl_msg_tx_free_cfm_params(cl_hw, i);
+ }
+ }
+}