summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSylvain Munaut <tnt@246tNt.com>2014-06-09 20:02:23 +0200
committerSylvain Munaut <tnt@246tNt.com>2014-06-15 19:31:12 +0200
commit31d3354b01e184e697df29d52da3066acd35d6b2 (patch)
tree9806819fc893e3b13c2b2ab1c98f22c3b54b00fb
parent9ec3f72277f4f3c96ac365de3022f4d181ba8c9f (diff)
first hack that does something usefulsylvain/ms-sdr
-rw-r--r--src/host/layer23/configure.ac6
-rw-r--r--src/host/layer23/src/Makefile.am4
-rw-r--r--src/host/layer23/src/transceiver/l1ctl.c12
-rw-r--r--src/host/layer23/src/transceiver/l1ctl_link.c136
-rw-r--r--src/host/layer23/src/transceiver/l1ctl_link.h22
-rw-r--r--src/host/layer23/src/transceiver/trx.c5
-rw-r--r--src/host/layer23/src/transceiver_ms/Makefile.am6
-rw-r--r--src/host/layer23/src/transceiver_ms/app.h50
-rw-r--r--src/host/layer23/src/transceiver_ms/l1ctl.c294
-rw-r--r--src/host/layer23/src/transceiver_ms/l1ctl.h31
l---------src/host/layer23/src/transceiver_ms/l1ctl_link.c1
l---------src/host/layer23/src/transceiver_ms/l1ctl_link.h1
-rw-r--r--src/host/layer23/src/transceiver_ms/main.c76
-rw-r--r--src/host/layer23/src/transceiver_ms/trx.c298
-rw-r--r--src/host/layer23/src/transceiver_ms/trx.h45
15 files changed, 951 insertions, 36 deletions
diff --git a/src/host/layer23/configure.ac b/src/host/layer23/configure.ac
index 1ff1bb7..8a34d9d 100644
--- a/src/host/layer23/configure.ac
+++ b/src/host/layer23/configure.ac
@@ -29,6 +29,11 @@ if test "$osmo_ac_build_transceiver" = "yes" ; then
fi
AM_CONDITIONAL(BUILD_TRANSCEIVER, test "x$osmo_ac_build_transceiver" = "xyes")
+dnl Optional transceiver_ms
+AC_ARG_ENABLE([transceiver-ms], [AS_HELP_STRING([--enable-transceiver-ms], [Build the MS transceiver application])],
+ [osmo_ac_build_transceiver_ms="$enableval"])
+AM_CONDITIONAL(BUILD_TRANSCEIVER_MS, test "x$osmo_ac_build_transceiver_ms" = "xyes")
+
dnl checks for header files
AC_HEADER_STDC
@@ -40,6 +45,7 @@ AC_OUTPUT(
src/misc/Makefile
src/mobile/Makefile
src/transceiver/Makefile
+ src/transceiver_ms/Makefile
include/Makefile
include/osmocom/Makefile
include/osmocom/bb/Makefile
diff --git a/src/host/layer23/src/Makefile.am b/src/host/layer23/src/Makefile.am
index c46548c..50b333f 100644
--- a/src/host/layer23/src/Makefile.am
+++ b/src/host/layer23/src/Makefile.am
@@ -3,3 +3,7 @@ SUBDIRS = common misc mobile
if BUILD_TRANSCEIVER
SUBDIRS += transceiver
endif
+
+if BUILD_TRANSCEIVER_MS
+SUBDIRS += transceiver_ms
+endif
diff --git a/src/host/layer23/src/transceiver/l1ctl.c b/src/host/layer23/src/transceiver/l1ctl.c
index e4cda24..00308bf 100644
--- a/src/host/layer23/src/transceiver/l1ctl.c
+++ b/src/host/layer23/src/transceiver/l1ctl.c
@@ -175,7 +175,7 @@ _l1ctl_rx_bts_burst_nb_ind(struct app_state *as, struct msgb *msg)
if (msgb_l1len(msg) < sizeof(*bi)) {
LOGP(DL1C, LOGL_ERROR, "MSG too short Burst NB Ind: %u\n",
- msgb_l2len(msg));
+ msgb_l1len(msg));
rc = -EINVAL;
goto exit;
}
@@ -214,7 +214,7 @@ _l1ctl_rx_bts_burst_ab_ind(struct app_state *as, struct msgb *msg)
if (msgb_l1len(msg) < sizeof(*bi)) {
LOGP(DL1C, LOGL_ERROR, "MSG too short Burst AB Ind: %u\n",
- msgb_l2len(msg));
+ msgb_l1len(msg));
rc = -EINVAL;
goto exit;
}
@@ -245,14 +245,14 @@ _l1ctl_rx_data_ind(struct app_state *as, struct msgb *msg)
if (msgb_l1len(msg) < sizeof(*dl)) {
LOGP(DL1C, LOGL_ERROR, "Short Layer2 message: %u\n",
- msgb_l2len(msg));
+ msgb_l1len(msg));
rc = -EINVAL;
goto exit;
}
if (msgb_l2len(msg) < sizeof(*di)) {
LOGP(DL1C, LOGL_ERROR, "MSG too short Data Ind: %u\n",
- msgb_l3len(msg));
+ msgb_l2len(msg));
rc = -EINVAL;
goto exit;
}
@@ -289,14 +289,14 @@ _l1ctl_rx_fbsb_conf(struct app_state *as, struct msgb *msg)
if (msgb_l1len(msg) < sizeof(*dl)) {
LOGP(DL1C, LOGL_ERROR, "Short Layer2 message: %u\n",
- msgb_l2len(msg));
+ msgb_l1len(msg));
rc = -EINVAL;
goto exit;
}
if (msgb_l2len(msg) < sizeof(*sc)) {
LOGP(DL1C, LOGL_ERROR, "MSG too short FBSB Conf: %u\n",
- msgb_l3len(msg));
+ msgb_l2len(msg));
rc = -EINVAL;
goto exit;
}
diff --git a/src/host/layer23/src/transceiver/l1ctl_link.c b/src/host/layer23/src/transceiver/l1ctl_link.c
index 5ec83e4..6060c82 100644
--- a/src/host/layer23/src/transceiver/l1ctl_link.c
+++ b/src/host/layer23/src/transceiver/l1ctl_link.c
@@ -29,6 +29,8 @@
#include <sys/un.h>
#include <arpa/inet.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/talloc.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/bb/common/logging.h>
@@ -103,44 +105,29 @@ _l1l_write(struct osmo_fd *fd, struct msgb *msg)
int
l1l_open(struct l1ctl_link *l1l,
- const char *path, l1ctl_cb_t cb, void *cb_data)
+ const char *path, l1ctl_cb_t cb, void *cb_data)
{
- int rc, fd;
- struct sockaddr_un local;
+ int rc;
+
+ memset(l1l, 0x00, sizeof(struct l1ctl_link));
l1l->cb = cb;
l1l->cb_data = cb_data;
- fd = socket(AF_UNIX, SOCK_STREAM, 0);
- if (fd < 0) {
- LOGP(DL1C, LOGL_ERROR, "Failed to create unix domain socket.\n");
- return fd;
- }
-
- l1l->wq.bfd.fd = fd;
-
- local.sun_family = AF_UNIX;
- strncpy(local.sun_path, path, sizeof(local.sun_path));
- local.sun_path[sizeof(local.sun_path) - 1] = '\0';
-
- rc = connect(fd, (struct sockaddr *) &local, sizeof(local));
- if (rc < 0) {
- LOGP(DL1C, LOGL_ERROR, "Failed to connect to '%s': %s\n",
- local.sun_path, strerror(errno));
- close(fd);
- return rc;
- }
-
osmo_wqueue_init(&l1l->wq, 100);
l1l->wq.bfd.data = l1l;
- l1l->wq.bfd.when = BSC_FD_READ;
l1l->wq.read_cb = _l1l_read;
l1l->wq.write_cb = _l1l_write;
- rc = osmo_fd_register(&l1l->wq.bfd);
- if (rc != 0) {
- LOGP(DL1C, LOGL_ERROR, "Failed to register fd.\n");
- close(fd);
+ rc = osmo_sock_unix_init_ofd(
+ &l1l->wq.bfd,
+ SOCK_STREAM, 0,
+ path,
+ OSMO_SOCK_F_CONNECT
+ );
+
+ if (rc < 0) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to create and init unix domain socket.\n");
return rc;
}
@@ -160,6 +147,9 @@ l1l_close(struct l1ctl_link *l1l)
osmo_wqueue_clear(&l1l->wq);
+ if (l1l->to_free)
+ talloc_free(l1l);
+
return 0;
}
@@ -185,3 +175,93 @@ l1l_send(struct l1ctl_link *l1l, struct msgb *msg)
return 0;
}
+
+
+static int
+_l1l_accept(struct osmo_fd *fd, unsigned int flags)
+{
+ struct l1ctl_server *l1s = fd->data;
+ struct l1ctl_link *l1l;
+ struct sockaddr_un un_addr;
+ socklen_t len;
+ int cfd, rc;
+
+ len = sizeof(un_addr);
+ cfd = accept(fd->fd, (struct sockaddr *) &un_addr, &len);
+ if (cfd < 0) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to accept a new L1CTL connection.\n");
+ return cfd;
+ }
+
+ l1l = talloc_zero(NULL, struct l1ctl_link);
+ if (!l1l) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to allocate a new L1CTL connection.\n");
+ return -ENOMEM;
+ }
+
+ l1l->to_free = 1;
+
+ osmo_wqueue_init(&l1l->wq, 100);
+ l1l->wq.bfd.fd = cfd;
+ l1l->wq.bfd.data = l1l;
+ l1l->wq.bfd.when = BSC_FD_READ;
+ l1l->wq.read_cb = _l1l_read;
+ l1l->wq.write_cb = _l1l_write;
+
+ INIT_LLIST_HEAD(&l1l->wq.bfd.list);
+
+ rc = l1s->cb(l1s->cb_data, l1l);
+ if (rc)
+ goto error;
+
+ if (!l1l->cb || !l1l->cb_data) {
+ LOGP(DL1C, LOGL_NOTICE, "New L1CTL callback didn't set message callback !\n");
+ }
+
+ rc = osmo_fd_register(&l1l->wq.bfd);
+ if (rc) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to register fd of new L1CTL connection.\n");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ if (l1l)
+ l1l_close(l1l);
+
+ return rc;
+}
+
+int
+l1l_start_server(struct l1ctl_server *l1s, const char *path,
+ l1ctl_server_cb_t cb, void *cb_data)
+{
+ int rc;
+
+ memset(l1s, 0x00, sizeof(struct l1ctl_server));
+
+ l1s->cb = cb;
+ l1s->cb_data = cb_data;
+
+ l1s->bfd.cb = _l1l_accept;
+ l1s->bfd.data = l1s;
+
+ rc = osmo_sock_unix_init_ofd(
+ &l1s->bfd,
+ SOCK_STREAM, 0,
+ path,
+ OSMO_SOCK_F_BIND
+ );
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+void
+l1l_stop_server(struct l1ctl_server *l1s)
+{
+ /* FIXME */
+
+}
diff --git a/src/host/layer23/src/transceiver/l1ctl_link.h b/src/host/layer23/src/transceiver/l1ctl_link.h
index e53bd82..8f96a0b 100644
--- a/src/host/layer23/src/transceiver/l1ctl_link.h
+++ b/src/host/layer23/src/transceiver/l1ctl_link.h
@@ -25,9 +25,12 @@
#define __TRX_L1CTL_LINK_H__
+#include <osmocom/core/socket.h>
#include <osmocom/core/write_queue.h>
+/* Link */
+
typedef int (*l1ctl_cb_t)(void *data, struct msgb *msgb);
struct l1ctl_link
@@ -36,6 +39,8 @@ struct l1ctl_link
l1ctl_cb_t cb;
void *cb_data;
+
+ int to_free;
};
@@ -47,4 +52,21 @@ int l1l_close(struct l1ctl_link *l1l);
int l1l_send(struct l1ctl_link *l1l, struct msgb *msg);
+/* Server */
+
+typedef int (*l1ctl_server_cb_t)(void *data, struct l1ctl_link *l1l);
+
+struct l1ctl_server
+{
+ struct osmo_fd bfd;
+
+ l1ctl_server_cb_t cb;
+ void *cb_data;
+};
+
+int l1l_start_server(struct l1ctl_server *l1s, const char *path,
+ l1ctl_server_cb_t cb, void *cb_data);
+void l1l_stop_server (struct l1ctl_server *l1s);
+
+
#endif /* __TRX_L1CTL_LINK_H__ */
diff --git a/src/host/layer23/src/transceiver/trx.c b/src/host/layer23/src/transceiver/trx.c
index 9149b7c..7d982b3 100644
--- a/src/host/layer23/src/transceiver/trx.c
+++ b/src/host/layer23/src/transceiver/trx.c
@@ -56,10 +56,10 @@ static int _trx_data_read_cb(struct osmo_fd *ofd, unsigned int what);
/* ------------------------------------------------------------------------ */
-/* Init */
+/* Init / Cleanup */
/* ------------------------------------------------------------------------ */
-int
+static int
_trx_udp_init(struct trx *trx,
struct osmo_fd *ofd, const char *addr, uint16_t port,
int (*cb)(struct osmo_fd *fd, unsigned int what))
@@ -150,6 +150,7 @@ trx_alloc(const char *addr, uint16_t base_port, struct l1ctl_link *l1l)
return trx;
err:
+ /* FIXME */
return NULL;
}
diff --git a/src/host/layer23/src/transceiver_ms/Makefile.am b/src/host/layer23/src/transceiver_ms/Makefile.am
new file mode 100644
index 0000000..88d7a7b
--- /dev/null
+++ b/src/host/layer23/src/transceiver_ms/Makefile.am
@@ -0,0 +1,6 @@
+AM_CFLAGS = -Wall $(all_includes) -I$(top_srcdir)/include $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBSOSMODSP_CFLAGS)
+LDADD = ../common/liblayer23.a $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBOSMODSP_LIBS)
+
+bin_PROGRAMS = transceiver-ms
+
+transceiver_ms_SOURCES = main.c l1ctl.c l1ctl_link.c trx.c ../common/logging.c
diff --git a/src/host/layer23/src/transceiver_ms/app.h b/src/host/layer23/src/transceiver_ms/app.h
new file mode 100644
index 0000000..0fca124
--- /dev/null
+++ b/src/host/layer23/src/transceiver_ms/app.h
@@ -0,0 +1,50 @@
+/*
+ * app.h
+ *
+ * Application state / defines
+ *
+ * Copyright (C) 2014 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * 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/>.
+ */
+
+#ifndef __TRXMS_APP_H__
+#define __TRXMS_APP_H__
+
+
+#include <stdint.h>
+
+#include "l1ctl_link.h"
+
+
+struct log_target;
+struct trx;
+
+struct app_state
+{
+ /* Logging */
+ struct log_target *stderr_target;
+
+ /* L1CTL server & active link */
+ struct l1ctl_server l1s;
+ struct l1ctl_link *l1l;
+
+ /* TRX link to Transceiver */
+ struct trx *trx;
+};
+
+
+#endif /* __TRXMS_APP_H__ */
diff --git a/src/host/layer23/src/transceiver_ms/l1ctl.c b/src/host/layer23/src/transceiver_ms/l1ctl.c
new file mode 100644
index 0000000..08f30ef
--- /dev/null
+++ b/src/host/layer23/src/transceiver_ms/l1ctl.c
@@ -0,0 +1,294 @@
+/*
+ * l1ctl.c
+ *
+ * L1CTL interface
+ *
+ * Copyright (C) 2014 Sylvain Munaut <tnt@246tNt.com>
+ *
+ *
+ * All Rights Reserved
+ *
+ * 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 <errno.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/gsm/gsm_utils.h>
+
+#include <osmocom/bb/common/logging.h>
+#include <l1ctl_proto.h>
+
+#include "app.h"
+#include "l1ctl.h"
+#include "l1ctl_link.h"
+#include "trx.h"
+
+
+/* ------------------------------------------------------------------------ */
+/* L1CTL exported API */
+/* ------------------------------------------------------------------------ */
+
+static struct msgb *
+_l1ctl_alloc(uint8_t msg_type)
+{
+ struct l1ctl_hdr *l1h;
+ struct msgb *msg = msgb_alloc_headroom(256, 4, "osmo_l1");
+
+ if (!msg) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory.\n");
+ return NULL;
+ }
+
+ msg->l1h = msgb_put(msg, sizeof(*l1h));
+ l1h = (struct l1ctl_hdr *) msg->l1h;
+ l1h->msg_type = msg_type;
+
+ return msg;
+}
+
+int
+l1ctl_tx_pm_conf(struct l1ctl_link *l1l, uint16_t band_arfcn, int dbm, int last)
+{
+ struct msgb *msg;
+ struct l1ctl_pm_conf *pmc;
+
+ msg = _l1ctl_alloc(L1CTL_PM_CONF);
+ if (!msg)
+ return -ENOMEM;
+
+ LOGP(DL1C, LOGL_DEBUG, "Tx PM Conf (%s %d = %d dBm)\n",
+ gsm_band_name(gsm_arfcn2band(band_arfcn)),
+ band_arfcn &~ ARFCN_FLAG_MASK,
+ dbm
+ );
+
+ pmc = (struct l1ctl_pm_conf *) msgb_put(msg, sizeof(*pmc));
+ pmc->band_arfcn = htons(band_arfcn);
+ pmc->pm[0] = dbm2rxlev(dbm);
+ pmc->pm[1] = 0;
+
+ if (last) {
+ struct l1ctl_hdr *l1h = ( struct l1ctl_hdr *)msg->l1h;
+ l1h->flags |= L1CTL_F_DONE;
+ }
+
+ return l1l_send(l1l, msg);
+}
+
+int
+l1ctl_tx_reset_ind(struct l1ctl_link *l1l, uint8_t type)
+{
+ struct msgb *msg;
+ struct l1ctl_reset *res;
+
+ msg = _l1ctl_alloc(L1CTL_RESET_IND);
+ if (!msg)
+ return -ENOMEM;
+
+ LOGP(DL1C, LOGL_DEBUG, "Tx Reset Ind (%u)\n", type);
+
+ res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res));
+ res->type = type;
+
+ return l1l_send(l1l, msg);
+}
+
+int
+l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type)
+{
+ struct msgb *msg;
+ struct l1ctl_reset *res;
+
+ msg = _l1ctl_alloc(L1CTL_RESET_CONF);
+ if (!msg)
+ return -ENOMEM;
+
+ LOGP(DL1C, LOGL_DEBUG, "Tx Reset Conf (%u)\n", type);
+ res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res));
+ res->type = type;
+
+ return l1l_send(l1l, msg);
+}
+
+
+/* ------------------------------------------------------------------------ */
+/* L1CTL Receive handling */
+/* ------------------------------------------------------------------------ */
+
+static int
+_l1ctl_rx_fbsb_req(struct app_state *as, struct msgb *msg)
+{
+ struct l1ctl_fbsb_req *fbsb;
+ uint16_t band_arfcn;
+ int rc = 0;
+
+ /* Grab message */
+ fbsb = (struct l1ctl_fbsb_req *) msg->l1h;
+
+ if (msgb_l1len(msg) < sizeof(*fbsb)) {
+ LOGP(DL1C, LOGL_ERROR, "MSG too short FBSB Req: %u\n",
+ msgb_l1len(msg));
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ band_arfcn = ntohs(fbsb->band_arfcn);
+
+ LOGP(DL1C, LOGL_DEBUG, "Rx FBSB Req (%s %d)\n",
+ gsm_band_name(gsm_arfcn2band(band_arfcn)),
+ band_arfcn &~ ARFCN_FLAG_MASK
+ );
+
+ /* Send request to TRX */
+ trx_ctrl_send_cmd(as->trx, "TXTUNE", "%d",
+ (int)gsm_arfcn2freq10(band_arfcn, 1) * 100);
+ trx_ctrl_send_cmd(as->trx, "RXTUNE", "%d",
+ (int)gsm_arfcn2freq10(band_arfcn, 0) * 100);
+ trx_ctrl_send_cmd(as->trx, "POWERON", NULL);
+ trx_ctrl_send_cmd(as->trx, "SYNC", NULL);
+
+exit:
+ msgb_free(msg);
+
+ return rc;
+}
+
+static int
+_l1ctl_rx_pm_req(struct app_state *as, struct msgb *msg)
+{
+ struct l1ctl_pm_req *pmr;
+ uint16_t arfcn_start, arfcn_stop, arfcn;
+ int rc = 0;
+
+ /* Grab message */
+ pmr = (struct l1ctl_pm_req *) msg->l1h;
+
+ if (msgb_l1len(msg) < sizeof(*pmr)) {
+ LOGP(DL1C, LOGL_ERROR, "MSG too short PM Req: %u\n",
+ msgb_l1len(msg));
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ arfcn_start = ntohs(pmr->range.band_arfcn_from);
+ arfcn_stop = ntohs(pmr->range.band_arfcn_to);
+
+ LOGP(DL1C, LOGL_DEBUG, "Rx PM Req (%s: %d -> %d)\n",
+ gsm_band_name(gsm_arfcn2band(arfcn_start)),
+ arfcn_start &~ ARFCN_FLAG_MASK,
+ arfcn_stop &~ ARFCN_FLAG_MASK
+ );
+
+ /* Send fake responses */
+ for (arfcn=arfcn_start; arfcn<=arfcn_stop; arfcn++)
+ {
+ l1ctl_tx_pm_conf(as->l1l, arfcn, arfcn == 36 ? -60 : -120, arfcn == arfcn_stop);
+ }
+
+exit:
+ msgb_free(msg);
+
+ return rc;
+}
+
+static int
+_l1ctl_rx_reset_req(struct app_state *as, struct msgb *msg)
+{
+ struct l1ctl_reset *res;
+ int rc = 0;
+
+ /* Grab message */
+ res = (struct l1ctl_reset *) msg->l1h;
+
+ if (msgb_l1len(msg) < sizeof(*res)) {
+ LOGP(DL1C, LOGL_ERROR, "MSG too short Reset Req: %u\n",
+ msgb_l1len(msg));
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ LOGP(DL1C, LOGL_DEBUG, "Rx Reset Req (%u)\n", res->type);
+
+ /* Request power off */
+ trx_ctrl_send_cmd(as->trx, "POWEROFF", NULL);
+
+ /* Simply confirm */
+ rc = l1ctl_tx_reset_conf(as->l1l, res->type);
+
+exit:
+ msgb_free(msg);
+
+ return rc;
+}
+
+
+static int
+_l1ctl_recv(void *data, struct msgb *msg)
+{
+ struct app_state *as = data;
+ struct l1ctl_hdr *l1h;
+ int rc = 0;
+
+ /* move the l1 header pointer to point _BEHIND_ l1ctl_hdr,
+ as the l1ctl header is of no interest to subsequent code */
+ l1h = (struct l1ctl_hdr *) msg->l1h;
+ msg->l1h = l1h->data;
+
+ /* Act */
+ switch (l1h->msg_type) {
+ case L1CTL_FBSB_REQ:
+ rc = _l1ctl_rx_fbsb_req(as, msg);
+ break;
+ case L1CTL_PM_REQ:
+ rc = _l1ctl_rx_pm_req(as, msg);
+ break;
+ case L1CTL_RESET_REQ:
+ rc = _l1ctl_rx_reset_req(as, msg);
+ break;
+ default:
+ LOGP(DL1C, LOGL_ERROR, "Unknown MSG: %u\n", l1h->msg_type);
+ msgb_free(msg);
+ }
+
+ return rc;
+}
+
+int
+l1ctl_new_cb(void *data, struct l1ctl_link *l1l)
+{
+ struct app_state *as = data;
+
+ LOGP(DL1C, LOGL_INFO, "New L1CTL connection\n");
+
+ /* Close previous link */
+ if (as->l1l) {
+ LOGP(DL1C, LOGL_INFO, "Closing old L1CTL connection\n");
+ l1l_close(as->l1l);
+ }
+
+ /* Setup new one */
+ as->l1l = l1l;
+
+ l1l->cb = _l1ctl_recv;
+ l1l->cb_data = data;
+
+ /* Send reset ind */
+ l1ctl_tx_reset_ind(l1l, L1CTL_RES_T_BOOT);
+
+ /* Done */
+ return 0;
+}
diff --git a/src/host/layer23/src/transceiver_ms/l1ctl.h b/src/host/layer23/src/transceiver_ms/l1ctl.h
new file mode 100644
index 0000000..e5dd931
--- /dev/null
+++ b/src/host/layer23/src/transceiver_ms/l1ctl.h
@@ -0,0 +1,31 @@
+/*
+ * l1ctl.h
+ *
+ * L1CTL interface
+ *
+ * Copyright (C) 2014 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * 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/>.
+ */
+
+#ifndef __TRXMS_L1CTL_H__
+#define __TRXMS_L1CTL_H__
+
+struct l1ctl_link;
+
+int l1ctl_new_cb(void *data, struct l1ctl_link *l1l);
+
+#endif /* __TRXMS_L1CTL_H__ */
diff --git a/src/host/layer23/src/transceiver_ms/l1ctl_link.c b/src/host/layer23/src/transceiver_ms/l1ctl_link.c
new file mode 120000
index 0000000..b2a489b
--- /dev/null
+++ b/src/host/layer23/src/transceiver_ms/l1ctl_link.c
@@ -0,0 +1 @@
+../transceiver/l1ctl_link.c \ No newline at end of file
diff --git a/src/host/layer23/src/transceiver_ms/l1ctl_link.h b/src/host/layer23/src/transceiver_ms/l1ctl_link.h
new file mode 120000
index 0000000..12cdab9
--- /dev/null
+++ b/src/host/layer23/src/transceiver_ms/l1ctl_link.h
@@ -0,0 +1 @@
+../transceiver/l1ctl_link.h \ No newline at end of file
diff --git a/src/host/layer23/src/transceiver_ms/main.c b/src/host/layer23/src/transceiver_ms/main.c
new file mode 100644
index 0000000..1598ab2
--- /dev/null
+++ b/src/host/layer23/src/transceiver_ms/main.c
@@ -0,0 +1,76 @@
+/*
+ * main.c
+ *
+ * MS-side Transceiver main program
+ *
+ * Copyright (C) 2014 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * 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 <stdio.h>
+#include <string.h>
+
+#include <osmocom/core/talloc.h>
+
+#include <osmocom/bb/common/logging.h>
+
+#include "app.h"
+#include "l1ctl.h"
+#include "l1ctl_link.h"
+#include "trx.h"
+
+
+void *l23_ctx = NULL;
+
+
+int main(int argc, char *argv[])
+{
+ struct app_state _as, *as = &_as;
+ int rv;
+
+ /* Options */
+
+ /* App state init */
+ memset(as, 0x00, sizeof(struct app_state));
+
+ /* Init talloc */
+ l23_ctx = talloc_named_const(NULL, 1, "l23 app context");
+
+ /* Init logging */
+ log_init(&log_info, l23_ctx);
+
+ as->stderr_target = log_target_create_stderr();
+
+ log_add_target(as->stderr_target);
+ log_set_all_filter(as->stderr_target, 1);
+ log_set_log_level(as->stderr_target, LOGL_DEBUG);
+
+ /* Init TRX interface */
+ as->trx = trx_alloc("127.0.0.1", 5700);
+ if (!as->trx)
+ exit(-1);
+
+ /* Start L1CTL server */
+ l1l_start_server(&as->l1s, "/tmp/osmocom_l2", l1ctl_new_cb, as);
+
+ /* Main loop */
+ while (1) {
+ osmo_select_main(0);
+ }
+
+ return 0;
+}
diff --git a/src/host/layer23/src/transceiver_ms/trx.c b/src/host/layer23/src/transceiver_ms/trx.c
new file mode 100644
index 0000000..d20dd54
--- /dev/null
+++ b/src/host/layer23/src/transceiver_ms/trx.c
@@ -0,0 +1,298 @@
+/*
+ * trx.c
+ *
+ * OpenBTS TRX interface handling
+ *
+ * Copyright (C) 2014 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * 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 <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/talloc.h>
+
+#include <osmocom/bb/common/logging.h>
+
+#include "trx.h"
+
+
+static int _trx_clk_read_cb(struct osmo_fd *ofd, unsigned int what);
+static int _trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what);
+static int _trx_data_read_cb(struct osmo_fd *ofd, unsigned int what);
+
+
+/* ------------------------------------------------------------------------ */
+/* Init / Cleanup */
+/* ------------------------------------------------------------------------ */
+
+static int
+_trx_udp_init(struct trx *trx,
+ struct osmo_fd *ofd, const char *addr, uint16_t port,
+ int (*cb)(struct osmo_fd *fd, unsigned int what))
+{
+ struct sockaddr_storage _sas;
+ struct sockaddr *sa = (struct sockaddr *)&_sas;
+ socklen_t sa_len;
+ int rv;
+
+ /* Init */
+ ofd->fd = -1;
+ ofd->cb = cb;
+ ofd->data = trx;
+
+ /* Listen / Binds */
+ rv = osmo_sock_init_ofd(
+ ofd,
+ AF_UNSPEC, SOCK_DGRAM, 0, addr, port + 100,
+ OSMO_SOCK_F_BIND);
+ if (rv < 0)
+ goto err;
+
+ /* Connect */
+ sa_len = sizeof(struct sockaddr_storage);
+ rv = getsockname(ofd->fd, sa, &sa_len);
+ if (rv)
+ goto err;
+
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+ sin->sin_port = htons(ntohs(sin->sin_port)-100);
+ } else if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+ sin6->sin6_port = htons(ntohs(sin6->sin6_port)-100);
+ } else {
+ rv = -EINVAL;
+ goto err;
+ }
+
+ rv = connect(ofd->fd, sa, sa_len);
+ if (rv)
+ goto err;
+
+ return 0;
+
+err:
+ if (ofd->fd >= 0) {
+ osmo_fd_unregister(ofd);
+ close(ofd->fd);
+ }
+
+ return rv;
+}
+
+
+struct trx *
+trx_alloc(const char *addr, uint16_t base_port)
+{
+ struct trx *trx;
+ int rv;
+
+ /* Alloc */
+ trx = talloc_zero(NULL, struct trx);
+ if (!trx)
+ return NULL;
+
+ /* Clock */
+ rv = _trx_udp_init(trx, &trx->ofd_clk, addr, base_port, _trx_clk_read_cb);
+ if (rv)
+ goto err;
+
+ /* Control */
+ rv = _trx_udp_init(trx, &trx->ofd_ctrl, addr, base_port+1, _trx_ctrl_read_cb);
+ if (rv)
+ goto err;
+
+ /* Data */
+ rv = _trx_udp_init(trx, &trx->ofd_data, addr, base_port+2, _trx_data_read_cb);
+ if (rv)
+ goto err;
+
+ /* Done */
+ return trx;
+
+ /* Error path */
+err:
+ trx_free(trx);
+
+ return NULL;
+}
+
+void
+trx_free(struct trx *trx)
+{
+ if (trx->ofd_data.fd >= 0) {
+ osmo_fd_unregister(&trx->ofd_data);
+ close(trx->ofd_data.fd);
+ }
+
+ if (trx->ofd_ctrl.fd >= 0) {
+ osmo_fd_unregister(&trx->ofd_ctrl);
+ close(trx->ofd_ctrl.fd);
+ }
+
+ if (trx->ofd_clk.fd >= 0) {
+ osmo_fd_unregister(&trx->ofd_clk);
+ close(trx->ofd_clk.fd);
+ }
+
+ talloc_free(trx);
+}
+
+
+/* ------------------------------------------------------------------------ */
+/* Clock interface */
+/* ------------------------------------------------------------------------ */
+
+#define CLK_BUF_LEN 128
+
+static int
+_trx_clk_read_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ char buf[CLK_BUF_LEN];
+ uint32_t fn;
+ int l;
+
+ l = recv(ofd->fd, buf, sizeof(buf), MSG_TRUNC);
+ if (l <= 0)
+ return -EIO;
+ if (l >= sizeof(buf)) {
+ LOGP(DTRX, LOGL_ERROR,
+ "Received large message on CLK interface (%d)\n", l);
+ return -EINVAL;
+ }
+
+ if (memcmp("IND CLOCK ", buf, 10) || buf[l-1]) {
+ LOGP(DTRX, LOGL_ERROR,
+ "Received invalid message on CLK interface\n");
+ return -EINVAL;
+ }
+
+ fn = atoi(&buf[11]);
+
+ LOGP(DTRX, LOGL_DEBUG, "Clock IND: fn=%d\n", (int)fn);
+
+ /* FIXME call the clk ind callback */
+
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------------ */
+/* Control interface */
+/* ------------------------------------------------------------------------ */
+
+#define CMD_BUF_LEN 128
+
+static int
+_trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ char buf[CMD_BUF_LEN];
+ int l;
+
+ l = recv(ofd->fd, buf, sizeof(buf), MSG_TRUNC);
+ if (l <= 0)
+ return -EIO;
+
+ /* FIXME should not happen ... */
+
+ printf("Here %s\n", buf);
+
+ return 0;
+}
+
+#include <fcntl.h>
+
+int
+trx_ctrl_send_cmd(struct trx *trx, const char *cmd, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[CMD_BUF_LEN], cmd_match[32];
+ int l;
+
+ /* Send the commands */
+ l = snprintf(buf, sizeof(buf)-1, "CMD %s%s", cmd, fmt ? " " : "");
+
+ if (fmt) {
+ va_start(ap, fmt);
+ l += vsnprintf(buf+l, sizeof(buf)-l-1, fmt, ap);
+ va_end(ap);
+ }
+
+ buf[l] = '\0';
+
+ LOGP(DTRX, LOGL_DEBUG, "TRX Control send: |%s|\n", buf);
+
+ send(trx->ofd_ctrl.fd, buf, strlen(buf)+1, 0);
+
+ /* Wait for response */
+ {
+ int fd = trx->ofd_ctrl.fd;
+ int flags;
+
+ /* make FD nonblocking */
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0)
+ return flags;
+ flags &= ~O_NONBLOCK;
+ flags = fcntl(fd, F_SETFL, flags);
+ if (flags < 0)
+ return flags;
+ }
+
+ /* Get a response */
+ l = recv(trx->ofd_ctrl.fd, buf, sizeof(buf), MSG_TRUNC);
+ if (l <= 0)
+ return -EIO;
+
+ if (memcmp(buf, "RSP ", 4) || buf[l-1] != '\0') {
+ LOGP(DTRX, LOGL_ERROR, "Invalid response on TRX Control socket\n");
+ return -EIO;
+ }
+
+ LOGP(DTRX, LOGL_DEBUG, "TRX Control read: |%s|\n", buf);
+
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------------ */
+/* Data interface */
+/* ------------------------------------------------------------------------ */
+
+static int
+_trx_data_read_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ char buf[128];
+ int l;
+
+ l = recv(ofd->fd, buf, sizeof(buf), MSG_TRUNC);
+ if (l <= 0)
+ return -EIO;
+
+ return 0;
+}
diff --git a/src/host/layer23/src/transceiver_ms/trx.h b/src/host/layer23/src/transceiver_ms/trx.h
new file mode 100644
index 0000000..8485cd8
--- /dev/null
+++ b/src/host/layer23/src/transceiver_ms/trx.h
@@ -0,0 +1,45 @@
+/*
+ * trx.h
+ *
+ * OpenBTS TRX interface handling
+ *
+ * Copyright (C) 2014 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * 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/>.
+ */
+
+#ifndef __TRXMS_TRX_H__
+#define __TRXMS_TRX_H__
+
+
+#include <stdint.h>
+
+
+struct trx {
+ /* UDP sockets */
+ struct osmo_fd ofd_clk;
+ struct osmo_fd ofd_ctrl;
+ struct osmo_fd ofd_data;
+
+ /* */
+};
+
+struct trx *trx_alloc(const char *addr, uint16_t base_port);
+void trx_free(struct trx *trx);
+
+int trx_ctrl_send_cmd(struct trx *trx, const char *cmd, const char *fmt, ...);
+
+#endif /* __TRXMS_TRX_H__ */