new file mode 100644
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+#include "fw/msg_tx.h"
+#include "rsrc_mgmt.h"
+#include "rx/rx_amsdu.h"
+#include "prot_mode.h"
+#include "env_det.h"
+
+#ifndef ETYPE2STR
+#define ETYPE2STR(_e) case _e: return # _e
+#endif
+
+static const char *subtype2str(enum mm_rsrc_mgmt_subtype subtype)
+{
+ switch (subtype) {
+ ETYPE2STR(MM_RSRC_MGMT_API_SANITY);
+ ETYPE2STR(MM_RSRC_MGMT_TRAFFIC_START);
+ ETYPE2STR(MM_RSRC_MGMT_TRAFFIC_STOP);
+ ETYPE2STR(MM_RSRC_MGMT_RATES_UPDATE);
+ ETYPE2STR(MM_RSRC_MGMT_CECLI_HINT);
+
+ ETYPE2STR(MM_RSRC_MGMT_ACTION_HINT);
+ ETYPE2STR(MM_RSRC_MGMT_ENV_CHANGE);
+ ETYPE2STR(MM_RSRC_MGMT_STATS);
+ ETYPE2STR(MM_RSRC_MGMT_CONFIG_QUERY);
+ ETYPE2STR(MM_RSRC_MGMT_NOTIF_POLICY_SET);
+ ETYPE2STR(MM_RSRC_MGMT_MAX);
+ default: return "UNKNOWN_SUBTYPE";
+ }
+}
+
+static const char *action2str(enum mm_rsrc_mgmt_action_hint action)
+{
+ switch (action) {
+ ETYPE2STR(MM_RSRC_MGMT_ACTION_ENABLE);
+ ETYPE2STR(MM_RSRC_MGMT_ACTION_DISABLE);
+ ETYPE2STR(MM_RSRC_MGMT_ACTION_RESTORE_DEFAULT);
+ ETYPE2STR(MM_RSRC_MGMT_ACTION_SET);
+ ETYPE2STR(MM_RSRC_MGMT_ACTION_GET);
+ ETYPE2STR(MM_RSRC_MGMT_ACTION_MAX);
+ default: return "UNKNOWN_ACTION";
+ }
+}
+
+static const char *control2str(enum mm_rsrc_mgmt_control_of control_of)
+{
+ switch (control_of) {
+ ETYPE2STR(MM_RSRC_MGMT_OF_TX_AMSDU);
+ ETYPE2STR(MM_RSRC_MGMT_OF_RX_AMSDU);
+ ETYPE2STR(MM_RSRC_MGMT_OF_PROT_MODE);
+ ETYPE2STR(MM_RSRC_MGMT_OF_CCA);
+ default: return "UNKNOWN_CONTROL_OF";
+ }
+}
+
+static inline bool is_in_low_range(s8 th, s8 v)
+{
+ if (th != -1)
+ return th >= v;
+ return true;
+}
+
+static inline bool is_in_high_range(s8 th, s8 v)
+{
+ if (th != -1)
+ return th <= v;
+ return true;
+}
+
+/*
+ * DRV <- FW: notification_policy.
+ *
+ * Notification filters:
+ * 1. on/off (enabled/disabled);
+ * 2. active sta count (lower and upper);
+ * 3. TODO: frequency (once per 100msec etc);
+ * 4. TODO: amount of failures in feedback;
+ */
+static bool must_notify(struct cl_hw *cl_hw, u8 event_type, u8 active_sta_cnt)
+{
+ struct mm_rsrc_mgmt_notif_policy *p = &cl_hw->rsrc_mgmt_db.notif_policies[event_type];
+
+ return p->enabled &&
+ is_in_low_range(p->active_sta.low_th, active_sta_cnt) &&
+ is_in_high_range(p->active_sta.high_th, active_sta_cnt);
+}
+
+/*
+ * Public API
+ */
+void cl_rsrc_mgmt_init(struct cl_hw *cl_hw)
+{
+ BUILD_BUG_ON(TRAFFIC_LEVEL_MAX >
+ BITS_PER_TYPE(typeof_member(struct mm_rsrc_mgmt_notif_policy,
+ level_mask)));
+ BUILD_BUG_ON(TRAFFIC_DIRECTION_MAX >
+ BITS_PER_TYPE(typeof_member(struct mm_rsrc_mgmt_notif_policy,
+ direction_mask)));
+
+ cl_dbg_trace(cl_hw, "RSRC MGMT Init: sizeof(req):%zu, sizeof(ind):%zu, sizeof(cfm):%zu\n",
+ sizeof(struct mm_rsrc_mgmt_req),
+ sizeof(struct mm_rsrc_mgmt_ind),
+ sizeof(struct mm_rsrc_mgmt_cfm));
+}
+
+/*
+ * Direction: DRV -> FW: Requests/Notifications
+ */
+void cl_rsrc_mgmt_traffic_start(struct cl_hw *cl_hw, enum cl_traffic_level level,
+ enum cl_traffic_direction direction)
+{
+ u8 event_type = MM_RSRC_MGMT_TRAFFIC_START;
+ u8 active_sta_cnt = cl_hw->traffic_db.num_active_sta_dir[direction][level];
+
+ if (!must_notify(cl_hw, event_type, active_sta_cnt))
+ return;
+
+ cl_msg_tx_rsrc_mgmt_traffic_event(cl_hw, event_type, level, direction,
+ active_sta_cnt);
+}
+
+void cl_rsrc_mgmt_traffic_stop(struct cl_hw *cl_hw, enum cl_traffic_level level,
+ enum cl_traffic_direction direction)
+{
+ u8 event_type = MM_RSRC_MGMT_TRAFFIC_STOP;
+ u8 active_sta_cnt = cl_hw->traffic_db.num_active_sta_dir[direction][level];
+
+ if (!must_notify(cl_hw, event_type, active_sta_cnt))
+ return;
+
+ cl_msg_tx_rsrc_mgmt_traffic_event(cl_hw, event_type, level, direction,
+ active_sta_cnt);
+}
+
+void cl_rsrc_mgmt_rates_update(struct cl_hw *cl_hw, struct cl_sta *cl_sta)
+{
+ u8 event_type = MM_RSRC_MGMT_RATES_UPDATE;
+ u8 active_sta_cnt = cl_traffic_num_active_sta(cl_hw);
+
+ if (!must_notify(cl_hw, event_type, active_sta_cnt))
+ return;
+
+ cl_msg_tx_rsrc_mgmt_rates_event(cl_hw, event_type, cl_sta);
+}
+
+void cl_rsrc_mgmt_process_cfm(struct cl_hw *cl_hw, struct mm_rsrc_mgmt_cfm *cfm)
+{
+ cl_dbg_trace(cl_hw, "CFM: subtype:%s(%u), status:%d\n",
+ subtype2str(cfm->subtype), cfm->subtype, cfm->status);
+}
+
+/*
+ * DRV <- FW: Indications processings
+ */
+static void process_env_change(struct cl_hw *cl_hw, enum cl_env_type type)
+{
+ cl_env_det_set_type(cl_hw, type);
+}
+
+static void process_notif_policy(struct cl_hw *cl_hw, u8 subtype,
+ struct mm_rsrc_mgmt_notif_policy *np)
+{
+ struct cl_rsrc_mgmt_db *db = &cl_hw->rsrc_mgmt_db;
+
+ cl_dbg_trace(cl_hw, "[%s(%u)] enabled:%u, sta.low_th:%d, sta.high_th:%d\n",
+ subtype2str(subtype), subtype, np->enabled,
+ np->active_sta.low_th, np->active_sta.high_th);
+
+ if (subtype < ARRAY_SIZE(db->notif_policies))
+ db->notif_policies[subtype] = *np;
+ else
+ cl_dbg_err(cl_hw, "Invalid subtype index:%s(%u)\n",
+ subtype2str(subtype), subtype);
+}
+
+static void process_action_hint(struct cl_hw *cl_hw, uint16_t target, uint8_t action)
+{
+ int rc = 0;
+
+ cl_dbg_info(cl_hw, "Processing hint: target - %s(%u), action - %s(%u)\n",
+ control2str(target), target, action2str(action), action);
+
+ switch (target) {
+ case MM_RSRC_MGMT_OF_TX_AMSDU:
+ switch (action) {
+ case MM_RSRC_MGMT_ACTION_ENABLE:
+ cl_hw->txamsdu_en = 1;
+ break;
+ case MM_RSRC_MGMT_ACTION_DISABLE:
+ cl_hw->txamsdu_en = 0;
+ break;
+ case MM_RSRC_MGMT_ACTION_RESTORE_DEFAULT:
+ cl_hw->txamsdu_en = cl_hw->conf->ce_txamsdu_en;
+ break;
+ default:
+ rc = -EOPNOTSUPP;
+ };
+ break;
+ case MM_RSRC_MGMT_OF_RX_AMSDU:
+ switch (action) {
+ case MM_RSRC_MGMT_ACTION_ENABLE:
+ cl_rx_amsdu_hw_en(cl_hw->hw, true);
+ break;
+ case MM_RSRC_MGMT_ACTION_DISABLE:
+ cl_rx_amsdu_hw_en(cl_hw->hw, false);
+ break;
+ case MM_RSRC_MGMT_ACTION_RESTORE_DEFAULT:
+ cl_rx_amsdu_hw_en(cl_hw->hw, cl_hw->conf->ce_rxamsdu_en);
+ break;
+ default:
+ rc = -EOPNOTSUPP;
+ };
+ break;
+ case MM_RSRC_MGMT_OF_PROT_MODE:
+ switch (action) {
+ case MM_RSRC_MGMT_ACTION_ENABLE:
+ cl_prot_mode_enable(cl_hw);
+ break;
+ case MM_RSRC_MGMT_ACTION_DISABLE:
+ cl_prot_mode_disable(cl_hw);
+ break;
+ case MM_RSRC_MGMT_ACTION_RESTORE_DEFAULT:
+ cl_prot_mode_restore_default(cl_hw);
+ break;
+ default:
+ rc = -EOPNOTSUPP;
+ };
+ break;
+ case MM_RSRC_MGMT_OF_CCA:
+ switch (action) {
+ case MM_RSRC_MGMT_ACTION_DISABLE:
+ cl_msg_tx_config_cca(cl_hw, false);
+ break;
+ case MM_RSRC_MGMT_ACTION_ENABLE:
+ case MM_RSRC_MGMT_ACTION_RESTORE_DEFAULT:
+ cl_msg_tx_config_cca(cl_hw, true);
+ break;
+ default:
+ rc = -EOPNOTSUPP;
+ };
+ break;
+ default:
+ rc = -EOPNOTSUPP;
+ break;
+ };
+
+ if (rc) {
+ cl_dbg_err(cl_hw, "Hint process failure, rc:%d [%s(%u):%s(%u)]\n",
+ rc, control2str(target), target, action2str(action),
+ action);
+ }
+}
+
+void cl_rsrc_mgmt_process_ind(struct cl_hw *cl_hw, struct mm_rsrc_mgmt_ind *ind)
+{
+ switch (ind->subtype) {
+ case MM_RSRC_MGMT_ACTION_HINT:
+ process_action_hint(cl_hw,
+ le16_to_cpu(ind->u.action_hint.target),
+ ind->u.action_hint.action);
+ break;
+ case MM_RSRC_MGMT_ENV_CHANGE:
+ process_env_change(cl_hw, ind->u.env_event.state);
+ break;
+ case MM_RSRC_MGMT_NOTIF_POLICY_SET:
+ process_notif_policy(cl_hw,
+ ind->u.notif_policy_set.subtype,
+ &ind->u.notif_policy_set.settings);
+ break;
+ case MM_RSRC_MGMT_STATS:
+ case MM_RSRC_MGMT_CONFIG_QUERY:
+ case MM_RSRC_MGMT_MAX:
+ default:
+ cl_dbg_err(cl_hw, "Invalid subtype %s(%u)\n",
+ subtype2str(ind->subtype), ind->subtype);
+ break;
+ }
+}
+