1*6b627f88SBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2*6b627f88SBjoern A. Zeeb /*
3*6b627f88SBjoern A. Zeeb  * Copyright (C) 2024 - 2025 Intel Corporation
4*6b627f88SBjoern A. Zeeb  */
5*6b627f88SBjoern A. Zeeb #include <net/cfg80211.h>
6*6b627f88SBjoern A. Zeeb #include <net/mac80211.h>
7*6b627f88SBjoern A. Zeeb 
8*6b627f88SBjoern A. Zeeb #include "mld.h"
9*6b627f88SBjoern A. Zeeb #include "roc.h"
10*6b627f88SBjoern A. Zeeb #include "hcmd.h"
11*6b627f88SBjoern A. Zeeb #include "iface.h"
12*6b627f88SBjoern A. Zeeb #include "sta.h"
13*6b627f88SBjoern A. Zeeb #include "mlo.h"
14*6b627f88SBjoern A. Zeeb 
15*6b627f88SBjoern A. Zeeb #include "fw/api/context.h"
16*6b627f88SBjoern A. Zeeb #include "fw/api/time-event.h"
17*6b627f88SBjoern A. Zeeb 
18*6b627f88SBjoern A. Zeeb #define AUX_ROC_MAX_DELAY MSEC_TO_TU(200)
19*6b627f88SBjoern A. Zeeb 
20*6b627f88SBjoern A. Zeeb static void
iwl_mld_vif_iter_emlsr_block_roc(void * data,u8 * mac,struct ieee80211_vif * vif)21*6b627f88SBjoern A. Zeeb iwl_mld_vif_iter_emlsr_block_roc(void *data, u8 *mac, struct ieee80211_vif *vif)
22*6b627f88SBjoern A. Zeeb {
23*6b627f88SBjoern A. Zeeb 	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
24*6b627f88SBjoern A. Zeeb 	int *result = data;
25*6b627f88SBjoern A. Zeeb 	int ret;
26*6b627f88SBjoern A. Zeeb 
27*6b627f88SBjoern A. Zeeb 	ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif,
28*6b627f88SBjoern A. Zeeb 				       IWL_MLD_EMLSR_BLOCKED_ROC,
29*6b627f88SBjoern A. Zeeb 				       iwl_mld_get_primary_link(vif));
30*6b627f88SBjoern A. Zeeb 	if (ret)
31*6b627f88SBjoern A. Zeeb 		*result = ret;
32*6b627f88SBjoern A. Zeeb }
33*6b627f88SBjoern A. Zeeb 
34*6b627f88SBjoern A. Zeeb struct iwl_mld_roc_iter_data {
35*6b627f88SBjoern A. Zeeb 	enum iwl_roc_activity activity;
36*6b627f88SBjoern A. Zeeb 	struct ieee80211_vif *vif;
37*6b627f88SBjoern A. Zeeb 	bool found;
38*6b627f88SBjoern A. Zeeb };
39*6b627f88SBjoern A. Zeeb 
iwl_mld_find_roc_vif_iter(void * data,u8 * mac,struct ieee80211_vif * vif)40*6b627f88SBjoern A. Zeeb static void iwl_mld_find_roc_vif_iter(void *data, u8 *mac,
41*6b627f88SBjoern A. Zeeb 				      struct ieee80211_vif *vif)
42*6b627f88SBjoern A. Zeeb {
43*6b627f88SBjoern A. Zeeb 	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
44*6b627f88SBjoern A. Zeeb 	struct iwl_mld_roc_iter_data *roc_data = data;
45*6b627f88SBjoern A. Zeeb 
46*6b627f88SBjoern A. Zeeb 	if (mld_vif->roc_activity != roc_data->activity)
47*6b627f88SBjoern A. Zeeb 		return;
48*6b627f88SBjoern A. Zeeb 
49*6b627f88SBjoern A. Zeeb 	/* The FW supports one ROC of each type simultaneously */
50*6b627f88SBjoern A. Zeeb 	if (WARN_ON(roc_data->found)) {
51*6b627f88SBjoern A. Zeeb 		roc_data->vif = NULL;
52*6b627f88SBjoern A. Zeeb 		return;
53*6b627f88SBjoern A. Zeeb 	}
54*6b627f88SBjoern A. Zeeb 
55*6b627f88SBjoern A. Zeeb 	roc_data->found = true;
56*6b627f88SBjoern A. Zeeb 	roc_data->vif = vif;
57*6b627f88SBjoern A. Zeeb }
58*6b627f88SBjoern A. Zeeb 
59*6b627f88SBjoern A. Zeeb static struct ieee80211_vif *
iwl_mld_find_roc_vif(struct iwl_mld * mld,enum iwl_roc_activity activity)60*6b627f88SBjoern A. Zeeb iwl_mld_find_roc_vif(struct iwl_mld *mld, enum iwl_roc_activity activity)
61*6b627f88SBjoern A. Zeeb {
62*6b627f88SBjoern A. Zeeb 	struct iwl_mld_roc_iter_data roc_data = {
63*6b627f88SBjoern A. Zeeb 		.activity = activity,
64*6b627f88SBjoern A. Zeeb 		.found = false,
65*6b627f88SBjoern A. Zeeb 	};
66*6b627f88SBjoern A. Zeeb 
67*6b627f88SBjoern A. Zeeb 	ieee80211_iterate_active_interfaces_mtx(mld->hw,
68*6b627f88SBjoern A. Zeeb 						IEEE80211_IFACE_ITER_NORMAL,
69*6b627f88SBjoern A. Zeeb 						iwl_mld_find_roc_vif_iter,
70*6b627f88SBjoern A. Zeeb 						&roc_data);
71*6b627f88SBjoern A. Zeeb 
72*6b627f88SBjoern A. Zeeb 	return roc_data.vif;
73*6b627f88SBjoern A. Zeeb }
74*6b627f88SBjoern A. Zeeb 
iwl_mld_start_roc(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_channel * channel,int duration,enum ieee80211_roc_type type)75*6b627f88SBjoern A. Zeeb int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
76*6b627f88SBjoern A. Zeeb 		      struct ieee80211_channel *channel, int duration,
77*6b627f88SBjoern A. Zeeb 		      enum ieee80211_roc_type type)
78*6b627f88SBjoern A. Zeeb {
79*6b627f88SBjoern A. Zeeb 	struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
80*6b627f88SBjoern A. Zeeb 	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
81*6b627f88SBjoern A. Zeeb 	struct iwl_mld_int_sta *aux_sta = &mld_vif->aux_sta;
82*6b627f88SBjoern A. Zeeb 	struct iwl_roc_req cmd = {
83*6b627f88SBjoern A. Zeeb 		.action = cpu_to_le32(FW_CTXT_ACTION_ADD),
84*6b627f88SBjoern A. Zeeb 	};
85*6b627f88SBjoern A. Zeeb 	u8 ver = iwl_fw_lookup_cmd_ver(mld->fw,
86*6b627f88SBjoern A. Zeeb 				       WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0);
87*6b627f88SBjoern A. Zeeb 	u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(cmd);
88*6b627f88SBjoern A. Zeeb 	enum iwl_roc_activity activity;
89*6b627f88SBjoern A. Zeeb 	int ret = 0;
90*6b627f88SBjoern A. Zeeb 
91*6b627f88SBjoern A. Zeeb 	lockdep_assert_wiphy(mld->wiphy);
92*6b627f88SBjoern A. Zeeb 
93*6b627f88SBjoern A. Zeeb 	if (vif->type != NL80211_IFTYPE_P2P_DEVICE &&
94*6b627f88SBjoern A. Zeeb 	    vif->type != NL80211_IFTYPE_STATION) {
95*6b627f88SBjoern A. Zeeb 		IWL_ERR(mld, "NOT SUPPORTED: ROC on vif->type %d\n",
96*6b627f88SBjoern A. Zeeb 			vif->type);
97*6b627f88SBjoern A. Zeeb 
98*6b627f88SBjoern A. Zeeb 		return -EOPNOTSUPP;
99*6b627f88SBjoern A. Zeeb 	}
100*6b627f88SBjoern A. Zeeb 
101*6b627f88SBjoern A. Zeeb 	if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
102*6b627f88SBjoern A. Zeeb 		switch (type) {
103*6b627f88SBjoern A. Zeeb 		case IEEE80211_ROC_TYPE_NORMAL:
104*6b627f88SBjoern A. Zeeb 			activity = ROC_ACTIVITY_P2P_DISC;
105*6b627f88SBjoern A. Zeeb 			break;
106*6b627f88SBjoern A. Zeeb 		case IEEE80211_ROC_TYPE_MGMT_TX:
107*6b627f88SBjoern A. Zeeb 			activity = ROC_ACTIVITY_P2P_NEG;
108*6b627f88SBjoern A. Zeeb 			break;
109*6b627f88SBjoern A. Zeeb 		default:
110*6b627f88SBjoern A. Zeeb 			WARN_ONCE(1, "Got an invalid P2P ROC type\n");
111*6b627f88SBjoern A. Zeeb 			return -EINVAL;
112*6b627f88SBjoern A. Zeeb 		}
113*6b627f88SBjoern A. Zeeb 	} else {
114*6b627f88SBjoern A. Zeeb 		activity = ROC_ACTIVITY_HOTSPOT;
115*6b627f88SBjoern A. Zeeb 	}
116*6b627f88SBjoern A. Zeeb 
117*6b627f88SBjoern A. Zeeb 	/* The FW supports one ROC of each type simultaneously */
118*6b627f88SBjoern A. Zeeb 	if (WARN_ON(iwl_mld_find_roc_vif(mld, activity)))
119*6b627f88SBjoern A. Zeeb 		return -EBUSY;
120*6b627f88SBjoern A. Zeeb 
121*6b627f88SBjoern A. Zeeb 	ieee80211_iterate_active_interfaces_mtx(mld->hw,
122*6b627f88SBjoern A. Zeeb 						IEEE80211_IFACE_ITER_NORMAL,
123*6b627f88SBjoern A. Zeeb 						iwl_mld_vif_iter_emlsr_block_roc,
124*6b627f88SBjoern A. Zeeb 						&ret);
125*6b627f88SBjoern A. Zeeb 	if (ret)
126*6b627f88SBjoern A. Zeeb 		return ret;
127*6b627f88SBjoern A. Zeeb 
128*6b627f88SBjoern A. Zeeb 	ret = iwl_mld_add_aux_sta(mld, aux_sta);
129*6b627f88SBjoern A. Zeeb 	if (ret)
130*6b627f88SBjoern A. Zeeb 		return ret;
131*6b627f88SBjoern A. Zeeb 
132*6b627f88SBjoern A. Zeeb 	cmd.activity = cpu_to_le32(activity);
133*6b627f88SBjoern A. Zeeb 	cmd.sta_id = cpu_to_le32(aux_sta->sta_id);
134*6b627f88SBjoern A. Zeeb 	cmd.channel_info.channel = cpu_to_le32(channel->hw_value);
135*6b627f88SBjoern A. Zeeb 	cmd.channel_info.band = iwl_mld_nl80211_band_to_fw(channel->band);
136*6b627f88SBjoern A. Zeeb 	cmd.channel_info.width = IWL_PHY_CHANNEL_MODE20;
137*6b627f88SBjoern A. Zeeb 	cmd.max_delay = cpu_to_le32(AUX_ROC_MAX_DELAY);
138*6b627f88SBjoern A. Zeeb 	cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));
139*6b627f88SBjoern A. Zeeb 
140*6b627f88SBjoern A. Zeeb 	memcpy(cmd.node_addr, vif->addr, ETH_ALEN);
141*6b627f88SBjoern A. Zeeb 
142*6b627f88SBjoern A. Zeeb 	ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, ROC_CMD),
143*6b627f88SBjoern A. Zeeb 				   &cmd, cmd_len);
144*6b627f88SBjoern A. Zeeb 	if (ret) {
145*6b627f88SBjoern A. Zeeb 		IWL_ERR(mld, "Couldn't send the ROC_CMD\n");
146*6b627f88SBjoern A. Zeeb 		return ret;
147*6b627f88SBjoern A. Zeeb 	}
148*6b627f88SBjoern A. Zeeb 
149*6b627f88SBjoern A. Zeeb 	mld_vif->roc_activity = activity;
150*6b627f88SBjoern A. Zeeb 
151*6b627f88SBjoern A. Zeeb 	return 0;
152*6b627f88SBjoern A. Zeeb }
153*6b627f88SBjoern A. Zeeb 
154*6b627f88SBjoern A. Zeeb static void
iwl_mld_vif_iter_emlsr_unblock_roc(void * data,u8 * mac,struct ieee80211_vif * vif)155*6b627f88SBjoern A. Zeeb iwl_mld_vif_iter_emlsr_unblock_roc(void *data, u8 *mac,
156*6b627f88SBjoern A. Zeeb 				   struct ieee80211_vif *vif)
157*6b627f88SBjoern A. Zeeb {
158*6b627f88SBjoern A. Zeeb 	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
159*6b627f88SBjoern A. Zeeb 
160*6b627f88SBjoern A. Zeeb 	iwl_mld_unblock_emlsr(mld_vif->mld, vif, IWL_MLD_EMLSR_BLOCKED_ROC);
161*6b627f88SBjoern A. Zeeb }
162*6b627f88SBjoern A. Zeeb 
iwl_mld_destroy_roc(struct iwl_mld * mld,struct ieee80211_vif * vif,struct iwl_mld_vif * mld_vif)163*6b627f88SBjoern A. Zeeb static void iwl_mld_destroy_roc(struct iwl_mld *mld,
164*6b627f88SBjoern A. Zeeb 				struct ieee80211_vif *vif,
165*6b627f88SBjoern A. Zeeb 				struct iwl_mld_vif *mld_vif)
166*6b627f88SBjoern A. Zeeb {
167*6b627f88SBjoern A. Zeeb 	mld_vif->roc_activity = ROC_NUM_ACTIVITIES;
168*6b627f88SBjoern A. Zeeb 
169*6b627f88SBjoern A. Zeeb 	ieee80211_iterate_active_interfaces_mtx(mld->hw,
170*6b627f88SBjoern A. Zeeb 						IEEE80211_IFACE_ITER_NORMAL,
171*6b627f88SBjoern A. Zeeb 						iwl_mld_vif_iter_emlsr_unblock_roc,
172*6b627f88SBjoern A. Zeeb 						NULL);
173*6b627f88SBjoern A. Zeeb 
174*6b627f88SBjoern A. Zeeb 	/* wait until every tx has seen that roc_activity has been reset */
175*6b627f88SBjoern A. Zeeb 	synchronize_net();
176*6b627f88SBjoern A. Zeeb 	/* from here, no new tx will be added
177*6b627f88SBjoern A. Zeeb 	 * we can flush the Tx on the queues
178*6b627f88SBjoern A. Zeeb 	 */
179*6b627f88SBjoern A. Zeeb 
180*6b627f88SBjoern A. Zeeb 	iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
181*6b627f88SBjoern A. Zeeb 
182*6b627f88SBjoern A. Zeeb 	iwl_mld_remove_aux_sta(mld, vif);
183*6b627f88SBjoern A. Zeeb }
184*6b627f88SBjoern A. Zeeb 
iwl_mld_cancel_roc(struct ieee80211_hw * hw,struct ieee80211_vif * vif)185*6b627f88SBjoern A. Zeeb int iwl_mld_cancel_roc(struct ieee80211_hw *hw,
186*6b627f88SBjoern A. Zeeb 		       struct ieee80211_vif *vif)
187*6b627f88SBjoern A. Zeeb {
188*6b627f88SBjoern A. Zeeb 	struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
189*6b627f88SBjoern A. Zeeb 	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
190*6b627f88SBjoern A. Zeeb 	struct iwl_roc_req cmd = {
191*6b627f88SBjoern A. Zeeb 		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
192*6b627f88SBjoern A. Zeeb 	};
193*6b627f88SBjoern A. Zeeb 	u8 ver = iwl_fw_lookup_cmd_ver(mld->fw,
194*6b627f88SBjoern A. Zeeb 				       WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0);
195*6b627f88SBjoern A. Zeeb 	u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(cmd);
196*6b627f88SBjoern A. Zeeb 	int ret;
197*6b627f88SBjoern A. Zeeb 
198*6b627f88SBjoern A. Zeeb 	lockdep_assert_wiphy(mld->wiphy);
199*6b627f88SBjoern A. Zeeb 
200*6b627f88SBjoern A. Zeeb 	if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE &&
201*6b627f88SBjoern A. Zeeb 		    vif->type != NL80211_IFTYPE_STATION))
202*6b627f88SBjoern A. Zeeb 		return -EOPNOTSUPP;
203*6b627f88SBjoern A. Zeeb 
204*6b627f88SBjoern A. Zeeb 	/* No roc activity running it's probably already done */
205*6b627f88SBjoern A. Zeeb 	if (mld_vif->roc_activity == ROC_NUM_ACTIVITIES)
206*6b627f88SBjoern A. Zeeb 		return 0;
207*6b627f88SBjoern A. Zeeb 
208*6b627f88SBjoern A. Zeeb 	cmd.activity = cpu_to_le32(mld_vif->roc_activity);
209*6b627f88SBjoern A. Zeeb 
210*6b627f88SBjoern A. Zeeb 	ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, ROC_CMD),
211*6b627f88SBjoern A. Zeeb 				   &cmd, cmd_len);
212*6b627f88SBjoern A. Zeeb 	if (ret)
213*6b627f88SBjoern A. Zeeb 		IWL_ERR(mld, "Couldn't send the command to cancel the ROC\n");
214*6b627f88SBjoern A. Zeeb 
215*6b627f88SBjoern A. Zeeb 	/* We may have raced with the firmware expiring the ROC instance at
216*6b627f88SBjoern A. Zeeb 	 * this very moment. In that case, we can have a notification in the
217*6b627f88SBjoern A. Zeeb 	 * async processing queue. However, none can arrive _after_ this as
218*6b627f88SBjoern A. Zeeb 	 * ROC_CMD was sent synchronously, i.e. we waited for a response and
219*6b627f88SBjoern A. Zeeb 	 * the firmware cannot refer to this ROC after the response. Thus,
220*6b627f88SBjoern A. Zeeb 	 * if we just cancel the notification (if there's one) we'll be at a
221*6b627f88SBjoern A. Zeeb 	 * clean state for any possible next ROC.
222*6b627f88SBjoern A. Zeeb 	 */
223*6b627f88SBjoern A. Zeeb 	iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_ROC,
224*6b627f88SBjoern A. Zeeb 					       mld_vif->roc_activity);
225*6b627f88SBjoern A. Zeeb 
226*6b627f88SBjoern A. Zeeb 	iwl_mld_destroy_roc(mld, vif, mld_vif);
227*6b627f88SBjoern A. Zeeb 
228*6b627f88SBjoern A. Zeeb 	return 0;
229*6b627f88SBjoern A. Zeeb }
230*6b627f88SBjoern A. Zeeb 
iwl_mld_handle_roc_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)231*6b627f88SBjoern A. Zeeb void iwl_mld_handle_roc_notif(struct iwl_mld *mld,
232*6b627f88SBjoern A. Zeeb 			      struct iwl_rx_packet *pkt)
233*6b627f88SBjoern A. Zeeb {
234*6b627f88SBjoern A. Zeeb 	const struct iwl_roc_notif *notif = (void *)pkt->data;
235*6b627f88SBjoern A. Zeeb 	u32 activity = le32_to_cpu(notif->activity);
236*6b627f88SBjoern A. Zeeb 	struct iwl_mld_vif *mld_vif;
237*6b627f88SBjoern A. Zeeb 	struct ieee80211_vif *vif;
238*6b627f88SBjoern A. Zeeb 
239*6b627f88SBjoern A. Zeeb 	vif = iwl_mld_find_roc_vif(mld, activity);
240*6b627f88SBjoern A. Zeeb 	if (WARN_ON(!vif))
241*6b627f88SBjoern A. Zeeb 		return;
242*6b627f88SBjoern A. Zeeb 
243*6b627f88SBjoern A. Zeeb 	mld_vif = iwl_mld_vif_from_mac80211(vif);
244*6b627f88SBjoern A. Zeeb 	/* It is possible that the ROC was canceled
245*6b627f88SBjoern A. Zeeb 	 * but the notification was already fired.
246*6b627f88SBjoern A. Zeeb 	 */
247*6b627f88SBjoern A. Zeeb 	if (mld_vif->roc_activity != activity)
248*6b627f88SBjoern A. Zeeb 		return;
249*6b627f88SBjoern A. Zeeb 
250*6b627f88SBjoern A. Zeeb 	if (le32_to_cpu(notif->success) &&
251*6b627f88SBjoern A. Zeeb 	    le32_to_cpu(notif->started)) {
252*6b627f88SBjoern A. Zeeb 		/* We had a successful start */
253*6b627f88SBjoern A. Zeeb 		ieee80211_ready_on_channel(mld->hw);
254*6b627f88SBjoern A. Zeeb 	} else {
255*6b627f88SBjoern A. Zeeb 		/* ROC was not successful, tell the firmware to remove it */
256*6b627f88SBjoern A. Zeeb 		if (le32_to_cpu(notif->started))
257*6b627f88SBjoern A. Zeeb 			iwl_mld_cancel_roc(mld->hw, vif);
258*6b627f88SBjoern A. Zeeb 		else
259*6b627f88SBjoern A. Zeeb 			iwl_mld_destroy_roc(mld, vif, mld_vif);
260*6b627f88SBjoern A. Zeeb 		/* we need to let know mac80211 about end OR
261*6b627f88SBjoern A. Zeeb 		 * an unsuccessful start
262*6b627f88SBjoern A. Zeeb 		 */
263*6b627f88SBjoern A. Zeeb 		ieee80211_remain_on_channel_expired(mld->hw);
264*6b627f88SBjoern A. Zeeb 	}
265*6b627f88SBjoern A. Zeeb }
266