]> sigrok.org Git - libsigrok.git/commitdiff
kingst-la2016: identify device type in scan() already
authorGerhard Sittig <redacted>
Sun, 30 Jan 2022 07:06:28 +0000 (08:06 +0100)
committerGerhard Sittig <redacted>
Sun, 6 Feb 2022 17:53:53 +0000 (18:53 +0100)
The vendor's design choice won't let us detect the device type from USB
enumeration data alone. EEPROM content must be read, which involves MCU
firmware communication, which only becomes available after the recently
uploaded firmware image becomes available. This extends the execution
time of the Kingst LA driver's scan() routine, but only if devices are
connected, and only for their first occurance after plugin. Subsequent
scans are quick.

Knowing the device type in the scan routine simplifies the open routine,
and allows model specific default parameters. The vendor's design choice
again won't let us read back previously configured values, each program
startup must assume a default configuration.

This implementation uses lots of small helpers to avoid open coding USB
communication details in several places. In theory up to 32 channels are
prepared, but all currently supported models have 16 channels. Different
memory sizes and channel counts are not yet effective in the acquisition
setup or session feed. The previous implementation of device open still
did too many things (configure PWM before user specs were received). A
future implementation needs to better pick which activities to run at
which points in time.

src/hardware/kingst-la2016/api.c
src/hardware/kingst-la2016/protocol.c
src/hardware/kingst-la2016/protocol.h

index 02293eb470b8324ab4c7201ef227f79c5c52ca9c..d89d25618ef81d0af2dd5404d54455ababa8b949 100644 (file)
@@ -71,9 +71,11 @@ static const int32_t trigger_matches[] = {
        SR_TRIGGER_FALLING,
 };
 
-static const char *channel_names[] = {
+static const char *channel_names_logic[] = {
        "CH0", "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7",
        "CH8", "CH9", "CH10", "CH11", "CH12", "CH13", "CH14", "CH15",
+       "CH16", "CH17", "CH18", "CH19", "CH20", "CH21", "CH22", "CH23",
+       "CH24", "CH25", "CH26", "CH27", "CH28", "CH29", "CH30", "CH31",
 };
 
 /*
@@ -167,6 +169,225 @@ static const char *logic_threshold[] = {
 
 #define LOGIC_THRESHOLD_IDX_USER       (ARRAY_SIZE(logic_threshold) - 1)
 
+/* 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;
+       }
+       ret = la2016_identify_device(sdi, show_message);
+       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;
+}
+
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
        struct drv_context *drvc;
@@ -176,21 +397,23 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        struct sr_usb_dev_inst *usb;
        struct sr_config *src;
        GSList *l;
-       GSList *devices;
+       GSList *devices, *found_devices, *renum_devices;
        GSList *conn_devices;
        struct libusb_device_descriptor des;
        libusb_device **devlist, *dev;
        size_t dev_count, dev_idx, ch_idx;
        uint8_t bus, addr;
+       uint16_t pid;
        const char *conn;
        char conn_id[64];
-       uint64_t fw_uploaded;
        int ret;
+       size_t ch_off, ch_max;
 
        drvc = di->context;
        ctx = drvc->sr_ctx;;
 
        conn = NULL;
+       conn_devices = NULL;
        for (l = options; l; l = l->next) {
                src = l->data;
                switch (src->key) {
@@ -201,11 +424,22 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
        }
        if (conn)
                conn_devices = sr_usb_find(ctx->libusb_ctx, conn);
-       else
-               conn_devices = NULL;
+       if (conn && !conn_devices) {
+               sr_err("Cannot find the specified connection '%s'.", conn);
+               return NULL;
+       }
 
-       /* Find all LA2016 devices, optionally upload firmware to them. */
+       /*
+        * Find all LA2016 devices, optionally upload firmware to them.
+        * Defer completion of sdi/devc creation until all (selected)
+        * devices were found in a usable state, and their models got
+        * identified which affect their feature set. It appears that
+        * we cannot communicate to the device within the same USB enum
+        * cycle, needs another USB enumeration after firmware upload.
+        */
        devices = NULL;
+       found_devices = NULL;
+       renum_devices = NULL;
        ret = libusb_get_device_list(ctx->libusb_ctx, &devlist);
        if (ret < 0) {
                sr_err("Cannot get device list: %s.", libusb_error_name(ret));
@@ -216,65 +450,123 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                dev = devlist[dev_idx];
                bus = libusb_get_bus_number(dev);
                addr = libusb_get_device_address(dev);
-               if (conn) {
-                       usb = NULL;
-                       for (l = conn_devices; l; l = l->next) {
-                               usb = l->data;
-                               if (usb->bus == bus && usb->address == addr)
-                                       break;
-                       }
-                       if (!l) {
-                               /*
-                                * A connection parameter was specified and
-                                * this device does not match the filter.
-                                */
-                               continue;
-                       }
+
+               /* Filter by connection when externally specified. */
+               for (l = conn_devices; l; l = l->next) {
+                       usb = l->data;
+                       if (usb->bus == bus && usb->address == addr)
+                               break;
+               }
+               if (conn_devices && !l) {
+                       sr_spew("Bus %hhu, addr %hhu do not match specified filter.",
+                               bus, addr);
+                       continue;
                }
 
+               /* Check USB VID:PID. Get the connection string. */
                libusb_get_device_descriptor(dev, &des);
+               if (des.idVendor != LA2016_VID || des.idProduct != LA2016_PID)
+                       continue;
+               pid = des.idProduct;
                ret = usb_get_port_path(dev, conn_id, sizeof(conn_id));
                if (ret < 0)
                        continue;
-               if (des.idVendor != LA2016_VID || des.idProduct != LA2016_PID)
-                       continue;
+               sr_dbg("USB enum found %04x:%04x at path %s, %d.%d.",
+                       des.idVendor, des.idProduct, conn_id, bus, addr);
+               usb = sr_usb_dev_inst_new(bus, addr, NULL);
 
-               /* USB identification matches, a device was found. */
-               sr_dbg("Found a device (USB identification).");
                sdi = g_malloc0(sizeof(*sdi));
+               sdi->driver = di;
                sdi->status = SR_ST_INITIALIZING;
+               sdi->inst_type = SR_INST_USB;
                sdi->connection_id = g_strdup(conn_id);
+               sdi->conn = usb;
 
-               fw_uploaded = 0;
-               if (des.iProduct != LA2016_IPRODUCT_INDEX) {
-                       sr_info("Device at '%s' has no firmware loaded.",
-                               conn_id);
+               devc = g_malloc0(sizeof(*devc));
+               sdi->priv = devc;
 
-                       ret = la2016_upload_firmware(ctx, dev, des.idProduct);
+               /*
+                * Load MCU firmware if it is currently missing. Which
+                * makes the device disappear and renumerate in USB.
+                * We need to come back another time to communicate to
+                * this device.
+                */
+               devc->fw_uploaded = 0;
+               if (des.iProduct != LA2016_IPRODUCT_INDEX) {
+                       sr_info("Uploading MCU firmware to '%s'.", conn_id);
+                       ret = la2016_upload_firmware(sdi, ctx, dev, pid);
                        if (ret != SR_OK) {
                                sr_err("MCU firmware upload failed.");
-                               g_free(sdi->connection_id);
-                               g_free(sdi);
+                               kingst_la2016_free_sdi(sdi);
                                continue;
                        }
-                       fw_uploaded = g_get_monotonic_time();
-                       /* Will re-enumerate. Mark as "unknown address yet". */
-                       addr = 0xff;
+                       devc->fw_uploaded = g_get_monotonic_time();
+                       usb->address = 0xff;
+                       renum_devices = g_slist_append(renum_devices, sdi);
+                       continue;
                }
 
-               sdi->vendor = g_strdup("Kingst");
-               sdi->model = g_strdup("LA2016");
+               /*
+                * Communicate to the MCU firmware to access EEPROM data
+                * which lets us identify the device type. Then stop, to
+                * share remaining sdi/devc creation with those devices
+                * which had their MCU firmware uploaded above and which
+                * get revisited later.
+                */
+               ret = la2016_identify_read(sdi, usb, dev, TRUE);
+               if (ret != SR_OK || !devc->model) {
+                       sr_err("Unknown or unsupported device type.");
+                       kingst_la2016_free_sdi(sdi);
+                       continue;
+               }
+               found_devices = g_slist_append(found_devices, sdi);
+       }
+       libusb_free_device_list(devlist, 1);
+       g_slist_free_full(conn_devices, sr_usb_dev_inst_free_cb);
 
-               for (ch_idx = 0; ch_idx < ARRAY_SIZE(channel_names); ch_idx++) {
-                       sr_channel_new(sdi, ch_idx, SR_CHANNEL_LOGIC,
-                               TRUE, channel_names[ch_idx]);
+       /*
+        * Wait for devices to re-appear after firmware upload. Append
+        * the yet unidentified device to the list of found devices, or
+        * release the previously allocated sdi/devc.
+        */
+       for (l = renum_devices; l; l = l->next) {
+               sdi = l->data;
+               devc = sdi->priv;
+               ret = la2016_identify_wait(sdi);
+               if (ret != SR_OK || !devc->model) {
+                       sr_dbg("Skipping unusable '%s'.", sdi->connection_id);
+                       kingst_la2016_free_sdi(sdi);
+                       continue;
                }
+               found_devices = g_slist_append(found_devices, sdi);
+       }
+       g_slist_free(renum_devices);
 
-               devices = g_slist_append(devices, sdi);
+       /*
+        * All found devices got identified, their type is known here.
+        * Complete the sdi/devc creation. Assign default settings
+        * because the vendor firmware would not let us read back the
+        * previously written configuration.
+        */
+       for (l = found_devices; l; l = l->next) {
+               sdi = l->data;
+               devc = sdi->priv;
+
+               sdi->vendor = g_strdup("Kingst");
+               sdi->model = g_strdup(devc->model->name);
+               ch_off = 0;
+
+               /* Create the logic channels. */
+               ch_max = ARRAY_SIZE(channel_names_logic);
+               if (ch_max > devc->model->channel_count)
+                       ch_max = devc->model->channel_count;
+               for (ch_idx = 0; ch_idx < ch_max; ch_idx++) {
+                       sr_channel_new(sdi, ch_off,
+                               SR_CHANNEL_LOGIC, TRUE,
+                               channel_names_logic[ch_idx]);
+                       ch_off++;
+               }
 
-               devc = g_malloc0(sizeof(*devc));
-               sdi->priv = devc;
-               devc->fw_uploaded = fw_uploaded;
                sr_sw_limits_init(&devc->sw_limits);
                devc->sw_limits.limit_samples = LA2016_DFLT_SAMPLEDEPTH;
                devc->capture_ratio = LA2016_DFLT_CAPT_RATIO;
@@ -283,166 +575,18 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
                devc->threshold_voltage = logic_threshold_value[devc->threshold_voltage_idx];
 
                sdi->status = SR_ST_INACTIVE;
-               sdi->inst_type = SR_INST_USB;
-
-               sdi->conn = sr_usb_dev_inst_new(bus, addr, NULL);
+               devices = g_slist_append(devices, sdi);
        }
-       libusb_free_device_list(devlist, 1);
-       g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free);
+       g_slist_free(found_devices);
 
        return std_scan_complete(di, devices);
 }
 
-static int la2016_dev_open(struct sr_dev_inst *sdi)
-{
-       struct sr_dev_driver *di;
-       struct drv_context *drvc;
-       struct sr_context *ctx;
-       libusb_device **devlist, *dev;
-       struct sr_usb_dev_inst *usb;
-       struct libusb_device_descriptor des;
-       int ret;
-       size_t device_count, dev_idx;
-       gboolean check_conn;
-       char conn_id[64];
-
-       di = sdi->driver;
-       drvc = di->context;
-       ctx = drvc->sr_ctx;;
-       usb = sdi->conn;
-       ret = SR_ERR;
-
-       ret = libusb_get_device_list(ctx->libusb_ctx, &devlist);
-       if (ret < 0) {
-               sr_err("Cannot get device list: %s.", libusb_error_name(ret));
-               return SR_ERR;
-       }
-       device_count = ret;
-       if (!device_count) {
-               sr_warn("Device list is empty. Cannot open.");
-               return SR_ERR;
-       }
-       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;
-
-               check_conn = sdi->status == SR_ST_INITIALIZING;
-               check_conn |= sdi->status == SR_ST_INACTIVE;
-               if (check_conn) {
-                       /* Check physical USB bus/port address. */
-                       ret = usb_get_port_path(dev, conn_id, sizeof(conn_id));
-                       if (ret < 0)
-                               continue;
-                       if (strcmp(sdi->connection_id, conn_id) != 0) {
-                               /* Not the device we looked up before. */
-                               continue;
-                       }
-               }
-
-               ret = libusb_open(dev, &usb->devhdl);
-               if (ret != 0) {
-                       sr_err("Cannot open device: %s.",
-                               libusb_error_name(ret));
-                       ret = SR_ERR_IO;
-                       break;
-               }
-
-               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?");
-                       ret = SR_ERR;
-                       break;
-               } else if (ret == LIBUSB_ERROR_NO_DEVICE) {
-                       sr_err("Device has been disconnected.");
-                       ret = SR_ERR;
-                       break;
-               } else if (ret != 0) {
-                       sr_err("Cannot claim USB interface: %s.",
-                               libusb_error_name(ret));
-                       ret = SR_ERR;
-                       break;
-               }
-
-               if ((ret = la2016_init_device(sdi)) != SR_OK) {
-                       sr_err("Cannot initialize device.");
-                       break;
-               }
-
-               sr_info("Opened device on %d.%d (logical) / %s (physical), interface %d.",
-                       usb->bus, usb->address, sdi->connection_id, USB_INTERFACE);
-               ret = SR_OK;
-               break;
-       }
-       libusb_free_device_list(devlist, 1);
-
-       if (ret != SR_OK) {
-               if (usb->devhdl) {
-                       libusb_release_interface(usb->devhdl, USB_INTERFACE);
-                       libusb_close(usb->devhdl);
-                       usb->devhdl = NULL;
-               }
-               return ret;
-       }
-
-       return SR_OK;
-}
-
 static int dev_open(struct sr_dev_inst *sdi)
 {
-       struct dev_context *devc;
-       uint64_t reset_done, now, elapsed_ms;
        int ret;
 
-       devc = sdi->priv;
-
-       /*
-        * When the sigrok driver recently has uploaded MCU firmware,
-        * then wait for the FX2 to re-enumerate. Allow the USB device
-        * to vanish before it reappears. Timeouts are rough estimates
-        * after all, the imprecise time of the last check (potentially
-        * executes after the total check period) simplifies code paths
-        * with optional diagnostics. And increases the probability of
-        * successfully detecting "late/slow" devices.
-        */
-       if (devc->fw_uploaded) {
-               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_dev_open(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);
-       } else {
-               ret = la2016_dev_open(sdi);
-       }
-
+       ret = la2016_open_enum(sdi);
        if (ret != SR_OK) {
                sr_err("Cannot open device.");
                return ret;
@@ -464,9 +608,7 @@ static int dev_close(struct sr_dev_inst *sdi)
 
        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;
+       la2016_close_usb(sdi->conn);
 
        return SR_OK;
 }
@@ -588,9 +730,9 @@ static int config_list(uint32_t key, GVariant **data,
        case SR_CONF_SAMPLERATE:
                if (!sdi)
                        return SR_ERR_ARG;
-               if (devc->max_samplerate == SR_MHZ(500))
+               if (devc->model->samplerate == SR_MHZ(500))
                        *data = std_gvar_samplerates(ARRAY_AND_SIZE(rates_500mhz));
-               else if (devc->max_samplerate == SR_MHZ(200))
+               else if (devc->model->samplerate == SR_MHZ(200))
                        *data = std_gvar_samplerates(ARRAY_AND_SIZE(rates_200mhz));
                else
                        *data = std_gvar_samplerates(ARRAY_AND_SIZE(rates_100mhz));
index 7af07906c813622d352ef5163d5b7b236f5ee420..102c5cc4b5da87ff16e44c69d13819d9453730fe 100644 (file)
 #include "libsigrok-internal.h"
 #include "protocol.h"
 
-#define UC_FIRMWARE    "kingst-la-%04x.fw"
-#define FPGA_FW_LA2016 "kingst-la2016-fpga.bitstream"
-#define FPGA_FW_LA2016A        "kingst-la2016a1-fpga.bitstream"
-#define FPGA_FW_LA1016 "kingst-la1016-fpga.bitstream"
-#define FPGA_FW_LA1016A        "kingst-la1016a1-fpga.bitstream"
+/* USB PID dependent MCU firmware. Model dependent FPGA bitstream. */
+#define MCU_FWFILE_FMT "kingst-la-%04x.fw"
+#define FPGA_FWFILE_FMT        "kingst-%s-fpga.bitstream"
+
+/*
+ * List of supported devices and their features. See @ref kingst_model
+ * for the fields' type and meaning. Table is sorted by EEPROM magic.
+ *
+ * TODO
+ * - Below LA1016 properties were guessed, need verification.
+ * - Add LA5016 and LA5032 devices when their EEPROM magic is known.
+ * - Does LA1010 fit the driver implementation? Samplerates vary with
+ *   channel counts, lack of local sample memory. Most probably not.
+ */
+static const struct kingst_model models[] = {
+       { 2, "LA2016", "la2016", SR_MHZ(200), 16, 1, },
+       { 3, "LA1016", "la1016", SR_MHZ(100), 16, 1, },
+       { 8, "LA2016", "la2016a1", SR_MHZ(200), 16, 1, },
+       { 9, "LA1016", "la1016a1", SR_MHZ(100), 16, 1, },
+};
 
 /* USB vendor class control requests, executed by the Cypress FX2 MCU. */
 #define CMD_FPGA_ENABLE        0x10
@@ -611,7 +626,7 @@ static int set_sample_config(const struct sr_dev_inst *sdi)
 
        devc = sdi->priv;
 
-       if (devc->cur_samplerate > devc->max_samplerate) {
+       if (devc->cur_samplerate > devc->model->samplerate) {
                sr_err("Too high a sample rate: %" PRIu64 ".",
                        devc->cur_samplerate);
                return SR_ERR_ARG;
@@ -622,11 +637,11 @@ static int set_sample_config(const struct sr_dev_inst *sdi)
                return SR_ERR_ARG;
        }
 
-       clock_divisor = devc->max_samplerate / (double)devc->cur_samplerate;
+       clock_divisor = devc->model->samplerate / (double)devc->cur_samplerate;
        if (clock_divisor > 65535)
                return SR_ERR_ARG;
        divider_u16 = (uint16_t)(clock_divisor + 0.5);
-       devc->cur_samplerate = devc->max_samplerate / divider_u16;
+       devc->cur_samplerate = devc->model->samplerate / divider_u16;
 
        ret = sr_sw_limits_get_remain(&devc->sw_limits,
                &limit_samples, NULL, NULL, NULL);
@@ -838,12 +853,31 @@ static int get_capture_info(const struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
-SR_PRIV int la2016_upload_firmware(struct sr_context *sr_ctx,
-       libusb_device *dev, uint16_t product_id)
+SR_PRIV int la2016_upload_firmware(const struct sr_dev_inst *sdi,
+       struct sr_context *sr_ctx, libusb_device *dev, uint16_t product_id)
 {
-       char fw_file[1024];
-       snprintf(fw_file, sizeof(fw_file), UC_FIRMWARE, product_id);
-       return ezusb_upload_firmware(sr_ctx, dev, USB_CONFIGURATION, fw_file);
+       struct dev_context *devc;
+       char *fw_file;
+       int ret;
+
+       devc = sdi ? sdi->priv : NULL;
+
+       fw_file = g_strdup_printf(MCU_FWFILE_FMT, product_id);
+       sr_info("USB PID %04hx, MCU firmware '%s'.", product_id, fw_file);
+
+       ret = ezusb_upload_firmware(sr_ctx, dev, USB_CONFIGURATION, fw_file);
+       if (ret != SR_OK) {
+               g_free(fw_file);
+               return ret;
+       }
+
+       if (devc) {
+               devc->mcu_firmware = fw_file;
+               fw_file = NULL;
+       }
+       g_free(fw_file);
+
+       return SR_OK;
 }
 
 SR_PRIV int la2016_setup_acquisition(const struct sr_dev_inst *sdi)
@@ -1186,16 +1220,17 @@ SR_PRIV int la2016_receive_data(int fd, int revents, void *cb_data)
        return TRUE;
 }
 
-SR_PRIV int la2016_init_device(const struct sr_dev_inst *sdi)
+SR_PRIV int la2016_identify_device(const struct sr_dev_inst *sdi,
+       gboolean show_message)
 {
        struct dev_context *devc;
-       uint16_t state;
        uint8_t buf[8];
        const uint8_t *rdptr;
        uint8_t date_yy, date_mm;
        uint8_t dinv_yy, dinv_mm;
        uint8_t magic;
-       const char *bitstream_fn;
+       size_t model_idx;
+       const struct kingst_model *model;
        int ret;
 
        devc = sdi->priv;
@@ -1208,7 +1243,10 @@ SR_PRIV int la2016_init_device(const struct sr_dev_inst *sdi)
         * devices when unknown magic numbers are seen.
         */
        ret = ctrl_in(sdi, CMD_EEPROM, 0x20, 0, buf, 4 * sizeof(uint8_t));
-       if (ret != SR_OK) {
+       if (ret != SR_OK && !show_message) {
+               sr_dbg("Cannot access EEPROM.");
+               return SR_ERR_IO;
+       } else if (ret != SR_OK) {
                sr_err("Cannot read manufacture date in EEPROM.");
        } else {
                rdptr = &buf[0];
@@ -1259,6 +1297,7 @@ SR_PRIV int la2016_init_device(const struct sr_dev_inst *sdi)
         * do not match the hardware model. An LA1016 won't become a
         * LA2016 by faking its EEPROM content.
         */
+       devc->identify_magic = 0;
        if ((ret = ctrl_in(sdi, CMD_EEPROM, 0x08, 0, &buf, sizeof(buf))) != SR_OK) {
                sr_err("Cannot read EEPROM device identifier bytes.");
                return ret;
@@ -1275,36 +1314,43 @@ SR_PRIV int la2016_init_device(const struct sr_dev_inst *sdi)
                sr_err("Cannot find consistent device type identification.");
                magic = 0;
        }
-       sr_dbg("Device type: magic number is %hhu.", magic);
+       devc->identify_magic = magic;
 
-       /* Select the FPGA bitstream depending on the model. */
-       switch (magic) {
-       case 2:
-               bitstream_fn = FPGA_FW_LA2016;
-               devc->max_samplerate = MAX_SAMPLE_RATE_LA2016;
-               break;
-       case 3:
-               bitstream_fn = FPGA_FW_LA1016;
-               devc->max_samplerate = MAX_SAMPLE_RATE_LA1016;
-               break;
-       case 8:
-               bitstream_fn = FPGA_FW_LA2016A;
-               devc->max_samplerate = MAX_SAMPLE_RATE_LA2016;
-               break;
-       case 9:
-               bitstream_fn = FPGA_FW_LA1016A;
-               devc->max_samplerate = MAX_SAMPLE_RATE_LA1016;
-               break;
-       default:
-               bitstream_fn = NULL;
+       devc->model = NULL;
+       for (model_idx = 0; model_idx < ARRAY_SIZE(models); model_idx++) {
+               model = &models[model_idx];
+               if (model->magic != magic)
+                       continue;
+               devc->model = model;
+               sr_info("EEPROM magic %d, model '%s'.", (int)magic, model->name);
+               devc->fpga_bitstream = g_strdup_printf(FPGA_FWFILE_FMT,
+                       model->fpga_stem);
+               sr_info("Max %zu channels at %" PRIu64 "MHz samplerate.",
+                       model->channel_count, model->samplerate / SR_MHZ(1));
+               sr_info("FPGA bitstream file '%s'.", devc->fpga_bitstream);
                break;
        }
-       if (!bitstream_fn || !*bitstream_fn) {
+       if (!devc->model) {
+               sr_spew("Device type: magic number is %hhu.", magic);
                sr_err("Cannot identify as one of the supported models.");
                return SR_ERR;
        }
 
-       if (check_fpga_bitstream(sdi) != SR_OK) {
+       return SR_OK;
+}
+
+SR_PRIV int la2016_init_device(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       const char *bitstream_fn;
+       int ret;
+       uint16_t state;
+
+       devc = sdi->priv;
+       bitstream_fn = devc ? devc->fpga_bitstream : "";
+
+       ret = check_fpga_bitstream(sdi);
+       if (ret != SR_OK) {
                ret = upload_fpga_bitstream(sdi, bitstream_fn);
                if (ret != SR_OK) {
                        sr_err("Cannot upload FPGA bitstream.");
index 41367bdfca9eb5b63be7d91c0995c093517ab955..1feabee37693d2cf4fbebb4e646fd6bf8886077e 100644 (file)
 
 #define LA2016_CONVBUFFER_SIZE (4 * 1024 * 1024)
 
+struct kingst_model {
+       uint8_t magic;          /* EEPROM magic byte value. */
+       const char *name;       /* User perceived model name. */
+       const char *fpga_stem;  /* Bitstream filename stem. */
+       uint64_t samplerate;    /* Max samplerate in Hz. */
+       size_t channel_count;   /* Max channel count (16, 32). */
+       uint64_t memory_bits;   /* RAM capacity in Gbit (1, 2, 4). */
+};
+
 struct pwm_setting_dev {
        uint32_t period;
        uint32_t duty;
@@ -119,13 +128,17 @@ struct pwm_setting {
 };
 
 struct dev_context {
+       uint16_t usb_pid;
+       char *mcu_firmware;
+       char *fpga_bitstream;
        uint64_t fw_uploaded; /* Timestamp of most recent FW upload. */
+       uint8_t identify_magic;
+       const struct kingst_model *model;
 
        /* User specified parameters. */
        struct pwm_setting pwm_setting[LA2016_NUM_PWMCH_MAX];
        size_t threshold_voltage_idx;
        float threshold_voltage;
-       uint64_t max_samplerate;
        uint64_t cur_samplerate;
        struct sr_sw_limits sw_limits;
        uint64_t capture_ratio;
@@ -146,12 +159,14 @@ struct dev_context {
        struct libusb_transfer *transfer;
 };
 
-SR_PRIV int la2016_upload_firmware(struct sr_context *sr_ctx,
-       libusb_device *dev, uint16_t product_id);
+SR_PRIV int la2016_upload_firmware(const struct sr_dev_inst *sdi,
+       struct sr_context *sr_ctx, libusb_device *dev, uint16_t product_id);
 SR_PRIV int la2016_setup_acquisition(const struct sr_dev_inst *sdi);
 SR_PRIV int la2016_start_acquisition(const struct sr_dev_inst *sdi);
 SR_PRIV int la2016_abort_acquisition(const struct sr_dev_inst *sdi);
 SR_PRIV int la2016_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int la2016_identify_device(const struct sr_dev_inst *sdi,
+       gboolean show_message);
 SR_PRIV int la2016_init_device(const struct sr_dev_inst *sdi);
 SR_PRIV int la2016_deinit_device(const struct sr_dev_inst *sdi);