drivers: serial: esp32_usb: don't use k_usleep in poll_out function

The previous implementation called k_usleep to wait if the fifo was not
empty. This leads to an exception if called from an ISR (e.g. for
for logging).

In addition to that, the k_usleep leads to noticeable interruptions
when printing strings longer than the 64 bytes of the fifo.

With this commit, the function will busy-wait until all characters are
sent or if the timeout is reached. The timeout will only be reached if
no USB host is connected to the port. After the timeout is reached
once, the function will return immediately for subsequent calls
(dropping the characters to be sent) until the USB host is connected
again.

Fixes #60825

Signed-off-by: Martin Jäger <martin@libre.solar>
This commit is contained in:
Martin Jäger 2023-08-26 09:37:43 +02:00 committed by Carles Cufí
commit 0fbbe02f07

View file

@ -28,9 +28,16 @@
#define ISR_HANDLER intr_handler_t
#endif
#define USBSERIAL_TIMEOUT_MAX_US 50000
static int s_usbserial_timeout;
/*
* Timeout after which the poll_out function stops waiting for space in the tx fifo.
*
* Without this timeout, the function would get stuck forever and block the processor if no host is
* connected to the USB port.
*
* USB full-speed uses a frame rate of 1 ms. Thus, a timeout of 50 ms provides plenty of safety
* margin even for a loaded bus. This is the same value as used in the ESP-IDF.
*/
#define USBSERIAL_POLL_OUT_TIMEOUT_MS (50U)
struct serial_esp32_usb_config {
const struct device *clock_dev;
@ -44,6 +51,7 @@ struct serial_esp32_usb_data {
void *irq_cb_data;
#endif
int irq_line;
int64_t last_tx_time;
};
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
@ -65,20 +73,20 @@ static int serial_esp32_usb_poll_in(const struct device *dev, unsigned char *p_c
static void serial_esp32_usb_poll_out(const struct device *dev, unsigned char c)
{
ARG_UNUSED(dev);
struct serial_esp32_usb_data *data = dev->data;
/* Wait for space in FIFO */
while (!usb_serial_jtag_ll_txfifo_writable() &&
s_usbserial_timeout < (USBSERIAL_TIMEOUT_MAX_US / 100)) {
k_usleep(100);
s_usbserial_timeout++;
}
if (usb_serial_jtag_ll_txfifo_writable()) {
usb_serial_jtag_ll_write_txfifo(&c, 1);
usb_serial_jtag_ll_txfifo_flush();
s_usbserial_timeout = 0;
}
/*
* If there is no USB host connected, this function will busy-wait once for the timeout
* period, but return immediately for subsequent calls.
*/
do {
if (usb_serial_jtag_ll_txfifo_writable()) {
usb_serial_jtag_ll_write_txfifo(&c, 1);
usb_serial_jtag_ll_txfifo_flush();
data->last_tx_time = k_uptime_get();
return;
}
} while ((k_uptime_get() - data->last_tx_time) < USBSERIAL_POLL_OUT_TIMEOUT_MS);
}
static int serial_esp32_usb_err_check(const struct device *dev)