File Index
Files and directories
Sign in
FILE

roc.c

File preview with syntax highlighting.

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