Hi everybody,

see below for the second version of my patch to support the Rigol DS2000
series scopes. It still is a separate driver for now.

If you want to test it be sure to use current firmware, I used
01.01.00.02.

What has been changed:

- the different models have the correct list of timebases
- reading the data blocks returned by the scope has been implemented
correctly (I hope ;-)
- there should not be as many duplicate sample frames returned as before
- there should be no garbage sample data returned

What does not work:

- I couldn't get the scope to return the 12bit samples claimed by the
specs, giving up on that for now
- also for now ignoring peak detect data
- there still may be duplicate sample frames returned
- it may take a long time to get multiple frames from the scope,
arguably it might never finish (depends on timebase set)
- AFAIK there is no guarantee that the samples for channel 1 and channel
2 are from the same acquisition period(!), this can not be fixed

So it turns out that reading samples from a running scope is not that
useful. Implementing reading the sample memory from a stopped scope will
be next.


Enjoy,

MGri


diff -urNx .git libsigrok.org/configure.ac libsigrok/configure.ac
--- libsigrok.org/configure.ac  2013-10-27 16:44:51.783590272 +0100
+++ libsigrok/configure.ac      2013-10-27 16:46:15.189828820 +0100
@@ -189,6 +189,11 @@
        [HW_RIGOL_DS1XX2="$enableval"],
        [HW_RIGOL_DS1XX2=$HW_ENABLED_DEFAULT])
 
+AC_ARG_ENABLE(rigol-ds2xx2, AC_HELP_STRING([--enable-rigol-ds2xx2],
+       [enable Rigol DS2xx2 support [default=yes]]),
+       [HW_RIGOL_DS2XX2="$enableval"],
+       [HW_RIGOL_DS2XX2=$HW_ENABLED_DEFAULT])
+
 AC_ARG_ENABLE(saleae-logic16, AC_HELP_STRING([--enable-saleae-logic16],
        [enable Saleae Logic16 support [default=yes]]),
        [HW_SALEAE_LOGIC16="$enableval"],
@@ -436,6 +441,11 @@
        AC_DEFINE(HAVE_HW_RIGOL_DS1XX2, 1, [Rigol DS1xx2 support])
 fi
 
+AM_CONDITIONAL(HW_RIGOL_DS2XX2, test x$HW_RIGOL_DS2XX2 = xyes)
+if test "x$HW_RIGOL_DS2XX2" = "xyes"; then
+       AC_DEFINE(HAVE_HW_RIGOL_DS2XX2, 1, [Rigol DS2xx2 support])
+fi
+
 AM_CONDITIONAL(HW_SERIAL_DMM, test x$HW_SERIAL_DMM = xyes)
 if test "x$HW_SERIAL_DMM" = "xyes"; then
        AC_DEFINE(HAVE_HW_SERIAL_DMM, 1, [Serial DMM support])
@@ -521,6 +531,7 @@
                 hardware/lascar-el-usb/Makefile
                 hardware/mic-985xx/Makefile
                 hardware/rigol-ds1xx2/Makefile
+                hardware/rigol-ds2xx2/Makefile
                 hardware/saleae-logic16/Makefile
                 hardware/teleinfo/Makefile
                 hardware/tondaj-sl-814/Makefile
@@ -592,6 +603,7 @@
 echo "  - norma-dmm....................... $HW_NORMA_DMM"
 echo "  - openbench-logic-sniffer......... $HW_OLS"
 echo "  - rigol-ds1xx2.................... $HW_RIGOL_DS1XX2"
+echo "  - rigol-ds2xx2.................... $HW_RIGOL_DS2XX2"
 echo "  - saleae-logic16.................. $HW_SALEAE_LOGIC16"
 echo "  - serial-dmm...................... $HW_SERIAL_DMM"
 echo "  - teleinfo........................ $HW_TELEINFO"
diff -urNx .git libsigrok.org/hardware/Makefile.am 
libsigrok/hardware/Makefile.am
--- libsigrok.org/hardware/Makefile.am  2013-10-27 16:44:51.783590272 +0100
+++ libsigrok/hardware/Makefile.am      2013-10-27 16:46:15.189828820 +0100
@@ -41,6 +41,7 @@
        norma-dmm \
        openbench-logic-sniffer \
        rigol-ds1xx2 \
+       rigol-ds2xx2 \
        saleae-logic16 \
        serial-dmm \
        teleinfo \
@@ -141,6 +142,10 @@
 libsigrokhardware_la_LIBADD += rigol-ds1xx2/libsigrok_hw_rigol_ds1xx2.la
 endif
 
+if HW_RIGOL_DS2XX2
+libsigrokhardware_la_LIBADD += rigol-ds2xx2/libsigrok_hw_rigol_ds2xx2.la
+endif
+
 if HW_SALEAE_LOGIC16
 libsigrokhardware_la_LIBADD += saleae-logic16/libsigrok_hw_saleae_logic16.la
 endif
diff -urNx .git libsigrok.org/hardware/rigol-ds2xx2/api.c 
libsigrok/hardware/rigol-ds2xx2/api.c
--- libsigrok.org/hardware/rigol-ds2xx2/api.c   1970-01-01 01:00:00.000000000 
+0100
+++ libsigrok/hardware/rigol-ds2xx2/api.c       2013-10-27 16:46:15.189828820 
+0100
@@ -0,0 +1,635 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Martin Ling <[email protected]>
+ * Copyright (C) 2013 Bert Vermeulen <[email protected]>
+ * Copyright (C) 2013 Mathias Grimmberger <[email protected]>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+/* Based on api.c from Rigol DS1xx2 driver. */
+
+static const int32_t hwopts[] = {
+       SR_CONF_CONN,
+};
+
+static const int32_t hwcaps[] = {
+       SR_CONF_OSCILLOSCOPE,
+       SR_CONF_TIMEBASE,
+       SR_CONF_TRIGGER_SOURCE,
+       SR_CONF_TRIGGER_SLOPE,
+       SR_CONF_HORIZ_TRIGGERPOS,
+       SR_CONF_VDIV,
+       SR_CONF_COUPLING,
+       SR_CONF_NUM_TIMEBASE,
+       SR_CONF_NUM_VDIV,
+};
+
+static const uint64_t timebase_values[][2] = {
+       /* nanoseconds */
+       { 2, 1000000000 },
+       { 5, 1000000000 },
+       { 10, 1000000000 },
+       { 20, 1000000000 },
+       { 50, 1000000000 },
+       { 100, 1000000000 },
+       { 500, 1000000000 },
+       /* microseconds */
+       { 1, 1000000 },
+       { 2, 1000000 },
+       { 5, 1000000 },
+       { 10, 1000000 },
+       { 20, 1000000 },
+       { 50, 1000000 },
+       { 100, 1000000 },
+       { 200, 1000000 },
+       { 500, 1000000 },
+       /* milliseconds */
+       { 1, 1000 },
+       { 2, 1000 },
+       { 5, 1000 },
+       { 10, 1000 },
+       { 20, 1000 },
+       { 50, 1000 },
+       { 100, 1000 },
+       { 200, 1000 },
+       { 500, 1000 },
+       /* seconds */
+       { 1, 1 },
+       { 2, 1 },
+       { 5, 1 },
+       { 10, 1 },
+       { 20, 1 },
+       { 50, 1 },
+       { 100, 1 },
+       { 200, 1 },
+       { 500, 1 },
+/*     { 1000, 1 }, Confuses other code? */
+};
+
+static const uint64_t vdivs[][2] = {
+       /* microvolts */
+       { 500, 1000000 },
+       /* millivolts */
+       { 1, 1000 },
+       { 2, 1000 },
+       { 5, 1000 },
+       { 10, 1000 },
+       { 20, 1000 },
+       { 50, 1000 },
+       { 100, 1000 },
+       { 200, 1000 },
+       { 500, 1000 },
+       /* volts */
+       { 1, 1 },
+       { 2, 1 },
+       { 5, 1 },
+       { 10, 1 },
+};
+
+#define NUM_TIMEBASES ARRAY_SIZE(timebase_values)
+#define NUM_VDIV      ARRAY_SIZE(vdivs)
+
+static const char *trigger_sources[] = {
+       "CH1",
+       "CH2",
+       "EXT",
+       "AC Line",
+};
+
+static const char *coupling[] = {
+       "AC",
+       "DC",
+       "GND",
+};
+
+static const char *supported_models[] = {
+       "DS2072",
+       "DS2102",
+       "DS2202",
+};
+
+SR_PRIV struct sr_dev_driver rigol_ds2xx2_driver_info;
+static struct sr_dev_driver *di = &rigol_ds2xx2_driver_info;
+
+static void clear_helper(void *priv)
+{
+       struct dev_context *devc;
+
+       devc = priv;
+
+       g_free(devc->coupling[0]);
+       g_free(devc->coupling[1]);
+       g_free(devc->trigger_source);
+       g_free(devc->trigger_slope);
+}
+
+static int dev_clear(void)
+{
+       return std_dev_clear(di, clear_helper);
+}
+
+static int set_cfg(const struct sr_dev_inst *sdi, const char *format, ...)
+{
+       va_list args;
+       char buf[256];
+
+       va_start(args, format);
+       vsnprintf(buf, 255, format, args);
+       va_end(args);
+       if (rigol_ds2xx2_send(sdi, buf) != SR_OK)
+               return SR_ERR;
+
+       /* When setting a bunch of parameters in a row, the DS1052E scrambles
+        * some of them unless there is at least 100ms delay in between. */
+        /* Not sure whether this applies to DS2xx2 too - left in for now */
+       sr_spew("delay %dms", 100);
+       g_usleep(100000);
+
+       return SR_OK;
+}
+
+static int init(struct sr_context *sr_ctx)
+{
+       return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static int probe_port(const char *port, GSList **devices)
+{
+       struct dev_context *devc;
+       struct sr_dev_inst *sdi;
+       struct sr_serial_dev_inst *serial;
+       struct sr_probe *probe;
+       unsigned int i;
+       int len, num_tokens;
+       gboolean matched;
+       const char *manufacturer, *model, *version;
+       char buf[256];
+       gchar **tokens;
+
+       *devices = NULL;
+       if (!(serial = sr_serial_dev_inst_new(port, NULL)))
+               return SR_ERR_MALLOC;
+
+       if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+               return SR_ERR;
+       len = serial_write(serial, "*IDN?", 5);
+       len = serial_read(serial, buf, sizeof(buf));
+       if (serial_close(serial) != SR_OK)
+               return SR_ERR;
+
+       sr_serial_dev_inst_free(serial);
+
+       if (len == 0)
+               return SR_ERR_NA;
+
+       buf[len] = 0;
+       tokens = g_strsplit(buf, ",", 0);
+       sr_dbg("response: %s [%s]", port, buf);
+
+       for (num_tokens = 0; tokens[num_tokens] != NULL; num_tokens++);
+
+       if (num_tokens < 4) {
+               g_strfreev(tokens);
+               return SR_ERR_NA;
+       }
+
+       manufacturer = tokens[0];
+       model = tokens[1];
+       version = tokens[3];
+
+       if (strcasecmp(manufacturer, "Rigol Technologies")) {
+               g_strfreev(tokens);
+               return SR_ERR_NA;
+       }
+
+       matched = FALSE;
+       for (i = 0; i < ARRAY_SIZE(supported_models); i++) {
+               if (!strcmp(model, supported_models[i])) {
+                       matched = TRUE;
+                       break;
+               }
+       }
+
+       if (!matched || !(sdi = sr_dev_inst_new(0, SR_ST_ACTIVE,
+               manufacturer, model, version))) {
+               g_strfreev(tokens);
+               return SR_ERR_NA;
+       }
+
+       g_strfreev(tokens);
+
+       if (!(sdi->conn = sr_serial_dev_inst_new(port, NULL)))
+               return SR_ERR_MALLOC;
+       sdi->driver = di;
+       sdi->inst_type = SR_INST_SERIAL;
+
+       if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
+               return SR_ERR_MALLOC;
+       devc->limit_frames = 0;
+
+       for (i = 0; i < 2; i++) {
+               if (!(probe = sr_probe_new(i, SR_PROBE_ANALOG, TRUE,
+                               i == 0 ? "CH1" : "CH2")))
+                       return SR_ERR_MALLOC;
+               sdi->probes = g_slist_append(sdi->probes, probe);
+       }
+
+        if (!strcmp(sdi->model, "DS2202"))
+        {
+                /* Only DS2202 has 2ns/DIV timebase */
+                devc->timebases = timebase_values;
+                devc->num_timebases = ARRAY_SIZE(timebase_values);
+        } else {
+                devc->timebases = timebase_values + 1;
+                devc->num_timebases = ARRAY_SIZE(timebase_values) - 1;
+        }
+        
+       sdi->priv = devc;
+
+       *devices = g_slist_append(NULL, sdi);
+
+       return SR_OK;
+}
+
+static GSList *scan(GSList *options)
+{
+       struct drv_context *drvc;
+       struct sr_config *src;
+       GSList *l, *devices;
+       GDir *dir;
+       int ret;
+       const gchar *dev_name;
+       gchar *port = NULL;
+
+       drvc = di->priv;
+
+       for (l = options; l; l = l->next) {
+               src = l->data;
+               if (src->key == SR_CONF_CONN) {
+                       port = (char *)g_variant_get_string(src->data, NULL);
+                       break;
+               }
+       }
+
+       devices = NULL;
+       if (port) {
+               if (probe_port(port, &devices) == SR_ERR_MALLOC)
+                       return NULL;
+       } else {
+               if (!(dir = g_dir_open("/sys/class/usbmisc/", 0, NULL)))
+                       if (!(dir = g_dir_open("/sys/class/usb/", 0, NULL)))
+                               return NULL;
+               while ((dev_name = g_dir_read_name(dir))) {
+                       if (strncmp(dev_name, "usbtmc", 6))
+                               continue;
+                       port = g_strconcat("/dev/", dev_name, NULL);
+                       ret = probe_port(port, &devices);
+                       g_free(port);
+                       if (ret == SR_ERR_MALLOC) {
+                               g_dir_close(dir);
+                               return NULL;
+                       }
+               }
+               g_dir_close(dir);
+       }
+
+       /* Tack a copy of the newly found devices onto the driver list. */
+       l = g_slist_copy(devices);
+       drvc->instances = g_slist_concat(drvc->instances, l);
+
+       return devices;
+}
+
+static GSList *dev_list(void)
+{
+       return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+
+       if (serial_open(sdi->conn, SERIAL_RDWR) != SR_OK)
+               return SR_ERR;
+
+       if (rigol_ds2xx2_get_dev_cfg(sdi) != SR_OK)
+               return SR_ERR;
+
+       sdi->status = SR_ST_ACTIVE;
+
+       return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+       struct sr_serial_dev_inst *serial;
+
+       serial = sdi->conn;
+       if (serial && serial->fd != -1) {
+               serial_close(serial);
+               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 dev_context *devc;
+
+        if (!(devc = sdi->priv))
+                return SR_ERR;
+
+       switch (id) {
+       case SR_CONF_NUM_TIMEBASE:
+               *data = g_variant_new_int32(devc->num_timebases);
+               break;
+       case SR_CONF_NUM_VDIV:
+               *data = g_variant_new_int32(NUM_VDIV);
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       uint64_t tmp_u64, p, q;
+       double t_dbl;
+       unsigned int i;
+       int ret;
+       const char *tmp_str;
+
+       devc = sdi->priv;
+
+       if (sdi->status != SR_ST_ACTIVE)
+               return SR_ERR_DEV_CLOSED;
+
+       ret = SR_OK;
+       switch (id) {
+       case SR_CONF_LIMIT_FRAMES:
+               devc->limit_frames = g_variant_get_uint64(data);
+               break;
+       case SR_CONF_TRIGGER_SLOPE:
+               tmp_u64 = g_variant_get_uint64(data);
+               if (tmp_u64 != 0 && tmp_u64 != 1)
+                       return SR_ERR;
+               g_free(devc->trigger_slope);
+               devc->trigger_slope = g_strdup(tmp_u64 ? "POS" : "NEG");
+               ret = set_cfg(sdi, ":TRIG:EDGE:SLOP %s", devc->trigger_slope);
+               break;
+       case SR_CONF_HORIZ_TRIGGERPOS:
+               t_dbl = g_variant_get_double(data);
+               if (t_dbl < 0.0 || t_dbl > 1.0)
+                       return SR_ERR;
+               devc->horiz_triggerpos = t_dbl;
+               /* We have the trigger offset as a percentage of the frame, but
+                * need to express this in seconds. */
+               t_dbl = -(devc->horiz_triggerpos - 0.5) * devc->timebase * 
devc->num_timebases;
+               ret = set_cfg(sdi, ":TIM:OFFS %.6f", t_dbl);
+               break;
+       case SR_CONF_TIMEBASE:
+               g_variant_get(data, "(tt)", &p, &q);
+               for (i = 0; i < devc->num_timebases; i++) {
+                       if (devc->timebases[i][0] == p && devc->timebases[i][1] 
== q) {
+                               devc->timebase = (float)p / q;
+                               ret = set_cfg(sdi, ":TIM:SCAL %.9f", 
devc->timebase);
+                               break;
+                       }
+               }
+               if (i == devc->num_timebases)
+                       ret = SR_ERR_ARG;
+               break;
+       case SR_CONF_TRIGGER_SOURCE:
+               tmp_str = g_variant_get_string(data, NULL);
+               for (i = 0; i < ARRAY_SIZE(trigger_sources); i++) {
+                       if (!strcmp(trigger_sources[i], tmp_str)) {
+                               g_free(devc->trigger_source);
+                               devc->trigger_source = 
g_strdup(trigger_sources[i]);
+                               if (!strcmp(devc->trigger_source, "AC Line"))
+                                       tmp_str = "ACL";
+                               else if (!strcmp(devc->trigger_source, "CH1"))
+                                       tmp_str = "CHAN1";
+                               else if (!strcmp(devc->trigger_source, "CH2"))
+                                       tmp_str = "CHAN2";
+                               ret = set_cfg(sdi, ":TRIG:EDGE:SOUR %s", 
tmp_str);
+                               break;
+                       }
+               }
+               if (i == ARRAY_SIZE(trigger_sources))
+                       ret = SR_ERR_ARG;
+               break;
+       case SR_CONF_VDIV:
+               g_variant_get(data, "(tt)", &p, &q);
+               for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
+                       if (vdivs[i][0] != p || vdivs[i][1] != q)
+                               continue;
+                       devc->vdiv[0] = devc->vdiv[1] = (float)p / q;
+                       set_cfg(sdi, ":CHAN1:SCAL %.3f", devc->vdiv[0]);
+                       ret = set_cfg(sdi, ":CHAN2:SCAL %.3f", devc->vdiv[1]);
+                       break;
+               }
+               if (i == ARRAY_SIZE(vdivs))
+                       ret = SR_ERR_ARG;
+               break;
+       case SR_CONF_COUPLING:
+               /* TODO: Not supporting coupling per channel yet. */
+               tmp_str = g_variant_get_string(data, NULL);
+               for (i = 0; i < ARRAY_SIZE(coupling); i++) {
+                       if (!strcmp(tmp_str, coupling[i])) {
+                               g_free(devc->coupling[0]);
+                               g_free(devc->coupling[1]);
+                               devc->coupling[0] = g_strdup(coupling[i]);
+                               devc->coupling[1] = g_strdup(coupling[i]);
+                               set_cfg(sdi, ":CHAN1:COUP %s", 
devc->coupling[0]);
+                               ret = set_cfg(sdi, ":CHAN2:COUP %s", 
devc->coupling[1]);
+                               break;
+                       }
+               }
+               if (i == ARRAY_SIZE(coupling))
+                       ret = SR_ERR_ARG;
+               break;
+       default:
+               ret = SR_ERR_NA;
+               break;
+       }
+
+       return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi)
+{
+       const struct dev_context *devc;
+       GVariant *tuple, *rational[2];
+       GVariantBuilder gvb;
+       unsigned int i;
+
+        if (!(devc = sdi->priv))
+                return SR_ERR;
+
+       switch (key) {
+       case SR_CONF_SCAN_OPTIONS:
+               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+                               hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+               break;
+       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_COUPLING:
+               *data = g_variant_new_strv(coupling, ARRAY_SIZE(coupling));
+               break;
+       case SR_CONF_VDIV:
+               g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+               for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
+                       rational[0] = g_variant_new_uint64(vdivs[i][0]);
+                       rational[1] = g_variant_new_uint64(vdivs[i][1]);
+                       tuple = g_variant_new_tuple(rational, 2);
+                       g_variant_builder_add_value(&gvb, tuple);
+               }
+               *data = g_variant_builder_end(&gvb);
+               break;
+       case SR_CONF_TIMEBASE:
+               g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+               for (i = 0; i < devc->num_timebases; i++) {
+                       rational[0] = 
g_variant_new_uint64(devc->timebases[i][0]);
+                       rational[1] = 
g_variant_new_uint64(devc->timebases[i][1]);
+                       tuple = g_variant_new_tuple(rational, 2);
+                       g_variant_builder_add_value(&gvb, tuple);
+               }
+               *data = g_variant_builder_end(&gvb);
+               break;
+       case SR_CONF_TRIGGER_SOURCE:
+               if (!sdi || !sdi->priv)
+                       /* Can't know this until we have the exact model. */
+                       return SR_ERR_ARG;
+               *data = g_variant_new_strv(trigger_sources, 4);
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+       struct sr_serial_dev_inst *serial;
+       struct dev_context *devc;
+       struct sr_probe *probe;
+       GSList *l;
+       char cmd[256];
+
+       if (sdi->status != SR_ST_ACTIVE)
+               return SR_ERR_DEV_CLOSED;
+
+       serial = sdi->conn;
+       devc = sdi->priv;
+
+       for (l = sdi->probes; l; l = l->next) {
+               probe = l->data;
+               sr_dbg("handling probe %s", probe->name);
+               if (probe->type == SR_PROBE_ANALOG) {
+                       if (probe->enabled)
+                               devc->enabled_analog_probes = g_slist_append(
+                                               devc->enabled_analog_probes, 
probe);
+                       if (probe->enabled != 
devc->analog_channels[probe->index]) {
+                               /* Enabled channel is currently disabled, or 
vice versa. */
+                               sprintf(cmd, ":CHAN%d:DISP %s", probe->index + 
1,
+                                               probe->enabled ? "ON" : "OFF");
+                               if (rigol_ds2xx2_send(sdi, cmd) != SR_OK)
+                                       return SR_ERR;
+                       }
+               }
+       }
+       if (!devc->enabled_analog_probes)
+               return SR_ERR;
+
+       sr_source_add(serial->fd, G_IO_IN, 50, rigol_ds2xx2_receive, (void 
*)sdi);
+
+       /* Send header packet to the session bus. */
+       std_session_send_df_header(cb_data, LOG_PREFIX);
+
+       /* Fetch the first frame. */
+       if (devc->enabled_analog_probes) {
+                /* Assume there already was a trigger event - don't wait */
+                if (rigol_ds2xx2_acquisition_start(sdi, FALSE) != SR_OK)
+                        return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+       struct dev_context *devc;
+       struct sr_serial_dev_inst *serial;
+
+       (void)cb_data;
+
+       devc = sdi->priv;
+
+       if (sdi->status != SR_ST_ACTIVE) {
+               sr_err("Device inactive, can't stop acquisition.");
+               return SR_ERR;
+       }
+
+       g_slist_free(devc->enabled_analog_probes);
+       devc->enabled_analog_probes = NULL;
+       serial = sdi->conn;
+       sr_source_remove(serial->fd);
+
+       return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver rigol_ds2xx2_driver_info = {
+       .name = "rigol-ds2xx2",
+       .longname = "Rigol DS2xx2",
+       .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,
+};
+
+/* Local Variables: */
+/* c-basic-offset: 8 */
+/* End: */
diff -urNx .git libsigrok.org/hardware/rigol-ds2xx2/Makefile.am 
libsigrok/hardware/rigol-ds2xx2/Makefile.am
--- libsigrok.org/hardware/rigol-ds2xx2/Makefile.am     1970-01-01 
01:00:00.000000000 +0100
+++ libsigrok/hardware/rigol-ds2xx2/Makefile.am 2013-10-27 16:46:15.190828799 
+0100
@@ -0,0 +1,33 @@
+##
+## This file is part of the libsigrok project.
+##
+## Copyright (C) 2012 Martin Ling <[email protected]>,
+##
+## 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 3 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, see <http://www.gnu.org/licenses/>.
+##
+
+if HW_RIGOL_DS2XX2
+
+# Local lib, this is NOT meant to be installed!
+noinst_LTLIBRARIES = libsigrok_hw_rigol_ds2xx2.la
+
+libsigrok_hw_rigol_ds2xx2_la_SOURCES = \
+       api.c \
+       protocol.c \
+       protocol.h
+
+libsigrok_hw_rigol_ds2xx2_la_CFLAGS = \
+       -I$(top_srcdir)
+
+endif
diff -urNx .git libsigrok.org/hardware/rigol-ds2xx2/protocol.c 
libsigrok/hardware/rigol-ds2xx2/protocol.c
--- libsigrok.org/hardware/rigol-ds2xx2/protocol.c      1970-01-01 
01:00:00.000000000 +0100
+++ libsigrok/hardware/rigol-ds2xx2/protocol.c  2013-10-27 16:46:15.190828799 
+0100
@@ -0,0 +1,544 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Martin Ling <[email protected]>
+ * Copyright (C) 2013 Bert Vermeulen <[email protected]>
+ * Copyright (C) 2013 Mathias Grimmberger <[email protected]>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <math.h>
+#include <ctype.h>
+#include <time.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+/* Based on protocol.c from Rigol DS1xx2 driver. */
+
+/*
+ * Tested with a Rigol DS2072 using firmware version 01.01.00.02.
+ */
+
+/*
+ * The Rigol DS2000 series scopes try to adhere to the IEEE 488.2 (I think)
+ * standard. If you want to read it - it costs real money...
+ *
+ * Every response from the scope has a linefeed appended because the
+ * standard says so. In principle this could be ignored because sending the
+ * next command clears the output queue of the scope. This driver tries to
+ * avoid doing that because it may cause an error being generated inside the
+ * scope and who knows what bugs the firmware has WRT this.
+ *
+ * Waveform data is transferred in a format called "arbitrary block program
+ * data" specified in IEEE 488.2. See Agilents programming manuals for their
+ * 2000/3000 series scopes for a nice description.
+ *
+ * Each data block from the scope has a header, e.g. "#900000001400".
+ * The '#' marks the start of a block.
+ * Next is one ASCII decimal digit between 1 and 9, this gives the number of
+ * ASCII decimal digits following.
+ * Last are the ASCII decimal digits giving the number of bytes (not
+ * samples!) in the block.
+ *
+ * After this header as many data bytes as indicated follow.
+ *
+ * Each data block has a trailing linefeed too.
+ */
+
+static int get_cfg(const struct sr_dev_inst *sdi, char *cmd, char *reply, 
size_t maxlen);
+static int get_cfg_int(const struct sr_dev_inst *sdi, char *cmd, int *i);
+
+static int parse_int(const char *str, int *ret)
+{
+        char *e;
+        long tmp;
+        
+        errno = 0;
+       tmp = strtol(str, &e, 10);
+       if (e == str || *e != '\0') {
+               sr_dbg("Failed to parse integer: '%s'", str);
+               return SR_ERR;
+       }
+        if (errno) {
+               sr_dbg("Failed to parse integer: '%s', numerical overflow", 
str);
+               return SR_ERR;
+        }
+        if (tmp > INT_MAX || tmp < INT_MIN) {
+               sr_dbg("Failed to parse integer: '%s', value to large/small", 
str);
+               return SR_ERR;
+        }
+
+        *ret = (int)tmp;
+       return SR_OK;        
+}
+
+/*
+ * Waiting for a trigger event will return a timeout after 2, 3 seconds in
+ * order to not block the application.
+ */
+
+static int rigol_ds2xx2_trigger_wait(const struct sr_dev_inst *sdi)
+{
+        char buf[20];
+       struct dev_context *devc;
+        time_t start;
+
+       if (!(devc = sdi->priv))
+               return SR_ERR;
+
+        start = time(NULL);
+        
+        /*
+         * Trigger status may return:
+         * "TD"   - triggered
+         * "AUTO" - autotriggered
+         * "RUN"  - running
+         * "WAIT" - waiting for trigger
+         * "STOP" - stopped
+         */
+
+        if (devc->trigger_wait_status == 1)
+        {
+                do 
+                {
+                        if (time(NULL) - start >= 3)
+                        {
+                                sr_dbg("Timeout waiting for trigger");
+                                return SR_ERR_TIMEOUT;
+                        }
+                        
+                        if (get_cfg(sdi, ":TRIG:STAT?", buf, sizeof(buf)) != 
SR_OK)
+                                return SR_ERR;
+                } while (buf[0] == 'T' || buf[0] == 'A');
+
+                devc->trigger_wait_status = 2;
+        }
+        if (devc->trigger_wait_status == 2)
+        {
+                do 
+                {
+                        if (time(NULL) - start >= 3)
+                        {
+                                sr_dbg("Timeout waiting for trigger");
+                                return SR_ERR_TIMEOUT;
+                        }
+
+                        if (get_cfg(sdi, ":TRIG:STAT?", buf, sizeof(buf)) != 
SR_OK)
+                                return SR_ERR;
+                } while (buf[0] != 'T' && buf[0] != 'A');
+
+                devc->trigger_wait_status = 0;
+        }
+        
+        return SR_OK;
+}
+
+/*
+ * This needs to wait for a new trigger event to ensure that sample data is
+ * not returned twice.
+ *
+ * Unfortunately this will never really work because for sufficiently fast
+ * timebases it just can't catch the status changes.
+ *
+ * What would be needed is a trigger event register with autoreset like the
+ * Agilents have. The Rigols don't seem to have anything like this.
+ *
+ * The workaround is to only wait for the trigger when the timebase is slow
+ * enough. Of course this means that for faster timebases sample data can be
+ * returned multiple times.
+ */
+
+SR_PRIV int rigol_ds2xx2_acquisition_start(const struct sr_dev_inst *sdi,
+                                           gboolean wait_for_trigger) 
+{
+       struct dev_context *devc;
+
+       if (!(devc = sdi->priv))
+               return SR_ERR;
+
+        devc->channel_frame = devc->enabled_analog_probes->data;
+
+        sr_dbg("Starting acquisition on channel %d",
+               devc->channel_frame->index + 1);
+        
+        if (rigol_ds2xx2_send(sdi, ":WAV:FORM BYTE") != SR_OK)
+                return SR_ERR;
+        if (rigol_ds2xx2_send(sdi, ":WAV:SOUR CHAN%d",
+                              devc->channel_frame->index + 1) != SR_OK)
+                return SR_ERR;
+        if (rigol_ds2xx2_send(sdi, ":WAV:MODE NORM") != SR_OK)
+                return SR_ERR;
+
+       devc->num_frame_bytes = 0;
+        devc->num_block_bytes = 0;
+
+        /* only wait for trigger if timbase 50 msecs/DIV or slower */
+        if (wait_for_trigger && devc->timebase > 0.0499) 
+        {
+                devc->trigger_wait_status = 1;
+        } else {
+                devc->trigger_wait_status = 0;
+        }
+        
+        return SR_OK;
+}
+
+static int rigol_ds2xx2_read_header(struct sr_serial_dev_inst *serial)
+{
+        char start[3], length[10];
+        int len, tmp;
+
+        /* Read the hashsign and length digit. */
+        tmp = serial_read(serial, start, 2);
+        start[2] = '\0';
+        if (tmp != 2) 
+        {
+                sr_err("Failed to read first two bytes of data block header.");
+                return -1;
+        }
+        if (start[0] != '#' || !isdigit(start[1]) || start[1] == '0')
+        {
+                sr_err("Received invalid data block header start '%s'.", 
start);
+                return -1;
+        }
+        len = atoi(start + 1);
+
+        /* Read the data length. */
+        tmp = serial_read(serial, length, len);
+        length[len] = '\0';
+        if (tmp != len) 
+        {
+                sr_err("Failed to read %d bytes of data block length.", len);
+                return -1;
+        }
+        if (parse_int(length, &len) != SR_OK) 
+        {
+                sr_err("Received invalid data block length '%s'.", length);
+                return -1;
+        }
+        
+        sr_dbg("Received data block header: %s%s -> block length %d", start, 
length, len);
+
+        return len;
+}
+
+SR_PRIV int rigol_ds2xx2_receive(int fd, int revents, void *cb_data)
+{
+       struct sr_dev_inst *sdi;
+       struct sr_serial_dev_inst *serial;
+       struct dev_context *devc;
+       struct sr_datafeed_packet packet;
+       struct sr_datafeed_analog analog;
+       unsigned char buf[ANALOG_WAVEFORM_SIZE];
+       double vdiv, offset;
+       float data[ANALOG_WAVEFORM_SIZE];
+       int len, i, vref;
+       struct sr_probe *probe;
+
+       (void)fd;
+
+       if (!(sdi = cb_data))
+               return TRUE;
+
+       if (!(devc = sdi->priv))
+               return TRUE;
+
+       serial = sdi->conn;
+
+       if (revents == G_IO_IN) {
+                if (devc->trigger_wait_status > 0)
+                {
+                        if (rigol_ds2xx2_trigger_wait(sdi) != SR_OK)
+                        {
+                                return TRUE;
+                        }
+                }
+                
+               if (devc->num_block_bytes == 0) {
+                        sr_dbg("New block header expected");
+                        if (rigol_ds2xx2_send(sdi, ":WAV:DATA?") != SR_OK)
+                                return TRUE;
+                        len = rigol_ds2xx2_read_header(serial);
+                        if (len == -1)
+                        {
+                                return TRUE;
+                        }
+                        /* At slow timebases the scope sometimes returns
+                         * "short" data blocks, with apparently no way to
+                         * get the rest of the data. Discard these, the
+                         * complete data block will appear eventually.
+                         */
+                        if (len < ANALOG_WAVEFORM_SIZE)
+                        {
+                                sr_dbg("Discarding short data block");
+                                serial_read(serial, buf, len + 1);
+                                return TRUE;
+                        }
+                        devc->num_block_bytes = len;
+                        devc->num_block_read = 0;
+               }
+
+               probe = devc->channel_frame;
+                len = devc->num_block_bytes - devc->num_block_read;
+               len = serial_read(serial, buf,
+                                  len < ANALOG_WAVEFORM_SIZE ? len : 
ANALOG_WAVEFORM_SIZE);
+               sr_dbg("Received %d bytes.", len);
+               if (len == -1)
+                       return TRUE;
+
+               if (devc->num_frame_bytes == 0) {
+                       /* Start of a new frame. */
+                       packet.type = SR_DF_FRAME_BEGIN;
+                       sr_session_send(sdi, &packet);
+               }
+
+                devc->num_block_read += len;
+                
+                vdiv = devc->vdiv[probe->index] / 25.6;
+                vref = devc->vert_reference[probe->index];
+                offset = devc->vert_offset[probe->index];
+                for (i = 0; i < len; i++) {
+                        data[i] = ((int)buf[i] - vref) * vdiv - offset;
+                }
+
+                if (devc->num_block_read == devc->num_block_bytes)
+                {
+                        sr_dbg("Block has been completed");
+                        /* Discard the terminating linefeed and prepare for
+                           possible next block */
+                        serial_read(serial, buf, 1);
+                        devc->num_block_bytes = 0;
+                }
+
+                analog.probes = g_slist_append(NULL, probe);
+                analog.num_samples = len;
+                analog.data = data;
+                analog.mq = SR_MQ_VOLTAGE;
+                analog.unit = SR_UNIT_VOLT;
+                analog.mqflags = 0;
+                packet.type = SR_DF_ANALOG;
+                packet.payload = &analog;
+                sr_session_send(cb_data, &packet);
+                g_slist_free(analog.probes);
+
+                devc->num_frame_bytes += len;
+                
+                if (devc->num_frame_bytes < ANALOG_WAVEFORM_SIZE)
+                        /* Don't have the whole frame yet. */
+                        return TRUE;
+
+                sr_dbg("Frame completed, %d samples", devc->num_frame_bytes);
+               /* End of the frame. */
+               packet.type = SR_DF_FRAME_END;
+               sr_session_send(sdi, &packet);
+
+               if (devc->enabled_analog_probes
+                               && devc->channel_frame == 
devc->enabled_analog_probes->data
+                               && devc->enabled_analog_probes->next != NULL) {
+                       /* We got the frame for the first analog channel, but
+                        * there's a second analog channel. Do not wait for
+                         * trigger to try and keep channel data related. */
+                        rigol_ds2xx2_acquisition_start(sdi, FALSE);
+               } else {
+                       /* Done with both analog channels in this frame. */
+                       if (++devc->num_frames == devc->limit_frames) {
+                               /* End of last frame. */
+                               sdi->driver->dev_acquisition_stop(sdi, cb_data);
+                       } else {
+                               /* Get the next frame, starting with the first 
analog channel. */
+                               if (devc->enabled_analog_probes) {
+                                        /* Must wait for trigger because at
+                                         * slow timebases the scope will
+                                         * return old data otherwise. */
+                                        rigol_ds2xx2_acquisition_start(sdi, 
TRUE);
+                               }
+                       }
+               }
+       }
+
+       return TRUE;
+}
+
+SR_PRIV int rigol_ds2xx2_send(const struct sr_dev_inst *sdi, const char 
*format, ...)
+{
+       va_list args;
+       char buf[256];
+       int len, out, ret;
+
+       va_start(args, format);
+       len = vsnprintf(buf, 255, format, args);
+       va_end(args);
+       strcat(buf, "\n");
+       len++;
+       out = serial_write(sdi->conn, buf, len);
+       buf[len - 1] = '\0';
+       if (out != len) {
+               sr_dbg("Only sent %d/%d bytes of '%s'.", out, len, buf);
+               ret = SR_ERR;
+       } else {
+               sr_spew("Sent '%s'.", buf);
+               ret = SR_OK;
+       }
+
+       return ret;
+}
+
+static int get_cfg(const struct sr_dev_inst *sdi, char *cmd, char *reply, 
size_t maxlen)
+{
+       int len;
+
+       if (rigol_ds2xx2_send(sdi, cmd) != SR_OK)
+               return SR_ERR;
+
+       if ((len = serial_read(sdi->conn, reply, maxlen - 1)) < 0)
+               return SR_ERR;
+       reply[len] = '\0';
+        /* get rid of trailing linefeed */
+        if (len >= 1 && reply[len-1] == '\n')
+        {
+            reply[len-1] = '\0';
+        }
+       sr_spew("Received '%s'.", reply);
+
+       return SR_OK;
+}
+
+static int get_cfg_int(const struct sr_dev_inst *sdi, char *cmd, int *i)
+{
+       char buf[32];
+
+       if (get_cfg(sdi, cmd, buf, sizeof(buf)) != SR_OK)
+               return SR_ERR;
+
+        if (parse_int(buf, i) != SR_OK)
+                return SR_ERR;
+        
+       return SR_OK;        
+}
+
+static int get_cfg_float(const struct sr_dev_inst *sdi, char *cmd, float *f)
+{
+       char buf[32], *e;
+
+       if (get_cfg(sdi, cmd, buf, sizeof(buf)) != SR_OK)
+               return SR_ERR;
+       *f = strtof(buf, &e);
+       if (e == buf || (fpclassify(*f) & (FP_ZERO | FP_NORMAL)) == 0) {
+               sr_dbg("failed to parse response to '%s': '%s'", cmd, buf);
+               return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+static int get_cfg_string(const struct sr_dev_inst *sdi, char *cmd, char **buf)
+{
+       if (!(*buf = g_try_malloc0(256)))
+               return SR_ERR_MALLOC;
+
+       if (get_cfg(sdi, cmd, *buf, 256) != SR_OK)
+               return SR_ERR;
+
+       return SR_OK;
+}
+
+SR_PRIV int rigol_ds2xx2_get_dev_cfg(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       char *t_s;
+
+       devc = sdi->priv;
+
+       /* Analog channel state. */
+       if (get_cfg_string(sdi, ":CHAN1:DISP?", &t_s) != SR_OK)
+               return SR_ERR;
+       devc->analog_channels[0] = !strcmp(t_s, "1") ? TRUE : FALSE;
+       g_free(t_s);
+       if (get_cfg_string(sdi, ":CHAN2:DISP?", &t_s) != SR_OK)
+               return SR_ERR;
+       devc->analog_channels[1] = !strcmp(t_s, "1") ? TRUE : FALSE;
+       g_free(t_s);
+       sr_dbg("Current analog channel state CH1 %s CH2 %s",
+                       devc->analog_channels[0] ? "on" : "off",
+                       devc->analog_channels[1] ? "on" : "off");
+
+       /* Timebase. */
+       if (get_cfg_float(sdi, ":TIM:SCAL?", &devc->timebase) != SR_OK)
+               return SR_ERR;
+       sr_dbg("Current timebase %g", devc->timebase);
+
+       /* Vertical gain. */
+       if (get_cfg_float(sdi, ":CHAN1:SCAL?", &devc->vdiv[0]) != SR_OK)
+               return SR_ERR;
+       if (get_cfg_float(sdi, ":CHAN2:SCAL?", &devc->vdiv[1]) != SR_OK)
+               return SR_ERR;
+       sr_dbg("Current vertical gain CH1 %g CH2 %g", devc->vdiv[0], 
devc->vdiv[1]);
+
+       /* Vertical reference - not certain if this is the place to read it. */
+        if (rigol_ds2xx2_send(sdi, ":WAV:SOUR CHAN1") != SR_OK)
+                return SR_ERR;
+       if (get_cfg_int(sdi, ":WAV:YREF?", &devc->vert_reference[0]) != SR_OK)
+               return SR_ERR;
+        if (rigol_ds2xx2_send(sdi, ":WAV:SOUR CHAN2") != SR_OK)
+                return SR_ERR;
+       if (get_cfg_int(sdi, ":WAV:YREF?", &devc->vert_reference[1]) != SR_OK)
+               return SR_ERR;
+       sr_dbg("Current vertical reference CH1 %d CH2 %d", 
devc->vert_reference[0],
+                       devc->vert_reference[1]);
+
+       /* Vertical offset. */
+       if (get_cfg_float(sdi, ":CHAN1:OFFS?", &devc->vert_offset[0]) != SR_OK)
+               return SR_ERR;
+       if (get_cfg_float(sdi, ":CHAN2:OFFS?", &devc->vert_offset[1]) != SR_OK)
+               return SR_ERR;
+       sr_dbg("Current vertical offset CH1 %g CH2 %g", devc->vert_offset[0],
+                       devc->vert_offset[1]);
+
+       /* Coupling. */
+       if (get_cfg_string(sdi, ":CHAN1:COUP?", &devc->coupling[0]) != SR_OK)
+               return SR_ERR;
+       if (get_cfg_string(sdi, ":CHAN2:COUP?", &devc->coupling[1]) != SR_OK)
+               return SR_ERR;
+       sr_dbg("Current coupling CH1 %s CH2 %s", devc->coupling[0],
+                       devc->coupling[1]);
+
+       /* Trigger source. */
+       if (get_cfg_string(sdi, ":TRIG:EDGE:SOUR?", &devc->trigger_source) != 
SR_OK)
+               return SR_ERR;
+       sr_dbg("Current trigger source %s", devc->trigger_source);
+
+       /* Horizontal trigger position. */
+       if (get_cfg_float(sdi, ":TIM:OFFS?", &devc->horiz_triggerpos) != SR_OK)
+               return SR_ERR;
+       sr_dbg("Current horizontal trigger position %g", 
devc->horiz_triggerpos);
+
+       /* Trigger slope. */
+       if (get_cfg_string(sdi, ":TRIG:EDGE:SLOP?", &devc->trigger_slope) != 
SR_OK)
+               return SR_ERR;
+       sr_dbg("Current trigger slope %s", devc->trigger_slope);
+
+       return SR_OK;
+}
+
+/* Local Variables: */
+/* c-basic-offset: 8 */
+/* End: */
diff -urNx .git libsigrok.org/hardware/rigol-ds2xx2/protocol.h 
libsigrok/hardware/rigol-ds2xx2/protocol.h
--- libsigrok.org/hardware/rigol-ds2xx2/protocol.h      1970-01-01 
01:00:00.000000000 +0100
+++ libsigrok/hardware/rigol-ds2xx2/protocol.h  2013-10-27 16:46:15.190828799 
+0100
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Martin Ling <[email protected]>
+ * Copyright (C) 2013 Bert Vermeulen <[email protected]>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_RIGOL_DS2XX2_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_RIGOL_DS2XX2_PROTOCOL_H
+
+#include <stdint.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+/* Message logging helpers with subsystem-specific prefix string. */
+#define LOG_PREFIX "rigol-ds2xx2: "
+#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
+#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
+#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
+#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
+#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
+#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
+
+#define ANALOG_WAVEFORM_SIZE 1400
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+        /* Device properties */
+        const uint64_t (*timebases)[2];
+        uint64_t num_timebases;
+    
+       /* Acquisition settings */
+       GSList *enabled_analog_probes;
+       uint64_t limit_frames;
+       void *cb_data;
+
+       /* Device settings */
+       gboolean analog_channels[2];
+       float timebase;
+       float vdiv[2];
+        int   vert_reference[2];
+       float vert_offset[2];
+       char *trigger_source;
+       float horiz_triggerpos;
+       char *trigger_slope;
+       char *coupling[2];
+
+       /* Operational state */
+       uint64_t num_frames;
+        /* FIXME: misnomer, actually this is number of frame samples? */
+       uint64_t num_frame_bytes;
+       struct sr_probe *channel_frame;
+        /* Number of bytes in current data block, if 0 block header expected */
+        uint64_t num_block_bytes;
+        /* Number of data block bytes already read */
+        uint64_t num_block_read;
+        /* Trigger waiting status, 0 - don't wait */
+        int trigger_wait_status;
+};
+
+SR_PRIV int rigol_ds2xx2_acquisition_start(const struct sr_dev_inst *sdi, 
gboolean wait_for_trigger);
+SR_PRIV int rigol_ds2xx2_receive(int fd, int revents, void *cb_data);
+SR_PRIV int rigol_ds2xx2_send(const struct sr_dev_inst *sdi, const char 
*format, ...);
+SR_PRIV int rigol_ds2xx2_get_dev_cfg(const struct sr_dev_inst *sdi);
+
+#endif
diff -urNx .git libsigrok.org/hwdriver.c libsigrok/hwdriver.c
--- libsigrok.org/hwdriver.c    2013-10-27 16:44:51.786590209 +0100
+++ libsigrok/hwdriver.c        2013-10-27 16:46:15.190828799 +0100
@@ -148,6 +148,9 @@
 #ifdef HAVE_HW_RIGOL_DS1XX2
 extern SR_PRIV struct sr_dev_driver rigol_ds1xx2_driver_info;
 #endif
+#ifdef HAVE_HW_RIGOL_DS2XX2
+extern SR_PRIV struct sr_dev_driver rigol_ds2xx2_driver_info;
+#endif
 #ifdef HAVE_HW_SALEAE_LOGIC16
 extern SR_PRIV struct sr_dev_driver saleae_logic16_driver_info;
 #endif
@@ -268,6 +271,9 @@
 #ifdef HAVE_HW_RIGOL_DS1XX2
        &rigol_ds1xx2_driver_info,
 #endif
+#ifdef HAVE_HW_RIGOL_DS2XX2
+       &rigol_ds2xx2_driver_info,
+#endif
 #ifdef HAVE_HW_SALEAE_LOGIC16
        &saleae_logic16_driver_info,
 #endif
diff -urNx .git libsigrok.org/README.devices libsigrok/README.devices
--- libsigrok.org/README.devices        2013-10-09 23:51:06.000000000 +0200
+++ libsigrok/README.devices    2013-10-27 16:46:15.190828799 +0100
@@ -66,6 +66,7 @@
  - mic-985xx
  - openbench-logic-sniffer
  - rigol-ds1xx2
+ - rigol-ds2xx2
  - serial-dmm
  - tondaj-sl-814
  - uni-t-dmm
@@ -115,6 +116,7 @@
  - kecheng-kc-330b
  - lascar-el-usb
  - rigol-ds1xx2
+ - rigol-ds2xx2
  - saleae-logic16
  - uni-t-dmm
  - uni-t-ut32x
@@ -353,3 +355,13 @@
 library (which uses libusb) soon, which will fix all these issues and make
 the driver portable at the same time.
 
+
+Rigol DS2xx2 oscilloscopes
+--------------------------
+
+Because the 'rigol-ds2xx2' driver is based on the 'rigol-ds1xx2' driver it
+shares its dependency on the Linux usbtmc kernel driver.
+
+The driver was tested with a DS2072 DSO using firmware version 01.01.00.02.
+Be aware that older firmware versions have known bugs regarding fetching
+sample data from the device.

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk
_______________________________________________
sigrok-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/sigrok-devel

Reply via email to