Add kernel (hardware) bridging

This commit is contained in:
Andreas Eversberg 2022-06-24 19:58:41 +02:00
parent 63371cc055
commit 8c0419b70e
7 changed files with 495 additions and 20 deletions

View File

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

423
src/isdn/bridge.c Normal file
View File

@ -0,0 +1,423 @@
/* kernel bridging
*
* (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/>.
*/
/*
* How does it work:
*
*
* The initial states:
*
* There are two call instances that might be bridged: A call orignator, that
* receives a call from ISDN interface and a terminator, that sends a call
* towards ISDN interface. Both might be connected directly via RTP, so that
* is does not make sense to connect two ISDN channels, if bridging can be
* applied. Each instance has two states: The "local bridge" states if local
* peer can bridge. The "remote bridge" states if the remote peer can bridge.
* At the beginning of a call, both state indicate that neither one can bridge.
*
* Local process of bridge:
*
* If codec negotiation is complete or if a state changes, which change the
* ability to bridge, the local bridge processing is triggered. This process
* checks if the negotiation is complete, the remote RTP peer is on the local
* machine and if there is a state that might not allow bridging, like 3PTY
* conferencing. If bridging becomes possible or if it becomes impossible, a
* mulitcast message with originator's RTP port, the originator of the message
* (call originator or call terminator) and the ability to bridge or not is
* sent in a hope that the remote instance does support bridging also. The
* "local bridge" state is changed according to the ability to bridge.
*
* Remote process of bridge:
*
* If a mulicast message with the originator of the other peer and with the
* same originator's RTP port is received, the "remote bridge" state is
* changed according to the ability to bridge.
*
* Enable or disable bridge:
*
* If the "local bridge" and "remote bridge" changes to a state where both
* sides can bridge, the bridge is enabled. When both sides changes to a
* state where not both sides can bridge anymore, the bridge is disabled.
*
*/
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <ifaddrs.h>
#include <errno.h>
#include "../libdebug/debug.h"
#include <mISDN/mbuffer.h>
#include "isdn.h"
#include "bridge.h"
#define SOCKET_IP "224.0.8.15"
#define SOCKET_PORT 4711
#define BRIDGE_MAGIC 0x4953444e
#define BRIDGE_MISDN 0
struct bridge_msg {
uint32_t magic;
int bridge_type;
int direction;
int bridge;
uint16_t orig_port;
} __attribute__((packed));
static int bridge_sock = 0;
static struct sockaddr_in bridge_addr;
/* check if ip address is one of the local interfaces */
int check_local_address(sa_family_t family, struct sockaddr *addr)
{
struct ifaddrs *ifaddr, *ifa;
int ret = 0;
int rc;
rc = getifaddrs(&ifaddr);
if (rc < 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Failed to read list of interface addresses.\n");
return 0;
}
for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr)
continue;
if (family != ifa->ifa_addr->sa_family)
continue;
if (family == AF_INET) {
if (!memcmp(&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr,
&((struct sockaddr_in *)addr)->sin_addr,
sizeof(struct in_addr))) {
ret = 1;
break;
}
}
if (family == AF_INET6) {
if (!memcmp(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr,
&((struct sockaddr_in6 *)addr)->sin6_addr,
sizeof(struct in6_addr))) {
ret = 1;
break;
}
}
}
freeifaddrs(ifaddr);
return ret;
}
int bridge_msg_open(void)
{
int flags;
int rc;
/* resolve IP and port */
memset(&bridge_addr, 0, sizeof(bridge_addr));
bridge_addr.sin_family = AF_INET;
rc = inet_pton(AF_INET, SOCKET_IP, &bridge_addr.sin_addr);
if (rc < 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Failed to parse socket address from '%s', please fix. (errno=%d %s)\n", SOCKET_IP, errno, strerror(errno));
goto error;
}
bridge_addr.sin_port = htons(SOCKET_PORT);
/* create socket */
rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (rc < 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Failed to create multicast socket, please fix. (errno=%d %s)\n", errno, strerror(errno));
goto error;
}
bridge_sock = rc;
/* configure socket */
int reuse = 1;
rc = setsockopt(bridge_sock, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));
if (rc < 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Failed to set SO_REUSEPORT, please fix. (errno=%d %s)\n", errno, strerror(errno));
goto error;
}
/* bind socket */
struct sockaddr_in any_addr;
memset(&any_addr, 0, sizeof(any_addr));
any_addr.sin_family = AF_INET;
any_addr.sin_addr.s_addr = INADDR_ANY;
any_addr.sin_port = htons(SOCKET_PORT);
rc = bind(bridge_sock, (struct sockaddr *)&any_addr, sizeof(any_addr));
if (rc < 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Failed to bind multicast socket, please fix. (errno=%d %s)\n", errno, strerror(errno));
goto error;
}
/* configure socket */
uint8_t loop = 1;
rc = setsockopt(bridge_sock, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
if (rc < 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Failed to set IP_MULTICAST_LOOP, please fix. (errno=%d %s)\n", errno, strerror(errno));
goto error;
}
uint8_t ttl = 0;
rc = setsockopt(bridge_sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
if (rc < 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Failed to set IP_MULTICAST_TTL, please fix. (errno=%d %s)\n", errno, strerror(errno));
goto error;
}
struct ip_mreq mreq;
memset(&mreq, 0, sizeof(mreq));
mreq.imr_multiaddr.s_addr = bridge_addr.sin_addr.s_addr;
mreq.imr_interface.s_addr = INADDR_ANY;
rc = setsockopt(bridge_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
if (rc < 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Failed to set IP_ADD_MEMBERSHIP, please fix. (errno=%d %s)\n", errno, strerror(errno));
goto error;
}
/* set nonblocking io */
flags = fcntl(bridge_sock, F_GETFL);
flags |= O_NONBLOCK;
fcntl(bridge_sock, F_SETFL, flags);
return 0;
error:
bridge_msg_close();
return rc;
}
void bridge_msg_close(void)
{
if (bridge_sock > 0) {
close(bridge_sock);
bridge_sock = 0;
}
}
void bridge_process_local(call_t *call)
{
struct osmo_cc_session_media *media;
struct sockaddr_storage sa;
struct sockaddr_in6 *sa6;
struct sockaddr_in *sa4;
sa_family_t family = 0; //make GCC happy
struct bridge_msg msg;
int rc;
if (bridge_sock <= 0)
return;
memset(&msg, 0, sizeof(msg));
msg.magic = BRIDGE_MAGIC;
msg.bridge_type = BRIDGE_MISDN;
msg.direction = call->direction;
/* check if essential structures are set */
if (!call->cc_session || !call->cc_session->media_list) {
PDEBUG(DISDN, DEBUG_ERROR, "Codec/media not set, please fix!\n");
return;
}
media = call->cc_session->media_list;
/* check if codec negotiation is complete */
if (!media->description.port_local || !media->description.port_remote) {
PDEBUG(DISDN, DEBUG_DEBUG, "Codec negotiation is not complete, cannot bridge yet.\n");
goto send;
}
/* is the remote interface equal to the one of our interfaces? */
switch (media->connection_data_remote.addrtype) {
case osmo_cc_session_addrtype_ipv4:
family = AF_INET;
memset(&sa, 0, sizeof(sa));
sa4 = (struct sockaddr_in *)&sa;
sa4->sin_family = family;
rc = inet_pton(AF_INET, media->connection_data_remote.address, &sa4->sin_addr);
if (rc < 1) {
PDEBUG(DISDN, DEBUG_ERROR, "inet_pton failed, please fix!\n");
goto send;
}
break;
case osmo_cc_session_addrtype_ipv6:
family = AF_INET6;
memset(&sa, 0, sizeof(sa));
sa6 = (struct sockaddr_in6 *)&sa;
sa6->sin6_family = family;
rc = inet_pton(AF_INET6, media->connection_data_remote.address, &sa6->sin6_addr);
if (rc < 1) {
PDEBUG(DISDN, DEBUG_ERROR, "inet_pton failed, please fix!\n");
goto send;
}
break;
case osmo_cc_session_addrtype_unknown:
PDEBUG(DISDN, DEBUG_DEBUG, "Unsupported address type '%s'.\n", media->connection_data_remote.addrtype_name);
goto send;
}
rc = check_local_address(family, (struct sockaddr *)&sa);
if (rc < 1) {
PDEBUG(DISDN, DEBUG_DEBUG, "Remote RTP peer is not on this machine, cannot use hardware bridging.\n");
goto send;
}
/* check if we can bridge */
if (!call->isdn_ep->l2sock) {
PDEBUG(DISDN, DEBUG_DEBUG, "We do not support kernel mISDN, cannot use hardware bridging.\n");
goto send;
}
/* check if 3PTY */
if (call->conference_3pty) {
PDEBUG(DISDN, DEBUG_DEBUG, "Whe have 3PTY conference, cannot bridge while this is going on.\n");
goto send;
}
msg.bridge = 1;
send:
/* state does not change, no action is taken */
if (call->local_bridge == msg.bridge)
return;
/* get port */
if (call->direction == DIRECTION_ORIGINATOR)
msg.orig_port = media->description.port_local;
else
msg.orig_port = media->description.port_remote;
/* send bridge request via multicast */
PDEBUG(DISDN, DEBUG_INFO, "Sending bridge information via multicast: from %s, bridging %s, orig port %d.\n", (call->direction == DIRECTION_ORIGINATOR) ? "originator" : "terminator", (msg.bridge) ? "yes" : "no", msg.orig_port);
rc = sendto(bridge_sock, &msg, sizeof(msg), 0, (struct sockaddr *)&bridge_addr, sizeof(bridge_addr));
if (rc != sizeof(msg)) {
PDEBUG(DISDN, DEBUG_ERROR, "Failed to send bridge message to multicast socket! (errno=%d %s)\n", errno, strerror(errno));
return;
}
/* bridge becomes possible */
if (!call->local_bridge && call->remote_bridge && msg.bridge) {
PDEBUG(DISDN, DEBUG_NOTICE, "Activate bridging, because our peer and remote peer support bridging.\n");
bchannel_bridge(call, msg.orig_port);
}
/* bridge becomes impossible */
if (call->local_bridge && call->remote_bridge && !msg.bridge) {
PDEBUG(DISDN, DEBUG_NOTICE, "Deactivate bridging, because our peer does not support bridging anymore.\n");
bchannel_bridge(call, 0);
}
/* change state */
call->local_bridge = msg.bridge;
}
static int bridge_process_remote(isdn_t *isdn_ep)
{
struct bridge_msg msg;
call_t *call;
struct osmo_cc_session_media *media;
int rc;
if (bridge_sock <= 0)
return 0;
/* read message and check if it is a bridge message */
rc = recv(bridge_sock, &msg, sizeof(msg), 0);
if (rc < 0) {
if (errno == EAGAIN)
return 0;
return rc;
}
if (rc != sizeof(msg)) {
PDEBUG(DISDN, DEBUG_ERROR, "Failed to receive bridge message from multicast socket: Message has invalid length\n");
return -EINVAL;
}
if (msg.magic != BRIDGE_MAGIC) {
PDEBUG(DISDN, DEBUG_ERROR, "Failed to receive bridge message from multicast socket: Message has invalid content\n");
return -EINVAL;
}
/* check if we support that type of bridge */
if (msg.bridge_type != BRIDGE_MISDN) {
PDEBUG(DISDN, DEBUG_DEBUG, "Received bridge message, but not compatible with out technology.\n");
return -EINVAL;
}
PDEBUG(DISDN, DEBUG_INFO, "Received bridge information via multicast: from %s, bridging %s, orig port %d.\n", (msg.direction == DIRECTION_ORIGINATOR) ? "originator" : "terminator", (msg.bridge) ? "yes" : "no", msg.orig_port);
/* search for call that has the indicated port */
for (call = isdn_ep->call_list; call; call = call->next) {
if (!call->cc_session || !call->cc_session->media_list)
continue;
media = call->cc_session->media_list;
if (msg.direction == DIRECTION_TERMINATOR && media->description.port_local == msg.orig_port)
break;
if (msg.direction == DIRECTION_ORIGINATOR && media->description.port_remote == msg.orig_port)
break;
}
if (!call) {
PDEBUG(DISDN, DEBUG_DEBUG, "Received bridge request, but not for us.\n");
return -EINVAL;
}
/* check if we can bridge */
if (!isdn_ep->l2sock) {
PDEBUG(DISDN, DEBUG_DEBUG, "We do not support kernel mISDN, cannot use hardware bridging.\n");
return -EINVAL;
}
/* bridge becomes possible */
if (call->local_bridge && !call->remote_bridge && msg.bridge) {
PDEBUG(DISDN, DEBUG_NOTICE, "Activate bridging, because our peer and remote peer support bridging.\n");
bchannel_bridge(call, msg.orig_port);
}
/* bridge becomes impossible */
if (call->local_bridge && call->remote_bridge && !msg.bridge) {
PDEBUG(DISDN, DEBUG_NOTICE, "Deactivate bridging, because remote peer does not support bridging anymore.\n");
bchannel_bridge(call, 0);
}
/* change state */
if (!call->remote_bridge && msg.bridge)
PDEBUG(DISDN, DEBUG_DEBUG, "We know now, that remote peer supports bridging.\n");
if (call->remote_bridge && !msg.bridge)
PDEBUG(DISDN, DEBUG_DEBUG, "We know now, that remote peer does not supports bridging anymore.\n");
call->remote_bridge = msg.bridge;
return 1;
}
int bridge_work(isdn_t *isdn_ep)
{
int work = 0;
if (bridge_process_remote(isdn_ep))
work = 1;
return work;
}

5
src/isdn/bridge.h Normal file
View File

@ -0,0 +1,5 @@
int bridge_msg_open(void);
void bridge_msg_close(void);
void bridge_process_local(call_t *call);
int bridge_work(isdn_t *isdn_ep);

View File

@ -29,6 +29,7 @@
#include "../libg711/g711.h"
#include "isdn.h"
#include "dss1.h"
#include "bridge.h"
#include "ie.h"
#ifndef u_char
#define u_char unsigned char
@ -152,6 +153,8 @@ static void split_3pty(call_t *call)
/* remove conference state */
if (other) {
other->conference_3pty = 0;
/* process local briding capability */
bridge_process_local(other);
/* create osmo-cc message */
msg = osmo_cc_new_msg(OSMO_CC_MSG_NOTIFY_IND);
/* notify the facility */
@ -1166,6 +1169,9 @@ 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);
} else {
PDEBUG(DDSS1, DEBUG_NOTICE, "Phone requests conference, but no call on hold!\n");
@ -1445,7 +1451,7 @@ int dss1_receive(isdn_t *isdn_ep, uint32_t cmd, uint32_t pid, struct l3_msg *l3m
switch(cmd) {
case MT_SETUP:
/* creating call instance, transparent until setup with hdlc */
call = call_create(isdn_ep, 0, 0, B_MODE_TRANSPARENT);
call = call_create(isdn_ep, DIRECTION_ORIGINATOR, 0, 0, B_MODE_TRANSPARENT);
if (!call) {
PDEBUG(DDSS1, DEBUG_ERROR, "Cannot create calll instance.\n");
abort();
@ -1506,8 +1512,8 @@ void setup_req(call_t *call, osmo_cc_msg_t *msg)
PDEBUG(DDSS1, DEBUG_INFO, "SETUP REQUEST\n");
/* sdp accept, force our preferred codec */
sdp = osmo_cc_helper_audio_accept(&call->isdn_ep->cc_ep.session_config, call, (call->isdn_ep->law == 'a') ? codecs_alaw : codecs_ulaw, bchannel_send, msg, &call->cc_session, &call->codec, 1);
/* 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, (call->isdn_ep->law == 'a') ? codecs_alaw : codecs_ulaw, bchannel_send, msg, &call->cc_session, &call->codec, 1/*force*/);
if (!sdp) {
release_and_destroy(call, 47, 415/* Unsupported Media*/, 0);
return;
@ -1628,6 +1634,9 @@ void setup_req(call_t *call, osmo_cc_msg_t *msg)
/* send message to ISDN */
call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_SETUP, call->l3_pid, l3m);
/* process local briding capability */
bridge_process_local(call);
}
void proc_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg, int with_ies);
@ -2300,7 +2309,7 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg)
return;
}
/* creating call instance, transparent until setup with hdlc */
call = call_create(isdn_ep, 0, 0, B_MODE_TRANSPARENT);
call = call_create(isdn_ep, DIRECTION_TERMINATOR, 0, 0, B_MODE_TRANSPARENT);
if (!call) {
PDEBUG(DDSS1, DEBUG_ERROR, "Cannot create call instance.\n");
abort();
@ -2315,18 +2324,22 @@ 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 */
@ -2361,6 +2374,7 @@ 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
@ -2390,6 +2404,7 @@ 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

@ -32,6 +32,7 @@
#include "isdn.h"
#include "dss1.h"
#include "ie.h"
#include "bridge.h"
#ifndef u_char
#define u_char unsigned char
#endif
@ -771,7 +772,7 @@ void isdn_add_msn(isdn_t *isdn_ep, const char *msn)
* call instance
*/
call_t *call_create(isdn_t *isdn_ep, int channel, int exclusive, int mode)
call_t *call_create(isdn_t *isdn_ep, int direction, int channel, int exclusive, int mode)
{
call_t *call, **call_p;
int rc;
@ -788,6 +789,7 @@ call_t *call_create(isdn_t *isdn_ep, int channel, int exclusive, int mode)
*call_p = call;
call->isdn_ep = isdn_ep;
call->direction = direction;
call->b_index = -1;
call->b_channel = 0;
call->b_exclusive = 0;
@ -796,7 +798,7 @@ call_t *call_create(isdn_t *isdn_ep, int channel, int exclusive, int mode)
call->hold = 0;
call->tx_gain = isdn_ep->tx_gain;
call->rx_gain = isdn_ep->rx_gain;
call->conf = 0;
call->bridge = 0;
call->mute = 0;
call->txdata = 0;
call->tx_delay = isdn_ep->tx_delay;
@ -1033,9 +1035,9 @@ static void bchannel_configure(isdn_t *isdn_ep, int index)
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->conf && !call->mute) {
if (call->bridge && !call->mute) {
PDEBUG(DISDN, DEBUG_DEBUG, "PH_CONTROL: set DSP_CONF_JOIN\n");
ph_control(handle, DSP_CONF_JOIN, call->conf);
ph_control(handle, DSP_CONF_JOIN, call->bridge);
}
if (call->echo) {
PDEBUG(DISDN, DEBUG_DEBUG, "PH_CONTROL: set DSP_ECHO_ON\n");
@ -1077,28 +1079,29 @@ void bchannel_tone(call_t *call, int tone)
}
}
#if 0
/* set bridge ID for B-channel */
static void bchannel_conference(call_t *call, int oldconf, int newconf)
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 (*sock_p <= 0)
return;
if (oldconf != newconf) {
PDEBUG(DISDN, DEBUG_DEBUG, "change conference from conf=%d to conf=%d for channel %d\n", oldconf, newconf, channel);
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, (newconf) ? DSP_CONF_JOIN : DSP_CONF_SPLIT, newconf);
ph_control(*sock_p, (bridge) ? DSP_CONF_JOIN : DSP_CONF_SPLIT, bridge);
} else
PDEBUG(DISDN, DEBUG_DEBUG, "we already have conf=%d at channel %d\n", newconf, channel);
PDEBUG(DISDN, DEBUG_DEBUG, "We already have bridge=%d on channel %d.\n", bridge, channel);
call->bridge = bridge;
}
#endif
/* destroy B-channel stack */
static void bchannel_destroy(isdn_t *isdn_ep, int index)
@ -1619,6 +1622,10 @@ static void send_to_rtp(call_t *call, unsigned char *data, int len)
return;
}
/* bridging, don't forward */
if (call->bridge)
return;
osmo_cc_rtp_send(call->codec, data, len, 1, len);
}
@ -1690,6 +1697,10 @@ void bchannel_send(struct osmo_cc_session_codec *codec, uint16_t __attribute__((
return;
}
/* bridging, don't forward */
if (call->bridge)
return;
/* no conference, just forward to ISDN interface */
if (call->b_index >= 0) {
unsigned char buf[MISDN_HEADER_LEN + len];

View File

@ -25,6 +25,9 @@
#define TONES_TYPE_GERMAN 2
#define TONES_TYPE_OLDGERMAN 3
#define DIRECTION_ORIGINATOR 0
#define DIRECTION_TERMINATOR 1
enum isdn_state {
ISDN_STATE_IDLE = 0, /* no call */
ISDN_STATE_IN_SETUP, /* incoming connection */
@ -129,7 +132,7 @@ typedef struct call_list {
int txdata;
int tx_delay;
int echo;
int conf;
uint32_t bridge;
int tone;
int rxoff;
int dtmf;
@ -153,6 +156,7 @@ typedef struct call_list {
int b_mode;
/* call states */
int direction; /* originator or terminator of call */
enum isdn_state state;
int any_dialing; /* if any digit was dialed, we track this for dial tone */
int channel_negotiated;
@ -167,6 +171,10 @@ typedef struct call_list {
int park_len;
uint8_t park_callid[8];
/* bridge states */
int local_bridge; /* if local peer can bridge */
int remote_bridge; /* if remote peer can bridge */
/* jitter buffer for 3pty call */
jitter_t dejitter;
@ -192,11 +200,12 @@ void isdn_bchannel_work(isdn_t *isdn_ep);
void isdn_rtp_work(isdn_t *isdn_ep);
/* call instance */
call_t *call_create(isdn_t *isdn_ep, int channel, int exclusive, int mode);
call_t *call_create(isdn_t *isdn_ep, int direction, int channel, int exclusive, int mode);
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_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);

View File

@ -30,6 +30,7 @@
#include <mISDN/mbuffer.h>
#include "isdn.h"
#include "dss1.h"
#include "bridge.h"
#include "../libmisdn/core.h"
#include "ph_driver.h"
@ -371,6 +372,13 @@ int main(int argc, char *argv[])
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");
@ -402,6 +410,7 @@ 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);
@ -432,6 +441,8 @@ error:
if (misdn_initialized)
mISDN_cleanup();
bridge_msg_close();
options_free();
return 0;