]> sigrok.org Git - libsigrokdecode.git/commitdiff
Backport recent changes from mainline.
authorUwe Hermann <redacted>
Mon, 12 Jun 2017 01:17:28 +0000 (03:17 +0200)
committerUwe Hermann <redacted>
Thu, 12 Apr 2018 18:15:59 +0000 (20:15 +0200)
This includes all changes from

  23e806c21e6e01999163c892635c6dea9d788daa
  "session.c: Fix a compiler warning on Mac OS X."

up to

  cffb6592f4cff804745b8456e2c9f8abc6571603
  "can: Fix incorrect stuff bit handling."

This is possible since none of the changes above change or remove public
API calls of the library.

111 files changed:
HACKING
Makefile.am
README
configure.ac
decoder.c
decoders/ac97/__init__.py [new file with mode: 0644]
decoders/ac97/pd.py [new file with mode: 0644]
decoders/ade77xx/pd.py
decoders/adf435x/pd.py
decoders/adns5020/pd.py
decoders/am230x/pd.py
decoders/arm_etmv3/pd.py
decoders/arm_itm/pd.py
decoders/arm_tpiu/pd.py
decoders/aud/pd.py
decoders/avr_isp/pd.py
decoders/avr_pdi/pd.py
decoders/can/pd.py
decoders/common/srdhelper/mod.py
decoders/counter/__init__.py [new file with mode: 0644]
decoders/counter/pd.py [new file with mode: 0644]
decoders/dali/lists.py
decoders/dali/pd.py
decoders/dcf77/pd.py
decoders/dmx512/pd.py
decoders/ds1307/pd.py
decoders/ds243x/__init__.py [new file with mode: 0644]
decoders/ds243x/pd.py [new file with mode: 0644]
decoders/ds28ea00/pd.py
decoders/dsi/pd.py
decoders/edid/pd.py
decoders/eeprom24xx/pd.py
decoders/eeprom93xx/pd.py
decoders/em4100/pd.py
decoders/em4305/pd.py
decoders/gpib/pd.py
decoders/graycode/__init__.py [new file with mode: 0644]
decoders/graycode/pd.py [new file with mode: 0644]
decoders/guess_bitrate/pd.py
decoders/i2c/pd.py
decoders/i2cdemux/pd.py
decoders/i2cfilter/pd.py
decoders/i2s/pd.py
decoders/iec/pd.py
decoders/ir_nec/pd.py
decoders/ir_rc5/pd.py
decoders/jitter/pd.py
decoders/jtag/pd.py
decoders/jtag_stm32/pd.py
decoders/lm75/pd.py
decoders/lpc/pd.py
decoders/maple_bus/__init__.py [new file with mode: 0644]
decoders/maple_bus/pd.py [new file with mode: 0644]
decoders/max7219/pd.py
decoders/mdio/pd.py
decoders/microwire/pd.py
decoders/midi/pd.py
decoders/mlx90614/pd.py
decoders/modbus/pd.py
decoders/morse/__init__.py [new file with mode: 0644]
decoders/morse/pd.py [new file with mode: 0644]
decoders/mrf24j40/pd.py
decoders/mxc6225xu/pd.py
decoders/nrf24l01/pd.py
decoders/nunchuk/pd.py
decoders/onewire_link/pd.py
decoders/onewire_network/pd.py
decoders/pan1321/pd.py
decoders/parallel/pd.py
decoders/ps2/pd.py
decoders/pwm/pd.py
decoders/qi/pd.py
decoders/rc_encode/__init__.py [new file with mode: 0644]
decoders/rc_encode/pd.py [new file with mode: 0644]
decoders/rfm12/pd.py
decoders/rgb_led_spi/pd.py
decoders/rgb_led_ws281x/pd.py
decoders/rtc8564/pd.py
decoders/sda2506/__init__.py [new file with mode: 0644]
decoders/sda2506/pd.py [new file with mode: 0644]
decoders/sdcard_sd/pd.py
decoders/sdcard_spi/pd.py
decoders/spdif/pd.py
decoders/spi/pd.py
decoders/spiflash/pd.py
decoders/ssi32/pd.py
decoders/stepper_motor/pd.py
decoders/swd/pd.py
decoders/t55xx/pd.py
decoders/tca6408a/pd.py
decoders/timing/pd.py
decoders/tlc5620/pd.py
decoders/uart/pd.py
decoders/usb_packet/pd.py
decoders/usb_power_delivery/pd.py
decoders/usb_request/pd.py
decoders/usb_signalling/pd.py
decoders/wiegand/pd.py
decoders/xfp/pd.py
decoders/z80/pd.py
exception.c
instance.c
libsigrokdecode-internal.h
libsigrokdecode.h
module_sigrokdecode.c
session.c
srd.c
tests/session.c
type_decoder.c
type_logic.c [deleted file]
util.c

diff --git a/HACKING b/HACKING
index a6fe7d3f7f35bc1111fb19fcc1d719b6e450966c..30cd1fe5425db6da688743adec6f5830610a8468 100644 (file)
--- a/HACKING
+++ b/HACKING
@@ -21,15 +21,16 @@ the Python PEP-8, which includes the convention of 4 spaces for indentation:
 Contributions
 -------------
 
- - Patches should be sent to the development mailinglist at
+ - In order to contribute you should ideally clone the git repository and
+   let us know (preferably via IRC, or via the mailing list) from where to
+   pull/review your changes. You can use github.com, or any other public git
+   hosting site.
+
+ - Alternatively, patches can be sent to the development mailinglist at
    sigrok-devel@lists.sourceforge.net (please subscribe to the list first).
 
    https://lists.sourceforge.net/lists/listinfo/sigrok-devel
 
- - Alternatively, you can also clone the git repository and let us know
-   from where to pull/review your changes. You can use gitorious.org,
-   github.com, or any other public git hosting site.
-
 
 Random notes
 ------------
index 675b46016e5658b3033b3927f259c02ee6fb2c5b..97e5228517357d15a959015d11cb27ff9f98c1b0 100644 (file)
@@ -44,7 +44,6 @@ libsigrokdecode_la_SOURCES = \
        exception.c \
        module_sigrokdecode.c \
        type_decoder.c \
-       type_logic.c \
        error.c \
        version.c
 
diff --git a/README b/README
index 686efeff3783ae473dbc8c7167daad32dbb79342..784be277fc6f28e488873473cc09a563f87b6153 100644 (file)
--- a/README
+++ b/README
@@ -34,7 +34,7 @@ Requirements
  - automake >= 1.11 (only needed when building from git)
  - libtool (only needed when building from git)
  - pkg-config >= 0.22
- - libglib >= 2.28.0
+ - libglib >= 2.34
  - Python >= 3.2
  - check >= 0.9.4 (optional, only needed to run unit tests)
  - doxygen (optional, only needed for the C API docs)
index 16aab585e7c591f9b06af816de62e36856048c09..894ce2ab48cde1e12ab78283f494b0efec054040 100644 (file)
@@ -67,6 +67,10 @@ SR_LIB_VERSION_SET([SRD_LIB_VERSION], [4:0:0])
 
 AM_CONDITIONAL([WIN32], [test -z "${host_os##mingw*}" || test -z "${host_os##cygwin*}"])
 
+# Initialize pkg-config.
+# We require at least 0.22, as "Requires.private" behaviour changed there.
+PKG_PROG_PKG_CONFIG
+
 ############################
 ##  Package dependencies  ##
 ############################
@@ -123,6 +127,8 @@ SR_SEARCH_LIBS([SRD_EXTRA_LIBS], [pow], [m])
 
 AC_SYS_LARGEFILE
 
+AC_C_BIGENDIAN
+
 ##############################
 ##  Finalize configuration  ##
 ##############################
@@ -131,7 +137,7 @@ AC_SUBST([SRD_PKGLIBS])
 
 # Retrieve the compile and link flags for all modules combined.
 # Also, bail out at this point if any module dependency is not met.
-PKG_CHECK_MODULES([LIBSIGROKDECODE], [glib-2.0 >= 2.28.0 $SRD_PKGLIBS])
+PKG_CHECK_MODULES([LIBSIGROKDECODE], [glib-2.0 >= 2.34 $SRD_PKGLIBS])
 PKG_CHECK_MODULES([TESTS], [$SRD_PKGLIBS_TESTS glib-2.0 $SRD_PKGLIBS])
 
 srd_glib_version=`$PKG_CONFIG --modversion glib-2.0 2>&AS_MESSAGE_LOG_FD`
@@ -162,7 +168,7 @@ Compile configuration:
  - Linker flags.................... $LDFLAGS
 
 Detected libraries (required):
- - glib-2.0 >= 2.28.0.............. $srd_glib_version
+ - glib-2.0 >= 2.34................ $srd_glib_version
 $srd_pkglibs_summary
 Detected libraries (optional):
 $srd_pkglibs_opt_summary
index 64292bc7077452abb45caba3de6b728546824f44..6689bd488b5b07552b6f819f27a37f2642d8f1a4 100644 (file)
--- a/decoder.c
+++ b/decoder.c
@@ -152,11 +152,15 @@ static void decoder_option_free(void *data)
 
 static void decoder_free(struct srd_decoder *dec)
 {
+       PyGILState_STATE gstate;
+
        if (!dec)
                return;
 
+       gstate = PyGILState_Ensure();
        Py_XDECREF(dec->py_dec);
        Py_XDECREF(dec->py_mod);
+       PyGILState_Release(gstate);
 
        g_slist_free_full(dec->options, &decoder_option_free);
        g_slist_free_full(dec->binary, (GDestroyNotify)&g_strfreev);
@@ -183,10 +187,15 @@ static int get_channels(const struct srd_decoder *d, const char *attr,
        struct srd_channel *pdch;
        GSList *pdchl;
        ssize_t i;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
 
-       if (!PyObject_HasAttrString(d->py_dec, attr))
+       if (!PyObject_HasAttrString(d->py_dec, attr)) {
                /* No channels of this type specified. */
+               PyGILState_Release(gstate);
                return SRD_OK;
+       }
 
        pdchl = NULL;
 
@@ -227,6 +236,8 @@ static int get_channels(const struct srd_decoder *d, const char *attr,
        Py_DECREF(py_channellist);
        *out_pdchl = pdchl;
 
+       PyGILState_Release(gstate);
+
        return SRD_OK;
 
 except_out:
@@ -235,6 +246,7 @@ except_out:
 err_out:
        g_slist_free_full(pdchl, &channel_free);
        Py_XDECREF(py_channellist);
+       PyGILState_Release(gstate);
 
        return SRD_ERR_PYTHON;
 }
@@ -246,10 +258,15 @@ static int get_options(struct srd_decoder *d)
        struct srd_decoder_option *o;
        GVariant *gvar;
        ssize_t opt, i;
+       PyGILState_STATE gstate;
 
-       if (!PyObject_HasAttrString(d->py_dec, "options"))
+       gstate = PyGILState_Ensure();
+
+       if (!PyObject_HasAttrString(d->py_dec, "options")) {
                /* No options, that's fine. */
+               PyGILState_Release(gstate);
                return SRD_OK;
+       }
 
        options = NULL;
 
@@ -281,7 +298,7 @@ static int get_options(struct srd_decoder *d)
 
                py_str = PyDict_GetItemString(py_opt, "id");
                if (!py_str) {
-                       srd_err("Protocol decoder %s option %zd has no id.",
+                       srd_err("Protocol decoder %s option %zd has no ID.",
                                d->name, opt);
                        goto err_out;
                }
@@ -342,6 +359,7 @@ static int get_options(struct srd_decoder *d)
        }
        d->options = options;
        Py_DECREF(py_opts);
+       PyGILState_Release(gstate);
 
        return SRD_OK;
 
@@ -350,6 +368,7 @@ except_out:
 err_out:
        g_slist_free_full(options, &decoder_option_free);
        Py_XDECREF(py_opts);
+       PyGILState_Release(gstate);
 
        return SRD_ERR_PYTHON;
 }
@@ -362,9 +381,14 @@ static int get_annotations(struct srd_decoder *dec)
        GSList *annotations;
        char **annpair;
        ssize_t i;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
 
-       if (!PyObject_HasAttrString(dec->py_dec, "annotations"))
+       if (!PyObject_HasAttrString(dec->py_dec, "annotations")) {
+               PyGILState_Release(gstate);
                return SRD_OK;
+       }
 
        annotations = NULL;
 
@@ -396,6 +420,7 @@ static int get_annotations(struct srd_decoder *dec)
        }
        dec->annotations = annotations;
        Py_DECREF(py_annlist);
+       PyGILState_Release(gstate);
 
        return SRD_OK;
 
@@ -404,6 +429,7 @@ except_out:
 err_out:
        g_slist_free_full(annotations, (GDestroyNotify)&g_strfreev);
        Py_XDECREF(py_annlist);
+       PyGILState_Release(gstate);
 
        return SRD_ERR_PYTHON;
 }
@@ -417,9 +443,14 @@ static int get_annotation_rows(struct srd_decoder *dec)
        struct srd_decoder_annotation_row *ann_row;
        ssize_t i, k;
        size_t class_idx;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
 
-       if (!PyObject_HasAttrString(dec->py_dec, "annotation_rows"))
+       if (!PyObject_HasAttrString(dec->py_dec, "annotation_rows")) {
+               PyGILState_Release(gstate);
                return SRD_OK;
+       }
 
        annotation_rows = NULL;
 
@@ -492,6 +523,7 @@ static int get_annotation_rows(struct srd_decoder *dec)
        }
        dec->annotation_rows = annotation_rows;
        Py_DECREF(py_ann_rows);
+       PyGILState_Release(gstate);
 
        return SRD_OK;
 
@@ -501,6 +533,7 @@ except_out:
 err_out:
        g_slist_free_full(annotation_rows, &annotation_row_free);
        Py_XDECREF(py_ann_rows);
+       PyGILState_Release(gstate);
 
        return SRD_ERR_PYTHON;
 }
@@ -513,9 +546,14 @@ static int get_binary_classes(struct srd_decoder *dec)
        GSList *bin_classes;
        char **bin;
        ssize_t i;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
 
-       if (!PyObject_HasAttrString(dec->py_dec, "binary"))
+       if (!PyObject_HasAttrString(dec->py_dec, "binary")) {
+               PyGILState_Release(gstate);
                return SRD_OK;
+       }
 
        bin_classes = NULL;
 
@@ -548,6 +586,7 @@ static int get_binary_classes(struct srd_decoder *dec)
        }
        dec->binary = bin_classes;
        Py_DECREF(py_bin_classes);
+       PyGILState_Release(gstate);
 
        return SRD_OK;
 
@@ -557,6 +596,7 @@ except_out:
 err_out:
        g_slist_free_full(bin_classes, (GDestroyNotify)&g_strfreev);
        Py_XDECREF(py_bin_classes);
+       PyGILState_Release(gstate);
 
        return SRD_ERR_PYTHON;
 }
@@ -568,17 +608,23 @@ static int check_method(PyObject *py_dec, const char *mod_name,
 {
        PyObject *py_method;
        int is_callable;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
 
        py_method = PyObject_GetAttrString(py_dec, method_name);
        if (!py_method) {
                srd_exception_catch("Protocol decoder %s Decoder class "
                                "has no %s() method", mod_name, method_name);
+               PyGILState_Release(gstate);
                return SRD_ERR_PYTHON;
        }
 
        is_callable = PyCallable_Check(py_method);
        Py_DECREF(py_method);
 
+       PyGILState_Release(gstate);
+
        if (!is_callable) {
                srd_err("Protocol decoder %s Decoder class attribute '%s' "
                        "is not a method.", mod_name, method_name);
@@ -601,15 +647,20 @@ SRD_PRIV long srd_decoder_apiver(const struct srd_decoder *d)
 {
        PyObject *py_apiver;
        long apiver;
+       PyGILState_STATE gstate;
 
        if (!d)
                return 0;
 
+       gstate = PyGILState_Ensure();
+
        py_apiver = PyObject_GetAttrString(d->py_dec, "api_version");
        apiver = (py_apiver && PyLong_Check(py_apiver))
                        ? PyLong_AsLong(py_apiver) : 0;
        Py_XDECREF(py_apiver);
 
+       PyGILState_Release(gstate);
+
        return apiver;
 }
 
@@ -629,6 +680,7 @@ SRD_API int srd_decoder_load(const char *module_name)
        long apiver;
        int is_subclass;
        const char *fail_txt;
+       PyGILState_STATE gstate;
 
        if (!srd_check_init())
                return SRD_ERR;
@@ -636,13 +688,14 @@ SRD_API int srd_decoder_load(const char *module_name)
        if (!module_name)
                return SRD_ERR_ARG;
 
+       gstate = PyGILState_Ensure();
+
        if (PyDict_GetItemString(PyImport_GetModuleDict(), module_name)) {
                /* Module was already imported. */
+               PyGILState_Release(gstate);
                return SRD_OK;
        }
 
-       srd_dbg("Loading protocol decoder '%s'.", module_name);
-
        d = g_malloc0(sizeof(struct srd_decoder));
        fail_txt = NULL;
 
@@ -686,8 +739,8 @@ SRD_API int srd_decoder_load(const char *module_name)
         * PDs of different API versions are incompatible and cannot work.
         */
        apiver = srd_decoder_apiver(d);
-       if (apiver != 2 && apiver != 3) {
-               srd_exception_catch("Only PD API version 2/3 is supported, "
+       if (apiver != 3) {
+               srd_exception_catch("Only PD API version 3 is supported, "
                        "decoder %s has version %ld", module_name, apiver);
                fail_txt = "API version mismatch";
                goto err_out;
@@ -775,6 +828,8 @@ SRD_API int srd_decoder_load(const char *module_name)
                goto err_out;
        }
 
+       PyGILState_Release(gstate);
+
        /* Append it to the list of loaded decoders. */
        pd_list = g_slist_append(pd_list, d);
 
@@ -791,6 +846,7 @@ err_out:
        if (fail_txt)
                srd_err("Failed to load decoder %s: %s", module_name, fail_txt);
        decoder_free(d);
+       PyGILState_Release(gstate);
 
        return SRD_ERR_PYTHON;
 }
@@ -809,6 +865,7 @@ SRD_API char *srd_decoder_doc_get(const struct srd_decoder *dec)
 {
        PyObject *py_str;
        char *doc;
+       PyGILState_STATE gstate;
 
        if (!srd_check_init())
                return NULL;
@@ -816,12 +873,14 @@ SRD_API char *srd_decoder_doc_get(const struct srd_decoder *dec)
        if (!dec)
                return NULL;
 
+       gstate = PyGILState_Ensure();
+
        if (!PyObject_HasAttrString(dec->py_mod, "__doc__"))
-               return NULL;
+               goto err;
 
        if (!(py_str = PyObject_GetAttrString(dec->py_mod, "__doc__"))) {
                srd_exception_catch("Failed to get docstring");
-               return NULL;
+               goto err;
        }
 
        doc = NULL;
@@ -829,7 +888,14 @@ SRD_API char *srd_decoder_doc_get(const struct srd_decoder *dec)
                py_str_as_str(py_str, &doc);
        Py_DECREF(py_str);
 
+       PyGILState_Release(gstate);
+
        return doc;
+
+err:
+       PyGILState_Release(gstate);
+
+       return NULL;
 }
 
 /**
@@ -852,8 +918,6 @@ SRD_API int srd_decoder_unload(struct srd_decoder *dec)
        if (!dec)
                return SRD_ERR_ARG;
 
-       srd_dbg("Unloading protocol decoder '%s'.", dec->name);
-
        /*
         * Since any instances of this decoder need to be released as well,
         * but they could be anywhere in the stack, just free the entire
@@ -880,9 +944,12 @@ static void srd_decoder_load_all_zip_path(char *path)
        Py_ssize_t pos = 0;
        char *prefix;
        size_t prefix_len;
+       PyGILState_STATE gstate;
 
        set = files = prefix_obj = zipimporter = zipimporter_class = NULL;
 
+       gstate = PyGILState_Ensure();
+
        zipimport_mod = py_import_by_name("zipimport");
        if (zipimport_mod == NULL)
                goto err_out;
@@ -951,6 +1018,7 @@ err_out:
        Py_XDECREF(zipimporter_class);
        Py_XDECREF(zipimport_mod);
        PyErr_Clear();
+       PyGILState_Release(gstate);
 }
 
 static void srd_decoder_load_all_path(char *path)
diff --git a/decoders/ac97/__init__.py b/decoders/ac97/__init__.py
new file mode 100644 (file)
index 0000000..8b96e8a
--- /dev/null
@@ -0,0 +1,36 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+AC'97 (Audio Codec '97) was specifically designed by Intel for audio and
+modem I/O functionality in mainstream PC systems. See the specification in
+http://download.intel.com/support/motherboards/desktop/sb/ac97_r23.pdf
+
+AC'97 communicates full duplex data (SDATA_IN, SDATA_OUT), where bits
+are clocked by the BIT_CLK and frames are signalled by the SYNC signals.
+A low active RESET# line completes the set of signals.
+
+Frames repeat at a nominal frequency of 48kHz, and consist of 256 bits
+each. One 16bit slot contains management information, twelve 20bit slots
+follow which carry data for three management and nine audio/modem channels.
+Optionally two slots of one frame can get combined for higher resolution
+on fewer channels, or double data rate.
+'''
+
+from .pd import Decoder
diff --git a/decoders/ac97/pd.py b/decoders/ac97/pd.py
new file mode 100644 (file)
index 0000000..6cb7e93
--- /dev/null
@@ -0,0 +1,503 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+# This implementation is incomplete. TODO items:
+# - Support the optional RESET# pin, detect cold and warm reset.
+# - Split slot values into audio samples of their respective width and
+#   frequency (either on user provided parameters, or from inspection of
+#   decoded register access).
+
+import sigrokdecode as srd
+
+class ChannelError(Exception):
+    pass
+
+class Pins:
+    (SYNC, BIT_CLK, SDATA_OUT, SDATA_IN, RESET) = range(5)
+
+class Ann:
+    (
+        BITS_OUT, BITS_IN,
+        SLOT_OUT_RAW, SLOT_OUT_TAG, SLOT_OUT_ADDR, SLOT_OUT_DATA,
+        SLOT_OUT_03, SLOT_OUT_04, SLOT_OUT_05, SLOT_OUT_06,
+        SLOT_OUT_07, SLOT_OUT_08, SLOT_OUT_09, SLOT_OUT_10,
+        SLOT_OUT_11, SLOT_OUT_IO,
+        SLOT_IN_RAW, SLOT_IN_TAG, SLOT_IN_ADDR, SLOT_IN_DATA,
+        SLOT_IN_03, SLOT_IN_04, SLOT_IN_05, SLOT_IN_06,
+        SLOT_IN_07, SLOT_IN_08, SLOT_IN_09, SLOT_IN_10,
+        SLOT_IN_11, SLOT_IN_IO,
+        WARN, ERROR,
+    ) = range(32)
+    (
+        BIN_FRAME_OUT,
+        BIN_FRAME_IN,
+        BIN_SLOT_RAW_OUT,
+        BIN_SLOT_RAW_IN,
+    ) = range(4)
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'ac97'
+    name = "AC '97"
+    longname = "Audio Codec '97"
+    desc = 'Audio and modem control for PC systems.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['ac97']
+    channels = (
+        {'id': 'sync', 'name': 'SYNC', 'desc': 'Frame synchronization'},
+        {'id': 'clk', 'name': 'BIT_CLK', 'desc': 'Data bits clock'},
+    )
+    optional_channels = (
+        {'id': 'out', 'name': 'SDATA_OUT', 'desc': 'Data output'},
+        {'id': 'in', 'name': 'SDATA_IN', 'desc': 'Data input'},
+        {'id': 'rst', 'name': 'RESET#', 'desc': 'Reset line'},
+    )
+    annotations = (
+        ('bit-out', 'Output bits'),
+        ('bit-in', 'Input bits'),
+        ('slot-out-raw', 'Output raw value'),
+        ('slot-out-tag', 'Output TAG'),
+        ('slot-out-cmd-addr', 'Output command address'),
+        ('slot-out-cmd-data', 'Output command data'),
+        ('slot-out-03', 'Output slot 3'),
+        ('slot-out-04', 'Output slot 4'),
+        ('slot-out-05', 'Output slot 5'),
+        ('slot-out-06', 'Output slot 6'),
+        ('slot-out-07', 'Output slot 7'),
+        ('slot-out-08', 'Output slot 8'),
+        ('slot-out-09', 'Output slot 9'),
+        ('slot-out-10', 'Output slot 10'),
+        ('slot-out-11', 'Output slot 11'),
+        ('slot-out-io-ctrl', 'Output I/O control'),
+        ('slot-in-raw', 'Input raw value'),
+        ('slot-in-tag', 'Input TAG'),
+        ('slot-in-sts-addr', 'Input status address'),
+        ('slot-in-sts-data', 'Input status data'),
+        ('slot-in-03', 'Input slot 3'),
+        ('slot-in-04', 'Input slot 4'),
+        ('slot-in-05', 'Input slot 5'),
+        ('slot-in-06', 'Input slot 6'),
+        ('slot-in-07', 'Input slot 7'),
+        ('slot-in-08', 'Input slot 8'),
+        ('slot-in-09', 'Input slot 9'),
+        ('slot-in-10', 'Input slot 10'),
+        ('slot-in-11', 'Input slot 11'),
+        ('slot-in-io-sts', 'Input I/O status'),
+        # TODO: Add more annotation classes:
+        # TAG: 'ready', 'valid', 'id', 'rsv'
+        # CMD ADDR: 'r/w', 'addr', 'unused'
+        # CMD DATA: 'data', 'unused'
+        # 3-11: 'data', 'unused', 'double data'
+        ('warning', 'Warning'),
+        ('error', 'Error'),
+    )
+    annotation_rows = (
+        ('bits-out', 'Output bits', (Ann.BITS_OUT,)),
+        ('slots-out-raw', 'Output numbers', (Ann.SLOT_OUT_RAW,)),
+        ('slots-out', 'Output slots', (
+            Ann.SLOT_OUT_TAG, Ann.SLOT_OUT_ADDR, Ann.SLOT_OUT_DATA,
+            Ann.SLOT_OUT_03, Ann.SLOT_OUT_04, Ann.SLOT_OUT_05, Ann.SLOT_OUT_06,
+            Ann.SLOT_OUT_07, Ann.SLOT_OUT_08, Ann.SLOT_OUT_09, Ann.SLOT_OUT_10,
+            Ann.SLOT_OUT_11, Ann.SLOT_OUT_IO,)),
+        ('bits-in', 'Input bits', (Ann.BITS_IN,)),
+        ('slots-in-raw', 'Input numbers', (Ann.SLOT_IN_RAW,)),
+        ('slots-in', 'Input slots', (
+            Ann.SLOT_IN_TAG, Ann.SLOT_IN_ADDR, Ann.SLOT_IN_DATA,
+            Ann.SLOT_IN_03, Ann.SLOT_IN_04, Ann.SLOT_IN_05, Ann.SLOT_IN_06,
+            Ann.SLOT_IN_07, Ann.SLOT_IN_08, Ann.SLOT_IN_09, Ann.SLOT_IN_10,
+            Ann.SLOT_IN_11, Ann.SLOT_IN_IO,)),
+        ('warnings', 'Warnings', (Ann.WARN,)),
+        ('errors', 'Errors', (Ann.ERROR,)),
+    )
+    binary = (
+        ('frame-out', 'Frame bits, output data'),
+        ('frame-in', 'Frame bits, input data'),
+        ('slot-raw-out', 'Raw slot bits, output data'),
+        ('slot-raw-in', 'Raw slot bits, input data'),
+        # TODO: Which (other) binary classes to implement?
+        # - Are binary annotations per audio slot useful?
+        # - Assume 20bit per slot, in 24bit units? Or assume 16bit
+        #   audio samples? Observe register access and derive width
+        #   of the audio data? Dump channels 3-11 or 1-12?
+    )
+
+    def putx(self, ss, es, cls, data):
+        self.put(ss, es, self.out_ann, [cls, data])
+
+    def putf(self, frombit, bitcount, cls, data):
+        ss = self.frame_ss_list[frombit]
+        es = self.frame_ss_list[frombit + bitcount]
+        self.putx(ss, es, cls, data)
+
+    def putb(self, frombit, bitcount, cls, data):
+        ss = self.frame_ss_list[frombit]
+        es = self.frame_ss_list[frombit + bitcount]
+        self.put(ss, es, self.out_binary, [cls, data])
+
+    def __init__(self):
+        self.out_binary = None
+        self.out_ann = None
+        self.reset()
+
+    def reset(self):
+        self.frame_ss_list = None
+        self.frame_slot_lens = [0, 16] + [16 + 20 * i for i in range(1, 13)]
+        self.frame_total_bits = self.frame_slot_lens[-1]
+        self.handle_slots = {
+            0: self.handle_slot_00,
+            1: self.handle_slot_01,
+            2: self.handle_slot_02,
+        }
+
+    def start(self):
+        if not self.out_binary:
+            self.out_binary = self.register(srd.OUTPUT_BINARY)
+        if not self.out_ann:
+            self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+
+    def bits_to_int(self, bits):
+        # Convert MSB-first bit sequence to integer value.
+        if not bits:
+            return 0
+        count = len(bits)
+        value = sum([2 ** (count - 1 - i) for i in range(count) if bits[i]])
+        return value
+
+    def bits_to_bin_ann(self, bits):
+        # Convert MSB-first bit sequence to binary annotation data.
+        # It's assumed that the number of bits does not (in useful ways)
+        # fit into an integer, and we need to create an array of bytes
+        # from the data afterwards, anyway. Hence the separate routine
+        # and the conversion of eight bits each.
+        out = []
+        count = len(bits)
+        while count > 0:
+            count -= 8
+            by, bits = bits[:8], bits[8:]
+            by = self.bits_to_int(by)
+            out.append(by)
+        out = bytes(out)
+        return out
+
+    def int_to_nibble_text(self, value, bitcount):
+        # Convert number to hex digits for given bit count.
+        digits = (bitcount + 3) // 4
+        text = '{{:0{:d}x}}'.format(digits).format(value)
+        return text
+
+    def get_bit_field(self, data, size, off, count):
+        shift = size - off - count
+        data >>= shift
+        mask = (1 << count) - 1
+        data &= mask
+        return data
+
+    def flush_frame_bits(self):
+        # Flush raw frame bits to binary annotation.
+        anncls = Ann.BIN_FRAME_OUT
+        data = self.frame_bits_out[:]
+        count = len(data)
+        data = self.bits_to_bin_ann(data)
+        self.putb(0, count, anncls, data)
+
+        anncls = Ann.BIN_FRAME_IN
+        data = self.frame_bits_in[:]
+        count = len(data)
+        data = self.bits_to_bin_ann(data)
+        self.putb(0, count, anncls, data)
+
+    def start_frame(self, ss):
+        # Mark the start of a frame.
+        if self.frame_ss_list:
+            # Flush bits if we had a frame before the frame which is
+            # starting here.
+            self.flush_frame_bits()
+        self.frame_ss_list = [ss]
+        self.frame_bits_out = []
+        self.frame_bits_in = []
+        self.frame_slot_data_out = []
+        self.frame_slot_data_in = []
+        self.have_slots = {True: None, False: None}
+
+    def handle_slot_dummy(self, slotidx, bitidx, bitcount, is_out, data):
+        # Handle slot x, default/fallback handler.
+        # Only process data of slots 1-12 when slot 0 says "valid".
+        if not self.have_slots[is_out]:
+            return
+        if not self.have_slots[is_out][slotidx]:
+            return
+
+        # Emit a naive annotation with just the data bits that we saw
+        # for the slot (hex nibbles for density). For audio data this
+        # can be good enough. Slots with special meaning should not end
+        # up calling the dummy handler.
+        text = self.int_to_nibble_text(data, bitcount)
+        anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG
+        self.putf(bitidx, bitcount, anncls + slotidx, [text])
+
+        # Emit binary output for the data that is contained in slots
+        # which end up calling the default handler. This transparently
+        # should translate to "the slots with audio data", as other
+        # slots which contain management data should have their specific
+        # handler routines. In the present form, this approach might be
+        # good enough to get a (header-less) audio stream for typical
+        # setups where only line-in or line-out are in use.
+        #
+        # TODO: Improve this early prototype implementation. For now the
+        # decoder just exports the upper 16 bits of each audio channel
+        # that happens to be valid. For an improved implementation, it
+        # either takes user provided specs or more smarts like observing
+        # register access (if the capture includes it).
+        anncls = Ann.BIN_SLOT_RAW_OUT if is_out else Ann.BIN_SLOT_RAW_IN
+        data_bin = data >> 4
+        data_bin &= 0xffff
+        data_bin = data_bin.to_bytes(2, byteorder = 'big')
+        self.putb(bitidx, bitcount, anncls, data_bin)
+
+    def handle_slot_00(self, slotidx, bitidx, bitcount, is_out, data):
+        # Handle slot 0, TAG.
+        slotpos = self.frame_slot_lens[slotidx]
+        fieldoff = 0
+        anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG
+
+        fieldlen = 1
+        ready = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+        text = ['READY: 1', 'READY', 'RDY', 'R'] if ready else ['ready: 0', 'rdy', '-']
+        self.putf(slotpos + fieldoff, fieldlen, anncls, text)
+        fieldoff += fieldlen
+
+        fieldlen = 12
+        valid = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+        text = ['VALID: {:3x}'.format(valid), '{:3x}'.format(valid)]
+        self.putf(slotpos + fieldoff, fieldlen, anncls, text)
+        have_slots = [True] + [False] * 12
+        for idx in range(12):
+            have_slots[idx + 1] = bool(valid & (1 << (11 - idx)))
+        self.have_slots[is_out] = have_slots
+        fieldoff += fieldlen
+
+        fieldlen = 1
+        rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+        if rsv != 0:
+            text = ['reserved bit error', 'rsv error', 'rsv']
+            self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text)
+        fieldoff += fieldlen
+
+        # TODO: Will input slot 0 have a Codec ID, or 3 reserved bits?
+        fieldlen = 2
+        codec = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+        text = ['CODEC: {:1x}'.format(codec), '{:1x}'.format(codec)]
+        self.putf(slotpos + fieldoff, fieldlen, anncls, text)
+        fieldoff += fieldlen
+
+    def handle_slot_01(self, slotidx, bitidx, bitcount, is_out, data):
+        # Handle slot 1, command/status address.
+        slotpos = self.frame_slot_lens[slotidx]
+        if not self.have_slots[is_out]:
+            return
+        if not self.have_slots[is_out][slotidx]:
+            return
+        fieldoff = 0
+        anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG
+        anncls += slotidx
+
+        fieldlen = 1
+        if is_out:
+            is_read = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+            text = ['READ', 'RD', 'R'] if is_read else ['WRITE', 'WR', 'W']
+            self.putf(slotpos + fieldoff, fieldlen, anncls, text)
+            # TODO: Check for the "atomic" constraint? Some operations
+            # involve address _and_ data, which cannot be spread across
+            # several frames. Slot 0 and 1 _must_ be provided within the
+            # same frame (the test should occur in the handler for slot
+            # 2 of course, in slot 1 we don't know what will follow).
+        else:
+            rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+            if rsv != 0:
+                text = ['reserved bit error', 'rsv error', 'rsv']
+                self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text)
+        fieldoff += fieldlen
+
+        fieldlen = 7
+        regaddr = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+        # TODO: Present 0-63 or 0-126 as the address of the 16bit register?
+        text = ['ADDR: {:2x}'.format(regaddr), '{:2x}'.format(regaddr)]
+        self.putf(slotpos + fieldoff, fieldlen, anncls, text)
+        if regaddr & 0x01:
+            text = ['odd register address', 'odd reg addr', 'odd addr', 'odd']
+            self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text)
+        fieldoff += fieldlen
+
+        # Strictly speaking there are 10 data request bits and 2 reserved
+        # bits for input slots, and 12 reserved bits for output slots. We
+        # test for 10 and 2 bits, to simplify the logic. Only in case of
+        # non-zero reserved bits for outputs this will result in "a little
+        # strange" an annotation. This is a cosmetic issue, we don't mind.
+        fieldlen = 10
+        reqdata = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+        if is_out and reqdata != 0:
+            text = ['reserved bit error', 'rsv error', 'rsv']
+            self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text)
+        if not is_out:
+            text = ['REQ: {:3x}'.format(reqdata), '{:3x}'.format(reqdata)]
+            self.putf(slotpos + fieldoff, fieldlen, anncls, text)
+        fieldoff += fieldlen
+
+        fieldlen = 2
+        rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+        if rsv != 0:
+            text = ['reserved bit error', 'rsv error', 'rsv']
+            self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text)
+        fieldoff += fieldlen
+
+    def handle_slot_02(self, slotidx, bitidx, bitcount, is_out, data):
+        # Handle slot 2, command/status data.
+        slotpos = self.frame_slot_lens[slotidx]
+        if not self.have_slots[is_out]:
+            return
+        if not self.have_slots[is_out][slotidx]:
+            return
+        fieldoff = 0
+        anncls = Ann.SLOT_OUT_TAG if is_out else Ann.SLOT_IN_TAG
+        anncls += slotidx
+
+        fieldlen = 16
+        rwdata = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+        # TODO: Check for zero output data when the operation is a read.
+        # TODO: Check for the "atomic" constraint.
+        text = ['DATA: {:4x}'.format(rwdata), '{:4x}'.format(rwdata)]
+        self.putf(slotpos + fieldoff, fieldlen, anncls, text)
+        fieldoff += fieldlen
+
+        fieldlen = 4
+        rsv = self.get_bit_field(data, bitcount, fieldoff, fieldlen)
+        if rsv != 0:
+            text = ['reserved bits error', 'rsv error', 'rsv']
+            self.putf(slotpos + fieldoff, fieldlen, Ann.ERROR, text)
+        fieldoff += fieldlen
+
+    # TODO: Implement other slots.
+    # - 1: cmd/status addr (check status vs command)
+    # - 2: cmd/status data (check status vs command)
+    # - 3-11: audio out/in
+    # - 12: io control/status (modem GPIO(?))
+
+    def handle_slot(self, slotidx, data_out, data_in):
+        # Process a received slot of a frame.
+        func = self.handle_slots.get(slotidx, self.handle_slot_dummy)
+        bitidx = self.frame_slot_lens[slotidx]
+        bitcount = self.frame_slot_lens[slotidx + 1] - bitidx
+        if data_out is not None:
+            func(slotidx, bitidx, bitcount, True, data_out)
+        if data_in is not None:
+            func(slotidx, bitidx, bitcount, False, data_in)
+
+    def handle_bits(self, ss, es, bit_out, bit_in):
+        # Process a received pair of bits.
+        # Emit the bits' annotations. Only interpret the data when we
+        # are in a frame (have seen the start of the frame, and don't
+        # exceed the expected number of bits in a frame).
+        if bit_out is not None:
+            self.putx(ss, es, Ann.BITS_OUT, ['{:d}'.format(bit_out)])
+        if bit_in is not None:
+            self.putx(ss, es, Ann.BITS_IN, ['{:d}'.format(bit_in)])
+        if self.frame_ss_list is None:
+            return
+        self.frame_ss_list.append(es)
+        have_len = len(self.frame_ss_list) - 1
+        if have_len > self.frame_total_bits:
+            return
+
+        # Accumulate the bits within the frame, until one slot of the
+        # frame has become available.
+        slot_idx = 0
+        if bit_out is not None:
+            self.frame_bits_out.append(bit_out)
+            slot_idx = len(self.frame_slot_data_out)
+        if bit_in is not None:
+            self.frame_bits_in.append(bit_in)
+            slot_idx = len(self.frame_slot_data_in)
+        want_len = self.frame_slot_lens[slot_idx + 1]
+        if have_len != want_len:
+            return
+        prev_len = self.frame_slot_lens[slot_idx]
+
+        # Convert bits to integer values. This shall simplify extraction
+        # of bit fields in multiple other locations.
+        slot_data_out = None
+        if bit_out is not None:
+            slot_bits = self.frame_bits_out[prev_len:]
+            slot_data = self.bits_to_int(slot_bits)
+            self.frame_slot_data_out.append(slot_data)
+            slot_data_out = slot_data
+        slot_data_in = None
+        if bit_in is not None:
+            slot_bits = self.frame_bits_in[prev_len:]
+            slot_data = self.bits_to_int(slot_bits)
+            self.frame_slot_data_in.append(slot_data)
+            slot_data_in = slot_data
+
+        # Emit simple annotations for the integer values, until upper
+        # layer decode stages will be implemented.
+        slot_len = have_len - prev_len
+        slot_ss = self.frame_ss_list[prev_len]
+        slot_es = self.frame_ss_list[have_len]
+        if slot_data_out is not None:
+            slot_text = self.int_to_nibble_text(slot_data_out, slot_len)
+            self.putx(slot_ss, slot_es, Ann.SLOT_OUT_RAW, [slot_text])
+        if slot_data_in is not None:
+            slot_text = self.int_to_nibble_text(slot_data_in, slot_len)
+            self.putx(slot_ss, slot_es, Ann.SLOT_IN_RAW, [slot_text])
+
+        self.handle_slot(slot_idx, slot_data_out, slot_data_in)
+
+    def decode(self):
+        have_sdo = self.has_channel(Pins.SDATA_OUT)
+        have_sdi = self.has_channel(Pins.SDATA_IN)
+        if not have_sdo and not have_sdi:
+            raise ChannelError('Either SDATA_OUT or SDATA_IN (or both) are required.')
+        have_reset = self.has_channel(Pins.RESET)
+
+        # Data is sampled at falling CLK edges. Annotations need to span
+        # the period between rising edges. SYNC rises one cycle _before_
+        # the start of a frame. Grab the earliest SYNC sample we can get
+        # and advance to the start of a bit time. Then keep getting the
+        # samples and the end of all subsequent bit times.
+        prev_sync = [None, None, None]
+        pins = self.wait({Pins.BIT_CLK: 'e'})
+        if pins[Pins.BIT_CLK] == 0:
+            prev_sync[-1] = pins[Pins.SYNC]
+            pins = self.wait({Pins.BIT_CLK: 'r'})
+        bit_ss = self.samplenum
+        while True:
+            pins = self.wait({Pins.BIT_CLK: 'f'})
+            prev_sync.pop(0)
+            prev_sync.append(pins[Pins.SYNC])
+            self.wait({Pins.BIT_CLK: 'r'})
+            if prev_sync[0] == 0 and prev_sync[1] == 1:
+                self.start_frame(bit_ss)
+            self.handle_bits(bit_ss, self.samplenum,
+                    pins[Pins.SDATA_OUT] if have_sdo else None,
+                    pins[Pins.SDATA_IN] if have_sdi else None)
+            bit_ss = self.samplenum
index 053575e868cc7eecfa0bcb07832bb1ef389721aa..0dfd7c86bf08fa0ceb25656ab97b00f5daa07909 100644 (file)
@@ -26,7 +26,7 @@ import sigrokdecode as srd
 from .lists import *
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'ade77xx'
     name = 'ADE77xx'
     longname = 'Analog Devices ADE77xx'
@@ -45,14 +45,17 @@ class Decoder(srd.Decoder):
         ('warnings', 'Warnings', (2,)),
     )
 
-    def reset(self):
+    def reset_data(self):
         self.expected = 0
         self.mosi_bytes, self.miso_bytes = [], []
 
     def __init__(self):
-        self.ss_cmd, self.es_cmd = 0, 0
         self.reset()
 
+    def reset(self):
+        self.ss_cmd, self.es_cmd = 0, 0
+        self.reset_data()
+
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
@@ -77,7 +80,7 @@ class Decoder(srd.Decoder):
                     idx = 1 if write else 0
                     self.putx([idx, ['%s: %s' % (rblob[0], "SHORT")]])
                     self.put_warn([self.ss_cmd, es], "Short transfer!")
-                self.reset()
+                self.reset_data()
             return
 
         # Don't care about anything else.
@@ -124,4 +127,4 @@ class Decoder(srd.Decoder):
         else:
             self.putx([0, ['%s: %#x' % (rblob[0], vali)]])
 
-        self.reset()
+        self.reset_data()
index 8f51ee245d43335feca823b9320ba5987ba98aca..dcc08dec55fd52eb1de3dc34b49ab4aa61ef0067 100644 (file)
@@ -88,7 +88,7 @@ regs = {
 ANN_REG = 0
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'adf435x'
     name = 'ADF435x'
     longname = 'Analog Devices ADF4350/1'
@@ -105,6 +105,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.bits = []
 
     def start(self):
index 5857314e77796055dc2166e08b621357b887e857..cd72eca8b46d93432379469b33d10031296b9d5f 100644 (file)
 import sigrokdecode as srd
 
 regs = {
-       0: 'Product_ID',
-       1: 'Revision_ID',
-       2: 'Motion',
-       3: 'Delta_X',
-       4: 'Delta_Y',
-       5: 'SQUAL',
-       6: 'Shutter_Upper',
-       7: 'Shutter_Lower',
-       8: 'Maximum_Pixel',
-       9: 'Pixel_Sum',
-       0xa: 'Minimum_Pixel',
-       0xb: 'Pixel_Grab',
-       0xd: 'Mouse_Control',
-       0x3a: 'Chip_Reset',
-       0x3f: 'Inv_Rev_ID',
-       0x63: 'Motion_Burst',
+    0: 'Product_ID',
+    1: 'Revision_ID',
+    2: 'Motion',
+    3: 'Delta_X',
+    4: 'Delta_Y',
+    5: 'SQUAL',
+    6: 'Shutter_Upper',
+    7: 'Shutter_Lower',
+    8: 'Maximum_Pixel',
+    9: 'Pixel_Sum',
+    0xa: 'Minimum_Pixel',
+    0xb: 'Pixel_Grab',
+    0xd: 'Mouse_Control',
+    0x3a: 'Chip_Reset',
+    0x3f: 'Inv_Rev_ID',
+    0x63: 'Motion_Burst',
 }
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'adns5020'
     name = 'ADNS-5020'
     longname = 'Avago ADNS-5020 optical mouse sensor'
@@ -59,6 +59,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.ss_cmd, self.es_cmd = 0, 0
         self.mosi_bytes = []
 
index 3c5003d129693dd0bb3f42dd968532c9cdbb307f..81c1f28da39506e4d714c779593b3ac77d60f5d3 100644 (file)
@@ -74,7 +74,7 @@ class Decoder(srd.Decoder):
     def putv(self, data):
         self.put(self.bytepos[-2], self.samplenum, self.out_ann, data)
 
-    def reset(self):
+    def reset_variables(self):
         self.state = 'WAIT FOR START LOW'
         self.fall = 0
         self.rise = 0
@@ -122,9 +122,12 @@ class Decoder(srd.Decoder):
         return checksum % 256
 
     def __init__(self):
-        self.samplerate = None
         self.reset()
 
+    def reset(self):
+        self.samplerate = None
+        self.reset_variables()
+
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
@@ -177,7 +180,7 @@ class Decoder(srd.Decoder):
                     self.rise = self.samplenum
                     self.state = 'WAIT FOR RESPONSE LOW'
                 else:
-                    self.reset()
+                    self.reset_variables()
             elif self.state == 'WAIT FOR RESPONSE LOW':
                 self.wait({0: 'f'})
                 if self.is_valid('START HIGH'):
@@ -185,14 +188,14 @@ class Decoder(srd.Decoder):
                     self.fall = self.samplenum
                     self.state = 'WAIT FOR RESPONSE HIGH'
                 else:
-                    self.reset()
+                    self.reset_variables()
             elif self.state == 'WAIT FOR RESPONSE HIGH':
                 self.wait({0: 'r'})
                 if self.is_valid('RESPONSE LOW'):
                     self.rise = self.samplenum
                     self.state = 'WAIT FOR FIRST BIT'
                 else:
-                    self.reset()
+                    self.reset_variables()
             elif self.state == 'WAIT FOR FIRST BIT':
                 self.wait({0: 'f'})
                 if self.is_valid('RESPONSE HIGH'):
@@ -201,14 +204,14 @@ class Decoder(srd.Decoder):
                     self.bytepos.append(self.samplenum)
                     self.state = 'WAIT FOR BIT HIGH'
                 else:
-                    self.reset()
+                    self.reset_variables()
             elif self.state == 'WAIT FOR BIT HIGH':
                 self.wait({0: 'r'})
                 if self.is_valid('BIT LOW'):
                     self.rise = self.samplenum
                     self.state = 'WAIT FOR BIT LOW'
                 else:
-                    self.reset()
+                    self.reset_variables()
             elif self.state == 'WAIT FOR BIT LOW':
                 self.wait({0: 'f'})
                 if self.is_valid('BIT 0 HIGH'):
@@ -216,10 +219,10 @@ class Decoder(srd.Decoder):
                 elif self.is_valid('BIT 1 HIGH'):
                     bit = 1
                 else:
-                    self.reset()
+                    self.reset_variables()
                     continue
                 self.handle_byte(bit)
             elif self.state == 'WAIT FOR END':
                 self.wait({0: 'r'})
                 self.putfs([3, ['End', 'E']])
-                self.reset()
+                self.reset_variables()
index 8f353a21afafd0c0584d0e86f6147b0374a59dba..8de3ce2c8d40915af88ef580f1a645a88d2e62b0 100644 (file)
@@ -127,7 +127,7 @@ def parse_branch_addr(bytes, ref_addr, cpu_state, branch_enc):
     return addr, addrlen, cpu_state, exc_info
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'arm_etmv3'
     name = 'ARM ETMv3'
     longname = 'ARM Embedded Trace Macroblock'
@@ -170,6 +170,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.buf = []
         self.syncbuf = []
         self.prevsample = 0
index 33649a07ec106a305aa531b2f3655ba3d54498cf..5970f27c0f9c2b97120562e6cf075b2cac7e24c8 100644 (file)
@@ -37,7 +37,7 @@ ARM_EXCEPTIONS = {
 }
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'arm_itm'
     name = 'ARM ITM'
     longname = 'ARM Instrumentation Trace Macroblock'
@@ -80,6 +80,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.buf = []
         self.syncbuf = []
         self.swpackets = {}
index 0a3257163b10cf477d5e2051678b4b853ac3a5b0..f50af65b471b652814aaa45f22185a8afc8ea7e3 100644 (file)
@@ -20,7 +20,7 @@
 import sigrokdecode as srd
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'arm_tpiu'
     name = 'ARM TPIU'
     longname = 'ARM Trace Port Interface Unit'
@@ -42,6 +42,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.buf = []
         self.syncbuf = []
         self.prevsample = 0
index baa920e933f59fa4af02ba2c0d397386d8eb6c12..30c32f5fcc89d46d89a6555f00a5fcaac2ac4723 100644 (file)
@@ -46,6 +46,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.ncnt = 0
         self.nmax = 0
         self.addr = 0
index 6a3a241015db89f7d21204fa9058a5bd6de06966..2530e8cc23b4c5bb0bb44a3281f96e90977e189d 100644 (file)
@@ -23,7 +23,7 @@ from .parts import *
 VENDOR_CODE_ATMEL = 0x1e
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'avr_isp'
     name = 'AVR ISP'
     longname = 'AVR In-System Programming'
@@ -51,6 +51,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.state = 'IDLE'
         self.mosi_bytes, self.miso_bytes = [], []
         self.ss_cmd, self.es_cmd = 0, 0
index fcb73af88b945f079a03da306bbbd87983483b04..7fedbbdfe18e4e18b8447134adc271591c143d21 100644 (file)
@@ -154,6 +154,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.clear_state()
 
index 375069f2042a1b1ea0d10a36857fc874cc27fc16..d76d649cc593bfeb7101f636cbb5b4d476d64619 100644 (file)
@@ -65,6 +65,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.reset_variables()
 
@@ -75,11 +78,11 @@ class Decoder(srd.Decoder):
         if key == srd.SRD_CONF_SAMPLERATE:
             self.samplerate = value
             self.bit_width = float(self.samplerate) / float(self.options['bitrate'])
-            self.bitpos = (self.bit_width / 100.0) * self.options['sample_point']
+            self.sample_point = (self.bit_width / 100.0) * self.options['sample_point']
 
     # Generic helper for CAN bit annotations.
     def putg(self, ss, es, data):
-        left, right = int(self.bitpos), int(self.bit_width - self.bitpos)
+        left, right = int(self.sample_point), int(self.bit_width - self.sample_point)
         self.put(ss - left, es + right, self.out_ann, data)
 
     # Single-CAN-bit annotation using the current samplenum.
@@ -105,16 +108,31 @@ class Decoder(srd.Decoder):
         self.ss_bit12 = None
         self.ss_databytebits = []
 
+    # Poor man's clock synchronization. Use signal edges which change to
+    # dominant state in rather simple ways. This naive approach is neither
+    # aware of the SYNC phase's width nor the specific location of the edge,
+    # but improves the decoder's reliability when the input signal's bitrate
+    # does not exactly match the nominal rate.
+    def dom_edge_seen(self, force = False):
+        self.dom_edge_snum = self.samplenum
+        self.dom_edge_bcount = self.curbit
+
+    def bit_sampled(self):
+        # EMPTY
+        pass
+
     # Determine the position of the next desired bit's sample point.
     def get_sample_point(self, bitnum):
-        bitpos = int(self.sof + (self.bit_width * bitnum) + self.bitpos)
-        return bitpos
+        samplenum = self.dom_edge_snum
+        samplenum += int(self.bit_width * (bitnum - self.dom_edge_bcount))
+        samplenum += int(self.sample_point)
+        return samplenum
 
     def is_stuff_bit(self):
         # CAN uses NRZ encoding and bit stuffing.
         # After 5 identical bits, a stuff bit of opposite value is added.
         # But not in the CRC delimiter, ACK, and end of frame fields.
-        if len(self.bits) > self.last_databit + 16:
+        if len(self.bits) > self.last_databit + 17:
             return False
         last_6_bits = self.rawbits[-6:]
         if last_6_bits not in ([0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 0]):
@@ -382,9 +400,14 @@ class Decoder(srd.Decoder):
                 # Wait for a dominant state (logic 0) on the bus.
                 (can_rx,) = self.wait({0: 'l'})
                 self.sof = self.samplenum
+                self.dom_edge_seen(force = True)
                 self.state = 'GET BITS'
             elif self.state == 'GET BITS':
                 # Wait until we're in the correct bit/sampling position.
                 pos = self.get_sample_point(self.curbit)
-                (can_rx,) = self.wait({'skip': pos - self.samplenum})
-                self.handle_bit(can_rx)
+                (can_rx,) = self.wait([{'skip': pos - self.samplenum}, {0: 'f'}])
+                if self.matched[1]:
+                    self.dom_edge_seen()
+                if self.matched[0]:
+                    self.handle_bit(can_rx)
+                    self.bit_sampled()
index 4871205f08261aad2786a175ee2fe48687f5941a..b559c95edc5f280ae727fb6c34a372143b01be17 100644 (file)
 # Return the specified BCD number (max. 8 bits) as integer.
 def bcd2int(b):
     return (b & 0x0f) + ((b >> 4) * 10)
+
+def bitpack(bits):
+    res = 0
+    for i, b in enumerate(bits):
+        res |= b << i
+    return res
+
+def bitunpack(num, minbits=0):
+    res = []
+    while num or minbits > 0:
+        res.append(num & 1)
+        num >>= 1
+        minbits -= 1
+    return tuple(res)
diff --git a/decoders/counter/__init__.py b/decoders/counter/__init__.py
new file mode 100644 (file)
index 0000000..e731311
--- /dev/null
@@ -0,0 +1,28 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Stefan Brüns <stefan.bruens@rwth-aachen.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This PD is a simple counter.
+
+It can count rising and/or falling edges, provides an optional reset
+signal. It can also divide the count to e.g. count the numger of
+fixed length words (where a word corresponds to e.g. 9 clock edges).
+'''
+
+from .pd import Decoder
diff --git a/decoders/counter/pd.py b/decoders/counter/pd.py
new file mode 100644 (file)
index 0000000..cbb6a5f
--- /dev/null
@@ -0,0 +1,99 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Stefan Brüns <stefan.bruens@rwth-aachen.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'counter'
+    name = 'Counter'
+    longname = 'Edge counter'
+    desc = 'Count number of edges.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = []
+    channels = (
+        {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
+    )
+    optional_channels = (
+        {'id': 'reset', 'name': 'Reset', 'desc': 'Reset line'},
+    )
+    annotations = (
+        ('edge_count', 'Edge count'),
+        ('word_count', 'Word count'),
+        ('word_reset', 'Word reset'),
+    )
+    annotation_rows = (
+        ('edge_counts', 'Edges', (0,)),
+        ('word_counts', 'Words', (1,)),
+        ('word_resets', 'Word resets', (2,)),
+    )
+    options = (
+        {'id': 'data_edge', 'desc': 'Edges to count (data)', 'default': 'any',
+            'values': ('any', 'rising', 'falling')},
+        {'id': 'divider', 'desc': 'Count divider (word width)', 'default': 0},
+        {'id': 'reset_edge', 'desc': 'Edge which clears counters (reset)',
+            'default': 'falling', 'values': ('rising', 'falling')},
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.edge_count = 0
+        self.word_count = 0
+        self.have_reset = None
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.edge = self.options['data_edge']
+        self.divider = self.options['divider']
+        if self.divider < 0:
+            self.divider = 0
+
+    def putc(self, cls, annlist):
+        self.put(self.samplenum, self.samplenum, self.out_ann, [cls, annlist])
+
+    def decode(self):
+        condition = [{'rising':  {0: 'r'},
+                      'falling': {0: 'f'},
+                      'any':     {0: 'e'},}[self.edge]]
+
+        if self.has_channel(1):
+            self.have_reset = True
+            condition.append({1: self.options['reset_edge'][0]})
+
+        while True:
+            self.wait(condition)
+            if self.have_reset and self.matched[1]:
+                self.edge_count = 0
+                self.word_count = 0
+                self.putc(2, ['Word reset', 'Reset', 'Rst', 'R'])
+                continue
+
+            self.edge_count += 1
+
+            self.putc(0, [str(self.edge_count)])
+            if self.divider > 0 and (self.edge_count % self.divider) == 0:
+                self.word_count += 1
+                self.putc(1, [str(self.word_count)])
index 24e6bc430e291a50e68f68f8d6b8525547daea83..e9d3a4ba529cedb4e2a34a7a44f3d9a18b62acb6 100644 (file)
@@ -17,8 +17,8 @@
 ## along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ##
 
-# DALI Extended commands
-extendedCommands = {
+# DALI extended commands
+extended_commands = {
     0xA1: ['Terminate special processes', 'Terminate'],
     0xA3: ['DTR = DATA', 'DTR'],
     0xA5: ['INITIALISE', 'INIT'],
@@ -39,7 +39,7 @@ extendedCommands = {
 }
 
 # List of commands
-DALICommands = {
+dali_commands = {
     0x00: ['Immediate Off', 'IOFF'],
     0x01: ['Up 200ms', 'Up'],
     0x02: ['Down 200ms', 'Down'],
@@ -89,7 +89,7 @@ DALICommands = {
 }
 
 # DALI device type 8
-DALIDeviceType8 = {
+dali_device_type8 = {
     0xE0: ['Set Temp X-Y Coordinate', 'Set X-Y'],
     0xE2: ['Activate Colour Set point', 'Activate SetPoint'],
     0xE7: ['Set Colour Temperature Tc', 'DTRs->ColTemp'],
index 5b801a1387b033248a830d8d20680c4165407aad..bf842ad403586418b03eacd920a4337bce3f2e64 100644 (file)
@@ -24,7 +24,7 @@ class SamplerateError(Exception):
     pass
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'dali'
     name = 'DALI'
     longname = 'Digital Addressable Lighting Interface'
@@ -52,17 +52,18 @@ class Decoder(srd.Decoder):
     annotation_rows = (
         ('bits', 'Bits', (0,)),
         ('raw', 'Raw data', (7,)),
-        ('fields', 'Fields', (1, 2, 3, 4, 5, 6,)),
+        ('fields', 'Fields', (1, 2, 3, 4, 5, 6)),
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.samplenum = None
         self.edges, self.bits, self.ss_es_bits = [], [], []
         self.state = 'IDLE'
-        self.nextSamplePoint = None
-        self.nextSample = None
-        self.devType = None
+        self.dev_type = None
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
@@ -132,8 +133,8 @@ class Decoder(srd.Decoder):
             self.putb(1, 7, [5, s])
         elif f >= 160: # Extended command 0b10100000
             if f == 0xC1: # DALI_ENABLE_DEVICE_TYPE_X
-                self.devType = -1
-            x = extendedCommands.get(f, ['Unknown', 'Unk'])
+                self.dev_type = -1
+            x = extended_commands.get(f, ['Unknown', 'Unk'])
             s = ['Extended Command: %02X (%s)' % (f, x[0]),
                  'XC: %02X (%s)' % (f, x[1]),
                  'XC: %02X' % f, 'X: %02X' % f, 'X']
@@ -151,19 +152,18 @@ class Decoder(srd.Decoder):
             s = ['YBit: %d' % b[1][1], 'YB: %d' % b[1][1], 'YB', 'Y', 'Y']
             self.putb(1, 1, [3, s])
             a = f >> 1
-            # x = system.get(a, ['Unknown', 'Unk'])
             s = ['Short address: %d' % a, 'Addr: %d' % a,
                 'Addr: %d' % a, 'A: %d' % a, 'A']
             self.putb(2, 7, [4, s])
 
         # Bits[9:16]: Command/data (MSB-first)
         if f >= 160 and f < 254:
-            if self.devType == -1:
-                self.devType = c
+            if self.dev_type == -1:
+                self.dev_type = c
                 s = ['Type: %d' % c, 'Typ: %d' % c,
                      'Typ: %d' % c, 'T: %d' % c, 'D']
             else:
-                self.devType = None
+                self.dev_type = None
                 s = ['Data: %d' % c, 'Dat: %d' % c,
                      'Dat: %d' % c, 'D: %d' % c, 'D']
         elif b[8][1] == 1:
@@ -182,12 +182,12 @@ class Decoder(srd.Decoder):
             elif un == 0xB0:
                 x = ['Query Scene %d Level' % ln, 'Sc %d Level' % ln]
             elif c >= 224: # Application specific commands
-                if self.devType == 8:
-                    x = DALIDeviceType8.get(c, ['Unknown App', 'Unk'])
+                if self.dev_type == 8:
+                    x = dali_device_type8.get(c, ['Unknown App', 'Unk'])
                 else:
                     x = ['Application Specific Command %d' % c, 'App Cmd %d' % c]
             else:
-                x = DALICommands.get(c, ['Unknown', 'Unk'])
+                x = dali_commands.get(c, ['Unknown', 'Unk'])
             s = ['Command: %d (%s)' % (c, x[0]), 'Com: %d (%s)' % (c, x[1]),
                  'Com: %d' % c, 'C: %d' % c, 'C']
         else:
@@ -198,46 +198,30 @@ class Decoder(srd.Decoder):
     def reset_decoder_state(self):
         self.edges, self.bits, self.ss_es_bits = [], [], []
         self.state = 'IDLE'
-        # self.devType = None
 
-    def decode(self, ss, es, data):
+    def decode(self):
         if not self.samplerate:
             raise SamplerateError('Cannot decode without samplerate.')
-        bit = 0;
-        for (self.samplenum, pins) in data:
-            self.dali = pins[0]
-            # data.itercnt += 1
-            # data.logic_mask = 1
-            # data.cur_pos = self.samplenum
-            # data.edge_index = -1
+        bit = 0
+        while True:
+            # TODO: Come up with more appropriate self.wait() conditions.
+            (dali,) = self.wait()
             if self.options['polarity'] == 'active-high':
-                self.dali ^= 1 # Invert.
+                dali ^= 1 # Invert.
 
             # State machine.
             if self.state == 'IDLE':
                 # Wait for any edge (rising or falling).
-                if self.old_dali == self.dali:
-                    # data.exp_logic = self.exp_logic
-                    # data.logic_mask = 1
-                    # logic.cur_pos = self.samplenum
+                if self.old_dali == dali:
                     continue
                 self.edges.append(self.samplenum)
                 self.state = 'PHASE0'
-                self.old_dali = self.dali
-                # Get the next sample point.
-                # self.nextSamplePoint = self.samplenum + int(self.halfbit / 2)
-                self.old_dali = self.dali
-                # bit = self.dali
-                # data.itercnt += int((self.halfbit - 1) * 0.5)
+                self.old_dali = dali
                 continue
 
-            # if(self.samplenum == self.nextSamplePoint):
-            #    bit = self.dali
-            #    continue
-
-            if (self.old_dali != self.dali):
+            if self.old_dali != dali:
                 self.edges.append(self.samplenum)
-            elif (self.samplenum == (self.edges[-1] + int(self.halfbit * 1.5))):
+            elif self.samplenum == (self.edges[-1] + int(self.halfbit * 1.5)):
                 self.edges.append(self.samplenum - int(self.halfbit * 0.5))
             else:
                 continue
@@ -247,9 +231,9 @@ class Decoder(srd.Decoder):
                 self.phase0 = bit
                 self.state = 'PHASE1'
             elif self.state == 'PHASE1':
-                if (bit == 1) and (self.phase0 == 1): # Stop bit
+                if (bit == 1) and (self.phase0 == 1): # Stop bit.
                     if len(self.bits) == 17 or len(self.bits) == 9:
-                        # Forward or Backward
+                        # Forward or Backward.
                         self.handle_bits(len(self.bits))
                     self.reset_decoder_state() # Reset upon errors.
                     continue
@@ -257,6 +241,4 @@ class Decoder(srd.Decoder):
                     self.bits.append([self.edges[-3], bit])
                     self.state = 'PHASE0'
 
-            # self.nextSamplePoint = self.edges[-1] + int(self.halfbit / 2)
-
-            self.old_dali = self.dali
+            self.old_dali = dali
index a4e87f64a114b7a94bb98d8f3082a8396d1d523a..7b180ce2855f90b4e1d2de1599631d05096b5ede 100644 (file)
@@ -65,6 +65,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.state = 'WAIT FOR RISING EDGE'
         self.ss_bit = self.ss_bit_old = self.es_bit = self.ss_block = 0
index e83d943e15f5a8e9dbf893b241bb9bce47cf72c3..1bcca20ff67cd108882fbf528b3fa4628655ad02 100644 (file)
@@ -20,7 +20,7 @@
 import sigrokdecode as srd
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'dmx512'
     name = 'DMX512'
     longname = 'Digital MultipleX 512'
@@ -52,9 +52,11 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.sample_usec = None
-        self.samplenum = -1
         self.run_start = -1
         self.run_bit = 0
         self.state = 'FIND BREAK'
@@ -71,15 +73,14 @@ class Decoder(srd.Decoder):
     def putr(self, data):
         self.put(self.run_start, self.samplenum, self.out_ann, data)
 
-    def decode(self, ss, es, data):
+    def decode(self):
         if not self.samplerate:
             raise SamplerateError('Cannot decode without samplerate.')
-        for (self.samplenum, pins) in data:
+        while True:
             # Seek for an interval with no state change with a length between
             # 88 and 1000000 us (BREAK).
             if self.state == 'FIND BREAK':
-                if self.run_bit == pins[0]:
-                    continue
+                (dmx,) = self.wait({0: 'h' if self.run_bit == 0 else 'l'})
                 runlen = (self.samplenum - self.run_start) * self.sample_usec
                 if runlen > 88 and runlen < 1000000:
                     self.putr([1, ['Break']])
@@ -89,23 +90,23 @@ class Decoder(srd.Decoder):
                 elif runlen >= 1000000:
                     # Error condition.
                     self.putr([10, ['Invalid break length']])
-                self.run_bit = pins[0]
+                self.run_bit = dmx
                 self.run_start = self.samplenum
             # Directly following the BREAK is the MARK AFTER BREAK.
             elif self.state == 'MARK MAB':
-                if self.run_bit == pins[0]:
-                    continue
+                (dmx,) = self.wait({0: 'h' if self.run_bit == 0 else 'l'})
                 self.putr([2, ['MAB']])
                 self.state = 'READ BYTE'
                 self.channel = 0
                 self.bit = 0
-                self.aggreg = pins[0]
+                self.aggreg = dmx
                 self.run_start = self.samplenum
             # Mark and read a single transmitted byte
             # (start bit, 8 data bits, 2 stop bits).
             elif self.state == 'READ BYTE':
+                (dmx,) = self.wait()
                 self.next_sample = self.run_start + (self.bit + 1) * self.skip_per_bit
-                self.aggreg += pins[0]
+                self.aggreg += dmx
                 if self.samplenum != self.next_sample:
                     continue
                 bit_value = 0 if round(self.aggreg/self.skip_per_bit) == self.bit_break else 1
@@ -126,7 +127,7 @@ class Decoder(srd.Decoder):
                             self.out_ann, [10, ['Invalid stop bit']])
                         if self.bit == 10:
                             # On invalid 2nd stop bit, search for new break.
-                            self.run_bit = pins[0]
+                            self.run_bit = dmx
                             self.state = 'FIND BREAK'
                 else:
                     # Label and process one bit.
@@ -148,19 +149,18 @@ class Decoder(srd.Decoder):
                     # Continue by scanning the IFT.
                     self.channel += 1
                     self.run_start = self.samplenum
-                    self.run_bit = pins[0]
+                    self.run_bit = dmx
                     self.state = 'MARK IFT'
 
-                self.aggreg = pins[0]
+                self.aggreg = dmx
                 self.bit += 1
             # Mark the INTERFRAME-TIME between bytes / INTERPACKET-TIME between packets.
             elif self.state == 'MARK IFT':
-                if self.run_bit == pins[0]:
-                    continue
+                (dmx,) = self.wait({0: 'h' if self.run_bit == 0 else 'l'})
                 if self.channel > 512:
                     self.putr([8, ['Interpacket']])
                     self.state = 'FIND BREAK'
-                    self.run_bit = pins[0]
+                    self.run_bit = dmx
                     self.run_start = self.samplenum
                 else:
                     self.putr([7, ['Interframe']])
index 9b7a21f90e60e10110d0d3ecf9d7ec85d3d58647..414da6531c31998977dab755af66dd3ce421257e 100644 (file)
@@ -52,7 +52,7 @@ def regs_and_bits():
     return tuple(l)
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'ds1307'
     name = 'DS1307'
     longname = 'Dallas DS1307'
@@ -75,6 +75,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.state = 'IDLE'
         self.hours = -1
         self.minutes = -1
diff --git a/decoders/ds243x/__init__.py b/decoders/ds243x/__init__.py
new file mode 100644 (file)
index 0000000..c460e04
--- /dev/null
@@ -0,0 +1,25 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 Kevin Redon <kingkevin@cuvoodoo.info>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This decoder stacks on top of the 'onewire_network' PD and decodes the
+Maxim DS243x (1-Wire EEPROM) protocol.
+'''
+
+from .pd import Decoder
diff --git a/decoders/ds243x/pd.py b/decoders/ds243x/pd.py
new file mode 100644 (file)
index 0000000..c7869a8
--- /dev/null
@@ -0,0 +1,269 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 Kevin Redon <kingkevin@cuvoodoo.info>
+## Copyright (C) 2017 Soeren Apel <soeren@apelpie.net>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+# Dictionary of FUNCTION commands and their names.
+commands_2432 = {
+    0x0f: 'Write scratchpad',
+    0xaa: 'Read scratchpad',
+    0x55: 'Copy scratchpad',
+    0xf0: 'Read memory',
+    0x5a: 'Load first secret',
+    0x33: 'Compute next secret',
+    0xa5: 'Read authenticated page',
+}
+
+commands_2433 = {
+    0x0f: 'Write scratchpad',
+    0xaa: 'Read scratchpad',
+    0x55: 'Copy scratchpad',
+    0xf0: 'Read memory',
+}
+
+# Maxim DS243x family code, present at the end of the ROM code.
+family_codes = {
+    0x33: ('DS2432', commands_2432),
+    0x23: ('DS2433', commands_2433),
+}
+
+# Calculate the CRC-16 checksum.
+# Initial value: 0x0000, xor-in: 0x0000, polynom 0x8005, xor-out: 0xffff.
+def crc16(byte_array):
+    reverse = 0xa001 # Use the reverse polynom to make algo simpler.
+    crc = 0x0000 # Initial value.
+    # Reverse CRC calculation.
+    for byte in byte_array:
+        for bit in range(8):
+            if (byte ^ crc) & 1:
+                crc = (crc >> 1) ^ reverse
+            else:
+                crc >>= 1
+            byte >>= 1
+    crc ^= 0xffff # Invert CRC.
+    return crc
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'ds243x'
+    name = 'DS243x'
+    longname = 'Maxim DS2432/2433'
+    desc = 'Maxim DS243x series 1-Wire EEPROM protocol.'
+    license = 'gplv2+'
+    inputs = ['onewire_network']
+    outputs = ['ds243x']
+    annotations = (
+        ('text', 'Human-readable text'),
+    )
+    binary = (
+        ('mem_read', 'Data read from memory'),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        # Bytes for function command.
+        self.bytes = []
+        self.family_code = None
+        self.family = ''
+        self.commands = commands_2432 # Use max command set until we know better.
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_binary = self.register(srd.OUTPUT_BINARY)
+
+    def putx(self, data):
+        self.put(self.ss, self.es, self.out_ann, data)
+
+    def decode(self, ss, es, data):
+        code, val = data
+
+        if code == 'RESET/PRESENCE':
+            self.ss, self.es = ss, es
+            self.putx([0, ['Reset/presence: %s'
+                           % ('true' if val else 'false')]])
+            self.bytes = []
+        elif code == 'ROM':
+            self.ss, self.es = ss, es
+            self.family_code = val & 0xff
+
+            s = None
+            if self.family_code in family_codes:
+                self.family, self.commands = family_codes[val & 0xff]
+                s = 'is 0x%02x, %s detected' % (self.family_code, self.family)
+            else:
+                s = '%x%02x unknown' % (self.family_code)
+
+            self.putx([0, ['ROM: 0x%016x (%s)' % (val, 'family code ' + s),
+                           'ROM: 0x%016x (%s)' % (val, self.family)]])
+            self.bytes = []
+        elif code == 'DATA':
+            self.bytes.append(val)
+            if 1 == len(self.bytes):
+                self.ss, self.es = ss, es
+                if val not in self.commands:
+                    self.putx([0, ['Unrecognized command: 0x%02x' % val]])
+                else:
+                    self.putx([0, ['Function command: %s (0x%02x)'
+                                   % (self.commands[val], val)]])
+            elif 0x0f == self.bytes[0]: # Write scratchpad
+                if 2 == len(self.bytes):
+                    self.ss = ss
+                elif 3 == len(self.bytes):
+                    self.es = es
+                    self.putx([0, ['Target address: 0x%04x'
+                                   % ((self.bytes[2] << 8) + self.bytes[1])]])
+                elif 4 == len(self.bytes):
+                    self.ss = ss
+                elif 11 == len(self.bytes):
+                    self.es = es
+                    self.putx([0, ['Data: ' + (','.join(format(n, '#04x')
+                                       for n in self.bytes[3:11]))]])
+                elif 12 == len(self.bytes):
+                    self.ss = ss
+                elif 13 == len(self.bytes):
+                    self.es = es
+                    self.putx([0, ['CRC: '
+                        + ('ok' if crc16(self.bytes[0:11]) == (self.bytes[11]
+                        + (self.bytes[12] << 8)) else 'error')]])
+            elif 0xaa == self.bytes[0]: # Read scratchpad
+                if 2 == len(self.bytes):
+                    self.ss = ss
+                elif 3 == len(self.bytes):
+                    self.es = es
+                    self.putx([0, ['Target address: 0x%04x'
+                                   % ((self.bytes[2] << 8) + self.bytes[1])]])
+                elif 4 == len(self.bytes):
+                    self.ss, self.es = ss, es
+                    self.putx([0, ['Data status (E/S): 0x%02x'
+                                   % (self.bytes[3])]])
+                elif 5 == len(self.bytes):
+                    self.ss = ss
+                elif 12 == len(self.bytes):
+                    self.es = es
+                    self.putx([0, ['Data: ' + (','.join(format(n, '#04x')
+                                       for n in self.bytes[4:12]))]])
+                elif 13 == len(self.bytes):
+                    self.ss = ss
+                elif 14 == len(self.bytes):
+                    self.es = es
+                    self.putx([0, ['CRC: '
+                        + ('ok' if crc16(self.bytes[0:12]) == (self.bytes[12]
+                        + (self.bytes[13] << 8)) else 'error')]])
+            elif 0x5a == self.bytes[0]: # Load first secret
+                if 2 == len(self.bytes):
+                    self.ss = ss
+                elif 4 == len(self.bytes):
+                    self.es = es
+                    self.putx([0, ['Authorization pattern (TA1, TA2, E/S): '
+                        + (','.join(format(n, '#04x')
+                            for n in self.bytes[1:4]))]])
+                elif 4 < len(self.bytes):
+                    self.ss, self.es = ss, es
+                    if (0xaa == self.bytes[-1] or 0x55 == self.bytes[-1]):
+                        self.putx([0, ['End of operation']])
+            elif 0x33 == self.bytes[0]: # Compute next secret
+                if 2 == len(self.bytes):
+                    self.ss = ss
+                elif 3 == len(self.bytes):
+                    self.es = es
+                    self.putx([0, ['Target address: 0x%04x'
+                                   % ((self.bytes[2] << 8) + self.bytes[1])]])
+                elif 3 < len(self.bytes):
+                    self.ss, self.es = ss, es
+                    if (0xaa == self.bytes[-1] or 0x55 == self.bytes[-1]):
+                        self.putx([0, ['End of operation']])
+            elif 0x55 == self.bytes[0]: # Copy scratchpad
+                if 2 == len(self.bytes):
+                    self.ss = ss
+                elif 4 == len(self.bytes):
+                    self.es = es
+                    self.putx([0, ['Authorization pattern (TA1, TA2, E/S): '
+                        + (','.join(format(n, '#04x')
+                            for n in self.bytes[1:4]))]])
+                elif 5 == len(self.bytes):
+                    self.ss = ss
+                elif 24 == len(self.bytes):
+                    self.es = es
+                    mac = ','.join(format(n, '#04x') for n in self.bytes[4:24])
+                    self.putx([0, ['Message authentication code: ' + mac,
+                                   'MAC: ' + mac]])
+                elif 24 < len(self.bytes):
+                    self.ss, self.es = ss, es
+                    if (0xaa == self.bytes[-1] or 0x55 == self.bytes[-1]):
+                        self.putx([0, ['Operation succeeded']])
+                    elif (0 == self.bytes[-1]):
+                        self.putx([0, ['Operation failed']])
+            elif 0xa5 == self.bytes[0]: # Read authenticated page
+                if 2 == len(self.bytes):
+                    self.ss = ss
+                elif 3 == len(self.bytes):
+                    self.es = es
+                    self.putx([0, ['Target address: 0x%04x'
+                                   % ((self.bytes[2] << 8) + self.bytes[1])]])
+                elif 4 == len(self.bytes):
+                    self.ss = ss
+                elif 35 == len(self.bytes):
+                    self.es = es
+                    self.putx([0, ['Data: ' + (','.join(format(n, '#04x')
+                                       for n in self.bytes[3:35]))]])
+                elif 36 == len(self.bytes):
+                    self.ss, self.es = ss, es
+                    self.putx([0, ['Padding: '
+                        + ('ok' if 0xff == self.bytes[-1] else 'error')]])
+                elif 37 == len(self.bytes):
+                    self.ss = ss
+                elif 38 == len(self.bytes):
+                    self.es = es
+                    self.putx([0, ['CRC: '
+                        + ('ok' if crc16(self.bytes[0:36]) == (self.bytes[36]
+                        + (self.bytes[37] << 8)) else 'error')]])
+                elif 39 == len(self.bytes):
+                    self.ss = ss
+                elif 58 == len(self.bytes):
+                    self.es = es
+                    mac = ','.join(format(n, '#04x') for n in self.bytes[38:58])
+                    self.putx([0, ['Message authentication code: ' + mac,
+                                   'MAC: ' + mac]])
+                elif 59 == len(self.bytes):
+                    self.ss = ss
+                elif 60 == len(self.bytes):
+                    self.es = es
+                    self.putx([0, ['MAC CRC: '
+                        + ('ok' if crc16(self.bytes[38:58]) == (self.bytes[58]
+                        + (self.bytes[59] << 8)) else 'error')]])
+                elif 60 < len(self.bytes):
+                    self.ss, self.es = ss, es
+                    if (0xaa == self.bytes[-1] or 0x55 == self.bytes[-1]):
+                        self.putx([0, ['Operation completed']])
+            elif 0xf0 == self.bytes[0]: # Read memory
+                if 2 == len(self.bytes):
+                    self.ss = ss
+                elif 3 == len(self.bytes):
+                    self.es = es
+                    self.putx([0, ['Target address: 0x%04x'
+                                   % ((self.bytes[2] << 8) + self.bytes[1])]])
+                elif 3 < len(self.bytes):
+                    self.ss, self.es = ss, es
+                    self.putx([0, ['Data: 0x%02x' % (self.bytes[-1])]])
+
+                    bdata = self.bytes[-1].to_bytes(1, byteorder='big')
+                    self.put(ss, es, self.out_binary, [0, bdata])
index 255f7c8fba8c7d5c7669d96b9458510fe442d7fc..a792d95956f3533094e50583e548bf8cfeded180 100644 (file)
@@ -35,7 +35,7 @@ command = {
 }
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'ds28ea00'
     name = 'DS28EA00'
     longname = 'Maxim DS28EA00 1-Wire digital thermometer'
@@ -48,6 +48,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.trn_beg = 0
         self.trn_end = 0
         self.state = 'ROM'
index f2ca4be76ccb1059f8d3be2c1d6ba35ddbacb510..c5d9bf9c25c34c4f2d2375a577114f288ca6c5e8 100644 (file)
@@ -23,7 +23,7 @@ class SamplerateError(Exception):
     pass
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'dsi'
     name = 'DSI'
     longname = 'Digital Serial Interface'
@@ -40,23 +40,24 @@ class Decoder(srd.Decoder):
     )
     annotations = (
         ('bit', 'Bit'),
-        ('startbit', 'Startbit'),
-        ('Level', 'Dimmer level'),
+        ('startbit', 'Start bit'),
+        ('level', 'Dimmer level'),
         ('raw', 'Raw data'),
     )
     annotation_rows = (
         ('bits', 'Bits', (0,)),
-        ('raw', 'Raw Data',(3,)),
-        ('fields', 'Fields', (1, 2,)),
+        ('raw', 'Raw data', (3,)),
+        ('fields', 'Fields', (1, 2)),
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.samplenum = None
         self.edges, self.bits, self.ss_es_bits = [], [], []
         self.state = 'IDLE'
-        self.nextSamplePoint = None
-        self.nextSample = None
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
@@ -105,13 +106,12 @@ class Decoder(srd.Decoder):
         self.edges, self.bits, self.ss_es_bits = [], [], []
         self.state = 'IDLE'
 
-    def decode(self, ss, es, data):
+    def decode(self):
         if not self.samplerate:
             raise SamplerateError('Cannot decode without samplerate.')
-        bit = 0;
-        for (self.samplenum, pins) in data:
-            self.dsi = pins[0]
-            # data.itercnt += 1
+        bit = 0
+        while True:
+            (self.dsi,) = self.wait()
             if self.options['polarity'] == 'active-high':
                 self.dsi ^= 1 # Invert.
 
@@ -128,15 +128,9 @@ class Decoder(srd.Decoder):
                 self.state = 'PHASE1'
                 self.old_dsi = self.dsi
                 # Get the next sample point.
-                # self.nextSamplePoint = self.samplenum + int(self.halfbit / 2)
                 self.old_dsi = self.dsi
-                # bit = self.dsi
                 continue
 
-            # if(self.samplenum == self.nextSamplePoint):
-            #    bit = self.dsi
-            #    continue
-
             if self.old_dsi != self.dsi:
                 self.edges.append(self.samplenum)
             elif self.samplenum == (self.edges[-1] + int(self.halfbit * 1.5)):
@@ -149,9 +143,9 @@ class Decoder(srd.Decoder):
                 self.phase0 = bit
                 self.state = 'PHASE1'
             elif self.state == 'PHASE1':
-                if (bit == 1) and (self.phase0 == 1): # Stop bit
+                if (bit == 1) and (self.phase0 == 1): # Stop bit.
                     if len(self.bits) == 17 or len(self.bits) == 9:
-                        # Forward or Backward
+                        # Forward or Backward.
                         self.handle_bits(len(self.bits))
                     self.reset_decoder_state() # Reset upon errors.
                     continue
@@ -159,6 +153,4 @@ class Decoder(srd.Decoder):
                     self.bits.append([self.edges[-3], bit])
                     self.state = 'PHASE0'
 
-            # self.nextSamplePoint = self.edges[-1] + int(self.halfbit / 2)
-
             self.old_dsi = self.dsi
index 389fbda9f48a4cdbf9196428d86533e6adfad673..e73884e7e2b9bf7b6ce4bbe27c6767132056eaa6 100644 (file)
@@ -73,7 +73,7 @@ ANN_FIELDS = 0
 ANN_SECTIONS = 1
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'edid'
     name = 'EDID'
     longname = 'Extended Display Identification Data'
@@ -91,6 +91,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.state = None
         # Received data items, used as an index into samplenum/data
         self.cnt = 0
index c259dc10d4af1b44e9a492323d58d40bbfbbe280..49c586d23b417ebbbd8729e05ae2dfbbc6193e24 100644 (file)
@@ -21,7 +21,7 @@ import sigrokdecode as srd
 from .lists import *
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'eeprom24xx'
     name = '24xx EEPROM'
     longname = '24xx I²C EEPROM'
@@ -75,6 +75,9 @@ class Decoder(srd.Decoder):
     def __init__(self):
         self.reset()
 
+    def reset(self):
+        self.reset_variables()
+
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
         self.out_binary = self.register(srd.OUTPUT_BINARY)
@@ -90,7 +93,7 @@ class Decoder(srd.Decoder):
     def putbits(self, bit1, bit2, bits, data):
         self.put(bits[bit1][1], bits[bit2][2], self.out_ann, data)
 
-    def reset(self):
+    def reset_variables(self):
         self.state = 'WAIT FOR START'
         self.packets = []
         self.bytebuf = []
@@ -179,7 +182,7 @@ class Decoder(srd.Decoder):
 
     def decide_on_seq_or_rnd_read(self):
         if len(self.bytebuf) < 2:
-            self.reset()
+            self.reset_variables()
             return
         if len(self.bytebuf) == 2:
             self.is_random_access_read = True
@@ -237,7 +240,7 @@ class Decoder(srd.Decoder):
     def handle_get_control_word(self):
         # The packet after START must be an ADDRESS READ or ADDRESS WRITE.
         if self.cmd not in ('ADDRESS READ', 'ADDRESS WRITE'):
-            self.reset()
+            self.reset_variables()
             return
         self.packet_append()
         self.put_control_word(self.bits)
@@ -249,18 +252,18 @@ class Decoder(srd.Decoder):
         elif self.cmd == 'NACK':
             self.es_block = self.es
             self.putb([0, ['Warning: No reply from slave!']])
-            self.reset()
+            self.reset_variables()
         else:
-            self.reset()
+            self.reset_variables()
 
     def handle_r_get_word_addr_or_byte(self):
         if self.cmd == 'STOP':
             self.es_block = self.es
             self.putb([0, ['Warning: Slave replied, but master aborted!']])
-            self.reset()
+            self.reset_variables()
             return
         elif self.cmd != 'DATA READ':
-            self.reset()
+            self.reset_variables()
             return
         self.packet_append()
         self.state = 'R GET ACK NACK AFTER WORD ADDR OR BYTE'
@@ -272,20 +275,20 @@ class Decoder(srd.Decoder):
             self.is_cur_addr_read = True
             self.state = 'GET STOP AFTER LAST BYTE'
         else:
-            self.reset()
+            self.reset_variables()
 
     def handle_r_get_restart(self):
         if self.cmd == 'RESTART':
             self.state = 'R READ BYTE'
         else:
-            self.reset()
+            self.reset_variables()
 
     def handle_r_read_byte(self):
         if self.cmd == 'DATA READ':
             self.packet_append()
             self.state = 'R GET ACK NACK AFTER BYTE WAS READ'
         else:
-            self.reset()
+            self.reset_variables()
 
     def handle_r_get_ack_nack_after_byte_was_read(self):
         if self.cmd == 'ACK':
@@ -294,7 +297,7 @@ class Decoder(srd.Decoder):
             # It's either a RANDOM READ or a SEQUENTIAL READ.
             self.state = 'GET STOP AFTER LAST BYTE'
         else:
-            self.reset()
+            self.reset_variables()
 
     def handle_w_get_ack_nack_after_control_word(self):
         if self.cmd == 'ACK':
@@ -302,18 +305,18 @@ class Decoder(srd.Decoder):
         elif self.cmd == 'NACK':
             self.es_block = self.es
             self.putb([0, ['Warning: No reply from slave!']])
-            self.reset()
+            self.reset_variables()
         else:
-            self.reset()
+            self.reset_variables()
 
     def handle_w_get_word_addr(self):
         if self.cmd == 'STOP':
             self.es_block = self.es
             self.putb([0, ['Warning: Slave replied, but master aborted!']])
-            self.reset()
+            self.reset_variables()
             return
         elif self.cmd != 'DATA WRITE':
-            self.reset()
+            self.reset_variables()
             return
         self.packet_append()
         self.state = 'W GET ACK AFTER WORD ADDR'
@@ -322,7 +325,7 @@ class Decoder(srd.Decoder):
         if self.cmd == 'ACK':
             self.state = 'W DETERMINE EEPROM READ OR WRITE'
         else:
-            self.reset()
+            self.reset_variables()
 
     def handle_w_determine_eeprom_read_or_write(self):
         if self.cmd == 'START REPEAT':
@@ -332,7 +335,7 @@ class Decoder(srd.Decoder):
             self.packet_append()
             self.state = 'W GET ACK NACK AFTER BYTE WAS WRITTEN'
         else:
-            self.reset()
+            self.reset_variables()
 
     def handle_w_write_byte(self):
         if self.cmd == 'DATA WRITE':
@@ -340,7 +343,7 @@ class Decoder(srd.Decoder):
             self.state = 'W GET ACK NACK AFTER BYTE WAS WRITTEN'
         elif self.cmd == 'STOP':
             if len(self.bytebuf) < 2:
-                self.reset()
+                self.reset_variables()
                 return
             self.es_block = self.es
             if len(self.bytebuf) == 2:
@@ -348,31 +351,31 @@ class Decoder(srd.Decoder):
             else:
                 self.is_page_write = True
             self.put_operation()
-            self.reset()
+            self.reset_variables()
         elif self.cmd == 'START REPEAT':
             # It's either a RANDOM ACCESS READ or SEQUENTIAL RANDOM READ.
             self.state = 'R2 GET CONTROL WORD'
         else:
-            self.reset()
+            self.reset_variables()
 
     def handle_w_get_ack_nack_after_byte_was_written(self):
         if self.cmd == 'ACK':
             self.state = 'W WRITE BYTE'
         else:
-            self.reset()
+            self.reset_variables()
 
     def handle_r2_get_control_word(self):
         if self.cmd == 'ADDRESS READ':
             self.packet_append()
             self.state = 'R2 GET ACK AFTER ADDR READ'
         else:
-            self.reset()
+            self.reset_variables()
 
     def handle_r2_get_ack_after_addr_read(self):
         if self.cmd == 'ACK':
             self.state = 'R2 READ BYTE'
         else:
-            self.reset()
+            self.reset_variables()
 
     def handle_r2_read_byte(self):
         if self.cmd == 'DATA READ':
@@ -383,9 +386,9 @@ class Decoder(srd.Decoder):
             self.es_block = self.es
             self.putb([0, ['Warning: STOP expected after a NACK (not ACK)']])
             self.put_operation()
-            self.reset()
+            self.reset_variables()
         else:
-            self.reset()
+            self.reset_variables()
 
     def handle_r2_get_ack_nack_after_byte_was_read(self):
         if self.cmd == 'ACK':
@@ -394,22 +397,22 @@ class Decoder(srd.Decoder):
             self.decide_on_seq_or_rnd_read()
             self.state = 'GET STOP AFTER LAST BYTE'
         else:
-            self.reset()
+            self.reset_variables()
 
     def handle_get_stop_after_last_byte(self):
         if self.cmd == 'STOP':
             self.es_block = self.es
             self.put_operation()
-            self.reset()
+            self.reset_variables()
         elif self.cmd == 'START REPEAT':
             self.es_block = self.es
             self.putb([0, ['Warning: STOP expected (not RESTART)']])
             self.put_operation()
-            self.reset()
+            self.reset_variables()
             self.ss_block = self.ss
             self.state = 'GET CONTROL WORD'
         else:
-            self.reset()
+            self.reset_variables()
 
     def decode(self, ss, es, data):
         self.cmd, self.databyte = data
index 5ae789f3f5c1fa103046eacfbbb21eb80d78f764..d76b869756c25fbb6fb23a513ba641f46b280819 100644 (file)
@@ -20,7 +20,7 @@
 import sigrokdecode as srd
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'eeprom93xx'
     name = '93xx EEPROM'
     longname = '93xx Microwire EEPROM'
@@ -43,6 +43,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.frame = []
 
     def start(self):
index 8c8a72bfb0092620293e96043243e1da516083b9..778cfd1621e6a918977a9567b0f3898dd877c4f6 100644 (file)
@@ -62,6 +62,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.oldpin = None
         self.last_samplenum = None
@@ -212,7 +215,7 @@ class Decoder(srd.Decoder):
             raise SamplerateError('Cannot decode without samplerate.')
 
         # Initialize internal state from the very first sample.
-        (pin,) = self.wait({'skip': 1})
+        (pin,) = self.wait()
         self.oldpin = pin
         self.last_samplenum = self.samplenum
         self.lastlast_samplenum = self.samplenum
index 895ee15feb2e216b4d2af7f86f86e11e1188b00c..9fac9c6da0d582e601576a5eed818f4311145e8a 100644 (file)
@@ -65,6 +65,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.last_samplenum = None
         self.state = 'FFS_SEARCH'
index 01801bc592161009898a64c28249d626012952df..d131cb9dee30523ef4c56f4e73c4793eb365a066 100644 (file)
@@ -61,6 +61,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.items = []
         self.itemcount = 0
         self.saved_item = None
diff --git a/decoders/graycode/__init__.py b/decoders/graycode/__init__.py
new file mode 100644 (file)
index 0000000..90ef824
--- /dev/null
@@ -0,0 +1,24 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 Christoph Rackwitz <christoph.rackwitz@rwth-aachen.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+Gray code and rotary encoder protocol.
+'''
+
+from .pd import Decoder
diff --git a/decoders/graycode/pd.py b/decoders/graycode/pd.py
new file mode 100644 (file)
index 0000000..ef5d513
--- /dev/null
@@ -0,0 +1,196 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 Christoph Rackwitz <christoph.rackwitz@rwth-aachen.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import math
+import sigrokdecode as srd
+from collections import deque
+from common.srdhelper import bitpack, bitunpack
+
+def gray_encode(plain):
+    return plain & (plain >> 1)
+
+def gray_decode(gray):
+    temp = gray
+    temp ^= (temp >> 8)
+    temp ^= (temp >> 4)
+    temp ^= (temp >> 2)
+    temp ^= (temp >> 1)
+    return temp
+
+def prefix_fmt(value, emin=None):
+    sgn = (value > 0) - (value < 0)
+    value = abs(value)
+    p = math.log10(value) if value else 0
+    value = sgn * math.floor(value * 10**int(3 - p)) * 10**-int(3 - p)
+    e = p // 3 * 3
+    if emin is not None and e < emin:
+        e = emin
+    value *= 10**-e
+    p -= e
+    decimals = 2 - int(p)
+    prefixes = {-9: 'n', -6: 'µ', -3: 'm', 0: '', 3: 'k', 6: 'M', 9: 'G'}
+    return '{0:.{1}f} {2}'.format(value, decimals, prefixes[e])
+
+class ChannelMapError(Exception):
+    pass
+
+class Value:
+    def __init__(self, onchange):
+        self.onchange = onchange
+        self.timestamp = None
+        self.value = None
+
+    def get(self):
+        return self.value
+
+    def set(self, timestamp, newval):
+        if newval != self.value:
+            if self.value is not None:
+                self.onchange(self.timestamp, self.value, timestamp, newval)
+
+            self.value = newval
+            self.timestamp = timestamp
+        elif False:
+            if self.value is not None:
+                self.onchange(self.timestamp, self.value, timestamp, newval)
+
+MAX_CHANNELS = 8 # 10 channels causes some weird problems...
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'graycode'
+    name = 'Gray code'
+    longname = 'Gray code and rotary encoder'
+    desc = 'Accumulate rotary encoder increments, provide timing statistics.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['graycode']
+    optional_channels = tuple(
+        {'id': 'd{}'.format(i), 'name': 'D{}'.format(i), 'desc': 'Data line {}'.format(i)}
+        for i in range(MAX_CHANNELS)
+    )
+    options = (
+        {'id': 'edges', 'desc': 'Edges per rotation', 'default': 0},
+        {'id': 'avg_period', 'desc': 'Averaging period', 'default': 10},
+    )
+    annotations = (
+        ('phase', 'Phase'),
+        ('increment', 'Increment'),
+        ('count', 'Count'),
+        ('turns', 'Turns'),
+        ('interval', 'Interval'),
+        ('average', 'Average'),
+        ('rpm', 'Rate'),
+    )
+    annotation_rows = tuple((u, v, (i,)) for i, (u, v) in enumerate(annotations))
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.num_channels = 0
+        self.samplerate = None
+        self.last_n = deque()
+
+        self.phase = Value(self.on_phase)
+        self.increment = Value(self.on_increment)
+        self.count = Value(self.on_count)
+        self.turns = Value(self.on_turns)
+
+    def on_phase(self, told, vold, tnew, vnew):
+        self.put(told, tnew, self.out_ann, [0, ['{}'.format(vold)]])
+
+    def on_increment(self, told, vold, tnew, vnew):
+        if vold == 0:
+            message = '0'
+        elif abs(vold) == self.ENCODER_STEPS // 2:
+            message = '±π'
+        else:
+            message = '{:+d}'.format(vold)
+        self.put(told, tnew, self.out_ann, [1, [message]])
+
+    def on_count(self, told, vold, tnew, vnew):
+        self.put(told, tnew, self.out_ann, [2, ['{}'.format(vold)]])
+
+    def on_turns(self, told, vold, tnew, vnew):
+        self.put(told, tnew, self.out_ann, [3, ['{:+d}'.format(vold)]])
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def decode(self):
+        chmask = [self.has_channel(i) for i in range(MAX_CHANNELS)]
+        self.num_channels = sum(chmask)
+        if chmask != [i < self.num_channels for i in range(MAX_CHANNELS)]:
+            raise ChannelMapError('Assigned channels need to be contiguous')
+
+        self.ENCODER_STEPS = 1 << self.num_channels
+
+        startbits = self.wait()
+        curtime = self.samplenum
+
+        self.turns.set(self.samplenum, 0)
+        self.count.set(self.samplenum, 0)
+        self.phase.set(self.samplenum, gray_decode(bitpack(startbits[:self.num_channels])))
+
+        while True:
+            prevtime = curtime
+            bits = self.wait([{i: 'e'} for i in range(self.num_channels)])
+            curtime = self.samplenum
+
+            oldcount = self.count.get()
+            oldphase = self.phase.get()
+
+            newphase = gray_decode(bitpack(bits[:self.num_channels]))
+            self.phase.set(self.samplenum, newphase)
+
+            phasedelta_raw = (newphase - oldphase + (self.ENCODER_STEPS // 2 - 1)) % self.ENCODER_STEPS - (self.ENCODER_STEPS // 2 - 1)
+            phasedelta = phasedelta_raw
+            self.increment.set(self.samplenum, phasedelta)
+            if abs(phasedelta) == self.ENCODER_STEPS // 2:
+                phasedelta = 0
+
+            self.count.set(self.samplenum, self.count.get() + phasedelta)
+
+            if self.options['edges']:
+                self.turns.set(self.samplenum, self.count.get() // self.options['edges'])
+
+            if self.samplerate:
+                period = (curtime - prevtime) / self.samplerate
+                freq = abs(phasedelta_raw) / period
+
+                self.put(prevtime, curtime, self.out_ann, [4, [
+                    '{}s, {}Hz'.format(prefix_fmt(period), prefix_fmt(freq))]])
+
+                if self.options['avg_period']:
+                    self.last_n.append((abs(phasedelta_raw), period))
+                    if len(self.last_n) > self.options['avg_period']:
+                        self.last_n.popleft()
+
+                    avg_period = sum(v for u, v in self.last_n) / (sum(u for u, v in self.last_n) or 1)
+                    self.put(prevtime, curtime, self.out_ann, [5, [
+                        '{}s, {}Hz'.format(prefix_fmt(avg_period),
+                            prefix_fmt(1 / avg_period))]])
+
+                if self.options['edges']:
+                    self.put(prevtime, curtime, self.out_ann, [6, ['{}rpm'.format(prefix_fmt(60 * freq / self.options['edges'], emin=0))]])
index 1bccc5435dd88c7536a1eb6f8b60a0070855ccc5..7a3121a3009e0f5c062bb95c390366fb1483b3e4 100644 (file)
@@ -42,9 +42,10 @@ class Decoder(srd.Decoder):
         self.put(self.ss_edge, self.samplenum, self.out_ann, data)
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.ss_edge = None
-        self.first_transition = True
-        self.bitwidth = None
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
@@ -57,19 +58,21 @@ class Decoder(srd.Decoder):
         if not self.samplerate:
             raise SamplerateError('Cannot decode without samplerate.')
 
+        # Get the first edge on the data line.
+        self.wait({0: 'e'})
+        self.ss_edge = self.samplenum
+
+        # Get any subsequent edge on the data line. Get the smallest
+        # distance between any two transitions, assuming it corresponds
+        # to one bit time of the respective bitrate of the input stream.
+        # This heuristics keeps getting better for longer captures.
+        bitwidth = None
         while True:
-            # Wait for any transition/edge on the data line.
             self.wait({0: 'e'})
 
-            # Get the smallest distance between two transitions
-            # and use that to calculate the bitrate/baudrate.
-            if self.first_transition:
-                self.ss_edge = self.samplenum
-                self.first_transition = False
-            else:
-                b = self.samplenum - self.ss_edge
-                if self.bitwidth is None or b < self.bitwidth:
-                    self.bitwidth = b
-                    bitrate = int(float(self.samplerate) / float(b))
-                    self.putx([0, ['%d' % bitrate]])
-                self.ss_edge = self.samplenum
+            b = self.samplenum - self.ss_edge
+            if bitwidth is None or b < bitwidth:
+                bitwidth = b
+                bitrate = int(float(self.samplerate) / float(b))
+                self.putx([0, ['%d' % bitrate]])
+            self.ss_edge = self.samplenum
index 0e7f769e16f16eda276de9809f01fe43106b3983..e70c27de27b301ffcc52d12bf800f141263d3eb1 100644 (file)
@@ -18,7 +18,6 @@
 ##
 
 # TODO: Look into arbitration, collision detection, clock synchronisation, etc.
-# TODO: Implement support for 10bit slave addresses.
 # TODO: Implement support for inverting SDA/SCL levels (0->1 and 1->0).
 # TODO: Implement support for detecting various bus errors.
 
@@ -62,9 +61,6 @@ proto = {
     'DATA WRITE':      [9, 'Data write',    'DW'],
 }
 
-class SamplerateError(Exception):
-    pass
-
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'i2c'
@@ -108,6 +104,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.ss = self.es = self.ss_byte = -1
         self.bitcount = 0
@@ -236,9 +235,10 @@ class Decoder(srd.Decoder):
 
     def handle_stop(self, pins):
         # Meta bitrate
-        elapsed = 1 / float(self.samplerate) * (self.samplenum - self.pdu_start + 1)
-        bitrate = int(1 / elapsed * self.pdu_bits)
-        self.put(self.ss_byte, self.samplenum, self.out_bitrate, bitrate)
+        if self.samplerate:
+            elapsed = 1 / float(self.samplerate) * (self.samplenum - self.pdu_start + 1)
+            bitrate = int(1 / elapsed * self.pdu_bits)
+            self.put(self.ss_byte, self.samplenum, self.out_bitrate, bitrate)
 
         cmd = 'STOP'
         self.ss, self.es = self.samplenum, self.samplenum
@@ -250,11 +250,6 @@ class Decoder(srd.Decoder):
         self.bits = []
 
     def decode(self):
-        if not self.samplerate:
-            raise SamplerateError('Cannot decode without samplerate.')
-
-        self.wait({})
-
         while True:
             # State machine.
             if self.state == 'FIND START':
index d28763b2f55a32c9a37b743da3cc997b2f7f6086..2495e84931a3a42d0af015672d154bd32fd46d5e 100644 (file)
@@ -20,7 +20,7 @@
 import sigrokdecode as srd
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'i2cdemux'
     name = 'I²C demux'
     longname = 'I²C demultiplexer'
@@ -30,6 +30,9 @@ class Decoder(srd.Decoder):
     outputs = [] # TODO: Only known at run-time.
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.packets = [] # Local cache of I²C packets
         self.slaves = [] # List of known slave addresses
         self.stream = -1 # Current output stream
index c3f148fc07e8ae78550b7afb9765cd20a363a04b..1dc7fd1ddfc3e73cb1a2b53f3d38fa091aec0922 100644 (file)
@@ -23,7 +23,7 @@
 import sigrokdecode as srd
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'i2cfilter'
     name = 'I²C filter'
     longname = 'I²C filter'
@@ -39,6 +39,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.curslave = -1
         self.curdirection = None
         self.packets = [] # Local cache of I²C packets
index b0b177f1f007f0687e460db06e49361121af18a3..bfb2c9e9f71b3952551d46499a957b7066c663ff 100644 (file)
@@ -18,6 +18,7 @@
 ##
 
 import sigrokdecode as srd
+import struct
 
 '''
 OUTPUT_PYTHON format:
@@ -32,9 +33,6 @@ Packet:
 <value>: integer
 '''
 
-class SamplerateError(Exception):
-    pass
-
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'i2s'
@@ -59,6 +57,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.oldws = 1
         self.bitcount = 0
@@ -88,12 +89,12 @@ class Decoder(srd.Decoder):
         self.put(self.ss_block, self.samplenum, self.out_ann, data)
 
     def report(self):
-
         # Calculate the sample rate.
         samplerate = '?'
         if self.ss_block is not None and \
             self.first_sample is not None and \
-            self.ss_block > self.first_sample:
+            self.ss_block > self.first_sample and \
+            self.samplerate:
             samplerate = '%d' % (self.samplesreceived *
                 self.samplerate / (self.ss_block -
                 self.first_sample))
@@ -112,25 +113,18 @@ class Decoder(srd.Decoder):
         h += b'\x01\x00'         # Audio format (0x0001 == PCM)
         h += b'\x02\x00'         # Number of channels (2)
         h += b'\x80\x3e\x00\x00' # Samplerate (16000)
-        h += b'\x00\x7d\x00\x00' # Byterate (32000)
+        h += b'\x00\xfa\x00\x00' # Byterate (64000)
         h += b'\x04\x00'         # Blockalign (4)
-        h += b'\x10\x00'         # Bits per sample (16)
+        h += b'\x20\x00'         # Bits per sample (32)
         # Data subchunk
         h += b'data'
-        h += b'\xff\xff\x00\x00' # Subchunk size (65535 bytes) TODO
+        h += b'\xff\xff\xff\xff' # Subchunk size (4G bytes) TODO
         return h
 
     def wav_sample(self, sample):
-        # TODO: This currently assumes U32 samples, and converts to S16.
-        s = sample >> 16
-        if s >= 0x8000:
-            s -= 0x10000
-        lo, hi = s & 0xff, (s >> 8) & 0xff
-        return bytes([lo, hi])
+        return struct.pack('<I', self.data)
 
     def decode(self):
-        if not self.samplerate:
-            raise SamplerateError('Cannot decode without samplerate.')
         while True:
             # Wait for a rising edge on the SCK pin.
             sck, ws, sd = self.wait({0: 'r'})
index 0b34391d2f0732e126824decb09c4d9ec44725e7..06fdc7a3282aa455b0a5982fa37a578b851eca94 100644 (file)
@@ -55,6 +55,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.saved_ATN = False
         self.saved_EOI = False
         self.ss_item = self.es_item = None
index 03a5469c2e27d92ee9397a11a2ae10458cb92453..02d70a9c299aa36ce5fffcb6fc41075715cfeb9c 100644 (file)
@@ -38,6 +38,7 @@ class Decoder(srd.Decoder):
     options = (
         {'id': 'polarity', 'desc': 'Polarity', 'default': 'active-low',
             'values': ('active-low', 'active-high')},
+        {'id': 'cd_freq', 'desc': 'Carrier Frequency', 'default': 0},
     )
     annotations = (
         ('bit', 'Bit'),
@@ -99,6 +100,9 @@ class Decoder(srd.Decoder):
                  '%s' % btn[1]]])
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.state = 'IDLE'
         self.ss_bit = self.ss_start = self.ss_other_edge = self.ss_remote = 0
         self.data = self.count = self.active = None
@@ -111,18 +115,22 @@ class Decoder(srd.Decoder):
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
             self.samplerate = value
-        self.margin = int(self.samplerate * 0.0001) - 1 # 0.1ms
+        self.tolerance = 0.05 # +/-5%
         self.lc = int(self.samplerate * 0.0135) - 1 # 13.5ms
         self.rc = int(self.samplerate * 0.01125) - 1 # 11.25ms
         self.dazero = int(self.samplerate * 0.001125) - 1 # 1.125ms
         self.daone = int(self.samplerate * 0.00225) - 1 # 2.25ms
         self.stop = int(self.samplerate * 0.000652) - 1 # 0.652ms
 
+    def compare_with_tolerance(self, measured, base):
+        return (measured >= base * (1 - self.tolerance)
+                and measured <= base * (1 + self.tolerance))
+
     def handle_bit(self, tick):
         ret = None
-        if tick in range(self.dazero - self.margin, self.dazero + self.margin):
+        if self.compare_with_tolerance(tick, self.dazero):
             ret = 0
-        elif tick in range(self.daone - self.margin, self.daone + self.margin):
+        elif self.compare_with_tolerance(tick, self.daone):
             ret = 1
         if ret in (0, 1):
             self.putb([0, ['%d' % ret]])
@@ -151,9 +159,36 @@ class Decoder(srd.Decoder):
     def decode(self):
         if not self.samplerate:
             raise SamplerateError('Cannot decode without samplerate.')
+
+        cd_count = None
+        if self.options['cd_freq']:
+            cd_count = int(self.samplerate / self.options['cd_freq']) + 1
+            prev_ir = None
+
         while True:
-            # Wait for any edge (rising or falling).
-            (self.ir,) = self.wait({0: 'e'})
+            # Detect changes in the presence of an active input signal.
+            # The decoder can either be fed an already filtered RX signal
+            # or optionally can detect the presence of a carrier. Periods
+            # of inactivity (signal changes slower than the carrier freq,
+            # if specified) pass on the most recently sampled level. This
+            # approach works for filtered and unfiltered input alike, and
+            # only slightly extends the active phase of input signals with
+            # carriers included by one period of the carrier frequency.
+            # IR based communication protocols can cope with this slight
+            # inaccuracy just fine by design. Enabling carrier detection
+            # on already filtered signals will keep the length of their
+            # active period, but will shift their signal changes by one
+            # carrier period before they get passed to decoding logic.
+            if cd_count:
+                (cur_ir,) = self.wait([{0: 'e'}, {'skip': cd_count}])
+                if self.matched[0]:
+                    cur_ir = self.active
+                if cur_ir == prev_ir:
+                    continue
+                prev_ir = cur_ir
+                self.ir = cur_ir
+            else:
+                (self.ir,) = self.wait({0: 'e'})
 
             if self.ir != self.active:
                 # Save the non-active edge, then wait for the next edge.
@@ -164,13 +199,13 @@ class Decoder(srd.Decoder):
 
             # State machine.
             if self.state == 'IDLE':
-                if b in range(self.lc - self.margin, self.lc + self.margin):
+                if self.compare_with_tolerance(b, self.lc):
                     self.putpause('Long')
                     self.putx([5, ['Leader code', 'Leader', 'LC', 'L']])
                     self.ss_remote = self.ss_start
                     self.data = self.count = 0
                     self.state = 'ADDRESS'
-                elif b in range(self.rc - self.margin, self.rc + self.margin):
+                elif self.compare_with_tolerance(b, self.rc):
                     self.putpause('Short')
                     self.putstop(self.samplenum)
                     self.samplenum += self.stop
index adf4e06c56485e67f5c68c1b71b722263b517ecf..60a94160498a3beac0e74f31c1eec5b67c57d288 100644 (file)
@@ -24,7 +24,7 @@ class SamplerateError(Exception):
     pass
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'ir_rc5'
     name = 'IR RC-5'
     longname = 'IR RC-5'
@@ -56,6 +56,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.samplenum = None
         self.edges, self.bits, self.ss_es_bits = [], [], []
@@ -134,12 +137,12 @@ class Decoder(srd.Decoder):
         self.edges, self.bits, self.ss_es_bits = [], [], []
         self.state = 'IDLE'
 
-    def decode(self, ss, es, data):
+    def decode(self):
         if not self.samplerate:
             raise SamplerateError('Cannot decode without samplerate.')
-        for (self.samplenum, pins) in data:
+        while True:
 
-            self.ir = pins[0]
+            (self.ir,) = self.wait()
 
             # Wait for any edge (rising or falling).
             if self.old_ir == self.ir:
@@ -147,8 +150,9 @@ class Decoder(srd.Decoder):
 
             # State machine.
             if self.state == 'IDLE':
+                bit = 1
                 self.edges.append(self.samplenum)
-                self.bits.append([self.samplenum, 1])
+                self.bits.append([self.samplenum, bit])
                 self.state = 'MID1'
                 self.old_ir = self.ir
                 continue
index 04a2577403094fa6f6aba83376973494a9e06229..f492a9fa077c9027965a856b8809dabee2699191 100644 (file)
@@ -63,6 +63,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.state = 'CLK'
         self.samplerate = None
         self.oldclk, self.oldsig = 0, 0
index 4ad11c4cccc2b80d12e99f21c0b2e8558e83126d..7f784eb138dbe19a28571978ece928ce011624d0 100644 (file)
@@ -88,6 +88,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         # self.state = 'TEST-LOGIC-RESET'
         self.state = 'RUN-TEST/IDLE'
         self.oldstate = None
index d8e10e7dc2d9cd0d3b71fea6f25269e4a8982abc..d27a5577babc7af7889c7f0154652d80c17cc393 100644 (file)
@@ -134,7 +134,7 @@ def data_out(bits):
            % (data_hex, ack_meaning)
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'jtag_stm32'
     name = 'JTAG / STM32'
     longname = 'Joint Test Action Group / ST STM32'
@@ -156,6 +156,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.state = 'IDLE'
         self.samplenums = None
 
index c8f4a1a55a8bb8ff3ebdb5cb672e571bc45f6e68..29237d7adaf17528fb3907330d48286cd247ff59 100644 (file)
@@ -39,7 +39,7 @@ ft = {
 }
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'lm75'
     name = 'LM75'
     longname = 'National LM75'
@@ -62,6 +62,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.state = 'IDLE'
         self.reg = 0x00 # Currently selected register
         self.databytes = []
index d0dadaa43a04b629c8f822e96b4d6d939a8a807f..7242208829b95109079b1699788d3a3e7f051b9d 100644 (file)
@@ -95,7 +95,7 @@ fields = {
 }
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'lpc'
     name = 'LPC'
     longname = 'Low-Pin-Count'
@@ -136,10 +136,12 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.state = 'IDLE'
         self.oldlclk = -1
         self.samplenum = 0
-        self.clocknum = 0
         self.lad = -1
         self.addr = 0
         self.cur_nibble = 0
@@ -183,10 +185,10 @@ class Decoder(srd.Decoder):
     def handle_get_ct_dr(self, lad, lad_bits):
         # LAD[3:0]: Cycle type / direction field (1 clock cycle).
 
-        self.cycle_type = fields['CT_DR'][lad]
+        self.cycle_type = fields['CT_DR'].get(lad, 'Reserved / unknown')
 
         # TODO: Warning/error on invalid cycle types.
-        if self.cycle_type == 'Reserved':
+        if 'Reserved' in self.cycle_type:
             self.putb([0, ['Invalid cycle type (%s)' % lad_bits]])
 
         self.es_block = self.samplenum
@@ -252,10 +254,10 @@ class Decoder(srd.Decoder):
         # LAD[3:0]: SYNC field (1-n clock cycles).
 
         self.sync_val = lad_bits
-        self.cycle_type = fields['SYNC'][lad]
+        self.cycle_type = fields['SYNC'].get(lad, 'Reserved / unknown')
 
         # TODO: Warnings if reserved value are seen?
-        if self.cycle_type == 'Reserved':
+        if 'Reserved' in self.cycle_type:
             self.putb([0, ['SYNC, cycle %d: %s (reserved value)' % \
                            (self.synccount, self.sync_val)]])
 
@@ -312,8 +314,10 @@ class Decoder(srd.Decoder):
         self.tarcount = 0
         self.state = 'IDLE'
 
-    def decode(self, ss, es, data):
-        for (self.samplenum, pins) in data:
+    def decode(self):
+        while True:
+            # TODO: Come up with more appropriate self.wait() conditions.
+            pins = self.wait()
 
             # If none of the pins changed, there's nothing to do.
             if self.oldpins == pins:
@@ -349,7 +353,6 @@ class Decoder(srd.Decoder):
                 self.ss_block = self.samplenum
                 self.state = 'GET START'
                 self.lad = -1
-                # self.clocknum = 0
             elif self.state == 'GET START':
                 self.handle_get_start(lad, lad_bits, lframe)
             elif self.state == 'GET CT/DR':
diff --git a/decoders/maple_bus/__init__.py b/decoders/maple_bus/__init__.py
new file mode 100644 (file)
index 0000000..33b90a5
--- /dev/null
@@ -0,0 +1,25 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 Marcus Comstedt <marcus@mc.pp.se>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+Maple bus is serial communication protocol used by peripherals for the
+SEGA Dreamcast game console.
+'''
+
+from .pd import Decoder
diff --git a/decoders/maple_bus/pd.py b/decoders/maple_bus/pd.py
new file mode 100644 (file)
index 0000000..8306061
--- /dev/null
@@ -0,0 +1,217 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 Marcus Comstedt <marcus@mc.pp.se>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+ann = [
+    ['Size', 'L'],
+    ['SrcAP', 'S'],
+    ['DstAP', 'D'],
+    ['Cmd', 'C'],
+    ['Data'],
+    ['Cksum', 'K'],
+]
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'maple_bus'
+    name = 'Maple bus'
+    longname = 'SEGA Maple bus'
+    desc = 'Maple bus peripheral protocol for SEGA Dreamcast.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['maple_bus']
+    channels = (
+        {'id': 'sdcka', 'name': 'SDCKA', 'desc': 'Data/clock line A'},
+        {'id': 'sdckb', 'name': 'SDCKB', 'desc': 'Data/clock line B'},
+    )
+    annotations = (
+        ('start', 'Start pattern'),
+        ('end', 'End pattern'),
+        ('start-with-crc', 'Start pattern with CRC'),
+        ('occupancy', 'SDCKB occupancy pattern'),
+        ('reset', 'RESET pattern'),
+        ('bit', 'Bit'),
+        ('size', 'Data size'),
+        ('source', 'Source AP'),
+        ('dest', 'Destination AP'),
+        ('command', 'Command'),
+        ('data', 'Data'),
+        ('checksum', 'Checksum'),
+        ('frame-error', 'Frame error'),
+        ('checksum-error', 'Checksum error'),
+        ('size-error', 'Size error'),
+    )
+    annotation_rows = (
+        ('bits', 'Bits', (0, 1, 2, 3, 4, 5)),
+        ('fields', 'Fields', (6, 7, 8, 9, 10, 11)),
+        ('warnings', 'Warnings', (12, 13, 14)),
+    )
+    binary = (
+        ('size', 'Data size'),
+        ('source', 'Source AP'),
+        ('dest', 'Destination AP'),
+        ('command', 'Command code'),
+        ('data', 'Data'),
+        ('checksum', 'Checksum'),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        pass
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_binary = self.register(srd.OUTPUT_BINARY)
+        self.pending_bit_pos = None
+
+    def putx(self, data):
+        self.put(self.ss, self.es, self.out_ann, data)
+
+    def putb(self, data):
+        self.put(self.ss, self.es, self.out_binary, data)
+
+    def byte_annotation(self, bintype, d):
+        return [bintype + 6,
+            ['%s: %02X' % (name, d) for name in ann[bintype]] + ['%02X' % d]]
+
+    def got_start(self):
+        self.putx([0, ['Start pattern', 'Start', 'S']])
+
+    def got_end(self):
+        self.putx([1, ['End pattern', 'End', 'E']])
+        if self.length != self.expected_length + 1:
+            self.putx([14, ['Size error', 'L error', 'LE']])
+
+    def got_start_with_crc(self):
+        self.putx([2, ['Start pattern with CRC', 'Start CRC', 'SC']])
+
+    def got_occupancy(self):
+        self.putx([3, ['SDCKB occupancy pattern', 'Occupancy', 'O']])
+
+    def got_reset(self):
+        self.putx([4, ['RESET pattern', 'RESET', 'R']])
+
+    def output_pending_bit(self):
+        if self.pending_bit_pos:
+            self.put(self.pending_bit_pos, self.pending_bit_pos, self.out_ann, [5, ['Bit: %d' % self.pending_bit, '%d' % self.pending_bit]])
+
+    def got_bit(self, n):
+        self.output_pending_bit()
+        self.data = self.data * 2 + n
+        self.pending_bit = n
+        self.pending_bit_pos = self.samplenum
+
+    def got_byte(self):
+        self.output_pending_bit()
+        bintype = 4
+        if self.length < 4:
+            if self.length == 0:
+                self.expected_length = 4 * (self.data + 1)
+            bintype = self.length
+        elif self.length == self.expected_length:
+            bintype = 5
+            if self.data != self.checksum:
+                self.putx([13, ['Cksum error', 'K error', 'KE']])
+        self.length = self.length + 1
+        self.checksum = self.checksum ^ self.data
+        self.putx(self.byte_annotation(bintype, self.data))
+        self.putb([bintype, bytes([self.data])])
+        self.pending_bit_pos = None
+
+    def frame_error(self):
+        self.putx([7, ['Frame error', 'F error', 'FE']])
+
+    def handle_start(self):
+        self.wait({0: 'l', 1: 'h'})
+        self.ss = self.samplenum
+        count = 0
+        while True:
+            sdcka, sdckb = self.wait([{1: 'f'}, {0: 'r'}])
+            if self.matched[0]:
+                count = count + 1
+            if self.matched[1]:
+                self.es = self.samplenum
+                if sdckb == 1:
+                    if count == 4:
+                        self.got_start()
+                        return True
+                    elif count == 6:
+                        self.got_start_with_crc()
+                        return True
+                    elif count == 8:
+                        self.got_occupancy()
+                        return False
+                    elif count >= 14:
+                        self.got_reset()
+                        return False
+                self.frame_error()
+                return False
+
+    def handle_byte_or_stop(self):
+        self.ss = self.samplenum
+        self.pending_bit_pos = None
+        initial = True
+        counta = 0
+        countb = 0
+        self.data = 0
+        while countb < 4:
+            sdcka, sdckb = self.wait([{0: 'f'}, {1: 'f'}])
+            self.es = self.samplenum
+            if self.matched[0]:
+                if counta == countb:
+                    self.got_bit(sdckb)
+                    counta = counta + 1
+                elif counta == 1 and countb == 0 and self.data == 0 and sdckb == 0:
+                    self.wait([{0: 'h', 1: 'h'}, {0: 'f'}, {1: 'f'}])
+                    self.es = self.samplenum
+                    if self.matched[0]:
+                        self.got_end()
+                    else:
+                        self.frame_error()
+                    return False
+                else:
+                    self.frame_error()
+                    return False
+            elif self.matched[1]:
+                if counta == countb + 1:
+                    self.got_bit(sdcka)
+                    countb = countb + 1
+                elif counta == 0 and countb == 0 and sdcka == 1 and initial:
+                    self.ss = self.samplenum
+                    initial = False
+                else:
+                    self.frame_error()
+                    return False
+        self.wait({0: 'h'})
+        self.es = self.samplenum
+        self.got_byte()
+        return True
+
+    def decode(self):
+        while True:
+            while not self.handle_start():
+                pass
+            self.length = 0
+            self.expected_length = 4
+            self.checksum = 0
+            while self.handle_byte_or_stop():
+                pass
index 9589c8f4828e6c4a3cc1f416e60541f96d46a51e..c8e99e1cc948e9fe548ab39158ff8aa905612b23 100644 (file)
@@ -41,7 +41,7 @@ registers = {
 ann_reg, ann_digit, ann_warning = range(3)
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'max7219'
     name = 'MAX7219'
     longname = 'Maxim MAX7219/MAX7221'
@@ -59,6 +59,12 @@ class Decoder(srd.Decoder):
         ('warnings', 'Warnings', (ann_warning,)),
     )
 
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        pass
+
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
         self.pos = 0
index 7c2fc5f3039b465bbe65fb5055c8eb21848d1c85..3580acc1cb888ca7c10a4b41329d20009e3d6b09 100644 (file)
@@ -62,6 +62,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.illegal_bus = 0
         self.samplenum = -1
         self.clause45_addr = -1 # Clause 45 is context sensitive.
index 4ec6eb0420f790d9a075d5e0cbcf9427732304d1..4180ba2f318b1e3f661b72662a87c25379fc4a96 100644 (file)
@@ -71,6 +71,12 @@ class Decoder(srd.Decoder):
         ('warnings', 'Warnings', (5,)),
     )
 
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        pass
+
     def start(self):
         self.out_python = self.register(srd.OUTPUT_PYTHON)
         self.out_ann = self.register(srd.OUTPUT_ANN)
index 21c402477cc392cc3f55a3f1b9c6a77c1845a692..feb581c6684522c8d87a7fc449310403332976b3 100644 (file)
@@ -25,7 +25,7 @@ RX = 0
 TX = 1
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'midi'
     name = 'MIDI'
     longname = 'Musical Instrument Digital Interface'
@@ -44,6 +44,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.state = 'IDLE'
         self.status_byte = 0
         self.explicit_status_byte = False
index c2b37c7bcf9c33a4688c3bac2d85ad13d3a0168a..60927f7324bb5d96c390f27837f1c4845ca7968a 100644 (file)
@@ -20,7 +20,7 @@
 import sigrokdecode as srd
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'mlx90614'
     name = 'MLX90614'
     longname = 'Melexis MLX90614'
@@ -34,6 +34,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.state = 'IGNORE START REPEAT'
         self.data = []
 
index 3420f99db419f33afdc873082b6d79376c50daf7..d2307b9ec5506855331131c2e0d035b2dafc088d 100644 (file)
@@ -811,7 +811,7 @@ class Modbus_ADU_CS(Modbus_ADU):
         self.check_crc(bytecount + 12)
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'modbus'
     name = 'Modbus'
     longname = 'Modbus RTU over RS232/RS485'
@@ -847,6 +847,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.ADUSc = None # Start off with empty slave -> client ADU.
         self.ADUCs = None # Start off with empty client -> slave ADU.
 
diff --git a/decoders/morse/__init__.py b/decoders/morse/__init__.py
new file mode 100644 (file)
index 0000000..5d91624
--- /dev/null
@@ -0,0 +1,28 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 Christoph Rackwitz <christoph.rackwitz@rwth-aachen.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+Morse code is a method of transmitting text information as a series of
+on-off tones.
+
+Details:
+https://en.wikipedia.org/wiki/Morse_code
+'''
+
+from .pd import Decoder
diff --git a/decoders/morse/pd.py b/decoders/morse/pd.py
new file mode 100644 (file)
index 0000000..3048332
--- /dev/null
@@ -0,0 +1,247 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2017 Christoph Rackwitz <christoph.rackwitz@rwth-aachen.de>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+def decode_ditdah(s):
+    return tuple({'-': 3, '.': 1}[c] for c in s)
+
+def encode_ditdah(tpl):
+    return ''.join({1: '.', 3: '-'}[c] for c in tpl)
+
+# https://www.itu.int/dms_pubrec/itu-r/rec/m/R-REC-M.1677-1-200910-I!!PDF-E.pdf
+# Recommendation ITU-R M.1677-1
+# (10/2009)
+# International Morse code
+alphabet = {
+    # 1.1.1 Letters
+    '.-':       'a',
+    '-...':     'b',
+    '-.-.':     'c',
+    '-..':      'd',
+    '.':        'e',
+    '..-..':    'é', # "accented"
+    '..-.':     'f',
+    '--.':      'g',
+    '....':     'h',
+    '..':       'i',
+    '.---':     'j',
+    '-.-':      'k',
+    '.-..':     'l',
+    '--':       'm',
+    '-.':       'n',
+    '---':      'o',
+    '.--.':     'p',
+    '--.-':     'q',
+    '.-.':      'r',
+    '...':      's',
+    '-':        't',
+    '..-':      'u',
+    '...-':     'v',
+    '.--':      'w',
+    '-..-':     'x',
+    '-.--':     'y',
+    '--..':     'z',
+
+    # 1.1.2 Figures
+    '.----':    '1',
+    '..---':    '2',
+    '...--':    '3',
+    '....-':    '4',
+    '.....':    '5',
+    '-....':    '6',
+    '--...':    '7',
+    '---..':    '8',
+    '----.':    '9',
+    '-----':    '0',
+
+    # 1.1.3 Punctuation marks and miscellaneous signs
+    '.-.-.-':   '.',          # Full stop (period)
+    '--..--':   ',',          # Comma
+    '---...':   ':',          # Colon or division sign
+    '..--..':   '?',          # Question mark (note of interrogation or request for repetition of a transmission not understood)
+    '.----.':   '’',          # Apostrophe
+    '-....-':   '-',          # Hyphen or dash or subtraction sign
+    '-..-.':    '/',          # Fraction bar or division sign
+    '-.--.':    '(',          # Left-hand bracket (parenthesis)
+    '-.--.-':   ')',          # Right-hand bracket (parenthesis)
+    '.-..-.':   '“ ”',        # Inverted commas (quotation marks) (before and after the words)
+    '-...-':    '=',          # Double hyphen
+    '...-.':    'UNDERSTOOD', # Understood
+    '........': 'ERROR',      # Error (eight dots)
+    '.-.-.':    '+',          # Cross or addition sign
+    '.-...':    'WAIT',       # Wait
+    '...-.-':   'EOW',        # End of work
+    '-.-.-':    'START',      # Starting signal (to precede every transmission)
+    '.--.-.':   '@',          # Commercial at
+
+    #'-.-':      'ITT',        # K: Invitation to transmit
+
+    # 3.2.1 For the multiplication sign, the signal corresponding to the letter X shall be transmitted.
+    #'-..-':     '×',          # Multiplication sign
+}
+
+alphabet = {decode_ditdah(k): v for k, v in alphabet.items()}
+
+# 2 Spacing and length of the signals (right side is just for printing).
+symbols = { # level, time units
+    # 2.1 A dash is equal to three dots.
+    (1, 1): '*',
+    (1, 3): '===',
+    # 2.2 The space between the signals forming the same letter is equal to one dot.
+    (0, 1): '_',
+    # 2.3 The space between two letters is equal to three dots.
+    (0, 3): '__',
+    # 2.4 The space between two words is equal to seven dots.
+    (0, 7): '___',
+}
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'morse'
+    name = 'Morse'
+    longname = 'Morse code'
+    desc = 'Demodulated morse code protocol.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['morse']
+    channels = (
+        {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
+    )
+    options = (
+        {'id': 'timeunit', 'desc': 'Time unit (guess)', 'default': 0.1},
+    )
+    annotations = (
+        ('time', 'Time'),
+        ('units', 'Units'),
+        ('symbol', 'Symbol'),
+        ('letter', 'Letter'),
+        ('word', 'Word'),
+    )
+    annotation_rows = tuple((u, v, (i,)) for i, (u, v) in enumerate(annotations))
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.samplerate = None
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_binary = self.register(srd.OUTPUT_BINARY)
+
+    def decode_symbols(self):
+        # Annotate symbols, emit symbols, handle timeout via token.
+
+        timeunit = self.options['timeunit']
+
+        self.wait({0: 'r'})
+        prevtime = self.samplenum # Time of an actual edge.
+
+        while True:
+            (val,) = self.wait([{0: 'e'}, {'skip': int(5 * self.samplerate * timeunit)}])
+
+            pval = 1 - val
+            curtime = self.samplenum
+            dt = (curtime - prevtime) / self.samplerate
+            units = dt / timeunit
+            iunits = round(units)
+            error = abs(units - iunits)
+
+            symbol = (pval, iunits)
+
+            if self.matched[1]:
+                yield None # Flush word.
+                continue
+
+            self.put(prevtime, curtime, self.out_ann, [0, ['{:.3g}'.format(dt)]])
+            self.put(prevtime, curtime, self.out_ann, [1, ['{:.1f}*{:.3g}'.format(units, timeunit)]])
+
+            if symbol in symbols:
+                yield (prevtime, curtime, symbol)
+
+            prevtime = curtime
+
+            thisunit = dt / iunits
+            timeunit += (thisunit - timeunit) * 0.02 * iunits # Adapt.
+
+    def decode_morse(self):
+        # Group symbols into letters.
+        sequence = ()
+        s0 = s1 = None
+
+        for item in self.decode_symbols():
+            do_yield = False
+            if item is not None: # Level + width.
+                (t0, t1, symbol) = item
+                (sval, sunits) = symbol
+                if sval == 1:
+                    if s0 is None:
+                        s0 = t0
+                    s1 = t1
+                    sequence += (sunits,)
+                else:
+                    # Generate "flush" for end of letter, end of word.
+                    if sunits >= 3:
+                        do_yield = True
+            else:
+                do_yield = True
+            if do_yield:
+                if sequence:
+                    yield (s0, s1, alphabet.get(sequence, encode_ditdah(sequence)))
+                    sequence = ()
+                    s0 = s1 = None
+            if item is None:
+                yield None # Pass through flush of 5+ space.
+
+    def decode(self):
+
+        # Strictly speaking there is no point in running this decoder
+        # when the sample rate is unknown or zero. But the previous
+        # implementation already fell back to a rate of 1 in that case.
+        # We stick with this approach, to not introduce new constraints
+        # for existing use scenarios.
+        if not self.samplerate:
+            self.samplerate = 1.0
+
+        # Annotate letters, group into words.
+        s0 = s1 = None
+        word = ''
+        for item in self.decode_morse():
+            do_yield = False
+
+            if item is not None: # Append letter.
+                (t0, t1, letter) = item
+                self.put(t0, t1, self.out_ann, [3, [letter]])
+                if s0 is None:
+                    s0 = t0
+                s1 = t1
+                word += letter
+            else:
+                do_yield = True
+
+            if do_yield: # Flush of word.
+                if word:
+                    self.put(s0, s1, self.out_ann, [4, [word]])
+                    word = ''
+                    s0 = s1 = None
index 83fc254461e698b91bb14d18e7eacb378808b9d2..8ccf47384a67f4b54b3f9b2c353aa545d0e9bd47 100644 (file)
@@ -21,7 +21,7 @@ import sigrokdecode as srd
 from .lists import *
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'mrf24j40'
     name = 'MRF24J40'
     longname = 'Microchip MRF24J40'
@@ -43,6 +43,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.ss_cmd, self.es_cmd = 0, 0
         self.mosi_bytes = []
         self.miso_bytes = []
@@ -56,7 +59,7 @@ class Decoder(srd.Decoder):
     def putw(self, pos, msg):
         self.put(pos[0], pos[1], self.out_ann, [4, [msg]])
 
-    def reset(self):
+    def reset_data(self):
         self.mosi_bytes = []
         self.miso_bytes = []
 
@@ -103,7 +106,7 @@ class Decoder(srd.Decoder):
             if cs_old is not None and cs_old == 0 and cs_new == 1:
                 if len(self.mosi_bytes) not in (0, 2, 3):
                     self.putw([self.ss_cmd, es], 'Misplaced CS!')
-                    self.reset()
+                    self.reset_data()
             return
 
         # Don't care about anything else.
@@ -126,8 +129,8 @@ class Decoder(srd.Decoder):
             if len(self.mosi_bytes) == 3:
                 self.es_cmd = es
                 self.handle_long()
-                self.reset()
+                self.reset_data()
         else:
             self.es_cmd = es
             self.handle_short()
-            self.reset()
+            self.reset_data()
index 39e73f7f1c70cfe6bd75e1fa4c01800d4ece2544..1e9045519c90ce41bfecef1dd3b7ac8b9f7872d8 100644 (file)
@@ -59,7 +59,7 @@ status = {
 }
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'mxc6225xu'
     name = 'MXC6225XU'
     longname = 'MEMSIC MXC6225XU'
@@ -72,6 +72,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.state = 'IDLE'
 
     def start(self):
index ae1cb35adf416ce24e47be26c12384a6de0d8ae4..221b968ea2ac0db672e2a5a30761120b16f86f3b 100644 (file)
@@ -59,7 +59,7 @@ xn297_regs = {
 }
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'nrf24l01'
     name = 'nRF24L01(+)'
     longname = 'Nordic Semiconductor nRF24L01/nRF24L01+'
@@ -94,6 +94,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.next()
         self.requirements_met = True
         self.cs_was_released = False
index 6a6b4e9613f7873b6dfb64c3632ecf93272d208d..4f006f2f8f0816546124b288982781eec5cb3f49 100644 (file)
@@ -20,7 +20,7 @@
 import sigrokdecode as srd
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'nunchuk'
     name = 'Nunchuk'
     longname = 'Nintendo Wii Nunchuk'
@@ -47,6 +47,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.state = 'IDLE'
         self.sx = self.sy = self.ax = self.ay = self.az = self.bz = self.bc = -1
         self.databytecount = 0
index ca324159aacd920d140fc6ad01bee288987ac100..72aea83f7248952e91cda83823aa3605f7d09156 100644 (file)
@@ -118,6 +118,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.state = 'INITIAL'
         self.present = 0
index fca16dae272de10d9830d90bd6fc8d44fe3403fc..5d850b5897abc5912843d20d5c2dc27335391dba 100644 (file)
@@ -32,7 +32,7 @@ command = {
 }
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'onewire_network'
     name = '1-Wire network layer'
     longname = '1-Wire serial communication bus (network layer)'
@@ -45,6 +45,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.ss_block = 0
         self.es_block = 0
         self.state = 'COMMAND'
@@ -129,7 +132,7 @@ class Decoder(srd.Decoder):
     # Data collector.
     def onewire_collect(self, length, val, ss, es):
         # Storing the sample this sequence begins with.
-        if self.bit_cnt == 1:
+        if self.bit_cnt == 0:
             self.ss_block = ss
         self.data = self.data & ~(1 << self.bit_cnt) | (val << self.bit_cnt)
         self.bit_cnt += 1
index d054e8975acb99dfc6568c4f342ee0bc70ba56ef..ecaa6807f752c20d0ade4bce2c56081ba27e0d58 100644 (file)
@@ -24,7 +24,7 @@ RX = 0
 TX = 1
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'pan1321'
     name = 'PAN1321'
     longname = 'Panasonic PAN1321'
@@ -39,6 +39,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.cmd = ['', '']
         self.ss_block = None
 
index c8ac2b0f74d2211b86f9a3b88c8f031a23e91223..0e81344855a204c2407cc285f6102e2f3dc14943 100644 (file)
@@ -18,6 +18,7 @@
 ##
 
 import sigrokdecode as srd
+from common.srdhelper import bitpack
 
 '''
 OUTPUT_PYTHON format:
@@ -78,7 +79,8 @@ class Decoder(srd.Decoder):
     options = (
         {'id': 'clock_edge', 'desc': 'Clock edge to sample on',
             'default': 'rising', 'values': ('rising', 'falling')},
-        {'id': 'wordsize', 'desc': 'Data wordsize', 'default': 1},
+        {'id': 'wordsize', 'desc': 'Data wordsize (# bus cycles)',
+            'default': 0},
         {'id': 'endianness', 'desc': 'Data endianness',
             'default': 'little', 'values': ('little', 'big')},
     )
@@ -86,14 +88,21 @@ class Decoder(srd.Decoder):
         ('items', 'Items'),
         ('words', 'Words'),
     )
+    annotation_rows = (
+        ('items', 'Items', (0,)),
+        ('words', 'Words', (1,)),
+    )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.items = []
-        self.itemcount = 0
         self.saved_item = None
         self.ss_item = self.es_item = None
+        self.saved_word = None
+        self.ss_word = self.es_word = None
         self.first = True
-        self.num_channels = 0
 
     def start(self):
         self.out_python = self.register(srd.OUTPUT_PYTHON)
@@ -111,19 +120,20 @@ class Decoder(srd.Decoder):
     def putw(self, data):
         self.put(self.ss_word, self.es_word, self.out_ann, data)
 
-    def handle_bits(self, datapins):
-        # If this is the first item in a word, save its sample number.
-        if self.itemcount == 0:
-            self.ss_word = self.samplenum
+    def handle_bits(self, item, used_pins):
 
-        # Get the bits for this item.
-        item, used_pins = 0, datapins.count(1) + datapins.count(0)
-        for i in range(used_pins):
-            item |= datapins[i] << i
-
-        self.items.append(item)
-        self.itemcount += 1
+        # If a word was previously accumulated, then emit its annotation
+        # now after its end samplenumber became available.
+        if self.saved_word is not None:
+            if self.options['wordsize'] > 0:
+                self.es_word = self.samplenum
+                self.putw([1, [self.fmt_word.format(self.saved_word)]])
+                self.putpw(['WORD', self.saved_word])
+            self.saved_word = None
 
+        # Defer annotations for individual items until the next sample
+        # is taken, and the previous sample's end samplenumber has
+        # become available.
         if self.first:
             # Save the start sample and item for later (no output yet).
             self.ss_item = self.samplenum
@@ -133,50 +143,69 @@ class Decoder(srd.Decoder):
             # Output the saved item (from the last CLK edge to the current).
             self.es_item = self.samplenum
             self.putpb(['ITEM', self.saved_item])
-            self.putb([0, ['%X' % self.saved_item]])
+            self.putb([0, [self.fmt_item.format(self.saved_item)]])
             self.ss_item = self.samplenum
             self.saved_item = item
 
-        endian, ws = self.options['endianness'], self.options['wordsize']
-
-        # Get as many items as the configured wordsize says.
-        if self.itemcount < ws:
+        # Get as many items as the configured wordsize specifies.
+        if not self.items:
+            self.ss_word = self.samplenum
+        self.items.append(item)
+        ws = self.options['wordsize']
+        if len(self.items) < ws:
             return
 
-        # Output annotations/python for a word (a collection of items).
-        word = 0
-        for i in range(ws):
-            if endian == 'little':
-                word |= self.items[i] << ((ws - 1 - i) * used_pins)
-            elif endian == 'big':
-                word |= self.items[i] << (i * used_pins)
-
-        self.es_word = self.samplenum
-        # self.putpw(['WORD', word])
-        # self.putw([1, ['%X' % word]])
-        self.ss_word = self.samplenum
-
-        self.itemcount, self.items = 0, []
+        # Collect words and prepare annotation details, but defer emission
+        # until the end samplenumber becomes available.
+        endian = self.options['endianness']
+        if endian == 'big':
+            self.items.reverse()
+        word = sum([self.items[i] << (i * used_pins) for i in range(ws)])
+        self.saved_word = word
+        self.items = []
 
     def decode(self):
-        for i in range(len(self.optional_channels)):
-            if self.has_channel(i):
-                self.num_channels += 1
-
-        if self.num_channels == 0:
+        # Determine which (optional) channels have input data. Insist in
+        # a non-empty input data set. Cope with sparse connection maps.
+        # Store enough state to later "compress" sampled input data.
+        max_possible = len(self.optional_channels)
+        idx_channels = [
+            idx if self.has_channel(idx) else None
+            for idx in range(max_possible)
+        ]
+        has_channels = [idx for idx in idx_channels if idx is not None]
+        if not has_channels:
             raise ChannelError('At least one channel has to be supplied.')
-
-        if not self.has_channel(0):
-            # CLK was not supplied, sample on ANY edge of ANY of the pins
-            # (but only of those pins that were actually supplied).
-            conds = []
-            for i in range(1, len(self.optional_channels)):
-                if self.has_channel(i):
-                    conds.append({i: 'e'})
-            while True:
-                self.handle_bits(self.wait(conds)[1:])
+        max_connected = max(has_channels)
+
+        # Determine .wait() conditions, depending on the presence of a
+        # clock signal. Either inspect samples on the configured edge of
+        # the clock, or inspect samples upon ANY edge of ANY of the pins
+        # which provide input data.
+        if self.has_channel(0):
+            edge = self.options['clock_edge'][0]
+            conds = {0: edge}
         else:
-            # Sample on the rising or falling CLK edge (depends on config).
-            while True:
-                pins = self.wait({0: self.options['clock_edge'][0]})
-                self.handle_bits(pins[1:])
+            conds = [{idx: 'e'} for idx in has_channels]
+
+        # Pre-determine which input data to strip off, the width of
+        # individual items and multiplexed words, as well as format
+        # strings here. This simplifies call sites which run in tight
+        # loops later.
+        idx_strip = max_connected + 1
+        num_item_bits = idx_strip - 1
+        num_word_items = self.options['wordsize']
+        num_word_bits = num_item_bits * num_word_items
+        num_digits = (num_item_bits + 3) // 4
+        self.fmt_item = "{{:0{}x}}".format(num_digits)
+        num_digits = (num_word_bits + 3) // 4
+        self.fmt_word = "{{:0{}x}}".format(num_digits)
+
+        # Keep processing the input stream. Assume "always zero" for
+        # not-connected input lines. Pass data bits (all inputs except
+        # clock) to the handle_bits() method.
+        while True:
+            pins = self.wait(conds)
+            bits = [0 if idx is None else pins[idx] for idx in idx_channels]
+            item = bitpack(bits[1:idx_strip])
+            self.handle_bits(item, num_item_bits)
index 1a7f0be988cd6d3af7212b39bd848f27782fb14c..6ed04c80af0b40770113caf5e1c84eeab3fbacd0 100644 (file)
@@ -53,6 +53,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.bits = []
         self.samplenum = 0
         self.bitcount = 0
index 0b7be458d881302e368bd778af0e5c4b2252ad8a..97704450b7701f5a01387f234eea6e9d16cc0a8e 100644 (file)
@@ -20,6 +20,9 @@
 
 import sigrokdecode as srd
 
+class SamplerateError(Exception):
+    pass
+
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'pwm'
@@ -49,19 +52,17 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.samplerate = None
         self.ss_block = self.es_block = None
-        self.first_samplenum = None
-        self.start_samplenum = None
-        self.end_samplenum = None
-        self.num_cycles = 0
-        self.average = 0
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
             self.samplerate = value
 
     def start(self):
-        self.startedge = 0 if self.options['polarity'] == 'active-low' else 1
         self.out_ann = self.register(srd.OUTPUT_ANN)
         self.out_binary = self.register(srd.OUTPUT_BINARY)
         self.out_average = \
@@ -89,51 +90,51 @@ class Decoder(srd.Decoder):
         self.put(self.ss_block, self.es_block, self.out_ann, [1, [period_s]])
 
     def putb(self, data):
-        self.put(self.num_cycles, self.num_cycles, self.out_binary, data)
+        self.put(self.ss_block, self.es_block, self.out_binary, data)
 
     def decode(self):
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
 
-        # Get the first rising edge.
-        pin, = self.wait({0: 'e'})
-        if pin != self.startedge:
-            pin, = self.wait({0: 'e'})
+        num_cycles = 0
+        average = 0
+
+        # Wait for an "active" edge (depends on config). This starts
+        # the first full period of the inspected signal waveform.
+        self.wait({0: 'f' if self.options['polarity'] == 'active-low' else 'r'})
         self.first_samplenum = self.samplenum
-        self.start_samplenum = self.samplenum
 
-        # Handle all next edges.
+        # Keep getting samples for the period's middle and terminal edges.
+        # At the same time that last sample starts the next period.
         while True:
-            pin, = self.wait({0: 'e'})
-
-            if pin == self.startedge:
-                # Rising edge
-                # We are on a full cycle we can calculate
-                # the period, the duty cycle and its ratio.
-                period = self.samplenum - self.start_samplenum
-                duty = self.end_samplenum - self.start_samplenum
-                ratio = float(duty / period)
-
-                # This interval starts at this edge.
-                self.ss_block = self.start_samplenum
-                # Store the new rising edge position and the ending
-                # edge interval.
-                self.start_samplenum = self.es_block = self.samplenum
-
-                # Report the duty cycle in percent.
-                percent = float(ratio * 100)
-                self.putx([0, ['%f%%' % percent]])
-
-                # Report the duty cycle in the binary output.
-                self.putb([0, bytes([int(ratio * 256)])])
-
-                # Report the period in units of time.
-                period_t = float(period / self.samplerate)
-                self.putp(period_t)
-
-                # Update and report the new duty cycle average.
-                self.num_cycles += 1
-                self.average += percent
-                self.put(self.first_samplenum, self.es_block, self.out_average,
-                         float(self.average / self.num_cycles))
-            else:
-                # Falling edge
-                self.end_samplenum = self.ss_block = self.samplenum
+
+            # Get the next two edges. Setup some variables that get
+            # referenced in the calculation and in put() routines.
+            start_samplenum = self.samplenum
+            self.wait({0: 'e'})
+            end_samplenum = self.samplenum
+            self.wait({0: 'e'})
+            self.ss_block = start_samplenum
+            self.es_block = self.samplenum
+
+            # Calculate the period, the duty cycle, and its ratio.
+            period = self.samplenum - start_samplenum
+            duty = end_samplenum - start_samplenum
+            ratio = float(duty / period)
+
+            # Report the duty cycle in percent.
+            percent = float(ratio * 100)
+            self.putx([0, ['%f%%' % percent]])
+
+            # Report the duty cycle in the binary output.
+            self.putb([0, bytes([int(ratio * 256)])])
+
+            # Report the period in units of time.
+            period_t = float(period / self.samplerate)
+            self.putp(period_t)
+
+            # Update and report the new duty cycle average.
+            num_cycles += 1
+            average += percent
+            self.put(self.first_samplenum, self.es_block, self.out_average,
+                     float(average / num_cycles))
index 0c880dd217ae60028655a16560a09aa31491a2d8..345efd89250e2ec5472417c107927597a04a085a 100644 (file)
@@ -49,7 +49,7 @@ class Decoder(srd.Decoder):
     id = 'qi'
     name = 'Qi'
     longname = 'Qi charger protocol'
-    desc = 'Protocol used by Qi receiver'
+    desc = 'Protocol used by Qi receiver.'
     license = 'gplv2+'
     inputs = ['logic']
     outputs = ['qi']
@@ -73,6 +73,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.reset_variables()
 
@@ -232,7 +235,7 @@ class Decoder(srd.Decoder):
         if not self.samplerate:
             raise SamplerateError('Cannot decode without samplerate.')
 
-        (qi,) = self.wait({'skip': 1})
+        (qi,) = self.wait()
         self.handle_transition(self.samplenum, qi == 0)
         while True:
             prev = self.samplenum
diff --git a/decoders/rc_encode/__init__.py b/decoders/rc_encode/__init__.py
new file mode 100644 (file)
index 0000000..db78dc1
--- /dev/null
@@ -0,0 +1,36 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Steve R <steversig@virginmedia.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+This PD decodes the remote control protocol which is frequently used
+within key fobs and power socket remotes.
+
+They contain encoding chips like the PT2262 which converts the button
+pressed and address settings into a series of pulses which is then
+transmitted over whatever frequency and modulation that the designer
+chooses. These devices operate at a number of frequencies including 433MHz.
+
+This PD should also decode the HX2262 and SC5262 which are equivalents.
+
+The decoder also contains some additional decoding for a Maplin L95AR
+remote control and will turn the received signal into which button was
+pressed and what the address code DIP switches are set to.
+'''
+
+from .pd import Decoder
diff --git a/decoders/rc_encode/pd.py b/decoders/rc_encode/pd.py
new file mode 100644 (file)
index 0000000..56b60ca
--- /dev/null
@@ -0,0 +1,166 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Steve R <steversig@virginmedia.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sigrokdecode as srd
+
+bitvals = ('0', '1', 'f', 'U')
+
+def decode_bit(edges):
+    # Datasheet says long pulse is 3 times short pulse.
+    lmin = 2 # long min multiplier
+    lmax = 5 # long max multiplier
+    eqmin = 0.5 # equal min multiplier
+    eqmax = 1.5 # equal max multiplier
+    if ( # 0 -___-___
+        (edges[1] >= edges[0] * lmin and edges[1] <= edges[0] * lmax) and
+        (edges[2] >= edges[0] * eqmin and edges[2] <= edges[0] * eqmax) and
+        (edges[3] >= edges[0] * lmin and edges[3] <= edges[0] * lmax)):
+        return '0'
+    elif ( # 1 ---_---_
+        (edges[0] >= edges[1] * lmin and edges[0] <= edges[1] * lmax) and
+        (edges[0] >= edges[2] * eqmin and edges[0] <= edges[2] * eqmax) and
+        (edges[0] >= edges[3] * lmin and edges[0] <= edges[3] * lmax)):
+        return '1'
+    elif ( # float ---_-___
+        (edges[1] >= edges[0] * lmin and edges[1] <= edges[0] * lmax) and
+        (edges[2] >= edges[0] * lmin and edges[2] <= edges[0]* lmax) and
+        (edges[3] >= edges[0] * eqmin and edges[3] <= edges[0] * eqmax)):
+        return 'f'
+    else:
+        return 'U'
+
+def pinlabels(bit_count):
+    if bit_count <= 6:
+        return 'A%i' % (bit_count - 1)
+    else:
+        return 'A%i/D%i' % (bit_count - 1, 12 - bit_count)
+
+def decode_model(model, bits):
+    if model == 'maplin_l95ar':
+        address = 'Addr' # Address pins A0 to A5
+        for i in range(0, 6):
+            address += ' %i:' % (i + 1) + ('on' if bits[i][0] == '0' else 'off')
+        button = 'Button'
+        # Button pins A6/D5 to A11/D0
+        if bits[6][0] == '0' and bits[11][0] == '0':
+            button += ' A ON/OFF'
+        elif bits[7][0] == '0' and bits[11][0] == '0':
+            button += ' B ON/OFF'
+        elif bits[9][0] == '0' and bits[11][0] == '0':
+            button += ' C ON/OFF'
+        elif bits[8][0] == '0' and bits[11][0] == '0':
+            button += ' D ON/OFF'
+        else:
+            button += ' Unknown'
+        return ['%s' % address, bits[0][1], bits[5][2], \
+                '%s' % button, bits[6][1], bits[11][2]]
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'rc_encode'
+    name = 'RC encode'
+    longname = 'Remote control encoder'
+    desc = 'PT2262/HX2262/SC5262 remote control encoder protocol.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = []
+    channels = (
+        {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
+    )
+    annotations = (
+        ('bit-0', 'Bit 0'),
+        ('bit-1', 'Bit 1'),
+        ('bit-f', 'Bit f'),
+        ('bit-U', 'Bit U'),
+        ('bit-sync', 'Bit sync'),
+        ('pin', 'Pin'),
+        ('code-word-addr', 'Code word address'),
+        ('code-word-data', 'Code word data'),
+    )
+    annotation_rows = (
+        ('bits', 'Bits', (0, 1, 2, 3, 4)),
+        ('pins', 'Pins', (5,)),
+        ('code-words', 'Code words', (6, 7)),
+    )
+    options = (
+        {'id': 'remote', 'desc': 'Remote', 'default': 'none', 
+            'values': ('none', 'maplin_l95ar')},
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.samplenumber_last = None
+        self.pulses = []
+        self.bits = []
+        self.labels = []
+        self.bit_count = 0
+        self.ss = None
+        self.es = None
+        self.state = 'IDLE'
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.model = self.options['remote']
+
+    def putx(self, data):
+        self.put(self.ss, self.es, self.out_ann, data)
+
+    def decode(self):
+        while True:
+            pin = self.wait({0: 'e'})
+            self.state = 'DECODING'
+
+            if not self.samplenumber_last: # Set counters to start of signal.
+                self.samplenumber_last = self.samplenum
+                self.ss = self.samplenum
+                continue
+
+            if self.bit_count < 12: # Decode A0 to A11.
+                self.bit_count += 1
+                for i in range(0, 4): # Get four pulses for each bit.
+                    if i > 0:
+                        pin = self.wait({0: 'e'}) # Get next 3 edges.
+                    samples = self.samplenum - self.samplenumber_last
+                    self.pulses.append(samples) # Save the pulse width.
+                    self.samplenumber_last = self.samplenum
+                self.es = self.samplenum
+                self.bits.append([decode_bit(self.pulses), self.ss,
+                                  self.es]) # Save states and times.
+                idx = bitvals.index(decode_bit(self.pulses))
+                self.putx([idx, [decode_bit(self.pulses)]]) # Write decoded bit.
+                self.putx([5, [pinlabels(self.bit_count)]]) # Write pin labels.
+                self.pulses = []
+                self.ss = self.samplenum
+            else:
+                if self.model != 'none':
+                    self.labels = decode_model(self.model, self.bits)
+                    self.put(self.labels[1], self.labels[2], self.out_ann,
+                             [6, [self.labels[0]]]) # Write model decode.
+                    self.put(self.labels[4], self.labels[5], self.out_ann,
+                             [7, [self.labels[3]]]) # Write model decode.
+                samples = self.samplenum - self.samplenumber_last
+                pin = self.wait({'skip': 8 * samples}) # Wait for end of sync bit.
+                self.es = self.samplenum
+                self.putx([4, ['Sync']]) # Write sync label.
+                self.reset() # Reset and wait for next set of pulses.
+                self.state = 'DECODE_TIMEOUT'
+            if not self.state == 'DECODE_TIMEOUT':
+                self.samplenumber_last = self.samplenum
index ab208237fca7240f0a2eada156a318443b4c2a42..e709696c9ef7fbbc28fc3c86cd2b17126875cd7d 100644 (file)
@@ -20,7 +20,7 @@
 import sigrokdecode as srd
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'rfm12'
     name = 'RFM12'
     longname = 'RFM12 control protocol'
@@ -43,6 +43,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.mosi_bytes, self.miso_bytes = [], []
         self.mosi_bits, self.miso_bits = [], []
         self.row_pos = [0, 0, 0]
index c64bd2990a789c0ac1387d7a6d0e41cd33aa3d31..d0875561138ae43ecff813f244091df8b8bcf627 100644 (file)
@@ -20,7 +20,7 @@
 import sigrokdecode as srd
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'rgb_led_spi'
     name = 'RGB LED (SPI)'
     longname = 'RGB LED string decoder (SPI)'
@@ -33,6 +33,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.ss_cmd, self.es_cmd = 0, 0
         self.mosi_bytes = []
 
index 0cb6603fb81dd7eaa9b20969387784b95c2f5aef..b4f7c581d7e8f14b971fef5a912a56c9c42be64b 100644 (file)
@@ -24,7 +24,7 @@ class SamplerateError(Exception):
     pass
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'rgb_led_ws281x'
     name = 'RGB LED (WS281x)'
     longname = 'RGB LED string decoder (WS281x)'
@@ -46,6 +46,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.oldpin = None
         self.ss_packet = None
@@ -70,11 +73,14 @@ class Decoder(srd.Decoder):
             self.bits = []
             self.ss_packet = None
 
-    def decode(self, ss, es, data):
+    def decode(self):
         if not self.samplerate:
             raise SamplerateError('Cannot decode without samplerate.')
 
-        for (self.samplenum, (pin, )) in data:
+        while True:
+            # TODO: Come up with more appropriate self.wait() conditions.
+            (pin,) = self.wait()
+
             if self.oldpin is None:
                 self.oldpin = pin
                 continue
index 2e9057316a97880fd11bf4759858be2b7d2d3a16..3a9dca0c48b36d67fbd44250059dd21855009a42 100644 (file)
@@ -28,7 +28,7 @@ def reg_list():
     return tuple(l)
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'rtc8564'
     name = 'RTC-8564'
     longname = 'Epson RTC-8564 JE/NB'
@@ -52,6 +52,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.state = 'IDLE'
         self.hours = -1
         self.minutes = -1
diff --git a/decoders/sda2506/__init__.py b/decoders/sda2506/__init__.py
new file mode 100644 (file)
index 0000000..4e820ef
--- /dev/null
@@ -0,0 +1,24 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Max Weller
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+'''
+Decoder for Siemens EEPROM SDA2506.
+'''
+
+from .pd import Decoder
diff --git a/decoders/sda2506/pd.py b/decoders/sda2506/pd.py
new file mode 100644 (file)
index 0000000..8b63cf0
--- /dev/null
@@ -0,0 +1,142 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2018 Max Weller
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import re
+import sigrokdecode as srd
+
+ann_cmdbit, ann_databit, ann_cmd, ann_data, ann_warning = range(5)
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'sda2506'
+    name = 'SDA2506'
+    longname = 'Siemens SDA 2506-5'
+    desc = 'Serial nonvolatile 1-Kbit EEPROM.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['sda2506']
+    channels = (
+        {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
+        {'id': 'd', 'name': 'DATA', 'desc': 'Data'},
+        {'id': 'ce', 'name': 'CE#', 'desc': 'Chip-enable'},
+    )
+    annotations = (
+        ('cmdbit', 'Command bit'),
+        ('databit', 'Data bit'),
+        ('cmd', 'Command'),
+        ('data', 'Data byte'),
+        ('warnings', 'Human-readable warnings'),
+    )
+    annotation_rows = (
+        ('bits', 'Bits', (ann_cmdbit, ann_databit)),
+        ('commands', 'Commands', (ann_cmd,)),
+        ('data', 'Data', (ann_data,)),
+        ('warnings', 'Warnings', (ann_warning,)),
+    )
+
+    def __init__(self):
+        self.samplerate = None
+        self.reset()
+
+    def reset(self):
+        self.cmdbits = []
+        self.databits = []
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putbit(self, ss, es, typ, value):
+        self.put(ss, es, self.out_ann, [typ, ['%s' % (value)]])
+
+    def putdata(self, ss, es):
+        value = 0
+        for i in range(8):
+            value = (value << 1) | self.databits[i]
+        self.put(ss, es, self.out_ann, [ann_data, ['%02X' % (value)]])
+
+    def decode_bits(self, offset, width):
+        out = 0
+        for i in range(width):
+            out = (out << 1) | self.cmdbits[offset + i][0]
+        return (out, self.cmdbits[offset + width - 1][1], self.cmdbits[offset][2])
+
+    def decode_field(self, name, offset, width):
+        val, ss, es = self.decode_bits(offset, width)
+        self.put(ss, es, self.out_ann, [ann_data, ['%s: %02X' % (name, val)]])
+        return val
+
+    def decode(self):
+        while True:
+            # Wait for CLK edge or CE edge.
+            clk, d, ce = self.wait([{0: 'e'}, {2: 'e'}])
+
+            if self.matched[0] and ce == 1 and clk == 1:
+                # Rising clk edge and command mode.
+                bitstart = self.samplenum
+                self.wait({0: 'f'})
+                self.cmdbits = [(d, bitstart, self.samplenum)] + self.cmdbits
+                if len(self.cmdbits) > 24:
+                    self.cmdbits = self.cmdbits[0:24]
+                self.putbit(bitstart, self.samplenum, ann_cmdbit, d)
+            elif self.matched[0] and ce == 0 and clk == 0:
+                # Falling clk edge and data mode.
+                bitstart = self.samplenum
+                clk, d, ce = self.wait([{'skip': int(2.5 * (1e6 / self.samplerate))}, {0: 'r'}, {2: 'e'}]) # Wait 25 us for data ready.
+                if self.matched == (True, False, False):
+                    self.wait([{0: 'r'}, {2: 'e'}])
+                if len(self.databits) == 0:
+                    self.datastart = bitstart
+                self.databits = [d] + self.databits
+                self.putbit(bitstart, self.samplenum, ann_databit, d)
+                if len(self.databits) == 8:
+                    self.putdata(self.datastart, self.samplenum)
+                    self.databits = []
+            elif self.matched[1] and ce == 0:
+                # Chip enable edge.
+                try:
+                    self.decode_field('addr', 1, 7)
+                    self.decode_field('CB', 0, 1)
+                    if self.cmdbits[0][0] == 0:
+                        # Beginning read command.
+                        self.decode_field('read', 1, 7)
+                        self.put(self.cmdbits[7][1], self.samplenum,
+                            self.out_ann, [ann_cmd, ['read' ]])
+                    elif d == 0:
+                        # Beginning write command.
+                        self.decode_field('data', 8, 8)
+                        addr, ss, es = self.decode_bits(1, 7)
+                        data, ss, es = self.decode_bits(8, 8)
+                        cmdstart = self.samplenum
+                        self.wait({2: 'r'})
+                        self.put(cmdstart, self.samplenum, self.out_ann,
+                            [ann_cmd, ['Write to %02X: %02X' % (addr, data)]])
+                    else:
+                        # Beginning erase command.
+                        val, ss, es = self.decode_bits(1, 7)
+                        cmdstart = self.samplenum
+                        self.wait({2: 'r'})
+                        self.put(cmdstart, self.samplenum, self.out_ann,
+                            [ann_cmd, ['Erase: %02X' % (val)]])
+                    self.databits = []
+                except Exception as ex:
+                    self.reset()
index 4f10f7ca9fa87256c1cbaf8b29c5bc4921b480e9..9402f7ef7692bc486cc9b6adf9b5e3ba379d8bdd 100644 (file)
@@ -61,6 +61,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.state = 'GET COMMAND TOKEN'
         self.token = []
         self.is_acmd = False # Indicates CMD vs. ACMD
@@ -92,10 +95,6 @@ class Decoder(srd.Decoder):
     def putr(self, desc):
         self.putt([self.last_cmd, ['Reply: %s' % desc]])
 
-    def reset(self):
-        self.cmd, self.arg = None, None
-        self.token, self.state = [], 'GET COMMAND TOKEN'
-
     def cmd_name(self, cmd):
         c = acmd_names if self.is_acmd else cmd_names
         return c.get(cmd, 'Unknown')
index 545cee58a5b6f470c89129fc719aa2fe7d1f0783..7c596345b1c1b8c655dab78e10a4748d0a0ee80f 100644 (file)
@@ -21,7 +21,7 @@ import sigrokdecode as srd
 from common.sdcard import (cmd_names, acmd_names)
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'sdcard_spi'
     name = 'SD card (SPI mode)'
     longname = 'Secure Digital card (SPI mode)'
@@ -46,6 +46,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.state = 'IDLE'
         self.ss, self.es = 0, 0
         self.ss_bit, self.es_bit = 0, 0
index 0c535e4a6d86e56530e6837390d01bac714b2c73..add2834e2f232869ab962c4cd7b9d3ee329887eb 100644 (file)
@@ -58,6 +58,9 @@ class Decoder(srd.Decoder):
         self.put(self.ss_edge, self.samplenum, self.out_ann, data)
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.state = 'GET FIRST PULSE WIDTH'
         self.ss_edge = None
         self.first_edge = True
index b08b8a8d93ee0a208160fac6b7805b1573d17159..9beb4e63b2642358b7447881e22a2fe0376208ed 100644 (file)
@@ -121,6 +121,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.bitcount = 0
         self.misodata = self.mosidata = 0
@@ -142,7 +145,7 @@ class Decoder(srd.Decoder):
         self.out_python = self.register(srd.OUTPUT_PYTHON)
         self.out_ann = self.register(srd.OUTPUT_ANN)
         self.out_binary = self.register(srd.OUTPUT_BINARY)
-        if self.samplerate is not None:
+        if self.samplerate:
             self.out_bitrate = self.register(srd.OUTPUT_META,
                     meta=(int, 'Bitrate', 'Bitrate during transfers'))
         self.bw = (self.options['wordsize'] + 7) // 8
@@ -207,17 +210,18 @@ class Decoder(srd.Decoder):
                 not self.cs_asserted(cs) if self.have_cs else False
 
         ws = self.options['wordsize']
+        bo = self.options['bitorder']
 
         # Receive MISO bit into our shift register.
         if self.have_miso:
-            if self.options['bitorder'] == 'msb-first':
+            if bo == 'msb-first':
                 self.misodata |= miso << (ws - 1 - self.bitcount)
             else:
                 self.misodata |= miso << self.bitcount
 
         # Receive MOSI bit into our shift register.
         if self.have_mosi:
-            if self.options['bitorder'] == 'msb-first':
+            if bo == 'msb-first':
                 self.mosidata |= mosi << (ws - 1 - self.bitcount)
             else:
                 self.mosidata |= mosi << self.bitcount
@@ -249,10 +253,10 @@ class Decoder(srd.Decoder):
         self.putdata()
 
         # Meta bitrate.
-        if self.samplerate is not None:
+        if self.samplerate:
             elapsed = 1 / float(self.samplerate)
             elapsed *= (self.samplenum - self.ss_block + 1)
-            bitrate = int(1 / elapsed * self.options['wordsize'])
+            bitrate = int(1 / elapsed * ws)
             self.put(self.ss_block, self.samplenum, self.out_bitrate, bitrate)
 
         if self.have_cs and self.cs_was_deasserted:
@@ -326,12 +330,9 @@ class Decoder(srd.Decoder):
         # process the very first sample before checking for edges. The
         # previous implementation did this by seeding old values with
         # None, which led to an immediate "change" in comparison.
-        pins = self.wait({})
-        (clk, miso, mosi, cs) = pins
+        (clk, miso, mosi, cs) = self.wait({})
         self.find_clk_edge(miso, mosi, clk, cs, True)
 
         while True:
-            # Ignore identical samples early on (for performance reasons).
-            pins = self.wait(wait_cond)
-            (clk, miso, mosi, cs) = pins
+            (clk, miso, mosi, cs) = self.wait(wait_cond)
             self.find_clk_edge(miso, mosi, clk, cs, False)
index a9eff9ee38fbb25229216d96d189875870ef063d..07934c52c149e206cd008a52c68f12ab54075e2e 100644 (file)
@@ -71,7 +71,7 @@ def decode_status_reg(data):
     return ret
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'spiflash'
     name = 'SPI flash'
     longname = 'SPI flash chips'
@@ -98,6 +98,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.device_id = -1
         self.on_end_transaction = None
         self.end_current_transaction()
index f2e79b8a48c361e0a4800cc15a0635ab9b357a15..f2685e2869dc3010520633b98a511457724b482b 100644 (file)
@@ -23,7 +23,7 @@
 import sigrokdecode as srd
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'ssi32'
     name = 'SSI32'
     longname = 'Synchronous Serial Interface (32bit)'
@@ -46,6 +46,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.ss_cmd, self.es_cmd = 0, 0
         self.mosi_bytes = []
         self.miso_bytes = []
@@ -59,7 +62,7 @@ class Decoder(srd.Decoder):
     def putx(self, data):
         self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
 
-    def reset(self):
+    def reset_data(self):
         self.mosi_bytes = []
         self.miso_bytes = []
         self.es_array = []
@@ -93,7 +96,7 @@ class Decoder(srd.Decoder):
     def decode(self, ss, es, data):
         ptype = data[0]
         if ptype == 'CS-CHANGE':
-            self.reset()
+            self.reset_data()
             return
 
         # Don't care about anything else.
@@ -114,10 +117,10 @@ class Decoder(srd.Decoder):
                 return
 
             self.handle_ack()
-            self.reset()
+            self.reset_data()
         else:
             if len(self.mosi_bytes) < self.options['msgsize']:
                 return
 
             self.handle_ctrl()
-            self.reset()
+            self.reset_data()
index 52e3e635a4bea1185e8e2282bad2d8c901a3d9e1..05463958a28797b196405843cda420f4fd7d543d 100644 (file)
@@ -19,9 +19,6 @@
 
 import sigrokdecode as srd
 
-class SamplerateError(Exception):
-    pass
-
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'stepper_motor'
@@ -50,6 +47,10 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.samplerate = None
         self.oldstep = None
         self.ss_prev_step = None
         self.pos = 0
@@ -70,12 +71,13 @@ class Decoder(srd.Decoder):
 
     def step(self, ss, direction):
         if self.ss_prev_step is not None:
-            delta = ss - self.ss_prev_step
-            speed = self.samplerate / delta / self.scale
-            speed_txt = self.format % speed
+            if self.samplerate:
+                delta = ss - self.ss_prev_step
+                speed = self.samplerate / delta / self.scale
+                speed_txt = self.format % speed
+                self.put(self.ss_prev_step, ss, self.out_ann,
+                    [0, [speed_txt + ' ' + self.unit + '/s', speed_txt]])
             pos_txt = self.format % (self.pos / self.scale)
-            self.put(self.ss_prev_step, ss, self.out_ann,
-                [0, [speed_txt + ' ' + self.unit + '/s', speed_txt]])
             self.put(self.ss_prev_step, ss, self.out_ann,
                 [1, [pos_txt + ' ' + self.unit, pos_txt]])
 
@@ -87,8 +89,6 @@ class Decoder(srd.Decoder):
             self.samplerate = value
 
     def decode(self):
-        if not self.samplerate:
-            raise SamplerateError('Cannot decode without samplerate.')
         while True:
             step, direction = self.wait({0: 'r'})
             self.step(self.samplenum, direction)
index 22aad4533d25b89aba66f55edabbd4058b195156..0f7bc22aab281eaa8b418604f6970131a5524d69 100644 (file)
@@ -92,6 +92,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         # SWD data/clock state
         self.state = 'UNKNOWN'
         self.sample_edge = RISING
index 5a7611a46a14ac0c63110ee7ce7361b3a5075684..07df9c2cddd8eb9a38f6a05f648939da51693373 100644 (file)
@@ -66,6 +66,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.last_samplenum = None
         self.lastlast_samplenum = None
index 9fa9d23b095bb47f505be091da4e341c34916204..0e607675f0465f3df0f7e3b36cd8da8da28bb493 100644 (file)
@@ -22,7 +22,7 @@
 import sigrokdecode as srd
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'tca6408a'
     name = 'TI TCA6408A'
     longname = 'Texas Instruments TCA6408A'
@@ -41,6 +41,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.state = 'IDLE'
         self.chip = -1
 
index ab963bb11d33341ff564073f07c62c865f66c574..61eab1ea104c03b1a45b8f646f2b16df697cf4eb 100644 (file)
@@ -74,6 +74,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.last_samplenum = None
         self.last_n = deque()
index 3ca1415dd7ab845f16cd798eccd3533b15632cad..6fe8ae10d651c836a83b9dd13e4424a51307a902 100644 (file)
@@ -71,6 +71,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.bits = []
         self.ss_dac_first = None
         self.ss_dac = self.es_dac = 0
index 16729cc48fd5837a51e4a377e69b20ef9317e704..89932717ad8c24972e886f53c802260cc1575fff 100644 (file)
@@ -160,6 +160,9 @@ class Decoder(srd.Decoder):
         self.put(s - floor(halfbit), self.samplenum + ceil(halfbit), self.out_binary, data)
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.samplenum = 0
         self.frame_start = [-1, -1]
index aab0c956c11526aa708efb35abbee42604a57d02..489e58eb81419b10b548de0d12865413acfc5ad4 100644 (file)
@@ -173,7 +173,7 @@ def calc_crc16(bitstr):
     return reverse_number(crc16, 16)
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'usb_packet'
     name = 'USB packet'
     longname = 'Universal Serial Bus (LS/FS) packet'
@@ -222,6 +222,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.bits = []
         self.packet = []
         self.packet_summary = ''
index 3258ac329674e3ac8f1f0751e34c8e051fd0b55d..c182498c025f5535cf758013699d35694485e2ab 100644 (file)
@@ -101,14 +101,23 @@ EOP = 0x16
 SYNC_CODES = [SYNC1, SYNC2, SYNC3]
 HRST_CODES = [RST1, RST1, RST1, RST2]
 
+SOP_SEQUENCES = [
+    (SYNC1, SYNC1, SYNC1, SYNC2),
+    (SYNC1, SYNC1, SYNC3, SYNC3),
+    (SYNC1, SYNC3, SYNC1, SYNC3),
+    (SYNC1, RST2,  RST2,  SYNC3),
+    (SYNC1, RST2,  SYNC3, SYNC2),
+    (RST1,  SYNC1, RST1,  SYNC3),
+    (RST1,  RST1,  RST1,   RST2),
+]
 START_OF_PACKETS = {
-    (SYNC1, SYNC1, SYNC1, SYNC2): 'SOP',
-    (SYNC1, SYNC1, SYNC3, SYNC3): "SOP'",
-    (SYNC1, SYNC3, SYNC1, SYNC3): 'SOP"',
-    (SYNC1, RST2,  RST2,  SYNC3): "SOP' Debug",
-    (SYNC1, RST2,  SYNC3, SYNC2): 'SOP" Debug',
-    (RST1,  SYNC1, RST1,  SYNC3): 'Cable Reset',
-    (RST1,  RST1,  RST1,   RST2): 'Hard Reset',
+    SOP_SEQUENCES[0]: 'SOP',
+    SOP_SEQUENCES[1]: "SOP'",
+    SOP_SEQUENCES[2]: 'SOP"',
+    SOP_SEQUENCES[3]: "SOP' Debug",
+    SOP_SEQUENCES[4]: 'SOP" Debug',
+    SOP_SEQUENCES[5]: 'Cable Reset',
+    SOP_SEQUENCES[6]: 'Hard Reset',
 }
 
 SYM_NAME = [
@@ -228,7 +237,7 @@ class Decoder(srd.Decoder):
         op_ma = ((rdo >> 10) & 0x3ff) * 10
         max_ma = (rdo & 0x3ff) * 10
         flags = ''
-        for f in RDO_FLAGS.keys():
+        for f in sorted(RDO_FLAGS.keys(), reverse = True):
             if rdo & f:
                 flags += ' ' + RDO_FLAGS[f]
         return '[%d]%d/%d mA%s' % (pos, op_ma, max_ma, flags)
@@ -252,7 +261,7 @@ class Decoder(srd.Decoder):
         else:
             p = ''
         flags = ''
-        for f in PDO_FLAGS.keys():
+        for f in sorted(PDO_FLAGS.keys(), reverse = True):
             if pdo & f:
                 flags += ' ' + PDO_FLAGS[f]
         return '%s%s%s' % (PDO_TYPE[t], p, flags)
@@ -276,7 +285,7 @@ class Decoder(srd.Decoder):
         else:
             p = ''
         flags = ''
-        for f in PDO_FLAGS.keys():
+        for f in sorted(PDO_FLAGS.keys(), reverse = True):
             if pdo & f:
                 flags += ' ' + PDO_FLAGS[f]
         return '%s%s%s' % (PDO_TYPE[t], p, flags)
@@ -403,7 +412,7 @@ class Decoder(srd.Decoder):
     def find_corrupted_sop(self, k):
         # Start of packet are valid even if they have only 3 correct symbols
         # out of 4.
-        for seq in START_OF_PACKETS.keys():
+        for seq in SOP_SEQUENCES:
             if [k[i] == seq[i] for i in range(len(k))].count(True) >= 3:
                 return START_OF_PACKETS[seq]
         return None
@@ -412,7 +421,7 @@ class Decoder(srd.Decoder):
         for i in range(len(self.bits) - 19):
             k = (self.get_sym(i, rec=False), self.get_sym(i+5, rec=False),
                  self.get_sym(i+10, rec=False), self.get_sym(i+15, rec=False))
-            sym = START_OF_PACKETS[k] if k in START_OF_PACKETS else None
+            sym = START_OF_PACKETS.get(k, None)
             if not sym:
                 sym = self.find_corrupted_sop(k)
             # We have an interesting symbol sequence
@@ -439,6 +448,9 @@ class Decoder(srd.Decoder):
         return -1   # No Start Of Packet
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.idx = 0
         self.packet_seq = 0
index c1f00cdb008568eca7c19102853d58bbbf453ca6..e77debc46d784a06372960285cea3844bfab8585 100644 (file)
@@ -112,7 +112,7 @@ class pcap_usb_pkt():
         return 64 + len(self.data)
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'usb_request'
     name = 'USB request'
     longname = 'Universal Serial Bus (LS/FS) transaction/request'
@@ -136,6 +136,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.request = {}
         self.request_id = 0
@@ -168,7 +171,8 @@ class Decoder(srd.Decoder):
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
             self.samplerate = value
-            self.secs_per_sample = float(1) / float(self.samplerate)
+            if self.samplerate:
+                self.secs_per_sample = float(1) / float(self.samplerate)
 
     def start(self):
         self.out_binary = self.register(srd.OUTPUT_BINARY)
@@ -179,6 +183,14 @@ class Decoder(srd.Decoder):
         request_end = self.handshake in ('ACK', 'STALL', 'timeout')
         ep = self.transaction_ep
         addr = self.transaction_addr
+
+        # Handle protocol STALLs, condition lasts until next SETUP transfer (8.5.3.4)
+        if self.transaction_type == 'SETUP' and (addr, ep) in self.request:
+            request = self.request[(addr,ep)]
+            if request['type'] in ('SETUP IN', 'SETUP OUT'):
+                request['es'] = self.ss_transaction
+                self.handle_request(0, 1)
+
         if not (addr, ep) in self.request:
             self.request[(addr, ep)] = {'setup_data': [], 'data': [],
                 'type': None, 'ss': self.ss_transaction, 'es': None,
@@ -187,16 +199,18 @@ class Decoder(srd.Decoder):
             request_started = 1
         request = self.request[(addr,ep)]
 
+        if request_end:
+            request['es'] = self.es_transaction
+            request['handshake'] = self.handshake
+
         # BULK or INTERRUPT transfer
         if request['type'] in (None, 'BULK IN') and self.transaction_type == 'IN':
             request['type'] = 'BULK IN'
             request['data'] += self.transaction_data
-            request['es'] = self.es_transaction
             self.handle_request(request_started, request_end)
         elif request['type'] in (None, 'BULK OUT') and self.transaction_type == 'OUT':
             request['type'] = 'BULK OUT'
             request['data'] += self.transaction_data
-            request['es'] = self.es_transaction
             self.handle_request(request_started, request_end)
 
         # CONTROL, SETUP stage
@@ -222,11 +236,9 @@ class Decoder(srd.Decoder):
 
         # CONTROL, STATUS stage
         elif request['type'] == 'SETUP IN' and self.transaction_type == 'OUT':
-            request['es'] = self.es_transaction
             self.handle_request(0, request_end)
 
         elif request['type'] == 'SETUP OUT' and self.transaction_type == 'IN':
-            request['es'] = self.es_transaction
             self.handle_request(0, request_end)
 
         else:
@@ -251,7 +263,7 @@ class Decoder(srd.Decoder):
             s += ' ]['
         for b in request['data']:
             s += ' %02X' % b
-        s += ' ] : %s' % self.handshake
+        s += ' ] : %s' % request['handshake']
         return s
 
     def handle_request(self, request_start, request_end):
index 0ad8fdc707d53c6dead64d65200268abbe37f0c1..424117dbe2cb5613744f6f54afd88b7022fa892c 100644 (file)
@@ -100,7 +100,7 @@ class SamplerateError(Exception):
     pass
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'usb_signalling'
     name = 'USB signalling'
     longname = 'Universal Serial Bus (LS/FS) signalling'
@@ -135,6 +135,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.samplerate = None
         self.oldsym = 'J' # The "idle" state is J.
         self.ss_block = None
@@ -145,11 +148,10 @@ class Decoder(srd.Decoder):
         self.samplenum_target = None
         self.samplenum_edge = None
         self.samplenum_lastedge = 0
-        self.oldpins = None
         self.edgepins = None
         self.consecutive_ones = 0
         self.bits = None
-        self.state = 'INIT'
+        self.state = 'IDLE'
 
     def start(self):
         self.out_python = self.register(srd.OUTPUT_PYTHON)
@@ -298,17 +300,21 @@ class Decoder(srd.Decoder):
         self.oldsym = 'J'
         self.state = 'IDLE'
 
-    def decode(self, ss, es, data):
+    def decode(self):
         if not self.samplerate:
             raise SamplerateError('Cannot decode without samplerate.')
-        for (self.samplenum, pins) in data:
+
+        # Seed internal state from the very first sample.
+        pins = self.wait()
+        sym = symbols[self.options['signalling']][pins]
+        self.handle_idle(sym)
+
+        while True:
             # State machine.
             if self.state == 'IDLE':
-                # Ignore identical samples early on (for performance reasons).
-                if self.oldpins == pins:
-                    continue
-                self.oldpins = pins
-                sym = symbols[self.signalling][tuple(pins)]
+                # Wait for any edge on either DP and/or DM.
+                pins = self.wait([{0: 'e'}, {1: 'e'}])
+                sym = symbols[self.signalling][pins]
                 if sym == 'SE0':
                     self.samplenum_lastedge = self.samplenum
                     self.state = 'WAIT IDLE'
@@ -317,28 +323,23 @@ class Decoder(srd.Decoder):
                 self.edgepins = pins
             elif self.state in ('GET BIT', 'GET EOP'):
                 # Wait until we're in the middle of the desired bit.
-                if self.samplenum == self.samplenum_edge:
-                    self.edgepins = pins
-                if self.samplenum < self.samplenum_target:
-                    continue
-                sym = symbols[self.signalling][tuple(pins)]
+                self.edgepins = self.wait([{'skip': self.samplenum_edge - self.samplenum}])
+                pins = self.wait([{'skip': self.samplenum_target - self.samplenum}])
+
+                sym = symbols[self.signalling][pins]
                 if self.state == 'GET BIT':
                     self.get_bit(sym)
                 elif self.state == 'GET EOP':
                     self.get_eop(sym)
-                self.oldpins = pins
             elif self.state == 'WAIT IDLE':
-                if tuple(pins) == (0, 0):
-                    continue
+                # Skip "all-low" input. Wait for high level on either DP or DM.
+                pins = self.wait()
+                while not pins[0] and not pins[1]:
+                    pins = self.wait([{0: 'h'}, {1: 'h'}])
                 if self.samplenum - self.samplenum_lastedge > 1:
-                    sym = symbols[self.options['signalling']][tuple(pins)]
+                    sym = symbols[self.options['signalling']][pins]
                     self.handle_idle(sym)
                 else:
-                    sym = symbols[self.signalling][tuple(pins)]
+                    sym = symbols[self.signalling][pins]
                     self.wait_for_sop(sym)
-                self.oldpins = pins
                 self.edgepins = pins
-            elif self.state == 'INIT':
-                sym = symbols[self.options['signalling']][tuple(pins)]
-                self.handle_idle(sym)
-                self.oldpins = pins
index 7a24fc8650721622270b9ef8cfc781e87736d287..c494789840131fb11d2b083145bc34188032596d 100644 (file)
 
 import sigrokdecode as srd
 
+class SamplerateError(Exception):
+    pass
+
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'wiegand'
     name = 'Wiegand'
     longname = 'Wiegand interface'
@@ -48,6 +51,10 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.samplerate = None
         self._samples_per_bit = 10
 
         self._d0_prev = None
@@ -64,15 +71,17 @@ class Decoder(srd.Decoder):
     def start(self):
         'Register output types and verify user supplied decoder values.'
         self.out_ann = self.register(srd.OUTPUT_ANN)
-        self._active = self.options['active'] == 'high' and 1 or 0
+        self._active = 1 if self.options['active'] == 'high' else 0
         self._inactive = 1 - self._active
 
     def metadata(self, key, value):
         'Receive decoder metadata about the data stream.'
         if key == srd.SRD_CONF_SAMPLERATE:
-            ms_per_sample = 1000 * (1.0 / value)
-            ms_per_bit = float(self.options['bitwidth_ms'])
-            self._samples_per_bit = int(max(1, int(ms_per_bit / ms_per_sample)))
+            self.samplerate = value
+            if self.samplerate:
+                ms_per_sample = 1000 * (1.0 / self.samplerate)
+                ms_per_bit = float(self.options['bitwidth_ms'])
+                self._samples_per_bit = int(max(1, int(ms_per_bit / ms_per_sample)))
 
     def _update_state(self, state, bit=None):
         'Update state and bit values when they change.'
@@ -102,8 +111,13 @@ class Decoder(srd.Decoder):
             self._state = state
             self._bits = []
 
-    def decode(self, ss, es, data):
-        for self.samplenum, (d0, d1) in data:
+    def decode(self):
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
+        while True:
+            # TODO: Come up with more appropriate self.wait() conditions.
+            (d0, d1) = self.wait()
+
             if d0 == self._d0_prev and d1 == self._d1_prev:
                 if self.es_bit and self.samplenum >= self.es_bit:
                     if (d0, d1) == (self._inactive, self._inactive):
index 27135e585a1c267a7990af2dc56469b9222bf1a6..0bc6724511966f8ba43ba8f94076372b40d3209e 100644 (file)
@@ -23,7 +23,7 @@ from common.plugtrx import (MODULE_ID, ALARM_THRESHOLDS, AD_READOUTS, GCS_BITS,
         ENHANCED_OPTS, AUX_TYPES)
 
 class Decoder(srd.Decoder):
-    api_version = 2
+    api_version = 3
     id = 'xfp'
     name = 'XFP'
     longname = '10 Gigabit Small Form Factor Pluggable Module (XFP)'
@@ -37,6 +37,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         # Received data items, used as an index into samplenum/data
         self.cnt = -1
         # Start/end sample numbers per data item
index bd2ec575a95aa4e6ca35b303a925426e014c5df3..b7ff9a56b16ac83cc62bba2e6781876241b306dc 100644 (file)
@@ -111,6 +111,9 @@ class Decoder(srd.Decoder):
     )
 
     def __init__(self):
+        self.reset()
+
+    def reset(self):
         self.prev_cycle = Cycle.NONE
         self.op_state   = self.state_IDLE
 
@@ -132,7 +135,7 @@ class Decoder(srd.Decoder):
     def decode(self):
         while True:
             # TODO: Come up with more appropriate self.wait() conditions.
-            pins = self.wait({'skip': 1})
+            pins = self.wait()
             cycle = Cycle.NONE
             if pins[Pin.MREQ] != 1: # default to asserted
                 if pins[Pin.RD] == 0:
index 7027df5abcdf65b8269323113e1809193ea4b085..b4407588c346be4d3aa77fbc6cff4a7fb8db93ab 100644 (file)
@@ -28,6 +28,8 @@ static char *py_stringify(PyObject *py_obj)
        PyObject *py_str, *py_bytes;
        char *str = NULL;
 
+       /* Note: Caller already ran PyGILState_Ensure(). */
+
        if (!py_obj)
                return NULL;
 
@@ -56,6 +58,8 @@ static char *py_get_string_attr(PyObject *py_obj, const char *attr)
        PyObject *py_str, *py_bytes;
        char *str = NULL;
 
+       /* Note: Caller already ran PyGILState_Ensure(). */
+
        if (!py_obj)
                return NULL;
 
@@ -87,6 +91,7 @@ SRD_PRIV void srd_exception_catch(const char *format, ...)
        PyObject *py_mod, *py_func, *py_tracefmt;
        char *msg, *etype_name, *evalue_str, *tracefmt_str;
        const char *etype_name_fallback;
+       PyGILState_STATE gstate;
 
        py_etype = py_evalue = py_etraceback = py_mod = py_func = NULL;
 
@@ -94,6 +99,8 @@ SRD_PRIV void srd_exception_catch(const char *format, ...)
        msg = g_strdup_vprintf(format, args);
        va_end(args);
 
+       gstate = PyGILState_Ensure();
+
        PyErr_Fetch(&py_etype, &py_evalue, &py_etraceback);
        if (!py_etype) {
                /* No current exception, so just print the message. */
@@ -151,5 +158,7 @@ cleanup:
        /* Just in case. */
        PyErr_Clear();
 
+       PyGILState_Release(gstate);
+
        g_free(msg);
 }
index b716f2fa1fe1b29339558903a1dcc5271f2ac14c..6fbdc01fe7e888c57b43e9ad2a3af6cd5dd2788a 100644 (file)
 
 extern SRD_PRIV GSList *sessions;
 
-/* module_sigrokdecode.c */
-extern SRD_PRIV PyObject *srd_logic_type;
-
 static void srd_inst_join_decode_thread(struct srd_decoder_inst *di);
 static void srd_inst_reset_state(struct srd_decoder_inst *di);
+SRD_PRIV void oldpins_array_seed(struct srd_decoder_inst *di);
 SRD_PRIV void oldpins_array_free(struct srd_decoder_inst *di);
 
 /** @endcond */
@@ -76,6 +74,7 @@ SRD_API int srd_inst_option_set(struct srd_decoder_inst *di,
        gint64 val_int;
        int ret;
        const char *val_str;
+       PyGILState_STATE gstate;
 
        if (!di) {
                srd_err("Invalid decoder instance.");
@@ -87,8 +86,11 @@ SRD_API int srd_inst_option_set(struct srd_decoder_inst *di,
                return SRD_ERR_ARG;
        }
 
+       gstate = PyGILState_Ensure();
+
        if (!PyObject_HasAttrString(di->decoder->py_dec, "options")) {
                /* Decoder has no options. */
+               PyGILState_Release(gstate);
                if (g_hash_table_size(options) == 0) {
                        /* No options provided. */
                        return SRD_OK;
@@ -170,6 +172,7 @@ err_out:
                srd_exception_catch("Stray exception in srd_inst_option_set()");
                ret = SRD_ERR_PYTHON;
        }
+       PyGILState_Release(gstate);
 
        return ret;
 }
@@ -253,14 +256,20 @@ SRD_API int srd_inst_channel_set_all(struct srd_decoder_inst *di,
                }
                pdch = sl->data;
                new_channelmap[pdch->order] = new_channelnum;
-               srd_dbg("Setting channel mapping: %s (index %d) = channel %d.",
+               srd_dbg("Setting channel mapping: %s (PD ch idx %d) = input data ch idx %d.",
                        pdch->id, pdch->order, new_channelnum);
        }
 
        srd_dbg("Final channel map:");
        num_required_channels = g_slist_length(di->decoder->channels);
        for (i = 0; i < di->dec_num_channels; i++) {
-               srd_dbg(" - index %d = channel %d (%s)", i, new_channelmap[i],
+               GSList *l = g_slist_nth(di->decoder->channels, i);
+               if (!l)
+                       l = g_slist_nth(di->decoder->opt_channels,
+                               i - num_required_channels);
+               pdch = l->data;
+               srd_dbg(" - PD ch idx %d (%s) = input data ch idx %d (%s)", i,
+                       pdch->id, new_channelmap[i],
                        (i < num_required_channels) ? "required" : "optional");
        }
 
@@ -300,6 +309,7 @@ SRD_API struct srd_decoder_inst *srd_inst_new(struct srd_session *sess,
        struct srd_decoder *dec;
        struct srd_decoder_inst *di;
        char *inst_id;
+       PyGILState_STATE gstate;
 
        i = 1;
        srd_dbg("Creating new %s instance.", decoder_id);
@@ -354,22 +364,23 @@ SRD_API struct srd_decoder_inst *srd_inst_new(struct srd_session *sess,
        }
 
        /* Default to the initial pins being the same as in sample 0. */
-       di->old_pins_array = g_array_sized_new(FALSE, TRUE, sizeof(uint8_t),
-                                               di->dec_num_channels);
-       g_array_set_size(di->old_pins_array, di->dec_num_channels);
-       memset(di->old_pins_array->data, SRD_INITIAL_PIN_SAME_AS_SAMPLE0,
-               di->dec_num_channels);
+       oldpins_array_seed(di);
+
+       gstate = PyGILState_Ensure();
 
        /* Create a new instance of this decoder class. */
        if (!(di->py_inst = PyObject_CallObject(dec->py_dec, NULL))) {
                if (PyErr_Occurred())
                        srd_exception_catch("Failed to create %s instance",
                                        decoder_id);
+               PyGILState_Release(gstate);
                g_free(di->dec_channelmap);
                g_free(di);
                return NULL;
        }
 
+       PyGILState_Release(gstate);
+
        if (options && srd_inst_option_set(di, options) != SRD_OK) {
                g_free(di->dec_channelmap);
                g_free(di);
@@ -480,7 +491,6 @@ SRD_API int srd_inst_stack(struct srd_session *sess,
                struct srd_decoder_inst *di_bottom,
                struct srd_decoder_inst *di_top)
 {
-
        if (session_is_valid(sess) != SRD_OK) {
                srd_err("Invalid session.");
                return SRD_ERR_ARG;
@@ -665,6 +675,7 @@ SRD_API int srd_inst_initial_pins_set_all(struct srd_decoder_inst *di, GArray *i
        }
 
        s = g_string_sized_new(100);
+       oldpins_array_seed(di);
        for (i = 0; i < di->dec_num_channels; i++) {
                di->old_pins_array->data[i] = initial_pins->data[i];
                g_string_append_printf(s, "%d, ", di->old_pins_array->data[i]);
@@ -676,6 +687,25 @@ SRD_API int srd_inst_initial_pins_set_all(struct srd_decoder_inst *di, GArray *i
        return SRD_OK;
 }
 
+/** @private */
+SRD_PRIV void oldpins_array_seed(struct srd_decoder_inst *di)
+{
+       size_t count;
+       GArray *arr;
+
+       if (!di)
+               return;
+       if (di->old_pins_array)
+               return;
+
+       srd_dbg("%s: Seeding old pins, %s().", di->inst_id, __func__);
+       count = di->dec_num_channels;
+       arr = g_array_sized_new(FALSE, TRUE, sizeof(uint8_t), count);
+       g_array_set_size(arr, count);
+       memset(arr->data, SRD_INITIAL_PIN_SAME_AS_SAMPLE0, count);
+       di->old_pins_array = arr;
+}
+
 /** @private */
 SRD_PRIV void oldpins_array_free(struct srd_decoder_inst *di)
 {
@@ -697,14 +727,18 @@ SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di)
        GSList *l;
        struct srd_decoder_inst *next_di;
        int ret;
+       PyGILState_STATE gstate;
 
        srd_dbg("Calling start() method on protocol decoder instance %s.",
                        di->inst_id);
 
+       gstate = PyGILState_Ensure();
+
        /* Run self.start(). */
        if (!(py_res = PyObject_CallMethod(di->py_inst, "start", NULL))) {
                srd_exception_catch("Protocol decoder instance %s",
                                di->inst_id);
+               PyGILState_Release(gstate);
                return SRD_ERR_PYTHON;
        }
        Py_DecRef(py_res);
@@ -715,6 +749,8 @@ SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di)
        /* Set self.matched to None. */
        PyObject_SetAttrString(di->py_inst, "matched", Py_None);
 
+       PyGILState_Release(gstate);
+
        /* Start all the PDs stacked on top of this one. */
        for (l = di->next_di; l; l = l->next) {
                next_di = l->data;
@@ -743,8 +779,7 @@ SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di)
  */
 static gboolean sample_matches(uint8_t old_sample, uint8_t sample, struct srd_term *term)
 {
-       if (!term)
-               return FALSE;
+       /* Caller ensures term != NULL. */
 
        switch (term->type) {
        case SRD_TERM_HIGH:
@@ -836,6 +871,7 @@ static void update_old_pins_array(struct srd_decoder_inst *di,
        if (!di || !di->dec_channelmap || !sample_pos)
                return;
 
+       oldpins_array_seed(di);
        for (i = 0; i < di->dec_num_channels; i++) {
                byte_offset = di->dec_channelmap[i] / 8;
                bit_offset = di->dec_channelmap[i] % 8;
@@ -855,6 +891,7 @@ static void update_old_pins_array_initial_pins(struct srd_decoder_inst *di)
 
        sample_pos = di->inbuf + ((di->abs_cur_samplenum - di->abs_start_samplenum) * di->data_unitsize);
 
+       oldpins_array_seed(di);
        for (i = 0; i < di->dec_num_channels; i++) {
                if (di->old_pins_array->data[i] != SRD_INITIAL_PIN_SAME_AS_SAMPLE0)
                        continue;
@@ -871,19 +908,16 @@ static gboolean term_matches(const struct srd_decoder_inst *di,
        uint8_t old_sample, sample;
        int byte_offset, bit_offset, ch;
 
-       if (!di || !di->dec_channelmap || !term || !sample_pos)
-               return FALSE;
+       /* Caller ensures di, di->dec_channelmap, term, sample_pos != NULL. */
 
-       /* Overwritten below (or ignored for SRD_TERM_SKIP). */
-       old_sample = sample = 0;
+       if (term->type == SRD_TERM_SKIP)
+               return sample_matches(0, 0, term);
 
-       if (term->type != SRD_TERM_SKIP) {
-               ch = term->channel;
-               byte_offset = di->dec_channelmap[ch] / 8;
-               bit_offset = di->dec_channelmap[ch] % 8;
-               sample = *(sample_pos + byte_offset) & (1 << bit_offset) ? 1 : 0;
-               old_sample = di->old_pins_array->data[ch];
-       }
+       ch = term->channel;
+       byte_offset = di->dec_channelmap[ch] / 8;
+       bit_offset = di->dec_channelmap[ch] % 8;
+       sample = *(sample_pos + byte_offset) & (1 << bit_offset) ? 1 : 0;
+       old_sample = di->old_pins_array->data[ch];
 
        return sample_matches(old_sample, sample, term);
 }
@@ -894,8 +928,7 @@ static gboolean all_terms_match(const struct srd_decoder_inst *di,
        const GSList *l;
        struct srd_term *term;
 
-       if (!di || !cond || !sample_pos)
-               return FALSE;
+       /* Caller ensures di, cond, sample_pos != NULL. */
 
        for (l = cond; l; l = l->next) {
                term = l->data;
@@ -911,8 +944,7 @@ static gboolean at_least_one_condition_matched(
 {
        unsigned int i;
 
-       if (!di)
-               return FALSE;
+       /* Caller ensures di != NULL. */
 
        for (i = 0; i < num_conditions; i++) {
                if (di->match_array->data[i])
@@ -924,12 +956,13 @@ static gboolean at_least_one_condition_matched(
 
 static gboolean find_match(struct srd_decoder_inst *di)
 {
-       static uint64_t s = 0;
        uint64_t i, j, num_samples_to_process;
        GSList *l, *cond;
        const uint8_t *sample_pos;
        unsigned int num_conditions;
 
+       /* Caller ensures di != NULL. */
+
        /* Check whether the condition list is NULL/empty. */
        if (!di->condition_list) {
                srd_dbg("NULL/empty condition list, automatic match.");
@@ -953,7 +986,7 @@ static gboolean find_match(struct srd_decoder_inst *di)
        if (di->abs_cur_samplenum == 0)
                update_old_pins_array_initial_pins(di);
 
-       for (i = 0, s = 0; i < num_samples_to_process; i++, s++, (di->abs_cur_samplenum)++) {
+       for (i = 0; i < num_samples_to_process; i++, (di->abs_cur_samplenum)++) {
 
                sample_pos = di->inbuf + ((di->abs_cur_samplenum - di->abs_start_samplenum) * di->data_unitsize);
 
@@ -1040,6 +1073,7 @@ static gpointer di_thread(gpointer data)
        PyObject *py_res;
        struct srd_decoder_inst *di;
        int wanted_term;
+       PyGILState_STATE gstate;
 
        if (!data)
                return NULL;
@@ -1048,6 +1082,8 @@ static gpointer di_thread(gpointer data)
 
        srd_dbg("%s: Starting thread routine for decoder.", di->inst_id);
 
+       gstate = PyGILState_Ensure();
+
        /*
         * Call self.decode(). Only returns if the PD throws an exception.
         * "Regular" termination of the decode() method is not expected.
@@ -1083,6 +1119,7 @@ static gpointer di_thread(gpointer data)
                 */
                srd_dbg("%s: Thread done (!res, want_term).", di->inst_id);
                PyErr_Clear();
+               PyGILState_Release(gstate);
                return NULL;
        }
        if (!py_res) {
@@ -1094,6 +1131,7 @@ static gpointer di_thread(gpointer data)
                srd_dbg("%s: decode() terminated unrequested.", di->inst_id);
                srd_exception_catch("Protocol decoder instance %s: ", di->inst_id);
                srd_dbg("%s: Thread done (!res, !want_term).", di->inst_id);
+               PyGILState_Release(gstate);
                return NULL;
        }
 
@@ -1106,6 +1144,8 @@ static gpointer di_thread(gpointer data)
        Py_DecRef(py_res);
        PyErr_Clear();
 
+       PyGILState_Release(gstate);
+
        srd_dbg("%s: Thread done (with res).", di->inst_id);
 
        return NULL;
@@ -1165,10 +1205,6 @@ SRD_PRIV int srd_inst_decode(struct srd_decoder_inst *di,
                uint64_t abs_start_samplenum, uint64_t abs_end_samplenum,
                const uint8_t *inbuf, uint64_t inbuflen, uint64_t unitsize)
 {
-       PyObject *py_res;
-       srd_logic *logic;
-       long apiver;
-
        /* Return an error upon unusable input. */
        if (!di) {
                srd_dbg("empty decoder instance");
@@ -1203,60 +1239,96 @@ SRD_PRIV int srd_inst_decode(struct srd_decoder_inst *di,
                abs_end_samplenum - abs_start_samplenum, inbuflen, di->data_unitsize,
                di->inst_id);
 
-       apiver = srd_decoder_apiver(di->decoder);
+       /* If this is the first call, start the worker thread. */
+       if (!di->thread_handle) {
+               srd_dbg("No worker thread for this decoder stack "
+                       "exists yet, creating one: %s.", di->inst_id);
+               di->thread_handle = g_thread_new(di->inst_id,
+                                                di_thread, di);
+       }
 
-       if (apiver == 2) {
-               /*
-                * Create new srd_logic object. Each iteration around the PD's
-                * loop will fill one sample into this object.
-                */
-               logic = PyObject_New(srd_logic, (PyTypeObject *)srd_logic_type);
-               Py_INCREF(logic);
-               logic->di = (struct srd_decoder_inst *)di;
-               logic->abs_start_samplenum = abs_start_samplenum;
-               logic->itercnt = 0;
-               logic->inbuf = (uint8_t *)inbuf;
-               logic->inbuflen = inbuflen;
-               logic->sample = PyList_New(2);
-               Py_INCREF(logic->sample);
-
-               Py_IncRef(di->py_inst);
-               if (!(py_res = PyObject_CallMethod(di->py_inst, "decode",
-                       "KKO", abs_start_samplenum, abs_end_samplenum, logic))) {
-                       srd_exception_catch("Protocol decoder instance %s",
-                                       di->inst_id);
-                       return SRD_ERR_PYTHON;
-               }
-               di->abs_cur_samplenum = abs_end_samplenum;
-               Py_DecRef(py_res);
-       } else {
-               /* If this is the first call, start the worker thread. */
-               if (!di->thread_handle) {
-                       srd_dbg("No worker thread for this decoder stack "
-                               "exists yet, creating one: %s.", di->inst_id);
-                       di->thread_handle = g_thread_new(di->inst_id,
-                                                        di_thread, di);
-               }
+       /* Push the new sample chunk to the worker thread. */
+       g_mutex_lock(&di->data_mutex);
+       di->abs_start_samplenum = abs_start_samplenum;
+       di->abs_end_samplenum = abs_end_samplenum;
+       di->inbuf = inbuf;
+       di->inbuflen = inbuflen;
+       di->got_new_samples = TRUE;
+       di->handled_all_samples = FALSE;
+       di->want_wait_terminate = FALSE;
 
-               /* Push the new sample chunk to the worker thread. */
-               g_mutex_lock(&di->data_mutex);
-               di->abs_start_samplenum = abs_start_samplenum;
-               di->abs_end_samplenum = abs_end_samplenum;
-               di->inbuf = inbuf;
-               di->inbuflen = inbuflen;
-               di->got_new_samples = TRUE;
-               di->handled_all_samples = FALSE;
-               di->want_wait_terminate = FALSE;
-
-               /* Signal the thread that we have new data. */
-               g_cond_signal(&di->got_new_samples_cond);
-               g_mutex_unlock(&di->data_mutex);
-
-               /* When all samples in this chunk were handled, return. */
-               g_mutex_lock(&di->data_mutex);
-               while (!di->handled_all_samples && !di->want_wait_terminate)
-                       g_cond_wait(&di->handled_all_samples_cond, &di->data_mutex);
-               g_mutex_unlock(&di->data_mutex);
+       /* Signal the thread that we have new data. */
+       g_cond_signal(&di->got_new_samples_cond);
+       g_mutex_unlock(&di->data_mutex);
+
+       /* When all samples in this chunk were handled, return. */
+       g_mutex_lock(&di->data_mutex);
+       while (!di->handled_all_samples && !di->want_wait_terminate)
+               g_cond_wait(&di->handled_all_samples_cond, &di->data_mutex);
+       g_mutex_unlock(&di->data_mutex);
+
+       return SRD_OK;
+}
+
+/**
+ * Terminate current decoder work, prepare for re-use on new input data.
+ *
+ * Terminates all decoder operations in the specified decoder instance
+ * and the instances stacked on top of it. Resets internal state such
+ * that the previously constructed stack can process new input data that
+ * is not related to previously processed input data. This avoids the
+ * expensive and complex re-construction of decoder stacks.
+ *
+ * Callers are expected to follow up with start, metadata, and decode
+ * calls like they would for newly constructed decoder stacks.
+ *
+ * @param di The decoder instance to call. Must not be NULL.
+ * @return SRD_OK upon success, a (negative) error code otherwise.
+ * @private
+ */
+SRD_PRIV int srd_inst_terminate_reset(struct srd_decoder_inst *di)
+{
+       PyGILState_STATE gstate;
+       PyObject *py_ret;
+       GSList *l;
+       int ret;
+
+       if (!di)
+               return SRD_ERR_ARG;
+
+       /*
+        * Request termination and wait for previously initiated
+        * background operation to finish. Reset internal state, but
+        * do not start releasing resources yet. This shall result in
+        * decoders' state just like after creation. This block handles
+        * the C language library side.
+        */
+       srd_dbg("Terminating instance %s", di->inst_id);
+       srd_inst_join_decode_thread(di);
+       srd_inst_reset_state(di);
+
+       /*
+        * Have the Python side's .reset() method executed (if the PD
+        * implements it). It's assumed that .reset() assigns variables
+        * very much like __init__() used to do in the past. Thus memory
+        * that was allocated in previous calls gets released by Python
+        * as it's not referenced any longer.
+        */
+       gstate = PyGILState_Ensure();
+       if (PyObject_HasAttrString(di->py_inst, "reset")) {
+               srd_dbg("Calling .reset() of instance %s", di->inst_id);
+               py_ret = PyObject_CallMethod(di->py_inst, "reset", NULL);
+               Py_XDECREF(py_ret);
+       }
+       PyGILState_Release(gstate);
+
+       /*
+        * Pass the "restart" request to all stacked decoders.
+        */
+       for (l = di->next_di; l; l = l->next) {
+               ret = srd_inst_terminate_reset(l->data);
+               if (ret != SRD_OK)
+                       return ret;
        }
 
        return SRD_OK;
@@ -1267,13 +1339,18 @@ SRD_PRIV void srd_inst_free(struct srd_decoder_inst *di)
 {
        GSList *l;
        struct srd_pd_output *pdo;
+       PyGILState_STATE gstate;
 
        srd_dbg("Freeing instance %s", di->inst_id);
 
        srd_inst_join_decode_thread(di);
+
        srd_inst_reset_state(di);
 
+       gstate = PyGILState_Ensure();
        Py_DecRef(di->py_inst);
+       PyGILState_Release(gstate);
+
        g_free(di->inst_id);
        g_free(di->dec_channelmap);
        g_free(di->channel_samples);
index eb792870b8f0e329b778236c8696286e5c293e5c..6fb590cff7ce8d4c5d1139aee9ca8b7eee44214e 100644 (file)
@@ -84,6 +84,7 @@ SRD_PRIV int srd_inst_decode(struct srd_decoder_inst *di,
                uint64_t abs_start_samplenum, uint64_t abs_end_samplenum,
                const uint8_t *inbuf, uint64_t inbuflen, uint64_t unitsize);
 SRD_PRIV int process_samples_until_condition_match(struct srd_decoder_inst *di, gboolean *found_match);
+SRD_PRIV int srd_inst_terminate_reset(struct srd_decoder_inst *di);
 SRD_PRIV void srd_inst_free(struct srd_decoder_inst *di);
 SRD_PRIV void srd_inst_free_all(struct srd_session *sess);
 
index d37c432a537c9c5a8cbacbc6b4e27674eec69149..ee610c939d3398ded1e068953fb3edec0c95eab7 100644 (file)
@@ -320,6 +320,7 @@ struct srd_pd_callback {
 /* srd.c */
 SRD_API int srd_init(const char *path);
 SRD_API int srd_exit(void);
+SRD_API GSList *srd_searchpaths_get(void);
 
 /* session.c */
 SRD_API int srd_session_new(struct srd_session **sess);
@@ -329,6 +330,7 @@ SRD_API int srd_session_metadata_set(struct srd_session *sess, int key,
 SRD_API int srd_session_send(struct srd_session *sess,
                uint64_t abs_start_samplenum, uint64_t abs_end_samplenum,
                const uint8_t *inbuf, uint64_t inbuflen, uint64_t unitsize);
+SRD_API int srd_session_terminate_reset(struct srd_session *sess);
 SRD_API int srd_session_destroy(struct srd_session *sess);
 SRD_API int srd_pd_output_callback_add(struct srd_session *sess,
                int output_type, srd_pd_output_callback cb, void *cb_data);
index 17563c1d85d4571e362b5b71a3c5868a36d8c488..ab5df19e3db27cde47cf1cc943a83ccbd4d51aa6 100644 (file)
@@ -23,8 +23,6 @@
 
 /** @cond PRIVATE */
 
-SRD_PRIV PyObject *srd_logic_type = NULL;
-
 /*
  * When initialized, a reference to this module inside the Python interpreter
  * lives here.
@@ -43,7 +41,10 @@ static struct PyModuleDef sigrokdecode_module = {
 /** @cond PRIVATE */
 PyMODINIT_FUNC PyInit_sigrokdecode(void)
 {
-       PyObject *mod, *Decoder_type, *logic_type;
+       PyObject *mod, *Decoder_type;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
 
        mod = PyModule_Create(&sigrokdecode_module);
        if (!mod)
@@ -55,12 +56,6 @@ PyMODINIT_FUNC PyInit_sigrokdecode(void)
        if (PyModule_AddObject(mod, "Decoder", Decoder_type) < 0)
                goto err_out;
 
-       logic_type = srd_logic_type_new();
-       if (!logic_type)
-               goto err_out;
-       if (PyModule_AddObject(mod, "srd_logic", logic_type) < 0)
-               goto err_out;
-
        /* Expose output types as symbols in the sigrokdecode module */
        if (PyModule_AddIntConstant(mod, "OUTPUT_ANN", SRD_OUTPUT_ANN) < 0)
                goto err_out;
@@ -74,13 +69,15 @@ PyMODINIT_FUNC PyInit_sigrokdecode(void)
        if (PyModule_AddIntConstant(mod, "SRD_CONF_SAMPLERATE", SRD_CONF_SAMPLERATE) < 0)
                goto err_out;
 
-       srd_logic_type = logic_type;
        mod_sigrokdecode = mod;
 
+       PyGILState_Release(gstate);
+
        return mod;
 err_out:
        Py_XDECREF(mod);
        srd_exception_catch("Failed to initialize module");
+       PyGILState_Release(gstate);
 
        return NULL;
 }
index 128d2240301e8de52eb788a14cf8f4fffb35f8b7..4d794df4b97a5a1d44cf99e79f80b253cbfd3d2c 100644 (file)
--- a/session.c
+++ b/session.c
@@ -131,11 +131,14 @@ static int srd_inst_send_meta(struct srd_decoder_inst *di, int key,
        GSList *l;
        struct srd_decoder_inst *next_di;
        int ret;
+       PyGILState_STATE gstate;
 
        if (key != SRD_CONF_SAMPLERATE)
                /* This is the only key we pass on to the decoder for now. */
                return SRD_OK;
 
+       gstate = PyGILState_Ensure();
+
        if (PyObject_HasAttrString(di->py_inst, "metadata")) {
                py_ret = PyObject_CallMethod(di->py_inst, "metadata", "lK",
                                (long)SRD_CONF_SAMPLERATE,
@@ -143,6 +146,8 @@ static int srd_inst_send_meta(struct srd_decoder_inst *di, int key,
                Py_XDECREF(py_ret);
        }
 
+       PyGILState_Release(gstate);
+
        /* Push metadata to all the PDs stacked on top of this one. */
        for (l = di->next_di; l; l = l->next) {
                next_di = l->data;
@@ -199,7 +204,7 @@ SRD_API int srd_session_metadata_set(struct srd_session *sess, int key,
                return SRD_ERR_ARG;
        }
 
-       srd_dbg("Setting session %d samplerate to %"PRIu64".",
+       srd_dbg("Setting session %d samplerate to %"G_GUINT64_FORMAT".",
                        sess->session_id, g_variant_get_uint64(data));
 
        ret = SRD_OK;
@@ -292,6 +297,45 @@ SRD_API int srd_session_send(struct srd_session *sess,
        return SRD_OK;
 }
 
+/**
+ * Terminate currently executing decoders in a session, reset internal state.
+ *
+ * All decoder instances have their .wait() method terminated, which
+ * shall terminate .decode() as well. Afterwards the decoders' optional
+ * .reset() method gets executed.
+ *
+ * This routine allows callers to abort pending expensive operations,
+ * when they are no longer interested in the decoders' results. Note
+ * that the decoder state is lost and aborted work cannot resume.
+ *
+ * This routine also allows callers to re-use previously created decoder
+ * stacks to process new input data which is not related to previously
+ * processed input data. This avoids the necessity to re-construct the
+ * decoder stack.
+ *
+ * @param sess The session in which to terminate decoders.
+ * @return SRD_OK upon success, a (negative) error code otherwise.
+ *
+ * @since 0.6.0
+ */
+SRD_API int srd_session_terminate_reset(struct srd_session *sess)
+{
+       GSList *d;
+       int ret;
+
+       if (session_is_valid(sess) != SRD_OK) {
+               srd_err("Invalid session.");
+               return SRD_ERR_ARG;
+       }
+
+       for (d = sess->di_list; d; d = d->next) {
+               ret = srd_inst_terminate_reset(d->data);
+               if (ret != SRD_OK)
+                       return ret;
+       }
+       return SRD_OK;
+}
+
 /**
  * Destroy a decoding session.
  *
diff --git a/srd.c b/srd.c
index e8fd7513e7b14a3679d4541279d2087045c3c93c..bed803198c99bb339db52c5da93d9b399ef989b9 100644 (file)
--- a/srd.c
+++ b/srd.c
@@ -144,6 +144,55 @@ static void print_versions(void)
        g_free(str);
 }
 
+static int print_searchpaths(void)
+{
+       PyObject *py_paths, *py_path, *py_bytes;
+       PyGILState_STATE gstate;
+       GString *s;
+       GSList *l;
+       int i;
+
+       s = g_string_sized_new(500);
+       g_string_append(s, "Protocol decoder search paths:\n");
+       for (l = searchpaths; l; l = l->next)
+               g_string_append_printf(s, " - %s\n", (const char *)l->data);
+       s->str[s->len - 1] = '\0';
+       srd_dbg("%s", s->str);
+       g_string_free(s, TRUE);
+
+       gstate = PyGILState_Ensure();
+
+       py_paths = PySys_GetObject("path");
+       if (!py_paths)
+               goto err;
+
+       s = g_string_sized_new(500);
+       g_string_append(s, "Python system search paths:\n");
+       for (i = 0; i < PyList_Size(py_paths); i++) {
+               py_path = PyList_GetItem(py_paths, i);
+               py_bytes = PyUnicode_AsUTF8String(py_path);
+               g_string_append_printf(s, " - %s\n", PyBytes_AsString(py_bytes));
+       }
+       s->str[s->len - 1] = '\0';
+       srd_dbg("%s", s->str);
+       g_string_free(s, TRUE);
+
+       PyGILState_Release(gstate);
+
+       return SRD_OK;
+
+err:
+       srd_err("Unable to query Python system search paths.");
+       PyGILState_Release(gstate);
+
+       return SRD_ERR_PYTHON;
+}
+
+SRD_API GSList *srd_searchpaths_get(void)
+{
+       return g_slist_copy_deep(searchpaths, (GCopyFunc)g_strdup, NULL);
+}
+
 /**
  * Initialize libsigrokdecode.
  *
@@ -232,8 +281,16 @@ SRD_API int srd_init(const char *path)
                }
        }
 
+       /* Initialize the Python GIL (this also happens to acquire it). */
+       PyEval_InitThreads();
+
+       /* Release the GIL (ignore return value, we don't need it here). */
+       (void)PyEval_SaveThread();
+
        max_session_id = 0;
 
+       print_searchpaths();
+
        return SRD_OK;
 }
 
@@ -261,9 +318,18 @@ SRD_API int srd_exit(void)
        g_slist_free_full(searchpaths, g_free);
        searchpaths = NULL;
 
+       /*
+        * Acquire the GIL, otherwise Py_Finalize() might have issues.
+        * Ignore the return value, we don't need it here.
+        */
+       if (Py_IsInitialized())
+               (void)PyGILState_Ensure();
+
        /* Py_Finalize() returns void, any finalization errors are ignored. */
        Py_Finalize();
 
+       /* Note: No need to release the GIL since Python is shut down now. */
+
        max_session_id = -1;
 
        return SRD_OK;
@@ -291,28 +357,38 @@ SRD_API int srd_exit(void)
 SRD_PRIV int srd_decoder_searchpath_add(const char *path)
 {
        PyObject *py_cur_path, *py_item;
+       PyGILState_STATE gstate;
 
        srd_dbg("Adding '%s' to module path.", path);
 
+       gstate = PyGILState_Ensure();
+
        py_cur_path = PySys_GetObject("path");
        if (!py_cur_path)
-               return SRD_ERR_PYTHON;
+               goto err;
 
        py_item = PyUnicode_FromString(path);
        if (!py_item) {
                srd_exception_catch("Failed to create Unicode object");
-               return SRD_ERR_PYTHON;
+               goto err;
        }
        if (PyList_Insert(py_cur_path, 0, py_item) < 0) {
                srd_exception_catch("Failed to insert path element");
                Py_DECREF(py_item);
-               return SRD_ERR_PYTHON;
+               goto err;
        }
        Py_DECREF(py_item);
 
+       PyGILState_Release(gstate);
+
        searchpaths = g_slist_prepend(searchpaths, g_strdup(path));
 
        return SRD_OK;
+
+err:
+       PyGILState_Release(gstate);
+
+       return SRD_ERR_PYTHON;
 }
 
 /** @} */
index f9e42dfcba6f3dca71951325a2bad5bda45319aa..e79496e576da56e949c80b75d68ae26c9e444b40 100644 (file)
@@ -224,6 +224,36 @@ START_TEST(test_session_metadata_set_bogus)
 }
 END_TEST
 
+/*
+ * Check whether srd_session_terminate_reset() succeeds on newly created
+ * sessions, as well as after calling start() and meta(). No data is fed
+ * to decoders here.
+ */
+START_TEST(test_session_reset_nodata)
+{
+       struct srd_session *sess;
+       int ret;
+       GVariant *data;
+
+       srd_init(NULL);
+       srd_session_new(&sess);
+       ret = srd_session_terminate_reset(sess);
+       fail_unless(ret == SRD_OK, "srd_session_terminate_reset() failed: %d.", ret);
+       ret = srd_session_start(sess);
+       fail_unless(ret == SRD_OK, "srd_session_start() failed: %d.", ret);
+       ret = srd_session_terminate_reset(sess);
+       fail_unless(ret == SRD_OK, "srd_session_terminate_reset() failed: %d.", ret);
+       data = g_variant_new_uint64(1000000);
+       ret = srd_session_metadata_set(sess, SRD_CONF_SAMPLERATE, data);
+       fail_unless(ret == SRD_OK, "srd_session_metadata_set() failed: %d.", ret);
+       ret = srd_session_terminate_reset(sess);
+       fail_unless(ret == SRD_OK, "srd_session_terminate_reset() failed: %d.", ret);
+       ret = srd_session_destroy(sess);
+       fail_unless(ret == SRD_OK, "srd_session_destroy() failed: %d.", ret);
+       srd_exit();
+}
+END_TEST
+
 Suite *suite_session(void)
 {
        Suite *s;
@@ -246,5 +276,9 @@ Suite *suite_session(void)
        tcase_add_test(tc, test_session_metadata_set_bogus);
        suite_add_tcase(s, tc);
 
+       tc = tcase_create("reset");
+       tcase_add_test(tc, test_session_reset_nodata);
+       suite_add_tcase(s, tc);
+
        return s;
 }
index 9ed3339d418593507b61c649f3a8f09fc4a3a127..06842f9e9069230a44b03fef24e8cc5838a341c3 100644 (file)
@@ -48,12 +48,15 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj,
        struct srd_proto_data_annotation *pda;
        int ann_class;
        char **ann_text;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
 
        /* Should be a list of [annotation class, [string, ...]]. */
        if (!PyList_Check(obj)) {
                srd_err("Protocol decoder %s submitted an annotation that"
                        " is not a list", di->decoder->name);
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        /* Should have 2 elements. */
@@ -61,7 +64,7 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj,
                srd_err("Protocol decoder %s submitted annotation list with "
                        "%zd elements instead of 2", di->decoder->name,
                        PyList_Size(obj));
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        /*
@@ -72,13 +75,13 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj,
        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;
+               goto err;
        }
        ann_class = PyLong_AsLong(py_tmp);
        if (!(pdo = g_slist_nth_data(di->decoder->annotations, ann_class))) {
                srd_err("Protocol decoder %s submitted data to unregistered "
                        "annotation class %d.", di->decoder->name, ann_class);
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        /* Second element must be a list. */
@@ -86,12 +89,12 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj,
        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;
+               goto err;
        }
        if (py_strseq_to_char(py_tmp, &ann_text) != SRD_OK) {
                srd_err("Protocol decoder %s submitted annotation list, but "
                        "second element was malformed.", di->decoder->name);
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        pda = g_malloc(sizeof(struct srd_proto_data_annotation));
@@ -99,7 +102,14 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj,
        pda->ann_text = ann_text;
        pdata->data = pda;
 
+       PyGILState_Release(gstate);
+
        return SRD_OK;
+
+err:
+       PyGILState_Release(gstate);
+
+       return SRD_ERR_PYTHON;
 }
 
 static int convert_binary(struct srd_decoder_inst *di, PyObject *obj,
@@ -110,12 +120,15 @@ static int convert_binary(struct srd_decoder_inst *di, PyObject *obj,
        Py_ssize_t size;
        int bin_class;
        char *class_name, *buf;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
 
        /* Should be a list of [binary class, bytes]. */
        if (!PyList_Check(obj)) {
                srd_err("Protocol decoder %s submitted non-list for SRD_OUTPUT_BINARY.",
                        di->decoder->name);
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        /* Should have 2 elements. */
@@ -123,7 +136,7 @@ static int convert_binary(struct srd_decoder_inst *di, PyObject *obj,
                srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list "
                                "with %zd elements instead of 2", di->decoder->name,
                                PyList_Size(obj));
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        /* The first element should be an integer. */
@@ -131,13 +144,13 @@ static int convert_binary(struct srd_decoder_inst *di, PyObject *obj,
        if (!PyLong_Check(py_tmp)) {
                srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list, "
                        "but first element was not an integer.", di->decoder->name);
-               return SRD_ERR_PYTHON;
+               goto err;
        }
        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;
+               goto err;
        }
 
        /* Second element should be bytes. */
@@ -145,19 +158,22 @@ static int convert_binary(struct srd_decoder_inst *di, PyObject *obj,
        if (!PyBytes_Check(py_tmp)) {
                srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list, "
                        "but second element was not bytes.", di->decoder->name);
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        /* 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;
+               goto err;
        }
 
        pdb = g_malloc(sizeof(struct srd_proto_data_binary));
        if (PyBytes_AsStringAndSize(py_tmp, &buf, &size) == -1)
-               return SRD_ERR_PYTHON;
+               goto err;
+
+       PyGILState_Release(gstate);
+
        pdb->bin_class = bin_class;
        pdb->size = size;
        if (!(pdb->data = g_try_malloc(pdb->size)))
@@ -166,36 +182,51 @@ static int convert_binary(struct srd_decoder_inst *di, PyObject *obj,
        pdata->data = pdb;
 
        return SRD_OK;
+
+err:
+       PyGILState_Release(gstate);
+
+       return SRD_ERR_PYTHON;
 }
 
 static int convert_meta(struct srd_proto_data *pdata, PyObject *obj)
 {
        long long intvalue;
        double dvalue;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
 
        if (pdata->pdo->meta_type == G_VARIANT_TYPE_INT64) {
                if (!PyLong_Check(obj)) {
                        PyErr_Format(PyExc_TypeError, "This output was registered "
                                        "as 'int', but something else was passed.");
-                       return SRD_ERR_PYTHON;
+                       goto err;
                }
                intvalue = PyLong_AsLongLong(obj);
                if (PyErr_Occurred())
-                       return SRD_ERR_PYTHON;
+                       goto err;
                pdata->data = g_variant_new_int64(intvalue);
        } else if (pdata->pdo->meta_type == G_VARIANT_TYPE_DOUBLE) {
                if (!PyFloat_Check(obj)) {
                        PyErr_Format(PyExc_TypeError, "This output was registered "
                                        "as 'float', but something else was passed.");
-                       return SRD_ERR_PYTHON;
+                       goto err;
                }
                dvalue = PyFloat_AsDouble(obj);
                if (PyErr_Occurred())
-                       return SRD_ERR_PYTHON;
+                       goto err;
                pdata->data = g_variant_new_double(dvalue);
        }
 
+       PyGILState_Release(gstate);
+
        return SRD_OK;
+
+err:
+       PyGILState_Release(gstate);
+
+       return SRD_ERR_PYTHON;
 }
 
 static PyObject *Decoder_put(PyObject *self, PyObject *args)
@@ -204,15 +235,18 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args)
        PyObject *py_data, *py_res;
        struct srd_decoder_inst *di, *next_di;
        struct srd_pd_output *pdo;
-       struct srd_proto_data *pdata;
+       struct srd_proto_data pdata;
        uint64_t start_sample, end_sample;
        int output_id;
        struct srd_pd_callback *cb;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
 
        if (!(di = srd_inst_find_by_obj(NULL, self))) {
                /* Shouldn't happen. */
                srd_dbg("put(): self instance not found.");
-               return NULL;
+               goto err;
        }
 
        if (!PyArg_ParseTuple(args, "KKiO", &start_sample, &end_sample,
@@ -222,13 +256,13 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args)
                 * Python raise it. This results in a much better trace in
                 * controller.c on the decode() method call.
                 */
-               return NULL;
+               goto err;
        }
 
        if (!(l = g_slist_nth(di->pd_output, output_id))) {
                srd_err("Protocol decoder %s submitted invalid output ID %d.",
                        di->decoder->name, output_id);
-               return NULL;
+               goto err;
        }
        pdo = l->data;
 
@@ -236,21 +270,23 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args)
                 di->inst_id, start_sample, end_sample,
                 output_type_name(pdo->output_type), output_id);
 
-       pdata = g_malloc0(sizeof(struct srd_proto_data));
-       pdata->start_sample = start_sample;
-       pdata->end_sample = end_sample;
-       pdata->pdo = pdo;
+       pdata.start_sample = start_sample;
+       pdata.end_sample = end_sample;
+       pdata.pdo = pdo;
+       pdata.data = NULL;
 
        switch (pdo->output_type) {
        case SRD_OUTPUT_ANN:
                /* Annotations are only fed to callbacks. */
                if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) {
                        /* Convert from PyDict to srd_proto_data_annotation. */
-                       if (convert_annotation(di, py_data, pdata) != SRD_OK) {
+                       if (convert_annotation(di, py_data, &pdata) != SRD_OK) {
                                /* An error was already logged. */
                                break;
                        }
-                       cb->cb(pdata, cb->cb_data);
+                       Py_BEGIN_ALLOW_THREADS
+                       cb->cb(&pdata, cb->cb_data);
+                       Py_END_ALLOW_THREADS
                }
                break;
        case SRD_OUTPUT_PYTHON:
@@ -269,28 +305,32 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args)
                if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) {
                        /* Frontends aren't really supposed to get Python
                         * callbacks, but it's useful for testing. */
-                       pdata->data = py_data;
-                       cb->cb(pdata, cb->cb_data);
+                       pdata.data = py_data;
+                       cb->cb(&pdata, cb->cb_data);
                }
                break;
        case SRD_OUTPUT_BINARY:
                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) {
+                       if (convert_binary(di, py_data, &pdata) != SRD_OK) {
                                /* An error was already logged. */
                                break;
                        }
-                       cb->cb(pdata, cb->cb_data);
+                       Py_BEGIN_ALLOW_THREADS
+                       cb->cb(&pdata, cb->cb_data);
+                       Py_END_ALLOW_THREADS
                }
                break;
        case SRD_OUTPUT_META:
                if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) {
                        /* Annotations need converting from PyObject. */
-                       if (convert_meta(pdata, py_data) != SRD_OK) {
+                       if (convert_meta(&pdata, py_data) != SRD_OK) {
                                /* An exception was already set up. */
                                break;
                        }
-                       cb->cb(pdata, cb->cb_data);
+                       Py_BEGIN_ALLOW_THREADS
+                       cb->cb(&pdata, cb->cb_data);
+                       Py_END_ALLOW_THREADS
                }
                break;
        default:
@@ -299,9 +339,14 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args)
                break;
        }
 
-       g_free(pdata);
+       PyGILState_Release(gstate);
 
        Py_RETURN_NONE;
+
+err:
+       PyGILState_Release(gstate);
+
+       return NULL;
 }
 
 static PyObject *Decoder_register(PyObject *self, PyObject *args,
@@ -315,6 +360,12 @@ static PyObject *Decoder_register(PyObject *self, PyObject *args,
        int output_type;
        char *proto_id, *meta_name, *meta_descr;
        char *keywords[] = {"output_type", "proto_id", "meta", NULL};
+       PyGILState_STATE gstate;
+       gboolean is_meta;
+       GSList *l;
+       struct srd_pd_output *cmp;
+
+       gstate = PyGILState_Ensure();
 
        meta_type_py = NULL;
        meta_type_gv = NULL;
@@ -322,7 +373,7 @@ static PyObject *Decoder_register(PyObject *self, PyObject *args,
 
        if (!(di = srd_inst_find_by_obj(NULL, self))) {
                PyErr_SetString(PyExc_Exception, "decoder instance not found");
-               return NULL;
+               goto err;
        }
 
        /* Default to instance id, which defaults to class id. */
@@ -331,21 +382,46 @@ static PyObject *Decoder_register(PyObject *self, PyObject *args,
                        &output_type, &proto_id,
                        &meta_type_py, &meta_name, &meta_descr)) {
                /* Let Python raise this exception. */
-               return NULL;
+               goto err;
        }
 
        /* Check if the meta value's type is supported. */
-       if (output_type == SRD_OUTPUT_META) {
+       is_meta = output_type == SRD_OUTPUT_META;
+       if (is_meta) {
                if (meta_type_py == &PyLong_Type)
                        meta_type_gv = G_VARIANT_TYPE_INT64;
                else if (meta_type_py == &PyFloat_Type)
                        meta_type_gv = G_VARIANT_TYPE_DOUBLE;
                else {
                        PyErr_Format(PyExc_TypeError, "Unsupported type.");
-                       return NULL;
+                       goto err;
                }
        }
 
+       srd_dbg("Instance %s checking registration type %d for %s.",
+               di->inst_id, output_type, proto_id);
+       pdo = NULL;
+       for (l = di->pd_output; l; l = l->next) {
+               cmp = l->data;
+               if (cmp->output_type != output_type)
+                       continue;
+               if (strcmp(cmp->proto_id, proto_id) != 0)
+                       continue;
+               if (is_meta && cmp->meta_type != meta_type_gv)
+                       continue;
+               if (is_meta && strcmp(cmp->meta_name, meta_name) != 0)
+                       continue;
+               if (is_meta && strcmp(cmp->meta_descr, meta_descr) != 0)
+                       continue;
+               pdo = cmp;
+               break;
+       }
+       if (pdo) {
+               py_new_output_id = Py_BuildValue("i", pdo->pdo_id);
+               PyGILState_Release(gstate);
+               return py_new_output_id;
+       }
+
        srd_dbg("Instance %s creating new output type %d for %s.",
                di->inst_id, output_type, proto_id);
 
@@ -366,7 +442,14 @@ static PyObject *Decoder_register(PyObject *self, PyObject *args,
        di->pd_output = g_slist_append(di->pd_output, pdo);
        py_new_output_id = Py_BuildValue("i", pdo->pdo_id);
 
+       PyGILState_Release(gstate);
+
        return py_new_output_id;
+
+err:
+       PyGILState_Release(gstate);
+
+       return NULL;
 }
 
 static int get_term_type(const char *v)
@@ -405,9 +488,13 @@ static PyObject *get_current_pinvalues(const struct srd_decoder_inst *di)
        const uint8_t *sample_pos;
        int byte_offset, bit_offset;
        PyObject *py_pinvalues;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
 
        if (!di) {
                srd_err("Invalid decoder instance.");
+               PyGILState_Release(gstate);
                return NULL;
        }
 
@@ -427,6 +514,8 @@ static PyObject *get_current_pinvalues(const struct srd_decoder_inst *di)
                }
        }
 
+       PyGILState_Release(gstate);
+
        return py_pinvalues;
 }
 
@@ -448,6 +537,7 @@ static int create_term_list(PyObject *py_dict, GSList **term_list)
        struct srd_term *term;
        uint64_t num_samples_to_skip;
        char *term_str;
+       PyGILState_STATE gstate;
 
        if (!py_dict || !term_list)
                return SRD_ERR_ARG;
@@ -455,6 +545,8 @@ static int create_term_list(PyObject *py_dict, GSList **term_list)
        /* "Create" an empty GSList of terms. */
        *term_list = NULL;
 
+       gstate = PyGILState_Ensure();
+
        /* Iterate over all items in the current dict. */
        while (PyDict_Next(py_dict, &pos, &py_key, &py_value)) {
                /* Check whether the current key is a string or a number. */
@@ -464,7 +556,7 @@ static int create_term_list(PyObject *py_dict, GSList **term_list)
                        /* Get the value string. */
                        if ((py_pydictitem_as_str(py_dict, py_key, &term_str)) != SRD_OK) {
                                srd_err("Failed to get the value.");
-                               return SRD_ERR;
+                               goto err;
                        }
                        term = g_malloc0(sizeof(struct srd_term));
                        term->type = get_term_type(term_str);
@@ -475,7 +567,7 @@ static int create_term_list(PyObject *py_dict, GSList **term_list)
                        /* TODO: Check if it's "skip". */
                        if ((py_pydictitem_as_long(py_dict, py_key, &num_samples_to_skip)) != SRD_OK) {
                                srd_err("Failed to get number of samples to skip.");
-                               return SRD_ERR;
+                               goto err;
                        }
                        term = g_malloc0(sizeof(struct srd_term));
                        term->type = SRD_TERM_SKIP;
@@ -483,14 +575,21 @@ static int create_term_list(PyObject *py_dict, GSList **term_list)
                        term->num_samples_already_skipped = 0;
                } else {
                        srd_err("Term key is neither a string nor a number.");
-                       return SRD_ERR;
+                       goto err;
                }
 
                /* Add the term to the list of terms. */
                *term_list = g_slist_append(*term_list, term);
        }
 
+       PyGILState_Release(gstate);
+
        return SRD_OK;
+
+err:
+       PyGILState_Release(gstate);
+
+       return SRD_ERR;
 }
 
 /**
@@ -510,14 +609,17 @@ static int set_new_condition_list(PyObject *self, PyObject *args)
        GSList *term_list;
        PyObject *py_conditionlist, *py_conds, *py_dict;
        int i, num_conditions, ret;
+       PyGILState_STATE gstate;
 
        if (!self || !args)
                return SRD_ERR_ARG;
 
+       gstate = PyGILState_Ensure();
+
        /* Get the decoder instance. */
        if (!(di = srd_inst_find_by_obj(NULL, self))) {
                PyErr_SetString(PyExc_Exception, "decoder instance not found");
-               return SRD_ERR;
+               goto err;
        }
 
        /*
@@ -526,27 +628,35 @@ static int set_new_condition_list(PyObject *self, PyObject *args)
         */
        if (di->want_wait_terminate) {
                srd_dbg("%s: %s: Skip (want_term).", di->inst_id, __func__);
-               return SRD_ERR;
+               goto err;
        }
 
-       /* Parse the argument of self.wait() into 'py_conds'. */
-       if (!PyArg_ParseTuple(args, "O", &py_conds)) {
+       /*
+        * Parse the argument of self.wait() into 'py_conds', and check
+        * the data type. The argument is optional, None is assumed in
+        * its absence. None or an empty dict or an empty list mean that
+        * there is no condition, and the next available sample shall
+        * get returned to the caller.
+        */
+       py_conds = Py_None;
+       if (!PyArg_ParseTuple(args, "|O", &py_conds)) {
                /* Let Python raise this exception. */
-               return SRD_ERR;
+               goto err;
        }
-
-       /* Check whether 'py_conds' is a dict or a list. */
-       if (PyList_Check(py_conds)) {
+       if (py_conds == Py_None) {
+               /* 'py_conds' is None. */
+               goto ret_9999;
+       } else if (PyList_Check(py_conds)) {
                /* 'py_conds' is a list. */
                py_conditionlist = py_conds;
                num_conditions = PyList_Size(py_conditionlist);
                if (num_conditions == 0)
-                       return 9999; /* The PD invoked self.wait([]). */
+                       goto ret_9999; /* The PD invoked self.wait([]). */
                Py_IncRef(py_conditionlist);
        } else if (PyDict_Check(py_conds)) {
                /* 'py_conds' is a dict. */
                if (PyDict_Size(py_conds) == 0)
-                       return 9999; /* The PD invoked self.wait({}). */
+                       goto ret_9999; /* The PD invoked self.wait({}). */
                /* Make a list and put the dict in there for convenience. */
                py_conditionlist = PyList_New(1);
                Py_IncRef(py_conds);
@@ -554,7 +664,7 @@ static int set_new_condition_list(PyObject *self, PyObject *args)
                num_conditions = 1;
        } else {
                srd_err("Condition list is neither a list nor a dict.");
-               return SRD_ERR;
+               goto err;
        }
 
        /* Free the old condition list. */
@@ -582,38 +692,108 @@ static int set_new_condition_list(PyObject *self, PyObject *args)
 
        Py_DecRef(py_conditionlist);
 
+       PyGILState_Release(gstate);
+
        return ret;
+
+err:
+       PyGILState_Release(gstate);
+
+       return SRD_ERR;
+
+ret_9999:
+       PyGILState_Release(gstate);
+
+       return 9999;
+}
+
+/**
+ * Create a SKIP condition list for condition-less .wait() calls.
+ *
+ * @param di Decoder instance.
+ * @param count Number of samples to skip.
+ *
+ * @retval SRD_OK The new condition list was set successfully.
+ * @retval SRD_ERR There was an error setting the new condition list.
+ *                 The contents of di->condition_list are undefined.
+ *
+ * This routine is a reduced and specialized version of the @ref
+ * set_new_condition_list() and @ref create_term_list() routines which
+ * gets invoked when .wait() was called without specifications for
+ * conditions. This minor duplication of the SKIP term list creation
+ * simplifies the logic and avoids the creation of expensive Python
+ * objects with "constant" values which the caller did not pass in the
+ * first place. It results in maximum sharing of match handling code
+ * paths.
+ */
+static int set_skip_condition(struct srd_decoder_inst *di, uint64_t count)
+{
+       struct srd_term *term;
+       GSList *term_list;
+
+       condition_list_free(di);
+       term = g_malloc0(sizeof(*term));
+       term->type = SRD_TERM_SKIP;
+       term->num_samples_to_skip = count;
+       term->num_samples_already_skipped = 0;
+       term_list = g_slist_append(NULL, term);
+       di->condition_list = g_slist_append(di->condition_list, term_list);
+
+       return SRD_OK;
 }
 
 static PyObject *Decoder_wait(PyObject *self, PyObject *args)
 {
        int ret;
+       uint64_t skip_count;
        unsigned int i;
        gboolean found_match;
        struct srd_decoder_inst *di;
        PyObject *py_pinvalues, *py_matched;
+       PyGILState_STATE gstate;
 
        if (!self || !args)
                return NULL;
 
+       gstate = PyGILState_Ensure();
+
        if (!(di = srd_inst_find_by_obj(NULL, self))) {
                PyErr_SetString(PyExc_Exception, "decoder instance not found");
+               PyGILState_Release(gstate);
                Py_RETURN_NONE;
        }
 
        ret = set_new_condition_list(self, args);
        if (ret < 0) {
                srd_dbg("%s: %s: Aborting wait().", di->inst_id, __func__);
-               return NULL;
+               goto err;
        }
        if (ret == 9999) {
-               /* Empty condition list, automatic match. */
-               PyObject_SetAttrString(di->py_inst, "matched", Py_None);
-               /* Leave self.samplenum unchanged (== di->abs_cur_samplenum). */
-               return get_current_pinvalues(di);
+               /*
+                * Empty condition list, automatic match. Arrange for the
+                * execution of regular match handling code paths such that
+                * the next available sample is returned to the caller.
+                * Make sure to skip one sample when "anywhere within the
+                * stream", yet make sure to not skip sample number 0.
+                */
+               if (di->abs_cur_samplenum)
+                       skip_count = 1;
+               else if (!di->condition_list)
+                       skip_count = 0;
+               else
+                       skip_count = 1;
+               ret = set_skip_condition(di, skip_count);
+               if (ret < 0) {
+                       srd_dbg("%s: %s: Cannot setup condition-less wait().",
+                               di->inst_id, __func__);
+                       goto err;
+               }
        }
 
        while (1) {
+
+               Py_BEGIN_ALLOW_THREADS
+
                /* Wait for new samples to process, or termination request. */
                g_mutex_lock(&di->data_mutex);
                while (!di->got_new_samples && !di->want_wait_terminate)
@@ -629,6 +809,8 @@ static PyObject *Decoder_wait(PyObject *self, PyObject *args)
                found_match = FALSE;
                ret = process_samples_until_condition_match(di, &found_match);
 
+               Py_END_ALLOW_THREADS
+
                /* If there's a match, set self.samplenum etc. and return. */
                if (found_match) {
                        /* Set self.samplenum to the (absolute) sample number that matched. */
@@ -644,11 +826,13 @@ static PyObject *Decoder_wait(PyObject *self, PyObject *args)
                        } else {
                                PyObject_SetAttrString(di->py_inst, "matched", Py_None);
                        }
-       
+
                        py_pinvalues = get_current_pinvalues(di);
 
                        g_mutex_unlock(&di->data_mutex);
 
+                       PyGILState_Release(gstate);
+
                        return py_pinvalues;
                }
 
@@ -671,13 +855,20 @@ static PyObject *Decoder_wait(PyObject *self, PyObject *args)
                        srd_dbg("%s: %s: Will return from wait().",
                                di->inst_id, __func__);
                        g_mutex_unlock(&di->data_mutex);
-                       return NULL;
+                       goto err;
                }
 
                g_mutex_unlock(&di->data_mutex);
        }
 
+       PyGILState_Release(gstate);
+
        Py_RETURN_NONE;
+
+err:
+       PyGILState_Release(gstate);
+
+       return NULL;
 }
 
 /**
@@ -692,40 +883,45 @@ static PyObject *Decoder_wait(PyObject *self, PyObject *args)
  */
 static PyObject *Decoder_has_channel(PyObject *self, PyObject *args)
 {
-       int idx, max_idx;
+       int idx, count;
        struct srd_decoder_inst *di;
-       PyObject *py_channel;
+       PyGILState_STATE gstate;
 
        if (!self || !args)
                return NULL;
 
+       gstate = PyGILState_Ensure();
+
        if (!(di = srd_inst_find_by_obj(NULL, self))) {
                PyErr_SetString(PyExc_Exception, "decoder instance not found");
-               return NULL;
+               goto err;
        }
 
-       /* Parse the argument of self.has_channel() into 'py_channel'. */
-       if (!PyArg_ParseTuple(args, "O", &py_channel)) {
+       /*
+        * Get the integer argument of self.has_channel(). Check for
+        * the range of supported PD input channel numbers.
+        */
+       if (!PyArg_ParseTuple(args, "i", &idx)) {
                /* Let Python raise this exception. */
-               return NULL;
+               goto err;
        }
 
-       if (!PyLong_Check(py_channel)) {
-               PyErr_SetString(PyExc_Exception, "channel index not a number");
-               return NULL;
+       count = g_slist_length(di->decoder->channels) +
+               g_slist_length(di->decoder->opt_channels);
+       if (idx < 0 || idx >= count) {
+               srd_err("Invalid index %d, PD channel count %d.", idx, count);
+               PyErr_SetString(PyExc_IndexError, "invalid channel index");
+               goto err;
        }
 
-       idx = PyLong_AsLong(py_channel);
-       max_idx = g_slist_length(di->decoder->channels)
-               + g_slist_length(di->decoder->opt_channels) - 1;
-
-       if (idx < 0 || idx > max_idx) {
-               srd_err("Invalid channel index %d/%d.", idx, max_idx);
-               PyErr_SetString(PyExc_Exception, "invalid channel");
-               return NULL;
-       }
+       PyGILState_Release(gstate);
 
        return (di->dec_channelmap[idx] == -1) ? Py_False : Py_True;
+
+err:
+       PyGILState_Release(gstate);
+
+       return NULL;
 }
 
 static PyMethodDef Decoder_methods[] = {
@@ -756,11 +952,20 @@ SRD_PRIV PyObject *srd_Decoder_type_new(void)
                { Py_tp_new, (void *)&PyType_GenericNew },
                { 0, NULL }
        };
+       PyObject *py_obj;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
+
        spec.name = "sigrokdecode.Decoder";
        spec.basicsize = sizeof(srd_Decoder);
        spec.itemsize = 0;
        spec.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
        spec.slots = slots;
 
-       return PyType_FromSpec(&spec);
+       py_obj = PyType_FromSpec(&spec);
+
+       PyGILState_Release(gstate);
+
+       return py_obj;
 }
diff --git a/type_logic.c b/type_logic.c
deleted file mode 100644 (file)
index ba356c0..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * This file is part of the libsigrokdecode project.
- *
- * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <config.h>
-#include "libsigrokdecode-internal.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */
-#include "libsigrokdecode.h"
-#include <inttypes.h>
-#include <string.h>
-
-static PyObject *srd_logic_iter(PyObject *self)
-{
-       return self;
-}
-
-static PyObject *srd_logic_iternext(PyObject *self)
-{
-       srd_logic *logic;
-       PyObject *py_samplenum, *py_samples;
-       uint8_t *sample_pos, sample;
-       int byte_offset, bit_offset, i;
-
-       logic = (srd_logic *)self;
-       if (logic->itercnt >= logic->inbuflen / logic->di->data_unitsize) {
-               /* End iteration loop. */
-               return NULL;
-       }
-
-       /*
-        * Convert the bit-packed sample to an array of bytes, with only 0x01
-        * and 0x00 values, so the PD doesn't need to do any bitshifting.
-        */
-       sample_pos = logic->inbuf + logic->itercnt * logic->di->data_unitsize;
-       for (i = 0; i < logic->di->dec_num_channels; i++) {
-               /* A channelmap value of -1 means "unused optional channel". */
-               if (logic->di->dec_channelmap[i] == -1) {
-                       /* Value of unused channel is 0xff, instead of 0 or 1. */
-                       logic->di->channel_samples[i] = 0xff;
-               } else {
-                       byte_offset = logic->di->dec_channelmap[i] / 8;
-                       bit_offset = logic->di->dec_channelmap[i] % 8;
-                       sample = *(sample_pos + byte_offset) & (1 << bit_offset) ? 1 : 0;
-                       logic->di->channel_samples[i] = sample;
-               }
-       }
-
-       /* Prepare the next samplenum/sample list in this iteration. */
-       py_samplenum =
-           PyLong_FromUnsignedLongLong(logic->abs_start_samplenum +
-                                       logic->itercnt);
-       PyList_SetItem(logic->sample, 0, py_samplenum);
-       py_samples = PyBytes_FromStringAndSize((const char *)logic->di->channel_samples,
-                                              logic->di->dec_num_channels);
-       PyList_SetItem(logic->sample, 1, py_samples);
-       Py_INCREF(logic->sample);
-       logic->itercnt++;
-
-       return logic->sample;
-}
-
-/** Create the srd_logic type.
- * @return The new type object.
- * @private
- */
-SRD_PRIV PyObject *srd_logic_type_new(void)
-{
-       PyType_Spec spec;
-       PyType_Slot slots[] = {
-               { Py_tp_doc, "sigrokdecode logic sample object" },
-               { Py_tp_iter, (void *)&srd_logic_iter },
-               { Py_tp_iternext, (void *)&srd_logic_iternext },
-               { Py_tp_new, (void *)&PyType_GenericNew },
-               { 0, NULL }
-       };
-       spec.name = "srd_logic";
-       spec.basicsize = sizeof(srd_logic);
-       spec.itemsize = 0;
-       spec.flags = Py_TPFLAGS_DEFAULT;
-       spec.slots = slots;
-
-       return PyType_FromSpec(&spec);
-}
diff --git a/util.c b/util.c
index 9a573ccd29e3ef68bc0fac2034fa188d05ef42f8..430a7fb5e5c5bff8800ae7fd711425d276cc20bc 100644 (file)
--- a/util.c
+++ b/util.c
 SRD_PRIV PyObject *py_import_by_name(const char *name)
 {
        PyObject *py_mod, *py_modname;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
 
        py_modname = PyUnicode_FromString(name);
-       if (!py_modname)
+       if (!py_modname) {
+               PyGILState_Release(gstate);
                return NULL;
+       }
 
        py_mod = PyImport_Import(py_modname);
        Py_DECREF(py_modname);
 
+       PyGILState_Release(gstate);
+
        return py_mod;
 }
 
@@ -64,21 +71,31 @@ SRD_PRIV int py_attr_as_str(PyObject *py_obj, const char *attr, char **outstr)
 {
        PyObject *py_str;
        int ret;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
 
        if (!PyObject_HasAttrString(py_obj, attr)) {
                srd_dbg("Object has no attribute '%s'.", attr);
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        if (!(py_str = PyObject_GetAttrString(py_obj, attr))) {
                srd_exception_catch("Failed to get attribute '%s'", attr);
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        ret = py_str_as_str(py_str, outstr);
        Py_DECREF(py_str);
 
+       PyGILState_Release(gstate);
+
        return ret;
+
+err:
+       PyGILState_Release(gstate);
+
+       return SRD_ERR_PYTHON;
 }
 
 /**
@@ -101,20 +118,23 @@ SRD_PRIV int py_attr_as_strlist(PyObject *py_obj, const char *attr, GSList **out
        Py_ssize_t i;
        int ret;
        char *outstr;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
 
        if (!PyObject_HasAttrString(py_obj, attr)) {
                srd_dbg("Object has no attribute '%s'.", attr);
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        if (!(py_list = PyObject_GetAttrString(py_obj, attr))) {
                srd_exception_catch("Failed to get attribute '%s'", attr);
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        if (!PyList_Check(py_list)) {
                srd_dbg("Object is not a list.");
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        *outstrlist = NULL;
@@ -123,14 +143,21 @@ SRD_PRIV int py_attr_as_strlist(PyObject *py_obj, const char *attr, GSList **out
                ret = py_listitem_as_str(py_list, i, &outstr);
                if (ret < 0) {
                        srd_dbg("Couldn't get item %" PY_FORMAT_SIZE_T "d.", i);
-                       return SRD_ERR_PYTHON;
+                       goto err;
                }
                *outstrlist = g_slist_append(*outstrlist, outstr);
        }
 
        Py_DECREF(py_list);
 
+       PyGILState_Release(gstate);
+
        return SRD_OK;
+
+err:
+       PyGILState_Release(gstate);
+
+       return SRD_ERR_PYTHON;
 }
 
 /**
@@ -150,18 +177,28 @@ SRD_PRIV int py_dictitem_as_str(PyObject *py_obj, const char *key,
                                char **outstr)
 {
        PyObject *py_value;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
 
        if (!PyDict_Check(py_obj)) {
                srd_dbg("Object is not a dictionary.");
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        if (!(py_value = PyDict_GetItemString(py_obj, key))) {
                srd_dbg("Dictionary has no attribute '%s'.", key);
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
+       PyGILState_Release(gstate);
+
        return py_str_as_str(py_value, outstr);
+
+err:
+       PyGILState_Release(gstate);
+
+       return SRD_ERR_PYTHON;
 }
 
 /**
@@ -181,18 +218,28 @@ SRD_PRIV int py_listitem_as_str(PyObject *py_obj, Py_ssize_t idx,
                                char **outstr)
 {
        PyObject *py_value;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
 
        if (!PyList_Check(py_obj)) {
                srd_dbg("Object is not a list.");
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        if (!(py_value = PyList_GetItem(py_obj, idx))) {
                srd_dbg("Couldn't get list item %" PY_FORMAT_SIZE_T "d.", idx);
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
+       PyGILState_Release(gstate);
+
        return py_str_as_str(py_value, outstr);
+
+err:
+       PyGILState_Release(gstate);
+
+       return SRD_ERR_PYTHON;
 }
 
 /**
@@ -212,26 +259,34 @@ SRD_PRIV int py_pydictitem_as_str(PyObject *py_obj, PyObject *py_key,
                                char **outstr)
 {
        PyObject *py_value;
+       PyGILState_STATE gstate;
 
        if (!py_obj || !py_key || !outstr)
                return SRD_ERR_ARG;
 
+       gstate = PyGILState_Ensure();
+
        if (!PyDict_Check(py_obj)) {
                srd_dbg("Object is not a dictionary.");
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        if (!(py_value = PyDict_GetItem(py_obj, py_key))) {
                srd_dbg("Dictionary has no such key.");
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        if (!PyUnicode_Check(py_value)) {
                srd_dbg("Dictionary value should be a string.");
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        return py_str_as_str(py_value, outstr);
+
+err:
+       PyGILState_Release(gstate);
+
+       return SRD_ERR_PYTHON;
 }
 
 /**
@@ -249,28 +304,38 @@ SRD_PRIV int py_pydictitem_as_str(PyObject *py_obj, PyObject *py_key,
 SRD_PRIV int py_pydictitem_as_long(PyObject *py_obj, PyObject *py_key, uint64_t *out)
 {
        PyObject *py_value;
+       PyGILState_STATE gstate;
 
        if (!py_obj || !py_key || !out)
                return SRD_ERR_ARG;
 
+       gstate = PyGILState_Ensure();
+
        if (!PyDict_Check(py_obj)) {
                srd_dbg("Object is not a dictionary.");
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        if (!(py_value = PyDict_GetItem(py_obj, py_key))) {
                srd_dbg("Dictionary has no such key.");
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        if (!PyLong_Check(py_value)) {
                srd_dbg("Dictionary value should be a long.");
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        *out = PyLong_AsUnsignedLongLong(py_value);
 
+       PyGILState_Release(gstate);
+
        return SRD_OK;
+
+err:
+       PyGILState_Release(gstate);
+
+       return SRD_ERR_PYTHON;
 }
 
 /**
@@ -289,9 +354,13 @@ SRD_PRIV int py_str_as_str(PyObject *py_str, char **outstr)
 {
        PyObject *py_bytes;
        char *str;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
 
        if (!PyUnicode_Check(py_str)) {
                srd_dbg("Object is not a string object.");
+               PyGILState_Release(gstate);
                return SRD_ERR_PYTHON;
        }
 
@@ -301,11 +370,14 @@ SRD_PRIV int py_str_as_str(PyObject *py_str, char **outstr)
                Py_DECREF(py_bytes);
                if (str) {
                        *outstr = str;
+                       PyGILState_Release(gstate);
                        return SRD_OK;
                }
        }
        srd_exception_catch("Failed to extract string");
 
+       PyGILState_Release(gstate);
+
        return SRD_ERR_PYTHON;
 }
 
@@ -327,22 +399,27 @@ SRD_PRIV int py_strseq_to_char(PyObject *py_strseq, char ***out_strv)
        PyObject *py_item, *py_bytes;
        char **strv, *str;
        ssize_t seq_len, i;
+       PyGILState_STATE gstate;
+       int ret = SRD_ERR_PYTHON;
+
+       gstate = PyGILState_Ensure();
 
        if (!PySequence_Check(py_strseq)) {
                srd_err("Object does not provide sequence protocol.");
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        seq_len = PySequence_Size(py_strseq);
        if (seq_len < 0) {
                srd_exception_catch("Failed to obtain sequence size");
-               return SRD_ERR_PYTHON;
+               goto err;
        }
 
        strv = g_try_new0(char *, seq_len + 1);
        if (!strv) {
                srd_err("Failed to allocate result string vector.");
-               return SRD_ERR_MALLOC;
+               ret = SRD_ERR_MALLOC;
+               goto err;
        }
 
        for (i = 0; i < seq_len; i++) {
@@ -374,7 +451,10 @@ err_out:
        g_strfreev(strv);
        srd_exception_catch("Failed to obtain string item");
 
-       return SRD_ERR_PYTHON;
+err:
+       PyGILState_Release(gstate);
+
+       return ret;
 }
 
 /**
@@ -389,6 +469,9 @@ err_out:
 SRD_PRIV GVariant *py_obj_to_variant(PyObject *py_obj)
 {
        GVariant *var = NULL;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
 
        if (PyUnicode_Check(py_obj)) { /* string */
                PyObject *py_bytes;
@@ -426,5 +509,7 @@ SRD_PRIV GVariant *py_obj_to_variant(PyObject *py_obj)
                srd_err("Failed to extract value of unsupported type.");
        }
 
+       PyGILState_Release(gstate);
+
        return var;
 }