Skip to content
Snippets Groups Projects
app_rpt.c 121 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    /** @file app_rpt.c 
     *
     * Asterisk -- A telephony toolkit for Linux.
     *
    
     * Radio Repeater / Remote Base program 
    
    Mark Spencer's avatar
    Mark Spencer committed
     * 
    
     * See http://www.zapatatelephony.org/app_rpt.html
     *
    
    Jim Dixon's avatar
    Jim Dixon committed
     * Copyright (C) 2002-2004, Jim Dixon, WB6NIL
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * Jim Dixon, WB6NIL <jim@lambdatel.com>
    
     * Serious contributions by Steve RoDgers, WA6ZFT <hwstar@rodgers.sdcoxmail.com>
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
     * This program is free software, distributed under the terms of
     * the GNU General Public License
     *
    
     * Repeater / Remote Functions:
     * "Simple" Mode:  * - autopatch access, # - autopatch hangup
     * Normal mode:
    
     * See the function list in rpt.conf
    
     *
     *  To send an asterisk (*) while dialing or talking on phone,
     *  use the autopatch acess code.
    
     * status cmds:
     *
     *  1 - Force ID
     *  2 - Give Time of Day
     *  3 - Give software Version
     *
     * cop (control operator) cmds:
     *
     *  1 - System warm boot
     *  2 - System enable
     *  3 - System disable
     *
     * ilink cmds:
     *
     *  1 - Disconnect specified link
     *  2 - Connect specified link -- monitor only
     *  3 - Connect specified link -- tranceive
     *  4 - Enter command mode on specified link
     *  5 - System status
     *  6 - Disconnect all links
     *
     * remote cmds:
     *
     *  0 - Recall Memory MM  (*000-*099) (Gets memory from rpt.conf)
     *  1 - Set VFO MMMMM*KKK*O   (Mhz digits, Khz digits, Offset)
     *  2 - Set Rx PL Tone HHH*D*
     *  3 - Set Tx PL Tone HHH*D* (Not currently implemented with DHE RBI-1)
     *  5 - Link Status
     *  100 - RX PL off (Default)
     *  101 - RX PL On
     *  102 - TX PL Off (Default)
     *  103 - TX PL On
     *  104 - Low Power
     *  105 - Med Power
     *  106 - Hi Power
    
    /* The following is JUST GROSS!! There is some soft of underlying problem,
       probably in channel_iax2.c, that causes an IAX2 connection to sometimes
       stop transmitting randomly. We have been working for weeks to try to
       locate it and fix it, but to no avail We finally decided to put our
       tail between our legs, and just make the radio system re-connect upon
       network failure. This just shouldnt have to be done. For normal operation,
       comment-out the following line */
    #define	RECONNECT_KLUDGE 
    
    
    /* maximum digits in DTMF buffer, and seconds after * for DTMF command timeout */
    
    
    #define	DISC_TIME 10000  /* report disc after 10 seconds of no connect */
    #define	MAX_RETRIES 5
    
    
    Jim Dixon's avatar
    Jim Dixon committed
    #define	REDUNDANT_TX_TIME 2000
    
    
    #define	RETRY_TIMER_MS 5000
    
    #define	FUNCTIONS "functions"
    #define TELEMETRY "telemetry"
    #define MORSE "morse"
    #define	FUNCCHAR '*'
    #define	ENDCHAR '#'
    
    enum{ID,PROC,TERM,COMPLETE,UNKEY,REMDISC,REMALREADY,REMNOTFOUND,REMGO,
    
    	CONNECTED,CONNFAIL,STATUS,TIMEOUT,ID1, STATS_TIME,
    
    Jim Dixon's avatar
    Jim Dixon committed
    	STATS_VERSION, IDTALKOVER, ARB_ALPHA, TEST_TONE};
    
    enum {REM_SIMPLEX,REM_MINUS,REM_PLUS};
    
    enum {REM_LOWPWR,REM_MEDPWR,REM_HIPWR};
    
    
    enum {DC_INDETERMINATE, DC_REQ_FLUSH, DC_ERROR, DC_COMPLETE};
    enum {SOURCE_RPT, SOURCE_LNK, SOURCE_RMT};
    
    
    enum {DLY_TELEM, DLY_ID, DLY_UNKEY, DLY_CALLTERM};
    
    
    #include "asterisk.h"
    
    ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
    
    
    Jim Dixon's avatar
    Jim Dixon committed
    #include <signal.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdlib.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <errno.h>
    #include <dirent.h>
    #include <ctype.h>
    #include <sys/stat.h>
    #include <sys/time.h>
    #include <sys/file.h>
    #include <sys/ioctl.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <math.h>
    #include <tonezone.h>
    #include <linux/zaptel.h>
    
    
    #include "asterisk/utils.h"
    #include "asterisk/lock.h"
    #include "asterisk/file.h"
    #include "asterisk/logger.h"
    #include "asterisk/channel.h"
    #include "asterisk/callerid.h"
    #include "asterisk/pbx.h"
    #include "asterisk/module.h"
    #include "asterisk/translate.h"
    #include "asterisk/options.h"
    #include "asterisk/cli.h"
    #include "asterisk/config.h"
    #include "asterisk/say.h"
    #include "asterisk/localtime.h"
    
    
    static  char *tdesc = "Radio Repeater / Remote Base  version 0.27  07/09/2005";
    
    Jim Dixon's avatar
    Jim Dixon committed
    
    
    static char *app = "Rpt";
    
    static char *synopsis = "Radio Repeater/Remote Base Control System";
    
    static char *descrip = 
    "  Rpt(sysname):  Radio Remote Link or Remote Base Link Endpoint Process.\n";
    
    
    static int debug = 0;  /* Set this >0 for extra debug output */
    
    char *discstr = "!!DISCONNECT!!";
    
    Mark Spencer's avatar
    Mark Spencer committed
    STANDARD_LOCAL_USER;
    LOCAL_USER_DECL;
    
    #define	MSWAIT 200
    #define	HANGTIME 5000
    #define	TOTIME 180000
    #define	IDTIME 300000
    #define	MAXRPTS 20
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static  pthread_t rpt_master_thread;
    
    
    struct rpt;
    
    struct rpt_link
    {
    	struct rpt_link *next;
    	struct rpt_link *prev;
    	char	mode;			/* 1 if in tx mode */
    	char	isremote;
    	char	name[MAXNODESTR];	/* identifier (routing) string */
    	char	lasttx;
    	char	lastrx;
    	char	connected;
    
    Jim Dixon's avatar
    Jim Dixon committed
    	char	hasconnected;
    
    	char	disced;
    
    Jim Dixon's avatar
    Jim Dixon committed
    	char	killme;
    
    	long	elaptime;
    	long	disctime;
    	long 	retrytimer;
    
    Jim Dixon's avatar
    Jim Dixon committed
    	long	retxtimer;
    
    	int	retries;
    
    	struct ast_channel *chan;	
    	struct ast_channel *pchan;	
    } ;
    
    struct rpt_tele
    {
    	struct rpt_tele *next;
    	struct rpt_tele *prev;
    	struct rpt *rpt;
    
    struct function_table_tag
    {
    	char action[ACTIONSIZE];
    	int (*function)(struct rpt *myrpt, char *param, char *digitbuf, int command_source);
    } ;
    
    /* Used to store the morse code patterns */
    
    struct morse_bits
    {		  
    	int len;
    	int ddcomb;
    } ;
    
    struct telem_defaults
    {
    	char name[20];
    	char value[80];
    } ;
    
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static struct rpt
    {
    	char *name;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *rxchanname;
    	char *txchanname;
    	char *ourcontext;
    	char *ourcallerid;
    	char *acctcode;
    
    	char *functions;
    	char *link_functions;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int hangtime;
    	int totime;
    	int idtime;
    
    	int unkeytocttimer;
    	char keyed;
    
    	char tounkeyed;
    	char tonotify;
    	char enable;
    
    	char dtmfbuf[MAXDTMF];
    	char rem_dtmfbuf[MAXDTMF];
    	char cmdnode[50];
    	struct ast_channel *rxchannel,*txchannel;
    
    	struct ast_channel *pchannel,*txpchannel, *remchannel;
    
    	struct rpt_tele tele;
    	pthread_t rpt_call_thread,rpt_thread;
    
    	time_t rem_dtmf_time,dtmf_time_rem;
    
    	int tailtimer,totimer,idtimer,txconf,conf,callmode,cidx;
    
    Jim Dixon's avatar
    Jim Dixon committed
    	long	retxtimer;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char exten[AST_MAX_EXTENSION];
    
    	char freq[MAXREMSTR],rxpl[MAXREMSTR],txpl[MAXREMSTR];
    	char offset;
    	char powerlevel;
    	char txplon;
    	char rxplon;
    
    Jim Dixon's avatar
    Jim Dixon committed
    	char stopgen;
    
    	int link_longestfunc;
    	int longestfunc;
    
    	int longestnode;
    	int threadrestarts;		
    	time_t disgorgetime;
    	time_t lastthreadrestarttime;
    
    /*
    * CLI extensions
    */
    
    /* Debug mode */
    static int rpt_do_debug(int fd, int argc, char *argv[]);
    
    static char debug_usage[] =
    "Usage: rpt debug level {0-7}\n"
    "       Enables debug messages in app_rpt\n";
                                                                                                                                    
    static struct ast_cli_entry  cli_debug =
            { { "rpt", "debug", "level" }, rpt_do_debug, "Enable app_rpt debugging", debug_usage };
    
    
    
    /*
    * Telemetry defaults
    */
    
    
    
    static struct telem_defaults tele_defs[] = {
    	{"ct1","|t(350,0,100,3072)(500,0,100,3072)(660,0,100,3072)"},
    	{"ct2","|t(660,880,150,3072)"},
    	{"ct3","|t(440,0,150,3072)"},
    	{"ct4","|t(550,0,150,3072)"},
    	{"ct5","|t(660,0,150,3072)"},
    	{"ct6","|t(880,0,150,3072)"},
    	{"ct7","|t(660,440,150,3072)"},
    	{"ct8","|t(700,1100,150,3072)"},
    	{"remotemon","|t(1600,0,75,2048)"},
    	{"remotetx","|t(2000,0,75,2048)(0,0,75,0)(1600,0,75,2048)"},
    	{"cmdmode","|t(900,904,200,2048)"},
    	{"functcomplete","|t(1000,0,100,2048)(0,0,100,0)(1000,0,100,2048)"}
    } ;
    
    /*
    * Forward decl's - these suppress compiler warnings when funcs coded further down the file than thier invokation
    */
    
    static int setrbi(struct rpt *myrpt);
    
    
    
    /*
    * Define function protos for function table here
    */
    
    static int function_ilink(struct rpt *myrpt, char *param, char *digitbuf, int command_source);
    static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf, int command_source);
    static int function_autopatchdn(struct rpt *myrpt, char *param, char *digitbuf, int command_source);
    static int function_status(struct rpt *myrpt, char *param, char *digitbuf, int command_source);
    static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source);
    static int function_remote(struct rpt *myrpt, char *param, char *digitbuf, int command_source);
    /*
    * Function table
    */
    
    static struct function_table_tag function_table[] = {
    	{"cop", function_cop},
    	{"autopatchup", function_autopatchup},
    	{"autopatchdn", function_autopatchdn},
    	{"ilink", function_ilink},
    	{"status", function_status},
    	{"remote", function_remote}
    } ;
    	
    static int myatoi(char *str)
    {
    int	ret;
    
    	if (str == NULL) return -1;
    
    	/* leave this %i alone, non-base-10 input is useful here */
    	if (sscanf(str,"%i",&ret) != 1) return -1;
    
    /*
    * Enable or disable debug output at a given level at the console
    */
                                                                                                                                     
    static int rpt_do_debug(int fd, int argc, char *argv[])
    {
    	int newlevel;
    
            if (argc != 4)
                    return RESULT_SHOWUSAGE;
            newlevel = myatoi(argv[3]);
            if((newlevel < 0) || (newlevel > 7))
                    return RESULT_SHOWUSAGE;
            if(newlevel)
                    ast_cli(fd, "app_rpt Debugging enabled, previous level: %d, new level: %d\n", debug, newlevel);
            else
                    ast_cli(fd, "app_rpt Debugging disabled\n");
    
            debug = newlevel;                                                                                                                          
            return RESULT_SUCCESS;
    }
                                                                                                                                     
    
    
    
    static int play_tone_pair(struct ast_channel *chan, int f1, int f2, int duration, int amplitude)
    {
    
    	int res;
    
            if ((res = ast_tonepair_start(chan, f1, f2, duration, amplitude)))
                    return res;
                                                                                                                                                
            while(chan->generatordata) {
    
    		if (ast_safe_sleep(chan,1)) return -1;
    	}
    
    }
    
    static int play_tone(struct ast_channel *chan, int freq, int duration, int amplitude)
    {
    	return play_tone_pair(chan, freq, 0, duration, amplitude);
    }
    
    static int play_silence(struct ast_channel *chan, int duration)
    {
    	return play_tone_pair(chan, 0, 0, duration, 0);
    }
    
    
    static int send_morse(struct ast_channel *chan, char *string, int speed, int freq, int amplitude)
    {
    
    static struct morse_bits mbits[] = {
    		{0, 0}, /* SPACE */
    		{0, 0}, 
    		{6, 18},/* " */
    		{0, 0},
    		{7, 72},/* $ */
    		{0, 0},
    		{0, 0},
    		{6, 30},/* ' */
    		{5, 13},/* ( */
    		{6, 29},/* ) */
    		{0, 0},
    		{5, 10},/* + */
    		{6, 51},/* , */
    		{6, 33},/* - */
    		{6, 42},/* . */
    		{5, 9}, /* / */
    		{5, 31},/* 0 */
    		{5, 30},/* 1 */
    		{5, 28},/* 2 */
    		{5, 24},/* 3 */
    		{5, 16},/* 4 */
    		{5, 0}, /* 5 */
    		{5, 1}, /* 6 */
    		{5, 3}, /* 7 */
    		{5, 7}, /* 8 */
    		{5, 15},/* 9 */
    		{6, 7}, /* : */
    		{6, 21},/* ; */
    		{0, 0},
    		{5, 33},/* = */
    		{0, 0},
    		{6, 12},/* ? */
    		{0, 0},
            	{2, 2}, /* A */
     		{4, 1}, /* B */
    		{4, 5}, /* C */
    		{3, 1}, /* D */
    		{1, 0}, /* E */
    		{4, 4}, /* F */
    		{3, 3}, /* G */
    		{4, 0}, /* H */
    		{2, 0}, /* I */
    		{4, 14},/* J */
    		{3, 5}, /* K */
    		{4, 2}, /* L */
    		{2, 3}, /* M */
    		{2, 1}, /* N */
    		{3, 7}, /* O */
    		{4, 6}, /* P */
    		{4, 11},/* Q */
    		{3, 2}, /* R */
    		{3, 0}, /* S */
    		{1, 1}, /* T */
    		{3, 4}, /* U */
    		{4, 8}, /* V */
    		{3, 6}, /* W */
    		{4, 9}, /* X */
    		{4, 13},/* Y */
    		{4, 3}  /* Z */
    	};
    
    
    	int dottime;
    	int dashtime;
    	int intralettertime;
    	int interlettertime;
    	int interwordtime;
    	int len, ddcomb;
    	int res;
    	int c;
    
    	int i;
    	int flags;
    			
    
    	res = 0;
    	
    	/* Approximate the dot time from the speed arg. */
    	
    	dottime = 900/speed;
    	
    	/* Establish timing releationships */
    	
    	dashtime = 3 * dottime;
    	intralettertime = dottime;
    	interlettertime = dottime * 4 ;
    	interwordtime = dottime * 7;
    	
    	for(;(*string) && (!res); string++){
    	
    		c = *string;
    		
    		/* Convert lower case to upper case */
    		
    		if((c >= 'a') && (c <= 'z'))
    			c -= 0x20;
    		
    		/* Can't deal with any char code greater than Z, skip it */
    		
    		if(c  > 'Z')
    			continue;
    		
    		/* If space char, wait the inter word time */
    					
    		if(c == ' '){
    			if(!res)
    				res = play_silence(chan, interwordtime);
    			continue;
    		}
    		
    		/* Subtract out control char offset to match our table */
    		
    		c -= 0x20;
    		
    		/* Get the character data */
    		
    		len = mbits[c].len;
    		ddcomb = mbits[c].ddcomb;
    		
    		/* Send the character */
    		
    		for(; len ; len--){
    			if(!res)
    				res = play_tone(chan, freq, (ddcomb & 1) ? dashtime : dottime, amplitude);
    			if(!res)
    				res = play_silence(chan, intralettertime);
    			ddcomb >>= 1;
    		}
    		
    		/* Wait the interletter time */
    		
    		if(!res)
    			res = play_silence(chan, interlettertime - intralettertime);
    	}
    	
    	/* Wait for all the frames to be sent */
    	
    	if (!res) 
    		res = ast_waitstream(chan, "");
    	ast_stopstream(chan);
    
    	
    	/*
    	* Wait for the zaptel driver to physically write the tone blocks to the hardware
    	*/
    
    	for(i = 0; i < 20 ; i++){
    		flags =  ZT_IOMUX_WRITEEMPTY | ZT_IOMUX_NOWAIT; 
    		res = ioctl(chan->fds[0], ZT_IOMUX, &flags);
    		if(flags & ZT_IOMUX_WRITEEMPTY)
    			break;
    		if( ast_safe_sleep(chan, 50)){
    			res = -1;
    			break;
    		}
    	}
    
    	
    
    	return res;
    }
    
    static int send_tone_telemetry(struct ast_channel *chan, char *tonestring)
    {
    	char *stringp;
    	char *tonesubset;
    	int f1,f2;
    	int duration;
    	int amplitude;
    	int res;
    
    	int i;
    	int flags;
    
    	
    	res = 0;
    	
    	stringp = ast_strdupa(tonestring);
    
    	for(;tonestring;){
    		tonesubset = strsep(&stringp,")");
    		if(!tonesubset)
    			break;
    		if(sscanf(tonesubset,"(%d,%d,%d,%d", &f1, &f2, &duration, &amplitude) != 4)
    			break;
    		res = play_tone_pair(chan, f1, f2, duration, amplitude);
    		if(res)
    			break;
    	}
    	if(!res)
    		res = play_tone_pair(chan, 0, 0, 100, 0); /* This is needed to ensure the last tone segment is timed correctly */
    	
    	if (!res) 
    		res = ast_waitstream(chan, "");
    	ast_stopstream(chan);
    
    
    	/*
    	* Wait for the zaptel driver to physically write the tone blocks to the hardware
    	*/
    
    	for(i = 0; i < 20 ; i++){
    		flags =  ZT_IOMUX_WRITEEMPTY | ZT_IOMUX_NOWAIT; 
    		res = ioctl(chan->fds[0], ZT_IOMUX, &flags);
    		if(flags & ZT_IOMUX_WRITEEMPTY)
    			break;
    		if( ast_safe_sleep(chan, 50)){
    			res = -1;
    			break;
    		}
    	}
    		
    
    static int sayfile(struct ast_channel *mychannel,char *fname)
    {
    int	res;
    
    	res = ast_streamfile(mychannel, fname, mychannel->language);
    	if (!res) 
    		res = ast_waitstream(mychannel, "");
    	else
    		 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
    	ast_stopstream(mychannel);
    	return res;
    }
    
    static int saycharstr(struct ast_channel *mychannel,char *str)
    {
    int	res;
    
    	res = ast_say_character_str(mychannel,str,NULL,mychannel->language);
    	if (!res) 
    		res = ast_waitstream(mychannel, "");
    	else
    		 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
    	ast_stopstream(mychannel);
    	return res;
    }
    
    
    /* Retrieve an int from a config file */
                                                                                    
    static int retrieve_astcfgint(char *category, char *name, int min, int max, int defl)
    {
            char *var;
            int ret;
                                                                                    
            var = ast_variable_retrieve(cfg, category, name);
            if(var){
                    ret = myatoi(var);
                    if(ret < min)
                            ret = min;
                    if(ret > max)
                            ret = max;
            }
            else
                    ret = defl;
            return ret;
    }
    
    static int telem_any(struct ast_channel *chan, char *entry)
    {
    	int res;
    	char c;
    	
    	static int morsespeed;
    	static int morsefreq;
    	static int morseampl;
    	static int morseidfreq = 0;
    	static int morseidampl;
    	static char mcat[] = MORSE;
    	
    	res = 0;
    	
    	if(!morseidfreq){ /* Get the morse parameters if not already loaded */
    		morsespeed = retrieve_astcfgint( mcat, "speed", 5, 20, 20);
            	morsefreq = retrieve_astcfgint( mcat, "frequency", 300, 3000, 800);
            	morseampl = retrieve_astcfgint( mcat, "amplitude", 200, 8192, 4096);
    		morseidampl = retrieve_astcfgint( mcat, "idamplitude", 200, 8192, 2048);
    		morseidfreq = retrieve_astcfgint( mcat, "idfrequency", 300, 3000, 330);	
    	}
    	
    	/* Is it a file, or a tone sequence? */
    			
    	if(entry[0] == '|'){
    		c = entry[1];
    		if((c >= 'a')&&(c <= 'z'))
    			c -= 0x20;
    	
    		switch(c){
    			case 'I': /* Morse ID */
    				res = send_morse(chan, entry + 2, morsespeed, morseidfreq, morseidampl);
    				break;
    			
    			case 'M': /* Morse Message */
    				res = send_morse(chan, entry + 2, morsespeed, morsefreq, morseampl);
    				break;
    			
    			case 'T': /* Tone sequence */
    				res = send_tone_telemetry(chan, entry + 2);
    				break;
    			default:
    				res = -1;
    		}
    	}
    	else
    		res = sayfile(chan, entry); /* File */
    	return res;
    }
    
    /*
    * This function looks up a telemetry name in the config file, and does a telemetry response as configured.
    *
    * 4 types of telemtry are handled: Morse ID, Morse Message, Tone Sequence, and a File containing a recording.
    */
    
    
    static int telem_lookup(struct ast_channel *chan, char *node, char *name)
    
    	char *telemetry;
    	char *telemetry_save;
    
    	/* Retrieve the section name for telemetry from the node section */
    	
    	telemetry = ast_variable_retrieve(cfg, node, TELEMETRY);
    	if(telemetry){
    		telemetry_save = ast_strdupa(telemetry);
    		if(!telemetry_save){
    			ast_log(LOG_WARNING,"ast_strdupa() failed in telem_lookup()\n");
    			return res;
    		}
    		entry = ast_variable_retrieve(cfg, telemetry_save, name);
    	}
    	
    
    	/* Try to look up the telemetry name */
    	
    	if(!entry){
    		/* Telemetry name wasn't found in the config file, use the default */
    		for(i = 0; i < sizeof(tele_defs)/sizeof(struct telem_defaults) ; i++){
    			if(!strcasecmp(tele_defs[i].name, name))
    				entry = tele_defs[i].value;
    		}
    	}
    	if(entry)	
    		telem_any(chan, entry);
    	else{
    		ast_log(LOG_WARNING, "Telemetry name not found: %s\n", name);
    		res = -1;
    	}
    	return res;
    }
    
    
    /*
    * Retrieve a wait interval
    */
    
    static int get_wait_interval(struct rpt *myrpt, int type)
    {
            int interval;
            char *wait_times;
            char *wait_times_save;
                                                                                                                      
            wait_times_save = NULL;
            wait_times = ast_variable_retrieve(cfg, myrpt->name, "wait_times");
                                                                                                                      
            if(wait_times){
                    wait_times_save = ast_strdupa(wait_times);
                    if(!wait_times_save){
                            ast_log(LOG_WARNING, "Out of memory in wait_interval()\n");
                            wait_times = NULL;
                    }
            }
                                                                                                                      
            switch(type){
                    case DLY_TELEM:
                            if(wait_times)
                                    interval = retrieve_astcfgint(wait_times_save, "telemwait", 500, 5000, 1000);
                            else
                                    interval = 1000;
                            break;
                                                                                                                      
                    case DLY_ID:
                            if(wait_times)
                                    interval = retrieve_astcfgint(wait_times_save, "idwait",250,5000,500);
                            else
                                    interval = 500;
                            break;
                                                                                                                      
                    case DLY_UNKEY:
                            if(wait_times)
                                    interval = retrieve_astcfgint(wait_times_save, "unkeywait",500,5000,1000);
                            else
                                    interval = 1000;
                            break;
                                                                                                                      
                    case DLY_CALLTERM:
                            if(wait_times)
                                    interval = retrieve_astcfgint(wait_times_save, "calltermwait",500,5000,1500);
                            else
                                    interval = 1500;
                            break;
                                                                                                                      
                    default:
                            return 0;
            }
    	return interval;
    }                                                                                                                  
    
    
    
    static void wait_interval(struct rpt *myrpt, int type, struct ast_channel *chan)
    
    	if((interval = get_wait_interval(myrpt, type)))
    		ast_safe_sleep(chan,interval);
    
    static void *rpt_tele_thread(void *this)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    ZT_CONFINFO ci;  /* conference info */
    
    int	res = 0,hastx,imdone = 0, unkeys_queued, x;
    
    struct	rpt_tele *mytele = (struct rpt_tele *)this;
    
    struct	rpt *myrpt;
    struct	rpt_link *l,*m,linkbase;
    struct	ast_channel *mychannel;
    
    int vmajor, vminor;
    
    char *p,*ct,*ct_copy,*ident, *nodename;
    
    time_t t;
    struct tm localtm;
    
    
    	/* get a pointer to myrpt */
    	myrpt = mytele->rpt;
    
    
    	/* Snag copies of a few key myrpt variables */
    	ast_mutex_lock(&myrpt->lock);
    	nodename = ast_strdupa(myrpt->name);
    	ident = ast_strdupa(myrpt->ident);
    	ast_mutex_unlock(&myrpt->lock);
    	
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* allocate a pseudo-channel thru asterisk */
    
    	mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!mychannel)
    	{
    		fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
    
    Jim Dixon's avatar
    Jim Dixon committed
    		ast_mutex_lock(&myrpt->lock);
    
    		remque((struct qelem *)mytele);
    		ast_mutex_unlock(&myrpt->lock);
    		free(mytele);		
    
    Mark Spencer's avatar
    Mark Spencer committed
    		pthread_exit(NULL);
    	}
    
    	ast_mutex_lock(&myrpt->lock);
    	mytele->chan = mychannel; /* Save a copy of the channel so we can access it externally if need be */
    	ast_mutex_unlock(&myrpt->lock);
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* make a conference for the tx */
    	ci.chan = 0;
    
    	/* If there's an ID queued, only connect the ID audio to the local tx conference so 
    		linked systems can't hear it */
    
    	ci.confno = (((mytele->mode == ID) || (mytele->mode == IDTALKOVER) || (mytele->mode == UNKEY)) ?
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ci.confmode = ZT_CONF_CONFANN;
    	/* first put the channel on the conference in announce mode */
    	if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1)
    	{
    		ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
    
    		ast_mutex_lock(&myrpt->lock);
    		remque((struct qelem *)mytele);
    		ast_mutex_unlock(&myrpt->lock);
    		free(mytele);		
    		ast_hangup(mychannel);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		pthread_exit(NULL);
    	}
    	ast_stopstream(mychannel);
    
    		wait_interval(myrpt, (mytele->mode == ID) ? DLY_ID : DLY_TELEM,mychannel);
    
    		res = telem_any(mychannel, ident); 
    		imdone=1;
    	
    
    		
    		
    	    case IDTALKOVER:
    	    	p = ast_variable_retrieve(cfg, nodename, "idtalkover");
    	    	if(p)
    			res = telem_any(mychannel, p); 
    		imdone=1;	
    	    	break;
    	    		
    
    	    case PROC:
    		/* wait a little bit longer */
    
    		wait_interval(myrpt, DLY_TELEM, mychannel);
    
    		res = ast_streamfile(mychannel, "rpt/callproceeding", mychannel->language);
    		break;
    	    case TERM:
    		/* wait a little bit longer */
    
    		wait_interval(myrpt, DLY_CALLTERM, mychannel);
    
    		res = ast_streamfile(mychannel, "rpt/callterminated", mychannel->language);
    		break;
    	    case COMPLETE:
    		/* wait a little bit */
    
    		wait_interval(myrpt, DLY_TELEM, mychannel);
    
    		res = telem_lookup(mychannel, myrpt->name, "functcomplete");
    
    		* Reset the Unkey to CT timer
    		*/
    
    		x = get_wait_interval(myrpt, DLY_UNKEY);
    		ast_mutex_lock(&myrpt->lock);
    		myrpt->unkeytocttimer = x; /* Must be protected as it is changed below */
    		ast_mutex_unlock(&myrpt->lock);
    
    		/*
    		* If there's one already queued, don't do another
    
    		*/
    
    		tlist = myrpt->tele.next;
    		unkeys_queued = 0;
                    if (tlist != &myrpt->tele)
                    {
                            ast_mutex_lock(&myrpt->lock);
                            while(tlist != &myrpt->tele){
                                    if (tlist->mode == UNKEY) unkeys_queued++;
                                    tlist = tlist->next;
                            }
                            ast_mutex_unlock(&myrpt->lock);
    		}
    		if( unkeys_queued > 1){
    			imdone = 1;
    			break;
    		}
    
    
    		/* Wait for the telemetry timer to expire */
    		/* Periodically check the timer since it can be re-initialized above */
    
    		while(myrpt->unkeytocttimer)
    		{
    			int ctint;
    			if(myrpt->unkeytocttimer > 100)
    				ctint = 100;
    			else
    				ctint = myrpt->unkeytocttimer;