diff options
| author | Harald Welte <laforge@gnumonks.org> | 2017-04-30 21:39:33 +0200 | 
|---|---|---|
| committer | Harald Welte <laforge@gnumonks.org> | 2017-05-17 10:46:39 +0000 | 
| commit | cc95f4b06db2b396587e687b89075418b86041a5 (patch) | |
| tree | a8d9ddb8dc7bfcb86691c0522c0e85ce74ba93b6 | |
| parent | c68af6a64bc762b570b84eb90401da0acc914ed4 (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.h | 47 | ||||
| -rw-r--r-- | src/sercomm.c | 163 | 
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;  	} | 
