Review at  https://gerrit.osmocom.org/6682

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
---
M src/host/trxcon/Makefile.am
A src/host/trxcon/l1ctl_link.c
A src/host/trxcon/l1ctl_link.h
M src/host/trxcon/logging.c
M src/host/trxcon/logging.h
M src/host/trxcon/trxcon.c
6 files changed, 309 insertions(+), 7 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/82/6682/1

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 @@
 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 <t...@246tnt.com>
+ * (C) 2016-2017 by Vadim Yanitskiy <axilira...@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 @@
                .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 <axilira...@gmail.com>\n" \
@@ -49,9 +50,12 @@
        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 @@
        /* 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 @@
                osmo_select_main(0);
 
 exit:
+       /* Close active connections */
+       l1ctl_link_shutdown(app_data.l1l);
+
        /* Make Valgrind happy */
        log_fini();
        talloc_free(tall_trx_ctx);

-- 
To view, visit https://gerrit.osmocom.org/6682
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I58d069bcc7742b42c0bf95e52063933bf2c352ff
Gerrit-PatchSet: 1
Gerrit-Project: osmocom-bb
Gerrit-Branch: master
Gerrit-Owner: Harald Welte <lafo...@gnumonks.org>

Reply via email to