Control FTDI CBUS while TTY is open

For embedded projects I want to control the FTDI CBUS pins as GPIOs. The libFTDI1 library allows to program the CBUS pins as GPIOs by setting the CBUS function to CBUS_IOMODE. I created a Python script available at the python_ft232_cbus_config repository (must be run as root!). The script reprograms CBUS2/3 to IOMODE, while leaving CBUS0/1 at their default (TXLED/RXLED). It should be fairly easy to change the script for your needs.

Once the CBUS pins are programmed as IOMODE, they can be controlled through libFTDI1. There is a C example in the source repository at examples/bitbang_cbus.c. However, there is one downside when using the library: The FTDI kernel driver gets detached automatically. Since I am a screen user, this means that my open sessions get closed automatically too! Using DONT_DETACH_SIO_MODULE seems not to do the trick, the library returns with error code -5 (unable to claim device). It seems that controlling FTDI from user space inherently conflicts with the in-kernel FTDI driver.

Already a while ago I tried to fix this by adding GPIO support to the kernel driver. Unfortunately this did not work out/I lost interest. However, Peter had a valuable hint back then: Control Transfers should be possible without detaching the kernel driver.

And indeed, with hacking a bit around with PyUSB it is possible:

def ftdi_set_bitmode(dev, bitmask):
    bmRequestType = usb.util.build_request_type(usb.util.CTRL_OUT,
                                                usb.util.CTRL_TYPE_VENDOR,
                                                usb.util.CTRL_RECIPIENT_DEVICE)

    wValue = bitmask | (BITMODE_CBUS << 8)
    dev.ctrl_transfer(bmRequestType, SIO_SET_BITMODE_REQUEST, wValue)

def main():
    """Main program"""
    dev = usb.core.find(custom_match = \
            lambda d: \
                d.idVendor==0x0403 and
                d.idProduct==0x6001 and
                d.serial_number=="A105N7O2")

    # Set CBUS2/3 high...
    ftdi_set_bitmode(dev, 0xCC)

Full source of the ftdi-cbus-ctrlep.py.

In my use-case, I connected two of the CBUS pins on a Toradex Aster carrier board to the reset and recovery line of the module. I then bound keys combinations in .screenrc which execute the ftdi-cbus-ctrlep-tdx.py Python scripts. This allows me to reset the board from my desktop while having the GNU screen terminal open using a simple key stroke.

Update

When you experience issues such as “Error: The device has no langid” following the pyftdi installation instructions help to setup user access rights for the FTDI devices correctly.

Update 2

Setting the bitmode was using BITMODE_CBUS as bit shift (it used to be (BITMODE_CBUS << BITMODE_CBUS). It seems to work on some chips, but it is wrong. The mode should be shifted by 8. See also src/ftdi.c of flibftdi.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.