+static double threshold_voltage(const struct sr_dev_inst *sdi, double *high)
+{
+ struct dev_context *devc;
+ size_t idx;
+ double voltage;
+
+ devc = sdi->priv;
+ idx = devc->threshold_voltage_idx;
+ if (idx == LOGIC_THRESHOLD_IDX_USER)
+ voltage = devc->threshold_voltage;
+ else
+ voltage = logic_threshold_value[idx];
+ if (high)
+ *high = voltage;
+
+ return voltage;
+}
+
+#endif /* WITH_THRESHOLD_SIMPLE */
+
+/* Convenience. Release an allocated devc from error paths. */
+static void kingst_la2016_free_devc(struct dev_context *devc)
+{
+ if (!devc)
+ return;
+ g_free(devc->mcu_firmware);
+ g_free(devc->fpga_bitstream);
+ g_free(devc);
+}
+
+/* Convenience. Release an allocated sdi from error paths. */
+static void kingst_la2016_free_sdi(struct sr_dev_inst *sdi)
+{
+ if (!sdi)
+ return;
+ g_free(sdi->vendor);
+ g_free(sdi->model);
+ g_free(sdi->version);
+ g_free(sdi->serial_num);
+ g_free(sdi->connection_id);
+ sr_usb_dev_inst_free(sdi->conn);
+ kingst_la2016_free_devc(sdi->priv);
+}
+
+/* Convenience. Open a USB device (including claiming an interface). */
+static int la2016_open_usb(struct sr_usb_dev_inst *usb,
+ libusb_device *dev, gboolean show_message)
+{
+ int ret;
+
+ ret = libusb_open(dev, &usb->devhdl);
+ if (ret != 0) {
+ if (show_message) {
+ sr_err("Cannot open device: %s.",
+ libusb_error_name(ret));
+ }
+ return SR_ERR_IO;
+ }
+
+ if (usb->address == 0xff) {
+ /*
+ * First encounter after firmware upload.
+ * Grab current address after enumeration.
+ */
+ usb->address = libusb_get_device_address(dev);
+ }
+
+ ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE);
+ if (ret == LIBUSB_ERROR_BUSY) {
+ sr_err("Cannot claim USB interface. Another program or driver using it?");
+ return SR_ERR_IO;
+ } else if (ret == LIBUSB_ERROR_NO_DEVICE) {
+ sr_err("Device has been disconnected.");
+ return SR_ERR_IO;
+ } else if (ret != 0) {
+ sr_err("Cannot claim USB interface: %s.",
+ libusb_error_name(ret));
+ return SR_ERR_IO;
+ }
+
+ return SR_OK;
+}
+
+/* Convenience. Close an opened USB device (and release the interface). */
+static void la2016_close_usb(struct sr_usb_dev_inst *usb)
+{
+
+ if (!usb)
+ return;
+
+ if (usb->devhdl) {
+ libusb_release_interface(usb->devhdl, USB_INTERFACE);
+ libusb_close(usb->devhdl);
+ usb->devhdl = NULL;
+ }
+}
+
+/* Communicate to an USB device to identify the Kingst LA model. */
+static int la2016_identify_read(struct sr_dev_inst *sdi,
+ struct sr_usb_dev_inst *usb, libusb_device *dev,
+ gboolean show_message)
+{
+ int ret;
+
+ ret = la2016_open_usb(usb, dev, show_message);
+ if (ret != SR_OK) {
+ if (show_message)
+ sr_err("Cannot communicate to MCU firmware.");
+ return ret;
+ }
+
+ /*
+ * Also complete the hardware configuration (FPGA bitstream)
+ * when MCU firmware communication became operational. Either
+ * failure is considered fatal when probing for the device.
+ */
+ ret = la2016_identify_device(sdi, show_message);
+ if (ret == SR_OK) {
+ ret = la2016_init_hardware(sdi);
+ }
+
+ la2016_close_usb(usb);
+
+ return ret;
+}
+
+/* Find given conn_id in another USB enum. Identify Kingst LA model. */
+static int la2016_identify_enum(struct sr_dev_inst *sdi)
+{
+ struct sr_dev_driver *di;
+ struct drv_context *drvc;
+ struct sr_context *ctx;
+ libusb_device **devlist, *dev;
+ struct libusb_device_descriptor des;
+ int ret, id_ret;
+ size_t device_count, dev_idx;
+ char conn_id[64];
+
+ di = sdi->driver;
+ drvc = di->context;
+ ctx = drvc->sr_ctx;;
+
+ ret = libusb_get_device_list(ctx->libusb_ctx, &devlist);
+ if (ret < 0)
+ return SR_ERR_IO;
+ device_count = ret;
+ if (!device_count)
+ return SR_ERR_IO;
+ id_ret = SR_ERR_IO;
+ for (dev_idx = 0; dev_idx < device_count; dev_idx++) {
+ dev = devlist[dev_idx];
+ libusb_get_device_descriptor(dev, &des);
+ if (des.idVendor != LA2016_VID || des.idProduct != LA2016_PID)
+ continue;
+ if (des.iProduct != LA2016_IPRODUCT_INDEX)
+ continue;
+ ret = usb_get_port_path(dev, conn_id, sizeof(conn_id));
+ if (ret < 0)
+ continue;
+ if (strcmp(sdi->connection_id, conn_id) != 0)
+ continue;
+ id_ret = la2016_identify_read(sdi, sdi->conn, dev, FALSE);
+ break;
+ }
+ libusb_free_device_list(devlist, 1);
+
+ return id_ret;
+}
+
+/* Wait for a device to re-appear after firmware upload. */
+static int la2016_identify_wait(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ uint64_t reset_done, now, elapsed_ms;
+ int ret;
+
+ devc = sdi->priv;
+
+ sr_info("Waiting for device to reset after firmware upload.");
+ now = g_get_monotonic_time();
+ reset_done = devc->fw_uploaded + RENUM_GONE_DELAY_MS * 1000;
+ if (now < reset_done)
+ g_usleep(reset_done - now);
+ do {
+ now = g_get_monotonic_time();
+ elapsed_ms = (now - devc->fw_uploaded) / 1000;
+ sr_spew("Waited %" PRIu64 "ms.", elapsed_ms);
+ ret = la2016_identify_enum(sdi);
+ if (ret == SR_OK) {
+ devc->fw_uploaded = 0;
+ break;
+ }
+ g_usleep(RENUM_POLL_INTERVAL_MS * 1000);
+ } while (elapsed_ms < RENUM_CHECK_PERIOD_MS);
+ if (ret != SR_OK) {
+ sr_err("Device failed to re-enumerate.");
+ return ret;
+ }
+ sr_info("Device came back after %" PRIi64 "ms.", elapsed_ms);
+
+ return SR_OK;
+}
+
+/*
+ * Open given conn_id from another USB enum. Used by dev_open(). Similar
+ * to, and should be kept in sync with la2016_identify_enum().
+ */
+static int la2016_open_enum(struct sr_dev_inst *sdi)
+{
+ struct sr_dev_driver *di;
+ struct drv_context *drvc;
+ struct sr_context *ctx;
+ libusb_device **devlist, *dev;
+ struct libusb_device_descriptor des;
+ int ret, open_ret;
+ size_t device_count, dev_idx;
+ char conn_id[64];
+
+ di = sdi->driver;
+ drvc = di->context;
+ ctx = drvc->sr_ctx;;
+
+ ret = libusb_get_device_list(ctx->libusb_ctx, &devlist);
+ if (ret < 0)
+ return SR_ERR_IO;
+ device_count = ret;
+ if (!device_count)
+ return SR_ERR_IO;
+ open_ret = SR_ERR_IO;
+ for (dev_idx = 0; dev_idx < device_count; dev_idx++) {
+ dev = devlist[dev_idx];
+ libusb_get_device_descriptor(dev, &des);
+ if (des.idVendor != LA2016_VID || des.idProduct != LA2016_PID)
+ continue;
+ if (des.iProduct != LA2016_IPRODUCT_INDEX)
+ continue;
+ ret = usb_get_port_path(dev, conn_id, sizeof(conn_id));
+ if (ret < 0)
+ continue;
+ if (strcmp(sdi->connection_id, conn_id) != 0)
+ continue;
+ open_ret = la2016_open_usb(sdi->conn, dev, TRUE);
+ break;
+ }
+ libusb_free_device_list(devlist, 1);
+
+ return open_ret;
+}
+