/* osmo-cc-misdn-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 #include "../liblogging/logging.h" #include "../liboptions/options.h" #include #include #include "isdn.h" #include "dss1.h" #include "bridge.h" #include "../libmisdn/core.h" #include "ph_driver.h" isdn_t *isdn_ep = NULL; int num_kanal = 1; static char law = 'a'; static const char *portname = NULL; static int misdn_kernel = 0, misdn_user = 0; static int ntmode = 0; static int ptp = 0; static int layer1hold = 0; static int layer2hold = 0; static int aocd = 0; static int aocs = 0; static int time_no_sec = 0; static const char *channel_out = NULL; static const char *channel_in = NULL; static const char *timeouts = NULL; static int tx_delay = 0; static int local_tones = 0; static int debug_mISDN = 0; static int serving_location = 1; /* private network serving local user */ static int use_hfc_bridging = 1; static int bridging_server_only = 0; static int pcm_slots = 32; static int rt_prio = 1; #define MAX_CC_ARGS 1024 static int cc_argc = 0; static const char *cc_argv[MAX_CC_ARGS]; static void print_usage(const char *app) { printf("Usage: %s [--port ] [--nt] []\n", app); } 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"); logging_print_help(); printf(" --ulaw\n"); printf(" Use U-LAW for b-channel coding instead of alaw.\n"); printf(" -p --port | \n"); printf(" Number or name of misdn port (see misdn_info).\n"); printf(" -s --socket \n"); printf(" Path to UNIX socket that provides layer 1 connection to an ISDN\n"); printf(" interface.\n"); printf(" -n --nt\n"); printf(" The given port is configured as NT-mode, instead of TE-mode.\n"); printf(" -0 --ptp\n"); printf(" The given port is configured as point-to-point. PRI-Ports are always\n"); printf(" configured as point-to-point.\n"); printf(" -M --msn [--msn ...]\n"); printf(" Give one or multiple MSN numbers. If MSN numbers are defined, only\n"); printf(" MSN numbers that are defined are allowed. If a different MSN number\n"); printf(" is received from a phone, it is replaced by the first MSN number.\n"); printf(" This is only useful for NT-mode with point-to-mulipoint configuration.\n"); printf(" (Any MSN is accepted and forwarded by default.).\n"); printf(" -1 --layer-1-hold 0 | 1\n"); printf(" Keep layer 1 always active. (Default for point-to-point)\n"); printf(" -2 --layer-2-hold 0 | 1\n"); printf(" Keep layer 2 always active. (Default for point-to-point)\n"); printf(" --channel-out [force,][][,...][,free][,any][,no]\n"); printf(" Channel selection list for all outgoing calls to the interface.\n"); printf(" A free channels is searched in order of appearance.\n"); printf(" force - Forces the selected port with no acceptable alternative.\n"); printf(" -> this must be used for multipoint NT-mode ports\n"); printf(" [,...] - List of channels to search.\n"); printf(" free - Select any free channel\n"); printf(" any - On outgoing calls, signal 'any channel acceptable'. (see DSS1)\n"); printf(" no - Signal 'no channel available' aka 'call waiting'. (see DSS1)\n"); printf(" Default for multipoint NT-Mode: 'force,free,no'\n"); printf(" Default for point-to-point NT-Mode: 'force,free'\n"); printf(" Default for TE-Mode: 'any'\n"); printf(" --channel-in [][,...][,free]\n"); printf(" Give list of channels to select for calls from ISDN\n"); printf(" Channel selection list for all incoming calls from the interface.\n"); printf(" A free channels is accepted if in the list.\n"); printf(" If any channel was requested, the first free channel found is selected.\n"); printf(" [,...] - List of channels to accept.\n"); printf(" free - Accept any free channel\n"); printf(" Default: 'free'\n"); printf(" --timeouts ,,,,\n"); printf(" Alter ISDN protocol times.\n"); printf(" The default is 120 seconds for all states. Use 0 to disable.\n"); printf(" --tx-delay \n"); printf(" Give a delay in milliseconds. This is required for modem/fax. Audio\n"); printf(" toward ISDN interface is buffered with the given delay.\n"); printf(" This feature alters dejittering strategy.\n"); printf(" --aocd\n"); printf(" Send AOC-D charging information\n"); printf(" --aocs\n"); printf(" Send AOC-S charging information\n"); printf(" --time-no-seconds\n"); printf(" Send date/time IE without seconds\n"); printf(" -T --local-tones german | oldgerman | american\n"); printf(" Send locally generated tones, if not provided by remote interface.\n"); printf(" -D --debug-misdn\n"); printf(" Enables mISDN stack debugging.\n"); printf(" --serving-location (see Q.931)\n"); printf(" 0 = user, 1 = private network serving local user (default=%d)\n", serving_location); printf(" -B --bridging 0 | 1\n"); printf(" Enable or disable hardware bridging with HFC cards. (default = %d)\n", use_hfc_bridging); printf(" --pcm-slots 32 | 64 | 128\n"); printf(" The number of slots must match the configured PCM bus size.\n"); printf(" (default = %d)\n", pcm_slots); printf(" -r --realtime \n"); printf(" Set prio: 0 to disable, 99 for maximum (default = %d)\n", rt_prio); printf(" -C --cc \"\" [--cc ...]\n"); printf(" Pass arguments to Osmo-CC endpoint. Use '-cc help' for description.\n"); } #define OPT_ULAW 256 #define OPT_CHANNEL_OUT 257 #define OPT_CHANNEL_IN 258 #define OPT_TIMEOUTS 259 #define OPT_TX_DELAY 260 #define OPT_TX_GAIN 261 #define OPT_RX_GAIN 262 #define OPT_PIPELINE 263 #define OPT_DTMF 264 #define OPT_SERVING 265 #define OPT_PCM_SLOTS 266 #define OPT_BR_ONLY 267 #define OPT_AOCD 268 #define OPT_AOCS 269 #define OPT_TIME_NO_SEC 270 static void add_options(void) { option_add('h', "help", 0); option_add('v', "verbose", 1); option_add(OPT_ULAW, "ulaw", 0); option_add('p', "port", 1); option_add('s', "socket", 1); option_add('n', "nt", 0); option_add('0', "ptp", 0); option_add('M', "msn", 1); option_add('1', "layer-1-hold", 1); option_add('2', "layer-2-hold", 1); option_add(OPT_CHANNEL_OUT, "channel-out", 1); option_add(OPT_CHANNEL_IN, "channel-in", 1); option_add(OPT_TIMEOUTS, "timeouts", 1); option_add(OPT_TX_DELAY, "tx-delay", 1); option_add('T', "local-tones", 1); option_add(OPT_AOCD, "aocd", 0); option_add(OPT_AOCS, "aocs", 0); option_add(OPT_TIME_NO_SEC, "time-no-seconds", 0); option_add('D', "debug-misdn", 0); option_add(OPT_SERVING, "serving-location", 1); option_add('B', "bridging", 1); option_add(OPT_PCM_SLOTS, "pcm-slots", 1); option_add(OPT_BR_ONLY, "bridging-server-only", 0); option_add('r', "realtime", 1); option_add('C', "cc", 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': rc = parse_logging_opt(argv[argi]); if (rc > 0) return 0; if (rc < 0) { fprintf(stderr, "Failed to parse debug option, please use -h for help.\n"); return rc; } break; case OPT_ULAW: law = 'u'; break; case 'p': portname = options_strdup(argv[argi]); misdn_kernel = 1; break; case 's': portname = options_strdup(argv[argi]); misdn_user = 1; break; case 'n': ntmode = 1; break; case '0': ptp = 1; break; case 'M': isdn_add_msn(isdn_ep, argv[argi]); break; case '1': layer1hold = atoi(argv[argi]); break; case '2': layer2hold = atoi(argv[argi]); break; case OPT_CHANNEL_OUT: channel_out = options_strdup(argv[argi]); break; case OPT_CHANNEL_IN: channel_in = options_strdup(argv[argi]); break; case OPT_TIMEOUTS: timeouts = options_strdup(argv[argi]); break; case OPT_TX_DELAY: tx_delay = atoi(argv[argi]); break; case OPT_AOCD: aocd = 1; break; case OPT_AOCS: aocs = 1; break; case OPT_TIME_NO_SEC: time_no_sec = 1; break; case 'T': if (!strcasecmp(argv[argi], "american")) local_tones = TONES_TYPE_AMERICAN; else if (!strcasecmp(argv[argi], "german")) local_tones = TONES_TYPE_GERMAN; else if (!strcasecmp(argv[argi], "oldgerman")) local_tones = TONES_TYPE_OLDGERMAN; else { fprintf(stderr, "Invalid tones type given!\n"); return -EINVAL; } break; case 'D': debug_mISDN = 1; break; case OPT_SERVING: serving_location = atoi(argv[argi]); break; case 'B': use_hfc_bridging = atoi(argv[argi]); break; case OPT_PCM_SLOTS: pcm_slots = strtoul(argv[argi], NULL, 10); break; case OPT_BR_ONLY: bridging_server_only = 1; break; case 'r': rt_prio = atoi(argv[argi]); break; case 'C': if (!strcasecmp(argv[argi], "help")) { osmo_cc_help(); return 0; } if (cc_argc == MAX_CC_ARGS) { fprintf(stderr, "Too many osmo-cc args!\n"); break; } cc_argv[cc_argc++] = 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 struct mi_ext_fn_s mi_fn; static int mISDNlib_debug(const char *file, int line, const char __attribute__((unused)) *func, int __attribute__((unused)) level, const char *fmt, va_list va) { LOGPSRC(DMISDN, LOGL_NOTICE, file, line, fmt, va); return 0; } int main(int argc, char *argv[]) { int argi, rc; int misdn_initialized = 0; int ph_drv_initialized = 0; int layer3_initialized = 0; struct ph_socket_driver ph_drv; const char *p; struct osmo_timer_list mISDN_timer; logging_init(); g711_init(); isdn_ep = isdn_create(); if (!isdn_ep) goto error; cc_argv[cc_argc++] = options_strdup("remote auto"); /* handle options / config file */ add_options(); rc = options_config_file(argc, argv, "~/.osmocom/isdn/isdn.conf", handle_options); if (rc < 0) return 0; argi = options_command_line(argc, argv, handle_options); if (argi <= 0) return argi; if (bridging_server_only) { bridge_socket_server_child(pcm_slots, 1); return 0; } /* start bridge server */ if (use_hfc_bridging) { bridge_socket_server(pcm_slots); bridge_socket_client(isdn_ep); } if (!misdn_kernel && !misdn_user) { fprintf(stderr, "You defined no mISDN port or layer 1 socket. You must define either one of them! Use '-h' for help.\n"); goto error; } if (misdn_kernel && misdn_user) { fprintf(stderr, "You defined mISDN port and layer 1 socket. You must define either one of them!\n"); goto error; } /* init user space mISDN */ if (misdn_user) { rc = mISDNInit((debug_mISDN) ? 0xffffffff : 0); if (rc) goto error; misdn_initialized = 1; rc = init_ph_socket_driver(&ph_drv, isdn_ep, portname, 0, ntmode, (debug_mISDN) ? 0xffffffff : 0); if (rc) goto error; ph_drv_initialized = 1; } /* mISDNuser init and debug */ mi_fn.prt_debug = mISDNlib_debug; init_layer3(4, &mi_fn, (misdn_user) ? 1 : 0); layer3_initialized = 1; mISDN_set_debug_level((debug_mISDN) ? 0xfffffeff : 0); /* change tones to ulaw */ if (law == 'u') isdn_tone_generate_ulaw_samples(); /* init instance */ rc = isdn_initialize(isdn_ep, (misdn_user) ? &ph_drv.ph_socket : NULL, law, portname, ntmode, ptp, layer1hold, layer2hold, channel_out, channel_in, timeouts, tx_delay, local_tones, serving_location, aocd, aocs, time_no_sec); if (rc) { LOGP(DISDN, LOGL_ERROR, "mISDN initializing failed!\n"); goto error; } rc = isdn_open(isdn_ep); if (rc) { LOGP(DISDN, LOGL_ERROR, "mISDN open failed!\n"); goto error; } while ((p = strchr(portname, '/'))) portname = p + 1; rc = osmo_cc_new(&isdn_ep->cc_ep, OSMO_CC_VERSION, isdn_ep->portname, serving_location, cc_message, NULL, isdn_ep, cc_argc, cc_argv); if (rc < 0) goto error; /* Dummy timer to interrupt osmo_select_main when mISDN timer fires before anything else. */ osmo_timer_setup(&mISDN_timer, NULL, NULL); /* real time priority */ if (rt_prio > 0) { struct sched_param schedp; int rc; memset(&schedp, 0, sizeof(schedp)); schedp.sched_priority = rt_prio; rc = sched_setscheduler(0, SCHED_RR, &schedp); if (rc) fprintf(stderr, "Error setting SCHED_RR with prio %d\n", rt_prio); } signal(SIGINT, sighandler); signal(SIGHUP, sighandler); signal(SIGTERM, sighandler); signal(SIGPIPE, sighandler); while (!quit) { int work; double misdn_timeout = 0.0; do { work = 0; work |= osmo_cc_handle(); if (misdn_user) { /* run workers of mISDN stacks in user space */ work |= mISDN_work(); work |= work_layer3(isdn_ep->ml3, &misdn_timeout); } } while (work); /* wait for event or stop, if mISDN_timer fires */ if (misdn_timeout) osmo_timer_schedule(&mISDN_timer, floor(misdn_timeout), (misdn_timeout - floor(misdn_timeout)) * 1000000); osmo_select_main(0); osmo_timer_del(&mISDN_timer); } signal(SIGINT, SIG_DFL); signal(SIGTSTP, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGPIPE, SIG_DFL); /* reset real time prio */ if (rt_prio > 0) { struct sched_param schedp; memset(&schedp, 0, sizeof(schedp)); schedp.sched_priority = 0; sched_setscheduler(0, SCHED_OTHER, &schedp); } error: if (isdn_ep) { osmo_cc_delete(&isdn_ep->cc_ep); isdn_destroy(isdn_ep); } if (layer3_initialized) cleanup_layer3(); if (ph_drv_initialized) exit_ph_socket_driver(&ph_drv); if (misdn_initialized) mISDN_cleanup(); options_free(); return 0; }