+#include "command.h"
+
+static const struct fx2lafw_profile supported_fx2[] = {
+ /*
+ * CWAV USBee AX
+ * EE Electronics ESLA201A
+ * ARMFLY AX-Pro
+ */
+ { 0x08a9, 0x0014, "CWAV", "USBee AX", NULL,
+ FIRMWARE_DIR "/fx2lafw-cwav-usbeeax.fw",
+ 0 },
+ /*
+ * CWAV USBee DX
+ * XZL-Studio DX
+ */
+ { 0x08a9, 0x0015, "CWAV", "USBee DX", NULL,
+ FIRMWARE_DIR "/fx2lafw-cwav-usbeedx.fw",
+ DEV_CAPS_16BIT },
+
+ /*
+ * CWAV USBee SX
+ */
+ { 0x08a9, 0x0009, "CWAV", "USBee SX", NULL,
+ FIRMWARE_DIR "/fx2lafw-cwav-usbeesx.fw",
+ 0 },
+
+ /*
+ * Saleae Logic
+ * EE Electronics ESLA100
+ * Robomotic MiniLogic
+ * Robomotic BugLogic 3
+ */
+ { 0x0925, 0x3881, "Saleae", "Logic", NULL,
+ FIRMWARE_DIR "/fx2lafw-saleae-logic.fw",
+ 0 },
+
+ /*
+ * Default Cypress FX2 without EEPROM, e.g.:
+ * Lcsoft Mini Board
+ * Braintechnology USB Interface V2.x
+ */
+ { 0x04B4, 0x8613, "Cypress", "FX2", NULL,
+ FIRMWARE_DIR "/fx2lafw-cypress-fx2.fw",
+ DEV_CAPS_16BIT },
+
+ /*
+ * Braintechnology USB-LPS
+ */
+ { 0x16d0, 0x0498, "Braintechnology", "USB-LPS", NULL,
+ FIRMWARE_DIR "/fx2lafw-braintechnology-usb-lps.fw",
+ DEV_CAPS_16BIT },
+
+ { 0, 0, 0, 0, 0, 0, 0 }
+};
+
+static const int hwcaps[] = {
+ SR_HWCAP_LOGIC_ANALYZER,
+ SR_HWCAP_SAMPLERATE,
+
+ /* These are really implemented in the driver, not the hardware. */
+ SR_HWCAP_LIMIT_SAMPLES,
+ SR_HWCAP_CONTINUOUS,
+ 0,
+};
+
+static const char *probe_names[] = {
+ "0",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ "10",
+ "11",
+ "12",
+ "13",
+ "14",
+ "15",
+ NULL,
+};
+
+static const uint64_t supported_samplerates[] = {
+ SR_KHZ(20),
+ SR_KHZ(25),
+ SR_KHZ(50),
+ SR_KHZ(100),
+ SR_KHZ(200),
+ SR_KHZ(250),
+ SR_KHZ(500),
+ SR_MHZ(1),
+ SR_MHZ(2),
+ SR_MHZ(3),
+ SR_MHZ(4),
+ SR_MHZ(6),
+ SR_MHZ(8),
+ SR_MHZ(12),
+ SR_MHZ(16),
+ SR_MHZ(24),
+ 0,
+};
+
+static const struct sr_samplerates samplerates = {
+ 0,
+ 0,
+ 0,
+ supported_samplerates,
+};
+
+static libusb_context *usb_context = NULL;
+
+SR_PRIV struct sr_dev_driver fx2lafw_driver_info;
+static struct sr_dev_driver *fdi = &fx2lafw_driver_info;
+static int hw_dev_close(struct sr_dev_inst *sdi);
+static int hw_dev_config_set(const struct sr_dev_inst *sdi, int hwcap,
+ const void *value);
+static int hw_dev_acquisition_stop(const struct sr_dev_inst *sdi,
+ void *cb_data);
+
+/**
+ * Check the USB configuration to determine if this is an fx2lafw device.
+ *
+ * @return TRUE if the device's configuration profile match fx2lafw
+ * configuration, FALSE otherwise.
+ */
+static gboolean check_conf_profile(libusb_device *dev)
+{
+ struct libusb_device_descriptor des;
+ struct libusb_device_handle *hdl;
+ gboolean ret;
+ unsigned char strdesc[64];
+
+ hdl = NULL;
+ ret = FALSE;
+ while (!ret) {
+ /* Assume the FW has not been loaded, unless proven wrong. */
+ if (libusb_get_device_descriptor(dev, &des) != 0)
+ break;
+
+ if (libusb_open(dev, &hdl) != 0)
+ break;
+
+ if (libusb_get_string_descriptor_ascii(hdl,
+ des.iManufacturer, strdesc, sizeof(strdesc)) < 0)
+ break;
+ if (strncmp((const char *)strdesc, "sigrok", 6))
+ break;
+
+ if (libusb_get_string_descriptor_ascii(hdl,
+ des.iProduct, strdesc, sizeof(strdesc)) < 0)
+ break;
+ if (strncmp((const char *)strdesc, "fx2lafw", 7))
+ break;
+
+ /* If we made it here, it must be an fx2lafw. */
+ ret = TRUE;
+ }
+ if (hdl)
+ libusb_close(hdl);
+
+ return ret;
+}
+
+static int fx2lafw_dev_open(struct sr_dev_inst *sdi)
+{
+ libusb_device **devlist;
+ struct libusb_device_descriptor des;
+ struct context *ctx;
+ struct version_info vi;
+ int ret, skip, i;
+ uint8_t revid;
+
+ ctx = sdi->priv;
+
+ if (sdi->status == SR_ST_ACTIVE)
+ /* already in use */
+ return SR_ERR;
+
+ skip = 0;
+ const int device_count = libusb_get_device_list(usb_context, &devlist);
+ if (device_count < 0) {
+ sr_err("fx2lafw: Failed to retrieve device list (%d)",
+ device_count);
+ return SR_ERR;
+ }
+
+ for (i = 0; i < device_count; i++) {
+ if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
+ sr_err("fx2lafw: Failed to get device descriptor: %d.",
+ ret);
+ continue;
+ }
+
+ if (des.idVendor != ctx->profile->vid
+ || des.idProduct != ctx->profile->pid)
+ continue;
+
+ if (sdi->status == SR_ST_INITIALIZING) {
+ if (skip != sdi->index) {
+ /* Skip devices of this type that aren't the one we want. */
+ skip += 1;
+ continue;
+ }
+ } else if (sdi->status == SR_ST_INACTIVE) {
+ /*
+ * This device is fully enumerated, so we need to find
+ * this device by vendor, product, bus and address.
+ */
+ if (libusb_get_bus_number(devlist[i]) != ctx->usb->bus
+ || libusb_get_device_address(devlist[i]) != ctx->usb->address)
+ /* this is not the one */
+ continue;
+ }
+
+ if (!(ret = libusb_open(devlist[i], &ctx->usb->devhdl))) {
+ if (ctx->usb->address == 0xff)
+ /*
+ * first time we touch this device after firmware upload,
+ * so we don't know the address yet.
+ */
+ ctx->usb->address = libusb_get_device_address(devlist[i]);
+ } else {
+ sr_err("fx2lafw: Failed to open device: %d.", ret);
+ break;
+ }
+
+ ret = command_get_fw_version(ctx->usb->devhdl, &vi);
+ if (ret != SR_OK) {
+ sr_err("fx2lafw: Failed to retrieve "
+ "firmware version information.");
+ break;
+ }
+
+ ret = command_get_revid_version(ctx->usb->devhdl, &revid);
+ if (ret != SR_OK) {
+ sr_err("fx2lafw: Failed to retrieve 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 != FX2LAFW_REQUIRED_VERSION_MAJOR) {
+ sr_err("fx2lafw: Expected firmware version %d.x, "
+ "got %d.%d.", FX2LAFW_REQUIRED_VERSION_MAJOR,
+ vi.major, vi.minor);
+ break;
+ }
+
+ sdi->status = SR_ST_ACTIVE;
+ sr_info("fx2lafw: Opened device %d on %d.%d "
+ "interface %d, firmware %d.%d, REVID %d.",
+ sdi->index, ctx->usb->bus, ctx->usb->address,
+ USB_INTERFACE, vi.major, vi.minor, revid);
+
+ break;
+ }
+ libusb_free_device_list(devlist, 1);
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+static int configure_probes(struct context *ctx, GSList *probes)
+{
+ struct sr_probe *probe;
+ GSList *l;
+ int probe_bit, stage, i;
+ char *tc;
+
+ for (i = 0; i < NUM_TRIGGER_STAGES; i++) {
+ ctx->trigger_mask[i] = 0;
+ ctx->trigger_value[i] = 0;
+ }
+
+ stage = -1;
+ for (l = probes; l; l = l->next) {
+ probe = (struct sr_probe *)l->data;
+ if (probe->enabled == FALSE)
+ continue;
+
+ if (probe->index > 7)
+ ctx->sample_wide = TRUE;
+
+ probe_bit = 1 << (probe->index);
+ if (!(probe->trigger))
+ continue;
+
+ stage = 0;
+ for (tc = probe->trigger; *tc; tc++) {
+ ctx->trigger_mask[stage] |= probe_bit;
+ if (*tc == '1')
+ ctx->trigger_value[stage] |= probe_bit;
+ stage++;
+ if (stage > NUM_TRIGGER_STAGES)
+ return SR_ERR;
+ }
+ }
+
+ if (stage == -1)
+ /*
+ * We didn't configure any triggers, make sure acquisition
+ * doesn't wait for any.
+ */
+ ctx->trigger_stage = TRIGGER_FIRED;
+ else
+ ctx->trigger_stage = 0;
+
+ return SR_OK;
+}
+
+static struct context *fx2lafw_dev_new(void)
+{
+ struct context *ctx;
+
+ if (!(ctx = g_try_malloc0(sizeof(struct context)))) {
+ sr_err("fx2lafw: %s: ctx malloc failed.", __func__);
+ return NULL;
+ }
+
+ ctx->trigger_stage = TRIGGER_FIRED;
+
+ return ctx;
+}
+
+static int clear_instances(void)
+{
+ GSList *l;
+ struct sr_dev_inst *sdi;
+ struct context *ctx;
+ int ret;
+
+ ret = SR_OK;
+ for (l = fdi->instances; l; l = l->next) {
+ if (!(sdi = l->data)) {
+ /* Log error, but continue cleaning up the rest. */
+ sr_err("fx2lafw: %s: sdi was NULL, continuing.",
+ __func__);
+ ret = SR_ERR_BUG;
+ continue;
+ }
+ if (!(ctx = sdi->priv)) {
+ /* Log error, but continue cleaning up the rest. */
+ sr_err("fx2lafw: %s: sdi->priv was NULL, continuing",
+ __func__);
+ ret = SR_ERR_BUG;
+ continue;
+ }
+ hw_dev_close(sdi);
+ sdi = l->data;
+ sr_dev_inst_free(sdi);
+ }
+
+ g_slist_free(fdi->instances);
+ fdi->instances = NULL;
+
+ return ret;
+}