aboutsummaryrefslogtreecommitdiffstats
path: root/src/common
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2017-10-29 07:58:20 +0100
committerAndreas Eversberg <jolly@eversberg.eu>2017-11-05 16:59:58 +0100
commite8a3306eee9eadd8d8d2547ed4e6c012a155109b (patch)
treee65787758a31f14f6df8f0062a4025d9bbc7fcf3 /src/common
parent87a21a285a121b4309b57a3d7e066124b7f26270 (diff)
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.
Diffstat (limited to 'src/common')
-rw-r--r--src/common/Makefile.am1
-rw-r--r--src/common/main_mobile.c51
-rw-r--r--src/common/mncc_cross.c240
-rw-r--r--src/common/mncc_cross.h5
4 files changed, 291 insertions, 6 deletions
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 <rate>\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 <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 <jolly@eversberg.eu>
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#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);
+