]> sigrok.org Git - libsigrok.git/blame - src/input/trace32_ad.c
Use std_session_send_df_trigger() where possible.
[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 *
166 g_variant_get_uint32(g_hash_table_lookup(options, "samplerate"));
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{
410 struct sr_datafeed_packet packet;
411 struct sr_datafeed_meta meta;
412 struct sr_config *src;
413 struct context *inc;
414
6266deb8
SA
415 inc = in->priv;
416
6d2897e3
SA
417 packet.type = SR_DF_META;
418 packet.payload = &meta;
6266deb8 419 src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(inc->samplerate));
6d2897e3
SA
420 meta.config = g_slist_append(NULL, src);
421 sr_session_send(in->sdi, &packet);
422 g_slist_free(meta.config);
423 sr_config_free(src);
424
6d2897e3
SA
425 inc->meta_sent = TRUE;
426}
427
428static void flush_output_buffer(struct sr_input *in)
429{
430 struct context *inc;
431 struct sr_datafeed_packet packet;
432 struct sr_datafeed_logic logic;
433
434 inc = in->priv;
435
436 if (inc->out_buf->len) {
437 packet.type = SR_DF_LOGIC;
438 packet.payload = &logic;
439 logic.unitsize = (g_slist_length(in->sdi->channels) + 7) / 8;
440 logic.data = inc->out_buf->str;
441 logic.length = inc->out_buf->len;
442 sr_session_send(in->sdi, &packet);
443
444 g_string_truncate(inc->out_buf, 0);
445 }
446}
447
448static void process_record_pi(struct sr_input *in, gsize start)
449{
6d2897e3
SA
450 struct context *inc;
451 uint64_t timestamp, next_timestamp;
452 uint32_t pod_data;
453 char single_payload[12 * 3];
454 GString *buf;
455 int i, pod_count, clk_offset, packet_count, pod;
456 int payload_bit, payload_len, value;
457
458 inc = in->priv;
459 buf = in->buf;
460
461 /*
628dc330
SA
462 * 0x00 u8 timestamp
463 * 0x08 u16 A15..0
464 * 0x0A u16 B15..0
465 * 0x0C u16 C15..0
466 * 0x0E u16 D15..0
467 * 0x10 u16 E15..0
468 * 0x12 u16 F15..0
469 * 0x14 u32 ??
470 * 0x18 u16 J15..0 Not present in 500MHz mode
471 * 0x1A u16 K15..0 Not present in 500MHz mode
472 * 0x1C u16 L15..0 Not present in 500MHz mode
473 * 0x1E u16 M15..0 Not present in 500MHz mode
474 * 0x20 u16 N15..0 Not present in 500MHz mode
475 * 0x22 u16 O15..0 Not present in 500MHz mode
476 * 0x24 u32 ?? Not present in 500MHz mode
477 * 0x28/18 u8 CLKF..A (32=CLKF, .., 1=CLKA)
478 * 0x29/1A u8 CLKO..J (32=CLKO, .., 1=CLKJ) Not present in 500MHz mode
479 * 0x2A/19 u8 ??
480 * 0x2B/1A u8 ??
481 * 0x2C/1B u8 ??
6d2897e3
SA
482 */
483
484 timestamp = RL64(buf->str + start);
485
486 if (inc->record_mode == AD_MODE_500MHZ) {
487 pod_count = 6;
628dc330 488 clk_offset = 0x18;
6d2897e3
SA
489 } else {
490 pod_count = 12;
628dc330 491 clk_offset = 0x28;
6d2897e3
SA
492 }
493
494 payload_bit = 0;
495 payload_len = 0;
496 single_payload[0] = 0;
497
498 for (pod = 0; pod < pod_count; pod++) {
499 if (!inc->pod_status[pod])
500 continue;
501
502 switch (pod) {
503 case 0: /* A */
628dc330 504 pod_data = RL16(buf->str + start + 0x08);
6d2897e3
SA
505 pod_data |= (RL16(buf->str + start + clk_offset) & 1) << 16;
506 break;
507 case 1: /* B */
628dc330 508 pod_data = RL16(buf->str + start + 0x0A);
6d2897e3
SA
509 pod_data |= (RL16(buf->str + start + clk_offset) & 2) << 15;
510 break;
511 case 2: /* C */
628dc330 512 pod_data = RL16(buf->str + start + 0x0C);
6d2897e3
SA
513 pod_data |= (RL16(buf->str + start + clk_offset) & 4) << 14;
514 break;
515 case 3: /* D */
628dc330 516 pod_data = RL16(buf->str + start + 0x0E);
6d2897e3
SA
517 pod_data |= (RL16(buf->str + start + clk_offset) & 8) << 13;
518 break;
519 case 4: /* E */
628dc330 520 pod_data = RL16(buf->str + start + 0x10);
6d2897e3
SA
521 pod_data |= (RL16(buf->str + start + clk_offset) & 16) << 12;
522 break;
523 case 5: /* F */
628dc330 524 pod_data = RL16(buf->str + start + 0x12);
6d2897e3
SA
525 pod_data |= (RL16(buf->str + start + clk_offset) & 32) << 11;
526 break;
527 case 6: /* J */
628dc330
SA
528 pod_data = RL16(buf->str + start + 0x18);
529 pod_data |= (RL16(buf->str + start + 0x29) & 1) << 16;
6d2897e3
SA
530 break;
531 case 7: /* K */
628dc330
SA
532 pod_data = RL16(buf->str + start + 0x1A);
533 pod_data |= (RL16(buf->str + start + 0x29) & 2) << 15;
6d2897e3
SA
534 break;
535 case 8: /* L */
628dc330
SA
536 pod_data = RL16(buf->str + start + 0x1C);
537 pod_data |= (RL16(buf->str + start + 0x29) & 4) << 14;
6d2897e3
SA
538 break;
539 case 9: /* M */
628dc330
SA
540 pod_data = RL16(buf->str + start + 0x1E);
541 pod_data |= (RL16(buf->str + start + 0x29) & 8) << 13;
6d2897e3
SA
542 break;
543 case 10: /* N */
628dc330
SA
544 pod_data = RL16(buf->str + start + 0x20);
545 pod_data |= (RL16(buf->str + start + 0x29) & 16) << 12;
6d2897e3
SA
546 break;
547 case 11: /* O */
628dc330
SA
548 pod_data = RL16(buf->str + start + 0x22);
549 pod_data |= (RL16(buf->str + start + 0x29) & 32) << 11;
6d2897e3
SA
550 break;
551 default:
35037b1d 552 pod_data = 0;
6d2897e3
SA
553 sr_err("Don't know how to obtain data for pod %d.", pod);
554 }
555
556 for (i = 0; i < 17; i++) {
557 value = (pod_data >> i) & 1;
558 single_payload[payload_len] |= value << payload_bit;
559
560 payload_bit++;
561 if (payload_bit > 7) {
562 payload_bit = 0;
563 payload_len++;
564 single_payload[payload_len] = 0;
565 }
566 }
567 }
568
569 /* Make sure that payload_len accounts for any incomplete bytes used. */
570 if (payload_bit)
571 payload_len++;
572
573 i = (g_slist_length(in->sdi->channels) + 7) / 8;
574 if (payload_len != i) {
575 sr_err("Payload unit size is %d but should be %d!", payload_len, i);
576 return;
577 }
578
a5be5d5b 579 if (timestamp == inc->trigger_timestamp && !inc->trigger_sent) {
6d2897e3
SA
580 sr_dbg("Trigger @%lf s, record #%d.",
581 timestamp * TIMESTAMP_RESOLUTION, inc->cur_record);
0fa71943 582 std_session_send_df_trigger(in->sdi);
a5be5d5b 583 inc->trigger_sent = TRUE;
6d2897e3
SA
584 }
585
586 /* Is this the last record in the file? */
587 if (inc->cur_record == inc->record_count - 1) {
588 /* It is, so send the last sample data only once. */
589 g_string_append_len(inc->out_buf, single_payload, payload_len);
590 } else {
591 /* It's not, so fill the time gap by sending lots of data. */
592 next_timestamp = RL64(buf->str + start + inc->record_size);
6266deb8 593 packet_count = (int)(next_timestamp - timestamp) / inc->timestamp_scale;
6d2897e3
SA
594
595 /* Make sure we send at least one data set. */
596 if (packet_count == 0)
597 packet_count = 1;
598
599 for (i = 0; i < packet_count; i++)
600 g_string_append_len(inc->out_buf, single_payload, payload_len);
601 }
602
8bc2fa6d 603 if (inc->out_buf->len >= CHUNK_SIZE)
6d2897e3
SA
604 flush_output_buffer(in);
605}
606
607static void process_record_iprobe(struct sr_input *in, gsize start)
608{
6d2897e3
SA
609 struct context *inc;
610 uint64_t timestamp, next_timestamp;
611 char single_payload[3];
612 int i, payload_len, packet_count;
613
614 inc = in->priv;
615
616 /*
628dc330
SA
617 * 0x00 u64 timestamp
618 * 0x08 u16 IP15..0
619 * 0x0A u8 CLK
6d2897e3
SA
620 */
621
622 timestamp = RL64(in->buf->str + start);
628dc330
SA
623 single_payload[0] = R8(in->buf->str + start + 0x08);
624 single_payload[1] = R8(in->buf->str + start + 0x09);
625 single_payload[2] = R8(in->buf->str + start + 0x0A) & 1;
6d2897e3
SA
626 payload_len = 3;
627
a5be5d5b 628 if (timestamp == inc->trigger_timestamp && !inc->trigger_sent) {
6d2897e3
SA
629 sr_dbg("Trigger @%lf s, record #%d.",
630 timestamp * TIMESTAMP_RESOLUTION, inc->cur_record);
0fa71943 631 std_session_send_df_trigger(in->sdi);
a5be5d5b 632 inc->trigger_sent = TRUE;
6d2897e3
SA
633 }
634
635 /* Is this the last record in the file? */
636 if (inc->cur_record == inc->record_count - 1) {
637 /* It is, so send the last sample data only once. */
638 g_string_append_len(inc->out_buf, single_payload, payload_len);
639 } else {
640 /* It's not, so fill the time gap by sending lots of data. */
641 next_timestamp = RL64(in->buf->str + start + inc->record_size);
6266deb8 642 packet_count = (int)(next_timestamp - timestamp) / inc->timestamp_scale;
6d2897e3
SA
643
644 /* Make sure we send at least one data set. */
645 if (packet_count == 0)
646 packet_count = 1;
647
648 for (i = 0; i < packet_count; i++)
649 g_string_append_len(inc->out_buf, single_payload, payload_len);
650 }
651
8bc2fa6d 652 if (inc->out_buf->len >= CHUNK_SIZE)
6d2897e3
SA
653 flush_output_buffer(in);
654}
655
656static void process_practice_token(struct sr_input *in, char *cmd_token)
657{
658 struct context *inc;
659 char **tokens;
660 char chan_suffix[2], chan_name[33];
661 char *s1, *s2;
662 int pod, ch;
663 struct sr_channel *channel;
664
665 inc = in->priv;
666
667 /*
668 * Commands of interest (I may also be IPROBE):
669 *
670 * I.TWIDTH
671 * I.TPREDELAY
672 * I.TDELAY
673 * I.TYSNC.SELECT I.A0 HIGH
674 * NAME.SET <port.chan> <name> <+/-> ...
675 */
676
677 if (!cmd_token)
678 return;
679
680 if (cmd_token[0] == 0)
681 return;
682
683 tokens = g_strsplit(cmd_token, " ", 0);
684
685 if (!tokens)
686 return;
687
688 if (g_strcmp0(tokens[0], "NAME.SET") == 0) {
689 /* Let the user know when the channel has been inverted. */
690 /* This *should* be token #3 but there's an additonal space, making it #4. */
691 chan_suffix[0] = 0;
692 chan_suffix[1] = 0;
693 if (tokens[4]) {
694 if (tokens[4][0] == '-')
695 chan_suffix[0] = '-'; /* This is the way PowerView shows it. */
696 }
697
698 /*
699 * Command is using structure "NAME.SET I.A00 I.XYZ" or
700 * "NAME.SET IP.00 IP.XYZ", depending on the device used.
701 * Let's get strings with the I./IP. from both tokens removed.
702 */
703 s1 = g_strstr_len(tokens[1], -1, ".") + 1;
704 s2 = g_strstr_len(tokens[2], -1, ".") + 1;
705
706 if (g_strcmp0(s1, "CLK") == 0) {
707 /* CLK for iprobe */
708 pod = 0;
709 ch = 16;
710 } else if ((strlen(s1) == 4) && g_ascii_isupper(s1[3])) {
711 /* CLKA/B/J/K for PowerIntegrator */
712 pod = s1[3] - (char)'A';
713 ch = 16;
714 } else if (g_ascii_isupper(s1[0])) {
715 /* A00 for PowerIntegrator */
716 pod = s1[0] - (char)'A';
717 ch = atoi(s1 + 1);
718 } else {
719 /* 00 for iprobe */
720 pod = 0;
721 ch = atoi(s1);
722 }
723
724 channel = inc->channels[pod][ch];
725 g_snprintf(chan_name, sizeof(chan_name), "%s%s", s2, chan_suffix);
726
727 sr_dbg("Changing channel name for %s to %s.", s1, chan_name);
728 sr_dev_channel_name_set(channel, chan_name);
729 }
730
731 g_strfreev(tokens);
732}
733
734static void process_practice(struct sr_input *in)
735{
736 char delimiter[3];
737 char **tokens, *token;
738 int i;
739
740 /* Gather all input data until we see the end marker. */
741 if (in->buf->str[in->buf->len - 1] != 0x29)
742 return;
743
744 delimiter[0] = 0x0A;
745 delimiter[1] = ' ';
746 delimiter[2] = 0;
747
748 tokens = g_strsplit(in->buf->str, delimiter, 0);
749
750 /* Special case: first token contains the start marker, too. Skip it. */
751 token = tokens[0];
752 for (i = 0; token[i]; i++) {
753 if (token[i] == ' ')
754 process_practice_token(in, token + i + 1);
755 }
756
757 for (i = 1; tokens[i]; i++)
758 process_practice_token(in, tokens[i]);
759
760 g_strfreev(tokens);
761
762 g_string_erase(in->buf, 0, in->buf->len);
763}
764
765static int process_buffer(struct sr_input *in)
766{
767 struct context *inc;
768 int i, chunk_size, res;
769
770 inc = in->priv;
771
772 if (!inc->header_read) {
773 res = process_header(in->buf, inc);
7ed4ae63 774 g_string_erase(in->buf, 0, inc->header_size);
6d2897e3
SA
775 if (res != SR_OK)
776 return res;
777 }
778
779 if (!inc->meta_sent) {
bee2b016 780 std_session_send_df_header(in->sdi);
6d2897e3
SA
781 send_metadata(in);
782 }
783
784 if (!inc->records_read) {
785 /* Cut off at a multiple of the record size. */
786 chunk_size = ((in->buf->len) / inc->record_size) * inc->record_size;
787
788 /* There needs to be at least one more record process_record() can peek into. */
789 chunk_size -= inc->record_size;
790
791 for (i = 0; (i < chunk_size) && (!inc->records_read); i += inc->record_size) {
792 switch (inc->device) {
793 case AD_DEVICE_PI:
794 process_record_pi(in, i);
795 break;
796 case AD_DEVICE_IPROBE:
797 process_record_iprobe(in, i);
798 break;
799 default:
800 sr_err("Trying to process records for unknown device!");
801 return SR_ERR;
802 }
803
804 inc->cur_record++;
805 if (inc->cur_record == inc->record_count)
806 inc->records_read = TRUE;
807 }
808
809 g_string_erase(in->buf, 0, i);
810 }
811
812 if (inc->records_read) {
813 /* Read practice commands that configure the setup. */
814 process_practice(in);
815 }
816
817 return SR_OK;
818}
819
820static int receive(struct sr_input *in, GString *buf)
821{
822 g_string_append_len(in->buf, buf->str, buf->len);
823
824 if (!in->sdi_ready) {
825 /* sdi is ready, notify frontend. */
826 in->sdi_ready = TRUE;
827 return SR_OK;
828 }
829
830 return process_buffer(in);
831}
832
833static int end(struct sr_input *in)
834{
835 struct context *inc;
6d2897e3
SA
836 int ret;
837
838 inc = in->priv;
839
840 if (in->sdi_ready)
841 ret = process_buffer(in);
842 else
843 ret = SR_OK;
844
845 flush_output_buffer(in);
846
3be42bc2 847 if (inc->meta_sent)
bee2b016 848 std_session_send_df_end(in->sdi);
6d2897e3
SA
849
850 return ret;
851}
852
87616181
SA
853static int reset(struct sr_input *in)
854{
855 struct context *inc = in->priv;
856
857 inc->meta_sent = FALSE;
858 inc->header_read = FALSE;
859 inc->records_read = FALSE;
860 inc->trigger_sent = FALSE;
861 inc->cur_record = 0;
862
863 g_string_truncate(in->buf, 0);
864
865 return SR_OK;
866}
867
6d2897e3
SA
868static struct sr_option options[] = {
869 { "podA", "Import pod A / iprobe",
870 "Create channels and data for pod A / iprobe", NULL, NULL },
871
872 { "podB", "Import pod B", "Create channels and data for pod B", NULL, NULL },
873 { "podC", "Import pod C", "Create channels and data for pod C", NULL, NULL },
874 { "podD", "Import pod D", "Create channels and data for pod D", NULL, NULL },
875 { "podE", "Import pod E", "Create channels and data for pod E", NULL, NULL },
876 { "podF", "Import pod F", "Create channels and data for pod F", NULL, NULL },
877 { "podJ", "Import pod J", "Create channels and data for pod J", NULL, NULL },
878 { "podK", "Import pod K", "Create channels and data for pod K", NULL, NULL },
879 { "podL", "Import pod L", "Create channels and data for pod L", NULL, NULL },
880 { "podM", "Import pod M", "Create channels and data for pod M", NULL, NULL },
881 { "podN", "Import pod N", "Create channels and data for pod N", NULL, NULL },
882 { "podO", "Import pod O", "Create channels and data for pod O", NULL, NULL },
6266deb8 883
867293a1 884 { "samplerate", "Reduced sample rate (MHz)", "Reduce the original sample rate of 12.8 GHz to the specified sample rate in MHz", NULL, NULL },
6266deb8 885
6d2897e3
SA
886 ALL_ZERO
887};
888
889static const struct sr_option *get_options(void)
890{
891 if (!options[0].def) {
892 options[0].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
893 options[1].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
894 options[2].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
895 options[3].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
896 options[4].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
897 options[5].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
898 options[6].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
899 options[7].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
900 options[8].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
901 options[9].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
902 options[10].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
903 options[11].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
6266deb8 904 options[12].def = g_variant_ref_sink(g_variant_new_uint32(DEFAULT_SAMPLERATE));
6d2897e3
SA
905 }
906
907 return options;
908}
909
910SR_PRIV struct sr_input_module input_trace32_ad = {
911 .id = "trace32_ad",
912 .name = "Trace32_ad",
913 .desc = "Lauterbach Trace32 logic analyzer data",
914 .exts = (const char*[]){"ad", NULL},
915 .options = get_options,
916 .metadata = { SR_INPUT_META_HEADER | SR_INPUT_META_REQUIRED },
917 .format_match = format_match,
918 .init = init,
919 .receive = receive,
920 .end = end,
87616181 921 .reset = reset,
6d2897e3 922};