2016-02-21 17:13:30 +00:00
/* NMT protocol handling
*
* ( C ) 2016 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 <stdint.h>
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# include "../common/debug.h"
# include "../common/timer.h"
# include "../common/call.h"
# include "../common/cause.h"
# include "nmt.h"
# include "dsp.h"
# include "frame.h"
/* Call reference for calls from mobile station to network
This offset of 0x400000000 is required for MNCC interface . */
static int new_callref = 0x40000000 ;
/* Timers */
# define RELEASE_TO 2.0 /* how long do we wait for release guard of the phone */
# define DIALING_TO 1.0 /* if we have a pause during dialing, we abort the call */
# define CHANNEL_TO 2.0 /* how long do we wait for phone to appear on assigned channel */
# define RINGING_TO 60.0 /* how long may the phone ring */
# define SUPERVISORY_TO1 3.0 /* 3 sec to detect after setup */
# define SUPERVISORY_TO2 20.0 /* 20 sec lost until abort */
/* Counters */
# define PAGE_TRIES 3 /* How many time do we try to page the phone */
const char * nmt_state_name ( enum nmt_state state )
{
static char invalid [ 16 ] ;
switch ( state ) {
case STATE_NULL :
return " (NULL) " ;
case STATE_IDLE :
return " IDLE " ;
case STATE_ROAMING_IDENT :
return " ROAMING IDENT " ;
case STATE_ROAMING_CONFIRM :
return " ROAMING CONFIRM " ;
case STATE_MO_IDENT :
return " MO CALL IDENT " ;
case STATE_MO_CONFIRM :
return " MO CALL CONFIRM " ;
case STATE_MO_DIALING :
return " MO CALL DIALING " ;
case STATE_MO_COMPLETE :
return " MO CALL COMPLETE " ;
case STATE_MT_PAGING :
return " MT CALL PAGING " ;
case STATE_MT_CHANNEL :
return " MT CALL CHANNEL " ;
case STATE_MT_IDENT :
return " MT CALL IDENT " ;
case STATE_MT_RINGING :
return " MT CALL RINGING " ;
case STATE_MT_COMPLETE :
return " MT CALL COMPLETE " ;
case STATE_ACTIVE :
return " ACTIVE " ;
case STATE_MO_RELEASE :
return " RELEASE MTX->MS " ;
case STATE_MT_RELEASE :
return " RELEASE MTX->MS " ;
}
sprintf ( invalid , " invalid(%d) " , state ) ;
return invalid ;
}
static void nmt_new_state ( nmt_t * nmt , enum nmt_state new_state )
{
if ( nmt - > state = = new_state )
return ;
PDEBUG ( DNMT , DEBUG_DEBUG , " State change: %s -> %s \n " , nmt_state_name ( nmt - > state ) , nmt_state_name ( new_state ) ) ;
nmt - > state = new_state ;
}
static struct nmt_channels {
enum nmt_chan_type chan_type ;
const char * short_name ;
const char * long_name ;
} nmt_channels [ ] = {
{ CHAN_TYPE_CC , " CC " , " calling channel " } ,
{ CHAN_TYPE_TC , " TC " , " traffic channel " } ,
{ CHAN_TYPE_CC_TC , " CC/TC " , " combined calling & traffic channel " } ,
{ CHAN_TYPE_TEST , " TEST " , " calling channel " } ,
{ 0 , NULL , NULL }
} ;
void nmt_channel_list ( void )
{
int i ;
printf ( " Type \t Description \n " ) ;
printf ( " ------------------------------------------------------------------------ \n " ) ;
for ( i = 0 ; nmt_channels [ i ] . long_name ; i + + )
printf ( " %s \t %s \n " , nmt_channels [ i ] . short_name , nmt_channels [ i ] . long_name ) ;
}
int nmt_channel_by_short_name ( const char * short_name )
{
int i ;
for ( i = 0 ; nmt_channels [ i ] . short_name ; i + + ) {
if ( ! strcasecmp ( nmt_channels [ i ] . short_name , short_name ) ) {
PDEBUG ( DNMT , DEBUG_INFO , " Selecting channel '%s' = %s \n " , nmt_channels [ i ] . short_name , nmt_channels [ i ] . long_name ) ;
return nmt_channels [ i ] . chan_type ;
}
}
return - 1 ;
}
const char * chan_type_short_name ( enum nmt_chan_type chan_type )
{
int i ;
for ( i = 0 ; nmt_channels [ i ] . short_name ; i + + ) {
if ( nmt_channels [ i ] . chan_type = = chan_type )
return nmt_channels [ i ] . short_name ;
}
return " invalid " ;
}
const char * chan_type_long_name ( enum nmt_chan_type chan_type )
{
int i ;
for ( i = 0 ; nmt_channels [ i ] . long_name ; i + + ) {
if ( nmt_channels [ i ] . chan_type = = chan_type )
return nmt_channels [ i ] . long_name ;
}
return " invalid " ;
}
const char * nmt_dir_name ( enum nmt_direction dir )
{
switch ( dir ) {
case MTX_TO_MS :
return " MTX->MS " ;
case MTX_TO_BS :
return " MTX->BS " ;
case MTX_TO_XX :
return " MTX->XX " ;
case BS_TO_MTX :
return " BS->MTX " ;
case MS_TO_MTX :
return " MS->MTX " ;
case XX_TO_MTX :
return " XX->MTX " ;
}
return " invalid " ;
}
/* Convert channel number to frequency number of base station.
Set ' uplink ' to 1 to get frequency of mobile station . */
double nmt_channel2freq ( int channel , int uplink )
{
double freq ;
if ( channel < 1 )
return 0 ;
else if ( channel < = 180 )
freq = 463.000 + ( channel - 1 ) * 0.025 ;
else if ( channel < = 200 )
freq = 462.500 + ( channel - 181 ) * 0.025 ;
else if ( channel < = 380 )
freq = 463.000 + ( channel - 201 ) * 0.025 + 0.0125 ;
else if ( channel < = 399 ) /* no channel 400, caused by interleaving and coding */
freq = 462.500 + ( channel - 381 ) * 0.025 + 0.0125 ;
else
return 0 ;
if ( uplink )
freq - = 10.000 ;
return freq ;
}
/* convert 7-digits dial string to NMT number */
static int dialstring2number ( const char * dialstring , char * ms_country , char * ms_number )
{
if ( strlen ( dialstring ) ! = 7 ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Wrong number of digits, use 7 digits: ZXXXXXX (Z=country, X=mobile number) \n " ) ;
return - 1 ;
}
if ( dialstring [ 0 ] < ' 0 ' & & dialstring [ 0 ] > ' 9 ' ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Invalid country digit (first digit) of dial string \n " ) ;
return - 1 ;
}
* ms_country = dialstring [ 0 ] ;
memcpy ( ms_number , dialstring + 1 , 6 ) ;
return 0 ;
}
/* country selector */
static struct nmt_country {
int y ;
const char * short_name ;
const char * long_name ;
const char * provider_name ;
} nmt_country [ ] = {
{ 5 , " DK " , " Denmark " , " Tele Danmark Mobile " } ,
{ 6 , " SE " , " Sweden " , " Telia Mobitel " } ,
{ 7 , " NO " , " Norway " , " Telenor Mobil " } ,
{ 8 , " FI " , " Finland " , " Telecom Finland " } ,
{ 8 , " IS " , " Iceland " , " Post & Telecom " } ,
{ 5 , " FO " , " Faroe Island " , " Faroese Telecom " } ,
{ 7 , " EE " , " Estonia " , " Eesti Mobiiltelefon " } ,
{ 5 , " LV " , " Latvia " , " Latvian Mobile Telephone " } ,
{ 8 , " LT " , " Lithuania " , " COMLIET " } ,
{ 6 , " BY " , " Belarus " , " Belcel " } ,
{ 5 , " MO " , " OSS/Moscow " , " Moscow Cellular Comm. " } ,
{ 6 , " STP " , " OSS/St Petersburg " , " Delta Telecom " } ,
{ 6 , " STP " , " OSS/Leningrads Dist. " , " Delta Telecom " } ,
{ 7 , " CAR " , " OSS/Carelian Rep. " , " Telecom Finland " } ,
{ 5 , " MUR " , " OSS/Murmansk " , " Telecom Finland " } ,
{ 5 , " LED " , " OSS/Leningrads Dist. " , " Telecom Finland " } ,
{ 5 , " KAL " , " Kaliningrad " , " Telecom Finland " } ,
{ 7 , " PL " , " Poland " , " CENTERTEL " } ,
{ 6 , " BG " , " Bulgaria " , " MOBIFON " } ,
{ 5 , " RO " , " Romania " , " Telefonica Romania " } ,
{ 6 , " UA " , " Ukraine " , " Ukraine Mobile Comm. " } ,
{ 1 , " RU1 " , " " , " " } ,
{ 2 , " RU2 " , " " , " " } ,
{ 3 , " RU3 " , " " , " " } ,
{ 4 , " RU4 " , " " , " " } ,
{ 0 , NULL , NULL , NULL }
} ;
void nmt_country_list ( void )
{
int i ;
printf ( " TA \t Short \t Country (Provider) \n " ) ;
printf ( " ------------------------------------------------------------------------ \n " ) ;
for ( i = 0 ; nmt_country [ i ] . short_name ; i + + ) {
2016-03-26 17:42:38 +00:00
if ( nmt_country [ i ] . long_name [ 0 ] )
2016-02-21 17:13:30 +00:00
printf ( " %d \t %s \t %s (%s) \n " , nmt_country [ i ] . y , nmt_country [ i ] . short_name , nmt_country [ i ] . long_name , nmt_country [ i ] . provider_name ) ;
else
printf ( " %d \t %s \n " , nmt_country [ i ] . y , nmt_country [ i ] . short_name ) ;
}
}
uint8_t nmt_country_by_short_name ( const char * short_name )
{
int i ;
for ( i = 0 ; nmt_country [ i ] . short_name ; i + + ) {
if ( ! strcasecmp ( nmt_country [ i ] . short_name , short_name ) ) {
PDEBUG ( DNMT , DEBUG_INFO , " Selecting country code %d of %s,%s (provider '%s') \n " , nmt_country [ i ] . y , nmt_country [ i ] . short_name , nmt_country [ i ] . long_name , nmt_country [ i ] . provider_name ) ;
return nmt_country [ i ] . y ;
}
}
return 0 ;
}
static void nmt_timeout ( struct timer * timer ) ;
static void nmt_go_idle ( nmt_t * nmt ) ;
/* Create transceiver instance and link to a list. */
2016-04-23 16:50:11 +00:00
int nmt_create ( const char * sounddev , int samplerate , int pre_emphasis , int de_emphasis , const char * write_wave , const char * read_wave , int channel , enum nmt_chan_type chan_type , uint8_t ms_power , uint8_t traffic_area , uint8_t area_no , int compander , int supervisory , int loopback )
2016-02-21 17:13:30 +00:00
{
nmt_t * nmt ;
int rc ;
if ( channel < 1 | | channel > 399 ) {
PDEBUG ( DNMT , DEBUG_ERROR , " Channel number %d invalid. \n " , channel ) ;
return - EINVAL ;
}
if ( channel > = 201 ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " *** Channels numbers above 200 have been specified, but never used. These 'interleaved channels are probably not supports by the phone. \n " ) ;
}
if ( channel > = 181 & & channel < = 200 ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Extended channel numbers (181..200) have been specified, but never been supported for sure. There is no phone to test with, so don't use it! \n " ) ;
}
if ( chan_type = = CHAN_TYPE_CC ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " *** Selected channel can be used for calling only. \n " ) ;
PDEBUG ( DNMT , DEBUG_NOTICE , " *** No call from the mobile phone is possible. \n " ) ;
PDEBUG ( DNMT , DEBUG_NOTICE , " *** Use combined 'CC/TC' instead! \n " ) ;
}
if ( chan_type = = CHAN_TYPE_TC ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " *** Selected channel can be used for traffic only. \n " ) ;
PDEBUG ( DNMT , DEBUG_NOTICE , " *** No call to the mobile phone is possible. \n " ) ;
PDEBUG ( DNMT , DEBUG_NOTICE , " *** Use combined 'CC/TC' instead! \n " ) ;
}
if ( chan_type = = CHAN_TYPE_TEST & & ! loopback ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " *** Selected channel can be used for nothing but testing signal decoder. \n " ) ;
}
nmt = calloc ( 1 , sizeof ( nmt_t ) ) ;
if ( ! nmt ) {
PDEBUG ( DNMT , DEBUG_ERROR , " No memory! \n " ) ;
return - ENOMEM ;
}
PDEBUG ( DNMT , DEBUG_DEBUG , " Creating 'NMT' instance for channel = %d (sample rate %d). \n " , channel , samplerate ) ;
/* init general part of transceiver */
2016-04-23 16:50:11 +00:00
rc = sender_create ( & nmt - > sender , sounddev , samplerate , pre_emphasis , de_emphasis , write_wave , read_wave , channel , loopback , 0 , - 1 ) ;
2016-02-21 17:13:30 +00:00
if ( rc < 0 ) {
PDEBUG ( DNMT , DEBUG_ERROR , " Failed to init transceiver process! \n " ) ;
goto error ;
}
/* init audio processing */
rc = dsp_init_sender ( nmt ) ;
if ( rc < 0 ) {
PDEBUG ( DNMT , DEBUG_ERROR , " Failed to init audio processing! \n " ) ;
goto error ;
}
timer_init ( & nmt - > timer , nmt_timeout , nmt ) ;
nmt - > sysinfo . chan_type = chan_type ;
nmt - > sysinfo . ms_power = ms_power ;
nmt - > sysinfo . traffic_area = traffic_area ;
nmt - > sysinfo . area_no = area_no ;
nmt - > compander = compander ;
nmt - > supervisory = supervisory ;
/* go into idle state */
nmt_go_idle ( nmt ) ;
return 0 ;
error :
nmt_destroy ( & nmt - > sender ) ;
return rc ;
}
/* Destroy transceiver instance and unlink from list. */
void nmt_destroy ( sender_t * sender )
{
nmt_t * nmt = ( nmt_t * ) sender ;
PDEBUG ( DNMT , DEBUG_DEBUG , " Destroying 'NMT' instance for channel = %d. \n " , sender - > kanal ) ;
dsp_cleanup_sender ( nmt ) ;
timer_exit ( & nmt - > timer ) ;
sender_destroy ( & nmt - > sender ) ;
free ( nmt ) ;
}
/* Abort connection towards mobile station by sending idle digits. */
static void nmt_go_idle ( nmt_t * nmt )
{
timer_stop ( & nmt - > timer ) ;
PDEBUG ( DNMT , DEBUG_INFO , " Entering IDLE state, sending idle frames on %s. \n " , chan_type_long_name ( nmt - > sysinfo . chan_type ) ) ;
nmt_new_state ( nmt , STATE_IDLE ) ;
nmt_set_dsp_mode ( nmt , DSP_MODE_FRAME ) ;
memset ( & nmt - > subscriber , 0 , sizeof ( nmt - > subscriber ) ) ;
memset ( & nmt - > dialing , 0 , sizeof ( nmt - > dialing ) ) ;
}
/* release an ongoing connection, this is used by roaming update and release initiated by MTX */
static void nmt_release ( nmt_t * nmt )
{
timer_stop ( & nmt - > timer ) ;
PDEBUG ( DNMT , DEBUG_INFO , " Releasing connection towards mobile station. \n " ) ;
nmt_new_state ( nmt , STATE_MT_RELEASE ) ;
nmt - > tx_frame_count = 0 ;
nmt_set_dsp_mode ( nmt , DSP_MODE_FRAME ) ;
timer_start ( & nmt - > timer , RELEASE_TO ) ;
}
/* Enter paging state and transmit phone's number on calling channel */
static void nmt_page ( nmt_t * nmt , char ms_country , const char * ms_number , int try )
{
PDEBUG ( DNMT , DEBUG_INFO , " Entering paging state (try %d), sending call to '%c,%s'. \n " , try , ms_country , ms_number ) ;
nmt - > subscriber . country = ms_country ;
strcpy ( nmt - > subscriber . number , ms_number ) ;
nmt - > page_try = try ;
nmt_new_state ( nmt , STATE_MT_PAGING ) ;
nmt_set_dsp_mode ( nmt , DSP_MODE_FRAME ) ;
nmt - > tx_frame_count = 0 ;
}
/*
* frame matching functions to check if channels is accessed correctly
*/
/* check match channel no, area no and traffic area */
static int match_channel ( nmt_t * nmt , frame_t * frame )
{
int channel , power ;
/* check channel match */
nmt_decode_channel ( frame - > channel_no , & channel , & power ) ;
if ( channel ! = nmt - > sender . kanal ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Frame for different channel %d received, ignoring. \n " , channel ) ;
return 0 ;
}
return 1 ;
}
static int match_area ( nmt_t * nmt , frame_t * frame )
{
uint8_t area_no , traffic_area ;
/* old phones do not support ZY digits */
if ( frame - > area_info + frame - > traffic_area = = 0 )
goto skip_area ;
area_no = frame - > area_info > > 2 ;
if ( area_no = = 0 & & nmt - > sysinfo . area_no ! = 0 )
area_no = 4 ;
if ( area_no ! = nmt - > sysinfo . area_no ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Received area no (%d) does not match the base station's area no (%d), ignoring. \n " , area_no , nmt - > sysinfo . area_no ) ;
return 0 ;
}
traffic_area = ( ( frame - > area_info & 0x3 ) < < 4 ) | frame - > traffic_area ;
if ( nmt - > sysinfo . traffic_area ! = 0 & & ( nmt - > sysinfo . traffic_area & 0x3f ) ! = traffic_area ) {
2016-03-16 17:21:29 +00:00
PDEBUG ( DNMT , DEBUG_NOTICE , " Received 6 bits of traffic area (0x%02x) does not match the 6 bits of base station's traffic area (0x%02x), ignoring. \n " , nmt - > sysinfo . traffic_area & 0x3f , traffic_area ) ;
2016-02-21 17:13:30 +00:00
return 0 ;
}
skip_area :
return 1 ;
}
/* check match subscriber number */
static int match_subscriber ( nmt_t * nmt , frame_t * frame )
{
if ( nmt_digits2value ( & nmt - > subscriber . country , 1 ) ! = frame - > ms_country ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Received non matching subscriber counrtry, ignoring. \n " ) ;
return 0 ;
}
if ( nmt_digits2value ( nmt - > subscriber . number , 6 ) ! = frame - > ms_number ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Received non matching subscriber number, ignoring. \n " ) ;
return 0 ;
}
return 1 ;
}
/*
* handle idle channel
*/
static void tx_idle ( nmt_t * nmt , frame_t * frame )
{
switch ( nmt - > sysinfo . chan_type ) {
case CHAN_TYPE_CC :
frame - > index = NMT_MESSAGE_1a ;
break ;
case CHAN_TYPE_TC :
frame - > index = NMT_MESSAGE_4 ;
break ;
case CHAN_TYPE_CC_TC :
frame - > index = NMT_MESSAGE_1b ;
break ;
case CHAN_TYPE_TEST :
frame - > index = NMT_MESSAGE_30 ;
break ;
}
frame - > channel_no = nmt_encode_channel ( nmt - > sender . kanal , nmt - > sysinfo . ms_power ) ;
frame - > traffic_area = nmt - > sysinfo . traffic_area ;
frame - > additional_info = nmt_encode_area_no ( nmt - > sysinfo . area_no ) ;
}
static void rx_idle ( nmt_t * nmt , frame_t * frame )
{
switch ( frame - > index ) {
case NMT_MESSAGE_11a : /* roaming update and seizure */
if ( ! match_channel ( nmt , frame ) )
break ;
if ( ! match_area ( nmt , frame ) )
break ;
/* set subscriber */
nmt_value2digits ( frame - > ms_country , & nmt - > subscriber . country , 1 ) ;
nmt_value2digits ( frame - > ms_number , nmt - > subscriber . number , 6 ) ;
nmt - > subscriber . number [ 6 ] = ' \0 ' ;
PDEBUG ( DNMT , DEBUG_INFO , " Received roaming seizure from subscriber %c,%s \n " , nmt - > subscriber . country , nmt - > subscriber . number ) ;
/* change state */
nmt_new_state ( nmt , STATE_ROAMING_IDENT ) ;
nmt - > rx_frame_count = 0 ;
nmt - > tx_frame_count = 0 ;
break ;
case NMT_MESSAGE_10b : /* seizure from ordinary MS */
case NMT_MESSAGE_12 : /* seizure from coinbox MS */
if ( ! match_channel ( nmt , frame ) )
break ;
if ( ! match_area ( nmt , frame ) )
break ;
/* set subscriber */
nmt_value2digits ( frame - > ms_country , & nmt - > subscriber . country , 1 ) ;
nmt_value2digits ( frame - > ms_number , nmt - > subscriber . number , 6 ) ;
if ( frame - > index = = NMT_MESSAGE_12 )
nmt - > subscriber . coinbox = 1 ;
nmt - > subscriber . number [ 6 ] = ' \0 ' ;
PDEBUG ( DNMT , DEBUG_INFO , " Received call from subscriber %c,%s%s \n " , nmt - > subscriber . country , nmt - > subscriber . number , ( nmt - > subscriber . coinbox ) ? " (coinbox) " : " " ) ;
/* change state */
nmt_new_state ( nmt , STATE_MO_IDENT ) ;
nmt - > rx_frame_count = 0 ;
nmt - > tx_frame_count = 0 ;
break ;
/* signals after release */
case NMT_MESSAGE_13a : /* line signal */
break ;
default :
PDEBUG ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > index ) , nmt_state_name ( nmt - > state ) ) ;
}
}
/*
* handle roaming
*/
static void tx_ident ( nmt_t * nmt , frame_t * frame )
{
frame - > index = NMT_MESSAGE_3b ;
frame - > channel_no = nmt_encode_channel ( nmt - > sender . kanal , nmt - > sysinfo . ms_power ) ;
frame - > traffic_area = nmt - > sysinfo . traffic_area ;
frame - > ms_country = nmt_digits2value ( & nmt - > subscriber . country , 1 ) ;
frame - > ms_number = nmt_digits2value ( nmt - > subscriber . number , 6 ) ;
frame - > additional_info = nmt_encode_area_no ( nmt - > sysinfo . area_no ) ;
}
static void set_line_signal ( nmt_t * nmt , frame_t * frame , uint8_t signal )
{
frame - > index = NMT_MESSAGE_5a ;
frame - > channel_no = nmt_encode_channel ( nmt - > sender . kanal , nmt - > sysinfo . ms_power ) ;
frame - > traffic_area = nmt - > sysinfo . traffic_area ;
frame - > ms_country = nmt_digits2value ( & nmt - > subscriber . country , 1 ) ;
frame - > ms_number = nmt_digits2value ( nmt - > subscriber . number , 6 ) ;
frame - > line_signal = ( signal < < 8 ) | ( signal < < 4 ) | signal ;
}
static void tx_roaming_ident ( nmt_t * nmt , frame_t * frame )
{
if ( + + nmt - > tx_frame_count = = 1 )
PDEBUG ( DNMT , DEBUG_INFO , " Sending identity request. \n " ) ;
if ( nmt - > tx_frame_count = = 8 ) {
nmt_release ( nmt ) ;
}
tx_ident ( nmt , frame ) ;
}
static void rx_roaming_ident ( nmt_t * nmt , frame_t * frame )
{
switch ( frame - > index ) {
case NMT_MESSAGE_11a : /* roaming update */
if ( ! match_channel ( nmt , frame ) )
break ;
if ( ! match_area ( nmt , frame ) )
break ;
if ( ! match_subscriber ( nmt , frame ) )
break ;
if ( nmt - > rx_frame_count < 2 ) {
PDEBUG ( DNMT , DEBUG_DEBUG , " Skipping second seizure frame \n " ) ;
break ;
}
nmt_value2digits ( frame - > ms_password , nmt - > subscriber . password , 3 ) ;
PDEBUG ( DNMT , DEBUG_INFO , " Received identity confirm (password %s). \n " , nmt - > subscriber . password ) ;
nmt_new_state ( nmt , STATE_ROAMING_CONFIRM ) ;
nmt - > tx_frame_count = 0 ;
break ;
default :
PDEBUG ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > index ) , nmt_state_name ( nmt - > state ) ) ;
}
}
static void tx_roaming_confirm ( nmt_t * nmt , frame_t * frame )
{
set_line_signal ( nmt , frame , 3 ) ;
if ( + + nmt - > tx_frame_count = = 1 )
PDEBUG ( DNMT , DEBUG_INFO , " Send 'ready to receive'. \n " ) ;
if ( nmt - > tx_frame_count = = 2 )
nmt_release ( nmt ) ; /* continue with this frame, then release */
}
static void rx_roaming_confirm ( nmt_t * nmt , frame_t * frame )
{
switch ( frame - > index ) {
default :
PDEBUG ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > index ) , nmt_state_name ( nmt - > state ) ) ;
}
}
/*
* handle call MS - > MTX
*/
static void tx_mo_ident ( nmt_t * nmt , frame_t * frame )
{
if ( + + nmt - > tx_frame_count = = 1 )
PDEBUG ( DNMT , DEBUG_INFO , " Sending identity request. \n " ) ;
if ( nmt - > tx_frame_count = = 8 ) {
nmt_release ( nmt ) ;
}
tx_ident ( nmt , frame ) ;
}
static void rx_mo_ident ( nmt_t * nmt , frame_t * frame )
{
switch ( frame - > index ) {
case NMT_MESSAGE_10b : /* seizure */
case NMT_MESSAGE_12 : /* seizure */
if ( ! match_channel ( nmt , frame ) )
break ;
if ( ! match_subscriber ( nmt , frame ) )
break ;
if ( nmt - > rx_frame_count < 2 ) {
PDEBUG ( DNMT , DEBUG_DEBUG , " Skipping second seizure frame \n " ) ;
break ;
}
nmt_value2digits ( frame - > ms_password , nmt - > subscriber . password , 3 ) ;
PDEBUG ( DNMT , DEBUG_INFO , " Received identity confirm (password %s). \n " , nmt - > subscriber . password ) ;
nmt_new_state ( nmt , STATE_MO_CONFIRM ) ;
nmt - > tx_frame_count = 0 ;
break ;
default :
PDEBUG ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > index ) , nmt_state_name ( nmt - > state ) ) ;
}
}
static void tx_mo_confirm ( nmt_t * nmt , frame_t * frame )
{
set_line_signal ( nmt , frame , 3 ) ;
if ( + + nmt - > tx_frame_count < = 2 ) {
if ( nmt - > tx_frame_count = = 1 )
PDEBUG ( DNMT , DEBUG_INFO , " Send 'ready to receive'. \n " ) ;
} else {
if ( nmt - > tx_frame_count = = 3 ) {
PDEBUG ( DNMT , DEBUG_INFO , " Send dial tone. \n " ) ;
nmt_new_state ( nmt , STATE_MO_DIALING ) ;
nmt_set_dsp_mode ( nmt , DSP_MODE_DIALTONE ) ;
timer_start ( & nmt - > timer , DIALING_TO ) ;
}
}
}
static void rx_mo_dialing ( nmt_t * nmt , frame_t * frame )
{
int len = strlen ( nmt - > dialing ) ;
switch ( frame - > index ) {
case NMT_MESSAGE_14a : /* digits */
if ( ! match_channel ( nmt , frame ) )
break ;
if ( ! match_area ( nmt , frame ) )
break ;
timer_start ( & nmt - > timer , DIALING_TO ) ;
/* max digits received */
if ( len + 1 = = sizeof ( nmt - > dialing ) )
break ;
/* received odd digit, but be already have odd number of digits */
if ( ( len & 1 ) ) {
if ( nmt - > rx_frame_count > 1 )
goto missing_digit ;
break ;
} else if ( len ) {
if ( nmt - > rx_frame_count > 3 )
goto missing_digit ;
}
nmt - > dialing [ len ] = nmt_value2digit ( frame - > digit ) ;
nmt - > dialing [ len + 1 ] = ' \0 ' ;
PDEBUG ( DNMT , DEBUG_INFO , " Received (odd) digit %c. \n " , nmt - > dialing [ len ] ) ;
nmt - > rx_frame_count = 0 ;
/* finish dial tone after first digit */
if ( ! len )
nmt_set_dsp_mode ( nmt , DSP_MODE_AUDIO ) ;
break ;
case NMT_MESSAGE_14b : /* digits */
if ( ! match_channel ( nmt , frame ) )
break ;
if ( ! match_area ( nmt , frame ) )
break ;
timer_start ( & nmt - > timer , DIALING_TO ) ;
/* max digits received */
if ( len + 1 = = sizeof ( nmt - > dialing ) )
break ;
/* received odd digit, but be already have odd number of digits */
if ( ! ( len & 1 ) ) {
if ( len & & nmt - > rx_frame_count > 1 )
goto missing_digit ;
break ;
} else {
if ( nmt - > rx_frame_count > 3 )
goto missing_digit ;
}
nmt - > dialing [ len ] = nmt_value2digit ( frame - > digit ) ;
nmt - > dialing [ len + 1 ] = ' \0 ' ;
PDEBUG ( DNMT , DEBUG_INFO , " Received (even) digit %c. \n " , nmt - > dialing [ len ] ) ;
nmt - > rx_frame_count = 0 ;
break ;
case NMT_MESSAGE_15 : /* idle */
if ( ! len )
break ;
PDEBUG ( DNMT , DEBUG_INFO , " Dialing complete %s->%s, call established. \n " , & nmt - > subscriber . country , nmt - > dialing ) ;
/* setup call */
{
int callref = + + new_callref ;
int rc ;
PDEBUG ( DNMT , DEBUG_INFO , " Setup call to network. \n " ) ;
rc = call_in_setup ( callref , & nmt - > subscriber . country , nmt - > dialing ) ;
if ( rc < 0 ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Call rejected (cause %d), releasing. \n " , rc ) ;
nmt_release ( nmt ) ;
return ;
}
nmt - > sender . callref = callref ;
}
nmt_new_state ( nmt , STATE_MO_COMPLETE ) ;
nmt_set_dsp_mode ( nmt , DSP_MODE_FRAME ) ;
nmt - > tx_frame_count = 0 ;
break ;
default :
PDEBUG ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > index ) , nmt_state_name ( nmt - > state ) ) ;
}
return ;
missing_digit :
PDEBUG ( DNMT , DEBUG_NOTICE , " Missing digit, aborting. \n " ) ;
nmt_release ( nmt ) ;
}
static void tx_mo_complete ( nmt_t * nmt , frame_t * frame )
{
if ( + + nmt - > tx_frame_count < = 4 ) {
set_line_signal ( nmt , frame , 6 ) ;
if ( nmt - > tx_frame_count = = 1 )
PDEBUG ( DNMT , DEBUG_INFO , " Send 'addess complete'. \n " ) ;
} else {
if ( nmt - > compander ) {
set_line_signal ( nmt , frame , 5 ) ;
if ( nmt - > tx_frame_count = = 5 )
PDEBUG ( DNMT , DEBUG_INFO , " Send 'compander in'. \n " ) ;
} else
frame - > index = NMT_MESSAGE_6 ;
if ( nmt - > tx_frame_count = = 9 ) {
PDEBUG ( DNMT , DEBUG_INFO , " Connect audio. \n " ) ;
nmt_new_state ( nmt , STATE_ACTIVE ) ;
nmt - > active_state = ACTIVE_STATE_VOICE ;
nmt_set_dsp_mode ( nmt , DSP_MODE_AUDIO ) ;
if ( nmt - > supervisory ) {
super_reset ( nmt ) ;
timer_start ( & nmt - > timer , SUPERVISORY_TO1 ) ;
}
}
}
}
static void timeout_mo_dialing ( nmt_t * nmt )
{
PDEBUG ( DNMT , DEBUG_NOTICE , " Timeout while receiving digits. \n " ) ;
nmt_release ( nmt ) ;
PDEBUG ( DNMT , DEBUG_INFO , " Release call towards network. \n " ) ;
call_in_release ( nmt - > sender . callref , CAUSE_TEMPFAIL ) ;
nmt - > sender . callref = 0 ;
}
/*
* handle call MTX - > MS
*/
static void tx_mt_paging ( nmt_t * nmt , frame_t * frame )
{
frame - > index = NMT_MESSAGE_2a ;
frame - > channel_no = nmt_encode_channel ( nmt - > sender . kanal , nmt - > sysinfo . ms_power ) ;
frame - > traffic_area = nmt - > sysinfo . traffic_area ;
frame - > ms_country = nmt_digits2value ( & nmt - > subscriber . country , 1 ) ;
frame - > ms_number = nmt_digits2value ( nmt - > subscriber . number , 6 ) ;
frame - > additional_info = nmt_encode_area_no ( nmt - > sysinfo . area_no ) ;
if ( + + nmt - > tx_frame_count = = 1 ) {
PDEBUG ( DNMT , DEBUG_INFO , " Send call to mobile. \n " ) ;
} else
tx_idle ( nmt , frame ) ;
/* wait some time to get answer. use more than one frame due to delay of audio processing */
if ( nmt - > tx_frame_count = = 5 ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " No answer from mobile phone (try %d). \n " , nmt - > page_try ) ;
if ( nmt - > page_try = = PAGE_TRIES ) {
PDEBUG ( DNMT , DEBUG_INFO , " Release call towards network. \n " ) ;
call_in_release ( nmt - > sender . callref , CAUSE_OUTOFORDER ) ;
nmt - > sender . callref = 0 ;
nmt_go_idle ( nmt ) ;
return ;
}
nmt_page ( nmt , nmt - > subscriber . country , nmt - > subscriber . number , nmt - > page_try + 1 ) ;
}
}
static void rx_mt_paging ( nmt_t * nmt , frame_t * frame )
{
switch ( frame - > index ) {
case NMT_MESSAGE_10a : /* call acknowledgement */
if ( ! match_channel ( nmt , frame ) )
break ;
if ( ! match_subscriber ( nmt , frame ) )
break ;
PDEBUG ( DNMT , DEBUG_INFO , " Received call acknowledgement. \n " ) ;
nmt_new_state ( nmt , STATE_MT_CHANNEL ) ;
nmt - > tx_frame_count = 0 ;
break ;
default :
PDEBUG ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > index ) , nmt_state_name ( nmt - > state ) ) ;
}
}
static void tx_mt_channel ( nmt_t * nmt , frame_t * frame )
{
frame - > index = NMT_MESSAGE_2b ;
frame - > channel_no = nmt_encode_channel ( nmt - > sender . kanal , nmt - > sysinfo . ms_power ) ;
frame - > traffic_area = nmt - > sysinfo . traffic_area ;
frame - > ms_country = nmt_digits2value ( & nmt - > subscriber . country , 1 ) ;
frame - > ms_number = nmt_digits2value ( nmt - > subscriber . number , 6 ) ;
frame - > tc_no = nmt_encode_channel ( nmt - > sender . kanal , nmt - > sysinfo . ms_power ) ;
PDEBUG ( DNMT , DEBUG_INFO , " Send channel activation to mobile. \n " ) ;
nmt_new_state ( nmt , STATE_MT_IDENT ) ;
}
static void tx_mt_ident ( nmt_t * nmt , frame_t * frame )
{
if ( + + nmt - > tx_frame_count = = 1 )
PDEBUG ( DNMT , DEBUG_INFO , " Sending identity request. \n " ) ;
if ( nmt - > tx_frame_count = = 8 ) {
PDEBUG ( DNMT , DEBUG_INFO , " Release call towards network. \n " ) ;
call_in_release ( nmt - > sender . callref , CAUSE_TEMPFAIL ) ;
nmt - > sender . callref = 0 ;
nmt_go_idle ( nmt ) ;
}
tx_ident ( nmt , frame ) ;
}
static void rx_mt_ident ( nmt_t * nmt , frame_t * frame )
{
switch ( frame - > index ) {
case NMT_MESSAGE_10b : /* seizure */
if ( ! match_subscriber ( nmt , frame ) )
break ;
nmt_value2digits ( frame - > ms_password , nmt - > subscriber . password , 3 ) ;
PDEBUG ( DNMT , DEBUG_INFO , " Received identity (password %s). \n " , nmt - > subscriber . password ) ;
nmt_new_state ( nmt , STATE_MT_RINGING ) ;
nmt - > tx_frame_count = 0 ;
timer_start ( & nmt - > timer , RINGING_TO ) ;
call_in_alerting ( nmt - > sender . callref ) ;
break ;
default :
PDEBUG ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > index ) , nmt_state_name ( nmt - > state ) ) ;
}
}
static void tx_mt_ringing ( nmt_t * nmt , frame_t * frame )
{
set_line_signal ( nmt , frame , 9 ) ;
if ( + + nmt - > tx_frame_count = = 1 )
PDEBUG ( DNMT , DEBUG_INFO , " Send 'ringing order'. \n " ) ;
if ( nmt - > tx_frame_count > = 4 )
frame - > index = NMT_MESSAGE_6 ;
/* repeat ringing after 5 seconds */
if ( nmt - > tx_frame_count = = 36 )
nmt - > tx_frame_count = 0 ;
}
static void rx_mt_ringing ( nmt_t * nmt , frame_t * frame )
{
switch ( frame - > index ) {
case NMT_MESSAGE_13a : /* line signal */
if ( ! match_subscriber ( nmt , frame ) )
break ;
if ( ( frame - > line_signal & 0xf ) ! = 14 )
break ;
PDEBUG ( DNMT , DEBUG_INFO , " Received 'answer' from phone. \n " ) ;
nmt_new_state ( nmt , STATE_MT_COMPLETE ) ;
nmt - > tx_frame_count = 0 ;
call_in_answer ( nmt - > sender . callref , & nmt - > subscriber . country ) ;
break ;
default :
PDEBUG ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > index ) , nmt_state_name ( nmt - > state ) ) ;
}
}
static void tx_mt_complete ( nmt_t * nmt , frame_t * frame )
{
set_line_signal ( nmt , frame , 5 ) ;
+ + nmt - > tx_frame_count ;
if ( nmt - > compander ) {
if ( nmt - > tx_frame_count = = 1 )
PDEBUG ( DNMT , DEBUG_INFO , " Send 'compander in'. \n " ) ;
} else
frame - > index = NMT_MESSAGE_6 ;
if ( nmt - > tx_frame_count = = 5 ) {
PDEBUG ( DNMT , DEBUG_INFO , " Connect audio. \n " ) ;
nmt_new_state ( nmt , STATE_ACTIVE ) ;
nmt - > active_state = ACTIVE_STATE_VOICE ;
nmt_set_dsp_mode ( nmt , DSP_MODE_AUDIO ) ;
if ( nmt - > supervisory ) {
super_reset ( nmt ) ;
timer_start ( & nmt - > timer , SUPERVISORY_TO1 ) ;
}
}
}
static void timeout_mt_ringing ( nmt_t * nmt )
{
PDEBUG ( DNMT , DEBUG_NOTICE , " Timeout while waiting for answer of the phone. \n " ) ;
nmt_release ( nmt ) ;
PDEBUG ( DNMT , DEBUG_INFO , " Release call towards network. \n " ) ;
call_in_release ( nmt - > sender . callref , CAUSE_NOANSWER ) ;
nmt - > sender . callref = 0 ;
}
/*
* handle clearing towards MTX
*/
static void tx_mo_release ( nmt_t * nmt , frame_t * frame )
{
set_line_signal ( nmt , frame , 15 ) ;
if ( + + nmt - > tx_frame_count = = 1 )
PDEBUG ( DNMT , DEBUG_INFO , " Send release. \n " ) ;
if ( nmt - > tx_frame_count = = 4 )
nmt_go_idle ( nmt ) ; /* continue with this frame, then go idle */
}
/*
* handle clearing towards MS
*/
static void tx_mt_release ( nmt_t * nmt , frame_t * frame )
{
set_line_signal ( nmt , frame , 15 ) ;
if ( + + nmt - > tx_frame_count = = 1 )
PDEBUG ( DNMT , DEBUG_INFO , " Send release. \n " ) ;
}
static void rx_mt_release ( nmt_t * nmt , frame_t * frame )
{
switch ( frame - > index ) {
case NMT_MESSAGE_13a : /* line signal */
if ( ! match_channel ( nmt , frame ) )
break ;
if ( ! match_subscriber ( nmt , frame ) )
break ;
if ( ( frame - > line_signal & 0xf ) ! = 1 )
break ;
PDEBUG ( DNMT , DEBUG_INFO , " Received release guard. \n " ) ;
nmt_go_idle ( nmt ) ;
break ;
default :
PDEBUG ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > index ) , nmt_state_name ( nmt - > state ) ) ;
}
}
static void timeout_mt_release ( nmt_t * nmt )
{
PDEBUG ( DNMT , DEBUG_NOTICE , " Timeout while releasing. \n " ) ;
nmt_go_idle ( nmt ) ;
}
/*
* handle call
*/
void nmt_rx_super ( nmt_t * nmt , int tone , double quality )
{
if ( tone )
PDEBUG ( DNMT , DEBUG_INFO , " Detected supervisory signal with quality=%.0f. \n " , quality * 100.0 ) ;
else
PDEBUG ( DNMT , DEBUG_INFO , " Lost supervisory signal \n " ) ;
if ( nmt - > sender . loopback )
return ;
/* only detect supervisory signal during active call */
if ( nmt - > state ! = STATE_ACTIVE | | ! nmt - > supervisory )
return ;
if ( tone )
timer_stop ( & nmt - > timer ) ;
else
timer_start ( & nmt - > timer , SUPERVISORY_TO2 ) ;
}
static void timeout_active ( nmt_t * nmt , double duration )
{
if ( duration = = SUPERVISORY_TO1 )
PDEBUG ( DNMT , DEBUG_NOTICE , " Timeout after %.0f seconds not receiving supervisory signal. \n " , duration ) ;
else
PDEBUG ( DNMT , DEBUG_NOTICE , " Timeout after %.0f seconds loosing supervisory signal. \n " , duration ) ;
nmt_release ( nmt ) ;
PDEBUG ( DNMT , DEBUG_INFO , " Release call towards network. \n " ) ;
call_in_release ( nmt - > sender . callref , CAUSE_TEMPFAIL ) ;
nmt - > sender . callref = 0 ;
}
static void rx_active ( nmt_t * nmt , frame_t * frame )
{
char digit ;
/* restart timer on every reception of frame */
if ( nmt - > supervisory )
timer_start ( & nmt - > timer , SUPERVISORY_TO2 ) ;
switch ( frame - > index ) {
case NMT_MESSAGE_13a : /* line signal */
if ( ! match_channel ( nmt , frame ) )
break ;
if ( ! match_subscriber ( nmt , frame ) )
break ;
switch ( ( frame - > line_signal & 0xf ) ) {
case 5 :
PDEBUG ( DNMT , DEBUG_NOTICE , " Register Recall is not supported. \n " ) ;
break ;
case 8 :
if ( nmt - > active_state ! = ACTIVE_STATE_VOICE )
break ;
PDEBUG ( DNMT , DEBUG_INFO , " Received 'MFT in' request. \n " ) ;
nmt - > active_state = ACTIVE_STATE_MFT_IN ;
nmt - > tx_frame_count = 0 ;
nmt_set_dsp_mode ( nmt , DSP_MODE_FRAME ) ;
nmt - > mft_num = 0 ;
break ;
case 7 :
if ( nmt - > active_state ! = ACTIVE_STATE_MFT )
break ;
PDEBUG ( DNMT , DEBUG_INFO , " Received 'MFT out' request. \n " ) ;
nmt - > active_state = ACTIVE_STATE_MFT_OUT ;
nmt - > tx_frame_count = 0 ;
nmt_set_dsp_mode ( nmt , DSP_MODE_FRAME ) ;
break ;
}
break ;
case NMT_MESSAGE_14a : /* digits */
if ( ! match_channel ( nmt , frame ) )
break ;
if ( ! match_area ( nmt , frame ) )
break ;
if ( nmt - > active_state ! = ACTIVE_STATE_MFT )
break ;
if ( ( nmt - > mft_num & 1 ) )
break ;
digit = nmt_value2digit ( frame - > digit ) ;
dtmf_set_tone ( & nmt - > dtmf , digit ) ;
PDEBUG ( DNMT , DEBUG_INFO , " Received (odd) digit %c. \n " , digit ) ;
nmt - > mft_num + + ;
break ;
case NMT_MESSAGE_14b : /* digits */
if ( ! match_channel ( nmt , frame ) )
break ;
if ( ! match_area ( nmt , frame ) )
break ;
if ( nmt - > active_state ! = ACTIVE_STATE_MFT )
break ;
if ( ! ( nmt - > mft_num & 1 ) )
break ;
digit = nmt_value2digit ( frame - > digit ) ;
dtmf_set_tone ( & nmt - > dtmf , digit ) ;
PDEBUG ( DNMT , DEBUG_INFO , " Received (even) digit %c. \n " , digit ) ;
nmt - > mft_num + + ;
break ;
default :
PDEBUG ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > index ) , nmt_state_name ( nmt - > state ) ) ;
}
}
static void tx_active ( nmt_t * nmt , frame_t * frame )
{
switch ( nmt - > active_state ) {
case ACTIVE_STATE_MFT_IN :
set_line_signal ( nmt , frame , 4 ) ;
if ( + + nmt - > tx_frame_count = = 1 )
PDEBUG ( DNMT , DEBUG_INFO , " Send 'MFT in acknowledge'. \n " ) ;
if ( nmt - > tx_frame_count > 4 ) {
nmt - > active_state = ACTIVE_STATE_MFT ;
nmt_set_dsp_mode ( nmt , DSP_MODE_DTMF ) ;
}
break ;
case ACTIVE_STATE_MFT_OUT :
set_line_signal ( nmt , frame , 10 ) ;
if ( + + nmt - > tx_frame_count = = 1 )
PDEBUG ( DNMT , DEBUG_INFO , " Send 'MFT out acknowledge'. \n " ) ;
if ( nmt - > tx_frame_count > 4 ) {
nmt - > active_state = ACTIVE_STATE_VOICE ;
nmt_set_dsp_mode ( nmt , DSP_MODE_AUDIO ) ;
if ( nmt - > supervisory )
super_reset ( nmt ) ;
}
break ;
default :
;
}
}
/*
* general handlers to call sub handling
*/
void nmt_receive_frame ( nmt_t * nmt , const char * bits , double quality , double level , double frames_elapsed )
{
frame_t frame ;
int rc ;
rc = decode_frame ( & frame , bits , ( nmt - > sender . loopback ) ? MTX_TO_XX : XX_TO_MTX , ( nmt - > state = = STATE_MT_PAGING ) ) ;
if ( rc < 0 ) {
2016-04-23 12:33:54 +00:00
PDEBUG ( DFRAME , DEBUG_INFO , " RX Level: %.0f%% Quality=%.0f \n " , level * 100.0 , quality * 100.0 ) ;
PDEBUG ( DNMT , ( nmt - > sender . loopback ) ? DEBUG_NOTICE : DEBUG_DEBUG , " Received invalid frame. \n " ) ;
2016-02-21 17:13:30 +00:00
return ;
}
/* frame counter */
nmt - > rx_frame_count + = ( int ) ( frames_elapsed + 0.5 ) ;
2016-04-23 12:33:54 +00:00
PDEBUG ( DFRAME , DEBUG_INFO , " RX Level: %.0f%% Quality=%.0f \n " , level * 100.0 , quality * 100.0 ) ;
PDEBUG ( DNMT , ( nmt - > sender . loopback ) ? DEBUG_NOTICE : DEBUG_DEBUG , " Received frame %s \n " , nmt_frame_name ( frame . index ) ) ;
2016-02-21 17:13:30 +00:00
if ( nmt - > sender . loopback )
return ;
/* MS releases, but this is not the acknowledge of MTX release */
if ( frame . index = = NMT_MESSAGE_13a
& & ( frame . line_signal & 0xf ) = = 1
& & nmt - > state ! = STATE_MO_RELEASE
& & nmt - > state ! = STATE_MT_RELEASE ) {
/* drop packets after release */
if ( nmt - > state = = STATE_IDLE )
return ;
if ( ! match_subscriber ( nmt , & frame ) )
return ;
PDEBUG ( DNMT , DEBUG_INFO , " Received clearing by mobile phone in state %s. \n " , nmt_state_name ( nmt - > state ) ) ;
nmt_new_state ( nmt , STATE_MO_RELEASE ) ;
nmt - > tx_frame_count = 0 ;
nmt_set_dsp_mode ( nmt , DSP_MODE_FRAME ) ;
if ( nmt - > sender . callref ) {
PDEBUG ( DNMT , DEBUG_INFO , " Release call towards network. \n " ) ;
call_in_release ( nmt - > sender . callref , CAUSE_NORMAL ) ;
nmt - > sender . callref = 0 ;
}
return ;
}
switch ( nmt - > state ) {
case STATE_IDLE :
rx_idle ( nmt , & frame ) ;
break ;
case STATE_ROAMING_IDENT :
rx_roaming_ident ( nmt , & frame ) ;
break ;
case STATE_ROAMING_CONFIRM :
rx_roaming_confirm ( nmt , & frame ) ;
break ;
case STATE_MO_IDENT :
rx_mo_ident ( nmt , & frame ) ;
break ;
case STATE_MO_CONFIRM :
case STATE_MO_DIALING :
rx_mo_dialing ( nmt , & frame ) ;
break ;
case STATE_MO_RELEASE :
break ;
case STATE_MT_PAGING :
rx_mt_paging ( nmt , & frame ) ;
break ;
case STATE_MT_IDENT :
rx_mt_ident ( nmt , & frame ) ;
break ;
case STATE_MT_RINGING :
rx_mt_ringing ( nmt , & frame ) ;
break ;
case STATE_MT_RELEASE :
rx_mt_release ( nmt , & frame ) ;
break ;
case STATE_ACTIVE :
rx_active ( nmt , & frame ) ;
break ;
default :
PDEBUG ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame . index ) , nmt_state_name ( nmt - > state ) ) ;
}
}
/* Timeout handling */
static void nmt_timeout ( struct timer * timer )
{
nmt_t * nmt = ( nmt_t * ) timer - > priv ;
switch ( nmt - > state ) {
case STATE_MO_DIALING :
timeout_mo_dialing ( nmt ) ;
break ;
case STATE_MT_RINGING :
timeout_mt_ringing ( nmt ) ;
break ;
case STATE_MT_RELEASE :
timeout_mt_release ( nmt ) ;
break ;
case STATE_ACTIVE :
timeout_active ( nmt , timer - > duration ) ;
break ;
default :
break ;
}
}
/* FSK processing requests next frame after transmission of previous
frame has been finished . */
const char * nmt_get_frame ( nmt_t * nmt )
{
frame_t frame ;
const char * bits ;
int debug = 1 ;
memset ( & frame , 0 , sizeof ( frame ) ) ;
switch ( nmt - > state ) {
case STATE_IDLE :
tx_idle ( nmt , & frame ) ;
break ;
case STATE_ROAMING_IDENT :
tx_roaming_ident ( nmt , & frame ) ;
break ;
case STATE_ROAMING_CONFIRM :
tx_roaming_confirm ( nmt , & frame ) ;
break ;
case STATE_MO_IDENT :
tx_mo_ident ( nmt , & frame ) ;
break ;
case STATE_MO_CONFIRM :
tx_mo_confirm ( nmt , & frame ) ;
break ;
case STATE_MO_COMPLETE :
tx_mo_complete ( nmt , & frame ) ;
break ;
case STATE_MO_RELEASE :
tx_mo_release ( nmt , & frame ) ;
break ;
case STATE_MT_PAGING :
tx_mt_paging ( nmt , & frame ) ;
break ;
case STATE_MT_CHANNEL :
tx_mt_channel ( nmt , & frame ) ;
break ;
case STATE_MT_IDENT :
tx_mt_ident ( nmt , & frame ) ;
break ;
case STATE_MT_RINGING :
tx_mt_ringing ( nmt , & frame ) ;
break ;
case STATE_MT_COMPLETE :
tx_mt_complete ( nmt , & frame ) ;
break ;
case STATE_MT_RELEASE :
tx_mt_release ( nmt , & frame ) ;
break ;
case STATE_ACTIVE :
tx_active ( nmt , & frame ) ;
break ;
default :
break ;
}
/* no encoding debug for certain (idle) frames */
switch ( frame . index ) {
case NMT_MESSAGE_1a :
case NMT_MESSAGE_4 :
case NMT_MESSAGE_1b :
case NMT_MESSAGE_30 :
debug = 0 ;
break ;
}
/* frame sending aborted (e.g. due to audio) */
if ( nmt - > dsp_mode ! = DSP_MODE_FRAME )
return NULL ;
bits = encode_frame ( & frame , debug ) ;
PDEBUG ( DNMT , DEBUG_DEBUG , " Sending frame %s. \n " , nmt_frame_name ( frame . index ) ) ;
return bits ;
}
/*
* call states received from call control
*/
/* Call control starts call towards mobile station. */
int call_out_setup ( int callref , char * dialing )
{
sender_t * sender ;
nmt_t * nmt ;
int i ;
char ms_country ;
char ms_number [ 7 ] = " 000000 " ;
/* 1. check if number is invalid, return INVALNUMBER */
if ( dialstring2number ( dialing , & ms_country , ms_number ) ) {
inval :
PDEBUG ( DNMT , DEBUG_NOTICE , " Outgoing call to invalid number '%s', rejecting! \n " , dialing ) ;
return - CAUSE_INVALNUMBER ;
}
for ( i = 0 ; i < 6 ; i + + ) {
if ( ms_number [ i ] < ' 0 ' | | ms_number [ i ] > ' 9 ' )
goto inval ;
}
/* 2. check if given number is already in a call, return BUSY */
for ( sender = sender_head ; sender ; sender = sender - > next ) {
nmt = ( nmt_t * ) sender ;
if ( nmt - > subscriber . country = = ms_country & & ! strcmp ( nmt - > subscriber . number , ms_number ) )
break ;
}
if ( sender ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Outgoing call to busy number, rejecting! \n " ) ;
return - CAUSE_BUSY ;
}
/* 3. check if all senders are busy, return NOCHANNEL */
for ( sender = sender_head ; sender ; sender = sender - > next ) {
nmt = ( nmt_t * ) sender ;
if ( nmt - > sysinfo . chan_type ! = CHAN_TYPE_CC
& & nmt - > sysinfo . chan_type ! = CHAN_TYPE_CC_TC )
continue ;
if ( nmt - > state = = STATE_IDLE )
break ;
}
if ( ! sender ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Outgoing call, but no free calling channel, rejecting! \n " ) ;
return - CAUSE_NOCHANNEL ;
}
PDEBUG ( DNMT , DEBUG_INFO , " Call to mobile station, paging station id '%c%s' \n " , ms_country , ms_number ) ;
/* 4. trying to page mobile station */
sender - > callref = callref ;
nmt_page ( nmt , ms_country , ms_number , 1 ) ;
return 0 ;
}
/* Call control sends disconnect (with tones).
* An active call stays active , so tones and annoucements can be received
* by mobile station .
*/
void call_out_disconnect ( int callref , int cause )
{
sender_t * sender ;
nmt_t * nmt ;
PDEBUG ( DNMT , DEBUG_INFO , " Call has been disconnected by network. \n " ) ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
nmt = ( nmt_t * ) sender ;
if ( sender - > callref = = callref )
break ;
}
if ( ! sender ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Outgoing disconnect, but no callref! \n " ) ;
call_in_release ( callref , CAUSE_INVALCALLREF ) ;
return ;
}
/* Release when not active */
if ( nmt - > state = = STATE_ACTIVE )
return ;
switch ( nmt - > state ) {
case STATE_MT_RINGING :
PDEBUG ( DNMT , DEBUG_NOTICE , " Outgoing disconnect, during ringing, releasing! \n " ) ;
nmt_release ( nmt ) ;
break ;
default :
PDEBUG ( DNMT , DEBUG_NOTICE , " Outgoing disconnect, when phone is in call setup, releasing! \n " ) ;
nmt_release ( nmt ) ;
break ;
}
call_in_release ( callref , cause ) ;
sender - > callref = 0 ;
}
/* Call control releases call toward mobile station. */
void call_out_release ( int callref , int cause )
{
sender_t * sender ;
nmt_t * nmt ;
PDEBUG ( DNMT , DEBUG_INFO , " Call has been released by network, releasing call. \n " ) ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
nmt = ( nmt_t * ) sender ;
if ( sender - > callref = = callref )
break ;
}
if ( ! sender ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Outgoing release, but no callref! \n " ) ;
/* don't send release, because caller already released */
return ;
}
sender - > callref = 0 ;
switch ( nmt - > state ) {
case STATE_ACTIVE :
PDEBUG ( DNMT , DEBUG_NOTICE , " Outgoing release, during active call, releasing! \n " ) ;
nmt_release ( nmt ) ;
break ;
case STATE_MT_RINGING :
PDEBUG ( DNMT , DEBUG_NOTICE , " Outgoing release, during ringing, releasing! \n " ) ;
nmt_release ( nmt ) ;
break ;
default :
PDEBUG ( DNMT , DEBUG_NOTICE , " Outgoing release, when phone is in call setup, releasing! \n " ) ;
nmt_release ( nmt ) ;
break ;
}
}
/* Receive audio from call instance. */
void call_rx_audio ( int callref , int16_t * samples , int count )
{
sender_t * sender ;
nmt_t * nmt ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
nmt = ( nmt_t * ) sender ;
if ( sender - > callref = = callref )
break ;
}
if ( ! sender )
return ;
if ( nmt - > dsp_mode = = DSP_MODE_AUDIO | | nmt - > dsp_mode = = DSP_MODE_DTMF ) {
2016-04-17 13:17:38 +00:00
int16_t up [ ( int ) ( ( double ) count * nmt - > sender . srstate . factor + 0.5 ) + 10 ] ;
2016-02-21 17:13:30 +00:00
if ( nmt - > compander )
compress_audio ( & nmt - > cstate , samples , count ) ;
count = samplerate_upsample ( & nmt - > sender . srstate , samples , count , up ) ;
jitter_save ( & nmt - > sender . audio , up , count ) ;
}
}