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