File 0001-sipeed-slogic-analyzer-Initial-driver-skeleton.patch of Package libsigrok

From 02fb41b0b523d4dd53aaef70d5c86503e7d313e5 Mon Sep 17 00:00:00 2001
From: taorye <taorye@outlook.com>
Date: Sun, 29 Jan 2023 18:19:49 +0800
Subject: [PATCH 01/17] sipeed-slogic-analyzer: Initial driver skeleton.

sipeed-slogic-analyzer: Add support for SlogicCombo8

feat: use pattern to control active channels

feat: capture data and regroup channels

feat: now support max 160Msps(2ch)

continous and multi channel

sipeed-slogic-analyzer: Add support for SlogicBasic16U3

sipeed-slogic-analyzer: feat: use SR_CONF_BUFFERSIZE to configurate the channel

sipeed-slogic-analyzer: feat: at least 2 transfers are pending in parallel, and evaluate transfer latency, also fix cancel_transfer must be handled

sipeed-slogic-analyzer: feat: attempt to use function pointers to maintain compatibility with Lite 8

sipeed-slogic-analyzer: fix: slogic lite 8 use ep(0x81)

sipeed-slogic-analyzer: fix: stop while has no transfers submitted

sipeed-slogic-analyzer: fix: skip sending real stop command for slogic lite 8

sipeed-slogic-analyzer: fix: libusb_handle_events at right time,  use `%%` and start from 125ms

sipeed-slogic-analyzer: fix: transfers not consumed from the end, so just use NUM_MAX_TRANSFERS instead of num_transfers_used

sipeed-slogic-analyzer: chore: add udev rules for slogic

sipeed-slogic-analyzer: feat: support sample rate and channel configuration

sipeed-slogic-analyzer: feat: separate sr_session_send(sdi, &packet) from receive_transfer handler to ensure usb work continuously

sipeed-slogic-analyzer: feat: switch back to 5% tolerance and use a continuous count to determine real timeout

sipeed-slogic-analyzer: refactor: simplify and reuse data packet format

sipeed-slogic-analyzer: perf: use GThread for raw_data_handle to not distrub handle_events

sipeed-slogic-analyzer: fix: aux length starts from bit9 and try to split control write in 4 bytes

sipeed-slogic-analyzer: feat: add samplerate configuration support

sipeed-slogic-analyzer: fix: select updated samplerate base

sipeed-slogic-analyzer: fix: use 4MiB size transfers default to clear previously generated EP data

sipeed-slogic-analyzer: refactor: consolidate data submission logic by using `slogic_submit_raw_data`

Replace multiple data submission implementations with the common
`slogic_submit_raw_data` function to maintain consistency and reduce
code duplication.

sipeed-slogic-analyzer: Prepare for upstream submission

* Implement USB streaming thread isolation
  - Decouples raw data processing from libusb transfers callback
  - Enables stable 200MHz sampling (16ch) on SlogicBasic16U3 (max bandwidth: up to 430 MiB/s on test)

* Windows-specific enhancements
  - Resolves USB scheduling bottleneck
  - Support stable 40MHz sampling (8ch) on SlogicCombo8
  - Verified on Win10/Win11 platforms (VMs included)

Co-authored-by: Shenzhen Sipeed Team <support@sipeed.com>
Co-authored-by: taorye <hongtao@sipeed.com, taorye@outlook.com>

sipeed-slogic-analyzer: fix: standardize product name to "SLogic16U3"

sipeed-slogic-analyzer: refactor: lift per_transfer_nbytes enumeration into a separate function

fix: use libusb_handle_events after to free transfer that submitted in `train_bulk_in_transfer`

feat: support to set vref

feat: support adjust vref

fix: handle libusb_event in a dedicated thread after opening usb device, and set LIBUSB_TRANSFER_FREE_BUFFER. Ensure all transfers can be freed.

feat: support test on patten selected

tmp: stop sampling after all transfers are freed

fix: update samplerates and voltage threshold calculations; handle acquisition abort correctly

fix: implement soft trigger logic and manage trigger state in acquisition
---
 Makefile.am                                   |    6 +
 configure.ac                                  |    1 +
 contrib/60-libsigrok.rules                    |    7 +
 src/hardware/sipeed-slogic-analyzer/api.c     | 1109 +++++++++++++++++
 .../sipeed-slogic-analyzer/protocol.c         |  462 +++++++
 .../sipeed-slogic-analyzer/protocol.h         |  116 ++
 6 files changed, 1701 insertions(+)
 create mode 100644 src/hardware/sipeed-slogic-analyzer/api.c
 create mode 100644 src/hardware/sipeed-slogic-analyzer/protocol.c
 create mode 100644 src/hardware/sipeed-slogic-analyzer/protocol.h

diff --git a/Makefile.am b/Makefile.am
index 3b68142f2..7f21304dc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -701,6 +701,12 @@ src_libdrivers_la_SOURCES += \
 	src/hardware/siglent-sds/protocol.c \
 	src/hardware/siglent-sds/api.c
 endif
+if HW_SIPEED_SLOGIC_ANALYZER
+src_libdrivers_la_SOURCES += \
+	src/hardware/sipeed-slogic-analyzer/protocol.h \
+	src/hardware/sipeed-slogic-analyzer/protocol.c \
+	src/hardware/sipeed-slogic-analyzer/api.c
+endif
 if HW_SYSCLK_LWLA
 src_libdrivers_la_SOURCES += \
 	src/hardware/sysclk-lwla/lwla.h \
diff --git a/configure.ac b/configure.ac
index 0b3f426c8..78bcd5507 100644
--- a/configure.ac
+++ b/configure.ac
@@ -385,6 +385,7 @@ SR_DRIVER([serial DMM], [serial-dmm], [serial_comm])
 SR_DRIVER([serial LCR], [serial-lcr], [serial_comm])
 SR_DRIVER([SIGLENT SDL10x0], [siglent-sdl10x0])
 SR_DRIVER([Siglent SDS], [siglent-sds])
+SR_DRIVER([Sipeed Slogic Analyzer], [sipeed-slogic-analyzer], [libusb])
 SR_DRIVER([Sysclk LWLA], [sysclk-lwla], [libusb])
 SR_DRIVER([Sysclk SLA5032], [sysclk-sla5032], [libusb])
 SR_DRIVER([Teleinfo], [teleinfo], [serial_comm])
diff --git a/contrib/60-libsigrok.rules b/contrib/60-libsigrok.rules
index 716178039..56e05a704 100644
--- a/contrib/60-libsigrok.rules
+++ b/contrib/60-libsigrok.rules
@@ -307,6 +307,13 @@ ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608e", ENV{ID_SIGROK}="1"
 # sigrok usb-c-grok
 ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="608f", ENV{ID_SIGROK}="1"
 
+
+# Sipeed(359f) Slogic series
+# 0300: Slogic Lite 8
+# 3031: Slogic Basic 16 U3
+ATTRS{idVendor}=="359f", ATTRS{idProduct}=="0300", ENV{ID_SIGROK}="1"
+ATTRS{idVendor}=="359f", ATTRS{idProduct}=="3031", ENV{ID_SIGROK}="1"
+
 # SiLabs CP210x (USB CDC) UART bridge, used (among others) in:
 # CEM DT-8852
 # Manson HCS-3202
diff --git a/src/hardware/sipeed-slogic-analyzer/api.c b/src/hardware/sipeed-slogic-analyzer/api.c
new file mode 100644
index 000000000..e20b8fba9
--- /dev/null
+++ b/src/hardware/sipeed-slogic-analyzer/api.c
@@ -0,0 +1,1109 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2023-2025 Shenzhen Sipeed Technology Co., Ltd.
+ * (深圳市矽速科技有限公司) <support@sipeed.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "protocol.h"
+
+static int slogic16U3_remote_test_mode(const struct sr_dev_inst *sdi, uint32_t mode);
+
+static const uint32_t scanopts[] = {
+	SR_CONF_CONN,
+};
+
+static const uint32_t drvopts[] = {
+	SR_CONF_LOGIC_ANALYZER,
+};
+
+static const uint32_t devopts[] = {
+	SR_CONF_CONTINUOUS,
+	SR_CONF_BUFFERSIZE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+	SR_CONF_PATTERN_MODE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+	SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+	SR_CONF_VOLTAGE_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const uint64_t samplerates[] = {
+	/* 160M = 2^5*5M */
+	/* 1600M = 2^6*5^2M */
+	SR_MHZ(1),
+	SR_MHZ(2),
+	SR_MHZ(4),
+	SR_MHZ(5),
+	SR_MHZ(8),
+	SR_MHZ(10),
+	SR_MHZ(16),
+	SR_MHZ(20),
+	SR_MHZ(32),
+
+	/* SLogic Combo 8 */
+	/* x 8ch */
+	SR_MHZ(40),
+	/* x 4ch */
+	SR_MHZ(60),
+	SR_MHZ(80),
+	SR_MHZ(100),
+	/* x 2ch */
+	SR_MHZ(125),
+	SR_MHZ(150),
+	SR_MHZ(160),
+
+	/* SLogic16U3 */
+	/* x 16ch */
+	SR_MHZ(200),
+	/* x 8ch */
+	SR_MHZ(300),
+	SR_MHZ(400),
+	/* x 4ch */
+	SR_MHZ(500),
+	SR_MHZ(600),
+	SR_MHZ(750),
+	SR_MHZ(800),
+	/* x 2ch */
+	SR_MHZ(1000),
+	SR_MHZ(1200),
+	SR_MHZ(1500),
+	SR_MHZ(1600),
+};
+
+static const uint64_t buffersizes[] = { 2, 4, 8, 16 };
+
+static const char *patterns[] = {
+	[PATTERN_MODE_NOMAL] = "PATTERN_MODE_NOMAL",
+	[PATTERN_MODE_TEST_MAX_SPEED] = "PATTERN_MODE_TEST_MAX_SPEED",
+	[PATTERN_MODE_TEST_HARDWARE_USB_MAX_SPEED] = "PATTERN_MODE_TEST_HARDWARE_USB_MAX_SPEED",
+	[PATTERN_MODE_TEST_HARDWARE_EMU_DATA] = "PATTERN_MODE_TEST_HARDWARE_EMU_DATA",
+};
+
+static const int32_t trigger_matches[] = {
+	SR_TRIGGER_ZERO,    SR_TRIGGER_ONE,  SR_TRIGGER_RISING,
+	SR_TRIGGER_FALLING, SR_TRIGGER_EDGE,
+};
+
+static struct sr_dev_driver sipeed_slogic_analyzer_driver_info;
+
+static struct slogic_model *const support_models_ptr;
+
+static gpointer libusb_event_thread_func(gpointer user_data)
+{
+	struct sr_dev_inst *sdi;
+	struct sr_dev_driver *di;
+	struct dev_context *devc;
+	struct drv_context *drvc;
+
+	sdi = user_data;
+	devc = sdi->priv;
+	di = sdi->driver;
+	drvc = di->context;
+
+	while (devc->libusb_event_thread_run) {
+		libusb_handle_events_timeout_completed(
+			drvc->sr_ctx->libusb_ctx, &(struct timeval){ 1, 0 },
+			NULL);
+	}
+
+	return NULL;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+	int ret;
+	struct sr_dev_inst *sdi;
+	struct sr_usb_dev_inst *usb;
+	struct drv_context *drvc;
+	struct dev_context *devc;
+
+	struct slogic_model *model;
+	struct sr_config *option;
+	struct libusb_device_descriptor des;
+	GSList *devices;
+	GSList *l, *conn_devices;
+	const char *conn;
+	char cbuf[128];
+	char *iManufacturer, *iProduct, *iSerialNumber, *iPortPath;
+
+	struct sr_channel *ch;
+	unsigned int i;
+	gchar *channel_name;
+
+	(void)options;
+
+	conn = NULL;
+
+	devices = NULL;
+	drvc = di->context;
+	// drvc->instances = NULL;
+
+	/* scan for devices, either based on a SR_CONF_CONN option
+   * or on a USB scan. */
+	for (l = options; l; l = l->next) {
+		option = l->data;
+		switch (option->key) {
+		case SR_CONF_CONN:
+			conn = g_variant_get_string(option->data, NULL);
+			sr_info("Use conn: %s", conn);
+			sr_err("Not supported now!");
+			return NULL;
+			break;
+		default:
+			sr_warn("Unhandled option key: %u", option->key);
+		}
+	}
+
+	for (model = support_models_ptr; model->name; model++) {
+		conn = g_strdup_printf("%04x.%04x", USB_VID_SIPEED, model->pid);
+		/* Find all slogic compatible devices. */
+		conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn);
+		for (l = conn_devices; l; l = l->next) {
+			usb = l->data;
+			ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb);
+			if (SR_OK != ret)
+				continue;
+			libusb_get_device_descriptor(
+				libusb_get_device(usb->devhdl), &des);
+			libusb_get_string_descriptor_ascii(usb->devhdl,
+							   des.iManufacturer,
+							   cbuf, sizeof(cbuf));
+			iManufacturer = g_strdup(cbuf);
+			libusb_get_string_descriptor_ascii(
+				usb->devhdl, des.iProduct, cbuf, sizeof(cbuf));
+			iProduct = g_strdup(cbuf);
+			libusb_get_string_descriptor_ascii(usb->devhdl,
+							   des.iSerialNumber,
+							   cbuf, sizeof(cbuf));
+			iSerialNumber = g_strdup(cbuf);
+			usb_get_port_path(libusb_get_device(usb->devhdl), cbuf,
+					  sizeof(cbuf));
+			iPortPath = g_strdup(cbuf);
+
+			sdi = sr_dev_inst_user_new(iManufacturer, iProduct,
+						   NULL);
+			sdi->serial_num = iSerialNumber;
+			sdi->connection_id = iPortPath;
+			sdi->status = SR_ST_INACTIVE;
+			sdi->conn = usb;
+			sdi->inst_type = SR_INST_USB;
+
+			devc = g_malloc0(sizeof(struct dev_context));
+			sdi->priv = devc;
+
+			{
+				devc->model = model;
+
+				devc->limit_samplechannel =
+					devc->model->max_samplechannel;
+				devc->limit_samplerate =
+					devc->model->max_bandwidth /
+					devc->model->max_samplechannel;
+
+				devc->cur_samplechannel =
+					devc->limit_samplechannel;
+				devc->cur_samplerate = devc->limit_samplerate;
+				devc->cur_pattern_mode_idx = PATTERN_MODE_NOMAL;
+				devc->voltage_threshold[0] =
+					devc->voltage_threshold[1] = 0.8f;
+
+				devc->digital_group =
+					sr_channel_group_new(sdi, "LA", NULL);
+				for (i = 0; i < devc->model->max_samplechannel;
+				     i++) {
+					channel_name =
+						g_strdup_printf("D%u", i);
+					ch = sr_channel_new(sdi, i,
+							    SR_CHANNEL_LOGIC,
+							    TRUE, channel_name);
+					g_free(channel_name);
+					devc->digital_group
+						->channels = g_slist_append(
+						devc->digital_group->channels,
+						ch);
+				}
+
+				devc->speed = libusb_get_device_speed(
+					libusb_get_device(usb->devhdl));
+			}
+
+			sr_usb_close(usb);
+			devices = g_slist_append(devices, sdi);
+		}
+		// g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free);
+		g_free(conn);
+	}
+
+	return std_scan_complete(di, devices);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+	int ret;
+	struct sr_usb_dev_inst *usb;
+	struct dev_context *devc;
+	struct sr_dev_driver *di;
+	struct drv_context *drvc;
+
+	usb = sdi->conn;
+	devc = sdi->priv;
+	di = sdi->driver;
+	drvc = di->context;
+
+	ret = sr_usb_open(drvc->sr_ctx->libusb_ctx, usb);
+	if (SR_OK != ret)
+		return ret;
+
+	ret = libusb_claim_interface(usb->devhdl, 0);
+	if (ret != LIBUSB_SUCCESS) {
+		switch (ret) {
+		case LIBUSB_ERROR_BUSY:
+			sr_err("Unable to claim USB interface. Another "
+			       "program or driver has already claimed it.");
+			break;
+		case LIBUSB_ERROR_NO_DEVICE:
+			sr_err("Device has been disconnected.");
+			break;
+		default:
+			sr_err("Unable to claim interface: %s.",
+			       libusb_error_name(ret));
+			break;
+		}
+		return SR_ERR;
+	}
+
+	devc->libusb_event_thread_run = 1;
+	devc->libusb_event_thread = g_thread_new("libusb_event_thread",
+						 libusb_event_thread_func, sdi);
+	if (!devc->libusb_event_thread) {
+		devc->libusb_event_thread_run = 0;
+		sr_err("Unable to new libusb_event_thread!");
+		return SR_ERR_MALLOC;
+	}
+
+	if (devc->model->operation.remote_reset)
+		devc->model->operation.remote_reset(sdi);
+
+	return std_dummy_dev_open(sdi);
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+	int ret;
+	struct sr_usb_dev_inst *usb;
+	struct dev_context *devc;
+	struct sr_dev_driver *di;
+	struct drv_context *drvc;
+
+	usb = sdi->conn;
+	devc = sdi->priv;
+	di = sdi->driver;
+	drvc = di->context;
+
+	ret = libusb_release_interface(usb->devhdl, 0);
+	if (ret != LIBUSB_SUCCESS) {
+		switch (ret) {
+		case LIBUSB_ERROR_NO_DEVICE:
+			sr_err("Device has been disconnected.");
+			// return SR_ERR_DEV_CLOSED;
+			break;
+		default:
+			sr_err("Unable to release Interface for %s.",
+			       libusb_error_name(ret));
+			break;
+		}
+	}
+
+	devc->libusb_event_thread_run = 0;
+	sr_usb_close(usb);
+	if (devc->libusb_event_thread) {
+		g_thread_join(devc->libusb_event_thread);
+		devc->libusb_event_thread = NULL;
+	}
+
+	return std_dummy_dev_close(sdi);
+}
+
+static int config_get(uint32_t key, GVariant **data,
+		      const struct sr_dev_inst *sdi,
+		      const struct sr_channel_group *cg)
+{
+	int ret;
+	struct dev_context *devc;
+
+	(void)cg;
+
+	devc = sdi->priv;
+
+	ret = SR_OK;
+	switch (key) {
+	case SR_CONF_SAMPLERATE:
+		*data = g_variant_new_uint64(devc->cur_samplerate);
+		break;
+	case SR_CONF_BUFFERSIZE:
+		*data = g_variant_new_uint64(devc->cur_samplechannel);
+		break;
+	case SR_CONF_PATTERN_MODE:
+		*data = g_variant_new_string(
+			patterns[devc->cur_pattern_mode_idx]);
+		break;
+	case SR_CONF_LIMIT_SAMPLES:
+		*data = g_variant_new_uint64(devc->cur_limit_samples);
+		break;
+	case SR_CONF_VOLTAGE_THRESHOLD:
+		*data = std_gvar_tuple_double(devc->voltage_threshold[0],
+					      devc->voltage_threshold[1]);
+		break;
+	default:
+		return SR_ERR_NA;
+	}
+
+	return ret;
+}
+
+static int config_set(uint32_t key, GVariant *data,
+		      const struct sr_dev_inst *sdi,
+		      const struct sr_channel_group *cg)
+{
+	int ret;
+	struct dev_context *devc;
+
+	(void)cg;
+
+	devc = sdi->priv;
+
+	ret = SR_OK;
+	switch (key) {
+	case SR_CONF_SAMPLERATE:
+		if (g_variant_get_uint64(data) > devc->limit_samplerate ||
+		    std_u64_idx(data, ARRAY_AND_SIZE(samplerates)) < 0) {
+			devc->cur_samplerate = devc->limit_samplerate;
+			sr_warn("Reach limit or not supported, wrap to %uMHz.",
+				devc->limit_samplerate / SR_MHZ(1));
+		} else {
+			devc->cur_samplerate = g_variant_get_uint64(data);
+		}
+		devc->limit_samplechannel =
+			devc->model->max_bandwidth / devc->cur_samplerate;
+		if (devc->limit_samplechannel > devc->model->max_samplechannel)
+			devc->limit_samplechannel =
+				devc->model->max_samplechannel;
+		break;
+	case SR_CONF_BUFFERSIZE:
+		if (g_variant_get_uint64(data) > devc->limit_samplechannel ||
+		    std_u64_idx(data, ARRAY_AND_SIZE(buffersizes)) < 0) {
+			devc->cur_samplechannel = devc->limit_samplechannel;
+			sr_warn("Reach limit or not supported, wrap to %uch.",
+				devc->limit_samplechannel);
+		} else {
+			devc->cur_samplechannel = g_variant_get_uint64(data);
+		}
+		devc->limit_samplerate =
+			devc->model->max_bandwidth / devc->cur_samplechannel;
+		if (devc->limit_samplerate > devc->model->max_samplerate)
+			devc->limit_samplerate = devc->model->max_samplerate;
+
+		// [en|dis]able channels and dbg
+		{
+			for (GSList *l = devc->digital_group->channels; l;
+			     l = l->next) {
+				struct sr_channel *ch = l->data;
+				if (ch->type ==
+				    SR_CHANNEL_LOGIC) { /* Might as well do this now, these
+                                               are static. */
+					sr_dev_channel_enable(
+						ch, (ch->index >=
+						     devc->cur_samplechannel) ?
+							    FALSE :
+							    TRUE);
+				} else {
+					sr_warn("devc->digital_group->channels[%u] is not Logic?",
+						ch->index);
+				}
+				sr_dbg("\tch[%2u] %-3s:%d %sabled priv:%p.",
+				       ch->index, ch->name, ch->type,
+				       ch->enabled ? "en" : "dis", ch->priv);
+			}
+		}
+		break;
+	case SR_CONF_PATTERN_MODE:
+		devc->cur_pattern_mode_idx =
+			std_str_idx(data, ARRAY_AND_SIZE(patterns));
+		if (devc->cur_pattern_mode_idx < 0)
+			devc->cur_pattern_mode_idx = 0;
+		if (devc->model != &support_models_ptr[1]) {
+			sr_warn("unsupported model: %s.", devc->model->name);
+			break;
+		}
+		if (devc->cur_pattern_mode_idx == PATTERN_MODE_NOMAL) {
+			if (devc->model->operation.remote_reset)
+				devc->model->operation.remote_reset(sdi);
+			slogic16U3_remote_test_mode(sdi, 0x0);
+			sr_dbg("reset model: %s success.", devc->model->name);
+		} else if (devc->cur_pattern_mode_idx == PATTERN_MODE_TEST_HARDWARE_USB_MAX_SPEED) {
+			slogic16U3_remote_test_mode(sdi, 0x1);
+		} else if (devc->cur_pattern_mode_idx == PATTERN_MODE_TEST_HARDWARE_EMU_DATA) {
+			slogic16U3_remote_test_mode(sdi, 0x2);
+		}
+		break;
+	case SR_CONF_LIMIT_SAMPLES:
+		devc->cur_limit_samples = g_variant_get_uint64(data);
+		break;
+	case SR_CONF_VOLTAGE_THRESHOLD:
+		g_variant_get(data, "(dd)", &devc->voltage_threshold[0],
+			      &devc->voltage_threshold[1]);
+		break;
+	default:
+		ret = SR_ERR_NA;
+	}
+
+	return ret;
+}
+
+static int config_list(uint32_t key, GVariant **data,
+		       const struct sr_dev_inst *sdi,
+		       const struct sr_channel_group *cg)
+{
+	int ret;
+	struct dev_context *devc;
+
+	(void)cg;
+
+	devc = sdi ? (sdi->priv) : NULL;
+
+	ret = SR_OK;
+	switch (key) {
+	case SR_CONF_SCAN_OPTIONS:
+	case SR_CONF_DEVICE_OPTIONS:
+		ret = STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts,
+				      devopts);
+		break;
+	case SR_CONF_SAMPLERATE:
+		*data = std_gvar_samplerates(
+			samplerates,
+			1 + std_u64_idx(g_variant_new_uint64(
+						devc->limit_samplerate),
+					ARRAY_AND_SIZE(samplerates)));
+		break;
+	case SR_CONF_BUFFERSIZE:
+		*data = std_gvar_array_u64(
+			buffersizes,
+			1 + std_u64_idx(g_variant_new_uint64(
+						devc->limit_samplechannel),
+					ARRAY_AND_SIZE(buffersizes)));
+		break;
+	case SR_CONF_PATTERN_MODE:
+		*data = g_variant_new_strv(ARRAY_AND_SIZE(patterns));
+		break;
+	case SR_CONF_TRIGGER_MATCH:
+		*data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches));
+		break;
+	case SR_CONF_VOLTAGE_THRESHOLD:
+		*data = std_gvar_min_max_step_thresholds(0, 333, 1);
+		break;
+	default:
+		ret = SR_ERR_NA;
+	}
+
+	return ret;
+}
+
+static struct sr_dev_driver sipeed_slogic_analyzer_driver_info = {
+	.name = "sipeed-slogic-analyzer",
+	.longname = "Sipeed SLogic Analyzer",
+	.api_version = 1,
+	.init = std_init,
+	.cleanup = std_cleanup,
+	.scan = scan,
+	.dev_list = std_dev_list,
+	.dev_clear = std_dev_clear,
+	.config_get = config_get,
+	.config_set = config_set,
+	.config_list = config_list,
+	.dev_open = dev_open,
+	.dev_close = dev_close,
+	.dev_acquisition_start = sipeed_slogic_acquisition_start,
+	.dev_acquisition_stop = sipeed_slogic_acquisition_stop,
+	.context = NULL,
+};
+SR_REGISTER_DEV_DRIVER(sipeed_slogic_analyzer_driver_info);
+
+static int slogic_usb_control_write(const struct sr_dev_inst *sdi,
+				    uint8_t request, uint16_t value,
+				    uint16_t index, uint8_t *data, size_t len,
+				    int timeout)
+{
+	int ret;
+	struct dev_context *devc;
+	struct sr_usb_dev_inst *usb;
+
+	devc = sdi->priv;
+	usb = sdi->conn;
+
+	sr_spew("%s: req:%u value:%u index:%u %p:%u in %dms.", __func__,
+		request, value, index, data, len, timeout);
+	if (!data && len) {
+		sr_warn("%s: Nothing to write although len(%u)>0!", __func__,
+			len);
+		len = 0;
+	} else if (len & 0x3) {
+		size_t len_aligndup = (len + 0x3) & (~0x3);
+		sr_warn("%s: Align up to %u(from %u)!", __func__, len_aligndup,
+			len);
+		len = len_aligndup;
+	}
+
+	ret = 0;
+	for (size_t i = 0; i < len; i += 4) {
+		ret += libusb_control_transfer(
+			usb->devhdl,
+			LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT,
+			request, value + i, index, (unsigned char *)data + i, 4,
+			timeout);
+		if (ret < 0) {
+			sr_err("%s: failed(libusb: %s)!", __func__,
+			       libusb_error_name(ret));
+			return SR_ERR_NA;
+		}
+	}
+
+	return ret;
+}
+
+static int slogic_usb_control_read(const struct sr_dev_inst *sdi,
+				   uint8_t request, uint16_t value,
+				   uint16_t index, uint8_t *data, size_t len,
+				   int timeout)
+{
+	int ret;
+	struct dev_context *devc;
+	struct sr_usb_dev_inst *usb;
+
+	devc = sdi->priv;
+	usb = sdi->conn;
+
+	sr_spew("%s: req:%u value:%u index:%u %p:%u in %dms.", __func__,
+		request, value, index, data, len, timeout);
+	if (!data && len) {
+		sr_err("%s: Can't read to NULL while len(%u)>0!", __func__,
+		       len);
+		return SR_ERR_ARG;
+	} else if (len & 0x3) {
+		size_t len_aligndup = (len + 0x3) & (~0x3);
+		sr_warn("%s: Align up to %u(from %u)!", __func__, len_aligndup,
+			len);
+		len = len_aligndup;
+	}
+
+	ret = 0;
+	for (size_t i = 0; i < len; i += 4) {
+		ret += libusb_control_transfer(
+			usb->devhdl,
+			LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN,
+			request, value + i, index, (unsigned char *)data + i, 4,
+			timeout);
+		if (ret < 0) {
+			sr_err("%s: failed(libusb: %s)!", __func__,
+			       libusb_error_name(ret));
+			return SR_ERR_NA;
+		}
+	}
+
+	return ret;
+}
+
+static void slogic_submit_raw_data(void *data, size_t len,
+				   const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc = sdi->priv;
+
+	uint8_t *ptr = data;
+	uint64_t nCh = devc->cur_samplechannel;
+
+	if (nCh < 8) {
+		size_t nsp_in_bytes = 8 / nCh; // NOW must be 2 and 4
+		ptr = malloc(len * nsp_in_bytes);
+		for (size_t i = 0; i < len; i += nCh) {
+			for (size_t j = 0; j < 8; j++) {
+				ptr[i * nsp_in_bytes + j] =
+					(((uint8_t *)
+						  data)[i + j / nsp_in_bytes] >>
+					 (j % nsp_in_bytes * nCh)) &
+					((1 << nCh) - 1);
+			}
+		}
+		len *= nsp_in_bytes; // need reshape
+	}
+
+	sr_session_send(sdi, &(struct sr_datafeed_packet){
+				     .type = SR_DF_LOGIC,
+				     .payload = &(struct sr_datafeed_logic){
+					     .length = len,
+					     .unitsize = (nCh + 7) / 8,
+					     .data = ptr,
+				     } });
+
+	if (nCh < 8)
+		free(ptr);
+}
+
+int slogic_soft_trigger_raw_data(void *data, size_t len,
+				   const struct sr_dev_inst *sdi)
+{
+	int ret = 0;
+	struct dev_context *devc = sdi->priv;
+
+	uint8_t *ptr = data;
+	uint64_t nCh = devc->cur_samplechannel;
+	uint8_t uintsize = (nCh + 7) / 8;
+
+	if (nCh < 8) {
+		size_t nsp_in_bytes = 8 / nCh; // NOW must be 2 or 4
+		ptr = malloc(len * nsp_in_bytes);
+		for (size_t i = 0; i < len; i += nCh) {
+			for (size_t j = 0; j < 8; j++) {
+				ptr[i * nsp_in_bytes + j] =
+					(((uint8_t *)
+						  data)[i + j / nsp_in_bytes] >>
+					 (j % nsp_in_bytes * nCh)) &
+					((1 << nCh) - 1);
+			}
+		}
+		len *= nsp_in_bytes; // need reshape
+	}
+
+	int pre_trigger_samples;
+	devc->stl->unitsize = uintsize;
+	int64_t trigger_offset = soft_trigger_logic_check(devc->stl, ptr, len, &pre_trigger_samples);
+	if (trigger_offset > -1) {
+		ret += pre_trigger_samples;
+
+		sr_session_send(sdi, &(struct sr_datafeed_packet){
+				     .type = SR_DF_LOGIC,
+				     .payload = &(struct sr_datafeed_logic){
+					     .length = len - trigger_offset * uintsize,
+					     .unitsize = uintsize,
+					     .data = ptr + trigger_offset * uintsize,
+				     } });
+
+		ret += len / uintsize - trigger_offset;
+	}
+
+	if (nCh < 8)
+		free(ptr);
+
+	return ret;
+}
+
+// #define __USE_MISC 1
+// #include <endian.h>
+static inline uint16_t htole16(uint16_t value)
+{
+	const union {
+		uint16_t val;
+		uint8_t bytes[2];
+	} u = { .val = 0x1234 };
+	if (u.bytes[0] == 0x34) { // __LITTLE_ENDIAN
+		return value;
+	} else {
+		return ((value & 0xFF) << 8) | ((value >> 8) & 0xFF);
+	}
+}
+
+static inline void clear_ep(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc = sdi->priv;
+	struct sr_usb_dev_inst *usb = sdi->conn;
+	uint8_t ep = devc->model->ep_in;
+
+	size_t tmp_size = 4 * 1024 * 1024;
+	uint8_t *tmp = malloc(tmp_size);
+	int actual_length = 0;
+	do {
+		libusb_bulk_transfer(usb->devhdl, ep, tmp, tmp_size,
+				     &actual_length, 100);
+	} while (actual_length);
+	free(tmp);
+	sr_dbg("Cleared EP: 0x%02x", ep);
+}
+
+/* SLogic Combo 8 start */
+#pragma pack(push, 1)
+struct cmd_start_acquisition {
+	union {
+		struct {
+			uint8_t sample_rate_l;
+			uint8_t sample_rate_h;
+		};
+		uint16_t sample_rate;
+	};
+	uint8_t sample_channel;
+};
+#pragma pack(pop)
+
+#define CMD_START 0xb1
+#define CMD_STOP 0xb3
+
+static int slogic_combo8_remote_run(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc = sdi->priv;
+	const struct cmd_start_acquisition cmd_run = {
+		.sample_rate = htole16(devc->cur_samplerate /
+				       SR_MHZ(1)), // force little endian
+		.sample_channel = devc->cur_samplechannel,
+	};
+	return slogic_usb_control_write(sdi, CMD_START, 0x0000, 0x0000,
+					(uint8_t *)&cmd_run, sizeof(cmd_run),
+					500);
+}
+
+static int slogic_combo8_remote_stop(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc = sdi->priv;
+	struct sr_usb_dev_inst *usb = sdi->conn;
+	clear_ep(sdi);
+	return SR_OK;
+	/* not stable, but can be ignored */
+	// int ret = slogic_usb_control_write(sdi, CMD_STOP, 0x0000, 0x0000, NULL, 0,
+	// 500); clear_ep(sdi); return ret;
+}
+/* SLogic Combo 8 end */
+
+/* SLogic16U3 start */
+#define SLOGIC16U3_CONTROL_IN_REQ_REG_READ 0x00
+#define SLOGIC16U3_CONTROL_OUT_REQ_REG_WRITE 0x01
+
+#define SLOGIC16U3_R32_CTRL 0x0004
+#define SLOGIC16U3_R32_FLAG 0x0008
+#define SLOGIC16U3_R32_AUX 0x000c
+
+static int slogic16U3_remote_test_mode(const struct sr_dev_inst *sdi, uint32_t mode) {
+	struct dev_context *devc = sdi->priv;
+	uint8_t cmd_aux[64] = { 0 }; // configure aux
+
+	{
+		size_t retry = 0;
+		memset(cmd_aux, 0, sizeof(cmd_aux));
+		*(uint32_t *)(cmd_aux) = 0x00000005;
+		slogic_usb_control_write(sdi,
+					 SLOGIC16U3_CONTROL_OUT_REQ_REG_WRITE,
+					 SLOGIC16U3_R32_AUX, 0x0000, cmd_aux, 4,
+					 500);
+		do {
+			slogic_usb_control_read(
+				sdi, SLOGIC16U3_CONTROL_IN_REQ_REG_READ,
+				SLOGIC16U3_R32_AUX, 0x0000, cmd_aux, 4, 500);
+			sr_dbg("[%u]read testmode: %08x.", retry,
+			       ((uint32_t *)cmd_aux)[0]);
+			retry += 1;
+			if (retry > 5)
+				return SR_ERR_TIMEOUT;
+		} while (!(cmd_aux[2] & 0x01));
+
+		sr_dbg("test_mode length: %u.", (*(uint16_t *)cmd_aux) >> 9);
+		slogic_usb_control_read(sdi, SLOGIC16U3_CONTROL_IN_REQ_REG_READ,
+					SLOGIC16U3_R32_AUX + 4, 0x0000,
+					cmd_aux + 4,
+					(*(uint16_t *)cmd_aux) >> 9, 500);
+
+		sr_dbg("aux: %u %u %u %u %08x.", cmd_aux[0], cmd_aux[1],
+		       cmd_aux[2], cmd_aux[3], ((uint32_t *)(cmd_aux + 4))[0]);
+
+		((uint32_t *)(cmd_aux + 4))[0] = mode;
+
+		sr_dbg("aux: %u %u %u %u %08x.", cmd_aux[0], cmd_aux[1],
+		       cmd_aux[2], cmd_aux[3], ((uint32_t *)(cmd_aux + 4))[0]);
+		slogic_usb_control_write(sdi,
+					 SLOGIC16U3_CONTROL_OUT_REQ_REG_WRITE,
+					 SLOGIC16U3_R32_AUX + 4, 0x0000,
+					 cmd_aux + 4,
+					 (*(uint16_t *)cmd_aux) >> 9, 500);
+
+		slogic_usb_control_read(sdi, SLOGIC16U3_CONTROL_IN_REQ_REG_READ,
+					SLOGIC16U3_R32_AUX + 4, 0x0000,
+					cmd_aux + 4,
+					(*(uint16_t *)cmd_aux) >> 9, 500);
+		sr_dbg("aux: %u %u %u %u %08x.", cmd_aux[0], cmd_aux[1],
+		       cmd_aux[2], cmd_aux[3], ((uint32_t *)(cmd_aux + 4))[0]);
+
+		if (mode != *(uint32_t *)(cmd_aux + 4)) {
+			sr_dbg("Failed to configure test_mode.");
+		} else {
+			sr_dbg("Succeed to configure test_mode.");
+		}
+	}
+}
+
+static int slogic16U3_remote_reset(const struct sr_dev_inst *sdi) {
+	struct dev_context *devc = sdi->priv;
+	const uint8_t cmd_rst[] = { 0x02, 0x00, 0x00, 0x00 };
+	const uint8_t cmd_derst[] = { 0x00, 0x00, 0x00, 0x00 };
+
+	slogic_usb_control_write(sdi, SLOGIC16U3_CONTROL_OUT_REQ_REG_WRITE,
+		SLOGIC16U3_R32_CTRL, 0x0000,
+		ARRAY_AND_SIZE(cmd_rst), 500);
+
+	return slogic_usb_control_write(sdi, SLOGIC16U3_CONTROL_OUT_REQ_REG_WRITE,
+				 SLOGIC16U3_R32_CTRL, 0x0000,
+				 ARRAY_AND_SIZE(cmd_derst), 500);
+}
+
+static int slogic16U3_remote_run(const struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc = sdi->priv;
+	const uint8_t cmd_run[] = { 0x01, 0x00, 0x00, 0x00 };
+	uint8_t cmd_aux[64] = { 0 }; // configure aux
+
+	{
+		size_t retry = 0;
+		memset(cmd_aux, 0, sizeof(cmd_aux));
+		*(uint32_t *)(cmd_aux) = 0x00000001;
+		slogic_usb_control_write(sdi,
+					 SLOGIC16U3_CONTROL_OUT_REQ_REG_WRITE,
+					 SLOGIC16U3_R32_AUX, 0x0000, cmd_aux, 4,
+					 500);
+		do {
+			slogic_usb_control_read(
+				sdi, SLOGIC16U3_CONTROL_IN_REQ_REG_READ,
+				SLOGIC16U3_R32_AUX, 0x0000, cmd_aux, 4, 500);
+			sr_dbg("[%u]read aux channel: %08x.", retry,
+			       ((uint32_t *)cmd_aux)[0]);
+			retry += 1;
+			if (retry > 5)
+				return SR_ERR_TIMEOUT;
+		} while (!(cmd_aux[2] & 0x01));
+		sr_dbg("channel length: %u.", (*(uint16_t *)cmd_aux) >> 9);
+		slogic_usb_control_read(sdi, SLOGIC16U3_CONTROL_IN_REQ_REG_READ,
+					SLOGIC16U3_R32_AUX + 4, 0x0000,
+					cmd_aux + 4,
+					(*(uint16_t *)cmd_aux) >> 9, 500);
+
+		sr_dbg("aux: %u %u %u %u %08x.", cmd_aux[0], cmd_aux[1],
+		       cmd_aux[2], cmd_aux[3], ((uint32_t *)(cmd_aux + 4))[0]);
+
+		*(uint32_t *)(cmd_aux + 4) = (1 << devc->cur_samplechannel) - 1;
+
+		sr_dbg("aux: %u %u %u %u %08x.", cmd_aux[0], cmd_aux[1],
+		       cmd_aux[2], cmd_aux[3], ((uint32_t *)(cmd_aux + 4))[0]);
+		slogic_usb_control_write(sdi,
+					 SLOGIC16U3_CONTROL_OUT_REQ_REG_WRITE,
+					 SLOGIC16U3_R32_AUX + 4, 0x0000,
+					 cmd_aux + 4,
+					 (*(uint16_t *)cmd_aux) >> 9, 500);
+
+		slogic_usb_control_read(sdi, SLOGIC16U3_CONTROL_IN_REQ_REG_READ,
+					SLOGIC16U3_R32_AUX + 4, 0x0000,
+					cmd_aux + 4,
+					(*(uint16_t *)cmd_aux) >> 9, 500);
+		sr_dbg("aux: %u %u %u %u %08x.", cmd_aux[0], cmd_aux[1],
+		       cmd_aux[2], cmd_aux[3], ((uint32_t *)(cmd_aux + 4))[0]);
+
+		if ((1 << devc->cur_samplechannel) - 1 !=
+		    *(uint32_t *)(cmd_aux + 4)) {
+			sr_dbg("Failed to configure sample channel.");
+		} else {
+			sr_dbg("Succeed to configure sample channel.");
+		}
+	}
+
+	{
+		size_t retry = 0;
+		memset(cmd_aux, 0, sizeof(cmd_aux));
+		*(uint32_t *)(cmd_aux) = 0x00000002;
+		slogic_usb_control_write(sdi,
+					 SLOGIC16U3_CONTROL_OUT_REQ_REG_WRITE,
+					 SLOGIC16U3_R32_AUX, 0x0000, cmd_aux, 4,
+					 500);
+		do {
+			slogic_usb_control_read(
+				sdi, SLOGIC16U3_CONTROL_IN_REQ_REG_READ,
+				SLOGIC16U3_R32_AUX, 0x0000, cmd_aux, 4, 500);
+			sr_dbg("[%u]read aux samplerate: %08x.", retry,
+			       ((uint32_t *)cmd_aux)[0]);
+			retry += 1;
+			if (retry > 5)
+				return SR_ERR_TIMEOUT;
+		} while (!(cmd_aux[2] & 0x01));
+		sr_dbg("samplerate length: %u.", (*(uint16_t *)cmd_aux) >> 9);
+
+		while (((uint16_t *)(cmd_aux + 4))[0] <= 1) {
+			slogic_usb_control_read(
+				sdi, SLOGIC16U3_CONTROL_IN_REQ_REG_READ,
+				SLOGIC16U3_R32_AUX + 4, 0x0000, cmd_aux + 4,
+				(*(uint16_t *)cmd_aux) >> 9, 500);
+
+			sr_dbg("aux: %u %u %u %u %x %u %u.", cmd_aux[0],
+			       cmd_aux[1], cmd_aux[2], cmd_aux[3],
+			       ((uint16_t *)(cmd_aux + 4))[0],
+			       ((uint16_t *)(cmd_aux + 4))[1],
+			       ((uint32_t *)(cmd_aux + 4))[1]);
+
+			uint64_t base =
+				SR_MHZ(1) * ((uint16_t *)(cmd_aux + 4))[1];
+			if (base % devc->cur_samplerate) {
+				sr_dbg("Failed to configure samplerate from base[%u] %u.",
+				       ((uint16_t *)(cmd_aux + 4))[0], base);
+				((uint16_t *)(cmd_aux + 4))[0] += 1;
+				slogic_usb_control_write(
+					sdi,
+					SLOGIC16U3_CONTROL_OUT_REQ_REG_WRITE,
+					SLOGIC16U3_R32_AUX + 4, 0x0000,
+					cmd_aux + 4, 4, 500);
+				continue;
+			}
+			uint32_t div = base / devc->cur_samplerate;
+			((uint32_t *)(cmd_aux + 4))[1] = div-1;
+
+			sr_dbg("aux: %u %u %u %u %x %u %u.", cmd_aux[0],
+			       cmd_aux[1], cmd_aux[2], cmd_aux[3],
+			       ((uint16_t *)(cmd_aux + 4))[0],
+			       ((uint16_t *)(cmd_aux + 4))[1],
+			       ((uint32_t *)(cmd_aux + 4))[1]);
+			slogic_usb_control_write(
+				sdi, SLOGIC16U3_CONTROL_OUT_REQ_REG_WRITE,
+				SLOGIC16U3_R32_AUX + 4, 0x0000, cmd_aux + 4,
+				(*(uint16_t *)cmd_aux) >> 9, 500);
+
+			slogic_usb_control_read(
+				sdi, SLOGIC16U3_CONTROL_IN_REQ_REG_READ,
+				SLOGIC16U3_R32_AUX + 4, 0x0000, cmd_aux + 4,
+				(*(uint16_t *)cmd_aux) >> 9, 500);
+			sr_dbg("aux: %u %u %u %u %x %u %u.", cmd_aux[0],
+			       cmd_aux[1], cmd_aux[2], cmd_aux[3],
+			       ((uint16_t *)(cmd_aux + 4))[0],
+			       ((uint16_t *)(cmd_aux + 4))[1],
+			       ((uint32_t *)(cmd_aux + 4))[1]);
+			break;
+		}
+
+		if (((uint16_t *)(cmd_aux + 4))[0] <= 1) {
+			sr_dbg("Succeed to configure samplerate.");
+		} else {
+			sr_dbg("Failed to configure samplerate.");
+		}
+	}
+
+	{
+		size_t retry = 0;
+		memset(cmd_aux, 0, sizeof(cmd_aux));
+		*(uint32_t *)(cmd_aux) = 0x00000003;
+		slogic_usb_control_write(sdi,
+					 SLOGIC16U3_CONTROL_OUT_REQ_REG_WRITE,
+					 SLOGIC16U3_R32_AUX, 0x0000, cmd_aux, 4,
+					 500);
+		do {
+			slogic_usb_control_read(
+				sdi, SLOGIC16U3_CONTROL_IN_REQ_REG_READ,
+				SLOGIC16U3_R32_AUX, 0x0000, cmd_aux, 4, 500);
+			sr_dbg("[%u]read vref(/1024x1v6): %08x.", retry,
+			       ((uint32_t *)cmd_aux)[0]);
+			retry += 1;
+			if (retry > 5)
+				return SR_ERR_TIMEOUT;
+		} while (!(cmd_aux[2] & 0x01));
+		// *(uint16_t*)cmd_aux &= ~0xfe00;
+		// *(uint16_t*)cmd_aux |= 0x800;
+		sr_dbg("vref length: %u.", (*(uint16_t *)cmd_aux) >> 9);
+		slogic_usb_control_read(sdi, SLOGIC16U3_CONTROL_IN_REQ_REG_READ,
+					SLOGIC16U3_R32_AUX + 4, 0x0000,
+					cmd_aux + 4,
+					(*(uint16_t *)cmd_aux) >> 9, 500);
+
+		sr_dbg("aux: %u %u %u %u %08x.", cmd_aux[0], cmd_aux[1],
+		       cmd_aux[2], cmd_aux[3], ((uint32_t *)(cmd_aux + 4))[0]);
+
+		((uint32_t *)(cmd_aux + 4))[0] =
+			(uint32_t)((devc->voltage_threshold[0] +
+				    devc->voltage_threshold[1]) /
+				   2 / 333 * 1024);
+
+		sr_dbg("aux: %u %u %u %u %08x.", cmd_aux[0], cmd_aux[1],
+		       cmd_aux[2], cmd_aux[3], ((uint32_t *)(cmd_aux + 4))[0]);
+		slogic_usb_control_write(sdi,
+					 SLOGIC16U3_CONTROL_OUT_REQ_REG_WRITE,
+					 SLOGIC16U3_R32_AUX + 4, 0x0000,
+					 cmd_aux + 4,
+					 (*(uint16_t *)cmd_aux) >> 9, 500);
+
+		slogic_usb_control_read(sdi, SLOGIC16U3_CONTROL_IN_REQ_REG_READ,
+					SLOGIC16U3_R32_AUX + 4, 0x0000,
+					cmd_aux + 4,
+					(*(uint16_t *)cmd_aux) >> 9, 500);
+		sr_dbg("aux: %u %u %u %u %08x.", cmd_aux[0], cmd_aux[1],
+		       cmd_aux[2], cmd_aux[3], ((uint32_t *)(cmd_aux + 4))[0]);
+
+		if (1024 != *(uint32_t *)(cmd_aux + 4)) {
+			sr_dbg("Failed to configure vref.");
+		} else {
+			sr_dbg("Succeed to configure vref.");
+		}
+	}
+
+	return slogic_usb_control_write(sdi,
+					SLOGIC16U3_CONTROL_OUT_REQ_REG_WRITE,
+					SLOGIC16U3_R32_CTRL, 0x0000,
+					ARRAY_AND_SIZE(cmd_run), 500);
+}
+
+static int slogic16U3_remote_stop(const struct sr_dev_inst *sdi)
+{
+	const uint8_t cmd_stop[] = { 0x00, 0x00, 0x00, 0x00 };
+	return slogic_usb_control_write(sdi,
+					SLOGIC16U3_CONTROL_OUT_REQ_REG_WRITE,
+					SLOGIC16U3_R32_CTRL, 0x0000,
+					ARRAY_AND_SIZE(cmd_stop), 500);
+}
+/* SLogic16U3 end */
+
+static const struct slogic_model support_models[] = {
+    {
+        .name = "Sogic Combo 8",
+        .pid = 0x0300,
+        .ep_in = 0x01 | LIBUSB_ENDPOINT_IN,
+        .max_samplerate = SR_MHZ(160),
+        .max_samplechannel = 8,
+        .max_bandwidth = SR_MHZ(320),
+        .operation =
+            {
+                .remote_reset = NULL,
+                .remote_run = slogic_combo8_remote_run,
+                .remote_stop = slogic_combo8_remote_stop,
+            },
+        .submit_raw_data = slogic_submit_raw_data,
+    },
+    {
+        .name = "SLogic16U3",
+        .pid = 0x3031,
+        .ep_in = 0x02 | LIBUSB_ENDPOINT_IN,
+        .max_samplerate = SR_MHZ(1600),
+        .max_samplechannel = 16,
+        .max_bandwidth = SR_MHZ(3200),
+        .operation =
+            {
+                .remote_reset = slogic16U3_remote_reset,
+                .remote_run = slogic16U3_remote_run,
+                .remote_stop = slogic16U3_remote_stop,
+            },
+        .submit_raw_data = slogic_submit_raw_data,
+    },
+    {
+        .name = NULL,
+        .pid = 0x0000,
+    }};
+
+static struct slogic_model *const support_models_ptr = &support_models[0];
\ No newline at end of file
diff --git a/src/hardware/sipeed-slogic-analyzer/protocol.c b/src/hardware/sipeed-slogic-analyzer/protocol.c
new file mode 100644
index 000000000..19c0eb69e
--- /dev/null
+++ b/src/hardware/sipeed-slogic-analyzer/protocol.c
@@ -0,0 +1,462 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2023-2025 Shenzhen Sipeed Technology Co., Ltd.
+ * (深圳市矽速科技有限公司) <support@sipeed.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "protocol.h"
+
+static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer)
+{
+	int ret;
+	const struct sr_dev_inst *sdi;
+	struct dev_context *devc;
+
+	sdi = transfer->user_data;
+	if (!sdi)
+		return;
+	devc = sdi->priv;
+
+	int64_t transfers_reached_time_now = g_get_monotonic_time();
+	int64_t transfers_reached_duration =
+		transfers_reached_time_now -
+		devc->transfers_reached_time_latest;
+	int64_t transfers_all_duration =
+		transfers_reached_time_now - devc->transfers_reached_time_start;
+
+	devc->num_transfers_used -= 1;
+	devc->num_transfers_completed += 1;
+	sr_spew("[%d] Transfer #%d status: %d(%s).",
+		devc->num_transfers_completed,
+		std_u64_idx(g_variant_new_uint64((uint64_t)transfer),
+			    (uint64_t *)devc->transfers, NUM_MAX_TRANSFERS),
+		transfer->status, libusb_error_name(transfer->status));
+	switch (transfer->status) {
+	case LIBUSB_TRANSFER_COMPLETED: /* normal case */
+	case LIBUSB_TRANSFER_TIMED_OUT: /* may have received some data */
+	{
+		devc->transfers_reached_time_latest =
+			transfers_reached_time_now;
+
+		devc->transfers_reached_nbytes_latest = transfer->actual_length;
+		devc->transfers_reached_nbytes +=
+			devc->transfers_reached_nbytes_latest;
+
+		if (transfer->actual_length >
+		    devc->samples_need_nbytes - devc->samples_got_nbytes)
+			transfer->actual_length = devc->samples_need_nbytes -
+						  devc->samples_got_nbytes;
+		devc->samples_got_nbytes += transfer->actual_length;
+
+		sr_dbg("[%u] Got %u/%u(%.2f%%) => speed: %.2fMBps, %.2fMBps(avg) => "
+		       "+%.3f=%.3fms.",
+		       devc->num_transfers_completed, devc->samples_got_nbytes,
+		       devc->samples_need_nbytes,
+		       100.f * devc->samples_got_nbytes /
+			       devc->samples_need_nbytes,
+		       (double)devc->transfers_reached_nbytes_latest /
+			       transfers_reached_duration,
+		       (double)devc->transfers_reached_nbytes /
+			       transfers_all_duration,
+		       (double)transfers_reached_duration / SR_KHZ(1),
+		       (double)transfers_all_duration / SR_KHZ(1));
+
+		/* TODO: move out submit to ensure continuous transfers */
+		if (devc->raw_data_queue &&
+		    devc->cur_pattern_mode_idx != PATTERN_MODE_TEST_MAX_SPEED) {
+			uint8_t *d = transfer->buffer;
+			size_t len = transfer->actual_length;
+			// sr_dbg("HEAD: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x
+			// %02x %02x %02x %02x %02x", 	d[0], d[1], d[2], d[3], d[4], d[5], d[6],
+			// d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
+			// devc->model->submit_raw_data(d, len, sdi);
+
+			uint8_t *ptr = malloc(devc->per_transfer_nbytes);
+			if (!ptr) {
+				sr_err("Failed to allocate memory: %u bytes!",
+				       devc->per_transfer_nbytes);
+				devc->acq_aborted = 1;
+				break;
+			}
+			transfer->buffer = ptr;
+			GByteArray *array = g_byte_array_new_take(d, len);
+			g_async_queue_push(devc->raw_data_queue, array);
+		}
+
+		if (devc->samples_got_nbytes +
+			    devc->num_transfers_used *
+				    devc->per_transfer_nbytes <
+		    devc->samples_need_nbytes) {
+			transfer->actual_length = 0;
+			transfer->timeout = (TRANSFERS_DURATION_TOLERANCE + 1) *
+					    devc->per_transfer_duration *
+					    (devc->num_transfers_used + 2);
+			ret = libusb_submit_transfer(transfer);
+			if (ret) {
+				sr_dbg("Failed to submit transfer: %s",
+				       libusb_error_name(ret));
+			} else {
+				sr_spew("Resubmit transfer: %p", transfer);
+				devc->num_transfers_used += 1;
+			}
+		}
+	} break;
+
+	case LIBUSB_TRANSFER_OVERFLOW:
+	case LIBUSB_TRANSFER_STALL:
+	case LIBUSB_TRANSFER_NO_DEVICE:
+	default:
+		devc->acq_aborted = 1;
+		break;
+	}
+
+	if (devc->num_transfers_completed &&
+	    (double)transfers_reached_duration / SR_KHZ(1) >
+		    (TRANSFERS_DURATION_TOLERANCE + 1) *
+			    devc->per_transfer_duration) {
+		devc->timeout_count += 1;
+		if (devc->timeout_count > devc->num_transfers_used) {
+			sr_err("Timeout %.3fms!!! Reach duration limit: %.3f(%u+%.1f%%), %.3f > "
+			       "%.3f(%u+%.1f%%)(total) except first one.",
+			       (double)transfers_reached_duration / SR_KHZ(1),
+			       (TRANSFERS_DURATION_TOLERANCE + 1) *
+				       devc->per_transfer_duration,
+			       devc->per_transfer_duration,
+			       TRANSFERS_DURATION_TOLERANCE * 100,
+			       (double)transfers_all_duration / SR_KHZ(1),
+			       (TRANSFERS_DURATION_TOLERANCE + 1) *
+				       devc->per_transfer_duration *
+				       devc->num_transfers_completed,
+			       devc->per_transfer_duration *
+				       (devc->num_transfers_completed + 1),
+			       TRANSFERS_DURATION_TOLERANCE * 100);
+			devc->acq_aborted = 1;
+		}
+	} else {
+		devc->timeout_count = 0;
+	}
+
+	if (devc->num_transfers_used == 0) {
+		devc->acq_aborted = 1;
+	}
+};
+
+static int handle_events(int fd, int revents, void *cb_data)
+{
+	struct sr_dev_inst *sdi;
+	struct sr_dev_driver *di;
+	struct dev_context *devc;
+	struct drv_context *drvc;
+
+	(void)fd;
+	(void)revents;
+
+	sdi = cb_data;
+	devc = sdi->priv;
+	di = sdi->driver;
+	drvc = di->context;
+
+	// sr_spew("handle_events enter");
+
+	if (devc->acq_aborted) {
+		if (devc->num_transfers_used) {
+			for (size_t i = 0; i < NUM_MAX_TRANSFERS; ++i) {
+				struct libusb_transfer *transfer =
+					devc->transfers[i];
+				if (transfer) {
+					libusb_cancel_transfer(transfer);
+				}
+			}
+		} else {
+			int freed = 0;
+			for (size_t i = 0; i < NUM_MAX_TRANSFERS; ++i) {
+				struct libusb_transfer *transfer =
+					devc->transfers[i];
+				if (transfer) {
+					freed += 1;
+					libusb_free_transfer(transfer);
+					devc->transfers[i] = NULL;
+				}
+			}
+			if (freed) {
+				sr_dbg("Freed %d transfers.", freed);
+			} else {
+				if ((devc->model->operation.remote_stop(sdi)) < 0) {
+					sr_err("Unhandled `CMD_STOP`");
+				}
+				if (!g_async_queue_length(
+					   devc->raw_data_queue)) {
+					sr_dbg("Freed all transfers.");
+					g_async_queue_unref(devc->raw_data_queue);
+					devc->raw_data_queue = NULL;
+
+					if (devc->stl) {
+						soft_trigger_logic_free(devc->stl);
+						devc->stl = NULL;
+						devc->trigger_fired = FALSE;
+					}
+				}
+			}
+		}
+	}
+
+	if (!devc->raw_data_queue) {
+		sr_info("Bulk in %u/%u bytes with %u transfers.",
+			devc->samples_got_nbytes, devc->samples_need_nbytes,
+			devc->num_transfers_completed);
+		std_session_send_df_end(sdi);
+		sr_session_source_remove(sdi->session,
+					 -1 * (size_t)drvc->sr_ctx->libusb_ctx);
+	} else if (g_async_queue_length(devc->raw_data_queue)) {
+		GByteArray *array = g_async_queue_try_pop(devc->raw_data_queue);
+		if (array != NULL) {
+			if (devc->trigger_fired) {
+				devc->model->submit_raw_data(
+					array->data, array->len, sdi);
+			} else if (devc->stl) {
+				devc->samples_got_nbytes = 0;
+				extern int slogic_soft_trigger_raw_data(void *data, size_t len, const struct sr_dev_inst *sdi);
+				int sent_samples = slogic_soft_trigger_raw_data(array->data, array->len, sdi);
+				if (sent_samples) {
+					devc->samples_got_nbytes += sent_samples * devc->cur_samplechannel / 8;
+					devc->trigger_fired = TRUE;
+				}
+			}
+			g_byte_array_unref(array);
+		}
+	}
+
+	return TRUE;
+}
+
+// to find out the maixmum size of ONE transfer
+static int train_bulk_in_transfer(struct dev_context *devc,
+				  libusb_device_handle *dev_handle)
+{
+	struct libusb_transfer *transfer = libusb_alloc_transfer(0);
+	if (!transfer) {
+		sr_err("Failed to allocate libusb transfer!");
+		return SR_ERR_IO;
+	}
+
+	uint64_t sr = devc->cur_samplerate;
+	uint64_t ch = devc->cur_samplechannel;
+	uint64_t bps = sr * ch;
+	uint64_t Bps = bps / 8;
+	uint64_t BpMs = Bps / SR_KHZ(1);
+
+	uint64_t cur_transfer_duration = 125 /* ms */;
+	uint64_t try_transfer_nbytes = cur_transfer_duration * BpMs /* bytes */;
+
+	const uint64_t ALIGN_SIZE = 32 * 1024; /* 32kiB */
+	do {
+		// Align up
+		try_transfer_nbytes = (try_transfer_nbytes + (ALIGN_SIZE - 1)) &
+				      ~(ALIGN_SIZE - 1);
+
+		uint8_t *transfer_buffer = malloc(try_transfer_nbytes);
+		if (!transfer_buffer) {
+			sr_dbg("Failed to allocate memory: %u bytes! Half it.",
+			       try_transfer_nbytes);
+			try_transfer_nbytes >>= 1;
+			continue;
+		}
+
+		cur_transfer_duration = try_transfer_nbytes / BpMs;
+		sr_dbg("Train: receive %u bytes per %ums...",
+		       try_transfer_nbytes, cur_transfer_duration);
+
+		libusb_fill_bulk_transfer(transfer, dev_handle,
+					  devc->model->ep_in, transfer_buffer,
+					  try_transfer_nbytes, NULL, NULL, 0);
+		transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
+		transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
+		int ret = libusb_submit_transfer(transfer);
+		if (ret) {
+			sr_dbg("Failed to submit transfer: %s!",
+			       libusb_error_name(ret));
+			if (ret == LIBUSB_ERROR_NO_MEM) {
+				free(transfer->buffer);
+				sr_dbg("Half it and try again.");
+				try_transfer_nbytes >>= 1;
+				continue;
+			} else {
+				libusb_free_transfer(transfer);
+				return SR_ERR_IO;
+			}
+		}
+
+		ret = libusb_cancel_transfer(transfer);
+		if (ret) {
+			sr_dbg("Failed to cancel transfer: %s!",
+			       libusb_error_name(ret));
+		}
+
+		try_transfer_nbytes >>=
+			1; // At least 2 transfets can be pending.
+		break;
+	} while (try_transfer_nbytes >
+		 ALIGN_SIZE); // 32kiB > 125ms * 1MHZ * 2ch
+
+	cur_transfer_duration = try_transfer_nbytes / BpMs;
+	sr_dbg("Choose: receive %u bytes per %ums :)", try_transfer_nbytes,
+	       cur_transfer_duration);
+
+	// Assign
+	devc->per_transfer_duration = cur_transfer_duration;
+	devc->per_transfer_nbytes = try_transfer_nbytes;
+
+	return SR_OK;
+}
+
+SR_PRIV int sipeed_slogic_acquisition_start(const struct sr_dev_inst *sdi)
+{
+	struct sr_dev_driver *di;
+	struct dev_context *devc;
+	struct drv_context *drvc;
+	struct sr_usb_dev_inst *usb;
+
+	int ret;
+
+	devc = sdi->priv;
+	di = sdi->driver;
+	drvc = di->context;
+	usb = sdi->conn;
+
+	if ((ret = devc->model->operation.remote_stop(sdi)) < 0) {
+		sr_err("Unhandled `CMD_STOP`");
+		return ret;
+	}
+
+	devc->samples_got_nbytes = 0;
+	devc->samples_need_nbytes =
+		devc->cur_limit_samples * devc->cur_samplechannel / 8;
+	sr_info("Need %ux %uch@%uMHz in %ums.", devc->cur_limit_samples,
+		devc->cur_samplechannel, devc->cur_samplerate / SR_MHZ(1),
+		1000 * devc->cur_limit_samples / devc->cur_samplerate);
+
+	if ((ret = train_bulk_in_transfer(devc, usb->devhdl)) != SR_OK) {
+		sr_err("Failed to train bulk_in_transfer!`");
+		return ret;
+	}
+
+	devc->acq_aborted = 0;
+	devc->num_transfers_used = 0;
+	devc->num_transfers_completed = 0;
+	memset(devc->transfers, 0, sizeof(devc->transfers));
+	devc->transfers_reached_nbytes = 0;
+	devc->timeout_count = 0;
+	devc->raw_data_queue = g_async_queue_new();
+
+	if (!devc->raw_data_queue) {
+		sr_err("New g_async_queue failed, can't handle data anymore!");
+		return SR_ERR_MALLOC;
+	}
+
+	while (devc->num_transfers_used < NUM_MAX_TRANSFERS &&
+	       devc->samples_got_nbytes + devc->num_transfers_used *
+						  devc->per_transfer_nbytes <
+		       devc->samples_need_nbytes) {
+		uint8_t *dev_buf = malloc(devc->per_transfer_nbytes);
+		if (!dev_buf) {
+			sr_dbg("Failed to allocate memory[%d]",
+			       devc->num_transfers_used);
+			break;
+		}
+
+		struct libusb_transfer *transfer = libusb_alloc_transfer(0);
+		if (!transfer) {
+			sr_dbg("Failed to allocate transfer[%d]",
+			       devc->num_transfers_used);
+			free(dev_buf);
+			break;
+		}
+
+		libusb_fill_bulk_transfer(
+			transfer, usb->devhdl, devc->model->ep_in, dev_buf,
+			devc->per_transfer_nbytes, receive_transfer, sdi,
+			(TRANSFERS_DURATION_TOLERANCE + 1) *
+				devc->per_transfer_duration *
+				(devc->num_transfers_used + 2));
+		transfer->actual_length = 0;
+
+		transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
+		ret = libusb_submit_transfer(transfer);
+		if (ret) {
+			sr_dbg("Failed to submit transfer[%d]: %s.",
+			       devc->num_transfers_used,
+			       libusb_error_name(ret));
+			libusb_free_transfer(transfer);
+			break;
+		}
+		devc->transfers[devc->num_transfers_used] = transfer;
+		devc->num_transfers_used += 1;
+	}
+	sr_dbg("Submited %u transfers", devc->num_transfers_used);
+
+	if (!devc->num_transfers_used) {
+		return SR_ERR_IO;
+	}
+
+	std_session_send_df_header(sdi);
+	std_session_send_df_frame_begin(sdi);
+
+	sr_session_source_add(sdi->session,
+			      -1 * (size_t)drvc->sr_ctx->libusb_ctx, 0,
+			      (devc->per_transfer_duration / 2) ?: 1,
+			      handle_events, (void *)sdi);
+
+	devc->trigger_fired = TRUE;
+
+	devc->capture_ratio = 10;
+	struct sr_trigger *trigger = NULL;
+	/* Setup triggers */
+	if ((trigger = sr_session_trigger_get(sdi->session))) {
+		int pre_trigger_samples = 0;
+		if (devc->cur_limit_samples > 0)
+			pre_trigger_samples = (devc->capture_ratio * devc->cur_limit_samples) / 100;
+		devc->stl = soft_trigger_logic_new(sdi, trigger, pre_trigger_samples);
+		if (!devc->stl)
+			return SR_ERR_MALLOC;
+		devc->trigger_fired = FALSE;
+	}
+
+	if ((ret = devc->model->operation.remote_run(sdi)) < 0) {
+		sr_err("Unhandled `CMD_RUN`");
+		sipeed_slogic_acquisition_stop(sdi);
+		return ret;
+	}
+
+	devc->transfers_reached_time_start = g_get_monotonic_time();
+	devc->transfers_reached_time_latest =
+		devc->transfers_reached_time_start;
+
+	return SR_OK;
+}
+
+SR_PRIV int sipeed_slogic_acquisition_stop(struct sr_dev_inst *sdi)
+{
+	struct dev_context *devc;
+
+	devc = sdi->priv;
+
+	devc->acq_aborted = 1;
+
+	return SR_OK;
+}
diff --git a/src/hardware/sipeed-slogic-analyzer/protocol.h b/src/hardware/sipeed-slogic-analyzer/protocol.h
new file mode 100644
index 000000000..9eadd7e5d
--- /dev/null
+++ b/src/hardware/sipeed-slogic-analyzer/protocol.h
@@ -0,0 +1,116 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2023-2025 Shenzhen Sipeed Technology Co., Ltd.
+ * (深圳市矽速科技有限公司) <support@sipeed.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_SIPEED_SLOGIC_ANALYZER_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_SIPEED_SLOGIC_ANALYZER_PROTOCOL_H
+
+#include <glib.h>
+#include <libusb.h>
+#include <stdint.h>
+
+#include <libsigrok/libsigrok.h>
+
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "sipeed-slogic-analyzer"
+
+#define USB_VID_SIPEED UINT16_C(0x359f)
+#define NUM_MAX_TRANSFERS 64
+#define TRANSFERS_DURATION_TOLERANCE 0.05f
+
+enum {
+	PATTERN_MODE_NOMAL,
+	PATTERN_MODE_TEST_MAX_SPEED,
+	PATTERN_MODE_TEST_HARDWARE_USB_MAX_SPEED,
+	PATTERN_MODE_TEST_HARDWARE_EMU_DATA,
+};
+
+struct slogic_model {
+	char *name;
+	uint16_t pid;
+	uint8_t ep_in;
+	uint64_t max_samplerate; // limit by hardware
+	uint64_t max_samplechannel; // limit by hardware
+	uint64_t max_bandwidth; // limit by hardware
+	struct {
+		int (*remote_reset)(const struct sr_dev_inst *sdi);
+		int (*remote_run)(const struct sr_dev_inst *sdi);
+		int (*remote_stop)(const struct sr_dev_inst *sdi);
+	} operation;
+	void (*submit_raw_data)(void *data, size_t len,
+				const struct sr_dev_inst *sdi);
+};
+
+struct dev_context {
+	struct slogic_model *model;
+
+	struct sr_channel_group *digital_group;
+
+	struct {
+		uint64_t limit_samplerate;
+		uint64_t limit_samplechannel;
+	};
+
+	struct {
+		uint64_t cur_limit_samples;
+		uint64_t cur_samplerate;
+		uint64_t cur_samplechannel;
+		int64_t cur_pattern_mode_idx;
+	}; // configuration
+
+	struct {
+		GThread *libusb_event_thread;
+		int libusb_event_thread_run;
+
+		enum libusb_speed speed;
+
+		uint64_t samples_need_nbytes;
+		uint64_t samples_got_nbytes;
+
+		uint64_t per_transfer_duration; /* unit: ms */
+		uint64_t per_transfer_nbytes;
+
+		size_t num_transfers_completed;
+		size_t num_transfers_used;
+		struct libusb_transfer *transfers[NUM_MAX_TRANSFERS];
+
+		uint64_t transfers_reached_nbytes; /* real received bytes in all */
+		uint64_t transfers_reached_nbytes_latest; /* real received bytes this transfer */
+		int64_t transfers_reached_time_start;
+		int64_t transfers_reached_time_latest;
+
+		GAsyncQueue *raw_data_queue;
+		uint64_t timeout_count;
+	}; // usb
+
+	int acq_aborted;
+
+	/* Triggers */
+	uint64_t capture_ratio;
+	gboolean trigger_fired;
+	struct soft_trigger_logic *stl;
+
+	double voltage_threshold[2];
+};
+
+SR_PRIV int sipeed_slogic_acquisition_start(const struct sr_dev_inst *sdi);
+SR_PRIV int sipeed_slogic_acquisition_stop(struct sr_dev_inst *sdi);
+
+#endif
-- 
2.52.0

openSUSE Build Service is sponsored by