xref: /freebsd/sys/contrib/dev/iwlwifi/mvm/time-sync.c (revision 9af1bba44e1ce9b0296ae56760b564d67ab7a1cf)
1*9af1bba4SBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2*9af1bba4SBjoern A. Zeeb /*
3*9af1bba4SBjoern A. Zeeb  * Copyright (C) 2022 Intel Corporation
4*9af1bba4SBjoern A. Zeeb  */
5*9af1bba4SBjoern A. Zeeb 
6*9af1bba4SBjoern A. Zeeb #include "mvm.h"
7*9af1bba4SBjoern A. Zeeb #include "time-sync.h"
8*9af1bba4SBjoern A. Zeeb #include <linux/ieee80211.h>
9*9af1bba4SBjoern A. Zeeb 
iwl_mvm_init_time_sync(struct iwl_time_sync_data * data)10*9af1bba4SBjoern A. Zeeb void iwl_mvm_init_time_sync(struct iwl_time_sync_data *data)
11*9af1bba4SBjoern A. Zeeb {
12*9af1bba4SBjoern A. Zeeb 	skb_queue_head_init(&data->frame_list);
13*9af1bba4SBjoern A. Zeeb }
14*9af1bba4SBjoern A. Zeeb 
iwl_mvm_is_skb_match(struct sk_buff * skb,u8 * addr,u8 dialog_token)15*9af1bba4SBjoern A. Zeeb static bool iwl_mvm_is_skb_match(struct sk_buff *skb, u8 *addr, u8 dialog_token)
16*9af1bba4SBjoern A. Zeeb {
17*9af1bba4SBjoern A. Zeeb 	struct ieee80211_mgmt *mgmt = (void *)skb->data;
18*9af1bba4SBjoern A. Zeeb 	u8 skb_dialog_token;
19*9af1bba4SBjoern A. Zeeb 
20*9af1bba4SBjoern A. Zeeb 	if (ieee80211_is_timing_measurement(skb))
21*9af1bba4SBjoern A. Zeeb 		skb_dialog_token = mgmt->u.action.u.wnm_timing_msr.dialog_token;
22*9af1bba4SBjoern A. Zeeb 	else
23*9af1bba4SBjoern A. Zeeb 		skb_dialog_token = mgmt->u.action.u.ftm.dialog_token;
24*9af1bba4SBjoern A. Zeeb 
25*9af1bba4SBjoern A. Zeeb 	if ((ether_addr_equal(mgmt->sa, addr) ||
26*9af1bba4SBjoern A. Zeeb 	     ether_addr_equal(mgmt->da, addr)) &&
27*9af1bba4SBjoern A. Zeeb 	    skb_dialog_token == dialog_token)
28*9af1bba4SBjoern A. Zeeb 		return true;
29*9af1bba4SBjoern A. Zeeb 
30*9af1bba4SBjoern A. Zeeb 	return false;
31*9af1bba4SBjoern A. Zeeb }
32*9af1bba4SBjoern A. Zeeb 
iwl_mvm_time_sync_find_skb(struct iwl_mvm * mvm,u8 * addr,u8 dialog_token)33*9af1bba4SBjoern A. Zeeb static struct sk_buff *iwl_mvm_time_sync_find_skb(struct iwl_mvm *mvm, u8 *addr,
34*9af1bba4SBjoern A. Zeeb 						  u8 dialog_token)
35*9af1bba4SBjoern A. Zeeb {
36*9af1bba4SBjoern A. Zeeb 	struct sk_buff *skb;
37*9af1bba4SBjoern A. Zeeb 
38*9af1bba4SBjoern A. Zeeb 	/* The queue is expected to have only one SKB. If there are other SKBs
39*9af1bba4SBjoern A. Zeeb 	 * in the queue, they did not get a time sync notification and are
40*9af1bba4SBjoern A. Zeeb 	 * probably obsolete by now, so drop them.
41*9af1bba4SBjoern A. Zeeb 	 */
42*9af1bba4SBjoern A. Zeeb 	while ((skb = skb_dequeue(&mvm->time_sync.frame_list))) {
43*9af1bba4SBjoern A. Zeeb 		if (iwl_mvm_is_skb_match(skb, addr, dialog_token))
44*9af1bba4SBjoern A. Zeeb 			break;
45*9af1bba4SBjoern A. Zeeb 
46*9af1bba4SBjoern A. Zeeb 		kfree_skb(skb);
47*9af1bba4SBjoern A. Zeeb 		skb = NULL;
48*9af1bba4SBjoern A. Zeeb 	}
49*9af1bba4SBjoern A. Zeeb 
50*9af1bba4SBjoern A. Zeeb 	return skb;
51*9af1bba4SBjoern A. Zeeb }
52*9af1bba4SBjoern A. Zeeb 
iwl_mvm_get_64_bit(__le32 high,__le32 low)53*9af1bba4SBjoern A. Zeeb static u64 iwl_mvm_get_64_bit(__le32 high, __le32 low)
54*9af1bba4SBjoern A. Zeeb {
55*9af1bba4SBjoern A. Zeeb 	return ((u64)le32_to_cpu(high) << 32) | le32_to_cpu(low);
56*9af1bba4SBjoern A. Zeeb }
57*9af1bba4SBjoern A. Zeeb 
iwl_mvm_time_sync_msmt_event(struct iwl_mvm * mvm,struct iwl_rx_cmd_buffer * rxb)58*9af1bba4SBjoern A. Zeeb void iwl_mvm_time_sync_msmt_event(struct iwl_mvm *mvm,
59*9af1bba4SBjoern A. Zeeb 				  struct iwl_rx_cmd_buffer *rxb)
60*9af1bba4SBjoern A. Zeeb {
61*9af1bba4SBjoern A. Zeeb 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
62*9af1bba4SBjoern A. Zeeb 	struct iwl_time_msmt_notify *notif = (void *)pkt->data;
63*9af1bba4SBjoern A. Zeeb 	struct ieee80211_rx_status *rx_status;
64*9af1bba4SBjoern A. Zeeb 	struct skb_shared_hwtstamps *shwt;
65*9af1bba4SBjoern A. Zeeb 	u64 ts_10ns;
66*9af1bba4SBjoern A. Zeeb 	struct sk_buff *skb =
67*9af1bba4SBjoern A. Zeeb 		iwl_mvm_time_sync_find_skb(mvm, notif->peer_addr,
68*9af1bba4SBjoern A. Zeeb 					   le32_to_cpu(notif->dialog_token));
69*9af1bba4SBjoern A. Zeeb 	u64 adj_time;
70*9af1bba4SBjoern A. Zeeb 
71*9af1bba4SBjoern A. Zeeb 	if (!skb) {
72*9af1bba4SBjoern A. Zeeb 		IWL_DEBUG_INFO(mvm, "Time sync event but no pending skb\n");
73*9af1bba4SBjoern A. Zeeb 		return;
74*9af1bba4SBjoern A. Zeeb 	}
75*9af1bba4SBjoern A. Zeeb 
76*9af1bba4SBjoern A. Zeeb 	ts_10ns = iwl_mvm_get_64_bit(notif->t2_hi, notif->t2_lo);
77*9af1bba4SBjoern A. Zeeb 	adj_time = iwl_mvm_ptp_get_adj_time(mvm, ts_10ns * 10);
78*9af1bba4SBjoern A. Zeeb 	shwt = skb_hwtstamps(skb);
79*9af1bba4SBjoern A. Zeeb 	shwt->hwtstamp = ktime_set(0, adj_time);
80*9af1bba4SBjoern A. Zeeb 
81*9af1bba4SBjoern A. Zeeb 	ts_10ns = iwl_mvm_get_64_bit(notif->t3_hi, notif->t3_lo);
82*9af1bba4SBjoern A. Zeeb 	adj_time = iwl_mvm_ptp_get_adj_time(mvm, ts_10ns * 10);
83*9af1bba4SBjoern A. Zeeb 	rx_status = IEEE80211_SKB_RXCB(skb);
84*9af1bba4SBjoern A. Zeeb 	rx_status->ack_tx_hwtstamp = ktime_set(0, adj_time);
85*9af1bba4SBjoern A. Zeeb 
86*9af1bba4SBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm,
87*9af1bba4SBjoern A. Zeeb 		       "Time sync: RX event - report frame t2=%llu t3=%llu\n",
88*9af1bba4SBjoern A. Zeeb 		       ktime_to_ns(shwt->hwtstamp),
89*9af1bba4SBjoern A. Zeeb 		       ktime_to_ns(rx_status->ack_tx_hwtstamp));
90*9af1bba4SBjoern A. Zeeb 	ieee80211_rx_napi(mvm->hw, NULL, skb, NULL);
91*9af1bba4SBjoern A. Zeeb }
92*9af1bba4SBjoern A. Zeeb 
iwl_mvm_time_sync_msmt_confirm_event(struct iwl_mvm * mvm,struct iwl_rx_cmd_buffer * rxb)93*9af1bba4SBjoern A. Zeeb void iwl_mvm_time_sync_msmt_confirm_event(struct iwl_mvm *mvm,
94*9af1bba4SBjoern A. Zeeb 					  struct iwl_rx_cmd_buffer *rxb)
95*9af1bba4SBjoern A. Zeeb {
96*9af1bba4SBjoern A. Zeeb 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
97*9af1bba4SBjoern A. Zeeb 	struct iwl_time_msmt_cfm_notify *notif = (void *)pkt->data;
98*9af1bba4SBjoern A. Zeeb 	struct ieee80211_tx_status status = {};
99*9af1bba4SBjoern A. Zeeb 	struct skb_shared_hwtstamps *shwt;
100*9af1bba4SBjoern A. Zeeb 	u64 ts_10ns, adj_time;
101*9af1bba4SBjoern A. Zeeb 
102*9af1bba4SBjoern A. Zeeb 	status.skb =
103*9af1bba4SBjoern A. Zeeb 		iwl_mvm_time_sync_find_skb(mvm, notif->peer_addr,
104*9af1bba4SBjoern A. Zeeb 					   le32_to_cpu(notif->dialog_token));
105*9af1bba4SBjoern A. Zeeb 
106*9af1bba4SBjoern A. Zeeb 	if (!status.skb) {
107*9af1bba4SBjoern A. Zeeb 		IWL_DEBUG_INFO(mvm, "Time sync confirm but no pending skb\n");
108*9af1bba4SBjoern A. Zeeb 		return;
109*9af1bba4SBjoern A. Zeeb 	}
110*9af1bba4SBjoern A. Zeeb 
111*9af1bba4SBjoern A. Zeeb 	ts_10ns = iwl_mvm_get_64_bit(notif->t1_hi, notif->t1_lo);
112*9af1bba4SBjoern A. Zeeb 	adj_time = iwl_mvm_ptp_get_adj_time(mvm, ts_10ns * 10);
113*9af1bba4SBjoern A. Zeeb 	shwt = skb_hwtstamps(status.skb);
114*9af1bba4SBjoern A. Zeeb 	shwt->hwtstamp = ktime_set(0, adj_time);
115*9af1bba4SBjoern A. Zeeb 
116*9af1bba4SBjoern A. Zeeb 	ts_10ns = iwl_mvm_get_64_bit(notif->t4_hi, notif->t4_lo);
117*9af1bba4SBjoern A. Zeeb 	adj_time = iwl_mvm_ptp_get_adj_time(mvm, ts_10ns * 10);
118*9af1bba4SBjoern A. Zeeb 	status.info = IEEE80211_SKB_CB(status.skb);
119*9af1bba4SBjoern A. Zeeb 	status.ack_hwtstamp = ktime_set(0, adj_time);
120*9af1bba4SBjoern A. Zeeb 
121*9af1bba4SBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm,
122*9af1bba4SBjoern A. Zeeb 		       "Time sync: TX event - report frame t1=%llu t4=%llu\n",
123*9af1bba4SBjoern A. Zeeb 		       ktime_to_ns(shwt->hwtstamp),
124*9af1bba4SBjoern A. Zeeb 		       ktime_to_ns(status.ack_hwtstamp));
125*9af1bba4SBjoern A. Zeeb 	ieee80211_tx_status_ext(mvm->hw, &status);
126*9af1bba4SBjoern A. Zeeb }
127*9af1bba4SBjoern A. Zeeb 
iwl_mvm_time_sync_config(struct iwl_mvm * mvm,const u8 * addr,u32 protocols)128*9af1bba4SBjoern A. Zeeb int iwl_mvm_time_sync_config(struct iwl_mvm *mvm, const u8 *addr, u32 protocols)
129*9af1bba4SBjoern A. Zeeb {
130*9af1bba4SBjoern A. Zeeb 	struct iwl_time_sync_cfg_cmd cmd = {};
131*9af1bba4SBjoern A. Zeeb 	int err;
132*9af1bba4SBjoern A. Zeeb 
133*9af1bba4SBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
134*9af1bba4SBjoern A. Zeeb 
135*9af1bba4SBjoern A. Zeeb 	if (!fw_has_capa(&mvm->fw->ucode_capa,
136*9af1bba4SBjoern A. Zeeb 			 IWL_UCODE_TLV_CAPA_TIME_SYNC_BOTH_FTM_TM))
137*9af1bba4SBjoern A. Zeeb 		return -EINVAL;
138*9af1bba4SBjoern A. Zeeb 
139*9af1bba4SBjoern A. Zeeb 	/* The fw only supports one peer. We do allow reconfiguration of the
140*9af1bba4SBjoern A. Zeeb 	 * same peer for cases of fw reset etc.
141*9af1bba4SBjoern A. Zeeb 	 */
142*9af1bba4SBjoern A. Zeeb 	if (mvm->time_sync.active &&
143*9af1bba4SBjoern A. Zeeb 	    !ether_addr_equal(addr, mvm->time_sync.peer_addr)) {
144*9af1bba4SBjoern A. Zeeb 		IWL_DEBUG_INFO(mvm, "Time sync: reject config for peer: %pM\n",
145*9af1bba4SBjoern A. Zeeb 			       addr);
146*9af1bba4SBjoern A. Zeeb 		return -ENOBUFS;
147*9af1bba4SBjoern A. Zeeb 	}
148*9af1bba4SBjoern A. Zeeb 
149*9af1bba4SBjoern A. Zeeb 	if (protocols & ~(IWL_TIME_SYNC_PROTOCOL_TM |
150*9af1bba4SBjoern A. Zeeb 			  IWL_TIME_SYNC_PROTOCOL_FTM))
151*9af1bba4SBjoern A. Zeeb 		return -EINVAL;
152*9af1bba4SBjoern A. Zeeb 
153*9af1bba4SBjoern A. Zeeb 	cmd.protocols = cpu_to_le32(protocols);
154*9af1bba4SBjoern A. Zeeb 
155*9af1bba4SBjoern A. Zeeb 	ether_addr_copy(cmd.peer_addr, addr);
156*9af1bba4SBjoern A. Zeeb 
157*9af1bba4SBjoern A. Zeeb 	err = iwl_mvm_send_cmd_pdu(mvm,
158*9af1bba4SBjoern A. Zeeb 				   WIDE_ID(DATA_PATH_GROUP,
159*9af1bba4SBjoern A. Zeeb 					   WNM_80211V_TIMING_MEASUREMENT_CONFIG_CMD),
160*9af1bba4SBjoern A. Zeeb 				   0, sizeof(cmd), &cmd);
161*9af1bba4SBjoern A. Zeeb 	if (err) {
162*9af1bba4SBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to send time sync cfg cmd: %d\n", err);
163*9af1bba4SBjoern A. Zeeb 	} else {
164*9af1bba4SBjoern A. Zeeb 		mvm->time_sync.active = protocols != 0;
165*9af1bba4SBjoern A. Zeeb 		ether_addr_copy(mvm->time_sync.peer_addr, addr);
166*9af1bba4SBjoern A. Zeeb 		IWL_DEBUG_INFO(mvm, "Time sync: set peer addr=%pM\n", addr);
167*9af1bba4SBjoern A. Zeeb 	}
168*9af1bba4SBjoern A. Zeeb 
169*9af1bba4SBjoern A. Zeeb 	if (!mvm->time_sync.active)
170*9af1bba4SBjoern A. Zeeb 		skb_queue_purge(&mvm->time_sync.frame_list);
171*9af1bba4SBjoern A. Zeeb 
172*9af1bba4SBjoern A. Zeeb 	return err;
173*9af1bba4SBjoern A. Zeeb }
174