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
iwl_mld_handle_session_prot_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)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
_iwl_mld_schedule_session_protection(struct iwl_mld * mld,struct ieee80211_vif * vif,u32 duration,u32 min_duration,int link_id)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
iwl_mld_schedule_session_protection(struct iwl_mld * mld,struct ieee80211_vif * vif,u32 duration,u32 min_duration,int link_id)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
iwl_mld_session_start_fn(struct iwl_notif_wait_data * notif_wait,struct iwl_rx_packet * pkt,void * _data)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
iwl_mld_start_session_protection(struct iwl_mld * mld,struct ieee80211_vif * vif,u32 duration,u32 min_duration,int link_id,unsigned long timeout)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
iwl_mld_cancel_session_protection(struct iwl_mld * mld,struct ieee80211_vif * vif,int link_id)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