1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3 * Copyright (C) 2024 - 2025 Intel Corporation
4 */
5 #include <net/cfg80211.h>
6 #include <net/mac80211.h>
7
8 #include "mld.h"
9 #include "roc.h"
10 #include "hcmd.h"
11 #include "iface.h"
12 #include "sta.h"
13 #include "mlo.h"
14
15 #include "fw/api/context.h"
16 #include "fw/api/time-event.h"
17
18 #define AUX_ROC_MAX_DELAY MSEC_TO_TU(200)
19
20 static void
iwl_mld_vif_iter_emlsr_block_roc(void * data,u8 * mac,struct ieee80211_vif * vif)21 iwl_mld_vif_iter_emlsr_block_roc(void *data, u8 *mac, struct ieee80211_vif *vif)
22 {
23 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
24 int *result = data;
25 int ret;
26
27 ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif,
28 IWL_MLD_EMLSR_BLOCKED_ROC,
29 iwl_mld_get_primary_link(vif));
30 if (ret)
31 *result = ret;
32 }
33
34 struct iwl_mld_roc_iter_data {
35 enum iwl_roc_activity activity;
36 struct ieee80211_vif *vif;
37 bool found;
38 };
39
iwl_mld_find_roc_vif_iter(void * data,u8 * mac,struct ieee80211_vif * vif)40 static void iwl_mld_find_roc_vif_iter(void *data, u8 *mac,
41 struct ieee80211_vif *vif)
42 {
43 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
44 struct iwl_mld_roc_iter_data *roc_data = data;
45
46 if (mld_vif->roc_activity != roc_data->activity)
47 return;
48
49 /* The FW supports one ROC of each type simultaneously */
50 if (WARN_ON(roc_data->found)) {
51 roc_data->vif = NULL;
52 return;
53 }
54
55 roc_data->found = true;
56 roc_data->vif = vif;
57 }
58
59 static struct ieee80211_vif *
iwl_mld_find_roc_vif(struct iwl_mld * mld,enum iwl_roc_activity activity)60 iwl_mld_find_roc_vif(struct iwl_mld *mld, enum iwl_roc_activity activity)
61 {
62 struct iwl_mld_roc_iter_data roc_data = {
63 .activity = activity,
64 .found = false,
65 };
66
67 ieee80211_iterate_active_interfaces_mtx(mld->hw,
68 IEEE80211_IFACE_ITER_NORMAL,
69 iwl_mld_find_roc_vif_iter,
70 &roc_data);
71
72 return roc_data.vif;
73 }
74
iwl_mld_start_roc(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_channel * channel,int duration,enum ieee80211_roc_type type)75 int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
76 struct ieee80211_channel *channel, int duration,
77 enum ieee80211_roc_type type)
78 {
79 struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
80 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
81 struct iwl_mld_int_sta *aux_sta = &mld_vif->aux_sta;
82 struct iwl_roc_req cmd = {
83 .action = cpu_to_le32(FW_CTXT_ACTION_ADD),
84 };
85 u8 ver = iwl_fw_lookup_cmd_ver(mld->fw,
86 WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0);
87 u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(cmd);
88 enum iwl_roc_activity activity;
89 int ret = 0;
90
91 lockdep_assert_wiphy(mld->wiphy);
92
93 if (vif->type != NL80211_IFTYPE_P2P_DEVICE &&
94 vif->type != NL80211_IFTYPE_STATION) {
95 IWL_ERR(mld, "NOT SUPPORTED: ROC on vif->type %d\n",
96 vif->type);
97
98 return -EOPNOTSUPP;
99 }
100
101 if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
102 switch (type) {
103 case IEEE80211_ROC_TYPE_NORMAL:
104 activity = ROC_ACTIVITY_P2P_DISC;
105 break;
106 case IEEE80211_ROC_TYPE_MGMT_TX:
107 activity = ROC_ACTIVITY_P2P_NEG;
108 break;
109 default:
110 WARN_ONCE(1, "Got an invalid P2P ROC type\n");
111 return -EINVAL;
112 }
113 } else {
114 activity = ROC_ACTIVITY_HOTSPOT;
115 }
116
117 /* The FW supports one ROC of each type simultaneously */
118 if (WARN_ON(iwl_mld_find_roc_vif(mld, activity)))
119 return -EBUSY;
120
121 ieee80211_iterate_active_interfaces_mtx(mld->hw,
122 IEEE80211_IFACE_ITER_NORMAL,
123 iwl_mld_vif_iter_emlsr_block_roc,
124 &ret);
125 if (ret)
126 return ret;
127
128 ret = iwl_mld_add_aux_sta(mld, aux_sta);
129 if (ret)
130 return ret;
131
132 cmd.activity = cpu_to_le32(activity);
133 cmd.sta_id = cpu_to_le32(aux_sta->sta_id);
134 cmd.channel_info.channel = cpu_to_le32(channel->hw_value);
135 cmd.channel_info.band = iwl_mld_nl80211_band_to_fw(channel->band);
136 cmd.channel_info.width = IWL_PHY_CHANNEL_MODE20;
137 cmd.max_delay = cpu_to_le32(AUX_ROC_MAX_DELAY);
138 cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));
139
140 memcpy(cmd.node_addr, vif->addr, ETH_ALEN);
141
142 ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, ROC_CMD),
143 &cmd, cmd_len);
144 if (ret) {
145 IWL_ERR(mld, "Couldn't send the ROC_CMD\n");
146 return ret;
147 }
148
149 mld_vif->roc_activity = activity;
150
151 return 0;
152 }
153
154 static void
iwl_mld_vif_iter_emlsr_unblock_roc(void * data,u8 * mac,struct ieee80211_vif * vif)155 iwl_mld_vif_iter_emlsr_unblock_roc(void *data, u8 *mac,
156 struct ieee80211_vif *vif)
157 {
158 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
159
160 iwl_mld_unblock_emlsr(mld_vif->mld, vif, IWL_MLD_EMLSR_BLOCKED_ROC);
161 }
162
iwl_mld_destroy_roc(struct iwl_mld * mld,struct ieee80211_vif * vif,struct iwl_mld_vif * mld_vif)163 static void iwl_mld_destroy_roc(struct iwl_mld *mld,
164 struct ieee80211_vif *vif,
165 struct iwl_mld_vif *mld_vif)
166 {
167 mld_vif->roc_activity = ROC_NUM_ACTIVITIES;
168
169 ieee80211_iterate_active_interfaces_mtx(mld->hw,
170 IEEE80211_IFACE_ITER_NORMAL,
171 iwl_mld_vif_iter_emlsr_unblock_roc,
172 NULL);
173
174 /* wait until every tx has seen that roc_activity has been reset */
175 synchronize_net();
176 /* from here, no new tx will be added
177 * we can flush the Tx on the queues
178 */
179
180 iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
181
182 iwl_mld_remove_aux_sta(mld, vif);
183 }
184
iwl_mld_cancel_roc(struct ieee80211_hw * hw,struct ieee80211_vif * vif)185 int iwl_mld_cancel_roc(struct ieee80211_hw *hw,
186 struct ieee80211_vif *vif)
187 {
188 struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
189 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
190 struct iwl_roc_req cmd = {
191 .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
192 };
193 u8 ver = iwl_fw_lookup_cmd_ver(mld->fw,
194 WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0);
195 u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(cmd);
196 int ret;
197
198 lockdep_assert_wiphy(mld->wiphy);
199
200 if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE &&
201 vif->type != NL80211_IFTYPE_STATION))
202 return -EOPNOTSUPP;
203
204 /* No roc activity running it's probably already done */
205 if (mld_vif->roc_activity == ROC_NUM_ACTIVITIES)
206 return 0;
207
208 cmd.activity = cpu_to_le32(mld_vif->roc_activity);
209
210 ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, ROC_CMD),
211 &cmd, cmd_len);
212 if (ret)
213 IWL_ERR(mld, "Couldn't send the command to cancel the ROC\n");
214
215 /* We may have raced with the firmware expiring the ROC instance at
216 * this very moment. In that case, we can have a notification in the
217 * async processing queue. However, none can arrive _after_ this as
218 * ROC_CMD was sent synchronously, i.e. we waited for a response and
219 * the firmware cannot refer to this ROC after the response. Thus,
220 * if we just cancel the notification (if there's one) we'll be at a
221 * clean state for any possible next ROC.
222 */
223 iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_ROC,
224 mld_vif->roc_activity);
225
226 iwl_mld_destroy_roc(mld, vif, mld_vif);
227
228 return 0;
229 }
230
iwl_mld_handle_roc_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)231 void iwl_mld_handle_roc_notif(struct iwl_mld *mld,
232 struct iwl_rx_packet *pkt)
233 {
234 const struct iwl_roc_notif *notif = (void *)pkt->data;
235 u32 activity = le32_to_cpu(notif->activity);
236 struct iwl_mld_vif *mld_vif;
237 struct ieee80211_vif *vif;
238
239 vif = iwl_mld_find_roc_vif(mld, activity);
240 if (WARN_ON(!vif))
241 return;
242
243 mld_vif = iwl_mld_vif_from_mac80211(vif);
244 /* It is possible that the ROC was canceled
245 * but the notification was already fired.
246 */
247 if (mld_vif->roc_activity != activity)
248 return;
249
250 if (le32_to_cpu(notif->success) &&
251 le32_to_cpu(notif->started)) {
252 /* We had a successful start */
253 ieee80211_ready_on_channel(mld->hw);
254 } else {
255 /* ROC was not successful, tell the firmware to remove it */
256 if (le32_to_cpu(notif->started))
257 iwl_mld_cancel_roc(mld->hw, vif);
258 else
259 iwl_mld_destroy_roc(mld, vif, mld_vif);
260 /* we need to let know mac80211 about end OR
261 * an unsuccessful start
262 */
263 ieee80211_remain_on_channel_expired(mld->hw);
264 }
265 }
266