#include "protos.h" #ifdef DBOARD_HAS_I2C #include #include #include #include "tusb.h" #include "device/usbd_pvt.h" #include "i2ctinyusb.h" #include "pinout.h" #include 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); i2ctu_init(); } static void iub_reset(uint8_t rhport) { status = 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) { static char* stages[]={"SETUP","DATA","ACK"}; static char* types[]={"STD","CLS","VND","INV"}; /*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);*/ if (req->bmRequestType_bit.type != TUSB_REQ_TYPE_VENDOR) return true; 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 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 != 4) return false; const uint32_t func = i2ctu_get_func(); 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 if (req->wLength != 0) return false; uint32_t us = req->wValue ? req->wValue : 1; uint32_t freq = 1000*1000 / us; //printf("set freq us=%u freq=%u\n", us, freq); if (i2ctu_set_freq(freq, us) != 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 = status; 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.flags = req->wValue; cmd.addr = req->wIndex; cmd.len = req->wLength; cmd.cmd = req->bRequest; 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 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 { // handled in DATA stage! curcmd = cmd; bool rv = tud_control_xfer(rhport, req, rxbuf, cmd.len > sizeof rxbuf ? sizeof rxbuf : cmd.len); return rv; } } } break; default: printf("I2C-Tiny-USB: unknown command %02x\n", req->bRequest); return false; } } else return true; // other stage... } // 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 */