2016-05-01 17:51:56 +00:00
/* AMPS 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-12-06 16:45:48 +00:00
/*
* Notes on frames and scheduling :
*
* If the amps - > dsp_mode is set to transmit frames , fsk_frame ( ) at dsp . c code
* requests frames , modulates them and forwards them to sound device . Whenever
* the dsp . c code requests frame ( if no frame exists or frame had been sent ) ,
* it calls amps_encode_frame_focc ( ) or amps_encode_frame_fvc ( ) of frame . c .
* There it generates a sequence of frames ( message train ) . If no sequence is
* transmitted or a new sequence starts , amps_tx_frame_focc ( ) or
* amps_tx_frame_fvc ( ) of amps . c is called . There it sets message data and
* other states according to the current trans - > state .
*
* If a frame is received by dsp . c code , amps_decode_frame ( ) at frame . c is
* called . There the bits are decoded and messages are assembled from multiple
* frames . Then amps_rx_recc ( ) at amps . c is called , so the received messages
* are processed .
*/
2016-12-09 15:34:33 +00:00
# define CHAN amps->sender.kanal
2016-05-01 17:51:56 +00:00
# include <stdio.h>
# include <stdint.h>
# include <stdlib.h>
# include <string.h>
# include <errno.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-18 07:33:07 +00:00
# include "../libmobile/call.h"
2017-11-17 08:01:44 +00:00
# include "../libmncc/cause.h"
2016-05-01 17:51:56 +00:00
# include "amps.h"
# include "dsp.h"
# include "frame.h"
2017-05-30 10:49:40 +00:00
# include "stations.h"
# include "main.h"
2016-05-01 17:51:56 +00:00
/* Uncomment this to test SAT via loopback */
//#define DEBUG_VC
# define SAT_TO1 5.0 /* 5 sec to detect after setup */
# define SAT_TO2 5.0 /* 5 sec lost until abort (specs say 5) */
# define PAGE_TRIES 2 /* how many times to page the phone */
2016-10-30 08:25:45 +00:00
# define PAGE_TO1 8.0 /* max time to wait for paging reply */
# define PAGE_TO2 4.0 /* max time to wait for last paging reply */
2016-05-01 17:51:56 +00:00
# define ALERT_TO 60.0 /* max time to wait for answer */
# define RELEASE_TIMER 5.0 /* max time to send release messages */
/* Call reference for calls from mobile station to network
This offset of 0x400000000 is required for MNCC interface . */
static int new_callref = 0x40000000 ;
/* Convert channel number to frequency number of base station.
Set ' uplink ' to 1 to get frequency of mobile station . */
double amps_channel2freq ( int channel , int uplink )
{
double freq ;
2017-05-30 10:49:40 +00:00
if ( ! tacs ) {
2017-10-12 17:43:15 +00:00
if ( uplink = = 2 )
return - 45.000 * 1e6 ;
2017-05-30 10:49:40 +00:00
/* 832 channels, 990 not used, see TIA/EIA-136-110 */
if ( channel < 1 | | channel > 1023 | | ( channel > 799 & & channel < 991 ) )
return 0 ;
2016-05-01 17:51:56 +00:00
2017-05-30 10:49:40 +00:00
if ( channel > = 990 ) // 990 is not used
channel - = 1023 ;
2016-05-01 17:51:56 +00:00
2017-05-30 10:49:40 +00:00
freq = 870.030 + ( channel - 1 ) * 0.030 ;
2017-08-27 16:02:43 +00:00
if ( uplink )
freq - = 45.000 ;
} else if ( ! jtacs ) {
2017-10-12 17:43:15 +00:00
if ( uplink = = 2 )
return - 45.000 * 1e6 ;
2017-05-30 10:49:40 +00:00
/* 600 channels */
if ( channel < 1 | | channel > 600 )
return 0 ;
freq = 935.0125 + ( channel - 1 ) * 0.025 ;
2016-05-01 17:51:56 +00:00
2017-08-27 16:02:43 +00:00
if ( uplink )
freq - = 45.000 ;
} else {
2017-10-12 17:43:15 +00:00
if ( uplink = = 2 )
return - 55.000 * 1e6 ;
2017-08-27 16:02:43 +00:00
/* 799 channels */
if ( channel < 1 | | channel > 799 )
return 0 ;
freq = 860.0125 + ( channel - 1 ) * 0.025 ;
if ( uplink )
freq + = 55.000 ;
}
2016-05-01 17:51:56 +00:00
2017-01-04 13:14:02 +00:00
return freq * 1e6 ;
2016-05-01 17:51:56 +00:00
}
enum amps_chan_type amps_channel2type ( int channel )
{
2017-05-30 10:49:40 +00:00
if ( ! tacs ) {
if ( channel > = 313 & & channel < = 354 )
return CHAN_TYPE_CC ;
} else {
if ( channel > = 23 & & channel < = 43 )
return CHAN_TYPE_CC ;
if ( channel > = 323 & & channel < = 343 )
return CHAN_TYPE_CC ;
}
2016-05-01 17:51:56 +00:00
return CHAN_TYPE_VC ;
}
2017-05-21 14:40:10 +00:00
const char * amps_channel2band ( int channel )
2016-05-01 17:51:56 +00:00
{
2017-05-30 10:49:40 +00:00
if ( ! tacs ) {
if ( channel > = 991 & & channel < = 1023 )
return " A'' " ;
if ( channel > = 1 & & channel < = 333 )
return " A " ;
if ( channel > = 334 & & channel < = 666 )
return " B " ;
if ( channel > = 667 & & channel < = 716 )
return " A' " ;
if ( channel > = 717 & & channel < = 799 )
return " B' " ;
} else {
if ( channel > = 1 & & channel < = 300 )
return " A " ;
if ( channel > = 301 & & channel < = 600 )
return " B " ;
}
2016-05-01 17:51:56 +00:00
2017-05-21 14:40:10 +00:00
return " <invalid> " ;
2016-05-01 17:51:56 +00:00
}
static inline int digit2binary ( int digit )
{
if ( digit = = ' 0 ' )
return 10 ;
return digit - ' 0 ' ;
}
static inline int binary2digit ( int binary )
{
if ( binary = = 10 )
return ' 0 ' ;
return binary + ' 0 ' ;
}
2017-05-30 10:49:40 +00:00
/* AMPS: convert NPA-NXX-XXXX to MIN1 and MIN2
2016-05-01 17:51:56 +00:00
* NPA = numbering plan area ( MIN2 )
* NXX = mobile exchange code
* XXXX = telephone number within the exchange
*/
2017-05-30 10:49:40 +00:00
/* TACS: convert AREA-XXXXXX to MIN1 and MIN2
* AREA = 3 + 1 Digits
* XXXXXX = telephone number
*/
2016-05-01 17:51:56 +00:00
void amps_number2min ( const char * number , uint32_t * min1 , uint16_t * min2 )
{
int nlen = strlen ( number ) ;
int i ;
if ( nlen ! = 10 ) {
2019-07-15 19:14:52 +00:00
fprintf ( stderr , " illegal length %d. Must be 10, aborting! " , nlen ) ;
2016-05-01 17:51:56 +00:00
abort ( ) ;
}
for ( i = 0 ; i < nlen ; i + + ) {
if ( number [ i ] < ' 0 ' | | number [ i ] > ' 9 ' ) {
fprintf ( stderr , " illegal number %s. Must consists only of digits 0..9, aborting! " , number ) ;
abort ( ) ;
}
}
/* MIN2 */
if ( nlen = = 10 ) {
* min2 = digit2binary ( number [ 0 ] ) * 100 + digit2binary ( number [ 1 ] ) * 10 + digit2binary ( number [ 2 ] ) - 111 ;
number + = 3 ;
nlen - = 3 ;
}
2017-05-30 10:49:40 +00:00
if ( ! tacs ) {
/* MIN1 */
* min1 = ( ( uint32_t ) ( digit2binary ( number [ 0 ] ) * 100 + digit2binary ( number [ 1 ] ) * 10 + digit2binary ( number [ 2 ] ) - 111 ) ) < < 14 ;
* min1 | = digit2binary ( number [ 3 ] ) < < 10 ;
* min1 | = digit2binary ( number [ 4 ] ) * 100 + digit2binary ( number [ 5 ] ) * 10 + digit2binary ( number [ 6 ] ) - 111 ;
} else {
/* MIN1 */
2018-04-19 19:08:45 +00:00
* min1 = digit2binary ( number [ 0 ] ) < < 20 ;
2017-05-30 10:49:40 +00:00
* min1 | = ( digit2binary ( number [ 1 ] ) * 100 + digit2binary ( number [ 2 ] ) * 10 + digit2binary ( number [ 3 ] ) - 111 ) < < 10 ;
* min1 | = digit2binary ( number [ 4 ] ) * 100 + digit2binary ( number [ 5 ] ) * 10 + digit2binary ( number [ 6 ] ) - 111 ;
}
2016-05-01 17:51:56 +00:00
}
2017-05-30 10:49:40 +00:00
/* AMPS: convert MIN1 and MIN2 to NPA-NXX-XXXX
*/
/* TACS: convert MIN1 and MIN2 to AREA-XXXXXXX
2016-05-01 17:51:56 +00:00
*/
const char * amps_min22number ( uint16_t min2 )
{
static char number [ 4 ] ;
/* MIN2 */
if ( min2 > 999 )
strcpy ( number , " ??? " ) ;
else {
number [ 0 ] = binary2digit ( ( min2 / 100 ) + 1 ) ;
number [ 1 ] = binary2digit ( ( ( min2 / 10 ) % 10 ) + 1 ) ;
number [ 2 ] = binary2digit ( ( min2 % 10 ) + 1 ) ;
}
number [ 3 ] = ' \0 ' ;
return number ;
}
const char * amps_min12number ( uint32_t min1 )
{
static char number [ 8 ] ;
2017-05-30 10:49:40 +00:00
if ( ! tacs ) {
/* MIN1 */
if ( ( min1 > > 14 ) > 999 )
strcpy ( number , " ??? " ) ;
else {
number [ 0 ] = binary2digit ( ( ( min1 > > 14 ) / 100 ) + 1 ) ;
number [ 1 ] = binary2digit ( ( ( ( min1 > > 14 ) / 10 ) % 10 ) + 1 ) ;
number [ 2 ] = binary2digit ( ( ( min1 > > 14 ) % 10 ) + 1 ) ;
}
if ( ( ( min1 > > 10 ) & 0xf ) < 1 | | ( ( min1 > > 10 ) & 0xf ) > 10 )
number [ 3 ] = ' ? ' ;
else
number [ 3 ] = binary2digit ( ( min1 > > 10 ) & 0xf ) ;
if ( ( min1 & 0x3ff ) > 999 )
strcpy ( number + 4 , " ??? " ) ;
else {
number [ 4 ] = binary2digit ( ( ( min1 & 0x3ff ) / 100 ) + 1 ) ;
number [ 5 ] = binary2digit ( ( ( ( min1 & 0x3ff ) / 10 ) % 10 ) + 1 ) ;
number [ 6 ] = binary2digit ( ( ( min1 & 0x3ff ) % 10 ) + 1 ) ;
}
} else {
/* MIN1 */
2018-04-19 19:08:45 +00:00
if ( ( min1 > > 20 ) < 1 | | ( min1 > > 20 ) > 10 )
2017-05-30 10:49:40 +00:00
number [ 0 ] = ' ? ' ;
else
2018-04-19 19:08:45 +00:00
number [ 0 ] = binary2digit ( min1 > > 20 ) ;
2017-05-30 10:49:40 +00:00
if ( ( ( min1 > > 10 ) & 0x3ff ) > 999 )
strcpy ( number + 1 , " ??? " ) ;
else {
number [ 1 ] = binary2digit ( ( ( ( min1 > > 10 ) & 0x3ff ) / 100 ) + 1 ) ;
number [ 2 ] = binary2digit ( ( ( ( ( min1 > > 10 ) & 0x3ff ) / 10 ) % 10 ) + 1 ) ;
number [ 3 ] = binary2digit ( ( ( ( min1 > > 10 ) & 0x3ff ) % 10 ) + 1 ) ;
}
if ( ( min1 & 0x3ff ) > 999 )
strcpy ( number + 4 , " ??? " ) ;
else {
number [ 4 ] = binary2digit ( ( ( min1 & 0x3ff ) / 100 ) + 1 ) ;
number [ 5 ] = binary2digit ( ( ( ( min1 & 0x3ff ) / 10 ) % 10 ) + 1 ) ;
number [ 6 ] = binary2digit ( ( ( min1 & 0x3ff ) % 10 ) + 1 ) ;
}
2016-05-01 17:51:56 +00:00
}
number [ 7 ] = ' \0 ' ;
return number ;
}
const char * amps_min2number ( uint32_t min1 , uint16_t min2 )
{
static char number [ 11 ] ;
sprintf ( number , " %s%s " , amps_min22number ( min2 ) , amps_min12number ( min1 ) ) ;
return number ;
}
/* encode ESN */
void amps_encode_esn ( uint32_t * esn , uint8_t mfr , uint32_t serial )
{
* esn = ( ( ( uint32_t ) mfr ) < < 24 ) | ( serial & 0xffffff ) ;
}
/* decode ESN */
void amps_decode_esn ( uint32_t esn , uint8_t * mfr , uint32_t * serial )
{
* mfr = esn > > 24 ;
* serial = esn & 0xffffff ;
}
const char * amps_scm ( uint8_t scm )
{
static char text [ 64 ] ;
2017-05-21 14:40:10 +00:00
sprintf ( text , " Class %d / %sontinuous / %d MHz " , ( ( scm & 16 ) > > 2 ) + ( scm & 3 ) + 1 , ( scm & 4 ) ? " Disc " : " C " , ( scm & 8 ) ? 25 : 20 ) ;
2016-05-01 17:51:56 +00:00
return text ;
}
2017-05-21 14:40:10 +00:00
const char * amps_mpci ( uint8_t mpci )
{
switch ( mpci ) {
case 0 :
return " TIA/EIA-553 or IS-54A mobile station " ;
case 1 :
return " TIA/EIA-627 dual-mode mobile station " ;
case 2 :
return " reserved (see TIA/EIA IS-95) " ;
case 3 :
return " EIATIA/EIA-136 dual-mode mobile station " ;
default :
return " MPCI INVALID, PLEASE FIX! " ;
}
}
2016-05-01 17:51:56 +00:00
const char * amps_state_name ( enum amps_state state )
{
static char invalid [ 16 ] ;
switch ( state ) {
case STATE_NULL :
return " (NULL) " ;
case STATE_IDLE :
return " IDLE " ;
case STATE_BUSY :
return " BUSY " ;
}
sprintf ( invalid , " invalid(%d) " , state ) ;
return invalid ;
}
2017-05-25 16:43:54 +00:00
void amps_display_status ( void )
{
sender_t * sender ;
amps_t * amps ;
transaction_t * trans ;
display_status_start ( ) ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
amps = ( amps_t * ) sender ;
display_status_channel ( amps - > sender . kanal , chan_type_short_name ( amps - > chan_type ) , amps_state_name ( amps - > state ) ) ;
for ( trans = amps - > trans_list ; trans ; trans = trans - > next )
display_status_subscriber ( amps_min2number ( trans - > min1 , trans - > min2 ) , trans_short_state_name ( trans - > state ) ) ;
}
display_status_end ( ) ;
}
2016-05-01 17:51:56 +00:00
static void amps_new_state ( amps_t * amps , enum amps_state new_state )
{
if ( amps - > state = = new_state )
return ;
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_DEBUG , " State change: %s -> %s \n " , amps_state_name ( amps - > state ) , amps_state_name ( new_state ) ) ;
2016-05-01 17:51:56 +00:00
amps - > state = new_state ;
2017-05-25 16:43:54 +00:00
amps_display_status ( ) ;
2016-05-01 17:51:56 +00:00
}
static struct amps_channels {
enum amps_chan_type chan_type ;
const char * short_name ;
const char * long_name ;
} amps_channels [ ] = {
{ CHAN_TYPE_CC , " CC " , " control channel " } ,
{ CHAN_TYPE_CC , " PC " , " paging channel " } ,
{ CHAN_TYPE_CC_PC , " CC/PC " , " combined control & paging channel " } ,
{ CHAN_TYPE_VC , " VC " , " voice channel " } ,
{ CHAN_TYPE_CC_PC_VC , " CC/PC/VC " , " combined control & paging & voice channel " } ,
{ 0 , NULL , NULL }
} ;
void amps_channel_list ( void )
{
int i ;
printf ( " Type \t \t Description \n " ) ;
printf ( " ------------------------------------------------------------------------ \n " ) ;
for ( i = 0 ; amps_channels [ i ] . long_name ; i + + )
printf ( " %s%s \t %s \n " , amps_channels [ i ] . short_name , ( strlen ( amps_channels [ i ] . short_name ) > = 8 ) ? " " : " \t " , amps_channels [ i ] . long_name ) ;
}
int amps_channel_by_short_name ( const char * short_name )
{
int i ;
for ( i = 0 ; amps_channels [ i ] . short_name ; i + + ) {
if ( ! strcasecmp ( amps_channels [ i ] . short_name , short_name ) ) {
PDEBUG ( DAMPS , DEBUG_INFO , " Selecting channel '%s' = %s \n " , amps_channels [ i ] . short_name , amps_channels [ i ] . long_name ) ;
return amps_channels [ i ] . chan_type ;
}
}
return - 1 ;
}
const char * chan_type_short_name ( enum amps_chan_type chan_type )
{
int i ;
for ( i = 0 ; amps_channels [ i ] . short_name ; i + + ) {
if ( amps_channels [ i ] . chan_type = = chan_type )
return amps_channels [ i ] . short_name ;
}
return " invalid " ;
}
const char * chan_type_long_name ( enum amps_chan_type chan_type )
{
int i ;
for ( i = 0 ; amps_channels [ i ] . long_name ; i + + ) {
if ( amps_channels [ i ] . chan_type = = chan_type )
return amps_channels [ i ] . long_name ;
}
return " invalid " ;
}
2016-10-30 08:25:45 +00:00
static amps_t * search_channel ( int channel )
{
sender_t * sender ;
amps_t * amps ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
2019-07-20 16:11:17 +00:00
if ( atoi ( sender - > kanal ) ! = channel )
2016-10-30 08:25:45 +00:00
continue ;
amps = ( amps_t * ) sender ;
if ( amps - > state = = STATE_IDLE )
return amps ;
}
return NULL ;
}
2016-05-01 17:51:56 +00:00
static amps_t * search_free_vc ( void )
{
sender_t * sender ;
amps_t * amps , * cc_pc_vc = NULL ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
amps = ( amps_t * ) sender ;
if ( amps - > state ! = STATE_IDLE )
continue ;
2016-10-30 08:25:45 +00:00
/* return first free voice channel */
2016-05-01 17:51:56 +00:00
if ( amps - > chan_type = = CHAN_TYPE_VC )
return amps ;
2016-10-30 08:25:45 +00:00
/* remember combined voice/control/paging channel as second alternative */
2016-05-01 17:51:56 +00:00
if ( amps - > chan_type = = CHAN_TYPE_CC_PC_VC )
cc_pc_vc = amps ;
}
return cc_pc_vc ;
}
static amps_t * search_pc ( void )
{
sender_t * sender ;
amps_t * amps ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
amps = ( amps_t * ) sender ;
if ( amps - > state ! = STATE_IDLE )
continue ;
if ( amps - > chan_type = = CHAN_TYPE_PC )
return amps ;
if ( amps - > chan_type = = CHAN_TYPE_CC_PC )
return amps ;
if ( amps - > chan_type = = CHAN_TYPE_CC_PC_VC )
return amps ;
}
return NULL ;
}
/* Create transceiver instance and link to a list. */
2020-06-14 18:59:41 +00:00
int amps_create ( const char * kanal , enum amps_chan_type chan_type , const char * audiodev , int use_sdr , int samplerate , double rx_gain , double tx_gain , int pre_emphasis , int de_emphasis , const char * write_rx_wave , const char * write_tx_wave , const char * read_rx_wave , const char * read_tx_wave , amps_si * si , uint16_t sid , uint8_t sat , int polarity , int tolerant , int loopback )
2016-05-01 17:51:56 +00:00
{
sender_t * sender ;
amps_t * amps ;
int rc ;
enum amps_chan_type ct ;
2017-05-21 14:40:10 +00:00
const char * band ;
2016-05-01 17:51:56 +00:00
/* check for channel number */
2019-07-20 16:11:17 +00:00
if ( amps_channel2freq ( atoi ( kanal ) , 0 ) = = 0 ) {
PDEBUG ( DAMPS , DEBUG_ERROR , " Channel number %s invalid. \n " , kanal ) ;
2016-05-01 17:51:56 +00:00
return - EINVAL ;
}
2016-10-30 08:25:45 +00:00
/* no paging channel (without control channel) support */
if ( chan_type = = CHAN_TYPE_PC ) {
PDEBUG ( DAMPS , DEBUG_ERROR , " Dedicated paging channel currently not supported. Please select CC/PC or CC/PC/VC instead. \n " ) ;
return - EINVAL ;
}
2016-05-01 17:51:56 +00:00
/* check if there is only one paging channel */
if ( chan_type = = CHAN_TYPE_PC | | chan_type = = CHAN_TYPE_CC_PC | | chan_type = = CHAN_TYPE_CC_PC_VC ) {
for ( sender = sender_head ; sender ; sender = sender - > next ) {
amps = ( amps_t * ) sender ;
2016-10-30 08:25:45 +00:00
if ( amps - > chan_type = = CHAN_TYPE_PC | | amps - > chan_type = = CHAN_TYPE_CC_PC | | amps - > chan_type = = CHAN_TYPE_CC_PC_VC ) {
2016-05-01 17:51:56 +00:00
PDEBUG ( DAMPS , DEBUG_ERROR , " Only one paging channel is currently supported. Please check your channel types. \n " ) ;
return - EINVAL ;
}
}
}
/* check if channel type matches channel number */
2019-07-20 16:11:17 +00:00
ct = amps_channel2type ( atoi ( kanal ) ) ;
2016-05-01 17:51:56 +00:00
if ( ct = = CHAN_TYPE_CC & & chan_type ! = CHAN_TYPE_PC & & chan_type ! = CHAN_TYPE_CC_PC & & chan_type ! = CHAN_TYPE_CC_PC_VC ) {
2019-07-20 16:11:17 +00:00
PDEBUG ( DAMPS , DEBUG_NOTICE , " Channel number %s belongs to a control channel, but your channel type '%s' requires to be on a voice channel number. Some phone may reject this, but all my phones don't. \n " , kanal , chan_type_long_name ( chan_type ) ) ;
2016-05-01 17:51:56 +00:00
}
if ( ct = = CHAN_TYPE_VC & & chan_type ! = CHAN_TYPE_VC ) {
2019-07-20 16:11:17 +00:00
PDEBUG ( DAMPS , DEBUG_ERROR , " Channel number %s belongs to a voice channel, but your channel type '%s' requires to be on a control channel number. Please use correct channel. \n " , kanal , chan_type_long_name ( chan_type ) ) ;
2016-05-01 17:51:56 +00:00
return - EINVAL ;
}
/* check if sid machtes channel band */
2019-07-20 16:11:17 +00:00
band = amps_channel2band ( atoi ( kanal ) ) ;
2017-05-21 14:40:10 +00:00
if ( band [ 0 ] = = ' A ' & & ( sid & 1 ) = = 0 & & chan_type ! = CHAN_TYPE_VC ) {
2019-07-20 16:11:17 +00:00
PDEBUG ( DAMPS , DEBUG_ERROR , " Channel number %s belongs to system A, but your %s %d is even and belongs to system B. Please give odd %s. \n " , kanal , ( ! tacs ) ? " SID " : " AID " , sid , ( ! tacs ) ? " SID " : " AID " ) ;
2016-05-01 17:51:56 +00:00
return - EINVAL ;
}
2017-05-21 14:40:10 +00:00
if ( band [ 0 ] = = ' B ' & & ( sid & 1 ) = = 1 & & chan_type ! = CHAN_TYPE_VC ) {
2019-07-20 16:11:17 +00:00
PDEBUG ( DAMPS , DEBUG_ERROR , " Channel number %s belongs to system B, but your %s %d is odd and belongs to system A. Please give even %s. \n " , kanal , ( ! tacs ) ? " SID " : " AID " , sid , ( ! tacs ) ? " SID " : " AID " ) ;
2016-05-01 17:51:56 +00:00
return - EINVAL ;
}
/* check if we use combined voice channel hack */
if ( chan_type = = CHAN_TYPE_CC_PC_VC ) {
2017-01-08 11:10:29 +00:00
PDEBUG ( DAMPS , DEBUG_NOTICE , " You selected '%s'. This is a hack, but the only way to use control channel and voice channel on one transceiver. Some phones may reject this, but all my phones don't. \n " , chan_type_long_name ( chan_type ) ) ;
2016-05-01 17:51:56 +00:00
}
2017-05-21 14:40:10 +00:00
/* check if we selected a voice channel that i outside 20 MHz band */
2019-07-20 16:11:17 +00:00
if ( chan_type = = CHAN_TYPE_VC & & atoi ( kanal ) > 666 ) {
PDEBUG ( DAMPS , DEBUG_NOTICE , " You selected '%s' on channel #%s. Older phones do not support channels above #666. \n " , chan_type_long_name ( chan_type ) , kanal ) ;
2017-05-21 14:40:10 +00:00
}
2016-05-01 17:51:56 +00:00
amps = calloc ( 1 , sizeof ( amps_t ) ) ;
if ( ! amps ) {
PDEBUG ( DAMPS , DEBUG_ERROR , " No memory! \n " ) ;
return - ENOMEM ;
}
2019-07-20 16:11:17 +00:00
PDEBUG ( DAMPS , DEBUG_DEBUG , " Creating 'AMPS' instance for channel = %s of band %s (sample rate %d). \n " , kanal , band , samplerate ) ;
2016-05-01 17:51:56 +00:00
/* init general part of transceiver */
2020-06-14 18:59:41 +00:00
rc = sender_create ( & amps - > sender , kanal , amps_channel2freq ( atoi ( kanal ) , 0 ) , amps_channel2freq ( atoi ( kanal ) , 1 ) , audiodev , use_sdr , samplerate , rx_gain , tx_gain , 0 , 0 , write_rx_wave , write_tx_wave , read_rx_wave , read_tx_wave , loopback , PAGING_SIGNAL_NONE ) ;
2016-05-01 17:51:56 +00:00
if ( rc < 0 ) {
PDEBUG ( DAMPS , DEBUG_ERROR , " Failed to init transceiver process! \n " ) ;
goto error ;
}
amps - > chan_type = chan_type ;
memcpy ( & amps - > si , si , sizeof ( amps - > si ) ) ;
amps - > sat = sat ;
2018-08-31 18:05:38 +00:00
if ( polarity < 0 )
amps - > flip_polarity = 1 ;
2016-05-01 17:51:56 +00:00
amps - > pre_emphasis = pre_emphasis ;
amps - > de_emphasis = de_emphasis ;
2019-07-15 19:14:52 +00:00
/* the AMPS uses a frequency rage of 300..3000 Hz, but we still use the default low pass filter, which is not too far above */
2018-01-20 14:49:19 +00:00
rc = init_emphasis ( & amps - > estate , samplerate , CUT_OFF_EMPHASIS_DEFAULT , CUT_OFF_HIGHPASS_DEFAULT , CUT_OFF_LOWPASS_DEFAULT ) ;
2016-05-01 17:51:56 +00:00
if ( rc < 0 )
goto error ;
2018-08-31 18:05:38 +00:00
/* init audio processing */
rc = dsp_init_sender ( amps , tolerant ) ;
if ( rc < 0 ) {
PDEBUG ( DAMPS , DEBUG_ERROR , " Failed to init audio processing! \n " ) ;
goto error ;
}
2016-05-01 17:51:56 +00:00
/* go into idle state */
amps_go_idle ( amps ) ;
# ifdef DEBUG_VC
uint32_t min1 ;
uint16_t min2 ;
amps_number2min ( " 1234567890 " , & min1 , & min2 ) ;
2017-10-02 10:24:57 +00:00
transaction_t __attribute__ ( ( __unused__ ) ) * trans = create_transaction ( amps , TRANS_CALL_MO_ASSIGN , min1 , min2 , 0 , 0 , 0 , amps - > sender . kanal ) ;
// amps_new_state(amps, STATE_BUSY);
2016-05-01 17:51:56 +00:00
# endif
2019-07-20 16:11:17 +00:00
PDEBUG ( DAMPS , DEBUG_NOTICE , " Created channel #%s (System %s) of type '%s' = %s \n " , kanal , band , chan_type_short_name ( chan_type ) , chan_type_long_name ( chan_type ) ) ;
2017-02-05 07:36:51 +00:00
2016-05-01 17:51:56 +00:00
return 0 ;
error :
amps_destroy ( & amps - > sender ) ;
return rc ;
}
/* Destroy transceiver instance and unlink from list. */
void amps_destroy ( sender_t * sender )
{
amps_t * amps = ( amps_t * ) sender ;
transaction_t * trans ;
2019-07-20 16:11:17 +00:00
PDEBUG ( DAMPS , DEBUG_DEBUG , " Destroying 'AMPS' instance for channel = %s. \n " , sender - > kanal ) ;
2016-05-01 17:51:56 +00:00
while ( ( trans = amps - > trans_list ) ) {
const char * number = amps_min2number ( trans - > min1 , trans - > min2 ) ;
PDEBUG ( DAMPS , DEBUG_NOTICE , " Removing pending transaction for subscriber '%s' \n " , number ) ;
destroy_transaction ( trans ) ;
}
dsp_cleanup_sender ( amps ) ;
sender_destroy ( & amps - > sender ) ;
free ( amps ) ;
}
/* Abort connection towards mobile station by sending FOCC/FVC pattern. */
2017-05-26 05:27:30 +00:00
void amps_go_idle ( amps_t * amps )
2016-05-01 17:51:56 +00:00
{
int frame_length ;
if ( amps - > state = = STATE_IDLE )
return ;
2016-08-21 08:01:37 +00:00
if ( amps - > trans_list ) {
PDEBUG ( DAMPS , DEBUG_ERROR , " Releasing but still having transaction, please fix! \n " ) ;
if ( amps - > trans_list - > callref )
2017-10-28 05:11:40 +00:00
call_up_release ( amps - > trans_list - > callref , CAUSE_NORMAL ) ;
2016-08-21 08:01:37 +00:00
destroy_transaction ( amps - > trans_list ) ;
}
2016-05-01 17:51:56 +00:00
amps_new_state ( amps , STATE_IDLE ) ;
2016-10-30 08:25:45 +00:00
if ( amps - > chan_type ! = CHAN_TYPE_VC ) {
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Entering IDLE state, sending Overhead/Filler frames on %s. \n " , chan_type_long_name ( amps - > chan_type ) ) ;
2016-10-30 08:25:45 +00:00
if ( amps - > sender . loopback )
frame_length = 441 ; /* bits after sync (FOCC) */
else
frame_length = 247 ; /* bits after sync (RECC) */
amps_set_dsp_mode ( amps , DSP_MODE_FRAME_RX_FRAME_TX , frame_length ) ;
} else {
2018-12-23 18:44:05 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Entering IDLE state (sending silence / no RF) on %s. \n " , chan_type_long_name ( amps - > chan_type ) ) ;
2016-10-30 08:25:45 +00:00
amps_set_dsp_mode ( amps , DSP_MODE_OFF , 0 ) ;
}
2016-05-01 17:51:56 +00:00
}
/* Abort connection towards mobile station by sending FOCC/FVC pattern. */
static void amps_release ( transaction_t * trans , uint8_t cause )
{
amps_t * amps = trans - > amps ;
timer_stop ( & trans - > timer ) ;
timer_start ( & trans - > timer , RELEASE_TIMER ) ;
trans_new_state ( trans , TRANS_CALL_RELEASE ) ;
trans - > chan = 0 ;
trans - > msg_type = 0 ;
trans - > ordq = 0 ;
trans - > order = 3 ;
/* release towards call control */
2016-08-21 08:01:37 +00:00
if ( trans - > callref ) {
2017-10-28 05:11:40 +00:00
call_up_release ( trans - > callref , cause ) ;
2016-08-21 08:01:37 +00:00
trans - > callref = 0 ;
2016-05-01 17:51:56 +00:00
}
/* change DSP mode to transmit release */
2017-10-28 06:34:50 +00:00
if ( amps - > dsp_mode = = DSP_MODE_AUDIO_RX_AUDIO_TX | | amps - > dsp_mode = = DSP_MODE_OFF )
2016-05-01 17:51:56 +00:00
amps_set_dsp_mode ( amps , DSP_MODE_AUDIO_RX_FRAME_TX , 0 ) ;
}
/*
2016-07-24 08:26:01 +00:00
* receive signaling
2016-05-01 17:51:56 +00:00
*/
2016-07-24 08:26:01 +00:00
void amps_rx_signaling_tone ( amps_t * amps , int tone , double quality )
2016-05-01 17:51:56 +00:00
{
transaction_t * trans = amps - > trans_list ;
if ( trans = = NULL ) {
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_ERROR , " Signaling Tone without transaction, please fix! \n " ) ;
2016-05-01 17:51:56 +00:00
return ;
}
if ( tone )
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Detected Signaling Tone with quality=%.0f. \n " , quality * 100.0 ) ;
2016-05-01 17:51:56 +00:00
else
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Lost Signaling Tone signal \n " ) ;
2016-05-01 17:51:56 +00:00
switch ( trans - > state ) {
case TRANS_CALL :
if ( ! tone )
break ;
2018-01-27 14:52:33 +00:00
/* FALLTHRU */
2017-10-28 06:34:50 +00:00
case TRANS_CALL_RELEASE :
case TRANS_CALL_RELEASE_SEND :
/* also loosing singaling tone indicates release confirm (after alerting) */
2016-05-01 17:51:56 +00:00
timer_stop ( & trans - > timer ) ;
2017-09-24 12:11:54 +00:00
if ( trans - > callref )
2017-10-28 05:11:40 +00:00
call_up_release ( trans - > callref , CAUSE_NORMAL ) ;
2017-09-24 12:11:54 +00:00
destroy_transaction ( trans ) ;
2016-05-01 17:51:56 +00:00
amps_go_idle ( amps ) ;
break ;
case TRANS_CALL_MT_ALERT :
if ( tone ) {
timer_stop ( & trans - > timer ) ;
2017-10-28 05:11:40 +00:00
call_up_alerting ( trans - > callref ) ;
2016-05-01 17:51:56 +00:00
amps_set_dsp_mode ( amps , DSP_MODE_AUDIO_RX_AUDIO_TX , 0 ) ;
trans_new_state ( trans , TRANS_CALL_MT_ALERT_SEND ) ;
timer_start ( & trans - > timer , ALERT_TO ) ;
}
break ;
case TRANS_CALL_MT_ALERT_SEND :
if ( ! tone ) {
timer_stop ( & trans - > timer ) ;
if ( ! trans - > sat_detected )
timer_start ( & trans - > timer , SAT_TO1 ) ;
2017-10-28 05:11:40 +00:00
call_up_answer ( trans - > callref , amps_min2number ( trans - > min1 , trans - > min2 ) ) ;
2016-05-01 17:51:56 +00:00
trans_new_state ( trans , TRANS_CALL ) ;
}
break ;
default :
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_ERROR , " Signaling Tone without active call, please fix! \n " ) ;
2016-05-01 17:51:56 +00:00
}
}
void amps_rx_sat ( amps_t * amps , int tone , double quality )
{
transaction_t * trans = amps - > trans_list ;
if ( trans = = NULL ) {
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_ERROR , " SAT signal without transaction, please fix! \n " ) ;
2016-05-01 17:51:56 +00:00
return ;
}
/* irgnoring SAT loss on release */
if ( trans - > state = = TRANS_CALL_RELEASE
| | trans - > state = = TRANS_CALL_RELEASE_SEND )
return ;
if ( trans - > state ! = TRANS_CALL
& & trans - > state ! = TRANS_CALL_MT_ALERT
& & trans - > state ! = TRANS_CALL_MT_ALERT_SEND ) {
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_ERROR , " SAT signal without active call, please fix! \n " ) ;
2016-05-01 17:51:56 +00:00
return ;
}
if ( tone ) {
PDEBUG ( DAMPS , DEBUG_INFO , " Detected SAT signal with quality=%.0f. \n " , quality * 100.0 ) ;
trans - > sat_detected = 1 ;
} else {
PDEBUG ( DAMPS , DEBUG_INFO , " Lost SAT signal \n " ) ;
trans - > sat_detected = 0 ;
}
/* no SAT during alerting */
if ( trans - > state = = TRANS_CALL_MT_ALERT
| | trans - > state = = TRANS_CALL_MT_ALERT_SEND )
return ;
if ( tone ) {
timer_stop ( & trans - > timer ) ;
} else {
2017-05-25 07:57:22 +00:00
if ( ! trans - > dtx )
timer_start ( & trans - > timer , SAT_TO2 ) ;
else
timer_stop ( & trans - > timer ) ;
2016-05-01 17:51:56 +00:00
}
2017-10-02 10:24:57 +00:00
if ( amps - > sender . loopback )
return ;
2016-05-01 17:51:56 +00:00
}
static void timeout_sat ( amps_t * amps , double duration )
{
if ( ! amps - > trans_list ) {
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_ERROR , " SAT timeout, but no transaction, please fix! \n " ) ;
2016-05-01 17:51:56 +00:00
return ;
}
if ( duration = = SAT_TO1 )
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_NOTICE , " Timeout after %.0f seconds not receiving SAT signal. \n " , duration ) ;
2016-05-01 17:51:56 +00:00
else
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_NOTICE , " Timeout after %.0f seconds loosing SAT signal. \n " , duration ) ;
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Release call towards network. \n " ) ;
2016-05-01 17:51:56 +00:00
amps_release ( amps - > trans_list , CAUSE_TEMPFAIL ) ;
}
/* receive message from phone on RECC */
2017-05-21 14:40:10 +00:00
void amps_rx_recc ( amps_t * amps , uint8_t scm , uint8_t mpci , uint32_t esn , uint32_t min1 , uint16_t min2 , uint8_t msg_type , uint8_t ordq , uint8_t order , const char * dialing )
2016-05-01 17:51:56 +00:00
{
amps_t * vc ;
transaction_t * trans ;
const char * callerid = amps_min2number ( min1 , min2 ) ;
2017-05-30 10:49:40 +00:00
const char * carrier = NULL , * country = NULL , * national_number = NULL ;
2016-05-01 17:51:56 +00:00
2016-07-24 08:26:01 +00:00
/* check if we are busy, so we ignore all signaling */
2016-05-01 17:51:56 +00:00
if ( amps - > state = = STATE_BUSY ) {
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_NOTICE , " Ignoring RECC messages from phone while using this channel for voice. \n " ) ;
2016-05-01 17:51:56 +00:00
return ;
}
if ( order = = 13 & & ( ordq = = 0 | | ordq = = 1 | | ordq = = 2 | | ordq = = 3 ) & & msg_type = = 0 ) {
2017-05-21 14:40:10 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Registration %s (ESN = %08x, %s, %s) \n " , callerid , esn , amps_scm ( scm ) , amps_mpci ( mpci ) ) ;
2017-05-30 10:49:40 +00:00
_register :
numbering ( callerid , & carrier , & country , & national_number ) ;
if ( carrier )
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " -> Home carrier: %s \n " , carrier ) ;
if ( country )
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " -> Home country: %s \n " , country ) ;
if ( national_number )
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " -> Home number: %s \n " , national_number ) ;
2016-05-01 17:51:56 +00:00
trans = create_transaction ( amps , TRANS_REGISTER_ACK , min1 , min2 , msg_type , ordq , order , 0 ) ;
if ( ! trans ) {
PDEBUG ( DAMPS , DEBUG_ERROR , " Failed to create transaction \n " ) ;
return ;
}
} else
if ( order = = 13 & & ordq = = 3 & & msg_type = = 1 ) {
2017-05-21 14:40:10 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Registration - Power Down %s (ESN = %08x, %s, %s) \n " , callerid , esn , amps_scm ( scm ) , amps_mpci ( mpci ) ) ;
2017-05-30 10:49:40 +00:00
goto _register ;
2016-05-01 17:51:56 +00:00
} else
if ( order = = 0 & & ordq = = 0 & & msg_type = = 0 ) {
2016-06-19 15:41:42 +00:00
if ( ! dialing )
2017-05-21 14:40:10 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Paging reply %s (ESN = %08x, %s, %s) \n " , callerid , esn , amps_scm ( scm ) , amps_mpci ( mpci ) ) ;
2016-06-19 15:41:42 +00:00
else
2017-05-21 14:40:10 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Call %s -> %s (ESN = %08x, %s, %s) \n " , callerid , dialing , esn , amps_scm ( scm ) , amps_mpci ( mpci ) ) ;
2016-06-19 15:41:42 +00:00
trans = search_transaction_number ( amps , min1 , min2 ) ;
if ( ! trans & & ! dialing ) {
PDEBUG ( DAMPS , DEBUG_NOTICE , " Paging reply, but call is already gone, rejecting call \n " ) ;
goto reject ;
2016-05-01 17:51:56 +00:00
}
2016-06-19 15:41:42 +00:00
if ( trans & & dialing )
PDEBUG ( DAMPS , DEBUG_NOTICE , " There is already a transaction for this phone. Cloning? \n " ) ;
2016-05-01 17:51:56 +00:00
vc = search_free_vc ( ) ;
if ( ! vc ) {
PDEBUG ( DAMPS , DEBUG_NOTICE , " No free channel, rejecting call \n " ) ;
2016-06-19 15:41:42 +00:00
reject :
2016-05-01 17:51:56 +00:00
if ( ! trans ) {
trans = create_transaction ( amps , TRANS_CALL_REJECT , min1 , min2 , 0 , 0 , 3 , 0 ) ;
if ( ! trans ) {
PDEBUG ( DAMPS , DEBUG_ERROR , " Failed to create transaction \n " ) ;
return ;
}
} else {
trans_new_state ( trans , TRANS_CALL_REJECT ) ;
trans - > chan = 0 ;
trans - > msg_type = 0 ;
trans - > ordq = 0 ;
trans - > order = 3 ;
}
return ;
}
if ( ! trans ) {
2019-07-20 16:11:17 +00:00
trans = create_transaction ( amps , TRANS_CALL_MO_ASSIGN , min1 , min2 , 0 , 0 , 0 , atoi ( vc - > sender . kanal ) ) ;
2016-05-01 17:51:56 +00:00
strncpy ( trans - > dialing , dialing , sizeof ( trans - > dialing ) - 1 ) ;
if ( ! trans ) {
PDEBUG ( DAMPS , DEBUG_ERROR , " Failed to create transaction \n " ) ;
return ;
}
} else {
trans_new_state ( trans , TRANS_CALL_MT_ASSIGN ) ;
2019-07-20 16:11:17 +00:00
trans - > chan = atoi ( vc - > sender . kanal ) ;
2016-05-01 17:51:56 +00:00
}
2017-05-25 07:57:22 +00:00
/* if we support DTX and also the phone does, we set DTX state of transaction */
if ( amps - > si . word2 . dtx ) {
if ( ( scm & 4 ) ) {
PDEBUG ( DAMPS , DEBUG_INFO , " -> Use DTX for this call \n " ) ;
trans - > dtx = 1 ;
} else
PDEBUG ( DAMPS , DEBUG_INFO , " -> Requested DTX, but not supported by phone \n " ) ;
}
2016-05-01 17:51:56 +00:00
} else
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_NOTICE , " Unsupported RECC messages: ORDER: %d ORDQ: %d MSG TYPE: %d (See Table 4 of specs.) \n " , order , ordq , msg_type ) ;
2016-05-01 17:51:56 +00:00
}
/*
* call states received from call control
*/
/* Call control starts call towards mobile station. */
2017-10-28 05:11:40 +00:00
int call_down_setup ( int callref , const char __attribute__ ( ( unused ) ) * caller_id , enum number_type __attribute__ ( ( unused ) ) caller_type , const char * dialing )
2016-05-01 17:51:56 +00:00
{
sender_t * sender ;
amps_t * amps ;
transaction_t * trans ;
uint32_t min1 ;
uint16_t min2 ;
int i ;
/* 1. check if number is invalid, return INVALNUMBER */
2019-07-06 05:29:40 +00:00
if ( ! tacs ) {
if ( strlen ( dialing ) = = 12 & & ! strncmp ( dialing , " +1 " , 2 ) )
dialing + = 2 ;
if ( strlen ( dialing ) = = 11 & & ! strncmp ( dialing , " 1 " , 1 ) )
dialing + = 1 ;
} else if ( ! jtacs ) {
if ( strlen ( dialing ) = = 14 & & ! strncmp ( dialing , " +44 " , 3 ) )
dialing + = 3 ;
if ( strlen ( dialing ) = = 11 & & ! strncmp ( dialing , " 0 " , 1 ) )
dialing + = 1 ;
}
2016-05-01 17:51:56 +00:00
if ( strlen ( dialing ) ! = 10 ) {
inval :
PDEBUG ( DAMPS , DEBUG_NOTICE , " Outgoing call to invalid number '%s', rejecting! \n " , dialing ) ;
return - CAUSE_INVALNUMBER ;
}
for ( i = 0 ; i < 10 ; i + + ) {
if ( dialing [ i ] < ' 0 ' | | dialing [ i ] > ' 9 ' )
goto inval ;
}
amps_number2min ( dialing , & min1 , & min2 ) ;
/* 2. check if the subscriber is attached */
// if (!find_db(min1, min2)) {
// PDEBUG(DAMPS, DEBUG_NOTICE, "Outgoing call to not attached subscriber, rejecting!\n");
// return -CAUSE_OUTOFORDER;
// }
/* 3. check if given number is already in a call, return BUSY */
for ( sender = sender_head ; sender ; sender = sender - > next ) {
amps = ( amps_t * ) sender ;
/* search transaction for this number */
trans = search_transaction_number ( amps , min1 , min2 ) ;
if ( trans )
break ;
}
if ( sender ) {
PDEBUG ( DAMPS , DEBUG_NOTICE , " Outgoing call to busy number, rejecting! \n " ) ;
return - CAUSE_BUSY ;
}
/* 4. check if all senders are busy, return NOCHANNEL */
if ( ! search_free_vc ( ) ) {
PDEBUG ( DAMPS , DEBUG_NOTICE , " Outgoing call, but no free channel, rejecting! \n " ) ;
return - CAUSE_NOCHANNEL ;
}
/* 5. check if we have (currently) no paging channel, return NOCHANNEL */
amps = search_pc ( ) ;
if ( ! amps ) {
PDEBUG ( DAMPS , DEBUG_NOTICE , " Outgoing call, but paging channel (control channel) is currently busy, rejecting! \n " ) ;
return - CAUSE_NOCHANNEL ;
}
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Call to mobile station, paging station id '%s' \n " , dialing ) ;
2016-05-01 17:51:56 +00:00
/* 6. trying to page mobile station */
trans = create_transaction ( amps , TRANS_PAGE , min1 , min2 , 0 , 0 , 0 , 0 ) ;
if ( ! trans ) {
PDEBUG ( DAMPS , DEBUG_ERROR , " Failed to create transaction \n " ) ;
return - CAUSE_TEMPFAIL ;
}
2016-08-21 08:01:37 +00:00
trans - > callref = callref ;
trans - > page_retry = 1 ;
2016-05-01 17:51:56 +00:00
return 0 ;
}
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-05-01 17:51:56 +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-05-01 17:51:56 +00:00
{
sender_t * sender ;
amps_t * amps ;
transaction_t * trans ;
PDEBUG ( DAMPS , DEBUG_INFO , " Call has been disconnected by network. \n " ) ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
amps = ( amps_t * ) sender ;
2016-08-21 08:01:37 +00:00
/* search transaction for this callref */
trans = search_transaction_callref ( amps , callref ) ;
if ( trans )
2016-05-01 17:51:56 +00:00
break ;
}
if ( ! sender ) {
PDEBUG ( DAMPS , DEBUG_NOTICE , " Outgoing disconnect, but no callref! \n " ) ;
2017-10-28 05:11:40 +00:00
call_up_release ( callref , CAUSE_INVALCALLREF ) ;
2016-05-01 17:51:56 +00:00
return ;
}
/* Release when not active */
switch ( amps - > dsp_mode ) {
case DSP_MODE_AUDIO_RX_AUDIO_TX :
case DSP_MODE_AUDIO_RX_FRAME_TX :
if ( trans - > state = = TRANS_CALL_MT_ALERT
| | trans - > state = = TRANS_CALL_MT_ALERT_SEND ) {
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Call control disconnect on voice channel while alerting, releasing towards mobile station. \n " ) ;
2016-05-01 17:51:56 +00:00
amps_release ( trans , cause ) ;
}
return ;
default :
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Call control disconnects on control channel, removing transaction. \n " ) ;
2017-10-28 05:11:40 +00:00
call_up_release ( callref , cause ) ;
2016-08-21 08:01:37 +00:00
trans - > callref = 0 ;
2016-05-01 17:51:56 +00:00
destroy_transaction ( trans ) ;
amps_go_idle ( amps ) ;
}
}
/* Call control releases call toward mobile station. */
2017-10-28 05:11:40 +00:00
void call_down_release ( int callref , int cause )
2016-05-01 17:51:56 +00:00
{
sender_t * sender ;
amps_t * amps ;
transaction_t * trans ;
PDEBUG ( DAMPS , DEBUG_INFO , " Call has been released by network, releasing call. \n " ) ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
amps = ( amps_t * ) sender ;
2016-08-21 08:01:37 +00:00
/* search transaction for this callref */
trans = search_transaction_callref ( amps , callref ) ;
if ( trans )
2016-05-01 17:51:56 +00:00
break ;
}
if ( ! sender ) {
PDEBUG ( DAMPS , DEBUG_NOTICE , " Outgoing release, but no callref! \n " ) ;
/* don't send release, because caller already released */
return ;
}
2016-08-21 08:01:37 +00:00
trans - > callref = 0 ;
2016-05-01 17:51:56 +00:00
switch ( amps - > dsp_mode ) {
case DSP_MODE_AUDIO_RX_AUDIO_TX :
case DSP_MODE_AUDIO_RX_FRAME_TX :
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Call control releases on voice channel, releasing towards mobile station. \n " ) ;
2016-05-01 17:51:56 +00:00
amps_release ( trans , cause ) ;
break ;
default :
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Call control releases on control channel, removing transaction. \n " ) ;
2016-05-01 17:51:56 +00:00
destroy_transaction ( trans ) ;
amps_go_idle ( amps ) ;
}
}
/* 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-05-01 17:51:56 +00:00
{
sender_t * sender ;
amps_t * amps ;
for ( sender = sender_head ; sender ; sender = sender - > next ) {
amps = ( amps_t * ) sender ;
2016-08-21 08:01:37 +00:00
if ( amps - > trans_list & & amps - > trans_list - > callref = = callref )
2016-05-01 17:51:56 +00:00
break ;
}
if ( ! sender )
return ;
if ( amps - > dsp_mode = = DSP_MODE_AUDIO_RX_AUDIO_TX ) {
2017-01-27 15:57:34 +00:00
sample_t up [ ( int ) ( ( double ) count * amps - > sender . srstate . factor + 0.5 ) + 10 ] ;
2016-05-01 17:51:56 +00:00
compress_audio ( & amps - > cstate , samples , count ) ;
count = samplerate_upsample ( & amps - > sender . srstate , samples , count , up ) ;
2017-01-04 13:14:02 +00:00
jitter_save ( & amps - > sender . dejitter , up , count ) ;
2016-05-01 17:51:56 +00:00
}
}
2020-01-12 06:54:25 +00:00
void call_down_clock ( void ) { }
2016-05-01 17:51:56 +00:00
/* Timeout handling */
void transaction_timeout ( struct timer * timer )
{
transaction_t * trans = ( transaction_t * ) timer - > priv ;
amps_t * amps = trans - > amps ;
switch ( trans - > state ) {
case TRANS_CALL :
timeout_sat ( amps , timer - > duration ) ;
break ;
case TRANS_CALL_RELEASE :
case TRANS_CALL_RELEASE_SEND :
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_NOTICE , " Release timeout, destroying transaction \n " ) ;
2016-05-01 17:51:56 +00:00
destroy_transaction ( trans ) ;
amps_go_idle ( amps ) ;
break ;
case TRANS_CALL_MT_ALERT :
amps_release ( trans , CAUSE_TEMPFAIL ) ;
break ;
case TRANS_CALL_MT_ALERT_SEND :
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_NOTICE , " Alerting timeout, destroying transaction \n " ) ;
2016-05-01 17:51:56 +00:00
amps_release ( trans , CAUSE_NOANSWER ) ;
break ;
case TRANS_PAGE_REPLY :
2016-08-21 08:01:37 +00:00
if ( trans - > page_retry + + = = PAGE_TRIES ) {
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_NOTICE , " Paging timeout, destroying transaction \n " ) ;
2016-05-01 17:51:56 +00:00
amps_release ( trans , CAUSE_OUTOFORDER ) ;
} else {
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_NOTICE , " Paging timeout, retrying \n " ) ;
2016-05-01 17:51:56 +00:00
trans_new_state ( trans , TRANS_PAGE ) ;
}
break ;
default :
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_ERROR , " Timeout unhandled in state %d \n " , trans - > state ) ;
2016-05-01 17:51:56 +00:00
}
}
/* assigning voice channel and moving transaction+callref to that channel */
2016-10-30 08:25:45 +00:00
static amps_t * assign_voice_channel ( transaction_t * trans )
2016-05-01 17:51:56 +00:00
{
amps_t * amps = trans - > amps , * vc ;
const char * callerid = amps_min2number ( trans - > min1 , trans - > min2 ) ;
int callref = + + new_callref ;
int rc ;
2016-10-30 08:25:45 +00:00
vc = search_channel ( trans - > chan ) ;
2016-05-01 17:51:56 +00:00
if ( ! vc ) {
2016-10-30 08:25:45 +00:00
PDEBUG ( DAMPS , DEBUG_NOTICE , " Channel %d is not free anymore, rejecting call \n " , trans - > chan ) ;
2016-05-01 17:51:56 +00:00
amps_release ( trans , CAUSE_NOCHANNEL ) ;
2016-10-30 08:25:45 +00:00
return NULL ;
2016-05-01 17:51:56 +00:00
}
2016-08-21 08:01:37 +00:00
if ( vc = = amps )
2019-07-20 16:11:17 +00:00
PDEBUG ( DAMPS , DEBUG_INFO , " Staying on combined control + voice channel %s \n " , vc - > sender . kanal ) ;
2016-08-21 08:01:37 +00:00
else
2019-07-20 16:11:17 +00:00
PDEBUG ( DAMPS , DEBUG_INFO , " Moving to voice channel %s \n " , vc - > sender . kanal ) ;
2017-10-28 06:34:50 +00:00
/* switch channel... */
timer_start ( & trans - > timer , SAT_TO1 ) ;
/* make channel busy */
amps_new_state ( vc , STATE_BUSY ) ;
/* relink */
unlink_transaction ( trans ) ;
link_transaction ( trans , vc ) ;
/* flush all other transactions, if any (in case of combined VC + CC) */
amps_flush_other_transactions ( vc , trans ) ;
2016-08-21 08:01:37 +00:00
if ( ! trans - > callref ) {
2016-05-01 17:51:56 +00:00
/* setup call */
PDEBUG ( DAMPS , DEBUG_INFO , " Setup call to network. \n " ) ;
2017-10-28 05:11:40 +00:00
rc = call_up_setup ( callref , callerid , trans - > dialing ) ;
2016-05-01 17:51:56 +00:00
if ( rc < 0 ) {
PDEBUG ( DAMPS , DEBUG_NOTICE , " Call rejected (cause %d), releasing. \n " , rc ) ;
amps_release ( trans , 0 ) ;
2016-10-30 08:25:45 +00:00
return NULL ;
2016-05-01 17:51:56 +00:00
}
2016-08-21 08:01:37 +00:00
trans - > callref = callref ;
2016-05-01 17:51:56 +00:00
}
2016-10-30 08:25:45 +00:00
return vc ;
2016-05-01 17:51:56 +00:00
}
transaction_t * amps_tx_frame_focc ( amps_t * amps )
{
transaction_t * trans ;
2016-10-30 08:25:45 +00:00
amps_t * vc ;
2016-05-01 17:51:56 +00:00
again :
trans = amps - > trans_list ;
if ( ! trans )
return NULL ;
switch ( trans - > state ) {
case TRANS_REGISTER_ACK :
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Sending Register acknowledge \n " ) ;
2016-05-01 17:51:56 +00:00
trans_new_state ( trans , TRANS_REGISTER_ACK_SEND ) ;
return trans ;
case TRANS_REGISTER_ACK_SEND :
destroy_transaction ( trans ) ;
goto again ;
case TRANS_CALL_REJECT :
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Rejecting call from mobile station \n " ) ;
2016-05-01 17:51:56 +00:00
trans_new_state ( trans , TRANS_CALL_REJECT_SEND ) ;
return trans ;
case TRANS_CALL_REJECT_SEND :
destroy_transaction ( trans ) ;
goto again ;
case TRANS_CALL_MO_ASSIGN :
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Assigning channel to call from mobile station \n " ) ;
2016-05-01 17:51:56 +00:00
trans_new_state ( trans , TRANS_CALL_MO_ASSIGN_SEND ) ;
return trans ;
case TRANS_CALL_MO_ASSIGN_SEND :
2016-10-30 08:25:45 +00:00
vc = assign_voice_channel ( trans ) ;
if ( vc ) {
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Assignment complete, voice connected \n " ) ;
2016-10-30 08:25:45 +00:00
trans_new_state ( trans , TRANS_CALL ) ;
amps_set_dsp_mode ( vc , DSP_MODE_AUDIO_RX_AUDIO_TX , 0 ) ;
}
2016-05-01 17:51:56 +00:00
return NULL ;
case TRANS_CALL_MT_ASSIGN :
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Assigning channel to call to mobile station \n " ) ;
2016-05-01 17:51:56 +00:00
trans_new_state ( trans , TRANS_CALL_MT_ASSIGN_SEND ) ;
return trans ;
case TRANS_CALL_MT_ASSIGN_SEND :
2016-10-30 08:25:45 +00:00
vc = assign_voice_channel ( trans ) ;
if ( vc ) {
2017-01-08 11:10:29 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Assignment complete, next: sending alerting on VC \n " ) ;
2016-10-30 08:25:45 +00:00
trans - > chan = 0 ;
trans - > msg_type = 0 ;
trans - > ordq = 0 ;
trans - > order = 1 ;
trans_new_state ( trans , TRANS_CALL_MT_ALERT ) ;
amps_set_dsp_mode ( vc , DSP_MODE_AUDIO_RX_FRAME_TX , 0 ) ;
}
2016-05-01 17:51:56 +00:00
return NULL ;
case TRANS_PAGE :
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Paging the phone \n " ) ;
2016-05-01 17:51:56 +00:00
trans_new_state ( trans , TRANS_PAGE_SEND ) ;
return trans ;
case TRANS_PAGE_SEND :
trans_new_state ( trans , TRANS_PAGE_REPLY ) ;
2016-08-21 08:01:37 +00:00
timer_start ( & trans - > timer , ( trans - > page_retry = = PAGE_TRIES ) ? PAGE_TO2 : PAGE_TO1 ) ;
2016-05-01 17:51:56 +00:00
return NULL ;
default :
return NULL ;
}
}
transaction_t * amps_tx_frame_fvc ( amps_t * amps )
{
transaction_t * trans = amps - > trans_list ;
trans = amps - > trans_list ;
if ( ! trans )
return NULL ;
switch ( trans - > state ) {
case TRANS_CALL_RELEASE :
2016-12-09 15:34:33 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Releasing call towards mobile station \n " ) ;
2016-05-01 17:51:56 +00:00
trans_new_state ( trans , TRANS_CALL_RELEASE_SEND ) ;
return trans ;
case TRANS_CALL_RELEASE_SEND :
2017-10-28 06:34:50 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Release call was sent, continue sending release \n " ) ;
return trans ;
2016-05-01 17:51:56 +00:00
case TRANS_CALL_MT_ALERT :
2017-01-08 11:10:29 +00:00
PDEBUG_CHAN ( DAMPS , DEBUG_INFO , " Sending alerting \n " ) ;
2016-05-01 17:51:56 +00:00
return trans ;
default :
return NULL ;
}
}
2016-08-02 07:03:46 +00:00
void dump_info ( void ) { }