diff --git a/CMakeLists.txt b/CMakeLists.txt index 695d138..126092a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,8 +46,6 @@ if(FAMILY STREQUAL "rp2040") pico_enable_stdio_usb(${PROJECT} 0) target_sources(${PROJECT} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/main.c - ${CMAKE_CURRENT_SOURCE_DIR}/usb_descriptors.c ${CMAKE_CURRENT_SOURCE_DIR}/libco/libco.S ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Source/DAP.c ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Source/JTAG_DP.c @@ -56,11 +54,14 @@ if(FAMILY STREQUAL "rp2040") ${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}/spi_serprog.c - ${CMAKE_CURRENT_SOURCE_DIR}/cdc_serprog.c - ${CMAKE_CURRENT_SOURCE_DIR}/rtconf.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/cdc_serprog.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/i2ctinyusb.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/rtconf.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c ) target_include_directories(${PROJECT} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/ + ${CMAKE_CURRENT_SOURCE_DIR}/src/ ${CMAKE_CURRENT_SOURCE_DIR}/libco/ ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Include/ ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/Core/Include/ diff --git a/README.md b/README.md index 410176c..250332c 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,7 @@ 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 @@ -175,16 +176,16 @@ libco is licensed under the [ISC license](https://opensource.org/licenses/ISC) - https://github.com/derekmulcahy/xvcpi - OpenOCD as XVC client?? - [ ] Maybe use the ADCs for something? +- [ ] SD/MMC/SDIO (will be a pain) +- [ ] SUMP logic analyzer????? + - see also [this](https://github.com/perexg/picoprobe-sump) - [ ] AVR programming (USBavr emulation?) - AVR ISP is hardly used anymore - TPI/UPDI requires 5V levels, Pico doesn't do that :/ - debugWIRE???? - Renesas E7-{0,1,2} programming thing???? - Renesas tell us how this works pls -- Maybe steal other features from the Bus Pirate or Glasgow or so - - 1-wire? Never seen this one in the wild - - MIDI? Feels mostly gimmicky... - - PS/2? Hmmmm idk - - HD44780 LCD? See MIDI - - CAN? If I'd first be able to find a CAN device to test it with, sure +- Maybe steal other features from the Bus Pirate, [HydraBus](https://github.com/hydrabus/hydrafw) or Glasgow or so + - 1-wire and 3-wire? Never seen this one in the wild + - CAN? LIN? If I'd first be able to find a CAN device to test it with, sure diff --git a/bsp/rp2040/protocfg.h b/bsp/rp2040/protocfg.h index 7d037b4..0dc4009 100644 --- a/bsp/rp2040/protocfg.h +++ b/bsp/rp2040/protocfg.h @@ -5,11 +5,12 @@ #define DBOARD_HAS_UART #define DBOARD_HAS_CMSISDAP #define DBOARD_HAS_SERPROG -/*#define DBOARD_HAS_TINYI2C*/ +#define DBOARD_HAS_I2C #define HID_N_CMSISDAP 0 #define CDC_N_UART 0 #define CDC_N_SERPROG 1 +#define VND_N_I2CTINYUSB 0 #ifdef USE_USBCDC_FOR_STDIO #define CDC_N_STDIO 2 diff --git a/i2c-tiny-usb-misc/Makefile b/i2c-tiny-usb-misc/Makefile new file mode 100644 index 0000000..2501bd1 --- /dev/null +++ b/i2c-tiny-usb-misc/Makefile @@ -0,0 +1,8 @@ + +obj-m := i2c-tiny-usb.o +KDIR := /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +default: + $(MAKE) -C $(KDIR) M=$(PWD) modules + diff --git a/i2c-tiny-usb-misc/i2c-tiny-usb-proto.txt b/i2c-tiny-usb-misc/i2c-tiny-usb-proto.txt new file mode 100644 index 0000000..eede51b --- /dev/null +++ b/i2c-tiny-usb-misc/i2c-tiny-usb-proto.txt @@ -0,0 +1,156 @@ +all multibyte words are little-endian + +-- + +USB interface is a vendor interface, with an in and an out endpoint + +-- + +module: + cmd value index len +kernel: + request reqtype(fix) value index dmalen +i2c_msg: + cmd? type? flags index len +CMD_SET_DELAY: + delayv 0 0 +CMD_GET_STATUS: data=[status retval] + 0 0 1 + +i2c_msg: + type: byte + cmd: byte + flags: ushort + addr: ushort + len: ushort + data: byte[len] + +commands: + CMD_ECHO = 0 + CMD_GET_FUNC = 1 + CMD_SET_DELAY = 2 + CMD_GET_STATUS = 3 + + CMD_I2C_IO = 4 + CMD_I2C_IO_W_BEGIN = 5 = CMD_I2C_IO | CMD_I2C_IO_BEGIN + CMD_I2C_IO_W_END = 6 = CMD_I2C_IO | CMD_I2C_IO_END + CMD_I2C_IO_W_BEGINEND = 7 = CMD_I2C_IO | CMD_I2C_IO_BEGIN | CMD_I2C_IO_END + + CMD_I2C_IO_BEGIN = 1<<0 // the beginning of the I2C transaction: do a start cond + CMD_I2C_IO_END = 1<<1 // the end of the I2C transaction: do an end cond + // if none of the above two: repeated start condition anyway? + +statuses: + STATUS_IDLE = 0 + STATUS_ADDRESS_ACK = 1 + STATUS_DDRESS_NAK = 2 + +flags: (literally just from the kernel) + 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 */ + +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) + +USB vendor setup: + state machine: 3 8-bit registers: status, cmd, len + status is STATUS_ADDRESS_IDLE at reset + cmd, len are reset to 0 + + ECHO command: + send back flags bytes + + GET_FUNC command: + 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 + + GET_STATUS command: + return the status register (1 byte) + + + I2C_IO command: I2C xfers: + actual CMD_I2C_IO stuff: + do start cond + send (device addr << 1) | (I2C_M_RD?1:0) (aka set device address and read/write mode) + if ack bit received from previous step + status = STATUS_ADDRESS_ACK + save cmd and len in state regs + if CMD_I2C_IO_END set in cmd AND len is 0: + do end cond + else + status = STATUS_ADDRESS_NAK + do end cond + +USB vendor read: + if status == STATUS_ADDRESS_ACK: + if number of bytes in this block larger than bytes expected: + clamp this length, only use the number of expected bytes in the block + + read bytes, decrease len reg by number of bytes read + (if last byte read, set NAK in I2C xfer, else ACK) + if CMD_I2C_IO_END set in cmd reg AND len reg is 0: + do end cond + return bytes read over USB + else: + do nothing, return zeros + +USB vendor write: + if status == STATUS_ADDRESS_ACK: + if number of bytes in this block larger than bytes expected: + clamp this length, only use the number of expected bytes in the block + + write bytes, decrease len reg by number of bytes written + (if no ack bit returned, maybe set an error state??) + if CMD_I2C_IO_END set in cmd reg AND len reg is 0: + do end cond + + echo back bytes written + else: + do nothing, return zeros + diff --git a/i2c-tiny-usb-misc/i2c-tiny-usb.c b/i2c-tiny-usb-misc/i2c-tiny-usb.c new file mode 100644 index 0000000..01a44b4 --- /dev/null +++ b/i2c-tiny-usb-misc/i2c-tiny-usb.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * driver for the i2c-tiny-usb adapter - 1.0 + * http://www.harbaum.org/till/i2c_tiny_usb + * + * Copyright (C) 2006-2007 Till Harbaum (Till@Harbaum.org) + */ + +#include +#include +#include +#include +#include + +/* include interfaces to usb layer */ +#include + +/* include interface to i2c layer */ +#include + +/* commands via USB, must match command ids in the firmware */ +#define CMD_ECHO 0 +#define CMD_GET_FUNC 1 +#define CMD_SET_DELAY 2 +#define CMD_GET_STATUS 3 + +#define CMD_I2C_IO 4 +#define CMD_I2C_IO_BEGIN (1<<0) +#define CMD_I2C_IO_END (1<<1) + +/* i2c bit delay, default is 10us -> 100kHz max + (in practice, due to additional delays in the i2c bitbanging + code this results in a i2c clock of about 50kHz) */ +static unsigned short delay = 10; +module_param(delay, ushort, 0); +MODULE_PARM_DESC(delay, "bit delay in microseconds " + "(default is 10us for 100kHz max)"); + +static int usb_read(struct i2c_adapter *adapter, int cmd, + int value, int index, void *data, int len); + +static int usb_write(struct i2c_adapter *adapter, int cmd, + int value, int index, void *data, int len); + +/* ----- begin of i2c layer ---------------------------------------------- */ + +#define STATUS_IDLE 0 +#define STATUS_ADDRESS_ACK 1 +#define STATUS_ADDRESS_NAK 2 + +static int usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) +{ + unsigned char *pstatus; + struct i2c_msg *pmsg; + int i, ret, r; + + dev_dbg(&adapter->dev, "master xfer %d messages:\n", num); + + pstatus = kmalloc(sizeof(*pstatus), GFP_KERNEL); + if (!pstatus) + return -ENOMEM; + + for (i = 0 ; i < num ; i++) { + int cmd = CMD_I2C_IO; + + if (i == 0) + cmd |= CMD_I2C_IO_BEGIN; + + if (i == num-1) + cmd |= CMD_I2C_IO_END; + + pmsg = &msgs[i]; + + dev_dbg(&adapter->dev, + " %d: %s (flags %d) %d bytes to 0x%02x\n", + i, pmsg->flags & I2C_M_RD ? "read" : "write", + pmsg->flags, pmsg->len, pmsg->addr); + + /* and directly send the message */ + if (pmsg->flags & I2C_M_RD) { + /* read data */ + if ((r = usb_read(adapter, cmd, + pmsg->flags, pmsg->addr, + pmsg->buf, pmsg->len)) != pmsg->len) { + dev_err(&adapter->dev, + "failure reading data: %i\n", r); + ret = -EIO; + goto out; + } + } else { + /* write data */ + if ((r = usb_write(adapter, cmd, + pmsg->flags, pmsg->addr, + pmsg->buf, pmsg->len)) != pmsg->len) { + dev_err(&adapter->dev, + "failure writing data: %i\n", r); + ret = -EIO; + goto out; + } + } + + /* read status */ + if ((r = usb_read(adapter, CMD_GET_STATUS, 0, 0, pstatus, 1)) != 1) { + dev_err(&adapter->dev, "failure reading status: %i\n", r); + ret = -EIO; + goto out; + } + + dev_dbg(&adapter->dev, " status = %d\n", *pstatus); + if (*pstatus == STATUS_ADDRESS_NAK) { + ret = -ENXIO; + goto out; + } + } + + ret = i; +out: + kfree(pstatus); + return ret; +} + +static u32 usb_func(struct i2c_adapter *adapter) +{ + __le32 *pfunc; + u32 ret; + int i=-1; + + pfunc = kmalloc(sizeof(*pfunc), GFP_KERNEL); + + /* get functionality from adapter */ + if (!pfunc || (i=usb_read(adapter, CMD_GET_FUNC, 0, 0, pfunc, + sizeof(*pfunc))) != sizeof(*pfunc)) { + dev_err(&adapter->dev, "failure reading functionality: %i\n"); + ret = 0; + goto out; + } + + ret = le32_to_cpup(pfunc); + dev_warn(&adapter->dev, "itu func=%08x\n", ret); +out: + kfree(pfunc); + return ret; +} + +/* This is the actual algorithm we define */ +static const struct i2c_algorithm usb_algorithm = { + .master_xfer = usb_xfer, + .functionality = usb_func, +}; + +/* ----- end of i2c layer ------------------------------------------------ */ + +/* ----- begin of usb layer ---------------------------------------------- */ + +/* + * Initially the usb i2c interface uses a vid/pid pair donated by + * Future Technology Devices International Ltd., later a pair was + * bought from EZPrototypes + */ +static const struct usb_device_id i2c_tiny_usb_table[] = { + { USB_DEVICE(0x0403, 0xc631) }, /* FTDI */ + { USB_DEVICE(0x1c40, 0x0534) }, /* EZPrototypes */ + { USB_DEVICE_INTERFACE_CLASS(0xcafe, 0x4017, 0/*255*/) }, /* TinyUSB DapperMime: we want the Vendor interface */ + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, i2c_tiny_usb_table); + +/* Structure to hold all of our device specific stuff */ +struct i2c_tiny_usb { + struct usb_device *usb_dev; /* the usb device for this device */ + struct usb_interface *interface; /* the interface for this device */ + struct i2c_adapter adapter; /* i2c related things */ + bool is_tusb_dmime; +}; + +static int usb_read(struct i2c_adapter *adapter, int cmd, + int value, int index, void *data, int len) +{ + struct i2c_tiny_usb *dev = (struct i2c_tiny_usb *)adapter->algo_data; + uint8_t *dmadata; + int ret, actual_len = 0; + + if (dev->is_tusb_dmime) { +// actual_len = len; +// if (actual_len < 7) actual_len = 7; /* cmd(1), value(2), index(2), len(2) */ +// +// dmadata = (uint8_t*)kzalloc(actual_len, GFP_KERNEL); +// if (!dmadata) +// return -ENOMEM; +// +// dmadata[0] = cmd; +// dmadata[1] = value & 0xff; +// dmadata[2] = (value >> 8) & 0xff; +// dmadata[3] = index & 0xff; +// dmadata[4] = (index >> 8) & 0xff; +// dmadata[5] = len & 0xff; +// dmadata[6] = (len >> 8) & 0xff; +// +// /* send command */ +// ret = usb_bulk_msg(dev->usb_dev, usb_sndbulkpipe(dev->usb_dev, 9), +// dmadata, 7, &actual_len, 2000); +// +// dev_warn(&dev->usb_dev->dev, "read bulk msg retval=%i\n", ret); +// +// /* command received correctly */ +// if (ret == 7) { +// actual_len = 0; +// /* receive reply */ +// ret = usb_bulk_msg(dev->usb_dev, usb_rcvbulkpipe(dev->usb_dev, 9), +// dmadata, len, &actual_len, 2000); +// +// memcpy(data, dmadata, len); +// } + dmadata = kmalloc(len, GFP_KERNEL); + + if (!dmadata) + return -ENOMEM; + + /* do control transfer */ + ret = usb_control_msg(dev->usb_dev, usb_rcvctrlpipe(dev->usb_dev, 0), + cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_IN, + value, index, dmadata, len, 2000); + + memcpy(data, dmadata, len); + } else { + dmadata = kmalloc(len, GFP_KERNEL); + + if (!dmadata) + return -ENOMEM; + + /* do control transfer */ + ret = usb_control_msg(dev->usb_dev, usb_rcvctrlpipe(dev->usb_dev, 0), + cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_IN, + value, index, dmadata, len, 2000); + + memcpy(data, dmadata, len); + } + + kfree(dmadata); + return ret; +} + +static int usb_write(struct i2c_adapter *adapter, int cmd, + int value, int index, void *data, int len) +{ + struct i2c_tiny_usb *dev = (struct i2c_tiny_usb *)adapter->algo_data; + uint8_t *dmadata; + int ret, actual_len = 0; + + if (dev->is_tusb_dmime) { +// dmadata = (uint8_t*)kzalloc(len + 7, GFP_KERNEL); /* cmd(1), value(2), index(2), len(2) */ +// if (!dmadata) +// return -ENOMEM; +// +// dmadata[0] = cmd; +// dmadata[1] = value & 0xff; +// dmadata[2] = (value >> 8) & 0xff; +// dmadata[3] = index & 0xff; +// dmadata[4] = (index >> 8) & 0xff; +// dmadata[5] = len & 0xff; +// dmadata[6] = (len >> 8) & 0xff; +// +// if (data && len) +// memcpy(&dmadata[7], data, len); +// +// /* send command data */ +// ret = usb_bulk_msg(dev->usb_dev, usb_sndbulkpipe(dev->usb_dev, 9), +// dmadata, len+7, &actual_len, 2000); + dmadata = (uint8_t*)kmemdup(data, len, GFP_KERNEL); + if (!dmadata) + return -ENOMEM; + + /* do control transfer */ + ret = usb_control_msg(dev->usb_dev, usb_sndctrlpipe(dev->usb_dev, 0), + cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, index, dmadata, len, 2000); + } else { + dmadata = (uint8_t*)kmemdup(data, len, GFP_KERNEL); + if (!dmadata) + return -ENOMEM; + + /* do control transfer */ + ret = usb_control_msg(dev->usb_dev, usb_sndctrlpipe(dev->usb_dev, 0), + cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, index, dmadata, len, 2000); + } + + kfree(dmadata); + return ret; +} + +static void i2c_tiny_usb_free(struct i2c_tiny_usb *dev) +{ + usb_put_dev(dev->usb_dev); + kfree(dev); +} + +static int i2c_tiny_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct i2c_tiny_usb *dev; + int retval = -ENOMEM; + u16 version; + + dev_dbg(&interface->dev, "probing usb device\n"); + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + dev_err(&interface->dev, "Out of memory\n"); + goto error; + } + + dev->usb_dev = usb_get_dev(interface_to_usbdev(interface)); + dev->interface = interface; + + /* TinyUSB DapperMime needs different treatment because it has MANY endpoints */ + dev->is_tusb_dmime = le16_to_cpu(dev->usb_dev->descriptor.idVendor ) == 0xcafe + && le16_to_cpu(dev->usb_dev->descriptor.idProduct) == 0x4017; + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + version = le16_to_cpu(dev->usb_dev->descriptor.bcdDevice); + dev_info(&interface->dev, + "version %x.%02x found at bus %03d address %03d\n", + version >> 8, version & 0xff, + dev->usb_dev->bus->busnum, dev->usb_dev->devnum); + + /* setup i2c adapter description */ + dev->adapter.owner = THIS_MODULE; + dev->adapter.class = I2C_CLASS_HWMON; + dev->adapter.algo = &usb_algorithm; + dev->adapter.algo_data = dev; + snprintf(dev->adapter.name, sizeof(dev->adapter.name), + "i2c-tiny-usb at bus %03d device %03d", + dev->usb_dev->bus->busnum, dev->usb_dev->devnum); + + if (usb_write(&dev->adapter, CMD_SET_DELAY, delay, 0, NULL, 0) != 0) { + dev_err(/*&dev->adapter.dev*/ &dev->usb_dev->dev, /* adapter.dev is null at this point */ + "failure setting delay to %dus\n", delay); + retval = -EIO; + goto error; + } + + dev->adapter.dev.parent = &dev->interface->dev; + + /* and finally attach to i2c layer */ + i2c_add_adapter(&dev->adapter); + + /* inform user about successful attachment to i2c layer */ + dev_info(&dev->adapter.dev, "connected i2c-tiny-usb device\n"); + + return 0; + + error: + if (dev) + i2c_tiny_usb_free(dev); + + return retval; +} + +static void i2c_tiny_usb_disconnect(struct usb_interface *interface) +{ + struct i2c_tiny_usb *dev = usb_get_intfdata(interface); + + i2c_del_adapter(&dev->adapter); + usb_set_intfdata(interface, NULL); + i2c_tiny_usb_free(dev); + + dev_dbg(&interface->dev, "disconnected\n"); +} + +static struct usb_driver i2c_tiny_usb_driver = { + .name = "i2c-tiny-usb", + .probe = i2c_tiny_usb_probe, + .disconnect = i2c_tiny_usb_disconnect, + .id_table = i2c_tiny_usb_table, +}; + +module_usb_driver(i2c_tiny_usb_driver); + +/* ----- end of usb layer ------------------------------------------------ */ + +MODULE_AUTHOR("Till Harbaum "); +MODULE_DESCRIPTION("i2c-tiny-usb driver v1.0"); +MODULE_LICENSE("GPL"); diff --git a/cdc_serprog.c b/src/cdc_serprog.c similarity index 100% rename from cdc_serprog.c rename to src/cdc_serprog.c diff --git a/src/i2ctinyusb.c b/src/i2ctinyusb.c new file mode 100644 index 0000000..212323f --- /dev/null +++ b/src/i2ctinyusb.c @@ -0,0 +1,135 @@ + +#include "protocfg.h" + +#ifdef DBOARD_HAS_I2C + +#include +#include +#include + +#include "tusb.h" +#include "device/usbd_pvt.h" + +#include "protos.h" +#include "thread.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", ' ', ' ', ' ', ' ', ' '); + // ???? + return tud_control_status(rhport, req); + } + } + } + } + + 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/main.c b/src/main.c similarity index 83% rename from main.c rename to src/main.c index 198c971..de7daaf 100644 --- a/main.c +++ b/src/main.c @@ -52,6 +52,9 @@ static cothread_t mainthread #ifdef DBOARD_HAS_SERPROG , serprogthread #endif +#ifdef DBOARD_HAS_I2C + //, ituthread +#endif ; void thread_yield(void) { @@ -80,18 +83,41 @@ static void serprog_thread_fn(void) { } #endif +#ifdef DBOARD_HAS_I2C +/*static void itu_thread_fn(void) { + itu_init(); + thread_yield(); + while (1) { + itu_task(); + thread_yield(); + } +}*/ +#endif + #ifdef DBOARD_HAS_UART static uint8_t uartstack[4096]; #endif -#ifdef DBOARD_HAS_UART +#ifdef DBOARD_HAS_SERPROG static uint8_t serprogstack[4096]; #endif +#ifdef DBOARD_HAS_I2C +static uint8_t itustack[4096]; +#endif extern uint32_t co_active_buffer[64]; uint32_t co_active_buffer[64]; extern cothread_t co_active_handle; cothread_t co_active_handle; +extern char msgbuf[256]; +extern volatile bool msgflag; +static void domsg(void) { + if (msgflag) { + printf("%s", msgbuf); + //msgflag = false; + } +} + int main(void) { mainthread = co_active(); @@ -104,15 +130,17 @@ int main(void) board_init(); #ifdef DBOARD_HAS_UART - //cdc_uart_init(); uartthread = co_derive(uartstack, sizeof uartstack, uart_thread_fn); co_switch(uartthread); // will call cdc_uart_init() on correct thread #endif #ifdef DBOARD_HAS_SERPROG - //cdc_serprog_init(); serprogthread = co_derive(serprogstack, sizeof serprogstack, serprog_thread_fn); co_switch(serprogthread); // will call cdc_serprog_init() on correct thread #endif +#ifdef DBOARD_HAS_I2C + //ituthread = co_derive(itustack, sizeof itustack, itu_thread_fn); + //co_switch(ituthread); +#endif #ifdef DBOARD_HAS_CMSISDAP DAP_Setup(); #endif @@ -127,19 +155,27 @@ int main(void) { //printf("hi\n"); + domsg(); tud_task(); // tinyusb device task #ifdef DBOARD_HAS_UART //cdc_uart_task(); co_switch(uartthread); #endif + domsg(); tud_task(); // tinyusb device task #ifdef DBOARD_HAS_SERPROG //cdc_serprog_task(); co_switch(serprogthread); #endif - //printf("hi\n"); + //domsg(); + //tud_task(); +#ifdef DBOARD_HAS_I2C + //co_switch(ituthread); +#endif + + //printf("hi2\n"); } return 0; diff --git a/protos.h b/src/protos.h similarity index 80% rename from protos.h rename to src/protos.h index 5491fb7..67e3dd4 100644 --- a/protos.h +++ b/src/protos.h @@ -19,10 +19,15 @@ void cdc_serprog_task(void); #endif #ifdef USE_USBCDC_FOR_STDIO -//#ifndef PICO_BOARD +//#ifdef PICO_BOARD bool stdio_usb_init(void); //#endif #endif +#ifdef DBOARD_HAS_I2C +void itu_init(void); +void itu_task(void); +#endif + #endif diff --git a/rtconf.c b/src/rtconf.c similarity index 100% rename from rtconf.c rename to src/rtconf.c diff --git a/rtconf.h b/src/rtconf.h similarity index 100% rename from rtconf.h rename to src/rtconf.h diff --git a/serprog.h b/src/serprog.h similarity index 100% rename from serprog.h rename to src/serprog.h diff --git a/thread.h b/src/thread.h similarity index 100% rename from thread.h rename to src/thread.h diff --git a/tusb_config.h b/src/tusb_config.h similarity index 94% rename from tusb_config.h rename to src/tusb_config.h index 5ce775c..30b6d60 100644 --- a/tusb_config.h +++ b/src/tusb_config.h @@ -115,6 +115,8 @@ // CDC FIFO size of TX and RX #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_VENDOR_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_VENDOR_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #ifdef __cplusplus } diff --git a/usb_descriptors.c b/src/usb_descriptors.c similarity index 89% rename from usb_descriptors.c rename to src/usb_descriptors.c index 7bec616..9f10540 100644 --- a/usb_descriptors.c +++ b/src/usb_descriptors.c @@ -28,6 +28,7 @@ #include "protocfg.h" +// TODO: actually do this properly /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. * @@ -103,31 +104,28 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance) enum { -#ifdef DBOARD_HAS_CMSISDAP ITF_NUM_HID_CMSISDAP, -#endif - -#ifdef DBOARD_HAS_UART ITF_NUM_CDC_UART_COM, ITF_NUM_CDC_UART_DATA, -#endif - -#ifdef DBOARD_HAS_SERPROG ITF_NUM_CDC_SERPROG_COM, ITF_NUM_CDC_SERPROG_DATA, -#endif - -#ifdef USE_USBCDC_FOR_STDIO ITF_NUM_CDC_STDIO_COM, ITF_NUM_CDC_STDIO_DATA, -#endif + ITF_NUM_VND_I2CTINYUSB, ITF_NUM_TOTAL }; -/*#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_INOUT_DESC_LEN)*/ +#define TUD_I2CTINYUSB_LEN (9) +#define TUD_I2CTINYUSB_DESCRIPTOR(_itfnum, _stridx) \ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, 0, 0, 0, _stridx \ -static const int CONFIG_TOTAL_LEN = TUD_CONFIG_DESC_LEN + +/*enum {*/ static const int CONFIG_TOTAL_LEN = TUD_CONFIG_DESC_LEN +#ifdef DBOARD_HAS_I2C + + TUD_I2CTINYUSB_LEN + //+ TUD_VENDOR_DESC_LEN +#endif #ifdef DBOARD_HAS_UART + TUD_CDC_DESC_LEN #endif @@ -140,7 +138,7 @@ static const int CONFIG_TOTAL_LEN = TUD_CONFIG_DESC_LEN #ifdef USE_USBCDC_FOR_STDIO + TUD_CDC_DESC_LEN #endif - ; +/*}*/; #define EPNUM_CDC_UART_OUT 0x02 // 2 #define EPNUM_CDC_UART_IN 0x82 // 83 @@ -152,23 +150,32 @@ static const int CONFIG_TOTAL_LEN = TUD_CONFIG_DESC_LEN #define EPNUM_CDC_STDIO_OUT 0x07 #define EPNUM_CDC_STDIO_IN 0x87 #define EPNUM_CDC_STDIO_NOTIF 0x88 +#define EPNUM_VND_I2C_OUT 0x09 +#define EPNUM_VND_I2C_IN 0x89 // NOTE: if you modify this table, don't forget to keep tusb_config.h up to date as well! +// TODO: maybe add some strings to all these interfaces uint8_t const desc_configuration[] = { // Config number, interface count, string index, total length, attribute, power in mA TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), -#ifdef DBOARD_HAS_UART - // Interface number, string index, EP notification address and size, EP data address (out, in) and size. - TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_UART_COM, 0, EPNUM_CDC_UART_NOTIF, 64, EPNUM_CDC_UART_OUT, EPNUM_CDC_UART_IN, 64), -#endif - #ifdef DBOARD_HAS_CMSISDAP // Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID_CMSISDAP, 0, 0/*HID_PROTOCOL_NONE*/, sizeof(desc_hid_report), EPNUM_HID_CMSISDAP, 0x80 | (EPNUM_HID_CMSISDAP+0), CFG_TUD_HID_EP_BUFSIZE, 1), #endif +#ifdef DBOARD_HAS_I2C + // ??? + TUD_I2CTINYUSB_DESCRIPTOR(ITF_NUM_VND_I2CTINYUSB, 0), + //TUD_VENDOR_DESCRIPTOR(ITF_NUM_VND_I2CTINYUSB, 0, EPNUM_VND_I2C_OUT, EPNUM_VND_I2C_IN, 64), +#endif + +#ifdef DBOARD_HAS_UART + // Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_UART_COM, 0, EPNUM_CDC_UART_NOTIF, 64, EPNUM_CDC_UART_OUT, EPNUM_CDC_UART_IN, 64), +#endif + #ifdef DBOARD_HAS_SERPROG // Interface number, string index, EP notification address and size, EP data address (out, in) and size. TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_SERPROG_COM, 0, EPNUM_CDC_SERPROG_NOTIF, 64, EPNUM_CDC_SERPROG_OUT, EPNUM_CDC_SERPROG_IN, 64),