diff --git a/Makefile.am b/Makefile.am index ef4d4b33..51c091fa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -45,9 +45,10 @@ libsigrok_la_SOURCES = \ # src/input/binary.c \ # src/input/chronovu_la8.c \ # src/input/csv.c \ -# src/input/vcd.c +# libsigrok_la_SOURCES += \ src/input/input.c \ + src/input/vcd.c \ src/input/wav.c # Output modules diff --git a/src/input/input.c b/src/input/input.c index d2c13aa7..c7855434 100644 --- a/src/input/input.c +++ b/src/input/input.c @@ -66,7 +66,7 @@ extern SR_PRIV struct sr_input_module input_wav; /* @endcond */ static const struct sr_input_module *input_module_list[] = { -// &input_vcd, + &input_vcd, // &input_chronovu_la8, &input_wav, // &input_csv, @@ -219,13 +219,13 @@ SR_API struct sr_input *sr_input_new(const struct sr_input_module *imod, in = g_malloc0(sizeof(struct sr_input)); in->module = imod; - if (options) { - new_opts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, - (GDestroyNotify)g_variant_unref); + new_opts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)g_variant_unref); + if (imod->options) { mod_opts = imod->options(); for (i = 0; mod_opts[i].id; i++) { - if (g_hash_table_lookup_extended(options, mod_opts[i].id, - &key, &value)) { + if (options && g_hash_table_lookup_extended(options, + mod_opts[i].id, &key, &value)) { /* Option not given: insert the default value. */ gvt = g_variant_get_type(mod_opts[i].def); if (!g_variant_is_of_type(value, gvt)) { @@ -243,17 +243,18 @@ SR_API struct sr_input *sr_input_new(const struct sr_input_module *imod, } /* Make sure no invalid options were given. */ - g_hash_table_iter_init(&iter, options); - while (g_hash_table_iter_next(&iter, &key, &value)) { - if (!g_hash_table_lookup(new_opts, key)) { - sr_err("Input module '%s' has no option '%s'", imod->id, key); - g_hash_table_destroy(new_opts); - g_free(in); - return NULL; + if (options) { + g_hash_table_iter_init(&iter, options); + while (g_hash_table_iter_next(&iter, &key, &value)) { + if (!g_hash_table_lookup(new_opts, key)) { + sr_err("Input module '%s' has no option '%s'", imod->id, key); + g_hash_table_destroy(new_opts); + g_free(in); + return NULL; + } } } - } else - new_opts = NULL; + } if (in->module->init && in->module->init(in, new_opts) != SR_OK) { g_hash_table_destroy(new_opts); @@ -273,7 +274,7 @@ static gboolean check_required_metadata(const uint8_t *metadata, uint8_t *avail) uint8_t reqd; for (m = 0; metadata[m]; m++) { - if (!metadata[m] & SR_INPUT_META_REQUIRED) + if (!(metadata[m] & SR_INPUT_META_REQUIRED)) continue; reqd = metadata[m] & ~SR_INPUT_META_REQUIRED; for (a = 0; avail[a]; a++) { @@ -490,6 +491,8 @@ SR_API int sr_input_free(const struct sr_input *in) ret = SR_OK; if (in->module->cleanup) ret = in->module->cleanup((struct sr_input *)in); + if (in->sdi) + sr_dev_inst_free(in->sdi); if (in->buf) g_string_free(in->buf, TRUE); g_free((gpointer)in); diff --git a/src/input/vcd.c b/src/input/vcd.c index 0c5e7878..8111c59c 100644 --- a/src/input/vcd.c +++ b/src/input/vcd.c @@ -2,6 +2,7 @@ * This file is part of the libsigrok project. * * Copyright (C) 2012 Petteri Aimonen + * Copyright (C) 2014 Bert Vermeulen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -69,12 +70,14 @@ #define CHUNKSIZE 1024 struct context { + gboolean got_header; uint64_t samplerate; - int maxchannels; - int channelcount; + unsigned int maxchannels; + unsigned int channelcount; int downsample; unsigned compress; int64_t skip; + gboolean skip_until_end; GSList *channels; }; @@ -84,81 +87,56 @@ struct vcd_channel { }; -/* Read until specific type of character occurs in file. - * Skip input if dest is NULL. - * Modes: - * 'W' read until whitespace - * 'N' read until non-whitespace, and ungetc() the character - * '$' read until $end - */ -static gboolean read_until(FILE *file, GString *dest, char mode) -{ - int c; - char prev[4] = ""; - - for(;;) { - c = fgetc(file); - - if (c == EOF) { - if (mode == '$') - sr_err("Unexpected EOF."); - return FALSE; - } - - if (mode == 'W' && g_ascii_isspace(c)) - return TRUE; - - if (mode == 'N' && !g_ascii_isspace(c)) { - ungetc(c, file); - return TRUE; - } - - if (mode == '$') { - prev[0] = prev[1]; prev[1] = prev[2]; prev[2] = prev[3]; prev[3] = c; - if (prev[0] == '$' && prev[1] == 'e' && prev[2] == 'n' && prev[3] == 'd') { - if (dest != NULL) - g_string_truncate(dest, dest->len - 3); - - return TRUE; - } - } - - if (dest != NULL) - g_string_append_c(dest, c); - } -} - /* - * Reads a single VCD section from input file and parses it to structure. + * Reads a single VCD section from input file and parses it to name/contents. * e.g. $timescale 1ps $end => "timescale" "1ps" */ -static gboolean parse_section(FILE *file, gchar **name, gchar **contents) +static gboolean parse_section(GString *buf, gchar **name, gchar **contents) { + GString *sname, *scontent; gboolean status; - GString *sname, *scontents; + unsigned int pos; - /* Skip any initial white-space */ - if (!read_until(file, NULL, 'N')) return FALSE; + *name = *contents = NULL; + status = FALSE; + pos = 0; + + /* Skip any initial white-space. */ + while (pos < buf->len && g_ascii_isspace(buf->str[pos])) + pos++; /* Section tag should start with $. */ - if (fgetc(file) != '$') + if (buf->str[pos++] != '$') return FALSE; - /* Read the section tag */ sname = g_string_sized_new(32); - status = read_until(file, sname, 'W'); + scontent = g_string_sized_new(128); - /* Skip whitespace before content */ - status = status && read_until(file, NULL, 'N'); + /* Read the section tag. */ + while (pos < buf->len && !g_ascii_isspace(buf->str[pos])) + g_string_append_c(sname, buf->str[pos++]); - /* Read the content */ - scontents = g_string_sized_new(128); - status = status && read_until(file, scontents, '$'); - g_strchomp(scontents->str); + /* Skip whitespace before content. */ + while (pos < buf->len && g_ascii_isspace(buf->str[pos])) + pos++; + + /* Read the content. */ + while (pos < buf->len - 4 && strncmp(buf->str + pos, "$end", 4)) + g_string_append_c(scontent, buf->str[pos++]); + + if (sname->len && pos < buf->len - 4 && !strncmp(buf->str + pos, "$end", 4)) { + status = TRUE; + pos += 4; + while (pos < buf->len && g_ascii_isspace(buf->str[pos])) + pos++; + g_string_erase(buf, 0, pos); + } - /* Release strings if status is FALSE, return them if status is TRUE */ *name = g_string_free(sname, !status); - *contents = g_string_free(scontents, !status); + *contents = g_string_free(scontent, !status); + if (*contents) + g_strchomp(*contents); + return status; } @@ -170,12 +148,6 @@ static void free_channel(void *data) g_free(vcd_ch); } -static void release_context(struct context *ctx) -{ - g_slist_free_full(ctx->channels, free_channel); - g_free(ctx); -} - /* Remove empty parts from an array returned by g_strsplit. */ static void remove_empty_parts(gchar **parts) { @@ -194,14 +166,18 @@ static void remove_empty_parts(gchar **parts) * Parse VCD header to get values for context structure. * The context structure should be zeroed before calling this. */ -static gboolean parse_header(FILE *file, struct context *ctx) +static gboolean parse_header(const struct sr_input *in, GString *buf) { - uint64_t p, q; - gchar *name = NULL, *contents = NULL; - gboolean status = FALSE; struct vcd_channel *vcd_ch; + uint64_t p, q; + struct context *inc; + gboolean status; + gchar *name, *contents, **parts; - while (parse_section(file, &name, &contents)) { + inc = in->priv; + name = contents = NULL; + status = FALSE; + while (parse_section(buf, &name, &contents)) { sr_dbg("Section '%s', contents '%s'.", name, contents); if (g_strcmp0(name, "enddefinitions") == 0) { @@ -211,22 +187,22 @@ static gboolean parse_header(FILE *file, struct context *ctx) /* * The standard allows for values 1, 10 or 100 * and units s, ms, us, ns, ps and fs. - * */ + */ if (sr_parse_period(contents, &p, &q) == SR_OK) { - ctx->samplerate = q / p; + inc->samplerate = q / p; if (q % p != 0) { /* Does not happen unless time value is non-standard */ sr_warn("Inexact rounding of samplerate, %" PRIu64 " / %" PRIu64 " to %" PRIu64 " Hz.", - q, p, ctx->samplerate); + q, p, inc->samplerate); } - sr_dbg("Samplerate: %" PRIu64, ctx->samplerate); + sr_dbg("Samplerate: %" PRIu64, inc->samplerate); } else { sr_err("Parsing timescale failed."); } } else if (g_strcmp0(name, "var") == 0) { /* Format: $var type size identifier reference $end */ - gchar **parts = g_strsplit_set(contents, " \r\n\t", 0); + parts = g_strsplit_set(contents, " \r\n\t", 0); remove_empty_parts(parts); if (g_strv_length(parts) != 4) @@ -235,15 +211,17 @@ static gboolean parse_header(FILE *file, struct context *ctx) sr_info("Unsupported signal type: '%s'", parts[0]); else if (strtol(parts[1], NULL, 10) != 1) sr_info("Unsupported signal size: '%s'", parts[1]); - else if (ctx->channelcount >= ctx->maxchannels) - sr_warn("Skipping '%s' because only %d channels requested.", parts[3], ctx->maxchannels); + else if (inc->channelcount >= inc->maxchannels) + sr_warn("Skipping '%s' because only %d channels requested.", + parts[3], inc->maxchannels); else { - sr_info("Channel %d is '%s' identified by '%s'.", ctx->channelcount, parts[3], parts[2]); + sr_info("Channel %d is '%s' identified by '%s'.", + inc->channelcount, parts[3], parts[2]); vcd_ch = g_malloc(sizeof(struct vcd_channel)); vcd_ch->identifier = g_strdup(parts[2]); vcd_ch->name = g_strdup(parts[3]); - ctx->channels = g_slist_append(ctx->channels, vcd_ch); - ctx->channelcount++; + inc->channels = g_slist_append(inc->channels, vcd_ch); + inc->channelcount++; } g_strfreev(parts); @@ -252,107 +230,35 @@ static gboolean parse_header(FILE *file, struct context *ctx) g_free(name); name = NULL; g_free(contents); contents = NULL; } - g_free(name); g_free(contents); + inc->got_header = status; + return status; } -static int format_match(const char *filename) +static int format_match(GHashTable *metadata) { - FILE *file; - gchar *name = NULL, *contents = NULL; + GString *buf, *tmpbuf; gboolean status; + gchar *name, *contents; - file = fopen(filename, "r"); - if (file == NULL) - return FALSE; + buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER)); + tmpbuf = g_string_new_len(buf->str, buf->len); /* * If we can parse the first section correctly, * then it is assumed to be a VCD file. */ - status = parse_section(file, &name, &contents); - status = status && (*name != '\0'); - + status = parse_section(tmpbuf, &name, &contents); + g_string_free(tmpbuf, TRUE); g_free(name); g_free(contents); - fclose(file); return status; } -static int init(struct sr_input *in, const char *filename) -{ - struct sr_channel *ch; - int num_channels, i; - char name[SR_MAX_CHANNELNAME_LEN + 1]; - char *param; - struct context *ctx; - - (void)filename; - - if (!(ctx = g_try_malloc0(sizeof(*ctx)))) { - sr_err("Input format context malloc failed."); - return SR_ERR_MALLOC; - } - - num_channels = DEFAULT_NUM_CHANNELS; - ctx->samplerate = 0; - ctx->downsample = 1; - ctx->skip = -1; - - if (in->param) { - param = g_hash_table_lookup(in->param, "numchannels"); - if (param) { - num_channels = strtoul(param, NULL, 10); - if (num_channels < 1) { - release_context(ctx); - return SR_ERR; - } else if (num_channels > 64) { - sr_err("No more than 64 channels supported."); - return SR_ERR; - } - } - - param = g_hash_table_lookup(in->param, "downsample"); - if (param) { - ctx->downsample = strtoul(param, NULL, 10); - if (ctx->downsample < 1) - ctx->downsample = 1; - } - - param = g_hash_table_lookup(in->param, "compress"); - if (param) - ctx->compress = strtoul(param, NULL, 10); - - param = g_hash_table_lookup(in->param, "skip"); - if (param) - ctx->skip = strtoul(param, NULL, 10) / ctx->downsample; - } - - /* Maximum number of channels to parse from the VCD */ - ctx->maxchannels = num_channels; - - /* Create a virtual device. */ - in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL); - in->internal = ctx; - - for (i = 0; i < num_channels; i++) { - snprintf(name, SR_MAX_CHANNELNAME_LEN, "%d", i); - - if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, name))) { - release_context(ctx); - return SR_ERR; - } - - in->sdi->channels = g_slist_append(in->sdi->channels, ch); - } - - return SR_OK; -} - /* Send N samples of the given value. */ static void send_samples(const struct sr_dev_inst *sdi, uint64_t sample, uint64_t count) { @@ -384,161 +290,262 @@ static void send_samples(const struct sr_dev_inst *sdi, uint64_t sample, uint64_ } } -/* Parse the data section of VCD */ -static void parse_contents(FILE *file, const struct sr_dev_inst *sdi, struct context *ctx) +/* Parse a set of lines from the data section. */ +static void parse_contents(const struct sr_input *in, char *data) { - GString *token = g_string_sized_new(32); + struct context *inc; + struct vcd_channel *vcd_ch; + GSList *l; + uint64_t timestamp, prev_timestamp, prev_values; + unsigned int bit, i, j; + char **tokens; - uint64_t prev_timestamp = 0; - uint64_t prev_values = 0; + inc = in->priv; + prev_timestamp = prev_values = 0; /* Read one space-delimited token at a time. */ - while (read_until(file, NULL, 'N') && read_until(file, token, 'W')) { - if (token->str[0] == '#' && g_ascii_isdigit(token->str[1])) { + tokens = g_strsplit_set(data, " \t\r\n", 0); + remove_empty_parts(tokens); + for (i = 0; tokens[i]; i++) { + if (inc->skip_until_end) { + if (!strcmp(tokens[i], "$end")) { + /* Done with unhandled/unknown section. */ + inc->skip_until_end = FALSE; + break; + } + } + if (tokens[i][0] == '#' && g_ascii_isdigit(tokens[i][1])) { /* Numeric value beginning with # is a new timestamp value */ - uint64_t timestamp; - timestamp = strtoull(token->str + 1, NULL, 10); + timestamp = strtoull(tokens[i] + 1, NULL, 10); - if (ctx->downsample > 1) - timestamp /= ctx->downsample; + if (inc->downsample > 1) + timestamp /= inc->downsample; /* * Skip < 0 => skip until first timestamp. * Skip = 0 => don't skip * Skip > 0 => skip until timestamp >= skip. */ - if (ctx->skip < 0) { - ctx->skip = timestamp; + if (inc->skip < 0) { + inc->skip = timestamp; prev_timestamp = timestamp; - } else if (ctx->skip > 0 && timestamp < (uint64_t)ctx->skip) { - prev_timestamp = ctx->skip; - } - else if (timestamp == prev_timestamp) { + } else if (inc->skip > 0 && timestamp < (uint64_t)inc->skip) { + prev_timestamp = inc->skip; + } else if (timestamp == prev_timestamp) { /* Ignore repeated timestamps (e.g. sigrok outputs these) */ - } - else { - if (ctx->compress != 0 && timestamp - prev_timestamp > ctx->compress) - { + } else { + if (inc->compress != 0 && timestamp - prev_timestamp > inc->compress) { /* Compress long idle periods */ - prev_timestamp = timestamp - ctx->compress; + prev_timestamp = timestamp - inc->compress; } sr_dbg("New timestamp: %" PRIu64, timestamp); /* Generate samples from prev_timestamp up to timestamp - 1. */ - send_samples(sdi, prev_values, timestamp - prev_timestamp); + send_samples(in->sdi, prev_values, timestamp - prev_timestamp); prev_timestamp = timestamp; } - } else if (token->str[0] == '$' && token->len > 1) { - /* This is probably a $dumpvars, $comment or similar. - * $dump* contain useful data, but other tags will be skipped until $end. */ - if (g_strcmp0(token->str, "$dumpvars") == 0 - || g_strcmp0(token->str, "$dumpon") == 0 - || g_strcmp0(token->str, "$dumpoff") == 0 - || g_strcmp0(token->str, "$end") == 0) { + } else if (tokens[i][0] == '$' && tokens[i][1] != '\0') { + /* + * This is probably a $dumpvars, $comment or similar. + * $dump* contain useful data. + */ + if (g_strcmp0(tokens[i], "$dumpvars") == 0 + || g_strcmp0(tokens[i], "$dumpon") == 0 + || g_strcmp0(tokens[i], "$dumpoff") == 0 + || g_strcmp0(tokens[i], "$end") == 0) { /* Ignore, parse contents as normally. */ } else { - /* Skip until $end */ - read_until(file, NULL, '$'); + /* Ignore this and future lines until $end. */ + inc->skip_until_end = TRUE; + break; } - } - else if (strchr("bBrR", token->str[0]) != NULL) { - /* A vector value. Skip it and also the following identifier. */ - read_until(file, NULL, 'N'); - read_until(file, NULL, 'W'); - } else if (strchr("01xXzZ", token->str[0]) != NULL) { + } else if (strchr("bBrR", tokens[i][0]) != NULL) { + /* A vector value, not supported yet. */ + break; + } else if (strchr("01xXzZ", tokens[i][0]) != NULL) { /* A new 1-bit sample value */ - int i, bit; - GSList *l; - struct vcd_channel *vcd_ch; + bit = (tokens[i][0] == '1'); - bit = (token->str[0] == '1'); - - g_string_erase(token, 0, 1); - if (token->len == 0) { - /* There was a space between value and identifier. - * Read in the rest. - */ - read_until(file, NULL, 'N'); - read_until(file, token, 'W'); + /* + * The identifier is either the next character, or, if + * there was whitespace after the bit, the next token. + */ + if (tokens[i][1] == '\0') { + if (!tokens[++i]) + /* Missing identifier */ + continue; + } else { + for (j = 1; tokens[i][j]; j++) + tokens[i][j - 1] = tokens[i][j]; + tokens[i][j - 1] = '\0'; } - for (i = 0, l = ctx->channels; i < ctx->channelcount && l; i++, l = l->next) { + for (j = 0, l = inc->channels; j < inc->channelcount && l; j++, l = l->next) { vcd_ch = l->data; - - if (g_strcmp0(token->str, vcd_ch->identifier) == 0) { + if (g_strcmp0(tokens[i], vcd_ch->identifier) == 0) { /* Found our channel */ if (bit) - prev_values |= (uint64_t)1 << i; + prev_values |= (uint64_t)1 << j; else - prev_values &= ~((uint64_t)1 << i); - + prev_values &= ~((uint64_t)1 << j); break; } } - - if (i == ctx->channelcount) - sr_dbg("Did not find channel for identifier '%s'.", token->str); + if (j == inc->channelcount) + sr_dbg("Did not find channel for identifier '%s'.", tokens[i]); } else { - sr_warn("Skipping unknown token '%s'.", token->str); + sr_warn("Skipping unknown token '%s'.", tokens[i]); } - - g_string_truncate(token, 0); } - - g_string_free(token, TRUE); + g_strfreev(tokens); } -static int loadfile(struct sr_input *in, const char *filename) +static int init(struct sr_input *in, GHashTable *options) { - struct sr_datafeed_packet packet; - struct sr_datafeed_meta meta; - struct sr_config *src; - FILE *file; - struct context *ctx; - uint64_t samplerate; + struct sr_channel *ch; + int num_channels, i; + char name[16]; + struct context *inc; - ctx = in->internal; + inc = g_malloc0(sizeof(struct context)); - if ((file = fopen(filename, "r")) == NULL) - return SR_ERR; - - if (!parse_header(file, ctx)) { - sr_err("VCD parsing failed"); - fclose(file); - return SR_ERR; + num_channels = g_variant_get_int32(g_hash_table_lookup(options, "numchannels")); + if (num_channels < 1) { + sr_err("Invalid value for numchannels: must be at least 1."); + return SR_ERR_ARG; } + if (num_channels > 64) { + sr_err("No more than 64 channels supported."); + return SR_ERR_ARG; + } + inc->maxchannels = num_channels; - /* Send header packet to the session bus. */ - std_session_send_df_header(in->sdi, LOG_PREFIX); + inc->downsample = g_variant_get_int32(g_hash_table_lookup(options, "downsample")); + if (inc->downsample < 1) + inc->downsample = 1; - /* Send metadata about the SR_DF_LOGIC packets to come. */ - packet.type = SR_DF_META; - packet.payload = &meta; - samplerate = ctx->samplerate / ctx->downsample; - src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(samplerate)); - meta.config = g_slist_append(NULL, src); - sr_session_send(in->sdi, &packet); - sr_config_free(src); + inc->compress = g_variant_get_int32(g_hash_table_lookup(options, "compress")); + inc->skip = g_variant_get_int32(g_hash_table_lookup(options, "skip")); + inc->skip /= inc->downsample; - /* Parse the contents of the VCD file */ - parse_contents(file, in->sdi, ctx); + /* Create a virtual device. */ + in->sdi = sr_dev_inst_new(0, SR_ST_ACTIVE, NULL, NULL, NULL); + in->priv = inc; - /* Send end packet to the session bus. */ - packet.type = SR_DF_END; - sr_session_send(in->sdi, &packet); - - fclose(file); - release_context(ctx); - in->internal = NULL; + for (i = 0; i < num_channels; i++) { + snprintf(name, 16, "%d", i); + ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE, name); + in->sdi->channels = g_slist_append(in->sdi->channels, ch); + } return SR_OK; } +static gboolean have_header(GString *buf) +{ + unsigned int pos; + char *p; + + if (!(p = g_strstr_len(buf->str, buf->len, "$enddefinitions"))) + return FALSE; + pos = p - buf->str + 15; + while (pos < buf->len - 4 && g_ascii_isspace(buf->str[pos])) + pos++; + if (!strncmp(buf->str + pos, "$end", 4)) + return TRUE; + + return FALSE; +} + +static int receive(const struct sr_input *in, GString *buf) +{ + struct sr_datafeed_packet packet; + struct sr_datafeed_meta meta; + struct sr_config *src; + struct context *inc; + uint64_t samplerate; + char *p; + + if (buf->len == 0) { + /* End of stream. */ + packet.type = SR_DF_END; + sr_session_send(in->sdi, &packet); + return SR_OK; + } + + g_string_append_len(in->buf, buf->str, buf->len); + + inc = in->priv; + if (!inc->got_header) { + if (!have_header(in->buf)) + return SR_OK; + if (!parse_header(in, in->buf) != SR_OK) + /* There was a header in there, but it was malformed. */ + return SR_ERR; + + std_session_send_df_header(in->sdi, LOG_PREFIX); + + packet.type = SR_DF_META; + packet.payload = &meta; + samplerate = inc->samplerate / inc->downsample; + src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(samplerate)); + meta.config = g_slist_append(NULL, src); + sr_session_send(in->sdi, &packet); + sr_config_free(src); + } + + while ((p = g_strrstr_len(in->buf->str, in->buf->len, "\n"))) { + *p = '\0'; + g_strstrip(in->buf->str); + if (in->buf->str[0] != '\0') + parse_contents(in, in->buf->str); + g_string_erase(in->buf, 0, p - in->buf->str + 1); + } + + return SR_OK; +} + +static int cleanup(struct sr_input *in) +{ + struct context *inc; + + inc = in->priv; + g_slist_free_full(inc->channels, free_channel); + g_free(inc); + in->priv = NULL; + + return SR_OK; +} + +static struct sr_option options[] = { + { "numchannels", "Max channels", "Maximum number of channels", NULL, NULL }, + { "skip", "Skip", "Skip until timestamp", NULL, NULL }, + { "downsample", "Downsample", "Divide samplerate by factor", NULL, NULL }, + { "compress", "Compress", "Compress idle periods longer than this value", NULL, NULL }, + { 0 } +}; + +static struct sr_option *get_options(void) +{ + if (!options[0].def) { + options[0].def = g_variant_ref_sink(g_variant_new_int32(DEFAULT_NUM_CHANNELS)); + options[1].def = g_variant_ref_sink(g_variant_new_int32(-1)); + options[2].def = g_variant_ref_sink(g_variant_new_int32(1)); + options[3].def = g_variant_ref_sink(g_variant_new_int32(0)); + } + + return options; +} + SR_PRIV struct sr_input_module input_vcd = { .id = "vcd", + .name = "VCD", .desc = "Value Change Dump", + .metadata = { SR_INPUT_META_HEADER | SR_INPUT_META_REQUIRED }, + .options = get_options, .format_match = format_match, .init = init, - .loadfile = loadfile, + .receive = receive, + .cleanup = cleanup, }; diff --git a/src/input/wav.c b/src/input/wav.c index 4faa1182..5669e8a4 100644 --- a/src/input/wav.c +++ b/src/input/wav.c @@ -145,7 +145,7 @@ static int find_data_chunk(GString *buf, int initial_offset) return offset; } -static int initial_receive(const struct sr_input *in) +static int initial_receive(struct sr_input *in) { struct sr_datafeed_packet packet; struct sr_datafeed_meta meta; @@ -159,7 +159,7 @@ static int initial_receive(const struct sr_input *in) /* Shouldn't happen. */ return SR_ERR; - inc = in->sdi->priv = g_malloc(sizeof(struct context)); + inc = in->priv = g_malloc(sizeof(struct context)); if (parse_wav_header(in->buf, inc) != SR_OK) return SR_ERR; @@ -191,7 +191,7 @@ static void send_chunk(const struct sr_input *in, int offset, int num_samples) int total_samples, samplenum; char *s, *d; - inc = in->sdi->priv; + inc = in->priv; s = in->buf->str + offset; d = (char *)fdata; @@ -251,8 +251,8 @@ static int receive(const struct sr_input *in, GString *buf) g_string_append_len(in->buf, buf->str, buf->len); - if (!in->sdi->priv) { - if (initial_receive(in) != SR_OK) + if (!in->priv) { + if (initial_receive((struct sr_input *)in) != SR_OK) return SR_ERR; if (in->buf->len < MIN_DATA_CHUNK_OFFSET) { /* @@ -262,7 +262,7 @@ static int receive(const struct sr_input *in, GString *buf) return SR_OK; } } - inc = in->sdi->priv; + inc = in->priv; if (!inc->found_data) { /* Skip past size of 'fmt ' chunk. */ @@ -306,6 +306,14 @@ static int receive(const struct sr_input *in, GString *buf) return SR_OK; } +static int cleanup(struct sr_input *in) +{ + g_free(in->priv); + in->priv = NULL; + + return SR_OK; +} + SR_PRIV struct sr_input_module input_wav = { .id = "wav", .name = "WAV", @@ -314,5 +322,6 @@ SR_PRIV struct sr_input_module input_wav = { .format_match = format_match, .init = init, .receive = receive, + .cleanup = cleanup, };