Newer
Older
/*
* Asterisk -- An open source telephony toolkit.
* Copyright (C) 2002-2005, Jim Dixon, WB6NIL
* Jim Dixon, WB6NIL <jim@lambdatel.com>
* Serious contributions by Steve RoDgers, WA6ZFT <hwstar@rodgers.sdcoxmail.com>
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
* \brief Radio Repeater / Remote Base program
* version 0.48 06/13/06
* \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:
Jim Dixon
committed
* 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
* 5 - Dump System Variables on Console (debug)
* 6 - PTT (phone mode only)
*
* ilink cmds:
*
* 1 - Disconnect specified link
* 2 - Connect specified link -- monitor only
* 3 - Connect specified link -- tranceive
* 4 - Enter command mode on specified link
* 5 - System status
* 6 - Disconnect all links
*
* remote cmds:
*
* 1 - Recall Memory MM (*000-*099) (Gets memory from rpt.conf)
* 2 - Set VFO MMMMM*KKK*O (Mhz digits, Khz digits, Offset)
* 3 - Set Rx PL Tone HHH*D*
* 4 - Set Tx PL Tone HHH*D* (Not currently implemented with DHE RBI-1)
* 5 - Link Status (long)
* 6 - Set operating mode M (FM, USB, LSB, AM, etc)
* 100 - RX PL off (Default)
* 101 - RX PL On
* 102 - TX PL Off (Default)
* 103 - TX PL On
* 104 - Low Power
* 105 - Med Power
* 106 - Hi Power
* 107 - Bump Down 20 Hz
* 108 - Bump Down 100 Hz
* 109 - Bump Down 500 Hz
* 110 - Bump Up 20 Hz
* 111 - Bump Up 100 Hz
* 112 - Bump Up 500 Hz
* 113 - Scan Down Slow
* 114 - Scan Down Medium
* 115 - Scan Down Fast
* 116 - Scan Up Slow
* 117 - Scan Up Medium
* 118 - Scan Up Fast
* 119 - Transmit allowing auto-tune
* 140 - Link Status (brief)
*
*
Jim Dixon
committed
*
* '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
*
*/
Kevin P. Fleming
committed
/*** MODULEINFO
<depend>zaptel</depend>
<depend>tonezone</depend>
Kevin P. Fleming
committed
<defaultenabled>no</defaultenabled>
***/
Jim Dixon
committed
/* 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
Steve Murphy
committed
#define MAXGOSUB 2048
#define MACROTIME 100
Steve Murphy
committed
#define GOSUBTIME 100
#define MACROPTIME 500
Steve Murphy
committed
#define GOSUBPTIME 500
#define DTMF_TIMEOUT 3
Jim Dixon
committed
#ifdef __RPT_NOTCH
#define MAXFILTERS 10
#endif
#define DISC_TIME 10000 /* report disc after 10 seconds of no connect */
#define MAX_RETRIES 5
Jim Dixon
committed
#define MAXPEERSTR 31
#define MAXREMSTR 15
#define DELIMCHR ','
#define QUOTECHR 34
#define NODES "nodes"
#define MEMORY "memory"
#define MACRO "macro"
Steve Murphy
committed
#define GOSUB "gosub"
#define FUNCTIONS "functions"
#define TELEMETRY "telemetry"
#define MORSE "morse"
#define FUNCCHAR '*'
#define ENDCHAR '#'
#define DEFAULT_IOBASE 0x378
#define MAXCONNECTTIME 5000
#define MAXNODESTR 300
Jim Dixon
committed
#define MAXPATCHCONTEXT 100
#define ACTIONSIZE 32
#define TELEPARAMSIZE 256
#define REM_SCANTIME 100
enum {REM_OFF, REM_MONITOR, REM_TX};
enum {ID, PROC, TERM, COMPLETE, UNKEY, REMDISC, REMALREADY, REMNOTFOUND, REMGO,
CONNECTED, CONNFAIL, STATUS, TIMEOUT, ID1, STATS_TIME,
STATS_VERSION, IDTALKOVER, ARB_ALPHA, TEST_TONE, REV_PATCH,
Steve Murphy
committed
TAILMSG, MACRO_NOTFOUND, GOSUB_NOTFOUND, MACRO_BUSY, GOSUB_BUSY, LASTNODEKEY};
enum {REM_SIMPLEX, REM_MINUS, REM_PLUS};
enum {REM_LOWPWR, REM_MEDPWR, REM_HIPWR};
enum {DC_INDETERMINATE, DC_REQ_FLUSH, DC_ERROR, DC_COMPLETE, DC_DOKEY};
enum {SOURCE_RPT, SOURCE_LNK, SOURCE_RMT, SOURCE_PHONE, SOURCE_DPHONE};
enum {DLY_TELEM, DLY_ID, DLY_UNKEY, DLY_CALLTERM};
enum {REM_MODE_FM, REM_MODE_USB, REM_MODE_LSB, REM_MODE_AM};
enum {HF_SCAN_OFF, HF_SCAN_DOWN_SLOW, HF_SCAN_DOWN_QUICK,
HF_SCAN_DOWN_FAST, HF_SCAN_UP_SLOW, HF_SCAN_UP_QUICK, HF_SCAN_UP_FAST};
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <search.h>
#include <sys/stat.h>
#include <dirent.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/io.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "asterisk/utils.h"
#include "asterisk/lock.h"
#include "asterisk/file.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/cli.h"
#include "asterisk/config.h"
#include "asterisk/say.h"
#include "asterisk/localtime.h"
#include "asterisk/app.h"
#include "asterisk/zapata.h"
static char *app = "Rpt";
static char *synopsis = "Radio Repeater/Remote Base Control System";
static char *descrip =
Tilghman Lesher
committed
" 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"
Tilghman Lesher
committed
" 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 unsigned int vmajor = 0;
static unsigned int vminor = 47;
static int debug = 0; /* FIXME Set this >0 for extra debug output */
static int nrpts = 0;
static char *remote_rig_ft897 = "ft897";
static char *remote_rig_rbi = "rbi";
#define MSWAIT 200
#define HANGTIME 5000
#define TOTIME 180000
#define IDTIME 300000
#define MAXRPTS 20
Jim Dixon
committed
#define MAX_STAT_LINKS 32
Jim Dixon
committed
#define POLITEID 30000
#define FUNCTDELAY 1500
struct rpt;
struct rpt_link
{
struct rpt_link *next;
struct rpt_link *prev;
char mode; /* 1 if in tx mode */
char isremote;
char phonemode;
char name[MAXNODESTR]; /* identifier (routing) string */
char lasttx;
char lastrx;
char connected;
char outbound;
long elaptime;
long disctime;
long retrytimer;
Jim Dixon
committed
int reconnects;
long long connecttime;
struct ast_channel *chan;
struct ast_channel *pchan;
} ;
Jim Dixon
committed
struct rpt_lstat
{
struct rpt_lstat *next;
struct rpt_lstat *prev;
char peer[MAXPEERSTR];
char name[MAXNODESTR];
char mode;
char outbound;
char reconnects;
long long connecttime;
} ;
struct rpt_tele
{
struct rpt_tele *next;
struct rpt_tele *prev;
struct rpt *rpt;
struct ast_channel *chan;
int mode;
struct rpt_link mylink;
char param[TELEPARAMSIZE];
pthread_t threadid;
} ;
struct function_table_tag
{
char action[ACTIONSIZE];
int (*function)(struct rpt *myrpt, char *param, char *digitbuf,
int command_source, struct rpt_link *mylink);
} ;
/* Used to store the morse code patterns */
struct morse_bits
{
int len;
int ddcomb;
} ;
struct telem_defaults
{
char name[20];
char value[80];
} ;
ast_mutex_t lock;
Jim Dixon
committed
struct ast_config *cfg;
char reload;
char *name;
Jim Dixon
committed
char *remote;
struct {
char ourcontext[80];
char ourcallerid[80];
char acctcode[21];
char ident[80];
char tonezone[80];
Jim Dixon
committed
char simple;
char functions[80];
char link_functions[80];
char phone_functions[80];
char dphone_functions[80];
char nodes[80];
Jim Dixon
committed
int hangtime;
int totime;
int idtime;
int tailmessagetime;
int tailsquashedtime;
int duplex;
int politeid;
char *tailmsgbuf;
AST_DECLARE_APP_ARGS(tailmsg,
AST_APP_ARG(msgs)[100];
);
char memory[80];
char macro[80];
Steve Murphy
committed
char gosub[80];
char startupmacro[80];
Steve Murphy
committed
char startupgosub[80];
Jim Dixon
committed
int iobase;
char funcchar;
char endchar;
char nobusyout;
Jim Dixon
committed
} p;
struct rpt_link links;
int unkeytocttimer;
char keyed;
char exttx;
char localtx;
char remoterx;
char remotetx;
char remoteon;
char tounkeyed;
char tonotify;
char enable;
char dtmfbuf[MAXDTMF];
char macrobuf[MAXMACRO];
Steve Murphy
committed
char gosubbuf[MAXGOSUB];
char rem_dtmfbuf[MAXDTMF];
Jim Dixon
committed
char lastdtmfcommand[MAXDTMF];
char cmdnode[50];
struct ast_channel *rxchannel, *txchannel;
struct ast_channel *pchannel, *txpchannel, *remchannel;
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;
Jim Dixon
committed
int tailevent;
int telemrefcount;
int dtmfidx, rem_dtmfidx;
int dailytxtime, dailykerchunks, totalkerchunks, dailykeyups, totalkeyups, timeouts;
Jim Dixon
committed
int totalexecdcommands, dailyexecdcommands;
long retxtimer;
Jim Dixon
committed
long long totaltxtime;
char mydtmf;
char freq[MAXREMSTR], rxpl[MAXREMSTR], txpl[MAXREMSTR];
char offset;
char powerlevel;
char txplon;
char rxplon;
char remmode;
char tunerequest;
char hfscanmode;
int hfscanstatus;
char lastlinknode[MAXNODESTR];
Jim Dixon
committed
char patchfarenddisconnect;
char patchnoct;
char patchquiet;
char patchcontext[MAXPATCHCONTEXT];
int patchdialtime;
int macro_longest;
Steve Murphy
committed
int gosub_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;
Steve Murphy
committed
long gosubtimer;
char lastnodewhichkeyedusup[MAXNODESTR];
Jim Dixon
committed
#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;
Jim Dixon
committed
} filters[MAXFILTERS];
#endif
#ifdef _MDC_DECODE_H_
mdc_decoder_t *mdc;
unsigned short lastunit;
#endif
} rpt_vars[MAXRPTS];
#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] = "";
Tilghman Lesher
committed
struct ast_tm tm;
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;
Tilghman Lesher
committed
ast_strftime(a, sizeof(a) - 1, "%m/%d/%Y %H:%M:%S",
ast_localtime(&lock_ring_copy[j].tv, &tm, NULL));
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 */
/*
* CLI extensions
*/
/* Debug mode */
static char *handle_cli_rpt_debug_level(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static char *handle_cli_rpt_dump(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static char *handle_cli_rpt_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static char *handle_cli_rpt_lstats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static char *handle_cli_rpt_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static char *handle_cli_rpt_restart(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
Jim Dixon
committed
static struct ast_cli_entry cli_rpt[] = {
Jason Parker
committed
AST_CLI_DEFINE(handle_cli_rpt_debug_level, "Enable app_rpt debuggin"),
AST_CLI_DEFINE(handle_cli_rpt_dump, "Dump app_rpt structs for debugging"),
AST_CLI_DEFINE(handle_cli_rpt_stats, "Dump node statistics"),
AST_CLI_DEFINE(handle_cli_rpt_lstats, "Dump link statistics"),
AST_CLI_DEFINE(handle_cli_rpt_reload, "Reload app_rpt config"),
AST_CLI_DEFINE(handle_cli_rpt_restart, "Restart app_rpt")
Jim Dixon
committed
/*
* 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 their invocation
*/
static int setrbi(struct rpt *myrpt);
/*
* Define function protos for function table here
*/
static int function_ilink(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
static int function_autopatchdn(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
static int function_status(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
static int function_remote(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
static int function_macro(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
Steve Murphy
committed
static int function_gosub(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},
{"gosub", function_gosub},
} ;
Jim Dixon
committed
/*
* 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++) {
Jim Dixon
committed
ls = strlen(keywords[i]);
if (!ls) {
Jim Dixon
committed
*param = NULL;
return 0;
}
if (!strncmp(string, keywords[i], ls)) {
if (param)
Jim Dixon
committed
*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]) {
Jim Dixon
committed
string++;
break;
}
}
if (!charlist[i])
Jim Dixon
committed
return string;
}
return string;
}
Tilghman Lesher
committed
static int myatoi(const 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;
}
Jim Dixon
committed
#ifdef __RPT_NOTCH
/* rpt filter routine */
static void rpt_filter(struct rpt *myrpt, volatile short *buf, int len)
{
int i, j;
struct rptfilter *f;
Jim Dixon
committed
for (i = 0; i < len; i++) {
for (j = 0; j < MAXFILTERS; j++) {
Jim Dixon
committed
f = &myrpt->filters[j];
if (!*f->desc)
continue;
Jim Dixon
committed
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);
Jim Dixon
committed
buf[i] = (short)f->y2;
}
}
}
#endif
/* Retrieve an int from a config file */
static int retrieve_astcfgint(struct rpt *myrpt, const char *category, const char *name, int min, int max, int defl)
Jim Dixon
committed
{
const char *var = ast_variable_retrieve(myrpt->cfg, category, name);
int ret;
if (var) {
ret = myatoi(var);
if (ret < min)
ret = min;
else if (ret > max)
ret = max;
} else
ret = defl;
return ret;
Jim Dixon
committed
}
static void load_rpt_vars(int n, int init)
Jim Dixon
committed
{
int j;
struct ast_variable *vp, *var;
struct ast_config *cfg;
struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
Jim Dixon
committed
#ifdef __RPT_NOTCH
AST_DECLARE_APP_ARGS(strs,
AST_APP_ARG(str)[100];
);
Jim Dixon
committed
#endif
ast_verb(3, "%s config for repeater %s\n",
(init) ? "Loading initial" : "Re-Loading", rpt_vars[n].name);
Jim Dixon
committed
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", config_flags);
Jim Dixon
committed
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");
Jim Dixon
committed
pthread_exit(NULL);
}
rpt_vars[n].cfg = cfg;
/* Free previously malloc'ed buffer */
if (!init && rpt_vars[n].p.tailmsgbuf)
ast_free(rpt_vars[n].p.tailmsgbuf);
memset(&rpt_vars[n].p, 0, sizeof(rpt_vars[n].p));
if (init) {
Jim Dixon
committed
char *cp;
int savearea = (char *)&rpt_vars[n].p - (char *)&rpt_vars[n];
cp = (char *) &rpt_vars[n].p;
Jim Dixon
committed
sizeof(rpt_vars[n]) - (sizeof(rpt_vars[n].p) + savearea));
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));
Jim Dixon
committed
#endif
/* Defaults */
ast_copy_string(rpt_vars[n].p.ourcontext, rpt_vars[n].name, sizeof(rpt_vars[n].p.ourcontext));
rpt_vars[n].p.hangtime = HANGTIME;
rpt_vars[n].p.totime = TOTIME;
rpt_vars[n].p.duplex = 2;
rpt_vars[n].p.idtime = IDTIME;
rpt_vars[n].p.politeid = POLITEID;
ast_copy_string(rpt_vars[n].p.memory, MEMORY, sizeof(rpt_vars[n].p.memory));
ast_copy_string(rpt_vars[n].p.macro, MACRO, sizeof(rpt_vars[n].p.macro));
Steve Murphy
committed
ast_copy_string(rpt_vars[n].p.gosub, GOSUB, sizeof(rpt_vars[n].p.gosub));
Jim Dixon
committed
rpt_vars[n].p.iobase = DEFAULT_IOBASE;
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
ast_copy_string(rpt_vars[n].p.functions, FUNCTIONS, sizeof(rpt_vars[n].p.functions));
rpt_vars[n].p.simple = 1;
rpt_vars[n].p.funcchar = FUNCCHAR;
rpt_vars[n].p.endchar = ENDCHAR;
ast_copy_string(rpt_vars[n].p.nodes, NODES, sizeof(rpt_vars[n].p.nodes));
for (var = ast_variable_browse(cfg, rpt_vars[n].name); var; var = var->next) {
if (!strcmp(var->name, "context")) {
ast_copy_string(rpt_vars[n].p.ourcontext, var->value, sizeof(rpt_vars[n].p.ourcontext));
} else if (!strcmp(var->name, "callerid")) {
ast_copy_string(rpt_vars[n].p.ourcallerid, var->value, sizeof(rpt_vars[n].p.ourcallerid));
} else if (!strcmp(var->name, "accountcode")) {
ast_copy_string(rpt_vars[n].p.acctcode, var->value, sizeof(rpt_vars[n].p.acctcode));
} else if (!strcmp(var->name, "idrecording")) {
ast_copy_string(rpt_vars[n].p.ident, var->value, sizeof(rpt_vars[n].p.ident));
} else if (!strcmp(var->name, "hangtime")) {
rpt_vars[n].p.hangtime = atoi(var->value);
} else if (!strcmp(var->name, "totime")) {
rpt_vars[n].p.totime = atoi(var->value);
} else if (!strcmp(var->name, "tailmessagetime")) {
rpt_vars[n].p.tailmessagetime = atoi(var->value);
if (rpt_vars[n].p.tailmessagetime < 0)
rpt_vars[n].p.tailmessagetime = 0;
else if (rpt_vars[n].p.tailmessagetime > 2400000)
rpt_vars[n].p.tailmessagetime = 2400000;
} else if (!strcmp(var->name, "tailsquashedtime")) {
rpt_vars[n].p.tailsquashedtime = atoi(var->value);
if (rpt_vars[n].p.tailsquashedtime < 0)
rpt_vars[n].p.tailsquashedtime = 0;
else if (rpt_vars[n].p.tailsquashedtime > 2400000)
rpt_vars[n].p.tailsquashedtime = 2400000;
} else if (!strcmp(var->name, "duplex")) {
rpt_vars[n].p.duplex = atoi(var->value);
if (rpt_vars[n].p.duplex < 0)
rpt_vars[n].p.duplex = 0;
else if (rpt_vars[n].p.duplex > 4)
rpt_vars[n].p.duplex = 4;
} else if (!strcmp(var->name, "idtime")) {
rpt_vars[n].p.idtime = atoi(var->value);
if (rpt_vars[n].p.idtime < 60000)
rpt_vars[n].p.idtime = 60000;
else if (rpt_vars[n].p.idtime > 2400000)
rpt_vars[n].p.idtime = 2400000;
} else if (!strcmp(var->name, "politeid")) {
rpt_vars[n].p.politeid = atoi(var->value);
if (rpt_vars[n].p.politeid < 30000)
rpt_vars[n].p.politeid = 30000;
else if (rpt_vars[n].p.politeid > 300000)
rpt_vars[n].p.politeid = 300000;
} else if (!strcmp(var->name, "tonezone")) {
ast_copy_string(rpt_vars[n].p.tonezone, var->value, sizeof(rpt_vars[n].p.tonezone));
} else if (!strcmp(var->name, "tailmessagelist")) {
rpt_vars[n].p.tailmsgbuf = ast_strdup(var->value);
Mark Michelson
committed
AST_STANDARD_APP_ARGS(rpt_vars[n].p.tailmsg, rpt_vars[n].p.tailmsgbuf);
} else if (!strcmp(var->name, "memory")) {
ast_copy_string(rpt_vars[n].p.memory, var->value, sizeof(rpt_vars[n].p.memory));
} else if (!strcmp(var->name, "macro")) {
ast_copy_string(rpt_vars[n].p.macro, var->value, sizeof(rpt_vars[n].p.macro));
Steve Murphy
committed
} else if (!strcmp(var->name, "gosub")) {
ast_copy_string(rpt_vars[n].p.gosub, var->value, sizeof(rpt_vars[n].p.gosub));
} else if (!strcmp(var->name, "startup_macro")) {
ast_copy_string(rpt_vars[n].p.startupmacro, var->value, sizeof(rpt_vars[n].p.startupmacro));
Steve Murphy
committed
} else if (!strcmp(var->name, "startup_gosub")) {
ast_copy_string(rpt_vars[n].p.startupgosub, var->value, sizeof(rpt_vars[n].p.startupgosub));
} else if (!strcmp(var->name, "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 (sscanf(var->value, "%i", &rpt_vars[n].p.iobase) != 1)
rpt_vars[n].p.iobase = DEFAULT_IOBASE;
} else if (!strcmp(var->name, "functions")) {
rpt_vars[n].p.simple = 0;
ast_copy_string(rpt_vars[n].p.functions, var->value, sizeof(rpt_vars[n].p.functions));
} else if (!strcmp(var->name, "link_functions")) {
ast_copy_string(rpt_vars[n].p.link_functions, var->value, sizeof(rpt_vars[n].p.link_functions));
} else if (!strcmp(var->name, "phone_functions")) {
ast_copy_string(rpt_vars[n].p.phone_functions, var->value, sizeof(rpt_vars[n].p.phone_functions));
} else if (!strcmp(var->name, "dphone_functions")) {
ast_copy_string(rpt_vars[n].p.dphone_functions, var->value, sizeof(rpt_vars[n].p.dphone_functions));
} else if (!strcmp(var->name, "funcchar")) {
rpt_vars[n].p.funcchar = *var->value;
} else if (!strcmp(var->name, "endchar")) {
rpt_vars[n].p.endchar = *var->value;