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