diff options
author | Harald Welte <laforge@gnumonks.org> | 2010-02-28 17:37:47 +0100 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2010-03-01 23:48:45 +0100 |
commit | f3685191f642bc772e9b7f17cfe66fb62d0a44ae (patch) | |
tree | 4c07faa6c1463dcb858d05b51f55ea22ed32baec /src/target/firmware/comm | |
parent | 80409074d92fa351ae88880a371cf54e16c10b5f (diff) |
SERCOMM: make sure to disable IRQ/FIQ to avoid race conditions
Diffstat (limited to 'src/target/firmware/comm')
-rw-r--r-- | src/target/firmware/comm/sercomm.c | 21 | ||||
-rw-r--r-- | src/target/firmware/comm/sercomm_cons.c | 11 |
2 files changed, 31 insertions, 1 deletions
diff --git a/src/target/firmware/comm/sercomm.c b/src/target/firmware/comm/sercomm.c index 7270c0ec..1cd0f3b1 100644 --- a/src/target/firmware/comm/sercomm.c +++ b/src/target/firmware/comm/sercomm.c @@ -30,10 +30,14 @@ #endif #include <osmocore/msgb.h> #include <sercomm.h> +#define local_irq_save(x) +#define local_fiq_disable() +#define local_irq_restore(x) #else #include <debug.h> #include <linuxlist.h> +#include <asm/system.h> #include <comm/msgb.h> #include <comm/sercomm.h> @@ -89,13 +93,20 @@ int sercomm_initialized(void) /* user interface for transmitting messages for a given DLCI */ void sercomm_sendmsg(uint8_t dlci, struct msgb *msg) { + unsigned long flags; uint8_t *hdr; /* prepend address + control octet */ hdr = msgb_push(msg, 2); hdr[0] = dlci; hdr[1] = HDLC_C_UI; + + /* This functiion can be called from any context: FIQ, IRQ + * and supervisor context. Proper locking is important! */ + local_irq_save(flags); + local_fiq_disable(); msgb_enqueue(&sercomm.tx.dlci_queues[dlci], msg); + local_irq_restore(flags); #ifndef HOST_BUILD /* tell UART that we have something to send */ @@ -119,6 +130,9 @@ unsigned int sercomm_tx_queue_depth(uint8_t dlci) /* fetch one octet of to-be-transmitted serial data */ int sercomm_drv_pull(uint8_t *ch) { + /* we are always called from interrupt context in this function, + * which means that any data structures we use need to be for + * our exclusive access */ if (!sercomm.tx.msg) { unsigned int i; /* dequeue a new message from the queues */ @@ -187,10 +201,14 @@ int sercomm_drv_rx_char(uint8_t ch) { uint8_t *ptr; + /* we are always called from interrupt context in this function, + * which means that any data structures we use need to be for + * our exclusive access */ if (!sercomm.rx.msg) sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE); if (msgb_tailroom(sercomm.rx.msg) == 0) { + cons_puts("sercomm_drv_rx_char() overflow!\n"); msgb_free(sercomm.rx.msg); sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE); sercomm.rx.state = RX_ST_WAIT_START; @@ -200,7 +218,7 @@ int sercomm_drv_rx_char(uint8_t ch) switch (sercomm.rx.state) { case RX_ST_WAIT_START: if (ch != HDLC_FLAG) - return 0; + break; sercomm.rx.state = RX_ST_ADDR; break; case RX_ST_ADDR: @@ -240,5 +258,6 @@ int sercomm_drv_rx_char(uint8_t ch) sercomm.rx.state = RX_ST_DATA; break; } + return 1; } diff --git a/src/target/firmware/comm/sercomm_cons.c b/src/target/firmware/comm/sercomm_cons.c index 5b481bcb..6d31a9f7 100644 --- a/src/target/firmware/comm/sercomm_cons.c +++ b/src/target/firmware/comm/sercomm_cons.c @@ -24,6 +24,8 @@ #include <errno.h> #include <string.h> +#include <asm/system.h> + #include <calypso/uart.h> #include <console.h> @@ -50,6 +52,7 @@ static void raw_puts(const char *s) int sercomm_puts(const char *s) { + unsigned long flags; const int len = strlen(s) + 1; unsigned int bytes_left = len; @@ -59,6 +62,12 @@ int sercomm_puts(const char *s) return len - 1; } + /* This function is called from any context: Supervisor, IRQ, FIQ, ... + * as such, we need to ensure re-entrant calls are either supported or + * avoided. */ + local_irq_save(flags); + local_fiq_disable(); + while (bytes_left > 0) { unsigned int write_num, space_left, flush; uint8_t *data; @@ -110,6 +119,8 @@ int sercomm_puts(const char *s) } } + local_irq_restore(flags); + return len - 1; } |