Takashi Iwai 7a138b
From ecc7b4187dd388549544195fb13a11b4ea8e6a84 Mon Sep 17 00:00:00 2001
Takashi Iwai 7a138b
From: Stephane Grosjean <s.grosjean@peak-system.com>
Takashi Iwai 7a138b
Date: Wed, 14 Oct 2020 10:56:31 +0200
Takashi Iwai 7a138b
Subject: [PATCH] can: peak_usb: peak_usb_get_ts_time(): fix timestamp wrapping
Takashi Iwai 7a138b
Git-commit: ecc7b4187dd388549544195fb13a11b4ea8e6a84
Takashi Iwai 7a138b
Patch-mainline: v5.10-rc3
Takashi Iwai 7a138b
References: git-fixes
Takashi Iwai 7a138b
Takashi Iwai 7a138b
Fabian Inostroza <fabianinostrozap@gmail.com> has discovered a potential
Takashi Iwai 7a138b
problem in the hardware timestamp reporting from the PCAN-USB USB CAN interface
Takashi Iwai 7a138b
(only), related to the fact that a timestamp of an event may precede the
Takashi Iwai 7a138b
timestamp used for synchronization when both records are part of the same USB
Takashi Iwai 7a138b
packet. However, this case was used to detect the wrapping of the time counter.
Takashi Iwai 7a138b
Takashi Iwai 7a138b
This patch details and fixes the two identified cases where this problem can
Takashi Iwai 7a138b
occur.
Takashi Iwai 7a138b
Takashi Iwai 7a138b
Reported-by: Fabian Inostroza <fabianinostrozap@gmail.com>
Takashi Iwai 7a138b
Signed-off-by: Stephane Grosjean <s.grosjean@peak-system.com>
Takashi Iwai 7a138b
Link: https://lore.kernel.org/r/20201014085631.15128-1-s.grosjean@peak-system.com
Takashi Iwai 7a138b
Fixes: bb4785551f64 ("can: usb: PEAK-System Technik USB adapters driver core")
Takashi Iwai 7a138b
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Takashi Iwai 7a138b
Acked-by: Takashi Iwai <tiwai@suse.de>
Takashi Iwai 7a138b
Takashi Iwai 7a138b
---
Takashi Iwai 7a138b
 drivers/net/can/usb/peak_usb/pcan_usb_core.c |   51 ++++++++++++++++++++++++---
Takashi Iwai 7a138b
 1 file changed, 46 insertions(+), 5 deletions(-)
Takashi Iwai 7a138b
Takashi Iwai 7a138b
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
Takashi Iwai 7a138b
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
Takashi Iwai 7a138b
@@ -154,14 +154,55 @@ void peak_usb_get_ts_tv(struct peak_time
Takashi Iwai 7a138b
 	/* protect from getting timeval before setting now */
Takashi Iwai 7a138b
 	if (time_ref->tv_host.tv_sec > 0) {
Takashi Iwai 7a138b
 		u64 delta_us;
Takashi Iwai 7a138b
+		s64 delta_ts = 0;
Takashi Iwai 7a138b
 
Takashi Iwai 7a138b
-		delta_us = ts - time_ref->ts_dev_2;
Takashi Iwai 7a138b
-		if (ts < time_ref->ts_dev_2)
Takashi Iwai 7a138b
-			delta_us &= (1 << time_ref->adapter->ts_used_bits) - 1;
Takashi Iwai 7a138b
+		/* General case: dev_ts_1 < dev_ts_2 < ts, with:
Takashi Iwai 7a138b
+		 *
Takashi Iwai 7a138b
+		 * - dev_ts_1 = previous sync timestamp
Takashi Iwai 7a138b
+		 * - dev_ts_2 = last sync timestamp
Takashi Iwai 7a138b
+		 * - ts = event timestamp
Takashi Iwai 7a138b
+		 * - ts_period = known sync period (theoretical)
Takashi Iwai 7a138b
+		 *             ~ dev_ts2 - dev_ts1
Takashi Iwai 7a138b
+		 * *but*:
Takashi Iwai 7a138b
+		 *
Takashi Iwai 7a138b
+		 * - time counters wrap (see adapter->ts_used_bits)
Takashi Iwai 7a138b
+		 * - sometimes, dev_ts_1 < ts < dev_ts2
Takashi Iwai 7a138b
+		 *
Takashi Iwai 7a138b
+		 * "normal" case (sync time counters increase):
Takashi Iwai 7a138b
+		 * must take into account case when ts wraps (tsw)
Takashi Iwai 7a138b
+		 *
Takashi Iwai 7a138b
+		 *      < ts_period > <          >
Takashi Iwai 7a138b
+		 *     |             |            |
Takashi Iwai 7a138b
+		 *  ---+--------+----+-------0-+--+-->
Takashi Iwai 7a138b
+		 *     ts_dev_1 |    ts_dev_2  |
Takashi Iwai 7a138b
+		 *              ts             tsw
Takashi Iwai 7a138b
+		 */
Takashi Iwai 7a138b
+		if (time_ref->ts_dev_1 < time_ref->ts_dev_2) {
Takashi Iwai 7a138b
+			/* case when event time (tsw) wraps */
Takashi Iwai 7a138b
+			if (ts < time_ref->ts_dev_1)
Takashi Iwai 7a138b
+				delta_ts = 1 << time_ref->adapter->ts_used_bits;
Takashi Iwai 7a138b
 
Takashi Iwai 7a138b
-		delta_us += time_ref->ts_total;
Takashi Iwai 7a138b
+		/* Otherwise, sync time counter (ts_dev_2) has wrapped:
Takashi Iwai 7a138b
+		 * handle case when event time (tsn) hasn't.
Takashi Iwai 7a138b
+		 *
Takashi Iwai 7a138b
+		 *      < ts_period > <          >
Takashi Iwai 7a138b
+		 *     |             |            |
Takashi Iwai 7a138b
+		 *  ---+--------+--0-+---------+--+-->
Takashi Iwai 7a138b
+		 *     ts_dev_1 |    ts_dev_2  |
Takashi Iwai 7a138b
+		 *              tsn            ts
Takashi Iwai 7a138b
+		 */
Takashi Iwai 7a138b
+		} else if (time_ref->ts_dev_1 < ts) {
Takashi Iwai 7a138b
+			delta_ts = -(1 << time_ref->adapter->ts_used_bits);
Takashi Iwai 7a138b
+		}
Takashi Iwai 7a138b
 
Takashi Iwai 7a138b
-		delta_us *= time_ref->adapter->us_per_ts_scale;
Takashi Iwai 7a138b
+		/* add delay between last sync and event timestamps */
Takashi Iwai 7a138b
+		delta_ts += (signed int)(ts - time_ref->ts_dev_2);
Takashi Iwai 7a138b
+
Takashi Iwai 7a138b
+		/* add time from beginning to last sync */
Takashi Iwai 7a138b
+		delta_ts += time_ref->ts_total;
Takashi Iwai 7a138b
+
Takashi Iwai 7a138b
+		/* convert ticks number into microseconds */
Takashi Iwai 7a138b
+		delta_us = delta_ts * time_ref->adapter->us_per_ts_scale;
Takashi Iwai 7a138b
 		delta_us >>= time_ref->adapter->us_per_ts_shift;
Takashi Iwai 7a138b
 
Takashi Iwai 7a138b
 		*tv = time_ref->tv_host_0;