[PATCH] libosmocore[master]: Import sercomm.c from osmocom-bb

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

Harald Welte gerrit-no-reply at lists.osmocom.org
Mon May 15 15:34:26 UTC 2017


Review at  https://gerrit.osmocom.org/2633

Import sercomm.c from osmocom-bb

This imports the file src/target/firmware/comm/sercomm.c from
osmocom-bb.git without introducing any modifications.  It will not even
be built yet, as Makefile integration is intentionally left until it has
been adapted to work inside libosmocore.

Change-Id: I9ee199381c7b5986a9540d124836cdddd0f66c86
---
A include/osmocom/core/sercomm.h
A src/sercomm.c
2 files changed, 404 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/33/2633/1

diff --git a/include/osmocom/core/sercomm.h b/include/osmocom/core/sercomm.h
new file mode 100644
index 0000000..21f715b
--- /dev/null
+++ b/include/osmocom/core/sercomm.h
@@ -0,0 +1,61 @@
+#ifndef _SERCOMM_H
+#define _SERCOMM_H
+
+#include <osmocom/core/msgb.h>
+
+#define HDLC_FLAG	0x7E
+#define HDLC_ESCAPE	0x7D
+
+#define HDLC_C_UI	0x03
+#define HDLC_C_P_BIT	(1 << 4)
+#define HDLC_C_F_BIT	(1 << 4)
+
+/* a low sercomm_dlci means high priority.  A high DLCI means low priority */
+enum sercomm_dlci {
+	SC_DLCI_HIGHEST = 0,
+	SC_DLCI_DEBUG   = 4,
+	SC_DLCI_L1A_L23 = 5,
+	SC_DLCI_LOADER  = 9,
+	SC_DLCI_CONSOLE = 10,
+	SC_DLCI_ECHO    = 128,
+	_SC_DLCI_MAX
+};
+
+#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);
+#endif
+
+void sercomm_init(void);
+int sercomm_initialized(void);
+
+/* User Interface: Tx */
+
+/* user interface for transmitting messages for a given DLCI */
+void sercomm_sendmsg(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);
+
+/* 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);
+
+/* Driver Interface */
+
+/* fetch one octet of to-be-transmitted serial data. returns 0 if no more data */
+int sercomm_drv_pull(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);
+
+static inline struct msgb *sercomm_alloc_msgb(unsigned int len)
+{
+	return msgb_alloc_headroom(len+4, 4, "sercomm_tx");
+}
+
+#endif /* _SERCOMM_H */
diff --git a/src/sercomm.c b/src/sercomm.c
new file mode 100644
index 0000000..810d661
--- /dev/null
+++ b/src/sercomm.c
@@ -0,0 +1,343 @@
+/* Serial communications layer, based on HDLC */
+
+/* (C) 2010 by Harald Welte <laforge at gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <osmocom/core/msgb.h>
+
+#ifdef HOST_BUILD
+
+# define SERCOMM_RX_MSG_SIZE	2048
+# ifndef ARRAY_SIZE
+#  define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+# endif
+# include <sercomm.h>
+
+static inline void sercomm_lock(unsigned long __attribute__((unused)) *flags) {}
+static inline void sercomm_unlock(unsigned long __attribute__((unused)) *flags) {}
+
+#else
+
+# define SERCOMM_RX_MSG_SIZE	256
+# include <debug.h>
+# include <osmocom/core/linuxlist.h>
+# include <asm/system.h>
+
+static inline void sercomm_lock(unsigned long *flags)
+{
+	local_firq_save(*flags);
+}
+
+static inline void sercomm_unlock(unsigned long *flags)
+{
+	local_irq_restore(*flags);
+}
+
+# include <comm/sercomm.h>
+# include <uart.h>
+
+#endif
+
+
+enum rx_state {
+	RX_ST_WAIT_START,
+	RX_ST_ADDR,
+	RX_ST_CTRL,
+	RX_ST_DATA,
+	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)
+{
+	sercomm.uart_id = uart;
+}
+
+int sercomm_get_uart(void)
+{
+	return sercomm.uart_id;
+}
+#endif
+
+void sercomm_init(void)
+{
+	unsigned int 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;
+
+	/* set up the echo dlci */
+	sercomm_register_rx_cb(SC_DLCI_ECHO, &sercomm_sendmsg);
+}
+
+int sercomm_initialized(void)
+{
+	return sercomm.initialized;
+}
+
+/* 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! */
+	sercomm_lock(&flags);
+	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);
+#endif
+}
+
+/* how deep is the Tx queue for a given DLCI */
+unsigned int sercomm_tx_queue_depth(uint8_t dlci)
+{
+	struct llist_head *le;
+	unsigned int num = 0;
+
+	llist_for_each(le, &sercomm.tx.dlci_queues[dlci]) {
+		num++;
+	}
+
+	return num;
+}
+
+#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)
+{
+	unsigned int i, count;
+	unsigned long flags;
+
+	while (1) {
+		/* count the number of pending messages */
+		count = 0;
+		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)
+			break;
+	}
+
+	while (1) {
+		/* 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) {
+			/* change speed */
+			uart_baudrate(sercomm.uart_id, bdrt);
+			sercomm_unlock(&flags);
+			break;
+		}
+			sercomm_unlock(&flags);
+	}
+}
+#endif
+
+/* fetch one octet of to-be-transmitted serial data */
+int sercomm_drv_pull(uint8_t *ch)
+{
+	unsigned long flags;
+
+	/* we may be called from interrupt context, but we stiff need to lock
+	 * because sercomm could be accessed from a FIQ context ... */
+
+	sercomm_lock(&flags);
+
+	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)
+				break;
+		}
+		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_unlock(&flags);
+			return 1;
+		} else {
+			/* no more data avilable */
+			sercomm_unlock(&flags);
+			return 0;
+		}
+	}
+
+	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) {
+		/* 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;
+	/* 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) {
+		/* 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;
+	} else {
+		/* standard case, simply send next octet */
+		*ch = *sercomm.tx.next_char++;
+	}
+
+	sercomm_unlock(&flags);
+	return 1;
+}
+
+/* register a handler for a given DLCI */
+int sercomm_register_rx_cb(uint8_t dlci, dlci_cb_t cb)
+{
+	if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler))
+		return -EINVAL;
+
+	if (sercomm.rx.dlci_handler[dlci])
+		return -EBUSY;
+
+	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)
+{
+	if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler) ||
+	    !sercomm.rx.dlci_handler[dlci]) {
+		msgb_free(msg);
+		return;
+	}
+	sercomm.rx.dlci_handler[dlci](dlci, msg);
+}
+
+/* the driver has received one byte, pass it into sercomm layer */
+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;
+		return 0;
+	}
+
+	switch (sercomm.rx.state) {
+	case RX_ST_WAIT_START:
+		if (ch != HDLC_FLAG)
+			break;
+		sercomm.rx.state = RX_ST_ADDR;
+		break;
+	case RX_ST_ADDR:
+		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;
+		break;
+	case RX_ST_DATA:
+		if (ch == HDLC_ESCAPE) {
+			/* drop the escape octet, but change state */
+			sercomm.rx.state = RX_ST_ESCAPE;
+			break;
+		} else if (ch == HDLC_FLAG) {
+			/* message is finished */
+			dispatch_rx_msg(sercomm.rx.dlci, sercomm.rx.msg);
+			/* allocate new buffer */
+			sercomm.rx.msg = NULL;
+			/* start all over again */
+			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 = ch;
+		break;
+	case RX_ST_ESCAPE:
+		/* store bif-5-inverted octet in buffer */
+		ch ^= (1 << 5);
+		ptr = msgb_put(sercomm.rx.msg, 1);
+		*ptr = ch;
+		/* transition back to normal DATA state */
+		sercomm.rx.state = RX_ST_DATA;
+		break;
+	}
+
+	return 1;
+}

-- 
To view, visit https://gerrit.osmocom.org/2633
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I9ee199381c7b5986a9540d124836cdddd0f66c86
Gerrit-PatchSet: 1
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Owner: Harald Welte <laforge at gnumonks.org>



More information about the gerrit-log mailing list