From 159699490ea4bf2495e99dcd5fb18b240d7499df Mon Sep 17 00:00:00 2001 From: Bert Vermeulen Date: Sat, 7 Jan 2012 02:50:14 +0100 Subject: [PATCH 1/1] convert data coming in from a PD to C structs This is in preparation for passing annotation data back to the calling frontend, and python data up to the next protocol in the stack. --- controller.c | 25 +++------ decoder.c | 39 ++++++++++--- decoders/i2c.py | 124 ++++++++++++++++++++++-------------------- module_sigrokdecode.c | 100 ++++++++++++++++++++++++++++++---- sigrokdecode.h | 20 ++++++- util.c | 29 +++++++++- 6 files changed, 237 insertions(+), 100 deletions(-) diff --git a/controller.c b/controller.c index de9b00a..19dc252 100644 --- a/controller.c +++ b/controller.c @@ -292,33 +292,24 @@ int srd_session_feed(uint64_t timeoffset, uint64_t duration, uint8_t *inbuf, int pd_output_new(struct srd_decoder_instance *di, int output_type, - char *protocol_id, char *description) + char *protocol_id) { - GSList *l; struct srd_pd_output *pdo; - int pdo_id; - - fprintf(stdout, "%s: output type %d, protocol_id %s, description %s\n", - __func__, output_type, protocol_id, description); - - pdo_id = -1; - for (l = di->pd_output; l; l = l->next) { - pdo = l->data; - if (pdo->pdo_id > pdo_id) - pdo_id = pdo->pdo_id; - } - pdo_id++; if (!(pdo = g_try_malloc(sizeof(struct srd_pd_output)))) return -1; - pdo->pdo_id = pdo_id; + /* pdo_id is just a simple index, nothing is deleted from this list anway */ + pdo->pdo_id = g_slist_length(di->pd_output); pdo->output_type = output_type; + pdo->decoder = di->decoder; pdo->protocol_id = g_strdup(protocol_id); - pdo->description = g_strdup(description); di->pd_output = g_slist_append(di->pd_output, pdo); - return pdo_id; + fprintf(stdout, "%s: output type %d, protocol_id %s, id %d\n", + __func__, output_type, protocol_id, pdo->pdo_id); + + return pdo->pdo_id; } struct srd_decoder_instance *get_di_by_decobject(void *decobject) diff --git a/decoder.c b/decoder.c index 628e953..b590916 100644 --- a/decoder.c +++ b/decoder.c @@ -20,6 +20,7 @@ #include "config.h" #include "sigrokdecode.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */ +#include "sigrokdecode-internal.h" #include /* The list of protocol decoders. */ @@ -63,17 +64,19 @@ struct srd_decoder *srd_get_decoder_by_id(const char *id) /** - * TODO + * Load a protocol decoder module into the embedded python interpreter. * - * @param name TODO + * @param name The module name to be loaded. + * @param dec Pointer to the struct srd_decoder filled with the loaded module. * * @return SRD_OK upon success, a (negative) error code otherwise. */ int srd_load_decoder(const char *name, struct srd_decoder **dec) { + PyObject *py_mod, *py_res, *py_annlist, *py_ann; struct srd_decoder *d; - PyObject *py_mod, *py_res; - int r; + int alen, r, i; + char **ann; fprintf(stdout, "%s: %s\n", __func__, name); @@ -102,15 +105,13 @@ int srd_load_decoder(const char *name, struct srd_decoder **dec) if ((r = h_str(py_res, "name", &(d->name))) < 0) return r; - if ((r = h_str(py_res, "longname", - &(d->longname))) < 0) + if ((r = h_str(py_res, "longname", &(d->longname))) < 0) return r; if ((r = h_str(py_res, "desc", &(d->desc))) < 0) return r; - if ((r = h_str(py_res, "longdesc", - &(d->longdesc))) < 0) + if ((r = h_str(py_res, "longdesc", &(d->longdesc))) < 0) return r; if ((r = h_str(py_res, "author", &(d->author))) < 0) @@ -131,6 +132,28 @@ int srd_load_decoder(const char *name, struct srd_decoder **dec) d->inputformats = NULL; d->outputformats = NULL; + /* Convert class annotation attribute to GSList of **char */ + d->annotation = NULL; + if ((py_annlist = PyObject_GetAttrString(py_res, "annotation"))) { + if (!PyList_Check(py_annlist)) { + srd_err("Protocol decoder module %s annotation should be a list", name); + return SRD_ERR_PYTHON; + } + alen = PyList_Size(py_annlist); + for (i = 0; i < alen; i++) { + py_ann = PyList_GetItem(py_annlist, i); + if (!PyList_Check(py_ann) || PyList_Size(py_ann) != 2) { + srd_err("Protocol decoder module %s annotation %d should be a list with two elements", + name, i+1); + return SRD_ERR_PYTHON; + } + + if (py_strlist_to_char(py_ann, &ann) != SRD_OK) + return SRD_ERR_PYTHON; + d->annotation = g_slist_append(d->annotation, ann); + } + } + *dec = d; return SRD_OK; diff --git a/decoders/i2c.py b/decoders/i2c.py index 1c4903b..0f554f7 100644 --- a/decoders/i2c.py +++ b/decoders/i2c.py @@ -89,26 +89,9 @@ # 'data': (actual data as integer ???) TODO: This can be very variable... # 'ann': (string; additional annotations / comments) # -# Example output: -# [{'type': 'S', 'range': (150, 160), 'data': None, 'ann': 'Foobar'}, -# {'type': 'AW', 'range': (200, 300), 'data': 0x50, 'ann': 'Slave 4'}, -# {'type': 'DW', 'range': (310, 370), 'data': 0x00, 'ann': 'Init cmd'}, -# {'type': 'AR', 'range': (500, 560), 'data': 0x50, 'ann': 'Get stat'}, -# {'type': 'DR', 'range': (580, 640), 'data': 0xfe, 'ann': 'OK'}, -# {'type': 'P', 'range': (650, 660), 'data': None, 'ann': None}] -# -# Possible other events: -# - Error event in case protocol looks broken: -# [{'type': 'ERROR', 'range': (min, max), -# 'data': TODO, 'ann': 'This is not a Microchip 24XX64 EEPROM'}, -# [{'type': 'ERROR', 'range': (min, max), -# 'data': TODO, 'ann': 'TODO'}, -# - TODO: Make list of possible errors accessible as metadata? -# # TODO: I2C address of slaves. # TODO: Handle multiple different I2C devices on same bus # -> we need to decode multiple protocols at the same time. -# TODO: range: Always contiguous? Splitted ranges? Multiple per event? # # @@ -128,22 +111,31 @@ import sigrokdecode -# symbols for i2c decoders up the stack -START = 1 -START_REPEAT = 2 -STOP = 3 -ACK = 4 -NACK = 5 -ADDRESS_READ = 6 -ADDRESS_WRITE = 7 -DATA_READ = 8 -DATA_WRITE = 9 +# values are verbose and short annotation, respectively +protocol = { + 'START': ['START', 'S'], + 'START_REPEAT': ['START REPEAT', 'Sr'], + 'STOP': ['STOP', 'P'], + 'ACK': ['ACK', 'A'], + 'NACK': ['NACK', 'N'], + 'ADDRESS_READ': ['ADDRESS READ', 'AR'], + 'ADDRESS_WRITE': ['ADDRESS WRITE','AW'], + 'DATA_READ': ['DATA READ', 'DR'], + 'DATA_WRITE': ['DATA WRITE', 'DW'], +} +# export protocol keys as symbols for i2c decoders up the stack +EXPORT = [ protocol.keys() ] # States FIND_START = 0 FIND_ADDRESS = 1 FIND_DATA = 2 +# annotation feed formats +ANN_SHIFTED = 0 +ANN_SHIFTED_SHORT = 1 +ANN_RAW = 2 + class Decoder(sigrokdecode.Decoder): id = 'i2c' @@ -163,6 +155,16 @@ class Decoder(sigrokdecode.Decoder): options = { 'address-space': ['Address space (in bits)', 7], } + annotation = [ + # ANN_SHIFTED + ["7-bit shifted hex", + "Read/Write bit shifted out from the 8-bit i2c slave address"], + # ANN_SHIFTED_SHORT + ["7-bit shifted hex (short)", + "Read/Write bit shifted out from the 8-bit i2c slave address"], + # ANN_RAW + ["Raw hex", "Unaltered raw data"] + ] def __init__(self, **kwargs): self.output_protocol = None @@ -178,8 +180,8 @@ class Decoder(sigrokdecode.Decoder): self.oldsda = None def start(self, metadata): - self.output_protocol = self.output_new(2) - self.output_annotation = self.output_new(1) + self.output_protocol = self.output_new(1) + self.output_annotation = self.output_new(0) def report(self): pass @@ -204,13 +206,12 @@ class Decoder(sigrokdecode.Decoder): def found_start(self, scl, sda): if self.is_repeat_start == 1: - out_proto = [ START_REPEAT ] - out_ann = [ "START REPEAT" ] + cmd = 'START_REPEAT' else: - out_proto = [ START ] - out_ann = [ "START" ] - self.put(self.output_protocol, out_proto) - self.put(self.output_annotation, out_ann) + cmd = 'START' + self.put(self.output_protocol, [ cmd ]) + self.put(self.output_annotation, [ ANN_SHIFTED, [protocol[cmd][0]] ]) + self.put(self.output_annotation, [ ANN_SHIFTED_SHORT, [protocol[cmd][1]] ]) self.state = FIND_ADDRESS self.bitcount = self.databyte = 0 @@ -233,6 +234,10 @@ class Decoder(sigrokdecode.Decoder): if self.bitcount != 9: return [] + # send raw output annotation before we start shifting out + # read/write and ack/nack bits + self.put(self.output_annotation, [ANN_RAW, ["0x%.2x" % self.databyte]]) + # We received 8 address/data bits and the ACK/NACK bit. self.databyte >>= 1 # Shift out unwanted ACK/NACK bit here. @@ -246,33 +251,32 @@ class Decoder(sigrokdecode.Decoder): # TODO: Error? pass - out_proto = [] - out_ann = [] + # last bit that came in was the ACK/NACK bit (1 = NACK) + if sda == 1: + ack_bit = 'NACK' + else: + ack_bit = 'ACK' + # TODO: Simplify. if self.state == FIND_ADDRESS and self.wr == 1: - cmd = ADDRESS_WRITE - ann = 'ADDRESS WRITE' + cmd = 'ADDRESS_WRITE' elif self.state == FIND_ADDRESS and self.wr == 0: - cmd = ADDRESS_READ - ann = 'ADDRESS READ' + cmd = 'ADDRESS_READ' elif self.state == FIND_DATA and self.wr == 1: - cmd = DATA_WRITE - ann = 'DATA WRITE' + cmd = 'DATA_WRITE' elif self.state == FIND_DATA and self.wr == 0: - cmd = DATA_READ - ann = 'DATA READ' - out_proto.append( [cmd, d] ) - out_ann.append( ["%s" % ann, "0x%02x" % d] ) - - if sda == 1: - out_proto.append( [NACK] ) - out_ann.append( ["NACK"] ) - else: - out_proto.append( [ACK] ) - out_ann.append( ["ACK"] ) - - self.put(self.output_protocol, out_proto) - self.put(self.output_annotation, out_ann) + cmd = 'DATA_READ' + self.put(self.output_protocol, [ [cmd, d], [ack_bit] ] ) + self.put(self.output_annotation, [ANN_SHIFTED, [ + "%s" % protocol[cmd][0], + "0x%02x" % d, + "%s" % protocol[ack_bit][0]] + ] ) + self.put(self.output_annotation, [ANN_SHIFTED_SHORT, [ + "%s" % protocol[cmd][1], + "0x%02x" % d, + "%s" % protocol[ack_bit][1]] + ] ) self.bitcount = self.databyte = 0 self.startsample = -1 @@ -285,8 +289,9 @@ class Decoder(sigrokdecode.Decoder): pass def found_stop(self, scl, sda): - self.put(self.output_protocol, [ STOP ]) - self.put(self.output_annotation, [ "STOP" ]) + self.put(self.output_protocol, [ 'STOP' ]) + self.put(self.output_annotation, [ ANN_SHIFTED, [protocol['STOP'][0]] ]) + self.put(self.output_annotation, [ ANN_SHIFTED_SHORT, [protocol['STOP'][1]] ]) self.state = FIND_START self.is_repeat_start = 0 @@ -294,6 +299,7 @@ class Decoder(sigrokdecode.Decoder): def put(self, output_id, data): # inject sample range into the call up to sigrok + # TODO: 0-0 sample range for now super(Decoder, self).put(0, 0, output_id, data) def decode(self, timeoffset, duration, data): diff --git a/module_sigrokdecode.c b/module_sigrokdecode.c index b547e9d..d4af5c3 100644 --- a/module_sigrokdecode.c +++ b/module_sigrokdecode.c @@ -18,12 +18,67 @@ */ #include "sigrokdecode.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */ +#include "sigrokdecode-internal.h" #include "config.h" /* lives in type_logic.c */ extern PyTypeObject srd_logic_type; +static int convert_pyobj(struct srd_decoder_instance *di, PyObject *obj, + int *annotation_format, char ***annotation) +{ + PyObject *py_tmp; + struct srd_pd_output *pdo; + int ann_id; + + /* Should be a list of [annotation format, [string, ...]] */ + if (!PyList_Check(obj) && !PyTuple_Check(obj)) { + srd_err("Protocol decoder %s submitted %s instead of list", + di->decoder->name, obj->ob_type->tp_name); + return SRD_ERR_PYTHON; + } + + /* Should have 2 elements... */ + if (PyList_Size(obj) != 2) { + srd_err("Protocol decoder %s submitted annotation list with %d elements instead of 2", + di->decoder->name, PyList_Size(obj)); + return SRD_ERR_PYTHON; + } + + /* First element should be an integer matching a previously + * registered annotation format. */ + py_tmp = PyList_GetItem(obj, 0); + if (!PyLong_Check(py_tmp)) { + srd_err("Protocol decoder %s submitted annotation list, but first element was not an integer", + di->decoder->name); + return SRD_ERR_PYTHON; + } + + ann_id = PyLong_AsLong(py_tmp); + if (!(pdo = g_slist_nth_data(di->decoder->annotation, ann_id))) { + srd_err("Protocol decoder %s submitted data to non-existent annotation format %d", + di->decoder->name, ann_id); + return SRD_ERR_PYTHON; + } + *annotation_format = ann_id; + + /* Second element must be a list */ + py_tmp = PyList_GetItem(obj, 1); + if (!PyList_Check(py_tmp)) { + srd_err("Protocol decoder %s submitted annotation list, but second element was not a list", + di->decoder->name); + return SRD_ERR_PYTHON; + } + if (py_strlist_to_char(py_tmp, annotation) != SRD_OK) { + srd_err("Protocol decoder %s submitted annotation list, but second element was malformed", + di->decoder->name); + return SRD_ERR_PYTHON; + } + + return SRD_OK; +} + /* TODO: not used, doesn't work actually */ static PyObject *Decoder_init(PyObject *self, PyObject *args) { @@ -41,7 +96,8 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args) struct srd_decoder_instance *di; struct srd_pd_output *pdo; uint64_t timeoffset, duration; - int output_id; + int output_id, annotation_format, i; + char **annotation, **ann_info; if (!(di = get_di_by_decobject(self))) return NULL; @@ -50,18 +106,38 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args) return NULL; if (!(l = g_slist_nth(di->pd_output, output_id))) { - /* PD supplied invalid output id */ - /* TODO: better error message */ + srd_err("Protocol decoder %s submitted invalid output ID %d", + di->decoder->name, output_id); return NULL; } pdo = l->data; - /* TODO: SRD_OUTPUT_ANNOTATION should go back up to the caller, - * and SRD_OUTPUT_PROTOCOL should go up the PD stack. - */ - printf("stream %d: ", pdo->output_type); - PyObject_Print(data, stdout, Py_PRINT_RAW); - puts(""); + switch (pdo->output_type) { + case SRD_OUTPUT_ANNOTATION: + if (convert_pyobj(di, data, &annotation_format, &annotation) != SRD_OK) + return NULL; + + /* TODO: SRD_OUTPUT_ANNOTATION should go back up to the caller */ + ann_info = g_slist_nth_data(pdo->decoder->annotation, annotation_format); + printf("annotation format %d (%s): ", annotation_format, ann_info[0]); + for (i = 0; annotation[i]; i++) + printf("\"%s\" ", annotation[i]); + printf("\n"); + break; + + case SRD_OUTPUT_PROTOCOL: + + /* TODO: SRD_OUTPUT_PROTOCOL should go up the PD stack. */ + printf("%s protocol data: ", pdo->decoder->name); + PyObject_Print(data, stdout, Py_PRINT_RAW); + puts(""); + break; + + default: + srd_err("Protocol decoder %s submitted invalid output type %d", + di->decoder->name, pdo->output_type); + break; + } Py_RETURN_NONE; } @@ -71,7 +147,7 @@ static PyObject *Decoder_output_new(PyObject *self, PyObject *py_output_type) { PyObject *ret; struct srd_decoder_instance *di; - char *protocol_id, *description; + char *protocol_id; int output_type, pdo_id; if (!(di = get_di_by_decobject(self))) @@ -82,9 +158,9 @@ static PyObject *Decoder_output_new(PyObject *self, PyObject *py_output_type) if (!PyArg_ParseTuple(py_output_type, "i:output_type", &output_type)) return NULL; + /* TODO: take protocol_id from python */ protocol_id = "i2c"; - description = "blah"; - pdo_id = pd_output_new(di, output_type, protocol_id, description); + pdo_id = pd_output_new(di, output_type, protocol_id); if (pdo_id < 0) Py_RETURN_NONE; else diff --git a/sigrokdecode.h b/sigrokdecode.h index 8c2ccd2..d58aea2 100644 --- a/sigrokdecode.h +++ b/sigrokdecode.h @@ -63,9 +63,9 @@ extern "C" { #define SRD_LOG_SPEW 5 /**< Output very noisy debug messages. */ enum { - SRD_OUTPUT_LOGIC = 1, SRD_OUTPUT_ANNOTATION, SRD_OUTPUT_PROTOCOL, + SRD_OUTPUT_BINARY, }; #define SRD_MAX_NUM_PROBES 64 @@ -105,6 +105,11 @@ struct srd_decoder { /** TODO */ GSList *outputformats; + /* List of NULL-terminated char[], containing descriptions of the + * supported annotation output. + */ + GSList *annotation; + /** TODO */ PyObject *py_mod; @@ -124,8 +129,8 @@ struct srd_decoder_instance { struct srd_pd_output { int pdo_id; int output_type; + struct srd_decoder *decoder; char *protocol_id; - char *description; }; typedef struct { @@ -137,6 +142,14 @@ typedef struct { PyObject *sample; } srd_logic; +struct srd_protocol_data { + uint64_t start_sample; + uint64_t end_sample; + struct srd_pd_output *pdo; + int annotation_type; + unsigned char **data; +}; + /*--- controller.c ----------------------------------------------------------*/ @@ -153,7 +166,7 @@ int srd_run_decoder(uint64_t timeoffset, uint64_t duration, int srd_session_feed(uint64_t timeoffset, uint64_t duration, uint8_t *inbuf, uint64_t inbuflen); int pd_output_new(struct srd_decoder_instance *di, int output_type, - char *output_id, char *description); + char *output_id); struct srd_decoder_instance *get_di_by_decobject(void *decobject); /*--- decoder.c -------------------------------------------------------------*/ @@ -167,6 +180,7 @@ int srd_unload_all_decoders(void); /*--- util.c ----------------------------------------------------------------*/ int h_str(PyObject *py_res, const char *key, char **outstr); +int py_strlist_to_char(PyObject *py_strlist, char ***outstr); /*--- log.c -----------------------------------------------------------------*/ diff --git a/util.c b/util.c index 6164aaa..df433d7 100644 --- a/util.c +++ b/util.c @@ -18,8 +18,8 @@ * along with this program. If not, see . */ -#include "config.h" #include "sigrokdecode.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */ +#include "config.h" /** @@ -73,8 +73,35 @@ err_out: Py_XDECREF(py_encstr); if (PyErr_Occurred()) + /* TODO: log level 4 debug message */ PyErr_Print(); return ret; } +/** + * Convert a python list of unicode strings to a NULL-terminated UTF8-encoded + * char * array. The caller must free each string when finished. + */ +int py_strlist_to_char(PyObject *py_strlist, char ***outstr) +{ + PyObject *py_str; + int list_len, i; + char **out, *str; + + list_len = PyList_Size(py_strlist); + if (!(out = g_try_malloc(sizeof(char *) * (list_len + 1)))) + return SRD_ERR_MALLOC; + for (i = 0; i < list_len; i++) { + if (!(py_str = PyUnicode_AsEncodedString(PyList_GetItem(py_strlist, i), "utf-8", NULL))) + return SRD_ERR_PYTHON; + if (!(str = PyBytes_AS_STRING(py_str))) + return SRD_ERR_PYTHON; + out[i] = g_strdup(str); + } + out[i] = NULL; + *outstr = out; + + return SRD_OK; +} + -- 2.30.2