fixeria has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmocom-bb/+/30745 )
Change subject: trxcon: add (optional) l1gprs_test binary for TTCN-3 ......................................................................
trxcon: add (optional) l1gprs_test binary for TTCN-3
Change-Id: I36ceec4035b2ea593d47998f3f14f1415c606765 Related: OS#5500 --- M src/host/trxcon/.gitignore M src/host/trxcon/configure.ac M src/host/trxcon/src/Makefile.am M src/host/trxcon/src/gprs.c A src/host/trxcon/src/l1gprs_test.c M src/host/trxcon/src/logging.c 6 files changed, 374 insertions(+), 4 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/45/30745/1
diff --git a/src/host/trxcon/.gitignore b/src/host/trxcon/.gitignore index b5c3a99..da1a961 100644 --- a/src/host/trxcon/.gitignore +++ b/src/host/trxcon/.gitignore @@ -28,6 +28,7 @@ *.la
/src/trxcon +/src/l1gprs_test
# various .version diff --git a/src/host/trxcon/configure.ac b/src/host/trxcon/configure.ac index b22d1c8..dfaf97d 100644 --- a/src/host/trxcon/configure.ac +++ b/src/host/trxcon/configure.ac @@ -24,6 +24,14 @@ dnl init libtool LT_INIT
+AC_ARG_WITH(l1gprs_test, [ + AS_HELP_STRING( + [--with-l1gprs-test], + [Enable building l1gprs_test binary]) + ]) + +AM_CONDITIONAL(ENABLE_L1GPRS_TEST, [test "x$with_l1gprs_test" = "xyes"]) + AC_ARG_ENABLE(sanitize, [AS_HELP_STRING( [--enable-sanitize], diff --git a/src/host/trxcon/src/Makefile.am b/src/host/trxcon/src/Makefile.am index 44546d1..f1db13f 100644 --- a/src/host/trxcon/src/Makefile.am +++ b/src/host/trxcon/src/Makefile.am @@ -53,6 +53,21 @@ $(NULL)
+if ENABLE_L1GPRS_TEST +noinst_PROGRAMS = l1gprs_test + +l1gprs_test_SOURCES = \ + l1ctl_server.c \ + l1gprs_test.c \ + logging.c \ + $(NULL) + +l1gprs_test_LDADD = \ + libl1gprs.la \ + $(NULL) +endif + + bin_PROGRAMS = trxcon
trxcon_SOURCES = \ diff --git a/src/host/trxcon/src/gprs.c b/src/host/trxcon/src/gprs.c index a550946..a5e4c5d 100644 --- a/src/host/trxcon/src/gprs.c +++ b/src/host/trxcon/src/gprs.c @@ -133,6 +133,9 @@ { const enum osmo_gprs_cs cs = osmo_gprs_dl_cs_by_block_bytes(ind->data_len);
+ LOGP_PDCH(pdch, LOGL_DEBUG, "Rx PDTCH/D block (fn=%u, len=%zu): %s\n", + ind->frame_nr, ind->data_len, osmo_hexdump(ind->data, ind->data_len)); + if (!pdch->enabled) { LOGP_PDCH(pdch, LOGL_ERROR, "Rx PDTCH/D block for disabled PDCH\n"); return -ENODEV; @@ -158,6 +161,9 @@ int l1gprs_handle_ptcch_ind(struct l1gprs_pdch *pdch, const struct l1gprs_prim_data_ind *ind) { + LOGP_PDCH(pdch, LOGL_DEBUG, "Rx PTCCH/D block (fn=%u, len=%zu): %s\n", + ind->frame_nr, ind->data_len, osmo_hexdump(ind->data, ind->data_len)); + if (!pdch->enabled) { LOGP_PDCH(pdch, LOGL_ERROR, "Rx PTCCH/D block for disabled PDCH\n"); return -ENODEV; @@ -170,8 +176,5 @@ return -EINVAL; }
- LOGP_PDCH(pdch, LOGL_INFO, "Rx PTCCH/D block: %s\n", - osmo_hexdump(ind->data, ind->data_len)); - return 0; } diff --git a/src/host/trxcon/src/l1gprs_test.c b/src/host/trxcon/src/l1gprs_test.c new file mode 100644 index 0000000..315e99d --- /dev/null +++ b/src/host/trxcon/src/l1gprs_test.c @@ -0,0 +1,343 @@ +/* + * MS side L1 GPRS implementation (testing gate) + * + * (C) 2022 by sysmocom - s.f.m.c. GmbH info@sysmocom.de + * Author: Vadim Yanitskiy vyanitskiy@sysmocom.de + * + * 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. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <time.h> + +#include <arpa/inet.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/signal.h> +#include <osmocom/core/select.h> +#include <osmocom/core/application.h> +#include <osmocom/core/gsmtap_util.h> +#include <osmocom/core/gsmtap.h> + +#include <osmocom/bb/trxcon/l1ctl_server.h> +#include <osmocom/bb/trxcon/l1ctl_proto.h> +#include <osmocom/bb/trxcon/logging.h> + +#include <osmocom/bb/l1gprs/l1gprs.h> +#include <osmocom/bb/l1gprs/logging.h> + +#define COPYRIGHT \ + "Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH info@sysmocom.de\n" \ + "License GPLv2+: GNU GPL version 2 or later " \ + "http://gnu.org/licenses/gpl.html\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; + + /* L1CTL specific */ + unsigned int max_clients; + const char *bind_socket; + + /* GSMTAP specific */ + struct gsmtap_inst *gsmtap; + const char *gsmtap_ip; +} app_data = { + .max_clients = 1, /* only one L1CTL client by default */ + .bind_socket = "/tmp/osmocom_l2", +}; + +static void *tall_ctx = NULL; + +static int l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg) +{ + struct l1gprs_grr_inst *grr = l1c->priv; + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->l1h; + + switch (l1h->msg_type) { + case L1CTL_RESET_REQ: + { + LOGP_GRR(grr, LOGL_NOTICE, "Resetting GRR state\n"); + + for (unsigned int tn = 0; tn < ARRAY_SIZE(grr->pdch); tn++) + l1gprs_pdch_disable(&grr->pdch[tn]); + + /* Reuse the original msgb for sending RESET.cnf */ + l1h->msg_type = L1CTL_RESET_CONF; + return l1ctl_client_send(l1c, msg); + } + case L1CTL_DM_EST_REQ: /* PDCH activation */ + case L1CTL_DM_REL_REQ: /* PDCH deactivation */ + { + const struct l1ctl_info_ul *ulh = (void *)l1h->data; + struct l1gprs_pdch *pdch = &grr->pdch[ulh->chan_nr & 0x07]; + + if (l1h->msg_type == L1CTL_DM_EST_REQ) { + if (l1gprs_pdch_enable(pdch) != 0) + LOGP_PDCH(pdch, LOGL_ERROR, "failed to enable\n"); + } else { + if (l1gprs_pdch_disable(pdch) != 0) + LOGP_PDCH(pdch, LOGL_ERROR, "failed to disable\n"); + } + break; + } + case L1CTL_DATA_IND: + { + const struct l1ctl_info_dl *dlh = (void *)l1h->data; + struct l1gprs_pdch *pdch = &grr->pdch[dlh->chan_nr & 0x07]; + const struct l1gprs_prim_data_ind grr_ind = { + .frame_nr = ntohl(dlh->frame_nr), + .data_len = msg->tail - dlh->payload, + .data = &dlh->payload[0], + }; + + if (dlh->link_id == 0x00) + l1gprs_handle_pdtch_ind(pdch, &grr_ind); + else + l1gprs_handle_ptcch_ind(pdch, &grr_ind); + + /* TODO: send over GSMTAP */ + break; + } + default: + LOGP(DAPP, LOGL_ERROR, + "%sL1CTL message 0x%02x is not supported\n", + grr->log_prefix, l1h->msg_type); + break; + } + + msgb_free(msg); + return 0; +} + +static void l1ctl_conn_accept_cb(struct l1ctl_client *l1c) +{ + struct l1gprs_grr_inst *grr; + + grr = l1gprs_grr_inst_alloc(l1c, NULL, l1c); + if (grr == NULL) { + LOGP(DAPP, LOGL_ERROR, "l1gprs_grr_inst_alloc() failed\n"); + l1ctl_client_conn_close(l1c); + return; + } + + l1c->log_prefix = talloc_asprintf(l1c, "l1c[%p]: ", l1c); + l1c->priv = grr; +} + +static void l1ctl_conn_close_cb(struct l1ctl_client *l1c) +{ + struct l1gprs_grr_inst *grr = l1c->priv; + + l1gprs_grr_inst_free(grr); +} + +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 (e.g. DL1C:DSCH)\n"); + printf(" -s --socket Listening socket for layer23 (default /tmp/osmocom_l2)\n"); + printf(" -g --gsmtap-ip The destination IP used for GSMTAP (disabled by default)\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'}, + {"gsmtap-ip", 1, 0, 'g'}, + {"daemonize", 0, 0, 'D'}, + {0, 0, 0, 0} + }; + + c = getopt_long(argc, argv, "d:s:g: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 's': + app_data.bind_socket = optarg; + break; + case 'g': + app_data.gsmtap_ip = optarg; + break; + case 'D': + app_data.daemonize = 1; + break; + default: + break; + } + } +} + +static void signal_handler(int signum) +{ + fprintf(stderr, "signal %u received\n", signum); + + switch (signum) { + case SIGINT: + case SIGTERM: + app_data.quit++; + break; + case SIGABRT: + /* in case of abort, we want to obtain a talloc report and + * then run default SIGABRT handler, who will generate coredump + * and abort the process. abort() should do this for us after we + * return, but program wouldn't exit if an external SIGABRT is + * received. + */ + talloc_report_full(tall_ctx, stderr); + signal(SIGABRT, SIG_DFL); + raise(SIGABRT); + break; + case SIGUSR1: + case SIGUSR2: + talloc_report_full(tall_ctx, stderr); + break; + default: + break; + } +} + +int main(int argc, char **argv) +{ + struct l1ctl_server_cfg server_cfg; + struct l1ctl_server *server = NULL; + int rc = 0; + + printf("%s", COPYRIGHT); + handle_options(argc, argv); + + /* Track the use of talloc NULL memory contexts */ + talloc_enable_null_tracking(); + + /* Init talloc memory management system */ + tall_ctx = talloc_init("l1gprs_test context"); + msgb_talloc_ctx_init(tall_ctx, 0); + + /* Setup signal handlers */ + signal(SIGINT, &signal_handler); + signal(SIGTERM, &signal_handler); + signal(SIGABRT, &signal_handler); + signal(SIGUSR1, &signal_handler); + signal(SIGUSR2, &signal_handler); + osmo_init_ignore_signals(); + + /* Init logging system */ + trxcon_logging_init(tall_ctx, app_data.debug_mask); + l1gprs_logging_init(DGRR); + + log_set_category_filter(osmo_stderr_target, DLCSN1, 1, LOGL_DEBUG); + + /* Configure pretty logging */ + log_set_print_extended_timestamp(osmo_stderr_target, 1); + log_set_print_category_hex(osmo_stderr_target, 0); + log_set_print_category(osmo_stderr_target, 1); + log_set_print_level(osmo_stderr_target, 1); + + log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME); + log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END); + + /* Optional GSMTAP */ + if (app_data.gsmtap_ip != NULL) { + app_data.gsmtap = gsmtap_source_init(app_data.gsmtap_ip, GSMTAP_UDP_PORT, 1); + if (!app_data.gsmtap) { + LOGP(DAPP, LOGL_ERROR, "Failed to init GSMTAP\n"); + goto exit; + } + /* Suppress ICMP "destination unreachable" errors */ + gsmtap_source_add_sink(app_data.gsmtap); + } + + /* Start the L1CTL server */ + server_cfg = (struct l1ctl_server_cfg) { + .sock_path = app_data.bind_socket, + .num_clients_max = app_data.max_clients, + .conn_read_cb = &l1ctl_rx_cb, + .conn_accept_cb = &l1ctl_conn_accept_cb, + .conn_close_cb = &l1ctl_conn_close_cb, + }; + + server = l1ctl_server_alloc(tall_ctx, &server_cfg); + if (server == NULL) { + rc = EXIT_FAILURE; + goto exit; + } + + LOGP(DAPP, LOGL_NOTICE, "Init complete\n"); + + if (app_data.daemonize) { + rc = osmo_daemonize(); + if (rc < 0) { + perror("Error during daemonize"); + goto exit; + } + } + + /* Initialize pseudo-random generator */ + srand(time(NULL)); + + while (!app_data.quit) + osmo_select_main(0); + +exit: + if (server != NULL) + l1ctl_server_free(server); + + /* Deinitialize logging */ + log_fini(); + + /** + * Print report for the root talloc context in order + * to be able to find and fix potential memory leaks. + */ + talloc_report_full(tall_ctx, stderr); + talloc_free(tall_ctx); + + /* Make both Valgrind and ASAN happy */ + talloc_report_full(NULL, stderr); + talloc_disable_null_tracking(); + + return rc; +} diff --git a/src/host/trxcon/src/logging.c b/src/host/trxcon/src/logging.c index e98d79a..c0803f6 100644 --- a/src/host/trxcon/src/logging.c +++ b/src/host/trxcon/src/logging.c @@ -69,7 +69,7 @@ [DGRR] = { .name = "DGRR", .description = "GPRS RR", - .color = "\033[1;36m", + .color = "\033[0;96m", .enabled = 1, .loglevel = LOGL_DEBUG, }, };