]> sigrok.org Git - libsigrok.git/blame - src/input/trace32_ad.c
Factor out std_session_send_df_end() helper.
[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
48#define MAX_CHUNK_SIZE 4096
49#define OUTBUF_FLUSH_SIZE 10240
50#define MAX_POD_COUNT 12
51#define HEADER_SIZE 80
52
53#define TIMESTAMP_RESOLUTION ((double)0.000000000078125) /* 0.078125 ns */
54
55/*
56 * The resolution equals a sampling freq of 12.8 GHz. That's a bit high
57 * for inter-record sample generation, so we scale it down to 200 MHz
58 * for now. That way, the scaling factor becomes 32.
59 */
60#define SAMPLING_FREQ 200000000
61#define TIMESTAMP_SCALE ((1 / TIMESTAMP_RESOLUTION) / SAMPLING_FREQ)
62
63enum {
64 AD_FORMAT_BINHDR = 1, /* Binary header, binary data, textual setup info */
65 AD_FORMAT_TXTHDR /* Textual header, binary data */
66};
67
68enum {
69 AD_DEVICE_PI = 1, /* Data recorded by LA-7940 PowerIntegrator or */
70 /* LA-394x PowerIntegrator II. */
71 AD_DEVICE_IPROBE /* Data recorded by LA-769x PowerTrace II IProbe. */
72 /* Missing file format info for LA-793x ICD PowerProbe */
73 /* Missing file format info for LA-4530 uTrace analog probe */
74};
75
76enum {
77 AD_MODE_250MHZ = 0,
78 AD_MODE_500MHZ = 1
79};
80
81enum {
82 AD_COMPR_NONE = 0, /* File created with /NOCOMPRESS */
83 AD_COMPR_QCOMP = 6 /* File created with /COMPRESS or /QUICKCOMPRESS */
84};
85
86struct context {
87 gboolean meta_sent;
a5be5d5b 88 gboolean header_read, records_read, trigger_sent;
6d2897e3
SA
89 char format, device, record_mode, compression;
90 char pod_status[MAX_POD_COUNT];
91 struct sr_channel *channels[MAX_POD_COUNT][17]; /* 16 + CLK */
92 uint64_t trigger_timestamp;
93 uint32_t record_size, record_count, cur_record;
94 int32_t last_record;
95 GString *out_buf;
96};
97
98static int process_header(GString *buf, struct context *inc);
99static void create_channels(struct sr_input *in);
100
101static char get_pod_name_from_id(int id)
102{
103 switch (id) {
104 case 0: return 'A';
105 case 1: return 'B';
106 case 2: return 'C';
107 case 3: return 'D';
108 case 4: return 'E';
109 case 5: return 'F';
110 case 6: return 'J';
111 case 7: return 'K';
112 case 8: return 'L';
113 case 9: return 'M';
114 case 10: return 'N';
115 case 11: return 'O';
116 default:
117 sr_err("get_pod_name_from_id() called with invalid ID %d!", id);
118 }
119 return 'X';
120}
121
122static int init(struct sr_input *in, GHashTable *options)
123{
124 struct context *inc;
125 int pod;
126 char id[17];
127
128 in->sdi = g_malloc0(sizeof(struct sr_dev_inst));
129 in->priv = g_malloc0(sizeof(struct context));
130
131 inc = in->priv;
132
133 /* Enable the pods the user chose to see. */
134 for (pod = 0; pod < MAX_POD_COUNT; pod++) {
135 g_snprintf(id, sizeof(id), "pod%c", get_pod_name_from_id(pod));
136 if (g_variant_get_boolean(g_hash_table_lookup(options, id)))
137 inc->pod_status[pod] = 1;
138 }
139
140 create_channels(in);
141 if (g_slist_length(in->sdi->channels) == 0) {
142 sr_err("No pods were selected and thus no channels created, aborting.");
143 g_free(in->priv);
144 g_free(in->sdi);
145 return SR_ERR;
146 }
147
148 inc->out_buf = g_string_sized_new(OUTBUF_FLUSH_SIZE);
149
150 return SR_OK;
151}
152
153static int format_match(GHashTable *metadata)
154{
155 GString *buf;
156
157 buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
158
159 return process_header(buf, NULL);
160}
161
162static int process_header(GString *buf, struct context *inc)
163{
164 char *format_name, *format_name_sig;
165 int i, record_size, device_id;
166
167 /*
168 * 00-31 (0x00-1F) file format name
169 * 32-39 (0x20-27) trigger timestamp u64
170 * 40-47 (0x28-2F) unused
171 * 48 (0x30) compression
172 * 49-53 (0x31-35) ??
173 * 50 (0x32) 0x00 (PI), 0x01 (iprobe)
174 * 54 (0x36) 0x08 (PI 250/500), 0x0A (iprobe 250)
175 * 55 (0x37) 0x00 (250), 0x01 (500)
176 * 56 (0x38) record size u8
177 * 57-59 (0x39-3B) const 0x00
178 * 60-63 (0x3C-3F) number of records u32
179 * 64-67 (0x40-43) id of last record s32
180 * 68-77 (0x44-4D) ??
181 * 71 (0x47) const 0x80=128
182 * 72 (0x48) const 0x01
183 * 78-79 (0x4E-4F) ??
184 */
185
186 /* Note: inc is off-limits until we check whether it's a valid pointer. */
187
188 format_name = g_strndup(buf->str, 32);
189
190 /* File format name ends on 0x20/0x1A, let's remove both. */
191 for (i = 1; i < 31; i++) {
192 if (format_name[i] == 0x1A) {
193 format_name[i - 1] = 0;
194 format_name[i] = 0;
195 }
196 }
197 g_strchomp(format_name); /* This is for additional padding spaces. */
198
199 format_name_sig = g_strndup(format_name, 5);
200
201 /* Desired file formats either start with digit+space or "trace32". */
202 if (g_strcmp0(format_name_sig, "trace32")) {
203 if (inc)
204 inc->format = AD_FORMAT_BINHDR;
205 } else if (g_ascii_isdigit(format_name[0]) && (format_name[1] == 0x20)) {
206 if (inc)
207 inc->format = AD_FORMAT_TXTHDR;
208 g_free(format_name_sig);
209 g_free(format_name);
210 sr_err("This format isn't implemented yet, aborting.");
211 return SR_ERR;
212 } else {
213 g_free(format_name_sig);
214 g_free(format_name);
215 sr_err("Don't know this file format, aborting.");
216 return SR_ERR;
217 }
218
219 sr_dbg("File says it's \"%s\"", format_name);
220
221 record_size = R8(buf->str + 56);
222 device_id = 0;
223
224 if (g_strcmp0(format_name, "trace32 power integrator data") == 0) {
225 if (record_size == 28 || record_size == 45)
226 device_id = AD_DEVICE_PI;
227 } else if (g_strcmp0(format_name, "trace32 iprobe data") == 0) {
228 if (record_size == 11)
229 device_id = AD_DEVICE_IPROBE;
230 }
231
232 if (!device_id) {
233 g_free(format_name_sig);
234 g_free(format_name);
235 sr_err("Don't know how to handle this file with record size %d.",
236 record_size);
237 return SR_ERR;
238 }
239
240 g_free(format_name_sig);
241 g_free(format_name);
242
243 /* Stop processing the header if we just want to identify the file. */
244 if (!inc)
245 return SR_OK;
246
247 inc->device = device_id;
248 inc->trigger_timestamp = RL64(buf->str + 32);
249 inc->compression = R8(buf->str + 48); /* Maps to the enum. */
250 inc->record_mode = R8(buf->str + 55); /* Maps to the enum. */
251 inc->record_size = record_size;
252 inc->record_count = RL32(buf->str + 60);
253 inc->last_record = RL32S(buf->str + 64);
254
255 sr_dbg("Trigger occured at %lf s.",
256 inc->trigger_timestamp * TIMESTAMP_RESOLUTION);
257 sr_dbg("File contains %d records: first one is %d, last one is %d.",
258 inc->record_count, (inc->last_record - inc->record_count + 1),
259 inc->last_record);
260
261 /* Check if we can work with this compression. */
262 if (inc->compression != AD_COMPR_NONE) {
263 sr_err("File uses unsupported compression (0x%02X), can't continue.",
264 inc->compression);
265 return SR_ERR;
266 }
267
268 inc->header_read = TRUE;
269
270 return SR_OK;
271}
272
273static void create_channels(struct sr_input *in)
274{
275 struct context *inc;
276 int pod, channel, chan_id;
277 char name[8];
278
279 inc = in->priv;
280 chan_id = 0;
281
282 for (pod = 0; pod < MAX_POD_COUNT; pod++) {
283 if (!inc->pod_status[pod])
284 continue;
285
286 for (channel = 0; channel < 16; channel++) {
287 snprintf(name, 8, "%c%d", get_pod_name_from_id(pod), channel);
288 inc->channels[pod][channel] =
289 sr_channel_new(in->sdi, chan_id, SR_CHANNEL_LOGIC, TRUE, name);
290 chan_id++;
291 }
292
293 snprintf(name, 8, "CLK%c", get_pod_name_from_id(pod));
294 inc->channels[pod][16] =
295 sr_channel_new(in->sdi, chan_id, SR_CHANNEL_LOGIC, TRUE, name);
296 chan_id++;
297 }
298}
299
300static void send_metadata(struct sr_input *in)
301{
302 struct sr_datafeed_packet packet;
303 struct sr_datafeed_meta meta;
304 struct sr_config *src;
305 struct context *inc;
306
307 packet.type = SR_DF_META;
308 packet.payload = &meta;
309 src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(SAMPLING_FREQ));
310 meta.config = g_slist_append(NULL, src);
311 sr_session_send(in->sdi, &packet);
312 g_slist_free(meta.config);
313 sr_config_free(src);
314
315 inc = in->priv;
316 inc->meta_sent = TRUE;
317}
318
319static void flush_output_buffer(struct sr_input *in)
320{
321 struct context *inc;
322 struct sr_datafeed_packet packet;
323 struct sr_datafeed_logic logic;
324
325 inc = in->priv;
326
327 if (inc->out_buf->len) {
328 packet.type = SR_DF_LOGIC;
329 packet.payload = &logic;
330 logic.unitsize = (g_slist_length(in->sdi->channels) + 7) / 8;
331 logic.data = inc->out_buf->str;
332 logic.length = inc->out_buf->len;
333 sr_session_send(in->sdi, &packet);
334
335 g_string_truncate(inc->out_buf, 0);
336 }
337}
338
339static void process_record_pi(struct sr_input *in, gsize start)
340{
341 struct sr_datafeed_packet packet;
342 struct context *inc;
343 uint64_t timestamp, next_timestamp;
344 uint32_t pod_data;
345 char single_payload[12 * 3];
346 GString *buf;
347 int i, pod_count, clk_offset, packet_count, pod;
348 int payload_bit, payload_len, value;
349
350 inc = in->priv;
351 buf = in->buf;
352
353 /*
354 * 00-07 timestamp
355 * 08-09 A15..0
356 * 10-11 B15..0
357 * 12-13 C15..0
358 * 14-15 D15..0
359 * 16-17 E15..0
360 * 18-19 F15..0
361 * 20-23 ??
362 * 24-25 J15..0 Not present in 500MHz mode
363 * 26-27 K15..0 Not present in 500MHz mode
364 * 28-29 L15..0 Not present in 500MHz mode
365 * 30-31 M15..0 Not present in 500MHz mode
366 * 32-33 N15..0 Not present in 500MHz mode
367 * 34-35 O15..0 Not present in 500MHz mode
368 * 36-39 ?? Not present in 500MHz mode
369 * 40/24 CLKF..A (32=CLKF, .., 1=CLKA)
370 * 41 CLKO..J (32=CLKO, .., 1=CLKJ) Not present in 500MHz mode
371 * 42/25 ??
372 * 43/26 ??
373 * 44/27 ??
374 */
375
376 timestamp = RL64(buf->str + start);
377
378 if (inc->record_mode == AD_MODE_500MHZ) {
379 pod_count = 6;
380 clk_offset = 24;
381 } else {
382 pod_count = 12;
383 clk_offset = 40;
384 }
385
386 payload_bit = 0;
387 payload_len = 0;
388 single_payload[0] = 0;
389
390 for (pod = 0; pod < pod_count; pod++) {
391 if (!inc->pod_status[pod])
392 continue;
393
394 switch (pod) {
395 case 0: /* A */
396 pod_data = RL16(buf->str + start + 8);
397 pod_data |= (RL16(buf->str + start + clk_offset) & 1) << 16;
398 break;
399 case 1: /* B */
400 pod_data = RL16(buf->str + start + 10);
401 pod_data |= (RL16(buf->str + start + clk_offset) & 2) << 15;
402 break;
403 case 2: /* C */
404 pod_data = RL16(buf->str + start + 12);
405 pod_data |= (RL16(buf->str + start + clk_offset) & 4) << 14;
406 break;
407 case 3: /* D */
408 pod_data = RL16(buf->str + start + 14);
409 pod_data |= (RL16(buf->str + start + clk_offset) & 8) << 13;
410 break;
411 case 4: /* E */
412 pod_data = RL16(buf->str + start + 16);
413 pod_data |= (RL16(buf->str + start + clk_offset) & 16) << 12;
414 break;
415 case 5: /* F */
416 pod_data = RL16(buf->str + start + 18);
417 pod_data |= (RL16(buf->str + start + clk_offset) & 32) << 11;
418 break;
419 case 6: /* J */
420 pod_data = RL16(buf->str + start + 24);
421 pod_data |= (RL16(buf->str + start + 41) & 1) << 16;
422 break;
423 case 7: /* K */
424 pod_data = RL16(buf->str + start + 26);
425 pod_data |= (RL16(buf->str + start + 41) & 2) << 15;
426 break;
427 case 8: /* L */
428 pod_data = RL16(buf->str + start + 28);
429 pod_data |= (RL16(buf->str + start + 41) & 4) << 14;
430 break;
431 case 9: /* M */
432 pod_data = RL16(buf->str + start + 30);
433 pod_data |= (RL16(buf->str + start + 41) & 8) << 13;
434 break;
435 case 10: /* N */
436 pod_data = RL16(buf->str + start + 32);
437 pod_data |= (RL16(buf->str + start + 41) & 16) << 12;
438 break;
439 case 11: /* O */
440 pod_data = RL16(buf->str + start + 34);
441 pod_data |= (RL16(buf->str + start + 41) & 32) << 11;
442 break;
443 default:
444 sr_err("Don't know how to obtain data for pod %d.", pod);
445 }
446
447 for (i = 0; i < 17; i++) {
448 value = (pod_data >> i) & 1;
449 single_payload[payload_len] |= value << payload_bit;
450
451 payload_bit++;
452 if (payload_bit > 7) {
453 payload_bit = 0;
454 payload_len++;
455 single_payload[payload_len] = 0;
456 }
457 }
458 }
459
460 /* Make sure that payload_len accounts for any incomplete bytes used. */
461 if (payload_bit)
462 payload_len++;
463
464 i = (g_slist_length(in->sdi->channels) + 7) / 8;
465 if (payload_len != i) {
466 sr_err("Payload unit size is %d but should be %d!", payload_len, i);
467 return;
468 }
469
a5be5d5b 470 if (timestamp == inc->trigger_timestamp && !inc->trigger_sent) {
6d2897e3
SA
471 sr_dbg("Trigger @%lf s, record #%d.",
472 timestamp * TIMESTAMP_RESOLUTION, inc->cur_record);
473
474 packet.type = SR_DF_TRIGGER;
475 packet.payload = NULL;
476 sr_session_send(in->sdi, &packet);
a5be5d5b 477 inc->trigger_sent = TRUE;
6d2897e3
SA
478 }
479
480 /* Is this the last record in the file? */
481 if (inc->cur_record == inc->record_count - 1) {
482 /* It is, so send the last sample data only once. */
483 g_string_append_len(inc->out_buf, single_payload, payload_len);
484 } else {
485 /* It's not, so fill the time gap by sending lots of data. */
486 next_timestamp = RL64(buf->str + start + inc->record_size);
487 packet_count = (int)(next_timestamp - timestamp) / TIMESTAMP_SCALE;
488
489 /* Make sure we send at least one data set. */
490 if (packet_count == 0)
491 packet_count = 1;
492
493 for (i = 0; i < packet_count; i++)
494 g_string_append_len(inc->out_buf, single_payload, payload_len);
495 }
496
497 if (inc->out_buf->len >= OUTBUF_FLUSH_SIZE)
498 flush_output_buffer(in);
499}
500
501static void process_record_iprobe(struct sr_input *in, gsize start)
502{
503 struct sr_datafeed_packet packet;
504 struct context *inc;
505 uint64_t timestamp, next_timestamp;
506 char single_payload[3];
507 int i, payload_len, packet_count;
508
509 inc = in->priv;
510
511 /*
512 * 00-07 timestamp
513 * 08-09 IP15..0
514 * 10 CLK
515 */
516
517 timestamp = RL64(in->buf->str + start);
518 single_payload[0] = R8(in->buf->str + start + 8);
519 single_payload[1] = R8(in->buf->str + start + 9);
520 single_payload[2] = R8(in->buf->str + start + 10) & 1;
521 payload_len = 3;
522
a5be5d5b 523 if (timestamp == inc->trigger_timestamp && !inc->trigger_sent) {
6d2897e3
SA
524 sr_dbg("Trigger @%lf s, record #%d.",
525 timestamp * TIMESTAMP_RESOLUTION, inc->cur_record);
526
527 packet.type = SR_DF_TRIGGER;
528 packet.payload = NULL;
529 sr_session_send(in->sdi, &packet);
a5be5d5b 530 inc->trigger_sent = TRUE;
6d2897e3
SA
531 }
532
533 /* Is this the last record in the file? */
534 if (inc->cur_record == inc->record_count - 1) {
535 /* It is, so send the last sample data only once. */
536 g_string_append_len(inc->out_buf, single_payload, payload_len);
537 } else {
538 /* It's not, so fill the time gap by sending lots of data. */
539 next_timestamp = RL64(in->buf->str + start + inc->record_size);
540 packet_count = (int)(next_timestamp - timestamp) / TIMESTAMP_SCALE;
541
542 /* Make sure we send at least one data set. */
543 if (packet_count == 0)
544 packet_count = 1;
545
546 for (i = 0; i < packet_count; i++)
547 g_string_append_len(inc->out_buf, single_payload, payload_len);
548 }
549
550 if (inc->out_buf->len >= OUTBUF_FLUSH_SIZE)
551 flush_output_buffer(in);
552}
553
554static void process_practice_token(struct sr_input *in, char *cmd_token)
555{
556 struct context *inc;
557 char **tokens;
558 char chan_suffix[2], chan_name[33];
559 char *s1, *s2;
560 int pod, ch;
561 struct sr_channel *channel;
562
563 inc = in->priv;
564
565 /*
566 * Commands of interest (I may also be IPROBE):
567 *
568 * I.TWIDTH
569 * I.TPREDELAY
570 * I.TDELAY
571 * I.TYSNC.SELECT I.A0 HIGH
572 * NAME.SET <port.chan> <name> <+/-> ...
573 */
574
575 if (!cmd_token)
576 return;
577
578 if (cmd_token[0] == 0)
579 return;
580
581 tokens = g_strsplit(cmd_token, " ", 0);
582
583 if (!tokens)
584 return;
585
586 if (g_strcmp0(tokens[0], "NAME.SET") == 0) {
587 /* Let the user know when the channel has been inverted. */
588 /* This *should* be token #3 but there's an additonal space, making it #4. */
589 chan_suffix[0] = 0;
590 chan_suffix[1] = 0;
591 if (tokens[4]) {
592 if (tokens[4][0] == '-')
593 chan_suffix[0] = '-'; /* This is the way PowerView shows it. */
594 }
595
596 /*
597 * Command is using structure "NAME.SET I.A00 I.XYZ" or
598 * "NAME.SET IP.00 IP.XYZ", depending on the device used.
599 * Let's get strings with the I./IP. from both tokens removed.
600 */
601 s1 = g_strstr_len(tokens[1], -1, ".") + 1;
602 s2 = g_strstr_len(tokens[2], -1, ".") + 1;
603
604 if (g_strcmp0(s1, "CLK") == 0) {
605 /* CLK for iprobe */
606 pod = 0;
607 ch = 16;
608 } else if ((strlen(s1) == 4) && g_ascii_isupper(s1[3])) {
609 /* CLKA/B/J/K for PowerIntegrator */
610 pod = s1[3] - (char)'A';
611 ch = 16;
612 } else if (g_ascii_isupper(s1[0])) {
613 /* A00 for PowerIntegrator */
614 pod = s1[0] - (char)'A';
615 ch = atoi(s1 + 1);
616 } else {
617 /* 00 for iprobe */
618 pod = 0;
619 ch = atoi(s1);
620 }
621
622 channel = inc->channels[pod][ch];
623 g_snprintf(chan_name, sizeof(chan_name), "%s%s", s2, chan_suffix);
624
625 sr_dbg("Changing channel name for %s to %s.", s1, chan_name);
626 sr_dev_channel_name_set(channel, chan_name);
627 }
628
629 g_strfreev(tokens);
630}
631
632static void process_practice(struct sr_input *in)
633{
634 char delimiter[3];
635 char **tokens, *token;
636 int i;
637
638 /* Gather all input data until we see the end marker. */
639 if (in->buf->str[in->buf->len - 1] != 0x29)
640 return;
641
642 delimiter[0] = 0x0A;
643 delimiter[1] = ' ';
644 delimiter[2] = 0;
645
646 tokens = g_strsplit(in->buf->str, delimiter, 0);
647
648 /* Special case: first token contains the start marker, too. Skip it. */
649 token = tokens[0];
650 for (i = 0; token[i]; i++) {
651 if (token[i] == ' ')
652 process_practice_token(in, token + i + 1);
653 }
654
655 for (i = 1; tokens[i]; i++)
656 process_practice_token(in, tokens[i]);
657
658 g_strfreev(tokens);
659
660 g_string_erase(in->buf, 0, in->buf->len);
661}
662
663static int process_buffer(struct sr_input *in)
664{
665 struct context *inc;
666 int i, chunk_size, res;
667
668 inc = in->priv;
669
670 if (!inc->header_read) {
671 res = process_header(in->buf, inc);
672 g_string_erase(in->buf, 0, HEADER_SIZE);
673 if (res != SR_OK)
674 return res;
675 }
676
677 if (!inc->meta_sent) {
678 std_session_send_df_header(in->sdi, LOG_PREFIX);
679 send_metadata(in);
680 }
681
682 if (!inc->records_read) {
683 /* Cut off at a multiple of the record size. */
684 chunk_size = ((in->buf->len) / inc->record_size) * inc->record_size;
685
686 /* There needs to be at least one more record process_record() can peek into. */
687 chunk_size -= inc->record_size;
688
689 for (i = 0; (i < chunk_size) && (!inc->records_read); i += inc->record_size) {
690 switch (inc->device) {
691 case AD_DEVICE_PI:
692 process_record_pi(in, i);
693 break;
694 case AD_DEVICE_IPROBE:
695 process_record_iprobe(in, i);
696 break;
697 default:
698 sr_err("Trying to process records for unknown device!");
699 return SR_ERR;
700 }
701
702 inc->cur_record++;
703 if (inc->cur_record == inc->record_count)
704 inc->records_read = TRUE;
705 }
706
707 g_string_erase(in->buf, 0, i);
708 }
709
710 if (inc->records_read) {
711 /* Read practice commands that configure the setup. */
712 process_practice(in);
713 }
714
715 return SR_OK;
716}
717
718static int receive(struct sr_input *in, GString *buf)
719{
720 g_string_append_len(in->buf, buf->str, buf->len);
721
722 if (!in->sdi_ready) {
723 /* sdi is ready, notify frontend. */
724 in->sdi_ready = TRUE;
725 return SR_OK;
726 }
727
728 return process_buffer(in);
729}
730
731static int end(struct sr_input *in)
732{
733 struct context *inc;
6d2897e3
SA
734 int ret;
735
736 inc = in->priv;
737
738 if (in->sdi_ready)
739 ret = process_buffer(in);
740 else
741 ret = SR_OK;
742
743 flush_output_buffer(in);
744
3be42bc2
UH
745 if (inc->meta_sent)
746 std_session_send_df_end(in->sdi, LOG_PREFIX);
6d2897e3
SA
747
748 return ret;
749}
750
751static struct sr_option options[] = {
752 { "podA", "Import pod A / iprobe",
753 "Create channels and data for pod A / iprobe", NULL, NULL },
754
755 { "podB", "Import pod B", "Create channels and data for pod B", NULL, NULL },
756 { "podC", "Import pod C", "Create channels and data for pod C", NULL, NULL },
757 { "podD", "Import pod D", "Create channels and data for pod D", NULL, NULL },
758 { "podE", "Import pod E", "Create channels and data for pod E", NULL, NULL },
759 { "podF", "Import pod F", "Create channels and data for pod F", NULL, NULL },
760 { "podJ", "Import pod J", "Create channels and data for pod J", NULL, NULL },
761 { "podK", "Import pod K", "Create channels and data for pod K", NULL, NULL },
762 { "podL", "Import pod L", "Create channels and data for pod L", NULL, NULL },
763 { "podM", "Import pod M", "Create channels and data for pod M", NULL, NULL },
764 { "podN", "Import pod N", "Create channels and data for pod N", NULL, NULL },
765 { "podO", "Import pod O", "Create channels and data for pod O", NULL, NULL },
766 ALL_ZERO
767};
768
769static const struct sr_option *get_options(void)
770{
771 if (!options[0].def) {
772 options[0].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
773 options[1].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
774 options[2].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
775 options[3].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
776 options[4].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
777 options[5].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
778 options[6].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
779 options[7].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
780 options[8].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
781 options[9].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
782 options[10].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
783 options[11].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
784 }
785
786 return options;
787}
788
789SR_PRIV struct sr_input_module input_trace32_ad = {
790 .id = "trace32_ad",
791 .name = "Trace32_ad",
792 .desc = "Lauterbach Trace32 logic analyzer data",
793 .exts = (const char*[]){"ad", NULL},
794 .options = get_options,
795 .metadata = { SR_INPUT_META_HEADER | SR_INPUT_META_REQUIRED },
796 .format_match = format_match,
797 .init = init,
798 .receive = receive,
799 .end = end,
800};