diff --git a/firmware/src_simtrace/host_communication.c b/firmware/src_simtrace/host_communication.c index c06c1409..dde54033 100644 --- a/firmware/src_simtrace/host_communication.c +++ b/firmware/src_simtrace/host_communication.c @@ -5,6 +5,7 @@ #include "board.h" #include "req_ctx.h" #include "linuxlist.h" +#include "llist_irqsafe.h" static volatile uint32_t usbep_in_progress[BOARD_USB_NUMENDPOINTS]; @@ -90,7 +91,7 @@ static void usb_read_cb(uint8_t *arg, uint8_t status, uint32_t transferred, } rctx->tot_len = transferred; req_ctx_set_state(rctx, RCTX_S_MAIN_PROCESSING); - llist_add_tail(&rctx->list, queue); + llist_add_tail_irqsafe(&rctx->list, queue); } int usb_refill_from_host(struct llist_head *queue, int ep) diff --git a/firmware/src_simtrace/llist_irqsafe.h b/firmware/src_simtrace/llist_irqsafe.h new file mode 100644 index 00000000..b466939b --- /dev/null +++ b/firmware/src_simtrace/llist_irqsafe.h @@ -0,0 +1,27 @@ +#pragma once + +#include "linuxlist.h" + +static inline void llist_add_tail_irqsafe(struct llist_head *_new, + struct llist_head *head) +{ + __disable_irq(); + llist_add_tail(_new, head); + __enable_irq(); +} + +static inline struct llist_head *llist_head_dequeue_irqsafe(struct llist_head *head) +{ + struct llist_head *lh; + + __disable_irq(); + if (llist_empty(head)) { + lh = NULL; + } else { + lh = head->next; + llist_del(lh); + } + __enable_irq(); + + return lh; +} diff --git a/firmware/src_simtrace/mode_cardemu.c b/firmware/src_simtrace/mode_cardemu.c index e51e9905..a26abe79 100644 --- a/firmware/src_simtrace/mode_cardemu.c +++ b/firmware/src_simtrace/mode_cardemu.c @@ -7,6 +7,7 @@ #include "iso7816_fidi.h" #include "utils.h" #include "linuxlist.h" +#include "llist_irqsafe.h" #include "req_ctx.h" #include "cardemu_prot.h" @@ -326,12 +327,22 @@ static void dispatch_usb_command(struct req_ctx *rctx, struct cardem_inst *ci) /* iterate over the queue of incoming USB commands and dispatch/execute * them */ -static void process_any_usb_commands(struct llist_head *main_q, struct cardem_inst *ci) +static void process_any_usb_commands(struct llist_head *main_q, + struct cardem_inst *ci) { - struct req_ctx *rctx, *tmp; + struct llist_head *lh; + struct req_ctx *rctx; + int i; - llist_for_each_entry_safe(rctx, tmp, main_q, list) { - llist_del(&rctx->list); + /* limit the number of iterations to 10, to ensure we don't get + * stuck here without returning to main loop processing */ + for (i = 0; i < 10; i++) { + /* de-queue the list head in an irq-safe way */ + lh = llist_head_dequeue_irqsafe(main_q); + if (!lh) + break; + rctx = llist_entry(lh, struct req_ctx, list); + /* dispatch the command with interrupts enabled */ dispatch_usb_command(rctx, ci); } }