Full Code of eblot/pyftdi for AI

main 998583bdde59 cached
91 files
746.4 KB
193.4k tokens
1180 symbols
1 requests
Download .txt
Showing preview only (780K chars total). Download the full file or copy to clipboard to get everything.
Repository: eblot/pyftdi
Branch: main
Commit: 998583bdde59
Files: 91
Total size: 746.4 KB

Directory structure:
gitextract_vloilsyq/

├── .flake8
├── .github/
│   └── workflows/
│       ├── pypi.yml
│       ├── pythonchecksyntax.yml
│       ├── pythonmocktests.yml
│       └── pythonpackage.yml
├── .gitignore
├── .pylintrc
├── LICENSE
├── MANIFEST.in
├── README.md
├── _config.yml
├── pyftdi/
│   ├── INSTALL
│   ├── __init__.py
│   ├── bin/
│   │   ├── ftconf.py
│   │   ├── ftdi_urls.py
│   │   ├── i2cscan.py
│   │   ├── pyterm.py
│   │   └── uphy.sh
│   ├── bits.py
│   ├── doc/
│   │   ├── api/
│   │   │   ├── eeprom.rst
│   │   │   ├── ftdi.rst
│   │   │   ├── gpio.rst
│   │   │   ├── i2c.rst
│   │   │   ├── index.rst
│   │   │   ├── misc.rst
│   │   │   ├── spi.rst
│   │   │   ├── uart.rst
│   │   │   └── usbtools.rst
│   │   ├── authors.rst
│   │   ├── conf.py
│   │   ├── defs.rst
│   │   ├── eeprom.rst
│   │   ├── features.rst
│   │   ├── gpio.rst
│   │   ├── index.rst
│   │   ├── installation.rst
│   │   ├── license.rst
│   │   ├── pinout.rst
│   │   ├── requirements.rst
│   │   ├── testing.rst
│   │   ├── tools.rst
│   │   ├── troubleshooting.rst
│   │   └── urlscheme.rst
│   ├── eeprom.py
│   ├── ftdi.py
│   ├── gpio.py
│   ├── i2c.py
│   ├── jtag.py
│   ├── misc.py
│   ├── serialext/
│   │   ├── __init__.py
│   │   ├── logger.py
│   │   ├── protocol_ftdi.py
│   │   ├── protocol_unix.py
│   │   └── tests/
│   │       └── rl.py
│   ├── spi.py
│   ├── term.py
│   ├── tests/
│   │   ├── backend/
│   │   │   ├── __init__.py
│   │   │   ├── consts.py
│   │   │   ├── ftdivirt.py
│   │   │   ├── loader.py
│   │   │   ├── mpsse.py
│   │   │   └── usbvirt.py
│   │   ├── bits.py
│   │   ├── cbus.py
│   │   ├── eeprom.py
│   │   ├── eeprom_mock.py
│   │   ├── ftdi.py
│   │   ├── gpio.py
│   │   ├── i2c.py
│   │   ├── jtag.py
│   │   ├── mockusb.py
│   │   ├── resources/
│   │   │   ├── custom_vidpid.yaml
│   │   │   ├── ft2232h.yaml
│   │   │   ├── ft230x.yaml
│   │   │   ├── ft230x_io.yaml
│   │   │   ├── ft231x_cbus.yaml
│   │   │   ├── ft232h.yaml
│   │   │   ├── ft232h_x2.yaml
│   │   │   ├── ft232r.yaml
│   │   │   ├── ft4232h.yaml
│   │   │   ├── ft4232ha.yaml
│   │   │   └── ftmany.yaml
│   │   ├── spi.py
│   │   ├── toolsimport.py
│   │   └── uart.py
│   ├── tracer.py
│   └── usbtools.py
├── requirements.txt
├── setup.cfg
├── setup.py
└── test-requirements.txt

================================================
FILE CONTENTS
================================================

================================================
FILE: .flake8
================================================
[flake8]
max-line-length = 80


================================================
FILE: .github/workflows/pypi.yml
================================================
name: Publish to PyPI

on:
  release:
    types:
      - published
  workflow_dispatch:
    inputs:
      twine_verbose:
        description: 'Enable Twine verbose mode'
        required: true
        type: boolean

jobs:
  pypi-publish:
    name: upload release to PyPI
    runs-on: ubuntu-latest
    environment:
      name: pypi
      url: https://pypi.org/p/pyftdi
    permissions:
      id-token: write
    strategy:
      matrix:
        python-version: ['3.13']
    steps:

    - uses: actions/checkout@v4

    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install setuptools wheel

    - name: Build package
      run: |
        python setup.py bdist_wheel

    - name: Publish package distributions to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1
      with:
          verbose: ${{ inputs.twine_verbose }}


================================================
FILE: .github/workflows/pythonchecksyntax.yml
================================================
name: Python syntax tests
# check that there is no import issues with tool suite

on:
  push:
  pull_request:
    types: [assigned, opened, synchronize, reopened]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']

    steps:
    - uses: actions/checkout@v4

    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install -r test-requirements.txt

    - name: Check style
      run: |
        python setup.py check_style

    - name: Linter
      run: |
        pylint --disable=fixme --disable=duplicate-code \
          $(git ls-files '*.py')

    - name: Install package
      run: |
        python setup.py install

    - name: Run tests
      run: |
        python pyftdi/tests/toolsimport.py


================================================
FILE: .github/workflows/pythonmocktests.yml
================================================
name: Python USB mock tests

on:
  push:
  pull_request:
    types: [assigned, opened, synchronize, reopened]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']

    steps:
    - uses: actions/checkout@v4

    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install setuptools wheel ruamel.yaml

    - name: Install package
      run: |
        python setup.py install

    - name: Run Mock tests
      env:
        FTDI_LOGLEVEL: WARNING
        FTDI_DEBUG: on
      run: |
        python pyftdi/tests/mockusb.py

    - name: Run GPIO tests
      env:
        FTDI_LOGLEVEL: WARNING
        FTDI_DEBUG: on
        FTDI_VIRTUAL: on
      run: |
        python pyftdi/tests/gpio.py

    - name: Run EEPROM tests
      env:
        FTDI_LOGLEVEL: WARNING
        FTDI_DEBUG: on
        FTDI_VIRTUAL: on
      run: |
        python pyftdi/tests/eeprom_mock.py


================================================
FILE: .github/workflows/pythonpackage.yml
================================================
name: Python package

on:
  push:
  pull_request:
    types: [assigned, opened, synchronize, reopened]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']

    steps:
    - uses: actions/checkout@v4

    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install setuptools wheel sphinx sphinx_rtd_theme sphinx_autodoc_typehints

    - name: Build package
      run: |
        python setup.py bdist
        python setup.py sdist bdist_wheel

    - name: Build documentation
      run: |
        mkdir doc
        cd doc
        sphinx-build -W -b html ../pyftdi/doc .


================================================
FILE: .gitignore
================================================
*.egg-info
*.pyc
*.pyo
**/.DS_Store
MANIFEST
pyusb*
dist/
build/
sphinx/
.vscode/
.venv/


================================================
FILE: .pylintrc
================================================
[MASTER]

init-hook='import sys; sys.path.append(".")'

[MESSAGES CONTROL]

disable=
    too-few-public-methods,
    too-many-arguments,
    too-many-branches,
    too-many-instance-attributes,
    too-many-lines,
    too-many-locals,
    too-many-nested-blocks,
    too-many-positional-arguments,
    too-many-public-methods,
    too-many-return-statements,
    too-many-statements,
    unspecified-encoding


================================================
FILE: LICENSE
================================================
Copyright (c) 2008-2025 Emmanuel Blot <emmanuel.blot@free.fr>
All Rights Reserved.

SPDX-License-Identifier: BSD-3-Clause

i.e.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its contributors may
      be used to endorse or promote products derived from this software
      without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL NEOTION BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: MANIFEST.in
================================================
exclude README.md
include pyftdi/doc/images/*.png

================================================
FILE: README.md
================================================
[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://vshymanskyy.github.io/StandWithUkraine)

# PyFtdi

![Python package](https://github.com/eblot/pyftdi/actions/workflows/pythonpackage.yml/badge.svg)
![Mock tests](https://github.com/eblot/pyftdi/actions/workflows/pythonmocktests.yml/badge.svg)
![Syntax tests](https://github.com/eblot/pyftdi/actions/workflows/pythonchecksyntax.yml/badge.svg)
[![StandWithUkraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://vshymanskyy.github.io/StandWithUkraine)

[![PyPI](https://img.shields.io/pypi/v/pyftdi.svg?maxAge=2592000)](https://pypi.org/project/pyftdi/)
[![Python Versions](https://img.shields.io/pypi/pyversions/pyftdi.svg)](https://pypi.org/project/pyftdi/)
[![Downloads](https://img.shields.io/pypi/dm/pyftdi.svg)](https://pypi.org/project/pyftdi/)

## Documentation

PyFtdi documentation is available from https://eblot.github.io/pyftdi/

## Overview

PyFtdi aims at providing a user-space driver for popular FTDI devices,
implemented in pure Python language.

Suported FTDI devices include:

* UART and GPIO bridges

  * FT232R (single port, 3Mbps)
  * FT230X/FT231X/FT234X (single port, 3Mbps)

* UART, GPIO and multi-serial protocols (SPI, I2C, JTAG) bridges

  * FT2232C/D (dual port, clock up to 6 MHz)
  * FT232H (single port, clock up to 30 MHz)
  * FT2232H (dual port, clock up to 30 MHz)
  * FT4232H (quad port, clock up to 30 MHz)
  * FT4232HA (quad port, clock up to 30 MHz)

## Features

PyFtdi currently supports the following features:

* UART/Serial USB converter, up to 12Mbps (depending on the FTDI device
  capability)
* GPIO/Bitbang support, with 8-bit asynchronous, 8-bit synchronous and
  8-/16-bit MPSSE variants
* SPI master, with simultanous GPIO support, up to 12 pins per port,
  with support for non-byte sized transfer
* I2C master, with simultanous GPIO support, up to 14 pins per port
* Basic JTAG master capabilities
* EEPROM support (some parameters cannot yet be modified, only retrieved)
* Experimental CBUS support on selected devices, 4 pins per port

## Supported host OSes

* macOS
* Linux
* FreeBSD
* Windows, although not officially supported

## License

`SPDX-License-Identifier: BSD-3-Clause`

## Warnings

### Python support

PyFtdi requires Python 3.9+.

See `pyftdi/doc/requirements.rst` for more details.


================================================
FILE: _config.yml
================================================
theme: jekyll-theme-architect

================================================
FILE: pyftdi/INSTALL
================================================
Please read pyftdi/README.rst for installation instructions.


================================================
FILE: pyftdi/__init__.py
================================================
# Copyright (c) 2010-2025 Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2010-2016, Neotion
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

# pylint: disable=missing-docstring

__version__ = '0.57.1'
__title__ = 'PyFtdi'
__description__ = 'FTDI device driver (pure Python)'
__uri__ = 'http://github.com/eblot/pyftdi'
__doc__ = __description__ + ' <' + __uri__ + '>'
__author__ = 'Emmanuel Blot'
# For all support requests, please open a new issue on GitHub
__email__ = 'emmanuel.blot@free.fr'
__license__ = 'BSD-3-Clause'
__copyright__ = 'Copyright (c) 2011-2025 Emmanuel Blot'


from logging import WARNING, NullHandler, getLogger


class FtdiLogger:

    log = getLogger('pyftdi')
    log.addHandler(NullHandler())
    log.setLevel(level=WARNING)

    @classmethod
    def set_formatter(cls, formatter):
        handlers = list(cls.log.handlers)
        for handler in handlers:
            handler.setFormatter(formatter)

    @classmethod
    def get_level(cls):
        return cls.log.getEffectiveLevel()

    @classmethod
    def set_level(cls, level):
        cls.log.setLevel(level=level)


================================================
FILE: pyftdi/bin/ftconf.py
================================================
#!/usr/bin/env python3

"""Simple FTDI EEPROM configurator.
"""

# Copyright (c) 2019-2024, Emmanuel Blot <emmanuel.blot@free.fr>
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

from argparse import ArgumentParser, FileType
from io import StringIO
from logging import Formatter, StreamHandler, DEBUG, ERROR
from sys import exit as sys_exit, modules, stderr, stdout
from textwrap import fill
from traceback import format_exc
from pyftdi import FtdiLogger
from pyftdi.eeprom import FtdiEeprom
from pyftdi.ftdi import Ftdi
from pyftdi.misc import add_custom_devices, hexdump

# pylint: disable=too-many-locals
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements


def main():
    """Main routine"""
    debug = False
    try:
        argparser = ArgumentParser(description=modules[__name__].__doc__)
        argparser.add_argument('device', nargs='?', default='ftdi:///?',
                               help='serial port device name')

        files = argparser.add_argument_group(title='Files')
        files.add_argument('-i', '--input', type=FileType('rt'),
                           help='input ini file to load EEPROM content')
        files.add_argument('-l', '--load', default='all',
                           choices=('all', 'raw', 'values'),
                           help='section(s) to load from input file')
        files.add_argument('-o', '--output',
                           help='output ini file to save EEPROM content')
        files.add_argument('-V', '--virtual', type=FileType('r'),
                           help='use a virtual device, specified as YaML')

        device = argparser.add_argument_group(title='Device')
        device.add_argument('-P', '--vidpid', action='append',
                            help='specify a custom VID:PID device ID '
                                 '(search for FTDI devices)')
        device.add_argument('-M', '--eeprom',
                            help='force an EEPROM model')
        device.add_argument('-S', '--size', type=int,
                            choices=FtdiEeprom.eeprom_sizes,
                            help='force an EEPROM size')

        fmt = argparser.add_argument_group(title='Format')
        fmt.add_argument('-x', '--hexdump', action='store_true',
                         help='dump EEPROM content as ASCII')
        fmt.add_argument('-X', '--hexblock', type=int,
                         help='dump EEPROM as indented hexa blocks')

        config = argparser.add_argument_group(title='Configuration')
        config.add_argument('-s', '--serial-number',
                            help='set serial number')
        config.add_argument('-m', '--manufacturer',
                            help='set manufacturer name')
        config.add_argument('-p', '--product',
                            help='set product name')
        config.add_argument('-c', '--config', action='append',
                            help='change/configure a property as key=value '
                                 'pair')
        config.add_argument('--vid', type=lambda x: int(x, 16),
                            help='shortcut to configure the USB vendor ID')
        config.add_argument('--pid', type=lambda x: int(x, 16),
                            help='shortcut to configure the USB product ID')

        action = argparser.add_argument_group(title='Action')
        action.add_argument('-e', '--erase', action='store_true',
                            help='erase the whole EEPROM content')
        action.add_argument('-E', '--full-erase', action='store_true',
                            default=False,
                            help='erase the whole EEPROM content, including '
                                 'the CRC')
        action.add_argument('-u', '--update', action='store_true',
                            help='perform actual update, use w/ care')

        extra = argparser.add_argument_group(title='Extras')
        extra.add_argument('-v', '--verbose', action='count', default=0,
                           help='increase verbosity')
        extra.add_argument('-d', '--debug', action='store_true',
                           help='enable debug mode')
        args = argparser.parse_args()
        debug = args.debug

        if not args.device:
            argparser.error('Serial device not specified')

        loglevel = max(DEBUG, ERROR - (10 * args.verbose))
        loglevel = min(ERROR, loglevel)
        if debug:
            formatter = Formatter('%(asctime)s.%(msecs)03d %(name)-20s '
                                  '%(message)s', '%H:%M:%S')
        else:
            formatter = Formatter('%(message)s')
        FtdiLogger.set_formatter(formatter)
        FtdiLogger.set_level(loglevel)
        FtdiLogger.log.addHandler(StreamHandler(stderr))

        if args.virtual:
            # pylint: disable=import-outside-toplevel
            from pyftdi.usbtools import UsbTools
            # Force PyUSB to use PyFtdi test framework for USB backends
            UsbTools.BACKENDS = ('pyftdi.tests.backend.usbvirt', )
            # Ensure the virtual backend can be found and is loaded
            backend = UsbTools.find_backend()
            loader = backend.create_loader()()
            loader.load(args.virtual)

        try:
            add_custom_devices(Ftdi, args.vidpid, force_hex=True)
        except ValueError as exc:
            argparser.error(str(exc))

        eeprom = FtdiEeprom()
        eeprom.open(args.device, size=args.size, model=args.eeprom)
        if args.erase or args.full_erase:
            eeprom.erase()
        if args.input:
            eeprom.load_config(args.input, args.load)
        if args.serial_number:
            eeprom.set_serial_number(args.serial_number)
        if args.manufacturer:
            eeprom.set_manufacturer_name(args.manufacturer)
        if args.product:
            eeprom.set_product_name(args.product)
        for conf in args.config or []:
            if conf in ('?', 'help'):
                helpstr = ', '.join(sorted(eeprom.properties))
                print(fill(helpstr, initial_indent='  ',
                           subsequent_indent='  '))
                sys_exit(1)
            for sep in ':=':
                if sep in conf:
                    name, value = conf.split(sep, 1)
                    if not value:
                        argparser.error(f'Configuration {conf} without value')
                    if value == 'help':
                        value = '?'
                    helpio = StringIO()
                    eeprom.set_property(name, value, helpio)
                    helpstr = helpio.getvalue()
                    if helpstr:
                        print(fill(helpstr, initial_indent='  ',
                                   subsequent_indent='  '))
                        sys_exit(1)
                    break
            else:
                argparser.error(f'Missing name:value separator in {conf}')
        if args.vid:
            eeprom.set_property('vendor_id', args.vid)
        if args.pid:
            eeprom.set_property('product_id', args.pid)
        if args.hexdump:
            print(hexdump(eeprom.data))
        if args.hexblock is not None:
            indent = ' ' * args.hexblock
            for pos in range(0, len(eeprom.data), 16):
                hexa = ' '.join([f'{x:02x}' for x in eeprom.data[pos:pos+16]])
                print(indent, hexa, sep='')
        if args.update:
            if eeprom.commit(False, no_crc=args.full_erase):
                eeprom.reset_device()
        if args.verbose > 0:
            eeprom.dump_config()
        if args.output:
            if args.output == '-':
                eeprom.save_config(stdout)
            else:
                with open(args.output, 'wt') as ofp:
                    eeprom.save_config(ofp)

    except (ImportError, IOError, NotImplementedError, ValueError) as exc:
        print(f'\nError: {exc}', file=stderr)
        if debug:
            print(format_exc(chain=False), file=stderr)
        sys_exit(1)
    except KeyboardInterrupt:
        sys_exit(2)
    finally:
        eeprom.close()


if __name__ == '__main__':
    main()


================================================
FILE: pyftdi/bin/ftdi_urls.py
================================================
#!/usr/bin/env python3

# Copyright (c) 2019-2024, Emmanuel Blot <emmanuel.blot@free.fr>
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""List valid FTDI device URLs and descriptors."""

from argparse import ArgumentParser, FileType
from logging import Formatter, StreamHandler, DEBUG, ERROR
from sys import exit as sys_exit, modules, stderr
from traceback import format_exc
from pyftdi import FtdiLogger
from pyftdi.ftdi import Ftdi
from pyftdi.misc import add_custom_devices


def main():
    """Entry point."""
    debug = False
    try:
        argparser = ArgumentParser(description=modules[__name__].__doc__)
        argparser.add_argument('-P', '--vidpid', action='append',
                               help='specify a custom VID:PID device ID, '
                                    'may be repeated')
        argparser.add_argument('-V', '--virtual', type=FileType('r'),
                               help='use a virtual device, specified as YaML')
        argparser.add_argument('-v', '--verbose', action='count', default=0,
                               help='increase verbosity')
        argparser.add_argument('-d', '--debug', action='store_true',
                               help='enable debug mode')
        args = argparser.parse_args()
        debug = args.debug

        loglevel = max(DEBUG, ERROR - (10 * args.verbose))
        loglevel = min(ERROR, loglevel)
        if debug:
            formatter = Formatter('%(asctime)s.%(msecs)03d %(name)-20s '
                                  '%(message)s', '%H:%M:%S')
        else:
            formatter = Formatter('%(message)s')
        FtdiLogger.set_formatter(formatter)
        FtdiLogger.set_level(loglevel)
        FtdiLogger.log.addHandler(StreamHandler(stderr))

        if args.virtual:
            # pylint: disable=import-outside-toplevel
            from pyftdi.usbtools import UsbTools
            # Force PyUSB to use PyFtdi test framework for USB backends
            UsbTools.BACKENDS = ('pyftdi.tests.backend.usbvirt', )
            # Ensure the virtual backend can be found and is loaded
            backend = UsbTools.find_backend()
            loader = backend.create_loader()()
            loader.load(args.virtual)

        try:
            add_custom_devices(Ftdi, args.vidpid, force_hex=True)
        except ValueError as exc:
            argparser.error(str(exc))

        Ftdi.show_devices()

    except (ImportError, IOError, NotImplementedError, ValueError) as exc:
        print(f'\nError: {exc}', file=stderr)
        if debug:
            print(format_exc(chain=False), file=stderr)
        sys_exit(1)
    except KeyboardInterrupt:
        sys_exit(2)


if __name__ == '__main__':
    main()


================================================
FILE: pyftdi/bin/i2cscan.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Copyright (c) 2018-2024, Emmanuel Blot <emmanuel.blot@free.fr>
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""Tiny I2C bus scanner."""

# pylint: disable=broad-except

from argparse import ArgumentParser, FileType
from logging import Formatter, StreamHandler, getLogger, DEBUG, ERROR
from sys import exit as sys_exit, modules, stderr
from traceback import format_exc
from pyftdi import FtdiLogger
from pyftdi.ftdi import Ftdi
from pyftdi.i2c import I2cController, I2cNackError
from pyftdi.misc import add_custom_devices


class I2cBusScanner:
    """Scan I2C bus to find slave.

       Emit the I2C address message, but no data. Detect any ACK on each valid
       address.
    """

    SMB_READ_RANGE = list(range(0x30, 0x38)) + list(range(0x50, 0x60))

    HIGHEST_I2C_SLAVE_ADDRESS = 0x78

    @classmethod
    def scan(cls, url: str, smb_mode: bool = True, force: bool = False) \
            -> None:
        """Scan an I2C bus to detect slave device.

           :param url: FTDI URL
           :param smb_mode: whether to use SMBbus restrictions or regular I2C
                            mode.
        """
        i2c = I2cController()
        slaves = []
        getLogger('pyftdi.i2c').setLevel(ERROR)
        try:
            i2c.set_retry_count(1)
            i2c.force_clock_mode(force)
            i2c.configure(url)
            for addr in range(cls.HIGHEST_I2C_SLAVE_ADDRESS+1):
                port = i2c.get_port(addr)
                if smb_mode:
                    try:
                        if addr in cls.SMB_READ_RANGE:
                            port.read(0)
                            slaves.append('R')
                        else:
                            port.write([])
                            slaves.append('W')
                    except I2cNackError:
                        slaves.append('.')
                else:
                    try:
                        port.read(0)
                        slaves.append('R')
                        continue
                    except I2cNackError:
                        pass
                    try:
                        port.write([])
                        slaves.append('W')
                    except I2cNackError:
                        slaves.append('.')
        finally:
            i2c.terminate()
        columns = 16
        row = 0
        print('  ', ''.join(f' {col:01X} ' for col in range(columns)))
        while True:
            chunk = slaves[row:row+columns]
            if not chunk:
                break
            print(f' {row//columns:01X}:', '  '.join(chunk))
            row += columns


def main():
    """Entry point."""
    debug = False
    try:
        argparser = ArgumentParser(description=modules[__name__].__doc__)
        argparser.add_argument('device', nargs='?', default='ftdi:///?',
                               help='serial port device name')
        argparser.add_argument('-S', '--no-smb', action='store_true',
                               default=False,
                               help='use regular I2C mode vs. SMBbus scan')
        argparser.add_argument('-P', '--vidpid', action='append',
                               help='specify a custom VID:PID device ID, '
                                    'may be repeated')
        argparser.add_argument('-V', '--virtual', type=FileType('r'),
                               help='use a virtual device, specified as YaML')
        argparser.add_argument('-v', '--verbose', action='count', default=0,
                               help='increase verbosity')
        argparser.add_argument('-d', '--debug', action='store_true',
                               help='enable debug mode')
        argparser.add_argument('-F', '--force', action='store_true',
                               help='force clock mode (for FT2232D)')
        args = argparser.parse_args()
        debug = args.debug

        if not args.device:
            argparser.error('Serial device not specified')

        loglevel = max(DEBUG, ERROR - (10 * args.verbose))
        loglevel = min(ERROR, loglevel)
        if debug:
            formatter = Formatter('%(asctime)s.%(msecs)03d %(name)-20s '
                                  '%(message)s', '%H:%M:%S')
        else:
            formatter = Formatter('%(message)s')
        FtdiLogger.log.addHandler(StreamHandler(stderr))
        FtdiLogger.set_formatter(formatter)
        FtdiLogger.set_level(loglevel)

        if args.virtual:
            # pylint: disable=import-outside-toplevel
            from pyftdi.usbtools import UsbTools
            # Force PyUSB to use PyFtdi test framework for USB backends
            UsbTools.BACKENDS = ('pyftdi.tests.backend.usbvirt', )
            # Ensure the virtual backend can be found and is loaded
            backend = UsbTools.find_backend()
            loader = backend.create_loader()()
            loader.load(args.virtual)

        try:
            add_custom_devices(Ftdi, args.vidpid, force_hex=True)
        except ValueError as exc:
            argparser.error(str(exc))

        I2cBusScanner.scan(args.device, not args.no_smb, args.force)

    except (ImportError, IOError, NotImplementedError, ValueError) as exc:
        print(f'\nError: {exc}', file=stderr)
        if debug:
            print(format_exc(chain=False), file=stderr)
        sys_exit(1)
    except KeyboardInterrupt:
        sys_exit(2)


if __name__ == '__main__':
    try:
        main()
    except Exception as _exc:
        print(str(_exc), file=stderr)


================================================
FILE: pyftdi/bin/pyterm.py
================================================
#!/usr/bin/env python3

"""Simple Python serial terminal
"""

# Copyright (c) 2010-2024, Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2016, Emmanuel Bouaziz <ebouaziz@free.fr>
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

# pylint: disable=broad-except
# pylint: disable=wrong-import-position

from argparse import ArgumentParser, FileType
from atexit import register
from collections import deque
from logging import Formatter, StreamHandler, DEBUG, ERROR
from os import environ, linesep, stat
from re import search
from sys import exit as sys_exit, modules, platform, stderr, stdout
from time import sleep
from threading import Event, Thread
from traceback import format_exc
from _thread import interrupt_main

# pylint: disable=import-error
# pylint: disable=import-outside-toplevel

from pyftdi import FtdiLogger
from pyftdi.ftdi import Ftdi
from pyftdi.misc import to_bps, add_custom_devices
from pyftdi.term import Terminal


class MiniTerm:
    """A mini serial terminal to demonstrate pyserial extensions"""

    DEFAULT_BAUDRATE = 115200

    def __init__(self, device, baudrate=None, parity=None, rtscts=False,
                 debug=False):
        self._terminal = Terminal()
        self._device = device
        self._baudrate = baudrate or self.DEFAULT_BAUDRATE
        self._port = self._open_port(self._device, self._baudrate, parity,
                                     rtscts, debug)
        self._resume = False
        self._silent = False
        self._rxq = deque()
        self._rxe = Event()
        self._debug = debug
        register(self._cleanup)

    def run(self, fullmode=False, loopback=False, silent=False,
            localecho=False, autocr=False):
        """Switch to a pure serial terminal application"""

        self._terminal.init(fullmode)
        print(f'Entering minicom mode @ { self._port.baudrate} bps')
        stdout.flush()
        self._resume = True
        # start the reader (target to host direction) within a dedicated thread
        args = [loopback]
        if self._device.startswith('ftdi://'):
            # with pyftdi/pyusb/libusb stack, there is no kernel buffering
            # which means that a UART source with data burst may overflow the
            # FTDI HW buffer while the SW stack is dealing with formatting
            # and console output. Use an intermediate thread to pop out data
            # out from the HW as soon as it is made available, and use a deque
            # to serve the actual reader thread
            args.append(self._get_from_source)
            sourcer = Thread(target=self._sourcer, daemon=True)
            sourcer.start()
        else:
            # regular kernel buffered device
            args.append(self._get_from_port)
        reader = Thread(target=self._reader, args=tuple(args), daemon=True)
        reader.start()
        # start the writer (host to target direction)
        self._writer(fullmode, silent, localecho, autocr)

    def _sourcer(self):
        try:
            while self._resume:
                data = self._port.read(4096)
                if not data:
                    continue
                self._rxq.append(data)
                self._rxe.set()
        except Exception as ex:
            self._resume = False
            print(str(ex), file=stderr)
            interrupt_main()

    def _get_from_source(self):
        while not self._rxq and self._resume:
            if self._rxe.wait(0.1):
                self._rxe.clear()
                break
        if not self._rxq:
            return bytearray()
        return self._rxq.popleft()

    def _get_from_port(self):
        try:
            return self._port.read(4096)
        except OSError as ex:
            self._resume = False
            print(str(ex), file=stderr)
            interrupt_main()
            return bytearray()
        except Exception as ex:
            print(str(ex), file=stderr)
            return bytearray()

    def _reader(self, loopback, getfunc):
        """Loop forever, processing received serial data in terminal mode"""
        try:
            # Try to read as many bytes as possible at once, and use a short
            # timeout to avoid blocking for more data
            self._port.timeout = 0.050
            while self._resume:
                if self._silent:
                    sleep(0.25)
                    continue
                data = getfunc()
                if data:
                    stdout.write(data.decode('utf8', errors='replace'))
                    stdout.flush()
                if loopback:
                    self._port.write(data)
        except KeyboardInterrupt:
            return
        except Exception as exc:
            print(f'Exception: {exc}')
            if self._debug:
                print(format_exc(chain=False), file=stderr)
            interrupt_main()

    def _writer(self, fullmode, silent, localecho, crlf=0):
        """Loop and copy console->serial until EOF character is found"""
        while self._resume:
            try:
                char = self._terminal.getkey()
                if fullmode and ord(char) == 0x2:    # Ctrl+B
                    self._cleanup(True)
                    return
                if self._terminal.IS_MSWIN:
                    if ord(char) in (0, 224):
                        char = self._terminal.getkey()
                        self._port.write(self._terminal.getch_to_escape(char))
                        continue
                    if ord(char) == 0x3:    # Ctrl+C
                        raise KeyboardInterrupt('Ctrl-C break')
                if silent:
                    if ord(char) == 0x6:    # Ctrl+F
                        self._silent = True
                        print('Silent\n')
                        continue
                    if ord(char) == 0x7:    # Ctrl+G
                        self._silent = False
                        print('Reg\n')
                        continue
                if localecho:
                    stdout.write(char.decode('utf8', errors='replace'))
                    stdout.flush()
                if crlf:
                    if char == b'\n':
                        self._port.write(b'\r')
                        if crlf > 1:
                            continue
                self._port.write(char)
            except KeyError:
                continue
            except KeyboardInterrupt:
                if fullmode:
                    if self._terminal.IS_MSWIN:
                        self._port.write(b'\x03')
                    continue
                self._cleanup(True)

    def _cleanup(self, *args):
        """Cleanup resource before exiting"""
        if args and args[0]:
            print(f'{linesep}Aborting...')
        try:
            self._resume = False
            if self._port:
                # wait till the other thread completes
                sleep(0.5)
                try:
                    rem = self._port.inWaiting()
                except IOError:
                    # maybe a bug in underlying wrapper...
                    rem = 0
                # consumes all the received bytes
                for _ in range(rem):
                    self._port.read()
                self._port.close()
                self._port = None
                print('Bye.')
        except Exception as ex:
            print(str(ex), file=stderr)
        finally:
            if self._terminal:
                self._terminal.reset()
                self._terminal = None

    @staticmethod
    def _open_port(device, baudrate, parity, rtscts, debug=False):
        """Open the serial communication port"""
        try:
            from serial.serialutil import SerialException
            from serial import PARITY_NONE
        except ImportError as exc:
            raise ImportError("Python serial module not installed") from exc
        try:
            from serial import serial_for_url, VERSION as serialver
            # use a simple regex rather than adding a new dependency on the
            # more complete 'packaging' module
            vmo = search(r'^(\d+)\.(\d+)', serialver)
            if not vmo:
                # unable to parse version
                raise ValueError()
            if tuple(int(x) for x in vmo.groups()) < (3, 0):
                # pysrial version is too old
                raise ValueError()
        except (ValueError, IndexError, ImportError) as exc:
            raise ImportError("pyserial 3.0+ is required") from exc
        # the following import enables serial protocol extensions
        if device.startswith('ftdi:'):
            try:
                from pyftdi import serialext
                serialext.touch()
            except ImportError as exc:
                raise ImportError("PyFTDI module not installed") from exc
        try:
            port = serial_for_url(device,
                                  baudrate=baudrate,
                                  parity=parity or PARITY_NONE,
                                  rtscts=rtscts,
                                  timeout=0)
            if not port.is_open:
                port.open()
            if not port.is_open:
                raise IOError(f"Cannot open port '{device}'")
            if debug:
                backend = port.BACKEND if hasattr(port, 'BACKEND') else '?'
                print(f"Using serial backend '{backend}'")
            return port
        except SerialException as exc:
            raise IOError(str(exc)) from exc


def get_default_device() -> str:
    """Return the default comm device, depending on the host/OS."""
    envdev = environ.get('FTDI_DEVICE', '')
    if envdev:
        return envdev
    if platform == 'win32':
        device = 'COM1'
    elif platform == 'darwin':
        device = '/dev/cu.usbserial'
    elif platform == 'linux':
        device = '/dev/ttyS0'
    else:
        device = ''
    try:
        stat(device)
    except OSError:
        device = 'ftdi:///1'
    return device


def main():
    """Main routine"""
    debug = False
    try:
        default_device = get_default_device()
        argparser = ArgumentParser(description=modules[__name__].__doc__)
        argparser.add_argument('-f', '--fullmode', dest='fullmode',
                               action='store_true',
                               help='use full terminal mode, exit with '
                                    '[Ctrl]+B')
        argparser.add_argument('device', nargs='?', default=default_device,
                               help=f'serial port device name '
                                    f'(default: {default_device}')
        argparser.add_argument('-b', '--baudrate',
                               default=str(MiniTerm.DEFAULT_BAUDRATE),
                               help=f'serial port baudrate '
                                    f'(default: {MiniTerm.DEFAULT_BAUDRATE})')
        argparser.add_argument('-w', '--hwflow',
                               action='store_true',
                               help='hardware flow control')
        argparser.add_argument('-e', '--localecho',
                               action='store_true',
                               help='local echo mode (print all typed chars)')
        argparser.add_argument('-r', '--crlf',
                               action='count', default=0,
                               help='prefix LF with CR char, use twice to '
                                    'replace all LF with CR chars')
        argparser.add_argument('-l', '--loopback',
                               action='store_true',
                               help='loopback mode (send back all received '
                                    'chars)')
        argparser.add_argument('-s', '--silent', action='store_true',
                               help='silent mode')
        argparser.add_argument('-P', '--vidpid', action='append',
                               help='specify a custom VID:PID device ID, '
                                    'may be repeated')
        argparser.add_argument('-V', '--virtual', type=FileType('r'),
                               help='use a virtual device, specified as YaML')
        argparser.add_argument('-v', '--verbose', action='count',
                               help='increase verbosity')
        argparser.add_argument('-d', '--debug', action='store_true',
                               help='enable debug mode')
        args = argparser.parse_args()
        debug = args.debug

        if not args.device:
            argparser.error('Serial device not specified')

        loglevel = max(DEBUG, ERROR - (10 * (args.verbose or 0)))
        loglevel = min(ERROR, loglevel)
        if debug:
            formatter = Formatter('%(asctime)s.%(msecs)03d %(name)-20s '
                                  '%(message)s', '%H:%M:%S')
        else:
            formatter = Formatter('%(message)s')
        FtdiLogger.set_formatter(formatter)
        FtdiLogger.set_level(loglevel)
        FtdiLogger.log.addHandler(StreamHandler(stderr))

        if args.virtual:
            from pyftdi.usbtools import UsbTools
            # Force PyUSB to use PyFtdi test framework for USB backends
            UsbTools.BACKENDS = ('pyftdi.tests.backend.usbvirt', )
            # Ensure the virtual backend can be found and is loaded
            backend = UsbTools.find_backend()
            loader = backend.create_loader()()
            loader.load(args.virtual)

        try:
            add_custom_devices(Ftdi, args.vidpid, force_hex=True)
        except ValueError as exc:
            argparser.error(str(exc))

        miniterm = MiniTerm(device=args.device,
                            baudrate=to_bps(args.baudrate),
                            parity='N',
                            rtscts=args.hwflow,
                            debug=args.debug)
        miniterm.run(args.fullmode, args.loopback, args.silent, args.localecho,
                     args.crlf)

    except (IOError, ValueError) as exc:
        print(f'\nError: {exc}', file=stderr)
        if debug:
            print(format_exc(chain=False), file=stderr)
        sys_exit(1)
    except KeyboardInterrupt:
        sys_exit(2)


if __name__ == '__main__':
    main()


================================================
FILE: pyftdi/bin/uphy.sh
================================================
#!/bin/sh

# Load/unload kernel extension helper for macOS

system=$(uname -s)
if [ "${system}" != "Darwin" ]; then
    echo "This script is dedicated to macOS" >&2
    exit 1
fi
version=$(sw_vers -productVersion | cut -d. -f2)
if [ ${version} -lt 9 ]; then
    echo "This version of OS X does not use an Apple FTDI driver"
    exit 0
fi
if [ ${version} -gt 13 ]; then
    echo "Apple FTDI driver on this macOS version should not be unloaded" >&2
    exit 1
fi
kextstat 2>/dev/null | grep com.apple.driver.AppleUSBFTDI > /dev/null
if [ $? -eq 0 ]; then
    echo "Admin priviledge required to unload Apple FTDI driver"
    sudo kextunload -v -bundle com.apple.driver.AppleUSBFTDI
else
   if [ "$1" = "0" ]; then
      echo "Admin priviledge required to load Apple FTDI driver"
      sudo kextload -v -bundle com.apple.driver.AppleUSBFTDI
   fi
fi


================================================
FILE: pyftdi/bits.py
================================================
# Copyright (c) 2010-2024 Emmanuel Blot <emmanuel.blot@free.fr>
# Copyright (c) 2008-2016, Neotion
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""Bit field and sequence management."""

from typing import Iterable, List, Optional, Tuple, Union
from .misc import is_iterable, xor

# pylint: disable=invalid-name
# pylint: disable=unneeded-not
# pylint: disable=duplicate-key


class BitSequenceError(Exception):
    """Bit sequence error"""


class BitSequence:
    """Bit sequence.

       Support most of the common bit operations: or, and, shift, comparison,
       and conversion from and to integral values.

       Bit sequence objects are iterable.

       Can be initialized with another bit sequence, a integral value,
       a sequence of bytes or an iterable of common boolean values.

       :param value:  initial value
       :param msb:    most significant bit first or not
       :param length: count of signficant bits in the bit sequence
       :param bytes_: initial value specified as a sequence of bytes
       :param msby:   most significant byte first or not
    """

    def __init__(self, value: Union['BitSequence', str, int] = None,
                 msb: bool = False, length: int = 0,
                 bytes_: Optional[bytes] = None, msby: bool = True):
        """Instantiate a new bit sequence.
        """
        self._seq = bytearray()
        seq = self._seq
        if value and bytes_:
            raise BitSequenceError("Cannot inialize with both a value and "
                                   "bytes")
        if bytes_:
            provider = list(bytes_).__iter__() if msby else reversed(bytes_)
            for byte in provider:
                if isinstance(byte, str):
                    byte = ord(byte)
                elif byte > 0xff:
                    raise BitSequenceError("Invalid byte value")
                b = []
                for _ in range(8):
                    b.append(bool(byte & 0x1))
                    byte >>= 1
                if msb:
                    b.reverse()
                seq.extend(b)
        else:
            value = self._tomutable(value)
        if isinstance(value, int):
            self._init_from_integer(value, msb, length)
        elif isinstance(value, BitSequence):
            self._init_from_sibling(value, msb)
        elif is_iterable(value):
            self._init_from_iterable(value, msb)
        elif value is None:
            pass
        else:
            raise BitSequenceError(f"Cannot initialize from '{type(value)}'")
        self._update_length(length, msb)

    def sequence(self) -> bytearray:
        """Return the internal representation as a new mutable sequence"""
        return bytearray(self._seq)

    def reverse(self) -> 'BitSequence':
        """In-place reverse"""
        self._seq.reverse()
        return self

    def invert(self) -> 'BitSequence':
        """In-place invert sequence values"""
        self._seq = bytearray([x ^ 1 for x in self._seq])
        return self

    def append(self, seq) -> 'BitSequence':
        """Concatenate a new BitSequence"""
        if not isinstance(seq, BitSequence):
            seq = BitSequence(seq)
        self._seq.extend(seq.sequence())
        return self

    def lsr(self, count: int) -> None:
        """Left shift rotate"""
        count %= len(self)
        self._seq[:] = self._seq[count:] + self._seq[:count]

    def rsr(self, count: int) -> None:
        """Right shift rotate"""
        count %= len(self)
        self._seq[:] = self._seq[-count:] + self._seq[:-count]

    def tobit(self) -> bool:
        """Degenerate the sequence into a single bit, if possible"""
        if len(self) != 1:
            raise BitSequenceError("BitSequence should be a scalar")
        return bool(self._seq[0])

    def tobyte(self, msb: bool = False) -> int:
        """Convert the sequence into a single byte value, if possible"""
        if len(self) > 8:
            raise BitSequenceError("Cannot fit into a single byte")
        byte = 0
        pos = -1 if not msb else 0
        # copy the sequence
        seq = self._seq[:]
        while seq:
            byte <<= 1
            byte |= seq.pop(pos)
        return byte

    def tobytes(self, msb: bool = False, msby: bool = False) -> bytearray:
        """Convert the sequence into a sequence of byte values"""
        blength = (len(self)+7) & (~0x7)
        sequence = list(self._seq)
        if not msb:
            sequence.reverse()
        bytes_ = bytearray()
        for pos in range(0, blength, 8):
            seq = sequence[pos:pos+8]
            byte = 0
            while seq:
                byte <<= 1
                byte |= seq.pop(0)
            bytes_.append(byte)
        if msby:
            bytes_.reverse()
        return bytes_

    @staticmethod
    def _tomutable(value: Union[str, Tuple]) -> List:
        """Convert a immutable sequence into a mutable one"""
        if isinstance(value, tuple):
            # convert immutable sequence into a list so it can be popped out
            value = list(value)
        elif isinstance(value, str):
            # convert immutable sequence into a list so it can be popped out
            if value.startswith('0b'):
                value = list(value[2:])
            else:
                value = list(value)
        return value

    def _init_from_integer(self, value: int, msb: bool, length: int) -> None:
        """Initialize from any integer value"""
        bl = length or -1
        seq = self._seq
        while bl:
            seq.append(bool(value & 1))
            value >>= 1
            if not value:
                break
            bl -= 1
        if msb:
            seq.reverse()

    def _init_from_iterable(self, iterable: Iterable, msb: bool) -> None:
        """Initialize from an iterable"""
        smap = {'0': 0, '1': 1, False: 0, True: 1, 0: 0, 1: 1}
        seq = self._seq
        try:
            if msb:
                seq.extend([smap[bit] for bit in reversed(iterable)])
            else:
                seq.extend([smap[bit] for bit in iterable])
        except KeyError as exc:
            raise BitSequenceError('Invalid binary character in initializer') \
                    from exc

    def _init_from_sibling(self, value: 'BitSequence', msb: bool) -> None:
        """Initialize from a fellow object"""
        self._seq = value.sequence()
        if msb:
            self._seq.reverse()

    def _update_length(self, length, msb):
        """If a specific length is specified, extend the sequence as
           expected"""
        if length and (len(self) < length):
            extra = bytearray([False] * (length-len(self)))
            if msb:
                extra.extend(self._seq)
                self._seq = extra
            else:
                self._seq.extend(extra)

    def __iter__(self):
        return self._seq.__iter__()

    def __reversed__(self):
        return self._seq.__reversed__()

    def __getitem__(self, index):
        if isinstance(index, slice):
            return self.__class__(value=self._seq[index])
        return self._seq[index]

    def __setitem__(self, index, value):
        if isinstance(value, BitSequence):
            if issubclass(value.__class__, self.__class__) and \
               value.__class__ != self.__class__:
                raise BitSequenceError("Cannot set item with instance of a "
                                       "subclass")
        if isinstance(index, slice):
            value = self.__class__(value, length=len(self._seq[index]))
            self._seq[index] = value.sequence()
        else:
            if not isinstance(value, BitSequence):
                value = self.__class__(value)
            val = value.tobit()
            if index > len(self._seq):
                raise BitSequenceError("Cannot change the sequence size")
            self._seq[index] = val

    def __len__(self):
        return len(self._seq)

    def __eq__(self, other):
        return self._cmp(other) == 0

    def __ne__(self, other):
        return not self == other

    def __le__(self, other):
        return not self._cmp(other) <= 0

    def __lt__(self, other):
        return not self._cmp(other) < 0

    def __ge__(self, other):
        return not self._cmp(other) >= 0

    def __gt__(self, other):
        return not self._cmp(other) > 0

    def _cmp(self, other):
        # the bit sequence should be of the same length
        ld = len(self) - len(other)
        if ld:
            return ld
        for n, (x, y) in enumerate(zip(self._seq, other.sequence()), start=1):
            if xor(x, y):
                return n
        return 0

    def __repr__(self):
        # cannot use bin() as it truncates the MSB zero bits
        return ''.join([b and '1' or '0' for b in reversed(self._seq)])

    def __str__(self):
        chunks = []
        srepr = repr(self)
        length = len(self)
        for i in range(0, length, 8):
            if i:
                j = -i
            else:
                j = None
            chunks.append(srepr[-i-8:j])
        return f'{len(self)}: {" ".join(reversed(chunks))}'

    def __int__(self):
        value = 0
        for b in reversed(self._seq):
            value <<= 1
            value |= b and 1
        return value

    def __and__(self, other):
        if not isinstance(other, self.__class__):
            raise BitSequenceError('Need a BitSequence to combine')
        if len(self) != len(other):
            raise BitSequenceError('Sequences must be the same size')
        return self.__class__(value=list(map(lambda x, y: x and y,
                                             self._seq, other.sequence())))

    def __or__(self, other):
        if not isinstance(other, self.__class__):
            raise BitSequenceError('Need a BitSequence to combine')
        if len(self) != len(other):
            raise BitSequenceError('Sequences must be the same size')
        return self.__class__(value=list(map(lambda x, y: x or y,
                                             self._seq, other.sequence())))

    def __add__(self, other):
        return self.__class__(value=self._seq + other.sequence())

    def __ilshift__(self, count):
        count %= len(self)
        seq = bytearray([0]*count)
        seq.extend(self._seq[:-count])
        self._seq = seq
        return self

    def __irshift__(self, count):
        count %= len(self)
        seq = self._seq[count:]
        seq.extend([0]*count)
        self._seq = seq
        return self

    def inc(self) -> None:
        """Increment the sequence"""
        for p, b in enumerate(self._seq):
            b ^= True
            self._seq[p] = b
            if b:
                break

    def dec(self) -> None:
        """Decrement the sequence"""
        for p, b in enumerate(self._seq):
            b ^= True
            self._seq[p] = b
            if not b:
                break

    def invariant(self) -> bool:
        """Tells whether all bits of the sequence are of the same value.

           Return the value, or ValueError if the bits are not of the same
           value
        """
        try:
            ref = self._seq[0]
        except IndexError as exc:
            raise ValueError('Empty sequence') from exc
        if len(self._seq) == 1:
            return ref
        for b in self._seq[1:]:
            if b != ref:
                raise ValueError('Bits do no match')
        return ref


class BitZSequence(BitSequence):
    """Tri-state bit sequence manipulation.

       Support most of the BitSequence operations, with an extra high-Z state

       :param value:  initial value
       :param msb:    most significant bit first or not
       :param length: count of signficant bits in the bit sequence
    """

    __slots__ = ['_seq']

    Z = 0xff  # maximum byte value

    def __init__(self, value=None, msb=False, length=0):
        BitSequence.__init__(self, value=value, msb=msb, length=length)

    def invert(self):
        self._seq = [x in (None, BitZSequence.Z) and BitZSequence.Z or x ^ 1
                     for x in self._seq]
        return self

    def tobyte(self, msb=False):
        raise BitSequenceError(f'Type {type(self)} cannot be converted to '
                               f'byte')

    def tobytes(self, msb=False, msby=False):
        raise BitSequenceError(f'Type {type(self)} cannot be converted to '
                               f'bytes')

    def matches(self, other):
        # pylint: disable=missing-function-docstring
        if not isinstance(self, BitSequence):
            raise BitSequenceError('Not a BitSequence instance')
        # the bit sequence should be of the same length
        ld = len(self) - len(other)
        if ld:
            return ld
        for (x, y) in zip(self._seq, other.sequence()):
            if BitZSequence.Z in (x, y):
                continue
            if x is not y:
                return False
        return True

    def _init_from_iterable(self, iterable, msb):
        """Initialize from an iterable"""
        smap = {'0': 0, '1': 1, 'Z': BitZSequence.Z,
                False: 0, True: 1, None: BitZSequence.Z,
                0: 0, 1: 1, BitZSequence.Z: BitZSequence.Z}
        seq = self._seq
        try:
            if msb:
                seq.extend([smap[bit] for bit in reversed(iterable)])
            else:
                seq.extend([smap[bit] for bit in iterable])
        except KeyError as exc:
            raise BitSequenceError("Invalid binary character in initializer") \
                    from exc

    def __repr__(self):
        smap = {False: '0', True: '1', BitZSequence.Z: 'Z'}
        return ''.join([smap[b] for b in reversed(self._seq)])

    def __int__(self):
        if BitZSequence.Z in self._seq:
            raise BitSequenceError("High-Z BitSequence cannot be converted to "
                                   "an integral type")
        return BitSequence.__int__(self)

    def __cmp__(self, other):
        # the bit sequence should be of the same length
        ld = len(self) - len(other)
        if ld:
            return ld
        for n, (x, y) in enumerate(zip(self._seq, other.sequence()), start=1):
            if x is not y:
                return n
        return 0

    def __and__(self, other):
        if not isinstance(self, BitSequence):
            raise BitSequenceError('Need a BitSequence-compliant object to '
                                   'combine')
        if len(self) != len(other):
            raise BitSequenceError('Sequences must be the same size')

        def andz(x, y):
            """Compute the boolean AND operation for a tri-state boolean"""
            if BitZSequence.Z in (x, y):
                return BitZSequence.Z
            return x and y
        return self.__class__(
            value=list(map(andz, self._seq, other.sequence())))

    def __or__(self, other):
        if not isinstance(self, BitSequence):
            raise BitSequenceError('Need a BitSequence-compliant object to '
                                   'combine')
        if len(self) != len(other):
            raise BitSequenceError('Sequences must be the same size')

        def orz(x, y):
            """Compute the boolean OR operation for a tri-state boolean"""
            if BitZSequence.Z in (x, y):
                return BitZSequence.Z
            return x or y
        return self.__class__(value=list(map(orz, self._seq,
                                             other.sequence())))

    def __rand__(self, other):
        return self.__and__(other)

    def __ror__(self, other):
        return self.__or__(other)

    def __radd__(self, other):
        return self.__class__(value=other) + self


class BitField:
    """Bit field class to access and modify an integral value

       Beware the slices does not behave as regular Python slices:
       bitfield[3:5] means b3..b5, NOT b3..b4 as with regular slices
    """

    __slots__ = ['_val']

    def __init__(self, value=0):
        self._val = value

    def to_seq(self, msb=0, lsb=0):
        """Return the BitFiled as a sequence of boolean value"""
        seq = bytearray()
        count = 0
        value = self._val
        while value:
            count += 1
            value >>= 1
        for x in range(lsb, max(msb, count)):
            seq.append(bool((self._val >> x) & 1))
        return tuple(reversed(seq))

    def __getitem__(self, index):
        if isinstance(index, slice):
            if index.stop == index.start:
                return None
            if index.stop < index.start:
                offset = index.stop
                count = index.start-index.stop+1
            else:
                offset = index.start
                count = index.stop-index.start+1
            mask = (1 << count)-1
            return (self._val >> offset) & mask
        return (self._val >> index) & 1

    def __setitem__(self, index, value):
        if isinstance(index, slice):
            if index.stop == index.start:
                return
            if index.stop < index.start:
                offset = index.stop
                count = index.start-index.stop+1
            else:
                offset = index.start
                count = index.stop-index.start+1
            mask = (1 << count)-1
            value = (value & mask) << offset
            mask <<= offset
            self._val = (self._val & ~mask) | value
        else:
            if isinstance(value, bool):
                value = int(value)
            value = (value & int(1)) << index
            mask = int(1) << index
            self._val = (self._val & ~mask) | value

    def __int__(self):
        return self._val

    def __str__(self):
        return bin(self._val)


================================================
FILE: pyftdi/doc/api/eeprom.rst
================================================

.. include:: ../defs.rst

:mod:`eeprom` - EEPROM API
--------------------------

.. module :: pyftdi.eeprom


Quickstart
~~~~~~~~~~

Example: dump the EEPROM content

.. code-block:: python

    # Instantiate an EEPROM manager
    eeprom = FtdiEeprom()

    # Select the FTDI device to access (the interface is mandatory but any
    # valid interface for the device fits)
    eeprom.open('ftdi://ftdi:2232h/1')

    # Show the EEPROM content
    eeprom.dump_config()

    # Show the raw EEPROM content
    from pyftdi.misc import hexdump
    print(hexdump(eeprom.data))


Example: update the serial number

.. code-block:: python

    # Instantiate an EEPROM manager
    eeprom = FtdiEeprom()

    # Select the FTDI device to access
    eeprom.open('ftdi://ftdi:2232h/1')

    # Change the serial number
    eeprom.set_serial_number('123456')

    # Commit the change to the EEPROM
    eeprom.commit(dry_run=False)


Classes
~~~~~~~

.. autoclass :: FtdiEeprom
 :members:


Exceptions
~~~~~~~~~~

.. autoexception :: FtdiEepromError


Tests
~~~~~

.. code-block:: shell

   # optional: specify an alternative FTDI device
   export FTDI_DEVICE=ftdi://ftdi:2232h/1
   PYTHONPATH=. python3 pyftdi/tests/eeprom.py


================================================
FILE: pyftdi/doc/api/ftdi.rst
================================================
.. -*- coding: utf-8 -*-

.. include:: ../defs.rst

:mod:`ftdi` - FTDI low-level driver
-----------------------------------

.. module :: pyftdi.ftdi

This module implements access to the low level FTDI hardware. There are very
few reasons to use this module directly. Most of PyFtdi_ features are available
through the dedicated :doc:`APIs <index>`.

Classes
~~~~~~~

.. autoclass :: Ftdi
 :members:


Exceptions
~~~~~~~~~~

.. autoexception :: FtdiError
.. autoexception :: FtdiMpsseError
.. autoexception :: FtdiFeatureError


================================================
FILE: pyftdi/doc/api/gpio.rst
================================================
.. -*- coding: utf-8 -*-

.. include:: ../defs.rst

:mod:`gpio` - GPIO API
----------------------

.. module :: pyftdi.gpio

Direct drive GPIO pins of FTDI device.

.. note::

  This mode is mutually exclusive with advanced serial MPSSE features, such as
  |I2C|, SPI, JTAG, ...

  If you need to use GPIO pins and MPSSE interface on the same port, you need
  to use the dedicated API. This shared mode is supported with the
  :doc:`SPI API <spi>` and the :doc:`I2C API <i2c>`.

.. warning::

  This API does not provide access to the special CBUS port of FT232R, FT232H,
  FT230X and FT231X devices. See :ref:`cbus_gpio` for details.

Quickstart
~~~~~~~~~~

See ``tests/gpio.py`` example


Classes
~~~~~~~

.. autoclass :: GpioPort

.. autoclass :: GpioAsyncController
 :members:

.. autoclass :: GpioSyncController
 :members:

.. autoclass :: GpioMpsseController
 :members:


Exceptions
~~~~~~~~~~

.. autoexception :: GpioException


Info about GPIO API
~~~~~~~~~~~~~~~~~~~

See :doc:`../gpio` for details


================================================
FILE: pyftdi/doc/api/i2c.rst
================================================

.. include:: ../defs.rst

:mod:`i2c` - |I2C| API
----------------------

.. module :: pyftdi.i2c


Quickstart
~~~~~~~~~~

Example: communication with an |I2C| GPIO expander

.. code-block:: python

    # Instantiate an I2C controller
    i2c = I2cController()

    # Configure the first interface (IF/1) of the FTDI device as an I2C master
    i2c.configure('ftdi://ftdi:2232h/1')

    # Get a port to an I2C slave device
    slave = i2c.get_port(0x21)

    # Send one byte, then receive one byte
    slave.exchange([0x04], 1)

    # Write a register to the I2C slave
    slave.write_to(0x06, b'\x00')

    # Read a register from the I2C slave
    slave.read_from(0x00, 1)

Example: mastering the |I2C| bus with a complex transaction

.. code-block:: python

   from time import sleep

   port = I2cController().get_port(0x56)

   # emit a START sequence is read address, but read no data and keep the bus
   # busy
   port.read(0, relax=False)

   # wait for ~1ms
   sleep(0.001)

   # write 4 bytes, without neither emitting the start or stop sequence
   port.write(b'\x00\x01', relax=False, start=False)

   # read 4 bytes, without emitting the start sequence, and release the bus
   port.read(4, start=False)

See also pyi2cflash_ module and ``tests/i2c.py``, which provide more detailed
examples on how to use the |I2C| API.


Classes
~~~~~~~

.. autoclass :: I2cPort
 :members:

.. autoclass :: I2cGpioPort
 :members:

.. autoclass :: I2cController
 :members:


Exceptions
~~~~~~~~~~

.. autoexception :: I2cIOError
.. autoexception :: I2cNackError
.. autoexception:: I2cTimeoutError


GPIOs
~~~~~

See :doc:`../gpio` for details

Tests
~~~~~

|I2C| sample tests expect:
  * TCA9555 device on slave address 0x21
  * ADXL345 device on slave address 0x53

Checkout a fresh copy from PyFtdi_ github repository.

See :doc:`../pinout` for FTDI wiring.

.. code-block:: shell

   # optional: specify an alternative FTDI device
   export FTDI_DEVICE=ftdi://ftdi:2232h/1
   # optional: increase log level
   export FTDI_LOGLEVEL=DEBUG
   # be sure to connect the appropriate I2C slaves to the FTDI I2C bus and run
   PYTHONPATH=. python3 pyftdi/tests/i2c.py


.. _i2c_limitations:

Caveats
~~~~~~~

Open-collector bus
``````````````````

|I2C| uses only two bidirectional open collector (or open drain) lines, pulled
up with resistors. These resistors are also required on an |I2C| bus when an
FTDI master is used.

However, most FTDI devices do not use open collector outputs. Some software
tricks are used to fake open collector mode when possible, for example to
sample for slave ACK/NACK, but most communication (R/W, addressing, data)
cannot use open collector mode. This means that most FTDI devices source
current to the SCL and SDA lines. FTDI HW is able to cope with conflicting
signalling, where FTDI HW forces a line the high logical level while a slave
forces it to the low logical level, and limits the sourced current. You may
want to check your schematics if the slave is not able to handle 4 .. 16 mA
input current in SCL and SDA, for example. The maximal source current depends
on the FTDI device and the attached EEPROM configuration which may be used to
limit further down the sourced current.

Fortunately, FT232H device is fitted with real open collector outputs, and
PyFtdi always enable this mode on SCL and SDA lines when a FT232H device is
used.

Other FTDI devices such as FT2232H, FT4232H and FT4232HA do not support open
collector mode, and source current to SCL and SDA lines.

Clock streching
```````````````

Clock stretching is supported through a hack that re-uses the JTAG adaptative
clock mode designed for ARM devices. FTDI HW drives SCL on ``AD0`` (`BD0`), and
samples the SCL line on : the 8\ :sup:`th` pin of a port ``AD7`` (``BD7``).

When a FTDI device without an open collector capability is used
(FT2232H, FT4232H, FT4232HA) the current sourced from AD0 may prevent proper
sampling ofthe SCL line when the slave attempts to strech the clock. It is
therefore recommended to add a low forward voltage drop diode to `AD0` to
prevent AD0 to source current to the SCL bus. See the wiring section.

Speed
`````

Due to the FTDI MPSSE engine limitations, the actual bitrate for write
operations over I2C is very slow. As the I2C protocol enforces that each I2C
exchanged byte needs to be acknowledged by the peer, a I2C byte cannot be
written to the slave before the previous byte has been acknowledged by the
slave and read back by the I2C master, that is the host. This requires several
USB transfer for each byte, on top of each latency of the USB stack may add up.
With the introduction of PyFtdi_ v0.51, read operations have been optimized so
that long read operations are now much faster thanwith previous PyFtdi_
versions, and exhibits far shorter latencies.

Use of PyFtdi_ should nevetherless carefully studied and is not recommended if
you need to achieve medium to high speed write operations with a slave
(relative to the I2C clock...). Dedicated I2C master such as FT4222H device is
likely a better option, but is not currently supported with PyFtdi_ as it uses
a different communication protocol.

.. _i2c_wiring:

Wiring
~~~~~~

.. figure:: ../images/i2c_wiring.png
   :scale: 50 %
   :alt: I2C wiring
   :align: right

   Fig.1: FT2232H with clock stretching

* ``AD0`` should be connected to the SCL bus
* ``AD1`` and ``AD2`` should be both connected to the SDA bus
* ``AD7`` should be connected to the SCL bus, if clock streching is required
* remaining pins can be freely used as regular GPIOs.

*Fig.1*:

* ``D1`` is only required when clock streching is used along with
  FT2232H, FT4232H or FT4232HA devices. It should not be fit with an FT232H.
* ``AD7`` may be used as a regular GPIO with clock stretching is not required.


================================================
FILE: pyftdi/doc/api/index.rst
================================================
API documentation
=================

.. include:: ../defs.rst

|release|
---------

.. toctree::
   :maxdepth: 1
   :glob:

   ftdi
   gpio
   i2c
   spi
   uart
   usbtools
   misc
   eeprom


================================================
FILE: pyftdi/doc/api/misc.rst
================================================
.. -*- coding: utf-8 -*-

:mod:`misc` - Miscellaneous helpers
-----------------------------------

Functions
~~~~~~~~~

.. automodule:: pyftdi.misc
   :members:



================================================
FILE: pyftdi/doc/api/spi.rst
================================================
.. include:: ../defs.rst

:mod:`spi` - SPI API
--------------------

.. module :: pyftdi.spi

Quickstart
~~~~~~~~~~

Example: communication with a SPI data flash (half-duplex example)

.. code-block:: python

    # Instantiate a SPI controller
    spi = SpiController()

    # Configure the first interface (IF/1) of the FTDI device as a SPI master
    spi.configure('ftdi://ftdi:2232h/1')

    # Get a port to a SPI slave w/ /CS on A*BUS3 and SPI mode 0 @ 12MHz
    slave = spi.get_port(cs=0, freq=12E6, mode=0)

    # Request the JEDEC ID from the SPI slave
    jedec_id = slave.exchange([0x9f], 3)


Example: communication with a remote SPI device using full-duplex mode

.. code-block:: python

    # Instantiate a SPI controller
    # We need want to use A*BUS4 for /CS, so at least 2 /CS lines should be
    # reserved for SPI, the remaining IO are available as GPIOs.
    spi = SpiController(cs_count=2)

    # Configure the first interface (IF/1) of the FTDI device as a SPI master
    spi.configure('ftdi://ftdi:2232h/1')

    # Get a port to a SPI slave w/ /CS on A*BUS4 and SPI mode 2 @ 10MHz
    slave = spi.get_port(cs=1, freq=10E6, mode=2)

    # Synchronous exchange with the remote SPI slave
    write_buf = b'\x01\x02\x03'
    read_buf = slave.exchange(write_buf, duplex=True)

Example: communication with a SPI device and an extra GPIO

.. code-block:: python

    # Instantiate a SPI controller
    spi = SpiController()

    # Configure the first interface (IF/1) of the first FTDI device as a
    # SPI master
    spi.configure('ftdi://::/1')

    # Get a SPI port to a SPI slave w/ /CS on A*BUS3 and SPI mode 0 @ 12MHz
    slave = spi.get_port(cs=0, freq=12E6, mode=0)

    # Get GPIO port to manage extra pins, use A*BUS4 as GPO, A*BUS5 as GPI
    gpio = spi.get_gpio()
    gpio.set_direction(pins=0b0011_0000, direction=0b0001_0000)

    # Assert GPO pin
    gpio.write(0x10)
    # Write to SPI slace
    slave.write(b'hello world!')
    # Release GPO pin
    gpio.write(0x00)
    # Test GPI pin
    pin = bool(gpio.read() & 0x20)


Example: managing non-byte aligned transfers

.. code-block:: python

    # Instantiate a SPI controller
    spi = SpiController()

    # Configure the first interface (IF/1) of the first FTDI device as a
    # SPI master
    spi.configure('ftdi://::/1')

    # Get a SPI port to a SPI slave w/ /CS on A*BUS3
    slave = spi.get_port(cs=0)

    # write 6 first bits of a byte buffer
    slave.write(b'\xff', droptail=2)

    # read only 13 bits from a slave (13 clock cycles)
    # only the 5 MSBs of the last byte are valid, 3 LSBs are force to zero
    slave.read(2, droptail=3)

See also pyspiflash_ module and ``tests/spi.py``, which provide more detailed
examples on how to use the SPI API.


Classes
~~~~~~~

.. autoclass :: SpiPort
 :members:

.. autoclass :: SpiGpioPort
 :members:

.. autoclass :: SpiController
 :members:

Exceptions
~~~~~~~~~~

.. autoexception :: SpiIOError


GPIOs
~~~~~

See :doc:`../gpio` for details

Tests
~~~~~

SPI sample tests expect:
  * MX25L1606E device on /CS 0, SPI mode 0
  * ADXL345 device on /CS 1, SPI mode 2
  * RFDA2125 device on /CS 2, SPI mode 0

Checkout a fresh copy from PyFtdi_ github repository.

See :doc:`../pinout` for FTDI wiring.

.. code-block:: shell

   # optional: specify an alternative FTDI device
   export FTDI_DEVICE=ftdi://ftdi:2232h/1
   # optional: increase log level
   export FTDI_LOGLEVEL=DEBUG
   # be sure to connect the appropriate SPI slaves to the FTDI SPI bus and run
   PYTHONPATH=. python3 pyftdi/tests/spi.py

.. _spi_limitations:

Limitations
~~~~~~~~~~~

SPI Modes 1 & 3
```````````````

FTDI hardware does not support cpha=1 (mode 1 and mode 3). As stated in
Application Node 114:

   "*It is recommended that designers review the SPI Slave
   data sheet to determine the SPI mode implementation. FTDI device can only
   support mode 0 and mode 2 due to the limitation of MPSSE engine.*".

Support for mode 1 and mode 3 is implemented with some workarounds, but
generated signals may not be reliable: YMMV. It is only available with -H
series (232H, 2232H, 4232H, 4232HA).

The 3-clock phase mode which has initially be designed to cope with |I2C|
signalling is used to delay the data lines from the clock signals. A direct
consequence of this workaround is that SCLK duty cycle is not longer 50% but
25% (mode 1) or 75% (mode 3). Again, support for mode 1 and mode 3 should be
considered as a kludge, you've been warned.

Time-sensitive usage
````````````````````

Due to the MPSSE engine limitation, it is not possible to achieve
time-controlled request sequence. In other words, if the SPI slave needs to
receive command sequences at precise instants - for example ADC or DAC
devices - PyFtdi_ use is not recommended. This limitation is likely to apply
to any library that relies on FTDI device. The USB bus latency and the lack
of timestamped commands always add jitter and delays, with no easy known
workaround.

.. _spi_wiring:

Wiring
~~~~~~

.. figure:: ../images/spi_wiring.png
   :scale: 50 %
   :alt: SPI wiring
   :align: right

   Fig.1: FT2232H with two SPI slaves

* ``AD0`` should be connected to SCLK
* ``AD1`` should be connected to MOSI
* ``AD2`` should be connected to MISO
* ``AD3`` should be connected to the first slave /CS.
* ``AD4`` should be connected to the second slave /CS, if any
* remaining pins can be freely used as regular GPIOs.

*Fig.1*:

* ``AD4`` may be used as a regular GPIO if a single SPI slave is used
* ``AD5`` may be used as another /CS signal for a third slave, in this case
  the first available GPIO is ``AD6``, etc.


================================================
FILE: pyftdi/doc/api/uart.rst
================================================
.. include:: ../defs.rst

:mod:`serialext` - UART API
---------------------------

There is no dedicated module for the UART API, as PyFtdi_ acts as a backend of
the well-known pyserial_ module.

The pyserial_ backend module is implemented as the `serialext.protocol_ftdi`
module. It is not documented here as no direct call to this module is required,
as the UART client should use the regular pyserial_ API.

Usage
~~~~~

To enable PyFtdi_ as a pyserial_ backend, use the following import:

.. code-block:: python

    import pyftdi.serialext

Then use

.. code-block:: python

    pyftdi.serialext.serial_for_url(url, **options)

to open a pyserial_ serial port instance.


Quickstart
~~~~~~~~~~

.. code-block:: python

    # Enable pyserial extensions
    import pyftdi.serialext

    # Open a serial port on the second FTDI device interface (IF/2) @ 3Mbaud
    port = pyftdi.serialext.serial_for_url('ftdi://ftdi:2232h/2', baudrate=3000000)

    # Send bytes
    port.write(b'Hello World')

    # Receive bytes
    data = port.read(1024)

.. _uart_gpio:

GPIO access
~~~~~~~~~~~

UART mode, the primary function of FTDI \*232\* devices, is somewhat limited
when it comes to GPIO management, as opposed to alternative mode such as |I2C|,
SPI and JTAG. It is not possible to assign the unused pins of an UART mode to
arbitrary GPIO functions.

All the 8 lower pins of an UART port are dedicated to the UART function,
although most of them are seldomely used, as dedicated to manage a modem or a
legacy DCE_ device. Upper pins (b\ :sub:`7`\ ..b\ :sub:`15`\ ), on devices that
have ones, cannot be driven while UART port is enabled.

It is nevertheless possible to have limited access to the lower pins as GPIO,
with many limitations:

- the GPIO direction of each pin is hardcoded and cannot be changed
- GPIO pins cannot be addressed atomically: it is possible to read the state
  of an input GPIO, or to change the state of an output GPIO, one after
  another. This means than obtaining the state of several input GPIOs or
  changing the state of several output GPIO at once is not possible.
- some pins cannot be used as GPIO is hardware flow control is enabled.
  Keep in mind However that HW flow control with FTDI is not reliable, see the
  :ref:`hardware_flow_control` section.

Accessing those GPIO pins is done through the UART extended pins, using their
UART assigned name, as PySerial port attributes. See the table below:

+---------------+------+-----------+-------------------------------+
| Bit           | UART | Direction | API                           |
+===============+======+===========+===============================+
| b\ :sub:`0`\  | TX   | Out       | ``port.write(buffer)``        |
+---------------+------+-----------+-------------------------------+
| b\ :sub:`1`\  | RX   | In        | ``buffer = port.read(count)`` |
+---------------+------+-----------+-------------------------------+
| b\ :sub:`2`\  | RTS  | Out       | ``port.rts = state``          |
+---------------+------+-----------+-------------------------------+
| b\ :sub:`3`\  | CTS  | In        | ``state = port.cts``          |
+---------------+------+-----------+-------------------------------+
| b\ :sub:`4`\  | DTR  | Out       | ``port.dtr = state``          |
+---------------+------+-----------+-------------------------------+
| b\ :sub:`5`\  | DSR  | In        | ``state = port.dsr``          |
+---------------+------+-----------+-------------------------------+
| b\ :sub:`6`\  | DCD  | In        | ``state = port.dcd``          |
+---------------+------+-----------+-------------------------------+
| b\ :sub:`7`\  | RI   | In        | ``state = port.ri``           |
+---------------+------+-----------+-------------------------------+

CBUS support
````````````

Some FTDI devices (FT232R, FT232H, FT230X, FT231X) support additional CBUS
pins, which can be used as regular GPIOs pins. See :ref:`CBUS GPIO<cbus_gpio>`
for details.


.. _pyterm:

Mini serial terminal
~~~~~~~~~~~~~~~~~~~~

``pyterm.py`` is a simple serial terminal that can be used to test the serial
port feature. See the :ref:`tools` chapter to locate this tool.

::

  Usage: pyterm.py [-h] [-f] [-b BAUDRATE] [-w] [-e] [-r] [-l] [-s] [-P VIDPID]
                   [-V VIRTUAL] [-v] [-d]
                   [device]

  Simple Python serial terminal

  positional arguments:
    device                serial port device name (default: ftdi:///1)

  optional arguments:
    -h, --help            show this help message and exit
    -f, --fullmode        use full terminal mode, exit with [Ctrl]+B
    -b BAUDRATE, --baudrate BAUDRATE
                          serial port baudrate (default: 115200)
    -w, --hwflow          hardware flow control
    -e, --localecho       local echo mode (print all typed chars)
    -r, --crlf            prefix LF with CR char, use twice to replace all LF
                          with CR chars
    -l, --loopback        loopback mode (send back all received chars)
    -s, --silent          silent mode
    -P VIDPID, --vidpid VIDPID
                          specify a custom VID:PID device ID, may be repeated
    -V VIRTUAL, --virtual VIRTUAL
                          use a virtual device, specified as YaML
    -v, --verbose         increase verbosity
    -d, --debug           enable debug mode

If the PyFtdi module is not yet installed and ``pyterm.py`` is run from the
archive directory, ``PYTHONPATH`` should be defined to the current directory::

    PYTHONPATH=$PWD pyftdi/bin/pyterm.py ftdi:///?

The above command lists all the available FTDI device ports. To avoid conflicts
with some shells such as `zsh`, escape the `?` char as ``ftdi:///\?``.

To start up a serial terminal session, specify the FTDI port to use, for
example:

.. code-block:: shell

    # detect all FTDI connected devices
    PYTHONPATH=. python3 pyftdi/bin/ftdi_urls.py
    # use the first interface of the first FT2232H as a serial port
    PYTHONPATH=$PWD pyftdi/bin/pyterm.py ftdi://ftdi:2232/1


.. _uart-limitations:

Limitations
~~~~~~~~~~~

Although the FTDI H series are in theory capable of 12 MBps baudrate, baudrates
above 6 Mbps are barely usable.

See the following table for details.

+------------+-------------+------------+-------------+------------+--------+
|  Requ. bps |HW capability| 9-bit time |  Real bps   | Duty cycle | Stable |
+============+=============+============+=============+============+========+
| 115.2 Kbps |  115.2 Kbps |   78.08 µs | 115.26 Kbps |     49.9%  |  Yes   |
+------------+-------------+------------+-------------+------------+--------+
| 460.8 Kbps | 461.54 Kbps |   19.49 µs | 461.77 Kbps |     49.9%  |  Yes   |
+------------+-------------+------------+-------------+------------+--------+
|     1 Mbps |      1 Mbps |   8.98 µs  |  1.002 Mbps |     49.5%  |  Yes   |
+------------+-------------+------------+-------------+------------+--------+
|     4 Mbps |      4 Mbps |   2.24 µs  |  4.018 Mbps |       48%  |  Yes   |
+------------+-------------+------------+-------------+------------+--------+
|     5 Mbps |  5.052 Mbps |   1.78 µs  |  5.056 Mbps |       50%  |  Yes   |
+------------+-------------+------------+-------------+------------+--------+
|     6 Mbps |      6 Mbps |   1.49 µs  |  6.040 Mbps |     48.5%  |  Yes   |
+------------+-------------+------------+-------------+------------+--------+
|     7 Mbps |  6.857 Mbps |   1.11 µs  |  8.108 Mbps |       44%  |   No   |
+------------+-------------+------------+-------------+------------+--------+
|     8 Mbps |      8 Mbps |   1.11 µs  |  8.108 Mbps |   44%-48%  |   No   |
+------------+-------------+------------+-------------+------------+--------+
|   8.8 Mbps |  8.727 Mbps |   1.13 µs  |  7.964 Mbps |       44%  |   No   |
+------------+-------------+------------+-------------+------------+--------+
|   9.6 Mbps |    9.6 Mbps |   1.12 µs  |  8.036 Mbps |       48%  |   No   |
+------------+-------------+------------+-------------+------------+--------+
|  10.5 Mbps | 10.667 Mbps |   1.11 µs  |  8.108 Mbps |       44%  |   No   |
+------------+-------------+------------+-------------+------------+--------+
|    12 Mbps |     12 Mbps |   0.75 µs  |     12 Mbps |       43%  |  Yes   |
+------------+-------------+------------+-------------+------------+--------+

 * 9-bit time is the measured time @ FTDI output pins for a 8-bit character
   (start bit + 8 bit data)
 * Duty cycle is the ratio between a low-bit duration and a high-bit duration,
   a good UART should exhibit the same duration for low bits and high bits,
   *i.e.* a duty cycle close to 50%.
 * Stability reports whether subsequent runs, with the very same HW settings,
   produce the same timings.

Achieving a reliable connection over 6 Mbps has proven difficult, if not
impossible: Any baudrate greater than 6 Mbps (except the upper 12 Mbps limit)
results into an actual baudrate of about 8 Mbps, and suffer from clock
fluterring [7.95 .. 8.1Mbps].

.. _hardware_flow_control:

Hardware flow control
`````````````````````

Moreover, as the hardware flow control of the FTDI device is not a true HW
flow control. Quoting FTDI application note:

   *If CTS# is logic 1 it is indicating the external device cannot accept more
   data. the FTxxx will stop transmitting within 0~3 characters, depending on
   what is in the buffer.*
   **This potential 3 character overrun does occasionally present problems.**
   *Customers shoud be made aware the FTxxx is a USB device and not a "normal"
   RS232 device as seen on a PC. As such the device operates on a packet
   basis as opposed to a byte basis.*



================================================
FILE: pyftdi/doc/api/usbtools.rst
================================================
.. -*- coding: utf-8 -*-

:mod:`usbtools` - USB tools
---------------------------

.. module :: pyftdi.usbtools


Classes
~~~~~~~

.. autoclass :: UsbTools
 :members:


Exceptions
~~~~~~~~~~

.. autoexception :: UsbToolsError


================================================
FILE: pyftdi/doc/authors.rst
================================================
Authors
-------

Main developers
~~~~~~~~~~~~~~~

 * Emmanuel Blot <emmanuel.blot@free.fr>
 * Emmanuel Bouaziz <ebouaziz@free.fr>

Contributors
~~~~~~~~~~~~

 * Nikus-V
 * Dave McCoy
 * Adam Feuer
 * endlesscoil
 * humm (Fabien Benureau)
 * dlharmon
 * DavidWC
 * Sebastian
 * Anders (anders-code)
 * Andrea Concil
 * Darren Garnier
 * Michael Leonard
 * nopeppermint (Stefan)
 * hannesweisbach
 * Vianney le Clément de Saint-Marcq
 * Pete Schwamb
 * Will Richey
 * sgoadhouse
 * tavip (Octavian Purdila)
 * Tim Legrand
 * vestom
 * meierphil
 * etherfi
 * sgoadhouse
 * jnmacd
 * naushir
 * markmelvin (Mark Melvin)
 * stiebrs
 * mpratt14
 * alexforencich
 * TedKus
 * Amanita-muscaria
 * len0rd
 * Rod Whitby
 * Kornel Swierzy
 * Taisuke Yamada
 * Michael Niewöhner
 * Kalofin
 * Henry Au-Yeung
 * Roman Dobrodii
 * Mark Mentovai
 * Alessandro Zini
 * Sjoerd Simons
 * David Schneider


================================================
FILE: pyftdi/doc/conf.py
================================================
# Copyright (c) 2010-2024 Emmanuel Blot <emmanuel.blot@free.fr>
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

# pylint: skip-file

import os
import re
import sys

# pip3 install wheel
# pip3 install sphinx-autodoc-typehints sphinx-pypi-upload sphinx_rtd_theme
# python3 setup.py build_sphinx
# sphinx-build -b html ../pyftdi/pyftdi/doc .

topdir = os.path.abspath(os.path.join(os.path.dirname(__file__),
                                      os.pardir, os.pardir))
sys.path.append(topdir)


def read(where, *parts):
    """
    Build an absolute path from *parts* and and return the contents of the
    resulting file.  Assume UTF-8 encoding.
    """
    with open(os.path.join(where, *parts), 'rt') as f:
        return f.read()


def find_meta(meta):
    """
    Extract __*meta*__ from meta_file.
    """
    meta_match = re.search(
        r"^__{meta}__ = ['\"]([^'\"]*)['\"]".format(meta=meta),
        meta_file, re.M
    )
    if meta_match:
        return meta_match.group(1)
    raise RuntimeError("Unable to find __{meta}__ string.".format(meta=meta))


meta_file = read(topdir, 'pyftdi', '__init__.py')

version = find_meta('version')

needs_sphinx = '2.1'
extensions = ['sphinx.ext.autodoc',
              'sphinx.ext.doctest',
              'sphinx_autodoc_typehints']
templates_path = ['templates']
source_suffix = '.rst'
master_doc = 'index'
project = find_meta('title')
contact = '%s <%s>' % (find_meta('author'), find_meta('email'))
copyright = '2010-2025, %s' % contact
show_authors = True

html_theme = 'sphinx_rtd_theme'
htmlhelp_basename = 'doc'

preamble = r'''
\usepackage{wallpaper}
\usepackage{titlesec}

\titleformat{\chapter}[display]{}{\filleft\scshape\chaptername\enspace\thechapter}{-2pt}{\filright \Huge \bfseries}[\vskip4.5pt\titlerule]
\titleformat{name=\chapter, numberless}[block]{}{}{0pt}{\filright \Huge \bfseries}[\vskip4.5pt\titlerule]

\titlespacing{\chapter}{0pt}{0pt}{1cm}
'''

latex_elements = {
  'papersize': 'a4paper',
  'fncychap': '',  # No Title Page
  'releasename': '',
  'sphinxsetup': 'hmargin={2.0cm,2.0cm}, vmargin={2.5cm,2.5cm}, marginpar=5cm',
  'classoptions': ',openany,oneside',  # Avoid blank page aftre TOC, etc.
  'preamble': preamble,
  'releasename': ''
}

latex_documents = [
  ('index', '%s.tex' % project.lower(),
   '%s Documentation' % project,
   contact, u'manual'),
]

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
latex_toplevel_sectioning = "chapter"

man_pages = [
  ('index', project,
   '%s Documentation' % project,
   [contact], 1)
]

texinfo_documents = [
  ('index', project,
   '%s Documentation' % project,
   contact, '',
   '%s Documentation' % project,
   'Miscellaneous'),
]


def setup(app):
    app.add_css_file('https://fonts.googleapis.com/css?family=Raleway')


================================================
FILE: pyftdi/doc/defs.rst
================================================
.. |I2C| replace:: I\ :sup:`2`\ C

.. _FT232R: https://www.ftdichip.com/Products/ICs/FT232R.htm
.. _FT230X: https://www.ftdichip.com/Products/ICs/FT230X.html
.. _FT2232D: https://www.ftdichip.com/Products/ICs/FT2232D.htm
.. _FT232H: https://www.ftdichip.com/Products/ICs/FT232H.htm
.. _FT2232H: https://www.ftdichip.com/Products/ICs/FT2232H.html
.. _FT4232H: https://www.ftdichip.com/Products/ICs/FT4232H.htm
.. _FT4232HA: http://ftdichip.com/products/ft4232haq/
.. _FTDI_Recovery: https://www.ftdichip.com/Support/Documents/AppNotes/AN_136%20Hi%20Speed%20Mini%20Module%20EEPROM%20Disaster%20Recovery.pdf
.. _PyFtdi: https://www.github.com/eblot/pyftdi
.. _PyFtdiTools: https://github.com/eblot/pyftdi/tree/master/pyftdi/bin
.. _PyJtagTools: https://www.github.com/eblot/pyjtagtools
.. _FTDI: https://www.ftdichip.com/
.. _PyUSB: https://pyusb.github.io/pyusb/
.. _Python: https://www.python.org/
.. _pyserial: https://pythonhosted.org/pyserial/
.. _libftdi: https://www.intra2net.com/en/developer/libftdi/
.. _pyspiflash: https://github.com/eblot/pyspiflash/
.. _pyi2cflash: https://github.com/eblot/pyi2cflash/
.. _libusb: https://www.libusb.info/
.. _Libusb on Windows: https://github.com/libusb/libusb/wiki/Windows
.. _Libusb win32: https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/
.. _Zadig: https://zadig.akeo.ie/
.. _FTDI macOS guide: https://www.ftdichip.com/Support/Documents/AppNotes/AN_134_FTDI_Drivers_Installation_Guide_for_MAC_OSX.pdf
.. _Libusb issue on macOs: https://github.com/libusb/libusb/commit/5e45e0741daee4fa295c6cc977edfb986c872152
.. _FT_PROG: https://www.ftdichip.com/Support/Utilities.htm#FT_PROG
.. _fstring: https://www.python.org/dev/peps/pep-0498
.. _DCE: https://en.wikipedia.org/wiki/Data_circuit-terminating_equipment
.. _PEP_498: https://www.python.org/dev/peps/pep-0498
.. _PEP_526: https://www.python.org/dev/peps/pep-0526
.. _ruamel.yaml: https://pypi.org/project/ruamel.yaml
.. _PyFtdiWin: https://github.com/mariusgreuel/pyftdiwin

.. Restructured Text levels

..   Level 1
..   -------

..   Level 2
..   ~~~~~~~

..   Level 3
..   ```````

..   Level 4
..   .......

..   Level 5
..   +++++++


================================================
FILE: pyftdi/doc/eeprom.rst
================================================
.. include:: defs.rst

EEPROM management
-----------------

.. warning::
   Writing to the EEPROM can cause very **undesired** effects if the wrong
   value is written in the wrong place. You can even essentially **brick** your
   FTDI device. Use this function only with **extreme** caution.

   It is not recommended to use this application with devices that use an
   internal EEPROM such as FT232R or FT-X series, as if something goes wrong,
   recovery options are indeed limited. FT232R internal EEPROM seems to be
   unstable, even the official FT_PROG_ tool from FTDI may fail to fix it on
   some conditions.

   If using a Hi-Speed Mini Module and you brick for FTDI device, see
   FTDI_Recovery_


Supported features
~~~~~~~~~~~~~~~~~~

EEPROM support is under active development.

Some features may be wrongly decoded, as each FTDI model implements a different
feature map, and more test/validation are required.

The :doc:`EEPROM API <api/eeprom>` implements the upper API to access the
EEPROM content.

.. _ftconf:

EEPROM configuration tool
~~~~~~~~~~~~~~~~~~~~~~~~~

``ftconf.py`` is a companion script to help managing the content of the FTDI
EEPROM from the command line. See the :ref:`tools` chapter to locate this tool.

::

  usage: ftconf.py [-h] [-i INPUT] [-l {all,raw,values}] [-o OUTPUT] [-V VIRTUAL]
                   [-P VIDPID] [-M EEPROM] [-S {128,256,1024}] [-x] [-X HEXBLOCK]
                   [-s SERIAL_NUMBER] [-m MANUFACTURER] [-p PRODUCT] [-c CONFIG]
                   [--vid VID] [--pid PID] [-e] [-E] [-u] [-v] [-d]
                   [device]

  Simple FTDI EEPROM configurator.

  positional arguments:
    device                serial port device name

  optional arguments:
    -h, --help            show this help message and exit

  Files:
    -i INPUT, --input INPUT
                          input ini file to load EEPROM content
    -l {all,raw,values}, --load {all,raw,values}
                          section(s) to load from input file
    -o OUTPUT, --output OUTPUT
                          output ini file to save EEPROM content
    -V VIRTUAL, --virtual VIRTUAL
                          use a virtual device, specified as YaML

  Device:
    -P VIDPID, --vidpid VIDPID
                          specify a custom VID:PID device ID (search for FTDI devices)
    -M EEPROM, --eeprom EEPROM
                          force an EEPROM model
    -S {128,256,1024}, --size {128,256,1024}
                          force an EEPROM size

  Format:
    -x, --hexdump         dump EEPROM content as ASCII
    -X HEXBLOCK, --hexblock HEXBLOCK
                          dump EEPROM as indented hexa blocks

  Configuration:
    -s SERIAL_NUMBER, --serial-number SERIAL_NUMBER
                          set serial number
    -m MANUFACTURER, --manufacturer MANUFACTURER
                          set manufacturer name
    -p PRODUCT, --product PRODUCT
                          set product name
    -c CONFIG, --config CONFIG
                          change/configure a property as key=value pair
    --vid VID             shortcut to configure the USB vendor ID
    --pid PID             shortcut to configure the USB product ID

  Action:
    -e, --erase           erase the whole EEPROM content
    -E, --full-erase      erase the whole EEPROM content, including the CRC
    -u, --update          perform actual update, use w/ care

  Extras:
    -v, --verbose         increase verbosity
    -d, --debug           enable debug mode

**Again, please read the** :doc:`license` **terms before using the EEPROM API
or this script. You may brick your device if something goes wrong, and there
may be no way to recover your device.**

Note that to protect the EEPROM content of unexpected modification, it is
mandatory to specify the :ref:`-u <option_u>` flag along any alteration/change
of the EEPROM content. Without this flag, the script performs a dry-run
execution of the changes, *i.e.* all actions but the write request to the
EEPROM are executed.

Once updated, you need to unplug/plug back the device to use the new EEPROM
configuration.

It is recommended to first save the current content of the EEPROM, using the
:ref:`-o <option_o>` flag, to have a working copy of the EEPROM data before any
attempt to modify it. It can help restoring the EEPROM if something gets wrong
during a subsequence update, thanks to the :ref:`-i <option_i>` option switch.

Most FTDI device can run without an EEPROM. If something goes wrong, try to
erase the EEPROM content, then restore the original content.


Option switches
```````````````
In addition to the :ref:`common_option_switches` for  PyFtdi_ tools,
``ftconf.py`` support the following arguments:

.. _option_c:

``-c name=value``
  Change a configuration in the EEPROM. This flag can be repeated as many times
  as required to change several configuration parameter at once. Note that
  without option ``-u``, the EEPROM content is not actually modified, the
  script runs in dry-run mode.

  The name should be separated from the value with an equal ``=`` sign or
  alternatively a full column ``:`` character.

  * To obtain the list of supported name, use the `?` wildcard: ``-c ?``, or
    `-c help` to avoid conflicts with some shells
  * To obtain the list of supported values for a name, use the `?` or the `help`
    wildcard:
    ``-c name=help``, where *name* is a supported name.

  See :ref:`cbus_func` table for the alternate function associated with each
  name.

.. _option_E_:

``-E``
  Erase the full EEPROM content including the CRC. As the CRC no longer
  validates the EEPROM content, the EEPROM configuration is ignored on the next
  power cycle of the device, so the default FTDI configuration is used.

  This may be useful to recover from a corrupted EEPROM, as when no EEPROM or a
  blank EEPROM is detected, the FTDI falls back to a default configuration.

  Note that without option :ref:`-u <option_u>`, the EEPROM content is not
  actually modified, the script runs in dry-run mode.

.. _option_e:

``-e``
  Erase the whole EEPROM and regenerates a valid CRC.

  Beware that as `-e` option generates a valid CRC for the erased EEPROM
  content, the FTDI device may identified itself as VID:PID FFFF:FFFF on next
  reboot. You should likely use the `--vid` and `--pid` option to define a
  valid FDTI device USB identifier with this option to ensure the device
  identifies itself as a FTDI device on next power cycle.

  Note that without option :ref:`-u <option_u>`, the EEPROM content is not
  actually modified, the script runs in dry-run mode.

  Alternatively, use `-E` option that erase the full EEPROM content including
  the CRC.

.. _option_i:

``-i``
  Load a INI file (as generated with the :ref:`-o <option_o>` option switch. It
  is possible to select which section(s) from the INI file are loaded, using
  :ref:`-l <option_l>` option switch. The ``values`` section may be modified,
  as it takes precedence over the ``raw`` section. Note that without option
  :ref:`-u <option_u>`, the EEPROM content is not actually modified, the script
  runs in dry-run mode.

.. _option_l:

``-l <all|raw|values>``
  Define which section(s) of the INI file are used to update the EEPROM content
  along with the :ref:`-i <option_i>` option switch. Defaults to ``all``.

  The supported feature set of the ``values`` is the same as the one exposed
  through the :ref:`-c <option_c>` option switch. Unsupported feature are
  ignored, and a warning is emitted for each unsupported feature.

.. _option_M_:

``-M <model>``
  Specify the EEPROM model (93c46, 93c56, 93c66) that is connected to the FTDI
  device. There is no reason to use this option except for recovery purposes,
  see option `-E`. It is mutually exclusive with the `-S` option.

.. _option_m:

``-m <manufacturer>``
  Assign a new manufacturer name to the device. Note that without option
  :ref:`-u <option_u>`, the EEPROM content is not actually modified, the script
  runs in dry-run mode. Manufacturer names with ``/`` or ``:`` characters are
  rejected, to avoid parsing issues with FTDI :ref:`URLs <url_scheme>`.

.. _option_o:

``-o <output>``
  Generate and write to the specified file the EEPROM content as decoded
  values and a hexa dump. The special ``-`` file can be used as the output file
  to print to the standard output. The output file contains two sections:

  * ``[values]`` that contain the decoded EEPROM configuration as key, value
    pair. Note that the keys and values can be used as configuration input, see
    option :ref:`-c <option_c>`.
  * ``[raw]`` that contains a compact representation of the EEPROM raw content,
    encoded as hexadecimal strings.

.. _option_p:

``-p <product>``
  Assign a new product name to the device. Note that without option :ref:`-u
  <option_u>`, the EEPROM content is not actually modified, the script runs in
  dry-run mode. Product names with ``/`` or ``:`` characters are rejected, to
  avoid parsing issues with FTDI :ref:`URLs <url_scheme>`.

.. _option_pid:

``--pid``
  Define the USB product identifier - as an hexadecimal number. This is a
  shortcut for `-c product_id`

.. _option_S_:

``-S <size>``
  Specify the EEPROM size -in bytes- that is connected to the FTDI device.
  There is no reason to use this option except for recovery purposes,
  see option `-E`. It is mutually exclusive with the `-M` option.

.. _option_s:

``-s <serial>``
  Assign a new serial number to the device. Note that without option :ref:`-u
  <option_u>`, the EEPROM content is not actually modified, the script runs in
  dry-run mode. Serial number with ``/`` or ``:`` characters are rejected, to
  avoid parsing issues with FTDI :ref:`URLs <url_scheme>`.

.. _option_u:

``-u``
  Update the EEPROM with the new settings. Without this flag, the script runs
  in dry-run mode, so no change is made to the EEPROM. Whenever this flag is
  used, the EEPROM is actually updated and its checksum regenerated. If
  something goes wrong at this point, you may brick you board, you've been
  warned. PyFtdi_ offers neither guarantee whatsoever than altering the EEPROM
  content is safe, nor that it is possible to recover from a bricked device.

.. _option_vid:

``--vid``
  Define the USB vendor identifier - as an hexadecimal number. This is a
  shortcut for `-c vendor_id`.

.. _option_x:

``-x``
  Generate and print a hexadecimal raw dump of the EEPROM content, similar to
  the output of the `hexdump -Cv` tool.


.. _cbus_func:

CBUS function
`````````````

The following table describes the CBUS pin alternate functions. Note that
depending on the actual device, some alternate function may not be available.

+-----------------+--------+--------------------------------------------------------------------------------+
| Name            | Active | Description                                                                    |
+=================+========+================================================================================+
| ``TRISTATE``    | Hi-Z   | IO Pad is tri-stated                                                           |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``TXLED``       | Low    | TX activity, can be used as status for LED                                     |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``RXLED``       | Low    | RX activity, can be used as status for LED                                     |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``TXRXLED``     | Low    | TX & RX activity, can be used as status for LED                                |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``PWREN``       | Low    | USB configured, USB suspend: high                                              |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``SLEEP``       | Low    | USB suspend, typically used to power down external devices.                    |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``DRIVE0``      | Low    | Drive a constant (FT232H and FT-X only)                                        |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``DRIVE1``      | High   | Drive a constant (FT232H and FT-X only)                                        |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``GPIO``        |        | IO port for CBUS bit bang mode                                                 |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``TXDEN``       | High   | Enable transmit for RS485 mode                                                 |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``CLK48``       |        | Output 48 MHz clock (FT232R only)                                              |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``CLK30``       |        | Output 30 MHz clock (FT232H only)                                              |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``CLK24``       |        | Output 24 MHz clock (FT232R and FT-X only)                                     |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``CLK15``       |        | Output 12 MHz clock (FT232H only)                                              |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``CLK12``       |        | Output 12 MHz clock (FT232R and FT-X only)                                     |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``CLK7_5``      |        | Output 7.5 MHz clock (FT232H only)                                             |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``CLK6``        |        | Output 6 MHz clock (FT232R and FT-X only)                                      |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``BAT_DETECT``  | High   | Battery Charger Detect (FT-X only)                                             |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``BAT_NDETECT`` | Low    | Inverse signal of BAT_DETECT (FT-X only)                                       |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``I2C_TXE``     | Low    | Transmit buffer empty (FT-X only)                                              |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``I2C_RXF``     | Low    | Receive buffer full  (FT-X only)                                               |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``VBUS_SENSE``  | High   | Detect when VBUS is present via the appropriate AC IO pad (FT-X only)          |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``BB_WR``       | Low    | Synchronous Bit Bang Write strobe (FT232R and FT-X only)                       |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``BB_RD``       | Low    | Synchronous Bit Bang Read strobe (FT232R and FT-X only)                        |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``TIMESTAMP``   |        | Toggle signal each time a USB SOF is received (FT-X only)                      |
+-----------------+--------+--------------------------------------------------------------------------------+
| ``AWAKE``       | Low    | Do not suspend when unplugged/disconnect/suspsend (FT-X only)                  |
+-----------------+--------+--------------------------------------------------------------------------------+


Examples
````````

* Change product name and serial number

  ::

    pyftdi/bin/ftconf.py ftdi:///1 -p UartBridge -s abcd1234 -u

* List supported configuration parameters

  ::

    pyftdi/bin/ftconf.py ftdi:///1 -c ?
      cbus_func_0, cbus_func_1, cbus_func_2, cbus_func_3, cbus_func_4,
      cbus_func_5, cbus_func_6, cbus_func_7, cbus_func_8, cbus_func_9,
      channel_a_driver, channel_a_type, chip, clock_polarity,
      flow_control, group_0_drive, group_0_schmitt, group_0_slew,
      group_1_drive, group_1_schmitt, group_1_slew, has_serial,
      has_usb_version, in_isochronous, lsb_data, out_isochronous,
      power_max, powersave, product_id, remote_wakeup, self_powered,
      suspend_pull_down, type, usb_version, vendor_id

* List supported configuration values for CBUS0

  ::

    pyftdi/bin/ftconf.py ftdi:///1 -c cbus_func_0:?
      AWAKE, BAT_DETECT, BAT_NDETECT, BB_RD, BB_WR, CLK12, CLK24, CLK6,
      DRIVE0, DRIVE1, I2C_RXF, I2C_TXE, GPIO, PWREN, RXLED, SLEEP,
      TIME_STAMP, TRISTATE, TXDEN, TXLED, TXRXLED, VBUS_SENSE

* Erase the whole EEPROM including its CRC.

  Once power cycle, the device should run as if no EEPROM was connected.
  Do not use this with internal, embedded EEPROMs such as FT230X.

  ::

    pyftdi/bin/ftconf.py -P ffff:ffff ftdi://ffff:ffff/1 -E -u

* Recover from an erased EEPROM with a valid CRC

  ::

    # for a FT4232 device
    # note that ffff matches an erased EEPROM, other corrupted values may
    # exist, such device can be identified with system tools such as lsusb

    pyftdi/bin/ftconf.py -P ffff:ffff ftdi://ffff:ffff/1 -e -u \
        --vid 0403 --pid 6011

.. _eeprom_cbus:

* Configure CBUS: 0 and 3 as GPIOs, then show the device configuration

  ::

   pyftdi/bin/ftconf.py ftdi:///1 -v
      -c cbus_func_0:GPIO -c cbus_func_3:GPIO


================================================
FILE: pyftdi/doc/features.rst
================================================
.. include:: defs.rst

Features
--------

Devices
~~~~~~~

* All FTDI device ports (UART, MPSSE) can be used simultaneously.

  * SPI and |I2C| SPI support simultaneous GPIO R/W access for all pins that
    are not used for SPI/|I2C| feature.

* Several FTDI adapters can be accessed simultaneously from the same Python
  runtime instance.

Supported features
~~~~~~~~~~~~~~~~~~

UART
````

Serial port, up to 6 Mbps. PyFtdi_ includes a pyserial_ emulation layer that
offers transparent access to the FTDI serial ports through a pyserial_-
compliant API. The ``serialext`` directory contains a minimal serial terminal
demonstrating the use of this extension, and a dispatcher automatically
selecting the serial backend (pyserial_, PyFtdi_), based on the serial port
name.

See also :ref:`uart-limitations`.

SPI master
``````````

Supported devices:

=====  ===== ====== ============================================================
Mode   CPol   CPha  Status
=====  ===== ====== ============================================================
  0      0      0   Supported on all MPSSE devices
  1      0      1   Workaround available for on -H series
  2      1      0   Supported on -H series (FT232H_/FT2232H_/FT4232H_/FT4232HA_)
  3      1      1   Workaround available for on -H series
=====  ===== ====== ============================================================

PyFtdi_ can be used with pyspiflash_ module that demonstrates how to
use the FTDI SPI master with a pure-Python serial flash device driver for
several common devices.

Both Half-duplex (write or read) and full-duplex (synchronous write and read)
communication modes are supported.

Experimental support for non-byte aligned access, where up to 7 trailing bits
can be discarded: no clock pulse is generated for those bits, so that SPI
transfer of non byte-sized can be performed.

See :ref:`spi_wiring` and :ref:`spi_limitations`.

Note: FTDI*232* devices cannot be used as an SPI slave.

|I2C| master
````````````

Supported devices: FT232H_, FT2232H_, FT4232H_, FT4232HA_

For now, only 7-bit addresses are supported.

GPIOs can be used while |I2C| mode is enabled.

The ``i2cscan.py`` script helps to discover which I2C devices are connected to
the FTDI I2C bus. See the :ref:`tools` chapter to locate this tool.

The pyi2cflash_ module demonstrates how to use the FTDI |I2C| master to access
serial EEPROMS.

See :ref:`i2c_wiring` and :ref:`i2c_limitations`.

Note: FTDI*232* devices cannot be used as an |I2C| slave.

JTAG
````

JTAG API is limited to low-level access. It is not intented to be used for
any flashing or debugging purpose, but may be used as a base to perform SoC
tests and boundary scans.

It requires the PyJtagTools_ Python module which integrates a JTAG engine, while
PyFtdi_ implements the FTDI JTAG backend.

EEPROM
``````

The ``pyftdi/bin/ftconf.py`` script helps to manage the content of the FTDI
companion EEPROM.


Status
~~~~~~

This project is still in beta development stage. PyFtdi_ is developed as an
open-source solution.


================================================
FILE: pyftdi/doc/gpio.rst
================================================
.. include:: defs.rst

GPIOs
-----

Overview
~~~~~~~~

Many PyFtdi APIs give direct access to the IO pins of the FTDI devices:

  * *GpioController*, implemented as ``GpioAsyncController``,
    ``GpioSyncController`` and ``GpioMpsseController`` (see :doc:`api/gpio`)
    gives full access to the FTDI pins as raw I/O pins,
  * ``SpiGpioPort`` (see :doc:`api/spi`) gives access to all free pins of an
    FTDI interface, which are not reserved for the SPI feature,
  * ``I2cGpioPort`` (see :doc:`api/i2c`) gives access to all free pins of an
    FTDI interface, which are not reserved for the I2C feature

Other modes
```````````

  * Gpio raw access is not yet supported with JTAG feature.
  * It is not possible to use GPIO along with UART mode on the same interface.
    However, UART mode still provides (very) limited access to GPIO pins, see
    UART :ref:`uart_gpio` for details.

This document presents the common definitions for these APIs and explain how to
drive those pins.


Definitions
~~~~~~~~~~~

Interfaces
``````````

An FTDI *interface* follows the definition of a *USB interface*: it is an
independent hardware communication port with an FTDI device. Each interface can
be configured independently from the other interfaces on the same device, e.g.
one interface may be configured as an UART, the other one as |I2C| + GPIO.

It is possible to access two distinct interfaces of the same FTDI device
from a multithreaded application, and even from different applications, or
Python interpreters. However two applications cannot access the same interface
at the same time.

.. warning::

   Performing a USB device reset affects all the interfaces of an FTDI device,
   this is the rationale for not automatically performing a device reset when
   an interface is initialiazed and configured from PyFtdi_.

.. _ftdi_ports:

Ports
`````

An FTDI port is ofter used in PyFtdi as a synonym for an interface. This may
differ from the FTDI datasheets that sometimes show an interface with several
ports (A\*BUS, B\*BUS). From a software standpoint, ports and interfaces are
equivalent: APIs access all the HW port from the same interface at once. From a
pure hardware standpoint, a single interface may be depicted as one or two
*ports*.

With PyFtdi_, *ports* and *interfaces* should be considered as synomyms.

Each port can be accessed as raw input/output pins. At a given time, a pin is
either configured as an input or an output function.

The width of a port, that is the number of pins of the interface, depending on
the actual hardware, *i.e.* the FTDI model:

* FT232R features a single port, which is 8-bit wide: `DBUS`,
* FT232H features a single port, which is 16-bit wide: `ADBUS/ACBUS`,
* FT2232D features two ports, which are 12-bit wide each: `ADBUS/ACBUS` and
  `BDBUS/BCBUS`,
* FT2232H features two ports, which are 16-bit wide each: `ADBUS/ACBUS` and
  `BDBUS/BCBUS`,
* FT4232H/FT4232HA features four ports, which are 8-bit wide each: `ADBUS`,
  `BDBUS`, `CDBUS` and `DDBUS`,
* FT230X features a single port, which is 4-bit wide,
* FT231X feature a single port, which is 8-bit wide

For historical reasons, 16-bit ports used to be named *wide* ports and 8-bit
ports used to be called *narrow* with PyFtdi_. This terminology and APIs are
no longer used, but are kept to prevent API break. Please only use the port
``width`` rather than these legacy port types.


GPIO value
``````````

* A logical ``0`` bit represents a low level value on a pin, that is *GND*
* A logical ``1`` bit represents a high level value on a pin, that is *Vdd*
  which is typically 3.3 volts on most FTDIs

Please refers to the FTDI datasheet of your device for the tolerance and
supported analog levels for more details

.. hint::

   FT232H supports a specific feature, which is dedicated to better supporting
   the |I2C| feature. This specific devices enables an open-collector mode:

   * Setting a pin to a low level drains it to *GND*
   * Setting a pin to a high level sets the pin as High-Z

   This feature is automatically activated when |I2C| feature is enabled on a
   port, for the two first pins, i.e. `SCL` and `SDA out`.

   However, PyFTDI does not yet provide an API to enable this mode to the
   other pins of a port, *i.e.* for the pins used as GPIOs.


Direction
`````````

An FTDI pin should either be configured as an input or an ouput. It is
mandatory to (re)configure the direction of a pin before changing the way it is
used.

* A logical ``0`` bit represents an input pin, *i.e.* a pin whose value can be
  sampled and read via the PyFTDI APIs
* A logical ``1`` bit represents an output pin, *i.e.* a pin whose value can be
  set/written with the PyFTDI APIs


.. _cbus_gpio:

CBUS GPIOs
~~~~~~~~~~

FT232R, FT232H and FT230X/FT231X support an additional port denoted CBUS:

* FT232R provides an additional 5-bit wide port, where only 4 LSBs can be
  used as programmable GPIOs: ``CBUS0`` to ``CBUS3``,
* FT232H provices an additional 10-bit wide port, where only 4 pins can be
  used as programmable GPIOs: ``CBUS5``, ``CBUS6``, ``CBUS8``, ``CBUS9``
* FT230X/FT231X provides an additional 4-bit wide port: ``CBUS0`` to ``CBUS3``

Note that CBUS access is slower than regular asynchronous bitbang mode.

CBUS EEPROM configuration
`````````````````````````

Accessing this extra port requires a specific EEPROM configuration.

The EEPROM needs to be configured so that the CBUS pins that need to be used
as GPIOs are defined as ``GPIO``. Without this special configuration, CBUS
pins are used for other functions, such as driving leds when data is exchanged
over the UART port. Remember to power-cycle the FTDI device after changing its
EEPROM configuration to force load the new configuration.

The :ref:`ftconf` tool can be used to query and change the EEPROM
configuration. See the EEPROM configuration :ref:`example <eeprom_cbus>`.

CBUS GPIO API
`````````````

PyFtdi_ starting from v0.47 supports CBUS pins as special GPIO port. This port
is *not* mapped as regular GPIO, a dedicated API is reserved to drive those
pins:

* :py:meth:`pyftdi.ftdi.Ftdi.has_cbus` to report whether the device supports
  CBUS gpios,
* :py:meth:`pyftdi.ftdi.Ftdi.set_cbus_direction` to configure the port,
* :py:meth:`pyftdi.ftdi.Ftdi.get_cbus_gpio` to get the logical values from the
  port,
* :py:meth:`pyftdi.ftdi.Ftdi.set_cbus_gpio` to set new logical values to the
  port

Additionally, the EEPROM configuration can be queried to retrieve which CBUS
pins have been assigned to GPIO functions:

* :py:meth:`pyftdi.eeprom.FtdiEeprom.cbus_pins` to report CBUS GPIO pins

The CBUS port is **not** available through the
:py:class:`pyftdi.gpio.GpioController` API, as it cannot be considered as a
regular GPIO port.

.. warning::

  CBUS GPIO feature has only be tested with the virtual test framework and a
  real FT231X HW device. It should be considered as an experimental feature
  for now.

Configuration
~~~~~~~~~~~~~

GPIO bitmap
```````````

The GPIO pins of a port are always accessed as an integer, whose supported
width depends on the width of the port. These integers should be considered as
a bitmap of pins, and are always assigned the same mapping, whatever feature is
enabled:

* b\ :sub:`0`\  (``0x01``) represents the first pin of a port, *i.e.* AD0/BD0
* b\ :sub:`1`\  (``0x02``) represents the second pin of a port, *i.e.* AD1/BD1
* ...
* b\ :sub:`7`\  (``0x80``) represents the eighth pin of a port, *i.e.* AD7/BD7
* b\ :sub:`N`\  represents the highest pin of a port, *i.e.* AD7/BD7 for an
  8-bit port, AD15/BD15 for a 16-bit port, etc.

Pins reserved for a specific feature (|I2C|, SPI, ...) cannot be accessed as
a regular GPIO. They cannot be arbitrarily written and should be masked out
when the GPIO output value is set. See :ref:`reserved_pins` for details.

FT232H CBUS exception
.....................

Note that there is an exception to this rule for FT232H CBUS port: FTDI has
decided to map non-contiguous CBUS pins as GPIO-capable CBUS pins, that is
``CBUS5``, ``CBUS6``, ``CBUS8``, ``CBUS9``, where other CBUS-enabled devices
use ``CBUS0``, ``CBUS1``, ``CBUS2``, ``CBUS3``.

If the CBUS GPIO feature is used with an FT232H device, the pin positions for
the GPIO port are not b\ :sub:`5`\  .. b\ :sub:`9`\  but b\ :sub:`0`\  to
b\ :sub:`3`\  . This may sounds weird, but CBUS feature is somewhat hack-ish
even with FTDI commands, so it did not deserve a special treatment for the sake
of handling the weird implementation of FT232H.

Direction bitmap
````````````````

Before using a port as GPIO, the port must be configured as GPIO. This is
achieved by either instanciating one of the *GpioController* or by requesting
the GPIO port from a specific serial bus controller:
``I2cController.get_gpio()`` and ``SpiController.get_gpio()``. All instances
provide a similar API (duck typing API) to configure, read and write to GPIO
pins.

Once a GPIO port is instanciated, the direction of each pin should be defined.
The direction can be changed at any time. It is not possible to write to /
read from a pin before the proper direction has been defined.

To configure the direction, use the `set_direction` API with a bitmap integer
value that defines the direction to use of each pin.

Direction example
.................

A 8-bit port, dedicated to GPIO, is configured as follows:

 * BD0, BD3, BD7: input, `I` for short
 * BD1-BD2, BD4-BD6: output, `O` for short

That is, MSB to LSB: *I O O O I O O I*.

This translates to 0b ``0111 0110`` as output is ``1`` and input is ``0``,
that is ``0x76`` as an hexa value. This is the direction value to use to
``configure()`` the port.

See also the ``set_direction()`` API to reconfigure the direction of GPIO pins
at any time. This method accepts two arguments. This first arguments,
``pins``, defines which pins - the ones with the maching bit set - to consider
in the second ``direction`` argument, so there is no need to
preserve/read-modify-copy the configuration of other pins. Pins with their
matching bit reset are not reconfigured, whatever their direction bit.

.. code-block:: python

    gpio = GpioAsyncController()
    gpio.configure('ftdi:///1', direction=0x76)
    # later, reconfigure BD2 as input and BD7 as output
    gpio.set_direction(0x84, 0x80)


Using GPIO APIs
~~~~~~~~~~~~~~~

There are 3 variant of *GpioController*, depending on which features are needed
and how the GPIO port usage is intended. :doc:`api/gpio` gives in depth details
about those controllers. Those controllers are mapped onto FTDI HW features.

* ``GpioAsyncController`` is likely the most useful API to drive GPIOs.

  It enables reading current GPIO input pin levels and to change GPIO output
  pin levels. When vector values (byte buffers) are used instead of scalar
  value (single byte), GPIO pins are samples/updated at a regular pace, whose
  frequency can be configured. It is however impossible to control the exact
  time when input pins start to be sampled, which can be tricky to use with
  most applications. See :doc:`api/gpio` for details.

* ``GpioSyncController`` is a variant of the previous API.

  It is aimed at precise time control of sampling/updating the GPIO: a new
  GPIO input sample is captured once every time GPIO output pins are updated.
  With byte buffers, GPIO pins are samples/updated at a regular pace, whose
  frequency can be configured as well. The API of ``GpioSyncController``
  slightly differ from the other GPIO APIs, as the usual ``read``/``write``
  method are replaced with a single ``exchange`` method.

Both ``GpioAsyncController`` and ``GpioSyncController`` are restricted to only
access the 8 LSB pins of a port, which means that FTDI device with wider port
(12- and 16- pins) cannot be fully addressed, as only b\ :sub:`0`\  to b\
:sub:`7`\  can be addressed.

* ``GpioMpsseController`` enables access to the MSB pins of wide ports.

  However LSB and MSB pins cannot be addressed in a true atomic manner, which
  means that there is a short delay between sampling/updating the LSB and MSB
  part of the same wide port. Byte buffer can also be sampled/updated at a
  regular pace, but the achievable frequency range may differ from the other
  controllers.

It is recommened to read the ``tests/gpio.py`` files - available from GitHub -
to get some examples on how to use these API variants.

Setting GPIO pin state
``````````````````````

To write to a GPIO, use the `write()` method. The caller needs to mask out
the bits configured as input, or an exception is triggered:

* writing ``0`` to an input pin is ignored
* writing ``1`` to an input pin raises an exception

.. code-block:: python

    gpio = GpioAsyncController()
    gpio.configure('ftdi:///1', direction=0x76)
    # all output set low
    gpio.write(0x00)
    # all output set high
    gpio.write(0x76)
    # all output set high, apply direction mask
    gpio.write(0xFF & gpio.direction)
    # all output forced to high, writing to input pins is illegal
    gpio.write(0xFF)  # raises an IOError
    gpio.close()


Retrieving GPIO pin state
`````````````````````````

To read a GPIO, use the `read()` method.

.. code-block:: python

    gpio = GpioAsyncController()
    gpio.configure('ftdi:///1', direction=0x76)
    # read whole port
    pins = gpio.read()
    # ignore output values (optional)
    pins &= ~gpio.direction
    gpio.close()


Modifying GPIO pin state
````````````````````````

A read-modify-write sequence is required.

.. code-block:: python

    gpio = GpioAsyncController()
    gpio.configure('ftdi:///1', direction=0x76)
    # read whole port
    pins = gpio.read()
    # clearing out AD1 and AD2
    pins &= ~((1 << 1) | (1 << 2))  # or 0x06
    # want AD2=0, AD1=1
    pins |= 1 << 1
    # update GPIO output
    gpio.write(pins)
    gpio.close()


Synchronous GPIO access
```````````````````````

.. code-block:: python

    gpio = GpioSyncController()
    gpio.configure('ftdi:///1', direction=0x0F, frequency=1e6)
    outs = bytes(range(16))
    ins = gpio.exchange(outs)
    # ins contains as many bytes as outs
    gpio.close()


CBUS GPIO access
````````````````

.. code-block:: python

   ftdi = Ftdi()
   ftdi.open_from_url('ftdi:///1')
   # validate CBUS feature with the current device
   assert ftdi.has_cbus
   # validate CBUS EEPROM configuration with the current device
   eeprom = FtdiEeprom()
   eeprom.connect(ftdi)
   # here we use CBUS0 and CBUS3 (or CBUS5 and CBUS9 on FT232H)
   assert eeprom.cbus_mask & 0b1001 == 0b1001
   # configure CBUS0 as output and CBUS3 as input
   ftdi.set_cbus_direction(0b1001, 0b0001)
   # set CBUS0
   ftdi.set_cbus_gpio(0x1)
   # get CBUS3
   cbus3 = ftdi.get_cbus_gpio() >> 3


.. code-block:: python

   # it is possible to open the ftdi object from an existing serial connection:
   port = serial_for_url('ftdi:///1')
   ftdi = port.ftdi
   ftdi.has_cbus
   # etc...

.. _reserved_pins:

Reserved pins
~~~~~~~~~~~~~

GPIO pins vs. feature pins
``````````````````````````

It is important to note that the reserved pins do not change the pin
assignment, *i.e.* the lowest pins of a port may become unavailable as regular
GPIO when the feature is enabled:

Example
.......

|I2C| feature reserves
the three first pins, as *SCL*, *SDA output*, *SDA input* (w/o clock stretching
feature which also reserves another pin). This means that AD0, AD1 and AD2,
that is b\ :sub:`0`\ , b\ :sub:`1`\ , b\ :sub:`2`\  cannot be directly
accessed.

The first accessible GPIO pin in this case is no longer AD0 but AD3, which
means that b\ :sub:`3`\ becomes the lowest bit which can be read/written.

.. code-block:: python

    # use I2C feature
    i2c = I2cController()
    # configure the I2C feature, and predefines the direction of the GPIO pins
    i2c.configure('ftdi:///1', direction=0x78)
    gpio = i2c.get_gpio()
    # read whole port
    pins = gpio.read()
    # clearing out I2C bits (SCL, SDAo, SDAi)
    pins &= 0x07
    # set AD4
    pins |= 1 << 4
    # update GPIO output
    gpio.write(pins)


================================================
FILE: pyftdi/doc/index.rst
================================================
PyFtdi
======

.. cannot use defs.rst here, as PyPi wants a standalone file.
.. |I2C| replace:: I\ :sup:`2`\ C

Documentation
-------------

The latest PyFtdi online documentation is always available from
`here <https://eblot.github.io/pyftdi>`_.

Beware the online version may be more recent than the PyPI hosted version, as
intermediate development versions are not published to
`PyPi <https://pypi.org/project/pyftdi>`_.

PyFtdi documentation can be locally build with Sphinx, see the installation
instructions.

Source code
-----------

PyFtdi releases are available from the Python Package Index from
`PyPi <https://pypi.org/project/pyftdi>`_.

PyFtdi development code is available from
`GitHub <https://github.com/eblot/pyftdi>`_.

Overview
--------

PyFtdi aims at providing a user-space driver for popular FTDI devices,
implemented in pure Python language.

Supported FTDI devices include:

* UART and GPIO bridges

  * FT232R (single port, 3Mbps)
  * FT230X/FT231X/FT234X (single port, 3Mbps)

* UART and multi-serial protocols (SPI, |I2C|, JTAG) bridges

  * FT2232C/D (dual port, clock up to 6 MHz)
  * FT232H (single port, clock up to 30 MHz)
  * FT2232H (dual port, clock up to 30 MHz)
  * FT4232H (quad port, clock up to 30 MHz)
  * FT4232HA (quad port, clock up to 30 MHz)

Features
--------

PyFtdi currently supports the following features:

* UART/Serial USB converter, up to 12Mbps (depending on the FTDI device
  capability)
* GPIO/Bitbang support, with 8-bit asynchronous, 8-bit synchronous and
  8-/16-bit MPSSE variants
* SPI master, with simultanous GPIO support, up to 12 pins per port,
  with support for non-byte sized transfer
* |I2C| master, with simultanous GPIO support, up to 14 pins per port
* Basic JTAG master capabilities
* EEPROM support (some parameters cannot yet be modified, only retrieved)
* Experimental CBUS support on selected devices, 4 pins per port

Supported host OSes
-------------------

* macOS
* Linux
* FreeBSD
* Windows, although not officially supported

.. EOT

Warning
-------

Starting with version *v0.40.0*, several API changes are being introduced.
While PyFtdi tries to maintain backward compatibility with previous versions,
some of these changes may require existing clients to update calls to PyFtdi.

Do not upgrade to *v0.40.0* or above without testing your client against the
new PyFtdi releases. PyFtdi versions up to *v0.39.9* keep a stable API
with *v0.22+* series.

See the *Major Changes* section on the online documentation for details about
potential API breaks.


Major changes
~~~~~~~~~~~~~

 * *read* methods now return ``bytearray`` instead of `Array('B')` so that
   pyserial ``readline()`` may be used. It also brings some performance
   improvements.
 * PyFtdi URLs now supports ``bus:address`` alternative specifiers, which
   required to augment the ``open_*()`` methods with new, optional parameters.
 * ``SpiController`` reserves only one slave line (*/CS*) where it used to
   reserve 4 slave lines in previous releases. This frees more GPIOs when
   default value is used - it is nevertheless still possible to reserve up to 5
   slave lines.
 * type hinting is used for most, if not all, public methods.
 * simplified baudrate divider calculation.

PyFTDI in details
-----------------

.. toctree::
   :maxdepth: 1
   :glob:

   features
   requirements
   installation
   urlscheme
   tools
   api/index
   pinout
   gpio
   eeprom
   testing
   troubleshooting
   authors
   license


================================================
FILE: pyftdi/doc/installation.rst
================================================
.. include:: defs.rst

Installation
------------

Prerequisites
~~~~~~~~~~~~~

PyFTDI_ relies on PyUSB_, which requires a native dependency: libusb 1.x.

The actual command to install depends on your OS and/or your distribution,
see below

.. _install_linux:

Debian/Ubuntu Linux
```````````````````

.. code-block:: shell

     apt-get install libusb-1.0

On Linux, you also need to create a `udev` configuration file to allow
user-space processes to access to the FTDI devices. There are many ways to
configure `udev`, here is a typical setup:

::

    # /etc/udev/rules.d/11-ftdi.rules

    # FT232AM/FT232BM/FT232R
    SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6001", GROUP="plugdev", MODE="0664"
    # FT2232C/FT2232D/FT2232H
    SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", GROUP="plugdev", MODE="0664"
    # FT4232/FT4232H
    SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6011", GROUP="plugdev", MODE="0664"
    # FT232H
    SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6014", GROUP="plugdev", MODE="0664"
    # FT230X/FT231X/FT234X
    SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6015", GROUP="plugdev", MODE="0664"
    # FT4232HA
    SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6048", GROUP="plugdev", MODE="0664"

.. note:: **Accessing FTDI devices with custom VID/PID**

   You need to add a line for each device with a custom VID / PID pair you
   declare, see :ref:`custom_vid_pid` for details.

You need to unplug / plug back the FTDI device once this file has been
created so that `udev` loads the rules for the matching device, or
alternatively, inform the ``udev`` daemon about the changes:

.. code-block:: shell

   sudo udevadm control --reload-rules
   sudo udevadm trigger

With this setup, be sure to add users that want to run PyFtdi_ to the
`plugdev` group, *e.g.*

.. code-block:: shell

    sudo adduser $USER plugdev

Remember that you need to log out / log in to get the above command
effective, or start a subshell to try testing PyFtdi_:

.. code-block:: shell

    newgrp plugdev


.. _install_macos:

Homebrew macOS
``````````````

.. code-block:: shell

    brew install libusb


.. _install_windows:

Windows
```````

Windows is not officially supported (*i.e.* not tested) but some users have
reported successful installations. Windows requires a specific libusb backend
installation.

Zadig
.....

The probably easiest way to deal with libusb on Windows is to use Zadig_

1. Start up the Zadig utility

2. Select ``Options/List All Devices``, then select the FTDI devices you want
   to communicate with. Its names depends on your hardware, *i.e.* the name
   stored in the FTDI EEPROM.

  * With FTDI devices with multiple channels, such as FT2232 (2 channels) and
    FT4232 (4 channels), you **must** install the driver for the composite
    parent, **not** for the individual interfaces. If you install the driver
    for each interface, each interface will be presented as a unique FTDI
    device and you may have difficulties to select a specific FTDI device port
    once the installation is completed. To make the composite parents to appear
    in the device list, uncheck the ``Options/Ignore Hubs or Composite Parents``
    menu item.

  * Be sure to select the parent device, *i.e.* the device name should not end
    with *(Interface N)*, where *N* is the channel number.

    * for example *Dual RS232-HS* represents the composite parent, while
      *Dual RS232-HS (Interface 0)* represents a single channel of the FTDI
      device. Always select the former.

3. Select ``libusb-win32`` (not ``WinUSB``) in the driver list.

4. Click on ``Replace Driver``

See also `Libusb on Windows`_


.. _install_python:

Python
~~~~~~

Python dependencies
```````````````````

Dependencies should be automatically installed with PIP.

  * pyusb >= 1.0.0, != 1.2.0
  * pyserial >= 3.0

Do *not* install PyUSB_ from GitHub development branch (``master``, ...).
Always prefer a stable, tagged release.

PyUSB 1.2.0 also broke the backward compatibility of the Device API, so it will
not work with PyFtdi.

Installing with PIP
```````````````````

PIP should automatically install the missing dependencies.

.. code-block:: shell

     pip3 install pyftdi


.. _install_from_source:

Installing from source
``````````````````````

If you prefer to install from source, check out a fresh copy from PyFtdi_
github repository.

.. code-block:: shell

     git clone https://github.com/eblot/pyftdi.git
     cd pyftdi
     # note: 'pip3' may simply be 'pip' on some hosts
     pip3 install -r requirements.txt
     python3 setup.py install


.. _generate_doc:

Generating the documentation
````````````````````````````

Follow :ref:`install_from_source` then:

.. code-block:: shell

     pip3 install setuptools wheel sphinx sphinx_autodoc_typehints
     # Shpinx Read the Doc theme seems to never get a release w/ fixed issues
     pip3 install -U -e git+https://github.com/readthedocs/sphinx_rtd_theme.git@2b8717a3647cc650625c566259e00305f7fb60aa#egg=sphinx_rtd_theme
     sphinx-build -b html pyftdi/doc .

The documentation may be accessed from the generated ``index.html`` entry file.


Post-installation sanity check
``````````````````````````````

Open a *shell*, or a *CMD* on Windows

.. code-block:: shell

    python3  # or 'python' on Windows
    from pyftdi.ftdi import Ftdi
    Ftdi.show_devices()

should list all the FTDI devices available on your host.

Alternatively, you can invoke ``ftdi_urls.py`` script that lists all detected
FTDI devices. See the :doc:`tools` chapter for details.

  * Example with 1 FT232H device with a serial number and 1 FT2232 device
    with no serial number, connected to the host:

    .. code-block::

        Available interfaces:
          ftdi://ftdi:232h:FT1PWZ0Q/1   (C232HD-DDHSP-0)
          ftdi://ftdi:2232/1            (Dual RS232-HS)
          ftdi://ftdi:2232/2            (Dual RS232-HS)


Note that FTDI devices with custom VID/PID are not listed with this simple
command, please refer to the PyFtdi_ API to add custom identifiers, *i.e.* see
:py:meth:`pyftdi.ftdi.Ftdi.add_custom_vendor` and
:py:meth:`pyftdi.ftdi.Ftdi.add_custom_product` APIs.


.. _custom_vid_pid:

Custom USB vendor and product IDs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

PyFtdi only recognizes FTDI official vendor and product IDs.

If you have an FTDI device with an EEPROM with customized IDs, you need to tell
PyFtdi to support those custom USB identifiers.

Custom PID
``````````

To support a custom product ID (16-bit integer) with the official FTDI ID, add
the following code **before** any call to an FTDI ``open()`` method.

.. code-block:: python

   from pyftdi.ftdi import Ftdi

   Ftdi.add_custom_product(Ftdi.DEFAULT_VENDOR, product_id)

Custom VID
``````````

To support a custom vendor ID and product ID (16-bit integers), add the
following code **before** any call to an FTDI ``open()`` method.

.. code-block:: python

   from pyftdi.ftdi import Ftdi

   Ftdi.add_custom_vendor(vendor_id)
   Ftdi.add_custom_product(vendor_id, product_id)

You may also specify an arbitrary string to each method if you want to specify
a URL by custom vendor and product names instead of their numerical values:

.. code-block:: python

   from pyftdi.ftdi import Ftdi

   Ftdi.add_custom_vendor(0x1234, 'myvendor')
   Ftdi.add_custom_product(0x1234, 0x5678, 'myproduct')

   f1 = Ftdi.create_from_url('ftdi://0x1234:0x5678/1')
   f2 = Ftdi.create_from_url('ftdi://myvendor:myproduct/2')

.. note::

   Remember that on OSes that require per-device access permissions such as
   Linux, you also need to add the custom VID/PID entry to the configuration
   file, see :ref:`Linux installation <install_linux>` ``udev`` rule file.


================================================
FILE: pyftdi/doc/license.rst
================================================
License
-------

.. include:: defs.rst

For historical reasons (PyFtdi has been initially developed as a compatibility
layer with libftdi_), the main ``ftdi.py`` file had originally been licensed
under the same license as the libftdi_ project, the GNU Lesser General Public
License LGPL v2 license. It does not share code from this project anymore, but
implements a similar API.

From my perspective, you may use it freely in open source or close source, free
or commercial projects as long as you comply with the BSD 3-clause license.


BSD 3-clause
~~~~~~~~~~~~

::

  Copyright (c) 2008-2025 Emmanuel Blot <emmanuel.blot@free.fr>
  All Rights Reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
      * Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.
      * Redistributions in binary form must reproduce the above copyright
        notice, this list of conditions and the following disclaimer in the
        documentation and/or other materials provided with the distribution.
      * Neither the name of the author nor the names of its contributors may
        be used to endorse or promote products derived from this software
        without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL NEOTION BE LIABLE FOR ANY DIRECT, INDIRECT,
  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: pyftdi/doc/pinout.rst
================================================
.. include:: defs.rst

FTDI device pinout
------------------

============ ============= ======= ====== ============== ========== ====== =============
 IF/1 [#ih]_ IF/2 [#if2]_  BitBang  UART   |I2C|          SPI        JTAG   C232HD cable
============ ============= ======= ====== ============== ========== ====== =============
 ``ADBUS0``   ``BDBUS0``   GPIO0    TxD    SCK            SCLK       TCK   Orange
 ``ADBUS1``   ``BDBUS1``   GPIO1    RxD    SDA/O [#i2c]_  MOSI       TDI   Yellow
 ``ADBUS2``   ``BDBUS2``   GPIO2    RTS    SDA/I [#i2c]_  MISO       TDO   Green
 ``ADBUS3``   ``BDBUS3``   GPIO3    CTS    GPIO3          CS0        TMS   Brown
 ``ADBUS4``   ``BDBUS4``   GPIO4    DTR    GPIO4          CS1/GPIO4        Grey
 ``ADBUS5``   ``BDBUS5``   GPIO5    DSR    GPIO5          CS2/GPIO5        Purple
 ``ADBUS6``   ``BDBUS6``   GPIO6    DCD    GPIO6          CS3/GPIO6        White
 ``ADBUS7``   ``BDBUS7``   GPIO7    RI     RSCK [#rck]_   CS4/GPIO7  RCLK  Blue
 ``ACBUS0``   ``BCBUS0``                   GPIO8          GPIO8
 ``ACBUS1``   ``BCBUS1``                   GPIO9          GPIO9
 ``ACBUS2``   ``BCBUS2``                   GPIO10         GPIO10
 ``ACBUS3``   ``BCBUS3``                   GPIO11         GPIO11
 ``ACBUS4``   ``BCBUS4``                   GPIO12         GPIO12
 ``ACBUS5``   ``BCBUS5``                   GPIO13         GPIO13
 ``ACBUS6``   ``BCBUS6``                   GPIO14         GPIO14
 ``ACBUS7``   ``BCBUS7``                   GPIO15         GPIO15
============ ============= ======= ====== ============== ========== ====== =============

.. [#ih]  16-bit port (ACBUS, BCBUS) is not available with FT4232H_ series, and
          FTDI2232C/D only support 12-bit ports.
.. [#i2c] FTDI pins are either configured as input or output. As |I2C| SDA line
          is bi-directional, two FTDI pins are required to provide the SDA
          feature, and they should be connected together and to the SDA |I2C|
          bus line. Pull-up resistors on SCK and SDA lines should be used.
.. [#if2] FT232H_ does not support a secondary MPSSE port, only FT2232H_,
          FT4232H_ and FT4232HA_ do. Note that FT4232H_/FT4232HA_ has 4 serial
          ports, but only the first two interfaces are MPSSE-capable. C232HD
          cable only exposes IF/1 (ADBUS).
.. [#rck] In order to support I2C clock stretch mode, ADBUS7 should be
          connected to SCK. When clock stretching mode is not selected, ADBUS7
          may be used as GPIO7.

================================================
FILE: pyftdi/doc/requirements.rst
================================================
.. include:: defs.rst

Requirements
------------

Python_ 3.9 or above is required.

PyFtdi_ relies on PyUSB_, which itself depends on one of the following native
libraries:

* libusb_, currently tested with 1.0.23

PyFtdi_ does not depend on any other native library. It only uses standard
Python modules, and PyUSB_, pyserial_ and PyJtagTools_.

PyFtdi_ is being tested with PyUSB_ 1.2.1.

Development
~~~~~~~~~~~

PyFtdi_ is developed on macOS platforms (64-bit kernel), and is validated on a
regular basis on Linux hosts.

As it contains no native code, it should work on any PyUSB_ and libusb_
supported platforms. However, M$ Windows is a seamless source of issues and is
not officially supported, although users have reported successful installation
with Windows 7 for example. Your mileage may vary.

A fork of PyFtdi which relies on the official FTDI D2XX Windows library might be
a better solution for Windows users, please check out PyFtdiWin_.

API breaks
~~~~~~~~~~

Starting with version *v0.40.0*, several API changes are being introduced.
While PyFtdi tries to maintain backward compatibility with previous versions,
some of these changes may require existing clients to update calls to PyFtdi.

Do not upgrade to *v0.40.0* or above without testing your client against the
new PyFtdi releases. PyFtdi versions up to *v0.39.9* keep a stable API
with *v0.22+* series.

See the *Major Changes* section for details about potential API breaks.

Legacy Python support
~~~~~~~~~~~~~~~~~~~~~

* PyFtdi *v0.55* is the last PyFtdi version to support Python 3.8.

  * Python 3.8 has reached end-of-life on October 7th, 2024.

* PyFtdi *v0.54* is the last PyFtdi version to support Python 3.7.

  * Python 3.7 has reached end-of-life on June 27rd, 2023.

* PyFtdi *v0.53* is the last PyFtdi version to support Python 3.6.

  * Python 3.6 has reached end-of-life on December 23rd, 2021.

* PyFtdi *v0.52* is the last PyFtdi version to support Python 3.5.

  * Python 3.5 has reached end-of-life on September 5th, 2020.


================================================
FILE: pyftdi/doc/testing.rst
================================================
Testing
-------

.. include:: defs.rst

Overview
~~~~~~~~

Testing PyFTDI is challenging because it relies on several pieces of hardware:

* one or more FTDI device
* |I2C|, SPI, JTAG bus slaves or communication equipment for UART

The ``tests`` directory contain several tests files, which are primarily aimed
at demonstrating usage of PyFTDI in common use cases.

Most unit tests are disabled, as they require specific slaves, with a dedicated
HW wiring. Reproducing such test environments can be challenging, as it
requires dedicated test benchs.

This is a growing concern as PyFTDI keeps evolving, and up to now, regression
tests were hard to run.

Hardware tests
~~~~~~~~~~~~~~

Please refer to the ``pyftdi/tests`` directory. There is one file dedicated to
each feature to test. Note that you need to read and edit these tests files to
fit your actual test environment, and enable the proper unit test cases, as
most are actually disabled by default.

You need specific bus slaves to perform most of these tests.

.. _virtual_framework:

Virtual test framework
~~~~~~~~~~~~~~~~~~~~~~

With PyFTDI v0.45, a new test module enables PyFTDI API partial testing using a
pure software environment with no hardware. This also eases automatic testing
within a continuous integration environment.

This new module implements a virtual USB backend for PyUSB, which creates some
kind of virtual, limited USB stack. The PyUSB can be told to substitute the
native platform's libusb with this module.

This module, ``usbvirt`` can be dynamically confifured with the help of YaML
definition files to create one or more virtual FTDI devices on a virtual USB
bus topology. This enables to test ``usbtools`` module to enumerate, detect,
report and access FTDI devices using the regular :doc:`urlscheme` syntax.

``usbvirt`` also routes all vendor-specific USB API calls to a secondary
``ftdivirt`` module, which is in charge of handling all FTDI USB requests.

This module enables testing PyFtdi_ APIs. It also re-uses the MPSSE tracker
engine to decode and verify MPSSE requests used to support |I2C|, SPI and UART
features.

For now, it is able to emulate most of GPIO requests (async, sync and MPSSE)
and UART input/output. It also manages the frequency and baudrate settings.

It is not able to emulate the MPSSE commands (with the exception of set and get
GPIO values), as it represents a massive workload...

Beware: WIP
```````````

This is an experimental work in progress, which is its early inception stage.

It has nevertheless already revealed a couple of bugs that had been hiding
within PyFtdi_ for years.

There is a large work effort ahead to be able to support more use cases and
tests more APIs, and many unit tests to write.

It cannot replace hardware tests with actual boards and slaves, but should
simplify test setup and help avoiding regression issues.


Usage
`````

No hardware is required to run these tests, to even a single FTDI device.

The test configuration files are described in YaML file format, therefore the
ruamel.yaml_ package is required.

.. code-block:: python

    pip3 install ruamel.yaml
    PYTHONPATH=. FTDI_LOGLEVEL=info pyftdi/tests/mockusb.py

Configuration
`````````````

The ``pyftdi/tests/resources`` directory contains definition files which are
loaded by the mock unit tests.

Although it is possible to create fine grained USB device definitions, the
configuration loader tries to automatically define missing parts to match the
USB device topology of FTDI devices.

This enables to create simple definition files without having to mess with low
level USB definitions whenever possible.

EEPROM content
..............

The :ref:`ftconf` tool can be used to load, modify and generate the content of
a virtual EEPROM, see :doc:`eeprom`.

Examples
........

 * An example of a nearly comprehensive syntax can be found in ``ft232h.yaml``.
 * Another, much more simple example with only mandatory settings can be found
   in ``ft230x.yaml``.
 * An example of multiple FTDI device definitions can be found in
   ``ftmany.yaml``


Availability
~~~~~~~~~~~~

Note that unit tests and the virtual infrastructure are not included in the
distributed Python packages, they are only available from the git repository.


================================================
FILE: pyftdi/doc/tools.rst
================================================
.. include:: defs.rst

.. _tools:

Tools
-----

Overview
~~~~~~~~

PyFtdi_ comes with a couple of scripts designed to help using PyFtdi_ APIs,
and can be useful to quick start working with PyFtdi_.

Scripts
~~~~~~~

.. _ftdi_urls:

``ftdi_urls``
`````````````

This tiny script ``ftdi_urls.py`` to list the available, *i.e.* detected,
FTDI devices connected to the host, and the URLs than can be used to open a
:py:class:`pyftdi.ftdi.Ftdi` instance with the
:py:class:`pyftdi.ftdi.Ftdi.open_from_url` family and ``configure`` methods.


``ftconf``
``````````

``ftconf.py`` is a companion script to help managing the content of
the FTDI EEPROM from the command line. See the :ref:`ftconf` documentation.


.. _i2cscan:

``i2cscan``
```````````

The ``i2cscan.py`` script helps to discover which I2C devices
are connected to the FTDI I2C bus.


.. _pyterm.py:

``pyterm``
``````````

``pyterm.py`` is a simple serial terminal that can be used to test the serial
port feature, see the :ref:`pyterm` documentation.


Where to find these tools?
~~~~~~~~~~~~~~~~~~~~~~~~~~

These scripts can be downloaded from PyFtdiTools_, and are also installed along
with the PyFtdi_ module on the local host.

The location of the scripts depends on how PyFtdi_ has been installed and the
type of hosts:

* on linux and macOS, there are located in the ``bin/`` directory, that is the
  directory where the Python interpreter is installed.

* on Windows, there are located in the ``Scripts/`` directory, which is a
  subdirectory of the directory where the Python interpreter is installed.


.. _common_option_switches:

Common options switches
~~~~~~~~~~~~~~~~~~~~~~~

PyFtdi_ tools share many common option switches:

.. _option_d:

``-d``
  Enable debug mode, which emits Python traceback on exceptions

.. _option_h:

``-h``
  Show quick help and exit

.. _option_P_:

``-P <vidpid>``
  Add custom vendor and product identifiers.

  PyFtdi_ only recognizes FTDI official USB vendor identifier (*0x403*) and
  the USB identifiers of their products.

  In order to use alternative VID/PID values, the PyFtdi_ tools accept the
  ``-P`` option to describe those products

  The ``vidpid`` argument should match the following format:

  ``[vendor_name=]<vendor_id>:[product_name=]<product_id>``

  * ``vendor_name`` and ``product_name`` are optional strings, they may be
    omitted as they only serve as human-readable aliases for the vendor and
    product names. See example below.
  * ``vendor_id`` and ``product_id`` are mandatory strings that should resolve
    into 16-bit integers (USB VID and PID values). Integer values are always
    interpreted as hexadecimal values, *e.g.* `-P 1234:6789` is parsed as
    `-P 0x1234:0x6789`.

  This option may be repeated as many times as required to add support for
  several custom devices.

  examples:

   * ``0x403:0x9999``, *vid:pid* short syntax, with no alias names;
     a matching FTDI :ref:`URL <url_scheme>` would be ``ftdi://ftdi:0x9999/1``
   * ``mycompany=0x666:myproduct=0xcafe``, *vid:pid* complete syntax with
     aliases; matching FTDI :ref:`URLs <url_scheme>` could be:

     * ``ftdi://0x666:0x9999/1``
     * ``ftdi://mycompany:myproduct/1``
     * ``ftdi://mycompany:0x9999/1``
     * ...

.. _option_v:

``-v``
  Increase verbosity, useful for debugging the tool. It can be repeated to
  increase more the verbosity.

.. _option_V_:

``-V <virtual>``
  Load a virtual USB device configuration, to use a virtualized FTDI/EEPROM
  environment. This is useful for PyFtdi_ development, and to test EEPROM
  configuration with a virtual setup. This option is not useful for regular
  usage. See :ref:`virtual_framework`.



================================================
FILE: pyftdi/doc/troubleshooting.rst
================================================
.. include:: defs.rst

Troubleshooting
---------------

Reporting a bug
~~~~~~~~~~~~~~~

Please do not contact the author by email. The preferered method to report bugs
and/or enhancement requests is through
`GitHub <https://github.com/eblot/pyftdi/issues>`_.

Please be sure to read the next sections before reporting a new issue.

Logging
~~~~~~~

FTDI uses the `pyftdi` logger.

It emits log messages with raw payload bytes at DEBUG level, and data loss
at ERROR level.

Common error messages
~~~~~~~~~~~~~~~~~~~~~

"Error: No backend available"
`````````````````````````````

libusb native library cannot be loaded. Try helping the dynamic loader:

* On Linux: ``export LD_LIBRARY_PATH=<path>``

  where ``<path>`` is the directory containing the ``libusb-1.*.so``
  library file

* On macOS: ``export DYLD_LIBRARY_PATH=.../lib``

  where ``<path>`` is the directory containing the ``libusb-1.*.dylib``
  library file

* On Windows:

  Try to copy the USB dll where the Python executable is installed, along
  with the other Python DLLs.

  If this happens while using an exe created by pyinstaller:
  ``copy C:\Windows\System32\libusb0.dll <path>``

  where ``<path>`` is the directory containing the executable created
  by pyinstaller. This assumes you have installed libusb (using a tool
  like Zadig) as referenced in the installation guide for Windows.


"Error: Access denied (insufficient permissions)"
`````````````````````````````````````````````````

The system may already be using the device.

* On macOS: starting with 10.9 "*Mavericks*", macOS ships with a native FTDI
  kernel extension that preempts access to the FTDI device.

  Up to 10.13 "*High Sierra*", this driver can be unloaded this way:

  .. code-block:: shell

      sudo kextunload [-v] -bundle com.apple.driver.AppleUSBFTDI

  You may want to use an alias or a tiny script such as
  ``pyftdi/bin/uphy.sh``

  Please note that the system automatically reloads the driver, so it may be
  useful to move the kernel extension so that the system never loads it.

  .. warning::

     From macOS 10.14 "*Mojave*", the Apple kernel extension peacefully
     co-exists with libusb_ and PyFtdi_, so you no longer need - and **should
     not attempt** - to unload the kernel extension. If you still experience
     this error, please verify you have not installed another driver from FTDI,
     such as FTDI's D2XX.

* On Linux: it may indicate a missing or invalid udev configuration. See
  the :doc:`installation` section.

* This error message may also be triggered whenever the communication port is
  already in use.


"Error: The device has no langid"
`````````````````````````````````

* On Linux, it usually comes from the same installation issue as the
  ``Access denied`` error: the current user is not granted the permissions to
  access the FTDI device, therefore pyusb cannot read the FTDI registers. Check
  out the :doc:`installation` section.


"Bus error / Access violation"
``````````````````````````````

PyFtdi does not use any native library, but relies on PyUSB_ and libusb_. The
latter uses native code that may trigger OS error. Some early development
versions of libusb_, for example 1.0.22-b…, have been reported to trigger
such issues. Please ensure you use a stable/final versions of libusb_ if you
experience this kind of fatal error.


"serial.serialutil.SerialException: Unable to open USB port"
````````````````````````````````````````````````````````````

May be caused by a conflict with the FTDI virtual COM port (VCOM). Try
uninstalling the driver. On macOS, refer to this `FTDI macOS guide`_.


Slow initialisation on OS X El Capitan
``````````````````````````````````````

It may take several seconds to open or enumerate FTDI devices.

If you run libusb <= v1.0.20, be sure to read the `Libusb issue on macOS`_
with OS X 10.11+.



================================================
FILE: pyftdi/doc/urlscheme.rst
================================================
.. include:: defs.rst

.. _url_scheme:

URL Scheme
----------

There are two ways to open a connection to an `Ftdi` object.

The recommended way to open a connection is to specify connection details
using a URL. The URL scheme is defined as:

::

    ftdi://[vendor][:[product][:serial|:bus:address|:index]]/interface

where:

* vendor: the USB vendor ID of the manufacturer

  * ex: ``ftdi`` or ``0x403``

* product: the USB product ID of the device

  * ex: ``232h`` or ``0x6014``
  * Supported product IDs: ``0x6001``, ``0x6010``, ``0x6011``, ``0x6014``,
    ``0x6015``
  * Supported product aliases:

    * ``232``, ``232r``, ``232h``, ``2232d``, ``2232h``, ``4232h``, ``4232ha``,
      ``230x``
    * ``ft`` prefix for all aliases is also accepted, as for example ``ft232h``

* ``serial``: the serial number as a string. This is the preferred method to
  uniquely identify a specific FTDI device. However, some FTDI device are not
  fitted with an EEPROM, or the EEPROM is either corrupted or erased. In this
  case, FTDI devices report no serial number

  Examples:
     * ``ftdi://ftdi:232h:FT0FMF6V/1``
     * ``ftdi://:232h:FT0FMF6V/1``
     * ``ftdi://::FT0FMF6V/1``

* ``bus:addess``: it is possible to select a FTDI device through a bus:address
  pair, specified as *hexadecimal* integer values.

  Examples:
     * ``ftdi://ftdi:232h:10:22/1``
     * ``ftdi://ftdi:232h:10:22/1``
     * ``ftdi://::10:22/1``

  Here, bus ``(0x)10`` = 16 (decimal) and address ``(0x)22`` = 34 (decimal)

* ``index``: an integer - not particularly useful, as it depends on the
  enumeration order on the USB buses, and may vary from on session to another.

* ``interface``: the interface of FTDI device, starting from 1

  * ``1`` for 230x and 232\* devices,
  * ``1`` or ``2`` for 2232\* devices,
  * ``1``, ``2``, ``3`` or ``4`` for 4232\* devices

All parameters but the interface are optional, PyFtdi tries to find the best
match. Therefore, if you have a single FTDI device connected to your system,
``ftdi:///1`` should be enough.

You can also ask PyFtdi to enumerate all the compatible devices with the
special ``ftdi:///?`` syntax. This syntax is useful to retrieve the available
FTDI URLs with serial number and/or bus:address selectors. To avoid conflicts
with some shells such as `zsh`, escape the `?` char as ``ftdi:///\?``.

There are several APIs available to enumerate/filter available FTDI device.
See :doc:`api/ftdi`.

Note that opening an FTDI connection with a URL ending with `?` is interpreted
as a query for matching FTDI devices and immediately stop. With this special
URL syntax, the avaialble devices are printed out to the standard output, and
the Python interpreter is forced to exit (`SystemExit` is raised).

When simple enumeration of the available FTDI devices is needed - so that
execution is not interrupted, two helper methods are available as
:py:meth:`pyftdi.ftdi.Ftdi.list_devices` and
:py:meth:`pyftdi.ftdi.Ftdi.show_devices` and accept the same URL syntax.

Opening a connection
~~~~~~~~~~~~~~~~~~~~

URL-based methods to open a connection
``````````````````````````````````````

.. code-block:: python

   open_from_url()
   open_mpsse_from_url()
   open_bitbang_from_url()


Device-based methods to open a connection
`````````````````````````````````````````

You may also open an Ftdi device from an existing PyUSB_ device, with the help
of the ``open_from_device()`` helper method.

.. code-block:: python

   open_from_device()
   open_mpsse_from_device()
   open_bitbang_from_device()


Legacy methods to open a connection
```````````````````````````````````

The old, deprecated method to open a connection is to use the ``open()``
methods without the ``_from_url`` suffix, which accept VID, PID, and serial
parameters (among others).

.. code-block:: python

   open()
   open_mpsse()
   open_bitbang()

See the :ref:`ftdi_urls` tool to obtain the URLs for the connected FTDI
devices.


================================================
FILE: pyftdi/eeprom.py
================================================
# Copyright (c) 2019-2024, Emmanuel Blot <emmanuel.blot@free.fr>
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""EEPROM management for PyFdti"""

# pylint: disable=wrong-import-position
# pylint: disable=import-error

import sys
from binascii import hexlify, unhexlify
from collections import OrderedDict, namedtuple
from configparser import ConfigParser
from enum import IntEnum, IntFlag
from logging import getLogger
from random import randint
from re import match
from struct import calcsize as scalc, pack as spack, unpack as sunpack
from typing import BinaryIO, List, Optional, Set, TextIO, Union, Tuple
from usb.core import Device as UsbDevice
from .ftdi import Ftdi, FtdiError
from .misc import classproperty, to_bool, to_int


class FtdiEepromError(FtdiError):
    """FTDI EEPROM error."""


class Hex2Int(int):
    """Hexa representation of a byte."""
    def __str__(self):
        return f'0x{int(self):02x}'


class Hex4Int(int):
    """Hexa representation of a half-word."""
    def __str__(self):
        return f'0x{int(self):04x}'


class FtdiEeprom:
    """FTDI EEPROM management
    """

    _PROPS = namedtuple('PROPS', 'size user dynoff chipoff')
    """Properties for each FTDI device release.

       * size is the size in bytes of the EEPROM storage area
       * user is the size in bytes of the user storage area, if any/supported
       * dynoff is the offset in EEPROM of the first bytes to store strings
       * chipoff is the offset in EEPROM of the EEPROM chip type
    """

    _PROPERTIES = {
        0x0200: _PROPS(0, None, 0, None),        # FT232AM
        0x0400: _PROPS(256, 0x14, 0x94, None),   # FT232BM
        0x0500: _PROPS(256, 0x16, 0x96, 0x14),   # FT2232D
        0x0600: _PROPS(128, None, 0x18, None),   # FT232R
        0x0700: _PROPS(256, 0x1A, 0x9A, 0x18),   # FT2232H
        0x0800: _PROPS(256, 0x1A, 0x9A, 0x18),   # FT4232H
        0x0900: _PROPS(256, 0x1A, 0xA0, 0x1e),   # FT232H
        0x1000: _PROPS(1024, 0x1A, 0xA0, None),  # FT230X/FT231X/FT234X
        0x3600: _PROPS(256, 0x1A, 0x9A, 0x18),   # FT4232HA
    }
    """EEPROM properties."""

    CBUS = IntEnum('CBus',
                   'TXDEN PWREN TXLED RXLED TXRXLED SLEEP CLK48 CLK24 CLK12 '
                   'CLK6 GPIO BB_WR BB_RD', start=0)
    """Alternate features for legacy FT232R devices."""

    CBUSH = IntEnum('CBusH',
                    'TRISTATE TXLED RXLED TXRXLED PWREN SLEEP DRIVE0 DRIVE1 '
                    'GPIO TXDEN CLK30 CLK15 CLK7_5', start=0)
    """Alternate features for FT232H/FT2232H/FT4232H devices."""

    CBUSX = IntEnum('CBusX',
                    'TRISTATE TXLED RXLED TXRXLED PWREN SLEEP DRIVE0 DRIVE1 '
                    'GPIO TXDEN CLK24 CLK12 CLK6 BAT_DETECT BAT_NDETECT '
                    'I2C_TXE I2C_RXF VBUS_SENSE BB_WR BB_RD TIMESTAMP AWAKE',
                    start=0)
    """Alternate features for FT230X devices."""

    UART_BITS = IntFlag('UartBits', 'TXD RXD RTS CTS DTR DSR DCD RI')
    """Inversion flags for FT232R and FT-X devices."""

    CHANNEL = IntFlag('Channel', 'FIFO OPTO CPU FT128 RS485')
    """Alternate port mode."""

    DRIVE = IntFlag('Drive',
                    'LOW HIGH SLOW_SLEW SCHMITT _10 _20 _40 PWRSAVE_DIS')
    """Driver options for I/O pins."""

    CFG1 = IntFlag('Cfg1', 'CLK_IDLE_STATE DATA_LSB FLOW_CONTROL _08 '
                           'HIGH_CURRENTDRIVE _20 _40 SUSPEND_DBUS7')
    """Configuration bits stored @ 0x01."""

    VAR_STRINGS = ('manufacturer', 'product', 'serial')
    """EEPROM strings with variable length."""

    def __init__(self):
        self.log = getLogger('pyftdi.eeprom')
        self._ftdi = Ftdi()
        self._eeprom = bytearray()
        self._size = 0
        self._dev_ver = 0
        self._valid = False
        self._config = OrderedDict()
        self._dirty = set()
        self._modified = False
        self._chip: Optional[int] = None
        self._mirror = False
        self._test_mode = False

    def __getattr__(self, name):
        if name in self._config:
            return self._config[name]
        raise AttributeError(f'No such attribute: {name}')

    @classproperty
    def eeprom_sizes(cls) -> List[int]:
        # pylint: disable=no-self-argument
        """Return a list of supported EEPROM sizes.

           :return: the supported EEPROM sizes
        """
        return sorted({p.size for p in cls._PROPERTIES.values() if p.size})

    def open(self, device: Union[str, UsbDevice],
             ignore: bool = False, size: Optional[int] = None,
             model: Optional[str] = None) -> None:
        """Open a new connection to the FTDI USB device.

           :param device: the device URL or a USB device instance.
           :param ignore: whether to ignore existing content
           :param size: a custom EEPROM size
           :param model: the EEPROM model used to specify a custom size
        """
        if self._ftdi.is_connected:
            raise FtdiError('Already open')
        if isinstance(device, str):
            self._ftdi.open_from_url(device)
        else:
            self._ftdi.open_from_device(device)
        if model and not size:
            # 93xxx46/56/66
            mmo = match(r'(?i)^93[a-z]*([456])6.*$', model)
            if not mmo:
                raise ValueError(f'Unknown EEPROM device: {model}')
            mmul = int(mmo.group(1))
            size = 128 << (mmul - 4)
        if size:
            if size not in self.eeprom_sizes:
                raise ValueError(f'Unsupported EEPROM size: {size}')
            self._size = min(size, 256)
        if not ignore:
            self._eeprom = self._read_eeprom()
            if self._valid:
                self._decode_eeprom()

    def close(self) -> None:
        """Close the current connection to the FTDI USB device,
        """
        if self._ftdi.is_connected:
            self._ftdi.close()
            self._eeprom = bytearray()
            self._dev_ver = 0
            self._config.clear()

    def connect(self, ftdi: Ftdi, ignore: bool = False) -> None:
        """Connect a FTDI EEPROM to an existing Ftdi instance.

           :param ftdi: the Ftdi instance to use
           :param ignore: whether to ignore existing content
        """
        self._ftdi = ftdi
        self._eeprom = bytearray()
        self._dev_ver = 0
        self._valid = False
        self._config = OrderedDict()
        self._dirty = set()
        if not ignore:
            self._eeprom = self._read_eeprom()
            if self._valid:
                self._decode_eeprom()
            self._decode_eeprom()

    @property
    def device_version(self) -> int:
        """Report the version of the FTDI device.

           :return: the release
        """
        if not self._dev_ver:
            if not self._ftdi.is_connected:
                raise FtdiError('Not connected')
            self._dev_ver = self._ftdi.device_version
        return self._dev_ver

    @property
    def size(self) -> int:
        """Report the EEPROM size.

           Use the most common (default) EEPROM size of the size is not yet
           known.

           :return: the size in bytes
        """
        if not self._size:
            self._size = self.default_size
        return self._size

    @property
    def default_size(self) -> int:
        """Report the default EEPROM size based on the FTDI type.

           The physical EEPROM size may be greater or lower, depending on the
           actual connected EEPROM device.

           :return: the size in bytes
        """
        if self._chip == 0x46:
            return 0x80  # 93C46
        if self._chip == 0x56:
            return 0x100  # 93C56
        if self._chip == 0x66:
            return 0x100  # 93C66 (512 bytes, only 256 are used)
        try:
            eeprom_size = self._PROPERTIES[self.device_version].size
        except (AttributeError, KeyError) as exc:
            raise FtdiError('No EEPROM') from exc
        return eeprom_size

    @property
    def storage_size(self) -> int:
        """Report the number of EEPROM bytes that can be used for configuration
            storage. The physical EEPROM size may be greater

            :return: the number of bytes in the eeprom that will be used for
                configuration storage
        """
        try:
            eeprom_storage_size = self.size
            if self.is_mirroring_enabled:
                eeprom_storage_size = self.mirror_sector
        except FtdiError as exc:
            raise exc
        return eeprom_storage_size

    @property
    def data(self) -> bytes:
        """Returns the content of the EEPROM.

           :return: the content as bytes.
        """
        self._sync_eeprom()
        return bytes(self._eeprom)

    @property
    def properties(self) -> Set[str]:
        """Returns the supported properties for the current device.

           :return: the supported properies.
        """
        props = set(self._config.keys())
        props -= set(self.VAR_STRINGS)
        return props

    @property
    def is_empty(self) -> bool:
        """Reports whether the EEPROM has been erased, or no EEPROM is
           connected to the FTDI EEPROM port.

           :return: True if no content is detected
        """
        if len(self._eeprom) != self.size:
            return False
        for byte in self._eeprom:
            if byte != 0xFF:
                return False
        return True

    @property
    def cbus_pins(self) -> List[int]:
        """Return the list of CBUS pins configured as GPIO, if any

           :return: list of CBUS pins
        """
        pins = [pin for pin in range(0, 10)
                if self._config.get(f'cbus_func_{pin}', '') == 'GPIO']
        return pins

    @property
    def cbus_mask(self) -> int:
        """Return the bitmask of CBUS pins configured as GPIO.

           The bitmap contains four bits, ordered in natural order.

           :return: CBUS mask
        """
        if self.device_version == 0x900:  # FT232H
            cbus = [5, 6, 8, 9]
        else:
            cbus = list(range(4))
        mask = 0
        for bix, pin in enumerate(cbus):
            if self._config.get(f'cbus_func_{pin}', '') == 'GPIO':
                mask |= 1 << bix
        return mask

    @property
    def has_mirroring(self) -> bool:
        """Report whether the device supports EEPROM content duplication
           across its two sectors.

           :return: True if the device support mirorring
        """
        return (self._PROPERTIES[self.device_version].user and
                self._ftdi.device_version != 0x1000)

    @property
    def mirror_sector(self) -> int:
        """Report start address of the mirror sector in the EEPROM.
           This is only valid if the FTDI is capable of mirroring EEPROM data.

           :return: the start address
        """
        if self.has_mirroring:
            return self.size // 2
        raise FtdiError('EEPROM does not support mirroring')

    @property
    def is_mirroring_enabled(self) -> bool:
        """Check if EEPROM mirroring is currently enabled for this EEPROM.
            See enable_mirroring for more details on EEPROM mirroring
            functionality
        """
        return self.has_mirroring and self._mirror

    def enable_mirroring(self, enable: bool) -> None:
        """Enable EEPROM write mirroring. When enabled, this divides the EEPROM
           into 2 sectors and mirrors configuration data between them.

           For example on a 256 byte EEPROM, two 128 byte 'sectors' will be
           used to store identical data. Configuration properties/strings will
           be writen to both of these sectors. For some devices (like the
           4232H), this makes the PyFtdi EEPROM functionally similar to
           FT_PROG.

           Note: Data will only be mirrored if the has_mirroring property
           returns true (after establishing a connection to the ftdi)

           :param enable: enable or disable EEPROM mirroring
        """
        self._mirror = enable

    def save_config(self, file: TextIO) -> None:
        """Save the EEPROM content as an INI stream.

           :param file: output stream
        """
        self._sync_eeprom()
        cfg = ConfigParser()
        cfg.add_section('values')
        for name, value in self._config.items():
            val = str(value)
            if isinstance(value, bool):
                val = val.lower()
            cfg.set('values', name, val)
        cfg.add_section('raw')
        length = 16
        for i in range(0, len(self._eeprom), length):
            chunk = self._eeprom[i:i+length]
            hexa = hexlify(chunk).decode()
            cfg.set('raw', f'@{i:02x}', hexa)
        cfg.write(file)

    def load_config(self, file: TextIO, section: Optional[str] = None) -> None:
        """Load the EEPROM content from an INI stream.

           The ``section`` argument selects which section(s) to load:

           * ``raw`` only loads the raw data (hexabytes) from a previous dump
           * ``values`` only loads the values section, that is the human
             readable configuration.
           * ``all``, which is the default section selection, load the raw
             section, then overwrite part of it with any configuration value
             from the ``values`` section. This provides a handy way to use an
             existing dump from a valid EEPROM content, while customizing some
             parameters, such as the serial number.

           :param file: input stream
           :paran section: which section to load from the ini file
        """
        self._sync_eeprom()
        cfg = ConfigParser()
        cfg.read_file(file)
        loaded = False
        sections = cfg.sections()
        if section not in ('all', None) and section not in sections:
            raise FtdiEepromError(f'No such configuration section {section}')
        sect = 'raw'
        if sect in sections and section in (None, 'all', sect):
            if not cfg.has_section(sect):
                raise FtdiEepromError(f"No '{sect}' section in INI file")
            options = cfg.options(sect)
            try:
                for opt in options:
                    if not opt.startswith('@'):
                        raise ValueError()
                    address = int(opt[1:], 16)
                    hexval = cfg.get(sect, opt).strip()
                    buf = unhexlify(hexval)
                    self._eeprom[address:address+len(buf)] = buf
            except IndexError as exc:
                raise ValueError(f"Invalid address in '{sect}' "
                                 f"section") from exc
            except ValueError as exc:
                raise ValueError(f"Invalid line in '{sect}' section") from exc
            self._compute_crc(self._eeprom, True)
            if not self._valid:
                raise ValueError('Loaded RAW section is invalid (CRC mismatch')
            loaded = True
        sect = 'values'
        vmap = {
            'manufacturer': 'manufacturer_name',
            'product': 'product_name',
            'serial': 'serial_number'
        }
        if sect in sections and section in (None, 'all', sect):
            if not cfg.has_section(sect):
                raise FtdiEepromError(f"No '{sect}' section in INI file")
            options = cfg.options(sect)
            for opt in options:
                value = cfg.get(sect, opt).strip()
                if opt in vmap:
                    func = getattr(self, f'set_{vmap[opt]}')
                    func(value)
                else:
                    self.log.debug('Assigning opt %s = %s', opt, value)
                    try:
                        self.set_property(opt, value)
                    except (TypeError, ValueError, NotImplementedError) as exc:
                        self.log.warning("Ignoring setting '%s': %s", opt, exc)
            loaded = True
        if not loaded:
            raise ValueError(f'Invalid section: {section}')
        self._sync_eeprom()

    def set_serial_number(self, serial: str) -> None:
        """Define a new serial number."""
        self._validate_string(serial)
        self._update_var_string('serial', serial)
        self.set_property('has_serial', True)

    def set_manufacturer_name(self, manufacturer: str) -> None:
        """Define a new manufacturer string."""
        self._validate_string(manufacturer)
        self._update_var_string('manufacturer', manufacturer)

    def set_product_name(self, product: str) -> None:
        """Define a new product name."""
        self._validate_string(product)
        self._update_var_string('product', product)

    def set_property(self, name: str, value: Union[str, int, bool],
                     out: Optional[TextIO] = None) -> None:
        """Change the value of a stored property.

           :see: :py:meth:`properties` for a list of valid property names.
                 Note that for now, only a small subset of properties can be
                 changed.
           :param name: the property to change
           :param value: the new value (supported values depend on property)
           :param out: optional output stream to report hints
        """
        mobj = match(r'cbus_func_(\d)', name)
        if mobj:
            if not isinstance(value, str):
                raise ValueError("'{name}' should be specified as a string")
            self._set_cbus_func(int(mobj.group(1)), value, out)
            self._dirty.add(name)
            return
        mobj = match(r'([abcd])bus_(drive|slow_slew|schmitt)', name)
        if mobj:
            self._set_bus_control(mobj.group(1), mobj.group(2), value, out)
            self._dirty.add(name)
            return
        mobj = match(r'group_(\d)_(drive|schmitt|slow_slew)', name)
        if mobj:
            self._set_group(int(mobj.group(1)), mobj.group(2), value, out)
            self._dirty.add(name)
            return
        mobj = match(r'channel_([abcd])_type', name)
        if mobj:
            chn = mobj.group(1)
            if value == 'UART':
                val = 0
            else:
                val = self.CHANNEL[value]
            if self.device_version == 0x0700 and chn in 'ab':
                # FT2232H
                idx = 0x00 if chn == 'a' else 0x01
                mask = 0x07
            elif self.device_version == 0x0800:
                # FT4232H
                idx = 0x0b
                mask = 1 << {'a': 4, 'b': 5, 'c': 6, 'd': 7}.get(chn)
                val = mask if val > 0 else 0
            elif self.device_version == 0x0900 and chn == 'a':
                # FT232H
                idx = 0x00
                mask = 0x0F
            else:
                raise ValueError(
                    f"Option '{name}' not supported by the device")
            if val & ~mask:
                raise ValueError(
                    f"Unsupported value for setting '{name}': {val}")
            self._eeprom[idx] &= ~mask
            self._eeprom[idx] |= val
            if self.is_mirroring_enabled:
                idx2 = self.mirror_sector + idx
                self._eeprom[idx2] &= ~mask
                self._eeprom[idx2] |= val
            self._dirty.add(name)
            return
        mobj = match(r'channel_([abcd])_driver', name)
        if mobj:
            chn = mobj.group(1)
            if value == 'VCP':
                val = 1
            elif value == 'D2XX':
                val = 0
            else:
                raise ValueError(
                    f"Invalid value '{value} for '{name}'")
            if self.device_version == 0x0700 and chn in 'ab':
                # FT2232H
                idx = 0x00 if chn == 'a' else 0x01
                mask = 1 << 3
            elif self.device_version == 0x0800:
                # FT4232H
                idx = {'a': 0, 'b': 1, 'c': 0, 'd': 1}.get(chn)
                mask = 1 << {'a': 3, 'b': 3, 'c': 7, 'd': 7}.get(chn)
            elif self.device_version == 0x0900 and chn == 'a':
                # FT232H
                idx = 0x00
                mask = 1 << 4
            else:
                raise ValueError(
                    f"Option '{name}' not supported by the device")
            self._eeprom[idx] &= ~mask
            if val:
                self._eeprom[idx] |= mask
            if self.is_mirroring_enabled:
                idx2 = self.mirror_sector + idx
                self._eeprom[idx2] &= ~mask
                if val:
                    self._eeprom[idx2] |= mask
            self._dirty.add(name)
            return
        confs = {
            'remote_wakeup': (0, 5),
            'self_powered': (0, 6),
            'in_isochronous': (2, 0),
            'out_isochronous': (2, 1),
            'suspend_pull_down': (2, 2),
            'has_serial': (2, 3),
        }
        hwords = {
            'vendor_id': 0x02,
            'product_id': 0x04,
            'type': 0x06,
        }
        if self.device_version in (0x0400, 0x0500):
            # Type BM and 2232C/D use 0xc to encode the USB version to expose
            # H device use this location to encode bus/group properties
            hwords['usb_version'] = 0x0c
            confs['use_usb_version'] = (2, 4)
        if name in hwords:
            val = to_int(value)
            if not 0 <= val <= 0xFFFF:
                raise ValueError(f'Invalid value for {name}')
            offset = hwords[name]
            self._eeprom[offset:offset+2] = spack('<H', val)
            if self.is_mirroring_enabled:
                # duplicate in 'sector 2'
                offset2 = self.mirror_sector + offset
                self._eeprom[offset2:offset2+2] = spack('<H', val)
            self._dirty.add(name)
            return
        if name in confs:
            val = to_bool(value, permissive=False, allow_int=True)
            offset, bit = confs[name]
            mask = 1 << bit
            idx = 0x08 + offset
            if val:
                self._eeprom[idx] |= mask
                if self.is_mirroring_enabled:
                    # duplicate in 'sector 2'
                    idx2 = self.mirror_sector + idx
                    self._eeprom[idx2] |= mask
            else:
                self._eeprom[idx] &= ~mask
                if self.is_mirroring_enabled:
                    # duplicate in 'sector 2'
                    idx2 = self.mirror_sector + idx
                    self._eeprom[idx2] &= ~mask
            self._dirty.add(name)
            return
        if name == 'power_max':
            val = to_int(value) >> 1
            idx = 0x09
            self._eeprom[idx] = val
            if self.is_mirroring_enabled:
                # duplicate in 'sector 2'
                idx2 = self.mirror_sector + idx
                self._eeprom[idx2] = val
            self._dirty.add(name)
            return
        if name.startswith('invert_'):
            if self.device_version not in (0x600, 0x1000):
                raise ValueError('UART control line inversion not available '
                                 'with this device')
            self._set_invert(name[len('invert_'):], value, out)
            self._dirty.add(name)
            return
        if name == 'chip':
            val = to_int(value)
            idx = self._PROPERTIES[self.device_version].chipoff
            if idx is None:
                raise ValueError(
                    f"Setting '{name}' is not supported by the chip")
            self._eeprom[idx] = val
            if self.is_mirroring_enabled:
                idx2 = self.mirror_sector + idx
                self._eeprom[idx2] = val
            self._dirty.add(name)
            return
        if name == 'suspend_dbus7':
            val = to_bool(value, permissive=False, allow_int=True)
            if self.device_version == 0x0700:
                # FT2232H
                idx = 0x01
                mask = self.CFG1.SUSPEND_DBUS7.value
                self._eeprom[idx] &= ~mask
                if val:
                    self._eeprom[idx] |= mask
                if self.is_mirroring_enabled:
                    idx2 = self.mirror_sector + idx
                    self._eeprom[idx2] &= ~mask
                    if val:
                        self._eeprom[idx2] |= mask
            else:
                raise ValueError(
                    f"Setting '{name}' is not supported by the chip")
            self._dirty.add(name)
            return
        if name in self.properties:
            if name not in self._config:
                raise NotImplementedError('Change is not supported')
            curval = self._config[name]
            try:
                curtype = type(curval)
                value = curtype(value)
            except (ValueError, TypeError) as exc:
                raise ValueError(f"Cannot be converted to the proper type "
                                 f"'{curtype}'") from exc
            if value != curval:
                raise NotImplementedError('Not yet supported')
            # no-op change is silently ignored
            return
        raise ValueError(f'Unknown property: {name}')

    def erase(self, erase_byte: Optional[int] = 0xFF) -> None:
        """Erase the whole EEPROM.

            :param erase_byte: Optional erase byte to use. Default to 0xFF
        """
        self._eeprom = bytearray([erase_byte] * self.size)
        self._config.clear()
        self._dirty.add('eeprom')

    def initialize(self) -> None:
        """Initialize the EEPROM with some default sensible values.
        """
        dev_ver = self.device_version
        dev_name = Ftdi.DEVICE_NAMES[dev_ver]
        vid = Ftdi.FTDI_VENDOR
        pid = Ftdi.PRODUCT_IDS[vid][dev_name]
        self.set_manufacturer_name('FTDI')
        self.set_product_name(dev_name.upper())
        sernum = ''.join([chr(randint(ord('A'), ord('Z'))) for _ in range(5)])
        self.set_serial_number(f'FT{randint(0, 9)}{sernum}')
        self.set_property('vendor_id', vid)
        self.set_property('product_id', pid)
        self.set_property('type', dev_ver)
        self.set_property('power_max', 150)
        self._sync_eeprom()

    def sync(self) -> None:
        """Force re-evaluation of configuration after some changes.

           This API is not useful for regular usage, but might help for testing
           when the EEPROM does not go through a full save/load cycle
        """
        self._sync_eeprom()

    def dump_config(self, file: Optional[BinaryIO] = None) -> None:
        """Dump the configuration to a file.

           :param file: the output file, default to stdout
        """
        if self._dirty:
            self._decode_eeprom()
        for name, value in self._config.items():
            print(f'{name}: {value}', file=file or sys.stdout)

    def commit(self, dry_run: bool = True, no_crc: bool = False) -> bool:
        """Commit any changes to the EEPROM.

           :param dry_run: log what should be written, do not actually change
                  the EEPROM content
           :param no_crc: do not compute EEPROM CRC. This should only be used
            to perform a full erasure of the EEPROM, as an attempt to recover
            from a corrupted config.

           :return: True if some changes have been committed to the EEPROM
        """
        self._sync_eeprom(no_crc)
        if not self._modified:
            self.log.warning('No change to commit, EEPROM not modified')
            return False
        self._ftdi.overwrite_eeprom(self._eeprom, dry_run=dry_run)
        if not dry_run:
            eeprom = self._read_eeprom()
            if eeprom != self._eeprom:
                pos = 0
                for pos, (old, new) in enumerate(zip(self._eeprom, eeprom)):
                    if old != new:
                        break
                pos &= ~0x1
                raise FtdiEepromError(f'Write to EEPROM failed @ 0x{pos:02x}')
            self._modified = False
        return dry_run

    def reset_device(self):
        """Execute a USB device reset."""
        self._ftdi.reset(usb_reset=True)

    def set_test_mode(self, enable: bool):
        """Enable test mode (silence some warnings)."""
        self._test_mode = enable

    @classmethod
    def _validate_string(cls, string):
        for invchr in ':/':
            # do not accept characters which are interpreted as URL seperators
            if invchr in string:
                raise ValueError(f"Invalid character '{invchr}' in string")

    def _update_var_string(self, name: str, value: str) -> None:
        if name not in self.VAR_STRINGS:
            raise ValueError(f'{name} is not a variable string')
        try:
            if value == self._config[name]:
                return
        except KeyError:
            # not yet defined
            pass
        self._config[name] = value
        self._dirty.add(name)

    def _generate_var_strings(self, fill=True) -> None:
        """
            :param fill: fill the remainder of the space after the var strings
                with 0s
        """
        stream = bytearray()
        dynpos = self._PROPERTIES[self.device_version].dynoff
        if dynpos > self._size:
            # if a custom, small EEPROM device is used
            dynpos = 0x40
        data_pos = dynpos
        tbl_pos = 0x0e
        if self.is_mirroring_enabled:
            # start of var-strings in sector 1 (used for mirrored config)
            s1_vstr_start = data_pos - self.mirror_sector
            tbl_sector2_pos = self.mirror_sector + tbl_pos
        for name in self.VAR_STRINGS:
            try:
                ustr = self._config[name].encode('utf-16le')
            except KeyError:
                ustr = ''
            length = len(ustr)+2
            stream.append(length)
            stream.append(0x03)  # string descriptor
            stream.extend(ustr)
            self._eeprom[tbl_pos] = data_pos | 0x80
            tbl_pos += 1
            if self.is_mirroring_enabled:
                self._eeprom[tbl_sector2_pos] = data_pos
                tbl_sector2_pos += 1
            self._eeprom[tbl_pos] = length
            tbl_pos += 1
            if self.is_mirroring_enabled:
                self._eeprom[tbl_sector2_pos] = length
                tbl_sector2_pos += 1
            data_pos += length
        if self.is_mirroring_enabled:
            self._eeprom[s1_vstr_start:s1_vstr_start+len(stream)] = stream
        self._eeprom[dynpos:dynpos+len(stream)] = stream
        mtp = self._ftdi.device_version == 0x1000
        crc_pos = 0x100 if mtp else self._size
        rem = crc_pos - (dynpos + len(stream))
        if rem < 0:
            oversize = (-rem + 2) // 2
            raise FtdiEepromError(f'Cannot fit strings into EEPROM, '
                                  f'{oversize} oversize characters')
        if fill:
            self._eeprom[dynpos+len(stream):crc_pos] = bytes(rem)
            if self.is_mirroring_enabled:
                crc_s1_pos = self.mirror_sector
                self._eeprom[s1_vstr_start+len(stream):crc_s1_pos] = bytes(rem)

    def _sync_eeprom(self, no_crc: bool = False):
        if not self._dirty:
            self.log.debug('No change detected for EEPROM content')
            return
        if not no_crc:
            if any(x in self._dirty for x in self.VAR_STRINGS):
                self._generate_var_strings()
                for varstr in self.VAR_STRINGS:
                    self._dirty.discard(varstr)
            self._update_crc()
            self._decode_eeprom()
        self._dirty.clear()
        self._modified = True
        self.log.debug('EEPROM content regenerated (not yet committed)')

    def _compute_crc(self, eeprom: Union[bytes, bytearray], check=False):
        mtp = self._ftdi.device_version == 0x1000
        crc_pos = 0x100 if mtp else len(eeprom)
        crc_size = scalc('<H')
        if not check:
            # check mode: add CRC itself, so that result should be zero
            crc_pos -= crc_size
        if self.is_mirroring_enabled:
            mirror_s1_crc_pos = self.mirror_sector
            if not check:
                mirror_s1_crc_pos -= crc_size
            # if mirroring, only calculate the crc for the first sector/half
            #   of the eeprom. Data (including this crc) are duplicated in
            #   the second sector/half
            crc = self._ftdi.calc_eeprom_checksum(eeprom[:mirror_s1_crc_pos])
        else:
            crc = self._ftdi.calc_eeprom_checksum(eeprom[:crc_pos])
        if check:
            self._valid = not bool(crc)
            if not self._valid:
                self.log.debug('CRC is now 0x%04x', crc)
            else:
                self.log.debug('CRC OK')
        return crc, crc_pos, crc_size

    def _update_crc(self):
        crc, crc_pos, crc_size = self._compute_crc(
            self._eeprom, False)
        self._eeprom[crc_pos:crc_pos+crc_size] = spack('<H', crc)
        if self.is_mirroring_enabled:
            # if mirroring calculate where the CRC will start in first sector
            crc_s1_start = self.mirror_sector - crc_size
            self._eeprom[crc_s1_start:crc_s1_start+crc_size] = spack('<H', crc)

    def _compute_size(self, eeprom: Union[bytes, bytearray]) \
            -> Tuple[int, bool]:
        """
            :return: Tuple of:
                - int of usable size of the eeprom
                - bool of whether eeprom mirroring was detected or not
        """
        if self._ftdi.is_eeprom_internal:
            return self._ftdi.max_eeprom_size, False
        if all(x == 0xFF for x in eeprom):
            # erased EEPROM, size is unknown
            return self._ftdi.max_eeprom_size, False
        if ee
Download .txt
gitextract_vloilsyq/

├── .flake8
├── .github/
│   └── workflows/
│       ├── pypi.yml
│       ├── pythonchecksyntax.yml
│       ├── pythonmocktests.yml
│       └── pythonpackage.yml
├── .gitignore
├── .pylintrc
├── LICENSE
├── MANIFEST.in
├── README.md
├── _config.yml
├── pyftdi/
│   ├── INSTALL
│   ├── __init__.py
│   ├── bin/
│   │   ├── ftconf.py
│   │   ├── ftdi_urls.py
│   │   ├── i2cscan.py
│   │   ├── pyterm.py
│   │   └── uphy.sh
│   ├── bits.py
│   ├── doc/
│   │   ├── api/
│   │   │   ├── eeprom.rst
│   │   │   ├── ftdi.rst
│   │   │   ├── gpio.rst
│   │   │   ├── i2c.rst
│   │   │   ├── index.rst
│   │   │   ├── misc.rst
│   │   │   ├── spi.rst
│   │   │   ├── uart.rst
│   │   │   └── usbtools.rst
│   │   ├── authors.rst
│   │   ├── conf.py
│   │   ├── defs.rst
│   │   ├── eeprom.rst
│   │   ├── features.rst
│   │   ├── gpio.rst
│   │   ├── index.rst
│   │   ├── installation.rst
│   │   ├── license.rst
│   │   ├── pinout.rst
│   │   ├── requirements.rst
│   │   ├── testing.rst
│   │   ├── tools.rst
│   │   ├── troubleshooting.rst
│   │   └── urlscheme.rst
│   ├── eeprom.py
│   ├── ftdi.py
│   ├── gpio.py
│   ├── i2c.py
│   ├── jtag.py
│   ├── misc.py
│   ├── serialext/
│   │   ├── __init__.py
│   │   ├── logger.py
│   │   ├── protocol_ftdi.py
│   │   ├── protocol_unix.py
│   │   └── tests/
│   │       └── rl.py
│   ├── spi.py
│   ├── term.py
│   ├── tests/
│   │   ├── backend/
│   │   │   ├── __init__.py
│   │   │   ├── consts.py
│   │   │   ├── ftdivirt.py
│   │   │   ├── loader.py
│   │   │   ├── mpsse.py
│   │   │   └── usbvirt.py
│   │   ├── bits.py
│   │   ├── cbus.py
│   │   ├── eeprom.py
│   │   ├── eeprom_mock.py
│   │   ├── ftdi.py
│   │   ├── gpio.py
│   │   ├── i2c.py
│   │   ├── jtag.py
│   │   ├── mockusb.py
│   │   ├── resources/
│   │   │   ├── custom_vidpid.yaml
│   │   │   ├── ft2232h.yaml
│   │   │   ├── ft230x.yaml
│   │   │   ├── ft230x_io.yaml
│   │   │   ├── ft231x_cbus.yaml
│   │   │   ├── ft232h.yaml
│   │   │   ├── ft232h_x2.yaml
│   │   │   ├── ft232r.yaml
│   │   │   ├── ft4232h.yaml
│   │   │   ├── ft4232ha.yaml
│   │   │   └── ftmany.yaml
│   │   ├── spi.py
│   │   ├── toolsimport.py
│   │   └── uart.py
│   ├── tracer.py
│   └── usbtools.py
├── requirements.txt
├── setup.cfg
├── setup.py
└── test-requirements.txt
Download .txt
SYMBOL INDEX (1180 symbols across 40 files)

FILE: pyftdi/__init__.py
  class FtdiLogger (line 24) | class FtdiLogger:
    method set_formatter (line 31) | def set_formatter(cls, formatter):
    method get_level (line 37) | def get_level(cls):
    method set_level (line 41) | def set_level(cls, level):

FILE: pyftdi/bin/ftconf.py
  function main (line 27) | def main():

FILE: pyftdi/bin/ftdi_urls.py
  function main (line 19) | def main():

FILE: pyftdi/bin/i2cscan.py
  class I2cBusScanner (line 23) | class I2cBusScanner:
    method scan (line 35) | def scan(cls, url: str, smb_mode: bool = True, force: bool = False) \
  function main (line 87) | def main():

FILE: pyftdi/bin/pyterm.py
  class MiniTerm (line 36) | class MiniTerm:
    method __init__ (line 41) | def __init__(self, device, baudrate=None, parity=None, rtscts=False,
    method run (line 55) | def run(self, fullmode=False, loopback=False, silent=False,
    method _sourcer (line 83) | def _sourcer(self):
    method _get_from_source (line 96) | def _get_from_source(self):
    method _get_from_port (line 105) | def _get_from_port(self):
    method _reader (line 117) | def _reader(self, loopback, getfunc):
    method _writer (line 141) | def _writer(self, fullmode, silent, localecho, crlf=0):
    method _cleanup (line 183) | def _cleanup(self, *args):
    method _open_port (line 211) | def _open_port(device, baudrate, parity, rtscts, debug=False):
  function get_default_device (line 256) | def get_default_device() -> str:
  function main (line 276) | def main():

FILE: pyftdi/bits.py
  class BitSequenceError (line 17) | class BitSequenceError(Exception):
  class BitSequence (line 21) | class BitSequence:
    method __init__ (line 39) | def __init__(self, value: Union['BitSequence', str, int] = None,
    method sequence (line 77) | def sequence(self) -> bytearray:
    method reverse (line 81) | def reverse(self) -> 'BitSequence':
    method invert (line 86) | def invert(self) -> 'BitSequence':
    method append (line 91) | def append(self, seq) -> 'BitSequence':
    method lsr (line 98) | def lsr(self, count: int) -> None:
    method rsr (line 103) | def rsr(self, count: int) -> None:
    method tobit (line 108) | def tobit(self) -> bool:
    method tobyte (line 114) | def tobyte(self, msb: bool = False) -> int:
    method tobytes (line 127) | def tobytes(self, msb: bool = False, msby: bool = False) -> bytearray:
    method _tomutable (line 146) | def _tomutable(value: Union[str, Tuple]) -> List:
    method _init_from_integer (line 159) | def _init_from_integer(self, value: int, msb: bool, length: int) -> None:
    method _init_from_iterable (line 172) | def _init_from_iterable(self, iterable: Iterable, msb: bool) -> None:
    method _init_from_sibling (line 185) | def _init_from_sibling(self, value: 'BitSequence', msb: bool) -> None:
    method _update_length (line 191) | def _update_length(self, length, msb):
    method __iter__ (line 202) | def __iter__(self):
    method __reversed__ (line 205) | def __reversed__(self):
    method __getitem__ (line 208) | def __getitem__(self, index):
    method __setitem__ (line 213) | def __setitem__(self, index, value):
    method __len__ (line 230) | def __len__(self):
    method __eq__ (line 233) | def __eq__(self, other):
    method __ne__ (line 236) | def __ne__(self, other):
    method __le__ (line 239) | def __le__(self, other):
    method __lt__ (line 242) | def __lt__(self, other):
    method __ge__ (line 245) | def __ge__(self, other):
    method __gt__ (line 248) | def __gt__(self, other):
    method _cmp (line 251) | def _cmp(self, other):
    method __repr__ (line 261) | def __repr__(self):
    method __str__ (line 265) | def __str__(self):
    method __int__ (line 277) | def __int__(self):
    method __and__ (line 284) | def __and__(self, other):
    method __or__ (line 292) | def __or__(self, other):
    method __add__ (line 300) | def __add__(self, other):
    method __ilshift__ (line 303) | def __ilshift__(self, count):
    method __irshift__ (line 310) | def __irshift__(self, count):
    method inc (line 317) | def inc(self) -> None:
    method dec (line 325) | def dec(self) -> None:
    method invariant (line 333) | def invariant(self) -> bool:
  class BitZSequence (line 351) | class BitZSequence(BitSequence):
    method __init__ (line 365) | def __init__(self, value=None, msb=False, length=0):
    method invert (line 368) | def invert(self):
    method tobyte (line 373) | def tobyte(self, msb=False):
    method tobytes (line 377) | def tobytes(self, msb=False, msby=False):
    method matches (line 381) | def matches(self, other):
    method _init_from_iterable (line 396) | def _init_from_iterable(self, iterable, msb):
    method __repr__ (line 411) | def __repr__(self):
    method __int__ (line 415) | def __int__(self):
    method __cmp__ (line 421) | def __cmp__(self, other):
    method __and__ (line 431) | def __and__(self, other):
    method __or__ (line 446) | def __or__(self, other):
    method __rand__ (line 461) | def __rand__(self, other):
    method __ror__ (line 464) | def __ror__(self, other):
    method __radd__ (line 467) | def __radd__(self, other):
  class BitField (line 471) | class BitField:
    method __init__ (line 480) | def __init__(self, value=0):
    method to_seq (line 483) | def to_seq(self, msb=0, lsb=0):
    method __getitem__ (line 495) | def __getitem__(self, index):
    method __setitem__ (line 509) | def __setitem__(self, index, value):
    method __int__ (line 530) | def __int__(self):
    method __str__ (line 533) | def __str__(self):

FILE: pyftdi/doc/conf.py
  function read (line 22) | def read(where, *parts):
  function find_meta (line 31) | def find_meta(meta):
  function setup (line 108) | def setup(app):

FILE: pyftdi/eeprom.py
  class FtdiEepromError (line 26) | class FtdiEepromError(FtdiError):
  class Hex2Int (line 30) | class Hex2Int(int):
    method __str__ (line 32) | def __str__(self):
  class Hex4Int (line 36) | class Hex4Int(int):
    method __str__ (line 38) | def __str__(self):
  class FtdiEeprom (line 42) | class FtdiEeprom:
    method __init__ (line 102) | def __init__(self):
    method __getattr__ (line 116) | def __getattr__(self, name):
    method eeprom_sizes (line 122) | def eeprom_sizes(cls) -> List[int]:
    method open (line 130) | def open(self, device: Union[str, UsbDevice],
    method close (line 162) | def close(self) -> None:
    method connect (line 171) | def connect(self, ftdi: Ftdi, ignore: bool = False) -> None:
    method device_version (line 190) | def device_version(self) -> int:
    method size (line 202) | def size(self) -> int:
    method default_size (line 215) | def default_size(self) -> int:
    method storage_size (line 236) | def storage_size(self) -> int:
    method data (line 252) | def data(self) -> bytes:
    method properties (line 261) | def properties(self) -> Set[str]:
    method is_empty (line 271) | def is_empty(self) -> bool:
    method cbus_pins (line 285) | def cbus_pins(self) -> List[int]:
    method cbus_mask (line 295) | def cbus_mask(self) -> int:
    method has_mirroring (line 313) | def has_mirroring(self) -> bool:
    method mirror_sector (line 323) | def mirror_sector(self) -> int:
    method is_mirroring_enabled (line 334) | def is_mirroring_enabled(self) -> bool:
    method enable_mirroring (line 341) | def enable_mirroring(self, enable: bool) -> None:
    method save_config (line 358) | def save_config(self, file: TextIO) -> None:
    method load_config (line 379) | def load_config(self, file: TextIO, section: Optional[str] = None) -> ...
    method set_serial_number (line 451) | def set_serial_number(self, serial: str) -> None:
    method set_manufacturer_name (line 457) | def set_manufacturer_name(self, manufacturer: str) -> None:
    method set_product_name (line 462) | def set_product_name(self, product: str) -> None:
    method set_property (line 467) | def set_property(self, name: str, value: Union[str, int, bool],
    method erase (line 677) | def erase(self, erase_byte: Optional[int] = 0xFF) -> None:
    method initialize (line 686) | def initialize(self) -> None:
    method sync (line 703) | def sync(self) -> None:
    method dump_config (line 711) | def dump_config(self, file: Optional[BinaryIO] = None) -> None:
    method commit (line 721) | def commit(self, dry_run: bool = True, no_crc: bool = False) -> bool:
    method reset_device (line 749) | def reset_device(self):
    method set_test_mode (line 753) | def set_test_mode(self, enable: bool):
    method _validate_string (line 758) | def _validate_string(cls, string):
    method _update_var_string (line 764) | def _update_var_string(self, name: str, value: str) -> None:
    method _generate_var_strings (line 776) | def _generate_var_strings(self, fill=True) -> None:
    method _sync_eeprom (line 828) | def _sync_eeprom(self, no_crc: bool = False):
    method _compute_crc (line 843) | def _compute_crc(self, eeprom: Union[bytes, bytearray], check=False):
    method _update_crc (line 868) | def _update_crc(self):
    method _compute_size (line 877) | def _compute_size(self, eeprom: Union[bytes, bytearray]) \
    method _read_eeprom (line 895) | def _read_eeprom(self) -> bytes:
    method _decode_eeprom (line 913) | def _decode_eeprom(self):
    method _decode_string (line 957) | def _decode_string(self, offset):
    method _set_cbus_func (line 967) | def _set_cbus_func(self, cpin: int, value: str,
    method _filter_cbus_func_x900 (line 1010) | def _filter_cbus_func_x900(cls, cpin: int, value: str):
    method _filter_cbus_func_x600 (line 1025) | def _filter_cbus_func_x600(cls, cpin: int, value: str):
    method _set_bus_control (line 1031) | def _set_bus_control(self, bus: str, control: str,
    method _set_group (line 1040) | def _set_group(self, group: int, control: str,
    method _set_bus_control_230x (line 1048) | def _set_bus_control_230x(self, bus: str, control: str,
    method _set_group_x232h (line 1055) | def _set_group_x232h(self, group: int, control: str, value: str,
    method _set_bus_xprop (line 1066) | def _set_bus_xprop(self, offset: int, high_nibble: bool, control: str,
    method _set_invert (line 1112) | def _set_invert(self, name, value, out):
    method _decode_x (line 1127) | def _decode_x(self):
    method _decode_232h (line 1151) | def _decode_232h(self):
    method _decode_232r (line 1182) | def _decode_232r(self):
    method _decode_2232h (line 1207) | def _decode_2232h(self):
    method _decode_4232h (line 1215) | def _decode_4232h(self):
    method _decode_x232h (line 1227) | def _decode_x232h(self, cfg):

FILE: pyftdi/ftdi.py
  class FtdiError (line 27) | class FtdiError(IOError):
  class FtdiFeatureError (line 31) | class FtdiFeatureError(FtdiError):
  class FtdiMpsseError (line 35) | class FtdiMpsseError(FtdiFeatureError):
  class FtdiEepromError (line 39) | class FtdiEepromError(FtdiError):
  class Ftdi (line 43) | class Ftdi:
    class BitMode (line 130) | class BitMode(IntEnum):
    method __init__ (line 315) | def __init__(self):
    method create_from_url (line 345) | def create_from_url(cls, url: str) -> 'Ftdi':
    method list_devices (line 358) | def list_devices(cls, url: Optional[str] = None) -> \
    method show_devices (line 370) | def show_devices(cls, url: Optional[str] = None,
    method get_identifiers (line 384) | def get_identifiers(cls, url: str) -> Tuple[UsbDeviceDescriptor, int]:
    method get_device (line 394) | def get_device(cls, url: str) -> UsbDevice:
    method add_custom_vendor (line 404) | def add_custom_vendor(cls, vid: int, vidname: str = '') -> None:
    method add_custom_product (line 420) | def add_custom_product(cls, vid: int, pid: int, pidname: str = '') -> ...
    method decode_modem_status (line 441) | def decode_modem_status(cls, value: bytes, error_only: bool = False) -> \
    method find_all (line 459) | def find_all(vps: Sequence[Tuple[int, int]], nocache: bool = False) -> \
    method is_connected (line 475) | def is_connected(self) -> bool:
    method open_from_url (line 482) | def open_from_url(self, url: str) -> None:
    method open (line 491) | def open(self, vendor: int, product: int, bus: Optional[int] = None,
    method open_from_device (line 524) | def open_from_device(self, device: UsbDevice,
    method close (line 558) | def close(self, freeze: bool = False) -> None:
    method reset (line 588) | def reset(self, usb_reset: bool = False) -> None:
    method open_mpsse_from_url (line 603) | def open_mpsse_from_url(self, url: str, direction: int = 0x0,
    method open_mpsse (line 632) | def open_mpsse(self, vendor: int, product: int, bus: Optional[int] = N...
    method open_mpsse_from_device (line 686) | def open_mpsse_from_device(self, device: UsbDevice,
    method open_bitbang_from_url (line 762) | def open_bitbang_from_url(self, url: str, direction: int = 0x0,
    method open_bitbang (line 787) | def open_bitbang(self, vendor: int, product: int,
    method open_bitbang_from_device (line 822) | def open_bitbang_from_device(self, device: UsbDevice,
    method usb_path (line 863) | def usb_path(self) -> Tuple[int, int, int]:
    method device_version (line 874) | def device_version(self) -> int:
    method ic_name (line 886) | def ic_name(self) -> str:
    method device_port_count (line 899) | def device_port_count(self) -> int:
    method port_index (line 909) | def port_index(self) -> int:
    method port_width (line 919) | def port_width(self) -> int:
    method has_mpsse (line 934) | def has_mpsse(self) -> bool:
    method has_wide_port (line 945) | def has_wide_port(self) -> bool:
    method has_cbus (line 954) | def has_cbus(self) -> bool:
    method has_drivezero (line 972) | def has_drivezero(self) -> bool:
    method is_legacy (line 985) | def is_legacy(self) -> bool:
    method is_H_series (line 997) | def is_H_series(self) -> bool:
    method is_mpsse (line 1008) | def is_mpsse(self) -> bool:
    method is_mpsse_interface (line 1015) | def is_mpsse_interface(self, interface: int) -> bool:
    method is_bitbang_enabled (line 1030) | def is_bitbang_enabled(self) -> bool:
    method is_eeprom_internal (line 1046) | def is_eeprom_internal(self) -> bool:
    method max_eeprom_size (line 1054) | def max_eeprom_size(self) -> int:
    method frequency_max (line 1068) | def frequency_max(self) -> float:
    method fifo_sizes (line 1076) | def fifo_sizes(self) -> Tuple[int, int]:
    method mpsse_bit_delay (line 1088) | def mpsse_bit_delay(self) -> float:
    method baudrate (line 1100) | def baudrate(self) -> int:
    method usb_dev (line 1106) | def usb_dev(self) -> UsbDevice:
    method set_baudrate (line 1111) | def set_baudrate(self, baudrate: int, constrain: bool = True) -> int:
    method set_frequency (line 1139) | def set_frequency(self, frequency: float) -> float:
    method purge_rx_buffer (line 1154) | def purge_rx_buffer(self) -> None:
    method purge_tx_buffer (line 1165) | def purge_tx_buffer(self) -> None:
    method purge_buffers (line 1171) | def purge_buffers(self) -> None:
    method write_data_set_chunksize (line 1176) | def write_data_set_chunksize(self, chunksize: int = 0) -> None:
    method write_data_get_chunksize (line 1191) | def write_data_get_chunksize(self) -> int:
    method read_data_set_chunksize (line 1198) | def read_data_set_chunksize(self, chunksize: int = 0) -> None:
    method read_data_get_chunksize (line 1225) | def read_data_get_chunksize(self) -> int:
    method set_bitmode (line 1232) | def set_bitmode(self, bitmask: int, mode: 'Ftdi.BitMode') -> None:
    method read_pins (line 1244) | def read_pins(self) -> int:
    method set_cbus_direction (line 1255) | def set_cbus_direction(self, mask: int, direction: int) -> None:
    method get_cbus_gpio (line 1268) | def get_cbus_gpio(self) -> int:
    method set_cbus_gpio (line 1287) | def set_cbus_gpio(self, pins: int) -> None:
    method set_latency_timer (line 1309) | def set_latency_timer(self, latency: int):
    method get_latency_timer (line 1326) | def get_latency_timer(self) -> int:
    method poll_modem_status (line 1336) | def poll_modem_status(self) -> int:
    method modem_status (line 1356) | def modem_status(self) -> Tuple[str, ...]:
    method set_flowctrl (line 1366) | def set_flowctrl(self, flowctrl: str) -> None:
    method set_dtr (line 1423) | def set_dtr(self, state: bool) -> None:
    method set_rts (line 1432) | def set_rts(self, state: bool) -> None:
    method set_dtr_rts (line 1441) | def set_dtr_rts(self, dtr: bool, rts: bool) -> None:
    method set_break (line 1453) | def set_break(self, break_: bool) -> None:
    method set_event_char (line 1468) | def set_event_char(self, eventch: int, enable: bool) -> None:
    method set_error_char (line 1476) | def set_error_char(self, errorch: int, enable: bool) -> None:
    method set_line_property (line 1484) | def set_line_property(self, bits: int, stopbit: Union[int, float],
    method enable_adaptive_clock (line 1549) | def enable_adaptive_clock(self, enable: bool = True) -> None:
    method enable_3phase_clock (line 1564) | def enable_3phase_clock(self, enable: bool = True) -> None:
    method enable_drivezero_mode (line 1583) | def enable_drivezero_mode(self, lines: int) -> None:
    method enable_loopback_mode (line 1602) | def enable_loopback_mode(self, loopback: bool = False) -> None:
    method calc_eeprom_checksum (line 1611) | def calc_eeprom_checksum(self, data: Union[bytes, bytearray]) -> int:
    method read_eeprom (line 1636) | def read_eeprom(self, addr: int = 0, length: Optional[int] = None,
    method write_eeprom (line 1674) | def write_eeprom(self, addr: int, data: Union[bytes, bytearray],
    method overwrite_eeprom (line 1734) | def overwrite_eeprom(self, data: Union[bytes, bytearray],
    method write_data (line 1753) | def write_data(self, data: Union[bytes, bytearray]) -> int:
    method read_data_bytes (line 1783) | def read_data_bytes(self, size: int, attempt: int = 1,
    method read_data (line 1912) | def read_data(self, size: int) -> bytes:
    method get_cts (line 1923) | def get_cts(self) -> bool:
    method get_dsr (line 1931) | def get_dsr(self) -> bool:
    method get_ri (line 1939) | def get_ri(self) -> bool:
    method get_cd (line 1947) | def get_cd(self) -> bool:
    method set_dynamic_latency (line 1955) | def set_dynamic_latency(self, lmin: int, lmax: int,
    method validate_mpsse (line 1996) | def validate_mpsse(self) -> None:
    method get_error_string (line 2008) | def get_error_string(cls) -> str:
    method _set_interface (line 2017) | def _set_interface(self, config: UsbConfiguration, ifnum: int):
    method _reset_usb_device (line 2039) | def _reset_usb_device(self) -> None:
    method _is_pyusb_handle_active (line 2043) | def _is_pyusb_handle_active(self) -> bool:
    method _reset_device (line 2050) | def _reset_device(self):
    method _ctrl_transfer_out (line 2056) | def _ctrl_transfer_out(self, reqtype: int, value: int, data: bytes = b...
    method _ctrl_transfer_in (line 2065) | def _ctrl_transfer_in(self, reqtype: int, length: int):
    method _write (line 2074) | def _write(self, data: Union[bytes, bytearray]) -> int:
    method _read (line 2088) | def _read(self) -> bytes:
    method _adapt_latency (line 2101) | def _adapt_latency(self, payload_detected: bool) -> None:
    method _check_eeprom_size (line 2126) | def _check_eeprom_size(self, eeprom_size: Optional[int]) -> int:
    method _write_eeprom_raw (line 2139) | def _write_eeprom_raw(self, addr: int, data: Union[bytes, bytearray],
    method _get_max_packet_size (line 2179) | def _get_max_packet_size(self) -> int:
    method _convert_baudrate_legacy (line 2189) | def _convert_baudrate_legacy(self, baudrate: int) -> Tuple[int, int, i...
    method _convert_baudrate (line 2210) | def _convert_baudrate(self, baudrate: int) -> Tuple[int, int, int]:
    method _set_baudrate (line 2253) | def _set_baudrate(self, baudrate: int, constrain: bool) -> int:
    method _set_frequency (line 2273) | def _set_frequency(self, frequency: float) -> float:
    method __get_timeouts (line 2320) | def __get_timeouts(self) -> Tuple[int, int]:
    method __set_timeouts (line 2323) | def __set_timeouts(self, timeouts: Tuple[int, int]):

FILE: pyftdi/gpio.py
  class GpioException (line 16) | class GpioException(FtdiError):
  class GpioPort (line 21) | class GpioPort:
  class GpioBaseController (line 26) | class GpioBaseController(GpioPort):
    method __init__ (line 32) | def __init__(self):
    method ftdi (line 40) | def ftdi(self) -> Ftdi:
    method is_connected (line 48) | def is_connected(self) -> bool:
    method configure (line 55) | def configure(self, url: str, direction: int = 0,
    method close (line 78) | def close(self, freeze: bool = False) -> None:
    method get_gpio (line 90) | def get_gpio(self) -> GpioPort:
    method direction (line 101) | def direction(self) -> int:
    method pins (line 110) | def pins(self) -> int:
    method all_pins (line 121) | def all_pins(self) -> int:
    method width (line 132) | def width(self) -> int:
    method frequency (line 140) | def frequency(self) -> float:
    method set_frequency (line 146) | def set_frequency(self, frequency: Union[int, float]) -> None:
    method set_direction (line 154) | def set_direction(self, pins: int, direction: int) -> None:
    method _configure (line 168) | def _configure(self, url: str, direction: int,
    method _update_direction (line 172) | def _update_direction(self) -> None:
  class GpioAsyncController (line 176) | class GpioAsyncController(GpioBaseController):
    method read (line 197) | def read(self, readlen: int = 1, peek: Optional[bool] = None,
    method write (line 254) | def write(self, out: Union[bytes, bytearray, int]) -> None:
    method set_frequency (line 276) | def set_frequency(self, frequency: Union[int, float]) -> None:
    method _configure (line 301) | def _configure(self, url: str, direction: int,
    method _update_direction (line 325) | def _update_direction(self) -> None:
  class GpioSyncController (line 338) | class GpioSyncController(GpioBaseController):
    method exchange (line 357) | def exchange(self, out: Union[bytes, bytearray]) -> bytes:
    method set_frequency (line 380) | def set_frequency(self, frequency: Union[int, float]) -> None:
    method _configure (line 388) | def _configure(self, url: str, direction: int,
    method _update_direction (line 412) | def _update_direction(self) -> None:
  class GpioMpsseController (line 416) | class GpioMpsseController(GpioBaseController):
    method read (line 430) | def read(self, readlen: int = 1, peek: Optional[bool] = None) \
    method write (line 457) | def write(self, out: Union[bytes, bytearray, Iterable[int], int]) -> N...
    method set_frequency (line 477) | def set_frequency(self, frequency: Union[int, float]) -> None:
    method _update_direction (line 482) | def _update_direction(self) -> None:
    method _configure (line 487) | def _configure(self, url: str, direction: int,
    method _read_mpsse (line 498) | def _read_mpsse(self, count: int) -> Tuple[int]:
    method _write_mpsse (line 518) | def _write_mpsse(self,

FILE: pyftdi/i2c.py
  class I2cIOError (line 19) | class I2cIOError(IOError):
  class I2cNackError (line 23) | class I2cNackError(I2cIOError):
  class I2cTimeoutError (line 27) | class I2cTimeoutError(TimeoutError):
  class I2cPort (line 31) | class I2cPort:
    method __init__ (line 54) | def __init__(self, controller: 'I2cController', address: int):
    method configure_register (line 61) | def configure_register(self,
    method shift_address (line 74) | def shift_address(self, offset: int):
    method read (line 80) | def read(self, readlen: int = 0, relax: bool = True,
    method write (line 95) | def write(self, out: Union[bytes, bytearray, Iterable[int]],
    method read_from (line 109) | def read_from(self, regaddr: int, readlen: int = 0,
    method write_to (line 125) | def write_to(self, regaddr: int,
    method exchange (line 141) | def exchange(self, out: Union[bytes, bytearray, Iterable[int]] = b'',
    method poll (line 158) | def poll(self, write: bool = False,
    method poll_cond (line 171) | def poll_cond(self, width: int, mask: int, value: int, count: int,
    method flush (line 199) | def flush(self) -> None:
    method frequency (line 205) | def frequency(self) -> float:
    method address (line 211) | def address(self) -> int:
    method _make_buffer (line 215) | def _make_buffer(self, regaddr: int,
  class I2cGpioPort (line 225) | class I2cGpioPort:
    method __init__ (line 250) | def __init__(self, controller: 'I2cController'):
    method pins (line 255) | def pins(self) -> int:
    method all_pins (line 266) | def all_pins(self) -> int:
    method width (line 277) | def width(self) -> int:
    method direction (line 287) | def direction(self) -> int:
    method read (line 296) | def read(self, with_output: bool = False) -> int:
    method write (line 304) | def write(self, value: int) -> None:
    method set_direction (line 311) | def set_direction(self, pins: int, direction: int) -> None:
  class I2cController (line 325) | class I2cController:
    method __init__ (line 365) | def __init__(self):
    method set_retry_count (line 395) | def set_retry_count(self, count: int) -> None:
    method configure (line 404) | def configure(self, url: Union[str, UsbDevice],
    method force_clock_mode (line 508) | def force_clock_mode(self, enable: bool) -> None:
    method close (line 526) | def close(self, freeze: bool = False) -> None:
    method terminate (line 536) | def terminate(self) -> None:
    method get_port (line 543) | def get_port(self, address: int) -> I2cPort:
    method get_gpio (line 556) | def get_gpio(self) -> I2cGpioPort:
    method ftdi (line 569) | def ftdi(self) -> Ftdi:
    method configured (line 577) | def configured(self) -> bool:
    method validate_address (line 585) | def validate_address(cls, address: Optional[int]) -> None:
    method frequency_max (line 598) | def frequency_max(self) -> float:
    method frequency (line 606) | def frequency(self) -> float:
    method direction (line 614) | def direction(self) -> int:
    method gpio_pins (line 624) | def gpio_pins(self) -> int:
    method gpio_all_pins (line 636) | def gpio_all_pins(self) -> int:
    method width (line 649) | def width(self) -> int:
    method read (line 656) | def read(self, address: Optional[int], readlen: int = 1,
    method write (line 698) | def write(self, address: Optional[int],
    method exchange (line 739) | def exchange(self, address: Optional[int],
    method poll (line 792) | def poll(self, address: int, write: bool = False,
    method poll_cond (line 823) | def poll_cond(self, address: int, fmt: str, mask: int, value: int,
    method flush (line 877) | def flush(self) -> None:
    method read_gpio (line 886) | def read_gpio(self, with_output: bool = False) -> int:
    method write_gpio (line 899) | def write_gpio(self, value: int) -> None:
    method set_gpio_direction (line 916) | def set_gpio_direction(self, pins: int, direction: int) -> None:
    method _set_gpio_direction (line 926) | def _set_gpio_direction(self, width: int, pins: int,
    method _data_lo (line 939) | def _data_lo(self) -> Tuple[int]:
    method _clk_lo_data_hi (line 945) | def _clk_lo_data_hi(self) -> Tuple[int]:
    method _clk_lo_data_input (line 951) | def _clk_lo_data_input(self) -> Tuple[int]:
    method _clk_lo_data_lo (line 957) | def _clk_lo_data_lo(self) -> Tuple[int]:
    method _clk_input_data_input (line 963) | def _clk_input_data_input(self) -> Tuple[int]:
    method _idle (line 969) | def _idle(self) -> Tuple[int]:
    method _start (line 975) | def _start(self) -> Tuple[int]:
    method _stop (line 980) | def _stop(self) -> Tuple[int]:
    method _compute_delay_cycles (line 986) | def _compute_delay_cycles(self, value: Union[int, float]) -> int:
    method _read_raw (line 992) | def _read_raw(self, read_high: bool) -> int:
    method _write_raw (line 1010) | def _write_raw(self, data: int, write_high: bool):
    method _do_prolog (line 1023) | def _do_prolog(self, i2caddress: Optional[int]) -> None:
    method _do_epilog (line 1037) | def _do_epilog(self) -> None:
    method _send_check_ack (line 1047) | def _send_check_ack(self, cmd: bytearray):
    method _i2c_write_data (line 1067) | def _i2c_write_data(self, cmd: bytearray):
    method _i2c_read_data_bytes (line 1073) | def _i2c_read_data_bytes(self, readlen: int, attempt: int = 1,
    method _do_read (line 1083) | def _do_read(self, readlen: int) -> bytearray:
    method _do_write (line 1163) | def _do_write(self, out: Union[bytes, bytearray, Iterable[int]]):

FILE: pyftdi/jtag.py
  class JtagError (line 18) | class JtagError(Exception):
  class JtagState (line 22) | class JtagState:
    method __init__ (line 25) | def __init__(self, name: str, modes: Tuple[str, str]):
    method __str__ (line 30) | def __str__(self):
    method __repr__ (line 33) | def __repr__(self):
    method setx (line 36) | def setx(self, fstate: 'JtagState', tstate: 'JtagState'):
    method getx (line 39) | def getx(self, event):
    method is_of (line 43) | def is_of(self, mode: str) -> bool:
  class JtagStateMachine (line 47) | class JtagStateMachine:
    method __init__ (line 50) | def __init__(self):
    method __getitem__ (line 92) | def __getitem__(self, name: str) -> JtagState:
    method state (line 95) | def state(self) -> JtagState:
    method state_of (line 98) | def state_of(self, mode: str) -> bool:
    method reset (line 101) | def reset(self):
    method find_path (line 104) | def find_path(self, target: Union[JtagState, str],
    method get_events (line 144) | def get_events(cls, path):
    method handle_events (line 157) | def handle_events(self, events):
  class JtagController (line 162) | class JtagController:
    method __init__ (line 174) | def __init__(self, trst: bool = False, frequency: float = 3.0E6):
    method configure (line 190) | def configure(self, url: str) -> None:
    method close (line 198) | def close(self, freeze: bool = False) -> None:
    method purge (line 210) | def purge(self) -> None:
    method reset (line 213) | def reset(self, sync: bool = False) -> None:
    method sync (line 238) | def sync(self) -> None:
    method write_tms (line 245) | def write_tms(self, tms: BitSequence,
    method read (line 268) | def read(self, length: int) -> BitSequence:
    method write (line 281) | def write(self, out: Union[BitSequence, str], use_last: bool = True):
    method write_with_read (line 300) | def write_with_read(self, out: BitSequence,
    method read_from_buffer (line 330) | def read_from_buffer(self, length) -> BitSequence:
    method ftdi (line 360) | def ftdi(self) -> Ftdi:
    method _stack_cmd (line 367) | def _stack_cmd(self, cmd: Union[bytes, bytearray]):
    method _read_bits (line 377) | def _read_bits(self, length: int):
    method _write_bits (line 391) | def _write_bits(self, out: BitSequence) -> None:
    method _read_bytes (line 399) | def _read_bytes(self, length: int) -> BitSequence:
    method _write_bytes (line 413) | def _write_bytes(self, out: BitSequence):
    method _write_bytes_raw (line 423) | def _write_bytes_raw(self, out: BitSequence):
  class JtagEngine (line 432) | class JtagEngine:
    method __init__ (line 435) | def __init__(self, trst: bool = False, frequency: float = 3E06):
    method state_machine (line 441) | def state_machine(self):
    method controller (line 445) | def controller(self):
    method configure (line 448) | def configure(self, url: str) -> None:
    method close (line 452) | def close(self, freeze: bool = False) -> None:
    method purge (line 460) | def purge(self) -> None:
    method reset (line 464) | def reset(self) -> None:
    method write_tms (line 469) | def write_tms(self, out, should_read=False) -> None:
    method read (line 473) | def read(self, length):
    method write (line 477) | def write(self, out, use_last=False) -> None:
    method get_available_statenames (line 481) | def get_available_statenames(self):
    method change_state (line 485) | def change_state(self, statename) -> None:
    method go_idle (line 496) | def go_idle(self) -> None:
    method write_ir (line 500) | def write_ir(self, instruction) -> None:
    method capture_ir (line 506) | def capture_ir(self) -> None:
    method write_dr (line 510) | def write_dr(self, data) -> None:
    method read_dr (line 516) | def read_dr(self, length: int) -> BitSequence:
    method capture_dr (line 523) | def capture_dr(self) -> None:
    method sync (line 527) | def sync(self) -> None:
    method shift_register (line 530) | def shift_register(self, out: BitSequence) -> BitSequence:
    method shift_and_update_register (line 545) | def shift_and_update_register(self, out: BitSequence) -> BitSequence:
  class JtagTool (line 578) | class JtagTool:
    method __init__ (line 581) | def __init__(self, engine):
    method idcode (line 584) | def idcode(self) -> None:
    method preload (line 589) | def preload(self, bsdl, data) -> None:
    method sample (line 595) | def sample(self, bsdl):
    method extest (line 602) | def extest(self, bsdl) -> None:
    method readback (line 606) | def readback(self, bsdl):
    method detect_register_size (line 611) | def detect_register_size(self) -> int:

FILE: pyftdi/misc.py
  function hexdump (line 28) | def hexdump(data: Union[bytes, bytearray, Iterable[int]],
  function hexline (line 77) | def hexline(data: Union[bytes, bytearray, Iterable[int]],
  function to_int (line 104) | def to_int(value: Union[int, str]) -> int:
  function to_bool (line 135) | def to_bool(value: Union[int, bool, str], permissive: bool = True,
  function to_bps (line 166) | def to_bps(value: str) -> int:
  function xor (line 191) | def xor(_a_: bool, _b_: bool) -> bool:
  function is_iterable (line 202) | def is_iterable(obj: Any) -> bool:
  function pretty_size (line 217) | def pretty_size(size, sep: str = ' ',
  function add_custom_devices (line 247) | def add_custom_devices(ftdicls=None,
  function show_call_stack (line 301) | def show_call_stack():
  class classproperty (line 309) | class classproperty(property):
    method __get__ (line 312) | def __get__(self, obj: Any, objtype=None) -> Any:
  class EasyDict (line 316) | class EasyDict(dict):
    method __init__ (line 320) | def __init__(self, dictionary=None, **kwargs):
    method __getattr__ (line 326) | def __getattr__(self, name):
    method __setattr__ (line 333) | def __setattr__(self, name, value):
    method copy (line 337) | def copy(cls, dictionary):
    method mirror (line 347) | def mirror(self) -> 'EasyDict':

FILE: pyftdi/serialext/__init__.py
  function touch (line 30) | def touch():

FILE: pyftdi/serialext/logger.py
  class SerialLogger (line 22) | class SerialLogger:
    method __init__ (line 26) | def __init__(self, *args, **kwargs):
    method open (line 39) | def open(self,):
    method close (line 43) | def close(self):
    method read (line 48) | def read(self, size=1):
    method write (line 53) | def write(self, data):
    method flush (line 58) | def flush(self):
    method reset_input_buffer (line 62) | def reset_input_buffer(self):
    method reset_output_buffer (line 66) | def reset_output_buffer(self):
    method send_break (line 70) | def send_break(self, duration=0.25):
    method _update_break_state (line 74) | def _update_break_state(self):
    method _update_rts_state (line 78) | def _update_rts_state(self):
    method _update_dtr_state (line 82) | def _update_dtr_state(self):
    method cts (line 87) | def cts(self):
    method dsr (line 93) | def dsr(self):
    method ri (line 99) | def ri(self):
    method cd (line 105) | def cd(self):
    method in_waiting (line 110) | def in_waiting(self):
    method _print (line 115) | def _print(self, header, string=None):
    method _log_init (line 124) | def _log_init(self, *args, **kwargs):
    method _log_open (line 132) | def _log_open(self):
    method _log_close (line 138) | def _log_close(self):
    method _log_read (line 144) | def _log_read(self, data):
    method _log_write (line 150) | def _log_write(self, data):
    method _log_flush (line 156) | def _log_flush(self):
    method _log_reset (line 162) | def _log_reset(self, type_):
    method _log_waiting (line 168) | def _log_waiting(self, count):
    method _log_signal (line 174) | def _log_signal(self, name, value):

FILE: pyftdi/serialext/protocol_ftdi.py
  class FtdiSerial (line 20) | class FtdiSerial(SerialBase):
    method open (line 31) | def open(self):
    method close (line 44) | def close(self):
    method read (line 51) | def read(self, size=1):
    method write (line 72) | def write(self, data):
    method flush (line 76) | def flush(self):
    method reset_input_buffer (line 80) | def reset_input_buffer(self):
    method reset_output_buffer (line 84) | def reset_output_buffer(self):
    method send_break (line 89) | def send_break(self, duration=0.25):
    method _update_break_state (line 95) | def _update_break_state(self):
    method _update_rts_state (line 99) | def _update_rts_state(self):
    method _update_dtr_state (line 103) | def _update_dtr_state(self):
    method ftdi (line 108) | def ftdi(self) -> Ftdi:
    method usb_path (line 116) | def usb_path(self):
    method cts (line 128) | def cts(self):
    method dsr (line 133) | def dsr(self):
    method ri (line 138) | def ri(self):
    method cd (line 143) | def cd(self):
    method in_waiting (line 148) | def in_waiting(self):
    method out_waiting (line 154) | def out_waiting(self):
    method fifoSizes (line 159) | def fifoSizes(self):
    method _reconfigure_port (line 163) | def _reconfigure_port(self):
    method _set_open_state (line 184) | def _set_open_state(self, open_):
  class Serial (line 190) | class Serial(FtdiSerial, RawIOBase):
    method __init__ (line 194) | def __init__(self, *args, **kwargs):

FILE: pyftdi/serialext/protocol_unix.py
  class SerialExceptionWithErrno (line 29) | class SerialExceptionWithErrno(SerialException):
    method __init__ (line 32) | def __init__(self, message, errno=None):
  class SocketSerial (line 37) | class SocketSerial(SerialBase):
    method _reconfigure_port (line 48) | def _reconfigure_port(self):
    method open (line 51) | def open(self):
    method close (line 78) | def close(self):
    method in_waiting (line 91) | def in_waiting(self):
    method read (line 95) | def read(self, size=1):
    method write (line 117) | def write(self, data):
    method flush (line 144) | def flush(self):
    method reset_input_buffer (line 148) | def reset_input_buffer(self):
    method reset_output_buffer (line 151) | def reset_output_buffer(self):
    method send_break (line 155) | def send_break(self, duration=0.25):
    method _update_break_state (line 158) | def _update_break_state(self):
    method _update_rts_state (line 161) | def _update_rts_state(self):
    method _update_dtr_state (line 164) | def _update_dtr_state(self):
    method setDTR (line 167) | def setDTR(self, value=1):
    method cts (line 171) | def cts(self):
    method dsr (line 176) | def dsr(self):
    method ri (line 181) | def ri(self):
    method cd (line 186) | def cd(self):
    method nonblocking (line 192) | def nonblocking(self):
    method dump (line 198) | def dump(self, enable):
    method _set_open_state (line 203) | def _set_open_state(self, open_):
  class Serial (line 212) | class Serial(SocketSerial, RawIOBase):

FILE: pyftdi/serialext/tests/rl.py
  function main (line 15) | def main():

FILE: pyftdi/spi.py
  class SpiIOError (line 19) | class SpiIOError(FtdiError):
  class SpiPort (line 23) | class SpiPort:
    method __init__ (line 44) | def __init__(self, controller: 'SpiController', cs: int, cs_hold: int ...
    method exchange (line 53) | def exchange(self, out: Union[bytes, bytearray, Iterable[int]] = b'',
    method read (line 81) | def read(self, readlen: int = 0, start: bool = True, stop: bool = True,
    method write (line 104) | def write(self, out: Union[bytes, bytearray, Iterable[int]],
    method flush (line 126) | def flush(self) -> None:
    method set_frequency (line 130) | def set_frequency(self, frequency: float):
    method set_mode (line 137) | def set_mode(self, mode: int, cs_hold: Optional[int] = None) -> None:
    method force_select (line 163) | def force_select(self, level: Optional[bool] = None,
    method frequency (line 202) | def frequency(self) -> float:
    method cs (line 207) | def cs(self) -> int:
    method mode (line 215) | def mode(self) -> int:
  class SpiGpioPort (line 223) | class SpiGpioPort:
    method __init__ (line 251) | def __init__(self, controller: 'SpiController'):
    method pins (line 256) | def pins(self) -> int:
    method all_pins (line 267) | def all_pins(self) -> int:
    method width (line 278) | def width(self) -> int:
    method direction (line 288) | def direction(self) -> int:
    method read (line 297) | def read(self, with_output: bool = False) -> int:
    method write (line 305) | def write(self, value: int) -> None:
    method set_direction (line 312) | def set_direction(self, pins: int, direction: int) -> None:
  class SpiController (line 321) | class SpiController:
    method __init__ (line 337) | def __init__(self, cs_count: int = 1, turbo: bool = True):
    method configure (line 356) | def configure(self, url: Union[str, UsbDevice],
    method close (line 438) | def close(self, freeze: bool = False) -> None:
    method terminate (line 449) | def terminate(self) -> None:
    method get_port (line 456) | def get_port(self, cs: int, freq: Optional[float] = None,
    method get_gpio (line 483) | def get_gpio(self) -> SpiGpioPort:
    method ftdi (line 496) | def ftdi(self) -> Ftdi:
    method configured (line 504) | def configured(self) -> bool:
    method frequency_max (line 512) | def frequency_max(self) -> float:
    method frequency (line 520) | def frequency(self) -> float:
    method direction (line 528) | def direction(self):
    method channels (line 538) | def channels(self) -> int:
    method active_channels (line 547) | def active_channels(self) -> Set[int]:
    method gpio_pins (line 555) | def gpio_pins(self):
    method gpio_all_pins (line 567) | def gpio_all_pins(self):
    method width (line 580) | def width(self):
    method is_inverted_cpha_supported (line 588) | def is_inverted_cpha_supported(self) -> bool:
    method exchange (line 595) | def exchange(self, frequency: float,
    method force_control (line 639) | def force_control(self, frequency: float, sequence: bytes) -> None:
    method flush (line 649) | def flush(self) -> None:
    method read_gpio (line 655) | def read_gpio(self, with_output: bool = False) -> int:
    method write_gpio (line 668) | def write_gpio(self, value: int) -> None:
    method set_gpio_direction (line 685) | def set_gpio_direction(self, pins: int, direction: int) -> None:
    method _set_gpio_direction (line 695) | def _set_gpio_direction(self, width: int, pins: int,
    method _read_raw (line 707) | def _read_raw(self, read_high: bool) -> int:
    method _write_raw (line 727) | def _write_raw(self, data: int, write_high: bool) -> None:
    method _force (line 742) | def _force(self, frequency: float, sequence: bytes):
    method _exchange_half_duplex (line 760) | def _exchange_half_duplex(self, frequency: float,
    method _exchange_full_duplex (line 869) | def _exchange_full_duplex(self, frequency: float,
    method _flush (line 947) | def _flush(self) -> None:

FILE: pyftdi/term.py
  function call (line 21) | def call():
  class Terminal (line 26) | class Terminal:
    method __init__ (line 141) | def __init__(self):
    method init (line 144) | def init(self, fullterm: bool) -> None:
    method reset (line 165) | def reset(self) -> None:
    method is_term (line 174) | def is_term() -> bool:
    method is_colorterm (line 180) | def is_colorterm() -> bool:
    method getkey (line 187) | def getkey(cls) -> bytes:
    method getch_to_escape (line 206) | def getch_to_escape(cls, char: bytes) -> bytes:

FILE: pyftdi/tests/backend/consts.py
  class UsbConstants (line 17) | class UsbConstants:
    method __init__ (line 33) | def __init__(self):
    method descriptors (line 51) | def descriptors(self):
    method _load_constants (line 55) | def _load_constants(cls, prefix: str, reverse=False):
    method _mask (line 76) | def _mask(cls, mapping: dict) -> int:
    method is_req_out (line 82) | def is_req_out(self, reqtype: int) -> str:
    method dec_req_ctrl (line 85) | def dec_req_ctrl(self, reqtype: int) -> str:
    method dec_req_type (line 88) | def dec_req_type(self, reqtype: int) -> str:
    method dec_req_rcpt (line 91) | def dec_req_rcpt(self, reqtype: int) -> str:
    method dec_req_name (line 94) | def dec_req_name(self, reqtype: int, request: int) -> str:
    method dec_desc_type (line 101) | def dec_desc_type(self, desctype: int) -> str:
  class FtdiConstants (line 105) | class FtdiConstants:
    method __init__ (line 110) | def __init__(self):
    method _load_constants (line 115) | def _load_constants(cls, prefix: str, reverse=False):
    method get_name (line 145) | def get_name(self, prefix: str, value: int) -> str:
    method get_value (line 153) | def get_value(self, prefix: str, name: str) -> str:
    method dec_req_name (line 161) | def dec_req_name(self, request: int) -> str:

FILE: pyftdi/tests/backend/ftdivirt.py
  class Pipe (line 30) | class Pipe:
    method __init__ (line 36) | def __init__(self):
    method close (line 40) | def close(self):
    method read (line 48) | def read(self, count: int) -> bytes:
    method write (line 51) | def write(self, buf: bytes) -> None:
    method ep_out (line 55) | def ep_out(self) -> 'Pipe':
    method ep_in (line 60) | def ep_in(self) -> 'Pipe':
    method __getattr__ (line 64) | def __getattr__(self, name):
  class Fifo (line 77) | class Fifo:
    method __init__ (line 80) | def __init__(self, size: int = 0):
  class VirtualFtdiPin (line 87) | class VirtualFtdiPin:
    class Function (line 91) | class Function(IntEnum):
    method __init__ (line 98) | def __init__(self, port, position):
    method connect_to (line 106) | def connect_to(self, pin: 'VirtualFtdiPin') -> None:
    method disconnect (line 112) | def disconnect(self, pin: 'VirtualFtdiPin') -> None:
    method set_function (line 115) | def set_function(self,
    method set (line 124) | def set(self, source: bool, signal: bool) -> None:
    method read (line 138) | def read(self, count: int) -> bytes:
    method push_to_pin (line 146) | def push_to_pin(self, buf: bytes) -> None:
    method write (line 156) | def write(self, buf: bytes) -> None:
    method port (line 165) | def port(self) -> 'VirtFtdiPort':
    method connection (line 169) | def connection(self) -> Optional['VirtualFtdiPin']:
    method is_connected (line 173) | def is_connected(self) -> bool:
    method position (line 177) | def position(self) -> int:
    method direction (line 181) | def direction(self) -> bool:
    method signal (line 185) | def signal(self) -> Optional[bool]:
  class VirtFtdiPort (line 193) | class VirtFtdiPort:
    class Fifos (line 204) | class Fifos(NamedTuple):
    class BitMode (line 209) | class BitMode(IntEnum):
    class Command (line 257) | class Command(IntEnum):
    method __init__ (line 263) | def __init__(self, parent: 'VirtFtdi', iface: int):
    method close (line 301) | def close(self, freeze: bool = False) -> None:
    method terminate (line 321) | def terminate(self):
    method __getitem__ (line 324) | def __getitem__(self, index: int) -> VirtualFtdiPin:
    method iface (line 334) | def iface(self) -> int:
    method baudrate (line 338) | def baudrate(self) -> int:
    method width (line 342) | def width(self) -> int:
    method direction (line 346) | def direction(self) -> int:
    method gpio (line 350) | def gpio(self) -> int:
    method cbus (line 354) | def cbus(self) -> Tuple[int, int]:
    method cbus (line 363) | def cbus(self, cbus: int) -> None:
    method write (line 367) | def write(self, data: array, timeout: int) -> int:
    method read (line 385) | def read(self, buff: array, timeout: int) -> int:
    method set_io (line 408) | def set_io(self, pin: VirtualFtdiPin, signal: bool) -> None:
    method update_gpio (line 417) | def update_gpio(self, mpsse: VirtMpsseEngine, source: bool,
    method modem_status (line 423) | def modem_status(self) -> Tuple[int, int]:
    method control_reset (line 461) | def control_reset(self, wValue: int, wIndex: int,
    method control_set_bitmode (line 489) | def control_set_bitmode(self, wValue: int, wIndex: int,
    method control_set_latency_timer (line 546) | def control_set_latency_timer(self, wValue: int, wIndex: int,
    method control_set_event_char (line 550) | def control_set_event_char(self, wValue: int, wIndex: int,
    method control_set_error_char (line 557) | def control_set_error_char(self, wValue: int, wIndex: int,
    method control_read_pins (line 564) | def control_read_pins(self, wValue: int, wIndex: int,
    method control_set_baudrate (line 576) | def control_set_baudrate(self, wValue: int, wIndex: int,
    method control_set_data (line 607) | def control_set_data(self, wValue: int, wIndex: int,
    method control_set_flow_ctrl (line 611) | def control_set_flow_ctrl(self, wValue: int, wIndex: int,
    method control_poll_modem_status (line 615) | def control_poll_modem_status(self, wValue: int, wIndex: int,
    method write_from_pin (line 622) | def write_from_pin(self, pin: int, buf: bytes) -> None:
    method write_from_mpsse (line 631) | def write_from_mpsse(self, mpsse: VirtMpsseEngine, buf: bytes) -> None:
    method _update_gpio (line 642) | def _update_gpio(self, source: bool, gpio: int) -> None:
    method _decode_cbus_x1000 (line 656) | def _decode_cbus_x1000(self) -> None:
    method _decode_cbus_x0900 (line 681) | def _decode_cbus_x0900(self) -> None:
    method _decode_cbus_x0600 (line 718) | def _decode_cbus_x0600(self) -> None:
    method _cbus_write (line 745) | def _cbus_write(self, cbus: int) -> None:
    method _cbus_read (line 769) | def _cbus_read(self) -> Tuple[int, int]:
    method _rx_worker (line 793) | def _rx_worker(self):
    method _tx_worker (line 866) | def _tx_worker(self):
    method _tx_worker_generate (line 914) | def _tx_worker_generate(self, bitmode) -> None:
  class VirtFtdi (line 934) | class VirtFtdi:
    class Properties (line 941) | class Properties(NamedTuple):
    method __init__ (line 976) | def __init__(self, version: int, bus: int, address: int,
    method close (line 987) | def close(self, freeze: bool = False) -> None:
    method terminate (line 991) | def terminate(self):
    method version (line 995) | def version(self) -> int:
    method bus (line 999) | def bus(self) -> int:
    method address (line 1003) | def address(self) -> int:
    method properties (line 1007) | def properties(self) -> 'VirtFtdi.Properties':
    method is_mpsse_device (line 1011) | def is_mpsse_device(self) -> bool:
    method is_hispeed_device (line 1019) | def is_hispeed_device(self) -> bool:
    method is_x_series (line 1027) | def is_x_series(self) -> bool:
    method apply_eeprom_config (line 1034) | def apply_eeprom_config(self, devdesc: dict,
    method control (line 1038) | def control(self, dev_handle: 'VirtDeviceHandle', bmRequestType: int,
    method write (line 1075) | def write(self, dev_handle: 'VirtDeviceHandle', ep: int, intf: int,
    method read (line 1079) | def read(self, dev_handle: 'VirtDeviceHandle', ep: int, intf: int,
    method get_port (line 1083) | def get_port(self, iface: int):
    method eeprom (line 1088) | def eeprom(self) -> bytes:
    method eeprom (line 1092) | def eeprom(self, value: bytes):
    method _build_eeprom (line 1098) | def _build_eeprom(cls, version, eeprom: Optional[dict]) -> bytearray:
    method _checksum_eeprom (line 1130) | def _checksum_eeprom(self, data: bytearray) -> int:
    method _load_eeprom (line 1147) | def _load_eeprom(self, devdesc: dict, cfgdescs: Sequence[dict]) -> None:
    method _decode_eeprom_string (line 1173) | def _decode_eeprom_string(self, offset):
    method _control_read_eeprom (line 1182) | def _control_read_eeprom(self, wValue: int, wIndex: int,
    method _control_write_eeprom (line 1199) | def _control_write_eeprom(self, wValue: int, wIndex: int,
    method _control_set_baudrate (line 1217) | def _control_set_baudrate(self, wValue: int, wIndex: int,

FILE: pyftdi/tests/backend/loader.py
  class VirtLoader (line 27) | class VirtLoader:
    method __init__ (line 31) | def __init__(self):
    method load (line 36) | def load(self, yamlfp: BinaryIO) -> None:
    method unload (line 52) | def unload(self) -> None:
    method get_virtual_ftdi (line 65) | def get_virtual_ftdi(self, bus, address):
    method eeprom_backup (line 69) | def eeprom_backup(self) -> bytes:
    method _validate (line 74) | def _validate(self):
    method _build_root (line 104) | def _build_root(self, backend, container):
    method _build_device (line 121) | def _build_device(self, container):
    method _build_device_descriptor (line 190) | def _build_device_descriptor(self, container) -> dict:
    method _build_configuration (line 213) | def _build_configuration(self, container):
    method _build_config_descriptor (line 238) | def _build_config_descriptor(self, container) -> dict:
    method _build_interfaces (line 269) | def _build_interfaces(self, container):
    method _build_alternative (line 300) | def _build_alternative(self, container):
    method _build_interface_descriptor (line 326) | def _build_interface_descriptor(self, container) -> dict:
    method _build_endpoint (line 342) | def _build_endpoint(self, container):
    method _build_endpoint_descriptor (line 358) | def _build_endpoint_descriptor(self, container) -> dict:

FILE: pyftdi/tests/backend/mpsse.py
  class VirtMpsseTracer (line 17) | class VirtMpsseTracer(FtdiMpsseTracer):
    method __init__ (line 21) | def __init__(self, port: 'VirtFtdiPort', version: int):
    method _get_engine (line 26) | def _get_engine(self, iface: int):
  class VirtMpsseEngine (line 38) | class VirtMpsseEngine(FtdiMpsseEngine):
    method __init__ (line 44) | def __init__(self, tracer: VirtMpsseTracer, port: 'VirtFtdiPort'):
    method send (line 53) | def send(self, buf: Union[bytes, bytearray]) -> None:
    method reply (line 60) | def reply(self) -> None:
    method _cmd_get_bits_low (line 65) | def _cmd_get_bits_low(self):
    method _cmd_get_bits_high (line 72) | def _cmd_get_bits_high(self):
    method _cmd_set_bits_low (line 79) | def _cmd_set_bits_low(self):
    method _cmd_set_bits_high (line 93) | def _cmd_set_bits_high(self):

FILE: pyftdi/tests/backend/usbvirt.py
  class VirtEndpoint (line 31) | class VirtEndpoint:
    method __init__ (line 37) | def __init__(self, defs: dict,
    method build_strings (line 55) | def build_strings(self, func):
    method get_length (line 58) | def get_length(self) -> int:
    method __getattr__ (line 61) | def __getattr__(self, name):
  class VirtInterface (line 65) | class VirtInterface:
    method __init__ (line 71) | def __init__(self, defs: dict, extra: Optional[bytes] = None):
    method add_endpoint (line 92) | def add_endpoint(self, endpoint: VirtEndpoint):
    method update_number (line 97) | def update_number(self, number: int) -> None:
    method endpoints (line 101) | def endpoints(self):
    method build_strings (line 104) | def build_strings(self, func):
    method add_bulk_pair (line 111) | def add_bulk_pair(self):
    method get_length (line 126) | def get_length(self) -> int:
    method num_altsetting (line 135) | def num_altsetting(self):
    method __getitem__ (line 138) | def __getitem__(self, item):
    method __getattr__ (line 143) | def __getattr__(self, name):
  class VirtConfiguration (line 147) | class VirtConfiguration:
    method __init__ (line 153) | def __init__(self, defs: dict, extra: Optional[bytes] = None):
    method add_interface (line 171) | def add_interface(self, interface: VirtInterface):
    method build_strings (line 176) | def build_strings(self, func):
    method update (line 181) | def update(self):
    method get_length (line 186) | def get_length(self) -> int:
    method __getattr__ (line 192) | def __getattr__(self, name):
  class VirtDevice (line 196) | class VirtDevice:
    method __init__ (line 204) | def __init__(self, defs: dict, **kwargs):
    method close (line 242) | def close(self, freeze: bool = False):
    method terminate (line 245) | def terminate(self):
    method add_configuration (line 248) | def add_configuration(self, config: VirtConfiguration):
    method build (line 253) | def build(self):
    method build_strings (line 257) | def build_strings(self, func):
    method ftdi (line 263) | def ftdi(self) -> VirtFtdi:
    method _store_strings (line 267) | def _store_strings(obj, desc, ignore=None):
    method get_string (line 276) | def get_string(self, type_: int, index: int) -> str:
    method __getattr__ (line 298) | def __getattr__(self, name):
  class VirtDeviceHandle (line 302) | class VirtDeviceHandle(EasyDict):
    method __init__ (line 306) | def __init__(self, dev, handle):
  class VirtBackend (line 312) | class VirtBackend(IBackend):
    method __init__ (line 319) | def __init__(self):
    method add_device (line 325) | def add_device(self, device: VirtDevice):
    method flush_devices (line 328) | def flush_devices(self):
    method create_loader (line 334) | def create_loader(cls) -> 'VirtLoader':
    method devices (line 349) | def devices(self) -> List[VirtDevice]:
    method get_virtual_ftdi (line 352) | def get_virtual_ftdi(self, bus: int, address: int) -> VirtFtdi:
    method enumerate_devices (line 358) | def enumerate_devices(self) -> VirtDevice:
    method open_device (line 361) | def open_device(self, dev: VirtDevice) -> VirtDeviceHandle:
    method close_device (line 367) | def close_device(self, dev_handle: VirtDeviceHandle) -> None:
    method claim_interface (line 370) | def claim_interface(self, dev_handle: VirtDeviceHandle, intf: int) \
    method release_interface (line 375) | def release_interface(self, dev_handle: VirtDeviceHandle, intf: int) \
    method get_configuration (line 380) | def get_configuration(self,
    method set_configuration (line 385) | def set_configuration(self, dev_handle: VirtDeviceHandle,
    method get_device_descriptor (line 390) | def get_device_descriptor(self, dev: VirtDevice) -> EasyDict:
    method get_configuration_descriptor (line 393) | def get_configuration_descriptor(self, dev: VirtDevice, config: int) \
    method get_interface_descriptor (line 397) | def get_interface_descriptor(self, dev: VirtDevice,
    method get_endpoint_descriptor (line 404) | def get_endpoint_descriptor(self, dev: VirtDevice,
    method ctrl_transfer (line 413) | def ctrl_transfer(self,
    method bulk_write (line 432) | def bulk_write(self, dev_handle: VirtDeviceHandle, ep: int, intf: int,
    method bulk_read (line 440) | def bulk_read(self, dev_handle: VirtDeviceHandle, ep: int, intf: int,
    method _ctrl_standard (line 447) | def _ctrl_standard(self,
  function get_backend (line 486) | def get_backend(*_):

FILE: pyftdi/tests/bits.py
  class BitSequenceTestCase (line 22) | class BitSequenceTestCase(TestCase):
    method setUp (line 24) | def setUp(self):
    method test_bitwise_ops (line 40) | def test_bitwise_ops(self):
    method test_cmp (line 67) | def test_cmp(self):
    method test_representation (line 83) | def test_representation(self):
    method test_init (line 103) | def test_init(self):
    method test_conversion (line 152) | def test_conversion(self):
    method test_misc (line 163) | def test_misc(self):
    method test_rotations (line 196) | def test_rotations(self):
    method test_concat (line 205) | def test_concat(self):
  function suite (line 213) | def suite():

FILE: pyftdi/tests/cbus.py
  class CbusOutputGpioTestCase (line 23) | class CbusOutputGpioTestCase(TestCase):
    method setUpClass (line 27) | def setUpClass(cls):
    method test_gpio (line 31) | def test_gpio(self):
  class CbusInputGpioTestCase (line 65) | class CbusInputGpioTestCase(TestCase):
    method setUpClass (line 69) | def setUpClass(cls):
    method test_gpio (line 73) | def test_gpio(self):
  function suite (line 107) | def suite():
  function main (line 120) | def main():

FILE: pyftdi/tests/eeprom.py
  class EepromTestCase (line 24) | class EepromTestCase(TestCase):
    method setUpClass (line 28) | def setUpClass(cls):
    method setUp (line 33) | def setUp(self):
    method tearDown (line 49) | def tearDown(self):
    method test_eeprom_read (line 53) | def test_eeprom_read(self):
    method test_eeprom_write_reject (line 79) | def test_eeprom_write_reject(self):
    method test_eeprom_write (line 92) | def test_eeprom_write(self):
  function suite (line 98) | def suite():
  function main (line 104) | def main():

FILE: pyftdi/tests/eeprom_mock.py
  class FtdiTestCase (line 26) | class FtdiTestCase:
    method setUpClass (line 37) | def setUpClass(cls):
    method tearDownClass (line 43) | def tearDownClass(cls):
    method setUp (line 47) | def setUp(self):
  class EepromMirrorTestCase (line 51) | class EepromMirrorTestCase(FtdiTestCase):
    method setUpClass (line 60) | def setUpClass(cls):
    method test_mirror_properties (line 72) | def test_mirror_properties(self):
    method test_mirror_manufacturer (line 95) | def test_mirror_manufacturer(self):
    method test_mirror_product (line 107) | def test_mirror_product(self):
    method test_mirror_serial (line 119) | def test_mirror_serial(self):
    method test_varstr_combinations (line 131) | def test_varstr_combinations(self):
    method test_compute_size_detects_mirror (line 165) | def test_compute_size_detects_mirror(self):
    method _check_for_mirrored_eeprom_contents (line 188) | def _check_for_mirrored_eeprom_contents(self, eeprom: FtdiEeprom):
  class NonMirroredEepromTestCase (line 201) | class NonMirroredEepromTestCase(FtdiTestCase):
    method setUpClass (line 210) | def setUpClass(cls):
    method test_mirror_properties (line 222) | def test_mirror_properties(self):
    method test_no_mirror_manufacturer (line 251) | def test_no_mirror_manufacturer(self):
    method test_no_mirror_product (line 262) | def test_no_mirror_product(self):
    method test_mirror_serial (line 273) | def test_mirror_serial(self):
    method test_varstr_combinations (line 284) | def test_varstr_combinations(self):
    method test_compute_size_does_not_mirror (line 318) | def test_compute_size_does_not_mirror(self):
    method _check_for_non_mirrored_eeprom_contents (line 339) | def _check_for_non_mirrored_eeprom_contents(self, eeprom: FtdiEeprom):
  class EepromMirrorFt232hTestCase (line 350) | class EepromMirrorFt232hTestCase(EepromMirrorTestCase, TestCase):
  class EepromMirrorFt2232hTestCase (line 354) | class EepromMirrorFt2232hTestCase(EepromMirrorTestCase, TestCase):
  class EepromMirrorFt4232hTestCase (line 358) | class EepromMirrorFt4232hTestCase(EepromMirrorTestCase, TestCase):
  class EepromMirrorFt232rTestCase (line 362) | class EepromMirrorFt232rTestCase(NonMirroredEepromTestCase, TestCase):
  class EepromMirrorFt230xTestCase (line 367) | class EepromMirrorFt230xTestCase(NonMirroredEepromTestCase, TestCase):
  class EepromNonMirroredFt232hTestCase (line 372) | class EepromNonMirroredFt232hTestCase(NonMirroredEepromTestCase, TestCase):
  class EepromNonMirroredFt2232hTestCase (line 377) | class EepromNonMirroredFt2232hTestCase(NonMirroredEepromTestCase, TestCa...
  class EepromNonMirroredFt4232hTestCase (line 382) | class EepromNonMirroredFt4232hTestCase(NonMirroredEepromTestCase, TestCa...
  function suite (line 387) | def suite():
  function virtualize (line 409) | def virtualize():
  function setup_module (line 427) | def setup_module():
  function main (line 449) | def main():

FILE: pyftdi/tests/ftdi.py
  class FtdiTestCase (line 24) | class FtdiTestCase(TestCase):
    method test_multiple_interface (line 27) | def test_multiple_interface(self):
  class HotplugTestCase (line 42) | class HotplugTestCase(TestCase):
    method test_hotplug_discovery (line 44) | def test_hotplug_discovery(self):
  class ResetTestCase (line 63) | class ResetTestCase(TestCase):
    method test_simple_reset (line 65) | def test_simple_reset(self):
    method test_dual_if_reset (line 79) | def test_dual_if_reset(self):
  class DisconnectTestCase (line 115) | class DisconnectTestCase(TestCase):
    method test_close_on_disconnect (line 119) | def test_close_on_disconnect(self):
  function suite (line 153) | def suite():

FILE: pyftdi/tests/gpio.py
  class FtdiTestCase (line 31) | class FtdiTestCase(TestCase):
    method setUpClass (line 36) | def setUpClass(cls):
    method tearDownClass (line 42) | def tearDownClass(cls):
    method setUp (line 46) | def setUp(self):
  class GpioAsyncTestCase (line 51) | class GpioAsyncTestCase(FtdiTestCase):
    method setUpClass (line 72) | def setUpClass(cls):
    method test_gpio_freeze (line 100) | def test_gpio_freeze(self):
    method test_gpio_values (line 134) | def test_gpio_values(self):
    method test_gpio_initial (line 177) | def test_gpio_initial(self):
    method test_gpio_loopback (line 195) | def test_gpio_loopback(self):
    method test_gpio_baudate (line 230) | def test_gpio_baudate(self):
  class GpioSyncTestCase (line 255) | class GpioSyncTestCase(FtdiTestCase):
    method setUpClass (line 276) | def setUpClass(cls):
    method test_gpio_values (line 304) | def test_gpio_values(self):
    method test_gpio_baudate (line 332) | def test_gpio_baudate(self):
  class GpioMultiportTestCase (line 355) | class GpioMultiportTestCase(FtdiTestCase):
    method setUpClass (line 377) | def setUpClass(cls):
    method test_gpio_peek (line 403) | def test_gpio_peek(self):
    method test_gpio_stream (line 420) | def test_gpio_stream(self):
    method test_direction (line 456) | def test_direction(self):
  class GpioMpsseTestCase (line 488) | class GpioMpsseTestCase(FtdiTestCase):
    method setUpClass (line 517) | def setUpClass(cls):
    method test_default_gpio (line 548) | def test_default_gpio(self):
    method test_peek_gpio (line 567) | def test_peek_gpio(self):
    method test_stream_gpio (line 586) | def test_stream_gpio(self):
  function suite (line 608) | def suite():
  function virtualize (line 614) | def virtualize():
  function setup_module (line 631) | def setup_module():
  function main (line 653) | def main():

FILE: pyftdi/tests/i2c.py
  class I2cTca9555TestCase (line 26) | class I2cTca9555TestCase(TestCase):
    method test (line 30) | def test(self):
    method _open (line 37) | def _open(self):
    method _read_it (line 42) | def _read_it(self):
    method _write_it (line 46) | def _write_it(self):
    method _close (line 52) | def _close(self):
  class I2cAccelTestCase (line 57) | class I2cAccelTestCase(TestCase):
    method test (line 61) | def test(self):
    method _open (line 67) | def _open(self):
    method _read_device_id (line 72) | def _read_device_id(self):
    method _close (line 79) | def _close(self):
  class I2cReadTestCase (line 84) | class I2cReadTestCase(TestCase):
    method test (line 88) | def test(self):
    method _open (line 94) | def _open(self):
    method _read (line 99) | def _read(self):
    method _close (line 106) | def _close(self):
  class I2cEepromTestCase (line 111) | class I2cEepromTestCase(TestCase):
    method setUpClass (line 117) | def setUpClass(cls):
    method setUp (line 122) | def setUp(self):
    method tearDown (line 127) | def tearDown(self):
    method test_short (line 130) | def test_short(self):
    method test_long (line 139) | def test_long(self):
  class I2cReadGpioTestCase (line 154) | class I2cReadGpioTestCase(TestCase):
    method test (line 171) | def test(self):
    method _open (line 178) | def _open(self):
    method _execute_sequence (line 191) | def _execute_sequence(self):
    method _execute_interleave (line 207) | def _execute_interleave(self):
    method _close (line 223) | def _close(self):
  class I2cClockStrechingGpioTestCase (line 228) | class I2cClockStrechingGpioTestCase(TestCase):
    method test (line 233) | def test(self):
  class I2cDualMasterTestCase (line 241) | class I2cDualMasterTestCase(TestCase):
    method test (line 246) | def test(self):
  class I2cIssue143TestCase (line 258) | class I2cIssue143TestCase(TestCase):
    method test (line 262) | def test(self):
  function suite (line 280) | def suite():
  function main (line 305) | def main():

FILE: pyftdi/tests/jtag.py
  class JtagTestCase (line 27) | class JtagTestCase(TestCase):
    method setUp (line 29) | def setUp(self):
    method tearDown (line 36) | def tearDown(self):
    method test_idcode_reset (line 39) | def test_idcode_reset(self):
    method test_idcode_sequence (line 46) | def test_idcode_sequence(self):
    method test_idcode_shift_register (line 54) | def test_idcode_shift_register(self):
    method test_bypass_shift_register (line 67) | def test_bypass_shift_register(self):
    method _test_detect_ir_length (line 81) | def _test_detect_ir_length(self):
  function suite (line 88) | def suite():

FILE: pyftdi/tests/mockusb.py
  class FtdiTestCase (line 35) | class FtdiTestCase(TestCase):
    method setUpClass (line 40) | def setUpClass(cls):
    method tearDownClass (line 45) | def tearDownClass(cls):
    method setUp (line 49) | def setUp(self):
  class MockUsbToolsTestCase (line 54) | class MockUsbToolsTestCase(FtdiTestCase):
    method setUpClass (line 59) | def setUpClass(cls):
    method test_enumerate (line 64) | def test_enumerate(self):
    method test_device (line 71) | def test_device(self):
    method test_string (line 82) | def test_string(self):
    method test_list_devices (line 91) | def test_list_devices(self):
  class MockFtdiDiscoveryTestCase (line 132) | class MockFtdiDiscoveryTestCase(FtdiTestCase):
    method setUpClass (line 138) | def setUpClass(cls):
    method test_list_devices (line 143) | def test_list_devices(self):
  class MockSimpleDeviceTestCase (line 175) | class MockSimpleDeviceTestCase(FtdiTestCase):
    method setUpClass (line 180) | def setUpClass(cls):
    method test_enumerate (line 185) | def test_enumerate(self):
  class MockDualDeviceTestCase (line 201) | class MockDualDeviceTestCase(FtdiTestCase):
    method setUpClass (line 206) | def setUpClass(cls):
    method test_enumerate (line 211) | def test_enumerate(self):
  class MockTwoPortDeviceTestCase (line 228) | class MockTwoPortDeviceTestCase(FtdiTestCase):
    method setUpClass (line 233) | def setUpClass(cls):
    method test_enumerate (line 238) | def test_enumerate(self):
  class MockFourPortDeviceTestCase (line 255) | class MockFourPortDeviceTestCase(FtdiTestCase):
    method setUpClass (line 260) | def setUpClass(cls):
    method test_enumerate (line 265) | def test_enumerate(self):
  class MockManyDevicesTestCase (line 282) | class MockManyDevicesTestCase(FtdiTestCase):
    method setUpClass (line 287) | def setUpClass(cls):
    method test_enumerate (line 292) | def test_enumerate(self):
  class MockSimpleDirectTestCase (line 322) | class MockSimpleDirectTestCase(FtdiTestCase):
    method setUpClass (line 327) | def setUpClass(cls):
    method test_open_close (line 332) | def test_open_close(self):
    method test_open_bitbang (line 339) | def test_open_bitbang(self):
    method test_open_mpsse (line 345) | def test_open_mpsse(self):
  class MockSimpleMpsseTestCase (line 353) | class MockSimpleMpsseTestCase(FtdiTestCase):
    method setUpClass (line 358) | def setUpClass(cls):
    method test_open_close (line 363) | def test_open_close(self):
    method test_open_bitbang (line 370) | def test_open_bitbang(self):
    method test_open_mpsse (line 376) | def test_open_mpsse(self):
  class MockSimpleGpioTestCase (line 383) | class MockSimpleGpioTestCase(FtdiTestCase):
    method tearDown (line 387) | def tearDown(self):
    method _test_gpio (line 390) | def _test_gpio(self):
    method test_baudrate (line 409) | def test_baudrate(self):
  class MockSimpleUartTestCase (line 429) | class MockSimpleUartTestCase(FtdiTestCase):
    method tearDown (line 433) | def tearDown(self):
    method test_uart (line 436) | def test_uart(self):
    method test_uart_loopback (line 456) | def test_uart_loopback(self):
    method test_baudrate_fs_dev (line 473) | def test_baudrate_fs_dev(self):
    method test_baudrate_hs_dev (line 490) | def test_baudrate_hs_dev(self):
  class MockRawExtEepromTestCase (line 508) | class MockRawExtEepromTestCase(FtdiTestCase):
    method setUpClass (line 513) | def setUpClass(cls):
    method _restore_eeprom (line 518) | def _restore_eeprom(self, ftdi):
    method test_dump (line 527) | def test_dump(self):
    method test_random_access_read (line 539) | def test_random_access_read(self):
    method test_randow_access_write (line 564) | def test_randow_access_write(self):
  class MockRawIntEepromTestCase (line 607) | class MockRawIntEepromTestCase(FtdiTestCase):
    method setUpClass (line 612) | def setUpClass(cls):
    method test_descriptor_update (line 617) | def test_descriptor_update(self):
    method test_eeprom_read (line 628) | def test_eeprom_read(self):
  class MockCBusEepromTestCase (line 637) | class MockCBusEepromTestCase(FtdiTestCase):
    method tearDown (line 641) | def tearDown(self):
    method test_ft230x (line 644) | def test_ft230x(self):
    method test_ft232h (line 685) | def test_ft232h(self):
  class MockCbusGpioTestCase (line 717) | class MockCbusGpioTestCase(FtdiTestCase):
    method tearDown (line 721) | def tearDown(self):
    method test_230x (line 724) | def test_230x(self):
    method test_lc231x (line 771) | def test_lc231x(self):
  function suite (line 818) | def suite():
  function setup_module (line 824) | def setup_module():
  function main (line 853) | def main():

FILE: pyftdi/tests/spi.py
  class SpiDataFlashTest (line 24) | class SpiDataFlashTest:
    method __init__ (line 29) | def __init__(self):
    method open (line 32) | def open(self):
    method read_jedec_id (line 38) | def read_jedec_id(self):
    method close (line 45) | def close(self):
  class SpiAccelTest (line 50) | class SpiAccelTest:
    method __init__ (line 55) | def __init__(self):
    method open (line 58) | def open(self):
    method read_device_id (line 64) | def read_device_id(self):
    method close (line 71) | def close(self):
  class SpiRfda2125Test (line 76) | class SpiRfda2125Test:
    method __init__ (line 82) | def __init__(self):
    method open (line 86) | def open(self):
    method change_attenuation (line 93) | def change_attenuation(self, value):
    method close (line 99) | def close(self):
  class SpiTestCase (line 104) | class SpiTestCase(TestCase):
    method test_spi1 (line 117) | def test_spi1(self):
    method _test_spi2 (line 124) | def _test_spi2(self):
    method _test_spi3 (line 131) | def _test_spi3(self):
  class SpiGpioTestCase (line 146) | class SpiGpioTestCase(TestCase):
    method setUpClass (line 163) | def setUpClass(cls):
    method setUp (line 167) | def setUp(self):
    method tearDown (line 173) | def tearDown(self):
    method test_ac_to_ad (line 177) | def test_ac_to_ad(self):
    method test_ad_to_ac (line 198) | def test_ad_to_ac(self):
  class SpiUnalignedTestCase (line 220) | class SpiUnalignedTestCase(TestCase):
    method setUpClass (line 229) | def setUpClass(cls):
    method setUp (line 233) | def setUp(self):
    method tearDown (line 238) | def tearDown(self):
    method test_invalid_write (line 242) | def test_invalid_write(self):
    method test_bit_write (line 246) | def test_bit_write(self):
    method test_bytebit_write (line 251) | def test_bytebit_write(self):
    method test_invalid_read (line 256) | def test_invalid_read(self):
    method test_bit_read (line 260) | def test_bit_read(self):
    method test_bytebit_read (line 272) | def test_bytebit_read(self):
    method test_invalid_duplex (line 283) | def test_invalid_duplex(self):
    method test_bit_duplex (line 294) | def test_bit_duplex(self):
    method test_bytebit_duplex (line 303) | def test_bytebit_duplex(self):
  class SpiCsForceTestCase (line 314) | class SpiCsForceTestCase(TestCase):
    method setUpClass (line 322) | def setUpClass(cls):
    method setUp (line 326) | def setUp(self):
    method tearDown (line 331) | def tearDown(self):
    method test_cs_default_pulse (line 335) | def test_cs_default_pulse(self):
    method test_cs_long_pulse (line 339) | def test_cs_long_pulse(self):
    method test_cs_manual_pulse (line 343) | def test_cs_manual_pulse(self):
    method test_cs_pulse_write (line 352) | def test_cs_pulse_write(self):
    method test_cs_default_pulse_rev_clock (line 356) | def test_cs_default_pulse_rev_clock(self):
  function suite (line 364) | def suite():
  function main (line 376) | def main():

FILE: pyftdi/tests/toolsimport.py
  class ToolsTestCase (line 19) | class ToolsTestCase(TestCase):
    method setUpClass (line 29) | def setUpClass(cls):
    method test_ftconf (line 33) | def test_ftconf(self):
    method test_i2cscan (line 39) | def test_i2cscan(self):
    method test_pyterm (line 45) | def test_pyterm(self):
    method test_ftdi_urls (line 51) | def test_ftdi_urls(self):
  function suite (line 58) | def suite():
  function main (line 64) | def main():

FILE: pyftdi/tests/uart.py
  class FtdiTestCase (line 37) | class FtdiTestCase(TestCase):
    method setUpClass (line 42) | def setUpClass(cls):
    method setUp (line 46) | def setUp(self):
  class UartTestCase (line 51) | class UartTestCase(FtdiTestCase):
    method setUpClass (line 69) | def setUpClass(cls):
    method test_uart_cross_talk_sp (line 74) | def test_uart_cross_talk_sp(self):
    method test_uart_cross_talk_mp (line 111) | def test_uart_cross_talk_mp(self):
    method test_uart_loopback (line 136) | def test_uart_loopback(self):
    method test2_uart_cross_talk_speed (line 159) | def test2_uart_cross_talk_speed(self):
    method test_loopback_talk_speed (line 193) | def test_loopback_talk_speed(self):
    method _stream_source (line 224) | def _stream_source(cls, port, chunk, size, results):
    method _stream_sink (line 239) | def _stream_sink(cls, port, size, results):
    method _cross_talk_write_then_read (line 272) | def _cross_talk_write_then_read(cls, url, refstream):
    method _cross_talk_read_then_write (line 293) | def _cross_talk_read_then_write(cls, url, refstream):
    method generate_bytes (line 314) | def generate_bytes(cls, count=0):
    method build_next_url (line 319) | def build_next_url(cls, url):
  class BaudrateTestCase (line 325) | class BaudrateTestCase(FtdiTestCase):
    method test (line 335) | def test(self):
  function suite (line 344) | def suite():
  function main (line 350) | def main():

FILE: pyftdi/tracer.py
  class FtdiMpsseTracer (line 21) | class FtdiMpsseTracer:
    method __init__ (line 36) | def __init__(self, version):
    method send (line 40) | def send(self, iface: int, buf: Union[bytes, bytearray]) -> None:
    method receive (line 43) | def receive(self, iface: int, buf: Union[bytes, bytearray]) -> None:
    method _get_engine (line 46) | def _get_engine(self, iface: int):
  class FtdiMpsseEngine (line 58) | class FtdiMpsseEngine:
    method __init__ (line 69) | def __init__(self, iface: int):
    method send (line 82) | def send(self, buf: Union[bytes, bytearray]) -> None:
    method receive (line 121) | def receive(self, buf: Union[bytes, bytearray]) -> None:
    method _build_commands (line 149) | def _build_commands(cls):
    method _cmd_enable_clk_div5 (line 170) | def _cmd_enable_clk_div5(self):
    method _cmd_disable_clk_div5 (line 176) | def _cmd_disable_clk_div5(self):
    method _cmd_set_tck_divisor (line 182) | def _cmd_set_tck_divisor(self):
    method _cmd_loopback_end (line 192) | def _cmd_loopback_end(self):
    method _cmd_enable_clk_adaptive (line 197) | def _cmd_enable_clk_adaptive(self):
    method _cmd_disable_clk_adaptive (line 202) | def _cmd_disable_clk_adaptive(self):
    method _cmd_enable_clk_3phase (line 207) | def _cmd_enable_clk_3phase(self):
    method _cmd_disable_clk_3phase (line 212) | def _cmd_disable_clk_3phase(self):
    method _cmd_drive_zero (line 217) | def _cmd_drive_zero(self):
    method _cmd_send_immediate (line 226) | def _cmd_send_immediate(self):
    method _cmd_get_bits_low (line 231) | def _cmd_get_bits_low(self):
    method _cmd_get_bits_high (line 236) | def _cmd_get_bits_high(self):
    method _cmd_set_bits_low (line 241) | def _cmd_set_bits_low(self):
    method _cmd_set_bits_high (line 250) | def _cmd_set_bits_high(self):
    method _cmd_write_bytes_pve_msb (line 259) | def _cmd_write_bytes_pve_msb(self):
    method _cmd_write_bytes_nve_msb (line 262) | def _cmd_write_bytes_nve_msb(self):
    method _cmd_write_bytes_pve_lsb (line 265) | def _cmd_write_bytes_pve_lsb(self):
    method _cmd_write_bytes_nve_lsb (line 268) | def _cmd_write_bytes_nve_lsb(self):
    method _cmd_read_bytes_pve_msb (line 271) | def _cmd_read_bytes_pve_msb(self):
    method _resp_read_bytes_pve_msb (line 274) | def _resp_read_bytes_pve_msb(self):
    method _cmd_read_bytes_nve_msb (line 277) | def _cmd_read_bytes_nve_msb(self):
    method _resp_read_bytes_nve_msb (line 280) | def _resp_read_bytes_nve_msb(self):
    method _cmd_read_bytes_pve_lsb (line 283) | def _cmd_read_bytes_pve_lsb(self):
    method _resp_read_bytes_pve_lsb (line 286) | def _resp_read_bytes_pve_lsb(self):
    method _cmd_read_bytes_nve_lsb (line 289) | def _cmd_read_bytes_nve_lsb(self):
    method _resp_read_bytes_nve_lsb (line 292) | def _resp_read_bytes_nve_lsb(self):
    method _cmd_rw_bytes_nve_pve_msb (line 295) | def _cmd_rw_bytes_nve_pve_msb(self):
    method _resp_rw_bytes_nve_pve_msb (line 299) | def _resp_rw_bytes_nve_pve_msb(self):
    method _cmd_rw_bytes_pve_nve_msb (line 302) | def _cmd_rw_bytes_pve_nve_msb(self):
    method _resp_rw_bytes_pve_nve_msb (line 306) | def _resp_rw_bytes_pve_nve_msb(self):
    method _cmd_write_bits_pve_msb (line 309) | def _cmd_write_bits_pve_msb(self):
    method _cmd_write_bits_nve_msb (line 312) | def _cmd_write_bits_nve_msb(self):
    method _cmd_write_bits_pve_lsb (line 315) | def _cmd_write_bits_pve_lsb(self):
    method _cmd_write_bits_nve_lsb (line 318) | def _cmd_write_bits_nve_lsb(self):
    method _cmd_read_bits_pve_msb (line 321) | def _cmd_read_bits_pve_msb(self):
    method _resp_read_bits_pve_msb (line 324) | def _resp_read_bits_pve_msb(self):
    method _cmd_read_bits_nve_msb (line 327) | def _cmd_read_bits_nve_msb(self):
    method _resp_read_bits_nve_msb (line 330) | def _resp_read_bits_nve_msb(self):
    method _cmd_read_bits_pve_lsb (line 333) | def _cmd_read_bits_pve_lsb(self):
    method _resp_read_bits_pve_lsb (line 336) | def _resp_read_bits_pve_lsb(self):
    method _cmd_read_bits_nve_lsb (line 339) | def _cmd_read_bits_nve_lsb(self):
    method _resp_read_bits_nve_lsb (line 342) | def _resp_read_bits_nve_lsb(self):
    method _cmd_rw_bits_nve_pve_msb (line 345) | def _cmd_rw_bits_nve_pve_msb(self):
    method _resp_rw_bits_nve_pve_msb (line 349) | def _resp_rw_bits_nve_pve_msb(self):
    method _cmd_rw_bits_pve_nve_msb (line 352) | def _cmd_rw_bits_pve_nve_msb(self):
    method _resp_rw_bits_pve_nve_msb (line 356) | def _resp_rw_bits_pve_nve_msb(self):
    method _resp_get_bits_low (line 359) | def _resp_get_bits_low(self):
    method _resp_get_bits_high (line 368) | def _resp_get_bits_high(self):
    method _decode_output_mpsse_bytes (line 377) | def _decode_output_mpsse_bytes(self, caller, expect_rx=False):
    method _decode_output_mpsse_bits (line 393) | def _decode_output_mpsse_bits(self, caller, expect_rx=False):
    method _decode_input_mpsse_byte_request (line 407) | def _decode_input_mpsse_byte_request(self):
    method _decode_input_mpsse_bit_request (line 415) | def _decode_input_mpsse_bit_request(self):
    method _decode_input_mpsse_bytes (line 423) | def _decode_input_mpsse_bytes(self, caller):
    method _decode_input_mpsse_bits (line 441) | def _decode_input_mpsse_bits(self, caller):
    method bit2str (line 460) | def bit2str(cls, value: int, count: int, msb: bool, hiz: str = '_') ->...
    method bm2str (line 467) | def bm2str(cls, value: int, mask: int, hiz: str = '_') -> str:
    method bitfmt (line 473) | def bitfmt(cls, value, width):

FILE: pyftdi/usbtools.py
  class UsbToolsError (line 59) | class UsbToolsError(Exception):
  class UsbTools (line 63) | class UsbTools:
    method find_all (line 79) | def find_all(cls, vps: Sequence[Tuple[int, int]],
    method flush_cache (line 107) | def flush_cache(cls, ):
    method get_device (line 122) | def get_device(cls, devdesc: UsbDeviceDescriptor) -> UsbDevice:
    method release_device (line 200) | def release_device(cls, usb_dev: UsbDevice):
    method release_all_devices (line 221) | def release_all_devices(cls, devclass: Optional[Type] = None) -> int:
    method list_devices (line 242) | def list_devices(cls, urlstr: str,
    method parse_url (line 266) | def parse_url(cls, urlstr: str, scheme: str,
    method enumerate_candidates (line 338) | def enumerate_candidates(cls, urlparts: SplitResult,
    method show_devices (line 423) | def show_devices(cls, scheme: str,
    method build_dev_strings (line 451) | def build_dev_strings(cls, scheme: str,
    method get_string (line 526) | def get_string(cls, device: UsbDevice, stridx: int) -> str:
    method find_backend (line 551) | def find_backend(cls) -> IBackend:
    method _find_devices (line 562) | def _find_devices(cls, vendor: int, product: int,
    method _get_backend_device (line 626) | def _get_backend_device(cls, device: UsbDevice) -> Any:
    method _load_backend (line 642) | def _load_backend(cls) -> IBackend:

FILE: setup.py
  function read (line 53) | def read(*parts):
  function read_desc (line 62) | def read_desc(*parts):
  function find_meta (line 73) | def find_meta(meta):
  class BuildPy (line 83) | class BuildPy(build_py):
    method byte_compile (line 100) | def byte_compile(self, files):
  class CheckStyle (line 118) | class CheckStyle(Command):
    method initialize_options (line 124) | def initialize_options(self):
    method finalize_options (line 127) | def finalize_options(self):
    method run (line 130) | def run(self):
  function main (line 156) | def main():
Condensed preview — 91 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (795K chars).
[
  {
    "path": ".flake8",
    "chars": 30,
    "preview": "[flake8]\nmax-line-length = 80\n"
  },
  {
    "path": ".github/workflows/pypi.yml",
    "chars": 1035,
    "preview": "name: Publish to PyPI\n\non:\n  release:\n    types:\n      - published\n  workflow_dispatch:\n    inputs:\n      twine_verbose:"
  },
  {
    "path": ".github/workflows/pythonchecksyntax.yml",
    "chars": 1027,
    "preview": "name: Python syntax tests\n# check that there is no import issues with tool suite\n\non:\n  push:\n  pull_request:\n    types:"
  },
  {
    "path": ".github/workflows/pythonmocktests.yml",
    "chars": 1189,
    "preview": "name: Python USB mock tests\n\non:\n  push:\n  pull_request:\n    types: [assigned, opened, synchronize, reopened]\n\njobs:\n  b"
  },
  {
    "path": ".github/workflows/pythonpackage.yml",
    "chars": 896,
    "preview": "name: Python package\n\non:\n  push:\n  pull_request:\n    types: [assigned, opened, synchronize, reopened]\n\njobs:\n  build:\n\n"
  },
  {
    "path": ".gitignore",
    "chars": 89,
    "preview": "*.egg-info\n*.pyc\n*.pyo\n**/.DS_Store\nMANIFEST\npyusb*\ndist/\nbuild/\nsphinx/\n.vscode/\n.venv/\n"
  },
  {
    "path": ".pylintrc",
    "chars": 409,
    "preview": "[MASTER]\n\ninit-hook='import sys; sys.path.append(\".\")'\n\n[MESSAGES CONTROL]\n\ndisable=\n    too-few-public-methods,\n    too"
  },
  {
    "path": "LICENSE",
    "chars": 1554,
    "preview": "Copyright (c) 2008-2025 Emmanuel Blot <emmanuel.blot@free.fr>\nAll Rights Reserved.\n\nSPDX-License-Identifier: BSD-3-Claus"
  },
  {
    "path": "MANIFEST.in",
    "chars": 49,
    "preview": "exclude README.md\ninclude pyftdi/doc/images/*.png"
  },
  {
    "path": "README.md",
    "chars": 2424,
    "preview": "[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://vshymansk"
  },
  {
    "path": "_config.yml",
    "chars": 29,
    "preview": "theme: jekyll-theme-architect"
  },
  {
    "path": "pyftdi/INSTALL",
    "chars": 61,
    "preview": "Please read pyftdi/README.rst for installation instructions.\n"
  },
  {
    "path": "pyftdi/__init__.py",
    "chars": 1122,
    "preview": "# Copyright (c) 2010-2025 Emmanuel Blot <emmanuel.blot@free.fr>\n# Copyright (c) 2010-2016, Neotion\n# All rights reserved"
  },
  {
    "path": "pyftdi/bin/ftconf.py",
    "chars": 8178,
    "preview": "#!/usr/bin/env python3\n\n\"\"\"Simple FTDI EEPROM configurator.\n\"\"\"\n\n# Copyright (c) 2019-2024, Emmanuel Blot <emmanuel.blot"
  },
  {
    "path": "pyftdi/bin/ftdi_urls.py",
    "chars": 2715,
    "preview": "#!/usr/bin/env python3\n\n# Copyright (c) 2019-2024, Emmanuel Blot <emmanuel.blot@free.fr>\n# All rights reserved.\n#\n# SPDX"
  },
  {
    "path": "pyftdi/bin/i2cscan.py",
    "chars": 5578,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# Copyright (c) 2018-2024, Emmanuel Blot <emmanuel.blot@free.fr>\n# All r"
  },
  {
    "path": "pyftdi/bin/pyterm.py",
    "chars": 14250,
    "preview": "#!/usr/bin/env python3\n\n\"\"\"Simple Python serial terminal\n\"\"\"\n\n# Copyright (c) 2010-2024, Emmanuel Blot <emmanuel.blot@fr"
  },
  {
    "path": "pyftdi/bin/uphy.sh",
    "chars": 846,
    "preview": "#!/bin/sh\n\n# Load/unload kernel extension helper for macOS\n\nsystem=$(uname -s)\nif [ \"${system}\" != \"Darwin\" ]; then\n    "
  },
  {
    "path": "pyftdi/bits.py",
    "chars": 17805,
    "preview": "# Copyright (c) 2010-2024 Emmanuel Blot <emmanuel.blot@free.fr>\n# Copyright (c) 2008-2016, Neotion\n# All rights reserved"
  },
  {
    "path": "pyftdi/doc/api/eeprom.rst",
    "chars": 1211,
    "preview": "\n.. include:: ../defs.rst\n\n:mod:`eeprom` - EEPROM API\n--------------------------\n\n.. module :: pyftdi.eeprom\n\n\nQuickstar"
  },
  {
    "path": "pyftdi/doc/api/ftdi.rst",
    "chars": 528,
    "preview": ".. -*- coding: utf-8 -*-\n\n.. include:: ../defs.rst\n\n:mod:`ftdi` - FTDI low-level driver\n--------------------------------"
  },
  {
    "path": "pyftdi/doc/api/gpio.rst",
    "chars": 1009,
    "preview": ".. -*- coding: utf-8 -*-\n\n.. include:: ../defs.rst\n\n:mod:`gpio` - GPIO API\n----------------------\n\n.. module :: pyftdi.g"
  },
  {
    "path": "pyftdi/doc/api/i2c.rst",
    "chars": 5782,
    "preview": "\n.. include:: ../defs.rst\n\n:mod:`i2c` - |I2C| API\n----------------------\n\n.. module :: pyftdi.i2c\n\n\nQuickstart\n~~~~~~~~~"
  },
  {
    "path": "pyftdi/doc/api/index.rst",
    "chars": 192,
    "preview": "API documentation\n=================\n\n.. include:: ../defs.rst\n\n|release|\n---------\n\n.. toctree::\n   :maxdepth: 1\n   :glo"
  },
  {
    "path": "pyftdi/doc/api/misc.rst",
    "chars": 162,
    "preview": ".. -*- coding: utf-8 -*-\n\n:mod:`misc` - Miscellaneous helpers\n-----------------------------------\n\nFunctions\n~~~~~~~~~\n\n"
  },
  {
    "path": "pyftdi/doc/api/spi.rst",
    "chars": 5609,
    "preview": ".. include:: ../defs.rst\n\n:mod:`spi` - SPI API\n--------------------\n\n.. module :: pyftdi.spi\n\nQuickstart\n~~~~~~~~~~\n\nExa"
  },
  {
    "path": "pyftdi/doc/api/uart.rst",
    "chars": 9615,
    "preview": ".. include:: ../defs.rst\n\n:mod:`serialext` - UART API\n---------------------------\n\nThere is no dedicated module for the "
  },
  {
    "path": "pyftdi/doc/api/usbtools.rst",
    "chars": 226,
    "preview": ".. -*- coding: utf-8 -*-\n\n:mod:`usbtools` - USB tools\n---------------------------\n\n.. module :: pyftdi.usbtools\n\n\nClasse"
  },
  {
    "path": "pyftdi/doc/authors.rst",
    "chars": 887,
    "preview": "Authors\n-------\n\nMain developers\n~~~~~~~~~~~~~~~\n\n * Emmanuel Blot <emmanuel.blot@free.fr>\n * Emmanuel Bouaziz <ebouaziz"
  },
  {
    "path": "pyftdi/doc/conf.py",
    "chars": 2820,
    "preview": "# Copyright (c) 2010-2024 Emmanuel Blot <emmanuel.blot@free.fr>\n# All rights reserved.\n#\n# SPDX-License-Identifier: BSD-"
  },
  {
    "path": "pyftdi/doc/defs.rst",
    "chars": 2162,
    "preview": ".. |I2C| replace:: I\\ :sup:`2`\\ C\n\n.. _FT232R: https://www.ftdichip.com/Products/ICs/FT232R.htm\n.. _FT230X: https://www."
  },
  {
    "path": "pyftdi/doc/eeprom.rst",
    "chars": 18524,
    "preview": ".. include:: defs.rst\n\nEEPROM management\n-----------------\n\n.. warning::\n   Writing to the EEPROM can cause very **undes"
  },
  {
    "path": "pyftdi/doc/features.rst",
    "chars": 3029,
    "preview": ".. include:: defs.rst\n\nFeatures\n--------\n\nDevices\n~~~~~~~\n\n* All FTDI device ports (UART, MPSSE) can be used simultaneou"
  },
  {
    "path": "pyftdi/doc/gpio.rst",
    "chars": 15986,
    "preview": ".. include:: defs.rst\n\nGPIOs\n-----\n\nOverview\n~~~~~~~~\n\nMany PyFtdi APIs give direct access to the IO pins of the FTDI de"
  },
  {
    "path": "pyftdi/doc/index.rst",
    "chars": 3475,
    "preview": "PyFtdi\n======\n\n.. cannot use defs.rst here, as PyPi wants a standalone file.\n.. |I2C| replace:: I\\ :sup:`2`\\ C\n\nDocument"
  },
  {
    "path": "pyftdi/doc/installation.rst",
    "chars": 7798,
    "preview": ".. include:: defs.rst\n\nInstallation\n------------\n\nPrerequisites\n~~~~~~~~~~~~~\n\nPyFTDI_ relies on PyUSB_, which requires "
  },
  {
    "path": "pyftdi/doc/license.rst",
    "chars": 2122,
    "preview": "License\n-------\n\n.. include:: defs.rst\n\nFor historical reasons (PyFtdi has been initially developed as a compatibility\nl"
  },
  {
    "path": "pyftdi/doc/pinout.rst",
    "chars": 2477,
    "preview": ".. include:: defs.rst\n\nFTDI device pinout\n------------------\n\n============ ============= ======= ====== ============== ="
  },
  {
    "path": "pyftdi/doc/requirements.rst",
    "chars": 2022,
    "preview": ".. include:: defs.rst\n\nRequirements\n------------\n\nPython_ 3.9 or above is required.\n\nPyFtdi_ relies on PyUSB_, which its"
  },
  {
    "path": "pyftdi/doc/testing.rst",
    "chars": 4250,
    "preview": "Testing\n-------\n\n.. include:: defs.rst\n\nOverview\n~~~~~~~~\n\nTesting PyFTDI is challenging because it relies on several pi"
  },
  {
    "path": "pyftdi/doc/tools.rst",
    "chars": 3670,
    "preview": ".. include:: defs.rst\n\n.. _tools:\n\nTools\n-----\n\nOverview\n~~~~~~~~\n\nPyFtdi_ comes with a couple of scripts designed to he"
  },
  {
    "path": "pyftdi/doc/troubleshooting.rst",
    "chars": 3845,
    "preview": ".. include:: defs.rst\n\nTroubleshooting\n---------------\n\nReporting a bug\n~~~~~~~~~~~~~~~\n\nPlease do not contact the autho"
  },
  {
    "path": "pyftdi/doc/urlscheme.rst",
    "chars": 3927,
    "preview": ".. include:: defs.rst\n\n.. _url_scheme:\n\nURL Scheme\n----------\n\nThere are two ways to open a connection to an `Ftdi` obje"
  },
  {
    "path": "pyftdi/eeprom.py",
    "chars": 48357,
    "preview": "# Copyright (c) 2019-2024, Emmanuel Blot <emmanuel.blot@free.fr>\n# All rights reserved.\n#\n# SPDX-License-Identifier: BSD"
  },
  {
    "path": "pyftdi/ftdi.py",
    "chars": 96571,
    "preview": "# Copyright (c) 2010-2024 Emmanuel Blot <emmanuel.blot@free.fr>\n# Copyright (c) 2016 Emmanuel Bouaziz <ebouaziz@free.fr>"
  },
  {
    "path": "pyftdi/gpio.py",
    "chars": 21336,
    "preview": "# Copyright (c) 2014-2024, Emmanuel Blot <emmanuel.blot@free.fr>\n# Copyright (c) 2016, Emmanuel Bouaziz <ebouaziz@free.f"
  },
  {
    "path": "pyftdi/i2c.py",
    "chars": 44979,
    "preview": "# Copyright (c) 2017-2025, Emmanuel Blot <emmanuel.blot@free.fr>\n# All rights reserved.\n#\n# SPDX-License-Identifier: BSD"
  },
  {
    "path": "pyftdi/jtag.py",
    "chars": 24094,
    "preview": "# Copyright (c) 2010-2024, Emmanuel Blot <emmanuel.blot@free.fr>\n# Copyright (c) 2016, Emmanuel Bouaziz <ebouaziz@free.f"
  },
  {
    "path": "pyftdi/misc.py",
    "chars": 11989,
    "preview": "# Copyright (c) 2010-2024 Emmanuel Blot <emmanuel.blot@free.fr>\n# Copyright (c) 2008-2016, Neotion\n# All rights reserved"
  },
  {
    "path": "pyftdi/serialext/__init__.py",
    "chars": 1023,
    "preview": "# Copyright (c) 2010-2024 Emmanuel Blot <emmanuel.blot@free.fr>\n# Copyright (c) 2008-2015, Neotion\n# All rights reserved"
  },
  {
    "path": "pyftdi/serialext/logger.py",
    "chars": 5231,
    "preview": "# Copyright (c) 2010-2024 Emmanuel Blot <emmanuel.blot@free.fr>\n# Copyright (c) 2008-2016, Neotion\n# All rights reserved"
  },
  {
    "path": "pyftdi/serialext/protocol_ftdi.py",
    "chars": 6200,
    "preview": "# Copyright (c) 2008-2024, Emmanuel Blot <emmanuel.blot@free.fr>\n# Copyright (c) 2008-2016, Neotion\n# All rights reserve"
  },
  {
    "path": "pyftdi/serialext/protocol_unix.py",
    "chars": 6836,
    "preview": "# Copyright (c) 2008-2024, Emmanuel Blot <emmanuel.blot@free.fr>\n# Copyright (c) 2016, Emmanuel Bouaziz <ebouaziz@free.f"
  },
  {
    "path": "pyftdi/serialext/tests/rl.py",
    "chars": 512,
    "preview": "#!/usr/bin/env python3\n\n\"\"\"Check readline feature.\"\"\"\n\nfrom os.path import dirname\nfrom sys import path\nfrom serial impo"
  },
  {
    "path": "pyftdi/spi.py",
    "chars": 38103,
    "preview": "# Copyright (c) 2010-2024, Emmanuel Blot <emmanuel.blot@free.fr>\n# Copyright (c) 2016, Emmanuel Bouaziz <ebouaziz@free.f"
  },
  {
    "path": "pyftdi/term.py",
    "chars": 6666,
    "preview": "\"\"\"Terminal management helpers\"\"\"\n\n# Copyright (c) 2020-2024, Emmanuel Blot <emmanuel.blot@free.fr>\n# Copyright (c) 2020"
  },
  {
    "path": "pyftdi/tests/backend/__init__.py",
    "chars": 125,
    "preview": "# Copyright (c) 2020, Emmanuel Blot <emmanuel.blot@free.fr>\n# All rights reserved.\n#\n# SPDX-License-Identifier: BSD-3-Cl"
  },
  {
    "path": "pyftdi/tests/backend/consts.py",
    "chars": 5950,
    "preview": "\"\"\"Constant importer from existing modules.\"\"\"\n\n# Copyright (c) 2020-2024, Emmanuel Blot <emmanuel.blot@free.fr>\n# All r"
  },
  {
    "path": "pyftdi/tests/backend/ftdivirt.py",
    "chars": 47269,
    "preview": "\"\"\"PyUSB virtual FTDI device.\"\"\"\n\n# Copyright (c) 2020-2024, Emmanuel Blot <emmanuel.blot@free.fr>\n# All rights reserved"
  },
  {
    "path": "pyftdi/tests/backend/loader.py",
    "chars": 15811,
    "preview": "\"\"\"Virtual USB backend loader.\n\"\"\"\n\n# Copyright (c) 2020-2024, Emmanuel Blot <emmanuel.blot@free.fr>\n# All rights reserv"
  },
  {
    "path": "pyftdi/tests/backend/mpsse.py",
    "chars": 3583,
    "preview": "\"\"\"PyUSB virtual FTDI device.\"\"\"\n\n# Copyright (c) 2020, Emmanuel Blot <emmanuel.blot@free.fr>\n# All rights reserved.\n#\n#"
  },
  {
    "path": "pyftdi/tests/backend/usbvirt.py",
    "chars": 16593,
    "preview": "\"\"\"PyUSB virtual USB backend to intercept all USB requests.\n\n   The role of this module is to enable PyFtdi API testing "
  },
  {
    "path": "pyftdi/tests/bits.py",
    "chars": 9788,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"BitSequence unit tests.\"\"\"\n\n# Copyright (c) 2010-2024 Emmanuel Blot <"
  },
  {
    "path": "pyftdi/tests/cbus.py",
    "chars": 4314,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"CBUS unit tests.\"\"\"\n\n# Copyright (c) 2020-2024, Emmanuel Blot <emmanu"
  },
  {
    "path": "pyftdi/tests/eeprom.py",
    "chars": 4359,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"EEPROM unit tests.\"\"\"\n\n# Copyright (c) 2018, Stephen Goadhouse <sgoad"
  },
  {
    "path": "pyftdi/tests/eeprom_mock.py",
    "chars": 16666,
    "preview": "#!/usr/bin/env python3\n# Copyright (c) 2019-2024, Emmanuel Blot <emmanuel.blot@free.fr>\n# All rights reserved.\n# SPDX-Li"
  },
  {
    "path": "pyftdi/tests/ftdi.py",
    "chars": 6301,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"FTDI detection and connection unit tests.\"\"\"\n\n# Copyright (c) 2010-20"
  },
  {
    "path": "pyftdi/tests/gpio.py",
    "chars": 26602,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# Copyright (c) 2016-2024, Emmanuel Blot <emmanuel.blot@free.fr>\n# All r"
  },
  {
    "path": "pyftdi/tests/i2c.py",
    "chars": 10025,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"I2C unit tests.\"\"\"\n\n# Copyright (c) 2017-2024, Emmanuel Blot <emmanue"
  },
  {
    "path": "pyftdi/tests/jtag.py",
    "chars": 3174,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"JTAG unit test.\"\"\"\n\n# Copyright (c) 2011-2024, Emmanuel Blot <emmanue"
  },
  {
    "path": "pyftdi/tests/mockusb.py",
    "chars": 31447,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# Copyright (c) 2020-2024, Emmanuel Blot <emmanuel.blot@free.fr>\n# All r"
  },
  {
    "path": "pyftdi/tests/resources/custom_vidpid.yaml",
    "chars": 411,
    "preview": "# SPDX-License-Identifier: BSD-3-Clause\n\ndevices:\n  - bus: 1\n    address: 1\n    descriptor:\n      vid: 0x403\n      pid: "
  },
  {
    "path": "pyftdi/tests/resources/ft2232h.yaml",
    "chars": 284,
    "preview": "# SPDX-License-Identifier: BSD-3-Clause\n\ndevices:\n  - bus: 1\n    address: 1\n    descriptor:\n      vid: 0x403\n      pid: "
  },
  {
    "path": "pyftdi/tests/resources/ft230x.yaml",
    "chars": 1187,
    "preview": "# SPDX-License-Identifier: BSD-3-Clause\n\ndevices:\n  - bus: 1\n    address: 1\n    descriptor:\n      vid: 0x403\n      pid: "
  },
  {
    "path": "pyftdi/tests/resources/ft230x_io.yaml",
    "chars": 1356,
    "preview": "# SPDX-License-Identifier: BSD-3-Clause\n\ndevices:\n  - bus: 1\n    address: 1\n    descriptor:\n      vid: 0x403\n      pid: "
  },
  {
    "path": "pyftdi/tests/resources/ft231x_cbus.yaml",
    "chars": 1448,
    "preview": "# SPDX-License-Identifier: BSD-3-Clause\n\ndevices:\n  - bus: 1\n    address: 1\n    descriptor:\n      vid: 0x403\n      pid: "
  },
  {
    "path": "pyftdi/tests/resources/ft232h.yaml",
    "chars": 2576,
    "preview": "# SPDX-License-Identifier: BSD-3-Clause\n\n# This file defines a regular FT232H virtual device\n# It also shows supported o"
  },
  {
    "path": "pyftdi/tests/resources/ft232h_x2.yaml",
    "chars": 396,
    "preview": "# SPDX-License-Identifier: BSD-3-Clause\n\ndevices:\n  - bus: 1\n    address: 1\n    descriptor:\n      vid: 0x403\n      pid: "
  },
  {
    "path": "pyftdi/tests/resources/ft232r.yaml",
    "chars": 743,
    "preview": "# SPDX-License-Identifier: BSD-3-Clause\n\ndevices:\n  - bus: 1\n    address: 1\n    descriptor:\n      vid: 0x403\n      pid: "
  },
  {
    "path": "pyftdi/tests/resources/ft4232h.yaml",
    "chars": 284,
    "preview": "# SPDX-License-Identifier: BSD-3-Clause\n\ndevices:\n  - bus: 1\n    address: 1\n    descriptor:\n      vid: 0x403\n      pid: "
  },
  {
    "path": "pyftdi/tests/resources/ft4232ha.yaml",
    "chars": 288,
    "preview": "# SPDX-License-Identifier: BSD-3-Clause\n\ndevices:\n  - bus: 1\n    address: 1\n    descriptor:\n      vid: 0x403\n      pid: "
  },
  {
    "path": "pyftdi/tests/resources/ftmany.yaml",
    "chars": 1393,
    "preview": "# SPDX-License-Identifier: BSD-3-Clause\n\ndevices:\n  - bus: 1\n    address: 2\n    descriptor:\n      vid: 0x403\n      pid: "
  },
  {
    "path": "pyftdi/tests/spi.py",
    "chars": 12406,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# Copyright (c) 2017-2020, Emmanuel Blot <emmanuel.blot@free.fr>\n# All r"
  },
  {
    "path": "pyftdi/tests/toolsimport.py",
    "chars": 1962,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# Copyright (c) 2024, Emmanuel Blot <emmanuel.blot@free.fr>\n# All rights"
  },
  {
    "path": "pyftdi/tests/uart.py",
    "chars": 13237,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"UART unit tests.\"\"\"\n\n# Copyright (c) 2017-2024, Emmanuel Blot <emmanu"
  },
  {
    "path": "pyftdi/tracer.py",
    "chars": 17647,
    "preview": "# Copyright (c) 2017-2024, Emmanuel Blot <emmanuel.blot@free.fr>\n# All rights reserved.\n#\n# SPDX-License-Identifier: BSD"
  },
  {
    "path": "pyftdi/usbtools.py",
    "chars": 27487,
    "preview": "# Copyright (c) 2014-2024, Emmanuel Blot <emmanuel.blot@free.fr>\n# Copyright (c) 2016, Emmanuel Bouaziz <ebouaziz@free.f"
  },
  {
    "path": "requirements.txt",
    "chars": 37,
    "preview": "pyusb>=1.0, != 1.2.0\npyserial >= 3.0\n"
  },
  {
    "path": "setup.cfg",
    "chars": 176,
    "preview": "[bdist_wheel]\n\n[metadata]\nlicense_files = pyftdi/doc/license.rst\n\n[project]\nlicense = \"BSD-3-Clause\"\n\n[build_sphinx]\nsou"
  },
  {
    "path": "setup.py",
    "chars": 6461,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n#\n# Copyright (c) 2010-2025 Emmanuel Blot <emmanuel.blot@free.fr>\n# Copyri"
  },
  {
    "path": "test-requirements.txt",
    "chars": 44,
    "preview": "setuptools\nwheel\npylint\nruamel.yaml >= 0.16\n"
  }
]

About this extraction

This page contains the full source code of the eblot/pyftdi GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 91 files (746.4 KB), approximately 193.4k tokens, and a symbol index with 1180 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!