* 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;
/**
* @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;
* (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
*/
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;
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)
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 (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_khz(&rx->sdata->wdev,
- ieee80211_rx_status_to_khz(status), sig,
- rx->skb->data, rx->skb->len, 0)) {
+ if (cfg80211_rx_mgmt_ext(&rx->sdata->wdev, &info)) {
if (rx->sta)
rx->sta->deflink.rx_stats.packets++;
dev_kfree_skb(rx->skb);
}
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",
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)) {
}
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;
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);
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");
}
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);
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) {
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);
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
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);