aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@gnumonks.org>2012-03-17 12:20:49 +0100
committerPablo Neira Ayuso <pablo@netfilter.org>2012-04-04 19:08:27 +0200
commit729a0917ac17e2c5a70ab24cc884ff68a7df52af (patch)
treeb6fab3d56abfcdf4af2ac7504271aef3940aefa8
parent163707ba12dc7cee520eee88b4f1d4dad2c20049 (diff)
add rs232 support
This include an example to open /dev/ttyACM0 to receive data from u-blox GPS and one to configure it in TIMEPULSE2 mode.
-rw-r--r--examples/Makefile.am8
-rw-r--r--examples/rs232-read.c92
-rw-r--r--examples/rs232-write.c386
-rw-r--r--include/osmocom/netif/rs232.h21
-rw-r--r--src/Makefile.am1
-rw-r--r--src/rs232.c272
6 files changed, 780 insertions, 0 deletions
diff --git a/examples/Makefile.am b/examples/Makefile.am
index c9849db..1063245 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -10,6 +10,8 @@ noinst_PROGRAMS = ipa-stream-client \
lapd-over-datagram-network \
stream-client \
stream-server \
+ rs232-read \
+ rs232-write \
rtp-udp-test-client \
rtp-udp-test-server
@@ -39,6 +41,12 @@ stream_server_SOURCES = stream-server.c
stream_server_LDADD = $(top_builddir)/src/libosmonetif.la \
$(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS)
+rs232_read_SOURCES = rs232-read.c
+rs232_read_LDADD = $(top_builddir)/src/libosmonetif.la $(LIBOSMOCORE_LIBS)
+
+rs232_write_SOURCES = rs232-write.c
+rs232_write_LDADD = $(top_builddir)/src/libosmonetif.la $(LIBOSMOCORE_LIBS)
+
rtp_udp_test_client_SOURCES = rtp-udp-test-client.c
rtp_udp_test_client_LDADD = $(top_builddir)/src/libosmonetif.la \
$(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS)
diff --git a/examples/rs232-read.c b/examples/rs232-read.c
new file mode 100644
index 0000000..8aeb6f8
--- /dev/null
+++ b/examples/rs232-read.c
@@ -0,0 +1,92 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/application.h>
+
+#include <osmocom/netif/rs232.h>
+
+#define DRS232TEST 0
+
+struct log_info_cat osmo_rs232_test_cat[] = {
+ [DRS232TEST] = {
+ .name = "DRS232TEST",
+ .description = "rs232 test",
+ .color = "\033[1;35m",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+};
+
+const struct log_info osmo_rs232_test_log_info = {
+ .filter_fn = NULL,
+ .cat = osmo_rs232_test_cat,
+ .num_cat = ARRAY_SIZE(osmo_rs232_test_cat),
+};
+
+static struct osmo_rs232 *r;
+
+void sighandler(int foo)
+{
+ LOGP(DRS232TEST, LOGL_NOTICE, "closing rs232.\n");
+ osmo_rs232_close(r);
+ osmo_rs232_destroy(r);
+ exit(EXIT_SUCCESS);
+}
+
+static int read_cb(struct osmo_rs232 *r)
+{
+ struct msgb *msg;
+
+ LOGP(DRS232TEST, LOGL_DEBUG, "received data from rs232\n");
+
+ msg = msgb_alloc(1024, "rs232/test");
+ if (msg == NULL) {
+ LOGP(DRS232TEST, LOGL_ERROR, "cannot allocate message\n");
+ return 0;
+ }
+ if (osmo_rs232_read(r, msg) < 0) {
+ LOGP(DRS232TEST, LOGL_ERROR, "cannot read from rs232\n");
+ return 0;
+ }
+ LOGP(DRS232TEST, LOGL_DEBUG, "received %d bytes\n", msg->len);
+
+ printf("%s", msg->data);
+
+ msgb_free(msg);
+ return 0;
+}
+
+static void *tall_test;
+
+int main(void)
+{
+ tall_test = talloc_named_const(NULL, 1, "osmo_rs232_test");
+
+ osmo_init_logging(&osmo_rs232_test_log_info);
+ log_set_log_level(osmo_stderr_target, LOGL_NOTICE);
+
+ r = osmo_rs232_create(tall_test);
+ if (r == NULL) {
+ LOGP(DRS232TEST, LOGL_ERROR, "cannot create rs232 object\n");
+ exit(EXIT_FAILURE);
+ }
+ osmo_rs232_set_serial_port(r, "/dev/ttyACM0");
+ osmo_rs232_set_baudrate(r, 9600);
+ osmo_rs232_set_read_cb(r, read_cb);
+
+ if (osmo_rs232_open(r) < 0) {
+ LOGP(DRS232TEST, LOGL_ERROR, "cannot open rs232\n");
+ exit(EXIT_FAILURE);
+ }
+
+ LOGP(DRS232TEST, LOGL_NOTICE, "Entering main loop\n");
+
+ while(1) {
+ osmo_select_main(0);
+ }
+}
diff --git a/examples/rs232-write.c b/examples/rs232-write.c
new file mode 100644
index 0000000..370bd12
--- /dev/null
+++ b/examples/rs232-write.c
@@ -0,0 +1,386 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/application.h>
+
+#include <osmocom/netif/rs232.h>
+
+#define DRS232TEST 0
+
+struct log_info_cat osmo_rs232_test_cat[] = {
+ [DRS232TEST] = {
+ .name = "DRS232TEST",
+ .description = "rs232 test",
+ .color = "\033[1;35m",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
+};
+
+const struct log_info osmo_rs232_test_log_info = {
+ .filter_fn = NULL,
+ .cat = osmo_rs232_test_cat,
+ .num_cat = ARRAY_SIZE(osmo_rs232_test_cat),
+};
+
+static struct osmo_rs232 *r;
+
+void sighandler(int foo)
+{
+ LOGP(DRS232TEST, LOGL_NOTICE, "closing rs232.\n");
+ osmo_rs232_close(r);
+ osmo_rs232_destroy(r);
+ exit(EXIT_SUCCESS);
+}
+
+static int read_cb(struct osmo_rs232 *r)
+{
+ struct msgb *msg;
+
+ LOGP(DRS232TEST, LOGL_DEBUG, "received data from rs232\n");
+
+ msg = msgb_alloc(1024, "rs232/test");
+ if (msg == NULL) {
+ LOGP(DRS232TEST, LOGL_ERROR, "cannot allocate message\n");
+ return 0;
+ }
+ if (osmo_rs232_read(r, msg) < 0) {
+ LOGP(DRS232TEST, LOGL_ERROR, "cannot receive message\n");
+ return 0;
+ }
+ LOGP(DRS232TEST, LOGL_DEBUG, "received %d bytes\n", msg->len);
+
+ printf("received %d bytes ", msg->len);
+
+ int i;
+ printf("(");
+ for (i=0; i<msg->len; i++)
+ printf("\\x%.2x", 0xff & msg->data[i]);
+ printf(") %s\n", msg->data);
+
+ msgb_free(msg);
+
+ return 0;
+}
+
+static void *tall_test;
+
+/* u-blox6_ReceiverDescriptionProtocolSpec_(GPS.G6-SW-10018).pdf */
+
+/* See Sect 23. */
+struct ubx_hdr {
+ uint8_t sync_char1; /* 0xb5 */
+ uint8_t sync_char2; /* 0x62 */
+ uint8_t class;
+ uint8_t id;
+} __attribute__((packed));
+
+static void ubx_header(struct msgb *msg, uint8_t class, uint8_t id)
+{
+ /* See Sect. 31.24 */
+ struct ubx_hdr ubxhdr = {
+ .sync_char1 = 0xb5,
+ .sync_char2 = 0x62,
+ .class = class,
+ .id = id,
+ };
+ memcpy(msg->data, &ubxhdr, sizeof(struct ubx_hdr));
+ msgb_put(msg, sizeof(struct ubx_hdr));
+}
+
+/* See Sect 26. */
+static void ubx_checksum(struct msgb *msg, uint8_t *ck)
+{
+ struct ubx_hdr *ubxhdr = (struct ubx_hdr *)msg->data;
+ /* skip sync chars in checksum calculation. */
+ uint8_t *buf = ((uint8_t *)ubxhdr) + 2;
+ int i;
+
+ memset(ck, 0, sizeof(uint16_t));
+
+ for (i=0; i<msg->len-2; i++) {
+ ck[0] += buf[i];
+ ck[1] += ck[0];
+ }
+}
+
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+# define utohl(x) (x)
+# define utohs(x) (x)
+# define htoul(x) (x)
+# define htous(x) (x)
+# else
+# if __BYTE_ORDER == __BIG_ENDIAN
+# define utohl(x) __bswap_32 (x)
+# define utohs(x) __bswap_16 (x)
+# define htoul(x) __bswap_32 (x)
+# define htous(x) __bswap_16 (x)
+# endif
+# endif
+
+static void ubx_payload_start(struct msgb *msg)
+{
+ uint16_t len = 0;
+ /* make room for payload length. */
+ memcpy(msg->data + msg->len, &len, sizeof(len));
+ msgb_put(msg, sizeof(len));
+}
+
+static void ubx_payload_put_u8(struct msgb *msg, uint8_t data)
+{
+ memcpy(msg->data + msg->len, &data, sizeof(data));
+ msgb_put(msg, sizeof(data));
+}
+
+static void ubx_payload_put_le16(struct msgb *msg, uint16_t data)
+{
+ uint16_t le_data = htous(data);
+ memcpy(msg->data + msg->len, &le_data, sizeof(data));
+ msgb_put(msg, sizeof(data));
+}
+
+static void ubx_payload_put_le32(struct msgb *msg, uint32_t data)
+{
+ uint32_t le_data = htoul(data);
+ memcpy(msg->data + msg->len, &le_data, sizeof(data));
+ msgb_put(msg, sizeof(data));
+}
+
+static void ubx_payload_stop(struct msgb *msg)
+{
+ uint16_t *length = (uint16_t *) &(msg->data[4]);
+ uint8_t checksum[2];
+
+ /* length does not includes the header, ID, length.
+ * note that checksum has not been yet added.
+ */
+ *length = htous(msg->len - 6);
+
+ ubx_checksum(msg, checksum);
+ memcpy(msg->data + msg->len, checksum, sizeof(checksum));
+ msgb_put(msg, sizeof(checksum));
+}
+
+static void cfg_prt(void)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc(512, "CFG-PRT for USB");
+ if (msg == NULL)
+ exit(EXIT_FAILURE);
+
+ ubx_header(msg, 0x06, 0x00); /* CFG-PRT */
+
+ ubx_payload_start(msg);
+ ubx_payload_put_u8(msg, 0x03); /* Port ID is (=3 USB). */
+ ubx_payload_put_u8(msg, 0x00); /* Reserved. */
+ ubx_payload_put_le16(msg, 0x0000); /* TX ready. */
+ ubx_payload_put_le32(msg, 0x00000000); /* Reserved. */
+ ubx_payload_put_le32(msg, 0x00000000); /* Reserved. */
+ ubx_payload_put_le16(msg, 0x0003); /* InProtoMask (NMEA+UBX). */
+ ubx_payload_put_le16(msg, 0x0001); /* OutProtoMask (UBX). */
+ ubx_payload_put_le16(msg, 0x0000); /* Flags. */
+ ubx_payload_put_le16(msg, 0x0000); /* Reserved. */
+ ubx_payload_stop(msg);
+
+ int i;
+ for (i=0; i<msg->len; i++)
+ printf("\\x%.2x", 0xff & msg->data[i]);
+ printf("\n");
+
+ if (osmo_rs232_write(r, msg) < 0) {
+ LOGP(DRS232TEST, LOGL_ERROR, "cannot write to rs232\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static int nmea_checksum(char *nmea_cmd, uint8_t *checksum)
+{
+ int i, ret = 0;
+ uint8_t from, to;
+ char *start, *end;
+
+ /* find starting $ */
+ start = strtok(nmea_cmd, "$");
+ if (start == NULL)
+ return -1;
+
+ from = start - nmea_cmd;
+
+ end = strtok(start+1, "*");
+ if (end == NULL)
+ return -1;
+
+ to = end - nmea_cmd;
+
+ ret = (uint8_t)nmea_cmd[0];
+ for (i=from+1; i<to; i++)
+ ret ^= (uint8_t)nmea_cmd[i];
+
+ *checksum = ret;
+
+ return 0;
+}
+
+static void send_pubx(void)
+{
+ struct msgb *msg;
+
+ /* See 21.8: UBX,41.
+ *
+ * $PUBX,41,portId,inProto,outProto,baudrate,autobauding*cs
+ *
+ * [in|out]Proto: bit = 0 (ubx), bit = 1 (nmea)
+ *
+ * Sect 4. Serial Communication Ports Description
+ *
+ * 0 DDC
+ * 1 UART1
+ * 2 UART2
+ * 3 USB
+ * 4 SPI
+ * 5 reserved
+ *
+ * The NMEA command below comes without the checksum calculated.
+ */
+ char nmea_cmd[128] = "$PUBX,41,3,0001,0001,9600,0*";
+ uint8_t checksum;
+
+ if (nmea_checksum(nmea_cmd, &checksum) < 0) {
+ LOGP(DRS232TEST, LOGL_ERROR, "error calculating checksum\n");
+ exit(EXIT_FAILURE);
+ }
+ sprintf(nmea_cmd + strlen(nmea_cmd), "%u\r\n", checksum);
+
+ msg = msgb_alloc(300, "rs232/test");
+ if (msg == NULL) {
+ LOGP(DRS232TEST, LOGL_ERROR, "cannot allocate message\n");
+ exit(EXIT_FAILURE);
+ }
+ memcpy(msg->data, nmea_cmd, strlen(nmea_cmd));
+ msgb_put(msg, strlen(nmea_cmd));
+
+ if (osmo_rs232_write(r, msg) < 0) {
+ LOGP(DRS232TEST, LOGL_ERROR, "cannot write to rs232\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void cfg_tp5(void)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc(512, "CFG-TP5 for USB");
+ if (msg == NULL)
+ exit(EXIT_FAILURE);
+
+ ubx_header(msg, 0x06, 0x31); /* CFG-TP5 */
+
+ ubx_payload_start(msg);
+ ubx_payload_put_u8(msg, 0x01); /* TIMEPULSE2 (=1) */
+ ubx_payload_put_u8(msg, 0x00); /* Reserved. */
+ ubx_payload_put_le16(msg, 0x0000); /* Reserved. */
+ ubx_payload_put_le16(msg, 0); /* Antenna Delay (ns) */
+ ubx_payload_put_le16(msg, 0); /* RF Group Delay (ns) */
+ ubx_payload_put_le32(msg, 8192000); /* freqPeriod (Hz/us) */
+ ubx_payload_put_le32(msg, 8192000); /* freqPeriodLoc (Hz/us) */
+ ubx_payload_put_le32(msg, 0x80000000); /* pulseLenRation:
+ 1/2^-32 (us() */
+ ubx_payload_put_le32(msg, 0x80000000); /* pulseLenRationLock:
+ 1/2^-32 (us() */
+ ubx_payload_put_le32(msg, 0); /* userConfigDelay (ns) */
+ ubx_payload_put_le32(msg, (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3));
+ /* flags: bits 0, 1 and 3. */
+ ubx_payload_stop(msg);
+
+ int i;
+ for (i=0; i<msg->len; i++)
+ printf("\\x%.2x", 0xff & msg->data[i]);
+ printf("\n");
+
+ if (osmo_rs232_write(r, msg) < 0) {
+ LOGP(DRS232TEST, LOGL_ERROR, "cannot write to rs232\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static int kbd_cb(struct osmo_fd *fd, unsigned int what)
+{
+ char buf[1024];
+ int ret, val;
+
+ ret = read(STDIN_FILENO, buf, sizeof(buf));
+ if (ret < 0) {
+ LOGP(DRS232TEST, LOGL_ERROR, "cannot write to read from "
+ "keyboard\n");
+ exit(EXIT_FAILURE);
+ }
+
+ val = atoi(buf);
+ switch(val) {
+ case 1:
+ printf("sending command PUBX to switch to UBX mode\n");
+ send_pubx();
+ break;
+ case 2:
+ printf("sending command TP5\n");
+ cfg_tp5();
+ break;
+ case 3:
+ printf("sending command CFG-PRT\n");
+ cfg_prt();
+ break;
+ default:
+ printf("wrong option: select 1, 2 or 3\n");
+ break;
+ }
+ return 0;
+}
+
+int main(void)
+{
+ struct osmo_fd *kbd_ofd;
+
+ tall_test = talloc_named_const(NULL, 1, "osmo_rs232_test");
+
+ osmo_init_logging(&osmo_rs232_test_log_info);
+ log_set_log_level(osmo_stderr_target, LOGL_NOTICE);
+
+ r = osmo_rs232_create(tall_test);
+ if (r == NULL) {
+ LOGP(DRS232TEST, LOGL_ERROR, "cannot create rs232 object\n");
+ exit(EXIT_FAILURE);
+ }
+ osmo_rs232_set_serial_port(r, "/dev/ttyACM0");
+ osmo_rs232_set_baudrate(r, 9600);
+ osmo_rs232_set_delay_us(r, 3330);
+ osmo_rs232_set_read_cb(r, read_cb);
+
+ if (osmo_rs232_open(r) < 0) {
+ LOGP(DRS232TEST, LOGL_ERROR, "cannot open rs232\n");
+ exit(EXIT_FAILURE);
+ }
+
+ LOGP(DRS232TEST, LOGL_NOTICE, "Entering main loop\n");
+
+ kbd_ofd = talloc_zero(tall_test, struct osmo_fd);
+ if (!kbd_ofd) {
+ LOGP(DRS232TEST, LOGL_ERROR, "OOM\n");
+ exit(EXIT_FAILURE);
+ }
+ kbd_ofd->fd = STDIN_FILENO;
+ kbd_ofd->when = BSC_FD_READ;
+ kbd_ofd->data = NULL;
+ kbd_ofd->cb = kbd_cb;
+ osmo_fd_register(kbd_ofd);
+
+ while(1) {
+ osmo_select_main(0);
+ }
+}
diff --git a/include/osmocom/netif/rs232.h b/include/osmocom/netif/rs232.h
new file mode 100644
index 0000000..30bc52b
--- /dev/null
+++ b/include/osmocom/netif/rs232.h
@@ -0,0 +1,21 @@
+#ifndef _OSMO_RS232_H_
+#define _OSMO_RS232_H_
+
+struct osmo_rs232;
+
+struct osmo_rs232 *osmo_rs232_create(void *ctx);
+
+void osmo_rs232_set_serial_port(struct osmo_rs232 *, const char *serial_port);
+void osmo_rs232_set_delay_us(struct osmo_rs232 *, int delay_us);
+void osmo_rs232_set_baudrate(struct osmo_rs232 *, int baudrate);
+void osmo_rs232_set_read_cb(struct osmo_rs232 *r, int (*read_cb)(struct osmo_rs232 *r));
+
+int osmo_rs232_open(struct osmo_rs232 *r);
+
+int osmo_rs232_read(struct osmo_rs232 *r, struct msgb *msg);
+int osmo_rs232_write(struct osmo_rs232 *r, struct msgb *msg);
+
+void osmo_rs232_close(struct osmo_rs232 *r);
+void osmo_rs232_destroy(struct osmo_rs232 *r);
+
+#endif /* _OSMO_RS232_H_ */
diff --git a/src/Makefile.am b/src/Makefile.am
index f29c40b..0a3d3db 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -15,5 +15,6 @@ libosmonetif_la_LIBADD = channel/libosmonetif-channel.la
libosmonetif_la_SOURCES = channel.c \
datagram.c \
ipa.c \
+ rs232.c \
rtp.c \
stream.c
diff --git a/src/rs232.c b/src/rs232.c
new file mode 100644
index 0000000..70aff46
--- /dev/null
+++ b/src/rs232.c
@@ -0,0 +1,272 @@
+/* T-Link interface using POSIX serial port */
+
+/* (C) 2008-2011 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * Authors: Harald Welte <laforge@gnumonks.org>
+ * Pablo Neira Ayuso <pablo@gnumonks.org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/abis/e1_input.h>
+
+struct osmo_rs232 {
+ struct osmo_fd ofd;
+ struct llist_head tx_queue;
+
+ struct {
+ int (*read)(struct osmo_rs232 *);
+ } cb;
+
+ /* sometimes we want to delay the transmission. */
+ struct osmo_timer_list tx_timer;
+
+ struct {
+ char serial_port[PATH_MAX];
+ int baudrate;
+ unsigned int delay_us;
+ } cfg;
+};
+
+void rs232_tx_timer_cb(void *ptr)
+{
+ struct osmo_rs232 *r = ptr;
+
+ /* we're again ready to transmit. */
+ r->ofd.when |= BSC_FD_WRITE;
+}
+
+static int handle_ser_write(struct osmo_fd *bfd)
+{
+ struct osmo_rs232 *r = bfd->data;
+ struct llist_head *lh;
+ struct msgb *msg;
+ int written;
+
+ LOGP(DLINP, LOGL_DEBUG, "writing data to rs232\n");
+
+ if (llist_empty(&r->tx_queue)) {
+ r->ofd.when &= ~BSC_FD_WRITE;
+ return 0;
+ }
+ lh = r->tx_queue.next;
+ llist_del(lh);
+ msg = llist_entry(lh, struct msgb, list);
+
+ written = write(bfd->fd, msg->data, msg->len);
+ if (written < msg->len) {
+ LOGP(DLINP, LOGL_ERROR, "rs232: short write\n");
+ msgb_free(msg);
+ return -1;
+ }
+ msgb_free(msg);
+
+ /* We've got more data to write, but we have to wait to make it. */
+ if (!llist_empty(&r->tx_queue) && r->cfg.delay_us) {
+ r->ofd.when &= ~BSC_FD_WRITE;
+ osmo_timer_schedule(&r->tx_timer, 0, r->cfg.delay_us);
+ }
+ return 0;
+}
+
+static int handle_ser_read(struct osmo_fd *bfd)
+{
+ struct osmo_rs232 *r = bfd->data;
+
+ LOGP(DLINP, LOGL_DEBUG, "data to be read in rs232\n");
+
+ if (r->cb.read)
+ r->cb.read(r);
+
+ return 0;
+}
+
+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;
+}
+
+struct osmo_rs232 *osmo_rs232_create(void *ctx)
+{
+ struct osmo_rs232 *r;
+
+ r = talloc_zero(ctx, struct osmo_rs232);
+ if (r == NULL)
+ return NULL;
+
+ INIT_LLIST_HEAD(&r->tx_queue);
+
+ return r;
+}
+
+void osmo_rs232_set_serial_port(struct osmo_rs232 *r, char *serial_port)
+{
+ strncpy(r->cfg.serial_port, serial_port, PATH_MAX);
+ r->cfg.serial_port[PATH_MAX-1] = '\0';
+}
+
+void osmo_rs232_set_baudrate(struct osmo_rs232 *r, int baudrate)
+{
+ r->cfg.baudrate = baudrate;
+}
+
+void osmo_rs232_set_delay_us(struct osmo_rs232 *r, int delay_us)
+{
+ r->cfg.delay_us = delay_us;
+}
+
+void osmo_rs232_set_read_cb(struct osmo_rs232 *r,
+ int (*read_cb)(struct osmo_rs232 *r))
+{
+ r->cb.read = read_cb;
+}
+
+/* XXX: Better use TIOCGSERIAL / TIOCSSERIAL to allow setting non-standard. */
+static struct baudrate2termbits {
+ int rate;
+ int def;
+} baudrate2termbits[] = {
+ { 9600, B9600 },
+ { 19200, B19200 },
+ { 38400, B38400 },
+ { 115200, B1152000 },
+ { -1, -1 },
+};
+
+int osmo_rs232_open(struct osmo_rs232 *r)
+{
+ int rc, i, speed = 0;
+ struct osmo_fd *bfd = &r->ofd;
+ struct termios tio;
+
+ rc = open(r->cfg.serial_port, O_RDWR);
+ if (rc < 0) {
+ LOGP(DLINP, 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(DLINP, LOGL_ERROR, "rs232: tcgetattr says: %s",
+ strerror(errno));
+ return rc;
+ }
+ for (i=0; i<baudrate2termbits[i].rate; i++) {
+ if (baudrate2termbits[i].rate == -1)
+ break;
+
+ if (baudrate2termbits[i].rate == r->cfg.baudrate) {
+ speed = baudrate2termbits[i].def;
+ break;
+ }
+ }
+ if (speed == 0) {
+ close(rc);
+ bfd->fd = -1;
+ return -1;
+ }
+
+ cfsetispeed(&tio, speed);
+ cfsetospeed(&tio, speed);
+ 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(DLINP, LOGL_ERROR, "rs232: tcsetattr says: %s",
+ strerror(errno));
+ return rc;
+ }
+
+ bfd->when = BSC_FD_READ;
+ bfd->cb = serial_fd_cb;
+ bfd->data = r;
+
+ rc = osmo_fd_register(bfd);
+ if (rc < 0) {
+ close(bfd->fd);
+ LOGP(DLINP, LOGL_ERROR, "rs232: could not register FD: %s\n",
+ strerror(rc));
+ return rc;
+ }
+
+ if (r->cfg.delay_us) {
+ r->tx_timer.cb = rs232_tx_timer_cb;
+ r->tx_timer.data = r;
+ }
+ return 0;
+}
+
+int osmo_rs232_read(struct osmo_rs232 *r, struct msgb *msg)
+{
+ int ret;
+
+ ret = read(r->ofd.fd, msg->data, msg->data_len);
+ if (ret < 0) {
+ LOGP(DLINP, LOGL_ERROR, "read error: %s\n", strerror(errno));
+ return -EIO;
+ }
+ msgb_put(msg, ret);
+ return ret;
+}
+
+void osmo_rs232_write(struct osmo_rs232 *r, struct msgb *msg)
+{
+ msgb_enqueue(&r->tx_queue, msg);
+ r->ofd.when |= BSC_FD_WRITE;
+}
+
+void osmo_rs232_close(struct osmo_rs232 *r)
+{
+ close(r->ofd.fd);
+ r->ofd.fd = -1;
+}
+
+void osmo_rs232_destroy(struct osmo_rs232 *r)
+{
+ talloc_free(r);
+}