From: Uwe Hermann Date: Tue, 20 Jun 2017 21:07:27 +0000 (+0200) Subject: dreamsourcelab-dslogic: Naming and other consistency fixes. X-Git-Url: http://sigrok.org/gitweb/?a=commitdiff_plain;h=44b46d703612aa7dfc406043cf1b82bdae19179b;hp=f74485b60805eef01e05ee90bab3ba5bcadbae26;p=libsigrok.git dreamsourcelab-dslogic: Naming and other consistency fixes. --- diff --git a/Makefile.am b/Makefile.am index 58e954d6..c9b89938 100644 --- a/Makefile.am +++ b/Makefile.am @@ -272,11 +272,11 @@ src_libdrivers_la_SOURCES += \ src/hardware/demo/protocol.c \ src/hardware/demo/api.c endif -if HW_DSLOGIC +if HW_DREAMSOURCELAB_DSLOGIC src_libdrivers_la_SOURCES += \ - src/hardware/dslogic/protocol.h \ - src/hardware/dslogic/protocol.c \ - src/hardware/dslogic/api.c + src/hardware/dreamsourcelab-dslogic/protocol.h \ + src/hardware/dreamsourcelab-dslogic/protocol.c \ + src/hardware/dreamsourcelab-dslogic/api.c endif if HW_FLUKE_DMM src_libdrivers_la_SOURCES += \ diff --git a/configure.ac b/configure.ac index 2a24d414..ce81eac0 100644 --- a/configure.ac +++ b/configure.ac @@ -234,7 +234,7 @@ SR_DRIVER([ChronoVu LA], [chronovu-la], [libusb libftdi]) SR_DRIVER([Colead SLM], [colead-slm], [libserialport]) SR_DRIVER([Conrad DIGI 35 CPU], [conrad-digi-35-cpu], [libserialport]) SR_DRIVER([demo], [demo]) -SR_DRIVER([DreamSourceLabs], [DSLogic], [libusb]) +SR_DRIVER([DreamSourceLab DSLogic], [dreamsourcelab-dslogic], [libusb]) SR_DRIVER([Fluke DMM], [fluke-dmm], [libserialport]) SR_DRIVER([FTDI LA], [ftdi-la], [libusb libftdi]) SR_DRIVER([fx2lafw], [fx2lafw], [libusb]) diff --git a/src/hardware/dreamsourcelab-dslogic/api.c b/src/hardware/dreamsourcelab-dslogic/api.c new file mode 100644 index 00000000..bc6979fe --- /dev/null +++ b/src/hardware/dreamsourcelab-dslogic/api.c @@ -0,0 +1,659 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2013 Bert Vermeulen + * Copyright (C) 2012 Joel Holdsworth + * + * 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 "protocol.h" + +static const struct dslogic_profile supported_device[] = { + /* DreamSourceLab DSLogic */ + { 0x2a0e, 0x0001, "DreamSourceLab", "DSLogic", NULL, + "dreamsourcelab-dslogic-fx2.fw", + 0, "DreamSourceLab", "DSLogic", 256 * 1024 * 1024}, + /* DreamSourceLab DSCope */ + { 0x2a0e, 0x0002, "DreamSourceLab", "DSCope", NULL, + "dreamsourcelab-dscope-fx2.fw", + 0, "DreamSourceLab", "DSCope", 256 * 1024 * 1024}, + /* DreamSourceLab DSLogic Pro */ + { 0x2a0e, 0x0003, "DreamSourceLab", "DSLogic Pro", NULL, + "dreamsourcelab-dslogic-pro-fx2.fw", + 0, "DreamSourceLab", "DSLogic", 256 * 1024 * 1024}, + /* DreamSourceLab DSLogic Plus */ + { 0x2a0e, 0x0020, "DreamSourceLab", "DSLogic Plus", NULL, + "dreamsourcelab-dslogic-plus-fx2.fw", + 0, "DreamSourceLab", "DSLogic", 256 * 1024 * 1024}, + /* DreamSourceLab DSLogic Basic */ + { 0x2a0e, 0x0021, "DreamSourceLab", "DSLogic Basic", NULL, + "dreamsourcelab-dslogic-basic-fx2.fw", + 0, "DreamSourceLab", "DSLogic", 256 * 1024}, + + ALL_ZERO +}; + +static const uint32_t drvopts[] = { + SR_CONF_LOGIC_ANALYZER, +}; + +static const uint32_t scanopts[] = { + SR_CONF_CONN, +}; + +static const uint32_t devopts[] = { + SR_CONF_CONTINUOUS | SR_CONF_SET | SR_CONF_GET, + SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, + SR_CONF_VOLTAGE_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_CONN | SR_CONF_GET, + SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_TRIGGER_MATCH | SR_CONF_LIST, + SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET, + SR_CONF_EXTERNAL_CLOCK | SR_CONF_GET | SR_CONF_SET, + SR_CONF_CLOCK_EDGE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, +}; + +/* Names assigned to available edge slope choices. */ +static const char *const signal_edge_names[] = { + [DS_EDGE_RISING] = "rising", + [DS_EDGE_FALLING] = "falling", +}; + +static const struct { + gdouble low; + gdouble high; +} dslogic_voltage_thresholds[] = { + { 0.7, 1.4 }, + { 1.4, 3.6 }, +}; + +static const uint64_t samplerates[] = { + SR_KHZ(10), + SR_KHZ(20), + SR_KHZ(50), + SR_KHZ(100), + SR_KHZ(200), + SR_KHZ(500), + SR_MHZ(1), + SR_MHZ(2), + SR_MHZ(5), + SR_MHZ(10), + SR_MHZ(20), + SR_MHZ(25), + SR_MHZ(50), + SR_MHZ(100), + SR_MHZ(200), + SR_MHZ(400), +}; + +static gboolean is_plausible(const struct libusb_device_descriptor *des) +{ + int i; + + for (i = 0; supported_device[i].vid; i++) { + if (des->idVendor != supported_device[i].vid) + continue; + if (des->idProduct == supported_device[i].pid) + return TRUE; + } + + return FALSE; +} + +static GSList *scan(struct sr_dev_driver *di, GSList *options) +{ + struct drv_context *drvc; + struct dev_context *devc; + struct sr_dev_inst *sdi; + struct sr_usb_dev_inst *usb; + struct sr_channel *ch; + struct sr_channel_group *cg; + struct sr_config *src; + const struct dslogic_profile *prof; + GSList *l, *devices, *conn_devices; + gboolean has_firmware; + struct libusb_device_descriptor des; + libusb_device **devlist; + struct libusb_device_handle *hdl; + int ret, i, j; + const char *conn; + char manufacturer[64], product[64], serial_num[64], connection_id[64]; + char channel_name[16]; + + drvc = di->context; + + conn = NULL; + for (l = options; l; l = l->next) { + src = l->data; + switch (src->key) { + case SR_CONF_CONN: + conn = g_variant_get_string(src->data, NULL); + break; + } + } + if (conn) + conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn); + else + conn_devices = NULL; + + /* Find all DSLogic compatible devices and upload firmware to them. */ + devices = NULL; + libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); + for (i = 0; devlist[i]; i++) { + if (conn) { + usb = NULL; + for (l = conn_devices; l; l = l->next) { + usb = l->data; + if (usb->bus == libusb_get_bus_number(devlist[i]) + && usb->address == libusb_get_device_address(devlist[i])) + break; + } + if (!l) + /* This device matched none of the ones that + * matched the conn specification. */ + continue; + } + + libusb_get_device_descriptor(devlist[i], &des); + + if (!is_plausible(&des)) + continue; + + if ((ret = libusb_open(devlist[i], &hdl)) < 0) { + sr_warn("Failed to open potential device with " + "VID:PID %04x:%04x: %s.", des.idVendor, + des.idProduct, libusb_error_name(ret)); + continue; + } + + if (des.iManufacturer == 0) { + manufacturer[0] = '\0'; + } else if ((ret = libusb_get_string_descriptor_ascii(hdl, + des.iManufacturer, (unsigned char *) manufacturer, + sizeof(manufacturer))) < 0) { + sr_warn("Failed to get manufacturer string descriptor: %s.", + libusb_error_name(ret)); + continue; + } + + if (des.iProduct == 0) { + product[0] = '\0'; + } else if ((ret = libusb_get_string_descriptor_ascii(hdl, + des.iProduct, (unsigned char *) product, + sizeof(product))) < 0) { + sr_warn("Failed to get product string descriptor: %s.", + libusb_error_name(ret)); + continue; + } + + if (des.iSerialNumber == 0) { + serial_num[0] = '\0'; + } else if ((ret = libusb_get_string_descriptor_ascii(hdl, + des.iSerialNumber, (unsigned char *) serial_num, + sizeof(serial_num))) < 0) { + sr_warn("Failed to get serial number string descriptor: %s.", + libusb_error_name(ret)); + continue; + } + + usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)); + + libusb_close(hdl); + + prof = NULL; + for (j = 0; supported_device[j].vid; j++) { + if (des.idVendor == supported_device[j].vid && + des.idProduct == supported_device[j].pid && + (!strcmp(manufacturer, supported_device[j].usb_manufacturer)) && + (!strcmp(product, "USB-based Instrument") || + !strcmp(product, supported_device[j].usb_product))) { + prof = &supported_device[j]; + break; + } + } + + /* Skip if the device was not found. */ + if (!prof) + continue; + + sdi = g_malloc0(sizeof(struct sr_dev_inst)); + sdi->status = SR_ST_INITIALIZING; + sdi->vendor = g_strdup(prof->vendor); + sdi->model = g_strdup(prof->model); + sdi->version = g_strdup(prof->model_version); + sdi->serial_num = g_strdup(serial_num); + sdi->connection_id = g_strdup(connection_id); + + /* Logic channels, all in one channel group. */ + cg = g_malloc0(sizeof(struct sr_channel_group)); + cg->name = g_strdup("Logic"); + for (j = 0; j < 16; j++) { + sprintf(channel_name, "%d", j); + ch = sr_channel_new(sdi, j, SR_CHANNEL_LOGIC, + TRUE, channel_name); + cg->channels = g_slist_append(cg->channels, ch); + } + sdi->channel_groups = g_slist_append(NULL, cg); + + devc = dslogic_dev_new(); + devc->profile = prof; + sdi->priv = devc; + devices = g_slist_append(devices, sdi); + + devc->samplerates = samplerates; + devc->num_samplerates = ARRAY_SIZE(samplerates); + has_firmware = usb_match_manuf_prod(devlist[i], "DreamSourceLab", "USB-based Instrument"); + + if (has_firmware) { + /* Already has the firmware, so fix the new address. */ + sr_dbg("Found a DSLogic device."); + sdi->status = SR_ST_INACTIVE; + sdi->inst_type = SR_INST_USB; + sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), + libusb_get_device_address(devlist[i]), NULL); + } else { + if (ezusb_upload_firmware(drvc->sr_ctx, devlist[i], + USB_CONFIGURATION, prof->firmware) == SR_OK) + /* Store when this device's FW was updated. */ + devc->fw_updated = g_get_monotonic_time(); + else + sr_err("Firmware upload failed for " + "device %d.%d (logical).", + libusb_get_bus_number(devlist[i]), + libusb_get_device_address(devlist[i])); + sdi->inst_type = SR_INST_USB; + sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), + 0xff, NULL); + } + } + libusb_free_device_list(devlist, 1); + g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free); + + return std_scan_complete(di, devices); +} + +static void clear_dev_context(void *priv) +{ + struct dev_context *devc; + + devc = priv; + g_free(devc); +} + +static int dev_clear(const struct sr_dev_driver *di) +{ + return std_dev_clear(di, clear_dev_context); +} + +static int dev_open(struct sr_dev_inst *sdi) +{ + struct sr_dev_driver *di = sdi->driver; + struct sr_usb_dev_inst *usb; + struct dev_context *devc; + int ret; + int64_t timediff_us, timediff_ms; + + devc = sdi->priv; + usb = sdi->conn; + + /* + * If the firmware was recently uploaded, wait up to MAX_RENUM_DELAY_MS + * milliseconds for the FX2 to renumerate. + */ + ret = SR_ERR; + if (devc->fw_updated > 0) { + 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) { + if ((ret = dslogic_dev_open(sdi, di)) == 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); + } else { + sr_info("Firmware upload was not needed."); + ret = dslogic_dev_open(sdi, di); + } + + if (ret != SR_OK) { + sr_err("Unable to open device."); + return SR_ERR; + } + + ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE); + if (ret != 0) { + 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; + } + + + if ((ret = dslogic_fpga_firmware_upload(sdi)) != SR_OK) + return ret; + + if (devc->cur_samplerate == 0) { + /* Samplerate hasn't been set; default to the slowest one. */ + devc->cur_samplerate = devc->samplerates[0]; + } + + if (devc->cur_threshold == 0.0) + devc->cur_threshold = 1.5; + + return SR_OK; +} + +static int dev_close(struct sr_dev_inst *sdi) +{ + struct sr_usb_dev_inst *usb; + + usb = sdi->conn; + + if (!usb->devhdl) + return SR_ERR; + + sr_info("Closing device on %d.%d (logical) / %s (physical) interface %d.", + usb->bus, usb->address, sdi->connection_id, USB_INTERFACE); + libusb_release_interface(usb->devhdl, USB_INTERFACE); + libusb_close(usb->devhdl); + usb->devhdl = NULL; + sdi->status = SR_ST_INACTIVE; + + return SR_OK; +} + +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; + struct sr_usb_dev_inst *usb; + GVariant *range[2]; + unsigned int i, voltage_range; + char str[128]; + + (void)cg; + + if (!sdi) + return SR_ERR_ARG; + + devc = sdi->priv; + + switch (key) { + case SR_CONF_CONN: + if (!sdi->conn) + return SR_ERR_ARG; + usb = sdi->conn; + if (usb->address == 255) + /* Device still needs to re-enumerate after firmware + * upload, so we don't know its (future) address. */ + return SR_ERR; + snprintf(str, 128, "%d.%d", usb->bus, usb->address); + *data = g_variant_new_string(str); + break; + case SR_CONF_VOLTAGE_THRESHOLD: + if (!strcmp(devc->profile->model, "DSLogic")) { + voltage_range = 0; + + for (i = 0; i < ARRAY_SIZE(dslogic_voltage_thresholds); i++) + if (dslogic_voltage_thresholds[i].low == + devc->cur_threshold) { + voltage_range = i; + break; + } + + range[0] = g_variant_new_double( + dslogic_voltage_thresholds[voltage_range].low); + range[1] = g_variant_new_double( + dslogic_voltage_thresholds[voltage_range].high); + } else { + range[0] = g_variant_new_double(devc->cur_threshold); + range[1] = g_variant_new_double(devc->cur_threshold); + } + *data = g_variant_new_tuple(range, 2); + break; + case SR_CONF_LIMIT_SAMPLES: + *data = g_variant_new_uint64(devc->limit_samples); + break; + case SR_CONF_SAMPLERATE: + *data = g_variant_new_uint64(devc->cur_samplerate); + break; + case SR_CONF_CAPTURE_RATIO: + *data = g_variant_new_uint64(devc->capture_ratio); + break; + case SR_CONF_EXTERNAL_CLOCK: + *data = g_variant_new_boolean(devc->external_clock); + break; + case SR_CONF_CONTINUOUS: + *data = g_variant_new_boolean(devc->continuous_mode); + break; + case SR_CONF_CLOCK_EDGE: + i = devc->clock_edge; + if (i >= ARRAY_SIZE(signal_edge_names)) + return SR_ERR_BUG; + *data = g_variant_new_string(signal_edge_names[0]); + break; + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +/* + * Helper for mapping a string-typed configuration value to an index + * within a table of possible values. + */ +static int lookup_index(GVariant *value, const char *const *table, int len) +{ + const char *entry; + int i; + + entry = g_variant_get_string(value, NULL); + if (!entry) + return -1; + + /* Linear search is fine for very small tables. */ + for (i = 0; i < len; i++) { + if (strcmp(entry, table[i]) == 0) + return i; + } + + return -1; +} + +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; + uint64_t arg; + int i, ret; + gdouble low, high; + + (void)cg; + + if (!sdi) + return SR_ERR_ARG; + + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR; + + devc = sdi->priv; + + ret = SR_OK; + + switch (key) { + case SR_CONF_SAMPLERATE: + arg = g_variant_get_uint64(data); + for (i = 0; i < devc->num_samplerates; i++) { + if (devc->samplerates[i] == arg) { + devc->cur_samplerate = arg; + break; + } + } + if (i == devc->num_samplerates) + ret = SR_ERR_ARG; + break; + case SR_CONF_LIMIT_SAMPLES: + devc->limit_samples = g_variant_get_uint64(data); + break; + case SR_CONF_CAPTURE_RATIO: + devc->capture_ratio = g_variant_get_uint64(data); + ret = (devc->capture_ratio > 100) ? SR_ERR : SR_OK; + break; + case SR_CONF_VOLTAGE_THRESHOLD: + g_variant_get(data, "(dd)", &low, &high); + if (!strcmp(devc->profile->model, "DSLogic")) { + for (i = 0; (unsigned int)i < + ARRAY_SIZE(dslogic_voltage_thresholds); i++) { + if (fabs(dslogic_voltage_thresholds[i].low - low) < 0.1 && + fabs(dslogic_voltage_thresholds[i].high - high) < 0.1) { + devc->cur_threshold = + dslogic_voltage_thresholds[i].low; + break; + } + } + ret = dslogic_fpga_firmware_upload(sdi); + } else { + ret = dslogic_set_voltage_threshold(sdi, (low + high) / 2.0); + } + break; + case SR_CONF_EXTERNAL_CLOCK: + devc->external_clock = g_variant_get_boolean(data); + break; + case SR_CONF_CONTINUOUS: + devc->continuous_mode = g_variant_get_boolean(data); + break; + case SR_CONF_CLOCK_EDGE: + i = lookup_index(data, signal_edge_names, + ARRAY_SIZE(signal_edge_names)); + if (i < 0) + return SR_ERR_ARG; + devc->clock_edge = i; + 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) +{ + struct dev_context *devc = NULL; + GVariant *gvar, *range[2]; + GVariantBuilder gvb; + unsigned int i; + double v; + + (void)cg; + + switch (key) { + case SR_CONF_SCAN_OPTIONS: + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t)); + break; + case SR_CONF_DEVICE_OPTIONS: + if (!sdi) { + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t)); + } else { + devc = sdi->priv; + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + devopts, ARRAY_SIZE(devopts), sizeof(uint32_t)); + } + break; + case SR_CONF_VOLTAGE_THRESHOLD: + if (sdi->priv) + devc = sdi->priv; + g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY); + if (devc && !strcmp(devc->profile->model, "DSLogic")) { + for (i = 0; i < ARRAY_SIZE(dslogic_voltage_thresholds); i++) { + range[0] = g_variant_new_double(dslogic_voltage_thresholds[i].low); + range[1] = g_variant_new_double(dslogic_voltage_thresholds[i].high); + gvar = g_variant_new_tuple(range, 2); + g_variant_builder_add_value(&gvb, gvar); + } + } else { + for (v = 0.0; v <= 5.0; v += 0.1) { + range[0] = g_variant_new_double(v); + range[1] = g_variant_new_double(v); + gvar = g_variant_new_tuple(range, 2); + g_variant_builder_add_value(&gvb, gvar); + } + } + *data = g_variant_builder_end(&gvb); + break; + case SR_CONF_SAMPLERATE: + devc = sdi->priv; + g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}")); + gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), devc->samplerates, + devc->num_samplerates, sizeof(uint64_t)); + g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar); + *data = g_variant_builder_end(&gvb); + break; + case SR_CONF_CLOCK_EDGE: + *data = g_variant_new_strv(signal_edge_names, + ARRAY_SIZE(signal_edge_names)); + break; + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +static struct sr_dev_driver dreamsourcelab_dslogic_driver_info = { + .name = "dreamsourcelab-dslogic", + .longname = "DreamSourceLab DSLogic", + .api_version = 1, + .init = std_init, + .cleanup = std_cleanup, + .scan = scan, + .dev_list = std_dev_list, + .dev_clear = 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 = dslogic_acquisition_start, + .dev_acquisition_stop = dslogic_acquisition_stop, + .context = NULL, +}; +SR_REGISTER_DEV_DRIVER(dreamsourcelab_dslogic_driver_info); diff --git a/src/hardware/dreamsourcelab-dslogic/protocol.c b/src/hardware/dreamsourcelab-dslogic/protocol.c new file mode 100644 index 00000000..cf6272b6 --- /dev/null +++ b/src/hardware/dreamsourcelab-dslogic/protocol.c @@ -0,0 +1,1113 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2013 Bert Vermeulen + * Copyright (C) 2012 Joel Holdsworth + * + * 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 +#include "protocol.h" + +#define DS_CMD_GET_FW_VERSION 0xb0 +#define DS_CMD_GET_REVID_VERSION 0xb1 +#define DS_CMD_START 0xb2 +#define DS_CMD_CONFIG 0xb3 +#define DS_CMD_SETTING 0xb4 +#define DS_CMD_CONTROL 0xb5 +#define DS_CMD_STATUS 0xb6 +#define DS_CMD_STATUS_INFO 0xb7 +#define DS_CMD_WR_REG 0xb8 +#define DS_CMD_WR_NVM 0xb9 +#define DS_CMD_RD_NVM 0xba +#define DS_CMD_RD_NVM_PRE 0xbb +#define DS_CMD_GET_HW_INFO 0xbc + +#define DS_START_FLAGS_STOP (1 << 7) +#define DS_START_FLAGS_CLK_48MHZ (1 << 6) +#define DS_START_FLAGS_SAMPLE_WIDE (1 << 5) +#define DS_START_FLAGS_MODE_LA (1 << 4) + +#define DS_ADDR_COMB 0x68 +#define DS_ADDR_EEWP 0x70 +#define DS_ADDR_VTH 0x78 + +#define DS_MAX_LOGIC_DEPTH SR_MHZ(16) +#define DS_MAX_LOGIC_SAMPLERATE SR_MHZ(100) +#define DS_MAX_TRIG_PERCENT 90 + +#define DS_MODE_TRIG_EN (1 << 0) +#define DS_MODE_CLK_TYPE (1 << 1) +#define DS_MODE_CLK_EDGE (1 << 2) +#define DS_MODE_RLE_MODE (1 << 3) +#define DS_MODE_DSO_MODE (1 << 4) +#define DS_MODE_HALF_MODE (1 << 5) +#define DS_MODE_QUAR_MODE (1 << 6) +#define DS_MODE_ANALOG_MODE (1 << 7) +#define DS_MODE_FILTER (1 << 8) +#define DS_MODE_INSTANT (1 << 9) +#define DS_MODE_STRIG_MODE (1 << 11) +#define DS_MODE_STREAM_MODE (1 << 12) +#define DS_MODE_LPB_TEST (1 << 13) +#define DS_MODE_EXT_TEST (1 << 14) +#define DS_MODE_INT_TEST (1 << 15) + +#define DSLOGIC_ATOMIC_SAMPLES (sizeof(uint64_t) * 8) +#define DSLOGIC_ATOMIC_BYTES sizeof(uint64_t) + +/* + * The FPGA is configured with TLV tuples. Length is specified as the + * number of 16-bit words. + */ +#define _DS_CFG(variable, wordcnt) ((variable << 8) | wordcnt) +#define DS_CFG_START 0xf5a5f5a5 +#define DS_CFG_MODE _DS_CFG(0, 1) +#define DS_CFG_DIVIDER _DS_CFG(1, 2) +#define DS_CFG_COUNT _DS_CFG(3, 2) +#define DS_CFG_TRIG_POS _DS_CFG(5, 2) +#define DS_CFG_TRIG_GLB _DS_CFG(7, 1) +#define DS_CFG_CH_EN _DS_CFG(8, 1) +#define DS_CFG_TRIG _DS_CFG(64, 160) +#define DS_CFG_END 0xfa5afa5a + +#pragma pack(push, 1) + +struct version_info { + uint8_t major; + uint8_t minor; +}; + +struct cmd_start_acquisition { + uint8_t flags; + uint8_t sample_delay_h; + uint8_t sample_delay_l; +}; + +struct dslogic_fpga_config { + uint32_t sync; + + uint16_t mode_header; + uint16_t mode; + uint16_t divider_header; + uint32_t divider; + uint16_t count_header; + uint32_t count; + uint16_t trig_pos_header; + uint32_t trig_pos; + uint16_t trig_glb_header; + uint16_t trig_glb; + uint16_t ch_en_header; + uint16_t ch_en; + + uint16_t trig_header; + uint16_t trig_mask0[NUM_TRIGGER_STAGES]; + uint16_t trig_mask1[NUM_TRIGGER_STAGES]; + uint16_t trig_value0[NUM_TRIGGER_STAGES]; + uint16_t trig_value1[NUM_TRIGGER_STAGES]; + uint16_t trig_edge0[NUM_TRIGGER_STAGES]; + uint16_t trig_edge1[NUM_TRIGGER_STAGES]; + uint16_t trig_logic0[NUM_TRIGGER_STAGES]; + uint16_t trig_logic1[NUM_TRIGGER_STAGES]; + uint32_t trig_count[NUM_TRIGGER_STAGES]; + + uint32_t end_sync; +}; + +#pragma pack(pop) + +/* + * This should be larger than the FPGA bitstream image so that it'll get + * uploaded in one big operation. There seem to be issues when uploading + * it in chunks. + */ +#define FW_BUFSIZE (1024 * 1024) + +#define FPGA_UPLOAD_DELAY (10 * 1000) + +#define USB_TIMEOUT (3 * 1000) + +static int command_get_fw_version(libusb_device_handle *devhdl, + struct version_info *vi) +{ + int ret; + + ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_ENDPOINT_IN, DS_CMD_GET_FW_VERSION, 0x0000, 0x0000, + (unsigned char *)vi, sizeof(struct version_info), USB_TIMEOUT); + + if (ret < 0) { + sr_err("Unable to get version info: %s.", + libusb_error_name(ret)); + return SR_ERR; + } + + return SR_OK; +} + +static int command_get_revid_version(struct sr_dev_inst *sdi, uint8_t *revid) +{ + struct sr_usb_dev_inst *usb = sdi->conn; + libusb_device_handle *devhdl = usb->devhdl; + int ret; + + ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_ENDPOINT_IN, DS_CMD_GET_REVID_VERSION, 0x0000, 0x0000, + revid, 1, USB_TIMEOUT); + + if (ret < 0) { + sr_err("Unable to get REVID: %s.", libusb_error_name(ret)); + return SR_ERR; + } + + return SR_OK; +} + +static int command_start_acquisition(const struct sr_dev_inst *sdi) +{ + struct sr_usb_dev_inst *usb; + struct dslogic_mode mode; + int ret; + + mode.flags = DS_START_FLAGS_MODE_LA | DS_START_FLAGS_SAMPLE_WIDE; + mode.sample_delay_h = mode.sample_delay_l = 0; + + usb = sdi->conn; + ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_ENDPOINT_OUT, DS_CMD_START, 0x0000, 0x0000, + (unsigned char *)&mode, sizeof(mode), USB_TIMEOUT); + if (ret < 0) { + sr_err("Failed to send start command: %s.", libusb_error_name(ret)); + return SR_ERR; + } + + return SR_OK; +} + +static int command_stop_acquisition(const struct sr_dev_inst *sdi) +{ + struct sr_usb_dev_inst *usb; + struct dslogic_mode mode; + int ret; + + mode.flags = DS_START_FLAGS_STOP; + mode.sample_delay_h = mode.sample_delay_l = 0; + + usb = sdi->conn; + ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_ENDPOINT_OUT, DS_CMD_START, 0x0000, 0x0000, + (unsigned char *)&mode, sizeof(struct dslogic_mode), USB_TIMEOUT); + if (ret < 0) { + sr_err("Failed to send stop command: %s.", libusb_error_name(ret)); + return SR_ERR; + } + + return SR_OK; +} + +SR_PRIV int dslogic_fpga_firmware_upload(const struct sr_dev_inst *sdi) +{ + const char *name = NULL; + uint64_t sum; + struct sr_resource bitstream; + struct drv_context *drvc; + struct dev_context *devc; + struct sr_usb_dev_inst *usb; + unsigned char *buf; + ssize_t chunksize; + int transferred; + int result, ret; + const uint8_t cmd[3] = {0, 0, 0}; + + drvc = sdi->driver->context; + devc = sdi->priv; + usb = sdi->conn; + + if (!strcmp(devc->profile->model, "DSLogic")) { + if (devc->cur_threshold < 1.40) + name = DSLOGIC_FPGA_FIRMWARE_3V3; + else + name = DSLOGIC_FPGA_FIRMWARE_5V; + } else if (!strcmp(devc->profile->model, "DSLogic Pro")){ + name = DSLOGIC_PRO_FPGA_FIRMWARE; + } else if (!strcmp(devc->profile->model, "DSLogic Plus")){ + name = DSLOGIC_PLUS_FPGA_FIRMWARE; + } else if (!strcmp(devc->profile->model, "DSLogic Basic")){ + name = DSLOGIC_BASIC_FPGA_FIRMWARE; + } else if (!strcmp(devc->profile->model, "DSCope")) { + name = DSCOPE_FPGA_FIRMWARE; + } else { + sr_err("Failed to select FPGA firmware."); + return SR_ERR; + } + + sr_dbg("Uploading FPGA firmware '%s'.", name); + + result = sr_resource_open(drvc->sr_ctx, &bitstream, + SR_RESOURCE_FIRMWARE, name); + if (result != SR_OK) + return result; + + /* Tell the device firmware is coming. */ + if ((ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_ENDPOINT_OUT, DS_CMD_CONFIG, 0x0000, 0x0000, + (unsigned char *)&cmd, sizeof(cmd), USB_TIMEOUT)) < 0) { + sr_err("Failed to upload FPGA firmware: %s.", libusb_error_name(ret)); + sr_resource_close(drvc->sr_ctx, &bitstream); + return SR_ERR; + } + + /* Give the FX2 time to get ready for FPGA firmware upload. */ + g_usleep(FPGA_UPLOAD_DELAY); + + buf = g_malloc(FW_BUFSIZE); + sum = 0; + result = SR_OK; + while (1) { + chunksize = sr_resource_read(drvc->sr_ctx, &bitstream, + buf, FW_BUFSIZE); + if (chunksize < 0) + result = SR_ERR; + if (chunksize <= 0) + break; + + if ((ret = libusb_bulk_transfer(usb->devhdl, 2 | LIBUSB_ENDPOINT_OUT, + buf, chunksize, &transferred, USB_TIMEOUT)) < 0) { + sr_err("Unable to configure FPGA firmware: %s.", + libusb_error_name(ret)); + result = SR_ERR; + break; + } + sum += transferred; + sr_spew("Uploaded %" PRIu64 "/%" PRIu64 " bytes.", + sum, bitstream.size); + + if (transferred != chunksize) { + sr_err("Short transfer while uploading FPGA firmware."); + result = SR_ERR; + break; + } + } + g_free(buf); + sr_resource_close(drvc->sr_ctx, &bitstream); + + if (result == SR_OK) + sr_dbg("FPGA firmware upload done."); + + return result; +} + +static unsigned int enabled_channel_count(const struct sr_dev_inst *sdi) +{ + unsigned int count = 0; + for (const GSList *l = sdi->channels; l; l = l->next) { + const struct sr_channel *const probe = (struct sr_channel *)l->data; + if (probe->enabled) + count++; + } + return count; +} + +static uint16_t enabled_channel_mask(const struct sr_dev_inst *sdi) +{ + unsigned int mask = 0; + for (const GSList *l = sdi->channels; l; l = l->next) { + const struct sr_channel *const probe = (struct sr_channel *)l->data; + if (probe->enabled) + mask |= 1 << probe->index; + } + return mask; +} + +/* + * Get the session trigger and configure the FPGA structure + * accordingly. + */ +static void set_trigger(const struct sr_dev_inst *sdi, + struct dslogic_fpga_config *cfg) +{ + struct sr_trigger *trigger; + struct sr_trigger_stage *stage; + struct sr_trigger_match *match; + struct dev_context *devc; + const GSList *l, *m; + const unsigned int num_enabled_channels = enabled_channel_count(sdi); + int num_trigger_stages = 0; + + int channelbit, i = 0; + uint32_t trigger_point; + + devc = sdi->priv; + + cfg->ch_en = enabled_channel_mask(sdi); + + cfg->trig_mask0[0] = 0xffff; + cfg->trig_mask1[0] = 0xffff; + + cfg->trig_value0[0] = 0; + cfg->trig_value1[0] = 0; + + cfg->trig_edge0[0] = 0; + cfg->trig_edge1[0] = 0; + + cfg->trig_logic0[0] = 2; + cfg->trig_logic1[0] = 2; + + cfg->trig_count[0] = 0; + + cfg->trig_glb = num_enabled_channels << 4; + + for (i = 1; i < NUM_TRIGGER_STAGES; i++) { + cfg->trig_mask0[i] = 0xffff; + cfg->trig_mask1[i] = 0xffff; + cfg->trig_value0[i] = 0; + cfg->trig_value1[i] = 0; + cfg->trig_edge0[i] = 0; + cfg->trig_edge1[i] = 0; + cfg->trig_logic0[i] = 2; + cfg->trig_logic1[i] = 2; + cfg->trig_count[i] = 0; + } + + trigger_point = (devc->capture_ratio * devc->limit_samples) / 100; + if (trigger_point < DSLOGIC_ATOMIC_SAMPLES) + trigger_point = DSLOGIC_ATOMIC_SAMPLES; + const uint32_t mem_depth = devc->profile->mem_depth; + const uint32_t max_trigger_point = devc->continuous_mode ? ((mem_depth * 10) / 100) : + ((mem_depth * DS_MAX_TRIG_PERCENT) / 100); + if (trigger_point > max_trigger_point) + trigger_point = max_trigger_point; + cfg->trig_pos = trigger_point & ~(DSLOGIC_ATOMIC_SAMPLES - 1); + + if (!(trigger = sr_session_trigger_get(sdi->session))) { + sr_dbg("No session trigger found"); + return; + } + + for (l = trigger->stages; l; l = l->next) { + stage = l->data; + num_trigger_stages++; + for (m = stage->matches; m; m = m->next) { + match = m->data; + if (!match->channel->enabled) + /* Ignore disabled channels with a trigger. */ + continue; + channelbit = 1 << (match->channel->index); + /* Simple trigger support (event). */ + if (match->match == SR_TRIGGER_ONE) { + cfg->trig_mask0[0] &= ~channelbit; + cfg->trig_mask1[0] &= ~channelbit; + cfg->trig_value0[0] |= channelbit; + cfg->trig_value1[0] |= channelbit; + } else if (match->match == SR_TRIGGER_ZERO) { + cfg->trig_mask0[0] &= ~channelbit; + cfg->trig_mask1[0] &= ~channelbit; + } else if (match->match == SR_TRIGGER_FALLING) { + cfg->trig_mask0[0] &= ~channelbit; + cfg->trig_mask1[0] &= ~channelbit; + cfg->trig_edge0[0] |= channelbit; + cfg->trig_edge1[0] |= channelbit; + } else if (match->match == SR_TRIGGER_RISING) { + cfg->trig_mask0[0] &= ~channelbit; + cfg->trig_mask1[0] &= ~channelbit; + cfg->trig_value0[0] |= channelbit; + cfg->trig_value1[0] |= channelbit; + cfg->trig_edge0[0] |= channelbit; + cfg->trig_edge1[0] |= channelbit; + } else if (match->match == SR_TRIGGER_EDGE) { + cfg->trig_edge0[0] |= channelbit; + cfg->trig_edge1[0] |= channelbit; + } + } + } + + cfg->trig_glb |= num_trigger_stages; +} + +static int fpga_configure(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + struct sr_usb_dev_inst *usb; + uint8_t c[3]; + struct dslogic_fpga_config cfg; + uint16_t v16; + uint32_t v32; + int transferred, len, ret; + + sr_dbg("Configuring FPGA."); + + usb = sdi->conn; + devc = sdi->priv; + + WL32(&cfg.sync, DS_CFG_START); + WL16(&cfg.mode_header, DS_CFG_MODE); + WL16(&cfg.divider_header, DS_CFG_DIVIDER); + WL16(&cfg.count_header, DS_CFG_COUNT); + WL16(&cfg.trig_pos_header, DS_CFG_TRIG_POS); + WL16(&cfg.trig_glb_header, DS_CFG_TRIG_GLB); + WL16(&cfg.ch_en_header, DS_CFG_CH_EN); + WL16(&cfg.trig_header, DS_CFG_TRIG); + WL32(&cfg.end_sync, DS_CFG_END); + + /* Pass in the length of a fixed-size struct. Really. */ + len = sizeof(struct dslogic_fpga_config) / 2; + c[0] = len & 0xff; + c[1] = (len >> 8) & 0xff; + c[2] = (len >> 16) & 0xff; + + ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_ENDPOINT_OUT, DS_CMD_SETTING, 0x0000, 0x0000, + c, sizeof(c), USB_TIMEOUT); + if (ret < 0) { + sr_err("Failed to send FPGA configure command: %s.", + libusb_error_name(ret)); + return SR_ERR; + } + + v16 = 0x0000; + + if (devc->mode == DS_OP_INTERNAL_TEST) + v16 = DS_MODE_INT_TEST; + else if (devc->mode == DS_OP_EXTERNAL_TEST) + v16 = DS_MODE_EXT_TEST; + else if (devc->mode == DS_OP_LOOPBACK_TEST) + v16 = DS_MODE_LPB_TEST; + + if (devc->cur_samplerate == DS_MAX_LOGIC_SAMPLERATE * 2) + v16 |= DS_MODE_HALF_MODE; + else if (devc->cur_samplerate == DS_MAX_LOGIC_SAMPLERATE * 4) + v16 |= DS_MODE_QUAR_MODE; + + if (devc->continuous_mode) + v16 |= DS_MODE_STREAM_MODE; + if (devc->external_clock) { + v16 |= DS_MODE_CLK_TYPE; + if (devc->clock_edge == DS_EDGE_FALLING) + v16 |= DS_MODE_CLK_EDGE; + } + if (devc->limit_samples > DS_MAX_LOGIC_DEPTH * + ceil(devc->cur_samplerate * 1.0 / DS_MAX_LOGIC_SAMPLERATE) + && !devc->continuous_mode) { + /* Enable RLE for long captures. + * Without this, captured data present errors. + */ + v16 |= DS_MODE_RLE_MODE; + } + + WL16(&cfg.mode, v16); + v32 = ceil(DS_MAX_LOGIC_SAMPLERATE * 1.0 / devc->cur_samplerate); + WL32(&cfg.divider, v32); + + /* Number of 16-sample units. */ + WL32(&cfg.count, devc->limit_samples / 16); + + set_trigger(sdi, &cfg); + + len = sizeof(struct dslogic_fpga_config); + ret = libusb_bulk_transfer(usb->devhdl, 2 | LIBUSB_ENDPOINT_OUT, + (unsigned char *)&cfg, len, &transferred, USB_TIMEOUT); + if (ret < 0 || transferred != len) { + sr_err("Failed to send FPGA configuration: %s.", libusb_error_name(ret)); + return SR_ERR; + } + + return SR_OK; +} + +SR_PRIV int dslogic_set_voltage_threshold(const struct sr_dev_inst *sdi, double threshold) +{ + int ret; + struct dev_context *const devc = sdi->priv; + const struct sr_usb_dev_inst *const usb = sdi->conn; + const uint8_t value = (threshold / 5.0) * 255; + const uint16_t cmd = value | (DS_ADDR_VTH << 8); + + /* Send the control command. */ + ret = libusb_control_transfer(usb->devhdl, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, + DS_CMD_WR_REG, 0x0000, 0x0000, + (unsigned char *)&cmd, sizeof(cmd), 3000); + if (ret < 0) { + sr_err("Unable to set voltage-threshold register: %s.", + libusb_error_name(ret)); + return SR_ERR; + } + + devc->cur_threshold = threshold; + + return SR_OK; +} + +SR_PRIV int dslogic_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di) +{ + libusb_device **devlist; + struct sr_usb_dev_inst *usb; + struct libusb_device_descriptor des; + struct dev_context *devc; + struct drv_context *drvc; + struct version_info vi; + int ret, i, device_count; + uint8_t revid; + char connection_id[64]; + + drvc = di->context; + devc = sdi->priv; + usb = sdi->conn; + + if (sdi->status == SR_ST_ACTIVE) + /* Device is already in use. */ + return SR_ERR; + + device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); + if (device_count < 0) { + sr_err("Failed to get device list: %s.", + libusb_error_name(device_count)); + return SR_ERR; + } + + for (i = 0; i < device_count; i++) { + libusb_get_device_descriptor(devlist[i], &des); + + if (des.idVendor != devc->profile->vid + || des.idProduct != devc->profile->pid) + continue; + + if ((sdi->status == SR_ST_INITIALIZING) || + (sdi->status == SR_ST_INACTIVE)) { + /* + * Check device by its physical USB bus/port address. + */ + usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)); + if (strcmp(sdi->connection_id, connection_id)) + /* This is not the one. */ + continue; + } + + if (!(ret = libusb_open(devlist[i], &usb->devhdl))) { + if (usb->address == 0xff) + /* + * First time we touch this device after FW + * upload, so we don't know the address yet. + */ + usb->address = libusb_get_device_address(devlist[i]); + } else { + sr_err("Failed to open device: %s.", + libusb_error_name(ret)); + break; + } + + if (libusb_has_capability(LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER)) { + if (libusb_kernel_driver_active(usb->devhdl, USB_INTERFACE) == 1) { + if ((ret = libusb_detach_kernel_driver(usb->devhdl, USB_INTERFACE)) < 0) { + sr_err("Failed to detach kernel driver: %s.", + libusb_error_name(ret)); + return SR_ERR; + } + } + } + + ret = command_get_fw_version(usb->devhdl, &vi); + if (ret != SR_OK) { + sr_err("Failed to get firmware version."); + break; + } + + ret = command_get_revid_version(sdi, &revid); + if (ret != SR_OK) { + sr_err("Failed to get REVID."); + break; + } + + /* + * Changes in major version mean incompatible/API changes, so + * bail out if we encounter an incompatible version. + * Different minor versions are OK, they should be compatible. + */ + if (vi.major != DSLOGIC_REQUIRED_VERSION_MAJOR) { + sr_err("Expected firmware version %d.x, " + "got %d.%d.", DSLOGIC_REQUIRED_VERSION_MAJOR, + vi.major, vi.minor); + break; + } + + sdi->status = SR_ST_ACTIVE; + sr_info("Opened device on %d.%d (logical) / %s (physical), " + "interface %d, firmware %d.%d.", + usb->bus, usb->address, connection_id, + USB_INTERFACE, vi.major, vi.minor); + + sr_info("Detected REVID=%d, it's a Cypress CY7C68013%s.", + revid, (revid != 1) ? " (FX2)" : "A (FX2LP)"); + + break; + } + libusb_free_device_list(devlist, 1); + + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR; + + return SR_OK; +} + +SR_PRIV struct dev_context *dslogic_dev_new(void) +{ + struct dev_context *devc; + + devc = g_malloc0(sizeof(struct dev_context)); + devc->profile = NULL; + devc->fw_updated = 0; + devc->cur_samplerate = 0; + devc->limit_samples = 0; + devc->capture_ratio = 0; + devc->continuous_mode = FALSE; + devc->clock_edge = DS_EDGE_RISING; + + return devc; +} + +static void abort_acquisition(struct dev_context *devc) +{ + int i; + + devc->acq_aborted = TRUE; + + for (i = devc->num_transfers - 1; i >= 0; i--) { + if (devc->transfers[i]) + libusb_cancel_transfer(devc->transfers[i]); + } +} + +static void finish_acquisition(struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + + devc = sdi->priv; + + std_session_send_df_end(sdi); + + usb_source_remove(sdi->session, devc->ctx); + + devc->num_transfers = 0; + g_free(devc->transfers); + g_free(devc->deinterleave_buffer); +} + +static void free_transfer(struct libusb_transfer *transfer) +{ + struct sr_dev_inst *sdi; + struct dev_context *devc; + unsigned int i; + + sdi = transfer->user_data; + devc = sdi->priv; + + g_free(transfer->buffer); + transfer->buffer = NULL; + libusb_free_transfer(transfer); + + for (i = 0; i < devc->num_transfers; i++) { + if (devc->transfers[i] == transfer) { + devc->transfers[i] = NULL; + break; + } + } + + devc->submitted_transfers--; + if (devc->submitted_transfers == 0) + finish_acquisition(sdi); +} + +static void resubmit_transfer(struct libusb_transfer *transfer) +{ + int ret; + + if ((ret = libusb_submit_transfer(transfer)) == LIBUSB_SUCCESS) + return; + + sr_err("%s: %s", __func__, libusb_error_name(ret)); + free_transfer(transfer); + +} + +static void deinterleave_buffer(const uint8_t *src, size_t length, + uint16_t *dst_ptr, size_t channel_count, uint16_t channel_mask) +{ + uint16_t sample; + + for (const uint64_t *src_ptr = (uint64_t*)src; + src_ptr < (uint64_t*)(src + length); + src_ptr += channel_count) { + for (int bit = 0; bit != 64; bit++) { + const uint64_t *word_ptr = src_ptr; + sample = 0; + for (size_t channel = 0; channel != channel_count; + channel++) { + if ((channel_mask & (1 << channel)) && + (*word_ptr++ & (1ULL << bit))) + sample |= 1 << channel; + } + *dst_ptr++ = sample; + } + } +} + +static void send_data(struct sr_dev_inst *sdi, + uint16_t *data, size_t sample_count) +{ + const struct sr_datafeed_logic logic = { + .length = sample_count * sizeof(uint16_t), + .unitsize = sizeof(uint16_t), + .data = data + }; + + const struct sr_datafeed_packet packet = { + .type = SR_DF_LOGIC, + .payload = &logic + }; + + sr_session_send(sdi, &packet); +} + +static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer) +{ + struct sr_dev_inst *const sdi = transfer->user_data; + struct dev_context *const devc = sdi->priv; + const size_t channel_count = enabled_channel_count(sdi); + const uint16_t channel_mask = enabled_channel_mask(sdi); + const unsigned int cur_sample_count = DSLOGIC_ATOMIC_SAMPLES * + transfer->actual_length / + (DSLOGIC_ATOMIC_BYTES * channel_count); + + gboolean packet_has_error = FALSE; + struct sr_datafeed_packet packet; + unsigned int num_samples; + int trigger_offset; + + /* + * If acquisition has already ended, just free any queued up + * transfer that come in. + */ + if (devc->acq_aborted) { + free_transfer(transfer); + return; + } + + sr_dbg("receive_transfer(): status %s received %d bytes.", + libusb_error_name(transfer->status), transfer->actual_length); + + /* Save incoming transfer before reusing the transfer struct. */ + + switch (transfer->status) { + case LIBUSB_TRANSFER_NO_DEVICE: + abort_acquisition(devc); + free_transfer(transfer); + return; + case LIBUSB_TRANSFER_COMPLETED: + case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though. */ + break; + default: + packet_has_error = TRUE; + break; + } + + if (transfer->actual_length == 0 || packet_has_error) { + devc->empty_transfer_count++; + if (devc->empty_transfer_count > MAX_EMPTY_TRANSFERS) { + /* + * The FX2 gave up. End the acquisition, the frontend + * will work out that the samplecount is short. + */ + abort_acquisition(devc); + free_transfer(transfer); + } else { + resubmit_transfer(transfer); + } + return; + } else { + devc->empty_transfer_count = 0; + } + + if (!devc->limit_samples || devc->sent_samples < devc->limit_samples) { + if (devc->limit_samples && devc->sent_samples + cur_sample_count > devc->limit_samples) + num_samples = devc->limit_samples - devc->sent_samples; + else + num_samples = cur_sample_count; + + /** + * The DSLogic emits sample data as sequences of 64-bit sample words + * in a round-robin i.e. 64-bits from channel 0, 64-bits from channel 1 + * etc. for each of the enabled channels, then looping back to the + * channel. + * + * Because sigrok's internal representation is bit-interleaved channels + * we must recast the data. + * + * Hopefully in future it will be possible to pass the data on as-is. + */ + assert(transfer->actual_length % (DSLOGIC_ATOMIC_BYTES * channel_count) == 0); + deinterleave_buffer(transfer->buffer, transfer->actual_length, + devc->deinterleave_buffer, channel_count, channel_mask); + + /* Send the incoming transfer to the session bus. */ + if (devc->trigger_pos > devc->sent_samples + && devc->trigger_pos <= devc->sent_samples + num_samples) { + /* DSLogic trigger in this block. Send trigger position. */ + trigger_offset = devc->trigger_pos - devc->sent_samples; + /* Pre-trigger samples. */ + send_data(sdi, devc->deinterleave_buffer, trigger_offset); + devc->sent_samples += trigger_offset; + /* Trigger position. */ + devc->trigger_pos = 0; + packet.type = SR_DF_TRIGGER; + packet.payload = NULL; + sr_session_send(sdi, &packet); + /* Post trigger samples. */ + num_samples -= trigger_offset; + send_data(sdi, devc->deinterleave_buffer + + trigger_offset, num_samples); + devc->sent_samples += num_samples; + } else { + send_data(sdi, devc->deinterleave_buffer, num_samples); + devc->sent_samples += num_samples; + } + } + + if (devc->limit_samples && devc->sent_samples >= devc->limit_samples) { + abort_acquisition(devc); + free_transfer(transfer); + } else + resubmit_transfer(transfer); +} + +static int receive_data(int fd, int revents, void *cb_data) +{ + struct timeval tv; + struct drv_context *drvc; + + (void)fd; + (void)revents; + + drvc = (struct drv_context *)cb_data; + + tv.tv_sec = tv.tv_usec = 0; + libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv); + + return TRUE; +} + +static size_t to_bytes_per_ms(const struct sr_dev_inst *sdi) +{ + const struct dev_context *const devc = sdi->priv; + const size_t ch_count = enabled_channel_count(sdi); + + if (devc->continuous_mode) + return (devc->cur_samplerate * ch_count) / (1000 * 8); + + + /* If we're in buffered mode, the transfer rate is not so important, + * but we expect to get at least 10% of the high-speed USB bandwidth. + */ + return 35000000 / (1000 * 10); +} + +static size_t get_buffer_size(const struct sr_dev_inst *sdi) +{ + /* + * The buffer should be large enough to hold 10ms of data and + * a multiple of the size of a data atom. + */ + const size_t block_size = enabled_channel_count(sdi) * 512; + const size_t s = 10 * to_bytes_per_ms(sdi); + return ((s + block_size - 1) / block_size) * block_size; +} + +static unsigned int get_number_of_transfers(const struct sr_dev_inst *sdi) +{ + /* Total buffer size should be able to hold about 100ms of data. */ + const unsigned int s = get_buffer_size(sdi); + const unsigned int n = (100 * to_bytes_per_ms(sdi) + s - 1) / s; + return (n > NUM_SIMUL_TRANSFERS) ? NUM_SIMUL_TRANSFERS : n; +} + +static unsigned int get_timeout(const struct sr_dev_inst *sdi) +{ + const size_t total_size = get_buffer_size(sdi) * + get_number_of_transfers(sdi); + const unsigned int timeout = total_size / to_bytes_per_ms(sdi); + return timeout + timeout / 4; /* Leave a headroom of 25% percent. */ +} + +static int start_transfers(const struct sr_dev_inst *sdi) +{ + const size_t channel_count = enabled_channel_count(sdi); + const size_t size = get_buffer_size(sdi); + const unsigned int num_transfers = get_number_of_transfers(sdi); + const unsigned int timeout = get_timeout(sdi); + + struct dev_context *devc; + struct sr_usb_dev_inst *usb; + struct libusb_transfer *transfer; + unsigned int i; + int ret; + unsigned char *buf; + + devc = sdi->priv; + usb = sdi->conn; + + devc->sent_samples = 0; + devc->acq_aborted = FALSE; + devc->empty_transfer_count = 0; + devc->submitted_transfers = 0; + + g_free(devc->transfers); + devc->transfers = g_try_malloc0(sizeof(*devc->transfers) * num_transfers); + if (!devc->transfers) { + sr_err("USB transfers malloc failed."); + return SR_ERR_MALLOC; + } + + devc->deinterleave_buffer = g_try_malloc(DSLOGIC_ATOMIC_SAMPLES * + (size / (channel_count * DSLOGIC_ATOMIC_BYTES)) * sizeof(uint16_t)); + if (!devc->deinterleave_buffer) { + sr_err("Deinterleave buffer malloc failed."); + g_free(devc->deinterleave_buffer); + return SR_ERR_MALLOC; + } + + devc->num_transfers = num_transfers; + for (i = 0; i < num_transfers; i++) { + if (!(buf = g_try_malloc(size))) { + sr_err("USB transfer buffer malloc failed."); + return SR_ERR_MALLOC; + } + transfer = libusb_alloc_transfer(0); + libusb_fill_bulk_transfer(transfer, usb->devhdl, + 6 | LIBUSB_ENDPOINT_IN, buf, size, + receive_transfer, (void *)sdi, timeout); + sr_info("submitting transfer: %d", i); + if ((ret = libusb_submit_transfer(transfer)) != 0) { + sr_err("Failed to submit transfer: %s.", + libusb_error_name(ret)); + libusb_free_transfer(transfer); + g_free(buf); + abort_acquisition(devc); + return SR_ERR; + } + devc->transfers[i] = transfer; + devc->submitted_transfers++; + } + + std_session_send_df_header(sdi); + + return SR_OK; +} + +static void LIBUSB_CALL trigger_receive(struct libusb_transfer *transfer) +{ + const struct sr_dev_inst *sdi; + struct dslogic_trigger_pos *tpos; + struct dev_context *devc; + + sdi = transfer->user_data; + devc = sdi->priv; + if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { + sr_dbg("Trigger transfer canceled."); + /* Terminate session. */ + std_session_send_df_end(sdi); + usb_source_remove(sdi->session, devc->ctx); + devc->num_transfers = 0; + g_free(devc->transfers); + } else if (transfer->status == LIBUSB_TRANSFER_COMPLETED + && transfer->actual_length == sizeof(struct dslogic_trigger_pos)) { + tpos = (struct dslogic_trigger_pos *)transfer->buffer; + sr_info("tpos real_pos %d ram_saddr %d cnt %d", tpos->real_pos, + tpos->ram_saddr, tpos->remain_cnt); + devc->trigger_pos = tpos->real_pos; + g_free(tpos); + start_transfers(sdi); + } + libusb_free_transfer(transfer); +} + +SR_PRIV int dslogic_acquisition_start(const struct sr_dev_inst *sdi) +{ + const unsigned int timeout = get_timeout(sdi); + + struct sr_dev_driver *di; + struct drv_context *drvc; + struct dev_context *devc; + struct sr_usb_dev_inst *usb; + struct dslogic_trigger_pos *tpos; + struct libusb_transfer *transfer; + int ret; + + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR_DEV_CLOSED; + + di = sdi->driver; + drvc = di->context; + devc = sdi->priv; + usb = sdi->conn; + + devc->ctx = drvc->sr_ctx; + devc->sent_samples = 0; + devc->empty_transfer_count = 0; + devc->acq_aborted = FALSE; + + usb_source_add(sdi->session, devc->ctx, timeout, receive_data, drvc); + + if ((ret = command_stop_acquisition(sdi)) != SR_OK) + return ret; + + if ((ret = fpga_configure(sdi)) != SR_OK) + return ret; + + if ((ret = command_start_acquisition(sdi)) != SR_OK) + return ret; + + sr_dbg("Getting trigger."); + tpos = g_malloc(sizeof(struct dslogic_trigger_pos)); + transfer = libusb_alloc_transfer(0); + libusb_fill_bulk_transfer(transfer, usb->devhdl, 6 | LIBUSB_ENDPOINT_IN, + (unsigned char *)tpos, sizeof(struct dslogic_trigger_pos), + trigger_receive, (void *)sdi, 0); + if ((ret = libusb_submit_transfer(transfer)) < 0) { + sr_err("Failed to request trigger: %s.", libusb_error_name(ret)); + libusb_free_transfer(transfer); + g_free(tpos); + return SR_ERR; + } + + devc->transfers = g_try_malloc0(sizeof(*devc->transfers)); + if (!devc->transfers) { + sr_err("USB trigger_pos transfer malloc failed."); + return SR_ERR_MALLOC; + } + devc->num_transfers = 1; + devc->submitted_transfers++; + devc->transfers[0] = transfer; + + return ret; +} + +SR_PRIV int dslogic_acquisition_stop(struct sr_dev_inst *sdi) +{ + command_stop_acquisition(sdi); + abort_acquisition(sdi->priv); + return SR_OK; +} diff --git a/src/hardware/dreamsourcelab-dslogic/protocol.h b/src/hardware/dreamsourcelab-dslogic/protocol.h new file mode 100644 index 00000000..467389bd --- /dev/null +++ b/src/hardware/dreamsourcelab-dslogic/protocol.h @@ -0,0 +1,152 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2013 Bert Vermeulen + * Copyright (C) 2012 Joel Holdsworth + * + * 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_DREAMSOURCELAB_DSLOGIC_PROTOCOL_H +#define LIBSIGROK_HARDWARE_DREAMSOURCELAB_DSLOGIC_PROTOCOL_H + +#include +#include +#include +#include +#include +#include +#include "libsigrok-internal.h" + +#define LOG_PREFIX "dslogic" + +#define USB_INTERFACE 0 +#define USB_CONFIGURATION 1 + +#define MAX_RENUM_DELAY_MS 3000 +#define NUM_SIMUL_TRANSFERS 32 +#define MAX_EMPTY_TRANSFERS (NUM_SIMUL_TRANSFERS * 2) + +#define NUM_CHANNELS 16 +#define NUM_TRIGGER_STAGES 16 + +#define DSLOGIC_REQUIRED_VERSION_MAJOR 1 + +/* 6 delay states of up to 256 clock ticks */ +#define MAX_SAMPLE_DELAY (6 * 256) + +#define DSLOGIC_FPGA_FIRMWARE_5V "dreamsourcelab-dslogic-fpga-5v.fw" +#define DSLOGIC_FPGA_FIRMWARE_3V3 "dreamsourcelab-dslogic-fpga-3v3.fw" +#define DSCOPE_FPGA_FIRMWARE "dreamsourcelab-dscope-fpga.fw" +#define DSLOGIC_PRO_FPGA_FIRMWARE "dreamsourcelab-dslogic-pro-fpga.fw" +#define DSLOGIC_PLUS_FPGA_FIRMWARE "dreamsourcelab-dslogic-plus-fpga.fw" +#define DSLOGIC_BASIC_FPGA_FIRMWARE "dreamsourcelab-dslogic-basic-fpga.fw" + +enum dslogic_operation_modes { + DS_OP_NORMAL, + DS_OP_INTERNAL_TEST, + DS_OP_EXTERNAL_TEST, + DS_OP_LOOPBACK_TEST, +}; + +enum dslogic_edge_modes { + DS_EDGE_RISING, + DS_EDGE_FALLING, +}; + +struct dslogic_version { + uint8_t major; + uint8_t minor; +}; + +struct dslogic_mode { + uint8_t flags; + uint8_t sample_delay_h; + uint8_t sample_delay_l; +}; + +struct dslogic_trigger_pos { + uint32_t real_pos; + uint32_t ram_saddr; + uint32_t remain_cnt; + uint8_t first_block[500]; +}; + +struct dslogic_profile { + uint16_t vid; + uint16_t pid; + + const char *vendor; + const char *model; + const char *model_version; + + const char *firmware; + + uint32_t dev_caps; + + const char *usb_manufacturer; + const char *usb_product; + + /* Memory depth in bits. */ + uint64_t mem_depth; +}; + +struct dev_context { + const struct dslogic_profile *profile; + /* + * Since we can't keep track of a DSLogic device after upgrading + * the firmware (it renumerates into a different device address + * after the upgrade) this is like a global lock. No device will open + * until a proper delay after the last device was upgraded. + */ + int64_t fw_updated; + + /* Supported samplerates */ + const uint64_t *samplerates; + int num_samplerates; + + /* Device/capture settings */ + uint64_t cur_samplerate; + uint64_t limit_samples; + uint64_t capture_ratio; + + /* Operational settings */ + gboolean acq_aborted; + + unsigned int sent_samples; + int submitted_transfers; + int empty_transfer_count; + + unsigned int num_transfers; + struct libusb_transfer **transfers; + struct sr_context *ctx; + + uint16_t *deinterleave_buffer; + + uint16_t mode; + uint32_t trigger_pos; + gboolean external_clock; + gboolean continuous_mode; + int clock_edge; + double cur_threshold; +}; + +SR_PRIV int dslogic_fpga_firmware_upload(const struct sr_dev_inst *sdi); +SR_PRIV int dslogic_set_voltage_threshold(const struct sr_dev_inst *sdi, double threshold); +SR_PRIV int dslogic_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di); +SR_PRIV struct dev_context *dslogic_dev_new(void); +SR_PRIV int dslogic_acquisition_start(const struct sr_dev_inst *sdi); +SR_PRIV int dslogic_acquisition_stop(struct sr_dev_inst *sdi); + +#endif diff --git a/src/hardware/dslogic/api.c b/src/hardware/dslogic/api.c deleted file mode 100644 index 8e49c92a..00000000 --- a/src/hardware/dslogic/api.c +++ /dev/null @@ -1,659 +0,0 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2013 Bert Vermeulen - * Copyright (C) 2012 Joel Holdsworth - * - * 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 "protocol.h" - -static const struct dslogic_profile supported_device[] = { - /* DreamSourceLab DSLogic */ - { 0x2a0e, 0x0001, "DreamSourceLab", "DSLogic", NULL, - "dreamsourcelab-dslogic-fx2.fw", - 0, "DreamSourceLab", "DSLogic", 256 * 1048576ULL}, - /* DreamSourceLab DSCope */ - { 0x2a0e, 0x0002, "DreamSourceLab", "DSCope", NULL, - "dreamsourcelab-dscope-fx2.fw", - 0, "DreamSourceLab", "DSCope", 256 * 1048576ULL}, - /* DreamSourceLab DSLogic Pro */ - { 0x2a0e, 0x0003, "DreamSourceLab", "DSLogic Pro", NULL, - "dreamsourcelab-dslogic-pro-fx2.fw", - 0, "DreamSourceLab", "DSLogic", 256 * 1048576ULL}, - /* DreamSourceLab DSLogic Plus */ - { 0x2a0e, 0x0020, "DreamSourceLab", "DSLogic Plus", NULL, - "dreamsourcelab-dslogic-plus-fx2.fw", - 0, "DreamSourceLab", "DSLogic", 256 * 1048576ULL}, - /* DreamSourceLab DSLogic Basic */ - { 0x2a0e, 0x0021, "DreamSourceLab", "DSLogic Basic", NULL, - "dreamsourcelab-dslogic-basic-fx2.fw", - 0, "DreamSourceLab", "DSLogic", 256 * 1024ULL}, - - ALL_ZERO -}; - -static const uint32_t drvopts[] = { - SR_CONF_LOGIC_ANALYZER, -}; - -static const uint32_t scanopts[] = { - SR_CONF_CONN, -}; - -static const uint32_t devopts[] = { - SR_CONF_CONTINUOUS | SR_CONF_SET | SR_CONF_GET, - SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, - SR_CONF_VOLTAGE_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, - SR_CONF_CONN | SR_CONF_GET, - SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, - SR_CONF_TRIGGER_MATCH | SR_CONF_LIST, - SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET, - SR_CONF_EXTERNAL_CLOCK | SR_CONF_GET | SR_CONF_SET, - SR_CONF_CLOCK_EDGE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, -}; - -/* Names assigned to available edge slope choices. */ -static const char *const signal_edge_names[] = { - [DS_EDGE_RISING] = "rising", - [DS_EDGE_FALLING] = "falling", -}; - -static const struct { - gdouble low; - gdouble high; -} dslogic_voltage_thresholds[] = { - { 0.7, 1.4 }, - { 1.4, 3.6 }, -}; - -static const uint64_t samplerates[] = { - SR_KHZ(10), - SR_KHZ(20), - SR_KHZ(50), - SR_KHZ(100), - SR_KHZ(200), - SR_KHZ(500), - SR_MHZ(1), - SR_MHZ(2), - SR_MHZ(5), - SR_MHZ(10), - SR_MHZ(20), - SR_MHZ(25), - SR_MHZ(50), - SR_MHZ(100), - SR_MHZ(200), - SR_MHZ(400), -}; - -static gboolean is_plausible(const struct libusb_device_descriptor *des) -{ - int i; - - for (i = 0; supported_device[i].vid; i++) { - if (des->idVendor != supported_device[i].vid) - continue; - if (des->idProduct == supported_device[i].pid) - return TRUE; - } - - return FALSE; -} - -static GSList *scan(struct sr_dev_driver *di, GSList *options) -{ - struct drv_context *drvc; - struct dev_context *devc; - struct sr_dev_inst *sdi; - struct sr_usb_dev_inst *usb; - struct sr_channel *ch; - struct sr_channel_group *cg; - struct sr_config *src; - const struct dslogic_profile *prof; - GSList *l, *devices, *conn_devices; - gboolean has_firmware; - struct libusb_device_descriptor des; - libusb_device **devlist; - struct libusb_device_handle *hdl; - int ret, i, j; - const char *conn; - char manufacturer[64], product[64], serial_num[64], connection_id[64]; - char channel_name[16]; - - drvc = di->context; - - conn = NULL; - for (l = options; l; l = l->next) { - src = l->data; - switch (src->key) { - case SR_CONF_CONN: - conn = g_variant_get_string(src->data, NULL); - break; - } - } - if (conn) - conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn); - else - conn_devices = NULL; - - /* Find all dslogic compatible devices and upload firmware to them. */ - devices = NULL; - libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); - for (i = 0; devlist[i]; i++) { - if (conn) { - usb = NULL; - for (l = conn_devices; l; l = l->next) { - usb = l->data; - if (usb->bus == libusb_get_bus_number(devlist[i]) - && usb->address == libusb_get_device_address(devlist[i])) - break; - } - if (!l) - /* This device matched none of the ones that - * matched the conn specification. */ - continue; - } - - libusb_get_device_descriptor( devlist[i], &des); - - if (!is_plausible(&des)) - continue; - - if ((ret = libusb_open(devlist[i], &hdl)) < 0) { - sr_warn("Failed to open potential device with " - "VID:PID %04x:%04x: %s.", des.idVendor, - des.idProduct, libusb_error_name(ret)); - continue; - } - - if (des.iManufacturer == 0) { - manufacturer[0] = '\0'; - } else if ((ret = libusb_get_string_descriptor_ascii(hdl, - des.iManufacturer, (unsigned char *) manufacturer, - sizeof(manufacturer))) < 0) { - sr_warn("Failed to get manufacturer string descriptor: %s.", - libusb_error_name(ret)); - continue; - } - - if (des.iProduct == 0) { - product[0] = '\0'; - } else if ((ret = libusb_get_string_descriptor_ascii(hdl, - des.iProduct, (unsigned char *) product, - sizeof(product))) < 0) { - sr_warn("Failed to get product string descriptor: %s.", - libusb_error_name(ret)); - continue; - } - - if (des.iSerialNumber == 0) { - serial_num[0] = '\0'; - } else if ((ret = libusb_get_string_descriptor_ascii(hdl, - des.iSerialNumber, (unsigned char *) serial_num, - sizeof(serial_num))) < 0) { - sr_warn("Failed to get serial number string descriptor: %s.", - libusb_error_name(ret)); - continue; - } - - usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)); - - libusb_close(hdl); - - prof = NULL; - for (j = 0; supported_device[j].vid; j++) { - if (des.idVendor == supported_device[j].vid && - des.idProduct == supported_device[j].pid && - (!strcmp(manufacturer, supported_device[j].usb_manufacturer)) && - (!strcmp(product, "USB-based Instrument") || - !strcmp(product, supported_device[j].usb_product))) { - prof = &supported_device[j]; - break; - } - } - - /* Skip if the device was not found. */ - if (!prof) - continue; - - sdi = g_malloc0(sizeof(struct sr_dev_inst)); - sdi->status = SR_ST_INITIALIZING; - sdi->vendor = g_strdup(prof->vendor); - sdi->model = g_strdup(prof->model); - sdi->version = g_strdup(prof->model_version); - sdi->serial_num = g_strdup(serial_num); - sdi->connection_id = g_strdup(connection_id); - - /* Logic channels, all in one channel group. */ - cg = g_malloc0(sizeof(struct sr_channel_group)); - cg->name = g_strdup("Logic"); - for (j = 0; j < 16; j++) { - sprintf(channel_name, "%d", j); - ch = sr_channel_new(sdi, j, SR_CHANNEL_LOGIC, - TRUE, channel_name); - cg->channels = g_slist_append(cg->channels, ch); - } - sdi->channel_groups = g_slist_append(NULL, cg); - - devc = dslogic_dev_new(); - devc->profile = prof; - sdi->priv = devc; - devices = g_slist_append(devices, sdi); - - devc->samplerates = samplerates; - devc->num_samplerates = ARRAY_SIZE(samplerates); - has_firmware = usb_match_manuf_prod(devlist[i], "DreamSourceLab", "USB-based Instrument"); - - if (has_firmware) { - /* Already has the firmware, so fix the new address. */ - sr_dbg("Found an dslogic device."); - sdi->status = SR_ST_INACTIVE; - sdi->inst_type = SR_INST_USB; - sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), - libusb_get_device_address(devlist[i]), NULL); - } else { - if (ezusb_upload_firmware(drvc->sr_ctx, devlist[i], - USB_CONFIGURATION, prof->firmware) == SR_OK) - /* Store when this device's FW was updated. */ - devc->fw_updated = g_get_monotonic_time(); - else - sr_err("Firmware upload failed for " - "device %d.%d (logical).", - libusb_get_bus_number(devlist[i]), - libusb_get_device_address(devlist[i])); - sdi->inst_type = SR_INST_USB; - sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), - 0xff, NULL); - } - } - libusb_free_device_list(devlist, 1); - g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free); - - return std_scan_complete(di, devices); -} - -static void clear_dev_context(void *priv) -{ - struct dev_context *devc; - - devc = priv; - g_free(devc); -} - -static int dev_clear(const struct sr_dev_driver *di) -{ - return std_dev_clear(di, clear_dev_context); -} - -static int dev_open(struct sr_dev_inst *sdi) -{ - struct sr_dev_driver *di = sdi->driver; - struct sr_usb_dev_inst *usb; - struct dev_context *devc; - int ret; - int64_t timediff_us, timediff_ms; - - devc = sdi->priv; - usb = sdi->conn; - - /* - * If the firmware was recently uploaded, wait up to MAX_RENUM_DELAY_MS - * milliseconds for the FX2 to renumerate. - */ - ret = SR_ERR; - if (devc->fw_updated > 0) { - 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) { - if ((ret = dslogic_dev_open(sdi, di)) == 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); - } else { - sr_info("Firmware upload was not needed."); - ret = dslogic_dev_open(sdi, di); - } - - if (ret != SR_OK) { - sr_err("Unable to open device."); - return SR_ERR; - } - - ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE); - if (ret != 0) { - 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; - } - - - if ((ret = dslogic_fpga_firmware_upload(sdi)) != SR_OK) - return ret; - - if (devc->cur_samplerate == 0) { - /* Samplerate hasn't been set; default to the slowest one. */ - devc->cur_samplerate = devc->samplerates[0]; - } - - if (devc->cur_threshold == 0.0) - devc->cur_threshold = 1.5; - - return SR_OK; -} - -static int dev_close(struct sr_dev_inst *sdi) -{ - struct sr_usb_dev_inst *usb; - - usb = sdi->conn; - - if (!usb->devhdl) - return SR_ERR; - - sr_info("dslogic: Closing device on %d.%d (logical) / %s (physical) interface %d.", - usb->bus, usb->address, sdi->connection_id, USB_INTERFACE); - libusb_release_interface(usb->devhdl, USB_INTERFACE); - libusb_close(usb->devhdl); - usb->devhdl = NULL; - sdi->status = SR_ST_INACTIVE; - - return SR_OK; -} - -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; - struct sr_usb_dev_inst *usb; - GVariant *range[2]; - unsigned int i, voltage_range; - char str[128]; - - (void)cg; - - if (!sdi) - return SR_ERR_ARG; - - devc = sdi->priv; - - switch (key) { - case SR_CONF_CONN: - if (!sdi->conn) - return SR_ERR_ARG; - usb = sdi->conn; - if (usb->address == 255) - /* Device still needs to re-enumerate after firmware - * upload, so we don't know its (future) address. */ - return SR_ERR; - snprintf(str, 128, "%d.%d", usb->bus, usb->address); - *data = g_variant_new_string(str); - break; - case SR_CONF_VOLTAGE_THRESHOLD: - if (!strcmp(devc->profile->model, "DSLogic")) { - voltage_range = 0; - - for (i = 0; i < ARRAY_SIZE(dslogic_voltage_thresholds); i++) - if (dslogic_voltage_thresholds[i].low == - devc->cur_threshold) { - voltage_range = i; - break; - } - - range[0] = g_variant_new_double( - dslogic_voltage_thresholds[voltage_range].low); - range[1] = g_variant_new_double( - dslogic_voltage_thresholds[voltage_range].high); - } else { - range[0] = g_variant_new_double(devc->cur_threshold); - range[1] = g_variant_new_double(devc->cur_threshold); - } - *data = g_variant_new_tuple(range, 2); - break; - case SR_CONF_LIMIT_SAMPLES: - *data = g_variant_new_uint64(devc->limit_samples); - break; - case SR_CONF_SAMPLERATE: - *data = g_variant_new_uint64(devc->cur_samplerate); - break; - case SR_CONF_CAPTURE_RATIO: - *data = g_variant_new_uint64(devc->capture_ratio); - break; - case SR_CONF_EXTERNAL_CLOCK: - *data = g_variant_new_boolean(devc->external_clock); - break; - case SR_CONF_CONTINUOUS: - *data = g_variant_new_boolean(devc->continuous_mode); - break; - case SR_CONF_CLOCK_EDGE: - i = devc->clock_edge; - if (i >= ARRAY_SIZE(signal_edge_names)) - return SR_ERR_BUG; - *data = g_variant_new_string(signal_edge_names[0]); - break; - default: - return SR_ERR_NA; - } - - return SR_OK; -} - -/* - * Helper for mapping a string-typed configuration value to an index - * within a table of possible values. - */ -static int lookup_index(GVariant *value, const char *const *table, int len) -{ - const char *entry; - int i; - - entry = g_variant_get_string(value, NULL); - if (!entry) - return -1; - - /* Linear search is fine for very small tables. */ - for (i = 0; i < len; i++) { - if (strcmp(entry, table[i]) == 0) - return i; - } - - return -1; -} - -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; - uint64_t arg; - int i, ret; - gdouble low, high; - - (void)cg; - - if (!sdi) - return SR_ERR_ARG; - - if (sdi->status != SR_ST_ACTIVE) - return SR_ERR; - - devc = sdi->priv; - - ret = SR_OK; - - switch (key) { - case SR_CONF_SAMPLERATE: - arg = g_variant_get_uint64(data); - for (i = 0; i < devc->num_samplerates; i++) { - if (devc->samplerates[i] == arg) { - devc->cur_samplerate = arg; - break; - } - } - if (i == devc->num_samplerates) - ret = SR_ERR_ARG; - break; - case SR_CONF_LIMIT_SAMPLES: - devc->limit_samples = g_variant_get_uint64(data); - break; - case SR_CONF_CAPTURE_RATIO: - devc->capture_ratio = g_variant_get_uint64(data); - ret = (devc->capture_ratio > 100) ? SR_ERR : SR_OK; - break; - case SR_CONF_VOLTAGE_THRESHOLD: - g_variant_get(data, "(dd)", &low, &high); - if (!strcmp(devc->profile->model, "DSLogic")) { - for (i = 0; (unsigned int)i < - ARRAY_SIZE(dslogic_voltage_thresholds); i++) { - if (fabs(dslogic_voltage_thresholds[i].low - low) < 0.1 && - fabs(dslogic_voltage_thresholds[i].high - high) < 0.1) { - devc->cur_threshold = - dslogic_voltage_thresholds[i].low; - break; - } - } - ret = dslogic_fpga_firmware_upload(sdi); - } else { - ret = dslogic_set_voltage_threshold(sdi, (low + high) / 2.0); - } - break; - case SR_CONF_EXTERNAL_CLOCK: - devc->external_clock = g_variant_get_boolean(data); - break; - case SR_CONF_CONTINUOUS: - devc->continuous_mode = g_variant_get_boolean(data); - break; - case SR_CONF_CLOCK_EDGE: - i = lookup_index(data, signal_edge_names, - ARRAY_SIZE(signal_edge_names)); - if (i < 0) - return SR_ERR_ARG; - devc->clock_edge = i; - 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) -{ - struct dev_context *devc = NULL; - GVariant *gvar, *range[2]; - GVariantBuilder gvb; - unsigned int i; - double v; - - (void)cg; - - switch (key) { - case SR_CONF_SCAN_OPTIONS: - *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, - scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t)); - break; - case SR_CONF_DEVICE_OPTIONS: - if (!sdi) { - *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, - drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t)); - } else { - devc = sdi->priv; - *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, - devopts, ARRAY_SIZE(devopts), sizeof(uint32_t)); - } - break; - case SR_CONF_VOLTAGE_THRESHOLD: - if (sdi->priv) - devc = sdi->priv; - g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY); - if (devc && !strcmp(devc->profile->model, "DSLogic")) { - for (i = 0; i < ARRAY_SIZE(dslogic_voltage_thresholds); i++) { - range[0] = g_variant_new_double(dslogic_voltage_thresholds[i].low); - range[1] = g_variant_new_double(dslogic_voltage_thresholds[i].high); - gvar = g_variant_new_tuple(range, 2); - g_variant_builder_add_value(&gvb, gvar); - } - } else { - for (v = 0.0; v <= 5.0; v += 0.1) { - range[0] = g_variant_new_double(v); - range[1] = g_variant_new_double(v); - gvar = g_variant_new_tuple(range, 2); - g_variant_builder_add_value(&gvb, gvar); - } - } - *data = g_variant_builder_end(&gvb); - break; - case SR_CONF_SAMPLERATE: - devc = sdi->priv; - g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}")); - gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), devc->samplerates, - devc->num_samplerates, sizeof(uint64_t)); - g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar); - *data = g_variant_builder_end(&gvb); - break; - case SR_CONF_CLOCK_EDGE: - *data = g_variant_new_strv(signal_edge_names, - ARRAY_SIZE(signal_edge_names)); - break; - default: - return SR_ERR_NA; - } - - return SR_OK; -} - -static struct sr_dev_driver dslogic_driver_info = { - .name = "dslogic", - .longname = "DreamSourceLabs DSLogic", - .api_version = 1, - .init = std_init, - .cleanup = std_cleanup, - .scan = scan, - .dev_list = std_dev_list, - .dev_clear = 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 = dslogic_acquisition_start, - .dev_acquisition_stop = dslogic_acquisition_stop, - .context = NULL, -}; -SR_REGISTER_DEV_DRIVER(dslogic_driver_info); diff --git a/src/hardware/dslogic/protocol.c b/src/hardware/dslogic/protocol.c deleted file mode 100644 index 49c46f06..00000000 --- a/src/hardware/dslogic/protocol.c +++ /dev/null @@ -1,1115 +0,0 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2013 Bert Vermeulen - * Copyright (C) 2012 Joel Holdsworth - * - * 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 -#include "protocol.h" - -#define DS_CMD_GET_FW_VERSION 0xb0 -#define DS_CMD_GET_REVID_VERSION 0xb1 -#define DS_CMD_START 0xb2 -#define DS_CMD_CONFIG 0xb3 -#define DS_CMD_SETTING 0xb4 -#define DS_CMD_CONTROL 0xb5 -#define DS_CMD_STATUS 0xb6 -#define DS_CMD_STATUS_INFO 0xb7 -#define DS_CMD_WR_REG 0xb8 -#define DS_CMD_WR_NVM 0xb9 -#define DS_CMD_RD_NVM 0xba -#define DS_CMD_RD_NVM_PRE 0xbb -#define DS_CMD_GET_HW_INFO 0xbc - -#define DS_START_FLAGS_STOP (1 << 7) -#define DS_START_FLAGS_CLK_48MHZ (1 << 6) -#define DS_START_FLAGS_SAMPLE_WIDE (1 << 5) -#define DS_START_FLAGS_MODE_LA (1 << 4) - -#define DS_ADDR_COMB 0x68 -#define DS_ADDR_EEWP 0x70 -#define DS_ADDR_VTH 0x78 - -#define DS_MAX_LOGIC_DEPTH SR_MHZ(16) -#define DS_MAX_LOGIC_SAMPLERATE SR_MHZ(100) -#define DS_MAX_TRIG_PERCENT 90 - -#define DS_MODE_TRIG_EN (1 << 0) -#define DS_MODE_CLK_TYPE (1 << 1) -#define DS_MODE_CLK_EDGE (1 << 2) -#define DS_MODE_RLE_MODE (1 << 3) -#define DS_MODE_DSO_MODE (1 << 4) -#define DS_MODE_HALF_MODE (1 << 5) -#define DS_MODE_QUAR_MODE (1 << 6) -#define DS_MODE_ANALOG_MODE (1 << 7) -#define DS_MODE_FILTER (1 << 8) -#define DS_MODE_INSTANT (1 << 9) -#define DS_MODE_STRIG_MODE (1 << 11) -#define DS_MODE_STREAM_MODE (1 << 12) -#define DS_MODE_LPB_TEST (1 << 13) -#define DS_MODE_EXT_TEST (1 << 14) -#define DS_MODE_INT_TEST (1 << 15) - -#define DSLOGIC_ATOMIC_SAMPLES (sizeof(uint64_t) * 8) -#define DSLOGIC_ATOMIC_BYTES sizeof(uint64_t) - -/* - * The FPGA is configured with TLV tuples. Length is specified as the - * number of 16-bit words. - */ -#define _DS_CFG(variable, wordcnt) ((variable << 8) | wordcnt) -#define DS_CFG_START 0xf5a5f5a5 -#define DS_CFG_MODE _DS_CFG(0, 1) -#define DS_CFG_DIVIDER _DS_CFG(1, 2) -#define DS_CFG_COUNT _DS_CFG(3, 2) -#define DS_CFG_TRIG_POS _DS_CFG(5, 2) -#define DS_CFG_TRIG_GLB _DS_CFG(7, 1) -#define DS_CFG_CH_EN _DS_CFG(8, 1) -#define DS_CFG_TRIG _DS_CFG(64, 160) -#define DS_CFG_END 0xfa5afa5a - -#pragma pack(push, 1) - -struct version_info { - uint8_t major; - uint8_t minor; -}; - -struct cmd_start_acquisition { - uint8_t flags; - uint8_t sample_delay_h; - uint8_t sample_delay_l; -}; - -struct dslogic_fpga_config { - uint32_t sync; - - uint16_t mode_header; - uint16_t mode; - uint16_t divider_header; - uint32_t divider; - uint16_t count_header; - uint32_t count; - uint16_t trig_pos_header; - uint32_t trig_pos; - uint16_t trig_glb_header; - uint16_t trig_glb; - uint16_t ch_en_header; - uint16_t ch_en; - - uint16_t trig_header; - uint16_t trig_mask0[NUM_TRIGGER_STAGES]; - uint16_t trig_mask1[NUM_TRIGGER_STAGES]; - uint16_t trig_value0[NUM_TRIGGER_STAGES]; - uint16_t trig_value1[NUM_TRIGGER_STAGES]; - uint16_t trig_edge0[NUM_TRIGGER_STAGES]; - uint16_t trig_edge1[NUM_TRIGGER_STAGES]; - uint16_t trig_logic0[NUM_TRIGGER_STAGES]; - uint16_t trig_logic1[NUM_TRIGGER_STAGES]; - uint32_t trig_count[NUM_TRIGGER_STAGES]; - - uint32_t end_sync; -}; - -#pragma pack(pop) - -/* - * This should be larger than the FPGA bitstream image so that it'll get - * uploaded in one big operation. There seem to be issues when uploading - * it in chunks. - */ -#define FW_BUFSIZE (1024 * 1024) - -#define FPGA_UPLOAD_DELAY (10 * 1000) - -#define USB_TIMEOUT (3 * 1000) - -static int command_get_fw_version(libusb_device_handle *devhdl, - struct version_info *vi) -{ - int ret; - - ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR | - LIBUSB_ENDPOINT_IN, DS_CMD_GET_FW_VERSION, 0x0000, 0x0000, - (unsigned char *)vi, sizeof(struct version_info), USB_TIMEOUT); - - if (ret < 0) { - sr_err("Unable to get version info: %s.", - libusb_error_name(ret)); - return SR_ERR; - } - - return SR_OK; -} - -static int command_get_revid_version(struct sr_dev_inst *sdi, uint8_t *revid) -{ - struct sr_usb_dev_inst *usb = sdi->conn; - libusb_device_handle *devhdl = usb->devhdl; - int ret; - - ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR | - LIBUSB_ENDPOINT_IN, DS_CMD_GET_REVID_VERSION, 0x0000, 0x0000, - revid, 1, USB_TIMEOUT); - - if (ret < 0) { - sr_err("Unable to get REVID: %s.", libusb_error_name(ret)); - return SR_ERR; - } - - return SR_OK; -} - -static int command_start_acquisition(const struct sr_dev_inst *sdi) -{ - struct sr_usb_dev_inst *usb; - struct dslogic_mode mode; - int ret; - - mode.flags = DS_START_FLAGS_MODE_LA | DS_START_FLAGS_SAMPLE_WIDE; - mode.sample_delay_h = mode.sample_delay_l = 0; - - usb = sdi->conn; - ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR | - LIBUSB_ENDPOINT_OUT, DS_CMD_START, 0x0000, 0x0000, - (unsigned char *)&mode, sizeof(mode), USB_TIMEOUT); - if (ret < 0) { - sr_err("Failed to send start command: %s.", libusb_error_name(ret)); - return SR_ERR; - } - - return SR_OK; -} - -static int command_stop_acquisition(const struct sr_dev_inst *sdi) -{ - struct sr_usb_dev_inst *usb; - struct dslogic_mode mode; - int ret; - - mode.flags = DS_START_FLAGS_STOP; - mode.sample_delay_h = mode.sample_delay_l = 0; - - usb = sdi->conn; - ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR | - LIBUSB_ENDPOINT_OUT, DS_CMD_START, 0x0000, 0x0000, - (unsigned char *)&mode, sizeof(struct dslogic_mode), USB_TIMEOUT); - if (ret < 0) { - sr_err("Failed to send stop command: %s.", libusb_error_name(ret)); - return SR_ERR; - } - - return SR_OK; -} - -SR_PRIV int dslogic_fpga_firmware_upload(const struct sr_dev_inst *sdi) -{ - const char *name = NULL; - uint64_t sum; - struct sr_resource bitstream; - struct drv_context *drvc; - struct dev_context *devc; - struct sr_usb_dev_inst *usb; - unsigned char *buf; - ssize_t chunksize; - int transferred; - int result, ret; - const uint8_t cmd[3] = {0, 0, 0}; - - drvc = sdi->driver->context; - devc = sdi->priv; - usb = sdi->conn; - - if (!strcmp(devc->profile->model, "DSLogic")) { - if (devc->cur_threshold < 1.40) - name = DSLOGIC_FPGA_FIRMWARE_3V3; - else - name = DSLOGIC_FPGA_FIRMWARE_5V; - } else if (!strcmp(devc->profile->model, "DSLogic Pro")){ - name = DSLOGIC_PRO_FPGA_FIRMWARE; - } else if (!strcmp(devc->profile->model, "DSLogic Plus")){ - name = DSLOGIC_PLUS_FPGA_FIRMWARE; - } else if (!strcmp(devc->profile->model, "DSLogic Basic")){ - name = DSLOGIC_BASIC_FPGA_FIRMWARE; - } else if (!strcmp(devc->profile->model, "DSCope")) { - name = DSCOPE_FPGA_FIRMWARE; - } else { - sr_err("Failed to select FPGA firmware."); - return SR_ERR; - } - - sr_dbg("Uploading FPGA firmware '%s'.", name); - - result = sr_resource_open(drvc->sr_ctx, &bitstream, - SR_RESOURCE_FIRMWARE, name); - if (result != SR_OK) - return result; - - /* Tell the device firmware is coming. */ - if ((ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR | - LIBUSB_ENDPOINT_OUT, DS_CMD_CONFIG, 0x0000, 0x0000, - (unsigned char *)&cmd, sizeof(cmd), USB_TIMEOUT)) < 0) { - sr_err("Failed to upload FPGA firmware: %s.", libusb_error_name(ret)); - sr_resource_close(drvc->sr_ctx, &bitstream); - return SR_ERR; - } - - /* Give the FX2 time to get ready for FPGA firmware upload. */ - g_usleep(FPGA_UPLOAD_DELAY); - - buf = g_malloc(FW_BUFSIZE); - sum = 0; - result = SR_OK; - while (1) { - chunksize = sr_resource_read(drvc->sr_ctx, &bitstream, - buf, FW_BUFSIZE); - if (chunksize < 0) - result = SR_ERR; - if (chunksize <= 0) - break; - - if ((ret = libusb_bulk_transfer(usb->devhdl, 2 | LIBUSB_ENDPOINT_OUT, - buf, chunksize, &transferred, USB_TIMEOUT)) < 0) { - sr_err("Unable to configure FPGA firmware: %s.", - libusb_error_name(ret)); - result = SR_ERR; - break; - } - sum += transferred; - sr_spew("Uploaded %" PRIu64 "/%" PRIu64 " bytes.", - sum, bitstream.size); - - if (transferred != chunksize) { - sr_err("Short transfer while uploading FPGA firmware."); - result = SR_ERR; - break; - } - } - g_free(buf); - sr_resource_close(drvc->sr_ctx, &bitstream); - - if (result == SR_OK) - sr_dbg("FPGA firmware upload done."); - - return result; -} - -static unsigned int enabled_channel_count(const struct sr_dev_inst *sdi) -{ - unsigned int count = 0; - for (const GSList *l = sdi->channels; l; l = l->next) { - const struct sr_channel *const probe = (struct sr_channel *)l->data; - if (probe->enabled) - count++; - } - return count; -} - -static uint16_t enabled_channel_mask(const struct sr_dev_inst *sdi) -{ - unsigned int mask = 0; - for (const GSList *l = sdi->channels; l; l = l->next) { - const struct sr_channel *const probe = (struct sr_channel *)l->data; - if (probe->enabled) - mask |= 1 << probe->index; - } - return mask; -} - -/* - * Get the session trigger and configure the FPGA structure - * accordingly. - */ -static void set_trigger(const struct sr_dev_inst *sdi, - struct dslogic_fpga_config *cfg) -{ - struct sr_trigger *trigger; - struct sr_trigger_stage *stage; - struct sr_trigger_match *match; - struct dev_context *devc; - const GSList *l, *m; - const unsigned int num_enabled_channels = enabled_channel_count(sdi); - int num_trigger_stages = 0; - - int channelbit, i = 0; - uint32_t trigger_point; - - devc = sdi->priv; - - cfg->ch_en = enabled_channel_mask(sdi); - - cfg->trig_mask0[0] = 0xffff; - cfg->trig_mask1[0] = 0xffff; - - cfg->trig_value0[0] = 0; - cfg->trig_value1[0] = 0; - - cfg->trig_edge0[0] = 0; - cfg->trig_edge1[0] = 0; - - cfg->trig_logic0[0] = 2; - cfg->trig_logic1[0] = 2; - - cfg->trig_count[0] = 0; - - cfg->trig_glb = num_enabled_channels << 4; - - for (i = 1; i < NUM_TRIGGER_STAGES; i++) { - cfg->trig_mask0[i] = 0xffff; - cfg->trig_mask1[i] = 0xffff; - cfg->trig_value0[i] = 0; - cfg->trig_value1[i] = 0; - cfg->trig_edge0[i] = 0; - cfg->trig_edge1[i] = 0; - cfg->trig_logic0[i] = 2; - cfg->trig_logic1[i] = 2; - cfg->trig_count[i] = 0; - } - - trigger_point = (devc->capture_ratio * devc->limit_samples) / 100; - if (trigger_point < DSLOGIC_ATOMIC_SAMPLES) - trigger_point = DSLOGIC_ATOMIC_SAMPLES; - const uint32_t mem_depth = devc->profile->mem_depth; - const uint32_t max_trigger_point = devc->continuous_mode ? ((mem_depth * 10) / 100) : - ((mem_depth * DS_MAX_TRIG_PERCENT) / 100); - if (trigger_point > max_trigger_point) - trigger_point = max_trigger_point; - cfg->trig_pos = trigger_point & ~(DSLOGIC_ATOMIC_SAMPLES - 1); - - if (!(trigger = sr_session_trigger_get(sdi->session))) { - sr_dbg("No session trigger found"); - return; - } - - for (l = trigger->stages; l; l = l->next) { - stage = l->data; - num_trigger_stages++; - for (m = stage->matches; m; m = m->next) { - match = m->data; - if (!match->channel->enabled) - /* Ignore disabled channels with a trigger. */ - continue; - channelbit = 1 << (match->channel->index); - /* Simple trigger support (event). */ - if (match->match == SR_TRIGGER_ONE) { - cfg->trig_mask0[0] &= ~channelbit; - cfg->trig_mask1[0] &= ~channelbit; - cfg->trig_value0[0] |= channelbit; - cfg->trig_value1[0] |= channelbit; - } else if (match->match == SR_TRIGGER_ZERO) { - cfg->trig_mask0[0] &= ~channelbit; - cfg->trig_mask1[0] &= ~channelbit; - } else if (match->match == SR_TRIGGER_FALLING) { - cfg->trig_mask0[0] &= ~channelbit; - cfg->trig_mask1[0] &= ~channelbit; - cfg->trig_edge0[0] |= channelbit; - cfg->trig_edge1[0] |= channelbit; - } else if (match->match == SR_TRIGGER_RISING) { - cfg->trig_mask0[0] &= ~channelbit; - cfg->trig_mask1[0] &= ~channelbit; - cfg->trig_value0[0] |= channelbit; - cfg->trig_value1[0] |= channelbit; - cfg->trig_edge0[0] |= channelbit; - cfg->trig_edge1[0] |= channelbit; - } else if (match->match == SR_TRIGGER_EDGE) { - cfg->trig_edge0[0] |= channelbit; - cfg->trig_edge1[0] |= channelbit; - } - } - } - - cfg->trig_glb |= num_trigger_stages; - - return; -} - -static int fpga_configure(const struct sr_dev_inst *sdi) -{ - struct dev_context *devc; - struct sr_usb_dev_inst *usb; - uint8_t c[3]; - struct dslogic_fpga_config cfg; - uint16_t v16; - uint32_t v32; - int transferred, len, ret; - - sr_dbg("Configuring FPGA."); - - usb = sdi->conn; - devc = sdi->priv; - - WL32(&cfg.sync, DS_CFG_START); - WL16(&cfg.mode_header, DS_CFG_MODE); - WL16(&cfg.divider_header, DS_CFG_DIVIDER); - WL16(&cfg.count_header, DS_CFG_COUNT); - WL16(&cfg.trig_pos_header, DS_CFG_TRIG_POS); - WL16(&cfg.trig_glb_header, DS_CFG_TRIG_GLB); - WL16(&cfg.ch_en_header, DS_CFG_CH_EN); - WL16(&cfg.trig_header, DS_CFG_TRIG); - WL32(&cfg.end_sync, DS_CFG_END); - - /* Pass in the length of a fixed-size struct. Really. */ - len = sizeof(struct dslogic_fpga_config) / 2; - c[0] = len & 0xff; - c[1] = (len >> 8) & 0xff; - c[2] = (len >> 16) & 0xff; - - ret = libusb_control_transfer(usb->devhdl, LIBUSB_REQUEST_TYPE_VENDOR | - LIBUSB_ENDPOINT_OUT, DS_CMD_SETTING, 0x0000, 0x0000, - c, sizeof(c), USB_TIMEOUT); - if (ret < 0) { - sr_err("Failed to send FPGA configure command: %s.", - libusb_error_name(ret)); - return SR_ERR; - } - - v16 = 0x0000; - - if (devc->mode == DS_OP_INTERNAL_TEST) - v16 = DS_MODE_INT_TEST; - else if (devc->mode == DS_OP_EXTERNAL_TEST) - v16 = DS_MODE_EXT_TEST; - else if (devc->mode == DS_OP_LOOPBACK_TEST) - v16 = DS_MODE_LPB_TEST; - - if (devc->cur_samplerate == DS_MAX_LOGIC_SAMPLERATE * 2) - v16 |= DS_MODE_HALF_MODE; - else if (devc->cur_samplerate == DS_MAX_LOGIC_SAMPLERATE * 4) - v16 |= DS_MODE_QUAR_MODE; - - if (devc->continuous_mode) - v16 |= DS_MODE_STREAM_MODE; - if (devc->external_clock) { - v16 |= DS_MODE_CLK_TYPE; - if (devc->clock_edge == DS_EDGE_FALLING) - v16 |= DS_MODE_CLK_EDGE; - } - if (devc->limit_samples > DS_MAX_LOGIC_DEPTH * - ceil(devc->cur_samplerate * 1.0 / DS_MAX_LOGIC_SAMPLERATE) - && !devc->continuous_mode) { - /* Enable RLE for long captures. - * Without this, captured data present errors. - */ - v16 |= DS_MODE_RLE_MODE; - } - - WL16(&cfg.mode, v16); - v32 = ceil(DS_MAX_LOGIC_SAMPLERATE * 1.0 / devc->cur_samplerate); - WL32(&cfg.divider, v32); - - /* Number of 16-sample units. */ - WL32(&cfg.count, devc->limit_samples / 16); - - set_trigger(sdi, &cfg); - - len = sizeof(struct dslogic_fpga_config); - ret = libusb_bulk_transfer(usb->devhdl, 2 | LIBUSB_ENDPOINT_OUT, - (unsigned char *)&cfg, len, &transferred, USB_TIMEOUT); - if (ret < 0 || transferred != len) { - sr_err("Failed to send FPGA configuration: %s.", libusb_error_name(ret)); - return SR_ERR; - } - - return SR_OK; -} - -SR_PRIV int dslogic_set_voltage_threshold(const struct sr_dev_inst *sdi, double threshold) -{ - int ret; - struct dev_context *const devc = sdi->priv; - const struct sr_usb_dev_inst *const usb = sdi->conn; - const uint8_t value = (threshold / 5.0) * 255; - const uint16_t cmd = value | (DS_ADDR_VTH << 8); - - /* Send the control command. */ - ret = libusb_control_transfer(usb->devhdl, - LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, - DS_CMD_WR_REG, 0x0000, 0x0000, - (unsigned char *)&cmd, sizeof(cmd), 3000); - if (ret < 0) { - sr_err("Unable to set voltage-threshold register: %s.", - libusb_error_name(ret)); - return SR_ERR; - } - - devc->cur_threshold = threshold; - - return SR_OK; -} - -SR_PRIV int dslogic_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di) -{ - libusb_device **devlist; - struct sr_usb_dev_inst *usb; - struct libusb_device_descriptor des; - struct dev_context *devc; - struct drv_context *drvc; - struct version_info vi; - int ret, i, device_count; - uint8_t revid; - char connection_id[64]; - - drvc = di->context; - devc = sdi->priv; - usb = sdi->conn; - - if (sdi->status == SR_ST_ACTIVE) - /* Device is already in use. */ - return SR_ERR; - - device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); - if (device_count < 0) { - sr_err("Failed to get device list: %s.", - libusb_error_name(device_count)); - return SR_ERR; - } - - for (i = 0; i < device_count; i++) { - libusb_get_device_descriptor(devlist[i], &des); - - if (des.idVendor != devc->profile->vid - || des.idProduct != devc->profile->pid) - continue; - - if ((sdi->status == SR_ST_INITIALIZING) || - (sdi->status == SR_ST_INACTIVE)) { - /* - * Check device by its physical USB bus/port address. - */ - usb_get_port_path(devlist[i], connection_id, sizeof(connection_id)); - if (strcmp(sdi->connection_id, connection_id)) - /* This is not the one. */ - continue; - } - - if (!(ret = libusb_open(devlist[i], &usb->devhdl))) { - if (usb->address == 0xff) - /* - * First time we touch this device after FW - * upload, so we don't know the address yet. - */ - usb->address = libusb_get_device_address(devlist[i]); - } else { - sr_err("Failed to open device: %s.", - libusb_error_name(ret)); - break; - } - - if (libusb_has_capability(LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER)) { - if (libusb_kernel_driver_active(usb->devhdl, USB_INTERFACE) == 1) { - if ((ret = libusb_detach_kernel_driver(usb->devhdl, USB_INTERFACE)) < 0) { - sr_err("Failed to detach kernel driver: %s.", - libusb_error_name(ret)); - return SR_ERR; - } - } - } - - ret = command_get_fw_version(usb->devhdl, &vi); - if (ret != SR_OK) { - sr_err("Failed to get firmware version."); - break; - } - - ret = command_get_revid_version(sdi, &revid); - if (ret != SR_OK) { - sr_err("Failed to get REVID."); - break; - } - - /* - * Changes in major version mean incompatible/API changes, so - * bail out if we encounter an incompatible version. - * Different minor versions are OK, they should be compatible. - */ - if (vi.major != DSLOGIC_REQUIRED_VERSION_MAJOR) { - sr_err("Expected firmware version %d.x, " - "got %d.%d.", DSLOGIC_REQUIRED_VERSION_MAJOR, - vi.major, vi.minor); - break; - } - - sdi->status = SR_ST_ACTIVE; - sr_info("Opened device on %d.%d (logical) / %s (physical), " - "interface %d, firmware %d.%d.", - usb->bus, usb->address, connection_id, - USB_INTERFACE, vi.major, vi.minor); - - sr_info("Detected REVID=%d, it's a Cypress CY7C68013%s.", - revid, (revid != 1) ? " (FX2)" : "A (FX2LP)"); - - break; - } - libusb_free_device_list(devlist, 1); - - if (sdi->status != SR_ST_ACTIVE) - return SR_ERR; - - return SR_OK; -} - -SR_PRIV struct dev_context *dslogic_dev_new(void) -{ - struct dev_context *devc; - - devc = g_malloc0(sizeof(struct dev_context)); - devc->profile = NULL; - devc->fw_updated = 0; - devc->cur_samplerate = 0; - devc->limit_samples = 0; - devc->capture_ratio = 0; - devc->continuous_mode = FALSE; - devc->clock_edge = DS_EDGE_RISING; - - return devc; -} - -static void abort_acquisition(struct dev_context *devc) -{ - int i; - - devc->acq_aborted = TRUE; - - for (i = devc->num_transfers - 1; i >= 0; i--) { - if (devc->transfers[i]) - libusb_cancel_transfer(devc->transfers[i]); - } -} - -static void finish_acquisition(struct sr_dev_inst *sdi) -{ - struct dev_context *devc; - - devc = sdi->priv; - - std_session_send_df_end(sdi); - - usb_source_remove(sdi->session, devc->ctx); - - devc->num_transfers = 0; - g_free(devc->transfers); - g_free(devc->deinterleave_buffer); -} - -static void free_transfer(struct libusb_transfer *transfer) -{ - struct sr_dev_inst *sdi; - struct dev_context *devc; - unsigned int i; - - sdi = transfer->user_data; - devc = sdi->priv; - - g_free(transfer->buffer); - transfer->buffer = NULL; - libusb_free_transfer(transfer); - - for (i = 0; i < devc->num_transfers; i++) { - if (devc->transfers[i] == transfer) { - devc->transfers[i] = NULL; - break; - } - } - - devc->submitted_transfers--; - if (devc->submitted_transfers == 0) - finish_acquisition(sdi); -} - -static void resubmit_transfer(struct libusb_transfer *transfer) -{ - int ret; - - if ((ret = libusb_submit_transfer(transfer)) == LIBUSB_SUCCESS) - return; - - sr_err("%s: %s", __func__, libusb_error_name(ret)); - free_transfer(transfer); - -} - -static void deinterleave_buffer(const uint8_t *src, size_t length, - uint16_t *dst_ptr, size_t channel_count, uint16_t channel_mask) -{ - uint16_t sample; - - for (const uint64_t *src_ptr = (uint64_t*)src; - src_ptr < (uint64_t*)(src + length); - src_ptr += channel_count) { - for (int bit = 0; bit != 64; bit++) { - const uint64_t *word_ptr = src_ptr; - sample = 0; - for (size_t channel = 0; channel != channel_count; - channel++) { - if ((channel_mask & (1 << channel)) && - (*word_ptr++ & (1ULL << bit))) - sample |= 1 << channel; - } - *dst_ptr++ = sample; - } - } -} - -static void send_data(struct sr_dev_inst *sdi, - uint16_t *data, size_t sample_count) -{ - const struct sr_datafeed_logic logic = { - .length = sample_count * sizeof(uint16_t), - .unitsize = sizeof(uint16_t), - .data = data - }; - - const struct sr_datafeed_packet packet = { - .type = SR_DF_LOGIC, - .payload = &logic - }; - - sr_session_send(sdi, &packet); -} - -static void LIBUSB_CALL receive_transfer(struct libusb_transfer *transfer) -{ - struct sr_dev_inst *const sdi = transfer->user_data; - struct dev_context *const devc = sdi->priv; - const size_t channel_count = enabled_channel_count(sdi); - const uint16_t channel_mask = enabled_channel_mask(sdi); - const unsigned int cur_sample_count = DSLOGIC_ATOMIC_SAMPLES * - transfer->actual_length / - (DSLOGIC_ATOMIC_BYTES * channel_count); - - gboolean packet_has_error = FALSE; - struct sr_datafeed_packet packet; - unsigned int num_samples; - int trigger_offset; - - /* - * If acquisition has already ended, just free any queued up - * transfer that come in. - */ - if (devc->acq_aborted) { - free_transfer(transfer); - return; - } - - sr_dbg("receive_transfer(): status %s received %d bytes.", - libusb_error_name(transfer->status), transfer->actual_length); - - /* Save incoming transfer before reusing the transfer struct. */ - - switch (transfer->status) { - case LIBUSB_TRANSFER_NO_DEVICE: - abort_acquisition(devc); - free_transfer(transfer); - return; - case LIBUSB_TRANSFER_COMPLETED: - case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though. */ - break; - default: - packet_has_error = TRUE; - break; - } - - if (transfer->actual_length == 0 || packet_has_error) { - devc->empty_transfer_count++; - if (devc->empty_transfer_count > MAX_EMPTY_TRANSFERS) { - /* - * The FX2 gave up. End the acquisition, the frontend - * will work out that the samplecount is short. - */ - abort_acquisition(devc); - free_transfer(transfer); - } else { - resubmit_transfer(transfer); - } - return; - } else { - devc->empty_transfer_count = 0; - } - - if (!devc->limit_samples || devc->sent_samples < devc->limit_samples) { - if (devc->limit_samples && devc->sent_samples + cur_sample_count > devc->limit_samples) - num_samples = devc->limit_samples - devc->sent_samples; - else - num_samples = cur_sample_count; - - /** - * The DSLogic emits sample data as sequences of 64-bit sample words - * in a round-robin i.e. 64-bits from channel 0, 64-bits from channel 1 - * etc. for each of the enabled channels, then looping back to the - * channel. - * - * Because sigrok's internal representation is bit-interleaved channels - * we must recast the data. - * - * Hopefully in future it will be possible to pass the data on as-is. - */ - assert(transfer->actual_length % (DSLOGIC_ATOMIC_BYTES * channel_count) == 0); - deinterleave_buffer(transfer->buffer, transfer->actual_length, - devc->deinterleave_buffer, channel_count, channel_mask); - - /* Send the incoming transfer to the session bus. */ - if (devc->trigger_pos > devc->sent_samples - && devc->trigger_pos <= devc->sent_samples + num_samples) { - /* DSLogic trigger in this block. Send trigger position. */ - trigger_offset = devc->trigger_pos - devc->sent_samples; - /* Pre-trigger samples. */ - send_data(sdi, devc->deinterleave_buffer, trigger_offset); - devc->sent_samples += trigger_offset; - /* Trigger position. */ - devc->trigger_pos = 0; - packet.type = SR_DF_TRIGGER; - packet.payload = NULL; - sr_session_send(sdi, &packet); - /* Post trigger samples. */ - num_samples -= trigger_offset; - send_data(sdi, devc->deinterleave_buffer - + trigger_offset, num_samples); - devc->sent_samples += num_samples; - } else { - send_data(sdi, devc->deinterleave_buffer, num_samples); - devc->sent_samples += num_samples; - } - } - - if (devc->limit_samples && devc->sent_samples >= devc->limit_samples) { - abort_acquisition(devc); - free_transfer(transfer); - } else - resubmit_transfer(transfer); -} - -static int receive_data(int fd, int revents, void *cb_data) -{ - struct timeval tv; - struct drv_context *drvc; - - (void)fd; - (void)revents; - - drvc = (struct drv_context *)cb_data; - - tv.tv_sec = tv.tv_usec = 0; - libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv); - - return TRUE; -} - -static size_t to_bytes_per_ms(const struct sr_dev_inst *sdi) -{ - const struct dev_context *const devc = sdi->priv; - const size_t ch_count = enabled_channel_count(sdi); - - if (devc->continuous_mode) - return (devc->cur_samplerate * ch_count) / (1000 * 8); - - - /* If we're in buffered mode, the transfer rate is not so important, - * but we expect to get at least 10% of the high-speed USB bandwidth. - */ - return 35000000 / (1000 * 10); -} - -static size_t get_buffer_size(const struct sr_dev_inst *sdi) -{ - /* - * The buffer should be large enough to hold 10ms of data and - * a multiple of the size of a data atom. - */ - const size_t block_size = enabled_channel_count(sdi) * 512; - const size_t s = 10 * to_bytes_per_ms(sdi); - return ((s + block_size - 1) / block_size) * block_size; -} - -static unsigned int get_number_of_transfers(const struct sr_dev_inst *sdi) -{ - /* Total buffer size should be able to hold about 100ms of data. */ - const unsigned int s = get_buffer_size(sdi); - const unsigned int n = (100 * to_bytes_per_ms(sdi) + s - 1) / s; - return (n > NUM_SIMUL_TRANSFERS) ? NUM_SIMUL_TRANSFERS : n; -} - -static unsigned int get_timeout(const struct sr_dev_inst *sdi) -{ - const size_t total_size = get_buffer_size(sdi) * - get_number_of_transfers(sdi); - const unsigned int timeout = total_size / to_bytes_per_ms(sdi); - return timeout + timeout / 4; /* Leave a headroom of 25% percent. */ -} - -static int start_transfers(const struct sr_dev_inst *sdi) -{ - const size_t channel_count = enabled_channel_count(sdi); - const size_t size = get_buffer_size(sdi); - const unsigned int num_transfers = get_number_of_transfers(sdi); - const unsigned int timeout = get_timeout(sdi); - - struct dev_context *devc; - struct sr_usb_dev_inst *usb; - struct libusb_transfer *transfer; - unsigned int i; - int ret; - unsigned char *buf; - - devc = sdi->priv; - usb = sdi->conn; - - devc->sent_samples = 0; - devc->acq_aborted = FALSE; - devc->empty_transfer_count = 0; - devc->submitted_transfers = 0; - - g_free(devc->transfers); - devc->transfers = g_try_malloc0(sizeof(*devc->transfers) * num_transfers); - if (!devc->transfers) { - sr_err("USB transfers malloc failed."); - return SR_ERR_MALLOC; - } - - devc->deinterleave_buffer = g_try_malloc(DSLOGIC_ATOMIC_SAMPLES * - (size / (channel_count * DSLOGIC_ATOMIC_BYTES)) * sizeof(uint16_t)); - if (!devc->deinterleave_buffer) { - sr_err("Deinterleave buffer malloc failed."); - g_free(devc->deinterleave_buffer); - return SR_ERR_MALLOC; - } - - devc->num_transfers = num_transfers; - for (i = 0; i < num_transfers; i++) { - if (!(buf = g_try_malloc(size))) { - sr_err("USB transfer buffer malloc failed."); - return SR_ERR_MALLOC; - } - transfer = libusb_alloc_transfer(0); - libusb_fill_bulk_transfer(transfer, usb->devhdl, - 6 | LIBUSB_ENDPOINT_IN, buf, size, - receive_transfer, (void *)sdi, timeout); - sr_info("submitting transfer: %d", i); - if ((ret = libusb_submit_transfer(transfer)) != 0) { - sr_err("Failed to submit transfer: %s.", - libusb_error_name(ret)); - libusb_free_transfer(transfer); - g_free(buf); - abort_acquisition(devc); - return SR_ERR; - } - devc->transfers[i] = transfer; - devc->submitted_transfers++; - } - - std_session_send_df_header(sdi); - - return SR_OK; -} - -static void LIBUSB_CALL trigger_receive(struct libusb_transfer *transfer) -{ - const struct sr_dev_inst *sdi; - struct dslogic_trigger_pos *tpos; - struct dev_context *devc; - - sdi = transfer->user_data; - devc = sdi->priv; - if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { - sr_dbg("Trigger transfer canceled."); - /* Terminate session. */ - std_session_send_df_end(sdi); - usb_source_remove(sdi->session, devc->ctx); - devc->num_transfers = 0; - g_free(devc->transfers); - } else if (transfer->status == LIBUSB_TRANSFER_COMPLETED - && transfer->actual_length == sizeof(struct dslogic_trigger_pos)) { - tpos = (struct dslogic_trigger_pos *)transfer->buffer; - sr_info("tpos real_pos %d ram_saddr %d cnt %d", tpos->real_pos, - tpos->ram_saddr, tpos->remain_cnt); - devc->trigger_pos = tpos->real_pos; - g_free(tpos); - start_transfers(sdi); - } - libusb_free_transfer(transfer); -} - -SR_PRIV int dslogic_acquisition_start(const struct sr_dev_inst *sdi) -{ - const unsigned int timeout = get_timeout(sdi); - - struct sr_dev_driver *di; - struct drv_context *drvc; - struct dev_context *devc; - struct sr_usb_dev_inst *usb; - struct dslogic_trigger_pos *tpos; - struct libusb_transfer *transfer; - int ret; - - if (sdi->status != SR_ST_ACTIVE) - return SR_ERR_DEV_CLOSED; - - di = sdi->driver; - drvc = di->context; - devc = sdi->priv; - usb = sdi->conn; - - devc->ctx = drvc->sr_ctx; - devc->sent_samples = 0; - devc->empty_transfer_count = 0; - devc->acq_aborted = FALSE; - - usb_source_add(sdi->session, devc->ctx, timeout, receive_data, drvc); - - if ((ret = command_stop_acquisition(sdi)) != SR_OK) - return ret; - - if ((ret = fpga_configure(sdi)) != SR_OK) - return ret; - - if ((ret = command_start_acquisition(sdi)) != SR_OK) - return ret; - - sr_dbg("Getting trigger."); - tpos = g_malloc(sizeof(struct dslogic_trigger_pos)); - transfer = libusb_alloc_transfer(0); - libusb_fill_bulk_transfer(transfer, usb->devhdl, 6 | LIBUSB_ENDPOINT_IN, - (unsigned char *)tpos, sizeof(struct dslogic_trigger_pos), - trigger_receive, (void *)sdi, 0); - if ((ret = libusb_submit_transfer(transfer)) < 0) { - sr_err("Failed to request trigger: %s.", libusb_error_name(ret)); - libusb_free_transfer(transfer); - g_free(tpos); - return SR_ERR; - } - - devc->transfers = g_try_malloc0(sizeof(*devc->transfers)); - if (!devc->transfers) { - sr_err("USB trigger_pos transfer malloc failed."); - return SR_ERR_MALLOC; - } - devc->num_transfers = 1; - devc->submitted_transfers++; - devc->transfers[0] = transfer; - - return ret; -} - -SR_PRIV int dslogic_acquisition_stop(struct sr_dev_inst *sdi) -{ - command_stop_acquisition(sdi); - abort_acquisition(sdi->priv); - return SR_OK; -} diff --git a/src/hardware/dslogic/protocol.h b/src/hardware/dslogic/protocol.h deleted file mode 100644 index 933ac256..00000000 --- a/src/hardware/dslogic/protocol.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2013 Bert Vermeulen - * Copyright (C) 2012 Joel Holdsworth - * - * 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_DSLOGIC_PROTOCOL_H -#define LIBSIGROK_HARDWARE_DSLOGIC_PROTOCOL_H - -#include -#include -#include -#include -#include -#include -#include "libsigrok-internal.h" - -#define LOG_PREFIX "dslogic" - -#define USB_INTERFACE 0 -#define USB_CONFIGURATION 1 - -#define MAX_RENUM_DELAY_MS 3000 -#define NUM_SIMUL_TRANSFERS 32 -#define MAX_EMPTY_TRANSFERS (NUM_SIMUL_TRANSFERS * 2) - -#define NUM_CHANNELS 16 -#define NUM_TRIGGER_STAGES 16 - -#define DSLOGIC_REQUIRED_VERSION_MAJOR 1 - -/* 6 delay states of up to 256 clock ticks */ -#define MAX_SAMPLE_DELAY (6 * 256) - -#define DSLOGIC_FPGA_FIRMWARE_5V "dreamsourcelab-dslogic-fpga-5v.fw" -#define DSLOGIC_FPGA_FIRMWARE_3V3 "dreamsourcelab-dslogic-fpga-3v3.fw" -#define DSCOPE_FPGA_FIRMWARE "dreamsourcelab-dscope-fpga.fw" -#define DSLOGIC_PRO_FPGA_FIRMWARE "dreamsourcelab-dslogic-pro-fpga.fw" -#define DSLOGIC_PLUS_FPGA_FIRMWARE "dreamsourcelab-dslogic-plus-fpga.fw" -#define DSLOGIC_BASIC_FPGA_FIRMWARE "dreamsourcelab-dslogic-basic-fpga.fw" - -enum dslogic_operation_modes { - DS_OP_NORMAL, - DS_OP_INTERNAL_TEST, - DS_OP_EXTERNAL_TEST, - DS_OP_LOOPBACK_TEST, -}; - -enum dslogic_edge_modes { - DS_EDGE_RISING, - DS_EDGE_FALLING, -}; - -struct dslogic_version { - uint8_t major; - uint8_t minor; -}; - -struct dslogic_mode { - uint8_t flags; - uint8_t sample_delay_h; - uint8_t sample_delay_l; -}; - -struct dslogic_trigger_pos { - uint32_t real_pos; - uint32_t ram_saddr; - uint32_t remain_cnt; - uint8_t first_block[500]; -}; - -struct dslogic_profile { - uint16_t vid; - uint16_t pid; - - const char *vendor; - const char *model; - const char *model_version; - - const char *firmware; - - uint32_t dev_caps; - - const char *usb_manufacturer; - const char *usb_product; - - /* Memory depth in bits. */ - uint64_t mem_depth; -}; - -struct dev_context { - const struct dslogic_profile *profile; - /* - * Since we can't keep track of an dslogic device after upgrading - * the firmware (it renumerates into a different device address - * after the upgrade) this is like a global lock. No device will open - * until a proper delay after the last device was upgraded. - */ - int64_t fw_updated; - - /* Supported samplerates */ - const uint64_t *samplerates; - int num_samplerates; - - /* Device/capture settings */ - uint64_t cur_samplerate; - uint64_t limit_samples; - uint64_t capture_ratio; - - /* Operational settings */ - gboolean acq_aborted; - - unsigned int sent_samples; - int submitted_transfers; - int empty_transfer_count; - - unsigned int num_transfers; - struct libusb_transfer **transfers; - struct sr_context *ctx; - - uint16_t *deinterleave_buffer; - - uint16_t mode; - uint32_t trigger_pos; - gboolean external_clock; - gboolean continuous_mode; - int clock_edge; - double cur_threshold; -}; - -SR_PRIV int dslogic_fpga_firmware_upload(const struct sr_dev_inst *sdi); -SR_PRIV int dslogic_set_voltage_threshold(const struct sr_dev_inst *sdi, double threshold); -SR_PRIV int dslogic_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di); -SR_PRIV struct dev_context *dslogic_dev_new(void); -SR_PRIV int dslogic_acquisition_start(const struct sr_dev_inst *sdi); -SR_PRIV int dslogic_acquisition_stop(struct sr_dev_inst *sdi); - -#endif