From 8c0419b70ec461eb399dd52cc668524119feebc4 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 24 Jun 2022 19:58:41 +0200 Subject: [PATCH] Add kernel (hardware) bridging --- src/isdn/Makefile.am | 1 + src/isdn/bridge.c | 423 +++++++++++++++++++++++++++++++++++++++++++ src/isdn/bridge.h | 5 + src/isdn/dss1.c | 23 ++- src/isdn/isdn.c | 39 ++-- src/isdn/isdn.h | 13 +- src/isdn/main.c | 11 ++ 7 files changed, 495 insertions(+), 20 deletions(-) create mode 100644 src/isdn/bridge.c create mode 100644 src/isdn/bridge.h diff --git a/src/isdn/Makefile.am b/src/isdn/Makefile.am index 85300fa..bfb7363 100644 --- a/src/isdn/Makefile.am +++ b/src/isdn/Makefile.am @@ -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 diff --git a/src/isdn/bridge.c b/src/isdn/bridge.c new file mode 100644 index 0000000..6e25f67 --- /dev/null +++ b/src/isdn/bridge.c @@ -0,0 +1,423 @@ +/* kernel bridging + * + * (C) 2020 by Andreas Eversberg + * 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 . + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../libdebug/debug.h" +#include +#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; +} + diff --git a/src/isdn/bridge.h b/src/isdn/bridge.h new file mode 100644 index 0000000..0d8136f --- /dev/null +++ b/src/isdn/bridge.h @@ -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); diff --git a/src/isdn/dss1.c b/src/isdn/dss1.c index 474febe..dd49b7b 100644 --- a/src/isdn/dss1.c +++ b/src/isdn/dss1.c @@ -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 diff --git a/src/isdn/isdn.c b/src/isdn/isdn.c index ea9acf6..365b514 100644 --- a/src/isdn/isdn.c +++ b/src/isdn/isdn.c @@ -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]; diff --git a/src/isdn/isdn.h b/src/isdn/isdn.h index ade717e..ee23bb7 100644 --- a/src/isdn/isdn.h +++ b/src/isdn/isdn.h @@ -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); diff --git a/src/isdn/main.c b/src/isdn/main.c index 24c820a..369eb0e 100644 --- a/src/isdn/main.c +++ b/src/isdn/main.c @@ -30,6 +30,7 @@ #include #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;