]> sigrok.org Git - libsigrok.git/blob - src/output/srzip.c
korad-kaxxxxp: use ID text prefix with optional version for RND models
[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 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.unit_size = logic_channels;
208         outc->logic_buff.unit_size += 8 - 1;
209         outc->logic_buff.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.unit_size)
214                 alloc_size /= outc->logic_buff.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         uint8_t *buf, size_t unitsize, size_t length, gboolean flush)
400 {
401         struct out_context *outc;
402         struct logic_buff *buff;
403         size_t send_size, remain, copy_size;
404         uint8_t *wrptr, *rdptr;
405         int ret;
406
407         outc = o->priv;
408         buff = &outc->logic_buff;
409         if (length && unitsize != buff->unit_size) {
410                 sr_warn("Unexpected unit size, discarding logic data.");
411                 return SR_ERR_ARG;
412         }
413
414         /*
415          * Queue most recently received samples to the local buffer.
416          * Flush to the ZIP archive when the buffer space is exhausted.
417          */
418         rdptr = buf;
419         send_size = buff->unit_size ? length / buff->unit_size : 0;
420         while (send_size) {
421                 remain = buff->alloc_size - buff->fill_size;
422                 if (remain) {
423                         wrptr = &buff->samples[buff->fill_size * buff->unit_size];
424                         copy_size = MIN(send_size, remain);
425                         send_size -= copy_size;
426                         buff->fill_size += copy_size;
427                         memcpy(wrptr, rdptr, copy_size * buff->unit_size);
428                         rdptr += copy_size * buff->unit_size;
429                         remain -= copy_size;
430                 }
431                 if (send_size && !remain) {
432                         ret = zip_append(o, buff->samples, buff->unit_size,
433                                 buff->fill_size * buff->unit_size);
434                         if (ret != SR_OK)
435                                 return ret;
436                         buff->fill_size = 0;
437                         remain = buff->alloc_size - buff->fill_size;
438                 }
439         }
440
441         /* Flush to the ZIP archive if the caller wants us to. */
442         if (flush && buff->fill_size) {
443                 ret = zip_append(o, buff->samples, buff->unit_size,
444                         buff->fill_size * buff->unit_size);
445                 if (ret != SR_OK)
446                         return ret;
447                 buff->fill_size = 0;
448         }
449
450         return SR_OK;
451 }
452
453 /**
454  * Append analog data of a channel to an srzip archive.
455  *
456  * @param[in] o Output module instance.
457  * @param[in] values Sample data as array of floating point values.
458  * @param[in] count Number of samples (float items, not bytes).
459  * @param[in] ch_nr 1-based channel number.
460  *
461  * @returns SR_OK et al error codes.
462  */
463 static int zip_append_analog(const struct sr_output *o,
464         const float *values, size_t count, size_t ch_nr)
465 {
466         struct out_context *outc;
467         struct zip *archive;
468         struct zip_source *analogsrc;
469         int64_t i, num_files;
470         size_t size;
471         struct zip_stat zs;
472         uint64_t chunk_num;
473         const char *entry_name;
474         char *basename;
475         gsize baselen;
476         char *chunkname;
477         unsigned int next_chunk_num;
478
479         outc = o->priv;
480
481         if (!(archive = zip_open(outc->filename, 0, NULL)))
482                 return SR_ERR;
483
484         if (zip_stat(archive, "metadata", 0, &zs) < 0) {
485                 sr_err("Failed to open metadata: %s", zip_strerror(archive));
486                 zip_discard(archive);
487                 return SR_ERR;
488         }
489
490         basename = g_strdup_printf("analog-1-%zu", ch_nr);
491         baselen = strlen(basename);
492         next_chunk_num = 1;
493         num_files = zip_get_num_entries(archive, 0);
494         for (i = 0; i < num_files; i++) {
495                 entry_name = zip_get_name(archive, i, 0);
496                 if (!entry_name || strncmp(entry_name, basename, baselen) != 0) {
497                         continue;
498                 } else if (entry_name[baselen] == '-') {
499                         chunk_num = g_ascii_strtoull(entry_name + baselen + 1, NULL, 10);
500                         if (chunk_num < G_MAXINT && chunk_num >= next_chunk_num)
501                                 next_chunk_num = chunk_num + 1;
502                 }
503         }
504
505         size = sizeof(values[0]) * count;
506         analogsrc = zip_source_buffer(archive, values, size, FALSE);
507         chunkname = g_strdup_printf("%s-%u", basename, next_chunk_num);
508         i = zip_add(archive, chunkname, analogsrc);
509         if (i < 0) {
510                 sr_err("Failed to add chunk '%s': %s", chunkname, zip_strerror(archive));
511                 g_free(chunkname);
512                 g_free(basename);
513                 zip_source_free(analogsrc);
514                 zip_discard(archive);
515                 return SR_ERR;
516         }
517         g_free(chunkname);
518         if (zip_close(archive) < 0) {
519                 sr_err("Error saving session file: %s", zip_strerror(archive));
520                 g_free(basename);
521                 zip_discard(archive);
522                 return SR_ERR;
523         }
524
525         g_free(basename);
526
527         return SR_OK;
528 }
529
530 /**
531  * Queue analog data of a channel for srzip archive writes.
532  *
533  * @param[in] o Output module instance.
534  * @param[in] analog Sample data (session feed packet format).
535  * @param[in] flush Force ZIP archive update (queue by default).
536  *
537  * @returns SR_OK et al error codes.
538  */
539 static int zip_append_analog_queue(const struct sr_output *o,
540         const struct sr_datafeed_analog *analog, gboolean flush)
541 {
542         struct out_context *outc;
543         const struct sr_channel *ch;
544         size_t idx, nr;
545         struct analog_buff *buff;
546         float *values, *wrptr, *rdptr;
547         size_t send_size, remain, copy_size;
548         int ret;
549
550         outc = o->priv;
551
552         /* Is this the DF_END flush call without samples submission? */
553         if (!analog && flush) {
554                 for (idx = 0; idx < outc->analog_ch_count; idx++) {
555                         nr = outc->first_analog_index + idx;
556                         buff = &outc->analog_buff[idx];
557                         if (!buff->fill_size)
558                                 continue;
559                         ret = zip_append_analog(o,
560                                 buff->samples, buff->fill_size, nr);
561                         if (ret != SR_OK)
562                                 return ret;
563                         buff->fill_size = 0;
564                 }
565                 return SR_OK;
566         }
567
568         /* Lookup index and number of the analog channel. */
569         /* TODO: support packets covering multiple channels */
570         if (g_slist_length(analog->meaning->channels) != 1) {
571                 sr_err("Analog packets covering multiple channels not supported yet");
572                 return SR_ERR;
573         }
574         ch = g_slist_nth_data(analog->meaning->channels, 0);
575         for (idx = 0; idx < outc->analog_ch_count; idx++) {
576                 if (outc->analog_index_map[idx] == ch->index)
577                         break;
578         }
579         if (idx == outc->analog_ch_count)
580                 return SR_ERR_ARG;
581         nr = outc->first_analog_index + idx;
582         buff = &outc->analog_buff[idx];
583
584         /* Convert the analog data to an array of float values. */
585         values = g_try_malloc0(analog->num_samples * sizeof(values[0]));
586         if (!values)
587                 return SR_ERR_MALLOC;
588         ret = sr_analog_to_float(analog, values);
589         if (ret != SR_OK) {
590                 g_free(values);
591                 return ret;
592         }
593
594         /*
595          * Queue most recently received samples to the local buffer.
596          * Flush to the ZIP archive when the buffer space is exhausted.
597          */
598         rdptr = values;
599         send_size = analog->num_samples;
600         while (send_size) {
601                 remain = buff->alloc_size - buff->fill_size;
602                 if (remain) {
603                         wrptr = &buff->samples[buff->fill_size];
604                         copy_size = MIN(send_size, remain);
605                         send_size -= copy_size;
606                         buff->fill_size += copy_size;
607                         memcpy(wrptr, rdptr, copy_size * sizeof(values[0]));
608                         rdptr += copy_size;
609                         remain -= copy_size;
610                 }
611                 if (send_size && !remain) {
612                         ret = zip_append_analog(o,
613                                 buff->samples, buff->fill_size, nr);
614                         if (ret != SR_OK) {
615                                 g_free(values);
616                                 return ret;
617                         }
618                         buff->fill_size = 0;
619                         remain = buff->alloc_size - buff->fill_size;
620                 }
621         }
622         g_free(values);
623
624         /* Flush to the ZIP archive if the caller wants us to. */
625         if (flush && buff->fill_size) {
626                 ret = zip_append_analog(o, buff->samples, buff->fill_size, nr);
627                 if (ret != SR_OK)
628                         return ret;
629                 buff->fill_size = 0;
630         }
631
632         return SR_OK;
633 }
634
635 static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
636                 GString **out)
637 {
638         struct out_context *outc;
639         const struct sr_datafeed_meta *meta;
640         const struct sr_datafeed_logic *logic;
641         const struct sr_datafeed_analog *analog;
642         const struct sr_config *src;
643         GSList *l;
644         int ret;
645
646         *out = NULL;
647         if (!o || !o->sdi || !(outc = o->priv))
648                 return SR_ERR_ARG;
649
650         switch (packet->type) {
651         case SR_DF_META:
652                 meta = packet->payload;
653                 for (l = meta->config; l; l = l->next) {
654                         src = l->data;
655                         if (src->key != SR_CONF_SAMPLERATE)
656                                 continue;
657                         outc->samplerate = g_variant_get_uint64(src->data);
658                 }
659                 break;
660         case SR_DF_LOGIC:
661                 if (!outc->zip_created) {
662                         if ((ret = zip_create(o)) != SR_OK)
663                                 return ret;
664                         outc->zip_created = TRUE;
665                 }
666                 logic = packet->payload;
667                 ret = zip_append_queue(o, logic->data,
668                         logic->unitsize, logic->length, FALSE);
669                 if (ret != SR_OK)
670                         return ret;
671                 break;
672         case SR_DF_ANALOG:
673                 if (!outc->zip_created) {
674                         if ((ret = zip_create(o)) != SR_OK)
675                                 return ret;
676                         outc->zip_created = TRUE;
677                 }
678                 analog = packet->payload;
679                 ret = zip_append_analog_queue(o, analog, FALSE);
680                 if (ret != SR_OK)
681                         return ret;
682                 break;
683         case SR_DF_END:
684                 if (outc->zip_created) {
685                         ret = zip_append_queue(o, NULL, 0, 0, TRUE);
686                         if (ret != SR_OK)
687                                 return ret;
688                         ret = zip_append_analog_queue(o, NULL, TRUE);
689                         if (ret != SR_OK)
690                                 return ret;
691                 }
692                 break;
693         }
694
695         return SR_OK;
696 }
697
698 static struct sr_option options[] = {
699         ALL_ZERO
700 };
701
702 static const struct sr_option *get_options(void)
703 {
704         return options;
705 }
706
707 static int cleanup(struct sr_output *o)
708 {
709         struct out_context *outc;
710         size_t idx;
711
712         outc = o->priv;
713
714         g_free(outc->analog_index_map);
715         g_free(outc->filename);
716         g_free(outc->logic_buff.samples);
717         for (idx = 0; idx < outc->analog_ch_count; idx++)
718                 g_free(outc->analog_buff[idx].samples);
719         g_free(outc->analog_buff);
720
721         g_free(outc);
722         o->priv = NULL;
723
724         return SR_OK;
725 }
726
727 SR_PRIV struct sr_output_module output_srzip = {
728         .id = "srzip",
729         .name = "srzip",
730         .desc = "srzip session file format data",
731         .exts = (const char*[]){"sr", NULL},
732         .flags = SR_OUTPUT_INTERNAL_IO_HANDLING,
733         .options = get_options,
734         .init = init,
735         .receive = receive,
736         .cleanup = cleanup,
737 };