aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--lib/CMakeLists.txt13
-rw-r--r--lib/python_bindings.c216
-rwxr-xr-xlib/python_bindings_install.sh23
-rw-r--r--lib/setup.py13
-rwxr-xr-xsamples/gpio-pci-idio-16.py8
6 files changed, 275 insertions, 0 deletions
diff --git a/README.md b/README.md
index bf06dd4..cf22b3a 100644
--- a/README.md
+++ b/README.md
@@ -90,6 +90,8 @@ To build and install the library run:
To specify an alternative kernel directory set the KDIR environment variable
accordingly.
+To enable Python bindings set the PYTHON_BINDINGS environment variable to a
+non-empty string.
Finally build your program and link it to libmuser.so.
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 92c1c27..3a0a811 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -48,3 +48,16 @@ install(TARGETS muser
install(DIRECTORY "caps"
DESTINATION ${MUSER_HEADERS_DIR}
FILES_MATCHING PATTERN "*.h")
+
+if (DEFINED ENV{PYTHON_BINDINGS})
+
+ add_custom_target(python_bindings_build ALL
+ COMMAND python setup.py build -b ${CMAKE_BINARY_DIR}
+ DEPENDS python_bindings.c setup.py
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/lib
+ SOURCES python_bindings.c setup.py)
+ # execute_process seems to only execute a single command, e.g. it can't
+ # handle two commands joined by &&
+ install(CODE "execute_process(COMMAND ${CMAKE_SOURCE_DIR}/lib/python_bindings_install.sh ${CMAKE_SOURCE_DIR})")
+
+endif()
diff --git a/lib/python_bindings.c b/lib/python_bindings.c
new file mode 100644
index 0000000..6b13707
--- /dev/null
+++ b/lib/python_bindings.c
@@ -0,0 +1,216 @@
+#include <Python.h>
+#include "muser.h"
+#include <assert.h>
+
+static PyObject *region_access_callbacks[LM_DEV_NUM_REGS] = { 0 };
+
+static int
+handle_read(char *dst, PyObject *result, int count)
+{
+ int i;
+
+ PyArg_Parse(result, "i", &i);
+ Py_DECREF(result);
+ memcpy(dst, &i, count);
+ return 0;
+}
+
+/*
+ * Function callback called by libmuser. We then call the Python function.
+ *
+ * FIXME need a way to provide private pointer.
+ */
+static ssize_t
+region_access_wrap(void *pvt, char * const buf, size_t count, loff_t offset,
+ const bool is_write, int region)
+{
+ PyObject *arglist;
+ PyObject *result = NULL;
+ uint64_t _buf = { 0 };
+
+ if (region_access_callbacks[region] == NULL)
+ {
+ fprintf(stderr, "FIXME no callback for region %d\n", region);
+ return -1;
+ }
+
+ if (is_write)
+ {
+ memcpy(&_buf, buf, count);
+ }
+
+ /* FIXME not sure about type of count and offset */
+ arglist = Py_BuildValue("IKIKi", pvt, _buf, count, offset, is_write);
+ if (arglist == NULL)
+ {
+ fprintf(stderr, "FIXME failed to build func args\n");
+ return -1;
+ }
+ result = PyEval_CallObject(region_access_callbacks[region], arglist);
+ Py_DECREF(arglist);
+ if (result == NULL)
+ {
+ fprintf(stderr, "FIXME failed to call fn for region %d\n", region);
+ return -1;
+ }
+ if (!is_write)
+ {
+ if (handle_read(buf, result, count))
+ {
+ return -1;
+ }
+ }
+ return count;
+}
+
+#define REGION_WRAP(region) \
+ static ssize_t \
+ r_ ## region ## _wrap(void *p, char * const b, size_t c, loff_t o, \
+ const bool w) \
+ { \
+ return region_access_wrap(p, b, c, o, w, region); \
+ }
+
+REGION_WRAP(0)
+REGION_WRAP(1)
+REGION_WRAP(2)
+REGION_WRAP(3)
+REGION_WRAP(4)
+REGION_WRAP(5)
+REGION_WRAP(6)
+REGION_WRAP(7)
+REGION_WRAP(8)
+
+static ssize_t (*region_access_wraps[LM_DEV_NUM_REGS])(void*, char * const, size_t, loff_t, const bool) = {
+ r_0_wrap,
+ r_1_wrap,
+ r_2_wrap,
+ r_3_wrap,
+ r_4_wrap,
+ r_5_wrap,
+ r_6_wrap,
+ r_7_wrap,
+ r_8_wrap};
+
+struct _region_info {
+ char *perm;
+ unsigned int size;
+ PyObject *fn;
+};
+
+static const struct _region_info _0_ri = { 0 };
+
+static PyObject *log_fn = NULL;
+static lm_log_lvl_t log_lvl = LM_ERR;
+
+static void _log_fn(void *pvt, const char *const msg)
+{
+ PyObject *arglist;
+ PyObject *result = NULL;
+
+ arglist = Py_BuildValue("(s)", msg);
+ result = PyEval_CallObject(log_fn, arglist);
+ Py_DECREF(arglist);
+ if (result != NULL)
+ {
+ Py_DECREF(result);
+ }
+}
+
+static PyObject *
+libmuser_run(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = {"vid", "did", "uuid", "log", "log_lvl",
+ "bar0", "bar1", "bar2", "bar3", "bar4", "bar5", "rom", "cfg", "vga",
+ "intx", "msi", "msix", "err", "req",
+ NULL};
+ int err;
+ lm_dev_info_t dev_info = { 0 };
+ int i;
+ struct _region_info _ri[LM_DEV_NUM_REGS] = { 0 };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "HHs|Oi(sIO)(sIO)(sIO)(sIO)(sIO)(sIO)(sIO)(sIO)(sIO)IIIII", kwlist,
+ &dev_info.pci_info.id.vid,
+ &dev_info.pci_info.id.did,
+ &dev_info.uuid,
+ &log_fn, &log_lvl,
+ &_ri[0].perm, &_ri[0].size, &_ri[0].fn,
+ &_ri[1].perm, &_ri[1].size, &_ri[1].fn,
+ &_ri[2].perm, &_ri[2].size, &_ri[2].fn,
+ &_ri[3].perm, &_ri[3].size, &_ri[3].fn,
+ &_ri[4].perm, &_ri[4].size, &_ri[4].fn,
+ &_ri[5].perm, &_ri[5].size, &_ri[5].fn,
+ &_ri[6].perm, &_ri[6].size, &_ri[6].fn,
+ &_ri[7].perm, &_ri[7].size, &_ri[7].fn,
+ &_ri[8].perm, &_ri[8].size, &_ri[8].fn,
+ &dev_info.pci_info.irq_count[0],
+ &dev_info.pci_info.irq_count[1],
+ &dev_info.pci_info.irq_count[2],
+ &dev_info.pci_info.irq_count[3],
+ &dev_info.pci_info.irq_count[4]))
+ {
+ return NULL;
+ }
+
+ for (i = 0; i < LM_DEV_NUM_REGS; i++)
+ {
+ int j;
+ uint32_t flags = 0;
+
+ if (i == LM_DEV_CFG_REG_IDX && !memcmp(&_0_ri, &_ri[i], sizeof _0_ri))
+ {
+ continue;
+ }
+
+ if (_ri[i].perm != NULL)
+ {
+ for (j = 0; j < strlen(_ri[i].perm); j++)
+ {
+ if (_ri[i].perm[j] == 'r')
+ {
+ flags |= LM_REG_FLAG_READ;
+ }
+ else if (_ri[i].perm[j] == 'w')
+ {
+ flags |= LM_REG_FLAG_WRITE;
+ }
+ else
+ {
+ /* FIXME shouldn't print to stderr */
+ fprintf(stderr, "bad permission '%c'\n", _ri[i].perm[j]);
+ return NULL;
+ }
+ }
+ }
+ region_access_callbacks[i] = _ri[i].fn;
+ dev_info.pci_info.reg_info[i].flags = flags;
+ dev_info.pci_info.reg_info[i].size = _ri[i].size;
+ dev_info.pci_info.reg_info[i].fn = region_access_wraps[i];
+ }
+
+ if (log_fn != NULL)
+ {
+ if (!PyCallable_Check(log_fn))
+ {
+ return NULL;
+ }
+ dev_info.log = _log_fn;
+ dev_info.log_lvl = log_lvl;
+ }
+
+ err = lm_ctx_run(&dev_info);
+ return Py_BuildValue("i", err);
+}
+
+static PyMethodDef LibmuserMethods[] = {
+ {"run", (PyCFunction)libmuser_run, METH_VARARGS | METH_KEYWORDS, "runs a device"},
+ {NULL, NULL, 0, NULL}
+};
+
+PyMODINIT_FUNC
+initmuser(void)
+{
+ (void) Py_InitModule("muser", LibmuserMethods);
+}
+
+/* ex: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/lib/python_bindings_install.sh b/lib/python_bindings_install.sh
new file mode 100755
index 0000000..82aae3f
--- /dev/null
+++ b/lib/python_bindings_install.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# 'python setup.py install' expects to find a directory named 'build' with
+# subdirectories 'lib.linux-x86_64-2.7' and 'temp.linux-x86_64-2.7'. If not,
+# then it tries to build, however there's no way to tell it where to put the
+# binary files -- it puts them in the sources directory. These two directories
+# are created during the build phase (by 'python setup.py build') and the
+# binary files are explicitly stored under build/dbg.
+
+# current directory is build/dbg/, move back to build/
+cd ../
+
+# now sylmink dbg to build (parent dir is also build)
+ln -s dbg build
+
+# now symlink'ed dir build contains lib.linux-x86_64-2.7 and
+# temp.linux-x86_64-2.7
+
+# --skip-build seems necessary otherwise 'python setup.py install' to tries to
+# build because source files aren't found (we're under the build directory).
+# We can't simply cd into the source directory and run the install command there
+# because it expects to find the build/ directory there.
+python ${1}/lib/setup.py install --skip-build
diff --git a/lib/setup.py b/lib/setup.py
new file mode 100644
index 0000000..8d63e94
--- /dev/null
+++ b/lib/setup.py
@@ -0,0 +1,13 @@
+from distutils.core import setup, Extension
+
+module1 = Extension('muser',
+ sources = ['python_bindings.c'],
+ #library_dirs=['/usr/local/lib'],
+ libraries=['muser'],
+ #extra_compile_args=['-g', '-O0']
+)
+
+setup (name = 'PackageName',
+ version = '1.0',
+ description = 'This is a demo package',
+ ext_modules = [module1])
diff --git a/samples/gpio-pci-idio-16.py b/samples/gpio-pci-idio-16.py
new file mode 100755
index 0000000..03fa3cb
--- /dev/null
+++ b/samples/gpio-pci-idio-16.py
@@ -0,0 +1,8 @@
+#!/usr/bin/python
+
+def bar2(pvt, buf, count, offset, is_write):
+ if not is_write and int(offset) == 0:
+ return int(input("enter new GPIO value: "))
+
+import muser
+muser.run(vid=0x494F, did=0x0DC8, uuid="00000000-0000-0000-0000-000000000000", bar2=("rw", 0x100, bar2), intx=1)