// 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 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; }