]> sigrok.org Git - libsigrok.git/blob - src/output/srzip.c
output/csv: use intermediate time_t var, silence compiler warning
[libsigrok.git] / src / output / srzip.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
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 3 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 #include <config.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <glib.h>
25 #include <glib/gstdio.h>
26 #include <zip.h>
27 #include <libsigrok/libsigrok.h>
28 #include "libsigrok-internal.h"
29
30 #define LOG_PREFIX "output/srzip"
31 #define CHUNK_SIZE (4 * 1024 * 1024)
32
33 struct out_context {
34         gboolean zip_created;
35         uint64_t samplerate;
36         char *filename;
37         size_t first_analog_index;
38         size_t analog_ch_count;
39         gint *analog_index_map;
40         struct logic_buff {
41                 size_t zip_unit_size;
42                 size_t alloc_size;
43                 uint8_t *samples;
44                 size_t fill_size;
45         } logic_buff;
46         struct analog_buff {
47                 size_t alloc_size;
48                 float *samples;
49                 size_t fill_size;
50         } *analog_buff;
51 };
52
53 static int init(struct sr_output *o, GHashTable *options)
54 {
55         struct out_context *outc;
56
57         (void)options;
58
59         if (!o->filename || o->filename[0] == '\0') {
60                 sr_info("srzip output module requires a file name, cannot save.");
61                 return SR_ERR_ARG;
62         }
63
64         outc = g_malloc0(sizeof(*outc));
65         outc->filename = g_strdup(o->filename);
66         o->priv = outc;
67
68         return SR_OK;
69 }
70
71 static int zip_create(const struct sr_output *o)
72 {
73         struct out_context *outc;
74         struct zip *zipfile;
75         struct zip_source *versrc, *metasrc;
76         struct sr_channel *ch;
77         size_t ch_nr;
78         size_t alloc_size;
79         GVariant *gvar;
80         GKeyFile *meta;
81         GSList *l;
82         const char *devgroup;
83         char *s, *metabuf;
84         gsize metalen;
85         guint logic_channels, enabled_logic_channels;
86         guint enabled_analog_channels;
87         guint index;
88
89         outc = o->priv;
90
91         if (outc->samplerate == 0 && sr_config_get(o->sdi->driver, o->sdi, NULL,
92                                         SR_CONF_SAMPLERATE, &gvar) == SR_OK) {
93                 outc->samplerate = g_variant_get_uint64(gvar);
94                 g_variant_unref(gvar);
95         }
96
97         /* Quietly delete it first, libzip wants replace ops otherwise. */
98         g_unlink(outc->filename);
99         zipfile = zip_open(outc->filename, ZIP_CREATE, NULL);
100         if (!zipfile)
101                 return SR_ERR;
102
103         /* "version" */
104         versrc = zip_source_buffer(zipfile, "2", 1, FALSE);
105         if (zip_add(zipfile, "version", versrc) < 0) {
106                 sr_err("Error saving version into zipfile: %s",
107                         zip_strerror(zipfile));
108                 zip_source_free(versrc);
109                 zip_discard(zipfile);
110                 return SR_ERR;
111         }
112
113         /* init "metadata" */
114         meta = g_key_file_new();
115
116         g_key_file_set_string(meta, "global", "sigrok version",
117                         sr_package_version_string_get());
118
119         devgroup = "device 1";
120
121         logic_channels = 0;
122         enabled_logic_channels = 0;
123         enabled_analog_channels = 0;
124         for (l = o->sdi->channels; l; l = l->next) {
125                 ch = l->data;
126
127                 switch (ch->type) {
128                 case SR_CHANNEL_LOGIC:
129                         if (ch->enabled)
130                                 enabled_logic_channels++;
131                         logic_channels++;
132                         break;
133                 case SR_CHANNEL_ANALOG:
134                         if (ch->enabled)
135                                 enabled_analog_channels++;
136                         break;
137                 }
138         }
139
140         /* When reading the file, the first index of the analog channels
141          * can only be deduced through the "total probes" count, so the
142          * first analog index must follow the last logic one, enabled or not. */
143         if (enabled_logic_channels > 0)
144                 outc->first_analog_index = logic_channels + 1;
145         else
146                 outc->first_analog_index = 1;
147
148         /* Only set capturefile and probes if we will actually save logic data. */
149         if (enabled_logic_channels > 0) {
150                 g_key_file_set_string(meta, devgroup, "capturefile", "logic-1");
151                 g_key_file_set_integer(meta, devgroup, "total probes", logic_channels);
152         }
153
154         s = sr_samplerate_string(outc->samplerate);
155         g_key_file_set_string(meta, devgroup, "samplerate", s);
156         g_free(s);
157
158         g_key_file_set_integer(meta, devgroup, "total analog", enabled_analog_channels);
159
160         outc->analog_ch_count = enabled_analog_channels;
161         alloc_size = sizeof(gint) * outc->analog_ch_count + 1;
162         outc->analog_index_map = g_malloc0(alloc_size);
163
164         index = 0;
165         for (l = o->sdi->channels; l; l = l->next) {
166                 ch = l->data;
167                 if (!ch->enabled)
168                         continue;
169
170                 s = NULL;
171                 switch (ch->type) {
172                 case SR_CHANNEL_LOGIC:
173                         ch_nr = ch->index + 1;
174                         s = g_strdup_printf("probe%zu", ch_nr);
175                         break;
176                 case SR_CHANNEL_ANALOG:
177                         ch_nr = outc->first_analog_index + index;
178                         outc->analog_index_map[index] = ch->index;
179                         s = g_strdup_printf("analog%zu", ch_nr);
180                         index++;
181                         break;
182                 }
183                 if (s) {
184                         g_key_file_set_string(meta, devgroup, s, ch->name);
185                         g_free(s);
186                 }
187         }
188
189         /*
190          * Allocate one samples buffer for all logic channels, and
191          * several samples buffers for the analog channels. Allocate
192          * buffers of CHUNK_SIZE size (in bytes), and determine the
193          * sample counts from the respective channel counts and data
194          * type widths.
195          *
196          * These buffers are intended to reduce the number of ZIP
197          * archive update calls, and decouple the srzip output module
198          * from implementation details in other acquisition device
199          * drivers and input modules.
200          *
201          * Avoid allocating zero bytes, to not depend on platform
202          * specific malloc(0) return behaviour. Avoid division by zero,
203          * holding a local buffer won't harm when no data is seen later
204          * during execution. This simplifies other locations.
205          */
206         alloc_size = CHUNK_SIZE;
207         outc->logic_buff.zip_unit_size = logic_channels;
208         outc->logic_buff.zip_unit_size += 8 - 1;
209         outc->logic_buff.zip_unit_size /= 8;
210         outc->logic_buff.samples = g_try_malloc0(alloc_size);
211         if (!outc->logic_buff.samples)
212                 return SR_ERR_MALLOC;
213         if (outc->logic_buff.zip_unit_size)
214                 alloc_size /= outc->logic_buff.zip_unit_size;
215         outc->logic_buff.alloc_size = alloc_size;
216         outc->logic_buff.fill_size = 0;
217
218         alloc_size = sizeof(outc->analog_buff[0]) * outc->analog_ch_count + 1;
219         outc->analog_buff = g_malloc0(alloc_size);
220         for (index = 0; index < outc->analog_ch_count; index++) {
221                 alloc_size = CHUNK_SIZE;
222                 outc->analog_buff[index].samples = g_try_malloc0(alloc_size);
223                 if (!outc->analog_buff[index].samples)
224                         return SR_ERR_MALLOC;
225                 alloc_size /= sizeof(outc->analog_buff[0].samples[0]);
226                 outc->analog_buff[index].alloc_size = alloc_size;
227                 outc->analog_buff[index].fill_size = 0;
228         }
229
230         metabuf = g_key_file_to_data(meta, &metalen, NULL);
231         g_key_file_free(meta);
232
233         metasrc = zip_source_buffer(zipfile, metabuf, metalen, FALSE);
234         if (zip_add(zipfile, "metadata", metasrc) < 0) {
235                 sr_err("Error saving metadata into zipfile: %s",
236                         zip_strerror(zipfile));
237                 zip_source_free(metasrc);
238                 zip_discard(zipfile);
239                 g_free(metabuf);
240                 return SR_ERR;
241         }
242
243         if (zip_close(zipfile) < 0) {
244                 sr_err("Error saving zipfile: %s", zip_strerror(zipfile));
245                 zip_discard(zipfile);
246                 g_free(metabuf);
247                 return SR_ERR;
248         }
249         g_free(metabuf);
250
251         return SR_OK;
252 }
253
254 /**
255  * Append a block of logic data to an srzip archive.
256  *
257  * @param[in] o Output module instance.
258  * @param[in] buf Logic data samples as byte sequence.
259  * @param[in] unitsize Logic data unit size (bytes per sample).
260  * @param[in] length Byte sequence length (in bytes, not samples).
261  *
262  * @returns SR_OK et al error codes.
263  */
264 static int zip_append(const struct sr_output *o,
265         uint8_t *buf, size_t unitsize, size_t length)
266 {
267         struct out_context *outc;
268         struct zip *archive;
269         struct zip_source *logicsrc;
270         int64_t i, num_files;
271         struct zip_stat zs;
272         struct zip_source *metasrc;
273         GKeyFile *kf;
274         GError *error;
275         uint64_t chunk_num;
276         const char *entry_name;
277         char *metabuf;
278         gsize metalen;
279         char *chunkname;
280         unsigned int next_chunk_num;
281
282         if (!length)
283                 return SR_OK;
284
285         outc = o->priv;
286         if (!(archive = zip_open(outc->filename, 0, NULL)))
287                 return SR_ERR;
288
289         if (zip_stat(archive, "metadata", 0, &zs) < 0) {
290                 sr_err("Failed to open metadata: %s", zip_strerror(archive));
291                 zip_discard(archive);
292                 return SR_ERR;
293         }
294         kf = sr_sessionfile_read_metadata(archive, &zs);
295         if (!kf) {
296                 zip_discard(archive);
297                 return SR_ERR_DATA;
298         }
299         /*
300          * If the file was only initialized but doesn't yet have any
301          * data it in, it won't have a unitsize field in metadata yet.
302          */
303         error = NULL;
304         metabuf = NULL;
305         if (!g_key_file_has_key(kf, "device 1", "unitsize", &error)) {
306                 if (error && error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
307                         sr_err("Failed to check unitsize key: %s", error->message);
308                         g_error_free(error);
309                         g_key_file_free(kf);
310                         zip_discard(archive);
311                         return SR_ERR;
312                 }
313                 g_clear_error(&error);
314
315                 /* Add unitsize field. */
316                 g_key_file_set_integer(kf, "device 1", "unitsize", unitsize);
317                 metabuf = g_key_file_to_data(kf, &metalen, NULL);
318                 metasrc = zip_source_buffer(archive, metabuf, metalen, FALSE);
319
320                 if (zip_replace(archive, zs.index, metasrc) < 0) {
321                         sr_err("Failed to replace metadata: %s",
322                                 zip_strerror(archive));
323                         g_key_file_free(kf);
324                         zip_source_free(metasrc);
325                         zip_discard(archive);
326                         g_free(metabuf);
327                         return SR_ERR;
328                 }
329         }
330         g_key_file_free(kf);
331
332         next_chunk_num = 1;
333         num_files = zip_get_num_entries(archive, 0);
334         for (i = 0; i < num_files; i++) {
335                 entry_name = zip_get_name(archive, i, 0);
336                 if (!entry_name || strncmp(entry_name, "logic-1", 7) != 0)
337                         continue;
338                 if (entry_name[7] == '\0') {
339                         /*
340                          * This file has no extra chunks, just a single
341                          * "logic-1". Rename it to "logic-1-1" and continue
342                          * with chunk 2.
343                          */
344                         if (zip_rename(archive, i, "logic-1-1") < 0) {
345                                 sr_err("Failed to rename 'logic-1' to 'logic-1-1': %s",
346                                         zip_strerror(archive));
347                                 zip_discard(archive);
348                                 g_free(metabuf);
349                                 return SR_ERR;
350                         }
351                         next_chunk_num = 2;
352                         break;
353                 } else if (entry_name[7] == '-') {
354                         chunk_num = g_ascii_strtoull(entry_name + 8, NULL, 10);
355                         if (chunk_num < G_MAXINT && chunk_num >= next_chunk_num)
356                                 next_chunk_num = chunk_num + 1;
357                 }
358         }
359
360         if (length % unitsize != 0) {
361                 sr_warn("Chunk size %zu not a multiple of the"
362                         " unit size %zu.", length, unitsize);
363         }
364         logicsrc = zip_source_buffer(archive, buf, length, FALSE);
365         chunkname = g_strdup_printf("logic-1-%u", next_chunk_num);
366         i = zip_add(archive, chunkname, logicsrc);
367         g_free(chunkname);
368         if (i < 0) {
369                 sr_err("Failed to add chunk 'logic-1-%u': %s",
370                         next_chunk_num, zip_strerror(archive));
371                 zip_source_free(logicsrc);
372                 zip_discard(archive);
373                 g_free(metabuf);
374                 return SR_ERR;
375         }
376         if (zip_close(archive) < 0) {
377                 sr_err("Error saving session file: %s", zip_strerror(archive));
378                 zip_discard(archive);
379                 g_free(metabuf);
380                 return SR_ERR;
381         }
382         g_free(metabuf);
383
384         return SR_OK;
385 }
386
387 /**
388  * Queue a block of logic data for srzip archive writes.
389  *
390  * @param[in] o Output module instance.
391  * @param[in] buf Logic data samples as byte sequence.
392  * @param[in] unitsize Logic data unit size (bytes per sample).
393  * @param[in] length Number of bytes of sample data.
394  * @param[in] flush Force ZIP archive update (queue by default).
395  *
396  * @returns SR_OK et al error codes.
397  */
398 static int zip_append_queue(const struct sr_output *o,
399         const uint8_t *buf, size_t feed_unitsize, size_t length,
400         gboolean flush)
401 {
402         static gboolean sizes_seen;
403
404         struct out_context *outc;
405         struct logic_buff *buff;
406         size_t sample_copy_size, sample_skip_size, sample_pad_size;
407         size_t send_count, remain, copy_count;
408         const uint8_t *rdptr;
409         uint8_t *wrptr;
410         int ret;
411
412         /*
413          * Check input parameters. Prepare to either grab data as is,
414          * or to adjust between differing input and output unit sizes.
415          * Diagnostics is rate limited for improved usability, assumes
416          * that session feeds are consistent across calls. Processing
417          * would cope with inconsistent calls though when required.
418          */
419         outc = o->priv;
420         buff = &outc->logic_buff;
421         if (length) {
422                 if (!sizes_seen) {
423                         sr_info("output unit size %zu, feed unit size %zu.",
424                                 buff->zip_unit_size, feed_unitsize);
425                 }
426                 if (feed_unitsize > buff->zip_unit_size) {
427                         if (!sizes_seen)
428                                 sr_info("Large unit size, discarding excess logic data.");
429                         sample_copy_size = buff->zip_unit_size;
430                         sample_skip_size = feed_unitsize - buff->zip_unit_size;
431                         sample_pad_size = 0;
432                 } else if (feed_unitsize < buff->zip_unit_size) {
433                         if (!sizes_seen)
434                                 sr_info("Small unit size, padding logic data.");
435                         sample_copy_size = feed_unitsize;
436                         sample_skip_size = 0;
437                         sample_pad_size = buff->zip_unit_size - feed_unitsize;
438                 } else {
439                         if (!sizes_seen)
440                                 sr_dbg("Matching unit size, passing logic data as is.");
441                         sample_copy_size = buff->zip_unit_size;
442                         sample_skip_size = 0;
443                         sample_pad_size = 0;
444                 }
445                 if (sample_copy_size + sample_skip_size != feed_unitsize) {
446                         sr_err("Inconsistent input unit size. Implementation flaw?");
447                         return SR_ERR_BUG;
448                 }
449                 if (sample_copy_size + sample_pad_size != buff->zip_unit_size) {
450                         sr_err("Inconsistent output unit size. Implementation flaw?");
451                         return SR_ERR_BUG;
452                 }
453                 sizes_seen = TRUE;
454         }
455
456         /*
457          * Queue most recently received samples to the local buffer.
458          * Flush to the ZIP archive when the buffer space is exhausted.
459          */
460         rdptr = buf;
461         send_count = feed_unitsize ? length / feed_unitsize : 0;
462         while (send_count) {
463                 remain = buff->alloc_size - buff->fill_size;
464                 wrptr = &buff->samples[buff->fill_size * buff->zip_unit_size];
465                 if (remain) {
466                         copy_count = MIN(send_count, remain);
467                         if (sample_skip_size || sample_pad_size)
468                                 copy_count = 1;
469                         send_count -= copy_count;
470                         buff->fill_size += copy_count;
471                         memcpy(wrptr, rdptr, copy_count * sample_copy_size);
472                         if (sample_pad_size) {
473                                 wrptr += sample_copy_size;
474                                 memset(wrptr, 0, sample_pad_size);
475                         }
476                         rdptr += copy_count * sample_copy_size;
477                         if (sample_skip_size)
478                                 rdptr += sample_skip_size;
479                         remain -= copy_count;
480                 }
481                 if (send_count && !remain) {
482                         ret = zip_append(o, buff->samples, buff->zip_unit_size,
483                                 buff->fill_size * buff->zip_unit_size);
484                         if (ret != SR_OK)
485                                 return ret;
486                         buff->fill_size = 0;
487                 }
488         }
489
490         /* Flush to the ZIP archive if the caller wants us to. */
491         if (flush && buff->fill_size) {
492                 ret = zip_append(o, buff->samples, buff->zip_unit_size,
493                         buff->fill_size * buff->zip_unit_size);
494                 if (ret != SR_OK)
495                         return ret;
496                 buff->fill_size = 0;
497         }
498
499         return SR_OK;
500 }
501
502 /**
503  * Append analog data of a channel to an srzip archive.
504  *
505  * @param[in] o Output module instance.
506  * @param[in] values Sample data as array of floating point values.
507  * @param[in] count Number of samples (float items, not bytes).
508  * @param[in] ch_nr 1-based channel number.
509  *
510  * @returns SR_OK et al error codes.
511  */
512 static int zip_append_analog(const struct sr_output *o,
513         const float *values, size_t count, size_t ch_nr)
514 {
515         struct out_context *outc;
516         struct zip *archive;
517         struct zip_source *analogsrc;
518         int64_t i, num_files;
519         size_t size;
520         struct zip_stat zs;
521         uint64_t chunk_num;
522         const char *entry_name;
523         char *basename;
524         gsize baselen;
525         char *chunkname;
526         unsigned int next_chunk_num;
527
528         outc = o->priv;
529
530         if (!(archive = zip_open(outc->filename, 0, NULL)))
531                 return SR_ERR;
532
533         if (zip_stat(archive, "metadata", 0, &zs) < 0) {
534                 sr_err("Failed to open metadata: %s", zip_strerror(archive));
535                 zip_discard(archive);
536                 return SR_ERR;
537         }
538
539         basename = g_strdup_printf("analog-1-%zu", ch_nr);
540         baselen = strlen(basename);
541         next_chunk_num = 1;
542         num_files = zip_get_num_entries(archive, 0);
543         for (i = 0; i < num_files; i++) {
544                 entry_name = zip_get_name(archive, i, 0);
545                 if (!entry_name || strncmp(entry_name, basename, baselen) != 0) {
546                         continue;
547                 } else if (entry_name[baselen] == '-') {
548                         chunk_num = g_ascii_strtoull(entry_name + baselen + 1, NULL, 10);
549                         if (chunk_num < G_MAXINT && chunk_num >= next_chunk_num)
550                                 next_chunk_num = chunk_num + 1;
551                 }
552         }
553
554         size = sizeof(values[0]) * count;
555         analogsrc = zip_source_buffer(archive, values, size, FALSE);
556         chunkname = g_strdup_printf("%s-%u", basename, next_chunk_num);
557         i = zip_add(archive, chunkname, analogsrc);
558         if (i < 0) {
559                 sr_err("Failed to add chunk '%s': %s", chunkname, zip_strerror(archive));
560                 g_free(chunkname);
561                 g_free(basename);
562                 zip_source_free(analogsrc);
563                 zip_discard(archive);
564                 return SR_ERR;
565         }
566         g_free(chunkname);
567         if (zip_close(archive) < 0) {
568                 sr_err("Error saving session file: %s", zip_strerror(archive));
569                 g_free(basename);
570                 zip_discard(archive);
571                 return SR_ERR;
572         }
573
574         g_free(basename);
575
576         return SR_OK;
577 }
578
579 /**
580  * Queue analog data of a channel for srzip archive writes.
581  *
582  * @param[in] o Output module instance.
583  * @param[in] analog Sample data (session feed packet format).
584  * @param[in] flush Force ZIP archive update (queue by default).
585  *
586  * @returns SR_OK et al error codes.
587  */
588 static int zip_append_analog_queue(const struct sr_output *o,
589         const struct sr_datafeed_analog *analog, gboolean flush)
590 {
591         struct out_context *outc;
592         const struct sr_channel *ch;
593         size_t idx, nr;
594         struct analog_buff *buff;
595         float *values, *wrptr, *rdptr;
596         size_t send_size, remain, copy_size;
597         int ret;
598
599         outc = o->priv;
600
601         /* Is this the DF_END flush call without samples submission? */
602         if (!analog && flush) {
603                 for (idx = 0; idx < outc->analog_ch_count; idx++) {
604                         nr = outc->first_analog_index + idx;
605                         buff = &outc->analog_buff[idx];
606                         if (!buff->fill_size)
607                                 continue;
608                         ret = zip_append_analog(o,
609                                 buff->samples, buff->fill_size, nr);
610                         if (ret != SR_OK)
611                                 return ret;
612                         buff->fill_size = 0;
613                 }
614                 return SR_OK;
615         }
616
617         /* Lookup index and number of the analog channel. */
618         /* TODO: support packets covering multiple channels */
619         if (g_slist_length(analog->meaning->channels) != 1) {
620                 sr_err("Analog packets covering multiple channels not supported yet");
621                 return SR_ERR;
622         }
623         ch = g_slist_nth_data(analog->meaning->channels, 0);
624         for (idx = 0; idx < outc->analog_ch_count; idx++) {
625                 if (outc->analog_index_map[idx] == ch->index)
626                         break;
627         }
628         if (idx == outc->analog_ch_count)
629                 return SR_ERR_ARG;
630         nr = outc->first_analog_index + idx;
631         buff = &outc->analog_buff[idx];
632
633         /* Convert the analog data to an array of float values. */
634         values = g_try_malloc0(analog->num_samples * sizeof(values[0]));
635         if (!values)
636                 return SR_ERR_MALLOC;
637         ret = sr_analog_to_float(analog, values);
638         if (ret != SR_OK) {
639                 g_free(values);
640                 return ret;
641         }
642
643         /*
644          * Queue most recently received samples to the local buffer.
645          * Flush to the ZIP archive when the buffer space is exhausted.
646          */
647         rdptr = values;
648         send_size = analog->num_samples;
649         while (send_size) {
650                 remain = buff->alloc_size - buff->fill_size;
651                 if (remain) {
652                         wrptr = &buff->samples[buff->fill_size];
653                         copy_size = MIN(send_size, remain);
654                         send_size -= copy_size;
655                         buff->fill_size += copy_size;
656                         memcpy(wrptr, rdptr, copy_size * sizeof(values[0]));
657                         rdptr += copy_size;
658                         remain -= copy_size;
659                 }
660                 if (send_size && !remain) {
661                         ret = zip_append_analog(o,
662                                 buff->samples, buff->fill_size, nr);
663                         if (ret != SR_OK) {
664                                 g_free(values);
665                                 return ret;
666                         }
667                         buff->fill_size = 0;
668                         remain = buff->alloc_size - buff->fill_size;
669                 }
670         }
671         g_free(values);
672
673         /* Flush to the ZIP archive if the caller wants us to. */
674         if (flush && buff->fill_size) {
675                 ret = zip_append_analog(o, buff->samples, buff->fill_size, nr);
676                 if (ret != SR_OK)
677                         return ret;
678                 buff->fill_size = 0;
679         }
680
681         return SR_OK;
682 }
683
684 static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
685                 GString **out)
686 {
687         struct out_context *outc;
688         const struct sr_datafeed_meta *meta;
689         const struct sr_datafeed_logic *logic;
690         const struct sr_datafeed_analog *analog;
691         const struct sr_config *src;
692         GSList *l;
693         int ret;
694
695         *out = NULL;
696         if (!o || !o->sdi || !(outc = o->priv))
697                 return SR_ERR_ARG;
698
699         switch (packet->type) {
700         case SR_DF_META:
701                 meta = packet->payload;
702                 for (l = meta->config; l; l = l->next) {
703                         src = l->data;
704                         if (src->key != SR_CONF_SAMPLERATE)
705                                 continue;
706                         outc->samplerate = g_variant_get_uint64(src->data);
707                 }
708                 break;
709         case SR_DF_LOGIC:
710                 if (!outc->zip_created) {
711                         if ((ret = zip_create(o)) != SR_OK)
712                                 return ret;
713                         outc->zip_created = TRUE;
714                 }
715                 logic = packet->payload;
716                 ret = zip_append_queue(o,
717                         logic->data, logic->unitsize, logic->length,
718                         FALSE);
719                 if (ret != SR_OK)
720                         return ret;
721                 break;
722         case SR_DF_ANALOG:
723                 if (!outc->zip_created) {
724                         if ((ret = zip_create(o)) != SR_OK)
725                                 return ret;
726                         outc->zip_created = TRUE;
727                 }
728                 analog = packet->payload;
729                 ret = zip_append_analog_queue(o, analog, FALSE);
730                 if (ret != SR_OK)
731                         return ret;
732                 break;
733         case SR_DF_END:
734                 if (outc->zip_created) {
735                         ret = zip_append_queue(o, NULL, 0, 0, TRUE);
736                         if (ret != SR_OK)
737                                 return ret;
738                         ret = zip_append_analog_queue(o, NULL, TRUE);
739                         if (ret != SR_OK)
740                                 return ret;
741                 }
742                 break;
743         }
744
745         return SR_OK;
746 }
747
748 static struct sr_option options[] = {
749         ALL_ZERO
750 };
751
752 static const struct sr_option *get_options(void)
753 {
754         return options;
755 }
756
757 static int cleanup(struct sr_output *o)
758 {
759         struct out_context *outc;
760         size_t idx;
761
762         outc = o->priv;
763
764         g_free(outc->analog_index_map);
765         g_free(outc->filename);
766         g_free(outc->logic_buff.samples);
767         for (idx = 0; idx < outc->analog_ch_count; idx++)
768                 g_free(outc->analog_buff[idx].samples);
769         g_free(outc->analog_buff);
770
771         g_free(outc);
772         o->priv = NULL;
773
774         return SR_OK;
775 }
776
777 SR_PRIV struct sr_output_module output_srzip = {
778         .id = "srzip",
779         .name = "srzip",
780         .desc = "srzip session file format data",
781         .exts = (const char*[]){"sr", NULL},
782         .flags = SR_OUTPUT_INTERNAL_IO_HANDLING,
783         .options = get_options,
784         .init = init,
785         .receive = receive,
786         .cleanup = cleanup,
787 };