+static int queue_analog_samples(const struct sr_input *in)
+{
+ struct context *inc;
+ int rc;
+
+ inc = in->priv;
+ if (!inc->analog_channels)
+ return SR_OK;
+
+ inc->analog_datafeed_buf_fill++;
+ if (inc->analog_datafeed_buf_fill == inc->analog_datafeed_buf_size) {
+ rc = flush_analog_samples(in);
+ if (rc != SR_OK)
+ return rc;
+ }
+
+ return SR_OK;
+}
+
+/* Helpers for "column processing". */
+
+static int split_column_format(const char *spec,
+ size_t *column_count, enum single_col_format *format, size_t *bit_count)
+{
+ size_t count;
+ char *endp, format_char;
+ enum single_col_format format_code;
+
+ if (!spec || !*spec)
+ return SR_ERR_ARG;
+
+ /* Get the (optional, decimal, default 1) column count. Accept '*'. */
+ endp = NULL;
+ if (*spec == '*') {
+ /* Workaround, strtoul("*") won't always yield expected endp. */
+ count = 0;
+ endp = (char *)&spec[1];
+ } else {
+ count = strtoul(spec, &endp, 10);
+ }
+ if (!endp)
+ return SR_ERR_ARG;
+ if (endp == spec)
+ count = 1;
+ if (column_count)
+ *column_count = count;
+ spec = endp;
+
+ /* Get the (mandatory, single letter) type spec (-/xob/l). */
+ format_char = *spec++;
+ switch (format_char) {
+ case '-':
+ case '/':
+ format_char = '-';
+ format_code = FORMAT_NONE;
+ break;
+ case 'x':
+ format_code = FORMAT_HEX;
+ break;
+ case 'o':
+ format_code = FORMAT_OCT;
+ break;
+ case 'b':
+ case 'l':
+ format_code = FORMAT_BIN;
+ break;
+ case 'a':
+ format_code = FORMAT_ANALOG;
+ break;
+ case 't':
+ format_code = FORMAT_TIME;
+ break;
+ default: /* includes NUL */
+ return SR_ERR_ARG;
+ }
+ if (format)
+ *format = format_code;
+
+ /* Get the (optional, decimal, default 1) bit count. */
+ endp = NULL;
+ count = strtoul(spec, &endp, 10);
+ if (!endp)
+ return SR_ERR_ARG;
+ if (endp == spec)
+ count = format_is_analog(format_code) ? 3 : 1;
+ if (format_is_ignore(format_code))
+ count = 0;
+ if (format_char == 'l')
+ count = 1;
+ if (bit_count)
+ *bit_count = count;
+ spec = endp;
+
+ /* Input spec must have been exhausted. */
+ if (*spec)
+ return SR_ERR_ARG;
+
+ return SR_OK;
+}
+
+static int make_column_details_from_format(const struct sr_input *in,
+ const char *column_format, char **column_texts)
+{
+ struct context *inc;
+ char **formats, *format;
+ size_t format_count, column_count, logic_count, analog_count;
+ size_t auto_column_count;
+ size_t format_idx, c, b, column_idx, channel_idx, analog_idx;
+ enum single_col_format f;
+ struct column_details *detail;
+ GString *channel_name;
+ size_t create_idx;
+ char *column;
+ const char *caption;
+ int channel_type, channel_sdi_nr;
+ void *channel;
+ int ret;
+
+ inc = in->priv;
+ inc->column_seen_count = g_strv_length(column_texts);
+
+ /* Split the input spec, count involved columns and channels. */
+ formats = g_strsplit(column_format, ",", 0);
+ if (!formats) {
+ sr_err("Cannot parse columns format %s (comma split).", column_format);
+ return SR_ERR_ARG;
+ }
+ format_count = g_strv_length(formats);
+ if (!format_count) {
+ sr_err("Cannot parse columns format %s (field count).", column_format);
+ g_strfreev(formats);
+ return SR_ERR_ARG;
+ }
+ column_count = logic_count = analog_count = 0;
+ auto_column_count = 0;
+ for (format_idx = 0; format_idx < format_count; format_idx++) {
+ format = formats[format_idx];
+ ret = split_column_format(format, &c, &f, &b);
+ sr_dbg("fmt %s -> %zu cols, %s fmt, %zu bits, rc %d", format, c, col_format_text[f], b, ret);
+ if (ret != SR_OK) {
+ sr_err("Cannot parse columns format %s (field split, %s).", column_format, format);
+ g_strfreev(formats);
+ return SR_ERR_ARG;
+ }
+ if (f && !c) {
+ /* User requested "auto-count", must be last format. */
+ if (formats[format_idx + 1]) {
+ sr_err("Auto column count must be last format field.");
+ g_strfreev(formats);
+ return SR_ERR_ARG;
+ }
+ auto_column_count = inc->column_seen_count - column_count;
+ c = auto_column_count;
+ }
+ column_count += c;
+ if (format_is_analog(f))
+ analog_count += c;
+ else if (format_is_logic(f))
+ logic_count += c * b;
+ }
+ sr_dbg("Column format %s -> %zu columns, %zu logic, %zu analog channels.",
+ column_format, column_count, logic_count, analog_count);
+
+ /* Allocate and fill in "column processing" details. */
+ inc->column_want_count = column_count;
+ if (inc->column_seen_count < inc->column_want_count) {
+ sr_err("Insufficient input text width for desired data amount, got %zu but want %zu columns.",
+ inc->column_seen_count, inc->column_want_count);
+ g_strfreev(formats);
+ return SR_ERR_ARG;
+ }
+ inc->logic_channels = logic_count;
+ inc->analog_channels = analog_count;
+ inc->analog_datafeed_digits = g_malloc0(inc->analog_channels * sizeof(inc->analog_datafeed_digits[0]));
+ inc->analog_datafeed_channels = g_malloc0(inc->analog_channels * sizeof(inc->analog_datafeed_channels[0]));
+ inc->column_details = g_malloc0_n(column_count, sizeof(inc->column_details[0]));
+ column_idx = channel_idx = analog_idx = 0;
+ channel_name = g_string_sized_new(64);
+ for (format_idx = 0; format_idx < format_count; format_idx++) {
+ /* Process a format field, which can span multiple columns. */
+ format = formats[format_idx];
+ (void)split_column_format(format, &c, &f, &b);
+ if (f && !c)
+ c = auto_column_count;
+ while (c-- > 0) {
+ /* Fill in a column's processing details. */
+ detail = &inc->column_details[column_idx++];
+ detail->col_nr = column_idx;
+ detail->text_format = f;
+ if (format_is_analog(detail->text_format)) {
+ detail->channel_offset = analog_idx;
+ detail->channel_count = 1;
+ detail->analog_digits = b;
+ analog_idx += detail->channel_count;
+ } else if (format_is_logic(detail->text_format)) {
+ detail->channel_offset = channel_idx;
+ detail->channel_count = b;
+ channel_idx += detail->channel_count;
+ } else if (format_is_ignore(detail->text_format)) {
+ /* EMPTY */
+ continue;
+ } else {
+ /*
+ * Neither logic nor analog data, nor ignore.
+ * Format was noted. No channel creation involved.
+ */
+ continue;
+ }
+ /*
+ * Pick most appropriate channel names. Optionally
+ * use text from a header line (when requested by the
+ * user). In the absence of header text, channels are
+ * assigned rather generic names.
+ *
+ * Manipulation of the column's caption (when a header
+ * line is seen) is acceptable, because this header
+ * line won't get processed another time.
+ */
+ column = column_texts[detail->col_nr - 1];
+ if (inc->use_header && column && *column) {
+ column = g_strstrip(column);
+ caption = sr_scpi_unquote_string(column);
+ } else {
+ caption = NULL;
+ }
+ if (!caption || !*caption)
+ caption = NULL;
+ /*
+ * Collect channel creation details here, but defer
+ * actual creation of the channels such that all
+ * logic channels can get created first and analog
+ * channels only get created afterwards.
+ */
+ detail->channel_names = g_malloc0(detail->channel_count * sizeof(detail->channel_names[0]));
+ for (create_idx = 0; create_idx < detail->channel_count; create_idx++) {
+ if (caption && detail->channel_count == 1) {
+ g_string_assign(channel_name, caption);
+ } else if (caption) {
+ g_string_printf(channel_name, "%s[%zu]",
+ caption, create_idx);
+ } else {
+ g_string_printf(channel_name, "%zu",
+ detail->channel_offset + create_idx);
+ }
+ detail->channel_names[create_idx] = g_string_new_len(channel_name->str, channel_name->len);
+ }
+ }
+ }
+ g_string_free(channel_name, TRUE);
+ g_strfreev(formats);
+
+ /* Create channels in strict logic to analog order. */
+ channel_type = SR_CHANNEL_LOGIC;
+ for (column_idx = 0; column_idx < inc->column_want_count; column_idx++) {
+ detail = &inc->column_details[column_idx];
+ if (!format_is_logic(detail->text_format))
+ continue;
+ for (create_idx = 0; create_idx < detail->channel_count; create_idx++) {
+ caption = detail->channel_names[create_idx]->str;
+ channel_sdi_nr = g_slist_length(in->sdi->channels);
+ sr_channel_new(in->sdi, channel_sdi_nr, channel_type, TRUE, caption);
+ }
+ }
+ channel_type = SR_CHANNEL_ANALOG;
+ for (column_idx = 0; column_idx < inc->column_want_count; column_idx++) {
+ detail = &inc->column_details[column_idx];
+ if (!format_is_analog(detail->text_format))
+ continue;
+ caption = detail->channel_names[0]->str;
+ channel_sdi_nr = g_slist_length(in->sdi->channels);
+ channel = sr_channel_new(in->sdi, channel_sdi_nr, channel_type, TRUE, caption);
+ channel_idx = channel_sdi_nr - inc->logic_channels;
+ inc->analog_datafeed_digits[channel_idx] = detail->analog_digits;
+ inc->analog_datafeed_channels[channel_idx] = g_slist_append(NULL, channel);
+ }
+
+ return SR_OK;
+}
+
+static const struct column_details *lookup_column_details(struct context *inc, size_t nr)
+{
+ if (!inc || !inc->column_details)
+ return NULL;
+ if (!nr || nr > inc->column_want_count)
+ return NULL;
+
+ return &inc->column_details[nr - 1];
+}
+