diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b018bf..875dfa9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,8 +33,8 @@ if(FAMILY STREQUAL "rp2040") family_initialize_project(PROJECT ${CMAKE_CURRENT_LIST_DIR}) # calls pico_sdk_init() #pico_sdk_init() - pico_set_program_name(PROJECT "${PROJECT}") - pico_set_program_version(PROJECT "0.1") + #pico_set_program_name(${PROJECT} "${PROJECT}") + #pico_set_program_version(${PROJECT} "0.1") add_executable(${PROJECT}) diff --git a/README.md b/README.md index 041bbf5..91a6fb5 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ These microcontrollers support the following protocols: | MCU | SWD | JTAG | UART | SPI (flashrom) | I2C | Other stuff | |:------ |:---:|:----:|:----:|:-------------- |:--- |:--------------- | -| RP2040 | X | X | X | X | WIP | Planned | +| RP2040 | X | X | X | X | X | Planned | | STM32F072B Discovery | X | | | | | | The [original repository](https://github.com/majbthrd/DapperMime/) (Dapper @@ -164,10 +164,10 @@ libco is licensed under the [ISC license](https://opensource.org/licenses/ISC) separate mode that temporarily disables all other IO protocols - [x] UART with CTS/RTS flow control - [x] Needs configurable stuff as well, as some UART interfaces won't use this. - - [ ] Second UART port for when stdio UART is disabled? - [x] Debug interface to send printf stuff directly to USB, instead of having to use the UART interface as a loopback thing. -- [ ] I2C support by emulating the I2C Tiny USB + - [ ] Second UART port for when stdio UART is disabled? +- [x] I2C support by emulating the I2C Tiny USB - [ ] Expose RP2040-internal temperature ADC on I2C-over-USB bus? - ~~Does SMBus stuff need special treatment here?~~ ~~No.~~ Actually, some parts do, but, laziness. diff --git a/bsp/default/protocfg.h b/bsp/default/protocfg.h index 66551c3..f3bff02 100644 --- a/bsp/default/protocfg.h +++ b/bsp/default/protocfg.h @@ -7,6 +7,10 @@ /*#define DBOARD_HAS_SERPROG*/ /*#define DBOARD_HAS_TINYI2C*/ +#define CFG_TUD_CDC 0 +#define CFG_TUD_HID 0 +#define CFG_TUD_VENDOR 0 + #define INFO_BOARDNAME "unknown" #endif diff --git a/bsp/rp2040/i2c_tinyusb.c b/bsp/rp2040/i2c_tinyusb.c index c55015e..215532e 100644 --- a/bsp/rp2040/i2c_tinyusb.c +++ b/bsp/rp2040/i2c_tinyusb.c @@ -377,33 +377,40 @@ uint32_t i2ctu_set_freq(uint32_t freq, uint32_t us) { } // TODO: FIX START AND STOP COND HANDLING. MAYBE. BUG IN vnd_i2ctinyusb.c MORE SERIOUS +// ALSO TODO: i2cex routines seem to mess with the I2C bus when a timeout (=> abort) happens? enum itu_status i2ctu_write(enum ki2c_flags flags, enum itu_command startstopflags, uint16_t addr, const uint8_t* buf, size_t len) { + bool nostop = !(startstopflags & ITU_CMD_I2C_IO_END); + printf("nostop=%c ", nostop?'t':'f'); + if (len == 0) { // do a read, that's less hazardous uint8_t stuff = 0; - int rv = i2cex_read_timeout_us(PINOUT_I2C_DEV, addr, false, &stuff, 1, - !(startstopflags & ITU_CMD_I2C_IO_END), 1000*1000); + int rv = i2c_read_timeout_us(PINOUT_I2C_DEV, addr, /*false,*/ &stuff, 1, + nostop, 1000*1000); if (rv < 0) return ITU_STATUS_ADDR_NAK; return ITU_STATUS_ADDR_ACK; } else { - int rv = i2cex_write_timeout_us(PINOUT_I2C_DEV, addr, false, buf, len, - !(startstopflags & ITU_CMD_I2C_IO_END), 1000*1000); + int rv = i2c_write_timeout_us(PINOUT_I2C_DEV, addr, /*false,*/ buf, len, + nostop, 1000*1000); if (rv < 0 || (size_t)rv < len) return ITU_STATUS_ADDR_NAK; return ITU_STATUS_ADDR_ACK; } } enum itu_status i2ctu_read(enum ki2c_flags flags, enum itu_command startstopflags, uint16_t addr, uint8_t* buf, size_t len) { + bool nostop = !(startstopflags & ITU_CMD_I2C_IO_END); + printf("nostop=%c ", nostop?'t':'f'); + if (len == 0) { uint8_t stuff = 0; - int rv = i2cex_read_timeout_us(PINOUT_I2C_DEV, addr, false, &stuff, 1, - !(startstopflags & ITU_CMD_I2C_IO_END), 1000*1000); + int rv = i2c_read_timeout_us(PINOUT_I2C_DEV, addr, /*false,*/ &stuff, 1, + nostop, 1000*1000); if (rv < 0) return ITU_STATUS_ADDR_NAK; return ITU_STATUS_ADDR_ACK; } else { - int rv = i2cex_read_timeout_us(PINOUT_I2C_DEV, addr, false, buf, len, - !(startstopflags & ITU_CMD_I2C_IO_END), 1000*1000); + int rv = i2c_read_timeout_us(PINOUT_I2C_DEV, addr, /*false,*/ buf, len, + nostop, 1000*1000); printf("p le rv=%d buf=%02x ", rv, buf[0]); if (rv < 0 || (size_t)rv < len) return ITU_STATUS_ADDR_NAK; return ITU_STATUS_ADDR_ACK; diff --git a/bsp/rp2040/protocfg.h b/bsp/rp2040/protocfg.h index 6c4f207..4ff59e5 100644 --- a/bsp/rp2040/protocfg.h +++ b/bsp/rp2040/protocfg.h @@ -7,15 +7,32 @@ #define DBOARD_HAS_SERPROG #define DBOARD_HAS_I2C -#define HID_N_CMSISDAP 0 -#define CDC_N_UART 0 -#define CDC_N_SERPROG 1 -#define VND_N_I2CTINYUSB 0 +enum { + HID_N_CMSISDAP = 0, + HID_N__NITF +}; +enum { + CDC_N_UART = 0, + CDC_N_SERPROG, #ifdef USE_USBCDC_FOR_STDIO -#define CDC_N_STDIO 2 + CDC_N_STDIO, #endif + CDC_N__NITF +}; +enum { + VND_N__NITF = 0 +}; + +#define CFG_TUD_HID 1 +#ifdef USE_USBCDC_FOR_STDIO +#define CFG_TUD_CDC 3 +#else +#define CFG_TUD_CDC 2 +#endif +#define CFG_TUD_VENDOR 0 + /*#define USB_VID 0x2e8a*/ /* Raspberry Pi */ #define USB_VID 0xcafe /* TinyUSB */ /*#define USB_VID 0x1209*/ /* Generic */ diff --git a/bsp/stm32f072disco/protocfg.h b/bsp/stm32f072disco/protocfg.h index f14ecc1..7a135c4 100644 --- a/bsp/stm32f072disco/protocfg.h +++ b/bsp/stm32f072disco/protocfg.h @@ -7,6 +7,22 @@ /*#define DBOARD_HAS_SERPROG*/ /*#define DBOARD_HAS_TINYI2C*/ +enum { + HID_N_CMSISDAP = 0, + + HID_N__NITF +}; +enum { + CDC_N__NITF +}; +enum { + VND_N__NITF = 0 +}; + +#define CFG_TUD_HID 1 +#define CFG_TUD_CDC 0 +#define CFG_TUD_VENDOR 0 + #define USB_VID 0xcafe /* TinyUSB */ /*#define USB_VID 0x1209*/ /* Generic */ /*#define USB_VID 0x1d50*/ /* OpenMoko */ diff --git a/src/i2ctinyusb.h b/src/i2ctinyusb.h index cf33ebf..1c9d9c2 100644 --- a/src/i2ctinyusb.h +++ b/src/i2ctinyusb.h @@ -84,11 +84,12 @@ enum ki2c_funcs { I2C_FUNC_SMBUS_BLOCK_PROC_CALL), }; +__attribute__((__packed__)) struct itu_cmd { - uint8_t cmd; uint16_t flags; uint16_t addr; uint16_t len; + uint8_t cmd; }; #ifdef DBOARD_HAS_I2C diff --git a/src/tusb_config.h b/src/tusb_config.h index 0f476bf..460bcd3 100644 --- a/src/tusb_config.h +++ b/src/tusb_config.h @@ -30,6 +30,8 @@ extern "C" { #endif +#include "protocfg.h" + //-------------------------------------------------------------------- // COMMON CONFIGURATION //-------------------------------------------------------------------- @@ -98,17 +100,10 @@ extern "C" { #endif //------------- CLASS -------------// -#ifdef USE_USBCDC_FOR_STDIO -#define CFG_TUD_CDC 3 -#else -#define CFG_TUD_CDC 2 -#endif - #define CFG_TUD_MSC 0 -#define CFG_TUD_HID 1 #define CFG_TUD_MIDI 0 -#define CFG_TUD_VENDOR 0 #define CFG_TUD_NET 0 +// see also: bsp//protocfg.h #define CFG_TUD_HID_EP_BUFSIZE 64 diff --git a/src/vnd_i2ctinyusb.c b/src/vnd_i2ctinyusb.c index 46379e0..9c8e948 100644 --- a/src/vnd_i2ctinyusb.c +++ b/src/vnd_i2ctinyusb.c @@ -20,6 +20,9 @@ static uint8_t itf_num; static enum itu_status status; static struct itu_cmd curcmd; +static uint8_t rxbuf[128]; +static uint8_t txbuf[128]; + static void iub_init(void) { status = ITU_STATUS_IDLE; memset(&curcmd, 0, sizeof curcmd); @@ -51,41 +54,32 @@ static uint16_t iub_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, } static bool iub_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const* req) { -// if (stage == CONTROL_STAGE_DATA && req->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR) { -// // TODO: should URB_CONTROL out data be read in this stage???? -// // FIXME: cond ^ has false-positives! -// // FIXME: handle 0-byte writes in SETUP stage -// // FIXME: use curcmd var for eliminating false-positives -// // FIXME: other stuff??? -// if (req->bRequest >= ITU_CMD_I2C_IO && req->bRequest <= ITU_CMD_I2C_IO_BEGINEND -// /*&& curcmd.cmd == req->bRequest && curcmd.flags == req->wValue -// && curcmd.addr == req->wIndex && curcmd.len == req->wLength*/) { -// uint8_t buf[req->wLength]; -// bool rv = tud_control_xfer(rhport, req, buf, req->wLength); -// printf("write addr=%04hx len=%04hx ", req->wIndex, req->wLength); -// if (rv) { -// printf("data=%02x %02x...\n", buf[0], buf[1]); -// status = i2ctu_write(req->wValue, req->bRequest & ITU_CMD_I2C_IO_DIR_MASK, -// req->wIndex, buf, sizeof buf); -// } else { -// printf("no data :/\n"); -// status = ITU_STATUS_ADDR_NAK; -// } -// return rv; -// } else { -// //printf("I2C-Tiny-USB: bad command in DATA stage\n"); -// //return false; -// } -// return true; -// } + static char* stages[]={"SETUP","DATA","ACK"}; + static char* types[]={"STD","CLS","VND","INV"}; - if (stage != CONTROL_STAGE_SETUP) return true; + /*printf("ctl req stage=%s rt=%s, wIndex=%04x, bReq=%02x, wValue=%04x wLength=%04x\n", + stages[stage], types[req->bmRequestType_bit.type], + req->wIndex, req->bRequest, req->wValue, req->wLength);*/ - /*printf("ctl req rhport=%02x, stage=%02x, wIndex=%04x, bReq=%02x, wValue=%04x\n", - rhport, stage, - req->wIndex, req->bRequest, req->wValue);*/ + if (req->bmRequestType_bit.type != TUSB_REQ_TYPE_VENDOR) return true; - if (req->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR) { + if (stage == CONTROL_STAGE_DATA) { + struct itu_cmd cmd = curcmd; + + if (req->bRequest >= ITU_CMD_I2C_IO && req->bRequest <= ITU_CMD_I2C_IO_BEGINEND + && cmd.cmd == req->bRequest && cmd.flags == req->wValue + && cmd.addr == req->wIndex && cmd.len == req->wLength) { + printf("WDATA a=%04hx l=%04hx ", cmd.addr, cmd.len); + + printf("data=%02x %02x...\n", rxbuf[0], rxbuf[1]); + status = i2ctu_write(cmd.flags, cmd.cmd & ITU_CMD_I2C_IO_DIR_MASK, + cmd.addr, rxbuf, cmd.len > sizeof rxbuf ? sizeof rxbuf : cmd.len); + + // cancel curcmd + curcmd.cmd = 0xff; + } + return true; + } else if (stage == CONTROL_STAGE_SETUP) { switch (req->bRequest) { case ITU_CMD_ECHO: { // flags to be echoed back, addr unused, len=2 if (req->wLength != 2) return false; // bad length -> let's stall @@ -100,12 +94,11 @@ static bool iub_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_request_t co if (req->wLength != 4) return false; const uint32_t func = i2ctu_get_func(); - uint8_t rv[4]; - rv[0]=func&0xff; - rv[1]=(func>>8)&0xff; - rv[2]=(func>>16)&0xff; - rv[3]=(func>>24)&0xff; - return tud_control_xfer(rhport, req, rv, sizeof rv); + txbuf[0]=func&0xff; + txbuf[1]=(func>>8)&0xff; + txbuf[2]=(func>>16)&0xff; + txbuf[3]=(func>>24)&0xff; + return tud_control_xfer(rhport, req, txbuf, 4); } break; case ITU_CMD_SET_DELAY: { // flags=delay, addr unused, len=0 @@ -132,50 +125,42 @@ static bool iub_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_request_t co case ITU_CMD_I2C_IO_BEGIN: // addr: I2C address case ITU_CMD_I2C_IO_END: // len: transfer size case ITU_CMD_I2C_IO_BEGINEND: { // (transfer dir is in flags) - /*struct itu_cmd cmd; - cmd.cmd = req->bRequest; + struct itu_cmd cmd; cmd.flags = req->wValue; cmd.addr = req->wIndex; cmd.len = req->wLength; - curcmd = cmd;*/ + cmd.cmd = req->bRequest; - // TODO: what's the max value of wLength? does this need - // to be handled separately in the data stage as well? - // will the entire thing be read into one big chunk, or - // does it also get split up into buffers of eg. 64 bytes? - uint8_t buf[req->wLength]; - - //printf("flags=%04x\n", req->wValue); - if (req->wValue & I2C_M_RD) { // read from I2C device - printf("read addr=%04hx len=%04hx ", req->wIndex, req->wLength); - status = i2ctu_read(req->wValue, req->bRequest & ITU_CMD_I2C_IO_DIR_MASK, - req->wIndex, buf, sizeof buf); - printf("data=%02x %02x...\n", buf[0], buf[1]); - return tud_control_xfer(rhport, req, buf, req->wLength); + if (cmd.flags & I2C_M_RD) { // read from I2C device + printf("read addr=%04hx len=%04hx ", cmd.addr, cmd.len); + status = i2ctu_read(cmd.flags, cmd.cmd & ITU_CMD_I2C_IO_DIR_MASK, + cmd.addr, txbuf, cmd.len); + printf("data=%02x %02x...\n", txbuf[0], txbuf[1]); + return tud_control_xfer(rhport, req, txbuf, + cmd.len > sizeof txbuf ? sizeof txbuf : cmd.len); } else { // write - return true; // handled in DATA stage - /*// FIXME: THIS NO WORKY! STUFF IN BUFFER IS NONSENSE - bool rv = tud_control_xfer(rhport, req, buf, req->wLength); - if (rv) { - printf("data=%02x %02x...\n", buf[0], buf[1]); - status = i2ctu_write(req->wValue, req->bRequest & ITU_CMD_I2C_IO_DIR_MASK, - req->wIndex, buf, sizeof buf); + printf("write addr=%04hx len=%04hx ", cmd.addr, cmd.len); + if (cmd.len == 0) { // address probe -> do this here + uint8_t bleh = 0; + status = i2ctu_write(cmd.flags, cmd.cmd & ITU_CMD_I2C_IO_DIR_MASK, + cmd.addr, &bleh, 0); + printf("probe -> %d\n", status); + return tud_control_status(rhport, req); } else { - printf("no data :/\n"); - status = ITU_STATUS_ADDR_NAK; + // handled in DATA stage! + curcmd = cmd; + bool rv = tud_control_xfer(rhport, req, rxbuf, + cmd.len > sizeof rxbuf ? sizeof rxbuf : cmd.len); + return rv; } - return rv;*/ } } break; default: printf("I2C-Tiny-USB: unknown command %02x\n", req->bRequest); - return false; // unknown! + return false; } - } else { - printf("I2C-Tiny-USB: bad request type %02x\n", req->bmRequestType); - return false; // not a vendor command? no clue what to do with it! - } + } else return true; // other stage... } // never actually called fsr