X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fhardware%2Fhameg-hmo%2Fprotocol.c;h=f534cb4705c07042dfbeb5da562870a950c5f990;hb=14cb6aa40a26e12f24a28c66e6a8d6ba636c6281;hp=8c80204fca024952bf9d7166a78da44d5a5f6dc3;hpb=f254bc4bba68d2cade0c8f7993d8fa8d3d9b556a;p=libsigrok.git diff --git a/src/hardware/hameg-hmo/protocol.c b/src/hardware/hameg-hmo/protocol.c index 8c80204f..f534cb47 100644 --- a/src/hardware/hameg-hmo/protocol.c +++ b/src/hardware/hameg-hmo/protocol.c @@ -17,17 +17,28 @@ * along with this program. If not, see . */ +#include +#include +#include +#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] = ":POD%d:DATA?", + [SCPI_CMD_GET_DIG_DATA] = ":FORM UINT,8;:POD%d:DATA?", [SCPI_CMD_GET_TIMEBASE] = ":TIM:SCAL?", [SCPI_CMD_SET_TIMEBASE] = ":TIM:SCAL %s", [SCPI_CMD_GET_COUPLING] = ":CHAN%d:COUP?", [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] = ":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?", @@ -43,75 +54,60 @@ static const char *hameg_scpi_dialect[] = { [SCPI_CMD_SET_HORIZ_TRIGGERPOS] = ":TIM:POS %s", [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?", }; -static const uint32_t hmo_devopts[] = { +static const uint32_t devopts[] = { SR_CONF_OSCILLOSCOPE, - SR_CONF_TRIGGER_SOURCE, - SR_CONF_TIMEBASE, - SR_CONF_NUM_TIMEBASE, - SR_CONF_TRIGGER_SLOPE, - SR_CONF_HORIZ_TRIGGERPOS, - SR_CONF_SAMPLERATE, - SR_CONF_LIMIT_FRAMES, + SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET, + 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_HORIZ_TRIGGERPOS | SR_CONF_GET | SR_CONF_SET, + 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[] = { - SR_CONF_NUM_VDIV, - SR_CONF_COUPLING, - SR_CONF_VDIV, +static const uint32_t devopts_cg_analog[] = { + SR_CONF_NUM_VDIV | SR_CONF_GET, + 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[] = { - "AC", - "ACL", - "DC", - "DCL", +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 + "DCL", // DC with 1 MOhm termination "GND", - NULL, }; static const char *scope_trigger_slopes[] = { "POS", "NEG", - NULL, + "EITH", +}; + +static const char *compact2_trigger_sources[] = { + "CH1", "CH2", + "LINE", "EXT", "PATT", "BUS1", "BUS2", + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", }; -static const char *hmo_compact2_trigger_sources[] = { - "CH1", - "CH2", - "LINE", - "EXT", - "D0", - "D1", - "D2", - "D3", - "D4", - "D5", - "D6", - "D7", - NULL, +static const char *compact4_trigger_sources[] = { + "CH1", "CH2", "CH3", "CH4", + "LINE", "EXT", "PATT", "BUS1", "BUS2", + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", }; -static const char *hmo_compact4_trigger_sources[] = { - "CH1", - "CH2", - "CH3", - "CH4", - "LINE", - "EXT", - "D0", - "D1", - "D2", - "D3", - "D4", - "D5", - "D6", - "D7", - NULL, +static const char *compact4_dig16_trigger_sources[] = { + "CH1", "CH2", "CH3", "CH4", + "LINE", "EXT", "PATT", "BUS1", "BUS2", + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", + "D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", }; -static const uint64_t hmo_timebases[][2] = { +static const uint64_t timebases[][2] = { /* nanoseconds */ { 2, 1000000000 }, { 5, 1000000000 }, @@ -150,7 +146,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 }, @@ -166,37 +162,24 @@ static const uint64_t hmo_vdivs[][2] = { { 2, 1 }, { 5, 1 }, { 10, 1 }, + { 20, 1 }, + { 50, 1 }, }; static const char *scope_analog_channel_names[] = { - "CH1", - "CH2", - "CH3", - "CH4", + "CH1", "CH2", "CH3", "CH4", }; static const char *scope_digital_channel_names[] = { - "D0", - "D1", - "D2", - "D3", - "D4", - "D5", - "D6", - "D7", - "D8", - "D9", - "D10", - "D11", - "D12", - "D13", - "D14", - "D15", + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", + "D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", }; -static struct scope_config scope_models[] = { +static const struct scope_config scope_models[] = { { - .name = {"HMO722", "HMO1022", "HMO1522", "HMO2022", NULL}, + /* HMO2522/3032/3042/3052 support 16 digital channels but they're not supported yet. */ + .name = {"HMO1002", "HMO722", "HMO1022", "HMO1522", "HMO2022", "HMO2522", + "HMO3032", "HMO3042", "HMO3052", NULL}, .analog_channels = 2, .digital_channels = 8, .digital_pods = 1, @@ -204,21 +187,26 @@ static 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), + .devopts_cg_analog = &devopts_cg_analog, + .num_devopts_cg_analog = ARRAY_SIZE(devopts_cg_analog), + + .coupling_options = &coupling_options, + .num_coupling_options = ARRAY_SIZE(coupling_options), + + .trigger_sources = &compact2_trigger_sources, + .num_trigger_sources = ARRAY_SIZE(compact2_trigger_sources), - .coupling_options = &hmo_coupling_options, - .trigger_sources = &hmo_compact2_trigger_sources, .trigger_slopes = &scope_trigger_slopes, + .num_trigger_slopes = ARRAY_SIZE(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, @@ -234,21 +222,61 @@ static 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), + + .devopts_cg_analog = &devopts_cg_analog, + .num_devopts_cg_analog = ARRAY_SIZE(devopts_cg_analog), - .analog_devopts = &hmo_analog_devopts, - .num_analog_devopts = ARRAY_SIZE(hmo_analog_devopts), + .coupling_options = &coupling_options, + .num_coupling_options = ARRAY_SIZE(coupling_options), + + .trigger_sources = &compact4_trigger_sources, + .num_trigger_sources = ARRAY_SIZE(compact4_trigger_sources), - .coupling_options = &hmo_coupling_options, - .trigger_sources = &hmo_compact4_trigger_sources, .trigger_slopes = &scope_trigger_slopes, + .num_trigger_slopes = ARRAY_SIZE(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, + + .scpi_dialect = &hameg_scpi_dialect, + }, + { + .name = {"HMO2524", "HMO3034", "HMO3044", "HMO3054", "HMO3524", NULL}, + .analog_channels = 4, + .digital_channels = 16, + .digital_pods = 2, + + .analog_names = &scope_analog_channel_names, + .digital_names = &scope_digital_channel_names, + + .devopts = &devopts, + .num_devopts = ARRAY_SIZE(devopts), + + .devopts_cg_analog = &devopts_cg_analog, + .num_devopts_cg_analog = ARRAY_SIZE(devopts_cg_analog), + + .coupling_options = &coupling_options, + .num_coupling_options = ARRAY_SIZE(coupling_options), + + .trigger_sources = &compact4_dig16_trigger_sources, + .num_trigger_sources = ARRAY_SIZE(compact4_dig16_trigger_sources), + + .trigger_slopes = &scope_trigger_slopes, + .num_trigger_slopes = ARRAY_SIZE(scope_trigger_slopes), + + .timebases = &timebases, + .num_timebases = ARRAY_SIZE(timebases), + + .vdivs = &vdivs, + .num_vdivs = ARRAY_SIZE(vdivs), .num_xdivs = 12, .num_ydivs = 8, @@ -257,32 +285,32 @@ static struct scope_config scope_models[] = { }, }; -static void scope_state_dump(struct scope_config *config, +static void scope_state_dump(const struct scope_config *config, struct scope_state *state) { unsigned int i; char *tmp; - for (i = 0; i < config->analog_channels; ++i) { + for (i = 0; i < config->analog_channels; i++) { tmp = sr_voltage_string((*config->vdivs)[state->analog_channels[i].vdiv][0], (*config->vdivs)[state->analog_channels[i].vdiv][1]); - sr_info("State of analog channel %d -> %s : %s (coupling) %s (vdiv) %2.2e (offset)", + sr_info("State of analog channel %d -> %s : %s (coupling) %s (vdiv) %2.2e (offset)", i + 1, state->analog_channels[i].state ? "On" : "Off", (*config->coupling_options)[state->analog_channels[i].coupling], tmp, state->analog_channels[i].vertical_offset); } - for (i = 0; i < config->digital_channels; ++i) { + for (i = 0; i < config->digital_channels; i++) { sr_info("State of digital channel %d -> %s", i, state->digital_channels[i] ? "On" : "Off"); } - for (i = 0; i < config->digital_pods; ++i) { + for (i = 0; i < config->digital_pods; i++) { sr_info("State of digital POD %d -> %s", i, 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); @@ -298,42 +326,84 @@ static void scope_state_dump(struct scope_config *config, } static int scope_state_get_array_option(struct sr_scpi_dev_inst *scpi, - const char *command, const char *(*array)[], int *result) + const char *command, const char *(*array)[], unsigned int n, int *result) { char *tmp; - unsigned int i; + int idx; if (sr_scpi_get_string(scpi, command, &tmp) != SR_OK) { g_free(tmp); return SR_ERR; } - for (i = 0; (*array)[i]; ++i) { - if (!g_strcmp0(tmp, (*array)[i])) { + if ((idx = std_str_idx_s(tmp, *array, n)) < 0) { + g_free(tmp); + return SR_ERR_ARG; + } + + *result = idx; + + g_free(tmp); + + return SR_OK; +} + +/** + * This function takes a value of the form "2.000E-03" and returns the index + * of an array where a matching pair was found. + * + * @param value The string to be parsed. + * @param array The array of s/f pairs. + * @param array_len The number of pairs in the array. + * @param result The index at which a matching pair was found. + * + * @return SR_ERR on any parsing error, SR_OK otherwise. + */ +static int array_float_get(gchar *value, const uint64_t array[][2], + int array_len, unsigned int *result) +{ + struct sr_rational rval; + struct sr_rational aval; + + if (sr_parse_rational(value, &rval) != SR_OK) + return SR_ERR; + + for (int i = 0; i < array_len; i++) { + sr_rational_set(&aval, array[i][0], array[i][1]); + if (sr_rational_eq(&rval, &aval)) { *result = i; - g_free(tmp); - tmp = NULL; - break; + return SR_OK; } } - if (tmp) { - g_free(tmp); - return SR_ERR; + return SR_ERR; +} + +static struct sr_channel *get_channel_by_index_and_type(GSList *channel_lhead, + int index, int type) +{ + while (channel_lhead) { + struct sr_channel *ch = channel_lhead->data; + if (ch->index == index && ch->type == type) + return ch; + + channel_lhead = channel_lhead->next; } - return SR_OK; + return 0; } -static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi, - struct scope_config *config, +static int analog_channel_state_get(struct sr_dev_inst *sdi, + const struct scope_config *config, struct scope_state *state) { unsigned int i, j; - float tmp_float; char command[MAX_COMMAND_SIZE]; + char *tmp_str; + struct sr_channel *ch; + struct sr_scpi_dev_inst *scpi = sdi->conn; - for (i = 0; i < config->analog_channels; ++i) { + for (i = 0; i < config->analog_channels; i++) { g_snprintf(command, sizeof(command), (*config->scpi_dialect)[SCPI_CMD_GET_ANALOG_CHAN_STATE], i + 1); @@ -342,24 +412,26 @@ static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi, &state->analog_channels[i].state) != SR_OK) return SR_ERR; + ch = get_channel_by_index_and_type(sdi->channels, i, SR_CHANNEL_ANALOG); + if (ch) + ch->enabled = state->analog_channels[i].state; + g_snprintf(command, sizeof(command), (*config->scpi_dialect)[SCPI_CMD_GET_VERTICAL_DIV], i + 1); - if (sr_scpi_get_float(scpi, command, &tmp_float) != SR_OK) + if (sr_scpi_get_string(scpi, command, &tmp_str) != SR_OK) return SR_ERR; - for (j = 0; j < config->num_vdivs; j++) { - if (tmp_float == ((float) (*config->vdivs)[j][0] / - (*config->vdivs)[j][1])) { - state->analog_channels[i].vdiv = j; - break; - } - } - if (j == config->num_vdivs) { + + if (array_float_get(tmp_str, ARRAY_AND_SIZE(vdivs), &j) != SR_OK) { + g_free(tmp_str); sr_err("Could not determine array index for vertical div scale."); return SR_ERR; } + g_free(tmp_str); + state->analog_channels[i].vdiv = j; + g_snprintf(command, sizeof(command), (*config->scpi_dialect)[SCPI_CMD_GET_VERTICAL_OFFSET], i + 1); @@ -373,21 +445,37 @@ static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi, i + 1); if (scope_state_get_array_option(scpi, command, config->coupling_options, + config->num_coupling_options, &state->analog_channels[i].coupling) != SR_OK) return SR_ERR; + + g_snprintf(command, sizeof(command), + (*config->scpi_dialect)[SCPI_CMD_GET_PROBE_UNIT], + i + 1); + + if (sr_scpi_get_string(scpi, command, &tmp_str) != SR_OK) + return SR_ERR; + + if (tmp_str[0] == 'A') + state->analog_channels[i].probe_unit = 'A'; + else + state->analog_channels[i].probe_unit = 'V'; + g_free(tmp_str); } return SR_OK; } -static int digital_channel_state_get(struct sr_scpi_dev_inst *scpi, - struct scope_config *config, +static int digital_channel_state_get(struct sr_dev_inst *sdi, + const struct scope_config *config, struct scope_state *state) { unsigned int i; char command[MAX_COMMAND_SIZE]; + struct sr_channel *ch; + struct sr_scpi_dev_inst *scpi = sdi->conn; - for (i = 0; i < config->digital_channels; ++i) { + for (i = 0; i < config->digital_channels; i++) { g_snprintf(command, sizeof(command), (*config->scpi_dialect)[SCPI_CMD_GET_DIG_CHAN_STATE], i); @@ -395,9 +483,13 @@ static int digital_channel_state_get(struct sr_scpi_dev_inst *scpi, if (sr_scpi_get_bool(scpi, command, &state->digital_channels[i]) != SR_OK) return SR_ERR; + + ch = get_channel_by_index_and_type(sdi->channels, i, SR_CHANNEL_LOGIC); + if (ch) + ch->enabled = state->digital_channels[i]; } - for (i = 0; i < config->digital_pods; ++i) { + for (i = 0; i < config->digital_pods; i++) { g_snprintf(command, sizeof(command), (*config->scpi_dialect)[SCPI_CMD_GET_DIG_POD_STATE], i + 1); @@ -414,8 +506,7 @@ SR_PRIV int hmo_update_sample_rate(const struct sr_dev_inst *sdi) { struct dev_context *devc; struct scope_state *state; - struct scope_config *config; - + const struct scope_config *config; int tmp; unsigned int i; float tmp_float; @@ -428,9 +519,22 @@ SR_PRIV int hmo_update_sample_rate(const struct sr_dev_inst *sdi) state = devc->model_state; channel_found = FALSE; - for (i = 0; i < config->analog_channels; ++i) { - if (state->analog_channels[i].state) { - g_snprintf(chan_name, sizeof(chan_name), "CHAN%d", i + 1); + for (i = 0; i < config->analog_channels; i++) { + if (!state->analog_channels[i].state) + continue; + g_snprintf(chan_name, sizeof(chan_name), "CHAN%d", i + 1); + g_snprintf(tmp_str, sizeof(tmp_str), + (*config->scpi_dialect)[SCPI_CMD_GET_SAMPLE_RATE_LIVE], + chan_name); + channel_found = TRUE; + break; + } + + if (!channel_found) { + for (i = 0; i < config->digital_pods; i++) { + if (!state->digital_pods[i]) + continue; + g_snprintf(chan_name, sizeof(chan_name), "POD%d", i); g_snprintf(tmp_str, sizeof(tmp_str), (*config->scpi_dialect)[SCPI_CMD_GET_SAMPLE_RATE_LIVE], chan_name); @@ -439,19 +543,6 @@ SR_PRIV int hmo_update_sample_rate(const struct sr_dev_inst *sdi) } } - if (!channel_found) { - for (i = 0; i < config->digital_pods; i++) { - if (state->digital_pods[i]) { - g_snprintf(chan_name, sizeof(chan_name), "POD%d", i); - g_snprintf(tmp_str, sizeof(tmp_str), - (*config->scpi_dialect)[SCPI_CMD_GET_SAMPLE_RATE_LIVE], - chan_name); - channel_found = TRUE; - break; - } - } - } - /* No channel is active, ask the instrument for the sample rate * in single shot mode */ if (!channel_found) { @@ -476,9 +567,10 @@ SR_PRIV int hmo_scope_state_get(struct sr_dev_inst *sdi) { struct dev_context *devc; struct scope_state *state; - struct scope_config *config; + const struct scope_config *config; float tmp_float; unsigned int i; + char *tmp_str; devc = sdi->priv; config = devc->model_config; @@ -486,10 +578,10 @@ SR_PRIV int hmo_scope_state_get(struct sr_dev_inst *sdi) sr_info("Fetching scope state"); - if (analog_channel_state_get(sdi->conn, config, state) != SR_OK) + if (analog_channel_state_get(sdi, config, state) != SR_OK) return SR_ERR; - if (digital_channel_state_get(sdi->conn, config, state) != SR_OK) + if (digital_channel_state_get(sdi, config, state) != SR_OK) return SR_ERR; if (sr_scpi_get_float(sdi->conn, @@ -497,17 +589,19 @@ SR_PRIV int hmo_scope_state_get(struct sr_dev_inst *sdi) &tmp_float) != SR_OK) return SR_ERR; - for (i = 0; i < config->num_timebases; i++) { - if (tmp_float == ((float) (*config->timebases)[i][0] / - (*config->timebases)[i][1])) { - state->timebase = i; - break; - } - } - if (i == config->num_timebases) { + if (sr_scpi_get_string(sdi->conn, + (*config->scpi_dialect)[SCPI_CMD_GET_TIMEBASE], + &tmp_str) != SR_OK) + return SR_ERR; + + if (array_float_get(tmp_str, ARRAY_AND_SIZE(timebases), &i) != SR_OK) { + g_free(tmp_str); sr_err("Could not determine array index for time base."); return SR_ERR; } + g_free(tmp_str); + + state->timebase = i; if (sr_scpi_get_float(sdi->conn, (*config->scpi_dialect)[SCPI_CMD_GET_HORIZ_TRIGGERPOS], @@ -521,12 +615,14 @@ SR_PRIV int hmo_scope_state_get(struct sr_dev_inst *sdi) if (scope_state_get_array_option(sdi->conn, (*config->scpi_dialect)[SCPI_CMD_GET_TRIGGER_SOURCE], - config->trigger_sources, &state->trigger_source) != SR_OK) + config->trigger_sources, config->num_trigger_sources, + &state->trigger_source) != SR_OK) return SR_ERR; if (scope_state_get_array_option(sdi->conn, - (*config->scpi_dialect)[SCPI_CMD_GET_TRIGGER_SLOPE], - config->trigger_slopes, &state->trigger_slope) != SR_OK) + (*config->scpi_dialect)[SCPI_CMD_GET_TRIGGER_SLOPE], + config->trigger_slopes, config->num_trigger_slopes, + &state->trigger_slope) != SR_OK) return SR_ERR; if (hmo_update_sample_rate(sdi) != SR_OK) @@ -539,37 +635,19 @@ SR_PRIV int hmo_scope_state_get(struct sr_dev_inst *sdi) return SR_OK; } -static struct scope_state *scope_state_new(struct scope_config *config) +static struct scope_state *scope_state_new(const struct scope_config *config) { struct scope_state *state; - if (!(state = g_try_malloc0(sizeof(struct scope_state)))) - return NULL; - - if (!(state->analog_channels = g_try_malloc0_n(config->analog_channels, - sizeof(struct analog_channel_state)))) - goto fail; - - if (!(state->digital_channels = g_try_malloc0_n( - config->digital_channels, sizeof(gboolean)))) - goto fail; - - if (!(state->digital_pods = g_try_malloc0_n(config->digital_pods, - sizeof(gboolean)))) - goto fail; + state = g_malloc0(sizeof(struct scope_state)); + state->analog_channels = g_malloc0_n(config->analog_channels, + sizeof(struct analog_channel_state)); + state->digital_channels = g_malloc0_n( + config->digital_channels, sizeof(gboolean)); + state->digital_pods = g_malloc0_n(config->digital_pods, + sizeof(gboolean)); return state; - -fail: - if (state->analog_channels) - g_free(state->analog_channels); - if (state->digital_channels) - g_free(state->digital_channels); - if (state->digital_pods) - g_free(state->digital_pods); - g_free(state); - - return NULL; } SR_PRIV void hmo_scope_state_free(struct scope_state *state) @@ -582,11 +660,11 @@ SR_PRIV void hmo_scope_state_free(struct scope_state *state) SR_PRIV int hmo_init_device(struct sr_dev_inst *sdi) { - char tmp[25]; int model_index; - unsigned int i, j; + unsigned int i, j, group; struct sr_channel *ch; struct dev_context *devc; + int ret; devc = sdi->priv; model_index = -1; @@ -610,16 +688,18 @@ SR_PRIV int hmo_init_device(struct sr_dev_inst *sdi) devc->analog_groups = g_malloc0(sizeof(struct sr_channel_group*) * scope_models[model_index].analog_channels); - devc->digital_groups = g_malloc0(sizeof(struct sr_channel_group*) * scope_models[model_index].digital_pods); + if (!devc->analog_groups || !devc->digital_groups) { + g_free(devc->analog_groups); + g_free(devc->digital_groups); + return SR_ERR_MALLOC; + } /* Add analog channels. */ for (i = 0; i < scope_models[model_index].analog_channels; i++) { - if (!(ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE, - (*scope_models[model_index].analog_names)[i]))) - return SR_ERR_MALLOC; - sdi->channels = g_slist_append(sdi->channels, ch); + ch = sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE, + (*scope_models[model_index].analog_names)[i]); devc->analog_groups[i] = g_malloc0(sizeof(struct sr_channel_group)); @@ -632,25 +712,28 @@ SR_PRIV int hmo_init_device(struct sr_dev_inst *sdi) } /* Add digital channel groups. */ - for (i = 0; i < scope_models[model_index].digital_pods; ++i) { - g_snprintf(tmp, 25, "POD%d", i); - + ret = SR_OK; + for (i = 0; i < scope_models[model_index].digital_pods; i++) { devc->digital_groups[i] = g_malloc0(sizeof(struct sr_channel_group)); - - devc->digital_groups[i]->name = g_strdup(tmp); + if (!devc->digital_groups[i]) { + ret = SR_ERR_MALLOC; + break; + } + devc->digital_groups[i]->name = g_strdup_printf("POD%d", i); sdi->channel_groups = g_slist_append(sdi->channel_groups, - devc->digital_groups[i < 8 ? 0 : 1]); + devc->digital_groups[i]); } + if (ret != SR_OK) + return ret; /* Add digital channels. */ for (i = 0; i < scope_models[model_index].digital_channels; i++) { - if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, - (*scope_models[model_index].digital_names)[i]))) - return SR_ERR_MALLOC; - sdi->channels = g_slist_append(sdi->channels, ch); + ch = sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, + (*scope_models[model_index].digital_names)[i]); - devc->digital_groups[i < 8 ? 0 : 1]->channels = g_slist_append( - devc->digital_groups[i < 8 ? 0 : 1]->channels, ch); + group = i / 8; + devc->digital_groups[group]->channels = g_slist_append( + devc->digital_groups[group]->channels, ch); } devc->model_config = &scope_models[model_index]; @@ -662,17 +745,108 @@ 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; struct sr_dev_inst *sdi; struct dev_context *devc; + struct scope_state *state; struct sr_datafeed_packet packet; - GArray *data; + GByteArray *data; 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; + size_t group; (void)fd; + (void)revents; + + data = NULL; if (!(sdi = cb_data)) return TRUE; @@ -680,68 +854,150 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data) if (!(devc = sdi->priv)) return TRUE; - if (revents == G_IO_IN) { - ch = devc->current_channel->data; - - switch (ch->type) { - case SR_CHANNEL_ANALOG: - if (sr_scpi_get_floatv(sdi->conn, NULL, &data) != SR_OK) { - if (data) - g_array_free(data, TRUE); + /* Although this is correct in general, the USBTMC libusb implementation + * currently does not generate an event prior to the first read. Often + * it is ok to start reading just after the 50ms timeout. See bug #785. + if (revents != G_IO_IN) + return TRUE; + */ - return TRUE; - } + ch = devc->current_channel->data; + state = devc->model_state; - packet.type = SR_DF_FRAME_BEGIN; - sr_session_send(sdi, &packet); + /* + * 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); + } - analog.channels = g_slist_append(NULL, ch); - analog.num_samples = data->len; - analog.data = (float *) data->data; - analog.mq = SR_MQ_VOLTAGE; - analog.unit = SR_UNIT_VOLT; - analog.mqflags = 0; - packet.type = SR_DF_ANALOG; - packet.payload = &analog; - sr_session_send(cb_data, &packet); - g_slist_free(analog.channels); - g_array_free(data, TRUE); - break; - case SR_CHANNEL_LOGIC: - if (sr_scpi_get_uint8v(sdi->conn, NULL, &data) != SR_OK) { - if (data) - g_free(data); - return TRUE; - } + /* + * 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) { + if (data) + g_byte_array_free(data, TRUE); + return TRUE; + } - packet.type = SR_DF_FRAME_BEGIN; - sr_session_send(sdi, &packet); + packet.type = SR_DF_ANALOG; + + analog.data = data->data; + analog.num_samples = data->len / sizeof(float); + analog.encoding = &encoding; + analog.meaning = &meaning; + analog.spec = &spec; + + encoding.unitsize = sizeof(float); + encoding.is_signed = TRUE; + encoding.is_float = TRUE; +#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; + encoding.scale.p = 1; + encoding.scale.q = 1; + encoding.offset.p = 0; + encoding.offset.q = 1; + if (state->analog_channels[ch->index].probe_unit == 'V') { + meaning.mq = SR_MQ_VOLTAGE; + meaning.unit = SR_UNIT_VOLT; + } else { + meaning.mq = SR_MQ_CURRENT; + meaning.unit = SR_UNIT_AMPERE; + } + meaning.mqflags = 0; + meaning.channels = g_slist_append(NULL, ch); + /* TODO: Use proper 'digits' value for this device (and its modes). */ + spec.spec_digits = 2; + packet.payload = &analog; + sr_session_send(sdi, &packet); + g_slist_free(meaning.channels); + g_byte_array_free(data, TRUE); + data = NULL; + break; + case SR_CHANNEL_LOGIC: + if (sr_scpi_get_block(sdi->conn, NULL, &data) != SR_OK) { + if (data) + g_byte_array_free(data, TRUE); + return TRUE; + } + /* + * 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; - logic.data = data->data; - packet.type = SR_DF_LOGIC; packet.payload = &logic; - sr_session_send(cb_data, &packet); - g_array_free(data, TRUE); - break; - default: - sr_err("Invalid channel type."); - break; + sr_session_send(sdi, &packet); + } else { + group = ch->index / 8; + hmo_queue_logic_data(devc, group, data); } - packet.type = SR_DF_FRAME_END; - sr_session_send(sdi, &packet); + g_byte_array_free(data, TRUE); + data = NULL; + break; + default: + sr_err("Invalid channel type."); + break; + } - 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, cb_data); - } else { - devc->current_channel = devc->enabled_channels; - hmo_request_data(sdi); - } + /* + * 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); + 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); } return TRUE;