ftdi protocol handling: everything except mpsse and mcuhost

This commit is contained in:
Triss 2021-08-28 02:35:58 +02:00
parent d2860c8e10
commit ca11c8e1e1
3 changed files with 550 additions and 79 deletions

View File

@ -8,18 +8,25 @@
#include "vnd_cfg.h"
#include "m_ftdi/bsp-feature.h"
#include "m_ftdi/ftdi.h"
#include "ftdi.h"
static bool data_dirty = false;
void ftdi_eeprom_dirty_set(bool v) { data_dirty = true; }
bool ftdi_eeprom_dirty_get(void) { return data_dirty; }
#ifdef DBOARD_HAS_FTDI
static cothread_t ftdithread;
static uint8_t ftdistack[THREAD_STACK_SIZE];
static cothread_t ftdithread_ifa, ftdithread_ifb;
static uint8_t ftdistack_ifa[THREAD_STACK_SIZE>>1], ftdistack_ifb[THREAD_STACK_SIZE>>1];
static void ftdi_thread_fn(void) {
ftdi_init();
thread_yield();
static void ftdi_thread_fn_ifa(void) {
while (1) {
ftdi_task();
ftdi_task_ifa();
thread_yield();
}
}
static void ftdi_thread_fn_ifb(void) {
while (1) {
ftdi_task_ifb();
thread_yield();
}
}
@ -32,9 +39,16 @@ static void enter_cb(void) {
vnd_cfg_set_itf_num(VND_N_CFG);
#ifdef DBOARD_HAS_FTDI
ftdithread = co_derive(ftdistack, sizeof ftdistack, ftdi_thread_fn);
thread_enter(ftdithread);
ftdithread_ifa = co_derive(ftdistack_ifa, sizeof ftdistack_ifa, ftdi_thread_fn_ifa);
ftdithread_ifb = co_derive(ftdistack_ifb, sizeof ftdistack_ifb, ftdi_thread_fn_ifb);
#endif
if (!data_dirty) {
struct mode_info mi = storage_mode_get_info(5);
if (mi.size != 0 && mi.version == 0x0010) {
storage_mode_read(5, ftdi_eeprom, 0, sizeof ftdi_eeprom);
}
}
}
static void leave_cb(void) {
#ifdef DBOARD_HAS_FTDI
@ -44,7 +58,10 @@ static void leave_cb(void) {
static void task_cb(void) {
#ifdef DBOARD_HAS_FTDI
ftdi_task();
tud_task();
thread_enter(ftdithread_ifa);
tud_task();
thread_enter(ftdithread_ifb);
#endif
}
@ -133,15 +150,15 @@ static const char* string_desc_arr[] = {
[STRID_CONFIG] = "Configuration descriptor",
// max string length check: |||||||||||||||||||||||||||||||
[STRID_IF_VND_CFG ] = "Device cfg/ctl interface",
[STRID_IF_VND_FTDI_IFA] = "FT2232C interface A",
[STRID_IF_VND_FTDI_IFB] = "FT2232C interface B",
[STRID_IF_VND_FTDI_IFA] = "DragonProbe FT2232D interface A",
[STRID_IF_VND_FTDI_IFB] = "DragonProbe FT2232D interface B",
#ifdef USE_USBCDC_FOR_STDIO
[STRID_IF_CDC_STDIO] = "stdio CDC interface (debug)",
#endif
};
// clang-format on
static const tusb_desc_device_t desc_device = {
static tusb_desc_device_t desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0110, // TODO: 0x0200 ? is an eeprom option
@ -165,10 +182,30 @@ static const uint8_t* my_descriptor_device_cb(void) {
return (const uint8_t*)&desc_device;
}
#if CFG_TUD_CDC > 0
static void my_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* line_coding) {
switch (itf) {
#ifdef USE_USBCDC_FOR_STDIO
case CDC_N_STDIO:
stdio_usb_line_coding_cb(line_coding);
break;
#endif
}
}
#endif
static uint16_t my_get_size(void) { return sizeof ftdi_eeprom; }
static void my_get_data(void* dst, size_t offset, size_t maxsize) {
memcpy(dst, (const uint8_t*)ftdi_eeprom + offset, maxsize);
data_dirty = false;
}
static bool my_is_dirty(void) { return data_dirty; }
extern struct mode m_05_ftdi;
// clang-format off
struct mode m_05_ftdi = {
.name = "FTDI FT2232C (single-channel) emulation mode",
.name = "FTDI FT2232D emulation mode",
.version = 0x0010,
.n_string_desc = sizeof(string_desc_arr)/sizeof(string_desc_arr[0]),
@ -180,10 +217,21 @@ struct mode m_05_ftdi = {
.task = task_cb,
.handle_cmd = handle_cmd_cb,
.storage = {
.stclass = mode_storage_512b,
.get_size = my_get_size,
.get_data = my_get_data,
.is_dirty = my_is_dirty
},
#ifdef DBOARD_HAS_FTDI
.tud_descriptor_device_cb = my_descriptor_device_cb,
#endif
#if CFG_TUD_CDC > 0
.tud_cdc_line_coding_cb = my_cdc_line_coding_cb,
#endif
#ifdef DBOARD_HAS_FTDI
.tud_vendor_control_xfer_cb = ftdi_control_xfer_cb,
#endif

View File

@ -2,16 +2,184 @@
#include "tusb_config.h"
#include <tusb.h>
// for handling USB bulk commands
void ftdi_init(void) {
#include "m_ftdi/bsp-feature.h"
#include "m_ftdi/ftdi.h"
// index = interface number (A/B)
// out/in bRequest wValue wIndex data wLength
// RESET: out 0 0 index
// TCIFLUSH: out 0 2 index
// TCOFLUSH: out 0 1 index
// SETMODEMCTRL:out 1 (mask:8<<8 | data:8) // bit0=dtr bit1=rts
// SETFLOWCTRL: out 2 xon?1:0 (flowctrl | index) // flowctrl: 0=disable, 1=ctsrts, 2=dtrdsr 4=xonxoff FROM VALUE, not set/reset!
// SETBAUDRATE: out 3 brate brate24|index // 48 MHz clocks, /16? , baudrate is 24bit, highest byte in index MSB
// SETLINEPROP: out 4 (break:1<<14 | stop:2<<11 | parity:3<<8 | bits:8) ; break: off/on, stop=1/15/2, parity=none/odd/even/mark/space; bits=7/8
// POLLMODEMSTAT:in 5 0 index &modemstat len=2 // first byte: bit0..3=0 bit4=cts bit5=dts bit6=ri bit7=rlsd ; second byte: bit0=dr bit1=oe bit2=pe bit3=fe bit4=bi bit5=thre bit6=temt bit7=fifoerr
// SETEVENTCHAR:out 6 (endis:1<<8 | char)
// SETERRORCHAR:out 7 (endir:1<<8 | char)
// <there is no bReqest 8>
// SETLATENCY: out 9 latency(1..255)
// GETLATENCY: in 0xa 0 index &latency len=1
// SETBITBANG: out 0xb dirmask:8<<8 | mode:8 // mode: MPSSE mode: 0=serial/fifo, 1=bitbang, 2=mpsse, 4=syncbb, 8=mcu, 16=opto
// READPINS: in 0xc 0 index &pins len=1
// READEEPROM: in 0x90 0 eepaddr &val 2 // eepaddr in 16-bit units(?)
// WRITEEEPROM: out 0x91 value eepaddr
// ERASEEEPROM: out 0x92 0 0
// eeprom layout:
// max size is 128 words (256 bytes), little-endian
// 00: type, driver, .. stuff (chanA byte, chanB byte) -> TODO: what does this mean exactly?
// 0=UART 1=FIFO 2=opto 4=CPUFIFO 8=FT1284
// 01: VID
// 02: PID
// 03: bcdDevice
// 04: byte08: flags: bit7=1 bit6=1=selfpowered bit5=1=remotewakeup ; byte09=max power, 2mA units
// 05: bit0=epin isochr, bit1=epout isochr, bit2=suspend pulldn, bit3=serialno use, bit4=usbver change, bit5..7=0
// 06: bcdUSB
// 07: byte0E=manufstr.off-0x80, byte0F=manufstr.len (in bytes, but 16-bit ascii)
// 08: ^ but product
// 09: ^ but serial
// 0a: chip type (66 etc -> we emulate '00', internal)
// "strings start at 0x96 for ft2232c" (byte addr)
// checksum:
// initial: 0xaaaa
// digest 1 byte:
// checksum = (checksum ^ word) <<< 1;
// placed at last word of EEPROM
// ftdi_write_data(), ftdi_read_data(): bulk xfer
// input/tristate: 200K pullup
// SI/WU: let's ignore this
// UART mode: standard stuff
// FIFO mode: RDF#=0: enable output. RD# rising when RXF#=0: fetch next byte
// TXF#=0: enable input. WR falling when TXF#=0: write byte
// bitbang mode: ^ similar, no RDF#/TXF#, RD#/WR# (now WR# jenai WR) pos depends on UART mode std (UART vs FIFO)
// sync bitbang: doesn't use RD#/WR#, clocked by baudrate control
// MPSSE: lots of magic. TCK/SK, TDI/DO, TDO/DI, TMS/CS, GPIOL0..3, GPIOH0..3(7?)
// bulk xfer formats: (interface index <-> bulk epno)
// * UART: ok I guess
// * FIFO: same
// * bitbang, sync bitbang: ???? (just data or also clock stuff?? ?)
// * MPSSE, MCU: see separate PDF
// * CPUFIFIO: same as FIFO ig
// * opto, FT1284: not supported
#ifdef DBOARD_HAS_FTDI
uint32_t __builtin_ctz(uint32_t v);
uint16_t ftdi_eeprom[128];
void ftdi_init(void) {
// init eeprom defaults
memset(ftdi_eeprom, 0xff, sizeof ftdi_eeprom);
ftdi_eeprom[ 0] = 0x0000; // both default to UART
ftdi_eeprom[ 1] = 0x0403; // idVendor
ftdi_eeprom[ 2] = 0x6010; // idProduct
ftdi_eeprom[ 3] = 0x0500; // bcdDevice
ftdi_eeprom[ 4] = 0x5080; // 100 mA, no flags
ftdi_eeprom[ 5] = 0x0000; // more flags
ftdi_eeprom[ 6] = 0x0110; // bcdUSB
ftdi_eeprom[ 7] = 0x0000; // no manuf. str (TODO?)
ftdi_eeprom[ 8] = 0x0000; // no prod. str (TODO?)
ftdi_eeprom[ 9] = 0x0000; // no serial str (TODO?)
ftdi_eeprom[10] = 0x0000; // internal chip
ftdi_eeprom[0x7f] = ftdi_eeprom_checksum_calc(ftdi_eeprom, 0x7f);
ftdi_ifa.index = 0;
ftdi_ifb.index = 1;
ftdi_if_init(&ftdi_ifa);
ftdi_if_init(&ftdi_ifb);
}
void ftdi_deinit(void) {
// TODO: ???
}
void ftdi_task(void) {
typedef void (*ftfifo_write_fn)(struct ftdi_interface*, const uint8_t*, size_t);
typedef size_t (*ftfifo_read_fn)(struct ftdi_interface*, uint8_t*, size_t);
struct ftfifo_fns {
ftfifo_write_fn write;
ftfifo_read_fn read ;
};
static const struct ftfifo_fns fifocbs[] = {
.{ ftdi_if_uart_write, ftdi_if_uart_read }, // technically mpsse
.{ ftdi_if_asyncbb_write, ftdi_if_asyncbb_read },
.{ ftdi_if_syncbb_write, ftdi_if_syncbb_read },
.{ NULL, NULL }, // mcuhost
.{ ftdi_if_fifo_write, ftdi_if_fifo_read },
.{ NULL, NULL }, // opto
.{ ftdi_if_cpufifo_write, ftdi_if_cpufifo_read },
.{ NULL, NULL }, // ft1284
};
// for handling USB bulk commands
static void ftdi_task(int itfnum) {
if (!tud_vendor_n_mounted(itfnum)) return; // can't do much in this case
struct ftdi_interface* itf;
if (itfnum == VND_N_FTDI_IFA) itf = &ftdi_ifa;
else if (itfnum == VND_N_FTDI_IFB) itf = &ftdi_ifb;
else return;
// for UART, FIFO, asyncbb, syncbb, and CPUfifo modes, operation is
// relatively straightforward: the device acts like some sort of FIFO, so
// it's just shoving bytes to the output pins. MPSSE and MCU host emulation
// modes are more difficult, as the bulk data actually has some form of
// protocol.
enum ftdi_mode mode = ftdi_if_get_mode(itf);
struct ftfifo_fns fifocb = fifocbs[(mode == 0) ? 0 : __builtin_ctz(mode)];
uint32_t avail;
switch (mode) {
case ftmode_uart : case ftmode_fifo : case ftmode_cpufifo:
case ftmode_asyncbb: case ftmode_syncbb:
if (fifocb.read == NULL || fifocb.write == NULL) goto case default; // welp
avail = tud_vendor_n_available(itfnum);
if (avail) {
tud_vendor_n_read(itfnum, itf->writebuf, avail);
fifocb.write(itf, itf->writebuf, avail);
}
do {
avail = fifocb.read(itf, itf->readbuf, sizeof itf->readbuf);
if (avail) tud_vendor_n_write(itfnum, itf->readbuf, avail);
} while (avail == sizeof itf->readbuf);
break;
case ftmode_mpsse: break; // BIG TODO
case ftmode_mcuhost: break; // BIG TODO
default: // drop incoming data so that the pipes don't get clogged. can't do much else
avail = tud_vendor_n_available(itfnum);
if (avail) tud_vendor_n_read(itfnum, itf->writebuf, avail);
break;
}
}
void ftdi_task_ifa(void) { ftdi_task(VND_N_FTDI_IFA); }
void ftdi_task_ifb(void) { ftdi_task(VND_N_FTDI_IFB); }
#define FT2232D_CLOCK (48*1000*1000)
uint32_t ftdi_if_decode_baudrate(uint32_t enc_brate) { // basically reversing libftdi ftdi_to_clkbits
static const uint8_t ftdi_brate_frac_lut[8] = { 0, 4, 2, 1, 3, 5, 6, 7 };
// special cases
if (enc_brate == 0) return FT2232D_CLOCK >> 4;
else if (enc_brate == 1) return FT2232D_CLOCK / 24;
else if (enc_brate == 2) return FT2232D_CLOCK >> 5;
uint32_t div = (enc_brate & 0x7fff) << 3; // integer part
uint32_t div = div | ftdi_brate_frac_lut[(enc_bate >> 14) & 7];
uint32_t baud = FT2232D_CLOCK / div;
if (baud & 1) baud = (baud >> 1) + 1; // raunding
else baud >>= 1;
return baud;
}
static uint8_t control_buf[2];
bool ftdi_control_xfer_cb(uint8_t rhport, uint8_t stage,
tusb_control_request_t const* req) {
@ -21,70 +189,96 @@ bool ftdi_control_xfer_cb(uint8_t rhport, uint8_t stage,
// not a vendor request -> not meant for this code
if (req->bmRequestType_bit.type != TUSB_REQ_TYPE_VENDOR) return true;
// index = interface number (A/B)
// out/in bRequest wValue wIndex data wLength
// RESET: out 0 0 index
// TCIFLUSH: out 0 2 index
// TCOFLUSH: out 0 1 index
// SETMODEMCTRL:out 1 (mask:8<<8 | data:8) // bit0=dtr bit1=rts
// SETFLOWCTRL: out 2 xon?1:0 (flowctrl | index) // flowctrl: 0=disable, 1=ctsrts, 2=dtrdsr 4=xonxoff FROM VALUE, not set/reset!
// SETBAUDRATE: out 3 brate index // 48 MHz clocks, /16?
// SETLINEPROP: out 4 (break:1<<14 | stop:2<<11 | parity:3<<8 | bits:8) ; break: off/on, stop=1/15/2, parity=none/odd/even/mark/space; bits=7/8
// POLLMODEMSTAT:in 5 0 index &modemstat len=2 // first byte: bit0..3=0 bit4=cts bit5=dts bit6=ri bit7=rlsd ; second byte: bit0=dr bit1=oe bit2=pe bit3=fe bit4=bi bit5=thre bit6=temt bit7=fifoerr
// SETEVENTCHAR:out 6 (endis:1<<8 | char)
// SETERRORCHAR:out 7 (endir:1<<8 | char)
// <there is no bReqest 8>
// SETLATENCY: out 9 latency(1..255)
// GETLATENCY: in 0xa 0 index &latency len=1
// SETBITBANG: out 0xb dirmask:8<<8 | mode:8 // mode: MPSSE mode: 0=serial/fifo, 1=bitbang, 2=mpsse, 4=syncbb, (8=mcu 16=opto)
// READPINS: in 0xc 0 index &pins len=1
// READEEPROM: in 0x90 0 eepaddr &val 2 // eepaddr in 16-bit units(?)
// WRITEEEPROM: out 0x91 value eepaddr
// ERASEEEPROM: out 0x92 0 0
// data stage not needed: data stages only for device->host xfers, never host->device
if (stage != CONTROL_STAGE_SETUP) return true;
// eeprom layout:
// max size is 128 words (256 bytes), little-endian
// 00: type, driver, .. stuff (chanA byte, chanB byte)
// 01: VID
// 02: PID
// 03: bcdDevice
// 04: byte08: flags: bit7=1 bit6=1=selfpowered bit5=1=remotewakeup ; byte09=max power, 2mA units
// 05: bit0=epin isochr, bit1=epout isochr, bit2=suspend pulldn, bit3=serialno use, bit4=usbver change, bit5..7=0
// 06: USB version
// 07: byte0E=manufstr.off-0x80, byte0F=manufstr.len (in bytes, but 16-bit ascii)
// 08: ^ but product
// 09: ^ but serial
// 0a: chip type (66 etc)
// TODO: checksum
// tud_control_status(rhport, req); : acknowledge
// tud_control_xfer(rhport, req, bufaddr, size); : ack + there's a data phase with stuff to do
// write: send bufaddr value. read: data stage will have bufaddr filled out
// ftdi_write_data(), ftdi_read_data(): bulk xfer
// do EEPROM stuff first, as these don't use wIndex as channel select
uint16_t tmp16;
switch (req->bRequest) {
case sio_readeeprom:
tmp16 = ftdi_eeprom[req->wIndex & 0x7f];
control_buf[0] = tmp16 & 0xff;
control_buf[1] = tmp16 >> 8;
return tud_control_xfer(rhport, req, control_buf, 2);
case sio_writeeeprom:
ftdi_eeprom[req->wIndex & 0x7f] = req->wValue;
return tud_control_status(rhport, req);
case sio_eraseeeprom:
memset(ftdi_eeprom, 0xff, sizeof ftdi_eeprom);
return tud_control_status(rhport, req);
}
// input/tristate: 200K pullup
// SI/WU: let's ignore this
// UART mode: standard stuff
// FIFO mode: RDF#=0: enable output. RD# rising when RXF#=0: fetch next byte
// TXF#=0: enable input. WR falling when TXF#=0: write byte
// bitbang mode: ^ similar, no RDF#/TXF#, RD#/WR# (now WR# jenai WR) pos depends on UART mode std (UART vs FIFO)
// sync bitbang: doesn't use RD#/WR#, clocked by baudrate control
// MPSSE: lots of magic. TCK/SK, TDI/DO, TDO/DI, TMS/CS, GPIOL0..3, GPIOH0..3(7?)
int itfnum = req->wIndex & 0xff;
struct ftdi_interface* itf = &ftdi_ifa; // default
// bulk xfer formats:
// * UART: ok I guess
// * FIFO: same
// * bitbang, sync bitbang: ???? (just data or also clock stuff?? ?)
// * MPSSE: see separate PDF
if (itfnum > 2) return false; // bad interface number
else if (itfnum == 1) itf = &ftdi_ifa;
else if (itfnum == 2) itf = &ftdi_ifb;
switch (stage) {
case CONTROL_STAGE_SETUP:
// tud_control_status(rhport, req); : acknowledge
// tud_control_xfer(rhport, req, bufaddr, size); : ack + there's a data phase with stuff to do
// write: send bufaddr value. read: data stage will have bufaddr filled out
return false; // stall if not recognised
case CONTROL_STAGE_DATA:
// TODO: stuff?
// 'bufaddr' from setup stage is now filled in for reads
return true;
default: return true; // ignore
switch (req->bRequest) {
case sio_cmd:
if (req->wValue == sio_reset) ftdi_if_sio_reset(itf);
else if (req->wValue == sio_tciflush) ftdi_if_sio_tciflush(itf);
else if (req->wValue == sio_tcoflush) ftdi_if_sio_tcoflush(itf);
else return false; // unk
return tud_control_status(rhport, req);
case sio_setmodemctrl:
ftdi_if_set_modemctrl(itf,
itf->modem_mask = (req->wValue >> 8),
itf->modem_data = (req->wValue & 0xff));
return tud_control_status(rhport, req);
case sio_setflowctrl: {
enum ftdi_flowctrl flow = (req->wIndex >> 8);
if (!req->wValue)
flow = (flow & ~ftflow_xonxoff) | (itf->flow & ftflow_xonxoff);
ftdi_if_set_flowctrl(itf, itf->flow = flow);
} return tud_control_status(rhport, req);
case sio_setbaudrate: {
uint32_t enc_brate = (uint32_t)req->wValue | ((uint32_t)(req->wIndex & 0xff00) << 8);
uint32_t brate = ftdi_if_decode_baudrate(enc_brate);
ftdi_if_set_baudrate(itf, itf->baudrate = brate);
} return tud_control_status(rhport, req);
case sio_setlineprop:
ftdi_if_set_lineprop(itf, itf->lineprop = req->wValue);
return tud_control_status(rhport, req);
case sio_pollmodemstat:
tmp16 = ftdi_if_poll_modemstat(itf);
control_buf[0] = tmp16 & 0xff;
control_buf[1] = tmp16 >> 8;
return tud_control_xfer(rhport, req, control_buf, 2);
case sio_seteventchar:
if (req->wValue >> 8) itf->charen |= eventchar_enable;
else if (itf->charen & eventchar_enable) itf->charen ^= eventchar_enable;
ftdi_if_set_eventchar(itf, req->wValue >> 8,
itf->eventchar = (req->wValue & 0xff));
return tud_control_status(rhport, req);
case sio_seterrorchar:
if (req->wValue >> 8) itf->charen |= errorchar_enable;
else if (itf->charen & errorchar_enable) itf->charen ^= errorchar_enable;
ftdi_if_set_errorchar(itf, req->wValue >> 8,
itf->errorchar = (req->wValue & 0xff));
return tud_control_status(rhport, req);
case sio_setlatency:
ftdi_if_set_latency(itf, itf->latency = (req->wValue & 0xff));
return tud_control_status(rhport, req);
case sio_getlatency:
control_buf[0] = ftdi_if_get_latency(itf);
return tud_control_xfer(rhport, req, control_buf, 1);
case sio_setbitbang:
ftdi_if_set_bitbang(itf,
itf->bb_dir = (req->wValue >> 8),
itf->bb_mode = (req->wValue & 0xff));
return tud_control_status(rhport, req);
case sio_readpins:
control_buf[0] = ftdi_if_read_pins(itf);
return tud_control_xfer(rhport, req, control_buf, 1);
default: return false; // stall if not recognised
}
}
#endif /* DBOARD_HAS_FTDI */

View File

@ -6,13 +6,242 @@
#include "tusb_config.h"
#include <tusb.h>
// for handling USB bulk commands
// USB command handling, and mode interfacing stuff
void ftdi_init(void);
void ftdi_deinit(void);
void ftdi_task(void);
// have separate tasks for the separate bulk endpoints so that a wait-for-data
// in one ep won't cause the otehr to stall, too
void ftdi_task_ifa(void);
void ftdi_task_ifb(void);
bool ftdi_control_xfer_cb(uint8_t rhport, uint8_t ep_addr,
tusb_control_request_t const* req);
void ftdi_eeprom_dirty_set(bool v);
bool ftdi_eeprom_dirty_get(void);
extern uint16_t ftdi_eeprom[128];
#define FTDI_EEP_IFA_MODE (ftdi_eeprom[0] & 0xff)
#define FTDI_EEP_IFB_MODE (ftdi_eeprom[0] >> 8)
#define FTDI_EEP_IDVENDOR (ftdi_eeprom[1])
#define FTDI_EEP_IDPRODUCT (ftdi_eeprom[2])
#define FTDI_EEP_BCDDEVICE (ftdi_eeprom[3])
#define FTDI_EEP_PWFLAGS (ftdi_eeprom[4] & 0xff)
#define FTDI_EEP_MAXAMP (ftdi_eeprom[4] >> 8)
#define FTDI_EEP_USBFLAGS (ftdi_eeprom[5])
#define FTDI_EEP_BCDUSB (ftdi_eeprom[6])
#define FTDI_EEP_MANUF_OFF (ftdi_eeprom[7] & 0xff)
#define FTDI_EEP_MANUF_LEN (ftdi_eeprom[7] >> 8)
#define FTDI_EEP_PROD_OFF (ftdi_eeprom[8] & 0xff)
#define FTDI_EEP_PROD_LEN (ftdi_eeprom[8] >> 8)
#define FTDI_EEP_SERIAL_OFF (ftdi_eeprom[9] & 0xff)
#define FTDI_EEP_SERIAL_LEN (ftdi_eeprom[9] >> 8)
#define FTDI_EEP_CHIPTYPE (ftdi_eeprom[10])
static inline uint16_t ftdi_eeprom_checksum_init(void) {
return 0xaaaa;
}
static inline uint16_t ftdi_eeprom_checksum_digest(uint16_t val, const uint16_t* data, size_t len) {
for (size_t i = 0; i < len; ++i) {
val = (val ^ data[i]);
val = (val << 1) | (val >> 15);
}
return val;
}
static inline uint16_t ftdi_eeprom_checksum_calc(const uint16_t* data, size_t len) {
return ftdi_eeprom_checksum_digest(ftdi_eeprom_checksum_init(), data, len);
}
// USB protocol stuff
enum ftdi_request {
sio_cmd = 0,
sio_setmodemctrl = 1,
sio_setflowctrl = 2,
sio_setbaudrate = 3,
sio_setlineprop = 4,
sio_pollmodemstat = 5,
sio_seteventchar = 6,
sio_seterrorchar = 7,
sio_setlatency = 9,
sio_getlatency = 10,
sio_setbitbang = 11,
sio_readpins = 12,
sio_readeeprom = 0x90,
sio_writeeeprom = 0x91,
sio_eraseeeprom = 0x92
};
enum ftdi_sio_cmd {
sio_reset = 0,
sio_tciflush = 2,
sio_tcoflush = 1
};
enum ftdi_sio_lineprop {
sio_break_on = 1<<14,
sio_break_off = 0<<14,
sio_break__mask = 1<<14,
sio_stop_1 = 0<<11,
sio_stop_15 = 1<<11, // deprecated?
sio_stop_2 = 2<<11,
sio_stop__mask = 3<<11,
sio_parity_none = 0<<8,
sio_parity_odd = 1<<8,
sio_parity_even = 2<<8,
sio_parity_mark = 3<<8,
sio_parity_space = 4<<8,
sio_parity__mask = 7<<8,
sio_bits_7 = 7<<0,
sio_bits_8 = 8<<0,
sio_bits__mask = 0xff<<0,
};
enum ftdi_sio_modemstat {
sio_modem_cts = 1<< 4, // Clear to Send active
sio_modem_dts = 1<< 5, // Data Set Ready active
sio_modem_ri = 1<< 6, // Ring Indicator active
sio_modem_rlsd = 1<< 7, // Receive Line Signal Detect active
sio_modem_dr = 1<< 8, // Data Ready
sio_modem_oe = 1<< 9, // Overrun Error
sio_modem_pe = 1<<10, // Parity Error
sio_modem_fe = 1<<11, // Framing Error
sio_modem_bi = 1<<12, // Break Interrupt
sio_modem_thre = 1<<13, // Transmitter Holding REgister
sio_modem_temt = 1<<14, // Transmitter Empty
sio_modem_fifoerr = 1<<15 // Error in receive FIFO
};
enum ftdi_sio_bitmode {
sio_mode_reset = 0, // i.e. from EEPROM
sio_mode_bitbang = 1,
sio_mode_mpsse = 2,
sio_mode_syncbb = 4,
sio_mode_mcu = 8,
// 0x10: opto
// 0x20: cbus bitbang (R-type only)
// 0x40: sync fifo (2232h) // like regular fifo mode, but with clock output
// 0x80: ft1284 (232h, not 2232d)
};
enum ftdi_eep_defmode {
fteep_mode_uart = 0,
fteep_mode_fifo = 1,
fteep_mode_opto = 2, // not implementing this here
fteep_mode_cpu = 4,
fteep_mode_ft1284 = 8, // not impl. on 2232d, 232h-only
};
// internal use only types
enum ftdi_mode { // combines EEPROM setting + bitmode
ftmode_uart = 0,
ftmode_mpsse = 1,
ftmode_asyncbb = 2,
ftmode_syncbb = 4,
ftmode_mcuhost = 8,
ftmode_fifo = 0x10,
ftmode_opto = 0x20, // not implementing this here
ftmode_cpu = 0x40,
ftmode_ft1284 = 0x80, // not impl. on 2232d
};
enum ftdi_flowctrl {
ftflow_none = 0,
ftflow_ctsrts = 1,
ftflow_dtrdts = 2,
ftflow_xonxoff = 4,
};
struct ftdi_interface {
int index;
uint8_t modem_mask;
uint8_t modem_data;
enum ftdi_flowctrl flow;
uint32_t baudrate; // TODO: what are the clock units of this? clock ticks of a 48MHz clock divided by 16?
enum ftdi_sio_lineprop lineprop;
enum ftdi_sio_modemstat modemstat;
uint8_t eventchar, errorchar;
enum { eventchar_enable = 1<<0, errorchar_enable = 1<<1 } charen;
uint8_t latency; // latency timer. TODO: implement this
uint8_t bb_dir; // high/1 bit = output, 0=input
enum ftdi_sio_bitmode bb_mode;
// "write" means write to hardware output pins
// "read" means read from hardware input pins
uint8_t writebuf[CFG_TUD_VENDOR_RX_BUFSIZE];
uint8_t readbuf [CFG_TUD_VENDOR_TX_BUFSIZE];
};
extern struct ftdi_interface ftdi_ifa, ftdi_ifb;
// interface control stuff
static inline enum ftdi_mode ftdi_if_get_mode(struct ftdi_interface* itf) {
if (itf->bb_mode == 0x10) return ftmode_opto;
if (itf->bb_mode == sio_mode_reset) {
return (eepmode << 4) & 0xf0;
} else return itf->bb_mode & 0xf;
}
uint32_t ftdi_if_decode_baudrate(uint32_t enc_brate);
// control request stuff. implemented by bsp driver
void ftdi_if_init(struct ftdi_interface* itf);
void ftdi_if_sio_reset(struct ftdi_interface* itf);
void ftdi_if_sio_tciflush(struct ftdi_interface* itf);
void ftdi_if_sio_tcoflush(struct ftdi_interface* itf);
void ftdi_if_set_modemctrl(struct ftdi_interface* itf, uint8_t mask, uint8_t data);
void ftdi_if_set_flowctrl(struct ftdi_interface* itf, enum ftdi_flowctrl flow);
void ftdi_if_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
void ftdi_if_set_lineprop(struct ftdi_interface* itf, enum ftdi_sio_lineprop lineprop);
enum ftdi_sio_modemstat ftdi_if_poll_modemstat(struct ftdi_interface* itf);
void ftdi_if_set_eventchar(struct ftdi_interface* itf, bool enable, uint8_t evchar);
void ftdi_if_set_errorchar(struct ftdi_interface* itf, bool enable, uint8_t erchar);
void ftdi_if_set_latency(struct ftdi_interface* itf, uint8_t latency);
uint8_t ftdi_if_get_latency(struct ftdi_interface* itf);
void ftdi_if_set_bitbang(struct ftdi_interface* itf, uint8_t dirmask, enum ftdi_sio_bitmode);
uint8_t ftdi_if_read_pins(struct ftdi_interface* itf);
// bulk commands (also implemented by bsp driver)
// "write" means write to hardware output pins
// "read" means read from hardware input pins
void ftdi_if_uart_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize);
size_t ftdi_if_uart_read (struct ftdi_interface* itf, uint8_t* data, size_t maxsize);
void ftdi_if_fifo_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize);
size_t ftdi_if_fifo_read (struct ftdi_interface* itf, uint8_t* data, size_t maxsize);
void ftdi_if_cpufifo_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize);
size_t ftdi_if_cpufifo_read (struct ftdi_interface* itf, uint8_t* data, size_t maxsize);
void ftdi_if_asyncbb_write(struct ftdi_interface* itf, const uint8_t* data, size_t datasize);
size_t ftdi_if_asyncbb_read (struct ftdi_interface* itf, uint8_t* data, size_t maxsize);
void ftdi_if_syncbb_write (struct ftdi_interface* itf, const uint8_t* data, size_t datasize);
size_t ftdi_if_syncbb_read (struct ftdi_interface* itf, uint8_t* data, size_t maxsize);
// TODO: mpsse, mcuhost
#endif