Remove DSP kernel module dependency

* Use raw bchannel instead of DSP feature
* Removed DSP features
* Use new jitter buffer and control TX FIFO load by this application
* Added bridging daemon to assign time slots for bridging
This commit is contained in:
Andreas Eversberg 2022-08-19 16:27:41 +02:00
parent 9bbca1fb9e
commit e6aa538562
7 changed files with 1136 additions and 662 deletions

View File

@ -7,6 +7,7 @@ osmo_cc_misdn_endpoint_SOURCES = \
ie.c \
dss1.c \
isdn.c \
tones.c \
bridge.c \
ph_driver.c \
main.c

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,8 @@
int bridge_msg_open(void);
void bridge_msg_close(void);
void bridge_process_local(call_t *call);
int bridge_work(isdn_t *isdn_ep);
int brigde_socket_server(int slots);
void bridge_socket_server_child(int slots, int daemon);
int bridge_socket_client(void);
void bridge_socket_client_work(isdn_t *isdn_ep);
void bridge_socket_client_update(call_t *call, int enable);

View File

@ -174,7 +174,7 @@ static void split_3pty(call_t *call)
if (other) {
other->conference_3pty = 0;
/* process local briding capability */
bridge_process_local(other);
bridge_socket_client_update(other, 1);
/* create osmo-cc message */
msg = osmo_cc_new_msg(OSMO_CC_MSG_NOTIFY_IND);
/* notify the facility */
@ -297,15 +297,21 @@ void setup_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
}
/* select codec */
if (clearmode)
if (clearmode) {
codecs = codecs_offer_clearmode;
else if (call->isdn_ep->law == 'a')
codecs = codecs_offer_alaw_ulaw;
else
codecs = codecs_offer_ulaw_alaw;
/* init jitter buffer */
call_create_jitter(call, 1);
} else {
if (call->isdn_ep->law == 'a')
codecs = codecs_offer_alaw_ulaw;
else
codecs = codecs_offer_ulaw_alaw;
/* init jitter buffer */
call_create_jitter(call, 0);
}
/* sdp offer */
call->cc_session = osmo_cc_helper_audio_offer(&call->isdn_ep->cc_ep.session_config, call, codecs, bchannel_send, msg, 1);
call->cc_session = osmo_cc_helper_audio_offer(&call->isdn_ep->cc_ep.session_config, call, codecs, rtp_receive, msg, 1);
/* dialing complete */
dec_ie_complete(l3m, &sending_complete);
@ -340,6 +346,9 @@ void setup_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
if (rc < 0)
goto no_channel;
/* process local briding capability */
bridge_socket_client_update(call, 1);
/* create endpoint */
osmo_cc_call_t *cc_call = osmo_cc_call_new(&call->isdn_ep->cc_ep);
call->cc_callref = cc_call->callref;
@ -392,8 +401,12 @@ void setup_ack_ind(call_t *call, uint32_t cmd, uint32_t pid, struct l3_msg *l3m)
/* send SDP answer */
if (coding == OSMO_CC_CODING_ITU_T && (progress == 1 || progress == 8) && !call->codec_negotiated) {
call->codec_negotiated = 1;
if (call->sdp)
if (call->sdp) {
/* send SDP */
osmo_cc_add_ie_sdp(msg, call->sdp);
/* process local briding capability */
bridge_socket_client_update(call, 1);
}
}
/* the audio path is throughconnected */
@ -463,8 +476,12 @@ void proc_ind(call_t *call, uint32_t cmd, uint32_t pid, struct l3_msg *l3m)
/* send SDP answer */
if (coding == OSMO_CC_CODING_ITU_T && (progress == 1 || progress == 8) && !call->codec_negotiated) {
call->codec_negotiated = 1;
if (call->sdp)
if (call->sdp) {
/* send SDP */
osmo_cc_add_ie_sdp(msg, call->sdp);
/* process local briding capability */
bridge_socket_client_update(call, 1);
}
}
/* the audio path is throughconnected */
@ -534,8 +551,12 @@ void alert_ind(call_t *call, uint32_t cmd, uint32_t pid, struct l3_msg *l3m)
/* send SDP answer */
if (coding == OSMO_CC_CODING_ITU_T && (progress == 1 || progress == 8) && !call->codec_negotiated) {
call->codec_negotiated = 1;
if (call->sdp)
if (call->sdp) {
/* send SDP */
osmo_cc_add_ie_sdp(msg, call->sdp);
/* process local briding capability */
bridge_socket_client_update(call, 1);
}
}
/* the audio path is throughconnected */
@ -614,8 +635,12 @@ void setup_cnf(call_t *call, uint32_t cmd, uint32_t pid, struct l3_msg *l3m)
/* send SDP answer */
if (!call->codec_negotiated) {
call->codec_negotiated = 1;
if (call->sdp)
if (call->sdp) {
/* send SDP */
osmo_cc_add_ie_sdp(msg, call->sdp);
/* process local briding capability */
bridge_socket_client_update(call, 1);
}
}
/* the audio path is throughconnected */
@ -713,7 +738,6 @@ void disconnect_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
{
osmo_cc_msg_t *msg;
uint8_t location, cause;
uint8_t coding, proglocation, progress;
char display[128];
int rc;
@ -722,15 +746,6 @@ void disconnect_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
/* create osmo-cc message */
msg = osmo_cc_new_msg(OSMO_CC_MSG_DISC_IND);
/* progress indicator */
rc = dec_ie_progress(l3m, &coding, &proglocation, &progress);
if (rc >= 0)
osmo_cc_add_ie_progress(msg, coding, proglocation, progress);
else {
coding = OSMO_CC_CODING_ITU_T;
progress = 0;
}
/* cause */
rc = dec_ie_cause(l3m, &location, &cause);
if (rc < 0) {
@ -743,18 +758,9 @@ void disconnect_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
if (rc >= 0)
osmo_cc_add_ie_display(msg, display);
/* send SDP answer */
if (coding == OSMO_CC_CODING_ITU_T && (progress == 1 || progress == 8) && !call->codec_negotiated) {
call->codec_negotiated = 1;
if (call->sdp)
osmo_cc_add_ie_sdp(msg, call->sdp);
}
/* note: disconnect does not assign channel ID */
/* the audio path is throughconnected */
if (coding == OSMO_CC_CODING_ITU_T && (progress == 1 || progress == 8))
call->audio_path = 1;
else
call->audio_path = 0;
/* note: disconnect does not have progress indicator */
new_state(call, ISDN_STATE_IN_DISCONNECT);
@ -928,6 +934,9 @@ void hold_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
return;
}
/* process local briding capability */
bridge_socket_client_update(call, 0);
/* create osmo-cc message */
msg = osmo_cc_new_msg(OSMO_CC_MSG_NOTIFY_IND);
@ -991,9 +1000,15 @@ void retrieve_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
if (rc < 0)
goto no_channel;
/* process local briding capability */
bridge_socket_client_update(call, 1);
/* set hold state */
call->hold = 0;
/* reset jitter buffer */
jitter_reset(&call->tx_dejitter);
/* acknowledge retrieve */
PDEBUG(DDSS1, DEBUG_INFO, "RETRIEVE-ACKNOWLEDGE REQUEST (pid = 0x%x callref = %d)\n", pid, call->cc_callref);
l3m = create_l3msg();
@ -1039,6 +1054,9 @@ void suspend_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
check = check->next;
}
/* process local briding capability */
bridge_socket_client_update(call, 0);
/* create osmo-cc message */
msg = osmo_cc_new_msg(OSMO_CC_MSG_NOTIFY_IND);
@ -1117,6 +1135,9 @@ void resume_ind(isdn_t *isdn_ep, uint32_t pid, struct l3_msg *l3m)
if (rc < 0)
goto no_channel;
/* process local briding capability */
bridge_socket_client_update(call, 1);
/* create osmo-cc message */
msg = osmo_cc_new_msg(OSMO_CC_MSG_NOTIFY_IND);
@ -1166,7 +1187,6 @@ void facility_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
notify = OSMO_CC_NOTIFY_CONFERENCE_ESTABLISHED;
invokeid = fac.u.inv.invokeId;
set_3pty = 1;
jitter_clear(&call->dejitter);
break;
case Fac_End3PTY:
@ -1200,9 +1220,12 @@ void facility_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
if (set_3pty >= 0 && other) {
other->conference_3pty = call->conference_3pty = set_3pty;
/* process local briding capability */
bridge_process_local(call);
bridge_process_local(other);
jitter_clear(&other->dejitter);
bridge_socket_client_update(call, 0);
bridge_socket_client_update(other, 0);
jitter_reset(&call->conf_dejitter);
jitter_reset(&call->tx_dejitter);
jitter_reset(&other->conf_dejitter);
jitter_reset(&other->tx_dejitter);
} else {
PDEBUG(DDSS1, DEBUG_NOTICE, "Phone requests conference, but no call on hold!\n");
notify = 0;
@ -1273,8 +1296,12 @@ void progress_ind(call_t *call, uint32_t pid, struct l3_msg *l3m)
/* send SDP answer */
if (coding == OSMO_CC_CODING_ITU_T && (progress == 1 || progress == 8) && !call->codec_negotiated) {
call->codec_negotiated = 1;
if (call->sdp)
if (call->sdp) {
/* send SDP */
osmo_cc_add_ie_sdp(msg, call->sdp);
/* process local briding capability */
bridge_socket_client_update(call, 1);
}
}
/* the audio path is throughconnected */
@ -1483,7 +1510,7 @@ int dss1_receive(isdn_t *isdn_ep, uint32_t cmd, uint32_t pid, struct l3_msg *l3m
/* creating call instance, transparent until setup with hdlc */
call = call_create(isdn_ep, DIRECTION_ORIGINATOR, 0, 0, B_MODE_TRANSPARENT);
if (!call) {
PDEBUG(DDSS1, DEBUG_ERROR, "Cannot create calll instance.\n");
PDEBUG(DDSS1, DEBUG_ERROR, "Cannot create call instance.\n");
abort();
}
dss1_message(isdn_ep, call, cmd, pid, l3m);
@ -1550,7 +1577,7 @@ void setup_req(call_t *call, osmo_cc_msg_t *msg)
codecs = codecs_accept_ulaw_alaw_clearmode;
/* sdp accept, force our codec, so we can use bchannel bridging if remote side supports it too */
sdp = osmo_cc_helper_audio_accept(&call->isdn_ep->cc_ep.session_config, call, codecs, bchannel_send, msg, &call->cc_session, &call->codec, 1/*force*/);
sdp = osmo_cc_helper_audio_accept(&call->isdn_ep->cc_ep.session_config, call, codecs, rtp_receive, msg, &call->cc_session, &call->codec, 1/*force*/);
if (!sdp) {
release_and_destroy(call, 47, 415/* Unsupported Media*/, 0);
return;
@ -1602,11 +1629,14 @@ void setup_req(call_t *call, osmo_cc_msg_t *msg)
user = 2;
else
user = 3;
/* init jitter buffer */
call_create_jitter(call, 0);
} else {
has_user = 0;
user = 0;
/* init jitter buffer */
call_create_jitter(call, 1);
}
printf("rate=%d\n", rate);
enc_ie_bearer(l3m, coding, capability, 1, mode, rate, 0, 0, has_user, user);
/* channel information */
@ -1677,7 +1707,7 @@ void setup_req(call_t *call, osmo_cc_msg_t *msg)
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_SETUP, call->l3_pid, l3m);
/* process local briding capability */
bridge_process_local(call);
bridge_socket_client_update(call, 1);
}
void proc_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg, int with_ies);
@ -2365,22 +2395,18 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
break;
case OSMO_CC_MSG_SETUP_ACK_REQ: /* more information is needed */
osmo_cc_helper_audio_negotiate(msg, &call->cc_session, &call->codec);
bridge_process_local(call);
setup_ack_req(call, call->l3_pid, msg);
break;
case OSMO_CC_MSG_PROC_REQ: /* call of endpoint is proceeding */
osmo_cc_helper_audio_negotiate(msg, &call->cc_session, &call->codec);
bridge_process_local(call);
proc_req(call, call->l3_pid, msg, 1);
break;
case OSMO_CC_MSG_ALERT_REQ: /* call of endpoint is ringing */
osmo_cc_helper_audio_negotiate(msg, &call->cc_session, &call->codec);
bridge_process_local(call);
alert_req(call, call->l3_pid, msg);
break;
case OSMO_CC_MSG_SETUP_RSP: /* call of endpoint is connected */
osmo_cc_helper_audio_negotiate(msg, &call->cc_session, &call->codec);
bridge_process_local(call);
setup_rsp(call, call->l3_pid, msg);
break;
case OSMO_CC_MSG_SETUP_COMP_REQ: /* call of endpoint is connected */
@ -2415,7 +2441,6 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
break;
case OSMO_CC_MSG_PROGRESS_REQ: /* progress */
osmo_cc_helper_audio_negotiate(msg, &call->cc_session, &call->codec);
bridge_process_local(call);
if (isdn_ep->ntmode
&& call->state != ISDN_STATE_OUT_OVERLAP
&& call->state != ISDN_STATE_OUT_PROCEEDING
@ -2445,7 +2470,6 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
break;
case OSMO_CC_MSG_DISC_REQ: /* call has been disconnected */
osmo_cc_helper_audio_negotiate(msg, &call->cc_session, &call->codec);
bridge_process_local(call);
if (call->state != ISDN_STATE_IN_SETUP
&& call->state != ISDN_STATE_IN_OVERLAP
&& call->state != ISDN_STATE_IN_PROCEEDING

View File

@ -17,6 +17,22 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Transmit algorithm:
*
* Transmit data is taken from jitter buffer.
*
* When data is received from b-channel the first time, two chunks of data are transmitted to ISDN interface in one block.
*
* When more data is received from b-channel, this extra chunk is transmitted to ISDN interface.
*
* This way the FIFO is kept with data all the time, to prevent underrund in hope that this application does not stall.
*
* The function that receives data and transmits data is bchannel_rx_tx().
*
* The jitter of received chunk is checked and debug is displayed accordingly.
*
*/
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
@ -49,32 +65,6 @@
#define B_TIMER_ACTIVATING 1 // seconds
#define B_TIMER_DEACTIVATING 1 // seconds
int check_mISDN_dsp(void)
{
char buffer[256];
int found = 0;
FILE *fp = fopen("/proc/modules", "r");
if (!fp) {
PDEBUG(DISDN, DEBUG_ERROR, "Failed to read /proc/modules.\n");
return -errno;
}
while((fgets(buffer, sizeof(buffer), fp))) {
buffer[sizeof(buffer) - 1] = '\0';
if (strstr(buffer, "mISDN_dsp"))
found = 1;
}
fclose(fp);
if (found)
return 0;
PDEBUG(DISDN, DEBUG_ERROR, "Required module 'mISDN_dsp' is not loaded. Run 'modprobe mISDN_dsp' to load the module!\n");
return -EINVAL;
}
/*
* Channel selection
*/
@ -687,7 +677,7 @@ void isdn_destroy(isdn_t *isdn_ep)
}
/* initialization and configuration to isdn interface instance */
int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const char *portname, int ntmode, int ptp, int layer1hold, int layer2hold, const char *channel_out, const char *channel_in, const char *timeouts, int tx_delay, int tx_gain, int rx_gain, const char *pipeline, int dtmf, int local_tones, int serving_location)
int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const char *portname, int ntmode, int ptp, int layer1hold, int layer2hold, const char *channel_out, const char *channel_in, const char *timeouts, int tx_delay, int local_tones, int serving_location)
{
int rc;
void *mui;
@ -721,10 +711,6 @@ int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const cha
isdn_ep->l2hold = layer2hold;
isdn_ep->timeouts = timeouts;
isdn_ep->tx_delay = tx_delay;
isdn_ep->tx_gain = tx_gain;
isdn_ep->rx_gain = rx_gain;
isdn_ep->pipeline = pipeline;
isdn_ep->dtmf = dtmf;
isdn_ep->local_tones = local_tones;
isdn_ep->serving_location = serving_location;
@ -775,7 +761,6 @@ void isdn_add_msn(isdn_t *isdn_ep, const char *msn)
call_t *call_create(isdn_t *isdn_ep, int direction, int channel, int exclusive, int mode)
{
call_t *call, **call_p;
int rc;
call = calloc(1, sizeof(*call));
if (!call) {
@ -796,18 +781,7 @@ call_t *call_create(isdn_t *isdn_ep, int direction, int channel, int exclusive,
call->b_reserve = 0;
call->b_mode = mode;
call->hold = 0;
call->tx_gain = isdn_ep->tx_gain;
call->rx_gain = isdn_ep->rx_gain;
call->bridge = 0;
call->mute = 0;
call->txdata = 0;
call->tx_delay = isdn_ep->tx_delay;
call->echo = 0;
call->tone = 0;
call->rxoff = 0;
call->dtmf = isdn_ep->dtmf;
call->dtmf_threshold = 0;
call->pipeline = isdn_ep->pipeline;
call->isdn_tone.tone = 0;
/* if any channel requested by constructor */
if (channel == CHANNEL_ANY) {
@ -820,22 +794,40 @@ call_t *call_create(isdn_t *isdn_ep, int direction, int channel, int exclusive,
if (channel > 0) // only if constructor was called with a channel resevation
seize_bchannel(call, channel, exclusive);
/* allocate jitter buffer */
rc = jitter_create(&call->dejitter, 8000 / 10); // FIXME: size
if (rc < 0)
abort();
PDEBUG(DISDN, DEBUG_DEBUG, "Created new call on port #%d:%s\n", isdn_ep->portnum, isdn_ep->portname);
return call;
}
void call_create_jitter(call_t *call, int data)
{
isdn_t *isdn_ep = call->isdn_ep;
int rc;
/* allocate jitter buffer */
if (isdn_ep->tx_delay)
rc = jitter_create(&call->tx_dejitter, "tx", 8000, sizeof(uint8_t), (double)isdn_ep->tx_delay / 1000.0, (double)isdn_ep->tx_delay / 1000.0 * 2.0, JITTER_FLAG_NONE);
else if (data)
rc = jitter_create(&call->tx_dejitter, "tx", 8000, sizeof(uint8_t), JITTER_DATA);
else
rc = jitter_create(&call->tx_dejitter, "tx", 8000, sizeof(uint8_t), JITTER_AUDIO);
if (rc < 0)
abort();
rc = jitter_create(&call->conf_dejitter, "conference", 8000, sizeof(int16_t), JITTER_AUDIO);
if (rc < 0)
abort();
}
void call_destroy(call_t *call)
{
call_t **call_p;
/* be sure to clean up bridge on server */
bridge_socket_client_update(call, 0);
/* free jitter buffer */
jitter_destroy(&call->dejitter);
jitter_destroy(&call->tx_dejitter);
jitter_destroy(&call->conf_dejitter);
/* remove B-channel relation */
drop_bchannel(call);
@ -863,56 +855,24 @@ void call_destroy(call_t *call)
* bchannel handling
*/
/* send control information to the channel (dsp-module) */
static void ph_control(int sock, uint32_t c1, uint32_t c2)
/* send control information to the channel (HFC hardware bridging) */
static void im_control(int sock, uint32_t op, uint32_t channel, uint32_t p1, uint32_t p2)
{
uint8_t buffer[MISDN_HEADER_LEN + 4 + 4];
struct mISDNhead *ctrl = (struct mISDNhead *)buffer;
uint32_t *d = (uint32_t *)(buffer + MISDN_HEADER_LEN);
int len = 8;
struct mISDN_ctrl_req cq;
int rc;
if (sock < 0)
return;
if (c1 == DTMF_TONE_START && c2 == 0)
len = 4;
PDEBUG(DISDN, DEBUG_DEBUG, "sending IMCTRLREQ 0x%08x, %d, 0x%02x, 0x%02x\n", op, channel, p1, p2);
PDEBUG(DISDN, DEBUG_DEBUG, "sending PH_CONTROL_REQ %d, %d\n", c1, c2);
ctrl->prim = PH_CONTROL_REQ;
ctrl->id = 0;
*d++ = c1;
*d++ = c2;
rc = sendto(sock, buffer, MISDN_HEADER_LEN + len, 0, NULL, 0);
if (rc <= 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Failed to send to socket %d (errno=%d:%s)\n", sock, errno, strerror(errno));
abort();
}
}
/* send control information, but with different length */
static void ph_control_block(int sock, uint32_t c1, const void *c2, int c2_len)
{
uint8_t *buffer[MISDN_HEADER_LEN + 4 + c2_len];
struct mISDNhead *ctrl = (struct mISDNhead *)buffer;
uint32_t *d = (uint32_t *)(buffer + MISDN_HEADER_LEN);
int rc;
if (sock < 0)
return;
PDEBUG(DISDN, DEBUG_DEBUG, "sending PH_CONTROL_REQ %d\n", c1);
ctrl->prim = PH_CONTROL_REQ;
ctrl->id = 0;
*d++ = c1;
memcpy(d, c2, c2_len);
rc = sendto(sock, buffer, sizeof(buffer), 0, NULL, 0);
if (rc <= 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Failed to send to socket %d (errno=%d:%s)\n", sock, errno, strerror(errno));
abort();
}
cq.op = op;
cq.channel = channel;
cq.p1 = p1;
cq.p2 = p2;
rc = ioctl(sock, IMCTRLREQ, &cq);
if (rc < 0)
PDEBUG(DISDN, DEBUG_ERROR, "Failed to send IMCTRLREQ to socket %d (errno=%d:%s)\n", sock, errno, strerror(errno));
}
/* create B-channel stack */
@ -935,7 +895,7 @@ static int bchannel_create(isdn_t *isdn_ep, int index)
if (isdn_ep->b_mode[index] == B_MODE_HDLC) {
PDEBUG(DISDN, DEBUG_NOTICE, "Use B-Channel with HDLC!!!\n");
}
rc = socket(PF_ISDN, SOCK_DGRAM, (isdn_ep->b_mode[index] == B_MODE_HDLC) ? ISDN_P_B_L2DSPHDLC : ISDN_P_B_L2DSP);
rc = socket(PF_ISDN, SOCK_DGRAM, (isdn_ep->b_mode[index] == B_MODE_HDLC) ? ISDN_P_B_HDLC : ISDN_P_B_RAW);
if (rc < 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Failed to open bchannel-socket for index %d with mISDN-DSP layer. Did you load mISDN_dsp.ko?\n", index);
return(0);
@ -1000,107 +960,45 @@ static void bchannel_activate(isdn_t *isdn_ep, int index, int activate, int time
/* configure B-channel */
static void bchannel_configure(isdn_t *isdn_ep, int index)
{
int channel = index + 1 + (index >= 15);
call_t *call;
int handle, mode;
int handle;
if (isdn_ep->b_sock[index] <= 0)
return;
handle = isdn_ep->b_sock[index];
call = isdn_ep->b_call[index];
mode = isdn_ep->b_mode[index];
/* set dsp features */
if (call->txdata) {
PDEBUG(DISDN, DEBUG_DEBUG, "PH_CONTROL: set DSP_TXDATA*\n");
ph_control(handle, (call->txdata) ? DSP_TXDATA_ON : DSP_TXDATA_OFF, 0);
}
if (call->tx_delay && mode == B_MODE_TRANSPARENT) {
PDEBUG(DISDN, DEBUG_DEBUG, "PH_CONTROL: set DSP_DELAY\n");
ph_control(handle, DSP_DELAY, call->tx_delay);
}
if (!call->tx_delay && mode == B_MODE_TRANSPARENT) {
PDEBUG(DISDN, DEBUG_DEBUG, "PH_CONTROL: set DSP_TX_DEJITTER\n");
ph_control(handle, DSP_TX_DEJITTER, 1);
}
if (call->tx_gain && mode == B_MODE_TRANSPARENT) {
PDEBUG(DISDN, DEBUG_DEBUG, "PH_CONTROL: set DSP_VOL_CHANGE_TX\n");
ph_control(handle, DSP_VOL_CHANGE_TX, call->tx_gain);
}
if (call->rx_gain && mode == B_MODE_TRANSPARENT) {
PDEBUG(DISDN, DEBUG_DEBUG, "PH_CONTROL: set DSP_VOL_CHANGE_RX\n");
ph_control(handle, DSP_VOL_CHANGE_RX, call->rx_gain);
}
if (call->pipeline && call->pipeline[0] && mode == B_MODE_TRANSPARENT) {
PDEBUG(DISDN, DEBUG_DEBUG, "PH_CONTROL: set DSP_PIPELINE_CFG\n");
ph_control_block(handle, DSP_PIPELINE_CFG, call->pipeline, strlen(call->pipeline) + 1);
}
if (call->bridge && !call->mute) {
PDEBUG(DISDN, DEBUG_DEBUG, "PH_CONTROL: set DSP_CONF_JOIN\n");
ph_control(handle, DSP_CONF_JOIN, call->bridge);
}
if (call->echo) {
PDEBUG(DISDN, DEBUG_DEBUG, "PH_CONTROL: set DSP_ECHO_ON\n");
ph_control(handle, DSP_ECHO_ON, 0);
}
if (call->tone && mode == B_MODE_TRANSPARENT) {
PDEBUG(DISDN, DEBUG_DEBUG, "PH_CONTROL: set DSP_TONE_PATT_ON (tone=%d)\n", call->tone);
ph_control(handle, DSP_TONE_PATT_ON, call->tone);
}
if (call->rxoff) {
PDEBUG(DISDN, DEBUG_DEBUG, "PH_CONTROL: set DSP_RECEIVE_OFF\n");
ph_control(handle, DSP_RECEIVE_OFF, 0);
}
if (call->dtmf && mode == B_MODE_TRANSPARENT) {
PDEBUG(DISDN, DEBUG_DEBUG, "PH_CONTROL: set DTMF_TONE_START\n");
ph_control(handle, DTMF_TONE_START, call->dtmf_threshold);
}
/* set PCM bridge features */
if (call->bridge_enabled)
im_control(handle, MISDN_CTRL_HFC_PCM_CONN, channel, call->bridge_slot_tx | (call->bridge_bank_tx << 8), call->bridge_slot_rx | (call->bridge_bank_rx << 8));
else
im_control(handle, MISDN_CTRL_HFC_PCM_DISC, channel, 0, 0);
}
void bchannel_bridge(call_t *call, int pcm_bridge, int rx_slot, int tx_slot, int rx_bank, int tx_bank)
{
/* bridge enabled */
call->bridge_enabled = pcm_bridge;
call->bridge_bank_tx = tx_bank;
call->bridge_slot_tx = tx_slot;
call->bridge_bank_rx = rx_bank;
call->bridge_slot_rx = rx_slot;
bchannel_configure(call->isdn_ep, call->b_index);
}
void bchannel_tone(call_t *call, int tone)
{
call->tone = tone;
if (call->b_index < 0)
return;
int handle = call->isdn_ep->b_sock[call->b_index];
int mode = call->isdn_ep->b_mode[call->b_index];
if (mode != B_MODE_TRANSPARENT)
return;
if (call->tone) {
PDEBUG(DISDN, DEBUG_DEBUG, "PH_CONTROL: set DSP_TONE_PATT_ON (tone=%d)\n", call->tone);
ph_control(handle, DSP_TONE_PATT_ON, call->tone);
} else {
PDEBUG(DISDN, DEBUG_DEBUG, "PH_CONTROL: set DSP_TONE_PATT_OFF\n");
ph_control(handle, DSP_TONE_PATT_OFF, 0);
}
}
/* set bridge ID for B-channel */
void bchannel_bridge(call_t *call, uint32_t bridge)
{
int *sock_p = &call->isdn_ep->b_sock[call->b_index];
int index, channel;
if (*sock_p <= 0) {
call->bridge = bridge;
return;
}
index = call->b_index;
channel = index + 1 + (index >= 15);
if (call->bridge != bridge) {
PDEBUG(DISDN, DEBUG_DEBUG, "Change bridge from %d to %d for channel %d.\n", call->bridge, bridge, channel);
if (index > -1 && call->isdn_ep->b_state[index] == B_STATE_ACTIVE)
ph_control(*sock_p, (bridge) ? DSP_CONF_JOIN : DSP_CONF_SPLIT, bridge);
} else
PDEBUG(DISDN, DEBUG_DEBUG, "We already have bridge=%d on channel %d.\n", bridge, channel);
call->bridge = bridge;
isdn_tone_set(&call->isdn_tone, tone);
}
/* destroy B-channel stack */
@ -1220,6 +1118,8 @@ void bchannel_event(isdn_t *isdn_ep, int index, int event)
/* bchannel is active and used by a call instance, so we configure bchannel */
bchannel_configure(isdn_ep, index);
state = B_STATE_ACTIVE;
call->b_rx_time = 0.0;
call->b_transmitting = 0;
//FIXME: init buffer state for delay
} else {
/* bchannel is active, but not used anymore (or has wrong stack config), so we deactivate */
@ -1322,10 +1222,11 @@ void bchannel_event(isdn_t *isdn_ep, int index, int event)
timer_start(&isdn_ep->b_timer[index], timer);
}
static void bchannel_receive(isdn_t *isdn_ep, int index, struct mISDNhead *hh, unsigned char *data, int len);
static void bchannel_rx_tx(isdn_t *isdn_ep, int index, struct mISDNhead *hh, unsigned char *data, int len);
static void bchannel_confirm(isdn_t *isdn_ep, int index);
/* handle frames from B-channel (kernel socket) */
static int bchannel_kernel_sock_work(isdn_t *isdn_ep, int index)
static int bchannel_kernel_sock_receive(isdn_t *isdn_ep, int index)
{
int channel = index + 1 + (index >= 15);
unsigned char buffer[2048+MISDN_HEADER_LEN];
@ -1346,6 +1247,8 @@ static int bchannel_kernel_sock_work(isdn_t *isdn_ep, int index)
switch (hh->prim) {
/* we don't care about confirms, we use rx data to sync tx */
case PH_DATA_CNF:
if (isdn_ep->b_call[index])
bchannel_confirm(isdn_ep, index);
break;
/* we receive audio data, we respond to it AND we send tones */
@ -1355,7 +1258,7 @@ static int bchannel_kernel_sock_work(isdn_t *isdn_ep, int index)
case DL_DATA_REQ:
case PH_CONTROL_IND:
if (isdn_ep->b_call[index])
bchannel_receive(isdn_ep, index, hh, buffer + MISDN_HEADER_LEN, rc - MISDN_HEADER_LEN);
bchannel_rx_tx(isdn_ep, index, hh, buffer + MISDN_HEADER_LEN, rc - MISDN_HEADER_LEN);
else
PDEBUG(DISDN, DEBUG_DEBUG, "b-channel is not associated to a call (channel %d), ignoring.\n", channel);
break;
@ -1393,10 +1296,14 @@ void bchannel_ph_sock_receive(void *priv, int channel, uint8_t prim, uint8_t *da
return;
switch (prim) {
case PH_PRIM_DATA_CNF:
if (isdn_ep->b_call[index])
bchannel_confirm(isdn_ep, index);
break;
case PH_PRIM_DATA_IND:
if (isdn_ep->b_call[index]) {
struct mISDNhead hh = { .prim = PH_DATA_IND };
bchannel_receive(isdn_ep, index, &hh, data, length);
bchannel_rx_tx(isdn_ep, index, &hh, data, length);
} else
PDEBUG(DISDN, DEBUG_DEBUG, "b-channel is not associated to a call (channel %d), ignoring.\n", channel);
break;
@ -1534,9 +1441,10 @@ static void send_to_rtp(call_t *call, unsigned char *data, int len)
return;
if (call->conference_3pty) {
int16_t *audio;
int16_t *audio_local;
int audio_len;
sample_t samples_local[len], samples_remote_active[len], samples_remote_hold[len], mix[len];
int16_t audio_mix[len], audio_remote_active[len], audio_remote_hold[len];
int32_t spl;
int i;
/* there should be no call on hold with audio coming from */
@ -1545,14 +1453,13 @@ static void send_to_rtp(call_t *call, unsigned char *data, int len)
/* convert local audio from interface to samples */
if (call->isdn_ep->law == 'a')
g711_decode_alaw_flipped(data, len, (uint8_t **)&audio, &audio_len);
g711_decode_alaw_flipped(data, len, (uint8_t **)&audio_local, &audio_len);
else
g711_decode_ulaw_flipped(data, len, (uint8_t **)&audio, &audio_len);
int16_to_samples(samples_local, audio, len);
g711_decode_ulaw_flipped(data, len, (uint8_t **)&audio_local, &audio_len);
// don't free audio, because we need that later when encoding
/* convert remote RTP to samples */
jitter_load(&call->dejitter, samples_remote_active, len);
jitter_load(&call->conf_dejitter, audio_remote_active, len);
/* search other party on hold */
other = call->isdn_ep->call_list;
@ -1566,101 +1473,174 @@ static void send_to_rtp(call_t *call, unsigned char *data, int len)
/* convert remote RTP to samples */
if (other)
jitter_load(&other->dejitter, samples_remote_hold, len);
jitter_load(&other->conf_dejitter, audio_remote_hold, len);
else
memset(samples_remote_hold, 0, sizeof(sample_t) * len);
memset(audio_remote_hold, 0, sizeof(*audio_remote_hold) * len);
/* mix audio for local interface and forward */
for (i = 0; i < len; i++)
mix[i] = samples_remote_active[i] + samples_remote_hold[i]; /* both remote parties */
samples_to_int16(audio, mix, len);
for (i = 0; i < len; i++) {
spl = (int32_t)audio_remote_active[i] + (int32_t)audio_remote_hold[i]; /* both remote parties */
if (spl < -32767)
spl = -32767;
if (spl > 32767)
spl = 32767;
audio_mix[i] = spl;
}
if (call->isdn_ep->law == 'a')
g711_encode_alaw_flipped((uint8_t *)audio, audio_len, &data, &len);
g711_encode_alaw_flipped((uint8_t *)audio_mix, audio_len, &data, &len);
else
g711_encode_ulaw_flipped((uint8_t *)audio, audio_len, &data, &len);
if (call->b_index >= 0) {
unsigned char buf[MISDN_HEADER_LEN + len];
struct mISDNhead *frm = (struct mISDNhead *)buf;
int rc = 0;
memcpy(buf + MISDN_HEADER_LEN, data, len);
frm->prim = PH_DATA_REQ;
frm->id = 0;
if (call->isdn_ep->ph_socket)
ph_socket_tx_msg(call->isdn_ep->ph_socket, call->b_channel, PH_PRIM_DATA_REQ, buf + MISDN_HEADER_LEN, len);
else
rc = send(call->isdn_ep->b_sock[call->b_index], buf, MISDN_HEADER_LEN + len, 0);
if (rc < 0)
PDEBUG(DISDN, DEBUG_ERROR, "write error B-channel data (socket #%d errno=%d:%s)\n", call->isdn_ep->b_sock[call->b_index], errno, strerror(errno));
g711_encode_ulaw_flipped((uint8_t *)audio_mix, audio_len, &data, &len);
if (call->b_index >= 0 && call->b_transmitting) {
jitter_save(&call->tx_dejitter, data, len, 0, 0, 0, 0);
}
free(data);
/* mix audio for (active) remote interface and forward */
for (i = 0; i < len; i++)
mix[i] = samples_local[i] + samples_remote_hold[i]; /* local + remote (hold) party */
samples_to_int16(audio, mix, len);
for (i = 0; i < len; i++) {
spl = audio_local[i] + audio_remote_hold[i]; /* local + remote (on hold) party */
if (spl < -32767)
spl = -32767;
if (spl > 32767)
spl = 32767;
audio_mix[i] = spl;
}
if (call->isdn_ep->law == 'a')
g711_encode_alaw_flipped((uint8_t *)audio, audio_len, &data, &len);
g711_encode_alaw_flipped((uint8_t *)audio_mix, audio_len, &data, &len);
else
g711_encode_ulaw_flipped((uint8_t *)audio, audio_len, &data, &len);
g711_encode_ulaw_flipped((uint8_t *)audio_mix, audio_len, &data, &len);
osmo_cc_rtp_send(call->codec, data, len, 1, len);
free(data);
/* mix audio for (hold) remote interface and forward */
if (other) {
for (i = 0; i < len; i++)
mix[i] = samples_local[i] + samples_remote_active[i]; /* local + remote (active) party */
samples_to_int16(audio, mix, len);
for (i = 0; i < len; i++) {
spl = audio_local[i] + audio_remote_active[i]; /* local + remote (active) party */
if (spl < -32767)
spl = -32767;
if (spl > 32767)
spl = 32767;
audio_mix[i] = spl;
}
if (call->isdn_ep->law == 'a')
g711_encode_alaw_flipped((uint8_t *)audio, audio_len, &data, &len);
g711_encode_alaw_flipped((uint8_t *)audio_mix, audio_len, &data, &len);
else
g711_encode_ulaw_flipped((uint8_t *)audio, audio_len, &data, &len);
g711_encode_ulaw_flipped((uint8_t *)audio_mix, audio_len, &data, &len);
osmo_cc_rtp_send(other->codec, data, len, 1, len);
free(data);
}
free(audio);
free(audio_local);
return;
}
/* bridging, don't forward */
if (call->bridge)
if (call->bridge_enabled)
return;
osmo_cc_rtp_send(call->codec, data, len, 1, len);
}
/* receive audio and control from B-channel */
static void bchannel_receive(isdn_t *isdn_ep, int index, struct mISDNhead *hh, unsigned char *data, int len)
/* receive audio and control from B-channel, transmit data from jitter buffer accoring to received length */
static void bchannel_rx_tx(isdn_t *isdn_ep, int index, struct mISDNhead *hh, unsigned char *data, int len)
{
uint8_t *buffer = isdn_ep->b_buffer[index];
int *buffer_pos = &(isdn_ep->b_buffer_pos[index]);
unsigned int cont = *((unsigned int *)data);
call_t *call = isdn_ep->b_call[index];
int i;
if (hh->prim == PH_CONTROL_IND) {
if (len < 4) {
PDEBUG(DISDN, DEBUG_ERROR, "SHORT READ OF PH_CONTROL INDICATION\n");
return;
}
if ((cont & (~DTMF_TONE_MASK)) == DTMF_TONE_VAL) {
// send_cc_dtmf(call, cont & DTMF_TONE_MASK);
// FIXME: DTMF via telephony events??
return;
}
return;
}
if (hh->prim != PH_DATA_IND && hh->prim != DL_DATA_IND)
if (hh->prim != PH_DATA_IND)
return;
/* add to buffer */
while (len) {
buffer[(*buffer_pos)++] = *data++;
len--;
if (!call) {
PDEBUG(DISDN, DEBUG_DEBUG, "Dropping b-channel data from channel without call.\n");
return;
}
if (isdn_ep->b_state[index] != B_STATE_ACTIVE) {
PDEBUG(DISDN, DEBUG_DEBUG, "Dropping b-channel data from inactive channel.\n");
return;
}
/* no transmission when bridging and playing no tones */
if (call->bridge_enabled && !call->isdn_tone.tone) {
call->b_transmitting = 0;
*buffer_pos = 0;
return;
}
/* check jitter of receved stream from ISDN */
double now = get_time();
if (call->b_rx_time) {
double elapsed = now - call->b_rx_time;
if ((int)(elapsed * 8000.0) - len > len / 4)
PDEBUG(DISDN, DEBUG_DEBUG, "Data received %d samples, time elapsed %d samples\n", len, (int)(elapsed * 8000.0));
}
call->b_rx_time = now;
/* add to buffer and send via RTP */
for (i = 0; i < len; i++) {
buffer[(*buffer_pos)++] = data[i];
if (*buffer_pos == 160) {
*buffer_pos = 0;
send_to_rtp(isdn_ep->b_call[index], buffer, 160);
send_to_rtp(call, buffer, 160);
}
}
/* prepare ISDN TX buffer */
unsigned char buf[MISDN_HEADER_LEN + len + len];
struct mISDNhead *frm = (struct mISDNhead *)buf;
int offset;
int rc = 0;
frm->prim = PH_DATA_REQ;
frm->id = 0;
if (!call->b_transmitting) {
PDEBUG(DISDN, DEBUG_DEBUG, "First received b-channel data, filling FIFO with double data of %d bytes.\n", len * 2);
memset(buf + MISDN_HEADER_LEN, 0xff, len);
offset = len;
} else {
// PDEBUG(DISDN, DEBUG_DEBUG, "More received b-channel data, filling FIFO with data of %d bytes.\n", len);
offset = 0;
}
/* load from TX jitter buffer and optionally overload with tones */
jitter_load(&call->tx_dejitter, buf + MISDN_HEADER_LEN + offset, len);
isdn_tone_copy(&call->isdn_tone, buf + MISDN_HEADER_LEN + offset, len);
/* forward to interface */
if (call->isdn_ep->ph_socket)
ph_socket_tx_msg(call->isdn_ep->ph_socket, call->b_channel, PH_PRIM_DATA_REQ, buf + MISDN_HEADER_LEN, offset + len);
else
rc = send(call->isdn_ep->b_sock[call->b_index], buf, MISDN_HEADER_LEN + offset + len, 0);
if (rc < 0)
PDEBUG(DISDN, DEBUG_ERROR, "Write error B-channel data (socket #%d errno=%d:%s)\n", call->isdn_ep->b_sock[call->b_index], errno, strerror(errno));
else
call->b_transmitting = 1;
}
/* receive confirm from bchannel */
static void bchannel_confirm(isdn_t *isdn_ep, int index)
{
call_t *call = isdn_ep->b_call[index];
if (!call) {
PDEBUG(DISDN, DEBUG_DEBUG, "Ignoring b-channel confirm of channel without call.\n");
return;
}
double now = get_time();
if (call->b_rx_time) {
double elapsed = now - call->b_rx_time;
if (elapsed > 4)
PDEBUG(DISDN, DEBUG_DEBUG, "Data confirmed, time elapsed %d samples\n", (int)(elapsed * 8000.0));
}
}
void isdn_rtp_work(isdn_t *isdn_ep)
@ -1675,8 +1655,8 @@ void isdn_rtp_work(isdn_t *isdn_ep)
}
}
/* send audio to B-channel */
void bchannel_send(struct osmo_cc_session_codec *codec, uint16_t __attribute__((unused)) sequence_number, uint32_t __attribute__((unused)) timestamp, uint8_t *data, int len)
/* send audio from RTP to B-channel's jitter buffer */
void rtp_receive(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len)
{
call_t *call = codec->media->session->priv;
@ -1684,38 +1664,31 @@ void bchannel_send(struct osmo_cc_session_codec *codec, uint16_t __attribute__((
if (call->conference_3pty) {
int16_t *audio;
int audio_len;
sample_t samples[len];
/* alaw/ulaw to linear */
if (call->isdn_ep->law == 'a')
g711_decode_alaw_flipped(data, len, (uint8_t **)&audio, &audio_len);
else
g711_decode_ulaw_flipped(data, len, (uint8_t **)&audio, &audio_len);
int16_to_samples(samples, audio, len);
free(audio);
/* enqueue data to jitter buffer */
jitter_save(&call->dejitter, samples, len);
jitter_save(&call->conf_dejitter, audio, len, 1, sequence_number, timestamp, ssrc);
free(audio);
return;
}
/* bridging, don't forward */
if (call->bridge)
if (call->bridge_enabled)
return;
/* not yet b_transmitting on bchannel */
if (!call->b_transmitting)
return;
/* ignore voice, if call is on hold */
if (call->hold)
return;
/* no conference, just forward to ISDN interface */
if (call->b_index >= 0) {
unsigned char buf[MISDN_HEADER_LEN + len];
struct mISDNhead *frm = (struct mISDNhead *)buf;
int rc = 0;
memcpy(buf + MISDN_HEADER_LEN, data, len);
frm->prim = PH_DATA_REQ;
frm->id = 0;
if (call->isdn_ep->ph_socket)
ph_socket_tx_msg(call->isdn_ep->ph_socket, call->b_channel, PH_PRIM_DATA_REQ, buf + MISDN_HEADER_LEN, len);
else
rc = send(call->isdn_ep->b_sock[call->b_index], buf, MISDN_HEADER_LEN + len, 0);
if (rc < 0)
PDEBUG(DISDN, DEBUG_ERROR, "write error B-channel data (socket #%d errno=%d:%s)\n", call->isdn_ep->b_sock[call->b_index], errno, strerror(errno));
}
jitter_save(&call->tx_dejitter, data, len, 1, sequence_number, timestamp, ssrc);
}
/*
@ -1955,6 +1928,15 @@ int isdn_open(isdn_t *isdn_ep)
if (isdn_ep->portname)
free(isdn_ep->portname);
isdn_ep->portname = strdup(devinfo.name);
if (!strncmp(isdn_ep->portname, "hfc-4s.", 7)
|| !strncmp(isdn_ep->portname, "hfc-8s.", 7)
|| !strncmp(isdn_ep->portname, "hfc-e1.", 7)) {
/* cards that support hardware bridging */
isdn_ep->bridge_possible = 1;
isdn_ep->bridge_cardnum = strtoul(isdn_ep->portname + 7, NULL, 10);
isdn_ep->bridge_portnum = portnum;
PDEBUG(DISDN, DEBUG_INFO, "Port can use HFC bridge of card %d.\n", isdn_ep->bridge_cardnum);
}
isdn_ep->ntmode = nt;
isdn_ep->pri = pri;
isdn_ep->ptp = ptp;
@ -1978,9 +1960,6 @@ int isdn_open(isdn_t *isdn_ep)
if (isdn_ep->ntmode && !isdn_ep->ptp)
isdn_ep->l2link = 1;
if (isdn_ep->l2sock)
PDEBUG(DISDN, DEBUG_DEBUG, "using 'mISDN_dsp.o' module\n");
return 0;
error:
@ -2155,7 +2134,7 @@ void isdn_bchannel_work(isdn_t *isdn_ep)
for (i = 0; i < isdn_ep->b_num; i++) {
do {
if (isdn_ep->b_sock[i] > 0)
w = bchannel_kernel_sock_work(isdn_ep, i);
w = bchannel_kernel_sock_receive(isdn_ep, i);
else
w = 0;
} while (w);

View File

@ -6,6 +6,7 @@
#include "../libsample/sample.h"
#include "../libjitter/jitter.h"
#include "../libph_socket/ph_socket.h"
#include "tones.h"
#define B_MODE_TRANSPARENT 0
#define B_MODE_HDLC 1
@ -78,10 +79,6 @@ typedef struct isdn {
int serving_location; /* who we serve when sending causes towards interface */
const char *timeouts;
int tx_delay;
int tx_gain;
int rx_gain;
const char *pipeline;
int dtmf;
int local_tones;
/* osmo-cc */
@ -117,6 +114,11 @@ typedef struct isdn {
uint8_t b_buffer[128][160];
int b_buffer_pos[128];
unsigned char l2mask[16]; /* 128 bits for each tei */
/* bridging */
int bridge_possible;
int bridge_cardnum;
int bridge_portnum;
} isdn_t;
typedef struct call_list {
@ -126,18 +128,9 @@ typedef struct call_list {
/* mISDN states */
uint32_t l3_pid;
uint16_t l3_ces;
int tx_gain;
int rx_gain;
int mute;
int txdata;
int tx_delay;
int echo;
uint32_t bridge;
int tone;
int rxoff;
int dtmf;
int dtmf_threshold;
const char *pipeline;
/* tone states */
struct isdn_tone isdn_tone;
/* osmo-cc states */
uint32_t cc_callref;
@ -154,6 +147,8 @@ typedef struct call_list {
int b_exclusive;
int b_reserve;
int b_mode;
int b_transmitting;
double b_rx_time;
/* call states */
int direction; /* originator or terminator of call */
@ -172,16 +167,19 @@ typedef struct call_list {
uint8_t park_callid[8];
/* bridge states */
int local_bridge; /* if local peer can bridge */
int remote_bridge; /* if remote peer can bridge */
int can_bridge; /* last state sent to the server */
int bridge_enabled; /* last state received by the server */
int bridge_bank_tx;
int bridge_slot_tx;
int bridge_bank_rx;
int bridge_slot_rx;
/* jitter buffer for 3pty call */
jitter_t dejitter;
/* jitter buffer for tx and 3pty call */
jitter_t tx_dejitter;
jitter_t conf_dejitter;
} call_t;
int check_mISDN_dsp(void);
/* channel selection */
int hunt_bchannel_in(isdn_t *isdn_ep, int channel, int exclusive);
int hunt_bchannel_out(isdn_t *isdn_ep, int *channel, int *exclusive);
@ -191,7 +189,7 @@ int open_bchannel_out(call_t *call, unsigned int cmd, int channel, int exclusive
/* isdn instance */
isdn_t *isdn_create(void);
void isdn_destroy(isdn_t *isdn_ep);
int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const char *portname, int ntmode, int ptp, int layer1hold, int layer2hold, const char *channel_out, const char *channel_in, const char *timeouts, int tx_delay, int tx_gain, int rx_gain, const char *pipeline, int dtmf, int local_tones, int serving_location);
int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const char *portname, int ntmode, int ptp, int layer1hold, int layer2hold, const char *channel_out, const char *channel_in, const char *timeouts, int tx_delay, int local_tones, int serving_location);
int isdn_open(isdn_t *isdn_ep);
void isdn_close(isdn_t *isdn_ep);
void isdn_add_msn(isdn_t *isdn_ep, const char *msn);
@ -201,13 +199,14 @@ void isdn_rtp_work(isdn_t *isdn_ep);
/* call instance */
call_t *call_create(isdn_t *isdn_ep, int direction, int channel, int exclusive, int mode);
void call_create_jitter(call_t *call, int data);
void call_destroy(call_t *call);
/* channel allocation and handling */
void bchannel_tone(call_t *call, int tone);
void bchannel_bridge(call_t *call, uint32_t bridge);
void bchannel_bridge(call_t *call, int pcm_bridge, int rx_slot, int tx_slot, int rx_bank, int tx_bank);
void bchannel_event(isdn_t *isdn_ep, int index, int event);
int seize_bchannel(call_t *call, int channel, int exclusive);
void drop_bchannel(call_t *call);
void bchannel_send(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len);
void rtp_receive(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len);

View File

@ -24,6 +24,7 @@
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <sched.h>
#include "../libdebug/debug.h"
#include "../liboptions/options.h"
#include "../libg711/g711.h"
@ -48,13 +49,13 @@ static const char *channel_out = NULL;
static const char *channel_in = NULL;
static const char *timeouts = NULL;
static int tx_delay = 0;
static int tx_gain = 0;
static int rx_gain = 0;
static const char *pipeline = NULL;
static int dtmf = 1;
static int local_tones = 0;
static int debug_mISDN = 0;
static int serving_location = 1; /* private network serving local user */
static int use_hfc_bridging = 1;
static int bridging_server_only = 0;
static int pcm_slots = 32;
static int rt_prio = 1;
#define MAX_CC_ARGS 1024
static int cc_argc = 0;
static const char *cc_argv[MAX_CC_ARGS];
@ -118,23 +119,20 @@ static void print_help()
printf(" --tx-delay <ms>\n");
printf(" Give a delay in milliseconds. This is required for modem/fax. Audio\n");
printf(" toward ISDN interface is buffered with the given delay.\n");
printf(" This feature turns off the dejittering.\n");
printf(" --tx-gain <dB>\n");
printf(" Changes gain of audio towards ISDN interface. Give Gain in steps of\n");
printf(" 6 dB. (-48 .. 48)\n");
printf(" --rx-gain <dB>\n");
printf(" Changes gain of audio coming from ISDN interface. Give Gain in steps\n");
printf(" of 6 dB. (-48 .. 48)\n");
printf(" --pipeline <string>\n");
printf(" mISDN allows to use echo cancellation modules. See mISDN documentation.\n");
printf(" --dtmf 1 | 0\n");
printf(" Turns DTMF detection on or off (default is %d).\n", dtmf);
printf(" This feature alters dejittering strategy.\n");
printf(" -T --local-tones german | oldgerman | american\n");
printf(" Send locally generated tones, if not provided by remote interface.\n");
printf(" -D --debug-misdn\n");
printf(" Enables mISDN stack debugging.\n");
printf(" --serving-location (see Q.931)\n");
printf(" 0 = user, 1 = private network serving local user (default=%d)\n", serving_location);
printf(" -B --bridging 0 | 1\n");
printf(" Enable or disable hardware bridging with HFC cards. (default = %d)\n", use_hfc_bridging);
printf(" --pcm-slots 32 | 64 | 128\n");
printf(" The number of slots must match the configured PCM bus size.\n");
printf(" (default = %d)\n", pcm_slots);
printf(" -r --realtime <prio>\n");
printf(" Set prio: 0 to disable, 99 for maximum (default = %d)\n", rt_prio);
printf(" -C --cc \"<osmo-cc arg>\" [--cc ...]\n");
printf(" Pass arguments to Osmo-CC endpoint. Use '-cc help' for description.\n");
}
@ -149,6 +147,8 @@ static void print_help()
#define OPT_PIPELINE 263
#define OPT_DTMF 264
#define OPT_SERVING 265
#define OPT_PCM_SLOTS 266
#define OPT_BR_ONLY 267
static void add_options(void)
{
@ -166,13 +166,13 @@ static void add_options(void)
option_add(OPT_CHANNEL_IN, "channel-in", 1);
option_add(OPT_TIMEOUTS, "timeouts", 1);
option_add(OPT_TX_DELAY, "tx-delay", 1);
option_add(OPT_TX_GAIN, "tx-gain", 1);
option_add(OPT_RX_GAIN, "rx-gain", 1);
option_add(OPT_PIPELINE, "pipeline", 1);
option_add(OPT_DTMF, "dtmf", 1);
option_add('T', "local-tones", 1);
option_add('D', "debug-misdn", 0);
option_add(OPT_SERVING, "serving-location", 0);
option_add(OPT_SERVING, "serving-location", 1);
option_add('B', "bridging", 1);
option_add(OPT_PCM_SLOTS, "pcm-slots", 1);
option_add(OPT_BR_ONLY, "bridging-server-only", 0);
option_add('r', "realtime", 1);
option_add('C', "cc", 1);
}
@ -234,18 +234,6 @@ static int handle_options(int short_option, int argi, char **argv)
case OPT_TX_DELAY:
tx_delay = atoi(argv[argi]);
break;
case OPT_TX_GAIN:
tx_gain = atoi(argv[argi]) / 6;
break;
case OPT_RX_GAIN:
rx_gain = atoi(argv[argi]) / 6;
break;
case OPT_PIPELINE:
pipeline = options_strdup(argv[argi]);
break;
case OPT_DTMF:
dtmf = atoi(argv[argi]);
break;
case 'T':
if (!strcasecmp(argv[argi], "american"))
local_tones = TONES_TYPE_AMERICAN;
@ -264,6 +252,18 @@ static int handle_options(int short_option, int argi, char **argv)
case OPT_SERVING:
serving_location = atoi(argv[argi]);
break;
case 'B':
use_hfc_bridging = atoi(argv[argi]);
break;
case OPT_PCM_SLOTS:
pcm_slots = strtoul(argv[argi], NULL, 10);
break;
case OPT_BR_ONLY:
bridging_server_only = 1;
break;
case 'r':
rt_prio = atoi(argv[argi]);
break;
case 'C':
if (!strcasecmp(argv[argi], "help")) {
osmo_cc_help();
@ -331,6 +331,17 @@ int main(int argc, char *argv[])
if (argi <= 0)
return argi;
if (bridging_server_only) {
bridge_socket_server_child(pcm_slots, 1);
return 0;
}
/* start bridge server */
if (use_hfc_bridging) {
brigde_socket_server(pcm_slots);
bridge_socket_client();
}
if (!misdn_kernel && !misdn_user) {
fprintf(stderr, "You defined no mISDN port or layer 1 socket. You must define either one of them! Use '-h' for help.\n");
goto error;
@ -340,13 +351,6 @@ int main(int argc, char *argv[])
goto error;
}
/* check for DSP (kernel only) */
if (misdn_kernel) {
rc = check_mISDN_dsp();
if (rc)
goto error;
}
/* init user space mISDN */
if (misdn_user) {
rc = mISDNInit((debug_mISDN) ? 0xffffffff : 0);
@ -365,20 +369,17 @@ int main(int argc, char *argv[])
layer3_initialized = 1;
mISDN_set_debug_level((debug_mISDN) ? 0xfffffeff : 0);
/* change tones to ulaw */
if (law == 'u')
isdn_tone_generate_ulaw_samples();
/* init instance */
rc = isdn_initialize(isdn_ep, (misdn_user) ? &ph_drv.ph_socket : NULL, law, portname, ntmode, ptp, layer1hold, layer2hold, channel_out, channel_in, timeouts, tx_delay, tx_gain, rx_gain, pipeline, dtmf, local_tones, serving_location);
rc = isdn_initialize(isdn_ep, (misdn_user) ? &ph_drv.ph_socket : NULL, law, portname, ntmode, ptp, layer1hold, layer2hold, channel_out, channel_in, timeouts, tx_delay, local_tones, serving_location);
if (rc) {
PDEBUG(DISDN, DEBUG_ERROR, "mISDN initializing failed!\n");
goto error;
}
/* open bridge notification socket */
rc = bridge_msg_open();
if (rc) {
PDEBUG(DISDN, DEBUG_ERROR, "This means that direct bridging is not supported!\n");
sleep(3);
}
rc = isdn_open(isdn_ep);
if (rc) {
PDEBUG(DISDN, DEBUG_ERROR, "mISDN open failed!\n");
@ -388,10 +389,22 @@ int main(int argc, char *argv[])
while ((p = strchr(portname, '/')))
portname = p + 1;
rc = osmo_cc_new(&isdn_ep->cc_ep, OSMO_CC_VERSION, portname, serving_location, cc_message, NULL, isdn_ep, cc_argc, cc_argv);
rc = osmo_cc_new(&isdn_ep->cc_ep, OSMO_CC_VERSION, isdn_ep->portname, serving_location, cc_message, NULL, isdn_ep, cc_argc, cc_argv);
if (rc < 0)
goto error;
/* real time priority */
if (rt_prio > 0) {
struct sched_param schedp;
int rc;
memset(&schedp, 0, sizeof(schedp));
schedp.sched_priority = rt_prio;
rc = sched_setscheduler(0, SCHED_RR, &schedp);
if (rc)
fprintf(stderr, "Error setting SCHED_RR with prio %d\n", rt_prio);
}
signal(SIGINT, sighandler);
signal(SIGHUP, sighandler);
signal(SIGTERM, sighandler);
@ -402,6 +415,7 @@ int main(int argc, char *argv[])
process_timer();
isdn_bchannel_work(isdn_ep);
isdn_rtp_work(isdn_ep);
bridge_socket_client_work(isdn_ep);
if (misdn_user) {
/* run workers of mISDN stacks in user space */
mISDN_work();
@ -410,7 +424,6 @@ int main(int argc, char *argv[])
w = 0;
w |= osmo_cc_handle();
w |= isdn_dchannel_work(isdn_ep);
w |= bridge_work(isdn_ep);
if (misdn_user) {
/* run workers of mISDN stacks in user space */
w |= ph_socket_work(&ph_drv.ph_socket);
@ -426,6 +439,15 @@ int main(int argc, char *argv[])
signal(SIGTERM, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
/* reset real time prio */
if (rt_prio > 0) {
struct sched_param schedp;
memset(&schedp, 0, sizeof(schedp));
schedp.sched_priority = 0;
sched_setscheduler(0, SCHED_OTHER, &schedp);
}
error:
if (isdn_ep) {
osmo_cc_delete(&isdn_ep->cc_ep);
@ -441,8 +463,6 @@ error:
if (misdn_initialized)
mISDN_cleanup();
bridge_msg_close();
options_free();
return 0;