Path: blob/main/sys/contrib/dev/iwlwifi/mvm/time-sync.c
48287 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2022 Intel Corporation3*/45#include "mvm.h"6#include "time-sync.h"7#include <linux/ieee80211.h>89void iwl_mvm_init_time_sync(struct iwl_time_sync_data *data)10{11skb_queue_head_init(&data->frame_list);12}1314static bool iwl_mvm_is_skb_match(struct sk_buff *skb, u8 *addr, u8 dialog_token)15{16struct ieee80211_mgmt *mgmt = (void *)skb->data;17u8 skb_dialog_token;1819if (ieee80211_is_timing_measurement(skb))20skb_dialog_token = mgmt->u.action.u.wnm_timing_msr.dialog_token;21else22skb_dialog_token = mgmt->u.action.u.ftm.dialog_token;2324if ((ether_addr_equal(mgmt->sa, addr) ||25ether_addr_equal(mgmt->da, addr)) &&26skb_dialog_token == dialog_token)27return true;2829return false;30}3132static struct sk_buff *iwl_mvm_time_sync_find_skb(struct iwl_mvm *mvm, u8 *addr,33u8 dialog_token)34{35struct sk_buff *skb;3637/* The queue is expected to have only one SKB. If there are other SKBs38* in the queue, they did not get a time sync notification and are39* probably obsolete by now, so drop them.40*/41while ((skb = skb_dequeue(&mvm->time_sync.frame_list))) {42if (iwl_mvm_is_skb_match(skb, addr, dialog_token))43break;4445kfree_skb(skb);46skb = NULL;47}4849return skb;50}5152static u64 iwl_mvm_get_64_bit(__le32 high, __le32 low)53{54return ((u64)le32_to_cpu(high) << 32) | le32_to_cpu(low);55}5657void iwl_mvm_time_sync_msmt_event(struct iwl_mvm *mvm,58struct iwl_rx_cmd_buffer *rxb)59{60struct iwl_rx_packet *pkt = rxb_addr(rxb);61struct iwl_time_msmt_notify *notif = (void *)pkt->data;62struct ieee80211_rx_status *rx_status;63struct skb_shared_hwtstamps *shwt;64u64 ts_10ns;65struct sk_buff *skb =66iwl_mvm_time_sync_find_skb(mvm, notif->peer_addr,67le32_to_cpu(notif->dialog_token));68u64 adj_time;6970if (!skb) {71IWL_DEBUG_INFO(mvm, "Time sync event but no pending skb\n");72return;73}7475ts_10ns = iwl_mvm_get_64_bit(notif->t2_hi, notif->t2_lo);76adj_time = iwl_mvm_ptp_get_adj_time(mvm, ts_10ns * 10);77shwt = skb_hwtstamps(skb);78shwt->hwtstamp = ktime_set(0, adj_time);7980ts_10ns = iwl_mvm_get_64_bit(notif->t3_hi, notif->t3_lo);81adj_time = iwl_mvm_ptp_get_adj_time(mvm, ts_10ns * 10);82rx_status = IEEE80211_SKB_RXCB(skb);83rx_status->ack_tx_hwtstamp = ktime_set(0, adj_time);8485IWL_DEBUG_INFO(mvm,86"Time sync: RX event - report frame t2=%llu t3=%llu\n",87ktime_to_ns(shwt->hwtstamp),88ktime_to_ns(rx_status->ack_tx_hwtstamp));89ieee80211_rx_napi(mvm->hw, NULL, skb, NULL);90}9192void iwl_mvm_time_sync_msmt_confirm_event(struct iwl_mvm *mvm,93struct iwl_rx_cmd_buffer *rxb)94{95struct iwl_rx_packet *pkt = rxb_addr(rxb);96struct iwl_time_msmt_cfm_notify *notif = (void *)pkt->data;97struct ieee80211_tx_status status = {};98struct skb_shared_hwtstamps *shwt;99u64 ts_10ns, adj_time;100101status.skb =102iwl_mvm_time_sync_find_skb(mvm, notif->peer_addr,103le32_to_cpu(notif->dialog_token));104105if (!status.skb) {106IWL_DEBUG_INFO(mvm, "Time sync confirm but no pending skb\n");107return;108}109110ts_10ns = iwl_mvm_get_64_bit(notif->t1_hi, notif->t1_lo);111adj_time = iwl_mvm_ptp_get_adj_time(mvm, ts_10ns * 10);112shwt = skb_hwtstamps(status.skb);113shwt->hwtstamp = ktime_set(0, adj_time);114115ts_10ns = iwl_mvm_get_64_bit(notif->t4_hi, notif->t4_lo);116adj_time = iwl_mvm_ptp_get_adj_time(mvm, ts_10ns * 10);117status.info = IEEE80211_SKB_CB(status.skb);118status.ack_hwtstamp = ktime_set(0, adj_time);119120IWL_DEBUG_INFO(mvm,121"Time sync: TX event - report frame t1=%llu t4=%llu\n",122ktime_to_ns(shwt->hwtstamp),123ktime_to_ns(status.ack_hwtstamp));124ieee80211_tx_status_ext(mvm->hw, &status);125}126127int iwl_mvm_time_sync_config(struct iwl_mvm *mvm, const u8 *addr, u32 protocols)128{129struct iwl_time_sync_cfg_cmd cmd = {};130int err;131132lockdep_assert_held(&mvm->mutex);133134if (!fw_has_capa(&mvm->fw->ucode_capa,135IWL_UCODE_TLV_CAPA_TIME_SYNC_BOTH_FTM_TM))136return -EINVAL;137138/* The fw only supports one peer. We do allow reconfiguration of the139* same peer for cases of fw reset etc.140*/141if (mvm->time_sync.active &&142!ether_addr_equal(addr, mvm->time_sync.peer_addr)) {143IWL_DEBUG_INFO(mvm, "Time sync: reject config for peer: %pM\n",144addr);145return -ENOBUFS;146}147148if (protocols & ~(IWL_TIME_SYNC_PROTOCOL_TM |149IWL_TIME_SYNC_PROTOCOL_FTM))150return -EINVAL;151152cmd.protocols = cpu_to_le32(protocols);153154ether_addr_copy(cmd.peer_addr, addr);155156err = iwl_mvm_send_cmd_pdu(mvm,157WIDE_ID(DATA_PATH_GROUP,158WNM_80211V_TIMING_MEASUREMENT_CONFIG_CMD),1590, sizeof(cmd), &cmd);160if (err) {161IWL_ERR(mvm, "Failed to send time sync cfg cmd: %d\n", err);162} else {163mvm->time_sync.active = protocols != 0;164ether_addr_copy(mvm->time_sync.peer_addr, addr);165IWL_DEBUG_INFO(mvm, "Time sync: set peer addr=%pM\n", addr);166}167168if (!mvm->time_sync.active)169skb_queue_purge(&mvm->time_sync.frame_list);170171return err;172}173174175