]> sigrok.org Git - libsigrok.git/blame - src/input/trace32_ad.c
input/trace32_ad: rephrase the header parse logic
[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
SA
49#define MAX_POD_COUNT 12
50#define HEADER_SIZE 80
51
80430d4d
GS
52#define SPACE ' '
53#define CTRLZ '\x1a'
54#define TRACE32 "trace32"
55
d9251a2c 56#define TIMESTAMP_RESOLUTION ((double)0.000000000078125) /* 0.078125 ns */
6d2897e3
SA
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 */
6266deb8 63#define DEFAULT_SAMPLERATE 200
6d2897e3
SA
64
65enum {
66 AD_FORMAT_BINHDR = 1, /* Binary header, binary data, textual setup info */
67 AD_FORMAT_TXTHDR /* Textual header, binary data */
68};
69
70enum {
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
78enum {
79 AD_MODE_250MHZ = 0,
80 AD_MODE_500MHZ = 1
81};
82
83enum {
84 AD_COMPR_NONE = 0, /* File created with /NOCOMPRESS */
d9251a2c 85 AD_COMPR_QCOMP = 6, /* File created with /COMPRESS or /QUICKCOMPRESS */
6d2897e3
SA
86};
87
88struct context {
89 gboolean meta_sent;
a5be5d5b 90 gboolean header_read, records_read, trigger_sent;
6d2897e3
SA
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;
6266deb8
SA
97 uint64_t samplerate;
98 double timestamp_scale;
6d2897e3
SA
99 GString *out_buf;
100};
101
102static int process_header(GString *buf, struct context *inc);
103static void create_channels(struct sr_input *in);
104
8c4bff1d
GS
105/* Transform non-printable chars to '\xNN' presentation. */
106static 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
6d2897e3
SA
128static 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
149static 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
6266deb8
SA
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
6d2897e3
SA
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
8bc2fa6d 181 inc->out_buf = g_string_sized_new(CHUNK_SIZE);
6d2897e3
SA
182
183 return SR_OK;
184}
185
54ee427d 186static int format_match(GHashTable *metadata, unsigned int *confidence)
6d2897e3
SA
187{
188 GString *buf;
54ee427d 189 int rc;
6d2897e3
SA
190
191 buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
54ee427d 192 rc = process_header(buf, NULL);
6d2897e3 193
54ee427d
GS
194 if (rc != SR_OK)
195 return rc;
196 *confidence = 10;
197
198 return SR_OK;
6d2897e3
SA
199}
200
201static int process_header(GString *buf, struct context *inc)
202{
203 char *format_name, *format_name_sig;
8c4bff1d 204 char *p;
80430d4d
GS
205 int has_trace32;
206 int record_size, device_id;
6d2897e3
SA
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
8c4bff1d
GS
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 */
6d2897e3 234
80430d4d
GS
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 */
6d2897e3 240 format_name = g_strndup(buf->str, 32);
80430d4d
GS
241 p = strchr(format_name, CTRLZ);
242 if (p)
243 *p = '\0';
244 g_strchomp(format_name);
6d2897e3 245
80430d4d
GS
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);
6d2897e3 253
80430d4d
GS
254 if (has_trace32) {
255 /* Literal "trace32" leader, binary header follows. */
6d2897e3
SA
256 if (inc)
257 inc->format = AD_FORMAT_BINHDR;
80430d4d
GS
258 } else if (g_ascii_isdigit(format_name[0]) && (format_name[1] == SPACE)) {
259 /* Digit and SPACE leader, currently unsupported text header. */
6d2897e3
SA
260 if (inc)
261 inc->format = AD_FORMAT_TXTHDR;
6d2897e3 262 g_free(format_name);
cdb134eb
GS
263 if (inc)
264 sr_err("This format isn't implemented yet, aborting.");
6d2897e3
SA
265 return SR_ERR;
266 } else {
80430d4d 267 /* Unknown kind of format name. Unsupported. */
6d2897e3 268 g_free(format_name);
cdb134eb
GS
269 if (inc)
270 sr_err("Don't know this file format, aborting.");
6d2897e3
SA
271 return SR_ERR;
272 }
273
8c4bff1d
GS
274 p = printable_name(format_name);
275 sr_dbg("File says it's \"%s\"", p);
276 g_free(p);
6d2897e3
SA
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) {
6d2897e3
SA
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
6d2897e3
SA
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);
d9251a2c
UH
304 inc->compression = R8(buf->str + 48); /* Maps to the enum. */
305 inc->record_mode = R8(buf->str + 55); /* Maps to the enum. */
6d2897e3
SA
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
328static 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++) {
00ed77f2 342 snprintf(name, sizeof(name), "%c%d", get_pod_name_from_id(pod), channel);
6d2897e3
SA
343 inc->channels[pod][channel] =
344 sr_channel_new(in->sdi, chan_id, SR_CHANNEL_LOGIC, TRUE, name);
345 chan_id++;
346 }
347
00ed77f2 348 snprintf(name, sizeof(name), "CLK%c", get_pod_name_from_id(pod));
6d2897e3
SA
349 inc->channels[pod][16] =
350 sr_channel_new(in->sdi, chan_id, SR_CHANNEL_LOGIC, TRUE, name);
351 chan_id++;
352 }
353}
354
355static 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
6266deb8
SA
362 inc = in->priv;
363
6d2897e3
SA
364 packet.type = SR_DF_META;
365 packet.payload = &meta;
6266deb8 366 src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(inc->samplerate));
6d2897e3
SA
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
6d2897e3
SA
372 inc->meta_sent = TRUE;
373}
374
375static 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
395static 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
a5be5d5b 526 if (timestamp == inc->trigger_timestamp && !inc->trigger_sent) {
6d2897e3
SA
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);
a5be5d5b 533 inc->trigger_sent = TRUE;
6d2897e3
SA
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);
6266deb8 543 packet_count = (int)(next_timestamp - timestamp) / inc->timestamp_scale;
6d2897e3
SA
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
8bc2fa6d 553 if (inc->out_buf->len >= CHUNK_SIZE)
6d2897e3
SA
554 flush_output_buffer(in);
555}
556
557static 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
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);
582
583 packet.type = SR_DF_TRIGGER;
584 packet.payload = NULL;
585 sr_session_send(in->sdi, &packet);
a5be5d5b 586 inc->trigger_sent = TRUE;
6d2897e3
SA
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);
6266deb8 596 packet_count = (int)(next_timestamp - timestamp) / inc->timestamp_scale;
6d2897e3
SA
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
8bc2fa6d 606 if (inc->out_buf->len >= CHUNK_SIZE)
6d2897e3
SA
607 flush_output_buffer(in);
608}
609
610static 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
688static 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
719static 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) {
bee2b016 734 std_session_send_df_header(in->sdi);
6d2897e3
SA
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
774static 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
787static int end(struct sr_input *in)
788{
789 struct context *inc;
6d2897e3
SA
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
3be42bc2 801 if (inc->meta_sent)
bee2b016 802 std_session_send_df_end(in->sdi);
6d2897e3
SA
803
804 return ret;
805}
806
87616181
SA
807static 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
6d2897e3
SA
822static 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 },
6266deb8 837
867293a1 838 { "samplerate", "Reduced sample rate (MHz)", "Reduce the original sample rate of 12.8 GHz to the specified sample rate in MHz", NULL, NULL },
6266deb8 839
6d2897e3
SA
840 ALL_ZERO
841};
842
843static 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));
6266deb8 858 options[12].def = g_variant_ref_sink(g_variant_new_uint32(DEFAULT_SAMPLERATE));
6d2897e3
SA
859 }
860
861 return options;
862}
863
864SR_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,
87616181 875 .reset = reset,
6d2897e3 876};