From 86c8339e101b5f17e44df81c17bbf4497cc14646 Mon Sep 17 00:00:00 2001 From: Rahix <rahix@rahix.de> Date: Sun, 15 Sep 2019 21:01:05 +0200 Subject: [PATCH] feat(serial): Add serial_flush function serial_flush() allows flushing the serial buffer from anywhere in Epicardium. - When run from thread mode it will flush to UART, CDC-ACM and BLE. This is similar to what the serial task would do once it is rescheduled. - When run inside an exception handler, it will only flush to UART because CDC-ACM and BLE cannot be flushed from an ISR. Note that characters flushed this way will never appear on the other outputs, even if the serial task is scheduled at some point afterwards. The main use of this function is to ensure output of messages even in cases of critical failures. Signed-off-by: Rahix <rahix@rahix.de> --- epicardium/modules/modules.h | 1 + epicardium/modules/serial.c | 122 +++++++++++++++++++++++++---------- 2 files changed, 88 insertions(+), 35 deletions(-) diff --git a/epicardium/modules/modules.h b/epicardium/modules/modules.h index fb65a737..d5087864 100644 --- a/epicardium/modules/modules.h +++ b/epicardium/modules/modules.h @@ -29,6 +29,7 @@ void return_to_menu(void); void serial_init(); void vSerialTask(void *pvParameters); void serial_enqueue_char(char chr); +void serial_flush(void); extern TaskHandle_t serial_task_id; // For the eSetBit xTaskNotify task semaphore trigger diff --git a/epicardium/modules/serial.c b/epicardium/modules/serial.c index 3292ab93..396d628d 100644 --- a/epicardium/modules/serial.c +++ b/epicardium/modules/serial.c @@ -121,6 +121,92 @@ void epic_uart_write_str(const char *str, intptr_t length) } } +static void serial_flush_from_isr(void) +{ + uint8_t rx_data[32]; + size_t received_bytes; + + BaseType_t resched = pdFALSE; + BaseType_t woken = pdFALSE; + + uint32_t basepri = __get_BASEPRI(); + taskENTER_CRITICAL_FROM_ISR(); + + do { + received_bytes = xStreamBufferReceiveFromISR( + write_stream_buffer, + (void *)rx_data, + sizeof(rx_data), + &woken + ); + resched |= woken; + + if (received_bytes == 0) { + break; + } + + /* + * The SDK-driver for UART is not reentrant + * which means we need to perform UART writes + * in a critical section. + */ + + UART_Write(ConsoleUart, (uint8_t *)&rx_data, received_bytes); + } while (received_bytes > 0); + + taskEXIT_CRITICAL_FROM_ISR(basepri); + + portYIELD_FROM_ISR(&resched); +} + +static void serial_flush_from_thread(void) +{ + uint8_t rx_data[32]; + size_t received_bytes; + + do { + taskENTER_CRITICAL(); + received_bytes = xStreamBufferReceive( + write_stream_buffer, + (void *)rx_data, + sizeof(rx_data), + 0 + ); + taskEXIT_CRITICAL(); + + if (received_bytes == 0) { + break; + } + + /* + * The SDK-driver for UART is not reentrant + * which means we need to perform UART writes + * in a critical section. + */ + taskENTER_CRITICAL(); + UART_Write(ConsoleUart, (uint8_t *)&rx_data, received_bytes); + taskEXIT_CRITICAL(); + + cdcacm_write((uint8_t *)&rx_data, received_bytes); + ble_uart_write((uint8_t *)&rx_data, received_bytes); + } while (received_bytes > 0); +} + +/* + * Flush all characters which are currently waiting to be printed. + * + * If this function is called from an ISR, it will only flush to hardware UART + * while a call from thread mode will flush to UART, CDC-ACM, and BLE Serial. + */ +void serial_flush(void) +{ + if (xPortIsInsideInterrupt()) { + serial_flush_from_isr(); + } else { + serial_flush_from_thread(); + } +} + /* * API-call to read a character from the queue. */ @@ -217,9 +303,6 @@ void vSerialTask(void *pvParameters) .callback = uart_callback, }; - uint8_t rx_data[20]; - size_t received_bytes; - while (1) { int ret = UART_ReadAsync(ConsoleUart, &read_req); if (ret != E_NO_ERROR && ret != E_BUSY) { @@ -230,38 +313,7 @@ void vSerialTask(void *pvParameters) ret = ulTaskNotifyTake(pdTRUE, portMAX_DELAY); if (ret & SERIAL_WRITE_NOTIFY) { - do { - received_bytes = xStreamBufferReceive( - write_stream_buffer, - (void *)rx_data, - sizeof(rx_data), - 0 - ); - - if (received_bytes == 0) { - break; - } - - /* - * The SDK-driver for UART is not reentrant - * which means we need to perform UART writes - * in a critical section. - */ - taskENTER_CRITICAL(); - UART_Write( - ConsoleUart, - (uint8_t *)&rx_data, - received_bytes - ); - taskEXIT_CRITICAL(); - - cdcacm_write( - (uint8_t *)&rx_data, received_bytes - ); - ble_uart_write( - (uint8_t *)&rx_data, received_bytes - ); - } while (received_bytes > 0); + serial_flush_from_thread(); } if (ret & SERIAL_READ_NOTIFY) { -- GitLab