diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | lib/CMakeLists.txt | 13 | ||||
-rw-r--r-- | lib/python_bindings.c | 216 | ||||
-rwxr-xr-x | lib/python_bindings_install.sh | 23 | ||||
-rw-r--r-- | lib/setup.py | 13 | ||||
-rwxr-xr-x | samples/gpio-pci-idio-16.py | 8 |
6 files changed, 275 insertions, 0 deletions
@@ -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) |