/* * This file is part of the OpenMV project. * * Copyright (c) 2022 Ibrahim Abdelkader * Copyright (c) 2022 Kwabena W. Agyeman * * This work is licensed under the MIT license, see the file LICENSE for details. * * Tinyusb CDC debugger helper code. */ #include "omv_boardconfig.h" #if (OMV_TUSBDBG_ENABLE == 1) #include "py/runtime.h" #include "py/stream.h" #include "py/mphal.h" #include "py/ringbuf.h" #include "pendsv.h" #include "tusb.h" #include "usbdbg.h" #include "tinyusb_debug.h" #define DEBUG_MAX_PACKET (OMV_TUSBDBG_PACKET) #define DEBUG_BAUDRATE_SLOW (921600) #define DEBUG_BAUDRATE_FAST (12000000) extern void __fatal_error(); typedef struct __attribute__((packed)) { uint8_t cmd; uint8_t request; uint32_t xfer_length; } usbdbg_cmd_t; static uint8_t debug_ringbuf_array[512]; 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); } 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) { return len; } // Try to read as much data as possible. uint32_t bytes = 0; for (; bytes < len; bytes++) { int c = ringbuf_get(&debug_ringbuf); if (c == -1) { break; } buf[bytes] = c; } return bytes; } void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const *coding) { debug_ringbuf.iget = 0; debug_ringbuf.iput = 0; if (0) { #if defined(MICROPY_BOARD_ENTER_BOOTLOADER) } else if (coding->bit_rate == 1200) { MICROPY_BOARD_ENTER_BOOTLOADER(0, 0); #endif } else if (coding->bit_rate == DEBUG_BAUDRATE_SLOW || coding->bit_rate == DEBUG_BAUDRATE_FAST) { tinyusb_debug_mode = true; } else { tinyusb_debug_mode = false; } } bool tinyusb_debug_enabled(void) { return tinyusb_debug_mode; } extern void __real_mp_usbd_task(void); void __wrap_mp_usbd_task(void) { if (!tinyusb_debug_enabled()) { __real_mp_usbd_task(); } } extern void __real_tud_cdc_rx_cb(uint8_t itf); void __wrap_tud_cdc_rx_cb(uint8_t itf) { if (!tinyusb_debug_enabled()) { __real_tud_cdc_rx_cb(itf); } } extern uintptr_t __real_mp_hal_stdio_poll(uintptr_t poll_flags); uintptr_t __wrap_mp_hal_stdio_poll(uintptr_t poll_flags) { if (!tinyusb_debug_enabled()) { return __real_mp_hal_stdio_poll(poll_flags); } return 0; } extern mp_uint_t __real_mp_hal_stdout_tx_strn(const char *str, mp_uint_t len); mp_uint_t __wrap_mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { if (tinyusb_debug_enabled()) { if (tud_cdc_connected()) { NVIC_DisableIRQ(PendSV_IRQn); for (int i = 0; i < len; i++) { // The ring buffer overflows occasionally, espcially when using a slow poll // 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; } } NVIC_EnableIRQ(PendSV_IRQn); } return len; } else { return __real_mp_hal_stdout_tx_strn(str, len); } } static void tinyusb_debug_task(void) { tud_task_ext(0, false); if (!tinyusb_debug_enabled() || !tud_cdc_connected() || tud_cdc_available() < 6) { return; } uint8_t dbg_buf[DEBUG_MAX_PACKET]; uint32_t count = tud_cdc_read(dbg_buf, 6); 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; } usbdbg_cmd_t *cmd = (usbdbg_cmd_t *) dbg_buf; uint8_t request = cmd->request; uint32_t xfer_length = cmd->xfer_length; usbdbg_control(NULL, request, xfer_length); while (xfer_length) { // && tud_cdc_connected()) if (tud_task_event_ready()) { tud_task_ext(0, false); } 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); } 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); } } } // For the mimxrt, and nrf ports this replaces the weak USB IRQ handlers. // For the RP2 port, this handler is installed in main.c void OMV_USB1_IRQ_HANDLER(void) { dcd_int_handler(0); // If there are any event to process, schedule a call to cdc loop. if (tud_task_event_ready()) { pendsv_schedule_dispatch(PENDSV_DISPATCH_CDC, tinyusb_debug_task); } } #if defined(OMV_USB2_IRQ_HANDLER) void OMV_USB2_IRQ_HANDLER(void) { dcd_int_handler(1); // If there are any event to process, schedule a call to cdc loop. if (tud_task_event_ready()) { pendsv_schedule_dispatch(PENDSV_DISPATCH_CDC, tinyusb_debug_task); } } #endif #endif //OMV_TINYUSB_DEBUG