From: Uwe Hermann Date: Wed, 26 Jun 2019 19:25:37 +0000 (+0200) Subject: Add initial OUTPUT_LOGIC support. X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=ddcde1861eb029e17d2592b2fbf644e2e00f4835;p=libsigrokdecode.git Add initial OUTPUT_LOGIC support. Protocol decoders can now declare an arbitrary number of logic output channels with a fixed assumed samplerate each. --- diff --git a/decoder.c b/decoder.c index fc6ae07..3bbc7f9 100644 --- a/decoder.c +++ b/decoder.c @@ -136,6 +136,18 @@ static void annotation_row_free(void *data) g_free(row); } +static void logic_output_channel_free(void *data) +{ + struct srd_decoder_logic_output_channel *logic_out_ch = data; + + if (!logic_out_ch) + return; + + g_free(logic_out_ch->desc); + g_free(logic_out_ch->id); + g_free(logic_out_ch); +} + static void decoder_option_free(void *data) { struct srd_decoder_option *opt = data; @@ -606,6 +618,92 @@ err_out: return SRD_ERR_PYTHON; } +/* Convert logic_output_channels to GSList of 'struct srd_decoder_logic_output_channel'. */ +static int get_logic_output_channels(struct srd_decoder *dec) +{ + PyObject *py_logic_out_chs, *py_logic_out_ch, *py_samplerate, *py_item; + GSList *logic_out_chs; + struct srd_decoder_logic_output_channel *logic_out_ch; + ssize_t i; + PyGILState_STATE gstate; + + gstate = PyGILState_Ensure(); + + if (!PyObject_HasAttrString(dec->py_dec, "logic_output_channels")) { + PyGILState_Release(gstate); + return SRD_OK; + } + + logic_out_chs = NULL; + + py_logic_out_chs = PyObject_GetAttrString(dec->py_dec, "logic_output_channels"); + if (!py_logic_out_chs) + goto except_out; + + if (!PyTuple_Check(py_logic_out_chs)) { + srd_err("Protocol decoder %s logic_output_channels " + "must be a tuple.", dec->name); + goto err_out; + } + + for (i = PyTuple_Size(py_logic_out_chs) - 1; i >= 0; i--) { + py_logic_out_ch = PyTuple_GetItem(py_logic_out_chs, i); + if (!py_logic_out_ch) + goto except_out; + + if (!PyTuple_Check(py_logic_out_ch) || PyTuple_Size(py_logic_out_ch) != 3) { + srd_err("Protocol decoder %s logic_output_channels " + "must contain only tuples of 3 elements.", + dec->name); + goto err_out; + } + logic_out_ch = g_malloc0(sizeof(*logic_out_ch)); + /* Add to list right away so it doesn't get lost. */ + logic_out_chs = g_slist_prepend(logic_out_chs, logic_out_ch); + + py_item = PyTuple_GetItem(py_logic_out_ch, 0); + if (!py_item) + goto except_out; + if (py_str_as_str(py_item, &logic_out_ch->id) != SRD_OK) + goto err_out; + + py_item = PyTuple_GetItem(py_logic_out_ch, 1); + if (!py_item) + goto except_out; + if (py_str_as_str(py_item, &logic_out_ch->desc) != SRD_OK) + goto err_out; + + py_samplerate = PyTuple_GetItem(py_logic_out_ch, 2); + if (!py_samplerate) + goto except_out; + + if (!PyLong_Check(py_samplerate)) { + srd_err("Protocol decoder %s logic_output_channels tuples " + "must have a number as 3rd element.", + dec->name); + goto err_out; + } + + logic_out_ch->samplerate = PyLong_AsUnsignedLongLong(py_samplerate); + } + dec->logic_output_channels = logic_out_chs; + Py_DECREF(py_logic_out_chs); + PyGILState_Release(gstate); + + return SRD_OK; + +except_out: + srd_exception_catch("Failed to get %s decoder logic output channels", + dec->name); + +err_out: + g_slist_free_full(logic_out_chs, &logic_output_channel_free); + Py_XDECREF(py_logic_out_chs); + PyGILState_Release(gstate); + + return SRD_ERR_PYTHON; +} + /* Check whether the Decoder class defines the named method. */ static int check_method(PyObject *py_dec, const char *mod_name, const char *method_name) @@ -944,6 +1042,11 @@ SRD_API int srd_decoder_load(const char *module_name) goto err_out; } + if (get_logic_output_channels(d) != SRD_OK) { + fail_txt = "cannot get logic output channels"; + goto err_out; + } + PyGILState_Release(gstate); /* Append it to the list of loaded decoders. */ diff --git a/libsigrokdecode.h b/libsigrokdecode.h index b9cc921..4440267 100644 --- a/libsigrokdecode.h +++ b/libsigrokdecode.h @@ -121,14 +121,15 @@ enum srd_loglevel { /* * When adding an output type, don't forget to... - * - expose it to PDs in controller.c:PyInit_sigrokdecode() - * - add a check in module_sigrokdecode.c:Decoder_put() - * - add a debug string in type_decoder.c:OUTPUT_TYPES + * - expose it to PDs in module_sigrokdecode.c:PyInit_sigrokdecode() + * - add a check in type_decoder.c:Decoder_put() + * - add a debug string in type_decoder.c:output_type_name() */ enum srd_output_type { SRD_OUTPUT_ANN, SRD_OUTPUT_PYTHON, SRD_OUTPUT_BINARY, + SRD_OUTPUT_LOGIC, SRD_OUTPUT_META, }; @@ -188,6 +189,11 @@ struct srd_decoder { */ GSList *binary; + /** + * List of logic output channels (item: id, description, samplerate). + */ + GSList *logic_output_channels; + /** List of decoder options. */ GSList *options; @@ -232,6 +238,12 @@ struct srd_decoder_annotation_row { GSList *ann_classes; }; +struct srd_decoder_logic_output_channel { + char *id; + char *desc; + uint64_t samplerate; +}; + struct srd_decoder_inst { struct srd_decoder *decoder; struct srd_session *sess; @@ -314,6 +326,11 @@ struct srd_proto_data_binary { uint64_t size; const unsigned char *data; }; +struct srd_proto_data_logic { + int logic_class; + uint64_t size; + const unsigned char *data; +}; typedef void (*srd_pd_output_callback)(struct srd_proto_data *pdata, void *cb_data); diff --git a/module_sigrokdecode.c b/module_sigrokdecode.c index 39b5b43..1fc0c77 100644 --- a/module_sigrokdecode.c +++ b/module_sigrokdecode.c @@ -63,6 +63,8 @@ PyMODINIT_FUNC PyInit_sigrokdecode(void) goto err_out; if (PyModule_AddIntConstant(mod, "OUTPUT_BINARY", SRD_OUTPUT_BINARY) < 0) goto err_out; + if (PyModule_AddIntConstant(mod, "OUTPUT_LOGIC", SRD_OUTPUT_LOGIC) < 0) + goto err_out; if (PyModule_AddIntConstant(mod, "OUTPUT_META", SRD_OUTPUT_META) < 0) goto err_out; /* Expose meta input symbols. */ diff --git a/type_decoder.c b/type_decoder.c index 1b378f1..ab2e21f 100644 --- a/type_decoder.c +++ b/type_decoder.c @@ -37,6 +37,7 @@ SRD_PRIV const char *output_type_name(unsigned int idx) "OUTPUT_ANN", "OUTPUT_PYTHON", "OUTPUT_BINARY", + "OUTPUT_LOGIC", "OUTPUT_META", "(invalid)" }; @@ -123,6 +124,89 @@ err: return SRD_ERR_PYTHON; } +static void release_logic(struct srd_proto_data_logic *pdl) +{ + if (!pdl) + return; + g_free((void *)pdl->data); +} + +static int convert_logic(struct srd_decoder_inst *di, PyObject *obj, + struct srd_proto_data *pdata) +{ + struct srd_proto_data_logic *pdl; + PyObject *py_tmp; + Py_ssize_t size; + int logic_class; + char *class_name, *buf; + PyGILState_STATE gstate; + + gstate = PyGILState_Ensure(); + + /* Should be a list of [logic class, bytes]. */ + if (!PyList_Check(obj)) { + srd_err("Protocol decoder %s submitted non-list for SRD_OUTPUT_LOGIC.", + di->decoder->name); + goto err; + } + + /* Should have 2 elements. */ + if (PyList_Size(obj) != 2) { + srd_err("Protocol decoder %s submitted SRD_OUTPUT_LOGIC list " + "with %zd elements instead of 2", di->decoder->name, + PyList_Size(obj)); + goto err; + } + + /* The first element should be an integer. */ + py_tmp = PyList_GetItem(obj, 0); + if (!PyLong_Check(py_tmp)) { + srd_err("Protocol decoder %s submitted SRD_OUTPUT_LOGIC list, " + "but first element was not an integer.", di->decoder->name); + goto err; + } + logic_class = PyLong_AsLong(py_tmp); + if (!(class_name = g_slist_nth_data(di->decoder->logic_output_channels, logic_class))) { + srd_err("Protocol decoder %s submitted SRD_OUTPUT_LOGIC with " + "unregistered logic class %d.", di->decoder->name, logic_class); + goto err; + } + + /* Second element should be bytes. */ + py_tmp = PyList_GetItem(obj, 1); + if (!PyBytes_Check(py_tmp)) { + srd_err("Protocol decoder %s submitted SRD_OUTPUT_LOGIC list, " + "but second element was not bytes.", di->decoder->name); + goto err; + } + + /* Consider an empty set of bytes a bug. */ + if (PyBytes_Size(py_tmp) == 0) { + srd_err("Protocol decoder %s submitted SRD_OUTPUT_LOGIC " + "with empty data set.", di->decoder->name); + goto err; + } + + if (PyBytes_AsStringAndSize(py_tmp, &buf, &size) == -1) + goto err; + + PyGILState_Release(gstate); + + pdl = pdata->data; + pdl->logic_class = logic_class; + pdl->size = size; + if (!(pdl->data = g_try_malloc(pdl->size))) + return SRD_ERR_MALLOC; + memcpy((void *)pdl->data, (const void *)buf, pdl->size); + + return SRD_OK; + +err: + PyGILState_Release(gstate); + + return SRD_ERR_PYTHON; +} + static void release_binary(struct srd_proto_data_binary *pdb) { if (!pdb) @@ -421,6 +505,20 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args) release_binary(pdata.data); } break; + case SRD_OUTPUT_LOGIC: + if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) { + pdata.data = &pdb; + /* Convert from PyDict to srd_proto_data_logic. */ + if (convert_logic(di, py_data, &pdata) != SRD_OK) { + /* An error was already logged. */ + break; + } + Py_BEGIN_ALLOW_THREADS + cb->cb(&pdata, cb->cb_data); + Py_END_ALLOW_THREADS + release_logic(pdata.data); + } + break; case SRD_OUTPUT_META: if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) { /* Annotations need converting from PyObject. */