From 84f21b48823903846f7060bd5576c4302a3aa169 Mon Sep 17 00:00:00 2001
From: Steve Tell <tell@telltronics.org>
Date: Thu, 16 Jun 2011 21:16:36 -0400
Subject: [PATCH 2/2] Add bindings for python that support many of the chain
 operations includes my work and additions by Jonathan
 Stroud to support additional methods needed for burning
 flash devices. also formatting and other cleanups from
 reviews. terse documentation on using the python
 bindings in doc/urjtag-pthon.txt

---
 urjtag/Makefile.am                       |    3 +-
 urjtag/acinclude.m4                      |   25 +
 urjtag/bindings/Makefile.am              |   28 +
 urjtag/bindings/python/Makefile.am       |   36 ++
 urjtag/bindings/python/chain.c           | 1007 ++++++++++++++++++++++++++++++
 urjtag/bindings/python/pycompat23.h      |   38 ++
 urjtag/bindings/python/setup.py          |   13 +
 urjtag/bindings/python/t_srst.py         |   49 ++
 urjtag/bindings/python/t_urjtag_chain.py |   81 +++
 urjtag/configure.ac                      |   37 ++-
 urjtag/doc/urjtag-python.txt             |  218 +++++++
 urjtag/include/urjtag/tap_register.h     |    4 +-
 urjtag/src/tap/discovery.c               |    3 +-
 urjtag/src/tap/register.c                |   52 +-
 14 files changed, 1562 insertions(+), 32 deletions(-)
 create mode 100644 urjtag/bindings/Makefile.am
 create mode 100644 urjtag/bindings/python/Makefile.am
 create mode 100644 urjtag/bindings/python/chain.c
 create mode 100644 urjtag/bindings/python/pycompat23.h
 create mode 100644 urjtag/bindings/python/setup.py
 create mode 100644 urjtag/bindings/python/t_srst.py
 create mode 100644 urjtag/bindings/python/t_urjtag_chain.py
 create mode 100644 urjtag/doc/urjtag-python.txt

diff --git a/urjtag/Makefile.am b/urjtag/Makefile.am
index abbe58d..8ab58c8 100644
--- a/urjtag/Makefile.am
+++ b/urjtag/Makefile.am
@@ -30,7 +30,8 @@ SUBDIRS = \
 	include/urjtag \
 	data \
 	src \
-	po
+	po \
+	bindings
 
 if ENABLE_APPS
 SUBDIRS += \
diff --git a/urjtag/acinclude.m4 b/urjtag/acinclude.m4
index 59fcc23..46d9133 100644
--- a/urjtag/acinclude.m4
+++ b/urjtag/acinclude.m4
@@ -177,3 +177,28 @@ dnl
 m4_ifndef([LT_INIT],[dnl
 m4_define([LT_INIT],[AC_PROG_LIBTOOL])
 ])dnl
+
+dnl a macro to check for ability to create python extensions
+dnl  AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE])
+dnl function also defines PYTHON_INCLUDES
+AC_DEFUN([AM_CHECK_PYTHON_HEADERS],
+[AC_REQUIRE([AM_PATH_PYTHON])
+AC_MSG_CHECKING(for headers required to compile python extensions)
+dnl deduce PYTHON_INCLUDES
+py_prefix=`$PYTHON -c 'import sys; sys.stdout.write(sys.prefix+"\n")'`
+py_exec_prefix=`$PYTHON -c 'import sys; sys.stdout.write(sys.exec_prefix+"\n")'`
+PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}"
+if test "$py_prefix" != "$py_exec_prefix"; then
+  PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}"
+fi
+AC_SUBST(PYTHON_INCLUDES)
+dnl check if the headers exist:
+save_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES"
+AC_TRY_CPP([#include <Python.h>],dnl
+[AC_MSG_RESULT(found)
+$1],dnl
+[AC_MSG_RESULT(not found)
+$2])
+CPPFLAGS="$save_CPPFLAGS"
+])
diff --git a/urjtag/bindings/Makefile.am b/urjtag/bindings/Makefile.am
new file mode 100644
index 0000000..486ff26
--- /dev/null
+++ b/urjtag/bindings/Makefile.am
@@ -0,0 +1,28 @@
+#
+# $Id$
+#
+# Copyright (C) 2002,2011 ETC s.r.o.
+#
+# 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+#
+# Written by Steve Tell <tell@telltronics.org> 2011
+#
+
+SUBDIRS =
+
+if BIND_PYTHON
+SUBDIRS += python
+endif
diff --git a/urjtag/bindings/python/Makefile.am b/urjtag/bindings/python/Makefile.am
new file mode 100644
index 0000000..6acf3e2
--- /dev/null
+++ b/urjtag/bindings/python/Makefile.am
@@ -0,0 +1,36 @@
+#
+# $Id$
+#
+# Copyright (C) 2002,2011 ETC s.r.o.
+#
+# 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+#
+# Written by Steve Tell <tell@telltronics.org> 2011
+#
+
+EXTRA_DIST = setup.py chain.c pycompat23.h t_urjtag_chain.py
+
+all-local: build
+
+build: chain.c
+	{ $(PYTHON) setup.py build && touch build; } || { $(RM) -r build; exit 1; }
+
+install-data-local:
+	$(PYTHON) setup.py install --prefix=$(DESTDIR)$(prefix)
+
+clean-local:
+	$(RM) -fr build
+
diff --git a/urjtag/bindings/python/chain.c b/urjtag/bindings/python/chain.c
new file mode 100644
index 0000000..c1307a8
--- /dev/null
+++ b/urjtag/bindings/python/chain.c
@@ -0,0 +1,1007 @@
+/* 
+ * $Id$
+ *
+ * Copyright (C) 2011 
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Python bindings for urjtag intially written by Steve Tell.
+ * Additional methods by Jonathan Stroud.
+ *
+ */
+#include <Python.h>
+#include "structmember.h"
+#include "pycompat23.h"
+
+#include <sysdep.h>
+
+#include <urjtag.h>
+#include <chain.h>
+#include <cmd.h>
+
+static PyObject *UrjtagError;
+
+typedef struct
+{
+    PyObject_HEAD urj_chain_t *urchain;
+} urj_pychain_t;
+
+static void
+urj_pyc_dealloc (urj_pychain_t *self)
+{
+    urj_tap_chain_free (self->urchain);
+    Py_TYPE (self)->tp_free ((PyObject *) self);
+}
+
+static PyObject *
+urj_pyc_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    urj_pychain_t *self;
+
+    self = (urj_pychain_t *) type->tp_alloc (type, 0);
+    if (self == NULL)
+        return NULL;
+
+    self->urchain = urj_tap_chain_alloc ();
+    if (self->urchain == NULL)
+    {
+        Py_DECREF (self);
+        return PyErr_NoMemory ();
+    }
+    self->urchain->main_part = 0;
+    return (PyObject *) self;
+}
+
+
+/* python helpers for methods */
+
+/*
+ * propagate return value from liburjtag function that returns
+ * URJ_STATUS_OK on success or somthing else on failure.
+ * if OK, return python "none".
+ * else, throw a python exception
+ */
+static PyObject *
+urj_py_chkret (int rc)
+{
+    if (rc == URJ_STATUS_OK)
+        return Py_BuildValue ("");  /* python "None" */
+    
+    if (urj_error_get ())
+    {
+        PyErr_SetString (UrjtagError, urj_error_describe ());
+        urj_error_reset ();
+    }
+    else
+    {
+        PyErr_SetString (UrjtagError,
+                         _("liburjtag BUG: unknown urjtag error"));
+    }
+    return NULL;
+}
+
+#define UPRC_CBL 1
+#define UPRC_DET 2
+#define UPRC_BUS 4
+
+/* perform selected prerequesite checks on the state of a chain object
+ * returns nonzero on success.
+ * if 0 is returned, python exception has been posted and
+ *     caller must return NULL to signal python exception.
+ */
+static int
+urj_pyc_precheck (urj_chain_t *urc, int checks_needed)
+{
+    if (urc == NULL)
+    {
+        PyErr_SetString (PyExc_RuntimeError, _("liburjtag python binding BUG: null chain"));
+        return 0;
+    }
+    if (checks_needed & UPRC_CBL)
+    {
+        if (urj_cmd_test_cable (urc) != URJ_STATUS_OK)
+        {
+            PyErr_SetString (UrjtagError, _("cable() has not been called"));
+            return 0;
+        }
+    }
+    if (checks_needed & UPRC_DET)
+    {
+        if (urc->parts == NULL)
+        {
+            PyErr_SetString (PyExc_RuntimeError,
+                             _("no parts: detect or addpart not called on this chain"));
+            return 0;
+        }
+    }
+    if (checks_needed & UPRC_BUS)
+    {
+        if (!urj_bus)   /* why is this a global and not a chain property? */
+        {
+            PyErr_SetString (PyExc_RuntimeError,
+                             _("Bus missing: initbus not called?"));
+
+            return 0;
+        }
+        if (!urj_bus->driver)
+        {
+            PyErr_SetString (PyExc_RuntimeError,
+                             _("Bus driver missing: initbus not called?"));
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+
+/* urj_chain_t / urjtag.chain methods */
+
+static PyObject *
+urj_pyc_cable (urj_pychain_t *self, PyObject *args)
+{
+    char *cable_params[5] = { NULL, NULL, NULL, NULL, NULL };
+    urj_chain_t *urc = self->urchain;
+    char *drivername;
+
+    if (!urj_pyc_precheck (urc, 0))
+        return NULL;
+
+    if (!PyArg_ParseTuple (args, "s|ssss",
+                           &drivername, 
+                           &cable_params[0],
+                           &cable_params[1],
+                           &cable_params[2], &cable_params[3]))
+    {
+        return NULL;
+    }
+
+    return urj_py_chkret (urj_tap_chain_connect (urc, drivername, cable_params));
+}
+
+static PyObject *
+urj_pyc_disconnect (urj_pychain_t *self)
+{
+    urj_chain_t *urc = self->urchain;
+    if (!urj_pyc_precheck (urc, 0))
+        return NULL;
+    urj_tap_chain_disconnect (urc);
+    return Py_BuildValue ("");
+}
+
+static PyObject *
+urj_pyc_test_cable (urj_pychain_t *self)
+{
+    urj_chain_t *urc = self->urchain;
+    if (!urj_pyc_precheck (urc, 0))
+        return NULL;
+    return urj_py_chkret (urj_cmd_test_cable (urc));
+}
+
+static PyObject *
+urj_pyc_tap_detect (urj_pychain_t *self, PyObject *args)
+{
+    urj_chain_t *urc = self->urchain;
+    int maxirlen = 0;
+    if (!PyArg_ParseTuple (args, "|i", &maxirlen))
+    {
+        return NULL;
+    }
+
+    if (!urj_pyc_precheck (urc, UPRC_CBL))
+        return NULL;
+    return urj_py_chkret (urj_tap_detect (urc, maxirlen));
+}
+
+static PyObject *
+urj_pyc_len (urj_pychain_t *self, PyObject *args)
+{
+    urj_chain_t *urc = self->urchain;
+    if (!urj_pyc_precheck (urc, UPRC_CBL|UPRC_DET))
+        return NULL;
+
+    return Py_BuildValue ("i", urc->parts->len);
+}
+
+static PyObject *
+urj_pyc_partid (urj_pychain_t *self, PyObject *args)
+{
+    urj_chain_t *urc = self->urchain;
+    int partno;
+    if (!PyArg_ParseTuple (args, "i", &partno))
+        return NULL;
+
+    if (!urj_pyc_precheck (urc, UPRC_CBL|UPRC_DET))
+        return NULL;
+
+    if (partno >= urc->parts->len)
+    {
+        PyErr_SetString (PyExc_RuntimeError, _("part number out of range"));
+        return NULL;
+    }
+    else
+    {
+        urj_part_t *p;
+        uint32_t id;
+
+        p = urc->parts->parts[partno];
+        id = urj_tap_register_get_value (p->id);
+        return Py_BuildValue ("i", id);
+    }
+}
+
+static PyObject *
+urj_pyc_reset (urj_pychain_t *self)
+{
+    urj_chain_t *urc = self->urchain;
+    PyObject *rc;
+
+    if (!urj_pyc_precheck (urc, UPRC_CBL))
+        return NULL;
+
+    rc = urj_py_chkret (urj_tap_reset_bypass (urc));
+    urj_tap_chain_flush (urc);
+    return rc;
+}
+
+
+static PyObject *
+urj_pyc_set_trst (urj_pychain_t *self, PyObject *args)
+{
+    urj_chain_t *urc = self->urchain;
+    int trstval;
+    if (!PyArg_ParseTuple (args, "i", &trstval))
+        return NULL;
+    if (!urj_pyc_precheck (urc, UPRC_CBL))
+        return NULL;
+    urj_tap_chain_set_trst (urc, trstval);
+    return Py_BuildValue ("");
+}
+
+static PyObject *
+urj_pyc_get_trst (urj_pychain_t *self)
+{
+    int trstval;
+    urj_chain_t *urc = self->urchain;
+    if (!urj_pyc_precheck (urc, UPRC_CBL))
+        return NULL;
+
+    trstval = urj_tap_chain_get_trst (urc);
+    return Py_BuildValue ("i", trstval);
+}
+
+static PyObject *
+urj_pyc_set_pod_signal (urj_pychain_t *self, PyObject *args)
+{
+    urj_chain_t *urc = self->urchain;
+    uint32_t mask, val, oldval;
+    if (!PyArg_ParseTuple (args, "ii", &mask, &val))
+        return NULL;
+    if (!urj_pyc_precheck (urc, UPRC_CBL))
+        return NULL;
+
+    oldval = urj_tap_chain_set_pod_signal (urc, mask, val);
+    return Py_BuildValue ("i", oldval);
+}
+
+static PyObject *
+urj_pyc_get_pod_signal (urj_pychain_t *self, PyObject *args)
+{
+    uint32_t sig;
+    uint32_t val;
+    urj_chain_t *urc = self->urchain;
+    if (!PyArg_ParseTuple (args, "i", &sig))
+        return NULL;
+    if (!urj_pyc_precheck (urc, UPRC_CBL))
+        return NULL;
+
+    val = urj_tap_chain_get_pod_signal (urc, sig);
+    return Py_BuildValue ("i", val);
+}
+
+static PyObject *
+urj_pyc_set_frequency (urj_pychain_t *self, PyObject *args)
+{
+    urj_chain_t *urc = self->urchain;
+    uint32_t freq;
+    if (!PyArg_ParseTuple (args, "i", &freq))
+        return NULL;
+    if (!urj_pyc_precheck (urc, UPRC_CBL))
+        return NULL;
+
+    urj_tap_cable_set_frequency (urc->cable, freq);
+    return Py_BuildValue ("");
+}
+
+static PyObject *
+urj_pyc_get_frequency (urj_pychain_t *self, PyObject *args)
+{
+    urj_chain_t *urc = self->urchain;
+    unsigned long freq;
+    if (!urj_pyc_precheck (urc, UPRC_CBL))
+        return NULL;
+
+    freq = urj_tap_cable_get_frequency (urc->cable);
+
+    return Py_BuildValue ("i", (uint32_t) freq);
+}
+
+/* set instruction for the active part
+ */
+static PyObject *
+urj_pyc_set_instruction (urj_pychain_t *self, PyObject *args)
+{
+    char *instname;
+    urj_part_t *part;
+    urj_chain_t *urc = self->urchain;
+    if (!PyArg_ParseTuple (args, "s", &instname))
+        return NULL;
+    if (!urj_pyc_precheck (urc, UPRC_CBL))
+        return NULL;
+
+    part = urj_tap_chain_active_part (urc);
+    if (part == NULL)
+    {
+        PyErr_SetString (UrjtagError, _("No active part on chain"));
+        return NULL;
+    }
+    urj_part_set_instruction (part, instname);
+    return Py_BuildValue ("");
+}
+
+static PyObject *
+urj_pyc_shift_ir (urj_pychain_t *self)
+{
+    urj_chain_t *urc = self->urchain;
+    if (!urj_pyc_precheck (urc, UPRC_CBL))
+        return NULL;
+
+    return urj_py_chkret (urj_tap_chain_shift_instructions (urc));
+}
+
+static PyObject *
+urj_pyc_shift_dr (urj_pychain_t *self)
+{
+    urj_chain_t *urc = self->urchain;
+    if (!urj_pyc_precheck (urc, UPRC_CBL))
+        return NULL;
+
+    /*  TODO: need a way to not capture the TDO output
+     */
+    return urj_py_chkret (urj_tap_chain_shift_data_registers (urc, 1));
+}
+
+static PyObject *
+urj_pyc_get_dr (urj_pychain_t *self, int in, int string, PyObject *args)
+{
+    urj_chain_t *urc = self->urchain;
+    urj_part_t *part;
+    urj_tap_register_t *r;
+    urj_data_register_t *dr;
+    urj_part_instruction_t *active_ir;
+    int lsb = -1;
+    int msb = -1;
+
+    if(!PyArg_ParseTuple (args, "|ii", &msb, &lsb)) {
+        return NULL;
+    }
+    if(lsb == -1)
+        lsb = msb;
+    if (!urj_pyc_precheck (urc, UPRC_CBL))
+        return NULL;
+
+    part = urj_tap_chain_active_part (urc);
+    if (part == NULL)
+    {
+        PyErr_SetString (UrjtagError, _("no active part in chain"));
+        return NULL;
+    }
+    active_ir = part->active_instruction;
+    if (active_ir == NULL)
+    {
+        PyErr_SetString (UrjtagError, _("part without active instruction"));
+        return NULL;
+    }
+    dr = active_ir->data_register;
+    if (dr == NULL)
+    {
+        PyErr_SetString (UrjtagError,
+                         _("instruction without active data register"));
+        return NULL;
+    }
+
+    if (in)
+        r = dr->in;             /* input buffer for next shift_dr */
+    else
+        r = dr->out;            /* recently captured+scanned-out values */
+
+    if(msb == -1) {
+        if(string) 
+            return Py_BuildValue ("s", urj_tap_register_get_string (r));
+        else
+            return Py_BuildValue ("L", urj_tap_register_get_value (r));
+    } else {
+        if(string) 
+            return Py_BuildValue (""); /* TODO urj_tap_register_get_string_bit_range (r, msb, lsb)); */
+        else
+            return Py_BuildValue ("L", urj_tap_register_get_value_bit_range (r, msb, lsb));
+    }
+}
+
+static PyObject *
+urj_pyc_get_str_dr_out (urj_pychain_t *self, PyObject *args)
+{
+    return urj_pyc_get_dr (self, 0, 1, args);
+}
+
+static PyObject *
+urj_pyc_get_str_dr_in (urj_pychain_t *self, PyObject *args)
+{
+    return urj_pyc_get_dr (self, 1, 1, args);
+}
+
+static PyObject *
+urj_pyc_get_int_dr_out (urj_pychain_t *self, PyObject *args)
+{
+    return urj_pyc_get_dr (self, 0, 0, args);
+}
+
+static PyObject *
+urj_pyc_get_int_dr_in (urj_pychain_t *self, PyObject *args)
+{
+    return urj_pyc_get_dr (self, 1, 0, args);
+}
+
+
+static PyObject *
+urj_pyc_set_dr (urj_pychain_t *self, int in, PyObject *args)
+{
+    urj_chain_t *urc = self->urchain;
+    urj_part_t *part;
+    urj_tap_register_t *r;
+    urj_data_register_t *dr;
+    urj_part_instruction_t *active_ir;
+    char *newstr = NULL;
+    uint64_t newval;
+    int lsb = -1;
+    int msb = -1;
+
+    if (!PyArg_ParseTuple (args, "s|ii", &newstr, &msb, &lsb)) {
+        if(!PyArg_ParseTuple (args, "L|ii", &newval, &msb, &lsb)) {
+            return NULL;
+            
+        }
+    }
+
+    if (!urj_pyc_precheck (urc, UPRC_CBL))
+        return NULL;
+
+    part = urj_tap_chain_active_part (urc);
+    if (part == NULL)
+    {
+        PyErr_SetString (UrjtagError, _("no active part in chain"));
+        return NULL;
+    }
+    active_ir = part->active_instruction;
+    if (active_ir == NULL)
+    {
+        PyErr_SetString (UrjtagError, _("part without active instruction"));
+        return NULL;
+    }
+    dr = active_ir->data_register;
+    if (dr == NULL)
+    {
+        PyErr_SetString (UrjtagError,
+                         _("instruction without active data register"));
+        return NULL;
+    }
+
+    if (in)
+        r = dr->in;
+    else
+        r = dr->out;
+
+    if(msb == -1) {
+        if(newstr)
+            return urj_py_chkret (urj_tap_register_set_string(r, newstr));
+        else
+            return urj_py_chkret (urj_tap_register_set_value(r, newval));
+    } else {
+        if(lsb == -1)
+            lsb = msb;
+
+        if(newstr) {
+            /* TODO urj_tap_register_set_string_bit_range(r, newstr, msb, lsb); */
+            return Py_BuildValue ("");
+        } else {
+            return urj_py_chkret (urj_tap_register_set_value_bit_range(r, newval, msb, lsb));
+        }
+ 
+    }
+}
+
+static PyObject *
+urj_pyc_set_dr_out (urj_pychain_t *self, PyObject *args)
+{
+    return urj_pyc_set_dr (self, 0, args);
+}
+
+static PyObject *
+urj_pyc_set_dr_in (urj_pychain_t *self, PyObject *args)
+{
+    return urj_pyc_set_dr (self, 1, args);
+}
+
+static PyObject *
+urj_pyc_run_svf (urj_pychain_t *self, PyObject *args)
+{
+    urj_chain_t *urc = self->urchain;
+    char *fname;
+    int stop = 0;
+    unsigned long ref_freq = 0;
+    FILE *svf_file;
+    PyObject *rc;
+
+    if (!PyArg_ParseTuple (args, "s|iI", &fname, &stop, &ref_freq))
+        return NULL;
+    if (!urj_pyc_precheck (urc, UPRC_CBL))
+        return NULL;
+
+    svf_file = fopen (fname, FOPEN_R);
+    if (!svf_file)
+    {
+        PyErr_SetFromErrnoWithFilename(PyExc_IOError, fname);
+        return NULL;
+    }
+    rc = urj_py_chkret (urj_svf_run (urc, svf_file, stop, ref_freq));
+    fclose (svf_file);
+    return rc;
+}
+
+static PyObject *
+urj_pyc_addpart (urj_pychain_t *self, PyObject *args)
+{
+    urj_chain_t *urc = self->urchain;
+    long unsigned len;
+
+    if (!PyArg_ParseTuple (args, "i", &len))
+        return NULL;
+
+    if (!urj_pyc_precheck (urc, UPRC_CBL))
+        return NULL;
+
+    if (urj_tap_manual_add (urc, len) < 0) {
+            PyErr_SetString (PyExc_RuntimeError,
+                             _("urj_tap_manual_add failed"));
+            return NULL;
+    }
+
+    if (urc->parts == NULL) {
+        PyErr_SetString (PyExc_RuntimeError,
+                         _("addpart: internal error; no parts."));       
+        return NULL;
+    }
+
+    // @@@@ RFHH this cannot be
+    if (urc->parts->len == 0)
+    {
+        urj_part_parts_free (urc->parts);
+        self->urchain->parts = NULL;
+        PyErr_SetString (PyExc_RuntimeError,
+                         _("addpart: internal error; parts->len==0."));       
+        return NULL;
+    }
+
+    urj_part_parts_set_instruction (urc->parts, "BYPASS");
+    urj_tap_chain_shift_instructions (urc);
+    return Py_BuildValue ("");
+}
+
+static PyObject *
+urj_pyc_add_register (urj_pychain_t *self, PyObject *args)
+{
+    char *regname;
+    int reglen;
+    urj_part_t *part;
+    urj_chain_t *urc = self->urchain;
+
+    if (!PyArg_ParseTuple (args, "si", &regname, &reglen))
+    {
+        return NULL;
+    }
+    if (!urj_pyc_precheck (urc, UPRC_CBL|UPRC_DET))
+        return NULL;
+
+    part = urj_tap_chain_active_part (urc);
+    if (part == NULL) {
+        if (urj_error_get ())
+        {
+            PyErr_SetString (UrjtagError, urj_error_describe ());
+            urj_error_reset ();
+        }
+        else
+        {
+            PyErr_SetString (UrjtagError,
+                             _("liburjtag BUG: unknown urjtag error"));
+        }
+        return NULL;
+    }
+    
+    return urj_py_chkret (urj_part_data_register_define (part, regname, reglen));
+}
+
+static PyObject *
+urj_pyc_add_instruction (urj_pychain_t *self, PyObject *args)
+{
+    char *instname;
+    char *code;
+    char *regname;
+    urj_part_t *part;
+    urj_chain_t *urc = self->urchain;
+
+    if (!PyArg_ParseTuple (args, "sss", &instname, &code, &regname))
+    {
+        return NULL;
+    }
+    if (!urj_pyc_precheck (urc, UPRC_CBL|UPRC_DET))
+        return NULL;
+    part = urj_tap_chain_active_part (urc);
+
+    if (part == NULL) {
+        if (urj_error_get ())
+        {
+            PyErr_SetString (UrjtagError, urj_error_describe ());
+            urj_error_reset ();
+        }
+        else
+        {
+            PyErr_SetString (UrjtagError,
+                             _("liburjtag BUG: unknown urjtag error"));
+        }
+        return NULL;
+    }
+
+    if(urj_part_instruction_define (part, instname, code, regname) == NULL) {
+        return urj_py_chkret (URJ_STATUS_FAIL);
+    } else {
+        return Py_BuildValue ("");
+    }
+}
+
+static PyObject *
+urj_pyc_setpart (urj_pychain_t *self, PyObject *args)
+{
+    urj_chain_t *urc = self->urchain;
+    int part;
+    if (!PyArg_ParseTuple (args, "i", &part))
+        return NULL;
+    if (!urj_pyc_precheck (urc, UPRC_CBL|UPRC_DET))
+        return NULL;
+
+    urc->active_part = part;
+    return Py_BuildValue ("");
+}
+
+static PyObject *
+urj_pyc_initbus (urj_pychain_t *self, PyObject *args)
+{
+    char *bus_params[5] = { NULL, NULL, NULL, NULL, NULL };
+    char *drivername;
+    urj_chain_t *urc = self->urchain;
+
+    if (!PyArg_ParseTuple (args, "s|ssss",
+                           &drivername,
+                           &bus_params[0], &bus_params[1], &bus_params[2],
+                           &bus_params[3]))
+    {
+        return NULL;
+    }
+    if (!urj_pyc_precheck (urc, UPRC_CBL|UPRC_DET))
+        return NULL;
+
+    return urj_py_chkret (urj_bus_init (urc, drivername, bus_params));
+}
+
+static PyObject *
+urj_pyc_detectflash (urj_pychain_t *self, PyObject *args)
+{
+    urj_chain_t *urc = self->urchain;
+    int adr;
+    if (!PyArg_ParseTuple (args, "i", &adr))
+        return NULL;
+    if (!urj_pyc_precheck (urc, UPRC_CBL|UPRC_BUS))
+        return NULL;
+
+    return Py_BuildValue ("i",
+                          urj_flash_detectflash (URJ_LOG_LEVEL_NORMAL,
+                                                 urj_bus, adr));
+}
+
+static PyObject *
+urj_pyc_peek (urj_pychain_t *self, PyObject *args)
+{
+    long unsigned adr;
+    uint32_t val;
+    urj_bus_area_t area;
+    urj_chain_t *urc = self->urchain;
+
+    if (!PyArg_ParseTuple (args, "i", &adr))
+        return NULL;
+
+    if (!urj_pyc_precheck (urc, UPRC_CBL|UPRC_BUS))
+        return NULL;
+
+    URJ_BUS_PREPARE (urj_bus);
+    URJ_BUS_AREA (urj_bus, adr, &area);
+    val = URJ_BUS_READ (urj_bus, adr);
+
+    switch (area.width)
+    {
+    case 8:
+        val &= 0xff;
+        break;
+    case 16:
+        val &= 0xffff;
+        break;
+    default:
+        break;
+    }
+    return Py_BuildValue ("i", val); 
+}
+
+static PyObject *
+urj_pyc_poke (urj_pychain_t *self, PyObject *args)
+{
+    long unsigned adr, val;
+    urj_bus_area_t area;
+    urj_chain_t *urc = self->urchain;
+
+    if (!PyArg_ParseTuple (args, "ii", &adr, &val))
+        return NULL;
+
+    if (!urj_pyc_precheck (urc, UPRC_CBL|UPRC_BUS))
+        return NULL;
+
+    URJ_BUS_PREPARE (urj_bus);
+    URJ_BUS_AREA (urj_bus, adr, &area);
+    URJ_BUS_WRITE (urj_bus, adr, val);
+    return Py_BuildValue ("");
+}
+
+static PyObject *
+urj_pyc_flashmem (urj_pychain_t *self, PyObject *args)
+{
+    urj_chain_t *urc = self->urchain;
+    int msbin;
+    int noverify = 0;
+    long unsigned adr = 0;
+    FILE *f;
+    char *optstr = NULL;
+    char *fname = NULL;
+    int r;
+
+    if (!urj_pyc_precheck (urc, UPRC_CBL|UPRC_BUS))
+        return NULL;
+
+    if (!PyArg_ParseTuple
+        (args, "ss|i", &optstr, &fname, &noverify))
+        return NULL;
+
+    msbin = strcasecmp ("msbin", optstr) == 0;
+    if (!msbin && urj_cmd_get_number (optstr, &adr) != URJ_STATUS_OK)
+        return NULL;
+
+    f = fopen (fname, FOPEN_R);
+    if (!f)
+    {
+        PyErr_SetFromErrnoWithFilename(PyExc_IOError, fname);
+        return NULL;
+    }
+
+    if (msbin)
+        r = urj_flashmsbin (urj_bus, f, noverify);
+    else
+        r = urj_flashmem (urj_bus, f, adr, noverify);
+
+    fclose (f);
+    return Py_BuildValue ("i", r);
+}
+
+static PyMethodDef urj_pyc_methods[] = {
+    {"cable", (PyCFunction) urj_pyc_cable, METH_VARARGS,
+     "Connect to the jtag hardware cable of the specified name and type."},
+    {"test_cable", (PyCFunction) urj_pyc_test_cable, METH_NOARGS,
+     "check that the jtag cable is connected to a valid chain"},
+    {"disconnect", (PyCFunction) urj_pyc_disconnect, METH_NOARGS,
+     "Disconnect from the jtag hardware cable"},
+    {"tap_detect", (PyCFunction) urj_pyc_tap_detect, METH_VARARGS,
+     "Identify the chips on the chain"},
+    {"len", (PyCFunction) urj_pyc_len, METH_NOARGS,
+     "Return the length of the TAP chain"},
+    {"reset", (PyCFunction) urj_pyc_reset, METH_NOARGS,
+     "Perform jtag reset using TMS"},
+    {"partid", (PyCFunction) urj_pyc_partid, METH_VARARGS,
+     "Return the IDCODE for the indicated part number in the chain"},
+    {"set_trst", (PyCFunction) urj_pyc_set_trst, METH_VARARGS,
+     "set the TRST output of the cable"},
+    {"get_trst", (PyCFunction) urj_pyc_get_trst, METH_NOARGS,
+     "get the current value of the TRST output of the cable"},
+    {"set_pod_signal", (PyCFunction) urj_pyc_set_pod_signal, METH_VARARGS,
+     "set an auxiliary pod signal"},
+    {"get_pod_signal", (PyCFunction) urj_pyc_get_pod_signal, METH_VARARGS,
+     "get the current value of an auxiliary pod signal"},
+    {"set_frequency", (PyCFunction) urj_pyc_set_frequency, METH_VARARGS,
+     "Change the TCK frequency to be at most the specified value in Hz"},
+    {"get_frequency", (PyCFunction) urj_pyc_get_frequency, METH_NOARGS,
+     "get the current TCK frequency"},
+    {"set_instruction", (PyCFunction) urj_pyc_set_instruction, METH_VARARGS,
+     "Set values in the instruction register holding buffer"},
+    {"shift_ir", (PyCFunction) urj_pyc_shift_ir, METH_NOARGS,
+     "scan values through the instruction register"},
+    {"shift_dr", (PyCFunction) urj_pyc_shift_dr, METH_NOARGS,
+     "scan values through the data register"},
+
+    {"get_dr_in_string", (PyCFunction) urj_pyc_get_str_dr_in, METH_VARARGS,
+     "get bits that will be scanned in on next shift_dr, as string"},
+    {"get_dr_out_string", (PyCFunction) urj_pyc_get_str_dr_out, METH_VARARGS,
+     "retrieve values scanned out from the data registers on the last shift_dr, as string"},
+    {"get_dr_in", (PyCFunction) urj_pyc_get_int_dr_in, METH_VARARGS,
+     "get bits that will be scanned in on next shift_dr, as integer"},
+    {"get_dr_out", (PyCFunction) urj_pyc_get_int_dr_out, METH_VARARGS,
+     "retrieve values scanned out from the data registers on the last shift_dr, as integer"},
+
+    {"set_dr_in", (PyCFunction) urj_pyc_set_dr_in, METH_VARARGS,
+     "set bits that will be scanned in on next shiftdr"},
+    {"set_dr_out", (PyCFunction) urj_pyc_set_dr_out, METH_VARARGS,
+     "set the holding register for values scanned out from the data registers"},
+    {"run_svf", (PyCFunction) urj_pyc_run_svf, METH_VARARGS,
+     "Play a named SVF file; optionally setting stop-on-mismatch and runtest frequency"},
+    {"addpart", (PyCFunction) urj_pyc_addpart, METH_VARARGS,
+     "manually adds parts on the JTAG chain"},
+    {"add_instruction", (PyCFunction) urj_pyc_add_instruction, METH_VARARGS,
+     "manually add intruction to the current part"},
+    {"add_register", (PyCFunction) urj_pyc_add_register, METH_VARARGS,
+     "manually add register to current part on the JTAG chain"},
+    {"part", (PyCFunction) urj_pyc_setpart, METH_VARARGS,
+     "change active part for current JTAG chain"},
+    {"initbus", (PyCFunction) urj_pyc_initbus, METH_VARARGS,
+     "initialize bus driver for active part"},
+    {"detectflash", (PyCFunction) urj_pyc_detectflash, METH_VARARGS,
+     "Detect parameters of flash chips attached to a part"},
+    {"peek", (PyCFunction) urj_pyc_peek, METH_VARARGS,
+     "read a single word"},
+    {"poke", (PyCFunction) urj_pyc_poke, METH_VARARGS,
+     "write a single word"},
+    {"flashmem", (PyCFunction) urj_pyc_flashmem, METH_VARARGS,
+     "burn flash memory with data from a file"},
+    {NULL}                      /* Sentinel */
+};
+
+static PyTypeObject urj_pychain_Type = {
+    PyVarObject_HEAD_INIT (NULL, 0) "urjtag.chain", /* tp_name */
+    sizeof (urj_pychain_t),             /* tp_basicsize */
+    0,                          /* tp_itemsize */
+    (destructor) urj_pyc_dealloc, /* tp_dealloc */
+    0,                          /* tp_print */
+    0,                          /* tp_getattr */
+    0,                          /* tp_setattr */
+    0,                          /* tp_compare */
+    0,                          /* tp_repr */
+    0,                          /* tp_as_number */
+    0,                          /* tp_as_sequence */
+    0,                          /* tp_as_mapping */
+    0,                          /* tp_hash */
+    0,                          /* tp_call */
+    0,                          /* tp_str */
+    0,                          /* tp_getattro */
+    0,                          /* tp_setattro */
+    0,                          /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,   /* tp_flags */
+    "JTAG chain objects",       /* tp_doc */
+    0,                          /* tp_traverse */
+    0,                          /* tp_clear */
+    0,                          /* tp_richcompare */
+    0,                          /* tp_weaklistoffset */
+    0,                          /* tp_iter */
+    0,                          /* tp_iternext */
+    urj_pyc_methods,              /* tp_methods */
+    0,                          /* tp_members */
+    0,                          /* tp_getset */
+    0,                          /* tp_base */
+    0,                          /* tp_dict */
+    0,                          /* tp_descr_get */
+    0,                          /* tp_descr_set */
+    0,                          /* tp_dictoffset */
+    0,                          /* tp_init */
+    0,                          /* tp_alloc */
+    urj_pyc_new,                  /* tp_new */
+};
+
+/************************************************************************
+ * module methods that are not part of any type 
+ */
+
+static PyObject *
+urjtag_loglevel (PyObject *self, PyObject *args)
+{
+    int loglevel; /* TODO: accept string or symbol and map to the enum */
+    if (!PyArg_ParseTuple (args, "i", &loglevel))
+        return NULL;
+    urj_log_state.level = loglevel;
+    return Py_BuildValue ("");
+}
+
+static PyMethodDef module_methods[] = {
+    {"loglevel", urjtag_loglevel, METH_VARARGS,
+     "Set log level of the urjtag library"},
+    {NULL}                      /* Sentinel */
+};
+
+static struct PyModuleDef chain_moduledef = {
+    PyModuleDef_HEAD_INIT,
+    "urjtag",
+    "Python extension module for urjtag",
+    -1,
+    module_methods,
+};
+
+MODINIT_DECL (urjtag)
+{
+    PyObject *m;
+
+    if (PyType_Ready (&urj_pychain_Type) < 0)
+        return MODINIT_ERROR_VAL;
+
+    m = PyModule_Create (&chain_moduledef);
+
+    if (m == NULL)
+        return MODINIT_ERROR_VAL;
+
+    UrjtagError = PyErr_NewException ("urjtag.error", NULL, NULL);
+    Py_INCREF (UrjtagError);
+    PyModule_AddObject (m, "error", UrjtagError);
+
+    PyModule_AddIntMacro(m, URJ_LOG_LEVEL_ALL     );
+    PyModule_AddIntMacro(m, URJ_LOG_LEVEL_COMM    );
+    PyModule_AddIntMacro(m, URJ_LOG_LEVEL_DEBUG   );
+    PyModule_AddIntMacro(m, URJ_LOG_LEVEL_DETAIL  );
+    PyModule_AddIntMacro(m, URJ_LOG_LEVEL_NORMAL  );
+    PyModule_AddIntMacro(m, URJ_LOG_LEVEL_WARNING );
+    PyModule_AddIntMacro(m, URJ_LOG_LEVEL_ERROR   );
+    PyModule_AddIntMacro(m, URJ_LOG_LEVEL_SILENT  );
+
+    PyModule_AddIntMacro(m, URJ_POD_CS_TDI    );
+    PyModule_AddIntMacro(m, URJ_POD_CS_TCK    );
+    PyModule_AddIntMacro(m, URJ_POD_CS_TMS    );
+    PyModule_AddIntMacro(m, URJ_POD_CS_TRST   );
+    PyModule_AddIntMacro(m, URJ_POD_CS_RESET  );
+    PyModule_AddIntMacro(m, URJ_POD_CS_SCK    );
+    PyModule_AddIntMacro(m, URJ_POD_CS_SDA    );
+    PyModule_AddIntMacro(m, URJ_POD_CS_SS     );
+
+    Py_INCREF (&urj_pychain_Type);
+    PyModule_AddObject (m, "chain", (PyObject *) &urj_pychain_Type);
+
+    return MODINIT_SUCCESS_VAL (m);
+}
+
+/* Local Variables: */
+/* mode:c */
+/* comment-column:0 */
+/* c-basic-offset:4 */
+/* space-before-funcall:t */
+/* indent-tabs-mode:nil */
+/* End: */
diff --git a/urjtag/bindings/python/pycompat23.h b/urjtag/bindings/python/pycompat23.h
new file mode 100644
index 0000000..35f322f
--- /dev/null
+++ b/urjtag/bindings/python/pycompat23.h
@@ -0,0 +1,38 @@
+/*
+ * some compatibility macros for python 2  / python 3
+ * by Steve Tell
+ * 
+ * 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.
+ *
+ */
+
+#if PY_MAJOR_VERSION >= 3
+
+#define MODINIT_ERROR_VAL NULL
+#define MODINIT_SUCCESS_VAL(val) val
+#define MODINIT_DECL(name) PyMODINIT_FUNC PyInit_##name(void)
+
+#else  /* assume python 2 */
+
+#ifndef PyMODINIT_FUNC
+#define PyMODINIT_FUNC void
+#endif
+
+#define MODINIT_ERROR_VAL
+#define MODINIT_SUCCESS_VAL(val)
+#define MODINIT_DECL(name) PyMODINIT_FUNC init##name(void)
+
+struct PyModuleDef {
+    int dc1;
+    const char *name;
+    const char *doc;
+    int dc2;
+    PyMethodDef *methods;
+};
+#define PyModuleDef_HEAD_INIT 0
+#define PyModule_Create(dp) Py_InitModule3((dp)->name, (dp)->methods, (dp)->doc)
+
+#endif
diff --git a/urjtag/bindings/python/setup.py b/urjtag/bindings/python/setup.py
new file mode 100644
index 0000000..346ccae
--- /dev/null
+++ b/urjtag/bindings/python/setup.py
@@ -0,0 +1,13 @@
+# python extension setup script for urjtag 
+
+from distutils.core import setup, Extension
+setup(name="urjtag", 
+      version="1.0",
+      description="urJtag Python Bindings",
+      ext_modules=[
+        Extension("urjtag", ["chain.c"],
+                  define_macros=[('HAVE_CONFIG_H', None)],
+                  include_dirs=['../..', '../../include/urjtag'],
+                  library_dirs=['../../src/.libs'],
+                  libraries=['urjtag'])
+         ])
diff --git a/urjtag/bindings/python/t_srst.py b/urjtag/bindings/python/t_srst.py
new file mode 100644
index 0000000..5f86950
--- /dev/null
+++ b/urjtag/bindings/python/t_srst.py
@@ -0,0 +1,49 @@
+#!/usr/bin/python
+
+# works in both python 2 and 3
+def printf(format, *args):
+     """Format args with the first argument as format string, and print.
+     If the format is not a string, it is converted to one with str.
+     You must use printf('%s', x) instead of printf(x) if x might
+     contain % or backslash characters."""
+     sys.stdout.write(str(format) % args)
+
+import sys
+sys.path.append( "." )
+
+import urjtag
+
+#urjtag.loglevel(0) # ALL
+
+urc = urjtag.chain()
+printf("%s\n", urc);
+
+urc.cable("JTAGKey")
+printf("urc.cable done %s\n", urc)
+
+urc.test_cable()
+printf("urc.test_cable done\n")
+
+# trst = urc.get_trst()
+# printf("TRST=%d\n", trst)
+# urc.set_trst(0)
+# trst = urc.get_trst()
+# printf( "TRST set 0 -> %d\n", trst)
+
+# urc.set_trst(1)
+# trst = urc.get_trst()
+# printf("TRST set 1 -> %d\n", trst)
+
+srstbit = 0x10
+srstval = urc.get_pod_signal(srstbit)
+printf( "srstval -> %s\n", srstval);
+
+urc.set_pod_signal(srstbit, 0)
+srstval = urc.get_pod_signal(srstbit)
+printf("srstval set 0 -> %s\n", srstval)
+urc.set_pod_signal(srstbit, 1)
+srstval = urc.get_pod_signal(srstbit)
+printf("srstval set 1 -> %s\n", srstval)
+       
+
+
diff --git a/urjtag/bindings/python/t_urjtag_chain.py b/urjtag/bindings/python/t_urjtag_chain.py
new file mode 100644
index 0000000..d0eaf61
--- /dev/null
+++ b/urjtag/bindings/python/t_urjtag_chain.py
@@ -0,0 +1,81 @@
+#!/usr/bin/python
+
+#
+# A general test and demonstration of several urjtag operations from python.
+#
+
+# works in both python 2 and 3
+def printf(format, *args):
+     """Format args with the first argument as format string, and print.
+     If the format is not a string, it is converted to one with str.
+     You must use printf('%s', x) instead of printf(x) if x might
+     contain % or backslash characters."""
+     sys.stdout.write(str(format) % args)
+
+import sys
+sys.path.append( "." )
+
+import urjtag
+
+#urjtag.loglevel(0) # ALL
+
+urc = urjtag.chain()
+printf("%s\n", urc);
+
+urc.cable("JTAGKey")
+printf("urc.cable done %s\n", urc)
+
+urc.test_cable()
+printf("urc.test_cable done\n")
+
+f = urc.get_frequency()
+printf("frequency was %d Hz\n", f)
+urc.set_frequency(10000)
+f = urc.get_frequency()
+printf("frequency readback = %d Hz\n", f)
+
+trst = urc.get_trst()
+printf("TRST=%d\n", trst)
+urc.set_trst(0)
+trst = urc.get_trst()
+printf( "TRST set 0 -> %d\n", trst)
+urc.set_trst(1)
+trst = urc.get_trst()
+printf("TRST set 1 -> %d\n", trst)
+
+urc.reset();
+
+urc.tap_detect()
+printf("urc.detect done\n")
+printf("chainlength=%d\n", urc.len())
+
+printf("id[0]=%08x\n", urc.partid(0) );
+
+srstbit = 0x10
+srstval = urc.get_pod_signal(srstbit)
+printf( "srstval -> %s\n", srstval);
+
+urc.set_pod_signal(srstbit, 0)
+srstval = urc.get_pod_signal(srstbit)
+printf("srstval set 0 -> %s\n", srstval)
+urc.set_pod_signal(srstbit, 1)
+srstval = urc.get_pod_signal(srstbit)
+printf("srstval set 1 -> %s\n", srstval)
+       
+urc.set_instruction("SAMPLE/PRELOAD")
+urc.shift_ir()
+drval = urc.get_dr_in()
+printf("BSR dr_in result: %s\n", drval)
+urc.shift_dr()
+drval = urc.get_dr_out()
+printf("BSR dr_out result: %s\n", drval)
+
+urc.set_instruction("IDCODE")
+urc.shift_ir()
+urc.shift_dr()
+drval = urc.get_dr_out()
+printf("IDREG dr result: %s\n", drval)
+
+urc.set_instruction("BYPASS")
+urc.shift_ir()
+
diff --git a/urjtag/configure.ac b/urjtag/configure.ac
index ac01572..ea97a7b 100644
--- a/urjtag/configure.ac
+++ b/urjtag/configure.ac
@@ -89,6 +89,8 @@ AC_CONFIG_FILES(
 	src/apps/bsdl2jtag/Makefile
 	src/bfin/Makefile
 	po/Makefile.in
+	bindings/Makefile
+	bindings/python/Makefile
 )
 
 AM_MAINTAINER_MODE
@@ -369,6 +371,36 @@ AS_IF([test "x$enable_apps" = xyes], [
   AM_CONDITIONAL(ENABLE_APPS, false)
 ])
 
+AC_ARG_ENABLE([python], 
+   [AS_HELP_STRING([--enable-python], [build python language bindings for liburjtag])],     
+ [BIND_PYTHON="$enableval"],[BIND_PYTHON="detect"])
+
+if test "x$BIND_PYTHON" != "xno"; then
+    AM_PATH_PYTHON(,, [BIND_PYTHON=no])
+    PYTHON_MAJOR_VERSION=$(echo $PYTHON_VERSION | cut -d. -f1 )
+    PYTHON_MINOR_VERSION=$(echo $PYTHON_VERSION | cut -d. -f2 )
+    if (( $PYTHON_MAJOR_VERSION < 2 )); then
+            if test "x$BIND_PYTHON" = xyes ; then
+               AC_MSG_ERROR([python version less than 2.5])
+                  else
+               AC_MSG_WARN([python version less than 2.5, disabling python binding])
+            fi
+            BIND_PYTHON="no"
+    fi
+
+    AM_CHECK_PYTHON_HEADERS([],[BIND_PYTHON=no;AC_MSG_NOTICE([python headers not found, disabling python binding])])
+
+fi
+
+# if haven't ruled it out by now, we're OK.
+if test "x$BIND_PYTHON" = xdetect ; then
+    BIND_PYTHON=yes
+fi 
+
+AM_CONDITIONAL([BIND_PYTHON], [test "x$BIND_PYTHON" = "xyes"])
+
+# for future use
+#AM_CONDITIONAL([BIND_TCL], false)
 
 # check for lex/flex
 AC_PROG_LEX
@@ -782,4 +814,7 @@ urjtag is now configured for
     Bus        : $enabled_bus_drivers
     Cable      : $enabled_cable_drivers
     Lowlevel   : $enabled_lowlevel_drivers
-])
+
+  Language bindings:
+    python     : $BIND_PYTHON
+])
\ No newline at end of file
diff --git a/urjtag/doc/urjtag-python.txt b/urjtag/doc/urjtag-python.txt
new file mode 100644
index 0000000..2559112
--- /dev/null
+++ b/urjtag/doc/urjtag-python.txt
@@ -0,0 +1,218 @@
+
+Python bindings for liburjtag: a brief introduction.
+
+
+Python bindings will be built automaticly if urjtag's configure
+process detects python and its necessary development libraries.
+
+To require a urjtag build with python, and fail if the python
+prerequesites aren't found, explicitly mention python to configure:
+	      ./configure --enable-python
+
+Configure can also be told to ignore python entirely:
+	      ./configure --disable-python
+
+If the interpreter for your preferred python version is named somthing
+besides python, or is not in your path, you can set $PYTHON in the
+environment before calling configure.  For example, to build
+additional python version 3 bindings in an OS where "python" is
+version 2.7, run:
+
+  PYTHON=python3 ./configure --enable-python
+
+
+
+
+So much for building. In order to use urjtag from python, we must
+import its module into our program or interactive session.
+ 
+ import urjtag 
+
+
+
+About all we can do directly in the urjtag module is change
+the urjag logging verbosity level:
+
+ urjtag.loglevel( urjtag.URJ_LOG_LEVEL_ALL )
+ urjtag.loglevel( urjtag.URJ_LOG_LEVEL_WARNING )
+ urjtag.loglevel( urjtag.URJ_LOG_LEVEL_SILENT )
+
+
+The must fundamental object in urjtag is the chain, and to do anything
+else we need to create one:
+
+ urc = urjtag.chain()
+
+An empty, disconnected chain isn't very useful.  In order to use a
+chain object, we must specify the interface and cable type used to
+connect to it.
+
+ urc.cable("JTAGKey")
+
+Some cables let you query and change the frequency at which they operate:
+
+ f = urc.get_frequency()
+ urc.set_frequency(1000000)  # TCK frequency in Hz
+
+
+To detect what chips are on the chain:
+
+ urc.tap_detect()
+
+After calling detect, we can query how many devices are on the chain,
+and what their IDCODE values are.  The idcode returned by partid() is
+an integer; you'll typicaly want to print it in hex.
+
+ length = urc.len()
+ for i in range(0,urc.len()):
+     idcode = urc.partid(0)
+     print "[%d] 0x%08x" % (i, idcode)
+
+Optionally, urc.tap_detect may be called with an integer argument that
+specifies the maximum combined length of all instruction registers expected to 
+be on the jtag chain:
+   maxirlen = 90
+   urc.tap_detect(maxirlen)
+Specifying a smaller value than the default 1024 can make detect return faster
+if the cable is disconnected or the chain is broken.  Specifying a larger value
+may be necessary for very long jtag chains.
+
+
+Some devices implement the TRST signal:
+
+ urc.set_trst(0)    	   # assert TRST
+ urc.set_trst(1)	   # deassert TRST
+
+
+Other devices and chains use JTAG synchronous reset, using TCK and TMS:
+
+ urc.reset()
+
+Some cables impelment a non-JTAG system reset signal and allow
+directly manipulating other signals:
+ urc.set_pod_signal(urjtag.URJ_POD_CS_RESET, 0)
+ urc.set_pod_signal(urjtag.URJ_POD_CS_RESET, 1)
+ v = urc.get_pod_signal(urjtag.URJ_POD_CS_RESET)
+
+
+
+
+A jtag chain object keeps track of a current instruction and a current part.
+
+ urc.part(0)
+ urc.set_instruction("SAMPLE/PRELOAD")
+ urc.set_instruction("IDCODE")
+ urc.set_instruction("EXTEST")
+
+To shift the instruction stored in the chain object into the actual hardware,
+we must call shift_ir:
+
+ urc.shift_ir()
+
+
+For each data register, the chain object tracks two register values.
+The "input" register value is sent to the device(s) on the TDI pin,
+and changes the device's status in the DR_UPDATE state.  The "output"
+register value is built up of the values observed on the TDO pin.
+The shift_dr method performs a complete data register shift operation:
+
+ inval = urc.get_dr_in_string()
+ print "BSR dr_in", inval
+ urc.shift_dr()
+ outval = urc.get_dr_out_string()
+ printf "BSR dr_out", outval
+
+Both holding registers can be modified:
+
+ s = "1001001001"
+ urc.set_dr_in_string(s)
+ urc.set_dr_out_string(s)
+
+More commonly, you'll want to change only a few bits.  This can be
+done with strings and lists.  Note that the ascii strings of '1' and
+'0' representing binary values print with bit 0 last, the way you'd
+typicaly write a binary number.  But the indexes of entries in a list
+or string count from the left. 
+
+ drval = urc.get_dr_in()
+ drlen=len(drval)
+ drl = list(drval);
+ drl[drlen-1-13] = '1';
+ drl[drlen-1-14] = '0';
+ ndrval = "".join(drl);
+ urc.set_dr_in(ndrval)
+
+But there is an easier way to set a single bit of a register:
+
+ urc.set_dr_out(1, 13);
+ urc.set_dr_out(0, 14);
+
+
+Retrieving and setting contiguous subfields of a register (up to 64
+bits) is also possible:
+
+ urc.set_dr_out(0x0d, 4, 7);
+ r = urc.get_dr_out(4,7); 
+ drval = urc.get_dr_out_string(); 
+ printf("  set_dr_out result: %s %02x\n", drval, r)
+
+
+Remember to call shift_dr() before making use of values read with get_dr_in(),
+and after changing values written with set_dr_out().
+
+
+
+
+
+Sometimes your chip isn't known to urjtag's database.  You can
+create a .jtag file and add it to the database (usually in
+/usr/share/urjtag or /usr/local/share/urjtag).
+Or, you can access it by adding it specialy within your script.
+
+ urc = urjtag.chain()
+ urc.cable("JTAGKey")
+ urc.reset();
+
+ urc.addpart(8); # instruction length of the one chip on your chain.
+
+ urc.add_instruction("CLAMP", "11101110", BYPASS) # another instruction that selects the BYPASS register.  The bypass register always exists.
+
+ urc.add_register("IDREG", 32);  # another register, with its  length
+ urc.add_instruction("IDCODE", "11111110", "IDREG");  # 
+
+Then you can set and scan the new registers:
+
+ urc.set_instruction("IDCODE")
+ urc.shift_ir()
+ urc.shift_dr()
+ print urc.get_dr_out_string()
+
+
+SVF files can be applied to a chain:
+
+ urc.run_svf(filename, stop=0, ref_freq=0)
+
+
+
+Bus support within the python bindings works somewhat like the jtag
+program's interactive mode.  It is not properly object oriented yet.
+
+ urc.initbus("foo", "bar");
+
+FUTURE: urc.initbus will return a urjtag.bus object.  Currently, there is one
+implicit global bus, and only one bus can be active at a time.
+
+
+
+ urc.detectflash(i)
+ v = urc.peek(addr)
+ urc.peek(addr,val)
+ urj.flashmem(options, filename, noverify=0)
+
+FUTURE: detectflash, peek, poke, and flashmem will be a methods in a new
+urjtag.bus class, not urjtag.chain.
+
+
+
+
+
diff --git a/urjtag/include/urjtag/tap_register.h b/urjtag/include/urjtag/tap_register.h
index d70b012..bb9fdbe 100644
--- a/urjtag/include/urjtag/tap_register.h
+++ b/urjtag/include/urjtag/tap_register.h
@@ -42,10 +42,10 @@ void urj_tap_register_free (urj_tap_register_t *tr);
 urj_tap_register_t *urj_tap_register_fill (urj_tap_register_t *tr, int val);
 int urj_tap_register_set_string (urj_tap_register_t *tr, const char *str);
 int urj_tap_register_set_value (urj_tap_register_t *tr, uint64_t val);
-int urj_tap_register_set_field_value (urj_tap_register_t *tr, uint64_t val, int msb, int lsb);
+int urj_tap_register_set_value_bit_range (urj_tap_register_t *tr, uint64_t val, int msb, int lsb);
 const char *urj_tap_register_get_string (const urj_tap_register_t *tr);
 uint64_t urj_tap_register_get_value (const urj_tap_register_t *tr);
-uint64_t urj_tap_register_get_field_value (const urj_tap_register_t *tr, int msb, int lsb);
+uint64_t urj_tap_register_get_value_bit_range (const urj_tap_register_t *tr, int msb, int lsb);
 /** @return 0 or 1 on success; -1 on error */
 int urj_tap_register_all_bits_same_value (const urj_tap_register_t *tr);
 urj_tap_register_t *urj_tap_register_init (urj_tap_register_t *tr,
diff --git a/urjtag/src/tap/discovery.c b/urjtag/src/tap/discovery.c
index e483e19..33597cd 100644
--- a/urjtag/src/tap/discovery.c
+++ b/urjtag/src/tap/discovery.c
@@ -33,7 +33,6 @@
 
 
 #define DETECT_PATTERN_SIZE     8
-#define MIN_REGISTER_LENGTH     2       /* anything less is silly */
 #define DEFAULT_MAX_REGISTER_LENGTH 1024
 #define TEST_COUNT              1
 #define TEST_THRESHOLD          100     /* in % */
@@ -48,7 +47,7 @@ urj_tap_detect_register_size (urj_chain_t *chain, int maxlen)
     urj_tap_register_t *rout;
     urj_tap_register_t *rpat;
 
-    if(maxlen < MIN_REGISTER_LENGTH)
+    if (maxlen == 0)
         maxlen = DEFAULT_MAX_REGISTER_LENGTH;
 
     /* This seems to be a good place to check if TDO changes at all */
diff --git a/urjtag/src/tap/register.c b/urjtag/src/tap/register.c
index 1c050d5..bb9828b 100644
--- a/urjtag/src/tap/register.c
+++ b/urjtag/src/tap/register.c
@@ -143,6 +143,10 @@ urj_tap_register_fill (urj_tap_register_t *tr, int val)
 int
 urj_tap_register_set_string (urj_tap_register_t *tr, const char *str)
 {
+    if (!tr) {
+         urj_error_set (URJ_ERROR_INVALID, _("tr == NULL"));
+         return URJ_STATUS_FAIL;
+    }
     if (strncmp (str, "0x", 2) == 0)
     {
         /* Hex values */
@@ -189,6 +193,10 @@ urj_tap_register_set_value (urj_tap_register_t *tr, uint64_t val)
 {
     unsigned int bit;
 
+    if (!tr) {
+         urj_error_set (URJ_ERROR_INVALID, _("tr == NULL"));
+         return URJ_STATUS_FAIL;
+    }
     if (val >> tr->len)
     {
         urj_error_set (URJ_ERROR_OUT_OF_BOUNDS,
@@ -204,10 +212,15 @@ urj_tap_register_set_value (urj_tap_register_t *tr, uint64_t val)
 }
 
 int
-urj_tap_register_set_field_value (urj_tap_register_t *tr, uint64_t val, int msb, int lsb)
+urj_tap_register_set_value_bit_range (urj_tap_register_t *tr, uint64_t val, int msb, int lsb)
 {
     unsigned int bit;
+    unsigned int step = msb >= lsb ? 1 : -1;
 
+    if (!tr) {
+         urj_error_set (URJ_ERROR_INVALID, _("tr == NULL"));
+         return URJ_STATUS_FAIL;
+    }
     if (msb > tr->len-1 || lsb > tr->len-1 || msb < 0 || lsb < 0)
     {
         urj_error_set (URJ_ERROR_OUT_OF_BOUNDS,
@@ -216,16 +229,9 @@ urj_tap_register_set_field_value (urj_tap_register_t *tr, uint64_t val, int msb,
         return URJ_STATUS_FAIL;
     }
 
-    if(msb >= lsb) {
-        for (bit = lsb; bit <= msb; ++bit) {
-            tr->data[bit] = !!(val & 1);
-            val >>= 1;
-        }
-    } else {
-        for (bit = lsb; bit >= msb; --bit) {
-            tr->data[bit] = !!(val & 1);
-            val >>= 1;
-        }
+    for (bit = lsb; bit <= msb * step; bit += step) {
+        tr->data[bit] = !!(val & 1);
+        val >>= 1;
     }
     return URJ_STATUS_OK;
 }
@@ -266,34 +272,28 @@ urj_tap_register_get_value (const urj_tap_register_t *tr)
 }
 
 uint64_t
-urj_tap_register_get_field_value (const urj_tap_register_t *tr, int msb, int lsb)
+urj_tap_register_get_value_bit_range (const urj_tap_register_t *tr, int msb, int lsb)
 {
     int bit;
     uint64_t l, b;
+    unsigned int step = msb >= lsb ? 1 : -1;
 
     if (!tr)
         return 0;
+    if (msb > tr->len-1 || lsb > tr->len-1 || msb < 0 || lsb < 0)
+        return 0;
 
     l = 0;
     b = 1;
-
-    if(msb >= lsb) {
-        for (bit = lsb; bit <= msb; ++bit) {
-            if (tr->data[bit] & 1)
-                l |= b;
-            b <<= 1;
-        }
-    } else {
-        for (bit = lsb; bit >= msb; --bit) {
-            if (tr->data[bit] & 1)
-                l |= b;
-            b <<= 1;
-        }
+    for (bit = lsb; bit <= msb * step; bit += step) {
+        if (tr->data[bit] & 1)
+            l |= b;
+        b <<= 1;
     }
+
     return l;
 }
 
-
 int
 urj_tap_register_all_bits_same_value (const urj_tap_register_t *tr)
 {
-- 
1.7.4.4


