]> sigrok.org Git - libsigrok.git/blame - src/input/saleae.c
input/saleae: improve L2D undersampling, do provide sample data
[libsigrok.git] / src / input / saleae.c
CommitLineData
d891892d
GS
1/*
2 * This file is part of the libsigrok project.
3 *
4 * Copyright (C) 2020 Gerhard Sittig <gerhard.sittig@gmx.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20/*
21 * See the vendor's FAQ on file format details for exported files and
22 * different software versions:
23 *
24 * https://support.saleae.com/faq/technical-faq/binary-data-export-format
25 * https://support.saleae.com/faq/technical-faq/data-export-format-analog-binary
26 * https://support.saleae.com/faq/technical-faq/binary-export-format-logic-2
27 *
28 * All data is in little endian representation, floating point values
29 * in IEEE754 format. Recent versions add header information, while
30 * previous versions tend to "raw" formats. This input module is about
31 * digital and analog data in their "binary presentation". CSV and VCD
32 * exports are handled by other input modules.
33 *
34 * Saleae Logic applications typically export one file per channel. The
35 * sigrok input modules exclusively handle an individual file, existing
36 * applications may not be prepared to handle a set of files, or handle
37 * "special" file types like directories. Some of them will even actively
38 * reject such input specs. Merging multiple exported channels into either
39 * another input file or a sigrok session is supposed to be done outside
40 * of this input module. Support for ZIP archives is currently missing.
41 *
42 * TODO
43 * - Need to create a channel group in addition to channels?
44 * - Check file re-load and channel references. See bug #1241.
45 * - Fixup 'digits' use for analog data. The current implementation made
46 * an educated guess, assuming some 12bit resolution and logic levels
47 * which roughly results in the single digit mV range.
48 * - Add support for "local I/O" in the input module when the common
49 * support code becomes available. The .sal save files of the Logic
50 * application appears to be a ZIP archive with *.bin files in it
51 * plus some meta.json dictionary. This will also introduce a new
52 * JSON reader dependency.
53 * - When ZIP support gets added and .sal files become accepted, this
54 * import module needs to merge the content of several per-channel
55 * files, which may be of different types (mixed signal), and/or may
56 * even differ in their samplerate (which becomes complex, similar to
57 * VCD or CSV input). Given the .sal archive's layout this format may
58 * even only become attractive when common sigrok infrastructure has
59 * support for per-channel compression and rate(?).
60 */
61
62#include <config.h>
63#include <glib.h>
64#include <stdint.h>
65#include <stdlib.h>
66#include <string.h>
67#include <libsigrok/libsigrok.h>
68#include "libsigrok-internal.h"
69
70#define LOG_PREFIX "input/saleae"
71
72/*
73 * Saleae Logic "save files" (ZIP archives with .sal file extension)
74 * could get detected, but are not yet supported. Usability would be
75 * rather limited when the current development support gets enabled.
76 * This compile time switch is strictly for internal developer use.
77 */
78#define SALEAE_WITH_SAL_SUPPORT 0
79
80#define CHUNK_SIZE (4 * 1024 * 1024)
81
82#define LOGIC2_MAGIC "<SALEAE>"
83#define LOGIC2_VERSION 0
84#define LOGIC2_TYPE_DIGITAL 0
85#define LOGIC2_TYPE_ANALOG 1
86
87/* Simple header check approach. Assume minimum file size for all formats. */
88#define LOGIC2_MIN_SIZE 0x30
89
90enum logic_format {
91 FMT_UNKNOWN,
92 FMT_AUTO_DETECT,
93 FMT_LOGIC1_DIGITAL,
94 FMT_LOGIC1_ANALOG,
95 FMT_LOGIC2_DIGITAL,
96 FMT_LOGIC2_ANALOG,
97 FMT_LOGIC2_ARCHIVE,
98};
99
100enum input_stage {
101 STAGE_ALL_WAIT_HEADER,
102 STAGE_ALL_DETECT_TYPE,
103 STAGE_ALL_READ_HEADER,
104 STAGE_L1D_EVERY_VALUE,
105 STAGE_L1D_CHANGE_INIT,
106 STAGE_L1D_CHANGE_VALUE,
107 STAGE_L1A_NEW_CHANNEL,
108 STAGE_L1A_SAMPLE,
109 STAGE_L2D_CHANGE_VALUE,
110 STAGE_L2A_FIRST_VALUE,
111 STAGE_L2A_EVERY_VALUE,
112};
113
114struct context {
115 struct context_options {
116 enum logic_format format;
117 gboolean when_changed;
118 size_t word_size;
119 size_t channel_count;
120 uint64_t sample_rate;
121 } options;
122 struct {
123 gboolean got_header;
124 gboolean header_sent;
125 gboolean rate_sent;
126 GSList *prev_channels;
127 } module_state;
128 struct {
129 enum logic_format format;
130 gboolean when_changed;
131 size_t word_size;
132 size_t channel_count;
133 uint64_t sample_rate;
134 enum input_stage stage;
135 struct {
136 uint64_t samples_per_channel;
137 uint64_t current_channel_idx;
138 uint64_t current_per_channel;
139 } l1a;
140 struct {
141 uint32_t init_state;
142 double begin_time;
143 double end_time;
144 uint64_t transition_count;
145 double sample_period;
146 double min_time_step;
147 } l2d;
148 struct {
149 double begin_time;
150 uint64_t sample_rate;
151 uint64_t down_sample;
152 uint64_t sample_count;
153 } l2a;
154 } logic_state;
155 struct {
156 GSList *channels;
157 gboolean is_analog;
158 size_t unit_size;
159 size_t samples_per_chunk;
160 size_t samples_in_buffer;
161 uint8_t *buffer_digital;
162 float *buffer_analog;
163 uint8_t *write_pos;
164 struct {
165 uint64_t stamp;
166 double time;
167 uint32_t digital;
168 float analog;
169 } last;
170 } feed;
171};
172
173static const char *format_texts[] = {
174 [FMT_UNKNOWN] = "unknown",
175 [FMT_AUTO_DETECT] = "auto-detect",
176 [FMT_LOGIC1_DIGITAL] = "logic1-digital",
177 [FMT_LOGIC1_ANALOG] = "logic1-analog",
178 [FMT_LOGIC2_DIGITAL] = "logic2-digital",
179 [FMT_LOGIC2_ANALOG] = "logic2-analog",
180#if SALEAE_WITH_SAL_SUPPORT
181 [FMT_LOGIC2_ARCHIVE] = "logic2-archive",
182#endif
183};
184
185static const char *get_format_text(enum logic_format fmt)
186{
187 const char *text;
188
189 if (fmt >= ARRAY_SIZE(format_texts))
190 return NULL;
191 text = format_texts[fmt];
192 if (!text || !*text)
193 return NULL;
194 return text;
195}
196
197static int create_channels(struct sr_input *in)
198{
199 struct context *inc;
200 int type;
201 size_t count, idx;
202 char name[4];
203 struct sr_channel *ch;
204
205 inc = in->priv;
206
207 if (in->sdi->channels)
208 return SR_OK;
209
210 count = inc->logic_state.channel_count;
211 switch (inc->logic_state.format) {
212 case FMT_LOGIC1_DIGITAL:
213 case FMT_LOGIC2_DIGITAL:
214 type = SR_CHANNEL_LOGIC;
215 break;
216 case FMT_LOGIC1_ANALOG:
217 case FMT_LOGIC2_ANALOG:
218 type = SR_CHANNEL_ANALOG;
219 break;
220 default:
221 return SR_ERR_NA;
222 }
223
224 /* TODO Need to create a channel group? */
225 for (idx = 0; idx < count; idx++) {
226 snprintf(name, sizeof(name), "%zu", idx);
227 ch = sr_channel_new(in->sdi, idx, type, TRUE, name);
228 if (!ch)
229 return SR_ERR_MALLOC;
230 }
231
232 return SR_OK;
233}
234
235static int alloc_feed_buffer(struct sr_input *in)
236{
237 struct context *inc;
238 size_t alloc_size;
239
240 inc = in->priv;
241
242 inc->feed.is_analog = FALSE;
243 alloc_size = CHUNK_SIZE;
244 switch (inc->logic_state.format) {
245 case FMT_LOGIC1_DIGITAL:
246 case FMT_LOGIC2_DIGITAL:
247 inc->feed.unit_size = sizeof(inc->feed.last.digital);
248 alloc_size /= inc->feed.unit_size;
249 inc->feed.samples_per_chunk = alloc_size;
250 alloc_size *= inc->feed.unit_size;
251 inc->feed.buffer_digital = g_try_malloc(alloc_size);
252 if (!inc->feed.buffer_digital)
253 return SR_ERR_MALLOC;
254 inc->feed.write_pos = inc->feed.buffer_digital;
255 break;
256 case FMT_LOGIC1_ANALOG:
257 case FMT_LOGIC2_ANALOG:
258 inc->feed.is_analog = TRUE;
259 alloc_size /= sizeof(inc->feed.last.analog);
260 inc->feed.samples_per_chunk = alloc_size;
261 alloc_size *= sizeof(inc->feed.last.analog);
262 inc->feed.buffer_analog = g_try_malloc(alloc_size);
263 if (!inc->feed.buffer_analog)
264 return SR_ERR_MALLOC;
265 inc->feed.write_pos = (void *)inc->feed.buffer_analog;
266 break;
267 default:
268 return SR_ERR_NA;
269 }
270 inc->feed.samples_in_buffer = 0;
271
272 return SR_OK;
273}
274
275static int relse_feed_buffer(struct sr_input *in)
276{
277 struct context *inc;
278
279 inc = in->priv;
280
281 inc->feed.is_analog = FALSE;
282 inc->feed.unit_size = 0;
283 inc->feed.samples_per_chunk = 0;
284 inc->feed.samples_in_buffer = 0;
285 g_free(inc->feed.buffer_digital);
286 inc->feed.buffer_digital = NULL;
287 g_free(inc->feed.buffer_analog);
288 inc->feed.buffer_analog = NULL;
289 inc->feed.write_pos = NULL;
290
291 return SR_OK;
292}
293
294static int setup_feed_buffer_channel(struct sr_input *in, size_t ch_idx)
295{
296 struct context *inc;
297 struct sr_channel *ch;
298
299 inc = in->priv;
300
301 g_slist_free(inc->feed.channels);
302 inc->feed.channels = NULL;
303 if (ch_idx >= inc->logic_state.channel_count)
304 return SR_OK;
305
306 ch = g_slist_nth_data(in->sdi->channels, ch_idx);
307 if (!ch)
308 return SR_ERR_ARG;
309 inc->feed.channels = g_slist_append(NULL, ch);
310 return SR_OK;
311}
312
313static int flush_feed_buffer(struct sr_input *in)
314{
315 struct context *inc;
316 struct sr_datafeed_packet packet;
317 struct sr_datafeed_logic logic;
318 struct sr_datafeed_analog analog;
319 struct sr_analog_encoding encoding;
320 struct sr_analog_meaning meaning;
321 struct sr_analog_spec spec;
322 int rc;
323
324 inc = in->priv;
325
326 if (!inc->feed.samples_in_buffer)
327 return SR_OK;
328
329 /* Automatically send a datafeed header before meta and samples. */
330 if (!inc->module_state.header_sent) {
331 rc = std_session_send_df_header(in->sdi);
332 if (rc)
333 return rc;
334 inc->module_state.header_sent = TRUE;
335 }
336
337 /* Automatically send the samplerate (when available). */
338 if (inc->logic_state.sample_rate && !inc->module_state.rate_sent) {
339 rc = sr_session_send_meta(in->sdi, SR_CONF_SAMPLERATE,
340 g_variant_new_uint64(inc->logic_state.sample_rate));
341 inc->module_state.rate_sent = TRUE;
342 }
343
344 /*
345 * Create a packet with either logic or analog payload. Rewind
346 * the caller's write position.
347 */
348 memset(&packet, 0, sizeof(packet));
349 if (inc->feed.is_analog) {
350 /* TODO: Use proper 'digits' value for this input module. */
351 sr_analog_init(&analog, &encoding, &meaning, &spec, 3);
352 analog.data = inc->feed.buffer_analog;
353 analog.num_samples = inc->feed.samples_in_buffer;
354 analog.meaning->channels = inc->feed.channels;
355 analog.meaning->mq = SR_MQ_VOLTAGE;
356 analog.meaning->mqflags |= SR_MQFLAG_DC;
357 analog.meaning->unit = SR_UNIT_VOLT;
358 packet.type = SR_DF_ANALOG;
359 packet.payload = &analog;
360 inc->feed.write_pos = (void *)inc->feed.buffer_analog;
361 } else {
362 memset(&logic, 0, sizeof(logic));
363 logic.length = inc->feed.samples_in_buffer;
364 logic.length *= inc->feed.unit_size;
365 logic.unitsize = inc->feed.unit_size;
366 logic.data = inc->feed.buffer_digital;
367 packet.type = SR_DF_LOGIC;
368 packet.payload = &logic;
369 inc->feed.write_pos = inc->feed.buffer_digital;
370 }
371 inc->feed.samples_in_buffer = 0;
372
373 /* Send the packet to the session feed. */
374 return sr_session_send(in->sdi, &packet);
375}
376
377static int addto_feed_buffer_logic(struct sr_input *in,
378 uint64_t data, size_t count)
379{
380 struct context *inc;
381
382 inc = in->priv;
383
384 if (inc->feed.is_analog)
385 return SR_ERR_ARG;
386
387 while (count--) {
388 if (inc->feed.unit_size == sizeof(uint64_t))
389 write_u64le_inc(&inc->feed.write_pos, data);
390 else if (inc->feed.unit_size == sizeof(uint32_t))
391 write_u32le_inc(&inc->feed.write_pos, data);
392 else if (inc->feed.unit_size == sizeof(uint16_t))
393 write_u16le_inc(&inc->feed.write_pos, data);
394 else if (inc->feed.unit_size == sizeof(uint8_t))
395 write_u8_inc(&inc->feed.write_pos, data);
396 else
397 return SR_ERR_BUG;
398 inc->feed.samples_in_buffer++;
399 if (inc->feed.samples_in_buffer == inc->feed.samples_per_chunk)
400 flush_feed_buffer(in);
401 }
402
403 return SR_OK;
404}
405
406static int addto_feed_buffer_analog(struct sr_input *in,
407 float data, size_t count)
408{
409 struct context *inc;
410
411 inc = in->priv;
412
413 if (!inc->feed.is_analog)
414 return SR_ERR_ARG;
415
416 while (count--) {
417 if (sizeof(inc->feed.buffer_analog[0]) == sizeof(float))
418 write_fltle_inc(&inc->feed.write_pos, data);
419 else if (sizeof(inc->feed.buffer_analog[0]) == sizeof(double))
420 write_dblle_inc(&inc->feed.write_pos, data);
421 else
422 return SR_ERR_BUG;
423 inc->feed.samples_in_buffer++;
424 if (inc->feed.samples_in_buffer == inc->feed.samples_per_chunk)
425 flush_feed_buffer(in);
426 }
427
428 return SR_OK;
429}
430
431static enum logic_format check_format(const uint8_t *data, size_t dlen)
432{
433 const char *s;
434 uint32_t v, t;
435
436 /* TODO
437 * Can we check ZIP content here in useful ways? Probably only
438 * when the input module got extended to optionally handle local
439 * file I/O, and passes some archive handle to this routine.
440 */
441
442 /* Check for the magic literal. */
443 s = (void *)data;
444 if (dlen < strlen(LOGIC2_MAGIC))
445 return FMT_UNKNOWN;
446 if (strncmp(s, LOGIC2_MAGIC, strlen(LOGIC2_MAGIC)) != 0)
447 return FMT_UNKNOWN;
448 data += strlen(LOGIC2_MAGIC);
449 dlen -= strlen(LOGIC2_MAGIC);
450
451 /* Get the version and type fields. */
452 if (dlen < 2 * sizeof(uint32_t))
453 return FMT_UNKNOWN;
454 v = read_u32le_inc(&data);
455 t = read_u32le_inc(&data);
456 if (v != LOGIC2_VERSION)
457 return FMT_UNKNOWN;
458 switch (t) {
459 case LOGIC2_TYPE_DIGITAL:
460 return FMT_LOGIC2_DIGITAL;
461 case LOGIC2_TYPE_ANALOG:
462 return FMT_LOGIC2_ANALOG;
463 default:
464 return FMT_UNKNOWN;
465 }
466
467 return FMT_UNKNOWN;
468}
469
470/* Check for availability of required header data. */
471static gboolean have_header(struct context *inc, GString *buf)
472{
473
474 /*
475 * The amount of required data depends on the file format. Which
476 * either was specified before, or is yet to get determined. The
477 * input module ideally would apply a sequence of checks for the
478 * currently available (partial) data, access a few first header
479 * fields, before checking for a little more receive data, before
480 * accessing more fields, until the input file's type was found,
481 * and its header length is known, and can get checked.
482 *
483 * This simple implementation just assumes that any input file
484 * has at least a given number of bytes, which should not be an
485 * issue for typical use cases. Only extremely short yet valid
486 * input files with just a few individual samples may fail this
487 * check. It's assumed that these files are very rare, and may
488 * be of types which are covered by other input modules (raw
489 * binary).
490 */
491 (void)inc;
492 return buf->len >= LOGIC2_MIN_SIZE;
493}
494
495/* Process/inspect previously received input data. Get header parameters. */
496static int parse_header(struct sr_input *in)
497{
498 struct context *inc;
499 const uint8_t *read_pos, *start_pos;
500 size_t read_len, want_len;
501 uint64_t samples_per_channel;
502 size_t channel_count;
503 double sample_period;
504 uint64_t sample_rate;
505
506 inc = in->priv;
507 read_pos = (const uint8_t *)in->buf->str;
508 read_len = in->buf->len;
509
510 /*
511 * Clear internal state. Normalize user specified option values
512 * before amending them from the input file's header information.
513 */
514 memset(&inc->logic_state, 0, sizeof(inc->logic_state));
515 inc->logic_state.format = inc->options.format;
516 inc->logic_state.when_changed = inc->options.when_changed;
517 inc->logic_state.word_size = inc->options.word_size;
518 if (!inc->logic_state.word_size) {
519 sr_err("Need a word size.");
520 return SR_ERR_ARG;
521 }
522 inc->logic_state.word_size += 8 - 1;
523 inc->logic_state.word_size /= 8; /* Sample width in bytes. */
524 if (inc->logic_state.word_size > sizeof(inc->feed.last.digital)) {
525 sr_err("Excessive word size %zu.", inc->logic_state.word_size);
526 return SR_ERR_ARG;
527 }
528 inc->logic_state.channel_count = inc->options.channel_count;
529 inc->logic_state.sample_rate = inc->options.sample_rate;
530 if (inc->logic_state.format == FMT_AUTO_DETECT)
531 inc->logic_state.stage = STAGE_ALL_DETECT_TYPE;
532 else
533 inc->logic_state.stage = STAGE_ALL_READ_HEADER;
534
535 /*
536 * Optionally auto-detect the format if none was specified yet.
537 * This only works for some of the supported formats. ZIP support
538 * requires local I/O in the input module (won't work on memory
539 * buffers).
540 */
541 if (inc->logic_state.stage == STAGE_ALL_DETECT_TYPE) {
542 inc->logic_state.format = check_format(read_pos, read_len);
543 if (inc->logic_state.format == FMT_UNKNOWN) {
544 sr_err("Unknown or unsupported file format.");
545 return SR_ERR_DATA;
546 }
547 sr_info("Detected file format: '%s'.",
548 get_format_text(inc->logic_state.format));
549 inc->logic_state.stage = STAGE_ALL_READ_HEADER;
550 }
551
552 /*
553 * Read the header fields, depending on the specific file format.
554 * Arrange for the subsequent inspection of sample data items.
555 */
556 start_pos = read_pos;
557 switch (inc->logic_state.format) {
558 case FMT_LOGIC1_DIGITAL:
559 channel_count = inc->logic_state.channel_count;
560 if (!channel_count) {
561 channel_count = inc->logic_state.word_size;
562 channel_count *= 8;
563 inc->logic_state.channel_count = channel_count;
564 }
565 /* EMPTY */ /* No header fields to read here. */
566 sr_dbg("L1D, empty header, changed %d.",
567 inc->logic_state.when_changed ? 1 : 0);
568 if (inc->logic_state.when_changed)
569 inc->logic_state.stage = STAGE_L1D_CHANGE_INIT;
570 else
571 inc->logic_state.stage = STAGE_L1D_EVERY_VALUE;
572 break;
573 case FMT_LOGIC1_ANALOG:
574 want_len = sizeof(uint64_t) + sizeof(uint32_t) + sizeof(double);
575 if (read_len < want_len)
576 return SR_ERR_DATA;
577 samples_per_channel = read_u64le_inc(&read_pos);
578 channel_count = read_u32le_inc(&read_pos);
579 sample_period = read_dblle_inc(&read_pos);
580 inc->logic_state.l1a.samples_per_channel = samples_per_channel;
581 inc->logic_state.channel_count = channel_count;
582 sample_rate = 0;
583 if (sample_period) {
584 sample_period = 1.0 / sample_period;
585 sample_period += 0.5;
586 sample_rate = (uint64_t)sample_period;
587 inc->logic_state.sample_rate = sample_rate;
588 }
589 sr_dbg("L1A header, smpls %zu, chans %zu, per %lf, rate %zu.",
590 (size_t)samples_per_channel, (size_t)channel_count,
591 sample_period, (size_t)sample_rate);
592 inc->logic_state.stage = STAGE_L1A_NEW_CHANNEL;
593 inc->logic_state.l1a.current_channel_idx = 0;
594 inc->logic_state.l1a.current_per_channel = 0;
595 break;
596 case FMT_LOGIC2_DIGITAL:
597 inc->logic_state.channel_count = 1;
598 want_len = sizeof(uint64_t); /* magic */
599 want_len += 2 * sizeof(uint32_t); /* version, type */
600 want_len += sizeof(uint32_t); /* initial state */
601 want_len += 2 * sizeof(double); /* begin time, end time */
602 want_len += sizeof(uint64_t); /* transition count */
603 if (read_len < want_len)
604 return SR_ERR_DATA;
605 if (check_format(read_pos, read_len) != FMT_LOGIC2_DIGITAL)
606 return SR_ERR_DATA;
607 (void)read_u64le_inc(&read_pos);
608 (void)read_u32le_inc(&read_pos);
609 (void)read_u32le_inc(&read_pos);
610 inc->logic_state.l2d.init_state = read_u32le_inc(&read_pos);
611 inc->logic_state.l2d.begin_time = read_dblle_inc(&read_pos);
612 inc->logic_state.l2d.end_time = read_dblle_inc(&read_pos);
613 inc->logic_state.l2d.transition_count = read_u64le_inc(&read_pos);
614 sr_dbg("L2D header, init %u, begin %lf, end %lf, transitions %" PRIu64 ".",
615 (unsigned)inc->logic_state.l2d.init_state,
616 inc->logic_state.l2d.begin_time,
617 inc->logic_state.l2d.end_time,
618 inc->logic_state.l2d.transition_count);
619 if (!inc->logic_state.sample_rate) {
620 sr_err("Need a samplerate.");
621 return SR_ERR_ARG;
622 }
623 inc->feed.last.time = inc->logic_state.l2d.begin_time;
624 inc->feed.last.digital = inc->logic_state.l2d.init_state ? 1 : 0;
625 inc->logic_state.l2d.sample_period = inc->logic_state.sample_rate;
626 inc->logic_state.l2d.sample_period = 1.0 / inc->logic_state.l2d.sample_period;
627 inc->logic_state.l2d.min_time_step = inc->logic_state.l2d.end_time;
628 inc->logic_state.l2d.min_time_step -= inc->logic_state.l2d.begin_time;
629 inc->logic_state.stage = STAGE_L2D_CHANGE_VALUE;
630 break;
631 case FMT_LOGIC2_ANALOG:
632 inc->logic_state.channel_count = 1;
633 want_len = sizeof(uint64_t); /* magic */
634 want_len += 2 * sizeof(uint32_t); /* version, type */
635 want_len += sizeof(double); /* begin time */
636 want_len += 2 * sizeof(uint64_t); /* sample rate, down sample */
637 want_len += sizeof(uint64_t); /* sample count */
638 if (read_len < want_len)
639 return SR_ERR_DATA;
640 if (check_format(read_pos, read_len) != FMT_LOGIC2_ANALOG)
641 return SR_ERR_DATA;
642 (void)read_u64le_inc(&read_pos);
643 (void)read_u32le_inc(&read_pos);
644 (void)read_u32le_inc(&read_pos);
645 inc->logic_state.l2a.begin_time = read_dblle_inc(&read_pos);
646 inc->logic_state.l2a.sample_rate = read_u64le_inc(&read_pos);
647 inc->logic_state.l2a.down_sample = read_u64le_inc(&read_pos);
648 inc->logic_state.l2a.sample_count = read_u64le_inc(&read_pos);
649 if (!inc->logic_state.sample_rate)
650 inc->logic_state.sample_rate = inc->logic_state.l2a.sample_rate;
651 sr_dbg("L2A header, begin %lf, rate %" PRIu64 ", down %" PRIu64 ", samples %" PRIu64 ".",
652 inc->logic_state.l2a.begin_time,
653 inc->logic_state.l2a.sample_rate,
654 inc->logic_state.l2a.down_sample,
655 inc->logic_state.l2a.sample_count);
656 inc->feed.last.time = inc->logic_state.l2a.begin_time;
657 inc->logic_state.stage = STAGE_L2A_FIRST_VALUE;
658 break;
659 case FMT_LOGIC2_ARCHIVE:
660 sr_err("Support for .sal archives not implemented yet.");
661 return SR_ERR_NA;
662 default:
663 sr_err("Unknown or unsupported file format.");
664 return SR_ERR_NA;
665 }
666
667 /* Remove the consumed header fields from the receive buffer. */
668 read_len = read_pos - start_pos;
669 g_string_erase(in->buf, 0, read_len);
670
671 return SR_OK;
672}
673
674/* Check availablity of the next sample data item. */
675static gboolean have_next_item(struct sr_input *in,
676 const uint8_t *buff, size_t blen,
677 const uint8_t **curr, const uint8_t **next)
678{
679 struct context *inc;
680 size_t want_len;
681 const uint8_t *pos;
682
683 inc = in->priv;
684 if (curr)
685 *curr = NULL;
686 if (next)
687 *next = NULL;
688
689 /*
690 * The amount of required data depends on the file format and
691 * the current state. Wait for the availabilty of the desired
692 * data before processing it (to simplify data inspection
693 * code paths).
694 */
695 switch (inc->logic_state.stage) {
696 case STAGE_L1D_EVERY_VALUE:
697 want_len = inc->logic_state.word_size;
698 break;
699 case STAGE_L1D_CHANGE_INIT:
700 case STAGE_L1D_CHANGE_VALUE:
701 want_len = sizeof(uint64_t);
702 want_len += inc->logic_state.word_size;
703 break;
704 case STAGE_L1A_NEW_CHANNEL:
705 want_len = 0;
706 break;
707 case STAGE_L1A_SAMPLE:
708 want_len = sizeof(float);
709 break;
710 case STAGE_L2D_CHANGE_VALUE:
711 want_len = sizeof(double);
712 break;
713 case STAGE_L2A_FIRST_VALUE:
714 case STAGE_L2A_EVERY_VALUE:
715 want_len = sizeof(float);
716 break;
717 default:
718 return FALSE;
719 }
720 if (blen < want_len)
721 return FALSE;
722
723 /* Provide references to the next item, and the position after it. */
724 pos = buff;
725 if (curr)
726 *curr = pos;
727 pos += want_len;
728 if (next)
729 *next = pos;
730 return TRUE;
731}
732
733/* Process the next sample data item after it became available. */
734static int parse_next_item(struct sr_input *in,
735 const uint8_t *curr, size_t len)
736{
737 struct context *inc;
738 uint64_t next_stamp, count;
739 uint64_t digital;
740 float analog;
741 double next_time, diff_time;
742 int rc;
743
744 inc = in->priv;
745 (void)len;
746
747 /*
748 * The specific item to get processed next depends on the file
749 * format and current state.
750 */
751 switch (inc->logic_state.stage) {
752 case STAGE_L1D_CHANGE_INIT:
753 case STAGE_L1D_CHANGE_VALUE:
754 next_stamp = read_u64le_inc(&curr);
755 if (inc->logic_state.stage == STAGE_L1D_CHANGE_INIT) {
756 inc->feed.last.stamp = next_stamp;
757 inc->logic_state.stage = STAGE_L1D_CHANGE_VALUE;
758 }
759 count = next_stamp - inc->feed.last.stamp;
760 digital = inc->feed.last.digital;
761 rc = addto_feed_buffer_logic(in, digital, count);
762 if (rc)
763 return rc;
764 inc->feed.last.stamp = next_stamp - 1;
765 /* FALLTHROUGH */
766 case STAGE_L1D_EVERY_VALUE:
767 if (inc->logic_state.word_size == sizeof(uint8_t)) {
768 digital = read_u8_inc(&curr);
769 } else if (inc->logic_state.word_size == sizeof(uint16_t)) {
770 digital = read_u16le_inc(&curr);
771 } else if (inc->logic_state.word_size == sizeof(uint32_t)) {
772 digital = read_u32le_inc(&curr);
773 } else if (inc->logic_state.word_size == sizeof(uint64_t)) {
774 digital = read_u64le_inc(&curr);
775 } else {
776 /*
777 * In theory the sigrok input module could support
778 * arbitrary word sizes, but the Saleae exporter
779 * only provides the 8/16/32/64 choices anyway.
780 */
781 sr_err("Unsupported word size %zu.", inc->logic_state.word_size);
782 return SR_ERR_ARG;
783 }
784 rc = addto_feed_buffer_logic(in, digital, 1);
785 if (rc)
786 return rc;
787 inc->feed.last.digital = digital;
788 inc->feed.last.stamp++;
789 return SR_OK;
790 case STAGE_L1A_NEW_CHANNEL:
791 /* Just select the channel. Don't consume any data. */
792 rc = setup_feed_buffer_channel(in, inc->logic_state.l1a.current_channel_idx);
793 if (rc)
794 return rc;
795 inc->logic_state.l1a.current_channel_idx++;
796 inc->logic_state.l1a.current_per_channel = 0;
797 inc->logic_state.stage = STAGE_L1A_SAMPLE;
798 return SR_OK;
799 case STAGE_L1A_SAMPLE:
800 analog = read_fltle_inc(&curr);
801 rc = addto_feed_buffer_analog(in, analog, 1);
802 if (rc)
803 return rc;
804 inc->logic_state.l1a.current_per_channel++;
805 if (inc->logic_state.l1a.current_channel_idx == inc->logic_state.l1a.samples_per_channel)
806 inc->logic_state.stage = STAGE_L1A_NEW_CHANNEL;
807 return SR_OK;
808 case STAGE_L2D_CHANGE_VALUE:
809 next_time = read_dblle_inc(&curr);
810 diff_time = next_time - inc->feed.last.time;
811 if (inc->logic_state.l2d.min_time_step > diff_time)
812 inc->logic_state.l2d.min_time_step = diff_time;
813 diff_time /= inc->logic_state.l2d.sample_period;
814 diff_time += 0.5;
815 count = (uint64_t)diff_time;
52082147
GS
816 if (count) {
817 digital = inc->feed.last.digital;
818 rc = addto_feed_buffer_logic(in, digital, count);
819 if (rc)
820 return rc;
821 inc->feed.last.time = next_time;
822 }
d891892d
GS
823 inc->feed.last.digital = 1 - inc->feed.last.digital;
824 return SR_OK;
825 case STAGE_L2A_FIRST_VALUE:
826 case STAGE_L2A_EVERY_VALUE:
827 analog = read_fltle_inc(&curr);
828 if (inc->logic_state.stage == STAGE_L2A_FIRST_VALUE) {
829 rc = setup_feed_buffer_channel(in, 0);
830 if (rc)
831 return rc;
832 count = 1;
833 } else {
834 count = inc->logic_state.l2a.down_sample;
835 }
836 rc = addto_feed_buffer_analog(in, analog, 1);
837 if (rc)
838 return rc;
839 return SR_OK;
840
841 default:
842 (void)analog;
843 return SR_ERR_NA;
844 }
845 /* UNREACH */
846}
847
848static int parse_samples(struct sr_input *in)
849{
850 const uint8_t *buff, *start;
851 size_t blen;
852
853 const uint8_t *curr, *next;
854 size_t len;
855 int rc;
856
857 start = (const uint8_t *)in->buf->str;
858 buff = start;
859 blen = in->buf->len;
860 while (have_next_item(in, buff, blen, &curr, &next)) {
861 len = next - curr;
862 rc = parse_next_item(in, curr, len);
863 if (rc)
864 return rc;
865 buff += len;
866 blen -= len;
867 }
868 len = buff - start;
869 g_string_erase(in->buf, 0, len);
870
871 return SR_OK;
872}
873
874/*
875 * Try to auto detect an input's file format. Mismatch is non-fatal.
876 * Silent operation by design. Not all details need to be available.
877 * Get the strongest possible match in a best-effort manner.
878 *
879 * TODO Extend the .sal check when local file I/O becomes available.
880 * File extensions can lie, and need not be available. Check for a
881 * ZIP archive and the meta.json member in it.
882 */
883static int format_match(GHashTable *metadata, unsigned int *confidence)
884{
885 static const char *zip_ext = ".sal";
886 static const char *bin_ext = ".bin";
887
9084c396 888 gboolean matched;
d891892d
GS
889 const char *fn;
890 size_t fn_len, ext_len;
891 const char *ext_pos;
892 GString *buf;
893
9084c396
GS
894 matched = FALSE;
895
d891892d
GS
896 /* Weak match on the filename (when available). */
897 fn = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_FILENAME));
898 if (fn && *fn) {
899 fn_len = strlen(fn);
900 ext_len = strlen(zip_ext);
901 ext_pos = &fn[fn_len - ext_len];
902 if (fn_len >= ext_len && g_ascii_strcasecmp(ext_pos, zip_ext) == 0) {
9084c396 903 if (SALEAE_WITH_SAL_SUPPORT) {
d891892d 904 *confidence = 10;
9084c396
GS
905 matched = TRUE;
906 }
d891892d
GS
907 }
908 ext_len = strlen(bin_ext);
909 ext_pos = &fn[fn_len - ext_len];
910 if (fn_len >= ext_len && g_ascii_strcasecmp(ext_pos, bin_ext) == 0) {
911 *confidence = 50;
9084c396 912 matched = TRUE;
d891892d
GS
913 }
914 }
915
916 /* Stronger match when magic literals are found in file content. */
917 buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
918 if (!buf || !buf->len || !buf->str)
919 return SR_ERR_ARG;
920 switch (check_format((const uint8_t *)buf->str, buf->len)) {
921 case FMT_LOGIC2_DIGITAL:
922 case FMT_LOGIC2_ANALOG:
923 *confidence = 1;
9084c396 924 matched = TRUE;
d891892d
GS
925 break;
926 default:
927 /* EMPTY */
928 break;
929 }
930
9084c396 931 return matched ? SR_OK : SR_ERR_DATA;
d891892d
GS
932}
933
934static int init(struct sr_input *in, GHashTable *options)
935{
936 struct context *inc;
937 const char *type, *fmt_text;
938 enum logic_format format, fmt_idx;
939 gboolean changed;
940 size_t size, count;
941 uint64_t rate;
942
943 /* Allocate resources. */
944 in->sdi = g_malloc0(sizeof(*in->sdi));
945 inc = g_malloc0(sizeof(*inc));
946 in->priv = inc;
947
948 /* Get caller provided specs, dump before check. */
949 type = g_variant_get_string(g_hash_table_lookup(options, "format"), NULL);
950 changed = g_variant_get_boolean(g_hash_table_lookup(options, "changed"));
951 size = g_variant_get_uint32(g_hash_table_lookup(options, "wordsize"));
952 count = g_variant_get_uint32(g_hash_table_lookup(options, "logic_channels"));
953 rate = g_variant_get_uint64(g_hash_table_lookup(options, "samplerate"));
954 sr_dbg("Caller options: type '%s', changed %d, wordsize %zu, channels %zu, rate %" PRIu64 ".",
955 type, changed ? 1 : 0, size, count, rate);
956
957 /* Run a few simple checks. Normalization is done in .init(). */
958 format = FMT_UNKNOWN;
959 for (fmt_idx = FMT_AUTO_DETECT; fmt_idx < ARRAY_SIZE(format_texts); fmt_idx++) {
960 fmt_text = format_texts[fmt_idx];
961 if (!fmt_text || !*fmt_text)
962 continue;
963 if (g_ascii_strcasecmp(type, fmt_text) != 0)
964 continue;
965 format = fmt_idx;
966 break;
967 }
968 if (format == FMT_UNKNOWN) {
969 sr_err("Unknown file type name: '%s'.", type);
970 return SR_ERR_ARG;
971 }
972 if (!size) {
973 sr_err("Need a word size.");
974 return SR_ERR_ARG;
975 }
976
977 /*
978 * Keep input specs around. We never get back to .init() even
979 * when input files are re-read later.
980 */
981 inc->options.format = format;
982 inc->options.when_changed = !!changed;
983 inc->options.word_size = size;
984 inc->options.channel_count = count;
985 inc->options.sample_rate = rate;
986 sr_dbg("Resulting options: type '%s', changed %d",
987 get_format_text(format), changed ? 1 : 0);
988
989 return SR_OK;
990}
991
992static int receive(struct sr_input *in, GString *buf)
993{
994 struct context *inc;
995 int rc;
996 const char *text;
997
998 inc = in->priv;
999
1000 /* Accumulate another chunk of input data. */
1001 g_string_append_len(in->buf, buf->str, buf->len);
1002
1003 /*
1004 * Wait for the full header's availability, then process it in
1005 * a single call, and set the "ready" flag. Make sure sample data
1006 * and the header get processed in disjoint receive() calls, the
1007 * backend requires those separate phases.
1008 */
1009 if (!inc->module_state.got_header) {
1010 if (!have_header(inc, in->buf))
1011 return SR_OK;
1012 rc = parse_header(in);
1013 if (rc)
1014 return rc;
1015 inc->module_state.got_header = TRUE;
1016 text = get_format_text(inc->logic_state.format) ? : "<unknown>";
1017 sr_info("Using file format: '%s'.", text);
1018 rc = create_channels(in);
1019 if (rc)
1020 return rc;
1021 rc = alloc_feed_buffer(in);
1022 if (rc)
1023 return rc;
1024 in->sdi_ready = TRUE;
1025 return SR_OK;
1026 }
1027
1028 /* Process sample data, after the header got processed. */
1029 return parse_samples(in);
1030}
1031
1032static int end(struct sr_input *in)
1033{
1034 struct context *inc;
1035 int rc;
1036
1037 /* Nothing to do here if we never started feeding the session. */
1038 if (!in->sdi_ready)
1039 return SR_OK;
1040
1041 /*
1042 * Process input data which may not have been inspected before.
1043 * Flush any potentially queued samples.
1044 */
1045 rc = parse_samples(in);
1046 if (rc)
1047 return rc;
1048 rc = flush_feed_buffer(in);
1049 if (rc)
1050 return rc;
1051
1052 /* End the session feed if one was started. */
1053 inc = in->priv;
1054 if (inc->module_state.header_sent) {
1055 rc = std_session_send_df_end(in->sdi);
1056 if (rc)
1057 return rc;
1058 inc->module_state.header_sent = FALSE;
1059 }
1060
1061 /* Input data shall be exhausted by now. Non-fatal condition. */
1062 if (in->buf->len)
1063 sr_warn("Unprocessed remaining input: %zu bytes.", in->buf->len);
1064
1065 return SR_OK;
1066}
1067
1068static void cleanup(struct sr_input *in)
1069{
1070 struct context *inc;
1071 struct context_options save_opts;
003ad0ab 1072 GSList *save_channels;
d891892d
GS
1073
1074 if (!in)
1075 return;
1076 inc = in->priv;
1077 if (!inc)
1078 return;
1079
1080 /* Keep references to previously created channels. */
1081 g_slist_free_full(inc->module_state.prev_channels, sr_channel_free_cb);
1082 inc->module_state.prev_channels = in->sdi->channels;
1083 in->sdi->channels = NULL;
1084
1085 /* Release dynamically allocated resources. */
1086 relse_feed_buffer(in);
1087
1088 /* Clear internal state, but keep what .init() has provided. */
1089 save_opts = inc->options;
003ad0ab 1090 save_channels = inc->module_state.prev_channels;
d891892d
GS
1091 memset(inc, 0, sizeof(*inc));
1092 inc->options = save_opts;
003ad0ab 1093 inc->module_state.prev_channels = save_channels;
d891892d
GS
1094}
1095
1096static int reset(struct sr_input *in)
1097{
1098 struct context *inc;
1099
1100 inc = in->priv;
1101
1102 /*
1103 * The input module's .reset() routine clears the 'inc' context.
1104 * But 'in' is kept which contains channel groups which reference
1105 * channels. We cannot re-create the channels, since applications
1106 * still reference them and expect us to keep them. The .cleanup()
1107 * routine also keeps the user specified option values, the module
1108 * will derive internal state again when the input gets re-read.
1109 */
1110 cleanup(in);
1111 in->sdi->channels = inc->module_state.prev_channels;
003ad0ab 1112 inc->module_state.prev_channels = NULL;
d891892d
GS
1113
1114 inc->module_state.got_header = FALSE;
1115 inc->module_state.header_sent = FALSE;
1116 inc->module_state.rate_sent = FALSE;
1117 g_string_truncate(in->buf, 0);
1118
1119 return SR_OK;
1120}
1121
1122enum option_index {
1123 OPT_FMT_TYPE,
1124 OPT_CHANGE,
1125 OPT_WORD_SIZE,
1126 OPT_NUM_LOGIC,
1127 OPT_SAMPLERATE,
1128 OPT_MAX,
1129};
1130
1131static struct sr_option options[] = {
1132 [OPT_FMT_TYPE] = {
1133 "format", "File format.",
1134 "Type of input file format. Not all types can get auto-detected.",
1135 NULL, NULL,
1136 },
1137 [OPT_CHANGE] = {
1138 "changed", "Save when changed.",
1139 "Sample value was saved when changed (in contrast to: every sample).",
1140 NULL, NULL,
1141 },
1142 [OPT_WORD_SIZE] = {
1143 "wordsize", "Word size.",
1144 "The number of bits per set of samples for digital data.",
1145 NULL, NULL,
1146 },
1147 [OPT_NUM_LOGIC] = {
1148 "logic_channels", "Channel count.",
1149 "The number of digital channels. Word size is used when not specified.",
1150 NULL, NULL,
1151 },
1152 [OPT_SAMPLERATE] = {
1153 "samplerate", "Samplerate.",
1154 "The samplerate. Needed when the file content lacks this information.",
1155 NULL, NULL,
1156 },
1157 [OPT_MAX] = ALL_ZERO,
1158};
1159
1160static const struct sr_option *get_options(void)
1161{
1162 enum logic_format fmt_idx;
1163 const char *fmt_text;
1164 size_t word_size;
1165 GSList *l;
1166
1167 /* Been here before? Already assigned default values? */
1168 if (options[0].def)
1169 return options;
1170
1171 /* Assign default values, and list choices to select from. */
1172 fmt_text = format_texts[FMT_AUTO_DETECT];
1173 options[OPT_FMT_TYPE].def = g_variant_ref_sink(g_variant_new_string(fmt_text));
1174 l = NULL;
1175 for (fmt_idx = FMT_AUTO_DETECT; fmt_idx < ARRAY_SIZE(format_texts); fmt_idx++) {
1176 fmt_text = format_texts[fmt_idx];
1177 if (!fmt_text || !*fmt_text)
1178 continue;
1179 l = g_slist_append(l, g_variant_ref_sink(g_variant_new_string(fmt_text)));
1180 }
1181 options[OPT_FMT_TYPE].values = l;
1182 options[OPT_CHANGE].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
1183 options[OPT_WORD_SIZE].def = g_variant_ref_sink(g_variant_new_uint32(8));
1184 l = NULL;
1185 for (word_size = sizeof(uint8_t); word_size <= sizeof(uint64_t); word_size *= 2)
1186 l = g_slist_append(l, g_variant_ref_sink(g_variant_new_uint32(8 * word_size)));
1187 options[OPT_WORD_SIZE].values = l;
1188 options[OPT_NUM_LOGIC].def = g_variant_ref_sink(g_variant_new_uint32(0));
1189 options[OPT_SAMPLERATE].def = g_variant_ref_sink(g_variant_new_uint64(0));
1190
1191 return options;
1192}
1193
1194SR_PRIV struct sr_input_module input_saleae = {
1195 .id = "saleae",
1196 .name = "Saleae",
1197#if SALEAE_WITH_SAL_SUPPORT
1198 .desc = "Saleae Logic software export/save files",
1199 .exts = (const char *[]){"bin", "sal", NULL},
1200#else
1201 .desc = "Saleae Logic software export files",
1202 .exts = (const char *[]){"bin", NULL},
1203#endif
1204 .metadata = {
1205 SR_INPUT_META_FILENAME,
1206 SR_INPUT_META_HEADER | SR_INPUT_META_REQUIRED
1207 },
1208 .options = get_options,
1209 .format_match = format_match,
1210 .init = init,
1211 .receive = receive,
1212 .end = end,
1213 .cleanup = cleanup,
1214 .reset = reset,
1215};