From c5285326555f088e7626dfa67ea7059e094d6e73 Mon Sep 17 00:00:00 2001 From: Michel Zou Date: Tue, 19 Feb 2013 10:59:42 +0100 Subject: [PATCH 1/1] Moved python stuff to own directory (was 'bindings' before) --- CMakeLists.txt | 2 +- bindings/CMakeLists.txt | 62 ------ bindings/doxy2swig.py | 451 ---------------------------------------- bindings/ftdi1.i | 138 ------------ examples/python/complete.py | 115 ---------- examples/python/simple.py | 33 --- python/CMakeLists.txt | 64 ++++++ python/doxy2swig.py | 451 ++++++++++++++++++++++++++++++++++++++++ python/examples/CMakeLists.txt | 5 + python/examples/complete.py | 115 ++++++++++ python/examples/simple.py | 33 +++ python/ftdi1.i | 138 ++++++++++++ 12 files changed, 807 insertions(+), 800 deletions(-) delete mode 100644 bindings/CMakeLists.txt delete mode 100644 bindings/doxy2swig.py delete mode 100644 bindings/ftdi1.i delete mode 100644 examples/python/complete.py delete mode 100644 examples/python/simple.py create mode 100644 python/CMakeLists.txt create mode 100644 python/doxy2swig.py create mode 100644 python/examples/CMakeLists.txt create mode 100644 python/examples/complete.py create mode 100644 python/examples/simple.py create mode 100644 python/ftdi1.i diff --git a/CMakeLists.txt b/CMakeLists.txt index a17b226..e7127f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -141,7 +141,7 @@ endif(DOCUMENTATION AND DOXYGEN_FOUND) add_subdirectory(src) add_subdirectory(ftdipp) -add_subdirectory(bindings) +add_subdirectory(python) add_subdirectory(ftdi_eeprom) add_subdirectory(examples) add_subdirectory(packages) diff --git a/bindings/CMakeLists.txt b/bindings/CMakeLists.txt deleted file mode 100644 index 0c335c8..0000000 --- a/bindings/CMakeLists.txt +++ /dev/null @@ -1,62 +0,0 @@ -option ( PYTHON_BINDINGS "Build python bindings via swig" ON ) - -if ( PYTHON_BINDINGS ) - find_package ( SWIG ) - find_package ( PythonLibs ) - find_package ( PythonInterp ) -endif () - -if ( SWIG_FOUND AND PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND ) - include ( UseSWIG ) - include_directories ( BEFORE ${CMAKE_SOURCE_DIR}/src ) - include_directories ( ${PYTHON_INCLUDE_DIRS} ) - link_directories ( ${CMAKE_CURRENT_BINARY_DIR}/../src ) - - swig_add_module ( ftdi1 python ftdi1.i ) - swig_link_libraries ( ftdi1 ftdi1 ) - - # do not link python modules on debian - # http://www.debian.org/doc/packaging-manuals/python-policy/ch-module_packages.html - if ( NOT UNIX OR ( UNIX AND NOT EXISTS "/etc/debian_version" ) ) - swig_link_libraries ( ftdi1 ${PYTHON_LIBRARIES} ) - endif () - - execute_process ( COMMAND ${PYTHON_EXECUTABLE} -c "import sys; print( '%d.%d' % ( sys.version_info[0], sys.version_info[1] ) )" - OUTPUT_VARIABLE PYTHON_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE ) - - set ( SITEPACKAGE lib${LIB_SUFFIX}/python${PYTHON_VERSION}/site-packages ) - - INSTALL ( FILES ${CMAKE_CURRENT_BINARY_DIR}/_ftdi1.so DESTINATION ${SITEPACKAGE} ) - INSTALL ( FILES ${CMAKE_CURRENT_BINARY_DIR}/ftdi1.py DESTINATION ${SITEPACKAGE} ) - - if ( DOCUMENTATION AND DOXYGEN_FOUND ) - - set(CMAKE_SWIG_FLAGS -DDOXYGEN=${DOXYGEN_FOUND}) - - # Run doxygen to only generate the xml - add_custom_command ( OUTPUT ${CMAKE_BINARY_DIR}/doc/xml/ftdi_8c.xml - COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/doc - COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_BINARY_DIR}/Doxyfile.xml - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS ${c_headers};${c_sources};${cpp_sources};${cpp_headers} - ) - - # generate .i from doxygen .xml - add_custom_command ( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ftdi1_doc.i - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/doxy2swig.py -n - ${CMAKE_BINARY_DIR}/doc/xml/ftdi_8c.xml - ${CMAKE_CURRENT_BINARY_DIR}/ftdi1_doc.i - DEPENDS ${CMAKE_BINARY_DIR}/doc/xml/ftdi_8c.xml - ) - add_custom_target ( doc_i DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/ftdi1_doc.i ) - add_dependencies( ${SWIG_MODULE_ftdi1_REAL_NAME} doc_i ) - - endif () - - set ( LIBFTDI_PYTHON_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/${SITEPACKAGE} ) - set ( LIBFTDI_PYTHON_MODULE_PATH ${LIBFTDI_PYTHON_MODULE_PATH} PARENT_SCOPE ) # for ftdiconfig.cmake - message(STATUS "Building python bindings via swig. Will be installed under ${LIBFTDI_PYTHON_MODULE_PATH}") -else () - message(STATUS "Not building python bindings") -endif () diff --git a/bindings/doxy2swig.py b/bindings/doxy2swig.py deleted file mode 100644 index cf4c52b..0000000 --- a/bindings/doxy2swig.py +++ /dev/null @@ -1,451 +0,0 @@ -#!/usr/bin/env python -"""Doxygen XML to SWIG docstring converter. - -Usage: - - doxy2swig.py [options] input.xml output.i - -Converts Doxygen generated XML files into a file containing docstrings -that can be used by SWIG-1.3.x. Note that you need to get SWIG -version > 1.3.23 or use Robin Dunn's docstring patch to be able to use -the resulting output. - -input.xml is your doxygen generated XML file and output.i is where the -output will be written (the file will be clobbered). - -""" -###################################################################### -# -# This code is implemented using Mark Pilgrim's code as a guideline: -# http://www.faqs.org/docs/diveintopython/kgp_divein.html -# -# Author: Prabhu Ramachandran -# License: BSD style -# -# Thanks: -# Johan Hake: the include_function_definition feature -# Bill Spotz: bug reports and testing. -# Sebastian Henschel: Misc. enhancements. -# -###################################################################### - -from xml.dom import minidom -import re -import textwrap -import sys -import os.path -import optparse - - -def my_open_read(source): - if hasattr(source, "read"): - return source - else: - return open(source) - -def my_open_write(dest): - if hasattr(dest, "write"): - return dest - else: - return open(dest, 'w') - - -class Doxy2SWIG: - """Converts Doxygen generated XML files into a file containing - docstrings that can be used by SWIG-1.3.x that have support for - feature("docstring"). Once the data is parsed it is stored in - self.pieces. - - """ - - def __init__(self, src, include_function_definition=True, quiet=False): - """Initialize the instance given a source object. `src` can - be a file or filename. If you do not want to include function - definitions from doxygen then set - `include_function_definition` to `False`. This is handy since - this allows you to use the swig generated function definition - using %feature("autodoc", [0,1]). - - """ - f = my_open_read(src) - self.my_dir = os.path.dirname(f.name) - self.xmldoc = minidom.parse(f).documentElement - f.close() - - self.pieces = [] - self.pieces.append('\n// File: %s\n'%\ - os.path.basename(f.name)) - - self.space_re = re.compile(r'\s+') - self.lead_spc = re.compile(r'^(%feature\S+\s+\S+\s*?)"\s+(\S)') - self.multi = 0 - self.ignores = ['inheritancegraph', 'param', 'listofallmembers', - 'innerclass', 'name', 'declname', 'incdepgraph', - 'invincdepgraph', 'programlisting', 'type', - 'references', 'referencedby', 'location', - 'collaborationgraph', 'reimplements', - 'reimplementedby', 'derivedcompoundref', - 'basecompoundref'] - #self.generics = [] - self.include_function_definition = include_function_definition - if not include_function_definition: - self.ignores.append('argsstring') - - self.quiet = quiet - - - def generate(self): - """Parses the file set in the initialization. The resulting - data is stored in `self.pieces`. - - """ - self.parse(self.xmldoc) - - def parse(self, node): - """Parse a given node. This function in turn calls the - `parse_` functions which handle the respective - nodes. - - """ - pm = getattr(self, "parse_%s"%node.__class__.__name__) - pm(node) - - def parse_Document(self, node): - self.parse(node.documentElement) - - def parse_Text(self, node): - txt = node.data - txt = txt.replace('\\', r'\\\\') - txt = txt.replace('"', r'\"') - # ignore pure whitespace - m = self.space_re.match(txt) - if m and len(m.group()) == len(txt): - pass - else: - self.add_text(textwrap.fill(txt, break_long_words=False)) - - def parse_Element(self, node): - """Parse an `ELEMENT_NODE`. This calls specific - `do_` handers for different elements. If no handler - is available the `generic_parse` method is called. All - tagNames specified in `self.ignores` are simply ignored. - - """ - name = node.tagName - ignores = self.ignores - if name in ignores: - return - attr = "do_%s" % name - if hasattr(self, attr): - handlerMethod = getattr(self, attr) - handlerMethod(node) - else: - self.generic_parse(node) - #if name not in self.generics: self.generics.append(name) - - def parse_Comment(self, node): - """Parse a `COMMENT_NODE`. This does nothing for now.""" - return - - def add_text(self, value): - """Adds text corresponding to `value` into `self.pieces`.""" - if isinstance(value, (list, tuple)): - self.pieces.extend(value) - else: - self.pieces.append(value) - - def get_specific_nodes(self, node, names): - """Given a node and a sequence of strings in `names`, return a - dictionary containing the names as keys and child - `ELEMENT_NODEs`, that have a `tagName` equal to the name. - - """ - nodes = [(x.tagName, x) for x in node.childNodes \ - if x.nodeType == x.ELEMENT_NODE and \ - x.tagName in names] - return dict(nodes) - - def generic_parse(self, node, pad=0): - """A Generic parser for arbitrary tags in a node. - - Parameters: - - - node: A node in the DOM. - - pad: `int` (default: 0) - - If 0 the node data is not padded with newlines. If 1 it - appends a newline after parsing the childNodes. If 2 it - pads before and after the nodes are processed. Defaults to - 0. - - """ - npiece = 0 - if pad: - npiece = len(self.pieces) - if pad == 2: - self.add_text('\n') - for n in node.childNodes: - self.parse(n) - if pad: - if len(self.pieces) > npiece: - self.add_text('\n') - - def space_parse(self, node): - self.add_text(' ') - self.generic_parse(node) - - do_ref = space_parse - do_emphasis = space_parse - do_bold = space_parse - do_computeroutput = space_parse - do_formula = space_parse - - def do_compoundname(self, node): - self.add_text('\n\n') - data = node.firstChild.data - self.add_text('%%feature("docstring") %s "\n'%data) - - def do_compounddef(self, node): - kind = node.attributes['kind'].value - if kind in ('class', 'struct'): - prot = node.attributes['prot'].value - if prot != 'public': - return - names = ('compoundname', 'briefdescription', - 'detaileddescription', 'includes') - first = self.get_specific_nodes(node, names) - for n in names: - if first.has_key(n): - self.parse(first[n]) - self.add_text(['";','\n']) - for n in node.childNodes: - if n not in first.values(): - self.parse(n) - elif kind in ('file', 'namespace'): - nodes = node.getElementsByTagName('sectiondef') - for n in nodes: - self.parse(n) - - def do_includes(self, node): - self.add_text('C++ includes: ') - self.generic_parse(node, pad=1) - - def do_parameterlist(self, node): - text='unknown' - for key, val in node.attributes.items(): - if key == 'kind': - if val == 'param': text = 'Parameters' - elif val == 'exception': text = 'Exceptions' - elif val == 'retval': text = 'Returns' - else: text = val - break - self.add_text(['\n', '\n', text, ':', '\n']) - self.generic_parse(node, pad=1) - - def do_para(self, node): - self.add_text('\n') - self.generic_parse(node, pad=1) - - def do_parametername(self, node): - self.add_text('\n') - try: - data=node.firstChild.data - except AttributeError: # perhaps a tag in it - data=node.firstChild.firstChild.data - if data.find('Exception') != -1: - self.add_text(data) - else: - self.add_text("%s: "%data) - - def do_parameterdefinition(self, node): - self.generic_parse(node, pad=1) - - def do_detaileddescription(self, node): - self.generic_parse(node, pad=1) - - def do_briefdescription(self, node): - self.generic_parse(node, pad=1) - - def do_memberdef(self, node): - prot = node.attributes['prot'].value - id = node.attributes['id'].value - kind = node.attributes['kind'].value - tmp = node.parentNode.parentNode.parentNode - compdef = tmp.getElementsByTagName('compounddef')[0] - cdef_kind = compdef.attributes['kind'].value - - if prot == 'public': - first = self.get_specific_nodes(node, ('definition', 'name')) - name = first['name'].firstChild.data - if name[:8] == 'operator': # Don't handle operators yet. - return - - if not 'definition' in first or \ - kind in ['variable', 'typedef']: - return - - if self.include_function_definition: - defn = first['definition'].firstChild.data - else: - defn = "" - self.add_text('\n') - self.add_text('%feature("docstring") ') - - anc = node.parentNode.parentNode - if cdef_kind in ('file', 'namespace'): - ns_node = anc.getElementsByTagName('innernamespace') - if not ns_node and cdef_kind == 'namespace': - ns_node = anc.getElementsByTagName('compoundname') - if ns_node: - ns = ns_node[0].firstChild.data - self.add_text(' %s::%s "\n%s'%(ns, name, defn)) - else: - self.add_text(' %s "\n%s'%(name, defn)) - elif cdef_kind in ('class', 'struct'): - # Get the full function name. - anc_node = anc.getElementsByTagName('compoundname') - cname = anc_node[0].firstChild.data - self.add_text(' %s::%s "\n%s'%(cname, name, defn)) - - for n in node.childNodes: - if n not in first.values(): - self.parse(n) - self.add_text(['";', '\n']) - - def do_definition(self, node): - data = node.firstChild.data - self.add_text('%s "\n%s'%(data, data)) - - def do_sectiondef(self, node): - kind = node.attributes['kind'].value - if kind in ('public-func', 'func', 'user-defined', ''): - self.generic_parse(node) - - def do_header(self, node): - """For a user defined section def a header field is present - which should not be printed as such, so we comment it in the - output.""" - data = node.firstChild.data - self.add_text('\n/*\n %s \n*/\n'%data) - # If our immediate sibling is a 'description' node then we - # should comment that out also and remove it from the parent - # node's children. - parent = node.parentNode - idx = parent.childNodes.index(node) - if len(parent.childNodes) >= idx + 2: - nd = parent.childNodes[idx+2] - if nd.nodeName == 'description': - nd = parent.removeChild(nd) - self.add_text('\n/*') - self.generic_parse(nd) - self.add_text('\n*/\n') - - def do_simplesect(self, node): - kind = node.attributes['kind'].value - if kind in ('date', 'rcs', 'version'): - pass - elif kind == 'warning': - self.add_text(['\n', 'WARNING: ']) - self.generic_parse(node) - elif kind == 'see': - self.add_text('\n') - self.add_text('See: ') - self.generic_parse(node) - else: - self.generic_parse(node) - - def do_argsstring(self, node): - self.generic_parse(node, pad=1) - - def do_member(self, node): - kind = node.attributes['kind'].value - refid = node.attributes['refid'].value - if kind == 'function' and refid[:9] == 'namespace': - self.generic_parse(node) - - def do_doxygenindex(self, node): - self.multi = 1 - comps = node.getElementsByTagName('compound') - for c in comps: - refid = c.attributes['refid'].value - fname = refid + '.xml' - if not os.path.exists(fname): - fname = os.path.join(self.my_dir, fname) - if not self.quiet: - print( "parsing file: %s"%fname ) - p = Doxy2SWIG(fname, self.include_function_definition, self.quiet) - p.generate() - self.pieces.extend(self.clean_pieces(p.pieces)) - - def write(self, fname): - o = my_open_write(fname) - if self.multi: - o.write("".join(self.pieces)) - else: - o.write("".join(self.clean_pieces(self.pieces))) - o.close() - - def clean_pieces(self, pieces): - """Cleans the list of strings given as `pieces`. It replaces - multiple newlines by a maximum of 2 and returns a new list. - It also wraps the paragraphs nicely. - - """ - ret = [] - count = 0 - for i in pieces: - if i == '\n': - count = count + 1 - else: - if i == '";': - if count: - ret.append('\n') - elif count > 2: - ret.append('\n\n') - elif count: - ret.append('\n'*count) - count = 0 - ret.append(i) - - _data = "".join(ret) - ret = [] - for i in _data.split('\n\n'): - if i == 'Parameters:' or i == 'Exceptions:' or i == 'Returns:': - ret.extend([i, '\n'+'-'*len(i), '\n\n']) - elif i.find('// File:') > -1: # leave comments alone. - ret.extend([i, '\n']) - else: - _tmp = textwrap.fill(i.strip(), break_long_words=False) - _tmp = self.lead_spc.sub(r'\1"\2', _tmp) - ret.extend([_tmp, '\n\n']) - return ret - - -def convert(input, output, include_function_definition=True, quiet=False): - p = Doxy2SWIG(input, include_function_definition, quiet) - p.generate() - p.write(output) - -def main(): - usage = __doc__ - parser = optparse.OptionParser(usage) - parser.add_option("-n", '--no-function-definition', - action='store_true', - default=False, - dest='func_def', - help='do not include doxygen function definitions') - parser.add_option("-q", '--quiet', - action='store_true', - default=False, - dest='quiet', - help='be quiet and minimize output') - - options, args = parser.parse_args() - if len(args) != 2: - parser.error("error: no input and output specified") - - convert(args[0], args[1], not options.func_def, options.quiet) - - -if __name__ == '__main__': - main() diff --git a/bindings/ftdi1.i b/bindings/ftdi1.i deleted file mode 100644 index 1e820dd..0000000 --- a/bindings/ftdi1.i +++ /dev/null @@ -1,138 +0,0 @@ -/* File: ftdi1.i */ - -%module(docstring="Python interface to libftdi1") ftdi1 -%feature("autodoc","1"); - -#ifdef DOXYGEN -%include "ftdi1_doc.i" -#endif - -%{ -#include "Python.h" - -PyObject* convertString( const char *v, Py_ssize_t len ) -{ -#if PY_MAJOR_VERSION >= 3 - return PyBytes_FromStringAndSize(v, len); -#else - return PyString_FromStringAndSize(v, len); -#endif -} -%} - -%include -%include - -%typemap(in) unsigned char* = char*; - -%immutable ftdi_version_info::version_str; -%immutable ftdi_version_info::snapshot_str; - -%rename("%(strip:[ftdi_])s") ""; - -%newobject ftdi_new; -%typemap(newfree) struct ftdi_context *ftdi "ftdi_free($1);"; -%delobject ftdi_free; - -%define ftdi_usb_find_all_docstring -"usb_find_all(context, vendor, product) -> (return_code, devlist)" -%enddef -%feature("autodoc", ftdi_usb_find_all_docstring) ftdi_usb_find_all; -%typemap(in,numinputs=0) SWIGTYPE** OUTPUT ($*ltype temp) %{ $1 = &temp; %} -%typemap(argout) SWIGTYPE** OUTPUT %{ $result = SWIG_Python_AppendOutput($result, SWIG_NewPointerObj((void*)*$1,$*descriptor,0)); %} -%apply SWIGTYPE** OUTPUT { struct ftdi_device_list **devlist }; - int ftdi_usb_find_all(struct ftdi_context *ftdi, struct ftdi_device_list **devlist, - int vendor, int product); -%clear struct ftdi_device_list **devlist; - -%define ftdi_usb_get_strings_docstring -"usb_get_strings(context, device) -> (return_code, manufacturer, description, serial)" -%enddef -%feature("autodoc", ftdi_usb_get_strings_docstring) ftdi_usb_get_strings; -%apply char *OUTPUT { char * manufacturer, char * description, char * serial }; -%cstring_bounded_output( char * manufacturer, 256 ); -%cstring_bounded_output( char * description, 256 ); -%cstring_bounded_output( char * serial, 256 ); -%typemap(default,noblock=1) int mnf_len, int desc_len, int serial_len { $1 = 256; } - int ftdi_usb_get_strings(struct ftdi_context *ftdi, struct libusb_device *dev, - char * manufacturer, int mnf_len, - char * description, int desc_len, - char * serial, int serial_len); -%clear char * manufacturer, char * description, char * serial; -%clear int mnf_len, int desc_len, int serial_len; - -%define ftdi_read_data_docstring -"read_data(context) -> (return_code, buf)" -%enddef -%feature("autodoc", ftdi_read_data_docstring) ftdi_read_data; -%typemap(in,numinputs=1) (unsigned char *buf, int size) %{ $2 = PyInt_AsLong($input);$1 = (unsigned char*)malloc($2*sizeof(char)); %} -%typemap(argout) (unsigned char *buf, int size) %{ if(result<0) $2=0; $result = SWIG_Python_AppendOutput($result, convertString((char*)$1, $2)); free($1); %} - int ftdi_read_data(struct ftdi_context *ftdi, unsigned char *buf, int size); -%clear (unsigned char *buf, int size); - -%apply int *OUTPUT { unsigned int *chunksize }; - int ftdi_read_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunksize); - int ftdi_write_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunksize); -%clear unsigned int *chunksize; - -%define ftdi_read_pins_docstring -"read_pins(context) -> (return_code, pins)" -%enddef -%feature("autodoc", ftdi_read_pins_docstring) ftdi_read_pins; -%typemap(in,numinputs=0) unsigned char *pins ($*ltype temp) %{ $1 = &temp; %} -%typemap(argout) (unsigned char *pins) %{ $result = SWIG_Python_AppendOutput($result, convertString((char*)$1, 1)); %} - int ftdi_read_pins(struct ftdi_context *ftdi, unsigned char *pins); -%clear unsigned char *pins; - -%typemap(in,numinputs=0) unsigned char *latency ($*ltype temp) %{ $1 = &temp; %} -%typemap(argout) (unsigned char *latency) %{ $result = SWIG_Python_AppendOutput($result, convertString((char*)$1, 1)); %} - int ftdi_get_latency_timer(struct ftdi_context *ftdi, unsigned char *latency); -%clear unsigned char *latency; - -%apply short *OUTPUT { unsigned short *status }; - int ftdi_poll_modem_status(struct ftdi_context *ftdi, unsigned short *status); -%clear unsigned short *status; - -%apply int *OUTPUT { int* value }; - int ftdi_get_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value value_name, int* value); -%clear int* value; - -%typemap(in,numinputs=1) (unsigned char *buf, int size) %{ $2 = PyInt_AsLong($input);$1 = (unsigned char*)malloc($2*sizeof(char)); %} -%typemap(argout) (unsigned char *buf, int size) %{ if(result<0) $2=0; $result = SWIG_Python_AppendOutput($result, convertString((char*)$1, $2)); free($1); %} - int ftdi_get_eeprom_buf(struct ftdi_context *ftdi, unsigned char * buf, int size); -%clear (unsigned char *buf, int size); - -%define ftdi_read_eeprom_location_docstring -"read_eeprom_location(context, eeprom_addr) -> (return_code, eeprom_val)" -%enddef -%feature("autodoc", ftdi_read_eeprom_location_docstring) ftdi_read_eeprom_location; -%apply short *OUTPUT { unsigned short *eeprom_val }; - int ftdi_read_eeprom_location (struct ftdi_context *ftdi, int eeprom_addr, unsigned short *eeprom_val); -%clear unsigned short *eeprom_val; - -%define ftdi_read_eeprom_docstring -"read_eeprom(context) -> (return_code, eeprom)" -%enddef -%feature("autodoc", ftdi_read_eeprom_docstring) ftdi_read_eeprom; - -%define ftdi_read_chipid_docstring -"ftdi_read_chipid(context) -> (return_code, chipid)" -%enddef -%feature("autodoc", ftdi_read_chipid_docstring) ftdi_read_chipid; -%apply int *OUTPUT { unsigned int *chipid }; - int ftdi_read_chipid(struct ftdi_context *ftdi, unsigned int *chipid); -%clear unsigned int *chipid; - -%include ftdi.h -%{ -#include -%} - -%include ftdi_i.h -%{ -#include -%} - -%pythoncode %{ -__version__ = get_library_version().version_str -%} diff --git a/examples/python/complete.py b/examples/python/complete.py deleted file mode 100644 index afa59fa..0000000 --- a/examples/python/complete.py +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -"""Python example program. - -Complete program to demonstrate the usage -of the swig generated python wrapper - -You need to build and install the wrapper first""" - -import os -import sys -import ftdi1 as ftdi -import time - -# version -print ( 'version: %s\n' % ftdi.__version__ ) - -# initialize -ftdic = ftdi.new() -if ftdic == 0: - print( 'new failed: %d' % ret ) - os._exit( 1 ) - -# try to list ftdi devices 0x6010 or 0x6001 -ret, devlist = ftdi.usb_find_all( ftdic, 0x0403, 0x6010 ) -if ret <= 0: - ret, devlist = ftdi.usb_find_all( ftdic, 0x0403, 0x6001) - -if ret < 0: - print( 'ftdi_usb_find_all failed: %d (%s)' % ( ret, ftdi.get_error_string( ftdic ) ) ) - os._exit( 1 ) -print( 'devices: %d' % ret ) -curnode = devlist -i = 0 -while( curnode != None ): - ret, manufacturer, description, serial = ftdi.usb_get_strings( ftdic, curnode.dev ) - if ret < 0: - print( 'ftdi_usb_get_strings failed: %d (%s)' % ( ret, ftdi.get_error_string( ftdic ) ) ) - os._exit( 1 ) - print( '#%d: manufacturer="%s" description="%s" serial="%s"\n' % ( i, manufacturer, description, serial ) ) - curnode = curnode.next - i += 1 - -# open usb -ret = ftdi.usb_open( ftdic, 0x0403, 0x6001 ) -if ret < 0: - print( 'unable to open ftdi device: %d (%s)' % ( ret, ftdi.get_error_string( ftdic ) ) ) - os._exit( 1 ) - - -# bitbang -ret = ftdi.set_bitmode( ftdic, 0xff, ftdi.BITMODE_BITBANG ) -if ret < 0: - print( 'Cannot enable bitbang' ) - os._exit( 1 ) -print( 'turning everything on' ) -ftdi.write_data( ftdic, chr(0xff), 1 ) -time.sleep( 1 ) -print( 'turning everything off\n' ) -ftdi.write_data( ftdic, chr(0x00), 1 ) -time.sleep( 1 ) -for i in range( 8 ): - val = 2**i - print( 'enabling bit #%d (0x%02x)' % (i, val) ) - ftdi.write_data( ftdic, chr(val), 1 ) - time.sleep ( 1 ) -ftdi.disable_bitbang( ftdic ) -print( '' ) - - -# read pins -ret, pins = ftdi.read_pins( ftdic ) -if ( ret == 0 ): - if sys.version_info[0] < 3: # python 2 - pins = ord( pins ) - else: - pins = pins[0] - print( 'pins: 0x%x' % pins ) - - -# read chip id -ret, chipid = ftdi.read_chipid( ftdic ) -if (ret==0): - print( 'chip id: %x\n' % chipid ) - - -# read eeprom -eeprom_addr = 1 -ret, eeprom_val = ftdi.read_eeprom_location( ftdic, eeprom_addr ) -if (ret==0): - print( 'eeprom @ %d: 0x%04x\n' % ( eeprom_addr, eeprom_val ) ) - -print( 'eeprom:' ) -ret = ftdi.read_eeprom( ftdic ) -size = 128 -ret, eeprom = ftdi.get_eeprom_buf ( ftdic, size ) -if ( ret == 0 ): - for i in range( size ): - octet = eeprom[i] - if sys.version_info[0] < 3: # python 2 - octet = ord( octet ) - sys.stdout.write( '%02x ' % octet ) - if ( i % 8 == 7 ): - print( '' ) -print( '' ) - -# close usb -ret = ftdi.usb_close( ftdic ) -if ret < 0: - print( 'unable to close ftdi device: %d (%s)' % ( ret, ftdi.get_error_string( ftdic ) ) ) - os._exit( 1 ) - -print ('device closed') -ftdi.free( ftdic ) diff --git a/examples/python/simple.py b/examples/python/simple.py deleted file mode 100644 index 9298c24..0000000 --- a/examples/python/simple.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -"""Python example program. - -Small program to demonstrate the usage -of the swig generated python wrapper - -You need to build and install the wrapper first""" - -import ftdi1 as ftdi - -def main(): - """Main program""" - context = ftdi.new() - - version_info = ftdi.get_library_version() - print("[FTDI version] major: %d, minor: %d, micro: %d" \ - ", version_str: %s, snapshot_str: %s" % - (version_info.major, version_info.minor, version_info.micro, - version_info.version_str, version_info.snapshot_str)) - - # try to open an ftdi 0x6010 or 0x6001 - ret = ftdi.usb_open(context, 0x0403, 0x6010) - if ret < 0: - ret = ftdi.usb_open(context, 0x0403, 0x6001) - - print("ftdi.usb_open(): %d" % ret) - print("ftdi.set_baudrate(): %d" % ftdi.set_baudrate(context, 9600)) - - ftdi.free(context) - -main() diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt new file mode 100644 index 0000000..5a61bc1 --- /dev/null +++ b/python/CMakeLists.txt @@ -0,0 +1,64 @@ +option ( PYTHON_BINDINGS "Build python bindings via swig" ON ) + +if ( PYTHON_BINDINGS ) + find_package ( SWIG ) + find_package ( PythonLibs ) + find_package ( PythonInterp ) +endif () + +if ( SWIG_FOUND AND PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND ) + include ( UseSWIG ) + include_directories ( BEFORE ${CMAKE_SOURCE_DIR}/src ) + include_directories ( ${PYTHON_INCLUDE_DIRS} ) + link_directories ( ${CMAKE_CURRENT_BINARY_DIR}/../src ) + + swig_add_module ( ftdi1 python ftdi1.i ) + swig_link_libraries ( ftdi1 ftdi1 ) + + # do not link python modules on debian + # http://www.debian.org/doc/packaging-manuals/python-policy/ch-module_packages.html + if ( NOT UNIX OR ( UNIX AND NOT EXISTS "/etc/debian_version" ) ) + swig_link_libraries ( ftdi1 ${PYTHON_LIBRARIES} ) + endif () + + execute_process ( COMMAND ${PYTHON_EXECUTABLE} -c "import sys; print( '%d.%d' % ( sys.version_info[0], sys.version_info[1] ) )" + OUTPUT_VARIABLE PYTHON_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE ) + + set ( SITEPACKAGE lib${LIB_SUFFIX}/python${PYTHON_VERSION}/site-packages ) + + INSTALL ( FILES ${CMAKE_CURRENT_BINARY_DIR}/_ftdi1.so DESTINATION ${SITEPACKAGE} ) + INSTALL ( FILES ${CMAKE_CURRENT_BINARY_DIR}/ftdi1.py DESTINATION ${SITEPACKAGE} ) + + if ( DOCUMENTATION AND DOXYGEN_FOUND ) + + set(CMAKE_SWIG_FLAGS -DDOXYGEN=${DOXYGEN_FOUND}) + + # Run doxygen to only generate the xml + add_custom_command ( OUTPUT ${CMAKE_BINARY_DIR}/doc/xml/ftdi_8c.xml + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/doc + COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_BINARY_DIR}/Doxyfile.xml + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS ${c_headers};${c_sources};${cpp_sources};${cpp_headers} + ) + + # generate .i from doxygen .xml + add_custom_command ( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ftdi1_doc.i + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/doxy2swig.py -n + ${CMAKE_BINARY_DIR}/doc/xml/ftdi_8c.xml + ${CMAKE_CURRENT_BINARY_DIR}/ftdi1_doc.i + DEPENDS ${CMAKE_BINARY_DIR}/doc/xml/ftdi_8c.xml + ) + add_custom_target ( doc_i DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/ftdi1_doc.i ) + add_dependencies( ${SWIG_MODULE_ftdi1_REAL_NAME} doc_i ) + + endif () + + set ( LIBFTDI_PYTHON_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/${SITEPACKAGE} ) + set ( LIBFTDI_PYTHON_MODULE_PATH ${LIBFTDI_PYTHON_MODULE_PATH} PARENT_SCOPE ) # for ftdiconfig.cmake + message(STATUS "Building python bindings via swig. Will be installed under ${LIBFTDI_PYTHON_MODULE_PATH}") + + add_subdirectory ( examples ) +else () + message(STATUS "Not building python bindings") +endif () diff --git a/python/doxy2swig.py b/python/doxy2swig.py new file mode 100644 index 0000000..cf4c52b --- /dev/null +++ b/python/doxy2swig.py @@ -0,0 +1,451 @@ +#!/usr/bin/env python +"""Doxygen XML to SWIG docstring converter. + +Usage: + + doxy2swig.py [options] input.xml output.i + +Converts Doxygen generated XML files into a file containing docstrings +that can be used by SWIG-1.3.x. Note that you need to get SWIG +version > 1.3.23 or use Robin Dunn's docstring patch to be able to use +the resulting output. + +input.xml is your doxygen generated XML file and output.i is where the +output will be written (the file will be clobbered). + +""" +###################################################################### +# +# This code is implemented using Mark Pilgrim's code as a guideline: +# http://www.faqs.org/docs/diveintopython/kgp_divein.html +# +# Author: Prabhu Ramachandran +# License: BSD style +# +# Thanks: +# Johan Hake: the include_function_definition feature +# Bill Spotz: bug reports and testing. +# Sebastian Henschel: Misc. enhancements. +# +###################################################################### + +from xml.dom import minidom +import re +import textwrap +import sys +import os.path +import optparse + + +def my_open_read(source): + if hasattr(source, "read"): + return source + else: + return open(source) + +def my_open_write(dest): + if hasattr(dest, "write"): + return dest + else: + return open(dest, 'w') + + +class Doxy2SWIG: + """Converts Doxygen generated XML files into a file containing + docstrings that can be used by SWIG-1.3.x that have support for + feature("docstring"). Once the data is parsed it is stored in + self.pieces. + + """ + + def __init__(self, src, include_function_definition=True, quiet=False): + """Initialize the instance given a source object. `src` can + be a file or filename. If you do not want to include function + definitions from doxygen then set + `include_function_definition` to `False`. This is handy since + this allows you to use the swig generated function definition + using %feature("autodoc", [0,1]). + + """ + f = my_open_read(src) + self.my_dir = os.path.dirname(f.name) + self.xmldoc = minidom.parse(f).documentElement + f.close() + + self.pieces = [] + self.pieces.append('\n// File: %s\n'%\ + os.path.basename(f.name)) + + self.space_re = re.compile(r'\s+') + self.lead_spc = re.compile(r'^(%feature\S+\s+\S+\s*?)"\s+(\S)') + self.multi = 0 + self.ignores = ['inheritancegraph', 'param', 'listofallmembers', + 'innerclass', 'name', 'declname', 'incdepgraph', + 'invincdepgraph', 'programlisting', 'type', + 'references', 'referencedby', 'location', + 'collaborationgraph', 'reimplements', + 'reimplementedby', 'derivedcompoundref', + 'basecompoundref'] + #self.generics = [] + self.include_function_definition = include_function_definition + if not include_function_definition: + self.ignores.append('argsstring') + + self.quiet = quiet + + + def generate(self): + """Parses the file set in the initialization. The resulting + data is stored in `self.pieces`. + + """ + self.parse(self.xmldoc) + + def parse(self, node): + """Parse a given node. This function in turn calls the + `parse_` functions which handle the respective + nodes. + + """ + pm = getattr(self, "parse_%s"%node.__class__.__name__) + pm(node) + + def parse_Document(self, node): + self.parse(node.documentElement) + + def parse_Text(self, node): + txt = node.data + txt = txt.replace('\\', r'\\\\') + txt = txt.replace('"', r'\"') + # ignore pure whitespace + m = self.space_re.match(txt) + if m and len(m.group()) == len(txt): + pass + else: + self.add_text(textwrap.fill(txt, break_long_words=False)) + + def parse_Element(self, node): + """Parse an `ELEMENT_NODE`. This calls specific + `do_` handers for different elements. If no handler + is available the `generic_parse` method is called. All + tagNames specified in `self.ignores` are simply ignored. + + """ + name = node.tagName + ignores = self.ignores + if name in ignores: + return + attr = "do_%s" % name + if hasattr(self, attr): + handlerMethod = getattr(self, attr) + handlerMethod(node) + else: + self.generic_parse(node) + #if name not in self.generics: self.generics.append(name) + + def parse_Comment(self, node): + """Parse a `COMMENT_NODE`. This does nothing for now.""" + return + + def add_text(self, value): + """Adds text corresponding to `value` into `self.pieces`.""" + if isinstance(value, (list, tuple)): + self.pieces.extend(value) + else: + self.pieces.append(value) + + def get_specific_nodes(self, node, names): + """Given a node and a sequence of strings in `names`, return a + dictionary containing the names as keys and child + `ELEMENT_NODEs`, that have a `tagName` equal to the name. + + """ + nodes = [(x.tagName, x) for x in node.childNodes \ + if x.nodeType == x.ELEMENT_NODE and \ + x.tagName in names] + return dict(nodes) + + def generic_parse(self, node, pad=0): + """A Generic parser for arbitrary tags in a node. + + Parameters: + + - node: A node in the DOM. + - pad: `int` (default: 0) + + If 0 the node data is not padded with newlines. If 1 it + appends a newline after parsing the childNodes. If 2 it + pads before and after the nodes are processed. Defaults to + 0. + + """ + npiece = 0 + if pad: + npiece = len(self.pieces) + if pad == 2: + self.add_text('\n') + for n in node.childNodes: + self.parse(n) + if pad: + if len(self.pieces) > npiece: + self.add_text('\n') + + def space_parse(self, node): + self.add_text(' ') + self.generic_parse(node) + + do_ref = space_parse + do_emphasis = space_parse + do_bold = space_parse + do_computeroutput = space_parse + do_formula = space_parse + + def do_compoundname(self, node): + self.add_text('\n\n') + data = node.firstChild.data + self.add_text('%%feature("docstring") %s "\n'%data) + + def do_compounddef(self, node): + kind = node.attributes['kind'].value + if kind in ('class', 'struct'): + prot = node.attributes['prot'].value + if prot != 'public': + return + names = ('compoundname', 'briefdescription', + 'detaileddescription', 'includes') + first = self.get_specific_nodes(node, names) + for n in names: + if first.has_key(n): + self.parse(first[n]) + self.add_text(['";','\n']) + for n in node.childNodes: + if n not in first.values(): + self.parse(n) + elif kind in ('file', 'namespace'): + nodes = node.getElementsByTagName('sectiondef') + for n in nodes: + self.parse(n) + + def do_includes(self, node): + self.add_text('C++ includes: ') + self.generic_parse(node, pad=1) + + def do_parameterlist(self, node): + text='unknown' + for key, val in node.attributes.items(): + if key == 'kind': + if val == 'param': text = 'Parameters' + elif val == 'exception': text = 'Exceptions' + elif val == 'retval': text = 'Returns' + else: text = val + break + self.add_text(['\n', '\n', text, ':', '\n']) + self.generic_parse(node, pad=1) + + def do_para(self, node): + self.add_text('\n') + self.generic_parse(node, pad=1) + + def do_parametername(self, node): + self.add_text('\n') + try: + data=node.firstChild.data + except AttributeError: # perhaps a tag in it + data=node.firstChild.firstChild.data + if data.find('Exception') != -1: + self.add_text(data) + else: + self.add_text("%s: "%data) + + def do_parameterdefinition(self, node): + self.generic_parse(node, pad=1) + + def do_detaileddescription(self, node): + self.generic_parse(node, pad=1) + + def do_briefdescription(self, node): + self.generic_parse(node, pad=1) + + def do_memberdef(self, node): + prot = node.attributes['prot'].value + id = node.attributes['id'].value + kind = node.attributes['kind'].value + tmp = node.parentNode.parentNode.parentNode + compdef = tmp.getElementsByTagName('compounddef')[0] + cdef_kind = compdef.attributes['kind'].value + + if prot == 'public': + first = self.get_specific_nodes(node, ('definition', 'name')) + name = first['name'].firstChild.data + if name[:8] == 'operator': # Don't handle operators yet. + return + + if not 'definition' in first or \ + kind in ['variable', 'typedef']: + return + + if self.include_function_definition: + defn = first['definition'].firstChild.data + else: + defn = "" + self.add_text('\n') + self.add_text('%feature("docstring") ') + + anc = node.parentNode.parentNode + if cdef_kind in ('file', 'namespace'): + ns_node = anc.getElementsByTagName('innernamespace') + if not ns_node and cdef_kind == 'namespace': + ns_node = anc.getElementsByTagName('compoundname') + if ns_node: + ns = ns_node[0].firstChild.data + self.add_text(' %s::%s "\n%s'%(ns, name, defn)) + else: + self.add_text(' %s "\n%s'%(name, defn)) + elif cdef_kind in ('class', 'struct'): + # Get the full function name. + anc_node = anc.getElementsByTagName('compoundname') + cname = anc_node[0].firstChild.data + self.add_text(' %s::%s "\n%s'%(cname, name, defn)) + + for n in node.childNodes: + if n not in first.values(): + self.parse(n) + self.add_text(['";', '\n']) + + def do_definition(self, node): + data = node.firstChild.data + self.add_text('%s "\n%s'%(data, data)) + + def do_sectiondef(self, node): + kind = node.attributes['kind'].value + if kind in ('public-func', 'func', 'user-defined', ''): + self.generic_parse(node) + + def do_header(self, node): + """For a user defined section def a header field is present + which should not be printed as such, so we comment it in the + output.""" + data = node.firstChild.data + self.add_text('\n/*\n %s \n*/\n'%data) + # If our immediate sibling is a 'description' node then we + # should comment that out also and remove it from the parent + # node's children. + parent = node.parentNode + idx = parent.childNodes.index(node) + if len(parent.childNodes) >= idx + 2: + nd = parent.childNodes[idx+2] + if nd.nodeName == 'description': + nd = parent.removeChild(nd) + self.add_text('\n/*') + self.generic_parse(nd) + self.add_text('\n*/\n') + + def do_simplesect(self, node): + kind = node.attributes['kind'].value + if kind in ('date', 'rcs', 'version'): + pass + elif kind == 'warning': + self.add_text(['\n', 'WARNING: ']) + self.generic_parse(node) + elif kind == 'see': + self.add_text('\n') + self.add_text('See: ') + self.generic_parse(node) + else: + self.generic_parse(node) + + def do_argsstring(self, node): + self.generic_parse(node, pad=1) + + def do_member(self, node): + kind = node.attributes['kind'].value + refid = node.attributes['refid'].value + if kind == 'function' and refid[:9] == 'namespace': + self.generic_parse(node) + + def do_doxygenindex(self, node): + self.multi = 1 + comps = node.getElementsByTagName('compound') + for c in comps: + refid = c.attributes['refid'].value + fname = refid + '.xml' + if not os.path.exists(fname): + fname = os.path.join(self.my_dir, fname) + if not self.quiet: + print( "parsing file: %s"%fname ) + p = Doxy2SWIG(fname, self.include_function_definition, self.quiet) + p.generate() + self.pieces.extend(self.clean_pieces(p.pieces)) + + def write(self, fname): + o = my_open_write(fname) + if self.multi: + o.write("".join(self.pieces)) + else: + o.write("".join(self.clean_pieces(self.pieces))) + o.close() + + def clean_pieces(self, pieces): + """Cleans the list of strings given as `pieces`. It replaces + multiple newlines by a maximum of 2 and returns a new list. + It also wraps the paragraphs nicely. + + """ + ret = [] + count = 0 + for i in pieces: + if i == '\n': + count = count + 1 + else: + if i == '";': + if count: + ret.append('\n') + elif count > 2: + ret.append('\n\n') + elif count: + ret.append('\n'*count) + count = 0 + ret.append(i) + + _data = "".join(ret) + ret = [] + for i in _data.split('\n\n'): + if i == 'Parameters:' or i == 'Exceptions:' or i == 'Returns:': + ret.extend([i, '\n'+'-'*len(i), '\n\n']) + elif i.find('// File:') > -1: # leave comments alone. + ret.extend([i, '\n']) + else: + _tmp = textwrap.fill(i.strip(), break_long_words=False) + _tmp = self.lead_spc.sub(r'\1"\2', _tmp) + ret.extend([_tmp, '\n\n']) + return ret + + +def convert(input, output, include_function_definition=True, quiet=False): + p = Doxy2SWIG(input, include_function_definition, quiet) + p.generate() + p.write(output) + +def main(): + usage = __doc__ + parser = optparse.OptionParser(usage) + parser.add_option("-n", '--no-function-definition', + action='store_true', + default=False, + dest='func_def', + help='do not include doxygen function definitions') + parser.add_option("-q", '--quiet', + action='store_true', + default=False, + dest='quiet', + help='be quiet and minimize output') + + options, args = parser.parse_args() + if len(args) != 2: + parser.error("error: no input and output specified") + + convert(args[0], args[1], not options.func_def, options.quiet) + + +if __name__ == '__main__': + main() diff --git a/python/examples/CMakeLists.txt b/python/examples/CMakeLists.txt new file mode 100644 index 0000000..bca7daf --- /dev/null +++ b/python/examples/CMakeLists.txt @@ -0,0 +1,5 @@ +install ( FILES simple.py complete.py + DESTINATION share/libftdi/examples + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ + ) + diff --git a/python/examples/complete.py b/python/examples/complete.py new file mode 100644 index 0000000..afa59fa --- /dev/null +++ b/python/examples/complete.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +"""Python example program. + +Complete program to demonstrate the usage +of the swig generated python wrapper + +You need to build and install the wrapper first""" + +import os +import sys +import ftdi1 as ftdi +import time + +# version +print ( 'version: %s\n' % ftdi.__version__ ) + +# initialize +ftdic = ftdi.new() +if ftdic == 0: + print( 'new failed: %d' % ret ) + os._exit( 1 ) + +# try to list ftdi devices 0x6010 or 0x6001 +ret, devlist = ftdi.usb_find_all( ftdic, 0x0403, 0x6010 ) +if ret <= 0: + ret, devlist = ftdi.usb_find_all( ftdic, 0x0403, 0x6001) + +if ret < 0: + print( 'ftdi_usb_find_all failed: %d (%s)' % ( ret, ftdi.get_error_string( ftdic ) ) ) + os._exit( 1 ) +print( 'devices: %d' % ret ) +curnode = devlist +i = 0 +while( curnode != None ): + ret, manufacturer, description, serial = ftdi.usb_get_strings( ftdic, curnode.dev ) + if ret < 0: + print( 'ftdi_usb_get_strings failed: %d (%s)' % ( ret, ftdi.get_error_string( ftdic ) ) ) + os._exit( 1 ) + print( '#%d: manufacturer="%s" description="%s" serial="%s"\n' % ( i, manufacturer, description, serial ) ) + curnode = curnode.next + i += 1 + +# open usb +ret = ftdi.usb_open( ftdic, 0x0403, 0x6001 ) +if ret < 0: + print( 'unable to open ftdi device: %d (%s)' % ( ret, ftdi.get_error_string( ftdic ) ) ) + os._exit( 1 ) + + +# bitbang +ret = ftdi.set_bitmode( ftdic, 0xff, ftdi.BITMODE_BITBANG ) +if ret < 0: + print( 'Cannot enable bitbang' ) + os._exit( 1 ) +print( 'turning everything on' ) +ftdi.write_data( ftdic, chr(0xff), 1 ) +time.sleep( 1 ) +print( 'turning everything off\n' ) +ftdi.write_data( ftdic, chr(0x00), 1 ) +time.sleep( 1 ) +for i in range( 8 ): + val = 2**i + print( 'enabling bit #%d (0x%02x)' % (i, val) ) + ftdi.write_data( ftdic, chr(val), 1 ) + time.sleep ( 1 ) +ftdi.disable_bitbang( ftdic ) +print( '' ) + + +# read pins +ret, pins = ftdi.read_pins( ftdic ) +if ( ret == 0 ): + if sys.version_info[0] < 3: # python 2 + pins = ord( pins ) + else: + pins = pins[0] + print( 'pins: 0x%x' % pins ) + + +# read chip id +ret, chipid = ftdi.read_chipid( ftdic ) +if (ret==0): + print( 'chip id: %x\n' % chipid ) + + +# read eeprom +eeprom_addr = 1 +ret, eeprom_val = ftdi.read_eeprom_location( ftdic, eeprom_addr ) +if (ret==0): + print( 'eeprom @ %d: 0x%04x\n' % ( eeprom_addr, eeprom_val ) ) + +print( 'eeprom:' ) +ret = ftdi.read_eeprom( ftdic ) +size = 128 +ret, eeprom = ftdi.get_eeprom_buf ( ftdic, size ) +if ( ret == 0 ): + for i in range( size ): + octet = eeprom[i] + if sys.version_info[0] < 3: # python 2 + octet = ord( octet ) + sys.stdout.write( '%02x ' % octet ) + if ( i % 8 == 7 ): + print( '' ) +print( '' ) + +# close usb +ret = ftdi.usb_close( ftdic ) +if ret < 0: + print( 'unable to close ftdi device: %d (%s)' % ( ret, ftdi.get_error_string( ftdic ) ) ) + os._exit( 1 ) + +print ('device closed') +ftdi.free( ftdic ) diff --git a/python/examples/simple.py b/python/examples/simple.py new file mode 100644 index 0000000..9298c24 --- /dev/null +++ b/python/examples/simple.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +"""Python example program. + +Small program to demonstrate the usage +of the swig generated python wrapper + +You need to build and install the wrapper first""" + +import ftdi1 as ftdi + +def main(): + """Main program""" + context = ftdi.new() + + version_info = ftdi.get_library_version() + print("[FTDI version] major: %d, minor: %d, micro: %d" \ + ", version_str: %s, snapshot_str: %s" % + (version_info.major, version_info.minor, version_info.micro, + version_info.version_str, version_info.snapshot_str)) + + # try to open an ftdi 0x6010 or 0x6001 + ret = ftdi.usb_open(context, 0x0403, 0x6010) + if ret < 0: + ret = ftdi.usb_open(context, 0x0403, 0x6001) + + print("ftdi.usb_open(): %d" % ret) + print("ftdi.set_baudrate(): %d" % ftdi.set_baudrate(context, 9600)) + + ftdi.free(context) + +main() diff --git a/python/ftdi1.i b/python/ftdi1.i new file mode 100644 index 0000000..1e820dd --- /dev/null +++ b/python/ftdi1.i @@ -0,0 +1,138 @@ +/* File: ftdi1.i */ + +%module(docstring="Python interface to libftdi1") ftdi1 +%feature("autodoc","1"); + +#ifdef DOXYGEN +%include "ftdi1_doc.i" +#endif + +%{ +#include "Python.h" + +PyObject* convertString( const char *v, Py_ssize_t len ) +{ +#if PY_MAJOR_VERSION >= 3 + return PyBytes_FromStringAndSize(v, len); +#else + return PyString_FromStringAndSize(v, len); +#endif +} +%} + +%include +%include + +%typemap(in) unsigned char* = char*; + +%immutable ftdi_version_info::version_str; +%immutable ftdi_version_info::snapshot_str; + +%rename("%(strip:[ftdi_])s") ""; + +%newobject ftdi_new; +%typemap(newfree) struct ftdi_context *ftdi "ftdi_free($1);"; +%delobject ftdi_free; + +%define ftdi_usb_find_all_docstring +"usb_find_all(context, vendor, product) -> (return_code, devlist)" +%enddef +%feature("autodoc", ftdi_usb_find_all_docstring) ftdi_usb_find_all; +%typemap(in,numinputs=0) SWIGTYPE** OUTPUT ($*ltype temp) %{ $1 = &temp; %} +%typemap(argout) SWIGTYPE** OUTPUT %{ $result = SWIG_Python_AppendOutput($result, SWIG_NewPointerObj((void*)*$1,$*descriptor,0)); %} +%apply SWIGTYPE** OUTPUT { struct ftdi_device_list **devlist }; + int ftdi_usb_find_all(struct ftdi_context *ftdi, struct ftdi_device_list **devlist, + int vendor, int product); +%clear struct ftdi_device_list **devlist; + +%define ftdi_usb_get_strings_docstring +"usb_get_strings(context, device) -> (return_code, manufacturer, description, serial)" +%enddef +%feature("autodoc", ftdi_usb_get_strings_docstring) ftdi_usb_get_strings; +%apply char *OUTPUT { char * manufacturer, char * description, char * serial }; +%cstring_bounded_output( char * manufacturer, 256 ); +%cstring_bounded_output( char * description, 256 ); +%cstring_bounded_output( char * serial, 256 ); +%typemap(default,noblock=1) int mnf_len, int desc_len, int serial_len { $1 = 256; } + int ftdi_usb_get_strings(struct ftdi_context *ftdi, struct libusb_device *dev, + char * manufacturer, int mnf_len, + char * description, int desc_len, + char * serial, int serial_len); +%clear char * manufacturer, char * description, char * serial; +%clear int mnf_len, int desc_len, int serial_len; + +%define ftdi_read_data_docstring +"read_data(context) -> (return_code, buf)" +%enddef +%feature("autodoc", ftdi_read_data_docstring) ftdi_read_data; +%typemap(in,numinputs=1) (unsigned char *buf, int size) %{ $2 = PyInt_AsLong($input);$1 = (unsigned char*)malloc($2*sizeof(char)); %} +%typemap(argout) (unsigned char *buf, int size) %{ if(result<0) $2=0; $result = SWIG_Python_AppendOutput($result, convertString((char*)$1, $2)); free($1); %} + int ftdi_read_data(struct ftdi_context *ftdi, unsigned char *buf, int size); +%clear (unsigned char *buf, int size); + +%apply int *OUTPUT { unsigned int *chunksize }; + int ftdi_read_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunksize); + int ftdi_write_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunksize); +%clear unsigned int *chunksize; + +%define ftdi_read_pins_docstring +"read_pins(context) -> (return_code, pins)" +%enddef +%feature("autodoc", ftdi_read_pins_docstring) ftdi_read_pins; +%typemap(in,numinputs=0) unsigned char *pins ($*ltype temp) %{ $1 = &temp; %} +%typemap(argout) (unsigned char *pins) %{ $result = SWIG_Python_AppendOutput($result, convertString((char*)$1, 1)); %} + int ftdi_read_pins(struct ftdi_context *ftdi, unsigned char *pins); +%clear unsigned char *pins; + +%typemap(in,numinputs=0) unsigned char *latency ($*ltype temp) %{ $1 = &temp; %} +%typemap(argout) (unsigned char *latency) %{ $result = SWIG_Python_AppendOutput($result, convertString((char*)$1, 1)); %} + int ftdi_get_latency_timer(struct ftdi_context *ftdi, unsigned char *latency); +%clear unsigned char *latency; + +%apply short *OUTPUT { unsigned short *status }; + int ftdi_poll_modem_status(struct ftdi_context *ftdi, unsigned short *status); +%clear unsigned short *status; + +%apply int *OUTPUT { int* value }; + int ftdi_get_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value value_name, int* value); +%clear int* value; + +%typemap(in,numinputs=1) (unsigned char *buf, int size) %{ $2 = PyInt_AsLong($input);$1 = (unsigned char*)malloc($2*sizeof(char)); %} +%typemap(argout) (unsigned char *buf, int size) %{ if(result<0) $2=0; $result = SWIG_Python_AppendOutput($result, convertString((char*)$1, $2)); free($1); %} + int ftdi_get_eeprom_buf(struct ftdi_context *ftdi, unsigned char * buf, int size); +%clear (unsigned char *buf, int size); + +%define ftdi_read_eeprom_location_docstring +"read_eeprom_location(context, eeprom_addr) -> (return_code, eeprom_val)" +%enddef +%feature("autodoc", ftdi_read_eeprom_location_docstring) ftdi_read_eeprom_location; +%apply short *OUTPUT { unsigned short *eeprom_val }; + int ftdi_read_eeprom_location (struct ftdi_context *ftdi, int eeprom_addr, unsigned short *eeprom_val); +%clear unsigned short *eeprom_val; + +%define ftdi_read_eeprom_docstring +"read_eeprom(context) -> (return_code, eeprom)" +%enddef +%feature("autodoc", ftdi_read_eeprom_docstring) ftdi_read_eeprom; + +%define ftdi_read_chipid_docstring +"ftdi_read_chipid(context) -> (return_code, chipid)" +%enddef +%feature("autodoc", ftdi_read_chipid_docstring) ftdi_read_chipid; +%apply int *OUTPUT { unsigned int *chipid }; + int ftdi_read_chipid(struct ftdi_context *ftdi, unsigned int *chipid); +%clear unsigned int *chipid; + +%include ftdi.h +%{ +#include +%} + +%include ftdi_i.h +%{ +#include +%} + +%pythoncode %{ +__version__ = get_library_version().version_str +%} -- 1.7.1