From f306d07b0370a52fbb8990245df887c90864bb6b Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Sat, 3 Aug 2024 10:40:25 +0300 Subject: [PATCH] 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. --- src/omv/common/tinyusb_debug.c | 73 +++++------ src/omv/common/usbdbg.c | 217 ++++++++++++++++----------------- src/omv/common/usbdbg.h | 7 +- 3 files changed, 151 insertions(+), 146 deletions(-) diff --git a/src/omv/common/tinyusb_debug.c b/src/omv/common/tinyusb_debug.c index 63d2813b6..ac07a6091 100644 --- a/src/omv/common/tinyusb_debug.c +++ b/src/omv/common/tinyusb_debug.c @@ -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); + } } } } diff --git a/src/omv/common/usbdbg.c b/src/omv/common/usbdbg.c index 8e15d741e..a0bc8285d 100644 --- a/src/omv/common/usbdbg.c +++ b/src/omv/common/usbdbg.c @@ -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 */ diff --git a/src/omv/common/usbdbg.h b/src/omv/common/usbdbg.h index ac0c5c789..9f702ae5a 100644 --- a/src/omv/common/usbdbg.h +++ b/src/omv/common/usbdbg.h @@ -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__ */