376 lines
14 KiB
C
376 lines
14 KiB
C
// vim: set et:
|
|
|
|
#ifndef FTDI_H_
|
|
#define FTDI_H_
|
|
|
|
#include "tusb_config.h"
|
|
#include <tusb.h>
|
|
|
|
// USB command handling, and mode interfacing stuff
|
|
|
|
void ftdi_init(void);
|
|
void ftdi_deinit(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
|
|
};
|
|
|
|
// mpsse, mcuhost commands
|
|
|
|
// if bit 7 of an MPSSE command byte
|
|
|
|
enum ftdi_mpsse_cflg {
|
|
ftmpsse_negedge_wr = 1<<0, // if 0, output bits on positive clock edige
|
|
ftmpsse_bitmode = 1<<1, // if 0, byte mode
|
|
ftmpsse_negedge_rd = 1<<2, // if 0, input bits on positive clock edge
|
|
ftmpsse_lsbfirst = 1<<3, // if 0, msb first
|
|
ftmpsse_tdiwrite = 1<<4, // 1 == do perform output
|
|
ftmpsse_tdoread = 1<<5, // 1 == do perform input
|
|
ftmpsse_tmswrite = 1<<6, // 1 == do perform output?
|
|
|
|
ftmpsse_specialcmd = 1<<7 // see below enum if set
|
|
};
|
|
// bitmode: 1 length byte, max=7 (#bits = length+1) for separate bits
|
|
// bytemode: 2 length bytes == number of bytes that follow
|
|
// both tdiwrite and tdoread high: only one length value/equal number of bits in/out!
|
|
// if both tdiwrite and tdoread are high, negedge_wr and negedge_rd must differ
|
|
// tms read/writes: readback is from tdo, bit7 in databyte is tdi output, held constant
|
|
// tdiwrite always 0, bitmode 1 in impls, can be ignored I guess
|
|
// also always lsbfirst, but not too hard to support msbfirst too
|
|
// idle levels (of eg. tms/cs) -> set_dirval?
|
|
|
|
enum ftdi_mpssemcu_cmd {
|
|
ftmpsse_set_dirval_lo = 0x80, // sets initial clock level!
|
|
ftmpsse_set_dirval_hi = 0x82,
|
|
ftmpsse_read_lo = 0x81,
|
|
ftmpsse_read_hi = 0x83,
|
|
ftmpsse_loopback_on = 0x84,
|
|
ftmpsse_loopback_off = 0x85,
|
|
ftmpsse_set_clkdiv = 0x86, // period = 12MHz / ((1 + value16) * 2)
|
|
|
|
ftmpsse_flush = 0x87, // flush dev->host usb buffer
|
|
ftmpsse_wait_io_hi = 0x88, // wait for gpiol1/io1 to be high
|
|
ftmpsse_wait_io_lo = 0x89, // wait for gpiol1/io1 to be low
|
|
|
|
// technically ft2232h-only but we can support these, too
|
|
ftmpsse_div5_disable = 0x8a, // ft2232h internally has a 5x faster clock, but slows it down by default
|
|
ftmpsse_div5_enable = 0x8b, // for backwards compat. these two commands enable/disable that slowdown
|
|
ftmpsse_data_3ph_en = 0x8c, // enable 3-phase data
|
|
ftmpsse_data_3ph_dis = 0x8d, // disable 3-phase data
|
|
ftmpsse_clockonly_bits = 0x8e, // enable clock for n bits, no data xfer
|
|
ftmpsse_clockonly_bytes = 0x8f, // enable clock for n bytes, no data xfer
|
|
ftmpsse_clock_wait_io_hi = 0x94, // wait_io_hi + clockonly
|
|
ftmpsse_clock_wait_io_lo = 0x95, // wait_io_lo + clockonly
|
|
ftmpsse_adapclk_enable = 0x96, // enable ARM JTAG adaptive clocking (rtck gpiol3 input)
|
|
ftmpsse_adapclk_disable = 0x97, // disable ARM JTAG adaptive clocking (rtck gpiol3 input)
|
|
ftmpsse_clock_bits_wait_io_hi = 0x9c, // clock_wait_io_hi + clockonly_bits
|
|
ftmpsse_clock_bits_wait_io_lo = 0x9d, // clock_wait_io_lo + clockonly_bits
|
|
ftmpsse_hi_is_tristate = 0x9e, // turns 1 output to tristate for selected outputs
|
|
|
|
ftmcu_flush = 0x87, // flush dev->host usb buffer
|
|
ftmcu_wait_io_hi = 0x88, // wait for gpiol1/io1 to be high
|
|
ftmcu_wait_io_lo = 0x89, // wait for gpiol1/io1 to be low
|
|
|
|
ftmcu_read8 = 0x90,
|
|
ftmcu_read16 = 0x91,
|
|
ftmcu_write8 = 0x92,
|
|
ftmcu_write16 = 0x93
|
|
};
|
|
|
|
// 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_cpufifo = 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 {
|
|
// TODO soft fields maybe, because it's a mess with lots of padding right now
|
|
int index;
|
|
|
|
uint8_t modem_mask;
|
|
uint8_t modem_data;
|
|
|
|
enum ftdi_flowctrl flow;
|
|
|
|
uint32_t baudrate;
|
|
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;
|
|
|
|
uint16_t mcu_addr_latch;
|
|
|
|
// these are for USB bulk cmds etc.
|
|
// "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];
|
|
uint8_t bufbuf [CFG_TUD_VENDOR_RX_BUFSIZE]; // for buffered IO
|
|
uint32_t rxavail, rxpos;
|
|
};
|
|
|
|
extern struct ftdi_interface ftdi_ifa, ftdi_ifb;
|
|
|
|
// interface control stuff
|
|
|
|
static inline enum ftdi_mode ftdi_get_mode_of(enum ftdi_sio_bitmode bb_mode, uint8_t eepmode) {
|
|
if (bb_mode == 0x10) return ftmode_opto;
|
|
|
|
if (bb_mode == sio_mode_reset) {
|
|
return (eepmode << 4) & 0xf0;
|
|
} else return bb_mode & 0xf;
|
|
}
|
|
static inline enum ftdi_mode ftdi_if_get_mode(struct ftdi_interface* itf) {
|
|
return ftdi_get_mode_of(itf->bb_mode, itf->index ? FTDI_EEP_IFB_MODE : FTDI_EEP_IFA_MODE);
|
|
}
|
|
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_deinit(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 olddir, enum ftdi_sio_bitmode oldmode);
|
|
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);
|
|
|
|
|
|
void ftdi_if_mpsse_flush(struct ftdi_interface* itf);
|
|
void ftdi_if_mpsse_wait_io(struct ftdi_interface* itf, bool level);
|
|
|
|
void ftdi_if_mpsse_set_dirval_lo(struct ftdi_interface* itf, uint8_t dir, uint8_t val);
|
|
void ftdi_if_mpsse_set_dirval_hi(struct ftdi_interface* itf, uint8_t dir, uint8_t val);
|
|
uint8_t ftdi_if_mpsse_read_lo(struct ftdi_interface* itf);
|
|
uint8_t ftdi_if_mpsse_read_hi(struct ftdi_interface* itf);
|
|
void ftdi_if_mpsse_loopback(struct ftdi_interface* itf, bool enable);
|
|
void ftdi_if_mpsse_set_clkdiv(struct ftdi_interface* itf, uint16_t div);
|
|
|
|
uint8_t ftdi_if_mpsse_xfer_bits(struct ftdi_interface* itf, int flags, size_t nbits, uint8_t value);
|
|
void ftdi_if_mpsse_xfer_bytes(struct ftdi_interface* itf, int flags, size_t nbytes, uint8_t* dst, const uint8_t* src);
|
|
uint8_t ftdi_if_mpsse_tms_xfer(struct ftdi_interface* itf, int flags, size_t nbits, uint8_t value);
|
|
|
|
void ftdi_if_mpsse_div5(struct ftdi_interface* itf, bool enable);
|
|
void ftdi_if_mpsse_data_3ph(struct ftdi_interface* itf, bool enable);
|
|
void ftdi_if_mpsse_adaptive(struct ftdi_interface* itf, bool enable);
|
|
|
|
void ftdi_if_mpsse_clockonly(struct ftdi_interface* itf, uint32_t cycles);
|
|
void ftdi_if_mpsse_clock_wait_io(struct ftdi_interface* itf, bool level);
|
|
void ftdi_if_mpsse_clockonly_wait_io(struct ftdi_interface* itf, bool level, uint32_t cycles);
|
|
void ftdi_if_mpsse_hi_is_tristate(struct ftdi_interface* itf, uint16_t pinmask);
|
|
|
|
|
|
void ftdi_if_mcuhost_flush(struct ftdi_interface* itf);
|
|
void ftdi_if_mcuhost_wait_io(struct ftdi_interface* itf, bool level);
|
|
|
|
uint8_t ftdi_if_mcuhost_read8 (struct ftdi_interface* itf, uint8_t addr);
|
|
uint8_t ftdi_if_mcuhost_read16(struct ftdi_interface* itf, uint16_t addr);
|
|
void ftdi_if_mcuhost_write8 (struct ftdi_interface* itf, uint8_t addr, uint8_t value);
|
|
void ftdi_if_mcuhost_write16(struct ftdi_interface* itf, uint16_t addr, uint8_t value);
|
|
|
|
void ftdi_if_uart_init(struct ftdi_interface* itf);
|
|
void ftdi_if_mpsse_init(struct ftdi_interface* itf);
|
|
void ftdi_if_asyncbb_init(struct ftdi_interface* itf);
|
|
void ftdi_if_syncbb_init(struct ftdi_interface* itf);
|
|
void ftdi_if_mcuhost_init(struct ftdi_interface* itf);
|
|
void ftdi_if_fifo_init(struct ftdi_interface* itf);
|
|
void ftdi_if_cpufifo_init(struct ftdi_interface* itf);
|
|
|
|
void ftdi_if_uart_deinit(struct ftdi_interface* itf);
|
|
void ftdi_if_mpsse_deinit(struct ftdi_interface* itf);
|
|
void ftdi_if_asyncbb_deinit(struct ftdi_interface* itf);
|
|
void ftdi_if_syncbb_deinit(struct ftdi_interface* itf);
|
|
void ftdi_if_mcuhost_deinit(struct ftdi_interface* itf);
|
|
void ftdi_if_fifo_deinit(struct ftdi_interface* itf);
|
|
void ftdi_if_cpufifo_deinit(struct ftdi_interface* itf);
|
|
|
|
void ftdi_if_uart_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
|
|
void ftdi_if_mpsse_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
|
|
void ftdi_if_asyncbb_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
|
|
void ftdi_if_syncbb_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
|
|
void ftdi_if_mcuhost_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
|
|
void ftdi_if_fifo_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
|
|
void ftdi_if_cpufifo_set_baudrate(struct ftdi_interface* itf, uint32_t baudrate);
|
|
|
|
#endif
|
|
|