summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/intel/iwlwifi/mld/session-protect.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mld/session-protect.c')
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/session-protect.c222
1 files changed, 222 insertions, 0 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/session-protect.c b/drivers/net/wireless/intel/iwlwifi/mld/session-protect.c
new file mode 100644
index 000000000000..dbb5615dc3f6
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/session-protect.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2024 Intel Corporation
+ */
+#include "session-protect.h"
+#include "fw/api/time-event.h"
+#include "fw/api/context.h"
+#include "iface.h"
+#include <net/mac80211.h>
+
+void iwl_mld_handle_session_prot_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_session_prot_notif *notif = (void *)pkt->data;
+ int fw_link_id = le32_to_cpu(notif->mac_link_id);
+ struct ieee80211_bss_conf *link_conf =
+ iwl_mld_fw_id_to_link_conf(mld, fw_link_id);
+ struct ieee80211_vif *vif;
+ struct iwl_mld_vif *mld_vif;
+ struct iwl_mld_session_protect *session_protect;
+
+ if (WARN_ON(!link_conf))
+ return;
+
+ vif = link_conf->vif;
+ mld_vif = iwl_mld_vif_from_mac80211(vif);
+ session_protect = &mld_vif->session_protect;
+
+ if (!le32_to_cpu(notif->status)) {
+ memset(session_protect, 0, sizeof(*session_protect));
+ } else if (le32_to_cpu(notif->start)) {
+ /* End_jiffies indicates an active session */
+ session_protect->session_requested = false;
+ session_protect->end_jiffies =
+ TU_TO_EXP_TIME(session_protect->duration);
+ /* !session_protect->end_jiffies means inactive session */
+ if (!session_protect->end_jiffies)
+ session_protect->end_jiffies = 1;
+ } else {
+ memset(session_protect, 0, sizeof(*session_protect));
+ }
+}
+
+static int _iwl_mld_schedule_session_protection(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ u32 duration, u32 min_duration,
+ int link_id)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_link *link =
+ iwl_mld_link_dereference_check(mld_vif, link_id);
+ struct iwl_mld_session_protect *session_protect =
+ &mld_vif->session_protect;
+ struct iwl_session_prot_cmd cmd = {
+ .id_and_color = cpu_to_le32(link->fw_id),
+ .action = cpu_to_le32(FW_CTXT_ACTION_ADD),
+ .conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC),
+ .duration_tu = cpu_to_le32(MSEC_TO_TU(duration)),
+ };
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ WARN(hweight16(vif->active_links) > 1,
+ "Session protection isn't allowed with more than one active link");
+
+ if (session_protect->end_jiffies &&
+ time_after(session_protect->end_jiffies,
+ TU_TO_EXP_TIME(min_duration))) {
+ IWL_DEBUG_TE(mld, "We have ample in the current session: %u\n",
+ jiffies_to_msecs(session_protect->end_jiffies -
+ jiffies));
+ return -EALREADY;
+ }
+
+ IWL_DEBUG_TE(mld, "Add a new session protection, duration %d TU\n",
+ le32_to_cpu(cmd.duration_tu));
+
+ ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP,
+ SESSION_PROTECTION_CMD), &cmd);
+
+ if (ret)
+ return ret;
+
+ /* end_jiffies will be updated when handling session_prot_notif */
+ session_protect->end_jiffies = 0;
+ session_protect->duration = duration;
+ session_protect->session_requested = true;
+
+ return 0;
+}
+
+void iwl_mld_schedule_session_protection(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ u32 duration, u32 min_duration,
+ int link_id)
+{
+ int ret;
+
+ ret = _iwl_mld_schedule_session_protection(mld, vif, duration,
+ min_duration, link_id);
+ if (ret && ret != -EALREADY)
+ IWL_ERR(mld,
+ "Couldn't send the SESSION_PROTECTION_CMD (%d)\n",
+ ret);
+}
+
+struct iwl_mld_session_start_data {
+ struct iwl_mld *mld;
+ struct ieee80211_bss_conf *link_conf;
+ bool success;
+};
+
+static bool iwl_mld_session_start_fn(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt, void *_data)
+{
+ struct iwl_session_prot_notif *notif = (void *)pkt->data;
+ unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);
+ struct iwl_mld_session_start_data *data = _data;
+ struct ieee80211_bss_conf *link_conf;
+ struct iwl_mld *mld = data->mld;
+ int fw_link_id;
+
+ if (IWL_FW_CHECK(mld, pkt_len < sizeof(*notif),
+ "short session prot notif (%d)\n",
+ pkt_len))
+ return false;
+
+ fw_link_id = le32_to_cpu(notif->mac_link_id);
+ link_conf = iwl_mld_fw_id_to_link_conf(mld, fw_link_id);
+
+ if (link_conf != data->link_conf)
+ return false;
+
+ if (!le32_to_cpu(notif->status))
+ return true;
+
+ if (notif->start) {
+ data->success = true;
+ return true;
+ }
+
+ return false;
+}
+
+int iwl_mld_start_session_protection(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ u32 duration, u32 min_duration,
+ int link_id, unsigned long timeout)
+{
+ static const u16 start_notif[] = { SESSION_PROTECTION_NOTIF };
+ struct iwl_notification_wait start_wait;
+ struct iwl_mld_session_start_data data = {
+ .mld = mld,
+ .link_conf = wiphy_dereference(mld->wiphy,
+ vif->link_conf[link_id]),
+ };
+ int ret;
+
+ if (WARN_ON(!data.link_conf))
+ return -EINVAL;
+
+ iwl_init_notification_wait(&mld->notif_wait, &start_wait,
+ start_notif, ARRAY_SIZE(start_notif),
+ iwl_mld_session_start_fn, &data);
+
+ ret = _iwl_mld_schedule_session_protection(mld, vif, duration,
+ min_duration, link_id);
+
+ if (ret) {
+ iwl_remove_notification(&mld->notif_wait, &start_wait);
+ return ret == -EALREADY ? 0 : ret;
+ }
+
+ ret = iwl_wait_notification(&mld->notif_wait, &start_wait, timeout);
+ if (ret)
+ return ret;
+ return data.success ? 0 : -EIO;
+}
+
+int iwl_mld_cancel_session_protection(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ int link_id)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct iwl_mld_link *link =
+ iwl_mld_link_dereference_check(mld_vif, link_id);
+ struct iwl_mld_session_protect *session_protect =
+ &mld_vif->session_protect;
+ struct iwl_session_prot_cmd cmd = {
+ .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
+ .conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC),
+ };
+ int ret;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ /* If there isn't an active session or a requested one for this
+ * link do nothing
+ */
+ if (!session_protect->session_requested &&
+ !session_protect->end_jiffies)
+ return 0;
+
+ if (WARN_ON(!link))
+ return -EINVAL;
+
+ cmd.id_and_color = cpu_to_le32(link->fw_id);
+
+ ret = iwl_mld_send_cmd_pdu(mld,
+ WIDE_ID(MAC_CONF_GROUP,
+ SESSION_PROTECTION_CMD), &cmd);
+ if (ret) {
+ IWL_ERR(mld,
+ "Couldn't send the SESSION_PROTECTION_CMD\n");
+ return ret;
+ }
+
+ memset(session_protect, 0, sizeof(*session_protect));
+
+ return 0;
+}