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