/* osmo-cc-ss5-endpoint main * * (C) 2020 by Andreas Eversberg * 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 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 General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "../libdebug/debug.h" #include "../liboptions/options.h" #include "../libg711/g711.h" #include "ss5.h" #include "display.h" ss5_endpoint_t *ss5_ep_sunset = NULL, *ss5_ep_sunrise = NULL; int num_kanal = 2; int endpoints = 1; int links = 0; int prevent_blueboxing = 0; int suppress_disconnect = 1; int crosstalk = 1; int delay_ms = 300; int comfort_noise = 1; double sense_db = 5; #define MAX_CC_ARGS 1024 static int cc_argc_sunset, cc_argc_sunrise = 0; static const char *cc_argv_sunset[MAX_CC_ARGS], *cc_argv_sunrise[MAX_CC_ARGS]; static void print_usage(const char *app) { printf("Usage: %s [--port ] [--nt] []\n", app); printf("This will create pairs of SS5 channels that are bridged together, so that\n"); printf("calls from one link to the other can be made using SS5. The a bluebox can be\n"); printf("used to play with it.\n"); printf("If one endpoint is used (default), its name is 'sunset' and each pair of\n"); printf("channels are bridged together. If two endpoints are used, their names are\n"); printf("'sunset' and 'sunrise' and same channel index of both endpoints are bridged\n"); printf("together.\n"); } static void print_help() { /* - - */ printf(" -h --help\n"); printf(" This help\n"); printf(" --config [~/]\n"); printf(" Give a config file to use. If it starts with '~/', path is at home dir.\n"); printf(" Each line in config file is one option, '-' or '--' must not be given!\n"); printf(" -v --verbose | ,[,[,...]] | list\n"); printf(" Use 'list' to get a list of all levels and categories\n"); printf(" Verbose level: digit of debug level (default = '%d')\n", debuglevel); printf(" Verbose level+category: level digit followed by one or more categories\n"); printf(" -> If no category is specified, all categories are selected\n"); printf(" -2 --two\n"); printf(" Create two Osmo-CC endpoints instead of one.\n"); printf(" -c --channels\n"); printf(" Give number of channels per endpoint. If you use a single endpoint,\n"); printf(" you must define an even number. By default this is '2' for one\n"); printf(" endpoint and '1' for two endpoints.\n"); printf(" -s --suppress-disconnect 1 | 0\n"); printf(" When a 'busy-flash' or 'release-guard' is received a disconnect is\n"); printf(" forwarded towards OsmoCC. Set to 1 to suppress this. (Default is %d.)\n", suppress_disconnect); printf(" -p --prevent-blueboxing 1 | 0\n"); printf(" Prevent blueboxing by making 'release-guard' 200 ms minimum length.\n"); printf(" -x --crosstalk 1 | 0\n"); printf(" Enable or disable some minor crosstalk. This allows you to hear\n"); printf(" transmitted tones at a low volume. (Default is %d.).\n", crosstalk); printf(" -d --delay | 0\n"); printf(" Add one-way delay to the connection between two SS5 links. This allows\n"); printf(" to hear 'acknowlege' tones delayed. (Default is %d ms.).\n", delay_ms); printf(" -n --comfort-noise 1 | 0\n"); printf(" Add comfort noise whenever there is no audio from the remote link\n"); printf(" (before or after call). (Default is %d ms.).\n", comfort_noise); printf(" --sense 0 | \n"); printf(" Change sensitivity (level) of tone detector. A bluebox must not be\n"); printf(" that loud. (Default is %.0f dB.).\n", sense_db); printf(" -C --cc \"\" [--cc ...]\n"); printf(" --cc2 \"\" [--cc2 ...]\n"); printf(" Pass arguments to Osmo-CC endpoint. Use '-cc help' for description.\n"); printf(" If you select two endpoints, use '--cc2' to pass arguments to the\n"); printf(" second endpoint.\n"); } #define OPT_SENSE 256 #define OPT_CC2 257 static void add_options(void) { option_add('h', "help", 0); option_add('v', "verbose", 1); option_add('2', "two", 0); option_add('c', "channels", 1); option_add('s', "suppress-disconnect", 1); option_add('p', "prevent-blueboxing", 1); option_add('x', "crosstalk", 1); option_add('d', "delay", 1); option_add('n', "comfort-noise", 1); option_add(OPT_SENSE, "sense", 1); option_add('C', "cc", 1); option_add(OPT_CC2, "cc2", 1); } static int handle_options(int short_option, int argi, char **argv) { int rc; switch (short_option) { case 'h': print_usage(argv[0]); print_help(); return 0; case 'v': if (!strcasecmp(argv[argi], "list")) { debug_list_cat(); return 0; } rc = parse_debug_opt(argv[argi]); if (rc < 0) { fprintf(stderr, "Failed to parse debug option, please use -h for help.\n"); return rc; } break; case '2': endpoints = 2; break; case 'c': links = atoi(argv[argi]); break; case 's': suppress_disconnect = atoi(argv[argi]); break; case 'p': prevent_blueboxing = atoi(argv[argi]); break; case 'x': crosstalk = atoi(argv[argi]); break; case 'd': delay_ms = atoi(argv[argi]); break; case 'n': comfort_noise = atoi(argv[argi]); break; case OPT_SENSE: sense_db = (double)atoi(argv[argi]); break; case 'C': if (!strcasecmp(argv[argi], "help")) { osmo_cc_help(); return 0; } if (cc_argc_sunset == MAX_CC_ARGS) { fprintf(stderr, "Too many osmo-cc args!\n"); break; } cc_argv_sunset[cc_argc_sunset++] = options_strdup(argv[argi]); break; case OPT_CC2: if (!strcasecmp(argv[argi], "help")) { osmo_cc_help(); return 0; } if (cc_argc_sunrise == MAX_CC_ARGS) { fprintf(stderr, "Too many osmo-cc args!\n"); break; } cc_argv_sunrise[cc_argc_sunrise++] = options_strdup(argv[argi]); break; default: return -EINVAL; } return 1; } static int quit = 0; void sighandler(int sigset) { if (sigset == SIGHUP || sigset == SIGPIPE) return; fprintf(stderr, "\nSignal %d received.\n", sigset); quit = 1; } static int get_char() { struct timeval tv = {0, 0}; fd_set fds; char c = 0; int __attribute__((__unused__)) rc; FD_ZERO(&fds); FD_SET(0, &fds); select(0+1, &fds, NULL, NULL, &tv); if (FD_ISSET(0, &fds)) { rc = read(0, &c, 1); return c; } else return -1; } int main(int argc, char *argv[]) { int argi, rc; struct termios term, term_orig; double now, last_time_call = 0.0; int c; /* init MF */ mf_init(0); /* init codecs (for recording) */ g711_init(); cc_argv_sunset[cc_argc_sunset++] = options_strdup("remote auto"); cc_argv_sunrise[cc_argc_sunrise++] = options_strdup("remote auto"); /* handle options / config file */ add_options(); rc = options_config_file(argc, argv, "~/.osmocom/ss5/ss5.conf", handle_options); if (rc < 0) return 0; argi = options_command_line(argc, argv, handle_options); if (argi <= 0) return argi; /* check links (per endpoint) */ if (!links) links = (endpoints == 2) ? 1 : 2; if (links == 1 && (endpoints % 1)) { PDEBUG(DSS5, DEBUG_ERROR, "You must define an even number of channels on a single endpoint!\n"); goto error; } /* create sunset and (optionally) sunrise */ ss5_ep_sunset = ss5_ep_create("sunset", links, prevent_blueboxing, crosstalk, delay_ms, comfort_noise, suppress_disconnect, sense_db); if (!ss5_ep_sunset) goto error; rc = osmo_cc_new(&ss5_ep_sunset->cc_ep, OSMO_CC_VERSION, "sunset", OSMO_CC_LOCATION_BEYOND_INTERWORKING, cc_message, NULL, ss5_ep_sunset, cc_argc_sunset, cc_argv_sunset); if (rc < 0) goto error; if (endpoints == 2) { ss5_ep_sunrise = ss5_ep_create("sunrise", links, prevent_blueboxing, crosstalk, delay_ms, comfort_noise, suppress_disconnect, sense_db); if (!ss5_ep_sunrise) goto error; rc = osmo_cc_new(&ss5_ep_sunrise->cc_ep, OSMO_CC_VERSION, "sunrise", OSMO_CC_LOCATION_BEYOND_INTERWORKING, cc_message, NULL, ss5_ep_sunrise, cc_argc_sunrise, cc_argv_sunrise); if (rc < 0) goto error; PDEBUG(DSS5, DEBUG_NOTICE, "Created endpoints 'sunset' and 'sunrise' with %d links that connect these endpoints.\n", links); } else PDEBUG(DSS5, DEBUG_NOTICE, "Created endpoint 'sunset' with %d links, each pair connected.\n", links); refresh_status(); /* prepare terminal */ tcgetattr(0, &term_orig); term = term_orig; term.c_lflag &= ~(ISIG|ICANON|ECHO); term.c_cc[VMIN]=1; term.c_cc[VTIME]=2; tcsetattr(0, TCSANOW, &term); /* catch signals */ signal(SIGINT, sighandler); signal(SIGHUP, sighandler); signal(SIGTERM, sighandler); signal(SIGPIPE, sighandler); while (!quit) { int w; /* send clock calls to play/record audio files */ now = get_time(); if (now - last_time_call >= 0.1) last_time_call = now; if (now - last_time_call >= 0.020) { last_time_call += 0.020; /* call clock every 20ms */ audio_clock(ss5_ep_sunset, ss5_ep_sunrise, 160); } process_timer(); do { w = 0; w |= osmo_cc_handle(); } while (w); usleep(1000); /* process keyboard input */ next_char: c = get_char(); switch (c) { case 3: printf("CTRL+c received, quitting!\n"); quit = 1; goto next_char; case 'c': display_status_on(-1); goto next_char; } } /* reset signals */ signal(SIGINT, SIG_DFL); signal(SIGTSTP, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGPIPE, SIG_DFL); /* reset terminal */ tcsetattr(0, TCSANOW, &term_orig); error: /* destroy endpoints */ if (ss5_ep_sunset) { osmo_cc_delete(&ss5_ep_sunset->cc_ep); ss5_ep_destroy(ss5_ep_sunset); } if (ss5_ep_sunrise) { osmo_cc_delete(&ss5_ep_sunrise->cc_ep); ss5_ep_destroy(ss5_ep_sunrise); } /* exit MF */ mf_exit(); options_free(); return 0; }