Beispiel #1
0
    def open(self):
        """
        open connection to a FTDI device
        """
        if self._opened:
            return

        # create context for this device
        self.ctx = create_string_buffer(1024)
        res = self.fdll.ftdi_init(byref(self.ctx))
        if res != 0:
            msg = "%s (%d)" % (self.get_error_string(), res)
            del self.ctx
            raise FtdiError(msg)

        if self.interface_select is not None:
            res = self.fdll.ftdi_set_interface(byref(self.ctx),
                                               self.interface_select)
            if res != 0:
                msg = "%s (%d)" % (self.get_error_string(), res)
                del self.ctx
                raise FtdiError(msg)

        # 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.
        res = self._open_device()

        if res != 0:
            msg = "%s (%d)" % (self.get_error_string(), res)
            # free the context
            self.fdll.ftdi_deinit(byref(self.ctx))
            del self.ctx
            raise FtdiError(msg)

        if self.auto_detach and self.driver.libftdi_version().major > 0:
            # This doesn't reliably work on libftdi 0.x, so we ignore it
            ctx_p = cast(byref(self.ctx),
                         POINTER(ftdi_context_partial)).contents
            dev = ctx_p.libusb_device_handle
            if dev:
                self.driver._libusb.libusb_set_auto_detach_kernel_driver(
                    dev, 1)

        # explicitly reset the device to serial mode with standard settings
        # - no flow control, 9600 baud - in case it had previously been used
        # in bitbang mode (some later driver versions might do bits of this
        # automatically)
        self.ftdi_fn.ftdi_set_bitmode(0, BITMODE_RESET)
        self.ftdi_fn.ftdi_setflowctrl(0)
        self.baudrate = 9600
        # reset the latency timer to 16ms (device default, but kernel device
        # drivers can set a different - e.g. 1ms - value)
        self.ftdi_fn.ftdi_set_latency_timer(16)
        self._opened = True
Beispiel #2
0
    def flush(self, flush_what=FLUSH_BOTH):
        """
        Instruct the FTDI device to flush its FIFO buffers

        By default both the input and output buffers will be
        flushed, but the caller can selectively chose to only
        flush the input or output buffers using `flush_what`:

        :param flush_what: select what to flush:
            `FLUSH_BOTH` (default);
            `FLUSH_INPUT` (just the rx buffer);
            `FLUSH_OUTPUT` (just the tx buffer)
        """
        if flush_what == FLUSH_BOTH:
            fn = self.fdll.ftdi_usb_purge_buffers
        elif flush_what == FLUSH_INPUT:
            fn = self.fdll.ftdi_usb_purge_rx_buffer
        elif flush_what == FLUSH_OUTPUT:
            fn = self.fdll.ftdi_usb_purge_tx_buffer
        else:
            raise ValueError("Invalid value passed to %s.flush()" %
                             self.__class__.__name__)
        res = fn(byref(self.ctx))
        if res != 0:
            msg = "%s (%d)" % (self.get_error_string(), res)
            raise FtdiError(msg)
Beispiel #3
0
    def read(self, length):
        """
        read(length) -> bytes/string of up to `length` bytes.

        read upto `length` bytes from the FTDI device
        :param length: maximum number of bytes to read
        :return: value read from device
        :rtype: bytes if self.mode is 'b', else decode with self.encoding
        """
        if not self._opened:
            raise FtdiError("read() on closed Device")

        # read the data
        if self.chunk_size != 0:
            remaining = length
            byte_data_list = []
            while remaining > 0:
                rx_bytes = self._read(min(remaining, self.chunk_size))
                if not rx_bytes:
                    break
                byte_data_list.append(rx_bytes)
                remaining -= len(rx_bytes)
            byte_data = b''.join(byte_data_list)
        else:
            byte_data = self._read(length)
        if self.mode == 'b':
            return byte_data
        else:
            return self.decoder.decode(byte_data)
Beispiel #4
0
    def read(self, length):
        """
        read(length) -> bytes/string of up to `length` bytes.

        read upto `length` bytes from the FTDI device
        :param length: maximum number of bytes to read
        :return: value read from device
        :rtype: bytes if self.mode is 'b', else decode with self.encoding
        """
        if not self._opened:
            raise FtdiError("read() on closed Device")

        # read the data
        if self.chunk_size != 0:
            remaining = length
            byte_data_list = []
            while remaining > 0:
                rx_bytes = self._read(min(remaining, self.chunk_size))
                if not rx_bytes:
                    break
                byte_data_list.append(rx_bytes)
                remaining -= len(rx_bytes)
            byte_data = b''.join(byte_data_list)
        else:
            byte_data = self._read(length)
        if self.mode == 'b':
            return byte_data
        else:
            # TODO: for some codecs, this may choke if we haven't read the
            # full required data. If this is the case we should probably trim
            # a byte at a time from the output until the decoding works, and
            # buffer the remainder for next time.
            return byte_data.decode(self.encoding)
Beispiel #5
0
    def open(self):
        """
        open connection to a FTDI device
        """
        if self._opened:
            return

        # create context for this device
        self.ctx = create_string_buffer(1024)
        res = self.fdll.ftdi_init(byref(self.ctx))
        if res != 0:
            msg = "%s (%d)" % (self.get_error_string(), res)
            del self.ctx
            raise FtdiError(msg)

        if self.interface_select is not None:
            res = self.fdll.ftdi_set_interface(byref(self.ctx),
                                               self.interface_select)
            if res != 0:
                msg = "%s (%d)" % (self.get_error_string(), res)
                del self.ctx
                raise FtdiError(msg)

        # 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.
        res = self._open_device()

        if res != 0:
            msg = "%s (%d)" % (self.get_error_string(), res)
            # free the context
            self.fdll.ftdi_deinit(byref(self.ctx))
            del self.ctx
            raise FtdiError(msg)

        # explicitly reset the device to serial mode with standard settings
        # - no flow control, 9600 baud - in case it had previously been used
        # in bitbang mode (some later driver versions might do bits of this
        # automatically)
        self.fdll.ftdi_set_bitmode(byref(self.ctx), 0, BITMODE_RESET)
        self.fdll.ftdi_setflowctrl(byref(self.ctx), 0)
        self.baudrate = 9600
        self._opened = True
Beispiel #6
0
    def _write(self, byte_data):
        """
        actually do the low level writing

        :param byte_data: data to be written
        :type byte_data: bytes
        :return: number of bytes written
        """
        buf = create_string_buffer(byte_data)
        written = self.fdll.ftdi_write_data(byref(self.ctx), byref(buf),
                                            len(byte_data))
        if written < 0:
            raise FtdiError(self.get_error_string())
        return written
Beispiel #7
0
    def _read(self, length):
        """
        actually do the low level reading

        :return: bytes read from the device
        :rtype: bytes
        """
        buf = create_string_buffer(length)
        rlen = self.fdll.ftdi_read_data(byref(self.ctx), byref(buf), length)
        if rlen < 0:
            raise FtdiError(self.get_error_string())
        byte_data = buf.raw[:rlen]

        return byte_data
Beispiel #8
0
    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, (str, bytes)):
            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
Beispiel #9
0
    def write(self, data):
        """
        write(data) -> count of bytes actually written

        write given `data` string to the FTDI device

        :param data: string to be written
        :type data: string or bytes
        :return: count of bytes written, which may be less than `len(data)`
        """
        if not self._opened:
            raise FtdiError("write() on closed Device")

        try:
            byte_data = bytes(data)
        except TypeError:
            # this will happen if we are Python3 and data is a str.
            byte_data = self.encoder.encode(data)

        # actually write it
        if self.chunk_size != 0:
            remaining = len(byte_data)
            written = 0
            while remaining > 0:
                start = written
                length = min(remaining, self.chunk_size)
                result = self._write(byte_data[start:start + length])
                if result == 0:
                    # don't continue to try writing forever if nothing
                    # is actually being written
                    break
                else:
                    written += result
                    remaining -= result
        else:
            written = self._write(byte_data)
        return written
Beispiel #10
0
    def list_devices(self):
        """
        :return: (manufacturer, description, serial#) for each attached
            device, e.g.:

            [('FTDI', 'UM232R USB <-> Serial', 'FTE4FFVQ'),
            ('FTDI', 'UM245R', 'FTE00P4L')]

        :rtype: a list of string triples

        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.
        # this will contain the device info to return
        devices = []
        manuf = create_string_buffer(128)
        desc = create_string_buffer(128)
        serial = create_string_buffer(128)
        devlistptrtype = POINTER(ftdi_device_list)
        dev_list_ptr = devlistptrtype()

        # create context for doing the enumeration
        ctx = create_string_buffer(1024)
        if self.fdll.ftdi_init(byref(ctx)) != 0:
            msg = self.fdll.ftdi_get_error_string(byref(ctx))
            raise FtdiError(msg)

        def _s(s):
            """From c_char_p / str you shall be / in Python2 or 3"""
            return str(s.decode())

        try:
            for usb_vid, usb_pid in itertools.product(USB_VID_LIST,
                                                      USB_PID_LIST):
                res = self.fdll.ftdi_usb_find_all(byref(ctx),
                                                  byref(dev_list_ptr), usb_vid,
                                                  usb_pid)
                if res < 0:
                    err_msg = self.fdll.ftdi_get_error_string(byref(ctx))
                    msg = "%s (%d)" % (err_msg, res)
                    raise FtdiError(msg)
                elif res > 0:
                    # take a copy of the dev_list for subsequent list_free
                    dev_list_base = byref(dev_list_ptr)
                    # traverse the linked list...
                    try:
                        while dev_list_ptr:
                            res = self.fdll.ftdi_usb_get_strings(
                                byref(ctx), dev_list_ptr.contents.dev, manuf,
                                127, desc, 127, serial, 127)
                            # don't error on failure to get all the data
                            # error codes: -7: manuf, -8: desc, -9: serial
                            if res < 0 and res not in (-7, -8, -9):
                                err_msg = self.fdll.ftdi_get_error_string(
                                    byref(ctx))
                                msg = "%s (%d)" % (err_msg, res)
                                raise FtdiError(msg)
                            devices.append((_s(manuf.value), _s(desc.value),
                                            _s(serial.value)))
                            # step to next in linked-list
                            dev_list_ptr = cast(dev_list_ptr.contents.next,
                                                devlistptrtype)
                    finally:
                        self.fdll.ftdi_list_free(dev_list_base)
        finally:
            self.fdll.ftdi_deinit(byref(ctx))
        return devices
Beispiel #11
0
    def open(self):
        """
        open connection to a FTDI device
        """
        if self._opened:
            return

        if not self.device_id and self.list_index is not None:
            # Use serial number from list_index
            dev_list = self.driver.list_devices()
            try:
                # The third (index 2) field is serial number.
                self.device_id = dev_list[self.list_index][2]
            except IndexError:
                raise FtdiError("index provided not in range of list_devices() entries")

        # create context for this device
        # 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
        # (at least for 64-bit) they only worked if argtypes was declared
        # (c_void_p for ctx), and that's too much like hard work to maintain.
        # So I've reverted to using create_string_buffer for memory management,
        # byref(ctx) to pass in the context instance, and ftdi_init() /
        # ftdi_deinit() pair to manage the driver resources. It's very nice
        # how layered the libftdi code is, with access to each layer.
        self.ctx = create_string_buffer(1024)
        res = self.fdll.ftdi_init(byref(self.ctx))
        if res != 0:
            msg = "%s (%d)" % (self.get_error_string(), res)
            del self.ctx
            raise FtdiError(msg)

        if self.interface_select is not None:
            res = self.fdll.ftdi_set_interface(byref(self.ctx),
                                               self.interface_select)
            if res != 0:
                msg = "%s (%d)" % (self.get_error_string(), res)
                del self.ctx
                raise FtdiError(msg)

        # 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.
        res = self._open_device()

        if res != 0:
            msg = self.handle_open_error(res)
            # free the context
            self.fdll.ftdi_deinit(byref(self.ctx))
            del self.ctx
            raise FtdiError(msg)

        if self.auto_detach and self.driver.libftdi_version().major > 0:
            # This doesn't reliably work on libftdi 0.x, so we ignore it
            ctx_p = cast(byref(self.ctx), POINTER(ftdi_context_partial)).contents
            dev = ctx_p.libusb_device_handle
            if dev:
                self.driver._libusb.libusb_set_auto_detach_kernel_driver(c_void_p(dev), 1)

        # explicitly reset the device to serial mode with standard settings
        # - no flow control, 9600 baud - in case it had previously been used
        # in bitbang mode (some later driver versions might do bits of this
        # automatically)
        self.ftdi_fn.ftdi_set_bitmode(0, BITMODE_RESET)
        self.ftdi_fn.ftdi_setflowctrl(0)
        self.baudrate = 9600
        # reset the latency timer to 16ms (device default, but kernel device
        # drivers can set a different - e.g. 1ms - value)
        self.ftdi_fn.ftdi_set_latency_timer(16)
        self._opened = True