forked from cc/osmo-cc-ss5-endpoint
Preparte for Bell system; Put common code into lib
This commit is contained in:
parent
48dd37e1ae
commit
91e8521071
|
@ -42,4 +42,5 @@ src/libsample/libsample.a
|
|||
src/libtimer/libtimer.a
|
||||
src/libselect/libselect.a
|
||||
src/libfilter/libfilter.a
|
||||
src/common/libcommon.a
|
||||
src/ss5/osmo-cc-ss5-endpoint
|
||||
|
|
|
@ -87,6 +87,7 @@ AC_OUTPUT(
|
|||
src/libosmocc/Makefile
|
||||
src/libg711/Makefile
|
||||
src/libfilter/Makefile
|
||||
src/common/Makefile
|
||||
src/ss5/Makefile
|
||||
src/Makefile
|
||||
Makefile)
|
||||
|
|
|
@ -10,5 +10,6 @@ SUBDIRS = \
|
|||
libosmocc \
|
||||
libg711 \
|
||||
libfilter \
|
||||
common \
|
||||
ss5
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||
|
||||
noinst_LIBRARIES = libcommon.a
|
||||
|
||||
libcommon_a_SOURCES = \
|
||||
mf.c \
|
||||
dsp.c
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define CHAN ((ss5_t *)(dsp->priv))->name
|
||||
#define CHAN dsp->name
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
@ -29,7 +29,7 @@
|
|||
#include <sys/types.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "../libdebug/debug.h"
|
||||
#include "ss5.h"
|
||||
#include "dsp.h"
|
||||
|
||||
//#define DEBUG_DEMODULATOR
|
||||
|
||||
|
@ -50,16 +50,20 @@ static double tone_diff_ampl_sq[NUM_TONES];
|
|||
#define SPLIT_DURATION 0.030
|
||||
#define MF_RECOGNITION 0.025
|
||||
|
||||
int dsp_init_inst(dsp_t *dsp, void *priv, double samplerate, 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 tone_amplitude[NUM_TONES];
|
||||
int t;
|
||||
int rc;
|
||||
|
||||
PDEBUG(DDSP, DEBUG_DEBUG, "Init DSP for SS5 instance.\n");
|
||||
PDEBUG(DDSP, DEBUG_DEBUG, "Init DSP instance.\n");
|
||||
|
||||
memset(dsp, 0, sizeof(*dsp));
|
||||
dsp->priv = priv;
|
||||
strncpy(dsp->name, name, sizeof(dsp->name - 1));
|
||||
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);
|
||||
|
@ -83,22 +87,44 @@ int dsp_init_inst(dsp_t *dsp, void *priv, double samplerate, double sense_db)
|
|||
if (!dsp->mf_mod)
|
||||
return -EINVAL;
|
||||
|
||||
/* alloc delay buffer */
|
||||
if (delay_ms) {
|
||||
dsp->delay_length = (int)(dsp->samplerate * (double)delay_ms / 1000.0);
|
||||
dsp->delay_buffer = calloc(dsp->delay_length, sizeof(*dsp->delay_buffer));
|
||||
}
|
||||
|
||||
/* allocate jitter buffer */
|
||||
rc = jitter_create(&dsp->tx_dejitter, "tx", 8000, sizeof(sample_t), JITTER_DATA);
|
||||
if (rc < 0)
|
||||
abort();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dsp_cleanup_inst(dsp_t *dsp)
|
||||
{
|
||||
PDEBUG(DDSP, DEBUG_DEBUG, "Cleanup DSP of SS5 instance.\n");
|
||||
PDEBUG(DDSP, DEBUG_DEBUG, "Cleanup DSP instance.\n");
|
||||
|
||||
/* free FM modulator */
|
||||
if (dsp->mf_mod) {
|
||||
mf_mod_exit(dsp->mf_mod);
|
||||
dsp->mf_mod = NULL;
|
||||
}
|
||||
|
||||
/* free FM demodulator */
|
||||
if (dsp->mf_demod) {
|
||||
mf_demod_exit(dsp->mf_demod);
|
||||
dsp->mf_demod = NULL;
|
||||
}
|
||||
|
||||
/* free delay buffer */
|
||||
if (dsp->delay_buffer) {
|
||||
free(dsp->delay_buffer);
|
||||
dsp->delay_buffer = NULL;
|
||||
}
|
||||
|
||||
/* free jitter buffer */
|
||||
jitter_destroy(&dsp->tx_dejitter);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -400,7 +426,7 @@ static void detect_tones(dsp_t *dsp, sample_t *samples, sample_t **levels_square
|
|||
}
|
||||
|
||||
/* process audio from one link (source) to another (destination) */
|
||||
static void process_audio(ss5_t *ss5_a, ss5_t *ss5_b, int length)
|
||||
static void process_audio(dsp_t *dsp_a, dsp_t *dsp_b, int length)
|
||||
{
|
||||
sample_t samples[2][length], s;
|
||||
sample_t b1[length], b2[length], b3[length], b4[length], b5[length], b6[length], b7[length], b8[length];
|
||||
|
@ -411,32 +437,32 @@ static void process_audio(ss5_t *ss5_a, ss5_t *ss5_b, int length)
|
|||
int i;
|
||||
|
||||
/* get audio from jitter buffer */
|
||||
jitter_load(&ss5_a->tx_dejitter, samples[0], length);
|
||||
jitter_load(&ss5_b->tx_dejitter, samples[1], length);
|
||||
jitter_load(&dsp_a->tx_dejitter, samples[0], length);
|
||||
jitter_load(&dsp_b->tx_dejitter, samples[1], length);
|
||||
|
||||
/* optionally add comfort noise */
|
||||
if (!ss5_a->cc_callref && ss5_a->ss5_ep->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;
|
||||
}
|
||||
if (!ss5_b->cc_callref && ss5_b->ss5_ep->comfort_noise) {
|
||||
if (!dsp_b->cc_callref && dsp_b->comfort_noise) {
|
||||
for (i = 0; i < length; i++)
|
||||
samples[1][i] += (double)((int8_t)random()) / 8000.0;
|
||||
}
|
||||
|
||||
/* modulate tone/digit. if no tone has to be played (or it stopped), count is less than length */
|
||||
count1 = assemble_tones(&ss5_a->dsp, mask, length);
|
||||
mf_mod(ss5_a->dsp.mf_mod, mask, samples[0], count1);
|
||||
count2 = assemble_tones(&ss5_b->dsp, mask, length);
|
||||
mf_mod(ss5_b->dsp.mf_mod, mask, samples[1], count2);
|
||||
count1 = assemble_tones(dsp_a, mask, length);
|
||||
mf_mod(dsp_a->mf_mod, mask, samples[0], count1);
|
||||
count2 = assemble_tones(dsp_b, mask, length);
|
||||
mf_mod(dsp_b->mf_mod, mask, samples[1], count2);
|
||||
|
||||
/* optionally add some crosstalk */
|
||||
if (ss5_a->ss5_ep->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 (ss5_b->ss5_ep->crosstalk) {
|
||||
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;
|
||||
|
@ -445,63 +471,63 @@ static void process_audio(ss5_t *ss5_a, ss5_t *ss5_b, int length)
|
|||
/* ! here is the bridge from a to b and from b to a ! */
|
||||
|
||||
/* optionally add one way delay */
|
||||
if (ss5_b->delay_buffer) {
|
||||
if (dsp_b->delay_buffer) {
|
||||
for (i = 0; i < length; i++) {
|
||||
s = ss5_b->delay_buffer[ss5_b->delay_index];
|
||||
ss5_b->delay_buffer[ss5_b->delay_index] = samples[0][i];
|
||||
if (++(ss5_b->delay_index) == ss5_b->delay_length)
|
||||
ss5_b->delay_index = 0;
|
||||
s = dsp_b->delay_buffer[dsp_b->delay_index];
|
||||
dsp_b->delay_buffer[dsp_b->delay_index] = samples[0][i];
|
||||
if (++(dsp_b->delay_index) == dsp_b->delay_length)
|
||||
dsp_b->delay_index = 0;
|
||||
samples[0][i] = s;
|
||||
}
|
||||
}
|
||||
if (ss5_a->delay_buffer) {
|
||||
if (dsp_a->delay_buffer) {
|
||||
for (i = 0; i < length; i++) {
|
||||
s = ss5_a->delay_buffer[ss5_a->delay_index];
|
||||
ss5_a->delay_buffer[ss5_a->delay_index] = samples[1][i];
|
||||
if (++(ss5_a->delay_index) == ss5_a->delay_length)
|
||||
ss5_a->delay_index = 0;
|
||||
s = dsp_a->delay_buffer[dsp_a->delay_index];
|
||||
dsp_a->delay_buffer[dsp_a->delay_index] = samples[1][i];
|
||||
if (++(dsp_a->delay_index) == dsp_a->delay_length)
|
||||
dsp_a->delay_index = 0;
|
||||
samples[1][i] = s;
|
||||
}
|
||||
}
|
||||
|
||||
/* demodulate and call tone detector */
|
||||
mf_demod(ss5_b->dsp.mf_demod, samples[0], length, levels_squared);
|
||||
detect_tones(&ss5_b->dsp, samples[0], levels_squared, length, 1);
|
||||
mf_demod(ss5_a->dsp.mf_demod, samples[1], length, levels_squared);
|
||||
detect_tones(&ss5_a->dsp, samples[1], levels_squared, length, 0);
|
||||
mf_demod(dsp_b->mf_demod, samples[0], length, levels_squared);
|
||||
detect_tones(dsp_b->priv, 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);
|
||||
|
||||
/* forward audio to CC if call exists */
|
||||
if (ss5_b->cc_callref && ss5_b->codec) {
|
||||
if (dsp_b->cc_callref && dsp_b->codec) {
|
||||
samples_to_int16_1mw(data, samples[0], length);
|
||||
osmo_cc_rtp_send(ss5_b->codec, (uint8_t *)data, length * 2, 0, 1, length, ss5_b);
|
||||
osmo_cc_rtp_send(dsp_b->codec, (uint8_t *)data, length * 2, 0, 1, length, dsp_b);
|
||||
}
|
||||
if (ss5_a->cc_callref && ss5_a->codec) {
|
||||
if (dsp_a->cc_callref && dsp_a->codec) {
|
||||
samples_to_int16_1mw(data, samples[1], length);
|
||||
osmo_cc_rtp_send(ss5_a->codec, (uint8_t *)data, length * 2, 0, 1, length, ss5_a);
|
||||
osmo_cc_rtp_send(dsp_a->codec, (uint8_t *)data, length * 2, 0, 1, length, dsp_a);
|
||||
}
|
||||
}
|
||||
|
||||
/* clock is called every given number of samples (20ms) */
|
||||
void audio_clock(ss5_endpoint_t *ss5_ep_sunset, ss5_endpoint_t *ss5_ep_sunrise, int len)
|
||||
void audio_clock(dsp_t *sunset_list, dsp_t *sunrise_list, int len)
|
||||
{
|
||||
ss5_t *ss5_a, *ss5_b;
|
||||
dsp_t *dsp_a, *dsp_b;
|
||||
|
||||
if (!ss5_ep_sunset)
|
||||
if (!sunset_list)
|
||||
return;
|
||||
|
||||
if (!ss5_ep_sunrise) {
|
||||
if (!sunrise_list) {
|
||||
/* each pair of links on the same endpoint are bridged */
|
||||
for (ss5_b = ss5_ep_sunset->link_list; ss5_b; ss5_b = ss5_b->next) {
|
||||
ss5_a = ss5_b;
|
||||
ss5_b = ss5_b->next;
|
||||
if (!ss5_b)
|
||||
for (dsp_b = sunset_list; dsp_b; dsp_b = dsp_b->next) {
|
||||
dsp_a = dsp_b;
|
||||
dsp_b = dsp_b->next;
|
||||
if (!dsp_b)
|
||||
break;
|
||||
process_audio(ss5_a, ss5_b, len);
|
||||
process_audio(dsp_a, dsp_b, len);
|
||||
}
|
||||
} else {
|
||||
/* each link on two endpoints are bridged */
|
||||
for (ss5_a = ss5_ep_sunset->link_list, ss5_b = ss5_ep_sunrise->link_list; ss5_a && ss5_b; ss5_a = ss5_a->next, ss5_b = ss5_b->next) {
|
||||
process_audio(ss5_a, ss5_b, len);
|
||||
for (dsp_a = sunset_list, dsp_b = sunrise_list; dsp_a && dsp_b; dsp_a = dsp_a->next, dsp_b = dsp_b->next) {
|
||||
process_audio(dsp_a, dsp_b, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -509,12 +535,12 @@ void audio_clock(ss5_endpoint_t *ss5_ep_sunset, ss5_endpoint_t *ss5_ep_sunrise,
|
|||
/* take audio from CC and store in jitter buffer */
|
||||
void down_audio(struct osmo_cc_session_codec *codec, uint8_t __attribute__((unused)) marker, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len)
|
||||
{
|
||||
ss5_t *ss5 = codec->media->session->priv;
|
||||
dsp_t *dsp = codec->media->session->priv;
|
||||
int count = len/2;
|
||||
sample_t samples[count];
|
||||
|
||||
int16_to_samples_1mw(samples, (int16_t *)data, count);
|
||||
jitter_save(&ss5->tx_dejitter, samples, count, 1, sequence, timestamp, ssrc);
|
||||
jitter_save(&dsp->tx_dejitter, samples, count, 1, sequence, timestamp, ssrc);
|
||||
}
|
||||
|
||||
void encode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
|
|
@ -1,8 +1,16 @@
|
|||
|
||||
struct ss5_endpoint;
|
||||
#include "../libtimer/timer.h"
|
||||
#include "../libselect/select.h"
|
||||
#include "../libosmocc/endpoint.h"
|
||||
#include "../libosmocc/helper.h"
|
||||
#include "../libsample/sample.h"
|
||||
#include "../libjitter/jitter.h"
|
||||
#include "mf.h"
|
||||
|
||||
typedef struct dsp {
|
||||
void *priv;
|
||||
struct dsp *next; /* next instance (list is maintained by dsp) */
|
||||
void *priv; /* back pointer to application */
|
||||
char name[32]; /* name of link for debugging */
|
||||
double samplerate;
|
||||
|
||||
/* tone generation */
|
||||
|
@ -28,15 +36,27 @@ typedef struct dsp {
|
|||
int mf_detect_duration; /* MF tone duration in milliseconds */
|
||||
int detect_count; /* counter for tone detection */
|
||||
char detect_tone; /* current tone detected or ' ' for no tone */
|
||||
|
||||
/* audio processing */
|
||||
jitter_t tx_dejitter; /* jitter buffer for audio from CC */
|
||||
sample_t *delay_buffer; /* buffer for delaying audio */
|
||||
int delay_length;
|
||||
int delay_index;
|
||||
int crosstalk; /* mix crosstalk from TX to RX */
|
||||
int comfort_noise; /* add comfort noise before answer and after disconnect */
|
||||
|
||||
/* osmo-CC */
|
||||
uint32_t cc_callref; /* ref to CC call */
|
||||
osmo_cc_session_t *cc_session; /* audio session description */
|
||||
osmo_cc_session_codec_t *codec; /* selected codec */
|
||||
} dsp_t;
|
||||
|
||||
|
||||
int dsp_init_inst(dsp_t *dsp, void *priv, double samplerate, 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);
|
||||
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);
|
||||
void audio_clock(struct ss5_endpoint *ss5_ep_sunset, struct ss5_endpoint *ss5_ep_sunrise, int len);
|
||||
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);
|
|
@ -4,14 +4,13 @@ bin_PROGRAMS = \
|
|||
osmo-cc-ss5-endpoint
|
||||
|
||||
osmo_cc_ss5_endpoint_SOURCES = \
|
||||
mf.c \
|
||||
dsp.c \
|
||||
ss5.c \
|
||||
display_status.c \
|
||||
main.c
|
||||
|
||||
osmo_cc_ss5_endpoint_LDADD = \
|
||||
$(COMMON_LA) \
|
||||
../common/libcommon.a \
|
||||
../libdebug/libdebug.a \
|
||||
../liboptions/liboptions.a \
|
||||
../libsample/libsample.a \
|
||||
|
|
|
@ -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, ss5_ep_sunrise, 160);
|
||||
audio_clock(ss5_ep_sunset->dsp_list, ss5_ep_sunrise->dsp_list, 160);
|
||||
|
||||
/* process keyboard input */
|
||||
c = get_char();
|
||||
|
@ -280,14 +280,14 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
/* create sunset and (optionally) sunrise */
|
||||
ss5_ep_sunset = ss5_ep_create("sunset", links, prevent_blueboxing, crosstalk, delay_ms, comfort_noise, suppress_disconnect, sense_db);
|
||||
ss5_ep_sunset = ss5_ep_create("sunset", links, prevent_blueboxing, suppress_disconnect, crosstalk, comfort_noise, delay_ms, sense_db);
|
||||
if (!ss5_ep_sunset)
|
||||
goto error;
|
||||
rc = osmo_cc_new(&ss5_ep_sunset->cc_ep, OSMO_CC_VERSION, "sunset", OSMO_CC_LOCATION_BEYOND_INTERWORKING, cc_message, NULL, ss5_ep_sunset, cc_argc_sunset, cc_argv_sunset);
|
||||
if (rc < 0)
|
||||
goto error;
|
||||
if (endpoints == 2) {
|
||||
ss5_ep_sunrise = ss5_ep_create("sunrise", links, prevent_blueboxing, crosstalk, delay_ms, comfort_noise, suppress_disconnect, sense_db);
|
||||
ss5_ep_sunrise = ss5_ep_create("sunrise", links, prevent_blueboxing, suppress_disconnect, crosstalk, comfort_noise, delay_ms, sense_db);
|
||||
if (!ss5_ep_sunrise)
|
||||
goto error;
|
||||
rc = osmo_cc_new(&ss5_ep_sunrise->cc_ep, OSMO_CC_VERSION, "sunrise", OSMO_CC_LOCATION_BEYOND_INTERWORKING, cc_message, NULL, ss5_ep_sunrise, cc_argc_sunrise, cc_argv_sunrise);
|
||||
|
|
151
src/ss5/ss5.c
151
src/ss5/ss5.c
|
@ -92,6 +92,7 @@ void refresh_status(void)
|
|||
{
|
||||
osmo_cc_endpoint_t *ep;
|
||||
ss5_endpoint_t *ss5_ep;
|
||||
dsp_t *dsp;
|
||||
ss5_t *ss5;
|
||||
int i;
|
||||
|
||||
|
@ -99,10 +100,12 @@ void refresh_status(void)
|
|||
|
||||
for (ep = osmo_cc_endpoint_list; ep; ep = ep->next) {
|
||||
ss5_ep = ep->priv;
|
||||
if (!ss5_ep->link_list)
|
||||
if (!ss5_ep->dsp_list)
|
||||
display_status_line(ep->local_name, 0, NULL, NULL, 0);
|
||||
for (i = 0, ss5 = ss5_ep->link_list; ss5; i++, ss5 = ss5->next)
|
||||
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_end();
|
||||
|
@ -125,20 +128,20 @@ void ss5_new_state(ss5_t *ss5, enum ss5_state state)
|
|||
static void link_reset(ss5_t *ss5)
|
||||
{
|
||||
/* unlink callref */
|
||||
ss5->cc_callref = 0;
|
||||
ss5->dsp.cc_callref = 0;
|
||||
|
||||
/* stop timer */
|
||||
timer_stop(&ss5->timer);
|
||||
|
||||
/* free session description */
|
||||
if (ss5->cc_session) {
|
||||
osmo_cc_free_session(ss5->cc_session);
|
||||
ss5->cc_session = NULL;
|
||||
ss5->codec = NULL;
|
||||
if (ss5->dsp.cc_session) {
|
||||
osmo_cc_free_session(ss5->dsp.cc_session);
|
||||
ss5->dsp.cc_session = NULL;
|
||||
ss5->dsp.codec = NULL;
|
||||
}
|
||||
|
||||
/* reset jitter buffer */
|
||||
jitter_reset(&ss5->tx_dejitter);
|
||||
jitter_reset(&ss5->dsp.tx_dejitter);
|
||||
|
||||
/* set recognition time */
|
||||
set_sig_detect_duration(&ss5->dsp, SIGN_RECOGNITION_FAST, SIGN_RECOGNITION_NORMAL);
|
||||
|
@ -153,10 +156,10 @@ static void link_reset(ss5_t *ss5)
|
|||
|
||||
static void ss5_timeout(void *data);
|
||||
|
||||
static ss5_t *link_create(ss5_endpoint_t *ss5_ep, const char *ep_name, int linkid)
|
||||
static ss5_t *link_create(ss5_endpoint_t *ss5_ep, const char *ep_name, int linkid, double samplerate, int crosstalk, int comfort_noise, int delay_ms, double sense_db)
|
||||
{
|
||||
ss5_t *ss5, **ss5_p;
|
||||
int rc;
|
||||
ss5_t *ss5;
|
||||
dsp_t **dsp_p;
|
||||
|
||||
ss5 = calloc(1, sizeof(*ss5));
|
||||
if (!ss5) {
|
||||
|
@ -165,37 +168,27 @@ static ss5_t *link_create(ss5_endpoint_t *ss5_ep, const char *ep_name, int linki
|
|||
}
|
||||
ss5->ss5_ep = ss5_ep;
|
||||
|
||||
ss5_p = &ss5_ep->link_list;
|
||||
while (*ss5_p)
|
||||
ss5_p = &((*ss5_p)->next);
|
||||
*ss5_p = ss5;
|
||||
|
||||
/* debug name */
|
||||
snprintf(ss5->name, sizeof(ss5->name) - 1, "%s/%d", ep_name, linkid);
|
||||
|
||||
/* init dsp instance */
|
||||
dsp_init_inst(&ss5->dsp, ss5, ss5_ep->samplerate, ss5_ep->sense_db);
|
||||
dsp_init_inst(&ss5->dsp, ss5, ss5->name, samplerate, crosstalk, comfort_noise, delay_ms, sense_db);
|
||||
|
||||
/* init timer */
|
||||
timer_init(&ss5->timer, ss5_timeout, ss5);
|
||||
|
||||
/* allocate jitter buffer */
|
||||
rc = jitter_create(&ss5->tx_dejitter, "tx", 8000, sizeof(sample_t), JITTER_DATA);
|
||||
if (rc < 0)
|
||||
abort();
|
||||
|
||||
/* alloc delay buffer */
|
||||
if (ss5_ep->delay_ms) {
|
||||
ss5->delay_length = (int)(ss5_ep->samplerate * (double)ss5_ep->delay_ms / 1000.0);
|
||||
ss5->delay_buffer = calloc(ss5->delay_length, sizeof(*ss5->delay_buffer));
|
||||
}
|
||||
|
||||
/* reset instance */
|
||||
link_reset(ss5);
|
||||
|
||||
/* state idle */
|
||||
ss5_new_state(ss5, SS5_STATE_IDLE);
|
||||
|
||||
/* link */
|
||||
dsp_p = &ss5_ep->dsp_list;
|
||||
while (*dsp_p)
|
||||
dsp_p = &((*dsp_p)->next);
|
||||
*dsp_p = &ss5->dsp;
|
||||
|
||||
PDEBUG_CHAN(DSS5, DEBUG_DEBUG, "created ss5 instance\n");
|
||||
|
||||
return ss5;
|
||||
|
@ -203,7 +196,16 @@ static ss5_t *link_create(ss5_endpoint_t *ss5_ep, const char *ep_name, int linki
|
|||
|
||||
static void link_destroy(ss5_t *ss5)
|
||||
{
|
||||
ss5_t **ss5_p;
|
||||
dsp_t **dsp_p;
|
||||
|
||||
/* detach */
|
||||
dsp_p = &ss5->ss5_ep->dsp_list;
|
||||
while (*dsp_p) {
|
||||
if (*dsp_p == &ss5->dsp)
|
||||
break;
|
||||
dsp_p = &((*dsp_p)->next);
|
||||
}
|
||||
*dsp_p = ss5->dsp.next;
|
||||
|
||||
/* state idle */
|
||||
ss5_new_state(ss5, SS5_STATE_IDLE);
|
||||
|
@ -214,33 +216,15 @@ static void link_destroy(ss5_t *ss5)
|
|||
/* exit timer */
|
||||
timer_exit(&ss5->timer);
|
||||
|
||||
/* free jitter buffer */
|
||||
jitter_destroy(&ss5->tx_dejitter);
|
||||
|
||||
/* free delay buffer */
|
||||
if (ss5->delay_buffer) {
|
||||
free(ss5->delay_buffer);
|
||||
ss5->delay_buffer = NULL;
|
||||
}
|
||||
|
||||
/* cleanup dsp instance */
|
||||
dsp_cleanup_inst(&ss5->dsp);
|
||||
|
||||
/* detach */
|
||||
ss5_p = &ss5->ss5_ep->link_list;
|
||||
while (*ss5_p) {
|
||||
if (*ss5_p == ss5)
|
||||
break;
|
||||
ss5_p = &((*ss5_p)->next);
|
||||
}
|
||||
*ss5_p = ss5->next;
|
||||
|
||||
PDEBUG_CHAN(DSS5, DEBUG_DEBUG, "destroyed ss5 instance\n");
|
||||
|
||||
free(ss5);
|
||||
}
|
||||
|
||||
ss5_endpoint_t *ss5_ep_create(const char *ep_name, int links, int prevent_blueboxing, int crosstalk, int delay_ms, int comfort_noise, int suppress_disconnect, double sense_db)
|
||||
ss5_endpoint_t *ss5_ep_create(const char *ep_name, int links, int prevent_blueboxing, int suppress_disconnect, int crosstalk, int comfort_noise, int delay_ms, double sense_db)
|
||||
{
|
||||
ss5_endpoint_t *ss5_ep;
|
||||
int i;
|
||||
|
@ -251,16 +235,11 @@ ss5_endpoint_t *ss5_ep_create(const char *ep_name, int links, int prevent_bluebo
|
|||
abort();
|
||||
}
|
||||
|
||||
ss5_ep->samplerate = 8000;
|
||||
ss5_ep->prevent_blueboxing = prevent_blueboxing;
|
||||
ss5_ep->crosstalk = crosstalk;
|
||||
ss5_ep->delay_ms = delay_ms;
|
||||
ss5_ep->comfort_noise = comfort_noise;
|
||||
ss5_ep->suppress_disconnect = suppress_disconnect;
|
||||
ss5_ep->sense_db = sense_db;
|
||||
|
||||
for (i = 0; i < links; i++)
|
||||
link_create(ss5_ep, ep_name, i + 1);
|
||||
link_create(ss5_ep, ep_name, i + 1, 8000, crosstalk, comfort_noise, delay_ms, sense_db);
|
||||
|
||||
PDEBUG(DSS5, DEBUG_DEBUG, "SS5 endpoint instance created\n");
|
||||
|
||||
|
@ -270,8 +249,8 @@ ss5_endpoint_t *ss5_ep_create(const char *ep_name, int links, int prevent_bluebo
|
|||
void ss5_ep_destroy(ss5_endpoint_t *ss5_ep)
|
||||
{
|
||||
/* destroy all calls */
|
||||
while (ss5_ep->link_list)
|
||||
link_destroy(ss5_ep->link_list);
|
||||
while (ss5_ep->dsp_list)
|
||||
link_destroy(ss5_ep->dsp_list->priv);
|
||||
|
||||
free(ss5_ep);
|
||||
|
||||
|
@ -304,7 +283,7 @@ static void release_call(ss5_t *ss5, uint8_t isdn_cause)
|
|||
/* 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->cc_callref, new_msg);
|
||||
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)
|
||||
|
@ -320,7 +299,7 @@ static void disconnect_call(ss5_t *ss5, uint8_t isdn_cause)
|
|||
/* 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->cc_callref, new_msg);
|
||||
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)
|
||||
|
@ -334,7 +313,7 @@ static void proceed_call(ss5_t *ss5, const char *sdp)
|
|||
/* sdp */
|
||||
osmo_cc_add_ie_sdp(new_msg, sdp);
|
||||
/* send message to osmo-cc */
|
||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->cc_callref, new_msg);
|
||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, new_msg);
|
||||
}
|
||||
|
||||
static void alert_call(ss5_t *ss5)
|
||||
|
@ -344,7 +323,7 @@ static void alert_call(ss5_t *ss5)
|
|||
/* 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->cc_callref, new_msg);
|
||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, new_msg);
|
||||
}
|
||||
|
||||
static void answer_call(ss5_t *ss5)
|
||||
|
@ -354,7 +333,7 @@ static void answer_call(ss5_t *ss5)
|
|||
/* 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->cc_callref, new_msg);
|
||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, new_msg);
|
||||
}
|
||||
|
||||
static void setup_comp_call(ss5_t *ss5)
|
||||
|
@ -364,7 +343,7 @@ static void setup_comp_call(ss5_t *ss5)
|
|||
/* 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->cc_callref, new_msg);
|
||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, new_msg);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -604,11 +583,11 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
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]);
|
||||
/* release outgoing call */
|
||||
if (ss5->cc_callref) {
|
||||
if (ss5->dsp.cc_callref) {
|
||||
/* send release indication towards CC */
|
||||
release_call(ss5, OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR);
|
||||
/* remove ref */
|
||||
ss5->cc_callref = 0;
|
||||
ss5->dsp.cc_callref = 0;
|
||||
}
|
||||
/* stop timer */
|
||||
timer_stop(&ss5->timer);
|
||||
|
@ -856,8 +835,8 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
/* called number */
|
||||
osmo_cc_add_ie_called(msg, type, OSMO_CC_PLAN_TELEPHONY, dialing);
|
||||
/* sdp offer */
|
||||
ss5->cc_session = osmo_cc_helper_audio_offer(&ss5->ss5_ep->cc_ep.session_config, ss5, codecs, down_audio, msg, 1);
|
||||
if (!ss5->cc_session) {
|
||||
ss5->dsp.cc_session = osmo_cc_helper_audio_offer(&ss5->ss5_ep->cc_ep.session_config, &ss5->dsp, codecs, down_audio, msg, 1);
|
||||
if (!ss5->dsp.cc_session) {
|
||||
osmo_cc_free_msg(msg);
|
||||
PDEBUG_CHAN(DSS5, DEBUG_NOTICE, "Failed to offer audio, sending 'clear-back'.\n");
|
||||
/* send clear-back */
|
||||
|
@ -868,9 +847,9 @@ void receive_digit(void *priv, char digit, double dbm)
|
|||
}
|
||||
/* create new call */
|
||||
osmo_cc_call_t *cc_call = osmo_cc_call_new(&ss5->ss5_ep->cc_ep);
|
||||
ss5->cc_callref = cc_call->callref;
|
||||
ss5->dsp.cc_callref = cc_call->callref;
|
||||
/* send message to CC */
|
||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->cc_callref, msg);
|
||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, msg);
|
||||
/* change state */
|
||||
ss5_new_state(ss5, SS5_STATE_IN_INACTIVE);
|
||||
break;
|
||||
|
@ -976,6 +955,7 @@ static void ss5_timeout(void *data)
|
|||
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;
|
||||
osmo_cc_msg_t *new_msg;
|
||||
uint8_t type, plan, present, screen;
|
||||
|
@ -984,12 +964,13 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
|||
int rc;
|
||||
|
||||
/* hunt for callref */
|
||||
ss5 = ss5_ep->link_list;
|
||||
while (ss5) {
|
||||
if (ss5->cc_callref == callref)
|
||||
dsp = ss5_ep->dsp_list;
|
||||
while (dsp) {
|
||||
if (dsp->cc_callref == callref)
|
||||
break;
|
||||
ss5 = ss5->next;
|
||||
dsp = dsp->next;
|
||||
}
|
||||
ss5 = (dsp) ? dsp->priv : NULL;
|
||||
|
||||
/* process SETUP */
|
||||
if (!ss5) {
|
||||
|
@ -998,19 +979,21 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
|||
goto done;
|
||||
}
|
||||
/* hunt free ss5 instance */
|
||||
ss5 = ss5_ep->link_list;
|
||||
while (ss5) {
|
||||
dsp = ss5_ep->dsp_list;
|
||||
while (dsp) {
|
||||
ss5 = dsp->priv;
|
||||
if (ss5->state == SS5_STATE_IDLE)
|
||||
break;
|
||||
ss5 = ss5->next;
|
||||
dsp = dsp->next;
|
||||
}
|
||||
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);
|
||||
goto done;
|
||||
}
|
||||
/* link with cc */
|
||||
ss5->cc_callref = callref;
|
||||
ss5->dsp.cc_callref = callref;
|
||||
}
|
||||
|
||||
switch (msg->type) {
|
||||
|
@ -1023,7 +1006,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->cc_callref, OSMO_CC_ISDN_CAUSE_INV_NR_FORMAT);
|
||||
reject_call(ss5->ss5_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_INV_NR_FORMAT);
|
||||
link_reset(ss5);
|
||||
goto done;
|
||||
}
|
||||
|
@ -1031,9 +1014,9 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
|||
if (rc < 0)
|
||||
goto inv_nr;
|
||||
/* sdp accept */
|
||||
sdp = osmo_cc_helper_audio_accept(&ss5->ss5_ep->cc_ep.session_config, ss5, codecs, down_audio, msg, &ss5->cc_session, &ss5->codec, 0);
|
||||
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->cc_callref, OSMO_CC_ISDN_CAUSE_RESOURCE_UNAVAIL);
|
||||
reject_call(ss5->ss5_ep, ss5->dsp.cc_callref, OSMO_CC_ISDN_CAUSE_RESOURCE_UNAVAIL);
|
||||
link_reset(ss5);
|
||||
goto done;
|
||||
}
|
||||
|
@ -1048,7 +1031,7 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
|||
case OSMO_CC_MSG_PROC_REQ: /* call of endpoint is proceeding */
|
||||
case OSMO_CC_MSG_ALERT_REQ: /* call of endpoint is ringing */
|
||||
case OSMO_CC_MSG_PROGRESS_REQ: /* progress */
|
||||
rc = osmo_cc_helper_audio_negotiate(msg, &ss5->cc_session, &ss5->codec);
|
||||
rc = osmo_cc_helper_audio_negotiate(msg, &ss5->dsp.cc_session, &ss5->dsp.codec);
|
||||
if (rc < 0) {
|
||||
codec_failed:
|
||||
PDEBUG_CHAN(DSS5, DEBUG_NOTICE, "Releasing, because codec negotiation failed.\n");
|
||||
|
@ -1065,7 +1048,7 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
|||
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]);
|
||||
rc = osmo_cc_helper_audio_negotiate(msg, &ss5->cc_session, &ss5->codec);
|
||||
rc = osmo_cc_helper_audio_negotiate(msg, &ss5->dsp.cc_session, &ss5->dsp.codec);
|
||||
if (rc < 0)
|
||||
goto codec_failed;
|
||||
/* connect acknowledge */
|
||||
|
@ -1081,7 +1064,7 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
|||
case OSMO_CC_MSG_REJ_REQ: /* call has been rejected */
|
||||
case OSMO_CC_MSG_REL_REQ: /* call has been released */
|
||||
case OSMO_CC_MSG_DISC_REQ: /* call has been disconnected */
|
||||
rc = osmo_cc_helper_audio_negotiate(msg, &ss5->cc_session, &ss5->codec);
|
||||
rc = osmo_cc_helper_audio_negotiate(msg, &ss5->dsp.cc_session, &ss5->dsp.codec);
|
||||
if (rc < 0)
|
||||
goto codec_failed;
|
||||
/* right state */
|
||||
|
@ -1109,7 +1092,7 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
|||
new_msg = osmo_cc_clone_msg(msg);
|
||||
new_msg->type = OSMO_CC_MSG_REL_IND;
|
||||
/* send message to osmo-cc */
|
||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->cc_callref, new_msg);
|
||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, new_msg);
|
||||
/* reset */
|
||||
link_reset(ss5);
|
||||
break;
|
||||
|
@ -1121,7 +1104,7 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
|
|||
new_msg = osmo_cc_clone_msg(msg);
|
||||
new_msg->type = OSMO_CC_MSG_REL_CNF;
|
||||
/* send message to osmo-cc */
|
||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->cc_callref, new_msg);
|
||||
osmo_cc_ll_msg(&ss5->ss5_ep->cc_ep, ss5->dsp.cc_callref, new_msg);
|
||||
}
|
||||
/* on reject/release we reset/unlink call */
|
||||
if (msg->type != OSMO_CC_MSG_DISC_REQ)
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
|
||||
#include "../libtimer/timer.h"
|
||||
#include "../libselect/select.h"
|
||||
#include "../libosmocc/endpoint.h"
|
||||
#include "../libosmocc/helper.h"
|
||||
#include "../libsample/sample.h"
|
||||
#include "../libjitter/jitter.h"
|
||||
#include "mf.h"
|
||||
#include "dsp.h"
|
||||
#include "../common/dsp.h"
|
||||
|
||||
enum ss5_state {
|
||||
SS5_STATE_NULL = 0,
|
||||
|
@ -45,42 +38,29 @@ struct ss5_endpoint;
|
|||
|
||||
/* SS5 link definition */
|
||||
typedef struct ss5 {
|
||||
struct ss5 *next;
|
||||
struct ss5_endpoint *ss5_ep;
|
||||
char name[32]; /* name of link for debugging */
|
||||
|
||||
/* call states */
|
||||
enum ss5_state state; /* state of link */
|
||||
struct timer timer; /* for several timeouts */
|
||||
uint32_t cc_callref; /* ref to CC call */
|
||||
osmo_cc_session_t *cc_session; /* audio session description */
|
||||
osmo_cc_session_codec_t *codec; /* selected codec */
|
||||
char callerid[65]; /* current caller id (outgoing only) */
|
||||
char dialing[33]; /* current dial string (send or receive) */
|
||||
|
||||
/* audio processing */
|
||||
jitter_t tx_dejitter; /* jitter buffer for audio from CC */
|
||||
dsp_t dsp; /* all dsp processing */
|
||||
sample_t *delay_buffer; /* buffer for delaying audio */
|
||||
int delay_length;
|
||||
int delay_index;
|
||||
} ss5_t;
|
||||
|
||||
/* SS5 endpoint definition */
|
||||
typedef struct ss5_endpoint {
|
||||
osmo_cc_endpoint_t cc_ep;
|
||||
ss5_t *link_list;
|
||||
double samplerate;
|
||||
dsp_t *dsp_list;
|
||||
int suppress_disconnect; /* do not forward disconnect towards CC */
|
||||
int prevent_blueboxing; /* extend release-guard, so outgoing exchange releases */
|
||||
int crosstalk; /* mix crosstalk from TX to RX */
|
||||
int comfort_noise; /* add comfort noise before answer and after disconnect */
|
||||
int delay_ms; /* add delay to simulate long distance lines */
|
||||
double sense_db; /* increase sensitivity of decoder by this value */
|
||||
} ss5_endpoint_t;
|
||||
|
||||
void refresh_status(void);
|
||||
ss5_endpoint_t *ss5_ep_create(const char *ep_name, int links, int prevent_blueboxing, int crosstalk, int delay_ms, int comfort_noise, int suppress_disconnect, double sense_db);
|
||||
ss5_endpoint_t *ss5_ep_create(const char *ep_name, int links, int prevent_blueboxing, int suppress_disconnect, int crosstalk, int comfort_noise, int delay_ms, double sense_db);
|
||||
void ss5_ep_destroy(ss5_endpoint_t *ss5_ep);
|
||||
void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg);
|
||||
|
||||
|
|
Loading…
Reference in New Issue