#define LOGIC16_PID 0x1001
#define NUM_PROBES 16
+#define USB_INTERFACE 0
+#define USB_CONFIGURATION 1
+#define FX2_FIRMWARE FIRMWARE_DIR "/saleae-logic16-fx2.fw"
+
+#define MAX_RENUM_DELAY_MS 3000
+
+
SR_PRIV struct sr_dev_driver saleae_logic16_driver_info;
static struct sr_dev_driver *di = &saleae_logic16_driver_info;
return std_init(sr_ctx, di, LOG_PREFIX);
}
+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 (strcmp((const char *)strdesc, "Saleae LLC"))
+ break;
+
+ if (libusb_get_string_descriptor_ascii(hdl,
+ des.iProduct, strdesc, sizeof(strdesc)) < 0)
+ break;
+ if (strcmp((const char *)strdesc, "Logic S/16"))
+ break;
+
+ /* If we made it here, it must be a configured Logic16. */
+ ret = TRUE;
+ }
+ if (hdl)
+ libusb_close(hdl);
+
+ return ret;
+}
+
static GSList *scan(GSList *options)
{
struct drv_context *drvc;
struct dev_context *devc;
struct sr_dev_inst *sdi;
+ struct sr_usb_dev_inst *usb;
struct sr_probe *probe;
+ struct sr_config *src;
+ GSList *l, *devices, *conn_devices;
struct libusb_device_descriptor des;
libusb_device **devlist;
- GSList *devices;
- int ret, devcnt, i, j;
-
- (void)options;
+ int devcnt, ret, i, j;
+ const char *conn;
drvc = di->priv;
+ 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 Logic16 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 ((ret = libusb_get_device_descriptor(devlist[i], &des)) != 0) {
- sr_warn("Failed to get device descriptor: %s",
- libusb_error_name(ret));
+ 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;
+ }
+
+ if ((ret = libusb_get_device_descriptor( devlist[i], &des)) != 0) {
+ sr_warn("Failed to get device descriptor: %s.",
+ libusb_error_name(ret));
continue;
}
continue;
devcnt = g_slist_length(drvc->instances);
- if (!(sdi = sr_dev_inst_new(devcnt, SR_ST_INACTIVE,
- "Saleae", "Logic16", NULL)))
+ sdi = sr_dev_inst_new(devcnt, SR_ST_INITIALIZING,
+ "Saleae", "Logic16", NULL);
+ if (!sdi)
return NULL;
sdi->driver = di;
- if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
- return NULL;
- sdi->priv = devc;
-
for (j = 0; probe_names[j]; j++) {
if (!(probe = sr_probe_new(j, SR_PROBE_LOGIC, TRUE,
probe_names[j])))
sdi->probes = g_slist_append(sdi->probes, probe);
}
- if (!(sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]),
- libusb_get_device_address(devlist[i]), NULL)))
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
return NULL;
- sdi->inst_type = SR_INST_USB;
-
+ sdi->priv = devc;
drvc->instances = g_slist_append(drvc->instances, sdi);
devices = g_slist_append(devices, sdi);
+
+ if (check_conf_profile(devlist[i])) {
+ /* Already has the firmware, so fix the new address. */
+ sr_dbg("Found a Logic16 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(devlist[i], USB_CONFIGURATION,
+ FX2_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.", devcnt);
+ 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 devices;
}
return std_dev_clear(di, NULL);
}
+static int logic16_dev_open(struct sr_dev_inst *sdi)
+{
+ libusb_device **devlist;
+ struct sr_usb_dev_inst *usb;
+ struct libusb_device_descriptor des;
+ struct dev_context *devc;
+ struct drv_context *drvc;
+ int ret, skip, i, device_count;
+
+ drvc = di->priv;
+ devc = sdi->priv;
+ usb = sdi->conn;
+
+ if (sdi->status == SR_ST_ACTIVE)
+ /* Device is already in use. */
+ return SR_ERR;
+
+ skip = 0;
+ 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++) {
+ if ((ret = libusb_get_device_descriptor(devlist[i], &des))) {
+ sr_err("Failed to get device descriptor: %s.",
+ libusb_error_name(ret));
+ continue;
+ }
+
+ if (des.idVendor != LOGIC16_VID
+ || des.idProduct != LOGIC16_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]) != usb->bus
+ || libusb_get_device_address(devlist[i]) != usb->address)
+ /* 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;
+ }
+
+ sdi->status = SR_ST_ACTIVE;
+ sr_info("Opened device %d on %d.%d, "
+ "interface %d.",
+ sdi->index, usb->bus, usb->address,
+ USB_INTERFACE);
+
+ break;
+ }
+ libusb_free_device_list(devlist, 1);
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
static int dev_open(struct sr_dev_inst *sdi)
{
- (void)sdi;
+ 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 = logic16_dev_open(sdi)) == 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 = logic16_dev_open(sdi);
+ }
+
+ 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;
+ }
- /* TODO: get handle from sdi->conn and open it. */
+ return SR_ERR;
+ }
- sdi->status = SR_ST_ACTIVE;
+ if (devc->cur_samplerate == 0) {
+ /* Samplerate hasn't been set; default to the slowest one. */
+ devc->cur_samplerate = 500000;
+ }
return SR_OK;
}
static int dev_close(struct sr_dev_inst *sdi)
{
- (void)sdi;
+ struct sr_usb_dev_inst *usb;
- /* TODO: get handle from sdi->conn and close it. */
+ usb = sdi->conn;
+ if (usb->devhdl == NULL)
+ return SR_ERR;
+ sr_info("Closing device %d on %d.%d interface %d.",
+ sdi->index, usb->bus, usb->address, USB_INTERFACE);
+ libusb_release_interface(usb->devhdl, USB_INTERFACE);
+ libusb_close(usb->devhdl);
+ usb->devhdl = NULL;
sdi->status = SR_ST_INACTIVE;
return SR_OK;