#define LOG_PREFIX "input/trace32_ad"
-#define MAX_CHUNK_SIZE 4096
-#define OUTBUF_FLUSH_SIZE 10240
+#define CHUNK_SIZE (4 * 1024 * 1024)
#define MAX_POD_COUNT 12
#define HEADER_SIZE 80
-#define TIMESTAMP_RESOLUTION ((double)0.000000000078125) /* 0.078125 ns */
+#define SPACE ' '
+#define CTRLZ '\x1a'
+#define TRACE32 "trace32"
+
+#define TIMESTAMP_RESOLUTION ((double)0.000000000078125) /* 0.078125 ns */
/*
* The resolution equals a sampling freq of 12.8 GHz. That's a bit high
*/
#define DEFAULT_SAMPLERATE 200
-enum {
- AD_FORMAT_BINHDR = 1, /* Binary header, binary data, textual setup info */
- AD_FORMAT_TXTHDR /* Textual header, binary data */
+enum ad_format {
+ AD_FORMAT_UNKNOWN,
+ AD_FORMAT_BINHDR, /* Binary header, binary data, textual setup info */
+ AD_FORMAT_TXTHDR, /* Textual header, binary data */
};
-enum {
+enum ad_device {
AD_DEVICE_PI = 1, /* Data recorded by LA-7940 PowerIntegrator or */
/* LA-394x PowerIntegrator II. */
AD_DEVICE_IPROBE /* Data recorded by LA-769x PowerTrace II IProbe. */
/* Missing file format info for LA-4530 uTrace analog probe */
};
-enum {
+enum ad_mode {
AD_MODE_250MHZ = 0,
AD_MODE_500MHZ = 1
};
-enum {
+enum ad_compr {
AD_COMPR_NONE = 0, /* File created with /NOCOMPRESS */
- AD_COMPR_QCOMP = 6 /* File created with /COMPRESS or /QUICKCOMPRESS */
+ AD_COMPR_QCOMP = 6, /* File created with /COMPRESS or /QUICKCOMPRESS */
};
struct context {
gboolean meta_sent;
gboolean header_read, records_read, trigger_sent;
- char format, device, record_mode, compression;
+ enum ad_format format;
+ enum ad_device device;
+ enum ad_mode record_mode;
+ enum ad_compr compression;
char pod_status[MAX_POD_COUNT];
struct sr_channel *channels[MAX_POD_COUNT][17]; /* 16 + CLK */
uint64_t trigger_timestamp;
static int process_header(GString *buf, struct context *inc);
static void create_channels(struct sr_input *in);
+/* Transform non-printable chars to '\xNN' presentation. */
+static char *printable_name(const char *name)
+{
+ size_t l, i;
+ char *s, *p;
+
+ if (!name)
+ return NULL;
+ l = strlen(name);
+ s = g_malloc0(l * strlen("\\x00") + 1);
+ for (p = s, i = 0; i < l; i++) {
+ if (g_ascii_isprint(name[i])) {
+ *p++ = name[i];
+ } else {
+ snprintf(p, 5, "\\x%02x", name[i]);
+ p += strlen("\\x00");
+ }
+ }
+ *p = '\0';
+
+ return s;
+}
+
static char get_pod_name_from_id(int id)
{
switch (id) {
return SR_ERR;
}
- inc->out_buf = g_string_sized_new(OUTBUF_FLUSH_SIZE);
+ inc->out_buf = g_string_sized_new(CHUNK_SIZE);
return SR_OK;
}
-static int format_match(GHashTable *metadata)
+static int format_match(GHashTable *metadata, unsigned int *confidence)
{
GString *buf;
+ int rc;
buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
+ rc = process_header(buf, NULL);
+
+ if (rc != SR_OK)
+ return rc;
+ *confidence = 10;
- return process_header(buf, NULL);
+ return SR_OK;
}
static int process_header(GString *buf, struct context *inc)
{
char *format_name, *format_name_sig;
- int i, record_size, device_id;
+ char *p;
+ int has_trace32;
+ size_t record_size;
+ enum ad_device device_id;
+ enum ad_format format;
/*
* 00-31 (0x00-1F) file format name
* 78-79 (0x4E-4F) ??
*/
- /* Note: inc is off-limits until we check whether it's a valid pointer. */
+ /*
+ * Note: The routine is called from different contexts. Either
+ * to auto-detect the file format (format_match(), 'inc' is NULL),
+ * or to process the data during acquisition (receive(), 'inc'
+ * is a valid pointer). This header parse routine shall gracefully
+ * deal with unexpected or incorrect input data.
+ */
+ /*
+ * Get up to the first 32 bytes of the file content. File format
+ * names end on SPACE or CTRL-Z (or NUL). Trim trailing SPACE
+ * before further processing.
+ */
format_name = g_strndup(buf->str, 32);
+ p = strchr(format_name, CTRLZ);
+ if (p)
+ *p = '\0';
+ g_strchomp(format_name);
- /* File format name ends on 0x20/0x1A, let's remove both. */
- for (i = 1; i < 31; i++) {
- if (format_name[i] == 0x1A) {
- format_name[i - 1] = 0;
- format_name[i] = 0;
- }
- }
- g_strchomp(format_name); /* This is for additional padding spaces. */
-
- format_name_sig = g_strndup(format_name, 5);
+ /*
+ * File format names either start with the "trace32" literal,
+ * or with a digit and SPACE.
+ */
+ format_name_sig = g_strndup(format_name, strlen(TRACE32));
+ has_trace32 = g_strcmp0(format_name_sig, TRACE32) == 0;
+ g_free(format_name_sig);
- /* Desired file formats either start with digit+space or "trace32". */
- if (g_strcmp0(format_name_sig, "trace32")) {
- if (inc)
- inc->format = AD_FORMAT_BINHDR;
- } else if (g_ascii_isdigit(format_name[0]) && (format_name[1] == 0x20)) {
- if (inc)
- inc->format = AD_FORMAT_TXTHDR;
- g_free(format_name_sig);
+ format = AD_FORMAT_UNKNOWN;
+ if (has_trace32) {
+ /* Literal "trace32" leader, binary header follows. */
+ format = AD_FORMAT_BINHDR;
+ } else if (g_ascii_isdigit(format_name[0]) && (format_name[1] == SPACE)) {
+ /* Digit and SPACE leader, currently unsupported text header. */
+ format = AD_FORMAT_TXTHDR;
g_free(format_name);
- sr_err("This format isn't implemented yet, aborting.");
+ if (inc)
+ sr_err("This format isn't implemented yet, aborting.");
return SR_ERR;
} else {
- g_free(format_name_sig);
+ /* Unknown kind of format name. Unsupported. */
g_free(format_name);
- sr_err("Don't know this file format, aborting.");
+ if (inc)
+ sr_err("Don't know this file format, aborting.");
return SR_ERR;
}
+ if (!format)
+ return SR_ERR;
- sr_dbg("File says it's \"%s\"", format_name);
+ p = printable_name(format_name);
+ if (inc)
+ sr_dbg("File says it's \"%s\" -> format type %u.", p, format);
+ g_free(p);
record_size = R8(buf->str + 56);
device_id = 0;
}
if (!device_id) {
- g_free(format_name_sig);
g_free(format_name);
- sr_err("Don't know how to handle this file with record size %d.",
- record_size);
+ if (inc)
+ sr_err("Cannot handle file with record size %zu.",
+ record_size);
return SR_ERR;
}
- g_free(format_name_sig);
g_free(format_name);
/* Stop processing the header if we just want to identify the file. */
if (!inc)
return SR_OK;
+ inc->format = format;
inc->device = device_id;
inc->trigger_timestamp = RL64(buf->str + 32);
- inc->compression = R8(buf->str + 48); /* Maps to the enum. */
- inc->record_mode = R8(buf->str + 55); /* Maps to the enum. */
+ inc->compression = R8(buf->str + 48); /* Maps to the enum. */
+ inc->record_mode = R8(buf->str + 55); /* Maps to the enum. */
inc->record_size = record_size;
inc->record_count = RL32(buf->str + 60);
inc->last_record = RL32S(buf->str + 64);
inc->last_record);
/* Check if we can work with this compression. */
- if (inc->compression != AD_COMPR_NONE) {
+ if (inc->compression) {
sr_err("File uses unsupported compression (0x%02X), can't continue.",
inc->compression);
return SR_ERR;
continue;
for (channel = 0; channel < 16; channel++) {
- snprintf(name, 8, "%c%d", get_pod_name_from_id(pod), channel);
+ snprintf(name, sizeof(name), "%c%d", get_pod_name_from_id(pod), channel);
inc->channels[pod][channel] =
sr_channel_new(in->sdi, chan_id, SR_CHANNEL_LOGIC, TRUE, name);
chan_id++;
}
- snprintf(name, 8, "CLK%c", get_pod_name_from_id(pod));
+ snprintf(name, sizeof(name), "CLK%c", get_pod_name_from_id(pod));
inc->channels[pod][16] =
sr_channel_new(in->sdi, chan_id, SR_CHANNEL_LOGIC, TRUE, name);
chan_id++;
g_string_append_len(inc->out_buf, single_payload, payload_len);
}
- if (inc->out_buf->len >= OUTBUF_FLUSH_SIZE)
+ if (inc->out_buf->len >= CHUNK_SIZE)
flush_output_buffer(in);
}
g_string_append_len(inc->out_buf, single_payload, payload_len);
}
- if (inc->out_buf->len >= OUTBUF_FLUSH_SIZE)
+ if (inc->out_buf->len >= CHUNK_SIZE)
flush_output_buffer(in);
}
{ "podN", "Import pod N", "Create channels and data for pod N", NULL, NULL },
{ "podO", "Import pod O", "Create channels and data for pod O", NULL, NULL },
- { "samplerate", "Reduced sample rate in MHz", "Reduced sample rate in MHz", NULL, NULL },
+ { "samplerate", "Reduced sample rate (MHz)", "Reduce the original sample rate of 12.8 GHz to the specified sample rate in MHz", NULL, NULL },
ALL_ZERO
};