diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mld/session-protect.c')
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mld/session-protect.c | 222 |
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; +} |