add vendor itf-based settings & commands for I2C and temp sensor stuff

TODOs are:
* add one for SPI too (TODO: look at linux kernel for required features)
* document all these commands somewhere
* implement linux kernel driver(s)
* maaaybeeee fix the I2C-Tiny-USB driver? idk
* also grep for minor TODOs in src
This commit is contained in:
Triss 2021-07-04 02:22:00 +02:00
parent 779c5e98f4
commit 5c17820a52
11 changed files with 263 additions and 61 deletions

View File

@ -4,11 +4,10 @@
#define DBOARD_HAS_UART
#define DBOARD_HAS_CMSISDAP
// TODO: rename to _SPI
#define DBOARD_HAS_SERPROG
// FIXME: this one doesn't work yet!!!!! (kernel usb device cfg fails)
// "usb 1-1: can't set config #1, error -32"
/*#define DBOARD_HAS_I2C*/
/*#define DBOARD_HAS_TEMPSENSOR*/
#define DBOARD_HAS_I2C
#define DBOARD_HAS_TEMPSENSOR
#include "bsp-info.h"

View File

@ -381,7 +381,7 @@ static inline int i2cex_read_timeout_us(i2c_inst_t* i2c, uint16_t addr, bool a10
return i2cex_read_blocking_until(i2c, addr, a10bit, dst, len, nostop, t);
}
__attribute__((__const__)) enum ki2c_funcs i2ctu_get_func(void) {
__attribute__((__const__)) enum ki2c_funcs i2ctu_dev_get_func(void) {
// TODO: SMBUS_EMUL_ALL => I2C_M_RECV_LEN
// TODO: maybe also PROTOCOL_MANGLING, NOSTART
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR;

View File

@ -21,6 +21,26 @@
/* CDC-Serprog */
#include "m_default/serprog.h"
/* temperature sensor */
#include "m_default/tempsensor.h"
// FIXME: this one doesn't work yet!!!!! (kernel usb device cfg fails)
// "usb 1-1: can't set config #1, error -32"
/*#define MODE_ENABLE_I2CTINYUSB*/
enum m_default_cmds {
mdef_cmd_spi = mode_cmd__specific,
mdef_cmd_i2c,
mdef_cmd_tempsense
};
enum m_default_feature {
mdef_feat_uart = 1<<0,
mdef_feat_cmsisdap = 1<<1,
mdef_feat_spi = 1<<2,
mdef_feat_i2c = 1<<3,
mdef_feat_tempsense = 1<<4,
};
#ifdef DBOARD_HAS_UART
static cothread_t uartthread;
static uint8_t uartstack[THREAD_STACK_SIZE];
@ -94,11 +114,46 @@ static void task_cb(void) {
static void handle_cmd_cb(uint8_t cmd) {
uint8_t resp = 0;
// TODO: tempsensor control commands!
switch (cmd) {
case mode_cmd_get_features:
#ifdef DBOARD_HAS_UART
resp |= mdef_feat_uart;
#endif
#ifdef DBOARD_HAS_CMSISDAP
resp |= mdef_feat_cmsisdap;
#endif
#ifdef DBOARD_HAS_SERPROG
resp |= mdef_feat_spi;
#endif
#ifdef DBOARD_HAS_I2C
resp |= mdef_feat_i2c;
#endif
#ifdef DBOARD_HAS_TEMPSENSOR
resp |= mdef_feat_tempsense;
#endif
vnd_cfg_write_resp(cfg_resp_ok, 1, &resp);
break;
case mdef_cmd_spi:
#ifdef DBOARD_HAS_SERPROG
sp_spi_bulk_cmd();
#else
vnd_cfg_write_resp(cfg_resp_illcmd, 0, NULL);
#endif
break;
case mdef_cmd_i2c:
#ifdef DBOARD_HAS_I2C
i2ctu_bulk_cmd();
#else
vnd_cfg_write_resp(cfg_resp_illcmd, 0, NULL);
#endif
break;
case mdef_cmd_tempsense:
#ifdef DBOARD_HAS_TEMPSENSOR
tempsense_bulk_cmd();
#else
vnd_cfg_write_resp(cfg_resp_illcmd, 0, NULL);
#endif
break;
default:
vnd_cfg_write_resp(cfg_resp_illcmd, 0, NULL);
break;
@ -129,7 +184,7 @@ enum {
#if CFG_TUD_VENDOR > 0
ITF_NUM_VND_CFG,
#endif
#ifdef DBOARD_HAS_I2C
#if defined(DBOARD_HAS_I2C) && defined(MODE_ENABLE_I2CTINYUSB)
ITF_NUM_VND_I2CTINYUSB,
#endif
#ifdef DBOARD_HAS_CMSISDAP
@ -156,7 +211,7 @@ enum {
#if CFG_TUD_VENDOR > 0
+ TUD_VENDOR_DESC_LEN
#endif
#ifdef DBOARD_HAS_I2C
#if defined(DBOARD_HAS_I2C) && defined(MODE_ENABLE_I2CTINYUSB)
+ TUD_I2CTINYUSB_LEN
#endif
#ifdef DBOARD_HAS_CMSISDAP
@ -213,7 +268,7 @@ static const uint8_t desc_configuration[] = {
EPNUM_VND_CFG_IN, 64),
#endif
#ifdef DBOARD_HAS_I2C
#if defined(DBOARD_HAS_I2C) && defined(MODE_ENABLE_I2CTINYUSB)
TUD_I2CTINYUSB_DESCRIPTOR(ITF_NUM_VND_I2CTINYUSB, STRID_IF_VND_I2CTINYUSB),
#endif
@ -288,7 +343,7 @@ static void my_hid_set_report_cb(uint8_t instance, uint8_t report_id,
}
#endif
#ifdef DBOARD_HAS_I2CTINYUSB
#if defined(DBOARD_HAS_I2C) && defined(MODE_ENABLE_I2CTINYUSB)
static bool my_vendor_control_xfer_cb(uint8_t rhport, uint8_t ep_addr,
tusb_control_request_t const* req) {
return i2ctu_ctl_req(rhport, ep_addr, req);
@ -318,7 +373,7 @@ struct mode m_01_default = {
.tud_hid_descriptor_report_cb = my_hid_descriptor_report_cb,
#endif
#if defined(DBOARD_HAS_I2CTINYUSB)
#if defined(DBOARD_HAS_I2C) && defined(MODE_ENABLE_I2CTINYUSB)
.tud_vendor_control_xfer_cb = i2ctu_ctl_req,
#endif
};

View File

@ -11,6 +11,7 @@
#include "info.h"
#include "util.h"
#include "thread.h"
#include "vnd_cfg.h"
#include "serprog.h"
@ -219,5 +220,10 @@ static void handle_cmd(void) {
void cdc_serprog_task(void) { handle_cmd(); }
void sp_spi_bulk_cmd(void) {
// TODO
vnd_cfg_write_resp(cfg_resp_illcmd, 0, NULL);
}
#endif /* DBOARD_HAS_SERPROG */

View File

@ -105,6 +105,7 @@ enum itu_status i2ctu_dev_read(enum ki2c_flags flags, enum itu_command startstop
void i2ctu_init(void);
void i2ctu_deinit(void);
bool i2ctu_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const* req);
void i2ctu_bulk_cmd(void); /* uses data in/out from vnd_cfg.h */
#endif
#endif

View File

@ -59,6 +59,7 @@ static inline void sp_spi_op_do(
void cdc_serprog_init(void);
void cdc_serprog_deinit(void);
void cdc_serprog_task(void);
void sp_spi_bulk_cmd(void);
#endif
#endif

View File

@ -13,6 +13,8 @@
// clang-format on
#endif
#include "vnd_cfg.h"
#ifdef DBOARD_HAS_TEMPSENSOR
#include "tempsensor.h"
@ -251,5 +253,46 @@ int tempsense_do_write(int length, const uint8_t* buf) {
return i;
}
void tempsense_bulk_cmd(void) {
uint16_t temp;
uint8_t resp[2];
switch (vnd_cfg_read_byte()) {
case tcmd_get_addr:
resp[0] = tempsense_get_addr();
vnd_cfg_write_resp(cfg_resp_ok, 1, resp);
break;
case tcmd_set_addr:
resp[0] = tempsense_get_addr();
tempsense_set_addr(vnd_cfg_read_byte());
resp[1] = tempsense_get_addr();
vnd_cfg_write_resp(cfg_resp_ok, 2, resp);
break;
case tcmd_get_temp:
temp = tempsense_dev_get_temp();
resp[0] = temp & 0xff;
resp[1] = (temp >> 8) & 0xff;
vnd_cfg_write_resp(cfg_resp_ok, 2, resp);
case tcmd_get_lower:
temp = tempsense_dev_get_lower();
resp[0] = temp & 0xff;
resp[1] = (temp >> 8) & 0xff;
vnd_cfg_write_resp(cfg_resp_ok, 2, resp);
case tcmd_get_upper:
temp = tempsense_dev_get_upper();
resp[0] = temp & 0xff;
resp[1] = (temp >> 8) & 0xff;
vnd_cfg_write_resp(cfg_resp_ok, 2, resp);
case tcmd_get_crit:
temp = tempsense_dev_get_crit();
resp[0] = temp & 0xff;
resp[1] = (temp >> 8) & 0xff;
vnd_cfg_write_resp(cfg_resp_ok, 2, resp);
default:
vnd_cfg_write_resp(cfg_resp_illcmd, 0, NULL);
break;
}
}
#endif

View File

@ -6,6 +6,15 @@
#include <stdbool.h>
#include <stdint.h>
enum tempsense_cmd {
tcmd_get_addr,
tcmd_set_addr,
tcmd_get_temp,
tcmd_get_lower,
tcmd_get_upper,
tcmd_get_crit
};
void tempsense_init(void);
void tempsense_deinit(void);
@ -19,6 +28,8 @@ int tempsense_do_read(int length, uint8_t* buf);
int tempsense_do_write(int length, const uint8_t* buf);
void tempsense_do_stop(void); // stop cond
void tempsense_bulk_cmd(void);
#ifdef DBOARD_HAS_TEMPSENSOR
void tempsense_dev_init(void);
void tempsense_dev_deinit(void);

View File

@ -1,3 +1,4 @@
// vim: set et:
#include "m_default/bsp-feature.h"
@ -13,7 +14,7 @@
#include <tusb.h>
#include <device/usbd_pvt.h>
#include "m_default/bsp-feature.h"
#include "vnd_cfg.h"
#include "i2ctinyusb.h"
@ -46,6 +47,61 @@ void i2ctu_deinit(void) {
#endif
}
static void handle_probe(struct itu_cmd* cmd) {
uint8_t bleh = 0;
#ifdef DBOARD_HAS_TEMPSENSOR
if (tempsense_get_active() && tempsense_get_addr() == cmd->addr) {
if (cmd->cmd & ITU_CMD_I2C_IO_BEGIN_F) tempsense_do_start();
int rv = tempsense_do_write(0, &bleh);
if (rv < 0 || rv != cmd->len)
status = ITU_STATUS_ADDR_NAK;
else
status = ITU_STATUS_ADDR_ACK;
if (cmd->cmd & ITU_CMD_I2C_IO_END_F) tempsense_do_stop();
} else
#endif
{
status = i2ctu_dev_write(cmd->flags, cmd->cmd & ITU_CMD_I2C_IO_DIR_MASK,
cmd->addr, &bleh, 0);
}
}
static void handle_read(struct itu_cmd* cmd) {
#ifdef DBOARD_HAS_TEMPSENSOR
if (tempsense_get_active() && tempsense_get_addr() == cmd->addr) {
if (cmd->cmd & ITU_CMD_I2C_IO_BEGIN_F) tempsense_do_start();
int rv = tempsense_do_read(
cmd->len > sizeof txbuf ? sizeof txbuf : cmd->len, txbuf);
if (rv < 0 || rv != cmd->len)
status = ITU_STATUS_ADDR_NAK;
else
status = ITU_STATUS_ADDR_ACK;
if (cmd->cmd & ITU_CMD_I2C_IO_END_F) tempsense_do_stop();
} else
#endif
{
status = i2ctu_dev_read(cmd->flags, cmd->cmd & ITU_CMD_I2C_IO_DIR_MASK, cmd->addr,
txbuf, cmd->len > sizeof txbuf ? sizeof txbuf : cmd->len);
}
}
static void handle_write(struct itu_cmd* cmd) {
#ifdef DBOARD_HAS_TEMPSENSOR
if (tempsense_get_active() && tempsense_get_addr() == cmd->addr) {
if (cmd->cmd & ITU_CMD_I2C_IO_BEGIN_F) tempsense_do_start();
// FIXME: fix status handling
int rv = tempsense_do_write(cmd->len > sizeof rxbuf ? sizeof rxbuf : cmd->len, rxbuf);
if (rv < 0 || rv != cmd->len)
status = ITU_STATUS_ADDR_NAK;
else
status = ITU_STATUS_ADDR_ACK;
if (cmd->cmd & ITU_CMD_I2C_IO_END_F) tempsense_do_stop();
} else
#endif
{
status = i2ctu_dev_write(cmd->flags, cmd->cmd & ITU_CMD_I2C_IO_DIR_MASK, cmd->addr, rxbuf,
cmd->len > sizeof rxbuf ? sizeof rxbuf : cmd->len);
}
}
bool i2ctu_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const* req) {
(void)rhport;
@ -67,22 +123,7 @@ bool i2ctu_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const*
// printf("WDATA a=%04hx l=%04hx ", cmd.addr, cmd.len);
// printf("data=%02x %02x...\n", rxbuf[0], rxbuf[1]);
#ifdef DBOARD_HAS_TEMPSENSOR
if (tempsense_get_active() && tempsense_get_addr() == cmd.addr) {
if (cmd.cmd & ITU_CMD_I2C_IO_BEGIN_F) tempsense_do_start();
// FIXME: fix status handling
int rv = tempsense_do_write(cmd.len > sizeof rxbuf ? sizeof rxbuf : cmd.len, rxbuf);
if (rv < 0 || rv != cmd.len)
status = ITU_STATUS_ADDR_NAK;
else
status = ITU_STATUS_ADDR_ACK;
if (cmd.cmd & ITU_CMD_I2C_IO_END_F) tempsense_do_stop();
} else
#endif
{
status = i2ctu_dev_write(cmd.flags, cmd.cmd & ITU_CMD_I2C_IO_DIR_MASK, cmd.addr, rxbuf,
cmd.len > sizeof rxbuf ? sizeof rxbuf : cmd.len);
}
handle_write(&cmd);
// cancel curcmd
curcmd.cmd = 0xff;
@ -140,44 +181,14 @@ bool i2ctu_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const*
if (cmd.flags & I2C_M_RD) { // read from I2C device
// printf("read addr=%04hx len=%04hx ", cmd.addr, cmd.len);
#ifdef DBOARD_HAS_TEMPSENSOR
if (tempsense_get_active() && tempsense_get_addr() == cmd.addr) {
if (cmd.cmd & ITU_CMD_I2C_IO_BEGIN_F) tempsense_do_start();
int rv = tempsense_do_read(
cmd.len > sizeof txbuf ? sizeof txbuf : cmd.len, txbuf);
if (rv < 0 || rv != cmd.len)
status = ITU_STATUS_ADDR_NAK;
else
status = ITU_STATUS_ADDR_ACK;
if (cmd.cmd & ITU_CMD_I2C_IO_END_F) tempsense_do_stop();
} else
#endif
{
status = i2ctu_dev_read(cmd.flags, cmd.cmd & ITU_CMD_I2C_IO_DIR_MASK, cmd.addr,
txbuf, cmd.len > sizeof txbuf ? sizeof txbuf : cmd.len);
}
handle_read(&cmd);
// 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;
#ifdef DBOARD_HAS_TEMPSENSOR
if (tempsense_get_active() && tempsense_get_addr() == cmd.addr) {
if (cmd.cmd & ITU_CMD_I2C_IO_BEGIN_F) tempsense_do_start();
int rv = tempsense_do_write(0, &bleh);
if (rv < 0 || rv != cmd.len)
status = ITU_STATUS_ADDR_NAK;
else
status = ITU_STATUS_ADDR_ACK;
if (cmd.cmd & ITU_CMD_I2C_IO_END_F) tempsense_do_stop();
} else
#endif
{
status = i2ctu_dev_write(cmd.flags, cmd.cmd & ITU_CMD_I2C_IO_DIR_MASK,
cmd.addr, &bleh, 0);
}
handle_probe(&cmd);
// printf("probe -> %d\n", status);
return tud_control_status(rhport, req);
} else {
@ -197,5 +208,77 @@ bool i2ctu_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const*
return true; // other stage...
}
void i2ctu_bulk_cmd(void) {
uint16_t us;
uint32_t func, freq;
switch (vnd_cfg_read_byte()) {
case ITU_CMD_ECHO:
txbuf[0] = vnd_cfg_read_byte();
vnd_cfg_write_resp(cfg_resp_ok, 1, txbuf);
break;
case ITU_CMD_GET_FUNC:
func = i2ctu_dev_get_func();
txbuf[0] = func & 0xff;
txbuf[1] = (func >> 8) & 0xff;
txbuf[2] = (func >> 16) & 0xff;
txbuf[3] = (func >> 24) & 0xff;
vnd_cfg_write_resp(cfg_resp_ok, 4, txbuf);
break;
case ITU_CMD_SET_DELAY:
us = (uint16_t)vnd_cfg_read_byte();
us |= ((uint16_t)vnd_cfg_read_byte() << 8);
if (us == 0) us = 1;
freq = 1000 * 1000 / (uint32_t)us;
if (i2ctu_dev_set_freq(freq, us) != 0)
vnd_cfg_write_resp(cfg_resp_ok, 0, NULL);
else
vnd_cfg_write_resp(cfg_resp_badarg, 0, NULL);
break;
case ITU_CMD_GET_STATUS:
txbuf[0] = status;
vnd_cfg_write_resp(cfg_resp_ok, 1, txbuf);
break;
case ITU_CMD_I2C_IO:
case ITU_CMD_I2C_IO_BEGIN:
case ITU_CMD_I2C_IO_END:
case ITU_CMD_I2C_IO_BEGINEND: {
struct itu_cmd cmd;
cmd.cmd = vnd_cfg_read_byte();
cmd.flags = (uint16_t)vnd_cfg_read_byte();
cmd.flags |= (uint16_t)vnd_cfg_read_byte() << 8;
cmd.addr = (uint16_t)vnd_cfg_read_byte();
cmd.addr |= (uint16_t)vnd_cfg_read_byte() << 8;
cmd.len = (uint16_t)vnd_cfg_read_byte();
cmd.len |= (uint16_t)vnd_cfg_read_byte() << 8;
if (cmd.flags & I2C_M_RD) {
handle_read(&cmd);
us = cmd.len;
if (us > sizeof txbuf) us = sizeof txbuf;
vnd_cfg_write_resp(cfg_resp_ok, us, txbuf);
} else if (cmd.len == 0) {
handle_probe(&cmd);
txbuf[0] = status;
vnd_cfg_write_resp(cfg_resp_ok, 1, txbuf);
} else {
us = cmd.len;
if (us > sizeof rxbuf) us = sizeof rxbuf;
for (size_t i = 0; i < us; ++i)
rxbuf[i] = vnd_cfg_read_byte();
handle_write(&cmd);
}
} break;
default:
vnd_cfg_write_resp(cfg_resp_illcmd, 0, NULL);
break;
}
}
#endif /* DBOARD_HAS_I2C */

View File

@ -7,7 +7,7 @@
.global tusb_plt
tusb_plt:
@ sigh, thumb...
@ sigh, thumb... (cant do `ldr r12, ...`)
.type tud_hid_get_report_cb, %function
.global tud_hid_get_report_cb
tud_hid_get_report_cb:

View File

@ -28,6 +28,8 @@ enum mode_cmd {
mode_cmd_get_name = 0x00,
mode_cmd_get_version = 0x01,
mode_cmd_get_features = 0x02,
mode_cmd__specific = 0x03
};
enum cfg_resp {
@ -35,6 +37,7 @@ enum cfg_resp {
cfg_resp_illcmd = 0x01,
cfg_resp_badmode = 0x02,
cfg_resp_nosuchmode = 0x03,
cfg_resp_badarg = 0x04
};
uint8_t vnd_cfg_read_byte (void);