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