libsigrok: add vbus voltage and current support for libsigrok

Vbus voltage and current are both in 16 bits.
Their timestamp offset with respect to the current packet timestamp
is also reported.  All these values are 16 bits.
Current value can be negative; negative means the samus is being charged;
positive means that the samus is supplying power.
Update: accepting timestamp in us; delete the threshold settings. report
all vbus information that are available. Let the decoders do the threshold
setting.

Updated: backward compatibility by allowing user to compile two verions of
the software. in libsigrok/src/hardware/chromium-twinkie/protocol.h
Use #define CONFIG_USBC_SNIFFER_HEADER_V1 if the twinkie is the old version,
which does not send out vbus information;
use #define CONFIG_USBC_SNIFFER_HEADER_V2 if the twinkie is the new version,
which sends out vbus information.

BUG=chrome-os-partner:42703
BRANCH=none

TEST=BEGIN
connect Twinkie to a waveform generator, record data using:
sudo sigrok-cli -d chromium-twinkie --continuous -o test.sr
then check the resulting waveforms on pulseview.
END

Signed-off-by: Dawei Li <daweili@google.com>

Change-Id: I471a1855f656dc93a0bf9935553acc4bcefd3329
Reviewed-on: https://chromium-review.googlesource.com/285532
Commit-Ready: Sheng-liang Song <ssl@chromium.org>
Tested-by: Dawei Li <daweili@google.com>
Reviewed-by: Sheng-liang Song <ssl@chromium.org>
diff --git a/src/hardware/chromium-twinkie/api.c b/src/hardware/chromium-twinkie/api.c
index afe0ec4..9960b7e 100644
--- a/src/hardware/chromium-twinkie/api.c
+++ b/src/hardware/chromium-twinkie/api.c
@@ -40,6 +40,8 @@
 SR_PRIV struct sr_dev_driver chromium_twinkie_driver_info;
 static struct sr_dev_driver *di = &chromium_twinkie_driver_info;
 
+extern int sniffer_sample_header_size;
+
 static const int32_t hwopts[] = {
 	SR_CONF_CONN,
 };
@@ -61,7 +63,7 @@
 };
 
 static const char *channel_names[] = {
-	"CC1", "CC2", "VBUS",
+	"CC1", "CC2", "VBus Voltage", "VBus Current",
 	NULL,
 };
 
@@ -128,6 +130,11 @@
 		if (des.idVendor != TWINKIE_VID || des.idProduct != TWINKIE_PID)
 			continue;
 
+#ifdef CONFIG_USBC_SNIFFER_HEADER_V1
+			sniffer_sample_header_size = SNIFFER_SAMPLE_HEADER_SIZE_WITHOUT_VBUS;
+#else /* CONFIG_USBC_SNIFFER_HEADER_V2 */
+			sniffer_sample_header_size = SNIFFER_SAMPLE_HEADER_SIZE_WITH_VBUS;
+#endif
 		usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
 
 		sdi = sr_dev_inst_new(SR_ST_INITIALIZING,
@@ -425,7 +432,6 @@
 
 	tv.tv_sec = tv.tv_usec = 0;
 	libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
-
 	if (devc->sent_samples == -2) {
 		abort_acquisition(devc);
 	}
@@ -463,11 +469,11 @@
 	devc->submitted_transfers = 0;
 
 	devc->convbuffer_size = convsize;
-	if (!(devc->convbuffer = g_try_malloc(convsize))) {
+	if (!(devc->convbuffer = g_try_malloc(convsize * SAMPLE_UNIT_SIZE))) {
 		sr_err("Conversion buffer malloc failed.");
 		return SR_ERR_MALLOC;
 	}
-	memset(devc->convbuffer, 0, devc->convbuffer_size);
+	memset(devc->convbuffer, 0, devc->convbuffer_size * SAMPLE_UNIT_SIZE);
 
 	devc->transfers = g_try_malloc0(sizeof(*devc->transfers) * num_transfers);
 	if (!devc->transfers) {
diff --git a/src/hardware/chromium-twinkie/protocol.c b/src/hardware/chromium-twinkie/protocol.c
index fc9acbc..fa0d74d 100644
--- a/src/hardware/chromium-twinkie/protocol.c
+++ b/src/hardware/chromium-twinkie/protocol.c
@@ -34,6 +34,8 @@
 #define COMMAND_ABORT_ACQUISITION_ASYNC	2
 #define COMMAND_ABORT_ACQUISITION_SYNC	0x7d
 
+int sniffer_sample_header_size; /* set in api.c, can be 4 or 8 */
+
 static int do_console_command(const struct sr_dev_inst *sdi,
 			      const uint8_t *command, uint8_t cmd_len,
 			      uint8_t *reply, uint8_t reply_len)
@@ -153,35 +155,80 @@
 	if (devc->limit_samples &&
 	    cnt > devc->limit_samples - devc->sent_samples)
 		cnt = devc->limit_samples - devc->sent_samples;
-	logic.length = cnt;
-	logic.unitsize = 1;
+	logic.length = cnt * SAMPLE_UNIT_SIZE;
+	logic.unitsize = SAMPLE_UNIT_SIZE;
 	logic.data = devc->convbuffer;
 	sr_session_send(devc->cb_data, &packet);
 	devc->sent_samples += cnt;
 }
 
+static void set_vbus_voltage_bits(uint8_t *vbus_dest, uint16_t vbus_voltage, int16_t offset)
+{
+	/*
+	 * Similar to RS232 encoding method. Encoding Format:
+	 * IDLE: 0
+	 * Start: 1
+	 * Data: Value[15:0], TimeStamp[15:0]
+	 * Stop: 0
+	 */
+	int i = 0;
+	/* the offset passed in is in us, and can be overflowed */
+	int offset_ms = offset/1000;
+	if (offset_ms > 0) /* convert overflowed value to the correct one */
+		offset_ms = -65 + offset_ms;
+	*vbus_dest |= 1<<2; /* start bit */
+	for (i = 0; i < 16; i ++)
+		*(vbus_dest + 1 + i) |= (((vbus_voltage >> i) & 1) << 2);
+	for (i = 0; i < 16; i ++)
+		*(vbus_dest + 17 + i) |= (((offset_ms >> i) & 1) << 2);
+}
+
+static void set_vbus_current_bits(uint8_t *vbus_dest, int16_t vbus_current, int16_t offset)
+{
+	int i = 0;
+	int offset_ms = offset/1000;
+	*vbus_dest |= 1<<3; /* start bit */
+	if (offset_ms > 0)
+		offset_ms = -65 + offset_ms;
+	for (i = 0; i < 16; i ++)
+		*(vbus_dest + 1 + i) |= (((vbus_current >> i) & 1) << 3);
+	for (i = 0; i < 16; i ++)
+		*(vbus_dest + 17 + i) |= (((offset_ms >> i) & 1) << 3);
+}
+
+uint16_t vbus_voltage_previous = 0;
+int16_t vbus_current_previous = 0;
+
 static void expand_sample_data(struct dev_context *devc,
 			       const uint8_t *src, size_t srccnt)
 {
 	int i, f;
 	size_t b;
 	size_t rdy_samples, left_samples;
-	int frames = srccnt / 64;
+	int frames = srccnt / SNIFFER_SAMPLE_PACKET_SIZE;
 
+	int sniffer_sample_payload_size = (SNIFFER_SAMPLE_PACKET_SIZE - sniffer_sample_header_size);
 	for (f = 0; f < frames; f++) {
 		int ch = (src[1] >> 4) & 3; /* samples channel number */
 		int bit = 1 << ch; /* channel bit mask */
 		struct cc_context *cc = devc->cc + ch;
 		uint8_t *dest = devc->convbuffer + cc->idx;
-
+		uint8_t *vbus_dest;
 		if (ch >= 2) /* only acquires CCx channels */
 			continue;
 
+		int vbus_voltage_set = 0; /* vbus set or not in this frame? 0: not; 1: set */
+		int vbus_current_set = 0;
+
 		/* TODO: check timestamp, overflow, sequence number */
+		/* need to calculate first; otherwise, will not get the correct value since src is updated */
+		uint16_t vbus_voltage = ((src[5] << 8 )| src[4]);
+		int16_t vbus_current = ((src[5] << 8 )| src[4]); /* may be voltage, may be current. */
+		int16_t vbus_offset = ((src[7] << 8 )| src[6]); /* may be the offset for voltage, or current.*/
 
 		/* skip header, go to edges data */
-		src+=4;
-		for (i = 0; i < 60; i++,src++)
+		src+= sniffer_sample_header_size;
+		for (i = 0; i < sniffer_sample_payload_size; i++,src++)
 			if (*src == cc->prev_src) {
 				cc->rollbacks++;
 			} else {
@@ -198,20 +245,39 @@
 					break;
 				}
 
+				vbus_dest = dest;
 				/* insert bits in the buffer */
 				if (cc->level)
 					for (b = 0 ; b < total ; b++, dest++)
 						*dest |= bit;
 				else
 					dest += total;
-				cc->idx += total;
 
+#ifdef CONFIG_USBC_SNIFFER_HEADER_V2
+				if (ch == 0) { /* cc1 header carries vbus voltage*/
+					if (!vbus_voltage_set) {
+						set_vbus_voltage_bits(vbus_dest, vbus_voltage, vbus_offset);
+						vbus_voltage_set = 1; /* after that, label vbus_voltage_set to true */
+						vbus_voltage_previous = vbus_voltage;
+					}
+				}
+				else { /* cc2 header carries vbus current*/
+					if (!vbus_current_set) {
+						set_vbus_current_bits(vbus_dest, vbus_current, vbus_offset);
+						vbus_current_set = 1;
+						vbus_current_previous = vbus_current;
+					}
+				}
+#endif
+
+				cc->idx += total;
 				/* flip level on the next edge */
 				cc->level = ~cc->level;
 
 				cc->rollbacks = 0;
 				cc->prev_src = *src;
 			}
+
 		/* expand repeated rollbacks */
 		if (cc->rollbacks > 1) {
 			size_t total = 256 * (cc->rollbacks - 1);
@@ -221,10 +287,30 @@
 				/* reset current decoding */
 				total = 0;
 			}
+			vbus_dest = dest;
 			/* insert bits in the buffer */
 			if (cc->level)
 				for (b = 0 ; b < total ; b++, dest++)
 					*dest |= bit ;
+			else
+				dest += total;
+
+#ifdef CONFIG_USBC_SNIFFER_HEADER_V2
+			if (ch == 0) {
+				if (!vbus_voltage_set) {
+					set_vbus_voltage_bits(vbus_dest, vbus_voltage, vbus_offset);
+					vbus_voltage_set = 1;
+					vbus_voltage_previous = vbus_voltage;
+				}
+			}
+			else {
+				if (!vbus_current_set) {
+					set_vbus_current_bits(vbus_dest, vbus_current, vbus_offset);
+					vbus_current_set = 1;
+					vbus_current_previous = vbus_current;
+				}
+			}
+#endif
 			cc->idx += total;
 			cc->rollbacks = 1;
 		}
@@ -240,8 +326,8 @@
 	export_samples(devc, rdy_samples);
 
 	/* clean up what we have sent */
-	memmove(devc->convbuffer, devc->convbuffer + rdy_samples, left_samples);
-	memset(devc->convbuffer + left_samples, 0, rdy_samples);
+	memmove(devc->convbuffer, devc->convbuffer + rdy_samples, left_samples * SAMPLE_UNIT_SIZE);
+	memset(devc->convbuffer + left_samples, 0, rdy_samples * SAMPLE_UNIT_SIZE);
 	devc->cc[0].idx -= rdy_samples;
 	devc->cc[1].idx -= rdy_samples;
 }
@@ -279,7 +365,7 @@
 		break;
 	}
 
-	if (transfer->actual_length % 64) {
+	if (transfer->actual_length % SNIFFER_SAMPLE_PACKET_SIZE) {
 		sr_err("Bad USB packet size.");
 		packet_has_error = TRUE;
 	}
diff --git a/src/hardware/chromium-twinkie/protocol.h b/src/hardware/chromium-twinkie/protocol.h
index 9b618d3..648ff99 100644
--- a/src/hardware/chromium-twinkie/protocol.h
+++ b/src/hardware/chromium-twinkie/protocol.h
@@ -26,6 +26,7 @@
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "twinkie"
+#define SAMPLE_UNIT_SIZE 1  /*in case we may want to change*/
 
 /** Private, per-CC logical channel context. */
 struct cc_context {
@@ -53,6 +54,24 @@
 	struct cc_context cc[2];
 };
 
+struct sniffer_sample_header {
+	uint16_t seq;
+	uint16_t tstamp;
+	uint16_t vbus_value; /* can be voltage or current */
+	int16_t vbus_offset; /* the timestamp offset with respect to current packet */
+};
+#define SNIFFER_SAMPLE_PACKET_SIZE 64
+#define SNIFFER_SAMPLE_HEADER_SIZE_WITHOUT_VBUS 4
+#define SNIFFER_SAMPLE_HEADER_SIZE_WITH_VBUS (sizeof(struct sniffer_sample_header))
+/*
+ * use #define CONFIG_USBC_SNIFFER_HEADER_V1
+ * if your twinkie does not send out vbus info;
+ * use #define CONFIG_USBC_SNIFFER_HEADER_V2
+ * if your twinkie sends out vbus info.
+ */
+#define CONFIG_USBC_SNIFFER_HEADER_V1
+
+
 SR_PRIV int twinkie_start_acquisition(const struct sr_dev_inst *sdi);
 SR_PRIV int twinkie_init_device(const struct sr_dev_inst *sdi);
 SR_PRIV void twinkie_receive_transfer(struct libusb_transfer *transfer);