aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2017-04-30 21:39:33 +0200
committerHarald Welte <laforge@gnumonks.org>2017-05-17 10:46:39 +0000
commitcc95f4b06db2b396587e687b89075418b86041a5 (patch)
treea8d9ddb8dc7bfcb86691c0522c0e85ce74ba93b6
parentc68af6a64bc762b570b84eb90401da0acc914ed4 (diff)
sercomm: Enable multiple instances of 'sercomm'
Rather than having one global instance, let's permit multiple instances of sercomm to co-exist, with all API functions extended by the instance as first argument. Change-Id: I0f3b53f464b119d65747bcb0be0af2d631e1cc05
-rw-r--r--include/osmocom/core/sercomm.h47
-rw-r--r--src/sercomm.c163
2 files changed, 106 insertions, 104 deletions
diff --git a/include/osmocom/core/sercomm.h b/include/osmocom/core/sercomm.h
index 21f715ba..b751bcd6 100644
--- a/include/osmocom/core/sercomm.h
+++ b/include/osmocom/core/sercomm.h
@@ -21,37 +21,62 @@ enum sercomm_dlci {
_SC_DLCI_MAX
};
+struct sercomm_inst;
+typedef void (*dlci_cb_t)(struct sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg);
+
+struct sercomm_inst {
+ int initialized;
+ int uart_id;
+
+ /* transmit side */
+ struct {
+ struct llist_head dlci_queues[_SC_DLCI_MAX];
+ struct msgb *msg;
+ int state;
+ uint8_t *next_char;
+ } tx;
+
+ /* receive side */
+ struct {
+ dlci_cb_t dlci_handler[_SC_DLCI_MAX];
+ struct msgb *msg;
+ int state;
+ uint8_t dlci;
+ uint8_t ctrl;
+ } rx;
+};
+
+
#ifndef HOST_BUILD
#include <uart.h>
/* helper functions for target */
-void sercomm_bind_uart(int uart);
-int sercomm_get_uart(void);
-void sercomm_change_speed(enum uart_baudrate bdrt);
+void sercomm_bind_uart(struct sercomm_inst *sercomm, int uart);
+int sercomm_get_uart(struct sercomm_inst *sercomm);
+void sercomm_change_speed(struct sercomm_inst *sercomm, enum uart_baudrate bdrt);
#endif
-void sercomm_init(void);
-int sercomm_initialized(void);
+void sercomm_init(struct sercomm_inst *sercomm);
+int sercomm_initialized(struct sercomm_inst *sercomm);
/* User Interface: Tx */
/* user interface for transmitting messages for a given DLCI */
-void sercomm_sendmsg(uint8_t dlci, struct msgb *msg);
+void sercomm_sendmsg(struct sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg);
/* how deep is the Tx queue for a given DLCI */
-unsigned int sercomm_tx_queue_depth(uint8_t dlci);
+unsigned int sercomm_tx_queue_depth(struct sercomm_inst *sercomm, uint8_t dlci);
/* User Interface: Rx */
/* receiving messages for a given DLCI */
-typedef void (*dlci_cb_t)(uint8_t dlci, struct msgb *msg);
-int sercomm_register_rx_cb(uint8_t dlci, dlci_cb_t cb);
+int sercomm_register_rx_cb(struct sercomm_inst *sercomm, uint8_t dlci, dlci_cb_t cb);
/* Driver Interface */
/* fetch one octet of to-be-transmitted serial data. returns 0 if no more data */
-int sercomm_drv_pull(uint8_t *ch);
+int sercomm_drv_pull(struct sercomm_inst *sercomm, uint8_t *ch);
/* the driver has received one byte, pass it into sercomm layer.
returns 1 in case of success, 0 in case of unrecognized char */
-int sercomm_drv_rx_char(uint8_t ch);
+int sercomm_drv_rx_char(struct sercomm_inst *sercomm, uint8_t ch);
static inline struct msgb *sercomm_alloc_msgb(unsigned int len)
{
diff --git a/src/sercomm.c b/src/sercomm.c
index 810d661c..4fd979a1 100644
--- a/src/sercomm.c
+++ b/src/sercomm.c
@@ -1,6 +1,6 @@
/* Serial communications layer, based on HDLC */
-/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2010,2017 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
@@ -59,7 +59,6 @@ static inline void sercomm_unlock(unsigned long *flags)
#endif
-
enum rx_state {
RX_ST_WAIT_START,
RX_ST_ADDR,
@@ -68,61 +67,39 @@ enum rx_state {
RX_ST_ESCAPE,
};
-static struct {
- int initialized;
- int uart_id;
-
- /* transmit side */
- struct {
- struct llist_head dlci_queues[_SC_DLCI_MAX];
- struct msgb *msg;
- enum rx_state state;
- uint8_t *next_char;
- } tx;
-
- /* receive side */
- struct {
- dlci_cb_t dlci_handler[_SC_DLCI_MAX];
- struct msgb *msg;
- enum rx_state state;
- uint8_t dlci;
- uint8_t ctrl;
- } rx;
-
-} sercomm;
#ifndef HOST_BUILD
-void sercomm_bind_uart(int uart)
+void sercomm_bind_uart(struct sercomm_inst *sercomm, int uart)
{
- sercomm.uart_id = uart;
+ sercomm->uart_id = uart;
}
-int sercomm_get_uart(void)
+int sercomm_get_uart(struct sercomm_inst *sercomm)
{
- return sercomm.uart_id;
+ return sercomm->uart_id;
}
#endif
-void sercomm_init(void)
+void sercomm_init(struct sercomm_inst *sercomm)
{
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++)
- INIT_LLIST_HEAD(&sercomm.tx.dlci_queues[i]);
+ for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++)
+ INIT_LLIST_HEAD(&sercomm->tx.dlci_queues[i]);
- sercomm.rx.msg = NULL;
- sercomm.initialized = 1;
+ sercomm->rx.msg = NULL;
+ sercomm->initialized = 1;
/* set up the echo dlci */
- sercomm_register_rx_cb(SC_DLCI_ECHO, &sercomm_sendmsg);
+ sercomm_register_rx_cb(sercomm, SC_DLCI_ECHO, &sercomm_sendmsg);
}
-int sercomm_initialized(void)
+int sercomm_initialized(struct sercomm_inst *sercomm)
{
- return sercomm.initialized;
+ return sercomm->initialized;
}
/* user interface for transmitting messages for a given DLCI */
-void sercomm_sendmsg(uint8_t dlci, struct msgb *msg)
+void sercomm_sendmsg(struct sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg)
{
unsigned long flags;
uint8_t *hdr;
@@ -135,22 +112,22 @@ void sercomm_sendmsg(uint8_t dlci, struct msgb *msg)
/* This functiion can be called from any context: FIQ, IRQ
* and supervisor context. Proper locking is important! */
sercomm_lock(&flags);
- msgb_enqueue(&sercomm.tx.dlci_queues[dlci], msg);
+ msgb_enqueue(&sercomm->tx.dlci_queues[dlci], msg);
sercomm_unlock(&flags);
#ifndef HOST_BUILD
/* tell UART that we have something to send */
- uart_irq_enable(sercomm.uart_id, UART_IRQ_TX_EMPTY, 1);
+ uart_irq_enable(sercomm->uart_id, UART_IRQ_TX_EMPTY, 1);
#endif
}
/* how deep is the Tx queue for a given DLCI */
-unsigned int sercomm_tx_queue_depth(uint8_t dlci)
+unsigned int sercomm_tx_queue_depth(struct sercomm_inst *sercomm, uint8_t dlci)
{
struct llist_head *le;
unsigned int num = 0;
- llist_for_each(le, &sercomm.tx.dlci_queues[dlci]) {
+ llist_for_each(le, &sercomm->tx.dlci_queues[dlci]) {
num++;
}
@@ -160,7 +137,7 @@ unsigned int sercomm_tx_queue_depth(uint8_t dlci)
#ifndef HOST_BUILD
/* wait until everything has been transmitted, then grab the lock and
* change the baud rate as requested */
-void sercomm_change_speed(enum uart_baudrate bdrt)
+void sercomm_change_speed(struct sercomm_inst *sercomm, enum uart_baudrate bdrt)
{
unsigned int i, count;
unsigned long flags;
@@ -168,7 +145,7 @@ void sercomm_change_speed(enum uart_baudrate bdrt)
while (1) {
/* count the number of pending messages */
count = 0;
- for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++)
+ for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++)
count += sercomm_tx_queue_depth(i);
/* if we still have any in the queue, restart */
if (count == 0)
@@ -179,9 +156,9 @@ void sercomm_change_speed(enum uart_baudrate bdrt)
/* no messages in the queue, grab the lock to ensure it
* stays that way */
sercomm_lock(&flags);
- if (!sercomm.tx.msg && !sercomm.tx.next_char) {
+ if (!sercomm->tx.msg && !sercomm->tx.next_char) {
/* change speed */
- uart_baudrate(sercomm.uart_id, bdrt);
+ uart_baudrate(sercomm->uart_id, bdrt);
sercomm_unlock(&flags);
break;
}
@@ -191,7 +168,7 @@ void sercomm_change_speed(enum uart_baudrate bdrt)
#endif
/* fetch one octet of to-be-transmitted serial data */
-int sercomm_drv_pull(uint8_t *ch)
+int sercomm_drv_pull(struct sercomm_inst *sercomm, uint8_t *ch)
{
unsigned long flags;
@@ -200,18 +177,18 @@ int sercomm_drv_pull(uint8_t *ch)
sercomm_lock(&flags);
- if (!sercomm.tx.msg) {
+ if (!sercomm->tx.msg) {
unsigned int i;
/* dequeue a new message from the queues */
- for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++) {
- sercomm.tx.msg = msgb_dequeue(&sercomm.tx.dlci_queues[i]);
- if (sercomm.tx.msg)
+ for (i = 0; i < ARRAY_SIZE(sercomm->tx.dlci_queues); i++) {
+ sercomm->tx.msg = msgb_dequeue(&sercomm->tx.dlci_queues[i]);
+ if (sercomm->tx.msg)
break;
}
- if (sercomm.tx.msg) {
+ if (sercomm->tx.msg) {
/* start of a new message, send start flag octet */
*ch = HDLC_FLAG;
- sercomm.tx.next_char = sercomm.tx.msg->data;
+ sercomm->tx.next_char = sercomm->tx.msg->data;
sercomm_unlock(&flags);
return 1;
} else {
@@ -221,31 +198,31 @@ int sercomm_drv_pull(uint8_t *ch)
}
}
- if (sercomm.tx.state == RX_ST_ESCAPE) {
+ if (sercomm->tx.state == RX_ST_ESCAPE) {
/* we've already transmitted the ESCAPE octet,
* we now need to transmit the escaped data */
- *ch = *sercomm.tx.next_char++;
- sercomm.tx.state = RX_ST_DATA;
- } else if (sercomm.tx.next_char >= sercomm.tx.msg->tail) {
+ *ch = *sercomm->tx.next_char++;
+ sercomm->tx.state = RX_ST_DATA;
+ } else if (sercomm->tx.next_char >= sercomm->tx.msg->tail) {
/* last character has already been transmitted,
* send end-of-message octet */
*ch = HDLC_FLAG;
/* we've reached the end of the message buffer */
- msgb_free(sercomm.tx.msg);
- sercomm.tx.msg = NULL;
- sercomm.tx.next_char = NULL;
+ msgb_free(sercomm->tx.msg);
+ sercomm->tx.msg = NULL;
+ sercomm->tx.next_char = NULL;
/* escaping for the two control octets */
- } else if (*sercomm.tx.next_char == HDLC_FLAG ||
- *sercomm.tx.next_char == HDLC_ESCAPE ||
- *sercomm.tx.next_char == 0x00) {
+ } else if (*sercomm->tx.next_char == HDLC_FLAG ||
+ *sercomm->tx.next_char == HDLC_ESCAPE ||
+ *sercomm->tx.next_char == 0x00) {
/* send an escape octet */
*ch = HDLC_ESCAPE;
/* invert bit 5 of the next octet to be sent */
- *sercomm.tx.next_char ^= (1 << 5);
- sercomm.tx.state = RX_ST_ESCAPE;
+ *sercomm->tx.next_char ^= (1 << 5);
+ sercomm->tx.state = RX_ST_ESCAPE;
} else {
/* standard case, simply send next octet */
- *ch = *sercomm.tx.next_char++;
+ *ch = *sercomm->tx.next_char++;
}
sercomm_unlock(&flags);
@@ -253,89 +230,89 @@ int sercomm_drv_pull(uint8_t *ch)
}
/* register a handler for a given DLCI */
-int sercomm_register_rx_cb(uint8_t dlci, dlci_cb_t cb)
+int sercomm_register_rx_cb(struct sercomm_inst *sercomm, uint8_t dlci, dlci_cb_t cb)
{
- if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler))
+ if (dlci >= ARRAY_SIZE(sercomm->rx.dlci_handler))
return -EINVAL;
- if (sercomm.rx.dlci_handler[dlci])
+ if (sercomm->rx.dlci_handler[dlci])
return -EBUSY;
- sercomm.rx.dlci_handler[dlci] = cb;
+ sercomm->rx.dlci_handler[dlci] = cb;
return 0;
}
/* dispatch an incoming message once it is completely received */
-static void dispatch_rx_msg(uint8_t dlci, struct msgb *msg)
+static void dispatch_rx_msg(struct sercomm_inst *sercomm, uint8_t dlci, struct msgb *msg)
{
- if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler) ||
- !sercomm.rx.dlci_handler[dlci]) {
+ if (dlci >= ARRAY_SIZE(sercomm->rx.dlci_handler) ||
+ !sercomm->rx.dlci_handler[dlci]) {
msgb_free(msg);
return;
}
- sercomm.rx.dlci_handler[dlci](dlci, msg);
+ sercomm->rx.dlci_handler[dlci](sercomm, dlci, msg);
}
/* the driver has received one byte, pass it into sercomm layer */
-int sercomm_drv_rx_char(uint8_t ch)
+int sercomm_drv_rx_char(struct sercomm_inst *sercomm, 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 (!sercomm->rx.msg)
+ sercomm->rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE);
- if (msgb_tailroom(sercomm.rx.msg) == 0) {
+ 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;
+ msgb_free(sercomm->rx.msg);
+ sercomm->rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE);
+ sercomm->rx.state = RX_ST_WAIT_START;
return 0;
}
- switch (sercomm.rx.state) {
+ switch (sercomm->rx.state) {
case RX_ST_WAIT_START:
if (ch != HDLC_FLAG)
break;
- sercomm.rx.state = RX_ST_ADDR;
+ sercomm->rx.state = RX_ST_ADDR;
break;
case RX_ST_ADDR:
- sercomm.rx.dlci = ch;
- sercomm.rx.state = RX_ST_CTRL;
+ sercomm->rx.dlci = ch;
+ sercomm->rx.state = RX_ST_CTRL;
break;
case RX_ST_CTRL:
- sercomm.rx.ctrl = ch;
- sercomm.rx.state = RX_ST_DATA;
+ sercomm->rx.ctrl = ch;
+ sercomm->rx.state = RX_ST_DATA;
break;
case RX_ST_DATA:
if (ch == HDLC_ESCAPE) {
/* drop the escape octet, but change state */
- sercomm.rx.state = RX_ST_ESCAPE;
+ sercomm->rx.state = RX_ST_ESCAPE;
break;
} else if (ch == HDLC_FLAG) {
/* message is finished */
- dispatch_rx_msg(sercomm.rx.dlci, sercomm.rx.msg);
+ dispatch_rx_msg(sercomm, sercomm->rx.dlci, sercomm->rx.msg);
/* allocate new buffer */
- sercomm.rx.msg = NULL;
+ sercomm->rx.msg = NULL;
/* start all over again */
- sercomm.rx.state = RX_ST_WAIT_START;
+ sercomm->rx.state = RX_ST_WAIT_START;
/* do not add the control char */
break;
}
/* default case: store the octet */
- ptr = msgb_put(sercomm.rx.msg, 1);
+ ptr = msgb_put(sercomm->rx.msg, 1);
*ptr = ch;
break;
case RX_ST_ESCAPE:
/* store bif-5-inverted octet in buffer */
ch ^= (1 << 5);
- ptr = msgb_put(sercomm.rx.msg, 1);
+ ptr = msgb_put(sercomm->rx.msg, 1);
*ptr = ch;
/* transition back to normal DATA state */
- sercomm.rx.state = RX_ST_DATA;
+ sercomm->rx.state = RX_ST_DATA;
break;
}