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.
This commit is contained in:
parent
5682595d75
commit
5873390226
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user