firmware: protect uart_tx_queue against ISR

cardem:
- dispatch_usb_command_cardem() appends to uart_tx_queue from the main loop
- tx_byte_tpdu() dequeues from the USART IRQ handler @ NVIC prio 0

card_handle_reset() has the same issue, drains queue and
frees uart_tx_msg from main loop while the ISR may own them.

All of this needs protection against the irq.

Needs a fixed llist_add_tail_irqsafe(), which called __enable_irq() instead of
restoring the saved PRIMASK for some unknown reason?!?!?!?

Change-Id: I7d9cdcc56263b27dfd4649dfb1da1d67761ee923
This commit is contained in:
Eric Wild
2026-07-28 17:42:32 +02:00
committed by lynxis lazus
parent b36adff672
commit f7170aff42
3 changed files with 21 additions and 8 deletions
+1 -1
View File
@@ -34,7 +34,7 @@ static inline void llist_add_tail_irqsafe(struct llist_head *_new,
local_irq_save(x); local_irq_save(x);
llist_add_tail(_new, head); llist_add_tail(_new, head);
__enable_irq(); local_irq_restore(x);
} }
static inline struct llist_head *llist_head_dequeue_irqsafe(struct llist_head *head) static inline struct llist_head *llist_head_dequeue_irqsafe(struct llist_head *head)
+18 -6
View File
@@ -219,19 +219,31 @@ struct card_handle {
static void card_handle_reset(struct card_handle *ch) static void card_handle_reset(struct card_handle *ch)
{ {
struct msgb *msg; struct msgb *msg;
unsigned long x;
card_emu_uart_update_wt(ch->uart_chan, 0); card_emu_uart_update_wt(ch->uart_chan, 0);
/* release any buffers we may still own */ /* Release any buffers we may still own.
if (ch->uart_tx_msg) { * uart_tx_msg + uart_tx_queue are shared with the UART IRQ handler,
usb_buf_free(ch->uart_tx_msg); * that preempts us here -> needs atomic detach and free */
ch->uart_tx_msg = NULL; local_irq_save(x);
} msg = ch->uart_tx_msg;
ch->uart_tx_msg = NULL;
local_irq_restore(x);
if (msg)
usb_buf_free(msg);
if (ch->uart_rx_msg) { if (ch->uart_rx_msg) {
usb_buf_free(ch->uart_rx_msg); usb_buf_free(ch->uart_rx_msg);
ch->uart_rx_msg = NULL; ch->uart_rx_msg = NULL;
} }
while ((msg = msgb_dequeue(&ch->uart_tx_queue))) {
while (1) {
local_irq_save(x);
msg = msgb_dequeue(&ch->uart_tx_queue);
local_irq_restore(x);
if (!msg)
break;
usb_buf_free(msg); usb_buf_free(msg);
} }
} }
+2 -1
View File
@@ -737,7 +737,8 @@ static void dispatch_usb_command_cardem(struct msgb *msg, struct cardem_inst *ci
switch (hdr->msg_type) { switch (hdr->msg_type) {
case SIMTRACE_MSGT_DT_CEMU_TX_DATA: case SIMTRACE_MSGT_DT_CEMU_TX_DATA:
queue = card_emu_get_uart_tx_queue(ci->ch); queue = card_emu_get_uart_tx_queue(ci->ch);
llist_add_tail(&msg->list, queue); /* drained from the USART IRQ handler at highest NVIC prio */
llist_add_tail_irqsafe(&msg->list, queue);
card_emu_have_new_uart_tx(ci->ch); card_emu_have_new_uart_tx(ci->ch);
break; break;
case SIMTRACE_MSGT_DT_CEMU_SET_ATR: case SIMTRACE_MSGT_DT_CEMU_SET_ATR: