]> sigrok.org Git - libsigrok.git/blob - src/input/input.c
input: Introduce new input module API.
[libsigrok.git] / src / input / input.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2014 Bert Vermeulen <bert@biot.com>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <string.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 #include "libsigrok.h"
27 #include "libsigrok-internal.h"
28
29 #define LOG_PREFIX "input"
30
31 /**
32  * @file
33  *
34  * Input file/data format handling.
35  */
36
37 /**
38  * @defgroup grp_input Input modules
39  *
40  * Input file/data module handling.
41  *
42  * libsigrok can process acquisition data in several different ways.
43  * Aside from acquiring data from a hardware device, it can also take it from
44  * a file in various formats (binary, CSV, VCD, and so on).
45  *
46  * Like everything in libsigrok that handles data, processing is done in a
47  * streaming manner -- input should be supplied to libsigrok a chunk at a time.
48  * This way anything that processes data can do so in real time, without the
49  * user having to wait for the whole thing to be finished.
50  *
51  * Every input module is "pluggable", meaning it's handled as being separate
52  * from the main libsigrok, but linked in to it statically. To keep things
53  * modular and separate like this, functions within an input module should be
54  * declared static, with only the respective 'struct sr_input_module' being
55  * exported for use into the wider libsigrok namespace.
56  *
57  * @{
58  */
59
60 /** @cond PRIVATE */
61 extern SR_PRIV struct sr_input_module input_chronovu_la8;
62 extern SR_PRIV struct sr_input_module input_csv;
63 extern SR_PRIV struct sr_input_module input_binary;
64 extern SR_PRIV struct sr_input_module input_vcd;
65 extern SR_PRIV struct sr_input_module input_wav;
66 /* @endcond */
67
68 static const struct sr_input_module *input_module_list[] = {
69 //      &input_vcd,
70 //      &input_chronovu_la8,
71         &input_wav,
72 //      &input_csv,
73         /* This one has to be last, because it will take any input. */
74 //      &input_binary,
75         NULL,
76 };
77
78 /** @since 0.4.0 */
79 SR_API const struct sr_input_module **sr_input_list(void)
80 {
81         return input_module_list;
82 }
83
84 /**
85  * Returns the specified input module's ID.
86  *
87  * @since 0.4.0
88  */
89 SR_API const char *sr_input_id_get(const struct sr_input_module *o)
90 {
91         if (!o) {
92                 sr_err("Invalid input module NULL!");
93                 return NULL;
94         }
95
96         return o->id;
97 }
98
99 /**
100  * Returns the specified input module's name.
101  *
102  * @since 0.4.0
103  */
104 SR_API const char *sr_input_name_get(const struct sr_input_module *o)
105 {
106         if (!o) {
107                 sr_err("Invalid input module NULL!");
108                 return NULL;
109         }
110
111         return o->name;
112 }
113
114 /**
115  * Returns the specified input module's description.
116  *
117  * @since 0.4.0
118  */
119 SR_API const char *sr_input_description_get(const struct sr_input_module *o)
120 {
121         if (!o) {
122                 sr_err("Invalid input module NULL!");
123                 return NULL;
124         }
125
126         return o->desc;
127 }
128
129 /**
130  * Return the input module with the specified ID, or NULL if no module
131  * with that id is found.
132  *
133  * @since 0.4.0
134  */
135 SR_API const struct sr_input_module *sr_input_find(char *id)
136 {
137         int i;
138
139         for (i = 0; input_module_list[i]; i++) {
140                 if (!strcmp(input_module_list[i]->id, id))
141                         return input_module_list[i];
142         }
143
144         return NULL;
145 }
146
147 /**
148  * Returns a NULL-terminated array of struct sr_option, or NULL if the
149  * module takes no options.
150  *
151  * Each call to this function must be followed by a call to
152  * sr_input_options_free().
153  *
154  * @since 0.4.0
155  */
156 SR_API const struct sr_option *sr_input_options_get(const struct sr_input_module *o)
157 {
158
159         if (!o || !o->options)
160                 return NULL;
161
162         return o->options();
163 }
164
165 /**
166  * After a call to sr_input_options_get(), this function cleans up all
167  * the resources allocated by that call.
168  *
169  * @since 0.4.0
170  */
171 SR_API void sr_input_options_free(const struct sr_input_module *o)
172 {
173         struct sr_option *opt;
174
175         if (!o || !o->options)
176                 return;
177
178         for (opt = o->options(); opt->id; opt++) {
179                 if (opt->def) {
180                         g_variant_unref(opt->def);
181                         opt->def = NULL;
182                 }
183
184                 if (opt->values) {
185                         g_slist_free_full(opt->values, (GDestroyNotify)g_variant_unref);
186                         opt->values = NULL;
187                 }
188         }
189 }
190
191 /**
192  * Create a new input instance using the specified input module.
193  *
194  * This function is used when a client wants to use a specific input
195  * module to parse a stream. No effort is made to identify the format.
196  *
197  * <code>options</code> is a *HashTable with the keys corresponding with
198  * the module options' <code>id</code> field. The values should be GVariant
199  * pointers with sunk * references, of the same GVariantType as the option's
200  * default value.
201  *
202  * @since 0.4.0
203  */
204 SR_API struct sr_input *sr_input_new(const struct sr_input_module *imod,
205                 GHashTable *options)
206 {
207         struct sr_input *in;
208         struct sr_option *mod_opts;
209         const GVariantType *gvt;
210         GHashTable *new_opts;
211         GHashTableIter iter;
212         gpointer key, value;
213         int i;
214
215         in = g_malloc0(sizeof(struct sr_input));
216         in->module = imod;
217
218         if (options) {
219                 new_opts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
220                                 (GDestroyNotify)g_variant_unref);
221                 mod_opts = imod->options();
222                 for (i = 0; mod_opts[i].id; i++) {
223                         if (g_hash_table_lookup_extended(options, mod_opts[i].id,
224                                         &key, &value)) {
225                                 /* Option not given: insert the default value. */
226                                 gvt = g_variant_get_type(mod_opts[i].def);
227                                 if (!g_variant_is_of_type(value, gvt)) {
228                                         sr_err("Invalid type for '%s' option.", key);
229                                         g_free(in);
230                                         return NULL;
231                                 }
232                                 g_hash_table_insert(new_opts, g_strdup(mod_opts[i].id),
233                                                 g_variant_ref(value));
234                         } else {
235                                 /* Pass option along. */
236                                 g_hash_table_insert(new_opts, g_strdup(mod_opts[i].id),
237                                                 g_variant_ref(mod_opts[i].def));
238                         }
239                 }
240
241                 /* Make sure no invalid options were given. */
242                 g_hash_table_iter_init(&iter, options);
243                 while (g_hash_table_iter_next(&iter, &key, &value)) {
244                         if (!g_hash_table_lookup(new_opts, key)) {
245                                 sr_err("Input module '%s' has no option '%s'", imod->id, key);
246                                 g_hash_table_destroy(new_opts);
247                                 g_free(in);
248                                 return NULL;
249                         }
250                 }
251         } else
252                 new_opts = NULL;
253
254         if (in->module->init && in->module->init(in, new_opts) != SR_OK) {
255                 g_hash_table_destroy(new_opts);
256                 g_free(in);
257                 in = NULL;
258         }
259         if (new_opts)
260                 g_hash_table_destroy(new_opts);
261
262         return in;
263 }
264
265 /* Returns TRUE is all required meta items are available. */
266 static gboolean check_required_metadata(const uint8_t *metadata, uint8_t *avail)
267 {
268         int m, a;
269         uint8_t reqd;
270
271         for (m = 0; metadata[m]; m++) {
272                 if (!metadata[m] & SR_INPUT_META_REQUIRED)
273                         continue;
274                 reqd = metadata[m] & ~SR_INPUT_META_REQUIRED;
275                 for (a = 0; avail[a]; a++) {
276                         if (avail[a] == reqd)
277                                 break;
278                 }
279                 if (!avail[a])
280                         /* Found a required meta item that isn't available. */
281                         return FALSE;
282         }
283
284         return TRUE;
285 }
286
287 /**
288  * Try to find an input module that can parse the given buffer.
289  *
290  * The buffer must contain enough of the beginning of the file for
291  * the input modules to find a match. This is format-dependent, but
292  * 128 bytes is normally enough.
293  *
294  * If an input module is found, an instance is created and returned.
295  * Otherwise, NULL is returned.
296  *
297  */
298 SR_API const struct sr_input *sr_input_scan_buffer(GString *buf)
299 {
300         struct sr_input *in;
301         const struct sr_input_module *imod;
302         GHashTable *meta;
303         unsigned int m, i;
304         uint8_t mitem, avail_metadata[8];
305
306         /* No more metadata to be had from a buffer. */
307         avail_metadata[0] = SR_INPUT_META_HEADER;
308         avail_metadata[1] = 0;
309
310         in = NULL;
311         for (i = 0; input_module_list[i]; i++) {
312                 imod = input_module_list[i];
313                 if (!imod->metadata[0]) {
314                         /* Module has no metadata for matching so will take
315                          * any input. No point in letting it try to match. */
316                         continue;
317                 }
318                 if (!check_required_metadata(imod->metadata, avail_metadata))
319                         /* Cannot satisfy this module's requirements. */
320                         continue;
321
322                 meta = g_hash_table_new(NULL, NULL);
323                 for (m = 0; m < sizeof(imod->metadata); m++) {
324                         mitem = imod->metadata[m] & ~SR_INPUT_META_REQUIRED;
325                         if (mitem == SR_INPUT_META_HEADER)
326                                 g_hash_table_insert(meta, GINT_TO_POINTER(mitem), buf);
327                 }
328                 if (g_hash_table_size(meta) == 0) {
329                         /* No metadata for this module, so nothing to match. */
330                         g_hash_table_destroy(meta);
331                         continue;
332                 }
333                 if (!imod->format_match(meta)) {
334                         /* Module didn't recognize this buffer. */
335                         g_hash_table_destroy(meta);
336                         continue;
337                 }
338                 g_hash_table_destroy(meta);
339
340                 /* Found a matching module. */
341                 in = sr_input_new(imod, NULL);
342                 in->buf = g_string_new_len(buf->str, buf->len);
343                 break;
344         }
345
346         return in;
347 }
348
349 /**
350  * Try to find an input module that can parse the given file.
351  *
352  * If an input module is found, an instance is created and returned.
353  * Otherwise, NULL is returned.
354  *
355  */
356 SR_API const struct sr_input *sr_input_scan_file(const char *filename)
357 {
358         struct sr_input *in;
359         const struct sr_input_module *imod;
360         GHashTable *meta;
361         GString *buf;
362         struct stat st;
363         unsigned int midx, m, i;
364         int fd;
365         ssize_t size;
366         uint8_t mitem, avail_metadata[8];
367
368         if (!filename || !filename[0]) {
369                 sr_err("Invalid filename.");
370                 return NULL;
371         }
372
373         if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
374                 sr_err("No such file.");
375                 return NULL;
376         }
377
378         if (stat(filename, &st) < 0) {
379                 sr_err("%s", strerror(errno));
380                 return NULL;
381         }
382
383         midx = 0;
384         avail_metadata[midx++] = SR_INPUT_META_FILENAME;
385         avail_metadata[midx++] = SR_INPUT_META_FILESIZE;
386         avail_metadata[midx++] = SR_INPUT_META_HEADER;
387         /* TODO: MIME type */
388
389         in = NULL;
390         buf = NULL;
391         for (i = 0; input_module_list[i]; i++) {
392                 imod = input_module_list[i];
393                 if (!imod->metadata[0]) {
394                         /* Module has no metadata for matching so will take
395                          * any input. No point in letting it try to match. */
396                         continue;
397                 }
398                 if (!check_required_metadata(imod->metadata, avail_metadata))
399                         /* Cannot satisfy this module's requirements. */
400                         continue;
401
402                 meta = g_hash_table_new(NULL, NULL);
403                 for (m = 0; m < sizeof(imod->metadata); m++) {
404                         mitem = imod->metadata[m] & ~SR_INPUT_META_REQUIRED;
405                         if (mitem == SR_INPUT_META_FILENAME)
406                                 g_hash_table_insert(meta, GINT_TO_POINTER(mitem),
407                                                 (gpointer)filename);
408                         else if (mitem == SR_INPUT_META_FILESIZE)
409                                 g_hash_table_insert(meta, GINT_TO_POINTER(mitem),
410                                                 GINT_TO_POINTER(st.st_size));
411                         else if (mitem == SR_INPUT_META_HEADER) {
412                                 buf = g_string_sized_new(128);
413                                 if ((fd = open(filename, O_RDONLY)) < 0) {
414                                         sr_err("%s", strerror(errno));
415                                         return NULL;
416                                 }
417                                 size = read(fd, buf->str, 128);
418                                 buf->len = size;
419                                 close(fd);
420                                 if (size <= 0) {
421                                         g_string_free(buf, TRUE);
422                                         sr_err("%s", strerror(errno));
423                                         return NULL;
424                                 }
425                                 g_hash_table_insert(meta, GINT_TO_POINTER(mitem), buf);
426                                 g_string_free(buf, TRUE);
427                         }
428                 }
429                 if (g_hash_table_size(meta) == 0) {
430                         /* No metadata for this module, so there's no way it
431                          * can match. */
432                         g_hash_table_destroy(meta);
433                         continue;
434                 }
435                 if (!imod->format_match(meta))
436                         /* Module didn't recognize this buffer. */
437                         continue;
438
439                 /* Found a matching module. */
440                 in = sr_input_new(imod, NULL);
441                 in->buf = g_string_new_len(buf->str, buf->len);
442                 break;
443         }
444         if (!in && buf)
445                 g_string_free(buf, TRUE);
446
447         return in;
448 }
449
450 SR_API struct sr_dev_inst *sr_input_dev_inst_get(const struct sr_input *in)
451 {
452         return in->sdi;
453 }
454
455 /**
456  * Send data to the specified input instance.
457  *
458  * When an input module instance is created with sr_input_new(), this
459  * function is used to feed data to the instance.
460  *
461  * @since 0.4.0
462  */
463 SR_API int sr_input_send(const struct sr_input *in, GString *buf)
464 {
465         return in->module->receive(in, buf);
466 }
467
468 /**
469  * Free the specified input instance and all associated resources.
470  *
471  * @since 0.4.0
472  */
473 SR_API int sr_input_free(const struct sr_input *in)
474 {
475         int ret;
476
477         if (!in)
478                 return SR_ERR_ARG;
479
480         ret = SR_OK;
481         if (in->module->cleanup)
482                 ret = in->module->cleanup((struct sr_input *)in);
483         if (in->buf)
484                 g_string_free(in->buf, TRUE);
485         g_free((gpointer)in);
486
487         return ret;
488 }
489
490
491 /** @} */