Ben Bass avatar Ben Bass committed 83ec1bc

partway through supporting 0.7 multiple devices

Comments (0)

Files changed (5)

 Metadata-Version: 1.0
 Name: pylibftdi
-Version: 0.6
+Version: 0.7
 Summary: Pythonic interface to FTDI devices using libftdi
 Home-page: http://bitbucket.org/codedstructure/pylibftdi
 Author: Ben Bass

pylibftdi/__init__.py

 rather than a problem with the libftdi library.
 """
 
-__VERSION__ = "0.6"
+__VERSION__ = "0.7"
 __AUTHOR__ = "Ben Bass"
 
 
-__ALL__ = ['Driver', 'BitBangDriver', 'Bus', 'FtdiError',
+__ALL__ = ['Driver', 'Device', 'BitBangDriver', 'Bus', 'FtdiError',
            'ALL_OUTPUTS', 'ALL_INPUTS', 'BB_OUTPUT', 'BB_INPUT']
 
 from pylibftdi import _base, driver, util, bitbang
 FtdiError = _base.FtdiError
 Bus = util.Bus
 Driver = driver.Driver
+Driver = driver.Device
 BitBangDriver = bitbang.BitBangDriver
 
 ALL_OUTPUTS = bitbang.ALL_OUTPUTS

pylibftdi/bitbang.py

 
 """
 
-from pylibftdi.driver import Driver
+from pylibftdi.driver import Device
 from ctypes import byref 
 
 ALL_OUTPUTS = 0xFF
 BB_OUTPUT = 1
 BB_INPUT = 0
 
-class BitBangDriver(Driver):
+class BitBangDriver(Device):
     """
     simple subclass to support bit-bang mode
 
         assert 0 <= dir <= 255, 'invalid direction bitmask'
         self._direction = dir
         if self.opened:
-            self.fdll.ftdi_set_bitmode(byref(self.ctx), dir, 0x01)
+            self.fdll.ftdi_set_bitmode(self.ctx, dir, 0x01)
 
 
     # port property - 8 bit read/write value

pylibftdi/driver.py

 
 import functools
 # be disciplined so pyflakes can check us...
-from ctypes import (CDLL, byref, c_char_p, create_string_buffer)
+from ctypes import (CDLL, byref, c_int, c_char_p, c_void_p, cast,
+                    create_string_buffer, Structure, pointer, POINTER)
 from ctypes.util import find_library
 
 from pylibftdi._base import ParrotEgg, DeadParrot, FtdiError
 
+
+class UsbDevList(Structure):
+    _fields_ = [('next', c_void_p),
+                ('usb_dev', c_void_p)]
+
+def Follower(x):
+    class _(object):
+        def __getattr__(self, key):
+            obj = getattr(x,key)
+            if callable(obj):
+                class _fn(object):
+                    def __getattr__(s1, key):
+                        return getattr(obj, key)
+                    def __setattr__(s1, key, value):
+                        return setattr(obj, key, value)
+                    def __delattr__(s1, key):
+                        return delattr(obj, key)
+                    def __call__(s1, *o, **k):
+                        print "%s(%s,%s)"%(obj.__name__,o,k),
+                        res = obj(*o, **k)
+                        print "-> %s"%res
+                        return res
+                return _fn()
+            else:
+                return obj
+        def __setattr__(self, key, value):
+            return setattr(x,key,value)
+        def __delattr__(self, key):
+            return delattr(x,key)
+    return _()
+    
+
+class FtdiEnumerate(object):
+    def __init__(self):
+        self.ctx = None
+
+    def list_devices(self):
+        """
+        return a list of triples (manufacturer, description, serial#)
+        for each attached device, e.g.:
+        [('ftdi', 'um245r', 'fte00p4l'),
+         ('ftdi', 'um232r usb <-> serial', 'fte4ffvq')]
+
+        the serial number can be used to open specific devices
+        """
+        # ftdi_usb_find_all sets dev_list_ptr to a linked list
+        # (*next/*usb_device) of usb_devices, each of which can
+        # be passed to ftdi_usb_get_strings() to get info about
+        # them.
+        manuf = create_string_buffer(128)
+        desc = create_string_buffer(128)
+        serial = create_string_buffer(128)
+        ctx = create_string_buffer(1024)
+        fdll.ftdi_init(byref(ctx))
+        usbdevlist = POINTER(UsbDevList)
+        devlistptrtype = pointer(usbdevlist)
+        dev_list_ptr = devlistptrtype()
+        res = fdll.ftdi_usb_find_all(byref(ctx), byref(dev_list_ptr), 0x0403, 0x6001)
+        if res < 0:
+            fdll.ftdi_deinit(byref(self.ctx))
+            raise ftdierror(fdll.ftdi_get_error_string(byref(self.ctx)))
+        # we'll add the info here.
+        devices = []
+        # take a copy of the dev_list for subsequent list_free
+        dev_list_base = pointer(dev_list_ptr.contents)
+        # traverse the linked list...
+        try:
+            while dev_list_ptr:
+                fdll.ftdi_usb_get_strings(byref(ctx), dev_list_ptr.contents.usb_dev,
+                        manuf,127, desc,127, serial,127)
+                devices.append((manuf.value, desc.value, serial.value))
+                # step to next in linked-list if not 
+                dev_list_ptr = cast(dev_list_ptr.contents.next, devlistptrtype)
+        finally:
+           fdll.ftdi_list_free(dev_list_base)
+        return devices
+
 class Driver(object):
     """
     This is where it all happens...
     We load the libftdi library, and use it.
     """
+
+    instance = None
+
+    def __init__(self):
+        ftdi_lib = find_library('ftdi')
+        if ftdi_lib is None:
+            raise FtdiError('libftdi library not found')
+        fdll = Follower(CDLL(ftdi_lib))
+        # most args/return types are fine with the implicit
+        # int/void* which ctypes uses, but some need setting here
+        fdll.ftdi_get_error_string.restype = c_char_p
+        fdll.ftdi_usb_get_strings.argtypes = (c_void_p, c_void_p,
+                                              c_char_p, c_int,
+                                              c_char_p, c_int,
+                                              c_char_p, c_int)
+        self.fdll = fdll
+        Driver.instance = self
+
+    def list_devices(self):
+        """
+        return a list of triples (manufacturer, description, serial#)
+        for each attached device, e.g.:
+        [('ftdi', 'um245r', 'fte00p4l'),
+         ('ftdi', 'um232r usb <-> serial', 'fte4ffvq')]
+
+        the serial number can be used to open specific devices
+        """
+        # ftdi_usb_find_all sets dev_list_ptr to a linked list
+        # (*next/*usb_device) of usb_devices, each of which can
+        # be passed to ftdi_usb_get_strings() to get info about
+        # them.
+        manuf = create_string_buffer(128)
+        desc = create_string_buffer(128)
+        serial = create_string_buffer(128)
+        ctx = create_string_buffer(1024)
+        self.fdll.ftdi_init(byref(ctx))
+        devlistptrtype = POINTER(UsbDevList)
+        dev_list_ptr = devlistptrtype()
+        res = self.fdll.ftdi_usb_find_all(byref(ctx), byref(dev_list_ptr), 0x0403, 0x6001)
+        if res < 0:
+            self.fdll.ftdi_deinit(byref(self.ctx))
+            raise ftdierror(self.fdll.ftdi_get_error_string(byref(self.ctx)))
+        # we'll add the info here.
+        devices = []
+        # take a copy of the dev_list for subsequent list_free
+        dev_list_base = pointer(dev_list_ptr.contents)
+        # traverse the linked list...
+        try:
+            while dev_list_ptr:
+                self.fdll.ftdi_usb_get_strings(byref(ctx), dev_list_ptr.contents.usb_dev,
+                        manuf,127, desc,127, serial,127)
+                devices.append((manuf.value, desc.value, serial.value))
+                # step to next in linked-list if not 
+                dev_list_ptr = cast(dev_list_ptr.contents.next, devlistptrtype)
+        finally:
+           self.fdll.ftdi_list_free(dev_list_base)
+        return devices
+
+class Device(object):
     def __init__(self, mode="b", encoding="latin1"):
-        self.ctx = None
-        self.fdll = ParrotEgg()
+        if Driver.instance is None:
+            # initialise it...
+            Driver()
+        self.ctx = self.fdll.ftdi_new()
+        if self.ctx == 0:
+            raise FtdiError("could not create new FTDI context")
         self.opened = False
         # mode can be either 'b' for binary, or 't' for text.
         # if set to text, the values returned from read() will
         # standard for serial devices.
         self._baudrate = 9600
 
-    def open(self):
-        "open connection to a FTDI device"
+    fdll = property(lambda self: Driver.instance.fdll)
+
+    def open(self, device_id=None):
+        """open connection to a FTDI device
+        device_id: [optional] serial number (string) of device to be opened
+        """
         if self.opened:
             return
-        # TODO: provide parameter to select required device
-        # (if multiple are attached)
-        ftdi_lib = find_library('ftdi')
-        if ftdi_lib is None:
-            raise FtdiError('libftdi library not found')
-        fdll = CDLL(ftdi_lib)
-        # most args/return types are fine with the implicit
-        # int/void* which ctypes uses, but some need setting here
-        fdll.ftdi_get_error_string.restype = c_char_p
         # Try to open the device.  If this fails, reset things to how
         # they were, but we can't use self.close as that assumes things
         # have already been setup.
-        # sizeof(struct ftdi_context) seems to be 112 on x86_64, 84 on i386
-        # provide a generous 1K buffer for (hopefully) all possibles...
-        self.ctx = create_string_buffer(1024)
-        if fdll.ftdi_init(byref(self.ctx)) != 0:
-            raise FtdiError(fdll.ftdi_get_error_string(byref(self.ctx)))
         # FTDI vendor/product ids required here.
-        if fdll.ftdi_usb_open(byref(self.ctx), 0x0403, 0x6001) != 0:
-            fdll.ftdi_deinit(byref(self.ctx))
-            raise FtdiError(fdll.ftdi_get_error_string(byref(self.ctx)))
-        # only at this point do we allow other things to access fdll.
-        # (so if exception is thrown above, there is no access).
-        # - maybe this should all be in the constructor. RAII, you know.
-        #   (except we don't have deterministic destructors. oh well.)
-        self.fdll = fdll
+        open_args = [self.ctx, 0x0403, 0x6001]
+        if device_id is None:
+            res = self.fdll.ftdi_usb_open(*tuple(open_args))
+        else:
+            open_args.extend([0, c_char_p(device_id.encode('latin1'))])
+            res = self.fdll.ftdi_usb_open_desc(*tuple(open_args))
+        if res != 0:
+            try:
+                raise FtdiError(self.fdll.ftdi_get_error_string(self.ctx))
+            finally:
+                self.fdll.ftdi_free(self.ctx)
+                self.ctx = None
         self.opened = True
 
     def close(self):
         "close our connection, free resources"
         self.opened = False
-        if self.fdll.ftdi_usb_close(byref(self.ctx)) == 0:
-            self.fdll.ftdi_deinit(byref(self.ctx))
-        self.fdll = DeadParrot()
-
+        if self.fdll.ftdi_usb_close(self.ctx) == 0:
+            self.fdll.ftdi_deinit(self.ctx)
 
     @property
     def baudrate(self):
 
     @baudrate.setter
     def baudrate(self, value):
-        result = self.fdll.ftdi_set_baudrate(byref(self.ctx), value)
+        result = self.fdll.ftdi_set_baudrate(self.ctx, value)
         if result == 0:
             self._baudrate = value
 
         raw bytes, else decode according to self.encoding
         """
         buf = create_string_buffer(length)
-        rlen = self.fdll.ftdi_read_data(byref(self.ctx), byref(buf), length)
+        rlen = self.fdll.ftdi_read_data(self.ctx, byref(buf), length)
         if rlen == -1:
             raise FtdiError(self.get_error_string())
         byte_data = buf.raw[:rlen]
             # this will happen if we are Python3 and data is a str.
             byte_data = data.encode(self.encoding)
         buf = create_string_buffer(byte_data)
-        written = self.fdll.ftdi_write_data(byref(self.ctx),
+        written = self.fdll.ftdi_write_data(self.ctx,
                                             byref(buf), len(data))
         if written == -1:
             raise FtdiError(self.get_error_string())
 
     def get_error_string(self):
         "return error string from libftdi driver"
-        return self.fdll.ftdi_get_error_string(byref(self.ctx))
+        return self.fdll.ftdi_get_error_string(self.ctx)
 
 
     @property
         class FtdiForwarder(object):
             def __getattr__(innerself, key):
                  return functools.partial(getattr(self.fdll, key),
-                                          byref(self.ctx))
+                                          self.ctx)
         return FtdiForwarder()
 
 
 
 setup(
     name="pylibftdi",
-    version="0.6",
+    version="0.7",
     description="Pythonic interface to FTDI devices using libftdi",
     author="Ben Bass",
     author_email="benbass@codedstructure.net",
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.