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 Intel Corporation
4*6b627f88SBjoern A. Zeeb  */
5*6b627f88SBjoern A. Zeeb #include "session-protect.h"
6*6b627f88SBjoern A. Zeeb #include "fw/api/time-event.h"
7*6b627f88SBjoern A. Zeeb #include "fw/api/context.h"
8*6b627f88SBjoern A. Zeeb #include "iface.h"
9*6b627f88SBjoern A. Zeeb #include <net/mac80211.h>
10*6b627f88SBjoern A. Zeeb 
iwl_mld_handle_session_prot_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)11*6b627f88SBjoern A. Zeeb void iwl_mld_handle_session_prot_notif(struct iwl_mld *mld,
12*6b627f88SBjoern A. Zeeb 				       struct iwl_rx_packet *pkt)
13*6b627f88SBjoern A. Zeeb {
14*6b627f88SBjoern A. Zeeb 	struct iwl_session_prot_notif *notif = (void *)pkt->data;
15*6b627f88SBjoern A. Zeeb 	int fw_link_id = le32_to_cpu(notif->mac_link_id);
16*6b627f88SBjoern A. Zeeb 	struct ieee80211_bss_conf *link_conf =
17*6b627f88SBjoern A. Zeeb 		iwl_mld_fw_id_to_link_conf(mld, fw_link_id);
18*6b627f88SBjoern A. Zeeb 	struct ieee80211_vif *vif;
19*6b627f88SBjoern A. Zeeb 	struct iwl_mld_vif *mld_vif;
20*6b627f88SBjoern A. Zeeb 	struct iwl_mld_session_protect *session_protect;
21*6b627f88SBjoern A. Zeeb 
22*6b627f88SBjoern A. Zeeb 	if (WARN_ON(!link_conf))
23*6b627f88SBjoern A. Zeeb 		return;
24*6b627f88SBjoern A. Zeeb 
25*6b627f88SBjoern A. Zeeb 	vif = link_conf->vif;
26*6b627f88SBjoern A. Zeeb 	mld_vif = iwl_mld_vif_from_mac80211(vif);
27*6b627f88SBjoern A. Zeeb 	session_protect = &mld_vif->session_protect;
28*6b627f88SBjoern A. Zeeb 
29*6b627f88SBjoern A. Zeeb 	if (!le32_to_cpu(notif->status)) {
30*6b627f88SBjoern A. Zeeb 		memset(session_protect, 0, sizeof(*session_protect));
31*6b627f88SBjoern A. Zeeb 	} else if (le32_to_cpu(notif->start)) {
32*6b627f88SBjoern A. Zeeb 		/* End_jiffies indicates an active session */
33*6b627f88SBjoern A. Zeeb 		session_protect->session_requested = false;
34*6b627f88SBjoern A. Zeeb 		session_protect->end_jiffies =
35*6b627f88SBjoern A. Zeeb 			TU_TO_EXP_TIME(session_protect->duration);
36*6b627f88SBjoern A. Zeeb 		/* !session_protect->end_jiffies means inactive session */
37*6b627f88SBjoern A. Zeeb 		if (!session_protect->end_jiffies)
38*6b627f88SBjoern A. Zeeb 			session_protect->end_jiffies = 1;
39*6b627f88SBjoern A. Zeeb 	} else {
40*6b627f88SBjoern A. Zeeb 		memset(session_protect, 0, sizeof(*session_protect));
41*6b627f88SBjoern A. Zeeb 	}
42*6b627f88SBjoern A. Zeeb }
43*6b627f88SBjoern A. Zeeb 
_iwl_mld_schedule_session_protection(struct iwl_mld * mld,struct ieee80211_vif * vif,u32 duration,u32 min_duration,int link_id)44*6b627f88SBjoern A. Zeeb static int _iwl_mld_schedule_session_protection(struct iwl_mld *mld,
45*6b627f88SBjoern A. Zeeb 						struct ieee80211_vif *vif,
46*6b627f88SBjoern A. Zeeb 						u32 duration, u32 min_duration,
47*6b627f88SBjoern A. Zeeb 						int link_id)
48*6b627f88SBjoern A. Zeeb {
49*6b627f88SBjoern A. Zeeb 	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
50*6b627f88SBjoern A. Zeeb 	struct iwl_mld_link *link =
51*6b627f88SBjoern A. Zeeb 		iwl_mld_link_dereference_check(mld_vif, link_id);
52*6b627f88SBjoern A. Zeeb 	struct iwl_mld_session_protect *session_protect =
53*6b627f88SBjoern A. Zeeb 		&mld_vif->session_protect;
54*6b627f88SBjoern A. Zeeb 	struct iwl_session_prot_cmd cmd = {
55*6b627f88SBjoern A. Zeeb 		.id_and_color = cpu_to_le32(link->fw_id),
56*6b627f88SBjoern A. Zeeb 		.action = cpu_to_le32(FW_CTXT_ACTION_ADD),
57*6b627f88SBjoern A. Zeeb 		.conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC),
58*6b627f88SBjoern A. Zeeb 		.duration_tu = cpu_to_le32(MSEC_TO_TU(duration)),
59*6b627f88SBjoern A. Zeeb 	};
60*6b627f88SBjoern A. Zeeb 	int ret;
61*6b627f88SBjoern A. Zeeb 
62*6b627f88SBjoern A. Zeeb 	lockdep_assert_wiphy(mld->wiphy);
63*6b627f88SBjoern A. Zeeb 
64*6b627f88SBjoern A. Zeeb 	WARN(hweight16(vif->active_links) > 1,
65*6b627f88SBjoern A. Zeeb 	     "Session protection isn't allowed with more than one active link");
66*6b627f88SBjoern A. Zeeb 
67*6b627f88SBjoern A. Zeeb 	if (session_protect->end_jiffies &&
68*6b627f88SBjoern A. Zeeb 	    time_after(session_protect->end_jiffies,
69*6b627f88SBjoern A. Zeeb 		       TU_TO_EXP_TIME(min_duration))) {
70*6b627f88SBjoern A. Zeeb 		IWL_DEBUG_TE(mld, "We have ample in the current session: %u\n",
71*6b627f88SBjoern A. Zeeb 			     jiffies_to_msecs(session_protect->end_jiffies -
72*6b627f88SBjoern A. Zeeb 					      jiffies));
73*6b627f88SBjoern A. Zeeb 		return -EALREADY;
74*6b627f88SBjoern A. Zeeb 	}
75*6b627f88SBjoern A. Zeeb 
76*6b627f88SBjoern A. Zeeb 	IWL_DEBUG_TE(mld, "Add a new session protection, duration %d TU\n",
77*6b627f88SBjoern A. Zeeb 		     le32_to_cpu(cmd.duration_tu));
78*6b627f88SBjoern A. Zeeb 
79*6b627f88SBjoern A. Zeeb 	ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP,
80*6b627f88SBjoern A. Zeeb 						SESSION_PROTECTION_CMD), &cmd);
81*6b627f88SBjoern A. Zeeb 
82*6b627f88SBjoern A. Zeeb 	if (ret)
83*6b627f88SBjoern A. Zeeb 		return ret;
84*6b627f88SBjoern A. Zeeb 
85*6b627f88SBjoern A. Zeeb 	/* end_jiffies will be updated when handling session_prot_notif */
86*6b627f88SBjoern A. Zeeb 	session_protect->end_jiffies = 0;
87*6b627f88SBjoern A. Zeeb 	session_protect->duration = duration;
88*6b627f88SBjoern A. Zeeb 	session_protect->session_requested = true;
89*6b627f88SBjoern A. Zeeb 
90*6b627f88SBjoern A. Zeeb 	return 0;
91*6b627f88SBjoern A. Zeeb }
92*6b627f88SBjoern A. Zeeb 
iwl_mld_schedule_session_protection(struct iwl_mld * mld,struct ieee80211_vif * vif,u32 duration,u32 min_duration,int link_id)93*6b627f88SBjoern A. Zeeb void iwl_mld_schedule_session_protection(struct iwl_mld *mld,
94*6b627f88SBjoern A. Zeeb 					 struct ieee80211_vif *vif,
95*6b627f88SBjoern A. Zeeb 					 u32 duration, u32 min_duration,
96*6b627f88SBjoern A. Zeeb 					 int link_id)
97*6b627f88SBjoern A. Zeeb {
98*6b627f88SBjoern A. Zeeb 	int ret;
99*6b627f88SBjoern A. Zeeb 
100*6b627f88SBjoern A. Zeeb 	ret = _iwl_mld_schedule_session_protection(mld, vif, duration,
101*6b627f88SBjoern A. Zeeb 						   min_duration, link_id);
102*6b627f88SBjoern A. Zeeb 	if (ret && ret != -EALREADY)
103*6b627f88SBjoern A. Zeeb 		IWL_ERR(mld,
104*6b627f88SBjoern A. Zeeb 			"Couldn't send the SESSION_PROTECTION_CMD (%d)\n",
105*6b627f88SBjoern A. Zeeb 			ret);
106*6b627f88SBjoern A. Zeeb }
107*6b627f88SBjoern A. Zeeb 
108*6b627f88SBjoern A. Zeeb struct iwl_mld_session_start_data {
109*6b627f88SBjoern A. Zeeb 	struct iwl_mld *mld;
110*6b627f88SBjoern A. Zeeb 	struct ieee80211_bss_conf *link_conf;
111*6b627f88SBjoern A. Zeeb 	bool success;
112*6b627f88SBjoern A. Zeeb };
113*6b627f88SBjoern A. Zeeb 
iwl_mld_session_start_fn(struct iwl_notif_wait_data * notif_wait,struct iwl_rx_packet * pkt,void * _data)114*6b627f88SBjoern A. Zeeb static bool iwl_mld_session_start_fn(struct iwl_notif_wait_data *notif_wait,
115*6b627f88SBjoern A. Zeeb 				     struct iwl_rx_packet *pkt, void *_data)
116*6b627f88SBjoern A. Zeeb {
117*6b627f88SBjoern A. Zeeb 	struct iwl_session_prot_notif *notif = (void *)pkt->data;
118*6b627f88SBjoern A. Zeeb 	unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);
119*6b627f88SBjoern A. Zeeb 	struct iwl_mld_session_start_data *data = _data;
120*6b627f88SBjoern A. Zeeb 	struct ieee80211_bss_conf *link_conf;
121*6b627f88SBjoern A. Zeeb 	struct iwl_mld *mld = data->mld;
122*6b627f88SBjoern A. Zeeb 	int fw_link_id;
123*6b627f88SBjoern A. Zeeb 
124*6b627f88SBjoern A. Zeeb 	if (IWL_FW_CHECK(mld, pkt_len < sizeof(*notif),
125*6b627f88SBjoern A. Zeeb 			 "short session prot notif (%d)\n",
126*6b627f88SBjoern A. Zeeb 			 pkt_len))
127*6b627f88SBjoern A. Zeeb 		return false;
128*6b627f88SBjoern A. Zeeb 
129*6b627f88SBjoern A. Zeeb 	fw_link_id = le32_to_cpu(notif->mac_link_id);
130*6b627f88SBjoern A. Zeeb 	link_conf = iwl_mld_fw_id_to_link_conf(mld, fw_link_id);
131*6b627f88SBjoern A. Zeeb 
132*6b627f88SBjoern A. Zeeb 	if (link_conf != data->link_conf)
133*6b627f88SBjoern A. Zeeb 		return false;
134*6b627f88SBjoern A. Zeeb 
135*6b627f88SBjoern A. Zeeb 	if (!le32_to_cpu(notif->status))
136*6b627f88SBjoern A. Zeeb 		return true;
137*6b627f88SBjoern A. Zeeb 
138*6b627f88SBjoern A. Zeeb 	if (notif->start) {
139*6b627f88SBjoern A. Zeeb 		data->success = true;
140*6b627f88SBjoern A. Zeeb 		return true;
141*6b627f88SBjoern A. Zeeb 	}
142*6b627f88SBjoern A. Zeeb 
143*6b627f88SBjoern A. Zeeb 	return false;
144*6b627f88SBjoern A. Zeeb }
145*6b627f88SBjoern A. Zeeb 
iwl_mld_start_session_protection(struct iwl_mld * mld,struct ieee80211_vif * vif,u32 duration,u32 min_duration,int link_id,unsigned long timeout)146*6b627f88SBjoern A. Zeeb int iwl_mld_start_session_protection(struct iwl_mld *mld,
147*6b627f88SBjoern A. Zeeb 				     struct ieee80211_vif *vif,
148*6b627f88SBjoern A. Zeeb 				     u32 duration, u32 min_duration,
149*6b627f88SBjoern A. Zeeb 				     int link_id, unsigned long timeout)
150*6b627f88SBjoern A. Zeeb {
151*6b627f88SBjoern A. Zeeb 	static const u16 start_notif[] = { SESSION_PROTECTION_NOTIF };
152*6b627f88SBjoern A. Zeeb 	struct iwl_notification_wait start_wait;
153*6b627f88SBjoern A. Zeeb 	struct iwl_mld_session_start_data data = {
154*6b627f88SBjoern A. Zeeb 		.mld = mld,
155*6b627f88SBjoern A. Zeeb 		.link_conf = wiphy_dereference(mld->wiphy,
156*6b627f88SBjoern A. Zeeb 					       vif->link_conf[link_id]),
157*6b627f88SBjoern A. Zeeb 	};
158*6b627f88SBjoern A. Zeeb 	int ret;
159*6b627f88SBjoern A. Zeeb 
160*6b627f88SBjoern A. Zeeb 	if (WARN_ON(!data.link_conf))
161*6b627f88SBjoern A. Zeeb 		return -EINVAL;
162*6b627f88SBjoern A. Zeeb 
163*6b627f88SBjoern A. Zeeb 	iwl_init_notification_wait(&mld->notif_wait, &start_wait,
164*6b627f88SBjoern A. Zeeb 				   start_notif, ARRAY_SIZE(start_notif),
165*6b627f88SBjoern A. Zeeb 				   iwl_mld_session_start_fn, &data);
166*6b627f88SBjoern A. Zeeb 
167*6b627f88SBjoern A. Zeeb 	ret = _iwl_mld_schedule_session_protection(mld, vif, duration,
168*6b627f88SBjoern A. Zeeb 						   min_duration, link_id);
169*6b627f88SBjoern A. Zeeb 
170*6b627f88SBjoern A. Zeeb 	if (ret) {
171*6b627f88SBjoern A. Zeeb 		iwl_remove_notification(&mld->notif_wait, &start_wait);
172*6b627f88SBjoern A. Zeeb 		return ret == -EALREADY ? 0 : ret;
173*6b627f88SBjoern A. Zeeb 	}
174*6b627f88SBjoern A. Zeeb 
175*6b627f88SBjoern A. Zeeb 	ret = iwl_wait_notification(&mld->notif_wait, &start_wait, timeout);
176*6b627f88SBjoern A. Zeeb 	if (ret)
177*6b627f88SBjoern A. Zeeb 		return ret;
178*6b627f88SBjoern A. Zeeb 	return data.success ? 0 : -EIO;
179*6b627f88SBjoern A. Zeeb }
180*6b627f88SBjoern A. Zeeb 
iwl_mld_cancel_session_protection(struct iwl_mld * mld,struct ieee80211_vif * vif,int link_id)181*6b627f88SBjoern A. Zeeb int iwl_mld_cancel_session_protection(struct iwl_mld *mld,
182*6b627f88SBjoern A. Zeeb 				      struct ieee80211_vif *vif,
183*6b627f88SBjoern A. Zeeb 				      int link_id)
184*6b627f88SBjoern A. Zeeb {
185*6b627f88SBjoern A. Zeeb 	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
186*6b627f88SBjoern A. Zeeb 	struct iwl_mld_link *link =
187*6b627f88SBjoern A. Zeeb 		iwl_mld_link_dereference_check(mld_vif, link_id);
188*6b627f88SBjoern A. Zeeb 	struct iwl_mld_session_protect *session_protect =
189*6b627f88SBjoern A. Zeeb 		&mld_vif->session_protect;
190*6b627f88SBjoern A. Zeeb 	struct iwl_session_prot_cmd cmd = {
191*6b627f88SBjoern A. Zeeb 		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
192*6b627f88SBjoern A. Zeeb 		.conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC),
193*6b627f88SBjoern A. Zeeb 	};
194*6b627f88SBjoern A. Zeeb 	int ret;
195*6b627f88SBjoern A. Zeeb 
196*6b627f88SBjoern A. Zeeb 	lockdep_assert_wiphy(mld->wiphy);
197*6b627f88SBjoern A. Zeeb 
198*6b627f88SBjoern A. Zeeb 	/* If there isn't an active session or a requested one for this
199*6b627f88SBjoern A. Zeeb 	 * link do nothing
200*6b627f88SBjoern A. Zeeb 	 */
201*6b627f88SBjoern A. Zeeb 	if (!session_protect->session_requested &&
202*6b627f88SBjoern A. Zeeb 	    !session_protect->end_jiffies)
203*6b627f88SBjoern A. Zeeb 		return 0;
204*6b627f88SBjoern A. Zeeb 
205*6b627f88SBjoern A. Zeeb 	if (WARN_ON(!link))
206*6b627f88SBjoern A. Zeeb 		return -EINVAL;
207*6b627f88SBjoern A. Zeeb 
208*6b627f88SBjoern A. Zeeb 	cmd.id_and_color = cpu_to_le32(link->fw_id);
209*6b627f88SBjoern A. Zeeb 
210*6b627f88SBjoern A. Zeeb 	ret = iwl_mld_send_cmd_pdu(mld,
211*6b627f88SBjoern A. Zeeb 				   WIDE_ID(MAC_CONF_GROUP,
212*6b627f88SBjoern A. Zeeb 					   SESSION_PROTECTION_CMD), &cmd);
213*6b627f88SBjoern A. Zeeb 	if (ret) {
214*6b627f88SBjoern A. Zeeb 		IWL_ERR(mld,
215*6b627f88SBjoern A. Zeeb 			"Couldn't send the SESSION_PROTECTION_CMD\n");
216*6b627f88SBjoern A. Zeeb 		return ret;
217*6b627f88SBjoern A. Zeeb 	}
218*6b627f88SBjoern A. Zeeb 
219*6b627f88SBjoern A. Zeeb 	memset(session_protect, 0, sizeof(*session_protect));
220*6b627f88SBjoern A. Zeeb 
221*6b627f88SBjoern A. Zeeb 	return 0;
222*6b627f88SBjoern A. Zeeb }
223