]> sigrok.org Git - libsigrok.git/blame - src/input/trace32_ad.c
input/trace32_ad: silence format match logging, improve robustness
[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
d9251a2c 52#define TIMESTAMP_RESOLUTION ((double)0.000000000078125) /* 0.078125 ns */
6d2897e3
SA
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 */
6266deb8 59#define DEFAULT_SAMPLERATE 200
6d2897e3
SA
60
61enum {
62 AD_FORMAT_BINHDR = 1, /* Binary header, binary data, textual setup info */
63 AD_FORMAT_TXTHDR /* Textual header, binary data */
64};
65
66enum {
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
74enum {
75 AD_MODE_250MHZ = 0,
76 AD_MODE_500MHZ = 1
77};
78
79enum {
80 AD_COMPR_NONE = 0, /* File created with /NOCOMPRESS */
d9251a2c 81 AD_COMPR_QCOMP = 6, /* File created with /COMPRESS or /QUICKCOMPRESS */
6d2897e3
SA
82};
83
84struct context {
85 gboolean meta_sent;
a5be5d5b 86 gboolean header_read, records_read, trigger_sent;
6d2897e3
SA
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;
6266deb8
SA
93 uint64_t samplerate;
94 double timestamp_scale;
6d2897e3
SA
95 GString *out_buf;
96};
97
98static int process_header(GString *buf, struct context *inc);
99static void create_channels(struct sr_input *in);
100
8c4bff1d
GS
101/* Transform non-printable chars to '\xNN' presentation. */
102static 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
6d2897e3
SA
124static 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
145static 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
6266deb8
SA
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
6d2897e3
SA
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
8bc2fa6d 177 inc->out_buf = g_string_sized_new(CHUNK_SIZE);
6d2897e3
SA
178
179 return SR_OK;
180}
181
54ee427d 182static int format_match(GHashTable *metadata, unsigned int *confidence)
6d2897e3
SA
183{
184 GString *buf;
54ee427d 185 int rc;
6d2897e3
SA
186
187 buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
54ee427d 188 rc = process_header(buf, NULL);
6d2897e3 189
54ee427d
GS
190 if (rc != SR_OK)
191 return rc;
192 *confidence = 10;
193
194 return SR_OK;
6d2897e3
SA
195}
196
197static int process_header(GString *buf, struct context *inc)
198{
199 char *format_name, *format_name_sig;
8c4bff1d 200 char *p;
6d2897e3
SA
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
8c4bff1d
GS
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 */
6d2897e3
SA
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);
cdb134eb
GS
252 if (inc)
253 sr_err("This format isn't implemented yet, aborting.");
6d2897e3
SA
254 return SR_ERR;
255 } else {
256 g_free(format_name_sig);
257 g_free(format_name);
cdb134eb
GS
258 if (inc)
259 sr_err("Don't know this file format, aborting.");
6d2897e3
SA
260 return SR_ERR;
261 }
262
8c4bff1d
GS
263 p = printable_name(format_name);
264 sr_dbg("File says it's \"%s\"", p);
265 g_free(p);
6d2897e3
SA
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);
d9251a2c
UH
295 inc->compression = R8(buf->str + 48); /* Maps to the enum. */
296 inc->record_mode = R8(buf->str + 55); /* Maps to the enum. */
6d2897e3
SA
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
319static 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++) {
00ed77f2 333 snprintf(name, sizeof(name), "%c%d", get_pod_name_from_id(pod), channel);
6d2897e3
SA
334 inc->channels[pod][channel] =
335 sr_channel_new(in->sdi, chan_id, SR_CHANNEL_LOGIC, TRUE, name);
336 chan_id++;
337 }
338
00ed77f2 339 snprintf(name, sizeof(name), "CLK%c", get_pod_name_from_id(pod));
6d2897e3
SA
340 inc->channels[pod][16] =
341 sr_channel_new(in->sdi, chan_id, SR_CHANNEL_LOGIC, TRUE, name);
342 chan_id++;
343 }
344}
345
346static 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
6266deb8
SA
353 inc = in->priv;
354
6d2897e3
SA
355 packet.type = SR_DF_META;
356 packet.payload = &meta;
6266deb8 357 src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(inc->samplerate));
6d2897e3
SA
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
6d2897e3
SA
363 inc->meta_sent = TRUE;
364}
365
366static 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
386static 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
a5be5d5b 517 if (timestamp == inc->trigger_timestamp && !inc->trigger_sent) {
6d2897e3
SA
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);
a5be5d5b 524 inc->trigger_sent = TRUE;
6d2897e3
SA
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);
6266deb8 534 packet_count = (int)(next_timestamp - timestamp) / inc->timestamp_scale;
6d2897e3
SA
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
8bc2fa6d 544 if (inc->out_buf->len >= CHUNK_SIZE)
6d2897e3
SA
545 flush_output_buffer(in);
546}
547
548static 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
a5be5d5b 570 if (timestamp == inc->trigger_timestamp && !inc->trigger_sent) {
6d2897e3
SA
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);
a5be5d5b 577 inc->trigger_sent = TRUE;
6d2897e3
SA
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);
6266deb8 587 packet_count = (int)(next_timestamp - timestamp) / inc->timestamp_scale;
6d2897e3
SA
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
8bc2fa6d 597 if (inc->out_buf->len >= CHUNK_SIZE)
6d2897e3
SA
598 flush_output_buffer(in);
599}
600
601static 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
679static 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
710static 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) {
bee2b016 725 std_session_send_df_header(in->sdi);
6d2897e3
SA
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
765static 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
778static int end(struct sr_input *in)
779{
780 struct context *inc;
6d2897e3
SA
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
3be42bc2 792 if (inc->meta_sent)
bee2b016 793 std_session_send_df_end(in->sdi);
6d2897e3
SA
794
795 return ret;
796}
797
87616181
SA
798static 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
6d2897e3
SA
813static 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 },
6266deb8 828
867293a1 829 { "samplerate", "Reduced sample rate (MHz)", "Reduce the original sample rate of 12.8 GHz to the specified sample rate in MHz", NULL, NULL },
6266deb8 830
6d2897e3
SA
831 ALL_ZERO
832};
833
834static 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));
6266deb8 849 options[12].def = g_variant_ref_sink(g_variant_new_uint32(DEFAULT_SAMPLERATE));
6d2897e3
SA
850 }
851
852 return options;
853}
854
855SR_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,
87616181 866 .reset = reset,
6d2897e3 867};