From cc95f4b06db2b396587e687b89075418b86041a5 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 30 Apr 2017 21:39:33 +0200 Subject: 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 --- src/sercomm.c | 163 +++++++++++++++++++++++++--------------------------------- 1 file changed, 70 insertions(+), 93 deletions(-) (limited to 'src/sercomm.c') 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 +/* (C) 2010,2017 by Harald Welte * * 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; } -- cgit v1.2.3