xref: /linux/drivers/net/wireless/intel/iwlwifi/mld/nan.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
19e978d8eSIlan Peer // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
29e978d8eSIlan Peer /*
39e978d8eSIlan Peer  * Copyright (C) 2025 Intel Corporation
49e978d8eSIlan Peer  */
59e978d8eSIlan Peer 
69e978d8eSIlan Peer #include "mld.h"
79e978d8eSIlan Peer #include "iface.h"
853d649aeSMiri Korenblit #include "mlo.h"
99e978d8eSIlan Peer #include "fw/api/mac-cfg.h"
109e978d8eSIlan Peer 
119e978d8eSIlan Peer #define IWL_NAN_DISOVERY_BEACON_INTERNVAL_TU 512
129e978d8eSIlan Peer #define IWL_NAN_RSSI_CLOSE 55
139e978d8eSIlan Peer #define IWL_NAN_RSSI_MIDDLE 70
149e978d8eSIlan Peer 
159e978d8eSIlan Peer bool iwl_mld_nan_supported(struct iwl_mld *mld)
169e978d8eSIlan Peer {
179e978d8eSIlan Peer 	return fw_has_capa(&mld->fw->ucode_capa,
189e978d8eSIlan Peer 			   IWL_UCODE_TLV_CAPA_NAN_SYNC_SUPPORT);
199e978d8eSIlan Peer }
209e978d8eSIlan Peer 
2189000fe4SIlan Peer static int iwl_mld_nan_send_config_cmd(struct iwl_mld *mld,
2289000fe4SIlan Peer 				       struct iwl_nan_config_cmd *cmd,
2389000fe4SIlan Peer 				       u8 *beacon_data, size_t beacon_data_len)
249e978d8eSIlan Peer {
2589000fe4SIlan Peer 	struct iwl_host_cmd hcmd = {
2689000fe4SIlan Peer 		.id = WIDE_ID(MAC_CONF_GROUP, NAN_CFG_CMD),
2789000fe4SIlan Peer 	};
289e978d8eSIlan Peer 
2989000fe4SIlan Peer 	hcmd.len[0] = sizeof(*cmd);
3089000fe4SIlan Peer 	hcmd.data[0] = cmd;
319e978d8eSIlan Peer 
3289000fe4SIlan Peer 	if (beacon_data_len) {
3389000fe4SIlan Peer 		hcmd.len[1] = beacon_data_len;
3489000fe4SIlan Peer 		hcmd.data[1] = beacon_data;
3589000fe4SIlan Peer 		hcmd.dataflags[1] = IWL_HCMD_DFL_DUP;
3689000fe4SIlan Peer 	}
3789000fe4SIlan Peer 
3889000fe4SIlan Peer 	return iwl_mld_send_cmd(mld, &hcmd);
399e978d8eSIlan Peer }
409e978d8eSIlan Peer 
41ab410758SIlan Peer static int iwl_mld_nan_config(struct iwl_mld *mld,
42ab410758SIlan Peer 			      struct ieee80211_vif *vif,
43ab410758SIlan Peer 			      struct cfg80211_nan_conf *conf,
44ab410758SIlan Peer 			      enum iwl_ctxt_action action)
459e978d8eSIlan Peer {
469e978d8eSIlan Peer 	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
479e978d8eSIlan Peer 	struct iwl_nan_config_cmd cmd = {
48ab410758SIlan Peer 		.action = cpu_to_le32(action),
499e978d8eSIlan Peer 	};
5089000fe4SIlan Peer 	u8 *data __free(kfree) = NULL;
519e978d8eSIlan Peer 
529e978d8eSIlan Peer 	lockdep_assert_wiphy(mld->wiphy);
539e978d8eSIlan Peer 
549e978d8eSIlan Peer 	ether_addr_copy(cmd.nmi_addr, vif->addr);
559e978d8eSIlan Peer 	cmd.master_pref = conf->master_pref;
5689000fe4SIlan Peer 
57*5b31cf3fSBenjamin Berg 	memcpy(cmd.cluster_id, conf->cluster_id + 4,
58*5b31cf3fSBenjamin Berg 	       sizeof(cmd.cluster_id));
5989000fe4SIlan Peer 
6089000fe4SIlan Peer 	cmd.scan_period = conf->scan_period < 255 ? conf->scan_period : 255;
6189000fe4SIlan Peer 	cmd.dwell_time =
6289000fe4SIlan Peer 		conf->scan_dwell_time < 255 ? conf->scan_dwell_time : 255;
6389000fe4SIlan Peer 
6489000fe4SIlan Peer 	if (conf->discovery_beacon_interval)
6589000fe4SIlan Peer 		cmd.discovery_beacon_interval =
6689000fe4SIlan Peer 			cpu_to_le32(conf->discovery_beacon_interval);
6789000fe4SIlan Peer 	else
6889000fe4SIlan Peer 		cmd.discovery_beacon_interval =
6989000fe4SIlan Peer 			cpu_to_le32(IWL_NAN_DISOVERY_BEACON_INTERNVAL_TU);
7089000fe4SIlan Peer 
7189000fe4SIlan Peer 	if (conf->enable_dw_notification)
729e978d8eSIlan Peer 		cmd.flags = IWL_NAN_FLAG_DW_END_NOTIF_ENABLED;
739e978d8eSIlan Peer 
7489000fe4SIlan Peer 	/* 2 GHz band must be supported */
7589000fe4SIlan Peer 	cmd.band_config[IWL_NAN_BAND_2GHZ].rssi_close =
7689000fe4SIlan Peer 		abs(conf->band_cfgs[NL80211_BAND_2GHZ].rssi_close);
7789000fe4SIlan Peer 	cmd.band_config[IWL_NAN_BAND_2GHZ].rssi_middle =
7889000fe4SIlan Peer 		abs(conf->band_cfgs[NL80211_BAND_2GHZ].rssi_middle);
7989000fe4SIlan Peer 	cmd.band_config[IWL_NAN_BAND_2GHZ].dw_interval =
8089000fe4SIlan Peer 		conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval;
819e978d8eSIlan Peer 
8289000fe4SIlan Peer 	/* 5 GHz band operation is optional. Configure its operation if
8389000fe4SIlan Peer 	 * supported. Note that conf->bands might be zero, so we need to check
8489000fe4SIlan Peer 	 * the channel pointer, not the band mask.
8589000fe4SIlan Peer 	 */
8689000fe4SIlan Peer 	if (conf->band_cfgs[NL80211_BAND_5GHZ].chan) {
8789000fe4SIlan Peer 		cmd.hb_channel =
8889000fe4SIlan Peer 			conf->band_cfgs[NL80211_BAND_5GHZ].chan->hw_value;
8989000fe4SIlan Peer 
9089000fe4SIlan Peer 		cmd.band_config[IWL_NAN_BAND_5GHZ].rssi_close =
9189000fe4SIlan Peer 			abs(conf->band_cfgs[NL80211_BAND_5GHZ].rssi_close);
9289000fe4SIlan Peer 		cmd.band_config[IWL_NAN_BAND_5GHZ].rssi_middle =
9389000fe4SIlan Peer 			abs(conf->band_cfgs[NL80211_BAND_5GHZ].rssi_middle);
9489000fe4SIlan Peer 		cmd.band_config[IWL_NAN_BAND_5GHZ].dw_interval =
9589000fe4SIlan Peer 			conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval;
969e978d8eSIlan Peer 	}
9789000fe4SIlan Peer 
9889000fe4SIlan Peer 	if (conf->extra_nan_attrs_len || conf->vendor_elems_len) {
9989000fe4SIlan Peer 		data = kmalloc(conf->extra_nan_attrs_len +
10089000fe4SIlan Peer 			       conf->vendor_elems_len, GFP_KERNEL);
10189000fe4SIlan Peer 		if (!data)
10289000fe4SIlan Peer 			return -ENOMEM;
10389000fe4SIlan Peer 
10489000fe4SIlan Peer 		cmd.nan_attr_len = cpu_to_le32(conf->extra_nan_attrs_len);
10589000fe4SIlan Peer 		cmd.nan_vendor_elems_len = cpu_to_le32(conf->vendor_elems_len);
10689000fe4SIlan Peer 
10789000fe4SIlan Peer 		if (conf->extra_nan_attrs_len)
10889000fe4SIlan Peer 			memcpy(data, conf->extra_nan_attrs,
10989000fe4SIlan Peer 			       conf->extra_nan_attrs_len);
11089000fe4SIlan Peer 
11189000fe4SIlan Peer 		if (conf->vendor_elems_len)
11289000fe4SIlan Peer 			memcpy(data + conf->extra_nan_attrs_len,
11389000fe4SIlan Peer 			       conf->vendor_elems,
11489000fe4SIlan Peer 			       conf->vendor_elems_len);
1159e978d8eSIlan Peer 	}
1169e978d8eSIlan Peer 
117ab410758SIlan Peer 	cmd.sta_id = mld_vif->aux_sta.sta_id;
118ab410758SIlan Peer 	return iwl_mld_nan_send_config_cmd(mld, &cmd, data,
119ab410758SIlan Peer 					   conf->extra_nan_attrs_len +
120ab410758SIlan Peer 					   conf->vendor_elems_len);
121ab410758SIlan Peer }
122ab410758SIlan Peer 
123ab410758SIlan Peer int iwl_mld_start_nan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
124ab410758SIlan Peer 		      struct cfg80211_nan_conf *conf)
125ab410758SIlan Peer {
126ab410758SIlan Peer 	struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
127ab410758SIlan Peer 	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
128ab410758SIlan Peer 	struct iwl_mld_int_sta *aux_sta = &mld_vif->aux_sta;
129ab410758SIlan Peer 	int ret;
130ab410758SIlan Peer 
131ab410758SIlan Peer 	IWL_DEBUG_MAC80211(mld, "NAN: start: bands=0x%x\n", conf->bands);
132ab410758SIlan Peer 
13353d649aeSMiri Korenblit 	ret = iwl_mld_update_emlsr_block(mld, true, IWL_MLD_EMLSR_BLOCKED_NAN);
1349e978d8eSIlan Peer 	if (ret)
1359e978d8eSIlan Peer 		return ret;
1369e978d8eSIlan Peer 
13753d649aeSMiri Korenblit 	ret = iwl_mld_add_aux_sta(mld, aux_sta);
13853d649aeSMiri Korenblit 	if (ret)
13953d649aeSMiri Korenblit 		goto unblock_emlsr;
14053d649aeSMiri Korenblit 
141ab410758SIlan Peer 	ret = iwl_mld_nan_config(mld, vif, conf, FW_CTXT_ACTION_ADD);
1429e978d8eSIlan Peer 	if (ret) {
1439e978d8eSIlan Peer 		IWL_ERR(mld, "Failed to start NAN. ret=%d\n", ret);
14453d649aeSMiri Korenblit 		goto remove_aux;
1459e978d8eSIlan Peer 	}
14653d649aeSMiri Korenblit 	return 0;
14753d649aeSMiri Korenblit 
14853d649aeSMiri Korenblit remove_aux:
14953d649aeSMiri Korenblit 	iwl_mld_remove_aux_sta(mld, vif);
15053d649aeSMiri Korenblit unblock_emlsr:
15153d649aeSMiri Korenblit 	iwl_mld_update_emlsr_block(mld, false, IWL_MLD_EMLSR_BLOCKED_NAN);
1529e978d8eSIlan Peer 
1539e978d8eSIlan Peer 	return ret;
1549e978d8eSIlan Peer }
1559e978d8eSIlan Peer 
156ab410758SIlan Peer int iwl_mld_nan_change_config(struct ieee80211_hw *hw,
157ab410758SIlan Peer 			      struct ieee80211_vif *vif,
158ab410758SIlan Peer 			      struct cfg80211_nan_conf *conf,
159ab410758SIlan Peer 			      u32 changes)
160ab410758SIlan Peer {
161ab410758SIlan Peer 	struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
162ab410758SIlan Peer 
163ab410758SIlan Peer 	IWL_DEBUG_MAC80211(mld, "NAN: change: changes=0x%x, bands=0x%x\n",
164ab410758SIlan Peer 			   changes, conf->bands);
165ab410758SIlan Peer 
166ab410758SIlan Peer 	/* Note that we do not use 'changes' as the FW always expects the
167ab410758SIlan Peer 	 * complete configuration, and mac80211 always provides the complete
168ab410758SIlan Peer 	 * configuration.
169ab410758SIlan Peer 	 */
170ab410758SIlan Peer 	return iwl_mld_nan_config(mld, vif, conf, FW_CTXT_ACTION_MODIFY);
171ab410758SIlan Peer }
172ab410758SIlan Peer 
1739e978d8eSIlan Peer int iwl_mld_stop_nan(struct ieee80211_hw *hw,
1749e978d8eSIlan Peer 		     struct ieee80211_vif *vif)
1759e978d8eSIlan Peer {
1769e978d8eSIlan Peer 	struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
1779e978d8eSIlan Peer 	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
1789e978d8eSIlan Peer 	struct iwl_nan_config_cmd cmd = {
1799e978d8eSIlan Peer 		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
1809e978d8eSIlan Peer 	};
1819e978d8eSIlan Peer 	int ret;
1829e978d8eSIlan Peer 
1839e978d8eSIlan Peer 	lockdep_assert_wiphy(mld->wiphy);
1849e978d8eSIlan Peer 
1859e978d8eSIlan Peer 	ret = iwl_mld_send_cmd_pdu(mld,
1869e978d8eSIlan Peer 				   WIDE_ID(MAC_CONF_GROUP, NAN_CFG_CMD),
1879e978d8eSIlan Peer 				   &cmd);
1889e978d8eSIlan Peer 	if (ret)
1899e978d8eSIlan Peer 		IWL_ERR(mld, "NAN: Failed to stop NAN. ret=%d\n", ret);
1909e978d8eSIlan Peer 
1919e978d8eSIlan Peer 	/* assume that higher layer guarantees that no additional frames are
1929e978d8eSIlan Peer 	 * added before calling this callback
1939e978d8eSIlan Peer 	 */
1949e978d8eSIlan Peer 	iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
1959e978d8eSIlan Peer 	iwl_mld_remove_aux_sta(mld, vif);
1969e978d8eSIlan Peer 
19760094146SIlan Peer 	/* cancel based on object type being NAN, as the NAN objects do
19860094146SIlan Peer 	 * not have a unique identifier associated with them
19960094146SIlan Peer 	 */
20060094146SIlan Peer 	iwl_mld_cancel_notifications_of_object(mld,
20160094146SIlan Peer 					       IWL_MLD_OBJECT_TYPE_NAN,
20260094146SIlan Peer 					       0);
20353d649aeSMiri Korenblit 
20453d649aeSMiri Korenblit 	iwl_mld_update_emlsr_block(mld, false, IWL_MLD_EMLSR_BLOCKED_NAN);
20553d649aeSMiri Korenblit 
2069e978d8eSIlan Peer 	return 0;
2079e978d8eSIlan Peer }
2089e978d8eSIlan Peer 
2099e978d8eSIlan Peer void iwl_mld_handle_nan_cluster_notif(struct iwl_mld *mld,
2109e978d8eSIlan Peer 				      struct iwl_rx_packet *pkt)
2119e978d8eSIlan Peer {
2129e978d8eSIlan Peer 	struct iwl_nan_cluster_notif *notif = (void *)pkt->data;
21360094146SIlan Peer 	struct wireless_dev *wdev = mld->nan_device_vif ?
21460094146SIlan Peer 		ieee80211_vif_to_wdev(mld->nan_device_vif) : NULL;
21560094146SIlan Peer 	bool new_cluster = !!(notif->flags &
21660094146SIlan Peer 			      IWL_NAN_CLUSTER_NOTIF_FLAG_NEW_CLUSTER);
217*5b31cf3fSBenjamin Berg 	u8 cluster_id[ETH_ALEN] = {
218*5b31cf3fSBenjamin Berg 		0x50, 0x6f, 0x9a, 0x01,
219*5b31cf3fSBenjamin Berg 		notif->cluster_id[0], notif->cluster_id[1]
22060094146SIlan Peer 	};
2219e978d8eSIlan Peer 
2229e978d8eSIlan Peer 	IWL_DEBUG_INFO(mld,
223*5b31cf3fSBenjamin Berg 		       "NAN: cluster event: cluster_id=%pM, flags=0x%x\n",
224*5b31cf3fSBenjamin Berg 		       cluster_id, notif->flags);
22560094146SIlan Peer 
22660094146SIlan Peer 	if (IWL_FW_CHECK(mld, !wdev, "NAN: cluster event without wdev\n"))
22760094146SIlan Peer 		return;
22860094146SIlan Peer 
22960094146SIlan Peer 	if (IWL_FW_CHECK(mld, !ieee80211_vif_nan_started(mld->nan_device_vif),
23060094146SIlan Peer 			 "NAN: cluster event without NAN started\n"))
23160094146SIlan Peer 		return;
23260094146SIlan Peer 
23360094146SIlan Peer 	cfg80211_nan_cluster_joined(wdev, cluster_id, new_cluster, GFP_KERNEL);
23460094146SIlan Peer }
23560094146SIlan Peer 
23660094146SIlan Peer bool iwl_mld_cancel_nan_cluster_notif(struct iwl_mld *mld,
23760094146SIlan Peer 				      struct iwl_rx_packet *pkt,
23860094146SIlan Peer 				      u32 obj_id)
23960094146SIlan Peer {
24060094146SIlan Peer 	return true;
24160094146SIlan Peer }
24260094146SIlan Peer 
24360094146SIlan Peer bool iwl_mld_cancel_nan_dw_end_notif(struct iwl_mld *mld,
24460094146SIlan Peer 				     struct iwl_rx_packet *pkt,
24560094146SIlan Peer 				     u32 obj_id)
24660094146SIlan Peer {
24760094146SIlan Peer 	return true;
2489e978d8eSIlan Peer }
2499e978d8eSIlan Peer 
2509e978d8eSIlan Peer void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld,
2519e978d8eSIlan Peer 				     struct iwl_rx_packet *pkt)
2529e978d8eSIlan Peer {
2539e978d8eSIlan Peer 	struct iwl_nan_dw_end_notif *notif = (void *)pkt->data;
2549e978d8eSIlan Peer 	struct iwl_mld_vif *mld_vif = mld->nan_device_vif ?
2559e978d8eSIlan Peer 		iwl_mld_vif_from_mac80211(mld->nan_device_vif) :
2569e978d8eSIlan Peer 		NULL;
25760094146SIlan Peer 	struct wireless_dev *wdev;
25860094146SIlan Peer 	struct ieee80211_channel *chan;
2599e978d8eSIlan Peer 
2609e978d8eSIlan Peer 	IWL_INFO(mld, "NAN: DW end: band=%u\n", notif->band);
2619e978d8eSIlan Peer 
26260094146SIlan Peer 	if (IWL_FW_CHECK(mld, !mld_vif, "NAN: DW end without mld_vif\n"))
26360094146SIlan Peer 		return;
26460094146SIlan Peer 
26560094146SIlan Peer 	if (IWL_FW_CHECK(mld, !ieee80211_vif_nan_started(mld->nan_device_vif),
26660094146SIlan Peer 			 "NAN: DW end without NAN started\n"))
2679e978d8eSIlan Peer 		return;
2689e978d8eSIlan Peer 
2699e978d8eSIlan Peer 	if (WARN_ON(mld_vif->aux_sta.sta_id == IWL_INVALID_STA))
2709e978d8eSIlan Peer 		return;
2719e978d8eSIlan Peer 
2729e978d8eSIlan Peer 	IWL_DEBUG_INFO(mld, "NAN: flush queues for aux sta=%u\n",
2739e978d8eSIlan Peer 		       mld_vif->aux_sta.sta_id);
2749e978d8eSIlan Peer 
2759e978d8eSIlan Peer 	iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
27660094146SIlan Peer 
27760094146SIlan Peer 	/* TODO: currently the notification specified the band on which the DW
27860094146SIlan Peer 	 * ended. Need to change that to the actual channel on which the next DW
27960094146SIlan Peer 	 * will be started.
28060094146SIlan Peer 	 */
28160094146SIlan Peer 	switch (notif->band) {
28260094146SIlan Peer 	case IWL_NAN_BAND_2GHZ:
28360094146SIlan Peer 		chan = ieee80211_get_channel(mld->wiphy, 2437);
28460094146SIlan Peer 		break;
28560094146SIlan Peer 	case IWL_NAN_BAND_5GHZ:
28660094146SIlan Peer 		/* TODO: use the actual channel */
28760094146SIlan Peer 		chan = ieee80211_get_channel(mld->wiphy, 5745);
28860094146SIlan Peer 		break;
28960094146SIlan Peer 	default:
29060094146SIlan Peer 		IWL_FW_CHECK(mld, false,
29160094146SIlan Peer 			     "NAN: Invalid band %u in DW end notif\n",
29260094146SIlan Peer 			     notif->band);
29360094146SIlan Peer 		return;
29460094146SIlan Peer 	}
29560094146SIlan Peer 
29660094146SIlan Peer 	wdev = ieee80211_vif_to_wdev(mld->nan_device_vif);
29760094146SIlan Peer 	cfg80211_next_nan_dw_notif(wdev, chan, GFP_KERNEL);
2989e978d8eSIlan Peer }
299