2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2002-2005, Jim Dixon, WB6NIL
6 * Jim Dixon, WB6NIL <jim@lambdatel.com>
7 * Serious contributions by Steve RoDgers, WA6ZFT <hwstar@rodgers.sdcoxmail.com>
9 * See http://www.asterisk.org for more information about
10 * the Asterisk project. Please do not directly contact
11 * any of the maintainers of this project for assistance;
12 * the project provides a web site, mailing lists and IRC
13 * channels for your use.
15 * This program is free software, distributed under the terms of
16 * the GNU General Public License Version 2. See the LICENSE file
17 * at the top of the source tree.
22 * \brief Radio Repeater / Remote Base program
23 * version 0.48 06/13/06
25 * \author Jim Dixon, WB6NIL <jim@lambdatel.com>
27 * \note Serious contributions by Steve RoDgers, WA6ZFT <hwstar@rodgers.sdcoxmail.com>
29 * See http://www.zapatatelephony.org/app_rpt.html
32 * Repeater / Remote Functions:
33 * "Simple" Mode: * - autopatch access, # - autopatch hangup
35 * See the function list in rpt.conf (autopatchup, autopatchdn)
36 * autopatchup can optionally take comma delimited setting=value pairs:
39 * context=string : Override default context with "string"
40 * dialtime=ms : Specify the max number of milliseconds between phone number digits (1000 milliseconds = 1 second)
41 * farenddisconnect=1 : Automatically disconnect when called party hangs up
42 * noct=1 : Don't send repeater courtesy tone during autopatch calls
43 * quiet=1 : Don't send dial tone, or connect messages. Do not send patch down message when called party hangs up
46 * Example: 123=autopatchup,dialtime=20000,noct=1,farenddisconnect=1
48 * To send an asterisk (*) while dialing or talking on phone,
49 * use the autopatch acess code.
55 * 2 - Give Time of Day
56 * 3 - Give software Version
58 * cop (control operator) cmds:
60 * 1 - System warm boot
64 * 5 - Dump System Variables on Console (debug)
65 * 6 - PTT (phone mode only)
69 * 1 - Disconnect specified link
70 * 2 - Connect specified link -- monitor only
71 * 3 - Connect specified link -- tranceive
72 * 4 - Enter command mode on specified link
74 * 6 - Disconnect all links
78 * 1 - Recall Memory MM (*000-*099) (Gets memory from rpt.conf)
79 * 2 - Set VFO MMMMM*KKK*O (Mhz digits, Khz digits, Offset)
80 * 3 - Set Rx PL Tone HHH*D*
81 * 4 - Set Tx PL Tone HHH*D* (Not currently implemented with DHE RBI-1)
82 * 5 - Link Status (long)
83 * 6 - Set operating mode M (FM, USB, LSB, AM, etc)
84 * 100 - RX PL off (Default)
86 * 102 - TX PL Off (Default)
91 * 107 - Bump Down 20 Hz
92 * 108 - Bump Down 100 Hz
93 * 109 - Bump Down 500 Hz
95 * 111 - Bump Up 100 Hz
96 * 112 - Bump Up 500 Hz
97 * 113 - Scan Down Slow
98 * 114 - Scan Down Medium
99 * 115 - Scan Down Fast
101 * 117 - Scan Up Medium
103 * 119 - Transmit allowing auto-tune
104 * 140 - Link Status (brief)
108 * 'duplex' modes: (defaults to duplex=2)
110 * 0 - Only remote links key Tx and no main repeat audio.
111 * 1 - Everything other then main Rx keys Tx, no main repeat audio.
113 * 3 - Normal except no main repeat audio.
114 * 4 - Normal except no main repeat audio during autopatch only
119 <depend>zaptel</depend>
120 <depend>tonezone</depend>
121 <defaultenabled>no</defaultenabled>
124 /* Un-comment the following to include support for MDC-1200 digital tone
125 signalling protocol (using KA6SQG's GPL'ed implementation) */
126 /* #include "mdc_decode.c" */
128 /* Un-comment the following to include support for notch filters in the
129 rx audio stream (using Tony Fisher's mknotch (mkfilter) implementation) */
130 /* #include "rpt_notch.c" */
132 /* maximum digits in DTMF buffer, and seconds after * for DTMF command timeout */
135 #define MAXMACRO 2048
136 #define MACROTIME 100
137 #define MACROPTIME 500
138 #define DTMF_TIMEOUT 3
141 #define MAXFILTERS 10
144 #define DISC_TIME 10000 /* report disc after 10 seconds of no connect */
145 #define MAX_RETRIES 5
147 #define REDUNDANT_TX_TIME 2000
149 #define RETRY_TIMER_MS 5000
151 #define MAXPEERSTR 31
157 #define NODES "nodes"
158 #define MEMORY "memory"
159 #define MACRO "macro"
160 #define FUNCTIONS "functions"
161 #define TELEMETRY "telemetry"
162 #define MORSE "morse"
166 #define DEFAULT_IOBASE 0x378
168 #define MAXCONNECTTIME 5000
170 #define MAXNODESTR 300
172 #define MAXPATCHCONTEXT 100
174 #define ACTIONSIZE 32
176 #define TELEPARAMSIZE 256
178 #define REM_SCANTIME 100
181 enum {REM_OFF, REM_MONITOR, REM_TX};
183 enum {ID, PROC, TERM, COMPLETE, UNKEY, REMDISC, REMALREADY, REMNOTFOUND, REMGO,
184 CONNECTED, CONNFAIL, STATUS, TIMEOUT, ID1, STATS_TIME,
185 STATS_VERSION, IDTALKOVER, ARB_ALPHA, TEST_TONE, REV_PATCH,
186 TAILMSG, MACRO_NOTFOUND, MACRO_BUSY, LASTNODEKEY};
188 enum {REM_SIMPLEX, REM_MINUS, REM_PLUS};
190 enum {REM_LOWPWR, REM_MEDPWR, REM_HIPWR};
192 enum {DC_INDETERMINATE, DC_REQ_FLUSH, DC_ERROR, DC_COMPLETE, DC_DOKEY};
194 enum {SOURCE_RPT, SOURCE_LNK, SOURCE_RMT, SOURCE_PHONE, SOURCE_DPHONE};
196 enum {DLY_TELEM, DLY_ID, DLY_UNKEY, DLY_CALLTERM};
198 enum {REM_MODE_FM, REM_MODE_USB, REM_MODE_LSB, REM_MODE_AM};
200 enum {HF_SCAN_OFF, HF_SCAN_DOWN_SLOW, HF_SCAN_DOWN_QUICK,
201 HF_SCAN_DOWN_FAST, HF_SCAN_UP_SLOW, HF_SCAN_UP_QUICK, HF_SCAN_UP_FAST};
203 #include "asterisk.h"
205 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
213 #include <sys/types.h>
214 #include <sys/stat.h>
218 #include <sys/stat.h>
219 #include <sys/time.h>
220 #include <sys/file.h>
221 #include <sys/ioctl.h>
224 #include <netinet/in.h>
225 #include <arpa/inet.h>
227 #include "asterisk/utils.h"
228 #include "asterisk/lock.h"
229 #include "asterisk/file.h"
230 #include "asterisk/logger.h"
231 #include "asterisk/channel.h"
232 #include "asterisk/callerid.h"
233 #include "asterisk/pbx.h"
234 #include "asterisk/module.h"
235 #include "asterisk/translate.h"
236 #include "asterisk/features.h"
237 #include "asterisk/options.h"
238 #include "asterisk/cli.h"
239 #include "asterisk/config.h"
240 #include "asterisk/say.h"
241 #include "asterisk/localtime.h"
242 #include "asterisk/app.h"
244 #include "asterisk/zapata.h"
246 static char *app = "Rpt";
248 static char *synopsis = "Radio Repeater/Remote Base Control System";
250 static char *descrip =
251 " Rpt(nodename[|options]): Radio Remote Link or Remote Base Link Endpoint Process.\n"
253 " Not specifying an option puts it in normal endpoint mode (where source\n"
254 " IP and nodename are verified).\n"
256 " Options are as follows:\n"
258 " X - Normal endpoint mode WITHOUT security check. Only specify\n"
259 " this if you have checked security already (like with an IAX2\n"
260 " user/password or something).\n"
262 " Rannounce-string[|timeout[|timeout-destination]] - Amateur Radio\n"
263 " Reverse Autopatch. Caller is put on hold, and announcement (as\n"
264 " specified by the 'announce-string') is played on radio system.\n"
265 " Users of radio system can access autopatch, dial specified\n"
266 " code, and pick up call. Announce-string is list of names of\n"
267 " recordings, or \"PARKED\" to substitute code for un-parking,\n"
268 " or \"NODE\" to substitute node number.\n"
270 " P - Phone Control mode. This allows a regular phone user to have\n"
271 " full control and audio access to the radio system. For the\n"
272 " user to have DTMF control, the 'phone_functions' parameter\n"
273 " must be specified for the node in 'rpt.conf'. An additional\n"
274 " function (cop,6) must be listed so that PTT control is available.\n"
276 " D - Dumb Phone Control mode. This allows a regular phone user to\n"
277 " have full control and audio access to the radio system. In this\n"
278 " mode, the PTT is activated for the entire length of the call.\n"
279 " For the user to have DTMF control (not generally recomended in\n"
280 " this mode), the 'dphone_functions' parameter must be specified\n"
281 " for the node in 'rpt.conf'. Otherwise no DTMF control will be\n"
282 " available to the phone user.\n"
285 static unsigned int vmajor = 0;
286 static unsigned int vminor = 47;
288 static int debug = 0; /* FIXME Set this >0 for extra debug output */
289 static int nrpts = 0;
291 char *discstr = "!!DISCONNECT!!";
292 static char *remote_rig_ft897 = "ft897";
293 static char *remote_rig_rbi = "rbi";
301 #define HANGTIME 5000
302 #define TOTIME 180000
303 #define IDTIME 300000
305 #define MAX_STAT_LINKS 32
306 #define POLITEID 30000
307 #define FUNCTDELAY 1500
309 static pthread_t rpt_master_thread;
315 struct rpt_link *next;
316 struct rpt_link *prev;
317 char mode; /* 1 if in tx mode */
320 char name[MAXNODESTR]; /* identifier (routing) string */
334 long long connecttime;
335 struct ast_channel *chan;
336 struct ast_channel *pchan;
341 struct rpt_lstat *next;
342 struct rpt_lstat *prev;
343 char peer[MAXPEERSTR];
344 char name[MAXNODESTR];
348 long long connecttime;
353 struct rpt_tele *next;
354 struct rpt_tele *prev;
356 struct ast_channel *chan;
358 struct rpt_link mylink;
359 char param[TELEPARAMSIZE];
363 struct function_table_tag
365 char action[ACTIONSIZE];
366 int (*function)(struct rpt *myrpt, char *param, char *digitbuf,
367 int command_source, struct rpt_link *mylink);
370 /* Used to store the morse code patterns */
378 struct telem_defaults
388 struct ast_config *cfg;
398 char ourcallerid[80];
404 char link_functions[80];
405 char phone_functions[80];
406 char dphone_functions[80];
412 int tailsquashedtime;
416 AST_DECLARE_APP_ARGS(tailmsg,
417 AST_APP_ARG(msgs)[100];
421 char startupmacro[80];
427 struct rpt_link links;
438 char dtmfbuf[MAXDTMF];
439 char macrobuf[MAXMACRO];
440 char rem_dtmfbuf[MAXDTMF];
441 char lastdtmfcommand[MAXDTMF];
443 struct ast_channel *rxchannel, *txchannel;
444 struct ast_channel *pchannel, *txpchannel, *remchannel;
445 struct rpt_tele tele;
446 struct timeval lasttv, curtv;
447 pthread_t rpt_call_thread, rpt_thread;
448 time_t dtmf_time, rem_dtmf_time, dtmf_time_rem;
449 int tailtimer, totimer, idtimer, txconf, conf, callmode, cidx, scantimer, tmsgtimer, skedtimer;
453 int dtmfidx, rem_dtmfidx;
454 int dailytxtime, dailykerchunks, totalkerchunks, dailykeyups, totalkeyups, timeouts;
455 int totalexecdcommands, dailyexecdcommands;
457 long long totaltxtime;
459 char exten[AST_MAX_EXTENSION];
460 char freq[MAXREMSTR], rxpl[MAXREMSTR], txpl[MAXREMSTR];
469 char lastlinknode[MAXNODESTR];
471 char patchfarenddisconnect;
474 char patchcontext[MAXPATCHCONTEXT];
477 int phone_longestfunc;
478 int dphone_longestfunc;
479 int link_longestfunc;
485 time_t lastthreadrestarttime;
487 char lastnodewhichkeyedusup[MAXNODESTR];
502 } filters[MAXFILTERS];
504 #ifdef _MDC_DECODE_H_
506 unsigned short lastunit;
511 #ifdef APP_RPT_LOCK_DEBUG
513 #warning COMPILING WITH LOCK-DEBUGGING ENABLED!!
515 #define MAXLOCKTHREAD 100
517 #define rpt_mutex_lock(x) _rpt_mutex_lock(x, myrpt, __LINE__)
518 #define rpt_mutex_unlock(x) _rpt_mutex_unlock(x, myrpt, __LINE__)
526 } lockthreads[MAXLOCKTHREAD];
534 struct lockthread lockthread;
538 int lock_ring_index = 0;
540 AST_MUTEX_DEFINE_STATIC(locklock);
542 static struct lockthread *get_lockthread(pthread_t id)
546 for (i = 0; i < MAXLOCKTHREAD; i++) {
547 if (lockthreads[i].id == id)
548 return(&lockthreads[i]);
553 static struct lockthread *put_lockthread(pthread_t id)
557 for (i = 0; i < MAXLOCKTHREAD; i++) {
558 if (lockthreads[i].id == id)
559 return(&lockthreads[i]);
561 for (i = 0; i < MAXLOCKTHREAD; i++) {
562 if (!lockthreads[i].id) {
563 lockthreads[i].lockcount = 0;
564 lockthreads[i].lastlock = 0;
565 lockthreads[i].lastunlock = 0;
566 lockthreads[i].id = id;
567 return &lockthreads[i];
574 static void rpt_mutex_spew(void)
576 struct by_lightning lock_ring_copy[32];
577 int lock_ring_index_copy;
581 struct timeval lasttv;
583 ast_mutex_lock(&locklock);
584 memcpy(&lock_ring_copy, &lock_ring, sizeof(lock_ring_copy));
585 lock_ring_index_copy = lock_ring_index;
586 ast_mutex_unlock(&locklock);
588 lasttv.tv_sec = lasttv.tv_usec = 0;
589 for (i = 0; i < 32; i++) {
590 j = (i + lock_ring_index_copy) % 32;
591 strftime(a, sizeof(a) - 1, "%m/%d/%Y %H:%M:%S",
592 localtime(&lock_ring_copy[j].tv.tv_sec));
595 diff = (lock_ring_copy[j].tv.tv_sec - lasttv.tv_sec) * 1000000;
596 diff += (lock_ring_copy[j].tv.tv_usec - lasttv.tv_usec);
598 lasttv.tv_sec = lock_ring_copy[j].tv.tv_sec;
599 lasttv.tv_usec = lock_ring_copy[j].tv.tv_usec;
600 if (!lock_ring_copy[j].tv.tv_sec)
602 if (lock_ring_copy[j].line < 0) {
603 ast_log(LOG_NOTICE, "LOCKDEBUG [#%d] UNLOCK app_rpt.c:%d node %s pid %x diff %lld us at %s.%06d\n",
604 i - 31, -lock_ring_copy[j].line, lock_ring_copy[j].rpt->name,
605 (int) lock_ring_copy[j].lockthread.id, diff, a, (int)lock_ring_copy[j].tv.tv_usec);
607 ast_log(LOG_NOTICE, "LOCKDEBUG [#%d] LOCK app_rpt.c:%d node %s pid %x diff %lld us at %s.%06d\n",
608 i - 31, lock_ring_copy[j].line, lock_ring_copy[j].rpt->name,
609 (int) lock_ring_copy[j].lockthread.id, diff, a, (int)lock_ring_copy[j].tv.tv_usec);
615 static void _rpt_mutex_lock(ast_mutex_t *lockp, struct rpt *myrpt, int line)
617 struct lockthread *t;
621 ast_mutex_lock(&locklock);
622 t = put_lockthread(id);
624 ast_mutex_unlock(&locklock);
628 int lastline = t->lastlock;
629 ast_mutex_unlock(&locklock);
630 ast_log(LOG_NOTICE, "rpt_mutex_lock: Double lock request line %d node %s pid %x, last lock was line %d\n",
631 line, myrpt->name, (int) t->id, lastline);
637 gettimeofday(&lock_ring[lock_ring_index].tv, NULL);
638 lock_ring[lock_ring_index].rpt = myrpt;
639 memcpy(&lock_ring[lock_ring_index].lockthread, t, sizeof(struct lockthread));
640 lock_ring[lock_ring_index++].line = line;
641 if (lock_ring_index == 32)
643 ast_mutex_unlock(&locklock);
644 ast_mutex_lock(lockp);
648 static void _rpt_mutex_unlock(ast_mutex_t *lockp, struct rpt *myrpt, int line)
650 struct lockthread *t;
654 ast_mutex_lock(&locklock);
655 t = put_lockthread(id);
657 ast_mutex_unlock(&locklock);
661 int lastline = t->lastunlock;
662 ast_mutex_unlock(&locklock);
663 ast_log(LOG_NOTICE, "rpt_mutex_lock: Double un-lock request line %d node %s pid %x, last un-lock was line %d\n",
664 line, myrpt->name, (int) t->id, lastline);
668 t->lastunlock = line;
670 gettimeofday(&lock_ring[lock_ring_index].tv, NULL);
671 lock_ring[lock_ring_index].rpt = myrpt;
672 memcpy(&lock_ring[lock_ring_index].lockthread, t, sizeof(struct lockthread));
673 lock_ring[lock_ring_index++].line = -line;
674 if (lock_ring_index == 32)
676 ast_mutex_unlock(&locklock);
677 ast_mutex_unlock(lockp);
680 #else /* APP_RPT_LOCK_DEBUG */
682 #define rpt_mutex_lock(x) ast_mutex_lock(x)
683 #define rpt_mutex_unlock(x) ast_mutex_unlock(x)
685 #endif /* APP_RPT_LOCK_DEBUG */
692 static int rpt_do_debug(int fd, int argc, char *argv[]);
693 static int rpt_do_dump(int fd, int argc, char *argv[]);
694 static int rpt_do_stats(int fd, int argc, char *argv[]);
695 static int rpt_do_lstats(int fd, int argc, char *argv[]);
696 static int rpt_do_reload(int fd, int argc, char *argv[]);
697 static int rpt_do_restart(int fd, int argc, char *argv[]);
699 static char debug_usage[] =
700 "Usage: rpt debug level {0-7}\n"
701 " Enables debug messages in app_rpt\n";
703 static char dump_usage[] =
704 "Usage: rpt dump <nodename>\n"
705 " Dumps struct debug info to log\n";
707 static char dump_stats[] =
708 "Usage: rpt stats <nodename>\n"
709 " Dumps node statistics to console\n";
711 static char dump_lstats[] =
712 "Usage: rpt lstats <nodename>\n"
713 " Dumps link statistics to console\n";
715 static char reload_usage[] =
716 "Usage: rpt reload\n"
717 " Reloads app_rpt running config parameters\n";
719 static char restart_usage[] =
720 "Usage: rpt restart\n"
721 " Restarts app_rpt\n";
723 static struct ast_cli_entry cli_rpt[] = {
724 { { "rpt", "debug", "level" },
725 rpt_do_debug, "Enable app_rpt debugging",
729 rpt_do_dump, "Dump app_rpt structs for debugging",
732 { { "rpt", "stats" },
733 rpt_do_stats, "Dump node statistics",
735 { { "rpt", "lstats" },
736 rpt_do_lstats, "Dump link statistics",
739 { { "rpt", "reload" },
740 rpt_do_reload, "Reload app_rpt config",
743 { { "rpt", "restart" },
744 rpt_do_restart, "Restart app_rpt",
753 static struct telem_defaults tele_defs[] = {
754 {"ct1", "|t(350,0,100,3072)(500,0,100,3072)(660,0,100,3072)"},
755 {"ct2", "|t(660,880,150,3072)"},
756 {"ct3", "|t(440,0,150,3072)"},
757 {"ct4", "|t(550,0,150,3072)"},
758 {"ct5", "|t(660,0,150,3072)"},
759 {"ct6", "|t(880,0,150,3072)"},
760 {"ct7", "|t(660,440,150,3072)"},
761 {"ct8", "|t(700,1100,150,3072)"},
762 {"remotemon", "|t(1600,0,75,2048)"},
763 {"remotetx", "|t(2000,0,75,2048)(0,0,75,0)(1600,0,75,2048)"},
764 {"cmdmode", "|t(900,904,200,2048)"},
765 {"functcomplete", "|t(1000,0,100,2048)(0,0,100,0)(1000,0,100,2048)"}
769 * Forward decl's - these suppress compiler warnings when funcs coded further down the file than their invocation
772 static int setrbi(struct rpt *myrpt);
777 * Define function protos for function table here
780 static int function_ilink(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
781 static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
782 static int function_autopatchdn(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
783 static int function_status(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
784 static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
785 static int function_remote(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
786 static int function_macro(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
791 static struct function_table_tag function_table[] = {
792 {"cop", function_cop},
793 {"autopatchup", function_autopatchup},
794 {"autopatchdn", function_autopatchdn},
795 {"ilink", function_ilink},
796 {"status", function_status},
797 {"remote", function_remote},
798 {"macro", function_macro}
802 * Match a keyword in a list, and return index of string plus 1 if there was a match,
803 * else return 0. If param is passed in non-null, then it will be set to the first character past the match
806 static int matchkeyword(char *string, char **param, char *keywords[])
809 for (i = 0; keywords[i]; i++) {
810 ls = strlen(keywords[i]);
815 if (!strncmp(string, keywords[i], ls)) {
817 *param = string + ls;
826 * Skip characters in string which are in charlist, and return a pointer to the
827 * first non-matching character
830 static char *skipchars(char *string, char *charlist)
834 for (i = 0; charlist[i] ; i++) {
835 if (*string == charlist[i]) {
848 static int myatoi(const char *str)
854 /* leave this %i alone, non-base-10 input is useful here */
855 if (sscanf(str, "%i", &ret) != 1)
863 /* rpt filter routine */
864 static void rpt_filter(struct rpt *myrpt, volatile short *buf, int len)
869 for (i = 0; i < len; i++) {
870 for (j = 0; j < MAXFILTERS; j++) {
871 f = &myrpt->filters[j];
874 f->x0 = f->x1; f->x1 = f->x2;
875 f->x2 = ((float)buf[i]) / f->gain;
876 f->y0 = f->y1; f->y1 = f->y2;
877 f->y2 = (f->x0 + f->x2) + f->const0 * f->x1
878 + (f->const1 * f->y0) + (f->const2 * f->y1);
879 buf[i] = (short)f->y2;
886 /* Retrieve an int from a config file */
887 static int retrieve_astcfgint(struct rpt *myrpt, const char *category, const char *name, int min, int max, int defl)
889 const char *var = ast_variable_retrieve(myrpt->cfg, category, name);
904 static void load_rpt_vars(int n, int init)
907 struct ast_variable *vp, *var;
908 struct ast_config *cfg;
910 AST_DECLARE_APP_ARGS(strs,
911 AST_APP_ARG(str)[100];
915 if (option_verbose > 2)
916 ast_verbose(VERBOSE_PREFIX_3 "%s config for repeater %s\n",
917 (init) ? "Loading initial" : "Re-Loading", rpt_vars[n].name);
918 ast_mutex_lock(&rpt_vars[n].lock);
920 ast_config_destroy(rpt_vars[n].cfg);
921 cfg = ast_config_load("rpt.conf");
923 ast_mutex_unlock(&rpt_vars[n].lock);
924 ast_log(LOG_NOTICE, "Unable to open radio repeater configuration rpt.conf. Radio Repeater disabled.\n");
927 rpt_vars[n].cfg = cfg;
928 /* Free previously malloc'ed buffer */
929 if (!init && rpt_vars[n].p.tailmsgbuf)
930 ast_free(rpt_vars[n].p.tailmsgbuf);
931 memset(&rpt_vars[n].p, 0, sizeof(rpt_vars[n].p));
934 int savearea = (char *)&rpt_vars[n].p - (char *)&rpt_vars[n];
936 cp = (char *) &rpt_vars[n].p;
937 memset(cp + sizeof(rpt_vars[n].p), 0,
938 sizeof(rpt_vars[n]) - (sizeof(rpt_vars[n].p) + savearea));
939 rpt_vars[n].tele.next = &rpt_vars[n].tele;
940 rpt_vars[n].tele.prev = &rpt_vars[n].tele;
941 rpt_vars[n].rpt_thread = AST_PTHREADT_NULL;
942 rpt_vars[n].tailmessagen = 0;
945 /* zot out filters stuff */
946 memset(&rpt_vars[n].filters, 0, sizeof(rpt_vars[n].filters));
950 ast_copy_string(rpt_vars[n].p.ourcontext, rpt_vars[n].name, sizeof(rpt_vars[n].p.ourcontext));
951 rpt_vars[n].p.hangtime = HANGTIME;
952 rpt_vars[n].p.totime = TOTIME;
953 rpt_vars[n].p.duplex = 2;
954 rpt_vars[n].p.idtime = IDTIME;
955 rpt_vars[n].p.politeid = POLITEID;
956 ast_copy_string(rpt_vars[n].p.memory, MEMORY, sizeof(rpt_vars[n].p.memory));
957 ast_copy_string(rpt_vars[n].p.macro, MACRO, sizeof(rpt_vars[n].p.macro));
958 rpt_vars[n].p.iobase = DEFAULT_IOBASE;
959 ast_copy_string(rpt_vars[n].p.functions, FUNCTIONS, sizeof(rpt_vars[n].p.functions));
960 rpt_vars[n].p.simple = 1;
961 rpt_vars[n].p.funcchar = FUNCCHAR;
962 rpt_vars[n].p.endchar = ENDCHAR;
963 ast_copy_string(rpt_vars[n].p.nodes, NODES, sizeof(rpt_vars[n].p.nodes));
965 for (var = ast_variable_browse(cfg, rpt_vars[n].name); var; var = var->next) {
966 if (!strcmp(var->name, "context")) {
967 ast_copy_string(rpt_vars[n].p.ourcontext, var->value, sizeof(rpt_vars[n].p.ourcontext));
968 } else if (!strcmp(var->name, "callerid")) {
969 ast_copy_string(rpt_vars[n].p.ourcallerid, var->value, sizeof(rpt_vars[n].p.ourcallerid));
970 } else if (!strcmp(var->name, "accountcode")) {
971 ast_copy_string(rpt_vars[n].p.acctcode, var->value, sizeof(rpt_vars[n].p.acctcode));
972 } else if (!strcmp(var->name, "idrecording")) {
973 ast_copy_string(rpt_vars[n].p.ident, var->value, sizeof(rpt_vars[n].p.ident));
974 } else if (!strcmp(var->name, "hangtime")) {
975 rpt_vars[n].p.hangtime = atoi(var->value);
976 } else if (!strcmp(var->name, "totime")) {
977 rpt_vars[n].p.totime = atoi(var->value);
978 } else if (!strcmp(var->name, "tailmessagetime")) {
979 rpt_vars[n].p.tailmessagetime = atoi(var->value);
980 if (rpt_vars[n].p.tailmessagetime < 0)
981 rpt_vars[n].p.tailmessagetime = 0;
982 else if (rpt_vars[n].p.tailmessagetime > 2400000)
983 rpt_vars[n].p.tailmessagetime = 2400000;
984 } else if (!strcmp(var->name, "tailsquashedtime")) {
985 rpt_vars[n].p.tailsquashedtime = atoi(var->value);
986 if (rpt_vars[n].p.tailsquashedtime < 0)
987 rpt_vars[n].p.tailsquashedtime = 0;
988 else if (rpt_vars[n].p.tailsquashedtime > 2400000)
989 rpt_vars[n].p.tailsquashedtime = 2400000;
990 } else if (!strcmp(var->name, "duplex")) {
991 rpt_vars[n].p.duplex = atoi(var->value);
992 if (rpt_vars[n].p.duplex < 0)
993 rpt_vars[n].p.duplex = 0;
994 else if (rpt_vars[n].p.duplex > 4)
995 rpt_vars[n].p.duplex = 4;
996 } else if (!strcmp(var->name, "idtime")) {
997 rpt_vars[n].p.idtime = atoi(var->value);
998 if (rpt_vars[n].p.idtime < 60000)
999 rpt_vars[n].p.idtime = 60000;
1000 else if (rpt_vars[n].p.idtime > 2400000)
1001 rpt_vars[n].p.idtime = 2400000;
1002 } else if (!strcmp(var->name, "politeid")) {
1003 rpt_vars[n].p.politeid = atoi(var->value);
1004 if (rpt_vars[n].p.politeid < 30000)
1005 rpt_vars[n].p.politeid = 30000;
1006 else if (rpt_vars[n].p.politeid > 300000)
1007 rpt_vars[n].p.politeid = 300000;
1008 } else if (!strcmp(var->name, "tonezone")) {
1009 ast_copy_string(rpt_vars[n].p.tonezone, var->value, sizeof(rpt_vars[n].p.tonezone));
1010 } else if (!strcmp(var->name, "tailmessagelist")) {
1011 rpt_vars[n].p.tailmsgbuf = ast_strdup(var->value);
1012 AST_NONSTANDARD_APP_ARGS(rpt_vars[n].p.tailmsg, rpt_vars[n].p.tailmsgbuf, ',');
1013 } else if (!strcmp(var->name, "memory")) {
1014 ast_copy_string(rpt_vars[n].p.memory, var->value, sizeof(rpt_vars[n].p.memory));
1015 } else if (!strcmp(var->name, "macro")) {
1016 ast_copy_string(rpt_vars[n].p.macro, var->value, sizeof(rpt_vars[n].p.macro));
1017 } else if (!strcmp(var->name, "startup_macro")) {
1018 ast_copy_string(rpt_vars[n].p.startupmacro, var->value, sizeof(rpt_vars[n].p.startupmacro));
1019 } else if (!strcmp(var->name, "iobase")) {
1020 /* do not use atoi() here, we need to be able to have
1021 the input specified in hex or decimal so we use
1023 if (sscanf(var->value, "%i", &rpt_vars[n].p.iobase) != 1)
1024 rpt_vars[n].p.iobase = DEFAULT_IOBASE;
1025 } else if (!strcmp(var->name, "functions")) {
1026 rpt_vars[n].p.simple = 0;
1027 ast_copy_string(rpt_vars[n].p.functions, var->value, sizeof(rpt_vars[n].p.functions));
1028 } else if (!strcmp(var->name, "link_functions")) {
1029 ast_copy_string(rpt_vars[n].p.link_functions, var->value, sizeof(rpt_vars[n].p.link_functions));
1030 } else if (!strcmp(var->name, "phone_functions")) {
1031 ast_copy_string(rpt_vars[n].p.phone_functions, var->value, sizeof(rpt_vars[n].p.phone_functions));
1032 } else if (!strcmp(var->name, "dphone_functions")) {
1033 ast_copy_string(rpt_vars[n].p.dphone_functions, var->value, sizeof(rpt_vars[n].p.dphone_functions));
1034 } else if (!strcmp(var->name, "funcchar")) {
1035 rpt_vars[n].p.funcchar = *var->value;
1036 } else if (!strcmp(var->name, "endchar")) {
1037 rpt_vars[n].p.endchar = *var->value;
1038 } else if (!strcmp(var->name, "nobusyout")) {
1039 rpt_vars[n].p.nobusyout = ast_true(var->value);
1040 } else if (!strcmp(var->name, "nodes")) {
1041 ast_copy_string(rpt_vars[n].p.nodes, var->value, sizeof(rpt_vars[n].p.nodes));
1043 } else if (!strcmp(var->name, "rxnotch")) {
1044 char *tmp = ast_strdupa(val);
1045 AST_NONSTANDARD_APP_ARGS(strs, tmp, ',');
1046 strs.argc &= ~1; /* force an even number, rounded down */
1047 if (strs.argc >= 2) {
1048 for (j = 0; j < strs.argc; j += 2) {
1049 rpt_mknotch(atof(strs.str[j]), atof(strs.str[j + 1]),
1050 &rpt_vars[n].filters[j >> 1].gain,
1051 &rpt_vars[n].filters[j >> 1].const0,
1052 &rpt_vars[n].filters[j >> 1].const1,
1053 &rpt_vars[n].filters[j >> 1].const2);
1054 sprintf(rpt_vars[n].filters[j >> 1].desc, "%s Hz, BW = %s",
1055 strs.str[j], strs.str[j + 1]);
1062 /* If these aren't specified, copy them from the functions property. */
1063 if (ast_strlen_zero(rpt_vars[n].p.link_functions))
1064 ast_copy_string(rpt_vars[n].p.link_functions, rpt_vars[n].p.functions, sizeof(rpt_vars[n].p.link_functions));
1066 rpt_vars[n].longestnode = 0;
1067 for (vp = ast_variable_browse(cfg, rpt_vars[n].p.nodes); vp; vp = vp->next) {
1068 if ((j = strlen(vp->name)) > rpt_vars[n].longestnode)
1069 rpt_vars[n].longestnode = j;
1073 * For this repeater, Determine the length of the longest function
1075 rpt_vars[n].longestfunc = 0;
1076 for (vp = ast_variable_browse(cfg, rpt_vars[n].p.functions); vp; vp = vp->next) {
1077 if ((j = strlen(vp->name)) > rpt_vars[n].longestfunc)
1078 rpt_vars[n].longestfunc = j;
1081 rpt_vars[n].link_longestfunc = 0;
1082 for (vp = ast_variable_browse(cfg, rpt_vars[n].p.link_functions); vp; vp = vp->next) {
1083 if ((j = strlen(vp->name)) > rpt_vars[n].link_longestfunc)
1084 rpt_vars[n].link_longestfunc = j;
1087 rpt_vars[n].phone_longestfunc = 0;
1088 for (vp = ast_variable_browse(cfg, rpt_vars[n].p.phone_functions); vp; vp = vp->next) {
1089 if ((j = strlen(vp->name)) > rpt_vars[n].phone_longestfunc)
1090 rpt_vars[n].phone_longestfunc = j;
1093 rpt_vars[n].dphone_longestfunc = 0;
1094 for (vp = ast_variable_browse(cfg, rpt_vars[n].p.dphone_functions); vp; vp = vp->next) {
1095 if ((j = strlen(vp->name)) > rpt_vars[n].dphone_longestfunc)
1096 rpt_vars[n].dphone_longestfunc = j;
1099 rpt_vars[n].macro_longest = 1;
1100 for (vp = ast_variable_browse(cfg, rpt_vars[n].p.macro); vp; vp = vp->next) {
1101 if ((j = strlen(vp->name)) > rpt_vars[n].macro_longest)
1102 rpt_vars[n].macro_longest = j;
1104 ast_mutex_unlock(&rpt_vars[n].lock);
1108 * Enable or disable debug output at a given level at the console
1110 static int rpt_do_debug(int fd, int argc, char *argv[])
1115 return RESULT_SHOWUSAGE;
1116 newlevel = myatoi(argv[3]);
1117 if ((newlevel < 0) || (newlevel > 7))
1118 return RESULT_SHOWUSAGE;
1120 ast_cli(fd, "app_rpt Debugging enabled, previous level: %d, new level: %d\n", debug, newlevel);
1122 ast_cli(fd, "app_rpt Debugging disabled\n");
1125 return RESULT_SUCCESS;
1129 * Dump rpt struct debugging onto console
1131 static int rpt_do_dump(int fd, int argc, char *argv[])
1136 return RESULT_SHOWUSAGE;
1138 for (i = 0; i < nrpts; i++) {
1139 if (!strcmp(argv[2], rpt_vars[i].name)) {
1140 rpt_vars[i].disgorgetime = time(NULL) + 10; /* Do it 10 seconds later */
1141 ast_cli(fd, "app_rpt struct dump requested for node %s\n", argv[2]);
1142 return RESULT_SUCCESS;
1145 return RESULT_FAILURE;
1149 * Dump statistics onto console
1151 static int rpt_do_stats(int fd, int argc, char *argv[])
1154 int dailytxtime, dailykerchunks;
1155 int totalkerchunks, dailykeyups, totalkeyups, timeouts;
1156 int totalexecdcommands, dailyexecdcommands, hours, minutes, seconds;
1157 long long totaltxtime;
1159 char *listoflinks[MAX_STAT_LINKS];
1160 char *lastnodewhichkeyedusup, *lastdtmfcommand;
1161 char *tot_state, *ider_state, *patch_state;
1162 char *reverse_patch_state, *enable_state, *input_signal, *called_number;
1165 static char *not_applicable = "N/A";
1168 return RESULT_SHOWUSAGE;
1170 for (i = 0 ; i <= MAX_STAT_LINKS; i++)
1171 listoflinks[i] = NULL;
1173 tot_state = ider_state =
1174 patch_state = reverse_patch_state =
1175 input_signal = called_number =
1176 lastdtmfcommand = not_applicable;
1178 for (i = 0; i < nrpts; i++) {
1179 if (!strcmp(argv[2], rpt_vars[i].name)) {
1180 /* Make a copy of all stat variables while locked */
1181 myrpt = &rpt_vars[i];
1182 rpt_mutex_lock(&myrpt->lock); /* LOCK */
1184 dailytxtime = myrpt->dailytxtime;
1185 totaltxtime = myrpt->totaltxtime;
1186 dailykeyups = myrpt->dailykeyups;
1187 totalkeyups = myrpt->totalkeyups;
1188 dailykerchunks = myrpt->dailykerchunks;
1189 totalkerchunks = myrpt->totalkerchunks;
1190 dailyexecdcommands = myrpt->dailyexecdcommands;
1191 totalexecdcommands = myrpt->totalexecdcommands;
1192 timeouts = myrpt->timeouts;
1194 /* Traverse the list of connected nodes */
1195 reverse_patch_state = "DOWN";
1197 l = myrpt->links.next;
1198 while (l != &myrpt->links) {
1199 if (l->name[0] == '0') { /* Skip '0' nodes */
1200 reverse_patch_state = "UP";
1204 listoflinks[j] = ast_strdupa(l->name);
1210 lastnodewhichkeyedusup = ast_strdupa(myrpt->lastnodewhichkeyedusup);
1211 if ((!lastnodewhichkeyedusup) || (ast_strlen_zero(lastnodewhichkeyedusup)))
1212 lastnodewhichkeyedusup = not_applicable;
1215 input_signal = "YES";
1217 input_signal = "NO";
1220 enable_state = "YES";
1222 enable_state = "NO";
1224 if (!myrpt->totimer)
1225 tot_state = "TIMED OUT!";
1226 else if (myrpt->totimer != myrpt->p.totime)
1227 tot_state = "ARMED";
1229 tot_state = "RESET";
1232 ider_state = "QUEUED IN TAIL";
1233 else if (myrpt->mustid)
1234 ider_state = "QUEUED FOR CLEANUP";
1236 ider_state = "CLEAN";
1238 switch (myrpt->callmode) {
1240 patch_state = "DIALING";
1243 patch_state = "CONNECTING";
1249 patch_state = "CALL FAILED";
1252 patch_state = "DOWN";
1255 if (!ast_strlen_zero(myrpt->exten))
1256 called_number = ast_strdupa(myrpt->exten);
1258 if (!ast_strlen_zero(myrpt->lastdtmfcommand))
1259 lastdtmfcommand = ast_strdupa(myrpt->lastdtmfcommand);
1261 rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
1263 ast_cli(fd, "************************ NODE %s STATISTICS *************************\n\n", myrpt->name);
1264 ast_cli(fd, "Signal on input..................................: %s\n", input_signal);
1265 ast_cli(fd, "Transmitter enabled..............................: %s\n", enable_state);
1266 ast_cli(fd, "Time out timer state.............................: %s\n", tot_state);
1267 ast_cli(fd, "Time outs since system initialization............: %d\n", timeouts);
1268 ast_cli(fd, "Identifier state.................................: %s\n", ider_state);
1269 ast_cli(fd, "Kerchunks today..................................: %d\n", dailykerchunks);
1270 ast_cli(fd, "Kerchunks since system initialization............: %d\n", totalkerchunks);
1271 ast_cli(fd, "Keyups today.....................................: %d\n", dailykeyups);
1272 ast_cli(fd, "Keyups since system initialization...............: %d\n", totalkeyups);
1273 ast_cli(fd, "DTMF commands today..............................: %d\n", dailyexecdcommands);
1274 ast_cli(fd, "DTMF commands since system initialization........: %d\n", totalexecdcommands);
1275 ast_cli(fd, "Last DTMF command executed.......................: %s\n", lastdtmfcommand);
1277 hours = dailytxtime / 3600000;
1278 dailytxtime %= 3600000;
1279 minutes = dailytxtime / 60000;
1280 dailytxtime %= 60000;
1281 seconds = dailytxtime / 1000;
1282 dailytxtime %= 1000;
1284 ast_cli(fd, "TX time today ...................................: %02d:%02d:%02d.%d\n",
1285 hours, minutes, seconds, dailytxtime);
1287 hours = (int) totaltxtime / 3600000;
1288 totaltxtime %= 3600000;
1289 minutes = (int) totaltxtime / 60000;
1290 totaltxtime %= 60000;
1291 seconds = (int) totaltxtime / 1000;
1292 totaltxtime %= 1000;
1294 ast_cli(fd, "TX time since system initialization..............: %02d:%02d:%02d.%d\n",
1295 hours, minutes, seconds, (int) totaltxtime);
1296 ast_cli(fd, "Nodes currently connected to us..................: ");
1298 if (!listoflinks[j]) {
1300 ast_cli(fd, "<NONE>");
1304 ast_cli(fd, "%s", listoflinks[j]);
1309 if (listoflinks[j + 1])
1315 ast_cli(fd, "Last node which transmitted to us................: %s\n", lastnodewhichkeyedusup);
1316 ast_cli(fd, "Autopatch state..................................: %s\n", patch_state);
1317 ast_cli(fd, "Autopatch called number..........................: %s\n", called_number);
1318 ast_cli(fd, "Reverse patch/IAXRPT connected...................: %s\n\n", reverse_patch_state);
1320 return RESULT_SUCCESS;
1323 return RESULT_FAILURE;
1327 * Link stats function
1329 static int rpt_do_lstats(int fd, int argc, char *argv[])
1334 struct rpt_lstat *s, *t;
1335 struct rpt_lstat s_head;
1337 return RESULT_SHOWUSAGE;
1340 s_head.next = &s_head;
1341 s_head.prev = &s_head;
1343 for (i = 0; i < nrpts; i++) {
1344 if (!strcmp(argv[2], rpt_vars[i].name)) {
1345 /* Make a copy of all stat variables while locked */
1346 myrpt = &rpt_vars[i];
1347 rpt_mutex_lock(&myrpt->lock); /* LOCK */
1348 /* Traverse the list of connected nodes */
1350 l = myrpt->links.next;
1351 while (l != &myrpt->links) {
1352 if (l->name[0] == '0') { /* Skip '0' nodes */
1356 if ((s = ast_calloc(1, sizeof(*s))) == NULL) {
1357 ast_log(LOG_ERROR, "Malloc failed in rpt_do_lstats\n");
1358 rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
1359 return RESULT_FAILURE;
1361 ast_copy_string(s->name, l->name, MAXREMSTR);
1362 pbx_substitute_variables_helper(l->chan, "${IAXPEER(CURRENTCHANNEL)}", s->peer, MAXPEERSTR - 1);
1364 s->outbound = l->outbound;
1365 s->reconnects = l->reconnects;
1366 s->connecttime = l->connecttime;
1367 insque((struct qelem *) s, (struct qelem *) s_head.next);
1370 rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
1371 ast_cli(fd, "NODE PEER RECONNECTS DIRECTION CONNECT TIME\n");
1372 ast_cli(fd, "---- ---- ---------- --------- ------------\n");
1374 for (s = s_head.next; s != &s_head; s = s->next) {
1375 int hours, minutes, seconds;
1376 long long connecttime = s->connecttime;
1378 hours = (int) connecttime/3600000;
1379 connecttime %= 3600000;
1380 minutes = (int) connecttime/60000;
1381 connecttime %= 60000;
1382 seconds = (int) connecttime/1000;
1383 connecttime %= 1000;
1384 snprintf(conntime, sizeof(conntime), "%02d:%02d:%02d.%d",
1385 hours, minutes, seconds, (int) connecttime);
1386 ast_cli(fd, "%-10s%-20s%-12d%-11s%-30s\n",
1387 s->name, s->peer, s->reconnects, (s->outbound)? "OUT":"IN", conntime);
1389 /* destroy our local link queue */
1391 while (s != &s_head) {
1394 remque((struct qelem *)t);
1397 return RESULT_SUCCESS;
1400 return RESULT_FAILURE;
1406 static int rpt_do_reload(int fd, int argc, char *argv[])
1411 return RESULT_SHOWUSAGE;
1413 for (n = 0; n < nrpts; n++)
1414 rpt_vars[n].reload = 1;
1416 return RESULT_FAILURE;
1422 static int rpt_do_restart(int fd, int argc, char *argv[])
1427 return RESULT_SHOWUSAGE;
1428 for (i = 0; i < nrpts; i++) {
1429 if (rpt_vars[i].rxchannel)
1430 ast_softhangup(rpt_vars[i].rxchannel, AST_SOFTHANGUP_DEV);
1432 return RESULT_FAILURE;
1435 static int play_tone_pair(struct ast_channel *chan, int f1, int f2, int duration, int amplitude)
1439 if ((res = ast_tonepair_start(chan, f1, f2, duration, amplitude)))
1442 while (chan->generatordata) {
1443 if (ast_safe_sleep(chan, 1))
1450 static int play_tone(struct ast_channel *chan, int freq, int duration, int amplitude)
1452 return play_tone_pair(chan, freq, 0, duration, amplitude);
1455 static int play_silence(struct ast_channel *chan, int duration)
1457 return play_tone_pair(chan, 0, 0, duration, 0);
1461 static int send_morse(struct ast_channel *chan, const char *string, int speed, int freq, int amplitude)
1464 static struct morse_bits mbits[] = {
1528 int intralettertime;
1529 int interlettertime;
1539 /* Approximate the dot time from the speed arg. */
1541 dottime = 900 / speed;
1543 /* Establish timing relationships */
1545 dashtime = 3 * dottime;
1546 intralettertime = dottime;
1547 interlettertime = dottime * 4 ;
1548 interwordtime = dottime * 7;
1550 for (; (*string) && (!res); string++) {
1554 /* Convert lower case to upper case */
1556 if ((c >= 'a') && (c <= 'z'))
1559 /* Can't deal with any char code greater than Z, skip it */
1564 /* If space char, wait the inter word time */
1568 res = play_silence(chan, interwordtime);
1572 /* Subtract out control char offset to match our table */
1576 /* Get the character data */
1579 ddcomb = mbits[c].ddcomb;
1581 /* Send the character */
1583 for (; len ; len--) {
1585 res = play_tone(chan, freq, (ddcomb & 1) ? dashtime : dottime, amplitude);
1587 res = play_silence(chan, intralettertime);
1591 /* Wait the interletter time */
1594 res = play_silence(chan, interlettertime - intralettertime);
1597 /* Wait for all the frames to be sent */
1600 res = ast_waitstream(chan, "");
1601 ast_stopstream(chan);
1604 * Wait for the zaptel driver to physically write the tone blocks to the hardware
1607 for (i = 0; i < 20 ; i++) {
1608 flags = ZT_IOMUX_WRITEEMPTY | ZT_IOMUX_NOWAIT;
1609 res = ioctl(chan->fds[0], ZT_IOMUX, &flags);
1610 if (flags & ZT_IOMUX_WRITEEMPTY)
1612 if ( ast_safe_sleep(chan, 50)) {
1622 static int send_tone_telemetry(struct ast_channel *chan, const char *tonestring)
1635 stringp = ast_strdupa(tonestring);
1637 for (;tonestring;) {
1638 tonesubset = strsep(&stringp, ")");
1641 if (sscanf(tonesubset, "(%d,%d,%d,%d", &f1, &f2, &duration, &litude) != 4)
1643 res = play_tone_pair(chan, f1, f2, duration, amplitude);
1648 res = play_tone_pair(chan, 0, 0, 100, 0); /* This is needed to ensure the last tone segment is timed correctly */
1651 res = ast_waitstream(chan, "");
1652 ast_stopstream(chan);
1655 * Wait for the zaptel driver to physically write the tone blocks to the hardware
1658 for (i = 0; i < 20 ; i++) {
1659 flags = ZT_IOMUX_WRITEEMPTY | ZT_IOMUX_NOWAIT;
1660 res = ioctl(chan->fds[0], ZT_IOMUX, &flags);
1661 if (flags & ZT_IOMUX_WRITEEMPTY)
1663 if (ast_safe_sleep(chan, 50)) {
1673 static int sayfile(struct ast_channel *mychannel, const char *fname)
1677 res = ast_streamfile(mychannel, fname, mychannel->language);
1679 res = ast_waitstream(mychannel, "");
1681 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
1682 ast_stopstream(mychannel);
1686 static int saycharstr(struct ast_channel *mychannel, char *str)
1690 res = ast_say_character_str(mychannel, str, NULL, mychannel->language);
1692 res = ast_waitstream(mychannel, "");
1694 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
1695 ast_stopstream(mychannel);
1699 static int saynum(struct ast_channel *mychannel, int num)
1702 res = ast_say_number(mychannel, num, NULL, mychannel->language, NULL);
1704 res = ast_waitstream(mychannel, "");
1706 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
1707 ast_stopstream(mychannel);
1711 static int saydigits(struct ast_channel *mychannel, int num)
1714 res = ast_say_digits(mychannel, num, NULL, mychannel->language);
1716 res = ast_waitstream(mychannel, "");
1718 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
1719 ast_stopstream(mychannel);
1724 static int telem_any(struct rpt *myrpt, struct ast_channel *chan, const char *entry)
1729 static int morsespeed;
1730 static int morsefreq;
1731 static int morseampl;
1732 static int morseidfreq = 0;
1733 static int morseidampl;
1734 static char mcat[] = MORSE;
1738 if (!morseidfreq) { /* Get the morse parameters if not already loaded */
1739 morsespeed = retrieve_astcfgint(myrpt, mcat, "speed", 5, 20, 20);
1740 morsefreq = retrieve_astcfgint(myrpt, mcat, "frequency", 300, 3000, 800);
1741 morseampl = retrieve_astcfgint(myrpt, mcat, "amplitude", 200, 8192, 4096);
1742 morseidampl = retrieve_astcfgint(myrpt, mcat, "idamplitude", 200, 8192, 2048);
1743 morseidfreq = retrieve_astcfgint(myrpt, mcat, "idfrequency", 300, 3000, 330);
1746 /* Is it a file, or a tone sequence? */
1748 if (entry[0] == '|') {
1750 if ((c >= 'a') && (c <= 'z'))
1754 case 'I': /* Morse ID */
1755 res = send_morse(chan, entry + 2, morsespeed, morseidfreq, morseidampl);
1757 case 'M': /* Morse Message */
1758 res = send_morse(chan, entry + 2, morsespeed, morsefreq, morseampl);
1760 case 'T': /* Tone sequence */
1761 res = send_tone_telemetry(chan, entry + 2);
1767 res = sayfile(chan, entry); /* File */
1772 * This function looks up a telemetry name in the config file, and does a telemetry response as configured.
1774 * 4 types of telemtry are handled: Morse ID, Morse Message, Tone Sequence, and a File containing a recording.
1777 static int telem_lookup(struct rpt *myrpt, struct ast_channel *chan, const char *node, const char *name)
1781 const char *entry = NULL;
1782 const char *telemetry;
1784 /* Retrieve the section name for telemetry from the node section */
1785 if ((telemetry = ast_variable_retrieve(myrpt->cfg, node, TELEMETRY)))
1786 entry = ast_variable_retrieve(myrpt->cfg, telemetry, name);
1788 /* Try to look up the telemetry name */
1791 /* Telemetry name wasn't found in the config file, use the default */
1792 for (i = 0; i < sizeof(tele_defs) / sizeof(struct telem_defaults); i++) {
1793 if (!strcasecmp(tele_defs[i].name, name))
1794 entry = tele_defs[i].value;
1798 if (!ast_strlen_zero(entry))
1799 telem_any(myrpt, chan, entry);
1807 * Retrieve a wait interval
1810 static int get_wait_interval(struct rpt *myrpt, int type)
1812 int interval = 1000;
1813 const char *wait_times = ast_variable_retrieve(myrpt->cfg, myrpt->name, "wait_times");
1818 interval = retrieve_astcfgint(myrpt, wait_times, "telemwait", 500, 5000, 1000);
1822 interval = retrieve_astcfgint(myrpt, wait_times, "idwait", 250, 5000, 500);
1828 interval = retrieve_astcfgint(myrpt, wait_times, "unkeywait", 500, 5000, 1000);
1832 interval = retrieve_astcfgint(myrpt, wait_times, "calltermwait", 500, 5000, 1500);
1842 * Wait a configurable interval of time
1846 static void wait_interval(struct rpt *myrpt, int type, struct ast_channel *chan)
1849 interval = get_wait_interval(myrpt, type);
1851 ast_log(LOG_NOTICE, " Delay interval = %d\n", interval);
1853 ast_safe_sleep(chan, interval);
1855 ast_log(LOG_NOTICE, "Delay complete\n");
1860 static void *rpt_tele_thread(void *this)
1862 ZT_CONFINFO ci; /* conference info */
1863 int res = 0, haslink, hastx, hasremote, imdone = 0, unkeys_queued, x;
1864 struct rpt_tele *mytele = (struct rpt_tele *)this;
1865 struct rpt_tele *tlist;
1867 struct rpt_link *l, *m, linkbase;
1868 struct ast_channel *mychannel;
1872 #ifdef APP_RPT_LOCK_DEBUG
1873 struct lockthread *t;
1876 /* get a pointer to myrpt */
1877 myrpt = mytele->rpt;
1879 /* Snag copies of a few key myrpt variables */
1880 rpt_mutex_lock(&myrpt->lock);
1881 rpt_mutex_unlock(&myrpt->lock);
1883 /* allocate a pseudo-channel thru asterisk */
1884 mychannel = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
1886 ast_log(LOG_WARNING, "rpt: unable to obtain pseudo channel\n");
1887 rpt_mutex_lock(&myrpt->lock);
1888 remque((struct qelem *)mytele);
1889 ast_log(LOG_NOTICE, "Telemetry thread aborted at line %d, mode: %d\n", __LINE__, mytele->mode); /*@@@@@@@@@@@*/
1890 rpt_mutex_unlock(&myrpt->lock);
1894 rpt_mutex_lock(&myrpt->lock);
1895 mytele->chan = mychannel; /* Save a copy of the channel so we can access it externally if need be */
1896 rpt_mutex_unlock(&myrpt->lock);
1898 /* make a conference for the tx */
1900 /* If there's an ID queued, or tail message queued, */
1901 /* only connect the ID audio to the local tx conference so */
1902 /* linked systems can't hear it */
1903 ci.confno = (((mytele->mode == ID) || (mytele->mode == IDTALKOVER) || (mytele->mode == UNKEY) ||
1904 (mytele->mode == TAILMSG)) ?
1905 myrpt->txconf : myrpt->conf);
1906 ci.confmode = ZT_CONF_CONFANN;
1907 /* first put the channel on the conference in announce mode */
1908 if (ioctl(mychannel->fds[0], ZT_SETCONF, &ci) == -1) {
1909 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
1910 rpt_mutex_lock(&myrpt->lock);
1911 remque((struct qelem *)mytele);
1912 rpt_mutex_unlock(&myrpt->lock);
1913 ast_log(LOG_NOTICE, "Telemetry thread aborted at line %d, mode: %d\n", __LINE__, mytele->mode); /*@@@@@@@@@@@*/
1915 ast_hangup(mychannel);
1918 ast_stopstream(mychannel);
1919 switch (mytele->mode) {
1923 wait_interval(myrpt, (mytele->mode == ID) ? DLY_ID : DLY_TELEM, mychannel);
1924 res = telem_any(myrpt, mychannel, myrpt->p.ident);
1929 res = ast_streamfile(mychannel, myrpt->p.tailmsg.msgs[myrpt->tailmessagen], mychannel->language);
1933 p = ast_variable_retrieve(myrpt->cfg, myrpt->name, "idtalkover");
1935 res = telem_any(myrpt, mychannel, p);
1939 /* wait a little bit longer */
1940 wait_interval(myrpt, DLY_TELEM, mychannel);
1941 res = telem_lookup(myrpt, mychannel, myrpt->name, "patchup");
1942 if (res < 0) { /* Then default message */
1943 res = ast_streamfile(mychannel, "rpt/callproceeding", mychannel->language);
1947 /* wait a little bit longer */
1948 wait_interval(myrpt, DLY_CALLTERM, mychannel);
1949 res = telem_lookup(myrpt, mychannel, myrpt->name, "patchdown");
1950 if (res < 0) { /* Then default message */
1951 res = ast_streamfile(mychannel, "rpt/callterminated", mychannel->language);
1955 /* wait a little bit */
1956 wait_interval(myrpt, DLY_TELEM, mychannel);
1957 res = telem_lookup(myrpt, mychannel, myrpt->name, "functcomplete");
1959 case MACRO_NOTFOUND:
1960 /* wait a little bit */
1961 wait_interval(myrpt, DLY_TELEM, mychannel);
1962 res = ast_streamfile(mychannel, "rpt/macro_notfound", mychannel->language);
1965 /* wait a little bit */
1966 wait_interval(myrpt, DLY_TELEM, mychannel);
1967 res = ast_streamfile(mychannel, "rpt/macro_busy", mychannel->language);
1970 if (myrpt->patchnoct && myrpt->callmode) { /* If no CT during patch configured, then don't send one */
1976 * Reset the Unkey to CT timer
1979 x = get_wait_interval(myrpt, DLY_UNKEY);
1980 rpt_mutex_lock(&myrpt->lock);
1981 myrpt->unkeytocttimer = x; /* Must be protected as it is changed below */
1982 rpt_mutex_unlock(&myrpt->lock);
1985 * If there's one already queued, don't do another
1988 tlist = myrpt->tele.next;
1990 if (tlist != &myrpt->tele) {
1991 rpt_mutex_lock(&myrpt->lock);
1992 while (tlist != &myrpt->tele) {
1993 if (tlist->mode == UNKEY)
1995 tlist = tlist->next;
1997 rpt_mutex_unlock(&myrpt->lock);
1999 if (unkeys_queued > 1) {
2004 /* Wait for the telemetry timer to expire */
2005 /* Periodically check the timer since it can be re-initialized above */
2006 while (myrpt->unkeytocttimer) {
2008 if (myrpt->unkeytocttimer > 100)
2011 ctint = myrpt->unkeytocttimer;
2012 ast_safe_sleep(mychannel, ctint);
2013 rpt_mutex_lock(&myrpt->lock);
2014 if (myrpt->unkeytocttimer < ctint)
2015 myrpt->unkeytocttimer = 0;
2017 myrpt->unkeytocttimer -= ctint;
2018 rpt_mutex_unlock(&myrpt->lock);
2022 * Now, the carrier on the rptr rx should be gone.
2023 * If it re-appeared, then forget about sending the CT
2030 rpt_mutex_lock(&myrpt->lock); /* Update the kerchunk counters */
2031 myrpt->dailykerchunks++;
2032 myrpt->totalkerchunks++;
2033 rpt_mutex_unlock(&myrpt->lock);
2038 l = myrpt->links.next;
2039 if (l != &myrpt->links) {
2040 rpt_mutex_lock(&myrpt->lock);
2041 while (l != &myrpt->links) {
2042 if (l->name[0] == '0') {
2054 rpt_mutex_unlock(&myrpt->lock);
2057 res = telem_lookup(myrpt, mychannel, myrpt->name, (!hastx) ? "remotemon" : "remotetx");
2059 ast_log(LOG_WARNING, "telem_lookup:remotexx failed on %s\n", mychannel->name);
2061 /* if in remote cmd mode, indicate it */
2062 if (myrpt->cmdnode[0]) {
2063 ast_safe_sleep(mychannel, 200);
2064 res = telem_lookup(myrpt, mychannel, myrpt->name, "cmdmode");
2066 ast_log(LOG_WARNING, "telem_lookup:cmdmode failed on %s\n", mychannel->name);
2067 ast_stopstream(mychannel);
2069 } else if ((ct = ast_variable_retrieve(myrpt->cfg, myrpt->name, "unlinkedct"))) { /* Unlinked Courtesy Tone */
2070 res = telem_lookup(myrpt, mychannel, myrpt->name, ct);
2072 ast_log(LOG_WARNING, "telem_lookup:ctx failed on %s\n", mychannel->name);
2074 if (hasremote && (!myrpt->cmdnode[0])) {
2075 /* set for all to hear */
2077 ci.confno = myrpt->conf;
2078 ci.confmode = ZT_CONF_CONFANN;
2079 /* first put the channel on the conference in announce mode */
2080 if (ioctl(mychannel->fds[0], ZT_SETCONF, &ci) == -1) {
2081 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
2082 rpt_mutex_lock(&myrpt->lock);
2083 remque((struct qelem *)mytele);
2084 rpt_mutex_unlock(&myrpt->lock);
2085 ast_log(LOG_NOTICE, "Telemetry thread aborted at line %d, mode: %d\n", __LINE__, mytele->mode); /*@@@@@@@@@@@*/
2087 ast_hangup(mychannel);
2090 if ((ct = ast_variable_retrieve(myrpt->cfg, myrpt->name, "remotect"))) { /* Unlinked Courtesy Tone */
2091 ast_safe_sleep(mychannel, 200);
2092 res = telem_lookup(myrpt, mychannel, myrpt->name, ct);
2094 ast_log(LOG_WARNING, "telem_lookup:ctx failed on %s\n", mychannel->name);
2097 #ifdef _MDC_DECODE_H_
2098 if (myrpt->lastunit) {
2101 ast_safe_sleep(mychannel, 200);
2102 /* set for all to hear */
2104 ci.confno = myrpt->txconf;
2105 ci.confmode = ZT_CONF_CONFANN;
2106 /* first put the channel on the conference in announce mode */
2107 if (ioctl(mychannel->fds[0], ZT_SETCONF, &ci) == -1) {
2108 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
2109 rpt_mutex_lock(&myrpt->lock);
2110 remque((struct qelem *)mytele);
2111 rpt_mutex_unlock(&myrpt->lock);
2112 ast_log(LOG_NOTICE, "Telemetry thread aborted at line %d, mode: %d\n", __LINE__, mytele->mode); /*@@@@@@@@@@@*/
2114 ast_hangup(mychannel);
2117 snprintf(mystr, sizeof(mystr), "%04x", myrpt->lastunit);
2118 myrpt->lastunit = 0;
2119 ast_say_character_str(mychannel, mystr, NULL, mychannel->language);
2126 /* wait a little bit */
2127 wait_interval(myrpt, DLY_TELEM, mychannel);
2128 res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
2130 res = ast_waitstream(mychannel, "");
2132 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
2133 ast_stopstream(mychannel);
2134 ast_say_character_str(mychannel, mytele->mylink.name, NULL, mychannel->language);
2135 res = ast_streamfile(mychannel, ((mytele->mylink.connected) ?
2136 "rpt/remote_disc" : "rpt/remote_busy"), mychannel->language);
2139 /* wait a little bit */
2140 wait_interval(myrpt, DLY_TELEM, mychannel);
2141 res = ast_streamfile(mychannel, "rpt/remote_already", mychannel->language);
2144 /* wait a little bit */
2145 wait_interval(myrpt, DLY_TELEM, mychannel);
2146 res = ast_streamfile(mychannel, "rpt/remote_notfound", mychannel->language);
2149 /* wait a little bit */
2150 wait_interval(myrpt, DLY_TELEM, mychannel);
2151 res = ast_streamfile(mychannel, "rpt/remote_go", mychannel->language);
2154 /* wait a little bit */
2155 wait_interval(myrpt, DLY_TELEM, mychannel);
2156 res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
2158 res = ast_waitstream(mychannel, "");
2160 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
2161 ast_stopstream(mychannel);
2162 ast_say_character_str(mychannel, mytele->mylink.name, NULL, mychannel->language);
2163 res = ast_streamfile(mychannel, "rpt/connected", mychannel->language);
2166 res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
2168 res = ast_waitstream(mychannel, "");
2170 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
2171 ast_stopstream(mychannel);
2172 ast_say_character_str(mychannel, mytele->mylink.name, NULL, mychannel->language);
2173 res = ast_streamfile(mychannel, "rpt/connection_failed", mychannel->language);
2176 /* wait a little bit */
2177 wait_interval(myrpt, DLY_TELEM, mychannel);
2179 linkbase.next = &linkbase;
2180 linkbase.prev = &linkbase;
2181 rpt_mutex_lock(&myrpt->lock);
2182 /* make our own list of links */
2183 l = myrpt->links.next;
2184 while (l != &myrpt->links) {
2185 if (l->name[0] == '0') {
2189 m = ast_malloc(sizeof(*m));
2191 ast_log(LOG_WARNING, "Cannot alloc memory on %s\n", mychannel->name);
2192 remque((struct qelem *)mytele);
2193 rpt_mutex_unlock(&myrpt->lock);
2194 ast_log(LOG_NOTICE, "Telemetry thread aborted at line %d, mode: %d\n", __LINE__, mytele->mode); /*@@@@@@@@@@@*/
2196 ast_hangup(mychannel);
2199 memcpy(m, l, sizeof(struct rpt_link));
2200 m->next = m->prev = NULL;
2201 insque((struct qelem *)m, (struct qelem *)linkbase.next);
2204 rpt_mutex_unlock(&myrpt->lock);
2205 res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
2207 res = ast_waitstream(mychannel, "");
2209 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
2210 ast_stopstream(mychannel);
2211 ast_say_character_str(mychannel, myrpt->name, NULL, mychannel->language);
2213 res = ast_waitstream(mychannel, "");
2215 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
2216 ast_stopstream(mychannel);
2217 if (myrpt->callmode) {
2219 res = ast_streamfile(mychannel, "rpt/autopatch_on", mychannel->language);
2221 res = ast_waitstream(mychannel, "");
2223 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
2224 ast_stopstream(mychannel);
2227 while (l != &linkbase) {
2229 res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
2231 res = ast_waitstream(mychannel, "");
2233 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
2234 ast_stopstream(mychannel);
2235 ast_say_character_str(mychannel, l->name, NULL, mychannel->language);
2237 res = ast_waitstream(mychannel, "");
2239 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
2240 ast_stopstream(mychannel);
2241 res = ast_streamfile(mychannel, ((l->mode) ?
2242 "rpt/tranceive" : "rpt/monitor"), mychannel->language);
2244 res = ast_waitstream(mychannel, "");
2246 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
2247 ast_stopstream(mychannel);
2251 res = ast_streamfile(mychannel, "rpt/repeat_only", mychannel->language);
2253 res = ast_waitstream(mychannel, "");
2255 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
2256 ast_stopstream(mychannel);
2258 /* destroy our local link queue */
2260 while (l != &linkbase) {
2263 remque((struct qelem *)m);
2269 case LASTNODEKEY: /* Identify last node which keyed us up */
2270 rpt_mutex_lock(&myrpt->lock);
2271 if (myrpt->lastnodewhichkeyedusup)
2272 p = ast_strdupa(myrpt->lastnodewhichkeyedusup); /* Make a local copy of the node name */
2275 rpt_mutex_unlock(&myrpt->lock);
2277 imdone = 1; /* no node previously keyed us up, or the node which did has been disconnected */
2280 wait_interval(myrpt, DLY_TELEM, mychannel);
2281 res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
2283 res = ast_waitstream(mychannel, "");
2285 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
2286 ast_stopstream(mychannel);
2287 ast_say_character_str(mychannel, p, NULL, mychannel->language);
2289 res = ast_waitstream(mychannel, "");
2291 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
2292 ast_stopstream(mychannel);
2297 res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
2299 res = ast_waitstream(mychannel, "");
2301 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
2302 ast_stopstream(mychannel);
2303 ast_say_character_str(mychannel, myrpt->name, NULL, mychannel->language);
2304 res = ast_streamfile(mychannel, "rpt/timeout", mychannel->language);
2308 wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
2310 localtime_r(&t, &localtm);
2311 /* Say the phase of the day is before the time */
2312 if ((localtm.tm_hour >= 0) && (localtm.tm_hour < 12))
2313 p = "rpt/goodmorning";
2314 else if ((localtm.tm_hour >= 12) && (localtm.tm_hour < 18))
2315 p = "rpt/goodafternoon";
2317 p = "rpt/goodevening";
2318 if (sayfile(mychannel, p) == -1) {
2322 /* Say the time is ... */
2323 if (sayfile(mychannel, "rpt/thetimeis") == -1) {
2328 res = ast_say_time(mychannel, t, "", mychannel->language);
2330 res = ast_waitstream(mychannel, "");
2331 ast_stopstream(mychannel);
2335 wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
2337 if (sayfile(mychannel, "rpt/version") == -1) {
2341 if (!res) /* Say "X" */
2342 ast_say_number(mychannel, vmajor, "", mychannel->language, (char *) NULL);
2344 res = ast_waitstream(mychannel, "");
2345 ast_stopstream(mychannel);
2346 if (saycharstr(mychannel, ".") == -1) {
2350 if (!res) /* Say "Y" */
2351 ast_say_number(mychannel, vminor, "", mychannel->language, (char *) NULL);
2353 res = ast_waitstream(mychannel, "");
2354 ast_stopstream(mychannel);
2356 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
2360 wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
2362 saycharstr(mychannel, mytele->param);
2366 wait_interval(myrpt, DLY_TELEM, mychannel); /* Wait a little bit */
2367 if (mytele->param) {
2368 /* Parts of this section taken from app_parkandannounce */
2369 char *tpl_working, *tpl_current;
2370 char *tmp[100], *myparm;
2371 int looptemp=0, i = 0, dres = 0;
2373 tpl_working = ast_strdupa(mytele->param);
2374 myparm = strsep(&tpl_working, ",");
2375 tpl_current = strsep(&tpl_working, ":");
2377 while (tpl_current && looptemp < sizeof(tmp)) {
2378 tmp[looptemp] = tpl_current;
2380 tpl_current = strsep(&tpl_working, ":");
2383 for (i = 0; i < looptemp; i++) {
2384 if (!strcmp(tmp[i], "PARKED")) {
2385 ast_say_digits(mychannel, atoi(myparm), "", mychannel->language);
2386 } else if (!strcmp(tmp[i], "NODE")) {
2387 ast_say_digits(mychannel, atoi(myrpt->name), "", mychannel->language);
2389 dres = ast_streamfile(mychannel, tmp[i], mychannel->language);
2391 dres = ast_waitstream(mychannel, "");
2393 ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", tmp[i], mychannel->name);
2404 if ((res = ast_tonepair_start(mychannel, 1004.0, 0, 99999999, 7200.0)))
2406 while (mychannel->generatordata && (!myrpt->stopgen)) {
2407 if (ast_safe_sleep(mychannel, 1)) break;
2418 res = ast_waitstream(mychannel, "");
2420 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
2424 ast_stopstream(mychannel);
2425 rpt_mutex_lock(&myrpt->lock);
2426 if (mytele->mode == TAILMSG) {
2428 myrpt->tailmessagen++;
2429 if (myrpt->tailmessagen >= myrpt->p.tailmsg.argc)
2430 myrpt->tailmessagen = 0;
2432 myrpt->tmsgtimer = myrpt->p.tailsquashedtime;
2435 remque((struct qelem *)mytele);
2436 rpt_mutex_unlock(&myrpt->lock);
2438 ast_hangup(mychannel);
2439 #ifdef APP_RPT_LOCK_DEBUG
2441 ast_mutex_lock(&locklock);
2442 t = get_lockthread(pthread_self());
2444 memset(t, 0, sizeof(struct lockthread));
2445 ast_mutex_unlock(&locklock);
2450 static void rpt_telemetry(struct rpt *myrpt, int mode, void *data)
2452 struct rpt_tele *tele;
2453 struct rpt_link *mylink = (struct rpt_link *) data;
2456 tele = ast_calloc(1, sizeof(*tele));
2458 ast_log(LOG_WARNING, "Unable to allocate memory\n");
2463 rpt_mutex_lock(&myrpt->lock);
2464 if ((mode == CONNFAIL) || (mode == REMDISC) || (mode == CONNECTED)) {
2466 memcpy(&tele->mylink, mylink, sizeof(struct rpt_link));
2468 } else if ((mode == ARB_ALPHA) || (mode == REV_PATCH)) {
2469 ast_copy_string(tele->param, (char *) data, sizeof(tele->param));
2471 insque((struct qelem *)tele, (struct qelem *)myrpt->tele.next);
2472 rpt_mutex_unlock(&myrpt->lock);
2473 res = ast_pthread_create_detached(&tele->threadid, NULL, rpt_tele_thread, (void *) tele);
2475 rpt_mutex_lock(&myrpt->lock);
2476 remque((struct qlem *) tele); /* We don't like stuck transmitters, remove it from the queue */
2477 rpt_mutex_unlock(&myrpt->lock);
2478 ast_log(LOG_WARNING, "Could not create telemetry thread: %s\n", strerror(res));
2483 static void *rpt_call(void *this)
2485 ZT_CONFINFO ci; /* conference info */
2486 struct rpt *myrpt = (struct rpt *)this;
2488 struct ast_frame wf;
2489 int stopped, congstarted, dialtimer, lastcidx, aborted;
2490 struct ast_channel *mychannel, *genchannel;
2493 /* allocate a pseudo-channel thru asterisk */
2494 mychannel = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
2496 ast_log(LOG_ERROR, "rpt: unable to obtain pseudo channel\n");
2500 ci.confno = myrpt->conf; /* use the pseudo conference */
2501 ci.confmode = ZT_CONF_REALANDPSEUDO | ZT_CONF_TALKER | ZT_CONF_LISTENER
2502 | ZT_CONF_PSEUDO_TALKER | ZT_CONF_PSEUDO_LISTENER;
2503 /* first put the channel on the conference */
2504 if (ioctl(mychannel->fds[0], ZT_SETCONF, &ci) == -1) {
2505 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
2506 ast_hangup(mychannel);
2507 myrpt->callmode = 0;
2510 /* allocate a pseudo-channel thru asterisk */
2511 genchannel = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
2513 ast_log(LOG_ERROR, "rpt: unable to obtain pseudo channel\n");
2514 ast_hangup(mychannel);
2518 ci.confno = myrpt->conf;
2519 ci.confmode = ZT_CONF_REALANDPSEUDO | ZT_CONF_TALKER | ZT_CONF_LISTENER
2520 | ZT_CONF_PSEUDO_TALKER | ZT_CONF_PSEUDO_LISTENER;
2521 /* first put the channel on the conference */
2522 if (ioctl(genchannel->fds[0], ZT_SETCONF, &ci) == -1) {
2523 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
2524 ast_hangup(mychannel);
2525 ast_hangup(genchannel);
2526 myrpt->callmode = 0;
2529 if (myrpt->p.tonezone && (tone_zone_set_zone(mychannel->fds[0], myrpt->p.tonezone) == -1)) {
2530 ast_log(LOG_WARNING, "Unable to set tone zone %s\n", myrpt->p.tonezone);
2531 ast_hangup(mychannel);
2532 ast_hangup(genchannel);
2533 myrpt->callmode = 0;
2536 if (myrpt->p.tonezone && (tone_zone_set_zone(genchannel->fds[0], myrpt->p.tonezone) == -1)) {
2537 ast_log(LOG_WARNING, "Unable to set tone zone %s\n", myrpt->p.tonezone);
2538 ast_hangup(mychannel);
2539 ast_hangup(genchannel);
2540 myrpt->callmode = 0;
2543 /* start dialtone if patchquiet is 0. Special patch modes don't send dial tone */
2544 if ((!myrpt->patchquiet) && (tone_zone_play_tone(mychannel->fds[0], ZT_TONE_DIALTONE) < 0)) {
2545 ast_log(LOG_WARNING, "Cannot start dialtone\n");
2546 ast_hangup(mychannel);
2547 ast_hangup(genchannel);
2548 myrpt->callmode = 0;
2557 while ((myrpt->callmode == 1) || (myrpt->callmode == 4)) {
2558 if ((myrpt->patchdialtime) && (myrpt->callmode == 1) && (myrpt->cidx != lastcidx)) {
2560 lastcidx = myrpt->cidx;
2563 if ((myrpt->patchdialtime) && (dialtimer >= myrpt->patchdialtime)) {
2564 rpt_mutex_lock(&myrpt->lock);
2566 myrpt->callmode = 0;
2567 rpt_mutex_unlock(&myrpt->lock);
2571 if ((!myrpt->patchquiet) && (!stopped) && (myrpt->callmode == 1) && (myrpt->cidx > 0)) {
2573 /* stop dial tone */
2574 tone_zone_play_tone(mychannel->fds[0], -1);
2576 if (myrpt->callmode == 4) {
2579 /* start congestion tone */
2580 tone_zone_play_tone(mychannel->fds[0], ZT_TONE_CONGESTION);
2583 res = ast_safe_sleep(mychannel, MSWAIT);
2585 ast_hangup(mychannel);
2586 ast_hangup(genchannel);
2587 rpt_mutex_lock(&myrpt->lock);
2588 myrpt->callmode = 0;
2589 rpt_mutex_unlock(&myrpt->lock);
2592 dialtimer += MSWAIT;
2594 /* stop any tone generation */
2595 tone_zone_play_tone(mychannel->fds[0], -1);
2597 if (!myrpt->callmode) {
2598 ast_hangup(mychannel);
2599 ast_hangup(genchannel);
2600 rpt_mutex_lock(&myrpt->lock);
2601 myrpt->callmode = 0;
2602 rpt_mutex_unlock(&myrpt->lock);
2603 if ((!myrpt->patchquiet) && aborted)
2604 rpt_telemetry(myrpt, TERM, NULL);
2608 if (myrpt->p.ourcallerid && *myrpt->p.ourcallerid) {
2609 char *name, *loc, *instr;
2610 instr = ast_strdup(myrpt->p.ourcallerid);
2612 ast_callerid_parse(instr, &name, &loc);
2614 if (mychannel->cid.cid_num)
2615 ast_free(mychannel->cid.cid_num);
2616 mychannel->cid.cid_num = ast_strdup(loc);
2619 if (mychannel->cid.cid_name)
2620 ast_free(mychannel->cid.cid_name);
2621 mychannel->cid.cid_name = ast_strdup(name);
2627 ast_copy_string(mychannel->exten, myrpt->exten, sizeof(mychannel->exten));
2628 ast_copy_string(mychannel->context, myrpt->patchcontext, sizeof(mychannel->context));
2630 if (myrpt->p.acctcode)
2631 ast_copy_string((char *)mychannel->accountcode, myrpt->p.acctcode, sizeof(mychannel->accountcode));
2632 mychannel->priority = 1;
2633 ast_channel_undefer_dtmf(mychannel);
2634 if (ast_pbx_start(mychannel) < 0) {
2635 ast_log(LOG_WARNING, "Unable to start PBX!!\n");
2636 ast_hangup(mychannel);
2637 ast_hangup(genchannel);
2638 rpt_mutex_lock(&myrpt->lock);
2639 myrpt->callmode = 0;
2640 rpt_mutex_unlock(&myrpt->lock);
2644 rpt_mutex_lock(&myrpt->lock);
2645 myrpt->callmode = 3;
2646 /* set appropriate conference for the pseudo */
2648 ci.confno = myrpt->conf;
2649 ci.confmode = (myrpt->p.duplex == 2) ? ZT_CONF_CONFANNMON :
2650 (ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER);
2651 /* first put the channel on the conference in announce mode */
2652 if (ioctl(myrpt->pchannel->fds[0], ZT_SETCONF, &ci) == -1) {
2653 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
2654 ast_hangup(mychannel);
2655 ast_hangup(genchannel);
2656 myrpt->callmode = 0;
2659 while (myrpt->callmode) {
2660 if ((!mychannel->pbx) && (myrpt->callmode != 4)) {
2661 if (myrpt->patchfarenddisconnect) { /* If patch is setup for far end disconnect */
2662 myrpt->callmode = 0;
2663 if (!myrpt->patchquiet) {
2664 rpt_mutex_unlock(&myrpt->lock);
2665 rpt_telemetry(myrpt, TERM, NULL);
2666 rpt_mutex_lock(&myrpt->lock);
2668 } else { /* Send congestion until patch is downed by command */
2669 myrpt->callmode = 4;
2670 rpt_mutex_unlock(&myrpt->lock);
2671 /* start congestion tone */
2672 tone_zone_play_tone(genchannel->fds[0], ZT_TONE_CONGESTION);
2673 rpt_mutex_lock(&myrpt->lock);
2676 if (myrpt->mydtmf) {
2677 wf.frametype = AST_FRAME_DTMF;
2678 wf.subclass = myrpt->mydtmf;
2684 rpt_mutex_unlock(&myrpt->lock);
2685 ast_write(genchannel, &wf);
2686 rpt_mutex_lock(&myrpt->lock);
2689 rpt_mutex_unlock(&myrpt->lock);
2690 usleep(MSWAIT * 1000);
2691 rpt_mutex_lock(&myrpt->lock);
2693 rpt_mutex_unlock(&myrpt->lock);
2694 tone_zone_play_tone(genchannel->fds[0], -1);
2696 ast_softhangup(mychannel, AST_SOFTHANGUP_DEV);
2697 ast_hangup(genchannel);
2698 rpt_mutex_lock(&myrpt->lock);
2699 myrpt->callmode = 0;
2700 rpt_mutex_unlock(&myrpt->lock);
2701 /* set appropriate conference for the pseudo */
2703 ci.confno = myrpt->conf;
2704 ci.confmode = ((myrpt->p.duplex == 2) || (myrpt->p.duplex == 4)) ? ZT_CONF_CONFANNMON :
2705 (ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER);
2706 /* first put the channel on the conference in announce mode */
2707 if (ioctl(myrpt->pchannel->fds[0], ZT_SETCONF, &ci) == -1) {
2708 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
2713 static void send_link_dtmf(struct rpt *myrpt, char c)
2716 struct ast_frame wf;
2719 snprintf(str, sizeof(str), "D %s %s %d %c", myrpt->cmdnode, myrpt->name, ++(myrpt->dtmfidx), c);
2720 wf.frametype = AST_FRAME_TEXT;
2724 wf.datalen = strlen(str) + 1;
2726 l = myrpt->links.next;
2727 /* first, see if our dude is there */
2728 while (l != &myrpt->links) {
2729 if (l->name[0] == '0') {
2733 /* if we found it, write it and were done */
2734 if (!strcmp(l->name, myrpt->cmdnode)) {
2735 wf.data = ast_strdup(str);
2737 ast_write(l->chan, &wf);
2742 l = myrpt->links.next;
2743 /* if not, give it to everyone */
2744 while (l != &myrpt->links) {
2745 wf.data = ast_strdup(str);
2747 ast_write(l->chan, &wf);
2754 * Internet linking function
2757 static int function_ilink(struct rpt *myrpt, char *param, char *digits, int command_source, struct rpt_link *mylink)
2761 char deststr[300] = "", modechange = 0;
2762 char digitbuf[MAXNODESTR];
2765 ZT_CONFINFO ci; /* conference info */
2766 AST_DECLARE_APP_ARGS(args,
2768 AST_APP_ARG(s2); /* XXX Never used. Scratch? XXX */
2777 ast_copy_string(digitbuf, digits, sizeof(digitbuf));
2778 ast_debug(1, "@@@@ ilink param = %s, digitbuf = %s\n", S_OR(param, "(null)"), digitbuf);
2780 switch (myatoi(param)) {
2781 case 1: /* Link off */
2782 if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
2783 strcpy(digitbuf, myrpt->lastlinknode);
2784 val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, digitbuf);
2786 if (strlen(digitbuf) >= myrpt->longestnode)
2790 rpt_mutex_lock(&myrpt->lock);
2791 l = myrpt->links.next;
2792 /* try to find this one in queue */
2793 while (l != &myrpt->links) {
2794 if (l->name[0] == '0') {
2798 /* if found matching string */
2799 if (!strcmp(l->name, digitbuf))
2803 if (l != &myrpt->links) { /* if found */
2804 struct ast_frame wf;
2805 ast_copy_string(myrpt->lastlinknode, digitbuf, MAXNODESTR);
2806 l->retries = MAX_RETRIES + 1;
2808 rpt_mutex_unlock(&myrpt->lock);
2809 wf.frametype = AST_FRAME_TEXT;
2813 wf.datalen = strlen(discstr) + 1;
2815 wf.data = ast_strdup(discstr);
2817 ast_write(l->chan, &wf);
2818 if (ast_safe_sleep(l->chan, 250) == -1)
2820 ast_softhangup(l->chan, AST_SOFTHANGUP_DEV);
2822 rpt_telemetry(myrpt, COMPLETE, NULL);
2825 rpt_mutex_unlock(&myrpt->lock);
2827 case 2: /* Link Monitor */
2828 if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
2829 strcpy(digitbuf, myrpt->lastlinknode);
2830 val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, digitbuf);
2832 if (strlen(digitbuf) >= myrpt->longestnode)
2836 s = ast_strdupa(val);
2837 AST_NONSTANDARD_APP_ARGS(args, s, ',');
2838 rpt_mutex_lock(&myrpt->lock);
2839 l = myrpt->links.next;
2840 /* try to find this one in queue */
2841 while (l != &myrpt->links) {
2842 if (l->name[0] == '0') {
2846 /* if found matching string */
2847 if (!strcmp(l->name, digitbuf))
2852 if (l != &myrpt->links) {
2853 /* if already in this mode, just ignore */
2854 if ((!l->mode) || (!l->chan)) {
2855 rpt_mutex_unlock(&myrpt->lock);
2856 rpt_telemetry(myrpt, REMALREADY, NULL);
2859 reconnects = l->reconnects;
2860 rpt_mutex_unlock(&myrpt->lock);
2862 ast_softhangup(l->chan, AST_SOFTHANGUP_DEV);
2863 l->retries = MAX_RETRIES + 1;
2867 rpt_mutex_unlock(&myrpt->lock);
2868 ast_copy_string(myrpt->lastlinknode, digitbuf, MAXNODESTR);
2869 /* establish call in monitor mode */
2870 l = ast_calloc(1, sizeof(*l));
2872 ast_log(LOG_WARNING, "Unable to malloc\n");
2875 snprintf(deststr, sizeof(deststr), "IAX2/%s", args.s1);
2876 tele = strchr(deststr, '/');
2878 ast_log(LOG_ERROR, "link2:Dial number (%s) must be in format tech/number\n", deststr);
2882 l->isremote = (s && ast_true(s));
2883 ast_copy_string(l->name, digitbuf, MAXNODESTR);
2884 l->chan = ast_request(deststr, AST_FORMAT_SLINEAR, tele, NULL);
2888 ast_set_read_format(l->chan, AST_FORMAT_SLINEAR);
2889 ast_set_write_format(l->chan, AST_FORMAT_SLINEAR);
2890 l->chan->whentohangup = 0;
2891 l->chan->appl = "Apprpt";
2892 l->chan->data = "(Remote Rx)";
2893 if (option_verbose > 2)
2894 ast_verbose(VERBOSE_PREFIX_3 "rpt (remote) initiating call to %s/%s on %s\n",
2895 deststr, tele, l->chan->name);
2896 if (l->chan->cid.cid_num)
2897 ast_free(l->chan->cid.cid_num);
2898 l->chan->cid.cid_num = ast_strdup(myrpt->name);
2899 ast_call(l->chan, tele, 0);
2901 rpt_telemetry(myrpt, CONNFAIL, l);
2903 if (option_verbose > 2)
2904 ast_verbose(VERBOSE_PREFIX_3 "Unable to place call to %s/%s on %s\n",
2905 deststr, tele, l->chan->name);
2908 /* allocate a pseudo-channel thru asterisk */
2909 l->pchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
2911 ast_log(LOG_ERROR, "rpt:Sorry unable to obtain pseudo channel\n");
2912 ast_hangup(l->chan);
2916 ast_set_read_format(l->pchan, AST_FORMAT_SLINEAR);
2917 ast_set_write_format(l->pchan, AST_FORMAT_SLINEAR);
2918 /* make a conference for the pseudo-one */
2920 ci.confno = myrpt->conf;
2921 ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER;
2922 /* first put the channel on the conference in proper mode */
2923 if (ioctl(l->pchan->fds[0], ZT_SETCONF, &ci) == -1) {
2924 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
2925 ast_hangup(l->chan);
2926 ast_hangup(l->pchan);
2930 rpt_mutex_lock(&myrpt->lock);
2931 l->reconnects = reconnects;
2932 /* insert at end of queue */
2933 insque((struct qelem *)l, (struct qelem *)myrpt->links.next);
2934 rpt_mutex_unlock(&myrpt->lock);
2935 rpt_telemetry(myrpt, COMPLETE, NULL);
2937 case 3: /* Link transceive */
2938 if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
2939 strcpy(digitbuf, myrpt->lastlinknode);
2940 val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, digitbuf);
2942 if (strlen(digitbuf) >= myrpt->longestnode)
2946 s = ast_strdupa(val);
2947 AST_NONSTANDARD_APP_ARGS(args, s, ',');
2948 rpt_mutex_lock(&myrpt->lock);
2949 l = myrpt->links.next;
2950 /* try to find this one in queue */
2951 while (l != &myrpt->links) {
2952 if (l->name[0] == '0') {
2956 /* if found matching string */
2957 if (!strcmp(l->name, digitbuf))
2962 if (l != &myrpt->links) {
2963 /* if already in this mode, just ignore */
2964 if ((l->mode) || (!l->chan)) {
2965 rpt_mutex_unlock(&myrpt->lock);
2966 rpt_telemetry(myrpt, REMALREADY, NULL);
2969 reconnects = l->reconnects;
2970 rpt_mutex_unlock(&myrpt->lock);
2972 ast_softhangup(l->chan, AST_SOFTHANGUP_DEV);
2973 l->retries = MAX_RETRIES + 1;
2977 rpt_mutex_unlock(&myrpt->lock);
2978 ast_copy_string(myrpt->lastlinknode, digitbuf, MAXNODESTR);
2979 /* establish call in tranceive mode */
2980 l = ast_calloc(1, sizeof(*l));
2982 ast_log(LOG_WARNING, "Unable to malloc\n");
2987 ast_copy_string(l->name, digitbuf, MAXNODESTR);
2988 l->isremote = (s && ast_true(s));
2991 snprintf(deststr, sizeof(deststr), "IAX2/%s", args.s1);
2992 tele = strchr(deststr, '/');
2994 ast_log(LOG_ERROR, "link3:Dial number (%s) must be in format tech/number\n", deststr);
2999 l->chan = ast_request(deststr, AST_FORMAT_SLINEAR, tele, NULL);
3001 ast_set_read_format(l->chan, AST_FORMAT_SLINEAR);
3002 ast_set_write_format(l->chan, AST_FORMAT_SLINEAR);
3003 l->chan->whentohangup = 0;
3004 l->chan->appl = "Apprpt";
3005 l->chan->data = "(Remote Rx)";
3006 if (option_verbose > 2)
3007 ast_verbose(VERBOSE_PREFIX_3 "rpt (remote) initiating call to %s/%s on %s\n",
3008 deststr, tele, l->chan->name);
3009 if (l->chan->cid.cid_num)
3010 ast_free(l->chan->cid.cid_num);
3011 l->chan->cid.cid_num = ast_strdup(myrpt->name);
3012 ast_call(l->chan, tele, 999);
3014 rpt_telemetry(myrpt, CONNFAIL, l);
3016 if (option_verbose > 2)
3017 ast_verbose(VERBOSE_PREFIX_3 "Unable to place call to %s/%s on %s\n",
3018 deststr, tele, l->chan->name);
3021 /* allocate a pseudo-channel thru asterisk */
3022 l->pchan = ast_request("zap", AST_FORMAT_SLINEAR, "pseudo", NULL);
3024 ast_log(LOG_ERROR, "rpt:Sorry unable to obtain pseudo channel\n");
3025 ast_hangup(l->chan);
3029 ast_set_read_format(l->pchan, AST_FORMAT_SLINEAR);
3030 ast_set_write_format(l->pchan, AST_FORMAT_SLINEAR);
3031 /* make a conference for the tx */
3033 ci.confno = myrpt->conf;
3034 ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER;
3035 /* first put the channel on the conference in proper mode */
3036 if (ioctl(l->pchan->fds[0], ZT_SETCONF, &ci) == -1) {
3037 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
3038 ast_hangup(l->chan);
3039 ast_hangup(l->pchan);
3043 rpt_mutex_lock(&myrpt->lock);
3044 l->reconnects = reconnects;
3045 /* insert at end of queue */
3046 insque((struct qelem *)l, (struct qelem *)myrpt->links.next);
3047 rpt_mutex_unlock(&myrpt->lock);
3048 rpt_telemetry(myrpt, COMPLETE, NULL);
3050 case 4: /* Enter Command Mode */
3051 /* if doesnt allow link cmd, or no links active, return */
3052 if (((command_source != SOURCE_RPT) &&
3053 (command_source != SOURCE_PHONE) &&
3054 (command_source != SOURCE_DPHONE)) ||
3055 (myrpt->links.next == &myrpt->links))
3057 /* if already in cmd mode, or selected self, fughetabahtit */
3058 if ((myrpt->cmdnode[0]) || (!strcmp(myrpt->name, digitbuf))) {
3059 rpt_telemetry(myrpt, REMALREADY, NULL);
3062 if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
3063 strcpy(digitbuf, myrpt->lastlinknode);
3064 /* node must at least exist in list */
3065 val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, digitbuf);
3067 if (strlen(digitbuf) >= myrpt->longestnode)
3071 rpt_mutex_lock(&myrpt->lock);
3072 strcpy(myrpt->lastlinknode, digitbuf);
3073 ast_copy_string(myrpt->cmdnode, digitbuf, sizeof(myrpt->cmdnode));
3074 rpt_mutex_unlock(&myrpt->lock);
3075 rpt_telemetry(myrpt, REMGO, NULL);
3077 case 5: /* Status */
3078 rpt_telemetry(myrpt, STATUS, NULL);
3080 case 6: /* All Links Off */
3081 l = myrpt->links.next;
3082 while (l != &myrpt->links) { /* This code is broke and needs to be changed to work with the reconnect kludge */
3084 ast_softhangup(l->chan, AST_SOFTHANGUP_DEV); /* Hang 'em up */
3087 rpt_telemetry(myrpt, COMPLETE, NULL);
3089 case 7: /* Identify last node which keyed us up */
3090 rpt_telemetry(myrpt, LASTNODEKEY, NULL);
3096 return DC_INDETERMINATE;
3103 static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
3107 AST_DECLARE_APP_ARGS(params,
3108 AST_APP_ARG(list)[20];
3111 static char *keywords[] = {
3123 ast_debug(1, "@@@@ Autopatch up\n");
3125 if (!myrpt->callmode) {
3127 myrpt->patchnoct = 0;
3128 myrpt->patchdialtime = 0;
3129 myrpt->patchfarenddisconnect = 0;
3130 myrpt->patchquiet = 0;
3131 ast_copy_string(myrpt->patchcontext, myrpt->p.ourcontext, sizeof(myrpt->patchcontext));
3134 /* Process parameter list */
3135 char *tmp = ast_strdupa(param);
3136 AST_NONSTANDARD_APP_ARGS(params, tmp, ',');
3137 for (i = 0; i < params.argc; i++) {
3138 index = matchkeyword(params.list[i], &value, keywords);
3140 value = skipchars(value, "= ");
3142 case 1: /* context */
3143 ast_copy_string(myrpt->patchcontext, value, sizeof(myrpt->patchcontext)) ;
3145 case 2: /* dialtime */
3146 myrpt->patchdialtime = atoi(value);
3148 case 3: /* farenddisconnect */
3149 myrpt->patchfarenddisconnect = atoi(value);
3152 myrpt->patchnoct = atoi(value);
3155 myrpt->patchquiet = atoi(value);
3164 rpt_mutex_lock(&myrpt->lock);
3166 /* if on call, force * into current audio stream */
3168 if ((myrpt->callmode == 2) || (myrpt->callmode == 3)) {
3169 myrpt->mydtmf = myrpt->p.funcchar;
3171 if (myrpt->callmode) {
3172 rpt_mutex_unlock(&myrpt->lock);
3175 myrpt->callmode = 1;
3177 myrpt->exten[myrpt->cidx] = 0;
3178 rpt_mutex_unlock(&myrpt->lock);
3179 ast_pthread_create_detached(&myrpt->rpt_call_thread, NULL, rpt_call, (void *) myrpt);
3187 static int function_autopatchdn(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
3192 ast_debug(1, "@@@@ Autopatch down\n");
3194 rpt_mutex_lock(&myrpt->lock);
3196 if (!myrpt->callmode) {
3197 rpt_mutex_unlock(&myrpt->lock);
3201 myrpt->callmode = 0;
3202 rpt_mutex_unlock(&myrpt->lock);
3203 rpt_telemetry(myrpt, TERM, NULL);
3211 static int function_status(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
3220 ast_debug(1, "@@@@ status param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf);
3222 switch (myatoi(param)) {
3223 case 1: /* System ID */
3224 rpt_telemetry(myrpt, ID1, NULL);
3226 case 2: /* System Time */
3227 rpt_telemetry(myrpt, STATS_TIME, NULL);
3229 case 3: /* app_rpt.c version */
3230 rpt_telemetry(myrpt, STATS_VERSION, NULL);
3236 return DC_INDETERMINATE;
3240 * Macro-oni (without Salami)
3243 static int function_macro(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
3248 struct ast_channel *mychannel;
3250 if ((!myrpt->remote) && (!myrpt->enable))
3253 ast_debug(1, "@@@@ macro-oni param = %s, digitbuf = %s\n", (param)? param : "(null)", digitbuf);
3255 mychannel = myrpt->remchannel;
3257 if (ast_strlen_zero(digitbuf)) /* needs 1 digit */
3258 return DC_INDETERMINATE;
3260 for (i = 0; i < digitbuf[i]; i++) {
3261 if ((digitbuf[i] < '0') || (digitbuf[i] > '9'))
3265 if (*digitbuf == '0')
3266 val = myrpt->p.startupmacro;
3268 val = ast_variable_retrieve(myrpt->cfg, myrpt->p.macro, digitbuf);
3269 /* param was 1 for local buf */
3271 rpt_telemetry(myrpt, MACRO_NOTFOUND, NULL);
3274 rpt_mutex_lock(&myrpt->lock);
3275 if ((sizeof(myrpt->macrobuf) - strlen(myrpt->macrobuf)) < strlen(val)) {
3276 rpt_mutex_unlock(&myrpt->lock);
3277 rpt_telemetry(myrpt, MACRO_BUSY, NULL);
3280 myrpt->macrotimer = MACROTIME;
3281 strncat(myrpt->macrobuf, val, sizeof(myrpt->macrobuf) - 1);
3282 rpt_mutex_unlock(&myrpt->lock);
3287 * COP - Control operator
3290 static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
3295 switch(myatoi(param)) {
3296 case 1: /* System reset */
3297 ast_cli_command(STDERR_FILENO, "restart now"); /* A little less drastic than what was previously here. */
3301 rpt_telemetry(myrpt, ARB_ALPHA, (void *) "RPTENA");
3306 case 4: /* test tone on */
3307 rpt_telemetry(myrpt, TEST_TONE, NULL);
3309 case 5: /* Disgorge variables to log for debug purposes */
3310 myrpt->disgorgetime = time(NULL) + 10; /* Do it 10 seconds later */
3312 case 6: /* Simulate COR being activated (phone only) */
3313 if (command_source != SOURCE_PHONE)
3314 return DC_INDETERMINATE;
3317 return DC_INDETERMINATE;
3321 * Collect digits one by one until something matches
3324 static int collect_function_digits(struct rpt *myrpt, char *digits, int command_source, struct rpt_link *mylink)
3327 char *stringp, *functiondigits;
3328 char function_table_name[30] = "";
3329 struct ast_variable *vp;
3330 AST_DECLARE_APP_ARGS(args,
3331 AST_APP_ARG(action);
3335 ast_debug(1, "@@@@ Digits collected: %s, source: %d\n", digits, command_source);
3337 if (command_source == SOURCE_DPHONE) {
3338 if (!myrpt->p.dphone_functions)
3339 return DC_INDETERMINATE;
3340 ast_copy_string(function_table_name, myrpt->p.dphone_functions, sizeof(function_table_name));
3341 } else if (command_source == SOURCE_PHONE) {
3342 if (!myrpt->p.phone_functions)
3343 return DC_INDETERMINATE;
3344 ast_copy_string(function_table_name, myrpt->p.phone_functions, sizeof(function_table_name));
3345 } else if (command_source == SOURCE_LNK)
3346 ast_copy_string(function_table_name, myrpt->p.link_functions, sizeof(function_table_name));
3348 ast_copy_string(function_table_name, myrpt->p.functions, sizeof(function_table_name));
3350 for (vp = ast_variable_browse(myrpt->cfg, function_table_name); vp; vp = vp->next) {
3351 if (!strncasecmp(vp->name, digits, strlen(vp->name)))
3357 n = myrpt->longestfunc;
3358 if (command_source == SOURCE_LNK)
3359 n = myrpt->link_longestfunc;
3360 else if (command_source == SOURCE_PHONE)
3361 n = myrpt->phone_longestfunc;
3362 else if (command_source == SOURCE_DPHONE)
3363 n = myrpt->dphone_longestfunc;
3365 if (strlen(digits) >= n)
3368 return DC_INDETERMINATE;
3371 /* Found a match, retrieve value part and parse */
3372 stringp = ast_strdupa(vp->value);
3373 AST_NONSTANDARD_APP_ARGS(args, stringp, ',');
3375 ast_debug(1, "@@@@ action: %s, param = %s\n", args.action, S_OR(args.param, "(null)"));
3376 /* Look up the action */
3377 for (i = 0; i < (sizeof(function_table) / sizeof(struct function_table_tag)); i++) {
3378 if (!strncasecmp(args.action, function_table[i].action, strlen(args.action)))
3381 ast_debug(1, "@@@@ table index i = %d\n", i);
3382 if (i == (sizeof(function_table) / sizeof(struct function_table_tag))) {
3383 /* Error, action not in table */
3386 if (function_table[i].function == NULL) {
3387 /* Error, function undefined */
3388 ast_debug(1, "@@@@ NULL for action: %s\n", args.action);
3391 functiondigits = digits + strlen(vp->name);
3392 return (*function_table[i].function)(myrpt, args.param, functiondigits, command_source, mylink);
3396 static void handle_link_data(struct rpt *myrpt, struct rpt_link *mylink, char *str)
3398 char cmd[300] = "", dest[300], src[300], c;
3401 struct ast_frame wf;
3403 wf.frametype = AST_FRAME_TEXT;
3407 wf.datalen = strlen(str) + 1;
3409 if (!strcmp(str, discstr)) {
3411 mylink->retries = MAX_RETRIES + 1;
3412 ast_softhangup(mylink->chan, AST_SOFTHANGUP_DEV);
3415 if (sscanf(str, "%s %s %s %d %c", cmd, dest, src, &seq, &c) != 5) {
3416 ast_log(LOG_WARNING, "Unable to parse link string %s\n", str);
3419 if (strcmp(cmd, "D")) {
3420 ast_log(LOG_WARNING, "Unable to parse link string %s\n", str);
3424 if (dest[0] == '0') {
3425 strcpy(dest, myrpt->name);
3428 /* if not for me, redistribute to all links */
3429 if (strcmp(dest, myrpt->name)) {
3430 l = myrpt->links.next;
3431 /* see if this is one in list */
3432 while (l != &myrpt->links) {
3433 if (l->name[0] == '0') {
3437 /* dont send back from where it came */
3438 if ((l == mylink) || (!strcmp(l->name, mylink->name))) {
3442 /* if it is, send it and we're done */
3443 if (!strcmp(l->name, dest)) {
3444 /* send, but not to src */
3445 if (strcmp(l->name, src)) {
3446 wf.data = ast_strdup(str);
3448 ast_write(l->chan, &wf);
3454 l = myrpt->links.next;
3455 /* otherwise, send it to all of em */
3456 while (l != &myrpt->links) {
3457 if (l->name[0] == '0') {
3461 /* dont send back from where it came */
3462 if ((l == mylink) || (!strcmp(l->name, mylink->name))) {
3466 /* send, but not to src */
3467 if (strcmp(l->name, src)) {
3468 wf.data = ast_strdup(str);
3470 ast_write(l->chan, &wf);
3476 rpt_mutex_lock(&myrpt->lock);
3477 if (c == myrpt->p.endchar)
3479 if (myrpt->callmode == 1) {
3480 myrpt->exten[myrpt->cidx++] = c;
3481 myrpt->exten[myrpt->cidx] = 0;
3482 /* if this exists */
3483 if (ast_exists_extension(myrpt->pchannel, myrpt->patchcontext, myrpt->exten, 1, NULL)) {
3484 myrpt->callmode = 2;
3485 if (!myrpt->patchquiet) {
3486 rpt_mutex_unlock(&myrpt->lock);
3487 rpt_telemetry(myrpt, PROC, NULL);
3488 rpt_mutex_lock(&myrpt->lock);
3491 /* if can continue, do so */
3492 if (!ast_canmatch_extension(myrpt->pchannel, myrpt->patchcontext, myrpt->exten, 1, NULL)) {
3493 /* call has failed, inform user */
3494 myrpt->callmode = 4;
3497 if ((myrpt->callmode == 2) || (myrpt->callmode == 3)) {
3500 if (c == myrpt->p.funcchar) {
3501 myrpt->rem_dtmfidx = 0;
3502 myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
3503 time(&myrpt->rem_dtmf_time);
3504 rpt_mutex_unlock(&myrpt->lock);
3506 } else if ((c != myrpt->p.endchar) && (myrpt->rem_dtmfidx >= 0)) {
3507 time(&myrpt->rem_dtmf_time);
3508 if (myrpt->rem_dtmfidx < MAXDTMF) {
3509 myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx++] = c;
3510 myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
3512 rpt_mutex_unlock(&myrpt->lock);
3513 ast_copy_string(cmd, myrpt->rem_dtmfbuf, sizeof(cmd));
3514 res = collect_function_digits(myrpt, cmd, SOURCE_LNK, mylink);
3515 rpt_mutex_lock(&myrpt->lock);
3518 case DC_INDETERMINATE:
3521 myrpt->rem_dtmfidx = 0;
3522 myrpt->rem_dtmfbuf[0] = 0;
3525 myrpt->totalexecdcommands++;
3526 myrpt->dailyexecdcommands++;
3527 ast_copy_string(myrpt->lastdtmfcommand, cmd, MAXDTMF);
3528 myrpt->lastdtmfcommand[MAXDTMF-1] = '\0';
3529 myrpt->rem_dtmfbuf[0] = 0;
3530 myrpt->rem_dtmfidx = -1;
3531 myrpt->rem_dtmf_time = 0;