view doc/FTDI-EEPROM-tools @ 77:66cbbd7d85cf

ftee-gen*: allow trailing ws and comments after ftdi-chip and eeprom settings
author Mychaela Falconia <falcon@freecalypso.org>
date Tue, 26 Sep 2023 01:17:32 +0000
parents 9cfe3223fbf8
children 930bd5316d56
line wrap: on
line source

Mother Mychaela has developed a set of Linux command line tools for manipulating
configuration EEPROMs that are attached to FT2232x devices and accessed in-band
via USB.  This document describes these tools.

Supported FTDI chips and EEPROMs
================================

The present tools work with 93C46, 93C56 and 93C66 EEPROMs attached behind
FT2232x dual-channel UART/FIFO/MPSSE/etc chips, both FT2232C/D and FT2232H.
We can read these EEPROMs for examination or backup, and we can program them
with new bits, either restoring a previously saved backup or creating a new
from-scratch configuration.  These EEPROM configurations (which we can save,
restore or create from scratch) set the USB VID:PID and the textual strings
naming the manufacturer, the product model and an optional serial number,
select whether each FT2232x channel will come up in the default UART mode or
one of the other EEPROM-configurable modes (245 FIFO, CPU-style FIFO or fast
opto-isolated serial), and allow a few other obscure chip settings to be
tweaked.

Some work has also been done toward the goal of being able to program the
internal EEPROM in FT232R chips (a very popular single-channel USB to UART
converter needing no external components), but this work should be considered
experimental: the tools appear to work on an UB232R module from Digi-Key
(presumably containing a genuine FT232RQ chip) and on a no-name FT232RL adapter
where the chip is uncertain, but because we have no real production use case
yet, we are not ready to truly vouch for FT232R support.

More generally:

* our fteeprom-read tool should be able to read out the EEPROM content from
  just about any FTDI chip;

* our fteeprom-prog tool should be able to program a user-supplied set of bits
  into any FTDI+EEPROM combo where the EEPROM is a separate chip, or into FT232R
  internal EEPROM - but it most likely won't work for newer FT-X chips;

* if the goal is to generate a new EEPROM config from scratch, as opposed to
  restoring a saved backup, we currently have generators only for FT2232C/D,
  for FT2232H and for FT232R, with the last one considered experimental and not
  proven.

No more libftdi dependency!
===========================

Our initial implementation of fteeprom-* tools was based on libftdi; more
specifically, one had to use an old libftdi-0.x version, as these old versions
were the only ones that allowed writing to the EEPROM directly with
ftdi_write_eeprom_location() API calls.  However, the present version has been
reimplemented to NOT use libftdi at all - instead we have our own minilibs,
maintained as part of fc-usbser-tools package, that are built on top of
libusb-0.x API.  (The version of libftdi we used previously was also built on
top of the same libusb-0.x API, hence no change in that dependency.)  The
libusb-0.x API we use consists of <usb.h> include header and -lusb link library
pull-in; on "modern" systems these pieces will typically be provided by
libusb-compat-0.1 package wrapping around libusb-1.x, but in the spirit of Holy
retrocomputing, really old systems can be used with native libusb-0.1.

Selecting the device to operate on
==================================

Our fteeprom-read, fteeprom-prog and fteeprom-erase tools take a device selector
argument, selecting the device to operate on.  The design of this device
selector mechanism has been copied from libftdi; while we no longer use libftdi,
we found its device selector mechanism to be a really good design and we have
fully reimplemented it.  The device selector argument is a string in one of the
following formats (some wording copied from libftdi documentation):

d:<devicenode> - path of bus and device-node (e.g. "003/001") within USB device
tree as enumerated via libusb-0.x API.  Libftdi documentation said
/proc/bus/usb, but at least on Mother's Slackware 14.2 system, the observed
location of this device tree is /dev/bus/usb.

i:<vendor>:<product> - first device with given vendor and product id, ids can
be decimal, octal (preceded by "0") or hex (preceded by "0x")

i:<vendor>:<product>:<index> - as above with index being the number of the
device (starting with 0) if there are more than one

s:<vendor>:<product>:<serial> - first device with given vendor id, product id
and serial string

If you have only one FTDI device connected to your PC or laptop at the time of
your EEPROM manipulation session (generally a good idea to avoid hitting the
wrong device by mistake) and if that FTDI device has some sensible starting
USB VID:PID (either from the previous EEPROM config or the chip's sans-EEPROM
default) that doesn't clash with anything else, then the i: form will probably
be the most convenient, e.g.:

i:0x0403:0x6001 for single-channel FT232x devices running with the default ID
i:0x0403:0x6010 for dual-channel FT2232x devices running with the default ID
i:0x0403:0xPPPP for custom PIDs assigned out of FTDI's VID range
i:0xVVVV:0xPPPP for totally custom USB IDs

Or if the current device config is totally hosed (the EEPROM has a passing
checksum, but sets some completely bogus USB ID), then the d: form will
probably be required for recovery.

Reading the EEPROM
==================

The basic EEPROM read command is as follows:

fteeprom-read <device-selector>

See the previous section for the device selector argument.  In this default
form the tool will read the first 64 EEPROM words, which is appropriate for
93C46 external EEPROMs or for the internal 1024-bit EEPROM in the FT232R chip.
However, if you are working with an FT2232x board with an external EEPROM and
that EEPROM is of a larger variety (93C56 or 93C66), this basic form with give
you an incomplete (truncated) read, and you will need one of the following
extended forms to read the complete EEPROM:

fteeprom-read -b <device-selector>	-- read 128 EEPROM words (93C56)
fteeprom-read -B <device-selector>	-- read 256 EEPROM words (93C66)

(If you use one of the extended forms on a smaller EEPROM, you will get 2 or 4
 copies of the same bits.)

The output of fteeprom-read is in the same format as the input to fteeprom-prog,
thus you can redirect the output to a file and get a restorable backup copy of
your EEPROM.

Change from previous version
----------------------------

In the original libftdi-based implementation of fteeprom-read, the act of
reading the EEPROM was invasive: libftdi's open function would unbind the
kernel's ftdi_sio driver (Channel A ttyUSB device disappears) and
reset/reinitialize the SIO channel itself.  However, it turns out that these
invasive steps aren't needed if the goal is only to read the EEPROM - the
necessary USB control endpoint transactions can be done while the kernel's
ftdi_sio driver remains attached with ttyUSBx devices intact on all channels.
The current version of fteeprom-read has been fixed to be non-invasive in this
regard: you can now freely read the EEPROM of any connected FTDI device
*without* bumping off that device's ttyUSB.

Programming the EEPROM
======================

In terms of the primitives provided over USB, writing to EEPROMs sitting behind
FTDI chips is accomplished by writing one 16-bit word at a time: the
SIO_WRITE_EEPROM_REQUEST command writes a user-supplied word at a user-supplied
EEPROM address.  However, our fteeprom-prog tool currently supports only writing
complete EEPROMs (64 or 128 or 256 16-bit words starting at address 0) and we
do not currently provide any kind of "random access write" utility; the primary
reason for this design decision is practical usefulness: FTDI's EEPROM structure
includes a checksum over the first 64 words for 1024-bit EEPROMs or over the
first 128 words for larger ones, and if this checksum fails to match, the entire
structure is deemed to be invalid - hence there is no practical use case for
selectively rewriting individual words.  The only exception may be with 93C66
EEPROMs: on these giants only the first half would be subject to the checksum,
and the second half could be used arbitrarily.  However, we have not yet
encountered any boards out in the wild with such big EEPROMs, and we have no
plans to use such in any of our own hardware designs either, hence there is no
business case at the present moment to develop tooling support for them.

There are two primary modes of usage for our fteeprom-prog tool: restoring a
saved EEPROM backup or writing a new EEPROM config which you generate yourself.
To restore a saved EEPROM backup, run the tool as follows:

fteeprom-prog <device-selector> <eeprom-image-file>

To program a new EEPROM config of your own, run a pipeline of this form:

<generator-tool> | fteeprom-prog <device-selector>

fteeprom-prog reads the EEPROM image from stdin if no image file is named on
the command line; the image format is the same in both cases, and the length of
this EEPROM image tells the tool how many words need to be programmed - there
are no -b or -B options to fteeprom-prog.

Generator tools
===============

Unfortunately FTDI never documented the format of their EEPROM configuration
structure - apparently they consider it a proprietary trade secret just like
the wire protocol spoken over USB between their chips and their closed-source
proprietary drivers.  All FOSS community support for these chips is based on
reverse engineering, and that includes the EEPROM format.

The present suite of tools includes ftee-gen2232c and ftee-gen2232h EEPROM image
generators, meant for use with FT2232C/D and FT2232H chips, respectively.  These
tools are based on the knowledge extracted from other (pre-existing) community
tools, primarily the EEPROM config code built into various libftdi versions -
we haven't done any FTDI RE of our own, instead the goal of this project has
been to create a set of tools that are better fit for production use.

Our ftee-gen2232c and ftee-gen2232h tools are invoked as follows:

ftee-gen2232[ch] [-b|-B] <config-file> [serial-num]

The output of these generator tools is meant to be piped directly into
fteeprom-prog.

The philosophy of which settings are given in the config file vs. which ones
are given on the command line reflects configuration management and factory
production line operations.  In the envisioned usage there would be a config
file for each product, giving the USB VID:PID, textual manufacturer and product
ID strings and possibly other config settings which need to be changed from the
defaults, but the optional serial number string is given on the command line
because it would be different for each individual unit being programmed.

The EEPROM size selection is also made on the command line, so that the same
config can be programmed into a smaller EEPROM or a bigger one.  By default our
tools generate an image suitable for a 93C46 EEPROM: the generated image is 64
words long, with a checksum in word 63, and the EEPROM type byte in FTDI's
structure is set to 0x46.  Running with -b produces an image for a 93C56 EEPROM:
the EEPROM type byte is set to 0x56, and the checksum-covered image length is
extended to 128 words.  Finally, -B sets things up for a 93C66 EEPROM: the
EEPROM type byte is set to 0x66, but the generated checksum-covered image is
still 128 words long just like with -b, as that is what FT2232x chips apparently
expect.  I said "apparently" because I don't have any FT2232x hardware with
93C66 EEPROMs and I don't plan on acquiring or building any, hence this minimal
93C66 support is completely untested - use at your own risk.

It also needs to be noted that with our current RE-based understanding of FTDI's
undocumented EEPROM structure, using a bigger EEPROM does NOT provide more room
for strings: all that happens with -b and -B options is that a gap of 64 unused
EEPROM words is inserted between the end of the fixed structure and the
beginning of strings.  The exact same arrangement has been observed in all 93C56
EEPROM images found in the wild, presumably produced with FTDI's official tools,
including FTDI's own USB-COM232-PLUS2 board - thus it is not clear at all if
FT2232x chips actually support longer strings with bigger EEPROMs, and if not,
what does one need a bigger EEPROM for...

For the format of config files read by our ftee-gen2232[ch] tools and what
settings can be tweaked, read the source code.

Installation directory for EEPROM config files
----------------------------------------------

If the name of the config file passed to ftee-gen* does not contain any '/'
characters, the named file is sought first in an installation directory
(/opt/freecalypso/ftdi) and then in the current directory.  To suppress this
search path and read EEPROM config files only from the current directory,
specify your config file as ./name - filenames (pathnames) containing slashes
are read as-is.

This installation directory and search mechanism have been added in order to
allow standard (usually developed at FreeCalypso HQ) FTDI EEPROM configs which
end users can then program into their boards by executing a fixed command line
given in a manual.

FT232R differences
==================

The EEPROM generator tool for FT232R is ftee-gen232r; it works on the same
principle as ftee-gen2232[ch] for FT2232x.  However, when you run fteeprom-prog
to program FT232R's internal EEPROM (whether you are restoring a backup or
programming the output of ftee-gen232r), you need to add -r option before the
device selector string.  This option tells fteeprom-prog to execute the
FT232R-specific magic sequence (documented in FT232R-notes) before proceeding
to actual EEPROM writes - without this option the EEPROM content will be garbage
(bitwise AND of old and new EEPROM images), producing the appearance of a
bricked chip.

In the previous libftdi-based version of fteeprom-prog the magic sequence in
question was executed unconditionally - however, because it is needed only for
FT232R and because we could simplify our new sans-libftdi code by implementing
it in an FT232R-only manner (no support for different "index" values for
multichannel FTDI devices), we've changed it from unconditional to -r option.

Experiments show that fteeprom-prog -r option appears to be harmless (though
unnecessary) on FT2232D and FT2232H - however, Mother's recommendation is to
use it only on FT232R devices.

Erasing the EEPROM (making it blank)
====================================

If you are playing with a "generic" FT2232x breakout board that is made for
tinkering, as opposed to a more finished product, such boards are typically
shipped with their EEPROMs completely blank.  In that case restoring the EEPROM
to its "pristine" state after playing around would mean erasing it, i.e.,
bringing it into a blank (all ones) state.  FT2232x chips provide two ways to
do so: one can explicitly write 0xFFFF into each individual EEPROM word with
SIO_WRITE_EEPROM_REQUEST, or one can send a SIO_ERASE_EEPROM_REQUEST command to
the chip, and the chip then erases the entire EEPROM.  But we don't know how
the latter SIO_ERASE_EEPROM_REQUEST operation is implemented by FT2232x chips:
does the FT2232x chip go through and erase each word individually, or does it
issue an "erase full chip" opcode to the serial EEPROM?  If the latter, then
according to some EEPROM datasheets that operation may not work if the EEPROM
is powered from a 3.3V rail rather than the full USB 5V - may be an issue in
FT2232H-based designs.

In any case our tools provide both ways.  To perform the "automatic full chip
erase" operation, run the following command:

fteeprom-erase <device-selector>

To blank the EEPROM by writing 0xFFFF into each word, run one of the following
pipelines:

ftee-mkblank | fteeprom-prog <device-selector>		-- blank a 93C46 EEPROM
ftee-mkblank -b | fteeprom-prog <device-selector>	-- blank a 93C56 EEPROM
ftee-mkblank -B | fteeprom-prog <device-selector>	-- blank a 93C66 EEPROM

Unbinding of ftdi_sio ttyUSBx devices
=====================================

When invoked without any options, current versions of fteeprom-prog and
fteeprom-erase do NOT unbind the kernel's ftdi_sio driver from any of the chip's
interfaces, thus none of the associated ttyUSB devices disappear.  This behavior
can be modified as follows:

* Both fteeprom-prog and fteeprom-erase support -d1, -d2 and -d4 options.  These
  options instruct the tool to command the kernel to unbind from one, two or
  four interfaces on the target chip - they should be used with FT232x, FT2232x
  and FT4232x, respectively.

* fteeprom-prog running with -r option (FT232R special mode) and no -d options
  defaults to -d1.  This default was made because -r option is intended only for
  FT232R, which is known to be a single-channel chip, and the special magic
  sequence is invasive/disruptive to normal UART operation, hence it isn't
  really compatible with ttyUSB sticking around.  If you are a hacker and you
  really know what you are doing, you can defeat this logic with -r -d0.

Please refer to Replug-after-EEPROM-write article for additional notes.