untested but finished I2C-Tiny-USB impl
This commit is contained in:
parent
b5e29c1dd7
commit
191c9025e8
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
|
||||
#include <pico/stdlib.h>
|
||||
#include <pico/binary_info.h>
|
||||
#include <hardware/i2c.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
#include "protos.h"
|
||||
|
||||
bool stdio_usb_init(void) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
#include "protos.h"
|
||||
|
||||
void cdc_uart_init(void) {
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
138
src/i2ctinyusb.c
138
src/i2ctinyusb.c
|
@ -1,138 +0,0 @@
|
|||
|
||||
#include "protocfg.h"
|
||||
|
||||
#ifdef DBOARD_HAS_I2C
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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 */
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
|
||||
#ifndef I2CTINYUSB_H_
|
||||
#define I2CTINYUSB_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
|
||||
#include "protos.h"
|
||||
|
||||
#ifdef DBOARD_HAS_I2C
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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 */
|
||||
|
Loading…
Reference in New Issue