From e6aa5385621e80440646f12028cd1498e253f565 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 19 Aug 2022 16:27:41 +0200 Subject: [PATCH] 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 --- src/isdn/Makefile.am | 1 + src/isdn/bridge.c | 1006 ++++++++++++++++++++++++++++++------------ src/isdn/bridge.h | 11 +- src/isdn/dss1.c | 118 +++-- src/isdn/isdn.c | 491 ++++++++++----------- src/isdn/isdn.h | 49 +- src/isdn/main.c | 122 ++--- 7 files changed, 1136 insertions(+), 662 deletions(-) diff --git a/src/isdn/Makefile.am b/src/isdn/Makefile.am index 539b22f..564dc82 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 \ + tones.c \ bridge.c \ ph_driver.c \ main.c diff --git a/src/isdn/bridge.c b/src/isdn/bridge.c index 6e25f67..eb0d5c8 100644 --- a/src/isdn/bridge.c +++ b/src/isdn/bridge.c @@ -1,6 +1,6 @@ -/* kernel bridging +/* HFC PCM bridging server and client * - * (C) 2020 by Andreas Eversberg + * (C) 2022 by Andreas Eversberg * All Rights Reserved * * This program is free software: you can redistribute it and/or modify @@ -20,47 +20,47 @@ /* * How does it work: * + * The time slot assignment is done by a server process that runs on a Unix + * socket. The server is implemented in this application and is started by + * the first instance of this application. Each instance of this application + * connects to the server. If all instances of this applications disconnect + * (exit) from the server, it will terminate itself. * - * The initial states: + * If a call is made, the client need to check if it can bridge: * - * 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. + * - There must be no 3pty call, this would not allow bridging. + * Note: Transmitting tones will override bridge inside HFC card, so no + * disconnect is required. + * - The RTP port of the originator (interface -> osmo-cc) must be known. + * - The terminator (osmo-cc -> interface) knows that the originator's RTP IP + * is on the local machine. * - * Local process of bridge: + * The client will then tell the server about card number, port of the card + * and channel of the port, together with the originator's RTP port. If there + * is another client with the same originator's RTP port, PCM time slots will + * be assigned to both clients. Then the clients will connect to the time + * slots that are given by the server. If the clients are on different cards, + * only one time slot will be assigned, because PCM bus has two lines (banks) + * for both directions. If the clients are on the same card, two time slots + * will be assigned, one for each direction. This is caused by a hardware + * limitation. * - * 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. + * If the client terminates the call or the call can no longer be bridged, it + * telles the server that it cannot bridge. The server will unassign the time + * slots to both clients, if they were assigned. Then the clients will + * disconnect from time slots, if connected. * - * 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. + * If the client terminates socket connection to the server (crash), the + * server will remove all assigned slots that were allocated to this clients. * */ + #include #include +#include #include -#include +#include #include #include #include @@ -72,22 +72,608 @@ #include "isdn.h" #include "bridge.h" -#define SOCKET_IP "224.0.8.15" -#define SOCKET_PORT 4711 +#define SOCKET_NAME "isdn_hfc_bridge_service_bla_blub_peng" +#define SOCKET_VERSION 0x4fc4fc01 -#define BRIDGE_MAGIC 0x4953444e -#define BRIDGE_MISDN 0 +/* message from client to server */ +struct msg_channel_info { + /* version magic */ + uint32_t version; + /* physical identity */ + int card; + int port; + int channel; + /* rtp stream identity */ + uint16_t orig_rtp_port; + /* can we bridge? */ + int can_bridge; +}; -struct bridge_msg { - uint32_t magic; - int bridge_type; - int direction; - int bridge; - uint16_t orig_port; -} __attribute__((packed)); +/* message from server to client */ +struct msg_bridge_order { + /* version magic */ + uint32_t version; + /* physical identity */ + int card; + int port; + int channel; + /* pcm connection */ + int pcm_bridge; + int rx_slot; + int tx_slot; + int rx_bank; + int tx_bank; +}; + +static int num_slots; + +/* + * server + */ + +struct conn { + struct conn *next; + int socket; + int rx_index; + struct msg_channel_info + rx_msg; +}; + +struct channel { + struct channel *next; + /* client realation */ + int socket; + /* physical identity */ + int card; + int port; + int channel; + /* rtp stream identity */ + uint16_t orig_rtp_port; + /* pcm connection */ + int pcm_bridge; + int rx_slot; + int tx_slot; + int rx_bank; + int tx_bank; +}; + +static int server_socket; +static struct conn *conn_list = NULL; +static struct channel *channel_list = NULL; + +static void free_channel(struct channel *c) +{ + struct channel **c_p; + + c_p = &channel_list; + while (*c_p) { + if (*c_p == c) + break; + c_p = &((*c_p)->next); + } + *c_p = c->next; + free(c); +} + +static void free_all_channels(void) +{ + struct channel *c, *c2; + + c = channel_list; + while (c) { + c2 = c; + c = c->next; + free(c2); + } + channel_list = NULL; +} + +static void free_connection(struct conn *c) +{ + struct conn **c_p; + + c_p = &conn_list; + while (*c_p) { + if (*c_p == c) + break; + c_p = &((*c_p)->next); + } + *c_p = c->next; + free(c); +} + +static void free_all_connections(void) +{ + struct conn *c, *c2; + + c = conn_list; + while (c) { + PDEBUG(DISDN, DEBUG_INFO, "Free pending bridge connection.\n"); + c2 = c; + close(c->socket); + c = c->next; + free(c2); + } + conn_list = NULL; +} + +/* function to open the server socket */ +static int open_server_socket(int daemon) +{ + struct sockaddr_un sock_address; + int flags; + int rc; + + memset(&sock_address, 0, sizeof(sock_address)); + sock_address.sun_family = AF_UNIX; + strcpy(sock_address.sun_path + 1, SOCKET_NAME); + + rc = socket(PF_UNIX, SOCK_STREAM, 0); + if (rc < 0) { + PDEBUG(DISDN, DEBUG_ERROR, "Failed to create UNIX socket.\n"); + return rc; + } + server_socket = rc; + + rc = bind(server_socket, (struct sockaddr *)(&sock_address), sizeof(struct sockaddr_un)); + if (rc < 0) { + if (!daemon || errno != EADDRINUSE) + PDEBUG(DISDN, DEBUG_INFO, "Failed to bind UNIX socket with path '%s' (errno = %d (%s)).\n", SOCKET_NAME, errno, strerror(errno)); + close(server_socket); + return -EADDRINUSE; + } + + rc = listen(server_socket, 10); + if (rc < 0) { + PDEBUG(DISDN, DEBUG_INFO, "Failed to listen to UNIX socket with path '%s' (errno = %d (%s)).\n", SOCKET_NAME, errno, strerror(errno)); + close(server_socket); + return rc; + } + + /* set nonblocking io */ + flags = fcntl(server_socket, F_GETFL); + flags |= O_NONBLOCK; + fcntl(server_socket, F_SETFL, flags); + + return 0; +} + +static void listen_server_socket(void) +{ + struct sockaddr_un sock_address; + socklen_t sock_len = sizeof(sock_address); + struct conn *c; + int flags; + int rc; + + rc = accept(server_socket, (struct sockaddr *)&sock_address, &sock_len); + if (rc > 0) { + PDEBUG(DISDN, DEBUG_INFO, "Connection from bridge socket client.\n"); + /* create connection */ + c = calloc(1, sizeof(*c)); + if (!c) { + PDEBUG(DISDN, DEBUG_ERROR, "No mem!\n"); + abort(); + } + c->next = conn_list; + conn_list = c; + c->socket = rc; + /* set nonblocking io */ + flags = fcntl(c->socket, F_GETFL); + flags |= O_NONBLOCK; + fcntl(c->socket, F_SETFL, flags); + /* reset rx buffer */ + c->rx_index = 0; + } +} + +static void rx_channel_info(int socket, int card, int port, int channel, uint16_t orig_rtp_port, int can_bridge); + +/* close server socket and remove all channels that are associated with it */ +void close_server_connection(int socket) +{ + struct channel *c; + + /* safely remove all channels by calling rx_channel_info, which also removes bridges from remote channels */ + do { + c = channel_list; + while (c) { + if (c->socket == socket) + break; + c = c->next; + } + if (c) { + c->socket = -1; + rx_channel_info(-1, c->card, c->port, c->channel, c->orig_rtp_port, 0); + } + } while (c); + + close(socket); +} + +/* read message from server socket */ +static void read_server_socket(struct conn *c) +{ + int rc; + + /* read message until complete */ + rc = recv(c->socket, ((uint8_t *)&c->rx_msg) + c->rx_index, sizeof(c->rx_msg) - c->rx_index, 0); + if (rc > 0) { + c->rx_index += rc; + if (c->rx_index == sizeof(c->rx_msg)) { + PDEBUG(DISDN, DEBUG_DEBUG, "Message from bridge socket client.\n"); + /* different version ? */ + if (c->rx_msg.version != SOCKET_VERSION) { + PDEBUG(DISDN, DEBUG_ERROR, "Bridge client uses different version than bridge server. Please update all applications to use same server version\n"); + return; + } + /* process message and reset buffer index */ + rx_channel_info(c->socket, c->rx_msg.card, c->rx_msg.port, c->rx_msg.channel, c->rx_msg.orig_rtp_port, c->rx_msg.can_bridge); + c->rx_index = 0; + return; + } + } else if (rc == 0 || errno != EAGAIN) { + PDEBUG(DISDN, DEBUG_DEBUG, "Close from bridge socket client.\n"); + close_server_connection(c->socket); + /* destroy connection */ + free_connection(c); + } +} + +static void sighandler(int sigset) +{ + PDEBUG(DISDN, DEBUG_DEBUG, "Signal %d received.\n", sigset); +} + +/* open socket, wait for connections and read incoming messages */ +void bridge_socket_server_child(int slots, int daemon) +{ + struct conn *c, *c2; + int count_ms = 0, delay_ms = 10; + int rc; + + num_slots = slots; + + rc = open_server_socket(daemon); + if (rc < 0) { + if (rc == -EADDRINUSE) + PDEBUG(DISDN, DEBUG_INFO, "Bridging socket is already running by a different instance.\n"); + return; + } + PDEBUG(DISDN, DEBUG_DEBUG, "Created bridging socket server.\n"); + + if (daemon) { + signal(SIGINT, sighandler); + signal(SIGHUP, sighandler); + signal(SIGTERM, sighandler); + signal(SIGPIPE, sighandler); + } + + while(42) { + listen_server_socket(); + c = conn_list; + if (daemon) { + /* timeout after some time without a connection */ + if (!c) { + count_ms += delay_ms; + if (count_ms >= 300) { + PDEBUG(DISDN, DEBUG_DEBUG, "All clients gone, exitting.\n"); + break; + } + } + } else { + /* show all existing channels */ + count_ms += delay_ms; + if (count_ms >= 1000) { + struct channel *ch = channel_list; + if (ch) + printf("Connections:\n"); + while (ch) { + printf("Card=%d Port=%d Channel=%d Bridge=%d\n", ch->card, ch->port, ch->channel, ch->pcm_bridge); + ch = ch->next; + } + count_ms = 0; + } + } + while (c) { + c2 = c->next; + read_server_socket(c); + c = c2; + } + usleep(delay_ms * 1000); + } + + free_all_channels(); + free_all_connections(); + close(server_socket); +} + +/* create socket server thread and return 0 on success, return -errno on failure */ +int brigde_socket_server(int slots) +{ + pid_t pid; + + pid = fork(); + if (pid < 0) { + PDEBUG(DISDN, DEBUG_ERROR, "fork() failed: errno=%d\n", errno); + return -EINVAL; + } + if (pid == 0) { + /* child */ + pid = fork(); + if (pid < 0) { + PDEBUG(DISDN, DEBUG_ERROR, "fork() failed: errno=%d\n", errno); + exit(0); + } + if (pid == 0) { + /* grandchild */ + bridge_socket_server_child(slots, 1); + } + /* exit child */ + exit(0); + } + + usleep(100000); + + return 0; +} + +void tx_bridge_order(int socket, int card, int port, int channel, int pcm_bridge, int rx_slot, int tx_slot, int rx_bank, int tx_bank); + +/* receive channel info client */ +static void rx_channel_info(int socket, int card, int port, int channel, uint16_t orig_rtp_port, int can_bridge) +{ + struct channel *c, *o, *t; + + /* first check if channel already exists */ + c = channel_list; + while (c) { + if (c->card == card && c->port == port && c->channel == channel) + break; + c = c->next; + } + if (c) { + /* we are already can bridge */ + if (can_bridge) { + PDEBUG(DISDN, DEBUG_DEBUG, "There is no change in bridge, do nothing.\n"); + return; + } + /* we must remove bridge, if remote channel exists */ + o = channel_list; + while (o) { + if (o != c && o->orig_rtp_port == c->orig_rtp_port) + break; + o = o->next; + } + /* remote channel exists, so remove bridge */ + if (o) { + if (o->pcm_bridge && o->socket > 0) { + PDEBUG(DISDN, DEBUG_DEBUG, "We cannot bridge anymore, remove remote channel's bridge.\n"); + tx_bridge_order(o->socket, o->card, o->port, o->channel, 0, 0, 0, 0, 0); + o->pcm_bridge = 0; + } + } + /* now we remove our channel */ + if (c->pcm_bridge && c->socket > 0) { + PDEBUG(DISDN, DEBUG_DEBUG, "We cannot bridge anymore, remove our bridge.\n"); + tx_bridge_order(c->socket, c->card, c->port, c->channel, 0, 0, 0, 0, 0); + c->pcm_bridge = 0; + } + PDEBUG(DISDN, DEBUG_DEBUG, "Freeing our channel.\n"); + free_channel(c); + return; + } + + /* if channel does not exists and we cannot bridge, do nothing */ + if (!can_bridge) { + PDEBUG(DISDN, DEBUG_DEBUG, "There is no change in bridge, do nothing.\n"); + return; + } + + /* channel does not exist, so we create it */ + /* create connection */ + PDEBUG(DISDN, DEBUG_DEBUG, "Creating our channel.\n"); + c = calloc(1, sizeof(*c)); + if (!c) { + PDEBUG(DISDN, DEBUG_ERROR, "No mem!\n"); + abort(); + } + c->next = channel_list; + channel_list = c; + c->socket = socket; + c->card = card; + c->port = port; + c->channel = channel; + c->orig_rtp_port = orig_rtp_port; + c->pcm_bridge = 0; + + /* if there is a remote channel, select pcm slots and send bridge order */ + o = channel_list; + while (o) { + if (o != c && o->orig_rtp_port == c->orig_rtp_port) + break; + o = o->next; + } + /* no other channel, so we do not need to send a bridge order */ + if (!o) { + PDEBUG(DISDN, DEBUG_DEBUG, "We can bridge, but there is no remote that can bridge (yet)..\n"); + return; + } + + PDEBUG(DISDN, DEBUG_DEBUG, "We can bridge, and remote can bridge, so we assign time slots.\n"); + + /* make a list of allocated time slots */ + uint8_t pcm_slot[num_slots]; + memset(pcm_slot, 0, sizeof(pcm_slot)); + t = channel_list; + while (t) { + if (t->pcm_bridge) { + pcm_slot[t->tx_slot] = 1; + pcm_slot[t->rx_slot] = 1; + } + t = t->next; + } + + /* hunt for free slot */ + if (c->card != o->card) { + int s; + /* different card, so we need one slot */ + for (s = 0; s < num_slots; s++) { + if (!pcm_slot[s]) + break; + } + if (s < num_slots) { + /* cross banks */ + c->pcm_bridge = 1; + c->rx_slot = s; + c->tx_slot = s; + c->rx_bank = 0; + c->tx_bank = 1; + o->pcm_bridge = 1; + o->rx_slot = s; + o->tx_slot = s; + o->rx_bank = 1; + o->tx_bank = 0; + } else { + PDEBUG(DISDN, DEBUG_NOTICE, "No single free slots, cannot set bridge.\n"); + return; + } + } else { + int s, s1, s2; + /* same card, so we need two slot */ + s1 = s2 = -1; + for (s = 0; s < num_slots; s++) { + if (!pcm_slot[s]) { + if (s1 < 0) + s1 = s; + else if (s2 < 0) { + s2 = s; + break; + } + } + } + if (s < num_slots) { + /* cross slots */ + c->pcm_bridge = 1; + c->rx_slot = s1; + c->tx_slot = s2; + c->rx_bank = 0; + c->tx_bank = 0; + o->pcm_bridge = 1; + o->rx_slot = s2; + o->tx_slot = s1; + o->rx_bank = 0; + o->tx_bank = 0; + } else { + PDEBUG(DISDN, DEBUG_NOTICE, "No two free slots, cannot set bridge.\n"); + return; + } + } + + /* bridge order to both ends */ + tx_bridge_order(c->socket, c->card, c->port, c->channel, c->pcm_bridge, c->rx_slot, c->tx_slot, c->rx_bank, c->tx_bank); + tx_bridge_order(o->socket, o->card, o->port, o->channel, o->pcm_bridge, o->rx_slot, o->tx_slot, o->rx_bank, o->tx_bank); +} + +/* send bridge order towards client */ +void tx_bridge_order(int socket, int card, int port, int channel, int pcm_bridge, int rx_slot, int tx_slot, int rx_bank, int tx_bank) +{ + struct msg_bridge_order msg; + int __attribute__((unused)) rc; + + memset(&msg, 0, sizeof(msg)); + msg.version = SOCKET_VERSION; + msg.card = card; + msg.port = port; + msg.channel = channel; + msg.pcm_bridge = pcm_bridge; + msg.rx_slot = rx_slot; + msg.tx_slot = tx_slot; + msg.rx_bank = rx_bank; + msg.tx_bank = tx_bank; + + rc = send(socket, ((uint8_t *)&msg), sizeof(msg), 0); +} + +/* + * client + */ + +static int client_socket = -1; +int client_rx_index = 0; +struct msg_bridge_order client_rx_msg; + +/* function to open the client socket */ +int bridge_socket_client(void) +{ + struct sockaddr_un sock_address; + int flags; + int rc; + + memset(&sock_address, 0, sizeof(sock_address)); + sock_address.sun_family = AF_UNIX; + strcpy(sock_address.sun_path + 1, SOCKET_NAME); + + rc = socket(PF_UNIX, SOCK_STREAM, 0); + if (rc < 0) { + PDEBUG(DISDN, DEBUG_ERROR, "Failed to create UNIX socket.\n"); + return rc; + } + client_socket = rc; + + rc = connect(client_socket, (struct sockaddr *)(&sock_address), sizeof(struct sockaddr_un)); + if (rc < 0) { + PDEBUG(DISDN, DEBUG_DEBUG, "Failed to connect to UNIX socket with path '%s' (errno = %d (%s)).\n", SOCKET_NAME, errno, strerror(errno)); + close(client_socket); + client_socket = 0; + return -errno; + } + + /* set nonblocking io */ + flags = fcntl(client_socket, F_GETFL); + flags |= O_NONBLOCK; + fcntl(client_socket, F_SETFL, flags); + + PDEBUG(DISDN, DEBUG_DEBUG, "Created bridging socket client.\n"); + + return 0; +} + +void rx_bridge_order(isdn_t *isdn_ep, int card, int port, int channel, int pcm_bridge, int rx_slot, int tx_slot, int rx_bank, int tx_bank); + +/* read message from client socket */ +void bridge_socket_client_work(isdn_t *isdn_ep) +{ + int rc; + +again: + /* no client */ + if (client_socket <= 0) + return; + + /* read message until complete */ + rc = recv(client_socket, ((uint8_t *)&client_rx_msg) + client_rx_index, sizeof(client_rx_msg) - client_rx_index, 0); + if (rc > 0) { + client_rx_index += rc; + if (client_rx_index == sizeof(client_rx_msg)) { + PDEBUG(DISDN, DEBUG_DEBUG, "Message from bridge socket server.\n"); + /* different version ? */ + if (client_rx_msg.version != SOCKET_VERSION) { + PDEBUG(DISDN, DEBUG_ERROR, "Bridge server uses different version than bridge client. Please update all applications to use same server version\n"); + return; + } + /* process message and reset buffer index */ + rx_bridge_order(isdn_ep, client_rx_msg.card, client_rx_msg.port, client_rx_msg.channel, client_rx_msg.pcm_bridge, client_rx_msg.rx_slot, client_rx_msg.tx_slot, client_rx_msg.rx_bank, client_rx_msg.tx_bank); + client_rx_index = 0; + goto again; + } + } else if (rc == 0 || errno != EAGAIN) { + PDEBUG(DISDN, DEBUG_DEBUG, "Close from bridge socket client.\n"); + close(client_socket); + client_socket = -1; + } +} -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) @@ -130,294 +716,156 @@ int check_local_address(sa_family_t family, struct sockaddr *addr) return ret; } - -int bridge_msg_open(void) +void rx_bridge_order(isdn_t *isdn_ep, int card, int port, int channel, int pcm_bridge, int rx_slot, int tx_slot, int rx_bank, int tx_bank) { - int flags; - int rc; + call_t *call; - /* 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; + if (!isdn_ep->bridge_possible || isdn_ep->bridge_cardnum != card || isdn_ep->bridge_portnum != port) { + PDEBUG(DISDN, DEBUG_ERROR, "Client received bridge order that does not match our port/port, please fix!\n"); } - /* 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; + /* hunt for call */ + call = isdn_ep->call_list; + while (call) { + if (call->b_channel == channel) + break; + call = call->next; + } + if (!call) { + PDEBUG(DISDN, DEBUG_DEBUG, "Client received bridge order that does not belong to an active call, ignoring!\n"); + return; } - /* 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; - } + if (pcm_bridge) + PDEBUG(DISDN, DEBUG_DEBUG, "Client received bridge connect order to connect to PCM slots (%d,%d,%d,%d).\n", rx_slot, tx_slot, rx_bank, tx_bank); + else + PDEBUG(DISDN, DEBUG_DEBUG, "Client received bridge disconnect order to disconnect to PCM slots.\n"); - /* 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; + bchannel_bridge(call, pcm_bridge, rx_slot, tx_slot, rx_bank, tx_bank); } -void bridge_msg_close(void) -{ - if (bridge_sock > 0) { - close(bridge_sock); - bridge_sock = 0; - } -} +static void tx_channel_info(int socket, int card, int port, int channel, uint16_t orig_rtp_port, int can_bridge); - -void bridge_process_local(call_t *call) +void bridge_socket_client_update(call_t *call, int enable) { 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; + uint16_t orig_rtp_port = 0; + int can_bridge = 0; int rc; - if (bridge_sock <= 0) + /* no client */ + if (client_socket <= 0) return; - memset(&msg, 0, sizeof(msg)); - msg.magic = BRIDGE_MAGIC; - msg.bridge_type = BRIDGE_MISDN; - msg.direction = call->direction; + PDEBUG(DISDN, DEBUG_INFO, "We got called with enable=%d\n", enable); + + /* do we have HFC card ? */ + if (!call->isdn_ep->bridge_possible && enable) { + PDEBUG(DISDN, DEBUG_INFO, "Our card is not an HFC card, cannot use hardware bridging.\n"); + return; + } + + if (!call->b_channel) { + PDEBUG(DISDN, DEBUG_DEBUG, "Cannot check bridge, no bchannel currently assigned.\n"); + return; + } /* 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"); + PDEBUG(DISDN, DEBUG_DEBUG, "Codec/media not set, not ready for bridging.\n"); return; } media = call->cc_session->media_list; /* check if codec negotiation is complete */ - if (!media->description.port_local || !media->description.port_remote) { + if (!media->description.port_local || (call->direction == DIRECTION_TERMINATOR && !media->description.port_remote)) { PDEBUG(DISDN, DEBUG_DEBUG, "Codec negotiation is not complete, cannot bridge yet.\n"); - goto send; + return; } - /* 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; + if (call->direction == DIRECTION_TERMINATOR) { + /* 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"); + return; + } + 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"); + return; + } + break; + case osmo_cc_session_addrtype_unknown: + PDEBUG(DISDN, DEBUG_DEBUG, "Unsupported address type '%s'.\n", media->connection_data_remote.addrtype_name); + return; } - 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); + rc = check_local_address(family, (struct sockaddr *)&sa); if (rc < 1) { - PDEBUG(DISDN, DEBUG_ERROR, "inet_pton failed, please fix!\n"); - goto send; + PDEBUG(DISDN, DEBUG_DEBUG, "Remote RTP peer is not on this machine, cannot use hardware bridging.\n"); + return; } - 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; + } else { + PDEBUG(DISDN, DEBUG_DEBUG, "We are originator, we may use hardware bridging.\n"); } - /* 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; + can_bridge = enable; send: - /* state does not change, no action is taken */ - if (call->local_bridge == msg.bridge) + /* only send on change */ + if (can_bridge && !call->can_bridge) { + PDEBUG(DISDN, DEBUG_INFO, "We tell the server we can do hardware bridging.\n"); + } else + if (!can_bridge && call->can_bridge) { + PDEBUG(DISDN, DEBUG_INFO, "We tell the server we cannot do hardware bridging anymore.\n"); + } else return; + call->can_bridge = can_bridge; /* get port */ if (call->direction == DIRECTION_ORIGINATOR) - msg.orig_port = media->description.port_local; + orig_rtp_port = media->description.port_local; else - msg.orig_port = media->description.port_remote; + orig_rtp_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; + tx_channel_info(client_socket, call->isdn_ep->bridge_cardnum, call->isdn_ep->bridge_portnum, call->b_channel, orig_rtp_port, can_bridge); } -static int bridge_process_remote(isdn_t *isdn_ep) +static void tx_channel_info(int socket, int card, int port, int channel, uint16_t orig_rtp_port, int can_bridge) { - struct bridge_msg msg; - call_t *call; - struct osmo_cc_session_media *media; - int rc; + struct msg_channel_info msg; + int __attribute__((unused)) rc; - if (bridge_sock <= 0) - return 0; + memset(&msg, 0, sizeof(msg)); + msg.version = SOCKET_VERSION; + msg.card = card; + msg.port = port; + msg.channel = channel; + msg.orig_rtp_port = orig_rtp_port; + msg.can_bridge = can_bridge; - /* 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; + rc = send(socket, ((uint8_t *)&msg), sizeof(msg), 0); } diff --git a/src/isdn/bridge.h b/src/isdn/bridge.h index 0d8136f..c2cef5c 100644 --- a/src/isdn/bridge.h +++ b/src/isdn/bridge.h @@ -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); + diff --git a/src/isdn/dss1.c b/src/isdn/dss1.c index c8836a1..fd51756 100644 --- a/src/isdn/dss1.c +++ b/src/isdn/dss1.c @@ -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 diff --git a/src/isdn/isdn.c b/src/isdn/isdn.c index d97141c..717fa52 100644 --- a/src/isdn/isdn.c +++ b/src/isdn/isdn.c @@ -17,6 +17,22 @@ * along with this program. If not, see . */ +/* 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 #include #include @@ -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); diff --git a/src/isdn/isdn.h b/src/isdn/isdn.h index 97a013f..1fab144 100644 --- a/src/isdn/isdn.h +++ b/src/isdn/isdn.h @@ -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); diff --git a/src/isdn/main.c b/src/isdn/main.c index 369eb0e..799f329 100644 --- a/src/isdn/main.c +++ b/src/isdn/main.c @@ -24,6 +24,7 @@ #include #include #include +#include #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 \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 \n"); - printf(" Changes gain of audio towards ISDN interface. Give Gain in steps of\n"); - printf(" 6 dB. (-48 .. 48)\n"); - printf(" --rx-gain \n"); - printf(" Changes gain of audio coming from ISDN interface. Give Gain in steps\n"); - printf(" of 6 dB. (-48 .. 48)\n"); - printf(" --pipeline \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 \n"); + printf(" Set prio: 0 to disable, 99 for maximum (default = %d)\n", rt_prio); printf(" -C --cc \"\" [--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;