Move more common code; make it usable for SS5 and R1

This commit is contained in:
Andreas Eversberg 2023-06-11 10:27:17 +02:00
parent 91e8521071
commit 2483f1c2b1
14 changed files with 770 additions and 494 deletions

View File

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

345
src/common/common.c Normal file
View File

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

11
src/common/common.h Normal file
View File

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

View File

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

View File

@ -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 */

View File

@ -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) {

View File

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

View File

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

View File

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

53
src/common/sit.c Normal file
View File

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

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

@ -0,0 +1,5 @@
void init_sit(int samplerate);
void exit_sit(void);
int sit_play(sample_t *samples, int count, int length);

View File

@ -5,7 +5,6 @@ bin_PROGRAMS = \
osmo_cc_ss5_endpoint_SOURCES = \
ss5.c \
display_status.c \
main.c
osmo_cc_ss5_endpoint_LDADD = \

View File

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

View File

@ -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 */