diff --git a/src/m_ftdi/ftdi.c b/src/m_ftdi/ftdi.c index 0a07dd1..2afe31c 100644 --- a/src/m_ftdi/ftdi.c +++ b/src/m_ftdi/ftdi.c @@ -156,6 +156,7 @@ static void ftdi_task(int itfnum) { enum ftdi_mode mode = ftdi_if_get_mode(itf); struct ftfifo_fns fifocb = fifocbs[(mode == 0) ? 0 : __builtin_ctz(mode)]; uint32_t avail; + uint8_t cmdbyte; switch (mode) { case ftmode_uart : case ftmode_fifo : case ftmode_cpufifo: case ftmode_asyncbb: case ftmode_syncbb: @@ -173,8 +174,159 @@ static void ftdi_task(int itfnum) { 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 + case ftmode_mpsse: + avail = 0; + + switch ((cmdbyte = vnd_read_byte(itf, itfnum))) { + case ftmpsse_set_dirval_lo: // low byte of output gpio, not to low level + itf->writebuf[0] = vnd_read_byte(itf, itfnum); // val + itf->writebuf[1] = vnd_read_byte(itf, itfnum); // dir + ftdi_if_mpsse_set_dirval_lo(itf, itf->writebuf[1], itf->writebuf[0]); + break; + case ftmpsse_set_dirval_hi: + itf->writebuf[0] = vnd_read_byte(itf, itfnum); // val + itf->writebuf[1] = vnd_read_byte(itf, itfnum); // dir + ftdi_if_mpsse_set_dirval_hi(itf, itf->writebuf[1], itf->writebuf[0]); + break; + case ftmpsse_read_lo: + itf->readbuf[0] = ftdi_if_mpsse_read_lo(itf); + avail = 1; + break; + case ftmpsse_read_hi: + itf->readbuf[0] = ftdi_if_mpsse_read_hi(itf); + avail = 1; + break; + case ftmpsse_loopback_on : ftdi_if_mpsse_loopback(itf, true ); break; + case ftmpsse_loopback_off: ftdi_if_mpsse_loopback(itf, false); break; + case ftmpsse_set_clkdiv: + avail = vnd_read_byte(itf, itfnum); + avail |= (uint32_t)vnd_read_byte(itf, itfnum) << 8; + ftdi_if_mpsse_set_clkdiv(itf, (uint16_t)avail); + avail = 0; + break; + + case ftmpsse_flush: ftdi_if_mpsse_flush(itf); break; + case ftmpsse_wait_io_hi: ftdi_if_mpsse_wait_io(itf, true ); break; + case ftmpsse_wait_io_lo: ftdi_if_mpsse_wait_io(itf, false); break; + + case ftmpsse_div5_disable: ftdi_if_mpsse_div5(itf, false); break; + case ftmpsse_div5_enable : ftdi_if_mpsse_div5(itf, true ); break; + case ftmpsse_data_3ph_en : ftdi_if_mpsse_data_3ph(itf, true ); break; + case ftmpsse_data_3ph_dis: ftdi_if_mpsse_data_3ph(itf, false); break; + case ftmpsse_clockonly_bits: ftdi_if_mpsse_clockonly(itf, vnd_read_byte(itf, itfnum)); break; + case ftmpsse_clockonly_bytes: + avail = vnd_read_byte(itf, itfnum); + avail |= (uint32_t)vnd_read_byte(itf, itfnum) << 8; + ftdi_if_mpsse_clockonly(itf, avail); + avail = 0; + break; + case ftmpsse_clock_wait_io_hi: ftdi_if_mpsse_clock_wait_io(itf, true ); break; + case ftmpsse_clock_wait_io_lo: ftdi_if_mpsse_clock_wait_io(itf, false); break; + case ftmpsse_clock_adapclk_enable : ftdi_if_mpsse_adaptive(itf, true ); break; + case ftmpsse_clock_adapclk_disable: ftdi_if_mpsse_adaptive(itf, false); break; + case ftmpsse_clock_bits_wait_io_hi: + avail = vnd_read_byte(itf, itfnum); + avail |= (uint32_t)vnd_read_byte(itf, itfnum) << 8; + ftdi_if_mpsse_clockonly_wait_io(itf, true, avail); + avail = 0; + break; + case ftmpsse_clock_bits_wait_io_lo: + avail = vnd_read_byte(itf, itfnum); + avail |= (uint32_t)vnd_read_byte(itf, itfnum) << 8; + ftdi_if_mpsse_clockonly_wait_io(itf, false, avail); + avail = 0; + break; + case ftmpsse_hi_is_tristate: + avail = vnd_read_byte(itf, itfnum); + avail |= (uint32_t)vnd_read_byte(itf, itfnum) << 8; + ftdi_if_mpsse_hi_is_tristate(itf, avail); + avail = 0; + break; + + default: + if (!(cmdbyte & ftmpsse_specialcmd)) { + if (cmdbyte & ftmpsse_tmswrite) { + if (cmdbyte & ftmpsse_bitmode) { + itf->writebuf[0] = vnd_read_byte(itf, itfnum); // number of bits + itf->writebuf[1] = vnd_read_byte(itf, itfnum); // data bits to output + itf->readbuf[0] = ftdi_if_mpsse_tms_xfer(itf, cmdbyte, itf->writebuf[0], itf->writebuf[1]); + if (cmdbyte & tdoread) avail = 1; + break; + } + // else: fallthru to error code + } else { + if (cmdbyte & bitmode) { + itf->writebuf[0] = vnd_read_byte(itf, itfnum); // number of bits + if (cmdbyte & ftmpsse_tdiwrite) + itf->writebuf[1] = vnd_read_byte(itf, itfnum); // data bits to output + itf->readbuf[0] = ftdi_if_mpsse_xfer_bits(itf, cmdbyte, itf->writebuf[0], itf->writebuf[1]); + if (cmdbyte & tdoread) avail = 1; + break; + } else { + avail = vnd_read_byte(itf, itfnum); + avail |= (uint32_t)vnd_read_byte(itf, itfnum) << 8; + + for (size_t i = 0; i < avail; i += 64) { + uint32_t thisbatch = avail - i; + if (thisbatch > 64) thisbatch = 64; + for (size_t j = 0; j < thisbatch; ++j) + itf->writebuf[j] = vnd_read_byte(itf, itfnum); + ftdi_if_mpsse_xfer_bytes(itf, cmdbyte, thisbatch, itf->readbuf, itf->writebuf); + tud_vendor_n_write(itfnum, itf->readbuf, thisbatch); + } + + avail = 0; + break; + } + } + } + + itf->readbuf[0] = 0xfa; + itf->readbuf[1] = cmdbyte; + avail = 2; + break; + } + + if (avail) tud_vendor_n_write(itfnum, itf->readbuf, avail); + break; + case ftmode_mcuhost: + avail = 0; + switch ((cmdbyte = vnd_read_byte(itf, itfnum))) { + case ftmcu_flush: ftdi_if_mcu_flush(itf); break; + case ftmcu_wait_io_hi: ftdi_if_mcu_wait_io(itf, true ); break; + case ftmcu_wait_io_lo: ftdi_if_mcu_wait_io(itf, false); break; + + case ftmcu_read8: + itf->readbuf[0] = ftdi_if_mcu_read8(itf, vnd_read_byte(itf, itfnum)); + avail = 1; + break; + case ftmcu_read16: + avail = (uint32_t)vnd_read_byte(itf, itfnum) << 8; + avail |= vnd_read_byte(itf, itfnum); + itf->readbuf[0] = ftdi_if_mcu_read16(itf, (uint16_t)avail); + avail = 1; + break; + case ftmcu_write8: + itf->writebuf[0] = vnd_read_byte(itf, itfnum); + itf->writebuf[1] = vnd_read_byte(itf, itfnum); + ftdi_if_mcu_write8(itf, itf->writebuf[0], itf->writebuf[1]); + break; + case ftmcu_write16: + avail = (uint32_t)vnd_read_byte(itf, itfnum) << 8; + avail |= vnd_read_byte(itf, itfnum); + itf->writebuf[0] = vnd_read_byte(itf, itfnum); + ftdi_if_mcu_write8(itf, addr, itf->writebuf[0]); + break; + + default: // send error response when command doesn't exist + itf->readbuf[0] = 0xfa; + itf->readbuf[1] = cmdbyte; + avail = 2; + break; + } + + if (avail) tuf_vendor_n_write(itfnum, itf->readbuf, avail); + break; 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); diff --git a/src/m_ftdi/ftdi.h b/src/m_ftdi/ftdi.h index 47f9473..c48dc72 100644 --- a/src/m_ftdi/ftdi.h +++ b/src/m_ftdi/ftdi.h @@ -154,7 +154,9 @@ enum ftdi_mpsse_cflg { 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_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 @@ -314,8 +316,7 @@ void ftdi_if_mpsse_set_dirval_lo(struct ftdi_interface* itf, uint8_t dir, uint8_ 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_on(struct ftdi_interface* itf); -void ftdi_if_mpsse_loopback_off(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);