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