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