blob: 67fb1d7fee652ef32d484eddd05fd0e6e8a29b31 [file] [log] [blame]
Eric Wild901f6892022-05-07 15:36:47 +02001/*
2 * OsmocomBB <-> SDR connection bridge
3 *
4 * (C) 2016-2020 by Vadim Yanitskiy <axilirator@gmail.com>
5 *
6 * All Rights Reserved
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 */
23
Eric2c12b302022-07-19 21:12:58 +020024#define _GNU_SOURCE
Eric Wild901f6892022-05-07 15:36:47 +020025#include <stdio.h>
26#include <stdlib.h>
27#include <stdint.h>
28#include <string.h>
29#include <getopt.h>
30#include <unistd.h>
31#include <signal.h>
32#include <time.h>
Eric2c12b302022-07-19 21:12:58 +020033#include <pthread.h>
Eric Wild901f6892022-05-07 15:36:47 +020034
35#include <arpa/inet.h>
36
37#include <osmocom/core/fsm.h>
38#include <osmocom/core/msgb.h>
39#include <osmocom/core/talloc.h>
40#include <osmocom/core/signal.h>
41#include <osmocom/core/select.h>
42#include <osmocom/core/application.h>
43#include <osmocom/core/gsmtap_util.h>
44#include <osmocom/core/gsmtap.h>
45
46#include <osmocom/gsm/gsm_utils.h>
47
48#include "trxcon.h"
49#include "trx_if.h"
50#include "logging.h"
51#include "l1ctl.h"
52#include "l1ctl_link.h"
53#include "l1ctl_proto.h"
54#include "scheduler.h"
55#include "sched_trx.h"
56
57#define COPYRIGHT \
58 "Copyright (C) 2016-2020 by Vadim Yanitskiy <axilirator@gmail.com>\n" \
59 "License GPLv2+: GNU GPL version 2 or later " \
60 "<http://gnu.org/licenses/gpl.html>\n" \
61 "This is free software: you are free to change and redistribute it.\n" \
62 "There is NO WARRANTY, to the extent permitted by law.\n\n"
63
64static struct {
65 const char *debug_mask;
66 int daemonize;
67 int quit;
68
69 /* L1CTL specific */
70 struct l1ctl_link *l1l;
71 const char *bind_socket;
72
73 /* TRX specific */
74 struct trx_instance *trx;
75 const char *trx_bind_ip;
76 const char *trx_remote_ip;
77 uint16_t trx_base_port;
78 uint32_t trx_fn_advance;
79 const char *gsmtap_ip;
80} app_data;
81
82static void *tall_trxcon_ctx = NULL;
83struct gsmtap_inst *gsmtap = NULL;
84struct osmo_fsm_inst *trxcon_fsm;
85
86static void trxcon_fsm_idle_action(struct osmo_fsm_inst *fi,
87 uint32_t event, void *data)
88{
89 if (event == L1CTL_EVENT_CONNECT)
90 osmo_fsm_inst_state_chg(trxcon_fsm, TRXCON_STATE_MANAGED, 0, 0);
91}
92
93static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi,
94 uint32_t event, void *data)
95{
96 switch (event) {
97 case L1CTL_EVENT_DISCONNECT:
98 osmo_fsm_inst_state_chg(trxcon_fsm, TRXCON_STATE_IDLE, 0, 0);
99
100 if (app_data.trx->fsm->state != TRX_STATE_OFFLINE) {
101 /* Reset scheduler and clock counter */
102 sched_trx_reset(app_data.trx, true);
103
104 /* TODO: implement trx_if_reset() */
105 trx_if_cmd_poweroff(app_data.trx);
106 trx_if_cmd_echo(app_data.trx);
107 }
108 break;
109 case TRX_EVENT_RSP_ERROR:
110 case TRX_EVENT_OFFLINE:
111 /* TODO: notify L2 & L3 about that */
112 break;
113 default:
114 LOGPFSML(fi, LOGL_ERROR, "Unhandled event %u\n", event);
115 }
116}
117
118static struct osmo_fsm_state trxcon_fsm_states[] = {
119 [TRXCON_STATE_IDLE] = {
120 .in_event_mask = GEN_MASK(L1CTL_EVENT_CONNECT),
121 .out_state_mask = GEN_MASK(TRXCON_STATE_MANAGED),
122 .name = "IDLE",
123 .action = trxcon_fsm_idle_action,
124 },
125 [TRXCON_STATE_MANAGED] = {
126 .in_event_mask = (
127 GEN_MASK(L1CTL_EVENT_DISCONNECT) |
128 GEN_MASK(TRX_EVENT_RSP_ERROR) |
129 GEN_MASK(TRX_EVENT_OFFLINE)),
130 .out_state_mask = GEN_MASK(TRXCON_STATE_IDLE),
131 .name = "MANAGED",
132 .action = trxcon_fsm_managed_action,
133 },
134};
135
136static const struct value_string app_evt_names[] = {
137 OSMO_VALUE_STRING(L1CTL_EVENT_CONNECT),
138 OSMO_VALUE_STRING(L1CTL_EVENT_DISCONNECT),
139 OSMO_VALUE_STRING(TRX_EVENT_OFFLINE),
140 OSMO_VALUE_STRING(TRX_EVENT_RSP_ERROR),
141 { 0, NULL }
142};
143
144static struct osmo_fsm trxcon_fsm_def = {
145 .name = "trxcon_app_fsm",
146 .states = trxcon_fsm_states,
147 .num_states = ARRAY_SIZE(trxcon_fsm_states),
148 .log_subsys = DAPP,
149 .event_names = app_evt_names,
150};
151
152static void print_usage(const char *app)
153{
154 printf("Usage: %s\n", app);
155}
156
157static void print_help(void)
158{
159 printf(" Some help...\n");
160 printf(" -h --help this text\n");
161 printf(" -d --debug Change debug flags. Default: %s\n", DEBUG_DEFAULT);
162 printf(" -b --trx-bind TRX bind IP address (default 0.0.0.0)\n");
163 printf(" -i --trx-remote TRX remote IP address (default 127.0.0.1)\n");
164 printf(" -p --trx-port Base port of TRX instance (default 6700)\n");
165 printf(" -f --trx-advance Uplink burst scheduling advance (default 3)\n");
166 printf(" -s --socket Listening socket for layer23 (default /tmp/osmocom_l2)\n");
167 printf(" -g --gsmtap-ip The destination IP used for GSMTAP (disabled by default)\n");
168 printf(" -D --daemonize Run as daemon\n");
169}
170
171static void handle_options(int argc, char **argv)
172{
173 while (1) {
174 int option_index = 0, c;
175 static struct option long_options[] = {
176 {"help", 0, 0, 'h'},
177 {"debug", 1, 0, 'd'},
178 {"socket", 1, 0, 's'},
179 {"trx-bind", 1, 0, 'b'},
180 /* NOTE: 'trx-ip' is now an alias for 'trx-remote'
181 * due to backward compatibility reasons! */
182 {"trx-ip", 1, 0, 'i'},
183 {"trx-remote", 1, 0, 'i'},
184 {"trx-port", 1, 0, 'p'},
185 {"trx-advance", 1, 0, 'f'},
186 {"gsmtap-ip", 1, 0, 'g'},
187 {"daemonize", 0, 0, 'D'},
188 {0, 0, 0, 0}
189 };
190
191 c = getopt_long(argc, argv, "d:b:i:p:f:s:g:Dh",
192 long_options, &option_index);
193 if (c == -1)
194 break;
195
196 switch (c) {
197 case 'h':
198 print_usage(argv[0]);
199 print_help();
200 exit(0);
201 break;
202 case 'd':
203 app_data.debug_mask = optarg;
204 break;
205 case 'b':
206 app_data.trx_bind_ip = optarg;
207 break;
208 case 'i':
209 app_data.trx_remote_ip = optarg;
210 break;
211 case 'p':
212 app_data.trx_base_port = atoi(optarg);
213 break;
214 case 'f':
215 app_data.trx_fn_advance = atoi(optarg);
216 break;
217 case 's':
218 app_data.bind_socket = optarg;
219 break;
220 case 'g':
221 app_data.gsmtap_ip = optarg;
222 break;
223 case 'D':
224 app_data.daemonize = 1;
225 break;
226 default:
227 break;
228 }
229 }
230}
231
232static void init_defaults(void)
233{
234 app_data.bind_socket = "/tmp/osmocom_l2";
235 app_data.trx_remote_ip = "127.0.0.1";
236 app_data.trx_bind_ip = "0.0.0.0";
237 app_data.trx_base_port = 6700;
238 app_data.trx_fn_advance = 3;
239
240 app_data.debug_mask = NULL;
241 app_data.gsmtap_ip = NULL;
242 app_data.daemonize = 0;
243 app_data.quit = 0;
244}
245
246static void signal_handler(int signum)
247{
248 fprintf(stderr, "signal %u received\n", signum);
249
250 switch (signum) {
251 case SIGINT:
252 app_data.quit++;
253 break;
254 case SIGABRT:
255 /* in case of abort, we want to obtain a talloc report and
256 * then run default SIGABRT handler, who will generate coredump
257 * and abort the process. abort() should do this for us after we
258 * return, but program wouldn't exit if an external SIGABRT is
259 * received.
260 */
261 talloc_report_full(tall_trxcon_ctx, stderr);
262 signal(SIGABRT, SIG_DFL);
263 raise(SIGABRT);
264 break;
265 case SIGUSR1:
266 case SIGUSR2:
267 talloc_report_full(tall_trxcon_ctx, stderr);
268 break;
269 default:
270 break;
271 }
272}
273
Eric2ecd9f62022-10-31 15:01:26 +0100274extern void init_external_transceiver(struct trx_instance *trx, int argc, char **argv) __attribute__((weak));
275extern void close_external_transceiver(int argc, char **argv) __attribute__((weak));
Eric Wild901f6892022-05-07 15:36:47 +0200276
277int main(int argc, char **argv)
278{
279 int rc = 0;
280
Eric2c12b302022-07-19 21:12:58 +0200281 cpu_set_t cpuset;
282
283 CPU_ZERO(&cpuset);
284 CPU_SET(3, &cpuset);
285 pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
286
287 int prio = sched_get_priority_max(SCHED_RR) - 5;
288 struct sched_param param;
289 param.sched_priority = prio;
290 int rv = sched_setscheduler(0, SCHED_RR, &param);
291 if (rv < 0) {
292 LOGP(DAPP, LOGL_ERROR, "Failed to set sched!\n");
293 exit(0);
294 }
295
Eric Wild901f6892022-05-07 15:36:47 +0200296 printf("%s", COPYRIGHT);
297 init_defaults();
298 handle_options(argc, argv);
299
300 /* Track the use of talloc NULL memory contexts */
301 talloc_enable_null_tracking();
302
303 /* Init talloc memory management system */
304 tall_trxcon_ctx = talloc_init("trxcon context");
305 msgb_talloc_ctx_init(tall_trxcon_ctx, 0);
306
307 /* Setup signal handlers */
308// signal(SIGINT, &signal_handler);
309// signal(SIGABRT, &signal_handler);
310// signal(SIGUSR1, &signal_handler);
311// signal(SIGUSR2, &signal_handler);
312 osmo_init_ignore_signals();
313
314 /* Init logging system */
315 trx_log_init(tall_trxcon_ctx, app_data.debug_mask);
316
317 /* Configure pretty logging */
318 log_set_print_extended_timestamp(osmo_stderr_target, 1);
319 log_set_print_category_hex(osmo_stderr_target, 0);
320 log_set_print_category(osmo_stderr_target, 1);
321 log_set_print_level(osmo_stderr_target, 1);
322
323 /* Optional GSMTAP */
324 if (app_data.gsmtap_ip != NULL) {
325 gsmtap = gsmtap_source_init(app_data.gsmtap_ip, GSMTAP_UDP_PORT, 1);
326 if (!gsmtap) {
327 LOGP(DAPP, LOGL_ERROR, "Failed to init GSMTAP\n");
328 goto exit;
329 }
330 /* Suppress ICMP "destination unreachable" errors */
331 gsmtap_source_add_sink(gsmtap);
332 }
333
334 /* Allocate the application state machine */
335 OSMO_ASSERT(osmo_fsm_register(&trxcon_fsm_def) == 0);
336 trxcon_fsm = osmo_fsm_inst_alloc(&trxcon_fsm_def, tall_trxcon_ctx,
337 NULL, LOGL_DEBUG, "main");
338
339 /* Init L1CTL server */
340 app_data.l1l = l1ctl_link_init(tall_trxcon_ctx,
341 app_data.bind_socket);
342 if (app_data.l1l == NULL)
343 goto exit;
344
345 /* Init transceiver interface */
346 app_data.trx = trx_if_open(tall_trxcon_ctx,
347 app_data.trx_bind_ip, app_data.trx_remote_ip,
348 app_data.trx_base_port);
349 if (!app_data.trx)
350 goto exit;
351
352 /* Bind L1CTL with TRX and vice versa */
353 app_data.l1l->trx = app_data.trx;
354 app_data.trx->l1l = app_data.l1l;
355
356 /* Init scheduler */
357 rc = sched_trx_init(app_data.trx, app_data.trx_fn_advance);
358 if (rc)
359 goto exit;
360
361 LOGP(DAPP, LOGL_NOTICE, "Init complete\n");
362
363 if (app_data.daemonize) {
364 rc = osmo_daemonize();
365 if (rc < 0) {
366 perror("Error during daemonize");
367 goto exit;
368 }
369 }
370
371 /* Initialize pseudo-random generator */
372 srand(time(NULL));
373
Eric2ecd9f62022-10-31 15:01:26 +0100374 if (init_external_transceiver)
375 init_external_transceiver(app_data.trx, argc, argv);
376 else
377 while (!app_data.quit)
378 osmo_select_main(0);
Eric Wild901f6892022-05-07 15:36:47 +0200379
Eric2ecd9f62022-10-31 15:01:26 +0100380 if (close_external_transceiver)
381 close_external_transceiver(argc, argv);
Eric Wild901f6892022-05-07 15:36:47 +0200382
383exit:
384 /* Close active connections */
385 l1ctl_link_shutdown(app_data.l1l);
386 sched_trx_shutdown(app_data.trx);
387 trx_if_close(app_data.trx);
388
389 /* Shutdown main state machine */
390 osmo_fsm_inst_free(trxcon_fsm);
391
392 /* Deinitialize logging */
393 log_fini();
394
395 /**
396 * Print report for the root talloc context in order
397 * to be able to find and fix potential memory leaks.
398 */
399 talloc_report_full(tall_trxcon_ctx, stderr);
400 talloc_free(tall_trxcon_ctx);
401
402 /* Make both Valgrind and ASAN happy */
403 talloc_report_full(NULL, stderr);
404 talloc_disable_null_tracking();
405
406 return rc;
407}