Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/iwlwifi/mld/nan.c
289285 views
1
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2
/*
3
* Copyright (C) 2025 Intel Corporation
4
*/
5
6
#include "mld.h"
7
#include "iface.h"
8
#include "mlo.h"
9
#include "fw/api/mac-cfg.h"
10
11
#define IWL_NAN_DISOVERY_BEACON_INTERNVAL_TU 512
12
#define IWL_NAN_RSSI_CLOSE 55
13
#define IWL_NAN_RSSI_MIDDLE 70
14
15
bool iwl_mld_nan_supported(struct iwl_mld *mld)
16
{
17
return fw_has_capa(&mld->fw->ucode_capa,
18
IWL_UCODE_TLV_CAPA_NAN_SYNC_SUPPORT);
19
}
20
21
static int iwl_mld_nan_send_config_cmd(struct iwl_mld *mld,
22
struct iwl_nan_config_cmd *cmd,
23
u8 *beacon_data, size_t beacon_data_len)
24
{
25
struct iwl_host_cmd hcmd = {
26
.id = WIDE_ID(MAC_CONF_GROUP, NAN_CFG_CMD),
27
};
28
29
hcmd.len[0] = sizeof(*cmd);
30
hcmd.data[0] = cmd;
31
32
if (beacon_data_len) {
33
hcmd.len[1] = beacon_data_len;
34
hcmd.data[1] = beacon_data;
35
hcmd.dataflags[1] = IWL_HCMD_DFL_DUP;
36
}
37
38
return iwl_mld_send_cmd(mld, &hcmd);
39
}
40
41
static int iwl_mld_nan_config(struct iwl_mld *mld,
42
struct ieee80211_vif *vif,
43
struct cfg80211_nan_conf *conf,
44
enum iwl_ctxt_action action)
45
{
46
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
47
struct iwl_nan_config_cmd cmd = {
48
.action = cpu_to_le32(action),
49
};
50
u8 *data __free(kfree) = NULL;
51
52
lockdep_assert_wiphy(mld->wiphy);
53
54
ether_addr_copy(cmd.nmi_addr, vif->addr);
55
cmd.master_pref = conf->master_pref;
56
57
if (conf->cluster_id)
58
memcpy(cmd.cluster_id, conf->cluster_id + 4,
59
sizeof(cmd.cluster_id));
60
61
cmd.scan_period = conf->scan_period < 255 ? conf->scan_period : 255;
62
cmd.dwell_time =
63
conf->scan_dwell_time < 255 ? conf->scan_dwell_time : 255;
64
65
if (conf->discovery_beacon_interval)
66
cmd.discovery_beacon_interval =
67
cpu_to_le32(conf->discovery_beacon_interval);
68
else
69
cmd.discovery_beacon_interval =
70
cpu_to_le32(IWL_NAN_DISOVERY_BEACON_INTERNVAL_TU);
71
72
if (conf->enable_dw_notification)
73
cmd.flags = IWL_NAN_FLAG_DW_END_NOTIF_ENABLED;
74
75
/* 2 GHz band must be supported */
76
cmd.band_config[IWL_NAN_BAND_2GHZ].rssi_close =
77
abs(conf->band_cfgs[NL80211_BAND_2GHZ].rssi_close);
78
cmd.band_config[IWL_NAN_BAND_2GHZ].rssi_middle =
79
abs(conf->band_cfgs[NL80211_BAND_2GHZ].rssi_middle);
80
cmd.band_config[IWL_NAN_BAND_2GHZ].dw_interval =
81
conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval;
82
83
/* 5 GHz band operation is optional. Configure its operation if
84
* supported. Note that conf->bands might be zero, so we need to check
85
* the channel pointer, not the band mask.
86
*/
87
if (conf->band_cfgs[NL80211_BAND_5GHZ].chan) {
88
cmd.hb_channel =
89
conf->band_cfgs[NL80211_BAND_5GHZ].chan->hw_value;
90
91
cmd.band_config[IWL_NAN_BAND_5GHZ].rssi_close =
92
abs(conf->band_cfgs[NL80211_BAND_5GHZ].rssi_close);
93
cmd.band_config[IWL_NAN_BAND_5GHZ].rssi_middle =
94
abs(conf->band_cfgs[NL80211_BAND_5GHZ].rssi_middle);
95
cmd.band_config[IWL_NAN_BAND_5GHZ].dw_interval =
96
conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval;
97
}
98
99
if (conf->extra_nan_attrs_len || conf->vendor_elems_len) {
100
data = kmalloc(conf->extra_nan_attrs_len +
101
conf->vendor_elems_len, GFP_KERNEL);
102
if (!data)
103
return -ENOMEM;
104
105
cmd.nan_attr_len = cpu_to_le32(conf->extra_nan_attrs_len);
106
cmd.nan_vendor_elems_len = cpu_to_le32(conf->vendor_elems_len);
107
108
if (conf->extra_nan_attrs_len)
109
memcpy(data, conf->extra_nan_attrs,
110
conf->extra_nan_attrs_len);
111
112
if (conf->vendor_elems_len)
113
memcpy(data + conf->extra_nan_attrs_len,
114
conf->vendor_elems,
115
conf->vendor_elems_len);
116
}
117
118
cmd.sta_id = mld_vif->aux_sta.sta_id;
119
return iwl_mld_nan_send_config_cmd(mld, &cmd, data,
120
conf->extra_nan_attrs_len +
121
conf->vendor_elems_len);
122
}
123
124
int iwl_mld_start_nan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
125
struct cfg80211_nan_conf *conf)
126
{
127
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
128
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
129
struct iwl_mld_int_sta *aux_sta = &mld_vif->aux_sta;
130
int ret;
131
132
IWL_DEBUG_MAC80211(mld, "NAN: start: bands=0x%x\n", conf->bands);
133
134
ret = iwl_mld_update_emlsr_block(mld, true, IWL_MLD_EMLSR_BLOCKED_NAN);
135
if (ret)
136
return ret;
137
138
ret = iwl_mld_add_aux_sta(mld, aux_sta);
139
if (ret)
140
goto unblock_emlsr;
141
142
ret = iwl_mld_nan_config(mld, vif, conf, FW_CTXT_ACTION_ADD);
143
if (ret) {
144
IWL_ERR(mld, "Failed to start NAN. ret=%d\n", ret);
145
goto remove_aux;
146
}
147
return 0;
148
149
remove_aux:
150
iwl_mld_remove_aux_sta(mld, vif);
151
unblock_emlsr:
152
iwl_mld_update_emlsr_block(mld, false, IWL_MLD_EMLSR_BLOCKED_NAN);
153
154
return ret;
155
}
156
157
int iwl_mld_nan_change_config(struct ieee80211_hw *hw,
158
struct ieee80211_vif *vif,
159
struct cfg80211_nan_conf *conf,
160
u32 changes)
161
{
162
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
163
164
IWL_DEBUG_MAC80211(mld, "NAN: change: changes=0x%x, bands=0x%x\n",
165
changes, conf->bands);
166
167
/* Note that we do not use 'changes' as the FW always expects the
168
* complete configuration, and mac80211 always provides the complete
169
* configuration.
170
*/
171
return iwl_mld_nan_config(mld, vif, conf, FW_CTXT_ACTION_MODIFY);
172
}
173
174
int iwl_mld_stop_nan(struct ieee80211_hw *hw,
175
struct ieee80211_vif *vif)
176
{
177
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
178
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
179
struct iwl_nan_config_cmd cmd = {
180
.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
181
};
182
int ret;
183
184
lockdep_assert_wiphy(mld->wiphy);
185
186
ret = iwl_mld_send_cmd_pdu(mld,
187
WIDE_ID(MAC_CONF_GROUP, NAN_CFG_CMD),
188
&cmd);
189
if (ret)
190
IWL_ERR(mld, "NAN: Failed to stop NAN. ret=%d\n", ret);
191
192
/* assume that higher layer guarantees that no additional frames are
193
* added before calling this callback
194
*/
195
iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
196
iwl_mld_remove_aux_sta(mld, vif);
197
198
/* cancel based on object type being NAN, as the NAN objects do
199
* not have a unique identifier associated with them
200
*/
201
iwl_mld_cancel_notifications_of_object(mld,
202
IWL_MLD_OBJECT_TYPE_NAN,
203
0);
204
205
iwl_mld_update_emlsr_block(mld, false, IWL_MLD_EMLSR_BLOCKED_NAN);
206
207
return 0;
208
}
209
210
void iwl_mld_handle_nan_cluster_notif(struct iwl_mld *mld,
211
struct iwl_rx_packet *pkt)
212
{
213
struct iwl_nan_cluster_notif *notif = (void *)pkt->data;
214
struct wireless_dev *wdev = mld->nan_device_vif ?
215
ieee80211_vif_to_wdev(mld->nan_device_vif) : NULL;
216
bool new_cluster = !!(notif->flags &
217
IWL_NAN_CLUSTER_NOTIF_FLAG_NEW_CLUSTER);
218
u8 cluster_id[ETH_ALEN] = {
219
0x50, 0x6f, 0x9a, 0x01,
220
notif->cluster_id[0], notif->cluster_id[1]
221
};
222
223
IWL_DEBUG_INFO(mld,
224
"NAN: cluster event: cluster_id=%pM, flags=0x%x\n",
225
cluster_id, notif->flags);
226
227
if (IWL_FW_CHECK(mld, !wdev, "NAN: cluster event without wdev\n"))
228
return;
229
230
if (IWL_FW_CHECK(mld, !ieee80211_vif_nan_started(mld->nan_device_vif),
231
"NAN: cluster event without NAN started\n"))
232
return;
233
234
cfg80211_nan_cluster_joined(wdev, cluster_id, new_cluster, GFP_KERNEL);
235
}
236
237
bool iwl_mld_cancel_nan_cluster_notif(struct iwl_mld *mld,
238
struct iwl_rx_packet *pkt,
239
u32 obj_id)
240
{
241
return true;
242
}
243
244
bool iwl_mld_cancel_nan_dw_end_notif(struct iwl_mld *mld,
245
struct iwl_rx_packet *pkt,
246
u32 obj_id)
247
{
248
return true;
249
}
250
251
void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld,
252
struct iwl_rx_packet *pkt)
253
{
254
struct iwl_nan_dw_end_notif *notif = (void *)pkt->data;
255
struct iwl_mld_vif *mld_vif = mld->nan_device_vif ?
256
iwl_mld_vif_from_mac80211(mld->nan_device_vif) :
257
NULL;
258
struct wireless_dev *wdev;
259
struct ieee80211_channel *chan;
260
261
IWL_INFO(mld, "NAN: DW end: band=%u\n", notif->band);
262
263
if (IWL_FW_CHECK(mld, !mld_vif, "NAN: DW end without mld_vif\n"))
264
return;
265
266
if (IWL_FW_CHECK(mld, !ieee80211_vif_nan_started(mld->nan_device_vif),
267
"NAN: DW end without NAN started\n"))
268
return;
269
270
if (WARN_ON(mld_vif->aux_sta.sta_id == IWL_INVALID_STA))
271
return;
272
273
IWL_DEBUG_INFO(mld, "NAN: flush queues for aux sta=%u\n",
274
mld_vif->aux_sta.sta_id);
275
276
iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
277
278
/* TODO: currently the notification specified the band on which the DW
279
* ended. Need to change that to the actual channel on which the next DW
280
* will be started.
281
*/
282
switch (notif->band) {
283
case IWL_NAN_BAND_2GHZ:
284
chan = ieee80211_get_channel(mld->wiphy, 2437);
285
break;
286
case IWL_NAN_BAND_5GHZ:
287
/* TODO: use the actual channel */
288
chan = ieee80211_get_channel(mld->wiphy, 5745);
289
break;
290
default:
291
IWL_FW_CHECK(mld, false,
292
"NAN: Invalid band %u in DW end notif\n",
293
notif->band);
294
return;
295
}
296
297
wdev = ieee80211_vif_to_wdev(mld->nan_device_vif);
298
cfg80211_next_nan_dw_notif(wdev, chan, GFP_KERNEL);
299
}
300
301