Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/iwlwifi/mld/roc.c
48287 views
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
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
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 *
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
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
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
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
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
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
267