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.
This commit is contained in:
Andreas Eversberg
2017-10-29 07:58:20 +01:00
parent 87a21a285a
commit e8a3306eee
5 changed files with 330 additions and 30 deletions

View File

@@ -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 \

View File

@@ -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_sock && call_audiodev[0]) {
fprintf(stderr, "You selected MNCC interface, but it cannot be used with call device (headset).\n");
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 && echo_test) {
fprintf(stderr, "You selected MNCC interface, but it cannot be used with echo test.\n");
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), but it cannot be used with echo test.\n");
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 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 socket interface, but it cannot be used with echo test.\n");
return;
}
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();
}

240
src/common/mncc_cross.c Normal file
View File

@@ -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");
}

5
src/common/mncc_cross.h Normal file
View File

@@ -0,0 +1,5 @@
void mncc_cross_handle(void);
int mncc_cross_init(void);
void mncc_cross_exit(void);