misc/usbdbg: Add CDC read/write callbacks.

These callbacks allow the debug code to read/write directly from
the stack, if possible, to avoid the extra memcpy.
This commit is contained in:
iabdalkader 2024-08-03 10:40:25 +03:00
parent 6b1308fcc5
commit f306d07b03
3 changed files with 151 additions and 146 deletions

View File

@ -20,12 +20,13 @@
#include "tusb.h"
#include "usbdbg.h"
#include "tinyusb_debug.h"
#include "omv_common.h"
#define DEBUG_MAX_PACKET (OMV_TUSBDBG_PACKET)
#define DEBUG_BAUDRATE_SLOW (921600)
#define DEBUG_BAUDRATE_FAST (12000000)
#define DEBUG_BAUDRATE_SLOW (921600)
#define DEBUG_BAUDRATE_FAST (12000000)
#define DEBUG_EP_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
extern void __fatal_error();
void NORETURN __fatal_error(const char *msg);
typedef struct __attribute__((packed)) {
uint8_t cmd;
@ -34,23 +35,23 @@ typedef struct __attribute__((packed)) {
}
usbdbg_cmd_t;
static uint8_t debug_ringbuf_array[OMV_TUSBDBG_BUFFER];
static uint8_t tx_array[OMV_TUSBDBG_BUFFER];
static ringbuf_t tx_ringbuf = { tx_array, sizeof(tx_array) };
static volatile bool tinyusb_debug_mode = false;
ringbuf_t debug_ringbuf = { debug_ringbuf_array, sizeof(debug_ringbuf_array) };
uint32_t usb_cdc_buf_len() {
return ringbuf_avail(&debug_ringbuf);
return ringbuf_avail(&tx_ringbuf);
}
uint32_t usb_cdc_get_buf(uint8_t *buf, uint32_t len) {
// Read all of the request bytes.
if (ringbuf_get_bytes(&debug_ringbuf, buf, len) == 0) {
if (ringbuf_get_bytes(&tx_ringbuf, buf, len) == 0) {
return len;
}
// Try to read as much data as possible.
uint32_t bytes = 0;
for (; bytes < len; bytes++) {
int c = ringbuf_get(&debug_ringbuf);
int c = ringbuf_get(&tx_ringbuf);
if (c == -1) {
break;
}
@ -59,9 +60,13 @@ uint32_t usb_cdc_get_buf(uint8_t *buf, uint32_t len) {
return bytes;
}
void usb_cdc_reset_buffers() {
}
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const *coding) {
debug_ringbuf.iget = 0;
debug_ringbuf.iput = 0;
tx_ringbuf.iget = 0;
tx_ringbuf.iput = 0;
if (0) {
#if defined(MICROPY_BOARD_ENTER_BOOTLOADER)
@ -112,9 +117,9 @@ mp_uint_t __wrap_mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
// rate and fast print rate. When this happens, reset the buffer and start
// over, if this string fits entirely in the buffer. This helps the ring buffer
// self-recover from broken strings.
if (ringbuf_put(&debug_ringbuf, str[i]) == -1 && len <= debug_ringbuf.size) {
debug_ringbuf.iget = 0;
debug_ringbuf.iput = 0;
if (ringbuf_put(&tx_ringbuf, str[i]) == -1 && len <= tx_ringbuf.size) {
tx_ringbuf.iget = 0;
tx_ringbuf.iput = 0;
}
}
NVIC_EnableIRQ(PendSV_IRQn);
@ -132,20 +137,16 @@ static void tinyusb_debug_task(void) {
return;
}
uint8_t dbg_buf[DEBUG_MAX_PACKET];
uint32_t count = tud_cdc_read(dbg_buf, 6);
usbdbg_cmd_t cmd;
tud_cdc_read(&cmd, sizeof(cmd));
if (count < 6 || dbg_buf[0] != 0x30) {
// Maybe we should try to recover from this state
// but for now, call __fatal_error which doesn't
// return.
__fatal_error();
return;
if (cmd.cmd != 0x30) {
// TODO: Try to recover from this state but for now, call __fatal_error.
__fatal_error("Invalid USB CMD received.");
}
usbdbg_cmd_t *cmd = (usbdbg_cmd_t *) dbg_buf;
uint8_t request = cmd->request;
uint32_t xfer_length = cmd->xfer_length;
uint8_t request = cmd.request;
uint32_t xfer_length = cmd.xfer_length;
usbdbg_control(NULL, request, xfer_length);
while (xfer_length) {
@ -155,19 +156,21 @@ static void tinyusb_debug_task(void) {
}
if (request & 0x80) {
// Device-to-host data phase
int bytes = MIN(xfer_length, DEBUG_MAX_PACKET);
if (bytes <= tud_cdc_write_available()) {
xfer_length -= bytes;
usbdbg_data_in(dbg_buf, bytes);
tud_cdc_write(dbg_buf, bytes);
int size = OMV_MIN(xfer_length, tud_cdc_write_available());
if (size) {
xfer_length -= size;
usbdbg_data_in(size, tud_cdc_write);
}
if (size > 0 && size < DEBUG_EP_SIZE) {
tud_cdc_write_flush();
}
tud_cdc_write_flush();
} else {
// Host-to-device data phase
int bytes = MIN(xfer_length, DEBUG_MAX_PACKET);
uint32_t count = tud_cdc_read(dbg_buf, bytes);
xfer_length -= count;
usbdbg_data_out(dbg_buf, count);
int size = OMV_MIN(xfer_length, tud_cdc_available());
if (size) {
xfer_length -= size;
usbdbg_data_out(size, tud_cdc_read);
}
}
}
}

View File

@ -27,8 +27,8 @@
#include "omv_boardconfig.h"
#include "py_image.h"
static int xfer_bytes;
static int xfer_length;
static int xfer_offs;
static int xfer_size;
static enum usbdbg_cmd cmd;
static volatile bool script_ready;
@ -36,13 +36,11 @@ static volatile bool script_running;
static volatile bool irq_enabled;
static vstr_t script_buf;
// These functions must be implemented in MicroPython CDC driver.
// These functions must be implemented by the stack.
extern uint32_t usb_cdc_buf_len();
extern uint32_t usb_cdc_get_buf(uint8_t *buf, uint32_t len);
extern void usb_cdc_reset_buffers();
void __attribute__((weak)) usb_cdc_reset_buffers() {
}
void usbdbg_init() {
cmd = USBDBG_NONE;
@ -100,47 +98,54 @@ bool usbdbg_get_irq_enabled() {
return irq_enabled;
}
void usbdbg_data_in(void *buffer, int length) {
void usbdbg_data_in(uint32_t size, usbdbg_write_callback_t write_callback) {
switch (cmd) {
case USBDBG_FW_VERSION: {
uint32_t *ver_buf = buffer;
ver_buf[0] = FIRMWARE_VERSION_MAJOR;
ver_buf[1] = FIRMWARE_VERSION_MINOR;
ver_buf[2] = FIRMWARE_VERSION_PATCH;
uint32_t buffer[3] = {
FIRMWARE_VERSION_MAJOR,
FIRMWARE_VERSION_MINOR,
FIRMWARE_VERSION_PATCH,
};
cmd = USBDBG_NONE;
write_callback(&buffer, sizeof(buffer));
break;
}
case USBDBG_TX_BUF_LEN: {
uint32_t tx_buf_len = usb_cdc_buf_len();
memcpy(buffer, &tx_buf_len, sizeof(tx_buf_len));
cmd = USBDBG_NONE;
write_callback(&tx_buf_len, sizeof(tx_buf_len));
break;
}
case USBDBG_SENSOR_ID: {
int sensor_id = 0xFF;
uint32_t buffer = 0xFF;
#if MICROPY_PY_SENSOR
if (sensor_is_detected() == true) {
sensor_id = sensor_get_id();
buffer = sensor_get_id();
}
#endif
memcpy(buffer, &sensor_id, 4);
cmd = USBDBG_NONE;
write_callback(&buffer, sizeof(buffer));
break;
}
case USBDBG_TX_BUF: {
xfer_bytes += usb_cdc_get_buf(buffer, length);
if (xfer_bytes == xfer_length) {
uint8_t buffer[size];
size = usb_cdc_get_buf(buffer, size);
xfer_offs += size;
if (xfer_offs == xfer_size) {
cmd = USBDBG_NONE;
}
if (size) {
write_callback(buffer, size);
}
break;
}
case USBDBG_FRAME_SIZE:
case USBDBG_FRAME_SIZE: {
// Return 0 if FB is locked or not ready.
((uint32_t *) buffer)[0] = 0;
uint32_t buffer[3] = { 0 };
// Try to lock FB. If header size == 0 frame is not ready
if (mutex_try_lock_alternate(&JPEG_FB()->lock, MUTEX_TID_IDE)) {
// If header size == 0 frame is not ready
@ -149,19 +154,21 @@ void usbdbg_data_in(void *buffer, int length) {
mutex_unlock(&JPEG_FB()->lock, MUTEX_TID_IDE);
} else {
// Return header w, h and size/bpp
((uint32_t *) buffer)[0] = JPEG_FB()->w;
((uint32_t *) buffer)[1] = JPEG_FB()->h;
((uint32_t *) buffer)[2] = JPEG_FB()->size;
buffer[0] = JPEG_FB()->w;
buffer[1] = JPEG_FB()->h;
buffer[2] = JPEG_FB()->size;
}
}
cmd = USBDBG_NONE;
write_callback(&buffer, sizeof(buffer));
break;
}
case USBDBG_FRAME_DUMP:
if (xfer_bytes < xfer_length) {
memcpy(buffer, JPEG_FB()->pixels + xfer_bytes, length);
xfer_bytes += length;
if (xfer_bytes == xfer_length) {
if (xfer_offs < xfer_size) {
write_callback(JPEG_FB()->pixels + xfer_offs, size);
xfer_offs += size;
if (xfer_offs == xfer_size) {
cmd = USBDBG_NONE;
JPEG_FB()->w = 0; JPEG_FB()->h = 0; JPEG_FB()->size = 0;
mutex_unlock(&JPEG_FB()->lock, MUTEX_TID_IDE);
@ -170,6 +177,7 @@ void usbdbg_data_in(void *buffer, int length) {
break;
case USBDBG_ARCH_STR: {
uint8_t buffer[64];
unsigned int uid[3] = {
#if (OMV_BOARD_UID_SIZE == 2)
0U,
@ -182,29 +190,29 @@ void usbdbg_data_in(void *buffer, int length) {
snprintf((char *) buffer, 64, "%s [%s:%08X%08X%08X]",
OMV_BOARD_ARCH, OMV_BOARD_TYPE, uid[0], uid[1], uid[2]);
cmd = USBDBG_NONE;
write_callback(&buffer, sizeof(buffer));
break;
}
case USBDBG_SCRIPT_RUNNING: {
uint32_t *buf = buffer;
buf[0] = (uint32_t) script_running;
cmd = USBDBG_NONE;
uint32_t buffer = script_running;
write_callback(&buffer, sizeof(buffer));
break;
}
case USBDBG_GET_STATE:
// Clear flags
((uint32_t *) buffer)[0] = 0;
case USBDBG_GET_STATE: {
uint32_t buffer[16] = { 0 };
// Set script running flag
if (script_running) {
((uint32_t *) buffer)[0] |= USBDBG_STATE_FLAGS_SCRIPT;
buffer[0] |= USBDBG_STATE_FLAGS_SCRIPT;
}
// Set text buf valid flag.
uint32_t tx_buf_len = usb_cdc_buf_len();
if (tx_buf_len) {
((uint32_t *) buffer)[0] |= USBDBG_STATE_FLAGS_TEXT;
buffer[0] |= USBDBG_STATE_FLAGS_TEXT;
}
// Try to lock FB. If header size == 0 frame is not ready
@ -214,13 +222,13 @@ void usbdbg_data_in(void *buffer, int length) {
// unlock FB
mutex_unlock(&JPEG_FB()->lock, MUTEX_TID_IDE);
} else {
// Set frame width, height and size/bpp
((uint32_t *) buffer)[1] = JPEG_FB()->w;
((uint32_t *) buffer)[2] = JPEG_FB()->h;
((uint32_t *) buffer)[3] = JPEG_FB()->size;
// Set valid frame flag.
((uint32_t *) buffer)[0] |= USBDBG_STATE_FLAGS_FRAME;
buffer[0] |= USBDBG_STATE_FLAGS_FRAME;
// Set frame width, height and size/bpp
buffer[1] = JPEG_FB()->w;
buffer[2] = JPEG_FB()->h;
buffer[3] = JPEG_FB()->size;
}
}
@ -233,19 +241,20 @@ void usbdbg_data_in(void *buffer, int length) {
}
cmd = USBDBG_NONE;
write_callback(&buffer, sizeof(buffer));
break;
}
default: /* error */
break;
}
}
void usbdbg_data_out(void *buffer, int length) {
void usbdbg_data_out(uint32_t size, usbdbg_read_callback_t read_callback) {
switch (cmd) {
case USBDBG_FB_ENABLE: {
uint32_t enable = *((int32_t *) buffer);
JPEG_FB()->enabled = enable;
if (enable == 0) {
read_callback(&(JPEG_FB()->enabled), 4);
if (JPEG_FB()->enabled == 0) {
// When disabling framebuffer, the IDE might still be holding FB lock.
// If the IDE is not the current lock owner, this operation is ignored.
mutex_unlock(&JPEG_FB()->lock, MUTEX_TID_IDE);
@ -256,16 +265,17 @@ void usbdbg_data_out(void *buffer, int length) {
case USBDBG_SCRIPT_EXEC:
// check if GC is locked before allocating memory for vstr. If GC was locked
// at least once before the script is fully uploaded xfer_bytes will be less
// than the total length (xfer_length) and the script will Not be executed.
// at least once before the script is fully uploaded xfer_offs will be less
// than the total size (xfer_size) and the script will Not be executed.
if (!script_running) {
nlr_buf_t nlr;
if (!gc_is_locked() && nlr_push(&nlr) == 0) {
vstr_add_strn(&script_buf, buffer, length);
char *buffer = vstr_add_len(&script_buf, size);
read_callback(buffer, size);
nlr_pop();
}
xfer_bytes += length;
if (xfer_bytes == xfer_length) {
xfer_offs += size;
if (xfer_offs == xfer_size) {
cmd = USBDBG_NONE;
// Schedule the IDE exception to interrupt the VM.
usbdbg_interrupt_vm(true);
@ -278,17 +288,15 @@ void usbdbg_data_out(void *buffer, int length) {
image_t image;
framebuffer_init_image(&image);
// null terminate the path
length = (length == 64) ? 63:length;
((char *) buffer)[length] = 0;
size = MIN(128, size);
char buffer[size];
read_callback(buffer, size);
buffer[size - 1] = 0;
rectangle_t *roi = (rectangle_t *) buffer;
char *path = (char *) buffer + sizeof(rectangle_t);
imlib_save_image(&image, path, roi, 50);
// raise a flash IRQ to flush image
//NVIC->STIR = FLASH_IRQn;
#endif //IMLIB_ENABLE_IMAGE_FILE_IO
break;
}
@ -299,9 +307,10 @@ void usbdbg_data_out(void *buffer, int length) {
image_t image;
framebuffer_init_image(&image);
// null terminate the path
length = (length == 64) ? 63:length;
((char *) buffer)[length] = 0;
size = MIN(128, size);
char buffer[size];
read_callback(buffer, size);
buffer[size - 1] = 0;
rectangle_t *roi = (rectangle_t *) buffer;
char *path = (char *) buffer + sizeof(rectangle_t);
@ -314,21 +323,24 @@ void usbdbg_data_out(void *buffer, int length) {
case USBDBG_ATTR_WRITE: {
#if MICROPY_PY_SENSOR
/* write sensor attribute */
int32_t attr = *((int32_t *) buffer);
int32_t val = *((int32_t *) buffer + 1);
switch (attr) {
struct {
int32_t name;
int32_t value;
}
attr;
read_callback(&attr, sizeof(attr));
switch (attr.name) {
case ATTR_CONTRAST:
sensor_set_contrast(val);
sensor_set_contrast(attr.value);
break;
case ATTR_BRIGHTNESS:
sensor_set_brightness(val);
sensor_set_brightness(attr.value);
break;
case ATTR_SATURATION:
sensor_set_saturation(val);
sensor_set_saturation(attr.value);
break;
case ATTR_GAINCEILING:
sensor_set_gainceiling(val);
sensor_set_gainceiling(attr.value);
break;
default:
break;
@ -340,61 +352,48 @@ void usbdbg_data_out(void *buffer, int length) {
case USBDBG_SET_TIME: {
// TODO implement
#if 0
uint32_t *timebuf = (uint32_t *) buffer;
timebuf[0]; // Year
timebuf[1]; // Month
timebuf[2]; // Day
timebuf[3]; // Day of the week
timebuf[4]; // Hour
timebuf[5]; // Minute
timebuf[6]; // Second
timebuf[7]; // Milliseconds
#endif
uint8_t buffer[32];
read_callback(&buffer, sizeof(buffer));
cmd = USBDBG_NONE;
break;
}
case USBDBG_TX_INPUT: {
// TODO implement
#if 0
uint32_t key = *((uint32_t *) buffer);
#endif
cmd = USBDBG_NONE;
break;
}
default: /* error */
break;
}
}
void usbdbg_control(void *buffer, uint8_t request, uint32_t length) {
void usbdbg_control(void *buffer, uint8_t request, uint32_t size) {
cmd = (enum usbdbg_cmd) request;
switch (cmd) {
case USBDBG_FW_VERSION:
xfer_bytes = 0;
xfer_length = length;
xfer_offs = 0;
xfer_size = size;
break;
case USBDBG_FRAME_SIZE:
xfer_bytes = 0;
xfer_length = length;
xfer_offs = 0;
xfer_size = size;
break;
case USBDBG_FRAME_DUMP:
xfer_bytes = 0;
xfer_length = length;
xfer_offs = 0;
xfer_size = size;
break;
case USBDBG_ARCH_STR:
xfer_bytes = 0;
xfer_length = length;
xfer_offs = 0;
xfer_size = size;
break;
case USBDBG_SCRIPT_EXEC:
xfer_bytes = 0;
xfer_length = length;
xfer_offs = 0;
xfer_size = size;
vstr_reset(&script_buf);
break;
@ -415,20 +414,20 @@ void usbdbg_control(void *buffer, uint8_t request, uint32_t length) {
break;
case USBDBG_SCRIPT_RUNNING:
xfer_bytes = 0;
xfer_length = length;
xfer_offs = 0;
xfer_size = size;
break;
case USBDBG_TEMPLATE_SAVE:
case USBDBG_DESCRIPTOR_SAVE:
/* save template */
xfer_bytes = 0;
xfer_length = length;
xfer_offs = 0;
xfer_size = size;
break;
case USBDBG_ATTR_WRITE:
xfer_bytes = 0;
xfer_length = length;
xfer_offs = 0;
xfer_size = size;
break;
case USBDBG_SYS_RESET:
@ -449,35 +448,35 @@ void usbdbg_control(void *buffer, uint8_t request, uint32_t length) {
}
case USBDBG_FB_ENABLE: {
xfer_bytes = 0;
xfer_length = length;
xfer_offs = 0;
xfer_size = size;
break;
}
case USBDBG_TX_BUF:
case USBDBG_TX_BUF_LEN:
xfer_bytes = 0;
xfer_length = length;
xfer_offs = 0;
xfer_size = size;
break;
case USBDBG_SENSOR_ID:
xfer_bytes = 0;
xfer_length = length;
xfer_offs = 0;
xfer_size = size;
break;
case USBDBG_SET_TIME:
xfer_bytes = 0;
xfer_length = length;
xfer_offs = 0;
xfer_size = size;
break;
case USBDBG_TX_INPUT:
xfer_bytes = 0;
xfer_length = length;
xfer_offs = 0;
xfer_size = size;
break;
case USBDBG_GET_STATE:
xfer_bytes = 0;
xfer_length = length;
xfer_offs = 0;
xfer_size = size;
break;
default: /* error */

View File

@ -58,6 +58,9 @@ enum usbdbg_state_flags {
USBDBG_STATE_FLAGS_FRAME = (1 << 2),
};
typedef uint32_t (*usbdbg_read_callback_t) (void *buf, uint32_t len);
typedef uint32_t (*usbdbg_write_callback_t) (const void *buf, uint32_t len);
void usbdbg_init();
void usbdbg_wait_for_command(uint32_t timeout);
bool usbdbg_script_ready();
@ -66,8 +69,8 @@ bool usbdbg_is_busy();
bool usbdbg_get_irq_enabled();
void usbdbg_set_irq_enabled(bool enabled);
void usbdbg_set_script_running(bool running);
void usbdbg_data_in(void *buffer, int length);
void usbdbg_data_out(void *buffer, int length);
void usbdbg_data_in(uint32_t size, usbdbg_write_callback_t write_callback);
void usbdbg_data_out(uint32_t size, usbdbg_read_callback_t read_callback);
void usbdbg_control(void *buffer, uint8_t brequest, uint32_t wlength);
#endif /* __USBDBG_H__ */