summaryrefslogtreecommitdiffstats
path: root/src/target/firmware/comm
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2010-02-18 16:46:36 +0100
committerHarald Welte <laforge@gnumonks.org>2010-02-18 16:46:36 +0100
commitfbe7b94c9c65f2df74acd5dff7503c9833ec2579 (patch)
tree5f47a597f2f396662719c5a76ac6bf26eda69f6c /src/target/firmware/comm
Initial import of OsmocomBB into git repository
Diffstat (limited to 'src/target/firmware/comm')
-rw-r--r--src/target/firmware/comm/Makefile26
-rw-r--r--src/target/firmware/comm/msgb.c134
-rw-r--r--src/target/firmware/comm/sercomm.c244
-rw-r--r--src/target/firmware/comm/sercomm_cons.c126
4 files changed, 530 insertions, 0 deletions
diff --git a/src/target/firmware/comm/Makefile b/src/target/firmware/comm/Makefile
new file mode 100644
index 00000000..c2c6fcf0
--- /dev/null
+++ b/src/target/firmware/comm/Makefile
@@ -0,0 +1,26 @@
+INCLUDES=-I../include/
+-include ../Makefile.inc
+
+LIBNAME=comm
+CSRCS=msgb.c sercomm.c sercomm_cons.c
+SSRCS=
+
+COBJS=$(CSRCS:.c=.o)
+SOBJS=$(SSRCS:.S=.o)
+OBJS=$(COBJS) $(SOBJS)
+
+LST=$(OBJS:.o=.lst)
+
+all: lib$(LIBNAME).a
+
+$(COBJS): %.o : %.c
+ $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^
+
+$(SOBJS): %.o : %.S
+ $(CROSS_COMPILE)$(CC) $(ASFLAGS) -c -o $@ $^
+
+lib$(LIBNAME).a: $(OBJS)
+ $(CROSS_COMPILE)$(AR) cru $@ $^
+
+clean:
+ rm -f *.a $(OBJS) $(LST)
diff --git a/src/target/firmware/comm/msgb.c b/src/target/firmware/comm/msgb.c
new file mode 100644
index 00000000..8245ed20
--- /dev/null
+++ b/src/target/firmware/comm/msgb.c
@@ -0,0 +1,134 @@
+/* (C) 2008-2010 by Harald Welte <laforge@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 <string.h>
+#include <sys/types.h>
+
+#include <debug.h>
+
+#include <comm/msgb.h>
+
+#include <calypso/backlight.h>
+
+#define NO_TALLOC
+
+void *tall_msgb_ctx;
+
+#ifdef NO_TALLOC
+/* This is a poor mans static allocator for msgb objects */
+#define MSGB_DATA_SIZE 256
+#define MSGB_NUM 16
+struct supermsg {
+ uint8_t allocated;
+ struct msgb msg;
+ uint8_t buf[MSGB_DATA_SIZE];
+};
+static struct supermsg msgs[MSGB_NUM];
+static void *_talloc_zero(void *ctx, unsigned int size, const char *name)
+{
+ unsigned int i;
+ if (size > sizeof(struct msgb) + MSGB_DATA_SIZE)
+ goto panic;
+ for (i = 0; i < ARRAY_SIZE(msgs); i++) {
+ if (!msgs[i].allocated) {
+ msgs[i].allocated = 1;
+ memset(&msgs[i].msg, 0, sizeof(&msgs[i].msg));
+ memset(&msgs[i].buf, 0, sizeof(&msgs[i].buf));
+ return &msgs[i].msg;
+ }
+ }
+
+panic:
+ while (1) {
+ bl_level(++i % 50);
+ delay_ms(50);
+ }
+ return NULL;
+}
+static void talloc_free(void *msg)
+{
+ struct supermsg *smsg = container_of(msg, struct supermsg, msg);
+ smsg->allocated = 0;
+}
+#endif
+
+struct msgb *msgb_alloc(uint16_t size, const char *name)
+{
+ struct msgb *msg;
+
+ msg = _talloc_zero(tall_msgb_ctx, sizeof(*msg) + size, name);
+
+ if (!msg) {
+ cons_puts("unable to allocate msgb\n");
+ return NULL;
+ }
+
+ msg->data_len = size;
+ msg->len = 0;
+ msg->data = msg->_data;
+
+ msg->head = msg->data;
+ msg->data = msg->data;
+ /* reset tail pointer */
+ msg->tail = msg->data;
+
+ return msg;
+}
+
+void msgb_free(struct msgb *m)
+{
+ talloc_free(m);
+}
+
+void msgb_enqueue(struct llist_head *queue, struct msgb *msg)
+{
+ llist_add_tail(&msg->list, queue);
+}
+
+struct msgb *msgb_dequeue(struct llist_head *queue)
+{
+ struct llist_head *lh;
+
+ if (llist_empty(queue))
+ return NULL;
+
+ lh = queue->next;
+ llist_del(lh);
+
+ return llist_entry(lh, struct msgb, list);
+}
+
+void msgb_reset(struct msgb *msg)
+{
+ msg->len = 0;
+ msg->len = 0;
+ msg->data = msg->_data;
+
+ msg->head = msg->data;
+ msg->data = msg->data;
+ /* reset tail pointer */
+ msg->tail = msg->data;
+
+ /* reset pointers */
+ msg->l2h = NULL;
+ msg->l3h = NULL;
+}
diff --git a/src/target/firmware/comm/sercomm.c b/src/target/firmware/comm/sercomm.c
new file mode 100644
index 00000000..d852eb99
--- /dev/null
+++ b/src/target/firmware/comm/sercomm.c
@@ -0,0 +1,244 @@
+/* Serial communications layer, based on HDLC */
+
+/* (C) 2010 by Harald Welte <laforge@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>
+
+#ifdef HOST_BUILD
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+#endif
+#include <osmocom/msgb.h>
+#include <sercomm.h>
+
+#else
+#include <debug.h>
+#include <linuxlist.h>
+
+#include <comm/msgb.h>
+#include <comm/sercomm.h>
+#include <calypso/uart.h>
+#endif
+
+#define SERCOMM_RX_MSG_SIZE 256
+
+enum rx_state {
+ RX_ST_WAIT_START,
+ RX_ST_ADDR,
+ RX_ST_CTRL,
+ RX_ST_DATA,
+ RX_ST_ESCAPE,
+};
+
+static struct {
+ int initialized;
+
+ /* transmit side */
+ struct {
+ struct llist_head dlci_queues[_SC_DLCI_MAX];
+ struct msgb *msg;
+ 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;
+
+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;
+}
+
+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)
+{
+ uint8_t *hdr;
+
+ /* prepend address + control octet */
+ hdr = msgb_push(msg, 2);
+ hdr[0] = dlci;
+ hdr[1] = HDLC_C_UI;
+ msgb_enqueue(&sercomm.tx.dlci_queues[dlci], msg);
+
+#ifndef HOST_BUILD
+ /* tell UART that we have something to send */
+ uart_irq_enable(SERCOMM_UART_NR, 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;
+}
+
+/* fetch one octet of to-be-transmitted serial data */
+int sercomm_drv_pull(uint8_t *ch)
+{
+ 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;
+ return 1;
+ } else {
+ /* no more data avilable */
+ return 0;
+ }
+ }
+
+ /* escaping for the two control octets */
+ if (*sercomm.tx.next_char == HDLC_FLAG ||
+ *sercomm.tx.next_char == HDLC_ESCAPE) {
+ /* send an escape octet */
+ *ch = HDLC_ESCAPE;
+ /* invert bit 5 of the next octet to be sent */
+ *sercomm.tx.next_char ^= (1 << 5);
+ } 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;
+ } else {
+ /* standard case, simply send next octet */
+ *ch = *sercomm.tx.next_char++;
+ }
+ 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 incomnig message once it is completely received */
+static void dispatch_rx_msg(uint8_t dlci, struct msgb *msg)
+{
+ if (sercomm.rx.dlci_handler[dlci])
+ sercomm.rx.dlci_handler[dlci](dlci, msg);
+ else
+ msgb_free(msg);
+}
+
+/* the driver has received one byte, pass it into sercomm layer */
+int sercomm_drv_rx_char(uint8_t ch)
+{
+ uint8_t *ptr;
+
+ if (!sercomm.rx.msg)
+ sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE);
+
+ if (msgb_tailroom(sercomm.rx.msg) == 0) {
+ 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)
+ return 0;
+ 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 nromal DATA state */
+ sercomm.rx.state = RX_ST_DATA;
+ break;
+ }
+ return 1;
+}
diff --git a/src/target/firmware/comm/sercomm_cons.c b/src/target/firmware/comm/sercomm_cons.c
new file mode 100644
index 00000000..fa08b50e
--- /dev/null
+++ b/src/target/firmware/comm/sercomm_cons.c
@@ -0,0 +1,126 @@
+/* Serial console layer, layered on top of sercomm HDLC */
+
+/* (C) 2010 by Harald Welte <laforge@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 <errno.h>
+#include <string.h>
+
+#include <calypso/uart.h>
+
+#include <console.h>
+#include <comm/msgb.h>
+#include <comm/sercomm.h>
+#include <comm/sercomm_cons.h>
+
+static struct {
+ struct msgb *cur_msg;
+} scons;
+
+static void raw_puts(const char *s)
+{
+ int i = strlen(s);
+ while (i--)
+ uart_putchar_wait(SERCOMM_UART_NR, *s++);
+}
+
+#ifdef DEBUG
+#define raw_putd(x) raw_puts(x)
+#else
+#define raw_putd(x)
+#endif
+
+int sercomm_puts(const char *s)
+{
+ const int len = strlen(s) + 1;
+ unsigned int bytes_left = len;
+
+ if (!sercomm_initialized()) {
+ raw_putd("sercomm not initialized: ");
+ raw_puts(s);
+ return len - 1;
+ }
+
+ while (bytes_left > 0) {
+ unsigned int write_num, space_left, flush;
+ uint8_t *data;
+
+ if (!scons.cur_msg)
+ scons.cur_msg = sercomm_alloc_msgb(SERCOMM_CONS_ALLOC);
+
+ if (!scons.cur_msg) {
+ raw_putd("cannot allocate sercomm msgb: ");
+ raw_puts(s);
+ return -ENOMEM;
+ }
+
+ /* space left in the current msgb */
+ space_left = msgb_tailroom(scons.cur_msg);
+
+ if (space_left <= bytes_left) {
+ write_num = space_left;
+ /* flush buffer when it is full */
+ flush = 1;
+ } else {
+ write_num = bytes_left;
+ flush = 0;
+ }
+
+ /* obtain pointer where to copy the data */
+ data = msgb_put(scons.cur_msg, write_num);
+
+ /* copy data while looking for \n line termination */
+ {
+ unsigned int i;
+ for (i = 0; i < write_num; i++) {
+ /* flush buffer at end of line */
+ if (*s == '\n')
+ flush = 1;
+ *data++ = *s++;
+ }
+ }
+ bytes_left -= write_num;
+
+ if (flush) {
+ sercomm_sendmsg(SC_DLCI_CONSOLE, scons.cur_msg);
+ /* reset scons.cur_msg pointer to ensure we allocate
+ * a new one next round */
+ scons.cur_msg = NULL;
+ }
+ }
+
+ return len - 1;
+}
+
+int sercomm_putchar(int c)
+{
+ char s[2];
+ int rc;
+
+ s[0] = c & 0xff;
+ s[1] = '\0';
+
+ rc = sercomm_puts(s);
+ if (rc < 0)
+ return rc;
+
+ return c;
+}