]> sigrok.org Git - libsigrok.git/commitdiff
la8: Add support for the ChronoVu LA16.
authorUwe Hermann <redacted>
Sun, 30 Mar 2014 23:26:07 +0000 (01:26 +0200)
committerUwe Hermann <redacted>
Wed, 2 Apr 2014 15:20:09 +0000 (17:20 +0200)
The ChronoVu LA16 is a new logic analyzer from ChronoVu with some
differences in features compared to the LA8, e.g.

 - Supports 16 channels (instead of 8).
 - Max. 200MHz samplerate (instead of 100MHz).
 - Supports state triggering (low and high channel value) and edge triggering
   (rising or falling edge), the LA8 only supports state triggering.

This driver now supports both the LA8 and LA16, but it needed a few
changes:

 - Add support for detecting multiple device instances at all.
 - Add support for both LA8 and/or LA16 devices being detected.
 - Add a device profile struct for LA8-/LA16-specific device properties.
 - Move the samplerates list to devc (it's different for LA8 and LA16).
 - Split scan() into two functions, one for scanning, one for adding a device.
 - Expand some variables and fields from uint8_t to uint16_t in order to
   support 16 channels.
 - Update the samplerate related functions to support the LA16's 200MHz.
 - Various other minor updates in order to better handle both device types.
 - Various error handling improvements and simplifications.
 - Also, replace time() with g_get_monotonic_time() everywhere.

This also fixes bug #247 (which was related to incorrect handling of
resources during scan and open of the device, which was exposed by
PulseView allowing multiple consecutive scan/close/open calls).

contrib/z60_libsigrok.rules
hardware/chronovu-la8/api.c
hardware/chronovu-la8/protocol.c
hardware/chronovu-la8/protocol.h

index e1e62b49902038c16083a01a2d9c1d7dc0762a1d..46590c1945cb57b7ba7480b3b6158392d002c814 100644 (file)
@@ -35,6 +35,10 @@ ATTRS{idVendor}=="16d0", ATTRS{idProduct}=="0498", MODE="664", GROUP="plugdev"
 # CEM DT-8852
 ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE="664", GROUP="plugdev"
 
+# ChronoVu LA8 (new VID/PID)
+# ChronoVu LA16 (new VID/PID)
+ATTRS{idVendor}=="0403", ATTRS{idProduct}=="8867", MODE="664", GROUP="plugdev"
+
 # CWAV USBee AX
 # ARMFLY AX-Pro (clone of the CWAV USBee AX)
 # ARMFLY Mini-Logic (clone of the CWAV USBee AX)
@@ -56,7 +60,8 @@ ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0009", MODE="664", GROUP="plugdev"
 ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="8613", MODE="664", GROUP="plugdev"
 
 # Dangerous Prototypes Buspirate (v3)
-# ChronoVu LA8
+# ChronoVu LA8 (old VID/PID)
+# ChronoVu LA16 (old VID/PID)
 ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="664", GROUP="plugdev"
 
 # Dangerous Prototypes Buspirate (v4)
index fb364502e023d290297b5de5498c6f292437178d..685e1a46d88d95f571e86167292460d2a9a5fb01 100644 (file)
 SR_PRIV struct sr_dev_driver chronovu_la8_driver_info;
 static struct sr_dev_driver *di = &chronovu_la8_driver_info;
 
-/*
- * This will be initialized via config_list()/SR_CONF_SAMPLERATE.
- *
- * Min: 1 sample per 0.01us -> sample time is 0.084s, samplerate 100MHz
- * Max: 1 sample per 2.55us -> sample time is 21.391s, samplerate 392.15kHz
- */
-SR_PRIV uint64_t cv_samplerates[255] = { 0 };
-
-SR_PRIV const int32_t cv_hwcaps[] = {
+static const int32_t hwcaps[] = {
        SR_CONF_LOGIC_ANALYZER,
        SR_CONF_SAMPLERATE,
        SR_CONF_LIMIT_MSEC, /* TODO: Not yet implemented. */
        SR_CONF_LIMIT_SAMPLES, /* TODO: Not yet implemented. */
 };
 
-/*
- * The ChronoVu LA8 can have multiple PIDs. Older versions shipped with
- * a standard FTDI USB VID/PID of 0403:6001, newer ones have 0403:8867.
- */ 
-static const uint16_t usb_pids[] = {
-       0x6001,
-       0x8867,
+/* The ChronoVu LA8/LA16 can have multiple VID/PID pairs. */
+static struct {
+       uint16_t vid;
+       uint16_t pid;
+       int model;
+       const char *iproduct;
+} vid_pid[] = {
+       { 0x0403, 0x6001, CHRONOVU_LA8,  "ChronoVu LA8"  },
+       { 0x0403, 0x8867, CHRONOVU_LA8,  "ChronoVu LA8"  },
+       { 0x0403, 0x6001, CHRONOVU_LA16, "ChronoVu LA16" },
+       { 0x0403, 0x8867, CHRONOVU_LA16, "ChronoVu LA16" },
 };
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data);
@@ -69,106 +65,127 @@ static int init(struct sr_context *sr_ctx)
        return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(GSList *options)
+static int add_device(int idx, int model, GSList **devices)
 {
+       int ret;
+       unsigned int i;
        struct sr_dev_inst *sdi;
-       struct sr_channel *ch;
        struct drv_context *drvc;
        struct dev_context *devc;
-       GSList *devices;
-       unsigned int i;
-       int ret;
+       struct sr_channel *ch;
 
-       (void)options;
+       ret = SR_OK;
 
        drvc = di->priv;
 
-       devices = NULL;
-
        /* Allocate memory for our private device context. */
        devc = g_try_malloc(sizeof(struct dev_context));
 
        /* Set some sane defaults. */
-       devc->ftdic = NULL;
-       devc->cur_samplerate = SR_MHZ(100); /* 100MHz == max. samplerate */
+       devc->prof = &cv_profiles[model];
+       devc->ftdic = NULL; /* Will be set in the open() API call. */
+       devc->cur_samplerate = 0; /* Set later (different for LA8/LA16). */
        devc->limit_msec = 0;
        devc->limit_samples = 0;
        devc->cb_data = NULL;
        memset(devc->mangled_buf, 0, BS);
        devc->final_buf = NULL;
-       devc->trigger_pattern = 0x00; /* Value irrelevant, see trigger_mask. */
-       devc->trigger_mask = 0x00; /* All channels are "don't care". */
-       devc->trigger_timeout = 10; /* Default to 10s trigger timeout. */
+       devc->trigger_pattern = 0x0000; /* Irrelevant, see trigger_mask. */
+       devc->trigger_mask = 0x0000; /* All channels: "don't care". */
+       devc->trigger_edgemask = 0x0000; /* All channels: "state triggered". */
        devc->trigger_found = 0;
        devc->done = 0;
        devc->block_counter = 0;
-       devc->divcount = 0; /* 10ns sample period == 100MHz samplerate */
-       devc->usb_pid = 0;
+       devc->divcount = 0;
+       devc->usb_vid = vid_pid[idx].vid;
+       devc->usb_pid = vid_pid[idx].pid;
+       memset(devc->samplerates, 0, sizeof(uint64_t) * 255);
 
        /* Allocate memory where we'll store the de-mangled data. */
        if (!(devc->final_buf = g_try_malloc(SDRAM_SIZE))) {
                sr_err("Failed to allocate memory for sample buffer.");
+               ret = SR_ERR_MALLOC;
                goto err_free_devc;
        }
 
-       /* Allocate memory for the FTDI context (ftdic) and initialize it. */
-       if (!(devc->ftdic = ftdi_new())) {
-               sr_err("Failed to initialize libftdi.");
-               goto err_free_final_buf;
-       }
-
-       /* Check for the device and temporarily open it. */
-       for (i = 0; i < ARRAY_SIZE(usb_pids); i++) {
-               sr_dbg("Probing for VID/PID %04x:%04x.", USB_VENDOR_ID,
-                      usb_pids[i]);
-               ret = ftdi_usb_open_desc(devc->ftdic, USB_VENDOR_ID,
-                                        usb_pids[i], USB_DESCRIPTION, NULL);
-               if (ret == 0) {
-                       sr_dbg("Found LA8 device (%04x:%04x).",
-                              USB_VENDOR_ID, usb_pids[i]);
-                       devc->usb_pid = usb_pids[i];
-               }
-       }
-
-       if (devc->usb_pid == 0)
-               goto err_free_ftdic;
+       /* We now know the device, set its max. samplerate as default. */
+       devc->cur_samplerate = devc->prof->max_samplerate;
 
        /* Register the device with libsigrok. */
        sdi = sr_dev_inst_new(0, SR_ST_INITIALIZING,
-                       USB_VENDOR_NAME, USB_MODEL_NAME, USB_MODEL_VERSION);
+                             "ChronoVu", devc->prof->modelname, NULL);
        if (!sdi) {
                sr_err("Failed to create device instance.");
-               goto err_close_ftdic;
+               ret = SR_ERR;
+               goto err_free_final_buf;
        }
        sdi->driver = di;
        sdi->priv = devc;
 
-       for (i = 0; cv_channel_names[i]; i++) {
+       for (i = 0; i < devc->prof->num_channels; i++) {
                if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
-                                         cv_channel_names[i])))
-                       return NULL;
+                                         cv_channel_names[i]))) {
+                       ret = SR_ERR;
+                       goto err_free_dev_inst;
+               }
                sdi->channels = g_slist_append(sdi->channels, ch);
        }
 
-       devices = g_slist_append(devices, sdi);
+       *devices = g_slist_append(*devices, sdi);
        drvc->instances = g_slist_append(drvc->instances, sdi);
 
-       /* Close device. We'll reopen it again when we need it. */
-       (void) cv_close(devc); /* Log, but ignore errors. */
-
-       return devices;
+       return SR_OK;
 
-err_close_ftdic:
-       (void) cv_close(devc); /* Log, but ignore errors. */
-err_free_ftdic:
-       ftdi_free(devc->ftdic); /* NOT free() or g_free()! */
+err_free_dev_inst:
+       sr_dev_inst_free(sdi);
 err_free_final_buf:
        g_free(devc->final_buf);
 err_free_devc:
        g_free(devc);
-err_free_nothing:
 
-       return NULL;
+       return ret;
+}
+
+static GSList *scan(GSList *options)
+{
+       int ret;
+       unsigned int i;
+       GSList *devices;
+       struct ftdi_context *ftdic;
+
+       (void)options;
+
+       devices = NULL;
+
+       /* Allocate memory for the FTDI context and initialize it. */
+       if (!(ftdic = ftdi_new())) {
+               sr_err("Failed to initialize libftdi.");
+               return NULL;
+       }
+
+       /* Check for LA8 and/or LA16 devices with various VID/PIDs. */
+       for (i = 0; i < ARRAY_SIZE(vid_pid); i++) {
+               ret = ftdi_usb_open_desc(ftdic, vid_pid[i].vid,
+                       vid_pid[i].pid, vid_pid[i].iproduct, NULL);
+               if (ret < 0)
+                       continue; /* No device found. */
+
+               sr_dbg("Found %s device (%04x:%04x).",
+                      vid_pid[i].iproduct, vid_pid[i].vid, vid_pid[i].pid);
+
+               if ((ret = add_device(i, vid_pid[i].model, &devices)) < 0)
+                       sr_dbg("Failed to add device: %d.", ret);
+
+               if ((ret = ftdi_usb_close(ftdic)) < 0)
+                       sr_dbg("Failed to close FTDI device (%d): %s.",
+                              ret, ftdi_get_error_string(ftdic));
+       }
+
+       /* Close USB device, deinitialize and free the FTDI context. */
+       ftdi_free(ftdic);
+       ftdic = NULL;
+
+       return devices;
 }
 
 static GSList *dev_list(void)
@@ -181,19 +198,26 @@ static int dev_open(struct sr_dev_inst *sdi)
        struct dev_context *devc;
        int ret;
 
+       ret = SR_ERR;
+
        if (!(devc = sdi->priv))
                return SR_ERR_BUG;
 
-       sr_dbg("Opening LA8 device (%04x:%04x).", USB_VENDOR_ID,
-              devc->usb_pid);
+       /* Allocate memory for the FTDI context and initialize it. */
+       if (!(devc->ftdic = ftdi_new())) {
+               sr_err("Failed to initialize libftdi.");
+               return SR_ERR;
+       }
+
+       sr_dbg("Opening %s device (%04x:%04x).", devc->prof->modelname,
+              devc->usb_vid, devc->usb_pid);
 
        /* Open the device. */
-       if ((ret = ftdi_usb_open_desc(devc->ftdic, USB_VENDOR_ID,
-                       devc->usb_pid, USB_DESCRIPTION, NULL)) < 0) {
+       if ((ret = ftdi_usb_open_desc(devc->ftdic, devc->usb_vid,
+                       devc->usb_pid, devc->prof->iproduct, NULL)) < 0) {
                sr_err("Failed to open FTDI device (%d): %s.",
                       ret, ftdi_get_error_string(devc->ftdic));
-               (void) cv_close_usb_reset_sequencer(devc); /* Ignore errors. */
-               return SR_ERR;
+               goto err_ftdi_free;
        }
        sr_dbg("Device opened successfully.");
 
@@ -201,8 +225,7 @@ static int dev_open(struct sr_dev_inst *sdi)
        if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0) {
                sr_err("Failed to purge FTDI buffers (%d): %s.",
                       ret, ftdi_get_error_string(devc->ftdic));
-               (void) cv_close_usb_reset_sequencer(devc); /* Ignore errors. */
-               goto err_dev_open_close_ftdic;
+               goto err_ftdi_free;
        }
        sr_dbg("FTDI buffers purged successfully.");
 
@@ -210,8 +233,7 @@ static int dev_open(struct sr_dev_inst *sdi)
        if ((ret = ftdi_setflowctrl(devc->ftdic, SIO_RTS_CTS_HS)) < 0) {
                sr_err("Failed to enable FTDI flow control (%d): %s.",
                       ret, ftdi_get_error_string(devc->ftdic));
-               (void) cv_close_usb_reset_sequencer(devc); /* Ignore errors. */
-               goto err_dev_open_close_ftdic;
+               goto err_ftdi_free;
        }
        sr_dbg("FTDI flow control enabled successfully.");
 
@@ -222,24 +244,25 @@ static int dev_open(struct sr_dev_inst *sdi)
 
        return SR_OK;
 
-err_dev_open_close_ftdic:
-       (void) cv_close(devc); /* Log, but ignore errors. */
-       return SR_ERR;
+err_ftdi_free:
+       ftdi_free(devc->ftdic); /* Close device (if open), free FTDI context. */
+       devc->ftdic = NULL;
+       return ret;
 }
 
 static int dev_close(struct sr_dev_inst *sdi)
 {
+       int ret;
        struct dev_context *devc;
 
-       devc = sdi->priv;
+       if (sdi->status != SR_ST_ACTIVE)
+               return SR_OK;
 
-       if (sdi->status == SR_ST_ACTIVE) {
-               sr_dbg("Status ACTIVE, closing device.");
-               (void) cv_close_usb_reset_sequencer(devc); /* Ignore errors. */
-       } else {
-               sr_spew("Status not ACTIVE, nothing to do.");
-       }
+       devc = sdi->priv;
 
+       if (devc->ftdic && (ret = ftdi_usb_close(devc->ftdic)) < 0)
+               sr_err("Failed to close FTDI device (%d): %s.",
+                      ret, ftdi_get_error_string(devc->ftdic));
        sdi->status = SR_ST_INACTIVE;
 
        return SR_OK;
@@ -285,7 +308,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
 
        switch (id) {
        case SR_CONF_SAMPLERATE:
-               if (set_samplerate(sdi, g_variant_get_uint64(data)) < 0)
+               if (cv_set_samplerate(sdi, g_variant_get_uint64(data)) < 0)
                        return SR_ERR;
                break;
        case SR_CONF_LIMIT_MSEC:
@@ -310,21 +333,23 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
 {
        GVariant *gvar, *grange[2];
        GVariantBuilder gvb;
+       struct dev_context *devc;
 
-       (void)sdi;
        (void)cg;
 
        switch (key) {
        case SR_CONF_DEVICE_OPTIONS:
                *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-                               cv_hwcaps, ARRAY_SIZE(cv_hwcaps),
-                               sizeof(int32_t));
+                               hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
                break;
        case SR_CONF_SAMPLERATE:
-               cv_fill_samplerates_if_needed();
+               if (!sdi || !sdi->priv || !(devc = sdi->priv))
+                       return SR_ERR_BUG;
+               cv_fill_samplerates_if_needed(sdi);
                g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
                gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
-                               cv_samplerates, ARRAY_SIZE(cv_samplerates),
+                               devc->samplerates,
+                               ARRAY_SIZE(devc->samplerates),
                                sizeof(uint64_t));
                g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
                *data = g_variant_builder_end(&gvb);
@@ -335,7 +360,9 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
                *data = g_variant_new_tuple(grange, 2);
                break;
        case SR_CONF_TRIGGER_TYPE:
-               *data = g_variant_new_string(TRIGGER_TYPE);
+               if (!sdi || !sdi->priv || !(devc = sdi->priv) || !devc->prof)
+                       return SR_ERR_BUG;
+               *data = g_variant_new_string(devc->prof->trigger_type);
                break;
        default:
                return SR_ERR_NA;
@@ -395,8 +422,8 @@ static int receive_data(int fd, int revents, void *cb_data)
 static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 {
        struct dev_context *devc;
-       uint8_t buf[4];
-       int bytes_written;
+       uint8_t buf[8];
+       int bytes_to_write, bytes_written;
 
        if (sdi->status != SR_ST_ACTIVE)
                return SR_ERR_DEV_CLOSED;
@@ -411,7 +438,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
                return SR_ERR_BUG;
        }
 
-       devc->divcount = cv_samplerate_to_divcount(devc->cur_samplerate);
+       devc->divcount = cv_samplerate_to_divcount(sdi, devc->cur_samplerate);
        if (devc->divcount == 0xff) {
                sr_err("Invalid divcount/samplerate.");
                return SR_ERR;
@@ -423,19 +450,29 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
        }
 
        /* Fill acquisition parameters into buf[]. */
-       buf[0] = devc->divcount;
-       buf[1] = 0xff; /* This byte must always be 0xff. */
-       buf[2] = devc->trigger_pattern;
-       buf[3] = devc->trigger_mask;
+       if (devc->prof->model == CHRONOVU_LA8) {
+               buf[0] = devc->divcount;
+               buf[1] = 0xff; /* This byte must always be 0xff. */
+               buf[2] = devc->trigger_pattern & 0xff;
+               buf[3] = devc->trigger_mask & 0xff;
+               bytes_to_write = 4;
+       } else {
+               buf[0] = devc->divcount;
+               buf[1] = 0xff; /* This byte must always be 0xff. */
+               buf[2] = (devc->trigger_pattern & 0xff00) >> 8;  /* LSB */
+               buf[3] = (devc->trigger_pattern & 0x00ff) >> 0;  /* MSB */
+               buf[4] = (devc->trigger_mask & 0xff00) >> 8;     /* LSB */
+               buf[5] = (devc->trigger_mask & 0x00ff) >> 0;     /* MSB */
+               buf[6] = (devc->trigger_edgemask & 0xff00) >> 8; /* LSB */
+               buf[7] = (devc->trigger_edgemask & 0x00ff) >> 0; /* MSB */
+               bytes_to_write = 8;
+       }
 
        /* Start acquisition. */
-       bytes_written = cv_write(devc, buf, 4);
+       bytes_written = cv_write(devc, buf, bytes_to_write);
 
-       if (bytes_written < 0) {
-               sr_err("Acquisition failed to start: %d.", bytes_written);
-               return SR_ERR;
-       } else if (bytes_written != 4) {
-               sr_err("Acquisition failed to start: %d.", bytes_written);
+       if (bytes_written < 0 || bytes_written != bytes_to_write) {
+               sr_err("Acquisition failed to start.");
                return SR_ERR;
        }
 
@@ -447,8 +484,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
        std_session_send_df_header(cb_data, LOG_PREFIX);
 
        /* Time when we should be done (for detecting trigger timeouts). */
-       devc->done = (devc->divcount + 1) * 0.08388608 + time(NULL)
-                       + devc->trigger_timeout;
+       devc->done = (devc->divcount + 1) * devc->prof->trigger_constant +
+                       g_get_monotonic_time() + (10 * G_TIME_SPAN_SECOND);
        devc->block_counter = 0;
        devc->trigger_found = 0;
 
index 3a50fe613104feb0643f8b4ef0b7b803500b3c15..28ac15bee0853f2e3f581ff4caf89b374914f324 100644 (file)
 
 #include "protocol.h"
 
-/* Channels are numbered 0-7. */
-SR_PRIV const char *cv_channel_names[NUM_CHANNELS + 1] = {
+SR_PRIV const struct cv_profile cv_profiles[] = {
+       { CHRONOVU_LA8,  "LA8",  "ChronoVu LA8",  8,  SR_MHZ(100), "01",
+         0.8388608 },
+       { CHRONOVU_LA16, "LA16", "ChronoVu LA16", 16, SR_MHZ(200), "01rf",
+         0.042 },
+       { 0, NULL, NULL, 0, 0, NULL, 0.0 },
+};
+
+/* LA8: channels are numbered 0-7. LA16: channels are numbered 0-15. */
+SR_PRIV const char *cv_channel_names[] = {
        "0", "1", "2", "3", "4", "5", "6", "7",
-       NULL,
+       "8", "9", "10", "11", "12", "13", "14", "15",
 };
 
-SR_PRIV void cv_fill_samplerates_if_needed(void)
+static int close_usb_reset_sequencer(struct dev_context *devc);
+
+SR_PRIV void cv_fill_samplerates_if_needed(const struct sr_dev_inst *sdi)
 {
        int i;
+       struct dev_context *devc;
+
+       devc = sdi->priv;
 
-       if (cv_samplerates[0] != 0)
+       if (devc->samplerates[0] != 0)
                return;
 
        for (i = 0; i < 255; i++)
-               cv_samplerates[254 - i] = SR_MHZ(100) / (i + 1);
+               devc->samplerates[254 - i] = devc->prof->max_samplerate / (i + 1);
 }
 
 /**
  * Check if the given samplerate is supported by the hardware.
  *
+ * @param sdi Device instance.
  * @param samplerate The samplerate (in Hz) to check.
+ *
  * @return 1 if the samplerate is supported/valid, 0 otherwise.
  */
-static int is_valid_samplerate(uint64_t samplerate)
+static int is_valid_samplerate(const struct sr_dev_inst *sdi,
+                              uint64_t samplerate)
 {
        int i;
+       struct dev_context *devc;
+
+       devc = sdi->priv;
 
-       cv_fill_samplerates_if_needed();
+       cv_fill_samplerates_if_needed(sdi);
 
        for (i = 0; i < 255; i++) {
-               if (cv_samplerates[i] == samplerate)
+               if (devc->samplerates[i] == samplerate)
                        return 1;
        }
 
@@ -62,26 +81,41 @@ static int is_valid_samplerate(uint64_t samplerate)
 /**
  * Convert a samplerate (in Hz) to the 'divcount' value the device wants.
  *
- * LA8 hardware: sample period = (divcount + 1) * 10ns.
- * Min. value for divcount: 0x00 (10ns sample period, 100MHz samplerate).
- * Max. value for divcount: 0xfe (2550ns sample period, 392.15kHz samplerate).
+ * The divcount value can be 0x00 - 0xfe (0xff is not valid).
+ *
+ * LA8:
+ * sample period = (divcount + 1) * 10ns.
+ * divcount = 0x00: 10ns period, 100MHz samplerate.
+ * divcount = 0xfe: 2550ns period, 392.15kHz samplerate.
+ *
+ * LA16:
+ * sample period = (divcount + 1) * 5ns.
+ * divcount = 0x00: 5ns period, 200MHz samplerate.
+ * divcount = 0xfe: 1275ns period, ~784.31kHz samplerate.
  *
+ * @param sdi Device instance.
  * @param samplerate The samplerate in Hz.
+ *
  * @return The divcount value as needed by the hardware, or 0xff upon errors.
  */
-SR_PRIV uint8_t cv_samplerate_to_divcount(uint64_t samplerate)
+SR_PRIV uint8_t cv_samplerate_to_divcount(const struct sr_dev_inst *sdi,
+                                         uint64_t samplerate)
 {
+       struct dev_context *devc;
+
+       devc = sdi->priv;
+
        if (samplerate == 0) {
                sr_err("Can't convert invalid samplerate of 0 Hz.");
                return 0xff;
        }
 
-       if (!is_valid_samplerate(samplerate)) {
+       if (!is_valid_samplerate(sdi, samplerate)) {
                sr_err("Can't get divcount, samplerate invalid.");
                return 0xff;
        }
 
-       return (SR_MHZ(100) / samplerate) - 1;
+       return (devc->prof->max_samplerate / samplerate) - 1;
 }
 
 /**
@@ -100,22 +134,16 @@ SR_PRIV int cv_write(struct dev_context *devc, uint8_t *buf, int size)
 
        /* Note: Caller ensures devc/devc->ftdic/buf != NULL and size > 0. */
 
-       if (!buf)
-               return SR_ERR_ARG;
-
-       if (size < 0)
-               return SR_ERR_ARG;
-
        bytes_written = ftdi_write_data(devc->ftdic, buf, size);
 
        if (bytes_written < 0) {
                sr_err("Failed to write data (%d): %s.",
                       bytes_written, ftdi_get_error_string(devc->ftdic));
-               (void) cv_close_usb_reset_sequencer(devc); /* Ignore errors. */
+               (void) close_usb_reset_sequencer(devc); /* Ignore errors. */
        } else if (bytes_written != size) {
                sr_err("Failed to write data, only %d/%d bytes written.",
                       size, bytes_written);
-               (void) cv_close_usb_reset_sequencer(devc); /* Ignore errors. */
+               (void) close_usb_reset_sequencer(devc); /* Ignore errors. */
        }
 
        return bytes_written;
@@ -151,47 +179,20 @@ static int cv_read(struct dev_context *devc, uint8_t *buf, int size)
        return bytes_read;
 }
 
-SR_PRIV int cv_close(struct dev_context *devc)
-{
-       int ret;
-
-       if (!devc) {
-               sr_err("%s: devc was NULL.", __func__);
-               return SR_ERR_ARG;
-       }
-
-       if (!devc->ftdic) {
-               sr_err("%s: devc->ftdic was NULL.", __func__);
-               return SR_ERR_ARG;
-       }
-
-       if ((ret = ftdi_usb_close(devc->ftdic)) < 0) {
-               sr_err("%s: ftdi_usb_close: (%d) %s.",
-                      __func__, ret, ftdi_get_error_string(devc->ftdic));
-       }
-
-       return ret;
-}
-
 /**
  * Close the USB port and reset the sequencer logic.
  *
  * @param devc The struct containing private per-device-instance data.
+ *
  * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments.
  */
-SR_PRIV int cv_close_usb_reset_sequencer(struct dev_context *devc)
+static int close_usb_reset_sequencer(struct dev_context *devc)
 {
        /* Magic sequence of bytes for resetting the sequencer logic. */
        uint8_t buf[8] = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
        int ret;
 
-       if (!devc)
-               return SR_ERR_ARG;
-
-       if (!devc->ftdic) {
-               sr_err("devc->ftdic was NULL.");
-               return SR_ERR_ARG;
-       }
+       /* Note: Caller checked that devc and devc->ftdic != NULL. */
 
        if (devc->ftdic->usb_dev) {
                /* Reset the sequencer logic, then wait 100ms. */
@@ -204,18 +205,18 @@ SR_PRIV int cv_close_usb_reset_sequencer(struct dev_context *devc)
 
                /* Log errors, but ignore them (i.e., don't abort). */
                if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0)
-                       sr_err("%s: ftdi_usb_purge_buffers: (%d) %s.",
-                           __func__, ret, ftdi_get_error_string(devc->ftdic));
+                       sr_err("Failed to purge FTDI buffers (%d): %s.",
+                              ret, ftdi_get_error_string(devc->ftdic));
                if ((ret = ftdi_usb_reset(devc->ftdic)) < 0)
-                       sr_err("%s: ftdi_usb_reset: (%d) %s.", __func__,
+                       sr_err("Failed to reset FTDI device (%d): %s.",
                               ret, ftdi_get_error_string(devc->ftdic));
                if ((ret = ftdi_usb_close(devc->ftdic)) < 0)
-                       sr_err("%s: ftdi_usb_close: (%d) %s.", __func__,
+                       sr_err("Failed to close FTDI device (%d): %s.",
                               ret, ftdi_get_error_string(devc->ftdic));
        }
 
        /* Close USB device, deinitialize and free the FTDI context. */
-       ftdi_free(devc->ftdic); /* Returns void. */
+       ftdi_free(devc->ftdic);
        devc->ftdic = NULL;
 
        return SR_OK;
@@ -227,23 +228,16 @@ SR_PRIV int cv_close_usb_reset_sequencer(struct dev_context *devc)
  * A reset is required after a failed read/write operation or upon timeouts.
  *
  * @param devc The struct containing private per-device-instance data.
+ *
  * @return SR_OK upon success, SR_ERR upon failure.
  */
-static int cv_reset(struct dev_context *devc)
+static int reset_device(struct dev_context *devc)
 {
        uint8_t buf[BS];
-       time_t done, now;
+       gint64 done, now;
        int bytes_read;
 
-       if (!devc) {
-               sr_err("%s: devc was NULL.", __func__);
-               return SR_ERR_ARG;
-       }
-
-       if (!devc->ftdic) {
-               sr_err("%s: devc->ftdic was NULL.", __func__);
-               return SR_ERR_ARG;
-       }
+       /* Note: Caller checked that devc and devc->ftdic != NULL. */
 
        sr_dbg("Resetting the device.");
 
@@ -251,15 +245,15 @@ static int cv_reset(struct dev_context *devc)
         * Purge pending read data from the FTDI hardware FIFO until
         * no more data is left, or a timeout occurs (after 20s).
         */
-       done = 20 + time(NULL);
+       done = (20 * G_TIME_SPAN_SECOND) + g_get_monotonic_time();
        do {
                /* Try to read bytes until none are left (or errors occur). */
                bytes_read = cv_read(devc, (uint8_t *)&buf, BS);
-               now = time(NULL);
+               now = g_get_monotonic_time();
        } while ((done > now) && (bytes_read > 0));
 
        /* Reset the sequencer logic and close the USB port. */
-       (void) cv_close_usb_reset_sequencer(devc); /* Ignore errors. */
+       (void) close_usb_reset_sequencer(devc); /* Ignore errors. */
 
        sr_dbg("Device reset finished.");
 
@@ -271,12 +265,13 @@ SR_PRIV int cv_configure_channels(const struct sr_dev_inst *sdi)
        struct dev_context *devc;
        const struct sr_channel *ch;
        const GSList *l;
-       uint8_t channel_bit;
+       uint16_t channel_bit;
        char *tc;
 
        devc = sdi->priv;
-       devc->trigger_pattern = 0;
-       devc->trigger_mask = 0; /* Default to "don't care" for all channels. */
+       devc->trigger_pattern = 0x0000; /* Default to "low" trigger. */
+       devc->trigger_mask = 0x0000; /* Default to "don't care". */
+       devc->trigger_edgemask = 0x0000; /* Default to "state triggered". */
 
        for (l = sdi->channels; l; l = l->next) {
                ch = (struct sr_channel *)l->data;
@@ -295,32 +290,43 @@ SR_PRIV int cv_configure_channels(const struct sr_dev_inst *sdi)
                        continue;
 
                /* Note: Must only be run if ch->trigger != NULL. */
-               if (ch->index < 0 || ch->index > 7) {
-                       sr_err("%s: Invalid channel index %d, must be "
-                              "between 0 and 7.", __func__, ch->index);
+               if (ch->index < 0 || ch->index > (int)devc->prof->num_channels - 1) {
+                       sr_err("Invalid channel index %d, must be "
+                              "between 0 and %d.", ch->index,
+                              devc->prof->num_channels - 1);
                        return SR_ERR;
                }
 
                channel_bit = (1 << (ch->index));
 
-               /* Configure the channel's trigger mask and trigger pattern. */
+               /* Configure the channel's trigger pattern/mask/edgemask. */
                for (tc = ch->trigger; tc && *tc; tc++) {
                        devc->trigger_mask |= channel_bit;
 
                        /* Sanity check, LA8 only supports low/high trigger. */
-                       if (*tc != '0' && *tc != '1') {
-                               sr_err("%s: Invalid trigger '%c', only "
-                                      "'0'/'1' supported.", __func__, *tc);
+                       if ((devc->prof->model == CHRONOVU_LA8) &&
+                           (*tc != '0' && *tc != '1')) {
+                               sr_err("Invalid trigger '%c', only "
+                                      "'0'/'1' supported.", *tc);
                                return SR_ERR;
                        }
 
-                       if (*tc == '1')
+                       /* state: 1 == high, edge: 1 == rising edge. */
+                       if (*tc == '1' || *tc == 'r')
                                devc->trigger_pattern |= channel_bit;
+
+                       /* LA16 (but not LA8) supports edge triggering. */
+                       if ((devc->prof->model == CHRONOVU_LA16)) {
+                               if (*tc == 'r' || *tc == 'f')
+                                       devc->trigger_edgemask |= channel_bit;
+                       }
+
                }
        }
 
-       sr_dbg("Trigger mask = 0x%x, trigger pattern = 0x%x.",
-              devc->trigger_mask, devc->trigger_pattern);
+       sr_dbg("Trigger pattern/mask/edgemask = 0x%04x / 0x%04x / 0x%04x.",
+              devc->trigger_pattern, devc->trigger_mask,
+              devc->trigger_edgemask);
 
        return SR_OK;
 }
@@ -335,16 +341,15 @@ SR_PRIV int cv_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate
 
        sr_spew("Trying to set samplerate to %" PRIu64 "Hz.", samplerate);
 
-       cv_fill_samplerates_if_needed();
+       cv_fill_samplerates_if_needed(sdi);
 
        /* Check if this is a samplerate supported by the hardware. */
-       if (!is_valid_samplerate(samplerate)) {
+       if (!is_valid_samplerate(sdi, samplerate)) {
                sr_dbg("Failed to set invalid samplerate (%" PRIu64 "Hz).",
                       samplerate);
                return SR_ERR;
        }
 
-       /* Set the new samplerate. */
        devc->cur_samplerate = samplerate;
 
        sr_dbg("Samplerate set to %" PRIu64 "Hz.", devc->cur_samplerate);
@@ -362,8 +367,8 @@ SR_PRIV int cv_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate
  */
 SR_PRIV int cv_read_block(struct dev_context *devc)
 {
-       int i, byte_offset, m, mi, p, index, bytes_read;
-       time_t now;
+       int i, byte_offset, m, mi, p, q, index, bytes_read;
+       gint64 now;
 
        /* Note: Caller checked that devc and devc->ftdic != NULL. */
 
@@ -375,16 +380,16 @@ SR_PRIV int cv_read_block(struct dev_context *devc)
        if ((bytes_read == 0) && (devc->block_counter == 0)) {
                do {
                        sr_spew("Reading block 0 (again).");
+                       /* Note: If bytes_read < 0 cv_read() will log errors. */
                        bytes_read = cv_read(devc, devc->mangled_buf, BS);
-                       /* TODO: How to handle read errors here? */
-                       now = time(NULL);
+                       now = g_get_monotonic_time();
                } while ((devc->done > now) && (bytes_read == 0));
        }
 
        /* Check if block read was successful or a timeout occured. */
        if (bytes_read != BS) {
                sr_err("Trigger timed out. Bytes read: %d.", bytes_read);
-               (void) cv_reset(devc); /* Ignore errors. */
+               (void) reset_device(devc); /* Ignore errors. */
                return SR_ERR;
        }
 
@@ -394,9 +399,16 @@ SR_PRIV int cv_read_block(struct dev_context *devc)
        m = byte_offset / (1024 * 1024);
        mi = m * (1024 * 1024);
        for (i = 0; i < BS; i++) {
-               p = i & (1 << 0);
-               index = m * 2 + (((byte_offset + i) - mi) / 2) * 16;
-               index += (devc->divcount == 0) ? p : (1 - p);
+               if (devc->prof->model == CHRONOVU_LA8) {
+                       p = i & (1 << 0);
+                       index = m * 2 + (((byte_offset + i) - mi) / 2) * 16;
+                       index += (devc->divcount == 0) ? p : (1 - p);
+               } else {
+                       p = i & (1 << 0);
+                       q = i & (1 << 1);
+                       index = m * 4 + (((byte_offset + i) - mi) / 4) * 32;
+                       index += q + (1 - p);
+               }
                devc->final_buf[index] = devc->mangled_buf[i];
        }
 
@@ -411,7 +423,9 @@ SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block)
        struct sr_datafeed_logic logic;
        int trigger_point; /* Relative trigger point (in this block). */
 
-       /* Note: No sanity checks on devc/block, caller is responsible. */
+       /* Note: Caller ensures devc/devc->ftdic != NULL and block > 0. */
+
+       /* TODO: Implement/test proper trigger support for the LA16. */
 
        /* Check if we can find the trigger condition in this block. */
        trigger_point = -1;
@@ -426,7 +440,7 @@ SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block)
                 * no trigger conditions were specified by the user. In that
                 * case we don't want to send an SR_DF_TRIGGER packet at all.
                 */
-               if (devc->trigger_mask == 0x00)
+               if (devc->trigger_mask == 0x0000)
                        break;
 
                sample = *(devc->final_buf + (block * BS) + i);
@@ -446,7 +460,7 @@ SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block)
                packet.type = SR_DF_LOGIC;
                packet.payload = &logic;
                logic.length = BS;
-               logic.unitsize = 1;
+               logic.unitsize = devc->prof->num_channels / 8;
                logic.data = devc->final_buf + (block * BS);
                sr_session_send(devc->cb_data, &packet);
                return;
@@ -469,7 +483,7 @@ SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block)
                packet.type = SR_DF_LOGIC;
                packet.payload = &logic;
                logic.length = trigger_point;
-               logic.unitsize = 1;
+               logic.unitsize = devc->prof->num_channels / 8;
                logic.data = devc->final_buf + (block * BS);
                sr_session_send(devc->cb_data, &packet);
        }
@@ -490,7 +504,7 @@ SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block)
                packet.type = SR_DF_LOGIC;
                packet.payload = &logic;
                logic.length = BS - trigger_point;
-               logic.unitsize = 1;
+               logic.unitsize = devc->prof->num_channels / 8;
                logic.data = devc->final_buf + (block * BS) + trigger_point;
                sr_session_send(devc->cb_data, &packet);
        }
index b44ca8165b43cbafbf2719ce895cebdbec3c4963..2107eff5c9e004fd1d485b5da967f500c66a8703 100644 (file)
 #include <glib.h>
 #include <ftdi.h>
 #include <stdint.h>
+#include <string.h>
 #include "libsigrok.h"
 #include "libsigrok-internal.h"
 
-#define LOG_PREFIX "chronovu-la8"
+#define LOG_PREFIX "la8/la16"
 
-#define USB_VENDOR_ID                  0x0403
-#define USB_DESCRIPTION                        "ChronoVu LA8"
-#define USB_VENDOR_NAME                        "ChronoVu"
-#define USB_MODEL_NAME                 "LA8"
-#define USB_MODEL_VERSION              ""
-
-#define NUM_CHANNELS                   8
-#define TRIGGER_TYPE                   "01"
 #define SDRAM_SIZE                     (8 * 1024 * 1024)
 #define MAX_NUM_SAMPLES                        SDRAM_SIZE
 
 #define BS                             4096 /* Block size */
 #define NUM_BLOCKS                     2048 /* Number of blocks */
 
+enum {
+       CHRONOVU_LA8,
+       CHRONOVU_LA16,
+};
+
+struct cv_profile {
+       int model;
+       const char *modelname;
+       const char *iproduct; /* USB iProduct string */
+       unsigned int num_channels;
+       uint64_t max_samplerate;
+       const char *trigger_type;
+       float trigger_constant;
+};
+
 /* Private, per-device-instance driver context. */
 struct dev_context {
+       /** Device profile struct for this device. */
+       const struct cv_profile *prof;
+
        /** FTDI device context (used by libftdi). */
        struct ftdi_context *ftdic;
 
@@ -67,31 +78,39 @@ struct dev_context {
 
        /**
         * An 8MB buffer where we'll store the de-mangled samples.
-        * Format: Each sample is 1 byte, MSB is channel 7, LSB is channel 0.
+        * LA8: Each sample is 1 byte, MSB is channel 7, LSB is channel 0.
+        * LA16: Each sample is 2 bytes, MSB is channel 15, LSB is channel 0.
         */
        uint8_t *final_buf;
 
        /**
-        * Trigger pattern (MSB = channel 7, LSB = channel 0).
+        * Trigger pattern.
         * A 1 bit matches a high signal, 0 matches a low signal on a channel.
-        * Only low/high triggers (but not e.g. rising/falling) are supported.
+        *
+        * If the resp. 'trigger_edgemask' bit is set, 1 means "rising edge",
+        * and 0 means "falling edge".
         */
-       uint8_t trigger_pattern;
+       uint16_t trigger_pattern;
 
        /**
-        * Trigger mask (MSB = channel 7, LSB = channel 0).
+        * Trigger mask.
         * A 1 bit means "must match trigger_pattern", 0 means "don't care".
         */
-       uint8_t trigger_mask;
+       uint16_t trigger_mask;
 
-       /** Time (in seconds) before the trigger times out. */
-       uint64_t trigger_timeout;
+       /**
+        * Trigger edge mask.
+        * A 1 bit means "edge triggered", 0 means "state triggered".
+        *
+        * Edge triggering is only supported on LA16 (but not LA8).
+        */
+       uint16_t trigger_edgemask;
 
        /** Tells us whether an SR_DF_TRIGGER packet was already sent. */
        int trigger_found;
 
        /** Used for keeping track how much time has passed. */
-       time_t done;
+       gint64 done;
 
        /** Counter/index for the data block to be read. */
        int block_counter;
@@ -99,19 +118,21 @@ struct dev_context {
        /** The divcount value (determines the sample period). */
        uint8_t divcount;
 
-       /** This ChronoVu device's USB PID. */
+       /** This ChronoVu device's USB VID/PID. */
+       uint16_t usb_vid;
        uint16_t usb_pid;
+
+       /** Samplerates supported by this device. */
+       uint64_t samplerates[255];
 };
 
 /* protocol.c */
-extern const int32_t cv_hwcaps[];
-extern uint64_t cv_samplerates[];
 extern SR_PRIV const char *cv_channel_names[];
-SR_PRIV void cv_fill_samplerates_if_needed(void);
-SR_PRIV uint8_t cv_samplerate_to_divcount(uint64_t samplerate);
+extern const struct cv_profile cv_profiles[];
+SR_PRIV void cv_fill_samplerates_if_needed(const struct sr_dev_inst *sdi);
+SR_PRIV uint8_t cv_samplerate_to_divcount(const struct sr_dev_inst *sdi,
+                                         uint64_t samplerate);
 SR_PRIV int cv_write(struct dev_context *devc, uint8_t *buf, int size);
-SR_PRIV int cv_close(struct dev_context *devc);
-SR_PRIV int cv_close_usb_reset_sequencer(struct dev_context *devc);
 SR_PRIV int cv_configure_channels(const struct sr_dev_inst *sdi);
 SR_PRIV int cv_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate);
 SR_PRIV int cv_read_block(struct dev_context *devc);