Harald Welte has submitted this change and it was merged.

Change subject: Import sercomm.c from osmocom-bb
......................................................................


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(-)

Approvals:
  Harald Welte: Looks good to me, approved
  Jenkins Builder: Verified



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 <[email protected]>
+ *
+ * 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: merged
Gerrit-Change-Id: I9ee199381c7b5986a9540d124836cdddd0f66c86
Gerrit-PatchSet: 3
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Owner: Harald Welte <[email protected]>
Gerrit-Reviewer: Harald Welte <[email protected]>
Gerrit-Reviewer: Jenkins Builder

Reply via email to