diff options
author | Harald Welte <laforge@gnumonks.org> | 2010-02-18 16:46:36 +0100 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2010-02-18 16:46:36 +0100 |
commit | fbe7b94c9c65f2df74acd5dff7503c9833ec2579 (patch) | |
tree | 5f47a597f2f396662719c5a76ac6bf26eda69f6c /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/Makefile | 26 | ||||
-rw-r--r-- | src/target/firmware/comm/msgb.c | 134 | ||||
-rw-r--r-- | src/target/firmware/comm/sercomm.c | 244 | ||||
-rw-r--r-- | src/target/firmware/comm/sercomm_cons.c | 126 |
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; +} |