summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVadim Yanitskiy <axilirator@gmail.com>2016-07-22 22:57:50 +0600
committerVadim Yanitskiy <axilirator@gmail.com>2017-10-23 22:05:49 +0330
commite2ddfadc5fec6d4c28c2acfe70cebc25fc15fa5e (patch)
tree5f3d491a7d1c5257092744fdbf2ed96dcc327e02
parent03265a13ef5110a00e3a8be8a74aeee9d76a9832 (diff)
host/trxcon: initial release of L1CTL interface
There are two sides of the 'OsmocomBB <-> SDR' bridge. One of them is the L1CTL interface, which is used by existing layer23 applications to drive GSM L1. Exactly this interface is provided by the osmocon application, but instead of forwarding messages between both host software and firmware we need to handle incoming messages from layer23 applications, perform some GSM L1 specific conversations (coding, mapping, interleaving, etc.), then finally forward them to transceiver through the scheduler. And vice versa. This code is just a basic implementation of UNIX socket handlers, so currently we can only accept and drop connections from layer23 applications. Change-Id: I58d069bcc7742b42c0bf95e52063933bf2c352ff
-rw-r--r--src/host/trxcon/Makefile.am1
-rw-r--r--src/host/trxcon/l1ctl_link.c266
-rw-r--r--src/host/trxcon/l1ctl_link.h19
-rw-r--r--src/host/trxcon/logging.c6
-rw-r--r--src/host/trxcon/logging.h5
-rw-r--r--src/host/trxcon/trxcon.c19
6 files changed, 309 insertions, 7 deletions
diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am
index 00869d9..d7c26d4 100644
--- a/src/host/trxcon/Makefile.am
+++ b/src/host/trxcon/Makefile.am
@@ -20,6 +20,7 @@ AM_CFLAGS = \
bin_PROGRAMS = trxcon
trxcon_SOURCES = \
+ l1ctl_link.c \
logging.c \
trxcon.c \
$(NULL)
diff --git a/src/host/trxcon/l1ctl_link.c b/src/host/trxcon/l1ctl_link.c
new file mode 100644
index 0000000..e52950f
--- /dev/null
+++ b/src/host/trxcon/l1ctl_link.c
@@ -0,0 +1,266 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * GSM L1 control socket (/tmp/osmocom_l2) handlers
+ *
+ * (C) 2013 by Sylvain Munaut <tnt@246tNt.com>
+ * (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
+ *
+ * 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 <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/write_queue.h>
+
+#include "logging.h"
+#include "l1ctl_link.h"
+
+extern void *tall_trx_ctx;
+
+static int l1ctl_link_read_cb(struct osmo_fd *bfd)
+{
+ struct l1ctl_link *l1l = (struct l1ctl_link *) bfd->data;
+ struct msgb *msg;
+ uint16_t len;
+ int rc;
+
+ /* Allocate a new msg */
+ msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_HEADROOM,
+ L1CTL_HEADROOM, "L1CTL");
+ if (!msg) {
+ fprintf(stderr, "Failed to allocate msg\n");
+ return -ENOMEM;
+ }
+
+ /* Attempt to read from socket */
+ rc = read(bfd->fd, &len, sizeof(len));
+ if (rc < sizeof(len)) {
+ LOGP(DL1C, LOGL_NOTICE, "L1CTL has lost connection\n");
+ msgb_free(msg);
+ if (rc >= 0)
+ rc = -EIO;
+ l1ctl_link_close_conn(l1l);
+ return rc;
+ }
+
+ /* Check message length */
+ len = ntohs(len);
+ if (len > L1CTL_LENGTH) {
+ LOGP(DL1C, LOGL_ERROR, "Length is too big: %u\n", len);
+ msgb_free(msg);
+ return -EINVAL;
+ }
+
+ msg->l1h = msgb_put(msg, len);
+ rc = read(bfd->fd, msg->l1h, msgb_l1len(msg));
+ if (rc != len) {
+ LOGP(DL1C, LOGL_ERROR, "Can not read data: len=%d < rc=%d: "
+ "%s\n", len, rc, strerror(errno));
+ msgb_free(msg);
+ return rc;
+ }
+
+ /* Debug print */
+ LOGP(DL1C, LOGL_DEBUG, "RX: '%s'\n",
+ osmo_hexdump(msg->data, msg->len));
+
+ /* TODO: call L1CTL handler here */
+ msgb_free(msg);
+
+ return 0;
+}
+
+static int l1ctl_link_write_cb(struct osmo_fd *bfd, struct msgb *msg)
+{
+ int len;
+
+ if (bfd->fd <= 0)
+ return -EINVAL;
+
+ len = write(bfd->fd, msg->data, msg->len);
+ if (len != msg->len) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to write data: "
+ "written (%d) < msg_len (%d)\n", len, msg->len);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Connection handler */
+static int l1ctl_link_accept(struct osmo_fd *bfd, unsigned int flags)
+{
+ struct l1ctl_link *l1l = (struct l1ctl_link *) bfd->data;
+ struct osmo_fd *conn_bfd = &l1l->wq.bfd;
+ struct sockaddr_un un_addr;
+ socklen_t len;
+ int cfd;
+
+ len = sizeof(un_addr);
+ cfd = accept(bfd->fd, (struct sockaddr *) &un_addr, &len);
+ if (cfd < 0) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to accept a new connection\n");
+ return -1;
+ }
+
+ /* Check if we already have an active connection */
+ if (conn_bfd->fd != -1) {
+ LOGP(DL1C, LOGL_NOTICE, "A new connection rejected: "
+ "we already have another active\n");
+ close(cfd);
+ return 0;
+ }
+
+ osmo_wqueue_init(&l1l->wq, 100);
+ INIT_LLIST_HEAD(&conn_bfd->list);
+
+ l1l->wq.write_cb = l1ctl_link_write_cb;
+ l1l->wq.read_cb = l1ctl_link_read_cb;
+ conn_bfd->when = BSC_FD_READ;
+ conn_bfd->data = l1l;
+ conn_bfd->fd = cfd;
+
+ if (osmo_fd_register(conn_bfd) != 0) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to register new connection fd\n");
+ close(conn_bfd->fd);
+ conn_bfd->fd = -1;
+ return -1;
+ }
+
+ /* TODO: switch the bridge to CONNECTED state */
+ LOGP(DL1C, LOGL_NOTICE, "L1CTL has a new connection\n");
+
+ return 0;
+}
+
+int l1ctl_link_send(struct l1ctl_link *l1l, struct msgb *msg)
+{
+ uint16_t *len;
+
+ /* Debug print */
+ LOGP(DL1C, LOGL_DEBUG, "TX: '%s'\n",
+ osmo_hexdump(msg->data, msg->len));
+
+ if (msg->l1h != msg->data)
+ LOGP(DL1C, LOGL_INFO, "Message L1 header != Message Data\n");
+
+ /* Prepend 16-bit length before sending */
+ len = (uint16_t *) msgb_push(msg, sizeof(*len));
+ *len = htons(msg->len - sizeof(*len));
+
+ if (osmo_wqueue_enqueue(&l1l->wq, msg) != 0) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to enqueue msg!\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int l1ctl_link_close_conn(struct l1ctl_link *l1l)
+{
+ struct osmo_fd *conn_bfd = &l1l->wq.bfd;
+
+ if (conn_bfd->fd <= 0)
+ return -EINVAL;
+
+ /* Close connection socket */
+ osmo_fd_unregister(conn_bfd);
+ close(conn_bfd->fd);
+ conn_bfd->fd = -1;
+
+ /* Clear pending messages */
+ osmo_wqueue_clear(&l1l->wq);
+
+ /* TODO: switch the bridge to IDLE state */
+ return 0;
+}
+
+int l1ctl_link_init(struct l1ctl_link **l1l, const char *sock_path)
+{
+ struct l1ctl_link *l1l_new;
+ struct osmo_fd *bfd;
+ int rc;
+
+ LOGP(DL1C, LOGL_NOTICE, "Init L1CTL link (%s)\n", sock_path);
+
+ l1l_new = talloc_zero(tall_trx_ctx, struct l1ctl_link);
+ if (!l1l_new) {
+ fprintf(stderr, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ /* Create a socket and bind handlers */
+ bfd = &l1l_new->listen_bfd;
+ rc = osmo_sock_unix_init_ofd(bfd, SOCK_STREAM, 0, sock_path,
+ OSMO_SOCK_F_BIND);
+ if (rc < 0) {
+ fprintf(stderr, "Could not create UNIX socket: %s\n",
+ strerror(errno));
+ talloc_free(l1l_new);
+ return rc;
+ }
+
+ bfd->cb = l1ctl_link_accept;
+ bfd->when = BSC_FD_READ;
+ bfd->data = l1l_new;
+
+ /**
+ * To be able to accept first connection and
+ * drop others, it should be set to -1
+ */
+ l1l_new->wq.bfd.fd = -1;
+
+ *l1l = l1l_new;
+
+ return 0;
+}
+
+void l1ctl_link_shutdown(struct l1ctl_link *l1l)
+{
+ struct osmo_fd *listen_bfd;
+
+ LOGP(DL1C, LOGL_NOTICE, "Shutdown L1CTL link\n");
+
+ listen_bfd = &l1l->listen_bfd;
+
+ /* Check if we have an established connection */
+ if (l1l->wq.bfd.fd != -1)
+ l1ctl_link_close_conn(l1l);
+
+ /* Unbind listening socket */
+ if (listen_bfd->fd != -1) {
+ osmo_fd_unregister(listen_bfd);
+ close(listen_bfd->fd);
+ listen_bfd->fd = -1;
+ }
+
+ talloc_free(l1l);
+}
diff --git a/src/host/trxcon/l1ctl_link.h b/src/host/trxcon/l1ctl_link.h
new file mode 100644
index 0000000..417dc75
--- /dev/null
+++ b/src/host/trxcon/l1ctl_link.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <osmocom/core/write_queue.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/msgb.h>
+
+#define L1CTL_LENGTH 256
+#define L1CTL_HEADROOM 32
+
+struct l1ctl_link {
+ struct osmo_fd listen_bfd;
+ struct osmo_wqueue wq;
+};
+
+int l1ctl_link_init(struct l1ctl_link **l1l, const char *sock_path);
+void l1ctl_link_shutdown(struct l1ctl_link *l1l);
+
+int l1ctl_link_send(struct l1ctl_link *l1l, struct msgb *msg);
+int l1ctl_link_close_conn(struct l1ctl_link *l1l);
diff --git a/src/host/trxcon/logging.c b/src/host/trxcon/logging.c
index 136cc76..734d138 100644
--- a/src/host/trxcon/logging.c
+++ b/src/host/trxcon/logging.c
@@ -34,6 +34,12 @@ static struct log_info_cat trx_log_info_cat[] = {
.color = "\033[1;35m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
+ [DL1C] = {
+ .name = "DL1C",
+ .description = "Layer 1 control interface",
+ .color = "\033[1;31m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
};
static const struct log_info trx_log_info = {
diff --git a/src/host/trxcon/logging.h b/src/host/trxcon/logging.h
index 4149e4f..049f322 100644
--- a/src/host/trxcon/logging.h
+++ b/src/host/trxcon/logging.h
@@ -2,10 +2,11 @@
#include <osmocom/core/logging.h>
-#define DEBUG_DEFAULT "DAPP"
+#define DEBUG_DEFAULT "DAPP:DL1C"
enum {
- DAPP
+ DAPP,
+ DL1C,
};
int trx_log_init(const char *category_mask);
diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c
index 03664ca..4ad8a0c 100644
--- a/src/host/trxcon/trxcon.c
+++ b/src/host/trxcon/trxcon.c
@@ -36,6 +36,7 @@
#include <osmocom/core/application.h>
#include "logging.h"
+#include "l1ctl_link.h"
#define COPYRIGHT \
"Copyright (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>\n" \
@@ -49,9 +50,12 @@ static struct {
int daemonize;
int quit;
+ /* L1CTL specific */
+ struct l1ctl_link *l1l;
+ const char *bind_socket;
+
const char *trx_ip;
uint16_t trx_base_port;
- const char *bind_socket;
} app_data;
void *tall_trx_ctx = NULL;
@@ -168,10 +172,12 @@ int main(int argc, char **argv)
/* Init logging system */
trx_log_init(app_data.debug_mask);
- /* Currently nothing to do */
- print_usage(argv[0]);
- print_help();
- goto exit;
+ /* Init L1CTL server */
+ rc = l1ctl_link_init(&app_data.l1l, app_data.bind_socket);
+ if (rc)
+ goto exit;
+
+ LOGP(DAPP, LOGL_NOTICE, "Init complete\n");
if (app_data.daemonize) {
rc = osmo_daemonize();
@@ -185,6 +191,9 @@ int main(int argc, char **argv)
osmo_select_main(0);
exit:
+ /* Close active connections */
+ l1ctl_link_shutdown(app_data.l1l);
+
/* Make Valgrind happy */
log_fini();
talloc_free(tall_trx_ctx);