Restructure: Move mncc from common code to 'libmncc'
This commit is contained in:
parent
c1d911d516
commit
d4fe5c2c2e
|
@ -0,0 +1,10 @@
|
|||
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||
|
||||
noinst_LIBRARIES = libmncc.a
|
||||
|
||||
libmncc_a_SOURCES = \
|
||||
mncc_console.c \
|
||||
mncc_cross.c \
|
||||
mncc_sock.c \
|
||||
testton.c \
|
||||
cause.c
|
|
@ -0,0 +1,48 @@
|
|||
/* Clear cause names
|
||||
*
|
||||
* (C) 2016 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 "cause.h"
|
||||
|
||||
const char *cause_name(int cause)
|
||||
{
|
||||
static char cause_str[16];
|
||||
|
||||
switch (cause) {
|
||||
case CAUSE_NORMAL:
|
||||
return "hangup";
|
||||
case CAUSE_BUSY:
|
||||
return "busy";
|
||||
case CAUSE_NOANSWER:
|
||||
return "no-answer";
|
||||
case CAUSE_OUTOFORDER:
|
||||
return "out-of-order";
|
||||
case CAUSE_INVALNUMBER:
|
||||
return "invalid-number";
|
||||
case CAUSE_NOCHANNEL:
|
||||
return "no-channel";
|
||||
case CAUSE_TEMPFAIL:
|
||||
return "link-failure";
|
||||
default:
|
||||
sprintf(cause_str, "cause=%d\n", cause);
|
||||
return cause_str;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#define CAUSE_NORMAL 16
|
||||
#define CAUSE_BUSY 17
|
||||
#define CAUSE_NOANSWER 19
|
||||
#define CAUSE_OUTOFORDER 27
|
||||
#define CAUSE_INVALNUMBER 28
|
||||
#define CAUSE_NOCHANNEL 34
|
||||
#define CAUSE_TEMPFAIL 41
|
||||
#define CAUSE_INVALCALLREF 81
|
||||
|
||||
#define LOCATION_USER 0
|
||||
#define LOCATION_PRIVATE_LOCAL 1
|
||||
|
||||
const char *cause_name(int cause);
|
||||
|
||||
|
|
@ -0,0 +1,347 @@
|
|||
|
||||
#define MNCC_SETUP_REQ 0x0101
|
||||
#define MNCC_SETUP_IND 0x0102
|
||||
#define MNCC_SETUP_RSP 0x0103
|
||||
#define MNCC_SETUP_CNF 0x0104
|
||||
#define MNCC_SETUP_COMPL_REQ 0x0105
|
||||
#define MNCC_SETUP_COMPL_IND 0x0106
|
||||
/* MNCC_REJ_* is perfomed via MNCC_REL_* */
|
||||
#define MNCC_CALL_CONF_IND 0x0107
|
||||
#define MNCC_CALL_PROC_REQ 0x0108
|
||||
#define MNCC_PROGRESS_REQ 0x0109
|
||||
#define MNCC_ALERT_REQ 0x010a
|
||||
#define MNCC_ALERT_IND 0x010b
|
||||
#define MNCC_NOTIFY_REQ 0x010c
|
||||
#define MNCC_NOTIFY_IND 0x010d
|
||||
#define MNCC_DISC_REQ 0x010e
|
||||
#define MNCC_DISC_IND 0x010f
|
||||
#define MNCC_REL_REQ 0x0110
|
||||
#define MNCC_REL_IND 0x0111
|
||||
#define MNCC_REL_CNF 0x0112
|
||||
#define MNCC_FACILITY_REQ 0x0113
|
||||
#define MNCC_FACILITY_IND 0x0114
|
||||
#define MNCC_START_DTMF_IND 0x0115
|
||||
#define MNCC_START_DTMF_RSP 0x0116
|
||||
#define MNCC_START_DTMF_REJ 0x0117
|
||||
#define MNCC_STOP_DTMF_IND 0x0118
|
||||
#define MNCC_STOP_DTMF_RSP 0x0119
|
||||
#define MNCC_MODIFY_REQ 0x011a
|
||||
#define MNCC_MODIFY_IND 0x011b
|
||||
#define MNCC_MODIFY_RSP 0x011c
|
||||
#define MNCC_MODIFY_CNF 0x011d
|
||||
#define MNCC_MODIFY_REJ 0x011e
|
||||
#define MNCC_HOLD_IND 0x011f
|
||||
#define MNCC_HOLD_CNF 0x0120
|
||||
#define MNCC_HOLD_REJ 0x0121
|
||||
#define MNCC_RETRIEVE_IND 0x0122
|
||||
#define MNCC_RETRIEVE_CNF 0x0123
|
||||
#define MNCC_RETRIEVE_REJ 0x0124
|
||||
#define MNCC_USERINFO_REQ 0x0125
|
||||
#define MNCC_USERINFO_IND 0x0126
|
||||
#define MNCC_REJ_REQ 0x0127
|
||||
#define MNCC_REJ_IND 0x0128
|
||||
#define MNCC_PROGRESS_IND 0x0129
|
||||
#define MNCC_CALL_PROC_IND 0x012a
|
||||
#define MNCC_CALL_CONF_REQ 0x012b
|
||||
#define MNCC_START_DTMF_REQ 0x012c
|
||||
#define MNCC_STOP_DTMF_REQ 0x012d
|
||||
#define MNCC_HOLD_REQ 0x012e
|
||||
#define MNCC_RETRIEVE_REQ 0x012f
|
||||
|
||||
#define MNCC_BRIDGE 0x0200
|
||||
#define MNCC_FRAME_RECV 0x0201
|
||||
#define MNCC_FRAME_DROP 0x0202
|
||||
#define MNCC_LCHAN_MODIFY 0x0203
|
||||
#define MNCC_RTP_CREATE 0x0204
|
||||
#define MNCC_RTP_CONNECT 0x0205
|
||||
#define MNCC_RTP_FREE 0x0206
|
||||
|
||||
#define GSM_TCHF_FRAME 0x0300
|
||||
#define GSM_TCHF_FRAME_EFR 0x0301
|
||||
#define GSM_TCHH_FRAME 0x0302
|
||||
#define GSM_TCH_FRAME_AMR 0x0303
|
||||
#define ANALOG_8000HZ 0x0380
|
||||
#define GSM_BAD_FRAME 0x03ff
|
||||
|
||||
#define MNCC_SOCKET_HELLO 0x0400
|
||||
|
||||
#define GSM_MAX_FACILITY 128
|
||||
#define GSM_MAX_SSVERSION 128
|
||||
#define GSM_MAX_USERUSER 128
|
||||
|
||||
#define MNCC_F_BEARER_CAP 0x0001
|
||||
#define MNCC_F_CALLED 0x0002
|
||||
#define MNCC_F_CALLING 0x0004
|
||||
#define MNCC_F_REDIRECTING 0x0008
|
||||
#define MNCC_F_CONNECTED 0x0010
|
||||
#define MNCC_F_CAUSE 0x0020
|
||||
#define MNCC_F_USERUSER 0x0040
|
||||
#define MNCC_F_PROGRESS 0x0080
|
||||
#define MNCC_F_EMERGENCY 0x0100
|
||||
#define MNCC_F_FACILITY 0x0200
|
||||
#define MNCC_F_SSVERSION 0x0400
|
||||
#define MNCC_F_CCCAP 0x0800
|
||||
#define MNCC_F_KEYPAD 0x1000
|
||||
#define MNCC_F_SIGNAL 0x2000
|
||||
|
||||
#define GSM_MAX_FACILITY 128
|
||||
#define GSM_MAX_SSVERSION 128
|
||||
#define GSM_MAX_USERUSER 128
|
||||
|
||||
/* GSM 04.08 Bearer Capability: Information Transfer Capability */
|
||||
enum gsm48_bcap_itcap {
|
||||
GSM48_BCAP_ITCAP_SPEECH = 0,
|
||||
GSM48_BCAP_ITCAP_UNR_DIG_INF = 1,
|
||||
GSM48_BCAP_ITCAP_3k1_AUDIO = 2,
|
||||
GSM48_BCAP_ITCAP_FAX_G3 = 3,
|
||||
GSM48_BCAP_ITCAP_OTHER = 5,
|
||||
GSM48_BCAP_ITCAP_RESERVED = 7,
|
||||
};
|
||||
|
||||
/* GSM 04.08 Bearer Capability: Transfer Mode */
|
||||
enum gsm48_bcap_tmod {
|
||||
GSM48_BCAP_TMOD_CIRCUIT = 0,
|
||||
GSM48_BCAP_TMOD_PACKET = 1,
|
||||
};
|
||||
|
||||
/* GSM 04.08 Bearer Capability: Coding Standard */
|
||||
enum gsm48_bcap_coding {
|
||||
GSM48_BCAP_CODING_GSM_STD = 0,
|
||||
};
|
||||
|
||||
/* GSM 04.08 Bearer Capability: Radio Channel Requirements */
|
||||
enum gsm48_bcap_rrq {
|
||||
GSM48_BCAP_RRQ_FR_ONLY = 1,
|
||||
GSM48_BCAP_RRQ_DUAL_HR = 2,
|
||||
GSM48_BCAP_RRQ_DUAL_FR = 3,
|
||||
};
|
||||
|
||||
/* GSM 04.08 Bearer Capability: Rate Adaption */
|
||||
enum gsm48_bcap_ra {
|
||||
GSM48_BCAP_RA_NONE = 0,
|
||||
GSM48_BCAP_RA_V110_X30 = 1,
|
||||
GSM48_BCAP_RA_X31 = 2,
|
||||
GSM48_BCAP_RA_OTHER = 3,
|
||||
};
|
||||
|
||||
/* GSM 04.08 Bearer Capability: Signaling access protocol */
|
||||
enum gsm48_bcap_sig_access {
|
||||
GSM48_BCAP_SA_I440_I450 = 1,
|
||||
GSM48_BCAP_SA_X21 = 2,
|
||||
GSM48_BCAP_SA_X28_DP_IN = 3,
|
||||
GSM48_BCAP_SA_X28_DP_UN = 4,
|
||||
GSM48_BCAP_SA_X28_NDP = 5,
|
||||
GSM48_BCAP_SA_X32 = 6,
|
||||
};
|
||||
|
||||
/* GSM 04.08 Bearer Capability: User Rate */
|
||||
enum gsm48_bcap_user_rate {
|
||||
GSM48_BCAP_UR_300 = 1,
|
||||
GSM48_BCAP_UR_1200 = 2,
|
||||
GSM48_BCAP_UR_2400 = 3,
|
||||
GSM48_BCAP_UR_4800 = 4,
|
||||
GSM48_BCAP_UR_9600 = 5,
|
||||
GSM48_BCAP_UR_12000 = 6,
|
||||
GSM48_BCAP_UR_1200_75 = 7,
|
||||
};
|
||||
|
||||
/* GSM 04.08 Bearer Capability: Parity */
|
||||
enum gsm48_bcap_parity {
|
||||
GSM48_BCAP_PAR_ODD = 0,
|
||||
GSM48_BCAP_PAR_EVEN = 2,
|
||||
GSM48_BCAP_PAR_NONE = 3,
|
||||
GSM48_BCAP_PAR_ZERO = 4,
|
||||
GSM48_BCAP_PAR_ONE = 5,
|
||||
};
|
||||
|
||||
/* GSM 04.08 Bearer Capability: Intermediate Rate */
|
||||
enum gsm48_bcap_interm_rate {
|
||||
GSM48_BCAP_IR_8k = 2,
|
||||
GSM48_BCAP_IR_16k = 3,
|
||||
};
|
||||
|
||||
/* GSM 04.08 Bearer Capability: Transparency */
|
||||
enum gsm48_bcap_transp {
|
||||
GSM48_BCAP_TR_TRANSP = 0,
|
||||
GSM48_BCAP_TR_RLP = 1,
|
||||
GSM48_BCAP_TR_TR_PREF = 2,
|
||||
GSM48_BCAP_TR_RLP_PREF = 3,
|
||||
};
|
||||
|
||||
/* GSM 04.08 Bearer Capability: Modem Type */
|
||||
enum gsm48_bcap_modem_type {
|
||||
GSM48_BCAP_MT_NONE = 0,
|
||||
GSM48_BCAP_MT_V21 = 1,
|
||||
GSM48_BCAP_MT_V22 = 2,
|
||||
GSM48_BCAP_MT_V22bis = 3,
|
||||
GSM48_BCAP_MT_V23 = 4,
|
||||
GSM48_BCAP_MT_V26ter = 5,
|
||||
GSM48_BCAP_MT_V32 = 6,
|
||||
GSM48_BCAP_MT_UNDEF = 7,
|
||||
GSM48_BCAP_MT_AUTO_1 = 8,
|
||||
};
|
||||
|
||||
/* GSM 04.08 Bearer Capability: Speech Version Indication */
|
||||
enum gsm48_bcap_speech_ver {
|
||||
GSM48_BCAP_SV_FR = 0,
|
||||
GSM48_BCAP_SV_HR = 1,
|
||||
GSM48_BCAP_SV_EFR = 2,
|
||||
GSM48_BCAP_SV_AMR_F = 4,
|
||||
GSM48_BCAP_SV_AMR_H = 5,
|
||||
BCAP_ANALOG_8000HZ = 0x80,
|
||||
};
|
||||
|
||||
/* Expanded fields from GSM TS 04.08, Table 10.5.102 */
|
||||
struct gsm_mncc_bearer_cap {
|
||||
int transfer; /* Information Transfer Capability */
|
||||
int mode; /* Transfer Mode */
|
||||
int coding; /* Coding Standard */
|
||||
int radio; /* Radio Channel Requirement */
|
||||
int speech_ctm; /* CTM text telephony indication */
|
||||
int speech_ver[8]; /* Speech version indication */
|
||||
struct {
|
||||
enum gsm48_bcap_ra rate_adaption;
|
||||
enum gsm48_bcap_sig_access sig_access;
|
||||
int async;
|
||||
int nr_stop_bits;
|
||||
int nr_data_bits;
|
||||
enum gsm48_bcap_user_rate user_rate;
|
||||
enum gsm48_bcap_parity parity;
|
||||
enum gsm48_bcap_interm_rate interm_rate;
|
||||
enum gsm48_bcap_transp transp;
|
||||
enum gsm48_bcap_modem_type modem_type;
|
||||
} data;
|
||||
};
|
||||
|
||||
struct gsm_mncc_number {
|
||||
int type;
|
||||
int plan;
|
||||
int present;
|
||||
int screen;
|
||||
char number[33];
|
||||
};
|
||||
|
||||
struct gsm_mncc_cause {
|
||||
int location;
|
||||
int coding;
|
||||
int rec;
|
||||
int rec_val;
|
||||
int value;
|
||||
int diag_len;
|
||||
char diag[32];
|
||||
};
|
||||
|
||||
struct gsm_mncc_useruser {
|
||||
int proto;
|
||||
char info[GSM_MAX_USERUSER + 1]; /* + termination char */
|
||||
};
|
||||
|
||||
struct gsm_mncc_progress {
|
||||
int coding;
|
||||
int location;
|
||||
int descr;
|
||||
};
|
||||
|
||||
struct gsm_mncc_facility {
|
||||
int len;
|
||||
char info[GSM_MAX_FACILITY];
|
||||
};
|
||||
|
||||
struct gsm_mncc_ssversion {
|
||||
int len;
|
||||
char info[GSM_MAX_SSVERSION];
|
||||
};
|
||||
|
||||
struct gsm_mncc_cccap {
|
||||
int dtmf;
|
||||
int pcp;
|
||||
};
|
||||
|
||||
enum {
|
||||
GSM_MNCC_BCAP_SPEECH = 0,
|
||||
GSM_MNCC_BCAP_UNR_DIG = 1,
|
||||
GSM_MNCC_BCAP_AUDIO = 2,
|
||||
GSM_MNCC_BCAP_FAX_G3 = 3,
|
||||
GSM_MNCC_BCAP_OTHER_ITC = 5,
|
||||
GSM_MNCC_BCAP_RESERVED = 7,
|
||||
};
|
||||
|
||||
enum {
|
||||
GSM_LCHAN_NONE,
|
||||
GSM_LCHAN_SDCCH,
|
||||
GSM_LCHAN_TCH_F,
|
||||
GSM_LCHAN_TCH_H,
|
||||
GSM_LCHAN_UNKNOWN,
|
||||
GSM_LCHAN_CCCH,
|
||||
GSM_LCHAN_PDTCH,
|
||||
_GSM_LCHAN_MAX
|
||||
};
|
||||
|
||||
struct gsm_mncc {
|
||||
/* context based information */
|
||||
uint32_t msg_type;
|
||||
uint32_t callref;
|
||||
|
||||
/* which fields are present */
|
||||
uint32_t fields;
|
||||
|
||||
/* data derived informations (MNCC_F_ based) */
|
||||
struct gsm_mncc_bearer_cap bearer_cap;
|
||||
struct gsm_mncc_number called;
|
||||
struct gsm_mncc_number calling;
|
||||
struct gsm_mncc_number redirecting;
|
||||
struct gsm_mncc_number connected;
|
||||
struct gsm_mncc_cause cause;
|
||||
struct gsm_mncc_progress progress;
|
||||
struct gsm_mncc_useruser useruser;
|
||||
struct gsm_mncc_facility facility;
|
||||
struct gsm_mncc_cccap cccap;
|
||||
struct gsm_mncc_ssversion ssversion;
|
||||
struct {
|
||||
int sup;
|
||||
int inv;
|
||||
} clir;
|
||||
int signal;
|
||||
|
||||
/* data derived information, not MNCC_F based */
|
||||
int keypad;
|
||||
int more;
|
||||
int notify; /* 0..127 */
|
||||
int emergency;
|
||||
char imsi[16];
|
||||
|
||||
unsigned char lchan_type;
|
||||
unsigned char lchan_mode;
|
||||
};
|
||||
|
||||
struct gsm_data_frame {
|
||||
uint32_t msg_type;
|
||||
uint32_t callref;
|
||||
unsigned char data[0];
|
||||
};
|
||||
|
||||
struct gsm_mncc_rtp {
|
||||
uint32_t msg_type;
|
||||
uint32_t callref;
|
||||
uint32_t ip;
|
||||
uint16_t port;
|
||||
uint32_t payload_type;
|
||||
uint32_t payload_msg_type;
|
||||
};
|
||||
|
||||
|
||||
#define MNCC_SOCK_VERSION 5
|
||||
struct gsm_mncc_hello {
|
||||
uint32_t msg_type;
|
||||
uint32_t version;
|
||||
|
||||
/* send the sizes of the structs */
|
||||
uint32_t mncc_size;
|
||||
uint32_t data_frame_size;
|
||||
|
||||
/* send some offsets */
|
||||
uint32_t called_offset;
|
||||
uint32_t signal_offset;
|
||||
uint32_t emergency_offset;
|
||||
uint32_t lchan_type_offset;
|
||||
};
|
|
@ -0,0 +1,488 @@
|
|||
/* built-in console to talk to a phone
|
||||
*
|
||||
* (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
|
||||
G* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
#include "../common/sample.h"
|
||||
#include "../libsamplerate/samplerate.h"
|
||||
#include "../libjitter/jitter.h"
|
||||
#include "../common/debug.h"
|
||||
#include "testton.h"
|
||||
#include "mncc.h"
|
||||
#include "mncc_console.h"
|
||||
#include "cause.h"
|
||||
#include "../common/call.h"
|
||||
#include "../common/sound.h"
|
||||
|
||||
static int new_callref = 0; /* toward mobile */
|
||||
|
||||
enum console_state {
|
||||
CONSOLE_IDLE = 0, /* IDLE */
|
||||
CONSOLE_SETUP_RO, /* call from radio to console */
|
||||
CONSOLE_SETUP_RT, /* call from console to radio */
|
||||
CONSOLE_ALERTING_RO, /* call from radio to console */
|
||||
CONSOLE_ALERTING_RT, /* call from console to radio */
|
||||
CONSOLE_CONNECT,
|
||||
CONSOLE_DISCONNECT,
|
||||
};
|
||||
|
||||
static const char *console_state_name[] = {
|
||||
"IDLE",
|
||||
"SETUP_RO",
|
||||
"SETUP_RT",
|
||||
"ALERTING_RO",
|
||||
"ALERTING_RT",
|
||||
"CONNECT",
|
||||
"DISCONNECT",
|
||||
};
|
||||
|
||||
/* console call instance */
|
||||
typedef struct console {
|
||||
uint32_t callref;
|
||||
enum console_state state;
|
||||
int disc_cause; /* cause that has been sent by transceiver instance for release */
|
||||
char station_id[16];
|
||||
char dialing[16];
|
||||
char audiodev[64]; /* headphone interface, if used */
|
||||
int samplerate; /* sample rate of headphone interface */
|
||||
void *sound; /* headphone interface */
|
||||
int latspl; /* sample latency at headphone interface */
|
||||
samplerate_t srstate; /* patterns/announcement upsampling */
|
||||
jitter_t dejitter; /* headphone audio dejittering */
|
||||
int test_audio_pos; /* position for test tone toward mobile */
|
||||
sample_t tx_buffer[160];/* transmit audio buffer */
|
||||
int tx_buffer_pos; /* current position in transmit audio buffer */
|
||||
int dial_digits; /* number of digits to be dialed */
|
||||
int loopback; /* loopback test for echo */
|
||||
int echo_test; /* send echo back to mobile phone */
|
||||
} console_t;
|
||||
|
||||
static console_t console;
|
||||
|
||||
/* stream test music */
|
||||
int16_t *test_spl = NULL;
|
||||
int test_size = 0;
|
||||
int test_max = 0;
|
||||
|
||||
static void get_test_patterns(int16_t *samples, int length)
|
||||
{
|
||||
const int16_t *spl;
|
||||
int size, max, pos;
|
||||
|
||||
spl = test_spl;
|
||||
size = test_size;
|
||||
max = test_max;
|
||||
|
||||
/* stream sample */
|
||||
pos = console.test_audio_pos;
|
||||
while(length--) {
|
||||
if (pos >= size)
|
||||
*samples++ = 0;
|
||||
else
|
||||
*samples++ = spl[pos] >> 2;
|
||||
if (++pos == max)
|
||||
pos = 0;
|
||||
}
|
||||
console.test_audio_pos = pos;
|
||||
}
|
||||
|
||||
static void console_new_state(enum console_state state)
|
||||
{
|
||||
PDEBUG(DMNCC, DEBUG_DEBUG, "Call state '%s' -> '%s'\n", console_state_name[console.state], console_state_name[state]);
|
||||
console.state = state;
|
||||
console.test_audio_pos = 0;
|
||||
}
|
||||
|
||||
static int console_mncc_up(uint8_t *buf, int length)
|
||||
{
|
||||
struct gsm_mncc *mncc = (struct gsm_mncc *)buf;
|
||||
|
||||
if (mncc->msg_type == ANALOG_8000HZ) {
|
||||
struct gsm_data_frame *data = (struct gsm_data_frame *)buf;
|
||||
int count = 160;
|
||||
sample_t samples[count];
|
||||
|
||||
/* save audio from transceiver to jitter buffer */
|
||||
if (console.sound) {
|
||||
sample_t up[(int)((double)count * console.srstate.factor + 0.5) + 10];
|
||||
int16_to_samples(samples, (int16_t *)data->data, count);
|
||||
count = samplerate_upsample(&console.srstate, samples, count, up);
|
||||
jitter_save(&console.dejitter, up, count);
|
||||
return 0;
|
||||
}
|
||||
/* if echo test is used, send echo back to mobile */
|
||||
if (console.echo_test) {
|
||||
/* send down reused MNCC */
|
||||
mncc_down(buf, length);
|
||||
return 0;
|
||||
}
|
||||
/* if no sound is used, send test tone to mobile */
|
||||
if (console.state == CONSOLE_CONNECT) {
|
||||
/* send down reused MNCC */
|
||||
get_test_patterns((int16_t *)data->data, count);
|
||||
mncc_down(buf, length);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mncc->msg_type != MNCC_SETUP_IND && console.callref != mncc->callref) {
|
||||
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 0;
|
||||
}
|
||||
|
||||
switch(mncc->msg_type) {
|
||||
case MNCC_SETUP_IND:
|
||||
PDEBUG(DMNCC, DEBUG_INFO, "Incoming call from '%s'\n", mncc->calling.number);
|
||||
/* setup is also allowed on disconnected call */
|
||||
if (console.state == CONSOLE_DISCONNECT) {
|
||||
PDEBUG(DMNCC, DEBUG_INFO, "Releasing pending disconnected call\n");
|
||||
if (console.callref) {
|
||||
uint8_t buf[sizeof(struct gsm_mncc)];
|
||||
struct gsm_mncc *mncc = (struct gsm_mncc *)buf;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
mncc->msg_type = MNCC_REL_REQ;
|
||||
mncc->callref = console.callref;
|
||||
mncc->fields |= MNCC_F_CAUSE;
|
||||
mncc->cause.location = LOCATION_USER;
|
||||
mncc->cause.value = CAUSE_NORMAL;
|
||||
mncc_down(buf, sizeof(struct gsm_mncc));
|
||||
console.callref = 0;
|
||||
}
|
||||
console_new_state(CONSOLE_IDLE);
|
||||
}
|
||||
if (console.state != CONSOLE_IDLE) {
|
||||
PDEBUG(DMNCC, DEBUG_NOTICE, "We are busy, rejecting.\n");
|
||||
return -CAUSE_BUSY;
|
||||
}
|
||||
console.callref = mncc->callref;
|
||||
if (mncc->calling.number[0]) {
|
||||
strncpy(console.station_id, mncc->calling.number, console.dial_digits);
|
||||
console.station_id[console.dial_digits] = '\0';
|
||||
}
|
||||
strncpy(console.dialing, mncc->called.number, sizeof(console.dialing) - 1);
|
||||
console.dialing[sizeof(console.dialing) - 1] = '\0';
|
||||
console_new_state(CONSOLE_CONNECT);
|
||||
PDEBUG(DMNCC, DEBUG_INFO, "Call automatically answered\n");
|
||||
/* send down reused MNCC */
|
||||
mncc->msg_type = MNCC_SETUP_RSP;
|
||||
mncc_down(buf, length);
|
||||
break;
|
||||
case MNCC_ALERT_IND:
|
||||
PDEBUG(DMNCC, DEBUG_INFO, "Call alerting\n");
|
||||
console_new_state(CONSOLE_ALERTING_RT);
|
||||
break;
|
||||
case MNCC_SETUP_CNF:
|
||||
PDEBUG(DMNCC, DEBUG_INFO, "Call connected to '%s'\n", mncc->connected.number);
|
||||
console_new_state(CONSOLE_CONNECT);
|
||||
strncpy(console.station_id, mncc->connected.number, console.dial_digits);
|
||||
console.station_id[console.dial_digits] = '\0';
|
||||
/* send down reused MNCC */
|
||||
mncc->msg_type = MNCC_SETUP_COMPL_REQ;
|
||||
mncc_down(buf, length);
|
||||
break;
|
||||
case MNCC_DISC_IND:
|
||||
PDEBUG(DMNCC, DEBUG_INFO, "Call disconnected (%s)\n", cause_name(mncc->cause.value));
|
||||
console_new_state(CONSOLE_DISCONNECT);
|
||||
console.disc_cause = mncc->cause.value;
|
||||
break;
|
||||
case MNCC_REL_IND:
|
||||
PDEBUG(DMNCC, DEBUG_INFO, "Call released (%s)\n", cause_name(mncc->cause.value));
|
||||
console_new_state(CONSOLE_IDLE);
|
||||
console.callref = 0;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int console_init(const char *station_id, const char *audiodev, int samplerate, int latency, int dial_digits, int loopback, int echo_test)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
init_testton();
|
||||
|
||||
memset(&console, 0, sizeof(console));
|
||||
strncpy(console.station_id, station_id, sizeof(console.station_id) - 1);
|
||||
strncpy(console.audiodev, audiodev, sizeof(console.audiodev) - 1);
|
||||
console.samplerate = samplerate;
|
||||
console.latspl = latency * samplerate / 1000;
|
||||
console.dial_digits = dial_digits;
|
||||
console.loopback = loopback;
|
||||
console.echo_test = echo_test;
|
||||
|
||||
mncc_up = console_mncc_up;
|
||||
|
||||
if (!audiodev[0])
|
||||
return 0;
|
||||
|
||||
rc = init_samplerate(&console.srstate, 8000.0, (double)samplerate, 3300.0);
|
||||
if (rc < 0) {
|
||||
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to init sample rate conversion!\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = jitter_create(&console.dejitter, samplerate / 5);
|
||||
if (rc < 0) {
|
||||
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to create and init dejitter buffer!\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
console_cleanup();
|
||||
return rc;
|
||||
}
|
||||
|
||||
int console_open_audio(int latspl)
|
||||
{
|
||||
if (!console.audiodev[0])
|
||||
return 0;
|
||||
|
||||
/* open sound device for call control */
|
||||
/* use factor 1.4 of speech level for complete range of sound card */
|
||||
console.sound = sound_open(console.audiodev, NULL, NULL, 1, 0.0, console.samplerate, latspl, 1.4, 4000.0);
|
||||
if (!console.sound) {
|
||||
PDEBUG(DSENDER, DEBUG_ERROR, "No sound device!\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int console_start_audio(void)
|
||||
{
|
||||
if (!console.audiodev[0])
|
||||
return 0;
|
||||
|
||||
return sound_start(console.sound);
|
||||
}
|
||||
|
||||
void console_cleanup(void)
|
||||
{
|
||||
/* close sound devoice */
|
||||
if (console.sound)
|
||||
sound_close(console.sound);
|
||||
|
||||
jitter_destroy(&console.dejitter);
|
||||
}
|
||||
|
||||
static char console_text[256];
|
||||
static char console_clear[256];
|
||||
static int console_len = 0;
|
||||
|
||||
static void process_ui(int c)
|
||||
{
|
||||
char text[256];
|
||||
int len;
|
||||
|
||||
switch (console.state) {
|
||||
case CONSOLE_IDLE:
|
||||
if (c > 0) {
|
||||
if (c >= '0' && c <= '9' && (int)strlen(console.station_id) < console.dial_digits) {
|
||||
console.station_id[strlen(console.station_id) + 1] = '\0';
|
||||
console.station_id[strlen(console.station_id)] = c;
|
||||
}
|
||||
if ((c == 8 || c == 127) && strlen(console.station_id))
|
||||
console.station_id[strlen(console.station_id) - 1] = '\0';
|
||||
dial_after_hangup:
|
||||
if (c == 'd' && (int)strlen(console.station_id) == console.dial_digits) {
|
||||
int callref = ++new_callref;
|
||||
uint8_t buf[sizeof(struct gsm_mncc)];
|
||||
struct gsm_mncc *mncc = (struct gsm_mncc *)buf;
|
||||
|
||||
PDEBUG(DMNCC, DEBUG_INFO, "Outgoing call to '%s'\n", console.station_id);
|
||||
console.dialing[0] = '\0';
|
||||
console_new_state(CONSOLE_SETUP_RT);
|
||||
console.callref = callref;
|
||||
memset(buf, 0, sizeof(buf));
|
||||
mncc->msg_type = MNCC_SETUP_REQ;
|
||||
mncc->callref = callref;
|
||||
mncc->fields |= MNCC_F_CALLED;
|
||||
strncpy(mncc->called.number, console.station_id, sizeof(mncc->called.number) - 1);
|
||||
mncc->called.type = 0; /* dialing is of type 'unknown' */
|
||||
mncc->lchan_type = GSM_LCHAN_TCH_F;
|
||||
mncc->fields |= MNCC_F_BEARER_CAP;
|
||||
mncc->bearer_cap.speech_ver[0] = BCAP_ANALOG_8000HZ;
|
||||
mncc->bearer_cap.speech_ver[1] = -1;
|
||||
mncc_down(buf, sizeof(struct gsm_mncc));
|
||||
}
|
||||
}
|
||||
if (console.dial_digits != (int)strlen(console.station_id))
|
||||
sprintf(text, "on-hook: %s%s (enter digits 0..9)\r", console.station_id, "..............." + 15 - console.dial_digits + strlen(console.station_id));
|
||||
else
|
||||
sprintf(text, "on-hook: %s (press d=dial)\r", console.station_id);
|
||||
break;
|
||||
case CONSOLE_SETUP_RO:
|
||||
case CONSOLE_SETUP_RT:
|
||||
case CONSOLE_ALERTING_RO:
|
||||
case CONSOLE_ALERTING_RT:
|
||||
case CONSOLE_CONNECT:
|
||||
case CONSOLE_DISCONNECT:
|
||||
if (c > 0) {
|
||||
if (c == 'h' || (c == 'd' && console.state == CONSOLE_DISCONNECT)) {
|
||||
PDEBUG(DMNCC, DEBUG_INFO, "Call hangup\n");
|
||||
console_new_state(CONSOLE_IDLE);
|
||||
if (console.callref) {
|
||||
uint8_t buf[sizeof(struct gsm_mncc)];
|
||||
struct gsm_mncc *mncc = (struct gsm_mncc *)buf;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
mncc->msg_type = MNCC_REL_REQ;
|
||||
mncc->callref = console.callref;
|
||||
mncc->fields |= MNCC_F_CAUSE;
|
||||
mncc->cause.location = LOCATION_USER;
|
||||
mncc->cause.value = CAUSE_NORMAL;
|
||||
mncc_down(buf, sizeof(struct gsm_mncc));
|
||||
console.callref = 0;
|
||||
}
|
||||
if (c == 'd')
|
||||
goto dial_after_hangup;
|
||||
}
|
||||
}
|
||||
if (console.state == CONSOLE_SETUP_RT)
|
||||
sprintf(text, "call setup: %s (press h=hangup)\r", console.station_id);
|
||||
if (console.state == CONSOLE_ALERTING_RT)
|
||||
sprintf(text, "call ringing: %s (press h=hangup)\r", console.station_id);
|
||||
if (console.state == CONSOLE_CONNECT) {
|
||||
if (console.dialing[0])
|
||||
sprintf(text, "call active: %s->%s (press h=hangup)\r", console.station_id, console.dialing);
|
||||
else
|
||||
sprintf(text, "call active: %s (press h=hangup)\r", console.station_id);
|
||||
}
|
||||
if (console.state == CONSOLE_DISCONNECT)
|
||||
sprintf(text, "call disconnected: %s (press h=hangup d=redial)\r", cause_name(console.disc_cause));
|
||||
break;
|
||||
}
|
||||
/* skip if nothing has changed */
|
||||
len = strlen(text);
|
||||
if (console_len == len && !memcmp(console_text, text, len))
|
||||
return;
|
||||
clear_console_text();
|
||||
console_len = len;
|
||||
memcpy(console_text, text, len);
|
||||
memset(console_clear, ' ', len - 1);
|
||||
console_clear[len - 1] = '\r';
|
||||
print_console_text();
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void clear_console_text(void)
|
||||
{
|
||||
if (!console_len)
|
||||
return;
|
||||
|
||||
fwrite(console_clear, console_len, 1, stdout);
|
||||
// note: fflused by user of this function
|
||||
console_len = 0;
|
||||
}
|
||||
|
||||
void print_console_text(void)
|
||||
{
|
||||
if (!console_len)
|
||||
return;
|
||||
|
||||
printf("\033[1;37m");
|
||||
fwrite(console_text, console_len, 1, stdout);
|
||||
printf("\033[0;39m");
|
||||
}
|
||||
|
||||
/* get keys from keyboad to control call via console
|
||||
* returns 1 on exit (ctrl+c) */
|
||||
void process_console(int c)
|
||||
{
|
||||
if (!console.loopback)
|
||||
process_ui(c);
|
||||
|
||||
if (!console.sound)
|
||||
return;
|
||||
|
||||
/* handle audio, if sound device is used */
|
||||
sample_t samples[console.latspl + 10], *samples_list[1];
|
||||
uint8_t *power_list[1];
|
||||
double rf_level_db[1];
|
||||
int count;
|
||||
int rc;
|
||||
|
||||
count = sound_get_tosend(console.sound, console.latspl);
|
||||
if (count < 0) {
|
||||
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to get samples in buffer (rc = %d)!\n", count);
|
||||
if (count == -EPIPE)
|
||||
PDEBUG(DSENDER, DEBUG_ERROR, "Trying to recover.\n");
|
||||
return;
|
||||
}
|
||||
if (count > 0) {
|
||||
jitter_load(&console.dejitter, samples, count);
|
||||
samples_list[0] = samples;
|
||||
power_list[0] = NULL;
|
||||
rc = sound_write(console.sound, samples_list, power_list, count, NULL, NULL, 1);
|
||||
if (rc < 0) {
|
||||
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to write TX data to sound device (rc = %d)\n", rc);
|
||||
if (rc == -EPIPE)
|
||||
PDEBUG(DSENDER, DEBUG_ERROR, "Trying to recover.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
samples_list[0] = samples;
|
||||
count = sound_read(console.sound, samples_list, console.latspl, 1, rf_level_db);
|
||||
if (count < 0) {
|
||||
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to read from sound device (rc = %d)!\n", count);
|
||||
if (count == -EPIPE)
|
||||
PDEBUG(DSENDER, DEBUG_ERROR, "Trying to recover.\n");
|
||||
return;
|
||||
}
|
||||
if (count) {
|
||||
int i;
|
||||
|
||||
if (console.loopback == 3)
|
||||
jitter_save(&console.dejitter, samples, count);
|
||||
count = samplerate_downsample(&console.srstate, samples, count);
|
||||
/* put samples into ring buffer */
|
||||
for (i = 0; i < count; i++) {
|
||||
console.tx_buffer[console.tx_buffer_pos] = samples[i];
|
||||
/* if ring buffer wrapps, deliver data down to call process */
|
||||
if (++console.tx_buffer_pos == 160) {
|
||||
console.tx_buffer_pos = 0;
|
||||
/* only if we have a call */
|
||||
if (console.callref) {
|
||||
uint8_t buf[sizeof(struct gsm_data_frame) + 160 * sizeof(int16_t)];
|
||||
struct gsm_data_frame *data = (struct gsm_data_frame *)buf;
|
||||
|
||||
data->msg_type = ANALOG_8000HZ;
|
||||
data->callref = console.callref;
|
||||
samples_to_int16((int16_t *)data->data, console.tx_buffer, 160);
|
||||
mncc_down(buf, sizeof(struct gsm_mncc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
int console_init(const char *station_id, const char *audiodev, int samplerate, int latency, int dial_digits, int loopback, int echo_test);
|
||||
void console_cleanup(void);
|
||||
int console_open_audio(int latspl);
|
||||
int console_start_audio(void);
|
||||
void console_process(int c);
|
||||
void clear_console_text(void);
|
||||
void print_console_text(void);
|
||||
void process_console(int 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 "../common/sample.h"
|
||||
#include "../common/debug.h"
|
||||
#include "../common/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");
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
void mncc_cross_handle(void);
|
||||
int mncc_cross_init(void);
|
||||
void mncc_cross_exit(void);
|
||||
|
|
@ -0,0 +1,239 @@
|
|||
/* Mobie Network Call Control (MNCC) socket handling
|
||||
*
|
||||
* (C) 2016 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 <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
#include "../common/sample.h"
|
||||
#include "../common/debug.h"
|
||||
#include "../common/call.h"
|
||||
#include "cause.h"
|
||||
#include "mncc_sock.h"
|
||||
|
||||
static int listen_sock = -1;
|
||||
static int mncc_sock = -1;
|
||||
|
||||
/* write to mncc socket, return error or -EIO if no socket connection */
|
||||
static int mncc_write(uint8_t *buf, int length)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (mncc_sock <= 0) {
|
||||
PDEBUG(DMNCC, DEBUG_NOTICE, "We have no MNCC connection, rejecting.\n");
|
||||
return -CAUSE_TEMPFAIL;
|
||||
}
|
||||
rc = send(mncc_sock, buf, length, 0);
|
||||
if (rc < 0) {
|
||||
PDEBUG(DMNCC, DEBUG_ERROR, "MNCC connection failed (errno = %d).\n", errno);
|
||||
mncc_sock_close();
|
||||
return -CAUSE_TEMPFAIL;
|
||||
}
|
||||
if (rc != length) {
|
||||
PDEBUG(DMNCC, DEBUG_NOTICE, "MNCC write failed.\n");
|
||||
mncc_sock_close();
|
||||
return -CAUSE_TEMPFAIL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* read from mncc socket */
|
||||
static int mncc_read(void)
|
||||
{
|
||||
uint8_t buf[sizeof(struct gsm_mncc)+1024];
|
||||
int rc;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
rc = recv(mncc_sock, buf, sizeof(buf), 0);
|
||||
if (rc == 0) {
|
||||
PDEBUG(DMNCC, DEBUG_NOTICE, "MNCC connection closed.\n");
|
||||
mncc_sock_close();
|
||||
return 0;
|
||||
}
|
||||
if (rc < 0) {
|
||||
if (errno == EWOULDBLOCK)
|
||||
return -errno;
|
||||
PDEBUG(DMNCC, DEBUG_ERROR, "MNCC connection failed (errno = %d).\n", errno);
|
||||
mncc_sock_close();
|
||||
return -errno;
|
||||
}
|
||||
|
||||
mncc_down(buf, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void mncc_hello(void)
|
||||
{
|
||||
struct gsm_mncc_hello hello;
|
||||
|
||||
memset(&hello, 0, sizeof(hello));
|
||||
hello.msg_type = MNCC_SOCKET_HELLO;
|
||||
hello.version = MNCC_SOCK_VERSION;
|
||||
hello.mncc_size = sizeof(struct gsm_mncc);
|
||||
hello.data_frame_size = sizeof(struct gsm_data_frame);
|
||||
hello.called_offset = offsetof(struct gsm_mncc, called);
|
||||
hello.signal_offset = offsetof(struct gsm_mncc, signal);
|
||||
hello.emergency_offset = offsetof(struct gsm_mncc, emergency);
|
||||
hello.lchan_type_offset = offsetof(struct gsm_mncc, lchan_type);
|
||||
|
||||
mncc_write((uint8_t *) &hello, sizeof(hello));
|
||||
}
|
||||
|
||||
|
||||
static int mncc_accept(void)
|
||||
{
|
||||
struct sockaddr_un __attribute__((__unused__)) un_addr;
|
||||
socklen_t __attribute__((__unused__)) len;
|
||||
int flags;
|
||||
int rc;
|
||||
|
||||
len = sizeof(un_addr);
|
||||
rc = accept(listen_sock, (struct sockaddr *) &un_addr, &len);
|
||||
if (rc < 0) {
|
||||
if (errno == EWOULDBLOCK)
|
||||
return 0;
|
||||
PDEBUG(DMNCC, DEBUG_ERROR, "Failed to accept incoming connection (errno=%d).\n", errno);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (mncc_sock > 0) {
|
||||
PDEBUG(DMNCC, DEBUG_NOTICE, "Rejecting multiple incoming connections.\n");
|
||||
close(rc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
mncc_sock = rc;
|
||||
flags = fcntl(mncc_sock, F_GETFL, 0);
|
||||
rc = fcntl(mncc_sock, F_SETFL, flags | O_NONBLOCK);
|
||||
if (rc < 0) {
|
||||
PDEBUG(DMNCC, DEBUG_ERROR, "Failed to set socket into non-blocking IO mode.\n");
|
||||
mncc_sock_close();
|
||||
return rc;
|
||||
}
|
||||
|
||||
PDEBUG(DMNCC, DEBUG_NOTICE, "MNCC socket connected.\n");
|
||||
|
||||
mncc_hello();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void mncc_sock_handle(void)
|
||||
{
|
||||
mncc_accept();
|
||||
|
||||
if (mncc_sock > 0) {
|
||||
while ((mncc_read()) > 0)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mncc_sock_close(void)
|
||||
{
|
||||
if (mncc_sock > 0) {
|
||||
PDEBUG(DMNCC, DEBUG_NOTICE, "MNCC socket disconnected.\n");
|
||||
close(mncc_sock);
|
||||
mncc_sock = -1;
|
||||
/* clear all call instances */
|
||||
mncc_flush();
|
||||
}
|
||||
}
|
||||
|
||||
int mncc_sock_init(const char *sock_name)
|
||||
{
|
||||
struct sockaddr_un local;
|
||||
unsigned int namelen;
|
||||
int flags;
|
||||
int rc;
|
||||
|
||||
listen_sock = socket(PF_UNIX, SOCK_SEQPACKET, 0);
|
||||
if (listen_sock < 0) {
|
||||
PDEBUG(DMNCC, DEBUG_ERROR, "Failed to create socket.\n");
|
||||
return listen_sock;
|
||||
}
|
||||
|
||||
local.sun_family = AF_UNIX;
|
||||
strncpy(local.sun_path, sock_name, sizeof(local.sun_path));
|
||||
local.sun_path[sizeof(local.sun_path) - 1] = '\0';
|
||||
unlink(local.sun_path);
|
||||
|
||||
/* we use the same magic that X11 uses in Xtranssock.c for
|
||||
* calculating the proper length of the sockaddr */
|
||||
#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
|
||||
local.sun_len = strlen(local.sun_path);
|
||||
#endif
|
||||
#if defined(BSD44SOCKETS) || defined(SUN_LEN)
|
||||
namelen = SUN_LEN(&local);
|
||||
#else
|
||||
namelen = strlen(local.sun_path) +
|
||||
offsetof(struct sockaddr_un, sun_path);
|
||||
#endif
|
||||
|
||||
rc = bind(listen_sock, (struct sockaddr *) &local, namelen);
|
||||
if (rc < 0) {
|
||||
PDEBUG(DMNCC, DEBUG_ERROR, "Failed to bind the unix domain "
|
||||
"socket. '%s'\n", local.sun_path);
|
||||
mncc_sock_exit();
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = listen(listen_sock, 0);
|
||||
if (rc < 0) {
|
||||
PDEBUG(DMNCC, DEBUG_ERROR, "Failed to listen.\n");
|
||||
mncc_sock_exit();
|
||||
return rc;
|
||||
}
|
||||
|
||||
flags = fcntl(listen_sock, F_GETFL, 0);
|
||||
flags = 0;
|
||||
rc = fcntl(listen_sock, F_SETFL, flags | O_NONBLOCK);
|
||||
if (rc < 0) {
|
||||
PDEBUG(DMNCC, DEBUG_ERROR, "Failed to set socket into non-blocking IO mode.\n");
|
||||
mncc_sock_exit();
|
||||
return rc;
|
||||
}
|
||||
|
||||
mncc_up = mncc_write;
|
||||
|
||||
PDEBUG(DMNCC, DEBUG_DEBUG, "MNCC socket at '%s' initialized, waiting for connection.\n", sock_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mncc_sock_exit(void)
|
||||
{
|
||||
mncc_sock_close();
|
||||
|
||||
if (listen_sock > 0) {
|
||||
close(listen_sock);
|
||||
listen_sock = -1;
|
||||
}
|
||||
|
||||
PDEBUG(DMNCC, DEBUG_DEBUG, "MNCC socket removed.\n");
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#include "mncc.h"
|
||||
|
||||
void mncc_sock_handle(void);
|
||||
void mncc_sock_close(void);
|
||||
int mncc_sock_init(const char *sock_name);
|
||||
void mncc_sock_exit(void);
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,3 @@
|
|||
|
||||
void init_testton(void);
|
||||
|
Loading…
Reference in New Issue