mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-08-05 01:18:36 +02:00
Initial commit
This commit is contained in:
+81
@@ -0,0 +1,81 @@
|
||||
option ( PYTHON_BINDINGS "Build python bindings via swig" ON )
|
||||
option ( LINK_PYTHON_LIBRARY "Link against python libraries" ON )
|
||||
|
||||
if ( PYTHON_BINDINGS )
|
||||
# workaround for cmake bug #0013449
|
||||
if ( NOT DEFINED CMAKE_FIND_ROOT_PATH )
|
||||
find_package ( SWIG )
|
||||
else ()
|
||||
find_program ( SWIG_EXECUTABLE NAMES swig2.0 swig )
|
||||
if ( SWIG_EXECUTABLE )
|
||||
set ( SWIG_USE_FILE ${CMAKE_ROOT}/Modules/UseSWIG.cmake )
|
||||
set ( SWIG_FOUND TRUE )
|
||||
endif ()
|
||||
endif ()
|
||||
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 )
|
||||
|
||||
if ( DOCUMENTATION AND DOXYGEN_FOUND )
|
||||
set(CMAKE_SWIG_FLAGS -DDOXYGEN=${DOXYGEN_FOUND})
|
||||
endif()
|
||||
swig_add_module ( ftdi1 python ftdi1.i )
|
||||
swig_link_libraries ( ftdi1 ftdi1 )
|
||||
|
||||
if ( LINK_PYTHON_LIBRARY )
|
||||
swig_link_libraries ( ftdi1 ${PYTHON_LIBRARIES} )
|
||||
elseif( APPLE )
|
||||
set_target_properties ( ${SWIG_MODULE_ftdi1_REAL_NAME} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup" )
|
||||
endif ()
|
||||
|
||||
set_target_properties ( ${SWIG_MODULE_ftdi1_REAL_NAME} PROPERTIES NO_SONAME ON )
|
||||
|
||||
execute_process ( COMMAND ${PYTHON_EXECUTABLE} -c "from distutils import sysconfig; print( sysconfig.get_python_lib( plat_specific=True, prefix='${CMAKE_INSTALL_PREFIX}' ) )"
|
||||
OUTPUT_VARIABLE _ABS_PYTHON_MODULE_PATH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE )
|
||||
|
||||
get_filename_component ( _ABS_PYTHON_MODULE_PATH ${_ABS_PYTHON_MODULE_PATH} ABSOLUTE )
|
||||
file ( RELATIVE_PATH _REL_PYTHON_MODULE_PATH ${CMAKE_INSTALL_PREFIX} ${_ABS_PYTHON_MODULE_PATH} )
|
||||
|
||||
set ( PYTHON_MODULE_PATH
|
||||
${_REL_PYTHON_MODULE_PATH}
|
||||
)
|
||||
|
||||
install ( FILES ${CMAKE_CURRENT_BINARY_DIR}/ftdi1.py DESTINATION ${PYTHON_MODULE_PATH} )
|
||||
install ( TARGETS ${SWIG_MODULE_ftdi1_REAL_NAME} LIBRARY DESTINATION ${PYTHON_MODULE_PATH} )
|
||||
|
||||
if ( DOCUMENTATION AND 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}/${PYTHON_MODULE_PATH} )
|
||||
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 ()
|
||||
+457
@@ -0,0 +1,457 @@
|
||||
#!/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_<nodeType>` 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_<tagName>` 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 <ref> 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()
|
||||
@@ -0,0 +1,5 @@
|
||||
install ( FILES simple.py complete.py cbus.py
|
||||
DESTINATION share/libftdi/examples
|
||||
PERMISSIONS OWNER_READ GROUP_READ WORLD_READ
|
||||
)
|
||||
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
#!/usr/bin/python2
|
||||
"""
|
||||
Copyright 2015, Sinclair R.F., Inc.
|
||||
|
||||
This program is distributed under the GPL, version 2.
|
||||
|
||||
Demonstrate how to configure the FT230X USB UART bridge as follows:
|
||||
max_power 500 mA
|
||||
CBUS3 Drive 1 (accomodate PCB error)
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
# Need to install libftdi for the following to work (see README.txt)
|
||||
import ftdi1 as ftdi
|
||||
|
||||
# Define class for displaying errors.
|
||||
class ErrorMsg(Exception):
|
||||
def __init__(self,message):
|
||||
self.message = message
|
||||
def __str__(self):
|
||||
return self.message
|
||||
|
||||
# Function to convert CBUSX values to human-readable strings
|
||||
def cbush_string(value):
|
||||
if value == ftdi.CBUSX_AWAKE:
|
||||
return 'AWAKE'
|
||||
if value == ftdi.CBUSX_BAT_DETECT:
|
||||
return 'BAT_DETECT'
|
||||
if value == ftdi.CBUSX_BAT_DETECT_NEG:
|
||||
return 'BAT_DETECT_NEG'
|
||||
if value == ftdi.CBUSX_BB_RD:
|
||||
return 'BB_RD'
|
||||
if value == ftdi.CBUSX_BB_WR:
|
||||
return 'BB_WR'
|
||||
if value == ftdi.CBUSX_CLK24:
|
||||
return 'CLK24'
|
||||
if value == ftdi.CBUSX_CLK12:
|
||||
return 'CLK12'
|
||||
if value == ftdi.CBUSX_CLK6:
|
||||
return 'CLK6'
|
||||
if value == ftdi.CBUSX_DRIVE_0:
|
||||
return 'DRIVE_0'
|
||||
if value == ftdi.CBUSX_DRIVE1:
|
||||
return 'DRIVE_1'
|
||||
if value == ftdi.CBUSX_I2C_RXF:
|
||||
return 'I2C_RXF'
|
||||
if value == ftdi.CBUSX_I2C_TXE:
|
||||
return 'I2C_TXE'
|
||||
if value == ftdi.CBUSX_IOMODE:
|
||||
return 'IOMODE'
|
||||
if value == ftdi.CBUSX_PWREN:
|
||||
return 'PWREN'
|
||||
if value == ftdi.CBUSX_RXLED:
|
||||
return 'RXLED'
|
||||
if value == ftdi.CBUSX_SLEEP:
|
||||
return 'SLEEP'
|
||||
if value == ftdi.CBUSX_TIME_STAMP:
|
||||
return 'TIME_STAMP'
|
||||
if value == ftdi.CBUSX_TRISTATE:
|
||||
return 'TRISTATE'
|
||||
if value == ftdi.CBUSX_TXDEN:
|
||||
return 'TXDEN'
|
||||
if value == ftdi.CBUSX_TXLED:
|
||||
return 'TXLED'
|
||||
if value == ftdi.CBUSX_TXRXLED:
|
||||
return 'TXRXLED'
|
||||
if value == ftdi.CBUSX_VBUS_SENSE:
|
||||
return 'VBUS_SENSE'
|
||||
return 'UNKNOWN'
|
||||
|
||||
# Surround the program with a try ... except clause.
|
||||
try:
|
||||
|
||||
# Allocate and inialize an ftdi context.
|
||||
ftdic = ftdi.new()
|
||||
if ftdic == 0:
|
||||
raise ErrorMsg('ftdi.new() failed')
|
||||
|
||||
# List all the FT230X devices.
|
||||
nDevices, devlist = ftdi.usb_find_all(ftdic, 0x0403, 0x6015)
|
||||
if nDevices < 0:
|
||||
raise ErrorMsg('ftdi.usb_find_all error = %s' % ftdi.get_error_string(ftdic))
|
||||
elif nDevices == 0:
|
||||
raise ErrorMsg('No FT230X devices found')
|
||||
elif nDevices != 1:
|
||||
raise ErrorMsg('More than one FT230X device found')
|
||||
|
||||
# Display the identified single FT230X device.
|
||||
ret, manufacturer, description, serial = ftdi.usb_get_strings(ftdic, devlist.dev)
|
||||
if ret < 0:
|
||||
raise ErrorMsg('ftdi.usb_get_strings error = %s' % ftdi.get_error_string(ftdic))
|
||||
print 'manufacturer="%s" description="%s" serial="%s"' % (manufacturer, description, serial)
|
||||
|
||||
# Open the identified single FT230X device.
|
||||
ret = ftdi.usb_open_desc(ftdic, 0x0403, 0x6015, description, serial)
|
||||
if ret < 0:
|
||||
raise ErrorMsg('ftdi.usb_open_desc error = %s' % ftdi.get_error_string(ftdic))
|
||||
|
||||
# Read the chip id.
|
||||
ret, chipid = ftdi.read_chipid(ftdic)
|
||||
if ret < 0:
|
||||
raise ErrorMsg('ftdi.read_chipid error = %s' % ftdi.get_error_string(ftdic))
|
||||
print 'chip id=0x%08X' % (chipid % 2**32)
|
||||
|
||||
# Read the EEPROM
|
||||
ret = ftdi.read_eeprom(ftdic)
|
||||
if ret < 0:
|
||||
raise ErrorMsg('ftdi.read_eeprom error = %s' % ftdi.get_error_string(ftdic))
|
||||
|
||||
# Get a read-only copy of the EEPROM
|
||||
if True:
|
||||
eeprom_size = ftdic.eeprom.size
|
||||
ret, eeprom_buf = ftdi.get_eeprom_buf(ftdic, eeprom_size)
|
||||
if ret < 0:
|
||||
raise ErrorMsg('ftdi.get_eeprom_buf error = %s' % ftdi.get_error_string(ftdic))
|
||||
for i in range(0,eeprom_size,16):
|
||||
sys.stdout.write('%04x: ' % i)
|
||||
for j in range(16):
|
||||
sys.stdout.write('%02x ' % ord(eeprom_buf[i+j]))
|
||||
if j in (7,15,):
|
||||
sys.stdout.write(' ')
|
||||
for j in range(16):
|
||||
x = eeprom_buf[i+j]
|
||||
if 32 <= ord(x) <= 0x7E:
|
||||
sys.stdout.write(x)
|
||||
else:
|
||||
sys.stdout.write('.')
|
||||
sys.stdout.write('\n')
|
||||
|
||||
# Read and display the EEPROM (in human readable format)
|
||||
ret = ftdi.eeprom_decode(ftdic, 1)
|
||||
if ret < 0:
|
||||
raise ErrorMsg('ftdi.eeprom_decode error = %s' % ftdi.get_error_string(ftdic))
|
||||
|
||||
# Set the maximum power to 500mA.
|
||||
print 'initial max_power = %dmA' % ftdic.eeprom.max_power
|
||||
ftdic.eeprom.max_power = 500
|
||||
print 'new max_power = %dmA' % ftdic.eeprom.max_power
|
||||
|
||||
# Set CBUS3 to DRIVE_1 (the board needs to be reworked to use PWREN# and BCD#)
|
||||
ret, value = ftdi.get_eeprom_value(ftdic,ftdi.CBUS_FUNCTION_3)
|
||||
if ret < 0:
|
||||
raise ErrorMsg('ftdi.get_eeprom_value error = %s' % ftdi.get_error_string(ftdic))
|
||||
print 'initial CBUS3 = %d (%s)' % (value,cbush_string(value),)
|
||||
ret = ftdi.set_eeprom_value(ftdic,ftdi.CBUS_FUNCTION_3,ftdi.CBUSX_DRIVE1)
|
||||
if ret < 0:
|
||||
raise ErrorMsg('ftdi.set_eeprom_value error = %s' % ftdi.get_error_string(ftdic))
|
||||
ret, value = ftdi.get_eeprom_value(ftdic,ftdi.CBUS_FUNCTION_3)
|
||||
if ret < 0:
|
||||
raise ErrorMsg('ftdi.get_eeprom_value error = %s' % ftdi.get_error_string(ftdic))
|
||||
print 'new CBUS3 = %d (%s)' % (value,cbush_string(value),)
|
||||
|
||||
# Write the new EEPROM settings.
|
||||
if False:
|
||||
ret = ftdi.eeprom_build(ftdic)
|
||||
if ret < 0:
|
||||
raise ErrorMsg('ftdi.eeprom_build error = %s' % ftdi.get_error_string(ftdic))
|
||||
ret = ftdi.write_eeprom(ftdic)
|
||||
if ret < 0:
|
||||
raise ErrorMsg('ftdi.write_eeprom error = %s' % ftdi.get_error_string(ftdic))
|
||||
print 'EEPROM write succeeded'
|
||||
else:
|
||||
print 'EEPROM write not attempted'
|
||||
|
||||
# Close the ftdi context.
|
||||
ret = ftdi.usb_close(ftdic)
|
||||
if ret < 0:
|
||||
raise ErrorMsg('ftdi.usb_close error = %s' % ftdi.get_error_string(ftdic))
|
||||
|
||||
except ErrorMsg, msg:
|
||||
print >> sys.stderr, 'FATAL ERROR: ' + str(msg)
|
||||
exit(1)
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
#!/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)
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
#!/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()
|
||||
Vendored
+170
@@ -0,0 +1,170 @@
|
||||
/* File: ftdi1.i */
|
||||
|
||||
%module(docstring="Python interface to libftdi1") ftdi1
|
||||
%feature("autodoc","1");
|
||||
|
||||
#ifdef DOXYGEN
|
||||
%include "ftdi1_doc.i"
|
||||
#endif
|
||||
|
||||
%{
|
||||
#include "Python.h"
|
||||
|
||||
inline PyObject* charp2str(const char *v_, long len)
|
||||
{
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return PyBytes_FromStringAndSize(v_, len);
|
||||
#else
|
||||
return PyString_FromStringAndSize(v_, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline char * str2charp_size(PyObject* pyObj, int * size)
|
||||
{
|
||||
char * v_ = 0;
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyBytes_AsStringAndSize(pyObj, &v_, (Py_ssize_t*)size);
|
||||
#else
|
||||
PyString_AsStringAndSize(pyObj, &v_, (Py_ssize_t*)size);
|
||||
#endif
|
||||
return v_;
|
||||
}
|
||||
%}
|
||||
|
||||
%include <typemaps.i>
|
||||
%include <cstring.i>
|
||||
|
||||
%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;
|
||||
%feature("autodoc", ftdi_usb_get_strings_docstring) ftdi_usb_get_strings2;
|
||||
%feature("autodoc", ftdi_usb_get_strings_docstring) ftdi_eeprom_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 * product, 256 );
|
||||
%cstring_bounded_output( char * serial, 256 );
|
||||
%typemap(default,noblock=1) int mnf_len, int desc_len, int product_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);
|
||||
int ftdi_usb_get_strings2(struct ftdi_context *ftdi, struct libusb_device *dev,
|
||||
char * manufacturer, int mnf_len,
|
||||
char * description, int desc_len,
|
||||
char * serial, int serial_len);
|
||||
int ftdi_eeprom_get_strings(struct ftdi_context *ftdi,
|
||||
char *manufacturer, int mnf_len,
|
||||
char *product, int product_len,
|
||||
char *serial, int serial_len);
|
||||
|
||||
%clear char * manufacturer, char * description, char * serial;
|
||||
%clear char * product;
|
||||
%clear int mnf_len, int desc_len, int product_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, charp2str((char*)$1, $2)); free($1); %}
|
||||
int ftdi_read_data(struct ftdi_context *ftdi, unsigned char *buf, int size);
|
||||
%clear (unsigned char *buf, int size);
|
||||
|
||||
%define ftdi_write_data_docstring
|
||||
"write_data(context, data) -> return_code"
|
||||
%enddef
|
||||
%feature("autodoc", ftdi_write_data_docstring) ftdi_write_data;
|
||||
%typemap(in,numinputs=1) (const unsigned char *buf, int size) %{ $1 = (unsigned char*)str2charp_size($input, &$2); %}
|
||||
int ftdi_write_data(struct ftdi_context *ftdi, const unsigned char *buf, int size);
|
||||
%clear (const 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, charp2str((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, charp2str((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, charp2str((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 <ftdi.h>
|
||||
%}
|
||||
|
||||
%include ftdi_i.h
|
||||
%{
|
||||
#include <ftdi_i.h>
|
||||
%}
|
||||
|
||||
%pythoncode %{
|
||||
__version__ = get_library_version().version_str
|
||||
%}
|
||||
Reference in New Issue
Block a user