From: Tilman Sauerbeck Date: Thu, 8 Oct 2015 18:13:53 +0000 (+0200) Subject: lecroy-logicstudio: Initial driver implementation. X-Git-Tag: libsigrok-0.4.0~81 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=c7b17bcba3751c7849229089cce8773bb4355be7;p=libsigrok.git lecroy-logicstudio: Initial driver implementation. This supports both 8 and 16 channel modes with samplerates up to 500 MHz. Voltage thresholds are currently fixed at 1.58V. --- diff --git a/Makefile.am b/Makefile.am index 4ed41bf7..0b12ea43 100644 --- a/Makefile.am +++ b/Makefile.am @@ -335,6 +335,12 @@ libsigrok_la_SOURCES += \ src/hardware/lascar-el-usb/protocol.c \ src/hardware/lascar-el-usb/api.c endif +if HW_LECROY_LOGICSTUDIO +libsigrok_la_SOURCES += \ + src/hardware/lecroy-logicstudio/protocol.h \ + src/hardware/lecroy-logicstudio/protocol.c \ + src/hardware/lecroy-logicstudio/api.c +endif if HW_MANSON_HCS_3XXX libsigrok_la_SOURCES += \ src/hardware/manson-hcs-3xxx/protocol.h \ diff --git a/configure.ac b/configure.ac index ac7259d3..37172f28 100644 --- a/configure.ac +++ b/configure.ac @@ -241,6 +241,7 @@ SR_DRIVER([Kecheng KC-330B], [kecheng-kc-330b], [libusb]) SR_DRIVER([KERN scale], [kern-scale], [libserialport]) SR_DRIVER([Korad KAxxxxP], [korad-kaxxxxp], [libserialport]) SR_DRIVER([Lascar EL-USB], [lascar-el-usb], [libusb]) +SR_DRIVER([LeCroy LogicStudio], [lecroy-logicstudio], [libusb]) SR_DRIVER([Manson HCS-3xxx], [manson-hcs-3xxx], [libserialport]) SR_DRIVER([maynuo-m97], [maynuo-m97]) SR_DRIVER([MIC 985xx], [mic-985xx], [libserialport]) diff --git a/src/drivers.c b/src/drivers.c index f4e5d1c3..702228d9 100644 --- a/src/drivers.c +++ b/src/drivers.c @@ -108,6 +108,9 @@ extern SR_PRIV struct sr_dev_driver korad_kaxxxxp_driver_info; #ifdef HAVE_HW_LASCAR_EL_USB extern SR_PRIV struct sr_dev_driver lascar_el_usb_driver_info; #endif +#ifdef HAVE_HW_LECROY_LOGICSTUDIO +extern SR_PRIV struct sr_dev_driver lecroy_logicstudio_driver_info; +#endif #ifdef HAVE_HW_LINK_MSO19 extern SR_PRIV struct sr_dev_driver link_mso19_driver_info; #endif @@ -269,6 +272,9 @@ SR_PRIV struct sr_dev_driver **drivers_lists[] = { #ifdef HAVE_HW_LASCAR_EL_USB (DRVS) {&lascar_el_usb_driver_info, NULL}, #endif +#ifdef HAVE_HW_LECROY_LOGICSTUDIO + (DRVS) {&lecroy_logicstudio_driver_info, NULL}, +#endif #ifdef HAVE_HW_LINK_MSO19 (DRVS) {&link_mso19_driver_info, NULL}, #endif diff --git a/src/hardware/lecroy-logicstudio/api.c b/src/hardware/lecroy-logicstudio/api.c new file mode 100644 index 00000000..53e7b017 --- /dev/null +++ b/src/hardware/lecroy-logicstudio/api.c @@ -0,0 +1,559 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2015 Tilman Sauerbeck + * Copyright (C) 2012 Bert Vermeulen + * + * 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 . + */ + +#include +#include +#include +#include +#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, +}; diff --git a/src/hardware/lecroy-logicstudio/protocol.c b/src/hardware/lecroy-logicstudio/protocol.c new file mode 100644 index 00000000..ef390ebc --- /dev/null +++ b/src/hardware/lecroy-logicstudio/protocol.c @@ -0,0 +1,1189 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2015 Tilman Sauerbeck + * + * 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 . + */ + +#include +#include +#include +#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)); +} diff --git a/src/hardware/lecroy-logicstudio/protocol.h b/src/hardware/lecroy-logicstudio/protocol.h new file mode 100644 index 00000000..1ac3d0fc --- /dev/null +++ b/src/hardware/lecroy-logicstudio/protocol.h @@ -0,0 +1,107 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2015 Tilman Sauerbeck + * + * 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 . + */ + +#ifndef LIBSIGROK_HARDWARE_LECROY_LOGICSTUDIO_PROTOCOL_H +#define LIBSIGROK_HARDWARE_LECROY_LOGICSTUDIO_PROTOCOL_H + +#include +#include +#include +#include "libsigrok-internal.h" + +#define LOG_PREFIX "lecroy-logicstudio" + +#define SAMPLE_BUF_SIZE 40960u +#define CONV_8TO16_BUF_SIZE 8192 +#define INTR_BUF_SIZE 32 + +struct samplerate_info; + +/** Private, per-device-instance driver context. */ +struct dev_context { + struct libusb_transfer *intr_xfer; + struct libusb_transfer *bulk_xfer; + + const struct samplerate_info *samplerate_info; + + /** + * When the device is opened, this will point at a buffer + * of SAMPLE_BUF_SIZE bytes. + */ + uint8_t *fetched_samples; + + /** + * Used to convert 8 bit samples (8 channels) to 16 bit samples + * (16 channels), thus only used in 8 channel mode. + * Holds CONV_8TO16_BUF_SIZE bytes. + */ + uint16_t *conv8to16; + + int64_t fw_updated; /* Time of last FX2 firmware upload. */ + + /** The pre-trigger capture ratio in percent. */ + uint64_t capture_ratio; + + uint64_t earliest_sample; + uint64_t trigger_sample; + + /** The number of eight-channel groups enabled (either 1 or 2). */ + uint32_t num_enabled_channel_groups; + + /** + * The number of samples to acquire (in thousands). + * This is not customizable, but depending on the number + * of enabled channel groups. + */ + uint32_t num_thousand_samples; + + uint32_t total_received_sample_bytes; + + /** Mask of enabled channels. */ + uint16_t channel_mask; + + uint16_t acquisition_id; + + gboolean want_trigger; + gboolean abort_acquisition; + + /** + * These two magic values are required in order to fix a sample + * buffer corruption. Before the first acquisition is run, they + * need to be set to 0. + */ + uint8_t magic_arm_trigger; + uint8_t magic_fetch_samples; + + /** + * Buffer for interrupt transfers (acquisition state notifications). + */ + uint8_t intr_buf[INTR_BUF_SIZE]; +}; + +SR_PRIV void lls_update_channel_mask(const struct sr_dev_inst *sdi); + +SR_PRIV int lls_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate); +SR_PRIV uint64_t lls_get_samplerate(const struct sr_dev_inst *sdi); + +SR_PRIV int lls_setup_acquisition(const struct sr_dev_inst *sdi); +SR_PRIV int lls_start_acquisition(const struct sr_dev_inst *sdi); +SR_PRIV int lls_stop_acquisition(const struct sr_dev_inst *sdi); + +#endif