diff options
| author | Martin Fischer <fischer.carlito@gmail.com> | 2022-03-30 23:00:46 +0200 |
|---|---|---|
| committer | Damien George <damien@micropython.org> | 2022-04-05 10:47:19 +1000 |
| commit | 587339022689187a1acbccc1d0e2425a67385ff7 (patch) | |
| tree | 8b527298e7e9ec8fe3cd3f5cd1e958121dfa0197 | |
| parent | 5682595d75c1dc1d48f4a474155a301bcb9e28b3 (diff) | |
rp2/mphalport: Fix USB CDC RX handling to not block when unprocessed.
Prior to this commit, the USB CDC OUT endpoint got NACK'd if a character
was received but not consumed by the application, e.g. via
sys.stdin.read(). This meant that USB CDC was blocked and no additional
characters could be sent from the host. In particular a ctrl-C could not
interrupt the application if another character was pending.
To fix the issue, the approach in this commit uses a callback tud_cdc_rx_cb
which is called by the TinyUSB stack on reception of new CDC data. By
consuming the data immediately, the endpoint does not stall anymore. The
previous handler tud_cdc_rx_wanted_cb was made obsolete and removed.
In addition some cleanup was done along the way: by adding interrupt_char.c
and removing the existing code mp_hal_set_interrupt_char(). Also, there is
now only one (stdin) ringbuffer.
Fixes issue #7996.
| -rw-r--r-- | ports/rp2/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | ports/rp2/mphalport.c | 80 |
2 files changed, 46 insertions, 35 deletions
diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index d58fb5687..a5e421734 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -72,6 +72,7 @@ set(MICROPY_SOURCE_LIB ${MICROPY_DIR}/shared/readline/readline.c ${MICROPY_DIR}/shared/runtime/gchelper_m0.s ${MICROPY_DIR}/shared/runtime/gchelper_native.c + ${MICROPY_DIR}/shared/runtime/interrupt_char.c ${MICROPY_DIR}/shared/runtime/mpirq.c ${MICROPY_DIR}/shared/runtime/pyexec.c ${MICROPY_DIR}/shared/runtime/stdout_helpers.c diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c index c4169fb66..4a5221cae 100644 --- a/ports/rp2/mphalport.c +++ b/ports/rp2/mphalport.c @@ -28,50 +28,69 @@ #include "py/stream.h" #include "py/mphal.h" #include "extmod/misc.h" +#include "shared/runtime/interrupt_char.h" #include "shared/timeutils/timeutils.h" #include "tusb.h" #include "uart.h" #include "hardware/rtc.h" -#if MICROPY_HW_ENABLE_UART_REPL +#if MICROPY_HW_ENABLE_UART_REPL || MICROPY_HW_ENABLE_USBDEV -#ifndef UART_BUFFER_LEN -// reasonably big so we can paste -#define UART_BUFFER_LEN 256 +#ifndef MICROPY_HW_STDIN_BUFFER_LEN +#define MICROPY_HW_STDIN_BUFFER_LEN 512 #endif -STATIC uint8_t stdin_ringbuf_array[UART_BUFFER_LEN]; +STATIC uint8_t stdin_ringbuf_array[MICROPY_HW_STDIN_BUFFER_LEN]; ringbuf_t stdin_ringbuf = { stdin_ringbuf_array, sizeof(stdin_ringbuf_array) }; #endif -#if MICROPY_KBD_EXCEPTION +#if MICROPY_HW_ENABLE_USBDEV -int mp_interrupt_char = -1; +uint8_t cdc_itf_pending; // keep track of cdc interfaces which need attention to poll -void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) { - (void)itf; - (void)wanted_char; - tud_cdc_read_char(); // discard interrupt char - mp_sched_keyboard_interrupt(); +void poll_cdc_interfaces(void) { + // any CDC interfaces left to poll? + if (cdc_itf_pending && ringbuf_free(&stdin_ringbuf)) { + for (uint8_t itf = 0; itf < 8; ++itf) { + if (cdc_itf_pending & (1 << itf)) { + tud_cdc_rx_cb(itf); + if (!cdc_itf_pending) { + break; + } + } + } + } } -void mp_hal_set_interrupt_char(int c) { - mp_interrupt_char = c; - tud_cdc_set_wanted_char(c); +void tud_cdc_rx_cb(uint8_t itf) { + // consume pending USB data immediately to free usb buffer and keep the endpoint from stalling. + // in case the ringbuffer is full, mark the CDC interface that need attention later on for polling + cdc_itf_pending &= ~(1 << itf); + for (uint32_t bytes_avail = tud_cdc_n_available(itf); bytes_avail > 0; --bytes_avail) { + if (ringbuf_free(&stdin_ringbuf)) { + int data_char = tud_cdc_read_char(); + if (data_char == mp_interrupt_char) { + mp_sched_keyboard_interrupt(); + } else { + ringbuf_put(&stdin_ringbuf, data_char); + } + } else { + cdc_itf_pending |= (1 << itf); + return; + } + } } #endif uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { uintptr_t ret = 0; - #if MICROPY_HW_ENABLE_UART_REPL - if ((poll_flags & MP_STREAM_POLL_RD) && ringbuf_peek(&stdin_ringbuf) != -1) { - ret |= MP_STREAM_POLL_RD; - } - #endif #if MICROPY_HW_ENABLE_USBDEV - if (tud_cdc_connected() && tud_cdc_available()) { + poll_cdc_interfaces(); + #endif + #if MICROPY_HW_ENABLE_UART_REPL || MICROPY_HW_ENABLE_USBDEV + if ((poll_flags & MP_STREAM_POLL_RD) && ringbuf_peek(&stdin_ringbuf) != -1) { ret |= MP_STREAM_POLL_RD; } #endif @@ -84,21 +103,14 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { // Receive single character int mp_hal_stdin_rx_chr(void) { for (;;) { - #if MICROPY_HW_ENABLE_UART_REPL + #if MICROPY_HW_ENABLE_USBDEV + poll_cdc_interfaces(); + #endif + int c = ringbuf_get(&stdin_ringbuf); if (c != -1) { return c; } - #endif - #if MICROPY_HW_ENABLE_USBDEV - if (tud_cdc_connected() && tud_cdc_available()) { - uint8_t buf[1]; - uint32_t count = tud_cdc_read(buf, sizeof(buf)); - if (count) { - return buf[0]; - } - } - #endif #if MICROPY_PY_OS_DUPTERM int dupterm_c = mp_uos_dupterm_rx_chr(); if (dupterm_c >= 0) { @@ -123,11 +135,9 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { n = CFG_TUD_CDC_EP_BUFSIZE; } while (n > tud_cdc_write_available()) { - tud_task(); - tud_cdc_write_flush(); + MICROPY_EVENT_POLL_HOOK } uint32_t n2 = tud_cdc_write(str + i, n); - tud_task(); tud_cdc_write_flush(); i += n2; } |
