CircuitsFT232H. USB
(circuits_ft232h v0.1.0)
Copy Markdown
Thin USB transport layer for the FT232H.
Wraps the :usb NIF (libusb) with
FT232H-specific endpoint addresses, FTDI SIO control-transfer helpers, and
modem-status-byte stripping for bulk reads.
This module is internal — higher layers (CircuitsFT232H.MPSSE,
CircuitsFT232H.Device) compose it into something useful. You normally
shouldn't need to call it directly.
Summary
Functions
Releases the interface, re-attaches the kernel driver if we detached it on open, and closes the device.
Decodes a raw USB string descriptor binary into an Elixir string.
Disables the FTDI event/error characters (we don't use them in MPSSE).
Puts the chip into MPSSE mode. After this, write/2 to the bulk OUT
endpoint is interpreted as a stream of MPSSE commands.
Resets bitmode to its default (UART). Useful to drop out of MPSSE before releasing the device.
Lists every FT232H currently attached to the host's USB bus.
Opens descriptor and prepares the chip for MPSSE traffic.
Purges the chip's RX FIFO.
Purges the chip's TX FIFO.
Reads exactly length bytes of MPSSE payload from the chip.
Reads a USB string descriptor at the given index.
Resets the FTDI SIO. Equivalent to issuing SIO_RESET with wValue = 0.
Sets the bitmode (high byte) and pin direction mask (low byte) on the low byte port (ADBUS).
Sets the chip's USB latency timer to milliseconds (1..255).
Strips the 2-byte modem-status prefix from every USB packet in data.
Sends raw MPSSE bytes to the chip over the bulk OUT endpoint.
Types
@type t() :: %CircuitsFT232H.USB{ address: 0..255, bus: 0..255, detached_kernel_driver?: boolean(), handle: :usb.device_handle(), max_packet_size: pos_integer() }
An open FT232H USB handle.
Functions
@spec close(t()) :: :ok
Releases the interface, re-attaches the kernel driver if we detached it on open, and closes the device.
Always returns :ok. Errors during teardown are logged but not surfaced —
by the time we're closing there is nothing useful for the caller to do.
Decodes a raw USB string descriptor binary into an Elixir string.
The on-wire format is <<length, 0x03, utf16_le_bytes::binary>>. Returns
{:error, :invalid_descriptor} if the input doesn't fit that shape
(typical when the chip returns 0xFF-filled garbage for an unprogrammed
string), and {:error, :invalid_utf16} if the bytes after the header
don't decode as UTF-16LE.
Disables the FTDI event/error characters (we don't use them in MPSSE).
Puts the chip into MPSSE mode. After this, write/2 to the bulk OUT
endpoint is interpreted as a stream of MPSSE commands.
Resets bitmode to its default (UART). Useful to drop out of MPSSE before releasing the device.
@spec list_devices() :: {:ok, [CircuitsFT232H.USB.Descriptor.t()]} | {:error, term()}
Lists every FT232H currently attached to the host's USB bus.
Identification is by VID 0x403,
PID 0x6014 — the values used by
the raw FT232H and the Adafruit FT232H Breakout.
@spec open( CircuitsFT232H.USB.Descriptor.t(), keyword() ) :: {:ok, t()} | {:error, term()}
Opens descriptor and prepares the chip for MPSSE traffic.
Detaches the kernel driver if one is attached (typically ftdi_sio on Linux
or Apple's FTDI VCP driver on macOS) and claims the FT232H's single USB
interface. Once open, use write/2 and read/3 to push and pull MPSSE
bytes, and the various set_*/purge_*/reset/1 helpers to configure the
SIO via FTDI control transfers.
Purges the chip's RX FIFO.
Purges the chip's TX FIFO.
@spec read(t(), non_neg_integer(), timeout()) :: {:ok, binary()} | {:error, term()}
Reads exactly length bytes of MPSSE payload from the chip.
Each USB packet from FT232H is prefixed by 2 modem-status bytes; this
function strips them before returning the result, so length refers to
real payload bytes.
@spec read_string_descriptor(:usb.device(), 1..255) :: {:ok, String.t()} | {:error, term()}
Reads a USB string descriptor at the given index.
Used for retrieving the FTDI serial number, manufacturer string, or product string. The first call reads the supported language IDs (index 0), then re-reads at the requested index in the first language.
Most failures are normal: :access if the udev rule isn't installed,
:invalid_descriptor if the chip has no programmed string at that index
(common for FT232Hs whose EEPROM hasn't been written).
Resets the FTDI SIO. Equivalent to issuing SIO_RESET with wValue = 0.
Sets the bitmode (high byte) and pin direction mask (low byte) on the low byte port (ADBUS).
The direction byte sets each pin to output (1) or input (0). In MPSSE mode the MPSSE engine overrides this for SCK/MOSI/MISO/CS, but the initial value matters for any GPIOs.
Sets the chip's USB latency timer to milliseconds (1..255).
Lower values give snappier reads at the cost of more USB traffic; MPSSE applications typically use 1-16 ms. The chip default is 16 ms.
@spec strip_status(binary(), pos_integer()) :: binary()
Strips the 2-byte modem-status prefix from every USB packet in data.
packet_size is the bulk endpoint's max packet size (typically 512 for
high-speed). Exposed because it's a pure function and the easiest piece of
this module to unit-test.
Sends raw MPSSE bytes to the chip over the bulk OUT endpoint.