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/>.
*/
2016-08-02 09:03:03 +00:00
# define CHAN nmt->sender.kanal
2016-02-21 17:13:30 +00:00
# include <stdio.h>
# include <stdint.h>
# include <stdlib.h>
# include <string.h>
# include <errno.h>
2016-07-04 17:52:00 +00:00
# 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"
2016-02-21 17:13:30 +00:00
# include "nmt.h"
2016-08-17 16:25:48 +00:00
# include "transaction.h"
2016-02-21 17:13:30 +00:00
# include "dsp.h"
# include "frame.h"
2017-06-09 18:18:24 +00:00
# include "countries.h"
2016-02-21 17:13:30 +00:00
2016-08-02 09:03:03 +00:00
/* How does paging on all channels work:
*
2016-08-17 16:25:48 +00:00
* Paging is performed on all free CC channels . The transaction pointer
* is set towards transaction . ( nmt - > trans points to trans )
*
* After timeout , all channels with that transaction pointer are released .
*
* When paging was replied , other channels with the transaction pointer are
* relased , but the channel with the reply is linked to transaction in both
* directions . ( trans - > nmt points to nmt , nmt - > trans points to trans )
2016-08-02 09:03:03 +00:00
*
*/
2016-07-04 17:52:00 +00:00
static int sms_ref = 0 ;
2016-02-21 17:13:30 +00:00
/* Call reference for calls from mobile station to network
This offset of 0x400000000 is required for MNCC interface . */
static int new_callref = 0x40000000 ;
/* Timers */
2016-08-17 16:25:48 +00:00
# define PAGING_TO 1.0 /* wait for paging response: fictive value */
2016-02-21 17:13:30 +00:00
# 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 " ;
2016-07-20 09:55:03 +00:00
case STATE_MT_AUTOANSWER :
return " MT CALL AUTOANSWER " ;
2016-02-21 17:13:30 +00:00
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 ;
}
2017-05-25 16:43:54 +00:00
void nmt_display_status ( void )
{
sender_t * sender ;
nmt_t * nmt ;
display_status_start ( ) ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
nmt = ( nmt_t * ) sender ;
2017-07-22 08:38:18 +00:00
display_status_channel ( nmt - > sender . kanal , chan_type_short_name ( nmt - > sysinfo . system , nmt - > sysinfo . chan_type ) , nmt_state_name ( nmt - > state ) ) ;
2017-05-25 16:43:54 +00:00
if ( nmt - > trans )
display_status_subscriber ( nmt - > trans - > subscriber . number , NULL ) ;
}
display_status_end ( ) ;
}
2016-02-21 17:13:30 +00:00
static void nmt_new_state ( nmt_t * nmt , enum nmt_state new_state )
{
if ( nmt - > state = = new_state )
return ;
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_DEBUG , " State change: %s -> %s \n " , nmt_state_name ( nmt - > state ) , nmt_state_name ( new_state ) ) ;
2016-02-21 17:13:30 +00:00
nmt - > state = new_state ;
2017-05-25 16:43:54 +00:00
nmt_display_status ( ) ;
2016-02-21 17:13:30 +00:00
}
static struct nmt_channels {
2017-07-22 08:38:18 +00:00
int system ;
2016-02-21 17:13:30 +00:00
enum nmt_chan_type chan_type ;
const char * short_name ;
const char * long_name ;
} nmt_channels [ ] = {
2018-12-23 18:44:05 +00:00
{ 0 , CHAN_TYPE_CC , " CC " , " calling channel (calls to mobile) " } ,
{ 900 , CHAN_TYPE_CCA , " CCA " , " calling channel for group A mobiles with odd secret key (calls to mobile) " } ,
{ 900 , CHAN_TYPE_CCB , " CCB " , " calling channel for group B mobiles with even secret key (calls to mobile) " } ,
{ 0 , CHAN_TYPE_TC , " TC " , " traffic channel (calls from mobile) " } ,
{ 900 , CHAN_TYPE_AC_TC , " AC/TC " , " combined access & traffic channel (calls from mobile) " } ,
2017-07-22 08:38:18 +00:00
{ 0 , CHAN_TYPE_CC_TC , " CC/TC " , " combined calling & traffic channel (both way calls) " } ,
{ 0 , CHAN_TYPE_TEST , " TEST " , " test channel " } ,
{ 0 , 0 , NULL , NULL }
2016-02-21 17:13:30 +00:00
} ;
2017-07-22 08:38:18 +00:00
void nmt_channel_list ( int nmt_system )
2016-02-21 17:13:30 +00:00
{
int i ;
printf ( " Type \t Description \n " ) ;
printf ( " ------------------------------------------------------------------------ \n " ) ;
2017-07-22 08:38:18 +00:00
for ( i = 0 ; nmt_channels [ i ] . long_name ; i + + ) {
if ( nmt_channels [ i ] . system ! = 0 & & nmt_channels [ i ] . system ! = nmt_system )
continue ;
2016-02-21 17:13:30 +00:00
printf ( " %s \t %s \n " , nmt_channels [ i ] . short_name , nmt_channels [ i ] . long_name ) ;
2017-07-22 08:38:18 +00:00
}
2016-02-21 17:13:30 +00:00
}
2017-07-22 08:38:18 +00:00
int nmt_channel_by_short_name ( int nmt_system , const char * short_name )
2016-02-21 17:13:30 +00:00
{
int i ;
for ( i = 0 ; nmt_channels [ i ] . short_name ; i + + ) {
2017-07-22 08:38:18 +00:00
if ( nmt_channels [ i ] . system ! = 0 & & nmt_channels [ i ] . system ! = nmt_system )
continue ;
2017-02-05 07:36:51 +00:00
if ( ! strcasecmp ( nmt_channels [ i ] . short_name , short_name ) )
2016-02-21 17:13:30 +00:00
return nmt_channels [ i ] . chan_type ;
}
return - 1 ;
}
2017-07-22 08:38:18 +00:00
const char * chan_type_short_name ( int nmt_system , enum nmt_chan_type chan_type )
2016-02-21 17:13:30 +00:00
{
int i ;
for ( i = 0 ; nmt_channels [ i ] . short_name ; i + + ) {
2017-07-22 08:38:18 +00:00
if ( nmt_channels [ i ] . system ! = 0 & & nmt_channels [ i ] . system ! = nmt_system )
continue ;
2016-02-21 17:13:30 +00:00
if ( nmt_channels [ i ] . chan_type = = chan_type )
return nmt_channels [ i ] . short_name ;
}
return " invalid " ;
}
2017-07-22 08:38:18 +00:00
const char * chan_type_long_name ( int nmt_system , enum nmt_chan_type chan_type )
2016-02-21 17:13:30 +00:00
{
int i ;
for ( i = 0 ; nmt_channels [ i ] . long_name ; i + + ) {
2017-07-22 08:38:18 +00:00
if ( nmt_channels [ i ] . system ! = 0 & & nmt_channels [ i ] . system ! = nmt_system )
continue ;
2016-02-21 17:13:30 +00:00
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 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 ;
}
2017-07-27 10:05:11 +00:00
if ( dialstring [ 0 ] < ' 0 ' | | dialstring [ 0 ] > ' 9 ' ) {
2016-02-21 17:13:30 +00:00
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 ;
}
2017-10-28 17:01:27 +00:00
static inline int is_chan_class_cc ( enum nmt_chan_type chan_type )
{
if ( chan_type = = CHAN_TYPE_CC
| | chan_type = = CHAN_TYPE_CCA
2017-11-11 07:10:37 +00:00
| | chan_type = = CHAN_TYPE_CCB
| | chan_type = = CHAN_TYPE_CC_TC )
2017-10-28 17:01:27 +00:00
return 1 ;
return 0 ;
}
static inline int is_chan_class_tc ( enum nmt_chan_type chan_type )
{
if ( chan_type = = CHAN_TYPE_TC
| | chan_type = = CHAN_TYPE_AC_TC
| | chan_type = = CHAN_TYPE_CC_TC )
return 1 ;
return 0 ;
}
2016-02-21 17:13:30 +00:00
static void nmt_timeout ( struct timer * timer ) ;
/* Create transceiver instance and link to a list. */
2019-07-20 16:11:17 +00:00
int nmt_create ( int nmt_system , const char * country , const char * kanal , enum nmt_chan_type chan_type , const char * audiodev , int use_sdr , int samplerate , double rx_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 , uint8_t ms_power , uint8_t traffic_area , uint8_t area_no , int compandor , int supervisory , const char * smsc_number , int send_callerid , int loopback )
2016-02-21 17:13:30 +00:00
{
nmt_t * nmt ;
int rc ;
2017-06-09 18:18:24 +00:00
double deviation_factor ;
int scandinavia ;
int tested ;
2016-02-21 17:13:30 +00:00
2017-06-09 18:18:24 +00:00
/* check channel matching and set deviation factor */
2019-07-20 16:11:17 +00:00
if ( nmt_channel2freq ( nmt_system , country , atoi ( kanal ) , 0 , & deviation_factor , & scandinavia , & tested ) = = 0.0 ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Channel number %s invalid, use '-Y list' to get a list of available channels. \n " , kanal ) ;
2016-02-21 17:13:30 +00:00
return - EINVAL ;
}
2017-06-09 18:18:24 +00:00
if ( ! tested ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " *** The given NMT country has not been tested yet. Please tell the Author, if it works. \n " ) ;
}
2019-07-20 16:11:17 +00:00
if ( scandinavia & & atoi ( kanal ) > = 201 ) {
2016-02-21 17:13:30 +00:00
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 " ) ;
}
2019-07-20 16:11:17 +00:00
if ( scandinavia & & atoi ( kanal ) > = 181 & & atoi ( kanal ) < = 200 ) {
2016-02-21 17:13:30 +00:00
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_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 ;
}
2019-07-20 16:11:17 +00:00
PDEBUG ( DNMT , DEBUG_DEBUG , " Creating 'NMT' instance for channel = %s (sample rate %d). \n " , kanal , samplerate ) ;
2016-02-21 17:13:30 +00:00
/* init general part of transceiver */
2019-07-20 16:11:17 +00:00
rc = sender_create ( & nmt - > sender , kanal , nmt_channel2freq ( nmt_system , country , atoi ( kanal ) , 0 , NULL , NULL , NULL ) , nmt_channel2freq ( nmt_system , country , atoi ( kanal ) , 1 , NULL , NULL , NULL ) , audiodev , use_sdr , samplerate , rx_gain , pre_emphasis , de_emphasis , write_rx_wave , write_tx_wave , read_rx_wave , read_tx_wave , loopback , PAGING_SIGNAL_NONE ) ;
2016-02-21 17:13:30 +00:00
if ( rc < 0 ) {
PDEBUG ( DNMT , DEBUG_ERROR , " Failed to init transceiver process! \n " ) ;
goto error ;
}
2017-02-05 07:36:51 +00:00
timer_init ( & nmt - > timer , nmt_timeout , nmt ) ;
2017-07-22 08:38:18 +00:00
nmt - > sysinfo . system = nmt_system ;
2017-02-05 07:36:51 +00:00
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 - > compandor = compandor ;
nmt - > supervisory = supervisory ;
nmt - > send_callerid = send_callerid ;
strncpy ( nmt - > smsc_number , smsc_number , sizeof ( nmt - > smsc_number ) - 1 ) ;
2016-02-21 17:13:30 +00:00
/* init audio processing */
2017-06-09 18:18:24 +00:00
rc = dsp_init_sender ( nmt , deviation_factor ) ;
2016-02-21 17:13:30 +00:00
if ( rc < 0 ) {
PDEBUG ( DNMT , DEBUG_ERROR , " Failed to init audio processing! \n " ) ;
goto error ;
}
2016-07-04 17:52:00 +00:00
/* init DMS processing */
2016-07-04 05:23:44 +00:00
rc = dms_init_sender ( nmt ) ;
if ( rc < 0 ) {
PDEBUG ( DNMT , DEBUG_ERROR , " Failed to init DMS processing! \n " ) ;
goto error ;
}
2016-07-04 17:52:00 +00:00
/* init SMS processing */
rc = sms_init_sender ( nmt ) ;
if ( rc < 0 ) {
PDEBUG ( DNMT , DEBUG_ERROR , " Failed to init SMS processing! \n " ) ;
goto error ;
}
2016-02-21 17:13:30 +00:00
/* go into idle state */
nmt_go_idle ( nmt ) ;
2019-07-20 16:11:17 +00:00
PDEBUG ( DNMT , DEBUG_NOTICE , " Created channel #%s of type '%s' = %s \n " , kanal , chan_type_short_name ( nmt_system , chan_type ) , chan_type_long_name ( nmt_system , chan_type ) ) ;
2017-07-22 08:38:18 +00:00
if ( nmt_long_name_by_short_name ( nmt_system , country ) )
PDEBUG ( DNMT , DEBUG_NOTICE , " -> Using country '%s' \n " , nmt_long_name_by_short_name ( nmt_system , country ) ) ;
2019-03-09 08:51:09 +00:00
PDEBUG ( DNMT , DEBUG_NOTICE , " -> Using traffic area %d,%d and area no %d \n " , traffic_area > > 4 , ( nmt_system = = 450 ) ? nmt_flip_ten ( ( traffic_area & 0xf ) ) : ( traffic_area & 0xf ) , area_no ) ;
2017-02-05 07:36:51 +00:00
if ( nmt - > supervisory )
PDEBUG ( DNMT , DEBUG_NOTICE , " -> Using supervisory signal %d \n " , supervisory ) ;
else
PDEBUG ( DNMT , DEBUG_NOTICE , " -> Using no supervisory signal \n " ) ;
2016-02-21 17:13:30 +00:00
return 0 ;
error :
nmt_destroy ( & nmt - > sender ) ;
return rc ;
}
2017-07-22 08:38:18 +00:00
void nmt_check_channels ( int __attribute__ ( ( unused ) ) nmt_system )
2016-07-31 13:24:19 +00:00
{
sender_t * sender ;
nmt_t * nmt ;
2017-07-22 08:38:18 +00:00
int cca = 0 , ccb = 0 , tc = 0 ;
int note = 0 ;
2016-07-31 13:24:19 +00:00
for ( sender = sender_head ; sender ; sender = sender - > next ) {
nmt = ( nmt_t * ) sender ;
2017-07-22 08:38:18 +00:00
if ( nmt - > sysinfo . chan_type = = CHAN_TYPE_CC ) {
cca = 1 ;
ccb = 1 ;
}
if ( nmt - > sysinfo . chan_type = = CHAN_TYPE_CCA )
cca = 1 ;
if ( nmt - > sysinfo . chan_type = = CHAN_TYPE_CCB )
ccb = 1 ;
2016-07-31 13:24:19 +00:00
if ( nmt - > sysinfo . chan_type = = CHAN_TYPE_TC )
tc = 1 ;
2017-07-22 08:38:18 +00:00
if ( nmt - > sysinfo . chan_type = = CHAN_TYPE_AC_TC )
tc = 1 ;
2016-07-31 13:24:19 +00:00
if ( nmt - > sysinfo . chan_type = = CHAN_TYPE_CC_TC ) {
2017-07-22 08:38:18 +00:00
cca = 1 ;
ccb = 1 ;
2016-07-31 13:24:19 +00:00
tc = 1 ;
}
}
2017-07-22 08:38:18 +00:00
if ( ( cca | | ccb ) & & ! tc ) {
2018-01-02 10:51:32 +00:00
PDEBUG ( DNMT , DEBUG_NOTICE , " \n " ) ;
PDEBUG ( DNMT , DEBUG_NOTICE , " *** Selected channel(s) can be used for control only. \n " ) ;
PDEBUG ( DNMT , DEBUG_NOTICE , " *** No call is possible. \n " ) ;
2017-10-28 17:01:27 +00:00
PDEBUG ( DNMT , DEBUG_NOTICE , " *** Use at least one 'TC'! \n " ) ;
2017-07-22 08:38:18 +00:00
note = 1 ;
2016-07-31 13:24:19 +00:00
}
2017-07-22 08:38:18 +00:00
if ( tc & & ! ( cca | | ccb ) ) {
2018-01-02 10:51:32 +00:00
PDEBUG ( DNMT , DEBUG_NOTICE , " \n " ) ;
2016-07-31 13:24:19 +00:00
PDEBUG ( DNMT , DEBUG_NOTICE , " *** Selected channel(s) can be used for traffic only. \n " ) ;
2018-01-02 10:51:32 +00:00
PDEBUG ( DNMT , DEBUG_NOTICE , " *** No call to the mobile phone is possible. \n " ) ;
2017-10-28 17:01:27 +00:00
PDEBUG ( DNMT , DEBUG_NOTICE , " *** Use one 'CC'! \n " ) ;
2017-07-22 08:38:18 +00:00
note = 1 ;
}
if ( cca & & ! ccb ) {
2018-01-02 10:51:32 +00:00
PDEBUG ( DNMT , DEBUG_NOTICE , " \n " ) ;
PDEBUG ( DNMT , DEBUG_NOTICE , " *** Selected channel(s) can be used for control of MS type A only. \n " ) ;
PDEBUG ( DNMT , DEBUG_NOTICE , " *** No call to the MS type B phone is possible. \n " ) ;
2017-10-28 17:01:27 +00:00
PDEBUG ( DNMT , DEBUG_NOTICE , " *** Use one 'CC' instead! \n " ) ;
2017-07-22 08:38:18 +00:00
note = 1 ;
}
if ( ! cca & & ccb ) {
2018-01-02 10:51:32 +00:00
PDEBUG ( DNMT , DEBUG_NOTICE , " \n " ) ;
PDEBUG ( DNMT , DEBUG_NOTICE , " *** Selected channel(s) can be used for control of MS type B only. \n " ) ;
PDEBUG ( DNMT , DEBUG_NOTICE , " *** No call to the MS type A phone is possible. \n " ) ;
2017-10-28 17:01:27 +00:00
PDEBUG ( DNMT , DEBUG_NOTICE , " *** Use one 'CC' instead! \n " ) ;
2017-07-22 08:38:18 +00:00
note = 1 ;
2016-07-31 13:24:19 +00:00
}
2018-01-02 10:51:32 +00:00
if ( note )
PDEBUG ( DNMT , DEBUG_NOTICE , " \n " ) ;
2016-07-31 13:24:19 +00:00
}
2016-02-21 17:13:30 +00:00
/* Destroy transceiver instance and unlink from list. */
void nmt_destroy ( sender_t * sender )
{
nmt_t * nmt = ( nmt_t * ) sender ;
2019-07-20 16:11:17 +00:00
PDEBUG ( DNMT , DEBUG_DEBUG , " Destroying 'NMT' instance for channel = %s. \n " , sender - > kanal ) ;
2016-02-21 17:13:30 +00:00
dsp_cleanup_sender ( nmt ) ;
2016-07-04 05:23:44 +00:00
dms_cleanup_sender ( nmt ) ;
2016-07-04 17:52:00 +00:00
sms_cleanup_sender ( nmt ) ;
2016-02-21 17:13:30 +00:00
timer_exit ( & nmt - > timer ) ;
sender_destroy ( & nmt - > sender ) ;
free ( nmt ) ;
}
/* Abort connection towards mobile station by sending idle digits. */
2016-08-17 16:25:48 +00:00
void nmt_go_idle ( nmt_t * nmt )
2016-02-21 17:13:30 +00:00
{
timer_stop ( & nmt - > timer ) ;
2016-07-04 17:52:00 +00:00
dms_reset ( nmt ) ;
sms_reset ( nmt ) ;
2016-02-21 17:13:30 +00:00
2017-07-22 08:38:18 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Entering IDLE state, sending idle frames on %s. \n " , chan_type_long_name ( nmt - > sysinfo . system , nmt - > sysinfo . chan_type ) ) ;
2017-05-25 16:43:54 +00:00
nmt - > trans = NULL ; /* remove transaction before state change, so status is shown correctly */
2016-02-21 17:13:30 +00:00
nmt_new_state ( nmt , STATE_IDLE ) ;
nmt_set_dsp_mode ( nmt , DSP_MODE_FRAME ) ;
memset ( & nmt - > dialing , 0 , sizeof ( nmt - > dialing ) ) ;
2016-07-04 05:23:44 +00:00
#if 0
/* go active for loopback tests */
nmt_new_state ( nmt , STATE_ACTIVE ) ;
nmt_set_dsp_mode ( nmt , DSP_MODE_AUDIO ) ;
# endif
2016-02-21 17:13:30 +00:00
}
/* release an ongoing connection, this is used by roaming update and release initiated by MTX */
static void nmt_release ( nmt_t * nmt )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-02-21 17:13:30 +00:00
timer_stop ( & nmt - > timer ) ;
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Releasing connection towards mobile station. \n " ) ;
2016-08-17 16:25:48 +00:00
if ( trans - > callref ) {
PDEBUG_CHAN ( DNMT , DEBUG_ERROR , " Callref already set, please fix! \n " ) ;
abort ( ) ;
}
2016-02-21 17:13:30 +00:00
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 */
2016-08-17 16:25:48 +00:00
static void nmt_page ( transaction_t * trans , int try )
2016-02-21 17:13:30 +00:00
{
2016-04-25 18:20:54 +00:00
sender_t * sender ;
2016-08-17 16:25:48 +00:00
nmt_t * nmt ;
2016-04-25 18:20:54 +00:00
2016-08-17 16:25:48 +00:00
PDEBUG ( DNMT , DEBUG_INFO , " Entering paging state (try %d), sending call to '%c,%s'. \n " , try , trans - > subscriber . country , trans - > subscriber . number ) ;
trans - > page_try = try ;
timer_start ( & trans - > timer , PAGING_TO ) ;
2016-04-25 18:20:54 +00:00
/* page on all CC (CC/TC) */
for ( sender = sender_head ; sender ; sender = sender - > next ) {
2016-08-17 16:25:48 +00:00
nmt = ( nmt_t * ) sender ;
2017-10-28 17:01:27 +00:00
if ( ! is_chan_class_cc ( nmt - > sysinfo . chan_type ) )
2016-08-17 16:25:48 +00:00
continue ;
2016-08-28 13:29:13 +00:00
/* page on all idle channels and on channels we previously paged */
if ( nmt - > state ! = STATE_IDLE & & nmt - > trans ! = trans )
2016-08-17 16:25:48 +00:00
continue ;
2019-07-20 16:11:17 +00:00
PDEBUG ( DNMT , DEBUG_INFO , " Paging on channel %s. \n " , sender - > kanal ) ;
2017-05-25 16:43:54 +00:00
nmt - > trans = trans ; /* add transaction before state change, so status is shown correctly */
2016-08-17 16:25:48 +00:00
nmt_new_state ( nmt , STATE_MT_PAGING ) ;
nmt_set_dsp_mode ( nmt , DSP_MODE_FRAME ) ;
nmt - > tx_frame_count = 0 ;
2016-04-25 18:20:54 +00:00
}
2016-02-21 17:13:30 +00:00
}
2017-11-11 07:10:37 +00:00
static nmt_t * search_free_tc ( nmt_t * own )
2017-10-28 17:01:27 +00:00
{
sender_t * sender ;
nmt_t * nmt , * cc_tc = NULL ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
nmt = ( nmt_t * ) sender ;
2017-11-11 07:10:37 +00:00
/* if our CC is used, we don't care about busy state,
* because it can be used , if it is CC / TC type */
if ( nmt ! = own & & nmt - > state ! = STATE_IDLE )
2017-10-28 17:01:27 +00:00
continue ;
/* remember combined voice/control/paging channel as second alternative */
if ( nmt - > sysinfo . chan_type = = CHAN_TYPE_CC_TC )
cc_tc = nmt ;
else if ( is_chan_class_tc ( nmt - > sysinfo . chan_type ) )
return nmt ;
}
return cc_tc ;
}
2016-02-21 17:13:30 +00:00
/*
* 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 ;
2017-10-08 15:10:03 +00:00
int rc ;
2016-02-21 17:13:30 +00:00
/* check channel match */
2017-10-08 15:10:03 +00:00
rc = nmt_decode_channel ( nmt - > sysinfo . system , frame - > channel_no , & channel , & power ) ;
if ( rc < 0 ) {
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Frame with illegal encoded channel received, ignoring. \n " ) ;
return 0 ;
}
/* in case of interleaved channel, ignore the missing upper bit */
2019-07-20 16:11:17 +00:00
if ( ( channel % 1024 ) ! = ( atoi ( nmt - > sender . kanal ) % 1024 ) ) {
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Frame for different channel %d received, ignoring. \n " , channel ) ;
2016-02-21 17:13:30 +00:00
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 ) {
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( 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 ) ;
2016-02-21 17:13:30 +00:00
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-08-02 09:03:03 +00:00
PDEBUG_CHAN ( 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 */
2016-08-17 16:25:48 +00:00
static int match_subscriber ( transaction_t * trans , frame_t * frame )
2016-02-21 17:13:30 +00:00
{
2016-08-17 16:25:48 +00:00
if ( nmt_digits2value ( & trans - > subscriber . country , 1 ) ! = frame - > ms_country ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Received non matching subscriber counrtry, ignoring. \n " ) ;
2016-02-21 17:13:30 +00:00
return 0 ;
}
2016-08-17 16:25:48 +00:00
if ( nmt_digits2value ( trans - > subscriber . number , 6 ) ! = frame - > ms_number ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Received non matching subscriber number, ignoring. \n " ) ;
2016-02-21 17:13:30 +00:00
return 0 ;
}
return 1 ;
}
2016-07-20 09:58:12 +00:00
/*
* helper functions to generate frames
*/
static void tx_ident ( nmt_t * nmt , frame_t * frame )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-07-20 10:51:25 +00:00
frame - > mt = NMT_MESSAGE_3b ;
2019-07-20 16:11:17 +00:00
frame - > channel_no = nmt_encode_channel ( nmt - > sysinfo . system , atoi ( nmt - > sender . kanal ) , nmt - > sysinfo . ms_power ) ;
frame - > traffic_area = nmt_encode_traffic_area ( nmt - > sysinfo . system , atoi ( nmt - > sender . kanal ) , nmt - > sysinfo . traffic_area ) ;
2016-08-17 16:25:48 +00:00
frame - > ms_country = nmt_digits2value ( & trans - > subscriber . country , 1 ) ;
frame - > ms_number = nmt_digits2value ( trans - > subscriber . number , 6 ) ;
2016-07-20 09:58:12 +00:00
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 )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-07-20 10:51:25 +00:00
frame - > mt = NMT_MESSAGE_5a ;
2019-07-20 16:11:17 +00:00
frame - > channel_no = nmt_encode_channel ( nmt - > sysinfo . system , atoi ( nmt - > sender . kanal ) , nmt - > sysinfo . ms_power ) ;
frame - > traffic_area = nmt_encode_traffic_area ( nmt - > sysinfo . system , atoi ( nmt - > sender . kanal ) , nmt - > sysinfo . traffic_area ) ;
2016-08-17 16:25:48 +00:00
frame - > ms_country = nmt_digits2value ( & trans - > subscriber . country , 1 ) ;
frame - > ms_number = nmt_digits2value ( trans - > subscriber . number , 6 ) ;
2016-07-20 09:58:12 +00:00
frame - > line_signal = ( signal < < 8 ) | ( signal < < 4 ) | signal ;
}
2016-02-21 17:13:30 +00:00
/*
* handle idle channel
*/
static void tx_idle ( nmt_t * nmt , frame_t * frame )
{
switch ( nmt - > sysinfo . chan_type ) {
case CHAN_TYPE_CC :
2016-07-20 10:51:25 +00:00
frame - > mt = NMT_MESSAGE_1a ;
2016-02-21 17:13:30 +00:00
break ;
2017-07-22 08:38:18 +00:00
case CHAN_TYPE_CCA :
frame - > mt = NMT_MESSAGE_1a_a ;
break ;
case CHAN_TYPE_CCB :
frame - > mt = NMT_MESSAGE_1a_b ;
break ;
2016-02-21 17:13:30 +00:00
case CHAN_TYPE_TC :
2016-07-20 10:51:25 +00:00
frame - > mt = NMT_MESSAGE_4 ;
2016-02-21 17:13:30 +00:00
break ;
2017-07-22 08:38:18 +00:00
case CHAN_TYPE_AC_TC :
frame - > mt = NMT_MESSAGE_4b ;
break ;
2016-02-21 17:13:30 +00:00
case CHAN_TYPE_CC_TC :
2016-07-20 10:51:25 +00:00
frame - > mt = NMT_MESSAGE_1b ;
2016-02-21 17:13:30 +00:00
break ;
case CHAN_TYPE_TEST :
2016-07-20 10:51:25 +00:00
frame - > mt = NMT_MESSAGE_30 ;
2016-02-21 17:13:30 +00:00
break ;
}
2019-07-20 16:11:17 +00:00
frame - > channel_no = nmt_encode_channel ( nmt - > sysinfo . system , atoi ( nmt - > sender . kanal ) , nmt - > sysinfo . ms_power ) ;
frame - > traffic_area = nmt_encode_traffic_area ( nmt - > sysinfo . system , atoi ( nmt - > sender . kanal ) , nmt - > sysinfo . traffic_area ) ;
2016-02-21 17:13:30 +00:00
frame - > additional_info = nmt_encode_area_no ( nmt - > sysinfo . area_no ) ;
}
static void rx_idle ( nmt_t * nmt , frame_t * frame )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans ;
nmt_subscriber_t subscr ;
2016-07-20 10:51:25 +00:00
switch ( frame - > mt ) {
2016-02-21 17:13:30 +00:00
case NMT_MESSAGE_11a : /* roaming update and seizure */
if ( ! match_channel ( nmt , frame ) )
break ;
if ( ! match_area ( nmt , frame ) )
break ;
/* set subscriber */
2016-08-17 16:25:48 +00:00
memset ( & subscr , 0 , sizeof ( subscr ) ) ;
nmt_value2digits ( frame - > ms_country , & subscr . country , 1 ) ;
nmt_value2digits ( frame - > ms_number , subscr . number , 6 ) ;
2017-06-09 18:18:24 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Received roaming seizure from subscriber %c,%s \n " , subscr . country , subscr . number ) ;
/* create transaction */
2016-08-17 16:25:48 +00:00
trans = create_transaction ( & subscr ) ;
if ( ! trans ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Failed to create transaction! \n " ) ;
break ;
}
2016-02-21 17:13:30 +00:00
/* change state */
2017-05-25 16:43:54 +00:00
nmt - > trans = trans ; /* add transaction before state change, so status is shown correctly */
2016-02-21 17:13:30 +00:00
nmt_new_state ( nmt , STATE_ROAMING_IDENT ) ;
2016-08-17 16:25:48 +00:00
trans - > nmt = nmt ;
2016-02-21 17:13:30 +00:00
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 */
2017-07-22 08:38:18 +00:00
case NMT_MESSAGE_10a : /* access signal */
2016-02-21 17:13:30 +00:00
if ( ! match_channel ( nmt , frame ) )
break ;
if ( ! match_area ( nmt , frame ) )
break ;
/* set subscriber */
2016-08-17 16:25:48 +00:00
memset ( & subscr , 0 , sizeof ( subscr ) ) ;
nmt_value2digits ( frame - > ms_country , & subscr . country , 1 ) ;
nmt_value2digits ( frame - > ms_number , subscr . number , 6 ) ;
2016-07-20 10:51:25 +00:00
if ( frame - > mt = = NMT_MESSAGE_12 )
2016-08-17 16:25:48 +00:00
subscr . coinbox = 1 ;
2017-06-09 18:18:24 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Received call from subscriber %c,%s%s \n " , subscr . country , subscr . number , ( subscr . coinbox ) ? " (coinbox) " : " " ) ;
/* create transaction */
2016-08-17 16:25:48 +00:00
trans = create_transaction ( & subscr ) ;
if ( ! trans ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Failed to create transaction! \n " ) ;
break ;
}
2016-02-21 17:13:30 +00:00
/* change state */
2017-05-25 16:43:54 +00:00
nmt - > trans = trans ; /* add transaction before state change, so status is shown correctly */
2016-02-21 17:13:30 +00:00
nmt_new_state ( nmt , STATE_MO_IDENT ) ;
2016-08-17 16:25:48 +00:00
trans - > nmt = nmt ;
2016-02-21 17:13:30 +00:00
nmt - > rx_frame_count = 0 ;
nmt - > tx_frame_count = 0 ;
break ;
/* signals after release */
case NMT_MESSAGE_13a : /* line signal */
break ;
default :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > mt ) , nmt_state_name ( nmt - > state ) ) ;
2016-02-21 17:13:30 +00:00
}
}
/*
* handle roaming
*/
static void tx_roaming_ident ( nmt_t * nmt , frame_t * frame )
{
if ( + + nmt - > tx_frame_count = = 1 )
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Sending identity request. \n " ) ;
2017-07-02 08:44:47 +00:00
tx_ident ( nmt , frame ) ;
2016-02-21 17:13:30 +00:00
if ( nmt - > tx_frame_count = = 8 ) {
2017-07-02 08:44:47 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Timeout waiting for identity reply \n " ) ;
2016-02-21 17:13:30 +00:00
nmt_release ( nmt ) ;
}
}
static void rx_roaming_ident ( nmt_t * nmt , frame_t * frame )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-07-20 10:51:25 +00:00
switch ( frame - > mt ) {
2016-02-21 17:13:30 +00:00
case NMT_MESSAGE_11a : /* roaming update */
if ( ! match_channel ( nmt , frame ) )
break ;
if ( ! match_area ( nmt , frame ) )
break ;
2016-08-17 16:25:48 +00:00
if ( ! match_subscriber ( trans , frame ) )
2016-02-21 17:13:30 +00:00
break ;
if ( nmt - > rx_frame_count < 2 ) {
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_DEBUG , " Skipping second seizure frame \n " ) ;
2016-02-21 17:13:30 +00:00
break ;
}
2016-08-17 16:25:48 +00:00
nmt_value2digits ( frame - > ms_password , trans - > subscriber . password , 3 ) ;
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Received identity confirm (password %s). \n " , trans - > subscriber . password ) ;
2016-02-21 17:13:30 +00:00
nmt_new_state ( nmt , STATE_ROAMING_CONFIRM ) ;
nmt - > tx_frame_count = 0 ;
break ;
default :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > mt ) , nmt_state_name ( nmt - > state ) ) ;
2016-02-21 17:13:30 +00:00
}
}
static void tx_roaming_confirm ( nmt_t * nmt , frame_t * frame )
{
set_line_signal ( nmt , frame , 3 ) ;
if ( + + nmt - > tx_frame_count = = 1 )
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Send 'Roaming updating confirmation'. \n " ) ;
2016-02-21 17:13:30 +00:00
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 )
{
2016-07-20 10:51:25 +00:00
switch ( frame - > mt ) {
2016-02-21 17:13:30 +00:00
default :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > mt ) , nmt_state_name ( nmt - > state ) ) ;
2016-02-21 17:13:30 +00:00
}
}
/*
* handle call MS - > MTX
*/
static void tx_mo_ident ( nmt_t * nmt , frame_t * frame )
{
if ( + + nmt - > tx_frame_count = = 1 )
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Sending identity request. \n " ) ;
2017-07-02 08:44:47 +00:00
tx_ident ( nmt , frame ) ;
2016-02-21 17:13:30 +00:00
if ( nmt - > tx_frame_count = = 8 ) {
2017-07-02 08:44:47 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Timeout waiting for identity reply \n " ) ;
2016-02-21 17:13:30 +00:00
nmt_release ( nmt ) ;
}
}
static void rx_mo_ident ( nmt_t * nmt , frame_t * frame )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-07-20 10:51:25 +00:00
switch ( frame - > mt ) {
2016-02-21 17:13:30 +00:00
case NMT_MESSAGE_10b : /* seizure */
case NMT_MESSAGE_12 : /* seizure */
if ( ! match_channel ( nmt , frame ) )
break ;
2016-08-17 16:25:48 +00:00
if ( ! match_subscriber ( trans , frame ) )
2016-02-21 17:13:30 +00:00
break ;
if ( nmt - > rx_frame_count < 2 ) {
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_DEBUG , " Skipping second seizure frame \n " ) ;
2016-02-21 17:13:30 +00:00
break ;
}
2016-08-17 16:25:48 +00:00
nmt_value2digits ( frame - > ms_password , trans - > subscriber . password , 3 ) ;
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Received identity confirm (password %s). \n " , trans - > subscriber . password ) ;
2016-02-21 17:13:30 +00:00
nmt_new_state ( nmt , STATE_MO_CONFIRM ) ;
nmt - > tx_frame_count = 0 ;
break ;
default :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > mt ) , nmt_state_name ( nmt - > state ) ) ;
2016-02-21 17:13:30 +00:00
}
}
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 )
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Send 'Proceed to send'. \n " ) ;
2016-02-21 17:13:30 +00:00
} else {
if ( nmt - > tx_frame_count = = 3 ) {
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Send dial tone. \n " ) ;
2016-02-21 17:13:30 +00:00
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 ) ;
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-02-21 17:13:30 +00:00
2016-07-20 10:51:25 +00:00
switch ( frame - > mt ) {
2016-02-21 17:13:30 +00:00
case NMT_MESSAGE_14a : /* digits */
if ( ! match_channel ( nmt , frame ) )
break ;
if ( ! match_area ( nmt , frame ) )
break ;
2016-08-17 16:25:48 +00:00
if ( ! match_subscriber ( trans , frame ) )
break ;
2016-02-21 17:13:30 +00:00
timer_start ( & nmt - > timer , DIALING_TO ) ;
/* max digits received */
if ( len + 1 = = sizeof ( nmt - > dialing ) )
break ;
if ( ( len & 1 ) ) {
2017-07-15 19:49:28 +00:00
/* received odd digit, but be already have odd number of digits */
if ( nmt - > rx_frame_count > 1 ) /* we lost even digit */
2016-02-21 17:13:30 +00:00
goto missing_digit ;
break ;
2017-07-15 19:49:28 +00:00
} else if ( len ) { /* complain only after first digit */
/* received odd digit, and we have even number of digits */
if ( nmt - > rx_frame_count > 3 ) /* we lost even digit */
2016-02-21 17:13:30 +00:00
goto missing_digit ;
}
2017-07-15 19:49:28 +00:00
if ( ( frame - > digit > > 12 ) ! = 0x00 ) /* digit 0x0 0x0, x, x, x */
goto not_right_position ;
if ( ( ( frame - > digit > > 8 ) & 0xf ) ! = ( ( frame - > digit > > 4 ) & 0xf )
| | ( ( frame - > digit > > 4 ) & 0xf ) ! = ( frame - > digit & 0xf ) )
goto not_consistent_digit ;
2016-02-21 17:13:30 +00:00
nmt - > dialing [ len ] = nmt_value2digit ( frame - > digit ) ;
nmt - > dialing [ len + 1 ] = ' \0 ' ;
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Received (odd) digit %c. \n " , nmt - > dialing [ len ] ) ;
2016-02-21 17:13:30 +00:00
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 ;
2016-08-17 16:25:48 +00:00
if ( ! match_subscriber ( trans , frame ) )
break ;
2016-02-21 17:13:30 +00:00
timer_start ( & nmt - > timer , DIALING_TO ) ;
/* max digits received */
if ( len + 1 = = sizeof ( nmt - > dialing ) )
break ;
2017-07-15 19:49:28 +00:00
/* received even digit, but no digit yet, so we lost first odd digit */
if ( ! len )
goto missing_digit ;
2016-02-21 17:13:30 +00:00
if ( ! ( len & 1 ) ) {
2017-07-15 19:49:28 +00:00
/* received even digit, but be already have even number of digits */
if ( nmt - > rx_frame_count > 1 ) /* we lost odd digit */
2016-02-21 17:13:30 +00:00
goto missing_digit ;
break ;
} else {
2017-07-15 19:49:28 +00:00
/* received even digit, and we have odd number of digits */
if ( nmt - > rx_frame_count > 3 ) /* we lost odd digit */
2016-02-21 17:13:30 +00:00
goto missing_digit ;
}
2017-07-15 19:49:28 +00:00
if ( ( frame - > digit > > 12 ) ! = 0xff ) /* digit 0xf 0xf, x, x, x */
goto not_right_position ;
if ( ( ( frame - > digit > > 8 ) & 0xf ) ! = ( ( frame - > digit > > 4 ) & 0xf )
| | ( ( frame - > digit > > 4 ) & 0xf ) ! = ( frame - > digit & 0xf ) )
goto not_consistent_digit ;
2016-02-21 17:13:30 +00:00
nmt - > dialing [ len ] = nmt_value2digit ( frame - > digit ) ;
nmt - > dialing [ len + 1 ] = ' \0 ' ;
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Received (even) digit %c. \n " , nmt - > dialing [ len ] ) ;
2016-02-21 17:13:30 +00:00
nmt - > rx_frame_count = 0 ;
break ;
case NMT_MESSAGE_15 : /* idle */
if ( ! len )
break ;
2016-08-17 16:25:48 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Dialing complete %s->%s, call established. \n " , & trans - > subscriber . country , nmt - > dialing ) ;
2016-02-21 17:13:30 +00:00
/* setup call */
2016-07-04 17:52:00 +00:00
if ( ! strcmp ( nmt - > dialing , nmt - > smsc_number ) ) {
2016-07-04 05:23:44 +00:00
/* SMS */
2016-07-04 17:52:00 +00:00
PDEBUG ( DNMT , DEBUG_INFO , " Setup call to SMSC. \n " ) ;
2017-11-11 07:10:37 +00:00
trans - > dms_call = 1 ;
2016-07-04 05:23:44 +00:00
} else {
2016-02-21 17:13:30 +00:00
int callref = + + new_callref ;
int rc ;
PDEBUG ( DNMT , DEBUG_INFO , " Setup call to network. \n " ) ;
2017-10-28 05:11:40 +00:00
rc = call_up_setup ( callref , & trans - > subscriber . country , nmt - > dialing ) ;
2016-02-21 17:13:30 +00:00
if ( rc < 0 ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Call rejected (cause %d), releasing. \n " , rc ) ;
nmt_release ( nmt ) ;
return ;
}
2016-08-17 16:25:48 +00:00
trans - > callref = callref ;
2016-02-21 17:13:30 +00:00
}
2016-08-17 16:25:48 +00:00
timer_stop ( & nmt - > timer ) ;
2016-02-21 17:13:30 +00:00
nmt_new_state ( nmt , STATE_MO_COMPLETE ) ;
nmt_set_dsp_mode ( nmt , DSP_MODE_FRAME ) ;
nmt - > tx_frame_count = 0 ;
break ;
default :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > mt ) , nmt_state_name ( nmt - > state ) ) ;
2016-02-21 17:13:30 +00:00
}
return ;
missing_digit :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Missing digit, aborting. \n " ) ;
2016-02-21 17:13:30 +00:00
nmt_release ( nmt ) ;
2017-07-15 19:49:28 +00:00
return ;
not_right_position :
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Position information of digit does not match, ignoring due to corrupt frame. \n " ) ;
return ;
not_consistent_digit :
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Digit repetition in frame does not match, ignoring due to corrupt frame. \n " ) ;
return ;
2016-02-21 17:13:30 +00:00
}
static void tx_mo_complete ( nmt_t * nmt , frame_t * frame )
{
2017-11-11 07:10:37 +00:00
transaction_t * trans = nmt - > trans ;
2016-02-21 17:13:30 +00:00
if ( + + nmt - > tx_frame_count < = 4 ) {
set_line_signal ( nmt , frame , 6 ) ;
if ( nmt - > tx_frame_count = = 1 )
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Send 'address complete'. \n " ) ;
2016-02-21 17:13:30 +00:00
} else {
2016-06-20 17:37:56 +00:00
if ( nmt - > compandor ) {
2016-02-21 17:13:30 +00:00
set_line_signal ( nmt , frame , 5 ) ;
if ( nmt - > tx_frame_count = = 5 )
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Send 'compandor in'. \n " ) ;
2016-02-21 17:13:30 +00:00
} else
2016-07-20 10:51:25 +00:00
frame - > mt = NMT_MESSAGE_6 ;
2016-02-21 17:13:30 +00:00
if ( nmt - > tx_frame_count = = 9 ) {
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Connect audio. \n " ) ;
2016-02-21 17:13:30 +00:00
nmt_new_state ( nmt , STATE_ACTIVE ) ;
nmt - > active_state = ACTIVE_STATE_VOICE ;
nmt_set_dsp_mode ( nmt , DSP_MODE_AUDIO ) ;
2017-11-11 07:10:37 +00:00
if ( nmt - > supervisory & & ! trans - > dms_call ) {
2016-02-21 17:13:30 +00:00
super_reset ( nmt ) ;
timer_start ( & nmt - > timer , SUPERVISORY_TO1 ) ;
}
}
}
}
static void timeout_mo_dialing ( nmt_t * nmt )
{
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Timeout while receiving digits. \n " ) ;
2016-02-21 17:13:30 +00:00
nmt_release ( nmt ) ;
PDEBUG ( DNMT , DEBUG_INFO , " Release call towards network. \n " ) ;
}
/*
* handle call MTX - > MS
*/
static void tx_mt_paging ( nmt_t * nmt , frame_t * frame )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-07-20 10:51:25 +00:00
frame - > mt = NMT_MESSAGE_2a ;
2019-07-20 16:11:17 +00:00
frame - > channel_no = nmt_encode_channel ( nmt - > sysinfo . system , atoi ( nmt - > sender . kanal ) , nmt - > sysinfo . ms_power ) ;
frame - > traffic_area = nmt_encode_traffic_area ( nmt - > sysinfo . system , atoi ( nmt - > sender . kanal ) , nmt - > sysinfo . traffic_area ) ;
2016-08-17 16:25:48 +00:00
frame - > ms_country = nmt_digits2value ( & trans - > subscriber . country , 1 ) ;
frame - > ms_number = nmt_digits2value ( trans - > subscriber . number , 6 ) ;
2016-02-21 17:13:30 +00:00
frame - > additional_info = nmt_encode_area_no ( nmt - > sysinfo . area_no ) ;
if ( + + nmt - > tx_frame_count = = 1 ) {
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Send call to mobile. \n " ) ;
2016-02-21 17:13:30 +00:00
} else
tx_idle ( nmt , frame ) ;
2016-08-17 16:25:48 +00:00
}
void timeout_mt_paging ( transaction_t * trans )
{
PDEBUG ( DNMT , DEBUG_NOTICE , " No answer from mobile phone (try %d). \n " , trans - > page_try ) ;
if ( trans - > page_try = = PAGE_TRIES ) {
PDEBUG ( DNMT , DEBUG_INFO , " Release call towards network. \n " ) ;
2017-10-28 05:11:40 +00:00
call_up_release ( trans - > callref , CAUSE_OUTOFORDER ) ;
2016-08-17 16:25:48 +00:00
destroy_transaction ( trans ) ;
return ;
2016-02-21 17:13:30 +00:00
}
2016-08-17 16:25:48 +00:00
nmt_page ( trans , trans - > page_try + 1 ) ;
2016-02-21 17:13:30 +00:00
}
static void rx_mt_paging ( nmt_t * nmt , frame_t * frame )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
sender_t * sender ;
nmt_t * other ;
2016-07-20 10:51:25 +00:00
switch ( frame - > mt ) {
2016-07-24 08:26:01 +00:00
case NMT_MESSAGE_10a : /* call acknowledgment */
2017-07-22 08:38:18 +00:00
case NMT_MESSAGE_10d : /* call ack on alternate type */
2016-02-21 17:13:30 +00:00
if ( ! match_channel ( nmt , frame ) )
break ;
2016-08-17 16:25:48 +00:00
if ( ! match_subscriber ( trans , frame ) )
break ;
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Received call acknowledgment from subscriber %c,%s. \n " , trans - > subscriber . country , trans - > subscriber . number ) ;
if ( trans - > sms_string [ 0 ] )
2017-11-11 07:10:37 +00:00
trans - > dms_call = 1 ;
2016-08-17 16:25:48 +00:00
timer_stop ( & trans - > timer ) ;
nmt_new_state ( nmt , STATE_MT_CHANNEL ) ;
trans - > nmt = nmt ;
nmt - > tx_frame_count = 0 ;
/* release other channels */
for ( sender = sender_head ; sender ; sender = sender - > next ) {
other = ( nmt_t * ) sender ;
if ( other = = nmt )
continue ;
if ( other - > trans = = trans )
nmt_go_idle ( other ) ;
2016-04-25 18:20:54 +00:00
}
2016-02-21 17:13:30 +00:00
break ;
default :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > mt ) , nmt_state_name ( nmt - > state ) ) ;
2016-02-21 17:13:30 +00:00
}
}
static void tx_mt_channel ( nmt_t * nmt , frame_t * frame )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2017-10-28 17:01:27 +00:00
nmt_t * tc ;
/* get free channel (after releasing all channels) */
2017-11-11 07:10:37 +00:00
tc = search_free_tc ( nmt ) ;
2017-10-28 17:01:27 +00:00
if ( ! tc ) {
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " TC is not free anymore. \n " ) ;
PDEBUG ( DNMT , DEBUG_INFO , " Release call towards network. \n " ) ;
call_up_release ( trans - > callref , CAUSE_NOCHANNEL ) ;
trans - > callref = 0 ;
nmt_release ( nmt ) ;
/* send idle for now, then continue with release */
tx_idle ( nmt , frame ) ;
return ;
}
2017-11-11 07:10:37 +00:00
if ( nmt ! = tc ) {
/* link trans and tc together, so we can continue with channel assignment */
2019-07-20 16:11:17 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Switching to TC channel #%s. \n " , tc - > sender . kanal ) ;
2017-11-11 07:10:37 +00:00
nmt_go_idle ( nmt ) ;
tc - > trans = trans ;
trans - > nmt = tc ;
} else
2019-07-20 16:11:17 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Staying on CC/TC channel #%s. \n " , tc - > sender . kanal ) ;
2017-10-28 17:01:27 +00:00
nmt_new_state ( tc , STATE_MT_IDENT ) ;
2018-12-23 18:44:05 +00:00
tc - > tx_frame_count = 0 ;
2016-08-17 16:25:48 +00:00
2017-10-28 17:01:27 +00:00
/* assign channel on 'nmt' to 'tc' */
2016-07-20 10:51:25 +00:00
frame - > mt = NMT_MESSAGE_2b ;
2019-07-20 16:11:17 +00:00
frame - > channel_no = nmt_encode_channel ( nmt - > sysinfo . system , atoi ( nmt - > sender . kanal ) , nmt - > sysinfo . ms_power ) ;
frame - > traffic_area = nmt_encode_traffic_area ( nmt - > sysinfo . system , atoi ( nmt - > sender . kanal ) , nmt - > sysinfo . traffic_area ) ;
2016-08-17 16:25:48 +00:00
frame - > ms_country = nmt_digits2value ( & trans - > subscriber . country , 1 ) ;
frame - > ms_number = nmt_digits2value ( trans - > subscriber . number , 6 ) ;
2019-07-20 16:11:17 +00:00
frame - > tc_no = nmt_encode_tc ( tc - > sysinfo . system , atoi ( tc - > sender . kanal ) , tc - > sysinfo . ms_power ) ;
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Send channel activation to mobile. \n " ) ;
2016-02-21 17:13:30 +00:00
}
static void tx_mt_ident ( nmt_t * nmt , frame_t * frame )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-02-21 17:13:30 +00:00
if ( + + nmt - > tx_frame_count = = 1 )
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Sending identity request. \n " ) ;
2017-07-02 08:44:47 +00:00
tx_ident ( nmt , frame ) ;
2016-02-21 17:13:30 +00:00
if ( nmt - > tx_frame_count = = 8 ) {
2017-07-02 08:44:47 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Timeout waiting for identity reply \n " ) ;
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Release call towards network. \n " ) ;
2017-10-28 05:11:40 +00:00
call_up_release ( trans - > callref , CAUSE_TEMPFAIL ) ;
2016-08-17 16:25:48 +00:00
destroy_transaction ( trans ) ;
2016-02-21 17:13:30 +00:00
}
}
static void rx_mt_ident ( nmt_t * nmt , frame_t * frame )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-07-20 10:51:25 +00:00
switch ( frame - > mt ) {
2016-02-21 17:13:30 +00:00
case NMT_MESSAGE_10b : /* seizure */
2016-08-17 16:25:48 +00:00
if ( ! match_subscriber ( trans , frame ) )
2016-02-21 17:13:30 +00:00
break ;
2016-08-17 16:25:48 +00:00
nmt_value2digits ( frame - > ms_password , trans - > subscriber . password , 3 ) ;
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Received identity (password %s). \n " , trans - > subscriber . password ) ;
2017-11-11 07:10:37 +00:00
if ( trans - > dms_call ) {
2016-07-20 09:55:03 +00:00
nmt_new_state ( nmt , STATE_MT_AUTOANSWER ) ;
nmt - > wait_autoanswer = 1 ;
2016-07-09 10:12:08 +00:00
nmt - > tx_frame_count = 0 ;
} else {
nmt_new_state ( nmt , STATE_MT_RINGING ) ;
2016-07-20 09:55:03 +00:00
/* start with caller ID before ringing */
if ( nmt - > send_callerid ) {
nmt - > tx_frame_count = 4 ;
nmt - > tx_callerid_count = 1 ;
} else {
nmt - > tx_frame_count = 0 ;
nmt - > tx_callerid_count = 0 ;
}
2016-07-09 10:12:08 +00:00
timer_start ( & nmt - > timer , RINGING_TO ) ;
2017-10-28 05:11:40 +00:00
call_up_alerting ( trans - > callref ) ;
2016-07-09 10:12:08 +00:00
}
2016-02-21 17:13:30 +00:00
break ;
default :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > mt ) , nmt_state_name ( nmt - > state ) ) ;
2016-02-21 17:13:30 +00:00
}
}
2016-07-20 09:55:03 +00:00
static void tx_mt_autoanswer ( nmt_t * nmt , frame_t * frame )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-07-20 09:55:03 +00:00
/* first we need to wait for autoanswer */
if ( nmt - > wait_autoanswer ) {
2016-07-20 10:51:25 +00:00
frame - > mt = NMT_MESSAGE_6 ;
2016-07-20 09:55:03 +00:00
return ;
}
if ( + + nmt - > tx_frame_count = = 1 )
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Send 'autoanswer order'. \n " ) ;
2016-07-20 09:55:03 +00:00
set_line_signal ( nmt , frame , 12 ) ;
if ( nmt - > tx_frame_count = = 4 ) {
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " No reaction to autoanswer, proceed with ringing. \n " ) ;
2016-07-20 09:55:03 +00:00
nmt_new_state ( nmt , STATE_MT_RINGING ) ;
nmt - > tx_frame_count = 0 ;
nmt - > tx_callerid_count = 0 ;
timer_start ( & nmt - > timer , RINGING_TO ) ;
2017-10-28 05:11:40 +00:00
call_up_alerting ( trans - > callref ) ;
2016-07-20 09:55:03 +00:00
}
}
static void rx_mt_autoanswer ( nmt_t * nmt , frame_t * frame )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-07-20 10:51:25 +00:00
switch ( frame - > mt ) {
2016-07-20 09:55:03 +00:00
case NMT_MESSAGE_15 : /* idle */
nmt - > wait_autoanswer = 0 ;
break ;
case NMT_MESSAGE_13a : /* line signal */
if ( ! match_channel ( nmt , frame ) )
break ;
2016-08-17 16:25:48 +00:00
if ( ! match_subscriber ( trans , frame ) )
2016-07-20 09:55:03 +00:00
break ;
2017-07-15 19:49:28 +00:00
if ( ( ( frame - > line_signal > > 16 ) & 0xf ) ! = ( ( frame - > line_signal > > 12 ) & 0xf )
| | ( ( frame - > line_signal > > 12 ) & 0xf ) ! = ( ( frame - > line_signal > > 8 ) & 0xf )
| | ( ( frame - > line_signal > > 8 ) & 0xf ) ! = ( ( frame - > line_signal > > 4 ) & 0xf )
| | ( ( frame - > line_signal > > 4 ) & 0xf ) ! = ( frame - > line_signal & 0xf ) ) {
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Line signal repetition in frame does not match, ignoring due to corrupt frame. \n " ) ;
break ;
}
2016-07-20 09:55:03 +00:00
if ( ( frame - > line_signal & 0xf ) ! = 12 )
break ;
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Received acknowledge to autoanswer. \n " ) ;
2016-07-20 09:55:03 +00:00
nmt_new_state ( nmt , STATE_MT_COMPLETE ) ;
nmt - > tx_frame_count = 0 ;
2017-10-28 05:11:40 +00:00
call_up_answer ( trans - > callref , & trans - > subscriber . country ) ;
2016-07-20 09:55:03 +00:00
break ;
default :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > mt ) , nmt_state_name ( nmt - > state ) ) ;
2016-07-20 09:55:03 +00:00
}
}
2016-02-21 17:13:30 +00:00
static void tx_mt_ringing ( nmt_t * nmt , frame_t * frame )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-02-21 17:13:30 +00:00
set_line_signal ( nmt , frame , 9 ) ;
if ( + + nmt - > tx_frame_count = = 1 )
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Send 'ringing order'. \n " ) ;
2016-07-20 09:58:12 +00:00
if ( nmt - > tx_frame_count > = 4 ) {
if ( nmt - > tx_callerid_count ) {
if ( nmt - > tx_frame_count = = 5 )
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Send 'A-number'. \n " ) ;
2019-07-20 16:11:17 +00:00
nmt_encode_a_number ( frame , nmt - > tx_frame_count - 4 , trans - > caller_type , trans - > caller_id , nmt - > sysinfo . system , atoi ( nmt - > sender . kanal ) , nmt - > sysinfo . ms_power , nmt - > sysinfo . traffic_area ) ;
2016-07-20 09:58:12 +00:00
} else
2016-07-20 10:51:25 +00:00
frame - > mt = NMT_MESSAGE_6 ;
2016-07-20 09:58:12 +00:00
}
if ( nmt - > tx_callerid_count = = 1 ) {
/* start ringing after first caller ID of 6 frames */
if ( nmt - > tx_frame_count = = 10 ) {
nmt - > tx_frame_count = 0 ;
nmt - > tx_callerid_count + + ;
}
} else {
/* repeat ringing after 5 seconds */
if ( nmt - > tx_frame_count = = 36 ) {
nmt - > tx_frame_count = 0 ;
}
}
2016-02-21 17:13:30 +00:00
}
static void rx_mt_ringing ( nmt_t * nmt , frame_t * frame )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-07-20 10:51:25 +00:00
switch ( frame - > mt ) {
2016-02-21 17:13:30 +00:00
case NMT_MESSAGE_13a : /* line signal */
2016-07-20 09:55:03 +00:00
if ( ! match_channel ( nmt , frame ) )
break ;
2016-08-17 16:25:48 +00:00
if ( ! match_subscriber ( trans , frame ) )
2016-02-21 17:13:30 +00:00
break ;
2017-07-15 19:49:28 +00:00
if ( ( ( frame - > line_signal > > 16 ) & 0xf ) ! = ( ( frame - > line_signal > > 12 ) & 0xf )
| | ( ( frame - > line_signal > > 12 ) & 0xf ) ! = ( ( frame - > line_signal > > 8 ) & 0xf )
| | ( ( frame - > line_signal > > 8 ) & 0xf ) ! = ( ( frame - > line_signal > > 4 ) & 0xf )
| | ( ( frame - > line_signal > > 4 ) & 0xf ) ! = ( frame - > line_signal & 0xf ) ) {
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Line signal repetition in frame does not match, ignoring due to corrupt frame. \n " ) ;
break ;
}
2016-02-21 17:13:30 +00:00
if ( ( frame - > line_signal & 0xf ) ! = 14 )
break ;
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Received 'answer' from phone. \n " ) ;
2016-02-21 17:13:30 +00:00
nmt_new_state ( nmt , STATE_MT_COMPLETE ) ;
nmt - > tx_frame_count = 0 ;
2016-08-02 09:18:13 +00:00
timer_stop ( & nmt - > timer ) ;
2017-10-28 05:11:40 +00:00
call_up_answer ( trans - > callref , & trans - > subscriber . country ) ;
2016-02-21 17:13:30 +00:00
break ;
default :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > mt ) , nmt_state_name ( nmt - > state ) ) ;
2016-02-21 17:13:30 +00:00
}
}
static void tx_mt_complete ( nmt_t * nmt , frame_t * frame )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-02-21 17:13:30 +00:00
+ + nmt - > tx_frame_count ;
2017-11-11 07:10:37 +00:00
if ( nmt - > compandor & & ! trans - > dms_call ) {
2016-02-21 17:13:30 +00:00
if ( nmt - > tx_frame_count = = 1 )
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Send 'compandor in'. \n " ) ;
2016-07-09 10:12:08 +00:00
set_line_signal ( nmt , frame , 5 ) ;
2016-02-21 17:13:30 +00:00
} else
2016-07-20 10:51:25 +00:00
frame - > mt = NMT_MESSAGE_6 ;
2016-02-21 17:13:30 +00:00
if ( nmt - > tx_frame_count = = 5 ) {
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Connect audio. \n " ) ;
2016-02-21 17:13:30 +00:00
nmt_new_state ( nmt , STATE_ACTIVE ) ;
nmt - > active_state = ACTIVE_STATE_VOICE ;
nmt_set_dsp_mode ( nmt , DSP_MODE_AUDIO ) ;
2017-11-11 07:10:37 +00:00
if ( nmt - > supervisory & & ! trans - > dms_call ) {
2016-02-21 17:13:30 +00:00
super_reset ( nmt ) ;
timer_start ( & nmt - > timer , SUPERVISORY_TO1 ) ;
}
2017-11-11 07:10:37 +00:00
if ( trans - > dms_call ) {
2016-07-04 17:52:00 +00:00
time_t ti = time ( NULL ) ;
2018-11-10 13:43:31 +00:00
sms_deliver ( nmt , sms_ref , trans - > caller_id , trans - > caller_type , SMS_PLAN_ISDN_TEL , ti , 1 , trans - > sms_string ) ;
2016-07-04 17:52:00 +00:00
}
2016-02-21 17:13:30 +00:00
}
}
static void timeout_mt_ringing ( nmt_t * nmt )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Timeout while waiting for answer of the phone. \n " ) ;
2016-02-21 17:13:30 +00:00
PDEBUG ( DNMT , DEBUG_INFO , " Release call towards network. \n " ) ;
2017-10-28 05:11:40 +00:00
call_up_release ( trans - > callref , CAUSE_NOANSWER ) ;
2016-08-17 16:25:48 +00:00
trans - > callref = 0 ;
nmt_release ( nmt ) ;
2016-02-21 17:13:30 +00:00
}
/*
* handle clearing towards MTX
*/
static void tx_mo_release ( nmt_t * nmt , frame_t * frame )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-02-21 17:13:30 +00:00
set_line_signal ( nmt , frame , 15 ) ;
if ( + + nmt - > tx_frame_count = = 1 )
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Send release. \n " ) ;
2016-02-21 17:13:30 +00:00
if ( nmt - > tx_frame_count = = 4 )
2016-08-17 16:25:48 +00:00
destroy_transaction ( trans ) ; /* continue with this frame, then go idle */
2016-02-21 17:13:30 +00:00
}
/*
* 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 )
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Send release. \n " ) ;
2016-02-21 17:13:30 +00:00
}
static void rx_mt_release ( nmt_t * nmt , frame_t * frame )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-07-20 10:51:25 +00:00
switch ( frame - > mt ) {
2016-02-21 17:13:30 +00:00
case NMT_MESSAGE_13a : /* line signal */
if ( ! match_channel ( nmt , frame ) )
break ;
2016-08-17 16:25:48 +00:00
if ( ! match_subscriber ( trans , frame ) )
2016-02-21 17:13:30 +00:00
break ;
2017-07-15 19:49:28 +00:00
if ( ( ( frame - > line_signal > > 16 ) & 0xf ) ! = ( ( frame - > line_signal > > 12 ) & 0xf )
| | ( ( frame - > line_signal > > 12 ) & 0xf ) ! = ( ( frame - > line_signal > > 8 ) & 0xf )
| | ( ( frame - > line_signal > > 8 ) & 0xf ) ! = ( ( frame - > line_signal > > 4 ) & 0xf )
| | ( ( frame - > line_signal > > 4 ) & 0xf ) ! = ( frame - > line_signal & 0xf ) ) {
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Line signal repetition in frame does not match, ignoring due to corrupt frame. \n " ) ;
break ;
}
2016-02-21 17:13:30 +00:00
if ( ( frame - > line_signal & 0xf ) ! = 1 )
break ;
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Received release guard. \n " ) ;
2016-08-17 16:25:48 +00:00
timer_stop ( & nmt - > timer ) ;
destroy_transaction ( trans ) ;
2016-02-21 17:13:30 +00:00
break ;
default :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > mt ) , nmt_state_name ( nmt - > state ) ) ;
2016-02-21 17:13:30 +00:00
}
}
static void timeout_mt_release ( nmt_t * nmt )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Timeout while releasing. \n " ) ;
2016-08-17 16:25:48 +00:00
destroy_transaction ( trans ) ;
2016-02-21 17:13:30 +00:00
}
/*
* handle call
*/
void nmt_rx_super ( nmt_t * nmt , int tone , double quality )
{
if ( tone )
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Detected supervisory signal with quality=%.0f. \n " , quality * 100.0 ) ;
2016-02-21 17:13:30 +00:00
else
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Lost supervisory signal \n " ) ;
2016-02-21 17:13:30 +00:00
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 )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-02-21 17:13:30 +00:00
if ( duration = = SUPERVISORY_TO1 )
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Timeout after %.0f seconds not receiving supervisory signal. \n " , duration ) ;
2016-02-21 17:13:30 +00:00
else
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Timeout after %.0f seconds loosing supervisory signal. \n " , duration ) ;
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Release call towards network. \n " ) ;
2017-10-28 05:11:40 +00:00
call_up_release ( trans - > callref , CAUSE_TEMPFAIL ) ;
2016-08-17 16:25:48 +00:00
trans - > callref = 0 ;
nmt_release ( nmt ) ;
2016-02-21 17:13:30 +00:00
}
static void rx_active ( nmt_t * nmt , frame_t * frame )
{
2016-08-17 16:25:48 +00:00
transaction_t * trans = nmt - > trans ;
2016-02-21 17:13:30 +00:00
char digit ;
/* restart timer on every reception of frame */
if ( nmt - > supervisory )
timer_start ( & nmt - > timer , SUPERVISORY_TO2 ) ;
2016-07-20 10:51:25 +00:00
switch ( frame - > mt ) {
2016-02-21 17:13:30 +00:00
case NMT_MESSAGE_13a : /* line signal */
if ( ! match_channel ( nmt , frame ) )
break ;
2016-08-17 16:25:48 +00:00
if ( ! match_subscriber ( trans , frame ) )
2016-02-21 17:13:30 +00:00
break ;
2017-07-15 19:49:28 +00:00
if ( ( ( frame - > line_signal > > 16 ) & 0xf ) ! = ( ( frame - > line_signal > > 12 ) & 0xf )
| | ( ( frame - > line_signal > > 12 ) & 0xf ) ! = ( ( frame - > line_signal > > 8 ) & 0xf )
| | ( ( frame - > line_signal > > 8 ) & 0xf ) ! = ( ( frame - > line_signal > > 4 ) & 0xf )
| | ( ( frame - > line_signal > > 4 ) & 0xf ) ! = ( frame - > line_signal & 0xf ) ) {
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Line signal repetition in frame does not match, ignoring due to corrupt frame. \n " ) ;
break ;
}
2016-02-21 17:13:30 +00:00
switch ( ( frame - > line_signal & 0xf ) ) {
case 5 :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Register Recall is not supported. \n " ) ;
2016-02-21 17:13:30 +00:00
break ;
case 8 :
if ( nmt - > active_state ! = ACTIVE_STATE_VOICE )
break ;
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Received 'MFT in' request. \n " ) ;
2016-02-21 17:13:30 +00:00
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 ;
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Received 'MFT out' request. \n " ) ;
2016-02-21 17:13:30 +00:00
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 ;
2017-07-15 19:49:28 +00:00
if ( ( frame - > digit > > 12 ) ! = 0x00 ) {
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Position information of digit does not match, ignoring due to corrupt frame. \n " ) ;
break ;
}
if ( ( ( frame - > digit > > 8 ) & 0xf ) ! = ( ( frame - > digit > > 4 ) & 0xf )
| | ( ( frame - > digit > > 4 ) & 0xf ) ! = ( frame - > digit & 0xf ) ) {
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Digit repetition in frame does not match, ignoring due to corrupt frame. \n " ) ;
break ;
}
2016-02-21 17:13:30 +00:00
digit = nmt_value2digit ( frame - > digit ) ;
2017-11-15 17:45:56 +00:00
dtmf_encode_set_tone ( & nmt - > dtmf , digit ) ;
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Received (odd) digit %c. \n " , digit ) ;
2016-02-21 17:13:30 +00:00
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 ;
2017-07-15 19:49:28 +00:00
if ( ( frame - > digit > > 12 ) ! = 0xff ) {
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Position information of digit does not match, ignoring due to corrupt frame. \n " ) ;
break ;
}
if ( ( ( frame - > digit > > 8 ) & 0xf ) ! = ( ( frame - > digit > > 4 ) & 0xf )
| | ( ( frame - > digit > > 4 ) & 0xf ) ! = ( frame - > digit & 0xf ) ) {
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Digit repetition in frame does not match, ignoring due to corrupt frame. \n " ) ;
break ;
}
2016-02-21 17:13:30 +00:00
digit = nmt_value2digit ( frame - > digit ) ;
2017-11-15 17:45:56 +00:00
dtmf_encode_set_tone ( & nmt - > dtmf , digit ) ;
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Received (even) digit %c. \n " , digit ) ;
2016-02-21 17:13:30 +00:00
nmt - > mft_num + + ;
break ;
default :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame - > mt ) , nmt_state_name ( nmt - > state ) ) ;
2016-02-21 17:13:30 +00:00
}
}
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 )
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Send 'MFT in acknowledge'. \n " ) ;
2016-02-21 17:13:30 +00:00
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 )
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Send 'MFT out acknowledge'. \n " ) ;
2016-02-21 17:13:30 +00:00
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
*/
2017-07-24 14:18:10 +00:00
void nmt_receive_frame ( nmt_t * nmt , const char * bits , double quality , double level , int frames_elapsed )
2016-02-21 17:13:30 +00:00
{
frame_t frame ;
int rc ;
2017-08-05 08:41:23 +00:00
PDEBUG_CHAN ( DDSP , DEBUG_INFO , " RX Level: %.0f%% Quality=%.0f%% \n " , level * 100.0 , quality * 100.0 ) ;
2016-05-01 08:23:45 +00:00
2017-07-22 08:38:18 +00:00
rc = decode_frame ( nmt - > sysinfo . system , & frame , bits , ( nmt - > sender . loopback ) ? MTX_TO_XX : XX_TO_MTX , ( nmt - > state = = STATE_MT_PAGING ) ) ;
2016-02-21 17:13:30 +00:00
if ( rc < 0 ) {
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , ( nmt - > sender . loopback ) ? DEBUG_NOTICE : DEBUG_DEBUG , " Received invalid frame. \n " ) ;
2016-02-21 17:13:30 +00:00
return ;
}
/* frame counter */
2017-07-24 14:18:10 +00:00
nmt - > rx_frame_count + = frames_elapsed ;
2016-02-21 17:13:30 +00:00
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , ( nmt - > sender . loopback ) ? DEBUG_NOTICE : DEBUG_DEBUG , " Received frame %s \n " , nmt_frame_name ( frame . mt ) ) ;
2016-02-21 17:13:30 +00:00
if ( nmt - > sender . loopback )
return ;
/* MS releases, but this is not the acknowledge of MTX release */
2016-07-20 10:51:25 +00:00
if ( frame . mt = = NMT_MESSAGE_13a
2016-02-21 17:13:30 +00:00
& & ( 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 ;
2016-08-17 16:25:48 +00:00
if ( ! match_subscriber ( nmt - > trans , & frame ) )
2016-02-21 17:13:30 +00:00
return ;
2017-07-15 19:49:28 +00:00
if ( ( ( frame . line_signal > > 16 ) & 0xf ) ! = ( ( frame . line_signal > > 12 ) & 0xf )
| | ( ( frame . line_signal > > 12 ) & 0xf ) ! = ( ( frame . line_signal > > 8 ) & 0xf )
| | ( ( frame . line_signal > > 8 ) & 0xf ) ! = ( ( frame . line_signal > > 4 ) & 0xf )
| | ( ( frame . line_signal > > 4 ) & 0xf ) ! = ( frame . line_signal & 0xf ) ) {
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Line signal repetition in frame does not match, ignoring due to corrupt frame. \n " ) ;
return ;
}
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_INFO , " Received clearing by mobile phone in state %s. \n " , nmt_state_name ( nmt - > state ) ) ;
2016-02-21 17:13:30 +00:00
nmt_new_state ( nmt , STATE_MO_RELEASE ) ;
nmt - > tx_frame_count = 0 ;
nmt_set_dsp_mode ( nmt , DSP_MODE_FRAME ) ;
2016-08-17 16:25:48 +00:00
if ( nmt - > trans - > callref ) {
2016-02-21 17:13:30 +00:00
PDEBUG ( DNMT , DEBUG_INFO , " Release call towards network. \n " ) ;
2017-10-28 05:11:40 +00:00
call_up_release ( nmt - > trans - > callref , CAUSE_NORMAL ) ;
2016-08-17 16:25:48 +00:00
nmt - > trans - > callref = 0 ;
2016-02-21 17:13:30 +00:00
}
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 ;
2016-07-20 09:55:03 +00:00
case STATE_MT_AUTOANSWER :
rx_mt_autoanswer ( nmt , & frame ) ;
break ;
2016-02-21 17:13:30 +00:00
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 :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_DEBUG , " Dropping message %s in state %s \n " , nmt_frame_name ( frame . mt ) , nmt_state_name ( nmt - > state ) ) ;
2016-02-21 17:13:30 +00:00
}
}
/* 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 ;
2016-07-20 09:55:03 +00:00
case STATE_MT_AUTOANSWER :
tx_mt_autoanswer ( nmt , & frame ) ;
break ;
2016-02-21 17:13:30 +00:00
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 */
2016-07-20 10:51:25 +00:00
switch ( frame . mt ) {
2016-02-21 17:13:30 +00:00
case NMT_MESSAGE_1a :
case NMT_MESSAGE_4 :
case NMT_MESSAGE_1b :
case NMT_MESSAGE_30 :
debug = 0 ;
break ;
2016-07-20 10:51:25 +00:00
default :
break ;
2016-02-21 17:13:30 +00:00
}
/* frame sending aborted (e.g. due to audio) */
if ( nmt - > dsp_mode ! = DSP_MODE_FRAME )
return NULL ;
2017-07-22 08:38:18 +00:00
bits = encode_frame ( nmt - > sysinfo . system , & frame , debug ) ;
2016-02-21 17:13:30 +00:00
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_DEBUG , " Sending frame %s. \n " , nmt_frame_name ( frame . mt ) ) ;
2016-02-21 17:13:30 +00:00
return bits ;
}
/*
* call states received from call control
*/
/* Call control starts call towards mobile station. */
2016-07-20 09:58:12 +00:00
int _out_setup ( int callref , const char * caller_id , enum number_type caller_type , const char * dialing , const char * sms )
2016-02-21 17:13:30 +00:00
{
sender_t * sender ;
nmt_t * nmt ;
int i ;
2016-08-17 16:25:48 +00:00
nmt_subscriber_t subscr ;
transaction_t * trans ;
memset ( & subscr , 0 , sizeof ( subscr ) ) ;
2016-02-21 17:13:30 +00:00
/* 1. check if number is invalid, return INVALNUMBER */
2016-08-17 16:25:48 +00:00
if ( dialstring2number ( dialing , & subscr . country , subscr . number ) ) {
2016-02-21 17:13:30 +00:00
inval :
PDEBUG ( DNMT , DEBUG_NOTICE , " Outgoing call to invalid number '%s', rejecting! \n " , dialing ) ;
return - CAUSE_INVALNUMBER ;
}
for ( i = 0 ; i < 6 ; i + + ) {
2016-08-17 16:25:48 +00:00
if ( subscr . number [ i ] < ' 0 ' | | subscr . number [ i ] > ' 9 ' )
2016-02-21 17:13:30 +00:00
goto inval ;
}
/* 2. check if given number is already in a call, return BUSY */
2016-08-17 16:25:48 +00:00
trans = get_transaction_by_number ( & subscr ) ;
if ( trans ) {
2016-02-21 17:13:30 +00:00
PDEBUG ( DNMT , DEBUG_NOTICE , " Outgoing call to busy number, rejecting! \n " ) ;
return - CAUSE_BUSY ;
}
2016-08-17 16:25:48 +00:00
/* 3. check if all paging (calling) channels are busy, return NOCHANNEL */
2016-02-21 17:13:30 +00:00
for ( sender = sender_head ; sender ; sender = sender - > next ) {
nmt = ( nmt_t * ) sender ;
2017-10-28 17:01:27 +00:00
if ( ! is_chan_class_cc ( nmt - > sysinfo . chan_type ) )
continue ;
2016-02-21 17:13:30 +00:00
if ( nmt - > state = = STATE_IDLE )
break ;
}
if ( ! sender ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Outgoing call, but no free calling channel, rejecting! \n " ) ;
return - CAUSE_NOCHANNEL ;
}
2017-11-11 07:10:37 +00:00
if ( ! search_free_tc ( NULL ) ) {
2017-10-28 17:01:27 +00:00
PDEBUG ( DNMT , DEBUG_NOTICE , " Outgoing call, but no free traffic channel, rejecting! \n " ) ;
return - CAUSE_NOCHANNEL ;
}
2016-02-21 17:13:30 +00:00
2016-08-17 16:25:48 +00:00
PDEBUG ( DNMT , DEBUG_INFO , " Call to mobile station, paging station id '%c%s' \n " , subscr . country , subscr . number ) ;
2016-02-21 17:13:30 +00:00
/* 4. trying to page mobile station */
2016-08-17 16:25:48 +00:00
trans = create_transaction ( & subscr ) ;
if ( ! trans ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Failed to create transaction, rejecting! \n " ) ;
return - CAUSE_TEMPFAIL ;
}
trans - > callref = callref ;
2016-07-04 17:52:00 +00:00
if ( sms ) {
2016-08-17 16:25:48 +00:00
strncpy ( trans - > sms_string , sms , sizeof ( trans - > sms_string ) - 1 ) ;
2016-07-04 17:52:00 +00:00
}
2016-07-20 09:58:12 +00:00
if ( caller_type = = TYPE_INTERNATIONAL ) {
2016-08-17 16:25:48 +00:00
trans - > caller_id [ 0 ] = ' + ' ; /* not done by phone */
strncpy ( trans - > caller_id + 1 , caller_id , sizeof ( trans - > caller_id ) - 2 ) ;
2016-07-20 09:58:12 +00:00
} else
2016-08-17 16:25:48 +00:00
strncpy ( trans - > caller_id , caller_id , sizeof ( trans - > caller_id ) - 1 ) ;
trans - > caller_type = caller_type ;
nmt_page ( trans , 1 ) ;
2016-02-21 17:13:30 +00:00
return 0 ;
}
2017-10-28 05:11:40 +00:00
int call_down_setup ( int callref , const char * caller_id , enum number_type caller_type , const char * dialing )
2016-07-04 17:52:00 +00:00
{
2016-07-20 09:58:12 +00:00
return _out_setup ( callref , caller_id , caller_type , dialing , NULL ) ;
2016-07-04 17:52:00 +00:00
}
2016-07-20 09:58:12 +00:00
int sms_out_setup ( char * dialing , const char * caller_id , enum number_type caller_type , const char * sms )
2016-07-04 17:52:00 +00:00
{
2016-07-20 09:58:12 +00:00
return _out_setup ( 0 , caller_id , caller_type , dialing , sms ) ;
2016-07-04 17:52:00 +00:00
}
2016-02-21 17:13:30 +00:00
2017-10-28 05:11:40 +00:00
void call_down_answer ( int __attribute__ ( ( unused ) ) callref )
2017-08-06 09:30:39 +00:00
{
}
2016-02-21 17:13:30 +00:00
/* Call control sends disconnect (with tones).
* An active call stays active , so tones and annoucements can be received
* by mobile station .
*/
2017-10-28 05:11:40 +00:00
void call_down_disconnect ( int callref , int cause )
2016-02-21 17:13:30 +00:00
{
2016-08-17 16:25:48 +00:00
transaction_t * trans ;
2016-02-21 17:13:30 +00:00
nmt_t * nmt ;
PDEBUG ( DNMT , DEBUG_INFO , " Call has been disconnected by network. \n " ) ;
2016-08-17 16:25:48 +00:00
trans = get_transaction_by_callref ( callref ) ;
if ( ! trans ) {
2016-02-21 17:13:30 +00:00
PDEBUG ( DNMT , DEBUG_NOTICE , " Outgoing disconnect, but no callref! \n " ) ;
2017-10-28 05:11:40 +00:00
call_up_release ( callref , CAUSE_INVALCALLREF ) ;
2016-02-21 17:13:30 +00:00
return ;
}
2016-08-17 16:25:48 +00:00
nmt = trans - > nmt ;
if ( ! nmt ) {
2017-10-28 05:11:40 +00:00
call_up_release ( callref , cause ) ;
2016-08-17 16:25:48 +00:00
trans - > callref = 0 ;
destroy_transaction ( trans ) ;
return ;
}
2016-02-21 17:13:30 +00:00
2016-07-20 10:50:22 +00:00
/* Release when not active and not waiting for answer */
if ( nmt - > state = = STATE_ACTIVE | | nmt - > state = = STATE_MO_COMPLETE )
2016-02-21 17:13:30 +00:00
return ;
switch ( nmt - > state ) {
case STATE_MT_RINGING :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Outgoing disconnect, during ringing, releasing! \n " ) ;
2016-08-17 16:25:48 +00:00
trans - > callref = 0 ;
2016-02-21 17:13:30 +00:00
nmt_release ( nmt ) ;
break ;
default :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Outgoing disconnect, when phone is in call setup, releasing! \n " ) ;
2016-08-17 16:25:48 +00:00
trans - > callref = 0 ;
2016-02-21 17:13:30 +00:00
nmt_release ( nmt ) ;
break ;
}
2017-10-28 05:11:40 +00:00
call_up_release ( callref , cause ) ;
2016-02-21 17:13:30 +00:00
}
/* Call control releases call toward mobile station. */
2017-10-28 05:11:40 +00:00
void call_down_release ( int callref , int __attribute__ ( ( unused ) ) cause )
2016-02-21 17:13:30 +00:00
{
2016-08-17 16:25:48 +00:00
transaction_t * trans ;
2016-02-21 17:13:30 +00:00
nmt_t * nmt ;
PDEBUG ( DNMT , DEBUG_INFO , " Call has been released by network, releasing call. \n " ) ;
2016-08-17 16:25:48 +00:00
trans = get_transaction_by_callref ( callref ) ;
if ( ! trans ) {
2016-02-21 17:13:30 +00:00
PDEBUG ( DNMT , DEBUG_NOTICE , " Outgoing release, but no callref! \n " ) ;
/* don't send release, because caller already released */
return ;
}
2016-08-17 16:25:48 +00:00
nmt = trans - > nmt ;
trans - > callref = 0 ;
2016-02-21 17:13:30 +00:00
2016-08-17 16:25:48 +00:00
if ( ! nmt ) {
destroy_transaction ( trans ) ;
return ;
}
2016-02-21 17:13:30 +00:00
switch ( nmt - > state ) {
case STATE_ACTIVE :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Outgoing release, during active call, releasing! \n " ) ;
2016-02-21 17:13:30 +00:00
nmt_release ( nmt ) ;
break ;
case STATE_MT_RINGING :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Outgoing release, during ringing, releasing! \n " ) ;
2016-02-21 17:13:30 +00:00
nmt_release ( nmt ) ;
break ;
default :
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Outgoing release, when phone is in call setup, releasing! \n " ) ;
2016-02-21 17:13:30 +00:00
nmt_release ( nmt ) ;
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 )
2016-02-21 17:13:30 +00:00
{
2016-08-17 16:25:48 +00:00
transaction_t * trans ;
2016-02-21 17:13:30 +00:00
nmt_t * nmt ;
2016-08-17 16:25:48 +00:00
trans = get_transaction_by_callref ( callref ) ;
if ( ! trans )
return ;
nmt = trans - > nmt ;
if ( ! nmt )
2016-02-21 17:13:30 +00:00
return ;
if ( nmt - > dsp_mode = = DSP_MODE_AUDIO | | nmt - > dsp_mode = = DSP_MODE_DTMF ) {
2017-01-27 15:57:34 +00:00
sample_t up [ ( int ) ( ( double ) count * nmt - > sender . srstate . factor + 0.5 ) + 10 ] ;
2016-06-20 17:37:56 +00:00
if ( nmt - > compandor )
2016-02-21 17:13:30 +00:00
compress_audio ( & nmt - > cstate , samples , count ) ;
count = samplerate_upsample ( & nmt - > sender . srstate , samples , count , up ) ;
2017-01-04 13:14:02 +00:00
jitter_save ( & nmt - > sender . dejitter , up , count ) ;
2016-02-21 17:13:30 +00:00
}
}
2020-01-12 06:54:25 +00:00
void call_down_clock ( void ) { }
2016-07-04 17:52:00 +00:00
/*
* SMS layer messages
*/
/* SMS layer releases */
void sms_release ( nmt_t * nmt )
{
2016-08-02 09:03:03 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Outgoing release, by SMS layer! \n " ) ;
2016-07-04 17:52:00 +00:00
nmt_release ( nmt ) ;
}
2016-10-07 06:55:18 +00:00
int sms_submit ( nmt_t * nmt , uint8_t ref , const char * orig_address , uint8_t __attribute__ ( ( unused ) ) orig_type , uint8_t __attribute__ ( ( unused ) ) orig_plan , int __attribute__ ( ( unused ) ) msg_ref , const char * dest_address , uint8_t __attribute__ ( ( unused ) ) dest_type , uint8_t __attribute__ ( ( unused ) ) dest_plan , const char * message )
2016-07-04 17:52:00 +00:00
{
2016-07-10 09:15:29 +00:00
char sms [ 512 ] ;
if ( ! orig_address [ 0 ] )
2016-08-17 16:25:48 +00:00
orig_address = & nmt - > trans - > subscriber . country ;
2016-07-10 09:15:29 +00:00
2016-10-07 06:55:18 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Received SMS from '%s' to '%s' (ref=%d) \n " , orig_address , dest_address , ref ) ;
2016-07-04 17:52:00 +00:00
printf ( " SMS received '%s' -> '%s': %s \n " , orig_address , dest_address , message ) ;
2016-07-10 09:15:29 +00:00
snprintf ( sms , sizeof ( sms ) - 1 , " %s,%s,%s " , orig_address , dest_address , message ) ;
sms [ sizeof ( sms ) - 1 ] = ' \0 ' ;
2016-07-04 17:52:00 +00:00
2016-07-10 09:15:29 +00:00
return submit_sms ( sms ) ;
2016-07-04 17:52:00 +00:00
}
void sms_deliver_report ( nmt_t * nmt , uint8_t ref , int error , uint8_t cause )
{
2016-10-07 06:55:18 +00:00
PDEBUG_CHAN ( DNMT , DEBUG_NOTICE , " Got SMS deliver report (ref=%d) \n " , ref ) ;
2016-07-04 17:52:00 +00:00
if ( error )
printf ( " SMS failed! (cause=%d) \n " , cause ) ;
else {
sms_ref + + ;
printf ( " SMS sent! \n " ) ;
}
}
/* application sends ud a message, we need to deliver */
void deliver_sms ( const char * sms )
{
2016-07-20 09:58:12 +00:00
int rc ;
char buffer [ strlen ( sms ) + 1 ] , * p = buffer , * caller_id , * number , * message ;
enum number_type caller_type ;
strcpy ( buffer , sms ) ;
caller_id = strsep ( & p , " , " ) ;
number = strsep ( & p , " , " ) ;
message = p ;
if ( ! caller_id | | ! number | | ! message ) {
inval :
PDEBUG ( DNMT , DEBUG_NOTICE , " Given SMS MUST be in the following format: [i|n|s|u]<caller ID>,<7 digits number>,<message with comma and spaces> (i, n, s, u indicate the type of number) \n " ) ;
return ;
}
if ( strlen ( number ) ! = 7 ) {
PDEBUG ( DNMT , DEBUG_NOTICE , " Given number must be 7 digits \n " ) ;
goto inval ;
}
2016-07-04 17:52:00 +00:00
2016-07-20 09:58:12 +00:00
switch ( caller_id [ 0 ] ) {
case ' \0 ' :
caller_type = TYPE_NOTAVAIL ;
2016-07-04 17:52:00 +00:00
break ;
2016-07-20 09:58:12 +00:00
case ' i ' :
caller_type = TYPE_INTERNATIONAL ;
caller_id + + ;
break ;
case ' n ' :
caller_type = TYPE_NATIONAL ;
caller_id + + ;
break ;
case ' s ' :
caller_type = TYPE_SUBSCRIBER ;
caller_id + + ;
break ;
case ' u ' :
caller_type = TYPE_UNKNOWN ;
caller_id + + ;
break ;
default :
caller_type = TYPE_UNKNOWN ;
2016-07-04 17:52:00 +00:00
}
2016-07-20 09:58:12 +00:00
2016-07-31 12:49:43 +00:00
PDEBUG ( DNMT , DEBUG_INFO , " SMS from '%s' for subscriber '%s' with message '%s' \n " , caller_id , number , message ) ;
printf ( " SMS sending '%s' -> '%s': %s \n " , caller_id , number , message ) ;
2016-07-04 17:52:00 +00:00
2016-07-20 09:58:12 +00:00
rc = sms_out_setup ( number , caller_id , caller_type , message ) ;
2016-07-04 17:52:00 +00:00
if ( rc < 0 ) {
PDEBUG ( DNMT , DEBUG_INFO , " SMS delivery failed with cause '%d' \n " , - rc ) ;
return ;
}
}
2016-08-02 07:03:46 +00:00
void dump_info ( void ) { }
2016-07-04 17:52:00 +00:00