1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 /* 3 * Copyright (C) 2024 Intel Corporation 4 */ 5 #include "session-protect.h" 6 #include "fw/api/time-event.h" 7 #include "fw/api/context.h" 8 #include "iface.h" 9 #include <net/mac80211.h> 10 11 void iwl_mld_handle_session_prot_notif(struct iwl_mld *mld, 12 struct iwl_rx_packet *pkt) 13 { 14 struct iwl_session_prot_notif *notif = (void *)pkt->data; 15 int fw_link_id = le32_to_cpu(notif->mac_link_id); 16 struct ieee80211_bss_conf *link_conf = 17 iwl_mld_fw_id_to_link_conf(mld, fw_link_id); 18 struct ieee80211_vif *vif; 19 struct iwl_mld_vif *mld_vif; 20 struct iwl_mld_session_protect *session_protect; 21 22 if (WARN_ON(!link_conf)) 23 return; 24 25 vif = link_conf->vif; 26 mld_vif = iwl_mld_vif_from_mac80211(vif); 27 session_protect = &mld_vif->session_protect; 28 29 if (!le32_to_cpu(notif->status)) { 30 memset(session_protect, 0, sizeof(*session_protect)); 31 } else if (le32_to_cpu(notif->start)) { 32 /* End_jiffies indicates an active session */ 33 session_protect->session_requested = false; 34 session_protect->end_jiffies = 35 TU_TO_EXP_TIME(session_protect->duration); 36 /* !session_protect->end_jiffies means inactive session */ 37 if (!session_protect->end_jiffies) 38 session_protect->end_jiffies = 1; 39 } else { 40 memset(session_protect, 0, sizeof(*session_protect)); 41 } 42 } 43 44 static int _iwl_mld_schedule_session_protection(struct iwl_mld *mld, 45 struct ieee80211_vif *vif, 46 u32 duration, u32 min_duration, 47 int link_id) 48 { 49 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 50 struct iwl_mld_link *link = 51 iwl_mld_link_dereference_check(mld_vif, link_id); 52 struct iwl_mld_session_protect *session_protect = 53 &mld_vif->session_protect; 54 struct iwl_session_prot_cmd cmd = { 55 .id_and_color = cpu_to_le32(link->fw_id), 56 .action = cpu_to_le32(FW_CTXT_ACTION_ADD), 57 .conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC), 58 .duration_tu = cpu_to_le32(MSEC_TO_TU(duration)), 59 }; 60 int ret; 61 62 lockdep_assert_wiphy(mld->wiphy); 63 64 WARN(hweight16(vif->active_links) > 1, 65 "Session protection isn't allowed with more than one active link"); 66 67 if (session_protect->end_jiffies && 68 time_after(session_protect->end_jiffies, 69 TU_TO_EXP_TIME(min_duration))) { 70 IWL_DEBUG_TE(mld, "We have ample in the current session: %u\n", 71 jiffies_to_msecs(session_protect->end_jiffies - 72 jiffies)); 73 return -EALREADY; 74 } 75 76 IWL_DEBUG_TE(mld, "Add a new session protection, duration %d TU\n", 77 le32_to_cpu(cmd.duration_tu)); 78 79 ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, 80 SESSION_PROTECTION_CMD), &cmd); 81 82 if (ret) 83 return ret; 84 85 /* end_jiffies will be updated when handling session_prot_notif */ 86 session_protect->end_jiffies = 0; 87 session_protect->duration = duration; 88 session_protect->session_requested = true; 89 90 return 0; 91 } 92 93 void iwl_mld_schedule_session_protection(struct iwl_mld *mld, 94 struct ieee80211_vif *vif, 95 u32 duration, u32 min_duration, 96 int link_id) 97 { 98 int ret; 99 100 ret = _iwl_mld_schedule_session_protection(mld, vif, duration, 101 min_duration, link_id); 102 if (ret && ret != -EALREADY) 103 IWL_ERR(mld, 104 "Couldn't send the SESSION_PROTECTION_CMD (%d)\n", 105 ret); 106 } 107 108 struct iwl_mld_session_start_data { 109 struct iwl_mld *mld; 110 struct ieee80211_bss_conf *link_conf; 111 bool success; 112 }; 113 114 static bool iwl_mld_session_start_fn(struct iwl_notif_wait_data *notif_wait, 115 struct iwl_rx_packet *pkt, void *_data) 116 { 117 struct iwl_session_prot_notif *notif = (void *)pkt->data; 118 unsigned int pkt_len = iwl_rx_packet_payload_len(pkt); 119 struct iwl_mld_session_start_data *data = _data; 120 struct ieee80211_bss_conf *link_conf; 121 struct iwl_mld *mld = data->mld; 122 int fw_link_id; 123 124 if (IWL_FW_CHECK(mld, pkt_len < sizeof(*notif), 125 "short session prot notif (%d)\n", 126 pkt_len)) 127 return false; 128 129 fw_link_id = le32_to_cpu(notif->mac_link_id); 130 link_conf = iwl_mld_fw_id_to_link_conf(mld, fw_link_id); 131 132 if (link_conf != data->link_conf) 133 return false; 134 135 if (!le32_to_cpu(notif->status)) 136 return true; 137 138 if (notif->start) { 139 data->success = true; 140 return true; 141 } 142 143 return false; 144 } 145 146 int iwl_mld_start_session_protection(struct iwl_mld *mld, 147 struct ieee80211_vif *vif, 148 u32 duration, u32 min_duration, 149 int link_id, unsigned long timeout) 150 { 151 static const u16 start_notif[] = { SESSION_PROTECTION_NOTIF }; 152 struct iwl_notification_wait start_wait; 153 struct iwl_mld_session_start_data data = { 154 .mld = mld, 155 .link_conf = wiphy_dereference(mld->wiphy, 156 vif->link_conf[link_id]), 157 }; 158 int ret; 159 160 if (WARN_ON(!data.link_conf)) 161 return -EINVAL; 162 163 iwl_init_notification_wait(&mld->notif_wait, &start_wait, 164 start_notif, ARRAY_SIZE(start_notif), 165 iwl_mld_session_start_fn, &data); 166 167 ret = _iwl_mld_schedule_session_protection(mld, vif, duration, 168 min_duration, link_id); 169 170 if (ret) { 171 iwl_remove_notification(&mld->notif_wait, &start_wait); 172 return ret == -EALREADY ? 0 : ret; 173 } 174 175 ret = iwl_wait_notification(&mld->notif_wait, &start_wait, timeout); 176 if (ret) 177 return ret; 178 return data.success ? 0 : -EIO; 179 } 180 181 int iwl_mld_cancel_session_protection(struct iwl_mld *mld, 182 struct ieee80211_vif *vif, 183 int link_id) 184 { 185 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 186 struct iwl_mld_link *link = 187 iwl_mld_link_dereference_check(mld_vif, link_id); 188 struct iwl_mld_session_protect *session_protect = 189 &mld_vif->session_protect; 190 struct iwl_session_prot_cmd cmd = { 191 .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), 192 .conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC), 193 }; 194 int ret; 195 196 lockdep_assert_wiphy(mld->wiphy); 197 198 /* If there isn't an active session or a requested one for this 199 * link do nothing 200 */ 201 if (!session_protect->session_requested && 202 !session_protect->end_jiffies) 203 return 0; 204 205 if (WARN_ON(!link)) 206 return -EINVAL; 207 208 cmd.id_and_color = cpu_to_le32(link->fw_id); 209 210 ret = iwl_mld_send_cmd_pdu(mld, 211 WIDE_ID(MAC_CONF_GROUP, 212 SESSION_PROTECTION_CMD), &cmd); 213 if (ret) { 214 IWL_ERR(mld, 215 "Couldn't send the SESSION_PROTECTION_CMD\n"); 216 return ret; 217 } 218 219 memset(session_protect, 0, sizeof(*session_protect)); 220 221 return 0; 222 } 223