ftdi protocol handling: everything except mpsse and mcuhost
This commit is contained in:
parent
d2860c8e10
commit
ca11c8e1e1
|
@ -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
|
||||
|
|
|
@ -2,24 +2,8 @@
|
|||
#include "tusb_config.h"
|
||||
#include <tusb.h>
|
||||
|
||||
// for handling USB bulk commands
|
||||
void ftdi_init(void) {
|
||||
|
||||
}
|
||||
void ftdi_deinit(void) {
|
||||
|
||||
}
|
||||
void ftdi_task(void) {
|
||||
|
||||
}
|
||||
|
||||
bool ftdi_control_xfer_cb(uint8_t rhport, uint8_t stage,
|
||||
tusb_control_request_t const* req) {
|
||||
// return true: don't stall
|
||||
// return false: stall
|
||||
|
||||
// not a vendor request -> not meant for this code
|
||||
if (req->bmRequestType_bit.type != TUSB_REQ_TYPE_VENDOR) return true;
|
||||
#include "m_ftdi/bsp-feature.h"
|
||||
#include "m_ftdi/ftdi.h"
|
||||
|
||||
// index = interface number (A/B)
|
||||
// out/in bRequest wValue wIndex data wLength
|
||||
|
@ -28,7 +12,7 @@ bool ftdi_control_xfer_cb(uint8_t rhport, uint8_t stage,
|
|||
// 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?
|
||||
// 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)
|
||||
|
@ -36,7 +20,7 @@ bool ftdi_control_xfer_cb(uint8_t rhport, uint8_t stage,
|
|||
// <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)
|
||||
// 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
|
||||
|
@ -44,18 +28,24 @@ bool ftdi_control_xfer_cb(uint8_t rhport, uint8_t stage,
|
|||
|
||||
// eeprom layout:
|
||||
// max size is 128 words (256 bytes), little-endian
|
||||
// 00: type, driver, .. stuff (chanA byte, chanB byte)
|
||||
// 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: USB version
|
||||
// 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)
|
||||
// TODO: checksum
|
||||
// 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
|
||||
|
||||
|
@ -68,23 +58,227 @@ bool ftdi_control_xfer_cb(uint8_t rhport, uint8_t stage,
|
|||
// 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:
|
||||
// bulk xfer formats: (interface index <-> bulk epno)
|
||||
// * UART: ok I guess
|
||||
// * FIFO: same
|
||||
// * bitbang, sync bitbang: ???? (just data or also clock stuff?? ?)
|
||||
// * MPSSE: see separate PDF
|
||||
// * 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: ???
|
||||
}
|
||||
|
||||
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) {
|
||||
// return true: don't stall
|
||||
// return false: stall
|
||||
|
||||
// not a vendor request -> not meant for this code
|
||||
if (req->bmRequestType_bit.type != TUSB_REQ_TYPE_VENDOR) return true;
|
||||
|
||||
// data stage not needed: data stages only for device->host xfers, never host->device
|
||||
if (stage != CONTROL_STAGE_SETUP) return true;
|
||||
|
||||
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
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
int itfnum = req->wIndex & 0xff;
|
||||
struct ftdi_interface* itf = &ftdi_ifa; // default
|
||||
|
||||
if (itfnum > 2) return false; // bad interface number
|
||||
else if (itfnum == 1) itf = &ftdi_ifa;
|
||||
else if (itfnum == 2) itf = &ftdi_ifb;
|
||||
|
||||
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 */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue