|
@@ -0,0 +1,526 @@
|
|
|
+/*
|
|
|
+ * Copyright (c) 2023 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: contains EPCS (Emergency Preparedness Communications Service)
|
|
|
+ * related functionality
|
|
|
+ */
|
|
|
+#include <wlan_cmn.h>
|
|
|
+#include <wlan_cm_public_struct.h>
|
|
|
+#include "wlan_epcs_api.h"
|
|
|
+#include <wlan_mlo_epcs.h>
|
|
|
+#include "wlan_cm_api.h"
|
|
|
+#include "wlan_mlo_mgr_roam.h"
|
|
|
+#include "wlan_cmn_ieee80211.h"
|
|
|
+#include "dot11f.h"
|
|
|
+
|
|
|
+#define EPCS_MIN_DIALOG_TOKEN 1
|
|
|
+#define EPCS_MAX_DIALOG_TOKEN 0xFF
|
|
|
+
|
|
|
+static struct ac_param_record default_epcs_edca[] = {
|
|
|
+#ifndef ANI_LITTLE_BIT_ENDIAN
|
|
|
+ /* The txop is multiple of 32us units */
|
|
|
+ {0x07, 0x95, 79 /* 2.528ms */},
|
|
|
+ {0x03, 0x95, 79 /* 2.528ms */},
|
|
|
+ {0x02, 0x54, 128 /* 4.096ms */},
|
|
|
+ {0x02, 0x43, 65 /* 2.080ms */}
|
|
|
+#else
|
|
|
+ {0x70, 0x59, 79 /* 2.528ms */},
|
|
|
+ {0x30, 0x59, 79 /* 2.528ms */},
|
|
|
+ {0x20, 0x45, 128 /* 4.096ms */},
|
|
|
+ {0x20, 0x34, 65 /* 2.080ms */}
|
|
|
+#endif
|
|
|
+};
|
|
|
+
|
|
|
+static
|
|
|
+const char *epcs_get_event_str(enum wlan_epcs_evt event)
|
|
|
+{
|
|
|
+ if (event > WLAN_EPCS_EV_ACTION_FRAME_MAX)
|
|
|
+ return "";
|
|
|
+
|
|
|
+ switch (event) {
|
|
|
+ CASE_RETURN_STRING(WLAN_EPCS_EV_ACTION_FRAME_RX_REQ);
|
|
|
+ CASE_RETURN_STRING(WLAN_EPCS_EV_ACTION_FRAME_TX_RESP);
|
|
|
+ CASE_RETURN_STRING(WLAN_EPCS_EV_ACTION_FRAME_TX_REQ);
|
|
|
+ CASE_RETURN_STRING(WLAN_EPCS_EV_ACTION_FRAME_RX_RESP);
|
|
|
+ CASE_RETURN_STRING(WLAN_EPCS_EV_ACTION_FRAME_RX_TEARDOWN);
|
|
|
+ CASE_RETURN_STRING(WLAN_EPCS_EV_ACTION_FRAME_TX_TEARDOWN);
|
|
|
+ default:
|
|
|
+ return "Unknown";
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static uint8_t
|
|
|
+epcs_gen_dialog_token(struct wlan_mlo_peer_epcs_info *epcs_info)
|
|
|
+{
|
|
|
+ if (!epcs_info)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (epcs_info->self_gen_dialog_token == EPCS_MAX_DIALOG_TOKEN)
|
|
|
+ /* wrap is ok */
|
|
|
+ epcs_info->self_gen_dialog_token = EPCS_MIN_DIALOG_TOKEN;
|
|
|
+ else
|
|
|
+ epcs_info->self_gen_dialog_token += 1;
|
|
|
+
|
|
|
+ mlme_debug("gen dialog token %d", epcs_info->self_gen_dialog_token);
|
|
|
+ return epcs_info->self_gen_dialog_token;
|
|
|
+}
|
|
|
+
|
|
|
+static void epcs_update_ac_value(tSirMacEdcaParamRecord *edca,
|
|
|
+ struct ac_param_record *epcs)
|
|
|
+{
|
|
|
+ edca->aci.rsvd = epcs->aci_aifsn >> RSVD_SHIFT_BIT & RSVD_MASK;
|
|
|
+ edca->aci.aci = epcs->aci_aifsn >> ACI_SHIFT_BIT & ACI_MASK;
|
|
|
+ edca->aci.acm = epcs->aci_aifsn >> ACM_SHIFT_BIT & ACM_MASK;
|
|
|
+ edca->aci.aifsn = epcs->aci_aifsn >> AIFSN_SHIFT_BIT & AIFSN_MASK;
|
|
|
+
|
|
|
+ edca->cw.max = epcs->ecw_min_max >> CWMAX_SHIFT_BIT & CWMAX_MASK;
|
|
|
+ edca->cw.min = epcs->ecw_min_max >> CWMIN_SHIFT_BIT & CWMIN_MASK;
|
|
|
+
|
|
|
+ edca->txoplimit = epcs->txop_limit;
|
|
|
+ mlme_debug("edca rsvd %d, aci %d, acm %d, aifsn %d, cwmax %d, cwmin %d",
|
|
|
+ edca->aci.rsvd, edca->aci.aci, edca->aci.acm,
|
|
|
+ edca->aci.aifsn, edca->cw.max, edca->cw.min);
|
|
|
+}
|
|
|
+
|
|
|
+static void epcs_update_mu_ac_value(tSirMacEdcaParamRecord *edca,
|
|
|
+ struct muac_param_record *epcs)
|
|
|
+{
|
|
|
+ edca->aci.rsvd = epcs->aci_aifsn >> RSVD_SHIFT_BIT & RSVD_MASK;
|
|
|
+ edca->aci.aci = epcs->aci_aifsn >> ACI_SHIFT_BIT & ACI_MASK;
|
|
|
+ edca->aci.acm = epcs->aci_aifsn >> ACM_SHIFT_BIT & ACM_MASK;
|
|
|
+ edca->aci.aifsn = epcs->aci_aifsn >> AIFSN_SHIFT_BIT & AIFSN_MASK;
|
|
|
+
|
|
|
+ edca->cw.max = epcs->ecw_min_max >> CWMAX_SHIFT_BIT & CWMAX_MASK;
|
|
|
+ edca->cw.min = epcs->ecw_min_max >> CWMIN_SHIFT_BIT & CWMIN_MASK;
|
|
|
+
|
|
|
+ edca->txoplimit = epcs->mu_edca_timer;
|
|
|
+ mlme_debug("muac rsvd %d, aci %d, acm %d, aifsn %d, cwmax %d, cwmin %d",
|
|
|
+ edca->aci.rsvd, edca->aci.aci, edca->aci.acm,
|
|
|
+ edca->aci.aifsn, edca->cw.max, edca->cw.min);
|
|
|
+}
|
|
|
+
|
|
|
+static QDF_STATUS
|
|
|
+epcs_update_def_edca_param(struct wlan_objmgr_vdev *vdev,
|
|
|
+ struct mac_context *mac_ctx,
|
|
|
+ tSirMacEdcaParamRecord *edca)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < QCA_WLAN_AC_ALL; i++) {
|
|
|
+ epcs_update_ac_value(&edca[i], &default_epcs_edca[i]);
|
|
|
+ edca[i].no_ack = mac_ctx->no_ack_policy_cfg[i];
|
|
|
+ }
|
|
|
+
|
|
|
+ return lim_send_epcs_update_edca_params(vdev, edca, false);
|
|
|
+}
|
|
|
+
|
|
|
+static QDF_STATUS
|
|
|
+epcs_update_edca_param(struct wlan_objmgr_vdev *vdev,
|
|
|
+ struct edca_ie *edca_ie)
|
|
|
+{
|
|
|
+ struct mac_context *mac_ctx;
|
|
|
+ struct ac_param_record *ac_record;
|
|
|
+ tSirMacEdcaParamRecord edca[QCA_WLAN_AC_ALL] = {0};
|
|
|
+ int i;
|
|
|
+
|
|
|
+ mac_ctx = cds_get_context(QDF_MODULE_ID_PE);
|
|
|
+ if (!mac_ctx)
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+
|
|
|
+ if (edca_ie->ie != DOT11F_EID_EDCAPARAMSET ||
|
|
|
+ edca_ie->len != DOT11F_IE_EDCAPARAMSET_MIN_LEN) {
|
|
|
+ mlme_debug("edca info is not valid or not exist, using def");
|
|
|
+ return epcs_update_def_edca_param(vdev, mac_ctx, edca);
|
|
|
+ }
|
|
|
+
|
|
|
+ ac_record = edca_ie->ac_record;
|
|
|
+ for (i = 0; i < QCA_WLAN_AC_ALL; i++) {
|
|
|
+ epcs_update_ac_value(&edca[i], &ac_record[i]);
|
|
|
+ edca[i].no_ack = mac_ctx->no_ack_policy_cfg[i];
|
|
|
+ }
|
|
|
+
|
|
|
+ return lim_send_epcs_update_edca_params(vdev, edca, false);
|
|
|
+}
|
|
|
+
|
|
|
+static QDF_STATUS
|
|
|
+epcs_update_mu_edca_param(struct wlan_objmgr_vdev *vdev,
|
|
|
+ struct muedca_ie *muedca)
|
|
|
+{
|
|
|
+ struct mac_context *mac_ctx;
|
|
|
+ struct muac_param_record *mu_record;
|
|
|
+ tSirMacEdcaParamRecord edca[QCA_WLAN_AC_ALL] = {0};
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (muedca->elem_id != DOT11F_EID_MU_EDCA_PARAM_SET ||
|
|
|
+ muedca->elem_len != (DOT11F_IE_MU_EDCA_PARAM_SET_MIN_LEN + 1)) {
|
|
|
+ mlme_debug("mu edca info for epcs is not valid or not exist");
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ mac_ctx = cds_get_context(QDF_MODULE_ID_PE);
|
|
|
+ if (!mac_ctx)
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+
|
|
|
+ mu_record = muedca->mu_record;
|
|
|
+ for (i = 0; i < QCA_WLAN_AC_ALL; i++) {
|
|
|
+ epcs_update_mu_ac_value(&edca[i], &mu_record[i]);
|
|
|
+ edca[i].no_ack = mac_ctx->no_ack_policy_cfg[i];
|
|
|
+ }
|
|
|
+
|
|
|
+ return lim_send_epcs_update_edca_params(vdev, edca, true);
|
|
|
+}
|
|
|
+
|
|
|
+static QDF_STATUS
|
|
|
+epcs_restore_edca_param(struct wlan_objmgr_vdev *vdev)
|
|
|
+{
|
|
|
+ struct wlan_objmgr_vdev *link_vdev;
|
|
|
+ struct wlan_mlo_dev_context *mlo_dev_ctx;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (!vdev)
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+
|
|
|
+ mlo_dev_ctx = vdev->mlo_dev_ctx;
|
|
|
+ if (!mlo_dev_ctx)
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+
|
|
|
+ for (i = 0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) {
|
|
|
+ link_vdev = mlo_dev_ctx->wlan_vdev_list[i];
|
|
|
+ if (!link_vdev)
|
|
|
+ continue;
|
|
|
+ lim_send_epcs_restore_edca_params(link_vdev);
|
|
|
+ }
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+static QDF_STATUS epcs_handle_rx_req(struct wlan_objmgr_vdev *vdev,
|
|
|
+ struct wlan_objmgr_peer *peer,
|
|
|
+ void *event_data, uint32_t len)
|
|
|
+{
|
|
|
+ struct wlan_mlo_peer_context *ml_peer;
|
|
|
+ struct wlan_mlo_peer_epcs_info *epcs_info;
|
|
|
+ struct wlan_epcs_info epcs_req = {0};
|
|
|
+ struct wlan_action_frame_args args;
|
|
|
+ struct ml_pa_info *edca_info;
|
|
|
+ struct ml_pa_partner_link_info *link;
|
|
|
+ struct wlan_objmgr_vdev *link_vdev;
|
|
|
+ uint32_t i;
|
|
|
+ QDF_STATUS status;
|
|
|
+
|
|
|
+ if (!vdev || !peer)
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+
|
|
|
+ ml_peer = peer->mlo_peer_ctx;
|
|
|
+ if (!ml_peer)
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+
|
|
|
+ epcs_info = &ml_peer->epcs_info;
|
|
|
+ if (epcs_info->state == EPCS_ENABLE) {
|
|
|
+ mlme_err("EPCS has been enable, ignore the req.");
|
|
|
+ return QDF_STATUS_E_ALREADY;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = wlan_mlo_parse_epcs_action_frame(&epcs_req, event_data, len);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ mlme_err("Unable to parse EPCS request action frame");
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ epcs_info->self_gen_dialog_token = epcs_req.dialog_token;
|
|
|
+ edca_info = &epcs_req.pa_info;
|
|
|
+ for (i = 0; i < edca_info->num_links; i++) {
|
|
|
+ link = &edca_info->link_info[i];
|
|
|
+ link_vdev = mlo_get_vdev_by_link_id(vdev, link->link_id);
|
|
|
+ epcs_update_edca_param(link_vdev, &link->edca);
|
|
|
+ epcs_update_mu_edca_param(link_vdev, &link->muedca);
|
|
|
+ }
|
|
|
+
|
|
|
+ args.category = ACTION_CATEGORY_PROTECTED_EHT;
|
|
|
+ args.action = EHT_EPCS_RESPONSE;
|
|
|
+ args.arg1 = epcs_info->self_gen_dialog_token;
|
|
|
+ args.arg2 = QDF_STATUS_SUCCESS;
|
|
|
+
|
|
|
+ status = lim_send_epcs_action_rsp_frame(vdev,
|
|
|
+ wlan_peer_get_macaddr(peer),
|
|
|
+ &args);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ mlme_err("Send EPCS response frame error");
|
|
|
+ epcs_restore_edca_param(vdev);
|
|
|
+ } else {
|
|
|
+ epcs_info->state = EPCS_ENABLE;
|
|
|
+ mlme_debug("EPCS (responder) state: Teardown -> Enable");
|
|
|
+ }
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+static QDF_STATUS epcs_handle_rx_resp(struct wlan_objmgr_vdev *vdev,
|
|
|
+ struct wlan_objmgr_peer *peer,
|
|
|
+ void *event_data, uint32_t len)
|
|
|
+{
|
|
|
+ struct wlan_mlo_peer_context *ml_peer;
|
|
|
+ struct wlan_mlo_peer_epcs_info *epcs_info;
|
|
|
+ struct wlan_epcs_info epcs_rsp = {0};
|
|
|
+ struct ml_pa_info *edca_info;
|
|
|
+ struct ml_pa_partner_link_info *link;
|
|
|
+ struct wlan_objmgr_vdev *link_vdev;
|
|
|
+ uint32_t i;
|
|
|
+ QDF_STATUS status;
|
|
|
+
|
|
|
+ if (!vdev || !peer)
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+
|
|
|
+ ml_peer = peer->mlo_peer_ctx;
|
|
|
+ if (!ml_peer)
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+
|
|
|
+ epcs_info = &ml_peer->epcs_info;
|
|
|
+ if (epcs_info->state == EPCS_ENABLE) {
|
|
|
+ mlme_err("EPCS has been enable, ignore the rsp.");
|
|
|
+ return QDF_STATUS_E_ALREADY;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = wlan_mlo_parse_epcs_action_frame(&epcs_rsp, event_data, len);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ mlme_err("Unable to parse EPCS response action frame");
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (epcs_info->self_gen_dialog_token != epcs_rsp.dialog_token) {
|
|
|
+ mlme_err("epcs rsp dialog token %d does not match",
|
|
|
+ epcs_rsp.dialog_token);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (epcs_rsp.status) {
|
|
|
+ mlme_err("epcs rsp status error %d", epcs_rsp.status);
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ edca_info = &epcs_rsp.pa_info;
|
|
|
+ for (i = 0; i < edca_info->num_links; i++) {
|
|
|
+ link = &edca_info->link_info[i];
|
|
|
+ link_vdev = mlo_get_vdev_by_link_id(vdev, link->link_id);
|
|
|
+ epcs_update_edca_param(link_vdev, &link->edca);
|
|
|
+ epcs_update_mu_edca_param(link_vdev, &link->muedca);
|
|
|
+ }
|
|
|
+
|
|
|
+ epcs_info->state = EPCS_ENABLE;
|
|
|
+ mlme_debug("EPCS (initiator) state: Teardown -> Enable");
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+static QDF_STATUS epcs_handle_rx_teardown(struct wlan_objmgr_vdev *vdev,
|
|
|
+ struct wlan_objmgr_peer *peer,
|
|
|
+ void *event_data, uint32_t len)
|
|
|
+{
|
|
|
+ struct wlan_mlo_peer_context *ml_peer;
|
|
|
+ struct wlan_mlo_peer_epcs_info *epcs_info;
|
|
|
+ struct wlan_epcs_info epcs_req = {0};
|
|
|
+ struct mac_context *mac_ctx;
|
|
|
+ QDF_STATUS status;
|
|
|
+
|
|
|
+ if (!vdev || !peer)
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+
|
|
|
+ mac_ctx = cds_get_context(QDF_MODULE_ID_PE);
|
|
|
+ if (!mac_ctx)
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
+
|
|
|
+ ml_peer = peer->mlo_peer_ctx;
|
|
|
+ if (!ml_peer)
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+
|
|
|
+ epcs_info = &ml_peer->epcs_info;
|
|
|
+ if (epcs_info->state == EPCS_DOWN) {
|
|
|
+ mlme_err("EPCS has been down, ignore the teardown req.");
|
|
|
+ return QDF_STATUS_E_ALREADY;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = wlan_mlo_parse_epcs_action_frame(&epcs_req, event_data, len);
|
|
|
+ if (status != QDF_STATUS_SUCCESS) {
|
|
|
+ mlme_err("Unable to parse EPCS teardown action frame");
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ epcs_restore_edca_param(vdev);
|
|
|
+
|
|
|
+ epcs_info->state = EPCS_DOWN;
|
|
|
+ mlme_debug("EPCS state: Enale -> Teardown.");
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+static QDF_STATUS epcs_handle_tx_req(struct wlan_objmgr_vdev *vdev)
|
|
|
+{
|
|
|
+ struct wlan_mlo_peer_context *ml_peer;
|
|
|
+ struct wlan_objmgr_peer *peer;
|
|
|
+ struct wlan_action_frame_args args;
|
|
|
+ struct wlan_mlo_peer_epcs_info *epcs_info;
|
|
|
+ QDF_STATUS status;
|
|
|
+
|
|
|
+ if (!vdev)
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+
|
|
|
+ peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_MLO_MGR_ID);
|
|
|
+ if (!peer)
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+
|
|
|
+ ml_peer = peer->mlo_peer_ctx;
|
|
|
+ if (!ml_peer) {
|
|
|
+ status = QDF_STATUS_E_NULL_VALUE;
|
|
|
+ goto release_peer;
|
|
|
+ }
|
|
|
+
|
|
|
+ epcs_info = &ml_peer->epcs_info;
|
|
|
+ if (epcs_info->state == EPCS_ENABLE) {
|
|
|
+ mlme_err("EPCS has been enable, ignore the req cmd.");
|
|
|
+ status = QDF_STATUS_E_ALREADY;
|
|
|
+ goto release_peer;
|
|
|
+ }
|
|
|
+
|
|
|
+ args.category = ACTION_CATEGORY_PROTECTED_EHT;
|
|
|
+ args.action = EHT_EPCS_REQUEST;
|
|
|
+ args.arg1 = epcs_gen_dialog_token(epcs_info);
|
|
|
+
|
|
|
+ status = lim_send_epcs_action_req_frame(vdev,
|
|
|
+ wlan_peer_get_macaddr(peer),
|
|
|
+ &args);
|
|
|
+ if (QDF_IS_STATUS_ERROR(status))
|
|
|
+ mlme_err("Failed to send EPCS action request frame");
|
|
|
+
|
|
|
+release_peer:
|
|
|
+ wlan_objmgr_peer_release_ref(peer, WLAN_MLO_MGR_ID);
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+static QDF_STATUS epcs_handle_tx_teardown(struct wlan_objmgr_vdev *vdev)
|
|
|
+{
|
|
|
+ struct wlan_mlo_peer_context *ml_peer;
|
|
|
+ struct wlan_objmgr_peer *peer;
|
|
|
+ struct wlan_action_frame_args args;
|
|
|
+ struct wlan_mlo_peer_epcs_info *epcs_info;
|
|
|
+ QDF_STATUS status;
|
|
|
+
|
|
|
+ if (!vdev)
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+
|
|
|
+ peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_MLO_MGR_ID);
|
|
|
+ if (!peer)
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
+
|
|
|
+ ml_peer = peer->mlo_peer_ctx;
|
|
|
+ if (!ml_peer) {
|
|
|
+ status = QDF_STATUS_E_NULL_VALUE;
|
|
|
+ goto release_peer;
|
|
|
+ }
|
|
|
+
|
|
|
+ epcs_info = &ml_peer->epcs_info;
|
|
|
+ if (epcs_info->state == EPCS_DOWN) {
|
|
|
+ mlme_err("EPCS has been down, ignore the teardwon cmd.");
|
|
|
+ status = QDF_STATUS_E_ALREADY;
|
|
|
+ goto release_peer;
|
|
|
+ }
|
|
|
+
|
|
|
+ args.category = ACTION_CATEGORY_PROTECTED_EHT;
|
|
|
+ args.action = EHT_EPCS_TEARDOWN;
|
|
|
+
|
|
|
+ status =
|
|
|
+ lim_send_epcs_action_teardown_frame(vdev,
|
|
|
+ wlan_peer_get_macaddr(peer),
|
|
|
+ &args);
|
|
|
+ if (QDF_IS_STATUS_ERROR(status)) {
|
|
|
+ mlme_err("Failed to send EPCS tear down frame");
|
|
|
+ } else {
|
|
|
+ epcs_restore_edca_param(vdev);
|
|
|
+ epcs_info->state = EPCS_DOWN;
|
|
|
+ mlme_debug("EPCS state: Enale -> Teardown.");
|
|
|
+ }
|
|
|
+
|
|
|
+release_peer:
|
|
|
+ wlan_objmgr_peer_release_ref(peer, WLAN_MLO_MGR_ID);
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+static QDF_STATUS epcs_deliver_event(struct wlan_objmgr_vdev *vdev,
|
|
|
+ struct wlan_objmgr_peer *peer,
|
|
|
+ enum wlan_epcs_evt event,
|
|
|
+ void *event_data, uint32_t len)
|
|
|
+{
|
|
|
+ QDF_STATUS status;
|
|
|
+
|
|
|
+ mlme_debug("EPCS event received: %s(%d)",
|
|
|
+ epcs_get_event_str(event), event);
|
|
|
+
|
|
|
+ switch (event) {
|
|
|
+ case WLAN_EPCS_EV_ACTION_FRAME_RX_REQ:
|
|
|
+ status = epcs_handle_rx_req(vdev, peer, event_data, len);
|
|
|
+ break;
|
|
|
+ case WLAN_EPCS_EV_ACTION_FRAME_RX_RESP:
|
|
|
+ status = epcs_handle_rx_resp(vdev, peer, event_data, len);
|
|
|
+ break;
|
|
|
+ case WLAN_EPCS_EV_ACTION_FRAME_RX_TEARDOWN:
|
|
|
+ status = epcs_handle_rx_teardown(vdev, peer, event_data, len);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ status = QDF_STATUS_E_FAILURE;
|
|
|
+ mlme_err("Unhandled EPCS event");
|
|
|
+ }
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+QDF_STATUS wlan_epcs_deliver_event(struct wlan_objmgr_vdev *vdev,
|
|
|
+ struct wlan_objmgr_peer *peer,
|
|
|
+ enum wlan_epcs_evt event,
|
|
|
+ void *event_data, uint32_t len)
|
|
|
+{
|
|
|
+ return epcs_deliver_event(vdev, peer, event, event_data, len);
|
|
|
+}
|
|
|
+
|
|
|
+static QDF_STATUS epcs_deliver_cmd(struct wlan_objmgr_vdev *vdev,
|
|
|
+ enum wlan_epcs_evt event)
|
|
|
+{
|
|
|
+ QDF_STATUS status;
|
|
|
+
|
|
|
+ mlme_debug("EPCS cmd received: %s(%d)",
|
|
|
+ epcs_get_event_str(event), event);
|
|
|
+
|
|
|
+ switch (event) {
|
|
|
+ case WLAN_EPCS_EV_ACTION_FRAME_TX_REQ:
|
|
|
+ status = epcs_handle_tx_req(vdev);
|
|
|
+ break;
|
|
|
+ case WLAN_EPCS_EV_ACTION_FRAME_TX_TEARDOWN:
|
|
|
+ status = epcs_handle_tx_teardown(vdev);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ status = QDF_STATUS_E_FAILURE;
|
|
|
+ mlme_err("Unhandled EPCS cmd");
|
|
|
+ }
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+QDF_STATUS wlan_epcs_deliver_cmd(struct wlan_objmgr_vdev *vdev,
|
|
|
+ enum wlan_epcs_evt event)
|
|
|
+{
|
|
|
+ return epcs_deliver_cmd(vdev, event);
|
|
|
+}
|