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