]> sigrok.org Git - sigrok-test.git/blobdiff - decoder/runtc.c
avr_isp: add test for ATmega328/P
[sigrok-test.git] / decoder / runtc.c
index bbeb40ef04ecc3929ce86707b73685f2c4eb5d02..074882629350a5e28cb3f6c90ab1bb8f9ea34cdb 100644 (file)
@@ -17,6 +17,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
+
 #include <Python.h>
 #include <libsigrokdecode/libsigrokdecode.h>
 #include <libsigrok/libsigrok.h>
@@ -37,6 +39,8 @@
 #include <sched.h>
 #endif
 
+#define CHUNK_SIZE     (4 * 1024 * 1024)
+
 static int debug = FALSE;
 static int statistics = FALSE;
 static char *coverage_report;
@@ -52,16 +56,30 @@ struct option {
        GVariant *value;
 };
 
+struct initial_pin_info {
+       char *name;
+       int value;
+};
+
 struct pd {
        const char *name;
        GSList *channels;
        GSList *options;
+       GSList *initial_pins;
+};
+
+struct input {
+       const char *filename;
+       const char *format;
+       GSList *opt_list;
+       GHashTable *opt_hash;
 };
 
 struct output {
        const char *pd;
+       const char *pd_id;
        int type;
-       const char *class;
+       const char *class_;
        int class_idx;
        const char *outfile;
        int outfd;
@@ -136,15 +154,18 @@ static void usage(const char *msg)
        if (msg)
                fprintf(stderr, "%s\n", msg);
 
-       printf("Usage: runtc [-dPpoiOf]\n");
-       printf("  -d  Debug\n");
-       printf("  -P  <protocol decoder>\n");
-       printf("  -p  <channelname=channelnum> (optional)\n");
-       printf("  -o  <channeloption=value> (optional)\n");
+       printf("Usage: runtc [-dPpoiOfcS]\n");
+       printf("  -d  (enables debug output)\n");
+       printf("  -P <protocol decoder>\n");
+       printf("  -p <channelname=channelnum> (optional)\n");
+       printf("  -o <channeloption=value> (optional)\n");
+       printf("  -N <channelname=initial-pin-value> (optional)\n");
        printf("  -i <input file>\n");
+       printf("  -I <input format> (optional)\n");
        printf("  -O <output-pd:output-type[:output-class]>\n");
        printf("  -f <output file> (optional)\n");
        printf("  -c <coverage report> (optional)\n");
+       printf("  -S  (enables statistics)\n");
        exit(msg ? 1 : 0);
 
 }
@@ -167,6 +188,28 @@ static char *py_str_as_str(const PyObject *py_str)
        return outstr;
 }
 
+/*
+ * The following routines are callbacks for libsigrokdecode. They receive
+ * output from protocol decoders, optionally dropping data to only forward
+ * a selected decoder's or class' information. Output is written to either
+ * a specified file or stdout, an external process will compare captured
+ * output against expectations.
+ *
+ * Note that runtc(1) output emits the decoder "class" name instead of the
+ * instance name. So that generated output remains compatible with existing
+ * .output files which hold expected output of test cases. Without this
+ * approach, developers had to "anticipate" instance names from test.conf
+ * setups (and knowledge about internal implementation details of the srd
+ * library), and adjust .output files to reflect those names. Or specify
+ * instance names in each and every test.conf description (-o inst_id=ID).
+ *
+ * It's assumed that runtc(1) is used to check stacked decoders, but not
+ * multiple stacks in parallel and no stacks with multiple instances of
+ * decoders of the same type. When such configurations become desirable,
+ * runtc(1) needs to emit the instance name, and test configurations and
+ * output expectations need adjustment.
+ */
+
 static void srd_cb_py(struct srd_proto_data *pdata, void *cb_data)
 {
        struct output *op;
@@ -179,7 +222,7 @@ static void srd_cb_py(struct srd_proto_data *pdata, void *cb_data)
        pydata = pdata->data;
        DBG("ptr %p", pydata);
 
-       if (strcmp(pdata->pdo->di->inst_id, op->pd))
+       if (strcmp(pdata->pdo->di->inst_id, op->pd_id))
                /* This is not the PD selected for output. */
                return;
 
@@ -190,11 +233,11 @@ static void srd_cb_py(struct srd_proto_data *pdata, void *cb_data)
        s = py_str_as_str(pyrepr);
        Py_DecRef(pyrepr);
 
-       /* Output format for testing is '<ss>-<es> <inst-id>: <repr>\n'. */
+       /* Output format for testing is '<ss>-<es> <decoder-id>: <repr>\n'. */
        out = g_string_sized_new(128);
        g_string_printf(out, "%" PRIu64 "-%" PRIu64 " %s: %s\n",
                        pdata->start_sample, pdata->end_sample,
-                       pdata->pdo->di->inst_id, s);
+                       pdata->pdo->di->decoder->id, s);
        g_free(s);
        if (write(op->outfd, out->str, out->len) == -1)
                ERR("SRD_OUTPUT_PYTHON callback write failure!");
@@ -214,7 +257,7 @@ static void srd_cb_bin(struct srd_proto_data *pdata, void *cb_data)
        op = cb_data;
        pdb = pdata->data;
 
-       if (strcmp(pdata->pdo->di->inst_id, op->pd))
+       if (strcmp(pdata->pdo->di->inst_id, op->pd_id))
                /* This is not the PD selected for output. */
                return;
 
@@ -228,7 +271,7 @@ static void srd_cb_bin(struct srd_proto_data *pdata, void *cb_data)
        out = g_string_sized_new(128);
        g_string_printf(out, "%" PRIu64 "-%" PRIu64 " %s:",
                        pdata->start_sample, pdata->end_sample,
-                       pdata->pdo->di->inst_id);
+                       pdata->pdo->di->decoder->id);
        for (i = 0; i < pdb->size; i++) {
                g_string_append_printf(out, " %.2x", pdb->data[i]);
        }
@@ -240,6 +283,7 @@ static void srd_cb_bin(struct srd_proto_data *pdata, void *cb_data)
 
 static void srd_cb_ann(struct srd_proto_data *pdata, void *cb_data)
 {
+       struct srd_decoder_inst *di;
        struct srd_decoder *dec;
        struct srd_proto_data_annotation *pda;
        struct output *op;
@@ -247,11 +291,17 @@ static void srd_cb_ann(struct srd_proto_data *pdata, void *cb_data)
        int i;
        char **dec_ann;
 
-       DBG("Annotation output from %s", pdata->pdo->di->inst_id);
+       /*
+        * Only inspect received annotations when they originate from
+        * the selected protocol decoder, and an optionally specified
+        * annotation class matches the received data.
+        */
        op = cb_data;
        pda = pdata->data;
-       dec = pdata->pdo->di->decoder;
-       if (strcmp(pdata->pdo->di->inst_id, op->pd))
+       di = pdata->pdo->di;
+       dec = di->decoder;
+       DBG("Annotation output from %s", di->inst_id);
+       if (strcmp(di->inst_id, op->pd_id))
                /* This is not the PD selected for output. */
                return;
 
@@ -262,11 +312,17 @@ static void srd_cb_ann(struct srd_proto_data *pdata, void *cb_data)
                 */
                return;
 
+       /*
+        * Print the annotation information in textual representation
+        * to the specified output file. Prefix the annotation strings
+        * with the start and end sample number, the decoder name, and
+        * the annotation name.
+        */
        dec_ann = g_slist_nth_data(dec->annotations, pda->ann_class);
        line = g_string_sized_new(256);
        g_string_printf(line, "%" PRIu64 "-%" PRIu64 " %s: %s:",
                        pdata->start_sample, pdata->end_sample,
-                       pdata->pdo->di->inst_id, dec_ann[0]);
+                       dec->id, dec_ann[0]);
        for (i = 0; pda->ann_text[i]; i++)
                g_string_append_printf(line, " \"%s\"", pda->ann_text[i]);
        g_string_append(line, "\n");
@@ -280,10 +336,16 @@ static void sr_cb(const struct sr_dev_inst *sdi,
                const struct sr_datafeed_packet *packet, void *cb_data)
 {
        static int samplecnt = 0;
+       static gboolean start_sent;
+
        const struct sr_datafeed_logic *logic;
        struct srd_session *sess;
+       const struct sr_datafeed_meta *meta;
+       struct sr_config *src;
+       GSList *l;
        GVariant *gvar;
        uint64_t samplerate;
+       int ret;
        int num_samples;
        struct sr_dev_driver *driver;
 
@@ -292,26 +354,49 @@ static void sr_cb(const struct sr_dev_inst *sdi,
        driver = sr_dev_inst_driver_get(sdi);
 
        switch (packet->type) {
+       case SR_DF_META:
+               DBG("Received SR_DF_META");
+               meta = packet->payload;
+               for (l = meta->config; l; l = l->next) {
+                       src = l->data;
+                       switch (src->key) {
+                       case SR_CONF_SAMPLERATE:
+                               samplerate = g_variant_get_uint64(src->data);
+                               ret = srd_session_metadata_set(sess,
+                                       SRD_CONF_SAMPLERATE,
+                                       g_variant_new_uint64(samplerate));
+                               if (ret != SRD_OK)
+                                       ERR("Setting samplerate failed (meta)");
+                               break;
+                       default:
+                               /* EMPTY */
+                               break;
+                       }
+               }
+               break;
        case SR_DF_HEADER:
                DBG("Received SR_DF_HEADER");
                if (sr_config_get(driver, sdi, NULL, SR_CONF_SAMPLERATE,
                                &gvar) != SR_OK) {
-                       ERR("Getting samplerate failed");
+                       DBG("Getting samplerate failed (SR_DF_HEADER)");
                        break;
                }
                samplerate = g_variant_get_uint64(gvar);
                g_variant_unref(gvar);
-               if (srd_session_metadata_set(sess, SRD_CONF_SAMPLERATE,
-                               g_variant_new_uint64(samplerate)) != SRD_OK) {
-                       ERR("Setting samplerate failed");
-                       break;
-               }
-               if (srd_session_start(sess) != SRD_OK) {
-                       ERR("Session start failed");
-                       break;
-               }
+               ret = srd_session_metadata_set(sess, SRD_CONF_SAMPLERATE,
+                       g_variant_new_uint64(samplerate));
+               if (ret != SRD_OK)
+                       ERR("Setting samplerate failed (header)");
                break;
        case SR_DF_LOGIC:
+               DBG("Received SR_DF_LOGIC");
+               if (!start_sent) {
+                       if (srd_session_start(sess) != SRD_OK) {
+                               ERR("Session start failed");
+                               break;
+                       }
+                       start_sent = TRUE;
+               }
                logic = packet->payload;
                num_samples = logic->length / logic->unitsize;
                DBG("Received SR_DF_LOGIC (%"PRIu64" bytes, unitsize = %d).",
@@ -322,12 +407,113 @@ static void sr_cb(const struct sr_dev_inst *sdi,
                break;
        case SR_DF_END:
                DBG("Received SR_DF_END");
+#if defined HAVE_SRD_SESSION_SEND_EOF && HAVE_SRD_SESSION_SEND_EOF
+               (void)srd_session_send_eof(sess);
+#endif
                break;
        }
 
 }
 
-static int run_testcase(const char *infile, GSList *pdlist, struct output *op)
+static GHashTable *parse_input_options(const struct sr_option **pd_opts,
+       GSList *user_specs)
+{
+       GHashTable *set_opts;
+       GVariant *pd_def, *gvar;
+       size_t idx;
+       GSList *l;
+       const char *pd_key;
+       const char *spec_text, *s;
+
+       set_opts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+               (GDestroyNotify)g_variant_unref);
+       for (idx = 0; pd_opts[idx]; idx++) {
+               pd_key = pd_opts[idx]->id;
+               /* Is the PD provided option in the set of input specs? */
+               s = NULL;
+               for (l = user_specs; l; l = l->next) {
+                       spec_text = l->data;
+                       if (strncmp(spec_text, pd_key, strlen(pd_key)) != 0)
+                               continue;
+                       spec_text += strlen(pd_key);
+                       if (!*spec_text) {
+                               /* Found 'key' up to end of text. */
+                               s = spec_text;
+                               break;
+                       }
+                       if (*spec_text == '=') {
+                               /* Found 'key=...', position to RHS value. */
+                               s = ++spec_text;
+                               break;
+                       }
+               }
+               if (!s)
+                       continue;
+               /*
+                * Normalize the input text for the user specified value.
+                * A key without an explicit value is useful for booleans.
+                */
+               if (!*s)
+                       s = "";
+               /*
+                * Convert the text to the PD default value's data type.
+                * Store the resulting variable in the hash which gets
+                * passed to the input module.
+                */
+               pd_def = pd_opts[idx]->def;
+               if (g_variant_is_of_type(pd_def, G_VARIANT_TYPE_UINT32)) {
+                       gvar = g_variant_new_uint32(strtoul(s, NULL, 10));
+                       g_hash_table_insert(set_opts, g_strdup(pd_key),
+                               g_variant_ref_sink(gvar));
+                       continue;
+               }
+               if (g_variant_is_of_type(pd_def, G_VARIANT_TYPE_INT32)) {
+                       gvar = g_variant_new_int32(strtol(s, NULL, 10));
+                       g_hash_table_insert(set_opts, g_strdup(pd_key),
+                               g_variant_ref_sink(gvar));
+                       continue;
+               }
+               if (g_variant_is_of_type(pd_def, G_VARIANT_TYPE_UINT64)) {
+                       gvar = g_variant_new_uint64(strtoull(s, NULL, 10));
+                       g_hash_table_insert(set_opts, g_strdup(pd_key),
+                               g_variant_ref_sink(gvar));
+                       continue;
+               }
+               if (g_variant_is_of_type(pd_def, G_VARIANT_TYPE_DOUBLE)) {
+                       gvar = g_variant_new_double(strtod(s, NULL));
+                       g_hash_table_insert(set_opts, g_strdup(pd_key),
+                               g_variant_ref_sink(gvar));
+                       continue;
+               }
+               if (g_variant_is_of_type(pd_def, G_VARIANT_TYPE_STRING)) {
+                       gvar = g_variant_new_string(s);
+                       g_hash_table_insert(set_opts, g_strdup(pd_key),
+                               g_variant_ref_sink(gvar));
+                       continue;
+               }
+               if (g_variant_is_of_type(pd_def, G_VARIANT_TYPE_BOOLEAN)) {
+                       gboolean b;
+                       if (strcmp(s, "false") == 0 || strcmp(s, "no") == 0) {
+                               b = FALSE;
+                       } else if (strcmp(s, "true") == 0 || strcmp(s, "yes") == 0) {
+                               b = TRUE;
+                       } else {
+                               ERR("Cannot convert '%s' to boolean", s);
+                               return NULL;
+                       }
+                       gvar = g_variant_new_boolean(b);
+                       g_hash_table_insert(set_opts, g_strdup(pd_key),
+                               g_variant_ref_sink(gvar));
+                       continue;
+               }
+               ERR("Unsupported data type for option '%s'", pd_key);
+               return NULL;
+       }
+
+       return set_opts;
+}
+
+static int run_testcase(struct input *inp, GSList *pdlist, struct output *op)
 {
        struct srd_session *sess;
        struct srd_decoder *dec;
@@ -338,13 +524,19 @@ static int run_testcase(const char *infile, GSList *pdlist, struct output *op)
        struct option *option;
        GVariant *gvar;
        GHashTable *channels, *opts;
-       GSList *pdl, *l, *devices;
-       int idx, i;
+       GSList *pdl, *l, *l2, *devices;
+       int ret, idx, i;
        int max_channel;
        char **decoder_class;
        struct sr_session *sr_sess;
+       const struct sr_input *in;
+       const struct sr_input_module *imod;
+       const struct sr_option **options;
+       GHashTable *mod_opts;
        gboolean is_number;
        const char *s;
+       GArray *initial_pins;
+       struct initial_pin_info *initial_pin;
 
        if (op->outfile) {
                if ((op->outfd = open(op->outfile, O_CREAT|O_WRONLY, 0600)) == -1) {
@@ -354,13 +546,81 @@ static int run_testcase(const char *infile, GSList *pdlist, struct output *op)
                }
        }
 
-       if (sr_session_load(ctx, infile, &sr_sess) != SR_OK)
+       /* Tell "session files" (.sr format) from other input modules. */
+       sr_sess = NULL;
+       in = NULL;
+       if (inp->format && strcmp(inp->format, "match") == 0) {
+               /* Automatic format detection. */
+               if (inp->opt_list) {
+                       ERR("Automatic file format won't take options");
+                       return FALSE;
+               }
+               ret = sr_input_scan_file(inp->filename, &in);
+               if (ret != SR_OK || !in) {
+                       ERR("Cannot open input file (format match)");
+                       return FALSE;
+               }
+               sr_session_new(ctx, &sr_sess);
+               ret = SR_OK;
+       } else if (inp->format) {
+               /* Caller specified format, potentially with options. */
+               imod = sr_input_find(inp->format);
+               if (!imod) {
+                       ERR("Cannot find specified input module");
+                       return FALSE;
+               }
+               mod_opts = NULL;
+               options = sr_input_options_get(imod);
+               if (!options && inp->opt_list) {
+                       ERR("Input module does not support options");
+                       return FALSE;
+               }
+               if (inp->opt_list) {
+                       mod_opts = parse_input_options(options, inp->opt_list);
+                       if (!mod_opts) {
+                               ERR("Cannot process input module options");
+                               return FALSE;
+                       }
+               }
+               sr_input_options_free(options);
+               in = sr_input_new(imod, mod_opts);
+               if (!in) {
+                       ERR("Cannot create input module instance");
+                       return FALSE;
+               }
+               if (mod_opts)
+                       g_hash_table_destroy(mod_opts);
+               sr_session_new(ctx, &sr_sess);
+               ret = SR_OK;
+       } else {
+               /* No caller's format spec, assume .sr session file. */
+               ret = sr_session_load(ctx, inp->filename, &sr_sess);
+               if (ret != SR_OK || !sr_sess) {
+                       ERR("Cannot open session file");
+                       return FALSE;
+               }
+       }
+       if (ret != SR_OK || !sr_sess) {
+               ERR("Failed to open input file");
                return FALSE;
+       }
+       if (in) {
+               /* Check file access early for non-session files. */
+               int fd;
+               fd = open(inp->filename, O_RDONLY);
+               if (fd < 0) {
+                       ERR("Cannot access input file (input module)");
+                       return FALSE;
+               }
+               close(fd);
+       }
 
        sr_session_dev_list(sr_sess, &devices);
 
-       if (srd_session_new(&sess) != SRD_OK)
+       if (srd_session_new(&sess) != SRD_OK) {
+               ERR("srd_session_new() failed");
                return FALSE;
+       }
        sr_session_datafeed_callback_add(sr_sess, sr_cb, sess);
        switch (op->type) {
        case SRD_OUTPUT_ANN:
@@ -373,6 +633,7 @@ static int run_testcase(const char *infile, GSList *pdlist, struct output *op)
                cb = srd_cb_py;
                break;
        default:
+               ERR("Invalid op->type");
                return FALSE;
        }
        srd_pd_output_callback_add(sess, op->type, cb, op);
@@ -381,8 +642,10 @@ static int run_testcase(const char *infile, GSList *pdlist, struct output *op)
        pd = NULL;
        for (pdl = pdlist; pdl; pdl = pdl->next) {
                pd = pdl->data;
-               if (srd_decoder_load(pd->name) != SRD_OK)
+               if (srd_decoder_load(pd->name) != SRD_OK) {
+                       ERR("srd_decoder_load() failed");
                        return FALSE;
+               }
 
                /* Instantiate decoder and pass in options. */
                opts = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
@@ -406,10 +669,23 @@ static int run_testcase(const char *infile, GSList *pdlist, struct output *op)
                                g_hash_table_insert(opts, option->key, option->value);
                        }
                }
-               if (!(di = srd_inst_new(sess, pd->name, opts)))
+               if (!(di = srd_inst_new(sess, pd->name, opts))) {
+                       ERR("srd_inst_new() failed");
                        return FALSE;
+               }
                g_hash_table_destroy(opts);
 
+               /*
+                * Get (a reference to) the decoder instance's ID if we
+                * are about to receive PD output from it. We need to
+                * filter output that carries the decoder instance's name.
+                */
+               if (strcmp(pd->name, op->pd) == 0) {
+                       op->pd_id = di->inst_id;
+                       DBG("Decoder of type \"%s\" has instance ID \"%s\".",
+                           op->pd, op->pd_id);
+               }
+
                /* Map channels. */
                if (pd->channels) {
                        channels = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
@@ -424,11 +700,37 @@ static int run_testcase(const char *infile, GSList *pdlist, struct output *op)
                                g_hash_table_insert(channels, channel->name, gvar);
                        }
 
-                       if (srd_inst_channel_set_all(di, channels) != SRD_OK)
+                       if (srd_inst_channel_set_all(di, channels) != SRD_OK) {
+                               ERR("srd_inst_channel_set_all() failed");
                                return FALSE;
+                       }
                        g_hash_table_destroy(channels);
                }
 
+               /* Set initial pins. */
+               if (pd->initial_pins) {
+                       initial_pins = g_array_sized_new(FALSE, TRUE, sizeof(uint8_t),
+                                               di->dec_num_channels);
+                       g_array_set_size(initial_pins, di->dec_num_channels);
+                       memset(initial_pins->data, SRD_INITIAL_PIN_SAME_AS_SAMPLE0,
+                               di->dec_num_channels);
+
+                       for (l = pd->channels, idx = 0; l; l = l->next, idx++) {
+                               channel = l->data;
+                               for (l2 = pd->initial_pins; l2; l2 = l2->next) {
+                                       initial_pin = l2->data;
+                                       if (!strcmp(initial_pin->name, channel->name))
+                                               initial_pins->data[idx] = initial_pin->value;
+                               }
+                       }
+
+                       if (srd_inst_initial_pins_set_all(di, initial_pins) != SRD_OK) {
+                               ERR("srd_inst_initial_pins_set_all() failed");
+                               return FALSE;
+                       }
+                       g_array_free(initial_pins, TRUE);
+               }
+
                /*
                 * If this is not the first decoder in the list, stack it
                 * on top of the previous one.
@@ -441,38 +743,105 @@ static int run_testcase(const char *infile, GSList *pdlist, struct output *op)
                }
                prev_di = di;
        }
+       /*
+        * Bail out if we haven't created an instance of the selected
+        * decoder type of which we shall grab output data from.
+        */
+       if (!op->pd_id) {
+               ERR("No / invalid decoder");
+               return FALSE;
+       }
 
-       /* Resolve top decoder's class index, so we can match. */
+       /* Resolve selected decoder's class index, so we can match. */
        dec = srd_decoder_get_by_id(pd->name);
-       if (op->class) {
+       if (op->class_) {
                if (op->type == SRD_OUTPUT_ANN)
                        l = dec->annotations;
                else if (op->type == SRD_OUTPUT_BINARY)
                        l = dec->binary;
-               else
+               else {
                        /* Only annotations and binary can have a class. */
+                       ERR("Invalid decoder class");
                        return FALSE;
+               }
                idx = 0;
                while (l) {
                        decoder_class = l->data;
-                       if (!strcmp(decoder_class[0], op->class)) {
+                       if (!strcmp(decoder_class[0], op->class_)) {
                                op->class_idx = idx;
                                break;
-                       } else
-                               idx++;
+                       }
+                       idx++;
                        l = l->next;
                }
                if (op->class_idx == -1) {
                        ERR("Output class '%s' not found in decoder %s.",
-                                       op->class, pd->name);
+                                       op->class_, pd->name);
                        return FALSE;
-               } else
-                       DBG("Class %s index is %d", op->class, op->class_idx);
+               }
+               DBG("Class %s index is %d", op->class_, op->class_idx);
        }
 
-       sr_session_start(sr_sess);
-       sr_session_run(sr_sess);
-       sr_session_stop(sr_sess);
+       /*
+        * Run a convenience sequence for session files. Run a custom
+        * read loop for non-native file formats (import modules).
+        *
+        * TODO Ideally the libsigrok library would provide transparent
+        * access to either kind of file. Either wrap an input module
+        * similar to "session files", or turn the currently "special"
+        * session file into a regular input module. It's unfortunate
+        * that multiple applications need to re-invent this logic.
+        */
+       if (!in) {
+               sr_session_start(sr_sess);
+               sr_session_run(sr_sess);
+               sr_session_stop(sr_sess);
+       } else if (in) {
+               int fd;
+               GString *buf;
+               gboolean got_sdi;
+               struct sr_dev_inst *sdi;
+               ssize_t len;
+
+               fd = open(inp->filename, O_RDONLY);
+               if (fd < 0) {
+                       ERR("Cannot open input file (read loop)");
+                       return FALSE;
+               }
+               buf = g_string_sized_new(CHUNK_SIZE);
+               got_sdi = FALSE;
+               while (TRUE) {
+                       g_string_truncate(buf, 0);
+                       len = read(fd, buf->str, buf->allocated_len);
+                       if (len < 0) {
+                               ERR("Cannot read from input file (read loop)");
+                               return FALSE;
+                       }
+                       if (len == 0)
+                               break;
+                       buf->len = len;
+                       ret = sr_input_send(in, buf);
+                       if (ret != SR_OK) {
+                               ERR("Cannot process input file content");
+                               break;
+                       }
+
+                       sdi = sr_input_dev_inst_get(in);
+                       if (!got_sdi && sdi) {
+                               /* First time we got a valid sdi. */
+                               if (sr_session_dev_add(sr_sess, sdi) != SR_OK) {
+                                       ERR("Cannot use device after sdi creation");
+                                       break;
+                               }
+                               got_sdi = TRUE;
+                       }
+               }
+               sr_input_end(in);
+               sr_input_free(in);
+               g_string_free(buf, TRUE);
+               close(fd);
+               sr_session_destroy(sr_sess);
+       }
 
        srd_session_destroy(sess);
 
@@ -711,22 +1080,30 @@ int main(int argc, char **argv)
        struct pd *pd;
        struct channel *channel;
        struct option *option;
+       struct input *inp;
        struct output *op;
        int ret, c;
-       char *opt_infile, **kv, **opstr;
+       char **kv, **opstr;
+       struct initial_pin_info *initial_pin;
+
+       inp = malloc(sizeof(*inp));
+       inp->filename = NULL;
+       inp->format = NULL;
+       inp->opt_list = NULL;
+       inp->opt_hash = NULL;
 
        op = malloc(sizeof(struct output));
        op->pd = NULL;
+       op->pd_id = NULL;
        op->type = -1;
-       op->class = NULL;
+       op->class_ = NULL;
        op->class_idx = -1;
        op->outfd = 1;
 
        pdlist = NULL;
-       opt_infile = NULL;
        pd = NULL;
        coverage = NULL;
-       while ((c = getopt(argc, argv, "dP:p:o:i:O:f:c:S")) != -1) {
+       while ((c = getopt(argc, argv, "dP:p:o:N:i:I:O:f:c:S")) != -1) {
                switch (c) {
                case 'd':
                        debug = TRUE;
@@ -734,11 +1111,12 @@ int main(int argc, char **argv)
                case 'P':
                        pd = g_malloc(sizeof(struct pd));
                        pd->name = g_strdup(optarg);
-                       pd->channels = pd->options = NULL;
+                       pd->channels = pd->options = pd->initial_pins = NULL;
                        pdlist = g_slist_append(pdlist, pd);
                        break;
                case 'p':
                case 'o':
+               case 'N':
                        if (g_slist_length(pdlist) == 0) {
                                /* No previous -P. */
                                ERR("Syntax error at '%s'", optarg);
@@ -754,20 +1132,34 @@ int main(int argc, char **argv)
                        if (c == 'p') {
                                channel = malloc(sizeof(struct channel));
                                channel->name = g_strdup(kv[0]);
-                               channel->channel = strtoul(kv[1], 0, 10);
+                               channel->channel = strtoul(kv[1], NULL, 10);
                                /* Apply to last PD. */
                                pd->channels = g_slist_append(pd->channels, channel);
-                       } else {
+                       } else if (c == 'o') {
                                option = malloc(sizeof(struct option));
                                option->key = g_strdup(kv[0]);
                                option->value = g_variant_new_string(kv[1]);
-                g_variant_ref_sink(option->value);
+                               g_variant_ref_sink(option->value);
                                /* Apply to last PD. */
                                pd->options = g_slist_append(pd->options, option);
+                       } else {
+                               initial_pin = malloc(sizeof(struct initial_pin_info));
+                               initial_pin->name = g_strdup(kv[0]);
+                               initial_pin->value = strtoul(kv[1], NULL, 10);
+                               /* Apply to last PD. */
+                               pd->initial_pins = g_slist_append(pd->initial_pins, initial_pin);
                        }
                        break;
                case 'i':
-                       opt_infile = optarg;
+                       inp->filename = optarg;
+                       break;
+               case 'I':
+                       /* First arg is the format name, others are options. */
+                       if (!inp->format) {
+                               inp->format = optarg;
+                               break;
+                       }
+                       inp->opt_list = g_slist_append(inp->opt_list, optarg);
                        break;
                case 'O':
                        opstr = g_strsplit(optarg, ":", 0);
@@ -793,7 +1185,7 @@ int main(int argc, char **argv)
                                usage(NULL);
                        }
                        if (opstr[2])
-                               op->class = g_strdup(opstr[2]);
+                               op->class_ = g_strdup(opstr[2]);
                        g_strfreev(opstr);
                        break;
                case 'f':
@@ -814,7 +1206,7 @@ int main(int argc, char **argv)
                usage(NULL);
        if (g_slist_length(pdlist) == 0)
                usage(NULL);
-       if (!opt_infile)
+       if (!inp->filename)
                usage(NULL);
        if (!op->pd || op->type == -1)
                usage(NULL);
@@ -838,7 +1230,7 @@ int main(int argc, char **argv)
        }
 
        ret = 0;
-       if (!run_testcase(opt_infile, pdlist, op))
+       if (!run_testcase(inp, pdlist, op))
                ret = 1;
 
        if (coverage) {