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
iwl_mld_nan_supported(struct iwl_mld * mld)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
iwl_mld_nan_send_config_cmd(struct iwl_mld * mld,struct iwl_nan_config_cmd * cmd,u8 * beacon_data,size_t beacon_data_len)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
iwl_mld_nan_config(struct iwl_mld * mld,struct ieee80211_vif * vif,struct cfg80211_nan_conf * conf,enum iwl_ctxt_action action)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
iwl_mld_start_nan(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct cfg80211_nan_conf * conf)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
iwl_mld_nan_change_config(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct cfg80211_nan_conf * conf,u32 changes)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
iwl_mld_stop_nan(struct ieee80211_hw * hw,struct ieee80211_vif * vif)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
iwl_mld_handle_nan_cluster_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)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
iwl_mld_cancel_nan_cluster_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt,u32 obj_id)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
iwl_mld_cancel_nan_dw_end_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt,u32 obj_id)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
iwl_mld_handle_nan_dw_end_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)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