Skip to content

Commit 5873390

Browse files
hoihudpgeorge
authored andcommitted
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.
1 parent 5682595 commit 5873390

File tree

2 files changed

+46
-35
lines changed

2 files changed

+46
-35
lines changed

ports/rp2/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ set(MICROPY_SOURCE_LIB
7272
${MICROPY_DIR}/shared/readline/readline.c
7373
${MICROPY_DIR}/shared/runtime/gchelper_m0.s
7474
${MICROPY_DIR}/shared/runtime/gchelper_native.c
75+
${MICROPY_DIR}/shared/runtime/interrupt_char.c
7576
${MICROPY_DIR}/shared/runtime/mpirq.c
7677
${MICROPY_DIR}/shared/runtime/pyexec.c
7778
${MICROPY_DIR}/shared/runtime/stdout_helpers.c

ports/rp2/mphalport.c

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -28,50 +28,69 @@
2828
#include "py/stream.h"
2929
#include "py/mphal.h"
3030
#include "extmod/misc.h"
31+
#include "shared/runtime/interrupt_char.h"
3132
#include "shared/timeutils/timeutils.h"
3233
#include "tusb.h"
3334
#include "uart.h"
3435
#include "hardware/rtc.h"
3536

36-
#if MICROPY_HW_ENABLE_UART_REPL
37+
#if MICROPY_HW_ENABLE_UART_REPL || MICROPY_HW_ENABLE_USBDEV
3738

38-
#ifndef UART_BUFFER_LEN
39-
// reasonably big so we can paste
40-
#define UART_BUFFER_LEN 256
39+
#ifndef MICROPY_HW_STDIN_BUFFER_LEN
40+
#define MICROPY_HW_STDIN_BUFFER_LEN 512
4141
#endif
4242

43-
STATIC uint8_t stdin_ringbuf_array[UART_BUFFER_LEN];
43+
STATIC uint8_t stdin_ringbuf_array[MICROPY_HW_STDIN_BUFFER_LEN];
4444
ringbuf_t stdin_ringbuf = { stdin_ringbuf_array, sizeof(stdin_ringbuf_array) };
4545

4646
#endif
4747

48-
#if MICROPY_KBD_EXCEPTION
48+
#if MICROPY_HW_ENABLE_USBDEV
4949

50-
int mp_interrupt_char = -1;
50+
uint8_t cdc_itf_pending; // keep track of cdc interfaces which need attention to poll
5151

52-
void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) {
53-
(void)itf;
54-
(void)wanted_char;
55-
tud_cdc_read_char(); // discard interrupt char
56-
mp_sched_keyboard_interrupt();
52+
void poll_cdc_interfaces(void) {
53+
// any CDC interfaces left to poll?
54+
if (cdc_itf_pending && ringbuf_free(&stdin_ringbuf)) {
55+
for (uint8_t itf = 0; itf < 8; ++itf) {
56+
if (cdc_itf_pending & (1 << itf)) {
57+
tud_cdc_rx_cb(itf);
58+
if (!cdc_itf_pending) {
59+
break;
60+
}
61+
}
62+
}
63+
}
5764
}
5865

59-
void mp_hal_set_interrupt_char(int c) {
60-
mp_interrupt_char = c;
61-
tud_cdc_set_wanted_char(c);
66+
void tud_cdc_rx_cb(uint8_t itf) {
67+
// consume pending USB data immediately to free usb buffer and keep the endpoint from stalling.
68+
// in case the ringbuffer is full, mark the CDC interface that need attention later on for polling
69+
cdc_itf_pending &= ~(1 << itf);
70+
for (uint32_t bytes_avail = tud_cdc_n_available(itf); bytes_avail > 0; --bytes_avail) {
71+
if (ringbuf_free(&stdin_ringbuf)) {
72+
int data_char = tud_cdc_read_char();
73+
if (data_char == mp_interrupt_char) {
74+
mp_sched_keyboard_interrupt();
75+
} else {
76+
ringbuf_put(&stdin_ringbuf, data_char);
77+
}
78+
} else {
79+
cdc_itf_pending |= (1 << itf);
80+
return;
81+
}
82+
}
6283
}
6384

6485
#endif
6586

6687
uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) {
6788
uintptr_t ret = 0;
68-
#if MICROPY_HW_ENABLE_UART_REPL
69-
if ((poll_flags & MP_STREAM_POLL_RD) && ringbuf_peek(&stdin_ringbuf) != -1) {
70-
ret |= MP_STREAM_POLL_RD;
71-
}
72-
#endif
7389
#if MICROPY_HW_ENABLE_USBDEV
74-
if (tud_cdc_connected() && tud_cdc_available()) {
90+
poll_cdc_interfaces();
91+
#endif
92+
#if MICROPY_HW_ENABLE_UART_REPL || MICROPY_HW_ENABLE_USBDEV
93+
if ((poll_flags & MP_STREAM_POLL_RD) && ringbuf_peek(&stdin_ringbuf) != -1) {
7594
ret |= MP_STREAM_POLL_RD;
7695
}
7796
#endif
@@ -84,21 +103,14 @@ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) {
84103
// Receive single character
85104
int mp_hal_stdin_rx_chr(void) {
86105
for (;;) {
87-
#if MICROPY_HW_ENABLE_UART_REPL
106+
#if MICROPY_HW_ENABLE_USBDEV
107+
poll_cdc_interfaces();
108+
#endif
109+
88110
int c = ringbuf_get(&stdin_ringbuf);
89111
if (c != -1) {
90112
return c;
91113
}
92-
#endif
93-
#if MICROPY_HW_ENABLE_USBDEV
94-
if (tud_cdc_connected() && tud_cdc_available()) {
95-
uint8_t buf[1];
96-
uint32_t count = tud_cdc_read(buf, sizeof(buf));
97-
if (count) {
98-
return buf[0];
99-
}
100-
}
101-
#endif
102114
#if MICROPY_PY_OS_DUPTERM
103115
int dupterm_c = mp_uos_dupterm_rx_chr();
104116
if (dupterm_c >= 0) {
@@ -123,11 +135,9 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
123135
n = CFG_TUD_CDC_EP_BUFSIZE;
124136
}
125137
while (n > tud_cdc_write_available()) {
126-
tud_task();
127-
tud_cdc_write_flush();
138+
MICROPY_EVENT_POLL_HOOK
128139
}
129140
uint32_t n2 = tud_cdc_write(str + i, n);
130-
tud_task();
131141
tud_cdc_write_flush();
132142
i += n2;
133143
}

0 commit comments

Comments
 (0)