Implement OUTPUT_BINARY
authorBert Vermeulen <bert@biot.com>
Sun, 10 Nov 2013 11:42:11 +0000 (12:42 +0100)
committerBert Vermeulen <bert@biot.com>
Fri, 15 Nov 2013 21:20:52 +0000 (22:20 +0100)
This requires the PD to have a tuple in its class called 'binary',
which contains a list of strings describing the different binary
classes it can output. For the SPI decoder this might be 'MOSI' and
'MISO', for example.

The data is submitted to the frontend as struct srd_proto_data_binary,
which contains the class that data belongs to.

decoder.c
libsigrokdecode.h.in
type_decoder.c

index 01dcf0a823b6da56a798d646750e355bfdd3de52..ef7e15d1bf40c8db63848a2a562bdb9972e5b354 100644 (file)
--- a/decoder.c
+++ b/decoder.c
@@ -245,10 +245,11 @@ err_out:
  */
 SRD_API int srd_decoder_load(const char *module_name)
 {
-       PyObject *py_basedec, *py_method, *py_attr, *py_annlist, *py_ann;
+       PyObject *py_basedec, *py_method, *py_attr, *py_annlist, *py_ann, \
+               *py_bin_classes, *py_bin_class;
        struct srd_decoder *d;
-       int alen, ret, i;
-       char **ann;
+       int len, ret, i;
+       char **ann, *bin;
        struct srd_probe *p;
        GSList *l;
 
@@ -362,7 +363,7 @@ SRD_API int srd_decoder_load(const char *module_name)
        if (py_attr_as_str(d->py_dec, "license", &(d->license)) != SRD_OK)
                goto err_out;
 
-       /* Convert class annotation attribute to GSList of **char. */
+       /* Convert annotation class attribute to GSList of char **. */
        d->annotations = NULL;
        if (PyObject_HasAttrString(d->py_dec, "annotations")) {
                py_annlist = PyObject_GetAttrString(d->py_dec, "annotations");
@@ -371,8 +372,8 @@ SRD_API int srd_decoder_load(const char *module_name)
                                "should be a list.", module_name);
                        goto err_out;
                }
-               alen = PyList_Size(py_annlist);
-               for (i = 0; i < alen; i++) {
+               len = PyList_Size(py_annlist);
+               for (i = 0; i < len; i++) {
                        py_ann = PyList_GetItem(py_annlist, i);
                        if (!PyList_Check(py_ann) || PyList_Size(py_ann) != 2) {
                                srd_err("Protocol decoder module %s "
@@ -388,6 +389,31 @@ SRD_API int srd_decoder_load(const char *module_name)
                }
        }
 
+       /* Convert binary class to GSList of char *. */
+       d->binary = NULL;
+       if (PyObject_HasAttrString(d->py_dec, "binary")) {
+               py_bin_classes = PyObject_GetAttrString(d->py_dec, "binary");
+               if (!PyTuple_Check(py_bin_classes)) {
+                       srd_err("Protocol decoder module %s binary classes "
+                               "should be a tuple.", module_name);
+                       goto err_out;
+               }
+               len = PyTuple_Size(py_bin_classes);
+               for (i = 0; i < len; i++) {
+                       py_bin_class = PyTuple_GetItem(py_bin_classes, i);
+                       if (!PyUnicode_Check(py_bin_class)) {
+                               srd_err("Protocol decoder module %s binary "
+                                               "class should be a string.", module_name);
+                               goto err_out;
+                       }
+
+                       if (py_str_as_str(py_bin_class, &bin) != SRD_OK) {
+                               goto err_out;
+                       }
+                       d->binary = g_slist_append(d->binary, bin);
+               }
+       }
+
        /* Append it to the list of supported/loaded decoders. */
        pd_list = g_slist_append(pd_list, d);
 
index a654373c570048e6a944ac110eb6e73085e27059..b75942808e2421207df994e49014263d7a43dc34 100644 (file)
@@ -202,6 +202,12 @@ struct srd_decoder {
         */
        GSList *annotations;
 
+       /**
+        * List of NULL-terminated char[], containing descriptions of the
+        * supported binary output.
+        */
+       GSList *binary;
+
        /** List of decoder options.  */
        GSList *options;
 
@@ -266,6 +272,11 @@ struct srd_proto_data_annotation {
        int ann_format;
        char **ann_text;
 };
+struct srd_proto_data_binary {
+       int bin_class;
+       uint64_t size;
+       const unsigned char *data;
+};
 
 typedef void (*srd_pd_output_callback_t)(struct srd_proto_data *pdata,
                                         void *cb_data);
index 52f7ca0640b31a3f53f7403ee39c4dd935415ab7..38fa1ae836d3bc5696327f4695f7507987b936f8 100644 (file)
@@ -93,6 +93,74 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj,
        return SRD_OK;
 }
 
+static int convert_binary(struct srd_decoder_inst *di, PyObject *obj,
+               struct srd_proto_data *pdata)
+{
+       struct srd_proto_data_binary *pdb;
+       PyObject *py_tmp;
+       Py_ssize_t size;
+       int bin_class;
+       char *class_name, *buf;
+
+       /* Should be a tuple of (binary class, bytes). */
+       if (!PyTuple_Check(obj)) {
+               srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY with "
+                               "%s instead of tuple.", di->decoder->name,
+                               obj->ob_type->tp_name);
+               return SRD_ERR_PYTHON;
+       }
+
+       /* Should have 2 elements. */
+       if (PyTuple_Size(obj) != 2) {
+               srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY tuple "
+                               "with %d elements instead of 2", di->decoder->name,
+                               PyList_Size(obj));
+               return SRD_ERR_PYTHON;
+       }
+
+       /* The first element should be an integer. */
+       py_tmp = PyTuple_GetItem(obj, 0);
+       if (!PyLong_Check(py_tmp)) {
+               srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY tuple, "
+                       "but first element was not an integer.", di->decoder->name);
+               return SRD_ERR_PYTHON;
+       }
+       bin_class = PyLong_AsLong(py_tmp);
+       if (!(class_name = g_slist_nth_data(di->decoder->binary, bin_class))) {
+               srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY with "
+                       "unregistered binary class %d.", di->decoder->name, bin_class);
+               return SRD_ERR_PYTHON;
+       }
+
+       /* Second element should be bytes. */
+       py_tmp = PyTuple_GetItem(obj, 1);
+       if (!PyBytes_Check(py_tmp)) {
+               srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY tuple, "
+                       "but second element was not bytes.", di->decoder->name);
+               return SRD_ERR_PYTHON;
+       }
+
+       /* Consider an empty set of bytes a bug. */
+       if (PyBytes_Size(py_tmp) == 0) {
+               srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY "
+                               "with empty data set.", di->decoder->name);
+               return SRD_ERR_PYTHON;
+       }
+
+       if (!(pdb = g_try_malloc(sizeof(struct srd_proto_data_binary))))
+               return SRD_ERR_MALLOC;
+       if (PyBytes_AsStringAndSize(py_tmp, &buf, &size) == -1)
+               return SRD_ERR_PYTHON;
+       pdb->bin_class = bin_class;
+       pdb->size = size;
+       if (!(pdb->data = g_try_malloc(pdb->size)))
+               return SRD_ERR_MALLOC;
+       memcpy((void *)pdb->data, (const void *)buf, pdb->size);
+       pdata->data = pdb;
+
+       return SRD_OK;
+}
+
 static int convert_meta(struct srd_proto_data *pdata, PyObject *obj)
 {
        long long intvalue;
@@ -173,7 +241,7 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args)
        case SRD_OUTPUT_ANN:
                /* Annotations are only fed to callbacks. */
                if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) {
-                       /* Annotations need converting from PyObject. */
+                       /* Convert from PyDict to srd_proto_data_annotation. */
                        if (convert_annotation(di, py_data, pdata) != SRD_OK) {
                                /* An error was already logged. */
                                break;
@@ -199,7 +267,14 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args)
                }
                break;
        case SRD_OUTPUT_BINARY:
-               srd_err("SRD_OUTPUT_BINARY not yet supported.");
+               if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) {
+                       /* Convert from PyDict to srd_proto_data_binary. */
+                       if (convert_binary(di, py_data, pdata) != SRD_OK) {
+                               /* An error was already logged. */
+                               break;
+                       }
+                       cb->cb(pdata, cb->cb_data);
+               }
                break;
        case SRD_OUTPUT_META:
                if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) {