]> sigrok.org Git - libsigrok.git/commitdiff
output: Finish output module API wrappers.
authorBert Vermeulen <redacted>
Fri, 25 Jul 2014 03:56:52 +0000 (05:56 +0200)
committerBert Vermeulen <redacted>
Sat, 26 Jul 2014 01:57:17 +0000 (03:57 +0200)
The sr_output and sr_output_module structs are now no longer accessible
from the client.

18 files changed:
include/libsigrok/libsigrok.h
include/libsigrok/proto.h
src/backend.c
src/libsigrok-internal.h
src/output/analog.c
src/output/ascii.c
src/output/binary.c
src/output/bits.c
src/output/chronovu_la8.c
src/output/csv.c
src/output/gnuplot.c
src/output/hex.c
src/output/ols.c
src/output/output.c
src/output/vcd.c
tests/check_output_all.c
tests/lib.c
tests/lib.h

index 3a2c900accc14b8132c01c9d6d66edd136477b67..8292483a67f1e45bf62a22e0dff2cc5f8c16cc06 100644 (file)
@@ -429,6 +429,20 @@ struct sr_datafeed_analog {
        float *data;
 };
 
+/** Generic option struct used by various subsystems. */
+struct sr_option {
+       /* Short name suitable for commandline usage, [a-z0-9-]. */
+       char *id;
+       /* Short name suitable for GUI usage, can contain UTF-8. */
+       char *name;
+       /* Description of the option, in a sentence. */
+       char *desc;
+       /* Default value for this option. */
+       GVariant *def;
+       /* List of possible values, if this is an option with few values. */
+       GSList *values;
+};
+
 /** Input (file) format struct. */
 struct sr_input {
        /**
@@ -501,102 +515,8 @@ struct sr_input_format {
        int (*loadfile) (struct sr_input *in, const char *filename);
 };
 
-/** Output (file) format struct. */
-struct sr_output {
-       /** A pointer to this output's format.  */
-       struct sr_output_format *format;
-
-       /**
-        * The device for which this output module is creating output. This
-        * can be used by the module to find out channel names and numbers.
-        */
-       const struct sr_dev_inst *sdi;
-
-       /**
-        * An optional parameter which the frontend can pass in to the
-        * output module. How the string is interpreted is entirely up to
-        * the module.
-        */
-       GHashTable *params;
-
-       /**
-        * A generic pointer which can be used by the module to keep internal
-        * state between calls into its callback functions.
-        *
-        * For example, the module might store a pointer to a chunk of output
-        * there, and only flush it when it reaches a certain size.
-        */
-       void *internal;
-};
-
-/** Output (file) format driver. */
-struct sr_output_format {
-       /**
-        * A unique ID for this output format. Must not be NULL.
-        *
-        * It can be used by frontends to select this output format for use.
-        *
-        * For example, calling sigrok-cli with <code>-O hex</code> will
-        * select the hexadecimal text output format.
-        */
-       char *id;
-
-       /**
-        * A short description of the output format. Must not be NULL.
-        *
-        * This can be displayed by frontends, e.g. when selecting the output
-        * format for saving a file.
-        */
-       char *description;
-
-       /**
-        * This function is called once, at the beginning of an output stream.
-        *
-        * The device struct will be available in the output struct passed in,
-        * as well as the param field -- which may be NULL or an empty string,
-        * if no parameter was passed.
-        *
-        * The module can use this to initialize itself, create a struct for
-        * keeping state and storing it in the <code>internal</code> field.
-        *
-        * @param o Pointer to the respective 'struct sr_output'.
-        *
-        * @retval SR_OK Success
-        * @retval other Negative error code.
-        */
-       int (*init) (struct sr_output *o);
-
-       /**
-        * This function is passed a copy of every packed in the data feed.
-        * Any output generated by the output module in response to the
-        * packet should be returned in a newly allocated GString
-        * <code>out</code>, which will be freed by the caller.
-        *
-        * Packets not of interest to the output module can just be ignored,
-        * and the <code>out</code> parameter set to NULL.
-        *
-        * @param o Pointer to the respective 'struct sr_output'.
-        * @param sdi The device instance that generated the packet.
-        * @param packet The complete packet.
-        * @param out A pointer where a GString * should be stored if
-        * the module generates output, or NULL if not.
-        *
-        * @retval SR_OK Success
-        * @retval other Negative error code.
-        */
-       int (*receive) (struct sr_output *o,
-                       const struct sr_datafeed_packet *packet, GString **out);
-
-       /**
-        * This function is called after the caller is finished using
-        * the output module, and can be used to free any internal
-        * resources the module may keep.
-        *
-        * @retval SR_OK Success
-        * @retval other Negative error code.
-        */
-       int (*cleanup) (struct sr_output *o);
-};
+struct sr_output;
+struct sr_output_module;
 
 /** Constants for channel type. */
 enum sr_channeltype {
index f11efeda3712572c7719c23ef35bc12f9c39f2a0..edcd4c978582146e6cd43cf936f604518f99142c 100644 (file)
@@ -128,12 +128,18 @@ SR_API struct sr_input_format **sr_input_list(void);
 
 /*--- output/output.c -------------------------------------------------------*/
 
-SR_API struct sr_output_format **sr_output_list(void);
-SR_API struct sr_output *sr_output_new(struct sr_output_format *of,
+SR_API const struct sr_output_module **sr_output_list(void);
+SR_API const char *sr_output_id_get(const struct sr_output_module *o);
+SR_API const char *sr_output_name_get(const struct sr_output_module *o);
+SR_API const char *sr_output_description_get(const struct sr_output_module *o);
+SR_API const struct sr_output_module *sr_output_find(char *id);
+SR_API const struct sr_option *sr_output_options_get(const struct sr_output_module *o);
+SR_API void sr_output_options_free(const struct sr_output_module *o);
+SR_API const struct sr_output *sr_output_new(const struct sr_output_module *o,
                GHashTable *params, const struct sr_dev_inst *sdi);
-SR_API int sr_output_send(struct sr_output *o,
+SR_API int sr_output_send(const struct sr_output *o,
                const struct sr_datafeed_packet *packet, GString **out);
-SR_API int sr_output_free(struct sr_output *o);
+SR_API int sr_output_free(const struct sr_output *o);
 
 /*--- trigger.c -------------------------------------------------------------*/
 
index e0e18175645ca5207be10348f4d786a0e92b0562..47f352bf720d897a8feb22e9a9d112cde8d6c113 100644 (file)
@@ -265,7 +265,7 @@ static int sanity_check_all_input_modules(void)
 static int sanity_check_all_output_modules(void)
 {
        int i, errors, ret = SR_OK;
-       struct sr_output_format **outputs;
+       const struct sr_output_module **outputs;
        const char *d;
 
        sr_spew("Sanity-checking all output modules.");
@@ -280,7 +280,11 @@ static int sanity_check_all_output_modules(void)
                        sr_err("No ID in module %d ('%s').", i, d);
                        errors++;
                }
-               if (!outputs[i]->description) {
+               if (!outputs[i]->name) {
+                       sr_err("No name in module %d ('%s').", i, d);
+                       errors++;
+               }
+               if (!outputs[i]->desc) {
                        sr_err("No description in module '%s'.", d);
                        errors++;
                }
index 8c58bd9f1e53e61c49985207cca5b2472321f699..2a4384c4b13047bb816fb9248d9ba558bdf3a07f 100644 (file)
@@ -159,6 +159,107 @@ struct sr_context {
 #endif
 };
 
+/** Output module instance. */
+struct sr_output {
+       /** A pointer to this output's module.  */
+       const struct sr_output_module *module;
+
+       /**
+        * The device for which this output module is creating output. This
+        * can be used by the module to find out channel names and numbers.
+        */
+       const struct sr_dev_inst *sdi;
+
+       /**
+        * A generic pointer which can be used by the module to keep internal
+        * state between calls into its callback functions.
+        *
+        * For example, the module might store a pointer to a chunk of output
+        * there, and only flush it when it reaches a certain size.
+        */
+       void *internal;
+};
+
+/** Output module driver. */
+struct sr_output_module {
+       /**
+        * A unique ID for this output module, suitable for use in command-line
+        * clients, [a-z0-9-]. Must not be NULL.
+        */
+       char *id;
+
+       /**
+        * A unique name for this output module, suitable for use in GUI
+        * clients, can contain UTF-8. Must not be NULL.
+        */
+       const char *name;
+
+       /**
+        * A short description of the output module. Must not be NULL.
+        *
+        * This can be displayed by frontends, e.g. when selecting the output
+        * module for saving a file.
+        */
+       char *desc;
+
+       /**
+        * Returns a NULL-terminated list of options this module can take.
+        * Can be NULL, if the module has no options.
+        *
+        * If cached is TRUE, no new GVariants are created for the def and
+        * values fields; instead, the current values are returned.
+        */
+       struct sr_option *(*options) (gboolean cached);
+
+       /**
+        * This function is called once, at the beginning of an output stream.
+        *
+        * The device struct will be available in the output struct passed in,
+        * as well as the param field -- which may be NULL or an empty string,
+        * if no parameter was passed.
+        *
+        * The module can use this to initialize itself, create a struct for
+        * keeping state and storing it in the <code>internal</code> field.
+        *
+        * @param o Pointer to the respective 'struct sr_output'.
+        *
+        * @retval SR_OK Success
+        * @retval other Negative error code.
+        */
+       int (*init) (struct sr_output *o, GHashTable *options);
+
+       /**
+        * This function is passed a copy of every packed in the data feed.
+        * Any output generated by the output module in response to the
+        * packet should be returned in a newly allocated GString
+        * <code>out</code>, which will be freed by the caller.
+        *
+        * Packets not of interest to the output module can just be ignored,
+        * and the <code>out</code> parameter set to NULL.
+        *
+        * @param o Pointer to the respective 'struct sr_output'.
+        * @param sdi The device instance that generated the packet.
+        * @param packet The complete packet.
+        * @param out A pointer where a GString * should be stored if
+        * the module generates output, or NULL if not.
+        *
+        * @retval SR_OK Success
+        * @retval other Negative error code.
+        */
+       int (*receive) (const struct sr_output *o,
+                       const struct sr_datafeed_packet *packet, GString **out);
+
+       /**
+        * This function is called after the caller is finished using
+        * the output module, and can be used to free any internal
+        * resources the module may keep.
+        *
+        * @retval SR_OK Success
+        * @retval other Negative error code.
+        */
+       int (*cleanup) (struct sr_output *o);
+};
+
 #ifdef HAVE_LIBUSB_1_0
 /** USB device instance */
 struct sr_usb_dev_inst {
index fbc41d88ffa9e52e1f010fcd705aeca52b203f16..f81e683c7665a7acd3481a73994774a2aaaf98e3 100644 (file)
@@ -31,13 +31,13 @@ struct context {
        GPtrArray *channellist;
 };
 
-static int init(struct sr_output *o)
+static int init(struct sr_output *o, GHashTable *options)
 {
        struct context *ctx;
        struct sr_channel *ch;
        GSList *l;
 
-       sr_spew("Initializing output module.");
+       (void)options;
 
        if (!o || !o->sdi)
                return SR_ERR_ARG;
@@ -215,7 +215,7 @@ static void fancyprint(int unit, int mqflags, float value, GString *out)
        g_string_append_c(out, '\n');
 }
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
                GString **out)
 {
        const struct sr_datafeed_analog *analog;
@@ -268,9 +268,11 @@ static int cleanup(struct sr_output *o)
        return SR_OK;
 }
 
-SR_PRIV struct sr_output_format output_analog = {
+SR_PRIV struct sr_output_module output_analog = {
        .id = "analog",
-       .description = "Analog data",
+       .name = "Analog",
+       .desc = "Analog data and types",
+       .options = NULL,
        .init = init,
        .receive = receive,
        .cleanup = cleanup
index d06894273338715d56db4a2bf66b336fb5e86ed8..72eb6822c58bc70278e18762b911cf06c860c503 100644 (file)
@@ -44,7 +44,7 @@ struct context {
        GString *header;
 };
 
-static int init(struct sr_output *o)
+static int init(struct sr_output *o, GHashTable *options)
 {
        struct context *ctx;
        struct sr_channel *ch;
@@ -52,22 +52,27 @@ static int init(struct sr_output *o)
        GHashTableIter iter;
        gpointer key, value;
        unsigned int i, j;
-       int spl;
+       uint32_t spl;
 
        if (!o || !o->sdi)
                return SR_ERR_ARG;
 
        spl = DEFAULT_SAMPLES_PER_LINE;
-       g_hash_table_iter_init(&iter, o->params);
-       while (g_hash_table_iter_next(&iter, &key, &value)) {
-               if (!strcmp(key, "width")) {
-                       if ((spl = strtoul(value, NULL, 10)) < 1) {
-                               sr_err("Invalid width.");
-                               return SR_ERR_ARG;
+       if (options) {
+               g_hash_table_iter_init(&iter, options);
+               while (g_hash_table_iter_next(&iter, &key, &value)) {
+                       if (!strcmp(key, "width")) {
+                               if (!g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32)) {
+                                       sr_err("Invalid type for 'width' option.");
+                                       return SR_ERR_ARG;
+                               }
+                               if (!(spl = g_variant_get_uint32(value))) {
+                                       sr_err("Invalid width.");
+                                       return SR_ERR_ARG;
+                               }
+                       } else {
+                               sr_err("Unknown option '%s'.", key);
                        }
-               } else {
-                       sr_err("Unknown parameter '%s'.", key);
-                       return SR_ERR_ARG;
                }
        }
 
@@ -106,7 +111,7 @@ static int init(struct sr_output *o)
        return SR_OK;
 }
 
-static GString *gen_header(struct sr_output *o)
+static GString *gen_header(const struct sr_output *o)
 {
        struct context *ctx;
        GVariant *gvar;
@@ -138,7 +143,7 @@ static GString *gen_header(struct sr_output *o)
        return header;
 }
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
                GString **out)
 {
        const struct sr_datafeed_meta *meta;
@@ -250,9 +255,27 @@ static int cleanup(struct sr_output *o)
        return SR_OK;
 }
 
-SR_PRIV struct sr_output_format output_ascii = {
+static struct sr_option options[] = {
+       { "width", "Width", "Number of samples per line", NULL, NULL },
+       { 0 }
+};
+
+static struct sr_option *get_options(gboolean cached)
+{
+       if (cached)
+               return options;
+
+       options[0].def = g_variant_new_uint32(DEFAULT_SAMPLES_PER_LINE);
+       g_variant_ref_sink(options[0].def);
+
+       return options;
+}
+
+SR_PRIV struct sr_output_module output_ascii = {
        .id = "ascii",
-       .description = "ASCII",
+       .name = "ASCII",
+       .desc = "ASCII art",
+       .options = get_options,
        .init = init,
        .receive = receive,
        .cleanup = cleanup,
index 6e2531d1a252af1e7a92ed63e14c0b2783c0824f..10f0409feeb807bc9d3a50e93b6f48c2691935b5 100644 (file)
@@ -26,7 +26,7 @@
 
 #define LOG_PREFIX "output/binary"
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
                GString **out)
 {
        const struct sr_datafeed_logic *logic;
@@ -42,8 +42,10 @@ static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
        return SR_OK;
 }
 
-SR_PRIV struct sr_output_format output_binary = {
+SR_PRIV struct sr_output_module output_binary = {
        .id = "binary",
-       .description = "Raw binary",
+       .name = "Binary",
+       .desc = "Raw binary",
+       .options = NULL,
        .receive = receive,
 };
index b44f1057d82c645676ec16a4c31094b3221060ea..1035d8497b776c211b1c00388cfb8986e93fdcc4 100644 (file)
@@ -39,7 +39,7 @@ struct context {
        GString **lines;
 };
 
-static int init(struct sr_output *o)
+static int init(struct sr_output *o, GHashTable *options)
 {
        struct context *ctx;
        struct sr_channel *ch;
@@ -47,22 +47,27 @@ static int init(struct sr_output *o)
        GHashTableIter iter;
        gpointer key, value;
        unsigned int i, j;
-       int spl;
+       uint32_t spl;
 
        if (!o || !o->sdi)
                return SR_ERR_ARG;
 
        spl = DEFAULT_SAMPLES_PER_LINE;
-       g_hash_table_iter_init(&iter, o->params);
-       while (g_hash_table_iter_next(&iter, &key, &value)) {
-               if (!strcmp(key, "width")) {
-                       if ((spl = strtoul(value, NULL, 10)) < 1) {
-                               sr_err("Invalid width.");
-                               return SR_ERR_ARG;
+       if (options) {
+               g_hash_table_iter_init(&iter, options);
+               while (g_hash_table_iter_next(&iter, &key, &value)) {
+                       if (!strcmp(key, "width")) {
+                               if (!g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32)) {
+                                       sr_err("Invalid type for 'width' option.");
+                                       return SR_ERR_ARG;
+                               }
+                               if (!(spl = g_variant_get_uint32(value))) {
+                                       sr_err("Invalid width.");
+                                       return SR_ERR_ARG;
+                               }
+                       } else {
+                               sr_err("Unknown option '%s'.", key);
                        }
-               } else {
-                       sr_err("Unknown parameter '%s'.", key);
-                       return SR_ERR_ARG;
                }
        }
 
@@ -100,7 +105,7 @@ static int init(struct sr_output *o)
        return SR_OK;
 }
 
-static GString *gen_header(struct sr_output *o)
+static GString *gen_header(const struct sr_output *o)
 {
        struct context *ctx;
        GVariant *gvar;
@@ -132,7 +137,7 @@ static GString *gen_header(struct sr_output *o)
        return header;
 }
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
                GString **out)
 {
        const struct sr_datafeed_meta *meta;
@@ -236,9 +241,27 @@ static int cleanup(struct sr_output *o)
        return SR_OK;
 }
 
-SR_PRIV struct sr_output_format output_bits = {
+static struct sr_option options[] = {
+       { "width", "Width", "Number of samples per line", NULL, NULL },
+       { 0 }
+};
+
+static struct sr_option *get_options(gboolean cached)
+{
+       if (cached)
+               return options;
+
+       options[0].def = g_variant_new_uint32(DEFAULT_SAMPLES_PER_LINE);
+       g_variant_ref_sink(options[0].def);
+
+       return options;
+}
+
+SR_PRIV struct sr_output_module output_bits = {
        .id = "bits",
-       .description = "Bits",
+       .name = "Bits",
+       .desc = "0/1 digits",
+       .options = get_options,
        .init = init,
        .receive = receive,
        .cleanup = cleanup,
index ad0b5d32fc2c1ccaf294fabdf7035cd977cecefe..4263fc36751d55718be25725f3d7d9c1e8a1ee5a 100644 (file)
@@ -75,12 +75,14 @@ static uint8_t samplerate_to_divcount(uint64_t samplerate)
        return (SR_MHZ(100) / samplerate) - 1;
 }
 
-static int init(struct sr_output *o)
+static int init(struct sr_output *o, GHashTable *options)
 {
        struct context *ctx;
        struct sr_channel *ch;
        GSList *l;
 
+       (void)options;
+
        if (!o || !o->sdi)
                return SR_ERR_ARG;
 
@@ -101,7 +103,7 @@ static int init(struct sr_output *o)
        return SR_OK;
 }
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
                GString **out)
 {
        const struct sr_datafeed_logic *logic;
@@ -181,9 +183,11 @@ static int cleanup(struct sr_output *o)
        return SR_OK;
 }
 
-SR_PRIV struct sr_output_format output_chronovu_la8 = {
+SR_PRIV struct sr_output_module output_chronovu_la8 = {
        .id = "chronovu-la8",
-       .description = "ChronoVu LA8",
+       .name = "ChronoVu LA8",
+       .desc = "ChronoVu LA8 native file format",
+       .options = NULL,
        .init = init,
        .receive = receive,
        .cleanup = cleanup,
index d17969e8279db59b83599b179e5c818193b1f0a9..6ac7a77f5e50727f7c9326fbe5d08a9be89301f6 100644 (file)
@@ -47,13 +47,15 @@ struct context {
  *  - Trigger support.
  */
 
-static int init(struct sr_output *o)
+static int init(struct sr_output *o, GHashTable *options)
 {
        struct context *ctx;
        struct sr_channel *ch;
        GSList *l;
        int i;
 
+       (void)options;
+
        if (!o || !o->sdi)
                return SR_ERR_ARG;
 
@@ -85,7 +87,7 @@ static int init(struct sr_output *o)
        return SR_OK;
 }
 
-static GString *gen_header(struct sr_output *o)
+static GString *gen_header(const struct sr_output *o)
 {
        struct context *ctx;
        struct sr_channel *ch;
@@ -137,7 +139,7 @@ static GString *gen_header(struct sr_output *o)
        return header;
 }
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
                GString **out)
 {
        const struct sr_datafeed_meta *meta;
@@ -211,9 +213,11 @@ static int cleanup(struct sr_output *o)
        return SR_OK;
 }
 
-SR_PRIV struct sr_output_format output_csv = {
+SR_PRIV struct sr_output_module output_csv = {
        .id = "csv",
-       .description = "Comma-separated values (CSV)",
+       .name = "CSV",
+       .desc = "Comma-separated values",
+       .options = NULL,
        .init = init,
        .receive = receive,
        .cleanup = cleanup,
index a9feec67ba056f2f55daeed75990fca6eb1619e9..7af417400ebd61c3cc336bbcd81bd1b7ea5cf203 100644 (file)
@@ -44,13 +44,15 @@ static const char *gnuplot_header2 = "\
 # 0\t\tSample counter (for internal gnuplot purposes)\n";
 
 
-static int init(struct sr_output *o)
+static int init(struct sr_output *o, GHashTable *options)
 {
        struct context *ctx;
        struct sr_channel *ch;
        GSList *l;
        unsigned int i;
 
+       (void)options;
+
        if (!o || !o->sdi)
                return SR_ERR_ARG;
 
@@ -84,7 +86,7 @@ static int init(struct sr_output *o)
        return SR_OK;
 }
 
-static GString *gen_header(struct sr_output *o)
+static GString *gen_header(const struct sr_output *o)
 {
        struct context *ctx;
        struct sr_channel *ch;
@@ -130,7 +132,7 @@ static GString *gen_header(struct sr_output *o)
        return header;
 }
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
                GString **out)
 {
        const struct sr_datafeed_meta *meta;
@@ -215,9 +217,11 @@ static int cleanup(struct sr_output *o)
        return SR_OK;
 }
 
-SR_PRIV struct sr_output_format output_gnuplot = {
+SR_PRIV struct sr_output_module output_gnuplot = {
        .id = "gnuplot",
-       .description = "Gnuplot",
+       .name = "Gnuplot",
+       .desc = "Gnuplot file format",
+       .options = NULL,
        .init = init,
        .receive = receive,
        .cleanup = cleanup,
index 07a430ca6193044599a0a0b4cbb1637a6264651e..313dace8ff71aef10ebe2e39360ffd316ce75252 100644 (file)
@@ -42,7 +42,7 @@ struct context {
        GString **lines;
 };
 
-static int init(struct sr_output *o)
+static int init(struct sr_output *o, GHashTable *options)
 {
        struct context *ctx;
        struct sr_channel *ch;
@@ -50,22 +50,28 @@ static int init(struct sr_output *o)
        GHashTableIter iter;
        gpointer key, value;
        unsigned int i, j;
-       int spl;
+       uint32_t spl;
 
        if (!o || !o->sdi)
                return SR_ERR_ARG;
 
        spl = DEFAULT_SAMPLES_PER_LINE;
-       g_hash_table_iter_init(&iter, o->params);
-       while (g_hash_table_iter_next(&iter, &key, &value)) {
-               if (!strcmp(key, "width")) {
-                       if ((spl = strtoul(value, NULL, 10)) < 1) {
-                               sr_err("Invalid width.");
+       if (options) {
+               g_hash_table_iter_init(&iter, options);
+               while (g_hash_table_iter_next(&iter, &key, &value)) {
+                       if (!strcmp(key, "width")) {
+                               if (!g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32)) {
+                                       sr_err("Invalid type for 'width' option.");
+                                       return SR_ERR_ARG;
+                               }
+                               if (!(spl = g_variant_get_uint32(value))) {
+                                       sr_err("Invalid width.");
+                                       return SR_ERR_ARG;
+                               }
+                       } else {
+                               sr_err("Unknown option '%s'.", key);
                                return SR_ERR_ARG;
                        }
-               } else {
-                       sr_err("Unknown parameter '%s'.", key);
-                       return SR_ERR_ARG;
                }
        }
 
@@ -105,7 +111,7 @@ static int init(struct sr_output *o)
        return SR_OK;
 }
 
-static GString *gen_header(struct sr_output *o)
+static GString *gen_header(const struct sr_output *o)
 {
        struct context *ctx;
        GVariant *gvar;
@@ -137,7 +143,7 @@ static GString *gen_header(struct sr_output *o)
        return header;
 }
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
                GString **out)
 {
        const struct sr_datafeed_meta *meta;
@@ -250,9 +256,27 @@ static int cleanup(struct sr_output *o)
        return SR_OK;
 }
 
-SR_PRIV struct sr_output_format output_hex = {
+static struct sr_option options[] = {
+       { "width", "Width", "Number of samples per line", NULL, NULL },
+       { 0 }
+};
+
+static struct sr_option *get_options(gboolean cached)
+{
+       if (cached)
+               return options;
+
+       options[0].def = g_variant_new_uint32(DEFAULT_SAMPLES_PER_LINE);
+       g_variant_ref_sink(options[0].def);
+
+       return options;
+}
+
+SR_PRIV struct sr_output_module output_hex = {
        .id = "hex",
-       .description = "Hexadecimal",
+       .name = "Hexadecimal",
+       .desc = "Hexadecimal digits",
+       .options = get_options,
        .init = init,
        .receive = receive,
        .cleanup = cleanup,
index 78a41bb0b4645032eb342ef1796246d165b6da6b..8c9cca8d33789954849d04e73129601e813c5f7c 100644 (file)
@@ -38,10 +38,12 @@ struct context {
        uint64_t num_samples;
 };
 
-static int init(struct sr_output *o)
+static int init(struct sr_output *o, GHashTable *options)
 {
        struct context *ctx;
 
+       (void)options;
+
        if (!(ctx = g_try_malloc(sizeof(struct context)))) {
                sr_err("%s: ctx malloc failed", __func__);
                return SR_ERR_MALLOC;
@@ -88,7 +90,7 @@ static GString *gen_header(const struct sr_dev_inst *sdi, struct context *ctx)
        return s;
 }
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
                GString **out)
 {
        struct context *ctx;
@@ -148,9 +150,11 @@ static int cleanup(struct sr_output *o)
        return SR_OK;
 }
 
-SR_PRIV struct sr_output_format output_ols = {
+SR_PRIV struct sr_output_module output_ols = {
        .id = "ols",
-       .description = "OpenBench Logic Sniffer",
+       .name = "OLS",
+       .desc = "OpenBench Logic Sniffer",
+       .options = NULL,
        .init = init,
        .receive = receive,
        .cleanup = cleanup
index 4cf78f60a8b201d2ba6c60a9d6e50702d750ba5e..6406fcaceb3e5c75b9763982f14084c7c78297d9 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <string.h>
 #include "libsigrok.h"
 #include "libsigrok-internal.h"
 
+#define LOG_PREFIX "output"
+
 /**
  * @file
  *
- * Output file/data format handling.
+ * Output module handling.
  */
 
 /**
- * @defgroup grp_output Output formats
+ * @defgroup grp_output Output modules
  *
- * Output file/data format handling.
+ * Output module handling.
  *
- * libsigrok supports several output (file) formats, e.g. binary, VCD,
- * gnuplot, and so on. It provides an output API that frontends can use.
- * New output formats can be added/implemented in libsigrok without having
+ * libsigrok supports several output modules for file formats such as binary,
+ * VCD, gnuplot, and so on. It provides an output API that frontends can use.
+ * New output modules can be added/implemented in libsigrok without having
  * to change the frontends at all.
  *
  * All output modules are fed data in a stream. Devices that can stream data
  */
 
 /** @cond PRIVATE */
-extern SR_PRIV struct sr_output_format output_bits;
-extern SR_PRIV struct sr_output_format output_hex;
-extern SR_PRIV struct sr_output_format output_ascii;
-extern SR_PRIV struct sr_output_format output_binary;
-extern SR_PRIV struct sr_output_format output_vcd;
-extern SR_PRIV struct sr_output_format output_ols;
-extern SR_PRIV struct sr_output_format output_gnuplot;
-extern SR_PRIV struct sr_output_format output_chronovu_la8;
-extern SR_PRIV struct sr_output_format output_csv;
-extern SR_PRIV struct sr_output_format output_analog;
+extern SR_PRIV struct sr_output_module output_bits;
+extern SR_PRIV struct sr_output_module output_hex;
+extern SR_PRIV struct sr_output_module output_ascii;
+extern SR_PRIV struct sr_output_module output_binary;
+extern SR_PRIV struct sr_output_module output_vcd;
+extern SR_PRIV struct sr_output_module output_ols;
+extern SR_PRIV struct sr_output_module output_gnuplot;
+extern SR_PRIV struct sr_output_module output_chronovu_la8;
+extern SR_PRIV struct sr_output_module output_csv;
+extern SR_PRIV struct sr_output_module output_analog;
 /* @endcond */
 
-static struct sr_output_format *output_module_list[] = {
+static const struct sr_output_module *output_module_list[] = {
        &output_ascii,
        &output_binary,
        &output_bits,
@@ -73,46 +76,182 @@ static struct sr_output_format *output_module_list[] = {
        NULL,
 };
 
-/** @since 0.1.0 */
-SR_API struct sr_output_format **sr_output_list(void)
+/**
+ * Returns a NULL-terminated list of all the available output modules.
+ *
+ * @since 0.4.0
+ */
+SR_API const struct sr_output_module **sr_output_list(void)
 {
        return output_module_list;
 }
 
-/** @since 0.3.0 */
-SR_API struct sr_output *sr_output_new(struct sr_output_format *of,
-               GHashTable *params, const struct sr_dev_inst *sdi)
+/**
+ * Returns the specified output module's ID.
+ *
+ * @since 0.4.0
+ */
+SR_API const char *sr_output_id_get(const struct sr_output_module *o)
+{
+       if (!o) {
+               sr_err("Invalid output module NULL!");
+               return NULL;
+       }
+
+       return o->id;
+}
+
+/**
+ * Returns the specified output module's name.
+ *
+ * @since 0.4.0
+ */
+SR_API const char *sr_output_name_get(const struct sr_output_module *o)
+{
+       if (!o) {
+               sr_err("Invalid output module NULL!");
+               return NULL;
+       }
+
+       return o->name;
+}
+
+/**
+ * Returns the specified output module's description.
+ *
+ * @since 0.4.0
+ */
+SR_API const char *sr_output_description_get(const struct sr_output_module *o)
+{
+       if (!o) {
+               sr_err("Invalid output module NULL!");
+               return NULL;
+       }
+
+       return o->desc;
+}
+
+/**
+ * Return the output module with the specified ID, or NULL if no module
+ * with that id is found.
+ *
+ * @since 0.4.0
+ */
+SR_API const struct sr_output_module *sr_output_find(char *id)
+{
+       int i;
+
+       for (i = 0; output_module_list[i]; i++) {
+               if (!strcmp(output_module_list[i]->id, id))
+                       return output_module_list[i];
+       }
+
+       return NULL;
+}
+
+/**
+ * Returns a NULL-terminated array of struct sr_option, or NULL if the
+ * module takes no options.
+ *
+ * Each call to this function must be followed by a call to
+ * sr_output_options_free().
+ *
+ * @since 0.4.0
+ */
+SR_API const struct sr_option *sr_output_options_get(const struct sr_output_module *o)
+{
+
+       if (!o || !o->options)
+               return NULL;
+
+       return o->options(FALSE);
+}
+
+/**
+ * After a call to sr_output_options_get(), this function cleans up all
+ * the resources allocated by that call.
+ *
+ * @since 0.4.0
+ */
+SR_API void sr_output_options_free(const struct sr_output_module *o)
 {
-       struct sr_output *o;
-
-       o = g_malloc(sizeof(struct sr_output));
-       o->format = of;
-       o->sdi = sdi;
-       o->params = params;
-       if (o->format->init && o->format->init(o) != SR_OK) {
-               g_free(o);
-               o = NULL;
+       struct sr_option *opt;
+
+       if (!o || !o->options)
+               return;
+
+       for (opt = o->options(TRUE); opt->id; opt++) {
+               if (opt->def) {
+                       g_variant_unref(opt->def);
+                       opt->def = NULL;
+               }
+
+               if (opt->values) {
+                       g_slist_free_full(opt->values, (GDestroyNotify)g_variant_unref);
+                       opt->values = NULL;
+               }
        }
+}
 
-       return o;
+/**
+ * Create a new output instance using the specified output module.
+ *
+ * <code>options</code> is a *HashTable with the keys corresponding with
+ * the module options' <code>id</code> field. The values should be GVariant
+ * pointers with sunk * references, of the same GVariantType as the option's
+ * default value.
+ *
+ * The sr_dev_inst passed in can be used by the instance to determine
+ * channel names, samplerate, and so on.
+ *
+ * @since 0.4.0
+ */
+SR_API const struct sr_output *sr_output_new(const struct sr_output_module *o,
+               GHashTable *options, const struct sr_dev_inst *sdi)
+{
+       struct sr_output *op;
+
+       op = g_malloc(sizeof(struct sr_output));
+       op->module = o;
+       op->sdi = sdi;
+       if (op->module->init && op->module->init(op, options) != SR_OK) {
+               g_free(op);
+               op = NULL;
+       }
+
+       return op;
 }
 
-/** @since 0.3.0 */
-SR_API int sr_output_send(struct sr_output *o,
+/**
+ * Send a packet to the specified output instance.
+ *
+ * The instance's output is returned as a newly allocated GString,
+ * which must be freed by the caller.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_output_send(const struct sr_output *o,
                const struct sr_datafeed_packet *packet, GString **out)
 {
-       return o->format->receive(o, packet, out);
+       return o->module->receive(o, packet, out);
 }
 
-/** @since 0.3.0 */
-SR_API int sr_output_free(struct sr_output *o)
+/**
+ * Free the specified output instance and all associated resources.
+ *
+ * @since 0.4.0
+ */
+SR_API int sr_output_free(const struct sr_output *o)
 {
        int ret;
 
+       if (!o)
+               return SR_ERR_ARG;
+
        ret = SR_OK;
-       if (o->format->cleanup)
-               ret = o->format->cleanup(o);
-       g_free(o);
+       if (o->module->cleanup)
+               ret = o->module->cleanup((struct sr_output *)o);
+       g_free((gpointer)o);
 
        return ret;
 }
index 87f8049603d6ff3733c91643e5d67188e72110ae..479f331cd838b2d5763029a32b0e221e46e75ee9 100644 (file)
@@ -42,13 +42,15 @@ struct context {
 static const char *const vcd_header_comment =
        "$comment\n  Acquisition with %d/%d channels at %s\n$end\n";
 
-static int init(struct sr_output *o)
+static int init(struct sr_output *o, GHashTable *options)
 {
        struct context *ctx;
        struct sr_channel *ch;
        GSList *l;
        int num_enabled_channels, i;
 
+       (void)options;
+
        num_enabled_channels = 0;
        for (l = o->sdi->channels; l; l = l->next) {
                ch = l->data;
@@ -81,7 +83,7 @@ static int init(struct sr_output *o)
        return SR_OK;
 }
 
-static GString *gen_header(struct sr_output *o)
+static GString *gen_header(const struct sr_output *o)
 {
        struct context *ctx;
        struct sr_channel *ch;
@@ -154,7 +156,7 @@ static GString *gen_header(struct sr_output *o)
        return header;
 }
 
-static int receive(struct sr_output *o, const struct sr_datafeed_packet *packet,
+static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet,
                GString **out)
 {
        const struct sr_datafeed_meta *meta;
@@ -260,9 +262,11 @@ static int cleanup(struct sr_output *o)
        return SR_OK;
 }
 
-struct sr_output_format output_vcd = {
+struct sr_output_module output_vcd = {
        .id = "vcd",
-       .description = "Value Change Dump (VCD)",
+       .name = "VCD",
+       .desc = "Value Change Dump",
+       .options = NULL,
        .init = init,
        .receive = receive,
        .cleanup = cleanup,
index 05b65b76e4c5c5898a5d01026d957ff346ed37a2..aa8a2fe205fecf05eda1e7cc3eb77ca7aafcddf4 100644 (file)
 /* Check whether at least one output module is available. */
 START_TEST(test_output_available)
 {
-       struct sr_output_format **outputs;
+       const struct sr_output_module **outputs;
 
        outputs = sr_output_list();
        fail_unless(outputs != NULL, "No output modules found.");
 }
 END_TEST
 
+/* Check whether sr_output_id_get() works. */
+START_TEST(test_output_id)
+{
+       const struct sr_output_module **outputs;
+       const char *id;
+
+       outputs = sr_output_list();
+
+       id = sr_output_id_get(outputs[0]);
+       fail_unless(id != NULL, "No id found in output module.");
+}
+END_TEST
+
+/* Check whether sr_output_name_get() works. */
+START_TEST(test_output_name)
+{
+       const struct sr_output_module **outputs;
+       const char *name;
+
+       outputs = sr_output_list();
+
+       name = sr_output_name_get(outputs[0]);
+       fail_unless(name != NULL, "No name found in output module.");
+}
+END_TEST
+
+/* Check whether sr_output_description_get() works. */
+START_TEST(test_output_desc)
+{
+       const struct sr_output_module **outputs;
+       const char *desc;
+
+       outputs = sr_output_list();
+
+       desc = sr_output_description_get(outputs[0]);
+       fail_unless(desc != NULL, "No description found in output module.");
+}
+END_TEST
+
+/* Check whether sr_output_find() works. */
+START_TEST(test_output_find)
+{
+       const struct sr_output_module *omod;
+       const char *id;
+
+       omod = sr_output_find("bits");
+       fail_unless(omod != NULL, "Couldn't find the 'bits' output module.");
+       id = sr_output_id_get(omod);
+       fail_unless(!strcmp(id, "bits"), "That is not the 'bits' module!");
+}
+END_TEST
+
+/* Check whether sr_output_options_get() works. */
+START_TEST(test_output_options)
+{
+       const struct sr_option *opt;
+
+       opt = sr_output_options_get(sr_output_find("bits"));
+       fail_unless(opt != NULL, "Couldn't find 'bits' options.");
+       fail_unless(!strcmp(opt->id, "width"), "Wrong 'bits' option found!");
+}
+END_TEST
+
 Suite *suite_output_all(void)
 {
        Suite *s;
@@ -42,6 +105,11 @@ Suite *suite_output_all(void)
 
        tc = tcase_create("basic");
        tcase_add_test(tc, test_output_available);
+       tcase_add_test(tc, test_output_id);
+       tcase_add_test(tc, test_output_name);
+       tcase_add_test(tc, test_output_desc);
+       tcase_add_test(tc, test_output_find);
+       tcase_add_test(tc, test_output_options);
        suite_add_tcase(s, tc);
 
        return s;
index aca44deb94da0041848909626ec2635cc97e1c4b..f1c3c0159a1b4cd52308ccd505be1d060899d4a5 100644 (file)
@@ -64,25 +64,6 @@ struct sr_input_format *srtest_input_get(const char *id)
        return input;
 }
 
-/* Get a libsigrok output format by ID. */
-struct sr_output_format *srtest_output_get(const char *id)
-{
-       struct sr_output_format **outputs, *output = NULL;
-       int i;
-
-       outputs = sr_output_list();
-       fail_unless(outputs != NULL, "No output modules found.");
-
-       for (i = 0; outputs[i]; i++) {
-               if (strcmp(outputs[i]->id, id))
-                       continue;
-               output = outputs[i];
-       }
-       fail_unless(output != NULL, "Output module '%s' not found.", id);
-
-       return output;
-}
-
 /* Initialize a libsigrok driver. */
 void srtest_driver_init(struct sr_context *sr_ctx, struct sr_dev_driver *driver)
 {
index 8f8e3b02d83a9abe3f4a171142527dd80424b379..daed18daffc218727688228c93c009d41399b2fb 100644 (file)
@@ -25,7 +25,6 @@
 
 struct sr_dev_driver *srtest_driver_get(const char *drivername);
 struct sr_input_format *srtest_input_get(const char *id);
-struct sr_output_format *srtest_output_get(const char *id);
 
 void srtest_driver_init(struct sr_context *sr_ctx, struct sr_dev_driver *driver);
 void srtest_driver_init_all(struct sr_context *sr_ctx);