]> sigrok.org Git - libsigrok.git/commitdiff
siglent-sds: initial driver implementation for Siglent SDS
authormhooijboer <redacted>
Sun, 11 Feb 2018 17:11:58 +0000 (18:11 +0100)
committerUwe Hermann <redacted>
Sun, 11 Feb 2018 23:13:49 +0000 (00:13 +0100)
Implement a first version of the driver for the Siglent SDS1000 and
SDS2000 oscilloscopes.

[ gsi: This commit corresponds to git 0228126017e6 of marchelh's tree,
  I adjusted the source to closer match the project's coding style. The
  conversion was verified by this command:

  $ git diff --word-diff=color -w -b <branch>:<dir> <branch>:<dir>

  Changes include: Whitespace adjustment, dead code removal, separation
  of variable declaration and assignment, alpha-sorted includes. Line
  length was not addressed and not every location got adjusted, to keep
  the diff minimal and to reduce effort during review of this version. ]

src/hardware/siglent-sds/api.c
src/hardware/siglent-sds/protocol.c
src/hardware/siglent-sds/protocol.h

index 8e5a8d1f075189ba87de4358dc4ee213c1dfb149..310c9fd0982079f8ec208eb740b330acf20592f3 100644 (file)
  */
 
 #include <config.h>
+
+#include <fcntl.h>
+#include <glib.h>
+#include <math.h>
+#include <rpc/rpc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
 #include "protocol.h"
+#include "scpi.h"
 
-static GSList *scan(struct sr_dev_driver *di, GSList *options)
+static const uint32_t scanopts[] = {
+       SR_CONF_CONN,
+       SR_CONF_SERIALCOMM
+};
+
+static const uint32_t devopts[] = {
+       SR_CONF_OSCILLOSCOPE,
+       SR_CONF_LOGIC_ANALYZER,
+       SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_TRIGGER_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_TRIGGER_LEVEL | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_HORIZ_TRIGGERPOS | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_NUM_HDIV | SR_CONF_GET | SR_CONF_LIST,
+       SR_CONF_SAMPLERATE | SR_CONF_GET,
+       SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_AVERAGING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_AVG_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const uint32_t analog_devopts[] = {
+       SR_CONF_NUM_VDIV | SR_CONF_GET,
+       SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_PROBE_FACTOR | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+static const uint64_t timebases[][2] = {
+       /* nanoseconds */
+       { 1, 1000000000 },
+       { 2, 1000000000 },
+       { 5, 1000000000 },
+       { 10, 1000000000 },
+       { 20, 1000000000 },
+       { 50, 1000000000 },
+       { 100, 1000000000 },
+       { 200, 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 },
+};
+
+static const uint64_t vdivs[][2] = {
+       /* microvolts */
+       { 500, 100000 },
+       /* 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 },
+       { 20, 1 },
+       { 50, 1 },
+       { 100, 1 },
+};
+
+#define NUM_TIMEBASE   ARRAY_SIZE(timebases)
+#define NUM_VDIV       ARRAY_SIZE(vdivs)
+
+static const char *trigger_sources[] = {
+       "CH1",
+       "CH2",
+       "Ext",
+       "Ext /5",
+       "AC Line",
+       "D0",
+       "D1",
+       "D2",
+       "D3",
+       "D4",
+       "D5",
+       "D6",
+       "D7",
+       "D8",
+       "D9",
+       "D10",
+       "D11",
+       "D12",
+       "D13",
+       "D14",
+       "D15",
+};
+
+static const char *trigger_slopes[] = {
+       "Rising",
+       "Falling",
+};
+
+static const char *coupling[] = {
+       "A1M AC 1 Meg",
+       "A50 AC 50 Ohm",
+       "D1M DC 1 Meg",
+       "D50 DC 50 Ohm",
+       "GND",
+};
+
+static const uint64_t probe_factor[] = {
+       1,
+       2,
+       5,
+       10,
+       20,
+       50,
+       100,
+       200,
+       500,
+       1000,
+       2000,
+       5000,
+       10000,
+};
+
+/* Do not change the order of entries */
+static const char *data_sources[] = {
+       "Display",
+       "History",
+};
+
+enum vendor {
+       SIGLENT,
+};
+
+enum series {
+       SDS1000CML,
+       SDS1000CNL,
+       SDS1000DL,
+       SDS1000X,
+       SDS1000XP,
+       SDS1000XE,
+       SDS2000X,
+};
+
+/* short name, full name, USB Name */
+static const struct siglent_sds_vendor supported_vendors[] = {
+       [SIGLENT] = {
+               "Siglent", "Siglent Technologies", "Siglent Technologies Co,. Ltd.",
+       },
+};
+
+#define VENDOR(x) &supported_vendors[x]
+/* vendor, series, protocol, max timebase, min vdiv, number of horizontal divs,
+ * number of vertical divs, live waveform samples, memory buffer samples */
+static const struct siglent_sds_series supported_series[] = {
+       [SDS1000CML] = {
+               VENDOR(SIGLENT), "SDS1000CML", NON_SPO_MODEL,
+               { 50, 1 },
+               { 2, 1000 }, 18, 8, 1400363,
+       },
+       [SDS1000CNL] = {
+               VENDOR(SIGLENT), "SDS1000CNL", NON_SPO_MODEL,
+               { 50, 1 },
+               { 2, 1000 }, 18, 8, 1400363,
+       },
+       [SDS1000DL] = {
+               VENDOR(SIGLENT), "SDS1000DL", NON_SPO_MODEL,
+               { 50, 1 },
+               { 2, 1000 }, 18, 8, 1400363,
+       },
+       [SDS1000X] = {
+               VENDOR(SIGLENT), "SDS1000X", SPO_MODEL,
+               { 50, 1 },
+               { 500, 100000 }, 14, 8, 14000363,
+       },
+       [SDS1000XP] = {
+               VENDOR(SIGLENT), "SDS1000X+", SPO_MODEL,
+               { 50, 1 },
+               { 500, 100000 }, 14, 8, 14000363,
+       },
+       [SDS1000XE] = {
+               VENDOR(SIGLENT), "SDS1000XE", SPO_MODEL,
+               { 50, 1 },
+               { 500, 100000 }, 14, 8, 14000363,
+       },
+       [SDS2000X] = {
+               VENDOR(SIGLENT), "SDS2000X", SPO_MODEL,
+               { 50, 1 },
+               { 500, 100000 }, 14, 8, 14000363,
+       },
+};
+
+#define SERIES(x) &supported_series[x]
+/* series, model, min timebase, analog channels, digital */
+static const struct siglent_sds_model supported_models[] = {
+       { SERIES(SDS1000CML), "SDS1152CML",
+               { 20, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000CML), "SDS1102CML",
+               { 10, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000CML), "SDS1072CML",
+               { 5, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000CNL), "SDS1202CNL",
+               { 20, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000CNL), "SDS1102CNL",
+               { 10, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000CNL), "SDS1072CNL",
+               { 5, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000DL), "SDS1202DL",
+               { 20, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000DL), "SDS1102DL",
+               { 10, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000DL), "SDS1022DL",
+               { 5, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000DL), "SDS1052DL",
+               { 5, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000X), "SDS1102X",
+               { 2, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000XP), "SDS1102X+",
+               { 2, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000X), "SDS1202X",
+               { 2, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000XP), "SDS1202X+",
+               { 2, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000XE), "SDS1202X-E",
+               { 1, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS1000XE), "SDS1104X-E",
+               { 1, 1000000000 }, 4, true, 16 },
+       { SERIES(SDS1000XE), "SDS1204X-E",
+               { 1, 1000000000 }, 4, true, 16 },
+       { SERIES(SDS2000X), "SDS2072X",
+               { 2, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS2000X), "SDS2074X",
+               { 2, 1000000000 }, 4, false, 0 },
+       { SERIES(SDS2000X), "SDS2102X",
+               { 2, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS2000X), "SDS2104X",
+               { 2, 1000000000 }, 4, false, 0 },
+       { SERIES(SDS2000X), "SDS2202X",
+               { 2, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS2000X), "SDS2204X",
+               { 2, 1000000000 }, 4, false, 0 },
+       { SERIES(SDS2000X), "SDS2302X",
+               { 2, 1000000000 }, 2, false, 0 },
+       { SERIES(SDS2000X), "SDS2304X",
+               { 2, 1000000000 }, 4, false, 0 },
+};
+
+SR_PRIV struct sr_dev_driver siglent_sds_driver_info;
+
+static void clear_helper(void *priv)
+{
+       struct dev_context *devc;
+
+       devc = priv;
+       if (!devc)
+               return;
+       g_free(devc->analog_groups);
+       g_free(devc->enabled_channels);
+}
+
+static int dev_clear(const struct sr_dev_driver *di)
+{
+
+       return std_dev_clear_with_callback(di, clear_helper);
+}
+
+static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
 {
-       struct drv_context *drvc;
-       GSList *devices;
+       struct dev_context *devc;
+       struct sr_dev_inst *sdi;
+       struct sr_scpi_hw_info *hw_info;
+       struct sr_channel *ch;
+       unsigned int i;
+       const struct siglent_sds_model *model;
+       gchar *channel_name;
+
+       sr_info("Device probing decode...");
 
-       (void)options;
+       /* Setting communication Header Format to OFF*/
+       sr_dbg("Setting Communication Headers to off");
+       if (sr_scpi_send(scpi, "CHDR OFF") != SR_OK)
+               return NULL;
 
-       devices = NULL;
-       drvc = di->context;
-       drvc->instances = NULL;
+       if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
+               sr_info("Couldn't get IDN response, retrying.");
+               sr_scpi_close(scpi);
+               sr_scpi_open(scpi);
+               if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
+                       sr_info("Couldn't get IDN response.");
+                       return NULL;
+               }
+       }
+
+       model = NULL;
+       for (i = 0; i < ARRAY_SIZE(supported_models); i++) {
+               if (!strcmp(hw_info->model, supported_models[i].name)) {
+                       model = &supported_models[i];
+                       break;
+               }
+       }
 
-       /* TODO: scan for devices, either based on a SR_CONF_CONN option
-        * or on a USB scan. */
+       sr_info("Decoded Manufacturer: %s", hw_info->manufacturer);
+       sr_info("Decoded Model: %s", hw_info->model);
 
-       return devices;
+       if (!model) {
+               sr_scpi_hw_info_free(hw_info);
+               return NULL;
+       }
+
+       sdi = g_malloc0(sizeof(struct sr_dev_inst));
+       sr_dbg("Setting Device Instance Vendor: %s", model->series->vendor->name);
+       sdi->vendor = g_strdup(model->series->vendor->name);
+       sr_dbg("Setting Device Instance model: %s", model->name);
+       sdi->model = g_strdup(model->name);
+       sr_dbg("Setting Device Instance version: %s", hw_info->firmware_version);
+       sdi->version = g_strdup(hw_info->firmware_version);
+       sdi->conn = scpi;
+       sdi->driver = &siglent_sds_driver_info;
+       sr_dbg("Setting Device Instance inst_type: SCPI");
+       sdi->inst_type = SR_INST_SCPI;
+       sdi->serial_num = g_strdup(hw_info->serial_number);
+       devc = g_malloc0(sizeof(struct dev_context));
+       devc->limit_frames = 1;
+       devc->model = model;
+       sr_dbg("Setting device Context model: %s", devc->model->name);
+
+       sr_scpi_hw_info_free(hw_info);
+
+       devc->analog_groups = g_malloc0(sizeof(struct sr_channel_group *) *
+               model->analog_channels);
+
+       for (i = 0; i < model->analog_channels; i++) {
+               channel_name = g_strdup_printf("CH%d", i + 1);
+               ch = sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE, channel_name);
+
+               devc->analog_groups[i] = g_malloc0(sizeof(struct sr_channel_group));
+
+               devc->analog_groups[i]->name = channel_name;
+               devc->analog_groups[i]->channels = g_slist_append(NULL, ch);
+               sdi->channel_groups = g_slist_append(sdi->channel_groups,
+                       devc->analog_groups[i]);
+       }
+
+       if (devc->model->has_digital) {
+               devc->digital_group = g_malloc0(sizeof(struct sr_channel_group));
+
+               for (i = 0; i < ARRAY_SIZE(devc->digital_channels); i++) {
+                       channel_name = g_strdup_printf("D%d", i);
+                       ch = sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, channel_name);
+                       g_free(channel_name);
+                       devc->digital_group->channels = g_slist_append(
+                               devc->digital_group->channels, ch);
+               }
+               devc->digital_group->name = g_strdup("LA");
+               sdi->channel_groups = g_slist_append(sdi->channel_groups,
+                       devc->digital_group);
+       }
+
+       for (i = 0; i < NUM_TIMEBASE; i++) {
+               if (!memcmp(&devc->model->min_timebase, &timebases[i], sizeof(uint64_t[2])))
+                       devc->timebases = &timebases[i];
+
+               if (!memcmp(&devc->model->series->max_timebase, &timebases[i], sizeof(uint64_t[2])))
+                       devc->num_timebases = &timebases[i] - devc->timebases + 1;
+       }
+
+       for (i = 0; i < NUM_VDIV; i++) {
+               devc->vdivs = &vdivs[i];
+               if (!memcmp(&devc->model->series->min_vdiv,
+                       &vdivs[i], sizeof(uint64_t[2]))) {
+                       devc->vdivs = &vdivs[i];
+                       devc->num_vdivs = NUM_VDIV - i;
+                       break;
+               }
+       }
+
+       devc->buffer = g_malloc(devc->model->series->buffer_samples);
+       sr_dbg("Setting device Context buffer Size: %i", devc->model->series->buffer_samples);
+       devc->data = g_malloc(devc->model->series->buffer_samples * sizeof(float));
+
+       devc->data_source = DATA_SOURCE_SCREEN;
+
+       sdi->priv = devc;
+
+       return sdi;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+
+       // TODO implement RPC call for LXI device discovery.
+       return sr_scpi_scan(di->context, options, probe_device);
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
 {
-       (void)sdi;
+       int ret;
+       struct sr_scpi_dev_inst *scpi = sdi->conn;
+
+       if ((ret = sr_scpi_open(scpi)) < 0) {
+               sr_err("Failed to open SCPI device: %s.", sr_strerror(ret));
+               return SR_ERR;
+       }
 
-       /* TODO: get handle from sdi->conn and open it. */
+       if ((ret = siglent_sds_get_dev_cfg(sdi)) < 0) {
+               sr_err("Failed to get device config: %s.", sr_strerror(ret));
+               return SR_ERR;
+       }
+
+       sdi->status = SR_ST_ACTIVE;
 
        return SR_OK;
 }
 
 static int dev_close(struct sr_dev_inst *sdi)
 {
-       (void)sdi;
 
-       /* TODO: get handle from sdi->conn and close it. */
-
-       return SR_OK;
+       return sr_scpi_close(sdi->conn);
 }
 
+
 static int config_get(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
+       struct dev_context *devc;
+       struct sr_channel *ch;
+       const char *tmp_str;
+       int analog_channel = -1;
+       float smallest_diff = INFINITY;
+       int idx = -1;
+       unsigned i;
 
-       (void)sdi;
-       (void)data;
-       (void)cg;
+       if (!sdi)
+               return SR_ERR_ARG;
+
+       devc = sdi->priv;
+
+       /* If a channel group is specified, it must be a valid one. */
+       if (cg && !g_slist_find(sdi->channel_groups, cg)) {
+               sr_err("Invalid channel group specified.");
+               return SR_ERR;
+       }
+
+       if (cg) {
+               ch = g_slist_nth_data(cg->channels, 0);
+               if (!ch)
+                       return SR_ERR;
+               if (ch->type == SR_CHANNEL_ANALOG) {
+                       if (ch->name[2] < '1' || ch->name[2] > '4')
+                               return SR_ERR;
+                       analog_channel = ch->name[2] - '1';
+               }
+       }
 
-       ret = SR_OK;
        switch (key) {
-       /* TODO */
+       case SR_CONF_NUM_HDIV:
+               *data = g_variant_new_int32(devc->model->series->num_horizontal_divs);
+               break;
+       case SR_CONF_NUM_VDIV:
+               *data = g_variant_new_int32(devc->num_vdivs);
+               break;
+       case SR_CONF_LIMIT_FRAMES:
+               *data = g_variant_new_uint64(devc->limit_frames);
+               break;
+       case SR_CONF_DATA_SOURCE:
+               if (devc->data_source == DATA_SOURCE_SCREEN)
+                       *data = g_variant_new_string("Screen");
+               else if (devc->data_source == DATA_SOURCE_HISTORY)
+                       *data = g_variant_new_string("History");
+               break;
+       case SR_CONF_SAMPLERATE:
+               siglent_sds_get_dev_cfg_horizontal(sdi);
+               *data = g_variant_new_uint64(devc->sampleRate);
+               sr_dbg("Sample rate set to %f", devc->sampleRate);
+               break;
+       case SR_CONF_TRIGGER_SOURCE:
+               if (!strcmp(devc->trigger_source, "ACL"))
+                       tmp_str = "AC Line";
+               else if (!strcmp(devc->trigger_source, "CHAN1"))
+                       tmp_str = "CH1";
+               else if (!strcmp(devc->trigger_source, "CHAN2"))
+                       tmp_str = "CH2";
+               else
+                       tmp_str = devc->trigger_source;
+               *data = g_variant_new_string(tmp_str);
+               break;
+       case SR_CONF_TRIGGER_SLOPE:
+               if (!strncmp(devc->trigger_slope, "POS", 3)) {
+                       tmp_str = "r";
+               } else if (!strncmp(devc->trigger_slope, "NEG", 3)) {
+                       tmp_str = "f";
+               } else {
+                       sr_dbg("Unknown trigger slope: '%s'.", devc->trigger_slope);
+                       return SR_ERR_NA;
+               }
+               *data = g_variant_new_string(tmp_str);
+               break;
+       case SR_CONF_TRIGGER_LEVEL:
+               *data = g_variant_new_double(devc->trigger_level);
+               break;
+       case SR_CONF_HORIZ_TRIGGERPOS:
+               *data = g_variant_new_double(devc->horiz_triggerpos);
+               break;
+       case SR_CONF_TIMEBASE:
+               for (i = 0; i < devc->num_timebases; i++) {
+                       float tb, diff;
+
+                       tb = (float)devc->timebases[i][0] / devc->timebases[i][1];
+                       diff = fabs(devc->timebase - tb);
+                       if (diff < smallest_diff) {
+                               smallest_diff = diff;
+                               idx = i;
+                       }
+               }
+               if (idx < 0) {
+                       sr_dbg("Negative timebase index: %d.", idx);
+                       return SR_ERR_NA;
+               }
+               *data = g_variant_new("(tt)", devc->timebases[idx][0],
+                       devc->timebases[idx][1]);
+               break;
+       case SR_CONF_VDIV:
+               if (analog_channel < 0) {
+                       sr_dbg("Negative analog channel: %d.", analog_channel);
+                       return SR_ERR_NA;
+               }
+               for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
+                       float vdiv, diff;
+
+                       vdiv = (float)vdivs[i][0] / vdivs[i][1];
+                       diff = fabsf(devc->vdiv[analog_channel] - vdiv);
+                       if (diff < smallest_diff) {
+                               smallest_diff = diff;
+                               idx = i;
+                       }
+               }
+               if (idx < 0) {
+                       sr_dbg("Negative vdiv index: %d.", idx);
+                       return SR_ERR_NA;
+               }
+               *data = g_variant_new("(tt)", vdivs[idx][0], vdivs[idx][1]);
+               break;
+       case SR_CONF_COUPLING:
+               if (analog_channel < 0) {
+                       sr_dbg("Negative analog channel: %d.", analog_channel);
+                       return SR_ERR_NA;
+               }
+               *data = g_variant_new_string(devc->coupling[analog_channel]);
+               break;
+       case SR_CONF_PROBE_FACTOR:
+               if (analog_channel < 0) {
+                       sr_dbg("Negative analog channel: %d.", analog_channel);
+                       return SR_ERR_NA;
+               }
+               *data = g_variant_new_uint64(devc->attenuation[analog_channel]);
+               break;
        default:
                return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int config_set(uint32_t key, GVariant *data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
+       struct dev_context *devc;
+       uint64_t p, q;
+       double t_dbl;
+       unsigned int i, j;
        int ret;
+       const char *tmp_str;
+       char buffer[16];
 
-       (void)sdi;
-       (void)data;
-       (void)cg;
+       devc = sdi->priv;
+
+       if (sdi->status != SR_ST_ACTIVE)
+               return SR_ERR_DEV_CLOSED;
+
+       /* If a channel group is specified, it must be a valid one. */
+       if (cg && !g_slist_find(sdi->channel_groups, cg)) {
+               sr_err("Invalid channel group specified.");
+               return SR_ERR;
+       }
 
        ret = SR_OK;
        switch (key) {
-       /* TODO */
+       case SR_CONF_LIMIT_FRAMES:
+               devc->limit_frames = g_variant_get_uint64(data);
+               break;
+       case SR_CONF_TRIGGER_SLOPE:
+               tmp_str = g_variant_get_string(data, NULL);
+               g_free(devc->trigger_slope);
+               ret = siglent_sds_config_set(sdi, "%s:TRSL %s", devc->trigger_source, 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) {
+                       sr_err("Invalid horiz. trigger position: %g.", t_dbl);
+                       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;
+               g_ascii_formatd(buffer, sizeof(buffer), "%.6f", t_dbl);
+               ret = siglent_sds_config_set(sdi, ":TIM:OFFS %s", buffer);
+               break;
+       case SR_CONF_TRIGGER_LEVEL:
+               t_dbl = g_variant_get_double(data);
+               g_ascii_formatd(buffer, sizeof(buffer), "%.3f", t_dbl);
+               ret = siglent_sds_config_set(sdi, ":TRIG:EDGE:LEV %s", buffer);
+               if (ret == SR_OK)
+                       devc->trigger_level = t_dbl;
+               break;
+       case SR_CONF_TIMEBASE:
+               sr_dbg("Setting device Timebase");
+               g_variant_get(data, "(tt)", &p, &q);
+               for (i = 0; i < devc->num_timebases; i++) {
+                       char *cmd;
+                       if (devc->timebases[i][0] == p && devc->timebases[i][1] == q) {
+                               cmd = "";
+                               devc->timebase = (float) p / q;
+                               switch (q) {
+                               case 1:
+                                       cmd = g_strdup_printf("%luS", p);
+                                       break;
+                               case 1000:
+                                       cmd = g_strdup_printf("%luMS", p);
+                                       break;
+                               case 1000000:
+                                       cmd = g_strdup_printf("%luUS", p);
+                                       break;
+                               case 100000000:
+                                       cmd = g_strdup_printf("%luNS", p);
+                                       break;
+                               }
+                               sr_dbg("Setting device Timebase: TDIV %s", cmd);
+                               ret = siglent_sds_config_set(sdi, "TDIV %s", cmd);
+                               break;
+                       }
+               }
+               if (i == devc->num_timebases) {
+                       sr_err("Invalid timebase index: %d.", i);
+                       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 = "LINE";
+                               else if (!strcmp(devc->trigger_source, "CH1"))
+                                       tmp_str = "C1";
+                               else if (!strcmp(devc->trigger_source, "CH2"))
+                                       tmp_str = "C2";
+                               else if (!strcmp(devc->trigger_source, "CH3"))
+                                       tmp_str = "C3";
+                               else if (!strcmp(devc->trigger_source, "CH4"))
+                                       tmp_str = "C4";
+                               else if (!strcmp(devc->trigger_source, "Ext"))
+                                       tmp_str = "EX";
+                               else if (!strcmp(devc->trigger_source, "Ext /5"))
+                                       tmp_str = "EX5";
+                               else
+                                       tmp_str = (char *) devc->trigger_source;
+                               ret = siglent_sds_config_set(sdi, "TRSE EDGE,SR,%s,OFF", tmp_str);
+                               break;
+                       }
+               }
+               if (i == ARRAY_SIZE(trigger_sources)) {
+                       sr_err("Invalid trigger source index: %d.", i);
+                       ret = SR_ERR_ARG;
+               }
+               break;
+       case SR_CONF_VDIV:
+               if (!cg) {
+                       sr_err("No channel group specified.");
+                       return SR_ERR_CHANNEL_GROUP;
+               }
+               g_variant_get(data, "(tt)", &p, &q);
+               for (i = 0; i < devc->model->analog_channels; i++) {
+                       char *cmd;
+                       if (cg == devc->analog_groups[i]) {
+                               for (j = 0; j < ARRAY_SIZE(vdivs); j++) {
+                                       if (vdivs[j][0] != p || vdivs[j][1] != q)
+                                               continue;
+                                       cmd = "";
+                                       switch (q) {
+                                       case 1:
+                                               cmd = g_strdup_printf("%luV", p);
+                                               break;
+                                       case 1000:
+                                               cmd = g_strdup_printf("%luMV", p);
+                                               break;
+                                       case 100000:
+                                               cmd = g_strdup_printf("%luUV", p);
+                                               break;
+                                       }
+                                       return siglent_sds_config_set(sdi, "C%d:VDIV %s", i + 1,
+                                               cmd);
+                               }
+                               sr_err("Invalid vdiv index: %d.", j);
+                               return SR_ERR_ARG;
+                       }
+               }
+               sr_dbg("Didn't set vdiv, unknown channel(group).");
+               return SR_ERR_NA;
+       case SR_CONF_COUPLING:
+               if (!cg) {
+                       sr_err("No channel group specified.");
+                       return SR_ERR_CHANNEL_GROUP;
+               }
+               tmp_str = g_variant_get_string(data, NULL);
+               for (i = 0; i < devc->model->analog_channels; i++) {
+                       char cmd[4];
+                       if (cg == devc->analog_groups[i]) {
+                               for (j = 0; j < ARRAY_SIZE(coupling); j++) {
+                                       if (!strcmp(tmp_str, coupling[j])) {
+                                               g_free(devc->coupling[i]);
+                                               devc->coupling[i] = g_strdup(coupling[j]);
+                                               strncpy(cmd, devc->coupling[i], 3);
+                                               cmd[3] = 0;
+                                               return siglent_sds_config_set(sdi, "C%d:CPL %s", i + 1,
+                                                       cmd);
+                                       }
+                               }
+                               sr_err("Invalid coupling index: %d.", j);
+                               return SR_ERR_ARG;
+                       }
+               }
+               sr_dbg("Didn't set coupling, unknown channel(group).");
+               return SR_ERR_NA;
+       case SR_CONF_PROBE_FACTOR:
+               if (!cg) {
+                       sr_err("No channel group specified.");
+                       return SR_ERR_CHANNEL_GROUP;
+               }
+               p = g_variant_get_uint64(data);
+               for (i = 0; i < devc->model->analog_channels; i++) {
+                       if (cg == devc->analog_groups[i]) {
+                               for (j = 0; j < ARRAY_SIZE(probe_factor); j++) {
+                                       if (p == probe_factor[j]) {
+                                               devc->attenuation[i] = p;
+                                               ret = siglent_sds_config_set(sdi, "C%d:ATTN %"PRIu64,
+                                                       i + 1, p);
+                                               if (ret == SR_OK)
+                                                       siglent_sds_get_dev_cfg_vertical(sdi);
+                                               return ret;
+                                       }
+                               }
+                               sr_err("Invalid probe factor: %"PRIu64".", p);
+                               return SR_ERR_ARG;
+                       }
+               }
+               sr_dbg("Didn't set probe factor, unknown channel(group).");
+               return SR_ERR_NA;
+       case SR_CONF_DATA_SOURCE:
+               tmp_str = g_variant_get_string(data, NULL);
+               if (!strcmp(tmp_str, "Display"))
+                       devc->data_source = DATA_SOURCE_SCREEN;
+               else if (devc->model->series->protocol >= SPO_MODEL
+                       && !strcmp(tmp_str, "History"))
+                       devc->data_source = DATA_SOURCE_HISTORY;
+               else {
+                       sr_err("Unknown data source: '%s'.", tmp_str);
+                       return SR_ERR;
+               }
+               break;
+       case SR_CONF_SAMPLERATE:
+               siglent_sds_get_dev_cfg_horizontal(sdi);
+               data = g_variant_new_uint64(devc->sampleRate);
+               break;
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
        return ret;
@@ -96,50 +844,279 @@ static int config_set(uint32_t key, GVariant *data,
 static int config_list(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
+       GVariant *tuple, *rational[2];
+       GVariantBuilder gvb;
+       unsigned int i;
+       struct dev_context *devc = NULL;
+
+       if (key == SR_CONF_SCAN_OPTIONS) {
+               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+                       scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+               return SR_OK;
+       } else if (key == SR_CONF_DEVICE_OPTIONS && !cg) {
+               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+               return SR_OK;
+       }
 
-       (void)sdi;
-       (void)data;
-       (void)cg;
+       /* Every other option requires a valid device instance. */
+       if (!sdi)
+               return SR_ERR_ARG;
+       devc = sdi->priv;
+
+       /* If a channel group is specified, it must be a valid one. */
+       if (cg && !g_slist_find(sdi->channel_groups, cg)) {
+               sr_err("Invalid channel group specified.");
+               return SR_ERR;
+       }
 
-       ret = SR_OK;
        switch (key) {
-       /* TODO */
+       case SR_CONF_DEVICE_OPTIONS:
+               if (!cg) {
+                       sr_err("No channel group specified.");
+                       return SR_ERR_CHANNEL_GROUP;
+               }
+               if (cg == devc->digital_group) {
+                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+                               NULL, 0, sizeof(uint32_t));
+                       return SR_OK;
+               } else {
+                       for (i = 0; i < devc->model->analog_channels; i++) {
+                               if (cg == devc->analog_groups[i]) {
+                                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+                                               analog_devopts, ARRAY_SIZE(analog_devopts), sizeof(uint32_t));
+                                       return SR_OK;
+                               }
+                       }
+                       return SR_ERR_NA;
+               }
+               break;
+       case SR_CONF_COUPLING:
+               if (!cg) {
+                       sr_err("No channel group specified.");
+                       return SR_ERR_CHANNEL_GROUP;
+               }
+               *data = g_variant_new_strv(coupling, ARRAY_SIZE(coupling));
+               break;
+       case SR_CONF_PROBE_FACTOR:
+               if (!cg) {
+                       sr_err("No channel group specified.");
+                       return SR_ERR_CHANNEL_GROUP;
+               }
+               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT64,
+                       probe_factor, ARRAY_SIZE(probe_factor), sizeof(uint64_t));
+               break;
+       case SR_CONF_VDIV:
+               if (!devc)
+                       /* Can't know this until we have the exact model. */
+                       return SR_ERR_ARG;
+               if (!cg) {
+                       sr_err("No channel group specified.");
+                       return SR_ERR_CHANNEL_GROUP;
+               }
+               g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+               for (i = 0; i < devc->num_vdivs; i++) {
+                       rational[0] = g_variant_new_uint64(devc->vdivs[i][0]);
+                       rational[1] = g_variant_new_uint64(devc->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:
+               if (!devc)
+                       /* Can't know this until we have the exact model. */
+                       return SR_ERR_ARG;
+               if (devc->num_timebases <= 0)
+                       return SR_ERR_NA;
+               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 (!devc)
+                       /* Can't know this until we have the exact model. */
+                       return SR_ERR_ARG;
+               *data = g_variant_new_strv(trigger_sources,
+                       devc->model->has_digital ? ARRAY_SIZE(trigger_sources) : 5);
+               break;
+       case SR_CONF_TRIGGER_SLOPE:
+               *data = g_variant_new_strv(trigger_slopes, ARRAY_SIZE(trigger_slopes));
+               break;
+       case SR_CONF_DATA_SOURCE:
+               if (!devc)
+                       /* Can't know this until we have the exact model. */
+                       return SR_ERR_ARG;
+               switch (devc->model->series->protocol) {
+               // TODO check what must be done here for the data source buffer sizes
+               case NON_SPO_MODEL:
+                       *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources) - 1);
+                       break;
+               case SPO_MODEL:
+                       *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources));
+                       break;
+               }
+               break;
+       case SR_CONF_NUM_HDIV:
+               *data = g_variant_new_int32(devc->model->series->num_horizontal_divs);
+               break;
+       case SR_CONF_AVERAGING:
+               //TODO implement averaging.
+               break;
        default:
                return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 {
-       /* TODO: configure hardware, reset acquisition state, set up
-        * callbacks and send header packet. */
+       struct sr_scpi_dev_inst *scpi;
+       struct dev_context *devc;
+       struct sr_channel *ch;
+       struct sr_datafeed_packet packet;
+       gboolean some_digital;
+       GSList *l;
+       GSList *d;
 
-       (void)sdi;
+       if (sdi->status != SR_ST_ACTIVE)
+               return SR_ERR_DEV_CLOSED;
+
+       scpi = sdi->conn;
+       devc = sdi->priv;
+
+       devc->num_frames = 0;
+       some_digital = FALSE;
+
+       /* Check if there are any logic channels enabled, if so then enable de MSO, otherwise skip the digital channel setup */
+       /* enable and disable channels on the device is very slow and it is faster when checked in a small loop without the actual actions */
+       for (d = sdi->channels; d; d = d->next) {
+               ch = d->data;
+               if (ch->type == SR_CHANNEL_LOGIC && ch->enabled) {
+                       some_digital = TRUE;
+               }
+       }
+
+       for (l = sdi->channels; l; l = l->next) {
+               ch = l->data;
+               sr_dbg("handling channel %s", ch->name);
+               if (ch->type == SR_CHANNEL_ANALOG) {
+                       if (ch->enabled)
+                               devc->enabled_channels = g_slist_append(
+                                       devc->enabled_channels, ch);
+                       if (ch->enabled != devc->analog_channels[ch->index]) {
+                               /* Enabled channel is currently disabled, or vice versa. */
+                               if (siglent_sds_config_set(sdi, "C%d:TRA %s", ch->index + 1,
+                                       ch->enabled ? "ON" : "OFF") != SR_OK)
+                                       return SR_ERR;
+                               devc->analog_channels[ch->index] = ch->enabled;
+                       }
+               } else if (ch->type == SR_CHANNEL_LOGIC && some_digital) {
+                       if (ch->enabled) {
+                               /* Turn on LA module if currently off and digital channels are enabled. */
+                               if (!devc->la_enabled) {
+                                       if (siglent_sds_config_set(sdi, "DGST ON") != SR_OK)
+                                               return SR_ERR;
+                                       g_usleep(630000);
+                                       devc->la_enabled = TRUE;
+                               }
+                               devc->enabled_channels = g_slist_append(
+                                       devc->enabled_channels, ch);
+                       }
+                       /* Enabled channel is currently disabled, or vice versa. */
+                       if (siglent_sds_config_set(sdi, "D%d:DGCH %s", ch->index,
+                               ch->enabled ? "ON" : "OFF") != SR_OK)
+                               return SR_ERR;
+                       /* Slowing the command sequence down to let the device handle it */
+                       g_usleep(630000);
+                       devc->digital_channels[ch->index] = ch->enabled;
+               }
+       }
+       if (!devc->enabled_channels)
+               return SR_ERR;
+       /* Turn off LA module if on and no digital channels selected. */
+       if (devc->la_enabled && !some_digital)
+               if (siglent_sds_config_set(sdi, "DGST OFF") != SR_OK) {
+                       devc->la_enabled = FALSE;
+                       g_usleep(500000);
+                       return SR_ERR;
+               }
+
+       // devc->analog_frame_size = devc->model->series->buffer_samples;
+       // devc->digital_frame_size = devc->model->series->buffer_samples;
+
+       switch (devc->model->series->protocol) {
+       case SPO_MODEL:
+               if (siglent_sds_config_set(sdi, "WFSU SP,0,TYPE,1") != SR_OK)
+                       return SR_ERR;
+               if (siglent_sds_config_set(sdi, "ACQW SAMPLING") != SR_OK)
+                       return SR_ERR;
+               break;
+       case NON_SPO_MODEL:
+               //TODO implement CML/CNL/DL models
+               if (siglent_sds_config_set(sdi, "WFSU SP,0,TYPE,1") != SR_OK)
+                       return SR_ERR;
+               if (siglent_sds_config_set(sdi, "ACQW SAMPLING") != SR_OK)
+                       return SR_ERR;
+               break;
+       default:
+               break;
+       }
+
+       sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 50,
+               siglent_sds_receive, (void *) sdi);
+
+       std_session_send_df_header(sdi);
+
+       devc->channel_entry = devc->enabled_channels;
+
+       if (siglent_sds_capture_start(sdi) != SR_OK)
+               return SR_ERR;
+
+       /* Start of first frame. */
+       packet.type = SR_DF_FRAME_BEGIN;
+       sr_session_send(sdi, &packet);
 
        return SR_OK;
 }
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       /* TODO: stop acquisition. */
+       struct dev_context *devc;
+       struct sr_scpi_dev_inst *scpi;
+
+       devc = sdi->priv;
+
+       if (sdi->status != SR_ST_ACTIVE) {
+               sr_err("Device inactive, can't stop acquisition.");
+               return SR_ERR;
+       }
+
+       std_session_send_df_end(sdi);
 
-       (void)sdi;
+       g_slist_free(devc->enabled_channels);
+       devc->enabled_channels = NULL;
+       scpi = sdi->conn;
+       sr_scpi_source_remove(sdi->session, scpi);
 
        return SR_OK;
 }
 
 SR_PRIV struct sr_dev_driver siglent_sds_driver_info = {
        .name = "siglent-sds",
-       .longname = "siglent-sds",
+       .longname = "Siglent SDS1000/SDS2000 Series",
        .api_version = 1,
        .init = std_init,
        .cleanup = std_cleanup,
        .scan = scan,
        .dev_list = std_dev_list,
-       .dev_clear = std_dev_clear,
+       .dev_clear = dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
index 1f67fd774d20a13e1e49e8d8b0b5867eca4b7537..40f7d104db549403b38b4d6209e1491f4cfee960 100644 (file)
@@ -1,7 +1,13 @@
 /*
  * This file is part of the libsigrok project.
  *
- * Copyright (C) 2018 mhooijboer <marchelh@gmail.com>
+ * Siglent implementation:
+ * Copyright (C) 2016 mhooijboer <marchelh@gmail.com>
+ *
+ * The Siglent implementation is based on Rigol driver sources, which are:
+ * Copyright (C) 2012 Martin Ling <martin-git@earth.li>
+ * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
+ * Copyright (C) 2013 Mathias Grimmberger <mgri@zaphod.sax.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
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#define _GNU_SOURCE
+
 #include <config.h>
+
+#include <errno.h>
+#include <glib.h>
+#include <glib-2.0/glib/gmacros.h>
+#include <glib-2.0/glib/gmain.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <libsigrok/libsigrok.h>
+#include "libsigrok-internal.h"
+#include "scpi.h"
 #include "protocol.h"
 
-SR_PRIV int siglent_sds_receive_data(int fd, int revents, void *cb_data)
+/* Set the next event to wait for in siglent_sds_receive */
+static void siglent_sds_set_wait_event(struct dev_context *devc, enum wait_events event)
+{
+
+       if (event == WAIT_STOP) {
+               devc->wait_status = 2;
+       } else {
+               devc->wait_status = 1;
+               devc->wait_event = event;
+       }
+}
+
+/*
+ * Waiting for a event will return a timeout after 2 to 3 seconds in order
+ * to not block the application.
+ */
+static int siglent_sds_event_wait(const struct sr_dev_inst *sdi)
+{
+       char *buf;
+       long s;
+       int out;
+       struct dev_context *devc;
+       time_t start;
+
+       if (!(devc = sdi->priv))
+               return SR_ERR;
+
+       start = time(NULL);
+
+       s = 10000; /* Sleep time for status refresh */
+       if (devc->wait_status == 1) {
+               do {
+                       if (time(NULL) - start >= 3) {
+                               sr_dbg("Timeout waiting for trigger");
+                               return SR_ERR_TIMEOUT;
+                       }
+
+                       if (sr_scpi_get_string(sdi->conn, ":INR?", &buf) != SR_OK)
+                               return SR_ERR;
+                       sr_atoi(buf, &out);
+                       g_usleep(s);
+               } while (out == 0);
+               sr_dbg("Device triggerd");
+               if (devc->timebase < 0.51) {
+                       if (devc->timebase > 0.99e-6) {
+                               /*
+                                * Timebase * num hor. divs * 85(%) * 1e6(usecs) / 100
+                                * -> 85 percent of sweep time
+                                */
+                               s = (devc->timebase * devc->model->series->num_horizontal_divs * 1000);
+                               sr_spew("Sleeping for %ld usecs after trigger, to let the acq buffer in the device fill", s);
+                               g_usleep(s);
+                       }
+
+               }
+       }
+       if (devc->wait_status == 2) {
+               do {
+                       if (time(NULL) - start >= 3) {
+                               sr_dbg("Timeout waiting for trigger");
+                               return SR_ERR_TIMEOUT;
+                       }
+                       if (sr_scpi_get_string(sdi->conn, ":INR?", &buf) != SR_OK)
+                               return SR_ERR;
+                       sr_atoi(buf, &out);
+                       g_usleep(s);
+               /* XXX
+                * Now this loop condition looks suspicious! A bitwise
+                * OR of a variable and a non-zero literal should be
+                * non-zero. Logical AND of several non-zero values
+                * should be non-zero. Are many parts of the condition
+                * not taking effect? Was some different condition meant
+                * to get encoded? This needs review, and adjustment.
+                */
+               } while ((out | DEVICE_STATE_TRIG_RDY && out | DEVICE_STATE_DATA_ACQ) && out | DEVICE_STATE_STOPPED);
+               sr_dbg("Device triggerd 2");
+               siglent_sds_set_wait_event(devc, WAIT_NONE);
+       }
+
+       return SR_OK;
+}
+
+static int siglent_sds_trigger_wait(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+
+       if (!(devc = sdi->priv))
+               return SR_ERR;
+       return siglent_sds_event_wait(sdi);
+}
+
+/* Wait for scope to got to "Stop" in single shot mode */
+static int siglent_sds_stop_wait(const struct sr_dev_inst *sdi)
+{
+
+       return siglent_sds_event_wait(sdi);
+}
+
+/* Send a configuration setting. */
+SR_PRIV int siglent_sds_config_set(const struct sr_dev_inst *sdi, const char *format, ...)
+{
+       va_list args;
+       int ret;
+
+       va_start(args, format);
+       ret = sr_scpi_send_variadic(sdi->conn, format, args);
+       va_end(args);
+
+       if (ret != SR_OK) {
+               return SR_ERR;
+       }
+
+       return SR_OK;
+}
+
+/* Start capturing a new frameset */
+SR_PRIV int siglent_sds_capture_start(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+
+       if (!(devc = sdi->priv))
+               return SR_ERR;
+
+       switch (devc->model->series->protocol) {
+       case SPO_MODEL:
+               if (devc->data_source == DATA_SOURCE_SCREEN) {
+                       char *buf;
+                       int out;
+
+                       sr_dbg("Starting data capture for active frameset %" PRIu64 " of %" PRIu64,
+                               devc->num_frames + 1, devc->limit_frames);
+                       if (siglent_sds_config_set(sdi, "ARM") != SR_OK)
+                               return SR_ERR;
+                       if (sr_scpi_get_string(sdi->conn, ":INR?", &buf) != SR_OK)
+                               return SR_ERR;
+                       sr_atoi(buf, &out);
+                       if (out == DEVICE_STATE_TRIG_RDY) {
+                               siglent_sds_set_wait_event(devc, WAIT_TRIGGER);
+                       } else if (out == DEVICE_STATE_TRIG_RDY + 1) {
+                               sr_spew("Device Triggerd");
+                               siglent_sds_set_wait_event(devc, WAIT_BLOCK);
+                               return SR_OK;
+                       } else {
+                               sr_spew("Device did not enter ARM mode");
+                               return SR_ERR;
+                       }
+               } else { //TODO implement History retrieval
+                       unsigned int framecount;
+                       char buf[200];
+                       int ret;
+
+                       sr_dbg("Starting data capture for history frameset");
+                       if (siglent_sds_config_set(sdi, "FPAR?") != SR_OK)
+                               return SR_ERR;
+                       ret = sr_scpi_read_data(sdi->conn, buf, 200);
+                       if (ret < 0) {
+                               sr_err("Read error while reading data header.");
+                               return SR_ERR;
+                       }
+                       memcpy(&framecount, buf + 40, 4);
+                       if (devc->limit_frames > framecount) {
+                               sr_err("Frame limit higher that frames in buffer of device!");
+                       } else if (devc->limit_frames == 0) {
+                               devc->limit_frames = framecount;
+                       }
+                       sr_dbg("Starting data capture for history frameset %" PRIu64 " of %" PRIu64,
+                               devc->num_frames + 1, devc->limit_frames);
+                       if (siglent_sds_config_set(sdi, "FRAM %i", devc->num_frames + 1) != SR_OK)
+                               return SR_ERR;
+                       if (siglent_sds_channel_start(sdi) != SR_OK)
+                               return SR_ERR;
+                       siglent_sds_set_wait_event(devc, WAIT_STOP);
+               }
+               break;
+       case NON_SPO_MODEL:
+               siglent_sds_set_wait_event(devc, WAIT_TRIGGER);
+               break;
+       }
+
+       return SR_OK;
+}
+
+/* Start reading data from the current channel */
+SR_PRIV int siglent_sds_channel_start(const struct sr_dev_inst *sdi)
 {
-       const struct sr_dev_inst *sdi;
        struct dev_context *devc;
+       struct sr_channel *ch;
+
+       if (!(devc = sdi->priv))
+               return SR_ERR;
+
+       ch = devc->channel_entry->data;
+
+       sr_dbg("Starting reading data from channel %d", ch->index + 1);
+
+       switch (devc->model->series->protocol) {
+       case NON_SPO_MODEL:
+       case SPO_MODEL:
+               if (ch->type == SR_CHANNEL_LOGIC) {
+                       if (sr_scpi_send(sdi->conn, "D%d:WF?",
+                               ch->index + 1) != SR_OK)
+                               return SR_ERR;
+               } else {
+                       if (sr_scpi_send(sdi->conn, "C%d:WF? ALL",
+                               ch->index + 1) != SR_OK)
+                               return SR_ERR;
+               }
+               siglent_sds_set_wait_event(devc, WAIT_NONE);
+               break;
+       }
+
+       siglent_sds_set_wait_event(devc, WAIT_BLOCK);
+
+       devc->num_channel_bytes = 0;
+       devc->num_header_bytes = 0;
+       devc->num_block_bytes = 0;
+
+       return SR_OK;
+}
+
+/* Read the header of a data block */
+static int siglent_sds_read_header(struct sr_dev_inst *sdi, int channelIndex)
+{
+       struct sr_scpi_dev_inst *scpi = sdi->conn;
+       struct dev_context *devc = sdi->priv;
+       char *buf = (char *)devc->buffer;
+       int ret;
+       int descLength;
+       int blockOffset = 15; // Offset for Descriptor block.
+       long dataLength = 0;
+
+       /* Read header from device */
+       ret = sr_scpi_read_data(scpi, buf + devc->num_header_bytes, devc->model->series->buffer_samples);
+       if (ret < 346) {
+               sr_err("Read error while reading data header.");
+               return SR_ERR;
+       }
+       sr_dbg("Device returned %i bytes", ret);
+       devc->num_header_bytes += ret;
+       buf += blockOffset; //Skip to start Descriptor block
+
+       // Parse WaveDescriptor Header
+       memcpy(&descLength, buf + 36, 4); // Descriptor block length
+       memcpy(&dataLength, buf + 60, 4); // Data block length
+
+       devc->vdiv[channelIndex] = 2;
+       devc->vert_offset[channelIndex] = 0;
+       devc->blockHeaderSize = descLength + 15;
+       ret = dataLength;
+       sr_dbg("Received data block header: '%s' -> block length %d", buf, ret);
+       return ret;
+}
+
+SR_PRIV int siglent_sds_receive(int fd, int revents, void *cb_data)
+{
+       struct sr_dev_inst *sdi;
+       struct sr_scpi_dev_inst *scpi;
+       struct dev_context *devc;
+       struct sr_datafeed_packet packet;
+       struct sr_datafeed_analog analog;
+       struct sr_analog_encoding encoding;
+       struct sr_analog_meaning meaning;
+       struct sr_analog_spec spec;
+       struct sr_datafeed_logic logic;
+       int len, i;
+       struct sr_channel *ch;
+       gsize expected_data_bytes = 0;
+       char *memsize;
 
        (void)fd;
 
@@ -33,9 +320,399 @@ SR_PRIV int siglent_sds_receive_data(int fd, int revents, void *cb_data)
        if (!(devc = sdi->priv))
                return TRUE;
 
-       if (revents == G_IO_IN) {
-               /* TODO */
+       scpi = sdi->conn;
+
+       if (!(revents == G_IO_IN || revents == 0))
+               return TRUE;
+
+       memsize = NULL;
+       if (sr_scpi_get_string(sdi->conn, "MSIZ?", &memsize) != SR_OK)
+               return SR_ERR;
+
+       switch (devc->wait_event) {
+       case WAIT_NONE:
+               break;
+       case WAIT_TRIGGER:
+               if (siglent_sds_trigger_wait(sdi) != SR_OK)
+                       return TRUE;
+               if (siglent_sds_channel_start(sdi) != SR_OK)
+                       return TRUE;
+               return TRUE;
+       case WAIT_BLOCK:
+               if (siglent_sds_channel_start(sdi) != SR_OK)
+                       return TRUE;
+               break;
+       case WAIT_STOP:
+               if (siglent_sds_stop_wait(sdi) != SR_OK) {
+                       return TRUE;
+               }
+               if (siglent_sds_channel_start(sdi) != SR_OK) {
+                       return TRUE;
+               }
+               return TRUE;
+       default:
+               sr_err("BUG: Unknown event target encountered");
+               break;
+       }
+
+       ch = devc->channel_entry->data;
+
+       if (devc->num_block_bytes == 0) {
+
+               if (g_ascii_strcasecmp(memsize, "14M") == 0){
+                       sr_err("Device memory depth is set to 14Mpts, so please be patient");
+                       g_usleep(4900000); // Sleep for large memory set
+               }
+               sr_dbg("New block header expected");
+               len = siglent_sds_read_header(sdi, ch->index);
+               expected_data_bytes = len;
+               if (len == 0)
+                       /* Still reading the header. */
+                       return TRUE;
+               if (len == -1) {
+                       sr_err("Read error, aborting capture.");
+                       packet.type = SR_DF_FRAME_END;
+                       sr_session_send(sdi, &packet);
+                       sdi->driver->dev_acquisition_stop(sdi);
+                       return TRUE;
+               }
+
+               if (devc->data_source == DATA_SOURCE_SCREEN
+                       && (unsigned)len < expected_data_bytes) {
+                       sr_dbg("Discarding short data block");
+                       sr_scpi_read_data(scpi, (char *)devc->buffer, len + 1);
+                       return TRUE;
+               }
+               devc->num_block_bytes = len;
+               devc->num_block_read = 0;
+       }
+
+       len = devc->num_block_bytes - devc->num_block_read;
+       if (len > ACQ_BUFFER_SIZE)
+               len = ACQ_BUFFER_SIZE;
+
+       /*Offset the data block buffer past the IEEE header and Description Header*/
+       devc->buffer += devc->blockHeaderSize;
+
+       if (len == -1) {
+               sr_err("Read error, aborting capture.");
+               packet.type = SR_DF_FRAME_END;
+               sr_session_send(sdi, &packet);
+               sdi->driver->dev_acquisition_stop(sdi);
+               return TRUE;
        }
 
+       sr_dbg("Received %d bytes.", len);
+
+       devc->num_block_read += len;
+
+       if (ch->type == SR_CHANNEL_ANALOG) {
+               float vdiv = devc->vdiv[ch->index];
+               float offset = devc->vert_offset[ch->index];
+               GArray *float_data;
+               static GArray *data;
+               float voltage;
+               float vdivlog;
+               int digits;
+
+               data = g_array_sized_new(FALSE, FALSE, sizeof(uint8_t), len);
+               g_array_append_vals(data, devc->buffer, len);
+               float_data = g_array_new(FALSE, FALSE, sizeof(float));
+               for (i = 0; i < len; i++) {
+                       voltage = (float)g_array_index(data, int8_t, i) / 25;
+                       voltage = ((vdiv * voltage) - offset);
+                       g_array_append_val(float_data, voltage);
+               }
+               vdivlog = log10f(vdiv);
+               digits = -(int) vdivlog + (vdivlog < 0.0);
+               sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
+               analog.meaning->channels = g_slist_append(NULL, ch);
+               analog.num_samples = float_data->len;
+               analog.data = (float *)float_data->data;
+               analog.meaning->mq = SR_MQ_VOLTAGE;
+               analog.meaning->unit = SR_UNIT_VOLT;
+               analog.meaning->mqflags = 0;
+               packet.type = SR_DF_ANALOG;
+               packet.payload = &analog;
+               sr_session_send(sdi, &packet);
+               g_slist_free(analog.meaning->channels);
+               g_array_free(data, TRUE);
+       } else {
+               logic.length = len;
+               logic.unitsize = 1;
+               logic.data = devc->buffer;
+               packet.type = SR_DF_LOGIC;
+               packet.payload = &logic;
+               sr_session_send(sdi, &packet);
+       }
+
+       if (devc->num_block_read == devc->num_block_bytes) {
+               sr_dbg("Block has been completed");
+               /* Prepare for possible next block */
+               sr_dbg("Prepare for possible next block");
+               devc->num_header_bytes = 0;
+               devc->num_block_bytes = 0;
+               if (devc->data_source != DATA_SOURCE_SCREEN) {
+                       siglent_sds_set_wait_event(devc, WAIT_BLOCK);
+               }
+               if (!sr_scpi_read_complete(scpi)) {
+                       sr_err("Read should have been completed");
+                       packet.type = SR_DF_FRAME_END;
+                       sr_session_send(sdi, &packet);
+                       sdi->driver->dev_acquisition_stop(sdi);
+                       return TRUE;
+               }
+               devc->num_block_read = 0;
+       } else {
+               sr_dbg("%" PRIu64 " of %" PRIu64 " block bytes read",
+                       devc->num_block_read, devc->num_block_bytes);
+       }
+       devc->num_channel_bytes += len;
+       if (devc->num_channel_bytes < expected_data_bytes) {
+               /* Don't have the full data for this channel yet, re-run. */
+               return TRUE;
+       }
+       if (devc->channel_entry->next) {
+               /* We got the frame for this channel, now get the next channel. */
+               devc->channel_entry = devc->channel_entry->next;
+               siglent_sds_channel_start(sdi);
+       } else {
+               /* Done with this frame. */
+               packet.type = SR_DF_FRAME_END;
+               sr_session_send(sdi, &packet);
+
+               if (++devc->num_frames == devc->limit_frames) {
+                       /* Last frame, stop capture. */
+                       sdi->driver->dev_acquisition_stop(sdi);
+               } else {
+                       /* Get the next frame, starting with the first channel. */
+                       devc->channel_entry = devc->enabled_channels;
+                       siglent_sds_capture_start(sdi);
+
+                       /* Start of next frame. */
+                       packet.type = SR_DF_FRAME_BEGIN;
+                       sr_session_send(sdi, &packet);
+               }
+       }
        return TRUE;
 }
+
+SR_PRIV int siglent_sds_get_dev_cfg(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_channel *ch;
+       char *cmd;
+       unsigned int i;
+       int res;
+       char *response;
+       gchar **tokens;
+       int num_tokens;
+
+       devc = sdi->priv;
+
+       /* Analog channel state. */
+       for (i = 0; i < devc->model->analog_channels; i++) {
+               cmd = g_strdup_printf("C%i:TRA?", i + 1);
+               res = sr_scpi_get_bool(sdi->conn, cmd, &devc->analog_channels[i]);
+               g_free(cmd);
+               if (res != SR_OK)
+                       return SR_ERR;
+               ch = g_slist_nth_data(sdi->channels, i);
+               ch->enabled = devc->analog_channels[i];
+       }
+       sr_dbg("Current analog channel state:");
+       for (i = 0; i < devc->model->analog_channels; i++)
+               sr_dbg("CH%d %s", i + 1, devc->analog_channels[i] ? "On" : "Off");
+
+       /* Digital channel state. */
+       if (devc->model->has_digital) {
+               gboolean status;
+
+               sr_dbg("Check Logic Analyzer channel state");
+               devc->la_enabled = FALSE;
+               cmd = g_strdup_printf("DGST?");
+               res = sr_scpi_get_bool(sdi->conn, cmd, &status);
+               g_free(cmd);
+               if (res != SR_OK)
+                       return SR_ERR;
+               sr_dbg("Logic Analyzer status: %s", status ? "On" : "Off");
+               if (status) {
+                       devc->la_enabled = TRUE;
+                       for (i = 0; i < ARRAY_SIZE(devc->digital_channels); i++) {
+                               cmd = g_strdup_printf("D%i:DGCH?", i);
+                               res = sr_scpi_get_bool(sdi->conn, cmd, &devc->digital_channels[i]);
+                               g_free(cmd);
+                               if (res != SR_OK)
+                                       return SR_ERR;
+                               ch = g_slist_nth_data(sdi->channels, i + devc->model->analog_channels);
+                               ch->enabled = devc->digital_channels[i];
+                               sr_dbg("D%d: %s", i, devc->digital_channels[i] ? "On" : "Off");
+                       }
+               } else {
+                       for (i = 0; i < ARRAY_SIZE(devc->digital_channels); i++) {
+                               ch = g_slist_nth_data(sdi->channels, i + devc->model->analog_channels);
+                               devc->digital_channels[i] = FALSE;
+                               ch->enabled = devc->digital_channels[i];
+                               sr_dbg("D%d: %s", i, devc->digital_channels[i] ? "On" : "Off");
+                       }
+               }
+       }
+
+       /* Timebase. */
+       if (sr_scpi_get_float(sdi->conn, ":TDIV?", &devc->timebase) != SR_OK)
+               return SR_ERR;
+       sr_dbg("Current timebase %g", devc->timebase);
+
+       /* Probe attenuation. */
+       for (i = 0; i < devc->model->analog_channels; i++) {
+               cmd = g_strdup_printf("C%d:ATTN?", i + 1);
+               res = sr_scpi_get_float(sdi->conn, cmd, &devc->attenuation[i]);
+               g_free(cmd);
+               if (res != SR_OK)
+                       return SR_ERR;
+       }
+       sr_dbg("Current probe attenuation:");
+       for (i = 0; i < devc->model->analog_channels; i++)
+               sr_dbg("CH%d %g", i + 1, devc->attenuation[i]);
+
+       /* Vertical gain and offset. */
+       if (siglent_sds_get_dev_cfg_vertical(sdi) != SR_OK)
+               return SR_ERR;
+
+       /* Coupling. */
+       for (i = 0; i < devc->model->analog_channels; i++) {
+               cmd = g_strdup_printf("C%d:CPL?", i + 1);
+               res = sr_scpi_get_string(sdi->conn, cmd, &devc->coupling[i]);
+               g_free(cmd);
+               if (res != SR_OK)
+                       return SR_ERR;
+       }
+
+       sr_dbg("Current coupling:");
+       for (i = 0; i < devc->model->analog_channels; i++)
+               sr_dbg("CH%d %s", i + 1, devc->coupling[i]);
+
+       /* Trigger source. */
+       response = NULL;
+       tokens = NULL;
+       if (sr_scpi_get_string(sdi->conn, "TRSE?", &response) != SR_OK)
+               return SR_ERR;
+       tokens = g_strsplit(response, ",", 0);
+       for (num_tokens = 0; tokens[num_tokens] != NULL; num_tokens++);
+       if (num_tokens < 4) {
+               sr_dbg("IDN response not according to spec: %80.s.", response);
+               g_strfreev(tokens);
+               g_free(response);
+               return SR_ERR_DATA;
+       }
+       g_free(response);
+       devc->trigger_source = g_strstrip(g_strdup(tokens[2]));
+       sr_dbg("Current trigger source %s", devc->trigger_source);
+
+       /* TODO Horizontal trigger position. */
+
+       devc->horiz_triggerpos = 0;
+       sr_dbg("Current horizontal trigger position %g", devc->horiz_triggerpos);
+
+       /* Trigger slope. */
+       cmd = g_strdup_printf("%s:TRSL?", devc->trigger_source);
+       res = sr_scpi_get_string(sdi->conn, cmd, &devc->trigger_slope);
+       g_free(cmd);
+       if (res != SR_OK)
+               return SR_ERR;
+       sr_dbg("Current trigger slope %s", devc->trigger_slope);
+
+       /* Trigger level. */
+       cmd = g_strdup_printf("%s:TRLV?", devc->trigger_source);
+       res = sr_scpi_get_float(sdi->conn, cmd, &devc->trigger_level);
+       g_free(cmd);
+       if (res != SR_OK)
+               return SR_ERR;
+       sr_dbg("Current trigger level %g", devc->trigger_level);
+       return SR_OK;
+}
+
+SR_PRIV int siglent_sds_get_dev_cfg_vertical(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       char *cmd;
+       unsigned int i;
+       int res;
+
+       devc = sdi->priv;
+
+       /* Vertical gain. */
+       for (i = 0; i < devc->model->analog_channels; i++) {
+               cmd = g_strdup_printf("C%d:VDIV?", i + 1);
+               res = sr_scpi_get_float(sdi->conn, cmd, &devc->vdiv[i]);
+               g_free(cmd);
+               if (res != SR_OK)
+                       return SR_ERR;
+       }
+       sr_dbg("Current vertical gain:");
+       for (i = 0; i < devc->model->analog_channels; i++)
+               sr_dbg("CH%d %g", i + 1, devc->vdiv[i]);
+
+       /* Vertical offset. */
+       for (i = 0; i < devc->model->analog_channels; i++) {
+               cmd = g_strdup_printf("C%d:OFST?", i + 1);
+               res = sr_scpi_get_float(sdi->conn, cmd, &devc->vert_offset[i]);
+               g_free(cmd);
+               if (res != SR_OK)
+                       return SR_ERR;
+       }
+       sr_dbg("Current vertical offset:");
+       for (i = 0; i < devc->model->analog_channels; i++)
+               sr_dbg("CH%d %g", i + 1, devc->vert_offset[i]);
+
+       return SR_OK;
+}
+
+SR_PRIV int siglent_sds_get_dev_cfg_horizontal(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       char *cmd;
+       int res;
+       char *samplePointsString;
+       float samplerateScope;
+       float fvalue;
+       char *first, *concat;
+
+       devc = sdi->priv;
+       cmd = g_strdup_printf("SANU? C1");
+       res = sr_scpi_get_string(sdi->conn, cmd, &samplePointsString);
+       g_free(cmd);
+       if (res != SR_OK)
+               return SR_ERR;
+       if (strcasestr(samplePointsString, "Mpts") != NULL) {
+               samplePointsString[strlen(samplePointsString) - 4] = '\0';
+
+               if (strcasestr(samplePointsString, ".") != NULL) {
+                       first = strtok(samplePointsString, ".");
+                       concat = strcat(first, strtok(NULL, "."));
+                       if (sr_atof_ascii(concat, &fvalue) != SR_OK || fvalue == 0.0) {
+                               sr_dbg("Invalid float converted from scope response.");
+                               return SR_ERR;
+                       }
+               } else {
+                       if (sr_atof_ascii(samplePointsString, &fvalue) != SR_OK || fvalue == 0.0) {
+                               sr_dbg("Invalid float converted from scope response.");
+                               return SR_ERR;
+                       }
+               }
+               samplerateScope = fvalue * 100000;
+       } else {
+               samplePointsString[strlen(samplePointsString) - 4] = '\0';
+               if (sr_atof_ascii(samplePointsString, &fvalue) != SR_OK || fvalue == 0.0) {
+                       sr_dbg("Invalid float converted from scope response.");
+                       return SR_ERR;
+               }
+               samplerateScope = fvalue * 1000;
+       }
+       /* Get the Timebase. */
+       if (sr_scpi_get_float(sdi->conn, ":TDIV?", &devc->timebase) != SR_OK)
+               return SR_ERR;
+       sr_dbg("Current timebase %g", devc->timebase);
+       devc->sampleRate = samplerateScope / (devc->timebase * devc->model->series->num_horizontal_divs);
+       return SR_OK;
+}
index bee751413f67510b6595a2f0c2458c5da630341f..198fa757c510778bb90e92a5e1659bdfb9bddeec 100644 (file)
@@ -20,6 +20,7 @@
 #ifndef LIBSIGROK_HARDWARE_SIGLENT_SDS_PROTOCOL_H
 #define LIBSIGROK_HARDWARE_SIGLENT_SDS_PROTOCOL_H
 
+#include <stdbool.h>
 #include <stdint.h>
 #include <glib.h>
 #include <libsigrok/libsigrok.h>
 
 #define LOG_PREFIX "siglent-sds"
 
+/* Size of acquisition buffers */
+//#define ACQ_BUFFER_SIZE (6000000)
+#define ACQ_BUFFER_SIZE (18000000)
+
+/* Maximum number of samples to retrieve at once. */
+#define ACQ_BLOCK_SIZE (30 * 1000)
+
+#define MAX_ANALOG_CHANNELS 4
+#define MAX_DIGITAL_CHANNELS 16
+
+#define DEVICE_STATE_STOPPED  0     /* Scope is in stopped state */
+#define DEVICE_STATE_DATA_ACQ 1     /* A new signal has been acquired */
+#define DEVICE_STATE_TRIG_RDY 8192  /* Trigger is ready */
+
+enum protocol_version {
+       SPO_MODEL,
+       NON_SPO_MODEL,
+};
+
+enum data_format {
+       FORMAT_IEEE488_2,
+};
+
+enum data_source {
+       DATA_SOURCE_SCREEN,
+       DATA_SOURCE_HISTORY,
+};
+
+struct siglent_sds_vendor {
+       const char *name;
+       const char *full_name;
+       const char *usb_name;
+};
+
+struct siglent_sds_series {
+       const struct siglent_sds_vendor *vendor;
+       const char *name;
+       enum protocol_version protocol;
+       uint64_t max_timebase[2];
+       uint64_t min_vdiv[2];
+       int num_horizontal_divs;
+       int num_vertical_divs;
+       int buffer_samples;
+};
+
+struct siglent_sds_model {
+       const struct siglent_sds_series *series;
+       const char *name;
+       uint64_t min_timebase[2];
+       unsigned int analog_channels;
+       bool has_digital;
+       unsigned int digital_channels;
+};
+
+enum wait_events {
+       WAIT_NONE,      /* Don't wait */
+       WAIT_TRIGGER,   /* Wait for trigger */
+       WAIT_BLOCK,     /* Wait for block data (only when reading sample mem) */
+       WAIT_STOP,      /* Wait for scope stopping (only single shots) */
+};
+
+/** Private, per-device-instance driver context. */
 struct dev_context {
+       /* Device model */
+       const struct siglent_sds_model *model;
+
+       /* Device properties */
+       const uint64_t (*timebases)[2];
+       uint64_t num_timebases;
+       const uint64_t (*vdivs)[2];
+       uint64_t num_vdivs;
+
+       /* Channel groups */
+       struct sr_channel_group **analog_groups;
+       struct sr_channel_group *digital_group;
+
+       /* Acquisition settings */
+       GSList *enabled_channels;
+       uint64_t limit_frames;
+       enum data_source data_source;
+       uint64_t analog_frame_size;
+       uint64_t digital_frame_size;
+       uint64_t num_samples;
+       long blockHeaderSize;
+       float sampleRate;
+
+       /* Device settings */
+       gboolean analog_channels[MAX_ANALOG_CHANNELS];
+       gboolean digital_channels[MAX_DIGITAL_CHANNELS];
+       gboolean la_enabled;
+       float timebase;
+       float attenuation[MAX_ANALOG_CHANNELS];
+       float vdiv[MAX_ANALOG_CHANNELS];
+       int vert_reference[MAX_ANALOG_CHANNELS];
+       float vert_offset[MAX_ANALOG_CHANNELS];
+       char *trigger_source;
+       float horiz_triggerpos;
+       char *trigger_slope;
+       float trigger_level;
+       char *coupling[MAX_ANALOG_CHANNELS];
+
+       /* Operational state */
+
+       /* Number of frames received in total. */
+       uint64_t num_frames;
+       /* GSList entry for the current channel. */
+       GSList *channel_entry;
+       /* Number of bytes received for current channel. */
+       uint64_t num_channel_bytes;
+       /* Number of bytes of block header read */
+       uint64_t num_header_bytes;
+       /* 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;
+       /* What to wait for in *_receive */
+       enum wait_events wait_event;
+       /* Trigger/block copying/stop waiting status */
+       int wait_status;
+       /* Acq buffers used for reading from the scope and sending data to app */
+       unsigned char *buffer;
+       float *data;
 };
 
-SR_PRIV int siglent_sds_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int siglent_sds_config_set(const struct sr_dev_inst *sdi,
+       const char *format, ...);
+SR_PRIV int siglent_sds_capture_start(const struct sr_dev_inst *sdi);
+SR_PRIV int siglent_sds_channel_start(const struct sr_dev_inst *sdi);
+SR_PRIV int siglent_sds_receive(int fd, int revents, void *cb_data);
+SR_PRIV int siglent_sds_get_dev_cfg(const struct sr_dev_inst *sdi);
+SR_PRIV int siglent_sds_get_dev_cfg_vertical(const struct sr_dev_inst *sdi);
+SR_PRIV int siglent_sds_get_dev_cfg_horizontal(const struct sr_dev_inst *sdi);
 
 #endif