#define CHUNK_SIZE (4 * 1024 * 1024)
#define MAX_POD_COUNT 12
-#define HEADER_SIZE 80
+
+#define SPACE ' '
+#define CTRLZ '\x1a'
+#define TRACE32 "trace32"
#define TIMESTAMP_RESOLUTION ((double)0.000000000078125) /* 0.078125 ns */
*/
#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_BINHDR1, /* Binary header, binary data, textual setup info, v1 */
+ AD_FORMAT_BINHDR2, /* Binary header, binary data, textual setup info, v2 */
+ 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 */
};
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;
- uint32_t record_size, record_count, cur_record;
+ uint32_t header_size, record_size, record_count, cur_record;
int32_t last_record;
uint64_t samplerate;
double timestamp_scale;
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_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;
+
+ /*
+ * First-level file header:
+ * 0x00-1F file format name
+ * 0x20 u64 trigger timestamp
+ * 0x28-2F unused
+ * 0x30 u8 compression
+ * 0x31-35 ??
+ * 0x32 u8 0x00 (PI), 0x01 (iprobe)
+ * 0x36 u8 device id: 0x08 (PI 250/500), 0x0A (iprobe 250)
+ */
/*
- * 00-31 (0x00-1F) file format name
- * 32-39 (0x20-27) trigger timestamp u64
- * 40-47 (0x28-2F) unused
- * 48 (0x30) compression
- * 49-53 (0x31-35) ??
- * 50 (0x32) 0x00 (PI), 0x01 (iprobe)
- * 54 (0x36) 0x08 (PI 250/500), 0x0A (iprobe 250)
- * 55 (0x37) 0x00 (250), 0x01 (500)
- * 56 (0x38) record size u8
- * 57-59 (0x39-3B) const 0x00
- * 60-63 (0x3C-3F) number of records u32
- * 64-67 (0x40-43) id of last record s32
- * 68-77 (0x44-4D) ??
- * 71 (0x47) const 0x80=128
- * 72 (0x48) const 0x01
- * 78-79 (0x4E-4F) ??
+ * Second-level file header, version 1:
+ * 0x37 u8 capture speed: 0x00 (250), 0x01 (500)
+ * 0x38 u8 record size
+ * 0x39-3B const 0x00
+ * 0x3C u32 number of records
+ * 0x40 s32 id of last record
+ * 0x44-4D ??
+ * 0x47 u8 const 0x80=128
+ * 0x48 u8 const 0x01
+ * 0x4E-4F ??
*/
- /* Note: inc is off-limits until we check whether it's a valid pointer. */
+ /*
+ * Second-level file header, version 2:
+ * 0x37 u8 ??
+ * 0x38 u64 ??
+ * 0x40 u64 ??
+ * 0x48 u8 record size
+ * 0x49-4F ??
+ * 0x50 u64 ??
+ * 0x58 u64 number of records
+ * 0x60 u64 ??
+ * 0x68 u64 ??
+ * 0x70 u64 ??
+ * 0x78 u64 ??
+ * 0x80 u64 ??
+ * 0x88 u64 ?? (timestamp of some kind?)
+ * 0x90 u64 ??
+ * 0x98-9E ??
+ * 0x9F u8 capture speed: 0x00 (250), 0x01 (500)
+ * 0xA0 u64 ??
+ * 0xA8 u64 ??
+ * 0xB0 u64 ??
+ * 0xB8-CF version string? (e.g. '93173--96069', same for all tested .ad files)
+ * 0xC8 u16 ??
+ */
- format_name = g_strndup(buf->str, 32);
+ /*
+ * 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.
+ */
- /* 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. */
+ /*
+ * 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);
- 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_BINHDR1;
+ } 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);
if (inc)
sr_err("This format isn't implemented yet, aborting.");
- else
- sr_dbg("Not a supported trace32 input file.");
return SR_ERR;
} else {
- g_free(format_name_sig);
+ /* Unknown kind of format name. Unsupported. */
g_free(format_name);
if (inc)
sr_err("Don't know this file format, aborting.");
- else
- sr_dbg("Not a trace32 input file.");
return SR_ERR;
}
+ if (!format)
+ return SR_ERR;
+
+ /* If the device id is 0x00, we have a v2 format file. */
+ if (R8(buf->str + 0x36) == 0x00)
+ format = AD_FORMAT_BINHDR2;
- 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);
+ record_size = (format == AD_FORMAT_BINHDR1) ?
+ R8(buf->str + 0x38) : R8(buf->str + 0x48);
device_id = 0;
if (g_strcmp0(format_name, "trace32 power integrator data") == 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->trigger_timestamp = RL64(buf->str + 0x20);
+ inc->compression = R8(buf->str + 0x30); /* Maps to the enum. */
+ inc->header_size = (format == AD_FORMAT_BINHDR1) ? 0x50 : 0xCA;
inc->record_size = record_size;
- inc->record_count = RL32(buf->str + 60);
- inc->last_record = RL32S(buf->str + 64);
+
+ if (format == AD_FORMAT_BINHDR1) {
+ inc->record_mode = R8(buf->str + 0x37); /* Maps to the enum. */
+ inc->record_count = RL32(buf->str + 0x3C);
+ inc->last_record = RL32S(buf->str + 0x40);
+ } else {
+ inc->record_mode = R8(buf->str + 0x9F); /* Maps to the enum. */
+ inc->record_count = RL32(buf->str + 0x58);
+ inc->last_record = inc->record_count;
+ }
sr_dbg("Trigger occured at %lf s.",
inc->trigger_timestamp * TIMESTAMP_RESOLUTION);
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;
static void send_metadata(struct sr_input *in)
{
- struct sr_datafeed_packet packet;
- struct sr_datafeed_meta meta;
- struct sr_config *src;
struct context *inc;
inc = in->priv;
-
- packet.type = SR_DF_META;
- packet.payload = &meta;
- src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(inc->samplerate));
- meta.config = g_slist_append(NULL, src);
- sr_session_send(in->sdi, &packet);
- g_slist_free(meta.config);
- sr_config_free(src);
-
+ (void)sr_session_send_meta(in->sdi, SR_CONF_SAMPLERATE,
+ g_variant_new_uint64(inc->samplerate));
inc->meta_sent = TRUE;
}
static void process_record_pi(struct sr_input *in, gsize start)
{
- struct sr_datafeed_packet packet;
struct context *inc;
uint64_t timestamp, next_timestamp;
uint32_t pod_data;
buf = in->buf;
/*
- * 00-07 timestamp
- * 08-09 A15..0
- * 10-11 B15..0
- * 12-13 C15..0
- * 14-15 D15..0
- * 16-17 E15..0
- * 18-19 F15..0
- * 20-23 ??
- * 24-25 J15..0 Not present in 500MHz mode
- * 26-27 K15..0 Not present in 500MHz mode
- * 28-29 L15..0 Not present in 500MHz mode
- * 30-31 M15..0 Not present in 500MHz mode
- * 32-33 N15..0 Not present in 500MHz mode
- * 34-35 O15..0 Not present in 500MHz mode
- * 36-39 ?? Not present in 500MHz mode
- * 40/24 CLKF..A (32=CLKF, .., 1=CLKA)
- * 41 CLKO..J (32=CLKO, .., 1=CLKJ) Not present in 500MHz mode
- * 42/25 ??
- * 43/26 ??
- * 44/27 ??
+ * 0x00 u8 timestamp
+ * 0x08 u16 A15..0
+ * 0x0A u16 B15..0
+ * 0x0C u16 C15..0
+ * 0x0E u16 D15..0
+ * 0x10 u16 E15..0
+ * 0x12 u16 F15..0
+ * 0x14 u32 ??
+ * 0x18 u16 J15..0 Not present in 500MHz mode
+ * 0x1A u16 K15..0 Not present in 500MHz mode
+ * 0x1C u16 L15..0 Not present in 500MHz mode
+ * 0x1E u16 M15..0 Not present in 500MHz mode
+ * 0x20 u16 N15..0 Not present in 500MHz mode
+ * 0x22 u16 O15..0 Not present in 500MHz mode
+ * 0x24 u32 ?? Not present in 500MHz mode
+ * 0x28/18 u8 CLKF..A (32=CLKF, .., 1=CLKA)
+ * 0x29/1A u8 CLKO..J (32=CLKO, .., 1=CLKJ) Not present in 500MHz mode
+ * 0x2A/19 u8 ??
+ * 0x2B/1A u8 ??
+ * 0x2C/1B u8 ??
*/
timestamp = RL64(buf->str + start);
if (inc->record_mode == AD_MODE_500MHZ) {
pod_count = 6;
- clk_offset = 24;
+ clk_offset = 0x18;
} else {
pod_count = 12;
- clk_offset = 40;
+ clk_offset = 0x28;
}
payload_bit = 0;
switch (pod) {
case 0: /* A */
- pod_data = RL16(buf->str + start + 8);
+ pod_data = RL16(buf->str + start + 0x08);
pod_data |= (RL16(buf->str + start + clk_offset) & 1) << 16;
break;
case 1: /* B */
- pod_data = RL16(buf->str + start + 10);
+ pod_data = RL16(buf->str + start + 0x0A);
pod_data |= (RL16(buf->str + start + clk_offset) & 2) << 15;
break;
case 2: /* C */
- pod_data = RL16(buf->str + start + 12);
+ pod_data = RL16(buf->str + start + 0x0C);
pod_data |= (RL16(buf->str + start + clk_offset) & 4) << 14;
break;
case 3: /* D */
- pod_data = RL16(buf->str + start + 14);
+ pod_data = RL16(buf->str + start + 0x0E);
pod_data |= (RL16(buf->str + start + clk_offset) & 8) << 13;
break;
case 4: /* E */
- pod_data = RL16(buf->str + start + 16);
+ pod_data = RL16(buf->str + start + 0x10);
pod_data |= (RL16(buf->str + start + clk_offset) & 16) << 12;
break;
case 5: /* F */
- pod_data = RL16(buf->str + start + 18);
+ pod_data = RL16(buf->str + start + 0x12);
pod_data |= (RL16(buf->str + start + clk_offset) & 32) << 11;
break;
case 6: /* J */
- pod_data = RL16(buf->str + start + 24);
- pod_data |= (RL16(buf->str + start + 41) & 1) << 16;
+ pod_data = RL16(buf->str + start + 0x18);
+ pod_data |= (RL16(buf->str + start + 0x29) & 1) << 16;
break;
case 7: /* K */
- pod_data = RL16(buf->str + start + 26);
- pod_data |= (RL16(buf->str + start + 41) & 2) << 15;
+ pod_data = RL16(buf->str + start + 0x1A);
+ pod_data |= (RL16(buf->str + start + 0x29) & 2) << 15;
break;
case 8: /* L */
- pod_data = RL16(buf->str + start + 28);
- pod_data |= (RL16(buf->str + start + 41) & 4) << 14;
+ pod_data = RL16(buf->str + start + 0x1C);
+ pod_data |= (RL16(buf->str + start + 0x29) & 4) << 14;
break;
case 9: /* M */
- pod_data = RL16(buf->str + start + 30);
- pod_data |= (RL16(buf->str + start + 41) & 8) << 13;
+ pod_data = RL16(buf->str + start + 0x1E);
+ pod_data |= (RL16(buf->str + start + 0x29) & 8) << 13;
break;
case 10: /* N */
- pod_data = RL16(buf->str + start + 32);
- pod_data |= (RL16(buf->str + start + 41) & 16) << 12;
+ pod_data = RL16(buf->str + start + 0x20);
+ pod_data |= (RL16(buf->str + start + 0x29) & 16) << 12;
break;
case 11: /* O */
- pod_data = RL16(buf->str + start + 34);
- pod_data |= (RL16(buf->str + start + 41) & 32) << 11;
+ pod_data = RL16(buf->str + start + 0x22);
+ pod_data |= (RL16(buf->str + start + 0x29) & 32) << 11;
break;
default:
+ pod_data = 0;
sr_err("Don't know how to obtain data for pod %d.", pod);
}
if (timestamp == inc->trigger_timestamp && !inc->trigger_sent) {
sr_dbg("Trigger @%lf s, record #%d.",
timestamp * TIMESTAMP_RESOLUTION, inc->cur_record);
-
- packet.type = SR_DF_TRIGGER;
- packet.payload = NULL;
- sr_session_send(in->sdi, &packet);
+ std_session_send_df_trigger(in->sdi);
inc->trigger_sent = TRUE;
}
static void process_record_iprobe(struct sr_input *in, gsize start)
{
- struct sr_datafeed_packet packet;
struct context *inc;
uint64_t timestamp, next_timestamp;
char single_payload[3];
inc = in->priv;
/*
- * 00-07 timestamp
- * 08-09 IP15..0
- * 10 CLK
+ * 0x00 u64 timestamp
+ * 0x08 u16 IP15..0
+ * 0x0A u8 CLK
*/
timestamp = RL64(in->buf->str + start);
- single_payload[0] = R8(in->buf->str + start + 8);
- single_payload[1] = R8(in->buf->str + start + 9);
- single_payload[2] = R8(in->buf->str + start + 10) & 1;
+ single_payload[0] = R8(in->buf->str + start + 0x08);
+ single_payload[1] = R8(in->buf->str + start + 0x09);
+ single_payload[2] = R8(in->buf->str + start + 0x0A) & 1;
payload_len = 3;
if (timestamp == inc->trigger_timestamp && !inc->trigger_sent) {
sr_dbg("Trigger @%lf s, record #%d.",
timestamp * TIMESTAMP_RESOLUTION, inc->cur_record);
-
- packet.type = SR_DF_TRIGGER;
- packet.payload = NULL;
- sr_session_send(in->sdi, &packet);
+ std_session_send_df_trigger(in->sdi);
inc->trigger_sent = TRUE;
}
if (!inc->header_read) {
res = process_header(in->buf, inc);
- g_string_erase(in->buf, 0, HEADER_SIZE);
+ g_string_erase(in->buf, 0, inc->header_size);
if (res != SR_OK)
return res;
}