From 191c9025e8ed3224399f8fa332acfa674bbe75bb Mon Sep 17 00:00:00 2001 From: sys64738 Date: Mon, 14 Jun 2021 03:37:32 +0200 Subject: [PATCH] untested but finished I2C-Tiny-USB impl --- CMakeLists.txt | 5 +- bsp/rp2040/i2c_tinyusb.c | 45 ++++++ bsp/rp2040/pinout.h | 5 + bsp/stm32f072disco/cdc_stdio.c | 7 + bsp/stm32f072disco/cdc_uart.c | 2 + bsp/stm32f072disco/i2c_tinyusb.c | 25 ++++ i2c-tiny-usb-misc/i2c-tiny-usb-proto.txt | 4 +- src/i2ctinyusb.c | 138 ------------------ src/i2ctinyusb.h | 106 ++++++++++++++ src/vnd_i2ctinyusb.c | 177 +++++++++++++++++++++++ 10 files changed, 372 insertions(+), 142 deletions(-) create mode 100644 bsp/rp2040/i2c_tinyusb.c create mode 100644 bsp/stm32f072disco/cdc_stdio.c create mode 100644 bsp/stm32f072disco/i2c_tinyusb.c delete mode 100644 src/i2ctinyusb.c create mode 100644 src/i2ctinyusb.h create mode 100644 src/vnd_i2ctinyusb.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 5409a21..ba03c27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,10 +56,11 @@ target_sources(${PROJECT} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Source/SWO.c ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Source/SW_DP.c ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/cdc_uart.c + ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/i2c_tinyusb.c ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/spi_serprog.c ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/unique.c ${CMAKE_CURRENT_SOURCE_DIR}/src/cdc_serprog.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/i2ctinyusb.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/vnd_i2ctinyusb.c ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c ${CMAKE_CURRENT_SOURCE_DIR}/src/rtconf.c ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c @@ -80,7 +81,7 @@ target_include_directories(${PROJECT} PUBLIC if(FAMILY STREQUAL "rp2040") target_link_libraries(${PROJECT} pico_stdlib pico_unique_id hardware_spi - pico_fix_rp2040_usb_device_enumeration + pico_fix_rp2040_usb_device_enumeration hardware_i2c tinyusb_device tinyusb_board tinyusb_additions) if(USE_USBCDC_FOR_STDIO) diff --git a/bsp/rp2040/i2c_tinyusb.c b/bsp/rp2040/i2c_tinyusb.c new file mode 100644 index 0000000..9e5da57 --- /dev/null +++ b/bsp/rp2040/i2c_tinyusb.c @@ -0,0 +1,45 @@ + +#include +#include +#include + +#include "protocfg.h" +#include "pinout.h" +#include "i2ctinyusb.h" + +__attribute__((__const__)) +enum ki2c_funcs i2ctu_get_func(void) { + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +void i2ctu_init(void) { + // default to 100 kHz (SDK example default so should be ok) + i2c_init(PINOUT_I2C_DEV, 100*1000); + + gpio_set_function(PINOUT_I2C_SCL, GPIO_FUNC_I2C); + gpio_set_function(PINOUT_I2C_SDA, GPIO_FUNC_I2C); + gpio_pull_up(PINOUT_I2C_SCL); + gpio_pull_up(PINOUT_I2C_SDA); + + bi_decl(bi_2pins_with_func(PINOUT_I2C_SCL, PINOUT_I2C_SDA, GPIO_FUNC_I2C)); +} + +uint32_t i2ctu_set_freq(uint32_t freq) { + return i2c_set_baudrate(PINOUT_I2C_DEV, freq); +} + +enum itu_status i2ctu_write(enum ki2c_flags flags, enum itu_command startstopflags, + uint16_t addr, const uint8_t* buf, size_t len) { + int rv = i2c_write_timeout_us(PINOUT_I2C_DEV, addr, buf, len, + !(startstopflags & ITU_CMD_I2C_IO_END), 1000*1000); + if (rv < 0) 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) { + int rv = i2c_read_timeout_us(PINOUT_I2C_DEV, addr, buf, len, + !(startstopflags & ITU_CMD_I2C_IO_END), 1000*1000); + if (rv < 0) return ITU_STATUS_ADDR_NAK; + return ITU_STATUS_ADDR_ACK; +} + diff --git a/bsp/rp2040/pinout.h b/bsp/rp2040/pinout.h index 15f221d..ac42d0e 100644 --- a/bsp/rp2040/pinout.h +++ b/bsp/rp2040/pinout.h @@ -25,6 +25,11 @@ #define PINOUT_SPI_MISO 12 #define PINOUT_SPI_nCS 13 +// I2C config +#define PINOUT_I2C_DEV i2c0 +#define PINOUT_I2C_SCL 21 +#define PINOUT_I2C_SDA 20 + // LED config // you can change these two as you like diff --git a/bsp/stm32f072disco/cdc_stdio.c b/bsp/stm32f072disco/cdc_stdio.c new file mode 100644 index 0000000..1247c2c --- /dev/null +++ b/bsp/stm32f072disco/cdc_stdio.c @@ -0,0 +1,7 @@ + +#include "protos.h" + +bool stdio_usb_init(void) { + return true; +} + diff --git a/bsp/stm32f072disco/cdc_uart.c b/bsp/stm32f072disco/cdc_uart.c index 54feaf2..9e74676 100644 --- a/bsp/stm32f072disco/cdc_uart.c +++ b/bsp/stm32f072disco/cdc_uart.c @@ -1,4 +1,6 @@ +#include "protos.h" + void cdc_uart_init(void) { } diff --git a/bsp/stm32f072disco/i2c_tinyusb.c b/bsp/stm32f072disco/i2c_tinyusb.c new file mode 100644 index 0000000..ec0f2cc --- /dev/null +++ b/bsp/stm32f072disco/i2c_tinyusb.c @@ -0,0 +1,25 @@ + +#include "protocfg.h" +#include "i2ctinyusb.h" + +__attribute__((__const__)) +enum ki2c_funcs i2ctu_get_func(void) { + return 0; +} + +void i2ctu_init(void) { +} + +uint32_t i2ctu_set_freq(uint32_t freq) { + return 0; +} + +enum itu_status i2ctu_write(enum ki2c_flags flags, enum itu_command startstopflags, + uint16_t addr, const uint8_t* buf, size_t len) { + return ITU_STATUS_IDLE; +} +enum itu_status i2ctu_read(enum ki2c_flags flags, enum itu_command startstopflags, + uint16_t addr, uint8_t* buf, size_t len) { + return ITU_STATUS_IDLE; +} + diff --git a/i2c-tiny-usb-misc/i2c-tiny-usb-proto.txt b/i2c-tiny-usb-misc/i2c-tiny-usb-proto.txt index eede51b..99a6b5b 100644 --- a/i2c-tiny-usb-misc/i2c-tiny-usb-proto.txt +++ b/i2c-tiny-usb-misc/i2c-tiny-usb-proto.txt @@ -11,7 +11,7 @@ module: kernel: request reqtype(fix) value index dmalen i2c_msg: - cmd? type? flags index len + cmd? type? flags addr len CMD_SET_DELAY: delayv 0 0 CMD_GET_STATUS: data=[status retval] @@ -108,7 +108,7 @@ USB vendor setup: send back supported functions: 4-byte little-endian of the above flags SET_DELAY command: - sets clock period, in flags, unit is half a microsecond + sets clock period, in flags, unit is microseconds GET_STATUS command: return the status register (1 byte) diff --git a/src/i2ctinyusb.c b/src/i2ctinyusb.c deleted file mode 100644 index 31f6a15..0000000 --- a/src/i2ctinyusb.c +++ /dev/null @@ -1,138 +0,0 @@ - -#include "protocfg.h" - -#ifdef DBOARD_HAS_I2C - -#include -#include -#include - -#include "tusb.h" -#include "device/usbd_pvt.h" - -#include "protos.h" - -static uint8_t itf_num; - -static void iub_init(void) { - //printf("i2c init\n"); -} - -static void iub_reset(uint8_t rhport) { - //printf("i2c reset %02x\n", rhport); - - itf_num = 0; -} - -static uint16_t iub_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, - uint16_t max_len) { - TU_VERIFY(itf_desc->bInterfaceClass == 0 - && itf_desc->bInterfaceSubClass == 0 - && itf_desc->bInterfaceProtocol == 0, 0); - - const uint16_t drv_len = sizeof(tusb_desc_interface_t); - TU_VERIFY(max_len >= drv_len, 0); - - itf_num = itf_desc->bInterfaceNumber; - - return drv_len; -} - -extern char msgbuf[256]; -extern volatile bool msgflag; -char msgbuf[256] = {0}; -volatile bool msgflag = false; - -//, b, c, d, e ,f -#define printf(fmt, a, b, c, d, e) do { \ - /*while (msgflag) ;*/\ - snprintf(msgbuf, sizeof msgbuf, fmt, a, b, c, d, e);\ - msgbuf[sizeof msgbuf - 1] = 0; \ - msgflag = true;\ - } while (0);\ - -static bool iub_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const* req) { - if (stage != CONTROL_STAGE_SETUP) return true; - - /*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) { - switch (req->bRequest) { - case 1: // get func - { - const uint32_t flags = 0xceff0001; - uint8_t rv[4]; - rv[0]=flags&0xff; - rv[1]=(flags>>8)&0xff; - rv[2]=(flags>>16)&0xff; - rv[3]=(flags>>24)&0xff; - return tud_control_xfer(rhport, req, rv, sizeof rv); - } - case 2: // set delay - return tud_control_status(rhport, req); - case 3: // get status - { - uint8_t rv = 0; // idle - return tud_control_xfer(rhport, req, &rv, 1); - } - case 4: case 5: case 6: case 7: // I2C_IO - { - if (req->wValue & 1) { // read: we need to return shit - //printf("read!%c%c%c%c%c\n", ' ', ' ', ' ', ' ', ' '); - // so, we'll return some garbage - uint8_t buf[req->wLength]; - return tud_control_xfer(rhport, req, buf, req->wLength); - } else { - //printf("write!%c%c%c%c%c\n", ' ', ' ', ' ', ' ', ' '); - // ???? - uint8_t buf[req->wLength]; - bool rv = tud_control_xfer(rhport, req, buf, req->wLength); - printf("rv %c len %04x buf %02x %02x %02x ...\n", - (rv?'t':'f'), req->wLength, buf[0], buf[1], buf[2]); - return rv; - } - } - } - } - - return false; // unk -} - -// never actually called -static bool iub_xfer(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { - printf("xfer cb! rh=%02x ep=%02x res=%02x len=%08x%c\n", - rhport, ep_addr, result, xferred_bytes, ' '); - - return true; -} - -// interfacing stuff for TinyUSB API, actually defines the driver - -static usbd_class_driver_t const i2ctinyusb_driver = { -#if CFG_TUSB_DEBUG >= 2 - .name = "i2c-tiny-usb", -#endif - - .init = iub_init, - .reset = iub_reset, - .open = iub_open, - .control_xfer_cb = iub_ctl_req, - .xfer_cb = iub_xfer, - .sof = NULL -}; - -usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count) { - *driver_count = 1; - return &i2ctinyusb_driver; -} - -// we need to implement this one, because tinyusb uses hardcoded stuff for -// endpoint 0, which is what the i2c-tiny-usb kernel module uses -bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, tusb_control_request_t const* req) { - return iub_ctl_req(rhport, ep_addr, req); -} - -#endif /* DBOARD_HAS_I2C */ - diff --git a/src/i2ctinyusb.h b/src/i2ctinyusb.h new file mode 100644 index 0000000..1dcf6cd --- /dev/null +++ b/src/i2ctinyusb.h @@ -0,0 +1,106 @@ + +#ifndef I2CTINYUSB_H_ +#define I2CTINYUSB_H_ + +#include +#include +#include + +#include "protocfg.h" + +enum itu_command { + ITU_CMD_ECHO = 0, + ITU_CMD_GET_FUNC = 1, + ITU_CMD_SET_DELAY = 2, + ITU_CMD_GET_STATUS = 3, + + ITU_CMD_I2C_IO_BEGIN_F = (1<<0), + ITU_CMD_I2C_IO_END_F = (1<<1), + ITU_CMD_I2C_IO_DIR_MASK = ITU_CMD_I2C_IO_BEGIN_F | ITU_CMD_I2C_IO_END_F, + + ITU_CMD_I2C_IO = 4, + ITU_CMD_I2C_IO_BEGIN = 4 | ITU_CMD_I2C_IO_BEGIN_F, + ITU_CMD_I2C_IO_END = 4 | ITU_CMD_I2C_IO_END_F , + ITU_CMD_I2C_IO_BEGINEND = 4 | ITU_CMD_I2C_IO_BEGIN_F | ITU_CMD_I2C_IO_END_F, +}; + +enum itu_status { + ITU_STATUS_IDLE = 0, + ITU_STATUS_ADDR_ACK = 1, + ITU_STATUS_ADDR_NAK = 2 +}; + +// these two are lifted straight from the linux kernel, lmao +enum ki2c_flags { + I2C_M_RD = 0x0001, /* guaranteed to be 0x0001! */ + I2C_M_TEN = 0x0010, /* use only if I2C_FUNC_10BIT_ADDR */ + I2C_M_DMA_SAFE = 0x0200, /* use only in kernel space */ + I2C_M_RECV_LEN = 0x0400, /* use only if I2C_FUNC_SMBUS_READ_BLOCK_DATA */ + I2C_M_NO_RD_ACK = 0x0800, /* use only if I2C_FUNC_PROTOCOL_MANGLING */ + I2C_M_IGNORE_NAK = 0x1000, /* use only if I2C_FUNC_PROTOCOL_MANGLING */ + I2C_M_REV_DIR_ADDR = 0x2000, /* use only if I2C_FUNC_PROTOCOL_MANGLING */ + I2C_M_NOSTART = 0x4000, /* use only if I2C_FUNC_NOSTART */ + I2C_M_STOP = 0x8000, /* use only if I2C_FUNC_PROTOCOL_MANGLING */ +}; + +enum ki2c_funcs { + I2C_FUNC_I2C = 0x00000001, + I2C_FUNC_10BIT_ADDR = 0x00000002, /* required for I2C_M_TEN */ + I2C_FUNC_PROTOCOL_MANGLING = 0x00000004, /* required for I2C_M_IGNORE_NAK etc. */ + I2C_FUNC_SMBUS_PEC = 0x00000008, + I2C_FUNC_NOSTART = 0x00000010, /* required for I2C_M_NOSTART */ + I2C_FUNC_SLAVE = 0x00000020, + I2C_FUNC_SMBUS_BLOCK_PROC_CALL = 0x00008000, /* SMBus 2.0 or later */ + I2C_FUNC_SMBUS_QUICK = 0x00010000, + I2C_FUNC_SMBUS_READ_BYTE = 0x00020000, + I2C_FUNC_SMBUS_WRITE_BYTE = 0x00040000, + I2C_FUNC_SMBUS_READ_BYTE_DATA = 0x00080000, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA = 0x00100000, + I2C_FUNC_SMBUS_READ_WORD_DATA = 0x00200000, + I2C_FUNC_SMBUS_WRITE_WORD_DATA = 0x00400000, + I2C_FUNC_SMBUS_PROC_CALL = 0x00800000, + I2C_FUNC_SMBUS_READ_BLOCK_DATA = 0x01000000, /* required for I2C_M_RECV_LEN */ + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA = 0x02000000, + I2C_FUNC_SMBUS_READ_I2C_BLOCK = 0x04000000, /* I2C-like block xfer */ + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK = 0x08000000, /* w/ 1-byte reg. addr. */ + I2C_FUNC_SMBUS_READ_I2C_BLOCK_2 = 0x10000000, /* I2C-like block xfer */ + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2 = 0x20000000, /* w/ 2-byte reg. addr. */ + I2C_FUNC_SMBUS_READ_BLOCK_DATA_PEC = 0x40000000, /* SMBus 2.0 or later */ + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC = 0x80000000, /* SMBus 2.0 or later */ + + I2C_FUNC_SMBUS_BYTE = (I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE), + I2C_FUNC_SMBUS_BYTE_DATA = (I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA), + I2C_FUNC_SMBUS_WORD_DATA = (I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA), + I2C_FUNC_SMBUS_BLOCK_DATA = (I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_BLOCK_DATA), + I2C_FUNC_SMBUS_I2C_BLOCK = (I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK), + + I2C_FUNC_SMBUS_EMUL = (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | \ + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | \ + I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \ + I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_PEC), + + /* if I2C_M_RECV_LEN is also supported */ + I2C_FUNC_SMBUS_EMUL_ALL = (I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA | \ + I2C_FUNC_SMBUS_BLOCK_PROC_CALL), +}; + +struct itu_cmd { + uint8_t cmd; + uint16_t flags; + uint16_t addr; + uint16_t len; +}; + +#ifdef DBOARD_HAS_I2C +__attribute__((__const__)) +enum ki2c_funcs i2ctu_get_func(void); +void i2ctu_init(void); +uint32_t i2ctu_set_freq(uint32_t freq); // returns selected frequency, or 0 on error +enum itu_status i2ctu_write(enum ki2c_flags flags, enum itu_command startstopflags, + uint16_t addr, const uint8_t* buf, size_t len); +enum itu_status i2ctu_read(enum ki2c_flags flags, enum itu_command startstopflags, + uint16_t addr, uint8_t* buf, size_t len); +#endif + +#endif + diff --git a/src/vnd_i2ctinyusb.c b/src/vnd_i2ctinyusb.c new file mode 100644 index 0000000..9f25e2e --- /dev/null +++ b/src/vnd_i2ctinyusb.c @@ -0,0 +1,177 @@ + +#include "protos.h" + +#ifdef DBOARD_HAS_I2C + +#include +#include +#include + +#include "tusb.h" +#include "device/usbd_pvt.h" + +#include "i2ctinyusb.h" + +static uint8_t itf_num; + +static enum itu_status state; +static struct itu_cmd curcmd; + +static void iub_init(void) { + state = ITU_STATUS_IDLE; + memset(&curcmd, 0, sizeof curcmd); + + i2ctu_init(); +} + +static void iub_reset(uint8_t rhport) { + state = ITU_STATUS_IDLE; + memset(&curcmd, 0, sizeof curcmd); + + i2ctu_init(); + + itf_num = 0; +} + +static uint16_t iub_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc, + uint16_t max_len) { + TU_VERIFY(itf_desc->bInterfaceClass == 0 + && itf_desc->bInterfaceSubClass == 0 + && itf_desc->bInterfaceProtocol == 0, 0); + + const uint16_t drv_len = sizeof(tusb_desc_interface_t); + TU_VERIFY(max_len >= drv_len, 0); + + itf_num = itf_desc->bInterfaceNumber; + + return drv_len; +} + +static bool iub_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const* req) { + if (stage == CONTROL_STAGE_DATA) { + // TODO: see also the TODO below + return true; + } + + if (stage != CONTROL_STAGE_SETUP) return true; + + /*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) { + 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 + + uint8_t rv[2]; + rv[0] = req->wValue&0xff; + rv[1] = (req->wValue>>8)&0xff; + return tud_control_xfer(rhport, req, rv, sizeof rv); + } + break; + case ITU_CMD_GET_FUNC: { // flags unused, addr unused, len=4 + if (req->wLength != 0) 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); + } + break; + case ITU_CMD_SET_DELAY: { // flags=delay, addr unused, len=0 + if (req->wLength != 0) return false; + + uint32_t us = req->wValue ? req->wValue : 1; + uint32_t freq = 1000*1000 / us; + + if (i2ctu_set_freq(freq) != 0) // returned an ok frequency + return tud_control_status(rhport, req); + else return false; + } + break; + case ITU_CMD_GET_STATUS: { // flags unused, addr unused, len=1 + if (req->wLength != 1) return false; + + uint8_t rv = state; + return tud_control_xfer(rhport, req, &rv, 1); + } + break; + + case ITU_CMD_I2C_IO: // flags: ki2c_flags + 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; + cmd.flags = req->wValue; + cmd.addr = req->wIndex; + cmd.len = req->wLength; + curcmd = cmd; + + // 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[cmd.len]; + + if (cmd.flags & I2C_M_RD) { // read from I2C device + state = i2ctu_read(cmd.flags, cmd.cmd & ITU_CMD_I2C_IO_DIR_MASK, + cmd.addr, buf, sizeof buf); + return tud_control_xfer(rhport, req, buf, cmd.len); + } else { // write + bool rv = tud_control_xfer(rhport, req, buf, cmd.len); + if (rv) { + state = i2ctu_write(cmd.flags, cmd.cmd & ITU_CMD_I2C_IO_DIR_MASK, + cmd.addr, buf, sizeof buf); + } + return rv; + } + } + break; + default: + printf("I2C-Tiny-USB: unknown command %02x\n", req->bRequest); + return false; // unknown! + } + } 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! + } +} + +// never actually called fsr +static bool iub_xfer(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { + return true; +} + +// interfacing stuff for TinyUSB API, actually defines the driver + +static usbd_class_driver_t const i2ctinyusb_driver = { +#if CFG_TUSB_DEBUG >= 2 + .name = "i2c-tiny-usb", +#endif + + .init = iub_init, + .reset = iub_reset, + .open = iub_open, + .control_xfer_cb = iub_ctl_req, + .xfer_cb = iub_xfer, + .sof = NULL +}; + +usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count) { + *driver_count = 1; + return &i2ctinyusb_driver; +} + +// we need to implement this one, because tinyusb uses hardcoded stuff for +// endpoint 0, which is what the i2c-tiny-usb kernel module uses +bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, tusb_control_request_t const* req) { + return iub_ctl_req(rhport, ep_addr, req); +} + +#endif /* DBOARD_HAS_I2C */ +