]> sigrok.org Git - libsigrok.git/blame - src/input/trace32_ad.c
output/csv: use intermediate time_t var, silence compiler warning
[libsigrok.git] / src / input / trace32_ad.c
CommitLineData
6d2897e3
SA
1/*
2 * This file is part of the libsigrok project.
3 *
4 * Copyright (C) 2015 Soeren Apel <soeren@apelpie.net>
5 * Copyright (C) 2015 Bert Vermeulen <bert@biot.com>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21/*
22 * Usage notes:
23 * This input module reads .ad files created using the
24 * following practice commands:
25 *
26 * I.SAVE <file> /NoCompress
27 * IPROBE.SAVE <file> /NoCompress
28 *
29 * It currently cannot make use of files that have been
30 * saved using /QuickCompress, /Compress or /ZIP.
31 * As a workaround you may load the file in PowerView
32 * using I.LOAD / IPROBE.LOAD and re-save using /NoCompress.
33 */
34
35#include <config.h>
36#include <stdlib.h>
37#include <string.h>
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <fcntl.h>
41#include <unistd.h>
42#include <sys/time.h>
43#include <libsigrok/libsigrok.h>
44#include "libsigrok-internal.h"
45
46#define LOG_PREFIX "input/trace32_ad"
47
9a4fd01a 48#define CHUNK_SIZE (4 * 1024 * 1024)
6d2897e3 49#define MAX_POD_COUNT 12
6d2897e3 50
80430d4d
GS
51#define SPACE ' '
52#define CTRLZ '\x1a'
53#define TRACE32 "trace32"
54
d9251a2c 55#define TIMESTAMP_RESOLUTION ((double)0.000000000078125) /* 0.078125 ns */
6d2897e3
SA
56
57/*
58 * The resolution equals a sampling freq of 12.8 GHz. That's a bit high
59 * for inter-record sample generation, so we scale it down to 200 MHz
60 * for now. That way, the scaling factor becomes 32.
61 */
6266deb8 62#define DEFAULT_SAMPLERATE 200
6d2897e3 63
83df004b
GS
64enum ad_format {
65 AD_FORMAT_UNKNOWN,
7ed4ae63
SA
66 AD_FORMAT_BINHDR1, /* Binary header, binary data, textual setup info, v1 */
67 AD_FORMAT_BINHDR2, /* Binary header, binary data, textual setup info, v2 */
83df004b 68 AD_FORMAT_TXTHDR, /* Textual header, binary data */
6d2897e3
SA
69};
70
83df004b 71enum ad_device {
6d2897e3
SA
72 AD_DEVICE_PI = 1, /* Data recorded by LA-7940 PowerIntegrator or */
73 /* LA-394x PowerIntegrator II. */
74 AD_DEVICE_IPROBE /* Data recorded by LA-769x PowerTrace II IProbe. */
75 /* Missing file format info for LA-793x ICD PowerProbe */
76 /* Missing file format info for LA-4530 uTrace analog probe */
77};
78
83df004b 79enum ad_mode {
6d2897e3
SA
80 AD_MODE_250MHZ = 0,
81 AD_MODE_500MHZ = 1
82};
83
83df004b 84enum ad_compr {
6d2897e3 85 AD_COMPR_NONE = 0, /* File created with /NOCOMPRESS */
d9251a2c 86 AD_COMPR_QCOMP = 6, /* File created with /COMPRESS or /QUICKCOMPRESS */
6d2897e3
SA
87};
88
89struct context {
90 gboolean meta_sent;
a5be5d5b 91 gboolean header_read, records_read, trigger_sent;
83df004b
GS
92 enum ad_format format;
93 enum ad_device device;
94 enum ad_mode record_mode;
95 enum ad_compr compression;
6d2897e3
SA
96 char pod_status[MAX_POD_COUNT];
97 struct sr_channel *channels[MAX_POD_COUNT][17]; /* 16 + CLK */
98 uint64_t trigger_timestamp;
7ed4ae63 99 uint32_t header_size, record_size, record_count, cur_record;
6d2897e3 100 int32_t last_record;
6266deb8
SA
101 uint64_t samplerate;
102 double timestamp_scale;
6d2897e3
SA
103 GString *out_buf;
104};
105
106static int process_header(GString *buf, struct context *inc);
107static void create_channels(struct sr_input *in);
108
8c4bff1d
GS
109/* Transform non-printable chars to '\xNN' presentation. */
110static char *printable_name(const char *name)
111{
112 size_t l, i;
113 char *s, *p;
114
115 if (!name)
116 return NULL;
117 l = strlen(name);
118 s = g_malloc0(l * strlen("\\x00") + 1);
119 for (p = s, i = 0; i < l; i++) {
120 if (g_ascii_isprint(name[i])) {
121 *p++ = name[i];
122 } else {
67765e46 123 snprintf(p, 5, "\\x%02x", name[i]);
8c4bff1d
GS
124 p += strlen("\\x00");
125 }
126 }
127 *p = '\0';
128
129 return s;
130}
131
6d2897e3
SA
132static char get_pod_name_from_id(int id)
133{
134 switch (id) {
135 case 0: return 'A';
136 case 1: return 'B';
137 case 2: return 'C';
138 case 3: return 'D';
139 case 4: return 'E';
140 case 5: return 'F';
141 case 6: return 'J';
142 case 7: return 'K';
143 case 8: return 'L';
144 case 9: return 'M';
145 case 10: return 'N';
146 case 11: return 'O';
147 default:
148 sr_err("get_pod_name_from_id() called with invalid ID %d!", id);
149 }
150 return 'X';
151}
152
153static int init(struct sr_input *in, GHashTable *options)
154{
155 struct context *inc;
156 int pod;
157 char id[17];
158
159 in->sdi = g_malloc0(sizeof(struct sr_dev_inst));
160 in->priv = g_malloc0(sizeof(struct context));
161
162 inc = in->priv;
163
6266deb8
SA
164 /* Calculate the desired timestamp scaling factor. */
165 inc->samplerate = 1000000 *
77ee3168 166 g_variant_get_uint64(g_hash_table_lookup(options, "samplerate"));
6266deb8
SA
167
168 inc->timestamp_scale = ((1 / TIMESTAMP_RESOLUTION) / (double)inc->samplerate);
169
6d2897e3
SA
170 /* Enable the pods the user chose to see. */
171 for (pod = 0; pod < MAX_POD_COUNT; pod++) {
172 g_snprintf(id, sizeof(id), "pod%c", get_pod_name_from_id(pod));
173 if (g_variant_get_boolean(g_hash_table_lookup(options, id)))
174 inc->pod_status[pod] = 1;
175 }
176
177 create_channels(in);
178 if (g_slist_length(in->sdi->channels) == 0) {
179 sr_err("No pods were selected and thus no channels created, aborting.");
180 g_free(in->priv);
181 g_free(in->sdi);
182 return SR_ERR;
183 }
184
8bc2fa6d 185 inc->out_buf = g_string_sized_new(CHUNK_SIZE);
6d2897e3
SA
186
187 return SR_OK;
188}
189
54ee427d 190static int format_match(GHashTable *metadata, unsigned int *confidence)
6d2897e3
SA
191{
192 GString *buf;
54ee427d 193 int rc;
6d2897e3
SA
194
195 buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
54ee427d 196 rc = process_header(buf, NULL);
6d2897e3 197
54ee427d
GS
198 if (rc != SR_OK)
199 return rc;
200 *confidence = 10;
201
202 return SR_OK;
6d2897e3
SA
203}
204
205static int process_header(GString *buf, struct context *inc)
206{
207 char *format_name, *format_name_sig;
8c4bff1d 208 char *p;
80430d4d 209 int has_trace32;
83df004b
GS
210 size_t record_size;
211 enum ad_device device_id;
212 enum ad_format format;
6d2897e3
SA
213
214 /*
7ed4ae63 215 * First-level file header:
628dc330
SA
216 * 0x00-1F file format name
217 * 0x20 u64 trigger timestamp
218 * 0x28-2F unused
219 * 0x30 u8 compression
220 * 0x31-35 ??
221 * 0x32 u8 0x00 (PI), 0x01 (iprobe)
7ed4ae63
SA
222 * 0x36 u8 device id: 0x08 (PI 250/500), 0x0A (iprobe 250)
223 */
224
225 /*
226 * Second-level file header, version 1:
227 * 0x37 u8 capture speed: 0x00 (250), 0x01 (500)
628dc330
SA
228 * 0x38 u8 record size
229 * 0x39-3B const 0x00
230 * 0x3C u32 number of records
231 * 0x40 s32 id of last record
232 * 0x44-4D ??
233 * 0x47 u8 const 0x80=128
234 * 0x48 u8 const 0x01
235 * 0x4E-4F ??
6d2897e3
SA
236 */
237
7ed4ae63
SA
238 /*
239 * Second-level file header, version 2:
240 * 0x37 u8 ??
241 * 0x38 u64 ??
242 * 0x40 u64 ??
243 * 0x48 u8 record size
244 * 0x49-4F ??
245 * 0x50 u64 ??
246 * 0x58 u64 number of records
247 * 0x60 u64 ??
248 * 0x68 u64 ??
249 * 0x70 u64 ??
250 * 0x78 u64 ??
251 * 0x80 u64 ??
252 * 0x88 u64 ?? (timestamp of some kind?)
253 * 0x90 u64 ??
254 * 0x98-9E ??
255 * 0x9F u8 capture speed: 0x00 (250), 0x01 (500)
256 * 0xA0 u64 ??
257 * 0xA8 u64 ??
258 * 0xB0 u64 ??
259 * 0xB8-CF version string? (e.g. '93173--96069', same for all tested .ad files)
260 * 0xC8 u16 ??
261 */
262
8c4bff1d
GS
263 /*
264 * Note: The routine is called from different contexts. Either
265 * to auto-detect the file format (format_match(), 'inc' is NULL),
266 * or to process the data during acquisition (receive(), 'inc'
267 * is a valid pointer). This header parse routine shall gracefully
268 * deal with unexpected or incorrect input data.
269 */
6d2897e3 270
80430d4d
GS
271 /*
272 * Get up to the first 32 bytes of the file content. File format
273 * names end on SPACE or CTRL-Z (or NUL). Trim trailing SPACE
274 * before further processing.
275 */
6d2897e3 276 format_name = g_strndup(buf->str, 32);
80430d4d
GS
277 p = strchr(format_name, CTRLZ);
278 if (p)
279 *p = '\0';
280 g_strchomp(format_name);
6d2897e3 281
80430d4d
GS
282 /*
283 * File format names either start with the "trace32" literal,
284 * or with a digit and SPACE.
285 */
286 format_name_sig = g_strndup(format_name, strlen(TRACE32));
287 has_trace32 = g_strcmp0(format_name_sig, TRACE32) == 0;
288 g_free(format_name_sig);
6d2897e3 289
83df004b 290 format = AD_FORMAT_UNKNOWN;
80430d4d
GS
291 if (has_trace32) {
292 /* Literal "trace32" leader, binary header follows. */
7ed4ae63 293 format = AD_FORMAT_BINHDR1;
80430d4d
GS
294 } else if (g_ascii_isdigit(format_name[0]) && (format_name[1] == SPACE)) {
295 /* Digit and SPACE leader, currently unsupported text header. */
83df004b 296 format = AD_FORMAT_TXTHDR;
6d2897e3 297 g_free(format_name);
cdb134eb
GS
298 if (inc)
299 sr_err("This format isn't implemented yet, aborting.");
6d2897e3
SA
300 return SR_ERR;
301 } else {
80430d4d 302 /* Unknown kind of format name. Unsupported. */
6d2897e3 303 g_free(format_name);
cdb134eb
GS
304 if (inc)
305 sr_err("Don't know this file format, aborting.");
6d2897e3
SA
306 return SR_ERR;
307 }
83df004b
GS
308 if (!format)
309 return SR_ERR;
6d2897e3 310
7ed4ae63
SA
311 /* If the device id is 0x00, we have a v2 format file. */
312 if (R8(buf->str + 0x36) == 0x00)
313 format = AD_FORMAT_BINHDR2;
314
8c4bff1d 315 p = printable_name(format_name);
83df004b
GS
316 if (inc)
317 sr_dbg("File says it's \"%s\" -> format type %u.", p, format);
8c4bff1d 318 g_free(p);
6d2897e3 319
7ed4ae63
SA
320 record_size = (format == AD_FORMAT_BINHDR1) ?
321 R8(buf->str + 0x38) : R8(buf->str + 0x48);
6d2897e3
SA
322 device_id = 0;
323
324 if (g_strcmp0(format_name, "trace32 power integrator data") == 0) {
325 if (record_size == 28 || record_size == 45)
326 device_id = AD_DEVICE_PI;
327 } else if (g_strcmp0(format_name, "trace32 iprobe data") == 0) {
328 if (record_size == 11)
329 device_id = AD_DEVICE_IPROBE;
330 }
331
332 if (!device_id) {
6d2897e3 333 g_free(format_name);
83df004b
GS
334 if (inc)
335 sr_err("Cannot handle file with record size %zu.",
336 record_size);
6d2897e3
SA
337 return SR_ERR;
338 }
339
6d2897e3
SA
340 g_free(format_name);
341
342 /* Stop processing the header if we just want to identify the file. */
343 if (!inc)
344 return SR_OK;
345
83df004b 346 inc->format = format;
6d2897e3 347 inc->device = device_id;
628dc330
SA
348 inc->trigger_timestamp = RL64(buf->str + 0x20);
349 inc->compression = R8(buf->str + 0x30); /* Maps to the enum. */
7ed4ae63 350 inc->header_size = (format == AD_FORMAT_BINHDR1) ? 0x50 : 0xCA;
6d2897e3 351 inc->record_size = record_size;
7ed4ae63
SA
352
353 if (format == AD_FORMAT_BINHDR1) {
354 inc->record_mode = R8(buf->str + 0x37); /* Maps to the enum. */
355 inc->record_count = RL32(buf->str + 0x3C);
356 inc->last_record = RL32S(buf->str + 0x40);
357 } else {
358 inc->record_mode = R8(buf->str + 0x9F); /* Maps to the enum. */
359 inc->record_count = RL32(buf->str + 0x58);
360 inc->last_record = inc->record_count;
361 }
6d2897e3
SA
362
363 sr_dbg("Trigger occured at %lf s.",
364 inc->trigger_timestamp * TIMESTAMP_RESOLUTION);
365 sr_dbg("File contains %d records: first one is %d, last one is %d.",
366 inc->record_count, (inc->last_record - inc->record_count + 1),
367 inc->last_record);
368
369 /* Check if we can work with this compression. */
83df004b 370 if (inc->compression) {
6d2897e3
SA
371 sr_err("File uses unsupported compression (0x%02X), can't continue.",
372 inc->compression);
373 return SR_ERR;
374 }
375
376 inc->header_read = TRUE;
377
378 return SR_OK;
379}
380
381static void create_channels(struct sr_input *in)
382{
383 struct context *inc;
384 int pod, channel, chan_id;
385 char name[8];
386
387 inc = in->priv;
388 chan_id = 0;
389
390 for (pod = 0; pod < MAX_POD_COUNT; pod++) {
391 if (!inc->pod_status[pod])
392 continue;
393
394 for (channel = 0; channel < 16; channel++) {
00ed77f2 395 snprintf(name, sizeof(name), "%c%d", get_pod_name_from_id(pod), channel);
6d2897e3
SA
396 inc->channels[pod][channel] =
397 sr_channel_new(in->sdi, chan_id, SR_CHANNEL_LOGIC, TRUE, name);
398 chan_id++;
399 }
400
00ed77f2 401 snprintf(name, sizeof(name), "CLK%c", get_pod_name_from_id(pod));
6d2897e3
SA
402 inc->channels[pod][16] =
403 sr_channel_new(in->sdi, chan_id, SR_CHANNEL_LOGIC, TRUE, name);
404 chan_id++;
405 }
406}
407
408static void send_metadata(struct sr_input *in)
409{
6d2897e3
SA
410 struct context *inc;
411
6266deb8 412 inc = in->priv;
f8a8d4bb
GS
413 (void)sr_session_send_meta(in->sdi, SR_CONF_SAMPLERATE,
414 g_variant_new_uint64(inc->samplerate));
6d2897e3
SA
415 inc->meta_sent = TRUE;
416}
417
418static void flush_output_buffer(struct sr_input *in)
419{
420 struct context *inc;
421 struct sr_datafeed_packet packet;
422 struct sr_datafeed_logic logic;
423
424 inc = in->priv;
425
426 if (inc->out_buf->len) {
427 packet.type = SR_DF_LOGIC;
428 packet.payload = &logic;
429 logic.unitsize = (g_slist_length(in->sdi->channels) + 7) / 8;
430 logic.data = inc->out_buf->str;
431 logic.length = inc->out_buf->len;
432 sr_session_send(in->sdi, &packet);
433
434 g_string_truncate(inc->out_buf, 0);
435 }
436}
437
438static void process_record_pi(struct sr_input *in, gsize start)
439{
6d2897e3
SA
440 struct context *inc;
441 uint64_t timestamp, next_timestamp;
442 uint32_t pod_data;
443 char single_payload[12 * 3];
444 GString *buf;
445 int i, pod_count, clk_offset, packet_count, pod;
446 int payload_bit, payload_len, value;
447
448 inc = in->priv;
449 buf = in->buf;
450
451 /*
628dc330
SA
452 * 0x00 u8 timestamp
453 * 0x08 u16 A15..0
454 * 0x0A u16 B15..0
455 * 0x0C u16 C15..0
456 * 0x0E u16 D15..0
457 * 0x10 u16 E15..0
458 * 0x12 u16 F15..0
459 * 0x14 u32 ??
460 * 0x18 u16 J15..0 Not present in 500MHz mode
461 * 0x1A u16 K15..0 Not present in 500MHz mode
462 * 0x1C u16 L15..0 Not present in 500MHz mode
463 * 0x1E u16 M15..0 Not present in 500MHz mode
464 * 0x20 u16 N15..0 Not present in 500MHz mode
465 * 0x22 u16 O15..0 Not present in 500MHz mode
466 * 0x24 u32 ?? Not present in 500MHz mode
467 * 0x28/18 u8 CLKF..A (32=CLKF, .., 1=CLKA)
468 * 0x29/1A u8 CLKO..J (32=CLKO, .., 1=CLKJ) Not present in 500MHz mode
469 * 0x2A/19 u8 ??
470 * 0x2B/1A u8 ??
471 * 0x2C/1B u8 ??
6d2897e3
SA
472 */
473
474 timestamp = RL64(buf->str + start);
475
476 if (inc->record_mode == AD_MODE_500MHZ) {
477 pod_count = 6;
628dc330 478 clk_offset = 0x18;
6d2897e3
SA
479 } else {
480 pod_count = 12;
628dc330 481 clk_offset = 0x28;
6d2897e3
SA
482 }
483
484 payload_bit = 0;
485 payload_len = 0;
486 single_payload[0] = 0;
487
488 for (pod = 0; pod < pod_count; pod++) {
489 if (!inc->pod_status[pod])
490 continue;
491
492 switch (pod) {
493 case 0: /* A */
628dc330 494 pod_data = RL16(buf->str + start + 0x08);
6d2897e3
SA
495 pod_data |= (RL16(buf->str + start + clk_offset) & 1) << 16;
496 break;
497 case 1: /* B */
628dc330 498 pod_data = RL16(buf->str + start + 0x0A);
6d2897e3
SA
499 pod_data |= (RL16(buf->str + start + clk_offset) & 2) << 15;
500 break;
501 case 2: /* C */
628dc330 502 pod_data = RL16(buf->str + start + 0x0C);
6d2897e3
SA
503 pod_data |= (RL16(buf->str + start + clk_offset) & 4) << 14;
504 break;
505 case 3: /* D */
628dc330 506 pod_data = RL16(buf->str + start + 0x0E);
6d2897e3
SA
507 pod_data |= (RL16(buf->str + start + clk_offset) & 8) << 13;
508 break;
509 case 4: /* E */
628dc330 510 pod_data = RL16(buf->str + start + 0x10);
6d2897e3
SA
511 pod_data |= (RL16(buf->str + start + clk_offset) & 16) << 12;
512 break;
513 case 5: /* F */
628dc330 514 pod_data = RL16(buf->str + start + 0x12);
6d2897e3
SA
515 pod_data |= (RL16(buf->str + start + clk_offset) & 32) << 11;
516 break;
517 case 6: /* J */
628dc330
SA
518 pod_data = RL16(buf->str + start + 0x18);
519 pod_data |= (RL16(buf->str + start + 0x29) & 1) << 16;
6d2897e3
SA
520 break;
521 case 7: /* K */
628dc330
SA
522 pod_data = RL16(buf->str + start + 0x1A);
523 pod_data |= (RL16(buf->str + start + 0x29) & 2) << 15;
6d2897e3
SA
524 break;
525 case 8: /* L */
628dc330
SA
526 pod_data = RL16(buf->str + start + 0x1C);
527 pod_data |= (RL16(buf->str + start + 0x29) & 4) << 14;
6d2897e3
SA
528 break;
529 case 9: /* M */
628dc330
SA
530 pod_data = RL16(buf->str + start + 0x1E);
531 pod_data |= (RL16(buf->str + start + 0x29) & 8) << 13;
6d2897e3
SA
532 break;
533 case 10: /* N */
628dc330
SA
534 pod_data = RL16(buf->str + start + 0x20);
535 pod_data |= (RL16(buf->str + start + 0x29) & 16) << 12;
6d2897e3
SA
536 break;
537 case 11: /* O */
628dc330
SA
538 pod_data = RL16(buf->str + start + 0x22);
539 pod_data |= (RL16(buf->str + start + 0x29) & 32) << 11;
6d2897e3
SA
540 break;
541 default:
35037b1d 542 pod_data = 0;
6d2897e3
SA
543 sr_err("Don't know how to obtain data for pod %d.", pod);
544 }
545
546 for (i = 0; i < 17; i++) {
547 value = (pod_data >> i) & 1;
548 single_payload[payload_len] |= value << payload_bit;
549
550 payload_bit++;
551 if (payload_bit > 7) {
552 payload_bit = 0;
553 payload_len++;
554 single_payload[payload_len] = 0;
555 }
556 }
557 }
558
559 /* Make sure that payload_len accounts for any incomplete bytes used. */
560 if (payload_bit)
561 payload_len++;
562
563 i = (g_slist_length(in->sdi->channels) + 7) / 8;
564 if (payload_len != i) {
565 sr_err("Payload unit size is %d but should be %d!", payload_len, i);
566 return;
567 }
568
a5be5d5b 569 if (timestamp == inc->trigger_timestamp && !inc->trigger_sent) {
6d2897e3
SA
570 sr_dbg("Trigger @%lf s, record #%d.",
571 timestamp * TIMESTAMP_RESOLUTION, inc->cur_record);
0fa71943 572 std_session_send_df_trigger(in->sdi);
a5be5d5b 573 inc->trigger_sent = TRUE;
6d2897e3
SA
574 }
575
576 /* Is this the last record in the file? */
577 if (inc->cur_record == inc->record_count - 1) {
578 /* It is, so send the last sample data only once. */
579 g_string_append_len(inc->out_buf, single_payload, payload_len);
580 } else {
581 /* It's not, so fill the time gap by sending lots of data. */
582 next_timestamp = RL64(buf->str + start + inc->record_size);
6266deb8 583 packet_count = (int)(next_timestamp - timestamp) / inc->timestamp_scale;
6d2897e3
SA
584
585 /* Make sure we send at least one data set. */
586 if (packet_count == 0)
587 packet_count = 1;
588
589 for (i = 0; i < packet_count; i++)
590 g_string_append_len(inc->out_buf, single_payload, payload_len);
591 }
592
8bc2fa6d 593 if (inc->out_buf->len >= CHUNK_SIZE)
6d2897e3
SA
594 flush_output_buffer(in);
595}
596
597static void process_record_iprobe(struct sr_input *in, gsize start)
598{
6d2897e3
SA
599 struct context *inc;
600 uint64_t timestamp, next_timestamp;
601 char single_payload[3];
602 int i, payload_len, packet_count;
603
604 inc = in->priv;
605
606 /*
628dc330
SA
607 * 0x00 u64 timestamp
608 * 0x08 u16 IP15..0
609 * 0x0A u8 CLK
6d2897e3
SA
610 */
611
612 timestamp = RL64(in->buf->str + start);
628dc330
SA
613 single_payload[0] = R8(in->buf->str + start + 0x08);
614 single_payload[1] = R8(in->buf->str + start + 0x09);
615 single_payload[2] = R8(in->buf->str + start + 0x0A) & 1;
6d2897e3
SA
616 payload_len = 3;
617
a5be5d5b 618 if (timestamp == inc->trigger_timestamp && !inc->trigger_sent) {
6d2897e3
SA
619 sr_dbg("Trigger @%lf s, record #%d.",
620 timestamp * TIMESTAMP_RESOLUTION, inc->cur_record);
0fa71943 621 std_session_send_df_trigger(in->sdi);
a5be5d5b 622 inc->trigger_sent = TRUE;
6d2897e3
SA
623 }
624
625 /* Is this the last record in the file? */
626 if (inc->cur_record == inc->record_count - 1) {
627 /* It is, so send the last sample data only once. */
628 g_string_append_len(inc->out_buf, single_payload, payload_len);
629 } else {
630 /* It's not, so fill the time gap by sending lots of data. */
631 next_timestamp = RL64(in->buf->str + start + inc->record_size);
6266deb8 632 packet_count = (int)(next_timestamp - timestamp) / inc->timestamp_scale;
6d2897e3
SA
633
634 /* Make sure we send at least one data set. */
635 if (packet_count == 0)
636 packet_count = 1;
637
638 for (i = 0; i < packet_count; i++)
639 g_string_append_len(inc->out_buf, single_payload, payload_len);
640 }
641
8bc2fa6d 642 if (inc->out_buf->len >= CHUNK_SIZE)
6d2897e3
SA
643 flush_output_buffer(in);
644}
645
646static void process_practice_token(struct sr_input *in, char *cmd_token)
647{
648 struct context *inc;
649 char **tokens;
650 char chan_suffix[2], chan_name[33];
651 char *s1, *s2;
652 int pod, ch;
653 struct sr_channel *channel;
654
655 inc = in->priv;
656
657 /*
658 * Commands of interest (I may also be IPROBE):
659 *
660 * I.TWIDTH
661 * I.TPREDELAY
662 * I.TDELAY
663 * I.TYSNC.SELECT I.A0 HIGH
664 * NAME.SET <port.chan> <name> <+/-> ...
665 */
666
667 if (!cmd_token)
668 return;
669
670 if (cmd_token[0] == 0)
671 return;
672
673 tokens = g_strsplit(cmd_token, " ", 0);
674
675 if (!tokens)
676 return;
677
678 if (g_strcmp0(tokens[0], "NAME.SET") == 0) {
679 /* Let the user know when the channel has been inverted. */
680 /* This *should* be token #3 but there's an additonal space, making it #4. */
681 chan_suffix[0] = 0;
682 chan_suffix[1] = 0;
683 if (tokens[4]) {
684 if (tokens[4][0] == '-')
685 chan_suffix[0] = '-'; /* This is the way PowerView shows it. */
686 }
687
688 /*
689 * Command is using structure "NAME.SET I.A00 I.XYZ" or
690 * "NAME.SET IP.00 IP.XYZ", depending on the device used.
691 * Let's get strings with the I./IP. from both tokens removed.
692 */
693 s1 = g_strstr_len(tokens[1], -1, ".") + 1;
694 s2 = g_strstr_len(tokens[2], -1, ".") + 1;
695
696 if (g_strcmp0(s1, "CLK") == 0) {
697 /* CLK for iprobe */
698 pod = 0;
699 ch = 16;
700 } else if ((strlen(s1) == 4) && g_ascii_isupper(s1[3])) {
701 /* CLKA/B/J/K for PowerIntegrator */
702 pod = s1[3] - (char)'A';
703 ch = 16;
704 } else if (g_ascii_isupper(s1[0])) {
705 /* A00 for PowerIntegrator */
706 pod = s1[0] - (char)'A';
707 ch = atoi(s1 + 1);
708 } else {
709 /* 00 for iprobe */
710 pod = 0;
711 ch = atoi(s1);
712 }
713
714 channel = inc->channels[pod][ch];
715 g_snprintf(chan_name, sizeof(chan_name), "%s%s", s2, chan_suffix);
716
717 sr_dbg("Changing channel name for %s to %s.", s1, chan_name);
718 sr_dev_channel_name_set(channel, chan_name);
719 }
720
721 g_strfreev(tokens);
722}
723
724static void process_practice(struct sr_input *in)
725{
726 char delimiter[3];
727 char **tokens, *token;
728 int i;
729
730 /* Gather all input data until we see the end marker. */
731 if (in->buf->str[in->buf->len - 1] != 0x29)
732 return;
733
734 delimiter[0] = 0x0A;
735 delimiter[1] = ' ';
736 delimiter[2] = 0;
737
738 tokens = g_strsplit(in->buf->str, delimiter, 0);
739
740 /* Special case: first token contains the start marker, too. Skip it. */
741 token = tokens[0];
742 for (i = 0; token[i]; i++) {
743 if (token[i] == ' ')
744 process_practice_token(in, token + i + 1);
745 }
746
747 for (i = 1; tokens[i]; i++)
748 process_practice_token(in, tokens[i]);
749
750 g_strfreev(tokens);
751
752 g_string_erase(in->buf, 0, in->buf->len);
753}
754
755static int process_buffer(struct sr_input *in)
756{
757 struct context *inc;
758 int i, chunk_size, res;
759
760 inc = in->priv;
761
762 if (!inc->header_read) {
763 res = process_header(in->buf, inc);
7ed4ae63 764 g_string_erase(in->buf, 0, inc->header_size);
6d2897e3
SA
765 if (res != SR_OK)
766 return res;
767 }
768
769 if (!inc->meta_sent) {
bee2b016 770 std_session_send_df_header(in->sdi);
6d2897e3
SA
771 send_metadata(in);
772 }
773
774 if (!inc->records_read) {
775 /* Cut off at a multiple of the record size. */
776 chunk_size = ((in->buf->len) / inc->record_size) * inc->record_size;
777
778 /* There needs to be at least one more record process_record() can peek into. */
779 chunk_size -= inc->record_size;
780
781 for (i = 0; (i < chunk_size) && (!inc->records_read); i += inc->record_size) {
782 switch (inc->device) {
783 case AD_DEVICE_PI:
784 process_record_pi(in, i);
785 break;
786 case AD_DEVICE_IPROBE:
787 process_record_iprobe(in, i);
788 break;
789 default:
790 sr_err("Trying to process records for unknown device!");
791 return SR_ERR;
792 }
793
794 inc->cur_record++;
795 if (inc->cur_record == inc->record_count)
796 inc->records_read = TRUE;
797 }
798
799 g_string_erase(in->buf, 0, i);
800 }
801
802 if (inc->records_read) {
803 /* Read practice commands that configure the setup. */
804 process_practice(in);
805 }
806
807 return SR_OK;
808}
809
810static int receive(struct sr_input *in, GString *buf)
811{
812 g_string_append_len(in->buf, buf->str, buf->len);
813
814 if (!in->sdi_ready) {
815 /* sdi is ready, notify frontend. */
816 in->sdi_ready = TRUE;
817 return SR_OK;
818 }
819
820 return process_buffer(in);
821}
822
823static int end(struct sr_input *in)
824{
825 struct context *inc;
6d2897e3
SA
826 int ret;
827
828 inc = in->priv;
829
830 if (in->sdi_ready)
831 ret = process_buffer(in);
832 else
833 ret = SR_OK;
834
835 flush_output_buffer(in);
836
3be42bc2 837 if (inc->meta_sent)
bee2b016 838 std_session_send_df_end(in->sdi);
6d2897e3
SA
839
840 return ret;
841}
842
87616181
SA
843static int reset(struct sr_input *in)
844{
845 struct context *inc = in->priv;
846
847 inc->meta_sent = FALSE;
848 inc->header_read = FALSE;
849 inc->records_read = FALSE;
850 inc->trigger_sent = FALSE;
851 inc->cur_record = 0;
852
853 g_string_truncate(in->buf, 0);
854
855 return SR_OK;
856}
857
6d2897e3
SA
858static struct sr_option options[] = {
859 { "podA", "Import pod A / iprobe",
860 "Create channels and data for pod A / iprobe", NULL, NULL },
861
862 { "podB", "Import pod B", "Create channels and data for pod B", NULL, NULL },
863 { "podC", "Import pod C", "Create channels and data for pod C", NULL, NULL },
864 { "podD", "Import pod D", "Create channels and data for pod D", NULL, NULL },
865 { "podE", "Import pod E", "Create channels and data for pod E", NULL, NULL },
866 { "podF", "Import pod F", "Create channels and data for pod F", NULL, NULL },
867 { "podJ", "Import pod J", "Create channels and data for pod J", NULL, NULL },
868 { "podK", "Import pod K", "Create channels and data for pod K", NULL, NULL },
869 { "podL", "Import pod L", "Create channels and data for pod L", NULL, NULL },
870 { "podM", "Import pod M", "Create channels and data for pod M", NULL, NULL },
871 { "podN", "Import pod N", "Create channels and data for pod N", NULL, NULL },
872 { "podO", "Import pod O", "Create channels and data for pod O", NULL, NULL },
6266deb8 873
867293a1 874 { "samplerate", "Reduced sample rate (MHz)", "Reduce the original sample rate of 12.8 GHz to the specified sample rate in MHz", NULL, NULL },
6266deb8 875
6d2897e3
SA
876 ALL_ZERO
877};
878
879static const struct sr_option *get_options(void)
880{
881 if (!options[0].def) {
882 options[0].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
883 options[1].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
884 options[2].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
885 options[3].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
886 options[4].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
887 options[5].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
888 options[6].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
889 options[7].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
890 options[8].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
891 options[9].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
892 options[10].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
893 options[11].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
77ee3168 894 options[12].def = g_variant_ref_sink(g_variant_new_uint64(DEFAULT_SAMPLERATE));
6d2897e3
SA
895 }
896
897 return options;
898}
899
900SR_PRIV struct sr_input_module input_trace32_ad = {
901 .id = "trace32_ad",
902 .name = "Trace32_ad",
903 .desc = "Lauterbach Trace32 logic analyzer data",
904 .exts = (const char*[]){"ad", NULL},
905 .options = get_options,
906 .metadata = { SR_INPUT_META_HEADER | SR_INPUT_META_REQUIRED },
907 .format_match = format_match,
908 .init = init,
909 .receive = receive,
910 .end = end,
87616181 911 .reset = reset,
6d2897e3 912};