summaryrefslogtreecommitdiffstats
path: root/src/target
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2010-02-28 17:37:47 +0100
committerHarald Welte <laforge@gnumonks.org>2010-03-01 23:48:45 +0100
commitf3685191f642bc772e9b7f17cfe66fb62d0a44ae (patch)
tree4c07faa6c1463dcb858d05b51f55ea22ed32baec /src/target
parent80409074d92fa351ae88880a371cf54e16c10b5f (diff)
SERCOMM: make sure to disable IRQ/FIQ to avoid race conditions
Diffstat (limited to 'src/target')
-rw-r--r--src/target/firmware/comm/sercomm.c21
-rw-r--r--src/target/firmware/comm/sercomm_cons.c11
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;
}