]> sigrok.org Git - libsigrok.git/commitdiff
Rename 'chronovu-la8' driver to 'chronovu-la'.
authorUwe Hermann <redacted>
Tue, 1 Apr 2014 19:47:11 +0000 (21:47 +0200)
committerUwe Hermann <redacted>
Wed, 2 Apr 2014 15:20:09 +0000 (17:20 +0200)
The driver now also supports the LA16 device (and possibly others in the
future).

Makefile.am
README.devices
configure.ac
hardware/chronovu-la/api.c [new file with mode: 0644]
hardware/chronovu-la/protocol.c [new file with mode: 0644]
hardware/chronovu-la/protocol.h [new file with mode: 0644]
hardware/chronovu-la8/api.c [deleted file]
hardware/chronovu-la8/protocol.c [deleted file]
hardware/chronovu-la8/protocol.h [deleted file]
hwdriver.c

index 7902c99079eccbc246d02b6550468f0416119276..81a8176d2225c1821cedb83660ae7ea580c59db2 100644 (file)
@@ -156,11 +156,11 @@ libsigrok_la_SOURCES += \
        hardware/center-3xx/protocol.c \
        hardware/center-3xx/api.c
 endif
        hardware/center-3xx/protocol.c \
        hardware/center-3xx/api.c
 endif
-if HW_CHRONOVU_LA8
+if HW_CHRONOVU_LA
 libsigrok_la_SOURCES += \
 libsigrok_la_SOURCES += \
-       hardware/chronovu-la8/protocol.h \
-       hardware/chronovu-la8/protocol.c \
-       hardware/chronovu-la8/api.c
+       hardware/chronovu-la/protocol.h \
+       hardware/chronovu-la/protocol.c \
+       hardware/chronovu-la/api.c
 endif
 if HW_COLEAD_SLM
 libsigrok_la_SOURCES += \
 endif
 if HW_COLEAD_SLM
 libsigrok_la_SOURCES += \
index 30f8e474d7eb2e9130f9f1f3fb9fb3a8b3d07597..554805a948c2e554913c2a9eabdeeca9919c3252 100644 (file)
@@ -56,7 +56,7 @@ The following drivers/devices do not need any firmware upload:
  - brymen-dmm
  - cem-dt-885x
  - center-3xx (including all subdrivers)
  - brymen-dmm
  - cem-dt-885x
  - center-3xx (including all subdrivers)
- - chronovu-la8
+ - chronovu-la
  - colead-slm
  - demo
  - fluke-dmm
  - colead-slm
  - demo
  - fluke-dmm
@@ -119,7 +119,7 @@ The following drivers/devices do not require a serial port specification:
  - alsa
  - asix-sigma
  - brymen-bm86x
  - alsa
  - asix-sigma
  - brymen-bm86x
- - chronovu-la8
+ - chronovu-la
  - demo
  - fx2lafw
  - hantek-dso
  - demo
  - fx2lafw
  - hantek-dso
@@ -340,20 +340,19 @@ system). Thus, this driver can currently only compile and work on Linux.
 We plan to fix this though, so that it'll work on all other OSes, too.
 
 
 We plan to fix this though, so that it'll work on all other OSes, too.
 
 
-ChronoVu LA8 USB VID/PIDs
--------------------------
+ChronoVu LA8/LA16 USB VID/PIDs
+------------------------------
 
 
-The ChronoVu LA8 logic analyzer is available in two revisions. Previously,
-the LA8 shipped with a USB VID/PID of 0403:6001, which is the standard ID
+The ChronoVu LA8/LA16 logic analyzer is available in two revisions. Previously,
+the device shipped with a USB VID/PID of 0403:6001, which is the standard ID
 for FTDI FT232 USB chips.
 
 for FTDI FT232 USB chips.
 
-Since this made it hard to distinguish the LA8 from any other device
+Since this made it hard to distinguish the LA8/LA16 from any other device
 with this FTDI chip connected to the PC, the vendor later shipped the
 with this FTDI chip connected to the PC, the vendor later shipped the
-LA8 with a USB VID/PID of 0403:8867.
+device with a USB VID/PID of 0403:8867.
 
 
-The 'chronovu-la8' driver in libsigrok supports both VID/PID pairs and
-automatically finds devices with either VID/PID pair. However, currently
-the driver will assume any device with VID/PID 0403:6001 is a ChronoVu LA8.
+The 'chronovu-la' driver in libsigrok supports both VID/PID pairs and
+automatically finds devices with either VID/PID pair.
 
 
 OLS
 
 
 OLS
index a7eb8f569d25cb1306286c069d64a9cbf01c348e..b76110fe5f6d13223deea4ec3af06641ca2a9874 100644 (file)
@@ -129,10 +129,10 @@ AC_ARG_ENABLE(center-3xx, AC_HELP_STRING([--enable-center-3xx],
        [HW_CENTER_3XX="$enableval"],
        [HW_CENTER_3XX=$HW_ENABLED_DEFAULT])
 
        [HW_CENTER_3XX="$enableval"],
        [HW_CENTER_3XX=$HW_ENABLED_DEFAULT])
 
-AC_ARG_ENABLE(chronovu-la8, AC_HELP_STRING([--enable-chronovu-la8],
-       [enable ChronoVu LA8 support [default=yes]]),
-       [HW_CHRONOVU_LA8="$enableval"],
-       [HW_CHRONOVU_LA8=$HW_ENABLED_DEFAULT])
+AC_ARG_ENABLE(chronovu-la, AC_HELP_STRING([--enable-chronovu-la],
+       [enable ChronoVu LA support [default=yes]]),
+       [HW_CHRONOVU_LA="$enableval"],
+       [HW_CHRONOVU_LA=$HW_ENABLED_DEFAULT])
 
 AC_ARG_ENABLE(colead-slm, AC_HELP_STRING([--enable-colead-slm],
        [enable Colead SLM support [default=yes]]),
 
 AC_ARG_ENABLE(colead-slm, AC_HELP_STRING([--enable-colead-slm],
        [enable Colead SLM support [default=yes]]),
@@ -379,7 +379,7 @@ PKG_CHECK_MODULES([libftdi], [libftdi >= 0.16],
        [CFLAGS="$CFLAGS $libftdi_CFLAGS";
        LIBS="$LIBS $libftdi_LIBS";
        SR_PKGLIBS="$SR_PKGLIBS libftdi"],
        [CFLAGS="$CFLAGS $libftdi_CFLAGS";
        LIBS="$LIBS $libftdi_LIBS";
        SR_PKGLIBS="$SR_PKGLIBS libftdi"],
-       [HW_ASIX_SIGMA="no"; HW_CHRONOVU_LA8="no"; HW_IKALOGIC_SCANAPLUS="no"])
+       [HW_ASIX_SIGMA="no"; HW_CHRONOVU_LA="no"; HW_IKALOGIC_SCANAPLUS="no"])
 
 # libudev is only needed for some hardware drivers. Disable them if not found.
 PKG_CHECK_MODULES([libudev], [libudev >= 151],
 
 # libudev is only needed for some hardware drivers. Disable them if not found.
 PKG_CHECK_MODULES([libudev], [libudev >= 151],
@@ -456,9 +456,9 @@ if test "x$HW_CENTER_3XX" = "xyes"; then
        AC_DEFINE(HAVE_HW_CENTER_3XX, 1, [Center 3xx support])
 fi
 
        AC_DEFINE(HAVE_HW_CENTER_3XX, 1, [Center 3xx support])
 fi
 
-AM_CONDITIONAL(HW_CHRONOVU_LA8, test x$HW_CHRONOVU_LA8 = xyes)
-if test "x$HW_CHRONOVU_LA8" = "xyes"; then
-       AC_DEFINE(HAVE_HW_CHRONOVU_LA8, 1, [ChronoVu LA8 support])
+AM_CONDITIONAL(HW_CHRONOVU_LA, test x$HW_CHRONOVU_LA = xyes)
+if test "x$HW_CHRONOVU_LA" = "xyes"; then
+       AC_DEFINE(HAVE_HW_CHRONOVU_LA, 1, [ChronoVu LA support])
 fi
 
 AM_CONDITIONAL(HW_COLEAD_SLM, test x$HW_COLEAD_SLM = xyes)
 fi
 
 AM_CONDITIONAL(HW_COLEAD_SLM, test x$HW_COLEAD_SLM = xyes)
@@ -648,7 +648,7 @@ echo "  - brymen-bm86x.................... $HW_BRYMEN_BM86X"
 echo "  - brymen-dmm...................... $HW_BRYMEN_DMM"
 echo "  - cem-dt-885x..................... $HW_CEM_DT_885X"
 echo "  - center-3xx...................... $HW_CENTER_3XX"
 echo "  - brymen-dmm...................... $HW_BRYMEN_DMM"
 echo "  - cem-dt-885x..................... $HW_CEM_DT_885X"
 echo "  - center-3xx...................... $HW_CENTER_3XX"
-echo "  - chronovu-la8.................... $HW_CHRONOVU_LA8"
+echo "  - chronovu-la..................... $HW_CHRONOVU_LA"
 echo "  - colead-slm...................... $HW_COLEAD_SLM"
 echo "  - conrad-digi-35-cpu.............. $HW_CONRAD_DIGI_35_CPU"
 echo "  - demo............................ $HW_DEMO"
 echo "  - colead-slm...................... $HW_COLEAD_SLM"
 echo "  - conrad-digi-35-cpu.............. $HW_CONRAD_DIGI_35_CPU"
 echo "  - demo............................ $HW_DEMO"
diff --git a/hardware/chronovu-la/api.c b/hardware/chronovu-la/api.c
new file mode 100644 (file)
index 0000000..d783f4f
--- /dev/null
@@ -0,0 +1,532 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2011-2014 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include "protocol.h"
+
+SR_PRIV struct sr_dev_driver chronovu_la_driver_info;
+static struct sr_dev_driver *di = &chronovu_la_driver_info;
+
+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/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);
+
+static void clear_helper(void *priv)
+{
+       struct dev_context *devc;
+
+       devc = priv;
+
+       ftdi_free(devc->ftdic);
+       g_free(devc->final_buf);
+}
+
+static int dev_clear(void)
+{
+       return std_dev_clear(di, clear_helper);
+}
+
+static int init(struct sr_context *sr_ctx)
+{
+       return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static int add_device(int idx, int model, GSList **devices)
+{
+       int ret;
+       unsigned int i;
+       struct sr_dev_inst *sdi;
+       struct drv_context *drvc;
+       struct dev_context *devc;
+       struct sr_channel *ch;
+
+       ret = SR_OK;
+
+       drvc = di->priv;
+
+       /* Allocate memory for our private device context. */
+       devc = g_try_malloc(sizeof(struct dev_context));
+
+       /* Set some sane defaults. */
+       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 = 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;
+       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;
+       }
+
+       /* 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,
+                             "ChronoVu", devc->prof->modelname, NULL);
+       if (!sdi) {
+               sr_err("Failed to create device instance.");
+               ret = SR_ERR;
+               goto err_free_final_buf;
+       }
+       sdi->driver = di;
+       sdi->priv = devc;
+
+       for (i = 0; i < devc->prof->num_channels; i++) {
+               if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
+                                         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);
+       drvc->instances = g_slist_append(drvc->instances, sdi);
+
+       return SR_OK;
+
+err_free_dev_inst:
+       sr_dev_inst_free(sdi);
+err_free_final_buf:
+       g_free(devc->final_buf);
+err_free_devc:
+       g_free(devc);
+
+       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)
+{
+       return ((struct drv_context *)(di->priv))->instances;
+}
+
+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;
+
+       /* 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, 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));
+               goto err_ftdi_free;
+       }
+       sr_dbg("Device opened successfully.");
+
+       /* Purge RX/TX buffers in the FTDI chip. */
+       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));
+               goto err_ftdi_free;
+       }
+       sr_dbg("FTDI buffers purged successfully.");
+
+       /* Enable flow control in the FTDI chip. */
+       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));
+               goto err_ftdi_free;
+       }
+       sr_dbg("FTDI flow control enabled successfully.");
+
+       /* Wait 100ms. */
+       g_usleep(100 * 1000);
+
+       sdi->status = SR_ST_ACTIVE;
+
+       return SR_OK;
+
+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;
+
+       if (sdi->status != SR_ST_ACTIVE)
+               return SR_OK;
+
+       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;
+}
+
+static int cleanup(void)
+{
+       return dev_clear();
+}
+
+static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+               const struct sr_channel_group *cg)
+{
+       struct dev_context *devc;
+
+       (void)cg;
+
+       switch (id) {
+       case SR_CONF_SAMPLERATE:
+               if (!sdi || !(devc = sdi->priv))
+                       return SR_ERR_BUG;
+               *data = g_variant_new_uint64(devc->cur_samplerate);
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
+               const struct sr_channel_group *cg)
+{
+       struct dev_context *devc;
+
+       (void)cg;
+
+       if (sdi->status != SR_ST_ACTIVE)
+               return SR_ERR_DEV_CLOSED;
+
+       if (!(devc = sdi->priv))
+               return SR_ERR_BUG;
+
+       switch (id) {
+       case SR_CONF_SAMPLERATE:
+               if (cv_set_samplerate(sdi, g_variant_get_uint64(data)) < 0)
+                       return SR_ERR;
+               break;
+       case SR_CONF_LIMIT_MSEC:
+               if (g_variant_get_uint64(data) == 0)
+                       return SR_ERR_ARG;
+               devc->limit_msec = g_variant_get_uint64(data);
+               break;
+       case SR_CONF_LIMIT_SAMPLES:
+               if (g_variant_get_uint64(data) == 0)
+                       return SR_ERR_ARG;
+               devc->limit_samples = g_variant_get_uint64(data);
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+               const struct sr_channel_group *cg)
+{
+       GVariant *gvar, *grange[2];
+       GVariantBuilder gvb;
+       struct dev_context *devc;
+
+       (void)cg;
+
+       switch (key) {
+       case SR_CONF_DEVICE_OPTIONS:
+               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+                               hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+               break;
+       case SR_CONF_SAMPLERATE:
+               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"),
+                               devc->samplerates,
+                               ARRAY_SIZE(devc->samplerates),
+                               sizeof(uint64_t));
+               g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
+               *data = g_variant_builder_end(&gvb);
+               break;
+       case SR_CONF_LIMIT_SAMPLES:
+               grange[0] = g_variant_new_uint64(0);
+               grange[1] = g_variant_new_uint64(MAX_NUM_SAMPLES);
+               *data = g_variant_new_tuple(grange, 2);
+               break;
+       case SR_CONF_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;
+       }
+
+       return SR_OK;
+}
+
+static int receive_data(int fd, int revents, void *cb_data)
+{
+       int i, ret;
+       struct sr_dev_inst *sdi;
+       struct dev_context *devc;
+
+       (void)fd;
+       (void)revents;
+
+       if (!(sdi = cb_data)) {
+               sr_err("cb_data was NULL.");
+               return FALSE;
+       }
+
+       if (!(devc = sdi->priv)) {
+               sr_err("sdi->priv was NULL.");
+               return FALSE;
+       }
+
+       if (!devc->ftdic) {
+               sr_err("devc->ftdic was NULL.");
+               return FALSE;
+       }
+
+       /* Get one block of data. */
+       if ((ret = cv_read_block(devc)) < 0) {
+               sr_err("Failed to read data block: %d.", ret);
+               dev_acquisition_stop(sdi, sdi);
+               return FALSE;
+       }
+
+       /* We need to get exactly NUM_BLOCKS blocks (i.e. 8MB) of data. */
+       if (devc->block_counter != (NUM_BLOCKS - 1)) {
+               devc->block_counter++;
+               return TRUE;
+       }
+
+       sr_dbg("Sampling finished, sending data to session bus now.");
+
+       /* All data was received and demangled, send it to the session bus. */
+       for (i = 0; i < NUM_BLOCKS; i++)
+               cv_send_block_to_session_bus(devc, i);
+
+       dev_acquisition_stop(sdi, sdi);
+
+       return TRUE;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+       struct dev_context *devc;
+       uint8_t buf[8];
+       int bytes_to_write, bytes_written;
+
+       if (sdi->status != SR_ST_ACTIVE)
+               return SR_ERR_DEV_CLOSED;
+
+       if (!(devc = sdi->priv)) {
+               sr_err("sdi->priv was NULL.");
+               return SR_ERR_BUG;
+       }
+
+       if (!devc->ftdic) {
+               sr_err("devc->ftdic was NULL.");
+               return SR_ERR_BUG;
+       }
+
+       devc->divcount = cv_samplerate_to_divcount(sdi, devc->cur_samplerate);
+       if (devc->divcount == 0xff) {
+               sr_err("Invalid divcount/samplerate.");
+               return SR_ERR;
+       }
+
+       if (cv_configure_channels(sdi) != SR_OK) {
+               sr_err("Failed to configure channels.");
+               return SR_ERR;
+       }
+
+       /* Fill acquisition parameters into buf[]. */
+       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, bytes_to_write);
+
+       if (bytes_written < 0 || bytes_written != bytes_to_write) {
+               sr_err("Acquisition failed to start.");
+               return SR_ERR;
+       }
+
+       sr_dbg("Hardware acquisition started successfully.");
+
+       devc->cb_data = cb_data;
+
+       /* Send header packet to the session bus. */
+       std_session_send_df_header(cb_data, LOG_PREFIX);
+
+       /* Time when we should be done (for detecting trigger timeouts). */
+       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;
+
+       /* Hook up a dummy handler to receive data from the device. */
+       sr_source_add(-1, G_IO_IN, 0, receive_data, (void *)sdi);
+
+       return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+       struct sr_datafeed_packet packet;
+
+       (void)sdi;
+
+       sr_dbg("Stopping acquisition.");
+       sr_source_remove(-1);
+
+       /* Send end packet to the session bus. */
+       sr_dbg("Sending SR_DF_END.");
+       packet.type = SR_DF_END;
+       sr_session_send(cb_data, &packet);
+
+       return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver chronovu_la_driver_info = {
+       .name = "chronovu-la",
+       .longname = "ChronoVu LA8/LA16",
+       .api_version = 1,
+       .init = init,
+       .cleanup = cleanup,
+       .scan = scan,
+       .dev_list = dev_list,
+       .dev_clear = dev_clear,
+       .config_get = config_get,
+       .config_set = config_set,
+       .config_list = config_list,
+       .dev_open = dev_open,
+       .dev_close = dev_close,
+       .dev_acquisition_start = dev_acquisition_start,
+       .dev_acquisition_stop = dev_acquisition_stop,
+       .priv = NULL,
+};
diff --git a/hardware/chronovu-la/protocol.c b/hardware/chronovu-la/protocol.c
new file mode 100644 (file)
index 0000000..28ac15b
--- /dev/null
@@ -0,0 +1,511 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2011-2014 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include "protocol.h"
+
+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",
+       "8", "9", "10", "11", "12", "13", "14", "15",
+};
+
+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 (devc->samplerates[0] != 0)
+               return;
+
+       for (i = 0; i < 255; i++)
+               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(const struct sr_dev_inst *sdi,
+                              uint64_t samplerate)
+{
+       int i;
+       struct dev_context *devc;
+
+       devc = sdi->priv;
+
+       cv_fill_samplerates_if_needed(sdi);
+
+       for (i = 0; i < 255; i++) {
+               if (devc->samplerates[i] == samplerate)
+                       return 1;
+       }
+
+       sr_err("Invalid samplerate (%" PRIu64 "Hz).", samplerate);
+
+       return 0;
+}
+
+/**
+ * Convert a samplerate (in Hz) to the 'divcount' value the device wants.
+ *
+ * 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(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(sdi, samplerate)) {
+               sr_err("Can't get divcount, samplerate invalid.");
+               return 0xff;
+       }
+
+       return (devc->prof->max_samplerate / samplerate) - 1;
+}
+
+/**
+ * Write data of a certain length to the FTDI device.
+ *
+ * @param devc The struct containing private per-device-instance data. Must not
+ *             be NULL. devc->ftdic must not be NULL either.
+ * @param buf The buffer containing the data to write. Must not be NULL.
+ * @param size The number of bytes to write. Must be > 0.
+ *
+ * @return The number of bytes written, or a negative value upon errors.
+ */
+SR_PRIV int cv_write(struct dev_context *devc, uint8_t *buf, int size)
+{
+       int bytes_written;
+
+       /* Note: Caller ensures devc/devc->ftdic/buf != NULL and size > 0. */
+
+       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) 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) close_usb_reset_sequencer(devc); /* Ignore errors. */
+       }
+
+       return bytes_written;
+}
+
+/**
+ * Read a certain amount of bytes from the FTDI device.
+ *
+ * @param devc The struct containing private per-device-instance data. Must not
+ *             be NULL. devc->ftdic must not be NULL either.
+ * @param buf The buffer where the received data will be stored. Must not
+ *            be NULL.
+ * @param size The number of bytes to read. Must be >= 1.
+ *
+ * @return The number of bytes read, or a negative value upon errors.
+ */
+static int cv_read(struct dev_context *devc, uint8_t *buf, int size)
+{
+       int bytes_read;
+
+       /* Note: Caller ensures devc/devc->ftdic/buf != NULL and size > 0. */
+
+       bytes_read = ftdi_read_data(devc->ftdic, buf, size);
+
+       if (bytes_read < 0) {
+               sr_err("Failed to read data (%d): %s.",
+                      bytes_read, ftdi_get_error_string(devc->ftdic));
+       } else if (bytes_read != size) {
+               // sr_err("Failed to read data, only %d/%d bytes read.",
+               //        bytes_read, size);
+       }
+
+       return bytes_read;
+}
+
+/**
+ * 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.
+ */
+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;
+
+       /* Note: Caller checked that devc and devc->ftdic != NULL. */
+
+       if (devc->ftdic->usb_dev) {
+               /* Reset the sequencer logic, then wait 100ms. */
+               sr_dbg("Resetting sequencer logic.");
+               (void) cv_write(devc, buf, 8); /* Ignore errors. */
+               g_usleep(100 * 1000);
+
+               /* Purge FTDI buffers, then reset and close the FTDI device. */
+               sr_dbg("Purging buffers, resetting+closing FTDI device.");
+
+               /* Log errors, but ignore them (i.e., don't abort). */
+               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));
+               if ((ret = ftdi_usb_reset(devc->ftdic)) < 0)
+                       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("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);
+       devc->ftdic = NULL;
+
+       return SR_OK;
+}
+
+/**
+ * Reset the ChronoVu device.
+ *
+ * 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 reset_device(struct dev_context *devc)
+{
+       uint8_t buf[BS];
+       gint64 done, now;
+       int bytes_read;
+
+       /* Note: Caller checked that devc and devc->ftdic != NULL. */
+
+       sr_dbg("Resetting the device.");
+
+       /*
+        * Purge pending read data from the FTDI hardware FIFO until
+        * no more data is left, or a timeout occurs (after 20s).
+        */
+       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 = g_get_monotonic_time();
+       } while ((done > now) && (bytes_read > 0));
+
+       /* Reset the sequencer logic and close the USB port. */
+       (void) close_usb_reset_sequencer(devc); /* Ignore errors. */
+
+       sr_dbg("Device reset finished.");
+
+       return SR_OK;
+}
+
+SR_PRIV int cv_configure_channels(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       const struct sr_channel *ch;
+       const GSList *l;
+       uint16_t channel_bit;
+       char *tc;
+
+       devc = sdi->priv;
+       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;
+
+               if (!ch) {
+                       sr_err("%s: channel was NULL.", __func__);
+                       return SR_ERR;
+               }
+
+               /* Skip disabled channels. */
+               if (!ch->enabled)
+                       continue;
+
+               /* Skip (enabled) channels with no configured trigger. */
+               if (!ch->trigger)
+                       continue;
+
+               /* Note: Must only be run if ch->trigger != NULL. */
+               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 pattern/mask/edgemask. */
+               for (tc = ch->trigger; tc && *tc; tc++) {
+                       devc->trigger_mask |= channel_bit;
+
+                       /* Sanity check, LA8 only supports low/high trigger. */
+                       if ((devc->prof->model == CHRONOVU_LA8) &&
+                           (*tc != '0' && *tc != '1')) {
+                               sr_err("Invalid trigger '%c', only "
+                                      "'0'/'1' supported.", *tc);
+                               return SR_ERR;
+                       }
+
+                       /* 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 pattern/mask/edgemask = 0x%04x / 0x%04x / 0x%04x.",
+              devc->trigger_pattern, devc->trigger_mask,
+              devc->trigger_edgemask);
+
+       return SR_OK;
+}
+
+SR_PRIV int cv_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate)
+{
+       struct dev_context *devc;
+
+       /* Note: Caller checked that sdi and sdi->priv != NULL. */
+
+       devc = sdi->priv;
+
+       sr_spew("Trying to set samplerate to %" PRIu64 "Hz.", samplerate);
+
+       cv_fill_samplerates_if_needed(sdi);
+
+       /* Check if this is a samplerate supported by the hardware. */
+       if (!is_valid_samplerate(sdi, samplerate)) {
+               sr_dbg("Failed to set invalid samplerate (%" PRIu64 "Hz).",
+                      samplerate);
+               return SR_ERR;
+       }
+
+       devc->cur_samplerate = samplerate;
+
+       sr_dbg("Samplerate set to %" PRIu64 "Hz.", devc->cur_samplerate);
+
+       return SR_OK;
+}
+
+/**
+ * Get a block of data from the device.
+ *
+ * @param devc The struct containing private per-device-instance data. Must not
+ *             be NULL. devc->ftdic must not be NULL either.
+ *
+ * @return SR_OK upon success, or SR_ERR upon errors.
+ */
+SR_PRIV int cv_read_block(struct dev_context *devc)
+{
+       int i, byte_offset, m, mi, p, q, index, bytes_read;
+       gint64 now;
+
+       /* Note: Caller checked that devc and devc->ftdic != NULL. */
+
+       sr_spew("Reading block %d.", devc->block_counter);
+
+       bytes_read = cv_read(devc, devc->mangled_buf, BS);
+
+       /* If first block read got 0 bytes, retry until success or timeout. */
+       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);
+                       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) reset_device(devc); /* Ignore errors. */
+               return SR_ERR;
+       }
+
+       /* De-mangle the data. */
+       sr_spew("Demangling block %d.", devc->block_counter);
+       byte_offset = devc->block_counter * BS;
+       m = byte_offset / (1024 * 1024);
+       mi = m * (1024 * 1024);
+       for (i = 0; i < BS; i++) {
+               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];
+       }
+
+       return SR_OK;
+}
+
+SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block)
+{
+       int i;
+       uint8_t sample, expected_sample;
+       struct sr_datafeed_packet packet;
+       struct sr_datafeed_logic logic;
+       int trigger_point; /* Relative trigger point (in this block). */
+
+       /* 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;
+       expected_sample = devc->trigger_pattern & devc->trigger_mask;
+       for (i = 0; i < BS; i++) {
+               /* Don't continue if the trigger was found previously. */
+               if (devc->trigger_found)
+                       break;
+
+               /*
+                * Also, don't continue if triggers are "don't care", i.e. if
+                * 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 == 0x0000)
+                       break;
+
+               sample = *(devc->final_buf + (block * BS) + i);
+
+               if ((sample & devc->trigger_mask) == expected_sample) {
+                       trigger_point = i;
+                       devc->trigger_found = 1;
+                       break;
+               }
+       }
+
+       /* If no trigger was found, send one SR_DF_LOGIC packet. */
+       if (trigger_point == -1) {
+               /* Send an SR_DF_LOGIC packet to the session bus. */
+               sr_spew("Sending SR_DF_LOGIC packet (%d bytes) for "
+                       "block %d.", BS, block);
+               packet.type = SR_DF_LOGIC;
+               packet.payload = &logic;
+               logic.length = BS;
+               logic.unitsize = devc->prof->num_channels / 8;
+               logic.data = devc->final_buf + (block * BS);
+               sr_session_send(devc->cb_data, &packet);
+               return;
+       }
+
+       /*
+        * We found the trigger, so some special handling is needed. We have
+        * to send an SR_DF_LOGIC packet with the samples before the trigger
+        * (if any), then the SD_DF_TRIGGER packet itself, then another
+        * SR_DF_LOGIC packet with the samples after the trigger (if any).
+        */
+
+       /* TODO: Send SR_DF_TRIGGER packet before or after the actual sample? */
+
+       /* If at least one sample is located before the trigger... */
+       if (trigger_point > 0) {
+               /* Send pre-trigger SR_DF_LOGIC packet to the session bus. */
+               sr_spew("Sending pre-trigger SR_DF_LOGIC packet, "
+                       "start = %d, length = %d.", block * BS, trigger_point);
+               packet.type = SR_DF_LOGIC;
+               packet.payload = &logic;
+               logic.length = trigger_point;
+               logic.unitsize = devc->prof->num_channels / 8;
+               logic.data = devc->final_buf + (block * BS);
+               sr_session_send(devc->cb_data, &packet);
+       }
+
+       /* Send the SR_DF_TRIGGER packet to the session bus. */
+       sr_spew("Sending SR_DF_TRIGGER packet, sample = %d.",
+               (block * BS) + trigger_point);
+       packet.type = SR_DF_TRIGGER;
+       packet.payload = NULL;
+       sr_session_send(devc->cb_data, &packet);
+
+       /* If at least one sample is located after the trigger... */
+       if (trigger_point < (BS - 1)) {
+               /* Send post-trigger SR_DF_LOGIC packet to the session bus. */
+               sr_spew("Sending post-trigger SR_DF_LOGIC packet, "
+                       "start = %d, length = %d.",
+                       (block * BS) + trigger_point, BS - trigger_point);
+               packet.type = SR_DF_LOGIC;
+               packet.payload = &logic;
+               logic.length = BS - trigger_point;
+               logic.unitsize = devc->prof->num_channels / 8;
+               logic.data = devc->final_buf + (block * BS) + trigger_point;
+               sr_session_send(devc->cb_data, &packet);
+       }
+}
diff --git a/hardware/chronovu-la/protocol.h b/hardware/chronovu-la/protocol.h
new file mode 100644 (file)
index 0000000..6941661
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2011-2014 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#ifndef LIBSIGROK_HARDWARE_CHRONOVU_LA_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_CHRONOVU_LA_PROTOCOL_H
+
+#include <glib.h>
+#include <ftdi.h>
+#include <stdint.h>
+#include <string.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+#define LOG_PREFIX "la8/la16"
+
+#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;
+
+       /** The currently configured samplerate of the device. */
+       uint64_t cur_samplerate;
+
+       /** The current sampling limit (in ms). */
+       uint64_t limit_msec;
+
+       /** The current sampling limit (in number of samples). */
+       uint64_t limit_samples;
+
+       void *cb_data;
+
+       /**
+        * A buffer containing some (mangled) samples from the device.
+        * Format: Pretty mangled-up (due to hardware reasons), see code.
+        */
+       uint8_t mangled_buf[BS];
+
+       /**
+        * An 8MB buffer where we'll store the de-mangled samples.
+        * 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.
+        * A 1 bit matches a high signal, 0 matches a low signal on a channel.
+        *
+        * If the resp. 'trigger_edgemask' bit is set, 1 means "rising edge",
+        * and 0 means "falling edge".
+        */
+       uint16_t trigger_pattern;
+
+       /**
+        * Trigger mask.
+        * A 1 bit means "must match trigger_pattern", 0 means "don't care".
+        */
+       uint16_t trigger_mask;
+
+       /**
+        * 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. */
+       gint64 done;
+
+       /** Counter/index for the data block to be read. */
+       int block_counter;
+
+       /** The divcount value (determines the sample period). */
+       uint8_t divcount;
+
+       /** 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 SR_PRIV const char *cv_channel_names[];
+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_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);
+SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block);
+
+#endif
diff --git a/hardware/chronovu-la8/api.c b/hardware/chronovu-la8/api.c
deleted file mode 100644 (file)
index 685e1a4..0000000
+++ /dev/null
@@ -1,532 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2011-2014 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
- */
-
-#include "protocol.h"
-
-SR_PRIV struct sr_dev_driver chronovu_la8_driver_info;
-static struct sr_dev_driver *di = &chronovu_la8_driver_info;
-
-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/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);
-
-static void clear_helper(void *priv)
-{
-       struct dev_context *devc;
-
-       devc = priv;
-
-       ftdi_free(devc->ftdic);
-       g_free(devc->final_buf);
-}
-
-static int dev_clear(void)
-{
-       return std_dev_clear(di, clear_helper);
-}
-
-static int init(struct sr_context *sr_ctx)
-{
-       return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
-static int add_device(int idx, int model, GSList **devices)
-{
-       int ret;
-       unsigned int i;
-       struct sr_dev_inst *sdi;
-       struct drv_context *drvc;
-       struct dev_context *devc;
-       struct sr_channel *ch;
-
-       ret = SR_OK;
-
-       drvc = di->priv;
-
-       /* Allocate memory for our private device context. */
-       devc = g_try_malloc(sizeof(struct dev_context));
-
-       /* Set some sane defaults. */
-       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 = 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;
-       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;
-       }
-
-       /* 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,
-                             "ChronoVu", devc->prof->modelname, NULL);
-       if (!sdi) {
-               sr_err("Failed to create device instance.");
-               ret = SR_ERR;
-               goto err_free_final_buf;
-       }
-       sdi->driver = di;
-       sdi->priv = devc;
-
-       for (i = 0; i < devc->prof->num_channels; i++) {
-               if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
-                                         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);
-       drvc->instances = g_slist_append(drvc->instances, sdi);
-
-       return SR_OK;
-
-err_free_dev_inst:
-       sr_dev_inst_free(sdi);
-err_free_final_buf:
-       g_free(devc->final_buf);
-err_free_devc:
-       g_free(devc);
-
-       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)
-{
-       return ((struct drv_context *)(di->priv))->instances;
-}
-
-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;
-
-       /* 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, 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));
-               goto err_ftdi_free;
-       }
-       sr_dbg("Device opened successfully.");
-
-       /* Purge RX/TX buffers in the FTDI chip. */
-       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));
-               goto err_ftdi_free;
-       }
-       sr_dbg("FTDI buffers purged successfully.");
-
-       /* Enable flow control in the FTDI chip. */
-       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));
-               goto err_ftdi_free;
-       }
-       sr_dbg("FTDI flow control enabled successfully.");
-
-       /* Wait 100ms. */
-       g_usleep(100 * 1000);
-
-       sdi->status = SR_ST_ACTIVE;
-
-       return SR_OK;
-
-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;
-
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_OK;
-
-       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;
-}
-
-static int cleanup(void)
-{
-       return dev_clear();
-}
-
-static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
-{
-       struct dev_context *devc;
-
-       (void)cg;
-
-       switch (id) {
-       case SR_CONF_SAMPLERATE:
-               if (!sdi || !(devc = sdi->priv))
-                       return SR_ERR_BUG;
-               *data = g_variant_new_uint64(devc->cur_samplerate);
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
-}
-
-static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
-{
-       struct dev_context *devc;
-
-       (void)cg;
-
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
-       if (!(devc = sdi->priv))
-               return SR_ERR_BUG;
-
-       switch (id) {
-       case SR_CONF_SAMPLERATE:
-               if (cv_set_samplerate(sdi, g_variant_get_uint64(data)) < 0)
-                       return SR_ERR;
-               break;
-       case SR_CONF_LIMIT_MSEC:
-               if (g_variant_get_uint64(data) == 0)
-                       return SR_ERR_ARG;
-               devc->limit_msec = g_variant_get_uint64(data);
-               break;
-       case SR_CONF_LIMIT_SAMPLES:
-               if (g_variant_get_uint64(data) == 0)
-                       return SR_ERR_ARG;
-               devc->limit_samples = g_variant_get_uint64(data);
-               break;
-       default:
-               return SR_ERR_NA;
-       }
-
-       return SR_OK;
-}
-
-static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
-               const struct sr_channel_group *cg)
-{
-       GVariant *gvar, *grange[2];
-       GVariantBuilder gvb;
-       struct dev_context *devc;
-
-       (void)cg;
-
-       switch (key) {
-       case SR_CONF_DEVICE_OPTIONS:
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
-                               hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
-               break;
-       case SR_CONF_SAMPLERATE:
-               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"),
-                               devc->samplerates,
-                               ARRAY_SIZE(devc->samplerates),
-                               sizeof(uint64_t));
-               g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
-               *data = g_variant_builder_end(&gvb);
-               break;
-       case SR_CONF_LIMIT_SAMPLES:
-               grange[0] = g_variant_new_uint64(0);
-               grange[1] = g_variant_new_uint64(MAX_NUM_SAMPLES);
-               *data = g_variant_new_tuple(grange, 2);
-               break;
-       case SR_CONF_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;
-       }
-
-       return SR_OK;
-}
-
-static int receive_data(int fd, int revents, void *cb_data)
-{
-       int i, ret;
-       struct sr_dev_inst *sdi;
-       struct dev_context *devc;
-
-       (void)fd;
-       (void)revents;
-
-       if (!(sdi = cb_data)) {
-               sr_err("cb_data was NULL.");
-               return FALSE;
-       }
-
-       if (!(devc = sdi->priv)) {
-               sr_err("sdi->priv was NULL.");
-               return FALSE;
-       }
-
-       if (!devc->ftdic) {
-               sr_err("devc->ftdic was NULL.");
-               return FALSE;
-       }
-
-       /* Get one block of data. */
-       if ((ret = cv_read_block(devc)) < 0) {
-               sr_err("Failed to read data block: %d.", ret);
-               dev_acquisition_stop(sdi, sdi);
-               return FALSE;
-       }
-
-       /* We need to get exactly NUM_BLOCKS blocks (i.e. 8MB) of data. */
-       if (devc->block_counter != (NUM_BLOCKS - 1)) {
-               devc->block_counter++;
-               return TRUE;
-       }
-
-       sr_dbg("Sampling finished, sending data to session bus now.");
-
-       /* All data was received and demangled, send it to the session bus. */
-       for (i = 0; i < NUM_BLOCKS; i++)
-               cv_send_block_to_session_bus(devc, i);
-
-       dev_acquisition_stop(sdi, sdi);
-
-       return TRUE;
-}
-
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
-{
-       struct dev_context *devc;
-       uint8_t buf[8];
-       int bytes_to_write, bytes_written;
-
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
-       if (!(devc = sdi->priv)) {
-               sr_err("sdi->priv was NULL.");
-               return SR_ERR_BUG;
-       }
-
-       if (!devc->ftdic) {
-               sr_err("devc->ftdic was NULL.");
-               return SR_ERR_BUG;
-       }
-
-       devc->divcount = cv_samplerate_to_divcount(sdi, devc->cur_samplerate);
-       if (devc->divcount == 0xff) {
-               sr_err("Invalid divcount/samplerate.");
-               return SR_ERR;
-       }
-
-       if (cv_configure_channels(sdi) != SR_OK) {
-               sr_err("Failed to configure channels.");
-               return SR_ERR;
-       }
-
-       /* Fill acquisition parameters into buf[]. */
-       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, bytes_to_write);
-
-       if (bytes_written < 0 || bytes_written != bytes_to_write) {
-               sr_err("Acquisition failed to start.");
-               return SR_ERR;
-       }
-
-       sr_dbg("Hardware acquisition started successfully.");
-
-       devc->cb_data = cb_data;
-
-       /* Send header packet to the session bus. */
-       std_session_send_df_header(cb_data, LOG_PREFIX);
-
-       /* Time when we should be done (for detecting trigger timeouts). */
-       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;
-
-       /* Hook up a dummy handler to receive data from the device. */
-       sr_source_add(-1, G_IO_IN, 0, receive_data, (void *)sdi);
-
-       return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
-{
-       struct sr_datafeed_packet packet;
-
-       (void)sdi;
-
-       sr_dbg("Stopping acquisition.");
-       sr_source_remove(-1);
-
-       /* Send end packet to the session bus. */
-       sr_dbg("Sending SR_DF_END.");
-       packet.type = SR_DF_END;
-       sr_session_send(cb_data, &packet);
-
-       return SR_OK;
-}
-
-SR_PRIV struct sr_dev_driver chronovu_la8_driver_info = {
-       .name = "chronovu-la8",
-       .longname = "ChronoVu LA8",
-       .api_version = 1,
-       .init = init,
-       .cleanup = cleanup,
-       .scan = scan,
-       .dev_list = dev_list,
-       .dev_clear = dev_clear,
-       .config_get = config_get,
-       .config_set = config_set,
-       .config_list = config_list,
-       .dev_open = dev_open,
-       .dev_close = dev_close,
-       .dev_acquisition_start = dev_acquisition_start,
-       .dev_acquisition_stop = dev_acquisition_stop,
-       .priv = NULL,
-};
diff --git a/hardware/chronovu-la8/protocol.c b/hardware/chronovu-la8/protocol.c
deleted file mode 100644 (file)
index 28ac15b..0000000
+++ /dev/null
@@ -1,511 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2011-2014 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
- */
-
-#include "protocol.h"
-
-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",
-       "8", "9", "10", "11", "12", "13", "14", "15",
-};
-
-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 (devc->samplerates[0] != 0)
-               return;
-
-       for (i = 0; i < 255; i++)
-               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(const struct sr_dev_inst *sdi,
-                              uint64_t samplerate)
-{
-       int i;
-       struct dev_context *devc;
-
-       devc = sdi->priv;
-
-       cv_fill_samplerates_if_needed(sdi);
-
-       for (i = 0; i < 255; i++) {
-               if (devc->samplerates[i] == samplerate)
-                       return 1;
-       }
-
-       sr_err("Invalid samplerate (%" PRIu64 "Hz).", samplerate);
-
-       return 0;
-}
-
-/**
- * Convert a samplerate (in Hz) to the 'divcount' value the device wants.
- *
- * 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(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(sdi, samplerate)) {
-               sr_err("Can't get divcount, samplerate invalid.");
-               return 0xff;
-       }
-
-       return (devc->prof->max_samplerate / samplerate) - 1;
-}
-
-/**
- * Write data of a certain length to the FTDI device.
- *
- * @param devc The struct containing private per-device-instance data. Must not
- *             be NULL. devc->ftdic must not be NULL either.
- * @param buf The buffer containing the data to write. Must not be NULL.
- * @param size The number of bytes to write. Must be > 0.
- *
- * @return The number of bytes written, or a negative value upon errors.
- */
-SR_PRIV int cv_write(struct dev_context *devc, uint8_t *buf, int size)
-{
-       int bytes_written;
-
-       /* Note: Caller ensures devc/devc->ftdic/buf != NULL and size > 0. */
-
-       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) 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) close_usb_reset_sequencer(devc); /* Ignore errors. */
-       }
-
-       return bytes_written;
-}
-
-/**
- * Read a certain amount of bytes from the FTDI device.
- *
- * @param devc The struct containing private per-device-instance data. Must not
- *             be NULL. devc->ftdic must not be NULL either.
- * @param buf The buffer where the received data will be stored. Must not
- *            be NULL.
- * @param size The number of bytes to read. Must be >= 1.
- *
- * @return The number of bytes read, or a negative value upon errors.
- */
-static int cv_read(struct dev_context *devc, uint8_t *buf, int size)
-{
-       int bytes_read;
-
-       /* Note: Caller ensures devc/devc->ftdic/buf != NULL and size > 0. */
-
-       bytes_read = ftdi_read_data(devc->ftdic, buf, size);
-
-       if (bytes_read < 0) {
-               sr_err("Failed to read data (%d): %s.",
-                      bytes_read, ftdi_get_error_string(devc->ftdic));
-       } else if (bytes_read != size) {
-               // sr_err("Failed to read data, only %d/%d bytes read.",
-               //        bytes_read, size);
-       }
-
-       return bytes_read;
-}
-
-/**
- * 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.
- */
-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;
-
-       /* Note: Caller checked that devc and devc->ftdic != NULL. */
-
-       if (devc->ftdic->usb_dev) {
-               /* Reset the sequencer logic, then wait 100ms. */
-               sr_dbg("Resetting sequencer logic.");
-               (void) cv_write(devc, buf, 8); /* Ignore errors. */
-               g_usleep(100 * 1000);
-
-               /* Purge FTDI buffers, then reset and close the FTDI device. */
-               sr_dbg("Purging buffers, resetting+closing FTDI device.");
-
-               /* Log errors, but ignore them (i.e., don't abort). */
-               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));
-               if ((ret = ftdi_usb_reset(devc->ftdic)) < 0)
-                       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("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);
-       devc->ftdic = NULL;
-
-       return SR_OK;
-}
-
-/**
- * Reset the ChronoVu device.
- *
- * 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 reset_device(struct dev_context *devc)
-{
-       uint8_t buf[BS];
-       gint64 done, now;
-       int bytes_read;
-
-       /* Note: Caller checked that devc and devc->ftdic != NULL. */
-
-       sr_dbg("Resetting the device.");
-
-       /*
-        * Purge pending read data from the FTDI hardware FIFO until
-        * no more data is left, or a timeout occurs (after 20s).
-        */
-       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 = g_get_monotonic_time();
-       } while ((done > now) && (bytes_read > 0));
-
-       /* Reset the sequencer logic and close the USB port. */
-       (void) close_usb_reset_sequencer(devc); /* Ignore errors. */
-
-       sr_dbg("Device reset finished.");
-
-       return SR_OK;
-}
-
-SR_PRIV int cv_configure_channels(const struct sr_dev_inst *sdi)
-{
-       struct dev_context *devc;
-       const struct sr_channel *ch;
-       const GSList *l;
-       uint16_t channel_bit;
-       char *tc;
-
-       devc = sdi->priv;
-       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;
-
-               if (!ch) {
-                       sr_err("%s: channel was NULL.", __func__);
-                       return SR_ERR;
-               }
-
-               /* Skip disabled channels. */
-               if (!ch->enabled)
-                       continue;
-
-               /* Skip (enabled) channels with no configured trigger. */
-               if (!ch->trigger)
-                       continue;
-
-               /* Note: Must only be run if ch->trigger != NULL. */
-               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 pattern/mask/edgemask. */
-               for (tc = ch->trigger; tc && *tc; tc++) {
-                       devc->trigger_mask |= channel_bit;
-
-                       /* Sanity check, LA8 only supports low/high trigger. */
-                       if ((devc->prof->model == CHRONOVU_LA8) &&
-                           (*tc != '0' && *tc != '1')) {
-                               sr_err("Invalid trigger '%c', only "
-                                      "'0'/'1' supported.", *tc);
-                               return SR_ERR;
-                       }
-
-                       /* 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 pattern/mask/edgemask = 0x%04x / 0x%04x / 0x%04x.",
-              devc->trigger_pattern, devc->trigger_mask,
-              devc->trigger_edgemask);
-
-       return SR_OK;
-}
-
-SR_PRIV int cv_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate)
-{
-       struct dev_context *devc;
-
-       /* Note: Caller checked that sdi and sdi->priv != NULL. */
-
-       devc = sdi->priv;
-
-       sr_spew("Trying to set samplerate to %" PRIu64 "Hz.", samplerate);
-
-       cv_fill_samplerates_if_needed(sdi);
-
-       /* Check if this is a samplerate supported by the hardware. */
-       if (!is_valid_samplerate(sdi, samplerate)) {
-               sr_dbg("Failed to set invalid samplerate (%" PRIu64 "Hz).",
-                      samplerate);
-               return SR_ERR;
-       }
-
-       devc->cur_samplerate = samplerate;
-
-       sr_dbg("Samplerate set to %" PRIu64 "Hz.", devc->cur_samplerate);
-
-       return SR_OK;
-}
-
-/**
- * Get a block of data from the device.
- *
- * @param devc The struct containing private per-device-instance data. Must not
- *             be NULL. devc->ftdic must not be NULL either.
- *
- * @return SR_OK upon success, or SR_ERR upon errors.
- */
-SR_PRIV int cv_read_block(struct dev_context *devc)
-{
-       int i, byte_offset, m, mi, p, q, index, bytes_read;
-       gint64 now;
-
-       /* Note: Caller checked that devc and devc->ftdic != NULL. */
-
-       sr_spew("Reading block %d.", devc->block_counter);
-
-       bytes_read = cv_read(devc, devc->mangled_buf, BS);
-
-       /* If first block read got 0 bytes, retry until success or timeout. */
-       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);
-                       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) reset_device(devc); /* Ignore errors. */
-               return SR_ERR;
-       }
-
-       /* De-mangle the data. */
-       sr_spew("Demangling block %d.", devc->block_counter);
-       byte_offset = devc->block_counter * BS;
-       m = byte_offset / (1024 * 1024);
-       mi = m * (1024 * 1024);
-       for (i = 0; i < BS; i++) {
-               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];
-       }
-
-       return SR_OK;
-}
-
-SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block)
-{
-       int i;
-       uint8_t sample, expected_sample;
-       struct sr_datafeed_packet packet;
-       struct sr_datafeed_logic logic;
-       int trigger_point; /* Relative trigger point (in this block). */
-
-       /* 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;
-       expected_sample = devc->trigger_pattern & devc->trigger_mask;
-       for (i = 0; i < BS; i++) {
-               /* Don't continue if the trigger was found previously. */
-               if (devc->trigger_found)
-                       break;
-
-               /*
-                * Also, don't continue if triggers are "don't care", i.e. if
-                * 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 == 0x0000)
-                       break;
-
-               sample = *(devc->final_buf + (block * BS) + i);
-
-               if ((sample & devc->trigger_mask) == expected_sample) {
-                       trigger_point = i;
-                       devc->trigger_found = 1;
-                       break;
-               }
-       }
-
-       /* If no trigger was found, send one SR_DF_LOGIC packet. */
-       if (trigger_point == -1) {
-               /* Send an SR_DF_LOGIC packet to the session bus. */
-               sr_spew("Sending SR_DF_LOGIC packet (%d bytes) for "
-                       "block %d.", BS, block);
-               packet.type = SR_DF_LOGIC;
-               packet.payload = &logic;
-               logic.length = BS;
-               logic.unitsize = devc->prof->num_channels / 8;
-               logic.data = devc->final_buf + (block * BS);
-               sr_session_send(devc->cb_data, &packet);
-               return;
-       }
-
-       /*
-        * We found the trigger, so some special handling is needed. We have
-        * to send an SR_DF_LOGIC packet with the samples before the trigger
-        * (if any), then the SD_DF_TRIGGER packet itself, then another
-        * SR_DF_LOGIC packet with the samples after the trigger (if any).
-        */
-
-       /* TODO: Send SR_DF_TRIGGER packet before or after the actual sample? */
-
-       /* If at least one sample is located before the trigger... */
-       if (trigger_point > 0) {
-               /* Send pre-trigger SR_DF_LOGIC packet to the session bus. */
-               sr_spew("Sending pre-trigger SR_DF_LOGIC packet, "
-                       "start = %d, length = %d.", block * BS, trigger_point);
-               packet.type = SR_DF_LOGIC;
-               packet.payload = &logic;
-               logic.length = trigger_point;
-               logic.unitsize = devc->prof->num_channels / 8;
-               logic.data = devc->final_buf + (block * BS);
-               sr_session_send(devc->cb_data, &packet);
-       }
-
-       /* Send the SR_DF_TRIGGER packet to the session bus. */
-       sr_spew("Sending SR_DF_TRIGGER packet, sample = %d.",
-               (block * BS) + trigger_point);
-       packet.type = SR_DF_TRIGGER;
-       packet.payload = NULL;
-       sr_session_send(devc->cb_data, &packet);
-
-       /* If at least one sample is located after the trigger... */
-       if (trigger_point < (BS - 1)) {
-               /* Send post-trigger SR_DF_LOGIC packet to the session bus. */
-               sr_spew("Sending post-trigger SR_DF_LOGIC packet, "
-                       "start = %d, length = %d.",
-                       (block * BS) + trigger_point, BS - trigger_point);
-               packet.type = SR_DF_LOGIC;
-               packet.payload = &logic;
-               logic.length = BS - trigger_point;
-               logic.unitsize = devc->prof->num_channels / 8;
-               logic.data = devc->final_buf + (block * BS) + trigger_point;
-               sr_session_send(devc->cb_data, &packet);
-       }
-}
diff --git a/hardware/chronovu-la8/protocol.h b/hardware/chronovu-la8/protocol.h
deleted file mode 100644 (file)
index 2107eff..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * This file is part of the libsigrok project.
- *
- * Copyright (C) 2011-2014 Uwe Hermann <uwe@hermann-uwe.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
- */
-
-#ifndef LIBSIGROK_HARDWARE_CHRONOVU_LA8_PROTOCOL_H
-#define LIBSIGROK_HARDWARE_CHRONOVU_LA8_PROTOCOL_H
-
-#include <glib.h>
-#include <ftdi.h>
-#include <stdint.h>
-#include <string.h>
-#include "libsigrok.h"
-#include "libsigrok-internal.h"
-
-#define LOG_PREFIX "la8/la16"
-
-#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;
-
-       /** The currently configured samplerate of the device. */
-       uint64_t cur_samplerate;
-
-       /** The current sampling limit (in ms). */
-       uint64_t limit_msec;
-
-       /** The current sampling limit (in number of samples). */
-       uint64_t limit_samples;
-
-       void *cb_data;
-
-       /**
-        * A buffer containing some (mangled) samples from the device.
-        * Format: Pretty mangled-up (due to hardware reasons), see code.
-        */
-       uint8_t mangled_buf[BS];
-
-       /**
-        * An 8MB buffer where we'll store the de-mangled samples.
-        * 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.
-        * A 1 bit matches a high signal, 0 matches a low signal on a channel.
-        *
-        * If the resp. 'trigger_edgemask' bit is set, 1 means "rising edge",
-        * and 0 means "falling edge".
-        */
-       uint16_t trigger_pattern;
-
-       /**
-        * Trigger mask.
-        * A 1 bit means "must match trigger_pattern", 0 means "don't care".
-        */
-       uint16_t trigger_mask;
-
-       /**
-        * 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. */
-       gint64 done;
-
-       /** Counter/index for the data block to be read. */
-       int block_counter;
-
-       /** The divcount value (determines the sample period). */
-       uint8_t divcount;
-
-       /** 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 SR_PRIV const char *cv_channel_names[];
-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_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);
-SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block);
-
-#endif
index 4728cb074ba9abbde76e56cedbdbcf0118997a02..3a8c8dcaef26b7779e7c84768a70bfcfd967678c 100644 (file)
@@ -208,8 +208,8 @@ extern SR_PRIV struct sr_dev_driver zeroplus_logic_cube_driver_info;
 #ifdef HAVE_HW_ASIX_SIGMA
 extern SR_PRIV struct sr_dev_driver asix_sigma_driver_info;
 #endif
 #ifdef HAVE_HW_ASIX_SIGMA
 extern SR_PRIV struct sr_dev_driver asix_sigma_driver_info;
 #endif
-#ifdef HAVE_HW_CHRONOVU_LA8
-extern SR_PRIV struct sr_dev_driver chronovu_la8_driver_info;
+#ifdef HAVE_HW_CHRONOVU_LA
+extern SR_PRIV struct sr_dev_driver chronovu_la_driver_info;
 #endif
 #ifdef HAVE_HW_LINK_MSO19
 extern SR_PRIV struct sr_dev_driver link_mso19_driver_info;
 #endif
 #ifdef HAVE_HW_LINK_MSO19
 extern SR_PRIV struct sr_dev_driver link_mso19_driver_info;
@@ -366,8 +366,8 @@ static struct sr_dev_driver *drivers_list[] = {
 #ifdef HAVE_HW_ASIX_SIGMA
        &asix_sigma_driver_info,
 #endif
 #ifdef HAVE_HW_ASIX_SIGMA
        &asix_sigma_driver_info,
 #endif
-#ifdef HAVE_HW_CHRONOVU_LA8
-       &chronovu_la8_driver_info,
+#ifdef HAVE_HW_CHRONOVU_LA
+       &chronovu_la_driver_info,
 #endif
 #ifdef HAVE_HW_LINK_MSO19
        &link_mso19_driver_info,
 #endif
 #ifdef HAVE_HW_LINK_MSO19
        &link_mso19_driver_info,