Add kernel (hardware) bridging
This commit is contained in:
parent
63371cc055
commit
8c0419b70e
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue