diff options
Diffstat (limited to 'apps/app_rpt.c')
-rw-r--r-- | apps/app_rpt.c | 11930 |
1 files changed, 11930 insertions, 0 deletions
diff --git a/apps/app_rpt.c b/apps/app_rpt.c new file mode 100644 index 000000000..b7dc13fdc --- /dev/null +++ b/apps/app_rpt.c @@ -0,0 +1,11930 @@ +/* #define OLD_ASTERISK */ +#define OLDKEY +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2002-2007, 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. + */ +/*! \file + * + * \brief Radio Repeater / Remote Base program + * version 0.73 09/04/07 + * + * \author Jim Dixon, WB6NIL <jim@lambdatel.com> + * + * \note Serious contributions by Steve RoDgers, WA6ZFT <hwstar@rodgers.sdcoxmail.com> + * + * 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 (autopatchup, autopatchdn) + * autopatchup can optionally take comma delimited setting=value pairs: + * + * + * context=string : Override default context with "string" + * dialtime=ms : Specify the max number of milliseconds between phone number digits (1000 milliseconds = 1 second) + * farenddisconnect=1 : Automatically disconnect when called party hangs up + * noct=1 : Don't send repeater courtesy tone during autopatch calls + * quiet=1 : Don't send dial tone, or connect messages. Do not send patch down message when called party hangs up + * + * + * Example: 123=autopatchup,dialtime=20000,noct=1,farenddisconnect=1 + * + * 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/Off + * 5 - Dump System Variables on Console (debug) + * 6 - PTT (phone mode only) + * 7 - Time out timer enable + * 8 - Time out timer disable + * 9 - Autopatch enable + * 10 - Autopatch disable + * 11 - Link enable + * 12 - Link disable + * 13 - Query System State + * 14 - Change System State + * 15 - Scheduler Enable + * 16 - Scheduler Disable + * 17 - User functions (time, id, etc) enable + * 18 - User functions (time, id, etc) disable + * 19 - Select alternate hang timer + * 20 - Select standard hang timer + * + * 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 + * 11 - Disconnect a previously permanently connected link + * 12 - Permanently connect specified link -- monitor only + * 13 - Permanently connect specified link -- tranceive + * 15 - Full system status (all nodes) + * 16 - Reconnect links disconnected with "disconnect all links" + * 200 thru 215 - (Send DTMF 0-9,*,#,A-D) (200=0, 201=1, 210=*, etc) + * + * 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) + * 200 thru 215 - (Send DTMF 0-9,*,#,A-D) (200=0, 201=1, 210=*, etc) + * + * + * 'duplex' modes: (defaults to duplex=2) + * + * 0 - Only remote links key Tx and no main repeat audio. + * 1 - Everything other then main Rx keys Tx, no main repeat audio. + * 2 - Normal mode + * 3 - Normal except no main repeat audio. + * 4 - Normal except no main repeat audio during autopatch only + * +*/ + +/*** MODULEINFO + <depend>dahdi</depend> + <depend>tonezone</depend> + <defaultenabled>no</defaultenabled> + ***/ + +/* Un-comment the following to include support for MDC-1200 digital tone + signalling protocol (using KA6SQG's GPL'ed implementation) */ +/* #include "mdc_decode.c" */ + +/* Un-comment the following to include support for notch filters in the + rx audio stream (using Tony Fisher's mknotch (mkfilter) implementation) */ +/* #include "rpt_notch.c" */ + +/* maximum digits in DTMF buffer, and seconds after * for DTMF command timeout */ + +#define MAXDTMF 32 +#define MAXMACRO 2048 +#define MAXLINKLIST 512 +#define LINKLISTTIME 10000 +#define LINKLISTSHORTTIME 200 +#define MACROTIME 100 +#define MACROPTIME 500 +#define DTMF_TIMEOUT 3 +#define KENWOOD_RETRIES 5 + +#define AUTHTELLTIME 7000 +#define AUTHTXTIME 1000 +#define AUTHLOGOUTTIME 25000 + +#ifdef __RPT_NOTCH +#define MAXFILTERS 10 +#endif + +#define DISC_TIME 10000 /* report disc after 10 seconds of no connect */ +#define MAX_RETRIES 5 +#define MAX_RETRIES_PERM 1000000000 + +#define REDUNDANT_TX_TIME 2000 + +#define RETRY_TIMER_MS 5000 + +#define START_DELAY 2 + +#define MAXPEERSTR 31 +#define MAXREMSTR 15 + +#define DELIMCHR ',' +#define QUOTECHR 34 + +#define MONITOR_DISK_BLOCKS_PER_MINUTE 38 + +#define DEFAULT_MONITOR_MIN_DISK_BLOCKS 10000 +#define DEFAULT_REMOTE_INACT_TIMEOUT (15 * 60) +#define DEFAULT_REMOTE_TIMEOUT (60 * 60) +#define DEFAULT_REMOTE_TIMEOUT_WARNING (3 * 60) +#define DEFAULT_REMOTE_TIMEOUT_WARNING_FREQ 30 + +#define NODES "nodes" +#define EXTNODES "extnodes" +#define MEMORY "memory" +#define MACRO "macro" +#define FUNCTIONS "functions" +#define TELEMETRY "telemetry" +#define MORSE "morse" +#define FUNCCHAR '*' +#define ENDCHAR '#' +#define EXTNODEFILE "/var/lib/asterisk/rpt_extnodes" + +#define DEFAULT_IOBASE 0x378 + +#define DEFAULT_CIV_ADDR 0x58 + +#define MAXCONNECTTIME 5000 + +#define MAXNODESTR 300 + +#define MAXPATCHCONTEXT 100 + +#define ACTIONSIZE 32 + +#define TELEPARAMSIZE 256 + +#define REM_SCANTIME 100 + +#define DTMF_LOCAL_TIME 250 +#define DTMF_LOCAL_STARTTIME 500 + +#define IC706_PL_MEMORY_OFFSET 50 + +#define ALLOW_LOCAL_CHANNELS + +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, + TAILMSG, MACRO_NOTFOUND, MACRO_BUSY, LASTNODEKEY, FULLSTATUS, + MEMNOTFOUND, INVFREQ, REMMODE, REMLOGIN, REMXXX, REMSHORTSTATUS, + REMLONGSTATUS, LOGINREQ, SCAN, SCANSTAT, TUNE, SETREMOTE, + TIMEOUT_WARNING, ACT_TIMEOUT_WARNING, LINKUNKEY, UNAUTHTX}; + + +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_COMPLETEQUIET, DC_DOKEY}; + +enum {SOURCE_RPT, SOURCE_LNK, SOURCE_RMT, SOURCE_PHONE, SOURCE_DPHONE}; + +enum {DLY_TELEM, DLY_ID, DLY_UNKEY, DLY_CALLTERM, DLY_COMP, DLY_LINKUNKEY}; + +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> +#ifdef HAVE_SYS_IO_H +#include <sys/io.h> +#endif +#include <sys/vfs.h> +#include <math.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <termios.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" +#include "asterisk/cdr.h" +#include "asterisk/options.h" + +#include "asterisk/dahdi_compat.h" +#include "asterisk/tonezone_compat.h" + +/* Start a tone-list going */ +int ast_playtones_start(struct ast_channel *chan, int vol, const char* tonelist, int interruptible); +/*! Stop the tones from playing */ +void ast_playtones_stop(struct ast_channel *chan); + +static char *tdesc = "Radio Repeater / Remote Base version 0.73 09/04/2007"; + +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; + +static char remdtmfstr[] = "0123456789*#ABCD"; + +enum {TOP_TOP,TOP_WON,WON_BEFREAD,BEFREAD_AFTERREAD}; + +int max_chan_stat [] = {22000,1000,22000,100,22000,2000,22000}; + +#define NRPTSTAT 7 + +struct rpt_chan_stat +{ + struct timeval last; + long long total; + unsigned long count; + unsigned long largest; + struct timeval largest_time; +}; + +char *discstr = "!!DISCONNECT!!"; +static char *remote_rig_ft897="ft897"; +static char *remote_rig_rbi="rbi"; +static char *remote_rig_kenwood="kenwood"; +static char *remote_rig_ic706="ic706"; + +#ifdef OLD_ASTERISK +STANDARD_LOCAL_USER; +LOCAL_USER_DECL; +#endif + +#define MSWAIT 200 +#define HANGTIME 5000 +#define TOTIME 180000 +#define IDTIME 300000 +#define MAXRPTS 20 +#define MAX_STAT_LINKS 32 +#define POLITEID 30000 +#define FUNCTDELAY 1500 + +#define MAXXLAT 20 +#define MAXXLATTIME 3 + +#define MAX_SYSSTATES 10 + +struct rpt_xlat +{ +char funccharseq[MAXXLAT]; +char endcharseq[MAXXLAT]; +char passchars[MAXXLAT]; +int funcindex; +int endindex; +time_t lastone; +} ; + +static time_t starttime = 0; + +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 lastrx1; + char connected; + char hasconnected; + char perma; + char thisconnected; + char outbound; + char disced; + char killme; + long elaptime; + long disctime; + long retrytimer; + long retxtimer; + long rerxtimer; + int retries; + int max_retries; + int reconnects; + long long connecttime; + struct ast_channel *chan; + struct ast_channel *pchan; + char linklist[MAXLINKLIST]; + time_t linklistreceived; + long linklisttimer; + int dtmfed; + int linkunkeytocttimer; + struct ast_frame *lastf1,*lastf2; + struct rpt_chan_stat chan_stat[NRPTSTAT]; +} ; + +struct rpt_lstat +{ + struct rpt_lstat *next; + struct rpt_lstat *prev; + char peer[MAXPEERSTR]; + char name[MAXNODESTR]; + char mode; + char outbound; + char reconnects; + char thisconnected; + long long connecttime; + struct rpt_chan_stat chan_stat[NRPTSTAT]; +} ; + +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]; + int submode; + 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]; +} ; + + +struct sysstate +{ + char txdisable; + char totdisable; + char linkfundisable; + char autopatchdisable; + char schedulerdisable; + char userfundisable; + char alternatetail; +}; + +static struct rpt +{ + ast_mutex_t lock; + ast_mutex_t remlock; + struct ast_config *cfg; + char reload; + + char *name; + char *rxchanname; + char *txchanname; + char *remote; + struct rpt_chan_stat chan_stat[NRPTSTAT]; + unsigned int scram; + + struct { + char *ourcontext; + char *ourcallerid; + char *acctcode; + char *ident; + char *tonezone; + char simple; + char *functions; + char *link_functions; + char *phone_functions; + char *dphone_functions; + char *nodes; + char *extnodes; + char *extnodefile; + int hangtime; + int althangtime; + int totime; + int idtime; + int tailmessagetime; + int tailsquashedtime; + int duplex; + int politeid; + char *tailmessages[500]; + int tailmessagemax; + char *memory; + char *macro; + char *startupmacro; + int iobase; + char *ioport; + char funcchar; + char endchar; + char nobusyout; + char notelemtx; + char propagate_dtmf; + char propagate_phonedtmf; + char linktolink; + unsigned char civaddr; + struct rpt_xlat inxlat; + struct rpt_xlat outxlat; + char *archivedir; + int authlevel; + char *csstanzaname; + char *skedstanzaname; + char *txlimitsstanzaname; + long monminblocks; + int remoteinacttimeout; + int remotetimeout; + int remotetimeoutwarning; + int remotetimeoutwarningfreq; + int sysstate_cur; + struct sysstate s[MAX_SYSSTATES]; + } p; + struct rpt_link links; + int unkeytocttimer; + char keyed; + char exttx; + char localtx; + char remoterx; + char remotetx; + char remoteon; + char remtxfreqok; + char tounkeyed; + char tonotify; + char dtmfbuf[MAXDTMF]; + char macrobuf[MAXMACRO]; + char rem_dtmfbuf[MAXDTMF]; + char lastdtmfcommand[MAXDTMF]; + char cmdnode[50]; + struct ast_channel *rxchannel,*txchannel, *monchannel; + struct ast_channel *pchannel,*txpchannel, *zaprxchannel, *zaptxchannel; + struct ast_frame *lastf1,*lastf2; + struct rpt_tele tele; + struct timeval lasttv,curtv; + pthread_t rpt_call_thread,rpt_thread; + time_t dtmf_time,rem_dtmf_time,dtmf_time_rem; + int tailtimer,totimer,idtimer,txconf,conf,callmode,cidx,scantimer,tmsgtimer,skedtimer; + int mustid,tailid; + int tailevent; + int telemrefcount; + int dtmfidx,rem_dtmfidx; + int dailytxtime,dailykerchunks,totalkerchunks,dailykeyups,totalkeyups,timeouts; + int totalexecdcommands, dailyexecdcommands; + long retxtimer; + long rerxtimer; + long long totaltxtime; + char mydtmf; + 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 hfscanstop; + char lastlinknode[MAXNODESTR]; + char savednodes[MAXNODESTR]; + int stopgen; + char patchfarenddisconnect; + char patchnoct; + char patchquiet; + char patchcontext[MAXPATCHCONTEXT]; + int patchdialtime; + int macro_longest; + int phone_longestfunc; + int dphone_longestfunc; + int link_longestfunc; + int longestfunc; + int longestnode; + int threadrestarts; + int tailmessagen; + time_t disgorgetime; + time_t lastthreadrestarttime; + long macrotimer; + char lastnodewhichkeyedusup[MAXNODESTR]; + int dtmf_local_timer; + char dtmf_local_str[100]; + struct ast_filestream *monstream; + char loginuser[50]; + char loginlevel[10]; + long authtelltimer; + long authtimer; + int iofd; + time_t start_time,last_activity_time; +#ifdef __RPT_NOTCH + struct rptfilter + { + char desc[100]; + float x0; + float x1; + float x2; + float y0; + float y1; + float y2; + float gain; + float const0; + float const1; + float const2; + } filters[MAXFILTERS]; +#endif +#ifdef _MDC_DECODE_H_ + mdc_decoder_t *mdc; + unsigned short lastunit; +#endif +} rpt_vars[MAXRPTS]; + +struct nodelog { +struct nodelog *next; +struct nodelog *prev; +time_t timestamp; +char archivedir[MAXNODESTR]; +char str[MAXNODESTR * 2]; +} nodelog; + +static int service_scan(struct rpt *myrpt); +static int set_mode_ft897(struct rpt *myrpt, char newmode); +static int set_mode_ic706(struct rpt *myrpt, char newmode); +static int simple_command_ft897(struct rpt *myrpt, char command); +static int setrem(struct rpt *myrpt); + +AST_MUTEX_DEFINE_STATIC(nodeloglock); + +AST_MUTEX_DEFINE_STATIC(nodelookuplock); + +#ifdef APP_RPT_LOCK_DEBUG + +#warning COMPILING WITH LOCK-DEBUGGING ENABLED!! + +#define MAXLOCKTHREAD 100 + +#define rpt_mutex_lock(x) _rpt_mutex_lock(x,myrpt,__LINE__) +#define rpt_mutex_unlock(x) _rpt_mutex_unlock(x,myrpt,__LINE__) + +struct lockthread +{ + pthread_t id; + int lockcount; + int lastlock; + int lastunlock; +} lockthreads[MAXLOCKTHREAD]; + + +struct by_lightning +{ + int line; + struct timeval tv; + struct rpt *rpt; + struct lockthread lockthread; +} lock_ring[32]; + +int lock_ring_index = 0; + +AST_MUTEX_DEFINE_STATIC(locklock); + +static struct lockthread *get_lockthread(pthread_t id) +{ +int i; + + for(i = 0; i < MAXLOCKTHREAD; i++) + { + if (lockthreads[i].id == id) return(&lockthreads[i]); + } + return(NULL); +} + +static struct lockthread *put_lockthread(pthread_t id) +{ +int i; + + for(i = 0; i < MAXLOCKTHREAD; i++) + { + if (lockthreads[i].id == id) + return(&lockthreads[i]); + } + for(i = 0; i < MAXLOCKTHREAD; i++) + { + if (!lockthreads[i].id) + { + lockthreads[i].lockcount = 0; + lockthreads[i].lastlock = 0; + lockthreads[i].lastunlock = 0; + lockthreads[i].id = id; + return(&lockthreads[i]); + } + } + return(NULL); +} + + +static void rpt_mutex_spew(void) +{ + struct by_lightning lock_ring_copy[32]; + int lock_ring_index_copy; + int i,j; + long long diff; + char a[100]; + struct timeval lasttv; + + ast_mutex_lock(&locklock); + memcpy(&lock_ring_copy, &lock_ring, sizeof(lock_ring_copy)); + lock_ring_index_copy = lock_ring_index; + ast_mutex_unlock(&locklock); + + lasttv.tv_sec = lasttv.tv_usec = 0; + for(i = 0 ; i < 32 ; i++) + { + j = (i + lock_ring_index_copy) % 32; + strftime(a,sizeof(a) - 1,"%m/%d/%Y %H:%M:%S", + localtime(&lock_ring_copy[j].tv.tv_sec)); + diff = 0; + if(lasttv.tv_sec) + { + diff = (lock_ring_copy[j].tv.tv_sec - lasttv.tv_sec) + * 1000000; + diff += (lock_ring_copy[j].tv.tv_usec - lasttv.tv_usec); + } + lasttv.tv_sec = lock_ring_copy[j].tv.tv_sec; + lasttv.tv_usec = lock_ring_copy[j].tv.tv_usec; + if (!lock_ring_copy[j].tv.tv_sec) continue; + if (lock_ring_copy[j].line < 0) + { + ast_log(LOG_NOTICE,"LOCKDEBUG [#%d] UNLOCK app_rpt.c:%d node %s pid %x diff %lld us at %s.%06d\n", + i - 31,-lock_ring_copy[j].line,lock_ring_copy[j].rpt->name,(int) lock_ring_copy[j].lockthread.id,diff,a,(int)lock_ring_copy[j].tv.tv_usec); + } + else + { + ast_log(LOG_NOTICE,"LOCKDEBUG [#%d] LOCK app_rpt.c:%d node %s pid %x diff %lld us at %s.%06d\n", + i - 31,lock_ring_copy[j].line,lock_ring_copy[j].rpt->name,(int) lock_ring_copy[j].lockthread.id,diff,a,(int)lock_ring_copy[j].tv.tv_usec); + } + } +} + + +static void _rpt_mutex_lock(ast_mutex_t *lockp, struct rpt *myrpt, int line) +{ +struct lockthread *t; +pthread_t id; + + id = pthread_self(); + ast_mutex_lock(&locklock); + t = put_lockthread(id); + if (!t) + { + ast_mutex_unlock(&locklock); + return; + } + if (t->lockcount) + { + int lastline = t->lastlock; + ast_mutex_unlock(&locklock); + ast_log(LOG_NOTICE,"rpt_mutex_lock: Double lock request line %d node %s pid %x, last lock was line %d\n",line,myrpt->name,(int) t->id,lastline); + rpt_mutex_spew(); + return; + } + t->lastlock = line; + t->lockcount = 1; + gettimeofday(&lock_ring[lock_ring_index].tv, NULL); + lock_ring[lock_ring_index].rpt = myrpt; + memcpy(&lock_ring[lock_ring_index].lockthread,t,sizeof(struct lockthread)); + lock_ring[lock_ring_index++].line = line; + if(lock_ring_index == 32) + lock_ring_index = 0; + ast_mutex_unlock(&locklock); + ast_mutex_lock(lockp); +} + + +static void _rpt_mutex_unlock(ast_mutex_t *lockp, struct rpt *myrpt, int line) +{ +struct lockthread *t; +pthread_t id; + + id = pthread_self(); + ast_mutex_lock(&locklock); + t = put_lockthread(id); + if (!t) + { + ast_mutex_unlock(&locklock); + return; + } + if (!t->lockcount) + { + int lastline = t->lastunlock; + ast_mutex_unlock(&locklock); + ast_log(LOG_NOTICE,"rpt_mutex_lock: Double un-lock request line %d node %s pid %x, last un-lock was line %d\n",line,myrpt->name,(int) t->id,lastline); + rpt_mutex_spew(); + return; + } + t->lastunlock = line; + t->lockcount = 0; + gettimeofday(&lock_ring[lock_ring_index].tv, NULL); + lock_ring[lock_ring_index].rpt = myrpt; + memcpy(&lock_ring[lock_ring_index].lockthread,t,sizeof(struct lockthread)); + lock_ring[lock_ring_index++].line = -line; + if(lock_ring_index == 32) + lock_ring_index = 0; + ast_mutex_unlock(&locklock); + ast_mutex_unlock(lockp); +} + +#else /* APP_RPT_LOCK_DEBUG */ + +#define rpt_mutex_lock(x) ast_mutex_lock(x) +#define rpt_mutex_unlock(x) ast_mutex_unlock(x) + +#endif /* APP_RPT_LOCK_DEBUG */ + +/* +* Return 1 if rig is multimode capable +*/ + +static int multimode_capable(struct rpt *myrpt) +{ + if(!strcmp(myrpt->remote, remote_rig_ft897)) + return 1; + if(!strcmp(myrpt->remote, remote_rig_ic706)) + return 1; + return 0; +} + +/* +* CLI extensions +*/ + +/* Debug mode */ +static int rpt_do_debug(int fd, int argc, char *argv[]); +static int rpt_do_dump(int fd, int argc, char *argv[]); +static int rpt_do_stats(int fd, int argc, char *argv[]); +static int rpt_do_lstats(int fd, int argc, char *argv[]); +static int rpt_do_nodes(int fd, int argc, char *argv[]); +static int rpt_do_reload(int fd, int argc, char *argv[]); +static int rpt_do_restart(int fd, int argc, char *argv[]); +static int rpt_do_fun(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 char dump_usage[] = +"Usage: rpt dump <nodename>\n" +" Dumps struct debug info to log\n"; + +static char dump_stats[] = +"Usage: rpt stats <nodename>\n" +" Dumps node statistics to console\n"; + +static char dump_lstats[] = +"Usage: rpt lstats <nodename>\n" +" Dumps link statistics to console\n"; + +static char dump_nodes[] = +"Usage: rpt nodes <nodename>\n" +" Dumps a list of directly and indirectly connected nodes to the console\n"; + +static char reload_usage[] = +"Usage: rpt reload\n" +" Reloads app_rpt running config parameters\n"; + +static char restart_usage[] = +"Usage: rpt restart\n" +" Restarts app_rpt\n"; + +static char fun_usage[] = +"Usage: rpt fun <nodename> <command>\n" +" Send a DTMF function to a node\n"; + + +static struct ast_cli_entry cli_debug = + { { "rpt", "debug", "level" }, rpt_do_debug, + "Enable app_rpt debugging", debug_usage }; + +static struct ast_cli_entry cli_dump = + { { "rpt", "dump" }, rpt_do_dump, + "Dump app_rpt structs for debugging", dump_usage }; + +static struct ast_cli_entry cli_stats = + { { "rpt", "stats" }, rpt_do_stats, + "Dump node statistics", dump_stats }; + +static struct ast_cli_entry cli_nodes = + { { "rpt", "nodes" }, rpt_do_nodes, + "Dump node list", dump_nodes }; + +static struct ast_cli_entry cli_lstats = + { { "rpt", "lstats" }, rpt_do_lstats, + "Dump link statistics", dump_lstats }; + +static struct ast_cli_entry cli_reload = + { { "rpt", "reload" }, rpt_do_reload, + "Reload app_rpt config", reload_usage }; + +static struct ast_cli_entry cli_restart = + { { "rpt", "restart" }, rpt_do_restart, + "Restart app_rpt", restart_usage }; + +static struct ast_cli_entry cli_fun = + { { "rpt", "fun" }, rpt_do_fun, + "Execute a DTMF function", fun_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); +static int set_ft897(struct rpt *myrpt); +static int set_ic706(struct rpt *myrpt); +static int setkenwood(struct rpt *myrpt); +static int setrbi_check(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); +static int function_macro(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}, + {"macro", function_macro} +} ; + +static long diskavail(struct rpt *myrpt) +{ +struct statfs statfsbuf; + + if (!myrpt->p.archivedir) return(0); + if (statfs(myrpt->p.archivedir,&statfsbuf) == -1) + { + ast_log(LOG_WARNING,"Cannot get filesystem size for %s node %s\n", + myrpt->p.archivedir,myrpt->name); + return(-1); + } + return(statfsbuf.f_bavail); +} + +static void do_dtmf_phone(struct rpt *myrpt, struct rpt_link *mylink, char c) +{ +struct rpt_link *l; + + l = myrpt->links.next; + /* go thru all the links */ + while(l != &myrpt->links) + { + if (!l->phonemode) + { + l = l->next; + continue; + } + /* dont send to self */ + if (mylink && (l == mylink)) + { + l = l->next; + continue; + } + if (l->chan) ast_senddigit(l->chan,c); + l = l->next; + } + return; +} + +/* node logging function */ +static void donodelog(struct rpt *myrpt,char *str) +{ +struct nodelog *nodep; +char datestr[100]; + + if (!myrpt->p.archivedir) return; + nodep = (struct nodelog *)malloc(sizeof(struct nodelog)); + if (nodep == NULL) + { + ast_log(LOG_ERROR,"Cannot get memory for node log"); + return; + } + time(&nodep->timestamp); + strncpy(nodep->archivedir,myrpt->p.archivedir, + sizeof(nodep->archivedir) - 1); + strftime(datestr,sizeof(datestr) - 1,"%Y%m%d%H%M%S", + localtime(&nodep->timestamp)); + snprintf(nodep->str,sizeof(nodep->str) - 1,"%s %s,%s\n", + myrpt->name,datestr,str); + ast_mutex_lock(&nodeloglock); + insque((struct qelem *) nodep, (struct qelem *) nodelog.prev); + ast_mutex_unlock(&nodeloglock); +} + +/* must be called locked */ +static void do_dtmf_local(struct rpt *myrpt, char c) +{ +int i; +char digit; +static const char* dtmf_tones[] = { + "!941+1336/200,!0/200", /* 0 */ + "!697+1209/200,!0/200", /* 1 */ + "!697+1336/200,!0/200", /* 2 */ + "!697+1477/200,!0/200", /* 3 */ + "!770+1209/200,!0/200", /* 4 */ + "!770+1336/200,!0/200", /* 5 */ + "!770+1477/200,!0/200", /* 6 */ + "!852+1209/200,!0/200", /* 7 */ + "!852+1336/200,!0/200", /* 8 */ + "!852+1477/200,!0/200", /* 9 */ + "!697+1633/200,!0/200", /* A */ + "!770+1633/200,!0/200", /* B */ + "!852+1633/200,!0/200", /* C */ + "!941+1633/200,!0/200", /* D */ + "!941+1209/200,!0/200", /* * */ + "!941+1477/200,!0/200" }; /* # */ + + + if (c) + { + snprintf(myrpt->dtmf_local_str + strlen(myrpt->dtmf_local_str),sizeof(myrpt->dtmf_local_str) - 1,"%c",c); + if (!myrpt->dtmf_local_timer) + myrpt->dtmf_local_timer = DTMF_LOCAL_STARTTIME; + } + /* if at timeout */ + if (myrpt->dtmf_local_timer == 1) + { + /* if anything in the string */ + if (myrpt->dtmf_local_str[0]) + { + digit = myrpt->dtmf_local_str[0]; + myrpt->dtmf_local_str[0] = 0; + for(i = 1; myrpt->dtmf_local_str[i]; i++) + { + myrpt->dtmf_local_str[i - 1] = + myrpt->dtmf_local_str[i]; + } + myrpt->dtmf_local_str[i - 1] = 0; + myrpt->dtmf_local_timer = DTMF_LOCAL_TIME; + rpt_mutex_unlock(&myrpt->lock); + if (digit >= '0' && digit <='9') + ast_playtones_start(myrpt->txchannel, 0, dtmf_tones[digit-'0'], 0); + else if (digit >= 'A' && digit <= 'D') + ast_playtones_start(myrpt->txchannel, 0, dtmf_tones[digit-'A'+10], 0); + else if (digit == '*') + ast_playtones_start(myrpt->txchannel, 0, dtmf_tones[14], 0); + else if (digit == '#') + ast_playtones_start(myrpt->txchannel, 0, dtmf_tones[15], 0); + else { + /* not handled */ + ast_log(LOG_DEBUG, "Unable to generate DTMF tone '%c' for '%s'\n", digit, myrpt->txchannel->name); + } + rpt_mutex_lock(&myrpt->lock); + } + else + { + myrpt->dtmf_local_timer = 0; + } + } +} + +static int openserial(char *fname) +{ + struct termios mode; + int fd; + + fd = open(fname,O_RDWR); + if (fd == -1) + { + ast_log(LOG_WARNING,"Cannot open serial port %s\n",fname); + return -1; + } + memset(&mode, 0, sizeof(mode)); + if (tcgetattr(fd, &mode)) { + ast_log(LOG_WARNING, "Unable to get serial parameters on %s: %s\n", fname, strerror(errno)); + return -1; + } +#ifndef SOLARIS + cfmakeraw(&mode); +#else + mode.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP + |INLCR|IGNCR|ICRNL|IXON); + mode.c_oflag &= ~OPOST; + mode.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + mode.c_cflag &= ~(CSIZE|PARENB|CRTSCTS); + mode.c_cflag |= CS8; + mode.c_cc[TIME] = 3; + mode.c_cc[MAX] = 1; +#endif + + cfsetispeed(&mode, B9600); + cfsetospeed(&mode, B9600); + if (tcsetattr(fd, TCSANOW, &mode)) + ast_log(LOG_WARNING, "Unable to set serial parameters on %s: %s\n", fname, strerror(errno)); + return(fd); +} + +static void mdc1200_notify(struct rpt *myrpt,char *fromnode, unsigned int unit) +{ + if (!fromnode) + { + ast_verbose("Got MDC-1200 ID %04X from local system (%s)\n", + unit,myrpt->name); + } + else + { + ast_verbose("Got MDC-1200 ID %04X from node %s (%s)\n", + unit,fromnode,myrpt->name); + } +} + +#ifdef _MDC_DECODE_H_ + +static void mdc1200_send(struct rpt *myrpt, unsigned int unit) +{ +struct rpt_link *l; +struct ast_frame wf; +char str[200]; + + + sprintf(str,"I %s %04X",myrpt->name,unit); + + wf.frametype = AST_FRAME_TEXT; + wf.subclass = 0; + wf.offset = 0; + wf.mallocd = 0; + wf.datalen = strlen(str) + 1; + wf.samples = 0; + + + l = myrpt->links.next; + /* otherwise, send it to all of em */ + while(l != &myrpt->links) + { + if (l->name[0] == '0') + { + l = l->next; + continue; + } + wf.data = str; + if (l->chan) ast_write(l->chan,&wf); + l = l->next; + } + return; +} + +#endif + +static char func_xlat(struct rpt *myrpt,char c,struct rpt_xlat *xlat) +{ +time_t now; +int gotone; + + time(&now); + gotone = 0; + /* if too much time, reset the skate machine */ + if ((now - xlat->lastone) > MAXXLATTIME) + { + xlat->funcindex = xlat->endindex = 0; + } + if (xlat->funccharseq[0] && (c == xlat->funccharseq[xlat->funcindex++])) + { + time(&xlat->lastone); + gotone = 1; + if (!xlat->funccharseq[xlat->funcindex]) + { + xlat->funcindex = xlat->endindex = 0; + return(myrpt->p.funcchar); + } + } else xlat->funcindex = 0; + if (xlat->endcharseq[0] && (c == xlat->endcharseq[xlat->endindex++])) + { + time(&xlat->lastone); + gotone = 1; + if (!xlat->endcharseq[xlat->endindex]) + { + xlat->funcindex = xlat->endindex = 0; + return(myrpt->p.endchar); + } + } else xlat->endindex = 0; + /* if in middle of decode seq, send nothing back */ + if (gotone) return(0); + /* if no pass chars specified, return em all */ + if (!xlat->passchars[0]) return(c); + /* if a "pass char", pass it */ + if (strchr(xlat->passchars,c)) return(c); + return(0); +} + +/* + * Return a pointer to the first non-whitespace character + */ + +static char *eatwhite(char *s) +{ + while((*s == ' ') || (*s == 0x09)){ /* get rid of any leading white space */ + if(!*s) + break; + s++; + } + return s; +} + +/* +* Break up a delimited string into a table of substrings +* +* str - delimited string ( will be modified ) +* strp- list of pointers to substrings (this is built by this function), NULL will be placed at end of list +* limit- maximum number of substrings to process +*/ + + + +static int finddelim(char *str, char *strp[], int limit) +{ +int i,l,inquo; + + inquo = 0; + i = 0; + strp[i++] = str; + if (!*str) + { + strp[0] = 0; + return(0); + } + for(l = 0; *str && (l < limit) ; str++) + { + if (*str == QUOTECHR) + { + if (inquo) + { + *str = 0; + inquo = 0; + } + else + { + strp[i - 1] = str + 1; + inquo = 1; + } + } + if ((*str == DELIMCHR) && (!inquo)) + { + *str = 0; + l++; + strp[i++] = str + 1; + } + } + strp[i] = 0; + return(i); + +} + +/* must be called locked */ +static void __mklinklist(struct rpt *myrpt, struct rpt_link *mylink, char *buf) +{ +struct rpt_link *l; +char mode; +int i,spos; + + buf[0] = 0; /* clear output buffer */ + /* go thru all links */ + for(l = myrpt->links.next; l != &myrpt->links; l = l->next) + { + /* if is not a real link, ignore it */ + if (l->name[0] == '0') continue; + /* dont count our stuff */ + if (l == mylink) continue; + if (mylink && (!strcmp(l->name,mylink->name))) continue; + /* figure out mode to report */ + mode = 'T'; /* use Tranceive by default */ + if (!l->mode) mode = 'R'; /* indicate RX for our mode */ + if (!l->thisconnected) mode = 'C'; /* indicate connecting */ + spos = strlen(buf); /* current buf size (b4 we add our stuff) */ + if (spos) + { + strcat(buf,","); + spos++; + } + /* add nodes into buffer */ + if (l->linklist[0]) + { + snprintf(buf + spos,MAXLINKLIST - spos, + "%c%s,%s",mode,l->name,l->linklist); + } + else /* if no nodes, add this node into buffer */ + { + snprintf(buf + spos,MAXLINKLIST - spos, + "%c%s",mode,l->name); + } + /* if we are in tranceive mode, let all modes stand */ + if (mode == 'T') continue; + /* downgrade everyone on this node if appropriate */ + for(i = spos; buf[i]; i++) + { + if (buf[i] == 'T') buf[i] = mode; + if ((buf[i] == 'R') && (mode == 'C')) buf[i] = mode; + } + } + return; +} + +/* must be called locked */ +static void __kickshort(struct rpt *myrpt) +{ +struct rpt_link *l; + + for(l = myrpt->links.next; l != &myrpt->links; l = l->next) + { + /* if is not a real link, ignore it */ + if (l->name[0] == '0') continue; + l->linklisttimer = LINKLISTSHORTTIME; + } + return; +} + +static char *node_lookup(struct rpt *myrpt,char *digitbuf) +{ + +char *val; +int longestnode,j; +struct stat mystat; +static time_t last = 0; +static struct ast_config *ourcfg = NULL; +struct ast_variable *vp; + + /* try to look it up locally first */ + val = (char *) ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, digitbuf); + if (val) return(val); + ast_mutex_lock(&nodelookuplock); + /* if file does not exist */ + if (stat(myrpt->p.extnodefile,&mystat) == -1) + { + if (ourcfg) ast_config_destroy(ourcfg); + ourcfg = NULL; + ast_mutex_unlock(&nodelookuplock); + return(NULL); + } + /* if we need to reload */ + if (mystat.st_mtime > last) + { + if (ourcfg) ast_config_destroy(ourcfg); + ourcfg = ast_config_load(myrpt->p.extnodefile); + /* if file not there, just bail */ + if (!ourcfg) + { + ast_mutex_unlock(&nodelookuplock); + return(NULL); + } + /* reset "last" time */ + last = mystat.st_mtime; + + /* determine longest node length again */ + longestnode = 0; + vp = ast_variable_browse(myrpt->cfg, myrpt->p.nodes); + while(vp){ + j = strlen(vp->name); + if (j > longestnode) + longestnode = j; + vp = vp->next; + } + + vp = ast_variable_browse(ourcfg, myrpt->p.extnodes); + while(vp){ + j = strlen(vp->name); + if (j > longestnode) + longestnode = j; + vp = vp->next; + } + + myrpt->longestnode = longestnode; + } + val = NULL; + if (ourcfg) + val = (char *) ast_variable_retrieve(ourcfg, myrpt->p.extnodes, digitbuf); + ast_mutex_unlock(&nodelookuplock); + return(val); +} + +/* +* Match a keyword in a list, and return index of string plus 1 if there was a match,* else return 0. +* If param is passed in non-null, then it will be set to the first character past the match +*/ + +static int matchkeyword(char *string, char **param, char *keywords[]) +{ +int i,ls; + for( i = 0 ; keywords[i] ; i++){ + ls = strlen(keywords[i]); + if(!ls){ + *param = NULL; + return 0; + } + if(!strncmp(string, keywords[i], ls)){ + if(param) + *param = string + ls; + return i + 1; + } + } + param = NULL; + return 0; +} + +/* +* Skip characters in string which are in charlist, and return a pointer to the +* first non-matching character +*/ + +static char *skipchars(char *string, char *charlist) +{ +int i; + while(*string){ + for(i = 0; charlist[i] ; i++){ + if(*string == charlist[i]){ + string++; + break; + } + } + if(!charlist[i]) + return string; + } + return string; +} + + + +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; +} + +static int mycompar(const void *a, const void *b) +{ +char **x = (char **) a; +char **y = (char **) b; +int xoff,yoff; + + if ((**x < '0') || (**x > '9')) xoff = 1; else xoff = 0; + if ((**y < '0') || (**y > '9')) yoff = 1; else yoff = 0; + return(strcmp((*x) + xoff,(*y) + yoff)); +} + +#ifdef __RPT_NOTCH + +/* rpt filter routine */ +static void rpt_filter(struct rpt *myrpt, volatile short *buf, int len) +{ +int i,j; +struct rptfilter *f; + + for(i = 0; i < len; i++) + { + for(j = 0; j < MAXFILTERS; j++) + { + f = &myrpt->filters[j]; + if (!*f->desc) continue; + f->x0 = f->x1; f->x1 = f->x2; + f->x2 = ((float)buf[i]) / f->gain; + f->y0 = f->y1; f->y1 = f->y2; + f->y2 = (f->x0 + f->x2) + f->const0 * f->x1 + + (f->const1 * f->y0) + (f->const2 * f->y1); + buf[i] = (short)f->y2; + } + } +} + +#endif + + +/* + Get the time for the machine's time zone + Note: Asterisk requires a copy of localtime + in the /etc directory for this to work properly. + If /etc/localtime is not present, you will get + GMT time! This is especially important on systems + running embedded linux distributions as they don't usually + have support for locales. + + If OLD_ASTERISK is defined, then the older localtime_r + function will be used. The /etc/localtime file is not + required in this case. This provides backward compatibility + with Asterisk 1.2 systems. + +*/ + +static void rpt_localtime( time_t * t, struct tm *lt) +{ +#ifdef OLD_ASTERISK + localtime_r(t, lt); +#else + ast_localtime(t, lt, NULL); +#endif +} + +/* Retrieve an int from a config file */ + +static int retrieve_astcfgint(struct rpt *myrpt,char *category, char *name, int min, int max, int defl) +{ + char *var; + int ret; + char include_zero = 0; + + if(min < 0){ /* If min is negative, this means include 0 as a valid entry */ + min = -min; + include_zero = 1; + } + + var = (char *) ast_variable_retrieve(myrpt->cfg, category, name); + if(var){ + ret = myatoi(var); + if(include_zero && !ret) + return 0; + if(ret < min) + ret = min; + if(ret > max) + ret = max; + } + else + ret = defl; + return ret; +} + + +static void load_rpt_vars(int n,int init) +{ +char *this,*val; +int i,j,longestnode; +struct ast_variable *vp; +struct ast_config *cfg; +char *strs[100]; +char s1[256]; +static char *cs_keywords[] = {"rptena","rptdis","apena","apdis","lnkena","lnkdis","totena","totdis","skena","skdis", + "ufena","ufdis","atena","atdis",NULL}; + + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "%s config for repeater %s\n", + (init) ? "Loading initial" : "Re-Loading",rpt_vars[n].name); + ast_mutex_lock(&rpt_vars[n].lock); + if (rpt_vars[n].cfg) ast_config_destroy(rpt_vars[n].cfg); + cfg = ast_config_load("rpt.conf"); + if (!cfg) { + ast_mutex_unlock(&rpt_vars[n].lock); + ast_log(LOG_NOTICE, "Unable to open radio repeater configuration rpt.conf. Radio Repeater disabled.\n"); + pthread_exit(NULL); + } + rpt_vars[n].cfg = cfg; + this = rpt_vars[n].name; + memset(&rpt_vars[n].p,0,sizeof(rpt_vars[n].p)); + if (init) + { + /* clear all the fields in the structure after 'p' */ + memset(&rpt_vars[n].p + sizeof(rpt_vars[0].p), 0, sizeof(rpt_vars[0]) - sizeof(rpt_vars[0].p) - offsetof(typeof(rpt_vars[0]), p)); + 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].tailmessagen = 0; + } +#ifdef __RPT_NOTCH + /* zot out filters stuff */ + memset(&rpt_vars[n].filters,0,sizeof(rpt_vars[n].filters)); +#endif + val = (char *) ast_variable_retrieve(cfg,this,"context"); + if (val) rpt_vars[n].p.ourcontext = val; + else rpt_vars[n].p.ourcontext = this; + val = (char *) ast_variable_retrieve(cfg,this,"callerid"); + if (val) rpt_vars[n].p.ourcallerid = val; + val = (char *) ast_variable_retrieve(cfg,this,"accountcode"); + if (val) rpt_vars[n].p.acctcode = val; + val = (char *) ast_variable_retrieve(cfg,this,"idrecording"); + if (val) rpt_vars[n].p.ident = val; + val = (char *) ast_variable_retrieve(cfg,this,"hangtime"); + if (val) rpt_vars[n].p.hangtime = atoi(val); + else rpt_vars[n].p.hangtime = HANGTIME; + val = (char *) ast_variable_retrieve(cfg,this,"althangtime"); + if (val) rpt_vars[n].p.althangtime = atoi(val); + else rpt_vars[n].p.althangtime = HANGTIME; + val = (char *) ast_variable_retrieve(cfg,this,"totime"); + if (val) rpt_vars[n].p.totime = atoi(val); + else rpt_vars[n].p.totime = TOTIME; + rpt_vars[n].p.tailmessagetime = retrieve_astcfgint(&rpt_vars[n],this, "tailmessagetime", 0, 2400000, 0); + rpt_vars[n].p.tailsquashedtime = retrieve_astcfgint(&rpt_vars[n],this, "tailsquashedtime", 0, 2400000, 0); + rpt_vars[n].p.duplex = retrieve_astcfgint(&rpt_vars[n],this,"duplex",0,4,2); + rpt_vars[n].p.idtime = retrieve_astcfgint(&rpt_vars[n],this, "idtime", -60000, 2400000, IDTIME); /* Enforce a min max including zero */ + rpt_vars[n].p.politeid = retrieve_astcfgint(&rpt_vars[n],this, "politeid", 30000, 300000, POLITEID); /* Enforce a min max */ + val = (char *) ast_variable_retrieve(cfg,this,"tonezone"); + if (val) rpt_vars[n].p.tonezone = val; + rpt_vars[n].p.tailmessages[0] = 0; + rpt_vars[n].p.tailmessagemax = 0; + val = (char *) ast_variable_retrieve(cfg,this,"tailmessagelist"); + if (val) rpt_vars[n].p.tailmessagemax = finddelim(val, rpt_vars[n].p.tailmessages, 500); + val = (char *) ast_variable_retrieve(cfg,this,"memory"); + if (!val) val = MEMORY; + rpt_vars[n].p.memory = val; + val = (char *) ast_variable_retrieve(cfg,this,"macro"); + if (!val) val = MACRO; + rpt_vars[n].p.macro = val; + val = (char *) ast_variable_retrieve(cfg,this,"startup_macro"); + if (val) rpt_vars[n].p.startupmacro = val; + val = (char *) 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].p.iobase) != 1)) + rpt_vars[n].p.iobase = DEFAULT_IOBASE; + val = (char *) ast_variable_retrieve(cfg,this,"ioport"); + rpt_vars[n].p.ioport = val; + val = (char *) ast_variable_retrieve(cfg,this,"functions"); + if (!val) + { + val = FUNCTIONS; + rpt_vars[n].p.simple = 1; + } + rpt_vars[n].p.functions = val; + val = (char *) ast_variable_retrieve(cfg,this,"link_functions"); + if (val) rpt_vars[n].p.link_functions = val; + else + rpt_vars[n].p.link_functions = rpt_vars[n].p.functions; + val = (char *) ast_variable_retrieve(cfg,this,"phone_functions"); + if (val) rpt_vars[n].p.phone_functions = val; + val = (char *) ast_variable_retrieve(cfg,this,"dphone_functions"); + if (val) rpt_vars[n].p.dphone_functions = val; + val = (char *) ast_variable_retrieve(cfg,this,"funcchar"); + if (!val) rpt_vars[n].p.funcchar = FUNCCHAR; else + rpt_vars[n].p.funcchar = *val; + val = (char *) ast_variable_retrieve(cfg,this,"endchar"); + if (!val) rpt_vars[n].p.endchar = ENDCHAR; else + rpt_vars[n].p.endchar = *val; + val = (char *) ast_variable_retrieve(cfg,this,"nobusyout"); + if (val) rpt_vars[n].p.nobusyout = ast_true(val); + val = (char *) ast_variable_retrieve(cfg,this,"notelemtx"); + if (val) rpt_vars[n].p.notelemtx = ast_true(val); + val = (char *) ast_variable_retrieve(cfg,this,"propagate_dtmf"); + if (val) rpt_vars[n].p.propagate_dtmf = ast_true(val); + val = (char *) ast_variable_retrieve(cfg,this,"propagate_phonedtmf"); + if (val) rpt_vars[n].p.propagate_phonedtmf = ast_true(val); + val = (char *) ast_variable_retrieve(cfg,this,"linktolink"); + if (val) rpt_vars[n].p.linktolink = ast_true(val); + val = (char *) ast_variable_retrieve(cfg,this,"nodes"); + if (!val) val = NODES; + rpt_vars[n].p.nodes = val; + val = (char *) ast_variable_retrieve(cfg,this,"extnodes"); + if (!val) val = EXTNODES; + rpt_vars[n].p.extnodes = val; + val = (char *) ast_variable_retrieve(cfg,this,"extnodefile"); + if (!val) val = EXTNODEFILE; + rpt_vars[n].p.extnodefile = val; + val = (char *) ast_variable_retrieve(cfg,this,"archivedir"); + if (val) rpt_vars[n].p.archivedir = val; + val = (char *) ast_variable_retrieve(cfg,this,"authlevel"); + if (val) rpt_vars[n].p.authlevel = atoi(val); + else rpt_vars[n].p.authlevel = 0; + val = (char *) ast_variable_retrieve(cfg,this,"monminblocks"); + if (val) rpt_vars[n].p.monminblocks = atol(val); + else rpt_vars[n].p.monminblocks = DEFAULT_MONITOR_MIN_DISK_BLOCKS; + val = (char *) ast_variable_retrieve(cfg,this,"remote_inact_timeout"); + if (val) rpt_vars[n].p.remoteinacttimeout = atoi(val); + else rpt_vars[n].p.remoteinacttimeout = DEFAULT_REMOTE_INACT_TIMEOUT; + val = (char *) ast_variable_retrieve(cfg,this,"civaddr"); + if (val) rpt_vars[n].p.civaddr = atoi(val); + else rpt_vars[n].p.civaddr = DEFAULT_CIV_ADDR; + val = (char *) ast_variable_retrieve(cfg,this,"remote_timeout"); + if (val) rpt_vars[n].p.remotetimeout = atoi(val); + else rpt_vars[n].p.remotetimeout = DEFAULT_REMOTE_TIMEOUT; + val = (char *) ast_variable_retrieve(cfg,this,"remote_timeout_warning"); + if (val) rpt_vars[n].p.remotetimeoutwarning = atoi(val); + else rpt_vars[n].p.remotetimeoutwarning = DEFAULT_REMOTE_TIMEOUT_WARNING; + val = (char *) ast_variable_retrieve(cfg,this,"remote_timeout_warning_freq"); + if (val) rpt_vars[n].p.remotetimeoutwarningfreq = atoi(val); + else rpt_vars[n].p.remotetimeoutwarningfreq = DEFAULT_REMOTE_TIMEOUT_WARNING_FREQ; +#ifdef __RPT_NOTCH + val = (char *) ast_variable_retrieve(cfg,this,"rxnotch"); + if (val) { + i = finddelim(val,strs,MAXFILTERS * 2); + i &= ~1; /* force an even number, rounded down */ + if (i >= 2) for(j = 0; j < i; j += 2) + { + rpt_mknotch(atof(strs[j]),atof(strs[j + 1]), + &rpt_vars[n].filters[j >> 1].gain, + &rpt_vars[n].filters[j >> 1].const0, + &rpt_vars[n].filters[j >> 1].const1, + &rpt_vars[n].filters[j >> 1].const2); + sprintf(rpt_vars[n].filters[j >> 1].desc,"%s Hz, BW = %s", + strs[j],strs[j + 1]); + } + + } +#endif + val = (char *) ast_variable_retrieve(cfg,this,"inxlat"); + if (val) { + memset(&rpt_vars[n].p.inxlat,0,sizeof(struct rpt_xlat)); + i = finddelim(val,strs,3); + if (i) strncpy(rpt_vars[n].p.inxlat.funccharseq,strs[0],MAXXLAT - 1); + if (i > 1) strncpy(rpt_vars[n].p.inxlat.endcharseq,strs[1],MAXXLAT - 1); + if (i > 2) strncpy(rpt_vars[n].p.inxlat.passchars,strs[2],MAXXLAT - 1); + } + val = (char *) ast_variable_retrieve(cfg,this,"outxlat"); + if (val) { + memset(&rpt_vars[n].p.outxlat,0,sizeof(struct rpt_xlat)); + i = finddelim(val,strs,3); + if (i) strncpy(rpt_vars[n].p.outxlat.funccharseq,strs[0],MAXXLAT - 1); + if (i > 1) strncpy(rpt_vars[n].p.outxlat.endcharseq,strs[1],MAXXLAT - 1); + if (i > 2) strncpy(rpt_vars[n].p.outxlat.passchars,strs[2],MAXXLAT - 1); + } + /* retreive the stanza name for the control states if there is one */ + val = (char *) ast_variable_retrieve(cfg,this,"controlstates"); + rpt_vars[n].p.csstanzaname = val; + + /* retreive the stanza name for the scheduler if there is one */ + val = (char *) ast_variable_retrieve(cfg,this,"scheduler"); + rpt_vars[n].p.skedstanzaname = val; + + /* retreive the stanza name for the txlimits */ + val = (char *) ast_variable_retrieve(cfg,this,"txlimits"); + rpt_vars[n].p.txlimitsstanzaname = val; + + longestnode = 0; + + vp = ast_variable_browse(cfg, rpt_vars[n].p.nodes); + + while(vp){ + j = strlen(vp->name); + if (j > longestnode) + longestnode = j; + vp = vp->next; + } + + rpt_vars[n].longestnode = longestnode; + + /* + * For this repeater, Determine the length of the longest function + */ + rpt_vars[n].longestfunc = 0; + vp = ast_variable_browse(cfg, rpt_vars[n].p.functions); + while(vp){ + j = strlen(vp->name); + if (j > rpt_vars[n].longestfunc) + rpt_vars[n].longestfunc = j; + vp = vp->next; + } + /* + * For this repeater, Determine the length of the longest function + */ + rpt_vars[n].link_longestfunc = 0; + vp = ast_variable_browse(cfg, rpt_vars[n].p.link_functions); + while(vp){ + j = strlen(vp->name); + if (j > rpt_vars[n].link_longestfunc) + rpt_vars[n].link_longestfunc = j; + vp = vp->next; + } + rpt_vars[n].phone_longestfunc = 0; + if (rpt_vars[n].p.phone_functions) + { + vp = ast_variable_browse(cfg, rpt_vars[n].p.phone_functions); + while(vp){ + j = strlen(vp->name); + if (j > rpt_vars[n].phone_longestfunc) + rpt_vars[n].phone_longestfunc = j; + vp = vp->next; + } + } + rpt_vars[n].dphone_longestfunc = 0; + if (rpt_vars[n].p.dphone_functions) + { + vp = ast_variable_browse(cfg, rpt_vars[n].p.dphone_functions); + while(vp){ + j = strlen(vp->name); + if (j > rpt_vars[n].dphone_longestfunc) + rpt_vars[n].dphone_longestfunc = j; + vp = vp->next; + } + } + rpt_vars[n].macro_longest = 1; + vp = ast_variable_browse(cfg, rpt_vars[n].p.macro); + while(vp){ + j = strlen(vp->name); + if (j > rpt_vars[n].macro_longest) + rpt_vars[n].macro_longest = j; + vp = vp->next; + } + + /* Browse for control states */ + if(rpt_vars[n].p.csstanzaname) + vp = ast_variable_browse(cfg, rpt_vars[n].p.csstanzaname); + else + vp = NULL; + for( i = 0 ; vp && (i < MAX_SYSSTATES) ; i++){ /* Iterate over the number of control state lines in the stanza */ + int k,nukw,statenum; + statenum=atoi(vp->name); + strncpy(s1, vp->value, 255); + s1[255] = 0; + nukw = finddelim(s1,strs,32); + + for (k = 0 ; k < nukw ; k++){ /* for each user specified keyword */ + for(j = 0 ; cs_keywords[j] != NULL ; j++){ /* try to match to one in our internal table */ + if(!strcmp(strs[k],cs_keywords[j])){ + switch(j){ + case 0: /* rptena */ + rpt_vars[n].p.s[statenum].txdisable = 0; + break; + case 1: /* rptdis */ + rpt_vars[n].p.s[statenum].txdisable = 1; + break; + + case 2: /* apena */ + rpt_vars[n].p.s[statenum].autopatchdisable = 0; + break; + + case 3: /* apdis */ + rpt_vars[n].p.s[statenum].autopatchdisable = 1; + break; + + case 4: /* lnkena */ + rpt_vars[n].p.s[statenum].linkfundisable = 0; + break; + + case 5: /* lnkdis */ + rpt_vars[n].p.s[statenum].linkfundisable = 1; + break; + + case 6: /* totena */ + rpt_vars[n].p.s[statenum].totdisable = 0; + break; + + case 7: /* totdis */ + rpt_vars[n].p.s[statenum].totdisable = 1; + break; + + case 8: /* skena */ + rpt_vars[n].p.s[statenum].schedulerdisable = 0; + break; + + case 9: /* skdis */ + rpt_vars[n].p.s[statenum].schedulerdisable = 1; + break; + + case 10: /* ufena */ + rpt_vars[n].p.s[statenum].userfundisable = 0; + break; + + case 11: /* ufdis */ + rpt_vars[n].p.s[statenum].userfundisable = 1; + break; + + case 12: /* atena */ + rpt_vars[n].p.s[statenum].alternatetail = 1; + break; + + case 13: /* atdis */ + rpt_vars[n].p.s[statenum].alternatetail = 0; + break; + + default: + ast_log(LOG_WARNING, + "Unhandled control state keyword %s", cs_keywords[i]); + break; + } + } + } + } + vp = vp->next; + } + ast_mutex_unlock(&rpt_vars[n].lock); +} + +/* +* 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; +} + +/* +* Dump rpt struct debugging onto console +*/ + +static int rpt_do_dump(int fd, int argc, char *argv[]) +{ + int i; + + if (argc != 3) + return RESULT_SHOWUSAGE; + + for(i = 0; i < nrpts; i++) + { + if (!strcmp(argv[2],rpt_vars[i].name)) + { + rpt_vars[i].disgorgetime = time(NULL) + 10; /* Do it 10 seconds later */ + ast_cli(fd, "app_rpt struct dump requested for node %s\n",argv[2]); + return RESULT_SUCCESS; + } + } + return RESULT_FAILURE; +} + +/* +* Dump statistics onto console +*/ + +static int rpt_do_stats(int fd, int argc, char *argv[]) +{ + int i,j; + int dailytxtime, dailykerchunks; + int totalkerchunks, dailykeyups, totalkeyups, timeouts; + int totalexecdcommands, dailyexecdcommands, hours, minutes, seconds; + long long totaltxtime; + struct rpt_link *l; + char *listoflinks[MAX_STAT_LINKS]; + char *lastnodewhichkeyedusup, *lastdtmfcommand; + char *tot_state, *ider_state, *patch_state; + char *reverse_patch_state, *sys_ena, *tot_ena, *link_ena, *patch_ena; + char *sch_ena, *input_signal, *called_number, *user_funs, *tail_type; + struct rpt *myrpt; + + static char *not_applicable = "N/A"; + + if(argc != 3) + return RESULT_SHOWUSAGE; + + for(i = 0 ; i < MAX_STAT_LINKS; i++) + listoflinks[i] = NULL; + + tot_state = ider_state = + patch_state = reverse_patch_state = + input_signal = called_number = + lastdtmfcommand = not_applicable; + + for(i = 0; i < nrpts; i++) + { + if (!strcmp(argv[2],rpt_vars[i].name)){ + /* Make a copy of all stat variables while locked */ + myrpt = &rpt_vars[i]; + rpt_mutex_lock(&myrpt->lock); /* LOCK */ + + dailytxtime = myrpt->dailytxtime; + totaltxtime = myrpt->totaltxtime; + dailykeyups = myrpt->dailykeyups; + totalkeyups = myrpt->totalkeyups; + dailykerchunks = myrpt->dailykerchunks; + totalkerchunks = myrpt->totalkerchunks; + dailyexecdcommands = myrpt->dailyexecdcommands; + totalexecdcommands = myrpt->totalexecdcommands; + timeouts = myrpt->timeouts; + + /* Traverse the list of connected nodes */ + reverse_patch_state = "DOWN"; + j = 0; + l = myrpt->links.next; + while(l && (l != &myrpt->links)){ + if (l->name[0] == '0'){ /* Skip '0' nodes */ + reverse_patch_state = "UP"; + l = l->next; + continue; + } + listoflinks[j] = ast_strdupa(l->name); + if(listoflinks[j]) + j++; + l = l->next; + } + + lastnodewhichkeyedusup = ast_strdupa(myrpt->lastnodewhichkeyedusup); + if((!lastnodewhichkeyedusup) || (!strlen(lastnodewhichkeyedusup))) + lastnodewhichkeyedusup = not_applicable; + + if(myrpt->keyed) + input_signal = "YES"; + else + input_signal = "NO"; + + if(myrpt->p.s[myrpt->p.sysstate_cur].txdisable) + sys_ena = "DISABLED"; + else + sys_ena = "ENABLED"; + + if(myrpt->p.s[myrpt->p.sysstate_cur].totdisable) + tot_ena = "DISABLED"; + else + tot_ena = "ENABLED"; + + if(myrpt->p.s[myrpt->p.sysstate_cur].linkfundisable) + link_ena = "DISABLED"; + else + link_ena = "ENABLED"; + + if(myrpt->p.s[myrpt->p.sysstate_cur].autopatchdisable) + patch_ena = "DISABLED"; + else + patch_ena = "ENABLED"; + + if(myrpt->p.s[myrpt->p.sysstate_cur].schedulerdisable) + sch_ena = "DISABLED"; + else + sch_ena = "ENABLED"; + + if(myrpt->p.s[myrpt->p.sysstate_cur].userfundisable) + user_funs = "DISABLED"; + else + user_funs = "ENABLED"; + + if(myrpt->p.s[myrpt->p.sysstate_cur].alternatetail) + tail_type = "ALTERNATE"; + else + tail_type = "STANDARD"; + + if(!myrpt->totimer) + tot_state = "TIMED OUT!"; + else if(myrpt->totimer != myrpt->p.totime) + tot_state = "ARMED"; + else + tot_state = "RESET"; + + if(myrpt->tailid) + ider_state = "QUEUED IN TAIL"; + else if(myrpt->mustid) + ider_state = "QUEUED FOR CLEANUP"; + else + ider_state = "CLEAN"; + + switch(myrpt->callmode){ + case 1: + patch_state = "DIALING"; + break; + case 2: + patch_state = "CONNECTING"; + break; + case 3: + patch_state = "UP"; + break; + + case 4: + patch_state = "CALL FAILED"; + break; + + default: + patch_state = "DOWN"; + } + + if(strlen(myrpt->exten)){ + called_number = ast_strdupa(myrpt->exten); + if(!called_number) + called_number = not_applicable; + } + + if(strlen(myrpt->lastdtmfcommand)){ + lastdtmfcommand = ast_strdupa(myrpt->lastdtmfcommand); + if(!lastdtmfcommand) + lastdtmfcommand = not_applicable; + } + + rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */ + + ast_cli(fd, "************************ NODE %s STATISTICS *************************\n\n", myrpt->name); + ast_cli(fd, "Selected system state............................: %d\n", myrpt->p.sysstate_cur); + ast_cli(fd, "Signal on input..................................: %s\n", input_signal); + ast_cli(fd, "System...........................................: %s\n", sys_ena); + ast_cli(fd, "Scheduler........................................: %s\n", sch_ena); + ast_cli(fd, "Tail Time........................................: %s\n", tail_type); + ast_cli(fd, "Time out timer...................................: %s\n", tot_ena); + ast_cli(fd, "Time out timer state.............................: %s\n", tot_state); + ast_cli(fd, "Time outs since system initialization............: %d\n", timeouts); + ast_cli(fd, "Identifier state.................................: %s\n", ider_state); + ast_cli(fd, "Kerchunks today..................................: %d\n", dailykerchunks); + ast_cli(fd, "Kerchunks since system initialization............: %d\n", totalkerchunks); + ast_cli(fd, "Keyups today.....................................: %d\n", dailykeyups); + ast_cli(fd, "Keyups since system initialization...............: %d\n", totalkeyups); + ast_cli(fd, "DTMF commands today..............................: %d\n", dailyexecdcommands); + ast_cli(fd, "DTMF commands since system initialization........: %d\n", totalexecdcommands); + ast_cli(fd, "Last DTMF command executed.......................: %s\n", lastdtmfcommand); + hours = dailytxtime/3600000; + dailytxtime %= 3600000; + minutes = dailytxtime/60000; + dailytxtime %= 60000; + seconds = dailytxtime/1000; + dailytxtime %= 1000; + + ast_cli(fd, "TX time today ...................................: %02d:%02d:%02d.%d\n", + hours, minutes, seconds, dailytxtime); + + hours = (int) totaltxtime/3600000; + totaltxtime %= 3600000; + minutes = (int) totaltxtime/60000; + totaltxtime %= 60000; + seconds = (int) totaltxtime/1000; + totaltxtime %= 1000; + + ast_cli(fd, "TX time since system initialization..............: %02d:%02d:%02d.%d\n", + hours, minutes, seconds, (int) totaltxtime); + ast_cli(fd, "Nodes currently connected to us..................: "); + for(j = 0 ;; j++){ + if(!listoflinks[j]){ + if(!j){ + ast_cli(fd,"<NONE>"); + } + break; + } + ast_cli(fd, "%s", listoflinks[j]); + if(j % 4 == 3){ + ast_cli(fd, "\n"); + ast_cli(fd, " : "); + } + else{ + if(listoflinks[j + 1]) + ast_cli(fd, ", "); + } + } + ast_cli(fd,"\n"); + + ast_cli(fd, "Last node which transmitted to us................: %s\n", lastnodewhichkeyedusup); + ast_cli(fd, "Autopatch........................................: %s\n", patch_ena); + ast_cli(fd, "Autopatch state..................................: %s\n", patch_state); + ast_cli(fd, "Autopatch called number..........................: %s\n", called_number); + ast_cli(fd, "Reverse patch/IAXRPT connected...................: %s\n", reverse_patch_state); + ast_cli(fd, "User linking commands............................: %s\n", link_ena); + ast_cli(fd, "User functions...................................: %s\n\n", user_funs); + return RESULT_SUCCESS; + } + } + return RESULT_FAILURE; +} + +/* +* Link stats function +*/ + +static int rpt_do_lstats(int fd, int argc, char *argv[]) +{ + int i,j; + char *connstate; + struct rpt *myrpt; + struct rpt_link *l; + struct rpt_lstat *s,*t; + struct rpt_lstat s_head; + if(argc != 3) + return RESULT_SHOWUSAGE; + + s = NULL; + s_head.next = &s_head; + s_head.prev = &s_head; + + for(i = 0; i < nrpts; i++) + { + if (!strcmp(argv[2],rpt_vars[i].name)){ + /* Make a copy of all stat variables while locked */ + myrpt = &rpt_vars[i]; + rpt_mutex_lock(&myrpt->lock); /* LOCK */ + /* Traverse the list of connected nodes */ + j = 0; + l = myrpt->links.next; + while(l && (l != &myrpt->links)){ + if (l->name[0] == '0'){ /* Skip '0' nodes */ + l = l->next; + continue; + } + if((s = (struct rpt_lstat *) malloc(sizeof(struct rpt_lstat))) == NULL){ + ast_log(LOG_ERROR, "Malloc failed in rpt_do_lstats\n"); + rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */ + return RESULT_FAILURE; + } + memset(s, 0, sizeof(struct rpt_lstat)); + strncpy(s->name, l->name, MAXREMSTR - 1); + if (l->chan) pbx_substitute_variables_helper(l->chan, "${IAXPEER(CURRENTCHANNEL)}", s->peer, MAXPEERSTR - 1); + else strcpy(s->peer,"(none)"); + s->mode = l->mode; + s->outbound = l->outbound; + s->reconnects = l->reconnects; + s->connecttime = l->connecttime; + s->thisconnected = l->thisconnected; + memcpy(s->chan_stat,l->chan_stat,NRPTSTAT * sizeof(struct rpt_chan_stat)); + insque((struct qelem *) s, (struct qelem *) s_head.next); + memset(l->chan_stat,0,NRPTSTAT * sizeof(struct rpt_chan_stat)); + l = l->next; + } + rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */ + ast_cli(fd, "NODE PEER RECONNECTS DIRECTION CONNECT TIME CONNECT STATE\n"); + ast_cli(fd, "---- ---- ---------- --------- ------------ -------------\n"); + + for(s = s_head.next; s != &s_head; s = s->next){ + int hours, minutes, seconds; + long long connecttime = s->connecttime; + char conntime[21]; + hours = (int) connecttime/3600000; + connecttime %= 3600000; + minutes = (int) connecttime/60000; + connecttime %= 60000; + seconds = (int) connecttime/1000; + connecttime %= 1000; + snprintf(conntime, 20, "%02d:%02d:%02d.%d", + hours, minutes, seconds, (int) connecttime); + conntime[20] = 0; + if(s->thisconnected) + connstate = "ESTABLISHED"; + else + connstate = "CONNECTING"; + ast_cli(fd, "%-10s%-20s%-12d%-11s%-20s%-20s\n", + s->name, s->peer, s->reconnects, (s->outbound)? "OUT":"IN", conntime, connstate); + } + /* destroy our local link queue */ + s = s_head.next; + while(s != &s_head){ + t = s; + s = s->next; + remque((struct qelem *)t); + free(t); + } + return RESULT_SUCCESS; + } + } + return RESULT_FAILURE; +} + +/* +* List all nodes connected, directly or indirectly +*/ + +static int rpt_do_nodes(int fd, int argc, char *argv[]) +{ + int i,j; + char ns; + char lbuf[MAXLINKLIST],*strs[MAXLINKLIST]; + struct rpt *myrpt; + if(argc != 3) + return RESULT_SHOWUSAGE; + + for(i = 0; i < nrpts; i++) + { + if (!strcmp(argv[2],rpt_vars[i].name)){ + /* Make a copy of all stat variables while locked */ + myrpt = &rpt_vars[i]; + rpt_mutex_lock(&myrpt->lock); /* LOCK */ + __mklinklist(myrpt,NULL,lbuf); + rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */ + /* parse em */ + ns = finddelim(lbuf,strs,MAXLINKLIST); + /* sort em */ + if (ns) qsort((void *)strs,ns,sizeof(char *),mycompar); + ast_cli(fd,"\n"); + ast_cli(fd, "************************* CONNECTED NODES *************************\n\n"); + for(j = 0 ;; j++){ + if(!strs[j]){ + if(!j){ + ast_cli(fd,"<NONE>"); + } + break; + } + ast_cli(fd, "%s", strs[j]); + if(j % 8 == 7){ + ast_cli(fd, "\n"); + } + else{ + if(strs[j + 1]) + ast_cli(fd, ", "); + } + } + ast_cli(fd,"\n\n"); + return RESULT_SUCCESS; + } + } + return RESULT_FAILURE; +} + +/* +* reload vars +*/ + +static int rpt_do_reload(int fd, int argc, char *argv[]) +{ +int n; + + if (argc > 2) return RESULT_SHOWUSAGE; + + for(n = 0; n < nrpts; n++) rpt_vars[n].reload = 1; + + return RESULT_FAILURE; +} + +/* +* restart app_rpt +*/ + +static int rpt_do_restart(int fd, int argc, char *argv[]) +{ +int i; + + if (argc > 2) return RESULT_SHOWUSAGE; + for(i = 0; i < nrpts; i++) + { + if (rpt_vars[i].rxchannel) ast_softhangup(rpt_vars[i].rxchannel,AST_SOFTHANGUP_DEV); + } + return RESULT_FAILURE; +} + + +/* +* send an app_rpt DTMF function from the CLI +*/ + +static int rpt_do_fun(int fd, int argc, char *argv[]) +{ + int i,busy=0; + + if (argc != 4) return RESULT_SHOWUSAGE; + + for(i = 0; i < nrpts; i++){ + if(!strcmp(argv[2], rpt_vars[i].name)){ + struct rpt *myrpt = &rpt_vars[i]; + rpt_mutex_lock(&myrpt->lock); + if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(argv[3])){ + rpt_mutex_unlock(&myrpt->lock); + busy=1; + } + if(!busy){ + myrpt->macrotimer = MACROTIME; + strncat(myrpt->macrobuf, argv[3], MAXMACRO - strlen(myrpt->macrobuf) - 1); + } + rpt_mutex_unlock(&myrpt->lock); + } + } + if(busy){ + ast_cli(fd, "Function decoder busy"); + } + return RESULT_FAILURE; +} + + + +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 = DAHDI_IOMUX_WRITEEMPTY | DAHDI_IOMUX_NOWAIT; + res = ioctl(chan->fds[0], DAHDI_IOMUX, &flags); + if(flags & DAHDI_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 = DAHDI_IOMUX_WRITEEMPTY | DAHDI_IOMUX_NOWAIT; + res = ioctl(chan->fds[0], DAHDI_IOMUX, &flags); + if(flags & DAHDI_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; +} + + +static int telem_any(struct rpt *myrpt,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(myrpt, mcat, "speed", 5, 20, 20); + morsefreq = retrieve_astcfgint(myrpt, mcat, "frequency", 300, 3000, 800); + morseampl = retrieve_astcfgint(myrpt, mcat, "amplitude", 200, 8192, 4096); + morseidampl = retrieve_astcfgint(myrpt, mcat, "idamplitude", 200, 8192, 2048); + morseidfreq = retrieve_astcfgint(myrpt, 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 rpt *myrpt,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 = (char *) ast_variable_retrieve(myrpt->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 = (char *) ast_variable_retrieve(myrpt->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){ + if(strlen(entry)) + telem_any(myrpt,chan, entry); + } + else{ + 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 = (char *) ast_variable_retrieve(myrpt->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(myrpt,wait_times_save, "telemwait", 500, 5000, 1000); + else + interval = 1000; + break; + + case DLY_ID: + if(wait_times) + interval = retrieve_astcfgint(myrpt,wait_times_save, "idwait",250,5000,500); + else + interval = 500; + break; + + case DLY_UNKEY: + if(wait_times) + interval = retrieve_astcfgint(myrpt,wait_times_save, "unkeywait",500,5000,1000); + else + interval = 1000; + break; + + case DLY_LINKUNKEY: + if(wait_times) + interval = retrieve_astcfgint(myrpt,wait_times_save, "linkunkeywait",500,5000,1000); + else + interval = 1000; + break; + + case DLY_CALLTERM: + if(wait_times) + interval = retrieve_astcfgint(myrpt,wait_times_save, "calltermwait",500,5000,1500); + else + interval = 1500; + break; + + case DLY_COMP: + if(wait_times) + interval = retrieve_astcfgint(myrpt,wait_times_save, "compwait",500,5000,200); + else + interval = 200; + 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; + interval = get_wait_interval(myrpt, type); + if(debug) + ast_log(LOG_NOTICE," Delay interval = %d\n", interval); + if(interval) + ast_safe_sleep(chan,interval); + if(debug) + ast_log(LOG_NOTICE,"Delay complete\n"); + return; +} + +static int split_freq(char *mhz, char *decimals, char *freq); + +static void *rpt_tele_thread(void *this) +{ +struct dahdi_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,*l1,linkbase; +struct ast_channel *mychannel; +int vmajor, vminor, m; +char *p,*ct,*ct_copy,*ident, *nodename,*cp; +time_t t; +struct tm localtm; +char lbuf[MAXLINKLIST],*strs[MAXLINKLIST]; +int i,ns,rbimode; +char mhz[MAXREMSTR]; +char decimals[MAXREMSTR]; +struct dahdi_params par; + + + /* get a pointer to myrpt */ + myrpt = mytele->rpt; + + /* Snag copies of a few key myrpt variables */ + rpt_mutex_lock(&myrpt->lock); + nodename = ast_strdupa(myrpt->name); + if (myrpt->p.ident) ident = ast_strdupa(myrpt->p.ident); + else ident = ""; + rpt_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"); + rpt_mutex_lock(&myrpt->lock); + remque((struct qelem *)mytele); + ast_log(LOG_NOTICE,"Telemetry thread aborted at line %d, mode: %d\n",__LINE__, mytele->mode); /*@@@@@@@@@@@*/ + rpt_mutex_unlock(&myrpt->lock); + free(mytele); + pthread_exit(NULL); + } +#ifdef AST_CDR_FLAG_POST_DISABLED + ast_set_flag(mychannel->cdr,AST_CDR_FLAG_POST_DISABLED); +#endif + rpt_mutex_lock(&myrpt->lock); + mytele->chan = mychannel; + rpt_mutex_unlock(&myrpt->lock); + /* make a conference for the tx */ + ci.chan = 0; + /* If there's an ID queued, or tail message 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) || + (mytele->mode == TAILMSG) || (mytele->mode == LINKUNKEY)) || (mytele->mode == TIMEOUT) ? + myrpt->txconf : myrpt->conf); + ci.confmode = DAHDI_CONF_CONFANN; + /* first put the channel on the conference in announce mode */ + if (ioctl(mychannel->fds[0],DAHDI_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + rpt_mutex_lock(&myrpt->lock); + remque((struct qelem *)mytele); + rpt_mutex_unlock(&myrpt->lock); + ast_log(LOG_NOTICE,"Telemetry thread aborted at line %d, mode: %d\n",__LINE__, mytele->mode); /*@@@@@@@@@@@*/ + 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(myrpt,mychannel, ident); + imdone=1; + break; + + case TAILMSG: + res = ast_streamfile(mychannel, myrpt->p.tailmessages[myrpt->tailmessagen], mychannel->language); + break; + + case IDTALKOVER: + p = (char *) ast_variable_retrieve(myrpt->cfg, nodename, "idtalkover"); + if(p) + res = telem_any(myrpt,mychannel, p); + imdone=1; + break; + + case PROC: + /* wait a little bit longer */ + wait_interval(myrpt, DLY_TELEM, mychannel); + res = telem_lookup(myrpt, mychannel, myrpt->name, "patchup"); + if(res < 0){ /* Then default message */ + res = ast_streamfile(mychannel, "rpt/callproceeding", mychannel->language); + } + break; + case TERM: + /* wait a little bit longer */ + wait_interval(myrpt, DLY_CALLTERM, mychannel); + res = telem_lookup(myrpt, mychannel, myrpt->name, "patchdown"); + if(res < 0){ /* Then default message */ + res = ast_streamfile(mychannel, "rpt/callterminated", mychannel->language); + } + break; + case COMPLETE: + /* wait a little bit */ + wait_interval(myrpt, DLY_TELEM, mychannel); + res = telem_lookup(myrpt,mychannel, myrpt->name, "functcomplete"); + break; + case MACRO_NOTFOUND: + /* wait a little bit */ + wait_interval(myrpt, DLY_TELEM, mychannel); + res = ast_streamfile(mychannel, "rpt/macro_notfound", mychannel->language); + break; + case MACRO_BUSY: + /* wait a little bit */ + wait_interval(myrpt, DLY_TELEM, mychannel); + res = ast_streamfile(mychannel, "rpt/macro_busy", mychannel->language); + break; + case UNKEY: + if(myrpt->patchnoct && myrpt->callmode){ /* If no CT during patch configured, then don't send one */ + imdone = 1; + break; + } + + /* + * Reset the Unkey to CT timer + */ + + x = get_wait_interval(myrpt, DLY_UNKEY); + rpt_mutex_lock(&myrpt->lock); + myrpt->unkeytocttimer = x; /* Must be protected as it is changed below */ + rpt_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) + { + rpt_mutex_lock(&myrpt->lock); + while(tlist != &myrpt->tele){ + if (tlist->mode == UNKEY) unkeys_queued++; + tlist = tlist->next; + } + rpt_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); + rpt_mutex_lock(&myrpt->lock); + if(myrpt->unkeytocttimer < ctint) + myrpt->unkeytocttimer = 0; + else + myrpt->unkeytocttimer -= ctint; + rpt_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; + } + + rpt_mutex_lock(&myrpt->lock); /* Update the kerchunk counters */ + myrpt->dailykerchunks++; + myrpt->totalkerchunks++; + rpt_mutex_unlock(&myrpt->lock); + + haslink = 0; + hastx = 0; + hasremote = 0; + l = myrpt->links.next; + if (l != &myrpt->links) + { + rpt_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; + } + rpt_mutex_unlock(&myrpt->lock); + } + if (haslink) + { + + res = telem_lookup(myrpt,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(myrpt,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 = (char *) ast_variable_retrieve(myrpt->cfg, nodename, "unlinkedct"))){ /* Unlinked Courtesy Tone */ + ct_copy = ast_strdupa(ct); + res = telem_lookup(myrpt,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 = DAHDI_CONF_CONFANN; + /* first put the channel on the conference in announce mode */ + if (ioctl(mychannel->fds[0],DAHDI_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + rpt_mutex_lock(&myrpt->lock); + remque((struct qelem *)mytele); + rpt_mutex_unlock(&myrpt->lock); + ast_log(LOG_NOTICE,"Telemetry thread aborted at line %d, mode: %d\n",__LINE__, mytele->mode); /*@@@@@@@@@@@*/ + free(mytele); + ast_hangup(mychannel); + pthread_exit(NULL); + } + if((ct = (char *) ast_variable_retrieve(myrpt->cfg, nodename, "remotect"))){ /* Unlinked Courtesy Tone */ + ast_safe_sleep(mychannel,200); + ct_copy = ast_strdupa(ct); + res = telem_lookup(myrpt,mychannel, myrpt->name, ct_copy); + if(res) + ast_log(LOG_WARNING, "telem_lookup:ctx failed on %s\n", mychannel->name); + } + } +#if defined(_MDC_DECODE_H_) && defined(MDC_SAY_WHEN_DOING_CT) + if (myrpt->lastunit) + { + char mystr[10]; + + ast_safe_sleep(mychannel,200); + /* set for all to hear */ + ci.chan = 0; + ci.confno = myrpt->txconf; + ci.confmode = DAHDI_CONF_CONFANN; + /* first put the channel on the conference in announce mode */ + if (ioctl(mychannel->fds[0],DAHDI_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + rpt_mutex_lock(&myrpt->lock); + remque((struct qelem *)mytele); + rpt_mutex_unlock(&myrpt->lock); + ast_log(LOG_NOTICE,"Telemetry thread aborted at line %d, mode: %d\n",__LINE__, mytele->mode); /*@@@@@@@@@@@*/ + free(mytele); + ast_hangup(mychannel); + pthread_exit(NULL); + } + sprintf(mystr,"%04x",myrpt->lastunit); + myrpt->lastunit = 0; + ast_say_character_str(mychannel,mystr,NULL,mychannel->language); + break; + } +#endif + imdone = 1; + break; + case LINKUNKEY: + if(myrpt->patchnoct && myrpt->callmode){ /* If no CT during patch configured, then don't send one */ + imdone = 1; + break; + } + + /* + * Reset the Unkey to CT timer + */ + + x = get_wait_interval(myrpt, DLY_LINKUNKEY); + mytele->mylink.linkunkeytocttimer = x; /* Must be protected as it is changed below */ + + /* + * If there's one already queued, don't do another + */ + + tlist = myrpt->tele.next; + unkeys_queued = 0; + if (tlist != &myrpt->tele) + { + rpt_mutex_lock(&myrpt->lock); + while(tlist != &myrpt->tele){ + if (tlist->mode == LINKUNKEY) unkeys_queued++; + tlist = tlist->next; + } + rpt_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(mytele->mylink.linkunkeytocttimer) + { + int ctint; + if(mytele->mylink.linkunkeytocttimer > 100) + ctint = 100; + else + ctint = mytele->mylink.linkunkeytocttimer; + ast_safe_sleep(mychannel, ctint); + rpt_mutex_lock(&myrpt->lock); + if(mytele->mylink.linkunkeytocttimer < ctint) + mytele->mylink.linkunkeytocttimer = 0; + else + mytele->mylink.linkunkeytocttimer -= ctint; + rpt_mutex_unlock(&myrpt->lock); + } + + if((ct = (char *) ast_variable_retrieve(myrpt->cfg, nodename, "linkunkeyct"))){ /* Unlinked Courtesy Tone */ + ct_copy = ast_strdupa(ct); + res = telem_lookup(myrpt,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); + l = myrpt->links.next; + haslink = 0; + /* dont report if a link for this one still on system */ + if (l != &myrpt->links) + { + rpt_mutex_lock(&myrpt->lock); + while(l != &myrpt->links) + { + if (l->name[0] == '0') + { + l = l->next; + continue; + } + if (!strcmp(l->name,mytele->mylink.name)) + { + haslink = 1; + break; + } + l = l->next; + } + rpt_mutex_unlock(&myrpt->lock); + } + if (haslink) + { + imdone = 1; + break; + } + 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.hasconnected) ? + "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); + 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, "digits/2", 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, "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); + imdone = 1; + 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 MEMNOTFOUND: + /* wait a little bit */ + wait_interval(myrpt, DLY_TELEM, mychannel); + res = ast_streamfile(mychannel, "rpt/memory_notfound", mychannel->language); + break; + case SETREMOTE: + ast_mutex_lock(&myrpt->remlock); + res = 0; + if(!strcmp(myrpt->remote, remote_rig_ft897)) + { + res = set_ft897(myrpt); + } + if(!strcmp(myrpt->remote, remote_rig_ic706)) + { + res = set_ic706(myrpt); + } +#ifdef HAVE_IOPERM + else if(!strcmp(myrpt->remote, remote_rig_rbi)) + { + if (ioperm(myrpt->p.iobase,1,1) == -1) + { + rpt_mutex_unlock(&myrpt->lock); + ast_log(LOG_WARNING, "Cant get io permission on IO port %x hex\n",myrpt->p.iobase); + res = -1; + } + else res = setrbi(myrpt); + } +#endif + else if(!strcmp(myrpt->remote, remote_rig_kenwood)) + { + res = setkenwood(myrpt); + if (ast_safe_sleep(mychannel,200) == -1) + { + ast_mutex_unlock(&myrpt->remlock); + res = -1; + break; + } + i = DAHDI_FLUSH_EVENT; + if (ioctl(myrpt->zaptxchannel->fds[0],DAHDI_FLUSH,&i) == -1) + { + ast_mutex_unlock(&myrpt->remlock); + ast_log(LOG_ERROR,"Cant flush events"); + res = -1; + break; + } + if (ioctl(myrpt->zaprxchannel->fds[0],DAHDI_GET_PARAMS,&par) == -1) + { + ast_mutex_unlock(&myrpt->remlock); + ast_log(LOG_ERROR,"Cant get params"); + res = -1; + break; + } + myrpt->remoterx = + (par.rxisoffhook || (myrpt->tele.next != &myrpt->tele)); + } + ast_mutex_unlock(&myrpt->remlock); + if (!res) + { + imdone = 1; + break; + } + /* fall thru to invalid freq */ + case INVFREQ: + /* wait a little bit */ + wait_interval(myrpt, DLY_TELEM, mychannel); + res = ast_streamfile(mychannel, "rpt/invalid-freq", mychannel->language); + break; + case REMMODE: + cp = 0; + wait_interval(myrpt, DLY_TELEM, mychannel); + switch(myrpt->remmode) + { + case REM_MODE_FM: + saycharstr(mychannel,"FM"); + break; + case REM_MODE_USB: + saycharstr(mychannel,"USB"); + break; + case REM_MODE_LSB: + saycharstr(mychannel,"LSB"); + break; + case REM_MODE_AM: + saycharstr(mychannel,"AM"); + break; + } + wait_interval(myrpt, DLY_COMP, mychannel); + if (!res) res = telem_lookup(myrpt,mychannel, myrpt->name, "functcomplete"); + break; + case LOGINREQ: + wait_interval(myrpt, DLY_TELEM, mychannel); + sayfile(mychannel,"rpt/login"); + saycharstr(mychannel,myrpt->name); + break; + case REMLOGIN: + wait_interval(myrpt, DLY_TELEM, mychannel); + saycharstr(mychannel,myrpt->loginuser); + sayfile(mychannel,"rpt/node"); + saycharstr(mychannel,myrpt->name); + wait_interval(myrpt, DLY_COMP, mychannel); + if (!res) res = telem_lookup(myrpt,mychannel, myrpt->name, "functcomplete"); + break; + case REMXXX: + wait_interval(myrpt, DLY_TELEM, mychannel); + res = 0; + switch(mytele->submode) + { + case 100: /* RX PL Off */ + sayfile(mychannel, "rpt/rxpl"); + sayfile(mychannel, "rpt/off"); + break; + case 101: /* RX PL On */ + sayfile(mychannel, "rpt/rxpl"); + sayfile(mychannel, "rpt/on"); + break; + case 102: /* TX PL Off */ + sayfile(mychannel, "rpt/txpl"); + sayfile(mychannel, "rpt/off"); + break; + case 103: /* TX PL On */ + sayfile(mychannel, "rpt/txpl"); + sayfile(mychannel, "rpt/on"); + break; + case 104: /* Low Power */ + sayfile(mychannel, "rpt/lopwr"); + break; + case 105: /* Medium Power */ + sayfile(mychannel, "rpt/medpwr"); + break; + case 106: /* Hi Power */ + sayfile(mychannel, "rpt/hipwr"); + break; + case 113: /* Scan down slow */ + sayfile(mychannel,"rpt/down"); + sayfile(mychannel, "rpt/slow"); + break; + case 114: /* Scan down quick */ + sayfile(mychannel,"rpt/down"); + sayfile(mychannel, "rpt/quick"); + break; + case 115: /* Scan down fast */ + sayfile(mychannel,"rpt/down"); + sayfile(mychannel, "rpt/fast"); + break; + case 116: /* Scan up slow */ + sayfile(mychannel,"rpt/up"); + sayfile(mychannel, "rpt/slow"); + break; + case 117: /* Scan up quick */ + sayfile(mychannel,"rpt/up"); + sayfile(mychannel, "rpt/quick"); + break; + case 118: /* Scan up fast */ + sayfile(mychannel,"rpt/up"); + sayfile(mychannel, "rpt/fast"); + break; + default: + res = -1; + } + wait_interval(myrpt, DLY_COMP, mychannel); + if (!res) res = telem_lookup(myrpt,mychannel, myrpt->name, "functcomplete"); + break; + case SCAN: + ast_mutex_lock(&myrpt->remlock); + if (myrpt->hfscanstop) + { + myrpt->hfscanstatus = 0; + myrpt->hfscanmode = 0; + myrpt->hfscanstop = 0; + mytele->mode = SCANSTAT; + ast_mutex_unlock(&myrpt->remlock); + if (ast_safe_sleep(mychannel,1000) == -1) break; + sayfile(mychannel, "rpt/stop"); + imdone = 1; + break; + } + if (myrpt->hfscanstatus > -2) service_scan(myrpt); + i = myrpt->hfscanstatus; + myrpt->hfscanstatus = 0; + if (i) mytele->mode = SCANSTAT; + ast_mutex_unlock(&myrpt->remlock); + if (i < 0) sayfile(mychannel, "rpt/stop"); + else if (i > 0) saynum(mychannel,i); + imdone = 1; + break; + case TUNE: + ast_mutex_lock(&myrpt->remlock); + if (!strcmp(myrpt->remote,remote_rig_ic706)) + { + set_mode_ic706(myrpt, REM_MODE_AM); + if(play_tone(mychannel, 800, 6000, 8192) == -1) break; + ast_safe_sleep(mychannel,500); + set_mode_ic706(myrpt, myrpt->remmode); + myrpt->tunerequest = 0; + ast_mutex_unlock(&myrpt->remlock); + imdone = 1; + break; + } + set_mode_ft897(myrpt, REM_MODE_AM); + simple_command_ft897(myrpt, 8); + if(play_tone(mychannel, 800, 6000, 8192) == -1) break; + simple_command_ft897(myrpt, 0x88); + ast_safe_sleep(mychannel,500); + set_mode_ft897(myrpt, myrpt->remmode); + myrpt->tunerequest = 0; + ast_mutex_unlock(&myrpt->remlock); + imdone = 1; + break; + case REMSHORTSTATUS: + case REMLONGSTATUS: + wait_interval(myrpt, DLY_TELEM, mychannel); + 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 (!multimode_capable(myrpt)) decimals[3] = 0; + 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) break; + 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: + break; + } + } + 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: + break; + } + } + + if (res == -1) break; + + if(mytele->mode == REMSHORTSTATUS){ /* Short status? */ + wait_interval(myrpt, DLY_COMP, mychannel); + if (!res) res = telem_lookup(myrpt,mychannel, myrpt->name, "functcomplete"); + break; + } + + if (strcmp(myrpt->remote,remote_rig_ic706)) + { + 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; + } + } + + rbimode = ((!strncmp(myrpt->remote,remote_rig_rbi,3)) + || (!strncmp(myrpt->remote,remote_rig_ic706,3))); + if (res || (sayfile(mychannel,"rpt/rxpl") == -1)) break; + if (rbimode && (sayfile(mychannel,"rpt/txpl") == -1)) break; + if ((sayfile(mychannel,"rpt/frequency") == -1) || + (saycharstr(mychannel,myrpt->rxpl) == -1)) break; + if ((!rbimode) && ((sayfile(mychannel,"rpt/txpl") == -1) || + (sayfile(mychannel,"rpt/frequency") == -1) || + (saycharstr(mychannel,myrpt->txpl) == -1))) break; + if(myrpt->remmode == REM_MODE_FM){ /* Mode FM? */ + if ((sayfile(mychannel,"rpt/rxpl") == -1) || + (sayfile(mychannel,((myrpt->rxplon) ? "rpt/on" : "rpt/off")) == -1) || + (sayfile(mychannel,"rpt/txpl") == -1) || + (sayfile(mychannel,((myrpt->txplon) ? "rpt/on" : "rpt/off")) == -1)) + { + break; + } + } + wait_interval(myrpt, DLY_COMP, mychannel); + if (!res) res = telem_lookup(myrpt,mychannel, myrpt->name, "functcomplete"); + break; + case STATUS: + /* wait a little bit */ + wait_interval(myrpt, DLY_TELEM, mychannel); + hastx = 0; + linkbase.next = &linkbase; + linkbase.prev = &linkbase; + rpt_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; + } + l1 = malloc(sizeof(struct rpt_link)); + if (!l1) + { + ast_log(LOG_WARNING, "Cannot alloc memory on %s\n", mychannel->name); + remque((struct qelem *)mytele); + rpt_mutex_unlock(&myrpt->lock); + ast_log(LOG_NOTICE,"Telemetry thread aborted at line %d, mode: %d\n",__LINE__, mytele->mode); /*@@@@@@@@@@@*/ + free(mytele); + ast_hangup(mychannel); + pthread_exit(NULL); + } + memcpy(l1,l,sizeof(struct rpt_link)); + l1->next = l1->prev = NULL; + insque((struct qelem *)l1,(struct qelem *)linkbase.next); + l = l->next; + } + rpt_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) + { + char *s; + + 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); + s = "rpt/tranceive"; + if (!l->mode) s = "rpt/monitor"; + if (!l->thisconnected) s = "rpt/connecting"; + res = ast_streamfile(mychannel, s, 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) + { + l1 = l; + l = l->next; + remque((struct qelem *)l1); + free(l1); + } + imdone = 1; + break; + case FULLSTATUS: + rpt_mutex_lock(&myrpt->lock); + /* get all the nodes */ + __mklinklist(myrpt,NULL,lbuf); + rpt_mutex_unlock(&myrpt->lock); + /* parse em */ + ns = finddelim(lbuf,strs,MAXLINKLIST); + /* sort em */ + if (ns) qsort((void *)strs,ns,sizeof(char *),mycompar); + /* wait a little bit */ + wait_interval(myrpt, DLY_TELEM, mychannel); + hastx = 0; + 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); + } + /* go thru all the nodes in list */ + for(i = 0; i < ns; i++) + { + char *s,mode = 'T'; + + /* if a mode spec at first, handle it */ + if ((*strs[i] < '0') || (*strs[i] > '9')) + { + mode = *strs[i]; + strs[i]++; + } + + 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,strs[i],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); + s = "rpt/tranceive"; + if (mode == 'R') s = "rpt/monitor"; + if (mode == 'C') s = "rpt/connecting"; + res = ast_streamfile(mychannel, s, 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 (!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); + } + imdone = 1; + break; + + case LASTNODEKEY: /* Identify last node which keyed us up */ + rpt_mutex_lock(&myrpt->lock); + if(myrpt->lastnodewhichkeyedusup) + p = ast_strdupa(myrpt->lastnodewhichkeyedusup); /* Make a local copy of the node name */ + else + p = NULL; + rpt_mutex_unlock(&myrpt->lock); + if(!p){ + imdone = 1; /* no node previously keyed us up, or the node which did has been disconnected */ + break; + } + 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, p, 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); + imdone = 1; + break; + + case UNAUTHTX: /* Say unauthorized transmit frequency */ + wait_interval(myrpt, DLY_TELEM, mychannel); + res = ast_streamfile(mychannel, "rpt/unauthtx", mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + 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 TIMEOUT_WARNING: + time(&t); + 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-warning", 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(!res) /* Say number of seconds */ + ast_say_number(mychannel, myrpt->p.remotetimeout - + (t - myrpt->last_activity_time), + "", mychannel->language, (char *) NULL); + if (!res) + res = ast_waitstream(mychannel, ""); + ast_stopstream(mychannel); + res = ast_streamfile(mychannel, "queue-seconds", mychannel->language); + break; + + case ACT_TIMEOUT_WARNING: + time(&t); + 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/act-timeout-warning", 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(!res) /* Say number of seconds */ + ast_say_number(mychannel, myrpt->p.remoteinacttimeout - + (t - myrpt->last_activity_time), + "", mychannel->language, (char *) NULL); + if (!res) + res = ast_waitstream(mychannel, ""); + ast_stopstream(mychannel); + res = ast_streamfile(mychannel, "queue-seconds", mychannel->language); + break; + + case STATS_TIME: + wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */ + t = time(NULL); + rpt_localtime(&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; + if (myrpt->stopgen) break; + myrpt->stopgen = -1; + if ((res = ast_tonepair_start(mychannel, 1004.0, 0, 99999999, 7200.0))) + { + myrpt->stopgen = 0; + break; + } + while(mychannel->generatordata && (myrpt->stopgen <= 0)) { + if (ast_safe_sleep(mychannel,1)) break; + imdone = 1; + } + myrpt->stopgen = 0; + break; + default: + break; + } + 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); + rpt_mutex_lock(&myrpt->lock); + if (mytele->mode == TAILMSG) + { + if (!res) + { + myrpt->tailmessagen++; + if(myrpt->tailmessagen >= myrpt->p.tailmessagemax) myrpt->tailmessagen = 0; + } + else + { + myrpt->tmsgtimer = myrpt->p.tailsquashedtime; + } + } + remque((struct qelem *)mytele); + rpt_mutex_unlock(&myrpt->lock); + free(mytele); + ast_hangup(mychannel); +#ifdef APP_RPT_LOCK_DEBUG + { + struct lockthread *t; + + sleep(5); + ast_mutex_lock(&locklock); + t = get_lockthread(pthread_self()); + if (t) memset(t,0,sizeof(struct lockthread)); + ast_mutex_unlock(&locklock); + } +#endif + 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; +int res; +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; + rpt_mutex_lock(&myrpt->lock); + if((mode == CONNFAIL) || (mode == REMDISC) || (mode == CONNECTED) || + (mode == LINKUNKEY)){ + 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; + } + if (mode == REMXXX) tele->submode = (int) data; + insque((struct qelem *)tele, (struct qelem *)myrpt->tele.next); + rpt_mutex_unlock(&myrpt->lock); + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + res = ast_pthread_create(&tele->threadid,&attr,rpt_tele_thread,(void *) tele); + if(res < 0){ + rpt_mutex_lock(&myrpt->lock); + remque((struct qlem *) tele); /* We don't like stuck transmitters, remove it from the queue */ + rpt_mutex_unlock(&myrpt->lock); + ast_log(LOG_WARNING, "Could not create telemetry thread: %s",strerror(res)); + } + return; +} + +static void *rpt_call(void *this) +{ +struct dahdi_confinfo ci; /* conference info */ +struct rpt *myrpt = (struct rpt *)this; +int res; +int stopped,congstarted,dialtimer,lastcidx,aborted; +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); + } +#ifdef AST_CDR_FLAG_POST_DISABLED + ast_set_flag(mychannel->cdr,AST_CDR_FLAG_POST_DISABLED); +#endif + ci.chan = 0; + ci.confno = myrpt->conf; /* use the pseudo conference */ + ci.confmode = DAHDI_CONF_REALANDPSEUDO | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER + | DAHDI_CONF_PSEUDO_TALKER | DAHDI_CONF_PSEUDO_LISTENER; + /* first put the channel on the conference */ + if (ioctl(mychannel->fds[0],DAHDI_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); + } +#ifdef AST_CDR_FLAG_POST_DISABLED + ast_set_flag(genchannel->cdr,AST_CDR_FLAG_POST_DISABLED); +#endif + ci.chan = 0; + ci.confno = myrpt->conf; + ci.confmode = DAHDI_CONF_REALANDPSEUDO | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER + | DAHDI_CONF_PSEUDO_TALKER | DAHDI_CONF_PSEUDO_LISTENER; + /* first put the channel on the conference */ + if (ioctl(genchannel->fds[0],DAHDI_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->p.tonezone && (tone_zone_set_zone(mychannel->fds[0],myrpt->p.tonezone) == -1)) + { + ast_log(LOG_WARNING, "Unable to set tone zone %s\n",myrpt->p.tonezone); + ast_hangup(mychannel); + ast_hangup(genchannel); + myrpt->callmode = 0; + pthread_exit(NULL); + } + if (myrpt->p.tonezone && (tone_zone_set_zone(genchannel->fds[0],myrpt->p.tonezone) == -1)) + { + ast_log(LOG_WARNING, "Unable to set tone zone %s\n",myrpt->p.tonezone); + ast_hangup(mychannel); + ast_hangup(genchannel); + myrpt->callmode = 0; + pthread_exit(NULL); + } + /* start dialtone if patchquiet is 0. Special patch modes don't send dial tone */ + if ((!myrpt->patchquiet) && (tone_zone_play_tone(mychannel->fds[0],DAHDI_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; + dialtimer = 0; + lastcidx = 0; + aborted = 0; + + + while ((myrpt->callmode == 1) || (myrpt->callmode == 4)) + { + + if((myrpt->patchdialtime)&&(myrpt->callmode == 1)&&(myrpt->cidx != lastcidx)){ + dialtimer = 0; + lastcidx = myrpt->cidx; + } + + if((myrpt->patchdialtime)&&(dialtimer >= myrpt->patchdialtime)){ + rpt_mutex_lock(&myrpt->lock); + aborted = 1; + myrpt->callmode = 0; + rpt_mutex_unlock(&myrpt->lock); + break; + } + + if ((!myrpt->patchquiet) && (!stopped) && (myrpt->callmode == 1) && (myrpt->cidx > 0)) + { + stopped = 1; + /* stop dial tone */ + tone_zone_play_tone(mychannel->fds[0],-1); + } + if (myrpt->callmode == 4) + { + if(!congstarted){ + congstarted = 1; + /* start congestion tone */ + tone_zone_play_tone(mychannel->fds[0],DAHDI_TONE_CONGESTION); + } + } + res = ast_safe_sleep(mychannel, MSWAIT); + if (res < 0) + { + ast_hangup(mychannel); + ast_hangup(genchannel); + rpt_mutex_lock(&myrpt->lock); + myrpt->callmode = 0; + rpt_mutex_unlock(&myrpt->lock); + pthread_exit(NULL); + } + dialtimer += MSWAIT; + } + /* stop any tone generation */ + tone_zone_play_tone(mychannel->fds[0],-1); + /* end if done */ + if (!myrpt->callmode) + { + ast_hangup(mychannel); + ast_hangup(genchannel); + rpt_mutex_lock(&myrpt->lock); + myrpt->callmode = 0; + rpt_mutex_unlock(&myrpt->lock); + if((!myrpt->patchquiet) && aborted) + rpt_telemetry(myrpt, TERM, NULL); + pthread_exit(NULL); + } + + if (myrpt->p.ourcallerid && *myrpt->p.ourcallerid){ + char *name, *loc, *instr; + instr = strdup(myrpt->p.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); + } + } + + ast_copy_string(mychannel->exten, myrpt->exten, sizeof(mychannel->exten) - 1); + ast_copy_string(mychannel->context, myrpt->patchcontext, sizeof(mychannel->context) - 1); + + if (myrpt->p.acctcode) + ast_cdr_setaccount(mychannel,myrpt->p.acctcode); + 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); + rpt_mutex_lock(&myrpt->lock); + myrpt->callmode = 0; + rpt_mutex_unlock(&myrpt->lock); + pthread_exit(NULL); + } + usleep(10000); + rpt_mutex_lock(&myrpt->lock); + myrpt->callmode = 3; + /* set appropriate conference for the pseudo */ + ci.chan = 0; + ci.confno = myrpt->conf; + ci.confmode = (myrpt->p.duplex == 2) ? DAHDI_CONF_CONFANNMON : + (DAHDI_CONF_CONF | DAHDI_CONF_LISTENER | DAHDI_CONF_TALKER); + /* first put the channel on the conference in announce mode */ + if (ioctl(myrpt->pchannel->fds[0],DAHDI_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); + } + while(myrpt->callmode) + { + if ((!mychannel->pbx) && (myrpt->callmode != 4)) + { + if(myrpt->patchfarenddisconnect){ /* If patch is setup for far end disconnect */ + myrpt->callmode = 0; + if(!myrpt->patchquiet){ + rpt_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt, TERM, NULL); + rpt_mutex_lock(&myrpt->lock); + } + } + else{ /* Send congestion until patch is downed by command */ + myrpt->callmode = 4; + rpt_mutex_unlock(&myrpt->lock); + /* start congestion tone */ + tone_zone_play_tone(genchannel->fds[0],DAHDI_TONE_CONGESTION); + rpt_mutex_lock(&myrpt->lock); + } + } + if (myrpt->mydtmf) + { + struct ast_frame wf = {AST_FRAME_DTMF, } ; + wf.subclass = myrpt->mydtmf; + rpt_mutex_unlock(&myrpt->lock); + ast_queue_frame(mychannel,&wf); + ast_senddigit(genchannel,myrpt->mydtmf); + rpt_mutex_lock(&myrpt->lock); + myrpt->mydtmf = 0; + } + rpt_mutex_unlock(&myrpt->lock); + usleep(MSWAIT * 1000); + rpt_mutex_lock(&myrpt->lock); + } + rpt_mutex_unlock(&myrpt->lock); + tone_zone_play_tone(genchannel->fds[0],-1); + if (mychannel->pbx) ast_softhangup(mychannel,AST_SOFTHANGUP_DEV); + ast_hangup(genchannel); + rpt_mutex_lock(&myrpt->lock); + myrpt->callmode = 0; + rpt_mutex_unlock(&myrpt->lock); + /* set appropriate conference for the pseudo */ + ci.chan = 0; + ci.confno = myrpt->conf; + ci.confmode = ((myrpt->p.duplex == 2) || (myrpt->p.duplex == 4)) ? DAHDI_CONF_CONFANNMON : + (DAHDI_CONF_CONF | DAHDI_CONF_LISTENER | DAHDI_CONF_TALKER); + /* first put the channel on the conference in announce mode */ + if (ioctl(myrpt->pchannel->fds[0],DAHDI_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + } + 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 = 0; + 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 = 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 = str; + if (l->chan) ast_write(l->chan,&wf); + l = l->next; + } + return; +} + +/* + * Connect a link + * + * Return values: + * -1: Error + * 0: Success + * 1: No match yet + * 2: Already connected to this node + */ + +static int connect_link(struct rpt *myrpt, char* node, int mode, int perma) +{ + char *val, *s, *s1, *s2, *tele; + char lstr[MAXLINKLIST],*strs[MAXLINKLIST]; + char tmp[300], deststr[300] = "",modechange = 0; + struct rpt_link *l; + int reconnects = 0; + int i,n; + struct dahdi_confinfo ci; /* conference info */ + + val = node_lookup(myrpt,node); + if (!val){ + if(strlen(node) >= myrpt->longestnode) + return -1; /* No such node */ + return 1; /* No match yet */ + } + if(debug > 3){ + ast_log(LOG_NOTICE,"Connect attempt to node %s\n", node); + ast_log(LOG_NOTICE,"Mode: %s\n",(mode)?"Transceive":"Monitor"); + ast_log(LOG_NOTICE,"Connection type: %s\n",(perma)?"Permalink":"Normal"); + } + + strncpy(tmp,val,sizeof(tmp) - 1); + s = tmp; + s1 = strsep(&s,","); + s2 = strsep(&s,","); + rpt_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, node)) + break; + l = l->next; + } + /* if found */ + if (l != &myrpt->links){ + /* if already in this mode, just ignore */ + if ((l->mode) || (!l->chan)) { + rpt_mutex_unlock(&myrpt->lock); + return 2; /* Already linked */ + } + reconnects = l->reconnects; + rpt_mutex_unlock(&myrpt->lock); + if (l->chan) ast_softhangup(l->chan, AST_SOFTHANGUP_DEV); + l->retries = l->max_retries + 1; + l->disced = 2; + modechange = 1; + } else + { + __mklinklist(myrpt,NULL,lstr); + rpt_mutex_unlock(&myrpt->lock); + n = finddelim(lstr,strs,MAXLINKLIST); + for(i = 0; i < n; i++) + { + if ((*strs[i] < '0') || + (*strs[i] > '9')) strs[i]++; + if (!strcmp(strs[i],node)) + { + return 2; /* Already linked */ + } + } + } + strncpy(myrpt->lastlinknode,node,MAXNODESTR - 1); + /* establish call */ + l = malloc(sizeof(struct rpt_link)); + if (!l) + { + ast_log(LOG_WARNING, "Unable to malloc\n"); + return -1; + } + /* zero the silly thing */ + memset((char *)l,0,sizeof(struct rpt_link)); + l->mode = mode; + l->outbound = 1; + l->thisconnected = 0; + strncpy(l->name, node, MAXNODESTR - 1); + l->isremote = (s && ast_true(s)); + if (modechange) l->connected = 1; + l->hasconnected = l->perma = perma; +#ifdef ALLOW_LOCAL_CHANNELS + if ((strncasecmp(s1,"iax2/", 5) == 0) || (strncasecmp(s1, "local/", 6) == 0)) + strncpy(deststr, s1, sizeof(deststr)); + else + snprintf(deststr, sizeof(deststr), "IAX2/%s", s1); +#else + snprintf(deststr, sizeof(deststr), "IAX2/%s", s1); +#endif + tele = strchr(deststr, '/'); + if (!tele){ + ast_log(LOG_WARNING,"link3:Dial number (%s) must be in format tech/number\n",deststr); + free(l); + return -1; + } + *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); +#ifdef AST_CDR_FLAG_POST_DISABLED + ast_set_flag(l->chan->cdr,AST_CDR_FLAG_POST_DISABLED); +#endif + l->chan->whentohangup = 0; + l->chan->appl = "Apprpt"; + l->chan->data = "(Remote Rx)"; + if (debug > 3) + ast_log(LOG_NOTICE, "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 { + if(debug > 3) + ast_log(LOG_NOTICE, "Unable to place call to %s/%s on %s\n", + deststr,tele,l->chan->name); + if (myrpt->p.archivedir) + { + char str[100]; + sprintf(str,"LINKFAIL,%s",l->name); + donodelog(myrpt,str); + } + free(l); + return -1; + } + /* allocate a pseudo-channel thru asterisk */ + l->pchan = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL); + if (!l->pchan){ + ast_log(LOG_WARNING,"rpt connect: Sorry unable to obtain pseudo channel\n"); + ast_hangup(l->chan); + free(l); + return -1; + } + ast_set_read_format(l->pchan, AST_FORMAT_SLINEAR); + ast_set_write_format(l->pchan, AST_FORMAT_SLINEAR); +#ifdef AST_CDR_FLAG_POST_DISABLED + ast_set_flag(l->pchan->cdr,AST_CDR_FLAG_POST_DISABLED); +#endif + /* make a conference for the tx */ + ci.chan = 0; + ci.confno = myrpt->conf; + ci.confmode = DAHDI_CONF_CONF | DAHDI_CONF_LISTENER | DAHDI_CONF_TALKER; + /* first put the channel on the conference in proper mode */ + if (ioctl(l->pchan->fds[0], DAHDI_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 -1; + } + rpt_mutex_lock(&myrpt->lock); + l->reconnects = reconnects; + /* insert at end of queue */ + l->max_retries = MAX_RETRIES; + if (perma) + l->max_retries = MAX_RETRIES_PERM; + if (l->isremote) l->retries = l->max_retries + 1; + insque((struct qelem *)l,(struct qelem *)myrpt->links.next); + __kickshort(myrpt); + rpt_mutex_unlock(&myrpt->lock); + return 0; +} + + + +/* +* 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; + char tmp[300]; + char digitbuf[MAXNODESTR],*strs[MAXLINKLIST]; + char mode,perma; + struct rpt_link *l; + int i,r; + + if(!param) + return DC_ERROR; + + + if (myrpt->p.s[myrpt->p.sysstate_cur].txdisable || myrpt->p.s[myrpt->p.sysstate_cur].linkfundisable ) + return DC_ERROR; + + strncpy(digitbuf,digits,MAXNODESTR - 1); + + if(debug > 6) + printf("@@@@ ilink param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf); + + switch(myatoi(param)){ + case 11: /* Perm Link off */ + case 1: /* Link off */ + if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0])) + strcpy(digitbuf,myrpt->lastlinknode); + val = node_lookup(myrpt,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,","); + rpt_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; + + /* must use perm command on perm link */ + if ((myatoi(param) < 10) && + (l->max_retries > MAX_RETRIES)) + { + rpt_mutex_unlock(&myrpt->lock); + return DC_COMPLETE; + } + strncpy(myrpt->lastlinknode,digitbuf,MAXNODESTR - 1); + l->retries = l->max_retries + 1; + l->disced = 1; + rpt_mutex_unlock(&myrpt->lock); + wf.frametype = AST_FRAME_TEXT; + wf.subclass = 0; + wf.offset = 0; + wf.mallocd = 0; + wf.datalen = strlen(discstr) + 1; + wf.samples = 0; + wf.data = 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; + } + rpt_mutex_unlock(&myrpt->lock); + return DC_COMPLETE; + case 2: /* Link Monitor */ + case 3: /* Link transceive */ + case 12: /* Link Monitor permanent */ + case 13: /* Link transceive permanent */ + if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0])) + strcpy(digitbuf,myrpt->lastlinknode); + /* Attempt connection */ + perma = (atoi(param) > 10) ? 1 : 0; + mode = (atoi(param) & 1) ? 1 : 0; + r = connect_link(myrpt, digitbuf, mode, perma); + switch(r){ + case 0: + rpt_telemetry(myrpt, COMPLETE, NULL); + return DC_COMPLETE; + + case 1: + break; + + case 2: + rpt_telemetry(myrpt, REMALREADY, NULL); + return DC_COMPLETE; + + default: + rpt_telemetry(myrpt, CONNFAIL, NULL); + return DC_COMPLETE; + } + break; + + 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 = node_lookup(myrpt,digitbuf); + if (!val){ + if(strlen(digitbuf) >= myrpt->longestnode) + return DC_ERROR; + break; + + } + rpt_mutex_lock(&myrpt->lock); + strcpy(myrpt->lastlinknode,digitbuf); + strncpy(myrpt->cmdnode, digitbuf, sizeof(myrpt->cmdnode) - 1); + rpt_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt, REMGO, NULL); + return DC_COMPLETE; + + case 5: /* Status */ + rpt_telemetry(myrpt, STATUS, NULL); + return DC_COMPLETE; + + case 15: /* Full Status */ + rpt_telemetry(myrpt, FULLSTATUS, NULL); + return DC_COMPLETE; + + + case 6: /* All Links Off, including permalinks */ + rpt_mutex_lock(&myrpt->lock); + myrpt->savednodes[0] = 0; + l = myrpt->links.next; + /* loop through all links */ + while(l != &myrpt->links){ + struct ast_frame wf; + if (l->name[0] == '0') /* Skip any IAXRPT monitoring */ + { + l = l->next; + continue; + } + /* Make a string of disconnected nodes for possible restoration */ + sprintf(tmp,"%c%c%s",(l->mode) ? 'X' : 'M',(l->perma) ? 'P':'T',l->name); + if(strlen(tmp) + strlen(myrpt->savednodes) + 1 < MAXNODESTR){ + if(myrpt->savednodes[0]) + strcat(myrpt->savednodes, ","); + strcat(myrpt->savednodes, tmp); + } + l->retries = l->max_retries + 1; + l->disced = 2; /* Silently disconnect */ + rpt_mutex_unlock(&myrpt->lock); + /* ast_log(LOG_NOTICE,"dumping link %s\n",l->name); */ + + wf.frametype = AST_FRAME_TEXT; + wf.subclass = 0; + wf.offset = 0; + wf.mallocd = 0; + wf.datalen = strlen(discstr) + 1; + wf.samples = 0; + wf.data = discstr; + if (l->chan) + { + ast_write(l->chan,&wf); + ast_safe_sleep(l->chan,250); /* It's dead already, why check the return value? */ + ast_softhangup(l->chan,AST_SOFTHANGUP_DEV); + } + rpt_mutex_lock(&myrpt->lock); + l = l->next; + } + rpt_mutex_unlock(&myrpt->lock); + if(debug > 3) + ast_log(LOG_NOTICE,"Nodes disconnected: %s\n",myrpt->savednodes); + rpt_telemetry(myrpt, COMPLETE, NULL); + return DC_COMPLETE; + + case 7: /* Identify last node which keyed us up */ + rpt_telemetry(myrpt, LASTNODEKEY, NULL); + break; + + +#ifdef _MDC_DECODE_H_ + case 8: + myrpt->lastunit = 0xd00d; + mdc1200_notify(myrpt,NULL,myrpt->lastunit); + mdc1200_send(myrpt,myrpt->lastunit); + break; +#endif + + case 16: /* Restore links disconnected with "disconnect all links" command */ + strcpy(tmp, myrpt->savednodes); /* Make a copy */ + finddelim(tmp, strs, MAXLINKLIST); /* convert into substrings */ + for(i = 0; tmp[0] && strs[i] != NULL && i < MAXLINKLIST; i++){ + s1 = strs[i]; + mode = (s1[0] == 'X') ? 1 : 0; + perma = (s1[1] == 'P') ? 1 : 0; + connect_link(myrpt, s1 + 2, mode, perma); /* Try to reconnect */ + } + rpt_telemetry(myrpt, COMPLETE, NULL); + break; + + case 200: + case 201: + case 202: + case 203: + case 204: + case 205: + case 206: + case 207: + case 208: + case 209: + case 210: + case 211: + case 212: + case 213: + case 214: + case 215: + if (((myrpt->p.propagate_dtmf) && + (command_source == SOURCE_LNK)) || + ((myrpt->p.propagate_phonedtmf) && + ((command_source == SOURCE_PHONE) || + (command_source == SOURCE_DPHONE)))) + do_dtmf_local(myrpt, + remdtmfstr[myatoi(param) - 200]); + 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; + int i, index, paramlength; + char *lparam; + char *value = NULL; + char *paramlist[20]; + + static char *keywords[] = { + "context", + "dialtime", + "farenddisconnect", + "noct", + "quiet", + NULL + }; + + if (myrpt->p.s[myrpt->p.sysstate_cur].txdisable || myrpt->p.s[myrpt->p.sysstate_cur].autopatchdisable) + return DC_ERROR; + + if(debug) + printf("@@@@ Autopatch up\n"); + + if(!myrpt->callmode){ + /* Set defaults */ + myrpt->patchnoct = 0; + myrpt->patchdialtime = 0; + myrpt->patchfarenddisconnect = 0; + myrpt->patchquiet = 0; + strncpy(myrpt->patchcontext, myrpt->p.ourcontext, MAXPATCHCONTEXT); + + if(param){ + /* Process parameter list */ + lparam = ast_strdupa(param); + if(!lparam){ + ast_log(LOG_ERROR,"App_rpt out of memory on line %d\n",__LINE__); + return DC_ERROR; + } + paramlength = finddelim(lparam, paramlist, 20); + for(i = 0; i < paramlength; i++){ + index = matchkeyword(paramlist[i], &value, keywords); + if(value) + value = skipchars(value, "= "); + switch(index){ + + case 1: /* context */ + strncpy(myrpt->patchcontext, value, MAXPATCHCONTEXT - 1) ; + break; + + case 2: /* dialtime */ + myrpt->patchdialtime = atoi(value); + break; + + case 3: /* farenddisconnect */ + myrpt->patchfarenddisconnect = atoi(value); + break; + + case 4: /* noct */ + myrpt->patchnoct = atoi(value); + break; + + case 5: /* quiet */ + myrpt->patchquiet = atoi(value); + break; + + default: + break; + } + } + } + } + + rpt_mutex_lock(&myrpt->lock); + + /* if on call, force * into current audio stream */ + + if ((myrpt->callmode == 2) || (myrpt->callmode == 3)){ + myrpt->mydtmf = myrpt->p.endchar; + } + if (myrpt->callmode){ + rpt_mutex_unlock(&myrpt->lock); + return DC_COMPLETE; + } + myrpt->callmode = 1; + myrpt->cidx = 0; + myrpt->exten[myrpt->cidx] = 0; + rpt_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->p.s[myrpt->p.sysstate_cur].txdisable || myrpt->p.s[myrpt->p.sysstate_cur].autopatchdisable) + return DC_ERROR; + + if(debug) + printf("@@@@ Autopatch down\n"); + + rpt_mutex_lock(&myrpt->lock); + + if (!myrpt->callmode){ + rpt_mutex_unlock(&myrpt->lock); + return DC_COMPLETE; + } + + myrpt->callmode = 0; + rpt_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->p.s[myrpt->p.sysstate_cur].txdisable) || (myrpt->p.s[myrpt->p.sysstate_cur].userfundisable)) + 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; +} + +/* +* Macro-oni (without Salami) +*/ + +static int function_macro(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink) +{ + +char *val; +int i; + if (myrpt->remote) + return DC_ERROR; + + if(debug) + printf("@@@@ macro-oni param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf); + + if(strlen(digitbuf) < 1) /* needs 1 digit */ + return DC_INDETERMINATE; + + for(i = 0 ; i < digitbuf[i] ; i++) { + if((digitbuf[i] < '0') || (digitbuf[i] > '9')) + return DC_ERROR; + } + + if (*digitbuf == '0') val = myrpt->p.startupmacro; + else val = (char *) ast_variable_retrieve(myrpt->cfg, myrpt->p.macro, digitbuf); + /* param was 1 for local buf */ + if (!val){ + if (strlen(digitbuf) < myrpt->macro_longest) + return DC_INDETERMINATE; + rpt_telemetry(myrpt, MACRO_NOTFOUND, NULL); + return DC_COMPLETE; + } + rpt_mutex_lock(&myrpt->lock); + if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(val)) + { + rpt_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt, MACRO_BUSY, NULL); + return DC_ERROR; + } + myrpt->macrotimer = MACROTIME; + strncat(myrpt->macrobuf, val, MAXMACRO - strlen(myrpt->macrobuf) - 1); + rpt_mutex_unlock(&myrpt->lock); + return DC_COMPLETE; +} + +/* +* COP - Control operator +*/ + +static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink) +{ + char string[16]; + + if(!param) + return DC_ERROR; + + switch(myatoi(param)){ + case 1: /* System reset */ + system("killall -9 asterisk"); + return DC_COMPLETE; + + case 2: + myrpt->p.s[myrpt->p.sysstate_cur].txdisable = 0; + rpt_telemetry(myrpt, ARB_ALPHA, (void *) "RPTENA"); + return DC_COMPLETE; + + case 3: + myrpt->p.s[myrpt->p.sysstate_cur].txdisable = 1; + return DC_COMPLETE; + + case 4: /* test tone on */ + if (myrpt->stopgen < 0) + { + myrpt->stopgen = 1; + } + else + { + myrpt->stopgen = 0; + 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; + + + case 7: /* Time out timer enable */ + myrpt->p.s[myrpt->p.sysstate_cur].totdisable = 0; + rpt_telemetry(myrpt, ARB_ALPHA, (void *) "TOTENA"); + return DC_COMPLETE; + + case 8: /* Time out timer disable */ + myrpt->p.s[myrpt->p.sysstate_cur].totdisable = 1; + rpt_telemetry(myrpt, ARB_ALPHA, (void *) "TOTDIS"); + return DC_COMPLETE; + + case 9: /* Autopatch enable */ + myrpt->p.s[myrpt->p.sysstate_cur].autopatchdisable = 0; + rpt_telemetry(myrpt, ARB_ALPHA, (void *) "APENA"); + return DC_COMPLETE; + + case 10: /* Autopatch disable */ + myrpt->p.s[myrpt->p.sysstate_cur].autopatchdisable = 1; + rpt_telemetry(myrpt, ARB_ALPHA, (void *) "APDIS"); + return DC_COMPLETE; + + case 11: /* Link Enable */ + myrpt->p.s[myrpt->p.sysstate_cur].linkfundisable = 0; + rpt_telemetry(myrpt, ARB_ALPHA, (void *) "LNKENA"); + return DC_COMPLETE; + + case 12: /* Link Disable */ + myrpt->p.s[myrpt->p.sysstate_cur].linkfundisable = 1; + rpt_telemetry(myrpt, ARB_ALPHA, (void *) "LNKDIS"); + return DC_COMPLETE; + + case 13: /* Query System State */ + string[0] = string[1] = 'S'; + string[2] = myrpt->p.sysstate_cur + '0'; + string[3] = '\0'; + rpt_telemetry(myrpt, ARB_ALPHA, (void *) string); + return DC_COMPLETE; + + case 14: /* Change System State */ + if(strlen(digitbuf) == 0) + break; + if((digitbuf[0] < '0') || (digitbuf[0] > '9')) + return DC_ERROR; + myrpt->p.sysstate_cur = digitbuf[0] - '0'; + string[0] = string[1] = 'S'; + string[2] = myrpt->p.sysstate_cur + '0'; + string[3] = '\0'; + rpt_telemetry(myrpt, ARB_ALPHA, (void *) string); + return DC_COMPLETE; + + case 15: /* Scheduler Enable */ + myrpt->p.s[myrpt->p.sysstate_cur].schedulerdisable = 0; + rpt_telemetry(myrpt, ARB_ALPHA, (void *) "SKENA"); + return DC_COMPLETE; + + case 16: /* Scheduler Disable */ + myrpt->p.s[myrpt->p.sysstate_cur].schedulerdisable = 1; + rpt_telemetry(myrpt, ARB_ALPHA, (void *) "SKDIS"); + return DC_COMPLETE; + + case 17: /* User functions Enable */ + myrpt->p.s[myrpt->p.sysstate_cur].userfundisable = 0; + rpt_telemetry(myrpt, ARB_ALPHA, (void *) "UFENA"); + return DC_COMPLETE; + + case 18: /* User Functions Disable */ + myrpt->p.s[myrpt->p.sysstate_cur].userfundisable = 1; + rpt_telemetry(myrpt, ARB_ALPHA, (void *) "UFDIS"); + return DC_COMPLETE; + + case 19: /* Alternate Tail Enable */ + myrpt->p.s[myrpt->p.sysstate_cur].alternatetail = 1; + rpt_telemetry(myrpt, ARB_ALPHA, (void *) "ATENA"); + return DC_COMPLETE; + + case 20: /* Alternate Tail Disable */ + myrpt->p.s[myrpt->p.sysstate_cur].alternatetail = 0; + rpt_telemetry(myrpt, ARB_ALPHA, (void *) "ATDIS"); + return DC_COMPLETE; + } + 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[200]; + + struct ast_variable *vp; + + if(debug) + printf("@@@@ Digits collected: %s, source: %d\n", digits, command_source); + + if (command_source == SOURCE_DPHONE) { + if (!myrpt->p.dphone_functions) return DC_INDETERMINATE; + strncpy(function_table_name, myrpt->p.dphone_functions, sizeof(function_table_name) - 1); + } + else if (command_source == SOURCE_PHONE) { + if (!myrpt->p.phone_functions) return DC_INDETERMINATE; + strncpy(function_table_name, myrpt->p.phone_functions, sizeof(function_table_name) - 1); + } + else if (command_source == SOURCE_LNK) + strncpy(function_table_name, myrpt->p.link_functions, sizeof(function_table_name) - 1); + else + strncpy(function_table_name, myrpt->p.functions, sizeof(function_table_name) - 1); + vp = ast_variable_browse(myrpt->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[512],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 = 0; + 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 = mylink->max_retries + 1; + ast_softhangup(mylink->chan,AST_SOFTHANGUP_DEV); + return; + } + if (tmp[0] == 'L') + { + rpt_mutex_lock(&myrpt->lock); + strcpy(mylink->linklist,tmp + 2); + time(&mylink->linklistreceived); + rpt_mutex_unlock(&myrpt->lock); + if (debug > 6) ast_log(LOG_NOTICE,"@@@@ node %s recieved node list %s from node %s\n", + myrpt->name,tmp,mylink->name); + return; + } + if (tmp[0] == 'I') + { + if (sscanf(tmp,"%s %s %x",cmd,src,&seq) != 3) + { + ast_log(LOG_WARNING, "Unable to parse ident string %s\n",str); + return; + } + mdc1200_notify(myrpt,src,seq); + strcpy(dest,"*"); + } + else + { + 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 = 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 = str; + if (l->chan) ast_write(l->chan,&wf); + } + l = l->next; + } + return; + } + if (myrpt->p.archivedir) + { + char str[100]; + + sprintf(str,"DTMF,%s,%c",mylink->name,c); + donodelog(myrpt,str); + } + c = func_xlat(myrpt,c,&myrpt->p.outxlat); + if (!c) return; + rpt_mutex_lock(&myrpt->lock); + if (c == myrpt->p.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->patchcontext,myrpt->exten,1,NULL)) + { + myrpt->callmode = 2; + if(!myrpt->patchquiet){ + rpt_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,PROC,NULL); + rpt_mutex_lock(&myrpt->lock); + } + } + /* if can continue, do so */ + if (!ast_canmatch_extension(myrpt->pchannel,myrpt->patchcontext,myrpt->exten,1,NULL)) + { + /* call has failed, inform user */ + myrpt->callmode = 4; + } + } + if (c == myrpt->p.funcchar) + { + myrpt->rem_dtmfidx = 0; + myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0; + time(&myrpt->rem_dtmf_time); + rpt_mutex_unlock(&myrpt->lock); + return; + } + else if (myrpt->rem_dtmfidx < 0) + { + if ((myrpt->callmode == 2) || (myrpt->callmode == 3)) + { + myrpt->mydtmf = c; + } + if (myrpt->p.propagate_dtmf) do_dtmf_local(myrpt,c); + if (myrpt->p.propagate_phonedtmf) do_dtmf_phone(myrpt,mylink,c); + rpt_mutex_unlock(&myrpt->lock); + return; + } + else if ((c != myrpt->p.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; + + rpt_mutex_unlock(&myrpt->lock); + strncpy(cmd, myrpt->rem_dtmfbuf, sizeof(cmd) - 1); + res = collect_function_digits(myrpt, cmd, SOURCE_LNK, mylink); + rpt_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: + case DC_COMPLETEQUIET: + myrpt->totalexecdcommands++; + myrpt->dailyexecdcommands++; + strncpy(myrpt->lastdtmfcommand, cmd, MAXDTMF-1); + myrpt->lastdtmfcommand[MAXDTMF-1] = '\0'; + 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; + } + } + + } + rpt_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; + + if (myrpt->p.archivedir) + { + char str[100]; + + sprintf(str,"DTMF(P),%s,%c",mylink->name,c); + donodelog(myrpt,str); + } + rpt_mutex_lock(&myrpt->lock); + if (c == myrpt->p.endchar) + { + if (mylink->lastrx) + { + mylink->lastrx = 0; + rpt_mutex_unlock(&myrpt->lock); + return; + } + myrpt->stopgen = 1; + if (myrpt->cmdnode[0]) + { + myrpt->cmdnode[0] = 0; + myrpt->dtmfidx = -1; + myrpt->dtmfbuf[0] = 0; + rpt_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,COMPLETE,NULL); + return; + } + } + if (myrpt->cmdnode[0]) + { + rpt_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->patchcontext,myrpt->exten,1,NULL)) + { + myrpt->callmode = 2; + if(!myrpt->patchquiet){ + rpt_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,PROC,NULL); + rpt_mutex_lock(&myrpt->lock); + } + } + /* if can continue, do so */ + if (!ast_canmatch_extension(myrpt->pchannel,myrpt->patchcontext,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->p.funcchar) + { + myrpt->rem_dtmfidx = 0; + myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0; + time(&myrpt->rem_dtmf_time); + rpt_mutex_unlock(&myrpt->lock); + return; + } + else if ((c != myrpt->p.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; + + rpt_mutex_unlock(&myrpt->lock); + strncpy(cmd, myrpt->rem_dtmfbuf, sizeof(cmd) - 1); + switch(mylink->phonemode) + { + case 1: + res = collect_function_digits(myrpt, cmd, + SOURCE_PHONE, mylink); + break; + case 2: + res = collect_function_digits(myrpt, cmd, + SOURCE_DPHONE,mylink); + break; + default: + res = collect_function_digits(myrpt, cmd, + SOURCE_LNK, mylink); + break; + } + + rpt_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: + case DC_COMPLETEQUIET: + myrpt->totalexecdcommands++; + myrpt->dailyexecdcommands++; + strncpy(myrpt->lastdtmfcommand, cmd, MAXDTMF-1); + myrpt->lastdtmfcommand[MAXDTMF-1] = '\0'; + 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; + } + } + + } + rpt_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) + { +#ifdef __i386__ + 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->p.iobase); + /* >= 15 us */ + for(delayvar = 1; delayvar < 15000; delayvar++); + od >>= 1; + outb(d | 2,myrpt->p.iobase); + /* >= 30 us */ + for(delayvar = 1; delayvar < 30000; delayvar++); + outb(d,myrpt->p.iobase); + /* >= 10 us */ + for(delayvar = 1; delayvar < 10000; delayvar++); + } + } + /* >= 50 us */ + for(delayvar = 1; delayvar < 50000; delayvar++); +#endif + } + +static void rbi_out(struct rpt *myrpt,unsigned char *data) +{ +struct dahdi_radio_param r; + + memset(&r,0,sizeof(struct dahdi_radio_param)); + r.radpar = DAHDI_RADPAR_REMMODE; + r.data = DAHDI_RADPAR_REM_RBI1; + /* if setparam ioctl fails, its probably not a pciradio card */ + if (ioctl(myrpt->zaprxchannel->fds[0],DAHDI_RADIO_SETPARAM,&r) == -1) + { + rbi_out_parallel(myrpt,data); + return; + } + r.radpar = DAHDI_RADPAR_REMCOMMAND; + memcpy(&r.data,data,5); + if (ioctl(myrpt->zaprxchannel->fds[0],DAHDI_RADIO_SETPARAM,&r) == -1) + { + ast_log(LOG_WARNING,"Cannot send RBI command for channel %s\n",myrpt->zaprxchannel->name); + return; + } +} + +static int serial_remote_io(struct rpt *myrpt, unsigned char *txbuf, int txbytes, + unsigned char *rxbuf, int rxmaxbytes, int asciiflag) +{ + int i,j,index,oldmode,olddata; + struct dahdi_radio_param prm; + char c; + + if(debug){ + printf("String output was: "); + for(i = 0; i < txbytes; i++) + printf("%02X ", (unsigned char ) txbuf[i]); + printf("\n"); + } + if (myrpt->iofd > 0) /* if to do out a serial port */ + { + if (rxmaxbytes && rxbuf) tcflush(myrpt->iofd,TCIFLUSH); + if (write(myrpt->iofd,txbuf,txbytes) != txbytes) return -1; + if ((!rxmaxbytes) || (rxbuf == NULL)) return(0); + memset(rxbuf,0,rxmaxbytes); + for(i = 0; i < rxmaxbytes; i++) + { + j = read(myrpt->iofd,&c,1); + if (j < 1) return(i); + rxbuf[i] = c; + if (asciiflag & 1) + { + rxbuf[i + 1] = 0; + if (c == '\r') break; + } + } + if(debug){ + printf("String returned was: "); + for(j = 0; j < i; j++) + printf("%02X ", (unsigned char ) rxbuf[j]); + printf("\n"); + } + return(i); + } + + /* if not a zap channel, cant use pciradio stuff */ + if (myrpt->rxchannel != myrpt->zaprxchannel) return -1; + + prm.radpar = DAHDI_RADPAR_UIOMODE; + if (ioctl(myrpt->zaprxchannel->fds[0],DAHDI_RADIO_GETPARAM,&prm) == -1) return -1; + oldmode = prm.data; + prm.radpar = DAHDI_RADPAR_UIODATA; + if (ioctl(myrpt->zaprxchannel->fds[0],DAHDI_RADIO_GETPARAM,&prm) == -1) return -1; + olddata = prm.data; + prm.radpar = DAHDI_RADPAR_REMMODE; + if (asciiflag & 1) prm.data = DAHDI_RADPAR_REM_SERIAL_ASCII; + else prm.data = DAHDI_RADPAR_REM_SERIAL; + if (ioctl(myrpt->zaprxchannel->fds[0],DAHDI_RADIO_SETPARAM,&prm) == -1) return -1; + if (asciiflag & 2) + { + i = DAHDI_ONHOOK; + if (ioctl(myrpt->zaprxchannel->fds[0],DAHDI_HOOK,&i) == -1) return -1; + usleep(100000); + } + prm.radpar = DAHDI_RADPAR_REMCOMMAND; + prm.data = rxmaxbytes; + memcpy(prm.buf,txbuf,txbytes); + prm.index = txbytes; + if (ioctl(myrpt->zaprxchannel->fds[0],DAHDI_RADIO_SETPARAM,&prm) == -1) return -1; + if (rxbuf) + { + *rxbuf = 0; + memcpy(rxbuf,prm.buf,prm.index); + } + index = prm.index; + prm.radpar = DAHDI_RADPAR_REMMODE; + prm.data = DAHDI_RADPAR_REM_NONE; + if (ioctl(myrpt->zaprxchannel->fds[0],DAHDI_RADIO_SETPARAM,&prm) == -1) return -1; + if (asciiflag & 2) + { + i = DAHDI_OFFHOOK; + if (ioctl(myrpt->zaprxchannel->fds[0],DAHDI_HOOK,&i) == -1) return -1; + } + prm.radpar = DAHDI_RADPAR_UIOMODE; + prm.data = oldmode; + if (ioctl(myrpt->zaprxchannel->fds[0],DAHDI_RADIO_SETPARAM,&prm) == -1) return -1; + prm.radpar = DAHDI_RADPAR_UIODATA; + prm.data = olddata; + if (ioctl(myrpt->zaprxchannel->fds[0],DAHDI_RADIO_SETPARAM,&prm) == -1) return -1; + return(index); +} + +static int civ_cmd(struct rpt *myrpt,unsigned char *cmd, int cmdlen) +{ +unsigned char rxbuf[100]; +int i,rv ; + + rv = serial_remote_io(myrpt,cmd,cmdlen,rxbuf,cmdlen + 6,0); + if (rv == -1) return(-1); + if (rv != (cmdlen + 6)) return(1); + for(i = 0; i < 6; i++) + if (rxbuf[i] != cmd[i]) return(1); + if (rxbuf[cmdlen] != 0xfe) return(1); + if (rxbuf[cmdlen + 1] != 0xfe) return(1); + if (rxbuf[cmdlen + 4] != 0xfb) return(1); + if (rxbuf[cmdlen + 5] != 0xfd) return(1); + return(0); +} + +static int sendkenwood(struct rpt *myrpt,char *txstr, char *rxstr) +{ +int i; + + if (debug) printf("Send to kenwood: %s\n",txstr); + i = serial_remote_io(myrpt, (unsigned char *)txstr, strlen(txstr), + (unsigned char *)rxstr,RAD_SERIAL_BUFLEN - 1,3); + if (i < 0) return -1; + if ((i > 0) && (rxstr[i - 1] == '\r')) + rxstr[i-- - 1] = 0; + if (debug) printf("Got from kenwood: %s\n",rxstr); + return(i); +} + +/* take a PL frequency and turn it into a code */ +static int kenwood_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 1; + case 719: + return 3; + case 744: + return 4; + case 770: + return 5; + case 797: + return 6; + case 825: + return 7; + case 854: + return 8; + case 885: + return 9; + case 915: + return 10; + case 948: + return 11; + case 974: + return 12; + case 1000: + return 13; + case 1035: + return 14; + case 1072: + return 15; + case 1109: + return 16; + case 1148: + return 17; + case 1188: + return 18; + case 1230: + return 19; + case 1273: + return 20; + case 1318: + return 21; + case 1365: + return 22; + case 1413: + return 23; + case 1462: + return 24; + case 1514: + return 25; + case 1567: + return 26; + case 1622: + return 27; + case 1679: + return 28; + case 1738: + return 29; + case 1799: + return 30; + case 1862: + return 31; + case 1928: + return 32; + case 2035: + return 33; + case 2107: + return 34; + case 2181: + return 35; + case 2257: + return 36; + case 2336: + return 37; + case 2418: + return 38; + case 2503: + return 39; + } + return -1; +} + +static int sendrxkenwood(struct rpt *myrpt, char *txstr, char *rxstr, + char *cmpstr) +{ +int i,j; + + for(i = 0;i < KENWOOD_RETRIES;i++) + { + j = sendkenwood(myrpt,txstr,rxstr); + if (j < 0) return(j); + if (j == 0) continue; + if (!strncmp(rxstr,cmpstr,strlen(cmpstr))) return(0); + } + return(-1); +} + +static int setkenwood(struct rpt *myrpt) +{ +char rxstr[RAD_SERIAL_BUFLEN],txstr[RAD_SERIAL_BUFLEN],freq[20]; +char mhz[MAXREMSTR],offset[20],band,decimals[MAXREMSTR],band1,band2; + +int offsets[] = {0,2,1}; +int powers[] = {2,1,0}; + + if (sendrxkenwood(myrpt,"VMC 0,0\r",rxstr,"VMC") < 0) return -1; + split_freq(mhz, decimals, myrpt->freq); + if (atoi(mhz) > 400) + { + band = '6'; + band1 = '1'; + band2 = '5'; + strcpy(offset,"005000000"); + } + else + { + band = '2'; + band1 = '0'; + band2 = '2'; + strcpy(offset,"000600000"); + } + strcpy(freq,"000000"); + strncpy(freq,decimals,strlen(decimals)); + sprintf(txstr,"VW %c,%05d%s,0,%d,0,%d,%d,,%02d,,%02d,%s\r", + band,atoi(mhz),freq,offsets[(int)myrpt->offset], + (myrpt->txplon != 0),(myrpt->rxplon != 0), + kenwood_pltocode(myrpt->txpl),kenwood_pltocode(myrpt->rxpl), + offset); + if (sendrxkenwood(myrpt,txstr,rxstr,"VW") < 0) return -1; + sprintf(txstr,"RBN %c\r",band2); + if (sendrxkenwood(myrpt,txstr,rxstr,"RBN") < 0) return -1; + sprintf(txstr,"PC %c,%d\r",band1,powers[(int)myrpt->powerlevel]); + if (sendrxkenwood(myrpt,txstr,rxstr,"PC") < 0) return -1; + return 0; +} + +static int setrbi(struct rpt *myrpt) +{ +char tmp[MAXREMSTR] = "",*s; +unsigned char rbicmd[5]; +int band,txoffset = 0,txpower = 0,rxpl; + + /* must be a remote system */ + if (!myrpt->remote) return(0); + /* must have rbi hardware */ + if (strncmp(myrpt->remote,remote_rig_rbi,3)) return(0); + if (setrbi_check(myrpt) == -1) return(-1); + 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; + } + + rxpl = rbi_pltocode(myrpt->rxpl); + + if (rxpl == -1){ + if(debug) + printf("@@@@ Bad TX PL: %s\n", myrpt->rxpl); + 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] = rxpl; + if (myrpt->txplon) rbicmd[4] |= 0x40; + if (myrpt->rxplon) rbicmd[4] |= 0x80; + rbi_out(myrpt,rbicmd); + return 0; +} + +static int setrbi_check(struct rpt *myrpt) +{ +char tmp[MAXREMSTR] = "",*s; +int band,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; + } + return 0; +} + +static int check_freq_kenwood(int m, int d, int *defmode) +{ + int dflmd = REM_MODE_FM; + + if (m == 144){ /* 2 meters */ + if(d < 10100) + return -1; + } + else if((m >= 145) && (m < 148)){ + ; + } + else if((m >= 430) && (m < 450)){ /* 70 centimeters */ + ; + } + else + return -1; + + if(defmode) + *defmode = dflmd; + + + 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; +} + +/* + * Convert decimals of frequency to int + */ + +static int decimals2int(char *fraction) +{ + int i; + char len = strlen(fraction); + int multiplier = 100000; + int res = 0; + + if(!len) + return 0; + for( i = 0 ; i < len ; i++, multiplier /= 10) + res += (fraction[i] - '0') * multiplier; + return res; +} + + +/* +* 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 < 80000) + return -1; + } + else if(m == 3){ /* 80 meters */ + dflmd = REM_MODE_LSB; + if(d < 50000) + return -1; + } + else if(m == 7){ /* 40 meters */ + dflmd = REM_MODE_LSB; + if(d > 30000) + return -1; + } + else if(m == 14){ /* 20 meters */ + dflmd = REM_MODE_USB; + if(d > 35000) + return -1; + } + else if(m == 18){ /* 17 meters */ + dflmd = REM_MODE_USB; + if((d < 6800) || (d > 16800)) + return -1; + } + else if(m == 21){ /* 15 meters */ + dflmd = REM_MODE_USB; + if((d < 20000) || (d > 45000)) + return -1; + } + else if(m == 24){ /* 12 meters */ + dflmd = REM_MODE_USB; + if((d < 89000) || (d > 99000)) + return -1; + } + else if(m == 28){ /* 10 meters */ + dflmd = REM_MODE_USB; + } + else if(m == 29){ + if(d >= 51000) + dflmd = REM_MODE_FM; + else + dflmd = REM_MODE_USB; + if(d > 70000) + return -1; + } + else if(m == 50){ /* 6 meters */ + 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 >= 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) +{ + unsigned char cmdstr[5]; + int fd,m,d; + char mhz[MAXREMSTR]; + char decimals[MAXREMSTR]; + + 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); +} + + + +/* +* IC-706 I/O handlers +*/ + +/* Check to see that the frequency is valid */ +/* Hard coded limits now, configurable later, maybe? */ + + +static int check_freq_ic706(int m, int d, int *defmode) +{ + int dflmd = REM_MODE_FM; + + if(m == 1){ /* 160 meters */ + dflmd = REM_MODE_LSB; + if(d < 80000) + return -1; + } + else if(m == 3){ /* 80 meters */ + dflmd = REM_MODE_LSB; + if(d < 50000) + return -1; + } + else if(m == 7){ /* 40 meters */ + dflmd = REM_MODE_LSB; + if(d > 30000) + return -1; + } + else if(m == 14){ /* 20 meters */ + dflmd = REM_MODE_USB; + if(d > 35000) + return -1; + } + else if(m == 18){ /* 17 meters */ + dflmd = REM_MODE_USB; + if((d < 6800) || (d > 16800)) + return -1; + } + else if(m == 21){ /* 15 meters */ + dflmd = REM_MODE_USB; + if((d < 20000) || (d > 45000)) + return -1; + } + else if(m == 24){ /* 12 meters */ + dflmd = REM_MODE_USB; + if((d < 89000) || (d > 99000)) + return -1; + } + else if(m == 28){ /* 10 meters */ + dflmd = REM_MODE_USB; + } + else if(m == 29){ + if(d >= 51000) + dflmd = REM_MODE_FM; + else + dflmd = REM_MODE_USB; + if(d > 70000) + return -1; + } + else if(m == 50){ /* 6 meters */ + 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 >= 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; +} + +/* take a PL frequency and turn it into a code */ +static int ic706_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 693: + return 1; + case 719: + return 2; + case 744: + return 3; + case 770: + return 4; + case 797: + return 5; + case 825: + return 6; + case 854: + return 7; + case 885: + return 8; + case 915: + return 9; + case 948: + return 10; + case 974: + return 11; + case 1000: + return 12; + case 1035: + return 13; + case 1072: + return 14; + case 1109: + return 15; + case 1148: + return 16; + case 1188: + return 17; + case 1230: + return 18; + case 1273: + return 19; + case 1318: + return 20; + case 1365: + return 21; + case 1413: + return 22; + case 1462: + return 23; + case 1514: + return 24; + case 1567: + return 25; + case 1598: + return 26; + case 1622: + return 27; + case 1655: + return 28; + case 1679: + return 29; + case 1713: + return 30; + case 1738: + return 31; + case 1773: + return 32; + case 1799: + return 33; + case 1835: + return 34; + case 1862: + return 35; + case 1899: + return 36; + case 1928: + return 37; + case 1966: + return 38; + case 1995: + return 39; + case 2035: + return 40; + case 2065: + return 41; + case 2107: + return 42; + case 2181: + return 43; + case 2257: + return 44; + case 2291: + return 45; + case 2336: + return 46; + case 2418: + return 47; + case 2503: + return 48; + case 2541: + return 49; + } + return -1; +} + +/* ic-706 simple commands */ + +static int simple_command_ic706(struct rpt *myrpt, char command, char subcommand) +{ + unsigned char cmdstr[10]; + + cmdstr[0] = cmdstr[1] = 0xfe; + cmdstr[2] = myrpt->p.civaddr; + cmdstr[3] = 0xe0; + cmdstr[4] = command; + cmdstr[5] = subcommand; + cmdstr[6] = 0xfd; + + return(civ_cmd(myrpt,cmdstr,7)); +} + +/* +* Set a new frequency for the ic706 +*/ + +static int set_freq_ic706(struct rpt *myrpt, char *newfreq) +{ + unsigned char cmdstr[20]; + char mhz[MAXREMSTR], decimals[MAXREMSTR]; + 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 ic-706 likes packed BCD frequencies */ + + cmdstr[0] = cmdstr[1] = 0xfe; + cmdstr[2] = myrpt->p.civaddr; + cmdstr[3] = 0xe0; + cmdstr[4] = 5; + cmdstr[5] = ((d % 10) << 4); + cmdstr[6] = (((d % 1000)/ 100) << 4) + ((d % 100)/10); + cmdstr[7] = ((d / 10000) << 4) + ((d % 10000)/1000); + cmdstr[8] = (((m % 100)/10) << 4) + (m % 10); + cmdstr[9] = (m / 100); + cmdstr[10] = 0xfd; + + return(civ_cmd(myrpt,cmdstr,11)); +} + +/* ic-706 offset */ + +static int set_offset_ic706(struct rpt *myrpt, char offset) +{ + unsigned char c; + + switch(offset){ + case REM_SIMPLEX: + c = 0x10; + break; + + case REM_MINUS: + c = 0x11; + break; + + case REM_PLUS: + c = 0x12; + break; + + default: + return -1; + } + + return simple_command_ic706(myrpt,0x0f,c); + +} + +/* ic-706 mode */ + +static int set_mode_ic706(struct rpt *myrpt, char newmode) +{ + unsigned char c; + + switch(newmode){ + case REM_MODE_FM: + c = 5; + break; + + case REM_MODE_USB: + c = 1; + break; + + case REM_MODE_LSB: + c = 0; + break; + + case REM_MODE_AM: + c = 2; + break; + + default: + return -1; + } + return simple_command_ic706(myrpt,6,c); +} + +/* Set tone encode and decode modes */ + +static int set_ctcss_mode_ic706(struct rpt *myrpt, char txplon, char rxplon) +{ + unsigned char cmdstr[10]; + int rv; + + cmdstr[0] = cmdstr[1] = 0xfe; + cmdstr[2] = myrpt->p.civaddr; + cmdstr[3] = 0xe0; + cmdstr[4] = 0x16; + cmdstr[5] = 0x42; + cmdstr[6] = (txplon != 0); + cmdstr[7] = 0xfd; + + rv = civ_cmd(myrpt,cmdstr,8); + if (rv) return(-1); + + cmdstr[0] = cmdstr[1] = 0xfe; + cmdstr[2] = myrpt->p.civaddr; + cmdstr[3] = 0xe0; + cmdstr[4] = 0x16; + cmdstr[5] = 0x43; + cmdstr[6] = (rxplon != 0); + cmdstr[7] = 0xfd; + + return(civ_cmd(myrpt,cmdstr,8)); +} + +#if 0 +/* Set transmit and receive ctcss tone frequencies */ + +static int set_ctcss_freq_ic706(struct rpt *myrpt, char *txtone, char *rxtone) +{ + unsigned char cmdstr[10]; + char hertz[MAXREMSTR],decimal[MAXREMSTR]; + int h,d,rv; + + memset(cmdstr, 0, 5); + + if(split_ctcss_freq(hertz, decimal, txtone)) + return -1; + + h = atoi(hertz); + d = atoi(decimal); + + cmdstr[0] = cmdstr[1] = 0xfe; + cmdstr[2] = myrpt->p.civaddr; + cmdstr[3] = 0xe0; + cmdstr[4] = 0x1b; + cmdstr[5] = 0; + cmdstr[6] = ((h / 100) << 4) + (h % 100)/ 10; + cmdstr[7] = ((h % 10) << 4) + (d % 10); + cmdstr[8] = 0xfd; + + rv = civ_cmd(myrpt,cmdstr,9); + if (rv) return(-1); + + if (!rxtone) return(0); + + if(split_ctcss_freq(hertz, decimal, rxtone)) + return -1; + + h = atoi(hertz); + d = atoi(decimal); + + cmdstr[0] = cmdstr[1] = 0xfe; + cmdstr[2] = myrpt->p.civaddr; + cmdstr[3] = 0xe0; + cmdstr[4] = 0x1b; + cmdstr[5] = 1; + cmdstr[6] = ((h / 100) << 4) + (h % 100)/ 10; + cmdstr[7] = ((h % 10) << 4) + (d % 10); + cmdstr[8] = 0xfd; + return(civ_cmd(myrpt,cmdstr,9)); +} +#endif + +static int vfo_ic706(struct rpt *myrpt) +{ + unsigned char cmdstr[10]; + + cmdstr[0] = cmdstr[1] = 0xfe; + cmdstr[2] = myrpt->p.civaddr; + cmdstr[3] = 0xe0; + cmdstr[4] = 7; + cmdstr[5] = 0xfd; + + return(civ_cmd(myrpt,cmdstr,6)); +} + +static int mem2vfo_ic706(struct rpt *myrpt) +{ + unsigned char cmdstr[10]; + + cmdstr[0] = cmdstr[1] = 0xfe; + cmdstr[2] = myrpt->p.civaddr; + cmdstr[3] = 0xe0; + cmdstr[4] = 0x0a; + cmdstr[5] = 0xfd; + + return(civ_cmd(myrpt,cmdstr,6)); +} + +static int select_mem_ic706(struct rpt *myrpt, int slot) +{ + unsigned char cmdstr[10]; + + cmdstr[0] = cmdstr[1] = 0xfe; + cmdstr[2] = myrpt->p.civaddr; + cmdstr[3] = 0xe0; + cmdstr[4] = 8; + cmdstr[5] = 0; + cmdstr[6] = ((slot / 10) << 4) + (slot % 10); + cmdstr[7] = 0xfd; + + return(civ_cmd(myrpt,cmdstr,8)); +} + +static int set_ic706(struct rpt *myrpt) +{ + int res = 0,i; + + if(debug) + printf("Set to VFO A\n"); + + if (!res) + res = simple_command_ic706(myrpt,7,0); + + + if((myrpt->remmode == REM_MODE_FM)) + { + i = ic706_pltocode(myrpt->rxpl); + if (i == -1) return -1; + if(debug) + printf("Select memory number\n"); + if (!res) + res = select_mem_ic706(myrpt,i + IC706_PL_MEMORY_OFFSET); + if(debug) + printf("Transfer memory to VFO\n"); + if (!res) + res = mem2vfo_ic706(myrpt); + } + + if(debug) + printf("Set to VFO\n"); + + if (!res) + res = vfo_ic706(myrpt); + + if(debug) + printf("Modulation mode\n"); + + if (!res) + res = set_mode_ic706(myrpt, myrpt->remmode); /* Modulation mode */ + + if(debug) + printf("Split off\n"); + + if(!res) + simple_command_ic706(myrpt, 0x82,0); /* Split off */ + + if(debug) + printf("Frequency\n"); + + if(!res) + res = set_freq_ic706(myrpt, myrpt->freq); /* Frequency */ + if((myrpt->remmode == REM_MODE_FM)){ + if(debug) + printf("Offset\n"); + if(!res) + res = set_offset_ic706(myrpt, myrpt->offset); /* Offset if FM */ + if(!res){ + if(debug) + printf("CTCSS mode\n"); + res = set_ctcss_mode_ic706(myrpt, myrpt->txplon, myrpt->rxplon); /* CTCSS mode */ + } + } + return res; +} + +/* +* 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_ic706(struct rpt *myrpt, int interval) +{ + int m,d; + char mhz[MAXREMSTR], decimals[MAXREMSTR]; + unsigned char cmdstr[20]; + + 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_ic706(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); + + /* The ic-706 likes packed BCD frequencies */ + + cmdstr[0] = cmdstr[1] = 0xfe; + cmdstr[2] = myrpt->p.civaddr; + cmdstr[3] = 0xe0; + cmdstr[4] = 0; + cmdstr[5] = ((d % 10) << 4); + cmdstr[6] = (((d % 1000)/ 100) << 4) + ((d % 100)/10); + cmdstr[7] = ((d / 10000) << 4) + ((d % 10000)/1000); + cmdstr[8] = (((m % 100)/10) << 4) + (m % 10); + cmdstr[9] = (m / 100); + cmdstr[10] = 0xfd; + + return(serial_remote_io(myrpt,cmdstr,11,NULL,0,0)); +} + + + +/* +* Dispatch to correct I/O handler +*/ + +static int setrem(struct rpt *myrpt) +{ +char str[300]; +char *offsets[] = {"MINUS","SIMPLEX","PLUS"}; +char *powerlevels[] = {"LOW","MEDIUM","HIGH"}; +char *modes[] = {"FM","USB","LSB","AM"}; +int res = -1; + + if (myrpt->p.archivedir) + { + sprintf(str,"FREQ,%s,%s,%s,%s,%s,%s,%d,%d",myrpt->freq, + modes[(int)myrpt->remmode], + myrpt->txpl,myrpt->rxpl,offsets[(int)myrpt->offset], + powerlevels[(int)myrpt->powerlevel],myrpt->txplon, + myrpt->rxplon); + donodelog(myrpt,str); + } + if(!strcmp(myrpt->remote, remote_rig_ft897)) + { + rpt_telemetry(myrpt,SETREMOTE,NULL); + res = 0; + } + if(!strcmp(myrpt->remote, remote_rig_ic706)) + { + rpt_telemetry(myrpt,SETREMOTE,NULL); + res = 0; + } + else if(!strcmp(myrpt->remote, remote_rig_rbi)) + { + res = setrbi_check(myrpt); + if (!res) + { + rpt_telemetry(myrpt,SETREMOTE,NULL); + res = 0; + } + } + else if(!strcmp(myrpt->remote, remote_rig_kenwood)) { + rpt_telemetry(myrpt,SETREMOTE,NULL); + res = 0; + } + else + res = 0; + + if (res < 0) ast_log(LOG_ERROR,"Unable to send remote command on node %s\n",myrpt->name); + + return res; +} + +static int closerem(struct rpt *myrpt) +{ + if(!strcmp(myrpt->remote, remote_rig_ft897)) + return closerem_ft897(myrpt); + else + return 0; +} + +/* +* Dispatch to correct RX 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_ic706)) + return check_freq_ic706(m, d, defmode); + else if(!strcmp(myrpt->remote, remote_rig_rbi)) + return check_freq_rbi(m, d, defmode); + else if(!strcmp(myrpt->remote, remote_rig_kenwood)) + return check_freq_kenwood(m, d, defmode); + else + return -1; +} + +/* + * Check TX frequency before transmitting + */ + +static char check_tx_freq(struct rpt *myrpt) +{ + int i; + int radio_mhz, radio_decimals, ulimit_mhz, ulimit_decimals, llimit_mhz, llimit_decimals; + char radio_mhz_char[MAXREMSTR]; + char radio_decimals_char[MAXREMSTR]; + char limit_mhz_char[MAXREMSTR]; + char limit_decimals_char[MAXREMSTR]; + char limits[256]; + char *limit_ranges[40]; + struct ast_variable *limitlist; + + + /* Must have user logged in and tx_limits defined */ + + if(!myrpt->p.txlimitsstanzaname || !myrpt->loginuser[0] || !myrpt->loginlevel[0]){ + if(debug > 3){ + ast_log(LOG_NOTICE, "No tx band table defined, or no user logged in\n"); + } + return 1; /* Assume it's ok otherwise */ + } + + /* Retrieve the band table for the loginlevel */ + limitlist = ast_variable_browse(myrpt->cfg, myrpt->p.txlimitsstanzaname); + + if(!limitlist){ + ast_log(LOG_WARNING, "No entries in %s band table stanza\n", myrpt->p.txlimitsstanzaname); + return 0; + } + + split_freq(radio_mhz_char, radio_decimals_char, myrpt->freq); + radio_mhz = atoi(radio_mhz_char); + radio_decimals = decimals2int(radio_decimals_char); + + + if(debug > 3){ + ast_log(LOG_NOTICE, "Login User = %s, login level = %s\n", myrpt->loginuser, myrpt->loginlevel); + } + + /* Find our entry */ + + for(;limitlist; limitlist=limitlist->next){ + if(!strcmp(limitlist->name, myrpt->loginlevel)) + break; + } + + if(!limitlist){ + ast_log(LOG_WARNING, "Can't find %s entry in band table stanza %s\n", myrpt->loginlevel, myrpt->p.txlimitsstanzaname); + return 0; + } + + if(debug > 3){ + ast_log(LOG_NOTICE, "Auth %s = %s\n", limitlist->name, limitlist->value); + } + + /* Parse the limits */ + + strncpy(limits, limitlist->value, 256); + limits[255] = 0; + finddelim(limits, limit_ranges, 40); + for(i = 0; i < 40 && limit_ranges[i] ; i++){ + char range[40]; + char *r,*s; + strncpy(range, limit_ranges[i], 40); + range[39] = 0; + if(debug > 3){ + ast_log(LOG_NOTICE, "Checking to see if %s is within limits of %s\n", myrpt->freq, range); + } + + r = strchr(range, '-'); + if(!r){ + ast_log(LOG_WARNING, "Malformed range in %s tx band table entry\n", limitlist->name); + return 0; + } + *r++ = 0; + s = eatwhite(range); + r = eatwhite(r); + split_freq(limit_mhz_char, limit_decimals_char, s); + llimit_mhz = atoi(limit_mhz_char); + llimit_decimals = decimals2int(limit_decimals_char); + split_freq(limit_mhz_char, limit_decimals_char, r); + ulimit_mhz = atoi(limit_mhz_char); + ulimit_decimals = decimals2int(limit_decimals_char); + + if((radio_mhz >= llimit_mhz) && (radio_mhz <= ulimit_mhz)){ + if(radio_mhz == llimit_mhz){ /* CASE 1: TX freq is in llimit mhz portion of band */ + if(radio_decimals >= llimit_decimals){ /* Cannot be below llimit decimals */ + if(llimit_mhz == ulimit_mhz){ /* If bandwidth < 1Mhz, check ulimit decimals */ + if(radio_decimals <= ulimit_decimals){ + return 1; + } + else{ + if(debug > 3) + ast_log(LOG_NOTICE, "Invalid TX frequency, debug msg 1\n"); + return 0; + } + } + else{ + return 1; + } + } + else{ /* Is below llimit decimals */ + if(debug > 3) + ast_log(LOG_NOTICE, "Invalid TX frequency, debug msg 2\n"); + return 0; + } + } + else if(radio_mhz == ulimit_mhz){ /* CASE 2: TX freq not in llimit mhz portion of band */ + if(radio_decimals <= ulimit_decimals){ + return 1; + } + else{ /* Is above ulimit decimals */ + if(debug > 3) + ast_log(LOG_NOTICE, "Invalid TX frequency, debug msg 3\n"); + return 0; + } + } + else /* CASE 3: TX freq within a multi-Mhz band and ok */ + return 1; + } + } + if(debug > 3) /* No match found in TX band table */ + ast_log(LOG_NOTICE, "Invalid TX frequency, debug msg 4\n"); + 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 if(!strcmp(myrpt->remote, remote_rig_ic706)) + return multimode_bump_freq_ic706(myrpt, interval); + else + return -1; +} + + +/* +* Queue announcment that scan has been stopped +*/ + +static void stop_scan(struct rpt *myrpt) +{ + myrpt->hfscanstop = 1; + rpt_telemetry(myrpt,SCAN,0); +} + +/* +* 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){ + myrpt->hfscanmode = 0; + myrpt->hfscanstatus = -2; + 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; + } else myrpt->hfscanstatus = 0; + return res; + +} + +/* + * Retrieve a memory channel + * Return 0 if sucessful, + * -1 if channel not found, + * 1 if parse error + */ + +static int retreive_memory(struct rpt *myrpt, char *memory) +{ + char tmp[30], *s, *s1, *val; + + val = (char *) ast_variable_retrieve(myrpt->cfg, myrpt->p.memory, memory); + if (!val){ + return -1; + } + strncpy(tmp,val,sizeof(tmp) - 1); + tmp[sizeof(tmp)-1] = 0; + + s = strchr(tmp,','); + if (!s) + return 1; + *s++ = 0; + s1 = strchr(s,','); + if (!s1) + return 1; + *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; + default: + return 1; + } + } + return 0; +} + + + +/* +* 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; + int i,j,p,r,ht,k,l,ls2,m,d,offset,offsave, modesave, defmode; + char multimode = 0; + char oc,*cp,*cp1,*cp2; + char tmp[20], freq[20] = "", savestr[20] = ""; + char mhz[MAXREMSTR], decimals[MAXREMSTR]; + + if((!param) || (command_source == SOURCE_RPT) || (command_source == SOURCE_LNK)) + return DC_ERROR; + + p = myatoi(param); + + if ((p != 99) && (p != 5) && (p != 140) && myrpt->p.authlevel && + (!myrpt->loginlevel[0])) return DC_ERROR; + multimode = multimode_capable(myrpt); + + switch(p){ + + 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; + } + + r = retreive_memory(myrpt, digitbuf); + if (r < 0){ + rpt_telemetry(myrpt,MEMNOTFOUND,NULL); + return DC_COMPLETE; + } + if (r > 0){ + return DC_ERROR; + } + 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: + rpt_telemetry(myrpt,INVFREQ,NULL); + 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(!strcmp(myrpt->remote, remote_rig_rbi)) + { + strncpy(myrpt->txpl, tmp, sizeof(myrpt->txpl) - 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 */ + /* cant set tx tone on RBI (rx tone does both) */ + if(!strcmp(myrpt->remote, remote_rig_rbi)) + return DC_ERROR; + if(!strcmp(myrpt->remote, remote_rig_ic706)) + return DC_ERROR; + 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; + + rpt_telemetry(myrpt,REMMODE,NULL); + break; + + case '2': + myrpt->remmode = REM_MODE_USB; + rpt_telemetry(myrpt,REMMODE,NULL); + break; + + case '3': + myrpt->remmode = REM_MODE_LSB; + rpt_telemetry(myrpt,REMMODE,NULL); + break; + + case '4': + myrpt->remmode = REM_MODE_AM; + rpt_telemetry(myrpt,REMMODE,NULL); + break; + + default: + return DC_ERROR; + } + + if(setrem(myrpt)) + return DC_ERROR; + return DC_COMPLETEQUIET; + case 99: + /* cant log in when logged in */ + if (myrpt->loginlevel[0]) + return DC_ERROR; + *myrpt->loginuser = 0; + myrpt->loginlevel[0] = 0; + cp = strdup(param); + cp1 = strchr(cp,','); + ast_mutex_lock(&myrpt->lock); + if (cp1) + { + *cp1 = 0; + cp2 = strchr(cp1 + 1,','); + if (cp2) + { + *cp2 = 0; + strncpy(myrpt->loginlevel,cp2 + 1, + sizeof(myrpt->loginlevel) - 1); + } + strncpy(myrpt->loginuser,cp1 + 1,sizeof(myrpt->loginuser)); + ast_mutex_unlock(&myrpt->lock); + if (myrpt->p.archivedir) + { + char str[100]; + + sprintf(str,"LOGIN,%s,%s", + myrpt->loginuser,myrpt->loginlevel); + donodelog(myrpt,str); + } + if (debug) + printf("loginuser %s level %s\n",myrpt->loginuser,myrpt->loginlevel); + rpt_telemetry(myrpt,REMLOGIN,NULL); + } + free(cp); + return DC_COMPLETEQUIET; + case 100: /* RX PL Off */ + myrpt->rxplon = 0; + setrem(myrpt); + rpt_telemetry(myrpt,REMXXX,(void *)p); + return DC_COMPLETEQUIET; + case 101: /* RX PL On */ + myrpt->rxplon = 1; + setrem(myrpt); + rpt_telemetry(myrpt,REMXXX,(void *)p); + return DC_COMPLETEQUIET; + case 102: /* TX PL Off */ + myrpt->txplon = 0; + setrem(myrpt); + rpt_telemetry(myrpt,REMXXX,(void *)p); + return DC_COMPLETEQUIET; + case 103: /* TX PL On */ + myrpt->txplon = 1; + setrem(myrpt); + rpt_telemetry(myrpt,REMXXX,(void *)p); + return DC_COMPLETEQUIET; + case 104: /* Low Power */ + if(!strcmp(myrpt->remote, remote_rig_ic706)) + return DC_ERROR; + myrpt->powerlevel = REM_LOWPWR; + setrem(myrpt); + rpt_telemetry(myrpt,REMXXX,(void *)p); + return DC_COMPLETEQUIET; + case 105: /* Medium Power */ + if(!strcmp(myrpt->remote, remote_rig_ic706)) + return DC_ERROR; + myrpt->powerlevel = REM_MEDPWR; + setrem(myrpt); + rpt_telemetry(myrpt,REMXXX,(void *)p); + return DC_COMPLETEQUIET; + case 106: /* Hi Power */ + if(!strcmp(myrpt->remote, remote_rig_ic706)) + return DC_ERROR; + myrpt->powerlevel = REM_HIPWR; + setrem(myrpt); + rpt_telemetry(myrpt,REMXXX,(void *)p); + return DC_COMPLETEQUIET; + 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: /* Scan down slow */ + myrpt->scantimer = REM_SCANTIME; + myrpt->hfscanmode = HF_SCAN_DOWN_SLOW; + rpt_telemetry(myrpt,REMXXX,(void *)p); + return DC_COMPLETEQUIET; + case 114: /* Scan down quick */ + myrpt->scantimer = REM_SCANTIME; + myrpt->hfscanmode = HF_SCAN_DOWN_QUICK; + rpt_telemetry(myrpt,REMXXX,(void *)p); + return DC_COMPLETEQUIET; + case 115: /* Scan down fast */ + myrpt->scantimer = REM_SCANTIME; + myrpt->hfscanmode = HF_SCAN_DOWN_FAST; + rpt_telemetry(myrpt,REMXXX,(void *)p); + return DC_COMPLETEQUIET; + case 116: /* Scan up slow */ + myrpt->scantimer = REM_SCANTIME; + myrpt->hfscanmode = HF_SCAN_UP_SLOW; + rpt_telemetry(myrpt,REMXXX,(void *)p); + return DC_COMPLETEQUIET; + case 117: /* Scan up quick */ + myrpt->scantimer = REM_SCANTIME; + myrpt->hfscanmode = HF_SCAN_UP_QUICK; + rpt_telemetry(myrpt,REMXXX,(void *)p); + return DC_COMPLETEQUIET; + case 118: /* Scan up fast */ + myrpt->scantimer = REM_SCANTIME; + myrpt->hfscanmode = HF_SCAN_UP_FAST; + rpt_telemetry(myrpt,REMXXX,(void *)p); + return DC_COMPLETEQUIET; + case 119: /* Tune Request */ + /* if not currently going, and valid to do */ + if((!myrpt->tunerequest) && + ((!strcmp(myrpt->remote, remote_rig_ft897) || + !strcmp(myrpt->remote, remote_rig_ic706)) )) { + myrpt->remotetx = 0; + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY); + myrpt->tunerequest = 1; + rpt_telemetry(myrpt,TUNE,NULL); + return DC_COMPLETEQUIET; + } + return DC_ERROR; + case 5: /* Long Status */ + rpt_telemetry(myrpt,REMLONGSTATUS,NULL); + return DC_COMPLETEQUIET; + case 140: /* Short Status */ + rpt_telemetry(myrpt,REMSHORTSTATUS,NULL); + return DC_COMPLETEQUIET; + case 200: + case 201: + case 202: + case 203: + case 204: + case 205: + case 206: + case 207: + case 208: + case 209: + case 210: + case 211: + case 212: + case 213: + case 214: + case 215: + do_dtmf_local(myrpt,remdtmfstr[p - 200]); + return DC_COMPLETEQUIET; + default: + break; + } + 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; + + time(&myrpt->last_activity_time); + /* Stop scan mode if in scan mode */ + if(myrpt->hfscanmode){ + stop_scan(myrpt); + 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->p.funcchar) + { + if (!myrpt->p.propagate_dtmf) + { + rpt_mutex_lock(&myrpt->lock); + do_dtmf_local(myrpt,c); + rpt_mutex_unlock(&myrpt->lock); + } + 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->p.funcchar) + { + /* if star at beginning, or 2 together, erase buffer */ + if ((myrpt->dtmfidx < 1) || + (myrpt->dtmfbuf[myrpt->dtmfidx - 1] == myrpt->p.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: + res = 1; + case DC_COMPLETEQUIET: + myrpt->totalexecdcommands++; + myrpt->dailyexecdcommands++; + strncpy(myrpt->lastdtmfcommand, myrpt->dtmfbuf, MAXDTMF-1); + myrpt->lastdtmfcommand[MAXDTMF-1] = '\0'; + myrpt->dtmfbuf[0] = 0; + myrpt->dtmfidx = -1; + myrpt->dtmf_time_rem = 0; + 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; + +#ifndef DO_NOT_NOTIFY_MDC1200_ON_REMOTE_BASES + if (tmp[0] == 'I') + { + if (sscanf(tmp,"%s %s %x",cmd,src,&seq) != 3) + { + ast_log(LOG_WARNING, "Unable to parse ident string %s\n",str); + return 0; + } + mdc1200_notify(myrpt,src,seq); + return 0; + } +#endif + 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; + if (myrpt->p.archivedir) + { + char str[100]; + + sprintf(str,"DTMF,%c",c); + donodelog(myrpt,str); + } + c = func_xlat(myrpt,c,&myrpt->p.outxlat); + if (!c) return(0); + res = handle_remote_dtmf_digit(myrpt,c, NULL, 0); + if (res != 1) + return res; + rpt_telemetry(myrpt,COMPLETE,NULL); + return 0; +} + +static int handle_remote_phone_dtmf(struct rpt *myrpt, char c, char *keyed, int phonemode) +{ +int res; + + + if (keyed && *keyed && (c == myrpt->p.endchar)) + { + *keyed = 0; + return DC_INDETERMINATE; + } + + if (myrpt->p.archivedir) + { + char str[100]; + + sprintf(str,"DTMF(P),%c",c); + donodelog(myrpt,str); + } + res = handle_remote_dtmf_digit(myrpt,c,keyed, phonemode); + if (res != 1) + return res; + rpt_telemetry(myrpt,COMPLETE,NULL); + return 0; +} + +static int attempt_reconnect(struct rpt *myrpt, struct rpt_link *l) +{ + char *val, *s, *s1, *s2, *tele; + char tmp[300], deststr[300] = ""; + + val = node_lookup(myrpt,l->name); + if (!val) + { + fprintf(stderr,"attempt_reconnect: cannot find node %s\n",l->name); + return -1; + } + + rpt_mutex_lock(&myrpt->lock); + /* remove from queue */ + remque((struct qelem *) l); + rpt_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->connecttime = 0; + l->thisconnected = 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; + } + rpt_mutex_lock(&myrpt->lock); + /* put back in queue */ + insque((struct qelem *)l,(struct qelem *)myrpt->links.next); + rpt_mutex_unlock(&myrpt->lock); + ast_log(LOG_NOTICE,"Reconnect Attempt to %s in process\n",l->name); + return 0; +} + +/* 0 return=continue, 1 return = break, -1 return = error */ +static void local_dtmf_helper(struct rpt *myrpt,char c) +{ +int res; +pthread_attr_t attr; +char cmd[MAXDTMF+1] = ""; + + if (myrpt->p.archivedir) + { + char str[100]; + + sprintf(str,"DTMF,MAIN,%c",c); + donodelog(myrpt,str); + } + if (c == myrpt->p.endchar) + { + /* if in simple mode, kill autopatch */ + if (myrpt->p.simple && myrpt->callmode) + { + rpt_mutex_lock(&myrpt->lock); + myrpt->callmode = 0; + rpt_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,TERM,NULL); + return; + } + rpt_mutex_lock(&myrpt->lock); + myrpt->stopgen = 1; + if (myrpt->cmdnode[0]) + { + myrpt->cmdnode[0] = 0; + myrpt->dtmfidx = -1; + myrpt->dtmfbuf[0] = 0; + rpt_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,COMPLETE,NULL); + } + else + { + rpt_mutex_unlock(&myrpt->lock); + if (myrpt->p.propagate_phonedtmf) + do_dtmf_phone(myrpt,NULL,c); + } + return; + } + rpt_mutex_lock(&myrpt->lock); + if (myrpt->cmdnode[0]) + { + rpt_mutex_unlock(&myrpt->lock); + send_link_dtmf(myrpt,c); + return; + } + if (!myrpt->p.simple) + { + if (c == myrpt->p.funcchar) + { + myrpt->dtmfidx = 0; + myrpt->dtmfbuf[myrpt->dtmfidx] = 0; + rpt_mutex_unlock(&myrpt->lock); + time(&myrpt->dtmf_time); + return; + } + else if ((c != myrpt->p.endchar) && (myrpt->dtmfidx >= 0)) + { + time(&myrpt->dtmf_time); + + if (myrpt->dtmfidx < MAXDTMF) + { + myrpt->dtmfbuf[myrpt->dtmfidx++] = c; + myrpt->dtmfbuf[myrpt->dtmfidx] = 0; + + strncpy(cmd, myrpt->dtmfbuf, sizeof(cmd) - 1); + + rpt_mutex_unlock(&myrpt->lock); + res = collect_function_digits(myrpt, cmd, SOURCE_RPT, NULL); + rpt_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: + case DC_COMPLETEQUIET: + myrpt->totalexecdcommands++; + myrpt->dailyexecdcommands++; + strncpy(myrpt->lastdtmfcommand, cmd, MAXDTMF-1); + myrpt->lastdtmfcommand[MAXDTMF-1] = '\0'; + myrpt->dtmfbuf[0] = 0; + myrpt->dtmfidx = -1; + myrpt->dtmf_time = 0; + break; + + case DC_ERROR: + default: + myrpt->dtmfbuf[0] = 0; + myrpt->dtmfidx = -1; + myrpt->dtmf_time = 0; + break; + } + if(res != DC_INDETERMINATE) { + rpt_mutex_unlock(&myrpt->lock); + return; + } + } + } + } + else /* if simple */ + { + if ((!myrpt->callmode) && (c == myrpt->p.funcchar)) + { + myrpt->callmode = 1; + myrpt->patchnoct = 0; + myrpt->patchquiet = 0; + myrpt->patchfarenddisconnect = 0; + myrpt->patchdialtime = 0; + strncpy(myrpt->patchcontext, myrpt->p.ourcontext, MAXPATCHCONTEXT); + myrpt->cidx = 0; + myrpt->exten[myrpt->cidx] = 0; + rpt_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; + } + } + if (myrpt->callmode == 1) + { + myrpt->exten[myrpt->cidx++] = c; + myrpt->exten[myrpt->cidx] = 0; + /* if this exists */ + if (ast_exists_extension(myrpt->pchannel,myrpt->patchcontext,myrpt->exten,1,NULL)) + { + myrpt->callmode = 2; + rpt_mutex_unlock(&myrpt->lock); + if(!myrpt->patchquiet) + rpt_telemetry(myrpt,PROC,NULL); + return; + } + /* if can continue, do so */ + if (!ast_canmatch_extension(myrpt->pchannel,myrpt->patchcontext,myrpt->exten,1,NULL)) + { + /* call has failed, inform user */ + myrpt->callmode = 4; + } + rpt_mutex_unlock(&myrpt->lock); + return; + } + if ((myrpt->callmode == 2) || (myrpt->callmode == 3)) + { + myrpt->mydtmf = c; + } + rpt_mutex_unlock(&myrpt->lock); + if ((myrpt->dtmfidx < 0) && myrpt->p.propagate_phonedtmf) + do_dtmf_phone(myrpt,NULL,c); + return; +} + + +/* place an ID event in the telemetry queue */ + +static void queue_id(struct rpt *myrpt) +{ + if(myrpt->p.idtime){ /* ID time must be non-zero */ + myrpt->mustid = myrpt->tailid = 0; + myrpt->idtimer = myrpt->p.idtime; /* Reset our ID timer */ + rpt_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,ID,NULL); + rpt_mutex_lock(&myrpt->lock); + } +} + +/* Scheduler */ +/* must be called locked */ + +static void do_scheduler(struct rpt *myrpt) +{ + int i,res; + struct tm tmnow; + struct ast_variable *skedlist; + char *strs[5],*vp,*val,value[100]; + + memcpy(&myrpt->lasttv, &myrpt->curtv, sizeof(struct timeval)); + + if( (res = gettimeofday(&myrpt->curtv, NULL)) < 0) + ast_log(LOG_NOTICE, "Scheduler gettime of day returned: %s\n", strerror(res)); + + /* Try to get close to a 1 second resolution */ + + if(myrpt->lasttv.tv_sec == myrpt->curtv.tv_sec) + return; + + rpt_localtime(&myrpt->curtv.tv_sec, &tmnow); + + /* If midnight, then reset all daily statistics */ + + if((tmnow.tm_hour == 0)&&(tmnow.tm_min == 0)&&(tmnow.tm_sec == 0)){ + myrpt->dailykeyups = 0; + myrpt->dailytxtime = 0; + myrpt->dailykerchunks = 0; + myrpt->dailyexecdcommands = 0; + } + + if(tmnow.tm_sec != 0) + return; + + /* Code below only executes once per minute */ + + + /* Don't schedule if remote */ + + if (myrpt->remote) + return; + + /* Don't schedule if disabled */ + + if(myrpt->p.s[myrpt->p.sysstate_cur].schedulerdisable){ + if(debug > 6) + ast_log(LOG_NOTICE, "Scheduler disabled\n"); + return; + } + + if(!myrpt->p.skedstanzaname){ /* No stanza means we do nothing */ + if(debug > 6) + ast_log(LOG_NOTICE,"No stanza for scheduler in rpt.conf\n"); + return; + } + + /* get pointer to linked list of scheduler entries */ + skedlist = ast_variable_browse(myrpt->cfg, myrpt->p.skedstanzaname); + + if(debug > 6){ + ast_log(LOG_NOTICE, "Time now: %02d:%02d %02d %02d %02d\n", + tmnow.tm_hour,tmnow.tm_min,tmnow.tm_mday,tmnow.tm_mon + 1, tmnow.tm_wday); + } + /* walk the list */ + for(; skedlist; skedlist = skedlist->next){ + if(debug > 6) + ast_log(LOG_NOTICE, "Scheduler entry %s = %s being considered\n",skedlist->name, skedlist->value); + strncpy(value,skedlist->value,99); + value[99] = 0; + /* point to the substrings for minute, hour, dom, month, and dow */ + for( i = 0, vp = value ; i < 5; i++){ + if(!*vp) + break; + while((*vp == ' ') || (*vp == 0x09)) /* get rid of any leading white space */ + vp++; + strs[i] = vp; /* save pointer to beginning of substring */ + while((*vp != ' ') && (*vp != 0x09) && (*vp != 0)) /* skip over substring */ + vp++; + if(*vp) + *vp++ = 0; /* mark end of substring */ + } + if(debug > 6) + ast_log(LOG_NOTICE, "i = %d, min = %s, hour = %s, mday=%s, mon=%s, wday=%s\n",i, + strs[0], strs[1], strs[2], strs[3], strs[4]); + if(i == 5){ + if((*strs[0] != '*')&&(atoi(strs[0]) != tmnow.tm_min)) + continue; + if((*strs[1] != '*')&&(atoi(strs[1]) != tmnow.tm_hour)) + continue; + if((*strs[2] != '*')&&(atoi(strs[2]) != tmnow.tm_mday)) + continue; + if((*strs[3] != '*')&&(atoi(strs[3]) != tmnow.tm_mon + 1)) + continue; + if(atoi(strs[4]) == 7) + strs[4] = "0"; + if((*strs[4] != '*')&&(atoi(strs[4]) != tmnow.tm_wday)) + continue; + if(debug) + ast_log(LOG_NOTICE, "Executing scheduler entry %s = %s\n", skedlist->name, skedlist->value); + if(atoi(skedlist->name) == 0) + return; /* Zero is reserved for the startup macro */ + val = (char *) ast_variable_retrieve(myrpt->cfg, myrpt->p.macro, skedlist->name); + if (!val){ + ast_log(LOG_WARNING,"Scheduler could not find macro %s\n",skedlist->name); + return; /* Macro not found */ + } + if ((MAXMACRO - strlen(myrpt->macrobuf)) < strlen(val)){ + ast_log(LOG_WARNING, "Scheduler could not execute macro %s: Macro buffer full\n", + skedlist->name); + return; /* Macro buffer full */ + } + myrpt->macrotimer = MACROTIME; + strncat(myrpt->macrobuf,val,MAXMACRO - strlen(myrpt->macrobuf) - 1); + } + else{ + ast_log(LOG_WARNING,"Malformed scheduler entry in rpt.conf: %s = %s\n", + skedlist->name, skedlist->value); + } + } + +} + +/* single thread with one file (request) to dial */ +static void *rpt(void *this) +{ +struct rpt *myrpt = (struct rpt *)this; +char *tele,*idtalkover,c; +int ms = MSWAIT,i,lasttx=0,val,remrx=0,identqueued,othertelemqueued; +int tailmessagequeued,ctqueued,dtmfed; +struct ast_channel *who; +struct dahdi_confinfo ci; /* conference info */ +time_t t; +struct rpt_link *l,*m; +struct rpt_tele *telem; +char tmpstr[300],lstr[MAXLINKLIST]; + + + if (myrpt->p.archivedir) mkdir(myrpt->p.archivedir,0600); + sprintf(tmpstr,"%s/%s",myrpt->p.archivedir,myrpt->name); + mkdir(tmpstr,0600); + rpt_mutex_lock(&myrpt->lock); + + telem = myrpt->tele.next; + while(telem != &myrpt->tele) + { + ast_softhangup(telem->chan,AST_SOFTHANGUP_DEV); + telem = telem->next; + } + rpt_mutex_unlock(&myrpt->lock); + /* find our index, and load the vars initially */ + for(i = 0; i < nrpts; i++) + { + if (&rpt_vars[i] == myrpt) + { + load_rpt_vars(i,0); + break; + } + } + rpt_mutex_lock(&myrpt->lock); + strncpy(tmpstr,myrpt->rxchanname,sizeof(tmpstr) - 1); + tele = strchr(tmpstr,'/'); + if (!tele) + { + fprintf(stderr,"rpt:Rxchannel Dial number (%s) must be in format tech/number\n",myrpt->rxchanname); + rpt_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); + myrpt->zaprxchannel = NULL; + if (!strcasecmp(tmpstr,"Zap")) + myrpt->zaprxchannel = myrpt->rxchannel; + if (myrpt->rxchannel) + { + if (myrpt->rxchannel->_state == AST_STATE_BUSY) + { + fprintf(stderr,"rpt:Sorry unable to obtain Rx channel\n"); + rpt_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); +#ifdef AST_CDR_FLAG_POST_DISABLED + ast_set_flag(myrpt->rxchannel->cdr,AST_CDR_FLAG_POST_DISABLED); +#endif + 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) + { + rpt_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"); + rpt_mutex_unlock(&myrpt->lock); + myrpt->rpt_thread = AST_PTHREADT_STOP; + pthread_exit(NULL); + } + myrpt->zaptxchannel = NULL; + if (myrpt->txchanname) + { + strncpy(tmpstr,myrpt->txchanname,sizeof(tmpstr) - 1); + tele = strchr(tmpstr,'/'); + if (!tele) + { + fprintf(stderr,"rpt:Txchannel Dial number (%s) must be in format tech/number\n",myrpt->txchanname); + rpt_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 (!strcasecmp(tmpstr,"Zap")) + myrpt->zaptxchannel = myrpt->txchannel; + if (myrpt->txchannel) + { + if (myrpt->txchannel->_state == AST_STATE_BUSY) + { + fprintf(stderr,"rpt:Sorry unable to obtain Tx channel\n"); + rpt_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); +#ifdef AST_CDR_FLAG_POST_DISABLED + ast_set_flag(myrpt->txchannel->cdr,AST_CDR_FLAG_POST_DISABLED); +#endif + 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) + { + rpt_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"); + rpt_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"); + rpt_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); + } +#ifdef AST_CDR_FLAG_POST_DISABLED + ast_set_flag(myrpt->pchannel->cdr,AST_CDR_FLAG_POST_DISABLED); +#endif + if (!myrpt->zaprxchannel) myrpt->zaprxchannel = myrpt->pchannel; + if (!myrpt->zaptxchannel) + { + /* allocate a pseudo-channel thru asterisk */ + myrpt->zaptxchannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL); + if (!myrpt->zaptxchannel) + { + fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); + rpt_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); + } + ast_set_read_format(myrpt->zaptxchannel,AST_FORMAT_SLINEAR); + ast_set_write_format(myrpt->zaptxchannel,AST_FORMAT_SLINEAR); +#ifdef AST_CDR_FLAG_POST_DISABLED + ast_set_flag(myrpt->zaptxchannel->cdr,AST_CDR_FLAG_POST_DISABLED); +#endif + } + /* allocate a pseudo-channel thru asterisk */ + myrpt->monchannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL); + if (!myrpt->monchannel) + { + fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); + rpt_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); + } + ast_set_read_format(myrpt->monchannel,AST_FORMAT_SLINEAR); + ast_set_write_format(myrpt->monchannel,AST_FORMAT_SLINEAR); +#ifdef AST_CDR_FLAG_POST_DISABLED + ast_set_flag(myrpt->monchannel->cdr,AST_CDR_FLAG_POST_DISABLED); +#endif + /* make a conference for the tx */ + ci.chan = 0; + ci.confno = -1; /* make a new conf */ + ci.confmode = DAHDI_CONF_CONF | DAHDI_CONF_LISTENER; + /* first put the channel on the conference in proper mode */ + if (ioctl(myrpt->zaptxchannel->fds[0],DAHDI_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + rpt_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->pchannel); + ast_hangup(myrpt->monchannel); + 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 = ((myrpt->p.duplex == 2) || (myrpt->p.duplex == 4)) ? DAHDI_CONF_CONFANNMON : + (DAHDI_CONF_CONF | DAHDI_CONF_LISTENER | DAHDI_CONF_TALKER); + /* first put the channel on the conference in announce mode */ + if (ioctl(myrpt->pchannel->fds[0],DAHDI_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + rpt_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->pchannel); + ast_hangup(myrpt->monchannel); + 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; + /* make a conference for the pseudo */ + ci.chan = 0; + if ((strstr(myrpt->txchannel->name,"pseudo") == NULL) && + (myrpt->zaptxchannel == myrpt->txchannel)) + { + /* get tx channel's port number */ + if (ioctl(myrpt->txchannel->fds[0],DAHDI_CHANNO,&ci.confno) == -1) + { + ast_log(LOG_WARNING, "Unable to set tx channel's chan number\n"); + rpt_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->pchannel); + ast_hangup(myrpt->monchannel); + if (myrpt->txchannel != myrpt->rxchannel) + ast_hangup(myrpt->txchannel); + ast_hangup(myrpt->rxchannel); + myrpt->rpt_thread = AST_PTHREADT_STOP; + pthread_exit(NULL); + } + ci.confmode = DAHDI_CONF_MONITORTX; + } + else + { + ci.confno = myrpt->txconf; + ci.confmode = DAHDI_CONF_CONFANNMON; + } + /* first put the channel on the conference in announce mode */ + if (ioctl(myrpt->monchannel->fds[0],DAHDI_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode for monitor\n"); + rpt_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->pchannel); + ast_hangup(myrpt->monchannel); + if (myrpt->txchannel != myrpt->rxchannel) + ast_hangup(myrpt->txchannel); + ast_hangup(myrpt->rxchannel); + myrpt->rpt_thread = AST_PTHREADT_STOP; + pthread_exit(NULL); + } + /* 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"); + rpt_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->pchannel); + ast_hangup(myrpt->monchannel); + if (myrpt->txchannel != myrpt->rxchannel) + ast_hangup(myrpt->txchannel); + ast_hangup(myrpt->rxchannel); + myrpt->rpt_thread = AST_PTHREADT_STOP; + pthread_exit(NULL); + } +#ifdef AST_CDR_FLAG_POST_DISABLED + ast_set_flag(myrpt->txpchannel->cdr,AST_CDR_FLAG_POST_DISABLED); +#endif + /* make a conference for the tx */ + ci.chan = 0; + ci.confno = myrpt->txconf; + ci.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER ; + /* first put the channel on the conference in proper mode */ + if (ioctl(myrpt->txpchannel->fds[0],DAHDI_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + rpt_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->txpchannel); + ast_hangup(myrpt->monchannel); + 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->tmsgtimer = myrpt->p.tailmessagetime; + myrpt->idtimer = myrpt->p.politeid; + myrpt->mustid = myrpt->tailid = 0; + myrpt->callmode = 0; + myrpt->tounkeyed = 0; + myrpt->tonotify = 0; + myrpt->retxtimer = 0; + myrpt->rerxtimer = 0; + myrpt->skedtimer = 0; + myrpt->tailevent = 0; + lasttx = 0; + myrpt->keyed = 0; + idtalkover = (char *) ast_variable_retrieve(myrpt->cfg, myrpt->name, "idtalkover"); + myrpt->dtmfidx = -1; + myrpt->dtmfbuf[0] = 0; + myrpt->rem_dtmfidx = -1; + myrpt->rem_dtmfbuf[0] = 0; + myrpt->dtmf_time = 0; + myrpt->rem_dtmf_time = 0; + myrpt->disgorgetime = 0; + myrpt->lastnodewhichkeyedusup[0] = '\0'; + myrpt->dailytxtime = 0; + myrpt->totaltxtime = 0; + myrpt->dailykeyups = 0; + myrpt->totalkeyups = 0; + myrpt->dailykerchunks = 0; + myrpt->totalkerchunks = 0; + myrpt->dailyexecdcommands = 0; + myrpt->totalexecdcommands = 0; + myrpt->timeouts = 0; + myrpt->exten[0] = '\0'; + myrpt->lastdtmfcommand[0] = '\0'; + if (myrpt->p.startupmacro) + { + snprintf(myrpt->macrobuf,MAXMACRO - 1,"PPPP%s",myrpt->p.startupmacro); + } + rpt_mutex_unlock(&myrpt->lock); + val = 1; + ast_channel_setoption(myrpt->rxchannel,AST_OPTION_RELAXDTMF,&val,sizeof(char),0); + val = 1; + ast_channel_setoption(myrpt->rxchannel,AST_OPTION_TONE_VERIFY,&val,sizeof(char),0); + if (myrpt->p.archivedir) donodelog(myrpt,"STARTUP"); + dtmfed = 0; + while (ms >= 0) + { + struct ast_frame *f,*f1,*f2; + struct ast_channel *cs[300],*cs1[300]; + int totx=0,elap=0,n,x,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->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); + ast_log(LOG_NOTICE,"myrpt->tailevent = %d\n",myrpt->tailevent); + + 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); + ast_log(LOG_NOTICE," link->reconnects = %d\n",zl->reconnects); + 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"); + + } + + + if (myrpt->reload) + { + struct rpt_tele *telem; + + rpt_mutex_lock(&myrpt->lock); + telem = myrpt->tele.next; + while(telem != &myrpt->tele) + { + ast_softhangup(telem->chan,AST_SOFTHANGUP_DEV); + telem = telem->next; + } + myrpt->reload = 0; + rpt_mutex_unlock(&myrpt->lock); + usleep(10000); + /* find our index, and load the vars */ + for(i = 0; i < nrpts; i++) + { + if (&rpt_vars[i] == myrpt) + { + load_rpt_vars(i,0); + break; + } + } + } + + rpt_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->monchannel)) break; + if (ast_check_hangup(myrpt->txpchannel)) break; + if (myrpt->zaptxchannel && ast_check_hangup(myrpt->zaptxchannel)) break; + + /* Set local tx with keyed */ + myrpt->localtx = myrpt->keyed; + /* 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; + if(l->name[0] != '0') /* Ignore '0' nodes */ + strcpy(myrpt->lastnodewhichkeyedusup, l->name); /* Note the node which is doing the key up */ + } + l = l->next; + } + /* Create a "must_id" flag for the cleanup ID */ + if(myrpt->p.idtime) /* ID time must be non-zero */ + myrpt->mustid |= (myrpt->idtimer) && (myrpt->keyed || remrx) ; + /* Build a fresh totx from myrpt->keyed and autopatch activated */ + totx = myrpt->callmode; + /* If full duplex, add local tx to totx */ + if (myrpt->p.duplex > 1) + { + totx = totx || myrpt->localtx; + } + /* Traverse the telemetry list to see what's queued */ + identqueued = 0; + othertelemqueued = 0; + tailmessagequeued = 0; + ctqueued = 0; + telem = myrpt->tele.next; + while(telem != &myrpt->tele) + { + if((telem->mode == ID) || (telem->mode == IDTALKOVER)){ + identqueued = 1; /* Identification telemetry */ + } + else if(telem->mode == TAILMSG) + { + tailmessagequeued = 1; /* Tail message telemetry */ + } + else + { + if ((telem->mode != UNKEY) && (telem->mode != LINKUNKEY)) + othertelemqueued = 1; /* Other telemetry */ + else + ctqueued = 1; /* Courtesy tone telemetry */ + } + telem = telem->next; + } + + /* Add in any "other" telemetry, unless specified otherwise */ + if (!myrpt->p.notelemtx) totx = totx || othertelemqueued; + /* Update external (to links) transmitter PTT state with everything but ID, CT, and tailmessage telemetry */ + myrpt->exttx = totx; + totx = totx || myrpt->dtmf_local_timer; + /* If half or 3/4 duplex, add localtx to external link tx */ + if (myrpt->p.duplex < 2) myrpt->exttx = myrpt->exttx || myrpt->localtx; + /* Add in ID telemetry to local transmitter */ + totx = totx || remrx; + /* If 3/4 or full duplex, add in ident and CT telemetry */ + if (myrpt->p.duplex > 0) + totx = totx || identqueued || ctqueued; + /* If full duplex, add local dtmf stuff active */ + if (myrpt->p.duplex > 1) + { + totx = totx || (myrpt->dtmfidx > -1) || + myrpt->cmdnode[0]; + } + /* Reset time out timer variables if there is no activity */ + if (!totx) + { + myrpt->totimer = myrpt->p.totime; + myrpt->tounkeyed = 0; + myrpt->tonotify = 0; + } + else{ + myrpt->tailtimer = myrpt->p.s[myrpt->p.sysstate_cur].alternatetail ? + myrpt->p.althangtime : /* Initialize tail timer */ + myrpt->p.hangtime; + } + /* Disable the local transmitter if we are timed out */ + totx = totx && myrpt->totimer; + /* if timed-out and not said already, say it */ + if ((!myrpt->totimer) && (!myrpt->tonotify)) + { + myrpt->tonotify = 1; + myrpt->timeouts++; + rpt_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,TIMEOUT,NULL); + rpt_mutex_lock(&myrpt->lock); + } + + /* If unkey and re-key, reset time out timer */ + if ((!totx) && (!myrpt->totimer) && (!myrpt->tounkeyed) && (!myrpt->keyed)) + { + myrpt->tounkeyed = 1; + } + if ((!totx) && (!myrpt->totimer) && myrpt->tounkeyed && myrpt->keyed) + { + myrpt->totimer = myrpt->p.totime; + myrpt->tounkeyed = 0; + myrpt->tonotify = 0; + rpt_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 tail message, kill the message if someone keys up over it */ + if ((myrpt->keyed || remrx) && ((identqueued && idtalkover) || (tailmessagequeued))) { + 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 == TAILMSG){ + if (telem->chan) ast_softhangup(telem->chan, AST_SOFTHANGUP_DEV); /* Whoosh! */ + } + if (telem->mode == IDTALKOVER) hastalkover = 1; + telem = telem->next; + } + rpt_mutex_unlock(&myrpt->lock); + if (hasid && (!hastalkover)) rpt_telemetry(myrpt, IDTALKOVER, NULL); /* Start Talkover ID */ + rpt_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(myrpt->mustid && (!myrpt->idtimer)) + queue_id(myrpt); + + if ((myrpt->p.idtime && totx && (!myrpt->exttx) && + (myrpt->idtimer <= myrpt->p.politeid) && myrpt->tailtimer)) /* ID time must be non-zero */ + { + myrpt->tailid = 1; + } + + /* If tail timer expires, then check for tail messages */ + + if(myrpt->tailevent){ + myrpt->tailevent = 0; + if(myrpt->tailid){ + totx = 1; + queue_id(myrpt); + } + else if ((myrpt->p.tailmessages[0]) && + (myrpt->p.tailmessagetime) && (myrpt->tmsgtimer == 0)){ + totx = 1; + myrpt->tmsgtimer = myrpt->p.tailmessagetime; + rpt_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt, TAILMSG, NULL); + rpt_mutex_lock(&myrpt->lock); + } + } + + /* Main TX control */ + + /* let telemetry transmit anyway (regardless of timeout) */ + if (myrpt->p.duplex > 0) totx = totx || (myrpt->tele.next != &myrpt->tele); + if (totx && (!lasttx)) + { + char mydate[100],myfname[100]; + time_t myt; + + if (myrpt->monstream) ast_closestream(myrpt->monstream); + if (myrpt->p.archivedir) + { + long blocksleft; + + time(&myt); + strftime(mydate,sizeof(mydate) - 1,"%Y%m%d%H%M%S", + localtime(&myt)); + sprintf(myfname,"%s/%s/%s",myrpt->p.archivedir, + myrpt->name,mydate); + myrpt->monstream = ast_writefile(myfname,"wav49", + "app_rpt Air Archive",O_CREAT | O_APPEND,0,0600); + if (myrpt->p.monminblocks) + { + blocksleft = diskavail(myrpt); + if (blocksleft >= myrpt->p.monminblocks) + donodelog(myrpt,"TXKEY,MAIN"); + } else donodelog(myrpt,"TXKEY,MAIN"); + } + lasttx = 1; + myrpt->dailykeyups++; + myrpt->totalkeyups++; + rpt_mutex_unlock(&myrpt->lock); + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_KEY); + rpt_mutex_lock(&myrpt->lock); + } + totx = totx && !myrpt->p.s[myrpt->p.sysstate_cur].txdisable; + if ((!totx) && lasttx) + { + if (myrpt->monstream) ast_closestream(myrpt->monstream); + myrpt->monstream = NULL; + + lasttx = 0; + rpt_mutex_unlock(&myrpt->lock); + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY); + rpt_mutex_lock(&myrpt->lock); + donodelog(myrpt,"TXUNKEY,MAIN"); + } + time(&t); + /* if DTMF timeout */ + if ((!myrpt->cmdnode[0]) && (myrpt->dtmfidx >= 0) && ((myrpt->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 */ + + 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; + rpt_mutex_unlock(&myrpt->lock); + /* hang-up on call to device */ + if (l->chan) ast_hangup(l->chan); + ast_hangup(l->pchan); + free(l); + rpt_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->monchannel; + cs[n++] = myrpt->txpchannel; + if (myrpt->txchannel != myrpt->rxchannel) cs[n++] = myrpt->txchannel; + if (myrpt->zaptxchannel != myrpt->txchannel) + cs[n++] = myrpt->zaptxchannel; + 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; + } + rpt_mutex_unlock(&myrpt->lock); + ms = MSWAIT; + for(x = 0; x < n; x++) + { + int s = -(-x - myrpt->scram - 1) % n; + cs1[x] = cs[s]; + } + myrpt->scram++; + who = ast_waitfor_n(cs1,n,&ms); + if (who == NULL) ms = 0; + elap = MSWAIT - ms; + rpt_mutex_lock(&myrpt->lock); + l = myrpt->links.next; + while(l != &myrpt->links) + { + if (l->linklisttimer) + { + l->linklisttimer -= elap; + if (l->linklisttimer < 0) l->linklisttimer = 0; + } + if ((!l->linklisttimer) && (l->name[0] != '0') && (!l->isremote)) + { + struct ast_frame lf; + + memset(&lf,0,sizeof(lf)); + lf.frametype = AST_FRAME_TEXT; + lf.subclass = 0; + lf.offset = 0; + lf.mallocd = 0; + lf.samples = 0; + l->linklisttimer = LINKLISTTIME; + strcpy(lstr,"L "); + __mklinklist(myrpt,l,lstr + 2); + if (l->chan) + { + lf.datalen = strlen(lstr) + 1; + lf.data = lstr; + ast_write(l->chan,&lf); + if (debug > 6) ast_log(LOG_NOTICE, + "@@@@ node %s sent node string %s to node %s\n", + myrpt->name,lstr,l->name); + } + } +#ifndef OLDKEY + if ((l->retxtimer += elap) >= REDUNDANT_TX_TIME) + { + l->retxtimer = 0; + if (l->chan && l->phonemode == 0) + { + if (l->lasttx) + ast_indicate(l->chan,AST_CONTROL_RADIO_KEY); + else + ast_indicate(l->chan,AST_CONTROL_RADIO_UNKEY); + } + } + if ((l->rerxtimer += elap) >= (REDUNDANT_TX_TIME * 5)) + { + if (debug == 7) printf("@@@@ rx un-key\n"); + l->lastrx = 0; + l->rerxtimer = 0; + if(myrpt->p.duplex) + rpt_telemetry(myrpt,LINKUNKEY,l); + if (myrpt->p.archivedir) + { + char str[100]; + + l->lastrx1 = 0; + sprintf(str,"RXUNKEY(T),%s",l->name); + donodelog(myrpt,str); + } + } +#endif + 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; + } + + /* Tally connect time */ + l->connecttime += elap; + + /* 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; + rpt_mutex_unlock(&myrpt->lock); + if (l->chan) ast_softhangup(l->chan,AST_SOFTHANGUP_DEV); + rpt_mutex_lock(&myrpt->lock); + break; + } + if ((!l->chan) && (!l->retrytimer) && l->outbound && + (l->retries++ < l->max_retries) && (l->hasconnected)) + { + if (l->chan) ast_hangup(l->chan); + l->chan = 0; + rpt_mutex_unlock(&myrpt->lock); + if ((l->name[0] != '0') && (!l->isremote)) + { + if (attempt_reconnect(myrpt,l) == -1) + { + l->retrytimer = RETRY_TIMER_MS; + } + } + else + { + l->retrytimer = l->max_retries + 1; + } + + rpt_mutex_lock(&myrpt->lock); + break; + } + if ((!l->chan) && (!l->retrytimer) && l->outbound && + (l->retries >= l->max_retries)) + { + /* remove from queue */ + remque((struct qelem *) l); + if (!strcmp(myrpt->cmdnode,l->name)) + myrpt->cmdnode[0] = 0; + rpt_mutex_unlock(&myrpt->lock); + if (l->name[0] != '0') + { + if (!l->hasconnected) + rpt_telemetry(myrpt,CONNFAIL,l); + else rpt_telemetry(myrpt,REMDISC,l); + } + if (myrpt->p.archivedir) + { + char str[100]; + + if (!l->hasconnected) + sprintf(str,"LINKFAIL,%s",l->name); + else + sprintf(str,"LINKDISC,%s",l->name); + donodelog(myrpt,str); + } + /* hang-up on call to device */ + ast_hangup(l->pchan); + free(l); + rpt_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; + rpt_mutex_unlock(&myrpt->lock); + if (l->name[0] != '0') + { + rpt_telemetry(myrpt,REMDISC,l); + } + if (myrpt->p.archivedir) + { + char str[100]; + + sprintf(str,"LINKDISC,%s",l->name); + donodelog(myrpt,str); + } + /* hang-up on call to device */ + ast_hangup(l->pchan); + free(l); + rpt_mutex_lock(&myrpt->lock); + break; + } + l = l->next; + } + if(totx){ + myrpt->dailytxtime += elap; + myrpt->totaltxtime += elap; + } + i = myrpt->tailtimer; + if (myrpt->tailtimer) myrpt->tailtimer -= elap; + if (myrpt->tailtimer < 0) myrpt->tailtimer = 0; + if((i) && (myrpt->tailtimer == 0)) + myrpt->tailevent = 1; + if ((!myrpt->p.s[myrpt->p.sysstate_cur].totdisable) && 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; + if (myrpt->tmsgtimer) myrpt->tmsgtimer -= elap; + if (myrpt->tmsgtimer < 0) myrpt->tmsgtimer = 0; + /* do macro timers */ + if (myrpt->macrotimer) myrpt->macrotimer -= elap; + if (myrpt->macrotimer < 0) myrpt->macrotimer = 0; + /* do local dtmf timer */ + if (myrpt->dtmf_local_timer) + { + if (myrpt->dtmf_local_timer > 1) myrpt->dtmf_local_timer -= elap; + if (myrpt->dtmf_local_timer < 1) myrpt->dtmf_local_timer = 1; + } + do_dtmf_local(myrpt,0); + /* Execute scheduler appx. every 2 tenths of a second */ + if (myrpt->skedtimer <= 0){ + myrpt->skedtimer = 200; + do_scheduler(myrpt); + } + else + myrpt->skedtimer -=elap; + if (!ms) + { + rpt_mutex_unlock(&myrpt->lock); + continue; + } + c = myrpt->macrobuf[0]; + time(&t); + if (c && (!myrpt->macrotimer) && + starttime && (t > (starttime + START_DELAY))) + { + myrpt->macrotimer = MACROTIME; + memmove(myrpt->macrobuf,myrpt->macrobuf + 1,MAXMACRO - 1); + if ((c == 'p') || (c == 'P')) + myrpt->macrotimer = MACROPTIME; + rpt_mutex_unlock(&myrpt->lock); + if (myrpt->p.archivedir) + { + char str[100]; + + sprintf(str,"DTMF(M),MAIN,%c",c); + donodelog(myrpt,str); + } + local_dtmf_helper(myrpt,c); + } else rpt_mutex_unlock(&myrpt->lock); + if (who == myrpt->rxchannel) /* if it was a read from rx */ + { + int ismuted; + + f = ast_read(myrpt->rxchannel); + if (!f) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + break; + } + if (f->frametype == AST_FRAME_VOICE) + { +#ifdef _MDC_DECODE_H_ + unsigned char ubuf[2560]; + short *sp; + int n; +#endif + + if ((!myrpt->localtx) && (!myrpt->p.linktolink)) { + memset(f->data,0,f->datalen); + } + +#ifdef _MDC_DECODE_H_ + sp = (short *) f->data; + /* convert block to unsigned char */ + for(n = 0; n < f->datalen / 2; n++) + { + ubuf[n] = (*sp++ >> 8) + 128; + } + n = mdc_decoder_process_samples(myrpt->mdc,ubuf,f->datalen / 2); + if (n == 1) + { + unsigned char op,arg; + unsigned short unitID; + + mdc_decoder_get_packet(myrpt->mdc,&op,&arg,&unitID); + if (debug > 2) + { + ast_log(LOG_NOTICE,"Got (single-length) packet:\n"); + ast_log(LOG_NOTICE,"op: %02x, arg: %02x, UnitID: %04x\n", + op & 255,arg & 255,unitID); + } + if ((op == 1) && (arg == 0)) + { + myrpt->lastunit = unitID; + mdc1200_notify(myrpt,NULL,myrpt->lastunit); + mdc1200_send(myrpt,myrpt->lastunit); + } + } + if ((debug > 2) && (i == 2)) + { + unsigned char op,arg,ex1,ex2,ex3,ex4; + unsigned short unitID; + + mdc_decoder_get_double_packet(myrpt->mdc,&op,&arg,&unitID, + &ex1,&ex2,&ex3,&ex4); + ast_log(LOG_NOTICE,"Got (double-length) packet:\n"); + ast_log(LOG_NOTICE,"op: %02x, arg: %02x, UnitID: %04x\n", + op & 255,arg & 255,unitID); + ast_log(LOG_NOTICE,"ex1: %02x, ex2: %02x, ex3: %02x, ex4: %02x\n", + ex1 & 255, ex2 & 255, ex3 & 255, ex4 & 255); + } +#endif +#ifdef __RPT_NOTCH + /* apply inbound filters, if any */ + rpt_filter(myrpt,f->data,f->datalen / 2); +#endif + if (ioctl(myrpt->zaprxchannel->fds[0], DAHDI_GETCONFMUTE, &ismuted) == -1) + { + ismuted = 0; + } + if (dtmfed) ismuted = 1; + dtmfed = 0; + if (ismuted) + { + memset(f->data,0,f->datalen); + if (myrpt->lastf1) + memset(myrpt->lastf1->data,0,myrpt->lastf1->datalen); + if (myrpt->lastf2) + memset(myrpt->lastf2->data,0,myrpt->lastf2->datalen); + } + if (f) f2 = ast_frdup(f); + else f2 = NULL; + f1 = myrpt->lastf2; + myrpt->lastf2 = myrpt->lastf1; + myrpt->lastf1 = f2; + if (ismuted) + { + if (myrpt->lastf1) + memset(myrpt->lastf1->data,0,myrpt->lastf1->datalen); + if (myrpt->lastf2) + memset(myrpt->lastf2->data,0,myrpt->lastf2->datalen); + } + if (f1) + { + ast_write(myrpt->pchannel,f1); + ast_frfree(f1); + } + } +#ifndef OLD_ASTERISK + else if (f->frametype == AST_FRAME_DTMF_BEGIN) + { + if (myrpt->lastf1) + memset(myrpt->lastf1->data,0,myrpt->lastf1->datalen); + if (myrpt->lastf2) + memset(myrpt->lastf2->data,0,myrpt->lastf2->datalen); + dtmfed = 1; + } +#endif + else if (f->frametype == AST_FRAME_DTMF) + { + c = (char) f->subclass; /* get DTMF char */ + ast_frfree(f); + if (myrpt->lastf1) + memset(myrpt->lastf1->data,0,myrpt->lastf1->datalen); + if (myrpt->lastf2) + memset(myrpt->lastf2->data,0,myrpt->lastf2->datalen); + dtmfed = 1; + if (!myrpt->keyed) continue; + c = func_xlat(myrpt,c,&myrpt->p.inxlat); + if (c) local_dtmf_helper(myrpt,c); + 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 ((!lasttx) || (myrpt->p.duplex > 1) || (myrpt->p.linktolink)) + { + if (debug == 7) printf("@@@@ rx key\n"); + myrpt->keyed = 1; + } + if (myrpt->p.archivedir) + { + donodelog(myrpt,"RXKEY,MAIN"); + } + } + /* if RX un-key */ + if (f->subclass == AST_CONTROL_RADIO_UNKEY) + { + if ((!lasttx) || (myrpt->p.duplex > 1) || (myrpt->p.linktolink)) + { + if (debug == 7) printf("@@@@ rx un-key\n"); + if(myrpt->p.duplex && myrpt->keyed) { + rpt_telemetry(myrpt,UNKEY,NULL); + } + } + myrpt->keyed = 0; + if (myrpt->p.archivedir) + { + donodelog(myrpt,"RXUNKEY,MAIN"); + } + } + } + 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; + } + if (who == myrpt->zaptxchannel) /* if it was a read from pseudo-tx */ + { + f = ast_read(myrpt->zaptxchannel); + if (!f) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + break; + } + if (f->frametype == AST_FRAME_VOICE) + { + ast_write(myrpt->txchannel,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; + } + toexit = 0; + rpt_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 */ + { + int remnomute; + + 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; + } + rpt_mutex_unlock(&myrpt->lock); + remnomute = myrpt->localtx && + (!(myrpt->cmdnode[0] || + (myrpt->dtmfidx > -1))); + totx = (((l->isremote) ? (remnomute) : + myrpt->exttx) || remrx) && l->mode; + if (l->phonemode == 0 && l->chan && (l->lasttx != totx)) + { + if (totx) + { + ast_indicate(l->chan,AST_CONTROL_RADIO_KEY); + } + else + { + ast_indicate(l->chan,AST_CONTROL_RADIO_UNKEY); + } + if (myrpt->p.archivedir) + { + char str[100]; + + if (totx) + sprintf(str,"TXKEY,%s",l->name); + else + sprintf(str,"TXUNKEY,%s",l->name); + donodelog(myrpt,str); + } + } + l->lasttx = totx; + f = ast_read(l->chan); + if (!f) + { + rpt_mutex_lock(&myrpt->lock); + __kickshort(myrpt); + rpt_mutex_unlock(&myrpt->lock); + if ((!l->disced) && (!l->outbound)) + { + if ((l->name[0] == '0') || l->isremote) + l->disctime = 1; + else + l->disctime = DISC_TIME; + rpt_mutex_lock(&myrpt->lock); + ast_hangup(l->chan); + l->chan = 0; + break; + } + + if (l->retrytimer) + { + ast_hangup(l->chan); + l->chan = 0; + rpt_mutex_lock(&myrpt->lock); + break; + } + if (l->outbound && (l->retries++ < l->max_retries) && (l->hasconnected)) + { + rpt_mutex_lock(&myrpt->lock); + if (l->chan) ast_hangup(l->chan); + l->chan = 0; + l->hasconnected = 1; + l->retrytimer = RETRY_TIMER_MS; + l->elaptime = 0; + l->connecttime = 0; + l->thisconnected = 0; + break; + } + rpt_mutex_lock(&myrpt->lock); + /* remove from queue */ + remque((struct qelem *) l); + if (!strcmp(myrpt->cmdnode,l->name)) + myrpt->cmdnode[0] = 0; + __kickshort(myrpt); + rpt_mutex_unlock(&myrpt->lock); + if (!l->hasconnected) + rpt_telemetry(myrpt,CONNFAIL,l); + else if (l->disced != 2) rpt_telemetry(myrpt,REMDISC,l); + if (myrpt->p.archivedir) + { + char str[100]; + + if (!l->hasconnected) + sprintf(str,"LINKFAIL,%s",l->name); + else + sprintf(str,"LINKDISC,%s",l->name); + donodelog(myrpt,str); + } + if (l->lastf1) ast_frfree(l->lastf1); + l->lastf1 = NULL; + if (l->lastf2) ast_frfree(l->lastf2); + l->lastf2 = NULL; + /* hang-up on call to device */ + ast_hangup(l->chan); + ast_hangup(l->pchan); + free(l); + rpt_mutex_lock(&myrpt->lock); + break; + } + if (f->frametype == AST_FRAME_VOICE) + { + int ismuted; + + if (l->phonemode) + { + if (ioctl(l->chan->fds[0], DAHDI_GETCONFMUTE, &ismuted) == -1) + { + ismuted = 0; + } + /* if not receiving, zero-out audio */ + ismuted |= (!l->lastrx); + if (l->dtmfed && l->phonemode) ismuted = 1; + l->dtmfed = 0; + if (ismuted) + { + memset(f->data,0,f->datalen); + if (l->lastf1) + memset(l->lastf1->data,0,l->lastf1->datalen); + if (l->lastf2) + memset(l->lastf2->data,0,l->lastf2->datalen); + } + if (f) f2 = ast_frdup(f); + else f2 = NULL; + f1 = l->lastf2; + l->lastf2 = l->lastf1; + l->lastf1 = f2; + if (ismuted) + { + if (l->lastf1) + memset(l->lastf1->data,0,l->lastf1->datalen); + if (l->lastf2) + memset(l->lastf2->data,0,l->lastf2->datalen); + } + if (f1) + { + ast_write(l->pchan,f1); + ast_frfree(f1); + } + } + else + { + if (!l->lastrx) + memset(f->data,0,f->datalen); + ast_write(l->pchan,f); + } + } +#ifndef OLD_ASTERISK + else if (f->frametype == AST_FRAME_DTMF_BEGIN) + { + if (l->lastf1) + memset(l->lastf1->data,0,l->lastf1->datalen); + if (l->lastf2) + memset(l->lastf2->data,0,l->lastf2->datalen); + l->dtmfed = 1; + } +#endif + + if (f->frametype == AST_FRAME_TEXT) + { + handle_link_data(myrpt,l,f->data); + } + if (f->frametype == AST_FRAME_DTMF) + { + if (l->lastf1) + memset(l->lastf1->data,0,l->lastf1->datalen); + if (l->lastf2) + memset(l->lastf2->data,0,l->lastf2->datalen); + l->dtmfed = 1; + handle_link_phone_dtmf(myrpt,l,f->subclass); + } + if (f->frametype == AST_FRAME_CONTROL) + { + if (f->subclass == AST_CONTROL_ANSWER) + { + char lconnected = l->connected; + + __kickshort(myrpt); + l->connected = 1; + l->hasconnected = 1; + l->thisconnected = 1; + l->elaptime = -1; + if (!l->isremote) l->retries = 0; + if (!lconnected) + { + rpt_telemetry(myrpt,CONNECTED,l); + if (myrpt->p.archivedir) + { + char str[100]; + + if (l->mode) + sprintf(str,"LINKTRX,%s",l->name); + else + sprintf(str,"LINKMONITOR,%s",l->name); + donodelog(myrpt,str); + } + } + else + l->reconnects++; + } + /* if RX key */ + if (f->subclass == AST_CONTROL_RADIO_KEY) + { + if (debug == 7 ) printf("@@@@ rx key\n"); + l->lastrx = 1; + l->rerxtimer = 0; + if (myrpt->p.archivedir && (!l->lastrx1)) + { + char str[100]; + + l->lastrx1 = 1; + sprintf(str,"RXKEY,%s",l->name); + donodelog(myrpt,str); + } + } + /* if RX un-key */ + if (f->subclass == AST_CONTROL_RADIO_UNKEY) + { + if (debug == 7) printf("@@@@ rx un-key\n"); + l->lastrx = 0; + l->rerxtimer = 0; + if(myrpt->p.duplex) + rpt_telemetry(myrpt,LINKUNKEY,l); + if (myrpt->p.archivedir && (l->lastrx1)) + { + char str[100]; + + l->lastrx1 = 0; + sprintf(str,"RXUNKEY,%s",l->name); + donodelog(myrpt,str); + } + } + if (f->subclass == AST_CONTROL_HANGUP) + { + ast_frfree(f); + rpt_mutex_lock(&myrpt->lock); + __kickshort(myrpt); + rpt_mutex_unlock(&myrpt->lock); + if ((!l->outbound) && (!l->disced)) + { + if ((l->name[0] == '0') || l->isremote) + l->disctime = 1; + else + l->disctime = DISC_TIME; + rpt_mutex_lock(&myrpt->lock); + ast_hangup(l->chan); + l->chan = 0; + break; + } + if (l->retrytimer) + { + if (l->chan) ast_hangup(l->chan); + l->chan = 0; + rpt_mutex_lock(&myrpt->lock); + break; + } + if (l->outbound && (l->retries++ < l->max_retries) && (l->hasconnected)) + { + rpt_mutex_lock(&myrpt->lock); + if (l->chan) ast_hangup(l->chan); + l->chan = 0; + l->hasconnected = 1; + l->elaptime = 0; + l->retrytimer = RETRY_TIMER_MS; + l->connecttime = 0; + l->thisconnected = 0; + break; + } + rpt_mutex_lock(&myrpt->lock); + /* remove from queue */ + remque((struct qelem *) l); + if (!strcmp(myrpt->cmdnode,l->name)) + myrpt->cmdnode[0] = 0; + __kickshort(myrpt); + rpt_mutex_unlock(&myrpt->lock); + if (!l->hasconnected) + rpt_telemetry(myrpt,CONNFAIL,l); + else if (l->disced != 2) rpt_telemetry(myrpt,REMDISC,l); + if (myrpt->p.archivedir) + { + char str[100]; + + if (!l->hasconnected) + sprintf(str,"LINKFAIL,%s",l->name); + else + sprintf(str,"LINKDISC,%s",l->name); + donodelog(myrpt,str); + } + if (l->lastf1) ast_frfree(l->lastf1); + l->lastf1 = NULL; + if (l->lastf2) ast_frfree(l->lastf2); + l->lastf2 = NULL; + /* hang-up on call to device */ + ast_hangup(l->chan); + ast_hangup(l->pchan); + free(l); + rpt_mutex_lock(&myrpt->lock); + break; + } + } + ast_frfree(f); + rpt_mutex_lock(&myrpt->lock); + break; + } + if (who == l->pchan) + { + rpt_mutex_unlock(&myrpt->lock); + f = ast_read(l->pchan); + if (!f) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + toexit = 1; + rpt_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; + rpt_mutex_lock(&myrpt->lock); + break; + } + } + ast_frfree(f); + rpt_mutex_lock(&myrpt->lock); + break; + } + l = l->next; + } + rpt_mutex_unlock(&myrpt->lock); + if (toexit) break; + if (who == myrpt->monchannel) + { + f = ast_read(myrpt->monchannel); + if (!f) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + break; + } + if (f->frametype == AST_FRAME_VOICE) + { + if (myrpt->monstream) + ast_writestream(myrpt->monstream,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->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->monchannel); + ast_hangup(myrpt->txpchannel); + if (myrpt->txchannel != myrpt->rxchannel) ast_hangup(myrpt->txchannel); + if (myrpt->zaptxchannel != myrpt->txchannel) ast_hangup(myrpt->zaptxchannel); + if (myrpt->lastf1) ast_frfree(myrpt->lastf1); + myrpt->lastf1 = NULL; + if (myrpt->lastf2) ast_frfree(myrpt->lastf2); + myrpt->lastf2 = NULL; + ast_hangup(myrpt->rxchannel); + rpt_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); + } + rpt_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) +{ +int i,n; +pthread_attr_t attr; +struct ast_config *cfg; +char *this,*val; + + /* init nodelog queue */ + nodelog.next = nodelog.prev = &nodelog; + /* go thru all the specified repeaters */ + this = NULL; + n = 0; + /* wait until asterisk starts */ + while(!ast_test_flag(&ast_options,AST_OPT_FLAG_FULLY_BOOTED)) + usleep(250000); + rpt_vars[n].cfg = ast_config_load("rpt.conf"); + cfg = rpt_vars[n].cfg; + if (!cfg) { + ast_log(LOG_NOTICE, "Unable to open radio repeater configuration rpt.conf. Radio Repeater disabled.\n"); + pthread_exit(NULL); + } + 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 */ + memset(&rpt_vars[n],0,sizeof(rpt_vars[n])); + rpt_vars[n].name = strdup(this); + val = (char *) ast_variable_retrieve(cfg,this,"rxchannel"); + if (val) rpt_vars[n].rxchanname = strdup(val); + val = (char *) ast_variable_retrieve(cfg,this,"txchannel"); + if (val) rpt_vars[n].txchanname = strdup(val); + val = (char *) ast_variable_retrieve(cfg,this,"remote"); + if (val) rpt_vars[n].remote = strdup(val); + ast_mutex_init(&rpt_vars[n].lock); + ast_mutex_init(&rpt_vars[n].remlock); + 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].tailmessagen = 0; +#ifdef _MDC_DECODE_H_ + rpt_vars[n].mdc = mdc_decoder_new(8000); +#endif + n++; + } + nrpts = n; + ast_config_destroy(cfg); + + /* start em all */ + for(i = 0; i < n; i++) + { + load_rpt_vars(i,1); + + /* if is a remote, dont start one for it */ + if (rpt_vars[i].remote) + { + if(retreive_memory(&rpt_vars[i],"init")){ /* Try to retreive initial memory channel */ + 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].p.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); + time(&starttime); + 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); + } + + } + for(;;) + { + struct nodelog *nodep; + char *space,datestr[100],fname[300]; + int fd; + + ast_mutex_lock(&nodeloglock); + nodep = nodelog.next; + if(nodep == &nodelog) /* if nothing in queue */ + { + ast_mutex_unlock(&nodeloglock); + break; + } + remque((struct qelem *)nodep); + ast_mutex_unlock(&nodeloglock); + space = strchr(nodep->str,' '); + if (!space) + { + free(nodep); + continue; + } + *space = 0; + strftime(datestr,sizeof(datestr) - 1,"%Y%m%d", + localtime(&nodep->timestamp)); + sprintf(fname,"%s/%s/%s.txt",nodep->archivedir, + nodep->str,datestr); + fd = open(fname,O_WRONLY | O_CREAT | O_APPEND,0600); + if (fd == -1) + { + ast_log(LOG_ERROR,"Cannot open node log file %s for write",space + 1); + free(nodep); + continue; + } + if (write(fd,space + 1,strlen(space + 1)) != + strlen(space + 1)) + { + ast_log(LOG_ERROR,"Cannot write node log file %s for write",space + 1); + free(nodep); + continue; + } + close(fd); + free(nodep); + } + sleep(2); + } + ast_config_destroy(cfg); + pthread_exit(NULL); +} + +static int rpt_exec(struct ast_channel *chan, void *data) +{ + int res=-1,i,rem_totx,rem_rx,remkeyed,n,phone_mode = 0; + int iskenwood_pci4,authtold,authreq,setting,notremming,reming; + int ismuted,dtmfed; +#ifdef OLD_ASTERISK + struct localuser *u; +#endif + char tmp[256], keyed = 0,keyed1 = 0; + char *options,*stringp,*tele,c; + struct rpt *myrpt; + struct ast_frame *f,*f1,*f2; + struct ast_channel *who; + struct ast_channel *cs[20]; + struct rpt_link *l; + struct dahdi_confinfo ci; /* conference info */ + struct dahdi_params par; + int ms,elap,nullfd; + time_t t,last_timeout_warning; + struct dahdi_radio_param z; + struct rpt_tele *telem; + + nullfd = open("/dev/null",O_RDWR); + 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); + time(&t); + /* if time has externally shifted negative, screw it */ + if (t < starttime) t = starttime + START_DELAY; + if ((!starttime) || (t < (starttime + START_DELAY))) + { + ast_log(LOG_NOTICE,"Node %s rejecting call: too soon!\n",tmp); + ast_safe_sleep(chan,3000); + return -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(myrpt->p.s[myrpt->p.sysstate_cur].txdisable){ /* Do not allow incoming connections if disabled */ + ast_log(LOG_NOTICE, "Connect attempt to node %s with tx disabled", myrpt->name); + return -1; + } + + /* if not phone access, must be an IAX connection */ + if (options && ((*options == 'P') || (*options == 'D') || (*options == 'R'))) + { + int val; + + phone_mode = 1; + if (*options == 'D') phone_mode = 2; + ast_set_callerid(chan,"0","app_rpt user","0"); + val = 1; + ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&val,sizeof(char),0); + } + else + { +#ifdef ALLOW_LOCAL_CHANNELS + /* Check to insure the connection is IAX2 or Local*/ + if ( (strncmp(chan->name,"IAX2",4)) && (strncmp(chan->name,"Local",5)) ) { + ast_log(LOG_WARNING, "We only accept links via IAX2 or Local!!\n"); + return -1; + } +#else + if (strncmp(chan->name,"IAX2",4)) + { + ast_log(LOG_WARNING, "We only accept links via IAX2!!\n"); + return -1; + } +#endif + } + 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; + + + rpt_mutex_lock(&myrpt->lock); + m = myrpt->callmode; + rpt_mutex_unlock(&myrpt->lock); + + if ((!myrpt->p.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); +#ifdef OLD_ASTERISK + if(exten && strcasecmp(exten, "BYEXTENSION")) +#else + if(exten) +#endif + 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)); +#ifdef ALLOW_LOCAL_CHANNELS + /* set IP address if this is a local connection*/ + if (strncmp(chan->name,"Local",5)==0) { + strcpy(hisip,"127.0.0.1"); + } else { + pbx_substitute_variables_helper(chan,"${IAXPEER(CURRENTCHANNEL)}",hisip,sizeof(hisip) - 1); + } +#else + pbx_substitute_variables_helper(chan,"${IAXPEER(CURRENTCHANNEL)}",hisip,sizeof(hisip) - 1); +#endif + + 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 = node_lookup(myrpt,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)); +#ifdef OLD_ASTERISK + ast_inet_ntoa(nodeip,sizeof(nodeip) - 1,ia); +#else + strncpy(nodeip,ast_inet_ntoa(ia),sizeof(nodeip) - 1); +#endif + 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)); +#ifdef OLD_ASTERISK + ast_inet_ntoa(nodeip,sizeof(nodeip) - 1,ia); +#else + strncpy(nodeip,ast_inet_ntoa(ia),sizeof(nodeip) - 1); +#endif + 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; + int reconnects = 0; + + /* 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; + } + rpt_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 = l->max_retries + 1; + l->disced = 2; + reconnects = l->reconnects; + reconnects++; + rpt_mutex_unlock(&myrpt->lock); + usleep(500000); + } else + rpt_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->thisconnected = 1; + l->hasconnected = 1; + l->reconnects = reconnects; + l->phonemode = phone_mode; + l->lastf1 = NULL; + l->lastf2 = NULL; + l->dtmfed = 0; + 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); +#ifdef AST_CDR_FLAG_POST_DISABLED + ast_set_flag(l->pchan->cdr,AST_CDR_FLAG_POST_DISABLED); +#endif + /* make a conference for the tx */ + ci.chan = 0; + ci.confno = myrpt->conf; + ci.confmode = DAHDI_CONF_CONF | DAHDI_CONF_LISTENER | DAHDI_CONF_TALKER; + /* first put the channel on the conference in proper mode */ + if (ioctl(l->pchan->fds[0],DAHDI_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + pthread_exit(NULL); + } + rpt_mutex_lock(&myrpt->lock); + if (phone_mode > 1) l->lastrx = 1; + l->max_retries = MAX_RETRIES; + /* insert at end of queue */ + insque((struct qelem *)l,(struct qelem *)myrpt->links.next); + __kickshort(myrpt); + rpt_mutex_unlock(&myrpt->lock); + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } + if (myrpt->p.archivedir) + { + char str[100]; + + if (l->phonemode) + sprintf(str,"LINK(P),%s",l->name); + else + sprintf(str,"LINK,%s",l->name); + donodelog(myrpt,str); + } + return AST_PBX_KEEPALIVE; + } + /* well, then it is a remote */ + rpt_mutex_lock(&myrpt->lock); + /* if remote, error if anyone else already linked */ + if (myrpt->remoteon) + { + rpt_mutex_unlock(&myrpt->lock); + usleep(500000); + if (myrpt->remoteon) + { + ast_log(LOG_WARNING, "Trying to use busy link on %s\n",tmp); + return -1; + } + rpt_mutex_lock(&myrpt->lock); + } +#ifdef HAVE_IOPERM + if ((!strcmp(myrpt->remote, remote_rig_rbi)) && + (ioperm(myrpt->p.iobase,1,1) == -1)) + { + rpt_mutex_unlock(&myrpt->lock); + ast_log(LOG_WARNING, "Cant get io permission on IO port %x hex\n",myrpt->p.iobase); + return -1; + } +#endif + myrpt->remoteon = 1; +#ifdef OLD_ASTERISK + LOCAL_USER_ADD(u); +#endif + rpt_mutex_unlock(&myrpt->lock); + /* find our index, and load the vars initially */ + for(i = 0; i < nrpts; i++) + { + if (&rpt_vars[i] == myrpt) + { + load_rpt_vars(i,0); + break; + } + } + rpt_mutex_lock(&myrpt->lock); + tele = strchr(myrpt->rxchanname,'/'); + if (!tele) + { + fprintf(stderr,"rpt:Dial number must be in format tech/number\n"); + rpt_mutex_unlock(&myrpt->lock); + pthread_exit(NULL); + } + *tele++ = 0; + myrpt->rxchannel = ast_request(myrpt->rxchanname,AST_FORMAT_SLINEAR,tele,NULL); + myrpt->zaprxchannel = NULL; + if (!strcasecmp(myrpt->rxchanname,"Zap")) + myrpt->zaprxchannel = myrpt->rxchannel; + if (myrpt->rxchannel) + { + ast_set_read_format(myrpt->rxchannel,AST_FORMAT_SLINEAR); + ast_set_write_format(myrpt->rxchannel,AST_FORMAT_SLINEAR); +#ifdef AST_CDR_FLAG_POST_DISABLED + ast_set_flag(myrpt->rxchannel->cdr,AST_CDR_FLAG_POST_DISABLED); +#endif + 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); + rpt_mutex_unlock(&myrpt->lock); + ast_call(myrpt->rxchannel,tele,999); + rpt_mutex_lock(&myrpt->lock); + } + else + { + fprintf(stderr,"rpt:Sorry unable to obtain Rx channel\n"); + rpt_mutex_unlock(&myrpt->lock); + pthread_exit(NULL); + } + *--tele = '/'; + myrpt->zaptxchannel = NULL; + if (myrpt->txchanname) + { + tele = strchr(myrpt->txchanname,'/'); + if (!tele) + { + fprintf(stderr,"rpt:Dial number must be in format tech/number\n"); + rpt_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 (!strcasecmp(myrpt->txchanname,"Zap")) + myrpt->zaptxchannel = myrpt->txchannel; + if (myrpt->txchannel) + { + ast_set_read_format(myrpt->txchannel,AST_FORMAT_SLINEAR); + ast_set_write_format(myrpt->txchannel,AST_FORMAT_SLINEAR); +#ifdef AST_CDR_FLAG_POST_DISABLED + ast_set_flag(myrpt->txchannel->cdr,AST_CDR_FLAG_POST_DISABLED); +#endif + 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); + rpt_mutex_unlock(&myrpt->lock); + ast_call(myrpt->txchannel,tele,999); + rpt_mutex_lock(&myrpt->lock); + } + else + { + fprintf(stderr,"rpt:Sorry unable to obtain Tx channel\n"); + rpt_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->rxchannel); + pthread_exit(NULL); + } + *--tele = '/'; + } + else + { + myrpt->txchannel = myrpt->rxchannel; + } + /* 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"); + rpt_mutex_unlock(&myrpt->lock); + if (myrpt->txchannel != myrpt->rxchannel) + ast_hangup(myrpt->txchannel); + ast_hangup(myrpt->rxchannel); + pthread_exit(NULL); + } + ast_set_read_format(myrpt->pchannel,AST_FORMAT_SLINEAR); + ast_set_write_format(myrpt->pchannel,AST_FORMAT_SLINEAR); +#ifdef AST_CDR_FLAG_POST_DISABLED + ast_set_flag(myrpt->pchannel->cdr,AST_CDR_FLAG_POST_DISABLED); +#endif + if (!myrpt->zaprxchannel) myrpt->zaprxchannel = myrpt->pchannel; + if (!myrpt->zaptxchannel) myrpt->zaptxchannel = myrpt->pchannel; + /* make a conference for the pseudo */ + ci.chan = 0; + ci.confno = -1; /* make a new conf */ + ci.confmode = DAHDI_CONF_CONFANNMON ; + /* first put the channel on the conference in announce/monitor mode */ + if (ioctl(myrpt->pchannel->fds[0],DAHDI_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + rpt_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->pchannel); + if (myrpt->txchannel != myrpt->rxchannel) + ast_hangup(myrpt->txchannel); + ast_hangup(myrpt->rxchannel); + pthread_exit(NULL); + } + /* save pseudo channel conference number */ + myrpt->conf = myrpt->txconf = ci.confno; + /* if serial io port, open it */ + myrpt->iofd = -1; + if (myrpt->p.ioport && ((myrpt->iofd = openserial(myrpt->p.ioport)) == -1)) + { + rpt_mutex_unlock(&myrpt->lock); + ast_hangup(myrpt->pchannel); + if (myrpt->txchannel != myrpt->rxchannel) + ast_hangup(myrpt->txchannel); + ast_hangup(myrpt->rxchannel); + pthread_exit(NULL); + } + iskenwood_pci4 = 0; + memset(&z,0,sizeof(z)); + if ((myrpt->iofd < 1) && (myrpt->txchannel == myrpt->zaptxchannel)) + { + z.radpar = DAHDI_RADPAR_REMMODE; + z.data = DAHDI_RADPAR_REM_NONE; + res = ioctl(myrpt->zaptxchannel->fds[0],DAHDI_RADIO_SETPARAM,&z); + /* if PCIRADIO and kenwood selected */ + if ((!res) && (!strcmp(myrpt->remote,remote_rig_kenwood))) + { + z.radpar = DAHDI_RADPAR_UIOMODE; + z.data = 1; + if (ioctl(myrpt->zaptxchannel->fds[0],DAHDI_RADIO_SETPARAM,&z) == -1) + { + ast_log(LOG_ERROR,"Cannot set UIOMODE\n"); + return -1; + } + z.radpar = DAHDI_RADPAR_UIODATA; + z.data = 3; + if (ioctl(myrpt->zaptxchannel->fds[0],DAHDI_RADIO_SETPARAM,&z) == -1) + { + ast_log(LOG_ERROR,"Cannot set UIODATA\n"); + return -1; + } + i = DAHDI_OFFHOOK; + if (ioctl(myrpt->zaptxchannel->fds[0],DAHDI_HOOK,&i) == -1) + { + ast_log(LOG_ERROR,"Cannot set hook\n"); + return -1; + } + iskenwood_pci4 = 1; + } + } + if (myrpt->txchannel == myrpt->zaptxchannel) + { + i = DAHDI_ONHOOK; + ioctl(myrpt->zaptxchannel->fds[0],DAHDI_HOOK,&i); + /* if PCIRADIO and Yaesu ft897/ICOM IC-706 selected */ + if ((myrpt->iofd < 1) && (!res) && + (!strcmp(myrpt->remote,remote_rig_ft897) || + (!strcmp(myrpt->remote,remote_rig_ic706)))) + { + z.radpar = DAHDI_RADPAR_UIOMODE; + z.data = 1; + if (ioctl(myrpt->zaptxchannel->fds[0],DAHDI_RADIO_SETPARAM,&z) == -1) + { + ast_log(LOG_ERROR,"Cannot set UIOMODE\n"); + return -1; + } + z.radpar = DAHDI_RADPAR_UIODATA; + z.data = 3; + if (ioctl(myrpt->zaptxchannel->fds[0],DAHDI_RADIO_SETPARAM,&z) == -1) + { + ast_log(LOG_ERROR,"Cannot set UIODATA\n"); + return -1; + } + } + } + myrpt->remoterx = 0; + myrpt->remotetx = 0; + myrpt->retxtimer = 0; + myrpt->rerxtimer = 0; + myrpt->remoteon = 1; + myrpt->dtmfidx = -1; + myrpt->dtmfbuf[0] = 0; + myrpt->dtmf_time_rem = 0; + myrpt->hfscanmode = 0; + myrpt->hfscanstatus = 0; + if (myrpt->p.startupmacro) + { + snprintf(myrpt->macrobuf,MAXMACRO - 1,"PPPP%s",myrpt->p.startupmacro); + } + time(&myrpt->start_time); + myrpt->last_activity_time = myrpt->start_time; + last_timeout_warning = 0; + myrpt->reload = 0; + myrpt->tele.next = &myrpt->tele; + myrpt->tele.prev = &myrpt->tele; + rpt_mutex_unlock(&myrpt->lock); + ast_set_write_format(chan, AST_FORMAT_SLINEAR); + ast_set_read_format(chan, AST_FORMAT_SLINEAR); + rem_rx = 0; + remkeyed = 0; + /* if we are on 2w loop and are a remote, turn EC on */ + if (myrpt->remote && (myrpt->rxchannel == myrpt->txchannel)) + { + i = 128; + ioctl(myrpt->zaprxchannel->fds[0],DAHDI_ECHOCANCEL,&i); + } + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } + + if (myrpt->rxchannel == myrpt->zaprxchannel) + { + if (ioctl(myrpt->zaprxchannel->fds[0],DAHDI_GET_PARAMS,&par) != -1) + { + if (par.rxisoffhook) + { + ast_indicate(chan,AST_CONTROL_RADIO_KEY); + myrpt->remoterx = 1; + remkeyed = 1; + } + } + } + if (myrpt->p.archivedir) + { + char mycmd[100],mydate[100],*b,*b1; + time_t myt; + long blocksleft; + + + mkdir(myrpt->p.archivedir,0600); + sprintf(mycmd,"%s/%s",myrpt->p.archivedir,myrpt->name); + mkdir(mycmd,0600); + time(&myt); + strftime(mydate,sizeof(mydate) - 1,"%Y%m%d%H%M%S", + localtime(&myt)); + sprintf(mycmd,"mixmonitor start %s %s/%s/%s.wav49 a",chan->name, + myrpt->p.archivedir,myrpt->name,mydate); + if (myrpt->p.monminblocks) + { + blocksleft = diskavail(myrpt); + if (myrpt->p.remotetimeout) + { + blocksleft -= (myrpt->p.remotetimeout * + MONITOR_DISK_BLOCKS_PER_MINUTE) / 60; + } + if (blocksleft >= myrpt->p.monminblocks) + ast_cli_command(nullfd,mycmd); + } else ast_cli_command(nullfd,mycmd); + /* look at callerid to see what node this comes from */ + if (!chan->cid.cid_num) /* if doesn't have caller id */ + { + b1 = "0"; + } else { + ast_callerid_parse(chan->cid.cid_num,&b,&b1); + ast_shrink_phone_number(b1); + } + sprintf(mycmd,"CONNECT,%s",b1); + donodelog(myrpt,mycmd); + } + myrpt->loginuser[0] = 0; + myrpt->loginlevel[0] = 0; + myrpt->authtelltimer = 0; + myrpt->authtimer = 0; + authtold = 0; + authreq = 0; + if (myrpt->p.authlevel > 1) authreq = 1; + setrem(myrpt); + n = 0; + dtmfed = 0; + cs[n++] = chan; + cs[n++] = myrpt->rxchannel; + cs[n++] = myrpt->pchannel; + if (myrpt->rxchannel != myrpt->txchannel) + cs[n++] = myrpt->txchannel; + /* start un-locked */ + for(;;) + { + if (ast_check_hangup(chan)) break; + if (ast_check_hangup(myrpt->rxchannel)) break; + notremming = 0; + setting = 0; + reming = 0; + telem = myrpt->tele.next; + while(telem != &myrpt->tele) + { + if (telem->mode == SETREMOTE) setting = 1; + if ((telem->mode == SETREMOTE) || + (telem->mode == SCAN) || + (telem->mode == TUNE)) reming = 1; + else notremming = 1; + telem = telem->next; + } + if (myrpt->reload) + { + myrpt->reload = 0; + /* find our index, and load the vars */ + for(i = 0; i < nrpts; i++) + { + if (&rpt_vars[i] == myrpt) + { + load_rpt_vars(i,0); + break; + } + } + } + time(&t); + if (myrpt->p.remotetimeout) + { + time_t r; + + r = (t - myrpt->start_time); + if (r >= myrpt->p.remotetimeout) + { + sayfile(chan,"rpt/node"); + ast_say_character_str(chan,myrpt->name,NULL,chan->language); + sayfile(chan,"rpt/timeout"); + ast_safe_sleep(chan,1000); + break; + } + if ((myrpt->p.remotetimeoutwarning) && + (r >= (myrpt->p.remotetimeout - + myrpt->p.remotetimeoutwarning)) && + (r <= (myrpt->p.remotetimeout - + myrpt->p.remotetimeoutwarningfreq))) + { + if (myrpt->p.remotetimeoutwarningfreq) + { + if ((t - last_timeout_warning) >= + myrpt->p.remotetimeoutwarningfreq) + { + time(&last_timeout_warning); + rpt_telemetry(myrpt,TIMEOUT_WARNING,0); + } + } + else + { + if (!last_timeout_warning) + { + time(&last_timeout_warning); + rpt_telemetry(myrpt,TIMEOUT_WARNING,0); + } + } + } + } + if (myrpt->p.remoteinacttimeout && myrpt->last_activity_time) + { + time_t r; + + r = (t - myrpt->last_activity_time); + if (r >= myrpt->p.remoteinacttimeout) + { + sayfile(chan,"rpt/node"); + ast_say_character_str(chan,myrpt->name,NULL,chan->language); + sayfile(chan,"rpt/timeout"); + ast_safe_sleep(chan,1000); + break; + } + if ((myrpt->p.remotetimeoutwarning) && + (r >= (myrpt->p.remoteinacttimeout - + myrpt->p.remotetimeoutwarning)) && + (r <= (myrpt->p.remoteinacttimeout - + myrpt->p.remotetimeoutwarningfreq))) + { + if (myrpt->p.remotetimeoutwarningfreq) + { + if ((t - last_timeout_warning) >= + myrpt->p.remotetimeoutwarningfreq) + { + time(&last_timeout_warning); + rpt_telemetry(myrpt,ACT_TIMEOUT_WARNING,0); + } + } + else + { + if (!last_timeout_warning) + { + time(&last_timeout_warning); + rpt_telemetry(myrpt,ACT_TIMEOUT_WARNING,0); + } + } + } + } + ms = MSWAIT; + who = ast_waitfor_n(cs,n,&ms); + if (who == NULL) ms = 0; + elap = MSWAIT - ms; + if (myrpt->macrotimer) myrpt->macrotimer -= elap; + if (myrpt->macrotimer < 0) myrpt->macrotimer = 0; + if (!ms) continue; + /* do local dtmf timer */ + if (myrpt->dtmf_local_timer) + { + if (myrpt->dtmf_local_timer > 1) myrpt->dtmf_local_timer -= elap; + if (myrpt->dtmf_local_timer < 1) myrpt->dtmf_local_timer = 1; + } + rpt_mutex_lock(&myrpt->lock); + do_dtmf_local(myrpt,0); + rpt_mutex_unlock(&myrpt->lock); + rem_totx = myrpt->dtmf_local_timer && (!phone_mode); + rem_totx |= keyed && (!myrpt->tunerequest); + rem_rx = (remkeyed && (!setting)) || (myrpt->tele.next != &myrpt->tele); + if(!strcmp(myrpt->remote, remote_rig_ic706)) + rem_totx |= myrpt->tunerequest; + if (keyed && (!keyed1)) + { + keyed1 = 1; + } + + if (!keyed && (keyed1)) + { + time_t myt; + + keyed1 = 0; + time(&myt); + /* if login necessary, and not too soon */ + if ((myrpt->p.authlevel) && + (!myrpt->loginlevel[0]) && + (myt > (t + 3))) + { + authreq = 1; + authtold = 0; + myrpt->authtelltimer = AUTHTELLTIME - AUTHTXTIME; + } + } + + + if (rem_rx && (!myrpt->remoterx)) + { + myrpt->remoterx = 1; + ast_indicate(chan,AST_CONTROL_RADIO_KEY); + } + if ((!rem_rx) && (myrpt->remoterx)) + { + myrpt->remoterx = 0; + ast_indicate(chan,AST_CONTROL_RADIO_UNKEY); + } + /* if auth requested, and not authed yet */ + if (authreq && (!myrpt->loginlevel[0])) + { + if ((!authtold) && ((myrpt->authtelltimer += elap) + >= AUTHTELLTIME)) + { + authtold = 1; + rpt_telemetry(myrpt,LOGINREQ,NULL); + } + if ((myrpt->authtimer += elap) >= AUTHLOGOUTTIME) + { + break; /* if not logged in, hang up after a time */ + } + } +#ifndef OLDKEY + if ((myrpt->retxtimer += elap) >= REDUNDANT_TX_TIME) + { + myrpt->retxtimer = 0; + if ((myrpt->remoterx) && (!myrpt->remotetx)) + ast_indicate(chan,AST_CONTROL_RADIO_KEY); + else + ast_indicate(chan,AST_CONTROL_RADIO_UNKEY); + } + + if ((myrpt->rerxtimer += elap) >= (REDUNDANT_TX_TIME * 2)) + { + keyed = 0; + myrpt->rerxtimer = 0; + } +#endif + if (rem_totx && (!myrpt->remotetx)) + { + /* if not authed, and needed, dont transmit */ + if ((!myrpt->p.authlevel) || myrpt->loginlevel[0]) + { + myrpt->remotetx = 1; + if((myrpt->remtxfreqok = check_tx_freq(myrpt))) + { + time(&myrpt->last_activity_time); + if ((iskenwood_pci4) && (myrpt->txchannel == myrpt->zaptxchannel)) + { + z.radpar = DAHDI_RADPAR_UIODATA; + z.data = 1; + if (ioctl(myrpt->zaptxchannel->fds[0],DAHDI_RADIO_SETPARAM,&z) == -1) + { + ast_log(LOG_ERROR,"Cannot set UIODATA\n"); + return -1; + } + } + else + { + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_KEY); + } + if (myrpt->p.archivedir) donodelog(myrpt,"TXKEY"); + } + } + } + if ((!rem_totx) && myrpt->remotetx) /* Remote base radio TX unkey */ + { + myrpt->remotetx = 0; + if(!myrpt->remtxfreqok){ + rpt_telemetry(myrpt,UNAUTHTX,NULL); + } + if ((iskenwood_pci4) && (myrpt->txchannel == myrpt->zaptxchannel)) + { + z.radpar = DAHDI_RADPAR_UIODATA; + z.data = 3; + if (ioctl(myrpt->zaptxchannel->fds[0],DAHDI_RADIO_SETPARAM,&z) == -1) + { + ast_log(LOG_ERROR,"Cannot set UIODATA\n"); + return -1; + } + } + else + { + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY); + } + if (myrpt->p.archivedir) donodelog(myrpt,"TXUNKEY"); + } + if (myrpt->hfscanmode){ + myrpt->scantimer -= elap; + if(myrpt->scantimer <= 0){ + if (!reming) + { + myrpt->scantimer = REM_SCANTIME; + rpt_telemetry(myrpt,SCAN,0); + } else myrpt->scantimer = 1; + } + } + rpt_mutex_lock(&myrpt->lock); + c = myrpt->macrobuf[0]; + if (c && (!myrpt->macrotimer)) + { + myrpt->macrotimer = MACROTIME; + memmove(myrpt->macrobuf,myrpt->macrobuf + 1,MAXMACRO - 1); + if ((c == 'p') || (c == 'P')) + myrpt->macrotimer = MACROPTIME; + rpt_mutex_unlock(&myrpt->lock); + if (myrpt->p.archivedir) + { + char str[100]; + sprintf(str,"DTMF(M),%c",c); + donodelog(myrpt,str); + } + if (handle_remote_dtmf_digit(myrpt,c,&keyed,0) == -1) break; + continue; + } else rpt_mutex_unlock(&myrpt->lock); + 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 (ioctl(chan->fds[0], DAHDI_GETCONFMUTE, &ismuted) == -1) + { + ismuted = 0; + } + /* if not transmitting, zero-out audio */ + ismuted |= (!myrpt->remotetx); + if (dtmfed && phone_mode) ismuted = 1; + dtmfed = 0; + if (ismuted) + { + memset(f->data,0,f->datalen); + if (myrpt->lastf1) + memset(myrpt->lastf1->data,0,myrpt->lastf1->datalen); + if (myrpt->lastf2) + memset(myrpt->lastf2->data,0,myrpt->lastf2->datalen); + } + if (f) f2 = ast_frdup(f); + else f2 = NULL; + f1 = myrpt->lastf2; + myrpt->lastf2 = myrpt->lastf1; + myrpt->lastf1 = f2; + if (ismuted) + { + if (myrpt->lastf1) + memset(myrpt->lastf1->data,0,myrpt->lastf1->datalen); + if (myrpt->lastf2) + memset(myrpt->lastf2->data,0,myrpt->lastf2->datalen); + } + if (f1) + { + if (phone_mode) + ast_write(myrpt->txchannel,f1); + else + ast_write(myrpt->txchannel,f); + ast_frfree(f1); + } + } +#ifndef OLD_ASTERISK + else if (f->frametype == AST_FRAME_DTMF_BEGIN) + { + if (myrpt->lastf1) + memset(myrpt->lastf1->data,0,myrpt->lastf1->datalen); + if (myrpt->lastf2) + memset(myrpt->lastf2->data,0,myrpt->lastf2->datalen); + dtmfed = 1; + } +#endif + if (f->frametype == AST_FRAME_DTMF) + { + if (myrpt->lastf1) + memset(myrpt->lastf1->data,0,myrpt->lastf1->datalen); + if (myrpt->lastf2) + memset(myrpt->lastf2->data,0,myrpt->lastf2->datalen); + dtmfed = 1; + 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) + { + 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 == 7) printf("@@@@ rx key\n"); + keyed = 1; + myrpt->rerxtimer = 0; + } + /* if RX un-key */ + if (f->subclass == AST_CONTROL_RADIO_UNKEY) + { + myrpt->rerxtimer = 0; + if (debug == 7) printf("@@@@ rx un-key\n"); + keyed = 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) + { + int myreming = 0; + + if(!strcmp(myrpt->remote, remote_rig_kenwood)) + myreming = reming; + + if (myreming || (!remkeyed) || + ((myrpt->remote) && (myrpt->remotetx)) || + ((myrpt->remmode != REM_MODE_FM) && + notremming)) + memset(f->data,0,f->datalen); + ast_write(myrpt->pchannel,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 == 7) printf("@@@@ remote rx key\n"); + if (!myrpt->remotetx) + { + remkeyed = 1; + } + } + /* if RX un-key */ + if (f->subclass == AST_CONTROL_RADIO_UNKEY) + { + if (debug == 7) printf("@@@@ remote rx un-key\n"); + if (!myrpt->remotetx) + { + remkeyed = 0; + } + } + } + ast_frfree(f); + continue; + } + if (who == myrpt->pchannel) /* if is remote mix output */ + { + f = ast_read(myrpt->pchannel); + if (!f) + { + if (debug) printf("@@@@ link:Hung Up\n"); + break; + } + if (f->frametype == AST_FRAME_VOICE) + { + ast_write(chan,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 ((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; + } + } + if (myrpt->p.archivedir) + { + char mycmd[100],*b,*b1; + + /* look at callerid to see what node this comes from */ + if (!chan->cid.cid_num) /* if doesn't have caller id */ + { + b1 = "0"; + } else { + ast_callerid_parse(chan->cid.cid_num,&b,&b1); + ast_shrink_phone_number(b1); + } + sprintf(mycmd,"DISCONNECT,%s",b1); + donodelog(myrpt,mycmd); + } + /* wait for telem to be done */ + while(myrpt->tele.next != &myrpt->tele) usleep(100000); + sprintf(tmp,"mixmonitor stop %s",chan->name); + ast_cli_command(nullfd,tmp); + close(nullfd); + rpt_mutex_lock(&myrpt->lock); + myrpt->hfscanmode = 0; + myrpt->hfscanstatus = 0; + myrpt->remoteon = 0; + rpt_mutex_unlock(&myrpt->lock); + if (myrpt->lastf1) ast_frfree(myrpt->lastf1); + myrpt->lastf1 = NULL; + if (myrpt->lastf2) ast_frfree(myrpt->lastf2); + myrpt->lastf2 = NULL; + if ((iskenwood_pci4) && (myrpt->txchannel == myrpt->zaptxchannel)) + { + z.radpar = DAHDI_RADPAR_UIOMODE; + z.data = 3; + if (ioctl(myrpt->zaptxchannel->fds[0],DAHDI_RADIO_SETPARAM,&z) == -1) + { + ast_log(LOG_ERROR,"Cannot set UIOMODE\n"); + return -1; + } + z.radpar = DAHDI_RADPAR_UIODATA; + z.data = 3; + if (ioctl(myrpt->zaptxchannel->fds[0],DAHDI_RADIO_SETPARAM,&z) == -1) + { + ast_log(LOG_ERROR,"Cannot set UIODATA\n"); + return -1; + } + i = DAHDI_OFFHOOK; + if (ioctl(myrpt->zaptxchannel->fds[0],DAHDI_HOOK,&i) == -1) + { + ast_log(LOG_ERROR,"Cannot set hook\n"); + return -1; + } + } + if (myrpt->iofd) close(myrpt->iofd); + myrpt->iofd = -1; + ast_hangup(myrpt->pchannel); + if (myrpt->rxchannel != myrpt->txchannel) ast_hangup(myrpt->txchannel); + ast_hangup(myrpt->rxchannel); + closerem(myrpt); +#ifdef OLD_ASTERISK + LOCAL_USER_REMOVE(u); +#endif + return res; +} + +#ifdef OLD_ASTERISK +int unload_module() +#else +static int unload_module(void) +#endif +{ + int i; + +#ifdef OLD_ASTERISK + STANDARD_HANGUP_LOCALUSERS; +#endif + for(i = 0; i < nrpts; i++) { + if (!strcmp(rpt_vars[i].name,rpt_vars[i].p.nodes)) continue; + ast_mutex_destroy(&rpt_vars[i].lock); + ast_mutex_destroy(&rpt_vars[i].remlock); + } + i = ast_unregister_application(app); + + /* Unregister cli extensions */ + ast_cli_unregister(&cli_debug); + ast_cli_unregister(&cli_dump); + ast_cli_unregister(&cli_stats); + ast_cli_unregister(&cli_lstats); + ast_cli_unregister(&cli_nodes); + ast_cli_unregister(&cli_reload); + ast_cli_unregister(&cli_restart); + ast_cli_unregister(&cli_fun); + + return i; +} + +#ifdef OLD_ASTERISK +int load_module() +#else +static int load_module(void) +#endif +{ + ast_pthread_create(&rpt_master_thread,NULL,rpt_master,NULL); + + /* Register cli extensions */ + ast_cli_register(&cli_debug); + ast_cli_register(&cli_dump); + ast_cli_register(&cli_stats); + ast_cli_register(&cli_lstats); + ast_cli_register(&cli_nodes); + ast_cli_register(&cli_reload); + ast_cli_register(&cli_restart); + ast_cli_register(&cli_fun); + + return ast_register_application(app, rpt_exec, synopsis, descrip); +} + +#ifdef OLD_ASTERISK +char *description() +{ + return tdesc; +} +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} +#endif + +#ifdef OLD_ASTERISK +int reload() +#else +static int reload(void) +#endif +{ +int n; + + for(n = 0; n < nrpts; n++) rpt_vars[n].reload = 1; + return(0); +} + +#ifndef OLD_ASTERISK +/* STD_MOD(MOD_1, reload, NULL, NULL); */ +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Radio Repeater/Remote Base Application", + .load = load_module, + .unload = unload_module, + .reload = reload, + ); + +#endif + |