]> sigrok.org Git - libsigrok.git/blobdiff - src/hardware/hameg-hmo/protocol.c
drivers: Drop some unnecessary prefixes.
[libsigrok.git] / src / hardware / hameg-hmo / protocol.c
index 99a30acd75dd8ec21af14235be879732d87ea87b..437b5f37dfef445d2502379132a2e7590f3c9dc8 100644 (file)
 #include "scpi.h"
 #include "protocol.h"
 
+SR_PRIV void hmo_queue_logic_data(struct dev_context *devc,
+                                 size_t group, GByteArray *pod_data);
+SR_PRIV void hmo_send_logic_packet(struct sr_dev_inst *sdi,
+                                  struct dev_context *devc);
+SR_PRIV void hmo_cleanup_logic_data(struct dev_context *devc);
+
 static const char *hameg_scpi_dialect[] = {
        [SCPI_CMD_GET_DIG_DATA]             = ":FORM UINT,8;:POD%d:DATA?",
        [SCPI_CMD_GET_TIMEBASE]             = ":TIM:SCAL?",
@@ -31,7 +37,8 @@ static const char *hameg_scpi_dialect[] = {
        [SCPI_CMD_SET_COUPLING]             = ":CHAN%d:COUP %s",
        [SCPI_CMD_GET_SAMPLE_RATE]          = ":ACQ:SRAT?",
        [SCPI_CMD_GET_SAMPLE_RATE_LIVE]     = ":%s:DATA:POINTS?",
-       [SCPI_CMD_GET_ANALOG_DATA]          = ":FORM REAL,32;:CHAN%d:DATA?",
+       [SCPI_CMD_GET_ANALOG_DATA]          = ":FORM:BORD %s;" \
+                                             ":FORM REAL,32;:CHAN%d:DATA?",
        [SCPI_CMD_GET_VERTICAL_DIV]         = ":CHAN%d:SCAL?",
        [SCPI_CMD_SET_VERTICAL_DIV]         = ":CHAN%d:SCAL %s",
        [SCPI_CMD_GET_DIG_POD_STATE]        = ":POD%d:STAT?",
@@ -48,27 +55,26 @@ static const char *hameg_scpi_dialect[] = {
        [SCPI_CMD_GET_ANALOG_CHAN_STATE]    = ":CHAN%d:STAT?",
        [SCPI_CMD_SET_ANALOG_CHAN_STATE]    = ":CHAN%d:STAT %d",
        [SCPI_CMD_GET_PROBE_UNIT]           = ":PROB%d:SET:ATT:UNIT?",
-       [SCPI_CMD_GET_BYTE_ORDER]           = ":FORM:BORD?",
 };
 
-static const uint32_t hmo_devopts[] = {
+static const uint32_t devopts[] = {
        SR_CONF_OSCILLOSCOPE,
        SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET,
-       SR_CONF_TRIGGER_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_SAMPLERATE | SR_CONF_GET,
        SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_NUM_HDIV | SR_CONF_GET,
-       SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_HORIZ_TRIGGERPOS | SR_CONF_GET | SR_CONF_SET,
-       SR_CONF_SAMPLERATE | SR_CONF_GET,
+       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,
 };
 
-static const uint32_t hmo_analog_devopts[] = {
+static const uint32_t analog_devopts[] = {
        SR_CONF_NUM_VDIV | SR_CONF_GET,
-       SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
        SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
 };
 
-static const char *hmo_coupling_options[] = {
+static const char *coupling_options[] = {
        "AC",  // AC with 50 Ohm termination (152x, 202x, 30xx, 1202)
        "ACL", // AC with 1 MOhm termination
        "DC",  // DC with 50 Ohm termination
@@ -84,7 +90,7 @@ static const char *scope_trigger_slopes[] = {
        NULL,
 };
 
-static const char *hmo_compact2_trigger_sources[] = {
+static const char *compact2_trigger_sources[] = {
        "CH1",
        "CH2",
        "LINE",
@@ -103,7 +109,7 @@ static const char *hmo_compact2_trigger_sources[] = {
        NULL,
 };
 
-static const char *hmo_compact4_trigger_sources[] = {
+static const char *compact4_trigger_sources[] = {
        "CH1",
        "CH2",
        "CH3",
@@ -124,7 +130,7 @@ static const char *hmo_compact4_trigger_sources[] = {
        NULL,
 };
 
-static const char *hmo_compact4_dig16_trigger_sources[] = {
+static const char *compact4_dig16_trigger_sources[] = {
        "CH1",
        "CH2",
        "CH3",
@@ -153,7 +159,7 @@ static const char *hmo_compact4_dig16_trigger_sources[] = {
        NULL,
 };
 
-static const uint64_t hmo_timebases[][2] = {
+static const uint64_t timebases[][2] = {
        /* nanoseconds */
        { 2, 1000000000 },
        { 5, 1000000000 },
@@ -192,7 +198,7 @@ static const uint64_t hmo_timebases[][2] = {
        { 50, 1 },
 };
 
-static const uint64_t hmo_vdivs[][2] = {
+static const uint64_t vdivs[][2] = {
        /* millivolts */
        { 1, 1000 },
        { 2, 1000 },
@@ -250,21 +256,21 @@ static const struct scope_config scope_models[] = {
                .analog_names = &scope_analog_channel_names,
                .digital_names = &scope_digital_channel_names,
 
-               .devopts = &hmo_devopts,
-               .num_devopts = ARRAY_SIZE(hmo_devopts),
+               .devopts = &devopts,
+               .num_devopts = ARRAY_SIZE(devopts),
 
-               .analog_devopts = &hmo_analog_devopts,
-               .num_analog_devopts = ARRAY_SIZE(hmo_analog_devopts),
+               .analog_devopts = &analog_devopts,
+               .num_analog_devopts = ARRAY_SIZE(analog_devopts),
 
-               .coupling_options = &hmo_coupling_options,
-               .trigger_sources = &hmo_compact2_trigger_sources,
+               .coupling_options = &coupling_options,
+               .trigger_sources = &compact2_trigger_sources,
                .trigger_slopes = &scope_trigger_slopes,
 
-               .timebases = &hmo_timebases,
-               .num_timebases = ARRAY_SIZE(hmo_timebases),
+               .timebases = &timebases,
+               .num_timebases = ARRAY_SIZE(timebases),
 
-               .vdivs = &hmo_vdivs,
-               .num_vdivs = ARRAY_SIZE(hmo_vdivs),
+               .vdivs = &vdivs,
+               .num_vdivs = ARRAY_SIZE(vdivs),
 
                .num_xdivs = 12,
                .num_ydivs = 8,
@@ -280,21 +286,21 @@ static const struct scope_config scope_models[] = {
                .analog_names = &scope_analog_channel_names,
                .digital_names = &scope_digital_channel_names,
 
-               .devopts = &hmo_devopts,
-               .num_devopts = ARRAY_SIZE(hmo_devopts),
+               .devopts = &devopts,
+               .num_devopts = ARRAY_SIZE(devopts),
 
-               .analog_devopts = &hmo_analog_devopts,
-               .num_analog_devopts = ARRAY_SIZE(hmo_analog_devopts),
+               .analog_devopts = &analog_devopts,
+               .num_analog_devopts = ARRAY_SIZE(analog_devopts),
 
-               .coupling_options = &hmo_coupling_options,
-               .trigger_sources = &hmo_compact4_trigger_sources,
+               .coupling_options = &coupling_options,
+               .trigger_sources = &compact4_trigger_sources,
                .trigger_slopes = &scope_trigger_slopes,
 
-               .timebases = &hmo_timebases,
-               .num_timebases = ARRAY_SIZE(hmo_timebases),
+               .timebases = &timebases,
+               .num_timebases = ARRAY_SIZE(timebases),
 
-               .vdivs = &hmo_vdivs,
-               .num_vdivs = ARRAY_SIZE(hmo_vdivs),
+               .vdivs = &vdivs,
+               .num_vdivs = ARRAY_SIZE(vdivs),
 
                .num_xdivs = 12,
                .num_ydivs = 8,
@@ -302,7 +308,7 @@ static const struct scope_config scope_models[] = {
                .scpi_dialect = &hameg_scpi_dialect,
        },
        {
-               .name = {"HMO2524", "HMO3034", "HMO3044", "HMO3054", NULL},
+               .name = {"HMO2524", "HMO3034", "HMO3044", "HMO3054", "HMO3524", NULL},
                .analog_channels = 4,
                .digital_channels = 16,
                .digital_pods = 2,
@@ -310,21 +316,21 @@ static const struct scope_config scope_models[] = {
                .analog_names = &scope_analog_channel_names,
                .digital_names = &scope_digital_channel_names,
 
-               .devopts = &hmo_devopts,
-               .num_devopts = ARRAY_SIZE(hmo_devopts),
+               .devopts = &devopts,
+               .num_devopts = ARRAY_SIZE(devopts),
 
-               .analog_devopts = &hmo_analog_devopts,
-               .num_analog_devopts = ARRAY_SIZE(hmo_analog_devopts),
+               .analog_devopts = &analog_devopts,
+               .num_analog_devopts = ARRAY_SIZE(analog_devopts),
 
-               .coupling_options = &hmo_coupling_options,
-               .trigger_sources = &hmo_compact4_dig16_trigger_sources,
+               .coupling_options = &coupling_options,
+               .trigger_sources = &compact4_dig16_trigger_sources,
                .trigger_slopes = &scope_trigger_slopes,
 
-               .timebases = &hmo_timebases,
-               .num_timebases = ARRAY_SIZE(hmo_timebases),
+               .timebases = &timebases,
+               .num_timebases = ARRAY_SIZE(timebases),
 
-               .vdivs = &hmo_vdivs,
-               .num_vdivs = ARRAY_SIZE(hmo_vdivs),
+               .vdivs = &vdivs,
+               .num_vdivs = ARRAY_SIZE(vdivs),
 
                .num_xdivs = 12,
                .num_ydivs = 8,
@@ -358,7 +364,7 @@ static void scope_state_dump(const struct scope_config *config,
                        state->digital_pods[i] ? "On" : "Off");
        }
 
-       tmp = sr_period_string((*config->timebases)[state->timebase][0] *
+       tmp = sr_period_string((*config->timebases)[state->timebase][0],
                               (*config->timebases)[state->timebase][1]);
        sr_info("Current timebase: %s", tmp);
        g_free(tmp);
@@ -456,8 +462,7 @@ static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi,
                if (sr_scpi_get_string(scpi, command, &tmp_str) != SR_OK)
                        return SR_ERR;
 
-               if (array_float_get(tmp_str, hmo_vdivs, ARRAY_SIZE(hmo_vdivs),
-                               &j) != SR_OK) {
+               if (array_float_get(tmp_str, vdivs, ARRAY_SIZE(vdivs), &j) != SR_OK) {
                        g_free(tmp_str);
                        sr_err("Could not determine array index for vertical div scale.");
                        return SR_ERR;
@@ -496,22 +501,6 @@ static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi,
                g_free(tmp_str);
        }
 
-       /*
-        * Determine the byte order which will be used for data blocks.
-        * A ":FORM:BORD?" request will yield either an "MSBF" or "LSBF"
-        * response.
-        */
-       state->byteorder = '?';
-       if (sr_scpi_get_string(scpi,
-                              (*config->scpi_dialect)[SCPI_CMD_GET_BYTE_ORDER],
-                              &tmp_str) != SR_OK)
-               return SR_ERR;
-       if (tmp_str[0] == 'M')
-               state->byteorder = 'b';
-       else if (tmp_str[0] == 'L')
-               state->byteorder = 'l';
-       g_free(tmp_str);
-
        return SR_OK;
 }
 
@@ -638,8 +627,7 @@ SR_PRIV int hmo_scope_state_get(struct sr_dev_inst *sdi)
                        &tmp_str) != SR_OK)
                return SR_ERR;
 
-       if (array_float_get(tmp_str, hmo_timebases, ARRAY_SIZE(hmo_timebases),
-                       &i) != SR_OK) {
+       if (array_float_get(tmp_str, timebases, ARRAY_SIZE(timebases), &i) != SR_OK) {
                g_free(tmp_str);
                sr_err("Could not determine array index for time base.");
                return SR_ERR;
@@ -780,6 +768,89 @@ SR_PRIV int hmo_init_device(struct sr_dev_inst *sdi)
        return SR_OK;
 }
 
+/* Queue data of one channel group, for later submission. */
+SR_PRIV void hmo_queue_logic_data(struct dev_context *devc,
+                                 size_t group, GByteArray *pod_data)
+{
+       size_t size;
+       GByteArray *store;
+       uint8_t *logic_data;
+       size_t idx, logic_step;
+
+       /*
+        * Upon first invocation, allocate the array which can hold the
+        * combined logic data for all channels. Assume that each channel
+        * will yield an identical number of samples per receive call.
+        *
+        * As a poor man's safety measure: (Silently) skip processing
+        * for unexpected sample counts, and ignore samples for
+        * unexpected channel groups. Don't bother with complicated
+        * resize logic, considering that many models only support one
+        * pod, and the most capable supported models have two pods of
+        * identical size. We haven't yet seen any "odd" configuration.
+        */
+       if (!devc->logic_data) {
+               size = pod_data->len * devc->pod_count;
+               store = g_byte_array_sized_new(size);
+               memset(store->data, 0, size);
+               store = g_byte_array_set_size(store, size);
+               devc->logic_data = store;
+       } else {
+               store = devc->logic_data;
+               size = store->len / devc->pod_count;
+               if (size != pod_data->len)
+                       return;
+               if (group >= devc->pod_count)
+                       return;
+       }
+
+       /*
+        * Fold the data of the most recently received channel group into
+        * the storage, where data resides for all channels combined.
+        */
+       logic_data = store->data;
+       logic_data += group;
+       logic_step = devc->pod_count;
+       for (idx = 0; idx < pod_data->len; idx++) {
+               *logic_data = pod_data->data[idx];
+               logic_data += logic_step;
+       }
+}
+
+/* Submit data for all channels, after the individual groups got collected. */
+SR_PRIV void hmo_send_logic_packet(struct sr_dev_inst *sdi,
+                                  struct dev_context *devc)
+{
+       struct sr_datafeed_packet packet;
+       struct sr_datafeed_logic logic;
+
+       if (!devc->logic_data)
+               return;
+
+       logic.data = devc->logic_data->data;
+       logic.length = devc->logic_data->len;
+       logic.unitsize = devc->pod_count;
+
+       packet.type = SR_DF_LOGIC;
+       packet.payload = &logic;
+
+       sr_session_send(sdi, &packet);
+}
+
+/* Undo previous resource allocation. */
+SR_PRIV void hmo_cleanup_logic_data(struct dev_context *devc)
+{
+
+       if (devc->logic_data) {
+               g_byte_array_free(devc->logic_data, TRUE);
+               devc->logic_data = NULL;
+       }
+       /*
+        * Keep 'pod_count'! It's required when more frames will be
+        * received, and does not harm when kept after acquisition.
+        */
+}
+
 SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data)
 {
        struct sr_channel *ch;
@@ -793,6 +864,7 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data)
        struct sr_analog_meaning meaning;
        struct sr_analog_spec spec;
        struct sr_datafeed_logic logic;
+       size_t group;
 
        (void)fd;
        (void)revents;
@@ -815,6 +887,18 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data)
        ch = devc->current_channel->data;
        state = devc->model_state;
 
+       /*
+        * Send "frame begin" packet upon reception of data for the
+        * first enabled channel.
+        */
+       if (devc->current_channel == devc->enabled_channels) {
+               packet.type = SR_DF_FRAME_BEGIN;
+               sr_session_send(sdi, &packet);
+       }
+
+       /*
+        * Pass on the received data of the channel(s).
+        */
        switch (ch->type) {
        case SR_CHANNEL_ANALOG:
                if (sr_scpi_get_block(sdi->conn, NULL, &data) != SR_OK) {
@@ -824,9 +908,6 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data)
                        return TRUE;
                }
 
-               packet.type = SR_DF_FRAME_BEGIN;
-               sr_session_send(sdi, &packet);
-
                packet.type = SR_DF_ANALOG;
 
                analog.data = data->data;
@@ -838,8 +919,11 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data)
                encoding.unitsize = sizeof(float);
                encoding.is_signed = TRUE;
                encoding.is_float = TRUE;
-               /* Assume LE format when unknown for backwards compat. */
-               encoding.is_bigendian = (state->byteorder == 'b') ? TRUE : FALSE;
+#ifdef WORDS_BIGENDIAN
+               encoding.is_bigendian = TRUE;
+#else
+               encoding.is_bigendian = FALSE;
+#endif
                /* TODO: Use proper 'digits' value for this device (and its modes). */
                encoding.digits = 2;
                encoding.is_digits_decimal = FALSE;
@@ -870,15 +954,31 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data)
                        return TRUE;
                }
 
-               packet.type = SR_DF_FRAME_BEGIN;
-               sr_session_send(sdi, &packet);
+               /*
+                * If only data from the first pod is involved in the
+                * acquisition, then the raw input bytes can get passed
+                * forward for performance reasons. When the second pod
+                * is involved (either alone, or in combination with the
+                * first pod), then the received bytes need to be put
+                * into memory in such a layout that all channel groups
+                * get combined, and a unitsize larger than a single byte
+                * applies. The "queue" logic transparently copes with
+                * any such configuration. This works around the lack
+                * of support for "meaning" to logic data, which is used
+                * above for analog data.
+                */
+               if (devc->pod_count == 1) {
+                       packet.type = SR_DF_LOGIC;
+                       logic.data = data->data;
+                       logic.length = data->len;
+                       logic.unitsize = 1;
+                       packet.payload = &logic;
+                       sr_session_send(sdi, &packet);
+               } else {
+                       group = ch->index / 8;
+                       hmo_queue_logic_data(devc, group, data);
+               }
 
-               logic.length = data->len;
-               logic.unitsize = 1;
-               logic.data = data->data;
-               packet.type = SR_DF_LOGIC;
-               packet.payload = &logic;
-               sr_session_send(sdi, &packet);
                g_byte_array_free(data, TRUE);
                data = NULL;
                break;
@@ -887,14 +987,37 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data)
                break;
        }
 
-       packet.type = SR_DF_FRAME_END;
-       sr_session_send(sdi, &packet);
-
+       /*
+        * Advance to the next enabled channel. When data for all enabled
+        * channels was received, then flush potentially queued logic data,
+        * and send the "frame end" packet.
+        */
        if (devc->current_channel->next) {
                devc->current_channel = devc->current_channel->next;
                hmo_request_data(sdi);
-       } else if (++devc->num_frames == devc->frame_limit) {
-               sdi->driver->dev_acquisition_stop(sdi);
+               return TRUE;
+       }
+       hmo_send_logic_packet(sdi, devc);
+
+       /*
+        * Release the logic data storage after each frame. This copes
+        * with sample counts that differ in length per frame. -- Is
+        * this a real constraint when acquiring multiple frames with
+        * identical device settings?
+        */
+       hmo_cleanup_logic_data(devc);
+
+       packet.type = SR_DF_FRAME_END;
+       sr_session_send(sdi, &packet);
+
+       /*
+        * End of frame was reached. Stop acquisition after the specified
+        * number of frames, or continue reception by starting over at
+        * the first enabled channel.
+        */
+       if (++devc->num_frames == devc->frame_limit) {
+               sr_dev_acquisition_stop(sdi);
+               hmo_cleanup_logic_data(devc);
        } else {
                devc->current_channel = devc->enabled_channels;
                hmo_request_data(sdi);