Move more common code; make it usable for SS5 and R1
This commit is contained in:
parent
91e8521071
commit
2483f1c2b1
|
@ -3,6 +3,9 @@ AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
|||
noinst_LIBRARIES = libcommon.a
|
||||
|
||||
libcommon_a_SOURCES = \
|
||||
common.c \
|
||||
mf.c \
|
||||
dsp.c
|
||||
dsp.c \
|
||||
sit.c \
|
||||
display_status.c
|
||||
|
||||
|
|
|
@ -0,0 +1,345 @@
|
|||
/* common process
|
||||
*
|
||||
* (C) 2020 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 <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include "../libdebug/debug.h"
|
||||
#include "../libtimer/timer.h"
|
||||
#include "../libselect/select.h"
|
||||
#include "../libosmocc/endpoint.h"
|
||||
#include "../libosmocc/helper.h"
|
||||
#include "common.h"
|
||||
|
||||
/*
|
||||
* generate messages towards CC
|
||||
*/
|
||||
|
||||
void reject_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, uint8_t isdn_cause)
|
||||
|
||||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
/* create osmo-cc message */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REJ_IND);
|
||||
/* cause */
|
||||
osmo_cc_add_ie_cause(new_msg, OSMO_CC_LOCATION_BEYOND_INTERWORKING, isdn_cause, 0, 0);
|
||||
/* send message to osmo-cc */
|
||||
osmo_cc_ll_msg(cc_ep, callref, new_msg);
|
||||
}
|
||||
|
||||
void release_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, uint8_t isdn_cause)
|
||||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
/* create osmo-cc message */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND);
|
||||
/* cause */
|
||||
osmo_cc_add_ie_cause(new_msg, OSMO_CC_LOCATION_BEYOND_INTERWORKING, isdn_cause, 0, 0);
|
||||
/* send message to osmo-cc */
|
||||
osmo_cc_ll_msg(cc_ep, callref, new_msg);
|
||||
}
|
||||
|
||||
void disconnect_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, uint8_t isdn_cause)
|
||||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
/* create osmo-cc message */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_DISC_IND);
|
||||
/* progress */
|
||||
osmo_cc_add_ie_progress(new_msg, OSMO_CC_CODING_ITU_T, OSMO_CC_LOCATION_BEYOND_INTERWORKING, OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE);
|
||||
/* cause */
|
||||
osmo_cc_add_ie_cause(new_msg, OSMO_CC_LOCATION_BEYOND_INTERWORKING, isdn_cause, 0, 0);
|
||||
/* send message to osmo-cc */
|
||||
osmo_cc_ll_msg(cc_ep, callref, new_msg);
|
||||
}
|
||||
|
||||
void proceed_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, const char *sdp)
|
||||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
/* create osmo-cc message */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_PROC_IND);
|
||||
/* progress */
|
||||
osmo_cc_add_ie_progress(new_msg, OSMO_CC_CODING_ITU_T, OSMO_CC_LOCATION_BEYOND_INTERWORKING, OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE);
|
||||
/* sdp */
|
||||
osmo_cc_add_ie_sdp(new_msg, sdp);
|
||||
/* send message to osmo-cc */
|
||||
osmo_cc_ll_msg(cc_ep, callref, new_msg);
|
||||
}
|
||||
|
||||
void alert_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref)
|
||||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
/* create osmo-cc message */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_ALERT_IND);
|
||||
/* send message to osmo-cc */
|
||||
osmo_cc_ll_msg(cc_ep, callref, new_msg);
|
||||
}
|
||||
|
||||
void answer_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref)
|
||||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
/* create osmo-cc message */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_CNF);
|
||||
/* send message to osmo-cc */
|
||||
osmo_cc_ll_msg(cc_ep, callref, new_msg);
|
||||
}
|
||||
|
||||
void setup_comp_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref)
|
||||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
/* create osmo-cc message */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_COMP_IND);
|
||||
/* send message to osmo-cc */
|
||||
osmo_cc_ll_msg(cc_ep, callref, new_msg);
|
||||
}
|
||||
|
||||
/*
|
||||
* dial string generation and parsing
|
||||
*/
|
||||
|
||||
static char *prefix_1_digit[] = { "1", "7", NULL };
|
||||
static char *prefix_2_digit[] = {
|
||||
"20", "27", "28", "30", "31", "32", "33", "34", "36", "39", "40", "41",
|
||||
"43", "44", "45", "46", "47", "48", "49", "51", "52", "53", "54", "55",
|
||||
"56", "57", "58", "60", "61", "62", "63", "64", "65", "66", "81", "82",
|
||||
"83", "84", "86", "89", "90", "91", "92", "93", "94", "95", "98",
|
||||
NULL };
|
||||
|
||||
/* use number and number type to generate an SS5 dial string
|
||||
* the digits are checked if they can be dialed
|
||||
* if the number is already in SS5 format, only digits are checked
|
||||
*/
|
||||
int generate_dial_string(uint8_t type, const char *dialing, char *string, int string_size)
|
||||
{
|
||||
int full_string_given = 0;
|
||||
int i, ii;
|
||||
|
||||
if ((int)strlen(dialing) + 4 > string_size) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "Dial string is too long for our digit register, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* check for correct digits */
|
||||
for (i = 0, ii = strlen(dialing); i < ii; i++) {
|
||||
/* string may start with 'a' or 'b', but then 'c' must be the last digit */
|
||||
if (dialing[i] == 'a' || dialing[i] == 'b') {
|
||||
full_string_given = 1;
|
||||
if (dialing[ii - 1] != 'c') {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "Number starts with 'a' (KP1) or 'b' (KP2) but missing 'c' (ST) at the end, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* remove check of last digit 'c' */
|
||||
--ii;
|
||||
continue;
|
||||
}
|
||||
/* string must only consist of numerical digits and '*' (code 11) and '#' (code 12) */
|
||||
if (!strchr("0123456789*#", dialing[i])) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "Number has invalid digits, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* if full string with 'a'/'b' and 'c' is given, we have complete dial string */
|
||||
if (full_string_given) {
|
||||
strcpy(string, dialing);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if number is not of international type, create national dial string */
|
||||
if (type != OSMO_CC_TYPE_INTERNATIONAL) {
|
||||
// make GCC happy
|
||||
strcpy(string, "a0");
|
||||
strcat(string, dialing);
|
||||
strcat(string, "c");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check international prefix with length of 1 digit */
|
||||
if ((int)strlen(dialing) < 1) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; prefix_1_digit[i]; i++) {
|
||||
if (prefix_1_digit[i][0] == dialing[0])
|
||||
break;
|
||||
}
|
||||
/* if number is of international type, create international dial string */
|
||||
if (prefix_1_digit[i]) {
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wformat-overflow"
|
||||
sprintf(string, "b%c0%sc", dialing[0], dialing + 1);
|
||||
#pragma GCC diagnostic pop
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check international prefix with length of 2 digits */
|
||||
if ((int)strlen(dialing) < 2) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; prefix_2_digit[i]; i++) {
|
||||
if (prefix_2_digit[i][0] == dialing[0]
|
||||
&& prefix_2_digit[i][1] == dialing[1])
|
||||
break;
|
||||
}
|
||||
/* if number is of international type, create international dial string */
|
||||
if (prefix_2_digit[i]) {
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wformat-overflow"
|
||||
sprintf(string, "b%c%c0%sc", dialing[0], dialing[1], dialing + 2);
|
||||
#pragma GCC diagnostic pop
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check international prefix with length of 3 digits */
|
||||
if ((int)strlen(dialing) < 3) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* if number is of international type, create international dial string */
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wformat-overflow"
|
||||
sprintf(string, "b%c%c%c0%sc", dialing[0], dialing[1], dialing[2], dialing + 3);
|
||||
#pragma GCC diagnostic pop
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* parse received SS5 dial string and convert it into a national or international number */
|
||||
int parse_dial_string(uint8_t *type, char *dialing, int dialing_size, const char *string)
|
||||
{
|
||||
char kp_digit;
|
||||
const char *prefix;
|
||||
int length;
|
||||
int i;
|
||||
|
||||
/* remove start and stop digits, set string after start digit and set length to digits between start and stop */
|
||||
if (string[0] != 'a' && string[0] != 'b') {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "Received digits do not start with 'a' (KP1) nor 'b' (KP2), call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
length = strlen(string) - 1;
|
||||
if (string[length] != 'c') {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "Received digits do end with 'c' (ST), call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
kp_digit = *string++;
|
||||
length--;
|
||||
if (length > dialing_size - 1) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "Received dial string is too long, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!length) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "Nothing dialed between KP and ST, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* received local or national call */
|
||||
if (kp_digit == 'a') {
|
||||
/* remove discriminaing digit */
|
||||
if (string[0] == '0') {
|
||||
string++;
|
||||
length--;
|
||||
*type = OSMO_CC_TYPE_NATIONAL;
|
||||
} else
|
||||
*type = OSMO_CC_TYPE_UNKNOWN;
|
||||
strncpy(dialing, string, length);
|
||||
dialing[length] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check international prefix with length of 1 digit */
|
||||
if (length < 2) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; prefix_1_digit[i]; i++) {
|
||||
if (prefix_1_digit[i][0] == string[0])
|
||||
break;
|
||||
}
|
||||
/* if number is of international type, create international dial string */
|
||||
if (prefix_1_digit[i]) {
|
||||
prefix = string;
|
||||
string += 1;
|
||||
length -= 1;
|
||||
/* remove discriminaing digit */
|
||||
string++;
|
||||
--length;
|
||||
*type = OSMO_CC_TYPE_INTERNATIONAL;
|
||||
dialing[0] = prefix[0];
|
||||
strncpy(dialing + 1, string, length);
|
||||
dialing[1 + length] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check international prefix with length of 2 digits */
|
||||
if (length < 3) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; prefix_2_digit[i]; i++) {
|
||||
if (prefix_2_digit[i][0] == string[0]
|
||||
&& prefix_2_digit[i][1] == string[1])
|
||||
break;
|
||||
}
|
||||
/* if number is of international type, create international dial string */
|
||||
if (prefix_2_digit[i]) {
|
||||
prefix = string;
|
||||
string += 2;
|
||||
length -= 2;
|
||||
/* remove discriminaing digit */
|
||||
string++;
|
||||
--length;
|
||||
*type = OSMO_CC_TYPE_INTERNATIONAL;
|
||||
dialing[0] = prefix[0];
|
||||
dialing[1] = prefix[1];
|
||||
strncpy(dialing + 2, string, length);
|
||||
dialing[2 + length] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check international prefix with length of 3 digits */
|
||||
if (length < 4) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* if number is of international type, create international dial string */
|
||||
prefix = string;
|
||||
string += 3;
|
||||
length -= 3;
|
||||
/* remove discriminaing digit */
|
||||
string++;
|
||||
--length;
|
||||
*type = OSMO_CC_TYPE_INTERNATIONAL;
|
||||
dialing[0] = prefix[0];
|
||||
dialing[1] = prefix[1];
|
||||
dialing[2] = prefix[2];
|
||||
strncpy(dialing + 3, string, length);
|
||||
dialing[3 + length] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
void reject_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, uint8_t isdn_cause);
|
||||
void release_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, uint8_t isdn_cause);
|
||||
void disconnect_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, uint8_t isdn_cause);
|
||||
void proceed_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref, const char *sdp);
|
||||
void alert_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref);
|
||||
void answer_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref);
|
||||
void setup_comp_call(osmo_cc_endpoint_t *cc_ep, uint32_t callref);
|
||||
int generate_dial_string(uint8_t type, const char *dialing, char *string, int string_size);
|
||||
int parse_dial_string(uint8_t *type, char *dialing, int dialing_size, const char *string);
|
||||
|
|
@ -4,6 +4,6 @@
|
|||
|
||||
void display_status_on(int on);
|
||||
void display_status_start(void);
|
||||
void display_status_line(const char *if_name, int link_count, const char *from_id, const char *to_id, enum ss5_state state);
|
||||
void display_status_line(const char *if_name, int link_count, const char *from_id, const char *to_id, const char *state_name);
|
||||
void display_status_end(void);
|
||||
|
|
@ -22,7 +22,6 @@
|
|||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include "../libdebug/debug.h"
|
||||
#include "ss5.h"
|
||||
#include "display.h"
|
||||
|
||||
static int status_on = 0;
|
||||
|
@ -97,7 +96,7 @@ void display_status_start(void)
|
|||
line_count = 1;
|
||||
}
|
||||
|
||||
void display_status_line(const char *if_name, int link_count, const char *from_id, const char *to_id, enum ss5_state state)
|
||||
void display_status_line(const char *if_name, int link_count, const char *from_id, const char *to_id, const char *state_name)
|
||||
{
|
||||
char line[MAX_DISPLAY_WIDTH + 4096];
|
||||
char color[MAX_DISPLAY_WIDTH + 4096];
|
||||
|
@ -136,8 +135,8 @@ void display_status_line(const char *if_name, int link_count, const char *from_i
|
|||
index += 4 + from_len + 2 + 4 + to_len;
|
||||
line[index] = '\0';
|
||||
|
||||
strcpy(line + index, ss5_state_names[state]);
|
||||
memset(color + index, 7, strlen(ss5_state_names[state]));
|
||||
strcpy(line + index, state_name);
|
||||
memset(color + index, 7, strlen(state_name));
|
||||
}
|
||||
|
||||
/* store line without CR, but not more than MAX_DISPLAY_WIDTH - 1 */
|
298
src/common/dsp.c
298
src/common/dsp.c
|
@ -30,6 +30,7 @@
|
|||
#include <arpa/inet.h>
|
||||
#include "../libdebug/debug.h"
|
||||
#include "dsp.h"
|
||||
#include "sit.h"
|
||||
|
||||
//#define DEBUG_DEMODULATOR
|
||||
|
||||
|
@ -37,8 +38,9 @@
|
|||
#define db2level(db) pow(10, (double)(db) / 20.0)
|
||||
#define level2db(level) (20 * log10(level))
|
||||
|
||||
/* SS5 defines -9 dB (tx), -16 dB (min) for SF, R1 defines -8/-20 dB (tx), -27 dB (min) for SF */
|
||||
static double tone_dbm[NUM_TONES] = { -7, -7, -7, -7, -7, -7, -9, -9 };
|
||||
static double tone_freq[NUM_TONES] = { 700, 900, 1100, 1300, 1500, 1700, 2400, 2600 };
|
||||
static double tone_freq[NUM_TONES] = { 700, 900, 1100, 1300, 1500, 1700, 2600, 2400 };
|
||||
static double tone_width[NUM_TONES] = { 25, 25, 25, 25, 25, 25, 25, 25 };
|
||||
|
||||
static double tone_min_dbm[NUM_TONES] = { -14, -14, -14, -14, -14, -14, -16, -16 };
|
||||
|
@ -46,11 +48,15 @@ static double tone_min_ampl_sq[NUM_TONES];
|
|||
static double tone_diff_db[NUM_TONES] = { 4, 4, 4, 4, 4, 4, 5, 5 };
|
||||
static double tone_diff_ampl_sq[NUM_TONES];
|
||||
|
||||
#define INTERRUPT_DURATION 0.015
|
||||
#define SPLIT_DURATION 0.030
|
||||
#define MF_RECOGNITION 0.025
|
||||
void dsp_set_sf(double sf_db, double sf_min_db)
|
||||
{
|
||||
tone_dbm[6] = sf_db;
|
||||
tone_dbm[7] = sf_db;
|
||||
tone_min_dbm[6] = sf_min_db;
|
||||
tone_min_dbm[7] = sf_min_db;
|
||||
}
|
||||
|
||||
int dsp_init_inst(dsp_t *dsp, void *priv, const char *name, double samplerate, int crosstalk, int comfort_noise, int delay_ms, double sense_db)
|
||||
int dsp_init_inst(dsp_t *dsp, void *priv, const char *name, double samplerate, int crosstalk, int comfort_noise, int delay_ms, double sense_db, double interrupt_recognition, double split_recognition, double mf_recognition, double kp_digit_duration, double other_digit_duration, double digit_pause, double pulse_pause, double notch, int ss5)
|
||||
{
|
||||
double tone_amplitude[NUM_TONES];
|
||||
int t;
|
||||
|
@ -64,9 +70,14 @@ int dsp_init_inst(dsp_t *dsp, void *priv, const char *name, double samplerate, i
|
|||
dsp->samplerate = samplerate;
|
||||
dsp->crosstalk = crosstalk;
|
||||
dsp->comfort_noise = comfort_noise;
|
||||
dsp->interrupt_duration = (int)(1000.0 * INTERRUPT_DURATION);
|
||||
dsp->split_duration = (int)(1000.0 * SPLIT_DURATION);
|
||||
dsp->mf_detect_duration = (int)(1000.0 * MF_RECOGNITION);
|
||||
// NO interrupt recognition, because the filter itself has a slow response
|
||||
// dsp->interrupt_recognition = (int)(1000.0 * interrupt_recognition);
|
||||
dsp->split_recognition = (int)(1000.0 * split_recognition);
|
||||
dsp->mf_recognition = (int)(1000.0 * mf_recognition);
|
||||
dsp->kp_digit_duration = kp_digit_duration;
|
||||
dsp->other_digit_duration = other_digit_duration;
|
||||
dsp->digit_pause = digit_pause;
|
||||
dsp->pulse_pause = pulse_pause;
|
||||
dsp->ms_per_sample = 1000.0 / samplerate;
|
||||
dsp->detect_tone = ' ';
|
||||
|
||||
|
@ -83,7 +94,7 @@ int dsp_init_inst(dsp_t *dsp, void *priv, const char *name, double samplerate, i
|
|||
return -EINVAL;
|
||||
|
||||
/* init MF demodulator */
|
||||
dsp->mf_demod = mf_demod_init(samplerate, NUM_TONES, tone_freq, tone_width);
|
||||
dsp->mf_demod = mf_demod_init(samplerate, NUM_TONES - (!ss5), tone_freq, tone_width);
|
||||
if (!dsp->mf_mod)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -98,6 +109,12 @@ int dsp_init_inst(dsp_t *dsp, void *priv, const char *name, double samplerate, i
|
|||
if (rc < 0)
|
||||
abort();
|
||||
|
||||
/* notch filter */
|
||||
if (notch) {
|
||||
dsp->notch = 1;
|
||||
iir_notch_init(&dsp->notch_filter, notch, (int)dsp->samplerate, 1, 4);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -150,17 +167,13 @@ static struct dsp_digits {
|
|||
{ 'a', 0x04 + 0x20 }, /* KP1 */
|
||||
{ 'b', 0x08 + 0x20 }, /* KP2 */
|
||||
{ 'c', 0x10 + 0x20 }, /* ST */
|
||||
{ 'A', 0x40 }, /* 2400 answer, acknowledge */
|
||||
{ 'B', 0x80 }, /* 2600 busy */
|
||||
{ 'B', 0x40 }, /* 2600 busy */
|
||||
{ 'A', 0x80 }, /* 2400 answer, acknowledge */
|
||||
{ 'C', 0x40 + 0x80 }, /* 2600+2400 clear forward */
|
||||
{ ' ', 0 }, /* silence */
|
||||
{ 0 , 0 },
|
||||
};
|
||||
|
||||
#define KP_DIGIT_DURATION 0.100
|
||||
#define OTHER_DIGIT_DURATION 0.055
|
||||
#define DIGIT_PAUSE 0.055
|
||||
|
||||
/* set signaling tone duration threshold */
|
||||
void set_sig_detect_duration(dsp_t *dsp, double duration_AB, double duration_C)
|
||||
{
|
||||
|
@ -170,7 +183,7 @@ void set_sig_detect_duration(dsp_t *dsp, double duration_AB, double duration_C)
|
|||
}
|
||||
|
||||
/* set given tone with duration (ms) or continuously (0) */
|
||||
void set_tone(dsp_t *dsp, char tone, double duration)
|
||||
int set_tone(dsp_t *dsp, char tone, double duration)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -178,7 +191,7 @@ void set_tone(dsp_t *dsp, char tone, double duration)
|
|||
|
||||
if (!tone) {
|
||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Remove tone\n");
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; dsp_digits[i].tone; i++) {
|
||||
|
@ -186,12 +199,48 @@ void set_tone(dsp_t *dsp, char tone, double duration)
|
|||
dsp->tone_mask = dsp_digits[i].mask;
|
||||
dsp->tone = tone;
|
||||
dsp->tone_duration = (int)(dsp->samplerate * duration);
|
||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Set tone=\'%c\' duration=%.0fms (mask = 0x%02x)\n", dsp->tone, 1000.0 * duration, dsp->tone_mask);
|
||||
return;
|
||||
if (duration)
|
||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Set tone=\'%c\' duration=%.0fms (mask = 0x%02x)\n", dsp->tone, 1000.0 * duration, dsp->tone_mask);
|
||||
else
|
||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Set tone=\'%c\' duration=continuous (mask = 0x%02x)\n", dsp->tone, dsp->tone_mask);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
PDEBUG_CHAN(DDSP, DEBUG_ERROR, "Tone '%c' does not exist.\n", tone);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
void set_tone_transparent(dsp_t *dsp, int transparent)
|
||||
{
|
||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Set tones %stransparent.\n", (!transparent) ? "non-" : "");
|
||||
dsp->tone_transparent = transparent;
|
||||
}
|
||||
|
||||
void set_sit(dsp_t *dsp, int on)
|
||||
{
|
||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Turn sit %s.\n", (on) ? "on" : "off");
|
||||
dsp->sit_on = on;
|
||||
dsp->sit_count = 0;
|
||||
}
|
||||
|
||||
static int digit_to_pulses(char digit)
|
||||
{
|
||||
if (digit >= '1' && digit <= '9')
|
||||
return digit - '0';
|
||||
if (digit == '0')
|
||||
return 10;
|
||||
if (digit == '*')
|
||||
return 11;
|
||||
if (digit == '#')
|
||||
return 12;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SF_PULSE_BREAK 0.060
|
||||
#define SF_PULSE_MAKE 0.040
|
||||
|
||||
/* get next tone from dial string, if any */
|
||||
static void get_tone_from_dial_string(dsp_t *dsp)
|
||||
{
|
||||
|
@ -200,6 +249,7 @@ static void get_tone_from_dial_string(dsp_t *dsp)
|
|||
|
||||
dsp->tone = 0;
|
||||
|
||||
next_digit:
|
||||
if (dsp->dial_index == dsp->dial_length) {
|
||||
dsp->dial_length = 0;
|
||||
dialing_complete(dsp->priv);
|
||||
|
@ -207,33 +257,73 @@ static void get_tone_from_dial_string(dsp_t *dsp)
|
|||
}
|
||||
|
||||
/* get alternating tone/pause from dial string */
|
||||
if (!dsp->digit_pause) {
|
||||
/* digit on */
|
||||
tone = dsp->dial_string[dsp->dial_index++];
|
||||
dsp->digit_pause = 1;
|
||||
if (tone == 'a' || tone == 'b')
|
||||
duration = KP_DIGIT_DURATION;
|
||||
else
|
||||
duration = OTHER_DIGIT_DURATION;
|
||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Send digit \'%c\' from dial string\n", tone);
|
||||
if (!dsp->digit_on) {
|
||||
/* start digit */
|
||||
if (!dsp->pulsedialing) {
|
||||
/* MF digit on */
|
||||
tone = dsp->dial_string[dsp->dial_index++];
|
||||
if (tone == 'a' || tone == 'b')
|
||||
duration = dsp->kp_digit_duration;
|
||||
else
|
||||
duration = dsp->other_digit_duration;
|
||||
dsp->digit_on = 1;
|
||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Send digit \'%c\' from dial string\n", tone);
|
||||
} else {
|
||||
/* SF pulse */
|
||||
dsp->pulse_num = digit_to_pulses(dsp->dial_string[dsp->dial_index++]);
|
||||
if (dsp->pulse_num == 0) {
|
||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Skipping digit \'%c\' from dial string that cannot be puled\n", dsp->dial_string[dsp->dial_index]);
|
||||
goto next_digit;
|
||||
}
|
||||
/* pulse on */
|
||||
tone = 'B';
|
||||
dsp->pulse_on = 1;
|
||||
duration = SF_PULSE_BREAK;
|
||||
dsp->digit_on = 1;
|
||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Send digit \'%c\' from dial string as pulses\n", dsp->dial_string[dsp->dial_index]);
|
||||
}
|
||||
} else {
|
||||
/* digit pause */
|
||||
tone = ' ';
|
||||
dsp->digit_pause = 0;
|
||||
duration = DIGIT_PAUSE;
|
||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Send pause after digit from dial string\n");
|
||||
/* ongoing digit */
|
||||
if (dsp->pulse_num) {
|
||||
if (dsp->pulse_on) {
|
||||
/* pulse off */
|
||||
tone = ' ';
|
||||
dsp->pulse_on = 0;
|
||||
duration = SF_PULSE_MAKE;
|
||||
if (++dsp->pulse_count == dsp->pulse_num) {
|
||||
dsp->pulse_count = 0;
|
||||
dsp->digit_on = 0;
|
||||
duration = dsp->pulse_pause;
|
||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Send pause after pulsing digits from dial string\n");
|
||||
}
|
||||
} else {
|
||||
/* pulse on */
|
||||
tone = 'B';
|
||||
dsp->pulse_on = 1;
|
||||
duration = SF_PULSE_BREAK;
|
||||
}
|
||||
} else {
|
||||
/* digit pause */
|
||||
tone = ' ';
|
||||
dsp->digit_on = 0;
|
||||
duration = dsp->digit_pause;
|
||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Send pause after digit from dial string\n");
|
||||
}
|
||||
}
|
||||
|
||||
set_tone(dsp, tone, duration);
|
||||
}
|
||||
|
||||
/* set given dial string */
|
||||
void set_dial_string(dsp_t *dsp, const char *dial)
|
||||
void set_dial_string(dsp_t *dsp, const char *dial, int pulsedialing)
|
||||
{
|
||||
dsp->digit_pause = 0;
|
||||
dsp->digit_on = 0;
|
||||
strncpy(dsp->dial_string, dial, sizeof(dsp->dial_string) - 1);
|
||||
dsp->dial_index = 0;
|
||||
dsp->dial_length = strlen(dsp->dial_string);
|
||||
dsp->pulse_on = 0;
|
||||
dsp->pulse_count = 0;
|
||||
dsp->pulsedialing = pulsedialing;
|
||||
}
|
||||
|
||||
/* determine which tones to be modulated, get next tone, if elapsed */
|
||||
|
@ -267,7 +357,7 @@ static int assemble_tones(dsp_t *dsp, uint32_t *mask, int length)
|
|||
|
||||
/* detection array for one frequency */
|
||||
static char decode_one[8] =
|
||||
{ ' ', ' ', ' ', ' ', ' ', ' ', 'A', 'B' }; /* A = 2400, B = 2600 */
|
||||
{ ' ', ' ', ' ', ' ', ' ', ' ', 'B', 'A' }; /* A = 2400, B = 2600 */
|
||||
|
||||
/* detection matrix for two frequencies */
|
||||
static char decode_two[8][8] =
|
||||
|
@ -282,28 +372,36 @@ static char decode_two[8][8] =
|
|||
{ ' ', ' ', ' ', ' ', ' ', ' ', 'C', ' ' }
|
||||
};
|
||||
|
||||
#define NONE_MIN_LEVEL_SQUARED
|
||||
|
||||
/* determine which tone is played */
|
||||
static void detect_tones(dsp_t *dsp, sample_t *samples, sample_t **levels_squared, int length, int incoming)
|
||||
{
|
||||
int f1, f2;
|
||||
double f1_level_squared, f2_level_squared;
|
||||
char tone;
|
||||
int s, t;
|
||||
int s, t, l;
|
||||
|
||||
for (s = 0; s < length; s++) {
|
||||
/* mute if split duration reached */
|
||||
if (dsp->split_duration && dsp->split_count == dsp->split_duration)
|
||||
samples[s] = 0.0;
|
||||
/* only perform tone detection every millisecond */
|
||||
dsp->detect_interval += dsp->ms_per_sample;
|
||||
if (dsp->detect_interval < 1.0)
|
||||
/* only perform tone detection/muting every millisecond */
|
||||
dsp->ms_interval += dsp->ms_per_sample;
|
||||
dsp->ms_count++;
|
||||
if (dsp->ms_interval < 1.0)
|
||||
continue;
|
||||
dsp->detect_interval -= 1.0;
|
||||
|
||||
if (incoming) {
|
||||
/* mute/notch if split duration reached (this is the chunk, see below for the rest) */
|
||||
if (dsp->split_recognition && dsp->split_count == dsp->split_recognition) {
|
||||
/* l is the number of samples required to mute for this interval of one milliseconds */
|
||||
l = (dsp->ms_count < s + 1) ? dsp->ms_count : s + 1;
|
||||
if (dsp->notch)
|
||||
iir_process(&dsp->notch_filter, samples + s + 1 - l, l);
|
||||
else
|
||||
memset(samples + s + 1 - l, 0, l * sizeof(*samples));
|
||||
}
|
||||
|
||||
dsp->ms_interval -= 1.0;
|
||||
dsp->ms_count = 0;
|
||||
|
||||
#ifdef DEBUG_DEMODULATOR
|
||||
if (incoming) {
|
||||
for (t = 0; t < dsp->mf_demod->tones; t++) {
|
||||
char level[20];
|
||||
int db;
|
||||
|
@ -315,8 +413,8 @@ static void detect_tones(dsp_t *dsp, sample_t *samples, sample_t **levels_square
|
|||
printf("%s|", level);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/* find the tone which is the loudest */
|
||||
f1 = -1;
|
||||
|
@ -344,7 +442,10 @@ static void detect_tones(dsp_t *dsp, sample_t *samples, sample_t **levels_square
|
|||
if (f2 >= 0 && f2_level_squared < tone_min_ampl_sq[f2])
|
||||
f2 = -1;
|
||||
|
||||
// printf("%s f1=%.0f (%.1f dBm) f2=%.0f (%.1f dBm)\n", CHAN, (f1 >= 0) ? tone_freq[f1] : 0, level2db(sqrt(f1_level_squared)), (f2 >= 0) ? tone_freq[f2] : 0, level2db(sqrt(f2_level_squared)));
|
||||
#ifdef DEBUG_DEMODULATOR
|
||||
if (incoming)
|
||||
printf("%s f1=%.0f (%.1f dBm) f2=%.0f (%.1f dBm)\n", CHAN, (f1 >= 0) ? tone_freq[f1] : 0, level2db(sqrt(f1_level_squared)), (f2 >= 0) ? tone_freq[f2] : 0, level2db(sqrt(f2_level_squared)));
|
||||
#endif
|
||||
/* check if no, one or two tones are detected */
|
||||
if (f1 < 0)
|
||||
tone = ' ';
|
||||
|
@ -356,25 +457,34 @@ static void detect_tones(dsp_t *dsp, sample_t *samples, sample_t **levels_square
|
|||
else
|
||||
tone = decode_two[f1][f2];
|
||||
}
|
||||
//printf("tone=%c\n", tone);
|
||||
|
||||
/* process interrupt counting, keep tone until interrupt counter expires */
|
||||
if (dsp->detect_tone != ' ' && tone != dsp->detect_tone) {
|
||||
if (dsp->interrupt_count < dsp->interrupt_duration) {
|
||||
dsp->interrupt_count++;
|
||||
tone = dsp->detect_tone;
|
||||
if (dsp->interrupt_recognition) {
|
||||
if (dsp->detect_tone != ' ' && tone != dsp->detect_tone) {
|
||||
#ifdef DEBUG_DEMODULATOR
|
||||
if (!dsp->interrupt_count) if (incoming)
|
||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "interruption detected s=%d old='%c' new='%c'\n", s, dsp->detect_tone, tone);
|
||||
#endif
|
||||
if (dsp->interrupt_count < dsp->interrupt_recognition) {
|
||||
dsp->interrupt_count++;
|
||||
tone = dsp->detect_tone;
|
||||
}
|
||||
} else {
|
||||
#ifdef DEBUG_DEMODULATOR
|
||||
if (dsp->interrupt_count) if (incoming)
|
||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "interruption ceased at count %d s=%d old='%c' new='%c'\n", dsp->interrupt_count, s, dsp->detect_tone, tone);
|
||||
#endif
|
||||
dsp->interrupt_count = 0;
|
||||
}
|
||||
} else
|
||||
dsp->interrupt_count = 0;
|
||||
}
|
||||
|
||||
/* split audio, after minimum duration of detecting a tone */
|
||||
if (tone >= 'A' && tone <= 'C') {
|
||||
if (dsp->split_count < dsp->split_duration)
|
||||
if (dsp->split_count < dsp->split_recognition)
|
||||
dsp->split_count++;
|
||||
} else
|
||||
dsp->split_count = 0;
|
||||
|
||||
|
||||
/* some change in tone */
|
||||
if (dsp->detect_tone != tone) {
|
||||
if (dsp->detect_count == 0)
|
||||
|
@ -386,10 +496,14 @@ static void detect_tones(dsp_t *dsp, sample_t *samples, sample_t **levels_square
|
|||
if (dsp->detect_count < dsp->sig_detect_duration_AB)
|
||||
dsp->detect_count++;
|
||||
else {
|
||||
#ifdef DEBUG_DEMODULATOR
|
||||
if (incoming)
|
||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Tone stable '%c' (%.1f dBm)\n", tone, level2db(sqrt(f1_level_squared)));
|
||||
#endif
|
||||
/* sign tone detected */
|
||||
dsp->detect_count = 0;
|
||||
dsp->detect_tone = tone;
|
||||
receive_digit(dsp->priv, tone, level2db(sqrt(f1_level_squared)));
|
||||
receive_signal(dsp->priv, tone, level2db(sqrt(f1_level_squared)));
|
||||
}
|
||||
break;
|
||||
case 'C':
|
||||
|
@ -400,29 +514,41 @@ static void detect_tones(dsp_t *dsp, sample_t *samples, sample_t **levels_square
|
|||
/* sign tone detected */
|
||||
dsp->detect_count = 0;
|
||||
dsp->detect_tone = tone;
|
||||
receive_digit(dsp->priv, tone, level2db(sqrt(f1_level_squared)));
|
||||
receive_signal(dsp->priv, tone, level2db(sqrt(f1_level_squared)));
|
||||
}
|
||||
break;
|
||||
case ' ':
|
||||
/* tone appears or ceases */
|
||||
/* tone ceases, there is no counting here, since interrupt counting does the job for us */
|
||||
dsp->detect_count = 0;
|
||||
dsp->detect_tone = tone;
|
||||
receive_digit(dsp->priv, tone, 0.0);
|
||||
if (incoming)
|
||||
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "cease\n");
|
||||
receive_signal(dsp->priv, tone, 0.0);
|
||||
break;
|
||||
default:
|
||||
/* tone appears, wait some time */
|
||||
if (dsp->detect_count < dsp->mf_detect_duration)
|
||||
if (dsp->detect_count < dsp->mf_recognition)
|
||||
dsp->detect_count++;
|
||||
else {
|
||||
/* sign tone detected */
|
||||
dsp->detect_count = 0;
|
||||
dsp->detect_tone = tone;
|
||||
receive_digit(dsp->priv, tone, level2db(sqrt(f1_level_squared)));
|
||||
receive_signal(dsp->priv, tone, level2db(sqrt(f1_level_squared)));
|
||||
}
|
||||
}
|
||||
} else
|
||||
dsp->detect_count = 0;
|
||||
}
|
||||
|
||||
/* mute/notch if split duration reached (this is the rest, see above for each chunk) */
|
||||
if (dsp->split_recognition && dsp->split_count == dsp->split_recognition) {
|
||||
/* l is the number of samples that are left */
|
||||
l = (dsp->ms_count < s) ? dsp->ms_count : s;
|
||||
if (dsp->notch)
|
||||
iir_process(&dsp->notch_filter, samples + s - l, l);
|
||||
else
|
||||
memset(samples + s - l, 0, l * sizeof(*samples));
|
||||
}
|
||||
}
|
||||
|
||||
/* process audio from one link (source) to another (destination) */
|
||||
|
@ -440,33 +566,27 @@ static void process_audio(dsp_t *dsp_a, dsp_t *dsp_b, int length)
|
|||
jitter_load(&dsp_a->tx_dejitter, samples[0], length);
|
||||
jitter_load(&dsp_b->tx_dejitter, samples[1], length);
|
||||
|
||||
/* optionally add comfort noise */
|
||||
/* optionally add (-36 dBm) comfort noise */
|
||||
if (!dsp_a->cc_callref && dsp_a->comfort_noise) {
|
||||
for (i = 0; i < length; i++)
|
||||
samples[0][i] += (double)((int8_t)random()) / 8000.0;
|
||||
samples[0][i] += (double)((int8_t)random()) / 16384.0;
|
||||
}
|
||||
if (!dsp_b->cc_callref && dsp_b->comfort_noise) {
|
||||
for (i = 0; i < length; i++)
|
||||
samples[1][i] += (double)((int8_t)random()) / 8000.0;
|
||||
samples[1][i] += (double)((int8_t)random()) / 16385.0;
|
||||
}
|
||||
|
||||
/* send SIT, if on */
|
||||
if (dsp_a->sit_on)
|
||||
dsp_a->sit_count = sit_play(samples[0], dsp_a->sit_count, length);
|
||||
if (dsp_b->sit_on)
|
||||
dsp_b->sit_count = sit_play(samples[1], dsp_b->sit_count, length);
|
||||
|
||||
/* modulate tone/digit. if no tone has to be played (or it stopped), count is less than length */
|
||||
count1 = assemble_tones(dsp_a, mask, length);
|
||||
mf_mod(dsp_a->mf_mod, mask, samples[0], count1);
|
||||
mf_mod(dsp_a->mf_mod, mask, samples[0], count1, dsp_a->tone_transparent);
|
||||
count2 = assemble_tones(dsp_b, mask, length);
|
||||
mf_mod(dsp_b->mf_mod, mask, samples[1], count2);
|
||||
|
||||
/* optionally add some crosstalk */
|
||||
if (dsp_a->crosstalk) {
|
||||
/* use count, since it carries number of samples with signalling */
|
||||
for (i = 0; i < count1; i++)
|
||||
samples[1][i] += samples[0][i] / 70.0;
|
||||
}
|
||||
if (dsp_b->crosstalk) {
|
||||
/* use count, since it carries number of samples with signalling */
|
||||
for (i = 0; i < count2; i++)
|
||||
samples[0][i] += samples[1][i] / 70.0;
|
||||
}
|
||||
mf_mod(dsp_b->mf_mod, mask, samples[1], count2, dsp_b->tone_transparent);
|
||||
|
||||
/* ! here is the bridge from a to b and from b to a ! */
|
||||
|
||||
|
@ -492,9 +612,21 @@ static void process_audio(dsp_t *dsp_a, dsp_t *dsp_b, int length)
|
|||
|
||||
/* demodulate and call tone detector */
|
||||
mf_demod(dsp_b->mf_demod, samples[0], length, levels_squared);
|
||||
detect_tones(dsp_b->priv, samples[0], levels_squared, length, 1);
|
||||
detect_tones(dsp_b, samples[0], levels_squared, length, 1);
|
||||
mf_demod(dsp_a->mf_demod, samples[1], length, levels_squared);
|
||||
detect_tones(dsp_a->priv, samples[1], levels_squared, length, 0);
|
||||
detect_tones(dsp_a, samples[1], levels_squared, length, 0);
|
||||
|
||||
/* optionally add some crosstalk */
|
||||
if (dsp_a->crosstalk) {
|
||||
/* use count, since it carries number of samples with signalling */
|
||||
for (i = 0; i < count1; i++)
|
||||
samples[1][i] += samples[0][i] / 70.0;
|
||||
}
|
||||
if (dsp_b->crosstalk) {
|
||||
/* use count, since it carries number of samples with signalling */
|
||||
for (i = 0; i < count2; i++)
|
||||
samples[0][i] += samples[1][i] / 70.0;
|
||||
}
|
||||
|
||||
/* forward audio to CC if call exists */
|
||||
if (dsp_b->cc_callref && dsp_b->codec) {
|
||||
|
|
|
@ -16,24 +16,35 @@ typedef struct dsp {
|
|||
/* tone generation */
|
||||
mf_mod_t *mf_mod; /* MF modulator */
|
||||
char tone; /* current digit playing or 0 */
|
||||
int tone_transparent; /* mix tone with audio */
|
||||
uint32_t tone_mask; /* bit-mask of which MF tones to play */
|
||||
int tone_duration; /* counter of samples for length of tone */
|
||||
char dial_string[33]; /* stores digits when dialing number sequence */
|
||||
int dial_length; /* length of dial string, or 0 */
|
||||
int dial_index; /* current position at dial string */
|
||||
int digit_pause; /* flag that states if pause is played after digit */
|
||||
int digit_on; /* flag that states if digit is currently playing */
|
||||
double kp_digit_duration; /* duration of kp digit */
|
||||
double other_digit_duration; /* duration of other digits */
|
||||
double digit_pause; /* interdigit pause between */
|
||||
int pulsedialing; /* using pulses to dial */
|
||||
int pulse_on; /* flag that states if pulse is currently on */
|
||||
int pulse_num; /* number of pulses to send */
|
||||
int pulse_count; /* counter for pulses in one digit */
|
||||
double pulse_pause; /* interdigit pause between pulse digits */
|
||||
int sit_on, sit_count; /* sit tone generator */
|
||||
|
||||
/* tone detection */
|
||||
mf_demod_t *mf_demod; /* MF demodulator */
|
||||
double ms_per_sample; /* how many milliseconds between two samples */
|
||||
double detect_interval; /* counts milliseconds */
|
||||
int interrupt_duration; /* number of milliseconds until interruption is detected */
|
||||
double ms_interval; /* counts milliseconds */
|
||||
int ms_count; /* counts samples within millisecond interval */
|
||||
int interrupt_recognition; /* number of milliseconds until interruption is detected */
|
||||
int interrupt_count; /* counter for interrupt condition */
|
||||
int split_duration; /* number of milliseconds until split (mute) condition meats */
|
||||
int split_recognition; /* number of milliseconds until split (mute) condition meats */
|
||||
int split_count; /* counter for split condition */
|
||||
int sig_detect_duration_AB; /* signaling tone duration in milliseconds (TONE A/B) */
|
||||
int sig_detect_duration_C; /* signaling tone duration in milliseconds (TONE C) */
|
||||
int mf_detect_duration; /* MF tone duration in milliseconds */
|
||||
int mf_recognition; /* MF tone duration in milliseconds */
|
||||
int detect_count; /* counter for tone detection */
|
||||
char detect_tone; /* current tone detected or ' ' for no tone */
|
||||
|
||||
|
@ -44,6 +55,8 @@ typedef struct dsp {
|
|||
int delay_index;
|
||||
int crosstalk; /* mix crosstalk from TX to RX */
|
||||
int comfort_noise; /* add comfort noise before answer and after disconnect */
|
||||
int notch;
|
||||
iir_filter_t notch_filter; /* remove 2600 Hz */
|
||||
|
||||
/* osmo-CC */
|
||||
uint32_t cc_callref; /* ref to CC call */
|
||||
|
@ -51,16 +64,19 @@ typedef struct dsp {
|
|||
osmo_cc_session_codec_t *codec; /* selected codec */
|
||||
} dsp_t;
|
||||
|
||||
int dsp_init_inst(dsp_t *dsp, void *priv, const char *name, double samplerate, int crosstalk, int comfort_noise, int delay_ms, double sense_db);
|
||||
void dsp_set_sf(double sf_db, double sf_min_db);
|
||||
int dsp_init_inst(dsp_t *dsp, void *priv, const char *name, double samplerate, int crosstalk, int comfort_noise, int delay_ms, double sense_db, double interrupt_recognition, double split_recognition, double mf_recognition, double kp_digit_duration, double other_digit_duration, double digit_pause, double pulse_pause, double notch, int ss5);
|
||||
void dsp_cleanup_inst(dsp_t *dsp);
|
||||
void set_sig_detect_duration(dsp_t *dsp, double duration_AB, double duration_C);
|
||||
void set_tone(dsp_t *dsp, char tone, double duration);
|
||||
void set_dial_string(dsp_t *dsp, const char *dial);
|
||||
int set_tone(dsp_t *dsp, char tone, double duration);
|
||||
void set_tone_transparent(dsp_t *dsp, int transparent);
|
||||
void set_sit(dsp_t *dsp, int on);
|
||||
void set_dial_string(dsp_t *dsp, const char *dial, int pulsedialing);
|
||||
void audio_clock(dsp_t *sunset_list, dsp_t *sunrise_list, int len);
|
||||
void down_audio(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len);
|
||||
void encode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void decode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
|
||||
void receive_digit(void *priv, char digit, double dbm);
|
||||
void receive_signal(void *priv, char digit, double dbm);
|
||||
void dialing_complete(void *priv);
|
||||
|
||||
|
|
|
@ -100,13 +100,14 @@ void mf_mod_exit(mf_mod_t *mod)
|
|||
free(mod);
|
||||
}
|
||||
|
||||
void mf_mod(mf_mod_t *mod, uint32_t *mask, sample_t *samples, int length)
|
||||
void mf_mod(mf_mod_t *mod, uint32_t *mask, sample_t *samples, int length, int transparent)
|
||||
{
|
||||
int t, s;
|
||||
double phase;
|
||||
|
||||
for (s = 0; s < length; s++) {
|
||||
samples[s] = 0;
|
||||
if (!transparent)
|
||||
samples[s] = 0;
|
||||
for (t = 0; t < mod->tones; t++) {
|
||||
/* continue phase even on muted tones */
|
||||
phase = (mod->tone[t].phase += mod->tone[t].rot);
|
||||
|
|
|
@ -27,7 +27,7 @@ int mf_init(int _fast_math);
|
|||
void mf_exit(void);
|
||||
mf_mod_t *mf_mod_init(double samplerate, int tones, double *freq, double *amplitude);
|
||||
void mf_mod_exit(mf_mod_t *mod);
|
||||
void mf_mod(mf_mod_t *mod, uint32_t *mask, sample_t *samples, int length);
|
||||
void mf_mod(mf_mod_t *mod, uint32_t *mask, sample_t *samples, int length, int transparent);
|
||||
mf_demod_t *mf_demod_init(double samplerate, int tones, double *freq, double *width);
|
||||
void mf_demod_exit(mf_demod_t *demod);
|
||||
void mf_demod(mf_demod_t *demod, sample_t *samples, int length, sample_t **levels_squared);
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "../libsample/sample.h"
|
||||
#include "sit.h"
|
||||
|
||||
#define db2level(db) pow(10, (double)(db) / 20.0)
|
||||
|
||||
#define SIT_F1 950.0
|
||||
#define SIT_F2 1400.0
|
||||
#define SIT_F3 1800.0
|
||||
#define SIT_LEVEL -24.0 /* -24 dBm */
|
||||
#define SIT_DURATION 0.330 /* tone duration */
|
||||
#define SIT_PAUSE 1.0 /* tone pause */
|
||||
|
||||
static sample_t *sit_samples;
|
||||
static int sit_length = 0;
|
||||
|
||||
void init_sit(int samplerate)
|
||||
{
|
||||
int length, i;
|
||||
double level = db2level(SIT_LEVEL);
|
||||
|
||||
length = (int)((SIT_DURATION * 3 + SIT_PAUSE) * (double)samplerate);
|
||||
sit_samples = calloc(length, sizeof(*sit_samples));
|
||||
if (!sit_samples)
|
||||
abort();
|
||||
sit_length = length;
|
||||
|
||||
length = (int)(SIT_DURATION * (double)samplerate);
|
||||
for (i = 0; i < length; i++)
|
||||
sit_samples[i] = sin(2.0 * M_PI * SIT_F1 * (double)i / (double)samplerate) * level;
|
||||
for (; i < length * 2; i++)
|
||||
sit_samples[i] = sin(2.0 * M_PI * SIT_F2 * (double)i / (double)samplerate) * level;
|
||||
for (; i < length * 3; i++)
|
||||
sit_samples[i] = sin(2.0 * M_PI * SIT_F3 * (double)i / (double)samplerate) * level;
|
||||
}
|
||||
|
||||
void exit_sit(void)
|
||||
{
|
||||
free(sit_samples);
|
||||
sit_length = 0;
|
||||
}
|
||||
|
||||
int sit_play(sample_t *samples, int count, int length)
|
||||
{
|
||||
while (length--) {
|
||||
*samples++ = sit_samples[count];
|
||||
if (++count == sit_length)
|
||||
count = 0;
|
||||
}
|
||||
return count;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
void init_sit(int samplerate);
|
||||
void exit_sit(void);
|
||||
int sit_play(sample_t *samples, int count, int length);
|
||||
|
|
@ -5,7 +5,6 @@ bin_PROGRAMS = \
|
|||
|
||||
osmo_cc_ss5_endpoint_SOURCES = \
|
||||
ss5.c \
|
||||
display_status.c \
|
||||
main.c
|
||||
|
||||
osmo_cc_ss5_endpoint_LDADD = \
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
#include "../liboptions/options.h"
|
||||
#include "../libg711/g711.h"
|
||||
#include "ss5.h"
|
||||
#include "display.h"
|
||||
#include "../common/display.h"
|
||||
|
||||
ss5_endpoint_t *ss5_ep_sunset = NULL, *ss5_ep_sunrise = NULL;
|
||||
int num_kanal = 2;
|
||||
|
@ -80,16 +80,16 @@ static void print_help()
|
|||
printf(" Prevent blueboxing by making 'release-guard' 200 ms minimum length.\n");
|
||||
printf(" -x --crosstalk 1 | 0\n");
|
||||
printf(" Enable or disable some minor crosstalk. This allows you to hear\n");
|
||||
printf(" transmitted tones at a low volume. (Default is %d.).\n", crosstalk);
|
||||
printf(" transmitted tones at a low volume. (Default is %d.)\n", crosstalk);
|
||||
printf(" -d --delay <ms> | 0\n");
|
||||
printf(" Add one-way delay to the connection between two SS5 links. This allows\n");
|
||||
printf(" to hear 'acknowlege' tones delayed. (Default is %d ms.).\n", delay_ms);
|
||||
printf(" to hear 'acknowlege' tones delayed. (Default is %d ms.)\n", delay_ms);
|
||||
printf(" -n --comfort-noise 1 | 0\n");
|
||||
printf(" Add comfort noise whenever there is no audio from the remote link\n");
|
||||
printf(" (before or after call). (Default is %d ms.).\n", comfort_noise);
|
||||
printf(" (before or after call). (Default is %d ms.)\n", comfort_noise);
|
||||
printf(" --sense 0 | <db>\n");
|
||||
printf(" Change sensitivity (level) of tone detector. A bluebox must not be\n");
|
||||
printf(" that loud. (Default is %.0f dB.).\n", sense_db);
|
||||
printf(" Increase sensitivity of tone detector. A bluebox can have lower level\n");
|
||||
printf(" than what the standard requires. (Default is %.0f dB.)\n", sense_db);
|
||||
printf(" -C --cc \"<osmo-cc arg>\" [--cc ...]\n");
|
||||
printf(" --cc2 \"<osmo-cc arg>\" [--cc2 ...]\n");
|
||||
printf(" Pass arguments to Osmo-CC endpoint. Use '-cc help' for description.\n");
|
||||
|
@ -234,7 +234,7 @@ static void clock_timeout(void __attribute__((unused)) *data)
|
|||
timer_start(&clock_timer, last_time_clock - now);
|
||||
|
||||
/* call audio clock every 20ms */
|
||||
audio_clock(ss5_ep_sunset->dsp_list, ss5_ep_sunrise->dsp_list, 160);
|
||||
audio_clock((ss5_ep_sunset) ? ss5_ep_sunset->dsp_list : NULL, (ss5_ep_sunrise) ? ss5_ep_sunrise->dsp_list : NULL, 160);
|
||||
|
||||
/* process keyboard input */
|
||||
c = get_char();
|
||||
|
@ -259,6 +259,9 @@ int main(int argc, char *argv[])
|
|||
/* init codecs */
|
||||
g711_init();
|
||||
|
||||
/* init dsp */
|
||||
dsp_set_sf(-9.0, -16.0);
|
||||
|
||||
cc_argv_sunset[cc_argc_sunset++] = options_strdup("remote auto");
|
||||
cc_argv_sunrise[cc_argc_sunrise++] = options_strdup("remote auto");
|
||||
|
||||
|
|
479
src/ss5/ss5.c
479
src/ss5/ss5.c
|
@ -30,7 +30,8 @@
|
|||
#include "../libdebug/debug.h"
|
||||
#include "../libg711/g711.h"
|
||||
#include "ss5.h"
|
||||
#include "display.h"
|
||||
#include "../common/display.h"
|
||||
#include "../common/common.h"
|
||||
|
||||
/* names of all SS5 states */
|
||||
const char *ss5_state_names[] = {
|
||||
|
@ -65,6 +66,12 @@ const char *ss5_state_names[] = {
|
|||
};
|
||||
|
||||
/* timers and durations */
|
||||
#define INTERRUPT_RECOGNITION 0.015
|
||||
#define SPLIT_RECOGNITION 0.030
|
||||
#define MF_RECOGNITION 0.025
|
||||
#define KP_DIGIT_DURATION 0.100
|
||||
#define OTHER_DIGIT_DURATION 0.055
|
||||
#define DIGIT_PAUSE 0.055
|
||||
#define SIGN_RECOGNITION_FAST 0.040 /* 40 ms for seize and proceed-to-send */
|
||||
#define SIGN_RECOGNITION_NORMAL 0.125 /* 125 ms for all other signals */
|
||||
#define MIN_RELEASE_GUARD 0.200 /* minimum 200 ms, in case we prevent blueboxing */
|
||||
|
@ -101,10 +108,10 @@ void refresh_status(void)
|
|||
for (ep = osmo_cc_endpoint_list; ep; ep = ep->next) {
|
||||
ss5_ep = ep->priv;
|
||||
if (!ss5_ep->dsp_list)
|
||||
display_status_line(ep->local_name, 0, NULL, NULL, 0);
|
||||
display_status_line(ep->local_name, 0, NULL, NULL, "");
|
||||
for (i = 0, dsp = ss5_ep->dsp_list; dsp; i++, dsp = dsp->next) {
|
||||
ss5 = dsp->priv;
|
||||
display_status_line(ep->local_name, i, ss5->callerid, ss5->dialing, ss5->state);
|
||||
display_status_line(ep->local_name, i, ss5->callerid, ss5->dialing, ss5_state_names[ss5->state]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,7 +179,7 @@ static ss5_t *link_create(ss5_endpoint_t *ss5_ep, const char *ep_name, int linki
|
|||
snprintf(ss5->name, sizeof(ss5->name) - 1, "%s/%d", ep_name, linkid);
|
||||
|
||||
/* init dsp instance */
|
||||
dsp_init_inst(&ss5->dsp, ss5, ss5->name, samplerate, crosstalk, comfort_noise, delay_ms, sense_db);
|
||||
dsp_init_inst(&ss5->dsp, ss5, ss5->name, samplerate, crosstalk, comfort_noise, delay_ms, sense_db, INTERRUPT_RECOGNITION, SPLIT_RECOGNITION, MF_RECOGNITION, KP_DIGIT_DURATION, OTHER_DIGIT_DURATION, DIGIT_PAUSE, 0.0, 0.0, 1);
|
||||
|
||||
/* init timer */
|
||||
timer_init(&ss5->timer, ss5_timeout, ss5);
|
||||
|
@ -208,7 +215,7 @@ static void link_destroy(ss5_t *ss5)
|
|||
*dsp_p = ss5->dsp.next;
|
||||
|
||||
/* state idle */
|
||||
ss5_new_state(ss5, SS5_STATE_IDLE);
|
||||
ss5_new_state(ss5, SS5_STATE_NULL);
|
||||
|
||||
/* reset instance */
|
||||
link_reset(ss5);
|
||||
|
@ -239,7 +246,7 @@ ss5_endpoint_t *ss5_ep_create(const char *ep_name, int links, int prevent_bluebo
|
|||
ss5_ep->suppress_disconnect = suppress_disconnect;
|
||||
|
||||
for (i = 0; i < links; i++)
|
||||
link_create(ss5_ep, ep_name, i + 1, 8000, crosstalk, comfort_noise, delay_ms, sense_db);
|
||||
link_create(ss5_ep, ep_name, i + 1, 8000.0, crosstalk, comfort_noise, delay_ms, sense_db);
|
||||
|
||||
PDEBUG(DSS5, DEBUG_DEBUG, "SS5 endpoint instance created\n");
|
||||
|
||||
|
@ -257,335 +264,30 @@ void ss5_ep_destroy(ss5_endpoint_t *ss5_ep)
|
|||
PDEBUG(DSS5, DEBUG_DEBUG, "SS5 endpoint instance destroyed\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* several messages towards CC
|
||||
*/
|
||||
|
||||
static void reject_call(ss5_endpoint_t *ss5_ep, uint32_t callref, uint8_t isdn_cause)
|
||||
|
||||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
/* create osmo-cc message */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REJ_IND);
|
||||
/* cause */
|
||||
osmo_cc_add_ie_cause(new_msg, OSMO_CC_LOCATION_BEYOND_INTERWORKING, isdn_cause, 0, 0);
|
||||
/* send message to osmo-cc */
|
||||
osmo_cc_ll_msg(&ss5_ep->cc_ep, callref, new_msg);
|
||||
}
|
||||
|
||||
static void release_call(ss5_t *ss5, uint8_t isdn_cause)
|
||||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
/* create osmo-cc message */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND);
|
||||
/* cause */
|
||||
osmo_cc_add_ie_cause(new_msg, OSMO_CC_LOCATION_BEYOND_INTERWORKING, isdn_cause, 0, 0);
|
||||
/* send message to osmo-cc */
|
||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, new_msg);
|
||||
}
|
||||
|
||||
static void disconnect_call(ss5_t *ss5, uint8_t isdn_cause)
|
||||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
if (ss5->ss5_ep->suppress_disconnect)
|
||||
return;
|
||||
/* create osmo-cc message */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_DISC_IND);
|
||||
/* progress */
|
||||
osmo_cc_add_ie_progress(new_msg, OSMO_CC_CODING_ITU_T, OSMO_CC_LOCATION_BEYOND_INTERWORKING, OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE);
|
||||
/* cause */
|
||||
osmo_cc_add_ie_cause(new_msg, OSMO_CC_LOCATION_BEYOND_INTERWORKING, isdn_cause, 0, 0);
|
||||
/* send message to osmo-cc */
|
||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, new_msg);
|
||||
}
|
||||
|
||||
static void proceed_call(ss5_t *ss5, const char *sdp)
|
||||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
/* create osmo-cc message */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_PROC_IND);
|
||||
/* progress */
|
||||
osmo_cc_add_ie_progress(new_msg, OSMO_CC_CODING_ITU_T, OSMO_CC_LOCATION_BEYOND_INTERWORKING, OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE);
|
||||
/* sdp */
|
||||
osmo_cc_add_ie_sdp(new_msg, sdp);
|
||||
/* send message to osmo-cc */
|
||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, new_msg);
|
||||
}
|
||||
|
||||
static void alert_call(ss5_t *ss5)
|
||||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
/* create osmo-cc message */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_ALERT_IND);
|
||||
/* send message to osmo-cc */
|
||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, new_msg);
|
||||
}
|
||||
|
||||
static void answer_call(ss5_t *ss5)
|
||||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
/* create osmo-cc message */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_CNF);
|
||||
/* send message to osmo-cc */
|
||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, new_msg);
|
||||
}
|
||||
|
||||
static void setup_comp_call(ss5_t *ss5)
|
||||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
/* create osmo-cc message */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_COMP_IND);
|
||||
/* send message to osmo-cc */
|
||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, new_msg);
|
||||
}
|
||||
|
||||
/*
|
||||
* dial string generation and parsing
|
||||
*/
|
||||
|
||||
static char *prefix_1_digit[] = { "1", "7", NULL };
|
||||
static char *prefix_2_digit[] = {
|
||||
"20", "27", "28", "30", "31", "32", "33", "34", "36", "39", "40", "41",
|
||||
"43", "44", "45", "46", "47", "48", "49", "51", "52", "53", "54", "55",
|
||||
"56", "57", "58", "60", "61", "62", "63", "64", "65", "66", "81", "82",
|
||||
"83", "84", "86", "89", "90", "91", "92", "93", "94", "95", "98",
|
||||
NULL };
|
||||
|
||||
/* use number and number type to generate an SS5 dial string
|
||||
* the digits are checked if they can be dialed
|
||||
* if the number is already in SS5 format, only digits are checked
|
||||
*/
|
||||
static int generate_dial_string(uint8_t type, const char *dialing, char *string, int string_size)
|
||||
{
|
||||
int full_string_given = 0;
|
||||
int i, ii;
|
||||
|
||||
if ((int)strlen(dialing) + 4 > string_size) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "Dial string is too long for our digit register, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* check for correct digits */
|
||||
for (i = 0, ii = strlen(dialing); i < ii; i++) {
|
||||
/* string may start with 'a' or 'b', but then 'c' must be the last digit */
|
||||
if (dialing[i] == 'a' || dialing[i] == 'b') {
|
||||
full_string_given = 1;
|
||||
if (dialing[ii - 1] != 'c') {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "Number starts with 'a' (KP1) or 'b' (KP2) but missing 'c' (ST) at the end, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* remove check of last digit 'c' */
|
||||
--ii;
|
||||
continue;
|
||||
}
|
||||
/* string must only consist of numerical digits and '*' (code 11) and '#' (code 12) */
|
||||
if (!strchr("0123456789*#", dialing[i])) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "Number has invalid digits, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* if full string with 'a'/'b' and 'c' is given, we have complete dial string */
|
||||
if (full_string_given) {
|
||||
strcpy(string, dialing);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if number is not of international type, create national dial string */
|
||||
if (type != OSMO_CC_TYPE_INTERNATIONAL) {
|
||||
// make GCC happy
|
||||
strcpy(string, "a0");
|
||||
strcat(string, dialing);
|
||||
strcat(string, "c");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check international prefix with length of 1 digit */
|
||||
if ((int)strlen(dialing) < 1) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; prefix_1_digit[i]; i++) {
|
||||
if (prefix_1_digit[i][0] == dialing[0])
|
||||
break;
|
||||
}
|
||||
/* if number is of international type, create international dial string */
|
||||
if (prefix_1_digit[i]) {
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wformat-overflow"
|
||||
sprintf(string, "b%c0%sc", dialing[0], dialing + 1);
|
||||
#pragma GCC diagnostic pop
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check international prefix with length of 2 digits */
|
||||
if ((int)strlen(dialing) < 2) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; prefix_2_digit[i]; i++) {
|
||||
if (prefix_2_digit[i][0] == dialing[0]
|
||||
&& prefix_2_digit[i][1] == dialing[1])
|
||||
break;
|
||||
}
|
||||
/* if number is of international type, create international dial string */
|
||||
if (prefix_2_digit[i]) {
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wformat-overflow"
|
||||
sprintf(string, "b%c%c0%sc", dialing[0], dialing[1], dialing + 2);
|
||||
#pragma GCC diagnostic pop
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check international prefix with length of 3 digits */
|
||||
if ((int)strlen(dialing) < 3) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* if number is of international type, create international dial string */
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wformat-overflow"
|
||||
sprintf(string, "b%c%c%c0%sc", dialing[0], dialing[1], dialing[2], dialing + 3);
|
||||
#pragma GCC diagnostic pop
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* parse received SS5 dial string and convert it into a national or international number */
|
||||
static int parse_dial_string(uint8_t *type, char *dialing, int dialing_size, const char *string)
|
||||
{
|
||||
char kp_digit;
|
||||
const char *prefix;
|
||||
int length;
|
||||
int i;
|
||||
|
||||
/* remove start and stop digits, set string after start digit and set length to digits between start and stop */
|
||||
if (string[0] != 'a' && string[0] != 'b') {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "Received digits do not start with 'a' (KP1) nor 'b' (KP2), call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
kp_digit = *string++;
|
||||
length = strlen(string) - 1;
|
||||
if (string[length] != 'c') {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "Received digits do end with 'c' (ST), call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (length > dialing_size - 1) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "Received dial string is too long, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* received national call */
|
||||
if (kp_digit == 'a') {
|
||||
/* remove discriminaing digit */
|
||||
string++;
|
||||
--length;
|
||||
*type = OSMO_CC_TYPE_NATIONAL;
|
||||
strncpy(dialing, string, length);
|
||||
dialing[length] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check international prefix with length of 1 digit */
|
||||
if (length < 2) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; prefix_1_digit[i]; i++) {
|
||||
if (prefix_1_digit[i][0] == string[0])
|
||||
break;
|
||||
}
|
||||
/* if number is of international type, create international dial string */
|
||||
if (prefix_1_digit[i]) {
|
||||
prefix = string;
|
||||
string += 1;
|
||||
length -= 1;
|
||||
/* remove discriminaing digit */
|
||||
string++;
|
||||
--length;
|
||||
*type = OSMO_CC_TYPE_INTERNATIONAL;
|
||||
dialing[0] = prefix[0];
|
||||
strncpy(dialing + 1, string, length);
|
||||
dialing[1 + length] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check international prefix with length of 2 digits */
|
||||
if (length < 3) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; prefix_2_digit[i]; i++) {
|
||||
if (prefix_2_digit[i][0] == string[0]
|
||||
&& prefix_2_digit[i][1] == string[1])
|
||||
break;
|
||||
}
|
||||
/* if number is of international type, create international dial string */
|
||||
if (prefix_2_digit[i]) {
|
||||
prefix = string;
|
||||
string += 2;
|
||||
length -= 2;
|
||||
/* remove discriminaing digit */
|
||||
string++;
|
||||
--length;
|
||||
*type = OSMO_CC_TYPE_INTERNATIONAL;
|
||||
dialing[0] = prefix[0];
|
||||
dialing[1] = prefix[1];
|
||||
strncpy(dialing + 2, string, length);
|
||||
dialing[2 + length] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check international prefix with length of 3 digits */
|
||||
if (length < 4) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "International number too short to get country code from, call is rejected!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* if number is of international type, create international dial string */
|
||||
prefix = string;
|
||||
string += 3;
|
||||
length -= 3;
|
||||
/* remove discriminaing digit */
|
||||
string++;
|
||||
--length;
|
||||
*type = OSMO_CC_TYPE_INTERNATIONAL;
|
||||
dialing[0] = prefix[0];
|
||||
dialing[1] = prefix[1];
|
||||
dialing[2] = prefix[2];
|
||||
strncpy(dialing + 3, string, length);
|
||||
dialing[3 + length] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* event handling
|
||||
*/
|
||||
|
||||
/* function that receives the digit or the cease of it (' ' or different digit) */
|
||||
void receive_digit(void *priv, char digit, double dbm)
|
||||
/* function that receives the signal or the cease of it (' ' or different signal) */
|
||||
void receive_signal(void *priv, char signal, double dbm)
|
||||
{
|
||||
ss5_t *ss5 = priv;
|
||||
const char *state_name = ss5_state_names[ss5->state];
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
if (digit > ' ')
|
||||
PDEBUG_CHAN(DSS5, DEBUG_DEBUG, "Received digit '%c' in '%s' state. (%.1f dBm)\n", digit, ss5_state_names[ss5->state], dbm);
|
||||
if (signal > ' ')
|
||||
PDEBUG_CHAN(DSS5, DEBUG_DEBUG, "Received signal '%c' in '%s' state. (%.1f dBm)\n", signal, state_name, dbm);
|
||||
else
|
||||
PDEBUG_CHAN(DSS5, DEBUG_DEBUG, "Received cease of digit in '%s' state.\n", ss5_state_names[ss5->state]);
|
||||
PDEBUG_CHAN(DSS5, DEBUG_DEBUG, "Received cease of signal in '%s' state.\n", state_name);
|
||||
|
||||
/* a clear forward (not release guard) at any state (including idle state) */
|
||||
if (ss5->state != SS5_STATE_SEND_CLR_FWD && digit == 'C') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'clear-forward' signal in '%s' state, sending 'release-guard' and clearing call.\n", ss5_state_names[ss5->state]);
|
||||
if (ss5->state != SS5_STATE_SEND_CLR_FWD && signal == 'C') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'clear-forward' signal in '%s' state, sending 'release-guard' and clearing call.\n", state_name);
|
||||
/* release outgoing call */
|
||||
if (ss5->dsp.cc_callref) {
|
||||
/* send release indication towards CC */
|
||||
release_call(ss5, OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR);
|
||||
release_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR);
|
||||
/* remove ref */
|
||||
ss5->dsp.cc_callref = 0;
|
||||
}
|
||||
|
@ -594,7 +296,7 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
/* change state */
|
||||
ss5_new_state(ss5, SS5_STATE_SEND_RELEASE);
|
||||
/* unset dial string, if set */
|
||||
set_dial_string(&ss5->dsp, "");
|
||||
set_dial_string(&ss5->dsp, "", 0);
|
||||
/* send release-guard */
|
||||
set_tone(&ss5->dsp, 'C', 0);
|
||||
/* to prevent blueboxing */
|
||||
|
@ -609,15 +311,15 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
switch (ss5->state) {
|
||||
/* release guard */
|
||||
case SS5_STATE_SEND_RELEASE:
|
||||
if (digit != 'C') {
|
||||
if (signal != 'C') {
|
||||
/* wait at least the minimum release-guard time, to prevent blueboxing */
|
||||
if (timer_running(&ss5->timer)) {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'clear-forward' is ceased in '%s' state, must wait to prevent blueboxing.\n", ss5_state_names[ss5->state]);
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'clear-forward' is ceased in '%s' state, must wait to prevent blueboxing.\n", state_name);
|
||||
/* state idle */
|
||||
ss5_new_state(ss5, SS5_STATE_SEND_REL_WAIT);
|
||||
break;
|
||||
}
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'clear-forward' is ceased in '%s' state, going idle.\n", ss5_state_names[ss5->state]);
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'clear-forward' is ceased in '%s' state, going idle.\n", state_name);
|
||||
/* cease */
|
||||
set_tone(&ss5->dsp, 0, 0);
|
||||
/* state idle */
|
||||
|
@ -628,15 +330,15 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
break;
|
||||
/* outgoing call sends seize */
|
||||
case SS5_STATE_SEND_SEIZE:
|
||||
if (digit == 'A') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'seize' signal in '%s' state, this is double seizure.\n", ss5_state_names[ss5->state]);
|
||||
if (signal == 'A') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'seize' signal in '%s' state, this is double seizure.\n", state_name);
|
||||
/* set timeout */
|
||||
timer_start(&ss5->timer, DUR_DOUBLE_SEIZURE);
|
||||
/* change state */
|
||||
ss5_new_state(ss5, SS5_STATE_DOUBLE_SEIZE);
|
||||
}
|
||||
if (digit == 'B') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'proceed-to-send' signal in '%s' state, ceasing 'seize' signal.\n", ss5_state_names[ss5->state]);
|
||||
if (signal == 'B') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'proceed-to-send' signal in '%s' state, ceasing 'seize' signal.\n", state_name);
|
||||
/* set recognition time to normal */
|
||||
set_sig_detect_duration(&ss5->dsp, SIGN_RECOGNITION_NORMAL, SIGN_RECOGNITION_NORMAL);
|
||||
/* cease */
|
||||
|
@ -652,63 +354,67 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
break;
|
||||
/* outgoing call receives proceed-to-send */
|
||||
case SS5_STATE_RECV_PROCEED:
|
||||
if (digit != 'B') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "proceed-to-send' is ceased in '%s' state, sendig digits.\n", ss5_state_names[ss5->state]);
|
||||
if (signal != 'B') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "proceed-to-send' is ceased in '%s' state, sendig digits.\n", state_name);
|
||||
/* cease */
|
||||
set_tone(&ss5->dsp, 0, 0);
|
||||
/* dial */
|
||||
set_tone(&ss5->dsp, ' ', PAUSE_BEFORE_DIALING);
|
||||
set_dial_string(&ss5->dsp, ss5->dialing);
|
||||
set_dial_string(&ss5->dsp, ss5->dialing, 0);
|
||||
/* change state */
|
||||
ss5_new_state(ss5, SS5_STATE_SEND_DIGITS);
|
||||
}
|
||||
break;
|
||||
/* outgoing call receives answer or busy-flash */
|
||||
case SS5_STATE_OUT_INACTIVE:
|
||||
if (digit == 'A') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'answer' signal in '%s' state, sending 'acknowledge'.\n", ss5_state_names[ss5->state]);
|
||||
if (signal == 'A') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'answer' signal in '%s' state, sending 'acknowledge'.\n", state_name);
|
||||
/* send acknowledge */
|
||||
set_tone(&ss5->dsp, 'A', MAX_ACKNOWLEDGE);
|
||||
/* change state */
|
||||
ss5_new_state(ss5, SS5_STATE_SEND_ACK_ANS);
|
||||
/* indicate answer to upper layer */
|
||||
answer_call(ss5);
|
||||
answer_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref);
|
||||
}
|
||||
if (digit == 'B') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'busy-flash' signal in '%s' state, sending 'acknowledge'.\n", ss5_state_names[ss5->state]);
|
||||
if (signal == 'B') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'busy-flash' signal in '%s' state, sending 'acknowledge'.\n", state_name);
|
||||
/* send acknowledge */
|
||||
set_tone(&ss5->dsp, 'A', MAX_ACKNOWLEDGE);
|
||||
/* change state */
|
||||
ss5_new_state(ss5, SS5_STATE_SEND_ACK_BUS);
|
||||
/* indicate disconnect w/tones to upper layer */
|
||||
disconnect_call(ss5, OSMO_CC_ISDN_CAUSE_USER_BUSY);
|
||||
if (!ss5->ss5_ep->suppress_disconnect) {
|
||||
/* indicate disconnect w/tones to upper layer */
|
||||
disconnect_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_USER_BUSY);
|
||||
}
|
||||
}
|
||||
break;
|
||||
/* outgoing call receives clear-back */
|
||||
case SS5_STATE_OUT_ACTIVE:
|
||||
if (digit == 'A') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'answer' signal in '%s' state, sending 'acknowledge'.\n", ss5_state_names[ss5->state]);
|
||||
if (signal == 'A') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'answer' signal in '%s' state, sending 'acknowledge'.\n", state_name);
|
||||
/* send acknowledge */
|
||||
set_tone(&ss5->dsp, 'A', MAX_ACKNOWLEDGE);
|
||||
/* change state */
|
||||
ss5_new_state(ss5, SS5_STATE_SEND_ACK_ANS);
|
||||
/* indicate answer to upper layer */
|
||||
answer_call(ss5);
|
||||
answer_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref);
|
||||
}
|
||||
if (digit == 'B') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'clear-back' signal in '%s' state, sending 'acknowledge'.\n", ss5_state_names[ss5->state]);
|
||||
if (signal == 'B') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'clear-back' signal in '%s' state, sending 'acknowledge'.\n", state_name);
|
||||
/* send acknowledge */
|
||||
set_tone(&ss5->dsp, 'A', MAX_ACKNOWLEDGE);
|
||||
/* change state */
|
||||
ss5_new_state(ss5, SS5_STATE_SEND_ACK_CLR);
|
||||
/* indicate disconnect w/tones to upper layer */
|
||||
disconnect_call(ss5, OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR);
|
||||
if (!ss5->ss5_ep->suppress_disconnect) {
|
||||
/* indicate disconnect w/tones to upper layer */
|
||||
disconnect_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR);
|
||||
}
|
||||
}
|
||||
break;
|
||||
/* outgoing call receives answer */
|
||||
case SS5_STATE_SEND_ACK_ANS:
|
||||
if (digit != 'A') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'answer' is ceased in '%s' state, call is established.\n", ss5_state_names[ss5->state]);
|
||||
if (signal != 'A') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'answer' is ceased in '%s' state, call is established.\n", state_name);
|
||||
/* stop timer */
|
||||
timer_stop(&ss5->timer);
|
||||
/* cease */
|
||||
|
@ -720,12 +426,12 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
/* outgoing call receives busy-flash */
|
||||
case SS5_STATE_SEND_ACK_BUS:
|
||||
case SS5_STATE_SEND_DIGITS:
|
||||
if (digit != 'B') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'busy-flash' is ceased in '%s' state, call is disconnected.\n", ss5_state_names[ss5->state]);
|
||||
if (signal != 'B') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'busy-flash' is ceased in '%s' state, call is disconnected.\n", state_name);
|
||||
/* stop timer */
|
||||
timer_stop(&ss5->timer);
|
||||
/* unset dial string, if set */
|
||||
set_dial_string(&ss5->dsp, "");
|
||||
set_dial_string(&ss5->dsp, "", 0);
|
||||
/* cease */
|
||||
set_tone(&ss5->dsp, 0, 0);
|
||||
/* change state */
|
||||
|
@ -734,8 +440,8 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
break;
|
||||
/* outgoing call receives clear-back */
|
||||
case SS5_STATE_SEND_ACK_CLR:
|
||||
if (digit != 'B') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'clear-back' is ceased in '%s' state, call is disconnected.\n", ss5_state_names[ss5->state]);
|
||||
if (signal != 'B') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'clear-back' is ceased in '%s' state, call is disconnected.\n", state_name);
|
||||
/* stop timer */
|
||||
timer_stop(&ss5->timer);
|
||||
/* cease */
|
||||
|
@ -746,8 +452,8 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
break;
|
||||
/* outgoing call sends clear forward */
|
||||
case SS5_STATE_SEND_CLR_FWD:
|
||||
if (digit == 'C') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'release-guard' signal in '%s' state, ceasing 'clear-forward' signal.\n", ss5_state_names[ss5->state]);
|
||||
if (signal == 'C') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'release-guard' signal in '%s' state, ceasing 'clear-forward' signal.\n", state_name);
|
||||
/* cease */
|
||||
set_tone(&ss5->dsp, 0, 0);
|
||||
/* stop timer */
|
||||
|
@ -758,8 +464,8 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
break;
|
||||
/* outgoing call receives release guard */
|
||||
case SS5_STATE_RECV_RELEASE:
|
||||
if (digit != 'C') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'release-guard' is ceased in '%s' state, going idle.\n", ss5_state_names[ss5->state]);
|
||||
if (signal != 'C') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'release-guard' is ceased in '%s' state, going idle.\n", state_name);
|
||||
/* state idle */
|
||||
ss5_new_state(ss5, SS5_STATE_IDLE);
|
||||
/* reset instance */
|
||||
|
@ -768,8 +474,8 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
break;
|
||||
/* incoming call receives seize */
|
||||
case SS5_STATE_IDLE:
|
||||
if (digit == 'A') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'seize' signal in '%s' state, sending 'proceed-to-send'.\n", ss5_state_names[ss5->state]);
|
||||
if (signal == 'A') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'seize' signal in '%s' state, sending 'proceed-to-send'.\n", state_name);
|
||||
/* set recognition time to normal */
|
||||
set_sig_detect_duration(&ss5->dsp, SIGN_RECOGNITION_NORMAL, SIGN_RECOGNITION_NORMAL);
|
||||
/* change state */
|
||||
|
@ -780,8 +486,8 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
break;
|
||||
/* incoming call sends proceed-to-send */
|
||||
case SS5_STATE_SEND_PROCEED:
|
||||
if (digit != 'A') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'seize' is ceased in '%s' state, receiving digits.\n", ss5_state_names[ss5->state]);
|
||||
if (signal != 'A') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "'seize' is ceased in '%s' state, receiving digits.\n", state_name);
|
||||
/* cease */
|
||||
set_tone(&ss5->dsp, 0, 0);
|
||||
/* change state */
|
||||
|
@ -792,15 +498,15 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
break;
|
||||
/* incoming call receives digits */
|
||||
case SS5_STATE_RECV_DIGIT:
|
||||
if (!(digit >= '0' && digit <= '9') && !(digit >= 'a' && digit <= 'c')) {
|
||||
if (!(signal >= '0' && signal <= '9') && !(signal >= 'a' && signal <= 'c')) {
|
||||
break;
|
||||
}
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Digit '%c' is ceased in '%s' state.\n", digit, ss5_state_names[ss5->state]);
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Digit '%c' is detected in '%s' state.\n", signal, state_name);
|
||||
/* add digit */
|
||||
i = strlen(ss5->dialing);
|
||||
if (i + 1 == sizeof(ss5->dialing))
|
||||
break;
|
||||
ss5->dialing[i++] = digit;
|
||||
ss5->dialing[i++] = signal;
|
||||
ss5->dialing[i] = '\0';
|
||||
/* change state */
|
||||
ss5_new_state(ss5, SS5_STATE_RECV_SPACE);
|
||||
|
@ -808,7 +514,7 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
timer_start(&ss5->timer, TO_DIALING);
|
||||
break;
|
||||
case SS5_STATE_RECV_SPACE:
|
||||
if (digit != ' ')
|
||||
if (signal != ' ')
|
||||
break;
|
||||
/* check for end of dialing */
|
||||
i = strlen(ss5->dialing) - 1;
|
||||
|
@ -859,8 +565,8 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
break;
|
||||
/* incoming call sends answer */
|
||||
case SS5_STATE_SEND_ANSWER:
|
||||
if (digit == 'A') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'acknowledge' to 'answer' in '%s' state, call is now active.\n", ss5_state_names[ss5->state]);
|
||||
if (signal == 'A') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'acknowledge' to 'answer' in '%s' state, call is now active.\n", state_name);
|
||||
/* stop timer */
|
||||
timer_stop(&ss5->timer);
|
||||
/* cease */
|
||||
|
@ -871,8 +577,8 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
break;
|
||||
/* incoming call sends busy-flash */
|
||||
case SS5_STATE_SEND_BUSY:
|
||||
if (digit == 'A') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'acknowledge' to 'busy-flash' in '%s' state, call is now inactive.\n", ss5_state_names[ss5->state]);
|
||||
if (signal == 'A') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'acknowledge' to 'busy-flash' in '%s' state, call is now inactive.\n", state_name);
|
||||
/* stop timer */
|
||||
timer_stop(&ss5->timer);
|
||||
/* cease */
|
||||
|
@ -883,8 +589,8 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
break;
|
||||
/* incoming call sends clear-back */
|
||||
case SS5_STATE_SEND_CLR_BAK:
|
||||
if (digit == 'A') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'acknowledge' to 'clear-back' in '%s' state, call is now inactive.\n", ss5_state_names[ss5->state]);
|
||||
if (signal == 'A') {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received 'acknowledge' to 'clear-back' in '%s' state, call is now inactive.\n", state_name);
|
||||
/* stop timer */
|
||||
timer_stop(&ss5->timer);
|
||||
/* cease */
|
||||
|
@ -894,7 +600,7 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
}
|
||||
break;
|
||||
default:
|
||||
PDEBUG_CHAN(DSS5, DEBUG_ERROR, "Received digit '%c' in '%s' state is not handled, please fix!\n", digit, ss5_state_names[ss5->state]);
|
||||
PDEBUG_CHAN(DSS5, DEBUG_NOTICE, "Received signal '%c' in '%s' state is not handled!\n", signal, state_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -910,18 +616,19 @@ void dialing_complete(void *priv)
|
|||
}
|
||||
|
||||
/* indicate alerting */
|
||||
alert_call(ss5);
|
||||
alert_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref);
|
||||
}
|
||||
|
||||
/* timeouts */
|
||||
static void ss5_timeout(void *data)
|
||||
{
|
||||
ss5_t *ss5 = data;
|
||||
const char *state_name = ss5_state_names[ss5->state];
|
||||
|
||||
switch (ss5->state) {
|
||||
case SS5_STATE_RECV_DIGIT:
|
||||
case SS5_STATE_RECV_SPACE:
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received timeout in '%s' state, sending 'clear-back'.\n", ss5_state_names[ss5->state]);
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Received timeout in '%s' state, sending 'clear-back'.\n", state_name);
|
||||
/* send clear-back */
|
||||
set_tone(&ss5->dsp, 'B', MAX_CLEAR_BACK);
|
||||
/* change state */
|
||||
|
@ -933,7 +640,7 @@ static void ss5_timeout(void *data)
|
|||
/* state idle */
|
||||
ss5_new_state(ss5, SS5_STATE_IDLE);
|
||||
/* send release indication towards CC */
|
||||
release_call(ss5, OSMO_CC_ISDN_CAUSE_NO_CIRCUIT_CHAN);
|
||||
release_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_NO_CIRCUIT_CHAN);
|
||||
/* reset inst */
|
||||
link_reset(ss5);
|
||||
break;
|
||||
|
@ -957,6 +664,7 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
|||
ss5_endpoint_t *ss5_ep = ep->priv;
|
||||
dsp_t *dsp;
|
||||
ss5_t *ss5;
|
||||
const char *state_name;
|
||||
osmo_cc_msg_t *new_msg;
|
||||
uint8_t type, plan, present, screen;
|
||||
char dialing[64];
|
||||
|
@ -989,16 +697,17 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
|||
ss5 = (dsp) ? dsp->priv : NULL;
|
||||
if (!ss5) {
|
||||
PDEBUG(DSS5, DEBUG_NOTICE, "No free ss5 instance, rejecting.\n");
|
||||
reject_call(ss5_ep, callref, OSMO_CC_ISDN_CAUSE_NO_CIRCUIT_CHAN);
|
||||
reject_call(&ss5_ep->cc_ep, callref, OSMO_CC_ISDN_CAUSE_NO_CIRCUIT_CHAN);
|
||||
goto done;
|
||||
}
|
||||
/* link with cc */
|
||||
ss5->dsp.cc_callref = callref;
|
||||
}
|
||||
state_name = ss5_state_names[ss5->state];
|
||||
|
||||
switch (msg->type) {
|
||||
case OSMO_CC_MSG_SETUP_REQ: /* dial-out command received from epoint */
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Outgoing call in '%s' state, sending 'seize'.\n", ss5_state_names[ss5->state]);
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Outgoing call in '%s' state, sending 'seize'.\n", state_name);
|
||||
/* caller id */
|
||||
rc = osmo_cc_get_ie_calling(msg, 0, &type, &plan, &present, &screen, ss5->callerid, sizeof(ss5->callerid));
|
||||
/* called number */
|
||||
|
@ -1006,7 +715,7 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
|||
if (rc < 0 || !dialing[0]) {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_NOTICE, "No number given, call is rejected!\n");
|
||||
inv_nr:
|
||||
reject_call(ss5->ss5_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_INV_NR_FORMAT);
|
||||
reject_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_INV_NR_FORMAT);
|
||||
link_reset(ss5);
|
||||
goto done;
|
||||
}
|
||||
|
@ -1016,12 +725,12 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
|||
/* sdp accept */
|
||||
sdp = osmo_cc_helper_audio_accept(&ss5->ss5_ep->cc_ep.session_config, &ss5->dsp, codecs, down_audio, msg, &ss5->dsp.cc_session, &ss5->dsp.codec, 0);
|
||||
if (!sdp) {
|
||||
reject_call(ss5->ss5_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_RESOURCE_UNAVAIL);
|
||||
reject_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_RESOURCE_UNAVAIL);
|
||||
link_reset(ss5);
|
||||
goto done;
|
||||
}
|
||||
/* proceed */
|
||||
proceed_call(ss5, sdp);
|
||||
proceed_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, sdp);
|
||||
/* send seize */
|
||||
set_tone(&ss5->dsp, 'A', MAX_SEIZE);
|
||||
/* change state */
|
||||
|
@ -1040,19 +749,19 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
|||
/* change state */
|
||||
ss5_new_state(ss5, SS5_STATE_SEND_BUSY);
|
||||
/* release call */
|
||||
release_call(ss5, OSMO_CC_ISDN_CAUSE_RESOURCE_UNAVAIL);
|
||||
release_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_RESOURCE_UNAVAIL);
|
||||
/* reset inst */
|
||||
link_reset(ss5);
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
case OSMO_CC_MSG_SETUP_RSP: /* call of endpoint is connected */
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Incoming call has answered in '%s' state, sending 'answer'.\n", ss5_state_names[ss5->state]);
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Incoming call has answered in '%s' state, sending 'answer'.\n", state_name);
|
||||
rc = osmo_cc_helper_audio_negotiate(msg, &ss5->dsp.cc_session, &ss5->dsp.codec);
|
||||
if (rc < 0)
|
||||
goto codec_failed;
|
||||
/* connect acknowledge */
|
||||
setup_comp_call(ss5);
|
||||
setup_comp_call(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref);
|
||||
/* not in right state, which should never happen anyway */
|
||||
if (ss5->state != SS5_STATE_IN_INACTIVE)
|
||||
break;
|
||||
|
@ -1069,20 +778,20 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
|||
goto codec_failed;
|
||||
/* right state */
|
||||
if (ss5->state == SS5_STATE_IN_INACTIVE) {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Incoming call has disconnected in '%s' state, sending 'busy-flash'.\n", ss5_state_names[ss5->state]);
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Incoming call has disconnected in '%s' state, sending 'busy-flash'.\n", state_name);
|
||||
/* send busy-flash */
|
||||
set_tone(&ss5->dsp, 'B', MAX_BUSY_FLASH);
|
||||
/* change state */
|
||||
ss5_new_state(ss5, SS5_STATE_SEND_BUSY);
|
||||
} else
|
||||
if (ss5->state == SS5_STATE_IN_ACTIVE) {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Incoming call has disconnected in '%s' state, sending 'clear-back'.\n", ss5_state_names[ss5->state]);
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Incoming call has disconnected in '%s' state, sending 'clear-back'.\n", state_name);
|
||||
/* send clear-back */
|
||||
set_tone(&ss5->dsp, 'B', MAX_CLEAR_BACK);
|
||||
/* change state */
|
||||
ss5_new_state(ss5, SS5_STATE_SEND_CLR_BAK);
|
||||
} else {
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Outgoing call has disconnected in '%s' state, sending 'clear-forward'.\n", ss5_state_names[ss5->state]);
|
||||
PDEBUG_CHAN(DSS5, DEBUG_INFO, "Outgoing call has disconnected in '%s' state, sending 'clear-forward'.\n", state_name);
|
||||
/* send clear-forward */
|
||||
set_tone(&ss5->dsp, 'C', MAX_CLEAR_FORWARD);
|
||||
/* change state */
|
||||
|
|
Loading…
Reference in New Issue