Verified Commit 86c8339e authored by Rahix's avatar Rahix
Browse files

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's avatarRahix <rahix@rahix.de>
parent 3a0b7452
Pipeline #3948 passed with stages
in 59 seconds
......@@ -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
......
......@@ -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) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment