wifi: mac80211: add hardware timestamps for RX and TX
When the low level driver reports hardware timestamps for frame TX status or frame RX, pass the timestamps to cfg80211. Signed-off-by: Avraham Stern <avraham.stern@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
1ff715ffa0
commit
f9202638df
@ -125,6 +125,22 @@
|
||||
* via the usual ieee80211_tx_dequeue).
|
||||
*/
|
||||
|
||||
/**
|
||||
* DOC: HW timestamping
|
||||
*
|
||||
* Timing Measurement and Fine Timing Measurement require accurate timestamps
|
||||
* of the action frames TX/RX and their respective acks.
|
||||
*
|
||||
* To report hardware timestamps for Timing Measurement or Fine Timing
|
||||
* Measurement frame RX, the low level driver should set the SKB's hwtstamp
|
||||
* field to the frame RX timestamp and report the ack TX timestamp in the
|
||||
* ieee80211_rx_status struct.
|
||||
*
|
||||
* Similarly, To report hardware timestamps for Timing Measurement or Fine
|
||||
* Timing Measurement frame TX, the driver should set the SKB's hwtstamp field
|
||||
* to the frame TX timestamp and report the ack RX timestamp in the
|
||||
* ieee80211_tx_status struct.
|
||||
*/
|
||||
struct device;
|
||||
|
||||
/**
|
||||
@ -1176,12 +1192,16 @@ struct ieee80211_rate_status {
|
||||
* @rates: Mrr stages that were used when sending the packet
|
||||
* @n_rates: Number of mrr stages (count of instances for @rates)
|
||||
* @free_list: list where processed skbs are stored to be free'd by the driver
|
||||
* @ack_hwtstamp: Hardware timestamp of the received ack in nanoseconds
|
||||
* Only needed for Timing measurement and Fine timing measurement action
|
||||
* frames. Only reported by devices that have timestamping enabled.
|
||||
*/
|
||||
struct ieee80211_tx_status {
|
||||
struct ieee80211_sta *sta;
|
||||
struct ieee80211_tx_info *info;
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_rate_status *rates;
|
||||
ktime_t ack_hwtstamp;
|
||||
u8 n_rates;
|
||||
|
||||
struct list_head *free_list;
|
||||
@ -1419,6 +1439,9 @@ enum mac80211_rx_encoding {
|
||||
* (TSF) timer when the first data symbol (MPDU) arrived at the hardware.
|
||||
* @boottime_ns: CLOCK_BOOTTIME timestamp the frame was received at, this is
|
||||
* needed only for beacons and probe responses that update the scan cache.
|
||||
* @ack_tx_hwtstamp: Hardware timestamp for the ack TX in nanoseconds. Only
|
||||
* needed for Timing measurement and Fine timing measurement action frames.
|
||||
* Only reported by devices that have timestamping enabled.
|
||||
* @device_timestamp: arbitrary timestamp for the device, mac80211 doesn't use
|
||||
* it but can store it and pass it back to the driver for synchronisation
|
||||
* @band: the active band when this frame was received
|
||||
@ -1452,7 +1475,10 @@ enum mac80211_rx_encoding {
|
||||
*/
|
||||
struct ieee80211_rx_status {
|
||||
u64 mactime;
|
||||
u64 boottime_ns;
|
||||
union {
|
||||
u64 boottime_ns;
|
||||
ktime_t ack_tx_hwtstamp;
|
||||
};
|
||||
u32 device_timestamp;
|
||||
u32 ampdu_reference;
|
||||
u32 flag;
|
||||
|
@ -3644,7 +3644,11 @@ static ieee80211_rx_result debug_noinline
|
||||
ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
|
||||
{
|
||||
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
|
||||
int sig = 0;
|
||||
struct cfg80211_rx_info info = {
|
||||
.freq = ieee80211_rx_status_to_khz(status),
|
||||
.buf = rx->skb->data,
|
||||
.len = rx->skb->len
|
||||
};
|
||||
|
||||
/* skip known-bad action frames and return them in the next handler */
|
||||
if (status->rx_flags & IEEE80211_RX_MALFORMED_ACTION_FRM)
|
||||
@ -3659,11 +3663,15 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
|
||||
|
||||
if (ieee80211_hw_check(&rx->local->hw, SIGNAL_DBM) &&
|
||||
!(status->flag & RX_FLAG_NO_SIGNAL_VAL))
|
||||
sig = status->signal;
|
||||
info.sig_dbm = status->signal;
|
||||
|
||||
if (cfg80211_rx_mgmt_khz(&rx->sdata->wdev,
|
||||
ieee80211_rx_status_to_khz(status), sig,
|
||||
rx->skb->data, rx->skb->len, 0)) {
|
||||
if (ieee80211_is_timing_measurement(rx->skb) ||
|
||||
ieee80211_is_ftm(rx->skb)) {
|
||||
info.rx_tstamp = ktime_to_ns(skb_hwtstamps(rx->skb)->hwtstamp);
|
||||
info.ack_tstamp = ktime_to_ns(status->ack_tx_hwtstamp);
|
||||
}
|
||||
|
||||
if (cfg80211_rx_mgmt_ext(&rx->sdata->wdev, &info)) {
|
||||
if (rx->sta)
|
||||
rx->sta->deflink.rx_stats.packets++;
|
||||
dev_kfree_skb(rx->skb);
|
||||
@ -4758,8 +4766,10 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
|
||||
}
|
||||
|
||||
if (!consume) {
|
||||
skb = skb_copy(skb, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
struct skb_shared_hwtstamps *shwt;
|
||||
|
||||
rx->skb = skb_copy(skb, GFP_ATOMIC);
|
||||
if (!rx->skb) {
|
||||
if (net_ratelimit())
|
||||
wiphy_debug(local->hw.wiphy,
|
||||
"failed to copy skb for %s\n",
|
||||
@ -4767,7 +4777,11 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
|
||||
return true;
|
||||
}
|
||||
|
||||
rx->skb = skb;
|
||||
/* skb_copy() does not copy the hw timestamps, so copy it
|
||||
* explicitly
|
||||
*/
|
||||
shwt = skb_hwtstamps(rx->skb);
|
||||
shwt->hwtstamp = skb_hwtstamps(skb)->hwtstamp;
|
||||
}
|
||||
|
||||
if (unlikely(link_sta)) {
|
||||
|
@ -624,9 +624,11 @@ ieee80211_sdata_from_skb(struct ieee80211_local *local, struct sk_buff *skb)
|
||||
}
|
||||
|
||||
static void ieee80211_report_ack_skb(struct ieee80211_local *local,
|
||||
struct ieee80211_tx_info *info,
|
||||
bool acked, bool dropped)
|
||||
struct sk_buff *orig_skb,
|
||||
bool acked, bool dropped,
|
||||
ktime_t ack_hwtstamp)
|
||||
{
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(orig_skb);
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
|
||||
@ -643,6 +645,19 @@ static void ieee80211_report_ack_skb(struct ieee80211_local *local,
|
||||
struct ieee80211_hdr *hdr = (void *)skb->data;
|
||||
bool is_valid_ack_signal =
|
||||
!!(info->status.flags & IEEE80211_TX_STATUS_ACK_SIGNAL_VALID);
|
||||
struct cfg80211_tx_status status = {
|
||||
.cookie = cookie,
|
||||
.buf = skb->data,
|
||||
.len = skb->len,
|
||||
.ack = acked,
|
||||
};
|
||||
|
||||
if (ieee80211_is_timing_measurement(orig_skb) ||
|
||||
ieee80211_is_ftm(orig_skb)) {
|
||||
status.tx_tstamp =
|
||||
ktime_to_ns(skb_hwtstamps(orig_skb)->hwtstamp);
|
||||
status.ack_tstamp = ktime_to_ns(ack_hwtstamp);
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
sdata = ieee80211_sdata_from_skb(local, skb);
|
||||
@ -662,9 +677,9 @@ static void ieee80211_report_ack_skb(struct ieee80211_local *local,
|
||||
is_valid_ack_signal,
|
||||
GFP_ATOMIC);
|
||||
else if (ieee80211_is_mgmt(hdr->frame_control))
|
||||
cfg80211_mgmt_tx_status(&sdata->wdev, cookie,
|
||||
skb->data, skb->len,
|
||||
acked, GFP_ATOMIC);
|
||||
cfg80211_mgmt_tx_status_ext(&sdata->wdev,
|
||||
&status,
|
||||
GFP_ATOMIC);
|
||||
else
|
||||
pr_warn("Unknown status report in ack skb\n");
|
||||
|
||||
@ -681,7 +696,8 @@ static void ieee80211_report_ack_skb(struct ieee80211_local *local,
|
||||
}
|
||||
|
||||
static void ieee80211_report_used_skb(struct ieee80211_local *local,
|
||||
struct sk_buff *skb, bool dropped)
|
||||
struct sk_buff *skb, bool dropped,
|
||||
ktime_t ack_hwtstamp)
|
||||
{
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
u16 tx_time_est = ieee80211_info_get_tx_time_est(info);
|
||||
@ -744,7 +760,8 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
|
||||
|
||||
rcu_read_unlock();
|
||||
} else if (info->ack_frame_id) {
|
||||
ieee80211_report_ack_skb(local, info, acked, dropped);
|
||||
ieee80211_report_ack_skb(local, skb, acked, dropped,
|
||||
ack_hwtstamp);
|
||||
}
|
||||
|
||||
if (!dropped && skb->destructor) {
|
||||
@ -1038,7 +1055,7 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw,
|
||||
jiffies + msecs_to_jiffies(10));
|
||||
}
|
||||
|
||||
ieee80211_report_used_skb(local, skb, false);
|
||||
ieee80211_report_used_skb(local, skb, false, status->ack_hwtstamp);
|
||||
|
||||
/* this was a transmitted frame, but now we want to reuse it */
|
||||
skb_orphan(skb);
|
||||
@ -1201,7 +1218,7 @@ free:
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
ieee80211_report_used_skb(local, skb, false);
|
||||
ieee80211_report_used_skb(local, skb, false, status->ack_hwtstamp);
|
||||
if (status->free_list)
|
||||
list_add_tail(&skb->list, status->free_list);
|
||||
else
|
||||
@ -1262,8 +1279,9 @@ EXPORT_SYMBOL(ieee80211_report_low_ack);
|
||||
void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
ktime_t kt = ktime_set(0, 0);
|
||||
|
||||
ieee80211_report_used_skb(local, skb, true);
|
||||
ieee80211_report_used_skb(local, skb, true, kt);
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_free_txskb);
|
||||
|
Loading…
x
Reference in New Issue
Block a user