From e8a3306eee9eadd8d8d2547ed4e6c012a155109b Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 29 Oct 2017 07:58:20 +0100 Subject: Add option to cross-connect calls; Calls between mobiles are now possible Use -x to enable call cross-connect. No MNCC socket, no call device must be specified! Be sure to have at least one control channel and two voice channels. Alternatively you can use one combined control/voice channel and one voice channel. --- docs/software.html | 61 +++++++----- src/common/Makefile.am | 1 + src/common/main_mobile.c | 51 ++++++++-- src/common/mncc_cross.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++ src/common/mncc_cross.h | 5 + 5 files changed, 329 insertions(+), 29 deletions(-) create mode 100644 src/common/mncc_cross.c create mode 100644 src/common/mncc_cross.h diff --git a/docs/software.html b/docs/software.html index 731899d..944e2a5 100644 --- a/docs/software.html +++ b/docs/software.html @@ -139,29 +139,6 @@ Finally store the settings using "alsactl store" command. Do this whenever you want to keep your adjustments.

-

-Channel crossing -

- -

-By default, the right channel of your sound adapter is used to connect to the radio. -Some adapters have mono input, especially on microphone jack. -In this case you will always receive audio, no matter if you cross the channels or not. -If you cross the channels, the left channel of your sound adapter is used. -I use mono cables with only one channel on the tip of the plug. -Usual sound adapters connect left channel on the tip of the plug, so we need to swap channels to use mono plugs. -Use the command line option '-x' or '--cross': -

- -
-
-# src/bnetz/bnetz -k 1 -x -l 2
-bnetz.c:268 info   : Entering IDLE state, sending 'Gruppenfreisignal' 2 on channel 1.
-Base station ready, please tune transmitter to 153.010 MHz and receiver to 148.410 MHz.
-To call phone, switch transmitter (using pilot signal) to 153.370 MHz.
-
-
-

Emphasis

@@ -334,6 +311,44 @@ Current transceiver and call state can be viewed by pressing 'c' key. Press 'c' again to turn off this view.

+

+Mobile to mobile calls +

+ +

+Calls can be forwarded between mobiles. +By default, only one call can be made with the built-in console. +If a headset is used, only one call can be made between headset and one mobile station. +If call forwarding is used, two (or more) mobile stations can directly call each other. +It is essential to have at least two voice channels of course. +Depending on the network, a control channel or alternatively a combined control+voice channel is required. +This feature makes sense for SDR only, because SDR can provide multiple voice and control channels. +(It is also possible to use two radio receivers and transmitters connected to a sound card.) +

+ +

+To forward calls, be sure to configure the network with at least two channels that support voice. +Add '-x' to your command line. +On one phone, enter the number of the other phone and start the call. +

+ +
+
+nmt -k 1 -k 4 -a hw:0,0 -a hw:0,0 -T CC/TC -T TC -0 1 -0 2 -Y se,1 -x
+
+
+ +

+This example will run a base station with two channel (1 and 4) via two radios connectd to a stereo sound card. +The sound card is accessed via '-a hw:0,0'. +Because the sound card is stereo, the '-a' option can be given for two channels. +The first channel is a combined control+traffic channel and the second a traffic channel. +Both channels have different supervisory signals '-0 1 -0 2'. +The station code is '-Y se,1'. +Refer to NMT section about configuring an NMT network. +I highly recommend to use an SDR instead of radios connected to a sound card. +

+
[Back to main page]

diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 3b3375e..6005e56 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -28,6 +28,7 @@ libmobile_a_SOURCES = \ call.c \ testton.c \ mncc_console.c \ + mncc_cross.c \ mncc_sock.c \ hagelbarger.c \ display_status.c \ diff --git a/src/common/main_mobile.c b/src/common/main_mobile.c index bab9579..0be77d2 100644 --- a/src/common/main_mobile.c +++ b/src/common/main_mobile.c @@ -37,6 +37,7 @@ #include "call.h" #include "mncc_console.h" #include "mncc_sock.h" +#include "mncc_cross.h" #ifdef HAVE_SDR #include "sdr.h" #include "sdr_config.h" @@ -61,6 +62,7 @@ int do_de_emphasis = 0; double rx_gain = 1.0; static int echo_test = 0; static int use_mncc_sock = 0; +static int use_mncc_cross = 0; const char *mncc_name = ""; static int send_patterns = 1; static int release_on_disconnect = 1; @@ -124,6 +126,10 @@ void main_mobile_print_help(const char *arg0, const char *ext_usage) printf(" Sound card and device number for headset (default = '%s')\n", call_audiodev); printf(" --call-samplerate \n"); printf(" Sample rate of sound device for headset (default = '%d')\n", call_samplerate); + printf(" -x --mncc-cross\n"); + printf(" Enable built-in call forwarding between mobiles. Be sure to have\n"); + printf(" at least one control channel and two voice channels. Alternatively\n"); + printf(" use one combined control+voice channel and one voice channels.\n"); printf(" -m --mncc-sock\n"); printf(" Disable built-in call contol and offer socket (to LCR)\n"); printf(" --mncc-name \n"); @@ -184,6 +190,7 @@ static struct option main_mobile_long_options[] = { {"de-emphasis", 0, 0, 'd'}, {"rx-gain", 1, 0, 'g'}, {"echo-test", 0, 0, 'e'}, + {"mncc-cross", 0, 0, 'x'}, {"mncc-sock", 0, 0, 'm'}, {"mncc-name", 1, 0, OPT_MNCC_NAME}, {"call-device", 1, 0, 'c'}, @@ -198,7 +205,7 @@ static struct option main_mobile_long_options[] = { {0, 0, 0, 0} }; -static const char *main_mobile_optstring = "hv:k:a:s:i:b:pdg:mec:t:l:r:"; +static const char *main_mobile_optstring = "hv:k:a:s:i:b:pdg:exmc:t:l:r:"; struct option *long_options; char *optstring; @@ -320,6 +327,10 @@ void main_mobile_opt_switch(int c, char *arg0, int *skip_args) echo_test = 1; *skip_args += 1; break; + case 'x': + use_mncc_cross = 1; + *skip_args += 1; + break; case 'm': use_mncc_sock = 1; *skip_args += 1; @@ -427,16 +438,32 @@ void main_mobile(int *quit, int latency, int interval, void (*myhandler)(void), latspl = samplerate * latency / 1000; /* check MNCC support */ + if (use_mncc_cross && num_kanal == 1) { + fprintf(stderr, "You selected built-in call forwarding, but only channel is used. Does this makes sense?\n"); + return; + } + if (use_mncc_sock && use_mncc_cross) { + fprintf(stderr, "You selected MNCC socket interface and built-in call forwarding, but only one can be selected.\n"); + return; + } + if (echo_test && call_audiodev[0]) { + fprintf(stderr, "You selected call device (headset) and echo test, but only one can be selected.\n"); + return; + } if (use_mncc_sock && call_audiodev[0]) { - fprintf(stderr, "You selected MNCC interface, but it cannot be used with call device (headset).\n"); + fprintf(stderr, "You selected MNCC socket interface, but it cannot be used with call device (headset).\n"); + return; + } + if (use_mncc_cross && call_audiodev[0]) { + fprintf(stderr, "You selected built-in call forwarding, but it cannot be used with call device (headset).\n"); return; } if (use_mncc_sock && echo_test) { - fprintf(stderr, "You selected MNCC interface, but it cannot be used with echo test.\n"); + fprintf(stderr, "You selected MNCC socket interface, but it cannot be used with echo test.\n"); return; } - if (echo_test && call_audiodev[0]) { - fprintf(stderr, "You selected call device (headset), but it cannot be used with echo test.\n"); + if (use_mncc_cross && echo_test) { + fprintf(stderr, "You selected built-in call forwarding, but it cannot be used with echo test.\n"); return; } @@ -453,6 +480,12 @@ void main_mobile(int *quit, int latency, int interval, void (*myhandler)(void), fprintf(stderr, "Failed to setup MNCC socket. Quitting!\n"); return; } + } else if (use_mncc_cross) { + rc = mncc_cross_init(); + if (rc < 0) { + fprintf(stderr, "Failed to setup MNCC crossing process. Quitting!\n"); + return; + } } else { console_init(station_id, call_audiodev, call_samplerate, latency, station_id_digits, loopback, echo_test); } @@ -598,6 +631,8 @@ next_char: /* process call control */ if (use_mncc_sock) mncc_sock_handle(); + else if (use_mncc_cross) + mncc_cross_handle(); else process_console(c); @@ -639,11 +674,15 @@ next_char: } /* cleanup call control */ - if (!use_mncc_sock) + if (!use_mncc_sock && !use_mncc_cross) console_cleanup(); /* close mncc socket */ if (use_mncc_sock) mncc_sock_exit(); + + /* close mncc forwarding */ + if (use_mncc_cross) + mncc_cross_exit(); } diff --git a/src/common/mncc_cross.c b/src/common/mncc_cross.c new file mode 100644 index 0000000..5ca51ac --- /dev/null +++ b/src/common/mncc_cross.c @@ -0,0 +1,240 @@ +/* Mobie Network Call Control (MNCC) cross connecting mobiles + * + * (C) 2017 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 "sample.h" +#include "debug.h" +#include "call.h" +#include "cause.h" +#include "mncc.h" +#include "mncc_cross.h" + +int new_callref = 0; /* toward mobile */ + +typedef struct cross { + struct cross *next; + int callref1; + int callref2; +} cross_t; + +static cross_t *cross_head = NULL; + +static int create_cross(int callref) +{ + cross_t *cross; + + cross = calloc(1, sizeof(*cross)); + if (!cross) { + PDEBUG(DMNCC, DEBUG_ERROR, "No memory!\n"); + abort(); + } + + cross->callref1 = callref; + cross->callref2 = ++new_callref; + + /* attach to list */ + cross->next = cross_head; + cross_head = cross; + + PDEBUG(DMNCC, DEBUG_INFO, "Cross connection created\n"); + + return cross->callref2; +} + +static void destroy_cross(cross_t *cross) +{ + cross_t **crossp; + + /* detach from list */ + crossp = &cross_head; + while (*crossp && *crossp != cross) + crossp = &((*crossp)->next); + if (!(*crossp)) { + PDEBUG(DMNCC, DEBUG_ERROR, "Transaction not in list, please fix!!\n"); + abort(); + } + *crossp = cross->next; + + free(cross); + + PDEBUG(DMNCC, DEBUG_INFO, "Cross connection destroyed\n"); +} + +typedef struct queue { + struct queue *next; + int length; + uint8_t buf[0]; +} queue_t; + +static queue_t *queue_head; + +static void cross_mncc_up(uint8_t *buf, int length); + +static int cross_mncc_up_queue(uint8_t *buf, int length) +{ + struct gsm_mncc *mncc = (struct gsm_mncc *)buf; + queue_t *queue, **queuep; + + /* directly forward voice */ + if (mncc->msg_type == ANALOG_8000HZ) { + cross_mncc_up(buf, length); + return 0; + } + + /* queue all other messages */ + queue = calloc(1, sizeof(*queue) + length); + if (!queue) { + PDEBUG(DMNCC, DEBUG_ERROR, "No memory!\n"); + return -CAUSE_TEMPFAIL; + } + queue->length = length; + memcpy(queue->buf, buf, length); + + /* add tail */ + queuep = &queue_head; + while (*queuep) + queuep = &((*queuep)->next); + *queuep = queue; + + return 0; +} + +static void cross_mncc_up(uint8_t *buf, int length) +{ + struct gsm_mncc *mncc = (struct gsm_mncc *)buf; + cross_t *cross = NULL; + int callref = mncc->callref, remote = 0; + + /* find cross instance */ + for (cross = cross_head; cross; cross = cross->next) { + if (cross->callref1 == callref) { + remote = cross->callref2; + break; + } + if (cross->callref2 == callref) { + remote = cross->callref1; + break; + } + } + + if (mncc->msg_type == MNCC_REL_CNF) { + if (cross) + destroy_cross(cross); + return; + } + + if (!remote && mncc->msg_type != MNCC_SETUP_IND) { + PDEBUG(DMNCC, DEBUG_ERROR, "invalid call ref.\n"); + /* send down reused MNCC */ + mncc->msg_type = MNCC_REL_REQ; + mncc->fields |= MNCC_F_CAUSE; + mncc->cause.location = LOCATION_USER; + mncc->cause.value = CAUSE_INVALCALLREF; + mncc_down(buf, length); + return; + } + + switch (mncc->msg_type) { + case ANALOG_8000HZ: + /* send down reused MNCC */ + mncc->callref = remote; + mncc_down(buf, length); + break; + case MNCC_SETUP_IND: + remote = create_cross(callref); + /* send down reused MNCC */ + mncc->msg_type = MNCC_SETUP_REQ; + mncc->callref = remote; + mncc_down(buf, length); + break; + case MNCC_CALL_CONF_IND: + /* send down reused MNCC */ + mncc->msg_type = MNCC_CALL_PROC_REQ; + mncc->callref = remote; + mncc_down(buf, length); + break; + case MNCC_ALERT_IND: + /* send down reused MNCC */ + mncc->msg_type = MNCC_ALERT_REQ; + mncc->callref = remote; + mncc_down(buf, length); + break; + case MNCC_SETUP_CNF: + /* send down reused MNCC */ + mncc->msg_type = MNCC_SETUP_RSP; + mncc->callref = remote; + mncc_down(buf, length); + break; + case MNCC_SETUP_COMPL_IND: + /* send down reused MNCC */ + mncc->msg_type = MNCC_SETUP_COMPL_REQ; + mncc->callref = remote; + mncc_down(buf, length); + break; + case MNCC_DISC_IND: + /* send down reused MNCC */ + mncc->msg_type = MNCC_DISC_REQ; + mncc->callref = remote; + mncc_down(buf, length); + break; + case MNCC_REL_IND: + /* send down reused MNCC */ + mncc->msg_type = MNCC_REL_REQ; + mncc->callref = remote; + mncc_down(buf, length); + destroy_cross(cross); + break; + } +} + +void mncc_cross_handle(void) +{ + queue_t *queue; + + while (queue_head) { + /* remove from head */ + queue = queue_head; + queue_head = queue->next; + + cross_mncc_up(queue->buf, queue->length); + free(queue); + } +} + +int mncc_cross_init(void) +{ + mncc_up = cross_mncc_up_queue; + + PDEBUG(DMNCC, DEBUG_DEBUG, "MNCC crossconnect initialized, waiting for connection.\n"); + + return 0; +} + +void mncc_cross_exit(void) +{ + while (cross_head) + destroy_cross(cross_head); + + PDEBUG(DMNCC, DEBUG_DEBUG, "MNCC crossconnect removed.\n"); +} + diff --git a/src/common/mncc_cross.h b/src/common/mncc_cross.h new file mode 100644 index 0000000..10f2b0d --- /dev/null +++ b/src/common/mncc_cross.h @@ -0,0 +1,5 @@ + +void mncc_cross_handle(void); +int mncc_cross_init(void); +void mncc_cross_exit(void); + -- cgit v1.2.3