xref: /freebsd/sys/contrib/dev/iwlwifi/mld/time_sync.c (revision 6b627f88584ce13118e0a24951b503c0b1f2d5a7)
1*6b627f88SBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2*6b627f88SBjoern A. Zeeb /*
3*6b627f88SBjoern A. Zeeb  * Copyright (C) 2025 Intel Corporation
4*6b627f88SBjoern A. Zeeb  */
5*6b627f88SBjoern A. Zeeb 
6*6b627f88SBjoern A. Zeeb #include "mld.h"
7*6b627f88SBjoern A. Zeeb #include "hcmd.h"
8*6b627f88SBjoern A. Zeeb #include "ptp.h"
9*6b627f88SBjoern A. Zeeb #include "time_sync.h"
10*6b627f88SBjoern A. Zeeb #include <linux/ieee80211.h>
11*6b627f88SBjoern A. Zeeb 
iwl_mld_init_time_sync(struct iwl_mld * mld,u32 protocols,const u8 * addr)12*6b627f88SBjoern A. Zeeb static int iwl_mld_init_time_sync(struct iwl_mld *mld, u32 protocols,
13*6b627f88SBjoern A. Zeeb 				  const u8 *addr)
14*6b627f88SBjoern A. Zeeb {
15*6b627f88SBjoern A. Zeeb 	struct iwl_mld_time_sync_data *time_sync = kzalloc(sizeof(*time_sync),
16*6b627f88SBjoern A. Zeeb 							   GFP_KERNEL);
17*6b627f88SBjoern A. Zeeb 
18*6b627f88SBjoern A. Zeeb 	if (!time_sync)
19*6b627f88SBjoern A. Zeeb 		return -ENOMEM;
20*6b627f88SBjoern A. Zeeb 
21*6b627f88SBjoern A. Zeeb 	time_sync->active_protocols = protocols;
22*6b627f88SBjoern A. Zeeb 	ether_addr_copy(time_sync->peer_addr, addr);
23*6b627f88SBjoern A. Zeeb 	skb_queue_head_init(&time_sync->frame_list);
24*6b627f88SBjoern A. Zeeb 	rcu_assign_pointer(mld->time_sync, time_sync);
25*6b627f88SBjoern A. Zeeb 
26*6b627f88SBjoern A. Zeeb 	return 0;
27*6b627f88SBjoern A. Zeeb }
28*6b627f88SBjoern A. Zeeb 
iwl_mld_time_sync_fw_config(struct iwl_mld * mld)29*6b627f88SBjoern A. Zeeb int iwl_mld_time_sync_fw_config(struct iwl_mld *mld)
30*6b627f88SBjoern A. Zeeb {
31*6b627f88SBjoern A. Zeeb 	struct iwl_time_sync_cfg_cmd cmd = {};
32*6b627f88SBjoern A. Zeeb 	struct iwl_mld_time_sync_data *time_sync;
33*6b627f88SBjoern A. Zeeb 	int err;
34*6b627f88SBjoern A. Zeeb 
35*6b627f88SBjoern A. Zeeb 	time_sync = wiphy_dereference(mld->wiphy, mld->time_sync);
36*6b627f88SBjoern A. Zeeb 	if (!time_sync)
37*6b627f88SBjoern A. Zeeb 		return -EINVAL;
38*6b627f88SBjoern A. Zeeb 
39*6b627f88SBjoern A. Zeeb 	cmd.protocols = cpu_to_le32(time_sync->active_protocols);
40*6b627f88SBjoern A. Zeeb 	ether_addr_copy(cmd.peer_addr, time_sync->peer_addr);
41*6b627f88SBjoern A. Zeeb 
42*6b627f88SBjoern A. Zeeb 	err = iwl_mld_send_cmd_pdu(mld,
43*6b627f88SBjoern A. Zeeb 				   WIDE_ID(DATA_PATH_GROUP,
44*6b627f88SBjoern A. Zeeb 					   WNM_80211V_TIMING_MEASUREMENT_CONFIG_CMD),
45*6b627f88SBjoern A. Zeeb 				   &cmd);
46*6b627f88SBjoern A. Zeeb 	if (err)
47*6b627f88SBjoern A. Zeeb 		IWL_ERR(mld, "Failed to send time sync cfg cmd: %d\n", err);
48*6b627f88SBjoern A. Zeeb 
49*6b627f88SBjoern A. Zeeb 	return err;
50*6b627f88SBjoern A. Zeeb }
51*6b627f88SBjoern A. Zeeb 
iwl_mld_time_sync_config(struct iwl_mld * mld,const u8 * addr,u32 protocols)52*6b627f88SBjoern A. Zeeb int iwl_mld_time_sync_config(struct iwl_mld *mld, const u8 *addr, u32 protocols)
53*6b627f88SBjoern A. Zeeb {
54*6b627f88SBjoern A. Zeeb 	struct iwl_mld_time_sync_data *time_sync;
55*6b627f88SBjoern A. Zeeb 	int err;
56*6b627f88SBjoern A. Zeeb 
57*6b627f88SBjoern A. Zeeb 	time_sync = wiphy_dereference(mld->wiphy, mld->time_sync);
58*6b627f88SBjoern A. Zeeb 
59*6b627f88SBjoern A. Zeeb 	/* The fw only supports one peer. We do allow reconfiguration of the
60*6b627f88SBjoern A. Zeeb 	 * same peer for cases of fw reset etc.
61*6b627f88SBjoern A. Zeeb 	 */
62*6b627f88SBjoern A. Zeeb 	if (time_sync && time_sync->active_protocols &&
63*6b627f88SBjoern A. Zeeb 	    !ether_addr_equal(addr, time_sync->peer_addr)) {
64*6b627f88SBjoern A. Zeeb 		IWL_DEBUG_INFO(mld, "Time sync: reject config for peer: %pM\n",
65*6b627f88SBjoern A. Zeeb 			       addr);
66*6b627f88SBjoern A. Zeeb 		return -ENOBUFS;
67*6b627f88SBjoern A. Zeeb 	}
68*6b627f88SBjoern A. Zeeb 
69*6b627f88SBjoern A. Zeeb 	if (protocols & ~(IWL_TIME_SYNC_PROTOCOL_TM |
70*6b627f88SBjoern A. Zeeb 			  IWL_TIME_SYNC_PROTOCOL_FTM))
71*6b627f88SBjoern A. Zeeb 		return -EINVAL;
72*6b627f88SBjoern A. Zeeb 
73*6b627f88SBjoern A. Zeeb 	IWL_DEBUG_INFO(mld, "Time sync: set peer addr=%pM\n", addr);
74*6b627f88SBjoern A. Zeeb 
75*6b627f88SBjoern A. Zeeb 	iwl_mld_deinit_time_sync(mld);
76*6b627f88SBjoern A. Zeeb 	err = iwl_mld_init_time_sync(mld, protocols, addr);
77*6b627f88SBjoern A. Zeeb 	if (err)
78*6b627f88SBjoern A. Zeeb 		return err;
79*6b627f88SBjoern A. Zeeb 
80*6b627f88SBjoern A. Zeeb 	err = iwl_mld_time_sync_fw_config(mld);
81*6b627f88SBjoern A. Zeeb 	return err;
82*6b627f88SBjoern A. Zeeb }
83*6b627f88SBjoern A. Zeeb 
iwl_mld_deinit_time_sync(struct iwl_mld * mld)84*6b627f88SBjoern A. Zeeb void iwl_mld_deinit_time_sync(struct iwl_mld *mld)
85*6b627f88SBjoern A. Zeeb {
86*6b627f88SBjoern A. Zeeb 	struct iwl_mld_time_sync_data *time_sync =
87*6b627f88SBjoern A. Zeeb 		wiphy_dereference(mld->wiphy, mld->time_sync);
88*6b627f88SBjoern A. Zeeb 
89*6b627f88SBjoern A. Zeeb 	if (!time_sync)
90*6b627f88SBjoern A. Zeeb 		return;
91*6b627f88SBjoern A. Zeeb 
92*6b627f88SBjoern A. Zeeb 	RCU_INIT_POINTER(mld->time_sync, NULL);
93*6b627f88SBjoern A. Zeeb 	skb_queue_purge(&time_sync->frame_list);
94*6b627f88SBjoern A. Zeeb 	kfree_rcu(time_sync, rcu_head);
95*6b627f88SBjoern A. Zeeb }
96*6b627f88SBjoern A. Zeeb 
iwl_mld_time_sync_frame(struct iwl_mld * mld,struct sk_buff * skb,u8 * addr)97*6b627f88SBjoern A. Zeeb bool iwl_mld_time_sync_frame(struct iwl_mld *mld, struct sk_buff *skb, u8 *addr)
98*6b627f88SBjoern A. Zeeb {
99*6b627f88SBjoern A. Zeeb 	struct iwl_mld_time_sync_data *time_sync;
100*6b627f88SBjoern A. Zeeb 
101*6b627f88SBjoern A. Zeeb 	rcu_read_lock();
102*6b627f88SBjoern A. Zeeb 	time_sync = rcu_dereference(mld->time_sync);
103*6b627f88SBjoern A. Zeeb 	if (time_sync && ether_addr_equal(time_sync->peer_addr, addr) &&
104*6b627f88SBjoern A. Zeeb 	    (ieee80211_is_timing_measurement(skb) || ieee80211_is_ftm(skb))) {
105*6b627f88SBjoern A. Zeeb 		skb_queue_tail(&time_sync->frame_list, skb);
106*6b627f88SBjoern A. Zeeb 		rcu_read_unlock();
107*6b627f88SBjoern A. Zeeb 		return true;
108*6b627f88SBjoern A. Zeeb 	}
109*6b627f88SBjoern A. Zeeb 	rcu_read_unlock();
110*6b627f88SBjoern A. Zeeb 
111*6b627f88SBjoern A. Zeeb 	return false;
112*6b627f88SBjoern A. Zeeb }
113*6b627f88SBjoern A. Zeeb 
iwl_mld_is_skb_match(struct sk_buff * skb,u8 * addr,u8 dialog_token)114*6b627f88SBjoern A. Zeeb static bool iwl_mld_is_skb_match(struct sk_buff *skb, u8 *addr, u8 dialog_token)
115*6b627f88SBjoern A. Zeeb {
116*6b627f88SBjoern A. Zeeb 	struct ieee80211_mgmt *mgmt = (void *)skb->data;
117*6b627f88SBjoern A. Zeeb 	u8 skb_dialog_token;
118*6b627f88SBjoern A. Zeeb 
119*6b627f88SBjoern A. Zeeb 	if (ieee80211_is_timing_measurement(skb))
120*6b627f88SBjoern A. Zeeb 		skb_dialog_token = mgmt->u.action.u.wnm_timing_msr.dialog_token;
121*6b627f88SBjoern A. Zeeb 	else
122*6b627f88SBjoern A. Zeeb 		skb_dialog_token = mgmt->u.action.u.ftm.dialog_token;
123*6b627f88SBjoern A. Zeeb 
124*6b627f88SBjoern A. Zeeb 	if ((ether_addr_equal(mgmt->sa, addr) ||
125*6b627f88SBjoern A. Zeeb 	     ether_addr_equal(mgmt->da, addr)) &&
126*6b627f88SBjoern A. Zeeb 	    skb_dialog_token == dialog_token)
127*6b627f88SBjoern A. Zeeb 		return true;
128*6b627f88SBjoern A. Zeeb 
129*6b627f88SBjoern A. Zeeb 	return false;
130*6b627f88SBjoern A. Zeeb }
131*6b627f88SBjoern A. Zeeb 
iwl_mld_time_sync_find_skb(struct iwl_mld * mld,u8 * addr,u8 dialog_token)132*6b627f88SBjoern A. Zeeb static struct sk_buff *iwl_mld_time_sync_find_skb(struct iwl_mld *mld, u8 *addr,
133*6b627f88SBjoern A. Zeeb 						  u8 dialog_token)
134*6b627f88SBjoern A. Zeeb {
135*6b627f88SBjoern A. Zeeb 	struct iwl_mld_time_sync_data *time_sync;
136*6b627f88SBjoern A. Zeeb 	struct sk_buff *skb;
137*6b627f88SBjoern A. Zeeb 
138*6b627f88SBjoern A. Zeeb 	rcu_read_lock();
139*6b627f88SBjoern A. Zeeb 
140*6b627f88SBjoern A. Zeeb 	time_sync = rcu_dereference(mld->time_sync);
141*6b627f88SBjoern A. Zeeb 	if (IWL_FW_CHECK(mld, !time_sync,
142*6b627f88SBjoern A. Zeeb 			 "Time sync notification but time sync is not initialized\n")) {
143*6b627f88SBjoern A. Zeeb 		rcu_read_unlock();
144*6b627f88SBjoern A. Zeeb 		return NULL;
145*6b627f88SBjoern A. Zeeb 	}
146*6b627f88SBjoern A. Zeeb 
147*6b627f88SBjoern A. Zeeb 	/* The notifications are expected to arrive in the same order of the
148*6b627f88SBjoern A. Zeeb 	 * frames. If the incoming notification doesn't match the first SKB
149*6b627f88SBjoern A. Zeeb 	 * in the queue, it means there was no time sync notification for this
150*6b627f88SBjoern A. Zeeb 	 * SKB and it can be dropped.
151*6b627f88SBjoern A. Zeeb 	 */
152*6b627f88SBjoern A. Zeeb 	while ((skb = skb_dequeue(&time_sync->frame_list))) {
153*6b627f88SBjoern A. Zeeb 		if (iwl_mld_is_skb_match(skb, addr, dialog_token))
154*6b627f88SBjoern A. Zeeb 			break;
155*6b627f88SBjoern A. Zeeb 
156*6b627f88SBjoern A. Zeeb 		kfree_skb(skb);
157*6b627f88SBjoern A. Zeeb 		skb = NULL;
158*6b627f88SBjoern A. Zeeb 		IWL_DEBUG_DROP(mld,
159*6b627f88SBjoern A. Zeeb 			       "Time sync: drop SKB without matching notification\n");
160*6b627f88SBjoern A. Zeeb 	}
161*6b627f88SBjoern A. Zeeb 	rcu_read_unlock();
162*6b627f88SBjoern A. Zeeb 
163*6b627f88SBjoern A. Zeeb 	return skb;
164*6b627f88SBjoern A. Zeeb }
165*6b627f88SBjoern A. Zeeb 
iwl_mld_get_64_bit(__le32 high,__le32 low)166*6b627f88SBjoern A. Zeeb static u64 iwl_mld_get_64_bit(__le32 high, __le32 low)
167*6b627f88SBjoern A. Zeeb {
168*6b627f88SBjoern A. Zeeb 	return ((u64)le32_to_cpu(high) << 32) | le32_to_cpu(low);
169*6b627f88SBjoern A. Zeeb }
170*6b627f88SBjoern A. Zeeb 
iwl_mld_handle_time_msmt_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)171*6b627f88SBjoern A. Zeeb void iwl_mld_handle_time_msmt_notif(struct iwl_mld *mld,
172*6b627f88SBjoern A. Zeeb 				    struct iwl_rx_packet *pkt)
173*6b627f88SBjoern A. Zeeb {
174*6b627f88SBjoern A. Zeeb 	struct ptp_data *data = &mld->ptp_data;
175*6b627f88SBjoern A. Zeeb 	struct iwl_time_msmt_notify *notif = (void *)pkt->data;
176*6b627f88SBjoern A. Zeeb 	struct ieee80211_rx_status *rx_status;
177*6b627f88SBjoern A. Zeeb 	struct skb_shared_hwtstamps *shwt;
178*6b627f88SBjoern A. Zeeb 	u64 ts_10ns;
179*6b627f88SBjoern A. Zeeb 	struct sk_buff *skb =
180*6b627f88SBjoern A. Zeeb 		iwl_mld_time_sync_find_skb(mld, notif->peer_addr,
181*6b627f88SBjoern A. Zeeb 					   le32_to_cpu(notif->dialog_token));
182*6b627f88SBjoern A. Zeeb 	u64 adj_time;
183*6b627f88SBjoern A. Zeeb 
184*6b627f88SBjoern A. Zeeb 	if (IWL_FW_CHECK(mld, !skb, "Time sync event but no pending skb\n"))
185*6b627f88SBjoern A. Zeeb 		return;
186*6b627f88SBjoern A. Zeeb 
187*6b627f88SBjoern A. Zeeb 	spin_lock_bh(&data->lock);
188*6b627f88SBjoern A. Zeeb 	ts_10ns = iwl_mld_get_64_bit(notif->t2_hi, notif->t2_lo);
189*6b627f88SBjoern A. Zeeb 	adj_time = iwl_mld_ptp_get_adj_time(mld, ts_10ns * 10);
190*6b627f88SBjoern A. Zeeb 	shwt = skb_hwtstamps(skb);
191*6b627f88SBjoern A. Zeeb 	shwt->hwtstamp = ktime_set(0, adj_time);
192*6b627f88SBjoern A. Zeeb 
193*6b627f88SBjoern A. Zeeb 	ts_10ns = iwl_mld_get_64_bit(notif->t3_hi, notif->t3_lo);
194*6b627f88SBjoern A. Zeeb 	adj_time = iwl_mld_ptp_get_adj_time(mld, ts_10ns * 10);
195*6b627f88SBjoern A. Zeeb 	rx_status = IEEE80211_SKB_RXCB(skb);
196*6b627f88SBjoern A. Zeeb 	rx_status->ack_tx_hwtstamp = ktime_set(0, adj_time);
197*6b627f88SBjoern A. Zeeb 	spin_unlock_bh(&data->lock);
198*6b627f88SBjoern A. Zeeb 
199*6b627f88SBjoern A. Zeeb 	IWL_DEBUG_INFO(mld,
200*6b627f88SBjoern A. Zeeb 		       "Time sync: RX event - report frame t2=%llu t3=%llu\n",
201*6b627f88SBjoern A. Zeeb 		       ktime_to_ns(shwt->hwtstamp),
202*6b627f88SBjoern A. Zeeb 		       ktime_to_ns(rx_status->ack_tx_hwtstamp));
203*6b627f88SBjoern A. Zeeb 	ieee80211_rx_napi(mld->hw, NULL, skb, NULL);
204*6b627f88SBjoern A. Zeeb }
205*6b627f88SBjoern A. Zeeb 
iwl_mld_handle_time_sync_confirm_notif(struct iwl_mld * mld,struct iwl_rx_packet * pkt)206*6b627f88SBjoern A. Zeeb void iwl_mld_handle_time_sync_confirm_notif(struct iwl_mld *mld,
207*6b627f88SBjoern A. Zeeb 					    struct iwl_rx_packet *pkt)
208*6b627f88SBjoern A. Zeeb {
209*6b627f88SBjoern A. Zeeb 	struct ptp_data *data = &mld->ptp_data;
210*6b627f88SBjoern A. Zeeb 	struct iwl_time_msmt_cfm_notify *notif = (void *)pkt->data;
211*6b627f88SBjoern A. Zeeb 	struct ieee80211_tx_status status = {};
212*6b627f88SBjoern A. Zeeb 	struct skb_shared_hwtstamps *shwt;
213*6b627f88SBjoern A. Zeeb 	u64 ts_10ns, adj_time;
214*6b627f88SBjoern A. Zeeb 
215*6b627f88SBjoern A. Zeeb 	status.skb =
216*6b627f88SBjoern A. Zeeb 		iwl_mld_time_sync_find_skb(mld, notif->peer_addr,
217*6b627f88SBjoern A. Zeeb 					   le32_to_cpu(notif->dialog_token));
218*6b627f88SBjoern A. Zeeb 
219*6b627f88SBjoern A. Zeeb 	if (IWL_FW_CHECK(mld, !status.skb,
220*6b627f88SBjoern A. Zeeb 			 "Time sync confirm but no pending skb\n"))
221*6b627f88SBjoern A. Zeeb 		return;
222*6b627f88SBjoern A. Zeeb 
223*6b627f88SBjoern A. Zeeb 	spin_lock_bh(&data->lock);
224*6b627f88SBjoern A. Zeeb 	ts_10ns = iwl_mld_get_64_bit(notif->t1_hi, notif->t1_lo);
225*6b627f88SBjoern A. Zeeb 	adj_time = iwl_mld_ptp_get_adj_time(mld, ts_10ns * 10);
226*6b627f88SBjoern A. Zeeb 	shwt = skb_hwtstamps(status.skb);
227*6b627f88SBjoern A. Zeeb 	shwt->hwtstamp = ktime_set(0, adj_time);
228*6b627f88SBjoern A. Zeeb 
229*6b627f88SBjoern A. Zeeb 	ts_10ns = iwl_mld_get_64_bit(notif->t4_hi, notif->t4_lo);
230*6b627f88SBjoern A. Zeeb 	adj_time = iwl_mld_ptp_get_adj_time(mld, ts_10ns * 10);
231*6b627f88SBjoern A. Zeeb 	status.info = IEEE80211_SKB_CB(status.skb);
232*6b627f88SBjoern A. Zeeb 	status.ack_hwtstamp = ktime_set(0, adj_time);
233*6b627f88SBjoern A. Zeeb 	spin_unlock_bh(&data->lock);
234*6b627f88SBjoern A. Zeeb 
235*6b627f88SBjoern A. Zeeb 	IWL_DEBUG_INFO(mld,
236*6b627f88SBjoern A. Zeeb 		       "Time sync: TX event - report frame t1=%llu t4=%llu\n",
237*6b627f88SBjoern A. Zeeb 		       ktime_to_ns(shwt->hwtstamp),
238*6b627f88SBjoern A. Zeeb 		       ktime_to_ns(status.ack_hwtstamp));
239*6b627f88SBjoern A. Zeeb 	ieee80211_tx_status_ext(mld->hw, &status);
240*6b627f88SBjoern A. Zeeb }
241