1
mirror of https://github.com/flipperdevices/flipperzero-firmware.git synced 2025-12-12 04:41:26 +04:00

FuriHalSerial: Fix RXFNE interrupt hang (#4246)

* Expansion: Wake thread on UART error flag

* Expansion: Stop UART async rx early

* FuriHalSerial: Fix RXFNE interrupt hang

---------

Co-authored-by: hedger <hedger@users.noreply.github.com>
This commit is contained in:
WillyJL
2025-09-24 12:24:09 +02:00
committed by GitHub
parent dfd753703a
commit 7f0e8f39d3
2 changed files with 43 additions and 29 deletions

View File

@@ -35,7 +35,8 @@ typedef enum {
ExpansionWorkerFlagError = 1 << 2, ExpansionWorkerFlagError = 1 << 2,
} ExpansionWorkerFlag; } ExpansionWorkerFlag;
#define EXPANSION_ALL_FLAGS (ExpansionWorkerFlagData | ExpansionWorkerFlagStop) #define EXPANSION_ALL_FLAGS \
(ExpansionWorkerFlagData | ExpansionWorkerFlagStop | ExpansionWorkerFlagError)
struct ExpansionWorker { struct ExpansionWorker {
FuriThread* thread; FuriThread* thread;
@@ -360,6 +361,8 @@ static int32_t expansion_worker(void* context) {
expansion_worker_state_machine(instance); expansion_worker_state_machine(instance);
} }
furi_hal_serial_async_rx_stop(instance->serial_handle);
if(instance->state == ExpansionWorkerStateRpcActive) { if(instance->state == ExpansionWorkerStateRpcActive) {
expansion_worker_rpc_session_close(instance); expansion_worker_rpc_session_close(instance);
} }

View File

@@ -817,6 +817,21 @@ static void furi_hal_serial_async_rx_configure(
FuriHalSerialHandle* handle, FuriHalSerialHandle* handle,
FuriHalSerialAsyncRxCallback callback, FuriHalSerialAsyncRxCallback callback,
void* context) { void* context) {
// Disable RXFNE interrupts before unsetting the user callback that reads data
// Otherwise interrupt runs without reading data and without clearing RXFNE flag
// This would cause a system hang as the same interrupt runs in loop forever
if(!callback) {
if(handle->id == FuriHalSerialIdUsart) {
LL_USART_DisableIT_RXNE_RXFNE(USART1);
furi_hal_interrupt_set_isr(FuriHalInterruptIdUart1, NULL, NULL);
furi_hal_serial_usart_deinit_dma_rx();
} else if(handle->id == FuriHalSerialIdLpuart) {
LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1);
furi_hal_interrupt_set_isr(FuriHalInterruptIdLpUart1, NULL, NULL);
furi_hal_serial_lpuart_deinit_dma_rx();
}
}
// Handle must be configured before enabling RX interrupt // Handle must be configured before enabling RX interrupt
// as it might be triggered right away on a misconfigured handle // as it might be triggered right away on a misconfigured handle
furi_hal_serial[handle->id].rx_byte_callback = callback; furi_hal_serial[handle->id].rx_byte_callback = callback;
@@ -824,27 +839,17 @@ static void furi_hal_serial_async_rx_configure(
furi_hal_serial[handle->id].rx_dma_callback = NULL; furi_hal_serial[handle->id].rx_dma_callback = NULL;
furi_hal_serial[handle->id].context = context; furi_hal_serial[handle->id].context = context;
if(handle->id == FuriHalSerialIdUsart) { if(callback) {
if(callback) { if(handle->id == FuriHalSerialIdUsart) {
furi_hal_serial_usart_deinit_dma_rx(); furi_hal_serial_usart_deinit_dma_rx();
furi_hal_interrupt_set_isr( furi_hal_interrupt_set_isr(
FuriHalInterruptIdUart1, furi_hal_serial_usart_irq_callback, NULL); FuriHalInterruptIdUart1, furi_hal_serial_usart_irq_callback, NULL);
LL_USART_EnableIT_RXNE_RXFNE(USART1); LL_USART_EnableIT_RXNE_RXFNE(USART1);
} else { } else if(handle->id == FuriHalSerialIdLpuart) {
furi_hal_interrupt_set_isr(FuriHalInterruptIdUart1, NULL, NULL);
furi_hal_serial_usart_deinit_dma_rx();
LL_USART_DisableIT_RXNE_RXFNE(USART1);
}
} else if(handle->id == FuriHalSerialIdLpuart) {
if(callback) {
furi_hal_serial_lpuart_deinit_dma_rx(); furi_hal_serial_lpuart_deinit_dma_rx();
furi_hal_interrupt_set_isr( furi_hal_interrupt_set_isr(
FuriHalInterruptIdLpUart1, furi_hal_serial_lpuart_irq_callback, NULL); FuriHalInterruptIdLpUart1, furi_hal_serial_lpuart_irq_callback, NULL);
LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);
} else {
furi_hal_interrupt_set_isr(FuriHalInterruptIdLpUart1, NULL, NULL);
furi_hal_serial_lpuart_deinit_dma_rx();
LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1);
} }
} }
} }
@@ -944,33 +949,39 @@ static void furi_hal_serial_dma_configure(
FuriHalSerialHandle* handle, FuriHalSerialHandle* handle,
FuriHalSerialDmaRxCallback callback, FuriHalSerialDmaRxCallback callback,
void* context) { void* context) {
furi_check(handle); // Disable RXFNE interrupts before unsetting the user callback that reads data
// Otherwise interrupt runs without reading data and without clearing RXFNE flag
if(handle->id == FuriHalSerialIdUsart) { // This would cause a system hang as the same interrupt runs in loop forever
if(callback) { if(!callback) {
furi_hal_serial_usart_init_dma_rx(); if(handle->id == FuriHalSerialIdUsart) {
furi_hal_interrupt_set_isr(
FuriHalInterruptIdUart1, furi_hal_serial_usart_irq_callback, NULL);
} else {
LL_USART_DisableIT_RXNE_RXFNE(USART1); LL_USART_DisableIT_RXNE_RXFNE(USART1);
furi_hal_interrupt_set_isr(FuriHalInterruptIdUart1, NULL, NULL); furi_hal_interrupt_set_isr(FuriHalInterruptIdUart1, NULL, NULL);
furi_hal_serial_usart_deinit_dma_rx(); furi_hal_serial_usart_deinit_dma_rx();
} } else if(handle->id == FuriHalSerialIdLpuart) {
} else if(handle->id == FuriHalSerialIdLpuart) {
if(callback) {
furi_hal_serial_lpuart_init_dma_rx();
furi_hal_interrupt_set_isr(
FuriHalInterruptIdLpUart1, furi_hal_serial_lpuart_irq_callback, NULL);
} else {
LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1); LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1);
furi_hal_interrupt_set_isr(FuriHalInterruptIdLpUart1, NULL, NULL); furi_hal_interrupt_set_isr(FuriHalInterruptIdLpUart1, NULL, NULL);
furi_hal_serial_lpuart_deinit_dma_rx(); furi_hal_serial_lpuart_deinit_dma_rx();
} }
} }
// Handle must be configured before enabling RX interrupt
// as it might be triggered right away on a misconfigured handle
furi_hal_serial[handle->id].rx_byte_callback = NULL; furi_hal_serial[handle->id].rx_byte_callback = NULL;
furi_hal_serial[handle->id].handle = handle; furi_hal_serial[handle->id].handle = handle;
furi_hal_serial[handle->id].rx_dma_callback = callback; furi_hal_serial[handle->id].rx_dma_callback = callback;
furi_hal_serial[handle->id].context = context; furi_hal_serial[handle->id].context = context;
if(callback) {
if(handle->id == FuriHalSerialIdUsart) {
furi_hal_serial_usart_init_dma_rx();
furi_hal_interrupt_set_isr(
FuriHalInterruptIdUart1, furi_hal_serial_usart_irq_callback, NULL);
} else if(handle->id == FuriHalSerialIdLpuart) {
furi_hal_serial_lpuart_init_dma_rx();
furi_hal_interrupt_set_isr(
FuriHalInterruptIdLpUart1, furi_hal_serial_lpuart_irq_callback, NULL);
}
}
} }
void furi_hal_serial_dma_rx_start( void furi_hal_serial_dma_rx_start(