Wed Aug 20 06:36:19 2008

Asterisk developer's documentation


app_rpt.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2002-2005, Jim Dixon, WB6NIL
00005  *
00006  * Jim Dixon, WB6NIL <jim@lambdatel.com>
00007  * Serious contributions by Steve RoDgers, WA6ZFT <hwstar@rodgers.sdcoxmail.com>
00008  *
00009  * See http://www.asterisk.org for more information about
00010  * the Asterisk project. Please do not directly contact
00011  * any of the maintainers of this project for assistance;
00012  * the project provides a web site, mailing lists and IRC
00013  * channels for your use.
00014  *
00015  * This program is free software, distributed under the terms of
00016  * the GNU General Public License Version 2. See the LICENSE file
00017  * at the top of the source tree.
00018  */
00019 
00020 /*
00021  *
00022  * Radio Repeater / Remote Base program 
00023  *  version 0.37 11/3/05
00024  * 
00025  * See http://www.zapatatelephony.org/app_rpt.html
00026  *
00027  *
00028  * Repeater / Remote Functions:
00029  * "Simple" Mode:  * - autopatch access, # - autopatch hangup
00030  * Normal mode:
00031  * See the function list in rpt.conf
00032  *
00033  *  To send an asterisk (*) while dialing or talking on phone,
00034  *  use the autopatch acess code.
00035  *
00036  *
00037  * status cmds:
00038  *
00039  *  1 - Force ID
00040  *  2 - Give Time of Day
00041  *  3 - Give software Version
00042  *
00043  * cop (control operator) cmds:
00044  *
00045  *  1 - System warm boot
00046  *  2 - System enable
00047  *  3 - System disable
00048  *  4 - Test Tone On
00049  *  5 - Dump System Variables on Console (debug)
00050  *  6 - PTT (phone mode only)
00051  *
00052  * ilink cmds:
00053  *
00054  *  1 - Disconnect specified link
00055  *  2 - Connect specified link -- monitor only
00056  *  3 - Connect specified link -- tranceive
00057  *  4 - Enter command mode on specified link
00058  *  5 - System status
00059  *  6 - Disconnect all links
00060  *
00061  * remote cmds:
00062  *
00063  *  1 - Recall Memory MM  (*000-*099) (Gets memory from rpt.conf)
00064  *  2 - Set VFO MMMMM*KKK*O   (Mhz digits, Khz digits, Offset)
00065  *  3 - Set Rx PL Tone HHH*D*
00066  *  4 - Set Tx PL Tone HHH*D* (Not currently implemented with DHE RBI-1)
00067  *  5 - Link Status (long)
00068  *  6 - Set operating mode M (FM, USB, LSB, AM, etc)
00069  *  100 - RX PL off (Default)
00070  *  101 - RX PL On
00071  *  102 - TX PL Off (Default)
00072  *  103 - TX PL On
00073  *  104 - Low Power
00074  *  105 - Med Power
00075  *  106 - Hi Power
00076  *  107 - Bump Down 20 Hz
00077  *  108 - Bump Down 100 Hz
00078  *  109 - Bump Down 500 Hz
00079  *  110 - Bump Up 20 Hz
00080  *  111 - Bump Up 100 Hz
00081  *  112 - Bump Up 500 Hz
00082  *  113 - Scan Down Slow
00083  *  114 - Scan Down Medium
00084  *  115 - Scan Down Fast
00085  *  116 - Scan Up Slow
00086  *  117 - Scan Up Medium
00087  *  118 - Scan Up Fast
00088  *  119 - Transmit allowing auto-tune
00089  *  140 - Link Status (brief)
00090  *
00091  *
00092 */
00093 
00094 /* The following is JUST GROSS!! There is some soft of underlying problem,
00095    probably in channel_iax2.c, that causes an IAX2 connection to sometimes
00096    stop transmitting randomly. We have been working for weeks to try to
00097    locate it and fix it, but to no avail We finally decided to put our
00098    tail between our legs, and just make the radio system re-connect upon
00099    network failure. This just shouldnt have to be done. For normal operation,
00100    comment-out the following line */
00101 #define  RECONNECT_KLUDGE 
00102 
00103 /* maximum digits in DTMF buffer, and seconds after * for DTMF command timeout */
00104 
00105 #define  MAXDTMF 32
00106 #define  DTMF_TIMEOUT 3
00107 
00108 #define  DISC_TIME 10000  /* report disc after 10 seconds of no connect */
00109 #define  MAX_RETRIES 5
00110 
00111 #define  REDUNDANT_TX_TIME 2000
00112 
00113 #define  RETRY_TIMER_MS 5000
00114 
00115 #define  MAXREMSTR 15
00116 
00117 #define  NODES "nodes"
00118 #define MEMORY "memory"
00119 #define  FUNCTIONS "functions"
00120 #define TELEMETRY "telemetry"
00121 #define MORSE "morse"
00122 #define  FUNCCHAR '*'
00123 #define  ENDCHAR '#'
00124 
00125 #define  DEFAULT_IOBASE 0x378
00126 
00127 #define  MAXCONNECTTIME 5000
00128 
00129 #define MAXNODESTR 300
00130 
00131 #define ACTIONSIZE 32
00132 
00133 #define TELEPARAMSIZE 256
00134 
00135 #define REM_SCANTIME 100
00136 
00137 
00138 enum {REM_OFF,REM_MONITOR,REM_TX};
00139 
00140 enum{ID,PROC,TERM,COMPLETE,UNKEY,REMDISC,REMALREADY,REMNOTFOUND,REMGO,
00141    CONNECTED,CONNFAIL,STATUS,TIMEOUT,ID1, STATS_TIME,
00142    STATS_VERSION, IDTALKOVER, ARB_ALPHA, TEST_TONE, REV_PATCH};
00143 
00144 enum {REM_SIMPLEX,REM_MINUS,REM_PLUS};
00145 
00146 enum {REM_LOWPWR,REM_MEDPWR,REM_HIPWR};
00147 
00148 enum {DC_INDETERMINATE, DC_REQ_FLUSH, DC_ERROR, DC_COMPLETE, DC_DOKEY};
00149 
00150 enum {SOURCE_RPT, SOURCE_LNK, SOURCE_RMT, SOURCE_PHONE, SOURCE_DPHONE};
00151 
00152 enum {DLY_TELEM, DLY_ID, DLY_UNKEY, DLY_CALLTERM};
00153 
00154 enum {REM_MODE_FM,REM_MODE_USB,REM_MODE_LSB,REM_MODE_AM};
00155 
00156 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};
00157 
00158 #include "asterisk.h"
00159 
00160 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 53045 $")
00161 
00162 #include <signal.h>
00163 #include <stdio.h>
00164 #include <unistd.h>
00165 #include <string.h>
00166 #include <stdlib.h>
00167 #include <search.h>
00168 #include <sys/types.h>
00169 #include <sys/stat.h>
00170 #include <errno.h>
00171 #include <dirent.h>
00172 #include <ctype.h>
00173 #include <sys/stat.h>
00174 #include <sys/time.h>
00175 #include <sys/file.h>
00176 #include <sys/ioctl.h>
00177 #include <sys/io.h>
00178 #include <math.h>
00179 #include <tonezone.h>
00180 #include <linux/zaptel.h>
00181 #include <netinet/in.h>
00182 #include <arpa/inet.h>
00183 
00184 #include "asterisk/utils.h"
00185 #include "asterisk/lock.h"
00186 #include "asterisk/file.h"
00187 #include "asterisk/logger.h"
00188 #include "asterisk/channel.h"
00189 #include "asterisk/callerid.h"
00190 #include "asterisk/pbx.h"
00191 #include "asterisk/module.h"
00192 #include "asterisk/translate.h"
00193 #include "asterisk/features.h"
00194 #include "asterisk/options.h"
00195 #include "asterisk/cli.h"
00196 #include "asterisk/config.h"
00197 #include "asterisk/say.h"
00198 #include "asterisk/localtime.h"
00199 
00200 static  char *tdesc = "Radio Repeater / Remote Base  version 0.37  11/03/2005";
00201 
00202 static char *app = "Rpt";
00203 
00204 static char *synopsis = "Radio Repeater/Remote Base Control System";
00205 
00206 static char *descrip = 
00207 "  Rpt(nodename[|options]):  Radio Remote Link or Remote Base Link Endpoint Process.\n"
00208 "\n"
00209 "    Not specifying an option puts it in normal endpoint mode (where source\n"
00210 "    IP and nodename are verified).\n"
00211 "\n"
00212 "    Options are as follows:\n"
00213 "\n"
00214 "        X - Normal endpoint mode WITHOUT security check. Only specify\n"
00215 "            this if you have checked security already (like with an IAX2\n"
00216 "            user/password or something).\n"
00217 "\n"
00218 "        Rannounce-string[|timeout[|timeout-destination]] - Amateur Radio\n"
00219 "            Reverse Autopatch. Caller is put on hold, and announcement (as\n"
00220 "            specified by the 'announce-string') is played on radio system.\n"
00221 "            Users of radio system can access autopatch, dial specified\n"
00222 "            code, and pick up call. Announce-string is list of names of\n"
00223 "            recordings, or \"PARKED\" to substitute code for un-parking,\n"
00224 "            or \"NODE\" to substitute node number.\n"
00225 "\n"
00226 "        P - Phone Control mode. This allows a regular phone user to have\n"
00227 "            full control and audio access to the radio system. For the\n"
00228 "            user to have DTMF control, the 'phone_functions' parameter\n"
00229 "            must be specified for the node in 'rpt.conf'. An additional\n"
00230 "            function (cop,6) must be listed so that PTT control is available.\n"
00231 "\n"
00232 "        D - Dumb Phone Control mode. This allows a regular phone user to\n"
00233 "            have full control and audio access to the radio system. In this\n"
00234 "            mode, the PTT is activated for the entire length of the call.\n"
00235 "            For the user to have DTMF control (not generally recomended in\n"
00236 "            this mode), the 'dphone_functions' parameter must be specified\n"
00237 "            for the node in 'rpt.conf'. Otherwise no DTMF control will be\n"
00238 "            available to the phone user.\n"
00239 "\n";
00240 
00241 static int debug = 0;  /* Set this >0 for extra debug output */
00242 static int nrpts = 0;
00243 
00244 char *discstr = "!!DISCONNECT!!";
00245 static char *remote_rig_ft897="ft897";
00246 static char *remote_rig_rbi="rbi";
00247 
00248 struct   ast_config *cfg;
00249 
00250 STANDARD_LOCAL_USER;
00251 LOCAL_USER_DECL;
00252 
00253 #define  MSWAIT 200
00254 #define  HANGTIME 5000
00255 #define  TOTIME 180000
00256 #define  IDTIME 300000
00257 #define  MAXRPTS 20
00258 #define POLITEID 30000
00259 #define FUNCTDELAY 1500
00260 
00261 static  pthread_t rpt_master_thread;
00262 
00263 struct rpt;
00264 
00265 struct rpt_link
00266 {
00267    struct rpt_link *next;
00268    struct rpt_link *prev;
00269    char  mode;       /* 1 if in tx mode */
00270    char  isremote;
00271    char  phonemode;
00272    char  name[MAXNODESTR]; /* identifier (routing) string */
00273    char  lasttx;
00274    char  lastrx;
00275    char  connected;
00276    char  hasconnected;
00277    char  outbound;
00278    char  disced;
00279    char  killme;
00280    long  elaptime;
00281    long  disctime;
00282    long  retrytimer;
00283    long  retxtimer;
00284    int   retries;
00285    struct ast_channel *chan;  
00286    struct ast_channel *pchan; 
00287 } ;
00288 
00289 struct rpt_tele
00290 {
00291    struct rpt_tele *next;
00292    struct rpt_tele *prev;
00293    struct rpt *rpt;
00294    struct ast_channel *chan;
00295    int   mode;
00296    struct rpt_link mylink;
00297    char param[TELEPARAMSIZE];
00298    pthread_t threadid;
00299 } ;
00300 
00301 struct function_table_tag
00302 {
00303    char action[ACTIONSIZE];
00304    int (*function)(struct rpt *myrpt, char *param, char *digitbuf, 
00305       int command_source, struct rpt_link *mylink);
00306 } ;
00307 
00308 /* Used to store the morse code patterns */
00309 
00310 struct morse_bits
00311 {       
00312    int len;
00313    int ddcomb;
00314 } ;
00315 
00316 struct telem_defaults
00317 {
00318    char name[20];
00319    char value[80];
00320 } ;
00321 
00322 
00323 static struct rpt
00324 {
00325    char *name;
00326    ast_mutex_t lock;
00327    char *rxchanname;
00328    char *txchanname;
00329    char *ourcontext;
00330    char *ourcallerid;
00331    char *acctcode;
00332    char *ident;
00333    char *tonezone;
00334    char *functions;
00335    char *link_functions;
00336    char *phone_functions;
00337    char *dphone_functions;
00338    char *nodes;
00339    struct rpt_link links;
00340    int hangtime;
00341    int totime;
00342    int idtime;
00343    int unkeytocttimer;
00344    char keyed;
00345    char exttx;
00346    char localtx;
00347    char remoterx;
00348    char remotetx;
00349    char remoteon;
00350    char simple;
00351    char *remote;
00352    char tounkeyed;
00353    char tonotify;
00354    char enable;
00355    char dtmfbuf[MAXDTMF];
00356    char rem_dtmfbuf[MAXDTMF];
00357    char cmdnode[50];
00358    struct ast_channel *rxchannel,*txchannel;
00359    struct ast_channel *pchannel,*txpchannel, *remchannel;
00360    struct rpt_tele tele;
00361    pthread_t rpt_call_thread,rpt_thread;
00362    time_t rem_dtmf_time,dtmf_time_rem;
00363    int tailtimer,totimer,idtimer,txconf,conf,callmode,cidx,scantimer;
00364    int mustid;
00365    int politeid;
00366    int dtmfidx,rem_dtmfidx;
00367    long  retxtimer;
00368    char mydtmf;
00369    int iobase;
00370    char exten[AST_MAX_EXTENSION];
00371    char freq[MAXREMSTR],rxpl[MAXREMSTR],txpl[MAXREMSTR];
00372    char offset;
00373    char powerlevel;
00374    char txplon;
00375    char rxplon;
00376    char remmode;
00377    char tunerequest;
00378    char hfscanmode;
00379    int hfscanstatus;
00380    char lastlinknode[MAXNODESTR];
00381    char funcchar;
00382    char endchar;
00383    char stopgen;
00384    int phone_longestfunc;
00385    int dphone_longestfunc;
00386    int link_longestfunc;
00387    int longestfunc;
00388    int longestnode;
00389    int threadrestarts;     
00390    time_t disgorgetime;
00391    time_t lastthreadrestarttime;
00392    char  nobusyout;
00393 } rpt_vars[MAXRPTS]; 
00394 
00395 /*
00396 * CLI extensions
00397 */
00398 
00399 /* Debug mode */
00400 static int rpt_do_debug(int fd, int argc, char *argv[]);
00401 
00402 static char debug_usage[] =
00403 "Usage: rpt debug level {0-7}\n"
00404 "       Enables debug messages in app_rpt\n";
00405                                                                                                                                 
00406 static struct ast_cli_entry  cli_debug =
00407         { { "rpt", "debug", "level" }, rpt_do_debug, "Enable app_rpt debugging", debug_usage };
00408 
00409 
00410 
00411 /*
00412 * Telemetry defaults
00413 */
00414 
00415 
00416 static struct telem_defaults tele_defs[] = {
00417    {"ct1","|t(350,0,100,3072)(500,0,100,3072)(660,0,100,3072)"},
00418    {"ct2","|t(660,880,150,3072)"},
00419    {"ct3","|t(440,0,150,3072)"},
00420    {"ct4","|t(550,0,150,3072)"},
00421    {"ct5","|t(660,0,150,3072)"},
00422    {"ct6","|t(880,0,150,3072)"},
00423    {"ct7","|t(660,440,150,3072)"},
00424    {"ct8","|t(700,1100,150,3072)"},
00425    {"remotemon","|t(1600,0,75,2048)"},
00426    {"remotetx","|t(2000,0,75,2048)(0,0,75,0)(1600,0,75,2048)"},
00427    {"cmdmode","|t(900,904,200,2048)"},
00428    {"functcomplete","|t(1000,0,100,2048)(0,0,100,0)(1000,0,100,2048)"}
00429 } ;
00430 
00431 /*
00432 * Forward decl's - these suppress compiler warnings when funcs coded further down the file than thier invokation
00433 */
00434 
00435 static int setrbi(struct rpt *myrpt);
00436 
00437 
00438 
00439 /*
00440 * Define function protos for function table here
00441 */
00442 
00443 static int function_ilink(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
00444 static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
00445 static int function_autopatchdn(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
00446 static int function_status(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
00447 static int function_cop(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
00448 static int function_remote(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink);
00449 /*
00450 * Function table
00451 */
00452 
00453 static struct function_table_tag function_table[] = {
00454    {"cop", function_cop},
00455    {"autopatchup", function_autopatchup},
00456    {"autopatchdn", function_autopatchdn},
00457    {"ilink", function_ilink},
00458    {"status", function_status},
00459    {"remote", function_remote}
00460 } ;
00461    
00462 static int myatoi(char *str)
00463 {
00464 int   ret;
00465 
00466    if (str == NULL) return -1;
00467    /* leave this %i alone, non-base-10 input is useful here */
00468    if (sscanf(str,"%i",&ret) != 1) return -1;
00469    return ret;
00470 }
00471 
00472 /*
00473 * Enable or disable debug output at a given level at the console
00474 */
00475                                                                                                                                  
00476 static int rpt_do_debug(int fd, int argc, char *argv[])
00477 {
00478    int newlevel;
00479 
00480         if (argc != 4)
00481                 return RESULT_SHOWUSAGE;
00482         newlevel = myatoi(argv[3]);
00483         if((newlevel < 0) || (newlevel > 7))
00484                 return RESULT_SHOWUSAGE;
00485         if(newlevel)
00486                 ast_cli(fd, "app_rpt Debugging enabled, previous level: %d, new level: %d\n", debug, newlevel);
00487         else
00488                 ast_cli(fd, "app_rpt Debugging disabled\n");
00489 
00490         debug = newlevel;                                                                                                                          
00491         return RESULT_SUCCESS;
00492 }
00493                                                                                                                                  
00494 
00495 
00496 static int play_tone_pair(struct ast_channel *chan, int f1, int f2, int duration, int amplitude)
00497 {
00498    int res;
00499 
00500         if ((res = ast_tonepair_start(chan, f1, f2, duration, amplitude)))
00501                 return res;
00502                                                                                                                                             
00503         while(chan->generatordata) {
00504       if (ast_safe_sleep(chan,1)) return -1;
00505    }
00506 
00507         return 0;
00508 }
00509 
00510 static int play_tone(struct ast_channel *chan, int freq, int duration, int amplitude)
00511 {
00512    return play_tone_pair(chan, freq, 0, duration, amplitude);
00513 }
00514 
00515 static int play_silence(struct ast_channel *chan, int duration)
00516 {
00517    return play_tone_pair(chan, 0, 0, duration, 0);
00518 }
00519 
00520 
00521 static int send_morse(struct ast_channel *chan, char *string, int speed, int freq, int amplitude)
00522 {
00523 
00524 static struct morse_bits mbits[] = {
00525       {0, 0}, /* SPACE */
00526       {0, 0}, 
00527       {6, 18},/* " */
00528       {0, 0},
00529       {7, 72},/* $ */
00530       {0, 0},
00531       {0, 0},
00532       {6, 30},/* ' */
00533       {5, 13},/* ( */
00534       {6, 29},/* ) */
00535       {0, 0},
00536       {5, 10},/* + */
00537       {6, 51},/* , */
00538       {6, 33},/* - */
00539       {6, 42},/* . */
00540       {5, 9}, /* / */
00541       {5, 31},/* 0 */
00542       {5, 30},/* 1 */
00543       {5, 28},/* 2 */
00544       {5, 24},/* 3 */
00545       {5, 16},/* 4 */
00546       {5, 0}, /* 5 */
00547       {5, 1}, /* 6 */
00548       {5, 3}, /* 7 */
00549       {5, 7}, /* 8 */
00550       {5, 15},/* 9 */
00551       {6, 7}, /* : */
00552       {6, 21},/* ; */
00553       {0, 0},
00554       {5, 33},/* = */
00555       {0, 0},
00556       {6, 12},/* ? */
00557       {0, 0},
00558          {2, 2}, /* A */
00559       {4, 1}, /* B */
00560       {4, 5}, /* C */
00561       {3, 1}, /* D */
00562       {1, 0}, /* E */
00563       {4, 4}, /* F */
00564       {3, 3}, /* G */
00565       {4, 0}, /* H */
00566       {2, 0}, /* I */
00567       {4, 14},/* J */
00568       {3, 5}, /* K */
00569       {4, 2}, /* L */
00570       {2, 3}, /* M */
00571       {2, 1}, /* N */
00572       {3, 7}, /* O */
00573       {4, 6}, /* P */
00574       {4, 11},/* Q */
00575       {3, 2}, /* R */
00576       {3, 0}, /* S */
00577       {1, 1}, /* T */
00578       {3, 4}, /* U */
00579       {4, 8}, /* V */
00580       {3, 6}, /* W */
00581       {4, 9}, /* X */
00582       {4, 13},/* Y */
00583       {4, 3}  /* Z */
00584    };
00585 
00586 
00587    int dottime;
00588    int dashtime;
00589    int intralettertime;
00590    int interlettertime;
00591    int interwordtime;
00592    int len, ddcomb;
00593    int res;
00594    int c;
00595    int i;
00596    int flags;
00597          
00598    res = 0;
00599    
00600    /* Approximate the dot time from the speed arg. */
00601    
00602    dottime = 900/speed;
00603    
00604    /* Establish timing releationships */
00605    
00606    dashtime = 3 * dottime;
00607    intralettertime = dottime;
00608    interlettertime = dottime * 4 ;
00609    interwordtime = dottime * 7;
00610    
00611    for(;(*string) && (!res); string++){
00612    
00613       c = *string;
00614       
00615       /* Convert lower case to upper case */
00616       
00617       if((c >= 'a') && (c <= 'z'))
00618          c -= 0x20;
00619       
00620       /* Can't deal with any char code greater than Z, skip it */
00621       
00622       if(c  > 'Z')
00623          continue;
00624       
00625       /* If space char, wait the inter word time */
00626                
00627       if(c == ' '){
00628          if(!res)
00629             res = play_silence(chan, interwordtime);
00630          continue;
00631       }
00632       
00633       /* Subtract out control char offset to match our table */
00634       
00635       c -= 0x20;
00636       
00637       /* Get the character data */
00638       
00639       len = mbits[c].len;
00640       ddcomb = mbits[c].ddcomb;
00641       
00642       /* Send the character */
00643       
00644       for(; len ; len--){
00645          if(!res)
00646             res = play_tone(chan, freq, (ddcomb & 1) ? dashtime : dottime, amplitude);
00647          if(!res)
00648             res = play_silence(chan, intralettertime);
00649          ddcomb >>= 1;
00650       }
00651       
00652       /* Wait the interletter time */
00653       
00654       if(!res)
00655          res = play_silence(chan, interlettertime - intralettertime);
00656    }
00657    
00658    /* Wait for all the frames to be sent */
00659    
00660    if (!res) 
00661       res = ast_waitstream(chan, "");
00662    ast_stopstream(chan);
00663    
00664    /*
00665    * Wait for the zaptel driver to physically write the tone blocks to the hardware
00666    */
00667 
00668    for(i = 0; i < 20 ; i++){
00669       flags =  ZT_IOMUX_WRITEEMPTY | ZT_IOMUX_NOWAIT; 
00670       res = ioctl(chan->fds[0], ZT_IOMUX, &flags);
00671       if(flags & ZT_IOMUX_WRITEEMPTY)
00672          break;
00673       if( ast_safe_sleep(chan, 50)){
00674          res = -1;
00675          break;
00676       }
00677    }
00678 
00679    
00680    return res;
00681 }
00682 
00683 static int send_tone_telemetry(struct ast_channel *chan, char *tonestring)
00684 {
00685    char *stringp;
00686    char *tonesubset;
00687    int f1,f2;
00688    int duration;
00689    int amplitude;
00690    int res;
00691    int i;
00692    int flags;
00693    
00694    res = 0;
00695    
00696    stringp = ast_strdupa(tonestring);
00697 
00698    for(;tonestring;){
00699       tonesubset = strsep(&stringp,")");
00700       if(!tonesubset)
00701          break;
00702       if(sscanf(tonesubset,"(%d,%d,%d,%d", &f1, &f2, &duration, &amplitude) != 4)
00703          break;
00704       res = play_tone_pair(chan, f1, f2, duration, amplitude);
00705       if(res)
00706          break;
00707    }
00708    if(!res)
00709       res = play_tone_pair(chan, 0, 0, 100, 0); /* This is needed to ensure the last tone segment is timed correctly */
00710    
00711    if (!res) 
00712       res = ast_waitstream(chan, "");
00713    ast_stopstream(chan);
00714 
00715    /*
00716    * Wait for the zaptel driver to physically write the tone blocks to the hardware
00717    */
00718 
00719    for(i = 0; i < 20 ; i++){
00720       flags =  ZT_IOMUX_WRITEEMPTY | ZT_IOMUX_NOWAIT; 
00721       res = ioctl(chan->fds[0], ZT_IOMUX, &flags);
00722       if(flags & ZT_IOMUX_WRITEEMPTY)
00723          break;
00724       if( ast_safe_sleep(chan, 50)){
00725          res = -1;
00726          break;
00727       }
00728    }
00729       
00730    return res;
00731       
00732 }
00733    
00734 
00735 static int sayfile(struct ast_channel *mychannel,char *fname)
00736 {
00737 int   res;
00738 
00739    res = ast_streamfile(mychannel, fname, mychannel->language);
00740    if (!res) 
00741       res = ast_waitstream(mychannel, "");
00742    else
00743        ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
00744    ast_stopstream(mychannel);
00745    return res;
00746 }
00747 
00748 static int saycharstr(struct ast_channel *mychannel,char *str)
00749 {
00750 int   res;
00751 
00752    res = ast_say_character_str(mychannel,str,NULL,mychannel->language);
00753    if (!res) 
00754       res = ast_waitstream(mychannel, "");
00755    else
00756        ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
00757    ast_stopstream(mychannel);
00758    return res;
00759 }
00760 
00761 static int saynum(struct ast_channel *mychannel, int num)
00762 {
00763    int res;
00764    res = ast_say_number(mychannel, num, NULL, mychannel->language, NULL);
00765    if(!res)
00766       res = ast_waitstream(mychannel, "");
00767    else
00768       ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
00769    ast_stopstream(mychannel);
00770    return res;
00771 }
00772 
00773 
00774 /* Retrieve an int from a config file */
00775                                                                                 
00776 static int retrieve_astcfgint(char *category, char *name, int min, int max, int defl)
00777 {
00778         char *var;
00779         int ret;
00780                                                                                 
00781         var = ast_variable_retrieve(cfg, category, name);
00782         if(var){
00783                 ret = myatoi(var);
00784                 if(ret < min)
00785                         ret = min;
00786                 if(ret > max)
00787                         ret = max;
00788         }
00789         else
00790                 ret = defl;
00791         return ret;
00792 }
00793 
00794 static int telem_any(struct ast_channel *chan, char *entry)
00795 {
00796    int res;
00797    char c;
00798    
00799    static int morsespeed;
00800    static int morsefreq;
00801    static int morseampl;
00802    static int morseidfreq = 0;
00803    static int morseidampl;
00804    static char mcat[] = MORSE;
00805    
00806    res = 0;
00807    
00808    if(!morseidfreq){ /* Get the morse parameters if not already loaded */
00809       morsespeed = retrieve_astcfgint( mcat, "speed", 5, 20, 20);
00810          morsefreq = retrieve_astcfgint( mcat, "frequency", 300, 3000, 800);
00811          morseampl = retrieve_astcfgint( mcat, "amplitude", 200, 8192, 4096);
00812       morseidampl = retrieve_astcfgint( mcat, "idamplitude", 200, 8192, 2048);
00813       morseidfreq = retrieve_astcfgint( mcat, "idfrequency", 300, 3000, 330); 
00814    }
00815    
00816    /* Is it a file, or a tone sequence? */
00817          
00818    if(entry[0] == '|'){
00819       c = entry[1];
00820       if((c >= 'a')&&(c <= 'z'))
00821          c -= 0x20;
00822    
00823       switch(c){
00824          case 'I': /* Morse ID */
00825             res = send_morse(chan, entry + 2, morsespeed, morseidfreq, morseidampl);
00826             break;
00827          
00828          case 'M': /* Morse Message */
00829             res = send_morse(chan, entry + 2, morsespeed, morsefreq, morseampl);
00830             break;
00831          
00832          case 'T': /* Tone sequence */
00833             res = send_tone_telemetry(chan, entry + 2);
00834             break;
00835          default:
00836             res = -1;
00837       }
00838    }
00839    else
00840       res = sayfile(chan, entry); /* File */
00841    return res;
00842 }
00843 
00844 /*
00845 * This function looks up a telemetry name in the config file, and does a telemetry response as configured.
00846 *
00847 * 4 types of telemtry are handled: Morse ID, Morse Message, Tone Sequence, and a File containing a recording.
00848 */
00849 
00850 static int telem_lookup(struct ast_channel *chan, char *node, char *name)
00851 {
00852    
00853    int res;
00854    int i;
00855    char *entry;
00856    char *telemetry;
00857    char *telemetry_save;
00858 
00859    res = 0;
00860    telemetry_save = NULL;
00861    entry = NULL;
00862    
00863    
00864    /* Retrieve the section name for telemetry from the node section */
00865    
00866    telemetry = ast_variable_retrieve(cfg, node, TELEMETRY);
00867    if(telemetry){
00868       telemetry_save = ast_strdupa(telemetry);
00869       if(!telemetry_save){
00870          ast_log(LOG_WARNING,"ast_strdupa() failed in telem_lookup()\n");
00871          return res;
00872       }
00873       entry = ast_variable_retrieve(cfg, telemetry_save, name);
00874    }
00875    
00876    /* Try to look up the telemetry name */
00877    
00878    if(!entry){
00879       /* Telemetry name wasn't found in the config file, use the default */
00880       for(i = 0; i < sizeof(tele_defs)/sizeof(struct telem_defaults) ; i++){
00881          if(!strcasecmp(tele_defs[i].name, name))
00882             entry = tele_defs[i].value;
00883       }
00884    }
00885    if(entry)   
00886       telem_any(chan, entry);
00887    else{
00888       ast_log(LOG_WARNING, "Telemetry name not found: %s\n", name);
00889       res = -1;
00890    }
00891    return res;
00892 }
00893 
00894 /*
00895 * Retrieve a wait interval
00896 */
00897 
00898 static int get_wait_interval(struct rpt *myrpt, int type)
00899 {
00900    int interval;
00901    char *wait_times;
00902    char *wait_times_save = NULL;
00903 
00904    wait_times = ast_variable_retrieve(cfg, myrpt->name, "wait_times");
00905 
00906    if (wait_times) {
00907       wait_times_save = ast_strdupa(wait_times);
00908       if (!wait_times_save) {
00909          ast_log(LOG_WARNING, "Out of memory in wait_interval()\n");
00910          wait_times = NULL;
00911       }
00912    }
00913 
00914    switch (type) {
00915    case DLY_TELEM:
00916       if (wait_times)
00917          interval = retrieve_astcfgint(wait_times_save, "telemwait", 500, 5000, 1000);
00918       else
00919          interval = 1000;
00920       break;
00921 
00922    case DLY_ID:
00923       if (wait_times)
00924          interval = retrieve_astcfgint(wait_times_save, "idwait", 250, 5000, 500);
00925       else
00926          interval = 500;
00927       break;
00928 
00929    case DLY_UNKEY:
00930       if (wait_times)
00931          interval = retrieve_astcfgint(wait_times_save, "unkeywait", 500, 5000, 1000);
00932       else
00933          interval = 1000;
00934       break;
00935 
00936    case DLY_CALLTERM:
00937       if (wait_times)
00938          interval = retrieve_astcfgint(wait_times_save, "calltermwait", 500, 5000, 1500);
00939       else
00940          interval = 1500;
00941       break;
00942 
00943    default:
00944       return 0;
00945    }
00946    return interval;
00947 }                                           
00948 
00949 
00950 /*
00951 * Wait a configurable interval of time 
00952 */
00953 
00954 
00955 static void wait_interval(struct rpt *myrpt, int type, struct ast_channel *chan)
00956 {
00957    int interval;
00958    if((interval = get_wait_interval(myrpt, type)))
00959       ast_safe_sleep(chan,interval);
00960    return;
00961 }
00962 
00963 
00964 static void *rpt_tele_thread(void *this)
00965 {
00966 ZT_CONFINFO ci;  /* conference info */
00967 int   res = 0,haslink,hastx,hasremote,imdone = 0, unkeys_queued, x;
00968 struct   rpt_tele *mytele = (struct rpt_tele *)this;
00969 struct  rpt_tele *tlist;
00970 struct   rpt *myrpt;
00971 struct   rpt_link *l,*m,linkbase;
00972 struct   ast_channel *mychannel;
00973 int vmajor, vminor;
00974 char *p,*ct,*ct_copy,*ident, *nodename;
00975 time_t t;
00976 struct tm localtm;
00977 
00978 
00979    /* get a pointer to myrpt */
00980    myrpt = mytele->rpt;
00981 
00982    /* Snag copies of a few key myrpt variables */
00983    ast_mutex_lock(&myrpt->lock);
00984    nodename = ast_strdupa(myrpt->name);
00985    ident = ast_strdupa(myrpt->ident);
00986    ast_mutex_unlock(&myrpt->lock);
00987    
00988    
00989    /* allocate a pseudo-channel thru asterisk */
00990    mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL);
00991    if (!mychannel)
00992    {
00993       fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
00994       ast_mutex_lock(&myrpt->lock);
00995       remque((struct qelem *)mytele);
00996       ast_mutex_unlock(&myrpt->lock);
00997       free(mytele);     
00998       pthread_exit(NULL);
00999    }
01000    ast_mutex_lock(&myrpt->lock);
01001    mytele->chan = mychannel; /* Save a copy of the channel so we can access it externally if need be */
01002    ast_mutex_unlock(&myrpt->lock);
01003    
01004    /* make a conference for the tx */
01005    ci.chan = 0;
01006    /* If there's an ID queued, only connect the ID audio to the local tx conference so 
01007       linked systems can't hear it */
01008    ci.confno = (((mytele->mode == ID) || (mytele->mode == IDTALKOVER) || (mytele->mode == UNKEY)) ?
01009        myrpt->txconf : myrpt->conf);
01010    ci.confmode = ZT_CONF_CONFANN;
01011    /* first put the channel on the conference in announce mode */
01012    if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1)
01013    {
01014       ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
01015       ast_mutex_lock(&myrpt->lock);
01016       remque((struct qelem *)mytele);
01017       ast_mutex_unlock(&myrpt->lock);
01018       free(mytele);     
01019       ast_hangup(mychannel);
01020       pthread_exit(NULL);
01021    }
01022    ast_stopstream(mychannel);
01023    switch(mytele->