From c7eca79def44f1faf024d8442044287bef749818 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Mon, 20 Mar 2023 12:33:04 +0200 Subject: wifi: iwlwifi: mvm: report hardware timestamps in RX/TX status For TM/FTM frames, report the hardware timestamps reported by the fw as part of the RX/TX status. Since the fw reports the timestamps in a dedicated notification (and not as part of the RX/TX status), hold the frame until the fw timestamps notification is received. Timestamping is enabled when a station is connected and disabled when disconnected. For AP interface, only the first station will have timestamping enabled since the fw only supports timestamping for one peer. Signed-off-by: Avraham Stern Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230320122330.e0392d498101.I9bf12c8ecfb3f17253a13dc48a48647ddd6e7855@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c | 174 +++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c (limited to 'drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c new file mode 100644 index 000000000000..b42e58e5ff24 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2022 Intel Corporation + */ + +#include "mvm.h" +#include "time-sync.h" +#include + +void iwl_mvm_init_time_sync(struct iwl_time_sync_data *data) +{ + skb_queue_head_init(&data->frame_list); +} + +static bool iwl_mvm_is_skb_match(struct sk_buff *skb, u8 *addr, u8 dialog_token) +{ + struct ieee80211_mgmt *mgmt = (void *)skb->data; + u8 skb_dialog_token; + + if (ieee80211_is_timing_measurement(skb)) + skb_dialog_token = mgmt->u.action.u.wnm_timing_msr.dialog_token; + else + skb_dialog_token = mgmt->u.action.u.ftm.dialog_token; + + if ((ether_addr_equal(mgmt->sa, addr) || + ether_addr_equal(mgmt->da, addr)) && + skb_dialog_token == dialog_token) + return true; + + return false; +} + +static struct sk_buff *iwl_mvm_time_sync_find_skb(struct iwl_mvm *mvm, u8 *addr, + u8 dialog_token) +{ + struct sk_buff *skb; + + /* The queue is expected to have only one SKB. If there are other SKBs + * in the queue, they did not get a time sync notification and are + * probably obsolete by now, so drop them. + */ + while ((skb = skb_dequeue(&mvm->time_sync.frame_list))) { + if (iwl_mvm_is_skb_match(skb, addr, dialog_token)) + break; + + kfree_skb(skb); + skb = NULL; + } + + return skb; +} + +static u64 iwl_mvm_get_64_bit(__le32 high, __le32 low) +{ + return ((u64)le32_to_cpu(high) << 32) | le32_to_cpu(low); +} + +void iwl_mvm_time_sync_msmt_event(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_time_msmt_notify *notif = (void *)pkt->data; + struct ieee80211_rx_status *rx_status; + struct skb_shared_hwtstamps *shwt; + u64 ts_10ns; + struct sk_buff *skb = + iwl_mvm_time_sync_find_skb(mvm, notif->peer_addr, + le32_to_cpu(notif->dialog_token)); + + if (!skb) { + IWL_DEBUG_INFO(mvm, "Time sync event but no pending skb\n"); + return; + } + + ts_10ns = iwl_mvm_get_64_bit(notif->t3_hi, notif->t3_lo); + rx_status = IEEE80211_SKB_RXCB(skb); + rx_status->ack_tx_hwtstamp = ktime_set(0, ts_10ns * 10); + + ts_10ns = iwl_mvm_get_64_bit(notif->t2_hi, notif->t2_lo); + shwt = skb_hwtstamps(skb); + shwt->hwtstamp = ktime_set(0, ts_10ns * 10); + + IWL_DEBUG_INFO(mvm, + "Time sync: RX event - report frame t2=%llu t3=%llu\n", + ktime_to_ns(shwt->hwtstamp), + ktime_to_ns(rx_status->ack_tx_hwtstamp)); + ieee80211_rx_napi(mvm->hw, NULL, skb, NULL); +} + +void iwl_mvm_time_sync_msmt_confirm_event(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_time_msmt_cfm_notify *notif = (void *)pkt->data; + struct ieee80211_tx_status status = {}; + struct skb_shared_hwtstamps *shwt; + u64 ts_10ns; + + status.skb = + iwl_mvm_time_sync_find_skb(mvm, notif->peer_addr, + le32_to_cpu(notif->dialog_token)); + + if (!status.skb) { + IWL_DEBUG_INFO(mvm, "Time sync confirm but no pending skb\n"); + return; + } + + status.info = IEEE80211_SKB_CB(status.skb); + + ts_10ns = iwl_mvm_get_64_bit(notif->t4_hi, notif->t4_lo); + status.ack_hwtstamp = ktime_set(0, ts_10ns * 10); + + ts_10ns = iwl_mvm_get_64_bit(notif->t1_hi, notif->t1_lo); + shwt = skb_hwtstamps(status.skb); + shwt->hwtstamp = ktime_set(0, ts_10ns * 10); + + IWL_DEBUG_INFO(mvm, + "Time sync: TX event - report frame t1=%llu t4=%llu\n", + ktime_to_ns(shwt->hwtstamp), + ktime_to_ns(status.ack_hwtstamp)); + ieee80211_tx_status_ext(mvm->hw, &status); +} + +int iwl_mvm_time_sync_config(struct iwl_mvm *mvm, u8 *addr, u32 protocols) +{ + struct iwl_time_sync_cfg_cmd cmd = {}; + int err; + + lockdep_assert_held(&mvm->mutex); + + if (!fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_TIME_SYNC_BOTH_FTM_TM)) + return -EINVAL; + + /* The fw only supports one peer. We do allow reconfiguration of the + * same peer for cases of fw reset etc. + */ + if (mvm->time_sync.active && + !ether_addr_equal(addr, mvm->time_sync.peer_addr)) { + IWL_DEBUG_INFO(mvm, "Time sync: reject config for peer: %pM\n", + addr); + return -ENOBUFS; + } + + if (protocols & ~(IWL_TIME_SYNC_PROTOCOL_TM | + IWL_TIME_SYNC_PROTOCOL_FTM)) + return -EINVAL; + + cmd.protocols = cpu_to_le32(protocols); + + ether_addr_copy(cmd.peer_addr, addr); + + err = iwl_mvm_send_cmd_pdu(mvm, + WIDE_ID(DATA_PATH_GROUP, + WNM_80211V_TIMING_MEASUREMENT_CONFIG_CMD), + 0, sizeof(cmd), &cmd); + if (err) { + IWL_ERR(mvm, "Failed to send time sync cfg cmd: %d\n", err); + } else { + mvm->time_sync.active = protocols != 0; + ether_addr_copy(mvm->time_sync.peer_addr, addr); + IWL_DEBUG_INFO(mvm, "Time sync: set peer addr=%pM\n", addr); + } + + return err; +} + +void iwl_mvm_time_sync_sta_rm(struct iwl_mvm *mvm, struct ieee80211_sta *sta) +{ + /* Disable time sync with this station */ + iwl_mvm_time_sync_config(mvm, sta->addr, 0); + + skb_queue_purge(&mvm->time_sync.frame_list); +} -- cgit From a2f49f7d52a9e7d0dce8f090541a28192b7abd59 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Mon, 20 Mar 2023 12:33:07 +0200 Subject: wifi: iwlwifi: mvm: implement PHC clock adjustments Implement the following PHC operations: 1. adjtime - for adding an offset to the PHC clock time. 2. adjfine - for adjusting the PHC clock frequency. 3. gettime64 - for getting the PHC clock time. This function returns the time as adjusted by adjtime and adjfine. The adjusted time will also be used for time sync frames timestamping. Signed-off-by: Avraham Stern Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230320122330.f59750deeee4.I110a7e3ac3c6d39d9dbe1fe77001e3f5bc3814eb@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c | 28 ++++++++++++---------- 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c index b42e58e5ff24..434d27a44dea 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c @@ -66,19 +66,22 @@ void iwl_mvm_time_sync_msmt_event(struct iwl_mvm *mvm, struct sk_buff *skb = iwl_mvm_time_sync_find_skb(mvm, notif->peer_addr, le32_to_cpu(notif->dialog_token)); + u64 adj_time; if (!skb) { IWL_DEBUG_INFO(mvm, "Time sync event but no pending skb\n"); return; } - ts_10ns = iwl_mvm_get_64_bit(notif->t3_hi, notif->t3_lo); - rx_status = IEEE80211_SKB_RXCB(skb); - rx_status->ack_tx_hwtstamp = ktime_set(0, ts_10ns * 10); - ts_10ns = iwl_mvm_get_64_bit(notif->t2_hi, notif->t2_lo); + adj_time = iwl_mvm_ptp_get_adj_time(mvm, ts_10ns * 10); shwt = skb_hwtstamps(skb); - shwt->hwtstamp = ktime_set(0, ts_10ns * 10); + shwt->hwtstamp = ktime_set(0, adj_time); + + ts_10ns = iwl_mvm_get_64_bit(notif->t3_hi, notif->t3_lo); + adj_time = iwl_mvm_ptp_get_adj_time(mvm, ts_10ns * 10); + rx_status = IEEE80211_SKB_RXCB(skb); + rx_status->ack_tx_hwtstamp = ktime_set(0, adj_time); IWL_DEBUG_INFO(mvm, "Time sync: RX event - report frame t2=%llu t3=%llu\n", @@ -94,7 +97,7 @@ void iwl_mvm_time_sync_msmt_confirm_event(struct iwl_mvm *mvm, struct iwl_time_msmt_cfm_notify *notif = (void *)pkt->data; struct ieee80211_tx_status status = {}; struct skb_shared_hwtstamps *shwt; - u64 ts_10ns; + u64 ts_10ns, adj_time; status.skb = iwl_mvm_time_sync_find_skb(mvm, notif->peer_addr, @@ -105,14 +108,15 @@ void iwl_mvm_time_sync_msmt_confirm_event(struct iwl_mvm *mvm, return; } - status.info = IEEE80211_SKB_CB(status.skb); - - ts_10ns = iwl_mvm_get_64_bit(notif->t4_hi, notif->t4_lo); - status.ack_hwtstamp = ktime_set(0, ts_10ns * 10); - ts_10ns = iwl_mvm_get_64_bit(notif->t1_hi, notif->t1_lo); + adj_time = iwl_mvm_ptp_get_adj_time(mvm, ts_10ns * 10); shwt = skb_hwtstamps(status.skb); - shwt->hwtstamp = ktime_set(0, ts_10ns * 10); + shwt->hwtstamp = ktime_set(0, adj_time); + + ts_10ns = iwl_mvm_get_64_bit(notif->t4_hi, notif->t4_lo); + adj_time = iwl_mvm_ptp_get_adj_time(mvm, ts_10ns * 10); + status.info = IEEE80211_SKB_CB(status.skb); + status.ack_hwtstamp = ktime_set(0, adj_time); IWL_DEBUG_INFO(mvm, "Time sync: TX event - report frame t1=%llu t4=%llu\n", -- cgit From cf85123a210fe99cdd972a5cc84857fec925f794 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Mon, 20 Mar 2023 12:33:18 +0200 Subject: wifi: iwlwifi: mvm: support enabling and disabling HW timestamping Instead of enabling HW timestamping by default every time a station is connected, disable it by default and enable it only upon request for a specific station. HW timestamping can be enabled for only one peer at a time. Signed-off-by: Avraham Stern Signed-off-by: Gregory Greenman Link: https://lore.kernel.org/r/20230320122330.62b98fbf545b.I450c1017ada7900a71a63d879bb542a08e3166c8@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c index 434d27a44dea..edae3e24192b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-sync.c @@ -125,7 +125,7 @@ void iwl_mvm_time_sync_msmt_confirm_event(struct iwl_mvm *mvm, ieee80211_tx_status_ext(mvm->hw, &status); } -int iwl_mvm_time_sync_config(struct iwl_mvm *mvm, u8 *addr, u32 protocols) +int iwl_mvm_time_sync_config(struct iwl_mvm *mvm, const u8 *addr, u32 protocols) { struct iwl_time_sync_cfg_cmd cmd = {}; int err; @@ -166,13 +166,8 @@ int iwl_mvm_time_sync_config(struct iwl_mvm *mvm, u8 *addr, u32 protocols) IWL_DEBUG_INFO(mvm, "Time sync: set peer addr=%pM\n", addr); } - return err; -} + if (!mvm->time_sync.active) + skb_queue_purge(&mvm->time_sync.frame_list); -void iwl_mvm_time_sync_sta_rm(struct iwl_mvm *mvm, struct ieee80211_sta *sta) -{ - /* Disable time sync with this station */ - iwl_mvm_time_sync_config(mvm, sta->addr, 0); - - skb_queue_purge(&mvm->time_sync.frame_list); + return err; } -- cgit