diff options
Diffstat (limited to '1.2-netsec/apps/app_rpt.c')
-rw-r--r-- | 1.2-netsec/apps/app_rpt.c | 6560 |
1 files changed, 6560 insertions, 0 deletions
diff --git a/1.2-netsec/apps/app_rpt.c b/1.2-netsec/apps/app_rpt.c new file mode 100644 index 000000000..c8b10e44d --- /dev/null +++ b/1.2-netsec/apps/app_rpt.c @@ -0,0 +1,6560 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2002-2005, Jim Dixon, WB6NIL + * + * Jim Dixon, WB6NIL <jim@lambdatel.com> + * Serious contributions by Steve RoDgers, WA6ZFT <hwstar@rodgers.sdcoxmail.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/* + * + * Radio Repeater / Remote Base program + * version 0.37 11/3/05 + * + * See http://www.zapatatelephony.org/app_rpt.html + * + * + * 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 + * 4 - Test Tone On + * 5 - Dump System Variables on Console (debug) + * 6 - PTT (phone mode only) + * + * 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: + * + * 1 - Recall Memory MM (*000-*099) (Gets memory from rpt.conf) + * 2 - Set VFO MMMMM*KKK*O (Mhz digits, Khz digits, Offset) + * 3 - Set Rx PL Tone HHH*D* + * 4 - Set Tx PL Tone HHH*D* (Not currently implemented with DHE RBI-1) + * 5 - Link Status (long) + * 6 - Set operating mode M (FM, USB, LSB, AM, etc) + * 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 + * 107 - Bump Down 20 Hz + * 108 - Bump Down 100 Hz + * 109 - Bump Down 500 Hz + * 110 - Bump Up 20 Hz + * 111 - Bump Up 100 Hz + * 112 - Bump Up 500 Hz + * 113 - Scan Down Slow + * 114 - Scan Down Medium + * 115 - Scan Down Fast + * 116 - Scan Up Slow + * 117 - Scan Up Medium + * 118 - Scan Up Fast + * 119 - Transmit allowing auto-tune + * 140 - Link Status (brief) + * + * +*/ + +/* 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 MAXDTMF 32 +#define DTMF_TIMEOUT 3 + +#define DISC_TIME 10000 /* report disc after 10 seconds of no connect */ +#define MAX_RETRIES 5 + +#define REDUNDANT_TX_TIME 2000 + +#define RETRY_TIMER_MS 5000 + +#define MAXREMSTR 15 + +#define NODES "nodes" +#define MEMORY "memory" +#define FUNCTIONS "functions" +#define TELEMETRY "telemetry" +#define MORSE "morse" +#define FUNCCHAR '*' +#define ENDCHAR '#' + +#define DEFAULT_IOBASE 0x378 + +#define MAXCONNECTTIME 5000 + +#define MAXNODESTR 300 + +#define ACTIONSIZE 32 + +#define TELEPARAMSIZE 256 + +#define REM_SCANTIME 100 + + +enum {REM_OFF,REM_MONITOR,REM_TX}; + +enum{ID,PROC,TERM,COMPLETE,UNKEY,REMDISC,REMALREADY,REMNOTFOUND,REMGO, + CONNECTED,CONNFAIL,STATUS,TIMEOUT,ID1, STATS_TIME, + STATS_VERSION, IDTALKOVER, ARB_ALPHA, TEST_TONE, REV_PATCH}; + +enum {REM_SIMPLEX,REM_MINUS,REM_PLUS}; + +enum {REM_LOWPWR,REM_MEDPWR,REM_HIPWR}; + +enum {DC_INDETERMINATE, DC_REQ_FLUSH, DC_ERROR, DC_COMPLETE, DC_DOKEY}; + +enum {SOURCE_RPT, SOURCE_LNK, SOURCE_RMT, SOURCE_PHONE, SOURCE_DPHONE}; + +enum {DLY_TELEM, DLY_ID, DLY_UNKEY, DLY_CALLTERM}; + +enum {REM_MODE_FM,REM_MODE_USB,REM_MODE_LSB,REM_MODE_AM}; + +enum {HF_SCAN_OFF,HF_SCAN_DOWN_SLOW,HF_SCAN_DOWN_QUICK,HF_SCAN_DOWN_FAST,HF_SCAN_UP_SLOW,HF_SCAN_UP_QUICK,HF_SCAN_UP_FAST}; + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <signal.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <search.h> +#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> +#include <sys/io.h> +#include <math.h> +#include <tonezone.h> +#include <linux/zaptel.h> +#include <netinet/in.h> +#include <arpa/inet.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/features.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.37 11/03/2005"; + +static char *app = "Rpt"; + +static char *synopsis = "Radio Repeater/Remote Base Control System"; + +static char *descrip = +" Rpt(nodename[|options]): Radio Remote Link or Remote Base Link Endpoint Process.\n" +"\n" +" Not specifying an option puts it in normal endpoint mode (where source\n" +" IP and nodename are verified).\n" +"\n" +" Options are as follows:\n" +"\n" +" X - Normal endpoint mode WITHOUT security check. Only specify\n" +" this if you have checked security already (like with an IAX2\n" +" user/password or something).\n" +"\n" +" Rannounce-string[|timeout[|timeout-destination]] - Amateur Radio\n" +" Reverse Autopatch. Caller is put on hold, and announcement (as\n" +" specified by the 'announce-string') is played on radio system.\n" +" Users of radio system can access autopatch, dial specified\n" +" code, and pick up call. Announce-string is list of names of\n" +" recordings, or \"PARKED\" to substitute code for un-parking,\n" +" or \"NODE\" to substitute node number.\n" +"\n" +" P - Phone Control mode. This allows a regular phone user to have\n" +" full control and audio access to the radio system. For the\n" +" user to have DTMF control, the 'phone_functions' parameter\n" +" must be specified for the node in 'rpt.conf'. An additional\n" +" function (cop,6) must be listed so that PTT control is available.\n" +"\n" +" D - Dumb Phone Control mode. This allows a regular phone user to\n" +" have full control and audio access to the radio system. In this\n" +" mode, the PTT is activated for the entire length of the call.\n" +" For the user to have DTMF control (not generally recomended in\n" +" this mode), the 'dphone_functions' parameter must be specified\n" +" for the node in 'rpt.conf'. Otherwise no DTMF control will be\n" +" available to the phone user.\n" +"\n"; + +static int debug = 0; /* Set this >0 for extra debug output */ +static int nrpts = 0; + +char *discstr = "!!DISCONNECT!!"; +static char *remote_rig_ft897="ft897"; +static char *remote_rig_rbi="rbi"; + +struct ast_config *cfg; + +STANDARD_LOCAL_USER; +LOCAL_USER_DECL; + +#define MSWAIT 200 +#define HANGTIME 5000 +#define TOTIME 180000 +#define IDTIME 300000 +#define MAXRPTS 20 +#define POLITEID 30000 +#define FUNCTDELAY 1500 + +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 phonemode; + char name[MAXNODESTR]; /* identifier (routing) string */ + char lasttx; + char lastrx; + char connected; + char hasconnected; + char outbound; + char disced; + char killme; + long elaptime; + long disctime; + long retrytimer; + 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 ast_channel *chan; + int mode; + struct rpt_link mylink; + char param[TELEPARAMSIZE]; + pthread_t threadid; +} ; + +struct function_table_tag +{ + char action[ACTIONSIZE]; + int (*function)(struct rpt *myrpt, char *param, char *digitbuf, + int command_source, struct rpt_link *mylink); +} ; + +/* Used to store the morse code patterns */ + +struct morse_bits +{ + int len; + int ddcomb; +} ; + +struct telem_defaults +{ + char name[20]; + char value[80]; +} ; + + +static struct rpt +{ + char *name; + ast_mutex_t lock; + char *rxchanname; + char *txchanname; + char *ourcontext; + char *ourcallerid; + char *acctcode; + char *ident; + char *tonezone; + char *functions; + char *link_functions; + char *phone_functions; + char *dphone_functions; + char *nodes; + struct rpt_link links; + int hangtime; + int totime; + int idtime; + int unkeytocttimer; + char keyed; + char exttx; + char localtx; + char remoterx; + char remotetx; + char remoteon; + char simple; + char *remote; + 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,scantimer; + int mustid; + int politeid; + int dtmfidx,rem_dtmfidx; + long retxtimer; + char mydtmf; + int iobase; + char exten[AST_MAX_EXTENSION]; + char freq[MAXREMSTR],rxpl[MAXREMSTR],txpl[MAXREMSTR]; + char offset; + char powerlevel; + char txplon; + char rxplon; + char remmode; + char tunerequest; + char hfscanmode; + int hfscanstatus; + char lastlinknode[MAXNODESTR]; + char funcchar; + char endchar; + char stopgen; + int phone_longestfunc; + int dphone_longestfunc; + int link_longestfunc; + int longestfunc; + int longestnode; + int threadrestarts; + time_t disgorgetime; + time_t lastthreadrestarttime; + char nobusyout; +} rpt_vars[MAXRPTS]; + +/* +* 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, struct rpt_link *mylink); +static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink); +static int function_autopatchdn(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink); +static int function_status(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink); +static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink); +static int function_remote(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink); +/* +* 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; + return ret; +} + +/* +* 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; + } + + return 0; +} + +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, &litude) != 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; + } + } + + return res; + +} + + +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; +} + +static int saynum(struct ast_channel *mychannel, int num) +{ + int res; + res = ast_say_number(mychannel, num, NULL, mychannel->language, NULL); + 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) +{ + + int res; + int i; + char *entry; + char *telemetry; + char *telemetry_save; + + res = 0; + telemetry_save = NULL; + entry = NULL; + + + /* 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; +} + + +/* +* Wait a configurable interval of time +*/ + + +static void wait_interval(struct rpt *myrpt, int type, struct ast_channel *chan) +{ + int interval; + if((interval = get_wait_interval(myrpt, type))) + ast_safe_sleep(chan,interval); + return; +} + + +static void *rpt_tele_thread(void *this) +{ +ZT_CONFINFO ci; /* conference info */ +int res = 0,haslink,hastx,hasremote,imdone = 0, unkeys_queued, x; +struct rpt_tele *mytele = (struct rpt_tele *)this; +struct rpt_tele *tlist; +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); + + + /* allocate a pseudo-channel thru asterisk */ + mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL); + if (!mychannel) + { + fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); + ast_mutex_lock(&myrpt->lock); + remque((struct qelem *)mytele); + ast_mutex_unlock(&myrpt->lock); + free(mytele); + 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); + + /* 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)) ? + myrpt->txconf : myrpt->conf); + 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); + pthread_exit(NULL); + } + ast_stopstream(mychannel); + switch(mytele->mode) + { + case ID: + case ID1: + /* wait a bit */ + wait_interval(myrpt, (mytele->mode == ID) ? DLY_ID : DLY_TELEM,mychannel); + res = telem_any(mychannel, ident); + imdone=1; + + break; + + + 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"); + break; + case UNKEY: + + /* + * 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; + ast_safe_sleep(mychannel, ctint); + ast_mutex_lock(&myrpt->lock); + if(myrpt->unkeytocttimer < ctint) + myrpt->unkeytocttimer = 0; + else + myrpt->unkeytocttimer -= ctint; + ast_mutex_unlock(&myrpt->lock); + } + + + /* + * Now, the carrier on the rptr rx should be gone. + * If it re-appeared, then forget about sending the CT + */ + if(myrpt->keyed){ + imdone = 1; + break; + } + + haslink = 0; + hastx = 0; + hasremote = 0; + l = myrpt->links.next; + if (l != &myrpt->links) + { + ast_mutex_lock(&myrpt->lock); + while(l != &myrpt->links) + { + if (l->name[0] == '0') + { + l = l->next; + continue; + } + haslink = 1; + if (l->mode) { + hastx++; + if (l->isremote) hasremote++; + } + l = l->next; + } + ast_mutex_unlock(&myrpt->lock); + } + if (haslink) + { + + res = telem_lookup(mychannel, myrpt->name, (!hastx) ? "remotemon" : "remotetx"); + if(res) + ast_log(LOG_WARNING, "telem_lookup:remotexx failed on %s\n", mychannel->name); + + + /* if in remote cmd mode, indicate it */ + if (myrpt->cmdnode[0]) + { + ast_safe_sleep(mychannel,200); + res = telem_lookup(mychannel, myrpt->name, "cmdmode"); + if(res) + ast_log(LOG_WARNING, "telem_lookup:cmdmode failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + } + } + else if((ct = ast_variable_retrieve(cfg, nodename, "unlinkedct"))){ /* Unlinked Courtesy Tone */ + ct_copy = ast_strdupa(ct); + res = telem_lookup(mychannel, myrpt->name, ct_copy); + if(res) + ast_log(LOG_WARNING, "telem_lookup:ctx failed on %s\n", mychannel->name); + } + + if (hasremote && (!myrpt->cmdnode[0])) + { + /* set for all to hear */ + ci.chan = 0; + ci.confno = myrpt->conf; + 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); + pthread_exit(NULL); + } + if((ct = ast_variable_retrieve(cfg, nodename, "remotect"))){ /* Unlinked Courtesy Tone */ + ast_safe_sleep(mychannel,200); + ct_copy = ast_strdupa(ct); + res = telem_lookup(mychannel, myrpt->name, ct_copy); + if(res) + ast_log(LOG_WARNING, "telem_lookup:ctx failed on %s\n", mychannel->name); + } + } + imdone = 1; + break; + case REMDISC: + /* wait a little bit */ + wait_interval(myrpt, DLY_TELEM, mychannel); + res = ast_streamfile(mychannel, "rpt/node", mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + ast_say_character_str(mychannel,mytele->mylink.name,NULL,mychannel->language); + res = ast_streamfile(mychannel, ((mytele->mylink.connected) ? + "rpt/remote_disc" : "rpt/remote_busy"), mychannel->language); + break; + case REMALREADY: + /* wait a little bit */ + wait_interval(myrpt, DLY_TELEM, mychannel); + res = ast_streamfile(mychannel, "rpt/remote_already", mychannel->language); + break; + case REMNOTFOUND: + /* wait a little bit */ + wait_interval(myrpt, DLY_TELEM, mychannel); + res = ast_streamfile(mychannel, "rpt/remote_notfound", mychannel->language); + break; + case REMGO: + /* wait a little bit */ + wait_interval(myrpt, DLY_TELEM, mychannel); + res = ast_streamfile(mychannel, "rpt/remote_go", mychannel->language); + break; + case CONNECTED: + /* wait a little bit */ + wait_interval(myrpt, DLY_TELEM, mychannel); + res = ast_streamfile(mychannel, "rpt/node", mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + ast_say_character_str(mychannel,mytele->mylink.name,NULL,mychannel->language); + res = ast_streamfile(mychannel, "rpt/connected", mychannel->language); + break; + case CONNFAIL: + res = ast_streamfile(mychannel, "rpt/node", mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + ast_say_character_str(mychannel,mytele->mylink.name,NULL,mychannel->language); + res = ast_streamfile(mychannel, "rpt/connection_failed", mychannel->language); + break; + case STATUS: + /* wait a little bit */ + wait_interval(myrpt, DLY_TELEM, mychannel); + hastx = 0; + linkbase.next = &linkbase; + linkbase.prev = &linkbase; + ast_mutex_lock(&myrpt->lock); + /* make our own list of links */ + l = myrpt->links.next; + while(l != &myrpt->links) + { + if (l->name[0] == '0') + { + l = l->next; + continue; + } + m = malloc(sizeof(struct rpt_link)); + if (!m) + { + ast_log(LOG_WARNING, "Cannot alloc memory on %s\n", mychannel->name); + ast_mutex_lock(&myrpt->lock); + remque((struct qelem *)mytele); + ast_mutex_unlock(&myrpt->lock); + free(mytele); + ast_hangup(mychannel); + pthread_exit(NULL); + } + memcpy(m,l,sizeof(struct rpt_link)); + m->next = m->prev = NULL; + insque((struct qelem *)m,(struct qelem *)linkbase.next); + l = l->next; + } + ast_mutex_unlock(&myrpt->lock); + res = ast_streamfile(mychannel, "rpt/node", mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + ast_say_character_str(mychannel,myrpt->name,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); + if (myrpt->callmode) + { + hastx = 1; + res = ast_streamfile(mychannel, "rpt/autopatch_on", mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + } + l = linkbase.next; + while(l != &linkbase) + { + hastx = 1; + res = ast_streamfile(mychannel, "rpt/node", mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + ast_say_character_str(mychannel,l->name,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); + res = ast_streamfile(mychannel, ((l->mode) ? + "rpt/tranceive" : "rpt/monitor"), mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + l = l->next; + } + if (!hastx) + { + res = ast_streamfile(mychannel, "rpt/repeat_only", mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + } + /* destroy our local link queue */ + l = linkbase.next; + while(l != &linkbase) + { + m = l; + l = l->next; + remque((struct qelem *)m); + free(m); + } + imdone = 1; + break; + case TIMEOUT: + res = ast_streamfile(mychannel, "rpt/node", mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + ast_say_character_str(mychannel,myrpt->name,NULL,mychannel->language); + res = ast_streamfile(mychannel, "rpt/timeout", mychannel->language); + break; + + case STATS_TIME: + wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */ + t = time(NULL); + localtime_r(&t, &localtm); + /* Say the phase of the day is before the time */ + if((localtm.tm_hour >= 0) && (localtm.tm_hour < 12)) + p = "rpt/goodmorning"; + else if((localtm.tm_hour >= 12) && (localtm.tm_hour < 18)) + p = "rpt/goodafternoon"; + else + p = "rpt/goodevening"; + if (sayfile(mychannel,p) == -1) + { + imdone = 1; + break; + } + /* Say the time is ... */ + if (sayfile(mychannel,"rpt/thetimeis") == -1) + { + imdone = 1; + break; + } + /* Say the time */ + res = ast_say_time(mychannel, t, "", mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + ast_stopstream(mychannel); + imdone = 1; + break; + case STATS_VERSION: + p = strstr(tdesc, "version"); + if(!p) + break; + if(sscanf(p, "version %d.%d", &vmajor, &vminor) != 2) + break; + wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */ + /* Say "version" */ + if (sayfile(mychannel,"rpt/version") == -1) + { + imdone = 1; + break; + } + if(!res) /* Say "X" */ + ast_say_number(mychannel, vmajor, "", mychannel->language, (char *) NULL); + if (!res) + res = ast_waitstream(mychannel, ""); + ast_stopstream(mychannel); + if (saycharstr(mychannel,".") == -1) + { + imdone = 1; + break; + } + if(!res) /* Say "Y" */ + ast_say_number(mychannel, vminor, "", mychannel->language, (char *) NULL); + if (!res){ + res = ast_waitstream(mychannel, ""); + ast_stopstream(mychannel); + } + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + imdone = 1; + break; + case ARB_ALPHA: + wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */ + if(mytele->param) + saycharstr(mychannel, mytele->param); + imdone = 1; + break; + case REV_PATCH: + wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */ + if(mytele->param) { + + /* Parts of this section taken from app_parkandannounce */ + char *tpl_working, *tpl_current; + char *tmp[100], *myparm; + int looptemp=0,i=0, dres = 0; + + + tpl_working = strdupa(mytele->param); + myparm = strsep(&tpl_working,","); + tpl_current=strsep(&tpl_working, ":"); + + while(tpl_current && looptemp < sizeof(tmp)) { + tmp[looptemp]=tpl_current; + looptemp++; + tpl_current=strsep(&tpl_working,":"); + } + + for(i=0; i<looptemp; i++) { + if(!strcmp(tmp[i], "PARKED")) { + ast_say_digits(mychannel, atoi(myparm), "", mychannel->language); + } else if(!strcmp(tmp[i], "NODE")) { + ast_say_digits(mychannel, atoi(myrpt->name), "", mychannel->language); + } else { + dres = ast_streamfile(mychannel, tmp[i], mychannel->language); + if(!dres) { + dres = ast_waitstream(mychannel, ""); + } else { + ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", tmp[i], mychannel->name); + dres = 0; + } + } + } + } + imdone = 1; + break; + case TEST_TONE: + imdone = 1; + myrpt->stopgen = 0; + if ((res = ast_tonepair_start(mychannel, 1004.0, 0, 99999999, 7200.0))) + break; + while(mychannel->generatordata && (!myrpt->stopgen)) { + if (ast_safe_sleep(mychannel,1)) break; + imdone = 1; + } + break; + default: + break; + } + myrpt->stopgen = 0; + if (!imdone) + { + if (!res) + res = ast_waitstream(mychannel, ""); + else { + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + res = 0; + } + } + ast_stopstream(mychannel); + ast_mutex_lock(&myrpt->lock); + remque((struct qelem *)mytele); + ast_mutex_unlock(&myrpt->lock); + free(mytele); + ast_hangup(mychannel); + pthread_exit(NULL); +} + +static void rpt_telemetry(struct rpt *myrpt,int mode, void *data) +{ +struct rpt_tele *tele; +struct rpt_link *mylink = (struct rpt_link *) data; +pthread_attr_t attr; + + tele = malloc(sizeof(struct rpt_tele)); + if (!tele) + { + ast_log(LOG_WARNING, "Unable to allocate memory\n"); + pthread_exit(NULL); + return; + } + /* zero it out */ + memset((char *)tele,0,sizeof(struct rpt_tele)); + tele->rpt = myrpt; + tele->mode = mode; + ast_mutex_lock(&myrpt->lock); + if((mode == CONNFAIL) || (mode == REMDISC) || (mode == CONNECTED)){ + memset(&tele->mylink,0,sizeof(struct rpt_link)); + if (mylink){ + memcpy(&tele->mylink,mylink,sizeof(struct rpt_link)); + } + } + else if ((mode == ARB_ALPHA) || (mode == REV_PATCH)) { + strncpy(tele->param, (char *) data, TELEPARAMSIZE - 1); + tele->param[TELEPARAMSIZE - 1] = 0; + } + insque((struct qelem *)tele,(struct qelem *)myrpt->tele.next); + ast_mutex_unlock(&myrpt->lock); + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + ast_pthread_create(&tele->threadid,&attr,rpt_tele_thread,(void *) tele); + return; +} + +static void *rpt_call(void *this) +{ +ZT_CONFINFO ci; /* conference info */ +struct rpt *myrpt = (struct rpt *)this; +int res; +struct ast_frame wf; +int stopped,congstarted; +struct ast_channel *mychannel,*genchannel; + + myrpt->mydtmf = 0; + /* allocate a pseudo-channel thru asterisk */ + mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL); + if (!mychannel) + { + fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); + pthread_exit(NULL); + } + ci.chan = 0; + ci.confno = myrpt->conf; /* use the pseudo conference */ + ci.confmode = ZT_CONF_REALANDPSEUDO | ZT_CONF_TALKER | ZT_CONF_LISTENER + | ZT_CONF_PSEUDO_TALKER | ZT_CONF_PSEUDO_LISTENER; + /* first put the channel on the conference */ + if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + ast_hangup(mychannel); + myrpt->callmode = 0; + pthread_exit(NULL); + } + /* allocate a pseudo-channel thru asterisk */ + genchannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL); + if (!genchannel) + { + fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); + ast_hangup(mychannel); + pthread_exit(NULL); + } + ci.chan = 0; + ci.confno = myrpt->conf; + ci.confmode = ZT_CONF_REALANDPSEUDO | ZT_CONF_TALKER | ZT_CONF_LISTENER + | ZT_CONF_PSEUDO_TALKER | ZT_CONF_PSEUDO_LISTENER; + /* first put the channel on the conference */ + if (ioctl(genchannel->fds[0],ZT_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + ast_hangup(mychannel); + ast_hangup(genchannel); + myrpt->callmode = 0; + pthread_exit(NULL); + } + if (myrpt->tonezone && (tone_zone_set_zone(mychannel->fds[0],myrpt->tonezone) == -1)) + { + ast_log(LOG_WARNING, "Unable to set tone zone %s\n",myrpt->tonezone); + ast_hangup(mychannel); + ast_hangup(genchannel); + myrpt->callmode = 0; + pthread_exit(NULL); + } + if (myrpt->tonezone && (tone_zone_set_zone(genchannel->fds[0],myrpt->tonezone) == -1)) + { + ast_log(LOG_WARNING, "Unable to set tone zone %s\n",myrpt->tonezone); + ast_hangup(mychannel); + ast_hangup(genchannel); + myrpt->callmode = 0; + pthread_exit(NULL); + } + /* start dialtone */ + if (tone_zone_play_tone(mychannel->fds[0],ZT_TONE_DIALTONE) < 0) + { + ast_log(LOG_WARNING, "Cannot start dialtone\n"); + ast_hangup(mychannel); + ast_hangup(genchannel); + myrpt->callmode = 0; + pthread_exit(NULL); + } + stopped = 0; + congstarted = 0; + while ((myrpt->callmode == 1) || (myrpt->callmode == 4)) + { + + if ((myrpt->callmode == 1) && (myrpt->cidx > 0) && (!stopped)) + { + stopped = 1; + /* stop dial tone */ + tone_zone_play_tone(mychannel->fds[0],-1); + } + if ((myrpt->callmode == 4) && (!congstarted)) + { + congstarted = 1; + /* start congestion tone */ + tone_zone_play_tone(mychannel->fds[0],ZT_TONE_CONGESTION); + } + res = ast_safe_sleep(mychannel, MSWAIT); + if (res < 0) + { + ast_hangup(mychannel); + ast_hangup(genchannel); + ast_mutex_lock(&myrpt->lock); + myrpt->callmode = 0; + ast_mutex_unlock(&myrpt->lock); + pthread_exit(NULL); + } + } + /* stop any tone generation */ + tone_zone_play_tone(mychannel->fds[0],-1); + /* end if done */ + if (!myrpt->callmode) + { + ast_hangup(mychannel); + ast_hangup(genchannel); + ast_mutex_lock(&myrpt->lock); + myrpt->callmode = 0; + ast_mutex_unlock(&myrpt->lock); + pthread_exit(NULL); + } + + if (myrpt->ourcallerid && *myrpt->ourcallerid){ + char *name, *loc, *instr; + instr = strdup(myrpt->ourcallerid); + if(instr){ + ast_callerid_parse(instr, &name, &loc); + if(loc){ + if(mychannel->cid.cid_num) + free(mychannel->cid.cid_num); + mychannel->cid.cid_num = strdup(loc); + } + if(name){ + if(mychannel->cid.cid_name) + free(mychannel->cid.cid_name); + mychannel->cid.cid_name = strdup(name); + } + free(instr); + } + } + + strncpy(mychannel->exten, myrpt->exten, sizeof(mychannel->exten) - 1); + strncpy(mychannel->context, myrpt->ourcontext, sizeof(mychannel->context) - 1); + if (myrpt->acctcode) + strncpy(mychannel->accountcode, myrpt->acctcode, sizeof(mychannel->accountcode) - 1); + mychannel->priority = 1; + ast_channel_undefer_dtmf(mychannel); + if (ast_pbx_start(mychannel) < 0) + { + ast_log(LOG_WARNING, "Unable to start PBX!!\n"); + ast_hangup(mychannel); + ast_hangup(genchannel); + ast_mutex_lock(&myrpt->lock); + myrpt->callmode = 0; + ast_mutex_unlock(&myrpt->lock); + pthread_exit(NULL); + } + usleep(10000); + ast_mutex_lock(&myrpt->lock); + myrpt->callmode = 3; + while(myrpt->callmode) + { + if ((!mychannel->pbx) && (myrpt->callmode != 4)) + { + myrpt->callmode = 4; + ast_mutex_unlock(&myrpt->lock); + /* start congestion tone */ + tone_zone_play_tone(genchannel->fds[0],ZT_TONE_CONGESTION); + ast_mutex_lock(&myrpt->lock); + } + if (myrpt->mydtmf) + { + wf.frametype = AST_FRAME_DTMF; + wf.subclass = myrpt->mydtmf; + wf.offset = 0; + wf.mallocd = 0; + wf.data = NULL; + wf.datalen = 0; + wf.samples = 0; + ast_mutex_unlock(&myrpt->lock); + ast_write(genchannel,&wf); + ast_mutex_lock(&myrpt->lock); + myrpt->mydtmf = 0; + } + ast_mutex_unlock(&myrpt->lock); + usleep(MSWAIT * 1000); + ast_mutex_lock(&myrpt->lock); + } + ast_mutex_unlock(&myrpt->lock); + tone_zone_play_tone(genchannel->fds[0],-1); + if (mychannel->pbx) ast_softhangup(mychannel,AST_SOFTHANGUP_DEV); + ast_hangup(genchannel); + ast_mutex_lock(&myrpt->lock); + myrpt->callmode = 0; + ast_mutex_unlock(&myrpt->lock); + pthread_exit(NULL); +} + +static void send_link_dtmf(struct rpt *myrpt,char c) +{ +char str[300]; +struct ast_frame wf; +struct rpt_link *l; + + snprintf(str, sizeof(str), "D %s %s %d %c", myrpt->cmdnode, myrpt->name, ++(myrpt->dtmfidx), c); + wf.frametype = AST_FRAME_TEXT; + wf.subclass = 0; + wf.offset = 0; + wf.mallocd = 1; + wf.datalen = strlen(str) + 1; + wf.samples = 0; + l = myrpt->links.next; + /* first, see if our dude is there */ + while(l != &myrpt->links) + { + if (l->name[0] == '0') + { + l = l->next; + continue; + } + /* if we found it, write it and were done */ + if (!strcmp(l->name,myrpt->cmdnode)) + { + wf.data = strdup(str); + if (l->chan) ast_write(l->chan,&wf); + return; + } + l = l->next; + } + l = myrpt->links.next; + /* if not, give it to everyone */ + while(l != &myrpt->links) + { + wf.data = strdup(str); + if (l->chan) ast_write(l->chan,&wf); + l = l->next; + } + return; +} + +/* +* Internet linking function +*/ + +static int function_ilink(struct rpt *myrpt, char *param, char *digits, int command_source, struct rpt_link *mylink) +{ + + char *val, *s, *s1, *s2, *tele; + char tmp[300], deststr[300] = "",modechange = 0; + char digitbuf[MAXNODESTR]; + struct rpt_link *l; + ZT_CONFINFO ci; /* conference info */ + + if(!param) + return DC_ERROR; + + + if (!myrpt->enable) + return DC_ERROR; + + strncpy(digitbuf,digits,MAXNODESTR - 1); + + if(debug) + printf("@@@@ ilink param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf); + + switch(myatoi(param)){ + case 1: /* Link off */ + if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0])) + strcpy(digitbuf,myrpt->lastlinknode); + val = ast_variable_retrieve(cfg, myrpt->nodes, digitbuf); + if (!val){ + if(strlen(digitbuf) >= myrpt->longestnode) + return DC_ERROR; + break; + } + strncpy(tmp,val,sizeof(tmp) - 1); + s = tmp; + s1 = strsep(&s,","); + s2 = strsep(&s,","); + ast_mutex_lock(&myrpt->lock); + l = myrpt->links.next; + /* try to find this one in queue */ + while(l != &myrpt->links){ + if (l->name[0] == '0') + { + l = l->next; + continue; + } + /* if found matching string */ + if (!strcmp(l->name, digitbuf)) + break; + l = l->next; + } + if (l != &myrpt->links){ /* if found */ + struct ast_frame wf; + + strncpy(myrpt->lastlinknode,digitbuf,MAXNODESTR - 1); + l->retries = MAX_RETRIES + 1; + l->disced = 1; + ast_mutex_unlock(&myrpt->lock); + wf.frametype = AST_FRAME_TEXT; + wf.subclass = 0; + wf.offset = 0; + wf.mallocd = 1; + wf.datalen = strlen(discstr) + 1; + wf.samples = 0; + wf.data = strdup(discstr); + if (l->chan) + { + ast_write(l->chan,&wf); + if (ast_safe_sleep(l->chan,250) == -1) return DC_ERROR; + ast_softhangup(l->chan,AST_SOFTHANGUP_DEV); + } + rpt_telemetry(myrpt, COMPLETE, NULL); + return DC_COMPLETE; + } + ast_mutex_unlock(&myrpt->lock); + return DC_COMPLETE; + case 2: /* Link Monitor */ + if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0])) + strcpy(digitbuf,myrpt->lastlinknode); + val = ast_variable_retrieve(cfg, myrpt->nodes, digitbuf); + if (!val){ + if(strlen(digitbuf) >= myrpt->longestnode) + return DC_ERROR; + break; + } + strncpy(tmp,val,sizeof(tmp) - 1); + s = tmp; + s1 = strsep(&s,","); + s2 = strsep(&s,","); + ast_mutex_lock(&myrpt->lock); + l = myrpt->links.next; + /* try to find this one in queue */ + while(l != &myrpt->links){ + if (l->name[0] == '0') + { + l = l->next; + continue; + } + /* if found matching string */ + if (!strcmp(l->name, digitbuf)) + break; + l = l->next; + } + /* if found */ + if (l != &myrpt->links) + { + /* if already in this mode, just ignore */ + if ((!l->mode) || (!l->chan)) { + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,REMALREADY,NULL); + return DC_COMPLETE; + + } + ast_mutex_unlock(&myrpt->lock); + if (l->chan) ast_softhangup(l->chan,AST_SOFTHANGUP_DEV); + l->retries = MAX_RETRIES + 1; + l->disced = 2; + modechange = 1; + } else + ast_mutex_unlock(&myrpt->lock); + strncpy(myrpt->lastlinknode,digitbuf,MAXNODESTR - 1); + /* establish call in monitor mode */ + l = malloc(sizeof(struct rpt_link)); + if (!l){ + ast_log(LOG_WARNING, "Unable to malloc\n"); + return DC_ERROR; + } + /* zero the silly thing */ + memset((char *)l,0,sizeof(struct rpt_link)); + snprintf(deststr, sizeof(deststr), "IAX2/%s", s1); + tele = strchr(deststr,'/'); + if (!tele){ + fprintf(stderr,"link2:Dial number (%s) must be in format tech/number\n",deststr); + return DC_ERROR; + } + *tele++ = 0; + l->isremote = (s && ast_true(s)); + strncpy(l->name, digitbuf, MAXNODESTR - 1); + l->chan = ast_request(deststr,AST_FORMAT_SLINEAR,tele,NULL); + if (modechange) l->connected = 1; + if (l->chan){ + ast_set_read_format(l->chan,AST_FORMAT_SLINEAR); + ast_set_write_format(l->chan,AST_FORMAT_SLINEAR); + l->chan->whentohangup = 0; + l->chan->appl = "Apprpt"; + l->chan->data = "(Remote Rx)"; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "rpt (remote) initiating call to %s/%s on %s\n", + deststr,tele,l->chan->name); + if(l->chan->cid.cid_num) + free(l->chan->cid.cid_num); + l->chan->cid.cid_num = strdup(myrpt->name); + ast_call(l->chan,tele,0); + } + else + { + rpt_telemetry(myrpt,CONNFAIL,l); + free(l); + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Unable to place call to %s/%s on %s\n", + deststr,tele,l->chan->name); + return DC_ERROR; + } + /* allocate a pseudo-channel thru asterisk */ + l->pchan = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL); + if (!l->pchan){ + fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); + ast_hangup(l->chan); + free(l); + return DC_ERROR; + } + ast_set_read_format(l->pchan,AST_FORMAT_SLINEAR); + ast_set_write_format(l->pchan,AST_FORMAT_SLINEAR); + /* make a conference for the pseudo-one */ + ci.chan = 0; + ci.confno = myrpt->conf; + ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER; + /* first put the channel on the conference in proper mode */ + if (ioctl(l->pchan->fds[0],ZT_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + ast_hangup(l->chan); + ast_hangup(l->pchan); + free(l); + return DC_ERROR; + } + ast_mutex_lock(&myrpt->lock); + /* insert at end of queue */ + insque((struct qelem *)l,(struct qelem *)myrpt->links.next); + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,COMPLETE,NULL); + return DC_COMPLETE; + case 3: /* Link transceive */ + if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0])) + strcpy(digitbuf,myrpt->lastlinknode); + val = ast_variable_retrieve(cfg, myrpt->nodes, digitbuf); + if (!val){ + if(strlen(digitbuf) >= myrpt->longestnode) + return DC_ERROR; + break; + } + strncpy(tmp,val,sizeof(tmp) - 1); + s = tmp; + s1 = strsep(&s,","); + s2 = strsep(&s,","); + ast_mutex_lock(&myrpt->lock); + l = myrpt->links.next; + /* try to find this one in queue */ + while(l != &myrpt->links){ + if (l->name[0] == '0') + { + l = l->next; + continue; + } + /* if found matching string */ + if (!strcmp(l->name, digitbuf)) + break; + l = l->next; + } + /* if found */ + if (l != &myrpt->links){ + /* if already in this mode, just ignore */ + if ((l->mode) || (!l->chan)) { + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt, REMALREADY, NULL); + return DC_COMPLETE; + } + ast_mutex_unlock(&myrpt->lock); + if (l->chan) ast_softhangup(l->chan, AST_SOFTHANGUP_DEV); + l->retries = MAX_RETRIES + 1; + l->disced = 2; + modechange = 1; + } else + ast_mutex_unlock(&myrpt->lock); + strncpy(myrpt->lastlinknode,digitbuf,MAXNODESTR - 1); + /* establish call in tranceive mode */ + l = malloc(sizeof(struct rpt_link)); + if (!l){ + ast_log(LOG_WARNING, "Unable to malloc\n"); + return(DC_ERROR); + } + /* zero the silly thing */ + memset((char *)l,0,sizeof(struct rpt_link)); + l->mode = 1; + l->outbound = 1; + strncpy(l->name, digitbuf, MAXNODESTR - 1); + l->isremote = (s && ast_true(s)); + if (modechange) l->connected = 1; + snprintf(deststr, sizeof(deststr), "IAX2/%s", s1); + tele = strchr(deststr, '/'); + if (!tele){ + fprintf(stderr,"link3:Dial number (%s) must be in format tech/number\n",deststr); + free(l); + return DC_ERROR; + } + *tele++ = 0; + l->chan = ast_request(deststr, AST_FORMAT_SLINEAR, tele,NULL); + if (l->chan){ + ast_set_read_format(l->chan, AST_FORMAT_SLINEAR); + ast_set_write_format(l->chan, AST_FORMAT_SLINEAR); + l->chan->whentohangup = 0; + l->chan->appl = "Apprpt"; + l->chan->data = "(Remote Rx)"; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "rpt (remote) initiating call to %s/%s on %s\n", + deststr, tele, l->chan->name); + if(l->chan->cid.cid_num) + free(l->chan->cid.cid_num); + l->chan->cid.cid_num = strdup(myrpt->name); + ast_call(l->chan,tele,999); + } + else{ + rpt_telemetry(myrpt,CONNFAIL,l); + free(l); + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Unable to place call to %s/%s on %s\n", + deststr,tele,l->chan->name); + return DC_ERROR; + } + /* allocate a pseudo-channel thru asterisk */ + l->pchan = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL); + if (!l->pchan){ + fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); + ast_hangup(l->chan); + free(l); + return DC_ERROR; + } + ast_set_read_format(l->pchan, AST_FORMAT_SLINEAR); + ast_set_write_format(l->pchan, AST_FORMAT_SLINEAR); + /* make a conference for the tx */ + ci.chan = 0; + ci.confno = myrpt->conf; + ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER; + /* first put the channel on the conference in proper mode */ + if (ioctl(l->pchan->fds[0], ZT_SETCONF, &ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + ast_hangup(l->chan); + ast_hangup(l->pchan); + free(l); + return DC_ERROR; + } + ast_mutex_lock(&myrpt->lock); + /* insert at end of queue */ + insque((struct qelem *)l,(struct qelem *)myrpt->links.next); + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,COMPLETE,NULL); + return DC_COMPLETE; + case 4: /* Enter Command Mode */ + + /* if doesnt allow link cmd, or no links active, return */ + if (((command_source != SOURCE_RPT) && (command_source != SOURCE_PHONE) && (command_source != SOURCE_DPHONE)) || (myrpt->links.next == &myrpt->links)) + return DC_COMPLETE; + + /* if already in cmd mode, or selected self, fughetabahtit */ + if ((myrpt->cmdnode[0]) || (!strcmp(myrpt->name, digitbuf))){ + + rpt_telemetry(myrpt, REMALREADY, NULL); + return DC_COMPLETE; + } + if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0])) + strcpy(digitbuf,myrpt->lastlinknode); + /* node must at least exist in list */ + val = ast_variable_retrieve(cfg, myrpt->nodes, digitbuf); + if (!val){ + if(strlen(digitbuf) >= myrpt->longestnode) + return DC_ERROR; + break; + + } + ast_mutex_lock(&myrpt->lock); + strcpy(myrpt->lastlinknode,digitbuf); + strncpy(myrpt->cmdnode, digitbuf, sizeof(myrpt->cmdnode) - 1); + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt, REMGO, NULL); + return DC_COMPLETE; + + case 5: /* Status */ + rpt_telemetry(myrpt, STATUS, NULL); + return DC_COMPLETE; + + + case 6: /* All Links Off */ + l = myrpt->links.next; + + while(l != &myrpt->links){ + if (l->chan) ast_softhangup(l->chan, AST_SOFTHANGUP_DEV); /* Hang 'em up */ + l = l->next; + } + rpt_telemetry(myrpt, COMPLETE, NULL); + break; + + default: + return DC_ERROR; + + } + + return DC_INDETERMINATE; +} + +/* +* Autopatch up +*/ + +static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink) +{ + pthread_attr_t attr; + + + if (!myrpt->enable) + return DC_ERROR; + + if(debug) + printf("@@@@ Autopatch up\n"); + + ast_mutex_lock(&myrpt->lock); + + /* if on call, force * into current audio stream */ + + if ((myrpt->callmode == 2) || (myrpt->callmode == 3)){ + myrpt->mydtmf = myrpt->funcchar; + } + if (myrpt->callmode){ + ast_mutex_unlock(&myrpt->lock); + return DC_COMPLETE; + } + myrpt->callmode = 1; + myrpt->cidx = 0; + myrpt->exten[myrpt->cidx] = 0; + ast_mutex_unlock(&myrpt->lock); + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + ast_pthread_create(&myrpt->rpt_call_thread,&attr,rpt_call,(void *) myrpt); + return DC_COMPLETE; +} + +/* +* Autopatch down +*/ + +static int function_autopatchdn(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink) +{ + if (!myrpt->enable) + return DC_ERROR; + + if(debug) + printf("@@@@ Autopatch down\n"); + + ast_mutex_lock(&myrpt->lock); + + if (!myrpt->callmode){ + ast_mutex_unlock(&myrpt->lock); + return DC_COMPLETE; + } + + myrpt->callmode = 0; + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt, TERM, NULL); + return DC_COMPLETE; +} + +/* +* Status +*/ + +static int function_status(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink) +{ + + if(!param) + return DC_ERROR; + + + if (!myrpt->enable) + return DC_ERROR; + + if(debug) + printf("@@@@ status param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf); + + switch(myatoi(param)){ + case 1: /* System ID */ + rpt_telemetry(myrpt, ID1, NULL); + return DC_COMPLETE; + case 2: /* System Time */ + rpt_telemetry(myrpt, STATS_TIME, NULL); + return DC_COMPLETE; + case 3: /* app_rpt.c version */ + rpt_telemetry(myrpt, STATS_VERSION, NULL); + default: + return DC_ERROR; + } + return DC_INDETERMINATE; +} + +/* +* COP - Control operator +*/ + +static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink) +{ + if(!param) + return DC_ERROR; + + switch(myatoi(param)){ + case 1: /* System reset */ + system("killall -9 asterisk"); /* FIXME to drastic? */ + return DC_COMPLETE; + + case 2: + myrpt->enable = 1; + rpt_telemetry(myrpt, ARB_ALPHA, (void *) "RPTENA"); + return DC_COMPLETE; + + case 3: + myrpt->enable = 0; + return DC_COMPLETE; + + case 4: /* test tone on */ + rpt_telemetry(myrpt, TEST_TONE, NULL); + return DC_COMPLETE; + + case 5: /* Disgorge variables to log for debug purposes */ + myrpt->disgorgetime = time(NULL) + 10; /* Do it 10 seconds later */ + return DC_COMPLETE; + + case 6: /* Simulate COR being activated (phone only) */ + if (command_source != SOURCE_PHONE) return DC_INDETERMINATE; + return DC_DOKEY; + + } + return DC_INDETERMINATE; +} + +/* +* Collect digits one by one until something matches +*/ + +static int collect_function_digits(struct rpt *myrpt, char *digits, + int command_source, struct rpt_link *mylink) +{ + int i; + char *stringp,*action,*param,*functiondigits; + char function_table_name[30] = ""; + char workstring[80]; + + struct ast_variable *vp; + + if(debug) + printf("@@@@ Digits collected: %s, source: %d\n", digits, command_source); + + if (command_source == SOURCE_DPHONE) { + if (!myrpt->dphone_functions) return DC_INDETERMINATE; + strncpy(function_table_name, myrpt->dphone_functions, sizeof(function_table_name) - 1); + } + else if (command_source == SOURCE_PHONE) { + if (!myrpt->phone_functions) return DC_INDETERMINATE; + strncpy(function_table_name, myrpt->phone_functions, sizeof(function_table_name) - 1); + } + else if (command_source == SOURCE_LNK) + strncpy(function_table_name, myrpt->link_functions, sizeof(function_table_name) - 1); + else + strncpy(function_table_name, myrpt->functions, sizeof(function_table_name) - 1); + vp = ast_variable_browse(cfg, function_table_name); + while(vp) { + if(!strncasecmp(vp->name, digits, strlen(vp->name))) + break; + vp = vp->next; + } + if(!vp) { + int n; + + n = myrpt->longestfunc; + if (command_source == SOURCE_LNK) n = myrpt->link_longestfunc; + else + if (command_source == SOURCE_PHONE) n = myrpt->phone_longestfunc; + else + if (command_source == SOURCE_DPHONE) n = myrpt->dphone_longestfunc; + + if(strlen(digits) >= n) + return DC_ERROR; + else + return DC_INDETERMINATE; + } + /* Found a match, retrieve value part and parse */ + strncpy(workstring, vp->value, sizeof(workstring) - 1 ); + stringp = workstring; + action = strsep(&stringp, ","); + param = stringp; + if(debug) + printf("@@@@ action: %s, param = %s\n",action, (param) ? param : "(null)"); + /* Look up the action */ + for(i = 0 ; i < (sizeof(function_table)/sizeof(struct function_table_tag)); i++){ + if(!strncasecmp(action, function_table[i].action, strlen(action))) + break; + } + if(debug) + printf("@@@@ table index i = %d\n",i); + if(i == (sizeof(function_table)/sizeof(struct function_table_tag))){ + /* Error, action not in table */ + return DC_ERROR; + } + if(function_table[i].function == NULL){ + /* Error, function undefined */ + if(debug) + printf("@@@@ NULL for action: %s\n",action); + return DC_ERROR; + } + functiondigits = digits + strlen(vp->name); + return (*function_table[i].function)(myrpt, param, functiondigits, command_source, mylink); +} + + +static void handle_link_data(struct rpt *myrpt, struct rpt_link *mylink, + char *str) +{ +char tmp[300],cmd[300] = "",dest[300],src[300],c; +int seq, res; +struct rpt_link *l; +struct ast_frame wf; + + wf.frametype = AST_FRAME_TEXT; + wf.subclass = 0; + wf.offset = 0; + wf.mallocd = 1; + wf.datalen = strlen(str) + 1; + wf.samples = 0; + /* put string in our buffer */ + strncpy(tmp,str,sizeof(tmp) - 1); + + if (!strcmp(tmp,discstr)) + { + mylink->disced = 1; + mylink->retries = MAX_RETRIES + 1; + ast_softhangup(mylink->chan,AST_SOFTHANGUP_DEV); + return; + } + if (sscanf(tmp,"%s %s %s %d %c",cmd,dest,src,&seq,&c) != 5) + { + ast_log(LOG_WARNING, "Unable to parse link string %s\n",str); + return; + } + if (strcmp(cmd,"D")) + { + ast_log(LOG_WARNING, "Unable to parse link string %s\n",str); + return; + } + + if (dest[0] == '0') + { + strcpy(dest,myrpt->name); + } + + /* if not for me, redistribute to all links */ + if (strcmp(dest,myrpt->name)) + { + l = myrpt->links.next; + /* see if this is one in list */ + while(l != &myrpt->links) + { + if (l->name[0] == '0') + { + l = l->next; + continue; + } + /* dont send back from where it came */ + if ((l == mylink) || (!strcmp(l->name,mylink->name))) + { + l = l->next; + continue; + } + /* if it is, send it and we're done */ + if (!strcmp(l->name,dest)) + { + /* send, but not to src */ + if (strcmp(l->name,src)) { + wf.data = strdup(str); + if (l->chan) ast_write(l->chan,&wf); + } + return; + } + l = l->next; + } + l = myrpt->links.next; + /* otherwise, send it to all of em */ + while(l != &myrpt->links) + { + if (l->name[0] == '0') + { + l = l->next; + continue; + } + /* dont send back from where it came */ + if ((l == mylink) || (!strcmp(l->name,mylink->name))) + { + l = l->next; + continue; + } + /* send, but not to src */ + if (strcmp(l->name,src)) { + wf.data = strdup(str); + if (l->chan) ast_write(l->chan,&wf); + } + l = l->next; + } + return; + } + ast_mutex_lock(&myrpt->lock); + if (c == myrpt->endchar) myrpt->stopgen = 1; + if (myrpt->callmode == 1) + { + myrpt->exten[myrpt->cidx++] = c; + myrpt->exten[myrpt->cidx] = 0; + /* if this exists */ + if (ast_exists_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL)) + { + myrpt->callmode = 2; + rpt_telemetry(myrpt,PROC,NULL); + } + /* if can continue, do so */ + if (!ast_canmatch_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL)) + { + /* call has failed, inform user */ + myrpt->callmode = 4; + } + } + if ((myrpt->callmode == 2) || (myrpt->callmode == 3)) + { + myrpt->mydtmf = c; + } + if (c == myrpt->funcchar) + { + myrpt->rem_dtmfidx = 0; + myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0; + time(&myrpt->rem_dtmf_time); + ast_mutex_unlock(&myrpt->lock); + return; + } + else if ((c != myrpt->endchar) && (myrpt->rem_dtmfidx >= 0)) + { + time(&myrpt->rem_dtmf_time); + if (myrpt->rem_dtmfidx < MAXDTMF) + { + myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx++] = c; + myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0; + + ast_mutex_unlock(&myrpt->lock); + strncpy(cmd, myrpt->rem_dtmfbuf, sizeof(cmd) - 1); + res = collect_function_digits(myrpt, cmd, SOURCE_LNK, mylink); + ast_mutex_lock(&myrpt->lock); + + switch(res){ + + case DC_INDETERMINATE: + break; + + case DC_REQ_FLUSH: + myrpt->rem_dtmfidx = 0; + myrpt->rem_dtmfbuf[0] = 0; + break; + + + case DC_COMPLETE: + myrpt->rem_dtmfbuf[0] = 0; + myrpt->rem_dtmfidx = -1; + myrpt->rem_dtmf_time = 0; + break; + + case DC_ERROR: + default: + myrpt->rem_dtmfbuf[0] = 0; + myrpt->rem_dtmfidx = -1; + myrpt->rem_dtmf_time = 0; + break; + } + } + + } + ast_mutex_unlock(&myrpt->lock); + return; +} + +static void handle_link_phone_dtmf(struct rpt *myrpt, struct rpt_link *mylink, + char c) +{ + +char cmd[300]; +int res; + + ast_mutex_lock(&myrpt->lock); + if (c == myrpt->endchar) + { + if (mylink->lastrx) + { + mylink->lastrx = 0; + ast_mutex_unlock(&myrpt->lock); + return; + } + myrpt->stopgen = 1; + if (myrpt->cmdnode[0]) + { + myrpt->cmdnode[0] = 0; + myrpt->dtmfidx = -1; + myrpt->dtmfbuf[0] = 0; + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,COMPLETE,NULL); + ast_mutex_unlock(&myrpt->lock); + return; + } + } + if (myrpt->cmdnode[0]) + { + ast_mutex_unlock(&myrpt->lock); + send_link_dtmf(myrpt,c); + return; + } + if (myrpt->callmode == 1) + { + myrpt->exten[myrpt->cidx++] = c; + myrpt->exten[myrpt->cidx] = 0; + /* if this exists */ + if (ast_exists_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL)) + { + myrpt->callmode = 2; + rpt_telemetry(myrpt,PROC,NULL); + } + /* if can continue, do so */ + if (!ast_canmatch_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL)) + { + /* call has failed, inform user */ + myrpt->callmode = 4; + } + } + if ((myrpt->callmode == 2) || (myrpt->callmode == 3)) + { + myrpt->mydtmf = c; + } + if (c == myrpt->funcchar) + { + myrpt->rem_dtmfidx = 0; + myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0; + time(&myrpt->rem_dtmf_time); + ast_mutex_unlock(&myrpt->lock); + return; + } + else if ((c != myrpt->endchar) && (myrpt->rem_dtmfidx >= 0)) + { + time(&myrpt->rem_dtmf_time); + if (myrpt->rem_dtmfidx < MAXDTMF) + { + myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx++] = c; + myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0; + + ast_mutex_unlock(&myrpt->lock); + strncpy(cmd, myrpt->rem_dtmfbuf, sizeof(cmd) - 1); + res = collect_function_digits(myrpt, cmd, + ((mylink->phonemode == 2) ? SOURCE_DPHONE : SOURCE_PHONE), mylink); + ast_mutex_lock(&myrpt->lock); + + switch(res){ + + case DC_INDETERMINATE: + break; + + case DC_DOKEY: + mylink->lastrx = 1; + break; + + case DC_REQ_FLUSH: + myrpt->rem_dtmfidx = 0; + myrpt->rem_dtmfbuf[0] = 0; + break; + + + case DC_COMPLETE: + myrpt->rem_dtmfbuf[0] = 0; + myrpt->rem_dtmfidx = -1; + myrpt->rem_dtmf_time = 0; + break; + + case DC_ERROR: + default: + myrpt->rem_dtmfbuf[0] = 0; + myrpt->rem_dtmfidx = -1; + myrpt->rem_dtmf_time = 0; + break; + } + } + + } + ast_mutex_unlock(&myrpt->lock); + return; +} + +/* Doug Hall RBI-1 serial data definitions: + * + * Byte 0: Expansion external outputs + * Byte 1: + * Bits 0-3 are BAND as follows: + * Bits 4-5 are POWER bits as follows: + * 00 - Low Power + * 01 - Hi Power + * 02 - Med Power + * Bits 6-7 are always set + * Byte 2: + * Bits 0-3 MHZ in BCD format + * Bits 4-5 are offset as follows: + * 00 - minus + * 01 - plus + * 02 - simplex + * 03 - minus minus (whatever that is) + * Bit 6 is the 0/5 KHZ bit + * Bit 7 is always set + * Byte 3: + * Bits 0-3 are 10 KHZ in BCD format + * Bits 4-7 are 100 KHZ in BCD format + * Byte 4: PL Tone code and encode/decode enable bits + * Bits 0-5 are PL tone code (comspec binary codes) + * Bit 6 is encode enable/disable + * Bit 7 is decode enable/disable + */ + +/* take the frequency from the 10 mhz digits (and up) and convert it + to a band number */ + +static int rbi_mhztoband(char *str) +{ +int i; + + i = atoi(str) / 10; /* get the 10's of mhz */ + switch(i) + { + case 2: + return 10; + case 5: + return 11; + case 14: + return 2; + case 22: + return 3; + case 44: + return 4; + case 124: + return 0; + case 125: + return 1; + case 126: + return 8; + case 127: + return 5; + case 128: + return 6; + case 129: + return 7; + default: + break; + } + return -1; +} + +/* take a PL frequency and turn it into a code */ +static int rbi_pltocode(char *str) +{ +int i; +char *s; + + s = strchr(str,'.'); + i = 0; + if (s) i = atoi(s + 1); + i += atoi(str) * 10; + switch(i) + { + case 670: + return 0; + case 719: + return 1; + case 744: + return 2; + case 770: + return 3; + case 797: + return 4; + case 825: + return 5; + case 854: + return 6; + case 885: + return 7; + case 915: + return 8; + case 948: + return 9; + case 974: + return 10; + case 1000: + return 11; + case 1035: + return 12; + case 1072: + return 13; + case 1109: + return 14; + case 1148: + return 15; + case 1188: + return 16; + case 1230: + return 17; + case 1273: + return 18; + case 1318: + return 19; + case 1365: + return 20; + case 1413: + return 21; + case 1462: + return 22; + case 1514: + return 23; + case 1567: + return 24; + case 1622: + return 25; + case 1679: + return 26; + case 1738: + return 27; + case 1799: + return 28; + case 1862: + return 29; + case 1928: + return 30; + case 2035: + return 31; + case 2107: + return 32; + case 2181: + return 33; + case 2257: + return 34; + case 2336: + return 35; + case 2418: + return 36; + case 2503: + return 37; + } + return -1; +} + +/* +* Shift out a formatted serial bit stream +*/ + +static void rbi_out_parallel(struct rpt *myrpt,unsigned char *data) + { + int i,j; + unsigned char od,d; + static volatile long long delayvar; + + for(i = 0 ; i < 5 ; i++){ + od = *data++; + for(j = 0 ; j < 8 ; j++){ + d = od & 1; + outb(d,myrpt->iobase); + /* >= 15 us */ + for(delayvar = 1; delayvar < 15000; delayvar++); + od >>= 1; + outb(d | 2,myrpt->iobase); + /* >= 30 us */ + for(delayvar = 1; delayvar < 30000; delayvar++); + outb(d,myrpt->iobase); + /* >= 10 us */ + for(delayvar = 1; delayvar < 10000; delayvar++); + } + } + /* >= 50 us */ + for(delayvar = 1; delayvar < 50000; delayvar++); + } + +static void rbi_out(struct rpt *myrpt,unsigned char *data) +{ +struct zt_radio_param r; + + memset(&r,0,sizeof(struct zt_radio_param)); + r.radpar = ZT_RADPAR_REMMODE; + r.data = ZT_RADPAR_REM_RBI1; + /* if setparam ioctl fails, its probably not a pciradio card */ + if (ioctl(myrpt->rxchannel->fds[0],ZT_RADIO_SETPARAM,&r) == -1) + { + rbi_out_parallel(myrpt,data); + return; + } + r.radpar = ZT_RADPAR_REMCOMMAND; + memcpy(&r.data,data,5); + if (ioctl(myrpt->rxchannel->fds[0],ZT_RADIO_SETPARAM,&r) == -1) + { + ast_log(LOG_WARNING,"Cannot send RBI command for channel %s\n",myrpt->rxchannel->name); + return; + } +} + +static int serial_remote_io(struct rpt *myrpt, char *txbuf, int txbytes, char *rxbuf, + int rxmaxbytes, int asciiflag) +{ + int i; + struct zt_radio_param prm; + + if(debug){ + printf("String output was: "); + for(i = 0; i < txbytes; i++) + printf("%02X ", (unsigned char ) txbuf[i]); + printf("\n"); + } + + prm.radpar = ZT_RADPAR_REMMODE; + if (asciiflag) prm.data = ZT_RADPAR_REM_SERIAL_ASCII; + else prm.data = ZT_RADPAR_REM_SERIAL; + if (ioctl(myrpt->rxchannel->fds[0],ZT_RADIO_SETPARAM,&prm) == -1) return -1; + prm.radpar = ZT_RADPAR_REMCOMMAND; + prm.data = rxmaxbytes; + memcpy(prm.buf,txbuf,txbytes); + prm.index = txbytes; + if (ioctl(myrpt->rxchannel->fds[0],ZT_RADIO_SETPARAM,&prm) == -1) return -1; + if (rxbuf) + { + *rxbuf = 0; + memcpy(rxbuf,prm.buf,prm.index); + } + return(prm.index); +} + +static int setrbi(struct rpt *myrpt) +{ +char tmp[MAXREMSTR] = "",rbicmd[5],*s; +int band,txoffset = 0,txpower = 0,txpl; + + /* must be a remote system */ + if (!myrpt->remote) return(0); + /* must have rbi hardware */ + if (strncmp(myrpt->remote,remote_rig_rbi,3)) return(0); + strncpy(tmp, myrpt->freq, sizeof(tmp) - 1); + s = strchr(tmp,'.'); + /* if no decimal, is invalid */ + + if (s == NULL){ + if(debug) + printf("@@@@ Frequency needs a decimal\n"); + return -1; + } + + *s++ = 0; + if (strlen(tmp) < 2){ + if(debug) + printf("@@@@ Bad MHz digits: %s\n", tmp); + return -1; + } + + if (strlen(s) < 3){ + if(debug) + printf("@@@@ Bad KHz digits: %s\n", s); + return -1; + } + + if ((s[2] != '0') && (s[2] != '5')){ + if(debug) + printf("@@@@ KHz must end in 0 or 5: %c\n", s[2]); + return -1; + } + + band = rbi_mhztoband(tmp); + if (band == -1){ + if(debug) + printf("@@@@ Bad Band: %s\n", tmp); + return -1; + } + + txpl = rbi_pltocode(myrpt->txpl); + + if (txpl == -1){ + if(debug) + printf("@@@@ Bad TX PL: %s\n", myrpt->txpl); + return -1; + } + + + switch(myrpt->offset) + { + case REM_MINUS: + txoffset = 0; + break; + case REM_PLUS: + txoffset = 0x10; + break; + case REM_SIMPLEX: + txoffset = 0x20; + break; + } + switch(myrpt->powerlevel) + { + case REM_LOWPWR: + txpower = 0; + break; + case REM_MEDPWR: + txpower = 0x20; + break; + case REM_HIPWR: + txpower = 0x10; + break; + } + rbicmd[0] = 0; + rbicmd[1] = band | txpower | 0xc0; + rbicmd[2] = (*(s - 2) - '0') | txoffset | 0x80; + if (s[2] == '5') rbicmd[2] |= 0x40; + rbicmd[3] = ((*s - '0') << 4) + (s[1] - '0'); + rbicmd[4] = txpl; + if (myrpt->txplon) rbicmd[4] |= 0x40; + if (myrpt->rxplon) rbicmd[4] |= 0x80; + rbi_out(myrpt,rbicmd); + return 0; +} + + +/* Check for valid rbi frequency */ +/* Hard coded limits now, configurable later, maybe? */ + +static int check_freq_rbi(int m, int d, int *defmode) +{ + int dflmd = REM_MODE_FM; + + if(m == 50){ /* 6 meters */ + if(d < 10100) + return -1; + } + else if((m >= 51) && ( m < 54)){ + ; + } + else if(m == 144){ /* 2 meters */ + if(d < 10100) + return -1; + } + else if((m >= 145) && (m < 148)){ + ; + } + else if((m >= 222) && (m < 225)){ /* 1.25 meters */ + ; + } + else if((m >= 430) && (m < 450)){ /* 70 centimeters */ + ; + } + else if((m >= 1240) && (m < 1300)){ /* 23 centimeters */ + ; + } + else + return -1; + + if(defmode) + *defmode = dflmd; + + + return 0; +} + +/* +* Split frequency into mhz and decimals +*/ + +static int split_freq(char *mhz, char *decimals, char *freq) +{ + char freq_copy[MAXREMSTR]; + char *decp; + + decp = strchr(strncpy(freq_copy, freq, MAXREMSTR),'.'); + if(decp){ + *decp++ = 0; + strncpy(mhz, freq_copy, MAXREMSTR); + strcpy(decimals, "00000"); + strncpy(decimals, decp, strlen(decp)); + decimals[5] = 0; + return 0; + } + else + return -1; + +} + +/* +* Split ctcss frequency into hertz and decimal +*/ + +static int split_ctcss_freq(char *hertz, char *decimal, char *freq) +{ + char freq_copy[MAXREMSTR]; + char *decp; + + decp = strchr(strncpy(freq_copy, freq, MAXREMSTR),'.'); + if(decp){ + *decp++ = 0; + strncpy(hertz, freq_copy, MAXREMSTR); + strncpy(decimal, decp, strlen(decp)); + decimal[strlen(decp)] = '\0'; + return 0; + } + else + return -1; +} + + + +/* +* FT-897 I/O handlers +*/ + +/* Check to see that the frequency is valid */ +/* Hard coded limits now, configurable later, maybe? */ + + +static int check_freq_ft897(int m, int d, int *defmode) +{ + int dflmd = REM_MODE_FM; + + if(m == 1){ /* 160 meters */ + dflmd = REM_MODE_LSB; + if(d < 80001) + return -1; + } + else if(m == 3){ /* 80 meters */ + dflmd = REM_MODE_LSB; + if(d < 75001) + return -1; + } + else if(m == 7){ /* 40 meters */ + dflmd = REM_MODE_LSB; + if((d < 15001) || (d > 29999)) + return -1; + } + else if(m == 14){ /* 20 meters */ + dflmd = REM_MODE_USB; + if((d < 15001) || (d > 34999)) + return -1; + } + else if(m == 18){ /* 17 meters */ + dflmd = REM_MODE_USB; + if((d < 11001) || (d > 16797)) + return -1; + } + else if(m == 21){ /* 15 meters */ + dflmd = REM_MODE_USB; + if((d < 20001) || (d > 44999)) + return -1; + } + else if(m == 24){ /* 12 meters */ + dflmd = REM_MODE_USB; + if((d < 93001) || (d > 98999)) + return -1; + } + else if(m == 28){ /* 10 meters */ + dflmd = REM_MODE_USB; + if(d < 30001) + return -1; + } + else if(m == 29){ + if(d >= 51000) + dflmd = REM_MODE_FM; + else + dflmd = REM_MODE_USB; + if(d > 69999) + return -1; + } + else if(m == 50){ /* 6 meters */ + if(d < 10100) + return -1; + if(d >= 30000) + dflmd = REM_MODE_FM; + else + dflmd = REM_MODE_USB; + + } + else if((m >= 51) && ( m < 54)){ + dflmd = REM_MODE_FM; + } + else if(m == 144){ /* 2 meters */ + if(d < 10100) + return -1; + if(d >= 30000) + dflmd = REM_MODE_FM; + else + dflmd = REM_MODE_USB; + } + else if((m >= 145) && (m < 148)){ + dflmd = REM_MODE_FM; + } + else if((m >= 430) && (m < 450)){ /* 70 centimeters */ + if(m < 438) + dflmd = REM_MODE_USB; + else + dflmd = REM_MODE_FM; + ; + } + else + return -1; + + if(defmode) + *defmode = dflmd; + + return 0; +} + +/* +* Set a new frequency for the FT897 +*/ + +static int set_freq_ft897(struct rpt *myrpt, char *newfreq) +{ + char mhz[MAXREMSTR]; + char decimals[MAXREMSTR]; + unsigned char cmdstr[5]; + int fd,m,d; + + fd = 0; + if(debug) + printf("New frequency: %s\n",newfreq); + + if(split_freq(mhz, decimals, newfreq)) + return -1; + + m = atoi(mhz); + d = atoi(decimals); + + /* The FT-897 likes packed BCD frequencies */ + + cmdstr[0] = ((m / 100) << 4) + ((m % 100)/10); /* 100MHz 10Mhz */ + cmdstr[1] = ((m % 10) << 4) + (d / 10000); /* 1MHz 100KHz */ + cmdstr[2] = (((d % 10000)/1000) << 4) + ((d % 1000)/ 100); /* 10KHz 1KHz */ + cmdstr[3] = (((d % 100)/10) << 4) + (d % 10); /* 100Hz 10Hz */ + cmdstr[4] = 0x01; /* command */ + + return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0); + +} + +/* ft-897 simple commands */ + +static int simple_command_ft897(struct rpt *myrpt, char command) +{ + unsigned char cmdstr[5]; + + memset(cmdstr, 0, 5); + + cmdstr[4] = command; + + return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0); + +} + +/* ft-897 offset */ + +static int set_offset_ft897(struct rpt *myrpt, char offset) +{ + unsigned char cmdstr[5]; + + memset(cmdstr, 0, 5); + + switch(offset){ + case REM_SIMPLEX: + cmdstr[0] = 0x89; + break; + + case REM_MINUS: + cmdstr[0] = 0x09; + break; + + case REM_PLUS: + cmdstr[0] = 0x49; + break; + + default: + return -1; + } + + cmdstr[4] = 0x09; + + return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0); +} + +/* ft-897 mode */ + +static int set_mode_ft897(struct rpt *myrpt, char newmode) +{ + unsigned char cmdstr[5]; + + memset(cmdstr, 0, 5); + + switch(newmode){ + case REM_MODE_FM: + cmdstr[0] = 0x08; + break; + + case REM_MODE_USB: + cmdstr[0] = 0x01; + break; + + case REM_MODE_LSB: + cmdstr[0] = 0x00; + break; + + case REM_MODE_AM: + cmdstr[0] = 0x04; + break; + + default: + return -1; + } + cmdstr[4] = 0x07; + + return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0); +} + +/* Set tone encode and decode modes */ + +static int set_ctcss_mode_ft897(struct rpt *myrpt, char txplon, char rxplon) +{ + unsigned char cmdstr[5]; + + memset(cmdstr, 0, 5); + + if(rxplon && txplon) + cmdstr[0] = 0x2A; /* Encode and Decode */ + else if (!rxplon && txplon) + cmdstr[0] = 0x4A; /* Encode only */ + else if (rxplon && !txplon) + cmdstr[0] = 0x3A; /* Encode only */ + else + cmdstr[0] = 0x8A; /* OFF */ + + cmdstr[4] = 0x0A; + + return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0); +} + + +/* Set transmit and receive ctcss tone frequencies */ + +static int set_ctcss_freq_ft897(struct rpt *myrpt, char *txtone, char *rxtone) +{ + unsigned char cmdstr[5]; + char hertz[MAXREMSTR],decimal[MAXREMSTR]; + int h,d; + + memset(cmdstr, 0, 5); + + if(split_ctcss_freq(hertz, decimal, txtone)) + return -1; + + h = atoi(hertz); + d = atoi(decimal); + + cmdstr[0] = ((h / 100) << 4) + (h % 100)/ 10; + cmdstr[1] = ((h % 10) << 4) + (d % 10); + + if(rxtone){ + + if(split_ctcss_freq(hertz, decimal, rxtone)) + return -1; + + h = atoi(hertz); + d = atoi(decimal); + + cmdstr[2] = ((h / 100) << 4) + (h % 100)/ 10; + cmdstr[3] = ((h % 10) << 4) + (d % 10); + } + cmdstr[4] = 0x0B; + + return serial_remote_io(myrpt, cmdstr, 5, NULL, 0, 0); +} + + + +static int set_ft897(struct rpt *myrpt) +{ + int res; + + if(debug) + printf("@@@@ lock on\n"); + + res = simple_command_ft897(myrpt, 0x00); /* LOCK on */ + + if(debug) + printf("@@@@ ptt off\n"); + + if(!res) + res = simple_command_ft897(myrpt, 0x88); /* PTT off */ + + if(debug) + printf("Modulation mode\n"); + + if(!res) + res = set_mode_ft897(myrpt, myrpt->remmode); /* Modulation mode */ + + if(debug) + printf("Split off\n"); + + if(!res) + simple_command_ft897(myrpt, 0x82); /* Split off */ + + if(debug) + printf("Frequency\n"); + + if(!res) + res = set_freq_ft897(myrpt, myrpt->freq); /* Frequency */ + if((myrpt->remmode == REM_MODE_FM)){ + if(debug) + printf("Offset\n"); + if(!res) + res = set_offset_ft897(myrpt, myrpt->offset); /* Offset if FM */ + if((!res)&&(myrpt->rxplon || myrpt->txplon)){ + if(debug) + printf("CTCSS tone freqs.\n"); + res = set_ctcss_freq_ft897(myrpt, myrpt->txpl, myrpt->rxpl); /* CTCSS freqs if CTCSS is enabled */ + } + if(!res){ + if(debug) + printf("CTCSS mode\n"); + res = set_ctcss_mode_ft897(myrpt, myrpt->txplon, myrpt->rxplon); /* CTCSS mode */ + } + } + if((myrpt->remmode == REM_MODE_USB)||(myrpt->remmode == REM_MODE_LSB)){ + if(debug) + printf("Clarifier off\n"); + simple_command_ft897(myrpt, 0x85); /* Clarifier off if LSB or USB */ + } + return res; +} + +static int closerem_ft897(struct rpt *myrpt) +{ + simple_command_ft897(myrpt, 0x88); /* PTT off */ + return 0; +} + +/* +* Bump frequency up or down by a small amount +* Return 0 if the new frequnecy is valid, or -1 if invalid +* Interval is in Hz, resolution is 10Hz +*/ + +static int multimode_bump_freq_ft897(struct rpt *myrpt, int interval) +{ + int m,d; + char mhz[MAXREMSTR], decimals[MAXREMSTR]; + + if(debug) + printf("Before bump: %s\n", myrpt->freq); + + if(split_freq(mhz, decimals, myrpt->freq)) + return -1; + + m = atoi(mhz); + d = atoi(decimals); + + d += (interval / 10); /* 10Hz resolution */ + if(d < 0){ + m--; + d += 100000; + } + else if(d >= 100000){ + m++; + d -= 100000; + } + + if(check_freq_ft897(m, d, NULL)){ + if(debug) + printf("Bump freq invalid\n"); + return -1; + } + + snprintf(myrpt->freq, MAXREMSTR, "%d.%05d", m, d); + + if(debug) + printf("After bump: %s\n", myrpt->freq); + + return set_freq_ft897(myrpt, myrpt->freq); +} + + + +/* +* Dispatch to correct I/O handler +*/ + +static int setrem(struct rpt *myrpt) +{ + if(!strcmp(myrpt->remote, remote_rig_ft897)) + return set_ft897(myrpt); + else if(!strcmp(myrpt->remote, remote_rig_rbi)) + return setrbi(myrpt); + else + return -1; +} + +static int closerem(struct rpt *myrpt) +{ + if(!strcmp(myrpt->remote, remote_rig_ft897)) + return closerem_ft897(myrpt); + else + return 0; +} + +/* +* Dispatch to correct frequency checker +*/ + +static int check_freq(struct rpt *myrpt, int m, int d, int *defmode) +{ + if(!strcmp(myrpt->remote, remote_rig_ft897)) + return check_freq_ft897(m, d, defmode); + else if(!strcmp(myrpt->remote, remote_rig_rbi)) + return check_freq_rbi(m, d, defmode); + else + return -1; +} + +/* +* Return 1 if rig is multimode capable +*/ + +static int multimode_capable(struct rpt *myrpt) +{ + if(!strcmp(myrpt->remote, remote_rig_ft897)) + return 1; + return 0; +} + +/* +* Dispatch to correct frequency bumping function +*/ + +static int multimode_bump_freq(struct rpt *myrpt, int interval) +{ + if(!strcmp(myrpt->remote, remote_rig_ft897)) + return multimode_bump_freq_ft897(myrpt, interval); + else + return -1; +} + + +/* +* Queue announcment that scan has been stopped +*/ + +static void stop_scan(struct rpt *myrpt, int flag) +{ + myrpt->hfscanmode = 0; + myrpt->hfscanstatus = ((flag) ? -2 : -1); +} + +/* +* This is called periodically when in scan mode +*/ + + +static int service_scan(struct rpt *myrpt) +{ + int res, interval; + char mhz[MAXREMSTR], decimals[MAXREMSTR], k10=0i, k100=0; + + switch(myrpt->hfscanmode){ + + case HF_SCAN_DOWN_SLOW: + interval = -10; /* 100Hz /sec */ + break; + + case HF_SCAN_DOWN_QUICK: + interval = -50; /* 500Hz /sec */ + break; + + case HF_SCAN_DOWN_FAST: + interval = -200; /* 2KHz /sec */ + break; + + case HF_SCAN_UP_SLOW: + interval = 10; /* 100Hz /sec */ + break; + + case HF_SCAN_UP_QUICK: + interval = 50; /* 500 Hz/sec */ + break; + + case HF_SCAN_UP_FAST: + interval = 200; /* 2KHz /sec */ + break; + + default: + myrpt->hfscanmode = 0; /* Huh? */ + return -1; + } + + res = split_freq(mhz, decimals, myrpt->freq); + + if(!res){ + k100 =decimals[0]; + k10 = decimals[1]; + res = multimode_bump_freq(myrpt, interval); + } + + if(!res) + res = split_freq(mhz, decimals, myrpt->freq); + + + if(res){ + stop_scan(myrpt,1); + return -1; + } + + /* Announce 10KHz boundaries */ + if(k10 != decimals[1]){ + int myhund = (interval < 0) ? k100 : decimals[0]; + int myten = (interval < 0) ? k10 : decimals[1]; + myrpt->hfscanstatus = (myten == '0') ? (myhund - '0') * 100 : (myten - '0') * 10; + } + return res; + +} + + +static int rmt_telem_start(struct rpt *myrpt, struct ast_channel *chan, int delay) +{ + myrpt->remotetx = 0; + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY); + if (!myrpt->remoterx) + ast_indicate(chan,AST_CONTROL_RADIO_KEY); + if (ast_safe_sleep(chan, delay) == -1) + return -1; + return 0; +} + + +static int rmt_telem_finish(struct rpt *myrpt, struct ast_channel *chan) +{ + +struct zt_params par; + + if (ioctl(myrpt->txchannel->fds[0],ZT_GET_PARAMS,&par) == -1) + { + return -1; + + } + if (!par.rxisoffhook) + { + ast_indicate(myrpt->remchannel,AST_CONTROL_RADIO_UNKEY); + myrpt->remoterx = 0; + } + else + { + myrpt->remoterx = 1; + } + return 0; +} + + +static int rmt_sayfile(struct rpt *myrpt, struct ast_channel *chan, int delay, char *filename) +{ + int res; + + res = rmt_telem_start(myrpt, chan, delay); + + if(!res) + res = sayfile(chan, filename); + + if(!res) + res = rmt_telem_finish(myrpt, chan); + return res; +} + +static int rmt_saycharstr(struct rpt *myrpt, struct ast_channel *chan, int delay, char *charstr) +{ + int res; + + res = rmt_telem_start(myrpt, chan, delay); + + if(!res) + res = saycharstr(chan, charstr); + + if(!res) + res = rmt_telem_finish(myrpt, chan); + return res; +} + + + +/* +* Remote base function +*/ + +static int function_remote(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink) +{ + char *s,*s1,*s2,*val; + int i,j,ht,k,l,ls2,m,d,res,offset,offsave, modesave, defmode; + char multimode = 0; + char oc; + char tmp[20], freq[20] = "", savestr[20] = ""; + char mhz[MAXREMSTR], decimals[MAXREMSTR]; + struct ast_channel *mychannel; + + if((!param) || (command_source == SOURCE_RPT) || (command_source == SOURCE_LNK)) + return DC_ERROR; + + multimode = multimode_capable(myrpt); + + mychannel = myrpt->remchannel; + + + switch(myatoi(param)){ + + case 1: /* retrieve memory */ + if(strlen(digitbuf) < 2) /* needs 2 digits */ + break; + + for(i = 0 ; i < 2 ; i++){ + if((digitbuf[i] < '0') || (digitbuf[i] > '9')) + return DC_ERROR; + } + + val = ast_variable_retrieve(cfg, MEMORY, digitbuf); + if (!val){ + if (ast_safe_sleep(mychannel,1000) == -1) + return DC_ERROR; + sayfile(mychannel,"rpt/memory_notfound"); + return DC_COMPLETE; + } + strncpy(tmp,val,sizeof(tmp) - 1); + s = strchr(tmp,','); + if (!s) + return DC_ERROR; + *s++ = 0; + s1 = strchr(s,','); + if (!s1) + return DC_ERROR; + *s1++ = 0; + strncpy(myrpt->freq, tmp, sizeof(myrpt->freq) - 1); + strncpy(myrpt->rxpl, s, sizeof(myrpt->rxpl) - 1); + strncpy(myrpt->txpl, s, sizeof(myrpt->rxpl) - 1); + myrpt->remmode = REM_MODE_FM; + myrpt->offset = REM_SIMPLEX; + myrpt->powerlevel = REM_MEDPWR; + myrpt->txplon = myrpt->rxplon = 0; + while(*s1) + { + switch(*s1++){ + case 'A': + case 'a': + strcpy(myrpt->rxpl, "100.0"); + strcpy(myrpt->txpl, "100.0"); + myrpt->remmode = REM_MODE_AM; + break; + + case 'B': + case 'b': + strcpy(myrpt->rxpl, "100.0"); + strcpy(myrpt->txpl, "100.0"); + myrpt->remmode = REM_MODE_LSB; + break; + + case 'F': + myrpt->remmode = REM_MODE_FM; + break; + + case 'L': + case 'l': + myrpt->powerlevel = REM_LOWPWR; + break; + case 'H': + case 'h': + myrpt->powerlevel = REM_HIPWR; + break; + + case 'M': + case 'm': + myrpt->powerlevel = REM_MEDPWR; + break; + + case '-': + myrpt->offset = REM_MINUS; + break; + + case '+': + myrpt->offset = REM_PLUS; + break; + + case 'S': + case 's': + myrpt->offset = REM_SIMPLEX; + break; + + case 'T': + case 't': + myrpt->txplon = 1; + break; + + case 'R': + case 'r': + myrpt->rxplon = 1; + break; + + case 'U': + case 'u': + strcpy(myrpt->rxpl, "100.0"); + strcpy(myrpt->txpl, "100.0"); + myrpt->remmode = REM_MODE_USB; + break; + } + } + + + if (setrem(myrpt) == -1) + return DC_ERROR; + + + return DC_COMPLETE; + + case 2: /* set freq and offset */ + + + for(i = 0, j = 0, k = 0, l = 0 ; digitbuf[i] ; i++){ /* look for M+*K+*O or M+*H+* depending on mode */ + if(digitbuf[i] == '*'){ + j++; + continue; + } + if((digitbuf[i] < '0') || (digitbuf[i] > '9')) + goto invalid_freq; + else{ + if(j == 0) + l++; /* # of digits before first * */ + if(j == 1) + k++; /* # of digits after first * */ + } + } + + i = strlen(digitbuf) - 1; + if(multimode){ + if((j > 2) || (l > 3) || (k > 6)) + goto invalid_freq; /* &^@#! */ + } + else{ + if((j > 2) || (l > 4) || (k > 3)) + goto invalid_freq; /* &^@#! */ + } + + /* Wait for M+*K+* */ + + if(j < 2) + break; /* Not yet */ + + /* We have a frequency */ + + strncpy(tmp, digitbuf ,sizeof(tmp) - 1); + + s = tmp; + s1 = strsep(&s, "*"); /* Pick off MHz */ + s2 = strsep(&s,"*"); /* Pick off KHz and Hz */ + ls2 = strlen(s2); + + switch(ls2){ /* Allow partial entry of khz and hz digits for laziness support */ + case 1: + ht = 0; + k = 100 * atoi(s2); + break; + + case 2: + ht = 0; + k = 10 * atoi(s2); + break; + + case 3: + if(!multimode){ + if((s2[2] != '0')&&(s2[2] != '5')) + goto invalid_freq; + } + ht = 0; + k = atoi(s2); + break; + case 4: + k = atoi(s2)/10; + ht = 10 * (atoi(s2+(ls2-1))); + break; + + case 5: + k = atoi(s2)/100; + ht = (atoi(s2+(ls2-2))); + break; + + default: + goto invalid_freq; + } + + /* Check frequency for validity and establish a default mode */ + + snprintf(freq, sizeof(freq), "%s.%03d%02d",s1, k, ht); + + if(debug) + printf("New frequency: %s\n", freq); + + split_freq(mhz, decimals, freq); + m = atoi(mhz); + d = atoi(decimals); + + if(check_freq(myrpt, m, d, &defmode)) /* Check to see if frequency entered is legit */ + goto invalid_freq; + + + if((defmode == REM_MODE_FM) && (digitbuf[i] == '*')) /* If FM, user must enter and additional offset digit */ + break; /* Not yet */ + + + offset = REM_SIMPLEX; /* Assume simplex */ + + if(defmode == REM_MODE_FM){ + oc = *s; /* Pick off offset */ + + if (oc){ + switch(oc){ + case '1': + offset = REM_MINUS; + break; + + case '2': + offset = REM_SIMPLEX; + break; + + case '3': + offset = REM_PLUS; + break; + + default: + goto invalid_freq; + } + } + } + offsave = myrpt->offset; + modesave = myrpt->remmode; + strncpy(savestr, myrpt->freq, sizeof(savestr) - 1); + strncpy(myrpt->freq, freq, sizeof(myrpt->freq) - 1); + myrpt->offset = offset; + myrpt->remmode = defmode; + + if (setrem(myrpt) == -1){ + myrpt->offset = offsave; + myrpt->remmode = modesave; + strncpy(myrpt->freq, savestr, sizeof(myrpt->freq) - 1); + goto invalid_freq; + } + + return DC_COMPLETE; + + + invalid_freq: + + rmt_sayfile(myrpt, mychannel, 1000, "rpt/invalid-freq"); + + return DC_ERROR; + + case 3: /* set rx PL tone */ + + for(i = 0, j = 0, k = 0, l = 0 ; digitbuf[i] ; i++){ /* look for N+*N */ + if(digitbuf[i] == '*'){ + j++; + continue; + } + if((digitbuf[i] < '0') || (digitbuf[i] > '9')) + return DC_ERROR; + else{ + if(j) + l++; + else + k++; + } + } + if((j > 1) || (k > 3) || (l > 1)) + return DC_ERROR; /* &$@^! */ + i = strlen(digitbuf) - 1; + if((j != 1) || (k < 2)|| (l != 1)) + break; /* Not yet */ + if(debug) + printf("PL digits entered %s\n", digitbuf); + + strncpy(tmp, digitbuf, sizeof(tmp) - 1); + /* see if we have at least 1 */ + s = strchr(tmp,'*'); + if(s) + *s = '.'; + strncpy(savestr, myrpt->rxpl, sizeof(savestr) - 1); + strncpy(myrpt->rxpl, tmp, sizeof(myrpt->rxpl) - 1); + + if (setrem(myrpt) == -1){ + strncpy(myrpt->rxpl, savestr, sizeof(myrpt->rxpl) - 1); + return DC_ERROR; + } + + + return DC_COMPLETE; + + case 4: /* set tx PL tone */ + + for(i = 0, j = 0, k = 0, l = 0 ; digitbuf[i] ; i++){ /* look for N+*N */ + if(digitbuf[i] == '*'){ + j++; + continue; + } + if((digitbuf[i] < '0') || (digitbuf[i] > '9')) + return DC_ERROR; + else{ + if(j) + l++; + else + k++; + } + } + if((j > 1) || (k > 3) || (l > 1)) + return DC_ERROR; /* &$@^! */ + i = strlen(digitbuf) - 1; + if((j != 1) || (k < 2)|| (l != 1)) + break; /* Not yet */ + if(debug) + printf("PL digits entered %s\n", digitbuf); + + strncpy(tmp, digitbuf, sizeof(tmp) - 1); + /* see if we have at least 1 */ + s = strchr(tmp,'*'); + if(s) + *s = '.'; + strncpy(savestr, myrpt->txpl, sizeof(savestr) - 1); + strncpy(myrpt->txpl, tmp, sizeof(myrpt->txpl) - 1); + + if (setrem(myrpt) == -1){ + strncpy(myrpt->txpl, savestr, sizeof(myrpt->txpl) - 1); + return DC_ERROR; + } + + + return DC_COMPLETE; + + + case 6: /* MODE (FM,USB,LSB,AM) */ + if(strlen(digitbuf) < 1) + break; + + if(!multimode) + return DC_ERROR; /* Multimode radios only */ + + switch(*digitbuf){ + case '1': + split_freq(mhz, decimals, myrpt->freq); + m=atoi(mhz); + if(m < 29) /* No FM allowed below 29MHz! */ + return DC_ERROR; + myrpt->remmode = REM_MODE_FM; + res = rmt_saycharstr(myrpt, mychannel, 1000,"FM"); + break; + + case '2': + myrpt->remmode = REM_MODE_USB; + res = rmt_saycharstr(myrpt, mychannel, 1000,"USB"); + break; + + case '3': + myrpt->remmode = REM_MODE_LSB; + res = rmt_saycharstr(myrpt, mychannel, 1000,"LSB"); + break; + + case '4': + myrpt->remmode = REM_MODE_AM; + res = rmt_saycharstr(myrpt, mychannel, 1000,"AM"); + break; + + default: + return DC_ERROR; + } + if(res) + return DC_ERROR; + + if(setrem(myrpt)) + return DC_ERROR; + return DC_COMPLETE; + + case 100: /* other stuff */ + case 101: + case 102: + case 103: + case 104: + case 105: + case 106: + res = rmt_telem_start(myrpt, mychannel, 1000); + switch(myatoi(param)){ /* Quick commands requiring a setrem call */ + case 100: /* RX PL Off */ + myrpt->rxplon = 0; + if(!res) + res = sayfile(mychannel, "rpt/rxpl"); + if(!res) + sayfile(mychannel, "rpt/off"); + break; + + case 101: /* RX PL On */ + myrpt->rxplon = 1; + if(!res) + res = sayfile(mychannel, "rpt/rxpl"); + if(!res) + sayfile(mychannel, "rpt/on"); + break; + + + case 102: /* TX PL Off */ + myrpt->txplon = 0; + if(!res) + res = sayfile(mychannel, "rpt/txpl"); + if(!res) + sayfile(mychannel, "rpt/off"); + break; + + case 103: /* TX PL On */ + myrpt->txplon = 1; + if(!res) + res = sayfile(mychannel, "rpt/txpl"); + if(!res) + sayfile(mychannel, "rpt/on"); + break; + + case 104: /* Low Power */ + myrpt->powerlevel = REM_LOWPWR; + if(!res) + res = sayfile(mychannel, "rpt/lopwr"); + break; + + case 105: /* Medium Power */ + myrpt->powerlevel = REM_MEDPWR; + if(!res) + res = sayfile(mychannel, "rpt/medpwr"); + break; + + case 106: /* Hi Power */ + myrpt->powerlevel = REM_HIPWR; + if(!res) + res = sayfile(mychannel, "rpt/hipwr"); + break; + + default: + if(!res) + rmt_telem_finish(myrpt, mychannel); + return DC_ERROR; + } + if(!res) + res = rmt_telem_finish(myrpt, mychannel); + if(res) + return DC_ERROR; + + if (setrem(myrpt) == -1) + return DC_ERROR; + return DC_COMPLETE; + + case 107: /* Bump down 20Hz */ + multimode_bump_freq(myrpt, -20); + return DC_COMPLETE; + + case 108: /* Bump down 100Hz */ + multimode_bump_freq(myrpt, -100); + return DC_COMPLETE; + + case 109: /* Bump down 500Hz */ + multimode_bump_freq(myrpt, -500); + return DC_COMPLETE; + + case 110: /* Bump up 20Hz */ + multimode_bump_freq(myrpt, 20); + return DC_COMPLETE; + + case 111: /* Bump up 100Hz */ + multimode_bump_freq(myrpt, 100); + return DC_COMPLETE; + + case 112: /* Bump up 500Hz */ + multimode_bump_freq(myrpt, 500); + return DC_COMPLETE; + + + case 113: + case 114: + case 115: + case 116: + case 117: + case 118: + myrpt->remotetx = 0; + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY); + if (!myrpt->remoterx) + ast_indicate(mychannel,AST_CONTROL_RADIO_KEY); + if (ast_safe_sleep(mychannel,1000) == -1) + return DC_ERROR; + + switch(myatoi(param)){ + + case 113: /* Scan down slow */ + res = sayfile(mychannel,"rpt/down"); + if(!res) + res = sayfile(mychannel, "rpt/slow"); + if(!res){ + myrpt->scantimer = REM_SCANTIME; + myrpt->hfscanmode = HF_SCAN_DOWN_SLOW; + } + break; + + case 114: /* Scan down quick */ + res = sayfile(mychannel,"rpt/down"); + if(!res) + res = sayfile(mychannel, "rpt/quick"); + if(!res){ + myrpt->scantimer = REM_SCANTIME; + myrpt->hfscanmode = HF_SCAN_DOWN_QUICK; + } + break; + + case 115: /* Scan down fast */ + res = sayfile(mychannel,"rpt/down"); + if(!res) + res = sayfile(mychannel, "rpt/fast"); + if(!res){ + myrpt->scantimer = REM_SCANTIME; + myrpt->hfscanmode = HF_SCAN_DOWN_FAST; + } + break; + + case 116: /* Scan up slow */ + res = sayfile(mychannel,"rpt/up"); + if(!res) + res = sayfile(mychannel, "rpt/slow"); + if(!res){ + myrpt->scantimer = REM_SCANTIME; + myrpt->hfscanmode = HF_SCAN_UP_SLOW; + } + break; + + case 117: /* Scan up quick */ + res = sayfile(mychannel,"rpt/up"); + if(!res) + res = sayfile(mychannel, "rpt/quick"); + if(!res){ + myrpt->scantimer = REM_SCANTIME; + myrpt->hfscanmode = HF_SCAN_UP_QUICK; + } + break; + + case 118: /* Scan up fast */ + res = sayfile(mychannel,"rpt/up"); + if(!res) + res = sayfile(mychannel, "rpt/fast"); + if(!res){ + myrpt->scantimer = REM_SCANTIME; + myrpt->hfscanmode = HF_SCAN_UP_FAST; + } + break; + } + rmt_telem_finish(myrpt,mychannel); + return DC_COMPLETE; + + + case 119: /* Tune Request */ + myrpt->tunerequest = 1; + return DC_COMPLETE; + + case 5: /* Long Status */ + case 140: /* Short Status */ + res = rmt_telem_start(myrpt, mychannel, 1000); + + res = sayfile(mychannel,"rpt/node"); + if(!res) + res = saycharstr(mychannel, myrpt->name); + if(!res) + res = sayfile(mychannel,"rpt/frequency"); + if(!res) + res = split_freq(mhz, decimals, myrpt->freq); + if(!res){ + m = atoi(mhz); + if(m < 100) + res = saynum(mychannel, m); + else + res = saycharstr(mychannel, mhz); + } + if(!res) + res = sayfile(mychannel, "letters/dot"); + if(!res) + res = saycharstr(mychannel, decimals); + + if(res){ + rmt_telem_finish(myrpt,mychannel); + return DC_ERROR; + } + if(myrpt->remmode == REM_MODE_FM){ /* Mode FM? */ + switch(myrpt->offset){ + + case REM_MINUS: + res = sayfile(mychannel,"rpt/minus"); + break; + + case REM_SIMPLEX: + res = sayfile(mychannel,"rpt/simplex"); + break; + + case REM_PLUS: + res = sayfile(mychannel,"rpt/plus"); + break; + + default: + return DC_ERROR; + + } + } + else{ /* Must be USB, LSB, or AM */ + switch(myrpt->remmode){ + + case REM_MODE_USB: + res = saycharstr(mychannel, "USB"); + break; + + case REM_MODE_LSB: + res = saycharstr(mychannel, "LSB"); + break; + + case REM_MODE_AM: + res = saycharstr(mychannel, "AM"); + break; + + + default: + return DC_ERROR; + } + } + + if (res == -1){ + rmt_telem_finish(myrpt,mychannel); + return DC_ERROR; + } + + if(myatoi(param) == 140){ /* Short status? */ + if(!res) + res = rmt_telem_finish(myrpt, mychannel); + if(res) + return DC_ERROR; + return DC_COMPLETE; + } + + switch(myrpt->powerlevel){ + + case REM_LOWPWR: + res = sayfile(mychannel,"rpt/lopwr") ; + break; + + case REM_MEDPWR: + res = sayfile(mychannel,"rpt/medpwr"); + break; + case REM_HIPWR: + res = sayfile(mychannel,"rpt/hipwr"); + break; + } + if (res || (sayfile(mychannel,"rpt/rxpl") == -1) || + (sayfile(mychannel,"rpt/frequency") == -1) || + (saycharstr(mychannel,myrpt->rxpl) == -1) || + (sayfile(mychannel,"rpt/txpl") == -1) || + (sayfile(mychannel,"rpt/frequency") == -1) || + (saycharstr(mychannel,myrpt->txpl) == -1) || + (sayfile(mychannel,"rpt/txpl") == -1) || + (sayfile(mychannel,((myrpt->txplon) ? "rpt/on" : "rpt/off")) == -1) || + (sayfile(mychannel,"rpt/rxpl") == -1) || + (sayfile(mychannel,((myrpt->rxplon) ? "rpt/on" : "rpt/off")) == -1)) + { + rmt_telem_finish(myrpt,mychannel); + return DC_ERROR; + } + if(!res) + res = rmt_telem_finish(myrpt,mychannel); + if(res) + return DC_ERROR; + + return DC_COMPLETE; + default: + return DC_ERROR; + } + + return DC_INDETERMINATE; +} + +static int handle_remote_dtmf_digit(struct rpt *myrpt,char c, char *keyed, int phonemode) +{ +time_t now; +int ret,res = 0,src; + + /* Stop scan mode if in scan mode */ + if(myrpt->hfscanmode){ + stop_scan(myrpt,0); + return 0; + } + + time(&now); + /* if timed-out */ + if ((myrpt->dtmf_time_rem + DTMF_TIMEOUT) < now) + { + myrpt->dtmfidx = -1; + myrpt->dtmfbuf[0] = 0; + myrpt->dtmf_time_rem = 0; + } + /* if decode not active */ + if (myrpt->dtmfidx == -1) + { + /* if not lead-in digit, dont worry */ + if (c != myrpt->funcchar) return 0; + myrpt->dtmfidx = 0; + myrpt->dtmfbuf[0] = 0; + myrpt->dtmf_time_rem = now; + return 0; + } + /* if too many in buffer, start over */ + if (myrpt->dtmfidx >= MAXDTMF) + { + myrpt->dtmfidx = 0; + myrpt->dtmfbuf[0] = 0; + myrpt->dtmf_time_rem = now; + } + if (c == myrpt->funcchar) + { + /* if star at beginning, or 2 together, erase buffer */ + if ((myrpt->dtmfidx < 1) || + (myrpt->dtmfbuf[myrpt->dtmfidx - 1] == myrpt->funcchar)) + { + myrpt->dtmfidx = 0; + myrpt->dtmfbuf[0] = 0; + myrpt->dtmf_time_rem = now; + return 0; + } + } + myrpt->dtmfbuf[myrpt->dtmfidx++] = c; + myrpt->dtmfbuf[myrpt->dtmfidx] = 0; + myrpt->dtmf_time_rem = now; + + + src = SOURCE_RMT; + if (phonemode > 1) src = SOURCE_DPHONE; + else if (phonemode) src = SOURCE_PHONE; + ret = collect_function_digits(myrpt, myrpt->dtmfbuf, src, NULL); + + switch(ret){ + + case DC_INDETERMINATE: + res = 0; + break; + + case DC_DOKEY: + if (keyed) *keyed = 1; + res = 0; + break; + + case DC_REQ_FLUSH: + myrpt->dtmfidx = 0; + myrpt->dtmfbuf[0] = 0; + res = 0; + break; + + + case DC_COMPLETE: + myrpt->dtmfbuf[0] = 0; + myrpt->dtmfidx = -1; + myrpt->dtmf_time_rem = 0; + res = 1; + break; + + case DC_ERROR: + default: + myrpt->dtmfbuf[0] = 0; + myrpt->dtmfidx = -1; + myrpt->dtmf_time_rem = 0; + res = 0; + break; + } + + return res; +} + +static int handle_remote_data(struct rpt *myrpt, char *str) +{ +char tmp[300],cmd[300],dest[300],src[300],c; +int seq,res; + + /* put string in our buffer */ + strncpy(tmp,str,sizeof(tmp) - 1); + if (!strcmp(tmp,discstr)) return 0; + if (sscanf(tmp,"%s %s %s %d %c",cmd,dest,src,&seq,&c) != 5) + { + ast_log(LOG_WARNING, "Unable to parse link string %s\n",str); + return 0; + } + if (strcmp(cmd,"D")) + { + ast_log(LOG_WARNING, "Unable to parse link string %s\n",str); + return 0; + } + /* if not for me, ignore */ + if (strcmp(dest,myrpt->name)) return 0; + res = handle_remote_dtmf_digit(myrpt,c, NULL, 0); + if (res != 1) + return res; + myrpt->remotetx = 0; + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY); + if (!myrpt->remoterx) + { + ast_indicate(myrpt->remchannel,AST_CONTROL_RADIO_KEY); + } + if (ast_safe_sleep(myrpt->remchannel,1000) == -1) return -1; + res = telem_lookup(myrpt->remchannel, myrpt->name, "functcomplete"); + rmt_telem_finish(myrpt,myrpt->remchannel); + return res; +} + +static int handle_remote_phone_dtmf(struct rpt *myrpt, char c, char *keyed, int phonemode) +{ +int res; + + + if (keyed && *keyed && (c == myrpt->endchar)) + { + *keyed = 0; + return DC_INDETERMINATE; + } + + res = handle_remote_dtmf_digit(myrpt,c,keyed, phonemode); + if (res != 1) + return res; + myrpt->remotetx = 0; + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY); + if (!myrpt->remoterx) + { + ast_indicate(myrpt->remchannel,AST_CONTROL_RADIO_KEY); + } + if (ast_safe_sleep(myrpt->remchannel,1000) == -1) return -1; + res = telem_lookup(myrpt->remchannel, myrpt->name, "functcomplete"); + rmt_telem_finish(myrpt,myrpt->remchannel); + return res; +} + +static int attempt_reconnect(struct rpt *myrpt, struct rpt_link *l) +{ + char *val, *s, *s1, *s2, *tele; + char tmp[300], deststr[300] = ""; + + val = ast_variable_retrieve(cfg, myrpt->nodes, l->name); + if (!val) + { + fprintf(stderr,"attempt_reconnect: cannot find node %s\n",l->name); + return -1; + } + + ast_mutex_lock(&myrpt->lock); + /* remove from queue */ + remque((struct qelem *) l); + ast_mutex_unlock(&myrpt->lock); + strncpy(tmp,val,sizeof(tmp) - 1); + s = tmp; + s1 = strsep(&s,","); + s2 = strsep(&s,","); + snprintf(deststr, sizeof(deststr), "IAX2/%s", s1); + tele = strchr(deststr, '/'); + if (!tele) { + fprintf(stderr,"attempt_reconnect:Dial number (%s) must be in format tech/number\n",deststr); + return -1; + } + *tele++ = 0; + l->elaptime = 0; + l->chan = ast_request(deststr, AST_FORMAT_SLINEAR, tele,NULL); + if (l->chan){ + ast_set_read_format(l->chan, AST_FORMAT_SLINEAR); + ast_set_write_format(l->chan, AST_FORMAT_SLINEAR); + l->chan->whentohangup = 0; + l->chan->appl = "Apprpt"; + l->chan->data = "(Remote Rx)"; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "rpt (attempt_reconnect) initiating call to %s/%s on %s\n", + deststr, tele, l->chan->name); + if(l->chan->cid.cid_num) + free(l->chan->cid.cid_num); + l->chan->cid.cid_num = strdup(myrpt->name); + ast_call(l->chan,tele,999); + + } + else + { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Unable to place call to %s/%s on %s\n", + deststr,tele,l->chan->name); + return -1; + } + ast_mutex_lock(&myrpt->lock); + /* put back in queue queue */ + insque((struct qelem *)l,(struct qelem *)myrpt->links.next); + ast_mutex_unlock(&myrpt->lock); + ast_log(LOG_NOTICE,"Reconnect Attempt to %s in process\n",l->name); + return 0; +} + +/* single thread with one file (request) to dial */ +static void *rpt(void *this) +{ +struct rpt *myrpt = (struct rpt *)this; +char *tele,*idtalkover; +int ms = MSWAIT,lasttx=0,val,remrx=0,identqueued,nonidentqueued,res; +struct ast_channel *who; +ZT_CONFINFO ci; /* conference info */ +time_t dtmf_time,t; +struct rpt_link *l,*m; +struct rpt_tele *telem; +pthread_attr_t attr; +char tmpstr[300]; +char cmd[MAXDTMF+1] = ""; + + + ast_mutex_lock(&myrpt->lock); + strncpy(tmpstr,myrpt->rxchanname,sizeof(tmpstr) - 1); + tele = strchr(tmpstr,'/'); + if (!tele) + { + fprintf(stderr,"rpt:Dial number (%s) must be in format tech/number\n",myrpt->rxchanname); + ast_mutex_unlock(&myrpt->lock); + myrpt->rpt_thread = AST_PTHREADT_STOP; + pthread_exit(NULL); + } + *tele++ = 0; + myrpt->rxchannel = ast_request(tmpstr,AST_FORMAT_SLINEAR,tele,NULL); + if (myrpt->rxchannel) + { + if (myrpt->rxchannel->_state == AST_STATE_BUSY) + { + fprintf(stderr,"rpt:Sorry unable to obtain Rx channel\n"); + ast_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->rxchannel); + myrpt->rpt_thread = AST_PTHREADT_STOP; + pthread_exit(NULL); + } + ast_set_read_format(myrpt->rxchannel,AST_FORMAT_SLINEAR); + ast_set_write_format(myrpt->rxchannel,AST_FORMAT_SLINEAR); + myrpt->rxchannel->whentohangup = 0; + myrpt->rxchannel->appl = "Apprpt"; + myrpt->rxchannel->data = "(Repeater Rx)"; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "rpt (Rx) initiating call to %s/%s on %s\n", + tmpstr,tele,myrpt->rxchannel->name); + ast_call(myrpt->rxchannel,tele,999); + if (myrpt->rxchannel->_state != AST_STATE_UP) + { + ast_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->rxchannel); + myrpt->rpt_thread = AST_PTHREADT_STOP; + pthread_exit(NULL); + } + } + else + { + fprintf(stderr,"rpt:Sorry unable to obtain Rx channel\n"); + ast_mutex_unlock(&myrpt->lock); + myrpt->rpt_thread = AST_PTHREADT_STOP; + pthread_exit(NULL); + } + if (myrpt->txchanname) + { + strncpy(tmpstr,myrpt->txchanname,sizeof(tmpstr) - 1); + tele = strchr(tmpstr,'/'); + if (!tele) + { + fprintf(stderr,"rpt:Dial number (%s) must be in format tech/number\n",myrpt->txchanname); + ast_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->rxchannel); + myrpt->rpt_thread = AST_PTHREADT_STOP; + pthread_exit(NULL); + } + *tele++ = 0; + myrpt->txchannel = ast_request(tmpstr,AST_FORMAT_SLINEAR,tele,NULL); + if (myrpt->txchannel) + { + if (myrpt->txchannel->_state == AST_STATE_BUSY) + { + fprintf(stderr,"rpt:Sorry unable to obtain Tx channel\n"); + ast_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->txchannel); + ast_hangup(myrpt->rxchannel); + myrpt->rpt_thread = AST_PTHREADT_STOP; + pthread_exit(NULL); + } + ast_set_read_format(myrpt->txchannel,AST_FORMAT_SLINEAR); + ast_set_write_format(myrpt->txchannel,AST_FORMAT_SLINEAR); + myrpt->txchannel->whentohangup = 0; + myrpt->txchannel->appl = "Apprpt"; + myrpt->txchannel->data = "(Repeater Tx)"; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "rpt (Tx) initiating call to %s/%s on %s\n", + tmpstr,tele,myrpt->txchannel->name); + ast_call(myrpt->txchannel,tele,999); + if (myrpt->rxchannel->_state != AST_STATE_UP) + { + ast_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->rxchannel); + ast_hangup(myrpt->txchannel); + myrpt->rpt_thread = AST_PTHREADT_STOP; + pthread_exit(NULL); + } + } + else + { + fprintf(stderr,"rpt:Sorry unable to obtain Tx channel\n"); + ast_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->rxchannel); + myrpt->rpt_thread = AST_PTHREADT_STOP; + pthread_exit(NULL); + } + } + else + { + myrpt->txchannel = myrpt->rxchannel; + } + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_KEY); + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY); + /* allocate a pseudo-channel thru asterisk */ + myrpt->pchannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL); + if (!myrpt->pchannel) + { + fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); + ast_mutex_unlock(&myrpt->lock); + if (myrpt->txchannel != myrpt->rxchannel) + ast_hangup(myrpt->txchannel); + ast_hangup(myrpt->rxchannel); + myrpt->rpt_thread = AST_PTHREADT_STOP; + pthread_exit(NULL); + } + /* make a conference for the tx */ + ci.chan = 0; + ci.confno = -1; /* make a new conf */ + ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER; + /* first put the channel on the conference in proper mode */ + if (ioctl(myrpt->txchannel->fds[0],ZT_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + ast_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->pchannel); + if (myrpt->txchannel != myrpt->rxchannel) + ast_hangup(myrpt->txchannel); + ast_hangup(myrpt->rxchannel); + myrpt->rpt_thread = AST_PTHREADT_STOP; + pthread_exit(NULL); + } + /* save tx conference number */ + myrpt->txconf = ci.confno; + /* make a conference for the pseudo */ + ci.chan = 0; + ci.confno = -1; /* make a new conf */ + ci.confmode = ZT_CONF_CONFANNMON; + /* first put the channel on the conference in announce mode */ + if (ioctl(myrpt->pchannel->fds[0],ZT_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + ast_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->pchannel); + if (myrpt->txchannel != myrpt->rxchannel) + ast_hangup(myrpt->txchannel); + ast_hangup(myrpt->rxchannel); + myrpt->rpt_thread = AST_PTHREADT_STOP; + pthread_exit(NULL); + } + /* save pseudo channel conference number */ + myrpt->conf = ci.confno; + /* allocate a pseudo-channel thru asterisk */ + myrpt->txpchannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL); + if (!myrpt->txpchannel) + { + fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); + ast_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->pchannel); + if (myrpt->txchannel != myrpt->rxchannel) + ast_hangup(myrpt->txchannel); + ast_hangup(myrpt->rxchannel); + myrpt->rpt_thread = AST_PTHREADT_STOP; + pthread_exit(NULL); + } + /* make a conference for the tx */ + ci.chan = 0; + ci.confno = myrpt->txconf; + ci.confmode = ZT_CONF_CONF | ZT_CONF_TALKER ; + /* first put the channel on the conference in proper mode */ + if (ioctl(myrpt->txpchannel->fds[0],ZT_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + ast_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->txpchannel); + ast_hangup(myrpt->pchannel); + if (myrpt->txchannel != myrpt->rxchannel) + ast_hangup(myrpt->txchannel); + ast_hangup(myrpt->rxchannel); + myrpt->rpt_thread = AST_PTHREADT_STOP; + pthread_exit(NULL); + } + /* Now, the idea here is to copy from the physical rx channel buffer + into the pseudo tx buffer, and from the pseudo rx buffer into the + tx channel buffer */ + myrpt->links.next = &myrpt->links; + myrpt->links.prev = &myrpt->links; + myrpt->tailtimer = 0; + myrpt->totimer = 0; + myrpt->idtimer = myrpt->politeid; + myrpt->mustid = 0; + myrpt->callmode = 0; + myrpt->tounkeyed = 0; + myrpt->tonotify = 0; + myrpt->retxtimer = 0; + lasttx = 0; + myrpt->keyed = 0; + idtalkover = ast_variable_retrieve(cfg, myrpt->name, "idtalkover"); + myrpt->dtmfidx = -1; + myrpt->dtmfbuf[0] = 0; + myrpt->rem_dtmfidx = -1; + myrpt->rem_dtmfbuf[0] = 0; + dtmf_time = 0; + myrpt->rem_dtmf_time = 0; + myrpt->enable = 1; + myrpt->disgorgetime = 0; + ast_mutex_unlock(&myrpt->lock); + val = 0; + ast_channel_setoption(myrpt->rxchannel,AST_OPTION_TONE_VERIFY,&val,sizeof(char),0); + val = 1; + ast_channel_setoption(myrpt->rxchannel,AST_OPTION_RELAXDTMF,&val,sizeof(char),0); + while (ms >= 0) + { + struct ast_frame *f; + struct ast_channel *cs[300]; + int totx=0,elap=0,n,toexit=0; + + /* DEBUG Dump */ + if((myrpt->disgorgetime) && (time(NULL) >= myrpt->disgorgetime)){ + struct rpt_link *zl; + struct rpt_tele *zt; + + myrpt->disgorgetime = 0; + ast_log(LOG_NOTICE,"********** Variable Dump Start (app_rpt) **********\n"); + ast_log(LOG_NOTICE,"totx = %d\n",totx); + ast_log(LOG_NOTICE,"remrx = %d\n",remrx); + ast_log(LOG_NOTICE,"lasttx = %d\n",lasttx); + ast_log(LOG_NOTICE,"elap = %d\n",elap); + ast_log(LOG_NOTICE,"toexit = %d\n",toexit); + + ast_log(LOG_NOTICE,"myrpt->keyed = %d\n",myrpt->keyed); + ast_log(LOG_NOTICE,"myrpt->localtx = %d\n",myrpt->localtx); + ast_log(LOG_NOTICE,"myrpt->callmode = %d\n",myrpt->callmode); + ast_log(LOG_NOTICE,"myrpt->enable = %d\n",myrpt->enable); + ast_log(LOG_NOTICE,"myrpt->mustid = %d\n",myrpt->mustid); + ast_log(LOG_NOTICE,"myrpt->tounkeyed = %d\n",myrpt->tounkeyed); + ast_log(LOG_NOTICE,"myrpt->tonotify = %d\n",myrpt->tonotify); + ast_log(LOG_NOTICE,"myrpt->retxtimer = %ld\n",myrpt->retxtimer); + ast_log(LOG_NOTICE,"myrpt->totimer = %d\n",myrpt->totimer); + ast_log(LOG_NOTICE,"myrpt->tailtimer = %d\n",myrpt->tailtimer); + + zl = myrpt->links.next; + while(zl != &myrpt->links){ + ast_log(LOG_NOTICE,"*** Link Name: %s ***\n",zl->name); + ast_log(LOG_NOTICE," link->lasttx %d\n",zl->lasttx); + ast_log(LOG_NOTICE," link->lastrx %d\n",zl->lastrx); + ast_log(LOG_NOTICE," link->connected %d\n",zl->connected); + ast_log(LOG_NOTICE," link->hasconnected %d\n",zl->hasconnected); + ast_log(LOG_NOTICE," link->outbound %d\n",zl->outbound); + ast_log(LOG_NOTICE," link->disced %d\n",zl->disced); + ast_log(LOG_NOTICE," link->killme %d\n",zl->killme); + ast_log(LOG_NOTICE," link->disctime %ld\n",zl->disctime); + ast_log(LOG_NOTICE," link->retrytimer %ld\n",zl->retrytimer); + ast_log(LOG_NOTICE," link->retries = %d\n",zl->retries); + + zl = zl->next; + } + + zt = myrpt->tele.next; + if(zt != &myrpt->tele) + ast_log(LOG_NOTICE,"*** Telemetry Queue ***\n"); + while(zt != &myrpt->tele){ + ast_log(LOG_NOTICE," Telemetry mode: %d\n",zt->mode); + zt = zt->next; + } + ast_log(LOG_NOTICE,"******* Variable Dump End (app_rpt) *******\n"); + + } + + + + + + ast_mutex_lock(&myrpt->lock); + if (ast_check_hangup(myrpt->rxchannel)) break; + if (ast_check_hangup(myrpt->txchannel)) break; + if (ast_check_hangup(myrpt->pchannel)) break; + if (ast_check_hangup(myrpt->txpchannel)) break; + myrpt->localtx = myrpt->keyed && (myrpt->dtmfidx == -1) && (!myrpt->cmdnode[0]); + + /* If someone's connected, and they're transmitting from their end to us, set remrx true */ + + l = myrpt->links.next; + remrx = 0; + while(l != &myrpt->links) + { + if (l->lastrx) remrx = 1; + l = l->next; + } + + /* Create a "must_id" flag for the cleanup ID */ + + myrpt->mustid |= (myrpt->idtimer) && (myrpt->keyed || remrx) ; + + /* Build a fresh totx from myrpt->keyed and autopatch activated */ + + totx = myrpt->localtx || myrpt->callmode; + + /* Traverse the telemetry list to see if there's an ID queued and if there is not an ID queued */ + + identqueued = 0; + nonidentqueued = 0; + + telem = myrpt->tele.next; + while(telem != &myrpt->tele) + { + if((telem->mode == ID) || (telem->mode == IDTALKOVER)){ + identqueued = 1; + } + else + nonidentqueued = 1; + telem = telem->next; + } + + /* Add in any non-id telemetry */ + + totx = totx || nonidentqueued; + + /* Update external transmitter PTT state with everything but ID telemetry */ + + myrpt->exttx = totx; + + /* Add in ID telemetry to local transmitter */ + + totx = totx || remrx || identqueued; + + if (!totx) + { + myrpt->totimer = myrpt->totime; + myrpt->tounkeyed = 0; + myrpt->tonotify = 0; + } + else myrpt->tailtimer = myrpt->hangtime; + totx = totx && myrpt->totimer; + /* if timed-out and not said already, say it */ + if ((!myrpt->totimer) && (!myrpt->tonotify)) + { + myrpt->tonotify = 1; + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,TIMEOUT,NULL); + ast_mutex_lock(&myrpt->lock); + } + /* if wants to transmit and in phone call, but timed out, + reset time-out timer if keyed */ + if ((!totx) && (!myrpt->totimer) && (!myrpt->tounkeyed) && (!myrpt->keyed)) + { + myrpt->tounkeyed = 1; + } + if ((!totx) && (!myrpt->totimer) && myrpt->tounkeyed && myrpt->keyed) + { + myrpt->totimer = myrpt->totime; + myrpt->tounkeyed = 0; + myrpt->tonotify = 0; + ast_mutex_unlock(&myrpt->lock); + continue; + } + /* if timed-out and in circuit busy after call */ + if ((!totx) && (!myrpt->totimer) && (myrpt->callmode == 4)) + { + myrpt->callmode = 0; + } + /* get rid of tail if timed out */ + if (!myrpt->totimer) myrpt->tailtimer = 0; + /* if not timed-out, add in tail */ + if (myrpt->totimer) totx = totx || myrpt->tailtimer; + /* If user or links key up or are keyed up over standard ID, switch to talkover ID, if one is defined */ + if (identqueued && (myrpt->keyed || remrx) && idtalkover) { + int hasid = 0,hastalkover = 0; + + telem = myrpt->tele.next; + while(telem != &myrpt->tele){ + if(telem->mode == ID){ + if (telem->chan) ast_softhangup(telem->chan, AST_SOFTHANGUP_DEV); /* Whoosh! */ + hasid = 1; + } + if (telem->mode == IDTALKOVER) hastalkover = 1; + telem = telem->next; + } + ast_mutex_unlock(&myrpt->lock); + if (hasid && (!hastalkover)) rpt_telemetry(myrpt, IDTALKOVER, NULL); /* Start Talkover ID */ + ast_mutex_lock(&myrpt->lock); + } + /* Try to be polite */ + /* If the repeater has been inactive for longer than the ID time, do an initial ID in the tail*/ + /* If within 30 seconds of the time to ID, try do it in the tail */ + /* else if at ID time limit, do it right over the top of them */ + /* Lastly, if the repeater has been keyed, and the ID timer is expired, do a clean up ID */ + if (((totx && (!myrpt->exttx) && (myrpt->idtimer <= myrpt->politeid) && myrpt->tailtimer)) || + (myrpt->mustid && (!myrpt->idtimer))) + { + myrpt->mustid = 0; + myrpt->idtimer = myrpt->idtime; /* Reset our ID timer */ + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,ID,NULL); + ast_mutex_lock(&myrpt->lock); + } + /* let telemetry transmit anyway (regardless of timeout) */ + totx = totx || (myrpt->tele.next != &myrpt->tele); + if (totx && (!lasttx)) + { + lasttx = 1; + ast_mutex_unlock(&myrpt->lock); + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_KEY); + ast_mutex_lock(&myrpt->lock); + } + totx = totx && myrpt->enable; + if ((!totx) && lasttx) + { + lasttx = 0; + ast_mutex_unlock(&myrpt->lock); + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY); + ast_mutex_lock(&myrpt->lock); + } + time(&t); + /* if DTMF timeout */ + if ((!myrpt->cmdnode[0]) && (myrpt->dtmfidx >= 0) && ((dtmf_time + DTMF_TIMEOUT) < t)) + { + myrpt->dtmfidx = -1; + myrpt->dtmfbuf[0] = 0; + } + /* if remote DTMF timeout */ + if ((myrpt->rem_dtmfidx >= 0) && ((myrpt->rem_dtmf_time + DTMF_TIMEOUT) < t)) + { + myrpt->rem_dtmfidx = -1; + myrpt->rem_dtmfbuf[0] = 0; + } + + /* Reconnect kludge */ + l = myrpt->links.next; + while(l != &myrpt->links) + { + if (l->killme) + { + /* remove from queue */ + remque((struct qelem *) l); + if (!strcmp(myrpt->cmdnode,l->name)) + myrpt->cmdnode[0] = 0; + ast_mutex_unlock(&myrpt->lock); + /* hang-up on call to device */ + if (l->chan) ast_hangup(l->chan); + ast_hangup(l->pchan); + free(l); + ast_mutex_lock(&myrpt->lock); + /* re-start link traversal */ + l = myrpt->links.next; + continue; + } + l = l->next; + } + n = 0; + cs[n++] = myrpt->rxchannel; + cs[n++] = myrpt->pchannel; + cs[n++] = myrpt->txpchannel; + if (myrpt->txchannel != myrpt->rxchannel) cs[n++] = myrpt->txchannel; + l = myrpt->links.next; + while(l != &myrpt->links) + { + if ((!l->killme) && (!l->disctime) && l->chan) + { + cs[n++] = l->chan; + cs[n++] = l->pchan; + } + l = l->next; + } + ast_mutex_unlock(&myrpt->lock); + ms = MSWAIT; + who = ast_waitfor_n(cs,n,&ms); + if (who == NULL) ms = 0; + elap = MSWAIT - ms; + ast_mutex_lock(&myrpt->lock); + l = myrpt->links.next; + while(l != &myrpt->links) + { + if (!l->lasttx) + { + if ((l->retxtimer += elap) >= REDUNDANT_TX_TIME) + { + l->retxtimer = 0; + if (l->chan) ast_indicate(l->chan,AST_CONTROL_RADIO_UNKEY); + } + } else l->retxtimer = 0; +#ifdef RECONNECT_KLUDGE + if (l->disctime) /* Disconnect timer active on a channel ? */ + { + l->disctime -= elap; + if (l->disctime <= 0) /* Disconnect timer expired on inbound channel ? */ + l->disctime = 0; /* Yep */ + } + + if (l->retrytimer) + { + l->retrytimer -= elap; + if (l->retrytimer < 0) l->retrytimer = 0; + } +#endif + /* ignore non-timing channels */ + if (l->elaptime < 0) + { + l = l->next; + continue; + } + l->elaptime += elap; + /* if connection has taken too long */ + if ((l->elaptime > MAXCONNECTTIME) && + ((!l->chan) || (l->chan->_state != AST_STATE_UP))) + { + l->elaptime = 0; + ast_mutex_unlock(&myrpt->lock); + if (l->chan) ast_softhangup(l->chan,AST_SOFTHANGUP_DEV); +#ifndef RECONNECT_KLUDGE + rpt_telemetry(myrpt,CONNFAIL,l); +#endif + ast_mutex_lock(&myrpt->lock); + break; + } +#ifdef RECONNECT_KLUDGE + if ((!l->chan) && (!l->retrytimer) && l->outbound && + (l->retries++ < MAX_RETRIES) && (l->hasconnected)) + { + if (l->chan) ast_hangup(l->chan); + ast_mutex_unlock(&myrpt->lock); + if ((l->name[0] != '0') && (!l->isremote)) + { + l->retrytimer = MAX_RETRIES + 1; + } + else + { + if (attempt_reconnect(myrpt,l) == -1) + { + l->retrytimer = RETRY_TIMER_MS; + } + } + ast_mutex_lock(&myrpt->lock); + break; + } + if ((!l->chan) && (!l->retrytimer) && l->outbound && + (l->retries >= MAX_RETRIES)) + { + /* remove from queue */ + remque((struct qelem *) l); + if (!strcmp(myrpt->cmdnode,l->name)) + myrpt->cmdnode[0] = 0; + ast_mutex_unlock(&myrpt->lock); + if (l->name[0] != '0') + { + if (!l->hasconnected) + rpt_telemetry(myrpt,CONNFAIL,l); + else rpt_telemetry(myrpt,REMDISC,l); + } + /* hang-up on call to device */ + ast_hangup(l->pchan); + free(l); + ast_mutex_lock(&myrpt->lock); + break; + } + if ((!l->chan) && (!l->disctime) && (!l->outbound)) + { + /* remove from queue */ + remque((struct qelem *) l); + if (!strcmp(myrpt->cmdnode,l->name)) + myrpt->cmdnode[0] = 0; + ast_mutex_unlock(&myrpt->lock); + if (l->name[0] != '0') + { + rpt_telemetry(myrpt,REMDISC,l); + } + /* hang-up on call to device */ + ast_hangup(l->pchan); + free(l); + ast_mutex_lock(&myrpt->lock); + break; + } +#endif + l = l->next; + } + if (myrpt->tailtimer) myrpt->tailtimer -= elap; + if (myrpt->tailtimer < 0) myrpt->tailtimer = 0; + if (myrpt->totimer) myrpt->totimer -= elap; + if (myrpt->totimer < 0) myrpt->totimer = 0; + if (myrpt->idtimer) myrpt->idtimer -= elap; + if (myrpt->idtimer < 0) myrpt->idtimer = 0; + ast_mutex_unlock(&myrpt->lock); + if (!ms) continue; + if (who == myrpt->rxchannel) /* if it was a read from rx */ + { + f = ast_read(myrpt->rxchannel); + if (!f) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + break; + } + if (f->frametype == AST_FRAME_VOICE) + { + if (!myrpt->localtx) + memset(f->data,0,f->datalen); + ast_write(myrpt->pchannel,f); + } + else if (f->frametype == AST_FRAME_DTMF) + { + char c; + + c = (char) f->subclass; /* get DTMF char */ + ast_frfree(f); + if (!myrpt->keyed) continue; + if (c == myrpt->endchar) + { + /* if in simple mode, kill autopatch */ + if (myrpt->simple && myrpt->callmode) + { + ast_mutex_lock(&myrpt->lock); + myrpt->callmode = 0; + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,TERM,NULL); + continue; + } + ast_mutex_lock(&myrpt->lock); + myrpt->stopgen = 1; + if (myrpt->cmdnode[0]) + { + myrpt->cmdnode[0] = 0; + myrpt->dtmfidx = -1; + myrpt->dtmfbuf[0] = 0; + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,COMPLETE,NULL); + } else ast_mutex_unlock(&myrpt->lock); + continue; + } + ast_mutex_lock(&myrpt->lock); + if (myrpt->cmdnode[0]) + { + ast_mutex_unlock(&myrpt->lock); + send_link_dtmf(myrpt,c); + continue; + } + if (!myrpt->simple) + { + if (c == myrpt->funcchar) + { + myrpt->dtmfidx = 0; + myrpt->dtmfbuf[myrpt->dtmfidx] = 0; + ast_mutex_unlock(&myrpt->lock); + time(&dtmf_time); + continue; + } + else if ((c != myrpt->endchar) && (myrpt->dtmfidx >= 0)) + { + time(&dtmf_time); + + if (myrpt->dtmfidx < MAXDTMF) + { + myrpt->dtmfbuf[myrpt->dtmfidx++] = c; + myrpt->dtmfbuf[myrpt->dtmfidx] = 0; + + strncpy(cmd, myrpt->dtmfbuf, sizeof(cmd) - 1); + + ast_mutex_unlock(&myrpt->lock); + res = collect_function_digits(myrpt, cmd, SOURCE_RPT, NULL); + ast_mutex_lock(&myrpt->lock); + + switch(res){ + + case DC_INDETERMINATE: + break; + + case DC_REQ_FLUSH: + myrpt->dtmfidx = 0; + myrpt->dtmfbuf[0] = 0; + break; + + + case DC_COMPLETE: + myrpt->dtmfbuf[0] = 0; + myrpt->dtmfidx = -1; + dtmf_time = 0; + break; + + case DC_ERROR: + default: + myrpt->dtmfbuf[0] = 0; + myrpt->dtmfidx = -1; + dtmf_time = 0; + break; + } + if(res != DC_INDETERMINATE) { + ast_mutex_unlock(&myrpt->lock); + continue; + } + } + } + } + else /* if simple */ + { + if ((!myrpt->callmode) && (c == myrpt->funcchar)) + { + myrpt->callmode = 1; + myrpt->cidx = 0; + myrpt->exten[myrpt->cidx] = 0; + ast_mutex_unlock(&myrpt->lock); + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + ast_pthread_create(&myrpt->rpt_call_thread,&attr,rpt_call,(void *)myrpt); + continue; + } + } + if (myrpt->callmode == 1) + { + myrpt->exten[myrpt->cidx++] = c; + myrpt->exten[myrpt->cidx] = 0; + /* if this exists */ + if (ast_exists_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL)) + { + myrpt->callmode = 2; + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,PROC,NULL); + continue; + } + /* if can continue, do so */ + if (!ast_canmatch_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL)) + { + /* call has failed, inform user */ + myrpt->callmode = 4; + } + ast_mutex_unlock(&myrpt->lock); + continue; + } + if ((myrpt->callmode == 2) || (myrpt->callmode == 3)) + { + myrpt->mydtmf = c; + } + ast_mutex_unlock(&myrpt->lock); + continue; + } + else if (f->frametype == AST_FRAME_CONTROL) + { + if (f->subclass == AST_CONTROL_HANGUP) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + ast_frfree(f); + break; + } + /* if RX key */ + if (f->subclass == AST_CONTROL_RADIO_KEY) + { + if (debug) printf("@@@@ rx key\n"); + myrpt->keyed = 1; + } + /* if RX un-key */ + if (f->subclass == AST_CONTROL_RADIO_UNKEY) + { + if (debug) printf("@@@@ rx un-key\n"); + if(myrpt->keyed) { + rpt_telemetry(myrpt,UNKEY,NULL); + } + myrpt->keyed = 0; + } + } + ast_frfree(f); + continue; + } + if (who == myrpt->pchannel) /* if it was a read from pseudo */ + { + f = ast_read(myrpt->pchannel); + if (!f) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + break; + } + if (f->frametype == AST_FRAME_VOICE) + { + ast_write(myrpt->txpchannel,f); + } + if (f->frametype == AST_FRAME_CONTROL) + { + if (f->subclass == AST_CONTROL_HANGUP) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + ast_frfree(f); + break; + } + } + ast_frfree(f); + continue; + } + if (who == myrpt->txchannel) /* if it was a read from tx */ + { + f = ast_read(myrpt->txchannel); + if (!f) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + break; + } + if (f->frametype == AST_FRAME_CONTROL) + { + if (f->subclass == AST_CONTROL_HANGUP) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + ast_frfree(f); + break; + } + } + ast_frfree(f); + continue; + } + toexit = 0; + ast_mutex_lock(&myrpt->lock); + l = myrpt->links.next; + while(l != &myrpt->links) + { + if (l->disctime) + { + l = l->next; + continue; + } + if (who == l->chan) /* if it was a read from rx */ + { + remrx = 0; + /* see if any other links are receiving */ + m = myrpt->links.next; + while(m != &myrpt->links) + { + /* if not us, count it */ + if ((m != l) && (m->lastrx)) remrx = 1; + m = m->next; + } + ast_mutex_unlock(&myrpt->lock); + totx = (((l->isremote) ? myrpt->localtx : + myrpt->exttx) || remrx) && l->mode; + if (l->chan && (l->lasttx != totx)) + { + if (totx) + { + ast_indicate(l->chan,AST_CONTROL_RADIO_KEY); + } + else + { + ast_indicate(l->chan,AST_CONTROL_RADIO_UNKEY); + } + } + l->lasttx = totx; + f = ast_read(l->chan); + if (!f) + { +#ifdef RECONNECT_KLUDGE + if ((!l->disced) && (!l->outbound)) + { + if ((l->name[0] == '0') || l->isremote) + l->disctime = 1; + else + l->disctime = DISC_TIME; + ast_mutex_lock(&myrpt->lock); + ast_hangup(l->chan); + l->chan = 0; + break; + } + + if (l->retrytimer) + { + ast_mutex_lock(&myrpt->lock); + break; + } + if (l->outbound && (l->retries++ < MAX_RETRIES) && (l->hasconnected)) + { + ast_mutex_lock(&myrpt->lock); + ast_hangup(l->chan); + l->chan = 0; + ast_mutex_unlock(&myrpt->lock); + if (attempt_reconnect(myrpt,l) == -1) + { + l->retrytimer = RETRY_TIMER_MS; + } + ast_mutex_lock(&myrpt->lock); + break; + } +#endif + ast_mutex_lock(&myrpt->lock); + /* remove from queue */ + remque((struct qelem *) l); + if (!strcmp(myrpt->cmdnode,l->name)) + myrpt->cmdnode[0] = 0; + ast_mutex_unlock(&myrpt->lock); + if (!l->hasconnected) + rpt_telemetry(myrpt,CONNFAIL,l); + else if (l->disced != 2) rpt_telemetry(myrpt,REMDISC,l); + /* hang-up on call to device */ + ast_hangup(l->chan); + ast_hangup(l->pchan); + free(l); + ast_mutex_lock(&myrpt->lock); + break; + } + if (f->frametype == AST_FRAME_VOICE) + { + if (l->phonemode && (!l->lastrx)) + { + memset(f->data,0,f->datalen); + } + ast_write(l->pchan,f); + } + if (f->frametype == AST_FRAME_TEXT) + { + handle_link_data(myrpt,l,f->data); + } + if (f->frametype == AST_FRAME_DTMF) + { + handle_link_phone_dtmf(myrpt,l,f->subclass); + } + if (f->frametype == AST_FRAME_CONTROL) + { + if (f->subclass == AST_CONTROL_ANSWER) + { + char lconnected = l->connected; + l->connected = 1; + l->hasconnected = 1; + l->elaptime = -1; + l->retries = 0; + if (!lconnected) rpt_telemetry(myrpt,CONNECTED,l); + } + /* if RX key */ + if (f->subclass == AST_CONTROL_RADIO_KEY) + { + if (debug) printf("@@@@ rx key\n"); + l->lastrx = 1; + } + /* if RX un-key */ + if (f->subclass == AST_CONTROL_RADIO_UNKEY) + { + if (debug) printf("@@@@ rx un-key\n"); + l->lastrx = 0; + } + if (f->subclass == AST_CONTROL_HANGUP) + { + ast_frfree(f); +#ifdef RECONNECT_KLUDGE + if ((!l->outbound) && (!l->disced)) + { + if ((l->name[0] == '0') || l->isremote) + l->disctime = 1; + else + l->disctime = DISC_TIME; + ast_mutex_lock(&myrpt->lock); + ast_hangup(l->chan); + l->chan = 0; + break; + } + if (l->retrytimer) + { + ast_mutex_lock(&myrpt->lock); + break; + } + if (l->outbound && (l->retries++ < MAX_RETRIES) && (l->hasconnected)) + { + ast_mutex_lock(&myrpt->lock); + ast_hangup(l->chan); + l->chan = 0; + ast_mutex_unlock(&myrpt->lock); + if (attempt_reconnect(myrpt,l) == -1) + { + l->retrytimer = RETRY_TIMER_MS; + } + ast_mutex_lock(&myrpt->lock); + break; + } +#endif + ast_mutex_lock(&myrpt->lock); + /* remove from queue */ + remque((struct qelem *) l); + if (!strcmp(myrpt->cmdnode,l->name)) + myrpt->cmdnode[0] = 0; + ast_mutex_unlock(&myrpt->lock); + if (!l->hasconnected) + rpt_telemetry(myrpt,CONNFAIL,l); + else if (l->disced != 2) rpt_telemetry(myrpt,REMDISC,l); + /* hang-up on call to device */ + ast_hangup(l->chan); + ast_hangup(l->pchan); + free(l); + ast_mutex_lock(&myrpt->lock); + break; + } + } + ast_frfree(f); + ast_mutex_lock(&myrpt->lock); + break; + } + if (who == l->pchan) + { + ast_mutex_unlock(&myrpt->lock); + f = ast_read(l->pchan); + if (!f) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + toexit = 1; + ast_mutex_lock(&myrpt->lock); + break; + } + if (f->frametype == AST_FRAME_VOICE) + { + if (l->chan) ast_write(l->chan,f); + } + if (f->frametype == AST_FRAME_CONTROL) + { + if (f->subclass == AST_CONTROL_HANGUP) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + ast_frfree(f); + toexit = 1; + ast_mutex_lock(&myrpt->lock); + break; + } + } + ast_frfree(f); + ast_mutex_lock(&myrpt->lock); + break; + } + l = l->next; + } + ast_mutex_unlock(&myrpt->lock); + if (toexit) break; + if (who == myrpt->txpchannel) /* if it was a read from remote tx */ + { + f = ast_read(myrpt->txpchannel); + if (!f) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + break; + } + if (f->frametype == AST_FRAME_CONTROL) + { + if (f->subclass == AST_CONTROL_HANGUP) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + ast_frfree(f); + break; + } + } + ast_frfree(f); + continue; + } + } + usleep(100000); + ast_hangup(myrpt->pchannel); + ast_hangup(myrpt->txpchannel); + if (myrpt->txchannel != myrpt->rxchannel) ast_hangup(myrpt->txchannel); + ast_hangup(myrpt->rxchannel); + ast_mutex_lock(&myrpt->lock); + l = myrpt->links.next; + while(l != &myrpt->links) + { + struct rpt_link *ll = l; + /* remove from queue */ + remque((struct qelem *) l); + /* hang-up on call to device */ + if (l->chan) ast_hangup(l->chan); + ast_hangup(l->pchan); + l = l->next; + free(ll); + } + ast_mutex_unlock(&myrpt->lock); + if (debug) printf("@@@@ rpt:Hung up channel\n"); + myrpt->rpt_thread = AST_PTHREADT_STOP; + pthread_exit(NULL); + return NULL; +} + + +static void *rpt_master(void *ignore) +{ +char *this,*val; +struct ast_variable *vp; +int i,j,n,longestnode; +pthread_attr_t attr; + + /* start with blank config */ + memset(&rpt_vars,0,sizeof(rpt_vars)); + + cfg = ast_config_load("rpt.conf"); + if (!cfg) { + ast_log(LOG_NOTICE, "Unable to open radio repeater configuration rpt.conf. Radio Repeater disabled.\n"); + pthread_exit(NULL); + } + + /* go thru all the specified repeaters */ + this = NULL; + n = 0; + while((this = ast_category_browse(cfg,this)) != NULL) + { + + for(i = 0 ; i < strlen(this) ; i++){ + if((this[i] < '0') || (this[i] > '9')) + break; + } + if(i != strlen(this)) + continue; /* Not a node defn */ + + ast_log(LOG_DEBUG,"Loading config for repeater %s\n",this); + ast_mutex_init(&rpt_vars[n].lock); + rpt_vars[n].tele.next = &rpt_vars[n].tele; + rpt_vars[n].tele.prev = &rpt_vars[n].tele; + rpt_vars[n].rpt_thread = AST_PTHREADT_NULL; + rpt_vars[n].name = this; + rpt_vars[n].rxchanname = ast_variable_retrieve(cfg,this,"rxchannel"); + rpt_vars[n].txchanname = ast_variable_retrieve(cfg,this,"txchannel"); + rpt_vars[n].ourcontext = ast_variable_retrieve(cfg,this,"context"); + if (!rpt_vars[n].ourcontext) rpt_vars[n].ourcontext = this; + rpt_vars[n].ourcallerid = ast_variable_retrieve(cfg,this,"callerid"); + rpt_vars[n].acctcode = ast_variable_retrieve(cfg,this,"accountcode"); + rpt_vars[n].ident = ast_variable_retrieve(cfg,this,"idrecording"); + val = ast_variable_retrieve(cfg,this,"hangtime"); + if (val) rpt_vars[n].hangtime = atoi(val); + else rpt_vars[n].hangtime = HANGTIME; + val = ast_variable_retrieve(cfg,this,"totime"); + if (val) rpt_vars[n].totime = atoi(val); + else rpt_vars[n].totime = TOTIME; + + rpt_vars[n].idtime = retrieve_astcfgint( this, "idtime", 60000, 2400000, IDTIME); /* Enforce a min max */ + rpt_vars[n].politeid = retrieve_astcfgint( this, "politeid", 30000, 300000, POLITEID); /* Enforce a min max */ + rpt_vars[n].remote = ast_variable_retrieve(cfg,this,"remote"); + rpt_vars[n].tonezone = ast_variable_retrieve(cfg,this,"tonezone"); + val = ast_variable_retrieve(cfg,this,"iobase"); + /* do not use atoi() here, we need to be able to have + the input specified in hex or decimal so we use + sscanf with a %i */ + if ((!val) || (sscanf(val,"%i",&rpt_vars[n].iobase) != 1)) + rpt_vars[n].iobase = DEFAULT_IOBASE; + rpt_vars[n].simple = 0; + rpt_vars[n].functions = ast_variable_retrieve(cfg,this,"functions"); + if (!rpt_vars[n].functions) + { + rpt_vars[n].functions = FUNCTIONS; + rpt_vars[n].simple = 1; + } + rpt_vars[n].link_functions = ast_variable_retrieve(cfg,this,"link_functions"); + if (!rpt_vars[n].link_functions) + rpt_vars[n].link_functions = rpt_vars[n].functions; + rpt_vars[n].phone_functions = ast_variable_retrieve(cfg,this,"phone_functions"); + rpt_vars[n].dphone_functions = ast_variable_retrieve(cfg,this,"dphone_functions"); + val = ast_variable_retrieve(cfg,this,"funcchar"); + if (!val) rpt_vars[n].funcchar = FUNCCHAR; else + rpt_vars[n].funcchar = *val; + val = ast_variable_retrieve(cfg,this,"endchar"); + if (!val) rpt_vars[n].endchar = ENDCHAR; else + rpt_vars[n].endchar = *val; + val = ast_variable_retrieve(cfg,this,"nobusyout"); + if (val) rpt_vars[n].nobusyout = ast_true(val); + rpt_vars[n].nodes = ast_variable_retrieve(cfg,this,"nodes"); + if (!rpt_vars[n].nodes) + rpt_vars[n].nodes = NODES; + n++; + } + nrpts = n; + ast_log(LOG_DEBUG, "Total of %d repeaters configured.\n",n); + /* start em all */ + for(i = 0; i < n; i++) + { + + /* + * Go through the node list to determine the longest node + */ + longestnode = 0; + + vp = ast_variable_browse(cfg, rpt_vars[i].nodes); + + while(vp){ + j = strlen(vp->name); + if (j > longestnode) + longestnode = j; + vp = vp->next; + } + + + rpt_vars[i].longestnode = longestnode; + + /* + * For this repeater, Determine the length of the longest function + */ + rpt_vars[i].longestfunc = 0; + vp = ast_variable_browse(cfg, rpt_vars[i].functions); + while(vp){ + j = strlen(vp->name); + if (j > rpt_vars[i].longestfunc) + rpt_vars[i].longestfunc = j; + vp = vp->next; + } + /* + * For this repeater, Determine the length of the longest function + */ + rpt_vars[i].link_longestfunc = 0; + vp = ast_variable_browse(cfg, rpt_vars[i].link_functions); + while(vp){ + j = strlen(vp->name); + if (j > rpt_vars[i].link_longestfunc) + rpt_vars[i].link_longestfunc = j; + vp = vp->next; + } + rpt_vars[i].phone_longestfunc = 0; + if (rpt_vars[i].phone_functions) + { + vp = ast_variable_browse(cfg, rpt_vars[i].phone_functions); + while(vp){ + j = strlen(vp->name); + if (j > rpt_vars[i].phone_longestfunc) + rpt_vars[i].phone_longestfunc = j; + vp = vp->next; + } + } + rpt_vars[i].dphone_longestfunc = 0; + if (rpt_vars[i].dphone_functions) + { + vp = ast_variable_browse(cfg, rpt_vars[i].dphone_functions); + while(vp){ + j = strlen(vp->name); + if (j > rpt_vars[i].dphone_longestfunc) + rpt_vars[i].dphone_longestfunc = j; + vp = vp->next; + } + } + if (!rpt_vars[i].rxchanname) + { + ast_log(LOG_WARNING,"Did not specify rxchanname for node %s\n",rpt_vars[i].name); + ast_config_destroy(cfg); + pthread_exit(NULL); + } + /* if is a remote, dont start one for it */ + if (rpt_vars[i].remote) + { + strncpy(rpt_vars[i].freq, "146.580", sizeof(rpt_vars[i].freq) - 1); + strncpy(rpt_vars[i].rxpl, "100.0", sizeof(rpt_vars[i].rxpl) - 1); + + strncpy(rpt_vars[i].txpl, "100.0", sizeof(rpt_vars[i].txpl) - 1); + rpt_vars[i].remmode = REM_MODE_FM; + rpt_vars[i].offset = REM_SIMPLEX; + rpt_vars[i].powerlevel = REM_MEDPWR; + continue; + } + if (!rpt_vars[i].ident) + { + ast_log(LOG_WARNING,"Did not specify ident for node %s\n",rpt_vars[i].name); + ast_config_destroy(cfg); + pthread_exit(NULL); + } + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + ast_pthread_create(&rpt_vars[i].rpt_thread,&attr,rpt,(void *) &rpt_vars[i]); + } + usleep(500000); + for(;;) + { + /* Now monitor each thread, and restart it if necessary */ + for(i = 0; i < n; i++) + { + int rv; + if (rpt_vars[i].remote) continue; + if (rpt_vars[i].rpt_thread == AST_PTHREADT_STOP) + rv = -1; + else + rv = pthread_kill(rpt_vars[i].rpt_thread,0); + if (rv) + { + if(time(NULL) - rpt_vars[i].lastthreadrestarttime <= 15) + { + if(rpt_vars[i].threadrestarts >= 5) + { + ast_log(LOG_ERROR,"Continual RPT thread restarts, killing Asterisk\n"); + exit(1); /* Stuck in a restart loop, kill Asterisk and start over */ + } + else + { + ast_log(LOG_NOTICE,"RPT thread restarted on %s\n",rpt_vars[i].name); + rpt_vars[i].threadrestarts++; + } + } + else + rpt_vars[i].threadrestarts = 0; + + rpt_vars[i].lastthreadrestarttime = time(NULL); + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + ast_pthread_create(&rpt_vars[i].rpt_thread,&attr,rpt,(void *) &rpt_vars[i]); + ast_log(LOG_WARNING, "rpt_thread restarted on node %s\n", rpt_vars[i].name); + } + + } + usleep(2000000); + } + ast_config_destroy(cfg); + pthread_exit(NULL); +} + +static int rpt_exec(struct ast_channel *chan, void *data) +{ + int res=-1,i,rem_totx,n,phone_mode = 0; + struct localuser *u; + char tmp[256], keyed = 0; + char *options,*stringp,*tele; + struct rpt *myrpt; + struct ast_frame *f; + struct ast_channel *who; + struct ast_channel *cs[20]; + struct rpt_link *l; + ZT_CONFINFO ci; /* conference info */ + ZT_PARAMS par; + int ms,elap; + + if (ast_strlen_zero(data)) { + ast_log(LOG_WARNING, "Rpt requires an argument (system node)\n"); + return -1; + } + strncpy(tmp, (char *)data, sizeof(tmp)-1); + stringp=tmp; + strsep(&stringp, "|"); + options = stringp; + myrpt = NULL; + /* see if we can find our specified one */ + for(i = 0; i < nrpts; i++) + { + /* if name matches, assign it and exit loop */ + if (!strcmp(tmp,rpt_vars[i].name)) + { + myrpt = &rpt_vars[i]; + break; + } + } + if (myrpt == NULL) + { + ast_log(LOG_WARNING, "Cannot find specified system node %s\n",tmp); + return -1; + } + + /* if not phone access, must be an IAX connection */ + if (options && ((*options == 'P') || (*options == 'D') || (*options == 'R'))) + { + phone_mode = 1; + if (*options == 'D') phone_mode = 2; + ast_set_callerid(chan,"0","app_rpt user","0"); + } + else + { + if (strncmp(chan->name,"IAX2",4)) + { + ast_log(LOG_WARNING, "We only accept links via IAX2!!\n"); + return -1; + } + } + if (options && (*options == 'R')) + { + + /* Parts of this section taken from app_parkandannounce */ + char *return_context; + int l, m, lot, timeout = 0; + char tmp[256],*template; + char *working, *context, *exten, *priority; + char *s,*orig_s; + + + ast_mutex_lock(&myrpt->lock); + m = myrpt->callmode; + ast_mutex_unlock(&myrpt->lock); + + if ((!myrpt->nobusyout) && m) + { + if (chan->_state != AST_STATE_UP) + { + ast_indicate(chan,AST_CONTROL_BUSY); + } + while(ast_safe_sleep(chan,10000) != -1); + return -1; + } + + if (chan->_state != AST_STATE_UP) + { + ast_answer(chan); + } + + l=strlen(options)+2; + orig_s=malloc(l); + if(!orig_s) { + ast_log(LOG_WARNING, "Out of memory\n"); + return -1; + } + s=orig_s; + strncpy(s,options,l); + + template=strsep(&s,"|"); + if(!template) { + ast_log(LOG_WARNING, "An announce template must be defined\n"); + free(orig_s); + return -1; + } + + if(s) { + timeout = atoi(strsep(&s, "|")); + timeout *= 1000; + } + + return_context = s; + + if(return_context != NULL) { + /* set the return context. Code borrowed from the Goto builtin */ + + working = return_context; + context = strsep(&working, "|"); + exten = strsep(&working, "|"); + if(!exten) { + /* Only a priority in this one */ + priority = context; + exten = NULL; + context = NULL; + } else { + priority = strsep(&working, "|"); + if(!priority) { + /* Only an extension and priority in this one */ + priority = exten; + exten = context; + context = NULL; + } + } + if(atoi(priority) < 0) { + ast_log(LOG_WARNING, "Priority '%s' must be a number > 0\n", priority); + free(orig_s); + return -1; + } + /* At this point we have a priority and maybe an extension and a context */ + chan->priority = atoi(priority); + if(exten && strcasecmp(exten, "BYEXTENSION")) + strncpy(chan->exten, exten, sizeof(chan->exten)-1); + if(context) + strncpy(chan->context, context, sizeof(chan->context)-1); + } else { /* increment the priority by default*/ + chan->priority++; + } + + if(option_verbose > 2) { + ast_verbose( VERBOSE_PREFIX_3 "Return Context: (%s,%s,%d) ID: %s\n", chan->context,chan->exten, chan->priority, chan->cid.cid_num); + if(!ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) { + ast_verbose( VERBOSE_PREFIX_3 "Warning: Return Context Invalid, call will return to default|s\n"); + } + } + + /* we are using masq_park here to protect * from touching the channel once we park it. If the channel comes out of timeout + before we are done announcing and the channel is messed with, Kablooeee. So we use Masq to prevent this. */ + + ast_masq_park_call(chan, NULL, timeout, &lot); + + if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "Call Parking Called, lot: %d, timeout: %d, context: %s\n", lot, timeout, return_context); + + snprintf(tmp,sizeof(tmp) - 1,"%d,%s",lot,template + 1); + + rpt_telemetry(myrpt,REV_PATCH,tmp); + + free(orig_s); + + return 0; + + } + + if (!options) + { + struct ast_hostent ahp; + struct hostent *hp; + struct in_addr ia; + char hisip[100],nodeip[100],*val, *s, *s1, *s2, *b,*b1; + + /* look at callerid to see what node this comes from */ + if (!chan->cid.cid_num) /* if doesn't have caller id */ + { + ast_log(LOG_WARNING, "Doesnt have callerid on %s\n",tmp); + return -1; + } + + /* get his IP from IAX2 module */ + memset(hisip,0,sizeof(hisip)); + pbx_substitute_variables_helper(chan,"${IAXPEER(CURRENTCHANNEL)}",hisip,sizeof(hisip) - 1); + if (!hisip[0]) + { + ast_log(LOG_WARNING, "Link IP address cannot be determined!!\n"); + return -1; + } + + ast_callerid_parse(chan->cid.cid_num,&b,&b1); + ast_shrink_phone_number(b1); + if (!strcmp(myrpt->name,b1)) + { + ast_log(LOG_WARNING, "Trying to link to self!!\n"); + return -1; + } + + if (*b1 < '1') + { + ast_log(LOG_WARNING, "Node %s Invalid for connection here!!\n",b1); + return -1; + } + + + /* look for his reported node string */ + val = ast_variable_retrieve(cfg, myrpt->nodes, b1); + if (!val) + { + ast_log(LOG_WARNING, "Reported node %s cannot be found!!\n",b1); + return -1; + } + strncpy(tmp,val,sizeof(tmp) - 1); + s = tmp; + s1 = strsep(&s,","); + s2 = strsep(&s,","); + if (!s2) + { + ast_log(LOG_WARNING, "Reported node %s not in correct format!!\n",b1); + return -1; + } + if (strcmp(s2,"NONE")) { + hp = ast_gethostbyname(s2, &ahp); + if (!hp) + { + ast_log(LOG_WARNING, "Reported node %s, name %s cannot be found!!\n",b1,s2); + return -1; + } + memcpy(&ia,hp->h_addr,sizeof(in_addr_t)); + ast_inet_ntoa(nodeip,sizeof(nodeip) - 1,ia); + if (strcmp(hisip,nodeip)) + { + char *s3 = strchr(s1,'@'); + if (s3) s1 = s3 + 1; + s3 = strchr(s1,'/'); + if (s3) *s3 = 0; + hp = ast_gethostbyname(s1, &ahp); + if (!hp) + { + ast_log(LOG_WARNING, "Reported node %s, name %s cannot be found!!\n",b1,s1); + return -1; + } + memcpy(&ia,hp->h_addr,sizeof(in_addr_t)); + ast_inet_ntoa(nodeip,sizeof(nodeip) - 1,ia); + if (strcmp(hisip,nodeip)) + { + ast_log(LOG_WARNING, "Node %s IP %s does not match link IP %s!!\n",b1,nodeip,hisip); + return -1; + } + } + } + } + + /* if is not a remote */ + if (!myrpt->remote) + { + + char *b,*b1; + + /* look at callerid to see what node this comes from */ + if (!chan->cid.cid_num) /* if doesn't have caller id */ + { + ast_log(LOG_WARNING, "Doesnt have callerid on %s\n",tmp); + return -1; + } + + ast_callerid_parse(chan->cid.cid_num,&b,&b1); + ast_shrink_phone_number(b1); + if (!strcmp(myrpt->name,b1)) + { + ast_log(LOG_WARNING, "Trying to link to self!!\n"); + return -1; + } + ast_mutex_lock(&myrpt->lock); + l = myrpt->links.next; + /* try to find this one in queue */ + while(l != &myrpt->links) + { + if (l->name[0] == '0') + { + l = l->next; + continue; + } + /* if found matching string */ + if (!strcmp(l->name,b1)) break; + l = l->next; + } + /* if found */ + if (l != &myrpt->links) + { + l->killme = 1; + l->retries = MAX_RETRIES + 1; + l->disced = 2; + ast_mutex_unlock(&myrpt->lock); + usleep(500000); + } else + ast_mutex_unlock(&myrpt->lock); + /* establish call in tranceive mode */ + l = malloc(sizeof(struct rpt_link)); + if (!l) + { + ast_log(LOG_WARNING, "Unable to malloc\n"); + pthread_exit(NULL); + } + /* zero the silly thing */ + memset((char *)l,0,sizeof(struct rpt_link)); + l->mode = 1; + strncpy(l->name,b1,MAXNODESTR - 1); + l->isremote = 0; + l->chan = chan; + l->connected = 1; + l->hasconnected = 1; + l->phonemode = phone_mode; + ast_set_read_format(l->chan,AST_FORMAT_SLINEAR); + ast_set_write_format(l->chan,AST_FORMAT_SLINEAR); + /* allocate a pseudo-channel thru asterisk */ + l->pchan = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL); + if (!l->pchan) + { + fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); + pthread_exit(NULL); + } + ast_set_read_format(l->pchan,AST_FORMAT_SLINEAR); + ast_set_write_format(l->pchan,AST_FORMAT_SLINEAR); + /* make a conference for the tx */ + ci.chan = 0; + ci.confno = myrpt->conf; + ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER; + /* first put the channel on the conference in proper mode */ + if (ioctl(l->pchan->fds[0],ZT_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + pthread_exit(NULL); + } + ast_mutex_lock(&myrpt->lock); + if (phone_mode > 1) l->lastrx = 1; + /* insert at end of queue */ + insque((struct qelem *)l,(struct qelem *)myrpt->links.next); + ast_mutex_unlock(&myrpt->lock); + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } + return AST_PBX_KEEPALIVE; + } + ast_mutex_lock(&myrpt->lock); + /* if remote, error if anyone else already linked */ + if (myrpt->remoteon) + { + ast_mutex_unlock(&myrpt->lock); + usleep(500000); + if (myrpt->remoteon) + { + ast_log(LOG_WARNING, "Trying to use busy link on %s\n",tmp); + return -1; + } + ast_mutex_lock(&myrpt->lock); + } + myrpt->remoteon = 1; + if (ioperm(myrpt->iobase,1,1) == -1) + { + ast_mutex_unlock(&myrpt->lock); + ast_log(LOG_WARNING, "Cant get io permission on IO port %x hex\n",myrpt->iobase); + return -1; + } + LOCAL_USER_ADD(u); + tele = strchr(myrpt->rxchanname,'/'); + if (!tele) + { + fprintf(stderr,"rpt:Dial number must be in format tech/number\n"); + ast_mutex_unlock(&myrpt->lock); + pthread_exit(NULL); + } + *tele++ = 0; + myrpt->rxchannel = ast_request(myrpt->rxchanname,AST_FORMAT_SLINEAR,tele,NULL); + if (myrpt->rxchannel) + { + ast_set_read_format(myrpt->rxchannel,AST_FORMAT_SLINEAR); + ast_set_write_format(myrpt->rxchannel,AST_FORMAT_SLINEAR); + myrpt->rxchannel->whentohangup = 0; + myrpt->rxchannel->appl = "Apprpt"; + myrpt->rxchannel->data = "(Link Rx)"; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "rpt (Rx) initiating call to %s/%s on %s\n", + myrpt->rxchanname,tele,myrpt->rxchannel->name); + ast_mutex_unlock(&myrpt->lock); + ast_call(myrpt->rxchannel,tele,999); + ast_mutex_lock(&myrpt->lock); + } + else + { + fprintf(stderr,"rpt:Sorry unable to obtain Rx channel\n"); + ast_mutex_unlock(&myrpt->lock); + pthread_exit(NULL); + } + *--tele = '/'; + if (myrpt->txchanname) + { + tele = strchr(myrpt->txchanname,'/'); + if (!tele) + { + fprintf(stderr,"rpt:Dial number must be in format tech/number\n"); + ast_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->rxchannel); + pthread_exit(NULL); + } + *tele++ = 0; + myrpt->txchannel = ast_request(myrpt->txchanname,AST_FORMAT_SLINEAR,tele,NULL); + if (myrpt->txchannel) + { + ast_set_read_format(myrpt->txchannel,AST_FORMAT_SLINEAR); + ast_set_write_format(myrpt->txchannel,AST_FORMAT_SLINEAR); + myrpt->txchannel->whentohangup = 0; + myrpt->txchannel->appl = "Apprpt"; + myrpt->txchannel->data = "(Link Tx)"; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "rpt (Tx) initiating call to %s/%s on %s\n", + myrpt->txchanname,tele,myrpt->txchannel->name); + ast_mutex_unlock(&myrpt->lock); + ast_call(myrpt->txchannel,tele,999); + ast_mutex_lock(&myrpt->lock); + } + else + { + fprintf(stderr,"rpt:Sorry unable to obtain Tx channel\n"); + ast_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->rxchannel); + pthread_exit(NULL); + } + *--tele = '/'; + } + else + { + myrpt->txchannel = myrpt->rxchannel; + } + myrpt->remoterx = 0; + myrpt->remotetx = 0; + myrpt->retxtimer = 0; + myrpt->remoteon = 1; + myrpt->dtmfidx = -1; + myrpt->dtmfbuf[0] = 0; + myrpt->dtmf_time_rem = 0; + myrpt->hfscanmode = 0; + myrpt->hfscanstatus = 0; + ast_mutex_unlock(&myrpt->lock); + setrem(myrpt); + ast_set_write_format(chan, AST_FORMAT_SLINEAR); + ast_set_read_format(chan, AST_FORMAT_SLINEAR); + /* if we are on 2w loop and are a remote, turn EC on */ + if (myrpt->remote && (myrpt->rxchannel == myrpt->txchannel)) + { + i = 128; + ioctl(myrpt->rxchannel->fds[0],ZT_ECHOCANCEL,&i); + } + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } + + if (ioctl(myrpt->txchannel->fds[0],ZT_GET_PARAMS,&par) != -1) + { + if (par.rxisoffhook) + { + ast_indicate(chan,AST_CONTROL_RADIO_KEY); + myrpt->remoterx = 1; + } + } + n = 0; + cs[n++] = chan; + cs[n++] = myrpt->rxchannel; + if (myrpt->rxchannel != myrpt->txchannel) + cs[n++] = myrpt->txchannel; + for(;;) + { + if (ast_check_hangup(chan)) break; + if (ast_check_hangup(myrpt->rxchannel)) break; + ms = MSWAIT; + who = ast_waitfor_n(cs,n,&ms); + if (who == NULL) ms = 0; + elap = MSWAIT - ms; + if (!ms) continue; + rem_totx = keyed; + + + if ((!myrpt->remoterx) && (!myrpt->remotetx)) + { + if ((myrpt->retxtimer += elap) >= REDUNDANT_TX_TIME) + { + myrpt->retxtimer = 0; + ast_indicate(chan,AST_CONTROL_RADIO_UNKEY); + } + } else myrpt->retxtimer = 0; + if (rem_totx && (!myrpt->remotetx)) /* Remote base radio TX key */ + { + myrpt->remotetx = 1; + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_KEY); + } + if ((!rem_totx) && myrpt->remotetx) /* Remote base radio TX unkey */ + { + myrpt->remotetx = 0; + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY); + } + + if(myrpt->tunerequest && (!strcmp(myrpt->remote, remote_rig_ft897))){ /* ft-897 specific for now... */ + myrpt->tunerequest = 0; + set_mode_ft897(myrpt, REM_MODE_AM); + simple_command_ft897(myrpt, 8); + myrpt->remotetx = 0; + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY); + if (!myrpt->remoterx) + ast_indicate(chan, AST_CONTROL_RADIO_KEY); + if(play_tone(chan, 800, 6000, 8192) == -1) + break; + + rmt_telem_finish(myrpt,chan); + set_mode_ft897(myrpt, 0x88); + setrem(myrpt); + } + + if (myrpt->hfscanmode){ + myrpt->scantimer -= elap; + if(myrpt->scantimer <= 0){ + myrpt->scantimer = REM_SCANTIME; + service_scan(myrpt); + } + } + + + if (who == chan) /* if it was a read from incomming */ + { + f = ast_read(chan); + if (!f) + { + if (debug) printf("@@@@ link:Hung Up\n"); + break; + } + if (f->frametype == AST_FRAME_VOICE) + { + /* if not transmitting, zero-out audio */ + if (!myrpt->remotetx) + memset(f->data,0,f->datalen); + ast_write(myrpt->txchannel,f); + } + if (f->frametype == AST_FRAME_DTMF) + { + myrpt->remchannel = chan; /* Save copy of channel */ + if (handle_remote_phone_dtmf(myrpt,f->subclass,&keyed,phone_mode) == -1) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + ast_frfree(f); + break; + } + } + if (f->frametype == AST_FRAME_TEXT) + { + myrpt->remchannel = chan; /* Save copy of channel */ + if (handle_remote_data(myrpt,f->data) == -1) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + ast_frfree(f); + break; + } + } + if (f->frametype == AST_FRAME_CONTROL) + { + if (f->subclass == AST_CONTROL_HANGUP) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + ast_frfree(f); + break; + } + /* if RX key */ + if (f->subclass == AST_CONTROL_RADIO_KEY) + { + if (debug) printf("@@@@ rx key\n"); + keyed = 1; + } + /* if RX un-key */ + if (f->subclass == AST_CONTROL_RADIO_UNKEY) + { + if (debug) printf("@@@@ rx un-key\n"); + keyed = 0; + } + } + if (myrpt->hfscanstatus){ + myrpt->remchannel = chan; /* Save copy of channel */ + myrpt->remotetx = 0; + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY); + if (!myrpt->remoterx) + { + ast_indicate(myrpt->remchannel,AST_CONTROL_RADIO_KEY); + } + if(myrpt->hfscanstatus < 0) { + if (myrpt->hfscanstatus == -1) { + if (ast_safe_sleep(myrpt->remchannel,1000) == -1) break; + } + sayfile(myrpt->remchannel, "rpt/stop"); + } + else + { + saynum(myrpt->remchannel, myrpt->hfscanstatus ); + } + rmt_telem_finish(myrpt,myrpt->remchannel); + myrpt->hfscanstatus = 0; + } + ast_frfree(f); + continue; + } + if (who == myrpt->rxchannel) /* if it was a read from radio */ + { + f = ast_read(myrpt->rxchannel); + if (!f) + { + if (debug) printf("@@@@ link:Hung Up\n"); + break; + } + if (f->frametype == AST_FRAME_VOICE) + { + if ((myrpt->remote) && (myrpt->remotetx)) + memset(f->data,0,f->datalen); + ast_write(chan,f); + } + else if (f->frametype == AST_FRAME_CONTROL) + { + if (f->subclass == AST_CONTROL_HANGUP) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + ast_frfree(f); + break; + } + /* if RX key */ + if (f->subclass == AST_CONTROL_RADIO_KEY) + { + if (debug) printf("@@@@ remote rx key\n"); + if (!myrpt->remotetx) + { + ast_indicate(chan,AST_CONTROL_RADIO_KEY); + myrpt->remoterx = 1; + } + } + /* if RX un-key */ + if (f->subclass == AST_CONTROL_RADIO_UNKEY) + { + if (debug) printf("@@@@ remote rx un-key\n"); + if (!myrpt->remotetx) + { + ast_indicate(chan,AST_CONTROL_RADIO_UNKEY); + myrpt->remoterx = 0; + } + } + } + ast_frfree(f); + continue; + } + if ((myrpt->rxchannel != myrpt->txchannel) && + (who == myrpt->txchannel)) /* do this cuz you have to */ + { + f = ast_read(myrpt->txchannel); + if (!f) + { + if (debug) printf("@@@@ link:Hung Up\n"); + break; + } + if (f->frametype == AST_FRAME_CONTROL) + { + if (f->subclass == AST_CONTROL_HANGUP) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + ast_frfree(f); + break; + } + } + ast_frfree(f); + continue; + } + + } + ast_mutex_lock(&myrpt->lock); + if (myrpt->rxchannel != myrpt->txchannel) ast_hangup(myrpt->txchannel); + ast_hangup(myrpt->rxchannel); + myrpt->hfscanmode = 0; + myrpt->hfscanstatus = 0; + myrpt->remoteon = 0; + ast_mutex_unlock(&myrpt->lock); + closerem(myrpt); + LOCAL_USER_REMOVE(u); + return res; +} + +int unload_module(void) +{ + int i; + + STANDARD_HANGUP_LOCALUSERS; + for(i = 0; i < nrpts; i++) { + if (!strcmp(rpt_vars[i].name,rpt_vars[i].nodes)) continue; + ast_mutex_destroy(&rpt_vars[i].lock); + } + i = ast_unregister_application(app); + + /* Unregister cli extensions */ + ast_cli_unregister(&cli_debug); + + return i; +} + +int load_module(void) +{ + ast_pthread_create(&rpt_master_thread,NULL,rpt_master,NULL); + + /* Register cli extensions */ + ast_cli_register(&cli_debug); + + return ast_register_application(app, rpt_exec, synopsis, descrip); +} + +char *description(void) +{ + return tdesc; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} + |