osmo-cc-misdn-endpoint/src/isdn/isdn.c

2073 lines
56 KiB
C

/* layer 1/2 handling
*
* (C) 2020 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stddef.h>
#include <mISDN/mbuffer.h>
#include "../libdebug/debug.h"
#include "../libg711/g711.h"
#include "isdn.h"
#include "dss1.h"
#include "ie.h"
#ifndef u_char
#define u_char unsigned char
#endif
#include <mISDN/mlayer3.h>
#include <mISDN/q931.h>
#ifndef container_of
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#endif
#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
*/
/* set default out_channel */
static void default_out_channel(isdn_t *isdn_ep)
{
struct select_channel *selchannel;
selchannel = calloc(1, sizeof(struct select_channel));
if (isdn_ep->ntmode)
selchannel->channel = CHANNEL_FREE;
else
selchannel->channel = CHANNEL_ANY;
isdn_ep->out_channel = selchannel;
/* additional channel selection for multipoint NT ports */
if (!isdn_ep->ptp && isdn_ep->ntmode) {
selchannel->next = calloc(1, sizeof(struct select_channel));
selchannel = selchannel->next;
selchannel->channel = CHANNEL_NO; // call waiting
}
}
/* set default in_channel */
static void default_in_channel(isdn_t *isdn_ep)
{
struct select_channel *selchannel;
selchannel = calloc(1, sizeof(struct select_channel));
selchannel->channel = CHANNEL_FREE;
isdn_ep->in_channel = selchannel;
}
/* parse string for a positive number */
static int get_number(char *value)
{
int val = 0;
char text[10];
val = atoi(value);
sprintf(text, "%d", val);
if (!strcmp(value, text))
return val;
return -1;
}
/* remove element from buffer
* and return pointer to next element in buffer */
static char *get_separated(char *buffer)
{
while(*buffer) {
if (*buffer==',' || *buffer<=32) { /* separate */
*buffer++ = '\0';
while((*buffer>'\0' && *buffer<=32) || *buffer==',')
buffer++;
return(buffer);
}
buffer++;
}
return(buffer);
}
/* parse outgoing channel list */
static int parse_out_channel(isdn_t *isdn_ep, const char *_list)
{
char list[strlen(_list) + 1];
struct select_channel *selchannel, **selchannel_p = &isdn_ep->out_channel;
int val;
char *p, *el;
strcpy(list, _list);
p = list;
while(*p) {
el = p;
p = get_separated(p);
if (!strcasecmp(el, "force")) {
isdn_ep->out_channel_exclusive = 1;
if (isdn_ep->out_channel) {
PDEBUG(DISDN, DEBUG_ERROR, "Error outgoing channel string: value 'force' may only appear as first element in list.\n");
return(-1);
}
} else
if (!strcasecmp(el, "any")) {
val = CHANNEL_ANY;
goto selchannel;
} else
if (!strcasecmp(el, "free")) {
val = CHANNEL_FREE;
goto selchannel;
} else
if (!strcasecmp(el, "no")) {
val = CHANNEL_NO;
goto selchannel;
} else {
val = get_number(el);
if (val == -1) {
PDEBUG(DISDN, DEBUG_ERROR, "Error outgoing channel string: expecting a comma separated list of 'force', 'any', 'free', 'no' and any channel number.\n");
return(-1);
}
if (val<1 || val==16 || val>126) {
PDEBUG(DISDN, DEBUG_ERROR, "Error outgoing channel string: channel '%d' out of range.\n", val);
return(-1);
}
selchannel:
/* add to select-channel list */
selchannel = calloc(1, sizeof(struct select_channel));
/* set value */
selchannel->channel = val;
/* tail port */
*selchannel_p = selchannel;
selchannel_p = &((*selchannel_p)->next);
}
}
return(0);
}
/* parse incoming channel list */
static int parse_in_channel(isdn_t *isdn_ep, const char *_list)
{
char list[strlen(_list) + 1];
struct select_channel *selchannel, **selchannel_p = &isdn_ep->in_channel;
int val;
char *p, *el;
strcpy(list, _list);
p = list;
while(*p) {
el = p;
p = get_separated(p);
if (isdn_ep->in_channel) if (isdn_ep->in_channel->channel == CHANNEL_FREE) {
PDEBUG(DISDN, DEBUG_ERROR, "Error incoming channel list: values behind 'free' keyword have no effect.\n");
return(-1);
}
if (!strcasecmp(el, "free")) {
val = CHANNEL_FREE;
goto selchannel;
} else {
val = get_number(el);
if (val == -1) {
PDEBUG(DISDN, DEBUG_ERROR, "Error incoming channel list: expectng a comma separated list of channel numbers and 'free'.\n");
return(-1);
}
if (val<1 || val==16 || val>126) {
PDEBUG(DISDN, DEBUG_ERROR, "Error incoming channel list: channel '%d' out of range.\n", val);
return(-1);
}
selchannel:
/* add to select-channel list */
selchannel = calloc(1, sizeof(struct select_channel));
/* set value */
selchannel->channel = val;
/* tail port */
*selchannel_p = selchannel;
selchannel_p = &((*selchannel_p)->next);
}
}
return(0);
}
/* hunt bchannel for incoming setup */
int hunt_bchannel_in(isdn_t *isdn_ep, int channel, int exclusive)
{
struct select_channel *selchannel;
int i;
PDEBUG(DISDN, DEBUG_DEBUG, "Channel Selection (incoming call)\n");
if (exclusive<0)
exclusive = 0;
if (channel == CHANNEL_NO)
PDEBUG(DISDN, DEBUG_DEBUG, " -> request = no-channel\n");
else if (channel > 0)
PDEBUG(DISDN, DEBUG_DEBUG, " -> request = channel %d%s\n", channel, exclusive?" (forced)":"");
else
PDEBUG(DISDN, DEBUG_DEBUG, " -> request = any channel\n");
if (channel==CHANNEL_NO && !isdn_ep->ntmode) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = incoming call-waiting not supported for TE-mode\n");
return(-6); // channel unacceptable
}
if (channel <= 0) /* not given, no channel, whatever.. */
channel = CHANNEL_ANY; /* any channel */
if (isdn_ep->b_reserved >= isdn_ep->b_num) { // of out chan..
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = all channels are reserved\n");
return(-34); // no channel
}
if (channel == CHANNEL_ANY)
goto get_from_list;
if (channel > 0) {
/* check for given channel in selection list */
selchannel = isdn_ep->in_channel;
while(selchannel) {
if (selchannel->channel == channel || selchannel->channel == CHANNEL_FREE)
break;
selchannel = selchannel->next;
}
if (!selchannel)
channel = 0;
/* exclusive channel requests must be in the list */
if (exclusive) {
/* no exclusive channel */
if (!channel) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = exclusively requested channel not in list\n");
return(-6); // channel unacceptable
}
/* get index for channel */
i = channel-1-(channel>=17);
if (i < 0 || i >= isdn_ep->b_num || channel == 16) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = exclusively requested channel outside interface range\n");
return(-6); // channel unacceptable
}
/* check if busy */
if (isdn_ep->b_call[i] == NULL)
goto use_channel;
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = exclusively requested channel is busy\n");
return(-6); // channel unacceptable
}
/* requested channels in list will be used */
if (channel) {
/* get index for channel */
i = channel-1-(channel>=17);
if (i < 0 || i >= isdn_ep->b_num || channel == 16) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> quested channel %d outside interface range\n", channel);
} else /* if inside range (else) check if available */
if (isdn_ep->b_call[i] == NULL)
goto use_channel;
}
/* if channel is not available or not in list, it must be searched */
get_from_list:
/* check for first free channel in list */
channel = 0;
selchannel = isdn_ep->in_channel;
while(selchannel) {
switch(selchannel->channel) {
case CHANNEL_FREE: /* free channel */
PDEBUG(DISDN, DEBUG_DEBUG, " -> hunting free channel\n");
if (isdn_ep->b_reserved >= isdn_ep->b_num)
break; /* all channel in use or reserved */
/* find channel */
i = 0;
while(i < isdn_ep->b_num) {
if (isdn_ep->b_call[i] == NULL) {
channel = i+1+(i>=15);
break;
}
i++;
}
break;
default:
PDEBUG(DISDN, DEBUG_DEBUG, " -> hunting channel %d\n", selchannel->channel);
if (selchannel->channel<1 || selchannel->channel==16)
break; /* invalid channels */
i = selchannel->channel-1-(selchannel->channel>=17);
if (i >= isdn_ep->b_num)
break; /* channel not in port */
if (isdn_ep->b_call[i] == NULL) {
channel = selchannel->channel;
break;
}
break;
}
if (channel)
break; /* found channel */
selchannel = selchannel->next;
}
if (!channel) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = no channel available\n");
return(-6); // channel unacceptable
}
}
use_channel:
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = channel available\n");
PDEBUG(DISDN, DEBUG_DEBUG, " -> connect to channel %d\n", channel);
return(channel);
}
/* hunt bchannel for outgoing setup */
int hunt_bchannel_out(isdn_t *isdn_ep, int *channel, int *exclusive)
{
struct select_channel *selchannel;
int i;
PDEBUG(DISDN, DEBUG_DEBUG, "Channel Selection (outgoing call)\n");
/* see if link is up on PTP*/
if (isdn_ep->l2hold && isdn_ep->l2link<1) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = layer 2 is down\n");
return -27;
}
/* check for channel form selection list */
*exclusive = isdn_ep->out_channel_exclusive;
*channel = 0;
selchannel = isdn_ep->out_channel;
while(selchannel) {
switch(selchannel->channel) {
case CHANNEL_FREE: /* free channel */
if (isdn_ep->b_reserved >= isdn_ep->b_num)
break; /* all channel in use or reserved */
/* find channel */
i = 0;
while(i < isdn_ep->b_num) {
if (isdn_ep->b_call[i] == NULL) {
*channel = i+1+(i>=15);
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = free channel %d\n", *channel);
break;
}
i++;
}
if (*channel)
break;
PDEBUG(DISDN, DEBUG_DEBUG, " -> no free channel found\n");
break;
case CHANNEL_ANY: /* don't ask for channel */
if (isdn_ep->b_reserved >= isdn_ep->b_num) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> cannot ask for any channel, because all channel are reserved\n");
break; /* all channel in use or reserved */
}
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = any channel\n");
*channel = CHANNEL_ANY;
break;
case CHANNEL_NO: /* call waiting */
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = no channel\n");
*channel = CHANNEL_NO;
break;
default:
if (selchannel->channel<1 || selchannel->channel==16) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> channel %d is out of range\n", selchannel->channel);
break; /* invalid channels */
}
i = selchannel->channel-1-(selchannel->channel>=17);
if (i >= isdn_ep->b_num) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> channel %d is out of range\n", selchannel->channel);
break; /* channel not in port */
}
if (isdn_ep->b_call[i] == NULL) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = channel %d\n", selchannel->channel);
*channel = selchannel->channel;
break;
}
break;
}
if (*channel)
break; /* found channel */
selchannel = selchannel->next;
}
if (*channel)
return 0;
/* if channel was found, return channel */
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = no channel found\n");
return -34;
}
/* open channel of incoming setup */
int open_bchannel_in(call_t *call, int channel, int exclusive)
{
int rc;
rc = seize_bchannel(call, channel, exclusive);
if (rc < 0)
return rc;
bchannel_event(call->isdn_ep, call->b_index, B_EVENT_USE);
return 0;
}
/* open channel of outgoing setup */
int open_bchannel_out(call_t *call, unsigned int cmd, int channel, int exclusive)
{
int rc;
/* correct exclusive to 0, if no explicit channel was given */
if (exclusive < 0 || channel <= 0)
exclusive = 0;
/* select scenario */
if (call->b_channel && call->b_exclusive) {
/*** we gave an exclusive channel (or if we are done) ***/
/* if not first reply, we are done */
if (call->state != ISDN_STATE_OUT_SETUP)
return(0);
PDEBUG(DISDN, DEBUG_DEBUG, "Channel Selection (reply from outgoing call)\n");
PDEBUG(DISDN, DEBUG_DEBUG, " -> request = %d (forced)\n", call->b_channel);
PDEBUG(DISDN, DEBUG_DEBUG, (channel>=0)?" -> reply = %d\n":" -> reply = (none)\n", channel);
/* if give channel not accepted or not equal */
if (channel != -1 && call->b_channel != channel) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = forced channel not accepted\n");
return -44;
}
rc = seize_bchannel(call, channel, 1); // exclusively
if (rc < 0) {
PDEBUG(DISDN, DEBUG_DEBUG, "result = requested channel not available anymore\n");
return -47;
}
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = channel was accepted\n");
/* activate our exclusive channel */
bchannel_event(call->isdn_ep, call->b_index, B_EVENT_USE);
} else
if (call->b_channel) {
/*** we gave a non-exclusive channel ***/
/* if not first reply, we are done */
if (call->state != ISDN_STATE_OUT_SETUP)
return(0);
PDEBUG(DISDN, DEBUG_DEBUG, "Channel Selection (reply from outgoing call)\n");
PDEBUG(DISDN, DEBUG_DEBUG, " -> request = %d (suggest)\n", call->b_channel);
PDEBUG(DISDN, DEBUG_DEBUG, (channel>=0)?" -> reply = %d\n":" -> reply = (none)\n", channel);
/* if channel was accepted as given */
if (channel==-1 || call->b_channel==channel) {
call->b_exclusive = 1; // we are done
/* if channel was accepted, seize_bchannel shall simply return, because given channel is already set */
rc = seize_bchannel(call, call->b_channel, 1); // exclusively
if (rc < 0) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = replied channel not available\n");
return -47;
}
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = channel was accepted as given\n");
/* activate channel accepted by remote */
bchannel_event(call->isdn_ep, call->b_index, B_EVENT_USE);
return(0);
}
/* if channel value is faulty */
if (channel <= 0) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = illegal reply\n");
return -111; // protocol error
}
/* if channel was not accepted, try to get a different one */
rc = seize_bchannel(call, channel, 1); // exclusively
if (rc < 0) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = replied channel not available\n");
return -47;
}
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = replied channel accepted\n");
/* activate channel given by remote */
bchannel_event(call->isdn_ep, call->b_index, B_EVENT_USE);
} else
if (call->b_reserve) {
/*** we sent 'any channel acceptable' ***/
/* if not first reply, we are done */
if (call->state != ISDN_STATE_OUT_SETUP)
return(0);
PDEBUG(DISDN, DEBUG_DEBUG, "Channel Selection (reply from outgoing call)\n");
PDEBUG(DISDN, DEBUG_DEBUG, " -> request = any\n");
PDEBUG(DISDN, DEBUG_DEBUG, (channel>=0)?" -> reply = %d\n":" -> reply = (none)\n", channel);
/* if no channel was replied */
if (channel <= 0) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = no channel, protocol error\n");
return -111; // protocol error
}
/* we will see, if our received channel is available */
rc = seize_bchannel(call, channel, 1); // exclusively
if (rc < 0) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = replied channel not available\n");
return -47;
}
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = replied channel accepted\n");
/* activate channel given by remote */
bchannel_event(call->isdn_ep, call->b_index, B_EVENT_USE);
} else {
/*** we sent 'no channel available' ***/
/* if not the first reply, but a connect, we are forced */
if (cmd == MT_CONNECT && call->state != ISDN_STATE_OUT_SETUP) {
PDEBUG(DISDN, DEBUG_DEBUG, "Channel Selection (connect reply from outgoing call)\n");
PDEBUG(DISDN, DEBUG_DEBUG, " -> request = no channel\n");
PDEBUG(DISDN, DEBUG_DEBUG, (channel>=0)?" -> reply %d%s\n":" -> reply (none)\n", channel, exclusive?" (forced)":"");
if (channel > 0) {
goto use_from_connect;
}
rc = seize_bchannel(call, CHANNEL_ANY, 0); // any channel
if (rc < 0) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = no channel available during call-waiting\n");
return -34;
}
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = using channel %d\n", call->b_channel);
call->b_exclusive = 1; // we are done
/* activate channel given by remote */
bchannel_event(call->isdn_ep, call->b_index, B_EVENT_USE);
return(0);
}
/* if not first reply, we are done */
if (call->state != ISDN_STATE_OUT_SETUP)
return(0);
PDEBUG(DISDN, DEBUG_DEBUG, "Channel Selection (reply from outgoing call)\n");
PDEBUG(DISDN, DEBUG_DEBUG, " -> request = no channel\n");
PDEBUG(DISDN, DEBUG_DEBUG, (channel>=0)?" -> reply = %d\n":" -> reply = (none)\n", channel);
/* if first reply has no channel, we are done */
if (channel <= 0) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = no channel until connect\n");
return(0);
}
/* we will see, if our received channel is available */
use_from_connect:
rc = seize_bchannel(call, channel, exclusive);
if (rc < 0) {
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = replied channel not available\n");
return -47;
}
PDEBUG(DISDN, DEBUG_DEBUG, " -> result = replied channel accepted\n");
call->b_exclusive = 1; // we are done
/* activate channel given by remote */
bchannel_event(call->isdn_ep, call->b_index, B_EVENT_USE);
}
return(0);
}
/*
* ISDN instance
*/
/* create isdn interface instance */
isdn_t *isdn_create(void)
{
isdn_t *isdn_ep;
isdn_ep = calloc(1, sizeof(*isdn_ep));
if (!isdn_ep) {
PDEBUG(DISDN, DEBUG_ERROR, "No memory!\n");
abort();
}
if (pthread_mutex_init(&isdn_ep->upqueue_lock, NULL)) {
PDEBUG(DISDN, DEBUG_ERROR, "Cannot init mutex!\n");
abort();
}
PDEBUG(DISDN, DEBUG_DEBUG, "ISDN instance created\n");
return isdn_ep;
}
/* destroy isdn interface instance and free all resource */
void isdn_destroy(isdn_t *isdn_ep)
{
struct msn_list *msn;
/* remove stack instance */
isdn_close(isdn_ep);
/* close mISDN socket */
if (isdn_ep->socket > 0)
close(isdn_ep->socket);
/* destroy all calls */
while (isdn_ep->call_list)
call_destroy(isdn_ep->call_list);
/* free msn list */
while (isdn_ep->msn_list) {
msn = isdn_ep->msn_list;
isdn_ep->msn_list = msn->next;
free(msn);
}
pthread_mutex_destroy(&isdn_ep->upqueue_lock);
timer_exit(&isdn_ep->l2establish_timer);
free(isdn_ep);
PDEBUG(DISDN, DEBUG_DEBUG, "ISDN instance destroyed\n");
}
/* initialization and configuration to isdn interface instance */
int isdn_initialize(isdn_t *isdn_ep, 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 rc;
/* open mISDN socket */
rc = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
if (rc < 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Cannot open mISDN due to errno=%d:%s. (Does your Kernel support socket based mISDN? Protocol family is %d.)\n", errno, strerror(errno), PF_ISDN);
return -errno;
}
isdn_ep->socket = rc;
/* store settings */
isdn_ep->law = law;
isdn_ep->portname = strdup(portname);
isdn_ep->ntmode = ntmode;
isdn_ep->ptp = ptp;
isdn_ep->l1hold = layer1hold;
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;
/* channel selection list */
if (channel_out) {
rc = parse_out_channel(isdn_ep, channel_out);
if (rc < 0)
return rc;
} else
default_out_channel(isdn_ep);
if (channel_in) {
rc = parse_in_channel(isdn_ep, channel_in);
if (rc < 0)
return rc;
} else
default_in_channel(isdn_ep);
PDEBUG(DISDN, DEBUG_DEBUG, "ISDN instance initialized\n");
return 0;
}
/* add MSN number */
void isdn_add_msn(isdn_t *isdn_ep, const char *msn)
{
struct msn_list *m, **m_p;
m = calloc(1, sizeof(*m) + strlen(msn) + 1);
if (!m) {
PDEBUG(DISDN, DEBUG_ERROR, "No memory!\n");
abort();
}
m_p = &isdn_ep->msn_list;
while (*m_p)
m_p = &((*m_p)->next);
*m_p = m;
strcpy(m->msn, msn);
PDEBUG(DISDN, DEBUG_DEBUG, "added MSDN number '%s'\n", msn);
}
/*
* call instance
*/
call_t *call_create(isdn_t *isdn_ep, int channel, int exclusive, int mode)
{
call_t *call, **call_p;
int rc;
call = calloc(1, sizeof(*call));
if (!call) {
PDEBUG(DISDN, DEBUG_ERROR, "No memory!\n");
abort();
}
call_p = &isdn_ep->call_list;
while (*call_p)
call_p = &((*call_p)->next);
*call_p = call;
call->isdn_ep = isdn_ep;
call->b_index = -1;
call->b_channel = 0;
call->b_exclusive = 0;
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->conf = 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;
/* if any channel requested by constructor */
if (channel == CHANNEL_ANY) {
/* reserve channel */
call->b_reserve = 1;
isdn_ep->b_reserved++;
}
/* reserve channel */
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_destroy(call_t *call)
{
call_t **call_p;
/* free jitter buffer */
jitter_destroy(&call->dejitter);
/* remove B-channel relation */
drop_bchannel(call);
/* free session description */
if (call->cc_session)
osmo_cc_free_session(call->cc_session);
free((char *)call->sdp);
/* detach */
call_p = &call->isdn_ep->call_list;
while (*call_p) {
if (*call_p == call)
break;
call_p = &((*call_p)->next);
}
*call_p = call->next;
free(call);
PDEBUG(DISDN, DEBUG_DEBUG, "destroyed call instance\n");
}
/*
* bchannel handling
*/
/* send control information to the channel (dsp-module) */
static void ph_control(int sock, uint32_t c1, uint32_t c2)
{
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;
int rc;
if (sock < 0)
return;
if (c1 == DTMF_TONE_START && c2 == 0)
len = 4;
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();
}
}
/* create B-channel stack */
static int bchannel_create(isdn_t *isdn_ep, int index)
{
int channel = index + 1 + (index >= 15);
struct sockaddr_mISDN addr;
int flags;
int rc;
memset(&addr, 0, sizeof(addr));
if (isdn_ep->b_sock[index] > 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Socket already created for index %d\n", index);
return -EIO;
}
/* open socket */
if (isdn_ep->b_mode[index] == B_MODE_HDLC) {
PDEBUG(DISDN, DEBUG_NOTICE, "Use B-Channel with HDLC l!!!\n");
}
rc = socket(PF_ISDN, SOCK_DGRAM, (isdn_ep->b_mode[index] == B_MODE_HDLC) ? ISDN_P_B_L2DSPHDLC : ISDN_P_B_L2DSP);
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);
}
isdn_ep->b_sock[index] = rc;
/* set nonblocking io */
flags = fcntl(isdn_ep->b_sock[index], F_GETFL);
flags |= O_NONBLOCK;
fcntl(isdn_ep->b_sock[index], F_SETFL, flags);
/* bind socket to bchannel */
addr.family = AF_ISDN;
addr.dev = isdn_ep->portnum;
addr.channel = channel;
rc = bind(isdn_ep->b_sock[index], (struct sockaddr *)&addr, sizeof(addr));
if (rc < 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Failed to bind bchannel-socket for index %d with mISDN-DSP layer (errno=%d). Did you load mISDN_dsp.ko?\n", index, errno);
close(isdn_ep->b_sock[index]);
return -errno;
}
PDEBUG(DISDN, DEBUG_DEBUG, "created socket #%d for B-channel %d\n", isdn_ep->b_sock[index], channel);
return 0;
}
/* activate or deactivate B-channel */
static void bchannel_activate(isdn_t *isdn_ep, int index, int activate, int timeout)
{
struct mISDNhead act;
int channel = index + 1 + (index >= 15);
int rc;
if (isdn_ep->b_sock[index] <= 0)
return;
act.prim = (activate) ? PH_ACTIVATE_REQ : PH_DEACTIVATE_REQ;
act.id = 0;
rc = sendto(isdn_ep->b_sock[index], &act, MISDN_HEADER_LEN, 0, NULL, 0);
if (rc < 0)
PDEBUG(DISDN, DEBUG_ERROR, "Failed to send to socket #%d, of B-channel %d\n", isdn_ep->b_sock[index], channel);
PDEBUG(DISDN, DEBUG_DEBUG, "%s B-channel %d%s\n", (activate) ? "activating" : "deactivating", channel, (timeout) ? " after timeout recovery" : "");
/* reset rx buffer */
isdn_ep->b_buffer_pos[index] = 0;
}
/* configure B-channel */
static void bchannel_configure(isdn_t *isdn_ep, int index)
{
call_t *call;
int handle, mode;
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->conf && !call->mute) {
PDEBUG(DISDN, DEBUG_DEBUG, "PH_CONTROL: set DSP_CONF_JOIN\n");
ph_control(handle, DSP_CONF_JOIN, call->conf);
}
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);
}
}
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);
}
}
#if 0
/* set bridge ID for B-channel */
static void bchannel_conference(call_t *call, int oldconf, int newconf)
{
int *sock_p = &call->isdn_ep->b_sock[call->b_index];
int index, channel;
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 (index > -1 && call->isdn_ep->b_state[index] == B_STATE_ACTIVE)
ph_control(*sock_p, (newconf) ? DSP_CONF_JOIN : DSP_CONF_SPLIT, newconf);
} else
PDEBUG(DISDN, DEBUG_DEBUG, "we already have conf=%d at channel %d\n", newconf, channel);
}
#endif
/* destroy B-channel stack */
static void bchannel_destroy(isdn_t *isdn_ep, int index)
{
int channel = index + 1 + (index >= 15);
if (isdn_ep->b_sock[index] <= 0)
return;
PDEBUG(DISDN, DEBUG_DEBUG, "destroyed socket #%d for B-channel %d\n", isdn_ep->b_sock[index], channel);
close(isdn_ep->b_sock[index]);
isdn_ep->b_sock[index] = 0;
}
/*
* bchannel procedure
* ------------------
*
* A bchannel goes through the following states in this order:
*
* - B_STATE_IDLE
* No one is using the bchannel.
* It is available and not linked to call instance, nor reserved.
*
* - B_STATE_ACTIVATING
* The bchannel stack is created and an activation request is sent.
* It MAY be linked to a call, but already unlinked due to call release.
*
* - B_STATE_ACTIVE
* The bchannel is active and configured for the need of the call.
* Also it is linked to a call, otherwise it would be deactivated.
*
* - B_STATE_DEACTIVATING
* The bchannel is in deactivating state, due to deactivation request.
* It may be linked to a call, that likes to reactivate it.
*
* - B_STATE_IDLE
* See above.
* After deactivating bchannel, and if not used, the bchannel becomes idle again.
*
* A bchannel can have the following events:
*
* - B_EVENT_USE
* A bchannel is required by a call instance.
*
* - B_EVENT_ACTIVATED
* The bchannel beomes active.
*
* - B_EVENT_DROP
* The bchannel is not required by a call anymore
*
* - B_EVENT_DEACTIVATED
* The bchannel becomes inactive.
*
* All actions taken on these events depend on the current bchannel's state and
* whether it is linked to a call instance.
*
*/
/* process bchannel events */
void bchannel_event(isdn_t *isdn_ep, int index, int event)
{
call_t *call;
int state;
int channel;
int timer = -1; // no change
channel = index + 1 + (index >= 15);
call = isdn_ep->b_call[index];
state = isdn_ep->b_state[index];
PDEBUG(DISDN, DEBUG_DEBUG, "handling event %d at state %d for B-channel %d\n", event, state, channel);
switch(event) {
case B_EVENT_USE:
/* call must be linked in order to allow activation */
if (!call) {
PDEBUG(DISDN, DEBUG_ERROR, "bchannel must be linked to a call instance\n");
abort();
}
switch(state) {
case B_STATE_IDLE:
/* create stack and send activation request */
if (bchannel_create(isdn_ep, index) == 0) {
bchannel_activate(isdn_ep, index, 1, 0);
state = B_STATE_ACTIVATING;
timer = B_TIMER_ACTIVATING;
}
break;
case B_STATE_ACTIVATING:
/* do nothing, because it is already activating */
break;
default:
/* problems that might occur:
* B_EVENT_USE is received when channel already in use.
*/
PDEBUG(DISDN, DEBUG_ERROR, "Illegal event %d at state %d, please correct.\n", event, state);
}
break;
case B_EVENT_ACTIVATED:
timer = 0;
switch(state) {
case B_STATE_ACTIVATING:
if (call) {
/* bchannel is active and used by a call instance, so we configure bchannel */
bchannel_configure(isdn_ep, index);
state = B_STATE_ACTIVE;
//FIXME: init buffer state for delay
} else {
/* bchannel is active, but not used anymore (or has wrong stack config), so we deactivate */
bchannel_activate(isdn_ep, index, 0, 0);
state = B_STATE_DEACTIVATING;
timer = B_TIMER_DEACTIVATING;
}
break;
default:
PDEBUG(DISDN, DEBUG_ERROR, "Illegal event %d at state %d, please correct.\n", event, state);
}
break;
case B_EVENT_DROP:
if (!call) {
PDEBUG(DISDN, DEBUG_ERROR, "bchannel must be linked to a call instance\n");
abort();
}
switch(state) {
case B_STATE_IDLE:
/* bchannel is idle due to an error, so we do nothing */
break;
case B_STATE_ACTIVATING:
/* do nothing because we must wait until bchanenl is active before deactivating */
break;
case B_STATE_ACTIVE:
/* bchannel is active, so we deactivate */
bchannel_activate(isdn_ep, index, 0, 0);
state = B_STATE_DEACTIVATING;
timer = B_TIMER_DEACTIVATING;
break;
case B_STATE_DEACTIVATING:
/* we may have taken an already deactivating bchannel, but do not require it anymore, so we do nothing */
break;
default:
PDEBUG(DISDN, DEBUG_ERROR, "Illegal event %d at state %d, please correct.\n", event, state);
}
break;
case B_EVENT_DEACTIVATED:
timer = 0;
switch(state) {
case B_STATE_IDLE:
/* ignore due to deactivation confirm after unloading */
break;
case B_STATE_DEACTIVATING:
bchannel_destroy(isdn_ep, index);
state = B_STATE_IDLE;
if (call) {
/* bchannel is now deactivate, but is required by call instance, so we reactivate */
if (bchannel_create(isdn_ep, index) == 0) {
bchannel_activate(isdn_ep, index, 1, 0);
state = B_STATE_ACTIVATING;
timer = B_TIMER_ACTIVATING;
}
}
break;
default:
PDEBUG(DISDN, DEBUG_ERROR, "Illegal event %d at state %d, please correct.\n", event, state);
}
break;
case B_EVENT_TIMEOUT:
timer = 0;
switch(state) {
case B_STATE_IDLE:
/* ignore due to deactivation confirm after unloading */
break;
case B_STATE_ACTIVATING:
bchannel_activate(isdn_ep, index, 1, 1);
timer = B_TIMER_ACTIVATING;
break;
case B_STATE_DEACTIVATING:
bchannel_activate(isdn_ep, index, 0, 1);
timer = B_TIMER_DEACTIVATING;
break;
default:
PDEBUG(DISDN, DEBUG_ERROR, "Illegal event %d at state %d, please correct.\n", event, state);
}
break;
default:
PDEBUG(DISDN, DEBUG_ERROR, "Illegal event %d, please correct.\n", event);
}
isdn_ep->b_state[index] = state;
if (timer == 0)
timer_stop(&isdn_ep->b_timer[index]);
else if (timer > 0)
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);
/* handle frames from B-channel */
static int bchannel_work(isdn_t *isdn_ep, int index)
{
int channel = index + 1 + (index >= 15);
unsigned char buffer[2048+MISDN_HEADER_LEN];
struct mISDNhead *hh = (struct mISDNhead *)buffer;
int rc;
rc = recv(isdn_ep->b_sock[index], buffer, sizeof(buffer), 0);
if (rc < 0) {
if (errno == EAGAIN)
return 0;
PDEBUG(DISDN, DEBUG_ERROR, "read error B-channel data (socket #%d errno=%d:%s)\n", isdn_ep->b_sock[index], errno, strerror(errno));
return 0;
}
if (rc < (int)MISDN_HEADER_LEN) {
PDEBUG(DISDN, DEBUG_ERROR, "read short frame, got %d, expected %d\n", rc, (int)MISDN_HEADER_LEN);
return 0;
}
switch (hh->prim) {
/* we don't care about confirms, we use rx data to sync tx */
case PH_DATA_CNF:
break;
/* we receive audio data, we respond to it AND we send tones */
case PH_DATA_IND:
case DL_DATA_IND:
case PH_DATA_REQ:
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);
else
PDEBUG(DISDN, DEBUG_DEBUG, "b-channel is not associated to an ISDNPort (channel %d), ignoring.\n", channel);
break;
case PH_ACTIVATE_IND:
case DL_ESTABLISH_IND:
case PH_ACTIVATE_CNF:
case DL_ESTABLISH_CNF:
PDEBUG(DISDN, DEBUG_DEBUG, "DL_ESTABLISH confirm: bchannel is now activated (channel %d).\n", channel);
bchannel_event(isdn_ep, index, B_EVENT_ACTIVATED);
break;
case PH_DEACTIVATE_IND:
case DL_RELEASE_IND:
case PH_DEACTIVATE_CNF:
case DL_RELEASE_CNF:
PDEBUG(DISDN, DEBUG_DEBUG, "DL_RELEASE confirm: bchannel is now de-activated (channel %d).\n", channel);
bchannel_event(isdn_ep, index, B_EVENT_DEACTIVATED);
break;
default:
PDEBUG(DISDN, DEBUG_ERROR, "child message not handled: prim(0x%x) channel(%d) msg->len(%d)\n", hh->prim, channel, rc - (int)MISDN_HEADER_LEN);
}
return 1;
}
static void b_timer_timeout(struct timer *timer)
{
struct b_timer_inst *ti = timer->priv;
bchannel_event(ti->isdn_ep, ti->index, B_EVENT_TIMEOUT);
}
/*
* check for available channel and reserve+set it.
* give channel number or SEL_CHANNEL_ANY or SEL_CHANNEL_NO
* give exclusive flag
* returns -(cause value) or x = channel x or 0 = no channel
* NOTE: no activation is done here
*/
int seize_bchannel(call_t *call, int channel, int exclusive)
{
isdn_t *isdn_ep;
int index;
isdn_ep = call->isdn_ep;
/* the channel is what we already have */
if (call->b_channel == channel)
return channel;
/* if channel already in use, release it */
if (call->b_channel)
drop_bchannel(call);
/* if CHANNEL_NO */
if (channel == CHANNEL_NO || channel == 0)
return 0;
/* is channel out of range ? */
if (channel == 16
|| (channel > isdn_ep->b_num && channel < 16)
|| ((channel - 1) > isdn_ep->b_num && channel > 16)) /* channel-1 because channel 16 is not counted */
return -6; /* channel unacceptable */
/* request exclusive channel */
if (exclusive && channel > 0) {
index = channel - 1 - (channel > 16);
if (isdn_ep->b_call[index])
return -44; /* requested channel not available */
goto seize;
}
/* ask for channel */
if (channel > 0) {
index = channel - 1 - (channel > 16);
if (!isdn_ep->b_call[index])
goto seize;
}
/* search for channel */
for (index = 0; index < isdn_ep->b_num; index++) {
if (!isdn_ep->b_call[index]) {
channel = index + 1 + (index >= 15);
goto seize;
}
}
return -34; /* no free channel */
seize:
PDEBUG(DISDN, DEBUG_DEBUG, "seizing bchannel %d (index %d)\n", channel, index);
/* link Port, set parameters */
isdn_ep->b_call[index] = call;
isdn_ep->b_mode[index] = call->b_mode;
call->b_index = index;
call->b_channel = channel;
call->b_exclusive = exclusive;
/* reserve channel */
if (!call->b_reserve) {
call->b_reserve = 1;
isdn_ep->b_reserved++;
}
return channel;
}
/*
* drop reserved channel and unset it.
* deactivation is also done
*/
void drop_bchannel(call_t *call)
{
isdn_t *isdn_ep;
isdn_ep = call->isdn_ep;
/* unreserve channel */
if (call->b_reserve) {
isdn_ep->b_reserved--;
call->b_reserve = 0;
}
/* if not in use */
if (call->b_index < 0)
return;
if (!call->b_channel)
return;
PDEBUG(DISDN, DEBUG_DEBUG, "dropping bchannel %d (index %d)\n", call->b_channel, call->b_index);
if (isdn_ep->b_state[call->b_index] != B_STATE_IDLE)
bchannel_event(isdn_ep, call->b_index, B_EVENT_DROP);
isdn_ep->b_call[call->b_index] = NULL;
isdn_ep->b_mode[call->b_index] = 0;
call->b_index = -1;
call->b_channel = 0;
call->b_exclusive = 0;
}
static void send_to_rtp(call_t *call, unsigned char *data, int len)
{
call_t *other;
if (!call || !call->audio_path)
return;
if (call->conference_3pty) {
int16_t *audio;
int audio_len;
sample_t samples_local[len], samples_remote_active[len], samples_remote_hold[len], mix[len];
int i;
/* there should be no call on hold with audio coming from */
if (call->hold)
return;
/* convert local audio from interface to samples */
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_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);
/* search other party on hold */
other = call->isdn_ep->call_list;
while (other) {
if (other != call
&& other->l3_ces == call->l3_ces
&& other->hold && other->conference_3pty)
break;
other = other->next;
}
/* convert remote RTP to samples */
if (other)
jitter_load(&other->dejitter, samples_remote_hold, len);
else
memset(samples_remote_hold, 0, sizeof(sample_t) * 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);
if (call->isdn_ep->law == 'a')
g711_encode_alaw_flipped((uint8_t *)audio, 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;
memcpy(buf + MISDN_HEADER_LEN, data, len);
frm->prim = PH_DATA_REQ;
frm->id = 0;
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));
}
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);
if (call->isdn_ep->law == 'a')
g711_encode_alaw_flipped((uint8_t *)audio, audio_len, &data, &len);
else
g711_encode_ulaw_flipped((uint8_t *)audio, 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);
if (call->isdn_ep->law == 'a')
g711_encode_alaw_flipped((uint8_t *)audio, audio_len, &data, &len);
else
g711_encode_ulaw_flipped((uint8_t *)audio, audio_len, &data, &len);
osmo_cc_rtp_send(other->codec, data, len, 1, len);
free(data);
}
free(audio);
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)
{
uint8_t *buffer = isdn_ep->b_buffer[index];
int *buffer_pos = &(isdn_ep->b_buffer_pos[index]);
unsigned int cont = *((unsigned int *)data);
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)
return;
/* add to buffer */
while (len) {
buffer[(*buffer_pos)++] = *data++;
len--;
if (*buffer_pos == 160) {
*buffer_pos = 0;
send_to_rtp(isdn_ep->b_call[index], buffer, 160);
}
}
}
void isdn_rtp_work(isdn_t *isdn_ep)
{
call_t *call;
call = isdn_ep->call_list;
while (call) {
if (call->cc_session)
osmo_cc_session_handle(call->cc_session);
call = call->next;
}
}
/* 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)
{
call_t *call = codec->media->session->priv;
/* conference parties store their audio in jitter buffer */
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);
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;
memcpy(buf + MISDN_HEADER_LEN, data, len);
frm->prim = PH_DATA_REQ;
frm->id = 0;
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));
}
}
/*
* isdn stack handling
*/
int do_layer3(struct mlayer3 *ml3, unsigned int cmd, unsigned int pid, struct l3_msg *l3m)
{
isdn_t *isdn_ep = (isdn_t *)ml3->priv;
struct mbuffer *mb;
/* queue message, create, if required */
if (!l3m) {
l3m = alloc_l3_msg();
if (!l3m) {
PDEBUG(DISDN, DEBUG_ERROR, "No memory for layer 3 message\n");
abort();
}
}
mb = container_of(l3m, struct mbuffer, l3);
l3m->type = cmd;
l3m->pid = pid;
pthread_mutex_lock(&isdn_ep->upqueue_lock);
mqueue_tail(&isdn_ep->upqueue, mb);
pthread_mutex_unlock(&isdn_ep->upqueue_lock);
return 0;
}
int mISDN_getportbyname(int sock, int cnt, const char *portname)
{
struct mISDN_devinfo devinfo;
int port = 0, rc;
memset(&devinfo, 0, sizeof(devinfo));
/* resolve name */
while (port < cnt) {
devinfo.id = port;
rc = ioctl(sock, IMGETDEVINFO, &devinfo);
if (rc < 0)
return rc;
if (!strcasecmp(devinfo.name, portname))
break;
port++;
}
if (port == cnt)
return -EIO;
return port;
}
static void l2establish_timeout(struct timer *timer);
/* open mISDN port */
int isdn_open(isdn_t *isdn_ep)
{
const char *portname = isdn_ep->portname;
int portnum;
int ptp = isdn_ep->ptp;
int force_nt = isdn_ep->ntmode;
int l1hold = isdn_ep->l1hold;
int l2hold = isdn_ep->l2hold;
int i, cnt;
int pri, bri;
int nt, te;
struct mISDN_devinfo devinfo;
unsigned int protocol, prop;
int rc;
/* queue must be initializes, because l3-thread may send messages during open_layer3() */
mqueue_init(&isdn_ep->upqueue);
isdn_ep->upqueue_initialized = 1;
/* port number given ? */
for (i = 0; i < (int)strlen(portname); i++){
if (portname[i] < '0' || portname[i] > '9')
break;
}
if (i == (int)strlen(portname))
portnum = atoi(portname);
else
portnum = -1;
memset(&devinfo, 0, sizeof(devinfo));
/* check port counts */
rc = ioctl(isdn_ep->socket, IMGETCOUNT, &cnt);
if (rc < 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Cannot get number of mISDN devices. (ioctl IMGETCOUNT failed errno = %d)\n", errno);
goto error;
}
if (cnt <= 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Found no card. Please be sure to load card drivers.\n");
goto error;
}
if (portnum < 0) {
portnum = mISDN_getportbyname(isdn_ep->socket, cnt, portname);
if (portnum < 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Port name '%s' not found, use 'misdn_info' tool to list all existing ports.\n", portname);
goto error;
}
// note: 'portnum' has now the port number
}
if (portnum > cnt || portnum < 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Port (%d) given at 'ports' (options.conf) is out of existing port range (%d-%d)\n", portnum, 0, cnt);
goto error;
}
/* get port attributes */
pri = bri = nt = te = 0;
devinfo.id = portnum;
rc = ioctl(isdn_ep->socket, IMGETDEVINFO, &devinfo);
if (rc < 0) {
PDEBUG(DISDN, DEBUG_ERROR, "Cannot get device information for port %d. (ioctl IMGETDEVINFO failed errno=%d)\n", portnum, errno);
goto error;
}
if (devinfo.Dprotocols & (1 << ISDN_P_TE_S0)) {
bri = 1;
te = 1;
}
if (devinfo.Dprotocols & (1 << ISDN_P_NT_S0)) {
bri = 1;
nt = 1;
}
if (devinfo.Dprotocols & (1 << ISDN_P_TE_E1)) {
pri = 1;
te = 1;
}
if (devinfo.Dprotocols & (1 << ISDN_P_NT_E1)) {
pri = 1;
nt = 1;
}
if (force_nt && !nt) {
PDEBUG(DISDN, DEBUG_ERROR, "Port %d does not support NT-mode\n", portnum);
goto error;
}
if (bri && pri) {
PDEBUG(DISDN, DEBUG_ERROR, "Port %d supports BRI and PRI?? What kind of controller is that?. (Can't use this!)\n", portnum);
goto error;
}
if (!bri && !pri) {
PDEBUG(DISDN, DEBUG_ERROR, "Port %d does not support BRI nor PRI\n", portnum);
goto error;
}
if (!nt && !te) {
PDEBUG(DISDN, DEBUG_ERROR, "Port %d does not support NT-mode nor TE-mode!\n", portnum);
goto error;
}
/* set NT by turning off TE */
if (force_nt && nt)
te = 0;
/* if TE an NT is supported (and not forced to NT), turn off NT */
if (te && nt)
nt = 0;
/* check for continuous channelmap with no bchannel on slot 16 */
if (test_channelmap(0, devinfo.channelmap)) {
PDEBUG(DISDN, DEBUG_ERROR, "Port %d provides channel 0, but we cannot access it!\n", portnum);
goto error;
}
i = 1;
while(i < (int)devinfo.nrbchan + 1) {
if (i == 16) {
if (test_channelmap(i, devinfo.channelmap)) {
PDEBUG(DISDN, DEBUG_ERROR, "Port %d provides bchannel 16. Please upgrade mISDN, if this port is mISDN loopback interface.\n", portnum);
goto error;
}
} else {
if (!test_channelmap(i, devinfo.channelmap)) {
PDEBUG(DISDN, DEBUG_ERROR, "Port %d has no channel on slot %d!\n", portnum, i);
goto error;
}
}
i++;
}
timer_init(&isdn_ep->l2establish_timer, l2establish_timeout, isdn_ep);
isdn_ep->l1link = -1;
isdn_ep->l2link = -1;
/* if pri, must set PTP */
if (pri)
ptp = 1;
/* set l2hold */
switch (l2hold) {
case -1: // off
l2hold = 0;
break;
case 1: // on
l2hold = 1;
break;
default:
if (ptp)
l2hold = 1;
else
l2hold = 0;
break;
}
/* allocate resources of port */
protocol = (nt) ? L3_PROTOCOL_DSS1_NET : L3_PROTOCOL_DSS1_USER;
prop = (1 << MISDN_FLG_L2_CLEAN);
if (ptp) // ptp forced
prop |= (1 << MISDN_FLG_PTP);
if (nt) // supports hold/retrieve on nt-mode
prop |= (1 << MISDN_FLG_NET_HOLD);
if (l1hold) // supports layer 1 hold
prop |= (1 << MISDN_FLG_L1_HOLD);
if (l2hold) // supports layer 2 hold
prop |= (1 << MISDN_FLG_L2_HOLD);
/* open layer 3 */
isdn_ep->ml3 = open_layer3(portnum, protocol, prop, do_layer3, isdn_ep);
if (!isdn_ep->ml3) {
PDEBUG(DISDN, DEBUG_ERROR, "open_layer3() failed for port %d\n", portnum);
goto error;
}
isdn_ep->b_num = devinfo.nrbchan;
isdn_ep->portnum = portnum;
if (isdn_ep->portname)
free(isdn_ep->portname);
isdn_ep->portname = strdup(devinfo.name);
isdn_ep->ntmode = nt;
isdn_ep->pri = pri;
isdn_ep->ptp = ptp;
isdn_ep->l1hold = l1hold;
isdn_ep->l2hold = l2hold;
PDEBUG(DISDN, DEBUG_DEBUG, "Port has %d b-channels.\n", isdn_ep->b_num);
for (i = 0; i < isdn_ep->b_num; i++) {
isdn_ep->b_state[i] = B_STATE_IDLE;
timer_init(&isdn_ep->b_timer[i], b_timer_timeout, &isdn_ep->b_timer_inst);
isdn_ep->b_timer_inst[i].isdn_ep = isdn_ep;
isdn_ep->b_timer_inst[i].index = i;
}
/* if ptp, pull up the link */
if (isdn_ep->l2hold && (isdn_ep->ptp || !isdn_ep->ntmode)) {
PDEBUG(DISDN, DEBUG_DEBUG, "reqzest layer 2 to be establised for tei 0\n");
timer_start(&isdn_ep->l2establish_timer, 5.0); /* 5 seconds */
}
/* for nt-mode ptmp the link is always up */
if (isdn_ep->ntmode && !isdn_ep->ptp)
isdn_ep->l2link = 1;
PDEBUG(DISDN, DEBUG_DEBUG, "using 'mISDN_dsp.o' module\n");
return 0;
error:
isdn_close(isdn_ep);
return -EINVAL;
}
/* close mISDN port */
void isdn_close(isdn_t *isdn_ep)
{
int i;
struct select_channel *selchannel;
/* free bchannels */
for (i = 0; i < isdn_ep->b_num; i++) {
if (isdn_ep->b_sock[i] > 0) {
bchannel_destroy(isdn_ep, i);
PDEBUG(DISDN, DEBUG_DEBUG, "freeing %s port %s bchannel (index %d).\n", (isdn_ep->ntmode) ? "NT" : "TE", isdn_ep->portname, i);
}
timer_exit(&isdn_ep->b_timer[i]);
}
timer_exit(&isdn_ep->l2establish_timer);
/* close layer 3, if open */
if (isdn_ep->ml3) {
close_layer3(isdn_ep->ml3);
}
/* purge upqueue */
if (isdn_ep->upqueue_initialized)
mqueue_purge(&isdn_ep->upqueue);
if (isdn_ep->portname) {
free(isdn_ep->portname);
isdn_ep->portname = NULL;
}
while (isdn_ep->in_channel) {
selchannel = isdn_ep->in_channel;
isdn_ep->in_channel = selchannel->next;
free(selchannel);
}
while (isdn_ep->out_channel) {
selchannel = isdn_ep->out_channel;
isdn_ep->out_channel = selchannel->next;
free(selchannel);
}
}
/* receive message from ISDN protocol stack (out of queue) */
int isdn_dchannel_work(isdn_t *isdn_ep)
{
struct mbuffer *mb;
struct l3_msg *l3m;
int w = 0;
/* handle queued up-messages (d-channel) */
pthread_mutex_lock(&isdn_ep->upqueue_lock);
mb = mdequeue(&isdn_ep->upqueue);
pthread_mutex_unlock(&isdn_ep->upqueue_lock);
if (mb) {
w |= 1;
l3m = &mb->l3;
switch(l3m->type) {
case MPH_ACTIVATE_IND:
if (isdn_ep->l1link != 1) {
PDEBUG(DISDN, DEBUG_INFO, "layer 1 becomes active\n");
isdn_ep->l1link = 1;
}
break;
case MPH_DEACTIVATE_IND:
if (isdn_ep->l1link != 0) {
PDEBUG(DISDN, DEBUG_INFO, "layer 1 becomes inactive\n");
isdn_ep->l1link = 0;
}
break;
case MPH_INFORMATION_IND:
switch (l3m->pid) {
case L1_SIGNAL_LOS_ON:
PDEBUG(DISDN, DEBUG_DEBUG, "received LOS\n");
isdn_ep->los = 1;
break;
case L1_SIGNAL_LOS_OFF:
PDEBUG(DISDN, DEBUG_DEBUG, "LOS is gone\n");
isdn_ep->los = 0;
break;
case L1_SIGNAL_AIS_ON:
PDEBUG(DISDN, DEBUG_DEBUG, "received AIS\n");
isdn_ep->ais = 1;
break;
case L1_SIGNAL_AIS_OFF:
PDEBUG(DISDN, DEBUG_DEBUG, "AIS is gone\n");
isdn_ep->ais = 0;
break;
case L1_SIGNAL_RDI_ON:
PDEBUG(DISDN, DEBUG_DEBUG, "received RDI\n");
isdn_ep->rdi = 1;
break;
case L1_SIGNAL_RDI_OFF:
PDEBUG(DISDN, DEBUG_DEBUG, "RDI is gone\n");
isdn_ep->rdi = 0;
break;
case L1_SIGNAL_SLIP_TX:
isdn_ep->slip_tx++;
PDEBUG(DISDN, DEBUG_DEBUG, "received TX slip #%d\n", isdn_ep->slip_tx);
break;
case L1_SIGNAL_SLIP_RX:
isdn_ep->slip_rx++;
PDEBUG(DISDN, DEBUG_DEBUG, "received RX slip #%d\n", isdn_ep->slip_rx);
break;
}
break;
case MT_L2ESTABLISH:
PDEBUG(DISDN, DEBUG_INFO, "layer 2 becomes active (tei = %d)\n", l3m->pid);
isdn_ep->l2link = 1;
if (l3m->pid < 128)
isdn_ep->l2mask[l3m->pid >> 3] |= (1 << (l3m->pid & 7));
if ((!isdn_ep->ntmode || isdn_ep->ptp) && l3m->pid < 127) {
if (timer_running(&isdn_ep->l2establish_timer))
timer_stop(&isdn_ep->l2establish_timer);
}
break;
case MT_L2RELEASE:
if (l3m->pid < 128)
isdn_ep->l2mask[l3m->pid >> 3] &= ~(1 << (l3m->pid & 7));
if (!timer_running(&isdn_ep->l2establish_timer)) {
PDEBUG(DISDN, DEBUG_INFO, "layer 2 becomes inactive (tei = %d)\n", l3m->pid);
/* down if not nt-ptmp */
if (!isdn_ep->ntmode || isdn_ep->ptp)
isdn_ep->l2link = 0;
}
if ((!isdn_ep->ntmode || isdn_ep->ptp) && l3m->pid < 127) {
if (!timer_running(&isdn_ep->l2establish_timer) && isdn_ep->l2hold) {
timer_start(&isdn_ep->l2establish_timer, 5.0); /* 5 seconds */
isdn_ep->ml3->to_layer3(isdn_ep->ml3, MT_L2ESTABLISH, 0, NULL);
}
}
break;
default:
/* l3-data is sent to DSS1 handling */
dss1_receive(isdn_ep, l3m->type, l3m->pid, l3m);
}
/* free message */
free_l3_msg(l3m);
}
return w;
}
/* l2 establish timer fires */
static void l2establish_timeout(struct timer *timer)
{
isdn_t *isdn_ep = timer->priv;
if (isdn_ep->l2hold && (isdn_ep->ptp || !isdn_ep->ntmode)) {
PDEBUG(DISDN, DEBUG_DEBUG, "the L2 establish timer expired, we try to establish the link portnum=%d.\n", isdn_ep->portnum);
isdn_ep->ml3->to_layer3(isdn_ep->ml3, MT_L2ESTABLISH, 0, NULL);
timer_start(&isdn_ep->l2establish_timer, 5.0); /* 5 seconds */
}
}
void isdn_bchannel_work(isdn_t *isdn_ep)
{
int w, i;
for (i = 0; i < isdn_ep->b_num; i++) {
do {
if (isdn_ep->b_sock[i] > 0)
w = bchannel_work(isdn_ep, i);
else
w = 0;
} while (w);
}
}