2017-06-10 13:30:20 +00:00
/* Radiocom 2000 protocol handling
*
* ( C ) 2017 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/>.
*/
# define CHAN r2000->sender.kanal
# include <stdio.h>
# include <stdint.h>
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# include <time.h>
2017-11-18 07:06:06 +00:00
# include "../libsample/sample.h"
2017-11-18 07:58:57 +00:00
# include "../libdebug/debug.h"
2017-11-17 08:01:44 +00:00
# include "../libmncc/cause.h"
2017-06-10 13:30:20 +00:00
# include "r2000.h"
//#include "transaction.h"
# include "frame.h"
# include "dsp.h"
# define CUT_OFF_EMPHASIS_R2000 300 //FIXME: use real cut-off / time constant
2017-11-04 16:08:57 +00:00
# define PAGE_TRIES 3 /* how many times trying to page */
2017-06-10 13:30:20 +00:00
# define IDENT_TIME 3.0 /* time to wait for identity response */
# define ALERT_TIME 60.0 /* time to wait for party to answer */
# define DIAL1_TIME 1.0 /* time to wait for party to dial digits 1..10 */
# define DIAL2_TIME 0.5 /* time to wait for party to dial digits 11..20 */
# define SUSPEND_TIME 1.0 /* time to wait for suspend response */
# define SUPER_TIME1 4.0 /* time to release if not receiving initial supervisory signal */
# define SUPER_TIME2 20.0 /* time to release after loosing supervisory signal */
# define RELEASE_TIME 2.0 /* time to wait for release response */
/* Call reference for calls from station mobile to network
This offset of 0x400000000 is required for MNCC interface . */
static int new_callref = 0x40000000 ;
2019-07-15 19:14:52 +00:00
/* definition of bands and channels */
2017-06-10 13:30:20 +00:00
# define CHANNEL_SPACING 0.0125
static struct r2000_bands {
int number ;
const char * name ;
double dl_f0 ; /* first downlink channel (0) */
int channels ; /* number of channels (including 0) */
double duplex ; /* duplex distance (uplink below downlink) */
} r2000_bands [ ] = {
{ 1 , " UHF " , 424.8000 , 256 , 10.0 } ,
{ 3 , " VHF A/B " , 169.8000 , 296 , 4.6 } ,
{ 4 , " VHF 5/6/1 " , 176.5000 , 176 , - 8.0 } ,
{ 5 , " VHF 5/6/2 " , 178.7000 , 192 , - 8.0 } ,
{ 6 , " VHF 5/6/3 " , 181.1000 , 192 , - 8.0 } ,
{ 7 , " VHF 7/8/1 " , 200.5000 , 176 , 8.0 } ,
{ 8 , " VHF 7/8/2 " , 202.7000 , 192 , 8.0 } ,
{ 9 , " VHF 7/8/3 " , 205.1000 , 192 , 8.0 } ,
{ 10 , " VHF 9/10/1 " , 208.5000 , 176 , - 8.0 } ,
{ 11 , " VHF 9/10/2 " , 210.7000 , 192 , - 8.0 } ,
{ 12 , " VHF 9/10/3 " , 213.1000 , 192 , - 8.0 } ,
{ 0 , NULL , 0.0 , 0 , 0.0 }
} ;
void r2000_band_list ( void )
{
int i ;
printf ( " Bande \t Name \t \t Channels \t Downlink \t \t Uplink \n " ) ;
printf ( " -------------------------------------------------------------------------- \n " ) ;
for ( i = 0 ; r2000_bands [ i ] . name ; i + + ) {
printf ( " %d \t %s%s \t 0 .. %d \t %.4f..%.4f MHz \t %5.1f MHz \n " ,
r2000_bands [ i ] . number ,
r2000_bands [ i ] . name ,
( strlen ( r2000_bands [ i ] . name ) > = 8 ) ? " " : " \t " ,
r2000_bands [ i ] . channels - 1 ,
r2000_bands [ i ] . dl_f0 ,
r2000_bands [ i ] . dl_f0 + CHANNEL_SPACING * ( double ) ( r2000_bands [ i ] . channels - 1 ) ,
- r2000_bands [ i ] . duplex ) ;
}
}
/* Convert band+channel number to frequency number of base station.
Set ' uplink ' to 1 to get frequency of station mobile . */
double r2000_channel2freq ( int band , int channel , int uplink )
{
int i ;
double freq ;
for ( i = 0 ; r2000_bands [ i ] . name ; i + + ) {
if ( r2000_bands [ i ] . number = = band )
break ;
}
if ( ! r2000_bands [ i ] . name ) {
PDEBUG ( DR2000 , DEBUG_NOTICE , " Given band number is invalid! (use '-B list' for valid bands) \n " ) ;
return 0.0 ;
}
if ( channel < 0 | | channel > r2000_bands [ i ] . channels - 1 ) {
PDEBUG ( DR2000 , DEBUG_NOTICE , " Given channel number %d invalid! (use '-B list' for valid channels) \n " , channel ) ;
return 0.0 ;
}
2017-10-12 17:43:15 +00:00
if ( uplink = = 2 )
return - r2000_bands [ i ] . duplex * 1e6 ;
2017-06-10 13:30:20 +00:00
freq = r2000_bands [ i ] . dl_f0 + CHANNEL_SPACING * ( double ) ( channel ) ;
if ( uplink )
freq - = r2000_bands [ i ] . duplex ;
return freq * 1e6 ;
}
const char * r2000_state_name ( enum r2000_state state )
{
static char invalid [ 16 ] ;
switch ( state ) {
case STATE_NULL :
return " (NULL) " ;
case STATE_IDLE :
return " IDLE " ;
case STATE_INSCRIPTION :
return " INSCRIPTION " ;
case STATE_OUT_ASSIGN :
return " OUT ASSIGN " ;
case STATE_IN_ASSIGN :
return " IN ASSIGN " ;
case STATE_RECALL_ASSIGN :
return " RECALL ASSIGN " ;
case STATE_OUT_IDENT :
return " OUT IDENT " ;
case STATE_IN_IDENT :
return " IN IDENT " ;
case STATE_RECALL_IDENT :
return " RECALL IDENT " ;
case STATE_OUT_DIAL1 :
return " OUT DIAL1 " ;
case STATE_OUT_DIAL2 :
return " OUT DIAL2 " ;
case STATE_SUSPEND :
return " SUSPEND " ;
case STATE_RECALL_WAIT :
return " RECALL WAIT " ;
case STATE_IN_ALERT :
return " IN ALERT " ;
case STATE_OUT_ALERT :
return " OUT ALERT " ;
case STATE_RECALL_ALERT :
return " RECALL ALERT " ;
case STATE_ACTIVE :
return " ACTIVE " ;
case STATE_RELEASE_CC :
return " RELEASE CC " ;
case STATE_RELEASE_TC :
return " RELEASE TC " ;
}
sprintf ( invalid , " invalid(%d) " , state ) ;
return invalid ;
}
static const char * print_subscriber_subscr ( r2000_subscriber_t * subscr ) ;
void r2000_display_status ( void )
{
sender_t * sender ;
r2000_t * r2000 ;
display_status_start ( ) ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
r2000 = ( r2000_t * ) sender ;
display_status_channel ( r2000 - > sender . kanal , chan_type_short_name ( r2000 - > sysinfo . chan_type ) , r2000_state_name ( r2000 - > state ) ) ;
if ( r2000 - > state ! = STATE_IDLE ) {
char result [ 32 ] ;
sprintf ( result , " %s " , print_subscriber_subscr ( & r2000 - > subscriber ) ) ;
display_status_subscriber ( result , NULL ) ;
}
}
display_status_end ( ) ;
}
static struct r2000_channels {
enum r2000_chan_type chan_type ;
const char * short_name ;
const char * long_name ;
} r2000_channels [ ] = {
{ CHAN_TYPE_CC , " CC " , " control channel " } ,
{ CHAN_TYPE_TC , " TC " , " taffic channel " } ,
2017-09-03 17:13:21 +00:00
{ CHAN_TYPE_CC_TC , " CC/TC " , " combined control & taffic channel " } ,
2017-06-10 13:30:20 +00:00
{ 0 , NULL , NULL }
} ;
void r2000_channel_list ( void )
{
int i ;
printf ( " Type \t \t Description \n " ) ;
printf ( " ------------------------------------------------------------------------ \n " ) ;
for ( i = 0 ; r2000_channels [ i ] . long_name ; i + + )
printf ( " %s%s \t %s \n " , r2000_channels [ i ] . short_name , ( strlen ( r2000_channels [ i ] . short_name ) > = 8 ) ? " " : " \t " , r2000_channels [ i ] . long_name ) ;
}
int r2000_channel_by_short_name ( const char * short_name )
{
int i ;
for ( i = 0 ; r2000_channels [ i ] . short_name ; i + + ) {
if ( ! strcasecmp ( r2000_channels [ i ] . short_name , short_name ) ) {
PDEBUG ( DR2000 , DEBUG_INFO , " Selecting channel '%s' = %s \n " , r2000_channels [ i ] . short_name , r2000_channels [ i ] . long_name ) ;
return r2000_channels [ i ] . chan_type ;
}
}
return - 1 ;
}
const char * chan_type_short_name ( enum r2000_chan_type chan_type )
{
int i ;
for ( i = 0 ; r2000_channels [ i ] . short_name ; i + + ) {
if ( r2000_channels [ i ] . chan_type = = chan_type )
return r2000_channels [ i ] . short_name ;
}
return " invalid " ;
}
const char * chan_type_long_name ( enum r2000_chan_type chan_type )
{
int i ;
for ( i = 0 ; r2000_channels [ i ] . long_name ; i + + ) {
if ( r2000_channels [ i ] . chan_type = = chan_type )
return r2000_channels [ i ] . long_name ;
}
return " invalid " ;
}
static void r2000_new_state ( r2000_t * r2000 , enum r2000_state new_state )
{
if ( r2000 - > state = = new_state )
return ;
PDEBUG_CHAN ( DR2000 , DEBUG_DEBUG , " State change: %s -> %s \n " , r2000_state_name ( r2000 - > state ) , r2000_state_name ( new_state ) ) ;
r2000 - > state = new_state ;
r2000_display_status ( ) ;
r2000 - > tx_frame_count = 0 ;
}
/* used to print station mobile data */
static const char * print_subscriber_frame ( frame_t * frame )
{
static char result [ 32 ] ;
sprintf ( result , " %d,%03d,%05d " , frame - > sm_type , frame - > sm_relais , frame - > sm_mor ) ;
return result ;
}
static const char * print_subscriber_subscr ( r2000_subscriber_t * subscr )
{
static char result [ 32 ] ;
sprintf ( result , " %d,%03d,%05d " , subscr - > type , subscr - > relais , subscr - > mor ) ;
return result ;
}
/* convert station mobile id to 9 digits caller data */
static const char * subscriber2string ( r2000_subscriber_t * subscr )
{
static char result [ 32 ] ;
sprintf ( result , " %d%03d%05d " , subscr - > type , subscr - > relais , subscr - > mor ) ;
return result ;
}
/* convert 9-digits dial string to station mobile data */
static int string2subscriber ( const char * dialstring , r2000_subscriber_t * subscr )
{
char check [ 6 ] ;
int type , relais , mor ;
int i ;
if ( strlen ( dialstring ) ! = 9 ) {
PDEBUG ( DR2000 , DEBUG_NOTICE , " Wrong number of digits, use 9 digits: TRRRXXXXX (T=type, R=relais, X=mobile number) \n " ) ;
return - 1 ;
}
for ( i = 0 ; i < ( int ) strlen ( dialstring ) ; i + + ) {
if ( dialstring [ i ] < ' 0 ' | | dialstring [ i ] > ' 9 ' ) {
PDEBUG ( DR2000 , DEBUG_NOTICE , " Invalid digit in dial string, use only 0..9. \n " ) ;
return - 1 ;
}
}
memcpy ( check , dialstring , 1 ) ;
check [ 1 ] = ' \0 ' ;
type = atoi ( check ) ;
if ( type < 1 | | type > 511 ) {
PDEBUG ( DR2000 , DEBUG_NOTICE , " Invalid station type in dial string, use 0..7 as station mobile type. \n " ) ;
return - 1 ;
}
memcpy ( check , dialstring + 1 , 3 ) ;
check [ 3 ] = ' \0 ' ;
relais = atoi ( check ) ;
if ( relais < 1 | | relais > 511 ) {
PDEBUG ( DR2000 , DEBUG_NOTICE , " Invalid relais number in dial string, use 000..511 as relais number. \n " ) ;
return - 1 ;
}
memcpy ( check , dialstring + 4 , 5 ) ;
check [ 5 ] = ' \0 ' ;
mor = atoi ( check ) ;
if ( mor > 65535 ) {
PDEBUG ( DR2000 , DEBUG_NOTICE , " Invalid mobile number in dial string, use 00000..65535 as mobile number. \n " ) ;
return - 1 ;
}
subscr - > type = type ;
subscr - > relais = relais ;
subscr - > mor = mor ;
return 0 ;
}
static int match_voie ( r2000_t * r2000 , frame_t * frame , uint8_t voie )
{
if ( frame - > voie = = 0 & & voie = = 1 ) {
PDEBUG_CHAN ( DR2000 , DEBUG_NOTICE , " Frame for control channel, but expecting traffic channel, ignoring. (maybe radio noise) \n " ) ;
return 0 ;
}
if ( frame - > voie = = 1 & & voie = = 0 ) {
PDEBUG_CHAN ( DR2000 , DEBUG_NOTICE , " Frame for traffic channel, but expecting control channel, ignoring. (maybe radio noise) \n " ) ;
return 0 ;
}
return 1 ;
}
static int match_channel ( r2000_t * r2000 , frame_t * frame )
{
2019-07-20 16:11:17 +00:00
if ( frame - > channel ! = atoi ( r2000 - > sender . kanal ) ) {
2017-06-10 13:30:20 +00:00
PDEBUG_CHAN ( DR2000 , DEBUG_NOTICE , " Frame for different channel %d received, ignoring. \n " , frame - > channel ) ;
return 0 ;
}
return 1 ;
}
static int match_relais ( r2000_t * r2000 , frame_t * frame )
{
if ( frame - > relais ! = r2000 - > sysinfo . relais ) {
PDEBUG_CHAN ( DR2000 , DEBUG_NOTICE , " Frame for different relais %d received, ignoring. \n " , frame - > relais ) ;
return 0 ;
}
return 1 ;
}
static int match_subscriber ( r2000_t * r2000 , frame_t * frame )
{
2017-08-10 15:43:36 +00:00
/* ignore dialing messages, because subscriber info is not used in there */
if ( frame - > message = = 19 | | frame - > message = = 20 )
return 1 ;
2017-06-10 13:30:20 +00:00
if ( r2000 - > subscriber . relais ! = frame - > sm_relais
| | r2000 - > subscriber . mor ! = frame - > sm_mor ) {
PDEBUG_CHAN ( DR2000 , DEBUG_NOTICE , " Frame for different subscriber '%s' received, ignoring. \n " , print_subscriber_frame ( frame ) ) ;
return 0 ;
}
return 1 ;
}
/* convert nconv to supervisory digit to be transmitted to phone */
uint8_t r2000_encode_super ( r2000_t * r2000 )
{
uint8_t super , nconv , relais ;
nconv = r2000 - > sysinfo . nconv ;
relais = r2000 - > sysinfo . relais & 0xf ;
/* LSB first */
super = ( ( nconv < < 2 ) & 0x04 )
| ( nconv & 0x02 )
| ( ( nconv > > 2 ) & 0x01 )
| ( ( relais < < 6 ) & 0x40 )
| ( ( relais < < 4 ) & 0x20 )
| ( ( relais < < 2 ) & 0x10 )
| ( relais & 0x08 ) ;
PDEBUG_CHAN ( DDSP , DEBUG_INFO , " TX Supervisory: NCONV: %d relais (4 lowest bits): %d \n " , nconv , relais ) ;
return super ^ 0x7f ;
}
static void r2000_timeout ( struct timer * timer ) ;
/* Create transceiver instance and link to a list. */
2020-06-14 18:59:41 +00:00
int r2000_create ( int band , const char * kanal , enum r2000_chan_type chan_type , const char * audiodev , int use_sdr , int samplerate , double rx_gain , double tx_gain , int pre_emphasis , int de_emphasis , const char * write_rx_wave , const char * write_tx_wave , const char * read_rx_wave , const char * read_tx_wave , uint16_t relais , uint8_t deport , uint8_t agi , uint8_t sm_power , uint8_t taxe , uint8_t crins , int destruction , uint8_t nconv , int recall , int loopback )
2017-06-10 13:30:20 +00:00
{
sender_t * sender ;
r2000_t * r2000 = NULL ;
int rc ;
/* check channel matching and set deviation factor */
2019-07-20 16:11:17 +00:00
if ( r2000_channel2freq ( band , atoi ( kanal ) , 0 ) = = 0.0 )
2017-06-10 13:30:20 +00:00
return - EINVAL ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
r2000 = ( r2000_t * ) sender ;
if ( ( r2000 - > sysinfo . chan_type = = CHAN_TYPE_CC | | r2000 - > sysinfo . chan_type = = CHAN_TYPE_CC_TC )
& & ( chan_type = = CHAN_TYPE_CC | | chan_type = = CHAN_TYPE_CC_TC ) ) {
2017-11-04 16:08:57 +00:00
PDEBUG ( DCNETZ , DEBUG_NOTICE , " More than one control channel is not supported, please define other channels as traffic channels! \n " ) ;
2017-06-10 13:30:20 +00:00
return - EINVAL ;
}
}
r2000 = calloc ( 1 , sizeof ( r2000_t ) ) ;
if ( ! r2000 ) {
PDEBUG ( DR2000 , DEBUG_ERROR , " No memory! \n " ) ;
return - ENOMEM ;
}
2019-07-20 16:11:17 +00:00
PDEBUG ( DR2000 , DEBUG_DEBUG , " Creating 'Radiocom 2000' instance for channel = %s (sample rate %d). \n " , kanal , samplerate ) ;
2017-06-10 13:30:20 +00:00
/* init general part of transceiver */
2020-06-14 18:59:41 +00:00
rc = sender_create ( & r2000 - > sender , kanal , r2000_channel2freq ( band , atoi ( kanal ) , 0 ) , r2000_channel2freq ( band , atoi ( kanal ) , 1 ) , audiodev , use_sdr , samplerate , rx_gain , tx_gain , 0 , 0 , write_rx_wave , write_tx_wave , read_rx_wave , read_tx_wave , loopback , PAGING_SIGNAL_NONE ) ;
2017-06-10 13:30:20 +00:00
if ( rc < 0 ) {
PDEBUG ( DR2000 , DEBUG_ERROR , " Failed to init transceiver process! \n " ) ;
goto error ;
}
timer_init ( & r2000 - > timer , r2000_timeout , r2000 ) ;
r2000 - > sysinfo . relais = relais ;
r2000 - > sysinfo . chan_type = chan_type ;
r2000 - > sysinfo . deport = deport ;
r2000 - > sysinfo . agi = agi ;
r2000 - > sysinfo . sm_power = sm_power ;
r2000 - > sysinfo . taxe = taxe ;
r2000 - > sysinfo . crins = crins ;
r2000 - > sysinfo . nconv = nconv ;
r2000 - > sysinfo . recall = recall ;
if ( crins = = 3 & & destruction ! = 2342 ) {
PDEBUG ( DR2000 , DEBUG_ERROR , " Crins is 3, but destruction is not confirmed, please fix! \n " ) ;
abort ( ) ;
}
r2000 - > compandor = 1 ;
r2000 - > pre_emphasis = pre_emphasis ;
r2000 - > de_emphasis = de_emphasis ;
2018-01-20 14:49:19 +00:00
/* we don't know anything about frequency response, so we use NMT defaults */
rc = init_emphasis ( & r2000 - > estate , samplerate , CUT_OFF_EMPHASIS_R2000 , CUT_OFF_HIGHPASS_DEFAULT , CUT_OFF_LOWPASS_DEFAULT ) ;
2017-06-10 13:30:20 +00:00
if ( rc < 0 )
goto error ;
/* init audio processing */
rc = dsp_init_sender ( r2000 ) ;
if ( rc < 0 ) {
PDEBUG ( DR2000 , DEBUG_ERROR , " Failed to init audio processing! \n " ) ;
goto error ;
}
/* go into idle state */
r2000_go_idle ( r2000 ) ;
2019-07-20 16:11:17 +00:00
PDEBUG ( DR2000 , DEBUG_NOTICE , " Created channel #%s of type '%s' = %s \n " , kanal , chan_type_short_name ( chan_type ) , chan_type_long_name ( chan_type ) ) ;
2017-06-10 13:30:20 +00:00
return 0 ;
error :
r2000_destroy ( & r2000 - > sender ) ;
return rc ;
}
void r2000_check_channels ( void )
{
sender_t * sender ;
r2000_t * r2000 ;
int cc = 0 , tc = 0 , combined = 0 ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
r2000 = ( r2000_t * ) sender ;
if ( r2000 - > sysinfo . chan_type = = CHAN_TYPE_CC )
cc = 1 ;
if ( r2000 - > sysinfo . chan_type = = CHAN_TYPE_TC )
tc = 1 ;
if ( r2000 - > sysinfo . chan_type = = CHAN_TYPE_CC_TC ) {
cc = 1 ;
tc = 1 ;
combined = 1 ;
}
}
if ( cc & & ! tc ) {
2017-10-02 10:31:05 +00:00
PDEBUG ( DR2000 , DEBUG_NOTICE , " *** Selected channel(s) can be used for control only. \n " ) ;
2017-11-04 16:08:57 +00:00
PDEBUG ( DR2000 , DEBUG_NOTICE , " *** No call is possible at all! \n " ) ;
2017-10-02 10:31:05 +00:00
PDEBUG ( DR2000 , DEBUG_NOTICE , " *** Use combined 'CC/TC' instead! \n " ) ;
2017-06-10 13:30:20 +00:00
}
if ( tc & & ! cc ) {
2017-10-02 10:31:05 +00:00
PDEBUG ( DR2000 , DEBUG_NOTICE , " *** Selected channel(s) can be used for traffic only. \n " ) ;
2017-11-04 16:08:57 +00:00
PDEBUG ( DR2000 , DEBUG_NOTICE , " *** No register/call is possible at all! \n " ) ;
2017-10-02 10:31:05 +00:00
PDEBUG ( DR2000 , DEBUG_NOTICE , " *** Use combined 'CC/TC' instead! \n " ) ;
2017-06-10 13:30:20 +00:00
}
if ( combined ) {
2017-11-04 16:08:57 +00:00
PDEBUG ( DR2000 , DEBUG_NOTICE , " *** Selected (non standard) combined 'CC/TC'. \n " ) ;
PDEBUG ( DR2000 , DEBUG_NOTICE , " Phones might reject this, but non of my phones does, so it's ok. \n " ) ;
2017-06-10 13:30:20 +00:00
}
}
/* Destroy transceiver instance and unlink from list. */
void r2000_destroy ( sender_t * sender )
{
r2000_t * r2000 = ( r2000_t * ) sender ;
2019-07-20 16:11:17 +00:00
PDEBUG ( DR2000 , DEBUG_DEBUG , " Destroying 'Radiocom 2000' instance for channel = %s. \n " , sender - > kanal ) ;
2017-06-10 13:30:20 +00:00
dsp_cleanup_sender ( r2000 ) ;
timer_exit ( & r2000 - > timer ) ;
sender_destroy ( & r2000 - > sender ) ;
free ( r2000 ) ;
}
/* go idle and return to frame mode */
void r2000_go_idle ( r2000_t * r2000 )
{
timer_stop ( & r2000 - > timer ) ;
if ( r2000 - > callref ) {
PDEBUG ( DR2000 , DEBUG_ERROR , " Going idle, but still having callref, please fix! \n " ) ;
2017-10-28 05:11:40 +00:00
call_up_release ( r2000 - > callref , CAUSE_NORMAL ) ;
2017-06-10 13:30:20 +00:00
r2000 - > callref = 0 ;
}
if ( r2000 - > sysinfo . chan_type = = CHAN_TYPE_TC ) {
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Entering IDLE state, no transmission at relais %d on %s. \n " , r2000 - > sysinfo . relais , chan_type_long_name ( r2000 - > sysinfo . chan_type ) ) ;
r2000_set_dsp_mode ( r2000 , DSP_MODE_OFF , - 1 ) ;
} else {
2018-12-23 18:44:05 +00:00
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Entering IDLE state, sending idle frames at relais %d on %s. \n " , r2000 - > sysinfo . relais , chan_type_long_name ( r2000 - > sysinfo . chan_type ) ) ;
2017-06-10 13:30:20 +00:00
r2000_set_dsp_mode ( r2000 , DSP_MODE_FRAME , ( r2000 - > sender . loopback ) ? r2000_encode_super ( r2000 ) : - 1 ) ;
}
r2000_new_state ( r2000 , STATE_IDLE ) ;
// r2000_set_dsp_mode(r2000, DSP_MODE_AUDIO, r2000_encode_super(r2000));
}
/* release towards station mobile */
void r2000_release ( r2000_t * r2000 )
{
if ( r2000 - > state = = STATE_IDLE
| | r2000 - > state = = STATE_OUT_ASSIGN
| | r2000 - > state = = STATE_IN_ASSIGN
| | r2000 - > state = = STATE_RECALL_ASSIGN
| | r2000 - > state = = STATE_RECALL_WAIT ) {
/* release on CC */
r2000_new_state ( r2000 , STATE_RELEASE_CC ) ;
timer_start ( & r2000 - > timer , RELEASE_TIME ) ;
} else {
/* release on TC */
r2000_new_state ( r2000 , STATE_RELEASE_TC ) ;
timer_start ( & r2000 - > timer , RELEASE_TIME ) ;
}
r2000_set_dsp_mode ( r2000 , DSP_MODE_FRAME , - 1 ) ;
}
static void r2000_page ( r2000_t * r2000 , int try , enum r2000_state state )
{
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Entering paging state (try %d), sending 'Appel' to '%s'. \n " , try , print_subscriber_subscr ( & r2000 - > subscriber ) ) ;
r2000_new_state ( r2000 , state ) ;
r2000 - > page_try = try ;
}
static r2000_t * get_free_chan ( enum r2000_chan_type chan_type )
{
sender_t * sender ;
r2000_t * r2000 , * combined = NULL ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
r2000 = ( r2000_t * ) sender ;
/* only search for idle channel */
if ( r2000 - > state ! = STATE_IDLE )
continue ;
/* found exactly what we want */
if ( r2000 - > sysinfo . chan_type = = chan_type )
return r2000 ;
/* use combined channel as alternative */
if ( ! combined & & r2000 - > sysinfo . chan_type = = CHAN_TYPE_CC_TC )
combined = r2000 ;
}
/* return alternative, if any */
return combined ;
}
/* try to move call to given channel, release callref, if not possible */
static r2000_t * move_call_to_chan ( r2000_t * old_r2000 , enum r2000_chan_type chan_type )
{
r2000_t * new_r2000 = get_free_chan ( chan_type ) ;
/* no free channel, reuse combined channel, if possible, or release call */
if ( ! new_r2000 & & old_r2000 - > sysinfo . chan_type = = CHAN_TYPE_CC_TC ) {
PDEBUG ( DR2000 , DEBUG_NOTICE , " No %s found, straying on %s! \n " , chan_type_long_name ( chan_type ) , chan_type_long_name ( old_r2000 - > sysinfo . chan_type ) ) ;
return old_r2000 ;
}
if ( ! new_r2000 ) {
PDEBUG ( DR2000 , DEBUG_NOTICE , " Cannot move us to %s, because there is no free channel! \n " , chan_type_long_name ( chan_type ) ) ;
if ( old_r2000 - > callref ) {
PDEBUG ( DR2000 , DEBUG_NOTICE , " Failed to assign channel, releasing towards network \n " ) ;
2017-10-28 05:11:40 +00:00
call_up_release ( old_r2000 - > callref , CAUSE_NOCHANNEL ) ;
2017-06-10 13:30:20 +00:00
old_r2000 - > callref = 0 ;
}
r2000_release ( old_r2000 ) ;
return NULL ;
}
/* move subscriber */
memcpy ( & new_r2000 - > subscriber , & old_r2000 - > subscriber , sizeof ( r2000_subscriber_t ) ) ;
/* move callref */
new_r2000 - > callref = old_r2000 - > callref ;
/* move dsp mode */
r2000_set_dsp_mode ( new_r2000 , old_r2000 - > dsp_mode , - 1 ) ;
/* move call state */
r2000_new_state ( new_r2000 , old_r2000 - > state ) ;
/* cleanup old channel */
old_r2000 - > callref = 0 ;
r2000_go_idle ( old_r2000 ) ;
return new_r2000 ;
}
/*
* idle process
*/
/* trasmit beacon */
static void tx_idle ( r2000_t __attribute__ ( ( unused ) ) * r2000 , frame_t * frame )
{
frame - > voie = 1 ;
frame - > message = 1 ;
}
/*
* registration process
*/
/* receive registration */
static void rx_idle ( r2000_t * r2000 , frame_t * frame )
{
if ( ! match_voie ( r2000 , frame , 0 ) )
return ;
if ( ! match_channel ( r2000 , frame ) )
return ;
if ( ! match_relais ( r2000 , frame ) )
return ;
switch ( frame - > message ) {
case 0 :
/* inscription */
r2000 - > subscriber . type = frame - > sm_type ;
r2000 - > subscriber . relais = frame - > sm_relais ;
r2000 - > subscriber . mor = frame - > sm_mor ;
2017-08-10 15:43:36 +00:00
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Received inscription from station mobile '%s' \n " , print_subscriber_subscr ( & r2000 - > subscriber ) ) ;
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " -> Mobile Type: %d' \n " , r2000 - > subscriber . type ) ;
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " -> Home Relais: %d' \n " , r2000 - > subscriber . relais ) ;
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " -> Mobile ID: %d' \n " , r2000 - > subscriber . mor ) ;
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " (Use '%s' as dial string to call the station mobile.)' \n " , subscriber2string ( & r2000 - > subscriber ) ) ;
2017-06-10 13:30:20 +00:00
r2000_new_state ( r2000 , STATE_INSCRIPTION ) ;
break ;
case 1 :
case 3 :
/* call request */
r2000 - > subscriber . type = frame - > sm_type ;
r2000 - > subscriber . relais = frame - > sm_relais ;
r2000 - > subscriber . mor = frame - > sm_mor ;
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Received outgoing call from station mobile '%s' \n " , print_subscriber_frame ( frame ) ) ;
r2000_t * tc = get_free_chan ( CHAN_TYPE_TC ) ;
if ( ! tc ) {
PDEBUG_CHAN ( DR2000 , DEBUG_NOTICE , " Rejecting mobile originated call, no free traffic channel \n " ) ;
r2000_release ( r2000 ) ;
return ;
}
r2000_new_state ( r2000 , STATE_OUT_ASSIGN ) ;
break ;
default :
PDEBUG_CHAN ( DR2000 , DEBUG_DEBUG , " Dropping frame %s in state %s \n " , r2000_frame_name ( frame - > message , SM_TO_REL ) , r2000_state_name ( r2000 - > state ) ) ;
}
}
/* confirm registration */
static void tx_inscription ( r2000_t * r2000 , frame_t * frame )
{
frame - > voie = 1 ;
frame - > message = 0 ;
frame - > sm_type = r2000 - > subscriber . type ;
frame - > sm_relais = r2000 - > subscriber . relais ;
frame - > sm_mor = r2000 - > subscriber . mor ;
frame - > crins = r2000 - > sysinfo . crins ;
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Sending inscription acknowledge \n " ) ;
r2000_go_idle ( r2000 ) ;
}
/*
* channel assignment process
*/
/* confirm dialing, assign outgoing call */
static void tx_out_assign ( r2000_t * r2000 , frame_t * frame )
{
/* NOTE: We can only send this frame once, because afterwards we
* have moved to the new channel already !
*/
/* move us to tc */
r2000_t * tc = move_call_to_chan ( r2000 , CHAN_TYPE_TC ) ;
if ( ! tc ) {
tx_idle ( r2000 , frame ) ;
return ;
}
frame - > voie = 1 ;
frame - > message = 5 ;
frame - > sm_type = r2000 - > subscriber . type ;
frame - > sm_relais = r2000 - > subscriber . relais ;
frame - > sm_mor = r2000 - > subscriber . mor ;
2019-07-20 16:11:17 +00:00
frame - > chan_assign = atoi ( tc - > sender . kanal ) ;
2017-06-10 13:30:20 +00:00
2019-07-20 16:11:17 +00:00
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Sending outgoing assignment from channel %s to %s \n " , r2000 - > sender . kanal , tc - > sender . kanal ) ;
2017-06-10 13:30:20 +00:00
r2000_new_state ( tc , ( tc - > state = = STATE_OUT_ASSIGN ) ? STATE_OUT_IDENT : STATE_RECALL_IDENT ) ;
timer_start ( & tc - > timer , IDENT_TIME ) ;
}
/* page phone, assign incoming call */
static void tx_in_assign ( r2000_t * r2000 , frame_t * frame )
{
/* NOTE: We can only send this frame once, because afterwards we
* have moved to the new channel already !
*/
/* move us to tc */
r2000_t * tc = move_call_to_chan ( r2000 , CHAN_TYPE_TC ) ;
if ( ! tc ) {
tx_idle ( r2000 , frame ) ;
return ;
}
frame - > voie = 1 ;
frame - > message = 3 ;
frame - > sm_type = r2000 - > subscriber . type ;
frame - > sm_relais = r2000 - > subscriber . relais ;
frame - > sm_mor = r2000 - > subscriber . mor ;
2019-07-20 16:11:17 +00:00
frame - > chan_assign = atoi ( tc - > sender . kanal ) ;
2017-06-10 13:30:20 +00:00
2019-07-20 16:11:17 +00:00
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Sending incoming assignment from channel %s to %s \n " , r2000 - > sender . kanal , tc - > sender . kanal ) ;
2017-06-10 13:30:20 +00:00
r2000_new_state ( tc , STATE_IN_IDENT ) ;
timer_start ( & tc - > timer , IDENT_TIME ) ;
}
/*
* identity process
*/
/* identity request on assigned channel */
static void tx_ident ( r2000_t * r2000 , frame_t * frame )
{
frame - > voie = 0 ;
frame - > message = 16 ;
frame - > sm_type = r2000 - > subscriber . type ;
frame - > sm_relais = r2000 - > subscriber . relais ;
frame - > sm_mor = r2000 - > subscriber . mor ;
if ( r2000 - > tx_frame_count = = 1 )
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Sending identity requrest \n " ) ;
}
/* receive identity response */
static void rx_ident ( r2000_t * r2000 , frame_t * frame )
{
if ( ! match_voie ( r2000 , frame , 1 ) )
return ;
if ( ! match_channel ( r2000 , frame ) )
return ;
if ( ! match_relais ( r2000 , frame ) )
return ;
if ( ! match_subscriber ( r2000 , frame ) )
return ;
switch ( frame - > message ) {
case 16 :
/* identity response */
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Received identity response from station mobile '%s' \n " , print_subscriber_frame ( frame ) ) ;
switch ( r2000 - > state ) {
case STATE_IN_IDENT :
/* alert the phone */
r2000_new_state ( r2000 , STATE_IN_ALERT ) ;
timer_start ( & r2000 - > timer , ALERT_TIME ) ;
2017-10-28 05:11:40 +00:00
call_up_alerting ( r2000 - > callref ) ;
2017-06-10 13:30:20 +00:00
break ;
case STATE_RECALL_IDENT :
/* alert the phone */
r2000_new_state ( r2000 , STATE_RECALL_ALERT ) ;
timer_start ( & r2000 - > timer , ALERT_TIME ) ;
break ;
case STATE_OUT_IDENT :
/* request dial string */
r2000_new_state ( r2000 , STATE_OUT_DIAL1 ) ;
timer_start ( & r2000 - > timer , DIAL1_TIME ) ;
break ;
default :
break ;
}
break ;
default :
PDEBUG_CHAN ( DR2000 , DEBUG_DEBUG , " Dropping frame %s in state %s \n " , r2000_frame_name ( frame - > message , SM_TO_REL ) , r2000_state_name ( r2000 - > state ) ) ;
}
}
/* no identity response from phone */
static void timeout_out_ident ( r2000_t * r2000 )
{
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Timeout receiving identity (outgoing call) \n " ) ;
2017-08-10 15:43:36 +00:00
r2000_go_idle ( r2000 ) ;
2017-06-10 13:30:20 +00:00
}
static void timeout_in_ident ( r2000_t * r2000 )
{
2017-08-10 15:43:36 +00:00
if ( r2000 - > state = = STATE_IN_IDENT )
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Timeout receiving identity (incoming call) \n " ) ;
else
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Timeout receiving identity (recalling outgoing call) \n " ) ;
2017-06-10 13:30:20 +00:00
/* move us back to cc */
r2000 = move_call_to_chan ( r2000 , CHAN_TYPE_CC ) ;
if ( ! r2000 )
return ;
/* page again ... */
if ( - - r2000 - > page_try ) {
2017-11-04 16:08:57 +00:00
r2000_page ( r2000 , r2000 - > page_try , ( r2000 - > state = = STATE_IN_IDENT ) ? STATE_IN_ASSIGN : STATE_RECALL_ASSIGN ) ;
2017-06-10 13:30:20 +00:00
return ;
}
/* ... or release */
2017-08-10 15:43:36 +00:00
PDEBUG_CHAN ( DR2000 , DEBUG_NOTICE , " Phone does not response, releasing towards network \n " ) ;
2017-10-28 05:11:40 +00:00
call_up_release ( r2000 - > callref , CAUSE_OUTOFORDER ) ;
2017-08-10 15:43:36 +00:00
r2000 - > callref = 0 ;
2017-06-10 13:30:20 +00:00
r2000_release ( r2000 ) ;
}
/*
* alerting process ( mobile rings )
*/
static void tx_invitation ( r2000_t * r2000 , frame_t * frame , uint16_t invitation , uint8_t nconv )
{
frame - > voie = 0 ;
frame - > message = 17 ;
frame - > sm_type = r2000 - > subscriber . type ;
frame - > sm_relais = r2000 - > subscriber . relais ;
frame - > sm_mor = r2000 - > subscriber . mor ;
frame - > invitation = invitation ;
frame - > nconv = nconv ;
}
/* alert the phone */
static void tx_alert ( r2000_t * r2000 , frame_t * frame )
{
tx_invitation ( r2000 , frame , 3 , r2000 - > sysinfo . nconv ) ;
if ( r2000 - > tx_frame_count = = 1 )
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Sending answer invitation to station mobile \n " ) ;
}
static int setup_call ( r2000_t * r2000 )
{
int callref = + + new_callref ;
int rc ;
/* make call toward network */
PDEBUG ( DR2000 , DEBUG_INFO , " Setup call to network. \n " ) ;
2017-11-04 16:08:57 +00:00
/* must set callref, since MNCC may directly call call_down_answer to trigger recall, nested in call_up_setup */
r2000 - > callref = callref ;
2017-10-28 05:11:40 +00:00
rc = call_up_setup ( callref , subscriber2string ( & r2000 - > subscriber ) , r2000 - > subscriber . dialing ) ;
2017-06-10 13:30:20 +00:00
if ( rc < 0 ) {
PDEBUG ( DR2000 , DEBUG_NOTICE , " Call rejected (cause %d), releasing. \n " , - rc ) ;
2017-11-04 16:08:57 +00:00
r2000 - > callref = 0 ;
2017-06-10 13:30:20 +00:00
r2000_release ( r2000 ) ;
return rc ;
}
return 0 ;
}
/* receive answer */
static void rx_alert ( r2000_t * r2000 , frame_t * frame )
{
if ( ! match_voie ( r2000 , frame , 1 ) )
return ;
if ( ! match_channel ( r2000 , frame ) )
return ;
if ( ! match_relais ( r2000 , frame ) )
return ;
if ( ! match_subscriber ( r2000 , frame ) )
return ;
switch ( frame - > message ) {
case 17 :
/* answer */
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Received answer from station mobile '%s' \n " , print_subscriber_frame ( frame ) ) ;
switch ( r2000 - > state ) {
case STATE_IN_ALERT :
/* answer incomming call */
PDEBUG ( DR2000 , DEBUG_INFO , " Answer call to network. \n " ) ;
2017-10-28 05:11:40 +00:00
call_up_answer ( r2000 - > callref , subscriber2string ( & r2000 - > subscriber ) ) ;
2017-06-10 13:30:20 +00:00
break ;
case STATE_OUT_ALERT :
/* setup call, possible r2000_release() is called there! */
if ( setup_call ( r2000 ) < 0 )
return ;
break ;
default :
/* answer after recall, stop recall tone */
call_tone_recall ( r2000 - > callref , 0 ) ;
break ;
}
/* go active */
timer_stop ( & r2000 - > timer ) ;
r2000_new_state ( r2000 , STATE_ACTIVE ) ;
r2000_set_dsp_mode ( r2000 , DSP_MODE_AUDIO_TX , r2000_encode_super ( r2000 ) ) ;
/* start supervisory timer */
timer_start ( & r2000 - > timer , SUPER_TIME1 ) ;
break ;
default :
PDEBUG_CHAN ( DR2000 , DEBUG_DEBUG , " Dropping frame %s in state %s \n " , r2000_frame_name ( frame - > message , SM_TO_REL ) , r2000_state_name ( r2000 - > state ) ) ;
}
}
/* no answer */
static void timeout_alert ( r2000_t * r2000 )
{
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Timeout while alerting \n " ) ;
PDEBUG_CHAN ( DR2000 , DEBUG_NOTICE , " Phone does not response, releasing towards network \n " ) ;
if ( r2000 - > callref ) {
2017-10-28 05:11:40 +00:00
call_up_release ( r2000 - > callref , CAUSE_NOANSWER ) ;
2017-06-10 13:30:20 +00:00
r2000 - > callref = 0 ;
}
r2000_release ( r2000 ) ;
}
/*
* dialing process ( mobile dials )
*/
/* request digits from the phone */
static void tx_out_dial ( r2000_t * r2000 , frame_t * frame )
{
tx_invitation ( r2000 , frame , 10 , 0 ) ;
if ( r2000 - > tx_frame_count = = 1 )
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Sending dialing invitation to station mobile \n " ) ;
}
/* receive digits */
static void rx_out_dial1 ( r2000_t * r2000 , frame_t * frame )
{
int i ;
if ( ! match_voie ( r2000 , frame , 1 ) )
return ;
if ( ! match_channel ( r2000 , frame ) )
return ;
if ( ! match_relais ( r2000 , frame ) )
return ;
switch ( frame - > message ) {
case 19 :
/* digits */
for ( i = 0 ; i < 10 ; i + + )
r2000 - > subscriber . dialing [ i ] = frame - > digit [ i ] + ' 0 ' ;
r2000 - > subscriber . dialing [ 10 ] = ' \0 ' ;
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Received digits 1..10 from station mobile: %s \n " , r2000 - > subscriber . dialing ) ;
r2000_new_state ( r2000 , STATE_OUT_DIAL2 ) ;
timer_start ( & r2000 - > timer , DIAL2_TIME ) ;
break ;
default :
PDEBUG_CHAN ( DR2000 , DEBUG_DEBUG , " Dropping frame %s in state %s \n " , r2000_frame_name ( frame - > message , SM_TO_REL ) , r2000_state_name ( r2000 - > state ) ) ;
}
}
/* no digits */
static void timeout_out_dial1 ( r2000_t * r2000 )
{
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Timeout while receiving digits (outgoing call) \n " ) ;
r2000_release ( r2000 ) ;
}
/* receive digits */
static void rx_out_dial2 ( r2000_t * r2000 , frame_t * frame )
{
int i ;
if ( ! match_voie ( r2000 , frame , 1 ) )
return ;
if ( ! match_channel ( r2000 , frame ) )
return ;
if ( ! match_relais ( r2000 , frame ) )
return ;
switch ( frame - > message ) {
case 20 :
/* digits */
for ( i = 0 ; i < 10 ; i + + )
r2000 - > subscriber . dialing [ i + 10 ] = frame - > digit [ i ] + ' 0 ' ;
r2000 - > subscriber . dialing [ 20 ] = ' \0 ' ;
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Received digits 11..20 from station mobile: %s \n " , r2000 - > subscriber . dialing ) ;
if ( r2000 - > sysinfo . recall ) {
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Suspending call until called party has answered \n " ) ;
r2000_new_state ( r2000 , STATE_SUSPEND ) ;
timer_start ( & r2000 - > timer , SUSPEND_TIME ) ;
} else {
r2000_new_state ( r2000 , STATE_OUT_ALERT ) ;
timer_start ( & r2000 - > timer , ALERT_TIME ) ;
}
break ;
default :
PDEBUG_CHAN ( DR2000 , DEBUG_DEBUG , " Dropping frame %s in state %s \n " , r2000_frame_name ( frame - > message , SM_TO_REL ) , r2000_state_name ( r2000 - > state ) ) ;
}
}
/* no additional digits */
static void timeout_out_dial2 ( r2000_t * r2000 )
{
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Phone does not send digits 11..20 \n " ) ;
if ( r2000 - > sysinfo . recall ) {
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Suspending call until called party has answered \n " ) ;
r2000_new_state ( r2000 , STATE_SUSPEND ) ;
timer_start ( & r2000 - > timer , SUSPEND_TIME ) ;
} else {
r2000_new_state ( r2000 , STATE_OUT_ALERT ) ;
timer_start ( & r2000 - > timer , ALERT_TIME ) ;
}
}
/* release after dialing */
static void tx_suspend ( r2000_t * r2000 , frame_t * frame )
{
frame - > voie = 0 ;
frame - > message = 26 ;
frame - > sm_type = r2000 - > subscriber . type ;
frame - > sm_relais = r2000 - > subscriber . relais ;
frame - > sm_mor = r2000 - > subscriber . mor ;
if ( r2000 - > tx_frame_count = = 1 )
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Sending suspend frame \n " ) ;
}
/* release response */
static void rx_suspend ( r2000_t * r2000 , frame_t * frame )
{
if ( ! match_voie ( r2000 , frame , 1 ) )
return ;
if ( ! match_channel ( r2000 , frame ) )
return ;
if ( ! match_relais ( r2000 , frame ) )
return ;
if ( ! match_subscriber ( r2000 , frame ) )
return ;
switch ( frame - > message ) {
case 26 :
/* suspend ack */
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Received suspend response from station mobile '%s' \n " , print_subscriber_frame ( frame ) ) ;
timer_stop ( & r2000 - > timer ) ;
/* move us back to cc */
r2000 = move_call_to_chan ( r2000 , CHAN_TYPE_CC ) ;
if ( ! r2000 )
return ;
r2000_new_state ( r2000 , STATE_RECALL_WAIT ) ;
/* setup call, possible r2000_release() is called there! */
if ( setup_call ( r2000 ) < 0 )
return ;
break ;
default :
PDEBUG_CHAN ( DR2000 , DEBUG_DEBUG , " Dropping frame %s in state %s \n " , r2000_frame_name ( frame - > message , SM_TO_REL ) , r2000_state_name ( r2000 - > state ) ) ;
}
}
/* response to accept frame */
static void timeout_suspend ( r2000_t * r2000 )
{
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Phone does not respond to suspend frame \n " ) ;
r2000_release ( r2000 ) ;
}
/*
* process during active call
*/
static void timeout_active ( r2000_t * r2000 )
{
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Timeout after loosing supervisory signal, releasing call \n " ) ;
2017-10-28 05:11:40 +00:00
call_up_release ( r2000 - > callref , CAUSE_TEMPFAIL ) ;
2017-06-10 13:30:20 +00:00
r2000 - > callref = 0 ;
r2000_release ( r2000 ) ;
}
/*
* release process
*/
static void tx_release_cc ( r2000_t * r2000 , frame_t * frame )
{
frame - > voie = 1 ;
frame - > message = 9 ;
frame - > sm_type = r2000 - > subscriber . type ;
frame - > sm_relais = r2000 - > subscriber . relais ;
frame - > sm_mor = r2000 - > subscriber . mor ;
if ( r2000 - > tx_frame_count = = 1 )
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Sending release towards station mobile \n " ) ;
}
static void timeout_release_cc ( r2000_t * r2000 )
{
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Done sending release, going idle \n " ) ;
r2000_go_idle ( r2000 ) ;
}
static void tx_release_tc ( r2000_t * r2000 , frame_t * frame )
{
frame - > voie = 0 ;
frame - > message = 24 ;
frame - > sm_type = r2000 - > subscriber . type ;
frame - > sm_relais = r2000 - > subscriber . relais ;
frame - > sm_mor = r2000 - > subscriber . mor ;
if ( r2000 - > tx_frame_count = = 1 )
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Sending release towards station mobile \n " ) ;
}
static void timeout_release_tc ( r2000_t * r2000 )
{
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Timeout while sending release, going idle \n " ) ;
r2000_go_idle ( r2000 ) ;
}
/* FSK processing requests next frame after transmission of previous
frame has been finished . */
const char * r2000_get_frame ( r2000_t * r2000 )
{
frame_t frame ;
const char * bits ;
int debug = 1 ;
r2000 - > tx_frame_count + + ;
memset ( & frame , 0 , sizeof ( frame ) ) ;
2019-07-20 16:11:17 +00:00
frame . channel = atoi ( r2000 - > sender . kanal ) ;
2017-06-10 13:30:20 +00:00
frame . relais = r2000 - > sysinfo . relais ;
frame . deport = r2000 - > sysinfo . deport ;
frame . agi = r2000 - > sysinfo . agi ;
frame . sm_power = r2000 - > sysinfo . sm_power ;
frame . taxe = r2000 - > sysinfo . taxe ;
switch ( r2000 - > state ) {
case STATE_IDLE :
case STATE_RECALL_WAIT :
tx_idle ( r2000 , & frame ) ;
debug = 0 ;
break ;
case STATE_INSCRIPTION :
tx_inscription ( r2000 , & frame ) ;
break ;
case STATE_OUT_ASSIGN :
case STATE_RECALL_ASSIGN :
tx_out_assign ( r2000 , & frame ) ;
break ;
case STATE_IN_ASSIGN :
tx_in_assign ( r2000 , & frame ) ;
break ;
case STATE_OUT_IDENT :
case STATE_RECALL_IDENT :
case STATE_IN_IDENT :
tx_ident ( r2000 , & frame ) ;
break ;
case STATE_OUT_DIAL1 :
case STATE_OUT_DIAL2 :
tx_out_dial ( r2000 , & frame ) ;
break ;
case STATE_SUSPEND :
tx_suspend ( r2000 , & frame ) ;
break ;
case STATE_IN_ALERT :
case STATE_OUT_ALERT :
case STATE_RECALL_ALERT :
tx_alert ( r2000 , & frame ) ;
break ;
case STATE_RELEASE_CC :
tx_release_cc ( r2000 , & frame ) ;
break ;
case STATE_RELEASE_TC :
tx_release_tc ( r2000 , & frame ) ;
break ;
default :
/* in case there is no handling, change to audio mode */
/* this should not happen, but prevents an endless loop
* when the DSP tries to get next frame . */
r2000_set_dsp_mode ( r2000 , DSP_MODE_AUDIO_TX_RX , - 1 ) ;
}
/* frame sending aborted (e.g. due to audio) */
if ( r2000 - > dsp_mode ! = DSP_MODE_FRAME )
return NULL ;
bits = encode_frame ( & frame , debug ) ;
PDEBUG_CHAN ( DR2000 , DEBUG_DEBUG , " Sending frame %s. \n " , r2000_frame_name ( frame . message , REL_TO_SM ) ) ;
return bits ;
}
void r2000_receive_frame ( r2000_t * r2000 , const char * bits , double quality , double level )
{
frame_t frame ;
int rc ;
PDEBUG_CHAN ( DDSP , DEBUG_INFO , " RX Level: %.0f%% Quality=%.0f \n " , level * 100.0 , quality * 100.0 ) ;
rc = decode_frame ( & frame , bits ) ;
if ( rc < 0 ) {
PDEBUG_CHAN ( DR2000 , ( r2000 - > sender . loopback ) ? DEBUG_NOTICE : DEBUG_DEBUG , " Received invalid frame. \n " ) ;
return ;
}
if ( r2000 - > sender . loopback )
PDEBUG_CHAN ( DR2000 , DEBUG_NOTICE , " Received frame %s \n " , r2000_frame_name ( frame . message , REL_TO_SM ) ) ;
else
PDEBUG_CHAN ( DR2000 , DEBUG_DEBUG , " Received frame %s \n " , r2000_frame_name ( frame . message , SM_TO_REL ) ) ;
if ( r2000 - > sender . loopback )
return ;
/* release */
if ( frame . message = = 6 | | frame . message = = 24 ) {
if ( r2000 - > state = = STATE_IDLE )
return ;
if ( ! match_voie ( r2000 , & frame , ( frame . message < 16 ) ? 0 : 1 ) )
return ;
if ( ! match_channel ( r2000 , & frame ) )
return ;
if ( ! match_relais ( r2000 , & frame ) )
return ;
if ( ! match_subscriber ( r2000 , & frame ) )
return ;
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Received release from station mobile \n " ) ;
if ( r2000 - > callref ) {
2017-10-28 05:11:40 +00:00
call_up_release ( r2000 - > callref , CAUSE_NORMAL ) ;
2017-06-10 13:30:20 +00:00
r2000 - > callref = 0 ;
}
r2000_go_idle ( r2000 ) ;
return ;
}
switch ( r2000 - > state ) {
case STATE_IDLE :
rx_idle ( r2000 , & frame ) ;
break ;
case STATE_OUT_IDENT :
case STATE_RECALL_IDENT :
case STATE_IN_IDENT :
rx_ident ( r2000 , & frame ) ;
break ;
case STATE_OUT_DIAL1 :
rx_out_dial1 ( r2000 , & frame ) ;
break ;
case STATE_OUT_DIAL2 :
rx_out_dial2 ( r2000 , & frame ) ;
break ;
case STATE_SUSPEND :
rx_suspend ( r2000 , & frame ) ;
break ;
case STATE_IN_ALERT :
case STATE_OUT_ALERT :
case STATE_RECALL_ALERT :
rx_alert ( r2000 , & frame ) ;
break ;
default :
PDEBUG_CHAN ( DR2000 , DEBUG_DEBUG , " Dropping frame %s in state %s \n " , r2000_frame_name ( frame . message , SM_TO_REL ) , r2000_state_name ( r2000 - > state ) ) ;
}
}
void r2000_receive_super ( r2000_t * r2000 , uint8_t super , double quality , double level )
{
uint8_t nconv , relais ;
/* invert, if received from base station */
if ( r2000 - > sender . loopback )
super ^ = 0x7f ;
/* decode supervisory digit (nconv is LSB first) */
nconv = ( ( super > > 2 ) & 0x01 )
| ( super & 0x02 )
| ( ( super < < 2 ) & 0x04 ) ;
relais = ( ( super > > 6 ) & 0x01 )
| ( ( super > > 4 ) & 0x02 )
| ( ( super > > 2 ) & 0x04 )
| ( super & 0x08 ) ;
PDEBUG_CHAN ( DDSP , DEBUG_INFO , " RX Supervisory: NCONV: %d Relais (4 lowest bits): %d RX Level: %.0f%% Quality=%.0f \n " , nconv , relais , level * 100.0 , quality * 100.0 ) ;
if ( r2000 - > sender . loopback )
return ;
if ( r2000 - > state ! = STATE_ACTIVE )
return ;
if ( relais ! = ( r2000 - > sysinfo . relais & 0xf )
| | nconv ! = r2000 - > sysinfo . nconv )
return ;
/* unmute RX audio if not already */
r2000_set_dsp_mode ( r2000 , DSP_MODE_AUDIO_TX_RX , - 1 ) ;
/* reset supervisory timer */
timer_start ( & r2000 - > timer , SUPER_TIME2 ) ;
}
/* Timeout handling */
static void r2000_timeout ( struct timer * timer )
{
r2000_t * r2000 = ( r2000_t * ) timer - > priv ;
switch ( r2000 - > state ) {
case STATE_OUT_IDENT :
timeout_out_ident ( r2000 ) ;
break ;
case STATE_IN_IDENT :
case STATE_RECALL_IDENT :
timeout_in_ident ( r2000 ) ;
break ;
case STATE_OUT_DIAL1 :
timeout_out_dial1 ( r2000 ) ;
break ;
case STATE_OUT_DIAL2 :
timeout_out_dial2 ( r2000 ) ;
break ;
case STATE_SUSPEND :
timeout_suspend ( r2000 ) ;
break ;
case STATE_IN_ALERT :
case STATE_OUT_ALERT :
case STATE_RECALL_ALERT :
timeout_alert ( r2000 ) ;
break ;
case STATE_ACTIVE :
timeout_active ( r2000 ) ;
break ;
case STATE_RELEASE_CC :
timeout_release_cc ( r2000 ) ;
break ;
case STATE_RELEASE_TC :
timeout_release_tc ( r2000 ) ;
break ;
default :
break ;
}
}
/*
* call states received from call control
*/
/* Call control starts call towards station mobile. */
2017-10-28 05:11:40 +00:00
int call_down_setup ( int callref , const char __attribute__ ( ( unused ) ) * caller_id , enum number_type __attribute__ ( ( unused ) ) caller_type , const char * dialing )
2017-06-10 13:30:20 +00:00
{
sender_t * sender ;
r2000_t * r2000 , * tc ;
r2000_subscriber_t subscr ;
memset ( & subscr , 0 , sizeof ( subscr ) ) ;
/* 1. convert number to station mobile identification, return INVALNUMBER */
if ( string2subscriber ( dialing , & subscr ) ) {
PDEBUG ( DR2000 , DEBUG_NOTICE , " Outgoing call to invalid number '%s', rejecting! \n " , dialing ) ;
return - CAUSE_INVALNUMBER ;
}
/* 2. check if given number is already in a call, return BUSY */
for ( sender = sender_head ; sender ; sender = sender - > next ) {
r2000 = ( r2000_t * ) sender ;
if ( r2000 - > state ! = STATE_IDLE
& & r2000 - > subscriber . relais = = subscr . relais
& & r2000 - > subscriber . mor = = subscr . mor )
break ;
}
if ( sender ) {
PDEBUG ( DR2000 , DEBUG_NOTICE , " Outgoing call to busy number, rejecting! \n " ) ;
return - CAUSE_BUSY ;
}
/* 3. check if all paging (control) channels are busy, return NOCHANNEL */
r2000 = get_free_chan ( CHAN_TYPE_CC ) ;
if ( ! r2000 ) {
PDEBUG ( DR2000 , DEBUG_NOTICE , " Outgoing call, but no free control channel, rejecting! \n " ) ;
return - CAUSE_NOCHANNEL ;
}
tc = get_free_chan ( CHAN_TYPE_TC ) ;
if ( ! tc ) {
PDEBUG ( DR2000 , DEBUG_NOTICE , " Outgoing call, but no free traffic channel, rejecting! \n " ) ;
return - CAUSE_NOCHANNEL ;
}
PDEBUG ( DR2000 , DEBUG_INFO , " Call to station mobile, paging station id '%s' \n " , print_subscriber_subscr ( & subscr ) ) ;
/* 4. trying to page station mobile */
memcpy ( & r2000 - > subscriber , & subscr , sizeof ( r2000_subscriber_t ) ) ;
r2000 - > callref = callref ;
r2000_page ( r2000 , PAGE_TRIES , STATE_IN_ASSIGN ) ;
return 0 ;
}
/* Call control answers call toward station mobile. */
2017-10-28 05:11:40 +00:00
void call_down_answer ( int callref )
2017-06-10 13:30:20 +00:00
{
sender_t * sender ;
r2000_t * r2000 ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
r2000 = ( r2000_t * ) sender ;
if ( r2000 - > callref = = callref )
break ;
}
if ( ! sender ) {
PDEBUG ( DR2000 , DEBUG_NOTICE , " Outgoing answer, but no callref! \n " ) ;
2017-10-28 05:11:40 +00:00
call_up_release ( callref , CAUSE_INVALCALLREF ) ;
2017-06-10 13:30:20 +00:00
return ;
}
switch ( r2000 - > state ) {
case STATE_RECALL_WAIT :
PDEBUG_CHAN ( DR2000 , DEBUG_INFO , " Call has been answered by network, recalling station mobile. \n " ) ;
r2000_page ( r2000 , PAGE_TRIES , STATE_RECALL_ASSIGN ) ;
call_tone_recall ( callref , 1 ) ;
break ;
default :
break ;
}
}
/* Call control sends disconnect (with tones).
* An active call stays active , so tones and annoucements can be received
* by station mobile .
*/
2017-10-28 05:11:40 +00:00
void call_down_disconnect ( int callref , int __attribute__ ( ( unused ) ) cause )
2017-06-10 13:30:20 +00:00
{
sender_t * sender ;
r2000_t * r2000 ;
PDEBUG ( DR2000 , DEBUG_INFO , " Call has been disconnected by network. \n " ) ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
r2000 = ( r2000_t * ) sender ;
if ( r2000 - > callref = = callref )
break ;
}
if ( ! sender ) {
PDEBUG ( DR2000 , DEBUG_NOTICE , " Outgoing disconnect, but no callref! \n " ) ;
2017-10-28 05:11:40 +00:00
call_up_release ( callref , CAUSE_INVALCALLREF ) ;
2017-06-10 13:30:20 +00:00
return ;
}
/* Release when not active and not waiting for answer */
if ( r2000 - > state = = STATE_ACTIVE )
return ;
switch ( r2000 - > state ) {
default :
PDEBUG_CHAN ( DR2000 , DEBUG_NOTICE , " Outgoing disconnect, during call setup, releasing! \n " ) ;
r2000 - > callref = 0 ;
r2000_release ( r2000 ) ;
break ;
}
2017-10-28 05:11:40 +00:00
call_up_release ( callref , cause ) ;
2017-06-10 13:30:20 +00:00
}
/* Call control releases call toward station mobile. */
2017-10-28 05:11:40 +00:00
void call_down_release ( int callref , int __attribute__ ( ( unused ) ) cause )
2017-06-10 13:30:20 +00:00
{
sender_t * sender ;
r2000_t * r2000 ;
PDEBUG ( DR2000 , DEBUG_INFO , " Call has been released by network, releasing call. \n " ) ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
r2000 = ( r2000_t * ) sender ;
if ( r2000 - > callref = = callref )
break ;
}
if ( ! sender ) {
PDEBUG ( DR2000 , DEBUG_NOTICE , " Outgoing release, but no callref! \n " ) ;
/* don't send release, because caller already released */
return ;
}
r2000 - > callref = 0 ;
switch ( r2000 - > state ) {
case STATE_ACTIVE :
PDEBUG_CHAN ( DR2000 , DEBUG_NOTICE , " Outgoing release, during ringing, releasing! \n " ) ;
r2000_release ( r2000 ) ;
break ;
default :
PDEBUG_CHAN ( DR2000 , DEBUG_NOTICE , " Outgoing release, during call setup, releasing! \n " ) ;
r2000_release ( r2000 ) ;
break ;
}
}
/* Receive audio from call instance. */
2017-10-28 05:11:40 +00:00
void call_down_audio ( int callref , sample_t * samples , int count )
2017-06-10 13:30:20 +00:00
{
sender_t * sender ;
r2000_t * r2000 ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
r2000 = ( r2000_t * ) sender ;
if ( r2000 - > callref = = callref )
break ;
}
if ( ! sender )
return ;
if ( r2000 - > dsp_mode = = DSP_MODE_AUDIO_TX
| | r2000 - > dsp_mode = = DSP_MODE_AUDIO_TX_RX ) {
sample_t up [ ( int ) ( ( double ) count * r2000 - > sender . srstate . factor + 0.5 ) + 10 ] ;
if ( r2000 - > compandor )
compress_audio ( & r2000 - > cstate , samples , count ) ;
count = samplerate_upsample ( & r2000 - > sender . srstate , samples , count , up ) ;
jitter_save ( & r2000 - > sender . dejitter , up , count ) ;
}
}
2020-01-12 06:54:25 +00:00
void call_down_clock ( void ) { }
2017-06-10 13:30:20 +00:00
void dump_info ( void ) { }