xref: /linux/drivers/net/wireless/intel/iwlwifi/mld/nan.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
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 	memcpy(cmd.cluster_id, conf->cluster_id + 4,
58 	       sizeof(cmd.cluster_id));
59 
60 	cmd.scan_period = conf->scan_period < 255 ? conf->scan_period : 255;
61 	cmd.dwell_time =
62 		conf->scan_dwell_time < 255 ? conf->scan_dwell_time : 255;
63 
64 	if (conf->discovery_beacon_interval)
65 		cmd.discovery_beacon_interval =
66 			cpu_to_le32(conf->discovery_beacon_interval);
67 	else
68 		cmd.discovery_beacon_interval =
69 			cpu_to_le32(IWL_NAN_DISOVERY_BEACON_INTERNVAL_TU);
70 
71 	if (conf->enable_dw_notification)
72 		cmd.flags = IWL_NAN_FLAG_DW_END_NOTIF_ENABLED;
73 
74 	/* 2 GHz band must be supported */
75 	cmd.band_config[IWL_NAN_BAND_2GHZ].rssi_close =
76 		abs(conf->band_cfgs[NL80211_BAND_2GHZ].rssi_close);
77 	cmd.band_config[IWL_NAN_BAND_2GHZ].rssi_middle =
78 		abs(conf->band_cfgs[NL80211_BAND_2GHZ].rssi_middle);
79 	cmd.band_config[IWL_NAN_BAND_2GHZ].dw_interval =
80 		conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval;
81 
82 	/* 5 GHz band operation is optional. Configure its operation if
83 	 * supported. Note that conf->bands might be zero, so we need to check
84 	 * the channel pointer, not the band mask.
85 	 */
86 	if (conf->band_cfgs[NL80211_BAND_5GHZ].chan) {
87 		cmd.hb_channel =
88 			conf->band_cfgs[NL80211_BAND_5GHZ].chan->hw_value;
89 
90 		cmd.band_config[IWL_NAN_BAND_5GHZ].rssi_close =
91 			abs(conf->band_cfgs[NL80211_BAND_5GHZ].rssi_close);
92 		cmd.band_config[IWL_NAN_BAND_5GHZ].rssi_middle =
93 			abs(conf->band_cfgs[NL80211_BAND_5GHZ].rssi_middle);
94 		cmd.band_config[IWL_NAN_BAND_5GHZ].dw_interval =
95 			conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval;
96 	}
97 
98 	if (conf->extra_nan_attrs_len || conf->vendor_elems_len) {
99 		data = kmalloc(conf->extra_nan_attrs_len +
100 			       conf->vendor_elems_len, GFP_KERNEL);
101 		if (!data)
102 			return -ENOMEM;
103 
104 		cmd.nan_attr_len = cpu_to_le32(conf->extra_nan_attrs_len);
105 		cmd.nan_vendor_elems_len = cpu_to_le32(conf->vendor_elems_len);
106 
107 		if (conf->extra_nan_attrs_len)
108 			memcpy(data, conf->extra_nan_attrs,
109 			       conf->extra_nan_attrs_len);
110 
111 		if (conf->vendor_elems_len)
112 			memcpy(data + conf->extra_nan_attrs_len,
113 			       conf->vendor_elems,
114 			       conf->vendor_elems_len);
115 	}
116 
117 	cmd.sta_id = mld_vif->aux_sta.sta_id;
118 	return iwl_mld_nan_send_config_cmd(mld, &cmd, data,
119 					   conf->extra_nan_attrs_len +
120 					   conf->vendor_elems_len);
121 }
122 
123 int iwl_mld_start_nan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
124 		      struct cfg80211_nan_conf *conf)
125 {
126 	struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
127 	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
128 	struct iwl_mld_int_sta *aux_sta = &mld_vif->aux_sta;
129 	int ret;
130 
131 	IWL_DEBUG_MAC80211(mld, "NAN: start: bands=0x%x\n", conf->bands);
132 
133 	ret = iwl_mld_update_emlsr_block(mld, true, IWL_MLD_EMLSR_BLOCKED_NAN);
134 	if (ret)
135 		return ret;
136 
137 	ret = iwl_mld_add_aux_sta(mld, aux_sta);
138 	if (ret)
139 		goto unblock_emlsr;
140 
141 	ret = iwl_mld_nan_config(mld, vif, conf, FW_CTXT_ACTION_ADD);
142 	if (ret) {
143 		IWL_ERR(mld, "Failed to start NAN. ret=%d\n", ret);
144 		goto remove_aux;
145 	}
146 	return 0;
147 
148 remove_aux:
149 	iwl_mld_remove_aux_sta(mld, vif);
150 unblock_emlsr:
151 	iwl_mld_update_emlsr_block(mld, false, IWL_MLD_EMLSR_BLOCKED_NAN);
152 
153 	return ret;
154 }
155 
156 int iwl_mld_nan_change_config(struct ieee80211_hw *hw,
157 			      struct ieee80211_vif *vif,
158 			      struct cfg80211_nan_conf *conf,
159 			      u32 changes)
160 {
161 	struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
162 
163 	IWL_DEBUG_MAC80211(mld, "NAN: change: changes=0x%x, bands=0x%x\n",
164 			   changes, conf->bands);
165 
166 	/* Note that we do not use 'changes' as the FW always expects the
167 	 * complete configuration, and mac80211 always provides the complete
168 	 * configuration.
169 	 */
170 	return iwl_mld_nan_config(mld, vif, conf, FW_CTXT_ACTION_MODIFY);
171 }
172 
173 int iwl_mld_stop_nan(struct ieee80211_hw *hw,
174 		     struct ieee80211_vif *vif)
175 {
176 	struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
177 	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
178 	struct iwl_nan_config_cmd cmd = {
179 		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
180 	};
181 	int ret;
182 
183 	lockdep_assert_wiphy(mld->wiphy);
184 
185 	ret = iwl_mld_send_cmd_pdu(mld,
186 				   WIDE_ID(MAC_CONF_GROUP, NAN_CFG_CMD),
187 				   &cmd);
188 	if (ret)
189 		IWL_ERR(mld, "NAN: Failed to stop NAN. ret=%d\n", ret);
190 
191 	/* assume that higher layer guarantees that no additional frames are
192 	 * added before calling this callback
193 	 */
194 	iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
195 	iwl_mld_remove_aux_sta(mld, vif);
196 
197 	/* cancel based on object type being NAN, as the NAN objects do
198 	 * not have a unique identifier associated with them
199 	 */
200 	iwl_mld_cancel_notifications_of_object(mld,
201 					       IWL_MLD_OBJECT_TYPE_NAN,
202 					       0);
203 
204 	iwl_mld_update_emlsr_block(mld, false, IWL_MLD_EMLSR_BLOCKED_NAN);
205 
206 	return 0;
207 }
208 
209 void iwl_mld_handle_nan_cluster_notif(struct iwl_mld *mld,
210 				      struct iwl_rx_packet *pkt)
211 {
212 	struct iwl_nan_cluster_notif *notif = (void *)pkt->data;
213 	struct wireless_dev *wdev = mld->nan_device_vif ?
214 		ieee80211_vif_to_wdev(mld->nan_device_vif) : NULL;
215 	bool new_cluster = !!(notif->flags &
216 			      IWL_NAN_CLUSTER_NOTIF_FLAG_NEW_CLUSTER);
217 	u8 cluster_id[ETH_ALEN] = {
218 		0x50, 0x6f, 0x9a, 0x01,
219 		notif->cluster_id[0], notif->cluster_id[1]
220 	};
221 
222 	IWL_DEBUG_INFO(mld,
223 		       "NAN: cluster event: cluster_id=%pM, flags=0x%x\n",
224 		       cluster_id, notif->flags);
225 
226 	if (IWL_FW_CHECK(mld, !wdev, "NAN: cluster event without wdev\n"))
227 		return;
228 
229 	if (IWL_FW_CHECK(mld, !ieee80211_vif_nan_started(mld->nan_device_vif),
230 			 "NAN: cluster event without NAN started\n"))
231 		return;
232 
233 	cfg80211_nan_cluster_joined(wdev, cluster_id, new_cluster, GFP_KERNEL);
234 }
235 
236 bool iwl_mld_cancel_nan_cluster_notif(struct iwl_mld *mld,
237 				      struct iwl_rx_packet *pkt,
238 				      u32 obj_id)
239 {
240 	return true;
241 }
242 
243 bool iwl_mld_cancel_nan_dw_end_notif(struct iwl_mld *mld,
244 				     struct iwl_rx_packet *pkt,
245 				     u32 obj_id)
246 {
247 	return true;
248 }
249 
250 void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld,
251 				     struct iwl_rx_packet *pkt)
252 {
253 	struct iwl_nan_dw_end_notif *notif = (void *)pkt->data;
254 	struct iwl_mld_vif *mld_vif = mld->nan_device_vif ?
255 		iwl_mld_vif_from_mac80211(mld->nan_device_vif) :
256 		NULL;
257 	struct wireless_dev *wdev;
258 	struct ieee80211_channel *chan;
259 
260 	IWL_INFO(mld, "NAN: DW end: band=%u\n", notif->band);
261 
262 	if (IWL_FW_CHECK(mld, !mld_vif, "NAN: DW end without mld_vif\n"))
263 		return;
264 
265 	if (IWL_FW_CHECK(mld, !ieee80211_vif_nan_started(mld->nan_device_vif),
266 			 "NAN: DW end without NAN started\n"))
267 		return;
268 
269 	if (WARN_ON(mld_vif->aux_sta.sta_id == IWL_INVALID_STA))
270 		return;
271 
272 	IWL_DEBUG_INFO(mld, "NAN: flush queues for aux sta=%u\n",
273 		       mld_vif->aux_sta.sta_id);
274 
275 	iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
276 
277 	/* TODO: currently the notification specified the band on which the DW
278 	 * ended. Need to change that to the actual channel on which the next DW
279 	 * will be started.
280 	 */
281 	switch (notif->band) {
282 	case IWL_NAN_BAND_2GHZ:
283 		chan = ieee80211_get_channel(mld->wiphy, 2437);
284 		break;
285 	case IWL_NAN_BAND_5GHZ:
286 		/* TODO: use the actual channel */
287 		chan = ieee80211_get_channel(mld->wiphy, 5745);
288 		break;
289 	default:
290 		IWL_FW_CHECK(mld, false,
291 			     "NAN: Invalid band %u in DW end notif\n",
292 			     notif->band);
293 		return;
294 	}
295 
296 	wdev = ieee80211_vif_to_wdev(mld->nan_device_vif);
297 	cfg80211_next_nan_dw_notif(wdev, chan, GFP_KERNEL);
298 }
299