From 8975b437ed476a1655e19b68d113bc93c468b087 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 30 Jun 2016 00:41:58 +0600 Subject: [PATCH 001/211] host/trxcon: introduce a new 'trxcon' application This app is similar to the osmocon, but designed to connect L2 & L3 apps with SDR transceiver insted of obsolete Calypso based hardware. Change-Id: Ie3c17f19aad9c26f3c49966a7c96b65911f62369 --- src/Makefile | 18 +++- src/host/trxcon/.gitignore | 26 +++++ src/host/trxcon/Makefile.am | 29 ++++++ src/host/trxcon/configure.ac | 23 +++++ src/host/trxcon/logging.c | 52 ++++++++++ src/host/trxcon/logging.h | 11 ++ src/host/trxcon/trxcon.c | 193 +++++++++++++++++++++++++++++++++++ 7 files changed, 350 insertions(+), 2 deletions(-) create mode 100644 src/host/trxcon/.gitignore create mode 100644 src/host/trxcon/Makefile.am create mode 100644 src/host/trxcon/configure.ac create mode 100644 src/host/trxcon/logging.c create mode 100644 src/host/trxcon/logging.h create mode 100644 src/host/trxcon/trxcon.c diff --git a/src/Makefile b/src/Makefile index f5e2128e7..dedc40e83 100644 --- a/src/Makefile +++ b/src/Makefile @@ -12,8 +12,7 @@ CROSS_TOOL_PREFIX=$(CROSS_HOST)- TOPDIR=$(shell pwd) all: libosmocore-target nofirmware firmware mtk-firmware - -nofirmware: layer23 osmocon gsmmap virtphy +nofirmware: layer23 osmocon trxcon gsmmap virtphy libosmocore-target: shared/libosmocore/build-target/src/.libs/libosmocore.a @@ -58,6 +57,19 @@ host/virt_phy/Makefile: host/virt_phy/configure host/virt_phy/virtphy: host/virt_phy/Makefile make -C host/virt_phy +.PHONY: trxcon +trxcon: host/trxcon/trxcon + +host/trxcon/configure: host/trxcon/configure.ac + cd host/trxcon && autoreconf -i + +host/trxcon/Makefile: host/trxcon/configure + cd host/trxcon && ./configure $(HOST_CONFARGS) + +host/trxcon/trxcon: host/trxcon/Makefile + make -C host/trxcon + + .PHONY: gsmmap gsmmap: host/gsmmap/gsmmap @@ -99,6 +111,7 @@ clean: make -C host/osmocon $@ make -C host/gsmmap $@ make -C host/virt_phy $@ + make -C host/trxcon $@ make -C target/firmware $@ make -C target/firmware -f Makefile.mtk $@ @@ -108,5 +121,6 @@ distclean: make -C host/osmocon $@ make -C host/gsmmap $@ make -C host/virt_phy $@ + make -C host/trxcon $@ # 'firmware' also handles 'mtk-firmware' make -C target/firmware $@ diff --git a/src/host/trxcon/.gitignore b/src/host/trxcon/.gitignore new file mode 100644 index 000000000..d6b28ee9d --- /dev/null +++ b/src/host/trxcon/.gitignore @@ -0,0 +1,26 @@ +# autoreconf by-products +*.in + +aclocal.m4 +autom4te.cache/ +configure +depcomp +install-sh +missing +compile + +# configure by-products +.deps/ +Makefile + +config.status +version.h + +# build by-products +*.o + +trxcon + +# various +.version +.tarball-version diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am new file mode 100644 index 000000000..00869d9b6 --- /dev/null +++ b/src/host/trxcon/Makefile.am @@ -0,0 +1,29 @@ +AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 + +# versioning magic +BUILT_SOURCES = $(top_srcdir)/.version +$(top_srcdir)/.version: + echo $(VERSION) > $@-t && mv $@-t $@ +dist-hook: + echo $(VERSION) > $(distdir)/.tarball-version + +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + $(LIBOSMOCORE_CFLAGS) \ + $(NULL) + +bin_PROGRAMS = trxcon + +trxcon_SOURCES = \ + logging.c \ + trxcon.c \ + $(NULL) + +trxcon_LDADD = \ + $(LIBOSMOCORE_LIBS) \ + $(NULL) diff --git a/src/host/trxcon/configure.ac b/src/host/trxcon/configure.ac new file mode 100644 index 000000000..c411d0408 --- /dev/null +++ b/src/host/trxcon/configure.ac @@ -0,0 +1,23 @@ +dnl Process this file with autoconf to produce a configure script +AC_INIT([trxcon], [0.0.0]) +AM_INIT_AUTOMAKE + +dnl kernel style compile messages +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +dnl checks for programs +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_INSTALL + +dnl checks for libraries +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore) +PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding) + +dnl checks for header files +AC_HEADER_STDC + +dnl Checks for typedefs, structures and compiler characteristics + +AC_OUTPUT( + Makefile) diff --git a/src/host/trxcon/logging.c b/src/host/trxcon/logging.c new file mode 100644 index 000000000..136cc7650 --- /dev/null +++ b/src/host/trxcon/logging.c @@ -0,0 +1,52 @@ +/* + * OsmocomBB <-> SDR connection bridge + * + * (C) 2016-2017 by Vadim Yanitskiy + * + * 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 +#include +#include + +#include "logging.h" + +static struct log_info_cat trx_log_info_cat[] = { + [DAPP] = { + .name = "DAPP", + .description = "Application", + .color = "\033[1;35m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, +}; + +static const struct log_info trx_log_info = { + .cat = trx_log_info_cat, + .num_cat = ARRAY_SIZE(trx_log_info_cat), +}; + +int trx_log_init(const char *category_mask) +{ + osmo_init_logging(&trx_log_info); + + if (category_mask) + log_parse_category_mask(osmo_stderr_target, category_mask); + + return 0; +} diff --git a/src/host/trxcon/logging.h b/src/host/trxcon/logging.h new file mode 100644 index 000000000..4149e4f2e --- /dev/null +++ b/src/host/trxcon/logging.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#define DEBUG_DEFAULT "DAPP" + +enum { + DAPP +}; + +int trx_log_init(const char *category_mask); diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c new file mode 100644 index 000000000..03664ca6c --- /dev/null +++ b/src/host/trxcon/trxcon.c @@ -0,0 +1,193 @@ +/* + * OsmocomBB <-> SDR connection bridge + * + * (C) 2016-2017 by Vadim Yanitskiy + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "logging.h" + +#define COPYRIGHT \ + "Copyright (C) 2016-2017 by Vadim Yanitskiy \n" \ + "License GPLv2+: GNU GPL version 2 or later " \ + "\n" \ + "This is free software: you are free to change and redistribute it.\n" \ + "There is NO WARRANTY, to the extent permitted by law.\n\n" + +static struct { + const char *debug_mask; + int daemonize; + int quit; + + const char *trx_ip; + uint16_t trx_base_port; + const char *bind_socket; +} app_data; + +void *tall_trx_ctx = NULL; + +static void print_usage(const char *app) +{ + printf("Usage: %s\n", app); +} + +static void print_help(void) +{ + printf(" Some help...\n"); + printf(" -h --help this text\n"); + printf(" -d --debug Change debug flags. Default: %s\n", DEBUG_DEFAULT); + printf(" -i --trx-ip IP address of host runing TRX (default 127.0.0.1)\n"); + printf(" -p --trx-port Base port of TRX instance (default 5700)\n"); + printf(" -s --socket Listening socket for layer23 (default /tmp/osmocom_l2)\n"); + printf(" -D --daemonize Run as daemon\n"); +} + +static void handle_options(int argc, char **argv) +{ + while (1) { + int option_index = 0, c; + static struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"debug", 1, 0, 'd'}, + {"socket", 1, 0, 's'}, + {"trx-ip", 1, 0, 'i'}, + {"trx-port", 1, 0, 'p'}, + {"daemonize", 0, 0, 'D'}, + {0, 0, 0, 0} + }; + + c = getopt_long(argc, argv, "d:i:p:s:Dh", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(argv[0]); + print_help(); + exit(0); + break; + case 'd': + app_data.debug_mask = optarg; + break; + case 'i': + app_data.trx_ip = optarg; + break; + case 'p': + app_data.trx_base_port = atoi(optarg); + break; + case 's': + app_data.bind_socket = optarg; + break; + case 'D': + app_data.daemonize = 1; + break; + default: + break; + } + } +} + +static void init_defaults(void) +{ + app_data.bind_socket = "/tmp/osmocom_l2"; + app_data.trx_ip = "127.0.0.1"; + app_data.trx_base_port = 5700; + + app_data.debug_mask = NULL; + app_data.daemonize = 0; + app_data.quit = 0; +} + +static void signal_handler(int signal) +{ + fprintf(stderr, "signal %u received\n", signal); + + switch (signal) { + case SIGINT: + app_data.quit++; + break; + case SIGABRT: + case SIGUSR1: + case SIGUSR2: + talloc_report_full(tall_trx_ctx, stderr); + break; + default: + break; + } +} + +int main(int argc, char **argv) +{ + int rc = 0; + + printf("%s", COPYRIGHT); + init_defaults(); + handle_options(argc, argv); + + /* Init talloc memory management system */ + tall_trx_ctx = talloc_init("trxcon context"); + msgb_talloc_ctx_init(tall_trx_ctx, 0); + + /* Setup signal handlers */ + signal(SIGINT, &signal_handler); + signal(SIGUSR1, &signal_handler); + signal(SIGUSR2, &signal_handler); + osmo_init_ignore_signals(); + + /* Init logging system */ + trx_log_init(app_data.debug_mask); + + /* Currently nothing to do */ + print_usage(argv[0]); + print_help(); + goto exit; + + if (app_data.daemonize) { + rc = osmo_daemonize(); + if (rc < 0) { + perror("Error during daemonize"); + goto exit; + } + } + + while (!app_data.quit) + osmo_select_main(0); + +exit: + /* Make Valgrind happy */ + log_fini(); + talloc_free(tall_trx_ctx); + + return rc; +} From 9f5fefe792628d995945158e653b388c01969d86 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 22 Jul 2016 22:57:50 +0600 Subject: [PATCH 002/211] 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 --- src/host/trxcon/Makefile.am | 1 + src/host/trxcon/l1ctl_link.c | 266 +++++++++++++++++++++++++++++++++++ src/host/trxcon/l1ctl_link.h | 19 +++ src/host/trxcon/logging.c | 6 + src/host/trxcon/logging.h | 5 +- src/host/trxcon/trxcon.c | 19 ++- 6 files changed, 309 insertions(+), 7 deletions(-) create mode 100644 src/host/trxcon/l1ctl_link.c create mode 100644 src/host/trxcon/l1ctl_link.h diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am index 00869d9b6..d7c26d4b6 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 000000000..e52950fa9 --- /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 + * (C) 2016-2017 by Vadim Yanitskiy + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#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 000000000..417dc7570 --- /dev/null +++ b/src/host/trxcon/l1ctl_link.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +#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 136cc7650..734d13806 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 4149e4f2e..049f322a4 100644 --- a/src/host/trxcon/logging.h +++ b/src/host/trxcon/logging.h @@ -2,10 +2,11 @@ #include -#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 03664ca6c..4ad8a0cc9 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -36,6 +36,7 @@ #include #include "logging.h" +#include "l1ctl_link.h" #define COPYRIGHT \ "Copyright (C) 2016-2017 by Vadim Yanitskiy \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); From 48f2cb4b3b94000647358ddd7d06edbb55495bba Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sun, 24 Jul 2016 01:40:41 +0600 Subject: [PATCH 003/211] host/trxcon: initial release of transceiver interface This is the second side of the 'OsmocomBB <-> SDR' bridge. Most of source code taken from the OsmoBTS project. Change-Id: I96fa3ada05d010f31af419a4950fd8ae2b62ef34 --- src/host/trxcon/Makefile.am | 3 + src/host/trxcon/configure.ac | 1 + src/host/trxcon/logging.c | 6 + src/host/trxcon/logging.h | 3 +- src/host/trxcon/trx_if.c | 607 +++++++++++++++++++++++++++++++++++ src/host/trxcon/trx_if.h | 38 +++ src/host/trxcon/trxcon.c | 9 + 7 files changed, 666 insertions(+), 1 deletion(-) create mode 100644 src/host/trxcon/trx_if.c create mode 100644 src/host/trxcon/trx_if.h diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am index d7c26d4b6..9da21996e 100644 --- a/src/host/trxcon/Makefile.am +++ b/src/host/trxcon/Makefile.am @@ -15,16 +15,19 @@ AM_CPPFLAGS = \ AM_CFLAGS = \ -Wall \ $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ $(NULL) bin_PROGRAMS = trxcon trxcon_SOURCES = \ l1ctl_link.c \ + trx_if.c \ logging.c \ trxcon.c \ $(NULL) trxcon_LDADD = \ $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ $(NULL) diff --git a/src/host/trxcon/configure.ac b/src/host/trxcon/configure.ac index c411d0408..a94859b79 100644 --- a/src/host/trxcon/configure.ac +++ b/src/host/trxcon/configure.ac @@ -13,6 +13,7 @@ AC_PROG_INSTALL dnl checks for libraries PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore) PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding) +PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm) dnl checks for header files AC_HEADER_STDC diff --git a/src/host/trxcon/logging.c b/src/host/trxcon/logging.c index 734d13806..28e6776b3 100644 --- a/src/host/trxcon/logging.c +++ b/src/host/trxcon/logging.c @@ -40,6 +40,12 @@ static struct log_info_cat trx_log_info_cat[] = { .color = "\033[1;31m", .enabled = 1, .loglevel = LOGL_NOTICE, }, + [DTRX] = { + .name = "DTRX", + .description = "Transceiver interface", + .color = "\033[1;33m", + .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 049f322a4..4d7cea063 100644 --- a/src/host/trxcon/logging.h +++ b/src/host/trxcon/logging.h @@ -2,11 +2,12 @@ #include -#define DEBUG_DEFAULT "DAPP:DL1C" +#define DEBUG_DEFAULT "DAPP:DL1C:DTRX" enum { DAPP, DL1C, + DTRX, }; int trx_log_init(const char *category_mask); diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c new file mode 100644 index 000000000..060716799 --- /dev/null +++ b/src/host/trxcon/trx_if.c @@ -0,0 +1,607 @@ +/* + * OsmocomBB <-> SDR connection bridge + * Transceiver interface handlers + * + * Copyright (C) 2013 by Andreas Eversberg + * Copyright (C) 2016-2017 by Vadim Yanitskiy + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include "trx_if.h" +#include "logging.h" + +extern void *tall_trx_ctx; + +static int trx_udp_open(void *priv, struct osmo_fd *ofd, const char *host, + uint16_t port_local, uint16_t port_remote, + int (*cb)(struct osmo_fd *fd, unsigned int what)) +{ + struct sockaddr_storage sas; + struct sockaddr *sa = (struct sockaddr *) &sas; + socklen_t sa_len; + int rc; + + ofd->data = priv; + ofd->fd = -1; + ofd->cb = cb; + + /* Init RX side for UDP connection */ + rc = osmo_sock_init_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, + 0, host, port_local, OSMO_SOCK_F_BIND); + if (rc < 0) + return rc; + + /* Init TX side for UDP connection */ + sa_len = sizeof(sas); + rc = getsockname(ofd->fd, sa, &sa_len); + if (rc) + return rc; + + if (sa->sa_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *) sa; + sin->sin_port = htons(port_remote); + } else if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa; + sin6->sin6_port = htons(port_remote); + } else { + return -EINVAL; + } + + rc = connect(ofd->fd, sa, sa_len); + return rc; +} + +static void trx_udp_close(struct osmo_fd *ofd) +{ + if (ofd->fd > 0) { + osmo_fd_unregister(ofd); + close(ofd->fd); + ofd->fd = -1; + } +} + +/* ------------------------------------------------------------------------ */ +/* Clock (CLCK) interface handlers */ +/* ------------------------------------------------------------------------ */ +/* Indications on the Master Clock Interface */ +/* */ +/* The master clock interface is output only (from the radio). */ +/* Messages are "indications". */ +/* */ +/* CLOCK gives the current value of the transceiver clock to be used by the */ +/* core. This message is sent whenever a transmission packet arrives that */ +/* is too late or too early. The clock value is NOT the current transceiver */ +/* time. It is a time setting the core should use to give better packet */ +/* arrival times. */ +/* */ +/* IND CLOCK */ +/* ------------------------------------------------------------------------ */ + +static int trx_clck_read_cb(struct osmo_fd *ofd, unsigned int what) +{ + char buf[1500]; + uint32_t fn; + int len; + + len = recv(ofd->fd, buf, sizeof(buf) - 1, 0); + if (len <= 0) + return len; + + /* Terminate received string */ + buf[len] = '\0'; + + if (!!strncmp(buf, "IND CLOCK ", 10)) { + LOGP(DTRX, LOGL_ERROR, + "Unknown message on CLCK socket: %s\n", buf); + return 0; + } + + sscanf(buf, "IND CLOCK %u", &fn); + + LOGP(DTRX, LOGL_DEBUG, "Clock indication: fn=%u\n", fn); + + if (fn >= 2715648) { + fn %= 2715648; + LOGP(DTRX, LOGL_ERROR, "Indicated clock's FN is not wrapping " + "correctly, correcting to fn=%u\n", fn); + } + + /* TODO: call the clck_ind callback */ + return 0; +} + +/* ------------------------------------------------------------------------ */ +/* Control (CTRL) interface handlers */ +/* ------------------------------------------------------------------------ */ +/* Commands on the Per-ARFCN Control Interface */ +/* */ +/* The per-ARFCN control interface uses a command-response protocol. */ +/* Commands are NULL-terminated ASCII strings, one per UDP socket. */ +/* Each command has a corresponding response. */ +/* Every command is of the form: */ +/* */ +/* CMD [params] */ +/* */ +/* The is the actual command. */ +/* Parameters are optional depending on the commands type. */ +/* Every response is of the form: */ +/* */ +/* RSP [result] */ +/* */ +/* The is 0 for success and a non-zero error code for failure. */ +/* Successful responses may include results, depending on the command type. */ +/* ------------------------------------------------------------------------ */ + +static void trx_ctrl_timer_cb(void *data); + +/* Send first CTRL message and start timer */ +static void trx_ctrl_send(struct trx_instance *trx) +{ + struct trx_ctrl_msg *tcm; + + if (llist_empty(&trx->trx_ctrl_list)) + return; + tcm = llist_entry(trx->trx_ctrl_list.next, struct trx_ctrl_msg, list); + + /* Send command */ + LOGP(DTRX, LOGL_DEBUG, "Sending control '%s'\n", tcm->cmd); + send(trx->trx_ofd_ctrl.fd, tcm->cmd, strlen(tcm->cmd) + 1, 0); + + /* Start expire timer */ + trx->trx_ctrl_timer.data = trx; + trx->trx_ctrl_timer.cb = trx_ctrl_timer_cb; + osmo_timer_schedule(&trx->trx_ctrl_timer, 2, 0); +} + +static void trx_ctrl_timer_cb(void *data) +{ + LOGP(DTRX, LOGL_NOTICE, "No response from transceiver...\n"); + + /* Attempt to send a command again */ + trx_ctrl_send((struct trx_instance *) data); +} + +/* Add a new CTRL command to the trx_ctrl_list */ +static int trx_ctrl_cmd(struct trx_instance *trx, int critical, + const char *cmd, const char *fmt, ...) +{ + struct trx_ctrl_msg *tcm; + int len, pending = 0; + va_list ap; + + /*if (!transceiver_available && !!strcmp(cmd, "POWEROFF")) { + LOGP(DTRX, LOGL_ERROR, "CTRL ignored: No clock from " + "transceiver, please fix!\n"); + return -EIO; + }*/ + + if (!llist_empty(&trx->trx_ctrl_list)) + pending = 1; + + /* Allocate a message */ + tcm = talloc_zero(trx, struct trx_ctrl_msg); + if (!tcm) + return -ENOMEM; + + /* Fill in command arguments */ + if (fmt && fmt[0]) { + len = snprintf(tcm->cmd, sizeof(tcm->cmd) - 1, "CMD %s ", cmd); + va_start(ap, fmt); + vsnprintf(tcm->cmd + len, sizeof(tcm->cmd) - len - 1, fmt, ap); + va_end(ap); + } else { + snprintf(tcm->cmd, sizeof(tcm->cmd) - 1, "CMD %s", cmd); + } + + tcm->cmd_len = strlen(cmd); + tcm->critical = critical; + llist_add_tail(&tcm->list, &trx->trx_ctrl_list); + LOGP(DTRX, LOGL_INFO, "Adding new control '%s'\n", tcm->cmd); + + /* Send message, if no pending messages */ + if (!pending) + trx_ctrl_send(trx); + + return 0; +} + +/* + * Power Control + * + * POWEROFF shuts off transmitter power and stops the demodulator. + * CMD POWEROFF + * RSP POWEROFF + * + * POWERON starts the transmitter and starts the demodulator. + * Initial power level is very low. + * This command fails if the transmitter and receiver are not yet tuned. + * This command fails if the transmit or receive frequency creates a conflict + * with another ARFCN that is already running. + * If the transceiver is already on, it response with success to this command. + * CMD POWERON + * RSP POWERON + */ + +int trx_if_cmd_poweroff(struct trx_instance *trx) +{ + return trx_ctrl_cmd(trx, 1, "POWEROFF", ""); +} + +int trx_if_cmd_poweron(struct trx_instance *trx) +{ + return trx_ctrl_cmd(trx, 1, "POWERON", ""); +} + +/* + * SETPOWER sets output power in dB wrt full scale. + * This command fails if the transmitter and receiver are not running. + * CMD SETPOWER + * RSP SETPOWER + */ + +int trx_if_cmd_setpower(struct trx_instance *trx, int db) +{ + return trx_ctrl_cmd(trx, 0, "SETPOWER", "%d", db); +} + +/* + * ADJPOWER adjusts power by the given dB step. + * Response returns resulting power level wrt full scale. + * This command fails if the transmitter and receiver are not running. + * CMD ADJPOWER + * RSP ADJPOWER +*/ + +int trx_if_cmd_adjpower(struct trx_instance *trx, int db) +{ + return trx_ctrl_cmd(trx, 0, "ADJPOWER", "%d", db); +} + +int trx_if_cmd_setrxgain(struct trx_instance *trx, int db) +{ + return trx_ctrl_cmd(trx, 0, "SETRXGAIN", "%d", db); +} + +int trx_if_cmd_setmaxdly(struct trx_instance *trx, int dly) +{ + return trx_ctrl_cmd(trx, 0, "SETMAXDLY", "%d", dly); +} + +/* + * Timeslot Control + * + * SETSLOT sets the format of the uplink timeslots in the ARFCN. + * The indicates the timeslot of interest. + * The indicates the type of channel that occupies the timeslot. + * A chantype of zero indicates the timeslot is off. + * CMD SETSLOT + * RSP SETSLOT + */ + +int trx_if_cmd_setslot(struct trx_instance *trx, uint8_t tn, uint8_t type) +{ + return trx_ctrl_cmd(trx, 1, "SETSLOT", "%d %d", tn, type); +} + +/* + * Tuning Control + * + * (RX/TX)TUNE tunes the receiver to a given frequency in kHz. + * This command fails if the receiver is already running. + * (To re-tune you stop the radio, re-tune, and restart.) + * This command fails if the transmit or receive frequency + * creates a conflict with another ARFCN that is already running. + * CMD (RX/TX)TUNE + * RSP (RX/TX)TUNE + */ + +int trx_if_cmd_rxtune(struct trx_instance *trx, uint16_t arfcn) +{ + uint16_t freq10; + + /* RX is downlink on MS side */ + freq10 = gsm_arfcn2freq10(arfcn, 0); + if (freq10 == 0xffff) { + LOGP(DTRX, LOGL_ERROR, "ARFCN %d not defined\n", arfcn); + return -ENOTSUP; + } + + return trx_ctrl_cmd(trx, 1, "RXTUNE", "%d", freq10 * 100); +} + +int trx_if_cmd_txtune(struct trx_instance *trx, uint16_t arfcn) +{ + uint16_t freq10; + + /* TX is uplink on MS side */ + freq10 = gsm_arfcn2freq10(arfcn, 1); + if (freq10 == 0xffff) { + LOGP(DTRX, LOGL_ERROR, "ARFCN %d not defined\n", arfcn); + return -ENOTSUP; + } + + return trx_ctrl_cmd(trx, 1, "TXTUNE", "%d", freq10 * 100); +} + +/* Get response from CTRL socket */ +static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) +{ + struct trx_instance *trx = ofd->data; + struct trx_ctrl_msg *tcm; + int len, resp, rsp_len; + char buf[1500], *p; + + len = recv(ofd->fd, buf, sizeof(buf) - 1, 0); + if (len <= 0) + return len; + buf[len] = '\0'; + + if (!!strncmp(buf, "RSP ", 4)) { + LOGP(DTRX, LOGL_NOTICE, "Unknown message on CTRL port: %s\n", buf); + return 0; + } + + /* Calculate the length of response item */ + p = strchr(buf + 4, ' '); + rsp_len = p ? p - buf - 4 : strlen(buf) - 4; + + LOGP(DTRX, LOGL_INFO, "Response message: '%s'\n", buf); + + /* Abort expire timer */ + if (osmo_timer_pending(&trx->trx_ctrl_timer)) + osmo_timer_del(&trx->trx_ctrl_timer); + + /* Get command for response message */ + if (llist_empty(&trx->trx_ctrl_list)) { + LOGP(DTRX, LOGL_NOTICE, "Response message without command\n"); + return -EINVAL; + } + + tcm = llist_entry(trx->trx_ctrl_list.next, + struct trx_ctrl_msg, list); + + /* Check if response matches command */ + if (rsp_len != tcm->cmd_len || !!strncmp(buf + 4, tcm->cmd + 4, rsp_len)) { + LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR, + "Response message '%s' does not match command " + "message '%s'\n", buf, tcm->cmd); + goto rsp_error; + } + + /* Check for response code */ + sscanf(p + 1, "%d", &resp); + if (resp) { + LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR, + "Transceiver rejected TRX command with " + "response: '%s'\n", buf); + + if (tcm->critical) + goto rsp_error; + } + + /* Remove command from list */ + llist_del(&tcm->list); + talloc_free(tcm); + + /* Send next message, if any */ + trx_ctrl_send(trx); + + return 0; + +rsp_error: + /** + * TODO: stop/freeze trxcon process + * or notify higher layers about the problem with L1 + */ + return -EIO; +} + +/* ------------------------------------------------------------------------ */ +/* Data interface handlers */ +/* ------------------------------------------------------------------------ */ +/* DATA interface */ +/* */ +/* Messages on the data interface carry one radio burst per UDP message. */ +/* */ +/* Received Data Burst: */ +/* 1 byte timeslot index */ +/* 4 bytes GSM frame number, BE */ +/* 1 byte RSSI in -dBm */ +/* 2 bytes correlator timing offset in 1/256 symbol steps, 2's-comp, BE */ +/* 148 bytes soft symbol estimates, 0 -> definite "0", 255 -> definite "1" */ +/* */ +/* Transmit Data Burst: */ +/* 1 byte timeslot index */ +/* 4 bytes GSM frame number, BE */ +/* 1 byte transmit level wrt ARFCN max, -dB (attenuation) */ +/* 148 bytes output symbol values, 0 & 1 */ +/* ------------------------------------------------------------------------ */ + +static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what) +{ + struct trx_instance *trx = ofd->data; + uint8_t buf[256]; + sbit_t bits[148]; + int8_t rssi, tn; + uint32_t fn; + int len, i; + float toa; + + len = recv(ofd->fd, buf, sizeof(buf), 0); + if (len <= 0) + return len; + + if (len != 156) { + LOGP(DTRX, LOGL_ERROR, "Got data message with invalid length " + "'%d'\n", len); + return -EINVAL; + } + + tn = buf[0]; + fn = (buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4]; + rssi = -(int8_t) buf[5]; + toa = ((int16_t) (buf[6] << 8) | buf[7]) / 256.0F; + + /* Copy and convert bits {254..0} to sbits {-127..127} */ + for (i = 0; i < 148; i++) { + if (buf[8 + i] == 255) + bits[i] = -127; + else + bits[i] = 127 - buf[8 + i]; + } + + if (tn >= 8) { + LOGP(DTRX, LOGL_ERROR, "Illegal TS %d\n", tn); + return -EINVAL; + } + + if (fn >= 2715648) { + LOGP(DTRX, LOGL_ERROR, "Illegal FN %u\n", fn); + return -EINVAL; + } + + LOGP(DTRX, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%.2f\n", + tn, fn, rssi, toa); + + /* TODO: poke scheduler here! */ + return 0; +} + +int trx_if_data(struct trx_instance *trx, uint8_t tn, uint32_t fn, + uint8_t pwr, const ubit_t *bits) +{ + uint8_t buf[256]; + + LOGP(DTRX, LOGL_DEBUG, "TX burst tn=%u fn=%u pwr=%u\n", tn, fn, pwr); + + buf[0] = tn; + buf[1] = (fn >> 24) & 0xff; + buf[2] = (fn >> 16) & 0xff; + buf[3] = (fn >> 8) & 0xff; + buf[4] = (fn >> 0) & 0xff; + buf[5] = pwr; + + /* Copy ubits {0,1} */ + memcpy(buf + 6, bits, 148); + + /** + * TODO: is transceiver available??? + * + * We must be sure that we have clock, + * and we have sent all control data + * + * if (transceiver_available && llist_empty(&l1h->trx_ctrl_list)) + * send(l1h->trx_ofd_data.fd, buf, 154, 0); + * else + * LOGP(DTRX, LOGL_DEBUG, "Ignoring TX data, transceiver offline.\n"); + */ + + return 0; +} + +/* + * Open/close OsmoTRX connection + */ + +int trx_if_open(struct trx_instance **trx, const char *host, uint16_t port) +{ + struct trx_instance *trx_new; + int rc; + + LOGP(DTRX, LOGL_NOTICE, "Init transceiver interface\n"); + + /* Try to allocate memory */ + trx_new = talloc_zero(tall_trx_ctx, struct trx_instance); + if (!trx_new) { + fprintf(stderr, "Failed to allocate memory\n"); + return -ENOMEM; + } + + /* Initialize CTRL queue */ + INIT_LLIST_HEAD(&trx_new->trx_ctrl_list); + + /* Open sockets */ + rc = trx_udp_open(trx_new, &trx_new->trx_ofd_clck, host, + port + 100, port + 0, trx_clck_read_cb); + if (rc < 0) + goto error; + + rc = trx_udp_open(trx_new, &trx_new->trx_ofd_ctrl, host, + port + 101, port + 1, trx_ctrl_read_cb); + if (rc < 0) + goto error; + + rc = trx_udp_open(trx_new, &trx_new->trx_ofd_data, host, + port + 102, port + 2, trx_data_read_cb); + if (rc < 0) + goto error; + + *trx = trx_new; + + return 0; + +error: + LOGP(DTRX, LOGL_NOTICE, "Couldn't establish UDP connection\n"); + talloc_free(trx_new); + return rc; +} + +/* Flush pending control messages */ +static void trx_if_flush_ctrl(struct trx_instance *trx) +{ + struct trx_ctrl_msg *tcm; + + while (!llist_empty(&trx->trx_ctrl_list)) { + tcm = llist_entry(trx->trx_ctrl_list.next, + struct trx_ctrl_msg, list); + llist_del(&tcm->list); + talloc_free(tcm); + } +} + +void trx_if_close(struct trx_instance *trx) +{ + LOGP(DTRX, LOGL_NOTICE, "Shutdown transceiver interface\n"); + + /* Flush CTRL message list */ + trx_if_flush_ctrl(trx); + + /* Close sockets */ + trx_udp_close(&trx->trx_ofd_clck); + trx_udp_close(&trx->trx_ofd_ctrl); + trx_udp_close(&trx->trx_ofd_data); + + /* Free memory */ + talloc_free(trx); +} diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h new file mode 100644 index 000000000..fadf60430 --- /dev/null +++ b/src/host/trxcon/trx_if.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +struct trx_instance { + struct osmo_fd trx_ofd_clck; + struct osmo_fd trx_ofd_ctrl; + struct osmo_fd trx_ofd_data; + + struct osmo_timer_list trx_ctrl_timer; + struct llist_head trx_ctrl_list; +}; + +struct trx_ctrl_msg { + struct llist_head list; + char cmd[128]; + int critical; + int cmd_len; +}; + +int trx_if_open(struct trx_instance **trx, const char *host, uint16_t port); +void trx_if_close(struct trx_instance *trx); + +int trx_if_cmd_poweron(struct trx_instance *trx); +int trx_if_cmd_poweroff(struct trx_instance *trx); + +int trx_if_cmd_setpower(struct trx_instance *trx, int db); +int trx_if_cmd_adjpower(struct trx_instance *trx, int db); + +int trx_if_cmd_setrxgain(struct trx_instance *trx, int db); +int trx_if_cmd_setmaxdly(struct trx_instance *trx, int dly); + +int trx_if_cmd_rxtune(struct trx_instance *trx, uint16_t arfcn); +int trx_if_cmd_txtune(struct trx_instance *trx, uint16_t arfcn); + +int trx_if_cmd_setslot(struct trx_instance *trx, uint8_t tn, uint8_t type); diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index 4ad8a0cc9..781942a63 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -35,6 +35,7 @@ #include #include +#include "trx_if.h" #include "logging.h" #include "l1ctl_link.h" @@ -54,6 +55,8 @@ static struct { struct l1ctl_link *l1l; const char *bind_socket; + /* TRX specific */ + struct trx_instance *trx; const char *trx_ip; uint16_t trx_base_port; } app_data; @@ -177,6 +180,11 @@ int main(int argc, char **argv) if (rc) goto exit; + /* Init transceiver interface */ + rc = trx_if_open(&app_data.trx, app_data.trx_ip, app_data.trx_base_port); + if (rc) + goto exit; + LOGP(DAPP, LOGL_NOTICE, "Init complete\n"); if (app_data.daemonize) { @@ -193,6 +201,7 @@ int main(int argc, char **argv) exit: /* Close active connections */ l1ctl_link_shutdown(app_data.l1l); + trx_if_close(app_data.trx); /* Make Valgrind happy */ log_fini(); From 65664d088d3d2b30d108ca0b1b8b2f0244d0d7e4 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 8 Jun 2017 15:46:44 +0700 Subject: [PATCH 004/211] host/trxcon: fix NULL-pointer deference Change-Id: Idc036d4ea32b4aa3f4841d39144ef1733414728e --- src/host/trxcon/l1ctl_link.c | 4 ++++ src/host/trxcon/trx_if.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/host/trxcon/l1ctl_link.c b/src/host/trxcon/l1ctl_link.c index e52950fa9..62e8943f5 100644 --- a/src/host/trxcon/l1ctl_link.c +++ b/src/host/trxcon/l1ctl_link.c @@ -247,6 +247,10 @@ void l1ctl_link_shutdown(struct l1ctl_link *l1l) { struct osmo_fd *listen_bfd; + /* May be unallocated due to init error */ + if (!l1l) + return; + LOGP(DL1C, LOGL_NOTICE, "Shutdown L1CTL link\n"); listen_bfd = &l1l->listen_bfd; diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index 060716799..9ae490062 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -592,6 +592,10 @@ static void trx_if_flush_ctrl(struct trx_instance *trx) void trx_if_close(struct trx_instance *trx) { + /* May be unallocated due to init error */ + if (!trx) + return; + LOGP(DTRX, LOGL_NOTICE, "Shutdown transceiver interface\n"); /* Flush CTRL message list */ From 83a9c9ef50caf133df810587f645b70d11129919 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 3 Jun 2017 19:02:20 +0700 Subject: [PATCH 005/211] host/trxcon/trx_if.c: add ECHO command This command should be used to check transceiver availability. Change-Id: I6af2d5e413ff7ab751cb34e1659742b0f59b6cca --- src/host/trxcon/trx_if.c | 9 +++++++++ src/host/trxcon/trx_if.h | 1 + 2 files changed, 10 insertions(+) diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index 9ae490062..5b0b7b1cd 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -239,6 +239,10 @@ static int trx_ctrl_cmd(struct trx_instance *trx, int critical, /* * Power Control * + * ECHO is used to check transceiver availability. + * CMD ECHO + * RSP ECHO + * * POWEROFF shuts off transmitter power and stops the demodulator. * CMD POWEROFF * RSP POWEROFF @@ -253,6 +257,11 @@ static int trx_ctrl_cmd(struct trx_instance *trx, int critical, * RSP POWERON */ +int trx_if_cmd_echo(struct trx_instance *trx) +{ + return trx_ctrl_cmd(trx, 1, "ECHO", ""); +} + int trx_if_cmd_poweroff(struct trx_instance *trx) { return trx_ctrl_cmd(trx, 1, "POWEROFF", ""); diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h index fadf60430..6f54b3e6e 100644 --- a/src/host/trxcon/trx_if.h +++ b/src/host/trxcon/trx_if.h @@ -25,6 +25,7 @@ void trx_if_close(struct trx_instance *trx); int trx_if_cmd_poweron(struct trx_instance *trx); int trx_if_cmd_poweroff(struct trx_instance *trx); +int trx_if_cmd_echo(struct trx_instance *trx); int trx_if_cmd_setpower(struct trx_instance *trx, int db); int trx_if_cmd_adjpower(struct trx_instance *trx, int db); From 423aeefc4047038417b8da49aa5887553ffcfad3 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 31 May 2017 09:28:40 +0700 Subject: [PATCH 006/211] host/trxcon: integrate osmo-fsm framework This change introduces the following state machines: - trxcon_app_fsm - main application state machine. This state machine handles different events, raised from program modules (such as trx_if.c or l1ctl.c). - l1ctl_link_fsm - L1CTL server state machine. - trx_interface_fsm - TRX interface state machine. The program modules (such as trx_if.c or l1ctl.c) should be as much independent from each other as possible. In other words, one module should not call methods from another, e.g. L1CTL handlers are not able to send any command to transceiver directly. Instead of that, they should use shared event set to notify the main state machine about something. Depending on current state and received event, main state machine 'decides' what to do. This approach would allow to easily reuse the source code almost 'as is' anywhere outside the project. Change-Id: I7ee6fc891abe5f775f5b7ebbf093181a97950dea --- src/host/trxcon/l1ctl_link.c | 35 +++++++++- src/host/trxcon/l1ctl_link.h | 7 ++ src/host/trxcon/trx_if.c | 123 ++++++++++++++++++++++++++++------- src/host/trxcon/trx_if.h | 12 ++++ src/host/trxcon/trxcon.c | 66 +++++++++++++++++++ src/host/trxcon/trxcon.h | 19 ++++++ 6 files changed, 238 insertions(+), 24 deletions(-) create mode 100644 src/host/trxcon/trxcon.h diff --git a/src/host/trxcon/l1ctl_link.c b/src/host/trxcon/l1ctl_link.c index 62e8943f5..34aa4aaf8 100644 --- a/src/host/trxcon/l1ctl_link.c +++ b/src/host/trxcon/l1ctl_link.c @@ -34,15 +34,36 @@ #include #include +#include #include #include #include #include +#include "trxcon.h" #include "logging.h" #include "l1ctl_link.h" extern void *tall_trx_ctx; +extern struct osmo_fsm_inst *trxcon_fsm; + +static struct osmo_fsm_state l1ctl_fsm_states[] = { + [L1CTL_STATE_IDLE] = { + .out_state_mask = GEN_MASK(L1CTL_STATE_CONNECTED), + .name = "IDLE", + }, + [L1CTL_STATE_CONNECTED] = { + .out_state_mask = GEN_MASK(L1CTL_STATE_IDLE), + .name = "CONNECTED", + }, +}; + +static struct osmo_fsm l1ctl_fsm = { + .name = "l1ctl_link_fsm", + .states = l1ctl_fsm_states, + .num_states = ARRAY_SIZE(l1ctl_fsm_states), + .log_subsys = DL1C, +}; static int l1ctl_link_read_cb(struct osmo_fd *bfd) { @@ -154,7 +175,9 @@ static int l1ctl_link_accept(struct osmo_fd *bfd, unsigned int flags) return -1; } - /* TODO: switch the bridge to CONNECTED state */ + osmo_fsm_inst_dispatch(trxcon_fsm, L1CTL_EVENT_CONNECT, l1l); + osmo_fsm_inst_state_chg(l1l->fsm, L1CTL_STATE_CONNECTED, 0, 0); + LOGP(DL1C, LOGL_NOTICE, "L1CTL has a new connection\n"); return 0; @@ -199,7 +222,9 @@ int l1ctl_link_close_conn(struct l1ctl_link *l1l) /* Clear pending messages */ osmo_wqueue_clear(&l1l->wq); - /* TODO: switch the bridge to IDLE state */ + osmo_fsm_inst_dispatch(trxcon_fsm, L1CTL_EVENT_DISCONNECT, l1l); + osmo_fsm_inst_state_chg(l1l->fsm, L1CTL_STATE_IDLE, 0, 0); + return 0; } @@ -238,6 +263,11 @@ int l1ctl_link_init(struct l1ctl_link **l1l, const char *sock_path) */ l1l_new->wq.bfd.fd = -1; + /* Allocate a new dedicated state machine */ + osmo_fsm_register(&l1ctl_fsm); + l1l_new->fsm = osmo_fsm_inst_alloc(&l1ctl_fsm, l1l_new, + NULL, LOGL_DEBUG, sock_path); + *l1l = l1l_new; return 0; @@ -266,5 +296,6 @@ void l1ctl_link_shutdown(struct l1ctl_link *l1l) listen_bfd->fd = -1; } + osmo_fsm_inst_free(l1l->fsm); talloc_free(l1l); } diff --git a/src/host/trxcon/l1ctl_link.h b/src/host/trxcon/l1ctl_link.h index 417dc7570..620dde506 100644 --- a/src/host/trxcon/l1ctl_link.h +++ b/src/host/trxcon/l1ctl_link.h @@ -3,11 +3,18 @@ #include #include #include +#include #define L1CTL_LENGTH 256 #define L1CTL_HEADROOM 32 +enum l1ctl_fsm_states { + L1CTL_STATE_IDLE = 0, + L1CTL_STATE_CONNECTED, +}; + struct l1ctl_link { + struct osmo_fsm_inst *fsm; struct osmo_fd listen_bfd; struct osmo_wqueue wq; }; diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index 5b0b7b1cd..8f93b0a60 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -35,13 +35,49 @@ #include #include #include +#include #include +#include "trxcon.h" #include "trx_if.h" #include "logging.h" extern void *tall_trx_ctx; +extern struct osmo_fsm_inst *trxcon_fsm; + +static struct osmo_fsm_state trx_fsm_states[] = { + [TRX_STATE_OFFLINE] = { + .out_state_mask = ( + GEN_MASK(TRX_STATE_IDLE) | + GEN_MASK(TRX_STATE_RSP_WAIT)), + .name = "OFFLINE", + }, + [TRX_STATE_IDLE] = { + .out_state_mask = UINT32_MAX, + .name = "IDLE", + }, + [TRX_STATE_ACTIVE] = { + .out_state_mask = ( + GEN_MASK(TRX_STATE_IDLE) | + GEN_MASK(TRX_STATE_RSP_WAIT)), + .name = "ACTIVE", + }, + [TRX_STATE_RSP_WAIT] = { + .out_state_mask = ( + GEN_MASK(TRX_STATE_IDLE) | + GEN_MASK(TRX_STATE_ACTIVE) | + GEN_MASK(TRX_STATE_OFFLINE)), + .name = "RSP_WAIT", + }, +}; + +static struct osmo_fsm trx_fsm = { + .name = "trx_interface_fsm", + .states = trx_fsm_states, + .num_states = ARRAY_SIZE(trx_fsm_states), + .log_subsys = DTRX, +}; static int trx_udp_open(void *priv, struct osmo_fd *ofd, const char *host, uint16_t port_local, uint16_t port_remote, @@ -178,6 +214,12 @@ static void trx_ctrl_send(struct trx_instance *trx) LOGP(DTRX, LOGL_DEBUG, "Sending control '%s'\n", tcm->cmd); send(trx->trx_ofd_ctrl.fd, tcm->cmd, strlen(tcm->cmd) + 1, 0); + /* Trigger state machine */ + if (trx->fsm->state != TRX_STATE_RSP_WAIT) { + trx->prev_state = trx->fsm->state; + osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_RSP_WAIT, 0, 0); + } + /* Start expire timer */ trx->trx_ctrl_timer.data = trx; trx->trx_ctrl_timer.cb = trx_ctrl_timer_cb; @@ -186,10 +228,25 @@ static void trx_ctrl_send(struct trx_instance *trx) static void trx_ctrl_timer_cb(void *data) { + struct trx_instance *trx = (struct trx_instance *) data; + struct trx_ctrl_msg *tcm; + + /* Queue may be cleaned at this moment */ + if (llist_empty(&trx->trx_ctrl_list)) + return; + LOGP(DTRX, LOGL_NOTICE, "No response from transceiver...\n"); + tcm = llist_entry(trx->trx_ctrl_list.next, struct trx_ctrl_msg, list); + if (++tcm->retry_cnt > 3) { + LOGP(DTRX, LOGL_NOTICE, "Transceiver offline\n"); + osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_OFFLINE, 0, 0); + osmo_fsm_inst_dispatch(trxcon_fsm, TRX_EVENT_OFFLINE, trx); + return; + } + /* Attempt to send a command again */ - trx_ctrl_send((struct trx_instance *) data); + trx_ctrl_send(trx); } /* Add a new CTRL command to the trx_ctrl_list */ @@ -200,11 +257,7 @@ static int trx_ctrl_cmd(struct trx_instance *trx, int critical, int len, pending = 0; va_list ap; - /*if (!transceiver_available && !!strcmp(cmd, "POWEROFF")) { - LOGP(DTRX, LOGL_ERROR, "CTRL ignored: No clock from " - "transceiver, please fix!\n"); - return -EIO; - }*/ + /* TODO: make sure that transceiver online */ if (!llist_empty(&trx->trx_ctrl_list)) pending = 1; @@ -419,6 +472,18 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) goto rsp_error; } + /* Trigger state machine */ + if (!strncmp(tcm->cmd + 4, "POWERON", 7)) + osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_ACTIVE, 0, 0); + else if (!strncmp(tcm->cmd + 4, "POWEROFF", 8)) + osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_IDLE, 0, 0); + else if (!strncmp(tcm->cmd + 4, "ECHO", 4)) { + osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_IDLE, 0, 0); + osmo_fsm_inst_dispatch(trxcon_fsm, + TRX_EVENT_RESET_IND, trx); + } else + osmo_fsm_inst_state_chg(trx->fsm, trx->prev_state, 0, 0); + /* Remove command from list */ llist_del(&tcm->list); talloc_free(tcm); @@ -429,10 +494,8 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) return 0; rsp_error: - /** - * TODO: stop/freeze trxcon process - * or notify higher layers about the problem with L1 - */ + /* Notify higher layers about the problem */ + osmo_fsm_inst_dispatch(trxcon_fsm, TRX_EVENT_RSP_ERROR, trx); return -EIO; } @@ -512,6 +575,18 @@ int trx_if_data(struct trx_instance *trx, uint8_t tn, uint32_t fn, { uint8_t buf[256]; + /** + * We must be sure that we have clock, + * and we have sent all control data + * + * TODO: should we wait in TRX_STATE_RSP_WAIT state? + */ + if (trx->fsm->state != TRX_STATE_ACTIVE) { + LOGP(DTRX, LOGL_DEBUG, "Ignoring TX data, " + "transceiver isn't ready\n"); + return -EAGAIN; + } + LOGP(DTRX, LOGL_DEBUG, "TX burst tn=%u fn=%u pwr=%u\n", tn, fn, pwr); buf[0] = tn; @@ -524,17 +599,8 @@ int trx_if_data(struct trx_instance *trx, uint8_t tn, uint32_t fn, /* Copy ubits {0,1} */ memcpy(buf + 6, bits, 148); - /** - * TODO: is transceiver available??? - * - * We must be sure that we have clock, - * and we have sent all control data - * - * if (transceiver_available && llist_empty(&l1h->trx_ctrl_list)) - * send(l1h->trx_ofd_data.fd, buf, 154, 0); - * else - * LOGP(DTRX, LOGL_DEBUG, "Ignoring TX data, transceiver offline.\n"); - */ + /* Send data to transceiver */ + send(trx->trx_ofd_data.fd, buf, 154, 0); return 0; } @@ -546,6 +612,7 @@ int trx_if_data(struct trx_instance *trx, uint8_t tn, uint32_t fn, int trx_if_open(struct trx_instance **trx, const char *host, uint16_t port) { struct trx_instance *trx_new; + char *inst_name; int rc; LOGP(DTRX, LOGL_NOTICE, "Init transceiver interface\n"); @@ -576,6 +643,13 @@ int trx_if_open(struct trx_instance **trx, const char *host, uint16_t port) if (rc < 0) goto error; + /* Allocate a new dedicated state machine */ + osmo_fsm_register(&trx_fsm); + inst_name = talloc_asprintf(trx_new, "%s:%u", host, port); + trx_new->fsm = osmo_fsm_inst_alloc(&trx_fsm, trx_new, + NULL, LOGL_DEBUG, inst_name); + talloc_free(inst_name); + *trx = trx_new; return 0; @@ -587,10 +661,14 @@ error: } /* Flush pending control messages */ -static void trx_if_flush_ctrl(struct trx_instance *trx) +void trx_if_flush_ctrl(struct trx_instance *trx) { struct trx_ctrl_msg *tcm; + /* Reset state machine */ + osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_IDLE, 0, 0); + + /* Clear command queue */ while (!llist_empty(&trx->trx_ctrl_list)) { tcm = llist_entry(trx->trx_ctrl_list.next, struct trx_ctrl_msg, list); @@ -616,5 +694,6 @@ void trx_if_close(struct trx_instance *trx) trx_udp_close(&trx->trx_ofd_data); /* Free memory */ + osmo_fsm_inst_free(trx->fsm); talloc_free(trx); } diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h index 6f54b3e6e..a81da37ea 100644 --- a/src/host/trxcon/trx_if.h +++ b/src/host/trxcon/trx_if.h @@ -3,6 +3,14 @@ #include #include #include +#include + +enum trx_fsm_states { + TRX_STATE_OFFLINE = 0, + TRX_STATE_IDLE, + TRX_STATE_ACTIVE, + TRX_STATE_RSP_WAIT, +}; struct trx_instance { struct osmo_fd trx_ofd_clck; @@ -11,16 +19,20 @@ struct trx_instance { struct osmo_timer_list trx_ctrl_timer; struct llist_head trx_ctrl_list; + struct osmo_fsm_inst *fsm; + uint32_t prev_state; }; struct trx_ctrl_msg { struct llist_head list; char cmd[128]; + int retry_cnt; int critical; int cmd_len; }; int trx_if_open(struct trx_instance **trx, const char *host, uint16_t port); +void trx_if_flush_ctrl(struct trx_instance *trx); void trx_if_close(struct trx_instance *trx); int trx_if_cmd_poweron(struct trx_instance *trx); diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index 781942a63..5874560dd 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -29,12 +29,14 @@ #include #include +#include #include #include #include #include #include +#include "trxcon.h" #include "trx_if.h" #include "logging.h" #include "l1ctl_link.h" @@ -62,6 +64,62 @@ static struct { } app_data; void *tall_trx_ctx = NULL; +struct osmo_fsm_inst *trxcon_fsm; + +static void trxcon_fsm_idle_action(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + if (event == L1CTL_EVENT_CONNECT) + osmo_fsm_inst_state_chg(trxcon_fsm, TRXCON_STATE_MANAGED, 0, 0); +} + +static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + switch (event) { + case L1CTL_EVENT_DISCONNECT: + osmo_fsm_inst_state_chg(trxcon_fsm, TRXCON_STATE_IDLE, 0, 0); + + if (app_data.trx->fsm->state != TRX_STATE_OFFLINE) { + trx_if_flush_ctrl(app_data.trx); + trx_if_cmd_poweroff(app_data.trx); + } + break; + case TRX_EVENT_RESET_IND: + case TRX_EVENT_RSP_ERROR: + case TRX_EVENT_OFFLINE: + /* TODO: notify L2 & L3 about that */ + break; + default: + LOGPFSML(fi, LOGL_ERROR, "Unhandled event %u\n", event); + } +} + +static struct osmo_fsm_state trxcon_fsm_states[] = { + [TRXCON_STATE_IDLE] = { + .in_event_mask = GEN_MASK(L1CTL_EVENT_CONNECT), + .out_state_mask = GEN_MASK(TRXCON_STATE_MANAGED), + .name = "IDLE", + .action = trxcon_fsm_idle_action, + }, + [TRXCON_STATE_MANAGED] = { + .in_event_mask = ( + GEN_MASK(L1CTL_EVENT_DISCONNECT) | + GEN_MASK(TRX_EVENT_RESET_IND) | + GEN_MASK(TRX_EVENT_RSP_ERROR) | + GEN_MASK(TRX_EVENT_OFFLINE)), + .out_state_mask = GEN_MASK(TRXCON_STATE_IDLE), + .name = "MANAGED", + .action = trxcon_fsm_managed_action, + }, +}; + +static struct osmo_fsm trxcon_fsm_def = { + .name = "trxcon_app_fsm", + .states = trxcon_fsm_states, + .num_states = ARRAY_SIZE(trxcon_fsm_states), + .log_subsys = DAPP, +}; static void print_usage(const char *app) { @@ -175,6 +233,11 @@ int main(int argc, char **argv) /* Init logging system */ trx_log_init(app_data.debug_mask); + /* Allocate the application state machine */ + osmo_fsm_register(&trxcon_fsm_def); + trxcon_fsm = osmo_fsm_inst_alloc(&trxcon_fsm_def, tall_trx_ctx, + NULL, LOGL_DEBUG, "main"); + /* Init L1CTL server */ rc = l1ctl_link_init(&app_data.l1l, app_data.bind_socket); if (rc) @@ -203,6 +266,9 @@ exit: l1ctl_link_shutdown(app_data.l1l); trx_if_close(app_data.trx); + /* Shutdown main state machine */ + osmo_fsm_inst_free(trxcon_fsm); + /* Make Valgrind happy */ log_fini(); talloc_free(tall_trx_ctx); diff --git a/src/host/trxcon/trxcon.h b/src/host/trxcon/trxcon.h new file mode 100644 index 000000000..a7a3a65fe --- /dev/null +++ b/src/host/trxcon/trxcon.h @@ -0,0 +1,19 @@ +#pragma once + +#define GEN_MASK(state) (0x01 << state) + +enum trxcon_fsm_states { + TRXCON_STATE_IDLE = 0, + TRXCON_STATE_MANAGED, +}; + +enum trxcon_fsm_events { + /* L1CTL specific events */ + L1CTL_EVENT_CONNECT, + L1CTL_EVENT_DISCONNECT, + + /* TRX specific events */ + TRX_EVENT_RESET_IND, + TRX_EVENT_RSP_ERROR, + TRX_EVENT_OFFLINE, +}; From 90a0d3c78dbebc9722629c31dd8fcdf19c148cb4 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 25 Jul 2016 00:20:37 +0600 Subject: [PATCH 007/211] host/trxcon: initial release of L1CTL handlers Now it's possible to handle the following requests from layer23 apps: - L1CTL_FBSB_REQ - L1CTL_PM_REQ - L1CTL_RESET_REQ - L1CTL_ECHO_REQ It should be noted, that the L1CTL_PM_REQ isn't handled correctly yet, due to required task isn't implemented on the TRX side yet. Instead of this, temporary we are sending some fake responses. Change-Id: I343eca3e20922ddd83e06231811200b26da442f3 --- src/host/trxcon/Makefile.am | 1 + src/host/trxcon/l1ctl.c | 253 ++++++++++++++++++++++++++++++++++ src/host/trxcon/l1ctl.h | 12 ++ src/host/trxcon/l1ctl_link.c | 5 +- src/host/trxcon/l1ctl_proto.h | 1 + src/host/trxcon/trxcon.c | 18 +++ src/host/trxcon/trxcon.h | 2 + 7 files changed, 290 insertions(+), 2 deletions(-) create mode 100644 src/host/trxcon/l1ctl.c create mode 100644 src/host/trxcon/l1ctl.h create mode 120000 src/host/trxcon/l1ctl_proto.h diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am index 9da21996e..869ed8ba5 100644 --- a/src/host/trxcon/Makefile.am +++ b/src/host/trxcon/Makefile.am @@ -22,6 +22,7 @@ bin_PROGRAMS = trxcon trxcon_SOURCES = \ l1ctl_link.c \ + l1ctl.c \ trx_if.c \ logging.c \ trxcon.c \ diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c new file mode 100644 index 000000000..26670f1f4 --- /dev/null +++ b/src/host/trxcon/l1ctl.c @@ -0,0 +1,253 @@ +/* + * OsmocomBB <-> SDR connection bridge + * GSM L1 control interface handlers + * + * (C) 2014 by Sylvain Munaut + * (C) 2016-2017 by Vadim Yanitskiy + * + * 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 +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "trxcon.h" +#include "logging.h" +#include "l1ctl_link.h" +#include "l1ctl_proto.h" + +extern void *tall_trx_ctx; +extern struct osmo_fsm_inst *trxcon_fsm; + +static struct msgb *l1ctl_alloc_msg(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 l1ctl_pm_conf *pmc; + struct msgb *msg; + + msg = l1ctl_alloc_msg(L1CTL_PM_CONF); + if (!msg) + return -ENOMEM; + + LOGP(DL1C, LOGL_DEBUG, "Send 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 l1ctl_link_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_msg(L1CTL_RESET_IND); + if (!msg) + return -ENOMEM; + + LOGP(DL1C, LOGL_DEBUG, "Send Reset Ind (%u)\n", type); + + res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res)); + res->type = type; + + return l1ctl_link_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_msg(L1CTL_RESET_CONF); + if (!msg) + return -ENOMEM; + + LOGP(DL1C, LOGL_DEBUG, "Send Reset Conf (%u)\n", type); + res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res)); + res->type = type; + + return l1ctl_link_send(l1l, msg); +} + +static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg) +{ + struct l1ctl_fbsb_req *fbsb; + uint16_t band_arfcn; + int rc = 0; + + 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, "Recv FBSB Req (%s %d)\n", + gsm_band_name(gsm_arfcn2band(band_arfcn)), + band_arfcn &~ ARFCN_FLAG_MASK); + + osmo_fsm_inst_dispatch(trxcon_fsm, + L1CTL_EVENT_FBSB_REQ, &band_arfcn); + +exit: + msgb_free(msg); + return rc; +} + +static int l1ctl_rx_pm_req(struct l1ctl_link *l1l, struct msgb *msg) +{ + uint16_t arfcn_start, arfcn_stop, arfcn; + struct l1ctl_pm_req *pmr; + int rc = 0; + + 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, "Recv PM Req (%s: %d -> %d)\n", + gsm_band_name(gsm_arfcn2band(arfcn_start)), + arfcn_start &~ ARFCN_FLAG_MASK, + arfcn_stop &~ ARFCN_FLAG_MASK); + + /** + * HACK: power measurement isn't implemented yet, + * sending fake results for now... + * + * FIXME: l1ctl_link.c:203 Failed to enqueue msg! + * l1l->wq size is limited to 100, so we cannot + * put more messages until osmo_select_main() + * is called. + */ + for (arfcn = arfcn_start; arfcn <= arfcn_stop; arfcn++) + l1ctl_tx_pm_conf(l1l, arfcn, arfcn == 33 ? + -60 : -120, arfcn == arfcn_stop); + +exit: + msgb_free(msg); + return rc; +} + +static int l1ctl_rx_reset_req(struct l1ctl_link *l1l, struct msgb *msg) +{ + struct l1ctl_reset *res; + int rc = 0; + + 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, "Recv Reset Req (%u)\n", res->type); + + osmo_fsm_inst_dispatch(trxcon_fsm, + L1CTL_EVENT_RESET_REQ, res); + +exit: + msgb_free(msg); + return rc; +} + +static int l1ctl_rx_echo_req(struct l1ctl_link *l1l, struct msgb *msg) +{ + struct l1ctl_hdr *l1h; + + LOGP(DL1C, LOGL_NOTICE, "Recv Echo Req\n"); + LOGP(DL1C, LOGL_NOTICE, "Send Echo Conf\n"); + + /* Nothing to do, just send it back */ + l1h = (struct l1ctl_hdr *) msg->l1h; + l1h->msg_type = L1CTL_ECHO_CONF; + msg->data = msg->l1h; + + return l1ctl_link_send(l1l, msg); +} + +int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) +{ + struct l1ctl_hdr *l1h; + + l1h = (struct l1ctl_hdr *) msg->l1h; + msg->l1h = l1h->data; + + switch (l1h->msg_type) { + case L1CTL_FBSB_REQ: + return l1ctl_rx_fbsb_req(l1l, msg); + case L1CTL_PM_REQ: + return l1ctl_rx_pm_req(l1l, msg); + case L1CTL_RESET_REQ: + return l1ctl_rx_reset_req(l1l, msg); + case L1CTL_ECHO_REQ: + return l1ctl_rx_echo_req(l1l, msg); + default: + LOGP(DL1C, LOGL_ERROR, "Unknown MSG: %u\n", l1h->msg_type); + msgb_free(msg); + return -EINVAL; + } +} diff --git a/src/host/trxcon/l1ctl.h b/src/host/trxcon/l1ctl.h new file mode 100644 index 000000000..ffd1a81f2 --- /dev/null +++ b/src/host/trxcon/l1ctl.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +#include "l1ctl_link.h" + +int l1ctl_tx_pm_conf(struct l1ctl_link *l1l, uint16_t band_arfcn, + int dbm, int last); +int l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type); +int l1ctl_tx_reset_ind(struct l1ctl_link *l1l, uint8_t type); +int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg); diff --git a/src/host/trxcon/l1ctl_link.c b/src/host/trxcon/l1ctl_link.c index 34aa4aaf8..d2ceb177e 100644 --- a/src/host/trxcon/l1ctl_link.c +++ b/src/host/trxcon/l1ctl_link.c @@ -43,6 +43,7 @@ #include "trxcon.h" #include "logging.h" #include "l1ctl_link.h" +#include "l1ctl.h" extern void *tall_trx_ctx; extern struct osmo_fsm_inst *trxcon_fsm; @@ -112,8 +113,8 @@ static int l1ctl_link_read_cb(struct osmo_fd *bfd) LOGP(DL1C, LOGL_DEBUG, "RX: '%s'\n", osmo_hexdump(msg->data, msg->len)); - /* TODO: call L1CTL handler here */ - msgb_free(msg); + /* Call L1CTL handler */ + l1ctl_rx_cb(l1l, msg); return 0; } diff --git a/src/host/trxcon/l1ctl_proto.h b/src/host/trxcon/l1ctl_proto.h new file mode 120000 index 000000000..75862baeb --- /dev/null +++ b/src/host/trxcon/l1ctl_proto.h @@ -0,0 +1 @@ +../../../include/l1ctl_proto.h \ No newline at end of file diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index 5874560dd..a90d038df 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -39,7 +39,9 @@ #include "trxcon.h" #include "trx_if.h" #include "logging.h" +#include "l1ctl.h" #include "l1ctl_link.h" +#include "l1ctl_proto.h" #define COPYRIGHT \ "Copyright (C) 2016-2017 by Vadim Yanitskiy \n" \ @@ -76,6 +78,8 @@ static void trxcon_fsm_idle_action(struct osmo_fsm_inst *fi, static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) { + uint16_t *band_arfcn; + switch (event) { case L1CTL_EVENT_DISCONNECT: osmo_fsm_inst_state_chg(trxcon_fsm, TRXCON_STATE_IDLE, 0, 0); @@ -85,7 +89,19 @@ static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi, trx_if_cmd_poweroff(app_data.trx); } break; + case L1CTL_EVENT_RESET_REQ: + trx_if_cmd_echo(app_data.trx); + break; case TRX_EVENT_RESET_IND: + /* TODO: send proper reset type */ + l1ctl_tx_reset_conf(app_data.l1l, L1CTL_RES_T_BOOT); + break; + case L1CTL_EVENT_FBSB_REQ: + band_arfcn = (uint16_t *) data; + trx_if_cmd_rxtune(app_data.trx, *band_arfcn); + trx_if_cmd_txtune(app_data.trx, *band_arfcn); + trx_if_cmd_poweron(app_data.trx); + break; case TRX_EVENT_RSP_ERROR: case TRX_EVENT_OFFLINE: /* TODO: notify L2 & L3 about that */ @@ -105,6 +121,8 @@ static struct osmo_fsm_state trxcon_fsm_states[] = { [TRXCON_STATE_MANAGED] = { .in_event_mask = ( GEN_MASK(L1CTL_EVENT_DISCONNECT) | + GEN_MASK(L1CTL_EVENT_FBSB_REQ) | + GEN_MASK(L1CTL_EVENT_RESET_REQ) | GEN_MASK(TRX_EVENT_RESET_IND) | GEN_MASK(TRX_EVENT_RSP_ERROR) | GEN_MASK(TRX_EVENT_OFFLINE)), diff --git a/src/host/trxcon/trxcon.h b/src/host/trxcon/trxcon.h index a7a3a65fe..9535578e7 100644 --- a/src/host/trxcon/trxcon.h +++ b/src/host/trxcon/trxcon.h @@ -11,6 +11,8 @@ enum trxcon_fsm_events { /* L1CTL specific events */ L1CTL_EVENT_CONNECT, L1CTL_EVENT_DISCONNECT, + L1CTL_EVENT_FBSB_REQ, + L1CTL_EVENT_RESET_REQ, /* TRX specific events */ TRX_EVENT_RESET_IND, From 9b1d398685e70e3bc9ed2fa1a07209a24e2b31b6 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 9 Jun 2017 01:10:05 +0700 Subject: [PATCH 008/211] host/trxcon/scheduler: add basic clock counter The core of scheduler is a simple clock counter, which relays on system time for now. One was a bit simplified and migrated from OsmoBTS. Due to system time is not an ideal clock source, the counter should be periodically corrected by clock indications from BTS. Change-Id: I27d85bd3e2c8bca3f876f73517027b9fe43c9825 --- src/host/layer23/.gitignore | 1 - src/host/trxcon/.gitignore | 1 + src/host/trxcon/Makefile.am | 5 + src/host/trxcon/logging.c | 6 + src/host/trxcon/logging.h | 3 +- src/host/trxcon/sched_clck.c | 209 +++++++++++++++++++++++++++++++++++ src/host/trxcon/scheduler.h | 37 +++++++ src/host/trxcon/trxcon.c | 6 +- src/host/trxcon/trxcon.h | 4 + 9 files changed, 269 insertions(+), 3 deletions(-) create mode 100644 src/host/trxcon/sched_clck.c create mode 100644 src/host/trxcon/scheduler.h diff --git a/src/host/layer23/.gitignore b/src/host/layer23/.gitignore index 8fb93f735..59601be89 100644 --- a/src/host/layer23/.gitignore +++ b/src/host/layer23/.gitignore @@ -19,7 +19,6 @@ config.status # build by-products *.o -*.a # various *.sw? diff --git a/src/host/trxcon/.gitignore b/src/host/trxcon/.gitignore index d6b28ee9d..fe90e43c1 100644 --- a/src/host/trxcon/.gitignore +++ b/src/host/trxcon/.gitignore @@ -18,6 +18,7 @@ version.h # build by-products *.o +*.a trxcon diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am index 869ed8ba5..de1202974 100644 --- a/src/host/trxcon/Makefile.am +++ b/src/host/trxcon/Makefile.am @@ -28,6 +28,11 @@ trxcon_SOURCES = \ trxcon.c \ $(NULL) +# Scheduler +trxcon_SOURCES += \ + sched_clck.c \ + $(NULL) + trxcon_LDADD = \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ diff --git a/src/host/trxcon/logging.c b/src/host/trxcon/logging.c index 28e6776b3..3381c6a4c 100644 --- a/src/host/trxcon/logging.c +++ b/src/host/trxcon/logging.c @@ -46,6 +46,12 @@ static struct log_info_cat trx_log_info_cat[] = { .color = "\033[1;33m", .enabled = 1, .loglevel = LOGL_NOTICE, }, + [DSCH] = { + .name = "DSCH", + .description = "Scheduler", + .color = "\033[1;36m", + .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 4d7cea063..52afd4baf 100644 --- a/src/host/trxcon/logging.h +++ b/src/host/trxcon/logging.h @@ -2,12 +2,13 @@ #include -#define DEBUG_DEFAULT "DAPP:DL1C:DTRX" +#define DEBUG_DEFAULT "DAPP:DL1C:DTRX:DSCH" enum { DAPP, DL1C, DTRX, + DSCH, }; int trx_log_init(const char *category_mask); diff --git a/src/host/trxcon/sched_clck.c b/src/host/trxcon/sched_clck.c new file mode 100644 index 000000000..31f3ef2c6 --- /dev/null +++ b/src/host/trxcon/sched_clck.c @@ -0,0 +1,209 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: clock synchronization + * + * (C) 2013 by Andreas Eversberg + * (C) 2015 by Alexander Chemeris + * (C) 2015 by Harald Welte + * + * 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 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 "scheduler.h" +#include "logging.h" +#include "trx_if.h" +#include "trxcon.h" + +#define FRAME_DURATION_uS 4615 +#define MAX_FN_SKEW 50 +#define TRX_LOSS_FRAMES 400 + +extern struct osmo_fsm_inst *trxcon_fsm; + +static void sched_clck_tick(void *data) +{ + struct trx_sched *sched = (struct trx_sched *) data; + struct trx_instance *trx = (struct trx_instance *) sched->data; + + struct timeval tv_now, *tv_clock; + int32_t elapsed; + + /* Check if transceiver is still alive */ + if (sched->fn_counter_lost++ == TRX_LOSS_FRAMES) { + LOGP(DSCH, LOGL_NOTICE, "No more clock from transceiver\n"); + + osmo_fsm_inst_dispatch(trxcon_fsm, SCH_EVENT_CLCK_LOSS, trx); + sched->state = SCH_CLCK_STATE_WAIT; + + return; + } + + /* Get actual / previous frame time */ + gettimeofday(&tv_now, NULL); + tv_clock = &sched->clock; + + elapsed = (tv_now.tv_sec - tv_clock->tv_sec) * 1000000 + + (tv_now.tv_usec - tv_clock->tv_usec); + + /* If someone played with clock, or if the process stalled */ + if (elapsed > FRAME_DURATION_uS * MAX_FN_SKEW || elapsed < 0) { + LOGP(DSCH, LOGL_NOTICE, "PC clock skew: " + "elapsed uS %d\n", elapsed); + + osmo_fsm_inst_dispatch(trxcon_fsm, SCH_EVENT_CLCK_LOSS, trx); + sched->state = SCH_CLCK_STATE_WAIT; + + return; + } + + /* Schedule next FN clock */ + while (elapsed > FRAME_DURATION_uS / 2) { + tv_clock->tv_usec += FRAME_DURATION_uS; + elapsed -= FRAME_DURATION_uS; + + if (tv_clock->tv_usec >= 1000000) { + tv_clock->tv_sec++; + tv_clock->tv_usec -= 1000000; + } + + sched->fn_counter_proc = (sched->fn_counter_proc + 1) + % GSM_HYPERFRAME; + + /* Call frame callback */ + if (sched->clock_cb) + sched->clock_cb(sched); + } + + osmo_timer_schedule(&sched->clock_timer, 0, + FRAME_DURATION_uS - elapsed); +} + +static void sched_clck_correct(struct trx_sched *sched, + struct timeval *tv_now, uint32_t fn) +{ + sched->fn_counter_proc = fn; + + /* Call frame callback */ + if (sched->clock_cb) + sched->clock_cb(sched); + + /* Schedule first FN clock */ + memcpy(&sched->clock, tv_now, sizeof(struct timeval)); + memset(&sched->clock_timer, 0, sizeof(sched->clock_timer)); + + sched->clock_timer.cb = sched_clck_tick; + sched->clock_timer.data = sched; + osmo_timer_schedule(&sched->clock_timer, 0, FRAME_DURATION_uS); +} + +int sched_clck_handle(struct trx_sched *sched, uint32_t fn) +{ + struct trx_instance *trx = (struct trx_instance *) sched->data; + struct timeval tv_now, *tv_clock; + int32_t elapsed, elapsed_fn; + + /* Reset lost counter */ + sched->fn_counter_lost = 0; + + /* Get actual / previous frame time */ + gettimeofday(&tv_now, NULL); + tv_clock = &sched->clock; + + /* If this is the first CLCK IND */ + if (sched->state == SCH_CLCK_STATE_WAIT) { + sched_clck_correct(sched, &tv_now, fn); + + LOGP(DSCH, LOGL_NOTICE, "Initial clock received: fn=%u\n", fn); + osmo_fsm_inst_dispatch(trxcon_fsm, SCH_EVENT_CLCK_IND, trx); + sched->state = SCH_CLCK_STATE_OK; + + return 0; + } + + osmo_timer_del(&sched->clock_timer); + + /* Calculate elapsed time / frames since last processed fn */ + elapsed = (tv_now.tv_sec - tv_clock->tv_sec) * 1000000 + + (tv_now.tv_usec - tv_clock->tv_usec); + elapsed_fn = (fn + GSM_HYPERFRAME - sched->fn_counter_proc) + % GSM_HYPERFRAME; + + if (elapsed_fn >= 135774) + elapsed_fn -= GSM_HYPERFRAME; + + /* Check for max clock skew */ + if (elapsed_fn > MAX_FN_SKEW || elapsed_fn < -MAX_FN_SKEW) { + LOGP(DSCH, LOGL_NOTICE, "GSM clock skew: old fn=%u, " + "new fn=%u\n", sched->fn_counter_proc, fn); + + sched_clck_correct(sched, &tv_now, fn); + return 0; + } + + LOGP(DSCH, LOGL_INFO, "GSM clock jitter: %d\n", + elapsed_fn * FRAME_DURATION_uS - elapsed); + + /* Too many frames have been processed already */ + if (elapsed_fn < 0) { + /** + * Set clock to the time or last FN should + * have been transmitted + */ + tv_clock->tv_sec = tv_now.tv_sec; + tv_clock->tv_usec = tv_now.tv_usec + + (0 - elapsed_fn) * FRAME_DURATION_uS; + + if (tv_clock->tv_usec >= 1000000) { + tv_clock->tv_sec++; + tv_clock->tv_usec -= 1000000; + } + + /* Set time to the time our next FN has to be transmitted */ + osmo_timer_schedule(&sched->clock_timer, 0, + FRAME_DURATION_uS * (1 - elapsed_fn)); + + return 0; + } + + /* Transmit what we still need to transmit */ + while (fn != sched->fn_counter_proc) { + sched->fn_counter_proc = (sched->fn_counter_proc + 1) + % GSM_HYPERFRAME; + + /* Call frame callback */ + if (sched->clock_cb) + sched->clock_cb(sched); + } + + /* Schedule next FN to be transmitted */ + memcpy(tv_clock, &tv_now, sizeof(struct timeval)); + osmo_timer_schedule(&sched->clock_timer, 0, FRAME_DURATION_uS); + + return 0; +} diff --git a/src/host/trxcon/scheduler.h b/src/host/trxcon/scheduler.h new file mode 100644 index 000000000..0783e40aa --- /dev/null +++ b/src/host/trxcon/scheduler.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +#include + +#define GSM_SUPERFRAME (26 * 51) +#define GSM_HYPERFRAME (2048 * GSM_SUPERFRAME) + +enum tdma_sched_clck_state { + SCH_CLCK_STATE_WAIT, + SCH_CLCK_STATE_OK, +}; + +/* Forward structure declaration */ +struct trx_sched; + +/*! \brief One scheduler instance */ +struct trx_sched { + /*! \brief Clock state */ + uint8_t state; + /*! \brief Local clock source */ + struct timeval clock; + /*! \brief Count of processed frames */ + uint32_t fn_counter_proc; + /*! \brief Frame counter */ + uint32_t fn_counter_lost; + /*! \brief Frame callback timer */ + struct osmo_timer_list clock_timer; + /*! \brief Frame callback */ + void (*clock_cb)(struct trx_sched *sched); + /*! \brief Private data (e.g. pointer to trx instance) */ + void *data; +}; + +int sched_clck_handle(struct trx_sched *sched, uint32_t fn); diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index a90d038df..ace2f79af 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -104,6 +104,8 @@ static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi, break; case TRX_EVENT_RSP_ERROR: case TRX_EVENT_OFFLINE: + case SCH_EVENT_CLCK_IND: + case SCH_EVENT_CLCK_LOSS: /* TODO: notify L2 & L3 about that */ break; default: @@ -125,7 +127,9 @@ static struct osmo_fsm_state trxcon_fsm_states[] = { GEN_MASK(L1CTL_EVENT_RESET_REQ) | GEN_MASK(TRX_EVENT_RESET_IND) | GEN_MASK(TRX_EVENT_RSP_ERROR) | - GEN_MASK(TRX_EVENT_OFFLINE)), + GEN_MASK(TRX_EVENT_OFFLINE) | + GEN_MASK(SCH_EVENT_CLCK_IND) | + GEN_MASK(SCH_EVENT_CLCK_LOSS)), .out_state_mask = GEN_MASK(TRXCON_STATE_IDLE), .name = "MANAGED", .action = trxcon_fsm_managed_action, diff --git a/src/host/trxcon/trxcon.h b/src/host/trxcon/trxcon.h index 9535578e7..c266eaee2 100644 --- a/src/host/trxcon/trxcon.h +++ b/src/host/trxcon/trxcon.h @@ -18,4 +18,8 @@ enum trxcon_fsm_events { TRX_EVENT_RESET_IND, TRX_EVENT_RSP_ERROR, TRX_EVENT_OFFLINE, + + /* Scheduler specific events */ + SCH_EVENT_CLCK_IND, + SCH_EVENT_CLCK_LOSS, }; From 789040f91405d1fe9181a9e10b5b169c1177682d Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 9 Jun 2017 20:53:20 +0700 Subject: [PATCH 009/211] host/trxcon/trx_if.c: handle clock indications Change-Id: I333c1c44578eb62c52d2d059b798dd5feae3e444 --- src/host/trxcon/trx_if.c | 6 +++++- src/host/trxcon/trx_if.h | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index 8f93b0a60..83db173a3 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -42,6 +42,7 @@ #include "trxcon.h" #include "trx_if.h" #include "logging.h" +#include "scheduler.h" extern void *tall_trx_ctx; extern struct osmo_fsm_inst *trxcon_fsm; @@ -146,6 +147,7 @@ static void trx_udp_close(struct osmo_fd *ofd) static int trx_clck_read_cb(struct osmo_fd *ofd, unsigned int what) { + struct trx_instance *trx = (struct trx_instance *) ofd->data; char buf[1500]; uint32_t fn; int len; @@ -173,7 +175,9 @@ static int trx_clck_read_cb(struct osmo_fd *ofd, unsigned int what) "correctly, correcting to fn=%u\n", fn); } - /* TODO: call the clck_ind callback */ + /* Call the clck_ind callback */ + sched_clck_handle(&trx->sched, fn); + return 0; } diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h index a81da37ea..e41d506bc 100644 --- a/src/host/trxcon/trx_if.h +++ b/src/host/trxcon/trx_if.h @@ -5,6 +5,8 @@ #include #include +#include "scheduler.h" + enum trx_fsm_states { TRX_STATE_OFFLINE = 0, TRX_STATE_IDLE, @@ -21,6 +23,9 @@ struct trx_instance { struct llist_head trx_ctrl_list; struct osmo_fsm_inst *fsm; uint32_t prev_state; + + /* Scheduler stuff */ + struct trx_sched sched; }; struct trx_ctrl_msg { From 2e18fe463176702fd38f933f8691580fbc28cd9d Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 4 Jul 2017 19:38:50 +0700 Subject: [PATCH 010/211] host/trxcon/scheduler: add basic GSM PHY definitions Change-Id: I9f4faa15d5ca61af3e3fdbb95952ab4e4e0b7a4b --- src/host/trxcon/Makefile.am | 2 + src/host/trxcon/sched_lchan_desc.c | 281 +++++ src/host/trxcon/sched_mframe.c | 1814 ++++++++++++++++++++++++++++ src/host/trxcon/sched_trx.h | 235 ++++ 4 files changed, 2332 insertions(+) create mode 100644 src/host/trxcon/sched_lchan_desc.c create mode 100644 src/host/trxcon/sched_mframe.c create mode 100644 src/host/trxcon/sched_trx.h diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am index de1202974..a5008e6f5 100644 --- a/src/host/trxcon/Makefile.am +++ b/src/host/trxcon/Makefile.am @@ -30,6 +30,8 @@ trxcon_SOURCES = \ # Scheduler trxcon_SOURCES += \ + sched_lchan_desc.c \ + sched_mframe.c \ sched_clck.c \ $(NULL) diff --git a/src/host/trxcon/sched_lchan_desc.c b/src/host/trxcon/sched_lchan_desc.c new file mode 100644 index 000000000..880c2a592 --- /dev/null +++ b/src/host/trxcon/sched_lchan_desc.c @@ -0,0 +1,281 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: logical channels, RX / TX handlers + * + * (C) 2013 by Andreas Eversberg + * (C) 2015 by Alexander Chemeris + * (C) 2015 by Harald Welte + * + * 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 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 "sched_trx.h" + +#define LID_DEDIC 0x00 +#define LID_SACCH 0x40 + +/* TODO: implement */ +#define tx_data_fn NULL +#define tx_pdtch_fn NULL +#define tx_tchf_fn NULL +#define tx_tchh_fn NULL +#define tx_rach_fn NULL + +#define rx_data_fn NULL +#define rx_pdtch_fn NULL +#define rx_tchf_fn NULL +#define rx_tchh_fn NULL + +const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { + { + TRXC_IDLE, "IDLE", + 0x00, LID_DEDIC, + 0x00, 0x00, + + /** + * MS: do nothing, save power... + * BTS: send dummy burst on C0 + */ + NULL, NULL, + }, + { + TRXC_FCCH, "FCCH", + 0x00, LID_DEDIC, + 0x00, 0x00, + + /* FCCH is handled by transceiver */ + NULL, NULL, + }, + { + TRXC_SCH, "SCH", + 0x00, LID_DEDIC, + 0x00, 0x00, + + /* We already have clock indications from TRX */ + NULL, NULL, + }, + { + TRXC_BCCH, "BCCH", + 0x80, LID_DEDIC, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_AUTO, + rx_data_fn, NULL, + }, + { + TRXC_RACH, "RACH", + 0x88, LID_DEDIC, + 0x00, TRX_CH_FLAG_AUTO, + NULL, tx_rach_fn, + }, + { + TRXC_CCCH, "CCCH", + 0x90, LID_DEDIC, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_AUTO, + rx_data_fn, NULL, + }, + { + TRXC_TCHF, "TCH/F", + 0x08, LID_DEDIC, + 8 * GSM_BURST_PL_LEN, 0x00, + rx_tchf_fn, tx_tchf_fn, + }, + { + TRXC_TCHH_0, "TCH/H(0)", + 0x10, LID_DEDIC, + 6 * GSM_BURST_PL_LEN, 0x00, + rx_tchh_fn, tx_tchh_fn, + }, + { + TRXC_TCHH_1, "TCH/H(1)", + 0x18, LID_DEDIC, + 6 * GSM_BURST_PL_LEN, 0x00, + rx_tchh_fn, tx_tchh_fn, + }, + { + TRXC_SDCCH4_0, "SDCCH/4(0)", + 0x20, LID_DEDIC, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SDCCH4_1, "SDCCH/4(1)", + 0x28, LID_DEDIC, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SDCCH4_2, "SDCCH/4(2)", + 0x30, LID_DEDIC, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SDCCH4_3, "SDCCH/4(3)", + 0x38, LID_DEDIC, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SDCCH8_0, "SDCCH/8(0)", + 0x40, LID_DEDIC, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SDCCH8_1, "SDCCH/8(1)", + 0x48, LID_DEDIC, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SDCCH8_2, "SDCCH/8(2)", + 0x50, LID_DEDIC, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SDCCH8_3, "SDCCH/8(3)", + 0x58, LID_DEDIC, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SDCCH8_4, "SDCCH/8(4)", + 0x60, LID_DEDIC, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SDCCH8_5, "SDCCH/8(5)", + 0x68, LID_DEDIC, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SDCCH8_6, "SDCCH/8(6)", + 0x70, LID_DEDIC, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SDCCH8_7, "SDCCH/8(7)", + 0x78, LID_DEDIC, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SACCHTF, "SACCH/TF", + 0x08, LID_SACCH, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SACCHTH_0, "SACCH/TH(0)", + 0x10, LID_SACCH, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SACCHTH_1, "SACCH/TH(1)", + 0x18, LID_SACCH, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SACCH4_0, "SACCH/4(0)", + 0x20, LID_SACCH, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SACCH4_1, "SACCH/4(1)", + 0x28, LID_SACCH, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SACCH4_2, "SACCH/4(2)", + 0x30, LID_SACCH, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SACCH4_3, "SACCH/4(3)", + 0x38, LID_SACCH, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SACCH8_0, "SACCH/8(0)", + 0x40, LID_SACCH, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SACCH8_1, "SACCH/8(1)", + 0x48, LID_SACCH, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SACCH8_2, "SACCH/8(2)", + 0x50, LID_SACCH, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SACCH8_3, "SACCH/8(3)", + 0x58, LID_SACCH, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SACCH8_4, "SACCH/8(4)", + 0x60, LID_SACCH, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SACCH8_5, "SACCH/8(5)", + 0x68, LID_SACCH, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SACCH8_6, "SACCH/8(6)", + 0x70, LID_SACCH, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_SACCH8_7, "SACCH/8(7)", + 0x78, LID_SACCH, + 4 * GSM_BURST_PL_LEN, 0x00, + rx_data_fn, tx_data_fn, + }, + { + TRXC_PDTCH, "PDTCH", + 0x08, LID_DEDIC, + 12 * GSM_BURST_PL_LEN, TRX_CH_FLAG_PDCH, + rx_pdtch_fn, tx_pdtch_fn, + }, + { + TRXC_PTCCH, "PTCCH", + 0x08, LID_DEDIC, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_PDCH, + rx_data_fn, tx_data_fn, + }, +}; diff --git a/src/host/trxcon/sched_mframe.c b/src/host/trxcon/sched_mframe.c new file mode 100644 index 000000000..5f9d78fd3 --- /dev/null +++ b/src/host/trxcon/sched_mframe.c @@ -0,0 +1,1814 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: channel combinations, burst mapping + * + * (C) 2013 by Andreas Eversberg + * (C) 2015 by Alexander Chemeris + * (C) 2015 by Harald Welte + * + * 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 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 "sched_trx.h" + +/* Non-combined CCCH */ +static const struct trx_frame frame_bcch[51] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_FCCH, 0, TRXC_RACH, 0 }, + { TRXC_SCH, 0, TRXC_RACH, 0 }, + { TRXC_BCCH, 0, TRXC_RACH, 0 }, + { TRXC_BCCH, 1, TRXC_RACH, 0 }, + { TRXC_BCCH, 2, TRXC_RACH, 0 }, + { TRXC_BCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_FCCH, 0, TRXC_RACH, 0 }, + { TRXC_SCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_FCCH, 0, TRXC_RACH, 0 }, + { TRXC_SCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_FCCH, 0, TRXC_RACH, 0 }, + { TRXC_SCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_FCCH, 0, TRXC_RACH, 0 }, + { TRXC_SCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_IDLE, 0, TRXC_RACH, 0 }, +}; + +/* Combined CCCH+SDCCH4 */ +static const struct trx_frame frame_bcch_sdcch4[102] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_FCCH, 0, TRXC_SDCCH4_3, 0 }, + { TRXC_SCH, 0, TRXC_SDCCH4_3, 1 }, + { TRXC_BCCH, 0, TRXC_SDCCH4_3, 2 }, + { TRXC_BCCH, 1, TRXC_SDCCH4_3, 3 }, + { TRXC_BCCH, 2, TRXC_RACH, 0 }, + { TRXC_BCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_SACCH4_2, 0 }, + { TRXC_CCCH, 1, TRXC_SACCH4_2, 1 }, + { TRXC_CCCH, 2, TRXC_SACCH4_2, 2 }, + { TRXC_CCCH, 3, TRXC_SACCH4_2, 3 }, + { TRXC_FCCH, 0, TRXC_SACCH4_3, 0 }, + { TRXC_SCH, 0, TRXC_SACCH4_3, 1 }, + { TRXC_CCCH, 0, TRXC_SACCH4_3, 2 }, + { TRXC_CCCH, 1, TRXC_SACCH4_3, 3 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_FCCH, 0, TRXC_RACH, 0 }, + { TRXC_SCH, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_0, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_0, 1, TRXC_RACH, 0 }, + { TRXC_SDCCH4_0, 2, TRXC_RACH, 0 }, + { TRXC_SDCCH4_0, 3, TRXC_RACH, 0 }, + { TRXC_SDCCH4_1, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_1, 1, TRXC_RACH, 0 }, + { TRXC_SDCCH4_1, 2, TRXC_RACH, 0 }, + { TRXC_SDCCH4_1, 3, TRXC_RACH, 0 }, + { TRXC_FCCH, 0, TRXC_RACH, 0 }, + { TRXC_SCH, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_2, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_2, 1, TRXC_RACH, 0 }, + { TRXC_SDCCH4_2, 2, TRXC_RACH, 0 }, + { TRXC_SDCCH4_2, 3, TRXC_RACH, 0 }, + { TRXC_SDCCH4_3, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_3, 1, TRXC_SDCCH4_0, 0 }, + { TRXC_SDCCH4_3, 2, TRXC_SDCCH4_0, 1 }, + { TRXC_SDCCH4_3, 3, TRXC_SDCCH4_0, 2 }, + { TRXC_FCCH, 0, TRXC_SDCCH4_0, 3 }, + { TRXC_SCH, 0, TRXC_SDCCH4_1, 0 }, + { TRXC_SACCH4_0, 0, TRXC_SDCCH4_1, 1 }, + { TRXC_SACCH4_0, 1, TRXC_SDCCH4_1, 2 }, + { TRXC_SACCH4_0, 2, TRXC_SDCCH4_1, 3 }, + { TRXC_SACCH4_0, 3, TRXC_RACH, 0 }, + { TRXC_SACCH4_1, 0, TRXC_RACH, 0 }, + { TRXC_SACCH4_1, 1, TRXC_SDCCH4_2, 0 }, + { TRXC_SACCH4_1, 2, TRXC_SDCCH4_2, 1 }, + { TRXC_SACCH4_1, 3, TRXC_SDCCH4_2, 2 }, + { TRXC_IDLE, 0, TRXC_SDCCH4_2, 3 }, + + { TRXC_FCCH, 0, TRXC_SDCCH4_3, 0 }, + { TRXC_SCH, 0, TRXC_SDCCH4_3, 1 }, + { TRXC_BCCH, 0, TRXC_SDCCH4_3, 2 }, + { TRXC_BCCH, 1, TRXC_SDCCH4_3, 3 }, + { TRXC_BCCH, 2, TRXC_RACH, 0 }, + { TRXC_BCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_SACCH4_0, 0 }, + { TRXC_CCCH, 1, TRXC_SACCH4_0, 1 }, + { TRXC_CCCH, 2, TRXC_SACCH4_0, 2 }, + { TRXC_CCCH, 3, TRXC_SACCH4_0, 3 }, + { TRXC_FCCH, 0, TRXC_SACCH4_1, 0 }, + { TRXC_SCH, 0, TRXC_SACCH4_1, 1 }, + { TRXC_CCCH, 0, TRXC_SACCH4_1, 2 }, + { TRXC_CCCH, 1, TRXC_SACCH4_1, 3 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_FCCH, 0, TRXC_RACH, 0 }, + { TRXC_SCH, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_0, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_0, 1, TRXC_RACH, 0 }, + { TRXC_SDCCH4_0, 2, TRXC_RACH, 0 }, + { TRXC_SDCCH4_0, 3, TRXC_RACH, 0 }, + { TRXC_SDCCH4_1, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_1, 1, TRXC_RACH, 0 }, + { TRXC_SDCCH4_1, 2, TRXC_RACH, 0 }, + { TRXC_SDCCH4_1, 3, TRXC_RACH, 0 }, + { TRXC_FCCH, 0, TRXC_RACH, 0 }, + { TRXC_SCH, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_2, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_2, 1, TRXC_RACH, 0 }, + { TRXC_SDCCH4_2, 2, TRXC_RACH, 0 }, + { TRXC_SDCCH4_2, 3, TRXC_RACH, 0 }, + { TRXC_SDCCH4_3, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_3, 1, TRXC_SDCCH4_0, 0 }, + { TRXC_SDCCH4_3, 2, TRXC_SDCCH4_0, 1 }, + { TRXC_SDCCH4_3, 3, TRXC_SDCCH4_0, 2 }, + { TRXC_FCCH, 0, TRXC_SDCCH4_0, 3 }, + { TRXC_SCH, 0, TRXC_SDCCH4_1, 0 }, + { TRXC_SACCH4_2, 0, TRXC_SDCCH4_1, 1 }, + { TRXC_SACCH4_2, 1, TRXC_SDCCH4_1, 2 }, + { TRXC_SACCH4_2, 2, TRXC_SDCCH4_1, 3 }, + { TRXC_SACCH4_2, 3, TRXC_RACH, 0 }, + { TRXC_SACCH4_3, 0, TRXC_RACH, 0 }, + { TRXC_SACCH4_3, 1, TRXC_SDCCH4_2, 0 }, + { TRXC_SACCH4_3, 2, TRXC_SDCCH4_2, 1 }, + { TRXC_SACCH4_3, 3, TRXC_SDCCH4_2, 2 }, + { TRXC_IDLE, 0, TRXC_SDCCH4_2, 3 }, +}; + +static const struct trx_frame frame_sdcch8[102] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_SDCCH8_0, 0, TRXC_SACCH8_5, 0 }, + { TRXC_SDCCH8_0, 1, TRXC_SACCH8_5, 1 }, + { TRXC_SDCCH8_0, 2, TRXC_SACCH8_5, 2 }, + { TRXC_SDCCH8_0, 3, TRXC_SACCH8_5, 3 }, + { TRXC_SDCCH8_1, 0, TRXC_SACCH8_6, 0 }, + { TRXC_SDCCH8_1, 1, TRXC_SACCH8_6, 1 }, + { TRXC_SDCCH8_1, 2, TRXC_SACCH8_6, 2 }, + { TRXC_SDCCH8_1, 3, TRXC_SACCH8_6, 3 }, + { TRXC_SDCCH8_2, 0, TRXC_SACCH8_7, 0 }, + { TRXC_SDCCH8_2, 1, TRXC_SACCH8_7, 1 }, + { TRXC_SDCCH8_2, 2, TRXC_SACCH8_7, 2 }, + { TRXC_SDCCH8_2, 3, TRXC_SACCH8_7, 3 }, + { TRXC_SDCCH8_3, 0, TRXC_IDLE, 0 }, + { TRXC_SDCCH8_3, 1, TRXC_IDLE, 0 }, + { TRXC_SDCCH8_3, 2, TRXC_IDLE, 0 }, + { TRXC_SDCCH8_3, 3, TRXC_SDCCH8_0, 0 }, + { TRXC_SDCCH8_4, 0, TRXC_SDCCH8_0, 1 }, + { TRXC_SDCCH8_4, 1, TRXC_SDCCH8_0, 2 }, + { TRXC_SDCCH8_4, 2, TRXC_SDCCH8_0, 3 }, + { TRXC_SDCCH8_4, 3, TRXC_SDCCH8_1, 0 }, + { TRXC_SDCCH8_5, 0, TRXC_SDCCH8_1, 1 }, + { TRXC_SDCCH8_5, 1, TRXC_SDCCH8_1, 2 }, + { TRXC_SDCCH8_5, 2, TRXC_SDCCH8_1, 3 }, + { TRXC_SDCCH8_5, 3, TRXC_SDCCH8_2, 0 }, + { TRXC_SDCCH8_6, 0, TRXC_SDCCH8_2, 1 }, + { TRXC_SDCCH8_6, 1, TRXC_SDCCH8_2, 2 }, + { TRXC_SDCCH8_6, 2, TRXC_SDCCH8_2, 3 }, + { TRXC_SDCCH8_6, 3, TRXC_SDCCH8_3, 0 }, + { TRXC_SDCCH8_7, 0, TRXC_SDCCH8_3, 1 }, + { TRXC_SDCCH8_7, 1, TRXC_SDCCH8_3, 2 }, + { TRXC_SDCCH8_7, 2, TRXC_SDCCH8_3, 3 }, + { TRXC_SDCCH8_7, 3, TRXC_SDCCH8_4, 0 }, + { TRXC_SACCH8_0, 0, TRXC_SDCCH8_4, 1 }, + { TRXC_SACCH8_0, 1, TRXC_SDCCH8_4, 2 }, + { TRXC_SACCH8_0, 2, TRXC_SDCCH8_4, 3 }, + { TRXC_SACCH8_0, 3, TRXC_SDCCH8_5, 0 }, + { TRXC_SACCH8_1, 0, TRXC_SDCCH8_5, 1 }, + { TRXC_SACCH8_1, 1, TRXC_SDCCH8_5, 2 }, + { TRXC_SACCH8_1, 2, TRXC_SDCCH8_5, 3 }, + { TRXC_SACCH8_1, 3, TRXC_SDCCH8_6, 0 }, + { TRXC_SACCH8_2, 0, TRXC_SDCCH8_6, 1 }, + { TRXC_SACCH8_2, 1, TRXC_SDCCH8_6, 2 }, + { TRXC_SACCH8_2, 2, TRXC_SDCCH8_6, 3 }, + { TRXC_SACCH8_2, 3, TRXC_SDCCH8_7, 0 }, + { TRXC_SACCH8_3, 0, TRXC_SDCCH8_7, 1 }, + { TRXC_SACCH8_3, 1, TRXC_SDCCH8_7, 2 }, + { TRXC_SACCH8_3, 2, TRXC_SDCCH8_7, 3 }, + { TRXC_SACCH8_3, 3, TRXC_SACCH8_0, 0 }, + { TRXC_IDLE, 0, TRXC_SACCH8_0, 1 }, + { TRXC_IDLE, 0, TRXC_SACCH8_0, 2 }, + { TRXC_IDLE, 0, TRXC_SACCH8_0, 3 }, + + { TRXC_SDCCH8_0, 0, TRXC_SACCH8_1, 0 }, + { TRXC_SDCCH8_0, 1, TRXC_SACCH8_1, 1 }, + { TRXC_SDCCH8_0, 2, TRXC_SACCH8_1, 2 }, + { TRXC_SDCCH8_0, 3, TRXC_SACCH8_1, 3 }, + { TRXC_SDCCH8_1, 0, TRXC_SACCH8_2, 0 }, + { TRXC_SDCCH8_1, 1, TRXC_SACCH8_2, 1 }, + { TRXC_SDCCH8_1, 2, TRXC_SACCH8_2, 2 }, + { TRXC_SDCCH8_1, 3, TRXC_SACCH8_2, 3 }, + { TRXC_SDCCH8_2, 0, TRXC_SACCH8_3, 0 }, + { TRXC_SDCCH8_2, 1, TRXC_SACCH8_3, 1 }, + { TRXC_SDCCH8_2, 2, TRXC_SACCH8_3, 2 }, + { TRXC_SDCCH8_2, 3, TRXC_SACCH8_3, 3 }, + { TRXC_SDCCH8_3, 0, TRXC_IDLE, 0 }, + { TRXC_SDCCH8_3, 1, TRXC_IDLE, 0 }, + { TRXC_SDCCH8_3, 2, TRXC_IDLE, 0 }, + { TRXC_SDCCH8_3, 3, TRXC_SDCCH8_0, 0 }, + { TRXC_SDCCH8_4, 0, TRXC_SDCCH8_0, 1 }, + { TRXC_SDCCH8_4, 1, TRXC_SDCCH8_0, 2 }, + { TRXC_SDCCH8_4, 2, TRXC_SDCCH8_0, 3 }, + { TRXC_SDCCH8_4, 3, TRXC_SDCCH8_1, 0 }, + { TRXC_SDCCH8_5, 0, TRXC_SDCCH8_1, 1 }, + { TRXC_SDCCH8_5, 1, TRXC_SDCCH8_1, 2 }, + { TRXC_SDCCH8_5, 2, TRXC_SDCCH8_1, 3 }, + { TRXC_SDCCH8_5, 3, TRXC_SDCCH8_2, 0 }, + { TRXC_SDCCH8_6, 0, TRXC_SDCCH8_2, 1 }, + { TRXC_SDCCH8_6, 1, TRXC_SDCCH8_2, 2 }, + { TRXC_SDCCH8_6, 2, TRXC_SDCCH8_2, 3 }, + { TRXC_SDCCH8_6, 3, TRXC_SDCCH8_3, 0 }, + { TRXC_SDCCH8_7, 0, TRXC_SDCCH8_3, 1 }, + { TRXC_SDCCH8_7, 1, TRXC_SDCCH8_3, 2 }, + { TRXC_SDCCH8_7, 2, TRXC_SDCCH8_3, 3 }, + { TRXC_SDCCH8_7, 3, TRXC_SDCCH8_4, 0 }, + { TRXC_SACCH8_4, 0, TRXC_SDCCH8_4, 1 }, + { TRXC_SACCH8_4, 1, TRXC_SDCCH8_4, 2 }, + { TRXC_SACCH8_4, 2, TRXC_SDCCH8_4, 3 }, + { TRXC_SACCH8_4, 3, TRXC_SDCCH8_5, 0 }, + { TRXC_SACCH8_5, 0, TRXC_SDCCH8_5, 1 }, + { TRXC_SACCH8_5, 1, TRXC_SDCCH8_5, 2 }, + { TRXC_SACCH8_5, 2, TRXC_SDCCH8_5, 3 }, + { TRXC_SACCH8_5, 3, TRXC_SDCCH8_6, 0 }, + { TRXC_SACCH8_6, 0, TRXC_SDCCH8_6, 1 }, + { TRXC_SACCH8_6, 1, TRXC_SDCCH8_6, 2 }, + { TRXC_SACCH8_6, 2, TRXC_SDCCH8_6, 3 }, + { TRXC_SACCH8_6, 3, TRXC_SDCCH8_7, 0 }, + { TRXC_SACCH8_7, 0, TRXC_SDCCH8_7, 1 }, + { TRXC_SACCH8_7, 1, TRXC_SDCCH8_7, 2 }, + { TRXC_SACCH8_7, 2, TRXC_SDCCH8_7, 3 }, + { TRXC_SACCH8_7, 3, TRXC_SACCH8_4, 0 }, + { TRXC_IDLE, 0, TRXC_SACCH8_4, 1 }, + { TRXC_IDLE, 0, TRXC_SACCH8_4, 2 }, + { TRXC_IDLE, 0, TRXC_SACCH8_4, 3 }, +}; + +static const struct trx_frame frame_tchf_ts0[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, +}; + +static const struct trx_frame frame_tchf_ts1[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 }, +}; + +static const struct trx_frame frame_tchf_ts2[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, +}; + +static const struct trx_frame frame_tchf_ts3[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 }, +}; + +static const struct trx_frame frame_tchf_ts4[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, +}; + +static const struct trx_frame frame_tchf_ts5[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 }, +}; + +static const struct trx_frame frame_tchf_ts6[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, +}; + +static const struct trx_frame frame_tchf_ts7[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 }, +}; + +static const struct trx_frame frame_tchh_ts01[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 0, TRXC_SACCHTH_0, 0 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 0, TRXC_SACCHTH_1, 0 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 1, TRXC_SACCHTH_0, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 1, TRXC_SACCHTH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 2, TRXC_SACCHTH_0, 2 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 2, TRXC_SACCHTH_1, 2 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 3, TRXC_SACCHTH_0, 3 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 3, TRXC_SACCHTH_1, 3 }, +}; + +static const struct trx_frame frame_tchh_ts23[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 3, TRXC_SACCHTH_0, 3 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 3, TRXC_SACCHTH_1, 3 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 0, TRXC_SACCHTH_0, 0 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 0, TRXC_SACCHTH_1, 0 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 1, TRXC_SACCHTH_0, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 1, TRXC_SACCHTH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 2, TRXC_SACCHTH_0, 2 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 2, TRXC_SACCHTH_1, 2 }, +}; + +static const struct trx_frame frame_tchh_ts45[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 2, TRXC_SACCHTH_0, 2 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 2, TRXC_SACCHTH_1, 2 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 3, TRXC_SACCHTH_0, 3 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 3, TRXC_SACCHTH_1, 3 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 0, TRXC_SACCHTH_0, 0 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 0, TRXC_SACCHTH_1, 0 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 1, TRXC_SACCHTH_0, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 1, TRXC_SACCHTH_1, 1 }, +}; + +static const struct trx_frame frame_tchh_ts67[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 1, TRXC_SACCHTH_0, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 1, TRXC_SACCHTH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 2, TRXC_SACCHTH_0, 2 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 2, TRXC_SACCHTH_1, 2 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 3, TRXC_SACCHTH_0, 3 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 3, TRXC_SACCHTH_1, 3 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 0, TRXC_SACCHTH_0, 0 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 0, TRXC_SACCHTH_1, 0 }, +}; + +static const struct trx_frame frame_pdch[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PTCCH, 0, TRXC_PTCCH, 0 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PTCCH, 1, TRXC_PTCCH, 1 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PTCCH, 2, TRXC_PTCCH, 2 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PTCCH, 3, TRXC_PTCCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, +}; + +/** + * A few notes about frame count: + * + * 26 frame multiframe - traffic multiframe + * 51 frame multiframe - control multiframe + * + * 102 = 2 x 51 frame multiframe + * 104 = 4 x 26 frame multiframe + */ +static const struct trx_multiframe layouts[] = { + { + GSM_PCHAN_NONE, "NONE", + 0, 0xff, (uint64_t) 0x00, + NULL + }, + { + GSM_PCHAN_CCCH, "BCCH+CCCH", + 51, 0xff, (uint64_t) 0x3e, + frame_bcch + }, + { + GSM_PCHAN_CCCH_SDCCH4, "BCCH+CCCH+SDCCH/4+SACCH/4", + 102, 0xff, (uint64_t) 0xf001e3e, + frame_bcch_sdcch4 + }, + { + GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH/8+SACCH/8", + 102, 0xff, (uint64_t) 0xff01fe000, + frame_sdcch8 + }, + { + GSM_PCHAN_TCH_F, "TCH/F+SACCH", + 104, 0x01, (uint64_t) 0x200040, + frame_tchf_ts0 + }, + { + GSM_PCHAN_TCH_F, "TCH/F+SACCH", + 104, 0x02, (uint64_t) 0x200040, + frame_tchf_ts1 + }, + { + GSM_PCHAN_TCH_F, "TCH/F+SACCH", + 104, 0x04, (uint64_t) 0x200040, + frame_tchf_ts2 + }, + { + GSM_PCHAN_TCH_F, "TCH/F+SACCH", + 104, 0x08, (uint64_t) 0x200040, + frame_tchf_ts3 + }, + { + GSM_PCHAN_TCH_F, "TCH/F+SACCH", + 104, 0x10, (uint64_t) 0x200040, + frame_tchf_ts4 + }, + { + GSM_PCHAN_TCH_F, "TCH/F+SACCH", + 104, 0x20, (uint64_t) 0x200040, + frame_tchf_ts5 + }, + { + GSM_PCHAN_TCH_F, "TCH/F+SACCH", + 104, 0x40, (uint64_t) 0x200040, + frame_tchf_ts6 + }, + { + GSM_PCHAN_TCH_F, "TCH/F+SACCH", + 104, 0x80, (uint64_t) 0x200040, + frame_tchf_ts7 + }, + { + GSM_PCHAN_TCH_H, "TCH/H+SACCH", + 104, 0x03, (uint64_t) 0xc00180, + frame_tchh_ts01 + }, + { + GSM_PCHAN_TCH_H, "TCH/H+SACCH", + 104, 0x0c, (uint64_t) 0xc00180, + frame_tchh_ts23 + }, + { + GSM_PCHAN_TCH_H, "TCH/H+SACCH", + 104, 0x30, (uint64_t) 0xc00180, + frame_tchh_ts45 + }, + { + GSM_PCHAN_TCH_H, "TCH/H+SACCH", + 104, 0xc0, (uint64_t) 0xc00180, + frame_tchh_ts67 + }, + { + GSM_PCHAN_PDCH, "PDCH", + 104, 0xff, (uint64_t) 0x3000000000, + frame_pdch + }, +}; + +const struct trx_multiframe *sched_mframe_layout( + enum gsm_phys_chan_config config, int ts_num) +{ + int i, ts_allowed; + + for (i = 0; i < ARRAY_SIZE(layouts); i++) { + ts_allowed = layouts[i].slotmask & (0x01 << ts_num); + if (layouts[i].chan_config == config && ts_allowed) + return &layouts[i]; + } + + return NULL; +} diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h new file mode 100644 index 000000000..1abd5156d --- /dev/null +++ b/src/host/trxcon/sched_trx.h @@ -0,0 +1,235 @@ +#pragma once + +#include + +#include +#include +#include +#include + +#include "logging.h" +#include "scheduler.h" + +#define GSM_BURST_LEN 148 +#define GSM_BURST_PL_LEN 116 + +#define GPRS_BURST_LEN GSM_BURST_LEN +#define EDGE_BURST_LEN 444 + +#define TRX_CH_FLAG_PDCH (1 << 0) +#define TRX_CH_FLAG_AUTO (1 << 1) +#define TRX_TS_COUNT 8 + +#define MAX_A5_KEY_LEN (128 / 8) + +/* Forward declaration to avoid mutual include */ +struct trx_instance; +struct trx_ts; + +enum trx_burst_type { + TRX_BURST_GMSK, + TRX_BURST_8PSK, +}; + +/** + * These types define the different channels on a multiframe. + * Each channel has queues and can be activated individually. + */ +enum trx_lchan_type { + TRXC_IDLE = 0, + TRXC_FCCH, + TRXC_SCH, + TRXC_BCCH, + TRXC_RACH, + TRXC_CCCH, + TRXC_TCHF, + TRXC_TCHH_0, + TRXC_TCHH_1, + TRXC_SDCCH4_0, + TRXC_SDCCH4_1, + TRXC_SDCCH4_2, + TRXC_SDCCH4_3, + TRXC_SDCCH8_0, + TRXC_SDCCH8_1, + TRXC_SDCCH8_2, + TRXC_SDCCH8_3, + TRXC_SDCCH8_4, + TRXC_SDCCH8_5, + TRXC_SDCCH8_6, + TRXC_SDCCH8_7, + TRXC_SACCHTF, + TRXC_SACCHTH_0, + TRXC_SACCHTH_1, + TRXC_SACCH4_0, + TRXC_SACCH4_1, + TRXC_SACCH4_2, + TRXC_SACCH4_3, + TRXC_SACCH8_0, + TRXC_SACCH8_1, + TRXC_SACCH8_2, + TRXC_SACCH8_3, + TRXC_SACCH8_4, + TRXC_SACCH8_5, + TRXC_SACCH8_6, + TRXC_SACCH8_7, + TRXC_PDTCH, + TRXC_PTCCH, + _TRX_CHAN_MAX +}; + +typedef int trx_lchan_rx_func(struct trx_instance *trx, + struct trx_ts *ts, + uint32_t fn, enum trx_lchan_type chan, + uint8_t bid, sbit_t *bits, uint16_t nbits, + int8_t rssi, float toa); + +typedef ubit_t *trx_lchan_tx_func(struct trx_instance *trx, + struct trx_ts *ts, + uint32_t fn, enum trx_lchan_type chan, + uint8_t bid, uint16_t *nbits); + +struct trx_lchan_desc { + /*! \brief TRX Channel Type */ + enum trx_lchan_type chan; + /*! \brief Human-readable name */ + const char *name; + /*! \brief Channel Number (like in RSL) */ + uint8_t chan_nr; + /*! \brief Link ID (like in RSL) */ + uint8_t link_id; + + /*! \brief How much memory do we need to store bursts */ + size_t burst_buf_size; + /*! \brief Channel specific flags */ + uint8_t flags; + + /*! \brief Function to call when burst received from PHY */ + trx_lchan_rx_func *rx_fn; + /*! \brief Function to call when data received from L2 */ + trx_lchan_tx_func *tx_fn; +}; + +struct trx_frame { + /*! \brief Downlink TRX channel type */ + enum trx_lchan_type dl_chan; + /*! \brief Downlink block ID */ + uint8_t dl_bid; + /*! \brief Uplink TRX channel type */ + enum trx_lchan_type ul_chan; + /*! \brief Uplink block ID */ + uint8_t ul_bid; +}; + +struct trx_multiframe { + /*! \brief Channel combination */ + enum gsm_phys_chan_config chan_config; + /*! \brief Human-readable name */ + const char *name; + /*! \brief Repeats how many frames */ + uint8_t period; + /*! \brief Applies to which timeslots */ + uint8_t slotmask; + /*! \brief Contains which lchans */ + uint64_t lchan_mask; + /*! \brief Pointer to scheduling structure */ + const struct trx_frame *frames; +}; + +/* States each channel on a multiframe */ +struct trx_lchan_state { + /*! \brief Channel type */ + enum trx_lchan_type type; + /*! \brief Channel status */ + uint8_t active; + + /*! \brief Burst type: GMSK or 8PSK */ + enum trx_burst_type burst_type; + /*! \brief Frame number of first burst */ + uint32_t rx_first_fn; + /*! \brief Mask of received bursts */ + uint8_t rx_burst_mask; + /*! \brief Burst buffer for RX */ + sbit_t *rx_bursts; + /*! \brief Burst buffer for TX */ + ubit_t *tx_bursts; + + /*! \brief Number of RSSI values */ + uint8_t rssi_num; + /*! \brief Sum of RSSI values */ + float rssi_sum; + /*! \brief Number of TOA values */ + uint8_t toa_num; + /*! \brief Sum of TOA values */ + float toa_sum; + /*! \brief (SACCH) loss detection */ + uint8_t lost; + /*! \brief Mode for TCH channels */ + uint8_t rsl_cmode, tch_mode; + + /* AMR specific */ + /*! \brief 4 possible codecs for AMR */ + uint8_t codec[4]; + /*! \brief Number of possible codecs */ + int codecs; + /*! \brief Sum of bit error rates */ + float ber_sum; + /*! \brief Number of bit error rates */ + int ber_num; + /*! \brief Current uplink FT index */ + uint8_t ul_ft; + /*! \brief Current downlink FT index */ + uint8_t dl_ft; + /*! \brief Current uplink CMR index */ + uint8_t ul_cmr; + /*! \brief Current downlink CMR index */ + uint8_t dl_cmr; + /*! \brief If AMR loop is enabled */ + uint8_t amr_loop; + + /* TCH/H */ + uint8_t dl_ongoing_facch; /*! \brief FACCH/H on downlink */ + uint8_t ul_ongoing_facch; /*! \brief FACCH/H on uplink */ + + /*! \brief A5/x encryption algorithm */ + int encr_algo; + int encr_key_len; + uint8_t encr_key[MAX_A5_KEY_LEN]; + + /*! \brief Measurements */ + struct { + /*! \brief Cyclic clock counter */ + uint8_t clock; + /*! \brief Last RSSI values */ + int8_t rssi[32]; + /*! \brief Received RSSI values */ + int rssi_count; + /*! \brief Number of stored value */ + int rssi_valid_count; + /*! \brief Any burst received so far */ + int rssi_got_burst; + /*! \brief Sum of TOA values */ + float toa_sum; + /*! \brief Number of TOA value */ + int toa_num; + } meas; +}; + +struct trx_ts { + /*! \brief Timeslot index within a frame (0..7) */ + uint8_t index; + /*! \brief Last received frame number */ + uint32_t mf_last_fn; + + /*! \brief Pointer to multiframe layout */ + const struct trx_multiframe *mf_layout; + /*! \brief Channel states for logical channels */ + struct trx_lchan_state *lchans; + /*! \brief Queue primitives for TX */ + struct llist_head tx_prims; + /*! \brief Link to parent list */ + struct llist_head list; +}; + +extern const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX]; +const struct trx_multiframe *sched_mframe_layout( + enum gsm_phys_chan_config config, int ts_num); From 8baa369b239a262251af87d45cd4dfc4bfc5f4db Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 4 Jul 2017 20:55:12 +0700 Subject: [PATCH 011/211] host/trxcon/scheduler: implement management functions This change introduces some new functions to configure TDMA scheduler. They can be grouped as follows: - Scheduler management functions - sched_trx_init() - sched_trx_reset() - sched_trx_shutdown() - Timeslot management functions - sched_trx_add_ts() - sched_trx_find_ts() - sched_trx_del_ts() - sched_trx_reset_ts() - sched_trx_configure_ts() - Logical channel management functions - sched_trx_activate_lchan() - sched_trx_deactivate_lchan() - sched_trx_find_lchan() Change-Id: I6d0f437a68f0eb4e22bf635bf899b08673571085 --- src/host/trxcon/Makefile.am | 1 + src/host/trxcon/sched_trx.c | 328 ++++++++++++++++++++++++++++++++++++ src/host/trxcon/sched_trx.h | 19 +++ src/host/trxcon/trx_if.h | 2 + src/host/trxcon/trxcon.c | 10 ++ 5 files changed, 360 insertions(+) create mode 100644 src/host/trxcon/sched_trx.c diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am index a5008e6f5..f832651f8 100644 --- a/src/host/trxcon/Makefile.am +++ b/src/host/trxcon/Makefile.am @@ -33,6 +33,7 @@ trxcon_SOURCES += \ sched_lchan_desc.c \ sched_mframe.c \ sched_clck.c \ + sched_trx.c \ $(NULL) trxcon_LDADD = \ diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c new file mode 100644 index 000000000..a0a2ed5f1 --- /dev/null +++ b/src/host/trxcon/sched_trx.c @@ -0,0 +1,328 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: GSM PHY routines + * + * (C) 2017 by Vadim Yanitskiy + * + * 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 +#include +#include +#include + +#include +#include +#include +#include + +#include "scheduler.h" +#include "sched_trx.h" +#include "trx_if.h" +#include "logging.h" + +static void msgb_queue_flush(struct llist_head *list) +{ + struct msgb *msg, *msg2; + + llist_for_each_entry_safe(msg, msg2, list, list) + msgb_free(msg); +} + +static void sched_frame_clck_cb(struct trx_sched *sched) +{ + struct trx_instance *trx = (struct trx_instance *) sched->data; + + /* If we have no active timeslots, nothing to do */ + if (llist_empty(&trx->ts_list)) + return; + + /* Do nothing for now */ +} + +int sched_trx_init(struct trx_instance *trx) +{ + struct trx_sched *sched; + + if (!trx) + return -EINVAL; + + LOGP(DSCH, LOGL_NOTICE, "Init scheduler\n"); + + /* Obtain a scheduler instance from TRX */ + sched = &trx->sched; + + /* Register frame clock callback */ + sched->clock_cb = sched_frame_clck_cb; + + /* Set pointers */ + sched = &trx->sched; + sched->data = trx; + + INIT_LLIST_HEAD(&trx->ts_list); + + return 0; +} + +int sched_trx_shutdown(struct trx_instance *trx) +{ + int i; + + if (!trx) + return -EINVAL; + + LOGP(DSCH, LOGL_NOTICE, "Shutdown scheduler\n"); + + /* Free all potentially allocated timeslots */ + for (i = 0; i < TRX_TS_COUNT; i++) + sched_trx_del_ts(trx, i); + + return 0; +} + +int sched_trx_reset(struct trx_instance *trx) +{ + struct trx_sched *sched; + int i; + + if (!trx) + return -EINVAL; + + LOGP(DSCH, LOGL_NOTICE, "Reset scheduler\n"); + + /* Free all potentially allocated timeslots */ + for (i = 0; i < TRX_TS_COUNT; i++) + sched_trx_del_ts(trx, i); + + INIT_LLIST_HEAD(&trx->ts_list); + + /* Obtain a scheduler instance from TRX */ + sched = &trx->sched; + + /* Reset clock counter */ + osmo_timer_del(&sched->clock_timer); + sched->fn_counter_proc = 0; + sched->fn_counter_lost = 0; + + return 0; +} + +struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int ts_num) +{ + struct trx_ts *ts; + + LOGP(DSCH, LOGL_INFO, "Add a new TDMA timeslot #%u\n", ts_num); + + ts = talloc_zero(trx, struct trx_ts); + if (!ts) + return NULL; + + llist_add_tail(&ts->list, &trx->ts_list); + + return ts; +} + +struct trx_ts *sched_trx_find_ts(struct trx_instance *trx, int ts_num) +{ + struct trx_ts *ts; + + if (llist_empty(&trx->ts_list)) + return NULL; + + llist_for_each_entry(ts, &trx->ts_list, list) { + if (ts->index == ts_num) + return ts; + } + + return NULL; +} + +void sched_trx_del_ts(struct trx_instance *trx, int ts_num) +{ + struct trx_ts *ts; + + /* Find ts in list */ + ts = sched_trx_find_ts(trx, ts_num); + if (ts == NULL) + return; + + LOGP(DSCH, LOGL_INFO, "Delete TDMA timeslot #%u\n", ts_num); + + /* Flush queue primitives for TX */ + msgb_queue_flush(&ts->tx_prims); + + /* Remove ts from list */ + llist_del(&ts->list); + talloc_free(ts); +} + +int sched_trx_configure_ts(struct trx_instance *trx, int ts_num, + enum gsm_phys_chan_config config) +{ + int i, type, lchan_cnt = 0; + struct trx_ts *ts; + + /* Try to find specified ts */ + ts = sched_trx_find_ts(trx, ts_num); + if (ts != NULL) { + /* Reconfiguration of existing one */ + sched_trx_reset_ts(trx, ts_num); + } else { + /* Allocate a new one if doesn't exist */ + ts = sched_trx_add_ts(trx, ts_num); + if (ts == NULL) + return -ENOMEM; + } + + /* Init queue primitives for TX */ + INIT_LLIST_HEAD(&ts->tx_prims); + + /* Choose proper multiframe layout */ + ts->mf_layout = sched_mframe_layout(config, ts_num); + if (ts->mf_layout->chan_config != config) + return -EINVAL; + + LOGP(DSCH, LOGL_INFO, "(Re)configure TDMA timeslot #%u as %s\n", + ts_num, ts->mf_layout->name); + + /* Count channel states */ + for (type = 0; type < _TRX_CHAN_MAX; type++) + if (ts->mf_layout->lchan_mask & ((uint64_t) 0x01 << type)) + lchan_cnt++; + + if (!lchan_cnt) + return 0; + + /* Allocate channel states */ + ts->lchans = talloc_zero_array(ts, struct trx_lchan_state, lchan_cnt); + if (ts->lchans == NULL) + return -ENOMEM; + + /* Init channel states */ + for (type = 0, i = 0; type < _TRX_CHAN_MAX; type++) { + if (ts->mf_layout->lchan_mask & ((uint64_t) 0x01 << type)) { + /* Set proper channel type */ + ts->lchans[i++].type = type; + + /* Enable channel automatically if required */ + if (trx_lchan_desc[type].flags & TRX_CH_FLAG_AUTO) + sched_trx_activate_lchan(ts, type); + } + } + + return 0; +} + +int sched_trx_reset_ts(struct trx_instance *trx, int ts_num) +{ + struct trx_ts *ts; + + /* Try to find specified ts */ + ts = sched_trx_find_ts(trx, ts_num); + if (ts == NULL) + return -EINVAL; + + /* FIXME: where do we need it? */ + ts->mf_last_fn = 0; + + /* Undefine multiframe layout */ + ts->mf_layout = NULL; + + /* Flush queue primitives for TX */ + msgb_queue_flush(&ts->tx_prims); + + /* Free channel states */ + talloc_free(ts->lchans); + + return 0; +} + +struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts, + enum trx_lchan_type chan) +{ + int i, len; + + len = talloc_array_length(ts->lchans); + for (i = 0; i < len; i++) + if (ts->lchans[i].type == chan) + return ts->lchans + i; + + return NULL; +} + +int sched_trx_activate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) +{ + const struct trx_lchan_desc *lchan_desc = &trx_lchan_desc[chan]; + struct trx_lchan_state *lchan; + + /* Try to find requested logical channel */ + lchan = sched_trx_find_lchan(ts, chan); + if (lchan == NULL) + return -EINVAL; + + if (lchan->active) { + LOGP(DSCH, LOGL_ERROR, "Logical channel %s already activated " + "on ts=%d\n", trx_lchan_desc[chan].name, ts->index); + return -EINVAL; + } + + /* Conditionally allocate memory for bursts */ + if (lchan_desc->rx_fn && lchan_desc->burst_buf_size > 0) { + lchan->rx_bursts = talloc_zero_size(ts->lchans, + lchan_desc->burst_buf_size); + if (lchan->rx_bursts == NULL) + return -ENOMEM; + } + + if (lchan_desc->tx_fn && lchan_desc->burst_buf_size > 0) { + lchan->tx_bursts = talloc_zero_size(ts->lchans, + lchan_desc->burst_buf_size); + if (lchan->tx_bursts == NULL) + return -ENOMEM; + } + + /* Finally, update channel status */ + lchan->active = 1; + + return 0; +} + +int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) +{ + struct trx_lchan_state *lchan; + + /* Try to find requested logical channel */ + lchan = sched_trx_find_lchan(ts, chan); + if (lchan == NULL) + return -EINVAL; + + if (lchan->active) { + LOGP(DSCH, LOGL_ERROR, "Logical channel %s already deactivated " + "on ts=%d", trx_lchan_desc[chan].name, ts->index); + return -EINVAL; + } + + /* Free memory */ + talloc_free(lchan->rx_bursts); + talloc_free(lchan->tx_bursts); + + lchan->active = 0; + + return 0; +} diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 1abd5156d..bcb4df99c 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -233,3 +233,22 @@ struct trx_ts { extern const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX]; const struct trx_multiframe *sched_mframe_layout( enum gsm_phys_chan_config config, int ts_num); + +/* Scheduler management functions */ +int sched_trx_init(struct trx_instance *trx); +int sched_trx_reset(struct trx_instance *trx); +int sched_trx_shutdown(struct trx_instance *trx); + +/* Timeslot management functions */ +struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int ts_num); +struct trx_ts *sched_trx_find_ts(struct trx_instance *trx, int ts_num); +void sched_trx_del_ts(struct trx_instance *trx, int ts_num); +int sched_trx_reset_ts(struct trx_instance *trx, int ts_num); +int sched_trx_configure_ts(struct trx_instance *trx, int ts_num, + enum gsm_phys_chan_config config); + +/* Logical channel management functions */ +int sched_trx_activate_lchan(struct trx_ts *ts, enum trx_lchan_type chan); +int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan); +struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts, + enum trx_lchan_type chan); diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h index e41d506bc..1241fd5a7 100644 --- a/src/host/trxcon/trx_if.h +++ b/src/host/trxcon/trx_if.h @@ -6,6 +6,7 @@ #include #include "scheduler.h" +#include "sched_trx.h" enum trx_fsm_states { TRX_STATE_OFFLINE = 0, @@ -26,6 +27,7 @@ struct trx_instance { /* Scheduler stuff */ struct trx_sched sched; + struct llist_head ts_list; }; struct trx_ctrl_msg { diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index ace2f79af..791bd25da 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -42,6 +42,8 @@ #include "l1ctl.h" #include "l1ctl_link.h" #include "l1ctl_proto.h" +#include "scheduler.h" +#include "sched_trx.h" #define COPYRIGHT \ "Copyright (C) 2016-2017 by Vadim Yanitskiy \n" \ @@ -90,7 +92,9 @@ static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi, } break; case L1CTL_EVENT_RESET_REQ: + trx_if_cmd_poweroff(app_data.trx); trx_if_cmd_echo(app_data.trx); + sched_trx_reset(app_data.trx); break; case TRX_EVENT_RESET_IND: /* TODO: send proper reset type */ @@ -270,6 +274,11 @@ int main(int argc, char **argv) if (rc) goto exit; + /* Init scheduler */ + rc = sched_trx_init(app_data.trx); + if (rc) + goto exit; + LOGP(DAPP, LOGL_NOTICE, "Init complete\n"); if (app_data.daemonize) { @@ -286,6 +295,7 @@ int main(int argc, char **argv) exit: /* Close active connections */ l1ctl_link_shutdown(app_data.l1l); + sched_trx_shutdown(app_data.trx); trx_if_close(app_data.trx); /* Shutdown main state machine */ From ec3a1ba2e53506b476a24567bdb0fc29ce35e3ef Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 4 Jul 2017 21:12:25 +0700 Subject: [PATCH 012/211] host/trxcon/scheduler: handle bursts from TRX interface Change-Id: Iff15daf897f30cb98d4ec4c88b9fc259cb44ea4e --- src/host/trxcon/sched_trx.c | 76 +++++++++++++++++++++++++++++++++++++ src/host/trxcon/sched_trx.h | 3 ++ src/host/trxcon/trx_if.c | 4 +- 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index a0a2ed5f1..5cf877952 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -326,3 +326,79 @@ int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) return 0; } + +int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t ts_num, + uint32_t burst_fn, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa) +{ + struct trx_lchan_state *lchan; + const struct trx_frame *frame; + struct trx_ts *ts; + + trx_lchan_rx_func *handler; + enum trx_lchan_type chan; + uint32_t fn, elapsed; + uint8_t offset, bid; + + /* Check whether required timeslot is enabled / configured */ + ts = sched_trx_find_ts(trx, ts_num); + if (ts == NULL) { + LOGP(DSCH, LOGL_ERROR, "TDMA timeslot #%u isn't configured, " + "ignoring burst...\n", ts_num); + return -EINVAL; + } + + /* Calculate how many frames have been elapsed */ + elapsed = (burst_fn + GSM_HYPERFRAME - ts->mf_last_fn); + elapsed %= GSM_HYPERFRAME; + + /** + * If not too many frames have been elapsed, + * start counting from last fn + 1 + */ + if (elapsed < 10) + fn = (ts->mf_last_fn + 1) % GSM_HYPERFRAME; + else + fn = burst_fn; + + while (1) { + /* Get frame from multiframe */ + offset = fn % ts->mf_layout->period; + frame = ts->mf_layout->frames + offset; + + /* Get required info from frame */ + bid = frame->dl_bid; + chan = frame->dl_chan; + handler = trx_lchan_desc[chan].rx_fn; + + /* Omit bursts which have no handler, like IDLE bursts */ + if (!handler) + goto next_frame; + + /* Find required channel state */ + lchan = sched_trx_find_lchan(ts, chan); + if (lchan == NULL) /* FIXME: what should we do here? */ + goto next_frame; + + /* Ensure that channel is active */ + if (!lchan->active) + goto next_frame; + + /* Put burst to handler */ + if (fn == burst_fn) { + /* TODO: decrypt if required */ + handler(trx, ts, fn, chan, bid, bits, nbits, rssi, toa); + } + +next_frame: + /* Reached current fn */ + if (fn == burst_fn) + break; + + fn = (fn + 1) % GSM_HYPERFRAME; + } + + /* Set last processed frame number */ + ts->mf_last_fn = fn; + + return 0; +} diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index bcb4df99c..563ee198f 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -252,3 +252,6 @@ int sched_trx_activate_lchan(struct trx_ts *ts, enum trx_lchan_type chan); int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan); struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts, enum trx_lchan_type chan); + +int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t ts_num, + uint32_t burst_fn, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index 83db173a3..eb868ce92 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -570,7 +570,9 @@ static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what) LOGP(DTRX, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%.2f\n", tn, fn, rssi, toa); - /* TODO: poke scheduler here! */ + /* Poke scheduler */ + sched_trx_handle_rx_burst(trx, tn, fn, bits, 148, rssi, toa); + return 0; } From 62328cb08812ff440a699e92772b0c40bbc37869 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 6 Jul 2017 11:50:34 +0700 Subject: [PATCH 013/211] host/trxcon: store arfcn and band in trx_instance Change-Id: I95414ff1033d77f11e231178b7721b70bc45e6df --- src/host/trxcon/trx_if.h | 1 + src/host/trxcon/trxcon.c | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h index 1241fd5a7..4d9e4709d 100644 --- a/src/host/trxcon/trx_if.h +++ b/src/host/trxcon/trx_if.h @@ -24,6 +24,7 @@ struct trx_instance { struct llist_head trx_ctrl_list; struct osmo_fsm_inst *fsm; uint32_t prev_state; + uint16_t band_arfcn; /* Scheduler stuff */ struct trx_sched sched; diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index 791bd25da..d88e990f0 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -36,6 +36,8 @@ #include #include +#include + #include "trxcon.h" #include "trx_if.h" #include "logging.h" @@ -80,8 +82,6 @@ static void trxcon_fsm_idle_action(struct osmo_fsm_inst *fi, static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) { - uint16_t *band_arfcn; - switch (event) { case L1CTL_EVENT_DISCONNECT: osmo_fsm_inst_state_chg(trxcon_fsm, TRXCON_STATE_IDLE, 0, 0); @@ -101,9 +101,9 @@ static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi, l1ctl_tx_reset_conf(app_data.l1l, L1CTL_RES_T_BOOT); break; case L1CTL_EVENT_FBSB_REQ: - band_arfcn = (uint16_t *) data; - trx_if_cmd_rxtune(app_data.trx, *band_arfcn); - trx_if_cmd_txtune(app_data.trx, *band_arfcn); + app_data.trx->band_arfcn = *((uint16_t *) data); + trx_if_cmd_rxtune(app_data.trx, app_data.trx->band_arfcn); + trx_if_cmd_txtune(app_data.trx, app_data.trx->band_arfcn); trx_if_cmd_poweron(app_data.trx); break; case TRX_EVENT_RSP_ERROR: From 604ac30825dd3e915daaf6b155c07f0e1d49d500 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 6 Jul 2017 13:05:27 +0700 Subject: [PATCH 014/211] host/trxcon: handle ccch_mode from L1CTL_FBSB_REQ Previously, the content of L1CTL_FBSB_REQ message was only used to obtain a new ARFCN and retune transceiver. Now, since we have working TDMA scheduler, some other params (like ccch_mode) may be used too. Change-Id: Iccabba376d67e091b55a604a2ae87f2aa86362e5 --- src/host/trxcon/l1ctl.c | 17 ++++++++++++++--- src/host/trxcon/trxcon.c | 32 ++++++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 26670f1f4..0bb26f726 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -125,7 +125,7 @@ int l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type) static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg) { - struct l1ctl_fbsb_req *fbsb; + struct l1ctl_fbsb_req *fbsb, *fbsb_copy; uint16_t band_arfcn; int rc = 0; @@ -143,8 +143,19 @@ static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg) gsm_band_name(gsm_arfcn2band(band_arfcn)), band_arfcn &~ ARFCN_FLAG_MASK); - osmo_fsm_inst_dispatch(trxcon_fsm, - L1CTL_EVENT_FBSB_REQ, &band_arfcn); + /** + * We cannot simply pass a pointer to fbsb, + * because the memory will be freed. + * + * TODO: better solution? + */ + fbsb_copy = talloc_memdup(l1l, fbsb, sizeof(struct l1ctl_fbsb_req)); + if (fbsb_copy == NULL) { + rc = -EINVAL; + goto exit; + } + + osmo_fsm_inst_dispatch(trxcon_fsm, L1CTL_EVENT_FBSB_REQ, fbsb_copy); exit: msgb_free(msg); diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index d88e990f0..6f50d1a20 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -29,6 +29,8 @@ #include #include +#include + #include #include #include @@ -72,6 +74,31 @@ static struct { void *tall_trx_ctx = NULL; struct osmo_fsm_inst *trxcon_fsm; +static void trxcon_handle_fbsb_req(struct l1ctl_fbsb_req *req) +{ + uint16_t band_arfcn; + + /* Reset L1 */ + sched_trx_reset(app_data.trx); + + /* Configure a single timeslot */ + if (req->ccch_mode == CCCH_MODE_COMBINED) + sched_trx_configure_ts(app_data.trx, 0, GSM_PCHAN_CCCH_SDCCH4); + else + sched_trx_configure_ts(app_data.trx, 0, GSM_PCHAN_CCCH); + + /* Store current ARFCN */ + band_arfcn = ntohs(req->band_arfcn); + app_data.trx->band_arfcn = band_arfcn; + + /* Tune transceiver to required ARFCN */ + trx_if_cmd_rxtune(app_data.trx, band_arfcn); + trx_if_cmd_txtune(app_data.trx, band_arfcn); + trx_if_cmd_poweron(app_data.trx); + + talloc_free(req); +} + static void trxcon_fsm_idle_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) { @@ -101,10 +128,7 @@ static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi, l1ctl_tx_reset_conf(app_data.l1l, L1CTL_RES_T_BOOT); break; case L1CTL_EVENT_FBSB_REQ: - app_data.trx->band_arfcn = *((uint16_t *) data); - trx_if_cmd_rxtune(app_data.trx, app_data.trx->band_arfcn); - trx_if_cmd_txtune(app_data.trx, app_data.trx->band_arfcn); - trx_if_cmd_poweron(app_data.trx); + trxcon_handle_fbsb_req((struct l1ctl_fbsb_req *) data); break; case TRX_EVENT_RSP_ERROR: case TRX_EVENT_OFFLINE: From cf5c10f92a6ba150a40a0432b7faa526a68530eb Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 6 Jul 2017 11:34:15 +0700 Subject: [PATCH 015/211] host/trxcon: link trxcon against libosmocoding Change-Id: I9bb45428e6617bd6936d24340e688aef4aeafc8f --- src/host/trxcon/Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am index f832651f8..1a373f7ef 100644 --- a/src/host/trxcon/Makefile.am +++ b/src/host/trxcon/Makefile.am @@ -15,6 +15,7 @@ AM_CPPFLAGS = \ AM_CFLAGS = \ -Wall \ $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOCODING_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(NULL) @@ -38,5 +39,6 @@ trxcon_SOURCES += \ trxcon_LDADD = \ $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOCODING_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(NULL) From 228d42bc303284a9a7a1a8ad645d690b03ab7a26 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 6 Jul 2017 13:17:24 +0700 Subject: [PATCH 016/211] host/trxcon/scheduler: implement xCCH decoding Change-Id: Ieb71e3727b525e85d161855973f63042366ccb05 --- src/host/trxcon/Makefile.am | 1 + src/host/trxcon/l1ctl.c | 22 ++++ src/host/trxcon/l1ctl.h | 3 + src/host/trxcon/sched_lchan_desc.c | 6 +- src/host/trxcon/sched_lchan_handlers.c | 161 +++++++++++++++++++++++++ src/host/trxcon/trxcon.c | 5 +- src/host/trxcon/trxcon.h | 1 + 7 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 src/host/trxcon/sched_lchan_handlers.c diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am index 1a373f7ef..b2f22fab2 100644 --- a/src/host/trxcon/Makefile.am +++ b/src/host/trxcon/Makefile.am @@ -31,6 +31,7 @@ trxcon_SOURCES = \ # Scheduler trxcon_SOURCES += \ + sched_lchan_handlers.c \ sched_lchan_desc.c \ sched_mframe.c \ sched_clck.c \ diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 0bb26f726..3bcc5cb3a 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -123,6 +123,28 @@ int l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type) return l1ctl_link_send(l1l, msg); } +int l1ctl_tx_data_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data) +{ + struct l1ctl_info_dl *dl; + struct msgb *msg; + size_t len; + + msg = l1ctl_alloc_msg(L1CTL_DATA_IND); + if (msg == NULL) + return -ENOMEM; + + /* We store the 23-byte payload as a flexible array member */ + len = sizeof(struct l1ctl_info_dl) + 23; + dl = (struct l1ctl_info_dl *) msgb_put(msg, len); + + /* Copy header and data from source message */ + memcpy(dl, data, len); + talloc_free(data); + + /* Put message to upper layers */ + return l1ctl_link_send(l1l, msg); +} + static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg) { struct l1ctl_fbsb_req *fbsb, *fbsb_copy; diff --git a/src/host/trxcon/l1ctl.h b/src/host/trxcon/l1ctl.h index ffd1a81f2..05a2c5437 100644 --- a/src/host/trxcon/l1ctl.h +++ b/src/host/trxcon/l1ctl.h @@ -4,9 +4,12 @@ #include #include "l1ctl_link.h" +#include "l1ctl_proto.h" int l1ctl_tx_pm_conf(struct l1ctl_link *l1l, uint16_t band_arfcn, int dbm, int last); int l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type); int l1ctl_tx_reset_ind(struct l1ctl_link *l1l, uint8_t type); int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg); + +int l1ctl_tx_data_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *ind); diff --git a/src/host/trxcon/sched_lchan_desc.c b/src/host/trxcon/sched_lchan_desc.c index 880c2a592..93927a638 100644 --- a/src/host/trxcon/sched_lchan_desc.c +++ b/src/host/trxcon/sched_lchan_desc.c @@ -35,11 +35,15 @@ #define tx_tchh_fn NULL #define tx_rach_fn NULL -#define rx_data_fn NULL #define rx_pdtch_fn NULL #define rx_tchf_fn NULL #define rx_tchh_fn NULL +/* Forward declaration of handlers */ +int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, + uint32_t fn, enum trx_lchan_type chan, uint8_t bid, + sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); + const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { { TRXC_IDLE, "IDLE", diff --git a/src/host/trxcon/sched_lchan_handlers.c b/src/host/trxcon/sched_lchan_handlers.c new file mode 100644 index 000000000..2c2cb6a7c --- /dev/null +++ b/src/host/trxcon/sched_lchan_handlers.c @@ -0,0 +1,161 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: handlers for DL / UL bursts on logical channels + * + * (C) 2017 by Vadim Yanitskiy + * + * 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 +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include "l1ctl_proto.h" +#include "scheduler.h" +#include "sched_trx.h" +#include "logging.h" +#include "trx_if.h" +#include "trxcon.h" + +extern struct osmo_fsm_inst *trxcon_fsm; + +int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, + uint32_t fn, enum trx_lchan_type chan, uint8_t bid, + sbit_t *bits, uint16_t nbits, int8_t rssi, float toa) +{ + int n_errors, n_bits_total, rc; + struct trx_lchan_state *lchan; + uint8_t *rssi_num, *toa_num; + float *rssi_sum, *toa_sum; + sbit_t *buffer, *offset; + uint8_t l2[23], *mask; + uint32_t *first_fn; + + LOGP(DSCH, LOGL_DEBUG, "Data received on %s: fn=%u ts=%u bid=%u\n", + trx_lchan_desc[chan].name, fn, ts->index, bid); + + /* Find required channel state */ + lchan = sched_trx_find_lchan(ts, chan); + if (lchan == NULL) + return -EINVAL; + + /* Set up pointers */ + first_fn = &lchan->rx_first_fn; + mask = &lchan->rx_burst_mask; + buffer = lchan->rx_bursts; + + rssi_sum = &lchan->rssi_sum; + rssi_num = &lchan->rssi_num; + toa_sum = &lchan->toa_sum; + toa_num = &lchan->toa_num; + + /* Clear buffer & store frame number of first burst */ + if (bid == 0) { + memset(buffer, 0, 464); + + *first_fn = fn; + *mask = 0x0; + + *rssi_sum = 0; + *rssi_num = 0; + *toa_sum = 0; + *toa_num = 0; + } + + /* Update mask and RSSI */ + *mask |= (1 << bid); + *rssi_sum += rssi; + (*rssi_num)++; + *toa_sum += toa; + (*toa_num)++; + + /* Copy burst to buffer of 4 bursts */ + offset = buffer + bid * 116; + memcpy(offset, bits + 3, 58); + memcpy(offset + 58, bits + 87, 58); + + /* Wait until complete set of bursts */ + if (bid != 3) + return 0; + + /* Check for complete set of bursts */ + if ((*mask & 0xf) != 0xf) { + LOGP(DSCH, LOGL_DEBUG, "Received incomplete data frame at " + "fn=%u (%u/%u) for %s\n", *first_fn, + (*first_fn) % ts->mf_layout->period, + ts->mf_layout->period, + trx_lchan_desc[chan].name); + + /* We require first burst to have correct FN */ + if (!(*mask & 0x1)) { + *mask = 0x0; + return 0; + } + + /* FIXME: return from here? */ + } + + /* Attempt to decode */ + rc = gsm0503_xcch_decode(l2, buffer, &n_errors, &n_bits_total); + if (rc) { + LOGP(DSCH, LOGL_DEBUG, "Received bad data frame at fn=%u " + "(%u/%u) for %s\n", *first_fn, + (*first_fn) % ts->mf_layout->period, + ts->mf_layout->period, + trx_lchan_desc[chan].name); + return rc; + } + + /* Compose a message to the higher layers */ + struct l1ctl_info_dl *data; + data = talloc_zero_size(ts, sizeof(struct l1ctl_info_dl) + 23); + if (data == NULL) + return -ENOMEM; + + /* Fill in some downlink info */ + data->chan_nr = trx_lchan_desc[chan].chan_nr | ts->index; + data->link_id = trx_lchan_desc[chan].link_id; + data->band_arfcn = htons(trx->band_arfcn); + data->frame_nr = htonl(*first_fn); + data->rx_level = -(*rssi_sum / *rssi_num); + + /* FIXME: set proper values */ + data->num_biterr = n_errors; + data->fire_crc = 0; + data->snr = 0; + + /* Fill in decoded payload */ + memcpy(data->payload, l2, 23); + + /* Raise an event to trxcon */ + osmo_fsm_inst_dispatch(trxcon_fsm, SCH_EVENT_DATA, data); + + /* TODO: AGC, TA loops */ + return 0; +} diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index 6f50d1a20..32de25369 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -130,6 +130,8 @@ static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi, case L1CTL_EVENT_FBSB_REQ: trxcon_handle_fbsb_req((struct l1ctl_fbsb_req *) data); break; + case SCH_EVENT_DATA: + l1ctl_tx_data_ind(app_data.l1l, (struct l1ctl_info_dl *) data); case TRX_EVENT_RSP_ERROR: case TRX_EVENT_OFFLINE: case SCH_EVENT_CLCK_IND: @@ -157,7 +159,8 @@ static struct osmo_fsm_state trxcon_fsm_states[] = { GEN_MASK(TRX_EVENT_RSP_ERROR) | GEN_MASK(TRX_EVENT_OFFLINE) | GEN_MASK(SCH_EVENT_CLCK_IND) | - GEN_MASK(SCH_EVENT_CLCK_LOSS)), + GEN_MASK(SCH_EVENT_CLCK_LOSS) | + GEN_MASK(SCH_EVENT_DATA)), .out_state_mask = GEN_MASK(TRXCON_STATE_IDLE), .name = "MANAGED", .action = trxcon_fsm_managed_action, diff --git a/src/host/trxcon/trxcon.h b/src/host/trxcon/trxcon.h index c266eaee2..da777a9dd 100644 --- a/src/host/trxcon/trxcon.h +++ b/src/host/trxcon/trxcon.h @@ -22,4 +22,5 @@ enum trxcon_fsm_events { /* Scheduler specific events */ SCH_EVENT_CLCK_IND, SCH_EVENT_CLCK_LOSS, + SCH_EVENT_DATA, }; From 6ab67cc58704089ebf5a27f973ebaf36943e435f Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 7 Jul 2017 07:11:26 +0700 Subject: [PATCH 017/211] host/trxcon/scheduler: implement SCH decoding Change-Id: Ic5e7416271d647752cd1aaf90de51fa48286798e --- src/host/trxcon/sched_lchan_desc.c | 11 ++++- src/host/trxcon/sched_lchan_handlers.c | 64 ++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/src/host/trxcon/sched_lchan_desc.c b/src/host/trxcon/sched_lchan_desc.c index 93927a638..1ff6e1444 100644 --- a/src/host/trxcon/sched_lchan_desc.c +++ b/src/host/trxcon/sched_lchan_desc.c @@ -44,6 +44,10 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, uint32_t fn, enum trx_lchan_type chan, uint8_t bid, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); +int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, + uint32_t fn, enum trx_lchan_type chan, uint8_t bid, + sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); + const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { { TRXC_IDLE, "IDLE", @@ -69,8 +73,11 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { 0x00, LID_DEDIC, 0x00, 0x00, - /* We already have clock indications from TRX */ - NULL, NULL, + /** + * We already have clock indications from TRX, + * but we also need BSIC (BCC / NCC) value. + */ + rx_sch_fn, NULL, }, { TRXC_BCCH, "BCCH", diff --git a/src/host/trxcon/sched_lchan_handlers.c b/src/host/trxcon/sched_lchan_handlers.c index 2c2cb6a7c..41e60d8bd 100644 --- a/src/host/trxcon/sched_lchan_handlers.c +++ b/src/host/trxcon/sched_lchan_handlers.c @@ -34,6 +34,7 @@ #include #include +#include #include #include "l1ctl_proto.h" @@ -159,3 +160,66 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, /* TODO: AGC, TA loops */ return 0; } + +static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info) +{ + uint8_t t3p; + uint32_t sb; + + sb = (sb_info[3] << 24) + | (sb_info[2] << 16) + | (sb_info[1] << 8) + | sb_info[0]; + + *bsic = (sb >> 2) & 0x3f; + + /* TS 05.02 Chapter 3.3.2.2.1 SCH Frame Numbers */ + time->t1 = ((sb >> 23) & 0x01) + | ((sb >> 7) & 0x1fe) + | ((sb << 9) & 0x600); + + time->t2 = (sb >> 18) & 0x1f; + + t3p = ((sb >> 24) & 0x01) | ((sb >> 15) & 0x06); + time->t3 = t3p * 10 + 1; + + /* TS 05.02 Chapter 4.3.3 TDMA frame number */ + time->fn = gsm_gsmtime2fn(time); +} + +int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, + uint32_t fn, enum trx_lchan_type chan, uint8_t bid, + sbit_t *bits, uint16_t nbits, int8_t rssi, float toa) +{ + sbit_t payload[2 * 39]; + struct gsm_time time; + uint8_t sb_info[4]; + uint8_t bsic; + int rc; + + /* Obtain payload from burst */ + memcpy(payload, bits + 3, 39); + memcpy(payload + 39, bits + 3 + 39 + 64, 39); + + /* Attempt to decode */ + rc = gsm0503_sch_decode(sb_info, payload); + if (rc) { + LOGP(DSCH, LOGL_DEBUG, "Received bad SCH burst at fn=%u\n", fn); + return rc; + } + + /* Decode BSIC and TDMA frame number */ + decode_sb(&time, &bsic, sb_info); + + LOGP(DSCH, LOGL_DEBUG, "Received SCH: bsic=%u, fn=%u, sched_fn=%u\n", + bsic, time.fn, trx->sched.fn_counter_proc); + + /* Check if decoded frame number matches */ + if (time.fn != fn) { + LOGP(DSCH, LOGL_ERROR, "Decoded fn=%u does not match " + "fn=%u provided by scheduler\n", time.fn, fn); + return -EINVAL; + } + + return 0; +} From 705fedceebc116402f8a6ae67480c994ed745593 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 8 Jul 2017 18:16:42 +0700 Subject: [PATCH 018/211] host/trxcon: bind L1CTL link with TRX and vice versa Change-Id: I575f8699bf06fd5e86f7935c6ab3216db5a26ec5 --- src/host/trxcon/l1ctl_link.h | 6 ++++++ src/host/trxcon/trx_if.h | 6 ++++++ src/host/trxcon/trxcon.c | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/src/host/trxcon/l1ctl_link.h b/src/host/trxcon/l1ctl_link.h index 620dde506..43d187a31 100644 --- a/src/host/trxcon/l1ctl_link.h +++ b/src/host/trxcon/l1ctl_link.h @@ -8,6 +8,9 @@ #define L1CTL_LENGTH 256 #define L1CTL_HEADROOM 32 +/* Forward declaration to avoid mutual include */ +struct trx_instance; + enum l1ctl_fsm_states { L1CTL_STATE_IDLE = 0, L1CTL_STATE_CONNECTED, @@ -17,6 +20,9 @@ struct l1ctl_link { struct osmo_fsm_inst *fsm; struct osmo_fd listen_bfd; struct osmo_wqueue wq; + + /* Bind TRX instance */ + struct trx_instance *trx; }; int l1ctl_link_init(struct l1ctl_link **l1l, const char *sock_path); diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h index 4d9e4709d..5e9aebae5 100644 --- a/src/host/trxcon/trx_if.h +++ b/src/host/trxcon/trx_if.h @@ -8,6 +8,9 @@ #include "scheduler.h" #include "sched_trx.h" +/* Forward declaration to avoid mutual include */ +struct l1ctl_link; + enum trx_fsm_states { TRX_STATE_OFFLINE = 0, TRX_STATE_IDLE, @@ -29,6 +32,9 @@ struct trx_instance { /* Scheduler stuff */ struct trx_sched sched; struct llist_head ts_list; + + /* Bind L1CTL link */ + struct l1ctl_link *l1l; }; struct trx_ctrl_msg { diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index 32de25369..de69db195 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -301,6 +301,10 @@ int main(int argc, char **argv) if (rc) goto exit; + /* Bind L1CTL with TRX and vice versa */ + app_data.l1l->trx = app_data.trx; + app_data.trx->l1l = app_data.l1l; + /* Init scheduler */ rc = sched_trx_init(app_data.trx); if (rc) From c457cbf712dbbcdfd2e63a713591f041f531d3fc Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 8 Jul 2017 18:30:56 +0700 Subject: [PATCH 019/211] host/trxcon: handle L1CTL_FBSB_REQ inside l1ctl.c Change-Id: I5bcf39a20f1c6d3a2472e5e95725c6bb1c77bf5d --- src/host/trxcon/l1ctl.c | 32 +++++++++++++++++++------------- src/host/trxcon/trxcon.c | 29 ----------------------------- src/host/trxcon/trxcon.h | 1 - 3 files changed, 19 insertions(+), 43 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 3bcc5cb3a..4abec77c4 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -43,6 +43,9 @@ #include "l1ctl_link.h" #include "l1ctl_proto.h" +#include "trx_if.h" +#include "sched_trx.h" + extern void *tall_trx_ctx; extern struct osmo_fsm_inst *trxcon_fsm; @@ -147,7 +150,7 @@ int l1ctl_tx_data_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data) static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg) { - struct l1ctl_fbsb_req *fbsb, *fbsb_copy; + struct l1ctl_fbsb_req *fbsb; uint16_t band_arfcn; int rc = 0; @@ -165,19 +168,22 @@ static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg) gsm_band_name(gsm_arfcn2band(band_arfcn)), band_arfcn &~ ARFCN_FLAG_MASK); - /** - * We cannot simply pass a pointer to fbsb, - * because the memory will be freed. - * - * TODO: better solution? - */ - fbsb_copy = talloc_memdup(l1l, fbsb, sizeof(struct l1ctl_fbsb_req)); - if (fbsb_copy == NULL) { - rc = -EINVAL; - goto exit; - } + /* Reset L1 */ + sched_trx_reset(l1l->trx); - osmo_fsm_inst_dispatch(trxcon_fsm, L1CTL_EVENT_FBSB_REQ, fbsb_copy); + /* Configure a single timeslot */ + if (fbsb->ccch_mode == CCCH_MODE_COMBINED) + sched_trx_configure_ts(l1l->trx, 0, GSM_PCHAN_CCCH_SDCCH4); + else + sched_trx_configure_ts(l1l->trx, 0, GSM_PCHAN_CCCH); + + /* Store current ARFCN */ + l1l->trx->band_arfcn = band_arfcn; + + /* Tune transceiver to required ARFCN */ + trx_if_cmd_rxtune(l1l->trx, band_arfcn); + trx_if_cmd_txtune(l1l->trx, band_arfcn); + trx_if_cmd_poweron(l1l->trx); exit: msgb_free(msg); diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index de69db195..9bc98e55c 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -74,31 +74,6 @@ static struct { void *tall_trx_ctx = NULL; struct osmo_fsm_inst *trxcon_fsm; -static void trxcon_handle_fbsb_req(struct l1ctl_fbsb_req *req) -{ - uint16_t band_arfcn; - - /* Reset L1 */ - sched_trx_reset(app_data.trx); - - /* Configure a single timeslot */ - if (req->ccch_mode == CCCH_MODE_COMBINED) - sched_trx_configure_ts(app_data.trx, 0, GSM_PCHAN_CCCH_SDCCH4); - else - sched_trx_configure_ts(app_data.trx, 0, GSM_PCHAN_CCCH); - - /* Store current ARFCN */ - band_arfcn = ntohs(req->band_arfcn); - app_data.trx->band_arfcn = band_arfcn; - - /* Tune transceiver to required ARFCN */ - trx_if_cmd_rxtune(app_data.trx, band_arfcn); - trx_if_cmd_txtune(app_data.trx, band_arfcn); - trx_if_cmd_poweron(app_data.trx); - - talloc_free(req); -} - static void trxcon_fsm_idle_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) { @@ -127,9 +102,6 @@ static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi, /* TODO: send proper reset type */ l1ctl_tx_reset_conf(app_data.l1l, L1CTL_RES_T_BOOT); break; - case L1CTL_EVENT_FBSB_REQ: - trxcon_handle_fbsb_req((struct l1ctl_fbsb_req *) data); - break; case SCH_EVENT_DATA: l1ctl_tx_data_ind(app_data.l1l, (struct l1ctl_info_dl *) data); case TRX_EVENT_RSP_ERROR: @@ -153,7 +125,6 @@ static struct osmo_fsm_state trxcon_fsm_states[] = { [TRXCON_STATE_MANAGED] = { .in_event_mask = ( GEN_MASK(L1CTL_EVENT_DISCONNECT) | - GEN_MASK(L1CTL_EVENT_FBSB_REQ) | GEN_MASK(L1CTL_EVENT_RESET_REQ) | GEN_MASK(TRX_EVENT_RESET_IND) | GEN_MASK(TRX_EVENT_RSP_ERROR) | diff --git a/src/host/trxcon/trxcon.h b/src/host/trxcon/trxcon.h index da777a9dd..7b3a23213 100644 --- a/src/host/trxcon/trxcon.h +++ b/src/host/trxcon/trxcon.h @@ -11,7 +11,6 @@ enum trxcon_fsm_events { /* L1CTL specific events */ L1CTL_EVENT_CONNECT, L1CTL_EVENT_DISCONNECT, - L1CTL_EVENT_FBSB_REQ, L1CTL_EVENT_RESET_REQ, /* TRX specific events */ From e738f7827e77a05a7b214bfab1309b2da232a31f Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 8 Jul 2017 18:50:14 +0700 Subject: [PATCH 020/211] host/trxcon: handle L1CTL_RESET_REQ inside l1ctl.c Change-Id: Ie5930dec800885784fb38ce6188c9c969cd2ad4e --- src/host/trxcon/l1ctl.c | 20 ++++++++++++++++++-- src/host/trxcon/trxcon.c | 12 ++---------- src/host/trxcon/trxcon.h | 1 - 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 4abec77c4..c01d372b3 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -245,8 +245,24 @@ static int l1ctl_rx_reset_req(struct l1ctl_link *l1l, struct msgb *msg) LOGP(DL1C, LOGL_DEBUG, "Recv Reset Req (%u)\n", res->type); - osmo_fsm_inst_dispatch(trxcon_fsm, - L1CTL_EVENT_RESET_REQ, res); + switch (res->type) { + case L1CTL_RES_T_FULL: + /* TODO: implement trx_if_reset() */ + trx_if_flush_ctrl(l1l->trx); + trx_if_cmd_poweroff(l1l->trx); + trx_if_cmd_echo(l1l->trx); + + /* Fall through */ + case L1CTL_RES_T_SCHED: + sched_trx_reset(l1l->trx); + break; + default: + LOGP(DL1C, LOGL_ERROR, "Unknown L1CTL_RESET_REQ type\n"); + goto exit; + } + + /* Confirm */ + rc = l1ctl_tx_reset_conf(l1l, res->type); exit: msgb_free(msg); diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index 9bc98e55c..a87626f2b 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -89,25 +89,18 @@ static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi, osmo_fsm_inst_state_chg(trxcon_fsm, TRXCON_STATE_IDLE, 0, 0); if (app_data.trx->fsm->state != TRX_STATE_OFFLINE) { + /* TODO: implement trx_if_reset() */ trx_if_flush_ctrl(app_data.trx); trx_if_cmd_poweroff(app_data.trx); } break; - case L1CTL_EVENT_RESET_REQ: - trx_if_cmd_poweroff(app_data.trx); - trx_if_cmd_echo(app_data.trx); - sched_trx_reset(app_data.trx); - break; - case TRX_EVENT_RESET_IND: - /* TODO: send proper reset type */ - l1ctl_tx_reset_conf(app_data.l1l, L1CTL_RES_T_BOOT); - break; case SCH_EVENT_DATA: l1ctl_tx_data_ind(app_data.l1l, (struct l1ctl_info_dl *) data); case TRX_EVENT_RSP_ERROR: case TRX_EVENT_OFFLINE: case SCH_EVENT_CLCK_IND: case SCH_EVENT_CLCK_LOSS: + case TRX_EVENT_RESET_IND: /* TODO: notify L2 & L3 about that */ break; default: @@ -125,7 +118,6 @@ static struct osmo_fsm_state trxcon_fsm_states[] = { [TRXCON_STATE_MANAGED] = { .in_event_mask = ( GEN_MASK(L1CTL_EVENT_DISCONNECT) | - GEN_MASK(L1CTL_EVENT_RESET_REQ) | GEN_MASK(TRX_EVENT_RESET_IND) | GEN_MASK(TRX_EVENT_RSP_ERROR) | GEN_MASK(TRX_EVENT_OFFLINE) | diff --git a/src/host/trxcon/trxcon.h b/src/host/trxcon/trxcon.h index 7b3a23213..6452f8038 100644 --- a/src/host/trxcon/trxcon.h +++ b/src/host/trxcon/trxcon.h @@ -11,7 +11,6 @@ enum trxcon_fsm_events { /* L1CTL specific events */ L1CTL_EVENT_CONNECT, L1CTL_EVENT_DISCONNECT, - L1CTL_EVENT_RESET_REQ, /* TRX specific events */ TRX_EVENT_RESET_IND, From 7ce986c6385f6648261b7b17483a740175cd3019 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 8 Jul 2017 18:58:51 +0700 Subject: [PATCH 021/211] host/trxcon: send L1CTL_DATA_IND directly from lchan handler Change-Id: Idfc86a59469c7a0bba1c16177502844e59ed8887 --- src/host/trxcon/l1ctl.c | 1 - src/host/trxcon/sched_lchan_handlers.c | 6 ++++-- src/host/trxcon/trxcon.c | 5 +---- src/host/trxcon/trxcon.h | 1 - 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index c01d372b3..ab7319497 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -142,7 +142,6 @@ int l1ctl_tx_data_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data) /* Copy header and data from source message */ memcpy(dl, data, len); - talloc_free(data); /* Put message to upper layers */ return l1ctl_link_send(l1l, msg); diff --git a/src/host/trxcon/sched_lchan_handlers.c b/src/host/trxcon/sched_lchan_handlers.c index 41e60d8bd..7c18fdd0e 100644 --- a/src/host/trxcon/sched_lchan_handlers.c +++ b/src/host/trxcon/sched_lchan_handlers.c @@ -43,6 +43,7 @@ #include "logging.h" #include "trx_if.h" #include "trxcon.h" +#include "l1ctl.h" extern struct osmo_fsm_inst *trxcon_fsm; @@ -154,8 +155,9 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, /* Fill in decoded payload */ memcpy(data->payload, l2, 23); - /* Raise an event to trxcon */ - osmo_fsm_inst_dispatch(trxcon_fsm, SCH_EVENT_DATA, data); + /* Put a packet to higher layers */ + l1ctl_tx_data_ind(trx->l1l, data); + talloc_free(data); /* TODO: AGC, TA loops */ return 0; diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index a87626f2b..0ff3a7a46 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -94,8 +94,6 @@ static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi, trx_if_cmd_poweroff(app_data.trx); } break; - case SCH_EVENT_DATA: - l1ctl_tx_data_ind(app_data.l1l, (struct l1ctl_info_dl *) data); case TRX_EVENT_RSP_ERROR: case TRX_EVENT_OFFLINE: case SCH_EVENT_CLCK_IND: @@ -122,8 +120,7 @@ static struct osmo_fsm_state trxcon_fsm_states[] = { GEN_MASK(TRX_EVENT_RSP_ERROR) | GEN_MASK(TRX_EVENT_OFFLINE) | GEN_MASK(SCH_EVENT_CLCK_IND) | - GEN_MASK(SCH_EVENT_CLCK_LOSS) | - GEN_MASK(SCH_EVENT_DATA)), + GEN_MASK(SCH_EVENT_CLCK_LOSS)), .out_state_mask = GEN_MASK(TRXCON_STATE_IDLE), .name = "MANAGED", .action = trxcon_fsm_managed_action, diff --git a/src/host/trxcon/trxcon.h b/src/host/trxcon/trxcon.h index 6452f8038..b3d4e3e25 100644 --- a/src/host/trxcon/trxcon.h +++ b/src/host/trxcon/trxcon.h @@ -20,5 +20,4 @@ enum trxcon_fsm_events { /* Scheduler specific events */ SCH_EVENT_CLCK_IND, SCH_EVENT_CLCK_LOSS, - SCH_EVENT_DATA, }; From 99f8aea905de115a15460b23d69d352ccb563dc5 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 8 Jul 2017 19:39:14 +0700 Subject: [PATCH 022/211] host/trxcon/scheduler: fix channel deactivation Change-Id: I7c99b7bd99619084727af290b388f7492ba58531 --- src/host/trxcon/sched_trx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 5cf877952..5499a24c7 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -312,9 +312,9 @@ int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) if (lchan == NULL) return -EINVAL; - if (lchan->active) { + if (!lchan->active) { LOGP(DSCH, LOGL_ERROR, "Logical channel %s already deactivated " - "on ts=%d", trx_lchan_desc[chan].name, ts->index); + "on ts=%d\n", trx_lchan_desc[chan].name, ts->index); return -EINVAL; } From cc4282f5bedd1390934552c682302691b72e23e2 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 8 Jul 2017 20:28:09 +0700 Subject: [PATCH 023/211] host/trxcon/l1ctl.c: implement L1CTL_FBSB_CONF Change-Id: I33e4702d590a8d67285410fc8f1241bb9d4c50fc --- src/host/trxcon/l1ctl.c | 28 ++++++++++++++++++++++++++ src/host/trxcon/l1ctl.h | 1 + src/host/trxcon/l1ctl_link.h | 3 +++ src/host/trxcon/sched_lchan_desc.c | 2 +- src/host/trxcon/sched_lchan_handlers.c | 4 ++++ 5 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index ab7319497..255177ec6 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -126,6 +126,31 @@ int l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type) return l1ctl_link_send(l1l, msg); } +int l1ctl_tx_fbsb_conf(struct l1ctl_link *l1l, uint8_t result, uint8_t bsic) +{ + struct l1ctl_fbsb_conf *conf; + struct msgb *msg; + + msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF); + if (msg == NULL) + return -ENOMEM; + + LOGP(DL1C, LOGL_DEBUG, "Send FBSB Conf (result=%u, bsic=%u)\n", + result, bsic); + + conf = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*conf)); + conf->result = result; + conf->bsic = bsic; + + /* FIXME: set proper value */ + conf->initial_freq_err = 0; + + /* Ask SCH handler not to send L1CTL_FBSB_CONF anymore */ + l1l->fbsb_conf_sent = 1; + + return l1ctl_link_send(l1l, msg); +} + int l1ctl_tx_data_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data) { struct l1ctl_info_dl *dl; @@ -176,6 +201,9 @@ static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg) else sched_trx_configure_ts(l1l->trx, 0, GSM_PCHAN_CCCH); + /* Ask SCH handler to send L1CTL_FBSB_CONF */ + l1l->fbsb_conf_sent = 0; + /* Store current ARFCN */ l1l->trx->band_arfcn = band_arfcn; diff --git a/src/host/trxcon/l1ctl.h b/src/host/trxcon/l1ctl.h index 05a2c5437..124074b5f 100644 --- a/src/host/trxcon/l1ctl.h +++ b/src/host/trxcon/l1ctl.h @@ -6,6 +6,7 @@ #include "l1ctl_link.h" #include "l1ctl_proto.h" +int l1ctl_tx_fbsb_conf(struct l1ctl_link *l1l, uint8_t result, uint8_t bsic); int l1ctl_tx_pm_conf(struct l1ctl_link *l1l, uint16_t band_arfcn, int dbm, int last); int l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type); diff --git a/src/host/trxcon/l1ctl_link.h b/src/host/trxcon/l1ctl_link.h index 43d187a31..b310ee432 100644 --- a/src/host/trxcon/l1ctl_link.h +++ b/src/host/trxcon/l1ctl_link.h @@ -23,6 +23,9 @@ struct l1ctl_link { /* Bind TRX instance */ struct trx_instance *trx; + + /* L1CTL handlers specific */ + uint8_t fbsb_conf_sent; }; int l1ctl_link_init(struct l1ctl_link **l1l, const char *sock_path); diff --git a/src/host/trxcon/sched_lchan_desc.c b/src/host/trxcon/sched_lchan_desc.c index 1ff6e1444..f82a982d1 100644 --- a/src/host/trxcon/sched_lchan_desc.c +++ b/src/host/trxcon/sched_lchan_desc.c @@ -71,7 +71,7 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { { TRXC_SCH, "SCH", 0x00, LID_DEDIC, - 0x00, 0x00, + 0x00, TRX_CH_FLAG_AUTO, /** * We already have clock indications from TRX, diff --git a/src/host/trxcon/sched_lchan_handlers.c b/src/host/trxcon/sched_lchan_handlers.c index 7c18fdd0e..4d5351657 100644 --- a/src/host/trxcon/sched_lchan_handlers.c +++ b/src/host/trxcon/sched_lchan_handlers.c @@ -223,5 +223,9 @@ int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, return -EINVAL; } + /* Send L1CTL_FBSB_CONF to higher layers */ + if (!trx->l1l->fbsb_conf_sent) + l1ctl_tx_fbsb_conf(trx->l1l, 0, bsic); + return 0; } From 3afd469a9aa8a333b6bf1dcc13a298e1b091b357 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 8 Jul 2017 20:53:10 +0700 Subject: [PATCH 024/211] host/trxcon/l1ctl.c: implement L1CTL_CCCH_MODE_{REQ/CONF} Change-Id: I72f36a99aebcbafd657ceb475fd1c50cc79e2094 --- src/host/trxcon/l1ctl.c | 46 +++++++++++++++++++++++++++++++++++++++++ src/host/trxcon/l1ctl.h | 1 + 2 files changed, 47 insertions(+) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 255177ec6..beb5734ac 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -151,6 +151,21 @@ int l1ctl_tx_fbsb_conf(struct l1ctl_link *l1l, uint8_t result, uint8_t bsic) return l1ctl_link_send(l1l, msg); } +int l1ctl_tx_ccch_mode_conf(struct l1ctl_link *l1l, uint8_t mode) +{ + struct l1ctl_ccch_mode_conf *conf; + struct msgb *msg; + + msg = l1ctl_alloc_msg(L1CTL_CCCH_MODE_CONF); + if (msg == NULL) + return -ENOMEM; + + conf = (struct l1ctl_ccch_mode_conf *) msgb_put(msg, sizeof(*conf)); + conf->ccch_mode = mode; + + return l1ctl_link_send(l1l, msg); +} + int l1ctl_tx_data_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data) { struct l1ctl_info_dl *dl; @@ -311,6 +326,35 @@ static int l1ctl_rx_echo_req(struct l1ctl_link *l1l, struct msgb *msg) return l1ctl_link_send(l1l, msg); } +static int l1ctl_rx_ccch_mode_req(struct l1ctl_link *l1l, struct msgb *msg) +{ + struct l1ctl_ccch_mode_req *req; + int mode, rc = 0; + + req = (struct l1ctl_ccch_mode_req *) msg->l1h; + if (msgb_l1len(msg) < sizeof(*req)) { + LOGP(DL1C, LOGL_ERROR, "MSG too short Reset Req: %u\n", + msgb_l1len(msg)); + rc = -EINVAL; + goto exit; + } + + LOGP(DL1C, LOGL_DEBUG, "Recv CCCH Mode Req (%u)\n", req->ccch_mode); + + /* Reconfigure TS0 */ + mode = req->ccch_mode == CCCH_MODE_COMBINED ? + GSM_PCHAN_CCCH_SDCCH4 : GSM_PCHAN_CCCH; + rc = sched_trx_configure_ts(l1l->trx, 0, mode); + + /* Confirm reconfiguration */ + if (!rc) + rc = l1ctl_tx_ccch_mode_conf(l1l, req->ccch_mode); + +exit: + msgb_free(msg); + return rc; +} + int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) { struct l1ctl_hdr *l1h; @@ -327,6 +371,8 @@ int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) return l1ctl_rx_reset_req(l1l, msg); case L1CTL_ECHO_REQ: return l1ctl_rx_echo_req(l1l, msg); + case L1CTL_CCCH_MODE_REQ: + return l1ctl_rx_ccch_mode_req(l1l, msg); default: LOGP(DL1C, LOGL_ERROR, "Unknown MSG: %u\n", l1h->msg_type); msgb_free(msg); diff --git a/src/host/trxcon/l1ctl.h b/src/host/trxcon/l1ctl.h index 124074b5f..dae75e92c 100644 --- a/src/host/trxcon/l1ctl.h +++ b/src/host/trxcon/l1ctl.h @@ -7,6 +7,7 @@ #include "l1ctl_proto.h" int l1ctl_tx_fbsb_conf(struct l1ctl_link *l1l, uint8_t result, uint8_t bsic); +int l1ctl_tx_ccch_mode_conf(struct l1ctl_link *l1l, uint8_t mode); int l1ctl_tx_pm_conf(struct l1ctl_link *l1l, uint16_t band_arfcn, int dbm, int last); int l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type); From ca1d021c838193ff28c133e9a04d2b8ca3f484e3 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 8 Jul 2017 21:03:22 +0700 Subject: [PATCH 025/211] host/trxcon/scheduler: drop resolved FIXME label Change-Id: I5e6b30300a457422b2deb10ff4c051c86beb7cd2 --- src/host/trxcon/sched_trx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 5499a24c7..32e6a5908 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -238,7 +238,7 @@ int sched_trx_reset_ts(struct trx_instance *trx, int ts_num) if (ts == NULL) return -EINVAL; - /* FIXME: where do we need it? */ + /* Flush TS frame counter */ ts->mf_last_fn = 0; /* Undefine multiframe layout */ From 92aca364f7c0492689b990c3ea6937fe941e598a Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 10 Jul 2017 16:56:43 +0700 Subject: [PATCH 026/211] host/trxcon: reset scheduler when L1CTL is lost Change-Id: I1fd8a610085c8591a820e784b7122de7b3032d15 --- src/host/trxcon/trxcon.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index 0ff3a7a46..c0d5da323 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -89,6 +89,9 @@ static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi, osmo_fsm_inst_state_chg(trxcon_fsm, TRXCON_STATE_IDLE, 0, 0); if (app_data.trx->fsm->state != TRX_STATE_OFFLINE) { + /* Reset scheduler */ + sched_trx_reset(app_data.trx); + /* TODO: implement trx_if_reset() */ trx_if_flush_ctrl(app_data.trx); trx_if_cmd_poweroff(app_data.trx); From 3187c8e68d6259f092e24123c9b96c6909d2338e Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 10 Jul 2017 19:39:06 +0700 Subject: [PATCH 027/211] target/fake_trx: initial release of virtual transceiver This is a set of tools for creating a virtual Um-interface between OsmocomBB and OsmoBTS. It may be extremely useful for testing and development of GSM stack, including both sides (MS and BTS). This software implements OsmoTRX (Osmocom's fork of OpenBTS transceiver) style clock (CLCK), control (CTRL) and data interfaces. So, OsmoBTS source code doesn't require any modifications, while for OsmocomBB you will need to use a new application - trxcon, which can be found in the 'fixeria/sdr_phy' branch until one is merged to master. Brief description of available applications: - fake_trx.py - main application, that allows to connect both OsmocomBB and OsmoBTS without actual RF hardware. Currently only a single MS may work with a single BTS. - clck_gen.py - a peripheral tool aimed to emulate TDMA frame clock generator. Could be used for testing and clock synchronization of multiple applications. It should be noted, that one relays on generic system timer (via Python), so a random clock jitter takes place. - ctrl_cmd.py - another peripheral tool, which could be used for sending CTRL commands directly in manual mode, and also for application fuzzing. Change-Id: Ib1fb80682002ac85a72fa6abef459a4c44f4ab97 --- src/target/fake_trx/.gitignore | 4 + src/target/fake_trx/README | 24 ++++ src/target/fake_trx/burst_fwd.py | 75 +++++++++++ src/target/fake_trx/clck_gen.py | 104 +++++++++++++++ src/target/fake_trx/ctrl_cmd.py | 77 +++++++++++ src/target/fake_trx/ctrl_if.py | 74 +++++++++++ src/target/fake_trx/ctrl_if_bb.py | 87 ++++++++++++ src/target/fake_trx/ctrl_if_bts.py | 87 ++++++++++++ src/target/fake_trx/fake_trx.py | 204 +++++++++++++++++++++++++++++ src/target/fake_trx/udp_link.py | 53 ++++++++ 10 files changed, 789 insertions(+) create mode 100644 src/target/fake_trx/.gitignore create mode 100644 src/target/fake_trx/README create mode 100644 src/target/fake_trx/burst_fwd.py create mode 100755 src/target/fake_trx/clck_gen.py create mode 100755 src/target/fake_trx/ctrl_cmd.py create mode 100644 src/target/fake_trx/ctrl_if.py create mode 100644 src/target/fake_trx/ctrl_if_bb.py create mode 100644 src/target/fake_trx/ctrl_if_bts.py create mode 100755 src/target/fake_trx/fake_trx.py create mode 100644 src/target/fake_trx/udp_link.py diff --git a/src/target/fake_trx/.gitignore b/src/target/fake_trx/.gitignore new file mode 100644 index 000000000..749ccdafd --- /dev/null +++ b/src/target/fake_trx/.gitignore @@ -0,0 +1,4 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class diff --git a/src/target/fake_trx/README b/src/target/fake_trx/README new file mode 100644 index 000000000..ac138e099 --- /dev/null +++ b/src/target/fake_trx/README @@ -0,0 +1,24 @@ +This is a set of tools for creating a virtual Um-interface between +OsmocomBB and OsmoBTS. It may be extremely useful for testing and +development of GSM stack, including both sides (MS and BTS). This +software implements OsmoTRX (Osmocom's fork of OpenBTS transceiver) +style clock (CLCK), control (CTRL) and data interfaces. So, OsmoBTS +source code doesn't require any modifications, while for OsmocomBB +you will need to use a new application - trxcon, which can be found +in the 'fixeria/sdr_phy' branch until one is merged to master. + +Brief description of available applications: + + - fake_trx.py - main application, that allows to connect both + OsmocomBB and OsmoBTS without actual RF hardware. Currently + only a single MS may work with a single BTS. + + - clck_gen.py - a peripheral tool aimed to emulate TDMA frame + clock generator. Could be used for testing and clock + synchronization of multiple applications. It should be noted, + that one relays on generic system timer (via Python), so + a random clock jitter takes place. + + - ctrl_cmd.py - another peripheral tool, which could be used + for sending CTRL commands directly in manual mode, and also + for application fuzzing. diff --git a/src/target/fake_trx/burst_fwd.py b/src/target/fake_trx/burst_fwd.py new file mode 100644 index 000000000..d82274718 --- /dev/null +++ b/src/target/fake_trx/burst_fwd.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# Virtual Um-interface (fake transceiver) +# BTS <-> BB burst forwarding +# +# (C) 2017 by Vadim Yanitskiy +# +# 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. + +class BurstForwarder: + # Timeslot filter + ts_pass = 0 + + def __init__(self, bts_link, bb_link): + self.bts_link = bts_link + self.bb_link = bb_link + + def set_slot(self, ts): + if ts > 0 and ts < 8: + self.ts_pass = ts + else: + raise ValueError("Incorrect index for timeslot filter") + + def process_payload(self, data): + payload = bytearray(data) + length = len(payload) + + # HACK: set fake RSSI value (-53) + payload[5] = 0x35 + + # HACK: add fake TOA value (6th and 7th bytes) + payload[6:2] = [0x00, 0x00] + + # Convert ubits to {255..0} + for i in range(8, length): + payload[i] = 255 if payload[i] else 0 + + return payload + + # Downlink handler: BTS -> BB + def bts2bb(self): + # Read data from socket + data, addr = self.bts_link.sock.recvfrom(512) + payload = self.process_payload(data) + + # Timeslot filter + if payload[0] != self.ts_pass: + return None + + # Send burst to BB + self.bb_link.send(payload) + + # Uplink handler: BB -> BTS + def bb2bts(self): + # Read data from socket + data, addr = self.bb_link.sock.recvfrom(512) + payload = self.process_payload(data) + + # Send burst to BTS + self.bts_link.send(payload) diff --git a/src/target/fake_trx/clck_gen.py b/src/target/fake_trx/clck_gen.py new file mode 100755 index 000000000..4ef597f67 --- /dev/null +++ b/src/target/fake_trx/clck_gen.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# Virtual Um-interface (fake transceiver) +# Simple TDMA frame clock generator +# +# (C) 2017 by Vadim Yanitskiy +# +# 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. + +import signal +import time +import sys + +from threading import Timer +from udp_link import UDPLink + +class CLCKGen: + # GSM TDMA definitions + SEC_DELAY_US = 1000 * 1000 + GSM_SUPERFRAME = 2715648 + GSM_FRAME_US = 4615.0 + + # Average loop back delay + LO_DELAY_US = 90.0 + + def __init__(self, clck_links, clck_start = 0, ind_period = 102): + self.clck_links = clck_links + self.ind_period = ind_period + self.clck_src = clck_start + + # Calculate counter time + self.ctr_interval = self.GSM_FRAME_US - self.LO_DELAY_US + self.ctr_interval /= self.SEC_DELAY_US + self.ctr_interval *= self.ind_period + + # Create a timer manager + self.timer = Timer(self.ctr_interval, self.send_clck_ind) + + def start(self): + # Schedule the first indication + self.timer.start() + + def stop(self): + self.timer.cancel() + + def send_clck_ind(self): + # Keep clock cycle + if self.clck_src % self.GSM_SUPERFRAME >= 0: + self.clck_src %= self.GSM_SUPERFRAME + + # We don't need to send so often + if self.clck_src % self.ind_period == 0: + # Create UDP payload + payload = "IND CLOCK %u\0" % self.clck_src + + # Send indication to all UDP links + for link in self.clck_links: + link.send(payload) + + # Debug print + print("[T] %s" % payload) + + # Increase frame count + self.clck_src += self.ind_period + + # Schedule a new indication + self.timer = Timer(self.ctr_interval, self.send_clck_ind) + self.timer.start() + +# Just a wrapper for independent usage +class Application: + def __init__(self): + # Set up signal handlers + signal.signal(signal.SIGINT, self.sig_handler) + + def run(self): + self.link = UDPLink("127.0.0.1", 5800, 5700) + self.clck = CLCKGen([self.link], ind_period = 51) + self.clck.start() + + def sig_handler(self, signum, frame): + print "Signal %d received" % signum + if signum is signal.SIGINT: + self.clck.stop() + self.link.shutdown() + +if __name__ == '__main__': + app = Application() + app.run() diff --git a/src/target/fake_trx/ctrl_cmd.py b/src/target/fake_trx/ctrl_cmd.py new file mode 100755 index 000000000..91d2a6552 --- /dev/null +++ b/src/target/fake_trx/ctrl_cmd.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# Simple tool to send custom commands via TRX CTRL interface, +# which may be also useful for fuzzing +# +# (C) 2017 by Vadim Yanitskiy +# +# 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. + +import signal +import select +import sys + +from udp_link import UDPLink + +class Application: + def __init__(self, remote_addr, remote_port, bind_port, fuz = False): + # Init UDP connection + self.ctrl_link = UDPLink(remote_addr, remote_port, bind_port) + + # Determine working mode + self.fuzzing = fuz + + # Set up signal handlers + signal.signal(signal.SIGINT, self.sig_handler) + + def run(self): + while True: + self.print_prompt() + + # Wait until we get any data on any socket + socks = [sys.stdin] + r_event, w_event, x_event = select.select(socks, [], []) + + # Check for incoming CTRL commands + if sys.stdin in r_event: + cmd = sys.stdin.readline() + self.handle_cmd(cmd) + + def handle_cmd(self, cmd): + # Strip spaces, tabs, etc. + cmd = cmd.strip().strip("\0") + + # Send a command + if self.fuzzing: + self.ctrl_link.send("%s" % cmd) + else: + self.ctrl_link.send("CMD %s\0" % cmd) + + def print_prompt(self): + sys.stdout.write("CTRL# ") + sys.stdout.flush() + + def sig_handler(self, signum, frame): + print("\n\nSignal %d received" % signum) + if signum is signal.SIGINT: + self.ctrl_link.shutdown() + sys.exit(0) + +if __name__ == '__main__': + app = Application("127.0.0.1", 5701, 5801) + app.run() diff --git a/src/target/fake_trx/ctrl_if.py b/src/target/fake_trx/ctrl_if.py new file mode 100644 index 000000000..a87c4c491 --- /dev/null +++ b/src/target/fake_trx/ctrl_if.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# Virtual Um-interface (fake transceiver) +# CTRL interface implementation +# +# (C) 2016-2017 by Vadim Yanitskiy +# +# 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. + +from udp_link import UDPLink + +class CTRLInterface(UDPLink): + def handle_rx(self, data): + # print(data) + if self.verify_req(data): + request = self.prepare_req(data) + rc = self.parse_cmd(request) + self.send_response(request, rc) + else: + print("[!] Wrong data on CTRL interface") + + def verify_req(self, data): + # Verify command signature + return data.startswith("CMD") + + def prepare_req(self, data): + # Strip signature, paddings and \0 + request = data[4:].strip().strip("\0") + # Split into a command and arguments + request = request.split(" ") + # Now we have something like ["TXTUNE", "941600"] + return request + + def verify_cmd(self, request, cmd, argc): + # Check if requested command matches + if request[0] != cmd: + return False + + # And has enough arguments + if len(request) - 1 != argc: + return False + + # Check if all arguments are numeric + for v in request[1:]: + if not v.isdigit(): + return False + + return True + + def send_response(self, request, response_code): + # Include status code, for example ["TXTUNE", "0", "941600"] + request.insert(1, str(response_code)) + # Add the response signature, and join back to string + response = "RSP " + " ".join(request) + "\0" + # Now we have something like "RSP TXTUNE 0 941600" + self.send(response) + + def parse_cmd(self, request): + raise NotImplementedError diff --git a/src/target/fake_trx/ctrl_if_bb.py b/src/target/fake_trx/ctrl_if_bb.py new file mode 100644 index 000000000..6880000ff --- /dev/null +++ b/src/target/fake_trx/ctrl_if_bb.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# Virtual Um-interface (fake transceiver) +# CTRL interface implementation (OsmocomBB specific) +# +# (C) 2016-2017 by Vadim Yanitskiy +# +# 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. + +from ctrl_if import CTRLInterface + +class CTRLInterfaceBB(CTRLInterface): + # Internal state variables + trx_started = False + rx_freq = None + tx_freq = None + + def __init__(self, remote_addr, remote_port, bind_port): + print("[i] Init CTRL interface for BB") + CTRLInterface.__init__(self, remote_addr, remote_port, bind_port) + + def shutdown(self): + print("[i] Shutdown CTRL interface for BB") + CTRLInterface.shutdown(self) + + def parse_cmd(self, request): + # Power control + if self.verify_cmd(request, "POWERON", 0): + print("[i] Recv POWERON CMD") + + # Ensure transceiver isn't working + if self.trx_started: + print("[!] Transceiver already started") + return -1 + + # Ensure RX / TX freq. are set + if (self.rx_freq is None) or (self.tx_freq is None): + print("[!] Transceiver already started") + return -1 + + print("[i] Starting transceiver...") + self.trx_started = True + return 0 + + elif self.verify_cmd(request, "POWEROFF", 0): + print("[i] Recv POWEROFF cmd") + + print("[i] Stopping transceiver...") + self.trx_started = False + return 0 + + # Tuning Control + elif self.verify_cmd(request, "RXTUNE", 1): + print("[i] Recv RXTUNE cmd") + + # TODO: check freq range + self.rx_freq = int(request[1]) * 1000 + return 0 + + elif self.verify_cmd(request, "TXTUNE", 1): + print("[i] Recv TXTUNE cmd") + + # TODO: check freq range + self.tx_freq = int(request[1]) * 1000 + return 0 + + # Wrong / unknown command + else: + # We don't care about other commands, + # so let's merely ignore them ;) + print("[i] Ignore CMD %s" % request[0]) + return 0 diff --git a/src/target/fake_trx/ctrl_if_bts.py b/src/target/fake_trx/ctrl_if_bts.py new file mode 100644 index 000000000..cdaaf81c9 --- /dev/null +++ b/src/target/fake_trx/ctrl_if_bts.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# Virtual Um-interface (fake transceiver) +# CTRL interface implementation (OsmoBTS specific) +# +# (C) 2016-2017 by Vadim Yanitskiy +# +# 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. + +from ctrl_if import CTRLInterface + +class CTRLInterfaceBTS(CTRLInterface): + # Internal state variables + trx_started = False + rx_freq = None + tx_freq = None + + def __init__(self, remote_addr, remote_port, bind_port): + print("[i] Init CTRL interface for BTS") + CTRLInterface.__init__(self, remote_addr, remote_port, bind_port) + + def shutdown(self): + print("[i] Shutdown CTRL interface for BTS") + CTRLInterface.shutdown(self) + + def parse_cmd(self, request): + # Power control + if self.verify_cmd(request, "POWERON", 0): + print("[i] Recv POWERON CMD") + + # Ensure transceiver isn't working + if self.trx_started: + print("[!] Transceiver already started") + return -1 + + # Ensure RX / TX freq. are set + if (self.rx_freq is None) or (self.tx_freq is None): + print("[!] Transceiver already started") + return -1 + + print("[i] Starting transceiver...") + self.trx_started = True + return 0 + + elif self.verify_cmd(request, "POWEROFF", 0): + print("[i] Recv POWEROFF cmd") + + print("[i] Stopping transceiver...") + self.trx_started = False + return 0 + + # Tuning Control + elif self.verify_cmd(request, "RXTUNE", 1): + print("[i] Recv RXTUNE cmd") + + # TODO: check freq range + self.rx_freq = int(request[1]) * 1000 + return 0 + + elif self.verify_cmd(request, "TXTUNE", 1): + print("[i] Recv TXTUNE cmd") + + # TODO: check freq range + self.tx_freq = int(request[1]) * 1000 + return 0 + + # Wrong / unknown command + else: + # We don't care about other commands, + # so let's merely ignore them ;) + print("[i] Ignore CMD %s" % request[0]) + return 0 diff --git a/src/target/fake_trx/fake_trx.py b/src/target/fake_trx/fake_trx.py new file mode 100755 index 000000000..c30382b44 --- /dev/null +++ b/src/target/fake_trx/fake_trx.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# Virtual Um-interface (fake transceiver) +# OsmocomBB <-> OsmoBTS bridge +# +# (C) 2017 by Vadim Yanitskiy +# +# 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. + +import signal +import getopt +import select +import sys + +from ctrl_if_bts import CTRLInterfaceBTS +from ctrl_if_bb import CTRLInterfaceBB +from burst_fwd import BurstForwarder + +from udp_link import UDPLink +from clck_gen import CLCKGen + +COPYRIGHT = \ + "Copyright (C) 2017 by Vadim Yanitskiy \n" \ + "License GPLv2+: GNU GPL version 2 or later " \ + "\n" \ + "This is free software: you are free to change and redistribute it.\n" \ + "There is NO WARRANTY, to the extent permitted by law.\n" + +class Application: + # Application variables + bts_addr = "127.0.0.1" + bb_addr = "127.0.0.1" + bts_base_port = 5700 + bb_base_port = 5703 + + def __init__(self): + self.print_copyright() + self.parse_argv() + + # Set up signal handlers + signal.signal(signal.SIGINT, self.sig_handler) + + def run(self): + # Init TRX CTRL interface for BTS + self.bts_ctrl = CTRLInterfaceBTS(self.bts_addr, + self.bts_base_port + 101, self.bts_base_port + 1) + + # Init TRX CTRL interface for BB + self.bb_ctrl = CTRLInterfaceBB(self.bb_addr, + self.bb_base_port + 101, self.bb_base_port + 1) + + # BTS <-> BB burst forwarding + self.bts_data = UDPLink(self.bts_addr, + self.bts_base_port + 102, self.bts_base_port + 2) + self.bb_data = UDPLink(self.bb_addr, + self.bb_base_port + 102, self.bb_base_port + 2) + self.burst_fwd = BurstForwarder(self.bts_data, self.bb_data) + + # Share clock between BTS and BB + self.bts_clck = UDPLink(self.bts_addr, + self.bts_base_port + 100, self.bts_base_port) + self.bb_clck = UDPLink(self.bb_addr, + self.bb_base_port + 100, self.bb_base_port) + self.clck_gen = CLCKGen([self.bts_clck, self.bb_clck]) + self.clck_gen.start() + + print("[i] Init complete") + + # Enter main loop + while True: + socks = [self.bts_ctrl.sock, self.bb_ctrl.sock, + self.bts_data.sock, self.bb_data.sock] + + # Wait until we get any data on any socket + r_event, w_event, x_event = select.select(socks, [], []) + + # Downlink: BTS -> BB + if self.bts_data.sock in r_event: + self.burst_fwd.bts2bb() + + # Uplink: BB -> BTS + if self.bb_data.sock in r_event: + self.burst_fwd.bb2bts() + + # CTRL commands from BTS + if self.bts_ctrl.sock in r_event: + data, addr = self.bts_ctrl.sock.recvfrom(128) + self.bts_ctrl.handle_rx(data) + + # CTRL commands from BB + if self.bb_ctrl.sock in r_event: + data, addr = self.bb_ctrl.sock.recvfrom(128) + self.bb_ctrl.handle_rx(data) + + def shutdown(self): + print("[i] Shutting down...") + + # Stop clock generator + self.clck_gen.stop() + + # Close CLCK interfaces + self.bts_clck.shutdown() + self.bb_clck.shutdown() + + # Close CTRL interfaces + self.bts_ctrl.shutdown() + self.bb_ctrl.shutdown() + + # Close DATA interfaces + self.bts_data.shutdown() + self.bb_data.shutdown() + + + def print_copyright(self): + print(COPYRIGHT) + + def print_help(self, msg = None): + s = " Usage: " + sys.argv[0] + " [options]\n\n" \ + " Some help...\n" \ + " -h --help this text\n\n" + + s += " TRX interface specific\n" \ + " -R --bts-addr Set BTS remote address (default %s)\n" \ + " -r --bb-addr Set BB remote address (default %s)\n" \ + " -P --bts-base-port Set BTS base port number (default %d)\n" \ + " -p --bb-base-port Set BB base port number (default %d)\n" + + print(s % (self.bts_addr, self.bb_addr, + self.bts_base_port, self.bb_base_port)) + + if msg is not None: + print(msg) + + def parse_argv(self): + try: + opts, args = getopt.getopt(sys.argv[1:], + "R:r:P:p:h", + ["help", "bts-addr=", "bb-addr=", + "bts-base-port=", "bb-base-port="]) + except getopt.GetoptError as err: + self.print_help("[!] " + str(err)) + sys.exit(2) + + for o, v in opts: + if o in ("-h", "--help"): + self.print_help() + sys.exit(2) + + elif o in ("-R", "--bts-addr"): + self.bts_addr = v + elif o in ("-r", "--bb-addr"): + self.bb_addr = v + + elif o in ("-P", "--bts-base-port"): + self.bts_base_port = int(v) + elif o in ("-p", "--bb-base-port"): + self.bb_base_port = int(v) + + # Ensure there is no overlap between ports + if self.bts_base_port == self.bb_base_port: + self.print_help("[!] BTS and BB base ports should be different") + sys.exit(2) + + bts_ports = [ + self.bts_base_port + 0, self.bts_base_port + 100, + self.bts_base_port + 1, self.bts_base_port + 101, + self.bts_base_port + 2, self.bts_base_port + 102, + ] + + bb_ports = [ + self.bb_base_port + 0, self.bb_base_port + 100, + self.bb_base_port + 1, self.bb_base_port + 101, + self.bb_base_port + 2, self.bb_base_port + 102, + ] + + for p in bb_ports: + if p in bts_ports: + self.print_help("[!] BTS and BB ports overlap detected") + sys.exit(2) + + def sig_handler(self, signum, frame): + print("Signal %d received" % signum) + if signum is signal.SIGINT: + self.shutdown() + sys.exit(0) + +if __name__ == '__main__': + app = Application() + app.run() diff --git a/src/target/fake_trx/udp_link.py b/src/target/fake_trx/udp_link.py new file mode 100644 index 000000000..3fa50502e --- /dev/null +++ b/src/target/fake_trx/udp_link.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# Virtual Um-interface (fake transceiver) +# UDP link implementation +# +# (C) 2017 by Vadim Yanitskiy +# +# 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. + +import socket +import select + +class UDPLink: + def __init__(self, remote_addr, remote_port, bind_port): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.sock.bind(('0.0.0.0', bind_port)) + self.sock.setblocking(0) + + # Save remote info + self.remote_addr = remote_addr + self.remote_port = remote_port + + def loop(self): + r_event, w_event, x_event = select.select([self.sock], [], []) + + # Check for incoming data + if self.sock in r_event: + data, addr = self.sock.recvfrom(128) + self.handle_rx(data) + + def shutdown(self): + self.sock.close(); + + def send(self, data): + self.sock.sendto(data, (self.remote_addr, self.remote_port)) + + def handle_rx(self, data): + raise NotImplementedError From a692cacdfc3edb96db7c5d9a9a19c0d99cc4f767 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 12 Jul 2017 17:24:16 +0700 Subject: [PATCH 028/211] fake_trx/burst_fwd.py: append two unused bytes at the end Change-Id: I1d7ed076d93b37699520ee2a31c42c177f966865 --- src/target/fake_trx/burst_fwd.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/target/fake_trx/burst_fwd.py b/src/target/fake_trx/burst_fwd.py index d82274718..161710c0a 100644 --- a/src/target/fake_trx/burst_fwd.py +++ b/src/target/fake_trx/burst_fwd.py @@ -50,6 +50,9 @@ class BurstForwarder: for i in range(8, length): payload[i] = 255 if payload[i] else 0 + # WTF: append two unused bytes at the end + payload[length:2] = [0x00, 0x00] + return payload # Downlink handler: BTS -> BB From 0f227d802b1ed22c8863a794675222ceaac2fd8c Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 12 Jul 2017 17:40:22 +0700 Subject: [PATCH 029/211] host/trxcon/trx_if.c: fix compatibility with OsmoTRX For some reasons, OsmoTRX sends 158-byte long sequences on DATA interface, where the latest two bytes aren't used. Change-Id: Ie9295e9b0d8956d9e87e2ced8cca9d5e68040f88 --- src/host/trxcon/trx_if.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index eb868ce92..1ee9440ed 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -516,6 +516,7 @@ rsp_error: /* 1 byte RSSI in -dBm */ /* 2 bytes correlator timing offset in 1/256 symbol steps, 2's-comp, BE */ /* 148 bytes soft symbol estimates, 0 -> definite "0", 255 -> definite "1" */ +/* 2 bytes are not used, but being sent by OsmoTRX */ /* */ /* Transmit Data Burst: */ /* 1 byte timeslot index */ @@ -538,7 +539,7 @@ static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what) if (len <= 0) return len; - if (len != 156) { + if (len != 158) { LOGP(DTRX, LOGL_ERROR, "Got data message with invalid length " "'%d'\n", len); return -EINVAL; From f437a3bebdf5aca4c636162d9156deb72f49fda8 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 12 Jul 2017 17:48:01 +0700 Subject: [PATCH 030/211] host/trxcon/trx_if.c: use proper names for burst handlers Change-Id: I3d36e6d80fcf6353379aa308415c306e1a256a7d --- src/host/trxcon/trx_if.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index 1ee9440ed..c387bc195 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -525,7 +525,7 @@ rsp_error: /* 148 bytes output symbol values, 0 & 1 */ /* ------------------------------------------------------------------------ */ -static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what) +static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what) { struct trx_instance *trx = ofd->data; uint8_t buf[256]; @@ -577,7 +577,7 @@ static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what) return 0; } -int trx_if_data(struct trx_instance *trx, uint8_t tn, uint32_t fn, +int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn, uint8_t pwr, const ubit_t *bits) { uint8_t buf[256]; @@ -646,7 +646,7 @@ int trx_if_open(struct trx_instance **trx, const char *host, uint16_t port) goto error; rc = trx_udp_open(trx_new, &trx_new->trx_ofd_data, host, - port + 102, port + 2, trx_data_read_cb); + port + 102, port + 2, trx_data_rx_cb); if (rc < 0) goto error; From 417183b78e58ac21c01fc2fc6034145f77632a28 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 12 Jul 2017 17:49:43 +0700 Subject: [PATCH 031/211] host/trxcon/trx_if.c: expose the trx_if_tx_burst() Change-Id: Iebd644879e2e9067a94cb638b4ec5b75f806923f --- src/host/trxcon/trx_if.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h index 5e9aebae5..c78fdc4e3 100644 --- a/src/host/trxcon/trx_if.h +++ b/src/host/trxcon/trx_if.h @@ -63,3 +63,6 @@ int trx_if_cmd_rxtune(struct trx_instance *trx, uint16_t arfcn); int trx_if_cmd_txtune(struct trx_instance *trx, uint16_t arfcn); int trx_if_cmd_setslot(struct trx_instance *trx, uint8_t tn, uint8_t type); + +int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn, + uint8_t pwr, const ubit_t *bits); From 85b700421f424c8de2d6b675124e92e3df2631e9 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 12 Jul 2017 17:58:31 +0700 Subject: [PATCH 032/211] host/trxcon/scheduler: modify trx_lchan_tx_func definition Initially, it was assumed that TX lchan handler will only compose a burst and return a pointer to the buffer. A burst itself could be sent somewhere outside, e.g. by caller. It would be better to send bursts exactly from handler, because in this case it isn't required to have an external buffer. Change-Id: Ic9dcdd366e68cec38c5840ed8f8cdda8236d67c7 --- src/host/trxcon/sched_trx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 563ee198f..41662e004 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -83,7 +83,7 @@ typedef int trx_lchan_rx_func(struct trx_instance *trx, uint8_t bid, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); -typedef ubit_t *trx_lchan_tx_func(struct trx_instance *trx, +typedef int trx_lchan_tx_func(struct trx_instance *trx, struct trx_ts *ts, uint32_t fn, enum trx_lchan_type chan, uint8_t bid, uint16_t *nbits); From 59c98b14aca9bea8b805678cdd7ee7638c66695e Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 12 Jul 2017 17:54:09 +0700 Subject: [PATCH 033/211] host/trxcon/scheduler: store BSIC in trx_instance We need to know BSIC value, before sending RACH requests. So, let's store it in trx_instance and update as soon as the first SCH burst is received after L1CTL_FBSB_REQ. Change-Id: I49574c3661f79f3b4941db6c651baebab2665c1b --- src/host/trxcon/sched_lchan_handlers.c | 4 +++- src/host/trxcon/trx_if.h | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/host/trxcon/sched_lchan_handlers.c b/src/host/trxcon/sched_lchan_handlers.c index 4d5351657..e2d8e14d4 100644 --- a/src/host/trxcon/sched_lchan_handlers.c +++ b/src/host/trxcon/sched_lchan_handlers.c @@ -224,8 +224,10 @@ int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, } /* Send L1CTL_FBSB_CONF to higher layers */ - if (!trx->l1l->fbsb_conf_sent) + if (!trx->l1l->fbsb_conf_sent) { l1ctl_tx_fbsb_conf(trx->l1l, 0, bsic); + trx->bsic = bsic; + } return 0; } diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h index c78fdc4e3..df201d80f 100644 --- a/src/host/trxcon/trx_if.h +++ b/src/host/trxcon/trx_if.h @@ -27,7 +27,10 @@ struct trx_instance { struct llist_head trx_ctrl_list; struct osmo_fsm_inst *fsm; uint32_t prev_state; + + /* GSM L1 specific */ uint16_t band_arfcn; + uint8_t bsic; /* Scheduler stuff */ struct trx_sched sched; From 2abc7a499845e6b684ea15fd5f0f2dfec7c5aad7 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 12 Jul 2017 18:48:18 +0700 Subject: [PATCH 034/211] host/trxcon/scheduler: implement TX queue handling Change-Id: I2b7bae53901156524134c4904ea1179268d85601 --- src/host/trxcon/sched_trx.c | 34 +++++++++++++++++++++++++++++++++- src/host/trxcon/sched_trx.h | 10 ++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 32e6a5908..3c5e265e2 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -48,12 +48,44 @@ static void msgb_queue_flush(struct llist_head *list) static void sched_frame_clck_cb(struct trx_sched *sched) { struct trx_instance *trx = (struct trx_instance *) sched->data; + const struct trx_frame *frame; + trx_lchan_tx_func *handler; + struct trx_ts_prim *prim; + enum trx_lchan_type chan; + uint8_t offset, bid; + struct trx_ts *ts; + uint32_t fn; /* If we have no active timeslots, nothing to do */ if (llist_empty(&trx->ts_list)) return; - /* Do nothing for now */ + /* For each allocated timeslot */ + llist_for_each_entry(ts, &trx->ts_list, list) { + if (llist_empty(&ts->tx_prims)) + continue; + + /* Get frame from multiframe */ + fn = sched->fn_counter_proc; + offset = fn % ts->mf_layout->period; + frame = ts->mf_layout->frames + offset; + + /* Get required info from frame */ + bid = frame->ul_bid; + chan = frame->ul_chan; + handler = trx_lchan_desc[chan].tx_fn; + + /* Omit lchans without handler */ + if (!handler) + continue; + + /* Get a message from TX queue */ + prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); + + /* Poke lchan handler */ + if (prim->chan == chan) + handler(trx, ts, fn, chan, bid, NULL); + } } int sched_trx_init(struct trx_instance *trx) diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 41662e004..809a3243b 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -230,6 +230,16 @@ struct trx_ts { struct llist_head list; }; +/* Represents one TX primitive in the queue of trx_ts */ +struct trx_ts_prim { + /*! \brief Link to queue of TS */ + struct llist_head list; + /*! \brief Logical channel type */ + enum trx_lchan_type chan; + /*! \brief Payload */ + uint8_t payload[0]; +}; + extern const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX]; const struct trx_multiframe *sched_mframe_layout( enum gsm_phys_chan_config config, int ts_num); From 46b47f5a5fdba1aeae589fd1163f860ef83d8f11 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 14 Jul 2017 01:38:48 +0700 Subject: [PATCH 035/211] host/trxcon/scheduler: implement RACH handler Change-Id: I496dd682549570e37e63e7edcfc83a064c13a57f --- src/host/trxcon/sched_lchan_desc.c | 5 ++- src/host/trxcon/sched_lchan_handlers.c | 56 ++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/host/trxcon/sched_lchan_desc.c b/src/host/trxcon/sched_lchan_desc.c index f82a982d1..55edb9893 100644 --- a/src/host/trxcon/sched_lchan_desc.c +++ b/src/host/trxcon/sched_lchan_desc.c @@ -33,7 +33,6 @@ #define tx_pdtch_fn NULL #define tx_tchf_fn NULL #define tx_tchh_fn NULL -#define tx_rach_fn NULL #define rx_pdtch_fn NULL #define rx_tchf_fn NULL @@ -48,6 +47,10 @@ int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, uint32_t fn, enum trx_lchan_type chan, uint8_t bid, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); +int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, + uint32_t fn, enum trx_lchan_type chan, + uint8_t bid, uint16_t *nbits); + const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { { TRXC_IDLE, "IDLE", diff --git a/src/host/trxcon/sched_lchan_handlers.c b/src/host/trxcon/sched_lchan_handlers.c index e2d8e14d4..eeb09af3b 100644 --- a/src/host/trxcon/sched_lchan_handlers.c +++ b/src/host/trxcon/sched_lchan_handlers.c @@ -29,6 +29,7 @@ #include +#include #include #include #include @@ -231,3 +232,58 @@ int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, return 0; } + +/* 41-bit RACH synchronization sequence */ +static ubit_t rach_synch_seq[] = { + 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, + 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, +}; + +/* Obtain a to-be-transmitted RACH burst */ +int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, + uint32_t fn, enum trx_lchan_type chan, + uint8_t bid, uint16_t *nbits) +{ + struct trx_ts_prim *prim; + struct l1ctl_rach_req *req; + uint8_t burst[GSM_BURST_LEN]; + uint8_t payload[36]; + int rc; + + /* Get a message from TX queue */ + prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); + req = (struct l1ctl_rach_req *) prim->payload; + + /* Delay RACH sending according to offset value */ + if (req->offset-- > 0) + return 0; + + /* Encode payload */ + rc = gsm0503_rach_encode(payload, &req->ra, trx->bsic); + if (rc) { + LOGP(DSCH, LOGL_ERROR, "Could not encode RACH burst\n"); + return rc; + } + + /* Compose RACH burst */ + memset(burst, 0, 8); /* TB */ + memcpy(burst + 8, rach_synch_seq, 41); /* sync seq */ + memcpy(burst + 49, payload, 36); /* payload */ + memset(burst + 85, 0, 63); /* TB + GP */ + + LOGP(DSCH, LOGL_DEBUG, "Transmitting RACH fn=%u\n", fn); + + /* Send burst to transceiver */ + rc = trx_if_tx_burst(trx, ts->index, fn, 10, burst); + if (rc) { + LOGP(DSCH, LOGL_ERROR, "Could not send burst to transceiver\n"); + return rc; + } + + /* Remove primitive from queue and free memory */ + llist_del(&prim->list); + talloc_free(prim); + + return 0; +} From b91cdc6a4a7dee53b8bc6ded599d67803e5d139e Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 12 Jul 2017 18:46:56 +0700 Subject: [PATCH 036/211] host/trxcon/l1ctl.c: handle L1CTL_RACH_REQ Change-Id: I4c06bcf987120c6271b6e2ad94454fa69689a03e --- src/host/trxcon/l1ctl.c | 64 +++++++++++++++++++++++++++++++++++++++++ src/host/trxcon/l1ctl.h | 1 + 2 files changed, 65 insertions(+) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index beb5734ac..da69e86fd 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -187,6 +187,17 @@ int l1ctl_tx_data_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data) return l1ctl_link_send(l1l, msg); } +int l1ctl_tx_rach_conf(struct l1ctl_link *l1l) +{ + struct msgb *msg; + + msg = l1ctl_alloc_msg(L1CTL_RACH_CONF); + if (msg == NULL) + return -ENOMEM; + + return l1ctl_link_send(l1l, msg); +} + static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg) { struct l1ctl_fbsb_req *fbsb; @@ -355,6 +366,57 @@ exit: return rc; } +static int l1ctl_rx_rach_req(struct l1ctl_link *l1l, struct msgb *msg) +{ + struct l1ctl_rach_req *req; + struct trx_ts_prim *prim; + struct trx_ts *ts; + int len, rc = 0; + + req = (struct l1ctl_rach_req *) msg->l1h; + len = sizeof(*req); + if (msgb_l1len(msg) < len) { + LOGP(DL1C, LOGL_ERROR, "MSG too short RACH Req: %d\n", len); + rc = -EINVAL; + goto exit; + } + + /* Convert offset value to host format */ + req->offset = ntohs(req->offset); + + LOGP(DL1C, LOGL_DEBUG, "Recv RACH Req (offset=%u)\n", req->offset); + + /* FIXME: can we use other than TS0? */ + ts = sched_trx_find_ts(l1l->trx, 0); + if (ts == NULL) { + LOGP(DL1C, LOGL_DEBUG, "Couldn't send RACH: " + "TS0 is not active\n"); + rc = -EINVAL; + goto exit; + } + + /* Allocate a new primitive */ + prim = talloc_zero_size(ts, sizeof(struct trx_ts_prim) + len); + if (prim == NULL) { + LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory\n"); + rc = -ENOMEM; + goto exit; + } + + /* Set logical channel of primitive */ + prim->chan = TRXC_RACH; + + /* Fill in the payload */ + memcpy(prim->payload, req, len); + + /* Add to TS queue */ + llist_add_tail(&prim->list, &ts->tx_prims); + +exit: + msgb_free(msg); + return rc; +} + int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) { struct l1ctl_hdr *l1h; @@ -373,6 +435,8 @@ int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) return l1ctl_rx_echo_req(l1l, msg); case L1CTL_CCCH_MODE_REQ: return l1ctl_rx_ccch_mode_req(l1l, msg); + case L1CTL_RACH_REQ: + return l1ctl_rx_rach_req(l1l, msg); default: LOGP(DL1C, LOGL_ERROR, "Unknown MSG: %u\n", l1h->msg_type); msgb_free(msg); diff --git a/src/host/trxcon/l1ctl.h b/src/host/trxcon/l1ctl.h index dae75e92c..165865a83 100644 --- a/src/host/trxcon/l1ctl.h +++ b/src/host/trxcon/l1ctl.h @@ -15,3 +15,4 @@ int l1ctl_tx_reset_ind(struct l1ctl_link *l1l, uint8_t type); int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg); int l1ctl_tx_data_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *ind); +int l1ctl_tx_rach_conf(struct l1ctl_link *l1l); From d28f65917f0cc9106376020b107086f801fa2613 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 14 Jul 2017 01:47:40 +0700 Subject: [PATCH 037/211] host/trxcon/scheduler: confirm successful RACH requests Change-Id: I079ecebbeeb3843288118fbb55fa520af22859bb --- src/host/trxcon/sched_lchan_handlers.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/host/trxcon/sched_lchan_handlers.c b/src/host/trxcon/sched_lchan_handlers.c index eeb09af3b..49a28ef22 100644 --- a/src/host/trxcon/sched_lchan_handlers.c +++ b/src/host/trxcon/sched_lchan_handlers.c @@ -281,6 +281,9 @@ int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, return rc; } + /* Confirm RACH request */ + l1ctl_tx_rach_conf(trx->l1l); + /* Remove primitive from queue and free memory */ llist_del(&prim->list); talloc_free(prim); From ca5eee6e3f50704e69507f41523ab885be7a58fb Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 14 Jul 2017 03:19:13 +0700 Subject: [PATCH 038/211] host/trxcon/trx_if.c: simplify response matching Previously, we had both length and string matching of request and response. To be able to implement commands with additional params in the future, this change drops the length matching part. Change-Id: Id4c50115f5f1b1da450ff8b8dcfd6ccf572d23f5 --- src/host/trxcon/trx_if.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index c387bc195..c66bb4cb9 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -458,7 +458,7 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) struct trx_ctrl_msg, list); /* Check if response matches command */ - if (rsp_len != tcm->cmd_len || !!strncmp(buf + 4, tcm->cmd + 4, rsp_len)) { + if (!!strncmp(buf + 4, tcm->cmd + 4, rsp_len)) { LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR, "Response message '%s' does not match command " "message '%s'\n", buf, tcm->cmd); From 794deea3ece4df9dc5f9da2734455fbab0b4d3ae Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 14 Jul 2017 04:10:41 +0700 Subject: [PATCH 039/211] host/trxcon/trx_if.c: add power measurement command Change-Id: Ib947b60248cafad4edeb7e49c2bd3a1f81696239 --- src/host/trxcon/trx_if.c | 63 ++++++++++++++++++++++++++++++++++++++++ src/host/trxcon/trx_if.h | 5 ++++ 2 files changed, 68 insertions(+) diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index c66bb4cb9..41c378ff3 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -39,6 +39,7 @@ #include +#include "l1ctl.h" #include "trxcon.h" #include "trx_if.h" #include "logging.h" @@ -420,6 +421,66 @@ int trx_if_cmd_txtune(struct trx_instance *trx, uint16_t arfcn) return trx_ctrl_cmd(trx, 1, "TXTUNE", "%d", freq10 * 100); } +/* + * Power measurement + * + * MEASURE instructs the transceiver to perform a power + * measurement on specified frequency. After receiving this + * request, transceiver should quickly re-tune to requested + * frequency, measure power level and re-tune back to the + * previous frequency. + * CMD MEASURE + * RSP MEASURE + */ + +int trx_if_cmd_measure(struct trx_instance *trx, + uint16_t arfcn_start, uint16_t arfcn_stop) +{ + uint16_t freq10; + + /* Update ARFCN range for measurement */ + trx->pm_arfcn_start = arfcn_start; + trx->pm_arfcn_stop = arfcn_stop; + + /* Calculate a frequency for current ARFCN (DL) */ + freq10 = gsm_arfcn2freq10(arfcn_start, 0); + if (freq10 == 0xffff) { + LOGP(DTRX, LOGL_ERROR, "ARFCN %d not defined\n", arfcn_start); + return -ENOTSUP; + } + + return trx_ctrl_cmd(trx, 1, "MEASURE", "%d", freq10 * 100); +} + +static void trx_if_measure_rsp_cb(struct trx_instance *trx, char *resp) +{ + unsigned int freq10; + uint16_t arfcn; + int dbm; + + /* Parse freq. and power level */ + sscanf(resp, "%u %d", &freq10, &dbm); + freq10 /= 100; + + /* Check received ARFCN against expected */ + arfcn = gsm_freq102arfcn((uint16_t) freq10, 0); + if (arfcn != trx->pm_arfcn_start) { + LOGP(DTRX, LOGL_ERROR, "Power measurement error: " + "response ARFCN=%u doesn't match expected ARFCN=%u\n", + arfcn &~ ARFCN_FLAG_MASK, + trx->pm_arfcn_start &~ ARFCN_FLAG_MASK); + return; + } + + /* Send L1CTL_PM_CONF */ + l1ctl_tx_pm_conf(trx->l1l, arfcn, dbm, + arfcn == trx->pm_arfcn_stop); + + /* Schedule a next measurement */ + if (arfcn != trx->pm_arfcn_stop) + trx_if_cmd_measure(trx, ++arfcn, trx->pm_arfcn_stop); +} + /* Get response from CTRL socket */ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) { @@ -481,6 +542,8 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_ACTIVE, 0, 0); else if (!strncmp(tcm->cmd + 4, "POWEROFF", 8)) osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_IDLE, 0, 0); + else if (!strncmp(tcm->cmd + 4, "MEASURE", 7)) + trx_if_measure_rsp_cb(trx, buf + 14); else if (!strncmp(tcm->cmd + 4, "ECHO", 4)) { osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_IDLE, 0, 0); osmo_fsm_inst_dispatch(trxcon_fsm, diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h index df201d80f..8ba09b8a7 100644 --- a/src/host/trxcon/trx_if.h +++ b/src/host/trxcon/trx_if.h @@ -29,6 +29,8 @@ struct trx_instance { uint32_t prev_state; /* GSM L1 specific */ + uint16_t pm_arfcn_start; + uint16_t pm_arfcn_stop; uint16_t band_arfcn; uint8_t bsic; @@ -67,5 +69,8 @@ int trx_if_cmd_txtune(struct trx_instance *trx, uint16_t arfcn); int trx_if_cmd_setslot(struct trx_instance *trx, uint8_t tn, uint8_t type); +int trx_if_cmd_measure(struct trx_instance *trx, + uint16_t arfcn_start, uint16_t arfcn_stop); + int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn, uint8_t pwr, const ubit_t *bits); From 798e2b148cca4540245658730c3a51ff7acd6bd9 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 14 Jul 2017 05:32:19 +0700 Subject: [PATCH 040/211] fake_trx: fix copy-paste error Change-Id: I9a6b294ad3501aa26d92667a995d08ca3be03da4 --- src/target/fake_trx/ctrl_if_bb.py | 2 +- src/target/fake_trx/ctrl_if_bts.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/fake_trx/ctrl_if_bb.py b/src/target/fake_trx/ctrl_if_bb.py index 6880000ff..32b82d30d 100644 --- a/src/target/fake_trx/ctrl_if_bb.py +++ b/src/target/fake_trx/ctrl_if_bb.py @@ -50,7 +50,7 @@ class CTRLInterfaceBB(CTRLInterface): # Ensure RX / TX freq. are set if (self.rx_freq is None) or (self.tx_freq is None): - print("[!] Transceiver already started") + print("[!] RX / TX freq. are not set") return -1 print("[i] Starting transceiver...") diff --git a/src/target/fake_trx/ctrl_if_bts.py b/src/target/fake_trx/ctrl_if_bts.py index cdaaf81c9..d184f2efe 100644 --- a/src/target/fake_trx/ctrl_if_bts.py +++ b/src/target/fake_trx/ctrl_if_bts.py @@ -50,7 +50,7 @@ class CTRLInterfaceBTS(CTRLInterface): # Ensure RX / TX freq. are set if (self.rx_freq is None) or (self.tx_freq is None): - print("[!] Transceiver already started") + print("[!] RX / TX freq. are not set") return -1 print("[i] Starting transceiver...") From ef31f1bff220e025660554cc930055418b8cf98c Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 14 Jul 2017 06:25:37 +0700 Subject: [PATCH 041/211] fake_trx/ctrl_cmd.py: print response to stdout Change-Id: Icdbb1802b81eddf42786fafcf96200a60db3cc24 --- src/target/fake_trx/ctrl_cmd.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/target/fake_trx/ctrl_cmd.py b/src/target/fake_trx/ctrl_cmd.py index 91d2a6552..a9c7d68a3 100755 --- a/src/target/fake_trx/ctrl_cmd.py +++ b/src/target/fake_trx/ctrl_cmd.py @@ -44,7 +44,7 @@ class Application: self.print_prompt() # Wait until we get any data on any socket - socks = [sys.stdin] + socks = [sys.stdin, self.ctrl_link.sock] r_event, w_event, x_event = select.select(socks, [], []) # Check for incoming CTRL commands @@ -52,6 +52,11 @@ class Application: cmd = sys.stdin.readline() self.handle_cmd(cmd) + if self.ctrl_link.sock in r_event: + data, addr = self.ctrl_link.sock.recvfrom(128) + sys.stdout.write("\r%s\n" % data) + sys.stdout.flush() + def handle_cmd(self, cmd): # Strip spaces, tabs, etc. cmd = cmd.strip().strip("\0") From e39bb0f0d1a5615103ab631c0816ff27891fc0e4 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 14 Jul 2017 05:49:48 +0700 Subject: [PATCH 042/211] fake_trx/ctrl_if.py: allow adding custom params to response Change-Id: I551bb425c25a5c978801d9e1e033b4ba352e259f --- src/target/fake_trx/ctrl_if.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/target/fake_trx/ctrl_if.py b/src/target/fake_trx/ctrl_if.py index a87c4c491..e84c1c877 100644 --- a/src/target/fake_trx/ctrl_if.py +++ b/src/target/fake_trx/ctrl_if.py @@ -30,7 +30,11 @@ class CTRLInterface(UDPLink): if self.verify_req(data): request = self.prepare_req(data) rc = self.parse_cmd(request) - self.send_response(request, rc) + + if type(rc) is tuple: + self.send_response(request, rc[0], rc[1]) + else: + self.send_response(request, rc) else: print("[!] Wrong data on CTRL interface") @@ -62,9 +66,14 @@ class CTRLInterface(UDPLink): return True - def send_response(self, request, response_code): + def send_response(self, request, response_code, params = None): # Include status code, for example ["TXTUNE", "0", "941600"] request.insert(1, str(response_code)) + + # Optionally append command specific parameters + if params is not None: + request += params + # Add the response signature, and join back to string response = "RSP " + " ".join(request) + "\0" # Now we have something like "RSP TXTUNE 0 941600" From 02996abedaa3136163b804cc2335f3aa8cc6ba0a Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 14 Jul 2017 08:06:07 +0700 Subject: [PATCH 043/211] fake_trx: implement power measurement emulation This change introduces a new class named FakePM, which is intended to generate pseudo-random power levels for base stations and noise levels inactive frequencies. Also, there is a new command in BB CTRL, which instructs transceiver to perform a power measurement on requested frequency. As we work in virtual Um-interface, a FakePM instance is used to provide some fake power levels. Change-Id: If48c12fd0b1ba10e1cf76559b359e17a1256617d --- src/target/fake_trx/ctrl_if_bb.py | 14 ++++++++ src/target/fake_trx/ctrl_if_bts.py | 11 +++++++ src/target/fake_trx/fake_pm.py | 53 ++++++++++++++++++++++++++++++ src/target/fake_trx/fake_trx.py | 10 ++++++ 4 files changed, 88 insertions(+) create mode 100644 src/target/fake_trx/fake_pm.py diff --git a/src/target/fake_trx/ctrl_if_bb.py b/src/target/fake_trx/ctrl_if_bb.py index 32b82d30d..f840c09cc 100644 --- a/src/target/fake_trx/ctrl_if_bb.py +++ b/src/target/fake_trx/ctrl_if_bb.py @@ -29,6 +29,7 @@ class CTRLInterfaceBB(CTRLInterface): trx_started = False rx_freq = None tx_freq = None + pm = None def __init__(self, remote_addr, remote_port, bind_port): print("[i] Init CTRL interface for BB") @@ -79,6 +80,19 @@ class CTRLInterfaceBB(CTRLInterface): self.tx_freq = int(request[1]) * 1000 return 0 + # Power measurement + elif self.verify_cmd(request, "MEASURE", 1): + print("[i] Recv MEASURE cmd") + + if self.pm is None: + return -1 + + # TODO: check freq range + meas_freq = int(request[1]) * 1000 + meas_dbm = str(self.pm.measure(meas_freq)) + + return (0, [meas_dbm]) + # Wrong / unknown command else: # We don't care about other commands, diff --git a/src/target/fake_trx/ctrl_if_bts.py b/src/target/fake_trx/ctrl_if_bts.py index d184f2efe..a6b03cf67 100644 --- a/src/target/fake_trx/ctrl_if_bts.py +++ b/src/target/fake_trx/ctrl_if_bts.py @@ -29,6 +29,7 @@ class CTRLInterfaceBTS(CTRLInterface): trx_started = False rx_freq = None tx_freq = None + pm = None def __init__(self, remote_addr, remote_port, bind_port): print("[i] Init CTRL interface for BTS") @@ -55,6 +56,11 @@ class CTRLInterfaceBTS(CTRLInterface): print("[i] Starting transceiver...") self.trx_started = True + + # Power emulation + if self.pm is not None: + self.pm.add_bts_list([self.tx_freq]) + return 0 elif self.verify_cmd(request, "POWEROFF", 0): @@ -62,6 +68,11 @@ class CTRLInterfaceBTS(CTRLInterface): print("[i] Stopping transceiver...") self.trx_started = False + + # Power emulation + if self.pm is not None: + self.pm.del_bts_list([self.tx_freq]) + return 0 # Tuning Control diff --git a/src/target/fake_trx/fake_pm.py b/src/target/fake_trx/fake_pm.py new file mode 100644 index 000000000..1d7691650 --- /dev/null +++ b/src/target/fake_trx/fake_pm.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# Virtual Um-interface (fake transceiver) +# Power measurement emulation for BB +# +# (C) 2017 by Vadim Yanitskiy +# +# 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. + +from random import randint + +class FakePM: + # Freq. list for good power level + bts_list = [] + + def __init__(self, noise_min, noise_max, bts_min, bts_max): + # Save power level ranges + self.noise_min = noise_min + self.noise_max = noise_max + self.bts_min = bts_min + self.bts_max = bts_max + + def measure(self, bts): + if bts in self.bts_list: + return randint(self.bts_min, self.bts_max) + else: + return randint(self.noise_min, self.noise_max) + + def update_bts_list(self, new_list): + self.bts_list = new_list + + def add_bts_list(self, add_list): + self.bts_list += add_list + + def del_bts_list(self, del_list): + for item in del_list: + if item in self.bts_list: + self.bts_list.remove(item) diff --git a/src/target/fake_trx/fake_trx.py b/src/target/fake_trx/fake_trx.py index c30382b44..724a5207f 100755 --- a/src/target/fake_trx/fake_trx.py +++ b/src/target/fake_trx/fake_trx.py @@ -30,6 +30,7 @@ import sys from ctrl_if_bts import CTRLInterfaceBTS from ctrl_if_bb import CTRLInterfaceBB from burst_fwd import BurstForwarder +from fake_pm import FakePM from udp_link import UDPLink from clck_gen import CLCKGen @@ -64,6 +65,15 @@ class Application: self.bb_ctrl = CTRLInterfaceBB(self.bb_addr, self.bb_base_port + 101, self.bb_base_port + 1) + # Power measurement emulation + # Noise: -120 .. -105 + # BTS: -75 .. -50 + self.pm = FakePM(-120, -105, -75, -50) + + # Share a FakePM instance between both BTS and BB + self.bts_ctrl.pm = self.pm + self.bb_ctrl.pm = self.pm + # BTS <-> BB burst forwarding self.bts_data = UDPLink(self.bts_addr, self.bts_base_port + 102, self.bts_base_port + 2) From 924107d0d866a5bfe951054553a8e77a641a3d99 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 14 Jul 2017 08:26:24 +0700 Subject: [PATCH 044/211] host/trxcon/l1ctl.c: handle L1CTL_PM_REQ correctly Change-Id: Ib5a2198f21e747b6169ef141817ef22b241ef9fa --- src/host/trxcon/l1ctl.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index da69e86fd..d5c3b57b6 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -245,7 +245,7 @@ exit: static int l1ctl_rx_pm_req(struct l1ctl_link *l1l, struct msgb *msg) { - uint16_t arfcn_start, arfcn_stop, arfcn; + uint16_t arfcn_start, arfcn_stop; struct l1ctl_pm_req *pmr; int rc = 0; @@ -265,18 +265,8 @@ static int l1ctl_rx_pm_req(struct l1ctl_link *l1l, struct msgb *msg) arfcn_start &~ ARFCN_FLAG_MASK, arfcn_stop &~ ARFCN_FLAG_MASK); - /** - * HACK: power measurement isn't implemented yet, - * sending fake results for now... - * - * FIXME: l1ctl_link.c:203 Failed to enqueue msg! - * l1l->wq size is limited to 100, so we cannot - * put more messages until osmo_select_main() - * is called. - */ - for (arfcn = arfcn_start; arfcn <= arfcn_stop; arfcn++) - l1ctl_tx_pm_conf(l1l, arfcn, arfcn == 33 ? - -60 : -120, arfcn == arfcn_stop); + /* Send measurement request to transceiver */ + rc = trx_if_cmd_measure(l1l->trx, arfcn_start, arfcn_stop); exit: msgb_free(msg); From b4da181c399671514e1d04185cea3212bf24793b Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 14 Jul 2017 09:01:30 +0700 Subject: [PATCH 045/211] fake_trx: handle SETSLOT command from BB Change-Id: I152486377ecd6777dbae7024e5d12cfdbcb17628 --- src/target/fake_trx/burst_fwd.py | 4 ++-- src/target/fake_trx/ctrl_if_bb.py | 25 +++++++++++++++++++++++++ src/target/fake_trx/fake_trx.py | 1 + 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/target/fake_trx/burst_fwd.py b/src/target/fake_trx/burst_fwd.py index 161710c0a..1ac74c00c 100644 --- a/src/target/fake_trx/burst_fwd.py +++ b/src/target/fake_trx/burst_fwd.py @@ -23,8 +23,8 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. class BurstForwarder: - # Timeslot filter - ts_pass = 0 + # Timeslot filter (drop everything by default) + ts_pass = None def __init__(self, bts_link, bb_link): self.bts_link = bts_link diff --git a/src/target/fake_trx/ctrl_if_bb.py b/src/target/fake_trx/ctrl_if_bb.py index f840c09cc..3882b6afa 100644 --- a/src/target/fake_trx/ctrl_if_bb.py +++ b/src/target/fake_trx/ctrl_if_bb.py @@ -27,6 +27,7 @@ from ctrl_if import CTRLInterface class CTRLInterfaceBB(CTRLInterface): # Internal state variables trx_started = False + burst_fwd = None rx_freq = None tx_freq = None pm = None @@ -93,6 +94,30 @@ class CTRLInterfaceBB(CTRLInterface): return (0, [meas_dbm]) + elif self.verify_cmd(request, "SETSLOT", 2): + print("[i] Recv SETSLOT cmd") + + if self.burst_fwd is None: + return -1 + + # Obtain TS index + ts = int(request[1]) + if ts not in range(0, 8): + print("[!] TS index should be in range: 0..7") + return -1 + + # Parse TS type + ts_type = int(request[2]) + + # TS activation / deactivation + # We don't care about ts_type + if ts_type == 0: + self.burst_fwd.ts_pass = None + else: + self.burst_fwd.ts_pass = ts + + return 0 + # Wrong / unknown command else: # We don't care about other commands, diff --git a/src/target/fake_trx/fake_trx.py b/src/target/fake_trx/fake_trx.py index 724a5207f..533b26472 100755 --- a/src/target/fake_trx/fake_trx.py +++ b/src/target/fake_trx/fake_trx.py @@ -80,6 +80,7 @@ class Application: self.bb_data = UDPLink(self.bb_addr, self.bb_base_port + 102, self.bb_base_port + 2) self.burst_fwd = BurstForwarder(self.bts_data, self.bb_data) + self.bb_ctrl.burst_fwd = self.burst_fwd # Share clock between BTS and BB self.bts_clck = UDPLink(self.bts_addr, From 656e31f7620bce04e472cd2c3721d5f893347dfe Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 14 Jul 2017 09:18:03 +0700 Subject: [PATCH 046/211] host/trxcon/scheduler: notify transceiver about TS state Change-Id: I5783066e7c1b01e4ebb28d10b03f3290ff8a0f37 --- src/host/trxcon/sched_trx.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 3c5e265e2..88d9d97b7 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -202,6 +202,9 @@ void sched_trx_del_ts(struct trx_instance *trx, int ts_num) /* Remove ts from list */ llist_del(&ts->list); talloc_free(ts); + + /* Notify transceiver about that */ + trx_if_cmd_setslot(trx, ts_num, 0); } int sched_trx_configure_ts(struct trx_instance *trx, int ts_num, @@ -258,6 +261,10 @@ int sched_trx_configure_ts(struct trx_instance *trx, int ts_num, } } + /* Notify transceiver about TS activation */ + /* FIXME: set proper channel type */ + trx_if_cmd_setslot(trx, ts_num, 1); + return 0; } @@ -282,6 +289,9 @@ int sched_trx_reset_ts(struct trx_instance *trx, int ts_num) /* Free channel states */ talloc_free(ts->lchans); + /* Notify transceiver about that */ + trx_if_cmd_setslot(trx, ts_num, 0); + return 0; } From 87e5f67f66393016a840aeabcea3b5f0ef5d3370 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 14 Jul 2017 09:19:22 +0700 Subject: [PATCH 047/211] fake_trx: implement simple freq. filter Change-Id: Ifbeaebeaf68a88c07a05b672502f503ab7b890f2 --- src/target/fake_trx/burst_fwd.py | 20 ++++++++++++++++++++ src/target/fake_trx/ctrl_if_bb.py | 1 + src/target/fake_trx/ctrl_if_bts.py | 2 ++ src/target/fake_trx/fake_trx.py | 3 +++ 4 files changed, 26 insertions(+) diff --git a/src/target/fake_trx/burst_fwd.py b/src/target/fake_trx/burst_fwd.py index 1ac74c00c..5989ce9c9 100644 --- a/src/target/fake_trx/burst_fwd.py +++ b/src/target/fake_trx/burst_fwd.py @@ -26,6 +26,10 @@ class BurstForwarder: # Timeslot filter (drop everything by default) ts_pass = None + # Freq. filter + bts_freq = None + bb_freq = None + def __init__(self, bts_link, bb_link): self.bts_link = bts_link self.bb_link = bb_link @@ -61,6 +65,14 @@ class BurstForwarder: data, addr = self.bts_link.sock.recvfrom(512) payload = self.process_payload(data) + # BB is not connected / tuned + if self.bb_freq is None: + return None + + # Freq. filter + if self.bb_freq != self.bts_freq: + return None + # Timeslot filter if payload[0] != self.ts_pass: return None @@ -74,5 +86,13 @@ class BurstForwarder: data, addr = self.bb_link.sock.recvfrom(512) payload = self.process_payload(data) + # BTS is not connected / tuned + if self.bts_freq is None: + return None + + # Freq. filter + if self.bb_freq != self.bts_freq: + return None + # Send burst to BTS self.bts_link.send(payload) diff --git a/src/target/fake_trx/ctrl_if_bb.py b/src/target/fake_trx/ctrl_if_bb.py index 3882b6afa..440094a0e 100644 --- a/src/target/fake_trx/ctrl_if_bb.py +++ b/src/target/fake_trx/ctrl_if_bb.py @@ -72,6 +72,7 @@ class CTRLInterfaceBB(CTRLInterface): # TODO: check freq range self.rx_freq = int(request[1]) * 1000 + self.burst_fwd.bb_freq = self.rx_freq return 0 elif self.verify_cmd(request, "TXTUNE", 1): diff --git a/src/target/fake_trx/ctrl_if_bts.py b/src/target/fake_trx/ctrl_if_bts.py index a6b03cf67..96027fe91 100644 --- a/src/target/fake_trx/ctrl_if_bts.py +++ b/src/target/fake_trx/ctrl_if_bts.py @@ -27,6 +27,7 @@ from ctrl_if import CTRLInterface class CTRLInterfaceBTS(CTRLInterface): # Internal state variables trx_started = False + burst_fwd = None rx_freq = None tx_freq = None pm = None @@ -88,6 +89,7 @@ class CTRLInterfaceBTS(CTRLInterface): # TODO: check freq range self.tx_freq = int(request[1]) * 1000 + self.burst_fwd.bts_freq = self.tx_freq return 0 # Wrong / unknown command diff --git a/src/target/fake_trx/fake_trx.py b/src/target/fake_trx/fake_trx.py index 533b26472..aaa308d7e 100755 --- a/src/target/fake_trx/fake_trx.py +++ b/src/target/fake_trx/fake_trx.py @@ -80,6 +80,9 @@ class Application: self.bb_data = UDPLink(self.bb_addr, self.bb_base_port + 102, self.bb_base_port + 2) self.burst_fwd = BurstForwarder(self.bts_data, self.bb_data) + + # Share a BurstForwarder instance between BTS and BB + self.bts_ctrl.burst_fwd = self.burst_fwd self.bb_ctrl.burst_fwd = self.burst_fwd # Share clock between BTS and BB From 4de3591a3051dd47c783177c8e607ff4bf21ab00 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 14 Jul 2017 10:28:26 +0700 Subject: [PATCH 048/211] host/trxcon/l1ctl.c: fix incomplete msg in l1ctl_tx_fbsb_conf() Previously, all L1CTL_FBSB_CONF messages were sent without required l1ctl_info_dl header, what caused unpredictable behavior on higher layers (L2 & L3). Let's fix it. Change-Id: I8dae597bb4c09df36f80944434ce3624569f2cf8 --- src/host/trxcon/l1ctl.c | 12 ++++++++++- src/host/trxcon/l1ctl.h | 3 ++- src/host/trxcon/sched_lchan_handlers.c | 29 ++++++++++++++++++++++---- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index d5c3b57b6..9b0d35ac0 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -126,10 +126,13 @@ int l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type) return l1ctl_link_send(l1l, msg); } -int l1ctl_tx_fbsb_conf(struct l1ctl_link *l1l, uint8_t result, uint8_t bsic) +int l1ctl_tx_fbsb_conf(struct l1ctl_link *l1l, uint8_t result, + struct l1ctl_info_dl *dl_info, uint8_t bsic) { struct l1ctl_fbsb_conf *conf; + struct l1ctl_info_dl *dl; struct msgb *msg; + size_t len; msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF); if (msg == NULL) @@ -138,6 +141,13 @@ int l1ctl_tx_fbsb_conf(struct l1ctl_link *l1l, uint8_t result, uint8_t bsic) LOGP(DL1C, LOGL_DEBUG, "Send FBSB Conf (result=%u, bsic=%u)\n", result, bsic); + /* Copy DL info provided by handler */ + len = sizeof(struct l1ctl_info_dl); + dl = (struct l1ctl_info_dl *) msgb_put(msg, len); + memcpy(dl, dl_info, len); + talloc_free(dl_info); + + /* Fill in FBSB payload: BSIC and sync result */ conf = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*conf)); conf->result = result; conf->bsic = bsic; diff --git a/src/host/trxcon/l1ctl.h b/src/host/trxcon/l1ctl.h index 165865a83..af53b777b 100644 --- a/src/host/trxcon/l1ctl.h +++ b/src/host/trxcon/l1ctl.h @@ -6,7 +6,8 @@ #include "l1ctl_link.h" #include "l1ctl_proto.h" -int l1ctl_tx_fbsb_conf(struct l1ctl_link *l1l, uint8_t result, uint8_t bsic); +int l1ctl_tx_fbsb_conf(struct l1ctl_link *l1l, uint8_t result, + struct l1ctl_info_dl *dl_info, uint8_t bsic); int l1ctl_tx_ccch_mode_conf(struct l1ctl_link *l1l, uint8_t mode); int l1ctl_tx_pm_conf(struct l1ctl_link *l1l, uint16_t band_arfcn, int dbm, int last); diff --git a/src/host/trxcon/sched_lchan_handlers.c b/src/host/trxcon/sched_lchan_handlers.c index 49a28ef22..64893d7a1 100644 --- a/src/host/trxcon/sched_lchan_handlers.c +++ b/src/host/trxcon/sched_lchan_handlers.c @@ -224,11 +224,32 @@ int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, return -EINVAL; } + /* We don't need to send L1CTL_FBSB_CONF */ + if (trx->l1l->fbsb_conf_sent) + return 0; + /* Send L1CTL_FBSB_CONF to higher layers */ - if (!trx->l1l->fbsb_conf_sent) { - l1ctl_tx_fbsb_conf(trx->l1l, 0, bsic); - trx->bsic = bsic; - } + struct l1ctl_info_dl *data; + data = talloc_zero_size(ts, sizeof(struct l1ctl_info_dl)); + if (data == NULL) + return -ENOMEM; + + /* Fill in some downlink info */ + data->chan_nr = trx_lchan_desc[chan].chan_nr | ts->index; + data->link_id = trx_lchan_desc[chan].link_id; + data->band_arfcn = htons(trx->band_arfcn); + data->frame_nr = htonl(fn); + data->rx_level = -rssi; + + /* FIXME: set proper values */ + data->num_biterr = 0; + data->fire_crc = 0; + data->snr = 0; + + l1ctl_tx_fbsb_conf(trx->l1l, 0, data, bsic); + + /* Update BSIC value of trx_instance */ + trx->bsic = bsic; return 0; } From ff72b0724dab9f39051268eb5f51324027fc0b44 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 14 Jul 2017 19:15:41 +0700 Subject: [PATCH 049/211] host/trxcon/l1ctl.c: fix L1CTL_RACH_{REQ,CONF} handling Previously, L1CTL_RACH_REQ / L1CTL_RACH_CONF messages were handled without l1ctl_info_ul / l1ctl_info_dl header, what caused incorrect data parsing. Change-Id: I145d137f2cc7de234965e4fe64d9367ed6ccb999 --- src/host/trxcon/l1ctl.c | 25 ++++++++++++++++--------- src/host/trxcon/l1ctl.h | 2 +- src/host/trxcon/sched_lchan_handlers.c | 2 +- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 9b0d35ac0..3e9732f19 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -197,14 +197,23 @@ int l1ctl_tx_data_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data) return l1ctl_link_send(l1l, msg); } -int l1ctl_tx_rach_conf(struct l1ctl_link *l1l) +int l1ctl_tx_rach_conf(struct l1ctl_link *l1l, uint32_t fn) { + struct l1ctl_info_dl *dl; struct msgb *msg; + size_t len; msg = l1ctl_alloc_msg(L1CTL_RACH_CONF); if (msg == NULL) return -ENOMEM; + len = sizeof(struct l1ctl_info_dl); + dl = (struct l1ctl_info_dl *) msgb_put(msg, len); + + memset(dl, 0x00, len); + dl->band_arfcn = htons(l1l->trx->band_arfcn); + dl->frame_nr = htonl(fn); + return l1ctl_link_send(l1l, msg); } @@ -369,22 +378,20 @@ exit: static int l1ctl_rx_rach_req(struct l1ctl_link *l1l, struct msgb *msg) { struct l1ctl_rach_req *req; + struct l1ctl_info_ul *ul; struct trx_ts_prim *prim; struct trx_ts *ts; int len, rc = 0; - req = (struct l1ctl_rach_req *) msg->l1h; - len = sizeof(*req); - if (msgb_l1len(msg) < len) { - LOGP(DL1C, LOGL_ERROR, "MSG too short RACH Req: %d\n", len); - rc = -EINVAL; - goto exit; - } + ul = (struct l1ctl_info_ul *) msg->l1h; + req = (struct l1ctl_rach_req *) ul->payload; + len = sizeof(struct l1ctl_rach_req); /* Convert offset value to host format */ req->offset = ntohs(req->offset); - LOGP(DL1C, LOGL_DEBUG, "Recv RACH Req (offset=%u)\n", req->offset); + LOGP(DL1C, LOGL_DEBUG, "Recv RACH Req (offset=%u ra=0x%02x)\n", + req->offset, req->ra); /* FIXME: can we use other than TS0? */ ts = sched_trx_find_ts(l1l->trx, 0); diff --git a/src/host/trxcon/l1ctl.h b/src/host/trxcon/l1ctl.h index af53b777b..596a5b09e 100644 --- a/src/host/trxcon/l1ctl.h +++ b/src/host/trxcon/l1ctl.h @@ -16,4 +16,4 @@ int l1ctl_tx_reset_ind(struct l1ctl_link *l1l, uint8_t type); int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg); int l1ctl_tx_data_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *ind); -int l1ctl_tx_rach_conf(struct l1ctl_link *l1l); +int l1ctl_tx_rach_conf(struct l1ctl_link *l1l, uint32_t fn); diff --git a/src/host/trxcon/sched_lchan_handlers.c b/src/host/trxcon/sched_lchan_handlers.c index 64893d7a1..b27f811d5 100644 --- a/src/host/trxcon/sched_lchan_handlers.c +++ b/src/host/trxcon/sched_lchan_handlers.c @@ -303,7 +303,7 @@ int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, } /* Confirm RACH request */ - l1ctl_tx_rach_conf(trx->l1l); + l1ctl_tx_rach_conf(trx->l1l, fn); /* Remove primitive from queue and free memory */ llist_del(&prim->list); From ae62021dc931d1fe850c488718acc265943f10b9 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 14 Jul 2017 10:46:23 +0700 Subject: [PATCH 050/211] host/trxcon/l1ctl.c: implement FBSB expire timer Change-Id: I27f96cbe951de164fcecaf19f8305db5e5b20229 --- src/host/trxcon/l1ctl.c | 46 ++++++++++++++++++++++++++++++++++++ src/host/trxcon/l1ctl_link.h | 2 ++ 2 files changed, 48 insertions(+) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 3e9732f19..3098d9c66 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -158,6 +158,10 @@ int l1ctl_tx_fbsb_conf(struct l1ctl_link *l1l, uint8_t result, /* Ask SCH handler not to send L1CTL_FBSB_CONF anymore */ l1l->fbsb_conf_sent = 1; + /* Abort FBSB expire timer */ + if (osmo_timer_pending(&l1l->fbsb_timer)) + osmo_timer_del(&l1l->fbsb_timer); + return l1ctl_link_send(l1l, msg); } @@ -217,10 +221,45 @@ int l1ctl_tx_rach_conf(struct l1ctl_link *l1l, uint32_t fn) return l1ctl_link_send(l1l, msg); } +/* FBSB expire timer */ +static void fbsb_timer_cb(void *data) +{ + struct l1ctl_link *l1l = (struct l1ctl_link *) data; + struct l1ctl_fbsb_conf *conf; + struct l1ctl_info_dl *dl; + struct msgb *msg; + size_t len; + + msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF); + if (msg == NULL) + return; + + LOGP(DL1C, LOGL_DEBUG, "Send FBSB Conf (result=255, bsic=0)\n"); + + /* Compose DL info header */ + len = sizeof(struct l1ctl_info_dl); + dl = (struct l1ctl_info_dl *) msgb_put(msg, len); + memset(dl, 0x00, len); + + /* Fill in current ARFCN */ + dl->band_arfcn = htons(l1l->trx->band_arfcn); + + /* Fill in FBSB payload: BSIC and sync result */ + conf = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*conf)); + conf->result = 255; + conf->bsic = 0; + + /* Ask SCH handler not to send L1CTL_FBSB_CONF anymore */ + l1l->fbsb_conf_sent = 1; + + l1ctl_link_send(l1l, msg); +} + static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg) { struct l1ctl_fbsb_req *fbsb; uint16_t band_arfcn; + uint16_t timeout; int rc = 0; fbsb = (struct l1ctl_fbsb_req *) msg->l1h; @@ -232,6 +271,7 @@ static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg) } band_arfcn = ntohs(fbsb->band_arfcn); + timeout = ntohs(fbsb->timeout); LOGP(DL1C, LOGL_DEBUG, "Recv FBSB Req (%s %d)\n", gsm_band_name(gsm_arfcn2band(band_arfcn)), @@ -257,6 +297,12 @@ static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg) trx_if_cmd_txtune(l1l->trx, band_arfcn); trx_if_cmd_poweron(l1l->trx); + /* Start FBSB expire timer */ + /* TODO: share FRAME_DURATION_uS=4615 from scheduler.c */ + l1l->fbsb_timer.data = l1l; + l1l->fbsb_timer.cb = fbsb_timer_cb; + osmo_timer_schedule(&l1l->fbsb_timer, 0, timeout * 4615); + exit: msgb_free(msg); return rc; diff --git a/src/host/trxcon/l1ctl_link.h b/src/host/trxcon/l1ctl_link.h index b310ee432..f20af937b 100644 --- a/src/host/trxcon/l1ctl_link.h +++ b/src/host/trxcon/l1ctl_link.h @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -25,6 +26,7 @@ struct l1ctl_link { struct trx_instance *trx; /* L1CTL handlers specific */ + struct osmo_timer_list fbsb_timer; uint8_t fbsb_conf_sent; }; From 283fb5879c6c5fcc2418770cc0458ad1ab57c161 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 14 Jul 2017 18:33:48 +0700 Subject: [PATCH 051/211] host/trxcon/scheduler: fix TS index in sched_trx_configure_ts() TS index was not assigned after allocation of a new one. Change-Id: I1b62218f863acf27fb1d1f46b273b345b97d33d8 --- src/host/trxcon/sched_trx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 88d9d97b7..1effeb7c5 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -223,6 +223,9 @@ int sched_trx_configure_ts(struct trx_instance *trx, int ts_num, ts = sched_trx_add_ts(trx, ts_num); if (ts == NULL) return -ENOMEM; + + /* Assign TS index */ + ts->index = ts_num; } /* Init queue primitives for TX */ From 824bfa299d66039480da67e8d387a58fff845383 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 15 Jul 2017 15:20:35 +0700 Subject: [PATCH 052/211] host/trxcon/l1ctl.c: handle L1CTL_DM_{EST,REL}_REQ Change-Id: Ifdf229a6dd3c73ede313d2bfe384032e3887cc3a --- src/host/trxcon/l1ctl.c | 92 +++++++++++++++++++++++++++++++++++++ src/host/trxcon/sched_trx.c | 48 +++++++++++++++++++ src/host/trxcon/sched_trx.h | 3 ++ src/host/trxcon/trx_if.h | 1 + 4 files changed, 144 insertions(+) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 3098d9c66..34e4b3c28 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -470,6 +470,94 @@ exit: return rc; } +static int l1ctl_rx_dm_est_req(struct l1ctl_link *l1l, struct msgb *msg) +{ + enum gsm_phys_chan_config config; + enum trx_lchan_type lchan_type; + struct l1ctl_dm_est_req *est_req; + struct l1ctl_info_ul *ul; + struct trx_ts *ts; + uint16_t band_arfcn; + uint8_t chan_nr, tn; + int rc = 0; + + ul = (struct l1ctl_info_ul *) msg->l1h; + est_req = (struct l1ctl_dm_est_req *) ul->payload; + + band_arfcn = ntohs(est_req->h0.band_arfcn); + chan_nr = ul->chan_nr; + + LOGP(DL1C, LOGL_DEBUG, "Recv L1CTL_DM_EST_REQ (arfcn=%u, " + "chan_nr=0x%02x, tsc=%u)\n", (band_arfcn &~ ARFCN_FLAG_MASK), + chan_nr, est_req->tsc); + + if (est_req->h) { + LOGP(DL1C, LOGL_ERROR, "FHSS is not supported\n"); + rc = -ENOTSUP; + goto exit; + } + + /* Update TSC (Training Sequence) */ + /* FIXME: est_req->tsc is a number of TSC */ + memset(l1l->trx->tsc, 0x00, 26); + + /* Determine channel config */ + config = sched_trx_chan_nr2pchan_config(chan_nr); + if (config == GSM_PCHAN_NONE) { + LOGP(DL1C, LOGL_ERROR, "Couldn't determine channel config\n"); + rc = -EINVAL; + goto exit; + } + + /* Determine TS index */ + tn = chan_nr & 0x7; + if (tn > 7) { + LOGP(DL1C, LOGL_ERROR, "Incorrect TS index %u\n", tn); + rc = -EINVAL; + goto exit; + } + + /* Determine lchan type */ + lchan_type = sched_trx_chan_nr2lchan_type(chan_nr); + if (!lchan_type) { + LOGP(DL1C, LOGL_ERROR, "Couldn't determine lchan type\n"); + rc = -EINVAL; + goto exit; + } + + /* Configure requested TS */ + rc = sched_trx_configure_ts(l1l->trx, tn, config); + if (rc) { + rc = -EINVAL; + goto exit; + } + + /* Find just configured TS */ + ts = sched_trx_find_ts(l1l->trx, tn); + + /* Activate only requested lchan, disabling others */ + sched_trx_deactivate_all_lchans(ts); + rc = sched_trx_activate_lchan(ts, lchan_type); + if (rc) { + LOGP(DL1C, LOGL_ERROR, "Couldn't activate lchan\n"); + rc = -EINVAL; + goto exit; + } + +exit: + msgb_free(msg); + return rc; +} + +static int l1ctl_rx_dm_rel_req(struct l1ctl_link *l1l, struct msgb *msg) +{ + /* Reset scheduler */ + sched_trx_reset(l1l->trx); + + msgb_free(msg); + return 0; +} + int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) { struct l1ctl_hdr *l1h; @@ -490,6 +578,10 @@ int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) return l1ctl_rx_ccch_mode_req(l1l, msg); case L1CTL_RACH_REQ: return l1ctl_rx_rach_req(l1l, msg); + case L1CTL_DM_EST_REQ: + return l1ctl_rx_dm_est_req(l1l, msg); + case L1CTL_DM_REL_REQ: + return l1ctl_rx_dm_rel_req(l1l, msg); default: LOGP(DL1C, LOGL_ERROR, "Unknown MSG: %u\n", l1h->msg_type); msgb_free(msg); diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 1effeb7c5..be7a480df 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -372,6 +372,54 @@ int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) return 0; } +void sched_trx_deactivate_all_lchans(struct trx_ts *ts) +{ + struct trx_lchan_state *lchan; + int i, len; + + len = talloc_array_length(ts->lchans); + for (i = 0; i < len; i++) { + lchan = ts->lchans + i; + + talloc_free(lchan->rx_bursts); + talloc_free(lchan->tx_bursts); + + lchan->active = 0; + } +} + +enum gsm_phys_chan_config sched_trx_chan_nr2pchan_config(uint8_t chan_nr) +{ + uint8_t cbits = chan_nr >> 3; + + if (cbits == 0x01) + return GSM_PCHAN_TCH_F; + else if ((cbits & 0x1e) == 0x02) + return GSM_PCHAN_TCH_H; + else if ((cbits & 0x1c) == 0x04) + return GSM_PCHAN_CCCH_SDCCH4; + else if ((cbits & 0x18) == 0x08) + return GSM_PCHAN_SDCCH8_SACCH8C; + + return GSM_PCHAN_NONE; +} + +enum trx_lchan_type sched_trx_chan_nr2lchan_type(uint8_t chan_nr) +{ + uint8_t cbits = chan_nr >> 3; + + if (cbits == 0x01) + return TRXC_TCHF; + else if ((cbits & 0x1e) == 0x02) + return TRXC_TCHH_0 + (cbits & 0x1); + else if ((cbits & 0x1c) == 0x04) + return TRXC_SDCCH4_0 + (cbits & 0x3); + else if ((cbits & 0x18) == 0x08) + return TRXC_SDCCH8_0 + (cbits & 0x7); + + return TRXC_IDLE; +} + int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t ts_num, uint32_t burst_fn, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa) { diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 809a3243b..725f666de 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -258,6 +258,9 @@ int sched_trx_configure_ts(struct trx_instance *trx, int ts_num, enum gsm_phys_chan_config config); /* Logical channel management functions */ +enum gsm_phys_chan_config sched_trx_chan_nr2pchan_config(uint8_t chan_nr); +enum trx_lchan_type sched_trx_chan_nr2lchan_type(uint8_t chan_nr); +void sched_trx_deactivate_all_lchans(struct trx_ts *ts); int sched_trx_activate_lchan(struct trx_ts *ts, enum trx_lchan_type chan); int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan); struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts, diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h index 8ba09b8a7..9e3a32a2e 100644 --- a/src/host/trxcon/trx_if.h +++ b/src/host/trxcon/trx_if.h @@ -32,6 +32,7 @@ struct trx_instance { uint16_t pm_arfcn_start; uint16_t pm_arfcn_stop; uint16_t band_arfcn; + uint8_t tsc[26]; uint8_t bsic; /* Scheduler stuff */ From 74f85951560712c97af36b9679edd9ec2922887f Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 15 Jul 2017 22:46:54 +0700 Subject: [PATCH 053/211] host/trxcon/scheduler: implement xCCH TX capability Change-Id: I4da4816dcecc55eb9b4d2d6c631967026a5e4f68 --- src/host/trxcon/sched_lchan_desc.c | 5 +- src/host/trxcon/sched_lchan_handlers.c | 89 ++++++++++++++++++++++++++ src/host/trxcon/sched_trx.h | 2 + 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/src/host/trxcon/sched_lchan_desc.c b/src/host/trxcon/sched_lchan_desc.c index 55edb9893..e3998fa8b 100644 --- a/src/host/trxcon/sched_lchan_desc.c +++ b/src/host/trxcon/sched_lchan_desc.c @@ -29,7 +29,6 @@ #define LID_SACCH 0x40 /* TODO: implement */ -#define tx_data_fn NULL #define tx_pdtch_fn NULL #define tx_tchf_fn NULL #define tx_tchh_fn NULL @@ -43,6 +42,10 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, uint32_t fn, enum trx_lchan_type chan, uint8_t bid, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); +int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts, + uint32_t fn, enum trx_lchan_type chan, + uint8_t bid, uint16_t *nbits); + int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, uint32_t fn, enum trx_lchan_type chan, uint8_t bid, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); diff --git a/src/host/trxcon/sched_lchan_handlers.c b/src/host/trxcon/sched_lchan_handlers.c index b27f811d5..078c5fdea 100644 --- a/src/host/trxcon/sched_lchan_handlers.c +++ b/src/host/trxcon/sched_lchan_handlers.c @@ -164,6 +164,95 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, return 0; } +int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts, + uint32_t fn, enum trx_lchan_type chan, + uint8_t bid, uint16_t *nbits) +{ + struct trx_lchan_state *lchan; + struct trx_ts_prim *prim; + struct l1ctl_info_ul *ul; + ubit_t burst[GSM_BURST_LEN]; + ubit_t *buffer, *offset; + uint8_t *mask, *l2; + int rc; + + /* Find required channel state */ + lchan = sched_trx_find_lchan(ts, chan); + if (lchan == NULL) + return -EINVAL; + + /* Set up pointers */ + mask = &lchan->tx_burst_mask; + buffer = lchan->tx_bursts; + + if (bid > 0) { + /* If we have encoded bursts */ + if (*mask) + goto send_burst; + else + return 0; + } + + /* Encode payload if not yet */ + + /* Get a message from TX queue */ + prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); + ul = (struct l1ctl_info_ul *) prim->payload; + l2 = (uint8_t *) ul->payload; + + /* Encode bursts */ + rc = gsm0503_xcch_encode(buffer, l2); + if (rc) { + LOGP(DSCH, LOGL_ERROR, "Failed to encode L2 payload\n"); + + /* Remove primitive from queue and free memory */ + llist_del(&prim->list); + talloc_free(prim); + + return -EINVAL; + } + +send_burst: + /* Determine which burst should be sent */ + offset = buffer + bid * 116; + + /* Update mask */ + *mask |= (1 << bid); + + /* If we are sending the last (4/4) burst */ + if ((*mask & 0x0f) == 0x0f) { + /* Remove primitive from queue and free memory */ + prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); + llist_del(&prim->list); + talloc_free(prim); + + /* Reset mask */ + *mask = 0x00; + } + + /* Compose a new burst */ + memset(burst, 0, 3); /* TB */ + memcpy(burst + 3, offset, 58); /* Payload 1/2 */ + memcpy(burst + 61, trx->tsc, 26); /* TSC */ + memcpy(burst + 87, offset + 58, 58); /* Payload 2/2 */ + memset(burst + 145, 0, 3); /* TB */ + + if (nbits) + *nbits = GSM_BURST_LEN; + + LOGP(DSCH, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n", + trx_lchan_desc[chan].name, fn, ts->index, bid); + + /* Send burst to transceiver */ + rc = trx_if_tx_burst(trx, ts->index, fn, 10, burst); + if (rc) { + LOGP(DSCH, LOGL_ERROR, "Could not send burst to transceiver\n"); + return rc; + } + + return 0; +} + static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info) { uint8_t t3p; diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 725f666de..cab290b85 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -148,6 +148,8 @@ struct trx_lchan_state { uint32_t rx_first_fn; /*! \brief Mask of received bursts */ uint8_t rx_burst_mask; + /*! \brief Mask of transmitted bursts */ + uint8_t tx_burst_mask; /*! \brief Burst buffer for RX */ sbit_t *rx_bursts; /*! \brief Burst buffer for TX */ From b5e4f26f6fb4429248abd6d0287f00ee13201208 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 15 Jul 2017 22:51:12 +0700 Subject: [PATCH 054/211] host/trxcon/l1ctl.c: handle L1CTL_DATA_REQ Change-Id: Ia72fd3d55c86697ff144446bbae94f76839eb5a1 --- src/host/trxcon/l1ctl.c | 68 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 34e4b3c28..64e9d650e 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -558,6 +558,72 @@ static int l1ctl_rx_dm_rel_req(struct l1ctl_link *l1l, struct msgb *msg) return 0; } +static int l1ctl_rx_data_req(struct l1ctl_link *l1l, struct msgb *msg) +{ + struct trx_ts *ts; + struct trx_ts_prim *prim; + struct l1ctl_info_ul *ul; + struct l1ctl_data_ind *data_ind; + enum trx_lchan_type lchan_type; + uint8_t chan_nr, tn; + size_t len; + int rc = 0; + + ul = (struct l1ctl_info_ul *) msg->l1h; + data_ind = (struct l1ctl_data_ind *) ul->payload; + chan_nr = ul->chan_nr; + + LOGP(DL1C, LOGL_DEBUG, "Recv Data Req (chan_nr=0x%02x)\n", chan_nr); + + /* Determine TS index */ + tn = chan_nr & 0x7; + if (tn > 7) { + LOGP(DL1C, LOGL_ERROR, "Incorrect TS index %u\n", tn); + rc = -EINVAL; + goto exit; + } + + /* Determine lchan type */ + lchan_type = sched_trx_chan_nr2lchan_type(chan_nr); + if (!lchan_type) { + LOGP(DL1C, LOGL_ERROR, "Couldn't determine lchan type\n"); + rc = -EINVAL; + goto exit; + } + + /* Attempt to find required TS */ + ts = sched_trx_find_ts(l1l->trx, tn); + if (ts == NULL) { + LOGP(DL1C, LOGL_DEBUG, "Couldn't find required TS\n"); + rc = -EINVAL; + goto exit; + } + + /* Allocate a new primitive */ + len = sizeof(struct trx_ts_prim) + sizeof(struct l1ctl_info_ul) + 23; + prim = talloc_zero_size(ts, len); + if (prim == NULL) { + LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory\n"); + rc = -ENOMEM; + goto exit; + } + + /* Set logical channel of primitive */ + prim->chan = lchan_type; + + /* Fill in both UL info and payload */ + len = sizeof(struct l1ctl_info_ul); + memcpy(prim->payload, ul, len); + memcpy(prim->payload + len, data_ind, 23); + + /* Add to TS queue */ + llist_add_tail(&prim->list, &ts->tx_prims); + +exit: + msgb_free(msg); + return rc; +} + int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) { struct l1ctl_hdr *l1h; @@ -582,6 +648,8 @@ int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) return l1ctl_rx_dm_est_req(l1l, msg); case L1CTL_DM_REL_REQ: return l1ctl_rx_dm_rel_req(l1l, msg); + case L1CTL_DATA_REQ: + return l1ctl_rx_data_req(l1l, msg); default: LOGP(DL1C, LOGL_ERROR, "Unknown MSG: %u\n", l1h->msg_type); msgb_free(msg); From 538bea917b858b3c368adc2a59e070b7ae54c418 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sun, 16 Jul 2017 00:30:54 +0700 Subject: [PATCH 055/211] fake_trx: fix compatibility with Python 3 Change-Id: Idce0c631aa3fcd20092a3773558570e442b2fec8 --- src/target/fake_trx/clck_gen.py | 2 +- src/target/fake_trx/ctrl_cmd.py | 2 +- src/target/fake_trx/fake_trx.py | 4 ++-- src/target/fake_trx/udp_link.py | 5 ++++- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/target/fake_trx/clck_gen.py b/src/target/fake_trx/clck_gen.py index 4ef597f67..f1c1f9835 100755 --- a/src/target/fake_trx/clck_gen.py +++ b/src/target/fake_trx/clck_gen.py @@ -94,7 +94,7 @@ class Application: self.clck.start() def sig_handler(self, signum, frame): - print "Signal %d received" % signum + print("Signal %d received" % signum) if signum is signal.SIGINT: self.clck.stop() self.link.shutdown() diff --git a/src/target/fake_trx/ctrl_cmd.py b/src/target/fake_trx/ctrl_cmd.py index a9c7d68a3..e362408fe 100755 --- a/src/target/fake_trx/ctrl_cmd.py +++ b/src/target/fake_trx/ctrl_cmd.py @@ -54,7 +54,7 @@ class Application: if self.ctrl_link.sock in r_event: data, addr = self.ctrl_link.sock.recvfrom(128) - sys.stdout.write("\r%s\n" % data) + sys.stdout.write("\r%s\n" % data.decode()) sys.stdout.flush() def handle_cmd(self, cmd): diff --git a/src/target/fake_trx/fake_trx.py b/src/target/fake_trx/fake_trx.py index aaa308d7e..b18607164 100755 --- a/src/target/fake_trx/fake_trx.py +++ b/src/target/fake_trx/fake_trx.py @@ -114,12 +114,12 @@ class Application: # CTRL commands from BTS if self.bts_ctrl.sock in r_event: data, addr = self.bts_ctrl.sock.recvfrom(128) - self.bts_ctrl.handle_rx(data) + self.bts_ctrl.handle_rx(data.decode()) # CTRL commands from BB if self.bb_ctrl.sock in r_event: data, addr = self.bb_ctrl.sock.recvfrom(128) - self.bb_ctrl.handle_rx(data) + self.bb_ctrl.handle_rx(data.decode()) def shutdown(self): print("[i] Shutting down...") diff --git a/src/target/fake_trx/udp_link.py b/src/target/fake_trx/udp_link.py index 3fa50502e..0afd1504e 100644 --- a/src/target/fake_trx/udp_link.py +++ b/src/target/fake_trx/udp_link.py @@ -41,12 +41,15 @@ class UDPLink: # Check for incoming data if self.sock in r_event: data, addr = self.sock.recvfrom(128) - self.handle_rx(data) + self.handle_rx(data.decode()) def shutdown(self): self.sock.close(); def send(self, data): + if type(data) not in [bytearray, bytes]: + data = data.encode() + self.sock.sendto(data, (self.remote_addr, self.remote_port)) def handle_rx(self, data): From f21f7036e8f60419c43c8da875098d93059f5c3a Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sun, 16 Jul 2017 16:52:57 +0700 Subject: [PATCH 056/211] host/trxcon/scheduler: implement TSC selection Change-Id: I004cc71aafe0a26e5141a4b2ffa90063e961be31 --- src/host/trxcon/l1ctl.c | 5 ++- src/host/trxcon/sched_lchan_handlers.c | 42 +++++++++++++++++++++++++- src/host/trxcon/trx_if.h | 2 +- 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 64e9d650e..12745e97a 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -497,9 +497,8 @@ static int l1ctl_rx_dm_est_req(struct l1ctl_link *l1l, struct msgb *msg) goto exit; } - /* Update TSC (Training Sequence) */ - /* FIXME: est_req->tsc is a number of TSC */ - memset(l1l->trx->tsc, 0x00, 26); + /* Update TSC (Training Sequence Code) */ + l1l->trx->tsc = est_req->tsc; /* Determine channel config */ config = sched_trx_chan_nr2pchan_config(chan_nr); diff --git a/src/host/trxcon/sched_lchan_handlers.c b/src/host/trxcon/sched_lchan_handlers.c index 078c5fdea..303ae8185 100644 --- a/src/host/trxcon/sched_lchan_handlers.c +++ b/src/host/trxcon/sched_lchan_handlers.c @@ -48,6 +48,42 @@ extern struct osmo_fsm_inst *trxcon_fsm; +/* GSM 05.02 Chapter 5.2.3 Normal Burst (NB) */ +static const uint8_t nb_training_bits[8][26] = { + { + 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, + 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, + }, + { + 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, + 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, + }, + { + 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, + 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, + }, + { + 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, + 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, + }, + { + 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, + 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, + }, + { + 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, + 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, + }, + { + 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, + 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, + }, + { + 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, + 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, + }, +}; + int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, uint32_t fn, enum trx_lchan_type chan, uint8_t bid, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa) @@ -174,6 +210,7 @@ int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts, ubit_t burst[GSM_BURST_LEN]; ubit_t *buffer, *offset; uint8_t *mask, *l2; + const uint8_t *tsc; int rc; /* Find required channel state */ @@ -219,6 +256,9 @@ send_burst: /* Update mask */ *mask |= (1 << bid); + /* Choose proper TSC */ + tsc = nb_training_bits[trx->tsc]; + /* If we are sending the last (4/4) burst */ if ((*mask & 0x0f) == 0x0f) { /* Remove primitive from queue and free memory */ @@ -233,7 +273,7 @@ send_burst: /* Compose a new burst */ memset(burst, 0, 3); /* TB */ memcpy(burst + 3, offset, 58); /* Payload 1/2 */ - memcpy(burst + 61, trx->tsc, 26); /* TSC */ + memcpy(burst + 61, tsc, 26); /* TSC */ memcpy(burst + 87, offset + 58, 58); /* Payload 2/2 */ memset(burst + 145, 0, 3); /* TB */ diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h index 9e3a32a2e..6367f459e 100644 --- a/src/host/trxcon/trx_if.h +++ b/src/host/trxcon/trx_if.h @@ -32,8 +32,8 @@ struct trx_instance { uint16_t pm_arfcn_start; uint16_t pm_arfcn_stop; uint16_t band_arfcn; - uint8_t tsc[26]; uint8_t bsic; + uint8_t tsc; /* Scheduler stuff */ struct trx_sched sched; From f2af7d2953429a15cda5b52bedec226ea0b47aab Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sun, 16 Jul 2017 17:13:34 +0700 Subject: [PATCH 057/211] host/trxcon/scheduler: confirm xCCH data sending Change-Id: I40994e7046c25306a0a323910a65e195d2d8fbd0 --- src/host/trxcon/l1ctl.c | 13 ++++++++++ src/host/trxcon/l1ctl.h | 1 + src/host/trxcon/sched_lchan_handlers.c | 34 +++++++++++++++++--------- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 12745e97a..7d96396f0 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -221,6 +221,19 @@ int l1ctl_tx_rach_conf(struct l1ctl_link *l1l, uint32_t fn) return l1ctl_link_send(l1l, msg); } +int l1ctl_tx_data_conf(struct l1ctl_link *l1l) +{ + struct msgb *msg; + + msg = l1ctl_alloc_msg(L1CTL_DATA_CONF); + if (msg == NULL) + return -ENOMEM; + + LOGP(DL1C, LOGL_DEBUG, "Send Data Conf\n"); + + return l1ctl_link_send(l1l, msg); +} + /* FBSB expire timer */ static void fbsb_timer_cb(void *data) { diff --git a/src/host/trxcon/l1ctl.h b/src/host/trxcon/l1ctl.h index 596a5b09e..6c61bfd96 100644 --- a/src/host/trxcon/l1ctl.h +++ b/src/host/trxcon/l1ctl.h @@ -17,3 +17,4 @@ int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg); int l1ctl_tx_data_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *ind); int l1ctl_tx_rach_conf(struct l1ctl_link *l1l, uint32_t fn); +int l1ctl_tx_data_conf(struct l1ctl_link *l1l); diff --git a/src/host/trxcon/sched_lchan_handlers.c b/src/host/trxcon/sched_lchan_handlers.c index 303ae8185..1c7a3136a 100644 --- a/src/host/trxcon/sched_lchan_handlers.c +++ b/src/host/trxcon/sched_lchan_handlers.c @@ -259,17 +259,6 @@ send_burst: /* Choose proper TSC */ tsc = nb_training_bits[trx->tsc]; - /* If we are sending the last (4/4) burst */ - if ((*mask & 0x0f) == 0x0f) { - /* Remove primitive from queue and free memory */ - prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); - llist_del(&prim->list); - talloc_free(prim); - - /* Reset mask */ - *mask = 0x00; - } - /* Compose a new burst */ memset(burst, 0, 3); /* TB */ memcpy(burst + 3, offset, 58); /* Payload 1/2 */ @@ -287,9 +276,32 @@ send_burst: rc = trx_if_tx_burst(trx, ts->index, fn, 10, burst); if (rc) { LOGP(DSCH, LOGL_ERROR, "Could not send burst to transceiver\n"); + + /* Remove primitive from queue and free memory */ + prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); + llist_del(&prim->list); + talloc_free(prim); + + /* Reset mask */ + *mask = 0x00; + return rc; } + /* If we have sent the last (4/4) burst */ + if ((*mask & 0x0f) == 0x0f) { + /* Remove primitive from queue and free memory */ + prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); + llist_del(&prim->list); + talloc_free(prim); + + /* Reset mask */ + *mask = 0x00; + + /* Confirm data sending */ + l1ctl_tx_data_conf(trx->l1l); + } + return 0; } From 77b6833146618d119642c1fc61779ca96bf6e99c Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sun, 16 Jul 2017 17:15:56 +0700 Subject: [PATCH 058/211] host/trxcon/scheduler: add a reference to GSM 05.02 Change-Id: I067af9c114bcbc5bd74515d5008e21a07fd0167e --- src/host/trxcon/sched_lchan_handlers.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/host/trxcon/sched_lchan_handlers.c b/src/host/trxcon/sched_lchan_handlers.c index 1c7a3136a..fcc4e71c0 100644 --- a/src/host/trxcon/sched_lchan_handlers.c +++ b/src/host/trxcon/sched_lchan_handlers.c @@ -395,7 +395,10 @@ int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, return 0; } -/* 41-bit RACH synchronization sequence */ +/** + * 41-bit RACH synchronization sequence + * GSM 05.02 Chapter 5.2.7 Access burst (AB) + */ static ubit_t rach_synch_seq[] = { 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, From f604869944d594431654332b0bad59a6e7a997e4 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 26 Jul 2017 13:50:40 +0600 Subject: [PATCH 059/211] host/trxcon: share trxcon fsm and talloc ctx via trxcon.h Change-Id: I9ef558f84a6dc1c9b8fc394c48e108676fa169f8 --- src/host/trxcon/l1ctl.c | 3 --- src/host/trxcon/l1ctl_link.c | 3 --- src/host/trxcon/sched_clck.c | 2 -- src/host/trxcon/sched_lchan_handlers.c | 2 -- src/host/trxcon/trx_if.c | 3 --- src/host/trxcon/trxcon.h | 3 +++ 6 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 7d96396f0..cbe037ad5 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -46,9 +46,6 @@ #include "trx_if.h" #include "sched_trx.h" -extern void *tall_trx_ctx; -extern struct osmo_fsm_inst *trxcon_fsm; - static struct msgb *l1ctl_alloc_msg(uint8_t msg_type) { struct l1ctl_hdr *l1h; diff --git a/src/host/trxcon/l1ctl_link.c b/src/host/trxcon/l1ctl_link.c index d2ceb177e..dfcbecb20 100644 --- a/src/host/trxcon/l1ctl_link.c +++ b/src/host/trxcon/l1ctl_link.c @@ -45,9 +45,6 @@ #include "l1ctl_link.h" #include "l1ctl.h" -extern void *tall_trx_ctx; -extern struct osmo_fsm_inst *trxcon_fsm; - static struct osmo_fsm_state l1ctl_fsm_states[] = { [L1CTL_STATE_IDLE] = { .out_state_mask = GEN_MASK(L1CTL_STATE_CONNECTED), diff --git a/src/host/trxcon/sched_clck.c b/src/host/trxcon/sched_clck.c index 31f3ef2c6..efb3f4c9c 100644 --- a/src/host/trxcon/sched_clck.c +++ b/src/host/trxcon/sched_clck.c @@ -44,8 +44,6 @@ #define MAX_FN_SKEW 50 #define TRX_LOSS_FRAMES 400 -extern struct osmo_fsm_inst *trxcon_fsm; - static void sched_clck_tick(void *data) { struct trx_sched *sched = (struct trx_sched *) data; diff --git a/src/host/trxcon/sched_lchan_handlers.c b/src/host/trxcon/sched_lchan_handlers.c index fcc4e71c0..3eec5f18c 100644 --- a/src/host/trxcon/sched_lchan_handlers.c +++ b/src/host/trxcon/sched_lchan_handlers.c @@ -46,8 +46,6 @@ #include "trxcon.h" #include "l1ctl.h" -extern struct osmo_fsm_inst *trxcon_fsm; - /* GSM 05.02 Chapter 5.2.3 Normal Burst (NB) */ static const uint8_t nb_training_bits[8][26] = { { diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index 41c378ff3..f99a61645 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -45,9 +45,6 @@ #include "logging.h" #include "scheduler.h" -extern void *tall_trx_ctx; -extern struct osmo_fsm_inst *trxcon_fsm; - static struct osmo_fsm_state trx_fsm_states[] = { [TRX_STATE_OFFLINE] = { .out_state_mask = ( diff --git a/src/host/trxcon/trxcon.h b/src/host/trxcon/trxcon.h index b3d4e3e25..17d34089e 100644 --- a/src/host/trxcon/trxcon.h +++ b/src/host/trxcon/trxcon.h @@ -2,6 +2,9 @@ #define GEN_MASK(state) (0x01 << state) +extern struct osmo_fsm_inst *trxcon_fsm; +extern void *tall_trx_ctx; + enum trxcon_fsm_states { TRXCON_STATE_IDLE = 0, TRXCON_STATE_MANAGED, From 9760a84a6db802d8c54d239a4d76334e93bac607 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 26 Jul 2017 14:30:35 +0600 Subject: [PATCH 060/211] host/trxcon: split sched_lchan_handlers.c It would be better to have xCCH, SCH and RACH burst handlers in separate files, because as much code we add to a single file, as harder it becomes to read and understand one. Change-Id: I456a60e68b32b792e63dd03ae97159dc101fd4bf --- src/host/trxcon/Makefile.am | 4 +- src/host/trxcon/sched_lchan_rach.c | 108 +++++++++++++ src/host/trxcon/sched_lchan_sch.c | 137 ++++++++++++++++ ...ed_lchan_handlers.c => sched_lchan_xcch.c} | 151 ------------------ 4 files changed, 248 insertions(+), 152 deletions(-) create mode 100644 src/host/trxcon/sched_lchan_rach.c create mode 100644 src/host/trxcon/sched_lchan_sch.c rename src/host/trxcon/{sched_lchan_handlers.c => sched_lchan_xcch.c} (66%) diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am index b2f22fab2..cd4bbd259 100644 --- a/src/host/trxcon/Makefile.am +++ b/src/host/trxcon/Makefile.am @@ -31,8 +31,10 @@ trxcon_SOURCES = \ # Scheduler trxcon_SOURCES += \ - sched_lchan_handlers.c \ sched_lchan_desc.c \ + sched_lchan_xcch.c \ + sched_lchan_rach.c \ + sched_lchan_sch.c \ sched_mframe.c \ sched_clck.c \ sched_trx.c \ diff --git a/src/host/trxcon/sched_lchan_rach.c b/src/host/trxcon/sched_lchan_rach.c new file mode 100644 index 000000000..0e8d2e803 --- /dev/null +++ b/src/host/trxcon/sched_lchan_rach.c @@ -0,0 +1,108 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: handlers for DL / UL bursts on logical channels + * + * (C) 2017 by Vadim Yanitskiy + * + * 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 +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "l1ctl_proto.h" +#include "scheduler.h" +#include "sched_trx.h" +#include "logging.h" +#include "trx_if.h" +#include "trxcon.h" +#include "l1ctl.h" + +/** + * 41-bit RACH synchronization sequence + * GSM 05.02 Chapter 5.2.7 Access burst (AB) + */ +static ubit_t rach_synch_seq[] = { + 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, + 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, +}; + +/* Obtain a to-be-transmitted RACH burst */ +int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, + uint32_t fn, enum trx_lchan_type chan, + uint8_t bid, uint16_t *nbits) +{ + struct trx_ts_prim *prim; + struct l1ctl_rach_req *req; + uint8_t burst[GSM_BURST_LEN]; + uint8_t payload[36]; + int rc; + + /* Get a message from TX queue */ + prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); + req = (struct l1ctl_rach_req *) prim->payload; + + /* Delay RACH sending according to offset value */ + if (req->offset-- > 0) + return 0; + + /* Encode payload */ + rc = gsm0503_rach_encode(payload, &req->ra, trx->bsic); + if (rc) { + LOGP(DSCH, LOGL_ERROR, "Could not encode RACH burst\n"); + return rc; + } + + /* Compose RACH burst */ + memset(burst, 0, 8); /* TB */ + memcpy(burst + 8, rach_synch_seq, 41); /* sync seq */ + memcpy(burst + 49, payload, 36); /* payload */ + memset(burst + 85, 0, 63); /* TB + GP */ + + LOGP(DSCH, LOGL_DEBUG, "Transmitting RACH fn=%u\n", fn); + + /* Send burst to transceiver */ + rc = trx_if_tx_burst(trx, ts->index, fn, 10, burst); + if (rc) { + LOGP(DSCH, LOGL_ERROR, "Could not send burst to transceiver\n"); + return rc; + } + + /* Confirm RACH request */ + l1ctl_tx_rach_conf(trx->l1l, fn); + + /* Remove primitive from queue and free memory */ + llist_del(&prim->list); + talloc_free(prim); + + return 0; +} diff --git a/src/host/trxcon/sched_lchan_sch.c b/src/host/trxcon/sched_lchan_sch.c new file mode 100644 index 000000000..9e854bbfc --- /dev/null +++ b/src/host/trxcon/sched_lchan_sch.c @@ -0,0 +1,137 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: handlers for DL / UL bursts on logical channels + * + * (C) 2017 by Vadim Yanitskiy + * + * 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 +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "l1ctl_proto.h" +#include "scheduler.h" +#include "sched_trx.h" +#include "logging.h" +#include "trx_if.h" +#include "trxcon.h" +#include "l1ctl.h" + +static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info) +{ + uint8_t t3p; + uint32_t sb; + + sb = (sb_info[3] << 24) + | (sb_info[2] << 16) + | (sb_info[1] << 8) + | sb_info[0]; + + *bsic = (sb >> 2) & 0x3f; + + /* TS 05.02 Chapter 3.3.2.2.1 SCH Frame Numbers */ + time->t1 = ((sb >> 23) & 0x01) + | ((sb >> 7) & 0x1fe) + | ((sb << 9) & 0x600); + + time->t2 = (sb >> 18) & 0x1f; + + t3p = ((sb >> 24) & 0x01) | ((sb >> 15) & 0x06); + time->t3 = t3p * 10 + 1; + + /* TS 05.02 Chapter 4.3.3 TDMA frame number */ + time->fn = gsm_gsmtime2fn(time); +} + +int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, + uint32_t fn, enum trx_lchan_type chan, uint8_t bid, + sbit_t *bits, uint16_t nbits, int8_t rssi, float toa) +{ + sbit_t payload[2 * 39]; + struct gsm_time time; + uint8_t sb_info[4]; + uint8_t bsic; + int rc; + + /* Obtain payload from burst */ + memcpy(payload, bits + 3, 39); + memcpy(payload + 39, bits + 3 + 39 + 64, 39); + + /* Attempt to decode */ + rc = gsm0503_sch_decode(sb_info, payload); + if (rc) { + LOGP(DSCH, LOGL_DEBUG, "Received bad SCH burst at fn=%u\n", fn); + return rc; + } + + /* Decode BSIC and TDMA frame number */ + decode_sb(&time, &bsic, sb_info); + + LOGP(DSCH, LOGL_DEBUG, "Received SCH: bsic=%u, fn=%u, sched_fn=%u\n", + bsic, time.fn, trx->sched.fn_counter_proc); + + /* Check if decoded frame number matches */ + if (time.fn != fn) { + LOGP(DSCH, LOGL_ERROR, "Decoded fn=%u does not match " + "fn=%u provided by scheduler\n", time.fn, fn); + return -EINVAL; + } + + /* We don't need to send L1CTL_FBSB_CONF */ + if (trx->l1l->fbsb_conf_sent) + return 0; + + /* Send L1CTL_FBSB_CONF to higher layers */ + struct l1ctl_info_dl *data; + data = talloc_zero_size(ts, sizeof(struct l1ctl_info_dl)); + if (data == NULL) + return -ENOMEM; + + /* Fill in some downlink info */ + data->chan_nr = trx_lchan_desc[chan].chan_nr | ts->index; + data->link_id = trx_lchan_desc[chan].link_id; + data->band_arfcn = htons(trx->band_arfcn); + data->frame_nr = htonl(fn); + data->rx_level = -rssi; + + /* FIXME: set proper values */ + data->num_biterr = 0; + data->fire_crc = 0; + data->snr = 0; + + l1ctl_tx_fbsb_conf(trx->l1l, 0, data, bsic); + + /* Update BSIC value of trx_instance */ + trx->bsic = bsic; + + return 0; +} diff --git a/src/host/trxcon/sched_lchan_handlers.c b/src/host/trxcon/sched_lchan_xcch.c similarity index 66% rename from src/host/trxcon/sched_lchan_handlers.c rename to src/host/trxcon/sched_lchan_xcch.c index 3eec5f18c..12bb6ee53 100644 --- a/src/host/trxcon/sched_lchan_handlers.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -302,154 +302,3 @@ send_burst: return 0; } - -static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info) -{ - uint8_t t3p; - uint32_t sb; - - sb = (sb_info[3] << 24) - | (sb_info[2] << 16) - | (sb_info[1] << 8) - | sb_info[0]; - - *bsic = (sb >> 2) & 0x3f; - - /* TS 05.02 Chapter 3.3.2.2.1 SCH Frame Numbers */ - time->t1 = ((sb >> 23) & 0x01) - | ((sb >> 7) & 0x1fe) - | ((sb << 9) & 0x600); - - time->t2 = (sb >> 18) & 0x1f; - - t3p = ((sb >> 24) & 0x01) | ((sb >> 15) & 0x06); - time->t3 = t3p * 10 + 1; - - /* TS 05.02 Chapter 4.3.3 TDMA frame number */ - time->fn = gsm_gsmtime2fn(time); -} - -int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, - uint32_t fn, enum trx_lchan_type chan, uint8_t bid, - sbit_t *bits, uint16_t nbits, int8_t rssi, float toa) -{ - sbit_t payload[2 * 39]; - struct gsm_time time; - uint8_t sb_info[4]; - uint8_t bsic; - int rc; - - /* Obtain payload from burst */ - memcpy(payload, bits + 3, 39); - memcpy(payload + 39, bits + 3 + 39 + 64, 39); - - /* Attempt to decode */ - rc = gsm0503_sch_decode(sb_info, payload); - if (rc) { - LOGP(DSCH, LOGL_DEBUG, "Received bad SCH burst at fn=%u\n", fn); - return rc; - } - - /* Decode BSIC and TDMA frame number */ - decode_sb(&time, &bsic, sb_info); - - LOGP(DSCH, LOGL_DEBUG, "Received SCH: bsic=%u, fn=%u, sched_fn=%u\n", - bsic, time.fn, trx->sched.fn_counter_proc); - - /* Check if decoded frame number matches */ - if (time.fn != fn) { - LOGP(DSCH, LOGL_ERROR, "Decoded fn=%u does not match " - "fn=%u provided by scheduler\n", time.fn, fn); - return -EINVAL; - } - - /* We don't need to send L1CTL_FBSB_CONF */ - if (trx->l1l->fbsb_conf_sent) - return 0; - - /* Send L1CTL_FBSB_CONF to higher layers */ - struct l1ctl_info_dl *data; - data = talloc_zero_size(ts, sizeof(struct l1ctl_info_dl)); - if (data == NULL) - return -ENOMEM; - - /* Fill in some downlink info */ - data->chan_nr = trx_lchan_desc[chan].chan_nr | ts->index; - data->link_id = trx_lchan_desc[chan].link_id; - data->band_arfcn = htons(trx->band_arfcn); - data->frame_nr = htonl(fn); - data->rx_level = -rssi; - - /* FIXME: set proper values */ - data->num_biterr = 0; - data->fire_crc = 0; - data->snr = 0; - - l1ctl_tx_fbsb_conf(trx->l1l, 0, data, bsic); - - /* Update BSIC value of trx_instance */ - trx->bsic = bsic; - - return 0; -} - -/** - * 41-bit RACH synchronization sequence - * GSM 05.02 Chapter 5.2.7 Access burst (AB) - */ -static ubit_t rach_synch_seq[] = { - 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, - 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, - 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, -}; - -/* Obtain a to-be-transmitted RACH burst */ -int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, - uint32_t fn, enum trx_lchan_type chan, - uint8_t bid, uint16_t *nbits) -{ - struct trx_ts_prim *prim; - struct l1ctl_rach_req *req; - uint8_t burst[GSM_BURST_LEN]; - uint8_t payload[36]; - int rc; - - /* Get a message from TX queue */ - prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); - req = (struct l1ctl_rach_req *) prim->payload; - - /* Delay RACH sending according to offset value */ - if (req->offset-- > 0) - return 0; - - /* Encode payload */ - rc = gsm0503_rach_encode(payload, &req->ra, trx->bsic); - if (rc) { - LOGP(DSCH, LOGL_ERROR, "Could not encode RACH burst\n"); - return rc; - } - - /* Compose RACH burst */ - memset(burst, 0, 8); /* TB */ - memcpy(burst + 8, rach_synch_seq, 41); /* sync seq */ - memcpy(burst + 49, payload, 36); /* payload */ - memset(burst + 85, 0, 63); /* TB + GP */ - - LOGP(DSCH, LOGL_DEBUG, "Transmitting RACH fn=%u\n", fn); - - /* Send burst to transceiver */ - rc = trx_if_tx_burst(trx, ts->index, fn, 10, burst); - if (rc) { - LOGP(DSCH, LOGL_ERROR, "Could not send burst to transceiver\n"); - return rc; - } - - /* Confirm RACH request */ - l1ctl_tx_rach_conf(trx->l1l, fn); - - /* Remove primitive from queue and free memory */ - llist_del(&prim->list); - talloc_free(prim); - - return 0; -} From 17481e2b88633271e9347c8413ffe8445fca902a Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 26 Jul 2017 19:51:44 +0600 Subject: [PATCH 061/211] host/trxcon/trx_if.c: get rid of CLCK interface Local clock counter can be corrected using frame number values, obtained from burst header on DATA interface. Change-Id: I5a813e3dc1b960831343b8ecb80718291f20e80d --- src/host/trxcon/trx_if.c | 63 +++------------------------------------- src/host/trxcon/trx_if.h | 1 - 2 files changed, 4 insertions(+), 60 deletions(-) diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index f99a61645..961f46c4b 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -126,59 +126,6 @@ static void trx_udp_close(struct osmo_fd *ofd) } } -/* ------------------------------------------------------------------------ */ -/* Clock (CLCK) interface handlers */ -/* ------------------------------------------------------------------------ */ -/* Indications on the Master Clock Interface */ -/* */ -/* The master clock interface is output only (from the radio). */ -/* Messages are "indications". */ -/* */ -/* CLOCK gives the current value of the transceiver clock to be used by the */ -/* core. This message is sent whenever a transmission packet arrives that */ -/* is too late or too early. The clock value is NOT the current transceiver */ -/* time. It is a time setting the core should use to give better packet */ -/* arrival times. */ -/* */ -/* IND CLOCK */ -/* ------------------------------------------------------------------------ */ - -static int trx_clck_read_cb(struct osmo_fd *ofd, unsigned int what) -{ - struct trx_instance *trx = (struct trx_instance *) ofd->data; - char buf[1500]; - uint32_t fn; - int len; - - len = recv(ofd->fd, buf, sizeof(buf) - 1, 0); - if (len <= 0) - return len; - - /* Terminate received string */ - buf[len] = '\0'; - - if (!!strncmp(buf, "IND CLOCK ", 10)) { - LOGP(DTRX, LOGL_ERROR, - "Unknown message on CLCK socket: %s\n", buf); - return 0; - } - - sscanf(buf, "IND CLOCK %u", &fn); - - LOGP(DTRX, LOGL_DEBUG, "Clock indication: fn=%u\n", fn); - - if (fn >= 2715648) { - fn %= 2715648; - LOGP(DTRX, LOGL_ERROR, "Indicated clock's FN is not wrapping " - "correctly, correcting to fn=%u\n", fn); - } - - /* Call the clck_ind callback */ - sched_clck_handle(&trx->sched, fn); - - return 0; -} - /* ------------------------------------------------------------------------ */ /* Control (CTRL) interface handlers */ /* ------------------------------------------------------------------------ */ @@ -634,6 +581,10 @@ static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what) /* Poke scheduler */ sched_trx_handle_rx_burst(trx, tn, fn, bits, 148, rssi, toa); + /* Correct local clock counter */ + if (fn % 51 == 0) + sched_clck_handle(&trx->sched, fn); + return 0; } @@ -695,11 +646,6 @@ int trx_if_open(struct trx_instance **trx, const char *host, uint16_t port) INIT_LLIST_HEAD(&trx_new->trx_ctrl_list); /* Open sockets */ - rc = trx_udp_open(trx_new, &trx_new->trx_ofd_clck, host, - port + 100, port + 0, trx_clck_read_cb); - if (rc < 0) - goto error; - rc = trx_udp_open(trx_new, &trx_new->trx_ofd_ctrl, host, port + 101, port + 1, trx_ctrl_read_cb); if (rc < 0) @@ -756,7 +702,6 @@ void trx_if_close(struct trx_instance *trx) trx_if_flush_ctrl(trx); /* Close sockets */ - trx_udp_close(&trx->trx_ofd_clck); trx_udp_close(&trx->trx_ofd_ctrl); trx_udp_close(&trx->trx_ofd_data); diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h index 6367f459e..7278bc11c 100644 --- a/src/host/trxcon/trx_if.h +++ b/src/host/trxcon/trx_if.h @@ -19,7 +19,6 @@ enum trx_fsm_states { }; struct trx_instance { - struct osmo_fd trx_ofd_clck; struct osmo_fd trx_ofd_ctrl; struct osmo_fd trx_ofd_data; From 4fa6694006825376bac9299ebbd112b4ac1e16a6 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 26 Jul 2017 19:46:47 +0600 Subject: [PATCH 062/211] fake_trx: don't send clock indications to mobile stations Clock indications are only required for BTS, while MS can obtain current frame number from messages on DATA interface. Change-Id: Id2993847a3581cac0d355850ad09ceabc6116d3f --- src/target/fake_trx/fake_trx.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/target/fake_trx/fake_trx.py b/src/target/fake_trx/fake_trx.py index b18607164..477270199 100755 --- a/src/target/fake_trx/fake_trx.py +++ b/src/target/fake_trx/fake_trx.py @@ -85,12 +85,10 @@ class Application: self.bts_ctrl.burst_fwd = self.burst_fwd self.bb_ctrl.burst_fwd = self.burst_fwd - # Share clock between BTS and BB + # Provide clock to BTS self.bts_clck = UDPLink(self.bts_addr, self.bts_base_port + 100, self.bts_base_port) - self.bb_clck = UDPLink(self.bb_addr, - self.bb_base_port + 100, self.bb_base_port) - self.clck_gen = CLCKGen([self.bts_clck, self.bb_clck]) + self.clck_gen = CLCKGen([self.bts_clck]) self.clck_gen.start() print("[i] Init complete") @@ -129,7 +127,6 @@ class Application: # Close CLCK interfaces self.bts_clck.shutdown() - self.bb_clck.shutdown() # Close CTRL interfaces self.bts_ctrl.shutdown() From 6c3ce20d758cff3b7867a32490ae38a1eaaf2e9d Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 26 Jul 2017 20:28:53 +0600 Subject: [PATCH 063/211] host/trxcon/l1ctl.c: handle L1CTL_PARAM_REQ Change-Id: I5c23520dc0f19147b41ad2e13681bf0a62e9facd --- src/host/trxcon/l1ctl.c | 20 ++++++++++++++++++++ src/host/trxcon/trx_if.h | 2 ++ 2 files changed, 22 insertions(+) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index cbe037ad5..2c82e11a5 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -633,6 +633,24 @@ exit: return rc; } +static int l1ctl_rx_param_req(struct l1ctl_link *l1l, struct msgb *msg) +{ + struct l1ctl_par_req *par_req; + struct l1ctl_info_ul *ul; + + ul = (struct l1ctl_info_ul *) msg->l1h; + par_req = (struct l1ctl_par_req *) ul->payload; + + LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_PARAM_REQ " + "(ta=%d, tx_power=%u)\n", par_req->ta, par_req->tx_power); + + l1l->trx->ta = par_req->ta; + l1l->trx->tx_power = par_req->tx_power; + + msgb_free(msg); + return 0; +} + int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) { struct l1ctl_hdr *l1h; @@ -659,6 +677,8 @@ int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) return l1ctl_rx_dm_rel_req(l1l, msg); case L1CTL_DATA_REQ: return l1ctl_rx_data_req(l1l, msg); + case L1CTL_PARAM_REQ: + return l1ctl_rx_param_req(l1l, msg); default: LOGP(DL1C, LOGL_ERROR, "Unknown MSG: %u\n", l1h->msg_type); msgb_free(msg); diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h index 7278bc11c..f09a25621 100644 --- a/src/host/trxcon/trx_if.h +++ b/src/host/trxcon/trx_if.h @@ -31,8 +31,10 @@ struct trx_instance { uint16_t pm_arfcn_start; uint16_t pm_arfcn_stop; uint16_t band_arfcn; + uint8_t tx_power; uint8_t bsic; uint8_t tsc; + int8_t ta; /* Scheduler stuff */ struct trx_sched sched; From 10fd43e586231a3f9e1095ece1340a023aef4b19 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 27 Jul 2017 08:53:38 +0600 Subject: [PATCH 064/211] host/trxcon/l1ctl.c: reset FBSB expire timer on shutdown Change-Id: If3c8a34f0c1105c6acbfe4f9233482a31f6558de --- src/host/trxcon/l1ctl.c | 7 +++++++ src/host/trxcon/l1ctl.h | 5 ++++- src/host/trxcon/l1ctl_link.c | 8 ++++++++ src/host/trxcon/l1ctl_link.h | 3 +++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 2c82e11a5..f71807a0f 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -685,3 +685,10 @@ int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) return -EINVAL; } } + +void l1ctl_shutdown_cb(struct l1ctl_link *l1l) +{ + /* Abort FBSB expire timer */ + if (osmo_timer_pending(&l1l->fbsb_timer)) + osmo_timer_del(&l1l->fbsb_timer); +} diff --git a/src/host/trxcon/l1ctl.h b/src/host/trxcon/l1ctl.h index 6c61bfd96..e83138d6a 100644 --- a/src/host/trxcon/l1ctl.h +++ b/src/host/trxcon/l1ctl.h @@ -6,6 +6,10 @@ #include "l1ctl_link.h" #include "l1ctl_proto.h" +/* Event handlers */ +int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg); +void l1ctl_shutdown_cb(struct l1ctl_link *l1l); + int l1ctl_tx_fbsb_conf(struct l1ctl_link *l1l, uint8_t result, struct l1ctl_info_dl *dl_info, uint8_t bsic); int l1ctl_tx_ccch_mode_conf(struct l1ctl_link *l1l, uint8_t mode); @@ -13,7 +17,6 @@ int l1ctl_tx_pm_conf(struct l1ctl_link *l1l, uint16_t band_arfcn, int dbm, int last); int l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type); int l1ctl_tx_reset_ind(struct l1ctl_link *l1l, uint8_t type); -int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg); int l1ctl_tx_data_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *ind); int l1ctl_tx_rach_conf(struct l1ctl_link *l1l, uint32_t fn); diff --git a/src/host/trxcon/l1ctl_link.c b/src/host/trxcon/l1ctl_link.c index dfcbecb20..265bfeb21 100644 --- a/src/host/trxcon/l1ctl_link.c +++ b/src/host/trxcon/l1ctl_link.c @@ -251,6 +251,10 @@ int l1ctl_link_init(struct l1ctl_link **l1l, const char *sock_path) return rc; } + /* Bind shutdown handler */ + l1l_new->shutdown_cb = l1ctl_shutdown_cb; + + /* Bind connection handler */ bfd->cb = l1ctl_link_accept; bfd->when = BSC_FD_READ; bfd->data = l1l_new; @@ -281,6 +285,10 @@ void l1ctl_link_shutdown(struct l1ctl_link *l1l) LOGP(DL1C, LOGL_NOTICE, "Shutdown L1CTL link\n"); + /* Call shutdown callback */ + if (l1l->shutdown_cb != NULL) + l1l->shutdown_cb(l1l); + listen_bfd = &l1l->listen_bfd; /* Check if we have an established connection */ diff --git a/src/host/trxcon/l1ctl_link.h b/src/host/trxcon/l1ctl_link.h index f20af937b..f9d91f14d 100644 --- a/src/host/trxcon/l1ctl_link.h +++ b/src/host/trxcon/l1ctl_link.h @@ -28,6 +28,9 @@ struct l1ctl_link { /* L1CTL handlers specific */ struct osmo_timer_list fbsb_timer; uint8_t fbsb_conf_sent; + + /* Shutdown callback */ + void (*shutdown_cb)(struct l1ctl_link *l1l); }; int l1ctl_link_init(struct l1ctl_link **l1l, const char *sock_path); From b30031356cc6d4950b56bfd77060b0b1c3465a55 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 27 Jul 2017 08:57:13 +0600 Subject: [PATCH 065/211] host/trxcon: get rid of useless trxcon fsm events Both SCH_EVENT_CLCK_IND and SCH_EVENT_CLCK_LOSS were not handled, moreover there is no purpose to keep them. Change-Id: I8efac459a40f4287e3325890809991d5ef46e9b1 --- src/host/trxcon/sched_clck.c | 7 ------- src/host/trxcon/sched_trx.c | 3 +++ src/host/trxcon/trxcon.c | 6 +----- src/host/trxcon/trxcon.h | 4 ---- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/host/trxcon/sched_clck.c b/src/host/trxcon/sched_clck.c index efb3f4c9c..9c1edc5bc 100644 --- a/src/host/trxcon/sched_clck.c +++ b/src/host/trxcon/sched_clck.c @@ -47,16 +47,12 @@ static void sched_clck_tick(void *data) { struct trx_sched *sched = (struct trx_sched *) data; - struct trx_instance *trx = (struct trx_instance *) sched->data; - struct timeval tv_now, *tv_clock; int32_t elapsed; /* Check if transceiver is still alive */ if (sched->fn_counter_lost++ == TRX_LOSS_FRAMES) { LOGP(DSCH, LOGL_NOTICE, "No more clock from transceiver\n"); - - osmo_fsm_inst_dispatch(trxcon_fsm, SCH_EVENT_CLCK_LOSS, trx); sched->state = SCH_CLCK_STATE_WAIT; return; @@ -74,7 +70,6 @@ static void sched_clck_tick(void *data) LOGP(DSCH, LOGL_NOTICE, "PC clock skew: " "elapsed uS %d\n", elapsed); - osmo_fsm_inst_dispatch(trxcon_fsm, SCH_EVENT_CLCK_LOSS, trx); sched->state = SCH_CLCK_STATE_WAIT; return; @@ -122,7 +117,6 @@ static void sched_clck_correct(struct trx_sched *sched, int sched_clck_handle(struct trx_sched *sched, uint32_t fn) { - struct trx_instance *trx = (struct trx_instance *) sched->data; struct timeval tv_now, *tv_clock; int32_t elapsed, elapsed_fn; @@ -138,7 +132,6 @@ int sched_clck_handle(struct trx_sched *sched, uint32_t fn) sched_clck_correct(sched, &tv_now, fn); LOGP(DSCH, LOGL_NOTICE, "Initial clock received: fn=%u\n", fn); - osmo_fsm_inst_dispatch(trxcon_fsm, SCH_EVENT_CLCK_IND, trx); sched->state = SCH_CLCK_STATE_OK; return 0; diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index be7a480df..cc9b1ce66 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -152,6 +152,9 @@ int sched_trx_reset(struct trx_instance *trx) sched->fn_counter_proc = 0; sched->fn_counter_lost = 0; + /* Reset internal state */ + sched->state = SCH_CLCK_STATE_WAIT; + return 0; } diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index c0d5da323..f0fca16ba 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -99,8 +99,6 @@ static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi, break; case TRX_EVENT_RSP_ERROR: case TRX_EVENT_OFFLINE: - case SCH_EVENT_CLCK_IND: - case SCH_EVENT_CLCK_LOSS: case TRX_EVENT_RESET_IND: /* TODO: notify L2 & L3 about that */ break; @@ -121,9 +119,7 @@ static struct osmo_fsm_state trxcon_fsm_states[] = { GEN_MASK(L1CTL_EVENT_DISCONNECT) | GEN_MASK(TRX_EVENT_RESET_IND) | GEN_MASK(TRX_EVENT_RSP_ERROR) | - GEN_MASK(TRX_EVENT_OFFLINE) | - GEN_MASK(SCH_EVENT_CLCK_IND) | - GEN_MASK(SCH_EVENT_CLCK_LOSS)), + GEN_MASK(TRX_EVENT_OFFLINE)), .out_state_mask = GEN_MASK(TRXCON_STATE_IDLE), .name = "MANAGED", .action = trxcon_fsm_managed_action, diff --git a/src/host/trxcon/trxcon.h b/src/host/trxcon/trxcon.h index 17d34089e..1ab3a14bd 100644 --- a/src/host/trxcon/trxcon.h +++ b/src/host/trxcon/trxcon.h @@ -19,8 +19,4 @@ enum trxcon_fsm_events { TRX_EVENT_RESET_IND, TRX_EVENT_RSP_ERROR, TRX_EVENT_OFFLINE, - - /* Scheduler specific events */ - SCH_EVENT_CLCK_IND, - SCH_EVENT_CLCK_LOSS, }; From 806e528bd1d21a307d6f1959e65b80f4978a9a36 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 27 Jul 2017 09:14:20 +0600 Subject: [PATCH 066/211] host/trxcon: don't flush trx control messages on reset Change-Id: I0851f168adeb012a933c796c4180ef507b1c57ec --- src/host/trxcon/l1ctl.c | 1 - src/host/trxcon/trxcon.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index f71807a0f..436d04304 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -366,7 +366,6 @@ static int l1ctl_rx_reset_req(struct l1ctl_link *l1l, struct msgb *msg) switch (res->type) { case L1CTL_RES_T_FULL: /* TODO: implement trx_if_reset() */ - trx_if_flush_ctrl(l1l->trx); trx_if_cmd_poweroff(l1l->trx); trx_if_cmd_echo(l1l->trx); diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index f0fca16ba..c509bf1b1 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -93,8 +93,8 @@ static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi, sched_trx_reset(app_data.trx); /* TODO: implement trx_if_reset() */ - trx_if_flush_ctrl(app_data.trx); trx_if_cmd_poweroff(app_data.trx); + trx_if_cmd_echo(app_data.trx); } break; case TRX_EVENT_RSP_ERROR: From e6acd7bd075effc4906deb9b03b71a18a028da87 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 27 Jul 2017 09:21:11 +0600 Subject: [PATCH 067/211] host/trxcon: get rid of useless TRX_EVENT_RESET_IND Change-Id: I2aa4c000b37f64c351a806711b2d19bf27ef82bd --- src/host/trxcon/trx_if.c | 6 ++---- src/host/trxcon/trxcon.c | 2 -- src/host/trxcon/trxcon.h | 1 - 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index 961f46c4b..9c76c6d05 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -488,11 +488,9 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_IDLE, 0, 0); else if (!strncmp(tcm->cmd + 4, "MEASURE", 7)) trx_if_measure_rsp_cb(trx, buf + 14); - else if (!strncmp(tcm->cmd + 4, "ECHO", 4)) { + else if (!strncmp(tcm->cmd + 4, "ECHO", 4)) osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_IDLE, 0, 0); - osmo_fsm_inst_dispatch(trxcon_fsm, - TRX_EVENT_RESET_IND, trx); - } else + else osmo_fsm_inst_state_chg(trx->fsm, trx->prev_state, 0, 0); /* Remove command from list */ diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index c509bf1b1..82371bdb7 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -99,7 +99,6 @@ static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi, break; case TRX_EVENT_RSP_ERROR: case TRX_EVENT_OFFLINE: - case TRX_EVENT_RESET_IND: /* TODO: notify L2 & L3 about that */ break; default: @@ -117,7 +116,6 @@ static struct osmo_fsm_state trxcon_fsm_states[] = { [TRXCON_STATE_MANAGED] = { .in_event_mask = ( GEN_MASK(L1CTL_EVENT_DISCONNECT) | - GEN_MASK(TRX_EVENT_RESET_IND) | GEN_MASK(TRX_EVENT_RSP_ERROR) | GEN_MASK(TRX_EVENT_OFFLINE)), .out_state_mask = GEN_MASK(TRXCON_STATE_IDLE), diff --git a/src/host/trxcon/trxcon.h b/src/host/trxcon/trxcon.h index 1ab3a14bd..65b5e85d7 100644 --- a/src/host/trxcon/trxcon.h +++ b/src/host/trxcon/trxcon.h @@ -16,7 +16,6 @@ enum trxcon_fsm_events { L1CTL_EVENT_DISCONNECT, /* TRX specific events */ - TRX_EVENT_RESET_IND, TRX_EVENT_RSP_ERROR, TRX_EVENT_OFFLINE, }; From 00bb1d5a79c48da0142590e519f6452e72fb5ac7 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 27 Jul 2017 16:33:38 +0600 Subject: [PATCH 068/211] host/trxcon/scheduler: implement sched_clck_reset() It's better to have the clock management API inside a single file. Change-Id: I92772f3db404e70fdffd530779613196afec61c9 --- src/host/trxcon/sched_clck.c | 13 +++++++++++++ src/host/trxcon/sched_trx.c | 13 ++----------- src/host/trxcon/scheduler.h | 1 + 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/host/trxcon/sched_clck.c b/src/host/trxcon/sched_clck.c index 9c1edc5bc..d57d07e1c 100644 --- a/src/host/trxcon/sched_clck.c +++ b/src/host/trxcon/sched_clck.c @@ -198,3 +198,16 @@ int sched_clck_handle(struct trx_sched *sched, uint32_t fn) return 0; } + +void sched_clck_reset(struct trx_sched *sched) +{ + /* Reset internal state */ + sched->state = SCH_CLCK_STATE_WAIT; + + /* Stop clock timer */ + osmo_timer_del(&sched->clock_timer); + + /* Flush counters */ + sched->fn_counter_proc = 0; + sched->fn_counter_lost = 0; +} diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index cc9b1ce66..04fc5cf1c 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -130,7 +130,6 @@ int sched_trx_shutdown(struct trx_instance *trx) int sched_trx_reset(struct trx_instance *trx) { - struct trx_sched *sched; int i; if (!trx) @@ -144,16 +143,8 @@ int sched_trx_reset(struct trx_instance *trx) INIT_LLIST_HEAD(&trx->ts_list); - /* Obtain a scheduler instance from TRX */ - sched = &trx->sched; - - /* Reset clock counter */ - osmo_timer_del(&sched->clock_timer); - sched->fn_counter_proc = 0; - sched->fn_counter_lost = 0; - - /* Reset internal state */ - sched->state = SCH_CLCK_STATE_WAIT; + /* Stop and reset clock counter */ + sched_clck_reset(&trx->sched); return 0; } diff --git a/src/host/trxcon/scheduler.h b/src/host/trxcon/scheduler.h index 0783e40aa..b025d918c 100644 --- a/src/host/trxcon/scheduler.h +++ b/src/host/trxcon/scheduler.h @@ -35,3 +35,4 @@ struct trx_sched { }; int sched_clck_handle(struct trx_sched *sched, uint32_t fn); +void sched_clck_reset(struct trx_sched *sched); From 2001221d750282a87f98c14f10aabe4a88ef76f2 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 27 Jul 2017 16:53:09 +0600 Subject: [PATCH 069/211] host/trxcon/scheduler: optionally reset clock counter Change-Id: I4565620fc0c5f64133c2674d2c972fc34245cf32 --- src/host/trxcon/l1ctl.c | 8 ++++---- src/host/trxcon/sched_trx.c | 10 ++++++---- src/host/trxcon/sched_trx.h | 2 +- src/host/trxcon/trxcon.c | 4 ++-- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 436d04304..781076304 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -287,8 +287,8 @@ static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg) gsm_band_name(gsm_arfcn2band(band_arfcn)), band_arfcn &~ ARFCN_FLAG_MASK); - /* Reset L1 */ - sched_trx_reset(l1l->trx); + /* Reset scheduler and clock counter */ + sched_trx_reset(l1l->trx, 1); /* Configure a single timeslot */ if (fbsb->ccch_mode == CCCH_MODE_COMBINED) @@ -371,7 +371,7 @@ static int l1ctl_rx_reset_req(struct l1ctl_link *l1l, struct msgb *msg) /* Fall through */ case L1CTL_RES_T_SCHED: - sched_trx_reset(l1l->trx); + sched_trx_reset(l1l->trx, 1); break; default: LOGP(DL1C, LOGL_ERROR, "Unknown L1CTL_RESET_REQ type\n"); @@ -560,7 +560,7 @@ exit: static int l1ctl_rx_dm_rel_req(struct l1ctl_link *l1l, struct msgb *msg) { /* Reset scheduler */ - sched_trx_reset(l1l->trx); + sched_trx_reset(l1l->trx, 0); msgb_free(msg); return 0; diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 04fc5cf1c..94a8e68f8 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -128,14 +128,15 @@ int sched_trx_shutdown(struct trx_instance *trx) return 0; } -int sched_trx_reset(struct trx_instance *trx) +int sched_trx_reset(struct trx_instance *trx, int reset_clock) { int i; if (!trx) return -EINVAL; - LOGP(DSCH, LOGL_NOTICE, "Reset scheduler\n"); + LOGP(DSCH, LOGL_NOTICE, "Reset scheduler %s\n", + reset_clock ? "and clock counter" : ""); /* Free all potentially allocated timeslots */ for (i = 0; i < TRX_TS_COUNT; i++) @@ -143,8 +144,9 @@ int sched_trx_reset(struct trx_instance *trx) INIT_LLIST_HEAD(&trx->ts_list); - /* Stop and reset clock counter */ - sched_clck_reset(&trx->sched); + /* Stop and reset clock counter if required */ + if (reset_clock) + sched_clck_reset(&trx->sched); return 0; } diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index cab290b85..ff3d254ea 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -248,7 +248,7 @@ const struct trx_multiframe *sched_mframe_layout( /* Scheduler management functions */ int sched_trx_init(struct trx_instance *trx); -int sched_trx_reset(struct trx_instance *trx); +int sched_trx_reset(struct trx_instance *trx, int reset_clock); int sched_trx_shutdown(struct trx_instance *trx); /* Timeslot management functions */ diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index 82371bdb7..4a571b9ae 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -89,8 +89,8 @@ static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi, osmo_fsm_inst_state_chg(trxcon_fsm, TRXCON_STATE_IDLE, 0, 0); if (app_data.trx->fsm->state != TRX_STATE_OFFLINE) { - /* Reset scheduler */ - sched_trx_reset(app_data.trx); + /* Reset scheduler and clock counter */ + sched_trx_reset(app_data.trx, 1); /* TODO: implement trx_if_reset() */ trx_if_cmd_poweroff(app_data.trx); From 026ed14c4bc266fd7beccf8dd3335e9b91d40e26 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 26 Jul 2017 20:28:01 +0600 Subject: [PATCH 070/211] host/trxcon: adjust default verbosity level Change-Id: I91258091b59e5cdd30b767364fb48c3d67980eb7 --- src/host/trxcon/l1ctl.c | 21 ++++++++++++++------- src/host/trxcon/sched_clck.c | 6 ++++-- src/host/trxcon/sched_trx.c | 8 ++++---- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 781076304..f9c1cadef 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -283,7 +283,7 @@ static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg) band_arfcn = ntohs(fbsb->band_arfcn); timeout = ntohs(fbsb->timeout); - LOGP(DL1C, LOGL_DEBUG, "Recv FBSB Req (%s %d)\n", + LOGP(DL1C, LOGL_NOTICE, "Received FBSB request (%s %d)\n", gsm_band_name(gsm_arfcn2band(band_arfcn)), band_arfcn &~ ARFCN_FLAG_MASK); @@ -335,7 +335,8 @@ static int l1ctl_rx_pm_req(struct l1ctl_link *l1l, struct msgb *msg) arfcn_start = ntohs(pmr->range.band_arfcn_from); arfcn_stop = ntohs(pmr->range.band_arfcn_to); - LOGP(DL1C, LOGL_DEBUG, "Recv PM Req (%s: %d -> %d)\n", + LOGP(DL1C, LOGL_NOTICE, "Received power measurement " + "request (%s: %d -> %d)\n", gsm_band_name(gsm_arfcn2band(arfcn_start)), arfcn_start &~ ARFCN_FLAG_MASK, arfcn_stop &~ ARFCN_FLAG_MASK); @@ -361,7 +362,8 @@ static int l1ctl_rx_reset_req(struct l1ctl_link *l1l, struct msgb *msg) goto exit; } - LOGP(DL1C, LOGL_DEBUG, "Recv Reset Req (%u)\n", res->type); + LOGP(DL1C, LOGL_NOTICE, "Received reset request (%u)\n", + res->type); switch (res->type) { case L1CTL_RES_T_FULL: @@ -414,7 +416,9 @@ static int l1ctl_rx_ccch_mode_req(struct l1ctl_link *l1l, struct msgb *msg) goto exit; } - LOGP(DL1C, LOGL_DEBUG, "Recv CCCH Mode Req (%u)\n", req->ccch_mode); + LOGP(DL1C, LOGL_NOTICE, "Received CCCH mode request (%s)\n", + req->ccch_mode == CCCH_MODE_COMBINED ? + "combined" : "not combined"); /* Reconfigure TS0 */ mode = req->ccch_mode == CCCH_MODE_COMBINED ? @@ -445,8 +449,8 @@ static int l1ctl_rx_rach_req(struct l1ctl_link *l1l, struct msgb *msg) /* Convert offset value to host format */ req->offset = ntohs(req->offset); - LOGP(DL1C, LOGL_DEBUG, "Recv RACH Req (offset=%u ra=0x%02x)\n", - req->offset, req->ra); + LOGP(DL1C, LOGL_NOTICE, "Received RACH request " + "(offset=%u ra=0x%02x)\n", req->offset, req->ra); /* FIXME: can we use other than TS0? */ ts = sched_trx_find_ts(l1l->trx, 0); @@ -496,7 +500,7 @@ static int l1ctl_rx_dm_est_req(struct l1ctl_link *l1l, struct msgb *msg) band_arfcn = ntohs(est_req->h0.band_arfcn); chan_nr = ul->chan_nr; - LOGP(DL1C, LOGL_DEBUG, "Recv L1CTL_DM_EST_REQ (arfcn=%u, " + LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_DM_EST_REQ (arfcn=%u, " "chan_nr=0x%02x, tsc=%u)\n", (band_arfcn &~ ARFCN_FLAG_MASK), chan_nr, est_req->tsc); @@ -559,6 +563,9 @@ exit: static int l1ctl_rx_dm_rel_req(struct l1ctl_link *l1l, struct msgb *msg) { + LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_DM_REL_REQ, " + "switching back to CCCH\n"); + /* Reset scheduler */ sched_trx_reset(l1l->trx, 0); diff --git a/src/host/trxcon/sched_clck.c b/src/host/trxcon/sched_clck.c index d57d07e1c..59cffb243 100644 --- a/src/host/trxcon/sched_clck.c +++ b/src/host/trxcon/sched_clck.c @@ -52,7 +52,7 @@ static void sched_clck_tick(void *data) /* Check if transceiver is still alive */ if (sched->fn_counter_lost++ == TRX_LOSS_FRAMES) { - LOGP(DSCH, LOGL_NOTICE, "No more clock from transceiver\n"); + LOGP(DSCH, LOGL_DEBUG, "No more clock from transceiver\n"); sched->state = SCH_CLCK_STATE_WAIT; return; @@ -131,12 +131,14 @@ int sched_clck_handle(struct trx_sched *sched, uint32_t fn) if (sched->state == SCH_CLCK_STATE_WAIT) { sched_clck_correct(sched, &tv_now, fn); - LOGP(DSCH, LOGL_NOTICE, "Initial clock received: fn=%u\n", fn); + LOGP(DSCH, LOGL_DEBUG, "Initial clock received: fn=%u\n", fn); sched->state = SCH_CLCK_STATE_OK; return 0; } + LOGP(DSCH, LOGL_NOTICE, "Clock indication: fn=%u\n", fn); + osmo_timer_del(&sched->clock_timer); /* Calculate elapsed time / frames since last processed fn */ diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 94a8e68f8..7ac649780 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -155,7 +155,7 @@ struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int ts_num) { struct trx_ts *ts; - LOGP(DSCH, LOGL_INFO, "Add a new TDMA timeslot #%u\n", ts_num); + LOGP(DSCH, LOGL_NOTICE, "Add a new TDMA timeslot #%u\n", ts_num); ts = talloc_zero(trx, struct trx_ts); if (!ts) @@ -190,7 +190,7 @@ void sched_trx_del_ts(struct trx_instance *trx, int ts_num) if (ts == NULL) return; - LOGP(DSCH, LOGL_INFO, "Delete TDMA timeslot #%u\n", ts_num); + LOGP(DSCH, LOGL_NOTICE, "Delete TDMA timeslot #%u\n", ts_num); /* Flush queue primitives for TX */ msgb_queue_flush(&ts->tx_prims); @@ -232,7 +232,7 @@ int sched_trx_configure_ts(struct trx_instance *trx, int ts_num, if (ts->mf_layout->chan_config != config) return -EINVAL; - LOGP(DSCH, LOGL_INFO, "(Re)configure TDMA timeslot #%u as %s\n", + LOGP(DSCH, LOGL_NOTICE, "(Re)configure TDMA timeslot #%u as %s\n", ts_num, ts->mf_layout->name); /* Count channel states */ @@ -431,7 +431,7 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t ts_num, /* Check whether required timeslot is enabled / configured */ ts = sched_trx_find_ts(trx, ts_num); if (ts == NULL) { - LOGP(DSCH, LOGL_ERROR, "TDMA timeslot #%u isn't configured, " + LOGP(DSCH, LOGL_DEBUG, "TDMA timeslot #%u isn't configured, " "ignoring burst...\n", ts_num); return -EINVAL; } From d497bc82880e25adc988a13898c42b3f854aafdc Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 28 Jul 2017 13:43:35 +0600 Subject: [PATCH 071/211] fake_trx: add copyright message to clck_gen.py and ctrl_cmd.py Change-Id: Ia79279dd9e85d131d66d790f1f3fd64fb1914f58 --- src/target/fake_trx/clck_gen.py | 10 ++++++++++ src/target/fake_trx/ctrl_cmd.py | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/target/fake_trx/clck_gen.py b/src/target/fake_trx/clck_gen.py index f1c1f9835..85dc00e03 100755 --- a/src/target/fake_trx/clck_gen.py +++ b/src/target/fake_trx/clck_gen.py @@ -29,6 +29,13 @@ import sys from threading import Timer from udp_link import UDPLink +COPYRIGHT = \ + "Copyright (C) 2017 by Vadim Yanitskiy \n" \ + "License GPLv2+: GNU GPL version 2 or later " \ + "\n" \ + "This is free software: you are free to change and redistribute it.\n" \ + "There is NO WARRANTY, to the extent permitted by law.\n" + class CLCKGen: # GSM TDMA definitions SEC_DELAY_US = 1000 * 1000 @@ -88,6 +95,9 @@ class Application: # Set up signal handlers signal.signal(signal.SIGINT, self.sig_handler) + # Print copyright + print(COPYRIGHT) + def run(self): self.link = UDPLink("127.0.0.1", 5800, 5700) self.clck = CLCKGen([self.link], ind_period = 51) diff --git a/src/target/fake_trx/ctrl_cmd.py b/src/target/fake_trx/ctrl_cmd.py index e362408fe..30b19bdc6 100755 --- a/src/target/fake_trx/ctrl_cmd.py +++ b/src/target/fake_trx/ctrl_cmd.py @@ -28,6 +28,13 @@ import sys from udp_link import UDPLink +COPYRIGHT = \ + "Copyright (C) 2017 by Vadim Yanitskiy \n" \ + "License GPLv2+: GNU GPL version 2 or later " \ + "\n" \ + "This is free software: you are free to change and redistribute it.\n" \ + "There is NO WARRANTY, to the extent permitted by law.\n" + class Application: def __init__(self, remote_addr, remote_port, bind_port, fuz = False): # Init UDP connection @@ -39,6 +46,9 @@ class Application: # Set up signal handlers signal.signal(signal.SIGINT, self.sig_handler) + # Print copyright + print(COPYRIGHT) + def run(self): while True: self.print_prompt() From 539e9a1f18fcf8545a0d2f0646a928f99933622a Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 28 Jul 2017 13:56:09 +0600 Subject: [PATCH 072/211] fake_trx: add a new tool for burst generation This change introduces a new tool named 'burst_gen.py'. One can be used for sending GSM bursts either to L1 (OsmoBTS or OsmocomBB) or to TRX (OsmoTRX and GR-GSM TRX). Currently it is only possible to send random bursts of different types: NB, FB, SB, AB. Change-Id: Ie14281222d29536b8690517e57af2a1007fcbc91 --- src/target/fake_trx/README | 5 + src/target/fake_trx/burst_gen.py | 246 ++++++++++++++++++++++++++ src/target/fake_trx/rand_burst_gen.py | 177 ++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100755 src/target/fake_trx/burst_gen.py create mode 100644 src/target/fake_trx/rand_burst_gen.py diff --git a/src/target/fake_trx/README b/src/target/fake_trx/README index ac138e099..6b1c41344 100644 --- a/src/target/fake_trx/README +++ b/src/target/fake_trx/README @@ -22,3 +22,8 @@ Brief description of available applications: - ctrl_cmd.py - another peripheral tool, which could be used for sending CTRL commands directly in manual mode, and also for application fuzzing. + + - burst_gen.py - a tool for sending GSM bursts either to L1 + (OsmoBTS or OsmocomBB) or to TRX (OsmoTRX and GR-GSM TRX). + Currently it is only possible to send random bursts of + different types: NB, FB, SB, AB. diff --git a/src/target/fake_trx/burst_gen.py b/src/target/fake_trx/burst_gen.py new file mode 100755 index 000000000..195158a49 --- /dev/null +++ b/src/target/fake_trx/burst_gen.py @@ -0,0 +1,246 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# Simple tool to send custom messages via TRX DATA interface, +# which may be also useful for fuzzing and testing +# +# (C) 2017 by Vadim Yanitskiy +# +# 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. + +import random +import signal +import getopt +import select +import sys + +from udp_link import UDPLink +from rand_burst_gen import RandBurstGen + +COPYRIGHT = \ + "Copyright (C) 2017 by Vadim Yanitskiy \n" \ + "License GPLv2+: GNU GPL version 2 or later " \ + "\n" \ + "This is free software: you are free to change and redistribute it.\n" \ + "There is NO WARRANTY, to the extent permitted by law.\n" + +class DATAInterface(UDPLink): + # GSM PHY definitions + GSM_HYPERFRAME = 2048 * 26 * 51 + + def send_l1_msg(self, burst, + tn = None, fn = None, rssi = None): + # Generate random timeslot index if not preset + if tn is None: + tn = random.randint(0, 7) + + # Generate random frame number if not preset + if fn is None: + fn = random.randint(0, self.GSM_HYPERFRAME) + + # Generate random RSSI if not preset + if rssi is None: + rssi = -random.randint(-75, -50) + + # Prepare a buffer for header and burst + buf = [] + + # Put timeslot index + buf.append(tn) + + # Put frame number + buf.append((fn >> 24) & 0xff) + buf.append((fn >> 16) & 0xff) + buf.append((fn >> 8) & 0xff) + buf.append((fn >> 0) & 0xff) + + # Put RSSI + buf.append(rssi) + + # HACK: put fake TOA value + buf += [0x00] * 2 + + # Put burst + buf += burst + + # Put two unused bytes + buf += [0x00] * 2 + + # Send message + self.send(bytearray(buf)) + + def send_trx_msg(self, burst, + tn = None, fn = None, pwr = None): + # Generate random timeslot index if not preset + if tn is None: + tn = random.randint(0, 7) + + # Generate random frame number if not preset + if fn is None: + fn = random.randint(0, self.GSM_HYPERFRAME) + + # Generate random power level if not preset + if pwr is None: + pwr = random.randint(0, 34) + + # Prepare a buffer for header and burst + buf = [] + + # Put timeslot index + buf.append(tn) + + # Put frame number + buf.append((fn >> 24) & 0xff) + buf.append((fn >> 16) & 0xff) + buf.append((fn >> 8) & 0xff) + buf.append((fn >> 0) & 0xff) + + # Put transmit power level + buf.append(pwr) + + # Put burst + buf += burst + + # Send message + self.send(bytearray(buf)) + +class Application: + # Application variables + remote_addr = "127.0.0.1" + base_port = 5700 + conn_mode = "TRX" + + burst_type = None + burst_count = 1 + + def __init__(self): + self.print_copyright() + self.parse_argv() + + # Set up signal handlers + signal.signal(signal.SIGINT, self.sig_handler) + + def run(self): + # Init DATA interface with TRX or L1 + if self.conn_mode == "TRX": + self.data_if = DATAInterface(self.remote_addr, + self.base_port + 2, self.base_port + 102) + elif self.conn_mode == "L1": + self.data_if = DATAInterface(self.remote_addr, + self.base_port + 102, self.base_port + 2) + else: + self.print_help("[!] Unknown connection type") + sys.exit(2) + + # Init random burst generator + self.gen = RandBurstGen() + + # Send as much bursts as required + for i in range(self.burst_count): + # Generate a random burst + if self.burst_type == "NB": + buf = self.gen.gen_nb() + elif self.burst_type == "FB": + buf = self.gen.gen_fb() + elif self.burst_type == "SB": + buf = self.gen.gen_sb() + elif self.burst_type == "AB": + buf = self.gen.gen_ab() + else: + self.print_help("[!] Unknown burst type") + self.shutdown() + sys.exit(2) + + print("[i] Sending %d/%d %s burst to %s..." + % (i + 1, self.burst_count, self.burst_type, + self.conn_mode)) + + # Send to TRX or L1 + if self.conn_mode == "TRX": + self.data_if.send_trx_msg(buf) + elif self.conn_mode == "L1": + self.data_if.send_l1_msg(buf) + + self.shutdown() + + def print_copyright(self): + print(COPYRIGHT) + + def print_help(self, msg = None): + s = " Usage: " + sys.argv[0] + " [options]\n\n" \ + " Some help...\n" \ + " -h --help this text\n\n" + + s += " TRX interface specific\n" \ + " -m --conn-mode Send bursts to: TRX (default) / L1\n" \ + " -r --remote-addr Set remote address (default %s)\n" \ + " -p --base-port Set base port number (default %d)\n\n" + + s += " Burst generation\n" \ + " -b --burst-type Random burst type (NB, FB, SB, AB)\n" \ + " -c --burst-count How much bursts to send (default 1)\n" \ + + print(s % (self.remote_addr, self.base_port)) + + if msg is not None: + print(msg) + + def parse_argv(self): + try: + opts, args = getopt.getopt(sys.argv[1:], + "m:r:p:b:c:h", + [ + "help", + "conn-mode=", + "remote-addr=", + "base-port=", + "burst-type=", + "burst-count=", + ]) + except getopt.GetoptError as err: + self.print_help("[!] " + str(err)) + sys.exit(2) + + for o, v in opts: + if o in ("-h", "--help"): + self.print_help() + sys.exit(2) + + elif o in ("-m", "--conn-mode"): + self.conn_mode = v + elif o in ("-r", "--remote-addr"): + self.remote_addr = v + elif o in ("-p", "--base-port"): + self.base_port = int(v) + + elif o in ("-b", "--burst-type"): + self.burst_type = v + elif o in ("-c", "--burst-count"): + self.burst_count = int(v) + + def shutdown(self): + self.data_if.shutdown() + + def sig_handler(self, signum, frame): + print("Signal %d received" % signum) + if signum is signal.SIGINT: + self.shutdown() + sys.exit(0) + +if __name__ == '__main__': + app = Application() + app.run() \ No newline at end of file diff --git a/src/target/fake_trx/rand_burst_gen.py b/src/target/fake_trx/rand_burst_gen.py new file mode 100644 index 000000000..4475e5953 --- /dev/null +++ b/src/target/fake_trx/rand_burst_gen.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# Virtual Um-interface (fake transceiver) +# Random burst (NB, FB, SB, AB) generator +# +# (C) 2017 by Vadim Yanitskiy +# +# 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. + +import random + +class RandBurstGen: + # GSM L1 definitions + GSM_BURST_LEN = 148 + + # GSM 05.02 Chapter 5.2.3 Normal Burst + nb_tsc_list = [ + [ + 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, + 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, + ], + [ + 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, + 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, + ], + [ + 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, + 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, + ], + [ + 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, + 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, + ], + [ + 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, + 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, + ], + [ + 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, + 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, + ], + [ + 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, + 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, + ], + [ + 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, + 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, + ], + ] + + # GSM 05.02 Chapter 5.2.5 SCH training sequence + sb_tsc = [ + 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, + 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, + ] + + # GSM 05.02 Chapter 5.2.6 Dummy Burst + db_bits = [ + 0, 0, 0, + 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, + 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, + 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, + 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, + 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, + 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, + 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, + 0, 0, 0, + ] + + # GSM 05.02 Chapter 5.2.7 Access burst + ab_tsc = [ + 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, + 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, + ] + + # Generate a normal burst + def gen_nb(self, seq_idx = 0): + buf = [] + + # Tailing bits + buf += [0] * 3 + + # Random data 1 / 2 + for i in range(0, 57): + buf.append(random.randint(0, 1)) + + # Steal flag 1 / 2 + buf.append(random.randint(0, 1)) + + # Training sequence + buf += self.nb_tsc_list[seq_idx] + + # Steal flag 2 / 2 + buf.append(random.randint(0, 1)) + + # Random data 2 / 2 + for i in range(0, 57): + buf.append(random.randint(0, 1)) + + # Tailing bits + buf += [0] * 3 + + return buf + + # Generate a frequency correction burst + def gen_fb(self): + return [0] * self.GSM_BURST_LEN + + # Generate a synchronization burst + def gen_sb(self): + buf = [] + + # Tailing bits + buf += [0] * 3 + + # Random data 1 / 2 + for i in range(0, 39): + buf.append(random.randint(0, 1)) + + # Training sequence + buf += self.sb_tsc + + # Random data 2 / 2 + for i in range(0, 39): + buf.append(random.randint(0, 1)) + + # Tailing bits + buf += [0] * 3 + + return buf + + # Generate a dummy burst + def gen_db(self): + return self.db_bits + + # Generate an access burst + def gen_ab(self): + buf = [] + + # Tailing bits + buf += [0] * 8 + + # Training sequence + buf += self.ab_tsc + + # Random data + for i in range(0, 36): + buf.append(random.randint(0, 1)) + + # Tailing bits + buf += [0] * 3 + + # Guard period + buf += [0] * 60 + + return buf From c045bc4fbefc2dd1bd9e45cd21ebd50740700c3e Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 28 Jul 2017 14:47:41 +0600 Subject: [PATCH 073/211] host/trxcon/scheduler: simplify timeslot management As we know the count of timeslots per GSM TDMA frame, it would be better to have an array of pointers to trx_ts instances instead of linux list, which is more usable for lists with unknown length. Change-Id: I9510a5cddde22950ceb8422e0990d59f05ed4d60 --- src/host/trxcon/sched_trx.c | 105 +++++++++++++++++------------------- src/host/trxcon/sched_trx.h | 2 - src/host/trxcon/trx_if.h | 2 +- 3 files changed, 51 insertions(+), 58 deletions(-) diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 7ac649780..3e6158253 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -55,13 +55,20 @@ static void sched_frame_clck_cb(struct trx_sched *sched) uint8_t offset, bid; struct trx_ts *ts; uint32_t fn; + int i; - /* If we have no active timeslots, nothing to do */ - if (llist_empty(&trx->ts_list)) - return; + /* Iterate over timeslot list */ + for (i = 0; i < TRX_TS_COUNT; i++) { + /* Timeslot is not allocated */ + ts = trx->ts_list[i]; + if (ts == NULL) + continue; - /* For each allocated timeslot */ - llist_for_each_entry(ts, &trx->ts_list, list) { + /* Timeslot is not configured */ + if (ts->mf_layout == NULL) + continue; + + /* There is nothing to send */ if (llist_empty(&ts->tx_prims)) continue; @@ -107,8 +114,6 @@ int sched_trx_init(struct trx_instance *trx) sched = &trx->sched; sched->data = trx; - INIT_LLIST_HEAD(&trx->ts_list); - return 0; } @@ -142,8 +147,6 @@ int sched_trx_reset(struct trx_instance *trx, int reset_clock) for (i = 0; i < TRX_TS_COUNT; i++) sched_trx_del_ts(trx, i); - INIT_LLIST_HEAD(&trx->ts_list); - /* Stop and reset clock counter if required */ if (reset_clock) sched_clck_reset(&trx->sched); @@ -151,89 +154,81 @@ int sched_trx_reset(struct trx_instance *trx, int reset_clock) return 0; } -struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int ts_num) +struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int tn) { - struct trx_ts *ts; - - LOGP(DSCH, LOGL_NOTICE, "Add a new TDMA timeslot #%u\n", ts_num); - - ts = talloc_zero(trx, struct trx_ts); - if (!ts) + /* Make sure that ts isn't allocated yet */ + if (trx->ts_list[tn] != NULL) { + LOGP(DSCH, LOGL_ERROR, "Timeslot #%u already allocated\n", tn); return NULL; - - llist_add_tail(&ts->list, &trx->ts_list); - - return ts; -} - -struct trx_ts *sched_trx_find_ts(struct trx_instance *trx, int ts_num) -{ - struct trx_ts *ts; - - if (llist_empty(&trx->ts_list)) - return NULL; - - llist_for_each_entry(ts, &trx->ts_list, list) { - if (ts->index == ts_num) - return ts; } - return NULL; + LOGP(DSCH, LOGL_NOTICE, "Add a new TDMA timeslot #%u\n", tn); + + /* Allocate a new one */ + trx->ts_list[tn] = talloc_zero(trx, struct trx_ts); + + /* Assign TS index */ + trx->ts_list[tn]->index = tn; + + return trx->ts_list[tn]; } -void sched_trx_del_ts(struct trx_instance *trx, int ts_num) +/* FIXME: one kept here for compatibility reasons */ +struct trx_ts *sched_trx_find_ts(struct trx_instance *trx, int tn) +{ + return trx->ts_list[tn]; +} + +void sched_trx_del_ts(struct trx_instance *trx, int tn) { struct trx_ts *ts; /* Find ts in list */ - ts = sched_trx_find_ts(trx, ts_num); + ts = trx->ts_list[tn]; if (ts == NULL) return; - LOGP(DSCH, LOGL_NOTICE, "Delete TDMA timeslot #%u\n", ts_num); + LOGP(DSCH, LOGL_NOTICE, "Delete TDMA timeslot #%u\n", tn); /* Flush queue primitives for TX */ msgb_queue_flush(&ts->tx_prims); - /* Remove ts from list */ - llist_del(&ts->list); + /* Remove ts from list and free memory */ + trx->ts_list[tn] = NULL; talloc_free(ts); /* Notify transceiver about that */ - trx_if_cmd_setslot(trx, ts_num, 0); + trx_if_cmd_setslot(trx, tn, 0); } -int sched_trx_configure_ts(struct trx_instance *trx, int ts_num, +int sched_trx_configure_ts(struct trx_instance *trx, int tn, enum gsm_phys_chan_config config) { int i, type, lchan_cnt = 0; struct trx_ts *ts; /* Try to find specified ts */ - ts = sched_trx_find_ts(trx, ts_num); + ts = trx->ts_list[tn]; if (ts != NULL) { /* Reconfiguration of existing one */ - sched_trx_reset_ts(trx, ts_num); + sched_trx_reset_ts(trx, tn); } else { /* Allocate a new one if doesn't exist */ - ts = sched_trx_add_ts(trx, ts_num); + ts = sched_trx_add_ts(trx, tn); if (ts == NULL) return -ENOMEM; - - /* Assign TS index */ - ts->index = ts_num; } /* Init queue primitives for TX */ INIT_LLIST_HEAD(&ts->tx_prims); /* Choose proper multiframe layout */ - ts->mf_layout = sched_mframe_layout(config, ts_num); + ts->mf_layout = sched_mframe_layout(config, tn); if (ts->mf_layout->chan_config != config) return -EINVAL; LOGP(DSCH, LOGL_NOTICE, "(Re)configure TDMA timeslot #%u as %s\n", - ts_num, ts->mf_layout->name); + tn, ts->mf_layout->name); /* Count channel states */ for (type = 0; type < _TRX_CHAN_MAX; type++) @@ -262,17 +257,17 @@ int sched_trx_configure_ts(struct trx_instance *trx, int ts_num, /* Notify transceiver about TS activation */ /* FIXME: set proper channel type */ - trx_if_cmd_setslot(trx, ts_num, 1); + trx_if_cmd_setslot(trx, tn, 1); return 0; } -int sched_trx_reset_ts(struct trx_instance *trx, int ts_num) +int sched_trx_reset_ts(struct trx_instance *trx, int tn) { struct trx_ts *ts; /* Try to find specified ts */ - ts = sched_trx_find_ts(trx, ts_num); + ts = trx->ts_list[tn]; if (ts == NULL) return -EINVAL; @@ -289,7 +284,7 @@ int sched_trx_reset_ts(struct trx_instance *trx, int ts_num) talloc_free(ts->lchans); /* Notify transceiver about that */ - trx_if_cmd_setslot(trx, ts_num, 0); + trx_if_cmd_setslot(trx, tn, 0); return 0; } @@ -416,7 +411,7 @@ enum trx_lchan_type sched_trx_chan_nr2lchan_type(uint8_t chan_nr) return TRXC_IDLE; } -int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t ts_num, +int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, uint32_t burst_fn, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa) { struct trx_lchan_state *lchan; @@ -429,10 +424,10 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t ts_num, uint8_t offset, bid; /* Check whether required timeslot is enabled / configured */ - ts = sched_trx_find_ts(trx, ts_num); + ts = sched_trx_find_ts(trx, tn); if (ts == NULL) { LOGP(DSCH, LOGL_DEBUG, "TDMA timeslot #%u isn't configured, " - "ignoring burst...\n", ts_num); + "ignoring burst...\n", tn); return -EINVAL; } diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index ff3d254ea..42953b3da 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -228,8 +228,6 @@ struct trx_ts { struct trx_lchan_state *lchans; /*! \brief Queue primitives for TX */ struct llist_head tx_prims; - /*! \brief Link to parent list */ - struct llist_head list; }; /* Represents one TX primitive in the queue of trx_ts */ diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h index f09a25621..fbfa8b5e6 100644 --- a/src/host/trxcon/trx_if.h +++ b/src/host/trxcon/trx_if.h @@ -38,7 +38,7 @@ struct trx_instance { /* Scheduler stuff */ struct trx_sched sched; - struct llist_head ts_list; + struct trx_ts *ts_list[TRX_TS_COUNT]; /* Bind L1CTL link */ struct l1ctl_link *l1l; From 6aa6690277e4a1551ed6c6b8482e513253d6865b Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 28 Jul 2017 15:00:40 +0600 Subject: [PATCH 074/211] host/trxcon/scheduler: fix possible NULL deference We should make sure that required timeslot is not only allocated, but also configured, i.e. has a correct multiframe layout. Change-Id: I1d0b870c389802b51c709d089b80ac3fb3565fa8 --- src/host/trxcon/sched_trx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 3e6158253..cf05bb66b 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -423,9 +423,9 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn, elapsed; uint8_t offset, bid; - /* Check whether required timeslot is enabled / configured */ + /* Check whether required timeslot is allocated and configured */ ts = sched_trx_find_ts(trx, tn); - if (ts == NULL) { + if (ts == NULL || ts->mf_layout == NULL) { LOGP(DSCH, LOGL_DEBUG, "TDMA timeslot #%u isn't configured, " "ignoring burst...\n", tn); return -EINVAL; From 075c1ad738d66e536f93977969c432b709f4c350 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 28 Jul 2017 15:15:05 +0600 Subject: [PATCH 075/211] host/trxcon/scheduler: git rid of sched_trx_find_ts() After simplification of timeslot management API this function does not make sense. Change-Id: I2fc0c68d784c8f01e1452bc46f8e1eaac2917656 --- src/host/trxcon/l1ctl.c | 14 ++++++-------- src/host/trxcon/sched_trx.c | 8 +------- src/host/trxcon/sched_trx.h | 1 - 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index f9c1cadef..b6bc404ad 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -453,7 +453,7 @@ static int l1ctl_rx_rach_req(struct l1ctl_link *l1l, struct msgb *msg) "(offset=%u ra=0x%02x)\n", req->offset, req->ra); /* FIXME: can we use other than TS0? */ - ts = sched_trx_find_ts(l1l->trx, 0); + ts = l1l->trx->ts_list[0]; if (ts == NULL) { LOGP(DL1C, LOGL_DEBUG, "Couldn't send RACH: " "TS0 is not active\n"); @@ -539,14 +539,12 @@ static int l1ctl_rx_dm_est_req(struct l1ctl_link *l1l, struct msgb *msg) /* Configure requested TS */ rc = sched_trx_configure_ts(l1l->trx, tn, config); + ts = l1l->trx->ts_list[tn]; if (rc) { rc = -EINVAL; goto exit; } - /* Find just configured TS */ - ts = sched_trx_find_ts(l1l->trx, tn); - /* Activate only requested lchan, disabling others */ sched_trx_deactivate_all_lchans(ts); rc = sched_trx_activate_lchan(ts, lchan_type); @@ -606,10 +604,10 @@ static int l1ctl_rx_data_req(struct l1ctl_link *l1l, struct msgb *msg) goto exit; } - /* Attempt to find required TS */ - ts = sched_trx_find_ts(l1l->trx, tn); - if (ts == NULL) { - LOGP(DL1C, LOGL_DEBUG, "Couldn't find required TS\n"); + /* Check whether required timeslot is allocated and configured */ + ts = l1l->trx->ts_list[tn]; + if (ts == NULL || ts->mf_layout == NULL) { + LOGP(DL1C, LOGL_ERROR, "Timeslot %u isn't configured\n", tn); rc = -EINVAL; goto exit; } diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index cf05bb66b..b5f1abca9 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -173,12 +173,6 @@ struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int tn) return trx->ts_list[tn]; } -/* FIXME: one kept here for compatibility reasons */ -struct trx_ts *sched_trx_find_ts(struct trx_instance *trx, int tn) -{ - return trx->ts_list[tn]; -} - void sched_trx_del_ts(struct trx_instance *trx, int tn) { struct trx_ts *ts; @@ -424,7 +418,7 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, uint8_t offset, bid; /* Check whether required timeslot is allocated and configured */ - ts = sched_trx_find_ts(trx, tn); + ts = trx->ts_list[tn]; if (ts == NULL || ts->mf_layout == NULL) { LOGP(DSCH, LOGL_DEBUG, "TDMA timeslot #%u isn't configured, " "ignoring burst...\n", tn); diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 42953b3da..ad2c569dc 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -251,7 +251,6 @@ int sched_trx_shutdown(struct trx_instance *trx); /* Timeslot management functions */ struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int ts_num); -struct trx_ts *sched_trx_find_ts(struct trx_instance *trx, int ts_num); void sched_trx_del_ts(struct trx_instance *trx, int ts_num); int sched_trx_reset_ts(struct trx_instance *trx, int ts_num); int sched_trx_configure_ts(struct trx_instance *trx, int ts_num, From b4dd3ac9bbf0a4e8b1feb4e648dfad9977d37a79 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 28 Jul 2017 15:36:44 +0600 Subject: [PATCH 076/211] host/trxcon/scheduler: use 'tn' instead of 'ts_num' The new timeslot index designation is more generic for Osmocom projects, so let's use one. Change-Id: I8c0118aad439816148490e57938d7e32b6e20877 --- src/host/trxcon/sched_mframe.c | 4 ++-- src/host/trxcon/sched_trx.h | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/host/trxcon/sched_mframe.c b/src/host/trxcon/sched_mframe.c index 5f9d78fd3..25e7c29d6 100644 --- a/src/host/trxcon/sched_mframe.c +++ b/src/host/trxcon/sched_mframe.c @@ -1800,12 +1800,12 @@ static const struct trx_multiframe layouts[] = { }; const struct trx_multiframe *sched_mframe_layout( - enum gsm_phys_chan_config config, int ts_num) + enum gsm_phys_chan_config config, int tn) { int i, ts_allowed; for (i = 0; i < ARRAY_SIZE(layouts); i++) { - ts_allowed = layouts[i].slotmask & (0x01 << ts_num); + ts_allowed = layouts[i].slotmask & (0x01 << tn); if (layouts[i].chan_config == config && ts_allowed) return &layouts[i]; } diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index ad2c569dc..e733de8f1 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -242,7 +242,7 @@ struct trx_ts_prim { extern const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX]; const struct trx_multiframe *sched_mframe_layout( - enum gsm_phys_chan_config config, int ts_num); + enum gsm_phys_chan_config config, int tn); /* Scheduler management functions */ int sched_trx_init(struct trx_instance *trx); @@ -250,10 +250,10 @@ int sched_trx_reset(struct trx_instance *trx, int reset_clock); int sched_trx_shutdown(struct trx_instance *trx); /* Timeslot management functions */ -struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int ts_num); -void sched_trx_del_ts(struct trx_instance *trx, int ts_num); -int sched_trx_reset_ts(struct trx_instance *trx, int ts_num); -int sched_trx_configure_ts(struct trx_instance *trx, int ts_num, +struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int tn); +void sched_trx_del_ts(struct trx_instance *trx, int tn); +int sched_trx_reset_ts(struct trx_instance *trx, int tn); +int sched_trx_configure_ts(struct trx_instance *trx, int tn, enum gsm_phys_chan_config config); /* Logical channel management functions */ @@ -265,5 +265,5 @@ int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan); struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts, enum trx_lchan_type chan); -int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t ts_num, +int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, uint32_t burst_fn, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); From 56296999963dba3a311bc5e3afcb0dd4f5ee97e5 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 28 Jul 2017 15:53:59 +0600 Subject: [PATCH 077/211] host/trxcon/scheduler: drop a meaningless FIXME label Change-Id: If5497f4fdce22e986f46725cc1575a1e809ccdab --- src/host/trxcon/sched_trx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index b5f1abca9..76112f1a4 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -454,7 +454,7 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, /* Find required channel state */ lchan = sched_trx_find_lchan(ts, chan); - if (lchan == NULL) /* FIXME: what should we do here? */ + if (lchan == NULL) goto next_frame; /* Ensure that channel is active */ From 18bc7d5e064e249d8ab78226029fcf6af0aa8838 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 28 Jul 2017 16:03:05 +0600 Subject: [PATCH 078/211] host/trxcon/scheduler: ignore incomplete sets of bursts To be able to decode one xCCH message, it's required to have all set of bursts collected (4/4). Otherwise we should not even try to decode an incomplete set. Change-Id: Iaa63462efe19b8e96102fc8c8d8c968a2df2c70e --- src/host/trxcon/sched_lchan_xcch.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index 12bb6ee53..81bd30502 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -149,13 +149,7 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, ts->mf_layout->period, trx_lchan_desc[chan].name); - /* We require first burst to have correct FN */ - if (!(*mask & 0x1)) { - *mask = 0x0; - return 0; - } - - /* FIXME: return from here? */ + return -1; } /* Attempt to decode */ From 58c7faf5eb5d08180aa8ccf9597d9b38a330e034 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 29 Jul 2017 23:43:52 +0600 Subject: [PATCH 079/211] host/trxcon/scheduler: fix chan_nr parsing Previously, the chan_nr, received from L2, was parsed in a wrong way, so in some cases only one logical channel was activated or some messages (such as Measurement Requests) were sent on incorrect channel (e.g. on SDCCH instead of SACCH). This change reimplements the sched_trx_chan_nr2lchan_type(), and introduces a new function sched_trx_set_lchans(), whics will parse chan_nr and (de)activate required channels. Change-Id: I480311c65ef93bbc1644ec708dd2a68fd33091e0 --- src/host/trxcon/l1ctl.c | 32 ++++++++++----------- src/host/trxcon/sched_trx.c | 56 ++++++++++++++++++++++++++++++------- src/host/trxcon/sched_trx.h | 5 +++- 3 files changed, 65 insertions(+), 28 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index b6bc404ad..ed30205f5 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -486,7 +486,6 @@ exit: static int l1ctl_rx_dm_est_req(struct l1ctl_link *l1l, struct msgb *msg) { enum gsm_phys_chan_config config; - enum trx_lchan_type lchan_type; struct l1ctl_dm_est_req *est_req; struct l1ctl_info_ul *ul; struct trx_ts *ts; @@ -529,14 +528,6 @@ static int l1ctl_rx_dm_est_req(struct l1ctl_link *l1l, struct msgb *msg) goto exit; } - /* Determine lchan type */ - lchan_type = sched_trx_chan_nr2lchan_type(chan_nr); - if (!lchan_type) { - LOGP(DL1C, LOGL_ERROR, "Couldn't determine lchan type\n"); - rc = -EINVAL; - goto exit; - } - /* Configure requested TS */ rc = sched_trx_configure_ts(l1l->trx, tn, config); ts = l1l->trx->ts_list[tn]; @@ -545,11 +536,13 @@ static int l1ctl_rx_dm_est_req(struct l1ctl_link *l1l, struct msgb *msg) goto exit; } - /* Activate only requested lchan, disabling others */ + /* Deactivate all lchans */ sched_trx_deactivate_all_lchans(ts); - rc = sched_trx_activate_lchan(ts, lchan_type); + + /* Activate only requested lchans */ + rc = sched_trx_set_lchans(ts, chan_nr, 1); if (rc) { - LOGP(DL1C, LOGL_ERROR, "Couldn't activate lchan\n"); + LOGP(DL1C, LOGL_ERROR, "Couldn't activate requested lchans\n"); rc = -EINVAL; goto exit; } @@ -578,15 +571,19 @@ static int l1ctl_rx_data_req(struct l1ctl_link *l1l, struct msgb *msg) struct l1ctl_info_ul *ul; struct l1ctl_data_ind *data_ind; enum trx_lchan_type lchan_type; - uint8_t chan_nr, tn; + uint8_t chan_nr, link_id, tn; size_t len; int rc = 0; ul = (struct l1ctl_info_ul *) msg->l1h; data_ind = (struct l1ctl_data_ind *) ul->payload; - chan_nr = ul->chan_nr; - LOGP(DL1C, LOGL_DEBUG, "Recv Data Req (chan_nr=0x%02x)\n", chan_nr); + /* Obtain channel description */ + chan_nr = ul->chan_nr; + link_id = ul->link_id & 0x40; + + LOGP(DL1C, LOGL_DEBUG, "Recv Data Req (chan_nr=0x%02x, " + "link_id=0x%02x)\n", chan_nr, link_id); /* Determine TS index */ tn = chan_nr & 0x7; @@ -597,9 +594,10 @@ static int l1ctl_rx_data_req(struct l1ctl_link *l1l, struct msgb *msg) } /* Determine lchan type */ - lchan_type = sched_trx_chan_nr2lchan_type(chan_nr); + lchan_type = sched_trx_chan_nr2lchan_type(chan_nr, link_id); if (!lchan_type) { - LOGP(DL1C, LOGL_ERROR, "Couldn't determine lchan type\n"); + LOGP(DL1C, LOGL_ERROR, "Couldn't determine lchan type " + "for chan_nr=%02x and link_id=%02x\n", chan_nr, link_id); rc = -EINVAL; goto exit; } diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 76112f1a4..40d144659 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -296,6 +296,35 @@ struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts, return NULL; } +int sched_trx_set_lchans(struct trx_ts *ts, uint8_t chan_nr, int active) +{ + const struct trx_lchan_desc *lchan_desc; + struct trx_lchan_state *lchan; + int len, i, rc = 0; + + /* Prevent NULL-pointer deference */ + if (ts == NULL || ts->lchans == NULL) { + LOGP(DSCH, LOGL_ERROR, "Timeslot isn't configured\n"); + return -EINVAL; + } + + /* Iterate over all allocated lchans */ + len = talloc_array_length(ts->lchans); + for (i = 0; i < len; i++) { + lchan = ts->lchans + i; + lchan_desc = &trx_lchan_desc[lchan->type]; + + if (lchan_desc->chan_nr == (chan_nr & 0xf8)) { + if (active) + rc |= sched_trx_activate_lchan(ts, lchan->type); + else + rc |= sched_trx_deactivate_lchan(ts, lchan->type); + } + } + + return rc; +} + int sched_trx_activate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) { const struct trx_lchan_desc *lchan_desc = &trx_lchan_desc[chan]; @@ -312,6 +341,9 @@ int sched_trx_activate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) return -EINVAL; } + LOGP(DSCH, LOGL_NOTICE, "Activating lchan=%s " + "on ts=%d\n", trx_lchan_desc[chan].name, ts->index); + /* Conditionally allocate memory for bursts */ if (lchan_desc->rx_fn && lchan_desc->burst_buf_size > 0) { lchan->rx_bursts = talloc_zero_size(ts->lchans, @@ -348,6 +380,9 @@ int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) return -EINVAL; } + LOGP(DSCH, LOGL_DEBUG, "Deactivating lchan=%s " + "on ts=%d\n", trx_lchan_desc[chan].name, ts->index); + /* Free memory */ talloc_free(lchan->rx_bursts); talloc_free(lchan->tx_bursts); @@ -362,6 +397,9 @@ void sched_trx_deactivate_all_lchans(struct trx_ts *ts) struct trx_lchan_state *lchan; int i, len; + LOGP(DSCH, LOGL_DEBUG, "Deactivating all logical channels " + "on ts=%d\n", ts->index); + len = talloc_array_length(ts->lchans); for (i = 0; i < len; i++) { lchan = ts->lchans + i; @@ -389,18 +427,16 @@ enum gsm_phys_chan_config sched_trx_chan_nr2pchan_config(uint8_t chan_nr) return GSM_PCHAN_NONE; } -enum trx_lchan_type sched_trx_chan_nr2lchan_type(uint8_t chan_nr) +enum trx_lchan_type sched_trx_chan_nr2lchan_type(uint8_t chan_nr, + uint8_t link_id) { - uint8_t cbits = chan_nr >> 3; + int i; - if (cbits == 0x01) - return TRXC_TCHF; - else if ((cbits & 0x1e) == 0x02) - return TRXC_TCHH_0 + (cbits & 0x1); - else if ((cbits & 0x1c) == 0x04) - return TRXC_SDCCH4_0 + (cbits & 0x3); - else if ((cbits & 0x18) == 0x08) - return TRXC_SDCCH8_0 + (cbits & 0x7); + /* Iterate over all known lchan types */ + for (i = 0; i < _TRX_CHAN_MAX; i++) + if (trx_lchan_desc[i].chan_nr == (chan_nr & 0xf8)) + if (trx_lchan_desc[i].link_id == link_id) + return i; return TRXC_IDLE; } diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index e733de8f1..7ebfa1520 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -258,8 +258,11 @@ int sched_trx_configure_ts(struct trx_instance *trx, int tn, /* Logical channel management functions */ enum gsm_phys_chan_config sched_trx_chan_nr2pchan_config(uint8_t chan_nr); -enum trx_lchan_type sched_trx_chan_nr2lchan_type(uint8_t chan_nr); +enum trx_lchan_type sched_trx_chan_nr2lchan_type(uint8_t chan_nr, + uint8_t link_id); + void sched_trx_deactivate_all_lchans(struct trx_ts *ts); +int sched_trx_set_lchans(struct trx_ts *ts, uint8_t chan_nr, int active); int sched_trx_activate_lchan(struct trx_ts *ts, enum trx_lchan_type chan); int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan); struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts, From 8fd143ee5ae34573db56f2dd1c85ec5a6d889b32 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 31 Jul 2017 13:27:30 +0600 Subject: [PATCH 080/211] host/trxcon/scheduler: pass trx_lchan_state to lchan handlers It's better to pass a trx_lchan_state instance directly from caller to lchan handler instead of passing trx_lchan_type. This way a handler wouldn't need to find lchan itself. Change-Id: I47a40542b03ab31da12b0abb1c263c83662ff018 --- src/host/trxcon/sched_lchan_desc.c | 8 +++---- src/host/trxcon/sched_lchan_rach.c | 2 +- src/host/trxcon/sched_lchan_sch.c | 6 ++--- src/host/trxcon/sched_lchan_xcch.c | 36 ++++++++++++------------------ src/host/trxcon/sched_trx.c | 10 +++++++-- src/host/trxcon/sched_trx.h | 13 +++++------ 6 files changed, 36 insertions(+), 39 deletions(-) diff --git a/src/host/trxcon/sched_lchan_desc.c b/src/host/trxcon/sched_lchan_desc.c index e3998fa8b..dee8f6ac6 100644 --- a/src/host/trxcon/sched_lchan_desc.c +++ b/src/host/trxcon/sched_lchan_desc.c @@ -39,19 +39,19 @@ /* Forward declaration of handlers */ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, - uint32_t fn, enum trx_lchan_type chan, uint8_t bid, + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts, - uint32_t fn, enum trx_lchan_type chan, + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, uint16_t *nbits); int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, - uint32_t fn, enum trx_lchan_type chan, uint8_t bid, + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, - uint32_t fn, enum trx_lchan_type chan, + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, uint16_t *nbits); const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { diff --git a/src/host/trxcon/sched_lchan_rach.c b/src/host/trxcon/sched_lchan_rach.c index 0e8d2e803..6a10ab867 100644 --- a/src/host/trxcon/sched_lchan_rach.c +++ b/src/host/trxcon/sched_lchan_rach.c @@ -58,7 +58,7 @@ static ubit_t rach_synch_seq[] = { /* Obtain a to-be-transmitted RACH burst */ int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, - uint32_t fn, enum trx_lchan_type chan, + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, uint16_t *nbits) { struct trx_ts_prim *prim; diff --git a/src/host/trxcon/sched_lchan_sch.c b/src/host/trxcon/sched_lchan_sch.c index 9e854bbfc..aff8fb66e 100644 --- a/src/host/trxcon/sched_lchan_sch.c +++ b/src/host/trxcon/sched_lchan_sch.c @@ -73,7 +73,7 @@ static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info) } int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, - uint32_t fn, enum trx_lchan_type chan, uint8_t bid, + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa) { sbit_t payload[2 * 39]; @@ -117,8 +117,8 @@ int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, return -ENOMEM; /* Fill in some downlink info */ - data->chan_nr = trx_lchan_desc[chan].chan_nr | ts->index; - data->link_id = trx_lchan_desc[chan].link_id; + data->chan_nr = trx_lchan_desc[lchan->type].chan_nr | ts->index; + data->link_id = trx_lchan_desc[lchan->type].link_id; data->band_arfcn = htons(trx->band_arfcn); data->frame_nr = htonl(fn); data->rx_level = -rssi; diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index 81bd30502..aa95656b6 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -83,26 +83,19 @@ static const uint8_t nb_training_bits[8][26] = { }; int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, - uint32_t fn, enum trx_lchan_type chan, uint8_t bid, + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa) { + const struct trx_lchan_desc *lchan_desc; int n_errors, n_bits_total, rc; - struct trx_lchan_state *lchan; uint8_t *rssi_num, *toa_num; float *rssi_sum, *toa_sum; sbit_t *buffer, *offset; uint8_t l2[23], *mask; uint32_t *first_fn; - LOGP(DSCH, LOGL_DEBUG, "Data received on %s: fn=%u ts=%u bid=%u\n", - trx_lchan_desc[chan].name, fn, ts->index, bid); - - /* Find required channel state */ - lchan = sched_trx_find_lchan(ts, chan); - if (lchan == NULL) - return -EINVAL; - /* Set up pointers */ + lchan_desc = &trx_lchan_desc[lchan->type]; first_fn = &lchan->rx_first_fn; mask = &lchan->rx_burst_mask; buffer = lchan->rx_bursts; @@ -112,6 +105,9 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, toa_sum = &lchan->toa_sum; toa_num = &lchan->toa_num; + LOGP(DSCH, LOGL_DEBUG, "Data received on %s: fn=%u ts=%u bid=%u\n", + lchan_desc->name, fn, ts->index, bid); + /* Clear buffer & store frame number of first burst */ if (bid == 0) { memset(buffer, 0, 464); @@ -147,7 +143,7 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, "fn=%u (%u/%u) for %s\n", *first_fn, (*first_fn) % ts->mf_layout->period, ts->mf_layout->period, - trx_lchan_desc[chan].name); + lchan_desc->name); return -1; } @@ -159,7 +155,7 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, "(%u/%u) for %s\n", *first_fn, (*first_fn) % ts->mf_layout->period, ts->mf_layout->period, - trx_lchan_desc[chan].name); + lchan_desc->name); return rc; } @@ -170,8 +166,8 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, return -ENOMEM; /* Fill in some downlink info */ - data->chan_nr = trx_lchan_desc[chan].chan_nr | ts->index; - data->link_id = trx_lchan_desc[chan].link_id; + data->chan_nr = lchan_desc->chan_nr | ts->index; + data->link_id = lchan_desc->link_id; data->band_arfcn = htons(trx->band_arfcn); data->frame_nr = htonl(*first_fn); data->rx_level = -(*rssi_sum / *rssi_num); @@ -193,10 +189,10 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, } int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts, - uint32_t fn, enum trx_lchan_type chan, + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, uint16_t *nbits) { - struct trx_lchan_state *lchan; + const struct trx_lchan_desc *lchan_desc; struct trx_ts_prim *prim; struct l1ctl_info_ul *ul; ubit_t burst[GSM_BURST_LEN]; @@ -205,12 +201,8 @@ int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts, const uint8_t *tsc; int rc; - /* Find required channel state */ - lchan = sched_trx_find_lchan(ts, chan); - if (lchan == NULL) - return -EINVAL; - /* Set up pointers */ + lchan_desc = &trx_lchan_desc[lchan->type]; mask = &lchan->tx_burst_mask; buffer = lchan->tx_bursts; @@ -262,7 +254,7 @@ send_burst: *nbits = GSM_BURST_LEN; LOGP(DSCH, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n", - trx_lchan_desc[chan].name, fn, ts->index, bid); + lchan_desc->name, fn, ts->index, bid); /* Send burst to transceiver */ rc = trx_if_tx_burst(trx, ts->index, fn, 10, burst); diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 40d144659..b9f188915 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -49,6 +49,7 @@ static void sched_frame_clck_cb(struct trx_sched *sched) { struct trx_instance *trx = (struct trx_instance *) sched->data; const struct trx_frame *frame; + struct trx_lchan_state *lchan; trx_lchan_tx_func *handler; struct trx_ts_prim *prim; enum trx_lchan_type chan; @@ -86,12 +87,17 @@ static void sched_frame_clck_cb(struct trx_sched *sched) if (!handler) continue; + /* Make sure that lchan was allocated and activated */ + lchan = sched_trx_find_lchan(ts, chan); + if (lchan == NULL) + continue; + /* Get a message from TX queue */ prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); /* Poke lchan handler */ if (prim->chan == chan) - handler(trx, ts, fn, chan, bid, NULL); + handler(trx, ts, lchan, fn, bid, NULL); } } @@ -500,7 +506,7 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, /* Put burst to handler */ if (fn == burst_fn) { /* TODO: decrypt if required */ - handler(trx, ts, fn, chan, bid, bits, nbits, rssi, toa); + handler(trx, ts, lchan, fn, bid, bits, nbits, rssi, toa); } next_frame: diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 7ebfa1520..f8a8b536c 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -23,6 +23,7 @@ #define MAX_A5_KEY_LEN (128 / 8) /* Forward declaration to avoid mutual include */ +struct trx_lchan_state; struct trx_instance; struct trx_ts; @@ -78,15 +79,13 @@ enum trx_lchan_type { }; typedef int trx_lchan_rx_func(struct trx_instance *trx, - struct trx_ts *ts, - uint32_t fn, enum trx_lchan_type chan, - uint8_t bid, sbit_t *bits, uint16_t nbits, - int8_t rssi, float toa); + struct trx_ts *ts, struct trx_lchan_state *lchan, + uint32_t fn, uint8_t bid, sbit_t *bits, + uint16_t nbits, int8_t rssi, float toa); typedef int trx_lchan_tx_func(struct trx_instance *trx, - struct trx_ts *ts, - uint32_t fn, enum trx_lchan_type chan, - uint8_t bid, uint16_t *nbits); + struct trx_ts *ts, struct trx_lchan_state *lchan, + uint32_t fn, uint8_t bid, uint16_t *nbits); struct trx_lchan_desc { /*! \brief TRX Channel Type */ From c0100cd145ade5ff36025d9640f78ee0aabb0846 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 31 Jul 2017 14:03:51 +0600 Subject: [PATCH 081/211] host/trxcon/scheduler: get rid of useless nbits argument Change-Id: I8508676e2cb347396c6ca6b394f13113f3e63084 --- src/host/trxcon/sched_lchan_desc.c | 10 ++++------ src/host/trxcon/sched_lchan_rach.c | 3 +-- src/host/trxcon/sched_lchan_sch.c | 2 +- src/host/trxcon/sched_lchan_xcch.c | 8 ++------ src/host/trxcon/sched_trx.c | 4 ++-- src/host/trxcon/sched_trx.h | 4 ++-- 6 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/host/trxcon/sched_lchan_desc.c b/src/host/trxcon/sched_lchan_desc.c index dee8f6ac6..c483db8b3 100644 --- a/src/host/trxcon/sched_lchan_desc.c +++ b/src/host/trxcon/sched_lchan_desc.c @@ -40,19 +40,17 @@ /* Forward declaration of handlers */ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); + sbit_t *bits, int8_t rssi, float toa); int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, - uint8_t bid, uint16_t *nbits); + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid); int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); + sbit_t *bits, int8_t rssi, float toa); int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, - uint8_t bid, uint16_t *nbits); + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid); const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { { diff --git a/src/host/trxcon/sched_lchan_rach.c b/src/host/trxcon/sched_lchan_rach.c index 6a10ab867..2709df0e1 100644 --- a/src/host/trxcon/sched_lchan_rach.c +++ b/src/host/trxcon/sched_lchan_rach.c @@ -58,8 +58,7 @@ static ubit_t rach_synch_seq[] = { /* Obtain a to-be-transmitted RACH burst */ int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, - uint8_t bid, uint16_t *nbits) + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid) { struct trx_ts_prim *prim; struct l1ctl_rach_req *req; diff --git a/src/host/trxcon/sched_lchan_sch.c b/src/host/trxcon/sched_lchan_sch.c index aff8fb66e..e3c4c97cf 100644 --- a/src/host/trxcon/sched_lchan_sch.c +++ b/src/host/trxcon/sched_lchan_sch.c @@ -74,7 +74,7 @@ static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info) int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - sbit_t *bits, uint16_t nbits, int8_t rssi, float toa) + sbit_t *bits, int8_t rssi, float toa) { sbit_t payload[2 * 39]; struct gsm_time time; diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index aa95656b6..1ea74685f 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -84,7 +84,7 @@ static const uint8_t nb_training_bits[8][26] = { int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - sbit_t *bits, uint16_t nbits, int8_t rssi, float toa) + sbit_t *bits, int8_t rssi, float toa) { const struct trx_lchan_desc *lchan_desc; int n_errors, n_bits_total, rc; @@ -189,8 +189,7 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, } int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, - uint8_t bid, uint16_t *nbits) + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid) { const struct trx_lchan_desc *lchan_desc; struct trx_ts_prim *prim; @@ -250,9 +249,6 @@ send_burst: memcpy(burst + 87, offset + 58, 58); /* Payload 2/2 */ memset(burst + 145, 0, 3); /* TB */ - if (nbits) - *nbits = GSM_BURST_LEN; - LOGP(DSCH, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n", lchan_desc->name, fn, ts->index, bid); diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index b9f188915..956c261be 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -97,7 +97,7 @@ static void sched_frame_clck_cb(struct trx_sched *sched) /* Poke lchan handler */ if (prim->chan == chan) - handler(trx, ts, lchan, fn, bid, NULL); + handler(trx, ts, lchan, fn, bid); } } @@ -506,7 +506,7 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, /* Put burst to handler */ if (fn == burst_fn) { /* TODO: decrypt if required */ - handler(trx, ts, lchan, fn, bid, bits, nbits, rssi, toa); + handler(trx, ts, lchan, fn, bid, bits, rssi, toa); } next_frame: diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index f8a8b536c..1b74041c3 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -81,11 +81,11 @@ enum trx_lchan_type { typedef int trx_lchan_rx_func(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, sbit_t *bits, - uint16_t nbits, int8_t rssi, float toa); + int8_t rssi, float toa); typedef int trx_lchan_tx_func(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, - uint32_t fn, uint8_t bid, uint16_t *nbits); + uint32_t fn, uint8_t bid); struct trx_lchan_desc { /*! \brief TRX Channel Type */ From f2179e6763d1a7ea551df1c026078d5cfd25a04d Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 31 Jul 2017 15:00:15 +0600 Subject: [PATCH 082/211] host/trxcon/l1ctl.c: make l1ctl_tx_data_ind flexible Now this function can send both DATA and TRAFFIC indications. Change-Id: I945c10c317155917b6e6ce9d663d9cb46f2e085c --- src/host/trxcon/l1ctl.c | 15 +++++++++++---- src/host/trxcon/l1ctl.h | 3 ++- src/host/trxcon/sched_lchan_xcch.c | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index ed30205f5..ee03ad60f 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -177,18 +177,25 @@ int l1ctl_tx_ccch_mode_conf(struct l1ctl_link *l1l, uint8_t mode) return l1ctl_link_send(l1l, msg); } -int l1ctl_tx_data_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data) +int l1ctl_tx_data_ind(struct l1ctl_link *l1l, + struct l1ctl_info_dl *data, uint8_t msg_type) { struct l1ctl_info_dl *dl; struct msgb *msg; size_t len; - msg = l1ctl_alloc_msg(L1CTL_DATA_IND); + if (msg_type != L1CTL_DATA_IND && msg_type != L1CTL_TRAFFIC_IND) { + LOGP(DL1C, LOGL_DEBUG, "Incorrect indication type\n"); + return -EINVAL; + } + + msg = l1ctl_alloc_msg(msg_type); if (msg == NULL) return -ENOMEM; - /* We store the 23-byte payload as a flexible array member */ - len = sizeof(struct l1ctl_info_dl) + 23; + /* We store the payload as a flexible array member */ + len = sizeof(struct l1ctl_info_dl); + len += msg_type == L1CTL_DATA_IND ? 23 : TRAFFIC_DATA_LEN; dl = (struct l1ctl_info_dl *) msgb_put(msg, len); /* Copy header and data from source message */ diff --git a/src/host/trxcon/l1ctl.h b/src/host/trxcon/l1ctl.h index e83138d6a..4f48aaa0f 100644 --- a/src/host/trxcon/l1ctl.h +++ b/src/host/trxcon/l1ctl.h @@ -18,6 +18,7 @@ int l1ctl_tx_pm_conf(struct l1ctl_link *l1l, uint16_t band_arfcn, int l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type); int l1ctl_tx_reset_ind(struct l1ctl_link *l1l, uint8_t type); -int l1ctl_tx_data_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *ind); +int l1ctl_tx_data_ind(struct l1ctl_link *l1l, + struct l1ctl_info_dl *data, uint8_t msg_type); int l1ctl_tx_rach_conf(struct l1ctl_link *l1l, uint32_t fn); int l1ctl_tx_data_conf(struct l1ctl_link *l1l); diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index 1ea74685f..9a7a09b44 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -181,7 +181,7 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, memcpy(data->payload, l2, 23); /* Put a packet to higher layers */ - l1ctl_tx_data_ind(trx->l1l, data); + l1ctl_tx_data_ind(trx->l1l, data, L1CTL_DATA_IND); talloc_free(data); /* TODO: AGC, TA loops */ From 0dc5b233e63d2656ee44bce7f25a564e1f8d9765 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 31 Jul 2017 17:24:27 +0600 Subject: [PATCH 083/211] host/trxcon/scheduler: share common code for lchan handlers The training sequences array is currently used by xCCH handlers, but will be also used for handling both TCH/F and TCH/H bursts. Moreover the code that forwards decoded L2 payloads to L1CTL protocol handlers was separated into a new shared function. Change-Id: I34c3de351362ebd9a070f49bb78d7bd976784b04 --- src/host/trxcon/Makefile.am | 1 + src/host/trxcon/sched_lchan_common.c | 114 +++++++++++++++++++++++++++ src/host/trxcon/sched_lchan_xcch.c | 66 ++-------------- 3 files changed, 122 insertions(+), 59 deletions(-) create mode 100644 src/host/trxcon/sched_lchan_common.c diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am index cd4bbd259..90c7a3c11 100644 --- a/src/host/trxcon/Makefile.am +++ b/src/host/trxcon/Makefile.am @@ -31,6 +31,7 @@ trxcon_SOURCES = \ # Scheduler trxcon_SOURCES += \ + sched_lchan_common.c \ sched_lchan_desc.c \ sched_lchan_xcch.c \ sched_lchan_rach.c \ diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c new file mode 100644 index 000000000..7dad75022 --- /dev/null +++ b/src/host/trxcon/sched_lchan_common.c @@ -0,0 +1,114 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: common routines for lchan handlers + * + * (C) 2017 by Vadim Yanitskiy + * + * 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 +#include +#include +#include + +#include + +#include +#include + +#include "l1ctl_proto.h" +#include "scheduler.h" +#include "sched_trx.h" +#include "logging.h" +#include "trx_if.h" +#include "trxcon.h" +#include "l1ctl.h" + +/* GSM 05.02 Chapter 5.2.3 Normal Burst (NB) */ +const uint8_t nb_training_bits[8][26] = { + { + 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, + 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, + }, + { + 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, + 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, + }, + { + 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, + 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, + }, + { + 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, + 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, + }, + { + 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, + 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, + }, + { + 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, + 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, + }, + { + 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, + 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, + }, + { + 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, + 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, + }, +}; + +int sched_send_data_ind(struct trx_instance *trx, struct trx_ts *ts, + struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len) +{ + const struct trx_lchan_desc *lchan_desc; + struct l1ctl_info_dl *data; + + /* Allocate memory */ + data = talloc_zero_size(ts, sizeof(struct l1ctl_info_dl) + l2_len); + if (data == NULL) + return -ENOMEM; + + /* Set up pointers */ + lchan_desc = &trx_lchan_desc[lchan->type]; + + /* Fill in known downlink info */ + data->chan_nr = lchan_desc->chan_nr | ts->index; + data->link_id = lchan_desc->link_id; + data->band_arfcn = htons(trx->band_arfcn); + data->frame_nr = htonl(lchan->rx_first_fn); + data->rx_level = -(lchan->rssi_sum / lchan->rssi_num); + + /* FIXME: set proper values */ + data->num_biterr = 0; + data->fire_crc = 0; + data->snr = 0; + + /* Fill in the payload */ + memcpy(data->payload, l2, l2_len); + + /* Put a packet to higher layers */ + l1ctl_tx_data_ind(trx->l1l, data, l2_len == 23 ? + L1CTL_DATA_IND : L1CTL_TRAFFIC_IND); + talloc_free(data); + + return 0; +} diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index 9a7a09b44..ad4c971e7 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -46,41 +46,11 @@ #include "trxcon.h" #include "l1ctl.h" -/* GSM 05.02 Chapter 5.2.3 Normal Burst (NB) */ -static const uint8_t nb_training_bits[8][26] = { - { - 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, - 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, - }, - { - 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, - 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, - }, - { - 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, - 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, - }, - { - 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, - 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, - }, - { - 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, - 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, - }, - { - 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, - }, - { - 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, - 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, - }, - { - 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, - }, -}; +/* Forward declarations */ +extern const uint8_t nb_training_bits[8][26]; + +int sched_send_data_ind(struct trx_instance *trx, struct trx_ts *ts, + struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len); int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, @@ -159,30 +129,8 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, return rc; } - /* Compose a message to the higher layers */ - struct l1ctl_info_dl *data; - data = talloc_zero_size(ts, sizeof(struct l1ctl_info_dl) + 23); - if (data == NULL) - return -ENOMEM; - - /* Fill in some downlink info */ - data->chan_nr = lchan_desc->chan_nr | ts->index; - data->link_id = lchan_desc->link_id; - data->band_arfcn = htons(trx->band_arfcn); - data->frame_nr = htonl(*first_fn); - data->rx_level = -(*rssi_sum / *rssi_num); - - /* FIXME: set proper values */ - data->num_biterr = n_errors; - data->fire_crc = 0; - data->snr = 0; - - /* Fill in decoded payload */ - memcpy(data->payload, l2, 23); - - /* Put a packet to higher layers */ - l1ctl_tx_data_ind(trx->l1l, data, L1CTL_DATA_IND); - talloc_free(data); + /* Send a L2 frame to the higher layers */ + sched_send_data_ind(trx, ts, lchan, l2, 23); /* TODO: AGC, TA loops */ return 0; From f9ab2a0b49aac3973d8a3f94762d2315740733cc Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 31 Jul 2017 20:06:36 +0600 Subject: [PATCH 084/211] host/trxcon/scheduler: clean up some includes Change-Id: I47e3b953b80f4f822d563579d15498181009ca80 --- src/host/trxcon/sched_lchan_rach.c | 4 ---- src/host/trxcon/sched_lchan_sch.c | 2 -- src/host/trxcon/sched_lchan_xcch.c | 4 ---- 3 files changed, 10 deletions(-) diff --git a/src/host/trxcon/sched_lchan_rach.c b/src/host/trxcon/sched_lchan_rach.c index 2709df0e1..e92367251 100644 --- a/src/host/trxcon/sched_lchan_rach.c +++ b/src/host/trxcon/sched_lchan_rach.c @@ -27,13 +27,9 @@ #include #include -#include - #include #include #include -#include -#include #include #include diff --git a/src/host/trxcon/sched_lchan_sch.c b/src/host/trxcon/sched_lchan_sch.c index e3c4c97cf..db73b926d 100644 --- a/src/host/trxcon/sched_lchan_sch.c +++ b/src/host/trxcon/sched_lchan_sch.c @@ -32,8 +32,6 @@ #include #include #include -#include -#include #include #include diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index ad4c971e7..2db0beccc 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -27,13 +27,9 @@ #include #include -#include - #include #include #include -#include -#include #include #include From 1e9501671a73ba19ec7e21db7ca7dadc8a2ebff7 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 3 Aug 2017 19:34:39 +0600 Subject: [PATCH 085/211] fake_trx: add options to specify fn, tn and pwr Change-Id: Ifd4f4864707596a69fece11218a4800b98551c31 --- src/target/fake_trx/burst_gen.py | 36 +++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/target/fake_trx/burst_gen.py b/src/target/fake_trx/burst_gen.py index 195158a49..fb50ea3b0 100755 --- a/src/target/fake_trx/burst_gen.py +++ b/src/target/fake_trx/burst_gen.py @@ -126,6 +126,9 @@ class Application: burst_type = None burst_count = 1 + pwr = None + fn = None + tn = None def __init__(self): self.print_copyright() @@ -149,6 +152,12 @@ class Application: # Init random burst generator self.gen = RandBurstGen() + # Generate a random frame number or use provided one + if self.fn is None: + fn = random.randint(0, DATAInterface.GSM_HYPERFRAME) + else: + fn = self.fn + # Send as much bursts as required for i in range(self.burst_count): # Generate a random burst @@ -165,15 +174,20 @@ class Application: self.shutdown() sys.exit(2) - print("[i] Sending %d/%d %s burst to %s..." + print("[i] Sending %d/%d %s burst (fn=%u) to %s..." % (i + 1, self.burst_count, self.burst_type, - self.conn_mode)) + fn, self.conn_mode)) # Send to TRX or L1 if self.conn_mode == "TRX": - self.data_if.send_trx_msg(buf) + self.data_if.send_trx_msg(buf, + self.tn, fn, self.pwr) elif self.conn_mode == "L1": - self.data_if.send_l1_msg(buf) + self.data_if.send_l1_msg(buf, + self.tn, fn, self.pwr) + + # Increase frame number (for count > 1) + fn = (fn + 1) % DATAInterface.GSM_HYPERFRAME self.shutdown() @@ -193,6 +207,9 @@ class Application: s += " Burst generation\n" \ " -b --burst-type Random burst type (NB, FB, SB, AB)\n" \ " -c --burst-count How much bursts to send (default 1)\n" \ + " -f --frame-number Set frame number (default random)\n" \ + " -t --timeslot Set timeslot index (default random)\n" \ + " -l --power-level Set transmit level (default random)\n" \ print(s % (self.remote_addr, self.base_port)) @@ -202,7 +219,7 @@ class Application: def parse_argv(self): try: opts, args = getopt.getopt(sys.argv[1:], - "m:r:p:b:c:h", + "m:r:p:b:c:f:t:l:h", [ "help", "conn-mode=", @@ -210,6 +227,9 @@ class Application: "base-port=", "burst-type=", "burst-count=", + "frame-number=", + "timeslot=", + "power-level=", ]) except getopt.GetoptError as err: self.print_help("[!] " + str(err)) @@ -231,6 +251,12 @@ class Application: self.burst_type = v elif o in ("-c", "--burst-count"): self.burst_count = int(v) + elif o in ("-f", "--frame-number"): + self.fn = int(v) + elif o in ("-t", "--timeslot"): + self.tn = int(v) + elif o in ("-l", "--power-level"): + self.pwr = abs(int(v)) def shutdown(self): self.data_if.shutdown() From a59edfbd4062705266b300fe23cdf4d06b2a8671 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 3 Aug 2017 19:51:57 +0600 Subject: [PATCH 086/211] fake_trx: separate DataInterface from burst_gen.py Change-Id: I325cf2ae59ef8834c2ddfb67206eede44d1e0acf --- src/target/fake_trx/burst_gen.py | 82 +---------------------- src/target/fake_trx/data_if.py | 107 +++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 81 deletions(-) create mode 100644 src/target/fake_trx/data_if.py diff --git a/src/target/fake_trx/burst_gen.py b/src/target/fake_trx/burst_gen.py index fb50ea3b0..f8f924c50 100755 --- a/src/target/fake_trx/burst_gen.py +++ b/src/target/fake_trx/burst_gen.py @@ -28,8 +28,8 @@ import getopt import select import sys -from udp_link import UDPLink from rand_burst_gen import RandBurstGen +from data_if import DATAInterface COPYRIGHT = \ "Copyright (C) 2017 by Vadim Yanitskiy \n" \ @@ -38,86 +38,6 @@ COPYRIGHT = \ "This is free software: you are free to change and redistribute it.\n" \ "There is NO WARRANTY, to the extent permitted by law.\n" -class DATAInterface(UDPLink): - # GSM PHY definitions - GSM_HYPERFRAME = 2048 * 26 * 51 - - def send_l1_msg(self, burst, - tn = None, fn = None, rssi = None): - # Generate random timeslot index if not preset - if tn is None: - tn = random.randint(0, 7) - - # Generate random frame number if not preset - if fn is None: - fn = random.randint(0, self.GSM_HYPERFRAME) - - # Generate random RSSI if not preset - if rssi is None: - rssi = -random.randint(-75, -50) - - # Prepare a buffer for header and burst - buf = [] - - # Put timeslot index - buf.append(tn) - - # Put frame number - buf.append((fn >> 24) & 0xff) - buf.append((fn >> 16) & 0xff) - buf.append((fn >> 8) & 0xff) - buf.append((fn >> 0) & 0xff) - - # Put RSSI - buf.append(rssi) - - # HACK: put fake TOA value - buf += [0x00] * 2 - - # Put burst - buf += burst - - # Put two unused bytes - buf += [0x00] * 2 - - # Send message - self.send(bytearray(buf)) - - def send_trx_msg(self, burst, - tn = None, fn = None, pwr = None): - # Generate random timeslot index if not preset - if tn is None: - tn = random.randint(0, 7) - - # Generate random frame number if not preset - if fn is None: - fn = random.randint(0, self.GSM_HYPERFRAME) - - # Generate random power level if not preset - if pwr is None: - pwr = random.randint(0, 34) - - # Prepare a buffer for header and burst - buf = [] - - # Put timeslot index - buf.append(tn) - - # Put frame number - buf.append((fn >> 24) & 0xff) - buf.append((fn >> 16) & 0xff) - buf.append((fn >> 8) & 0xff) - buf.append((fn >> 0) & 0xff) - - # Put transmit power level - buf.append(pwr) - - # Put burst - buf += burst - - # Send message - self.send(bytearray(buf)) - class Application: # Application variables remote_addr = "127.0.0.1" diff --git a/src/target/fake_trx/data_if.py b/src/target/fake_trx/data_if.py new file mode 100644 index 000000000..315281cd5 --- /dev/null +++ b/src/target/fake_trx/data_if.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# Simple tool to send custom messages via TRX DATA interface, +# which may be also useful for fuzzing and testing +# +# (C) 2017 by Vadim Yanitskiy +# +# 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. + +import random + +from udp_link import UDPLink + +class DATAInterface(UDPLink): + # GSM PHY definitions + GSM_HYPERFRAME = 2048 * 26 * 51 + + def send_l1_msg(self, burst, + tn = None, fn = None, rssi = None): + # Generate random timeslot index if not preset + if tn is None: + tn = random.randint(0, 7) + + # Generate random frame number if not preset + if fn is None: + fn = random.randint(0, self.GSM_HYPERFRAME) + + # Generate random RSSI if not preset + if rssi is None: + rssi = -random.randint(-75, -50) + + # Prepare a buffer for header and burst + buf = [] + + # Put timeslot index + buf.append(tn) + + # Put frame number + buf.append((fn >> 24) & 0xff) + buf.append((fn >> 16) & 0xff) + buf.append((fn >> 8) & 0xff) + buf.append((fn >> 0) & 0xff) + + # Put RSSI + buf.append(rssi) + + # HACK: put fake TOA value + buf += [0x00] * 2 + + # Put burst + buf += burst + + # Put two unused bytes + buf += [0x00] * 2 + + # Send message + self.send(bytearray(buf)) + + def send_trx_msg(self, burst, + tn = None, fn = None, pwr = None): + # Generate random timeslot index if not preset + if tn is None: + tn = random.randint(0, 7) + + # Generate random frame number if not preset + if fn is None: + fn = random.randint(0, self.GSM_HYPERFRAME) + + # Generate random power level if not preset + if pwr is None: + pwr = random.randint(0, 34) + + # Prepare a buffer for header and burst + buf = [] + + # Put timeslot index + buf.append(tn) + + # Put frame number + buf.append((fn >> 24) & 0xff) + buf.append((fn >> 16) & 0xff) + buf.append((fn >> 8) & 0xff) + buf.append((fn >> 0) & 0xff) + + # Put transmit power level + buf.append(pwr) + + # Put burst + buf += burst + + # Send message + self.send(bytearray(buf)) From f28f0d343f6579778b45874de694c74b88e13b6f Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 3 Aug 2017 20:00:12 +0600 Subject: [PATCH 087/211] fake_trx: whitespace fix Change-Id: Iad2be36777e4a2454e181c856c7902574a4ab20c --- src/target/fake_trx/burst_gen.py | 2 +- src/target/fake_trx/rand_burst_gen.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/target/fake_trx/burst_gen.py b/src/target/fake_trx/burst_gen.py index f8f924c50..e61cc6404 100755 --- a/src/target/fake_trx/burst_gen.py +++ b/src/target/fake_trx/burst_gen.py @@ -189,4 +189,4 @@ class Application: if __name__ == '__main__': app = Application() - app.run() \ No newline at end of file + app.run() diff --git a/src/target/fake_trx/rand_burst_gen.py b/src/target/fake_trx/rand_burst_gen.py index 4475e5953..75887f2f4 100644 --- a/src/target/fake_trx/rand_burst_gen.py +++ b/src/target/fake_trx/rand_burst_gen.py @@ -76,14 +76,14 @@ class RandBurstGen: db_bits = [ 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, - 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, - 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, + 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, - 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, - 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, + 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, + 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, ] From 2e062039b662b02675d1795e14564021cde89c6e Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 4 Aug 2017 13:05:09 +0600 Subject: [PATCH 088/211] host/trxcon/l1ctl.c: do nothing if CCCH mode matches When the L1CTL_CCCH_MODE_REQ is received, we don't need to reconfigure anything if the current mode matches requested. Change-Id: Ib8a511e4edd7210b1806f47e83f316be00a8cbb1 --- src/host/trxcon/l1ctl.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index ee03ad60f..ec774d713 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -413,6 +413,7 @@ static int l1ctl_rx_echo_req(struct l1ctl_link *l1l, struct msgb *msg) static int l1ctl_rx_ccch_mode_req(struct l1ctl_link *l1l, struct msgb *msg) { struct l1ctl_ccch_mode_req *req; + struct trx_ts *ts; int mode, rc = 0; req = (struct l1ctl_ccch_mode_req *) msg->l1h; @@ -427,10 +428,21 @@ static int l1ctl_rx_ccch_mode_req(struct l1ctl_link *l1l, struct msgb *msg) req->ccch_mode == CCCH_MODE_COMBINED ? "combined" : "not combined"); - /* Reconfigure TS0 */ + /* Make sure that TS0 is allocated and configured */ + ts = l1l->trx->ts_list[0]; + if (ts == NULL || ts->mf_layout == NULL) { + LOGP(DL1C, LOGL_ERROR, "TS0 is not configured"); + rc = -EINVAL; + goto exit; + } + + /* Choose corresponding channel combination */ mode = req->ccch_mode == CCCH_MODE_COMBINED ? GSM_PCHAN_CCCH_SDCCH4 : GSM_PCHAN_CCCH; - rc = sched_trx_configure_ts(l1l->trx, 0, mode); + + /* Do nothing if the current mode matches required */ + if (ts->mf_layout->chan_config != mode) + rc = sched_trx_configure_ts(l1l->trx, 0, mode); /* Confirm reconfiguration */ if (!rc) From f5bbe5ebfec10efe04b45b60f6437b99e1a31e15 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 4 Aug 2017 16:31:04 +0600 Subject: [PATCH 089/211] host/trxcon: separate logging of L1 Control and L1 Data L1 Data is quite verbose, while Control is typically limited. And if you would need to debug some Control message handling, the Data messages wont overflow your terminal anymore. This change introduces a new logging category named 'DL1D'. Change-Id: Id830c8bf913f7a8ddc87c47f70a337ee4623abd8 --- src/host/trxcon/l1ctl.c | 14 +++++++------- src/host/trxcon/l1ctl_link.c | 16 ++++++++-------- src/host/trxcon/logging.c | 6 ++++++ src/host/trxcon/logging.h | 3 ++- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index ec774d713..ddea262da 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -185,7 +185,7 @@ int l1ctl_tx_data_ind(struct l1ctl_link *l1l, size_t len; if (msg_type != L1CTL_DATA_IND && msg_type != L1CTL_TRAFFIC_IND) { - LOGP(DL1C, LOGL_DEBUG, "Incorrect indication type\n"); + LOGP(DL1D, LOGL_DEBUG, "Incorrect indication type\n"); return -EINVAL; } @@ -233,7 +233,7 @@ int l1ctl_tx_data_conf(struct l1ctl_link *l1l) if (msg == NULL) return -ENOMEM; - LOGP(DL1C, LOGL_DEBUG, "Send Data Conf\n"); + LOGP(DL1D, LOGL_DEBUG, "Send Data Conf\n"); return l1ctl_link_send(l1l, msg); } @@ -601,13 +601,13 @@ static int l1ctl_rx_data_req(struct l1ctl_link *l1l, struct msgb *msg) chan_nr = ul->chan_nr; link_id = ul->link_id & 0x40; - LOGP(DL1C, LOGL_DEBUG, "Recv Data Req (chan_nr=0x%02x, " + LOGP(DL1D, LOGL_DEBUG, "Recv Data Req (chan_nr=0x%02x, " "link_id=0x%02x)\n", chan_nr, link_id); /* Determine TS index */ tn = chan_nr & 0x7; if (tn > 7) { - LOGP(DL1C, LOGL_ERROR, "Incorrect TS index %u\n", tn); + LOGP(DL1D, LOGL_ERROR, "Incorrect TS index %u\n", tn); rc = -EINVAL; goto exit; } @@ -615,7 +615,7 @@ static int l1ctl_rx_data_req(struct l1ctl_link *l1l, struct msgb *msg) /* Determine lchan type */ lchan_type = sched_trx_chan_nr2lchan_type(chan_nr, link_id); if (!lchan_type) { - LOGP(DL1C, LOGL_ERROR, "Couldn't determine lchan type " + LOGP(DL1D, LOGL_ERROR, "Couldn't determine lchan type " "for chan_nr=%02x and link_id=%02x\n", chan_nr, link_id); rc = -EINVAL; goto exit; @@ -624,7 +624,7 @@ static int l1ctl_rx_data_req(struct l1ctl_link *l1l, struct msgb *msg) /* Check whether required timeslot is allocated and configured */ ts = l1l->trx->ts_list[tn]; if (ts == NULL || ts->mf_layout == NULL) { - LOGP(DL1C, LOGL_ERROR, "Timeslot %u isn't configured\n", tn); + LOGP(DL1D, LOGL_ERROR, "Timeslot %u isn't configured\n", tn); rc = -EINVAL; goto exit; } @@ -633,7 +633,7 @@ static int l1ctl_rx_data_req(struct l1ctl_link *l1l, struct msgb *msg) len = sizeof(struct trx_ts_prim) + sizeof(struct l1ctl_info_ul) + 23; prim = talloc_zero_size(ts, len); if (prim == NULL) { - LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory\n"); + LOGP(DL1D, LOGL_ERROR, "Failed to allocate memory\n"); rc = -ENOMEM; goto exit; } diff --git a/src/host/trxcon/l1ctl_link.c b/src/host/trxcon/l1ctl_link.c index 265bfeb21..c38a87155 100644 --- a/src/host/trxcon/l1ctl_link.c +++ b/src/host/trxcon/l1ctl_link.c @@ -81,7 +81,7 @@ static int l1ctl_link_read_cb(struct osmo_fd *bfd) /* 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"); + LOGP(DL1D, LOGL_NOTICE, "L1CTL has lost connection\n"); msgb_free(msg); if (rc >= 0) rc = -EIO; @@ -92,7 +92,7 @@ static int l1ctl_link_read_cb(struct osmo_fd *bfd) /* Check message length */ len = ntohs(len); if (len > L1CTL_LENGTH) { - LOGP(DL1C, LOGL_ERROR, "Length is too big: %u\n", len); + LOGP(DL1D, LOGL_ERROR, "Length is too big: %u\n", len); msgb_free(msg); return -EINVAL; } @@ -100,14 +100,14 @@ static int l1ctl_link_read_cb(struct osmo_fd *bfd) 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: " + LOGP(DL1D, 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", + LOGP(DL1D, LOGL_DEBUG, "RX: '%s'\n", osmo_hexdump(msg->data, msg->len)); /* Call L1CTL handler */ @@ -125,7 +125,7 @@ static int l1ctl_link_write_cb(struct osmo_fd *bfd, struct msgb *msg) len = write(bfd->fd, msg->data, msg->len); if (len != msg->len) { - LOGP(DL1C, LOGL_ERROR, "Failed to write data: " + LOGP(DL1D, LOGL_ERROR, "Failed to write data: " "written (%d) < msg_len (%d)\n", len, msg->len); return -1; } @@ -186,18 +186,18 @@ int l1ctl_link_send(struct l1ctl_link *l1l, struct msgb *msg) uint16_t *len; /* Debug print */ - LOGP(DL1C, LOGL_DEBUG, "TX: '%s'\n", + LOGP(DL1D, 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"); + LOGP(DL1D, 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"); + LOGP(DL1D, LOGL_ERROR, "Failed to enqueue msg!\n"); msgb_free(msg); return -EIO; } diff --git a/src/host/trxcon/logging.c b/src/host/trxcon/logging.c index 3381c6a4c..338deaf82 100644 --- a/src/host/trxcon/logging.c +++ b/src/host/trxcon/logging.c @@ -40,6 +40,12 @@ static struct log_info_cat trx_log_info_cat[] = { .color = "\033[1;31m", .enabled = 1, .loglevel = LOGL_NOTICE, }, + [DL1D] = { + .name = "DL1D", + .description = "Layer 1 data", + .color = "\033[1;31m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, [DTRX] = { .name = "DTRX", .description = "Transceiver interface", diff --git a/src/host/trxcon/logging.h b/src/host/trxcon/logging.h index 52afd4baf..22f325176 100644 --- a/src/host/trxcon/logging.h +++ b/src/host/trxcon/logging.h @@ -2,11 +2,12 @@ #include -#define DEBUG_DEFAULT "DAPP:DL1C:DTRX:DSCH" +#define DEBUG_DEFAULT "DAPP:DL1C:DL1D:DTRX:DSCH" enum { DAPP, DL1C, + DL1D, DTRX, DSCH, }; From 17a773c7345fae9e4b28801898fb0d2093218c22 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 4 Aug 2017 16:37:45 +0600 Subject: [PATCH 090/211] host/trxcon/l1ctl.c: fix wrong log level Change-Id: I0ac65d94b0ae3dd370675318a26a65d11c49cbbe --- src/host/trxcon/l1ctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index ddea262da..3cf98b3a4 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -185,7 +185,7 @@ int l1ctl_tx_data_ind(struct l1ctl_link *l1l, size_t len; if (msg_type != L1CTL_DATA_IND && msg_type != L1CTL_TRAFFIC_IND) { - LOGP(DL1D, LOGL_DEBUG, "Incorrect indication type\n"); + LOGP(DL1D, LOGL_ERROR, "Incorrect indication type\n"); return -EINVAL; } From 14d0f670640e4084d20ee9093dc0d2b74c8985a8 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 5 Aug 2017 02:22:12 +0600 Subject: [PATCH 091/211] host/trxcon/scheduler: send stored tx_power to transceiver Previously a fixed fake value (10) was used. Change-Id: I8ba70bbda6c8c9249f8eb4294aeb41ab8769a19a --- src/host/trxcon/sched_lchan_rach.c | 2 +- src/host/trxcon/sched_lchan_xcch.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/host/trxcon/sched_lchan_rach.c b/src/host/trxcon/sched_lchan_rach.c index e92367251..613f644de 100644 --- a/src/host/trxcon/sched_lchan_rach.c +++ b/src/host/trxcon/sched_lchan_rach.c @@ -86,7 +86,7 @@ int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, LOGP(DSCH, LOGL_DEBUG, "Transmitting RACH fn=%u\n", fn); /* Send burst to transceiver */ - rc = trx_if_tx_burst(trx, ts->index, fn, 10, burst); + rc = trx_if_tx_burst(trx, ts->index, fn, trx->tx_power, burst); if (rc) { LOGP(DSCH, LOGL_ERROR, "Could not send burst to transceiver\n"); return rc; diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index 2db0beccc..afaacac78 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -197,7 +197,7 @@ send_burst: lchan_desc->name, fn, ts->index, bid); /* Send burst to transceiver */ - rc = trx_if_tx_burst(trx, ts->index, fn, 10, burst); + rc = trx_if_tx_burst(trx, ts->index, fn, trx->tx_power, burst); if (rc) { LOGP(DSCH, LOGL_ERROR, "Could not send burst to transceiver\n"); From 8e13093c88b8d8fd4e9751b9fe8695f86464d46e Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 7 Aug 2017 13:18:59 +0600 Subject: [PATCH 092/211] host/trxcon/l1ctl.c: retune TRX only if current ARFCN differs Change-Id: I797dc284bd92d07ad4859f851a44d048407db86d --- src/host/trxcon/l1ctl.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 3cf98b3a4..8f9689096 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -306,12 +306,16 @@ static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg) /* Ask SCH handler to send L1CTL_FBSB_CONF */ l1l->fbsb_conf_sent = 0; - /* Store current ARFCN */ - l1l->trx->band_arfcn = band_arfcn; + /* Only if current ARFCN differs */ + if (l1l->trx->band_arfcn != band_arfcn) { + /* Update current ARFCN */ + l1l->trx->band_arfcn = band_arfcn; + + /* Tune transceiver to required ARFCN */ + trx_if_cmd_rxtune(l1l->trx, band_arfcn); + trx_if_cmd_txtune(l1l->trx, band_arfcn); + } - /* Tune transceiver to required ARFCN */ - trx_if_cmd_rxtune(l1l->trx, band_arfcn); - trx_if_cmd_txtune(l1l->trx, band_arfcn); trx_if_cmd_poweron(l1l->trx); /* Start FBSB expire timer */ From 82b8c21b210535feaed98487f0df12f3acc2e46c Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sun, 13 Aug 2017 01:20:19 +0600 Subject: [PATCH 093/211] host/trxcon/l1ctl.c: don't fill l1ctl_info_ul into a primitive The UL frame header isn't used by lchan handlers. Change-Id: Ia1c63b6f17c3802b29f54299da1151a39edf3a03 --- src/host/trxcon/l1ctl.c | 6 ++---- src/host/trxcon/sched_lchan_xcch.c | 4 +--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 8f9689096..f193a7a56 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -645,10 +645,8 @@ static int l1ctl_rx_data_req(struct l1ctl_link *l1l, struct msgb *msg) /* Set logical channel of primitive */ prim->chan = lchan_type; - /* Fill in both UL info and payload */ - len = sizeof(struct l1ctl_info_ul); - memcpy(prim->payload, ul, len); - memcpy(prim->payload + len, data_ind, 23); + /* Fill in the payload */ + memcpy(prim->payload, data_ind, 23); /* Add to TS queue */ llist_add_tail(&prim->list, &ts->tx_prims); diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index afaacac78..62c44f9a6 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -137,7 +137,6 @@ int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts, { const struct trx_lchan_desc *lchan_desc; struct trx_ts_prim *prim; - struct l1ctl_info_ul *ul; ubit_t burst[GSM_BURST_LEN]; ubit_t *buffer, *offset; uint8_t *mask, *l2; @@ -161,8 +160,7 @@ int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts, /* Get a message from TX queue */ prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); - ul = (struct l1ctl_info_ul *) prim->payload; - l2 = (uint8_t *) ul->payload; + l2 = (uint8_t *) prim->payload; /* Encode bursts */ rc = gsm0503_xcch_encode(buffer, l2); From 7d95f8821e82895f7952860923a7624287f9c42c Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sun, 13 Aug 2017 00:16:24 +0600 Subject: [PATCH 094/211] host/trxcon/l1ctl.c: share primitive management code This change introduces shared primitive management functions, exposed from the l1ctl_rx_data_req() implementation: - sched_trx_init_prim() - allocates memory for a new primitive and its payload. Initializes primitive's header, setting the logical channel type and the payload length. After initialization, the talloc context of a primitive is a trx instance, which passed as the first argument. - sched_trx_push_prim() - decodes the timeslot index from chan_nr and pushes a primitive to its transimt queue. The talloc context of primitive is changed to the parent trx_ts instance after queuing. Both functions will be used for handling both L1CTL_TRAFFIC_REQ and L1CTL_RACH_REQ. Change-Id: I8169a1ef4ef54d91b50f3e213e4842f54af8b499 --- src/host/trxcon/l1ctl.c | 61 ++++++++------------------------ src/host/trxcon/sched_trx.c | 69 +++++++++++++++++++++++++++++++++++++ src/host/trxcon/sched_trx.h | 8 +++++ 3 files changed, 92 insertions(+), 46 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index f193a7a56..f125948d3 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -589,17 +589,13 @@ static int l1ctl_rx_dm_rel_req(struct l1ctl_link *l1l, struct msgb *msg) static int l1ctl_rx_data_req(struct l1ctl_link *l1l, struct msgb *msg) { - struct trx_ts *ts; - struct trx_ts_prim *prim; struct l1ctl_info_ul *ul; - struct l1ctl_data_ind *data_ind; - enum trx_lchan_type lchan_type; - uint8_t chan_nr, link_id, tn; - size_t len; - int rc = 0; + struct trx_ts_prim *prim; + uint8_t chan_nr, link_id; + int rc; + /* Extract UL frame header */ ul = (struct l1ctl_info_ul *) msg->l1h; - data_ind = (struct l1ctl_data_ind *) ul->payload; /* Obtain channel description */ chan_nr = ul->chan_nr; @@ -608,48 +604,21 @@ static int l1ctl_rx_data_req(struct l1ctl_link *l1l, struct msgb *msg) LOGP(DL1D, LOGL_DEBUG, "Recv Data Req (chan_nr=0x%02x, " "link_id=0x%02x)\n", chan_nr, link_id); - /* Determine TS index */ - tn = chan_nr & 0x7; - if (tn > 7) { - LOGP(DL1D, LOGL_ERROR, "Incorrect TS index %u\n", tn); - rc = -EINVAL; + /* Init a new primitive */ + rc = sched_trx_init_prim(l1l->trx, &prim, 23, + chan_nr, link_id); + if (rc) + goto exit; + + /* Push this primitive to transmit queue */ + rc = sched_trx_push_prim(l1l->trx, prim, chan_nr); + if (rc) { + talloc_free(prim); goto exit; } - /* Determine lchan type */ - lchan_type = sched_trx_chan_nr2lchan_type(chan_nr, link_id); - if (!lchan_type) { - LOGP(DL1D, LOGL_ERROR, "Couldn't determine lchan type " - "for chan_nr=%02x and link_id=%02x\n", chan_nr, link_id); - rc = -EINVAL; - goto exit; - } - - /* Check whether required timeslot is allocated and configured */ - ts = l1l->trx->ts_list[tn]; - if (ts == NULL || ts->mf_layout == NULL) { - LOGP(DL1D, LOGL_ERROR, "Timeslot %u isn't configured\n", tn); - rc = -EINVAL; - goto exit; - } - - /* Allocate a new primitive */ - len = sizeof(struct trx_ts_prim) + sizeof(struct l1ctl_info_ul) + 23; - prim = talloc_zero_size(ts, len); - if (prim == NULL) { - LOGP(DL1D, LOGL_ERROR, "Failed to allocate memory\n"); - rc = -ENOMEM; - goto exit; - } - - /* Set logical channel of primitive */ - prim->chan = lchan_type; - /* Fill in the payload */ - memcpy(prim->payload, data_ind, 23); - - /* Add to TS queue */ - llist_add_tail(&prim->list, &ts->tx_prims); + memcpy(prim->payload, ul->payload, 23); exit: msgb_free(msg); diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 956c261be..7f6729cb5 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -417,6 +417,75 @@ void sched_trx_deactivate_all_lchans(struct trx_ts *ts) } } +int sched_trx_init_prim(struct trx_instance *trx, + struct trx_ts_prim **prim, size_t pl_len, + uint8_t chan_nr, uint8_t link_id) +{ + enum trx_lchan_type lchan_type; + struct trx_ts_prim *new_prim; + uint8_t len; + + /* Determine lchan type */ + lchan_type = sched_trx_chan_nr2lchan_type(chan_nr, link_id); + if (!lchan_type) { + LOGP(DSCH, LOGL_ERROR, "Couldn't determine lchan type " + "for chan_nr=%02x and link_id=%02x\n", chan_nr, link_id); + return -EINVAL; + } + + /* How much memory do we need? */ + len = sizeof(struct trx_ts_prim); /* Primitive header */ + len += pl_len; /* Requested payload size */ + + /* Allocate a new primitive */ + new_prim = talloc_zero_size(trx, len); + if (new_prim == NULL) { + LOGP(DSCH, LOGL_ERROR, "Failed to allocate memory\n"); + return -ENOMEM; + } + + /* Init primitive header */ + new_prim->payload_len = pl_len; + new_prim->chan = lchan_type; + + /* Set external pointer */ + *prim = new_prim; + + return 0; +} + +int sched_trx_push_prim(struct trx_instance *trx, + struct trx_ts_prim *prim, uint8_t chan_nr) +{ + struct trx_ts *ts; + uint8_t tn; + + /* Determine TS index */ + tn = chan_nr & 0x7; + if (tn > 7) { + LOGP(DSCH, LOGL_ERROR, "Incorrect TS index %u\n", tn); + return -EINVAL; + } + + /* Check whether required timeslot is allocated and configured */ + ts = trx->ts_list[tn]; + if (ts == NULL || ts->mf_layout == NULL) { + LOGP(DSCH, LOGL_ERROR, "Timeslot %u isn't configured\n", tn); + return -EINVAL; + } + + /** + * Change talloc context of primitive + * from trx to the parent ts + */ + talloc_steal(ts, prim); + + /* Add primitive to TS transmit queue */ + llist_add_tail(&prim->list, &ts->tx_prims); + + return 0; +} + enum gsm_phys_chan_config sched_trx_chan_nr2pchan_config(uint8_t chan_nr) { uint8_t cbits = chan_nr >> 3; diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 1b74041c3..4209f533f 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -235,6 +235,8 @@ struct trx_ts_prim { struct llist_head list; /*! \brief Logical channel type */ enum trx_lchan_type chan; + /*! \brief Payload length */ + size_t payload_len; /*! \brief Payload */ uint8_t payload[0]; }; @@ -267,5 +269,11 @@ int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan); struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts, enum trx_lchan_type chan); +/* Primitive management functions */ +int sched_trx_init_prim(struct trx_instance *trx, struct trx_ts_prim **prim, + size_t pl_len, uint8_t chan_nr, uint8_t link_id); +int sched_trx_push_prim(struct trx_instance *trx, + struct trx_ts_prim *prim, uint8_t chan_nr); + int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, uint32_t burst_fn, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); From 3b1a03c31027e07554d17f7daf73e9f04735307a Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 15 Aug 2017 14:30:19 +0600 Subject: [PATCH 095/211] host/trxcon/l1ctl.c: use primitive management API for RACH Change-Id: I956ddfc4d1b47575715375c08f46c55953ec5fb6 --- src/host/trxcon/l1ctl.c | 47 ++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index f125948d3..658508435 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -462,8 +462,9 @@ static int l1ctl_rx_rach_req(struct l1ctl_link *l1l, struct msgb *msg) struct l1ctl_rach_req *req; struct l1ctl_info_ul *ul; struct trx_ts_prim *prim; - struct trx_ts *ts; - int len, rc = 0; + uint8_t chan_nr, link_id; + size_t len; + int rc; ul = (struct l1ctl_info_ul *) msg->l1h; req = (struct l1ctl_rach_req *) ul->payload; @@ -472,35 +473,37 @@ static int l1ctl_rx_rach_req(struct l1ctl_link *l1l, struct msgb *msg) /* Convert offset value to host format */ req->offset = ntohs(req->offset); + /** + * FIXME: l1ctl_info_ul doesn't provide channel description + * FIXME: Can we use other than TS0? + */ + chan_nr = 0x88; + link_id = 0x00; + LOGP(DL1C, LOGL_NOTICE, "Received RACH request " "(offset=%u ra=0x%02x)\n", req->offset, req->ra); - /* FIXME: can we use other than TS0? */ - ts = l1l->trx->ts_list[0]; - if (ts == NULL) { - LOGP(DL1C, LOGL_DEBUG, "Couldn't send RACH: " - "TS0 is not active\n"); - rc = -EINVAL; + /* Init a new primitive */ + rc = sched_trx_init_prim(l1l->trx, &prim, len, + chan_nr, link_id); + if (rc) + goto exit; + + /** + * Push this primitive to transmit queue + * + * FIXME: what if requested TS is not configured? + * Or what if one (such as TCH) has no TRXC_RACH slots? + */ + rc = sched_trx_push_prim(l1l->trx, prim, chan_nr); + if (rc) { + talloc_free(prim); goto exit; } - /* Allocate a new primitive */ - prim = talloc_zero_size(ts, sizeof(struct trx_ts_prim) + len); - if (prim == NULL) { - LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory\n"); - rc = -ENOMEM; - goto exit; - } - - /* Set logical channel of primitive */ - prim->chan = TRXC_RACH; - /* Fill in the payload */ memcpy(prim->payload, req, len); - /* Add to TS queue */ - llist_add_tail(&prim->list, &ts->tx_prims); - exit: msgb_free(msg); return rc; From 28088c8910171dda2d737a90a8cd40f13052ecc6 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 15 Aug 2017 16:03:02 +0600 Subject: [PATCH 096/211] host/trxcon/l1ctl.c: include DL frame info in L1CTL_DATA_CONF The l1ctl_info_dl header is expected to be a part of a L1CTL_DATA_CONF message, but was missing previously. Change-Id: Ia8dfaed924fd84395ba9ae539164eaa94f52d30b --- src/host/trxcon/l1ctl.c | 17 ++++++++++++--- src/host/trxcon/l1ctl.h | 3 ++- src/host/trxcon/sched_lchan_common.c | 31 ++++++++++++++++++++++++++++ src/host/trxcon/sched_lchan_xcch.c | 5 ++++- 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 658508435..5303130c9 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -225,15 +225,26 @@ int l1ctl_tx_rach_conf(struct l1ctl_link *l1l, uint32_t fn) return l1ctl_link_send(l1l, msg); } -int l1ctl_tx_data_conf(struct l1ctl_link *l1l) +int l1ctl_tx_data_conf(struct l1ctl_link *l1l, + struct l1ctl_info_dl *data, uint8_t msg_type) { + struct l1ctl_info_dl *dl; struct msgb *msg; + size_t len; - msg = l1ctl_alloc_msg(L1CTL_DATA_CONF); + if (msg_type != L1CTL_DATA_CONF && msg_type != L1CTL_TRAFFIC_CONF) { + LOGP(DL1D, LOGL_ERROR, "Incorrect confirmation type\n"); + return -EINVAL; + } + + msg = l1ctl_alloc_msg(msg_type); if (msg == NULL) return -ENOMEM; - LOGP(DL1D, LOGL_DEBUG, "Send Data Conf\n"); + /* Copy DL frame header from source message */ + len = sizeof(struct l1ctl_info_dl); + dl = (struct l1ctl_info_dl *) msgb_put(msg, len); + memcpy(dl, data, len); return l1ctl_link_send(l1l, msg); } diff --git a/src/host/trxcon/l1ctl.h b/src/host/trxcon/l1ctl.h index 4f48aaa0f..91a7f0f61 100644 --- a/src/host/trxcon/l1ctl.h +++ b/src/host/trxcon/l1ctl.h @@ -20,5 +20,6 @@ int l1ctl_tx_reset_ind(struct l1ctl_link *l1l, uint8_t type); int l1ctl_tx_data_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data, uint8_t msg_type); +int l1ctl_tx_data_conf(struct l1ctl_link *l1l, + struct l1ctl_info_dl *data, uint8_t msg_type); int l1ctl_tx_rach_conf(struct l1ctl_link *l1l, uint32_t fn); -int l1ctl_tx_data_conf(struct l1ctl_link *l1l); diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c index 7dad75022..5383ef423 100644 --- a/src/host/trxcon/sched_lchan_common.c +++ b/src/host/trxcon/sched_lchan_common.c @@ -112,3 +112,34 @@ int sched_send_data_ind(struct trx_instance *trx, struct trx_ts *ts, return 0; } + +int sched_send_data_conf(struct trx_instance *trx, struct trx_ts *ts, + struct trx_lchan_state *lchan, uint32_t fn, size_t l2_len) +{ + const struct trx_lchan_desc *lchan_desc; + struct l1ctl_info_dl *data; + uint8_t conf_type; + + /* Allocate memory */ + data = talloc_zero(ts, struct l1ctl_info_dl); + if (data == NULL) + return -ENOMEM; + + /* Set up pointers */ + lchan_desc = &trx_lchan_desc[lchan->type]; + + /* Fill in known downlink info */ + data->chan_nr = lchan_desc->chan_nr | ts->index; + data->link_id = lchan_desc->link_id; + data->band_arfcn = htons(trx->band_arfcn); + data->frame_nr = htonl(fn); + + /* Choose a confirmation type */ + conf_type = l2_len == 23 ? + L1CTL_DATA_CONF : L1CTL_TRAFFIC_CONF; + + l1ctl_tx_data_conf(trx->l1l, data, conf_type); + talloc_free(data); + + return 0; +} diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index 62c44f9a6..dd76d0a69 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -48,6 +48,9 @@ extern const uint8_t nb_training_bits[8][26]; int sched_send_data_ind(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len); +int sched_send_data_conf(struct trx_instance *trx, struct trx_ts *ts, + struct trx_lchan_state *lchan, uint32_t fn, size_t l2_len); + int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, sbit_t *bits, int8_t rssi, float toa) @@ -221,7 +224,7 @@ send_burst: *mask = 0x00; /* Confirm data sending */ - l1ctl_tx_data_conf(trx->l1l); + sched_send_data_conf(trx, ts, lchan, fn, 23); } return 0; From fd9aaaf3d0b00641fd26c114d44c0f963502e7c5 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 3 Aug 2017 20:18:05 +0600 Subject: [PATCH 097/211] host/trxcon/l1ctl.c: handle L1CTL_TCH_MODE_REQ Change-Id: Ib2332e1610fa873755cdfa745153c7b7d4a72a62 --- src/host/trxcon/l1ctl.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 5303130c9..6a9cdb3c2 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -657,6 +657,40 @@ static int l1ctl_rx_param_req(struct l1ctl_link *l1l, struct msgb *msg) return 0; } +static int l1ctl_rx_tch_mode_req(struct l1ctl_link *l1l, struct msgb *msg) +{ + struct l1ctl_tch_mode_req *req; + struct trx_ts *ts; + int len, i, j; + + req = (struct l1ctl_tch_mode_req *) msg->l1h; + + LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_TCH_MODE_REQ " + "(tch_mode=%u, audio_mode=%u)\n", req->tch_mode, req->audio_mode); + + /* Iterate over timeslot list */ + for (i = 0; i < TRX_TS_COUNT; i++) { + /* Timeslot is not allocated */ + ts = l1l->trx->ts_list[i]; + if (ts == NULL) + continue; + + /* Timeslot is not configured */ + if (ts->mf_layout == NULL) + continue; + + /* Iterate over all allocated lchans */ + len = talloc_array_length(ts->lchans); + for (j = 0; j < len; j++) + ts->lchans[j].tch_mode = req->tch_mode; + } + + /* TODO: do we need to care about audio_mode? */ + + msgb_free(msg); + return 0; +} + int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) { struct l1ctl_hdr *l1h; @@ -685,6 +719,8 @@ int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) return l1ctl_rx_data_req(l1l, msg); case L1CTL_PARAM_REQ: return l1ctl_rx_param_req(l1l, msg); + case L1CTL_TCH_MODE_REQ: + return l1ctl_rx_tch_mode_req(l1l, msg); default: LOGP(DL1C, LOGL_ERROR, "Unknown MSG: %u\n", l1h->msg_type); msgb_free(msg); From 863ccb7bd28e5342b84517afc01103b719f1d7ec Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 16 Aug 2017 13:36:20 +0600 Subject: [PATCH 098/211] host/trxcon/scheduler: share common declarations of lchan handlers The training sequences, data / traffic indication and confirmation helpers are used by several lchan handlers, like xCCC and TCH. It would be better to have them all declared within a shared header. Change-Id: I71980f09a0c0e023370e1a651afc24fff2491552 --- src/host/trxcon/sched_lchan_common.c | 2 +- src/host/trxcon/sched_lchan_xcch.c | 11 +---------- src/host/trxcon/sched_trx.h | 9 +++++++++ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c index 5383ef423..aa0614c31 100644 --- a/src/host/trxcon/sched_lchan_common.c +++ b/src/host/trxcon/sched_lchan_common.c @@ -41,7 +41,7 @@ #include "l1ctl.h" /* GSM 05.02 Chapter 5.2.3 Normal Burst (NB) */ -const uint8_t nb_training_bits[8][26] = { +const uint8_t sched_nb_training_bits[8][26] = { { 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index dd76d0a69..958d80d73 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -42,15 +42,6 @@ #include "trxcon.h" #include "l1ctl.h" -/* Forward declarations */ -extern const uint8_t nb_training_bits[8][26]; - -int sched_send_data_ind(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len); - -int sched_send_data_conf(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, size_t l2_len); - int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, sbit_t *bits, int8_t rssi, float toa) @@ -185,7 +176,7 @@ send_burst: *mask |= (1 << bid); /* Choose proper TSC */ - tsc = nb_training_bits[trx->tsc]; + tsc = sched_nb_training_bits[trx->tsc]; /* Compose a new burst */ memset(burst, 0, 3); /* TB */ diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 4209f533f..9d038fbea 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -277,3 +277,12 @@ int sched_trx_push_prim(struct trx_instance *trx, int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, uint32_t burst_fn, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); + +/* Shared declarations for lchan handlers */ +extern const uint8_t sched_nb_training_bits[8][26]; + +size_t sched_bad_frame_ind(uint8_t *l2, uint8_t rsl_cmode, uint8_t tch_mode); +int sched_send_data_ind(struct trx_instance *trx, struct trx_ts *ts, + struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len); +int sched_send_data_conf(struct trx_instance *trx, struct trx_ts *ts, + struct trx_lchan_state *lchan, uint32_t fn, size_t l2_len); From e38b500794456a353055639bedf91ac182d90419 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 16 Aug 2017 14:05:24 +0600 Subject: [PATCH 099/211] host/trxcon/scheduler: fix prim queue flushing function For some reasons, the function, which is used to flush a queue of transmit primitives, was intended to flush a list of msgb instances instead of trx_ts_prim, so memory was being cleaned incorrectly. Moreover, the items weren't actually removed from queue. Change-Id: Ia84b57350a5c2eee0afebc65f62e30eaddb141d4 --- src/host/trxcon/sched_trx.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 7f6729cb5..c7520b913 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -37,12 +37,14 @@ #include "trx_if.h" #include "logging.h" -static void msgb_queue_flush(struct llist_head *list) +static void prim_queue_flush(struct llist_head *list) { - struct msgb *msg, *msg2; + struct trx_ts_prim *prim, *prim_next; - llist_for_each_entry_safe(msg, msg2, list, list) - msgb_free(msg); + llist_for_each_entry_safe(prim, prim_next, list, list) { + llist_del(&prim->list); + talloc_free(prim); + } } static void sched_frame_clck_cb(struct trx_sched *sched) @@ -191,7 +193,7 @@ void sched_trx_del_ts(struct trx_instance *trx, int tn) LOGP(DSCH, LOGL_NOTICE, "Delete TDMA timeslot #%u\n", tn); /* Flush queue primitives for TX */ - msgb_queue_flush(&ts->tx_prims); + prim_queue_flush(&ts->tx_prims); /* Remove ts from list and free memory */ trx->ts_list[tn] = NULL; @@ -278,7 +280,7 @@ int sched_trx_reset_ts(struct trx_instance *trx, int tn) ts->mf_layout = NULL; /* Flush queue primitives for TX */ - msgb_queue_flush(&ts->tx_prims); + prim_queue_flush(&ts->tx_prims); /* Free channel states */ talloc_free(ts->lchans); From f979d44a72115050e3be95a7ce9bf29bffe964dc Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 19 Aug 2017 11:59:03 +0600 Subject: [PATCH 100/211] host/trxcon/trx_if.c: fix wrong logging category Change-Id: I0df0205e160fd9ea5811852077db7c49cddc7e8a --- src/host/trxcon/trx_if.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index 9c76c6d05..5820d7073 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -666,7 +666,7 @@ int trx_if_open(struct trx_instance **trx, const char *host, uint16_t port) return 0; error: - LOGP(DTRX, LOGL_NOTICE, "Couldn't establish UDP connection\n"); + LOGP(DTRX, LOGL_ERROR, "Couldn't establish UDP connection\n"); talloc_free(trx_new); return rc; } From 3641fe61238aabaa84ecbf14ca8eefe18acd5b18 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 19 Aug 2017 12:15:15 +0600 Subject: [PATCH 101/211] host/trxcon: use LOGP instead of fprintf There is no (performance) reason to use fprintf instead of LOGP. Second one provides more useful information, such as a file name and a line number. Change-Id: I86dda5b3d746c7802442e4226578a06c04941721 --- src/host/trxcon/l1ctl_link.c | 6 +++--- src/host/trxcon/trx_if.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/host/trxcon/l1ctl_link.c b/src/host/trxcon/l1ctl_link.c index c38a87155..3bbdf34b9 100644 --- a/src/host/trxcon/l1ctl_link.c +++ b/src/host/trxcon/l1ctl_link.c @@ -74,7 +74,7 @@ static int l1ctl_link_read_cb(struct osmo_fd *bfd) msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_HEADROOM, L1CTL_HEADROOM, "L1CTL"); if (!msg) { - fprintf(stderr, "Failed to allocate msg\n"); + LOGP(DL1D, LOGL_ERROR, "Failed to allocate msg\n"); return -ENOMEM; } @@ -236,7 +236,7 @@ int l1ctl_link_init(struct l1ctl_link **l1l, const char *sock_path) l1l_new = talloc_zero(tall_trx_ctx, struct l1ctl_link); if (!l1l_new) { - fprintf(stderr, "Failed to allocate memory\n"); + LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory\n"); return -ENOMEM; } @@ -245,7 +245,7 @@ int l1ctl_link_init(struct l1ctl_link **l1l, const char *sock_path) 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", + LOGP(DL1C, LOGL_ERROR, "Could not create UNIX socket: %s\n", strerror(errno)); talloc_free(l1l_new); return rc; diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index 5820d7073..368c47427 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -636,7 +636,7 @@ int trx_if_open(struct trx_instance **trx, const char *host, uint16_t port) /* Try to allocate memory */ trx_new = talloc_zero(tall_trx_ctx, struct trx_instance); if (!trx_new) { - fprintf(stderr, "Failed to allocate memory\n"); + LOGP(DTRX, LOGL_ERROR, "Failed to allocate memory\n"); return -ENOMEM; } From 5e9959cf6ae7e3b9e6992c63c71044817c26140f Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 19 Aug 2017 12:28:39 +0600 Subject: [PATCH 102/211] host/trxcon/trx_if.c: separate logging of data messages Change-Id: I74ebe0441aeb41c324eafb6b586b2edd9ef4fd1a --- src/host/trxcon/logging.c | 8 +++++++- src/host/trxcon/logging.h | 3 ++- src/host/trxcon/trx_if.c | 14 +++++++------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/host/trxcon/logging.c b/src/host/trxcon/logging.c index 338deaf82..119456b65 100644 --- a/src/host/trxcon/logging.c +++ b/src/host/trxcon/logging.c @@ -48,7 +48,13 @@ static struct log_info_cat trx_log_info_cat[] = { }, [DTRX] = { .name = "DTRX", - .description = "Transceiver interface", + .description = "Transceiver control interface", + .color = "\033[1;33m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DTRXD] = { + .name = "DTRXD", + .description = "Transceiver data interface", .color = "\033[1;33m", .enabled = 1, .loglevel = LOGL_NOTICE, }, diff --git a/src/host/trxcon/logging.h b/src/host/trxcon/logging.h index 22f325176..6221c55ec 100644 --- a/src/host/trxcon/logging.h +++ b/src/host/trxcon/logging.h @@ -2,13 +2,14 @@ #include -#define DEBUG_DEFAULT "DAPP:DL1C:DL1D:DTRX:DSCH" +#define DEBUG_DEFAULT "DAPP:DL1C:DL1D:DTRX:DTRXD:DSCH" enum { DAPP, DL1C, DL1D, DTRX, + DTRXD, DSCH, }; diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index 368c47427..6a84af62d 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -545,8 +545,8 @@ static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what) return len; if (len != 158) { - LOGP(DTRX, LOGL_ERROR, "Got data message with invalid length " - "'%d'\n", len); + LOGP(DTRXD, LOGL_ERROR, "Got data message with invalid " + "length '%d'\n", len); return -EINVAL; } @@ -564,16 +564,16 @@ static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what) } if (tn >= 8) { - LOGP(DTRX, LOGL_ERROR, "Illegal TS %d\n", tn); + LOGP(DTRXD, LOGL_ERROR, "Illegal TS %d\n", tn); return -EINVAL; } if (fn >= 2715648) { - LOGP(DTRX, LOGL_ERROR, "Illegal FN %u\n", fn); + LOGP(DTRXD, LOGL_ERROR, "Illegal FN %u\n", fn); return -EINVAL; } - LOGP(DTRX, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%.2f\n", + LOGP(DTRXD, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%.2f\n", tn, fn, rssi, toa); /* Poke scheduler */ @@ -598,12 +598,12 @@ int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn, * TODO: should we wait in TRX_STATE_RSP_WAIT state? */ if (trx->fsm->state != TRX_STATE_ACTIVE) { - LOGP(DTRX, LOGL_DEBUG, "Ignoring TX data, " + LOGP(DTRXD, LOGL_DEBUG, "Ignoring TX data, " "transceiver isn't ready\n"); return -EAGAIN; } - LOGP(DTRX, LOGL_DEBUG, "TX burst tn=%u fn=%u pwr=%u\n", tn, fn, pwr); + LOGP(DTRXD, LOGL_DEBUG, "TX burst tn=%u fn=%u pwr=%u\n", tn, fn, pwr); buf[0] = tn; buf[1] = (fn >> 24) & 0xff; From ac764e78fd1f14e29b7a686697a5c459c58f3f8b Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 19 Aug 2017 12:38:24 +0600 Subject: [PATCH 103/211] host/trxcon/scheduler: separate logging of data messages Change-Id: I3a33687a688db2a183b546425f71c7a0a7030594 --- src/host/trxcon/logging.c | 8 +++++++- src/host/trxcon/logging.h | 3 ++- src/host/trxcon/sched_lchan_rach.c | 6 +++--- src/host/trxcon/sched_lchan_sch.c | 6 +++--- src/host/trxcon/sched_lchan_xcch.c | 12 ++++++------ src/host/trxcon/sched_trx.c | 2 +- 6 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/host/trxcon/logging.c b/src/host/trxcon/logging.c index 119456b65..a76b4d97b 100644 --- a/src/host/trxcon/logging.c +++ b/src/host/trxcon/logging.c @@ -60,7 +60,13 @@ static struct log_info_cat trx_log_info_cat[] = { }, [DSCH] = { .name = "DSCH", - .description = "Scheduler", + .description = "Scheduler management", + .color = "\033[1;36m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DSCHD] = { + .name = "DSCHD", + .description = "Scheduler data", .color = "\033[1;36m", .enabled = 1, .loglevel = LOGL_NOTICE, }, diff --git a/src/host/trxcon/logging.h b/src/host/trxcon/logging.h index 6221c55ec..0206362a5 100644 --- a/src/host/trxcon/logging.h +++ b/src/host/trxcon/logging.h @@ -2,7 +2,7 @@ #include -#define DEBUG_DEFAULT "DAPP:DL1C:DL1D:DTRX:DTRXD:DSCH" +#define DEBUG_DEFAULT "DAPP:DL1C:DL1D:DTRX:DTRXD:DSCH:DSCHD" enum { DAPP, @@ -11,6 +11,7 @@ enum { DTRX, DTRXD, DSCH, + DSCHD, }; int trx_log_init(const char *category_mask); diff --git a/src/host/trxcon/sched_lchan_rach.c b/src/host/trxcon/sched_lchan_rach.c index 613f644de..820178364 100644 --- a/src/host/trxcon/sched_lchan_rach.c +++ b/src/host/trxcon/sched_lchan_rach.c @@ -73,7 +73,7 @@ int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, /* Encode payload */ rc = gsm0503_rach_encode(payload, &req->ra, trx->bsic); if (rc) { - LOGP(DSCH, LOGL_ERROR, "Could not encode RACH burst\n"); + LOGP(DSCHD, LOGL_ERROR, "Could not encode RACH burst\n"); return rc; } @@ -83,12 +83,12 @@ int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, memcpy(burst + 49, payload, 36); /* payload */ memset(burst + 85, 0, 63); /* TB + GP */ - LOGP(DSCH, LOGL_DEBUG, "Transmitting RACH fn=%u\n", fn); + LOGP(DSCHD, LOGL_DEBUG, "Transmitting RACH fn=%u\n", fn); /* Send burst to transceiver */ rc = trx_if_tx_burst(trx, ts->index, fn, trx->tx_power, burst); if (rc) { - LOGP(DSCH, LOGL_ERROR, "Could not send burst to transceiver\n"); + LOGP(DSCHD, LOGL_ERROR, "Could not send burst to transceiver\n"); return rc; } diff --git a/src/host/trxcon/sched_lchan_sch.c b/src/host/trxcon/sched_lchan_sch.c index db73b926d..6b4543f59 100644 --- a/src/host/trxcon/sched_lchan_sch.c +++ b/src/host/trxcon/sched_lchan_sch.c @@ -87,19 +87,19 @@ int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, /* Attempt to decode */ rc = gsm0503_sch_decode(sb_info, payload); if (rc) { - LOGP(DSCH, LOGL_DEBUG, "Received bad SCH burst at fn=%u\n", fn); + LOGP(DSCHD, LOGL_DEBUG, "Received bad SCH burst at fn=%u\n", fn); return rc; } /* Decode BSIC and TDMA frame number */ decode_sb(&time, &bsic, sb_info); - LOGP(DSCH, LOGL_DEBUG, "Received SCH: bsic=%u, fn=%u, sched_fn=%u\n", + LOGP(DSCHD, LOGL_DEBUG, "Received SCH: bsic=%u, fn=%u, sched_fn=%u\n", bsic, time.fn, trx->sched.fn_counter_proc); /* Check if decoded frame number matches */ if (time.fn != fn) { - LOGP(DSCH, LOGL_ERROR, "Decoded fn=%u does not match " + LOGP(DSCHD, LOGL_ERROR, "Decoded fn=%u does not match " "fn=%u provided by scheduler\n", time.fn, fn); return -EINVAL; } diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index 958d80d73..f57c8fceb 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -65,7 +65,7 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, toa_sum = &lchan->toa_sum; toa_num = &lchan->toa_num; - LOGP(DSCH, LOGL_DEBUG, "Data received on %s: fn=%u ts=%u bid=%u\n", + LOGP(DSCHD, LOGL_DEBUG, "Data received on %s: fn=%u ts=%u bid=%u\n", lchan_desc->name, fn, ts->index, bid); /* Clear buffer & store frame number of first burst */ @@ -99,7 +99,7 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, /* Check for complete set of bursts */ if ((*mask & 0xf) != 0xf) { - LOGP(DSCH, LOGL_DEBUG, "Received incomplete data frame at " + LOGP(DSCHD, LOGL_DEBUG, "Received incomplete data frame at " "fn=%u (%u/%u) for %s\n", *first_fn, (*first_fn) % ts->mf_layout->period, ts->mf_layout->period, @@ -111,7 +111,7 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, /* Attempt to decode */ rc = gsm0503_xcch_decode(l2, buffer, &n_errors, &n_bits_total); if (rc) { - LOGP(DSCH, LOGL_DEBUG, "Received bad data frame at fn=%u " + LOGP(DSCHD, LOGL_DEBUG, "Received bad data frame at fn=%u " "(%u/%u) for %s\n", *first_fn, (*first_fn) % ts->mf_layout->period, ts->mf_layout->period, @@ -159,7 +159,7 @@ int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts, /* Encode bursts */ rc = gsm0503_xcch_encode(buffer, l2); if (rc) { - LOGP(DSCH, LOGL_ERROR, "Failed to encode L2 payload\n"); + LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload\n"); /* Remove primitive from queue and free memory */ llist_del(&prim->list); @@ -185,13 +185,13 @@ send_burst: memcpy(burst + 87, offset + 58, 58); /* Payload 2/2 */ memset(burst + 145, 0, 3); /* TB */ - LOGP(DSCH, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n", + LOGP(DSCHD, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n", lchan_desc->name, fn, ts->index, bid); /* Send burst to transceiver */ rc = trx_if_tx_burst(trx, ts->index, fn, trx->tx_power, burst); if (rc) { - LOGP(DSCH, LOGL_ERROR, "Could not send burst to transceiver\n"); + LOGP(DSCHD, LOGL_ERROR, "Could not send burst to transceiver\n"); /* Remove primitive from queue and free memory */ prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index c7520b913..b7ebfeb2e 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -533,7 +533,7 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, /* Check whether required timeslot is allocated and configured */ ts = trx->ts_list[tn]; if (ts == NULL || ts->mf_layout == NULL) { - LOGP(DSCH, LOGL_DEBUG, "TDMA timeslot #%u isn't configured, " + LOGP(DSCHD, LOGL_DEBUG, "TDMA timeslot #%u isn't configured, " "ignoring burst...\n", tn); return -EINVAL; } From e8cf6c4eefbd7429b76da0878672d00143412ae1 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sun, 19 Nov 2017 15:41:19 +0700 Subject: [PATCH 104/211] host/trxcon: fix: use valid names for FSM instances Since 8c4f5457 in libosmocore there are some limitations on FSM and FSM instance names. This change adjusts the names of both l1ctl_fsm and trx_fsm instances. Change-Id: Icaaac3f51bdcfe4f7723060179b8730c3a06529b --- src/host/trxcon/l1ctl_link.c | 2 +- src/host/trxcon/trx_if.c | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/host/trxcon/l1ctl_link.c b/src/host/trxcon/l1ctl_link.c index 3bbdf34b9..39056547f 100644 --- a/src/host/trxcon/l1ctl_link.c +++ b/src/host/trxcon/l1ctl_link.c @@ -268,7 +268,7 @@ int l1ctl_link_init(struct l1ctl_link **l1l, const char *sock_path) /* Allocate a new dedicated state machine */ osmo_fsm_register(&l1ctl_fsm); l1l_new->fsm = osmo_fsm_inst_alloc(&l1ctl_fsm, l1l_new, - NULL, LOGL_DEBUG, sock_path); + NULL, LOGL_DEBUG, "l1ctl_link"); *l1l = l1l_new; diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index 6a84af62d..d7f496eb1 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -628,7 +628,6 @@ int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn, int trx_if_open(struct trx_instance **trx, const char *host, uint16_t port) { struct trx_instance *trx_new; - char *inst_name; int rc; LOGP(DTRX, LOGL_NOTICE, "Init transceiver interface\n"); @@ -656,10 +655,8 @@ int trx_if_open(struct trx_instance **trx, const char *host, uint16_t port) /* Allocate a new dedicated state machine */ osmo_fsm_register(&trx_fsm); - inst_name = talloc_asprintf(trx_new, "%s:%u", host, port); trx_new->fsm = osmo_fsm_inst_alloc(&trx_fsm, trx_new, - NULL, LOGL_DEBUG, inst_name); - talloc_free(inst_name); + NULL, LOGL_DEBUG, "trx_interface"); *trx = trx_new; From be7825d9834370f18cc42c7d8f1e9f85e4d7518c Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sun, 19 Nov 2017 17:16:24 +0700 Subject: [PATCH 105/211] fake_trx/ctrl_cmd.py: add help and basic command line options Sometimes it's important to use different CTRL port, for example when OsmoTRX is running at the same time. This change adds the corresponding command line options and help message. Change-Id: Ic6eeb69d9a1fc151eab2e63f3708e3d70e2e558b --- src/target/fake_trx/ctrl_cmd.py | 61 ++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/src/target/fake_trx/ctrl_cmd.py b/src/target/fake_trx/ctrl_cmd.py index 30b19bdc6..b7aec50bd 100755 --- a/src/target/fake_trx/ctrl_cmd.py +++ b/src/target/fake_trx/ctrl_cmd.py @@ -23,6 +23,7 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import signal +import getopt import select import sys @@ -36,18 +37,62 @@ COPYRIGHT = \ "There is NO WARRANTY, to the extent permitted by law.\n" class Application: - def __init__(self, remote_addr, remote_port, bind_port, fuz = False): - # Init UDP connection - self.ctrl_link = UDPLink(remote_addr, remote_port, bind_port) + # Application variables + remote_addr = "127.0.0.1" + base_port = 5700 + fuzzing = False - # Determine working mode - self.fuzzing = fuz + def __init__(self): + print(COPYRIGHT) + self.parse_argv() # Set up signal handlers signal.signal(signal.SIGINT, self.sig_handler) - # Print copyright - print(COPYRIGHT) + # Init UDP connection + self.ctrl_link = UDPLink(self.remote_addr, + self.base_port + 1, self.base_port + 101) + + def print_help(self, msg = None): + s = " Usage: " + sys.argv[0] + " [options]\n\n" \ + " Some help...\n" \ + " -h --help this text\n\n" + + s += " TRX interface specific\n" \ + " -r --remote-addr Set remote address (default %s)\n" \ + " -p --base-port Set base port number (default %d)\n" \ + " -f --fuzzing Send raw payloads (without CMD)\n" \ + + print(s % (self.remote_addr, self.base_port)) + + if msg is not None: + print(msg) + + def parse_argv(self): + try: + opts, args = getopt.getopt(sys.argv[1:], + "r:p:fh", + [ + "help", + "fuzzing", + "base-port=", + "remote-addr=", + ]) + except getopt.GetoptError as err: + self.print_help("[!] " + str(err)) + sys.exit(2) + + for o, v in opts: + if o in ("-h", "--help"): + self.print_help() + sys.exit(2) + + elif o in ("-r", "--remote-addr"): + self.remote_addr = v + elif o in ("-p", "--base-port"): + self.base_port = int(v) + elif o in ("-f", "--fuzzing"): + self.fuzzing = True def run(self): while True: @@ -88,5 +133,5 @@ class Application: sys.exit(0) if __name__ == '__main__': - app = Application("127.0.0.1", 5701, 5801) + app = Application() app.run() From 826c515f4ed872c37212c6e48bf9f3118e18024a Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 21 Nov 2017 17:42:19 +0700 Subject: [PATCH 106/211] fake_trx/README: correct the branch name Change-Id: I9d65580570d9bced65a7bcc95ca073ecc3e130e5 --- src/target/fake_trx/README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/target/fake_trx/README b/src/target/fake_trx/README index 6b1c41344..883a5f9b6 100644 --- a/src/target/fake_trx/README +++ b/src/target/fake_trx/README @@ -5,7 +5,7 @@ software implements OsmoTRX (Osmocom's fork of OpenBTS transceiver) style clock (CLCK), control (CTRL) and data interfaces. So, OsmoBTS source code doesn't require any modifications, while for OsmocomBB you will need to use a new application - trxcon, which can be found -in the 'fixeria/sdr_phy' branch until one is merged to master. +in the 'fixeria/trx' branch until one is merged to master. Brief description of available applications: From 3a395740d3b314852a923ae13b89bfebb6cfa5f9 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 21 Nov 2017 17:42:51 +0700 Subject: [PATCH 107/211] fake_trx/burst_gen.py: remove unused import Change-Id: I407877e2025663f706d1a2f93e6228770bfc253c --- src/target/fake_trx/burst_gen.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/target/fake_trx/burst_gen.py b/src/target/fake_trx/burst_gen.py index e61cc6404..e989d30c2 100755 --- a/src/target/fake_trx/burst_gen.py +++ b/src/target/fake_trx/burst_gen.py @@ -25,7 +25,6 @@ import random import signal import getopt -import select import sys from rand_burst_gen import RandBurstGen From eb56da374326f0af7b94b43b79964ee4f3efcf00 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 21 Nov 2017 18:25:43 +0700 Subject: [PATCH 108/211] fake_trx: implement a new tool for burst sending This change introduces a new tool for sending existing bursts from file or standard input either to L1 (OsmoBTS or OsmocomBB) or to TRX (OsmoTRX and GR-GSM TRX). Change-Id: I2c542583252d31daac466e6c7837317fda8a7020 --- src/target/fake_trx/README | 6 +- src/target/fake_trx/burst_send.py | 189 ++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+), 1 deletion(-) create mode 100755 src/target/fake_trx/burst_send.py diff --git a/src/target/fake_trx/README b/src/target/fake_trx/README index 883a5f9b6..6e3ce03a5 100644 --- a/src/target/fake_trx/README +++ b/src/target/fake_trx/README @@ -25,5 +25,9 @@ Brief description of available applications: - burst_gen.py - a tool for sending GSM bursts either to L1 (OsmoBTS or OsmocomBB) or to TRX (OsmoTRX and GR-GSM TRX). - Currently it is only possible to send random bursts of + Currently it is only possible to generate random bursts of different types: NB, FB, SB, AB. + + - burst_send.py - a tool for sending existing bursts from file + or standard input either to L1 (OsmoBTS or OsmocomBB) or to + TRX (OsmoTRX and GR-GSM TRX). diff --git a/src/target/fake_trx/burst_send.py b/src/target/fake_trx/burst_send.py new file mode 100755 index 000000000..4d5c47fc5 --- /dev/null +++ b/src/target/fake_trx/burst_send.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# Simple tool to send bursts via TRX DATA interface +# +# (C) 2017 by Vadim Yanitskiy +# +# 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. + +import random +import signal +import getopt +import sys + +from data_if import DATAInterface + +COPYRIGHT = \ + "Copyright (C) 2017 by Vadim Yanitskiy \n" \ + "License GPLv2+: GNU GPL version 2 or later " \ + "\n" \ + "This is free software: you are free to change and redistribute it.\n" \ + "There is NO WARRANTY, to the extent permitted by law.\n" + +class Application: + # Application variables + remote_addr = "127.0.0.1" + base_port = 5700 + conn_mode = "TRX" + + burst_src = None + pwr = None + fn = None + tn = None + + def __init__(self): + self.print_copyright() + self.parse_argv() + + # Set up signal handlers + signal.signal(signal.SIGINT, self.sig_handler) + + def run(self): + # Init DATA interface with TRX or L1 + if self.conn_mode == "TRX": + self.data_if = DATAInterface(self.remote_addr, + self.base_port + 2, self.base_port + 102) + elif self.conn_mode == "L1": + self.data_if = DATAInterface(self.remote_addr, + self.base_port + 102, self.base_port + 2) + else: + self.print_help("[!] Unknown connection type") + sys.exit(2) + + # Open the burst source (file or stdin) + if self.burst_src is not None: + src = open(self.burst_src, "r") + else: + src = sys.stdin + + # Generate a random frame number or use provided one + if self.fn is None: + fn = random.randint(0, DATAInterface.GSM_HYPERFRAME) + else: + fn = self.fn + + # Read the burst source line-by-line + for line in src: + # Strip spaces + burst = line.strip() + buf = [] + + # Check length + if len(burst) != 148: + print("[!] Dropping burst due to length != 148") + continue + + print("[i] Sending a burst (fn=%u) to %s..." + % (fn, self.conn_mode)) + + # Parse a string + for bit in burst: + if bit == "1": + buf.append(1) + else: + buf.append(0) + + # Send to TRX or L1 + if self.conn_mode == "TRX": + self.data_if.send_trx_msg(buf, + self.tn, fn, self.pwr) + elif self.conn_mode == "L1": + self.data_if.send_l1_msg(buf, + self.tn, fn, self.pwr) + + # Increase frame number (for count > 1) + fn = (fn + 1) % DATAInterface.GSM_HYPERFRAME + + # Finish + self.shutdown() + + def print_copyright(self): + print(COPYRIGHT) + + def print_help(self, msg = None): + s = " Usage: " + sys.argv[0] + " [options]\n\n" \ + " Some help...\n" \ + " -h --help this text\n\n" + + s += " TRX interface specific\n" \ + " -m --conn-mode Send bursts to: TRX (default) / L1\n" \ + " -r --remote-addr Set remote address (default %s)\n" \ + " -p --base-port Set base port number (default %d)\n\n" + + s += " Burst generation\n" \ + " -i --burst-file Read bursts from file (default stdin)\n" \ + " -f --frame-number Set frame number (default random)\n" \ + " -t --timeslot Set timeslot index (default random)\n" \ + " -l --power-level Set transmit level (default random)\n" \ + + print(s % (self.remote_addr, self.base_port)) + + if msg is not None: + print(msg) + + def parse_argv(self): + try: + opts, args = getopt.getopt(sys.argv[1:], + "m:r:p:i:f:t:l:h", + [ + "help", + "conn-mode=", + "remote-addr=", + "base-port=", + "burst-file=", + "frame-number=", + "timeslot=", + "power-level=", + ]) + except getopt.GetoptError as err: + self.print_help("[!] " + str(err)) + sys.exit(2) + + for o, v in opts: + if o in ("-h", "--help"): + self.print_help() + sys.exit(2) + + elif o in ("-m", "--conn-mode"): + self.conn_mode = v + elif o in ("-r", "--remote-addr"): + self.remote_addr = v + elif o in ("-p", "--base-port"): + self.base_port = int(v) + + elif o in ("-i", "--burst-file"): + self.burst_src = v + elif o in ("-f", "--frame-number"): + self.fn = int(v) + elif o in ("-t", "--timeslot"): + self.tn = int(v) + elif o in ("-l", "--power-level"): + self.pwr = abs(int(v)) + + def shutdown(self): + self.data_if.shutdown() + + def sig_handler(self, signum, frame): + print("Signal %d received" % signum) + if signum is signal.SIGINT: + self.shutdown() + sys.exit(0) + +if __name__ == '__main__': + app = Application() + app.run() From 04aecc7f8d37aa93f2313dd04c4052000097e696 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 21 Nov 2017 18:35:24 +0700 Subject: [PATCH 109/211] fake_trx: correct brief descriptions of files Change-Id: Ie76fee4a567681a5380be90e5744621c2aa3e5f0 --- src/target/fake_trx/burst_gen.py | 4 ++-- src/target/fake_trx/burst_send.py | 2 +- src/target/fake_trx/ctrl_cmd.py | 4 ++-- src/target/fake_trx/data_if.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/target/fake_trx/burst_gen.py b/src/target/fake_trx/burst_gen.py index e989d30c2..087fe791d 100755 --- a/src/target/fake_trx/burst_gen.py +++ b/src/target/fake_trx/burst_gen.py @@ -1,8 +1,8 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -# Simple tool to send custom messages via TRX DATA interface, -# which may be also useful for fuzzing and testing +# Auxiliary tool to generate and send random bursts via TRX DATA +# interface, which may be useful for fuzzing and testing # # (C) 2017 by Vadim Yanitskiy # diff --git a/src/target/fake_trx/burst_send.py b/src/target/fake_trx/burst_send.py index 4d5c47fc5..bed6b446e 100755 --- a/src/target/fake_trx/burst_send.py +++ b/src/target/fake_trx/burst_send.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -# Simple tool to send bursts via TRX DATA interface +# Auxiliary tool to send existing bursts via TRX DATA interface # # (C) 2017 by Vadim Yanitskiy # diff --git a/src/target/fake_trx/ctrl_cmd.py b/src/target/fake_trx/ctrl_cmd.py index b7aec50bd..377a1f26e 100755 --- a/src/target/fake_trx/ctrl_cmd.py +++ b/src/target/fake_trx/ctrl_cmd.py @@ -1,8 +1,8 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -# Simple tool to send custom commands via TRX CTRL interface, -# which may be also useful for fuzzing +# Auxiliary tool to send custom commands via TRX CTRL interface, +# which may be useful for testing and fuzzing # # (C) 2017 by Vadim Yanitskiy # diff --git a/src/target/fake_trx/data_if.py b/src/target/fake_trx/data_if.py index 315281cd5..6662f984a 100644 --- a/src/target/fake_trx/data_if.py +++ b/src/target/fake_trx/data_if.py @@ -1,8 +1,8 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -# Simple tool to send custom messages via TRX DATA interface, -# which may be also useful for fuzzing and testing +# Virtual Um-interface (fake transceiver) +# DATA interface implementation # # (C) 2017 by Vadim Yanitskiy # From cd9b850ee4ac4c0a876102924d7456b327a1b250 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 23 Nov 2017 20:05:00 +0700 Subject: [PATCH 110/211] host/trxcon/scheduler: process frames in advance In order to get the transceiver more time to process bursts, the L1 scheduler should process the frames and send the bursts in advance (a few frames before), like OsmoBTS does. By default, the advance value is 20 frames, but this can be adjusted using a new command line option of trxcon '-f'. Change-Id: Ic258a169f3554f931d6277e18ca060d029b77f32 --- src/host/trxcon/sched_trx.c | 13 +++++++++++-- src/host/trxcon/sched_trx.h | 2 +- src/host/trxcon/scheduler.h | 2 ++ src/host/trxcon/trxcon.c | 11 +++++++++-- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index b7ebfeb2e..02da24127 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -75,8 +75,14 @@ static void sched_frame_clck_cb(struct trx_sched *sched) if (llist_empty(&ts->tx_prims)) continue; + /** + * Advance frame number, giving the transceiver more + * time until a burst must be transmitted... + */ + fn = (sched->fn_counter_proc + sched->fn_counter_advance) + % GSM_HYPERFRAME; + /* Get frame from multiframe */ - fn = sched->fn_counter_proc; offset = fn % ts->mf_layout->period; frame = ts->mf_layout->frames + offset; @@ -103,7 +109,7 @@ static void sched_frame_clck_cb(struct trx_sched *sched) } } -int sched_trx_init(struct trx_instance *trx) +int sched_trx_init(struct trx_instance *trx, uint32_t fn_advance) { struct trx_sched *sched; @@ -122,6 +128,9 @@ int sched_trx_init(struct trx_instance *trx) sched = &trx->sched; sched->data = trx; + /* Set frame counter advance */ + sched->fn_counter_advance = fn_advance; + return 0; } diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 9d038fbea..e1ef92448 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -246,7 +246,7 @@ const struct trx_multiframe *sched_mframe_layout( enum gsm_phys_chan_config config, int tn); /* Scheduler management functions */ -int sched_trx_init(struct trx_instance *trx); +int sched_trx_init(struct trx_instance *trx, uint32_t fn_advance); int sched_trx_reset(struct trx_instance *trx, int reset_clock); int sched_trx_shutdown(struct trx_instance *trx); diff --git a/src/host/trxcon/scheduler.h b/src/host/trxcon/scheduler.h index b025d918c..f36c3b2b4 100644 --- a/src/host/trxcon/scheduler.h +++ b/src/host/trxcon/scheduler.h @@ -24,6 +24,8 @@ struct trx_sched { struct timeval clock; /*! \brief Count of processed frames */ uint32_t fn_counter_proc; + /*! \brief Local frame counter advance */ + uint32_t fn_counter_advance; /*! \brief Frame counter */ uint32_t fn_counter_lost; /*! \brief Frame callback timer */ diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index 4a571b9ae..07ab16981 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -69,6 +69,7 @@ static struct { struct trx_instance *trx; const char *trx_ip; uint16_t trx_base_port; + uint32_t trx_fn_advance; } app_data; void *tall_trx_ctx = NULL; @@ -143,6 +144,7 @@ static void print_help(void) printf(" -d --debug Change debug flags. Default: %s\n", DEBUG_DEFAULT); printf(" -i --trx-ip IP address of host runing TRX (default 127.0.0.1)\n"); printf(" -p --trx-port Base port of TRX instance (default 5700)\n"); + printf(" -f --trx-advance Scheduler clock advance (default 20)\n"); printf(" -s --socket Listening socket for layer23 (default /tmp/osmocom_l2)\n"); printf(" -D --daemonize Run as daemon\n"); } @@ -157,11 +159,12 @@ static void handle_options(int argc, char **argv) {"socket", 1, 0, 's'}, {"trx-ip", 1, 0, 'i'}, {"trx-port", 1, 0, 'p'}, + {"trx-advance", 1, 0, 'f'}, {"daemonize", 0, 0, 'D'}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, "d:i:p:s:Dh", + c = getopt_long(argc, argv, "d:i:p:f:s:Dh", long_options, &option_index); if (c == -1) break; @@ -181,6 +184,9 @@ static void handle_options(int argc, char **argv) case 'p': app_data.trx_base_port = atoi(optarg); break; + case 'f': + app_data.trx_fn_advance = atoi(optarg); + break; case 's': app_data.bind_socket = optarg; break; @@ -198,6 +204,7 @@ static void init_defaults(void) app_data.bind_socket = "/tmp/osmocom_l2"; app_data.trx_ip = "127.0.0.1"; app_data.trx_base_port = 5700; + app_data.trx_fn_advance = 20; app_data.debug_mask = NULL; app_data.daemonize = 0; @@ -263,7 +270,7 @@ int main(int argc, char **argv) app_data.trx->l1l = app_data.l1l; /* Init scheduler */ - rc = sched_trx_init(app_data.trx); + rc = sched_trx_init(app_data.trx, app_data.trx_fn_advance); if (rc) goto exit; From c5d9507b5ddd04d4ac14dc009b6df20c3098e2cc Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 4 Dec 2017 00:18:54 +0700 Subject: [PATCH 111/211] host/trxcon/trx_ic.c: use osmo_ubit2sbit() from libosmocore No need to reimplement the existing functions... Change-Id: Ic9b232c8561609d42dac10e6249a3e1c58c4edc1 --- src/host/trxcon/trx_if.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index d7f496eb1..1f5edb28e 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -537,8 +537,8 @@ static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what) sbit_t bits[148]; int8_t rssi, tn; uint32_t fn; - int len, i; float toa; + int len; len = recv(ofd->fd, buf, sizeof(buf), 0); if (len <= 0) @@ -556,12 +556,7 @@ static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what) toa = ((int16_t) (buf[6] << 8) | buf[7]) / 256.0F; /* Copy and convert bits {254..0} to sbits {-127..127} */ - for (i = 0; i < 148; i++) { - if (buf[8 + i] == 255) - bits[i] = -127; - else - bits[i] = 127 - buf[8 + i]; - } + osmo_ubit2sbit(bits, buf + 8, 148); if (tn >= 8) { LOGP(DTRXD, LOGL_ERROR, "Illegal TS %d\n", tn); From 0d9680e88ae4aabe54fea89e21f2a5fc14f6d8d6 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 4 Dec 2017 20:58:22 +0700 Subject: [PATCH 112/211] host/trxcon/trx_if.c: get rid of useless commands The 'SETMAXDLY' command is used on the BTS side to limit maximal Time of Arrival for access bursts. As we don't receive RACH bursts on the MS side, the command is useless. The 'SETRXGAIN' command is used on the BTS side to set initial receive gain value for TRX. On the MS side it's possible to set that parameter via command-line options of TRX. Change-Id: I3e61b4b48193004cdcb241cefabb44c12db93120 --- src/host/trxcon/trx_if.c | 10 ---------- src/host/trxcon/trx_if.h | 3 --- 2 files changed, 13 deletions(-) diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index 1f5edb28e..c9bf6c87a 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -299,16 +299,6 @@ int trx_if_cmd_adjpower(struct trx_instance *trx, int db) return trx_ctrl_cmd(trx, 0, "ADJPOWER", "%d", db); } -int trx_if_cmd_setrxgain(struct trx_instance *trx, int db) -{ - return trx_ctrl_cmd(trx, 0, "SETRXGAIN", "%d", db); -} - -int trx_if_cmd_setmaxdly(struct trx_instance *trx, int dly) -{ - return trx_ctrl_cmd(trx, 0, "SETMAXDLY", "%d", dly); -} - /* * Timeslot Control * diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h index fbfa8b5e6..02c9ff787 100644 --- a/src/host/trxcon/trx_if.h +++ b/src/host/trxcon/trx_if.h @@ -63,9 +63,6 @@ int trx_if_cmd_echo(struct trx_instance *trx); int trx_if_cmd_setpower(struct trx_instance *trx, int db); int trx_if_cmd_adjpower(struct trx_instance *trx, int db); -int trx_if_cmd_setrxgain(struct trx_instance *trx, int db); -int trx_if_cmd_setmaxdly(struct trx_instance *trx, int dly); - int trx_if_cmd_rxtune(struct trx_instance *trx, uint16_t arfcn); int trx_if_cmd_txtune(struct trx_instance *trx, uint16_t arfcn); From 8ed6f42772f3002bfa1b8ef8772b138e8cddd443 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 4 Dec 2017 23:49:29 +0700 Subject: [PATCH 113/211] host/trxcon: forward Timing Advance value to transceiver The time at which the phone is allowed to transmit a burst of traffic within a timeslot must be adjusted accordingly to prevent collisions with adjacent users. Timing Advance (TA) is the variable controlling this adjustment. The TA value is normally between 0 and 63, with each step representing an advance of one bit period (approximately 3.69 microseconds). As trxcon doesn't perform actual burst transmission, this value needs to be forwarded to the transceiver, which will take care about the timings. Change-Id: Ia8c0848827ab2b4cd7cf1efe128b28d5c06ec84e --- src/host/trxcon/l1ctl.c | 5 ++++- src/host/trxcon/trx_if.c | 26 ++++++++++++++++++++++++++ src/host/trxcon/trx_if.h | 2 ++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 6a9cdb3c2..aeb6c07ee 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -643,6 +643,7 @@ static int l1ctl_rx_param_req(struct l1ctl_link *l1l, struct msgb *msg) { struct l1ctl_par_req *par_req; struct l1ctl_info_ul *ul; + int rc = 0; ul = (struct l1ctl_info_ul *) msg->l1h; par_req = (struct l1ctl_par_req *) ul->payload; @@ -650,11 +651,13 @@ static int l1ctl_rx_param_req(struct l1ctl_link *l1l, struct msgb *msg) LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_PARAM_REQ " "(ta=%d, tx_power=%u)\n", par_req->ta, par_req->tx_power); + rc |= trx_if_cmd_setta(l1l->trx, par_req->ta); + l1l->trx->ta = par_req->ta; l1l->trx->tx_power = par_req->tx_power; msgb_free(msg); - return 0; + return rc; } static int l1ctl_rx_tch_mode_req(struct l1ctl_link *l1l, struct msgb *msg) diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index c9bf6c87a..6ee75d3e4 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -415,6 +415,32 @@ static void trx_if_measure_rsp_cb(struct trx_instance *trx, char *resp) trx_if_cmd_measure(trx, ++arfcn, trx->pm_arfcn_stop); } +/* + * Timing Advance control + * + * SETTA instructs the transceiver to transmit bursts in + * advance calculated from requested TA value. This value is + * normally between 0 and 63, with each step representing + * an advance of one bit period (about 3.69 microseconds). + * CMD SETTA <0-63> + * RSP SETTA + */ + +int trx_if_cmd_setta(struct trx_instance *trx, int8_t ta) +{ + /* Do nothing, if requested TA value matches the current */ + if (trx->ta == ta) + return 0; + + /* Make sure that TA value is in valid range */ + if (ta < 0 || ta > 63) { + LOGP(DTRX, LOGL_ERROR, "TA value %d is out of allowed range\n", ta); + return -ENOTSUP; + } + + return trx_ctrl_cmd(trx, 0, "SETTA", "%d", ta); +} + /* Get response from CTRL socket */ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) { diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h index 02c9ff787..dd84315e3 100644 --- a/src/host/trxcon/trx_if.h +++ b/src/host/trxcon/trx_if.h @@ -63,6 +63,8 @@ int trx_if_cmd_echo(struct trx_instance *trx); int trx_if_cmd_setpower(struct trx_instance *trx, int db); int trx_if_cmd_adjpower(struct trx_instance *trx, int db); +int trx_if_cmd_setta(struct trx_instance *trx, int8_t ta); + int trx_if_cmd_rxtune(struct trx_instance *trx, uint16_t arfcn); int trx_if_cmd_txtune(struct trx_instance *trx, uint16_t arfcn); From 05ff6b06674dd3b9c310e3b27e5bad821cd6d41c Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 9 Dec 2017 01:22:24 +0700 Subject: [PATCH 114/211] fake_trx/clck_gen.py: send the first indication immediately Change-Id: I0132dd939b02db357d248abf65c9116d6a1802d0 --- src/target/fake_trx/clck_gen.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/target/fake_trx/clck_gen.py b/src/target/fake_trx/clck_gen.py index 85dc00e03..1eb970a51 100755 --- a/src/target/fake_trx/clck_gen.py +++ b/src/target/fake_trx/clck_gen.py @@ -45,6 +45,9 @@ class CLCKGen: # Average loop back delay LO_DELAY_US = 90.0 + # State variables + timer = None + def __init__(self, clck_links, clck_start = 0, ind_period = 102): self.clck_links = clck_links self.ind_period = ind_period @@ -55,15 +58,15 @@ class CLCKGen: self.ctr_interval /= self.SEC_DELAY_US self.ctr_interval *= self.ind_period - # Create a timer manager - self.timer = Timer(self.ctr_interval, self.send_clck_ind) - def start(self): - # Schedule the first indication - self.timer.start() + # Send the first indication + self.send_clck_ind() def stop(self): - self.timer.cancel() + # Stop pending timer + if self.timer is not None: + self.timer.cancel() + self.timer = None def send_clck_ind(self): # Keep clock cycle From 26ecb9460e1871b26a5e3a3c72c2f7163ece2652 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 9 Dec 2017 01:24:05 +0700 Subject: [PATCH 115/211] fake_trx/clck_gen.py: reset the clck_src when calling stop() Change-Id: I1043f71a2cbe856a0cb605db8a7feab9defa6afd --- src/target/fake_trx/clck_gen.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/target/fake_trx/clck_gen.py b/src/target/fake_trx/clck_gen.py index 1eb970a51..088155b7b 100755 --- a/src/target/fake_trx/clck_gen.py +++ b/src/target/fake_trx/clck_gen.py @@ -51,6 +51,7 @@ class CLCKGen: def __init__(self, clck_links, clck_start = 0, ind_period = 102): self.clck_links = clck_links self.ind_period = ind_period + self.clck_start = clck_start self.clck_src = clck_start # Calculate counter time @@ -68,6 +69,9 @@ class CLCKGen: self.timer.cancel() self.timer = None + # Reset the clock source + self.clck_src = self.clck_start + def send_clck_ind(self): # Keep clock cycle if self.clck_src % self.GSM_SUPERFRAME >= 0: From af217395ccd058a8aeab6992c0bd0743a8e6895e Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 9 Dec 2017 01:25:52 +0700 Subject: [PATCH 116/211] fake_trx: don't sent clock indications until POWERON Change-Id: I86ccc9d26fc54e6511f74f858afdaebb2b284c19 --- src/target/fake_trx/ctrl_if_bts.py | 9 +++++++++ src/target/fake_trx/fake_trx.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/target/fake_trx/ctrl_if_bts.py b/src/target/fake_trx/ctrl_if_bts.py index 96027fe91..d0a7db3c7 100644 --- a/src/target/fake_trx/ctrl_if_bts.py +++ b/src/target/fake_trx/ctrl_if_bts.py @@ -28,6 +28,7 @@ class CTRLInterfaceBTS(CTRLInterface): # Internal state variables trx_started = False burst_fwd = None + clck_gen = None rx_freq = None tx_freq = None pm = None @@ -62,6 +63,10 @@ class CTRLInterfaceBTS(CTRLInterface): if self.pm is not None: self.pm.add_bts_list([self.tx_freq]) + # Start clock indications + if self.clck_gen is not None: + self.clck_gen.start() + return 0 elif self.verify_cmd(request, "POWEROFF", 0): @@ -74,6 +79,10 @@ class CTRLInterfaceBTS(CTRLInterface): if self.pm is not None: self.pm.del_bts_list([self.tx_freq]) + # Stop clock indications + if self.clck_gen is not None: + self.clck_gen.stop() + return 0 # Tuning Control diff --git a/src/target/fake_trx/fake_trx.py b/src/target/fake_trx/fake_trx.py index 477270199..962101c34 100755 --- a/src/target/fake_trx/fake_trx.py +++ b/src/target/fake_trx/fake_trx.py @@ -89,7 +89,7 @@ class Application: self.bts_clck = UDPLink(self.bts_addr, self.bts_base_port + 100, self.bts_base_port) self.clck_gen = CLCKGen([self.bts_clck]) - self.clck_gen.start() + self.bts_ctrl.clck_gen = self.clck_gen print("[i] Init complete") From ab566a7aef67eeb2abd19fedc7ed8fc33e9de684 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 16 Dec 2017 15:09:51 +0700 Subject: [PATCH 117/211] host/trxcon/scheduler: use new libosmocoding API for RACH Since the 32e5641d, the gsm0503_rach_encode() is deprecated, and the library provides new API with extended (11-bit) RACH support. Change-Id: I1955fe46eebd173d6eddd1d47ee9f7318b9b4e2d --- src/host/trxcon/sched_lchan_rach.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/host/trxcon/sched_lchan_rach.c b/src/host/trxcon/sched_lchan_rach.c index 820178364..bf81fd21d 100644 --- a/src/host/trxcon/sched_lchan_rach.c +++ b/src/host/trxcon/sched_lchan_rach.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -70,8 +71,8 @@ int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, if (req->offset-- > 0) return 0; - /* Encode payload */ - rc = gsm0503_rach_encode(payload, &req->ra, trx->bsic); + /* Encode (8-bit) payload */ + rc = gsm0503_rach_ext_encode(payload, req->ra, trx->bsic, false); if (rc) { LOGP(DSCHD, LOGL_ERROR, "Could not encode RACH burst\n"); return rc; From 2937cb4c16b6e5b9cf4fa139cfcaf678946f6cb2 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 16 Dec 2017 14:53:48 +0700 Subject: [PATCH 118/211] host/trxcon/scheduler: clean up the trx_lchan_state There were some BTS specific variables, which are meaningless. This change cleans them up, and also groups some measurement, encryption, and AMR specific variables into sub-structures. Change-Id: Ie753a7e3e7fa2b433d8319b3a05b85b8583d7be2 --- src/host/trxcon/sched_lchan_common.c | 2 +- src/host/trxcon/sched_lchan_xcch.c | 27 ++++----- src/host/trxcon/sched_trx.h | 90 ++++++++++++---------------- 3 files changed, 50 insertions(+), 69 deletions(-) diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c index aa0614c31..8f061659c 100644 --- a/src/host/trxcon/sched_lchan_common.c +++ b/src/host/trxcon/sched_lchan_common.c @@ -95,7 +95,7 @@ int sched_send_data_ind(struct trx_instance *trx, struct trx_ts *ts, data->link_id = lchan_desc->link_id; data->band_arfcn = htons(trx->band_arfcn); data->frame_nr = htonl(lchan->rx_first_fn); - data->rx_level = -(lchan->rssi_sum / lchan->rssi_num); + data->rx_level = -(lchan->meas.rssi_sum / lchan->meas.rssi_num); /* FIXME: set proper values */ data->num_biterr = 0; diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index f57c8fceb..cae3b19f2 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -48,8 +48,6 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, { const struct trx_lchan_desc *lchan_desc; int n_errors, n_bits_total, rc; - uint8_t *rssi_num, *toa_num; - float *rssi_sum, *toa_sum; sbit_t *buffer, *offset; uint8_t l2[23], *mask; uint32_t *first_fn; @@ -60,33 +58,28 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, mask = &lchan->rx_burst_mask; buffer = lchan->rx_bursts; - rssi_sum = &lchan->rssi_sum; - rssi_num = &lchan->rssi_num; - toa_sum = &lchan->toa_sum; - toa_num = &lchan->toa_num; - LOGP(DSCHD, LOGL_DEBUG, "Data received on %s: fn=%u ts=%u bid=%u\n", lchan_desc->name, fn, ts->index, bid); /* Clear buffer & store frame number of first burst */ if (bid == 0) { + /* Clean up old measurements */ + memset(&lchan->meas, 0x00, sizeof(lchan->meas)); + memset(buffer, 0, 464); *first_fn = fn; *mask = 0x0; - - *rssi_sum = 0; - *rssi_num = 0; - *toa_sum = 0; - *toa_num = 0; } - /* Update mask and RSSI */ + /* Update mask */ *mask |= (1 << bid); - *rssi_sum += rssi; - (*rssi_num)++; - *toa_sum += toa; - (*toa_num)++; + + /* Update measurements */ + lchan->meas.rssi_sum += rssi; + lchan->meas.toa_sum += toa; + lchan->meas.rssi_num++; + lchan->meas.toa_num++; /* Copy burst to buffer of 4 bursts */ offset = buffer + bid * 116; diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index e1ef92448..de7e07342 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -154,65 +154,53 @@ struct trx_lchan_state { /*! \brief Burst buffer for TX */ ubit_t *tx_bursts; - /*! \brief Number of RSSI values */ - uint8_t rssi_num; - /*! \brief Sum of RSSI values */ - float rssi_sum; - /*! \brief Number of TOA values */ - uint8_t toa_num; - /*! \brief Sum of TOA values */ - float toa_sum; - /*! \brief (SACCH) loss detection */ - uint8_t lost; /*! \brief Mode for TCH channels */ uint8_t rsl_cmode, tch_mode; - /* AMR specific */ - /*! \brief 4 possible codecs for AMR */ - uint8_t codec[4]; - /*! \brief Number of possible codecs */ - int codecs; - /*! \brief Sum of bit error rates */ - float ber_sum; - /*! \brief Number of bit error rates */ - int ber_num; - /*! \brief Current uplink FT index */ - uint8_t ul_ft; - /*! \brief Current downlink FT index */ - uint8_t dl_ft; - /*! \brief Current uplink CMR index */ - uint8_t ul_cmr; - /*! \brief Current downlink CMR index */ - uint8_t dl_cmr; - /*! \brief If AMR loop is enabled */ - uint8_t amr_loop; + /*! \brief FACCH/H on downlink */ + uint8_t dl_ongoing_facch; + /*! \brief FACCH/H on uplink */ + uint8_t ul_ongoing_facch; - /* TCH/H */ - uint8_t dl_ongoing_facch; /*! \brief FACCH/H on downlink */ - uint8_t ul_ongoing_facch; /*! \brief FACCH/H on uplink */ - - /*! \brief A5/x encryption algorithm */ - int encr_algo; - int encr_key_len; - uint8_t encr_key[MAX_A5_KEY_LEN]; - - /*! \brief Measurements */ struct { - /*! \brief Cyclic clock counter */ - uint8_t clock; - /*! \brief Last RSSI values */ - int8_t rssi[32]; - /*! \brief Received RSSI values */ - int rssi_count; - /*! \brief Number of stored value */ - int rssi_valid_count; - /*! \brief Any burst received so far */ - int rssi_got_burst; + /*! \brief Number of RSSI values */ + uint8_t rssi_num; + /*! \brief Sum of RSSI values */ + float rssi_sum; + /*! \brief Number of TOA values */ + uint8_t toa_num; /*! \brief Sum of TOA values */ float toa_sum; - /*! \brief Number of TOA value */ - int toa_num; } meas; + + /* AMR specific */ + struct { + /*! \brief 4 possible codecs for AMR */ + uint8_t codec[4]; + /*! \brief Number of possible codecs */ + uint8_t codecs; + /*! \brief Current uplink FT index */ + uint8_t ul_ft; + /*! \brief Current downlink FT index */ + uint8_t dl_ft; + /*! \brief Current uplink CMR index */ + uint8_t ul_cmr; + /*! \brief Current downlink CMR index */ + uint8_t dl_cmr; + /*! \brief If AMR loop is enabled */ + uint8_t amr_loop; + /*! \brief Number of bit error rates */ + uint8_t ber_num; + /*! \brief Sum of bit error rates */ + float ber_sum; + } amr; + + /*! \brief A5/X encryption state */ + struct { + uint8_t key[MAX_A5_KEY_LEN]; + uint8_t key_len; + uint8_t algo; + } a5; }; struct trx_ts { From 3d872d0eae76b42b0fdf235f2df196c8bc5abb7c Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 16 Dec 2017 14:58:04 +0700 Subject: [PATCH 119/211] host/trxcon/scheduler: drop meaningless memset call Change-Id: I18a938cef350632673cfc820beed5e42f40d89e7 --- src/host/trxcon/sched_lchan_xcch.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index cae3b19f2..0aff9ac56 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -61,13 +61,11 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, LOGP(DSCHD, LOGL_DEBUG, "Data received on %s: fn=%u ts=%u bid=%u\n", lchan_desc->name, fn, ts->index, bid); - /* Clear buffer & store frame number of first burst */ + /* Reset internal state */ if (bid == 0) { /* Clean up old measurements */ memset(&lchan->meas, 0x00, sizeof(lchan->meas)); - memset(buffer, 0, 464); - *first_fn = fn; *mask = 0x0; } From d2c13e3d209565a4bd1c6776e3a5a86f7ea5d857 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 16 Dec 2017 15:04:41 +0700 Subject: [PATCH 120/211] host/trxcon/scheduler: use GSM_MACBLOCK_LEN definition Change-Id: Ie3b27ecb62d6f0e84f2e3ec0c1558e32bb213d33 --- src/host/trxcon/sched_lchan_common.c | 6 ++++-- src/host/trxcon/sched_lchan_xcch.c | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c index 8f061659c..925a4415e 100644 --- a/src/host/trxcon/sched_lchan_common.c +++ b/src/host/trxcon/sched_lchan_common.c @@ -32,6 +32,8 @@ #include #include +#include + #include "l1ctl_proto.h" #include "scheduler.h" #include "sched_trx.h" @@ -106,7 +108,7 @@ int sched_send_data_ind(struct trx_instance *trx, struct trx_ts *ts, memcpy(data->payload, l2, l2_len); /* Put a packet to higher layers */ - l1ctl_tx_data_ind(trx->l1l, data, l2_len == 23 ? + l1ctl_tx_data_ind(trx->l1l, data, l2_len == GSM_MACBLOCK_LEN ? L1CTL_DATA_IND : L1CTL_TRAFFIC_IND); talloc_free(data); @@ -135,7 +137,7 @@ int sched_send_data_conf(struct trx_instance *trx, struct trx_ts *ts, data->frame_nr = htonl(fn); /* Choose a confirmation type */ - conf_type = l2_len == 23 ? + conf_type = l2_len == GSM_MACBLOCK_LEN ? L1CTL_DATA_CONF : L1CTL_TRAFFIC_CONF; l1ctl_tx_data_conf(trx->l1l, data, conf_type); diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index 0aff9ac56..7d4786db6 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -32,6 +32,7 @@ #include #include +#include #include #include "l1ctl_proto.h" @@ -47,9 +48,9 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, sbit_t *bits, int8_t rssi, float toa) { const struct trx_lchan_desc *lchan_desc; + uint8_t l2[GSM_MACBLOCK_LEN], *mask; int n_errors, n_bits_total, rc; sbit_t *buffer, *offset; - uint8_t l2[23], *mask; uint32_t *first_fn; /* Set up pointers */ @@ -111,7 +112,7 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, } /* Send a L2 frame to the higher layers */ - sched_send_data_ind(trx, ts, lchan, l2, 23); + sched_send_data_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN); /* TODO: AGC, TA loops */ return 0; @@ -206,7 +207,7 @@ send_burst: *mask = 0x00; /* Confirm data sending */ - sched_send_data_conf(trx, ts, lchan, fn, 23); + sched_send_data_conf(trx, ts, lchan, fn, GSM_MACBLOCK_LEN); } return 0; From 0b5231bdb4172adac64828bbcc57ac7b74411590 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 14 Dec 2017 04:03:59 +0700 Subject: [PATCH 121/211] common/l1ctl.c move TCH bit-ordering to the firmware Previously, TCH frames coming from L1 were reordered to the RTP format. Moreover, the implementation had a few problems: - L1CTL is not the best place for such manipulations; - payloads with other than FR codec were corrupted. Let's use RTP-ordered payloads on the L1CTL interface, performing TCH frame reordering at the firmware. Please note, that actual FR reordering was moved to the firmware as is, without any codec determination. This could be fixed in a separate change. Change-Id: I235a9f535c39d8e57f5d2c6566daeaf883aeef9e --- src/host/layer23/src/common/l1ctl.c | 39 -------------------- src/target/firmware/Makefile | 3 +- src/target/firmware/layer1/prim_tch.c | 52 +++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 40 deletions(-) diff --git a/src/host/layer23/src/common/l1ctl.c b/src/host/layer23/src/common/l1ctl.c index c75872e46..3e642df2b 100644 --- a/src/host/layer23/src/common/l1ctl.c +++ b/src/host/layer23/src/common/l1ctl.c @@ -47,7 +47,6 @@ #include #include #include -#include extern struct gsmtap_inst *gsmtap_inst; @@ -71,24 +70,6 @@ static struct msgb *osmo_l1_alloc(uint8_t msg_type) return msg; } - -static inline int msb_get_bit(uint8_t *buf, int bn) -{ - int pos_byte = bn >> 3; - int pos_bit = 7 - (bn & 7); - - return (buf[pos_byte] >> pos_bit) & 1; -} - -static inline void msb_set_bit(uint8_t *buf, int bn, int bit) -{ - int pos_byte = bn >> 3; - int pos_bit = 7 - (bn & 7); - - buf[pos_byte] |= (bit << pos_bit); -} - - static int osmo_make_band_arfcn(struct osmocom_ms *ms, uint16_t arfcn) { /* TODO: Include the band */ @@ -791,23 +772,12 @@ static int rx_l1_traffic_ind(struct osmocom_ms *ms, struct msgb *msg) { struct l1ctl_info_dl *dl; struct l1ctl_traffic_ind *ti; - uint8_t fr[33]; - int i, di, si; /* Header handling */ dl = (struct l1ctl_info_dl *) msg->l1h; msg->l2h = dl->payload; ti = (struct l1ctl_traffic_ind *) msg->l2h; - memset(fr, 0x00, 33); - fr[0] = 0xd0; - for (i = 0; i < 260; i++) { - di = gsm610_bitorder[i]; - si = (i > 181) ? i + 4 : i; - msb_set_bit(fr, 4 + di, msb_get_bit(ti->data, si)); - } - memcpy(ti->data, fr, 33); - DEBUGP(DL1C, "TRAFFIC IND (%s)\n", osmo_hexdump(ti->data, 33)); /* distribute or drop */ @@ -830,8 +800,6 @@ int l1ctl_tx_traffic_req(struct osmocom_ms *ms, struct msgb *msg, struct l1ctl_hdr *l1h; struct l1ctl_info_ul *l1i_ul; struct l1ctl_traffic_req *tr; - uint8_t fr[33]; - int i, di, si; /* Header handling */ tr = (struct l1ctl_traffic_req *) msg->l2h; @@ -853,13 +821,6 @@ int l1ctl_tx_traffic_req(struct osmocom_ms *ms, struct msgb *msg, return -EINVAL; } - memset(fr, 0x00, 33); - for (i = 0; i < 260; i++) { - si = gsm610_bitorder[i]; - di = (i > 181) ? i + 4 : i; - msb_set_bit(fr, di, msb_get_bit(tr->data, 4 + si)); - } - memcpy(tr->data, fr, 33); // printf("TX %s\n", osmo_hexdump(tr->data, 33)); /* prepend uplink info header */ diff --git a/src/target/firmware/Makefile b/src/target/firmware/Makefile index 2ae2f4dc2..22fa74613 100644 --- a/src/target/firmware/Makefile +++ b/src/target/firmware/Makefile @@ -102,7 +102,8 @@ ANY_APP_LIBS+= calypso/libcalypso.a \ lib/libmini.a \ comm/libcomm.a \ ../../shared/libosmocore/build-target/src/.libs/libosmocore.a \ - ../../shared/libosmocore/build-target/src/gsm/.libs/libosmogsm.a + ../../shared/libosmocore/build-target/src/gsm/.libs/libosmogsm.a \ + ../../shared/libosmocore/build-target/src/codec/.libs/libosmocodec.a # diff --git a/src/target/firmware/layer1/prim_tch.c b/src/target/firmware/layer1/prim_tch.c index a0a03b819..2dadaaf43 100644 --- a/src/target/firmware/layer1/prim_tch.c +++ b/src/target/firmware/layer1/prim_tch.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,44 @@ #include +static inline int msb_get_bit(uint8_t *buf, int bn) +{ + int pos_byte = bn >> 3; + int pos_bit = 7 - (bn & 7); + + return (buf[pos_byte] >> pos_bit) & 1; +} + +static inline void msb_set_bit(uint8_t *buf, int bn, int bit) +{ + int pos_byte = bn >> 3; + int pos_bit = 7 - (bn & 7); + + buf[pos_byte] |= (bit << pos_bit); +} + +static void tch_fr_bit_magic(uint8_t *frame, int dl) +{ + uint8_t fr[33]; + int i, di, si; + + memset(fr, 0x00, 33); + + if (dl) + fr[0] = 0xd0; + + for (i = 0; i < 260; i++) { + di = gsm610_bitorder[i]; + si = (i > 181) ? i + 4 : i; + + if (dl) + msb_set_bit(fr, 4 + di, msb_get_bit(frame, si)); + else + msb_set_bit(fr, si, msb_get_bit(frame, 4 + di)); + } + + memcpy(frame, fr, 33); +} /* This computes various parameters both for the DSP and for * our logic. Not all are used all the time, but it's easier @@ -314,6 +353,12 @@ static int l1s_tch_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) /* Copy actual data, skipping the information block [0,1,2] */ dsp_memcpy_from_api(ti->data, &traffic_buf[3], 33, 1); + /** + * Perform some bit conversations + * FIXME: what about other (than FR) codecs? + */ + tch_fr_bit_magic(ti, 1); + /* Give message to up layer */ l1_queue_for_l2(msg); } @@ -440,6 +485,13 @@ static int l1s_tch_cmd(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) /* Pull Traffic data (if any) */ msg = msgb_dequeue(&l1s.tx_queue[L1S_CHAN_TRAFFIC]); + /** + * Perform some bit conversations + * FIXME: what about other (than FR) codecs? + */ + if (msg) + tch_fr_bit_magic(msg->l2h, 0); + /* Copy actual data, skipping the information block [0,1,2] */ if (msg) { data = msg->l2h; From 21049e5fc46223670c259fedb413bd179bb4ffb2 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sun, 13 Aug 2017 01:35:22 +0600 Subject: [PATCH 122/211] host/trxcon/l1ctl.c: handle L1CTL_TRAFFIC_REQ Change-Id: Ibdf2d4f6aa464250a4c6951af86c06eb3fd3b98b --- src/host/trxcon/l1ctl.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index aeb6c07ee..b72f5ea1e 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -639,6 +639,44 @@ exit: return rc; } +static int l1ctl_rx_traffic_req(struct l1ctl_link *l1l, struct msgb *msg) +{ + struct l1ctl_info_ul *ul; + struct trx_ts_prim *prim; + uint8_t chan_nr, link_id; + int rc; + + /* Extract UL frame header */ + ul = (struct l1ctl_info_ul *) msg->l1h; + + /* Obtain channel description */ + chan_nr = ul->chan_nr; + link_id = ul->link_id & 0x40; + + LOGP(DL1D, LOGL_DEBUG, "Recv Traffic Req (chan_nr=0x%02x, " + "link_id=0x%02x)\n", chan_nr, link_id); + + /* Init a new primitive */ + rc = sched_trx_init_prim(l1l->trx, &prim, TRAFFIC_DATA_LEN, + chan_nr, link_id); + if (rc) + goto exit; + + /* Push this primitive to transmit queue */ + rc = sched_trx_push_prim(l1l->trx, prim, chan_nr); + if (rc) { + talloc_free(prim); + goto exit; + } + + /* Fill in the payload */ + memcpy(prim->payload, ul->payload, TRAFFIC_DATA_LEN); + +exit: + msgb_free(msg); + return rc; +} + static int l1ctl_rx_param_req(struct l1ctl_link *l1l, struct msgb *msg) { struct l1ctl_par_req *par_req; @@ -720,6 +758,8 @@ int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) return l1ctl_rx_dm_rel_req(l1l, msg); case L1CTL_DATA_REQ: return l1ctl_rx_data_req(l1l, msg); + case L1CTL_TRAFFIC_REQ: + return l1ctl_rx_traffic_req(l1l, msg); case L1CTL_PARAM_REQ: return l1ctl_rx_param_req(l1l, msg); case L1CTL_TCH_MODE_REQ: From 9b511668a48b2c7ef7646278b1d2cbdc19441490 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 7 Aug 2017 14:24:18 +0600 Subject: [PATCH 123/211] host/trxcon/scheduler: add initial TCH/F channel support This change implements basic TCH/F lchan handlers for both data reception and transmission. Only FACCH (signaling), FR and EFR payloads are supported at the moment. Change-Id: If6b0eaede2b484484d2a824e7219ff04483266a1 --- src/host/trxcon/Makefile.am | 1 + src/host/trxcon/sched_lchan_common.c | 80 ++++++++ src/host/trxcon/sched_lchan_desc.c | 9 +- src/host/trxcon/sched_lchan_tchf.c | 292 +++++++++++++++++++++++++++ src/host/trxcon/sched_trx.h | 6 + 5 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 src/host/trxcon/sched_lchan_tchf.c diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am index 90c7a3c11..88ff2bee9 100644 --- a/src/host/trxcon/Makefile.am +++ b/src/host/trxcon/Makefile.am @@ -34,6 +34,7 @@ trxcon_SOURCES += \ sched_lchan_common.c \ sched_lchan_desc.c \ sched_lchan_xcch.c \ + sched_lchan_tchf.c \ sched_lchan_rach.c \ sched_lchan_sch.c \ sched_mframe.c \ diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c index 925a4415e..6d75533da 100644 --- a/src/host/trxcon/sched_lchan_common.c +++ b/src/host/trxcon/sched_lchan_common.c @@ -33,6 +33,7 @@ #include #include +#include #include "l1ctl_proto.h" #include "scheduler.h" @@ -145,3 +146,82 @@ int sched_send_data_conf(struct trx_instance *trx, struct trx_ts *ts, return 0; } + +/** + * Composes a bad frame indication message + * according to the current tch_mode. + * + * @param l2 Pointer to allocated byte array + * @param tch_mode Current TCH mode + * @return How much bytes were written + */ +size_t sched_bad_frame_ind(uint8_t *l2, uint8_t rsl_cmode, uint8_t tch_mode) +{ + /* BFI is only required for speech */ + if (rsl_cmode != RSL_CMOD_SPD_SPEECH) + return 0; + + switch (tch_mode) { + case GSM48_CMODE_SIGN: + case GSM48_CMODE_SPEECH_V1: /* Full Rate */ + memset(l2, 0x00, GSM_FR_BYTES); + l2[0] = 0xd0; + return GSM_FR_BYTES; + case GSM48_CMODE_SPEECH_EFR: /* Enhanced Full Rate */ + memset(l2, 0x00, GSM_EFR_BYTES); + l2[0] = 0xc0; + return GSM_EFR_BYTES; + case GSM48_CMODE_SPEECH_AMR: /* Adaptive Multi Rate */ + /* FIXME: AMR is not implemented yet */ + return 0; + default: + LOGP(DSCH, LOGL_ERROR, "Invalid TCH mode: %u\n", tch_mode); + return 0; + } +} + +#define PRIM_IS_FACCH(prim) \ + prim->payload_len == GSM_MACBLOCK_LEN + +#define PRIM_IS_TCH(prim) \ + prim->payload_len != GSM_MACBLOCK_LEN + +struct trx_ts_prim *sched_dequeue_tch_prim(struct llist_head *queue) +{ + struct trx_ts_prim *a, *b; + + /* Obtain the first prim from TX queue */ + a = llist_entry(queue->next, struct trx_ts_prim, list); + + /* If this is the only one => do nothing... */ + if (queue->next->next == queue) + return a; + + /* Obtain the second prim from TX queue */ + b = llist_entry(queue->next->next, struct trx_ts_prim, list); + + /* Find and prioritize FACCH */ + if (PRIM_IS_FACCH(a) && PRIM_IS_TCH(b)) { + /** + * Case 1: first is FACCH, second is TCH: + * Prioritize FACCH, dropping TCH + */ + llist_del(&b->list); + talloc_free(b); + return a; + } else if (PRIM_IS_TCH(a) && PRIM_IS_FACCH(b)) { + /** + * Case 2: first is TCH, second is FACCH: + * Prioritize FACCH, dropping TCH + */ + llist_del(&a->list); + talloc_free(a); + return b; + } else { + /** + * Otherwise: both are TCH or FACCH frames: + * Nothing to prioritize, return the first one + */ + return a; + } +} diff --git a/src/host/trxcon/sched_lchan_desc.c b/src/host/trxcon/sched_lchan_desc.c index c483db8b3..390b1ef12 100644 --- a/src/host/trxcon/sched_lchan_desc.c +++ b/src/host/trxcon/sched_lchan_desc.c @@ -30,11 +30,9 @@ /* TODO: implement */ #define tx_pdtch_fn NULL -#define tx_tchf_fn NULL #define tx_tchh_fn NULL #define rx_pdtch_fn NULL -#define rx_tchf_fn NULL #define rx_tchh_fn NULL /* Forward declaration of handlers */ @@ -52,6 +50,13 @@ int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid); +int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, + sbit_t *bits, int8_t rssi, float toa); + +int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid); + const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { { TRXC_IDLE, "IDLE", diff --git a/src/host/trxcon/sched_lchan_tchf.c b/src/host/trxcon/sched_lchan_tchf.c new file mode 100644 index 000000000..237e5388c --- /dev/null +++ b/src/host/trxcon/sched_lchan_tchf.c @@ -0,0 +1,292 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: handlers for DL / UL bursts on logical channels + * + * (C) 2017 by Vadim Yanitskiy + * + * 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 +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "l1ctl_proto.h" +#include "scheduler.h" +#include "sched_trx.h" +#include "logging.h" +#include "trx_if.h" +#include "trxcon.h" +#include "l1ctl.h" + +int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, + sbit_t *bits, int8_t rssi, float toa) +{ + const struct trx_lchan_desc *lchan_desc; + uint8_t rsl_cmode, tch_mode, mode; + int n_errors, n_bits_total, rc; + sbit_t *buffer, *offset; + uint8_t l2[128], *mask; + uint32_t *first_fn; + size_t l2_len; + + /* Set up pointers */ + lchan_desc = &trx_lchan_desc[lchan->type]; + first_fn = &lchan->rx_first_fn; + mask = &lchan->rx_burst_mask; + buffer = lchan->rx_bursts; + + LOGP(DSCHD, LOGL_DEBUG, "Traffic received on %s: fn=%u ts=%u bid=%u\n", + lchan_desc->name, fn, ts->index, bid); + + /* Reset internal state */ + if (bid == 0) { + /* Clean up old measurements */ + memset(&lchan->meas, 0x00, sizeof(lchan->meas)); + + *first_fn = fn; + *mask = 0x00; + } + + /* Update mask */ + *mask |= (1 << bid); + + /* Update mask and RSSI */ + lchan->meas.rssi_sum += rssi; + lchan->meas.toa_sum += toa; + lchan->meas.rssi_num++; + lchan->meas.toa_num++; + + /* Copy burst to end of buffer of 8 bursts */ + offset = buffer + bid * 116 + 464; + memcpy(offset, bits + 3, 58); + memcpy(offset + 58, bits + 87, 58); + + /* Wait until complete set of bursts */ + if (bid != 3) + return 0; + + /* Check for complete set of bursts */ + if ((*mask & 0xf) != 0xf) { + LOGP(DSCHD, LOGL_DEBUG, "Received incomplete traffic frame at " + "fn=%u (%u/%u) for %s\n", *first_fn, + (*first_fn) % ts->mf_layout->period, + ts->mf_layout->period, + lchan_desc->name); + return -EINVAL; + } + + /** + * Get current RSL / TCH modes + * + * FIXME: we do support speech only, and + * CSD support may be implemented latter. + */ + rsl_cmode = RSL_CMOD_SPD_SPEECH; + tch_mode = lchan->tch_mode; + + mode = rsl_cmode != RSL_CMOD_SPD_SPEECH ? + GSM48_CMODE_SPEECH_V1 : tch_mode; + + switch (mode) { + case GSM48_CMODE_SIGN: + case GSM48_CMODE_SPEECH_V1: /* FR */ + rc = gsm0503_tch_fr_decode(l2, buffer, + 1, 0, &n_errors, &n_bits_total); + break; + case GSM48_CMODE_SPEECH_EFR: /* EFR */ + rc = gsm0503_tch_fr_decode(l2, buffer, + 1, 1, &n_errors, &n_bits_total); + break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + /** + * TODO: AMR requires a dedicated loop, + * which will be implemented later... + */ + LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet\n"); + return -ENOTSUP; + default: + LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u\n", tch_mode); + return -EINVAL; + } + + /* Shift buffer by 4 bursts for interleaving */ + memcpy(buffer, buffer + 464, 464); + + /* Check decoding result */ + if (rc < 4) { + LOGP(DSCHD, LOGL_DEBUG, "Received bad TCH frame ending at " + "fn=%u for %s\n", fn, lchan_desc->name); + + l2_len = sched_bad_frame_ind(l2, rsl_cmode, tch_mode); + } else if (rc == GSM_MACBLOCK_LEN) { + /* FACCH received, forward it to the higher layers */ + sched_send_data_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN); + /* Send BFI instead of stolen TCH frame */ + l2_len = sched_bad_frame_ind(l2, rsl_cmode, tch_mode); + } else { + /* A good TCH frame received */ + l2_len = rc; + } + + /* Send a traffic frame to the higher layers */ + if (l2_len > 0) + sched_send_data_ind(trx, ts, lchan, l2, l2_len); + + return 0; +} + +int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, + struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid) +{ + const struct trx_lchan_desc *lchan_desc; + struct trx_ts_prim *prim; + ubit_t burst[GSM_BURST_LEN]; + ubit_t *buffer, *offset; + uint8_t *mask, *l2; + const uint8_t *tsc; + size_t l2_len; + int rc; + + /* Set up pointers */ + lchan_desc = &trx_lchan_desc[lchan->type]; + mask = &lchan->tx_burst_mask; + buffer = lchan->tx_bursts; + + /* If we have encoded bursts */ + if (*mask) + goto send_burst; + + /* Wait until a first burst in period */ + if (bid > 0) + return 0; + + /* Check the current TCH mode */ + switch (lchan->tch_mode) { + case GSM48_CMODE_SIGN: + case GSM48_CMODE_SPEECH_V1: /* FR */ + l2_len = GSM_FR_BYTES; + break; + case GSM48_CMODE_SPEECH_EFR: /* EFR */ + l2_len = GSM_EFR_BYTES; + break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + /** + * TODO: AMR requires a dedicated loop, + * which will be implemented later... + */ + /* TODO: drop prim here */ + LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet\n"); + return -ENOTSUP; + default: + /* TODO: drop prim here */ + LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u\n", + lchan->tch_mode); + return -EINVAL; + } + + /* Get a message from TX queue */ + prim = sched_dequeue_tch_prim(&ts->tx_prims); + l2 = (uint8_t *) prim->payload; + + /* Determine payload length */ + if (prim->payload_len == GSM_MACBLOCK_LEN) + l2_len = GSM_MACBLOCK_LEN; + + /* Shift buffer by 4 bursts back for interleaving */ + memcpy(buffer, buffer + 464, 464); + + /* Encode payload */ + rc = gsm0503_tch_fr_encode(buffer, l2, l2_len, 1); + if (rc) { + LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload\n"); + + /* Remove primitive from queue and free memory */ + llist_del(&prim->list); + talloc_free(prim); + + return -EINVAL; + } + +send_burst: + /* Determine which burst should be sent */ + offset = buffer + bid * 116; + + /* Update mask */ + *mask |= (1 << bid); + + /* Choose proper TSC */ + tsc = sched_nb_training_bits[trx->tsc]; + + /* Compose a new burst */ + memset(burst, 0, 3); /* TB */ + memcpy(burst + 3, offset, 58); /* Payload 1/2 */ + memcpy(burst + 61, tsc, 26); /* TSC */ + memcpy(burst + 87, offset + 58, 58); /* Payload 2/2 */ + memset(burst + 145, 0, 3); /* TB */ + + LOGP(DSCHD, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n", + lchan_desc->name, fn, ts->index, bid); + + /* Send burst to transceiver */ + rc = trx_if_tx_burst(trx, ts->index, fn, trx->tx_power, burst); + if (rc) { + LOGP(DSCHD, LOGL_ERROR, "Could not send burst to transceiver\n"); + + /* Remove primitive from queue and free memory */ + prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); + llist_del(&prim->list); + talloc_free(prim); + + /* Reset mask */ + *mask = 0x00; + + return rc; + } + + /* If we have sent the last (4/4) burst */ + if (*mask == 0x0f) { + /* Get pointer to a prim which was sent */ + prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); + + /* Confirm data / traffic sending */ + sched_send_data_conf(trx, ts, lchan, fn, prim->payload_len); + + /* Remove primitive from queue and free memory */ + llist_del(&prim->list); + talloc_free(prim); + + /* Reset mask */ + *mask = 0x00; + } + + return 0; +} diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index de7e07342..268c6b707 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -22,6 +22,11 @@ #define MAX_A5_KEY_LEN (128 / 8) +/* TS 101318 Chapter 5.1: 260 bits + 4bit sig */ +#define GSM_FR_BYTES 33 +/* TS 101318 Chapter 5.3: 244 bits + 4bit sig */ +#define GSM_EFR_BYTES 31 + /* Forward declaration to avoid mutual include */ struct trx_lchan_state; struct trx_instance; @@ -262,6 +267,7 @@ int sched_trx_init_prim(struct trx_instance *trx, struct trx_ts_prim **prim, size_t pl_len, uint8_t chan_nr, uint8_t link_id); int sched_trx_push_prim(struct trx_instance *trx, struct trx_ts_prim *prim, uint8_t chan_nr); +struct trx_ts_prim *sched_dequeue_tch_prim(struct llist_head *queue); int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, uint32_t burst_fn, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); From a9c2ef2638523d62f1f9eecb1c6c326f4c6519b6 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 16 Dec 2017 16:21:05 +0700 Subject: [PATCH 124/211] host/trxcon/scheduler: inform L2&3 about decoding errors Previously, we used to drop a frame if decoding wasn't successful. This way, the higher layers didn't even know about that, so the local counters and Measurement Reports were incomplete. This change makes scheduler to forward L2 frames in any case, setting the num_biterr for each of them. In case of decoding error, a dummy (payload filled by 0x00) L2 frame will be sent. Change-Id: I31011d8f3ca8b9a12474cd0bc653faed18391033 --- src/host/trxcon/sched_lchan_common.c | 16 +++++++++++----- src/host/trxcon/sched_lchan_tchf.c | 7 +++++-- src/host/trxcon/sched_lchan_xcch.c | 4 ++-- src/host/trxcon/sched_trx.h | 4 +++- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c index 6d75533da..c6b287a93 100644 --- a/src/host/trxcon/sched_lchan_common.c +++ b/src/host/trxcon/sched_lchan_common.c @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -80,7 +81,8 @@ const uint8_t sched_nb_training_bits[8][26] = { }; int sched_send_data_ind(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len) + struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len, + bool dec_failed, int bit_error_count) { const struct trx_lchan_desc *lchan_desc; struct l1ctl_info_dl *data; @@ -99,14 +101,18 @@ int sched_send_data_ind(struct trx_instance *trx, struct trx_ts *ts, data->band_arfcn = htons(trx->band_arfcn); data->frame_nr = htonl(lchan->rx_first_fn); data->rx_level = -(lchan->meas.rssi_sum / lchan->meas.rssi_num); + data->num_biterr = bit_error_count; /* FIXME: set proper values */ - data->num_biterr = 0; - data->fire_crc = 0; data->snr = 0; - /* Fill in the payload */ - memcpy(data->payload, l2, l2_len); + if (dec_failed) { + /* Mark frame as broken */ + data->fire_crc = 2; + } else { + /* Fill in the payload */ + memcpy(data->payload, l2, l2_len); + } /* Put a packet to higher layers */ l1ctl_tx_data_ind(trx->l1l, data, l2_len == GSM_MACBLOCK_LEN ? diff --git a/src/host/trxcon/sched_lchan_tchf.c b/src/host/trxcon/sched_lchan_tchf.c index 237e5388c..a0adf69c3 100644 --- a/src/host/trxcon/sched_lchan_tchf.c +++ b/src/host/trxcon/sched_lchan_tchf.c @@ -149,7 +149,9 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, l2_len = sched_bad_frame_ind(l2, rsl_cmode, tch_mode); } else if (rc == GSM_MACBLOCK_LEN) { /* FACCH received, forward it to the higher layers */ - sched_send_data_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN); + sched_send_data_ind(trx, ts, lchan, + l2, GSM_MACBLOCK_LEN, false, n_errors); + /* Send BFI instead of stolen TCH frame */ l2_len = sched_bad_frame_ind(l2, rsl_cmode, tch_mode); } else { @@ -159,7 +161,8 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, /* Send a traffic frame to the higher layers */ if (l2_len > 0) - sched_send_data_ind(trx, ts, lchan, l2, l2_len); + sched_send_data_ind(trx, ts, lchan, + l2, l2_len, false, n_errors); return 0; } diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index 7d4786db6..1b7c4da3b 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -108,11 +108,11 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, (*first_fn) % ts->mf_layout->period, ts->mf_layout->period, lchan_desc->name); - return rc; } /* Send a L2 frame to the higher layers */ - sched_send_data_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN); + sched_send_data_ind(trx, ts, lchan, + l2, GSM_MACBLOCK_LEN, rc != 0, n_errors); /* TODO: AGC, TA loops */ return 0; diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 268c6b707..f2c091e11 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -277,6 +278,7 @@ extern const uint8_t sched_nb_training_bits[8][26]; size_t sched_bad_frame_ind(uint8_t *l2, uint8_t rsl_cmode, uint8_t tch_mode); int sched_send_data_ind(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len); + struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len, + bool dec_failed, int bit_error_count); int sched_send_data_conf(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, size_t l2_len); From 60ff614446fedc7b12767d9520abfc529a65fc7e Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 16 Dec 2017 16:33:17 +0700 Subject: [PATCH 125/211] host/trxcon/scheduler: always print error messages Some error messages previously had incorrect logging level 'debug'. We aren't going to hide anything, right? Let's print them! Change-Id: I85fb37292046b667386bfe26b9bbb000600e1c6f --- src/host/trxcon/sched_lchan_sch.c | 2 +- src/host/trxcon/sched_lchan_tchf.c | 4 ++-- src/host/trxcon/sched_lchan_xcch.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/host/trxcon/sched_lchan_sch.c b/src/host/trxcon/sched_lchan_sch.c index 6b4543f59..e66cad4da 100644 --- a/src/host/trxcon/sched_lchan_sch.c +++ b/src/host/trxcon/sched_lchan_sch.c @@ -87,7 +87,7 @@ int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, /* Attempt to decode */ rc = gsm0503_sch_decode(sb_info, payload); if (rc) { - LOGP(DSCHD, LOGL_DEBUG, "Received bad SCH burst at fn=%u\n", fn); + LOGP(DSCHD, LOGL_ERROR, "Received bad SCH burst at fn=%u\n", fn); return rc; } diff --git a/src/host/trxcon/sched_lchan_tchf.c b/src/host/trxcon/sched_lchan_tchf.c index a0adf69c3..7860007ca 100644 --- a/src/host/trxcon/sched_lchan_tchf.c +++ b/src/host/trxcon/sched_lchan_tchf.c @@ -96,7 +96,7 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, /* Check for complete set of bursts */ if ((*mask & 0xf) != 0xf) { - LOGP(DSCHD, LOGL_DEBUG, "Received incomplete traffic frame at " + LOGP(DSCHD, LOGL_ERROR, "Received incomplete traffic frame at " "fn=%u (%u/%u) for %s\n", *first_fn, (*first_fn) % ts->mf_layout->period, ts->mf_layout->period, @@ -143,7 +143,7 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, /* Check decoding result */ if (rc < 4) { - LOGP(DSCHD, LOGL_DEBUG, "Received bad TCH frame ending at " + LOGP(DSCHD, LOGL_ERROR, "Received bad TCH frame ending at " "fn=%u for %s\n", fn, lchan_desc->name); l2_len = sched_bad_frame_ind(l2, rsl_cmode, tch_mode); diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index 1b7c4da3b..312909785 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -91,7 +91,7 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, /* Check for complete set of bursts */ if ((*mask & 0xf) != 0xf) { - LOGP(DSCHD, LOGL_DEBUG, "Received incomplete data frame at " + LOGP(DSCHD, LOGL_ERROR, "Received incomplete data frame at " "fn=%u (%u/%u) for %s\n", *first_fn, (*first_fn) % ts->mf_layout->period, ts->mf_layout->period, @@ -103,7 +103,7 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, /* Attempt to decode */ rc = gsm0503_xcch_decode(l2, buffer, &n_errors, &n_bits_total); if (rc) { - LOGP(DSCHD, LOGL_DEBUG, "Received bad data frame at fn=%u " + LOGP(DSCHD, LOGL_ERROR, "Received bad data frame at fn=%u " "(%u/%u) for %s\n", *first_fn, (*first_fn) % ts->mf_layout->period, ts->mf_layout->period, From e17bb11c3b29919799b3637dece1c7c0273af304 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 18 Dec 2017 01:23:03 +0700 Subject: [PATCH 126/211] trxcon/scheduler: BUGFIX: distinguish between SACCH and FACCH Both SACCH and FACCH messages have the same 23-byte length, both are being queued together within a single transimt queue. So, previously a SACCH frame could be picked by TCH burst handler, and then sent as a FACCH frame. Let's fix this. A FACCH primitive may have one of the TRXC_TCH* logical channel types, while SACCH primitives have one of the TRXC_SACCH*. Change-Id: Ia7090384f3ff74c9d94997265135acbceffa0ffe --- src/host/trxcon/sched_lchan_common.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c index c6b287a93..bf4754144 100644 --- a/src/host/trxcon/sched_lchan_common.c +++ b/src/host/trxcon/sched_lchan_common.c @@ -186,11 +186,14 @@ size_t sched_bad_frame_ind(uint8_t *l2, uint8_t rsl_cmode, uint8_t tch_mode) } } -#define PRIM_IS_FACCH(prim) \ - prim->payload_len == GSM_MACBLOCK_LEN +#define CHAN_IS_TCH(chan) \ + (chan == TRXC_TCHF || chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1) #define PRIM_IS_TCH(prim) \ - prim->payload_len != GSM_MACBLOCK_LEN + CHAN_IS_TCH(prim->chan) && prim->payload_len != GSM_MACBLOCK_LEN + +#define PRIM_IS_FACCH(prim) \ + CHAN_IS_TCH(prim->chan) && prim->payload_len == GSM_MACBLOCK_LEN struct trx_ts_prim *sched_dequeue_tch_prim(struct llist_head *queue) { From 15d512d301545e10f69ade0562d5ea86832066b4 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 18 Dec 2017 02:13:41 +0700 Subject: [PATCH 127/211] trxcon/scheduler: separate primitive management code It's good to write, keep and make the source code as much modular as possible. So, Tte primitive management code was separated to the 'sched_prim.c' and going to be extended in the near future. Change-Id: Ifec8c9e4f2c95c72b00772688bcb5dc9c11d6de7 --- src/host/trxcon/Makefile.am | 1 + src/host/trxcon/l1ctl.c | 13 +- src/host/trxcon/sched_lchan_common.c | 49 ------- src/host/trxcon/sched_lchan_tchf.c | 2 +- src/host/trxcon/sched_prim.c | 199 +++++++++++++++++++++++++++ src/host/trxcon/sched_trx.c | 83 +---------- src/host/trxcon/sched_trx.h | 8 +- 7 files changed, 214 insertions(+), 141 deletions(-) create mode 100644 src/host/trxcon/sched_prim.c diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am index 88ff2bee9..c9cc170a0 100644 --- a/src/host/trxcon/Makefile.am +++ b/src/host/trxcon/Makefile.am @@ -39,6 +39,7 @@ trxcon_SOURCES += \ sched_lchan_sch.c \ sched_mframe.c \ sched_clck.c \ + sched_prim.c \ sched_trx.c \ $(NULL) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index b72f5ea1e..a11e79229 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -495,8 +495,7 @@ static int l1ctl_rx_rach_req(struct l1ctl_link *l1l, struct msgb *msg) "(offset=%u ra=0x%02x)\n", req->offset, req->ra); /* Init a new primitive */ - rc = sched_trx_init_prim(l1l->trx, &prim, len, - chan_nr, link_id); + rc = sched_prim_init(l1l->trx, &prim, len, chan_nr, link_id); if (rc) goto exit; @@ -506,7 +505,7 @@ static int l1ctl_rx_rach_req(struct l1ctl_link *l1l, struct msgb *msg) * FIXME: what if requested TS is not configured? * Or what if one (such as TCH) has no TRXC_RACH slots? */ - rc = sched_trx_push_prim(l1l->trx, prim, chan_nr); + rc = sched_prim_push(l1l->trx, prim, chan_nr); if (rc) { talloc_free(prim); goto exit; @@ -619,13 +618,13 @@ static int l1ctl_rx_data_req(struct l1ctl_link *l1l, struct msgb *msg) "link_id=0x%02x)\n", chan_nr, link_id); /* Init a new primitive */ - rc = sched_trx_init_prim(l1l->trx, &prim, 23, + rc = sched_prim_init(l1l->trx, &prim, 23, chan_nr, link_id); if (rc) goto exit; /* Push this primitive to transmit queue */ - rc = sched_trx_push_prim(l1l->trx, prim, chan_nr); + rc = sched_prim_push(l1l->trx, prim, chan_nr); if (rc) { talloc_free(prim); goto exit; @@ -657,13 +656,13 @@ static int l1ctl_rx_traffic_req(struct l1ctl_link *l1l, struct msgb *msg) "link_id=0x%02x)\n", chan_nr, link_id); /* Init a new primitive */ - rc = sched_trx_init_prim(l1l->trx, &prim, TRAFFIC_DATA_LEN, + rc = sched_prim_init(l1l->trx, &prim, TRAFFIC_DATA_LEN, chan_nr, link_id); if (rc) goto exit; /* Push this primitive to transmit queue */ - rc = sched_trx_push_prim(l1l->trx, prim, chan_nr); + rc = sched_prim_push(l1l->trx, prim, chan_nr); if (rc) { talloc_free(prim); goto exit; diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c index bf4754144..9eccc3e7b 100644 --- a/src/host/trxcon/sched_lchan_common.c +++ b/src/host/trxcon/sched_lchan_common.c @@ -185,52 +185,3 @@ size_t sched_bad_frame_ind(uint8_t *l2, uint8_t rsl_cmode, uint8_t tch_mode) return 0; } } - -#define CHAN_IS_TCH(chan) \ - (chan == TRXC_TCHF || chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1) - -#define PRIM_IS_TCH(prim) \ - CHAN_IS_TCH(prim->chan) && prim->payload_len != GSM_MACBLOCK_LEN - -#define PRIM_IS_FACCH(prim) \ - CHAN_IS_TCH(prim->chan) && prim->payload_len == GSM_MACBLOCK_LEN - -struct trx_ts_prim *sched_dequeue_tch_prim(struct llist_head *queue) -{ - struct trx_ts_prim *a, *b; - - /* Obtain the first prim from TX queue */ - a = llist_entry(queue->next, struct trx_ts_prim, list); - - /* If this is the only one => do nothing... */ - if (queue->next->next == queue) - return a; - - /* Obtain the second prim from TX queue */ - b = llist_entry(queue->next->next, struct trx_ts_prim, list); - - /* Find and prioritize FACCH */ - if (PRIM_IS_FACCH(a) && PRIM_IS_TCH(b)) { - /** - * Case 1: first is FACCH, second is TCH: - * Prioritize FACCH, dropping TCH - */ - llist_del(&b->list); - talloc_free(b); - return a; - } else if (PRIM_IS_TCH(a) && PRIM_IS_FACCH(b)) { - /** - * Case 2: first is TCH, second is FACCH: - * Prioritize FACCH, dropping TCH - */ - llist_del(&a->list); - talloc_free(a); - return b; - } else { - /** - * Otherwise: both are TCH or FACCH frames: - * Nothing to prioritize, return the first one - */ - return a; - } -} diff --git a/src/host/trxcon/sched_lchan_tchf.c b/src/host/trxcon/sched_lchan_tchf.c index 7860007ca..da24b238e 100644 --- a/src/host/trxcon/sched_lchan_tchf.c +++ b/src/host/trxcon/sched_lchan_tchf.c @@ -217,7 +217,7 @@ int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, } /* Get a message from TX queue */ - prim = sched_dequeue_tch_prim(&ts->tx_prims); + prim = sched_prim_dequeue_tch(&ts->tx_prims); l2 = (uint8_t *) prim->payload; /* Determine payload length */ diff --git a/src/host/trxcon/sched_prim.c b/src/host/trxcon/sched_prim.c new file mode 100644 index 000000000..1d1a94047 --- /dev/null +++ b/src/host/trxcon/sched_prim.c @@ -0,0 +1,199 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: primitive management + * + * (C) 2017 by Vadim Yanitskiy + * + * 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 +#include +#include + +#include +#include +#include + +#include + +#include "scheduler.h" +#include "sched_trx.h" +#include "trx_if.h" +#include "logging.h" + +/** + * Initializes a new primitive by allocating memory + * and filling some meta-information (e.g. lchan type). + * + * @param trx TRX instance to be used as initial talloc context + * @param prim external prim pointer (will point to the allocated prim) + * @param pl_len prim payload length + * @param chan_nr RSL channel description (used to set a proper chan) + * @param link_id RSL link description (used to set a proper chan) + * @return zero in case of success, otherwise a error number + */ +int sched_prim_init(struct trx_instance *trx, + struct trx_ts_prim **prim, size_t pl_len, + uint8_t chan_nr, uint8_t link_id) +{ + enum trx_lchan_type lchan_type; + struct trx_ts_prim *new_prim; + uint8_t len; + + /* Determine lchan type */ + lchan_type = sched_trx_chan_nr2lchan_type(chan_nr, link_id); + if (!lchan_type) { + LOGP(DSCH, LOGL_ERROR, "Couldn't determine lchan type " + "for chan_nr=%02x and link_id=%02x\n", chan_nr, link_id); + return -EINVAL; + } + + /* How much memory do we need? */ + len = sizeof(struct trx_ts_prim); /* Primitive header */ + len += pl_len; /* Requested payload size */ + + /* Allocate a new primitive */ + new_prim = talloc_zero_size(trx, len); + if (new_prim == NULL) { + LOGP(DSCH, LOGL_ERROR, "Failed to allocate memory\n"); + return -ENOMEM; + } + + /* Init primitive header */ + new_prim->payload_len = pl_len; + new_prim->chan = lchan_type; + + /* Set external pointer */ + *prim = new_prim; + + return 0; +} + +/** + * Adds a primitive to the end of transmit queue of a particular + * timeslot, whose index is parsed from chan_nr. + * + * @param trx TRX instance + * @param prim to be enqueued primitive + * @param chan_nr RSL channel description + * @return zero in case of success, otherwise a error number + */ +int sched_prim_push(struct trx_instance *trx, + struct trx_ts_prim *prim, uint8_t chan_nr) +{ + struct trx_ts *ts; + uint8_t tn; + + /* Determine TS index */ + tn = chan_nr & 0x7; + if (tn > 7) { + LOGP(DSCH, LOGL_ERROR, "Incorrect TS index %u\n", tn); + return -EINVAL; + } + + /* Check whether required timeslot is allocated and configured */ + ts = trx->ts_list[tn]; + if (ts == NULL || ts->mf_layout == NULL) { + LOGP(DSCH, LOGL_ERROR, "Timeslot %u isn't configured\n", tn); + return -EINVAL; + } + + /** + * Change talloc context of primitive + * from trx to the parent ts + */ + talloc_steal(ts, prim); + + /* Add primitive to TS transmit queue */ + llist_add_tail(&prim->list, &ts->tx_prims); + + return 0; +} + +#define CHAN_IS_TCH(chan) \ + (chan == TRXC_TCHF || chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1) + +#define PRIM_IS_TCH(prim) \ + CHAN_IS_TCH(prim->chan) && prim->payload_len != GSM_MACBLOCK_LEN + +#define PRIM_IS_FACCH(prim) \ + CHAN_IS_TCH(prim->chan) && prim->payload_len == GSM_MACBLOCK_LEN + +/** + * Dequeues a TCH or FACCH frame, prioritizing the second. + * In case if a FACCH frame is found, a TCH frame is being + * dropped (i.e. replaced). + * + * @param queue a transmit queue to take a prim from + * @return a FACCH or TCH primitive + */ +struct trx_ts_prim *sched_prim_dequeue_tch(struct llist_head *queue) +{ + struct trx_ts_prim *a, *b; + + /* Obtain the first prim from TX queue */ + a = llist_entry(queue->next, struct trx_ts_prim, list); + + /* If this is the only one => do nothing... */ + if (queue->next->next == queue) + return a; + + /* Obtain the second prim from TX queue */ + b = llist_entry(queue->next->next, struct trx_ts_prim, list); + + /* Find and prioritize FACCH */ + if (PRIM_IS_FACCH(a) && PRIM_IS_TCH(b)) { + /** + * Case 1: first is FACCH, second is TCH: + * Prioritize FACCH, dropping TCH + */ + llist_del(&b->list); + talloc_free(b); + return a; + } else if (PRIM_IS_TCH(a) && PRIM_IS_FACCH(b)) { + /** + * Case 2: first is TCH, second is FACCH: + * Prioritize FACCH, dropping TCH + */ + llist_del(&a->list); + talloc_free(a); + return b; + } else { + /** + * Otherwise: both are TCH or FACCH frames: + * Nothing to prioritize, return the first one + */ + return a; + } +} + +/** + * Flushes a queue of primitives + * + * @param list list of prims going to be flushed + */ +void sched_prim_flush_queue(struct llist_head *list) +{ + struct trx_ts_prim *prim, *prim_next; + + llist_for_each_entry_safe(prim, prim_next, list, list) { + llist_del(&prim->list); + talloc_free(prim); + } +} diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 02da24127..ec2f4484a 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -37,16 +37,6 @@ #include "trx_if.h" #include "logging.h" -static void prim_queue_flush(struct llist_head *list) -{ - struct trx_ts_prim *prim, *prim_next; - - llist_for_each_entry_safe(prim, prim_next, list, list) { - llist_del(&prim->list); - talloc_free(prim); - } -} - static void sched_frame_clck_cb(struct trx_sched *sched) { struct trx_instance *trx = (struct trx_instance *) sched->data; @@ -202,7 +192,7 @@ void sched_trx_del_ts(struct trx_instance *trx, int tn) LOGP(DSCH, LOGL_NOTICE, "Delete TDMA timeslot #%u\n", tn); /* Flush queue primitives for TX */ - prim_queue_flush(&ts->tx_prims); + sched_prim_flush_queue(&ts->tx_prims); /* Remove ts from list and free memory */ trx->ts_list[tn] = NULL; @@ -289,7 +279,7 @@ int sched_trx_reset_ts(struct trx_instance *trx, int tn) ts->mf_layout = NULL; /* Flush queue primitives for TX */ - prim_queue_flush(&ts->tx_prims); + sched_prim_flush_queue(&ts->tx_prims); /* Free channel states */ talloc_free(ts->lchans); @@ -428,75 +418,6 @@ void sched_trx_deactivate_all_lchans(struct trx_ts *ts) } } -int sched_trx_init_prim(struct trx_instance *trx, - struct trx_ts_prim **prim, size_t pl_len, - uint8_t chan_nr, uint8_t link_id) -{ - enum trx_lchan_type lchan_type; - struct trx_ts_prim *new_prim; - uint8_t len; - - /* Determine lchan type */ - lchan_type = sched_trx_chan_nr2lchan_type(chan_nr, link_id); - if (!lchan_type) { - LOGP(DSCH, LOGL_ERROR, "Couldn't determine lchan type " - "for chan_nr=%02x and link_id=%02x\n", chan_nr, link_id); - return -EINVAL; - } - - /* How much memory do we need? */ - len = sizeof(struct trx_ts_prim); /* Primitive header */ - len += pl_len; /* Requested payload size */ - - /* Allocate a new primitive */ - new_prim = talloc_zero_size(trx, len); - if (new_prim == NULL) { - LOGP(DSCH, LOGL_ERROR, "Failed to allocate memory\n"); - return -ENOMEM; - } - - /* Init primitive header */ - new_prim->payload_len = pl_len; - new_prim->chan = lchan_type; - - /* Set external pointer */ - *prim = new_prim; - - return 0; -} - -int sched_trx_push_prim(struct trx_instance *trx, - struct trx_ts_prim *prim, uint8_t chan_nr) -{ - struct trx_ts *ts; - uint8_t tn; - - /* Determine TS index */ - tn = chan_nr & 0x7; - if (tn > 7) { - LOGP(DSCH, LOGL_ERROR, "Incorrect TS index %u\n", tn); - return -EINVAL; - } - - /* Check whether required timeslot is allocated and configured */ - ts = trx->ts_list[tn]; - if (ts == NULL || ts->mf_layout == NULL) { - LOGP(DSCH, LOGL_ERROR, "Timeslot %u isn't configured\n", tn); - return -EINVAL; - } - - /** - * Change talloc context of primitive - * from trx to the parent ts - */ - talloc_steal(ts, prim); - - /* Add primitive to TS transmit queue */ - llist_add_tail(&prim->list, &ts->tx_prims); - - return 0; -} - enum gsm_phys_chan_config sched_trx_chan_nr2pchan_config(uint8_t chan_nr) { uint8_t cbits = chan_nr >> 3; diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index f2c091e11..f1ad7d38a 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -264,11 +264,13 @@ struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts, enum trx_lchan_type chan); /* Primitive management functions */ -int sched_trx_init_prim(struct trx_instance *trx, struct trx_ts_prim **prim, +int sched_prim_init(struct trx_instance *trx, struct trx_ts_prim **prim, size_t pl_len, uint8_t chan_nr, uint8_t link_id); -int sched_trx_push_prim(struct trx_instance *trx, +int sched_prim_push(struct trx_instance *trx, struct trx_ts_prim *prim, uint8_t chan_nr); -struct trx_ts_prim *sched_dequeue_tch_prim(struct llist_head *queue); + +struct trx_ts_prim *sched_prim_dequeue_tch(struct llist_head *queue); +void sched_prim_flush_queue(struct llist_head *list); int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, uint32_t burst_fn, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); From a403215beab8f92f7bd90fe8f80535bd61841b30 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 18 Dec 2017 03:47:23 +0700 Subject: [PATCH 128/211] trxcon/scheduler: move prim management outside lchan handlers Previously, each lchan handler used to obtain and delete primitives from a timeslot's tranmit queue itself. This approach entails many potential problems and bugs: - The lchan handlers shall not do that by definition, they should encode and decode frames according to GSM 05.03. - In some cases (e.g. TCH), a single transmit queue may contain primitives of different types (e.g. TCH, FACCH and SACCH). At the same time, the lchan handlers don't care and don't even know about each other. So, this could cause an unexpected behaviour in some cases. This change separates all primitive management routines, providing a new API for obtaining and dropping them. "Write programs that do one thing and do it well." Change-Id: I29503ece51903784bc53541015285234471c8d15 --- src/host/trxcon/sched_lchan_rach.c | 21 ++++++----- src/host/trxcon/sched_lchan_sch.c | 1 - src/host/trxcon/sched_lchan_tchf.c | 52 ++++++++++++--------------- src/host/trxcon/sched_lchan_xcch.c | 32 +++++------------ src/host/trxcon/sched_prim.c | 58 ++++++++++++++++++++++++++---- src/host/trxcon/sched_trx.c | 26 +++++++++----- src/host/trxcon/sched_trx.h | 7 +++- 7 files changed, 118 insertions(+), 79 deletions(-) diff --git a/src/host/trxcon/sched_lchan_rach.c b/src/host/trxcon/sched_lchan_rach.c index bf81fd21d..b26e27905 100644 --- a/src/host/trxcon/sched_lchan_rach.c +++ b/src/host/trxcon/sched_lchan_rach.c @@ -24,11 +24,9 @@ #include #include -#include #include #include -#include #include #include @@ -57,15 +55,13 @@ static ubit_t rach_synch_seq[] = { int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid) { - struct trx_ts_prim *prim; struct l1ctl_rach_req *req; uint8_t burst[GSM_BURST_LEN]; uint8_t payload[36]; int rc; - /* Get a message from TX queue */ - prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); - req = (struct l1ctl_rach_req *) prim->payload; + /* Get the payload from a current primitive */ + req = (struct l1ctl_rach_req *) lchan->prim->payload; /* Delay RACH sending according to offset value */ if (req->offset-- > 0) @@ -75,6 +71,10 @@ int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, rc = gsm0503_rach_ext_encode(payload, req->ra, trx->bsic, false); if (rc) { LOGP(DSCHD, LOGL_ERROR, "Could not encode RACH burst\n"); + + /* Forget this primitive */ + sched_prim_drop(lchan); + return rc; } @@ -90,15 +90,18 @@ int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, rc = trx_if_tx_burst(trx, ts->index, fn, trx->tx_power, burst); if (rc) { LOGP(DSCHD, LOGL_ERROR, "Could not send burst to transceiver\n"); + + /* Forget this primitive */ + sched_prim_drop(lchan); + return rc; } /* Confirm RACH request */ l1ctl_tx_rach_conf(trx->l1l, fn); - /* Remove primitive from queue and free memory */ - llist_del(&prim->list); - talloc_free(prim); + /* Forget processed primitive */ + sched_prim_drop(lchan); return 0; } diff --git a/src/host/trxcon/sched_lchan_sch.c b/src/host/trxcon/sched_lchan_sch.c index e66cad4da..b2377fa6e 100644 --- a/src/host/trxcon/sched_lchan_sch.c +++ b/src/host/trxcon/sched_lchan_sch.c @@ -29,7 +29,6 @@ #include -#include #include #include diff --git a/src/host/trxcon/sched_lchan_tchf.c b/src/host/trxcon/sched_lchan_tchf.c index da24b238e..e562a49b3 100644 --- a/src/host/trxcon/sched_lchan_tchf.c +++ b/src/host/trxcon/sched_lchan_tchf.c @@ -24,10 +24,8 @@ #include #include -#include #include -#include #include #include @@ -171,11 +169,10 @@ int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid) { const struct trx_lchan_desc *lchan_desc; - struct trx_ts_prim *prim; ubit_t burst[GSM_BURST_LEN]; ubit_t *buffer, *offset; - uint8_t *mask, *l2; const uint8_t *tsc; + uint8_t *mask; size_t l2_len; int rc; @@ -206,35 +203,37 @@ int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, * TODO: AMR requires a dedicated loop, * which will be implemented later... */ - /* TODO: drop prim here */ - LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet\n"); + LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet, " + "dropping frame...\n"); + + /* Forget this primitive */ + sched_prim_drop(lchan); + return -ENOTSUP; default: - /* TODO: drop prim here */ - LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u\n", - lchan->tch_mode); + LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u, " + "dropping frame...\n", lchan->tch_mode); + + /* Forget this primitive */ + sched_prim_drop(lchan); + return -EINVAL; } - /* Get a message from TX queue */ - prim = sched_prim_dequeue_tch(&ts->tx_prims); - l2 = (uint8_t *) prim->payload; - /* Determine payload length */ - if (prim->payload_len == GSM_MACBLOCK_LEN) + if (lchan->prim->payload_len == GSM_MACBLOCK_LEN) l2_len = GSM_MACBLOCK_LEN; /* Shift buffer by 4 bursts back for interleaving */ memcpy(buffer, buffer + 464, 464); /* Encode payload */ - rc = gsm0503_tch_fr_encode(buffer, l2, l2_len, 1); + rc = gsm0503_tch_fr_encode(buffer, lchan->prim->payload, l2_len, 1); if (rc) { LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload\n"); - /* Remove primitive from queue and free memory */ - llist_del(&prim->list); - talloc_free(prim); + /* Forget this primitive */ + sched_prim_drop(lchan); return -EINVAL; } @@ -264,10 +263,8 @@ send_burst: if (rc) { LOGP(DSCHD, LOGL_ERROR, "Could not send burst to transceiver\n"); - /* Remove primitive from queue and free memory */ - prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); - llist_del(&prim->list); - talloc_free(prim); + /* Forget this primitive */ + sched_prim_drop(lchan); /* Reset mask */ *mask = 0x00; @@ -277,15 +274,12 @@ send_burst: /* If we have sent the last (4/4) burst */ if (*mask == 0x0f) { - /* Get pointer to a prim which was sent */ - prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); - /* Confirm data / traffic sending */ - sched_send_data_conf(trx, ts, lchan, fn, prim->payload_len); + sched_send_data_conf(trx, ts, lchan, fn, + lchan->prim->payload_len); - /* Remove primitive from queue and free memory */ - llist_del(&prim->list); - talloc_free(prim); + /* Forget processed primitive */ + sched_prim_drop(lchan); /* Reset mask */ *mask = 0x00; diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index 312909785..d395b1da8 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -24,10 +24,8 @@ #include #include -#include #include -#include #include #include @@ -122,11 +120,10 @@ int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid) { const struct trx_lchan_desc *lchan_desc; - struct trx_ts_prim *prim; ubit_t burst[GSM_BURST_LEN]; ubit_t *buffer, *offset; - uint8_t *mask, *l2; const uint8_t *tsc; + uint8_t *mask; int rc; /* Set up pointers */ @@ -142,20 +139,13 @@ int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts, return 0; } - /* Encode payload if not yet */ - - /* Get a message from TX queue */ - prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); - l2 = (uint8_t *) prim->payload; - - /* Encode bursts */ - rc = gsm0503_xcch_encode(buffer, l2); + /* Encode payload */ + rc = gsm0503_xcch_encode(buffer, lchan->prim->payload); if (rc) { LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload\n"); - /* Remove primitive from queue and free memory */ - llist_del(&prim->list); - talloc_free(prim); + /* Forget this primitive */ + sched_prim_drop(lchan); return -EINVAL; } @@ -185,10 +175,8 @@ send_burst: if (rc) { LOGP(DSCHD, LOGL_ERROR, "Could not send burst to transceiver\n"); - /* Remove primitive from queue and free memory */ - prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); - llist_del(&prim->list); - talloc_free(prim); + /* Forget this primitive */ + sched_prim_drop(lchan); /* Reset mask */ *mask = 0x00; @@ -198,10 +186,8 @@ send_burst: /* If we have sent the last (4/4) burst */ if ((*mask & 0x0f) == 0x0f) { - /* Remove primitive from queue and free memory */ - prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); - llist_del(&prim->list); - talloc_free(prim); + /* Forget processed primitive */ + sched_prim_drop(lchan); /* Reset mask */ *mask = 0x00; diff --git a/src/host/trxcon/sched_prim.c b/src/host/trxcon/sched_prim.c index 1d1a94047..3a46ab642 100644 --- a/src/host/trxcon/sched_prim.c +++ b/src/host/trxcon/sched_prim.c @@ -143,19 +143,20 @@ int sched_prim_push(struct trx_instance *trx, * @param queue a transmit queue to take a prim from * @return a FACCH or TCH primitive */ -struct trx_ts_prim *sched_prim_dequeue_tch(struct llist_head *queue) +static struct trx_ts_prim *sched_prim_dequeue_tch(struct llist_head *queue) { struct trx_ts_prim *a, *b; - /* Obtain the first prim from TX queue */ + /* Dequeue the first prim */ a = llist_entry(queue->next, struct trx_ts_prim, list); + llist_del(&a->list); - /* If this is the only one => do nothing... */ - if (queue->next->next == queue) + /* If this was the only one => do nothing... */ + if (llist_empty(queue)) return a; - /* Obtain the second prim from TX queue */ - b = llist_entry(queue->next->next, struct trx_ts_prim, list); + /* Obtain the next prim */ + b = llist_entry(queue->next, struct trx_ts_prim, list); /* Find and prioritize FACCH */ if (PRIM_IS_FACCH(a) && PRIM_IS_TCH(b)) { @@ -171,7 +172,7 @@ struct trx_ts_prim *sched_prim_dequeue_tch(struct llist_head *queue) * Case 2: first is TCH, second is FACCH: * Prioritize FACCH, dropping TCH */ - llist_del(&a->list); + llist_del(&b->list); talloc_free(a); return b; } else { @@ -183,6 +184,49 @@ struct trx_ts_prim *sched_prim_dequeue_tch(struct llist_head *queue) } } +/** + * Dequeues a single primitive of required type + * from a specified transmit queue. + * + * @param queue a transmit queue to take a prim from + * @param lchan_type required primitive type + * @return a primitive or NULL if not found + */ +struct trx_ts_prim *sched_prim_dequeue(struct llist_head *queue, + enum trx_lchan_type lchan_type) +{ + struct trx_ts_prim *prim; + + /* There is nothing to dequeue */ + if (llist_empty(queue)) + return NULL; + + /* TCH requires FACCH prioritization, so handle it separately */ + if (CHAN_IS_TCH(lchan_type)) + return sched_prim_dequeue_tch(queue); + + llist_for_each_entry(prim, queue, list) { + if (prim->chan == lchan_type) { + llist_del(&prim->list); + return prim; + } + } + + return NULL; +} + +/** + * Drops the current primitive of specified logical channel + * + * @param lchan a logical channel to drop prim from + */ +void sched_prim_drop(struct trx_lchan_state *lchan) +{ + /* Forget this primitive */ + talloc_free(lchan->prim); + lchan->prim = NULL; +} + /** * Flushes a queue of primitives * diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index ec2f4484a..ee62e7080 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -43,7 +43,6 @@ static void sched_frame_clck_cb(struct trx_sched *sched) const struct trx_frame *frame; struct trx_lchan_state *lchan; trx_lchan_tx_func *handler; - struct trx_ts_prim *prim; enum trx_lchan_type chan; uint8_t offset, bid; struct trx_ts *ts; @@ -61,10 +60,6 @@ static void sched_frame_clck_cb(struct trx_sched *sched) if (ts->mf_layout == NULL) continue; - /* There is nothing to send */ - if (llist_empty(&ts->tx_prims)) - continue; - /** * Advance frame number, giving the transceiver more * time until a burst must be transmitted... @@ -90,12 +85,19 @@ static void sched_frame_clck_cb(struct trx_sched *sched) if (lchan == NULL) continue; - /* Get a message from TX queue */ - prim = llist_entry(ts->tx_prims.next, struct trx_ts_prim, list); + /** + * If we aren't processing any primitive yet, + * attempt to obtain a new one from queue + */ + if (lchan->prim == NULL) + lchan->prim = sched_prim_dequeue(&ts->tx_prims, chan); + + /* If there is no primitive, do nothing */ + if (lchan->prim == NULL) + continue; /* Poke lchan handler */ - if (prim->chan == chan) - handler(trx, ts, lchan, fn, bid); + handler(trx, ts, lchan, fn, bid); } } @@ -394,6 +396,9 @@ int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) talloc_free(lchan->rx_bursts); talloc_free(lchan->tx_bursts); + /* Forget the current prim */ + sched_prim_drop(lchan); + lchan->active = 0; return 0; @@ -414,6 +419,9 @@ void sched_trx_deactivate_all_lchans(struct trx_ts *ts) talloc_free(lchan->rx_bursts); talloc_free(lchan->tx_bursts); + /* Forget the current prim */ + sched_prim_drop(lchan); + lchan->active = 0; } } diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index f1ad7d38a..c857601fa 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -160,6 +160,9 @@ struct trx_lchan_state { /*! \brief Burst buffer for TX */ ubit_t *tx_bursts; + /*! \brief A primitive being sent */ + struct trx_ts_prim *prim; + /*! \brief Mode for TCH channels */ uint8_t rsl_cmode, tch_mode; @@ -269,7 +272,9 @@ int sched_prim_init(struct trx_instance *trx, struct trx_ts_prim **prim, int sched_prim_push(struct trx_instance *trx, struct trx_ts_prim *prim, uint8_t chan_nr); -struct trx_ts_prim *sched_prim_dequeue_tch(struct llist_head *queue); +struct trx_ts_prim *sched_prim_dequeue(struct llist_head *queue, + enum trx_lchan_type lchan_type); +void sched_prim_drop(struct trx_lchan_state *lchan); void sched_prim_flush_queue(struct llist_head *list); int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, From 32c2a1d74c1bb3f1cfa9a56b2e15b93fa321f942 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 18 Dec 2017 04:39:27 +0700 Subject: [PATCH 129/211] trxcon/scheduler: prioritize FACCH correctly Previously we used to compare two consecutive first primitives, taken from a transmit queue. This approach may cause some delay, which is critical for FACCH e.g. in case of handover. Let's walk through a whole transmit queue to find a pair of both FACCH frames, and only then decide what to do. Change-Id: I925cca77bfaa255dd095bc882c901d41c9bc4633 --- src/host/trxcon/sched_prim.c | 74 ++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/src/host/trxcon/sched_prim.c b/src/host/trxcon/sched_prim.c index 3a46ab642..da2ff3be3 100644 --- a/src/host/trxcon/sched_prim.c +++ b/src/host/trxcon/sched_prim.c @@ -145,43 +145,51 @@ int sched_prim_push(struct trx_instance *trx, */ static struct trx_ts_prim *sched_prim_dequeue_tch(struct llist_head *queue) { - struct trx_ts_prim *a, *b; + struct trx_ts_prim *facch = NULL; + struct trx_ts_prim *tch = NULL; + struct trx_ts_prim *i; - /* Dequeue the first prim */ - a = llist_entry(queue->next, struct trx_ts_prim, list); - llist_del(&a->list); + /* Attempt to find a pair of FACCH and TCH frames */ + llist_for_each_entry(i, queue, list) { + /* Find one FACCH frame */ + if (!facch && PRIM_IS_FACCH(i)) + facch = i; - /* If this was the only one => do nothing... */ - if (llist_empty(queue)) - return a; + /* Find one TCH frame */ + if (!tch && PRIM_IS_TCH(i)) + tch = i; - /* Obtain the next prim */ - b = llist_entry(queue->next, struct trx_ts_prim, list); - - /* Find and prioritize FACCH */ - if (PRIM_IS_FACCH(a) && PRIM_IS_TCH(b)) { - /** - * Case 1: first is FACCH, second is TCH: - * Prioritize FACCH, dropping TCH - */ - llist_del(&b->list); - talloc_free(b); - return a; - } else if (PRIM_IS_TCH(a) && PRIM_IS_FACCH(b)) { - /** - * Case 2: first is TCH, second is FACCH: - * Prioritize FACCH, dropping TCH - */ - llist_del(&b->list); - talloc_free(a); - return b; - } else { - /** - * Otherwise: both are TCH or FACCH frames: - * Nothing to prioritize, return the first one - */ - return a; + /* If both are found */ + if (facch && tch) + break; } + + /* There should be at least one frame found */ + OSMO_ASSERT(facch || tch); + + /* Prioritize FACCH */ + if (facch && tch) { + /* We found a pair, dequeue both */ + llist_del(&facch->list); + llist_del(&tch->list); + + /* Drop TCH */ + talloc_free(tch); + + /* FACCH replaces TCH */ + return facch; + } else if (facch) { + /* Only FACCH was found */ + llist_del(&facch->list); + return facch; + } else if (tch) { + /* Only TCH was found */ + llist_del(&tch->list); + return tch; + } + + /* Unreachable */ + OSMO_ASSERT(0); } /** From 255f25ef25c5ce1deefcfbe63844bc47d524c377 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 16 Dec 2017 17:52:31 +0700 Subject: [PATCH 130/211] L1CTL/L1CTL_CRYPTO_REQ: add key length and channel info Previously, the L1CTL_CRYPTO_REQ message contained only a ciphering algorithm and actual Kc key to be used. The key length was calculated manually using the MSGB API. Let's avoid manual calculations here, as it may cause unexpected behavior if the message structure is changed. Also, let's fill the UL header with minimal information about a channel, which is going to be encrypted. Change-Id: I1813a188e755141241273479b17896415abcc3f1 --- include/l1ctl_proto.h | 1 + src/host/layer23/include/osmocom/bb/common/l1ctl.h | 4 ++-- src/host/layer23/src/common/l1ctl.c | 8 ++++++-- src/host/layer23/src/mobile/gsm48_rr.c | 12 ++++++++---- src/target/firmware/layer1/l23_api.c | 5 ++--- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/include/l1ctl_proto.h b/include/l1ctl_proto.h index 37d3d8797..87e1fc3dd 100644 --- a/include/l1ctl_proto.h +++ b/include/l1ctl_proto.h @@ -280,6 +280,7 @@ struct l1ctl_dm_freq_req { struct l1ctl_crypto_req { uint8_t algo; + uint8_t key_len; uint8_t key[0]; } __attribute__((packed)); diff --git a/src/host/layer23/include/osmocom/bb/common/l1ctl.h b/src/host/layer23/include/osmocom/bb/common/l1ctl.h index 3534589db..824b1b032 100644 --- a/src/host/layer23/include/osmocom/bb/common/l1ctl.h +++ b/src/host/layer23/include/osmocom/bb/common/l1ctl.h @@ -16,8 +16,8 @@ int l1ctl_tx_data_req(struct osmocom_ms *ms, struct msgb *msg, uint8_t chan_nr, /* Transmit L1CTL_PARAM_REQ */ int l1ctl_tx_param_req(struct osmocom_ms *ms, uint8_t ta, uint8_t tx_power); -int l1ctl_tx_crypto_req(struct osmocom_ms *ms, uint8_t algo, uint8_t *key, - uint8_t len); +int l1ctl_tx_crypto_req(struct osmocom_ms *ms, uint8_t chan_nr, + uint8_t algo, uint8_t *key, uint8_t len); /* Transmit L1CTL_RACH_REQ */ int l1ctl_tx_rach_req(struct osmocom_ms *ms, uint8_t ra, uint16_t offset, diff --git a/src/host/layer23/src/common/l1ctl.c b/src/host/layer23/src/common/l1ctl.c index 3e642df2b..f4c214d4b 100644 --- a/src/host/layer23/src/common/l1ctl.c +++ b/src/host/layer23/src/common/l1ctl.c @@ -408,8 +408,8 @@ int l1ctl_tx_param_req(struct osmocom_ms *ms, uint8_t ta, uint8_t tx_power) } /* Transmit L1CTL_CRYPTO_REQ */ -int l1ctl_tx_crypto_req(struct osmocom_ms *ms, uint8_t algo, uint8_t *key, - uint8_t len) +int l1ctl_tx_crypto_req(struct osmocom_ms *ms, uint8_t chan_nr, + uint8_t algo, uint8_t *key, uint8_t len) { struct msgb *msg; struct l1ctl_info_ul *ul; @@ -422,7 +422,11 @@ int l1ctl_tx_crypto_req(struct osmocom_ms *ms, uint8_t algo, uint8_t *key, DEBUGP(DL1C, "CRYPTO Req. algo=%d, len=%d\n", algo, len); ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul)); req = (struct l1ctl_crypto_req *) msgb_put(msg, sizeof(*req) + len); + + ul->chan_nr = chan_nr; + req->key_len = len; req->algo = algo; + if (len) memcpy(req->key, key, len); diff --git a/src/host/layer23/src/mobile/gsm48_rr.c b/src/host/layer23/src/mobile/gsm48_rr.c index ac272143d..729170a84 100644 --- a/src/host/layer23/src/mobile/gsm48_rr.c +++ b/src/host/layer23/src/mobile/gsm48_rr.c @@ -1005,9 +1005,11 @@ static int gsm48_rr_rx_cip_mode_cmd(struct osmocom_ms *ms, struct msgb *msg) rr->cipher_on = sc; rr->cipher_type = alg_id; if (rr->cipher_on) - l1ctl_tx_crypto_req(ms, rr->cipher_type + 1, subscr->key, 8); + l1ctl_tx_crypto_req(ms, rr->cd_now.chan_nr, + rr->cipher_type + 1, subscr->key, 8); else - l1ctl_tx_crypto_req(ms, 0, NULL, 0); + l1ctl_tx_crypto_req(ms, rr->cd_now.chan_nr, + 0, NULL, 0); /* response (using the new mode) */ return gsm48_rr_tx_cip_mode_cpl(ms, cr); @@ -3001,7 +3003,8 @@ static int gsm48_rr_activate_channel(struct osmocom_ms *ms, s->si5 = s->si5bis = s->si5ter = s->si6 = 0; if (rr->cipher_on) - l1ctl_tx_crypto_req(ms, rr->cipher_type + 1, subscr->key, 8); + l1ctl_tx_crypto_req(ms, rr->cd_now.chan_nr, + rr->cipher_type + 1, subscr->key, 8); return 0; } @@ -3020,7 +3023,8 @@ static int gsm48_rr_channel_after_time(struct osmocom_ms *ms, l1ctl_tx_dm_freq_req_h0(ms, cd->arfcn, cd->tsc, fn); if (rr->cipher_on) - l1ctl_tx_crypto_req(ms, rr->cipher_type + 1, subscr->key, 8); + l1ctl_tx_crypto_req(ms, rr->cd_now.chan_nr, + rr->cipher_type + 1, subscr->key, 8); gsm48_rr_set_mode(ms, cd->chan_nr, cd->mode); diff --git a/src/target/firmware/layer1/l23_api.c b/src/target/firmware/layer1/l23_api.c index 311ca6a26..e53b0c8fe 100644 --- a/src/target/firmware/layer1/l23_api.c +++ b/src/target/firmware/layer1/l23_api.c @@ -301,11 +301,10 @@ static void l1ctl_rx_crypto_req(struct msgb *msg) struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data; struct l1ctl_crypto_req *cr = (struct l1ctl_crypto_req *) ul->payload; - uint8_t key_len = msg->len - sizeof(*l1h) - sizeof(*ul) - sizeof(*cr); - printd("L1CTL_CRYPTO_REQ (algo=A5/%u, len=%u)\n", cr->algo, key_len); + printd("L1CTL_CRYPTO_REQ (algo=A5/%u, len=%u)\n", cr->algo, cr->key_len); - if (cr->algo && key_len != 8) { + if (cr->algo && cr->key_len != 8) { printd("L1CTL_CRYPTO_REQ -> Invalid key\n"); return; } From 44838f79a2408feca664980d4b42a5c86b17db07 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 18 Dec 2017 05:45:27 +0700 Subject: [PATCH 131/211] trxcon/scheduler: preprocess UL bursts before sending Having a possibility to preprocess UL burst before sending to transceiver is required for the further ciphering support integration and probably some other tasks. Change-Id: Ia6eead5d4f51d7c0bf277b9d5ebb0a74676df567 --- src/host/trxcon/sched_lchan_rach.c | 6 ++---- src/host/trxcon/sched_lchan_tchf.c | 6 ++---- src/host/trxcon/sched_lchan_xcch.c | 6 ++---- src/host/trxcon/sched_trx.c | 18 ++++++++++++++++++ src/host/trxcon/sched_trx.h | 3 +++ 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/host/trxcon/sched_lchan_rach.c b/src/host/trxcon/sched_lchan_rach.c index b26e27905..2a09a37da 100644 --- a/src/host/trxcon/sched_lchan_rach.c +++ b/src/host/trxcon/sched_lchan_rach.c @@ -86,11 +86,9 @@ int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, LOGP(DSCHD, LOGL_DEBUG, "Transmitting RACH fn=%u\n", fn); - /* Send burst to transceiver */ - rc = trx_if_tx_burst(trx, ts->index, fn, trx->tx_power, burst); + /* Forward burst to scheduler */ + rc = sched_trx_handle_tx_burst(trx, ts, lchan, fn, burst); if (rc) { - LOGP(DSCHD, LOGL_ERROR, "Could not send burst to transceiver\n"); - /* Forget this primitive */ sched_prim_drop(lchan); diff --git a/src/host/trxcon/sched_lchan_tchf.c b/src/host/trxcon/sched_lchan_tchf.c index e562a49b3..45fa1c201 100644 --- a/src/host/trxcon/sched_lchan_tchf.c +++ b/src/host/trxcon/sched_lchan_tchf.c @@ -258,11 +258,9 @@ send_burst: LOGP(DSCHD, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n", lchan_desc->name, fn, ts->index, bid); - /* Send burst to transceiver */ - rc = trx_if_tx_burst(trx, ts->index, fn, trx->tx_power, burst); + /* Forward burst to scheduler */ + rc = sched_trx_handle_tx_burst(trx, ts, lchan, fn, burst); if (rc) { - LOGP(DSCHD, LOGL_ERROR, "Could not send burst to transceiver\n"); - /* Forget this primitive */ sched_prim_drop(lchan); diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index d395b1da8..c71b10fd0 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -170,11 +170,9 @@ send_burst: LOGP(DSCHD, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n", lchan_desc->name, fn, ts->index, bid); - /* Send burst to transceiver */ - rc = trx_if_tx_burst(trx, ts->index, fn, trx->tx_power, burst); + /* Forward burst to scheduler */ + rc = sched_trx_handle_tx_burst(trx, ts, lchan, fn, burst); if (rc) { - LOGP(DSCHD, LOGL_ERROR, "Could not send burst to transceiver\n"); - /* Forget this primitive */ sched_prim_drop(lchan); diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index ee62e7080..fba9ee44f 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -531,3 +531,21 @@ next_frame: return 0; } + +int sched_trx_handle_tx_burst(struct trx_instance *trx, + struct trx_ts *ts, struct trx_lchan_state *lchan, + uint32_t fn, ubit_t *bits) +{ + int rc; + + /* TODO: perform A5/X burst encryption if required */ + + /* Forward burst to transceiver */ + rc = trx_if_tx_burst(trx, ts->index, fn, trx->tx_power, bits); + if (rc) { + LOGP(DSCHD, LOGL_ERROR, "Could not send burst to transceiver\n"); + return rc; + } + + return 0; +} diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index c857601fa..54dbc8553 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -279,6 +279,9 @@ void sched_prim_flush_queue(struct llist_head *list); int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, uint32_t burst_fn, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); +int sched_trx_handle_tx_burst(struct trx_instance *trx, + struct trx_ts *ts, struct trx_lchan_state *lchan, + uint32_t fn, ubit_t *bits); /* Shared declarations for lchan handlers */ extern const uint8_t sched_nb_training_bits[8][26]; From feec102aea039c4091fa9cf6075d45fd23448bd3 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 18 Dec 2017 05:47:28 +0700 Subject: [PATCH 132/211] trxcon/scheduler: implement A5/X ciphering support This change implements the A5/X ciphering support transparently for the logical channel handlers. In other words, a DL burst is deciphered before being passed to a handler, and an UL burst is ciphered before being sent to transceiver. The implementation mostly relays on the libosmocore's A5 API. Change-Id: Ib53418d8c0f394fdece09cf5cc240887cb0bb5af --- src/host/trxcon/l1ctl.c | 45 +++++++++++++++++++++ src/host/trxcon/sched_trx.c | 81 +++++++++++++++++++++++++++++++++++-- src/host/trxcon/sched_trx.h | 2 + 3 files changed, 125 insertions(+), 3 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index a11e79229..070ee565b 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -731,6 +731,49 @@ static int l1ctl_rx_tch_mode_req(struct l1ctl_link *l1l, struct msgb *msg) return 0; } +static int l1ctl_rx_crypto_req(struct l1ctl_link *l1l, struct msgb *msg) +{ + struct l1ctl_crypto_req *req; + struct l1ctl_info_ul *ul; + struct trx_ts *ts; + uint8_t tn; + int rc = 0; + + ul = (struct l1ctl_info_ul *) msg->l1h; + req = (struct l1ctl_crypto_req *) ul->payload; + + LOGP(DL1C, LOGL_NOTICE, "L1CTL_CRYPTO_REQ (algo=A5/%u, key_len=%u)\n", + req->algo, req->key_len); + + /* Determine TS index */ + tn = ul->chan_nr & 0x7; + if (tn > 7) { + LOGP(DL1C, LOGL_ERROR, "Incorrect TS index %u\n", tn); + rc = -EINVAL; + goto exit; + } + + /* Make sure that required TS is allocated and configured */ + ts = l1l->trx->ts_list[tn]; + if (ts == NULL || ts->mf_layout == NULL) { + LOGP(DL1C, LOGL_ERROR, "TS %u is not configured\n", tn); + rc = -EINVAL; + goto exit; + } + + /* Poke scheduler */ + rc = sched_trx_start_ciphering(ts, req->algo, req->key, req->key_len); + if (rc) { + LOGP(DL1C, LOGL_ERROR, "Couldn't configure ciphering\n"); + rc = -EINVAL; + goto exit; + } + +exit: + msgb_free(msg); + return rc; +} + int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) { struct l1ctl_hdr *l1h; @@ -763,6 +806,8 @@ int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) return l1ctl_rx_param_req(l1l, msg); case L1CTL_TCH_MODE_REQ: return l1ctl_rx_tch_mode_req(l1l, msg); + case L1CTL_CRYPTO_REQ: + return l1ctl_rx_crypto_req(l1l, msg); default: LOGP(DL1C, LOGL_ERROR, "Unknown MSG: %u\n", l1h->msg_type); msgb_free(msg); diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index fba9ee44f..120098bd9 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -292,6 +293,37 @@ int sched_trx_reset_ts(struct trx_instance *trx, int tn) return 0; } +int sched_trx_start_ciphering(struct trx_ts *ts, uint8_t algo, + uint8_t *key, uint8_t key_len) +{ + struct trx_lchan_state *state; + size_t len; + int i; + + /* Prevent NULL-pointer deference */ + if (!ts) + return -EINVAL; + + /* Make sure we can store this key */ + if (key_len > MAX_A5_KEY_LEN) + return -ERANGE; + + /* Iterate over all allocated logical channels */ + len = talloc_array_length(ts->lchans); + for (i = 0; i < len; i++) { + /* Set key length and algorithm */ + state = ts->lchans + i; + state->a5.key_len = key_len; + state->a5.algo = algo; + + /* Copy requested key */ + if (key_len) + memcpy(state->a5.key, key, key_len); + } + + return 0; +} + struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts, enum trx_lchan_type chan) { @@ -396,6 +428,9 @@ int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) talloc_free(lchan->rx_bursts); talloc_free(lchan->tx_bursts); + /* Reset ciphering state */ + memset(&lchan->a5, 0x00, sizeof(lchan->a5)); + /* Forget the current prim */ sched_prim_drop(lchan); @@ -456,6 +491,40 @@ enum trx_lchan_type sched_trx_chan_nr2lchan_type(uint8_t chan_nr, return TRXC_IDLE; } +static void sched_trx_a5_burst_dec(struct trx_lchan_state *lchan, + uint32_t fn, sbit_t *burst) +{ + ubit_t ks[114]; + int i; + + /* Generate keystream for a DL burst */ + osmo_a5(lchan->a5.algo, lchan->a5.key, fn, ks, NULL); + + /* Apply keystream over ciphertext */ + for (i = 0; i < 57; i++) { + if (ks[i]) + burst[i + 3] *= -1; + if (ks[i + 57]) + burst[i + 88] *= -1; + } +} + +static void sched_trx_a5_burst_enc(struct trx_lchan_state *lchan, + uint32_t fn, ubit_t *burst) +{ + ubit_t ks[114]; + int i; + + /* Generate keystream for an UL burst */ + osmo_a5(lchan->a5.algo, lchan->a5.key, fn, NULL, ks); + + /* Apply keystream over plaintext */ + for (i = 0; i < 57; i++) { + burst[i + 3] ^= ks[i]; + burst[i + 88] ^= ks[i + 57]; + } +} + int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, uint32_t burst_fn, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa) { @@ -512,9 +581,13 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, if (!lchan->active) goto next_frame; - /* Put burst to handler */ + /* Reached current fn */ if (fn == burst_fn) { - /* TODO: decrypt if required */ + /* Perform A5/X decryption if required */ + if (lchan->a5.algo) + sched_trx_a5_burst_dec(lchan, fn, bits); + + /* Put burst to handler */ handler(trx, ts, lchan, fn, bid, bits, rssi, toa); } @@ -538,7 +611,9 @@ int sched_trx_handle_tx_burst(struct trx_instance *trx, { int rc; - /* TODO: perform A5/X burst encryption if required */ + /* Perform A5/X burst encryption if required */ + if (lchan->a5.algo) + sched_trx_a5_burst_enc(lchan, fn, bits); /* Forward burst to transceiver */ rc = trx_if_tx_burst(trx, ts->index, fn, trx->tx_power, bits); diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 54dbc8553..504054a58 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -253,6 +253,8 @@ void sched_trx_del_ts(struct trx_instance *trx, int tn); int sched_trx_reset_ts(struct trx_instance *trx, int tn); int sched_trx_configure_ts(struct trx_instance *trx, int tn, enum gsm_phys_chan_config config); +int sched_trx_start_ciphering(struct trx_ts *ts, uint8_t algo, + uint8_t *key, uint8_t key_len); /* Logical channel management functions */ enum gsm_phys_chan_config sched_trx_chan_nr2pchan_config(uint8_t chan_nr); From 6c0b1261a3cb62b578bf3f86d06f5498e70bf589 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 28 Dec 2017 19:44:49 +0100 Subject: [PATCH 133/211] trxcon/scheduler: FIX: return NULL from TCH dequeue function Initially it was expected that a TCH transmit queue could contain TCH and FACCH primitives only. But there are also SACCH primitives, which are also being stored there. So, let's drop the assertations from the sched_prim_dequeue_tch(), and return NULL if nothing was found. Change-Id: Iae37057d35883c09a76f0612e52c2d14d9ff91cb --- src/host/trxcon/sched_prim.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/host/trxcon/sched_prim.c b/src/host/trxcon/sched_prim.c index da2ff3be3..1f466a1e8 100644 --- a/src/host/trxcon/sched_prim.c +++ b/src/host/trxcon/sched_prim.c @@ -141,7 +141,7 @@ int sched_prim_push(struct trx_instance *trx, * dropped (i.e. replaced). * * @param queue a transmit queue to take a prim from - * @return a FACCH or TCH primitive + * @return a FACCH or TCH primitive, otherwise NULL */ static struct trx_ts_prim *sched_prim_dequeue_tch(struct llist_head *queue) { @@ -164,9 +164,6 @@ static struct trx_ts_prim *sched_prim_dequeue_tch(struct llist_head *queue) break; } - /* There should be at least one frame found */ - OSMO_ASSERT(facch || tch); - /* Prioritize FACCH */ if (facch && tch) { /* We found a pair, dequeue both */ @@ -188,8 +185,11 @@ static struct trx_ts_prim *sched_prim_dequeue_tch(struct llist_head *queue) return tch; } - /* Unreachable */ - OSMO_ASSERT(0); + /** + * Nothing was found, + * e.g. only SACCH frames are in queue + */ + return NULL; } /** From 96da00d45766d43c919d059fb94e9106cff33f2b Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 4 Jan 2018 01:20:39 +0100 Subject: [PATCH 134/211] trxcon/scheduler: share chan / prim identification helpers Because they would be also used outside. Change-Id: Ic8af9d7c72fdb124caef82e35170f92b84e16eb9 --- src/host/trxcon/sched_prim.c | 9 --------- src/host/trxcon/sched_trx.h | 9 +++++++++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/host/trxcon/sched_prim.c b/src/host/trxcon/sched_prim.c index 1f466a1e8..fb5f0a0de 100644 --- a/src/host/trxcon/sched_prim.c +++ b/src/host/trxcon/sched_prim.c @@ -126,15 +126,6 @@ int sched_prim_push(struct trx_instance *trx, return 0; } -#define CHAN_IS_TCH(chan) \ - (chan == TRXC_TCHF || chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1) - -#define PRIM_IS_TCH(prim) \ - CHAN_IS_TCH(prim->chan) && prim->payload_len != GSM_MACBLOCK_LEN - -#define PRIM_IS_FACCH(prim) \ - CHAN_IS_TCH(prim->chan) && prim->payload_len == GSM_MACBLOCK_LEN - /** * Dequeues a TCH or FACCH frame, prioritizing the second. * In case if a FACCH frame is found, a TCH frame is being diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 504054a58..b93011cb1 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -274,6 +274,15 @@ int sched_prim_init(struct trx_instance *trx, struct trx_ts_prim **prim, int sched_prim_push(struct trx_instance *trx, struct trx_ts_prim *prim, uint8_t chan_nr); +#define CHAN_IS_TCH(chan) \ + (chan == TRXC_TCHF || chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1) + +#define PRIM_IS_TCH(prim) \ + CHAN_IS_TCH(prim->chan) && prim->payload_len != GSM_MACBLOCK_LEN + +#define PRIM_IS_FACCH(prim) \ + CHAN_IS_TCH(prim->chan) && prim->payload_len == GSM_MACBLOCK_LEN + struct trx_ts_prim *sched_prim_dequeue(struct llist_head *queue, enum trx_lchan_type lchan_type); void sched_prim_drop(struct trx_lchan_state *lchan); From 5c70e48077352d35b86aec733dda59d05aab1e01 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 4 Jan 2018 01:26:34 +0100 Subject: [PATCH 135/211] trxcon/scheduler: reset lchan state after deactivation Let's assume that a logical channel, which was already in use, is activated again for a new connection. As we don't reset the state variables, such as burst masks or ciphering data, it may cause an unexpected behaviour. In order to avoid this, let's always reset the logical channel state after deactivation. Change-Id: I91e736a97cb05b167614cb488a00d847a9a859e0 --- src/host/trxcon/sched_trx.c | 58 ++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 120098bd9..2142119fa 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -406,6 +406,42 @@ int sched_trx_activate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) return 0; } +static void sched_trx_reset_lchan(struct trx_lchan_state *lchan) +{ + /* Prevent NULL-pointer deference */ + OSMO_ASSERT(lchan != NULL); + + /* Reset internal state variables */ + lchan->rx_burst_mask = 0x00; + lchan->tx_burst_mask = 0x00; + lchan->rx_first_fn = 0; + + /* Free burst memory */ + talloc_free(lchan->rx_bursts); + talloc_free(lchan->tx_bursts); + + lchan->rx_bursts = NULL; + lchan->tx_bursts = NULL; + + /* Forget the current prim */ + sched_prim_drop(lchan); + + /* TCH specific variables */ + if (CHAN_IS_TCH(lchan->type)) { + lchan->dl_ongoing_facch = 0; + lchan->ul_ongoing_facch = 0; + + lchan->rsl_cmode = 0x00; + lchan->tch_mode = 0x00; + + /* Reset AMR state */ + memset(&lchan->amr, 0x00, sizeof(lchan->amr)); + } + + /* Reset ciphering state */ + memset(&lchan->a5, 0x00, sizeof(lchan->a5)); +} + int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) { struct trx_lchan_state *lchan; @@ -424,16 +460,10 @@ int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) LOGP(DSCH, LOGL_DEBUG, "Deactivating lchan=%s " "on ts=%d\n", trx_lchan_desc[chan].name, ts->index); - /* Free memory */ - talloc_free(lchan->rx_bursts); - talloc_free(lchan->tx_bursts); - - /* Reset ciphering state */ - memset(&lchan->a5, 0x00, sizeof(lchan->a5)); - - /* Forget the current prim */ - sched_prim_drop(lchan); + /* Reset internal state, free memory */ + sched_trx_reset_lchan(lchan); + /* Update activation flag */ lchan->active = 0; return 0; @@ -451,12 +481,14 @@ void sched_trx_deactivate_all_lchans(struct trx_ts *ts) for (i = 0; i < len; i++) { lchan = ts->lchans + i; - talloc_free(lchan->rx_bursts); - talloc_free(lchan->tx_bursts); + /* Omit inactive channels */ + if (!lchan->active) + continue; - /* Forget the current prim */ - sched_prim_drop(lchan); + /* Reset internal state, free memory */ + sched_trx_reset_lchan(lchan); + /* Update activation flag */ lchan->active = 0; } } From 0c201abbffa6404b134374c59e6f9ed73e6757c0 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 4 Jan 2018 05:17:51 +0300 Subject: [PATCH 136/211] trxcon/scheduler: deactivate lchans when resetting / deleting TS Previously, when resetting or deleting a timeslot, we did not deactivate the logical channels, relaying on talloc hierarchical nature. This approach may cause some problems, e.g. on embedded systems with emulated talloc API. Change-Id: I8c34c793df87bd8c79b7bf1f05b949faf10520e8 --- src/host/trxcon/sched_trx.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 2142119fa..956784426 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -194,6 +194,12 @@ void sched_trx_del_ts(struct trx_instance *trx, int tn) LOGP(DSCH, LOGL_NOTICE, "Delete TDMA timeslot #%u\n", tn); + /* Deactivate all logical channels */ + sched_trx_deactivate_all_lchans(ts); + + /* Free channel states */ + talloc_free(ts->lchans); + /* Flush queue primitives for TX */ sched_prim_flush_queue(&ts->tx_prims); @@ -284,6 +290,9 @@ int sched_trx_reset_ts(struct trx_instance *trx, int tn) /* Flush queue primitives for TX */ sched_prim_flush_queue(&ts->tx_prims); + /* Deactivate all logical channels */ + sched_trx_deactivate_all_lchans(ts); + /* Free channel states */ talloc_free(ts->lchans); From bd6e320c086b0b272665b82ab794a03755706055 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 5 Jan 2018 07:24:04 +0700 Subject: [PATCH 137/211] trxcon/scheduler: use linuxlist API for lchan management As there is no any order relation between logical channels, it's better to use the linuxlist API instead of talloc array. Change-Id: I5a78582c77ed1ab33817d240e065dc4cd4708199 --- src/host/trxcon/l1ctl.c | 14 +++-- src/host/trxcon/sched_trx.c | 103 ++++++++++++++++++------------------ src/host/trxcon/sched_trx.h | 4 +- 3 files changed, 65 insertions(+), 56 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 070ee565b..dd75af954 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -700,8 +700,9 @@ static int l1ctl_rx_param_req(struct l1ctl_link *l1l, struct msgb *msg) static int l1ctl_rx_tch_mode_req(struct l1ctl_link *l1l, struct msgb *msg) { struct l1ctl_tch_mode_req *req; + struct trx_lchan_state *lchan; struct trx_ts *ts; - int len, i, j; + int i; req = (struct l1ctl_tch_mode_req *) msg->l1h; @@ -720,9 +721,14 @@ static int l1ctl_rx_tch_mode_req(struct l1ctl_link *l1l, struct msgb *msg) continue; /* Iterate over all allocated lchans */ - len = talloc_array_length(ts->lchans); - for (j = 0; j < len; j++) - ts->lchans[j].tch_mode = req->tch_mode; + llist_for_each_entry(lchan, &ts->lchans, list) { + /* Omit inactive channels */ + if (!lchan->active) + continue; + + /* Set TCH mode */ + lchan->tch_mode = req->tch_mode; + } } /* TODO: do we need to care about audio_mode? */ diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 956784426..0a9b7f73e 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -185,6 +185,7 @@ struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int tn) void sched_trx_del_ts(struct trx_instance *trx, int tn) { + struct trx_lchan_state *lchan; struct trx_ts *ts; /* Find ts in list */ @@ -198,7 +199,8 @@ void sched_trx_del_ts(struct trx_instance *trx, int tn) sched_trx_deactivate_all_lchans(ts); /* Free channel states */ - talloc_free(ts->lchans); + llist_for_each_entry(lchan, &ts->lchans, list) + talloc_free(lchan); /* Flush queue primitives for TX */ sched_prim_flush_queue(&ts->tx_prims); @@ -211,10 +213,14 @@ void sched_trx_del_ts(struct trx_instance *trx, int tn) trx_if_cmd_setslot(trx, tn, 0); } +#define LAYOUT_HAS_LCHAN(layout, lchan) \ + (layout->lchan_mask & ((uint64_t) 0x01 << lchan)) + int sched_trx_configure_ts(struct trx_instance *trx, int tn, enum gsm_phys_chan_config config) { - int i, type, lchan_cnt = 0; + struct trx_lchan_state *lchan; + enum trx_lchan_type type; struct trx_ts *ts; /* Try to find specified ts */ @@ -229,9 +235,6 @@ int sched_trx_configure_ts(struct trx_instance *trx, int tn, return -ENOMEM; } - /* Init queue primitives for TX */ - INIT_LLIST_HEAD(&ts->tx_prims); - /* Choose proper multiframe layout */ ts->mf_layout = sched_mframe_layout(config, tn); if (ts->mf_layout->chan_config != config) @@ -240,29 +243,30 @@ int sched_trx_configure_ts(struct trx_instance *trx, int tn, LOGP(DSCH, LOGL_NOTICE, "(Re)configure TDMA timeslot #%u as %s\n", tn, ts->mf_layout->name); - /* Count channel states */ - for (type = 0; type < _TRX_CHAN_MAX; type++) - if (ts->mf_layout->lchan_mask & ((uint64_t) 0x01 << type)) - lchan_cnt++; - - if (!lchan_cnt) - return 0; + /* Init queue primitives for TX */ + INIT_LLIST_HEAD(&ts->tx_prims); + /* Init logical channels list */ + INIT_LLIST_HEAD(&ts->lchans); /* Allocate channel states */ - ts->lchans = talloc_zero_array(ts, struct trx_lchan_state, lchan_cnt); - if (ts->lchans == NULL) - return -ENOMEM; + for (type = 0; type < _TRX_CHAN_MAX; type++) { + if (!LAYOUT_HAS_LCHAN(ts->mf_layout, type)) + continue; - /* Init channel states */ - for (type = 0, i = 0; type < _TRX_CHAN_MAX; type++) { - if (ts->mf_layout->lchan_mask & ((uint64_t) 0x01 << type)) { - /* Set proper channel type */ - ts->lchans[i++].type = type; + /* Allocate a channel state */ + lchan = talloc_zero(ts, struct trx_lchan_state); + if (!lchan) + return -ENOMEM; - /* Enable channel automatically if required */ - if (trx_lchan_desc[type].flags & TRX_CH_FLAG_AUTO) - sched_trx_activate_lchan(ts, type); - } + /* Set channel type */ + lchan->type = type; + + /* Add to the list of channel states */ + llist_add_tail(&lchan->list, &ts->lchans); + + /* Enable channel automatically if required */ + if (trx_lchan_desc[type].flags & TRX_CH_FLAG_AUTO) + sched_trx_activate_lchan(ts, type); } /* Notify transceiver about TS activation */ @@ -274,6 +278,7 @@ int sched_trx_configure_ts(struct trx_instance *trx, int tn, int sched_trx_reset_ts(struct trx_instance *trx, int tn) { + struct trx_lchan_state *lchan, *lchan_next; struct trx_ts *ts; /* Try to find specified ts */ @@ -294,7 +299,10 @@ int sched_trx_reset_ts(struct trx_instance *trx, int tn) sched_trx_deactivate_all_lchans(ts); /* Free channel states */ - talloc_free(ts->lchans); + llist_for_each_entry_safe(lchan, lchan_next, &ts->lchans, list) { + llist_del(&lchan->list); + talloc_free(lchan); + } /* Notify transceiver about that */ trx_if_cmd_setslot(trx, tn, 0); @@ -305,9 +313,7 @@ int sched_trx_reset_ts(struct trx_instance *trx, int tn) int sched_trx_start_ciphering(struct trx_ts *ts, uint8_t algo, uint8_t *key, uint8_t key_len) { - struct trx_lchan_state *state; - size_t len; - int i; + struct trx_lchan_state *lchan; /* Prevent NULL-pointer deference */ if (!ts) @@ -318,16 +324,18 @@ int sched_trx_start_ciphering(struct trx_ts *ts, uint8_t algo, return -ERANGE; /* Iterate over all allocated logical channels */ - len = talloc_array_length(ts->lchans); - for (i = 0; i < len; i++) { + llist_for_each_entry(lchan, &ts->lchans, list) { + /* Omit inactive channels */ + if (!lchan->active) + continue; + /* Set key length and algorithm */ - state = ts->lchans + i; - state->a5.key_len = key_len; - state->a5.algo = algo; + lchan->a5.key_len = key_len; + lchan->a5.algo = algo; /* Copy requested key */ if (key_len) - memcpy(state->a5.key, key, key_len); + memcpy(lchan->a5.key, key, key_len); } return 0; @@ -336,12 +344,11 @@ int sched_trx_start_ciphering(struct trx_ts *ts, uint8_t algo, struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts, enum trx_lchan_type chan) { - int i, len; + struct trx_lchan_state *lchan; - len = talloc_array_length(ts->lchans); - for (i = 0; i < len; i++) - if (ts->lchans[i].type == chan) - return ts->lchans + i; + llist_for_each_entry(lchan, &ts->lchans, list) + if (lchan->type == chan) + return lchan; return NULL; } @@ -350,18 +357,16 @@ int sched_trx_set_lchans(struct trx_ts *ts, uint8_t chan_nr, int active) { const struct trx_lchan_desc *lchan_desc; struct trx_lchan_state *lchan; - int len, i, rc = 0; + int rc = 0; /* Prevent NULL-pointer deference */ - if (ts == NULL || ts->lchans == NULL) { + if (ts == NULL) { LOGP(DSCH, LOGL_ERROR, "Timeslot isn't configured\n"); return -EINVAL; } /* Iterate over all allocated lchans */ - len = talloc_array_length(ts->lchans); - for (i = 0; i < len; i++) { - lchan = ts->lchans + i; + llist_for_each_entry(lchan, &ts->lchans, list) { lchan_desc = &trx_lchan_desc[lchan->type]; if (lchan_desc->chan_nr == (chan_nr & 0xf8)) { @@ -396,14 +401,14 @@ int sched_trx_activate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) /* Conditionally allocate memory for bursts */ if (lchan_desc->rx_fn && lchan_desc->burst_buf_size > 0) { - lchan->rx_bursts = talloc_zero_size(ts->lchans, + lchan->rx_bursts = talloc_zero_size(lchan, lchan_desc->burst_buf_size); if (lchan->rx_bursts == NULL) return -ENOMEM; } if (lchan_desc->tx_fn && lchan_desc->burst_buf_size > 0) { - lchan->tx_bursts = talloc_zero_size(ts->lchans, + lchan->tx_bursts = talloc_zero_size(lchan, lchan_desc->burst_buf_size); if (lchan->tx_bursts == NULL) return -ENOMEM; @@ -481,15 +486,11 @@ int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) void sched_trx_deactivate_all_lchans(struct trx_ts *ts) { struct trx_lchan_state *lchan; - int i, len; LOGP(DSCH, LOGL_DEBUG, "Deactivating all logical channels " "on ts=%d\n", ts->index); - len = talloc_array_length(ts->lchans); - for (i = 0; i < len; i++) { - lchan = ts->lchans + i; - + llist_for_each_entry(lchan, &ts->lchans, list) { /* Omit inactive channels */ if (!lchan->active) continue; diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index b93011cb1..7ee1d4725 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -146,6 +146,8 @@ struct trx_lchan_state { enum trx_lchan_type type; /*! \brief Channel status */ uint8_t active; + /*! \brief Link to a list of channels */ + struct llist_head list; /*! \brief Burst type: GMSK or 8PSK */ enum trx_burst_type burst_type; @@ -221,7 +223,7 @@ struct trx_ts { /*! \brief Pointer to multiframe layout */ const struct trx_multiframe *mf_layout; /*! \brief Channel states for logical channels */ - struct trx_lchan_state *lchans; + struct llist_head lchans; /*! \brief Queue primitives for TX */ struct llist_head tx_prims; }; From f09be8a9af8735449a0d846c0b432f7090a4342c Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 5 Jan 2018 14:08:58 +0700 Subject: [PATCH 138/211] trxcon/scheduler: drop meaningless TODO comment Since both TA and AGC loops should be implemented in transceiver, this TODO is meaningless. Let's drop it. Change-Id: I84979712e2a1b849acaee53d5cd50de4e1e357c2 --- src/host/trxcon/sched_lchan_xcch.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index c71b10fd0..201533050 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -112,7 +112,6 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, sched_send_data_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN, rc != 0, n_errors); - /* TODO: AGC, TA loops */ return 0; } From 0192c028e449d9d5f33cf8f0099e0742b1ba80e2 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 5 Jan 2018 15:06:42 +0700 Subject: [PATCH 139/211] trxcon/scheduler: use TCH frame length defs from libosmocodec Change-Id: I6439d3cadd2dc1fa8fe401eb61c977a12ec844f2 --- src/host/trxcon/sched_lchan_common.c | 2 ++ src/host/trxcon/sched_trx.h | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c index 9eccc3e7b..52c9a21f3 100644 --- a/src/host/trxcon/sched_lchan_common.c +++ b/src/host/trxcon/sched_lchan_common.c @@ -33,6 +33,8 @@ #include #include +#include + #include #include diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 7ee1d4725..b01624c8f 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -23,11 +23,6 @@ #define MAX_A5_KEY_LEN (128 / 8) -/* TS 101318 Chapter 5.1: 260 bits + 4bit sig */ -#define GSM_FR_BYTES 33 -/* TS 101318 Chapter 5.3: 244 bits + 4bit sig */ -#define GSM_EFR_BYTES 31 - /* Forward declaration to avoid mutual include */ struct trx_lchan_state; struct trx_instance; From 32462cfd62a25a461d36845e546f150bd8ca0e97 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 20 Jan 2018 16:44:19 +0600 Subject: [PATCH 140/211] fake_trx/burst_send.py: indicate actual burst source Change-Id: I7e45996f4a7a2aacc962ff9b65107c6b04e7bf68 --- src/target/fake_trx/burst_send.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/target/fake_trx/burst_send.py b/src/target/fake_trx/burst_send.py index bed6b446e..a8519edc3 100755 --- a/src/target/fake_trx/burst_send.py +++ b/src/target/fake_trx/burst_send.py @@ -67,8 +67,10 @@ class Application: # Open the burst source (file or stdin) if self.burst_src is not None: + print("[i] Reading bursts from file '%s'..." % self.burst_src) src = open(self.burst_src, "r") else: + print("[i] Reading bursts from stdin...") src = sys.stdin # Generate a random frame number or use provided one From ab7eb390cba51809b81d7fa41f5ecd5755e0b609 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sun, 21 Jan 2018 01:49:27 +0600 Subject: [PATCH 141/211] fake_trx: implement a new tool for TRX protocol sniffing This change introduces a new tool, which could be used to sniff a single connection between L1 and TRX in both directions, filter captured bursts by direction, timeslot and/or frame number, and finally write them to a binary file for further analysis. Sniffing capability is based on Scapy framework, so it should be installed in order to run this tool. Please also note, that sniffing requires root access. For details, see: https://github.com/secdev/scapy https://scapy.readthedocs.io/en/latest/ Usage example: sudo ./trx_sniff --frame-count 30 --timeslot 2 -o /tmp/bursts This command will capture 30 frames on timeslot number 2, and write them to a binary file. The format of this file is based on TLV (Tag Length Value), that wraps each burst: ... |-TAG (byte)-|-LEN (byte)-|-BURST (LEN bytes)-| ... TAG 0x01 - a message coming from L1 to TRX TAG 0x02 - a message coming from TRX to L1 Change-Id: I6e65e1d657574cc3e67bc7cdb1c01ef6bf08ecde --- src/target/fake_trx/README | 6 + src/target/fake_trx/trx_sniff.py | 318 +++++++++++++++++++++++++++++++ 2 files changed, 324 insertions(+) create mode 100755 src/target/fake_trx/trx_sniff.py diff --git a/src/target/fake_trx/README b/src/target/fake_trx/README index 6e3ce03a5..5d4960f4d 100644 --- a/src/target/fake_trx/README +++ b/src/target/fake_trx/README @@ -31,3 +31,9 @@ Brief description of available applications: - burst_send.py - a tool for sending existing bursts from file or standard input either to L1 (OsmoBTS or OsmocomBB) or to TRX (OsmoTRX and GR-GSM TRX). + + - trx_sniff.py - Scapy-based TRX protocol sniffer. Allows one + to observe a single connection between TRX and L1, and vice + versa. Also provides some capabilities for filtering bursts + by direction, frame and timeslot numbers, and for recording + captured messages to a binary file. diff --git a/src/target/fake_trx/trx_sniff.py b/src/target/fake_trx/trx_sniff.py new file mode 100755 index 000000000..cf3ab9ce7 --- /dev/null +++ b/src/target/fake_trx/trx_sniff.py @@ -0,0 +1,318 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# Scapy-based TRX interface sniffer +# +# (C) 2018 by Vadim Yanitskiy +# +# 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. + +import signal +import getopt +import sys + +import scapy.all + +COPYRIGHT = \ + "Copyright (C) 2018 by Vadim Yanitskiy \n" \ + "License GPLv2+: GNU GPL version 2 or later " \ + "\n" \ + "This is free software: you are free to change and redistribute it.\n" \ + "There is NO WARRANTY, to the extent permitted by law.\n" + +class Application: + # Application variables + sniff_interface = "lo" + sniff_base_port = 5700 + print_bursts = False + output_file = None + + # Counters + cnt_burst_dropped_num = 0 + cnt_burst_break = None + cnt_burst_num = 0 + + cnt_frame_break = None + cnt_frame_last = None + cnt_frame_num = 0 + + # Burst direction fliter + bf_dir_l12trx = None + + # Timeslot number filter + bf_tn_val = None + + # Frame number fliter + bf_fn_lt = None + bf_fn_gt = None + + # Internal variables + lo_trigger = False + + def __init__(self): + print(COPYRIGHT) + self.parse_argv() + + # Open requested file for writing + if self.output_file is not None: + self.output_file = open(self.output_file, "ab") + + def run(self): + # Compose a packet filter + pkt_filter = "udp and (port %d or port %d)" \ + % (self.sniff_base_port + 2, self.sniff_base_port + 102) + + print("[i] Listening on interface '%s'..." % self.sniff_interface) + + # Start sniffing... + scapy.all.sniff(iface = self.sniff_interface, store = 1, + filter = pkt_filter, prn = self.pkt_handler) + + # Scapy registers its own signal handler + self.shutdown() + + def pkt_handler(self, ether): + # Prevent loopback packet duplication + if self.sniff_interface == "lo": + self.lo_trigger = not self.lo_trigger + if not self.lo_trigger: + return + + # Extract a TRX payload + ip = ether.payload + udp = ip.payload + trx = udp.payload + + # Convert to bytearray + trx = bytearray(str(trx)) + + # Parse GSM TDMA specific data + fn = (trx[1] << 24) | (trx[2] << 16) | (trx[3] << 8) | trx[4] + tn = trx[0] + + # Determine a burst direction (L1 <-> TRX) + l12trx = udp.sport > udp.dport + + # Poke burst pass filter + rc = self.burst_pass_filter(l12trx, fn, tn) + if rc is False: + self.cnt_burst_dropped_num += 1 + return + + # Debug print + print("[i] %s burst: fn=%u, tn=%d" \ + % ("L1 -> TRX" if l12trx else "TRX -> L1", fn, tn)) + + # Poke burst handler + rc = self.burst_handle(trx, l12trx, fn, tn) + if rc is False: + self.shutdown() + + # Poke burst counter + rc = self.burst_count(fn, tn) + if rc is True: + self.shutdown() + + def burst_pass_filter(self, l12trx, fn, tn): + # Direction filter + if self.bf_dir_l12trx is not None: + if l12trx != self.bf_dir_l12trx: + return False + + # Timeslot filter + if self.bf_tn_val is not None: + if tn != self.bf_tn_val: + return False + + # Frame number filter + if self.bf_fn_lt is not None: + if fn > self.bf_fn_lt: + return False + if self.bf_fn_gt is not None: + if fn < self.bf_fn_gt: + return False + + # Burst passed ;) + return True + + def burst_handle(self, trx_burst, l12trx, fn, tn): + if self.print_bursts: + self.burst_dump_bits(sys.stdout, trx_burst, l12trx) + sys.stdout.flush() + + if self.output_file is not None: + # TLV: tag defines burst direction (one byte, BE) + self.output_file.write('\x01' if l12trx else '\x02') + + # TLV: length of value (one byte, BE) + length = len(trx_burst) + self.output_file.write(chr(length)) + + # TLV: raw value + self.output_file.write(trx_burst) + + return True + + def burst_dump_bits(self, dst, trx_burst, l12trx): + # Split out burst header + if l12trx: + burst = trx_burst[6:] + else: + burst = trx_burst[8:] + + # Print normal bits: 0 or 1 + for i in range(0, 148): + # Convert bits to chars + if l12trx: + # Convert bits as is + bit = '1' if burst[i] else '0' + else: + # Convert trx bits {254..0} to sbits {-127..127} + bit = -127 if burst[i] == 255 else 127 - burst[i] + # Convert sbits {-127..127} to ubits {0..1} + bit = '1' if bit < 0 else '0' + + # Write a normal bit + dst.write(bit) + dst.write("\n") + + def burst_count(self, fn, tn): + # Update frame counter + if self.cnt_frame_last is None: + self.cnt_frame_last = fn + self.cnt_frame_num += 1 + else: + if fn != self.cnt_frame_last: + self.cnt_frame_num += 1 + + # Update burst counter + self.cnt_burst_num += 1 + + # Stop sniffing after N bursts + if self.cnt_burst_break is not None: + if self.cnt_burst_num == self.cnt_burst_break: + print("[i] Collected required amount of bursts") + return True + + # Stop sniffing after N frames + if self.cnt_frame_break is not None: + if self.cnt_frame_num == self.cnt_frame_break: + print("[i] Collected required amount of frames") + return True + + return False + + def shutdown(self): + print("[i] Shutting down...") + + # Print statistics + print("[i] %u bursts handled, %u dropped" \ + % (self.cnt_burst_num, self.cnt_burst_dropped_num)) + + # Close output file if opened + if self.output_file is not None: + self.output_file.close() + + # Exit + sys.exit(0) + + def print_help(self, msg = None): + s = " Usage: " + sys.argv[0] + " [options]\n\n" \ + " Some help...\n" \ + " -h --help this text\n\n" + + s += " Sniffing options\n" \ + " -i --sniff-interface Set network interface (default '%s')\n" \ + " -p --sniff-base-port Set base port number (default %d)\n\n" + + s += " Processing (no processing by default)\n" \ + " -o --output-file Write bursts to file\n" \ + " -v --print-bits Print burst bits to stdout\n\n" \ + + s += " Count limitations (disabled by default)\n" \ + " --frame-count NUM Stop after sniffing NUM frames\n" \ + " --burst-count NUM Stop after sniffing NUM bursts\n\n" + + s += " Filtering (disabled by default)\n" \ + " --direction DIR Burst direction: L12TRX or TRX2L1\n" \ + " --timeslot NUM TDMA timeslot number [0..7]\n" \ + " --frame-num-lt NUM TDMA frame number lower than NUM\n" \ + " --burst-num-gt NUM TDMA frame number greater than NUM\n" + + print(s % (self.sniff_interface, self.sniff_base_port)) + + if msg is not None: + print(msg) + + def parse_argv(self): + try: + opts, args = getopt.getopt(sys.argv[1:], + "i:p:o:v:h", ["help", "sniff-interface=", "sniff-base-port=", + "frame-count=", "burst-count=", "direction=", + "timeslot=", "frame-num-lt=", "frame-num-gt=", + "output-file=", "print-bits"]) + except getopt.GetoptError as err: + self.print_help("[!] " + str(err)) + sys.exit(2) + + for o, v in opts: + if o in ("-h", "--help"): + self.print_help() + sys.exit(2) + + elif o in ("-i", "--sniff-interface"): + self.sniff_interface = v + elif o in ("-p", "--sniff-base-port"): + self.sniff_base_port = int(v) + + elif o in ("-o", "--output-file"): + self.output_file = v + elif o in ("-v", "--print-bits"): + self.print_bursts = True + + # Break counters + elif o == "--frame-count": + self.cnt_frame_break = int(v) + elif o == "--burst-count": + self.cnt_burst_break = int(v) + + # Direction filter + elif o == "--direction": + if v == "L12TRX": + self.bf_dir_l12trx = True + elif v == "TRX2L1": + self.bf_dir_l12trx = False + else: + self.print_help("[!] Wrong direction argument") + sys.exit(2) + + # Timeslot pass filter + elif o == "--timeslot": + self.bf_tn_val = int(v) + if self.bf_tn_val < 0 or self.bf_tn_val > 7: + self.print_help("[!] Wrong timeslot value") + sys.exit(2) + + # Frame number pass filter + elif o == "--frame-num-lt": + self.bf_fn_lt = int(v) + elif o == "--frame-num-gt": + self.bf_fn_gt = int(v) + +if __name__ == '__main__': + app = Application() + app.run() From 5da2ac71971d8568a4c0379a71198c6d759eb5da Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 24 Jan 2018 22:58:48 +0600 Subject: [PATCH 142/211] fake_trx: share and use common GSM constants Previously there were multiple definitions of some common GSM constants in different modules. Let's share them. Change-Id: Id6cdfbc6e8688755a0df7e44daa512c9afa7dad2 --- src/target/fake_trx/burst_gen.py | 5 +++-- src/target/fake_trx/burst_send.py | 5 +++-- src/target/fake_trx/clck_gen.py | 6 +++--- src/target/fake_trx/data_if.py | 7 +++--- src/target/fake_trx/gsm_shared.py | 31 +++++++++++++++++++++++++++ src/target/fake_trx/rand_burst_gen.py | 6 +++--- 6 files changed, 46 insertions(+), 14 deletions(-) create mode 100644 src/target/fake_trx/gsm_shared.py diff --git a/src/target/fake_trx/burst_gen.py b/src/target/fake_trx/burst_gen.py index 087fe791d..68ba0ecef 100755 --- a/src/target/fake_trx/burst_gen.py +++ b/src/target/fake_trx/burst_gen.py @@ -29,6 +29,7 @@ import sys from rand_burst_gen import RandBurstGen from data_if import DATAInterface +from gsm_shared import * COPYRIGHT = \ "Copyright (C) 2017 by Vadim Yanitskiy \n" \ @@ -73,7 +74,7 @@ class Application: # Generate a random frame number or use provided one if self.fn is None: - fn = random.randint(0, DATAInterface.GSM_HYPERFRAME) + fn = random.randint(0, GSM_HYPERFRAME) else: fn = self.fn @@ -106,7 +107,7 @@ class Application: self.tn, fn, self.pwr) # Increase frame number (for count > 1) - fn = (fn + 1) % DATAInterface.GSM_HYPERFRAME + fn = (fn + 1) % GSM_HYPERFRAME self.shutdown() diff --git a/src/target/fake_trx/burst_send.py b/src/target/fake_trx/burst_send.py index a8519edc3..ee8e51abf 100755 --- a/src/target/fake_trx/burst_send.py +++ b/src/target/fake_trx/burst_send.py @@ -27,6 +27,7 @@ import getopt import sys from data_if import DATAInterface +from gsm_shared import * COPYRIGHT = \ "Copyright (C) 2017 by Vadim Yanitskiy \n" \ @@ -75,7 +76,7 @@ class Application: # Generate a random frame number or use provided one if self.fn is None: - fn = random.randint(0, DATAInterface.GSM_HYPERFRAME) + fn = random.randint(0, GSM_HYPERFRAME) else: fn = self.fn @@ -109,7 +110,7 @@ class Application: self.tn, fn, self.pwr) # Increase frame number (for count > 1) - fn = (fn + 1) % DATAInterface.GSM_HYPERFRAME + fn = (fn + 1) % GSM_HYPERFRAME # Finish self.shutdown() diff --git a/src/target/fake_trx/clck_gen.py b/src/target/fake_trx/clck_gen.py index 088155b7b..484ecde54 100755 --- a/src/target/fake_trx/clck_gen.py +++ b/src/target/fake_trx/clck_gen.py @@ -28,6 +28,7 @@ import sys from threading import Timer from udp_link import UDPLink +from gsm_shared import * COPYRIGHT = \ "Copyright (C) 2017 by Vadim Yanitskiy \n" \ @@ -39,7 +40,6 @@ COPYRIGHT = \ class CLCKGen: # GSM TDMA definitions SEC_DELAY_US = 1000 * 1000 - GSM_SUPERFRAME = 2715648 GSM_FRAME_US = 4615.0 # Average loop back delay @@ -74,8 +74,8 @@ class CLCKGen: def send_clck_ind(self): # Keep clock cycle - if self.clck_src % self.GSM_SUPERFRAME >= 0: - self.clck_src %= self.GSM_SUPERFRAME + if self.clck_src % GSM_HYPERFRAME >= 0: + self.clck_src %= GSM_HYPERFRAME # We don't need to send so often if self.clck_src % self.ind_period == 0: diff --git a/src/target/fake_trx/data_if.py b/src/target/fake_trx/data_if.py index 6662f984a..0f373ab5c 100644 --- a/src/target/fake_trx/data_if.py +++ b/src/target/fake_trx/data_if.py @@ -25,10 +25,9 @@ import random from udp_link import UDPLink +from gsm_shared import * class DATAInterface(UDPLink): - # GSM PHY definitions - GSM_HYPERFRAME = 2048 * 26 * 51 def send_l1_msg(self, burst, tn = None, fn = None, rssi = None): @@ -38,7 +37,7 @@ class DATAInterface(UDPLink): # Generate random frame number if not preset if fn is None: - fn = random.randint(0, self.GSM_HYPERFRAME) + fn = random.randint(0, GSM_HYPERFRAME) # Generate random RSSI if not preset if rssi is None: @@ -79,7 +78,7 @@ class DATAInterface(UDPLink): # Generate random frame number if not preset if fn is None: - fn = random.randint(0, self.GSM_HYPERFRAME) + fn = random.randint(0, GSM_HYPERFRAME) # Generate random power level if not preset if pwr is None: diff --git a/src/target/fake_trx/gsm_shared.py b/src/target/fake_trx/gsm_shared.py new file mode 100644 index 000000000..cfe94b010 --- /dev/null +++ b/src/target/fake_trx/gsm_shared.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# Virtual Um-interface (fake transceiver) +# Common GSM constants +# +# (C) 2018 by Vadim Yanitskiy +# +# 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. + +# TDMA definitions +GSM_SUPERFRAME = 26 * 51 +GSM_HYPERFRAME = 2048 * GSM_SUPERFRAME + +# Burst length +GSM_BURST_LEN = 148 +EDGE_BURST_LEN = GSM_BURST_LEN * 3 diff --git a/src/target/fake_trx/rand_burst_gen.py b/src/target/fake_trx/rand_burst_gen.py index 75887f2f4..33e7c53a4 100644 --- a/src/target/fake_trx/rand_burst_gen.py +++ b/src/target/fake_trx/rand_burst_gen.py @@ -24,9 +24,9 @@ import random +from gsm_shared import * + class RandBurstGen: - # GSM L1 definitions - GSM_BURST_LEN = 148 # GSM 05.02 Chapter 5.2.3 Normal Burst nb_tsc_list = [ @@ -125,7 +125,7 @@ class RandBurstGen: # Generate a frequency correction burst def gen_fb(self): - return [0] * self.GSM_BURST_LEN + return [0] * GSM_BURST_LEN # Generate a synchronization burst def gen_sb(self): From f10a8d4c9219b9284ee5922340c182e387bb80dd Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 24 Jan 2018 05:10:09 +0600 Subject: [PATCH 143/211] fake_trx: implement classes for DATA messages This change introduces three new classes: - DATAMSG - abstract class, defines common fields and methods for any message on DATA interface, e.g. frame and timeslot numbers, bit conversation methods, etc. - DATAMSG_L12TRX - a child of DATAMSG, defines a message coming from L1 to TRX. - DATAMSG_TRX2L1 - a child of DATAMSG, defines a message coming from TRX to L1. Both child classes could be used to generate DATA messages from known fields (i.e. fn, tn, etc.), and parse them back from already encoded DATA messages. Change-Id: Id1c72f0b18fb128acc74d0cd899fb7aab7bd8790 --- src/target/fake_trx/data_msg.py | 372 ++++++++++++++++++++++++++++++++ 1 file changed, 372 insertions(+) create mode 100644 src/target/fake_trx/data_msg.py diff --git a/src/target/fake_trx/data_msg.py b/src/target/fake_trx/data_msg.py new file mode 100644 index 000000000..9acc0f1db --- /dev/null +++ b/src/target/fake_trx/data_msg.py @@ -0,0 +1,372 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# Virtual Um-interface (fake transceiver) +# DATA interface message definitions and helpers +# +# (C) 2018 by Vadim Yanitskiy +# +# 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. + +import random + +from gsm_shared import * + +class DATAMSG: + # Common message fields + burst = None + fn = None + tn = None + + # HACK: Abstract class definition + def __init__(self): + raise NotImplementedError + + # Generates message specific header + def gen_hdr(self): + raise NotImplementedError + + # Parses message specific header + def parse_hdr(self, hdr): + raise NotImplementedError + + # Converts unsigned soft-bits {254..0} to soft-bits {-127..127} + def usbit2sbit(self, bits): + buf = [] + + for bit in bits: + if bit == 0xff: + buf.append(-127) + else: + buf.append(127 - bit) + + return buf + + # Converts soft-bits {-127..127} to unsigned soft-bits {254..0} + def sbit2usbit(self, bits): + buf = [] + + for bit in bits: + buf.append(127 - bit) + + return buf + + # Converts soft-bits {-127..127} to bits {1..0} + def sbit2ubit(self, bits): + buf = [] + + for bit in bits: + buf.append(1 if bit < 0 else 0) + + return buf + + # Converts bits {1..0} to soft-bits {-127..127} + def ubit2sbit(self, bits): + buf = [] + + for bit in bits: + buf.append(-127 if bit else 127) + + return buf + + # Validates the message fields + def validate(self): + if self.burst is None: + return False + + if len(self.burst) not in (GSM_BURST_LEN, EDGE_BURST_LEN): + return False + + if self.fn is None: + return False + + if self.fn < 0 or self.fn > GSM_HYPERFRAME: + return False + + if self.tn is None: + return False + + if self.tn < 0 or self.tn > 7: + return False + + return True + + # Generates frame number to bytes + def gen_fn(self, fn): + # Allocate an empty byte-array + buf = bytearray() + + # Big endian, 4 bytes + buf.append((fn >> 24) & 0xff) + buf.append((fn >> 16) & 0xff) + buf.append((fn >> 8) & 0xff) + buf.append((fn >> 0) & 0xff) + + return buf + + # Parses frame number from bytes + def parse_fn(self, buf): + # Big endian, 4 bytes + return (buf[0] << 24) \ + | (buf[1] << 16) \ + | (buf[2] << 8) \ + | (buf[3] << 0) + + # Generates a TRX DATA message + def gen_msg(self): + # Validate all the fields + if not self.validate(): + raise ValueError("Message incomplete or incorrect") + + # Allocate an empty byte-array + buf = bytearray() + + # Put timeslot index + buf.append(self.tn) + + # Put frame number + fn = self.gen_fn(self.fn) + buf += fn + + # Generate message specific header part + hdr = self.gen_hdr() + buf += hdr + + # Put burst + # TODO: distinguish between: usbits, ubits and sbits + buf += bytearray(self.burst) + + return buf + + # Parses a TRX DATA message + def parse_msg(self, msg): + # Calculate message length + length = len(msg) + + # Check length + if length < (self.HDR_LEN + GSM_BURST_LEN): + raise ValueError("Message is to short") + + # Parse both fn and tn + self.fn = self.parse_fn(msg[1:]) + self.tn = msg[0] + + # Specific message part + self.parse_hdr(msg) + + # Copy burst, skipping header + self.burst = msg[self.HDR_LEN:] + +class DATAMSG_L12TRX(DATAMSG): + # Constants + HDR_LEN = 6 + PWR_MIN = 0x00 + PWR_MAX = 0xff + + # Specific message fields + pwr = None + + def __init__(self, fn = None, tn = None, pwr = None, burst = None): + # Init local variables + self.burst = burst + self.pwr = pwr + self.fn = fn + self.tn = tn + + # Validates the message fields + def validate(self): + # Validate common fields + if not DATAMSG.validate(self): + return False + + if self.pwr is None: + return False + + if self.pwr < self.PWR_MIN or self.pwr > self.PWR_MAX: + return False + + return True + + # Generates message specific header part + def gen_hdr(self): + # Allocate an empty byte-array + buf = bytearray() + + # Put power + buf.append(self.pwr) + + return buf + + # Parses message specific header part + def parse_hdr(self, hdr): + # Parse power level + self.pwr = hdr[5] + +class DATAMSG_TRX2L1(DATAMSG): + # Constants + HDR_LEN = 8 + RSSI_MIN = -120 + RSSI_MAX = -50 + + # TODO: verify this range + TOA_MIN = -10.0 + TOA_MAX = 10.0 + + # Specific message fields + rssi = None + toa = None + + def __init__(self, fn = None, tn = None, rssi = None, toa = None, burst = None): + # Init local variables + self.burst = burst + self.rssi = rssi + self.toa = toa + self.fn = fn + self.tn = tn + + # Validates the message fields + def validate(self): + # Validate common fields + if not DATAMSG.validate(self): + return False + + if self.rssi is None: + return False + + if self.rssi < self.RSSI_MIN or self.rssi > self.RSSI_MAX: + return False + + if self.toa is None: + return False + + if self.toa < self.TOA_MIN or self.toa > self.TOA_MAX: + return False + + return True + + # Generates message specific header part + def gen_hdr(self): + # Allocate an empty byte-array + buf = bytearray() + + # Put RSSI + buf.append(-self.rssi) + + # Round ToA (Time of Arrival) to closest integer + toa = int(self.toa * 256.0 + 0.5) + + # Encode ToA + buf.append((toa >> 8) & 0xff) + buf.append(toa & 0xff) + + return buf + + # Parses message specific header part + def parse_hdr(self, hdr): + # Parse RSSI + self.rssi = -(hdr[5]) + + # Parse ToA (Time of Arrival) + # FIXME: parsing unsupported + self.toa = None + +# Regression test +if __name__ == '__main__': + # Common reference data + fn = 1024 + tn = 0 + + # Generate a random burst + burst = bytearray() + for i in range(0, GSM_BURST_LEN): + byte = random.randint(0x00, 0xff) + burst.append(byte) + + print("[i] Generating the reference messages") + + # Create messages of both types + msg_l12trx_ref = DATAMSG_L12TRX(fn = fn, tn = tn) + msg_trx2l1_ref = DATAMSG_TRX2L1(fn = fn, tn = tn) + + # Fill in message specific fields + msg_trx2l1_ref.rssi = -88 + msg_l12trx_ref.pwr = 0x33 + msg_trx2l1_ref.toa = -0.6 + + # Specify the reference burst + msg_trx2l1_ref.burst = burst + msg_l12trx_ref.burst = burst + + print("[i] Encoding the reference messages") + + # Encode DATA messages + l12trx_raw = msg_l12trx_ref.gen_msg() + trx2l1_raw = msg_trx2l1_ref.gen_msg() + + print("[i] Parsing generated messages back") + + # Parse generated DATA messages + msg_l12trx_dec = DATAMSG_L12TRX() + msg_trx2l1_dec = DATAMSG_TRX2L1() + msg_l12trx_dec.parse_msg(l12trx_raw) + msg_trx2l1_dec.parse_msg(trx2l1_raw) + + print("[i] Comparing decoded messages with the reference") + + # Compare bursts + assert(msg_l12trx_dec.burst == burst) + assert(msg_trx2l1_dec.burst == burst) + + print("[?] Compare bursts: OK") + + # Compare both parsed messages with the reference data + assert(msg_l12trx_dec.fn == fn) + assert(msg_trx2l1_dec.fn == fn) + assert(msg_l12trx_dec.tn == tn) + assert(msg_trx2l1_dec.tn == tn) + + print("[?] Compare FN / TN: OK") + + # Compare message specific parts + assert(msg_trx2l1_dec.rssi == msg_trx2l1_ref.rssi) + assert(msg_l12trx_dec.pwr == msg_l12trx_ref.pwr) + + # FIXME: ToA check disabled until the parsing is implemented + # assert(msg_trx2l1_dec.toa == msg_trx2l1_ref.toa) + + print("[?] Compare message specific data: OK") + + # Bit conversation test + usbits_ref = range(0, 256) + sbits_ref = range(-127, 128) + + # Test both usbit2sbit() and sbit2usbit() + sbits = msg_trx2l1_ref.usbit2sbit(usbits_ref) + usbits = msg_trx2l1_ref.sbit2usbit(sbits) + assert(usbits[:255] == usbits_ref[:255]) + assert(usbits[255] == 254) + + print("[?] Check both usbit2sbit() and sbit2usbit(): OK") + + # Test both sbit2ubit() and ubit2sbit() + ubits = msg_trx2l1_ref.sbit2ubit(sbits_ref) + assert(ubits == ([1] * 127 + [0] * 128)) + + sbits = msg_trx2l1_ref.ubit2sbit(ubits) + assert(sbits == ([-127] * 127 + [127] * 128)) + + print("[?] Check both sbit2ubit() and ubit2sbit(): OK") From 8c79e2ce6b7aa61687cdda8fd0bd01d7a2e6d9e8 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 25 Jan 2018 01:24:44 +0600 Subject: [PATCH 144/211] fake_trx/data_msg.py: implement header randomization This feature could be used by both burst_gen.py and burst_send.py. Change-Id: I724e267382ff32ef1f964b1ee6cbe99069139867 --- src/target/fake_trx/data_msg.py | 64 +++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/target/fake_trx/data_msg.py b/src/target/fake_trx/data_msg.py index 9acc0f1db..c561280fe 100644 --- a/src/target/fake_trx/data_msg.py +++ b/src/target/fake_trx/data_msg.py @@ -44,6 +44,19 @@ class DATAMSG: def parse_hdr(self, hdr): raise NotImplementedError + # Generates a random frame number + def rand_fn(self): + return random.randint(0, GSM_HYPERFRAME) + + # Generates a random timeslot number + def rand_tn(self): + return random.randint(0, 7) + + # Randomizes the message header + def rand_hdr(self): + self.fn = self.rand_fn() + self.tn = self.rand_tn() + # Converts unsigned soft-bits {254..0} to soft-bits {-127..127} def usbit2sbit(self, bits): buf = [] @@ -201,6 +214,21 @@ class DATAMSG_L12TRX(DATAMSG): return True + # Generates a random power level + def rand_pwr(self, min = None, max = None): + if min is None: + min = self.PWR_MIN + + if max is None: + max = self.PWR_MAX + + return random.randint(min, max) + + # Randomizes message specific header + def rand_hdr(self): + DATAMSG.rand_hdr(self) + self.pwr = self.rand_pwr() + # Generates message specific header part def gen_hdr(self): # Allocate an empty byte-array @@ -258,6 +286,32 @@ class DATAMSG_TRX2L1(DATAMSG): return True + # Generates a random RSSI value + def rand_rssi(self, min = None, max = None): + if min is None: + min = self.RSSI_MIN + + if max is None: + max = self.RSSI_MAX + + return random.randint(min, max) + + # Generates a ToA (Time of Arrival) value + def rand_toa(self, min = None, max = None): + if min is None: + min = self.TOA_MIN + + if max is None: + max = self.TOA_MAX + + return random.uniform(min, max) + + # Randomizes message specific header + def rand_hdr(self): + DATAMSG.rand_hdr(self) + self.rssi = self.rand_rssi() + self.toa = self.rand_toa() + # Generates message specific header part def gen_hdr(self): # Allocate an empty byte-array @@ -350,6 +404,16 @@ if __name__ == '__main__': print("[?] Compare message specific data: OK") + # Validate header randomization + for i in range(0, 100): + msg_l12trx_ref.rand_hdr() + msg_trx2l1_ref.rand_hdr() + + assert(msg_l12trx_ref.validate()) + assert(msg_trx2l1_ref.validate()) + + print("[?] Validate header randomization: OK") + # Bit conversation test usbits_ref = range(0, 256) sbits_ref = range(-127, 128) From 475ece71825f38b9cbface49b432346b9a9cdf8a Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 27 Jan 2018 20:04:25 +0700 Subject: [PATCH 145/211] fake_trx/data_msg.py: handle bursts properly One L1 -> TRX message carries one to be transmitted burst encoded as regular bits (0 or 1). One TRX -> L1 message carries one received burst encoded as unsigned soft-bits (0..254). This shall be noted during message encoding and decoding process. Also, we shall distinguish between GSM and EDGE bursts. Change-Id: I909b7a4dc70e8c632987bde07f00281a6595c4cb --- src/target/fake_trx/data_msg.py | 78 +++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/src/target/fake_trx/data_msg.py b/src/target/fake_trx/data_msg.py index c561280fe..2805113cf 100644 --- a/src/target/fake_trx/data_msg.py +++ b/src/target/fake_trx/data_msg.py @@ -44,6 +44,14 @@ class DATAMSG: def parse_hdr(self, hdr): raise NotImplementedError + # Generates message specific burst + def gen_burst(self): + raise NotImplementedError + + # Parses message specific burst + def parse_burst(self, burst): + raise NotImplementedError + # Generates a random frame number def rand_fn(self): return random.randint(0, GSM_HYPERFRAME) @@ -159,9 +167,8 @@ class DATAMSG: hdr = self.gen_hdr() buf += hdr - # Put burst - # TODO: distinguish between: usbits, ubits and sbits - buf += bytearray(self.burst) + # Generate burst + buf += self.gen_burst() return buf @@ -182,7 +189,8 @@ class DATAMSG: self.parse_hdr(msg) # Copy burst, skipping header - self.burst = msg[self.HDR_LEN:] + msg_burst = msg[self.HDR_LEN:] + self.parse_burst(msg_burst) class DATAMSG_L12TRX(DATAMSG): # Constants @@ -244,6 +252,21 @@ class DATAMSG_L12TRX(DATAMSG): # Parse power level self.pwr = hdr[5] + # Generates message specific burst + def gen_burst(self): + # Copy burst 'as is' + return bytearray(self.burst) + + # Parses message specific burst + def parse_burst(self, burst): + length = len(burst) + + # Distinguish between GSM and EDGE + if length >= EDGE_BURST_LEN: + self.burst = list(burst[:EDGE_BURST_LEN]) + else: + self.burst = list(burst[:GSM_BURST_LEN]) + class DATAMSG_TRX2L1(DATAMSG): # Constants HDR_LEN = 8 @@ -338,17 +361,46 @@ class DATAMSG_TRX2L1(DATAMSG): # FIXME: parsing unsupported self.toa = None + # Generates message specific burst + def gen_burst(self): + # Convert soft-bits to unsigned soft-bits + burst_usbits = self.sbit2usbit(self.burst) + + # Encode to bytes + return bytearray(burst_usbits) + + # Parses message specific burst + def parse_burst(self, burst): + length = len(burst) + + # Distinguish between GSM and EDGE + if length >= EDGE_BURST_LEN: + burst_usbits = list(burst[:EDGE_BURST_LEN]) + else: + burst_usbits = list(burst[:GSM_BURST_LEN]) + + # Convert unsigned soft-bits to soft-bits + burst_sbits = self.usbit2sbit(burst_usbits) + + # Save + self.burst = burst_sbits + # Regression test if __name__ == '__main__': # Common reference data fn = 1024 tn = 0 - # Generate a random burst - burst = bytearray() + # Generate two random bursts + burst_l12trx_ref = [] + burst_trx2l1_ref = [] + for i in range(0, GSM_BURST_LEN): - byte = random.randint(0x00, 0xff) - burst.append(byte) + ubit = random.randint(0, 1) + burst_l12trx_ref.append(ubit) + + sbit = random.randint(-127, 127) + burst_trx2l1_ref.append(sbit) print("[i] Generating the reference messages") @@ -361,9 +413,9 @@ if __name__ == '__main__': msg_l12trx_ref.pwr = 0x33 msg_trx2l1_ref.toa = -0.6 - # Specify the reference burst - msg_trx2l1_ref.burst = burst - msg_l12trx_ref.burst = burst + # Specify the reference bursts + msg_l12trx_ref.burst = burst_l12trx_ref + msg_trx2l1_ref.burst = burst_trx2l1_ref print("[i] Encoding the reference messages") @@ -382,8 +434,8 @@ if __name__ == '__main__': print("[i] Comparing decoded messages with the reference") # Compare bursts - assert(msg_l12trx_dec.burst == burst) - assert(msg_trx2l1_dec.burst == burst) + assert(msg_l12trx_dec.burst == burst_l12trx_ref) + assert(msg_trx2l1_dec.burst == burst_trx2l1_ref) print("[?] Compare bursts: OK") From d9553faf62a6e5eec339dd0c324b1a31a71350ab Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 27 Jan 2018 20:44:15 +0700 Subject: [PATCH 146/211] fake_trx/data_msg.py: implement header description This change introduces a new method for both types of messages called 'desc_hdr', that generates human-readable header description. Examples: TRX -> L1: fn=571353 tn=1 rssi=-108 toa=-0.53 L1 -> TRX: fn=1777477 tn=3 pwr=161 Change-Id: Iafe63e39ad68f4ff373ae098424d76ca9f83c8fc --- src/target/fake_trx/data_msg.py | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/target/fake_trx/data_msg.py b/src/target/fake_trx/data_msg.py index 2805113cf..77d4f52bf 100644 --- a/src/target/fake_trx/data_msg.py +++ b/src/target/fake_trx/data_msg.py @@ -65,6 +65,18 @@ class DATAMSG: self.fn = self.rand_fn() self.tn = self.rand_tn() + # Generates human-readable header description + def desc_hdr(self): + result = "" + + if self.fn is not None: + result += ("fn=%u " % self.fn) + + if self.tn is not None: + result += ("tn=%u " % self.tn) + + return result + # Converts unsigned soft-bits {254..0} to soft-bits {-127..127} def usbit2sbit(self, bits): buf = [] @@ -237,6 +249,17 @@ class DATAMSG_L12TRX(DATAMSG): DATAMSG.rand_hdr(self) self.pwr = self.rand_pwr() + # Generates human-readable header description + def desc_hdr(self): + # Describe the common part + result = DATAMSG.desc_hdr(self) + + if self.pwr is not None: + result += ("pwr=%u " % self.pwr) + + # Strip useless whitespace and return + return result.strip() + # Generates message specific header part def gen_hdr(self): # Allocate an empty byte-array @@ -335,6 +358,20 @@ class DATAMSG_TRX2L1(DATAMSG): self.rssi = self.rand_rssi() self.toa = self.rand_toa() + # Generates human-readable header description + def desc_hdr(self): + # Describe the common part + result = DATAMSG.desc_hdr(self) + + if self.rssi is not None: + result += ("rssi=%d " % self.rssi) + + if self.toa is not None: + result += ("toa=%.2f " % self.toa) + + # Strip useless whitespace and return + return result.strip() + # Generates message specific header part def gen_hdr(self): # Allocate an empty byte-array From b84699096a3fc40717b792ddf51b678ee7503d91 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 27 Jan 2018 20:55:57 +0700 Subject: [PATCH 147/211] fake_trx/burst_gen.py: check argv separately Change-Id: I35b5475d3b6df6dc92a1981c693afb63df866c87 --- src/target/fake_trx/burst_gen.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/target/fake_trx/burst_gen.py b/src/target/fake_trx/burst_gen.py index 68ba0ecef..66d8e89e2 100755 --- a/src/target/fake_trx/burst_gen.py +++ b/src/target/fake_trx/burst_gen.py @@ -53,6 +53,7 @@ class Application: def __init__(self): self.print_copyright() self.parse_argv() + self.check_argv() # Set up signal handlers signal.signal(signal.SIGINT, self.sig_handler) @@ -65,9 +66,6 @@ class Application: elif self.conn_mode == "L1": self.data_if = DATAInterface(self.remote_addr, self.base_port + 102, self.base_port + 2) - else: - self.print_help("[!] Unknown connection type") - sys.exit(2) # Init random burst generator self.gen = RandBurstGen() @@ -89,10 +87,6 @@ class Application: buf = self.gen.gen_sb() elif self.burst_type == "AB": buf = self.gen.gen_ab() - else: - self.print_help("[!] Unknown burst type") - self.shutdown() - sys.exit(2) print("[i] Sending %d/%d %s burst (fn=%u) to %s..." % (i + 1, self.burst_count, self.burst_type, @@ -178,6 +172,17 @@ class Application: elif o in ("-l", "--power-level"): self.pwr = abs(int(v)) + def check_argv(self): + # Check connection mode + if self.conn_mode not in ("TRX", "L1"): + self.print_help("[!] Unknown connection type") + sys.exit(2) + + # Check connection mode + if self.burst_type not in ("NB", "FB", "SB", "AB"): + self.print_help("[!] Unknown burst type") + sys.exit(2) + def shutdown(self): self.data_if.shutdown() From 263ccef268c45a122d9d35b217babd772c65ccb2 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 27 Jan 2018 21:11:46 +0700 Subject: [PATCH 148/211] fake_trx/burst_gen.py: don't store RandBurstGen No need to keep it as a class member. Change-Id: I5bf5846c2b8fa1211cf5150545b9d001c17fa0eb --- src/target/fake_trx/burst_gen.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/target/fake_trx/burst_gen.py b/src/target/fake_trx/burst_gen.py index 66d8e89e2..04b7f477b 100755 --- a/src/target/fake_trx/burst_gen.py +++ b/src/target/fake_trx/burst_gen.py @@ -68,7 +68,7 @@ class Application: self.base_port + 102, self.base_port + 2) # Init random burst generator - self.gen = RandBurstGen() + burst_gen = RandBurstGen() # Generate a random frame number or use provided one if self.fn is None: @@ -80,13 +80,13 @@ class Application: for i in range(self.burst_count): # Generate a random burst if self.burst_type == "NB": - buf = self.gen.gen_nb() + buf = burst_gen.gen_nb() elif self.burst_type == "FB": - buf = self.gen.gen_fb() + buf = burst_gen.gen_fb() elif self.burst_type == "SB": - buf = self.gen.gen_sb() + buf = burst_gen.gen_sb() elif self.burst_type == "AB": - buf = self.gen.gen_ab() + buf = burst_gen.gen_ab() print("[i] Sending %d/%d %s burst (fn=%u) to %s..." % (i + 1, self.burst_count, self.burst_type, From d273ebf611bfafa6234961b13979f21d0223caec Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 27 Jan 2018 21:47:31 +0700 Subject: [PATCH 149/211] fake_trx: use DATAMSG classes for DATA messages The DATAMSG API, that was introduced and extended a few commits before, provides all required methods to create, validate, generate and parse DATA messages. Let's use it now. Change-Id: Ibc99126dc05d873c1ba538a5f4e74866de563f56 --- src/target/fake_trx/burst_gen.py | 62 ++++++++++++++-------- src/target/fake_trx/burst_send.py | 63 +++++++++++++++-------- src/target/fake_trx/data_if.py | 85 ++++--------------------------- src/target/fake_trx/trx_sniff.py | 61 +++++++++------------- 4 files changed, 114 insertions(+), 157 deletions(-) diff --git a/src/target/fake_trx/burst_gen.py b/src/target/fake_trx/burst_gen.py index 04b7f477b..ced2de3d9 100755 --- a/src/target/fake_trx/burst_gen.py +++ b/src/target/fake_trx/burst_gen.py @@ -4,7 +4,7 @@ # Auxiliary tool to generate and send random bursts via TRX DATA # interface, which may be useful for fuzzing and testing # -# (C) 2017 by Vadim Yanitskiy +# (C) 2017-2018 by Vadim Yanitskiy # # All Rights Reserved # @@ -22,7 +22,6 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import random import signal import getopt import sys @@ -30,6 +29,7 @@ import sys from rand_burst_gen import RandBurstGen from data_if import DATAInterface from gsm_shared import * +from data_msg import * COPYRIGHT = \ "Copyright (C) 2017 by Vadim Yanitskiy \n" \ @@ -70,38 +70,56 @@ class Application: # Init random burst generator burst_gen = RandBurstGen() + # Init an empty DATA message + if self.conn_mode == "TRX": + msg = DATAMSG_L12TRX() + elif self.conn_mode == "L1": + msg = DATAMSG_TRX2L1() + # Generate a random frame number or use provided one - if self.fn is None: - fn = random.randint(0, GSM_HYPERFRAME) - else: - fn = self.fn + fn_init = msg.rand_fn() if self.fn is None else self.fn # Send as much bursts as required for i in range(self.burst_count): + # Randomize the message header + msg.rand_hdr() + + # Increase and set frame number + msg.fn = (fn_init + i) % GSM_HYPERFRAME + + # Set timeslot number + if self.tn is not None: + msg.tn = self.tn + + # Set transmit power level + if self.pwr is not None: + msg.pwr = self.pwr + + # TODO: also set TRX2L1 specific fields + # Generate a random burst if self.burst_type == "NB": - buf = burst_gen.gen_nb() + burst = burst_gen.gen_nb() elif self.burst_type == "FB": - buf = burst_gen.gen_fb() + burst = burst_gen.gen_fb() elif self.burst_type == "SB": - buf = burst_gen.gen_sb() + burst = burst_gen.gen_sb() elif self.burst_type == "AB": - buf = burst_gen.gen_ab() + burst = burst_gen.gen_ab() - print("[i] Sending %d/%d %s burst (fn=%u) to %s..." + # Convert to soft-bits in case of TRX -> L1 message + if self.conn_mode == "L1": + burst = msg.ubit2sbit(burst) + + # Set burst + msg.burst = burst + + print("[i] Sending %d/%d %s burst %s to %s..." % (i + 1, self.burst_count, self.burst_type, - fn, self.conn_mode)) + msg.desc_hdr(), self.conn_mode)) - # Send to TRX or L1 - if self.conn_mode == "TRX": - self.data_if.send_trx_msg(buf, - self.tn, fn, self.pwr) - elif self.conn_mode == "L1": - self.data_if.send_l1_msg(buf, - self.tn, fn, self.pwr) - - # Increase frame number (for count > 1) - fn = (fn + 1) % GSM_HYPERFRAME + # Send message + self.data_if.send_msg(msg) self.shutdown() diff --git a/src/target/fake_trx/burst_send.py b/src/target/fake_trx/burst_send.py index ee8e51abf..4dbe9846b 100755 --- a/src/target/fake_trx/burst_send.py +++ b/src/target/fake_trx/burst_send.py @@ -3,7 +3,7 @@ # Auxiliary tool to send existing bursts via TRX DATA interface # -# (C) 2017 by Vadim Yanitskiy +# (C) 2017-2018 by Vadim Yanitskiy # # All Rights Reserved # @@ -21,13 +21,13 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import random import signal import getopt import sys from data_if import DATAInterface from gsm_shared import * +from data_msg import * COPYRIGHT = \ "Copyright (C) 2017 by Vadim Yanitskiy \n" \ @@ -74,40 +74,61 @@ class Application: print("[i] Reading bursts from stdin...") src = sys.stdin + # Init an empty DATA message + if self.conn_mode == "TRX": + msg = DATAMSG_L12TRX() + elif self.conn_mode == "L1": + msg = DATAMSG_TRX2L1() + # Generate a random frame number or use provided one - if self.fn is None: - fn = random.randint(0, GSM_HYPERFRAME) - else: - fn = self.fn + fn = msg.rand_fn() if self.fn is None else self.fn # Read the burst source line-by-line for line in src: # Strip spaces - burst = line.strip() - buf = [] + burst_str = line.strip() + burst = [] # Check length - if len(burst) != 148: + if len(burst_str) != 148: print("[!] Dropping burst due to length != 148") continue - print("[i] Sending a burst (fn=%u) to %s..." - % (fn, self.conn_mode)) + # Randomize the message header + msg.rand_hdr() + + # Set frame number + msg.fn = fn + + # Set timeslot number + if self.tn is not None: + msg.tn = self.tn + + # Set transmit power level + if self.pwr is not None: + msg.pwr = self.pwr + + # TODO: also set TRX2L1 specific fields # Parse a string - for bit in burst: + for bit in burst_str: if bit == "1": - buf.append(1) + burst.append(1) else: - buf.append(0) + burst.append(0) - # Send to TRX or L1 - if self.conn_mode == "TRX": - self.data_if.send_trx_msg(buf, - self.tn, fn, self.pwr) - elif self.conn_mode == "L1": - self.data_if.send_l1_msg(buf, - self.tn, fn, self.pwr) + # Convert to soft-bits in case of TRX -> L1 message + if self.conn_mode == "L1": + burst = msg.ubit2sbit(burst) + + # Set burst + msg.burst = burst + + print("[i] Sending a burst %s to %s..." + % (msg.desc_hdr(), self.conn_mode)) + + # Send message + self.data_if.send_msg(msg) # Increase frame number (for count > 1) fn = (fn + 1) % GSM_HYPERFRAME diff --git a/src/target/fake_trx/data_if.py b/src/target/fake_trx/data_if.py index 0f373ab5c..8b0cd8e56 100644 --- a/src/target/fake_trx/data_if.py +++ b/src/target/fake_trx/data_if.py @@ -4,7 +4,7 @@ # Virtual Um-interface (fake transceiver) # DATA interface implementation # -# (C) 2017 by Vadim Yanitskiy +# (C) 2017-2018 by Vadim Yanitskiy # # All Rights Reserved # @@ -22,85 +22,18 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import random - from udp_link import UDPLink -from gsm_shared import * +from data_msg import * class DATAInterface(UDPLink): - def send_l1_msg(self, burst, - tn = None, fn = None, rssi = None): - # Generate random timeslot index if not preset - if tn is None: - tn = random.randint(0, 7) + def send_msg(self, msg): + # Validate a message + if not msg.validate(): + raise ValueError("Message incomplete or incorrect") - # Generate random frame number if not preset - if fn is None: - fn = random.randint(0, GSM_HYPERFRAME) - - # Generate random RSSI if not preset - if rssi is None: - rssi = -random.randint(-75, -50) - - # Prepare a buffer for header and burst - buf = [] - - # Put timeslot index - buf.append(tn) - - # Put frame number - buf.append((fn >> 24) & 0xff) - buf.append((fn >> 16) & 0xff) - buf.append((fn >> 8) & 0xff) - buf.append((fn >> 0) & 0xff) - - # Put RSSI - buf.append(rssi) - - # HACK: put fake TOA value - buf += [0x00] * 2 - - # Put burst - buf += burst - - # Put two unused bytes - buf += [0x00] * 2 + # Generate TRX message + payload = msg.gen_msg() # Send message - self.send(bytearray(buf)) - - def send_trx_msg(self, burst, - tn = None, fn = None, pwr = None): - # Generate random timeslot index if not preset - if tn is None: - tn = random.randint(0, 7) - - # Generate random frame number if not preset - if fn is None: - fn = random.randint(0, GSM_HYPERFRAME) - - # Generate random power level if not preset - if pwr is None: - pwr = random.randint(0, 34) - - # Prepare a buffer for header and burst - buf = [] - - # Put timeslot index - buf.append(tn) - - # Put frame number - buf.append((fn >> 24) & 0xff) - buf.append((fn >> 16) & 0xff) - buf.append((fn >> 8) & 0xff) - buf.append((fn >> 0) & 0xff) - - # Put transmit power level - buf.append(pwr) - - # Put burst - buf += burst - - # Send message - self.send(bytearray(buf)) + self.send(payload) diff --git a/src/target/fake_trx/trx_sniff.py b/src/target/fake_trx/trx_sniff.py index cf3ab9ce7..91c2c4ad3 100755 --- a/src/target/fake_trx/trx_sniff.py +++ b/src/target/fake_trx/trx_sniff.py @@ -27,6 +27,8 @@ import sys import scapy.all +from data_msg import * + COPYRIGHT = \ "Copyright (C) 2018 by Vadim Yanitskiy \n" \ "License GPLv2+: GNU GPL version 2 or later " \ @@ -98,32 +100,39 @@ class Application: trx = udp.payload # Convert to bytearray - trx = bytearray(str(trx)) - - # Parse GSM TDMA specific data - fn = (trx[1] << 24) | (trx[2] << 16) | (trx[3] << 8) | trx[4] - tn = trx[0] + msg_raw = bytearray(str(trx)) # Determine a burst direction (L1 <-> TRX) l12trx = udp.sport > udp.dport + # Create an empty DATA message + msg = DATAMSG_L12TRX() if l12trx else DATAMSG_TRX2L1() + + # Attempt to parse the payload as a DATA message + try: + msg.parse_msg(msg_raw) + except: + print("[!] Failed to parse message, dropping...") + self.cnt_burst_dropped_num += 1 + return + # Poke burst pass filter - rc = self.burst_pass_filter(l12trx, fn, tn) + rc = self.burst_pass_filter(l12trx, msg.fn, msg.tn) if rc is False: self.cnt_burst_dropped_num += 1 return # Debug print - print("[i] %s burst: fn=%u, tn=%d" \ - % ("L1 -> TRX" if l12trx else "TRX -> L1", fn, tn)) + print("[i] %s burst: %s" \ + % ("L1 -> TRX" if l12trx else "TRX -> L1", msg.desc_hdr())) # Poke burst handler - rc = self.burst_handle(trx, l12trx, fn, tn) + rc = self.burst_handle(l12trx, msg_raw, msg) if rc is False: self.shutdown() # Poke burst counter - rc = self.burst_count(fn, tn) + rc = self.burst_count(msg.fn, msg.tn) if rc is True: self.shutdown() @@ -149,47 +158,23 @@ class Application: # Burst passed ;) return True - def burst_handle(self, trx_burst, l12trx, fn, tn): + def burst_handle(self, l12trx, msg_raw, msg): if self.print_bursts: - self.burst_dump_bits(sys.stdout, trx_burst, l12trx) - sys.stdout.flush() + print(msg.burst) if self.output_file is not None: # TLV: tag defines burst direction (one byte, BE) self.output_file.write('\x01' if l12trx else '\x02') # TLV: length of value (one byte, BE) - length = len(trx_burst) + length = len(msg_raw) self.output_file.write(chr(length)) # TLV: raw value - self.output_file.write(trx_burst) + self.output_file.write(msg_raw) return True - def burst_dump_bits(self, dst, trx_burst, l12trx): - # Split out burst header - if l12trx: - burst = trx_burst[6:] - else: - burst = trx_burst[8:] - - # Print normal bits: 0 or 1 - for i in range(0, 148): - # Convert bits to chars - if l12trx: - # Convert bits as is - bit = '1' if burst[i] else '0' - else: - # Convert trx bits {254..0} to sbits {-127..127} - bit = -127 if burst[i] == 255 else 127 - burst[i] - # Convert sbits {-127..127} to ubits {0..1} - bit = '1' if bit < 0 else '0' - - # Write a normal bit - dst.write(bit) - dst.write("\n") - def burst_count(self, fn, tn): # Update frame counter if self.cnt_frame_last is None: From e3a23102b715387b0a3d8d4e16d04ac52cde98b4 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 27 Jan 2018 22:09:53 +0700 Subject: [PATCH 150/211] fake_trx/burst_gen.py: also handle RSSI and ToA values Change-Id: I7c9441c1154c925dcb5c743e39445495233c123e --- src/target/fake_trx/burst_gen.py | 36 +++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/target/fake_trx/burst_gen.py b/src/target/fake_trx/burst_gen.py index ced2de3d9..9141b6748 100755 --- a/src/target/fake_trx/burst_gen.py +++ b/src/target/fake_trx/burst_gen.py @@ -46,10 +46,16 @@ class Application: burst_type = None burst_count = 1 - pwr = None + + # Common header fields fn = None tn = None + # Message specific header fields + rssi = None + toa = None + pwr = None + def __init__(self): self.print_copyright() self.parse_argv() @@ -95,7 +101,13 @@ class Application: if self.pwr is not None: msg.pwr = self.pwr - # TODO: also set TRX2L1 specific fields + # Set time of arrival + if self.toa is not None: + msg.toa = self.toa + + # Set RSSI + if self.rssi is not None: + msg.rssi = self.rssi # Generate a random burst if self.burst_type == "NB": @@ -141,7 +153,9 @@ class Application: " -c --burst-count How much bursts to send (default 1)\n" \ " -f --frame-number Set frame number (default random)\n" \ " -t --timeslot Set timeslot index (default random)\n" \ - " -l --power-level Set transmit level (default random)\n" \ + " --pwr Set power level (default random)\n" \ + " --rssi Set RSSI (default random)\n" \ + " --toa Set TOA (default random)\n\n" print(s % (self.remote_addr, self.base_port)) @@ -151,7 +165,7 @@ class Application: def parse_argv(self): try: opts, args = getopt.getopt(sys.argv[1:], - "m:r:p:b:c:f:t:l:h", + "m:r:p:b:c:f:t:h", [ "help", "conn-mode=", @@ -161,7 +175,9 @@ class Application: "burst-count=", "frame-number=", "timeslot=", - "power-level=", + "rssi=", + "toa=", + "pwr=", ]) except getopt.GetoptError as err: self.print_help("[!] " + str(err)) @@ -187,8 +203,14 @@ class Application: self.fn = int(v) elif o in ("-t", "--timeslot"): self.tn = int(v) - elif o in ("-l", "--power-level"): - self.pwr = abs(int(v)) + + # Message specific header fields + elif o == "--pwr": + self.pwr = int(v) + elif o == "--rssi": + self.rssi = int(v) + elif o == "--toa": + self.toa = float(v) def check_argv(self): # Check connection mode From 630cc5a3670ab6957c01c4bd6c8cc739c7aebc48 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 27 Jan 2018 22:13:49 +0700 Subject: [PATCH 151/211] fake_trx/burst_send.py: also handle RSSI and ToA values Change-Id: Idb9a5ae4a8980a320f6e620c66add7c7393d3ecb --- src/target/fake_trx/burst_send.py | 36 +++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/target/fake_trx/burst_send.py b/src/target/fake_trx/burst_send.py index 4dbe9846b..736792560 100755 --- a/src/target/fake_trx/burst_send.py +++ b/src/target/fake_trx/burst_send.py @@ -43,10 +43,16 @@ class Application: conn_mode = "TRX" burst_src = None - pwr = None + + # Common header fields fn = None tn = None + # Message specific header fields + rssi = None + toa = None + pwr = None + def __init__(self): self.print_copyright() self.parse_argv() @@ -108,7 +114,13 @@ class Application: if self.pwr is not None: msg.pwr = self.pwr - # TODO: also set TRX2L1 specific fields + # Set time of arrival + if self.toa is not None: + msg.toa = self.toa + + # Set RSSI + if self.rssi is not None: + msg.rssi = self.rssi # Parse a string for bit in burst_str: @@ -153,7 +165,9 @@ class Application: " -i --burst-file Read bursts from file (default stdin)\n" \ " -f --frame-number Set frame number (default random)\n" \ " -t --timeslot Set timeslot index (default random)\n" \ - " -l --power-level Set transmit level (default random)\n" \ + " --pwr Set power level (default random)\n" \ + " --rssi Set RSSI (default random)\n" \ + " --toa Set TOA (default random)\n\n" print(s % (self.remote_addr, self.base_port)) @@ -163,7 +177,7 @@ class Application: def parse_argv(self): try: opts, args = getopt.getopt(sys.argv[1:], - "m:r:p:i:f:t:l:h", + "m:r:p:i:f:t:h", [ "help", "conn-mode=", @@ -172,7 +186,9 @@ class Application: "burst-file=", "frame-number=", "timeslot=", - "power-level=", + "rssi=", + "toa=", + "pwr=", ]) except getopt.GetoptError as err: self.print_help("[!] " + str(err)) @@ -196,8 +212,14 @@ class Application: self.fn = int(v) elif o in ("-t", "--timeslot"): self.tn = int(v) - elif o in ("-l", "--power-level"): - self.pwr = abs(int(v)) + + # Message specific header fields + elif o == "--pwr": + self.pwr = int(v) + elif o == "--rssi": + self.rssi = int(v) + elif o == "--toa": + self.toa = float(v) def shutdown(self): self.data_if.shutdown() From 041bfc0b0313c2b89d3ff0488d3d5f54d22fa197 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 27 Jan 2018 23:14:01 +0700 Subject: [PATCH 152/211] fake_trx/burst_send.py: handle both GSM and EDGE bursts Previously, it was expected that burst length should be equal to 148. Let's also handle EDGE bursts and use GSM constants. Change-Id: Iab13dd06f175556137c5e25d2cbddb9bea403b09 --- src/target/fake_trx/burst_send.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/fake_trx/burst_send.py b/src/target/fake_trx/burst_send.py index 736792560..b51ce5d4c 100755 --- a/src/target/fake_trx/burst_send.py +++ b/src/target/fake_trx/burst_send.py @@ -96,8 +96,8 @@ class Application: burst = [] # Check length - if len(burst_str) != 148: - print("[!] Dropping burst due to length != 148") + if len(burst_str) not in (GSM_BURST_LEN, EDGE_BURST_LEN): + print("[!] Dropping burst due to incorrect length") continue # Randomize the message header From afd110a3b5d8a565bffb251f9e031b56605558ac Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 20 Feb 2018 17:36:09 +0700 Subject: [PATCH 153/211] fake_trx: implement classes for DATA capture menagement This change introduces the following classes: - DATADump - basic class, which contains methods to generate and parse the a message header, and some constants. - DATADumpFile - a child class, which contains methods to write and parse DATA messages from capture files. Usage example: # Open a capture file ddf = DATADumpFile("capture.bin") # Parse the 10th message msg = ddf.parse_msg(10) msg.fn = 100 msg.tn = 0 # Append one to the end of the capture ddf.append_msg(msg) Change-Id: I1b31183bd7bcca94de089847ee0b2f4ec88a7f1d --- src/target/fake_trx/data_dump.py | 379 +++++++++++++++++++++++++++++++ 1 file changed, 379 insertions(+) create mode 100644 src/target/fake_trx/data_dump.py diff --git a/src/target/fake_trx/data_dump.py b/src/target/fake_trx/data_dump.py new file mode 100644 index 000000000..b9047365b --- /dev/null +++ b/src/target/fake_trx/data_dump.py @@ -0,0 +1,379 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# TRX Toolkit +# Helpers for DATA capture management +# +# (C) 2018 by Vadim Yanitskiy +# +# 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. + +import struct + +from data_msg import * + +class DATADump: + # Constants + TAG_L12TRX = '\x01' + TAG_TRX2L1 = '\x02' + HDR_LENGTH = 2 + + # Generates raw bytes from a DATA message + # Return value: raw message bytes + def dump_msg(self, msg): + # Determine a message type + if isinstance(msg, DATAMSG_L12TRX): + tag = self.TAG_L12TRX + elif isinstance(msg, DATAMSG_TRX2L1): + tag = self.TAG_TRX2L1 + else: + raise ValueError("Unknown message type") + + # Generate a message payload + msg_raw = msg.gen_msg() + + # Calculate the length + msg_len = len(msg_raw) + + # Concatenate a message with header + return bytearray([tag, msg_len]) + msg_raw + + def parse_hdr(self, hdr): + # Extract the header info + msg_len = struct.unpack(" TRX + msg = DATAMSG_L12TRX() + elif tag == self.TAG_TRX2L1: + # TRX -> L1 + msg = DATAMSG_TRX2L1() + else: + # Unknown tag + return False + + return (msg, msg_len) + +class DATADumpFile(DATADump): + def __init__(self, capture): + # Check if capture file is already opened + if type(capture) is file: + self.f = capture + else: + print("[i] Opening capture file '%s'..." % capture) + self.f = open(capture, "a+b") + + def __del__(self): + print("[i] Closing the capture file") + self.f.close() + + # Moves the file descriptor before a specified message + # Return value: + # True in case of success, + # or False in case of EOF or header parsing error. + def _seek2msg(self, idx): + # Seek to the begining of the capture + self.f.seek(0) + + # Read the capture in loop... + for i in range(idx): + # Attempt to read a message header + hdr_raw = self.f.read(self.HDR_LENGTH) + if len(hdr_raw) != self.HDR_LENGTH: + return False + + # Attempt to parse it + rc = self.parse_hdr(hdr_raw) + if rc is False: + print("[!] Couldn't parse a message header") + return False + + # Expand the header + (_, msg_len) = rc + + # Skip a message + self.f.seek(msg_len, 1) + + return True + + # Parses a single message at the current descriptor position + # Return value: + # a parsed message in case of success, + # or None in case of EOF or header parsing error, + # or False in case of message parsing error. + def _parse_msg(self): + # Attempt to read a message header + hdr_raw = self.f.read(self.HDR_LENGTH) + if len(hdr_raw) != self.HDR_LENGTH: + return None + + # Attempt to parse it + rc = self.parse_hdr(hdr_raw) + if rc is False: + print("[!] Couldn't parse a message header") + return None + + # Expand the header + (msg, msg_len) = rc + + # Attempt to read a message + msg_raw = self.f.read(msg_len) + if len(msg_raw) != msg_len: + print("[!] Message length mismatch") + return None + + # Attempt to parse a message + try: + msg_raw = bytearray(msg_raw) + msg.parse_msg(msg_raw) + except: + print("[!] Couldn't parse a message, skipping...") + return False + + # Success + return msg + + # Parses a particular message defined by index idx + # Return value: + # a parsed message in case of success, + # or None in case of EOF or header parsing error, + # or False in case of message parsing error or out of range. + def parse_msg(self, idx): + # Move descriptor to the begining of requested message + rc = self._seek2msg(idx) + if not rc: + print("[!] Couldn't find requested message") + return False + + # Attempt to parse a message + return self._parse_msg() + + # Parses all messages from a given file + # Return value: + # list of parsed messages, + # or False in case of range error. + def parse_all(self, skip = None, count = None): + result = [] + + # Should we skip some messages? + if skip is None: + # Seek to the begining of the capture + self.f.seek(0) + else: + rc = self._seek2msg(skip) + if not rc: + print("[!] Couldn't find requested message") + return False + + # Read the capture in loop... + while True: + # Attempt to parse a message + msg = self._parse_msg() + + # EOF or broken header + if msg is None: + break + + # Skip unparsed messages + if msg is False: + continue + + # Success, append a message + result.append(msg) + + # Count limitation + if count is not None: + if len(result) == count: + break + + return result + + # Writes a new message at the end of the capture + def append_msg(self, msg): + # Generate raw bytes and write + msg_raw = self.dump_msg(msg) + self.f.write(msg_raw) + + # Writes a list of messages at the end of the capture + def append_all(self, msgs): + for msg in msgs: + self.append_msg(msg) + +# Regression tests +if __name__ == '__main__': + from tempfile import TemporaryFile + from gsm_shared import * + import random + + # Create a temporary file + tf = TemporaryFile() + + # Create an instance of DATA dump manager + ddf = DATADumpFile(tf) + + # Generate two random bursts + burst_l12trx = [] + burst_trx2l1 = [] + + for i in range(0, GSM_BURST_LEN): + ubit = random.randint(0, 1) + burst_l12trx.append(ubit) + + sbit = random.randint(-127, 127) + burst_trx2l1.append(sbit) + + # Generate a basic list of random messages + print("[i] Generating the reference messages") + messages_ref = [] + + for i in range(100): + # Create a message + if i % 2: + msg = DATAMSG_L12TRX() + msg.burst = burst_l12trx + else: + msg = DATAMSG_TRX2L1() + msg.burst = burst_trx2l1 + + # Randomize the header + msg.rand_hdr() + + # HACK: as ToA parsing is not implemented yet, + # we have to use a fixed 0.00 value for now... + if isinstance(msg, DATAMSG_TRX2L1): + msg.toa = 0.00 + + # Append + messages_ref.append(msg) + + print("[i] Adding the following messages to the capture:") + for msg in messages_ref[:3]: + print(" %s: burst_len=%d" + % (msg.desc_hdr(), len(msg.burst))) + + # Check single message appending + ddf.append_msg(messages_ref[0]) + ddf.append_msg(messages_ref[1]) + ddf.append_msg(messages_ref[2]) + + # Read the written messages back + messages_check = ddf.parse_all() + + print("[i] Read the following messages back:") + for msg in messages_check: + print(" %s: burst_len=%d" + % (msg.desc_hdr(), len(msg.burst))) + + # Expecting three messages + assert(len(messages_check) == 3) + + # Check the messages + for i in range(3): + # Compare common header parts and bursts + assert(messages_check[i].burst == messages_ref[i].burst) + assert(messages_check[i].fn == messages_ref[i].fn) + assert(messages_check[i].tn == messages_ref[i].tn) + + # HACK: as ToA parsing is not implemented yet, + # we have to use a fixed 0.00 value for now... + messages_check[i].toa = 0.00 + + # Validate a message + assert(messages_check[i].validate()) + + print("[?] Check append_msg(): OK") + + + # Append the pending reference messages + ddf.append_all(messages_ref[3:]) + + # Read the written messages back + messages_check = ddf.parse_all() + + # Check the final amount + assert(len(messages_check) == len(messages_ref)) + + # Check the messages + for i in range(len(messages_check)): + # Compare common header parts and bursts + assert(messages_check[i].burst == messages_ref[i].burst) + assert(messages_check[i].fn == messages_ref[i].fn) + assert(messages_check[i].tn == messages_ref[i].tn) + + # HACK: as ToA parsing is not implemented yet, + # we have to use a fixed 0.00 value for now... + messages_check[i].toa = 0.00 + + # Validate a message + assert(messages_check[i].validate()) + + print("[?] Check append_all(): OK") + + + # Check parse_msg() + msg0 = ddf.parse_msg(0) + msg10 = ddf.parse_msg(10) + + # Make sure parsing was successful + assert(msg0 and msg10) + + # Compare common header parts and bursts + assert(msg0.burst == messages_ref[0].burst) + assert(msg0.fn == messages_ref[0].fn) + assert(msg0.tn == messages_ref[0].tn) + + assert(msg10.burst == messages_ref[10].burst) + assert(msg10.fn == messages_ref[10].fn) + assert(msg10.tn == messages_ref[10].tn) + + # HACK: as ToA parsing is not implemented yet, + # we have to use a fixed 0.00 value for now... + msg0.toa = 0.00 + msg10.toa = 0.00 + + # Validate both messages + assert(msg0.validate()) + assert(msg10.validate()) + + print("[?] Check parse_msg(): OK") + + + # Check parse_all() with range + messages_check = ddf.parse_all(skip = 10, count = 20) + + # Make sure parsing was successful + assert(messages_check) + + # Check the amount + assert(len(messages_check) == 20) + + for i in range(20): + # Compare common header parts and bursts + assert(messages_check[i].burst == messages_ref[i + 10].burst) + assert(messages_check[i].fn == messages_ref[i + 10].fn) + assert(messages_check[i].tn == messages_ref[i + 10].tn) + + # HACK: as ToA parsing is not implemented yet, + # we have to use a fixed 0.00 value for now... + messages_check[i].toa = 0.00 + + # Validate a message + assert(messages_check[i].validate()) + + print("[?] Check parse_all(): OK") From 3dfd6cbae5b81f287bd95940330458ef1fd8d653 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 20 Feb 2018 18:02:35 +0700 Subject: [PATCH 154/211] fake_trx/trx_sniff.py: use DATADumpFile for capture writing Since we have a separate class for DATA capture management now, no need to implement the wheel - let's just use it! Change-Id: I7c30bcea294ce7270bf905ae5420a06dbc2e46f1 --- src/target/fake_trx/trx_sniff.py | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/src/target/fake_trx/trx_sniff.py b/src/target/fake_trx/trx_sniff.py index 91c2c4ad3..f59e609b0 100755 --- a/src/target/fake_trx/trx_sniff.py +++ b/src/target/fake_trx/trx_sniff.py @@ -27,6 +27,7 @@ import sys import scapy.all +from data_dump import DATADumpFile from data_msg import * COPYRIGHT = \ @@ -69,9 +70,9 @@ class Application: print(COPYRIGHT) self.parse_argv() - # Open requested file for writing + # Open requested capture file if self.output_file is not None: - self.output_file = open(self.output_file, "ab") + self.ddf = DATADumpFile(self.output_file) def run(self): # Compose a packet filter @@ -126,10 +127,8 @@ class Application: print("[i] %s burst: %s" \ % ("L1 -> TRX" if l12trx else "TRX -> L1", msg.desc_hdr())) - # Poke burst handler - rc = self.burst_handle(l12trx, msg_raw, msg) - if rc is False: - self.shutdown() + # Poke message handler + self.msg_handle(msg) # Poke burst counter rc = self.burst_count(msg.fn, msg.tn) @@ -158,22 +157,13 @@ class Application: # Burst passed ;) return True - def burst_handle(self, l12trx, msg_raw, msg): + def msg_handle(self, msg): if self.print_bursts: print(msg.burst) + # Append a new message to the capture if self.output_file is not None: - # TLV: tag defines burst direction (one byte, BE) - self.output_file.write('\x01' if l12trx else '\x02') - - # TLV: length of value (one byte, BE) - length = len(msg_raw) - self.output_file.write(chr(length)) - - # TLV: raw value - self.output_file.write(msg_raw) - - return True + self.ddf.append_msg(msg) def burst_count(self, fn, tn): # Update frame counter @@ -208,10 +198,6 @@ class Application: print("[i] %u bursts handled, %u dropped" \ % (self.cnt_burst_num, self.cnt_burst_dropped_num)) - # Close output file if opened - if self.output_file is not None: - self.output_file.close() - # Exit sys.exit(0) From 711e2f256e125398cca69b841493d3063389cef1 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 20 Feb 2018 18:19:48 +0700 Subject: [PATCH 155/211] fake_trx/burst_gen.py: add burst capture support Now all generated bursts can be also written to a capture file, using a new option called '--output-file'. If a file already exists, bursts would be appended to the end. Otherwise a new capture file is created. Change-Id: I074ff7dbc4d6beecdecce20de9dade5939e707f2 --- src/target/fake_trx/burst_gen.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/target/fake_trx/burst_gen.py b/src/target/fake_trx/burst_gen.py index 9141b6748..9f366874a 100755 --- a/src/target/fake_trx/burst_gen.py +++ b/src/target/fake_trx/burst_gen.py @@ -27,6 +27,7 @@ import getopt import sys from rand_burst_gen import RandBurstGen +from data_dump import DATADumpFile from data_if import DATAInterface from gsm_shared import * from data_msg import * @@ -43,6 +44,7 @@ class Application: remote_addr = "127.0.0.1" base_port = 5700 conn_mode = "TRX" + output_file = None burst_type = None burst_count = 1 @@ -64,6 +66,10 @@ class Application: # Set up signal handlers signal.signal(signal.SIGINT, self.sig_handler) + # Open requested capture file + if self.output_file is not None: + self.ddf = DATADumpFile(self.output_file) + def run(self): # Init DATA interface with TRX or L1 if self.conn_mode == "TRX": @@ -133,6 +139,10 @@ class Application: # Send message self.data_if.send_msg(msg) + # Append a new message to the capture + if self.output_file is not None: + self.ddf.append_msg(msg) + self.shutdown() def print_copyright(self): @@ -144,6 +154,7 @@ class Application: " -h --help this text\n\n" s += " TRX interface specific\n" \ + " -o --output-file Write bursts to a capture file\n" \ " -m --conn-mode Send bursts to: TRX (default) / L1\n" \ " -r --remote-addr Set remote address (default %s)\n" \ " -p --base-port Set base port number (default %d)\n\n" @@ -165,9 +176,10 @@ class Application: def parse_argv(self): try: opts, args = getopt.getopt(sys.argv[1:], - "m:r:p:b:c:f:t:h", + "o:m:r:p:b:c:f:t:h", [ "help", + "output-file=" "conn-mode=", "remote-addr=", "base-port=", @@ -188,6 +200,8 @@ class Application: self.print_help() sys.exit(2) + elif o in ("-o", "--output-file"): + self.output_file = v elif o in ("-m", "--conn-mode"): self.conn_mode = v elif o in ("-r", "--remote-addr"): From d406afd23e5e0efb38a11bd40a7ff6f0a01ee7f6 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 20 Feb 2018 19:21:54 +0700 Subject: [PATCH 156/211] fake_trx/burst_send.py: implement DATA capture support Previously, this tool was only able to read a hand-crafted text file with bursts and send them via the DATA interface. This is not so useful... This change implements support of reading DATA capture files, which can be generated e.g. by trx_sniff.py or burst_gen.py. Both standart input (stdio) and text-files are not supported anymore. Usage example: ./burst_send.py -m L1 -i capture.bin --timeslot 2 Change-Id: I626662bd1897c874421ab5178970ec19325f8a47 --- src/target/fake_trx/README | 6 +- src/target/fake_trx/burst_send.py | 201 +++++++++++++++--------------- 2 files changed, 101 insertions(+), 106 deletions(-) diff --git a/src/target/fake_trx/README b/src/target/fake_trx/README index 5d4960f4d..7b27414bf 100644 --- a/src/target/fake_trx/README +++ b/src/target/fake_trx/README @@ -28,9 +28,9 @@ Brief description of available applications: Currently it is only possible to generate random bursts of different types: NB, FB, SB, AB. - - burst_send.py - a tool for sending existing bursts from file - or standard input either to L1 (OsmoBTS or OsmocomBB) or to - TRX (OsmoTRX and GR-GSM TRX). + - burst_send.py - a tool for sending existing bursts from a + capture file either to L1 (OsmoBTS or OsmocomBB) or to + TRX (e.g. OsmoTRX or GR-GSM TRX). - trx_sniff.py - Scapy-based TRX protocol sniffer. Allows one to observe a single connection between TRX and L1, and vice diff --git a/src/target/fake_trx/burst_send.py b/src/target/fake_trx/burst_send.py index b51ce5d4c..87f9228b9 100755 --- a/src/target/fake_trx/burst_send.py +++ b/src/target/fake_trx/burst_send.py @@ -25,12 +25,13 @@ import signal import getopt import sys +from data_dump import DATADumpFile from data_if import DATAInterface from gsm_shared import * from data_msg import * COPYRIGHT = \ - "Copyright (C) 2017 by Vadim Yanitskiy \n" \ + "Copyright (C) 2017-2018 by Vadim Yanitskiy \n" \ "License GPLv2+: GNU GPL version 2 or later " \ "\n" \ "This is free software: you are free to change and redistribute it.\n" \ @@ -42,16 +43,17 @@ class Application: base_port = 5700 conn_mode = "TRX" - burst_src = None - - # Common header fields - fn = None - tn = None + # Burst source + capture_file = None - # Message specific header fields - rssi = None - toa = None - pwr = None + # Count limitations + msg_skip = None + msg_count = None + + # Pass filtering + pf_fn_lt = None + pf_fn_gt = None + pf_tn = None def __init__(self): self.print_copyright() @@ -60,81 +62,37 @@ class Application: # Set up signal handlers signal.signal(signal.SIGINT, self.sig_handler) + # Open requested capture file + self.ddf = DATADumpFile(self.capture_file) + def run(self): # Init DATA interface with TRX or L1 if self.conn_mode == "TRX": self.data_if = DATAInterface(self.remote_addr, self.base_port + 2, self.base_port + 102) + l12trx = True elif self.conn_mode == "L1": self.data_if = DATAInterface(self.remote_addr, self.base_port + 102, self.base_port + 2) + l12trx = False else: self.print_help("[!] Unknown connection type") sys.exit(2) - # Open the burst source (file or stdin) - if self.burst_src is not None: - print("[i] Reading bursts from file '%s'..." % self.burst_src) - src = open(self.burst_src, "r") - else: - print("[i] Reading bursts from stdin...") - src = sys.stdin + # Read messages from the capture + messages = self.ddf.parse_all( + skip = self.msg_skip, count = self.msg_count) + if messages is False: + pass # FIXME!!! - # Init an empty DATA message - if self.conn_mode == "TRX": - msg = DATAMSG_L12TRX() - elif self.conn_mode == "L1": - msg = DATAMSG_TRX2L1() - - # Generate a random frame number or use provided one - fn = msg.rand_fn() if self.fn is None else self.fn - - # Read the burst source line-by-line - for line in src: - # Strip spaces - burst_str = line.strip() - burst = [] - - # Check length - if len(burst_str) not in (GSM_BURST_LEN, EDGE_BURST_LEN): - print("[!] Dropping burst due to incorrect length") + for msg in messages: + # Pass filter + if not self.msg_pass_filter(l12trx, msg): continue - # Randomize the message header - msg.rand_hdr() - - # Set frame number - msg.fn = fn - - # Set timeslot number - if self.tn is not None: - msg.tn = self.tn - - # Set transmit power level - if self.pwr is not None: - msg.pwr = self.pwr - - # Set time of arrival - if self.toa is not None: - msg.toa = self.toa - - # Set RSSI - if self.rssi is not None: - msg.rssi = self.rssi - - # Parse a string - for bit in burst_str: - if bit == "1": - burst.append(1) - else: - burst.append(0) - - # Convert to soft-bits in case of TRX -> L1 message - if self.conn_mode == "L1": - burst = msg.ubit2sbit(burst) - - # Set burst - msg.burst = burst + # HACK: as ToA parsing is not implemented yet, + # we have to use a fixed 0.00 value for now... + msg.toa = 0.00 print("[i] Sending a burst %s to %s..." % (msg.desc_hdr(), self.conn_mode)) @@ -142,32 +100,56 @@ class Application: # Send message self.data_if.send_msg(msg) - # Increase frame number (for count > 1) - fn = (fn + 1) % GSM_HYPERFRAME - # Finish self.shutdown() + def msg_pass_filter(self, l12trx, msg): + # Direction filter + if isinstance(msg, DATAMSG_L12TRX) and not l12trx: + return False + elif isinstance(msg, DATAMSG_TRX2L1) and l12trx: + return False + + # Timeslot filter + if self.pf_tn is not None: + if msg.tn != self.pf_tn: + return False + + # Frame number filter + if self.pf_fn_lt is not None: + if msg.fn > self.pf_fn_lt: + return False + if self.pf_fn_gt is not None: + if msg.fn < self.pf_fn_gt: + return False + + # Burst passed ;) + return True + def print_copyright(self): print(COPYRIGHT) def print_help(self, msg = None): s = " Usage: " + sys.argv[0] + " [options]\n\n" \ " Some help...\n" \ - " -h --help this text\n\n" + " -h --help this text\n\n" s += " TRX interface specific\n" \ - " -m --conn-mode Send bursts to: TRX (default) / L1\n" \ - " -r --remote-addr Set remote address (default %s)\n" \ - " -p --base-port Set base port number (default %d)\n\n" + " -m --conn-mode Send bursts to: TRX (default) / L1\n" \ + " -r --remote-addr Set remote address (default %s)\n" \ + " -p --base-port Set base port number (default %d)\n\n" - s += " Burst generation\n" \ - " -i --burst-file Read bursts from file (default stdin)\n" \ - " -f --frame-number Set frame number (default random)\n" \ - " -t --timeslot Set timeslot index (default random)\n" \ - " --pwr Set power level (default random)\n" \ - " --rssi Set RSSI (default random)\n" \ - " --toa Set TOA (default random)\n\n" + s += " Burst source\n" \ + " -i --capture-file Read bursts from capture file\n\n" \ + + s += " Count limitations (disabled by default)\n" \ + " --msg-skip NUM Skip NUM messages before sending\n" \ + " --msg-count NUM Stop after sending NUM messages\n\n" \ + + s += " Filtering (disabled by default)\n" \ + " --timeslot NUM TDMA timeslot number [0..7]\n" \ + " --frame-num-lt NUM TDMA frame number lower than NUM\n" \ + " --frame-num-gt NUM TDMA frame number greater than NUM\n" print(s % (self.remote_addr, self.base_port)) @@ -177,18 +159,18 @@ class Application: def parse_argv(self): try: opts, args = getopt.getopt(sys.argv[1:], - "m:r:p:i:f:t:h", + "m:r:p:i:h", [ "help", "conn-mode=", "remote-addr=", "base-port=", - "burst-file=", - "frame-number=", + "capture-file=", + "msg-skip=", + "msg-count=", "timeslot=", - "rssi=", - "toa=", - "pwr=", + "frame-num-lt=", + "frame-num-gt=", ]) except getopt.GetoptError as err: self.print_help("[!] " + str(err)) @@ -199,6 +181,11 @@ class Application: self.print_help() sys.exit(2) + # Capture file + elif o in ("-i", "--capture-file"): + self.capture_file = v + + # TRX interface specific elif o in ("-m", "--conn-mode"): self.conn_mode = v elif o in ("-r", "--remote-addr"): @@ -206,20 +193,28 @@ class Application: elif o in ("-p", "--base-port"): self.base_port = int(v) - elif o in ("-i", "--burst-file"): - self.burst_src = v - elif o in ("-f", "--frame-number"): - self.fn = int(v) - elif o in ("-t", "--timeslot"): - self.tn = int(v) + # Count limitations + elif o == "--msg-skip": + self.msg_skip = int(v) + elif o == "--msg-count": + self.msg_count = int(v) - # Message specific header fields - elif o == "--pwr": - self.pwr = int(v) - elif o == "--rssi": - self.rssi = int(v) - elif o == "--toa": - self.toa = float(v) + # Timeslot pass filter + elif o == "--timeslot": + self.pf_tn = int(v) + if self.pf_tn < 0 or self.pf_tn > 7: + self.print_help("[!] Wrong timeslot value") + sys.exit(2) + + # Frame number pass filter + elif o == "--frame-num-lt": + self.pf_fn_lt = int(v) + elif o == "--frame-num-gt": + self.pf_fn_gt = int(v) + + if self.capture_file is None: + self.print_help("[!] Please specify a capture file") + sys.exit(2) def shutdown(self): self.data_if.shutdown() From 318f8b78aad5961eba6b179f84e772bff759ade3 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 20 Feb 2018 19:43:02 +0700 Subject: [PATCH 157/211] fake_trx/data_dump.py: use 2 bytes to store message length One byte may store a value in range [0x00, 0xff]. The maximal 0xff value is 255 in dec, so a message length is limited to 255 bytes. This is enough for GSM bursts, but not for EDGE. Since this change, two bytes of header are used to store the pending message length. All captures created before are not supported anymore... Change-Id: I5a69d5cf2914fe56b2f9acca6054c9470627f91e --- src/target/fake_trx/data_dump.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/target/fake_trx/data_dump.py b/src/target/fake_trx/data_dump.py index b9047365b..56e314c4f 100644 --- a/src/target/fake_trx/data_dump.py +++ b/src/target/fake_trx/data_dump.py @@ -30,7 +30,7 @@ class DATADump: # Constants TAG_L12TRX = '\x01' TAG_TRX2L1 = '\x02' - HDR_LENGTH = 2 + HDR_LENGTH = 3 # Generates raw bytes from a DATA message # Return value: raw message bytes @@ -46,15 +46,18 @@ class DATADump: # Generate a message payload msg_raw = msg.gen_msg() - # Calculate the length + # Calculate and pack the message length msg_len = len(msg_raw) + # Pack to unsigned short (2 bytes, BE) + msg_len = struct.pack(">H", msg_len) + # Concatenate a message with header - return bytearray([tag, msg_len]) + msg_raw + return bytearray(tag + msg_len) + msg_raw def parse_hdr(self, hdr): # Extract the header info - msg_len = struct.unpack("H", hdr[1:3])[0] tag = hdr[0] # Check if tag is known From 05ea7248f8e6c1aed7665efa82eeb92264027f86 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 22 Feb 2018 16:10:01 +0100 Subject: [PATCH 158/211] trxcon|fake_trx: change default TRX port number to 6700 In order to avoid clashes with OsmoTRX, which may be also running on the same host, let's use a different port range starting from 6700 by default. This idea was introduced as a result of OS#2984. Change-Id: I66b5f25aaba3b836448ed29839c39869b5622bed Related: OS#2984 --- src/host/trxcon/trxcon.c | 4 ++-- src/target/fake_trx/fake_trx.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index 07ab16981..0250b726a 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -143,7 +143,7 @@ static void print_help(void) printf(" -h --help this text\n"); printf(" -d --debug Change debug flags. Default: %s\n", DEBUG_DEFAULT); printf(" -i --trx-ip IP address of host runing TRX (default 127.0.0.1)\n"); - printf(" -p --trx-port Base port of TRX instance (default 5700)\n"); + printf(" -p --trx-port Base port of TRX instance (default 6700)\n"); printf(" -f --trx-advance Scheduler clock advance (default 20)\n"); printf(" -s --socket Listening socket for layer23 (default /tmp/osmocom_l2)\n"); printf(" -D --daemonize Run as daemon\n"); @@ -203,7 +203,7 @@ static void init_defaults(void) { app_data.bind_socket = "/tmp/osmocom_l2"; app_data.trx_ip = "127.0.0.1"; - app_data.trx_base_port = 5700; + app_data.trx_base_port = 6700; app_data.trx_fn_advance = 20; app_data.debug_mask = NULL; diff --git a/src/target/fake_trx/fake_trx.py b/src/target/fake_trx/fake_trx.py index 962101c34..4f6d8c6b1 100755 --- a/src/target/fake_trx/fake_trx.py +++ b/src/target/fake_trx/fake_trx.py @@ -47,7 +47,7 @@ class Application: bts_addr = "127.0.0.1" bb_addr = "127.0.0.1" bts_base_port = 5700 - bb_base_port = 5703 + bb_base_port = 6700 def __init__(self): self.print_copyright() From e5480d2c2bc703ed1b387df753220506db2aceec Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 27 Feb 2018 04:40:28 +0700 Subject: [PATCH 159/211] fake_trx/udp_link.py: close socket in destructor Previously it was required to call the UDPLink.shutdown() method manually in order to close a socket. Let's do this automatically using the destructor of UDPLink. Change-Id: I59c3dc61ec58cd9effeb789947d28fd602ca91f4 --- src/target/fake_trx/burst_gen.py | 6 ------ src/target/fake_trx/burst_send.py | 7 ------- src/target/fake_trx/clck_gen.py | 1 - src/target/fake_trx/ctrl_cmd.py | 1 - src/target/fake_trx/ctrl_if_bb.py | 4 ---- src/target/fake_trx/ctrl_if_bts.py | 4 ---- src/target/fake_trx/fake_trx.py | 12 ------------ src/target/fake_trx/udp_link.py | 6 +++--- 8 files changed, 3 insertions(+), 38 deletions(-) diff --git a/src/target/fake_trx/burst_gen.py b/src/target/fake_trx/burst_gen.py index 9f366874a..c2e1ad2d5 100755 --- a/src/target/fake_trx/burst_gen.py +++ b/src/target/fake_trx/burst_gen.py @@ -143,8 +143,6 @@ class Application: if self.output_file is not None: self.ddf.append_msg(msg) - self.shutdown() - def print_copyright(self): print(COPYRIGHT) @@ -237,13 +235,9 @@ class Application: self.print_help("[!] Unknown burst type") sys.exit(2) - def shutdown(self): - self.data_if.shutdown() - def sig_handler(self, signum, frame): print("Signal %d received" % signum) if signum is signal.SIGINT: - self.shutdown() sys.exit(0) if __name__ == '__main__': diff --git a/src/target/fake_trx/burst_send.py b/src/target/fake_trx/burst_send.py index 87f9228b9..61a526f65 100755 --- a/src/target/fake_trx/burst_send.py +++ b/src/target/fake_trx/burst_send.py @@ -100,9 +100,6 @@ class Application: # Send message self.data_if.send_msg(msg) - # Finish - self.shutdown() - def msg_pass_filter(self, l12trx, msg): # Direction filter if isinstance(msg, DATAMSG_L12TRX) and not l12trx: @@ -216,13 +213,9 @@ class Application: self.print_help("[!] Please specify a capture file") sys.exit(2) - def shutdown(self): - self.data_if.shutdown() - def sig_handler(self, signum, frame): print("Signal %d received" % signum) if signum is signal.SIGINT: - self.shutdown() sys.exit(0) if __name__ == '__main__': diff --git a/src/target/fake_trx/clck_gen.py b/src/target/fake_trx/clck_gen.py index 484ecde54..e7e58a3bf 100755 --- a/src/target/fake_trx/clck_gen.py +++ b/src/target/fake_trx/clck_gen.py @@ -114,7 +114,6 @@ class Application: print("Signal %d received" % signum) if signum is signal.SIGINT: self.clck.stop() - self.link.shutdown() if __name__ == '__main__': app = Application() diff --git a/src/target/fake_trx/ctrl_cmd.py b/src/target/fake_trx/ctrl_cmd.py index 377a1f26e..3faf8127d 100755 --- a/src/target/fake_trx/ctrl_cmd.py +++ b/src/target/fake_trx/ctrl_cmd.py @@ -129,7 +129,6 @@ class Application: def sig_handler(self, signum, frame): print("\n\nSignal %d received" % signum) if signum is signal.SIGINT: - self.ctrl_link.shutdown() sys.exit(0) if __name__ == '__main__': diff --git a/src/target/fake_trx/ctrl_if_bb.py b/src/target/fake_trx/ctrl_if_bb.py index 440094a0e..47c704847 100644 --- a/src/target/fake_trx/ctrl_if_bb.py +++ b/src/target/fake_trx/ctrl_if_bb.py @@ -36,10 +36,6 @@ class CTRLInterfaceBB(CTRLInterface): print("[i] Init CTRL interface for BB") CTRLInterface.__init__(self, remote_addr, remote_port, bind_port) - def shutdown(self): - print("[i] Shutdown CTRL interface for BB") - CTRLInterface.shutdown(self) - def parse_cmd(self, request): # Power control if self.verify_cmd(request, "POWERON", 0): diff --git a/src/target/fake_trx/ctrl_if_bts.py b/src/target/fake_trx/ctrl_if_bts.py index d0a7db3c7..92a4abb2e 100644 --- a/src/target/fake_trx/ctrl_if_bts.py +++ b/src/target/fake_trx/ctrl_if_bts.py @@ -37,10 +37,6 @@ class CTRLInterfaceBTS(CTRLInterface): print("[i] Init CTRL interface for BTS") CTRLInterface.__init__(self, remote_addr, remote_port, bind_port) - def shutdown(self): - print("[i] Shutdown CTRL interface for BTS") - CTRLInterface.shutdown(self) - def parse_cmd(self, request): # Power control if self.verify_cmd(request, "POWERON", 0): diff --git a/src/target/fake_trx/fake_trx.py b/src/target/fake_trx/fake_trx.py index 4f6d8c6b1..bdc4c614b 100755 --- a/src/target/fake_trx/fake_trx.py +++ b/src/target/fake_trx/fake_trx.py @@ -125,18 +125,6 @@ class Application: # Stop clock generator self.clck_gen.stop() - # Close CLCK interfaces - self.bts_clck.shutdown() - - # Close CTRL interfaces - self.bts_ctrl.shutdown() - self.bb_ctrl.shutdown() - - # Close DATA interfaces - self.bts_data.shutdown() - self.bb_data.shutdown() - - def print_copyright(self): print(COPYRIGHT) diff --git a/src/target/fake_trx/udp_link.py b/src/target/fake_trx/udp_link.py index 0afd1504e..c0c41ff65 100644 --- a/src/target/fake_trx/udp_link.py +++ b/src/target/fake_trx/udp_link.py @@ -35,6 +35,9 @@ class UDPLink: self.remote_addr = remote_addr self.remote_port = remote_port + def __del__(self): + self.sock.close() + def loop(self): r_event, w_event, x_event = select.select([self.sock], [], []) @@ -43,9 +46,6 @@ class UDPLink: data, addr = self.sock.recvfrom(128) self.handle_rx(data.decode()) - def shutdown(self): - self.sock.close(); - def send(self, data): if type(data) not in [bytearray, bytes]: data = data.encode() From 23446011afef95c4e83cbce8dfec736831878563 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 27 Feb 2018 04:50:28 +0700 Subject: [PATCH 160/211] fake_trx/udp_link.py: drop useless UDPLink.loop() API So far, this API is not used anywhere. Let's drop it. Change-Id: I87ea2436f0b6bbeb62fe17700af48a048be143bb --- src/target/fake_trx/udp_link.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/target/fake_trx/udp_link.py b/src/target/fake_trx/udp_link.py index c0c41ff65..7471b98ed 100644 --- a/src/target/fake_trx/udp_link.py +++ b/src/target/fake_trx/udp_link.py @@ -23,7 +23,6 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import socket -import select class UDPLink: def __init__(self, remote_addr, remote_port, bind_port): @@ -38,19 +37,8 @@ class UDPLink: def __del__(self): self.sock.close() - def loop(self): - r_event, w_event, x_event = select.select([self.sock], [], []) - - # Check for incoming data - if self.sock in r_event: - data, addr = self.sock.recvfrom(128) - self.handle_rx(data.decode()) - def send(self, data): if type(data) not in [bytearray, bytes]: data = data.encode() self.sock.sendto(data, (self.remote_addr, self.remote_port)) - - def handle_rx(self, data): - raise NotImplementedError From 24e30142aa51aa070e83e3c6b339662d5c038f12 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 27 Feb 2018 04:57:58 +0700 Subject: [PATCH 161/211] fake_trx/udp_link.py: set SO_REUSEADDR socket option Setting this option allows one to reuse existing connections, for example, by injecting CTRL commands or DATA bursts into existing connections between fake_trx.py and trxcon. Change-Id: I0882c76affa9a668a12d10967081054d2b666ed1 --- src/target/fake_trx/udp_link.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/target/fake_trx/udp_link.py b/src/target/fake_trx/udp_link.py index 7471b98ed..c464802b1 100644 --- a/src/target/fake_trx/udp_link.py +++ b/src/target/fake_trx/udp_link.py @@ -27,6 +27,7 @@ import socket class UDPLink: def __init__(self, remote_addr, remote_port, bind_port): self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.bind(('0.0.0.0', bind_port)) self.sock.setblocking(0) From 615faadcfb28e4f92105ab0336ef32aa5c8420c1 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 27 Feb 2018 06:46:15 +0700 Subject: [PATCH 162/211] fake_trx/data_msg.py: implement message transformation API This change introduces two new methods, which allow to perform L12TRX <-> TRX2L1 message type transformations. Change-Id: Ic99cf74baa1864bf20a8fc0fc025604bc160084c --- src/target/fake_trx/data_msg.py | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/target/fake_trx/data_msg.py b/src/target/fake_trx/data_msg.py index 77d4f52bf..aa264bebc 100644 --- a/src/target/fake_trx/data_msg.py +++ b/src/target/fake_trx/data_msg.py @@ -290,6 +290,17 @@ class DATAMSG_L12TRX(DATAMSG): else: self.burst = list(burst[:GSM_BURST_LEN]) + # Transforms this message to TRX2L1 message + def gen_trx2l1(self): + # Allocate a new message + msg = DATAMSG_TRX2L1(fn = self.fn, tn = self.tn) + + # Convert burst bits + if self.burst is not None: + msg.burst = self.ubit2sbit(self.burst) + + return msg + class DATAMSG_TRX2L1(DATAMSG): # Constants HDR_LEN = 8 @@ -422,6 +433,17 @@ class DATAMSG_TRX2L1(DATAMSG): # Save self.burst = burst_sbits + # Transforms this message to L12TRX message + def gen_l12trx(self): + # Allocate a new message + msg = DATAMSG_L12TRX(fn = self.fn, tn = self.tn) + + # Convert burst bits + if self.burst is not None: + msg.burst = self.sbit2ubit(self.burst) + + return msg + # Regression test if __name__ == '__main__': # Common reference data @@ -523,3 +545,18 @@ if __name__ == '__main__': assert(sbits == ([-127] * 127 + [127] * 128)) print("[?] Check both sbit2ubit() and ubit2sbit(): OK") + + # Test message transformation + msg_l12trx_dec = msg_trx2l1_ref.gen_l12trx() + msg_trx2l1_dec = msg_l12trx_ref.gen_trx2l1() + + assert(msg_l12trx_dec.fn == msg_trx2l1_ref.fn) + assert(msg_l12trx_dec.tn == msg_trx2l1_ref.tn) + + assert(msg_trx2l1_dec.fn == msg_l12trx_ref.fn) + assert(msg_trx2l1_dec.tn == msg_l12trx_ref.tn) + + assert(msg_l12trx_dec.burst == msg_l12trx_dec.sbit2ubit(burst_trx2l1_ref)) + assert(msg_trx2l1_dec.burst == msg_trx2l1_dec.ubit2sbit(burst_l12trx_ref)) + + print("[?] Check L12TRX <-> TRX2L1 type transformations: OK") From f35413691db5a78640a05d22586a03a7badcea62 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 27 Feb 2018 07:00:45 +0700 Subject: [PATCH 163/211] fake_trx/burst_fwd.py: use DATAMSG transformation API As the DATAMSG classes were introduced, let's use them. This approach abstracts one from dealing with raw bytes. Also, now BurstForwarder randomizes both RSSI and ToA values, as this feature is supported from-the-box by the DATAMSG_TRX2L1. Change-Id: Ib15018eab749150e244914dab4b6e433ce0c9209 --- src/target/fake_trx/burst_fwd.py | 85 +++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 19 deletions(-) diff --git a/src/target/fake_trx/burst_fwd.py b/src/target/fake_trx/burst_fwd.py index 5989ce9c9..663f4996e 100644 --- a/src/target/fake_trx/burst_fwd.py +++ b/src/target/fake_trx/burst_fwd.py @@ -22,6 +22,10 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +import random + +from data_msg import * + class BurstForwarder: # Timeslot filter (drop everything by default) ts_pass = None @@ -30,40 +34,60 @@ class BurstForwarder: bts_freq = None bb_freq = None + # Constants + # TODO: add options to change this + RSSI_RAND_TRESHOLD = 10 + RSSI_RAND_MIN = -90 + RSSI_RAND_MAX = -60 + + # TODO: add options to change this + TOA_RAND_TRESHOLD = 0.3 + TOA_RAND_BASE = 0.00 + def __init__(self, bts_link, bb_link): self.bts_link = bts_link self.bb_link = bb_link + # Generate a random RSSI range + rssi = random.randint(self.RSSI_RAND_MIN, self.RSSI_RAND_MAX) + self.rssi_min = rssi - self.RSSI_RAND_TRESHOLD + self.rssi_max = rssi + self.RSSI_RAND_TRESHOLD + + # Generate a random ToA range + self.toa_min = self.TOA_RAND_BASE - self.TOA_RAND_TRESHOLD + self.toa_max = self.TOA_RAND_BASE + self.TOA_RAND_TRESHOLD + def set_slot(self, ts): if ts > 0 and ts < 8: self.ts_pass = ts else: raise ValueError("Incorrect index for timeslot filter") - def process_payload(self, data): - payload = bytearray(data) - length = len(payload) + # Converts a L12TRX message to TRX2L1 message + def transform_msg(self, msg_raw): + # Attempt to parse a message + try: + msg_l12trx = DATAMSG_L12TRX() + msg_l12trx.parse_msg(bytearray(msg_raw)) + except: + print("[!] Dropping unhandled DL message...") + return None - # HACK: set fake RSSI value (-53) - payload[5] = 0x35 + # Compose a new message for L1 + msg_trx2l1 = msg_l12trx.gen_trx2l1() - # HACK: add fake TOA value (6th and 7th bytes) - payload[6:2] = [0x00, 0x00] + # Randomize both RSSI and ToA values + msg_trx2l1.rssi = msg_trx2l1.rand_rssi( + min = self.rssi_min, max = self.rssi_max) + msg_trx2l1.toa = msg_trx2l1.rand_toa( + min = self.toa_min, max = self.toa_max) - # Convert ubits to {255..0} - for i in range(8, length): - payload[i] = 255 if payload[i] else 0 - - # WTF: append two unused bytes at the end - payload[length:2] = [0x00, 0x00] - - return payload + return msg_trx2l1 # Downlink handler: BTS -> BB def bts2bb(self): # Read data from socket data, addr = self.bts_link.sock.recvfrom(512) - payload = self.process_payload(data) # BB is not connected / tuned if self.bb_freq is None: @@ -73,10 +97,22 @@ class BurstForwarder: if self.bb_freq != self.bts_freq: return None - # Timeslot filter - if payload[0] != self.ts_pass: + # Process a message + msg = self.transform_msg(data) + if msg is None: return None + # Timeslot filter + if msg.tn != self.ts_pass: + return None + + # Validate and generate the payload + payload = msg.gen_msg() + + # Append two unused bytes at the end + # in order to keep the compatibility + payload += bytearray(2) + # Send burst to BB self.bb_link.send(payload) @@ -84,7 +120,6 @@ class BurstForwarder: def bb2bts(self): # Read data from socket data, addr = self.bb_link.sock.recvfrom(512) - payload = self.process_payload(data) # BTS is not connected / tuned if self.bts_freq is None: @@ -94,5 +129,17 @@ class BurstForwarder: if self.bb_freq != self.bts_freq: return None + # Process a message + msg = self.transform_msg(data) + if msg is None: + return None + + # Validate and generate the payload + payload = msg.gen_msg() + + # Append two unused bytes at the end + # in order to keep the compatibility + payload += bytearray(2) + # Send burst to BTS self.bts_link.send(payload) From 77492b792619d66f5395cb0f000d053afb73f9c4 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 28 Feb 2018 01:17:31 +0700 Subject: [PATCH 164/211] fake_trx/data_msg.py: fix python3 compatibility in tests In Python3 a range has it's own type, so its comparasion with a list is incorrect. Let's explicitly convert both bit ranges to lists in the bit conversation tests. Change-Id: I98c40d3d63cbcdc3e5dc840ebf8d7310c5c08e56 --- src/target/fake_trx/data_msg.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/fake_trx/data_msg.py b/src/target/fake_trx/data_msg.py index aa264bebc..5ee8ed590 100644 --- a/src/target/fake_trx/data_msg.py +++ b/src/target/fake_trx/data_msg.py @@ -526,8 +526,8 @@ if __name__ == '__main__': print("[?] Validate header randomization: OK") # Bit conversation test - usbits_ref = range(0, 256) - sbits_ref = range(-127, 128) + usbits_ref = list(range(0, 256)) + sbits_ref = list(range(-127, 128)) # Test both usbit2sbit() and sbit2usbit() sbits = msg_trx2l1_ref.usbit2sbit(usbits_ref) From d93c1debb0f2b782c7b051c338753508bd6bca23 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 28 Feb 2018 15:08:58 +0700 Subject: [PATCH 165/211] fake_trx/data_dump.py: fix python3 compatibility There is no 'file' type in Python3 anymore, so let's reverse the condition in DATADumpFile constructor. Also, the tag definition was incorrect: both '\x01' and b'\x01' aren't the same. Change-Id: Ib00c7f0bd5871fcfce931a4bfa501ae5bf797c45 --- src/target/fake_trx/data_dump.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/target/fake_trx/data_dump.py b/src/target/fake_trx/data_dump.py index 56e314c4f..6102dc56d 100644 --- a/src/target/fake_trx/data_dump.py +++ b/src/target/fake_trx/data_dump.py @@ -28,8 +28,8 @@ from data_msg import * class DATADump: # Constants - TAG_L12TRX = '\x01' - TAG_TRX2L1 = '\x02' + TAG_L12TRX = b'\x01' + TAG_TRX2L1 = b'\x02' HDR_LENGTH = 3 # Generates raw bytes from a DATA message @@ -58,7 +58,7 @@ class DATADump: def parse_hdr(self, hdr): # Extract the header info msg_len = struct.unpack(">H", hdr[1:3])[0] - tag = hdr[0] + tag = hdr[:1] # Check if tag is known if tag == self.TAG_L12TRX: @@ -76,11 +76,11 @@ class DATADump: class DATADumpFile(DATADump): def __init__(self, capture): # Check if capture file is already opened - if type(capture) is file: - self.f = capture - else: + if isinstance(capture, str): print("[i] Opening capture file '%s'..." % capture) self.f = open(capture, "a+b") + else: + self.f = capture def __del__(self): print("[i] Closing the capture file") From 7881fd6001273692d87f18c05e6592232b732b40 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 28 Feb 2018 01:04:51 +0700 Subject: [PATCH 166/211] fake_trx/data_msg.py: use a single unified constructor There are no message specific initialization parts, excepting the header specific fields setting. Let's us a common constructor, dropping custom fields from its arguments. Change-Id: I13a3e4b2f6a1f443ebe7d809df62736e3c43f56f --- src/target/fake_trx/data_msg.py | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/src/target/fake_trx/data_msg.py b/src/target/fake_trx/data_msg.py index 5ee8ed590..045e281ed 100644 --- a/src/target/fake_trx/data_msg.py +++ b/src/target/fake_trx/data_msg.py @@ -32,9 +32,11 @@ class DATAMSG: fn = None tn = None - # HACK: Abstract class definition - def __init__(self): - raise NotImplementedError + # Common constructor + def __init__(self, fn = None, tn = None, burst = None): + self.burst = burst + self.fn = fn + self.tn = tn # Generates message specific header def gen_hdr(self): @@ -213,13 +215,6 @@ class DATAMSG_L12TRX(DATAMSG): # Specific message fields pwr = None - def __init__(self, fn = None, tn = None, pwr = None, burst = None): - # Init local variables - self.burst = burst - self.pwr = pwr - self.fn = fn - self.tn = tn - # Validates the message fields def validate(self): # Validate common fields @@ -315,14 +310,6 @@ class DATAMSG_TRX2L1(DATAMSG): rssi = None toa = None - def __init__(self, fn = None, tn = None, rssi = None, toa = None, burst = None): - # Init local variables - self.burst = burst - self.rssi = rssi - self.toa = toa - self.fn = fn - self.tn = tn - # Validates the message fields def validate(self): # Validate common fields From cbd3d76df9d7e78f7f2010a6cf1c12279fe6acb3 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 28 Feb 2018 18:30:16 +0700 Subject: [PATCH 167/211] fake_trx/burst_fwd.py: drop useless set_slot() method Change-Id: I721c87758f04a1962427341eb1b2d47cfdd3f780 --- src/target/fake_trx/burst_fwd.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/target/fake_trx/burst_fwd.py b/src/target/fake_trx/burst_fwd.py index 663f4996e..db0b05b9a 100644 --- a/src/target/fake_trx/burst_fwd.py +++ b/src/target/fake_trx/burst_fwd.py @@ -57,12 +57,6 @@ class BurstForwarder: self.toa_min = self.TOA_RAND_BASE - self.TOA_RAND_TRESHOLD self.toa_max = self.TOA_RAND_BASE + self.TOA_RAND_TRESHOLD - def set_slot(self, ts): - if ts > 0 and ts < 8: - self.ts_pass = ts - else: - raise ValueError("Incorrect index for timeslot filter") - # Converts a L12TRX message to TRX2L1 message def transform_msg(self, msg_raw): # Attempt to parse a message From 9fc30a4102a46866d88b84b6640372cb09ddf32e Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 28 Feb 2018 15:38:27 +0700 Subject: [PATCH 168/211] fake_trx/data_msg.py: use integer math for ToA In order to avoid both float arithmetic as well as loosing any precision, let's use integer math fot ToA (Timing of Arrival), i.e. let's express ToA values in units of 1/256 symbol periods. Change-Id: I56b88740f4d782ac7591fc096d1969514784a4e1 --- src/target/fake_trx/burst_fwd.py | 12 +++++----- src/target/fake_trx/burst_gen.py | 14 +++++++---- src/target/fake_trx/burst_send.py | 4 ++-- src/target/fake_trx/data_dump.py | 22 ++++++++--------- src/target/fake_trx/data_msg.py | 40 +++++++++++++++---------------- 5 files changed, 47 insertions(+), 45 deletions(-) diff --git a/src/target/fake_trx/burst_fwd.py b/src/target/fake_trx/burst_fwd.py index db0b05b9a..4a0e9bbbf 100644 --- a/src/target/fake_trx/burst_fwd.py +++ b/src/target/fake_trx/burst_fwd.py @@ -41,8 +41,8 @@ class BurstForwarder: RSSI_RAND_MAX = -60 # TODO: add options to change this - TOA_RAND_TRESHOLD = 0.3 - TOA_RAND_BASE = 0.00 + TOA256_RAND_TRESHOLD = 128 + TOA256_RAND_BASE = 0 def __init__(self, bts_link, bb_link): self.bts_link = bts_link @@ -54,8 +54,8 @@ class BurstForwarder: self.rssi_max = rssi + self.RSSI_RAND_TRESHOLD # Generate a random ToA range - self.toa_min = self.TOA_RAND_BASE - self.TOA_RAND_TRESHOLD - self.toa_max = self.TOA_RAND_BASE + self.TOA_RAND_TRESHOLD + self.toa256_min = self.TOA256_RAND_BASE - self.TOA256_RAND_TRESHOLD + self.toa256_max = self.TOA256_RAND_BASE + self.TOA256_RAND_TRESHOLD # Converts a L12TRX message to TRX2L1 message def transform_msg(self, msg_raw): @@ -73,8 +73,8 @@ class BurstForwarder: # Randomize both RSSI and ToA values msg_trx2l1.rssi = msg_trx2l1.rand_rssi( min = self.rssi_min, max = self.rssi_max) - msg_trx2l1.toa = msg_trx2l1.rand_toa( - min = self.toa_min, max = self.toa_max) + msg_trx2l1.toa256 = msg_trx2l1.rand_toa256( + min = self.toa256_min, max = self.toa256_max) return msg_trx2l1 diff --git a/src/target/fake_trx/burst_gen.py b/src/target/fake_trx/burst_gen.py index c2e1ad2d5..179dbcda8 100755 --- a/src/target/fake_trx/burst_gen.py +++ b/src/target/fake_trx/burst_gen.py @@ -54,8 +54,8 @@ class Application: tn = None # Message specific header fields + toa256 = None rssi = None - toa = None pwr = None def __init__(self): @@ -108,8 +108,8 @@ class Application: msg.pwr = self.pwr # Set time of arrival - if self.toa is not None: - msg.toa = self.toa + if self.toa256 is not None: + msg.toa256 = self.toa256 # Set RSSI if self.rssi is not None: @@ -164,7 +164,8 @@ class Application: " -t --timeslot Set timeslot index (default random)\n" \ " --pwr Set power level (default random)\n" \ " --rssi Set RSSI (default random)\n" \ - " --toa Set TOA (default random)\n\n" + " --toa Set ToA in symbols (default random)\n" \ + " --toa256 Set ToA in 1/256 symbol periods\n" print(s % (self.remote_addr, self.base_port)) @@ -187,6 +188,7 @@ class Application: "timeslot=", "rssi=", "toa=", + "toa256=", "pwr=", ]) except getopt.GetoptError as err: @@ -221,8 +223,10 @@ class Application: self.pwr = int(v) elif o == "--rssi": self.rssi = int(v) + elif o == "--toa256": + self.toa256 = int(v) elif o == "--toa": - self.toa = float(v) + self.toa256 = int(float(v) * 256.0 + 0.5) def check_argv(self): # Check connection mode diff --git a/src/target/fake_trx/burst_send.py b/src/target/fake_trx/burst_send.py index 61a526f65..d6c5c0c58 100755 --- a/src/target/fake_trx/burst_send.py +++ b/src/target/fake_trx/burst_send.py @@ -91,8 +91,8 @@ class Application: continue # HACK: as ToA parsing is not implemented yet, - # we have to use a fixed 0.00 value for now... - msg.toa = 0.00 + # we have to use a fixed value for now... + msg.toa256 = 0 print("[i] Sending a burst %s to %s..." % (msg.desc_hdr(), self.conn_mode)) diff --git a/src/target/fake_trx/data_dump.py b/src/target/fake_trx/data_dump.py index 6102dc56d..5e16da8d8 100644 --- a/src/target/fake_trx/data_dump.py +++ b/src/target/fake_trx/data_dump.py @@ -258,9 +258,9 @@ if __name__ == '__main__': msg.rand_hdr() # HACK: as ToA parsing is not implemented yet, - # we have to use a fixed 0.00 value for now... + # we have to use a fixed value for now... if isinstance(msg, DATAMSG_TRX2L1): - msg.toa = 0.00 + msg.toa256 = 0 # Append messages_ref.append(msg) @@ -294,8 +294,8 @@ if __name__ == '__main__': assert(messages_check[i].tn == messages_ref[i].tn) # HACK: as ToA parsing is not implemented yet, - # we have to use a fixed 0.00 value for now... - messages_check[i].toa = 0.00 + # we have to use a fixed value for now... + messages_check[i].toa256 = 0 # Validate a message assert(messages_check[i].validate()) @@ -320,8 +320,8 @@ if __name__ == '__main__': assert(messages_check[i].tn == messages_ref[i].tn) # HACK: as ToA parsing is not implemented yet, - # we have to use a fixed 0.00 value for now... - messages_check[i].toa = 0.00 + # we have to use a fixed value for now... + messages_check[i].toa256 = 0 # Validate a message assert(messages_check[i].validate()) @@ -346,9 +346,9 @@ if __name__ == '__main__': assert(msg10.tn == messages_ref[10].tn) # HACK: as ToA parsing is not implemented yet, - # we have to use a fixed 0.00 value for now... - msg0.toa = 0.00 - msg10.toa = 0.00 + # we have to use a fixed value for now... + msg0.toa256 = 0 + msg10.toa256 = 0 # Validate both messages assert(msg0.validate()) @@ -373,8 +373,8 @@ if __name__ == '__main__': assert(messages_check[i].tn == messages_ref[i + 10].tn) # HACK: as ToA parsing is not implemented yet, - # we have to use a fixed 0.00 value for now... - messages_check[i].toa = 0.00 + # we have to use a fixed value for now... + messages_check[i].toa256 = 0 # Validate a message assert(messages_check[i].validate()) diff --git a/src/target/fake_trx/data_msg.py b/src/target/fake_trx/data_msg.py index 045e281ed..5dbebcf9f 100644 --- a/src/target/fake_trx/data_msg.py +++ b/src/target/fake_trx/data_msg.py @@ -303,12 +303,12 @@ class DATAMSG_TRX2L1(DATAMSG): RSSI_MAX = -50 # TODO: verify this range - TOA_MIN = -10.0 - TOA_MAX = 10.0 + TOA256_MIN = -256 * 10 + TOA256_MAX = 256 * 10 # Specific message fields rssi = None - toa = None + toa256 = None # Validates the message fields def validate(self): @@ -322,10 +322,10 @@ class DATAMSG_TRX2L1(DATAMSG): if self.rssi < self.RSSI_MIN or self.rssi > self.RSSI_MAX: return False - if self.toa is None: + if self.toa256 is None: return False - if self.toa < self.TOA_MIN or self.toa > self.TOA_MAX: + if self.toa256 < self.TOA256_MIN or self.toa256 > self.TOA256_MAX: return False return True @@ -341,20 +341,20 @@ class DATAMSG_TRX2L1(DATAMSG): return random.randint(min, max) # Generates a ToA (Time of Arrival) value - def rand_toa(self, min = None, max = None): + def rand_toa256(self, min = None, max = None): if min is None: - min = self.TOA_MIN + min = self.TOA256_MIN if max is None: - max = self.TOA_MAX + max = self.TOA256_MAX - return random.uniform(min, max) + return random.randint(min, max) # Randomizes message specific header def rand_hdr(self): DATAMSG.rand_hdr(self) self.rssi = self.rand_rssi() - self.toa = self.rand_toa() + self.toa256 = self.rand_toa256() # Generates human-readable header description def desc_hdr(self): @@ -364,8 +364,8 @@ class DATAMSG_TRX2L1(DATAMSG): if self.rssi is not None: result += ("rssi=%d " % self.rssi) - if self.toa is not None: - result += ("toa=%.2f " % self.toa) + if self.toa256 is not None: + result += ("toa256=%d " % self.toa256) # Strip useless whitespace and return return result.strip() @@ -378,12 +378,10 @@ class DATAMSG_TRX2L1(DATAMSG): # Put RSSI buf.append(-self.rssi) - # Round ToA (Time of Arrival) to closest integer - toa = int(self.toa * 256.0 + 0.5) - - # Encode ToA - buf.append((toa >> 8) & 0xff) - buf.append(toa & 0xff) + # Encode ToA (Time of Arrival) + # Big endian, 2 bytes (int32_t) + buf.append((self.toa256 >> 8) & 0xff) + buf.append(self.toa256 & 0xff) return buf @@ -394,7 +392,7 @@ class DATAMSG_TRX2L1(DATAMSG): # Parse ToA (Time of Arrival) # FIXME: parsing unsupported - self.toa = None + self.toa256 = None # Generates message specific burst def gen_burst(self): @@ -457,7 +455,7 @@ if __name__ == '__main__': # Fill in message specific fields msg_trx2l1_ref.rssi = -88 msg_l12trx_ref.pwr = 0x33 - msg_trx2l1_ref.toa = -0.6 + msg_trx2l1_ref.toa256 = -256 # Specify the reference bursts msg_l12trx_ref.burst = burst_l12trx_ref @@ -498,7 +496,7 @@ if __name__ == '__main__': assert(msg_l12trx_dec.pwr == msg_l12trx_ref.pwr) # FIXME: ToA check disabled until the parsing is implemented - # assert(msg_trx2l1_dec.toa == msg_trx2l1_ref.toa) + # assert(msg_trx2l1_dec.toa256 == msg_trx2l1_ref.toa256) print("[?] Compare message specific data: OK") From d9cb0654179b3d3bcd2878a001593e95f0a22f80 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 28 Feb 2018 15:48:17 +0700 Subject: [PATCH 169/211] fake_trx/data_msg.py: implement ToA parsing support This change implements ToA (Timing of Arrival) parsing, which was missing in the DATAMSG_TRX2L1. Since we use integer math, a ToA value is represented in units of 1/256 symbol periods. Change-Id: Ib11482c06b977c4cf01b0644f5845a2e49d059fb --- src/target/fake_trx/burst_send.py | 4 ---- src/target/fake_trx/data_dump.py | 22 ---------------------- src/target/fake_trx/data_msg.py | 8 +++----- 3 files changed, 3 insertions(+), 31 deletions(-) diff --git a/src/target/fake_trx/burst_send.py b/src/target/fake_trx/burst_send.py index d6c5c0c58..882fcd649 100755 --- a/src/target/fake_trx/burst_send.py +++ b/src/target/fake_trx/burst_send.py @@ -90,10 +90,6 @@ class Application: if not self.msg_pass_filter(l12trx, msg): continue - # HACK: as ToA parsing is not implemented yet, - # we have to use a fixed value for now... - msg.toa256 = 0 - print("[i] Sending a burst %s to %s..." % (msg.desc_hdr(), self.conn_mode)) diff --git a/src/target/fake_trx/data_dump.py b/src/target/fake_trx/data_dump.py index 5e16da8d8..1d7805e30 100644 --- a/src/target/fake_trx/data_dump.py +++ b/src/target/fake_trx/data_dump.py @@ -257,11 +257,6 @@ if __name__ == '__main__': # Randomize the header msg.rand_hdr() - # HACK: as ToA parsing is not implemented yet, - # we have to use a fixed value for now... - if isinstance(msg, DATAMSG_TRX2L1): - msg.toa256 = 0 - # Append messages_ref.append(msg) @@ -293,10 +288,6 @@ if __name__ == '__main__': assert(messages_check[i].fn == messages_ref[i].fn) assert(messages_check[i].tn == messages_ref[i].tn) - # HACK: as ToA parsing is not implemented yet, - # we have to use a fixed value for now... - messages_check[i].toa256 = 0 - # Validate a message assert(messages_check[i].validate()) @@ -319,10 +310,6 @@ if __name__ == '__main__': assert(messages_check[i].fn == messages_ref[i].fn) assert(messages_check[i].tn == messages_ref[i].tn) - # HACK: as ToA parsing is not implemented yet, - # we have to use a fixed value for now... - messages_check[i].toa256 = 0 - # Validate a message assert(messages_check[i].validate()) @@ -345,11 +332,6 @@ if __name__ == '__main__': assert(msg10.fn == messages_ref[10].fn) assert(msg10.tn == messages_ref[10].tn) - # HACK: as ToA parsing is not implemented yet, - # we have to use a fixed value for now... - msg0.toa256 = 0 - msg10.toa256 = 0 - # Validate both messages assert(msg0.validate()) assert(msg10.validate()) @@ -372,10 +354,6 @@ if __name__ == '__main__': assert(messages_check[i].fn == messages_ref[i + 10].fn) assert(messages_check[i].tn == messages_ref[i + 10].tn) - # HACK: as ToA parsing is not implemented yet, - # we have to use a fixed value for now... - messages_check[i].toa256 = 0 - # Validate a message assert(messages_check[i].validate()) diff --git a/src/target/fake_trx/data_msg.py b/src/target/fake_trx/data_msg.py index 5dbebcf9f..5f93187a7 100644 --- a/src/target/fake_trx/data_msg.py +++ b/src/target/fake_trx/data_msg.py @@ -23,6 +23,7 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import random +import struct from gsm_shared import * @@ -391,8 +392,7 @@ class DATAMSG_TRX2L1(DATAMSG): self.rssi = -(hdr[5]) # Parse ToA (Time of Arrival) - # FIXME: parsing unsupported - self.toa256 = None + self.toa256 = struct.unpack(">h", hdr[6:8])[0] # Generates message specific burst def gen_burst(self): @@ -494,9 +494,7 @@ if __name__ == '__main__': # Compare message specific parts assert(msg_trx2l1_dec.rssi == msg_trx2l1_ref.rssi) assert(msg_l12trx_dec.pwr == msg_l12trx_ref.pwr) - - # FIXME: ToA check disabled until the parsing is implemented - # assert(msg_trx2l1_dec.toa256 == msg_trx2l1_ref.toa256) + assert(msg_trx2l1_dec.toa256 == msg_trx2l1_ref.toa256) print("[?] Compare message specific data: OK") From f09989b1fb2f2c0aa6797bc1d004bc27a4e43f17 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 28 Feb 2018 19:01:56 +0700 Subject: [PATCH 170/211] fake_trx: handle SETTA (Timing Advance) indicated by MS Timing Advance value is a timing correction value, indicated by the network to MS, which is used to compensate UL signal delay. In other words, the network instructs a phone to transmit bursts N=TA symbol periods earlier than expected. Since we are in virtual environment, let's use TA value to calculate the ToA (Timing of Arrival) value for BTS. Change-Id: Ie5833a9f221587bbcac10f0b223ead9c1cbda72b --- src/target/fake_trx/burst_fwd.py | 19 +++++++++++++++++++ src/target/fake_trx/ctrl_if_bb.py | 14 ++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/target/fake_trx/burst_fwd.py b/src/target/fake_trx/burst_fwd.py index 4a0e9bbbf..bcf2257e8 100644 --- a/src/target/fake_trx/burst_fwd.py +++ b/src/target/fake_trx/burst_fwd.py @@ -34,6 +34,11 @@ class BurstForwarder: bts_freq = None bb_freq = None + # Timing Advance value indicated by MS (0 by default) + # Valid range: 0..63, where each unit means + # one GSM symbol advance. + ta = 0 + # Constants # TODO: add options to change this RSSI_RAND_TRESHOLD = 10 @@ -57,6 +62,17 @@ class BurstForwarder: self.toa256_min = self.TOA256_RAND_BASE - self.TOA256_RAND_TRESHOLD self.toa256_max = self.TOA256_RAND_BASE + self.TOA256_RAND_TRESHOLD + # Calculates ToA value for Uplink bursts (coming to a BTS) + def calc_toa_ul(self): + # Generate a random ToA value + toa256 = random.randint(self.toa256_min, self.toa256_max) + + # Apply TA value + ta256 = self.ta * 256 + toa256 -= ta256 + + return toa256 + # Converts a L12TRX message to TRX2L1 message def transform_msg(self, msg_raw): # Attempt to parse a message @@ -128,6 +144,9 @@ class BurstForwarder: if msg is None: return None + # Emulate ToA value for BTS + msg.toa256 = self.calc_toa_ul() + # Validate and generate the payload payload = msg.gen_msg() diff --git a/src/target/fake_trx/ctrl_if_bb.py b/src/target/fake_trx/ctrl_if_bb.py index 47c704847..74fb242e6 100644 --- a/src/target/fake_trx/ctrl_if_bb.py +++ b/src/target/fake_trx/ctrl_if_bb.py @@ -115,6 +115,20 @@ class CTRLInterfaceBB(CTRLInterface): return 0 + # Timing Advance + elif self.verify_cmd(request, "SETTA", 1): + print("[i] Recv SETTA cmd") + + # Parse and check TA value + ta = int(request[1]) + if ta < 0 or ta > 63: + print("[!] TA value should be in range: 0..63") + return -1 + + # Save to the BurstForwarder instance + self.burst_fwd.ta = ta + return 0 + # Wrong / unknown command else: # We don't care about other commands, From 3da6166111f66afc5a797f2c7f01cce76f1f9821 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 28 Feb 2018 20:25:44 +0700 Subject: [PATCH 171/211] fake_trx/burst_fwd.py: calculate both RSSI and ToA separately In order to be able to simulate and randomize both RSSI and ToA values for Uplink and Downlink separately, let's calculate them in separate methods of the BurstForwarder. Change-Id: Ia2031f22f2b549c799c782d0c8c8d0691fb6f18c --- src/target/fake_trx/burst_fwd.py | 92 +++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 30 deletions(-) diff --git a/src/target/fake_trx/burst_fwd.py b/src/target/fake_trx/burst_fwd.py index bcf2257e8..503d035f5 100644 --- a/src/target/fake_trx/burst_fwd.py +++ b/src/target/fake_trx/burst_fwd.py @@ -39,42 +39,75 @@ class BurstForwarder: # one GSM symbol advance. ta = 0 - # Constants - # TODO: add options to change this - RSSI_RAND_TRESHOLD = 10 - RSSI_RAND_MIN = -90 - RSSI_RAND_MAX = -60 + # Timing of Arrival values indicated by transceiver + # in units of 1/256 of GSM symbol periods. A pair of + # base and threshold values defines a range of ToA value + # randomization: from (base - threshold) to (base + threshold). + toa256_dl_base = 0 + toa256_ul_base = 0 - # TODO: add options to change this - TOA256_RAND_TRESHOLD = 128 - TOA256_RAND_BASE = 0 + toa256_dl_threshold = 128 + toa256_ul_threshold = 128 + + # RSSI values indicated by transceiver in dBm. + # A pair of base and threshold values defines a range of RSSI + # randomization: from (base - threshold) to (base + threshold). + rssi_dl_base = -60 + rssi_ul_base = -70 + + rssi_dl_threshold = 10 + rssi_ul_threshold = 5 def __init__(self, bts_link, bb_link): self.bts_link = bts_link self.bb_link = bb_link - # Generate a random RSSI range - rssi = random.randint(self.RSSI_RAND_MIN, self.RSSI_RAND_MAX) - self.rssi_min = rssi - self.RSSI_RAND_TRESHOLD - self.rssi_max = rssi + self.RSSI_RAND_TRESHOLD + # Calculates a random ToA value for Downlink bursts + def calc_dl_toa256(self): + # Calculate a range for randomization + toa256_min = self.toa256_dl_base - self.toa256_dl_threshold + toa256_max = self.toa256_dl_base + self.toa256_dl_threshold - # Generate a random ToA range - self.toa256_min = self.TOA256_RAND_BASE - self.TOA256_RAND_TRESHOLD - self.toa256_max = self.TOA256_RAND_BASE + self.TOA256_RAND_TRESHOLD - - # Calculates ToA value for Uplink bursts (coming to a BTS) - def calc_toa_ul(self): # Generate a random ToA value - toa256 = random.randint(self.toa256_min, self.toa256_max) + toa256 = random.randint(toa256_min, toa256_max) - # Apply TA value + return toa256 + + # Calculates a random ToA value for Uplink bursts + def calc_ul_toa256(self): + # Calculate a range for randomization + toa256_min = self.toa256_ul_base - self.toa256_ul_threshold + toa256_max = self.toa256_ul_base + self.toa256_ul_threshold + + # Generate a random ToA value + toa256 = random.randint(toa256_min, toa256_max) + + # Apply TA value indicated by MS ta256 = self.ta * 256 toa256 -= ta256 return toa256 + # Calculates a random RSSI value for Downlink bursts + def calc_dl_rssi(self): + # Calculate a range for randomization + rssi_min = self.rssi_dl_base - self.rssi_dl_threshold + rssi_max = self.rssi_dl_base + self.rssi_dl_threshold + + # Generate a random RSSI value + return random.randint(rssi_min, rssi_max) + + # Calculates a random RSSI value for Uplink bursts + def calc_ul_rssi(self): + # Calculate a range for randomization + rssi_min = self.rssi_ul_base - self.rssi_ul_threshold + rssi_max = self.rssi_ul_base + self.rssi_ul_threshold + + # Generate a random RSSI value + return random.randint(rssi_min, rssi_max) + # Converts a L12TRX message to TRX2L1 message - def transform_msg(self, msg_raw): + def transform_msg(self, msg_raw, dl = True): # Attempt to parse a message try: msg_l12trx = DATAMSG_L12TRX() @@ -87,10 +120,12 @@ class BurstForwarder: msg_trx2l1 = msg_l12trx.gen_trx2l1() # Randomize both RSSI and ToA values - msg_trx2l1.rssi = msg_trx2l1.rand_rssi( - min = self.rssi_min, max = self.rssi_max) - msg_trx2l1.toa256 = msg_trx2l1.rand_toa256( - min = self.toa256_min, max = self.toa256_max) + if dl: + msg_trx2l1.toa256 = self.calc_dl_toa256() + msg_trx2l1.rssi = self.calc_dl_rssi() + else: + msg_trx2l1.toa256 = self.calc_ul_toa256() + msg_trx2l1.rssi = self.calc_ul_rssi() return msg_trx2l1 @@ -108,7 +143,7 @@ class BurstForwarder: return None # Process a message - msg = self.transform_msg(data) + msg = self.transform_msg(data, dl = True) if msg is None: return None @@ -140,13 +175,10 @@ class BurstForwarder: return None # Process a message - msg = self.transform_msg(data) + msg = self.transform_msg(data, dl = False) if msg is None: return None - # Emulate ToA value for BTS - msg.toa256 = self.calc_toa_ul() - # Validate and generate the payload payload = msg.gen_msg() From 81896a83288881e38a7c8bffaad74cecd3fd7f0e Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 28 Feb 2018 22:06:36 +0700 Subject: [PATCH 172/211] fake_trx/burst_fwd.py: disable field randomization by default Both RSSI and ToA fields randomization is only required in some specific test / use cases, so let's disable it by default. Change-Id: I94835a840b6239f2c05197292825cb26977d0216 --- src/target/fake_trx/burst_fwd.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/target/fake_trx/burst_fwd.py b/src/target/fake_trx/burst_fwd.py index 503d035f5..d059e1386 100644 --- a/src/target/fake_trx/burst_fwd.py +++ b/src/target/fake_trx/burst_fwd.py @@ -34,6 +34,14 @@ class BurstForwarder: bts_freq = None bb_freq = None + # Randomization of RSSI + randomize_dl_rssi = False + randomize_ul_rssi = False + + # Randomization of ToA + randomize_dl_toa256 = False + randomize_ul_toa256 = False + # Timing Advance value indicated by MS (0 by default) # Valid range: 0..63, where each unit means # one GSM symbol advance. @@ -64,6 +72,10 @@ class BurstForwarder: # Calculates a random ToA value for Downlink bursts def calc_dl_toa256(self): + # Check if randomization is required + if not self.randomize_dl_toa256: + return self.toa256_dl_base + # Calculate a range for randomization toa256_min = self.toa256_dl_base - self.toa256_dl_threshold toa256_max = self.toa256_dl_base + self.toa256_dl_threshold @@ -75,6 +87,10 @@ class BurstForwarder: # Calculates a random ToA value for Uplink bursts def calc_ul_toa256(self): + # Check if randomization is required + if not self.randomize_ul_toa256: + return self.toa256_ul_base + # Calculate a range for randomization toa256_min = self.toa256_ul_base - self.toa256_ul_threshold toa256_max = self.toa256_ul_base + self.toa256_ul_threshold @@ -90,6 +106,10 @@ class BurstForwarder: # Calculates a random RSSI value for Downlink bursts def calc_dl_rssi(self): + # Check if randomization is required + if not self.randomize_dl_rssi: + return self.rssi_dl_base + # Calculate a range for randomization rssi_min = self.rssi_dl_base - self.rssi_dl_threshold rssi_max = self.rssi_dl_base + self.rssi_dl_threshold @@ -99,6 +119,10 @@ class BurstForwarder: # Calculates a random RSSI value for Uplink bursts def calc_ul_rssi(self): + # Check if randomization is required + if not self.randomize_ul_rssi: + return self.rssi_ul_base + # Calculate a range for randomization rssi_min = self.rssi_ul_base - self.rssi_ul_threshold rssi_max = self.rssi_ul_base + self.rssi_ul_threshold From 7de70009f63f5c923aaebd35aa9549931357c2c2 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 28 Feb 2018 22:21:37 +0700 Subject: [PATCH 173/211] fake_trx/fake_trx.py: add options to enable field randomization By default, both RSSI and ToA fields randomization is disabled. Let's add command line options, which allow one to enable it. Change-Id: Ieac63cc3aadef397906479a6179ba54a53a5311a --- src/target/fake_trx/fake_trx.py | 41 +++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/target/fake_trx/fake_trx.py b/src/target/fake_trx/fake_trx.py index bdc4c614b..c9e427cae 100755 --- a/src/target/fake_trx/fake_trx.py +++ b/src/target/fake_trx/fake_trx.py @@ -49,6 +49,12 @@ class Application: bts_base_port = 5700 bb_base_port = 6700 + # BurstForwarder field randomization + randomize_dl_toa256 = False + randomize_ul_toa256 = False + randomize_dl_rssi = False + randomize_ul_rssi = False + def __init__(self): self.print_copyright() self.parse_argv() @@ -74,12 +80,18 @@ class Application: self.bts_ctrl.pm = self.pm self.bb_ctrl.pm = self.pm - # BTS <-> BB burst forwarding + # Init DATA links self.bts_data = UDPLink(self.bts_addr, self.bts_base_port + 102, self.bts_base_port + 2) self.bb_data = UDPLink(self.bb_addr, self.bb_base_port + 102, self.bb_base_port + 2) + + # BTS <-> BB burst forwarding self.burst_fwd = BurstForwarder(self.bts_data, self.bb_data) + self.burst_fwd.randomize_dl_toa256 = self.randomize_dl_toa256 + self.burst_fwd.randomize_ul_toa256 = self.randomize_ul_toa256 + self.burst_fwd.randomize_dl_rssi = self.randomize_dl_rssi + self.burst_fwd.randomize_ul_rssi = self.randomize_ul_rssi # Share a BurstForwarder instance between BTS and BB self.bts_ctrl.burst_fwd = self.burst_fwd @@ -137,7 +149,13 @@ class Application: " -R --bts-addr Set BTS remote address (default %s)\n" \ " -r --bb-addr Set BB remote address (default %s)\n" \ " -P --bts-base-port Set BTS base port number (default %d)\n" \ - " -p --bb-base-port Set BB base port number (default %d)\n" + " -p --bb-base-port Set BB base port number (default %d)\n\n" + + s += " Simulation\n" \ + " --rand-dl-rssi Enable DL RSSI randomization\n" \ + " --rand-ul-rssi Enable UL RSSI randomization\n" \ + " --rand-dl-toa Enable DL ToA randomization\n" \ + " --rand-ul-toa Enable UL ToA randomization\n" print(s % (self.bts_addr, self.bb_addr, self.bts_base_port, self.bb_base_port)) @@ -149,8 +167,13 @@ class Application: try: opts, args = getopt.getopt(sys.argv[1:], "R:r:P:p:h", - ["help", "bts-addr=", "bb-addr=", - "bts-base-port=", "bb-base-port="]) + [ + "help", + "bts-addr=", "bb-addr=", + "bts-base-port=", "bb-base-port=", + "rand-dl-rssi", "rand-ul-rssi", + "rand-dl-toa", "rand-ul-toa", + ]) except getopt.GetoptError as err: self.print_help("[!] " + str(err)) sys.exit(2) @@ -170,6 +193,16 @@ class Application: elif o in ("-p", "--bb-base-port"): self.bb_base_port = int(v) + # Message field randomization + elif o == "rand-dl-rssi": + self.randomize_dl_rssi = True + elif o == "rand-ul-rssi": + self.randomize_ul_rssi = True + elif o == "rand-dl-toa": + self.randomize_dl_toa256 = True + elif o == "rand-ul-toa": + self.randomize_ul_toa256 = True + # Ensure there is no overlap between ports if self.bts_base_port == self.bb_base_port: self.print_help("[!] BTS and BB base ports should be different") From 5ab622d2b72ac6df9d71d42736a6a65ac76cfab5 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 22 Feb 2018 22:24:19 +0100 Subject: [PATCH 174/211] fake_trx/ctrl_if_(bb|bts).py: add FAKE_TOA command FAKE_TOA is an auxilary CTRL command, which may be used to update the ToA (Timing of Arrival) value of forwarded bursts at runtime. This is useful for testing the measurement processing code in OsmoBTS. The command is implemented for both BTS and BB CTRL interfaces in two absolute and relative forms: CMD FAKE_TOA CMD FAKE_TOA <+-BASE_DELTA> The first form overwrites both ToA value and its treshold. The second one is relative, and applies a delta to the current ToA value. The command affects Downlink bursts if sent on BTS CTRL interface, and Uplink bursts if sent on the BB CTRL. Change-Id: Ia23becec4104d47e7b22350db67b8834d6f1ad1b --- src/target/fake_trx/ctrl_if_bb.py | 23 +++++++++++++++++++++++ src/target/fake_trx/ctrl_if_bts.py | 23 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/src/target/fake_trx/ctrl_if_bb.py b/src/target/fake_trx/ctrl_if_bb.py index 74fb242e6..c4d879c0b 100644 --- a/src/target/fake_trx/ctrl_if_bb.py +++ b/src/target/fake_trx/ctrl_if_bb.py @@ -129,6 +129,29 @@ class CTRLInterfaceBB(CTRLInterface): self.burst_fwd.ta = ta return 0 + # Timing of Arrival simulation for Uplink + # Absolute form: CMD FAKE_TOA + elif self.verify_cmd(request, "FAKE_TOA", 2): + print("[i] Recv FAKE_TOA cmd") + + # Parse and apply both base and threshold + self.burst_fwd.toa256_ul_base = int(request[1]) + self.burst_fwd.toa256_ul_threshold = int(request[2]) + + # TODO: avoid sending response + return -1 + + # Timing of Arrival simulation for Uplink + # Relative form: CMD FAKE_TOA <+-BASE_DELTA> + elif self.verify_cmd(request, "FAKE_TOA", 1): + print("[i] Recv FAKE_TOA cmd") + + # Parse and apply delta + self.burst_fwd.toa256_ul_base += int(request[1]) + + # TODO: avoid sending response + return -1 + # Wrong / unknown command else: # We don't care about other commands, diff --git a/src/target/fake_trx/ctrl_if_bts.py b/src/target/fake_trx/ctrl_if_bts.py index 92a4abb2e..f27b87d22 100644 --- a/src/target/fake_trx/ctrl_if_bts.py +++ b/src/target/fake_trx/ctrl_if_bts.py @@ -97,6 +97,29 @@ class CTRLInterfaceBTS(CTRLInterface): self.burst_fwd.bts_freq = self.tx_freq return 0 + # Timing of Arrival simulation for Downlink + # Absolute form: CMD FAKE_TOA + elif self.verify_cmd(request, "FAKE_TOA", 2): + print("[i] Recv FAKE_TOA cmd") + + # Parse and apply both base and threshold + self.burst_fwd.toa256_dl_base = int(request[1]) + self.burst_fwd.toa256_dl_threshold = int(request[2]) + + # TODO: avoid sending response + return -1 + + # Timing of Arrival simulation for Downlink + # Relative form: CMD FAKE_TOA <+-BASE_DELTA> + elif self.verify_cmd(request, "FAKE_TOA", 1): + print("[i] Recv FAKE_TOA cmd") + + # Parse and apply delta + self.burst_fwd.toa256_dl_base += int(request[1]) + + # TODO: avoid sending response + return -1 + # Wrong / unknown command else: # We don't care about other commands, From 6fa80f274ed932b8142a9f04a652820b135483e7 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 1 Mar 2018 01:02:11 +0700 Subject: [PATCH 175/211] fake_trx/burst_fwd.py: FIX: apply TA value correctly If field randomization is disabled, Timing Advance value indicated by MS would be ignored. Let's fix this by separating the TA calculation code. Change-Id: If43d5823fc33efc2f1649ea941ab6f619bb6f5e7 --- src/target/fake_trx/burst_fwd.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/target/fake_trx/burst_fwd.py b/src/target/fake_trx/burst_fwd.py index d059e1386..9f0f84f1d 100644 --- a/src/target/fake_trx/burst_fwd.py +++ b/src/target/fake_trx/burst_fwd.py @@ -70,6 +70,11 @@ class BurstForwarder: self.bts_link = bts_link self.bb_link = bb_link + # Converts TA value from symbols to + # units of 1/256 of GSM symbol periods + def calc_ta256(self): + return self.ta * 256 + # Calculates a random ToA value for Downlink bursts def calc_dl_toa256(self): # Check if randomization is required @@ -98,10 +103,6 @@ class BurstForwarder: # Generate a random ToA value toa256 = random.randint(toa256_min, toa256_max) - # Apply TA value indicated by MS - ta256 = self.ta * 256 - toa256 -= ta256 - return toa256 # Calculates a random RSSI value for Downlink bursts @@ -149,6 +150,7 @@ class BurstForwarder: msg_trx2l1.rssi = self.calc_dl_rssi() else: msg_trx2l1.toa256 = self.calc_ul_toa256() + msg_trx2l1.toa256 -= self.calc_ta256() msg_trx2l1.rssi = self.calc_ul_rssi() return msg_trx2l1 From b1b11620191d14943303a5104d845295f74750e8 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 28 Feb 2018 22:46:29 +0100 Subject: [PATCH 176/211] trxcon: Define event names for osmo_fsm's Change-Id: Id3279e99966a0ab236923c497ac0abbc9ed2c93c --- src/host/trxcon/l1ctl_link.c | 5 +++++ src/host/trxcon/trx_if.c | 5 +++++ src/host/trxcon/trxcon.c | 9 +++++++++ 3 files changed, 19 insertions(+) diff --git a/src/host/trxcon/l1ctl_link.c b/src/host/trxcon/l1ctl_link.c index 39056547f..596a9d1df 100644 --- a/src/host/trxcon/l1ctl_link.c +++ b/src/host/trxcon/l1ctl_link.c @@ -45,6 +45,10 @@ #include "l1ctl_link.h" #include "l1ctl.h" +static struct value_string l1ctl_evt_names[] = { + { 0, NULL } /* no events? */ +}; + static struct osmo_fsm_state l1ctl_fsm_states[] = { [L1CTL_STATE_IDLE] = { .out_state_mask = GEN_MASK(L1CTL_STATE_CONNECTED), @@ -61,6 +65,7 @@ static struct osmo_fsm l1ctl_fsm = { .states = l1ctl_fsm_states, .num_states = ARRAY_SIZE(l1ctl_fsm_states), .log_subsys = DL1C, + .event_names = l1ctl_evt_names, }; static int l1ctl_link_read_cb(struct osmo_fd *bfd) diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index 6ee75d3e4..98e3cdd49 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -45,6 +45,10 @@ #include "logging.h" #include "scheduler.h" +static struct value_string trx_evt_names[] = { + { 0, NULL } /* no events? */ +}; + static struct osmo_fsm_state trx_fsm_states[] = { [TRX_STATE_OFFLINE] = { .out_state_mask = ( @@ -76,6 +80,7 @@ static struct osmo_fsm trx_fsm = { .states = trx_fsm_states, .num_states = ARRAY_SIZE(trx_fsm_states), .log_subsys = DTRX, + .event_names = trx_evt_names, }; static int trx_udp_open(void *priv, struct osmo_fd *ofd, const char *host, diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index 0250b726a..60db88788 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -125,11 +125,20 @@ static struct osmo_fsm_state trxcon_fsm_states[] = { }, }; +static const struct value_string app_evt_names[] = { + OSMO_VALUE_STRING(L1CTL_EVENT_CONNECT), + OSMO_VALUE_STRING(L1CTL_EVENT_DISCONNECT), + OSMO_VALUE_STRING(TRX_EVENT_OFFLINE), + OSMO_VALUE_STRING(TRX_EVENT_RSP_ERROR), + { 0, NULL } +}; + static struct osmo_fsm trxcon_fsm_def = { .name = "trxcon_app_fsm", .states = trxcon_fsm_states, .num_states = ARRAY_SIZE(trxcon_fsm_states), .log_subsys = DAPP, + .event_names = app_evt_names, }; static void print_usage(const char *app) From 4e74311b00363b2ba417d7ca3ed67abfab1eaec8 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 28 Feb 2018 23:02:46 +0100 Subject: [PATCH 177/211] fake_trx: Always send control responses to where commands are from fake_trx is using locally bound and not connected UDP sockets for control commands. When we receive a control command, we should not simply send the response to the default destination, but send it back to the exact ip+prt from which the command originated. This ensures correct routing of responses even in case multiple programs are interfacing concurrently with a control socket. Change-Id: I24a0bba6eed059b101af95dac7d059f34dd715fc --- src/target/fake_trx/ctrl_if.py | 10 +++++----- src/target/fake_trx/fake_trx.py | 4 ++-- src/target/fake_trx/udp_link.py | 6 ++++++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/target/fake_trx/ctrl_if.py b/src/target/fake_trx/ctrl_if.py index e84c1c877..394c959f1 100644 --- a/src/target/fake_trx/ctrl_if.py +++ b/src/target/fake_trx/ctrl_if.py @@ -25,16 +25,16 @@ from udp_link import UDPLink class CTRLInterface(UDPLink): - def handle_rx(self, data): + def handle_rx(self, data, remote): # print(data) if self.verify_req(data): request = self.prepare_req(data) rc = self.parse_cmd(request) if type(rc) is tuple: - self.send_response(request, rc[0], rc[1]) + self.send_response(request, remote, rc[0], rc[1]) else: - self.send_response(request, rc) + self.send_response(request, remote, rc) else: print("[!] Wrong data on CTRL interface") @@ -66,7 +66,7 @@ class CTRLInterface(UDPLink): return True - def send_response(self, request, response_code, params = None): + def send_response(self, request, remote, response_code, params = None): # Include status code, for example ["TXTUNE", "0", "941600"] request.insert(1, str(response_code)) @@ -77,7 +77,7 @@ class CTRLInterface(UDPLink): # Add the response signature, and join back to string response = "RSP " + " ".join(request) + "\0" # Now we have something like "RSP TXTUNE 0 941600" - self.send(response) + self.sendto(response, remote) def parse_cmd(self, request): raise NotImplementedError diff --git a/src/target/fake_trx/fake_trx.py b/src/target/fake_trx/fake_trx.py index c9e427cae..a0534fd2a 100755 --- a/src/target/fake_trx/fake_trx.py +++ b/src/target/fake_trx/fake_trx.py @@ -124,12 +124,12 @@ class Application: # CTRL commands from BTS if self.bts_ctrl.sock in r_event: data, addr = self.bts_ctrl.sock.recvfrom(128) - self.bts_ctrl.handle_rx(data.decode()) + self.bts_ctrl.handle_rx(data.decode(), addr) # CTRL commands from BB if self.bb_ctrl.sock in r_event: data, addr = self.bb_ctrl.sock.recvfrom(128) - self.bb_ctrl.handle_rx(data.decode()) + self.bb_ctrl.handle_rx(data.decode(), addr) def shutdown(self): print("[i] Shutting down...") diff --git a/src/target/fake_trx/udp_link.py b/src/target/fake_trx/udp_link.py index c464802b1..dda901cb0 100644 --- a/src/target/fake_trx/udp_link.py +++ b/src/target/fake_trx/udp_link.py @@ -43,3 +43,9 @@ class UDPLink: data = data.encode() self.sock.sendto(data, (self.remote_addr, self.remote_port)) + + def sendto(self, data, remote): + if type(data) not in [bytearray, bytes]: + data = data.encode() + + self.sock.sendto(data, remote) From cc447afe3475a60c4745206ee219e20aa60494bd Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 28 Feb 2018 23:16:02 +0100 Subject: [PATCH 178/211] fake_trx: Send positive response to FAKE_TOA commands Now that ctrl_if.py is capable of sending back the response to where the command originated from, we can just as well send a positive response back after executing the related commands. Change-Id: Icba138835149a7264f4db3a6b05f54ca501c4d54 --- src/target/fake_trx/ctrl_if_bb.py | 6 ++---- src/target/fake_trx/ctrl_if_bts.py | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/target/fake_trx/ctrl_if_bb.py b/src/target/fake_trx/ctrl_if_bb.py index c4d879c0b..c0c2fb7cd 100644 --- a/src/target/fake_trx/ctrl_if_bb.py +++ b/src/target/fake_trx/ctrl_if_bb.py @@ -138,8 +138,7 @@ class CTRLInterfaceBB(CTRLInterface): self.burst_fwd.toa256_ul_base = int(request[1]) self.burst_fwd.toa256_ul_threshold = int(request[2]) - # TODO: avoid sending response - return -1 + return 0 # Timing of Arrival simulation for Uplink # Relative form: CMD FAKE_TOA <+-BASE_DELTA> @@ -149,8 +148,7 @@ class CTRLInterfaceBB(CTRLInterface): # Parse and apply delta self.burst_fwd.toa256_ul_base += int(request[1]) - # TODO: avoid sending response - return -1 + return 0 # Wrong / unknown command else: diff --git a/src/target/fake_trx/ctrl_if_bts.py b/src/target/fake_trx/ctrl_if_bts.py index f27b87d22..e0a6adabb 100644 --- a/src/target/fake_trx/ctrl_if_bts.py +++ b/src/target/fake_trx/ctrl_if_bts.py @@ -106,8 +106,7 @@ class CTRLInterfaceBTS(CTRLInterface): self.burst_fwd.toa256_dl_base = int(request[1]) self.burst_fwd.toa256_dl_threshold = int(request[2]) - # TODO: avoid sending response - return -1 + return 0 # Timing of Arrival simulation for Downlink # Relative form: CMD FAKE_TOA <+-BASE_DELTA> @@ -117,8 +116,7 @@ class CTRLInterfaceBTS(CTRLInterface): # Parse and apply delta self.burst_fwd.toa256_dl_base += int(request[1]) - # TODO: avoid sending response - return -1 + return 0 # Wrong / unknown command else: From 380dc7e768c944ab0efc28f24205334a4c736a42 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 28 Feb 2018 23:59:45 +0100 Subject: [PATCH 179/211] fake_trx: Increase TOA256 value ranges In theory, the maximum TA value is 63 symbols, i.e. 63*256 in this context. However, our test cases want to test the BTS behavior is correct if ever a larger timing offset is reported from TRX to the BTS, to ensure it is rejected in the BTS. Let's hence increase the values to rather large min/max limits. We could also remove them completely. Change-Id: I691d081256e8c6d18ef2836299ed8f7d502da3ee --- src/target/fake_trx/data_msg.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/fake_trx/data_msg.py b/src/target/fake_trx/data_msg.py index 5f93187a7..a48eff7cc 100644 --- a/src/target/fake_trx/data_msg.py +++ b/src/target/fake_trx/data_msg.py @@ -304,8 +304,8 @@ class DATAMSG_TRX2L1(DATAMSG): RSSI_MAX = -50 # TODO: verify this range - TOA256_MIN = -256 * 10 - TOA256_MAX = 256 * 10 + TOA256_MIN = -256 * 200 + TOA256_MAX = 256 * 200 # Specific message fields rssi = None From 0d3030c76405fbcf3d8cbcb61547eee53aec02b3 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 1 Mar 2018 14:40:32 +0100 Subject: [PATCH 180/211] trxcon: Fix '-i' to specify the "TRX IP address" The command line help states '-i' is for 'TRX IP address', which is the remote IP address at which the TRX is to be found. Hoewever, it was used as the local (bind) IP address of the socket used towards the TRX. This is my attempt at fixing this. A more complete solution probably allows to specify both local (bind) and remote (connect) address, just to be clear. Change-Id: If0252b15e9c7942687c6dc470951d777f7af651c --- src/host/trxcon/trx_if.c | 45 +++++++++++----------------------------- src/host/trxcon/trx_if.h | 3 ++- src/host/trxcon/trxcon.c | 2 +- 3 files changed, 15 insertions(+), 35 deletions(-) diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index 98e3cdd49..048b720cb 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -83,42 +83,20 @@ static struct osmo_fsm trx_fsm = { .event_names = trx_evt_names, }; -static int trx_udp_open(void *priv, struct osmo_fd *ofd, const char *host, - uint16_t port_local, uint16_t port_remote, +static int trx_udp_open(void *priv, struct osmo_fd *ofd, const char *host_local, + uint16_t port_local, const char *host_remote, uint16_t port_remote, int (*cb)(struct osmo_fd *fd, unsigned int what)) { - struct sockaddr_storage sas; - struct sockaddr *sa = (struct sockaddr *) &sas; - socklen_t sa_len; int rc; ofd->data = priv; ofd->fd = -1; ofd->cb = cb; - /* Init RX side for UDP connection */ - rc = osmo_sock_init_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, - 0, host, port_local, OSMO_SOCK_F_BIND); - if (rc < 0) - return rc; - - /* Init TX side for UDP connection */ - sa_len = sizeof(sas); - rc = getsockname(ofd->fd, sa, &sa_len); - if (rc) - return rc; - - if (sa->sa_family == AF_INET) { - struct sockaddr_in *sin = (struct sockaddr_in *) sa; - sin->sin_port = htons(port_remote); - } else if (sa->sa_family == AF_INET6) { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa; - sin6->sin6_port = htons(port_remote); - } else { - return -EINVAL; - } - - rc = connect(ofd->fd, sa, sa_len); + /* Init UDP Connection */ + rc = osmo_sock_init2_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, 0, host_local, port_local, + host_remote, port_remote, + OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT); return rc; } @@ -641,7 +619,8 @@ int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn, * Open/close OsmoTRX connection */ -int trx_if_open(struct trx_instance **trx, const char *host, uint16_t port) +int trx_if_open(struct trx_instance **trx, const char *local_host, + const char *remote_host, uint16_t port) { struct trx_instance *trx_new; int rc; @@ -659,13 +638,13 @@ int trx_if_open(struct trx_instance **trx, const char *host, uint16_t port) INIT_LLIST_HEAD(&trx_new->trx_ctrl_list); /* Open sockets */ - rc = trx_udp_open(trx_new, &trx_new->trx_ofd_ctrl, host, - port + 101, port + 1, trx_ctrl_read_cb); + rc = trx_udp_open(trx_new, &trx_new->trx_ofd_ctrl, local_host, + port + 101, remote_host, port + 1, trx_ctrl_read_cb); if (rc < 0) goto error; - rc = trx_udp_open(trx_new, &trx_new->trx_ofd_data, host, - port + 102, port + 2, trx_data_rx_cb); + rc = trx_udp_open(trx_new, &trx_new->trx_ofd_data, local_host, + port + 102, remote_host, port + 2, trx_data_rx_cb); if (rc < 0) goto error; diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h index dd84315e3..6080dceec 100644 --- a/src/host/trxcon/trx_if.h +++ b/src/host/trxcon/trx_if.h @@ -52,7 +52,8 @@ struct trx_ctrl_msg { int cmd_len; }; -int trx_if_open(struct trx_instance **trx, const char *host, uint16_t port); +int trx_if_open(struct trx_instance **trx, const char *local_host, + const char *remote_host, uint16_t port); void trx_if_flush_ctrl(struct trx_instance *trx); void trx_if_close(struct trx_instance *trx); diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index 60db88788..55422403b 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -270,7 +270,7 @@ int main(int argc, char **argv) goto exit; /* Init transceiver interface */ - rc = trx_if_open(&app_data.trx, app_data.trx_ip, app_data.trx_base_port); + rc = trx_if_open(&app_data.trx, "0.0.0.0", app_data.trx_ip, app_data.trx_base_port); if (rc) goto exit; From ff233256e12b370db78af29d4b283ba5e19becfa Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 28 Feb 2018 22:52:47 +0100 Subject: [PATCH 181/211] fake_trx/ctrl_if_(bb|bts).py: log link info at start-up Change-Id: I4ebeed7271d91ab2e45199e0fb59776c00ad833c --- src/target/fake_trx/ctrl_if_bb.py | 2 +- src/target/fake_trx/ctrl_if_bts.py | 2 +- src/target/fake_trx/udp_link.py | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/target/fake_trx/ctrl_if_bb.py b/src/target/fake_trx/ctrl_if_bb.py index c0c2fb7cd..33263e5ae 100644 --- a/src/target/fake_trx/ctrl_if_bb.py +++ b/src/target/fake_trx/ctrl_if_bb.py @@ -33,8 +33,8 @@ class CTRLInterfaceBB(CTRLInterface): pm = None def __init__(self, remote_addr, remote_port, bind_port): - print("[i] Init CTRL interface for BB") CTRLInterface.__init__(self, remote_addr, remote_port, bind_port) + print("[i] Init CTRL interface for BB (%s)" % self.desc_link()) def parse_cmd(self, request): # Power control diff --git a/src/target/fake_trx/ctrl_if_bts.py b/src/target/fake_trx/ctrl_if_bts.py index e0a6adabb..449df4585 100644 --- a/src/target/fake_trx/ctrl_if_bts.py +++ b/src/target/fake_trx/ctrl_if_bts.py @@ -34,8 +34,8 @@ class CTRLInterfaceBTS(CTRLInterface): pm = None def __init__(self, remote_addr, remote_port, bind_port): - print("[i] Init CTRL interface for BTS") CTRLInterface.__init__(self, remote_addr, remote_port, bind_port) + print("[i] Init CTRL interface for BTS (%s)" % self.desc_link()) def parse_cmd(self, request): # Power control diff --git a/src/target/fake_trx/udp_link.py b/src/target/fake_trx/udp_link.py index dda901cb0..0174ae374 100644 --- a/src/target/fake_trx/udp_link.py +++ b/src/target/fake_trx/udp_link.py @@ -34,10 +34,15 @@ class UDPLink: # Save remote info self.remote_addr = remote_addr self.remote_port = remote_port + self.bind_port = bind_port def __del__(self): self.sock.close() + def desc_link(self): + return "L:%u <-> R:%s:%u" \ + % (self.bind_port, self.remote_addr, self.remote_port) + def send(self, data): if type(data) not in [bytearray, bytes]: data = data.encode() From c066787fd5e9696e4af0a43f7bd3fd9392de4931 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 2 Mar 2018 21:24:57 +0700 Subject: [PATCH 182/211] host/trxcon: use integer math for ToA (Timing of Arrival) There's no need to express ToA value as a float. Let's turn it into an int16_t with 1/256 symbol period accuracy throughout the code to avoid both float arithmetic as well as loosing any precision. Inspired by Idce4178e0b1f7e940ebc22b3e2f340fcd544d4ec. Change-Id: I99c0f38db08a530d5846c474aba352aa0b68fe86 --- src/host/trxcon/sched_lchan_desc.c | 6 +++--- src/host/trxcon/sched_lchan_sch.c | 2 +- src/host/trxcon/sched_lchan_tchf.c | 6 +++--- src/host/trxcon/sched_lchan_xcch.c | 6 +++--- src/host/trxcon/sched_trx.c | 5 +++-- src/host/trxcon/sched_trx.h | 9 +++++---- src/host/trxcon/trx_if.c | 10 +++++----- 7 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/host/trxcon/sched_lchan_desc.c b/src/host/trxcon/sched_lchan_desc.c index 390b1ef12..e0617a540 100644 --- a/src/host/trxcon/sched_lchan_desc.c +++ b/src/host/trxcon/sched_lchan_desc.c @@ -38,21 +38,21 @@ /* Forward declaration of handlers */ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - sbit_t *bits, int8_t rssi, float toa); + sbit_t *bits, int8_t rssi, int16_t toa256); int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid); int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - sbit_t *bits, int8_t rssi, float toa); + sbit_t *bits, int8_t rssi, int16_t toa256); int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid); int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - sbit_t *bits, int8_t rssi, float toa); + sbit_t *bits, int8_t rssi, int16_t toa256); int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid); diff --git a/src/host/trxcon/sched_lchan_sch.c b/src/host/trxcon/sched_lchan_sch.c index b2377fa6e..1b241a083 100644 --- a/src/host/trxcon/sched_lchan_sch.c +++ b/src/host/trxcon/sched_lchan_sch.c @@ -71,7 +71,7 @@ static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info) int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - sbit_t *bits, int8_t rssi, float toa) + sbit_t *bits, int8_t rssi, int16_t toa256) { sbit_t payload[2 * 39]; struct gsm_time time; diff --git a/src/host/trxcon/sched_lchan_tchf.c b/src/host/trxcon/sched_lchan_tchf.c index 45fa1c201..b023126a6 100644 --- a/src/host/trxcon/sched_lchan_tchf.c +++ b/src/host/trxcon/sched_lchan_tchf.c @@ -46,7 +46,7 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - sbit_t *bits, int8_t rssi, float toa) + sbit_t *bits, int8_t rssi, int16_t toa256) { const struct trx_lchan_desc *lchan_desc; uint8_t rsl_cmode, tch_mode, mode; @@ -79,9 +79,9 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, /* Update mask and RSSI */ lchan->meas.rssi_sum += rssi; - lchan->meas.toa_sum += toa; + lchan->meas.toa256_sum += toa256; lchan->meas.rssi_num++; - lchan->meas.toa_num++; + lchan->meas.toa256_num++; /* Copy burst to end of buffer of 8 bursts */ offset = buffer + bid * 116 + 464; diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index 201533050..b2cbd2239 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -43,7 +43,7 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - sbit_t *bits, int8_t rssi, float toa) + sbit_t *bits, int8_t rssi, int16_t toa256) { const struct trx_lchan_desc *lchan_desc; uint8_t l2[GSM_MACBLOCK_LEN], *mask; @@ -74,9 +74,9 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, /* Update measurements */ lchan->meas.rssi_sum += rssi; - lchan->meas.toa_sum += toa; + lchan->meas.toa256_sum += toa256; lchan->meas.rssi_num++; - lchan->meas.toa_num++; + lchan->meas.toa256_num++; /* Copy burst to buffer of 4 bursts */ offset = buffer + bid * 116; diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 0a9b7f73e..9ae5a8f6c 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -568,7 +568,8 @@ static void sched_trx_a5_burst_enc(struct trx_lchan_state *lchan, } int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, - uint32_t burst_fn, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa) + uint32_t burst_fn, sbit_t *bits, uint16_t nbits, + int8_t rssi, int16_t toa256) { struct trx_lchan_state *lchan; const struct trx_frame *frame; @@ -630,7 +631,7 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, sched_trx_a5_burst_dec(lchan, fn, bits); /* Put burst to handler */ - handler(trx, ts, lchan, fn, bid, bits, rssi, toa); + handler(trx, ts, lchan, fn, bid, bits, rssi, toa256); } next_frame: diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index b01624c8f..856566c45 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -82,7 +82,7 @@ enum trx_lchan_type { typedef int trx_lchan_rx_func(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, sbit_t *bits, - int8_t rssi, float toa); + int8_t rssi, int16_t toa256); typedef int trx_lchan_tx_func(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, @@ -174,9 +174,9 @@ struct trx_lchan_state { /*! \brief Sum of RSSI values */ float rssi_sum; /*! \brief Number of TOA values */ - uint8_t toa_num; + uint8_t toa256_num; /*! \brief Sum of TOA values */ - float toa_sum; + int32_t toa256_sum; } meas; /* AMR specific */ @@ -286,7 +286,8 @@ void sched_prim_drop(struct trx_lchan_state *lchan); void sched_prim_flush_queue(struct llist_head *list); int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, - uint32_t burst_fn, sbit_t *bits, uint16_t nbits, int8_t rssi, float toa); + uint32_t burst_fn, sbit_t *bits, uint16_t nbits, + int8_t rssi, int16_t toa256); int sched_trx_handle_tx_burst(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, ubit_t *bits); diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/trx_if.c index 048b720cb..cab5a9bde 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/trx_if.c @@ -535,8 +535,8 @@ static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what) uint8_t buf[256]; sbit_t bits[148]; int8_t rssi, tn; + int16_t toa256; uint32_t fn; - float toa; int len; len = recv(ofd->fd, buf, sizeof(buf), 0); @@ -552,7 +552,7 @@ static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what) tn = buf[0]; fn = (buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4]; rssi = -(int8_t) buf[5]; - toa = ((int16_t) (buf[6] << 8) | buf[7]) / 256.0F; + toa256 = ((int16_t) (buf[6] << 8) | buf[7]); /* Copy and convert bits {254..0} to sbits {-127..127} */ osmo_ubit2sbit(bits, buf + 8, 148); @@ -567,11 +567,11 @@ static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what) return -EINVAL; } - LOGP(DTRXD, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%.2f\n", - tn, fn, rssi, toa); + LOGP(DTRXD, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%d\n", + tn, fn, rssi, toa256); /* Poke scheduler */ - sched_trx_handle_rx_burst(trx, tn, fn, bits, 148, rssi, toa); + sched_trx_handle_rx_burst(trx, tn, fn, bits, 148, rssi, toa256); /* Correct local clock counter */ if (fn % 51 == 0) From f97e3cd7882dff977973a2f29606c594f02c0b36 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 6 Mar 2018 02:22:24 +0700 Subject: [PATCH 183/211] fake_trx/ctrl_if.py: remove forgotten debug print Change-Id: I4886828fb0f927c59c5eb1945a3c2873687de7b3 --- src/target/fake_trx/ctrl_if.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/target/fake_trx/ctrl_if.py b/src/target/fake_trx/ctrl_if.py index 394c959f1..64f08bfd3 100644 --- a/src/target/fake_trx/ctrl_if.py +++ b/src/target/fake_trx/ctrl_if.py @@ -26,7 +26,6 @@ from udp_link import UDPLink class CTRLInterface(UDPLink): def handle_rx(self, data, remote): - # print(data) if self.verify_req(data): request = self.prepare_req(data) rc = self.parse_cmd(request) From 59d054906d1d5bd6f21fb6072ead0f76b6e5f27a Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 6 Mar 2018 02:25:51 +0700 Subject: [PATCH 184/211] fake_trx/ctrl_if.py: reduce code branch nasting Let's use the inverted condition to avoid additional code nasting. Change-Id: I8a62b39d9d9a597c612f9a576e98dc05e37cd25b --- src/target/fake_trx/ctrl_if.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/target/fake_trx/ctrl_if.py b/src/target/fake_trx/ctrl_if.py index 64f08bfd3..d4840cde9 100644 --- a/src/target/fake_trx/ctrl_if.py +++ b/src/target/fake_trx/ctrl_if.py @@ -26,16 +26,18 @@ from udp_link import UDPLink class CTRLInterface(UDPLink): def handle_rx(self, data, remote): - if self.verify_req(data): - request = self.prepare_req(data) - rc = self.parse_cmd(request) - - if type(rc) is tuple: - self.send_response(request, remote, rc[0], rc[1]) - else: - self.send_response(request, remote, rc) - else: + if not self.verify_req(data): print("[!] Wrong data on CTRL interface") + return + + # Attempt to parse a command + request = self.prepare_req(data) + rc = self.parse_cmd(request) + + if type(rc) is tuple: + self.send_response(request, remote, rc[0], rc[1]) + else: + self.send_response(request, remote, rc) def verify_req(self, data): # Verify command signature From c20f9bb5cd2491169959922ce1ba249a34f589aa Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 6 Mar 2018 02:11:06 +0700 Subject: [PATCH 185/211] fake_trx/ctrl_if.py: remove incorrect isdigit() check Previously, we used to check if all arguments of a command are numeric. This was done in a wrong way, so parsing a *valid* command with at least one negative argument could fail. Let's remove this check, allowing the command handlers to deal with argument types themselves. Change-Id: If31295274a09102c414b5a7aec5dd85d88b2e514 --- src/target/fake_trx/ctrl_if.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/target/fake_trx/ctrl_if.py b/src/target/fake_trx/ctrl_if.py index d4840cde9..af124f65d 100644 --- a/src/target/fake_trx/ctrl_if.py +++ b/src/target/fake_trx/ctrl_if.py @@ -60,11 +60,6 @@ class CTRLInterface(UDPLink): if len(request) - 1 != argc: return False - # Check if all arguments are numeric - for v in request[1:]: - if not v.isdigit(): - return False - return True def send_response(self, request, remote, response_code, params = None): From 2812cb7f80e08205f831934886c6019c92947614 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 6 Mar 2018 22:42:26 +0700 Subject: [PATCH 186/211] fake_trx/udp_link.py: use a random bind port by default This allows one to obtain a random available port from the OS, instead of enforcing to pick a static value manually. Change-Id: Ie8b60134239c5447d0b4373c6cca2f3a6ee3ec73 --- src/target/fake_trx/udp_link.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/target/fake_trx/udp_link.py b/src/target/fake_trx/udp_link.py index 0174ae374..99da95bb5 100644 --- a/src/target/fake_trx/udp_link.py +++ b/src/target/fake_trx/udp_link.py @@ -25,7 +25,7 @@ import socket class UDPLink: - def __init__(self, remote_addr, remote_port, bind_port): + def __init__(self, remote_addr, remote_port, bind_port = 0): self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.bind(('0.0.0.0', bind_port)) @@ -34,14 +34,15 @@ class UDPLink: # Save remote info self.remote_addr = remote_addr self.remote_port = remote_port - self.bind_port = bind_port def __del__(self): self.sock.close() def desc_link(self): - return "L:%u <-> R:%s:%u" \ - % (self.bind_port, self.remote_addr, self.remote_port) + (bind_addr, bind_port) = self.sock.getsockname() + + return "L:%s:%u <-> R:%s:%u" \ + % (bind_addr, bind_port, self.remote_addr, self.remote_port) def send(self, data): if type(data) not in [bytearray, bytes]: From 7fd8ef2d3f3d296a6032745396d3af8e8e3d4da2 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 6 Mar 2018 22:50:36 +0700 Subject: [PATCH 187/211] fake_trx/ctrl_cmd.py: use a random bind port by default Since it is not required to specify a bind port to the UDPLink constructor manually, let's use a random one by default, and also allow user to set it from command line. Change-Id: Ib4965ebeec83d9a99b2f026156eb5f5cb20875bf --- src/target/fake_trx/ctrl_cmd.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/target/fake_trx/ctrl_cmd.py b/src/target/fake_trx/ctrl_cmd.py index 3faf8127d..83baa1971 100755 --- a/src/target/fake_trx/ctrl_cmd.py +++ b/src/target/fake_trx/ctrl_cmd.py @@ -4,7 +4,7 @@ # Auxiliary tool to send custom commands via TRX CTRL interface, # which may be useful for testing and fuzzing # -# (C) 2017 by Vadim Yanitskiy +# (C) 2017-2018 by Vadim Yanitskiy # # All Rights Reserved # @@ -30,7 +30,7 @@ import sys from udp_link import UDPLink COPYRIGHT = \ - "Copyright (C) 2017 by Vadim Yanitskiy \n" \ + "Copyright (C) 2017-2018 by Vadim Yanitskiy \n" \ "License GPLv2+: GNU GPL version 2 or later " \ "\n" \ "This is free software: you are free to change and redistribute it.\n" \ @@ -40,6 +40,7 @@ class Application: # Application variables remote_addr = "127.0.0.1" base_port = 5700 + bind_port = 0 fuzzing = False def __init__(self): @@ -51,7 +52,11 @@ class Application: # Init UDP connection self.ctrl_link = UDPLink(self.remote_addr, - self.base_port + 1, self.base_port + 101) + self.base_port + 1, self.bind_port) + + # Debug print + print("[i] Init CTRL interface (%s)" \ + % self.ctrl_link.desc_link()) def print_help(self, msg = None): s = " Usage: " + sys.argv[0] + " [options]\n\n" \ @@ -61,6 +66,7 @@ class Application: s += " TRX interface specific\n" \ " -r --remote-addr Set remote address (default %s)\n" \ " -p --base-port Set base port number (default %d)\n" \ + " -P --bind-port Set local port number (default: random)\n" \ " -f --fuzzing Send raw payloads (without CMD)\n" \ print(s % (self.remote_addr, self.base_port)) @@ -71,11 +77,12 @@ class Application: def parse_argv(self): try: opts, args = getopt.getopt(sys.argv[1:], - "r:p:fh", + "r:p:P:fh", [ "help", "fuzzing", "base-port=", + "bind-port=", "remote-addr=", ]) except getopt.GetoptError as err: @@ -91,6 +98,8 @@ class Application: self.remote_addr = v elif o in ("-p", "--base-port"): self.base_port = int(v) + elif o in ("-P", "--bind-port"): + self.bind_port = int(v) elif o in ("-f", "--fuzzing"): self.fuzzing = True From cbf818d4dcba53b226df8d36966482dc8b6d6ef4 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 9 Mar 2018 16:20:12 +0700 Subject: [PATCH 188/211] trxcon/configure.ac: add --enable-sanitize option Change-Id: I099de726f9d67213c56d996039b4207f80a727c6 --- src/host/trxcon/configure.ac | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/host/trxcon/configure.ac b/src/host/trxcon/configure.ac index a94859b79..1f24260da 100644 --- a/src/host/trxcon/configure.ac +++ b/src/host/trxcon/configure.ac @@ -18,6 +18,17 @@ PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm) dnl checks for header files AC_HEADER_STDC +AC_ARG_ENABLE(sanitize, + [AS_HELP_STRING( + [--enable-sanitize], + [Compile with address sanitizer enabled], + )], [sanitize=$enableval], [sanitize="no"]) +if test x"$sanitize" = x"yes" +then + CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined" + CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined" +fi + dnl Checks for typedefs, structures and compiler characteristics AC_OUTPUT( From f06f31fdf7e0fc4fd2d4cb3a444855bfaafd59da Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 9 Mar 2018 15:33:59 +0700 Subject: [PATCH 189/211] trxcon/sched_trx.c: fix: properly deallocate lchans The llist_for_each_entry_safe() should be used instead of the llist_for_each_entry(), because it's safe against removal of llist entry. Found using Valgrind's memcheck tool. Change-Id: I65234971ec152df038c5388da537a503060c215b --- src/host/trxcon/sched_trx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 9ae5a8f6c..1197f2cab 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -185,7 +185,7 @@ struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int tn) void sched_trx_del_ts(struct trx_instance *trx, int tn) { - struct trx_lchan_state *lchan; + struct trx_lchan_state *lchan, *lchan_next; struct trx_ts *ts; /* Find ts in list */ @@ -199,8 +199,10 @@ void sched_trx_del_ts(struct trx_instance *trx, int tn) sched_trx_deactivate_all_lchans(ts); /* Free channel states */ - llist_for_each_entry(lchan, &ts->lchans, list) + llist_for_each_entry_safe(lchan, lchan_next, &ts->lchans, list) { + llist_del(&lchan->list); talloc_free(lchan); + } /* Flush queue primitives for TX */ sched_prim_flush_queue(&ts->tx_prims); From 2778e4ef432c261cbeaeb344137c465adc8393dd Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sun, 11 Mar 2018 04:20:57 +0700 Subject: [PATCH 190/211] trxcon/sched_trx.c: fix: omit inactive logical channels The sched_frame_clck_cb() is responsible for UL burst transmission. Iterating over each timeslot, it chooses a proper lchan handler according to a current frame number and a multiframe layout in use, takes a L2 UL frame from a TX buffer, and finally calls the chosen handler in order to to encode and transmit a taken frame. A handler should be called only for activated logical channels... but for some long time, there was a bug, so each lchan was processed, including inactive ones. It's time to fix this. Change-Id: I33e3ecc14be3ae64dfd02789c7f0970c945582c9 --- src/host/trxcon/sched_trx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 1197f2cab..8c859dee8 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -86,6 +86,10 @@ static void sched_frame_clck_cb(struct trx_sched *sched) if (lchan == NULL) continue; + /* Omit inactive lchans */ + if (!lchan->active) + continue; + /** * If we aren't processing any primitive yet, * attempt to obtain a new one from queue From c00985bf99cd38cab7f50855866524a4a60d47dd Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sun, 11 Mar 2018 03:16:40 +0700 Subject: [PATCH 191/211] trxcon/sched_trx.h: clarify lchan flags meaning Change-Id: I51b663dd16e46a4523488c3d3000922a7c3640d1 --- src/host/trxcon/sched_trx.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 856566c45..78f458dfe 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -17,11 +17,13 @@ #define GPRS_BURST_LEN GSM_BURST_LEN #define EDGE_BURST_LEN 444 +/* Is a channel related to PDCH (GPRS) */ #define TRX_CH_FLAG_PDCH (1 << 0) +/* Should a channel be activated automatically */ #define TRX_CH_FLAG_AUTO (1 << 1) -#define TRX_TS_COUNT 8 #define MAX_A5_KEY_LEN (128 / 8) +#define TRX_TS_COUNT 8 /* Forward declaration to avoid mutual include */ struct trx_lchan_state; From 4cf722364bdafd90d12984531e4ec8c8548d6579 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sun, 11 Mar 2018 03:19:33 +0700 Subject: [PATCH 192/211] trxcon/scheduler: introduce a new CBTX lchan flag This new flag is intended to indicate that continuous burst transmission is assumed on particular logical channel. In other words, if a logical channel has this flag, but there is nothing to transmit in a TX buffer, then either a dummy LAPDm frame or a silence frame shall be sent. Change-Id: I25fcf9eeb787ffe5378d92532439e67d7d42fa65 --- src/host/trxcon/sched_lchan_desc.c | 60 +++++++++++++++--------------- src/host/trxcon/sched_trx.h | 2 + 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/host/trxcon/sched_lchan_desc.c b/src/host/trxcon/sched_lchan_desc.c index e0617a540..e94a2083a 100644 --- a/src/host/trxcon/sched_lchan_desc.c +++ b/src/host/trxcon/sched_lchan_desc.c @@ -109,181 +109,181 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { { TRXC_TCHF, "TCH/F", 0x08, LID_DEDIC, - 8 * GSM_BURST_PL_LEN, 0x00, + 8 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_tchf_fn, tx_tchf_fn, }, { TRXC_TCHH_0, "TCH/H(0)", 0x10, LID_DEDIC, - 6 * GSM_BURST_PL_LEN, 0x00, + 6 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_tchh_fn, tx_tchh_fn, }, { TRXC_TCHH_1, "TCH/H(1)", 0x18, LID_DEDIC, - 6 * GSM_BURST_PL_LEN, 0x00, + 6 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_tchh_fn, tx_tchh_fn, }, { TRXC_SDCCH4_0, "SDCCH/4(0)", 0x20, LID_DEDIC, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH4_1, "SDCCH/4(1)", 0x28, LID_DEDIC, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH4_2, "SDCCH/4(2)", 0x30, LID_DEDIC, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH4_3, "SDCCH/4(3)", 0x38, LID_DEDIC, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH8_0, "SDCCH/8(0)", 0x40, LID_DEDIC, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH8_1, "SDCCH/8(1)", 0x48, LID_DEDIC, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH8_2, "SDCCH/8(2)", 0x50, LID_DEDIC, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH8_3, "SDCCH/8(3)", 0x58, LID_DEDIC, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH8_4, "SDCCH/8(4)", 0x60, LID_DEDIC, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH8_5, "SDCCH/8(5)", 0x68, LID_DEDIC, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH8_6, "SDCCH/8(6)", 0x70, LID_DEDIC, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH8_7, "SDCCH/8(7)", 0x78, LID_DEDIC, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCHTF, "SACCH/TF", 0x08, LID_SACCH, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCHTH_0, "SACCH/TH(0)", 0x10, LID_SACCH, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCHTH_1, "SACCH/TH(1)", 0x18, LID_SACCH, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH4_0, "SACCH/4(0)", 0x20, LID_SACCH, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH4_1, "SACCH/4(1)", 0x28, LID_SACCH, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH4_2, "SACCH/4(2)", 0x30, LID_SACCH, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH4_3, "SACCH/4(3)", 0x38, LID_SACCH, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH8_0, "SACCH/8(0)", 0x40, LID_SACCH, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH8_1, "SACCH/8(1)", 0x48, LID_SACCH, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH8_2, "SACCH/8(2)", 0x50, LID_SACCH, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH8_3, "SACCH/8(3)", 0x58, LID_SACCH, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH8_4, "SACCH/8(4)", 0x60, LID_SACCH, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH8_5, "SACCH/8(5)", 0x68, LID_SACCH, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH8_6, "SACCH/8(6)", 0x70, LID_SACCH, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH8_7, "SACCH/8(7)", 0x78, LID_SACCH, - 4 * GSM_BURST_PL_LEN, 0x00, + 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 78f458dfe..6a0254360 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -21,6 +21,8 @@ #define TRX_CH_FLAG_PDCH (1 << 0) /* Should a channel be activated automatically */ #define TRX_CH_FLAG_AUTO (1 << 1) +/* Is continuous burst transmission assumed */ +#define TRX_CH_FLAG_CBTX (1 << 2) #define MAX_A5_KEY_LEN (128 / 8) #define TRX_TS_COUNT 8 From 5eae19098ac6e0563d3e176818ca6a6a4fb8656a Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sun, 11 Mar 2018 04:18:06 +0700 Subject: [PATCH 193/211] trxcon/scheduler: transmit dummy frames on CBTX lchans If at the moment of transmission there are no frames in TX buffer, then either a dummy LAPDm frame (0x01, 0x03, 0x01, 0x2b ...) or a silence frame (depending on a codec in use) shall be transmitted. This is required for proper measurements on the BTS side. Change-Id: Ie590990f2274ea476678f6b2079f90eeadab6501 --- src/host/trxcon/sched_prim.c | 89 ++++++++++++++++++++++++++++++++++++ src/host/trxcon/sched_trx.c | 13 ++++++ src/host/trxcon/sched_trx.h | 12 +++++ src/host/trxcon/trxcon.c | 4 ++ 4 files changed, 118 insertions(+) diff --git a/src/host/trxcon/sched_prim.c b/src/host/trxcon/sched_prim.c index fb5f0a0de..c17fb2a41 100644 --- a/src/host/trxcon/sched_prim.c +++ b/src/host/trxcon/sched_prim.c @@ -23,6 +23,7 @@ */ #include +#include #include #include @@ -31,6 +32,7 @@ #include #include +#include #include "scheduler.h" #include "sched_trx.h" @@ -226,6 +228,93 @@ void sched_prim_drop(struct trx_lchan_state *lchan) lchan->prim = NULL; } +/** + * Assigns a dummy primitive to a lchan depending on its type. + * Could be used when there is nothing to transmit, but + * CBTX (Continuous Burst Transmission) is assumed. + * + * @param lchan lchan to assign a primitive + * @return zero in case of success, otherwise a error code + */ +int sched_prim_dummy(struct trx_lchan_state *lchan) +{ + enum trx_lchan_type chan = lchan->type; + uint8_t tch_mode = lchan->tch_mode; + struct trx_ts_prim *prim; + uint8_t prim_buffer[40]; + size_t prim_len = 0; + int i; + + /** + * TS 144.006, section 8.4.2.3 "Fill frames" + * A fill frame is a UI command frame for SAPI 0, P=0 + * and with an information field of 0 octet length. + */ + static const uint8_t lapdm_fill_frame[] = { + 0x01, 0x03, 0x01, + /* Pending part is to be randomized */ + }; + + /* Make sure that there is no existing primitive */ + OSMO_ASSERT(lchan->prim == NULL); + + /** + * Determine what actually should be generated: + * TCH in GSM48_CMODE_SIGN: LAPDm fill frame; + * TCH in other modes: silence frame; + * other channels: LAPDm fill frame. + */ + if (CHAN_IS_TCH(chan) && TCH_MODE_IS_SPEECH(tch_mode)) { + /** + * Silence frame indication + * HACK: use actual rsl_cmode! + */ + prim_len = sched_bad_frame_ind(prim_buffer, + RSL_CMOD_SPD_SPEECH, tch_mode); + } else if (CHAN_IS_TCH(chan) && TCH_MODE_IS_DATA(tch_mode)) { + /* FIXME: should we do anything for CSD? */ + return 0; + } else { + /** + * TS 144.006, section 8.1.2.3 "Fill frames" + * A fill frame is a UI command frame for SAPI 0, P=0 + * and with an information field of 0 octet length. + */ + memcpy(prim_buffer, lapdm_fill_frame, 3); + + /* Randomize pending unused bytes */ + for (i = 3; i < GSM_MACBLOCK_LEN; i++) + prim_buffer[i] = (uint8_t) rand(); + + /* Define a prim length */ + prim_len = GSM_MACBLOCK_LEN; + } + + /* Nothing to allocate / assign */ + if (!prim_len) + return 0; + + /* Allocate a new primitive */ + prim = talloc_zero_size(lchan, sizeof(struct trx_ts_prim) + prim_len); + if (prim == NULL) + return -ENOMEM; + + /* Init primitive header */ + prim->payload_len = prim_len; + prim->chan = lchan->type; + + /* Fill in the payload */ + memcpy(prim->payload, prim_buffer, prim_len); + + /* Assign the current prim */ + lchan->prim = prim; + + LOGP(DSCHD, LOGL_DEBUG, "Transmitting a dummy / silence frame " + "on lchan=%s\n", trx_lchan_desc[chan].name); + + return 0; +} + /** * Flushes a queue of primitives * diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 8c859dee8..168c4efa9 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -97,6 +97,19 @@ static void sched_frame_clck_cb(struct trx_sched *sched) if (lchan->prim == NULL) lchan->prim = sched_prim_dequeue(&ts->tx_prims, chan); + /* TODO: report TX buffers health to the higher layers */ + + /* If CBTX (Continuous Burst Transmission) is assumed */ + if (trx_lchan_desc[chan].flags & TRX_CH_FLAG_CBTX) { + /** + * Probably, a TX buffer is empty. Nevertheless, + * we shall continuously transmit anything on + * CBTX channels. + */ + if (lchan->prim == NULL) + sched_prim_dummy(lchan); + } + /* If there is no primitive, do nothing */ if (lchan->prim == NULL) continue; diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 6a0254360..d9e0c8ed4 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -275,6 +275,17 @@ int sched_prim_init(struct trx_instance *trx, struct trx_ts_prim **prim, int sched_prim_push(struct trx_instance *trx, struct trx_ts_prim *prim, uint8_t chan_nr); +#define TCH_MODE_IS_SPEECH(mode) \ + (mode == GSM48_CMODE_SPEECH_V1 \ + || mode == GSM48_CMODE_SPEECH_EFR \ + || mode == GSM48_CMODE_SPEECH_AMR) + +#define TCH_MODE_IS_DATA(mode) \ + (mode == GSM48_CMODE_DATA_14k5 \ + || mode == GSM48_CMODE_DATA_12k0 \ + || mode == GSM48_CMODE_DATA_6k0 \ + || mode == GSM48_CMODE_DATA_3k6) + #define CHAN_IS_TCH(chan) \ (chan == TRXC_TCHF || chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1) @@ -286,6 +297,7 @@ int sched_prim_push(struct trx_instance *trx, struct trx_ts_prim *sched_prim_dequeue(struct llist_head *queue, enum trx_lchan_type lchan_type); +int sched_prim_dummy(struct trx_lchan_state *lchan); void sched_prim_drop(struct trx_lchan_state *lchan); void sched_prim_flush_queue(struct llist_head *list); diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index 55422403b..43c98a5c0 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -293,6 +294,9 @@ int main(int argc, char **argv) } } + /* Initialize pseudo-random generator */ + srand(time(NULL)); + while (!app_data.quit) osmo_select_main(0); From a92fd3388cd7eac69e18938832be688c9a425613 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 8 Mar 2018 20:51:52 +0700 Subject: [PATCH 194/211] trxcon: use meaningful names for L1CTL messages There are two types of L1CTL messages: received and to be transmitted. Let's use proper names to indicate this. Change-Id: I7c17687579282fa389bca35dc7edbc3582e55701 --- src/host/trxcon/l1ctl.c | 2 +- src/host/trxcon/l1ctl_link.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index dd75af954..58b8a4ff2 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -49,7 +49,7 @@ static struct msgb *l1ctl_alloc_msg(uint8_t msg_type) { struct l1ctl_hdr *l1h; - struct msgb *msg = msgb_alloc_headroom(256, 4, "osmo_l1"); + struct msgb *msg = msgb_alloc_headroom(256, 4, "l1ctl_tx_msg"); if (!msg) { LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory\n"); diff --git a/src/host/trxcon/l1ctl_link.c b/src/host/trxcon/l1ctl_link.c index 596a9d1df..a7277ea52 100644 --- a/src/host/trxcon/l1ctl_link.c +++ b/src/host/trxcon/l1ctl_link.c @@ -77,7 +77,7 @@ static int l1ctl_link_read_cb(struct osmo_fd *bfd) /* Allocate a new msg */ msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_HEADROOM, - L1CTL_HEADROOM, "L1CTL"); + L1CTL_HEADROOM, "l1ctl_rx_msg"); if (!msg) { LOGP(DL1D, LOGL_ERROR, "Failed to allocate msg\n"); return -ENOMEM; From 2136891b7bbdfbd62f28f63c8a16711d9562ec1c Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 9 Mar 2018 06:34:04 +0700 Subject: [PATCH 195/211] trxcon: clarify L1CTL message length field Each L1CTL message gets its own length pushed in front before sending. This isn't specified in the 'l1ctl_proto.h', but assumed in the code. Let's clarify this. Change-Id: I118d00613aeaf5ff0bad1188fa5f7450d4ca8122 --- src/host/trxcon/l1ctl.c | 8 +++++++- src/host/trxcon/l1ctl_link.c | 8 ++++---- src/host/trxcon/l1ctl_link.h | 8 ++++++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 58b8a4ff2..f138b88c8 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -49,8 +49,14 @@ static struct msgb *l1ctl_alloc_msg(uint8_t msg_type) { struct l1ctl_hdr *l1h; - struct msgb *msg = msgb_alloc_headroom(256, 4, "l1ctl_tx_msg"); + struct msgb *msg; + /** + * Each L1CTL message gets its own length pushed in front + * before sending. This is why we need this small headroom. + */ + msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_MSG_LEN_FIELD, + L1CTL_MSG_LEN_FIELD, "l1ctl_tx_msg"); if (!msg) { LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory\n"); return NULL; diff --git a/src/host/trxcon/l1ctl_link.c b/src/host/trxcon/l1ctl_link.c index a7277ea52..0fa3efe5f 100644 --- a/src/host/trxcon/l1ctl_link.c +++ b/src/host/trxcon/l1ctl_link.c @@ -84,8 +84,8 @@ static int l1ctl_link_read_cb(struct osmo_fd *bfd) } /* Attempt to read from socket */ - rc = read(bfd->fd, &len, sizeof(len)); - if (rc < sizeof(len)) { + rc = read(bfd->fd, &len, L1CTL_MSG_LEN_FIELD); + if (rc < L1CTL_MSG_LEN_FIELD) { LOGP(DL1D, LOGL_NOTICE, "L1CTL has lost connection\n"); msgb_free(msg); if (rc >= 0) @@ -198,8 +198,8 @@ int l1ctl_link_send(struct l1ctl_link *l1l, struct msgb *msg) LOGP(DL1D, 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)); + len = (uint16_t *) msgb_push(msg, L1CTL_MSG_LEN_FIELD); + *len = htons(msg->len - L1CTL_MSG_LEN_FIELD); if (osmo_wqueue_enqueue(&l1l->wq, msg) != 0) { LOGP(DL1D, LOGL_ERROR, "Failed to enqueue msg!\n"); diff --git a/src/host/trxcon/l1ctl_link.h b/src/host/trxcon/l1ctl_link.h index f9d91f14d..01103dccc 100644 --- a/src/host/trxcon/l1ctl_link.h +++ b/src/host/trxcon/l1ctl_link.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -9,6 +11,12 @@ #define L1CTL_LENGTH 256 #define L1CTL_HEADROOM 32 +/** + * Each L1CTL message gets its own length pushed + * as two bytes in front before sending. + */ +#define L1CTL_MSG_LEN_FIELD 2 + /* Forward declaration to avoid mutual include */ struct trx_instance; From eb3a1cde8c29f90044ba7a0a180f7c8dc835952f Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 9 Mar 2018 14:20:41 +0700 Subject: [PATCH 196/211] trxcon/l1ctl_link.c: allocate msgb after its length is read Change-Id: I2b941c5ed91097c4ed2d859634bbe89f44546061 --- src/host/trxcon/l1ctl_link.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/host/trxcon/l1ctl_link.c b/src/host/trxcon/l1ctl_link.c index 0fa3efe5f..20cb70ce2 100644 --- a/src/host/trxcon/l1ctl_link.c +++ b/src/host/trxcon/l1ctl_link.c @@ -75,19 +75,10 @@ static int l1ctl_link_read_cb(struct osmo_fd *bfd) uint16_t len; int rc; - /* Allocate a new msg */ - msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_HEADROOM, - L1CTL_HEADROOM, "l1ctl_rx_msg"); - if (!msg) { - LOGP(DL1D, LOGL_ERROR, "Failed to allocate msg\n"); - return -ENOMEM; - } - /* Attempt to read from socket */ rc = read(bfd->fd, &len, L1CTL_MSG_LEN_FIELD); if (rc < L1CTL_MSG_LEN_FIELD) { LOGP(DL1D, LOGL_NOTICE, "L1CTL has lost connection\n"); - msgb_free(msg); if (rc >= 0) rc = -EIO; l1ctl_link_close_conn(l1l); @@ -98,10 +89,17 @@ static int l1ctl_link_read_cb(struct osmo_fd *bfd) len = ntohs(len); if (len > L1CTL_LENGTH) { LOGP(DL1D, LOGL_ERROR, "Length is too big: %u\n", len); - msgb_free(msg); return -EINVAL; } + /* Allocate a new msg */ + msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_HEADROOM, + L1CTL_HEADROOM, "l1ctl_rx_msg"); + if (!msg) { + LOGP(DL1D, LOGL_ERROR, "Failed to allocate msg\n"); + return -ENOMEM; + } + msg->l1h = msgb_put(msg, len); rc = read(bfd->fd, msg->l1h, msgb_l1len(msg)); if (rc != len) { From d316b84413a4646b440ff5a5ab198a9f10aa9459 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 9 Mar 2018 14:26:20 +0700 Subject: [PATCH 197/211] trxcon/l1ctl.c: combine both DATA and TRAFFIC REQ handlers Both functions are almost identical, and the only difference is the message type they set. Let's combine them into a single function and introduce a 'traffic' flag, which can be used to define a message type. Change-Id: I288f5d7b6cd242c4793973dcb3d2b1b6925d61a7 --- src/host/trxcon/l1ctl.c | 62 +++++++++++------------------------------ 1 file changed, 17 insertions(+), 45 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index f138b88c8..0bcd5d995 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -606,25 +606,35 @@ static int l1ctl_rx_dm_rel_req(struct l1ctl_link *l1l, struct msgb *msg) return 0; } -static int l1ctl_rx_data_req(struct l1ctl_link *l1l, struct msgb *msg) +/** + * Handles both L1CTL_DATA_REQ and L1CTL_TRAFFIC_REQ. + */ +static int l1ctl_rx_dt_req(struct l1ctl_link *l1l, + struct msgb *msg, bool traffic) { struct l1ctl_info_ul *ul; struct trx_ts_prim *prim; uint8_t chan_nr, link_id; + size_t payload_len; int rc; /* Extract UL frame header */ ul = (struct l1ctl_info_ul *) msg->l1h; + /* Calculate the payload len */ + msg->l2h = ul->payload; + payload_len = msgb_l2len(msg); + /* Obtain channel description */ chan_nr = ul->chan_nr; link_id = ul->link_id & 0x40; - LOGP(DL1D, LOGL_DEBUG, "Recv Data Req (chan_nr=0x%02x, " - "link_id=0x%02x)\n", chan_nr, link_id); + LOGP(DL1D, LOGL_DEBUG, "Recv %s Req (chan_nr=0x%02x, " + "link_id=0x%02x, len=%zu)\n", traffic ? "TRAFFIC" : "DATA", + chan_nr, link_id, payload_len); /* Init a new primitive */ - rc = sched_prim_init(l1l->trx, &prim, 23, + rc = sched_prim_init(l1l->trx, &prim, payload_len, chan_nr, link_id); if (rc) goto exit; @@ -637,45 +647,7 @@ static int l1ctl_rx_data_req(struct l1ctl_link *l1l, struct msgb *msg) } /* Fill in the payload */ - memcpy(prim->payload, ul->payload, 23); - -exit: - msgb_free(msg); - return rc; -} - -static int l1ctl_rx_traffic_req(struct l1ctl_link *l1l, struct msgb *msg) -{ - struct l1ctl_info_ul *ul; - struct trx_ts_prim *prim; - uint8_t chan_nr, link_id; - int rc; - - /* Extract UL frame header */ - ul = (struct l1ctl_info_ul *) msg->l1h; - - /* Obtain channel description */ - chan_nr = ul->chan_nr; - link_id = ul->link_id & 0x40; - - LOGP(DL1D, LOGL_DEBUG, "Recv Traffic Req (chan_nr=0x%02x, " - "link_id=0x%02x)\n", chan_nr, link_id); - - /* Init a new primitive */ - rc = sched_prim_init(l1l->trx, &prim, TRAFFIC_DATA_LEN, - chan_nr, link_id); - if (rc) - goto exit; - - /* Push this primitive to transmit queue */ - rc = sched_prim_push(l1l->trx, prim, chan_nr); - if (rc) { - talloc_free(prim); - goto exit; - } - - /* Fill in the payload */ - memcpy(prim->payload, ul->payload, TRAFFIC_DATA_LEN); + memcpy(prim->payload, ul->payload, payload_len); exit: msgb_free(msg); @@ -811,9 +783,9 @@ int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) case L1CTL_DM_REL_REQ: return l1ctl_rx_dm_rel_req(l1l, msg); case L1CTL_DATA_REQ: - return l1ctl_rx_data_req(l1l, msg); + return l1ctl_rx_dt_req(l1l, msg, false); case L1CTL_TRAFFIC_REQ: - return l1ctl_rx_traffic_req(l1l, msg); + return l1ctl_rx_dt_req(l1l, msg, true); case L1CTL_PARAM_REQ: return l1ctl_rx_param_req(l1l, msg); case L1CTL_TCH_MODE_REQ: From ddddf9e0c488b51d08f6127e29a2d2e48e440095 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 9 Mar 2018 14:34:21 +0700 Subject: [PATCH 198/211] trxcon: clean up DATA / TRAFFIC confirmation API - change 'l1ctl_tx_data_conf' symbol to 'l1ctl_tx_dt_conf' in order to indicate that it's used for both DATA and TRAFFIC; - introduce a 'traffic' flag, which is used to define either TRAFFIC or DATA confirmation type; Change-Id: Iedd569086a264dc7d8740abea5c6e5ca21e299f6 --- src/host/trxcon/l1ctl.c | 16 ++++++++-------- src/host/trxcon/l1ctl.h | 4 ++-- src/host/trxcon/sched_lchan_common.c | 8 ++------ 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 0bcd5d995..3702e8a79 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -231,19 +231,19 @@ int l1ctl_tx_rach_conf(struct l1ctl_link *l1l, uint32_t fn) return l1ctl_link_send(l1l, msg); } -int l1ctl_tx_data_conf(struct l1ctl_link *l1l, - struct l1ctl_info_dl *data, uint8_t msg_type) + +/** + * Handles both L1CTL_DATA_CONF and L1CTL_TRAFFIC_CONF. + */ +int l1ctl_tx_dt_conf(struct l1ctl_link *l1l, + struct l1ctl_info_dl *data, bool traffic) { struct l1ctl_info_dl *dl; struct msgb *msg; size_t len; - if (msg_type != L1CTL_DATA_CONF && msg_type != L1CTL_TRAFFIC_CONF) { - LOGP(DL1D, LOGL_ERROR, "Incorrect confirmation type\n"); - return -EINVAL; - } - - msg = l1ctl_alloc_msg(msg_type); + msg = l1ctl_alloc_msg(traffic ? + L1CTL_TRAFFIC_CONF : L1CTL_DATA_CONF); if (msg == NULL) return -ENOMEM; diff --git a/src/host/trxcon/l1ctl.h b/src/host/trxcon/l1ctl.h index 91a7f0f61..290a0f526 100644 --- a/src/host/trxcon/l1ctl.h +++ b/src/host/trxcon/l1ctl.h @@ -20,6 +20,6 @@ int l1ctl_tx_reset_ind(struct l1ctl_link *l1l, uint8_t type); int l1ctl_tx_data_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data, uint8_t msg_type); -int l1ctl_tx_data_conf(struct l1ctl_link *l1l, - struct l1ctl_info_dl *data, uint8_t msg_type); +int l1ctl_tx_dt_conf(struct l1ctl_link *l1l, + struct l1ctl_info_dl *data, bool traffic); int l1ctl_tx_rach_conf(struct l1ctl_link *l1l, uint32_t fn); diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c index 52c9a21f3..d946e5779 100644 --- a/src/host/trxcon/sched_lchan_common.c +++ b/src/host/trxcon/sched_lchan_common.c @@ -129,7 +129,6 @@ int sched_send_data_conf(struct trx_instance *trx, struct trx_ts *ts, { const struct trx_lchan_desc *lchan_desc; struct l1ctl_info_dl *data; - uint8_t conf_type; /* Allocate memory */ data = talloc_zero(ts, struct l1ctl_info_dl); @@ -145,11 +144,8 @@ int sched_send_data_conf(struct trx_instance *trx, struct trx_ts *ts, data->band_arfcn = htons(trx->band_arfcn); data->frame_nr = htonl(fn); - /* Choose a confirmation type */ - conf_type = l2_len == GSM_MACBLOCK_LEN ? - L1CTL_DATA_CONF : L1CTL_TRAFFIC_CONF; - - l1ctl_tx_data_conf(trx->l1l, data, conf_type); + l1ctl_tx_dt_conf(trx->l1l, data, + l2_len != GSM_MACBLOCK_LEN); talloc_free(data); return 0; From 633c806a2bc25bac0abce6b48c5cf255cc99f8a1 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 9 Mar 2018 14:52:35 +0700 Subject: [PATCH 199/211] trxcon: clean up DATA / TRAFFIC indication API - change 'l1ctl_tx_data_ind' symbol to 'l1ctl_tx_dt_ind' in order to indicate that it's used for both DATA and TRAFFIC; - introduce a 'traffic' flag, which is used to define either TRAFFIC or DATA indication type; - pass L2 payload and its length separately from the Downlink info header. Change-Id: I9fe65ee9b2d772576b86b7bc85d53518530d1579 --- src/host/trxcon/l1ctl.c | 31 ++++++++++++++-------------- src/host/trxcon/l1ctl.h | 4 ++-- src/host/trxcon/sched_lchan_common.c | 15 +++++--------- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 3702e8a79..3de0cf686 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -183,29 +183,30 @@ int l1ctl_tx_ccch_mode_conf(struct l1ctl_link *l1l, uint8_t mode) return l1ctl_link_send(l1l, msg); } -int l1ctl_tx_data_ind(struct l1ctl_link *l1l, - struct l1ctl_info_dl *data, uint8_t msg_type) +/** + * Handles both L1CTL_DATA_IND and L1CTL_TRAFFIC_IND. + */ +int l1ctl_tx_dt_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data, + uint8_t *l2, size_t l2_len, bool traffic) { struct l1ctl_info_dl *dl; struct msgb *msg; - size_t len; + uint8_t *msg_l2; - if (msg_type != L1CTL_DATA_IND && msg_type != L1CTL_TRAFFIC_IND) { - LOGP(DL1D, LOGL_ERROR, "Incorrect indication type\n"); - return -EINVAL; - } - - msg = l1ctl_alloc_msg(msg_type); + msg = l1ctl_alloc_msg(traffic ? + L1CTL_TRAFFIC_IND : L1CTL_DATA_IND); if (msg == NULL) return -ENOMEM; - /* We store the payload as a flexible array member */ - len = sizeof(struct l1ctl_info_dl); - len += msg_type == L1CTL_DATA_IND ? 23 : TRAFFIC_DATA_LEN; - dl = (struct l1ctl_info_dl *) msgb_put(msg, len); + /* Copy DL header */ + dl = (struct l1ctl_info_dl *) msgb_put(msg, sizeof(*dl)); + memcpy(dl, data, sizeof(*dl)); - /* Copy header and data from source message */ - memcpy(dl, data, len); + /* Copy the L2 payload if preset */ + if (l2 && l2_len > 0) { + msg_l2 = (uint8_t *) msgb_put(msg, l2_len); + memcpy(msg_l2, l2, l2_len); + } /* Put message to upper layers */ return l1ctl_link_send(l1l, msg); diff --git a/src/host/trxcon/l1ctl.h b/src/host/trxcon/l1ctl.h index 290a0f526..ca8c0be63 100644 --- a/src/host/trxcon/l1ctl.h +++ b/src/host/trxcon/l1ctl.h @@ -18,8 +18,8 @@ int l1ctl_tx_pm_conf(struct l1ctl_link *l1l, uint16_t band_arfcn, int l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type); int l1ctl_tx_reset_ind(struct l1ctl_link *l1l, uint8_t type); -int l1ctl_tx_data_ind(struct l1ctl_link *l1l, - struct l1ctl_info_dl *data, uint8_t msg_type); +int l1ctl_tx_dt_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data, + uint8_t *l2, size_t l2_len, bool traffic); int l1ctl_tx_dt_conf(struct l1ctl_link *l1l, struct l1ctl_info_dl *data, bool traffic); int l1ctl_tx_rach_conf(struct l1ctl_link *l1l, uint32_t fn); diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c index d946e5779..13c876493 100644 --- a/src/host/trxcon/sched_lchan_common.c +++ b/src/host/trxcon/sched_lchan_common.c @@ -90,7 +90,7 @@ int sched_send_data_ind(struct trx_instance *trx, struct trx_ts *ts, struct l1ctl_info_dl *data; /* Allocate memory */ - data = talloc_zero_size(ts, sizeof(struct l1ctl_info_dl) + l2_len); + data = talloc_zero_size(ts, sizeof(struct l1ctl_info_dl)); if (data == NULL) return -ENOMEM; @@ -108,17 +108,12 @@ int sched_send_data_ind(struct trx_instance *trx, struct trx_ts *ts, /* FIXME: set proper values */ data->snr = 0; - if (dec_failed) { - /* Mark frame as broken */ - data->fire_crc = 2; - } else { - /* Fill in the payload */ - memcpy(data->payload, l2, l2_len); - } + /* Mark frame as broken if so */ + data->fire_crc = dec_failed ? 2 : 0; /* Put a packet to higher layers */ - l1ctl_tx_data_ind(trx->l1l, data, l2_len == GSM_MACBLOCK_LEN ? - L1CTL_DATA_IND : L1CTL_TRAFFIC_IND); + l1ctl_tx_dt_ind(trx->l1l, data, l2, l2_len, + l2_len != GSM_MACBLOCK_LEN); talloc_free(data); return 0; From caebbebd164d5cfc4d1625cf5c11f64e85078f7f Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 9 Mar 2018 15:07:17 +0700 Subject: [PATCH 200/211] trxcon/sched_lchan_common.c: use static memory allocation There is no need to allocate the DL header for each new message. Change-Id: Id7ad815c6b403f5c3d15fc02022397188f1d87fd --- src/host/trxcon/sched_lchan_common.c | 49 ++++++++++++---------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c index 13c876493..1858619b2 100644 --- a/src/host/trxcon/sched_lchan_common.c +++ b/src/host/trxcon/sched_lchan_common.c @@ -87,34 +87,28 @@ int sched_send_data_ind(struct trx_instance *trx, struct trx_ts *ts, bool dec_failed, int bit_error_count) { const struct trx_lchan_desc *lchan_desc; - struct l1ctl_info_dl *data; - - /* Allocate memory */ - data = talloc_zero_size(ts, sizeof(struct l1ctl_info_dl)); - if (data == NULL) - return -ENOMEM; + struct l1ctl_info_dl dl_hdr; /* Set up pointers */ lchan_desc = &trx_lchan_desc[lchan->type]; /* Fill in known downlink info */ - data->chan_nr = lchan_desc->chan_nr | ts->index; - data->link_id = lchan_desc->link_id; - data->band_arfcn = htons(trx->band_arfcn); - data->frame_nr = htonl(lchan->rx_first_fn); - data->rx_level = -(lchan->meas.rssi_sum / lchan->meas.rssi_num); - data->num_biterr = bit_error_count; + dl_hdr.chan_nr = lchan_desc->chan_nr | ts->index; + dl_hdr.link_id = lchan_desc->link_id; + dl_hdr.band_arfcn = htons(trx->band_arfcn); + dl_hdr.frame_nr = htonl(lchan->rx_first_fn); + dl_hdr.rx_level = -(lchan->meas.rssi_sum / lchan->meas.rssi_num); + dl_hdr.num_biterr = bit_error_count; /* FIXME: set proper values */ - data->snr = 0; + dl_hdr.snr = 0; /* Mark frame as broken if so */ - data->fire_crc = dec_failed ? 2 : 0; + dl_hdr.fire_crc = dec_failed ? 2 : 0; /* Put a packet to higher layers */ - l1ctl_tx_dt_ind(trx->l1l, data, l2, l2_len, + l1ctl_tx_dt_ind(trx->l1l, &dl_hdr, l2, l2_len, l2_len != GSM_MACBLOCK_LEN); - talloc_free(data); return 0; } @@ -123,25 +117,22 @@ int sched_send_data_conf(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, size_t l2_len) { const struct trx_lchan_desc *lchan_desc; - struct l1ctl_info_dl *data; - - /* Allocate memory */ - data = talloc_zero(ts, struct l1ctl_info_dl); - if (data == NULL) - return -ENOMEM; + struct l1ctl_info_dl dl_hdr; /* Set up pointers */ lchan_desc = &trx_lchan_desc[lchan->type]; - /* Fill in known downlink info */ - data->chan_nr = lchan_desc->chan_nr | ts->index; - data->link_id = lchan_desc->link_id; - data->band_arfcn = htons(trx->band_arfcn); - data->frame_nr = htonl(fn); + /* Zero-initialize DL header, because we don't set all fields */ + memset(&dl_hdr, 0x00, sizeof(struct l1ctl_info_dl)); - l1ctl_tx_dt_conf(trx->l1l, data, + /* Fill in known downlink info */ + dl_hdr.chan_nr = lchan_desc->chan_nr | ts->index; + dl_hdr.link_id = lchan_desc->link_id; + dl_hdr.band_arfcn = htons(trx->band_arfcn); + dl_hdr.frame_nr = htonl(fn); + + l1ctl_tx_dt_conf(trx->l1l, &dl_hdr, l2_len != GSM_MACBLOCK_LEN); - talloc_free(data); return 0; } From 47aaf962fbd358f5b66a2d349160f25c521e6393 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 9 Mar 2018 15:24:20 +0700 Subject: [PATCH 201/211] trxcon/scheduler: enforce lchan handlers to set message type Since this change, each lchan handler shall manually indicate a type of both message indications and confirmations. Change-Id: I02e0b87d61c127d2f6f5b9532909af78332bf707 --- src/host/trxcon/sched_lchan_common.c | 14 ++++++-------- src/host/trxcon/sched_lchan_tchf.c | 11 +++++------ src/host/trxcon/sched_lchan_xcch.c | 6 +++--- src/host/trxcon/sched_trx.h | 8 ++++---- 4 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c index 1858619b2..47b01621b 100644 --- a/src/host/trxcon/sched_lchan_common.c +++ b/src/host/trxcon/sched_lchan_common.c @@ -82,9 +82,9 @@ const uint8_t sched_nb_training_bits[8][26] = { }, }; -int sched_send_data_ind(struct trx_instance *trx, struct trx_ts *ts, +int sched_send_dt_ind(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len, - bool dec_failed, int bit_error_count) + int bit_error_count, bool dec_failed, bool traffic) { const struct trx_lchan_desc *lchan_desc; struct l1ctl_info_dl dl_hdr; @@ -107,14 +107,13 @@ int sched_send_data_ind(struct trx_instance *trx, struct trx_ts *ts, dl_hdr.fire_crc = dec_failed ? 2 : 0; /* Put a packet to higher layers */ - l1ctl_tx_dt_ind(trx->l1l, &dl_hdr, l2, l2_len, - l2_len != GSM_MACBLOCK_LEN); + l1ctl_tx_dt_ind(trx->l1l, &dl_hdr, l2, l2_len, traffic); return 0; } -int sched_send_data_conf(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, size_t l2_len) +int sched_send_dt_conf(struct trx_instance *trx, struct trx_ts *ts, + struct trx_lchan_state *lchan, uint32_t fn, bool traffic) { const struct trx_lchan_desc *lchan_desc; struct l1ctl_info_dl dl_hdr; @@ -131,8 +130,7 @@ int sched_send_data_conf(struct trx_instance *trx, struct trx_ts *ts, dl_hdr.band_arfcn = htons(trx->band_arfcn); dl_hdr.frame_nr = htonl(fn); - l1ctl_tx_dt_conf(trx->l1l, &dl_hdr, - l2_len != GSM_MACBLOCK_LEN); + l1ctl_tx_dt_conf(trx->l1l, &dl_hdr, traffic); return 0; } diff --git a/src/host/trxcon/sched_lchan_tchf.c b/src/host/trxcon/sched_lchan_tchf.c index b023126a6..25c9b10b6 100644 --- a/src/host/trxcon/sched_lchan_tchf.c +++ b/src/host/trxcon/sched_lchan_tchf.c @@ -147,8 +147,8 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, l2_len = sched_bad_frame_ind(l2, rsl_cmode, tch_mode); } else if (rc == GSM_MACBLOCK_LEN) { /* FACCH received, forward it to the higher layers */ - sched_send_data_ind(trx, ts, lchan, - l2, GSM_MACBLOCK_LEN, false, n_errors); + sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN, + n_errors, false, false); /* Send BFI instead of stolen TCH frame */ l2_len = sched_bad_frame_ind(l2, rsl_cmode, tch_mode); @@ -159,8 +159,8 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, /* Send a traffic frame to the higher layers */ if (l2_len > 0) - sched_send_data_ind(trx, ts, lchan, - l2, l2_len, false, n_errors); + sched_send_dt_ind(trx, ts, lchan, l2, l2_len, + n_errors, rc < 4, true); return 0; } @@ -273,8 +273,7 @@ send_burst: /* If we have sent the last (4/4) burst */ if (*mask == 0x0f) { /* Confirm data / traffic sending */ - sched_send_data_conf(trx, ts, lchan, fn, - lchan->prim->payload_len); + sched_send_dt_conf(trx, ts, lchan, fn, PRIM_IS_TCH(lchan->prim)); /* Forget processed primitive */ sched_prim_drop(lchan); diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index b2cbd2239..f35167419 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -109,8 +109,8 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, } /* Send a L2 frame to the higher layers */ - sched_send_data_ind(trx, ts, lchan, - l2, GSM_MACBLOCK_LEN, rc != 0, n_errors); + sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN, + n_errors, rc != 0, false); return 0; } @@ -190,7 +190,7 @@ send_burst: *mask = 0x00; /* Confirm data sending */ - sched_send_data_conf(trx, ts, lchan, fn, GSM_MACBLOCK_LEN); + sched_send_dt_conf(trx, ts, lchan, fn, false); } return 0; diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index d9e0c8ed4..80b8dd29b 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -312,8 +312,8 @@ int sched_trx_handle_tx_burst(struct trx_instance *trx, extern const uint8_t sched_nb_training_bits[8][26]; size_t sched_bad_frame_ind(uint8_t *l2, uint8_t rsl_cmode, uint8_t tch_mode); -int sched_send_data_ind(struct trx_instance *trx, struct trx_ts *ts, +int sched_send_dt_ind(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len, - bool dec_failed, int bit_error_count); -int sched_send_data_conf(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, size_t l2_len); + int bit_error_count, bool dec_failed, bool traffic); +int sched_send_dt_conf(struct trx_instance *trx, struct trx_ts *ts, + struct trx_lchan_state *lchan, uint32_t fn, bool traffic); From 40e71126abc26eb4d3deea580ca8c8f1c6c8c73b Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 9 Mar 2018 17:09:44 +0700 Subject: [PATCH 202/211] trxcon/sched_lchan_xcch.c: always send data indications We shall always send data frame indications, even if received frame is incomplete or decoding was failed. This is required for proper Measurement Reporting. Change-Id: I7beee7e797f488d04c3b59bee9501ce823717092 --- src/host/trxcon/sched_lchan_xcch.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c index f35167419..04c9f4ec2 100644 --- a/src/host/trxcon/sched_lchan_xcch.c +++ b/src/host/trxcon/sched_lchan_xcch.c @@ -94,8 +94,6 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, (*first_fn) % ts->mf_layout->period, ts->mf_layout->period, lchan_desc->name); - - return -1; } /* Attempt to decode */ @@ -106,13 +104,18 @@ int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, (*first_fn) % ts->mf_layout->period, ts->mf_layout->period, lchan_desc->name); + + /** + * We should anyway send dummy frame for + * proper measurement reporting... + */ + return sched_send_dt_ind(trx, ts, lchan, NULL, 0, + n_errors, true, false); } /* Send a L2 frame to the higher layers */ - sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN, - n_errors, rc != 0, false); - - return 0; + return sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN, + n_errors, false, false); } int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts, From 4ccb2261b1ac2e207303393fe509878f160dd96b Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 9 Mar 2018 17:28:57 +0700 Subject: [PATCH 203/211] trxcon/sched_lchan_tchf.c: always send traffic indications We shall always send traffic frame indications, even if received frame is incomplete or decoding was failed. This is required for proper Measurement Reporting. Change-Id: I99e134699796c7075299459e96b2f2d462636619 --- src/host/trxcon/sched_lchan_tchf.c | 46 +++++++++++++++++++----------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/src/host/trxcon/sched_lchan_tchf.c b/src/host/trxcon/sched_lchan_tchf.c index 25c9b10b6..80e4d52fd 100644 --- a/src/host/trxcon/sched_lchan_tchf.c +++ b/src/host/trxcon/sched_lchan_tchf.c @@ -49,8 +49,8 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, sbit_t *bits, int8_t rssi, int16_t toa256) { const struct trx_lchan_desc *lchan_desc; + int n_errors = -1, n_bits_total, rc; uint8_t rsl_cmode, tch_mode, mode; - int n_errors, n_bits_total, rc; sbit_t *buffer, *offset; uint8_t l2[128], *mask; uint32_t *first_fn; @@ -92,16 +92,6 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, if (bid != 3) return 0; - /* Check for complete set of bursts */ - if ((*mask & 0xf) != 0xf) { - LOGP(DSCHD, LOGL_ERROR, "Received incomplete traffic frame at " - "fn=%u (%u/%u) for %s\n", *first_fn, - (*first_fn) % ts->mf_layout->period, - ts->mf_layout->period, - lchan_desc->name); - return -EINVAL; - } - /** * Get current RSL / TCH modes * @@ -111,6 +101,18 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, rsl_cmode = RSL_CMOD_SPD_SPEECH; tch_mode = lchan->tch_mode; + /* Check for complete set of bursts */ + if ((*mask & 0xf) != 0xf) { + LOGP(DSCHD, LOGL_ERROR, "Received incomplete traffic frame at " + "fn=%u (%u/%u) for %s\n", *first_fn, + (*first_fn) % ts->mf_layout->period, + ts->mf_layout->period, + lchan_desc->name); + + /* Send BFI */ + goto bfi; + } + mode = rsl_cmode != RSL_CMOD_SPD_SPEECH ? GSM48_CMODE_SPEECH_V1 : tch_mode; @@ -144,25 +146,35 @@ int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, LOGP(DSCHD, LOGL_ERROR, "Received bad TCH frame ending at " "fn=%u for %s\n", fn, lchan_desc->name); - l2_len = sched_bad_frame_ind(l2, rsl_cmode, tch_mode); + /* Send BFI */ + goto bfi; } else if (rc == GSM_MACBLOCK_LEN) { /* FACCH received, forward it to the higher layers */ sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN, n_errors, false, false); /* Send BFI instead of stolen TCH frame */ - l2_len = sched_bad_frame_ind(l2, rsl_cmode, tch_mode); + goto bfi; } else { /* A good TCH frame received */ l2_len = rc; } /* Send a traffic frame to the higher layers */ - if (l2_len > 0) - sched_send_dt_ind(trx, ts, lchan, l2, l2_len, - n_errors, rc < 4, true); + return sched_send_dt_ind(trx, ts, lchan, l2, l2_len, + n_errors, false, true); - return 0; +bfi: + /* Bad frame indication */ + l2_len = sched_bad_frame_ind(l2, rsl_cmode, tch_mode); + + /* Didn't try to decode */ + if (n_errors < 0) + n_errors = 116 * 4; + + /* Send a BFI frame to the higher layers */ + return sched_send_dt_ind(trx, ts, lchan, l2, l2_len, + n_errors, true, true); } int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, From c08ddc73838b08f9538a966d6ad8f9f8bc856cf9 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 12 Mar 2018 20:48:24 +0700 Subject: [PATCH 204/211] fake_trx: unify the GPL license header There is no need to manually put the license header as a variable in each application in order to print it. Let's use a common one. Change-Id: I1a6e8716a9069e7ade3ae15f2c04fd45d18e223c --- src/target/fake_trx/burst_gen.py | 15 ++++----------- src/target/fake_trx/burst_send.py | 15 ++++----------- src/target/fake_trx/clck_gen.py | 18 +++++++----------- src/target/fake_trx/copyright.py | 13 +++++++++++++ src/target/fake_trx/ctrl_cmd.py | 12 ++++-------- src/target/fake_trx/fake_trx.py | 17 +++++------------ src/target/fake_trx/trx_sniff.py | 12 ++++-------- 7 files changed, 41 insertions(+), 61 deletions(-) create mode 100644 src/target/fake_trx/copyright.py diff --git a/src/target/fake_trx/burst_gen.py b/src/target/fake_trx/burst_gen.py index 179dbcda8..6a034cd57 100755 --- a/src/target/fake_trx/burst_gen.py +++ b/src/target/fake_trx/burst_gen.py @@ -22,6 +22,9 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +from copyright import print_copyright +CR_HOLDERS = [("2017-2018", "Vadim Yanitskiy ")] + import signal import getopt import sys @@ -32,13 +35,6 @@ from data_if import DATAInterface from gsm_shared import * from data_msg import * -COPYRIGHT = \ - "Copyright (C) 2017 by Vadim Yanitskiy \n" \ - "License GPLv2+: GNU GPL version 2 or later " \ - "\n" \ - "This is free software: you are free to change and redistribute it.\n" \ - "There is NO WARRANTY, to the extent permitted by law.\n" - class Application: # Application variables remote_addr = "127.0.0.1" @@ -59,7 +55,7 @@ class Application: pwr = None def __init__(self): - self.print_copyright() + print_copyright(CR_HOLDERS) self.parse_argv() self.check_argv() @@ -143,9 +139,6 @@ class Application: if self.output_file is not None: self.ddf.append_msg(msg) - def print_copyright(self): - print(COPYRIGHT) - def print_help(self, msg = None): s = " Usage: " + sys.argv[0] + " [options]\n\n" \ " Some help...\n" \ diff --git a/src/target/fake_trx/burst_send.py b/src/target/fake_trx/burst_send.py index 882fcd649..c588b3dfd 100755 --- a/src/target/fake_trx/burst_send.py +++ b/src/target/fake_trx/burst_send.py @@ -21,6 +21,9 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +from copyright import print_copyright +CR_HOLDERS = [("2017-2018", "Vadim Yanitskiy ")] + import signal import getopt import sys @@ -30,13 +33,6 @@ from data_if import DATAInterface from gsm_shared import * from data_msg import * -COPYRIGHT = \ - "Copyright (C) 2017-2018 by Vadim Yanitskiy \n" \ - "License GPLv2+: GNU GPL version 2 or later " \ - "\n" \ - "This is free software: you are free to change and redistribute it.\n" \ - "There is NO WARRANTY, to the extent permitted by law.\n" - class Application: # Application variables remote_addr = "127.0.0.1" @@ -56,7 +52,7 @@ class Application: pf_tn = None def __init__(self): - self.print_copyright() + print_copyright(CR_HOLDERS) self.parse_argv() # Set up signal handlers @@ -119,9 +115,6 @@ class Application: # Burst passed ;) return True - def print_copyright(self): - print(COPYRIGHT) - def print_help(self, msg = None): s = " Usage: " + sys.argv[0] + " [options]\n\n" \ " Some help...\n" \ diff --git a/src/target/fake_trx/clck_gen.py b/src/target/fake_trx/clck_gen.py index e7e58a3bf..83c0a64d7 100755 --- a/src/target/fake_trx/clck_gen.py +++ b/src/target/fake_trx/clck_gen.py @@ -4,7 +4,7 @@ # Virtual Um-interface (fake transceiver) # Simple TDMA frame clock generator # -# (C) 2017 by Vadim Yanitskiy +# (C) 2017-2018 by Vadim Yanitskiy # # All Rights Reserved # @@ -22,6 +22,9 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +from copyright import print_copyright +CR_HOLDERS = [("2017-2018", "Vadim Yanitskiy ")] + import signal import time import sys @@ -30,13 +33,6 @@ from threading import Timer from udp_link import UDPLink from gsm_shared import * -COPYRIGHT = \ - "Copyright (C) 2017 by Vadim Yanitskiy \n" \ - "License GPLv2+: GNU GPL version 2 or later " \ - "\n" \ - "This is free software: you are free to change and redistribute it.\n" \ - "There is NO WARRANTY, to the extent permitted by law.\n" - class CLCKGen: # GSM TDMA definitions SEC_DELAY_US = 1000 * 1000 @@ -99,12 +95,12 @@ class CLCKGen: # Just a wrapper for independent usage class Application: def __init__(self): + # Print copyright + print_copyright(CR_HOLDERS) + # Set up signal handlers signal.signal(signal.SIGINT, self.sig_handler) - # Print copyright - print(COPYRIGHT) - def run(self): self.link = UDPLink("127.0.0.1", 5800, 5700) self.clck = CLCKGen([self.link], ind_period = 51) diff --git a/src/target/fake_trx/copyright.py b/src/target/fake_trx/copyright.py new file mode 100644 index 000000000..3d3597fd5 --- /dev/null +++ b/src/target/fake_trx/copyright.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +def print_copyright(holders = []): + # Print copyright holders if any + for date, author in holders: + print("Copyright (C) %s by %s" % (date, author)) + + # Print the license header itself + print("License GPLv2+: GNU GPL version 2 or later " \ + "\n" \ + "This is free software: you are free to change and redistribute it.\n" \ + "There is NO WARRANTY, to the extent permitted by law.\n") diff --git a/src/target/fake_trx/ctrl_cmd.py b/src/target/fake_trx/ctrl_cmd.py index 83baa1971..3526eaa51 100755 --- a/src/target/fake_trx/ctrl_cmd.py +++ b/src/target/fake_trx/ctrl_cmd.py @@ -22,6 +22,9 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +from copyright import print_copyright +CR_HOLDERS = [("2017-2018", "Vadim Yanitskiy ")] + import signal import getopt import select @@ -29,13 +32,6 @@ import sys from udp_link import UDPLink -COPYRIGHT = \ - "Copyright (C) 2017-2018 by Vadim Yanitskiy \n" \ - "License GPLv2+: GNU GPL version 2 or later " \ - "\n" \ - "This is free software: you are free to change and redistribute it.\n" \ - "There is NO WARRANTY, to the extent permitted by law.\n" - class Application: # Application variables remote_addr = "127.0.0.1" @@ -44,7 +40,7 @@ class Application: fuzzing = False def __init__(self): - print(COPYRIGHT) + print_copyright(CR_HOLDERS) self.parse_argv() # Set up signal handlers diff --git a/src/target/fake_trx/fake_trx.py b/src/target/fake_trx/fake_trx.py index a0534fd2a..4f0f28283 100755 --- a/src/target/fake_trx/fake_trx.py +++ b/src/target/fake_trx/fake_trx.py @@ -4,7 +4,7 @@ # Virtual Um-interface (fake transceiver) # OsmocomBB <-> OsmoBTS bridge # -# (C) 2017 by Vadim Yanitskiy +# (C) 2017-2018 by Vadim Yanitskiy # # All Rights Reserved # @@ -22,6 +22,9 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +from copyright import print_copyright +CR_HOLDERS = [("2017-2018", "Vadim Yanitskiy ")] + import signal import getopt import select @@ -35,13 +38,6 @@ from fake_pm import FakePM from udp_link import UDPLink from clck_gen import CLCKGen -COPYRIGHT = \ - "Copyright (C) 2017 by Vadim Yanitskiy \n" \ - "License GPLv2+: GNU GPL version 2 or later " \ - "\n" \ - "This is free software: you are free to change and redistribute it.\n" \ - "There is NO WARRANTY, to the extent permitted by law.\n" - class Application: # Application variables bts_addr = "127.0.0.1" @@ -56,7 +52,7 @@ class Application: randomize_ul_rssi = False def __init__(self): - self.print_copyright() + print_copyright(CR_HOLDERS) self.parse_argv() # Set up signal handlers @@ -137,9 +133,6 @@ class Application: # Stop clock generator self.clck_gen.stop() - def print_copyright(self): - print(COPYRIGHT) - def print_help(self, msg = None): s = " Usage: " + sys.argv[0] + " [options]\n\n" \ " Some help...\n" \ diff --git a/src/target/fake_trx/trx_sniff.py b/src/target/fake_trx/trx_sniff.py index f59e609b0..fe1ddb093 100755 --- a/src/target/fake_trx/trx_sniff.py +++ b/src/target/fake_trx/trx_sniff.py @@ -21,6 +21,9 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +from copyright import print_copyright +CR_HOLDERS = [("2018", "Vadim Yanitskiy ")] + import signal import getopt import sys @@ -30,13 +33,6 @@ import scapy.all from data_dump import DATADumpFile from data_msg import * -COPYRIGHT = \ - "Copyright (C) 2018 by Vadim Yanitskiy \n" \ - "License GPLv2+: GNU GPL version 2 or later " \ - "\n" \ - "This is free software: you are free to change and redistribute it.\n" \ - "There is NO WARRANTY, to the extent permitted by law.\n" - class Application: # Application variables sniff_interface = "lo" @@ -67,7 +63,7 @@ class Application: lo_trigger = False def __init__(self): - print(COPYRIGHT) + print_copyright(CR_HOLDERS) self.parse_argv() # Open requested capture file From 23914b9cf855de8e847d450c70494626da2216e6 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 13 Mar 2018 01:09:56 +0700 Subject: [PATCH 205/211] Rename 'fake_trx' to 'trx_toolkit' This toolkit has branched out into several different tools for TRX interface hacking, and creating a virtual Um-interface (FakeTRX) is only one of its potential applications. Change-Id: I56bcbc76b9c273d6b469a2bb68ddc46f3980e835 --- src/target/{fake_trx => trx_toolkit}/.gitignore | 0 src/target/{fake_trx => trx_toolkit}/README | 11 +++-------- src/target/{fake_trx => trx_toolkit}/burst_fwd.py | 2 +- src/target/{fake_trx => trx_toolkit}/burst_gen.py | 1 + src/target/{fake_trx => trx_toolkit}/burst_send.py | 1 + src/target/{fake_trx => trx_toolkit}/clck_gen.py | 2 +- src/target/{fake_trx => trx_toolkit}/copyright.py | 0 src/target/{fake_trx => trx_toolkit}/ctrl_cmd.py | 1 + src/target/{fake_trx => trx_toolkit}/ctrl_if.py | 2 +- src/target/{fake_trx => trx_toolkit}/ctrl_if_bb.py | 2 +- src/target/{fake_trx => trx_toolkit}/ctrl_if_bts.py | 2 +- src/target/{fake_trx => trx_toolkit}/data_dump.py | 0 src/target/{fake_trx => trx_toolkit}/data_if.py | 2 +- src/target/{fake_trx => trx_toolkit}/data_msg.py | 2 +- src/target/{fake_trx => trx_toolkit}/fake_pm.py | 2 +- src/target/{fake_trx => trx_toolkit}/fake_trx.py | 2 +- src/target/{fake_trx => trx_toolkit}/gsm_shared.py | 2 +- .../{fake_trx => trx_toolkit}/rand_burst_gen.py | 2 +- src/target/{fake_trx => trx_toolkit}/trx_sniff.py | 1 + src/target/{fake_trx => trx_toolkit}/udp_link.py | 2 +- 20 files changed, 19 insertions(+), 20 deletions(-) rename src/target/{fake_trx => trx_toolkit}/.gitignore (100%) rename src/target/{fake_trx => trx_toolkit}/README (71%) rename src/target/{fake_trx => trx_toolkit}/burst_fwd.py (99%) rename src/target/{fake_trx => trx_toolkit}/burst_gen.py (99%) rename src/target/{fake_trx => trx_toolkit}/burst_send.py (99%) rename src/target/{fake_trx => trx_toolkit}/clck_gen.py (98%) rename src/target/{fake_trx => trx_toolkit}/copyright.py (100%) rename src/target/{fake_trx => trx_toolkit}/ctrl_cmd.py (99%) rename src/target/{fake_trx => trx_toolkit}/ctrl_if.py (98%) rename src/target/{fake_trx => trx_toolkit}/ctrl_if_bb.py (99%) rename src/target/{fake_trx => trx_toolkit}/ctrl_if_bts.py (98%) rename src/target/{fake_trx => trx_toolkit}/data_dump.py (100%) rename src/target/{fake_trx => trx_toolkit}/data_if.py (96%) rename src/target/{fake_trx => trx_toolkit}/data_msg.py (99%) rename src/target/{fake_trx => trx_toolkit}/fake_pm.py (97%) rename src/target/{fake_trx => trx_toolkit}/fake_trx.py (99%) rename src/target/{fake_trx => trx_toolkit}/gsm_shared.py (96%) rename src/target/{fake_trx => trx_toolkit}/rand_burst_gen.py (98%) rename src/target/{fake_trx => trx_toolkit}/trx_sniff.py (99%) rename src/target/{fake_trx => trx_toolkit}/udp_link.py (97%) diff --git a/src/target/fake_trx/.gitignore b/src/target/trx_toolkit/.gitignore similarity index 100% rename from src/target/fake_trx/.gitignore rename to src/target/trx_toolkit/.gitignore diff --git a/src/target/fake_trx/README b/src/target/trx_toolkit/README similarity index 71% rename from src/target/fake_trx/README rename to src/target/trx_toolkit/README index 7b27414bf..91b6099b5 100644 --- a/src/target/fake_trx/README +++ b/src/target/trx_toolkit/README @@ -1,11 +1,6 @@ -This is a set of tools for creating a virtual Um-interface between -OsmocomBB and OsmoBTS. It may be extremely useful for testing and -development of GSM stack, including both sides (MS and BTS). This -software implements OsmoTRX (Osmocom's fork of OpenBTS transceiver) -style clock (CLCK), control (CTRL) and data interfaces. So, OsmoBTS -source code doesn't require any modifications, while for OsmocomBB -you will need to use a new application - trxcon, which can be found -in the 'fixeria/trx' branch until one is merged to master. +TRX toolkit is a set of tools intended for hacking and debugging +a TRX interface between both transceiver and L1 software, and +emulating a virtual Um-interface between OsmocomBB and OsmoBTS. Brief description of available applications: diff --git a/src/target/fake_trx/burst_fwd.py b/src/target/trx_toolkit/burst_fwd.py similarity index 99% rename from src/target/fake_trx/burst_fwd.py rename to src/target/trx_toolkit/burst_fwd.py index 9f0f84f1d..144ae5f46 100644 --- a/src/target/fake_trx/burst_fwd.py +++ b/src/target/trx_toolkit/burst_fwd.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -# Virtual Um-interface (fake transceiver) +# TRX Toolkit # BTS <-> BB burst forwarding # # (C) 2017 by Vadim Yanitskiy diff --git a/src/target/fake_trx/burst_gen.py b/src/target/trx_toolkit/burst_gen.py similarity index 99% rename from src/target/fake_trx/burst_gen.py rename to src/target/trx_toolkit/burst_gen.py index 6a034cd57..4bc036ea3 100755 --- a/src/target/fake_trx/burst_gen.py +++ b/src/target/trx_toolkit/burst_gen.py @@ -1,6 +1,7 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- +# TRX Toolkit # Auxiliary tool to generate and send random bursts via TRX DATA # interface, which may be useful for fuzzing and testing # diff --git a/src/target/fake_trx/burst_send.py b/src/target/trx_toolkit/burst_send.py similarity index 99% rename from src/target/fake_trx/burst_send.py rename to src/target/trx_toolkit/burst_send.py index c588b3dfd..7405c9019 100755 --- a/src/target/fake_trx/burst_send.py +++ b/src/target/trx_toolkit/burst_send.py @@ -1,6 +1,7 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- +# TRX Toolkit # Auxiliary tool to send existing bursts via TRX DATA interface # # (C) 2017-2018 by Vadim Yanitskiy diff --git a/src/target/fake_trx/clck_gen.py b/src/target/trx_toolkit/clck_gen.py similarity index 98% rename from src/target/fake_trx/clck_gen.py rename to src/target/trx_toolkit/clck_gen.py index 83c0a64d7..4b86c31a9 100755 --- a/src/target/fake_trx/clck_gen.py +++ b/src/target/trx_toolkit/clck_gen.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -# Virtual Um-interface (fake transceiver) +# TRX Toolkit # Simple TDMA frame clock generator # # (C) 2017-2018 by Vadim Yanitskiy diff --git a/src/target/fake_trx/copyright.py b/src/target/trx_toolkit/copyright.py similarity index 100% rename from src/target/fake_trx/copyright.py rename to src/target/trx_toolkit/copyright.py diff --git a/src/target/fake_trx/ctrl_cmd.py b/src/target/trx_toolkit/ctrl_cmd.py similarity index 99% rename from src/target/fake_trx/ctrl_cmd.py rename to src/target/trx_toolkit/ctrl_cmd.py index 3526eaa51..0791f85ce 100755 --- a/src/target/fake_trx/ctrl_cmd.py +++ b/src/target/trx_toolkit/ctrl_cmd.py @@ -1,6 +1,7 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- +# TRX Toolkit # Auxiliary tool to send custom commands via TRX CTRL interface, # which may be useful for testing and fuzzing # diff --git a/src/target/fake_trx/ctrl_if.py b/src/target/trx_toolkit/ctrl_if.py similarity index 98% rename from src/target/fake_trx/ctrl_if.py rename to src/target/trx_toolkit/ctrl_if.py index af124f65d..1e569a608 100644 --- a/src/target/fake_trx/ctrl_if.py +++ b/src/target/trx_toolkit/ctrl_if.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -# Virtual Um-interface (fake transceiver) +# TRX Toolkit # CTRL interface implementation # # (C) 2016-2017 by Vadim Yanitskiy diff --git a/src/target/fake_trx/ctrl_if_bb.py b/src/target/trx_toolkit/ctrl_if_bb.py similarity index 99% rename from src/target/fake_trx/ctrl_if_bb.py rename to src/target/trx_toolkit/ctrl_if_bb.py index 33263e5ae..a1ccd0d17 100644 --- a/src/target/fake_trx/ctrl_if_bb.py +++ b/src/target/trx_toolkit/ctrl_if_bb.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -# Virtual Um-interface (fake transceiver) +# TRX Toolkit # CTRL interface implementation (OsmocomBB specific) # # (C) 2016-2017 by Vadim Yanitskiy diff --git a/src/target/fake_trx/ctrl_if_bts.py b/src/target/trx_toolkit/ctrl_if_bts.py similarity index 98% rename from src/target/fake_trx/ctrl_if_bts.py rename to src/target/trx_toolkit/ctrl_if_bts.py index 449df4585..231641a63 100644 --- a/src/target/fake_trx/ctrl_if_bts.py +++ b/src/target/trx_toolkit/ctrl_if_bts.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -# Virtual Um-interface (fake transceiver) +# TRX Toolkit # CTRL interface implementation (OsmoBTS specific) # # (C) 2016-2017 by Vadim Yanitskiy diff --git a/src/target/fake_trx/data_dump.py b/src/target/trx_toolkit/data_dump.py similarity index 100% rename from src/target/fake_trx/data_dump.py rename to src/target/trx_toolkit/data_dump.py diff --git a/src/target/fake_trx/data_if.py b/src/target/trx_toolkit/data_if.py similarity index 96% rename from src/target/fake_trx/data_if.py rename to src/target/trx_toolkit/data_if.py index 8b0cd8e56..f4431a46c 100644 --- a/src/target/fake_trx/data_if.py +++ b/src/target/trx_toolkit/data_if.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -# Virtual Um-interface (fake transceiver) +# TRX Toolkit # DATA interface implementation # # (C) 2017-2018 by Vadim Yanitskiy diff --git a/src/target/fake_trx/data_msg.py b/src/target/trx_toolkit/data_msg.py similarity index 99% rename from src/target/fake_trx/data_msg.py rename to src/target/trx_toolkit/data_msg.py index a48eff7cc..ea415ab96 100644 --- a/src/target/fake_trx/data_msg.py +++ b/src/target/trx_toolkit/data_msg.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -# Virtual Um-interface (fake transceiver) +# TRX Toolkit # DATA interface message definitions and helpers # # (C) 2018 by Vadim Yanitskiy diff --git a/src/target/fake_trx/fake_pm.py b/src/target/trx_toolkit/fake_pm.py similarity index 97% rename from src/target/fake_trx/fake_pm.py rename to src/target/trx_toolkit/fake_pm.py index 1d7691650..840b4e407 100644 --- a/src/target/fake_trx/fake_pm.py +++ b/src/target/trx_toolkit/fake_pm.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -# Virtual Um-interface (fake transceiver) +# TRX Toolkit # Power measurement emulation for BB # # (C) 2017 by Vadim Yanitskiy diff --git a/src/target/fake_trx/fake_trx.py b/src/target/trx_toolkit/fake_trx.py similarity index 99% rename from src/target/fake_trx/fake_trx.py rename to src/target/trx_toolkit/fake_trx.py index 4f0f28283..1fcd3df9b 100755 --- a/src/target/fake_trx/fake_trx.py +++ b/src/target/trx_toolkit/fake_trx.py @@ -1,8 +1,8 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- +# TRX Toolkit # Virtual Um-interface (fake transceiver) -# OsmocomBB <-> OsmoBTS bridge # # (C) 2017-2018 by Vadim Yanitskiy # diff --git a/src/target/fake_trx/gsm_shared.py b/src/target/trx_toolkit/gsm_shared.py similarity index 96% rename from src/target/fake_trx/gsm_shared.py rename to src/target/trx_toolkit/gsm_shared.py index cfe94b010..d2f8278b6 100644 --- a/src/target/fake_trx/gsm_shared.py +++ b/src/target/trx_toolkit/gsm_shared.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -# Virtual Um-interface (fake transceiver) +# TRX Toolkit # Common GSM constants # # (C) 2018 by Vadim Yanitskiy diff --git a/src/target/fake_trx/rand_burst_gen.py b/src/target/trx_toolkit/rand_burst_gen.py similarity index 98% rename from src/target/fake_trx/rand_burst_gen.py rename to src/target/trx_toolkit/rand_burst_gen.py index 33e7c53a4..46c1e090b 100644 --- a/src/target/fake_trx/rand_burst_gen.py +++ b/src/target/trx_toolkit/rand_burst_gen.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -# Virtual Um-interface (fake transceiver) +# TRX Toolkit # Random burst (NB, FB, SB, AB) generator # # (C) 2017 by Vadim Yanitskiy diff --git a/src/target/fake_trx/trx_sniff.py b/src/target/trx_toolkit/trx_sniff.py similarity index 99% rename from src/target/fake_trx/trx_sniff.py rename to src/target/trx_toolkit/trx_sniff.py index fe1ddb093..577e6f97e 100755 --- a/src/target/fake_trx/trx_sniff.py +++ b/src/target/trx_toolkit/trx_sniff.py @@ -1,6 +1,7 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- +# TRX Toolkit # Scapy-based TRX interface sniffer # # (C) 2018 by Vadim Yanitskiy diff --git a/src/target/fake_trx/udp_link.py b/src/target/trx_toolkit/udp_link.py similarity index 97% rename from src/target/fake_trx/udp_link.py rename to src/target/trx_toolkit/udp_link.py index 99da95bb5..56d9c37c8 100644 --- a/src/target/fake_trx/udp_link.py +++ b/src/target/trx_toolkit/udp_link.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -# Virtual Um-interface (fake transceiver) +# TRX Toolkit # UDP link implementation # # (C) 2017 by Vadim Yanitskiy From 02abbe542087e761dd0d5cae6eebe32c785cefb3 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 22 Mar 2018 20:33:04 +0700 Subject: [PATCH 206/211] trxcon/sched_prim.c: fix: correct the first padding byte According to TS 144.006, section 5.2, the first octet containing fill bits shall be set to the binary value "00101011" == 0x2b. Change-Id: I8f0304bf84613a2dc07cb78aff0cb8bb4c5adf6c --- src/host/trxcon/sched_prim.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/host/trxcon/sched_prim.c b/src/host/trxcon/sched_prim.c index c17fb2a41..e1c87bbf2 100644 --- a/src/host/trxcon/sched_prim.c +++ b/src/host/trxcon/sched_prim.c @@ -251,7 +251,7 @@ int sched_prim_dummy(struct trx_lchan_state *lchan) * and with an information field of 0 octet length. */ static const uint8_t lapdm_fill_frame[] = { - 0x01, 0x03, 0x01, + 0x01, 0x03, 0x01, 0x2b, /* Pending part is to be randomized */ }; @@ -275,15 +275,16 @@ int sched_prim_dummy(struct trx_lchan_state *lchan) /* FIXME: should we do anything for CSD? */ return 0; } else { - /** - * TS 144.006, section 8.1.2.3 "Fill frames" - * A fill frame is a UI command frame for SAPI 0, P=0 - * and with an information field of 0 octet length. - */ - memcpy(prim_buffer, lapdm_fill_frame, 3); + /* Copy a fill frame payload */ + memcpy(prim_buffer, lapdm_fill_frame, sizeof(lapdm_fill_frame)); - /* Randomize pending unused bytes */ - for (i = 3; i < GSM_MACBLOCK_LEN; i++) + /** + * TS 144.006, section 5.2 "Frame delimitation and fill bits" + * Except for the first octet containing fill bits which shall + * be set to the binary value "00101011", each fill bit should + * be set to a random value when sent by the network. + */ + for (i = sizeof(lapdm_fill_frame); i < GSM_MACBLOCK_LEN; i++) prim_buffer[i] = (uint8_t) rand(); /* Define a prim length */ From e05f6901025cde493164d3cb016e4836749aebac Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 22 Mar 2018 23:02:25 +0700 Subject: [PATCH 207/211] trxcon/scheduler: share lchan link identifiers Change-Id: Ie1632f274b2ae6147a8e918ebfea60eeeb6a234c --- src/host/trxcon/sched_lchan_desc.c | 79 ++++++++++++++---------------- src/host/trxcon/sched_trx.h | 3 ++ 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/host/trxcon/sched_lchan_desc.c b/src/host/trxcon/sched_lchan_desc.c index e94a2083a..37d12730e 100644 --- a/src/host/trxcon/sched_lchan_desc.c +++ b/src/host/trxcon/sched_lchan_desc.c @@ -25,9 +25,6 @@ #include "sched_trx.h" -#define LID_DEDIC 0x00 -#define LID_SACCH 0x40 - /* TODO: implement */ #define tx_pdtch_fn NULL #define tx_tchh_fn NULL @@ -60,7 +57,7 @@ int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { { TRXC_IDLE, "IDLE", - 0x00, LID_DEDIC, + 0x00, TRX_CH_LID_DEDIC, 0x00, 0x00, /** @@ -71,7 +68,7 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { }, { TRXC_FCCH, "FCCH", - 0x00, LID_DEDIC, + 0x00, TRX_CH_LID_DEDIC, 0x00, 0x00, /* FCCH is handled by transceiver */ @@ -79,7 +76,7 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { }, { TRXC_SCH, "SCH", - 0x00, LID_DEDIC, + 0x00, TRX_CH_LID_DEDIC, 0x00, TRX_CH_FLAG_AUTO, /** @@ -90,211 +87,211 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { }, { TRXC_BCCH, "BCCH", - 0x80, LID_DEDIC, + 0x80, TRX_CH_LID_DEDIC, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_AUTO, rx_data_fn, NULL, }, { TRXC_RACH, "RACH", - 0x88, LID_DEDIC, + 0x88, TRX_CH_LID_DEDIC, 0x00, TRX_CH_FLAG_AUTO, NULL, tx_rach_fn, }, { TRXC_CCCH, "CCCH", - 0x90, LID_DEDIC, + 0x90, TRX_CH_LID_DEDIC, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_AUTO, rx_data_fn, NULL, }, { TRXC_TCHF, "TCH/F", - 0x08, LID_DEDIC, + 0x08, TRX_CH_LID_DEDIC, 8 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_tchf_fn, tx_tchf_fn, }, { TRXC_TCHH_0, "TCH/H(0)", - 0x10, LID_DEDIC, + 0x10, TRX_CH_LID_DEDIC, 6 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_tchh_fn, tx_tchh_fn, }, { TRXC_TCHH_1, "TCH/H(1)", - 0x18, LID_DEDIC, + 0x18, TRX_CH_LID_DEDIC, 6 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_tchh_fn, tx_tchh_fn, }, { TRXC_SDCCH4_0, "SDCCH/4(0)", - 0x20, LID_DEDIC, + 0x20, TRX_CH_LID_DEDIC, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH4_1, "SDCCH/4(1)", - 0x28, LID_DEDIC, + 0x28, TRX_CH_LID_DEDIC, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH4_2, "SDCCH/4(2)", - 0x30, LID_DEDIC, + 0x30, TRX_CH_LID_DEDIC, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH4_3, "SDCCH/4(3)", - 0x38, LID_DEDIC, + 0x38, TRX_CH_LID_DEDIC, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH8_0, "SDCCH/8(0)", - 0x40, LID_DEDIC, + 0x40, TRX_CH_LID_DEDIC, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH8_1, "SDCCH/8(1)", - 0x48, LID_DEDIC, + 0x48, TRX_CH_LID_DEDIC, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH8_2, "SDCCH/8(2)", - 0x50, LID_DEDIC, + 0x50, TRX_CH_LID_DEDIC, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH8_3, "SDCCH/8(3)", - 0x58, LID_DEDIC, + 0x58, TRX_CH_LID_DEDIC, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH8_4, "SDCCH/8(4)", - 0x60, LID_DEDIC, + 0x60, TRX_CH_LID_DEDIC, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH8_5, "SDCCH/8(5)", - 0x68, LID_DEDIC, + 0x68, TRX_CH_LID_DEDIC, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH8_6, "SDCCH/8(6)", - 0x70, LID_DEDIC, + 0x70, TRX_CH_LID_DEDIC, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SDCCH8_7, "SDCCH/8(7)", - 0x78, LID_DEDIC, + 0x78, TRX_CH_LID_DEDIC, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCHTF, "SACCH/TF", - 0x08, LID_SACCH, + 0x08, TRX_CH_LID_SACCH, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCHTH_0, "SACCH/TH(0)", - 0x10, LID_SACCH, + 0x10, TRX_CH_LID_SACCH, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCHTH_1, "SACCH/TH(1)", - 0x18, LID_SACCH, + 0x18, TRX_CH_LID_SACCH, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH4_0, "SACCH/4(0)", - 0x20, LID_SACCH, + 0x20, TRX_CH_LID_SACCH, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH4_1, "SACCH/4(1)", - 0x28, LID_SACCH, + 0x28, TRX_CH_LID_SACCH, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH4_2, "SACCH/4(2)", - 0x30, LID_SACCH, + 0x30, TRX_CH_LID_SACCH, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH4_3, "SACCH/4(3)", - 0x38, LID_SACCH, + 0x38, TRX_CH_LID_SACCH, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH8_0, "SACCH/8(0)", - 0x40, LID_SACCH, + 0x40, TRX_CH_LID_SACCH, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH8_1, "SACCH/8(1)", - 0x48, LID_SACCH, + 0x48, TRX_CH_LID_SACCH, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH8_2, "SACCH/8(2)", - 0x50, LID_SACCH, + 0x50, TRX_CH_LID_SACCH, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH8_3, "SACCH/8(3)", - 0x58, LID_SACCH, + 0x58, TRX_CH_LID_SACCH, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH8_4, "SACCH/8(4)", - 0x60, LID_SACCH, + 0x60, TRX_CH_LID_SACCH, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH8_5, "SACCH/8(5)", - 0x68, LID_SACCH, + 0x68, TRX_CH_LID_SACCH, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH8_6, "SACCH/8(6)", - 0x70, LID_SACCH, + 0x70, TRX_CH_LID_SACCH, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_SACCH8_7, "SACCH/8(7)", - 0x78, LID_SACCH, + 0x78, TRX_CH_LID_SACCH, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_CBTX, rx_data_fn, tx_data_fn, }, { TRXC_PDTCH, "PDTCH", - 0x08, LID_DEDIC, + 0x08, TRX_CH_LID_DEDIC, 12 * GSM_BURST_PL_LEN, TRX_CH_FLAG_PDCH, rx_pdtch_fn, tx_pdtch_fn, }, { TRXC_PTCCH, "PTCCH", - 0x08, LID_DEDIC, + 0x08, TRX_CH_LID_DEDIC, 4 * GSM_BURST_PL_LEN, TRX_CH_FLAG_PDCH, rx_data_fn, tx_data_fn, }, diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 80b8dd29b..bba8bcf82 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -17,6 +17,9 @@ #define GPRS_BURST_LEN GSM_BURST_LEN #define EDGE_BURST_LEN 444 +#define TRX_CH_LID_DEDIC 0x00 +#define TRX_CH_LID_SACCH 0x40 + /* Is a channel related to PDCH (GPRS) */ #define TRX_CH_FLAG_PDCH (1 << 0) /* Should a channel be activated automatically */ From 96a8f288c66dee920f5705dd976f13593fc29104 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Thu, 22 Mar 2018 23:04:16 +0700 Subject: [PATCH 208/211] trxcon/scheduler: add CHAN_IS_SACCH macro Change-Id: I2fc90d4732433f221c628058c9812815edf9c8cb --- src/host/trxcon/sched_trx.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index bba8bcf82..ce1c7f4f5 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -292,6 +292,9 @@ int sched_prim_push(struct trx_instance *trx, #define CHAN_IS_TCH(chan) \ (chan == TRXC_TCHF || chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1) +#define CHAN_IS_SACCH(chan) \ + (trx_lchan_desc[chan].link_id & TRX_CH_LID_SACCH) + #define PRIM_IS_TCH(prim) \ CHAN_IS_TCH(prim->chan) && prim->payload_len != GSM_MACBLOCK_LEN From 9d90d1907b9d1013a07ef7a9c187f16bc29129fb Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 2 Apr 2018 19:57:55 +0200 Subject: [PATCH 209/211] trxcon: Respect the tch_mode field of DM_EST_REQ the initial tch_mode is not always 0 (signalling) but can very well be directly a codec mode, if the initial activation of the channel is in speech mode as opposed to signalling Change-Id: I96e4c89da1165e9c5287d863e0e65d811460c606 --- src/host/trxcon/l1ctl.c | 6 +++--- src/host/trxcon/sched_trx.c | 7 ++++--- src/host/trxcon/sched_trx.h | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index 3de0cf686..a08c46053 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -543,8 +543,8 @@ static int l1ctl_rx_dm_est_req(struct l1ctl_link *l1l, struct msgb *msg) chan_nr = ul->chan_nr; LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_DM_EST_REQ (arfcn=%u, " - "chan_nr=0x%02x, tsc=%u)\n", (band_arfcn &~ ARFCN_FLAG_MASK), - chan_nr, est_req->tsc); + "chan_nr=0x%02x, tsc=%u, tch_mode=0x%02x)\n", (band_arfcn &~ ARFCN_FLAG_MASK), + chan_nr, est_req->tsc, est_req->tch_mode); if (est_req->h) { LOGP(DL1C, LOGL_ERROR, "FHSS is not supported\n"); @@ -583,7 +583,7 @@ static int l1ctl_rx_dm_est_req(struct l1ctl_link *l1l, struct msgb *msg) sched_trx_deactivate_all_lchans(ts); /* Activate only requested lchans */ - rc = sched_trx_set_lchans(ts, chan_nr, 1); + rc = sched_trx_set_lchans(ts, chan_nr, 1, est_req->tch_mode); if (rc) { LOGP(DL1C, LOGL_ERROR, "Couldn't activate requested lchans\n"); rc = -EINVAL; diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 168c4efa9..c263ce7fd 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -372,7 +372,7 @@ struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts, return NULL; } -int sched_trx_set_lchans(struct trx_ts *ts, uint8_t chan_nr, int active) +int sched_trx_set_lchans(struct trx_ts *ts, uint8_t chan_nr, int active, uint8_t tch_mode) { const struct trx_lchan_desc *lchan_desc; struct trx_lchan_state *lchan; @@ -389,9 +389,10 @@ int sched_trx_set_lchans(struct trx_ts *ts, uint8_t chan_nr, int active) lchan_desc = &trx_lchan_desc[lchan->type]; if (lchan_desc->chan_nr == (chan_nr & 0xf8)) { - if (active) + if (active) { rc |= sched_trx_activate_lchan(ts, lchan->type); - else + lchan->tch_mode = tch_mode; + } else rc |= sched_trx_deactivate_lchan(ts, lchan->type); } } diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index ce1c7f4f5..17e30bff4 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -266,7 +266,7 @@ enum trx_lchan_type sched_trx_chan_nr2lchan_type(uint8_t chan_nr, uint8_t link_id); void sched_trx_deactivate_all_lchans(struct trx_ts *ts); -int sched_trx_set_lchans(struct trx_ts *ts, uint8_t chan_nr, int active); +int sched_trx_set_lchans(struct trx_ts *ts, uint8_t chan_nr, int active, uint8_t tch_mode); int sched_trx_activate_lchan(struct trx_ts *ts, enum trx_lchan_type chan); int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan); struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts, From 55afe0072b435a0d9abc2db934ec9f17d4fb07dd Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Wed, 21 Mar 2018 13:46:34 +0100 Subject: [PATCH 210/211] trx_toolkit: Add cmdline arg to set bind addr Previous hardcoded default of 0.0.0.0 was inappropiate in some scenarios, as it sets the SRC addr of the packets sent through the socket based on the routing. For instance, if iface IF1 has assigned two IP addresses A and B, A being the first addr of the interface, and osmo-bts-trx is configured with "osmotrx ip local A" and "osmotrx ip remote B", the following happens: CMD POWER OFF src=A:5801 dst=B:5701 RSP POWER OFF src=A:5701 dst=A:5701 <-- A is assigned as src addr. But osmo-bts-trx is waiting for packets from B:5701, and the packet is dropped with ICMP Unreachable. If addr binding is forced in fake_trx to B, then everthing's fine. Let's extend the UDPLink in order to allow manual, but optional setting of bind address, and add a corresponding cmdline argument to all executables. Change-Id: I7be18fef40967fb7551f4115f22cbbd9cdb0840d --- src/target/trx_toolkit/burst_gen.py | 17 +++++++++----- src/target/trx_toolkit/burst_send.py | 17 +++++++++----- src/target/trx_toolkit/clck_gen.py | 2 +- src/target/trx_toolkit/ctrl_cmd.py | 13 +++++++---- src/target/trx_toolkit/ctrl_if_bb.py | 4 ++-- src/target/trx_toolkit/ctrl_if_bts.py | 4 ++-- src/target/trx_toolkit/fake_trx.py | 33 ++++++++++++++++----------- src/target/trx_toolkit/udp_link.py | 4 ++-- 8 files changed, 58 insertions(+), 36 deletions(-) diff --git a/src/target/trx_toolkit/burst_gen.py b/src/target/trx_toolkit/burst_gen.py index 4bc036ea3..d83f1378f 100755 --- a/src/target/trx_toolkit/burst_gen.py +++ b/src/target/trx_toolkit/burst_gen.py @@ -39,6 +39,7 @@ from data_msg import * class Application: # Application variables remote_addr = "127.0.0.1" + bind_addr = "0.0.0.0" base_port = 5700 conn_mode = "TRX" output_file = None @@ -70,11 +71,11 @@ class Application: def run(self): # Init DATA interface with TRX or L1 if self.conn_mode == "TRX": - self.data_if = DATAInterface(self.remote_addr, - self.base_port + 2, self.base_port + 102) + self.data_if = DATAInterface(self.remote_addr, self.base_port + 2, + self.bind_addr, self.base_port + 102) elif self.conn_mode == "L1": - self.data_if = DATAInterface(self.remote_addr, - self.base_port + 102, self.base_port + 2) + self.data_if = DATAInterface(self.remote_addr, self.base_port + 102, + self.bind_addr, self.base_port + 2) # Init random burst generator burst_gen = RandBurstGen() @@ -149,6 +150,7 @@ class Application: " -o --output-file Write bursts to a capture file\n" \ " -m --conn-mode Send bursts to: TRX (default) / L1\n" \ " -r --remote-addr Set remote address (default %s)\n" \ + " -b --bind-addr Set local address (default %s)\n" \ " -p --base-port Set base port number (default %d)\n\n" s += " Burst generation\n" \ @@ -161,7 +163,7 @@ class Application: " --toa Set ToA in symbols (default random)\n" \ " --toa256 Set ToA in 1/256 symbol periods\n" - print(s % (self.remote_addr, self.base_port)) + print(s % (self.remote_addr, self.bind_addr, self.base_port)) if msg is not None: print(msg) @@ -169,12 +171,13 @@ class Application: def parse_argv(self): try: opts, args = getopt.getopt(sys.argv[1:], - "o:m:r:p:b:c:f:t:h", + "o:m:r:b:p:b:c:f:t:h", [ "help", "output-file=" "conn-mode=", "remote-addr=", + "bind-addr=", "base-port=", "burst-type=", "burst-count=", @@ -200,6 +203,8 @@ class Application: self.conn_mode = v elif o in ("-r", "--remote-addr"): self.remote_addr = v + elif o in ("-b", "--bind-addr"): + self.bind_addr = v elif o in ("-p", "--base-port"): self.base_port = int(v) diff --git a/src/target/trx_toolkit/burst_send.py b/src/target/trx_toolkit/burst_send.py index 7405c9019..f6c85ba05 100755 --- a/src/target/trx_toolkit/burst_send.py +++ b/src/target/trx_toolkit/burst_send.py @@ -37,6 +37,7 @@ from data_msg import * class Application: # Application variables remote_addr = "127.0.0.1" + bind_addr = "0.0.0.0" base_port = 5700 conn_mode = "TRX" @@ -65,12 +66,12 @@ class Application: def run(self): # Init DATA interface with TRX or L1 if self.conn_mode == "TRX": - self.data_if = DATAInterface(self.remote_addr, - self.base_port + 2, self.base_port + 102) + self.data_if = DATAInterface(self.remote_addr, self.base_port + 2, + self.bind_addr, self.base_port + 102) l12trx = True elif self.conn_mode == "L1": - self.data_if = DATAInterface(self.remote_addr, - self.base_port + 102, self.base_port + 2) + self.data_if = DATAInterface(self.remote_addr, self.base_port + 102, + self.bind_addr, self.base_port + 2) l12trx = False else: self.print_help("[!] Unknown connection type") @@ -124,6 +125,7 @@ class Application: s += " TRX interface specific\n" \ " -m --conn-mode Send bursts to: TRX (default) / L1\n" \ " -r --remote-addr Set remote address (default %s)\n" \ + " -b --bind-addr Set bind address (default %s)\n" \ " -p --base-port Set base port number (default %d)\n\n" s += " Burst source\n" \ @@ -138,7 +140,7 @@ class Application: " --frame-num-lt NUM TDMA frame number lower than NUM\n" \ " --frame-num-gt NUM TDMA frame number greater than NUM\n" - print(s % (self.remote_addr, self.base_port)) + print(s % (self.remote_addr, self.bind_addr, self.base_port)) if msg is not None: print(msg) @@ -146,11 +148,12 @@ class Application: def parse_argv(self): try: opts, args = getopt.getopt(sys.argv[1:], - "m:r:p:i:h", + "m:r:b:p:i:h", [ "help", "conn-mode=", "remote-addr=", + "bind-addr=", "base-port=", "capture-file=", "msg-skip=", @@ -177,6 +180,8 @@ class Application: self.conn_mode = v elif o in ("-r", "--remote-addr"): self.remote_addr = v + elif o in ("-b", "--bind-addr"): + self.bind_addr = v elif o in ("-p", "--base-port"): self.base_port = int(v) diff --git a/src/target/trx_toolkit/clck_gen.py b/src/target/trx_toolkit/clck_gen.py index 4b86c31a9..b488770e2 100755 --- a/src/target/trx_toolkit/clck_gen.py +++ b/src/target/trx_toolkit/clck_gen.py @@ -102,7 +102,7 @@ class Application: signal.signal(signal.SIGINT, self.sig_handler) def run(self): - self.link = UDPLink("127.0.0.1", 5800, 5700) + self.link = UDPLink("127.0.0.1", 5800, "0.0.0.0", 5700) self.clck = CLCKGen([self.link], ind_period = 51) self.clck.start() diff --git a/src/target/trx_toolkit/ctrl_cmd.py b/src/target/trx_toolkit/ctrl_cmd.py index 0791f85ce..e56105a84 100755 --- a/src/target/trx_toolkit/ctrl_cmd.py +++ b/src/target/trx_toolkit/ctrl_cmd.py @@ -36,6 +36,7 @@ from udp_link import UDPLink class Application: # Application variables remote_addr = "127.0.0.1" + bind_addr = "0.0.0.0" base_port = 5700 bind_port = 0 fuzzing = False @@ -48,8 +49,8 @@ class Application: signal.signal(signal.SIGINT, self.sig_handler) # Init UDP connection - self.ctrl_link = UDPLink(self.remote_addr, - self.base_port + 1, self.bind_port) + self.ctrl_link = UDPLink(self.remote_addr, self.base_port + 1, + self.bind_addr, self.bind_port) # Debug print print("[i] Init CTRL interface (%s)" \ @@ -64,9 +65,10 @@ class Application: " -r --remote-addr Set remote address (default %s)\n" \ " -p --base-port Set base port number (default %d)\n" \ " -P --bind-port Set local port number (default: random)\n" \ + " -b --bind-addr Set local address (default %s)\n" \ " -f --fuzzing Send raw payloads (without CMD)\n" \ - print(s % (self.remote_addr, self.base_port)) + print(s % (self.remote_addr, self.base_port, self.bind_addr)) if msg is not None: print(msg) @@ -74,12 +76,13 @@ class Application: def parse_argv(self): try: opts, args = getopt.getopt(sys.argv[1:], - "r:p:P:fh", + "r:p:P:b:fh", [ "help", "fuzzing", "base-port=", "bind-port=", + "bind-addr=", "remote-addr=", ]) except getopt.GetoptError as err: @@ -93,6 +96,8 @@ class Application: elif o in ("-r", "--remote-addr"): self.remote_addr = v + elif o in ("-b", "--bind-addr"): + self.bind_addr = v elif o in ("-p", "--base-port"): self.base_port = int(v) elif o in ("-P", "--bind-port"): diff --git a/src/target/trx_toolkit/ctrl_if_bb.py b/src/target/trx_toolkit/ctrl_if_bb.py index a1ccd0d17..3de14ef73 100644 --- a/src/target/trx_toolkit/ctrl_if_bb.py +++ b/src/target/trx_toolkit/ctrl_if_bb.py @@ -32,8 +32,8 @@ class CTRLInterfaceBB(CTRLInterface): tx_freq = None pm = None - def __init__(self, remote_addr, remote_port, bind_port): - CTRLInterface.__init__(self, remote_addr, remote_port, bind_port) + def __init__(self, remote_addr, remote_port, bind_addr, bind_port): + CTRLInterface.__init__(self, remote_addr, remote_port, bind_addr, bind_port) print("[i] Init CTRL interface for BB (%s)" % self.desc_link()) def parse_cmd(self, request): diff --git a/src/target/trx_toolkit/ctrl_if_bts.py b/src/target/trx_toolkit/ctrl_if_bts.py index 231641a63..14886178f 100644 --- a/src/target/trx_toolkit/ctrl_if_bts.py +++ b/src/target/trx_toolkit/ctrl_if_bts.py @@ -33,8 +33,8 @@ class CTRLInterfaceBTS(CTRLInterface): tx_freq = None pm = None - def __init__(self, remote_addr, remote_port, bind_port): - CTRLInterface.__init__(self, remote_addr, remote_port, bind_port) + def __init__(self, remote_addr, remote_port, bind_addr, bind_port): + CTRLInterface.__init__(self, remote_addr, remote_port, bind_addr, bind_port) print("[i] Init CTRL interface for BTS (%s)" % self.desc_link()) def parse_cmd(self, request): diff --git a/src/target/trx_toolkit/fake_trx.py b/src/target/trx_toolkit/fake_trx.py index 1fcd3df9b..b818b2a9b 100755 --- a/src/target/trx_toolkit/fake_trx.py +++ b/src/target/trx_toolkit/fake_trx.py @@ -42,6 +42,7 @@ class Application: # Application variables bts_addr = "127.0.0.1" bb_addr = "127.0.0.1" + trx_bind_addr = "0.0.0.0" bts_base_port = 5700 bb_base_port = 6700 @@ -60,12 +61,12 @@ class Application: def run(self): # Init TRX CTRL interface for BTS - self.bts_ctrl = CTRLInterfaceBTS(self.bts_addr, - self.bts_base_port + 101, self.bts_base_port + 1) + self.bts_ctrl = CTRLInterfaceBTS(self.bts_addr, self.bts_base_port + 101, + self.trx_bind_addr, self.bts_base_port + 1) # Init TRX CTRL interface for BB - self.bb_ctrl = CTRLInterfaceBB(self.bb_addr, - self.bb_base_port + 101, self.bb_base_port + 1) + self.bb_ctrl = CTRLInterfaceBB(self.bb_addr, self.bb_base_port + 101, + self.trx_bind_addr, self.bb_base_port + 1) # Power measurement emulation # Noise: -120 .. -105 @@ -77,10 +78,10 @@ class Application: self.bb_ctrl.pm = self.pm # Init DATA links - self.bts_data = UDPLink(self.bts_addr, - self.bts_base_port + 102, self.bts_base_port + 2) - self.bb_data = UDPLink(self.bb_addr, - self.bb_base_port + 102, self.bb_base_port + 2) + self.bts_data = UDPLink(self.bts_addr, self.bts_base_port + 102, + self.trx_bind_addr, self.bts_base_port + 2) + self.bb_data = UDPLink(self.bb_addr, self.bb_base_port + 102, + self.trx_bind_addr, self.bb_base_port + 2) # BTS <-> BB burst forwarding self.burst_fwd = BurstForwarder(self.bts_data, self.bb_data) @@ -94,8 +95,8 @@ class Application: self.bb_ctrl.burst_fwd = self.burst_fwd # Provide clock to BTS - self.bts_clck = UDPLink(self.bts_addr, - self.bts_base_port + 100, self.bts_base_port) + self.bts_clck = UDPLink(self.bts_addr, self.bts_base_port + 100, + self.trx_bind_addr, self.bts_base_port) self.clck_gen = CLCKGen([self.bts_clck]) self.bts_ctrl.clck_gen = self.clck_gen @@ -142,7 +143,8 @@ class Application: " -R --bts-addr Set BTS remote address (default %s)\n" \ " -r --bb-addr Set BB remote address (default %s)\n" \ " -P --bts-base-port Set BTS base port number (default %d)\n" \ - " -p --bb-base-port Set BB base port number (default %d)\n\n" + " -p --bb-base-port Set BB base port number (default %d)\n" \ + " -b --trx-bind-addr Set TRX bind address (default %s)\n\n" s += " Simulation\n" \ " --rand-dl-rssi Enable DL RSSI randomization\n" \ @@ -151,7 +153,8 @@ class Application: " --rand-ul-toa Enable UL ToA randomization\n" print(s % (self.bts_addr, self.bb_addr, - self.bts_base_port, self.bb_base_port)) + self.bts_base_port, self.bb_base_port, + self.trx_bind_addr)) if msg is not None: print(msg) @@ -159,11 +162,12 @@ class Application: def parse_argv(self): try: opts, args = getopt.getopt(sys.argv[1:], - "R:r:P:p:h", + "R:r:P:p:b:h", [ "help", "bts-addr=", "bb-addr=", "bts-base-port=", "bb-base-port=", + "trx-bind-addr=", "rand-dl-rssi", "rand-ul-rssi", "rand-dl-toa", "rand-ul-toa", ]) @@ -186,6 +190,9 @@ class Application: elif o in ("-p", "--bb-base-port"): self.bb_base_port = int(v) + elif o in ("-b", "--trx-bind-addr"): + self.trx_bind_addr = v + # Message field randomization elif o == "rand-dl-rssi": self.randomize_dl_rssi = True diff --git a/src/target/trx_toolkit/udp_link.py b/src/target/trx_toolkit/udp_link.py index 56d9c37c8..b378b635f 100644 --- a/src/target/trx_toolkit/udp_link.py +++ b/src/target/trx_toolkit/udp_link.py @@ -25,10 +25,10 @@ import socket class UDPLink: - def __init__(self, remote_addr, remote_port, bind_port = 0): + def __init__(self, remote_addr, remote_port, bind_addr = '0.0.0.0', bind_port = 0): self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.sock.bind(('0.0.0.0', bind_port)) + self.sock.bind((bind_addr, bind_port)) self.sock.setblocking(0) # Save remote info From 00bfb39d6c0f561a53aa7642e5f005c061c668ad Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Wed, 21 Mar 2018 16:03:21 +0100 Subject: [PATCH 211/211] trxcon/l1ctl.c: hexdump content of unhandled messages Change-Id: Iec8fc6d49d1e35fe101960dd969de559e37a6a75 --- src/host/trxcon/l1ctl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c index a08c46053..74400be84 100644 --- a/src/host/trxcon/l1ctl.c +++ b/src/host/trxcon/l1ctl.c @@ -794,7 +794,8 @@ int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) case L1CTL_CRYPTO_REQ: return l1ctl_rx_crypto_req(l1l, msg); default: - LOGP(DL1C, LOGL_ERROR, "Unknown MSG: %u\n", l1h->msg_type); + LOGP(DL1C, LOGL_ERROR, "Unknown MSG type %u: %s\n", l1h->msg_type, + osmo_hexdump(msgb_data(msg), msgb_length(msg))); msgb_free(msg); return -EINVAL; }