2020-01-25 07:50:20 +00:00
/* 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>
2022-03-27 14:30:19 +00:00
# include "../libmisdn/socket.h"
2020-01-25 07:50:20 +00:00
# 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 */
2021-01-01 21:13:47 +00:00
static char * get_separated ( char * buffer )
2020-01-25 07:50:20 +00:00
{
while ( * buffer ) {
2021-01-01 21:13:47 +00:00
if ( * buffer = = ' , ' | | * buffer < = 32 ) { /* separate */
2020-01-25 07:50:20 +00:00
* 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 ;
2021-01-01 21:13:47 +00:00
p = get_separated ( p ) ;
2020-01-25 07:50:20 +00:00
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 ) {
2021-01-01 21:13:47 +00:00
PDEBUG ( DISDN , DEBUG_ERROR , " Error outgoing channel string: expecting a comma separated list of 'force', 'any', 'free', 'no' and any channel number. \n " ) ;
2020-01-25 07:50:20 +00:00
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 ;
2021-01-01 21:13:47 +00:00
p = get_separated ( p ) ;
2020-01-25 07:50:20 +00:00
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 ) {
2021-01-01 21:13:47 +00:00
PDEBUG ( DISDN , DEBUG_ERROR , " Error incoming channel list: expectng a comma separated list of channel numbers and 'free'. \n " ) ;
2020-01-25 07:50:20 +00:00
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 )
2021-01-01 21:13:47 +00:00
break ; /* all channel in use or reserved */
2020-01-25 07:50:20 +00:00
/* 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 )
2021-01-01 21:13:47 +00:00
break ; /* all channel in use or reserved */
2020-01-25 07:50:20 +00:00
/* 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 " ) ;
2021-01-01 21:13:47 +00:00
break ; /* all channel in use or reserved */
2020-01-25 07:50:20 +00:00
}
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
2022-05-01 08:10:35 +00:00
/* if channel was accepted, seize_bchannel shall simply return, because given channel is already set */
rc = seize_bchannel ( call , call - > b_channel , 1 ) ; // exclusively
2020-01-25 07:50:20 +00:00
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
}
2022-05-01 08:10:35 +00:00
/* if channel was not accepted, try to get a different one */
2020-01-25 07:50:20 +00:00
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 ;
2022-03-27 14:30:19 +00:00
/* destroy all calls */
while ( isdn_ep - > call_list )
call_destroy ( isdn_ep - > call_list ) ;
2020-01-25 07:50:20 +00:00
/* remove stack instance */
isdn_close ( isdn_ep ) ;
/* close mISDN socket */
2022-03-27 14:30:19 +00:00
if ( isdn_ep - > l2sock )
close ( isdn_ep - > l2sock ) ;
if ( isdn_ep - > l2inst )
mISDN_base_release ( isdn_ep - > l2inst ) ;
2020-01-25 07:50:20 +00:00
/* 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 */
2022-03-27 14:30:19 +00:00
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 )
2020-01-25 07:50:20 +00:00
{
int rc ;
2022-03-27 14:30:19 +00:00
void * mui ;
2020-01-25 07:50:20 +00:00
2022-03-27 14:30:19 +00:00
if ( ! ph_socket ) {
/* 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 - > l2sock = rc ;
} else {
isdn_ep - > ph_socket = ph_socket ;
/* open mISDN user space */
rc = mISDN_base_create ( & mui , ISDN_P_BASE ) ;
if ( rc < 0 ) {
PDEBUG ( DISDN , DEBUG_ERROR , " Cannot open mISDN due to errno=%d:%s. (Please fix!) \n " , errno , strerror ( errno ) ) ;
return - errno ;
}
isdn_ep - > l2inst = mui ;
portname = " 0 " ;
2020-01-25 07:50:20 +00:00
}
/* 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 ( ) ;
}
}
2021-01-01 21:13:47 +00:00
/* send control information, but with different length */
2020-01-25 07:50:20 +00:00
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 ) ) ;
2022-03-27 14:30:19 +00:00
if ( isdn_ep - > l2sock ) {
if ( isdn_ep - > b_sock [ index ] > 0 ) {
PDEBUG ( DISDN , DEBUG_ERROR , " Socket already created for index %d \n " , index ) ;
return - EIO ;
}
2020-01-25 07:50:20 +00:00
2022-03-27 14:30:19 +00:00
/* 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 ;
}
}
2020-01-25 07:50:20 +00:00
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 ;
2022-03-27 14:30:19 +00:00
if ( isdn_ep - > l2sock ) {
if ( isdn_ep - > b_sock [ index ] < = 0 )
return ;
2020-01-25 07:50:20 +00:00
2022-03-27 14:30:19 +00:00
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 ) ;
}
2020-01-25 07:50:20 +00:00
2022-03-27 14:30:19 +00:00
if ( isdn_ep - > l2inst ) {
uint8_t mode = PH_MODE_TRANS ;
if ( isdn_ep - > b_mode [ index ] = = B_MODE_HDLC ) {
PDEBUG ( DISDN , DEBUG_NOTICE , " Use B-Channel with HDLC!!! \n " ) ;
mode = PH_MODE_HDLC ;
}
ph_socket_tx_msg ( isdn_ep - > ph_socket , channel , ( activate ) ? PH_PRIM_ACT_REQ : PH_PRIM_DACT_REQ , & mode , 1 ) ;
}
2020-01-25 07:50:20 +00:00
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 ) ;
2022-03-27 14:30:19 +00:00
if ( isdn_ep - > l2sock ) {
if ( isdn_ep - > b_sock [ index ] < = 0 )
return ;
2020-01-25 07:50:20 +00:00
2022-03-27 14:30:19 +00:00
PDEBUG ( DISDN , DEBUG_DEBUG , " destroyed socket #%d for B-channel %d \n " , isdn_ep - > b_sock [ index ] , channel ) ;
2020-01-25 07:50:20 +00:00
2022-03-27 14:30:19 +00:00
close ( isdn_ep - > b_sock [ index ] ) ;
isdn_ep - > b_sock [ index ] = 0 ;
}
if ( isdn_ep - > l2inst ) {
PDEBUG ( DISDN , DEBUG_DEBUG , " destroyed B-channel %d \n " , channel ) ;
}
2020-01-25 07:50:20 +00:00
}
/*
* 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
2021-01-01 21:13:47 +00:00
* The bchannel is active and configured for the need of the call .
* Also it is linked to a call , otherwise it would be deactivated .
2020-01-25 07:50:20 +00:00
*
* - 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
2021-01-01 21:13:47 +00:00
* whether it is linked to a call instance .
2020-01-25 07:50:20 +00:00
*
*/
/* 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 :
2021-01-01 21:13:47 +00:00
/* problems that might occur:
2020-01-25 07:50:20 +00:00
* 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 ) {
2021-01-01 21:13:47 +00:00
/* bchannel is now deactivate, but is required by call instance, so we reactivate */
2020-01-25 07:50:20 +00:00
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 ) ;
2022-03-27 14:30:19 +00:00
/* handle frames from B-channel (kernel socket) */
static int bchannel_kernel_sock_work ( isdn_t * isdn_ep , int index )
2020-01-25 07:50:20 +00:00
{
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
2022-03-27 14:30:19 +00:00
PDEBUG ( DISDN , DEBUG_DEBUG , " b-channel is not associated to a call (channel %d), ignoring. \n " , channel ) ;
2020-01-25 07:50:20 +00:00
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 ;
}
2022-03-27 14:30:19 +00:00
/* handle data frames from B-channel (ph_socket) */
void bchannel_ph_sock_receive ( void * priv , int channel , uint8_t prim , uint8_t * data , int length )
{
isdn_t * isdn_ep = ( isdn_t * ) priv ;
int index = channel - 1 - ( channel > 16 ) ;
if ( index < 0 | | index > 127 )
return ;
switch ( prim ) {
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 ) ;
} else
PDEBUG ( DISDN , DEBUG_DEBUG , " b-channel is not associated to a call (channel %d), ignoring. \n " , channel ) ;
break ;
case PH_PRIM_ACT_IND :
PDEBUG ( DISDN , DEBUG_DEBUG , " PH_PRIM_ACT_IND: bchannel is now activated (channel %d). \n " , channel ) ;
bchannel_event ( isdn_ep , index , B_EVENT_ACTIVATED ) ;
break ;
case PH_PRIM_DACT_IND :
PDEBUG ( DISDN , DEBUG_DEBUG , " PH_PRIM_DACT_IND: bchannel is now deactivated (channel %d). \n " , channel ) ;
bchannel_event ( isdn_ep , index , B_EVENT_DEACTIVATED ) ;
break ;
}
}
2020-01-25 07:50:20 +00:00
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
2021-01-01 21:13:47 +00:00
* give exclusive flag
2020-01-25 07:50:20 +00:00
* 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 ;
2022-03-27 14:30:19 +00:00
int rc = 0 ;
2020-01-25 07:50:20 +00:00
memcpy ( buf + MISDN_HEADER_LEN , data , len ) ;
frm - > prim = PH_DATA_REQ ;
frm - > id = 0 ;
2022-03-27 14:30:19 +00:00
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 ) ;
2020-01-25 07:50:20 +00:00
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 ;
2022-03-27 14:30:19 +00:00
int rc = 0 ;
2020-01-25 07:50:20 +00:00
memcpy ( buf + MISDN_HEADER_LEN , data , len ) ;
frm - > prim = PH_DATA_REQ ;
frm - > id = 0 ;
2022-03-27 14:30:19 +00:00
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 ) ;
2020-01-25 07:50:20 +00:00
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 ;
}
2022-03-27 14:30:19 +00:00
int mISDN_getportbyname ( isdn_t * isdn_ep , int cnt , const char * portname )
2020-01-25 07:50:20 +00:00
{
struct mISDN_devinfo devinfo ;
int port = 0 , rc ;
memset ( & devinfo , 0 , sizeof ( devinfo ) ) ;
/* resolve name */
while ( port < cnt ) {
devinfo . id = port ;
2022-03-27 14:30:19 +00:00
if ( isdn_ep - > l2sock )
rc = ioctl ( isdn_ep - > l2sock , IMGETDEVINFO , & devinfo ) ;
if ( isdn_ep - > l2inst )
rc = mISDN_base_ioctl ( isdn_ep - > l2inst , IMGETDEVINFO , & devinfo ) ;
2020-01-25 07:50:20 +00:00
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 ;
2022-03-27 14:30:19 +00:00
int rc = 0 ;
2020-01-25 07:50:20 +00:00
/* 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 */
2022-03-27 14:30:19 +00:00
if ( isdn_ep - > l2sock )
rc = ioctl ( isdn_ep - > l2sock , IMGETCOUNT , & cnt ) ;
if ( isdn_ep - > l2inst )
rc = mISDN_base_ioctl ( isdn_ep - > l2inst , IMGETCOUNT , & cnt ) ;
2020-01-25 07:50:20 +00:00
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 ) {
2022-03-27 14:30:19 +00:00
portnum = mISDN_getportbyname ( isdn_ep , cnt , portname ) ;
2020-01-25 07:50:20 +00:00
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 ;
2022-03-27 14:30:19 +00:00
if ( isdn_ep - > l2sock )
rc = ioctl ( isdn_ep - > l2sock , IMGETDEVINFO , & devinfo ) ;
if ( isdn_ep - > l2inst )
rc = mISDN_base_ioctl ( isdn_ep - > l2inst , IMGETDEVINFO , & devinfo ) ;
2020-01-25 07:50:20 +00:00
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 ;
2021-01-01 21:13:47 +00:00
/* check for continuous channelmap with no bchannel on slot 16 */
2020-01-25 07:50:20 +00:00
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 ) ) {
2021-01-01 21:13:47 +00:00
PDEBUG ( DISDN , DEBUG_ERROR , " Port %d provides bchannel 16. Please upgrade mISDN, if this port is mISDN loopback interface. \n " , portnum ) ;
2020-01-25 07:50:20 +00:00
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 ;
}
2021-01-01 21:13:47 +00:00
/* allocate resources of port */
2020-01-25 07:50:20 +00:00
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 ;
2022-03-27 14:30:19 +00:00
if ( ! isdn_ep - > b_num )
isdn_ep - > b_num = ( pri ) ? 30 : 2 ;
2020-01-25 07:50:20 +00:00
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 ;
2022-03-27 14:30:19 +00:00
if ( isdn_ep - > l2sock )
PDEBUG ( DISDN , DEBUG_DEBUG , " using 'mISDN_dsp.o' module \n " ) ;
2020-01-25 07:50:20 +00:00
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 + + ;
2021-01-01 21:13:47 +00:00
PDEBUG ( DISDN , DEBUG_DEBUG , " received TX slip #%d \n " , isdn_ep - > slip_tx ) ;
2020-01-25 07:50:20 +00:00
break ;
case L1_SIGNAL_SLIP_RX :
isdn_ep - > slip_rx + + ;
2021-01-01 21:13:47 +00:00
PDEBUG ( DISDN , DEBUG_DEBUG , " received RX slip #%d \n " , isdn_ep - > slip_rx ) ;
2020-01-25 07:50:20 +00:00
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 )
2022-03-27 14:30:19 +00:00
w = bchannel_kernel_sock_work ( isdn_ep , i ) ;
2020-01-25 07:50:20 +00:00
else
w = 0 ;
} while ( w ) ;
}
}