Commits

Ben Bass  committed 3262110

support multiple libftdi versions

  • Participants
  • Parent commits f54f3b8

Comments (0)

Files changed (6)

File doc/how_to.rst

 
     OK
     $
+
+How can I determine and select the underlying libftdi library?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Since pylibftdi 0.12, the Driver exposes a ``libftdi_version`` method,
+which returns a tuple whose first three entries correspond to major, minor,
+and micro versions of the libftdi driver being used.
+
+With the recent (early 2013) release of libftdi1 - which can coexist with
+the earlier 0.x versions - it is now possible to select which library to
+load when instantiating the Driver::
+
+    Python 2.7.2 (default, Jun 20 2012, 16:23:33)
+    [GCC 4.2.1 Compatible Apple Clang 4.0 (tags/Apple/clang-418.0.60)] on darwin
+    Type "help", "copyright", "credits" or "license" for more information.
+    >>> from pylibftdi import Driver
+    >>> Driver().libftdi_version()
+    (1, 0, 0, '1.0', 'v1.0-6-gafb9082')
+    >>> Driver('ftdi').libftdi_version()
+    (0, 99, 0, '0.99', 'v0.17-305-g50d77f8')
+    >>> Driver('libftdi1').libftdi_version()
+    (1, 0, 0, '1.0', 'v1.0-6-gafb9082')
+    >>> Driver(('libftdi1', 'libftdi')).libftdi_version()
+    (1, 0, 0, '1.0', 'v1.0-6-gafb9082')
+    >>> Driver(('libftdi', 'libftdi1')).libftdi_version()
+    (0, 99, 0, '0.99', 'v0.17-305-g50d77f8')
+    >>> Driver(('libftdi', 'libftdi1')).libftdi_version()
+
+``pylibftdi`` now prefers libftdi1 over libftdi, if both are installed. Since
+different OSs require different parameters to be given to find a library,
+the default search list given to ctypes.util.find_library is as follows::
+
+    Driver._dll_list = ('ftdi1', 'libftdi1', 'ftdi', 'libftdi')
+
+This covers Windows (which requires the 'lib' prefix), Linux (which requires
+its absence), and Mac OS X, which is happy with either.

File doc/index.rst

    basic_usage
    advanced_usage
    how_to
+   troubleshooting
    pylibftdi
 
 Indices and tables

File doc/installation.rst

 
 .. _picusb: http://code.google.com/p/picusb
 
+Things:
+ - installing it in the first place, e.g. via ez_setup.py etc.
+
 Mac OS X
 --------
 

File doc/troubleshooting.rst

+pylibftdi troubleshooting
+=========================
+
+Once up-and-running, pylibftdi is designed to be very simple, but sometimes
+getting it working in the first place can be more difficult.
+
+Error messages
+--------------
+
+``FtdiError: unable to claim usb device. Make sure the default FTDI driver is not in use (-5)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This indicates a conflict with FTDI's own drivers, and is (as far as I know)
+mainly a problem on Mac OS X, where they can be disabled (until reboot) with
+the following::
+
+    sudo kextunload /System/Library/Extensions/FTDIUSBSerialDriver.kext
+
+To reload the kernel driver, do the following::
+
+    sudo kextload /System/Library/Extensions/FTDIUSBSerialDriver.kext
+
+To permanently remove the driver, just delete it::
+
+    sudo rm /System/Library/Extensions/FTDIUSBSerialDriver.kext
+
+
+Diagnosis
+---------
+
+Getting a list of USB devices
+
+Mac OS X
+~~~~~~~~
+
+Start 'System Information', then select Hardware > USB, and look for your
+device. On the command line, ``system_profiler SPUSBDataType`` can be used.
+In the following example I've piped it into ``grep -C 7 FTDI``, to print 7
+lines either side of a match on the string 'FTDI'::
+
+    ben$ system_profiler SPUSBDataType | grep -C 7 FTDI
+            UM232H:
+
+              Product ID: 0x6014
+              Vendor ID: 0x0403  (Future Technology Devices International Limited)
+              Version: 9.00
+              Serial Number: FTUBIOWF
+              Speed: Up to 480 Mb/sec
+              Manufacturer: FTDI
+              Location ID: 0x24710000 / 7
+              Current Available (mA): 500
+              Current Required (mA): 90
+
+            USB Reader:
+
+              Product ID: 0x4082
+
+Linux
+~~~~~
+Use ``lsusb``. Example from my laptop::
+
+    ben@ben-laptop:~$ lsusb
+    Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
+    Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
+    Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
+    Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
+    Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
+    Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
+    Bus 007 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
+    Bus 008 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
+    Bus 008 Device 011: ID 0a5c:217f Broadcom Corp. Bluetooth Controller
+    Bus 002 Device 009: ID 17ef:481d Lenovo 
+    Bus 002 Device 016: ID 0403:6014 Future Technology Devices International, Ltd FT232H Single HS USB-UART/FIFO IC

File pylibftdi/bitbang.py

                  lazy_open=False,
                  sync=True,
                  bitbang_mode=BITMODE_BITBANG,
-                 interface_select=None):
+                 interface_select=None,
+                 **kwargs):
         # initialise the super-class, but don't open yet. We really want
         # two-part initialisation here - set up all the instance variables
         # here in the super class, then open it after having set more
         super(BitBangDevice, self).__init__(device_id=device_id,
                                             mode='b',
                                             lazy_open=True,
-                                            interface_select=interface_select)
+                                            interface_select=interface_select,
+                                            **kwargs)
         self.direction = direction
         self.sync = sync
         self.bitbang_mode = bitbang_mode

File pylibftdi/driver.py

 from pylibftdi._base import FtdiError
 
 
-class UsbDevList(Structure):
+class ftdi_device_list(Structure):
     _fields_ = [('next', c_void_p),
-                ('usb_dev', c_void_p)]
+                ('dev', c_void_p)]
+
+
+class ftdi_version_info(Structure):
+    _fields_ = [('major', c_int),
+        ('minor', c_int),
+        ('micro', c_int),
+        ('version_str', c_char_p),
+        ('snapshot_str', c_char_p)]
 
 # Note I gave up on attempts to use ftdi_new/ftdi_free (just using
 # ctx instead of byref(ctx) in first param of most ftdi_* functions) as
     _instance = None
     _need_init = True
 
-    def __new__(cls, *o, **k):
-        # make this a singleton. There is only a single
-        # reference to the dynamic library.
-        if Driver._instance is None:
-            Driver._instance = object.__new__(cls)
-        return Driver._instance
+    # prefer libftdi1 if available. Windows uses 'lib' prefix.
+    _dll_list = ('ftdi1', 'libftdi1', 'ftdi', 'libftdi')
 
-    def __init__(self):
-        if self._need_init:
-            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
-            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
-        self._need_init = False
+    def __init__(self, libftdi_search=None):
+        self._libftdi_path = self._find_libftdi(libftdi_search)
+        self.fdll = CDLL(self._libftdi_path)
+        # most args/return types are fine with the implicit
+        # int/void* which ctypes uses, but some need setting here
+        self.fdll.ftdi_get_error_string.restype = c_char_p
+        self.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)
+
+    def _find_libftdi(self, libftdi_search=None):
+        """
+        find the libftdi path, suitable for ctypes.CDLL()
+        @param libftdi_search: string or sequence of strings
+            use to force a particular version of libftdi to be used
+        """
+        if libftdi_search is None:
+            search_list = self._dll_list
+        elif isinstance(libftdi_search, basestring):
+            search_list = (libftdi_search,)
+        else:
+            search_list = libftdi_search
+
+        ftdi_lib = None
+        for dll in search_list:
+            ftdi_lib = find_library(dll)
+            if ftdi_lib is not None:
+                break
+        if ftdi_lib is None:
+            raise FtdiError('libftdi library not found (search: {})'.format(search_list))
+        return ftdi_lib
+
+    def libftdi_version(self):
+        """
+        return the version of the underlying library being used
+        """
+        version = ftdi_version_info()
+        self.fdll.ftdi_get_library_version(byref(version))
+        return (version.major, version.minor, version.micro,
+                version.version_str, version.snapshot_str)
 
     def list_devices(self):
         """
         manuf = create_string_buffer(128)
         desc = create_string_buffer(128)
         serial = create_string_buffer(128)
-        devlistptrtype = POINTER(UsbDevList)
+        devlistptrtype = POINTER(ftdi_device_list)
         dev_list_ptr = devlistptrtype()
 
         # create context for doing the enumeration
                     # traverse the linked list...
                     try:
                         while dev_list_ptr:
-                            self.fdll.ftdi_usb_get_strings(byref(ctx),
-                                    dev_list_ptr.contents.usb_dev,
+                            res = self.fdll.ftdi_usb_get_strings(byref(ctx),
+                                    dev_list_ptr.contents.dev,
                                     manuf, 127, desc, 127, serial, 127)
+                            if res < 0:
+                                raise FtdiError(self.fdll.ftdi_get_error_string(byref(ctx)))
                             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,
 
     def __init__(self, device_id=None, mode="b",
                  encoding="latin1", lazy_open=False,
-                 chunk_size=0, interface_select=None):
+                 chunk_size=0, interface_select=None,
+                 **kwargs):
         """
         Device([device_id[, mode, [OPTIONS ...]]) -> Device instance
 
         interface_select - select interface to use on multi-interface devices
         """
         self._opened = False
-        self.driver = Driver()
+        self.driver = Driver(**kwargs)
         self.fdll = self.driver.fdll
         # device_id is an optional serial number of the requested device.
         self.device_id = device_id