From 7e0d006e3c7f163cf1e3621d77a7366a5a7e828a Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 19 Aug 2011 11:36:15 +0200 Subject: input: add rs232 driver for virtual E1 lines This patch adds the rs232 driver which is used by then bs11_config utility available in openBSC. --- include/osmocom/abis/e1_input.h | 3 + src/e1_input.c | 2 + src/input/Makefile.am | 3 +- src/input/rs232.c | 301 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 src/input/rs232.c diff --git a/include/osmocom/abis/e1_input.h b/include/osmocom/abis/e1_input.h index 8cf5c71..9562928 100644 --- a/include/osmocom/abis/e1_input.h +++ b/include/osmocom/abis/e1_input.h @@ -100,6 +100,9 @@ struct e1inp_ts { struct osmo_fd fd; struct lapd_instance *lapd; } dahdi; + struct { + struct osmo_fd fd; + } rs232; } driver; }; diff --git a/src/e1_input.c b/src/e1_input.c index 47d58ca..22f38d8 100644 --- a/src/e1_input.c +++ b/src/e1_input.c @@ -643,6 +643,7 @@ void e1inp_misdn_init(void); void e1inp_dahdi_init(void); void e1inp_ipaccess_init(void); void e1inp_hsl_init(void); +void e1inp_rs232_init(void); void e1inp_init(void) { @@ -655,4 +656,5 @@ void e1inp_init(void) e1inp_dahdi_init(); e1inp_ipaccess_init(); e1inp_hsl_init(); + e1inp_rs232_init(); } diff --git a/src/input/Makefile.am b/src/input/Makefile.am index c382269..9937465 100644 --- a/src/input/Makefile.am +++ b/src/input/Makefile.am @@ -9,4 +9,5 @@ libosmoabis_input_la_SOURCES = dahdi.c \ ipa.c \ ipaccess.c \ lapd.c \ - misdn.c + misdn.c \ + rs232.c diff --git a/src/input/rs232.c b/src/input/rs232.c new file mode 100644 index 0000000..eb6b5d0 --- /dev/null +++ b/src/input/rs232.c @@ -0,0 +1,301 @@ +/* T-Link interface using POSIX serial port */ + +/* (C) 2008-2011 by Harald Welte + * + * All Rights Reserved + * + * Authors: Harald Welte + * Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static void *tall_rs232_ctx; + +struct serial_handle { + struct e1inp_line *line; + + struct msgb *rx_msg; + unsigned int rxmsg_bytes_missing; + + unsigned int delay_ms; +}; + +#define CRAPD_HDR_LEN 10 + +static int handle_ser_write(struct osmo_fd *bfd); + +static void rs232_build_msg(struct msgb *msg) +{ + uint8_t *crapd; + unsigned int len; + + msg->l2h = msg->data; + + /* prepend CRAPD header */ + crapd = msgb_push(msg, CRAPD_HDR_LEN); + + len = msg->len - 2; + + crapd[0] = (len >> 8) & 0xff; + crapd[1] = len & 0xff; /* length of bytes startign at crapd[2] */ + crapd[2] = 0x00; + crapd[3] = 0x07; + crapd[4] = 0x01; + crapd[5] = 0x3e; + crapd[6] = 0x00; + crapd[7] = 0x00; + crapd[8] = msg->len - 10; /* length of bytes starting at crapd[10] */ + crapd[9] = crapd[8] ^ 0x38; +} + +/* select.c callback in case we can write to the rs232 */ +static int handle_ser_write(struct osmo_fd *bfd) +{ + struct serial_handle *sh = bfd->data; + struct e1inp_ts *e1i_ts = &sh->line->ts[0]; + struct e1inp_sign_link *sign_link; + struct msgb *msg; + int written; + + bfd->when &= ~BSC_FD_WRITE; + + /* get the next msg for this timeslot */ + msg = e1inp_tx_ts(e1i_ts, &sign_link); + if (!msg) { + /* no message after tx delay timer */ + return 0; + } + DEBUGP(DLMI, "rs232 TX: %s\n", osmo_hexdump(msg->data, msg->len)); + + rs232_build_msg(msg); + + /* send over serial line */ + written = write(bfd->fd, msg->data, msg->len); + if (written < msg->len) { + LOGP(DLMI, LOGL_ERROR, "rs232: short write\n"); + msgb_free(msg); + return -1; + } + + msgb_free(msg); + usleep(sh->delay_ms*1000); + + return 0; +} + +#define SERIAL_ALLOC_SIZE 300 + +/* select.c callback in case we can read from the rs232 */ +static int handle_ser_read(struct osmo_fd *bfd) +{ + struct serial_handle *sh = bfd->data; + struct msgb *msg; + int rc = 0; + + if (!sh->rx_msg) { + sh->rx_msg = msgb_alloc(SERIAL_ALLOC_SIZE, "rs232 Rx"); + sh->rx_msg->l2h = NULL; + } + msg = sh->rx_msg; + + /* first read two byes to obtain length */ + if (msg->len < 2) { + rc = read(bfd->fd, msg->tail, 2 - msg->len); + if (rc < 0) { + LOGP(DLMI, LOGL_ERROR, "rs232: error reading from " + "serial port: %s\n", strerror(errno)); + msgb_free(msg); + return rc; + } + msgb_put(msg, rc); + + if (msg->len >= 2) { + /* parse CRAPD payload length */ + if (msg->data[0] != 0) { + LOGP(DLMI, LOGL_ERROR, + "Suspicious header byte 0: 0x%02x\n", + msg->data[0]); + } + sh->rxmsg_bytes_missing = msg->data[0] << 8; + sh->rxmsg_bytes_missing += msg->data[1]; + + if (sh->rxmsg_bytes_missing < CRAPD_HDR_LEN -2) { + LOGP(DLMI, LOGL_ERROR, + "Invalid length in hdr: %u\n", + sh->rxmsg_bytes_missing); + } + } + } else { + /* try to read as many of the missing bytes as are available */ + rc = read(bfd->fd, msg->tail, sh->rxmsg_bytes_missing); + if (rc < 0) { + LOGP(DLMI, LOGL_ERROR, "rs232: error reading from " + "serial port: %s", strerror(errno)); + msgb_free(msg); + return rc; + } + msgb_put(msg, rc); + sh->rxmsg_bytes_missing -= rc; + + if (sh->rxmsg_bytes_missing == 0) { + struct e1inp_ts *e1i_ts = &sh->line->ts[0]; + + /* we have one complete message now */ + sh->rx_msg = NULL; + + if (msg->len > CRAPD_HDR_LEN) + msg->l2h = msg->data + CRAPD_HDR_LEN; + + DEBUGP(DLMI, "rs232 RX: %s", + osmo_hexdump(msg->data, msg->len)); + + /* don't use e1inp_tx_ts() here, this header does not + * contain any SAPI and TEI values. */ + if (!e1i_ts->line->ops->sign_link) { + LOGP(DLMI, LOGL_ERROR, "rs232: no callback set, " + "skipping message.\n"); + return -EINVAL; + } + e1i_ts->line->ops->sign_link(msg); + } + } + + return rc; +} + +/* select.c callback */ +static int serial_fd_cb(struct osmo_fd *bfd, unsigned int what) +{ + int rc = 0; + + if (what & BSC_FD_READ) + rc = handle_ser_read(bfd); + + if (rc < 0) + return rc; + + if (what & BSC_FD_WRITE) + rc = handle_ser_write(bfd); + + return rc; +} + +static int rs232_want_write(struct e1inp_ts *e1i_ts) +{ + e1i_ts->driver.rs232.fd.when |= BSC_FD_WRITE; + + return 0; +} + +static int +rs232_setup(struct e1inp_line *line, const char *serial_port, unsigned int delay_ms) +{ + int rc; + struct osmo_fd *bfd = &line->ts[0].driver.rs232.fd; + struct serial_handle *ser_handle; + struct termios tio; + + rc = open(serial_port, O_RDWR); + if (rc < 0) { + LOGP(DLMI, LOGL_ERROR, "rs232: cannot open serial port: %s", + strerror(errno)); + return rc; + } + bfd->fd = rc; + + /* set baudrate */ + rc = tcgetattr(bfd->fd, &tio); + if (rc < 0) { + LOGP(DLMI, LOGL_ERROR, "rs232: tcgetattr says: %s", + strerror(errno)); + return rc; + } + cfsetispeed(&tio, B19200); + cfsetospeed(&tio, B19200); + tio.c_cflag |= (CREAD | CLOCAL | CS8); + tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS); + tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + tio.c_iflag |= (INPCK | ISTRIP); + tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR); + tio.c_oflag &= ~(OPOST); + rc = tcsetattr(bfd->fd, TCSADRAIN, &tio); + if (rc < 0) { + LOGP(DLMI, LOGL_ERROR, "rs232: tcsetattr says: %s", + strerror(errno)); + return rc; + } + + ser_handle = talloc_zero(tall_rs232_ctx, struct serial_handle); + if (ser_handle == NULL) { + close(bfd->fd); + LOGP(DLMI, LOGL_ERROR, "rs232: cannot allocate memory for " + "serial handler\n"); + return -ENOMEM; + } + ser_handle->line = line; + ser_handle->delay_ms = delay_ms; + + bfd->when = BSC_FD_READ; + bfd->cb = serial_fd_cb; + bfd->data = ser_handle; + + rc = osmo_fd_register(bfd); + if (rc < 0) { + close(bfd->fd); + LOGP(DLMI, LOGL_ERROR, "rs232: could not register FD: %s\n", + strerror(rc)); + return rc; + } + + return 0; +} + +static int rs232_line_update(struct e1inp_line *line, + enum e1inp_line_role role, const char *addr); + +static struct e1inp_driver rs232_driver = { + .name = "rs232", + .want_write = rs232_want_write, + .line_update = rs232_line_update, +}; + +static int rs232_line_update(struct e1inp_line *line, + enum e1inp_line_role role, const char *addr) +{ + if (line->driver != &rs232_driver) + return -EINVAL; + + return rs232_setup(line, addr, 0); +} + +int e1inp_rs232_init(void) +{ + return e1inp_driver_register(&rs232_driver); +} -- cgit v1.2.3-55-g7522