--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Tilman Sauerbeck <tilman@code-monkey.de>
+ * Copyright (C) 2012 Bert Vermeulen <bert@biot.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 <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include "protocol.h"
+
+#define LOGICSTUDIO16_VID 0x05ff
+#define LOGICSTUDIO16_PID_LACK_FIRMWARE 0xa001
+#define LOGICSTUDIO16_PID_HAVE_FIRMWARE 0xa002
+
+#define USB_INTERFACE 0
+#define USB_CONFIGURATION 0
+#define FX2_FIRMWARE "lecroy-logicstudio16-fx2lp.fw"
+
+#define UNKNOWN_ADDRESS 0xff
+#define MAX_RENUM_DELAY_MS 3000
+
+static const uint32_t devopts[] = {
+ SR_CONF_LOGIC_ANALYZER,
+ SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_TRIGGER_MATCH | SR_CONF_LIST,
+};
+
+static const int32_t trigger_matches[] = {
+ SR_TRIGGER_ZERO,
+ SR_TRIGGER_ONE,
+ SR_TRIGGER_RISING,
+ SR_TRIGGER_FALLING,
+ SR_TRIGGER_EDGE,
+};
+
+static const uint64_t samplerates[] = {
+ SR_HZ(1000),
+ SR_HZ(2500),
+ SR_KHZ(5),
+ SR_KHZ(10),
+ SR_KHZ(25),
+ SR_KHZ(50),
+ SR_KHZ(100),
+ SR_KHZ(250),
+ SR_KHZ(500),
+ SR_KHZ(1000),
+ SR_KHZ(2500),
+ SR_MHZ(5),
+ SR_MHZ(10),
+ SR_MHZ(25),
+ SR_MHZ(50),
+ SR_MHZ(100),
+ SR_MHZ(250),
+ SR_MHZ(500),
+};
+
+SR_PRIV struct sr_dev_driver lecroy_logicstudio_driver_info;
+
+static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static struct sr_dev_inst *create_device(struct sr_dev_driver *di,
+ struct sr_usb_dev_inst *usb, enum sr_dev_inst_status status,
+ int64_t fw_updated)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ char channel_name[8];
+ int i;
+
+ sdi = g_malloc0(sizeof(struct sr_dev_inst));
+ sdi->status = status;
+ sdi->vendor = g_strdup("LeCroy");
+ sdi->model = g_strdup("LogicStudio16");
+ sdi->driver = di;
+ sdi->inst_type = SR_INST_USB;
+ sdi->conn = usb;
+
+ for (i = 0; i < 16; i++) {
+ snprintf(channel_name, sizeof(channel_name), "D%i", i);
+ sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, channel_name);
+ }
+
+ devc = g_malloc0(sizeof(struct dev_context));
+
+ sdi->priv = devc;
+
+ devc->fw_updated = fw_updated;
+ devc->capture_ratio = 50;
+
+ lls_set_samplerate(sdi, SR_MHZ(500));
+
+ return sdi;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+ struct sr_dev_inst *sdi;
+ struct drv_context *drvc;
+ struct sr_usb_dev_inst *usb;
+ struct libusb_device_descriptor des;
+ libusb_device **devlist;
+ GSList *devices;
+ char connection_id[64];
+ size_t i;
+ int r;
+
+ (void)options;
+
+ drvc = di->context;
+ drvc->instances = NULL;
+
+ devices = NULL;
+
+ libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+
+ for (i = 0; devlist[i]; i++) {
+ libusb_get_device_descriptor(devlist[i], &des);
+
+ if (des.idVendor != LOGICSTUDIO16_VID)
+ continue;
+
+ usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+
+ usb = NULL;
+
+ switch (des.idProduct) {
+ case LOGICSTUDIO16_PID_HAVE_FIRMWARE:
+ usb = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
+ libusb_get_device_address(devlist[i]), NULL);
+
+ sdi = create_device(di, usb, SR_ST_INACTIVE, 0);
+ break;
+ case LOGICSTUDIO16_PID_LACK_FIRMWARE:
+ r = ezusb_upload_firmware(drvc->sr_ctx, devlist[i],
+ USB_CONFIGURATION, FX2_FIRMWARE);
+ if (r != SR_OK) {
+ /*
+ * An error message has already been logged by
+ * ezusb_upload_firmware().
+ */
+ continue;
+ }
+
+ /*
+ * Put unknown as the address so that we know we still
+ * need to get the proper address after the device
+ * renumerates.
+ */
+ usb = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
+ UNKNOWN_ADDRESS, NULL);
+
+ sdi = create_device(di, usb, SR_ST_INITIALIZING,
+ g_get_monotonic_time());
+ break;
+ default:
+ break;
+ }
+
+ /* Cannot handle this device? */
+ if (!usb)
+ continue;
+
+ sdi->connection_id = g_strdup(connection_id);
+
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+ }
+
+ libusb_free_device_list(devlist, 1);
+
+ return devices;
+}
+
+static GSList *dev_list(const struct sr_dev_driver *di)
+{
+ return ((struct drv_context *)(di->context))->instances;
+}
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+ return std_dev_clear(di, NULL);
+}
+
+static int open_device(struct sr_dev_inst *sdi)
+{
+ struct drv_context *drvc;
+ struct sr_usb_dev_inst *usb;
+ struct libusb_device_descriptor des;
+ libusb_device **devlist;
+ char connection_id[64];
+ bool is_opened;
+ size_t i;
+ int r;
+
+ if (sdi->status == SR_ST_ACTIVE)
+ return SR_ERR;
+
+ drvc = sdi->driver->context;
+ usb = sdi->conn;
+
+ is_opened = FALSE;
+
+ libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
+
+ for (i = 0; devlist[i]; i++) {
+ libusb_get_device_descriptor(devlist[i], &des);
+
+ if (des.idVendor != LOGICSTUDIO16_VID ||
+ des.idProduct != LOGICSTUDIO16_PID_HAVE_FIRMWARE)
+ continue;
+
+ usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
+
+ /*
+ * Check if this device is the same one that we associated
+ * with this sdi in scan() and bail if it isn't.
+ */
+ if (strcmp(sdi->connection_id, connection_id))
+ continue;
+
+ r = libusb_open(devlist[i], &usb->devhdl);
+
+ if (r) {
+ sr_err("Failed to open device: %s.",
+ libusb_error_name(r));
+ break;
+ }
+
+ /* Fix up address after firmware upload. */
+ if (usb->address == UNKNOWN_ADDRESS)
+ usb->address = libusb_get_device_address(devlist[i]);
+
+ is_opened = TRUE;
+
+ break;
+ }
+
+ libusb_free_device_list(devlist, 1);
+
+ if (!is_opened)
+ return SR_ERR;
+
+ if ((r = libusb_claim_interface(usb->devhdl, USB_INTERFACE))) {
+ sr_err("Failed to claim interface: %s.",
+ libusb_error_name(r));
+ return SR_ERR;
+ }
+
+ sdi->status = SR_ST_ACTIVE;
+
+ return SR_OK;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ int64_t timediff_us, timediff_ms;
+ int ret;
+
+ drvc = sdi->driver->context;
+ devc = sdi->priv;
+
+ if (!drvc) {
+ sr_err("Driver was not initialized.");
+ return SR_ERR;
+ }
+
+ /*
+ * If we didn't need to upload FX2 firmware in scan(), open the device
+ * right away. Otherwise, wait up to MAX_RENUM_DELAY_MS ms for the
+ * FX2 to renumerate.
+ */
+ if (!devc->fw_updated) {
+ ret = open_device(sdi);
+ } else {
+ sr_info("Waiting for device to reset.");
+
+ /* Takes >= 300ms for the FX2 to be gone from the USB bus. */
+ g_usleep(300 * 1000);
+ timediff_ms = 0;
+
+ while (timediff_ms < MAX_RENUM_DELAY_MS) {
+ ret = open_device(sdi);
+
+ if (ret == SR_OK)
+ break;
+
+ g_usleep(100 * 1000);
+
+ timediff_us = g_get_monotonic_time() - devc->fw_updated;
+ timediff_ms = timediff_us / 1000;
+
+ sr_spew("Waited %" PRIi64 "ms.", timediff_ms);
+ }
+
+ if (ret != SR_OK) {
+ sr_err("Device failed to renumerate.");
+ return SR_ERR;
+ }
+
+ sr_info("Device came back after %" PRIi64 "ms.", timediff_ms);
+ }
+
+ if (ret != SR_OK) {
+ sr_err("Unable to open device.");
+ return ret;
+ }
+
+ /*
+ * Only allocate the sample buffer now since it's rather large.
+ * Don't want to allocate it before we know we are going to use it.
+ */
+ devc->fetched_samples = g_malloc(SAMPLE_BUF_SIZE);
+
+ devc->conv8to16 = g_malloc(CONV_8TO16_BUF_SIZE);
+
+ devc->intr_xfer = libusb_alloc_transfer(0);
+ devc->bulk_xfer = libusb_alloc_transfer(0);
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+ struct dev_context *devc;
+
+ usb = sdi->conn;
+ devc = sdi->priv;
+
+ g_free(devc->fetched_samples);
+ devc->fetched_samples = NULL;
+
+ g_free(devc->conv8to16);
+ devc->conv8to16 = NULL;
+
+ if (devc->intr_xfer) {
+ devc->intr_xfer->buffer = NULL; /* Points into devc. */
+ libusb_free_transfer(devc->intr_xfer);
+ devc->intr_xfer = NULL;
+ }
+
+ if (devc->bulk_xfer) {
+ devc->bulk_xfer->buffer = NULL; /* Points into devc. */
+ libusb_free_transfer(devc->bulk_xfer);
+ devc->bulk_xfer = NULL;
+ }
+
+ if (!usb->devhdl)
+ return SR_ERR;
+
+ libusb_release_interface(usb->devhdl, 0);
+
+ libusb_close(usb->devhdl);
+ usb->devhdl = NULL;
+
+ sdi->status = SR_ST_INACTIVE;
+
+ return SR_OK;
+}
+
+static int cleanup(const struct sr_dev_driver *di)
+{
+ struct drv_context *drvc;
+ int ret;
+
+ drvc = di->context;
+
+ ret = dev_clear(di);
+
+ g_free(drvc);
+
+ return ret;
+}
+
+static int config_get(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+
+ devc = sdi->priv;
+
+ switch (key) {
+ case SR_CONF_SAMPLERATE:
+ *data = g_variant_new_uint64(lls_get_samplerate(sdi));
+ break;
+ case SR_CONF_CAPTURE_RATIO:
+ *data = g_variant_new_uint64(devc->capture_ratio);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(uint32_t key, GVariant *data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+ struct dev_context *devc;
+
+ (void)cg;
+
+ if (!sdi)
+ return SR_ERR_ARG;
+
+ devc = sdi->priv;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ switch (key) {
+ case SR_CONF_SAMPLERATE:
+ return lls_set_samplerate(sdi, g_variant_get_uint64(data));
+ case SR_CONF_CAPTURE_RATIO:
+ devc->capture_ratio = g_variant_get_uint64(data);
+ if (devc->capture_ratio > 100)
+ return SR_ERR;
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_list(uint32_t key, GVariant **data,
+ const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
+ GVariantBuilder vb;
+ GVariant *var;
+
+ (void)sdi;
+ (void)cg;
+
+ switch (key) {
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+ devopts, ARRAY_SIZE(devopts),
+ sizeof(uint32_t));
+ break;
+ case SR_CONF_SAMPLERATE:
+ g_variant_builder_init(&vb, G_VARIANT_TYPE("a{sv}"));
+ var = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
+ samplerates, ARRAY_SIZE(samplerates),
+ sizeof(uint64_t));
+ g_variant_builder_add(&vb, "{sv}", "samplerates", var);
+ *data = g_variant_builder_end(&vb);
+ break;
+ case SR_CONF_TRIGGER_MATCH:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ trigger_matches, ARRAY_SIZE(trigger_matches),
+ sizeof(int32_t));
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_commit(const struct sr_dev_inst *sdi)
+{
+ return lls_setup_acquisition(sdi);
+}
+
+static int receive_usb_data(int fd, int revents, void *cb_data)
+{
+ struct drv_context *drvc;
+ struct timeval tv;
+
+ (void)fd;
+ (void)revents;
+
+ drvc = (struct drv_context *) cb_data;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx,
+ &tv, NULL);
+
+ return TRUE;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct drv_context *drvc;
+ int ret;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ drvc = sdi->driver->context;
+
+ if ((ret = lls_start_acquisition(sdi)) < 0)
+ return ret;
+
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ return usb_source_add(sdi->session, drvc->sr_ctx, 100,
+ receive_usb_data, drvc);
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ (void)cb_data;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ return lls_stop_acquisition(sdi);
+}
+
+SR_PRIV struct sr_dev_driver lecroy_logicstudio_driver_info = {
+ .name = "lecroy-logicstudio",
+ .longname = "LeCroy LogicStudio",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = dev_clear,
+ .config_get = config_get,
+ .config_set = config_set,
+ .config_list = config_list,
+ .config_commit = config_commit,
+ .dev_open = dev_open,
+ .dev_close = dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .context = NULL,
+};
--- /dev/null
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2015 Tilman Sauerbeck <tilman@code-monkey.de>
+ *
+ * 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 <string.h>
+#include <assert.h>
+#include "protocol.h"
+
+#define EP_INTR (LIBUSB_ENDPOINT_IN | 1)
+#define EP_BULK (LIBUSB_ENDPOINT_IN | 2)
+#define EP_BITSTREAM (LIBUSB_ENDPOINT_OUT | 6)
+
+#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN)
+#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT)
+
+#define USB_COMMAND_READ_WRITE_REGS 0xb1
+#define USB_COMMAND_WRITE_STATUS_REG 0xb2
+#define USB_COMMAND_START_UPLOAD 0xb3
+#define USB_COMMAND_VERIFY_UPLOAD 0xb4
+
+#define USB_TIMEOUT_MS 100
+
+/* Firmware for acquisition on 8 channels. */
+#define FPGA_FIRMWARE_8 "lecroy-logicstudio16-8.bitstream"
+/* Firmware for acquisition on 16 channels. */
+#define FPGA_FIRMWARE_16 "lecroy-logicstudio16-16.bitstream"
+
+#define FPGA_FIRMWARE_SIZE 464196
+#define FPGA_FIRMWARE_CHUNK_SIZE 2048
+
+#define NUM_TRIGGER_STAGES 2
+#define TRIGGER_CFG_SIZE 45
+
+#define ALIGN2_DOWN(n, p) ((((n) > (p)) ? ((n) - (p)) : (n)) & ~((p) - 1))
+
+#define TRIGGER_OP_A 0x1000
+#define TRIGGER_OP_B 0x2000
+#define TRIGGER_OP_A_OR_B 0x3000
+#define TRIGGER_OP_A_AND_B 0x4000
+#define TRIGGER_OP_A_THEN_B 0x8000
+
+#define REG_ACQUISITION_ID 0x00
+#define REG_SAMPLERATE 0x02
+#define REG_PRETRIG_LO 0x03
+#define REG_PRETRIG_HI 0x04
+#define REG_POSTTRIG_LO 0x05
+#define REG_POSTTRIG_HI 0x06
+#define REG_ARM_TRIGGER 0x07
+#define REG_FETCH_SAMPLES 0x08
+#define REG_UNK1_LO 0x09
+#define REG_UNK1_HI 0x0a
+#define REG_UNK2_LO 0x0b
+#define REG_UNK2_HI 0x0c
+#define REG_UNK3_LO 0x0d
+#define REG_UNK3_HI 0x0e
+#define REG_UNK4_LO 0x0f
+#define REG_UNK4_HI 0x10
+#define REG_UNK5_LO 0x11
+#define REG_UNK5_HI 0x12
+#define REG_UNK6_LO 0x13
+#define REG_UNK6_HI 0x14
+#define REG_UNK0_LO 0x15
+#define REG_UNK0_HI 0x16
+#define REG_TRIGGER_CFG 0x18
+#define REG_TRIGGER_COMBINE_OP 0x1b
+#define REG_SELECT_CHANNELS 0x21
+#define REG_VOLTAGE_THRESH_EXTERNAL 0x22
+#define REG_VOLTAGE_THRESH_LOWER_CHANNELS 0x23
+#define REG_VOLTAGE_THRESH_UPPER_CHANNELS 0x24
+
+struct samplerate_info {
+ /** The samplerate in Hz. */
+ uint64_t samplerate;
+
+ /**
+ * The offset to add to the sample offset for when the trigger fired.
+ *
+ * @note The value stored here only applies to 8 channel mode.
+ * When acquiring 16 channels, subtract another 8 samples.
+ */
+ int8_t trigger_sample_offset;
+
+ uint8_t cfg;
+};
+
+struct trigger_config {
+ uint16_t rising_edges;
+ uint16_t falling_edges;
+ uint16_t any_edges;
+
+ uint16_t ones;
+ uint16_t zeroes;
+};
+
+/** A register and its value. */
+struct regval {
+ uint8_t reg;
+ uint16_t val;
+};
+
+static void handle_fetch_samples_done(struct libusb_transfer *xfer);
+static void recv_bulk_transfer(struct libusb_transfer *xfer);
+
+static const struct samplerate_info samplerates[] = {
+ { SR_GHZ(1), -24, 0x1f },
+ { SR_MHZ(500), -6, 0x00 },
+ { SR_MHZ(250), -4, 0x01 },
+ { SR_MHZ(100), 2, 0x03 },
+ { SR_MHZ(50), 4, 0x04 },
+ { SR_MHZ(25), 8, 0x05 },
+ { SR_MHZ(10), 4, 0x07 },
+ { SR_MHZ(5), 8, 0x08 },
+ { SR_KHZ(2500), 8, 0x09 },
+ { SR_KHZ(1000), 8, 0x0b },
+ { SR_KHZ(500), 8, 0x0c },
+ { SR_KHZ(250), 8, 0x0d },
+ { SR_KHZ(100), 8, 0x0f },
+ { SR_KHZ(50), 8, 0x10 },
+ { SR_KHZ(25), 8, 0x11 },
+ { SR_KHZ(10), 8, 0x13 },
+ { SR_KHZ(5), 8, 0x14 },
+ { SR_HZ(2500), 8, 0x15 },
+ { SR_HZ(1000), 8, 0x17 },
+};
+
+static int read_register(const struct sr_dev_inst *sdi,
+ uint8_t reg, uint16_t *value)
+{
+ struct sr_usb_dev_inst *usb;
+ uint8_t data[2];
+ int r;
+
+ usb = sdi->conn;
+
+ r = libusb_control_transfer(usb->devhdl, CTRL_IN,
+ USB_COMMAND_READ_WRITE_REGS, reg, 5444,
+ data, sizeof(data), USB_TIMEOUT_MS);
+
+ if (r != sizeof(data)) {
+ sr_err("CTRL_IN failed: %i.", r);
+ return SR_ERR;
+ }
+
+ *value = RB16(data);
+
+ return SR_OK;
+}
+
+static int write_registers_sync(const struct sr_dev_inst *sdi,
+ unsigned int wValue, unsigned int wIndex,
+ const struct regval *regs, size_t num_regs)
+{
+ struct sr_usb_dev_inst *usb;
+ uint8_t *buf;
+ size_t i, bufsiz;
+ int r;
+
+ usb = sdi->conn;
+
+ /* Try to avoid overflowing the stack. */
+ if (num_regs > 32)
+ return SR_ERR;
+
+ bufsiz = num_regs * 3;
+ buf = alloca(bufsiz);
+
+ for (i = 0; i < num_regs; i++) {
+ W8(&buf[i * 3 + 0], regs[i].reg);
+ WB16(&buf[i * 3 + 1], regs[i].val);
+ }
+
+ r = libusb_control_transfer(usb->devhdl, CTRL_OUT,
+ USB_COMMAND_READ_WRITE_REGS, wValue, wIndex,
+ buf, bufsiz, USB_TIMEOUT_MS);
+
+ if (r != (int) bufsiz) {
+ sr_err("write_registers_sync(%u/%u) failed.", wValue, wIndex);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int write_registers_async(const struct sr_dev_inst *sdi,
+ unsigned int wValue, unsigned int wIndex,
+ const struct regval *regs, size_t num_regs,
+ libusb_transfer_cb_fn callback)
+{
+ struct sr_usb_dev_inst *usb;
+ struct libusb_transfer *xfer;
+ uint8_t *buf, *xfer_buf;
+ size_t i;
+
+ usb = sdi->conn;
+
+ xfer = libusb_alloc_transfer(0);
+ xfer_buf = g_malloc(LIBUSB_CONTROL_SETUP_SIZE + (num_regs * 3));
+
+ libusb_fill_control_setup(xfer_buf, CTRL_OUT,
+ USB_COMMAND_READ_WRITE_REGS, wValue, wIndex, num_regs * 3);
+
+ buf = xfer_buf + LIBUSB_CONTROL_SETUP_SIZE;
+
+ for (i = 0; i < num_regs; i++) {
+ W8(&buf[i * 3 + 0], regs[i].reg);
+ WB16(&buf[i * 3 + 1], regs[i].val);
+ }
+
+ libusb_fill_control_transfer(xfer, usb->devhdl,
+ xfer_buf, callback, (void *) sdi, USB_TIMEOUT_MS);
+
+ if (libusb_submit_transfer(xfer) < 0) {
+ g_free(xfer->buffer);
+ xfer->buffer = NULL;
+ libusb_free_transfer(xfer);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static void prep_regw(struct regval *regval, uint8_t reg, uint16_t val)
+{
+ regval->reg = reg;
+ regval->val = val;
+}
+
+static void handle_fetch_samples_done(struct libusb_transfer *xfer)
+{
+ const struct sr_dev_inst *sdi;
+ struct sr_usb_dev_inst *usb;
+ struct dev_context *devc;
+
+ sdi = xfer->user_data;
+ usb = sdi->conn;
+ devc = sdi->priv;
+
+ g_free(xfer->buffer);
+ xfer->buffer = NULL;
+
+ libusb_free_transfer(xfer);
+
+ libusb_fill_bulk_transfer(devc->bulk_xfer, usb->devhdl, EP_BULK,
+ devc->fetched_samples, 17 << 10,
+ recv_bulk_transfer, (void *)sdi, USB_TIMEOUT_MS);
+
+ libusb_submit_transfer(devc->bulk_xfer);
+}
+
+static void calc_unk0(uint32_t *a, uint32_t *b)
+{
+ uint32_t t;
+
+ t = 20000 / 4;
+
+ if (a)
+ *a = (t + 63) | 63;
+ if (b)
+ *b = (t + 63) & ~63;
+}
+
+static int fetch_samples_async(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct regval cmd[12];
+ uint32_t unk1, unk2, unk3;
+ int i;
+
+ devc = sdi->priv;
+
+ unk1 = devc->earliest_sample % (devc->num_thousand_samples << 10);
+ unk1 = (unk1 * devc->num_enabled_channel_groups) / 8;
+
+ calc_unk0(&unk2, &unk3);
+
+ i = 0;
+
+ prep_regw(&cmd[i++], REG_UNK1_LO, (unk1 >> 0) & 0xffff);
+ prep_regw(&cmd[i++], REG_UNK1_HI, (unk1 >> 16) & 0xffff);
+
+ prep_regw(&cmd[i++], REG_FETCH_SAMPLES, devc->magic_fetch_samples);
+ prep_regw(&cmd[i++], REG_FETCH_SAMPLES, devc->magic_fetch_samples | 0x02);
+
+ prep_regw(&cmd[i++], REG_UNK1_LO, 0x0000);
+ prep_regw(&cmd[i++], REG_UNK1_HI, 0x0000);
+
+ prep_regw(&cmd[i++], REG_UNK2_LO, (unk2 >> 0) & 0xffff);
+ prep_regw(&cmd[i++], REG_UNK2_HI, (unk2 >> 16) & 0xffff);
+
+ prep_regw(&cmd[i++], REG_UNK3_LO, (unk3 >> 0) & 0xffff);
+ prep_regw(&cmd[i++], REG_UNK3_HI, (unk3 >> 16) & 0xffff);
+
+ devc->magic_fetch_samples = 0x01;
+ prep_regw(&cmd[i++], REG_FETCH_SAMPLES, devc->magic_fetch_samples + 0x01);
+ prep_regw(&cmd[i++], REG_FETCH_SAMPLES, devc->magic_fetch_samples | 0x02);
+
+ return write_registers_async(sdi, 0x12, 5444, cmd, ARRAY_SIZE(cmd),
+ handle_fetch_samples_done);
+}
+
+static int handle_intr_data(const struct sr_dev_inst *sdi, uint8_t *buffer)
+{
+ struct dev_context *devc;
+ gboolean resubmit_intr_xfer;
+ uint64_t time_latest, time_trigger, sample_latest, sample_trigger;
+ uint32_t samplerate_divider;
+
+ resubmit_intr_xfer = TRUE;
+
+ if (!sdi)
+ goto out;
+
+ devc = sdi->priv;
+
+ if (!devc->want_trigger)
+ goto out;
+
+ /* Does this packet refer to our newly programmed trigger yet? */
+ if (RB16(&buffer[0x02]) != devc->acquisition_id)
+ goto out;
+
+ switch (buffer[0x1f]) {
+ case 0x09:
+ /* Storing pre-trigger samples. */
+ break;
+ case 0x0a:
+ /* Trigger armed? */
+ break;
+ case 0x0b:
+ /* Storing post-trigger samples. */
+ break;
+ case 0x04:
+ /* Acquisition complete. */
+ devc->total_received_sample_bytes = 0;
+
+ samplerate_divider = SR_GHZ(1) / devc->samplerate_info->samplerate;
+
+ /*
+ * These timestamps seem to be in units of eight nanoseconds.
+ * The first one refers to the time when the latest sample
+ * was written to the device's sample buffer, and the second
+ * one refers to the time when the trigger fired.
+ *
+ * They are stored as 48 bit integers in the packet and we
+ * shift it to the right by 16 to make up for that.
+ */
+ time_latest = RB64(&buffer[0x6]) >> 16;
+ time_trigger = RB64(&buffer[0xc]) >> 16;
+
+ /* Convert timestamps to sample offsets. */
+ sample_latest = time_latest * 8;
+ sample_latest /= samplerate_divider;
+
+ sample_latest = ALIGN2_DOWN(sample_latest,
+ 8 / devc->num_enabled_channel_groups);
+
+ devc->earliest_sample = sample_latest -
+ (devc->num_thousand_samples * 1000);
+
+ sample_trigger = time_trigger * 8;
+
+ /* Fill the zero bits on the right. */
+ sample_trigger |= RB16(&buffer[0x12]) & 7;
+
+ sample_trigger += devc->samplerate_info->trigger_sample_offset;
+
+ if (devc->num_enabled_channel_groups > 1)
+ sample_trigger -= 8;
+
+ sample_trigger -= 0x18;
+
+ if (samplerate_divider > 1) {
+ /* FIXME: Underflow. */
+ sample_trigger -= samplerate_divider;
+ sample_trigger /= samplerate_divider;
+ }
+
+ /*
+ * Seems the hardware reports one sample too early,
+ * so make up for that.
+ */
+ sample_trigger++;
+
+ devc->trigger_sample = sample_trigger;
+
+ fetch_samples_async(sdi);
+
+ /*
+ * Don't re-submit the interrupt transfer;
+ * we need to get the samples instead.
+ */
+ resubmit_intr_xfer = FALSE;
+
+ break;
+ default:
+ break;
+ }
+
+out:
+ return resubmit_intr_xfer;
+}
+
+static int upload_fpga_bitstream(const struct sr_dev_inst *sdi,
+ const char *firmware_name)
+{
+ struct drv_context *drvc;
+ struct sr_usb_dev_inst *usb;
+ struct sr_resource firmware;
+ uint8_t firmware_chunk[FPGA_FIRMWARE_CHUNK_SIZE];
+ uint8_t upload_succeeded;
+ ssize_t chunk_size;
+ int i, r, ret, actual_length;
+
+ drvc = sdi->driver->context;
+ usb = sdi->conn;
+
+ ret = sr_resource_open(drvc->sr_ctx, &firmware,
+ SR_RESOURCE_FIRMWARE, firmware_name);
+
+ if (ret != SR_OK)
+ return ret;
+
+ ret = SR_ERR;
+
+ if (firmware.size != FPGA_FIRMWARE_SIZE) {
+ sr_err("Invalid FPGA firmware file size: %" PRIu64 " bytes.",
+ firmware.size);
+ goto out;
+ }
+
+ /* Initiate upload. */
+ r = libusb_control_transfer(usb->devhdl, CTRL_OUT,
+ USB_COMMAND_START_UPLOAD, 0x07, 5444,
+ NULL, 0, USB_TIMEOUT_MS);
+
+ if (r != 0) {
+ sr_err("Failed to initiate firmware upload: %s.",
+ libusb_error_name(ret));
+ goto out;
+ }
+
+ for (;;) {
+ chunk_size = sr_resource_read(drvc->sr_ctx, &firmware,
+ firmware_chunk, sizeof(firmware_chunk));
+
+ if (chunk_size < 0)
+ goto out;
+
+ if (chunk_size == 0)
+ break;
+
+ actual_length = chunk_size;
+
+ r = libusb_bulk_transfer(usb->devhdl, EP_BITSTREAM,
+ firmware_chunk, chunk_size, &actual_length, USB_TIMEOUT_MS);
+
+ if (r != 0 || (ssize_t)actual_length != chunk_size) {
+ sr_err("FPGA firmware upload failed.");
+ goto out;
+ }
+ }
+
+ /* Verify upload. */
+ for (i = 0; i < 4; i++) {
+ g_usleep(250000);
+
+ upload_succeeded = 0x00;
+
+ r = libusb_control_transfer(usb->devhdl, CTRL_IN,
+ USB_COMMAND_VERIFY_UPLOAD, 0x07, 5444,
+ &upload_succeeded, sizeof(upload_succeeded),
+ USB_TIMEOUT_MS);
+
+ if (r != sizeof(upload_succeeded)) {
+ sr_err("CTRL_IN failed: %i.", r);
+ return SR_ERR;
+ }
+
+ if (upload_succeeded == 0x01) {
+ ret = SR_OK;
+ break;
+ }
+ }
+
+out:
+ sr_resource_close(drvc->sr_ctx, &firmware);
+
+ return ret;
+}
+
+static int upload_trigger(const struct sr_dev_inst *sdi,
+ uint8_t reg_values[TRIGGER_CFG_SIZE], uint8_t reg_offset)
+{
+ struct regval regs[3 * 5];
+ uint16_t value;
+ int i, j, k;
+
+ for (i = 0; i < TRIGGER_CFG_SIZE; i += 5) {
+ k = 0;
+
+ for (j = 0; j < 5; j++) {
+ value = ((reg_offset + i + j) << 8) | reg_values[i + j];
+
+ prep_regw(®s[k++], REG_TRIGGER_CFG, value);
+ prep_regw(®s[k++], REG_TRIGGER_CFG, value | 0x8000);
+ prep_regw(®s[k++], REG_TRIGGER_CFG, value);
+ }
+
+ if (write_registers_sync(sdi, 0x12, 5444, regs, ARRAY_SIZE(regs))) {
+ sr_err("Failed to upload trigger config.");
+ return SR_ERR;
+ }
+ }
+
+ return SR_OK;
+}
+
+static int program_trigger(const struct sr_dev_inst *sdi,
+ struct trigger_config *stages, int num_filled_stages)
+{
+ struct trigger_config *block;
+ struct regval combine_op;
+ uint8_t buf[TRIGGER_CFG_SIZE];
+ const uint8_t reg_offsets[] = { 0x00, 0x40 };
+ int i;
+
+ for (i = 0; i < NUM_TRIGGER_STAGES; i++) {
+ block = &stages[i];
+
+ memset(buf, 0, sizeof(buf));
+
+ WL16(&buf[0x00], ~(block->rising_edges | block->falling_edges));
+ WL16(&buf[0x05], block->rising_edges | block->falling_edges | block->any_edges);
+
+ if (block->ones | block->zeroes)
+ buf[0x09] = 0x10;
+
+ WL16(&buf[0x0a], block->rising_edges);
+ WL16(&buf[0x0f], block->ones | block->zeroes);
+ buf[0x13] = 0x10;
+ WL16(&buf[0x14], block->ones | 0x8000);
+
+ if (block->ones == 0x01)
+ WL16(&buf[0x19], block->ones << 1);
+ else
+ WL16(&buf[0x19], block->ones | 0x0001);
+
+ /*
+ * The final trigger has some special stuff.
+ * Not sure of the meaning yet.
+ */
+ if (i == NUM_TRIGGER_STAGES - 1) {
+ buf[0x09] = 0x10; /* This is most likely wrong. */
+
+ buf[0x28] = 0xff;
+ buf[0x29] = 0xff;
+ buf[0x2a] = 0xff;
+ buf[0x2b] = 0xff;
+ buf[0x2c] = 0x80;
+ }
+
+ if (upload_trigger(sdi, buf, reg_offsets[i]) < 0)
+ return SR_ERR;
+ }
+
+ /*
+ * If both available stages are used, AND them in the trigger
+ * criteria.
+ *
+ * Once sigrok learns to teach devices about the combination
+ * that the user wants, this seems to be the best default since
+ * edge triggers cannot be AND'ed otherwise
+ * (they are always OR'd within a single stage).
+ */
+ prep_regw(&combine_op, REG_TRIGGER_COMBINE_OP,
+ num_filled_stages > 1 ? TRIGGER_OP_A_AND_B : TRIGGER_OP_A);
+
+ return write_registers_sync(sdi, 0x12, 5444, &combine_op, 1);
+}
+
+static gboolean transform_trigger(struct sr_trigger_stage *stage,
+ struct trigger_config *config)
+{
+ GSList *l;
+ struct sr_trigger_match *match;
+ uint32_t channel_mask;
+ gboolean ret;
+
+ ret = FALSE;
+
+ for (l = stage->matches; l; l = l->next) {
+ match = l->data;
+
+ if (!match)
+ continue;
+
+ /* Ignore disabled channels. */
+ if (!match->channel->enabled)
+ continue;
+
+ channel_mask = 1 << match->channel->index;
+
+ switch (match->match) {
+ case SR_TRIGGER_RISING:
+ config->rising_edges |= channel_mask;
+ break;
+ case SR_TRIGGER_FALLING:
+ config->falling_edges |= channel_mask;
+ break;
+ case SR_TRIGGER_EDGE:
+ config->any_edges |= channel_mask;
+ break;
+ case SR_TRIGGER_ONE:
+ config->ones |= channel_mask;
+ break;
+ case SR_TRIGGER_ZERO:
+ config->zeroes |= channel_mask;
+ break;
+ }
+
+ ret = TRUE;
+ }
+
+ return ret;
+}
+
+static int configure_trigger(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_trigger *trigger;
+ struct sr_trigger_stage *stage;
+ struct sr_trigger_match *match;
+ struct trigger_config blocks[NUM_TRIGGER_STAGES];
+ gboolean stage_has_matches;
+ int num_filled_stages;
+ GSList *l, *ll;
+
+ devc = sdi->priv;
+
+ trigger = sr_session_trigger_get(sdi->session);
+
+ num_filled_stages = 0;
+
+ memset(blocks, 0, sizeof(blocks));
+
+ for (l = trigger ? trigger->stages : NULL; l; l = l->next) {
+ stage = l->data;
+ stage_has_matches = FALSE;
+
+ /* Check if this stage has any interesting matches. */
+ for (ll = stage->matches; ll; ll = ll->next) {
+ match = ll->data;
+
+ if (!match)
+ continue;
+
+ /* Ignore disabled channels. */
+ if (match->channel->enabled) {
+ stage_has_matches = TRUE;
+ break;
+ }
+ }
+
+ if (stage_has_matches == FALSE)
+ continue;
+
+ if (num_filled_stages == NUM_TRIGGER_STAGES)
+ return SR_ERR;
+
+ if (transform_trigger(stage, &blocks[num_filled_stages]))
+ num_filled_stages++;
+ }
+
+ devc->want_trigger = num_filled_stages > 0;
+
+ return program_trigger(sdi, blocks, num_filled_stages);
+}
+
+/** Update the bit mask of enabled channels. */
+SR_PRIV void lls_update_channel_mask(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct sr_channel *channel;
+ GSList *l;
+
+ devc = sdi->priv;
+
+ devc->channel_mask = 0;
+
+ for (l = sdi->channels; l; l = l->next) {
+ channel = l->data;
+ if (channel->enabled)
+ devc->channel_mask |= 1 << channel->index;
+ }
+}
+
+SR_PRIV int lls_set_samplerate(const struct sr_dev_inst *sdi,
+ uint64_t samplerate)
+{
+ struct dev_context *devc;
+ size_t i;
+
+ devc = sdi->priv;
+
+ for (i = 0; i < ARRAY_SIZE(samplerates); i++) {
+ if (samplerates[i].samplerate == samplerate) {
+ devc->samplerate_info = &samplerates[i];
+ return SR_OK;
+ }
+ }
+
+ return SR_ERR;
+}
+
+SR_PRIV uint64_t lls_get_samplerate(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+
+ devc = sdi->priv;
+
+ return devc->samplerate_info->samplerate;
+}
+
+static int read_0f12(const struct sr_dev_inst *sdi, uint64_t *value)
+{
+ uint64_t u64;
+ uint16_t u16;
+ int r, reg;
+
+ u64 = 0;
+
+ /*
+ * Read the 64 bit register spread over 4 16 bit registers.
+ *
+ * Note that these don't seem to be the same registers we're writing
+ * when arming the trigger (ie REG_UNK4 and REG_UNK5).
+ * Seems there's multiple register spaces?
+ */
+ for (reg = 0x0f; reg <= 0x12; reg++) {
+ r = read_register(sdi, reg, &u16);
+ if (r != SR_OK)
+ return r;
+ u64 <<= 16;
+ u64 |= u16;
+ }
+
+ *value = u64;
+
+ return SR_OK;
+}
+
+static int wait_for_dev_to_settle(const struct sr_dev_inst *sdi)
+{
+ uint64_t old_value, new_value;
+ int r, i;
+
+ /* Get the initial value. */
+ r = read_0f12(sdi, &old_value);
+
+ if (r != SR_OK)
+ return r;
+
+ /*
+ * We are looking for two consecutive reads that yield the
+ * same value. Try a couple of times.
+ */
+ for (i = 0; i < 100; i++) {
+ r = read_0f12(sdi, &new_value);
+ if (r != SR_OK)
+ return r;
+
+ if (old_value == new_value)
+ return SR_OK;
+
+ old_value = new_value;
+ }
+
+ return SR_ERR;
+}
+
+SR_PRIV int lls_setup_acquisition(const struct sr_dev_inst *sdi)
+{
+ uint8_t status_reg_value[] = {
+ 0x1, 0x0, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc,
+ 0xd, 0xe, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6
+ };
+ struct regval threshold[3];
+ struct regval channels;
+ struct dev_context *devc;
+ struct sr_usb_dev_inst *usb;
+ gboolean lower_enabled, upper_enabled, upload_bitstream;
+ uint32_t num_thousand_samples, num_enabled_channel_groups;
+ int i, r;
+
+ usb = sdi->conn;
+ devc = sdi->priv;
+
+ prep_regw(&threshold[0], REG_VOLTAGE_THRESH_LOWER_CHANNELS, 0x00c3);
+ prep_regw(&threshold[1], REG_VOLTAGE_THRESH_UPPER_CHANNELS, 0x00c2);
+ prep_regw(&threshold[2], REG_VOLTAGE_THRESH_EXTERNAL, 0x003e);
+
+ lls_update_channel_mask(sdi);
+
+ lower_enabled = (devc->channel_mask & 0x00ff) != 0x00;
+ upper_enabled = (devc->channel_mask & 0xff00) != 0x00;
+
+ num_thousand_samples = 20;
+ num_enabled_channel_groups = 2;
+
+ if (lower_enabled != upper_enabled) {
+ num_thousand_samples <<= 1;
+ num_enabled_channel_groups >>= 1;
+ }
+
+ if (upper_enabled && !lower_enabled)
+ prep_regw(&channels, REG_SELECT_CHANNELS, 0x01);
+ else
+ prep_regw(&channels, REG_SELECT_CHANNELS, 0x00);
+
+ /*
+ * If the number of enabled channel groups changed since
+ * the last acquisition, we need to switch FPGA bitstreams.
+ * This works for the initial bitstream upload because
+ * devc->num_enabled_channel_groups is initialized to zero.
+ */
+ upload_bitstream =
+ devc->num_enabled_channel_groups != num_enabled_channel_groups;
+
+ if (upload_bitstream) {
+ if (lls_stop_acquisition(sdi)) {
+ sr_err("Cannot stop acquisition for FPGA bitstream upload.");
+ return SR_ERR;
+ }
+
+ for (i = 0; i < 3; i++)
+ if (write_registers_sync(sdi, 0x0, 0x0, &threshold[i], 1))
+ return SR_ERR;
+
+ if (num_enabled_channel_groups == 1)
+ r = upload_fpga_bitstream(sdi, FPGA_FIRMWARE_8);
+ else
+ r = upload_fpga_bitstream(sdi, FPGA_FIRMWARE_16);
+
+ if (r != SR_OK) {
+ sr_err("Firmware not accepted by device.");
+ return SR_ERR;
+ }
+
+ r = wait_for_dev_to_settle(sdi);
+
+ if (r != SR_OK) {
+ sr_err("Device did not settle in time.");
+ return SR_ERR;
+ }
+
+ for (i = 0; i < 3; i++)
+ if (write_registers_sync(sdi, 0x12, 5444, &threshold[i], 1))
+ return SR_ERR;
+
+ devc->magic_arm_trigger = 0x00;
+ devc->magic_fetch_samples = 0x00;
+ }
+
+ if (write_registers_sync(sdi, 0x12, 5444, &channels, 1))
+ return SR_ERR;
+
+ if (configure_trigger(sdi) < 0)
+ return SR_ERR;
+
+ if (write_registers_sync(sdi, 0x12, 5444, &threshold[0], 1))
+ return SR_ERR;
+
+ if (write_registers_sync(sdi, 0x12, 5444, &threshold[1], 1))
+ return SR_ERR;
+
+ if (upload_bitstream) {
+ r = libusb_control_transfer(usb->devhdl, CTRL_OUT,
+ USB_COMMAND_WRITE_STATUS_REG, 0x12, 5444,
+ status_reg_value, sizeof(status_reg_value), USB_TIMEOUT_MS);
+
+ if (r != sizeof(status_reg_value)) {
+ sr_err("Failed to write status register: %s.",
+ libusb_error_name(r));
+ return SR_ERR;
+ }
+ }
+
+ devc->num_thousand_samples = num_thousand_samples;
+ devc->num_enabled_channel_groups = num_enabled_channel_groups;
+
+ return SR_OK;
+}
+
+static void recv_intr_transfer(struct libusb_transfer *xfer)
+{
+ const struct sr_dev_inst *sdi;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+
+ sdi = xfer->user_data;
+ drvc = sdi->driver->context;
+ devc = sdi->priv;
+
+ if (devc->abort_acquisition) {
+ packet.type = SR_DF_END;
+ sr_session_send(sdi, &packet);
+ usb_source_remove(sdi->session, drvc->sr_ctx);
+ return;
+ }
+
+ if (xfer->status == LIBUSB_TRANSFER_COMPLETED) {
+ if (xfer->actual_length != INTR_BUF_SIZE)
+ sr_err("Invalid size of interrupt transfer: %u.",
+ xfer->actual_length);
+ else if (handle_intr_data(sdi, xfer->buffer)) {
+ if (libusb_submit_transfer(xfer) < 0)
+ sr_err("Failed to submit interrupt transfer.");
+ }
+ }
+}
+
+static void send_samples(const struct sr_dev_inst *sdi,
+ uint8_t *samples, uint32_t length)
+{
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_logic logic;
+ gboolean lower_enabled, upper_enabled;
+ uint32_t shift, i;
+
+ devc = sdi->priv;
+
+ lower_enabled = (devc->channel_mask & 0x00ff) != 0x00;
+ upper_enabled = (devc->channel_mask & 0xff00) != 0x00;
+
+ logic.unitsize = 2;
+
+ packet.type = SR_DF_LOGIC;
+ packet.payload = &logic;
+
+ if (lower_enabled && upper_enabled) {
+ logic.length = length;
+ logic.data = samples;
+
+ sr_session_send(sdi, &packet);
+ } else {
+ /* Which channel group is enabled? */
+ shift = (lower_enabled) ? 0 : 8;
+
+ while (length >= (CONV_8TO16_BUF_SIZE / 2)) {
+ for (i = 0; i < (CONV_8TO16_BUF_SIZE / 2); i++) {
+ devc->conv8to16[i] = samples[i];
+ devc->conv8to16[i] <<= shift;
+ }
+
+ logic.length = CONV_8TO16_BUF_SIZE;
+ logic.data = devc->conv8to16;
+
+ sr_session_send(sdi, &packet);
+
+ samples += CONV_8TO16_BUF_SIZE / 2;
+ length -= CONV_8TO16_BUF_SIZE / 2;
+ }
+
+ /* Handle the remaining samples. */
+ for (i = 0; i < length; i++) {
+ devc->conv8to16[i] = samples[i];
+ devc->conv8to16[i] <<= shift;
+ }
+
+ logic.length = length * 2;
+ logic.data = devc->conv8to16;
+
+ sr_session_send(sdi, &packet);
+ }
+}
+
+static uint16_t sample_to_byte_offset(struct dev_context *devc, uint64_t o)
+{
+ o %= devc->num_thousand_samples << 10;
+
+ /* We have 8 bit per channel group, so this gets us a byte offset. */
+ return o * devc->num_enabled_channel_groups;
+}
+
+static void recv_bulk_transfer(struct libusb_transfer *xfer)
+{
+ const struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct drv_context *drvc;
+ struct sr_datafeed_packet packet;
+ uint32_t bytes_left, length;
+ uint16_t read_offset, trigger_offset;
+
+ sdi = xfer->user_data;
+
+ if (!sdi)
+ return;
+
+ drvc = sdi->driver->context;
+ devc = sdi->priv;
+
+ devc->total_received_sample_bytes += xfer->actual_length;
+
+ if (devc->total_received_sample_bytes < SAMPLE_BUF_SIZE) {
+ xfer->buffer = devc->fetched_samples
+ + devc->total_received_sample_bytes;
+
+ xfer->length = MIN(16 << 10,
+ SAMPLE_BUF_SIZE - devc->total_received_sample_bytes);
+
+ libusb_submit_transfer(xfer);
+ return;
+ }
+
+ usb_source_remove(sdi->session, drvc->sr_ctx);
+
+ read_offset = sample_to_byte_offset(devc, devc->earliest_sample);
+ trigger_offset = sample_to_byte_offset(devc, devc->trigger_sample);
+
+ /*
+ * The last few bytes seem to contain garbage data,
+ * so ignore them.
+ */
+ bytes_left = (SAMPLE_BUF_SIZE >> 10) * 1000;
+
+ sr_spew("Start reading at offset 0x%04hx.", read_offset);
+ sr_spew("Trigger offset 0x%04hx.", trigger_offset);
+
+ if (trigger_offset < read_offset) {
+ length = MIN(bytes_left, SAMPLE_BUF_SIZE - read_offset);
+
+ sr_spew("Sending %u pre-trigger bytes starting at 0x%04hx.",
+ length, read_offset);
+
+ send_samples(sdi, &devc->fetched_samples[read_offset], length);
+
+ bytes_left -= length;
+ read_offset = 0;
+ }
+
+ {
+ length = MIN(bytes_left, (uint32_t)(trigger_offset - read_offset));
+
+ sr_spew("Sending %u pre-trigger bytes starting at 0x%04hx.",
+ length, read_offset);
+
+ send_samples(sdi, &devc->fetched_samples[read_offset], length);
+
+ bytes_left -= length;
+
+ read_offset += length;
+ read_offset %= SAMPLE_BUF_SIZE;
+ }
+
+ /* Here comes the trigger. */
+ packet.type = SR_DF_TRIGGER;
+ packet.payload = NULL;
+
+ sr_session_send(sdi, &packet);
+
+ /* Send post-trigger samples. */
+ while (bytes_left > 0) {
+ length = MIN(bytes_left, SAMPLE_BUF_SIZE - read_offset);
+
+ sr_spew("Sending %u post-trigger bytes starting at 0x%04hx.",
+ length, read_offset);
+
+ send_samples(sdi, &devc->fetched_samples[read_offset], length);
+
+ bytes_left -= length;
+
+ read_offset += length;
+ read_offset %= SAMPLE_BUF_SIZE;
+ }
+
+ packet.type = SR_DF_END;
+ sr_session_send(sdi, &packet);
+}
+
+static uint32_t transform_sample_count(struct dev_context *devc,
+ uint32_t samples)
+{
+ uint32_t d = 8 / devc->num_enabled_channel_groups;
+
+ return (samples + 0x1c + d + d - 1) / d;
+}
+
+SR_PRIV int lls_start_acquisition(const struct sr_dev_inst *sdi)
+{
+ struct sr_usb_dev_inst *usb;
+ struct dev_context *devc;
+ struct regval cmd[17];
+ uint32_t unk0, total_samples, pre_trigger_samples, post_trigger_samples;
+ uint32_t pre_trigger_tr, post_trigger_tr;
+ int i;
+
+ usb = sdi->conn;
+ devc = sdi->priv;
+
+ devc->abort_acquisition = FALSE;
+
+ libusb_fill_interrupt_transfer(devc->intr_xfer, usb->devhdl, EP_INTR,
+ devc->intr_buf, INTR_BUF_SIZE,
+ recv_intr_transfer, (void *) sdi, USB_TIMEOUT_MS);
+
+ libusb_submit_transfer(devc->intr_xfer);
+
+ if (devc->want_trigger == FALSE)
+ return SR_OK;
+
+ calc_unk0(&unk0, NULL);
+
+ total_samples = devc->num_thousand_samples * 1000;
+
+ pre_trigger_samples = total_samples * devc->capture_ratio / 100;
+ post_trigger_samples = total_samples - pre_trigger_samples;
+
+ pre_trigger_tr = transform_sample_count(devc, pre_trigger_samples);
+ post_trigger_tr = transform_sample_count(devc, post_trigger_samples);
+
+ i = 0;
+
+ prep_regw(&cmd[i++], REG_ARM_TRIGGER, devc->magic_arm_trigger);
+ prep_regw(&cmd[i++], REG_ARM_TRIGGER, devc->magic_arm_trigger | 0x02);
+
+ prep_regw(&cmd[i++], REG_UNK6_LO, 0x0000);
+ prep_regw(&cmd[i++], REG_UNK6_HI, 0x0000);
+
+ prep_regw(&cmd[i++], REG_UNK0_LO, (unk0 >> 0) & 0xffff);
+ prep_regw(&cmd[i++], REG_UNK0_HI, (unk0 >> 16) & 0xffff);
+
+ prep_regw(&cmd[i++], REG_UNK4_LO, 0x0000);
+ prep_regw(&cmd[i++], REG_UNK4_HI, 0x0000);
+
+ prep_regw(&cmd[i++], REG_UNK5_LO, 0x0000);
+ prep_regw(&cmd[i++], REG_UNK5_HI, 0x0000);
+
+ prep_regw(&cmd[i++], REG_ACQUISITION_ID, ++devc->acquisition_id);
+ prep_regw(&cmd[i++], REG_SAMPLERATE, devc->samplerate_info->cfg);
+
+ prep_regw(&cmd[i++], REG_PRETRIG_LO, (pre_trigger_tr >> 0) & 0xffff);
+ prep_regw(&cmd[i++], REG_PRETRIG_HI, (pre_trigger_tr >> 16) & 0xffff);
+
+ prep_regw(&cmd[i++], REG_POSTTRIG_LO, (post_trigger_tr >> 0) & 0xffff);
+ prep_regw(&cmd[i++], REG_POSTTRIG_HI, (post_trigger_tr >> 16) & 0xffff);
+
+ devc->magic_arm_trigger = 0x0c;
+ prep_regw(&cmd[i++], REG_ARM_TRIGGER, devc->magic_arm_trigger | 0x01);
+
+ return write_registers_sync(sdi, 0x12, 5444, cmd, ARRAY_SIZE(cmd));
+}
+
+SR_PRIV int lls_stop_acquisition(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct regval cmd[2];
+ int i;
+
+ devc = sdi->priv;
+
+ devc->abort_acquisition = TRUE;
+
+ i = 0;
+
+ prep_regw(&cmd[i++], REG_ARM_TRIGGER, devc->magic_arm_trigger);
+ prep_regw(&cmd[i++], REG_ARM_TRIGGER, devc->magic_arm_trigger | 0x02);
+
+ assert(i == ARRAY_SIZE(cmd));
+
+ return write_registers_sync(sdi, 0x12, 5444, cmd, ARRAY_SIZE(cmd));
+}