From 2ee28d0ccf59240a3dbd9a1040a6b877371db730 Mon Sep 17 00:00:00 2001 From: Jim Dixon <telesistant@hotmail.com> Date: Mon, 21 Jun 2004 04:24:50 +0000 Subject: [PATCH] Majorly updated app_rpt.c allowing linking of repeaters/remote bases using (IAX2) and supporting remote base nodes as well (also added visual documentation in rpt_flow.pdf) git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@3252 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- apps/app_rpt.c | 1873 +++++++++++++++++++++++++++++++++------------ apps/rpt_flow.pdf | Bin 0 -> 51935 bytes 2 files changed, 1384 insertions(+), 489 deletions(-) create mode 100755 apps/rpt_flow.pdf diff --git a/apps/app_rpt.c b/apps/app_rpt.c index e6973725c8..a4992cb1e3 100755 --- a/apps/app_rpt.c +++ b/apps/app_rpt.c @@ -3,11 +3,11 @@ * Asterisk -- A telephony toolkit for Linux. * * Radio Repeater / Remote Base program - * version 0.2 5/30/04 + * version 0.4 6/19/04 * - * Copyright (C) 2002-2004, Jim Dixon + * Copyright (C) 2002-2004, Jim Dixon, WB6NIL * - * Jim Dixon <jim@lambdatel.com> + * Jim Dixon, WB6NIL <jim@lambdatel.com> * * This program is free software, distributed under the terms of * the GNU General Public License @@ -15,10 +15,13 @@ * Repeater / Remote Functions: * "Simple" Mode: * - autopatch access, # - autopatch hangup * Normal mode: - * *0 - autopatch access - * *1 - remote base off - * *2 - remote base monitor - * *3 - remote base tranceive + * *0 - autopatch off + * *1XXX - remote link off + * *2XXX - remote link monitor + * *3XXX - remote link tranceive + * *4XXX - remote link command mode + * *6 - autopatch access/send (*) + * *7 - system status * *8 - force ID * *9 - system reset * @@ -27,29 +30,44 @@ */ /* number of digits for function after *. Must be at least 1 */ -#define FUNCTION_LEN 1 +#define FUNCTION_LEN 4 +/* string containing all of the 1 digit functions */ +#define SHORTFUNCS "056789" /* maximum digits in DTMF buffer, and seconds after * for DTMF command timeout */ #define MAXDTMF 10 #define DTMF_TIMEOUT 3 +#define NODES "nodes" + +#define MAXCONNECTTIME 5000 + +#define MAXNODESTR 300 + enum {REM_OFF,REM_MONITOR,REM_TX}; +enum{ID,PROC,TERM,COMPLETE,UNKEY,REMDISC,REMALREADY,REMNOTFOUND,REMGO, + CONNECTED,CONNFAIL,STATUS}; + +#include <pthread.h> #include <asterisk/lock.h> #include <asterisk/file.h> #include <asterisk/logger.h> #include <asterisk/channel.h> +#include <asterisk/callerid.h> #include <asterisk/pbx.h> #include <asterisk/module.h> #include <asterisk/translate.h> #include <asterisk/options.h> #include <asterisk/config.h> +#include <asterisk/utils.h> +#include <asterisk/say.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> -#include <pthread.h> +#include <search.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> @@ -61,15 +79,21 @@ enum {REM_OFF,REM_MONITOR,REM_TX}; #include <sys/ioctl.h> #include <math.h> #include <tonezone.h> - -#ifdef __linux__ #include <linux/zaptel.h> -#else -#include <zaptel.h> -#endif /* __linux__ */ -static char *tdesc = "Radio Repeater / Remote Base version 0.2 05/30/2004"; +static char *tdesc = "Radio Repeater / Remote Base version 0.3 06/18/2004"; +static char *app = "Rpt"; + +static char *synopsis = "Radio Repeater/Remote Base Control System"; + +static char *descrip = +" Rpt(sysname): Radio Remote Link or Remote Base Link Endpoint Process.\n"; + static int debug = 0; +static int nrpts = 0; + +struct ast_config *cfg; + STANDARD_LOCAL_USER; LOCAL_USER_DECL; @@ -81,236 +105,351 @@ LOCAL_USER_DECL; static pthread_t rpt_master_thread; +struct rpt; + +struct rpt_link +{ + struct rpt_link *next; + struct rpt_link *prev; + char mode; /* 1 if in tx mode */ + char isremote; + char name[MAXNODESTR]; /* identifier (routing) string */ + char lasttx; + char lastrx; + char connected; + char outbound; + long elaptime; + struct ast_channel *chan; + struct ast_channel *pchan; +} ; + +struct rpt_tele +{ + struct rpt_tele *next; + struct rpt_tele *prev; + struct rpt *rpt; + int mode; + struct rpt_link mylink; + pthread_t threadid; +} ; + static struct rpt { char *name; + ast_mutex_t lock; char *rxchanname; char *txchanname; - char *rem_rxchanname; - char *rem_txchanname; char *ourcontext; char *ourcallerid; char *acctcode; - char *idrecording; + char *ident; char *tonezone; + struct rpt_link links; int hangtime; int totime; int idtime; + char exttx; + char localtx; char remoterx; char remotetx; - char remotemode; + char remoteon; char simple; - struct ast_channel *rxchannel,*txchannel,*rem_rxchannel; - struct ast_channel *rem_txchannel,*pchannel; - int tailtimer,totimer,idtimer,txconf,pconf,callmode,cidx; - pthread_t rpt_id_thread,rpt_term_thread,rpt_proc_thread,rpt_call_thread; - char mydtmf,iding,terming,teleing,comping,procing; + char remote; + char dtmfbuf[MAXDTMF]; + char rem_dtmfbuf[MAXDTMF]; + char cmdnode[50]; + struct ast_channel *rxchannel,*txchannel; + struct ast_channel *pchannel,*txpchannel; + struct rpt_tele tele; + pthread_t rpt_call_thread,rpt_thread; + time_t rem_dtmf_time; + int tailtimer,totimer,idtimer,txconf,conf,callmode,cidx; + int dtmfidx,rem_dtmfidx; + char mydtmf; char exten[AST_MAX_EXTENSION]; } rpt_vars[MAXRPTS]; - -static void *rpt_id(void *this) -{ -ZT_CONFINFO ci; /* conference info */ -int res; -struct rpt *myrpt = (struct rpt *)this; -struct ast_channel *mychannel; - - /* allocate a pseudo-channel thru asterisk */ - mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo"); - if (!mychannel) - { - fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); - pthread_exit(NULL); - } - /* make a conference for the tx */ - ci.chan = 0; - ci.confno = myrpt->txconf; /* use the tx conference */ - ci.confmode = ZT_CONF_CONFANN; - /* first put the channel on the conference in announce mode */ - if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1) - { - ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); - pthread_exit(NULL); - } - myrpt->iding = 1; - ast_stopstream(mychannel); - res = ast_streamfile(mychannel, myrpt->idrecording, mychannel->language); - if (!res) - res = ast_waitstream(mychannel, ""); - else { - ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); - res = 0; - } - myrpt->iding = 0; - ast_stopstream(mychannel); - ast_hangup(mychannel); - pthread_exit(NULL); -} - -static void *rpt_proc(void *this) -{ -ZT_CONFINFO ci; /* conference info */ -int res; -struct rpt *myrpt = (struct rpt *)this; -struct ast_channel *mychannel; - - /* wait a little bit */ - usleep(1500000); - /* allocate a pseudo-channel thru asterisk */ - mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo"); - if (!mychannel) - { - fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); - pthread_exit(NULL); - } - /* make a conference for the tx */ - ci.chan = 0; - ci.confno = myrpt->txconf; /* use the tx conference */ - ci.confmode = ZT_CONF_CONFANN; - /* first put the channel on the conference in announce mode */ - if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1) - { - ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); - pthread_exit(NULL); - } - myrpt->procing = 1; - ast_stopstream(mychannel); - res = ast_streamfile(mychannel, "callproceeding", mychannel->language); - if (!res) - res = ast_waitstream(mychannel, ""); - else { - ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); - res = 0; - } - myrpt->procing = 0; - ast_stopstream(mychannel); - ast_hangup(mychannel); - pthread_exit(NULL); -} - -static void *rpt_term(void *this) +static void *rpt_tele_thread(void *this) { ZT_CONFINFO ci; /* conference info */ -int res; -struct rpt *myrpt = (struct rpt *)this; -struct ast_channel *mychannel; +int res = 0,hastx,imdone = 0; +struct rpt_tele *mytele = (struct rpt_tele *)this; +struct rpt *myrpt; +struct rpt_link *l,*m,linkbase; +struct ast_channel *mychannel; - /* wait a little bit */ - usleep(1500000); + /* get a pointer to myrpt */ + myrpt = mytele->rpt; /* allocate a pseudo-channel thru asterisk */ mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo"); if (!mychannel) { fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); + ast_mutex_lock(&myrpt->lock); + remque((struct qelem *)mytele); + ast_mutex_unlock(&myrpt->lock); + free(mytele); pthread_exit(NULL); } /* make a conference for the tx */ ci.chan = 0; - ci.confno = myrpt->txconf; /* use the tx conference */ + ci.confno = myrpt->conf; /* use the tx conference */ ci.confmode = ZT_CONF_CONFANN; /* first put the channel on the conference in announce mode */ if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1) { ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + ast_mutex_lock(&myrpt->lock); + remque((struct qelem *)mytele); + ast_mutex_unlock(&myrpt->lock); + free(mytele); + ast_hangup(mychannel); pthread_exit(NULL); } - myrpt->terming = 1; - ast_stopstream(mychannel); - res = ast_streamfile(mychannel, "callterminated", mychannel->language); - if (!res) - res = ast_waitstream(mychannel, ""); - else { - ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); - res = 0; - } - myrpt->terming = 0; ast_stopstream(mychannel); - ast_hangup(mychannel); - pthread_exit(NULL); -} - -static void *rpt_complete(void *this) -{ -ZT_CONFINFO ci; /* conference info */ -int res; -struct rpt *myrpt = (struct rpt *)this; -struct ast_channel *mychannel; - - /* wait a little bit */ - usleep(1000000); - /* allocate a pseudo-channel thru asterisk */ - mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo"); - if (!mychannel) + switch(mytele->mode) { - fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); - pthread_exit(NULL); + case ID: + res = ast_streamfile(mychannel, myrpt->ident, mychannel->language); + break; + case PROC: + /* wait a little bit longer */ + usleep(1500000); + res = ast_streamfile(mychannel, "rpt/callproceeding", mychannel->language); + break; + case TERM: + /* wait a little bit longer */ + usleep(1500000); + res = ast_streamfile(mychannel, "rpt/callterminated", mychannel->language); + break; + case COMPLETE: + /* wait a little bit */ + usleep(1000000); + res = ast_streamfile(mychannel, "rpt/functioncomplete", mychannel->language); + break; + case UNKEY: + /* wait a little bit */ + usleep(1000000); + hastx = 0; + l = myrpt->links.next; + if (l != &myrpt->links) + { + ast_mutex_lock(&myrpt->lock); + while(l != &myrpt->links) + { + if (l->mode) hastx++; + l = l->next; + } + ast_mutex_unlock(&myrpt->lock); + res = ast_streamfile(mychannel, + ((!hastx) ? "rpt/remote_monitor" : "rpt/remote_tx"), + mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + } + /* if in remote cmd mode, indicate it */ + if (myrpt->cmdnode[0]) + { + ast_safe_sleep(mychannel,200); + res = ast_streamfile(mychannel, "rpt/remote_cmd", mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + } + imdone = 1; + break; + case REMDISC: + /* wait a little bit */ + usleep(1000000); + res = ast_streamfile(mychannel, "rpt/node", mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + ast_say_character_str(mychannel,mytele->mylink.name,NULL,mychannel->language); + res = ast_streamfile(mychannel, ((mytele->mylink.connected) ? + "rpt/remote_disc" : "rpt/remote_busy"), mychannel->language); + break; + case REMALREADY: + /* wait a little bit */ + usleep(1000000); + res = ast_streamfile(mychannel, "rpt/remote_already", mychannel->language); + break; + case REMNOTFOUND: + /* wait a little bit */ + usleep(1000000); + res = ast_streamfile(mychannel, "rpt/remote_notfound", mychannel->language); + break; + case REMGO: + /* wait a little bit */ + usleep(1000000); + res = ast_streamfile(mychannel, "rpt/remote_go", mychannel->language); + break; + case CONNECTED: + /* wait a little bit */ + usleep(1000000); + res = ast_streamfile(mychannel, "rpt/node", mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + ast_say_character_str(mychannel,mytele->mylink.name,NULL,mychannel->language); + res = ast_streamfile(mychannel, "rpt/connected", mychannel->language); + break; + case CONNFAIL: + res = ast_streamfile(mychannel, "rpt/node", mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + ast_say_character_str(mychannel,mytele->mylink.name,NULL,mychannel->language); + res = ast_streamfile(mychannel, "rpt/connection_failed", mychannel->language); + break; + case STATUS: + /* wait a little bit */ + usleep(1000000); + hastx = 0; + linkbase.next = &linkbase; + linkbase.prev = &linkbase; + ast_mutex_lock(&myrpt->lock); + /* make our own list of links */ + l = myrpt->links.next; + while(l != &myrpt->links) + { + m = malloc(sizeof(struct rpt_link)); + if (!m) + { + ast_log(LOG_WARNING, "Cannot alloc memory on %s\n", mychannel->name); + pthread_exit(NULL); + } + memcpy(m,l,sizeof(struct rpt_link)); + m->next = m->prev = NULL; + insque((struct qelem *)m,(struct qelem *)linkbase.next); + l = l->next; + } + ast_mutex_unlock(&myrpt->lock); + res = ast_streamfile(mychannel, "rpt/node", mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + ast_say_character_str(mychannel,myrpt->name,NULL,mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + if (myrpt->callmode) + { + hastx = 1; + res = ast_streamfile(mychannel, "rpt/autopatch_on", mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + } + l = linkbase.next; + while(l != &linkbase) + { + hastx = 1; + res = ast_streamfile(mychannel, "rpt/node", mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + ast_say_character_str(mychannel,l->name,NULL,mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + res = ast_streamfile(mychannel, ((l->mode) ? + "rpt/tranceive" : "rpt/monitor"), mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + l = l->next; + } + if (!hastx) + { + res = ast_streamfile(mychannel, "rpt/repeat_only", mychannel->language); + if (!res) + res = ast_waitstream(mychannel, ""); + else + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + ast_stopstream(mychannel); + } + /* destroy our local link queue */ + l = linkbase.next; + while(l != &linkbase) + { + m = l; + l = l->next; + remque((struct qelem *)m); + free(m); + } + imdone = 1; + break; } - /* make a conference for the tx */ - ci.chan = 0; - ci.confno = myrpt->txconf; /* use the tx conference */ - ci.confmode = ZT_CONF_CONFANN; - /* first put the channel on the conference in announce mode */ - if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1) + if (!imdone) { - ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); - pthread_exit(NULL); - } - myrpt->comping = 1; - ast_stopstream(mychannel); - res = ast_streamfile(mychannel, "functioncomplete", mychannel->language); - if (!res) - res = ast_waitstream(mychannel, ""); - else { - ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); - res = 0; + if (!res) + res = ast_waitstream(mychannel, ""); + else { + ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); + res = 0; + } } - myrpt->comping = 0; ast_stopstream(mychannel); + ast_mutex_lock(&myrpt->lock); + remque((struct qelem *)mytele); + ast_mutex_unlock(&myrpt->lock); + free(mytele); ast_hangup(mychannel); pthread_exit(NULL); } -static void *rpt_remote_telemetry(void *this) +static void rpt_telemetry(struct rpt *myrpt,int mode,struct rpt_link *mylink) { -ZT_CONFINFO ci; /* conference info */ -int res; -struct rpt *myrpt = (struct rpt *)this; -struct ast_channel *mychannel; +struct rpt_tele *tele; +pthread_attr_t attr; - /* wait a little bit */ - usleep(1000000); - /* allocate a pseudo-channel thru asterisk */ - mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo"); - if (!mychannel) + tele = malloc(sizeof(struct rpt_tele)); + if (!tele) { - fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); + ast_log(LOG_WARNING, "Unable to allocate memory\n"); pthread_exit(NULL); + return; } - /* make a conference for the tx */ - ci.chan = 0; - ci.confno = myrpt->txconf; /* use the tx conference */ - ci.confmode = ZT_CONF_CONFANN; - /* first put the channel on the conference in announce mode */ - if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1) + /* zero it out */ + memset((char *)tele,0,sizeof(struct rpt_tele)); + tele->rpt = myrpt; + tele->mode = mode; + ast_mutex_lock(&myrpt->lock); + memset(&tele->mylink,0,sizeof(struct rpt_link)); + if (mylink) { - ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); - pthread_exit(NULL); - } - myrpt->teleing = 1; - ast_stopstream(mychannel); - res = ast_streamfile(mychannel, - ((myrpt->remotemode == REM_MONITOR) ? "remote_monitor" : "remote_tx"), - mychannel->language); - if (!res) - res = ast_waitstream(mychannel, ""); - else { - ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name); - res = 0; - } - myrpt->teleing = 0; - ast_hangup(mychannel); - pthread_exit(NULL); + memcpy(&tele->mylink,mylink,sizeof(struct rpt_link)); + } + insque((struct qelem *)tele,(struct qelem *)myrpt->tele.next); + ast_mutex_unlock(&myrpt->lock); + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&tele->threadid,&attr,rpt_tele_thread,(void *) tele); + return; } static void *rpt_call(void *this) @@ -331,7 +470,7 @@ struct ast_channel *mychannel,*genchannel; pthread_exit(NULL); } ci.chan = 0; - ci.confno = myrpt->pconf; /* use the pseudo conference */ + ci.confno = myrpt->conf; /* use the pseudo conference */ ci.confmode = ZT_CONF_REALANDPSEUDO | ZT_CONF_TALKER | ZT_CONF_LISTENER | ZT_CONF_PSEUDO_TALKER | ZT_CONF_PSEUDO_LISTENER; /* first put the channel on the conference */ @@ -351,7 +490,7 @@ struct ast_channel *mychannel,*genchannel; pthread_exit(NULL); } ci.chan = 0; - ci.confno = myrpt->pconf; + ci.confno = myrpt->conf; ci.confmode = ZT_CONF_REALANDPSEUDO | ZT_CONF_TALKER | ZT_CONF_LISTENER | ZT_CONF_PSEUDO_TALKER | ZT_CONF_PSEUDO_LISTENER; /* first put the channel on the conference */ @@ -410,7 +549,9 @@ struct ast_channel *mychannel,*genchannel; { ast_hangup(mychannel); ast_hangup(genchannel); + ast_mutex_lock(&myrpt->lock); myrpt->callmode = 0; + ast_mutex_unlock(&myrpt->lock); pthread_exit(NULL); } if (res == 0) continue; @@ -419,7 +560,9 @@ struct ast_channel *mychannel,*genchannel; { ast_hangup(mychannel); ast_hangup(genchannel); + ast_mutex_lock(&myrpt->lock); myrpt->callmode = 0; + ast_mutex_unlock(&myrpt->lock); pthread_exit(NULL); } if ((f->frametype == AST_FRAME_CONTROL) && @@ -428,7 +571,9 @@ struct ast_channel *mychannel,*genchannel; ast_frfree(f); ast_hangup(mychannel); ast_hangup(genchannel); + ast_mutex_lock(&myrpt->lock); myrpt->callmode = 0; + ast_mutex_unlock(&myrpt->lock); pthread_exit(NULL); } ast_frfree(f); @@ -440,7 +585,9 @@ struct ast_channel *mychannel,*genchannel; { ast_hangup(mychannel); ast_hangup(genchannel); + ast_mutex_lock(&myrpt->lock); myrpt->callmode = 0; + ast_mutex_unlock(&myrpt->lock); pthread_exit(NULL); } if (myrpt->ourcallerid && *myrpt->ourcallerid) @@ -459,18 +606,22 @@ struct ast_channel *mychannel,*genchannel; ast_log(LOG_WARNING, "Unable to start PBX!!\n"); ast_hangup(mychannel); ast_hangup(genchannel); + ast_mutex_lock(&myrpt->lock); myrpt->callmode = 0; + ast_mutex_unlock(&myrpt->lock); pthread_exit(NULL); } + ast_mutex_lock(&myrpt->lock); myrpt->callmode = 3; - while(myrpt->callmode) { if ((!mychannel->pvt) && (myrpt->callmode != 4)) { myrpt->callmode = 4; + ast_mutex_unlock(&myrpt->lock); /* start congestion tone */ tone_zone_play_tone(genchannel->fds[0],ZT_TONE_CONGESTION); + ast_mutex_lock(&myrpt->lock); } if (myrpt->mydtmf) { @@ -481,122 +632,480 @@ struct ast_channel *mychannel,*genchannel; wf.data = NULL; wf.datalen = 0; wf.samples = 0; + ast_mutex_unlock(&myrpt->lock); ast_write(genchannel,&wf); + ast_mutex_lock(&myrpt->lock); myrpt->mydtmf = 0; } + ast_mutex_unlock(&myrpt->lock); usleep(25000); + ast_mutex_lock(&myrpt->lock); } + ast_mutex_unlock(&myrpt->lock); tone_zone_play_tone(genchannel->fds[0],-1); if (mychannel->pvt) ast_softhangup(mychannel,AST_SOFTHANGUP_DEV); ast_hangup(genchannel); + ast_mutex_lock(&myrpt->lock); myrpt->callmode = 0; + ast_mutex_unlock(&myrpt->lock); pthread_exit(NULL); } -static void process_dtmf(char *cmd,struct rpt *myrpt) +static void send_link_dtmf(struct rpt *myrpt,char c) +{ +char str[300]; +struct ast_frame wf; +struct rpt_link *l; + + sprintf(str,"D %s %s %d %c",myrpt->cmdnode,myrpt->name,++(myrpt->dtmfidx),c); + wf.frametype = AST_FRAME_TEXT; + wf.subclass = 0; + wf.offset = 0; + wf.mallocd = 1; + wf.datalen = strlen(str) + 1; + wf.samples = 0; + l = myrpt->links.next; + /* first, see if our dude is there */ + while(l != &myrpt->links) + { + if (!l->isremote) + { + /* if we found it, write it and were done */ + if (!strcmp(l->name,myrpt->cmdnode)) + { + wf.data = strdup(str); + ast_write(l->chan,&wf); + return; + } + } + l = l->next; + } + l = myrpt->links.next; + /* if not, give it to everyone */ + while(l != &myrpt->links) + { + if (!l->isremote) + { + wf.data = strdup(str); + ast_write(l->chan,&wf); + } + l = l->next; + } + return; +} + +static void process_dtmf(char *cmd,struct rpt *myrpt, int allow_linkcmd) { pthread_attr_t attr; +char *tele,tmp[300],deststr[300],*val,*s,*s1; +struct rpt_link *l; ZT_CONFINFO ci; /* conference info */ - switch(atoi(cmd)) + switch(atoi(cmd) / 1000) { - case 0: /* autopatch on / send asterisk (*) */ + case 6: /* autopatch on / send asterisk (*) */ + ast_mutex_lock(&myrpt->lock); /* if on call, force * into current audio stream */ if ((myrpt->callmode == 2) || (myrpt->callmode == 3)) { myrpt->mydtmf = '*'; + ast_mutex_unlock(&myrpt->lock); break; } - if (myrpt->callmode) return; + if (myrpt->callmode) + { + ast_mutex_unlock(&myrpt->lock); + return; + } myrpt->callmode = 1; myrpt->cidx = 0; myrpt->exten[myrpt->cidx] = 0; + ast_mutex_unlock(&myrpt->lock); pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&myrpt->rpt_call_thread,&attr,rpt_call,(void *)myrpt); + pthread_create(&myrpt->rpt_call_thread,&attr,rpt_call,(void *) myrpt); + return; + case 0: /* autopatch off */ + ast_mutex_lock(&myrpt->lock); + if (!myrpt->callmode) + { + ast_mutex_unlock(&myrpt->lock); + return; + } + myrpt->callmode = 0; + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,TERM,NULL); return; case 9: /* master reset */ + ast_mutex_lock(&myrpt->lock); myrpt->callmode = 0; - /* fall thru intentionally */ + ast_mutex_unlock(&myrpt->lock); + l = myrpt->links.next; + /* disconnect all of the remote stuff */ + while(l != &myrpt->links) + { + ast_softhangup(l->chan,AST_SOFTHANGUP_DEV); + l = l->next; + } + break; case 1: /* remote base off */ - if (myrpt->rem_rxchannel == NULL) return; - myrpt->remotemode = REM_OFF; - ci.chan = 0; - ci.confno = 0; - ci.confmode = 0; - /* Take off conf */ - if (ioctl(myrpt->rem_rxchannel->fds[0],ZT_SETCONF,&ci) == -1) + val = ast_variable_retrieve(cfg,NODES,cmd + 1); + if (!val) { - ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); - pthread_exit(NULL); + rpt_telemetry(myrpt,REMNOTFOUND,NULL); + return; } - /* Take off conf */ - if (ioctl(myrpt->rem_txchannel->fds[0],ZT_SETCONF,&ci) == -1) + strncpy(tmp,val,sizeof(tmp) - 1); + s = tmp; + s1 = strsep(&s,","); + ast_mutex_lock(&myrpt->lock); + l = myrpt->links.next; + /* try to find this one in queue */ + while(l != &myrpt->links) { - ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); - pthread_exit(NULL); + /* if found matching string */ + if (!strcmp(l->name,cmd + 1)) break; + l = l->next; } - break; + if (l != &myrpt->links) /* if found */ + { + ast_mutex_unlock(&myrpt->lock); + ast_softhangup(l->chan,AST_SOFTHANGUP_DEV); + break; + } + ast_mutex_unlock(&myrpt->lock); + return; case 2: /* remote base monitor */ - if (myrpt->rem_rxchannel == NULL) return; - myrpt->remotemode = REM_MONITOR; - if (myrpt->remoterx && (!myrpt->remotetx)) - { - ci.chan = 0; - ci.confno = myrpt->pconf; - ci.confmode = ZT_CONF_CONF | ZT_CONF_TALKER; - /* Put on conf */ - if (ioctl(myrpt->rem_rxchannel->fds[0],ZT_SETCONF,&ci) == -1) - { - ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); - pthread_exit(NULL); + val = ast_variable_retrieve(cfg,NODES,cmd + 1); + if (!val) + { + rpt_telemetry(myrpt,REMNOTFOUND,NULL); + return; + } + strncpy(tmp,val,sizeof(tmp) - 1); + s = tmp; + s1 = strsep(&s,","); + ast_mutex_lock(&myrpt->lock); + l = myrpt->links.next; + /* try to find this one in queue */ + while(l != &myrpt->links) + { + /* if found matching string */ + if (!strcmp(l->name,cmd + 1)) break; + l = l->next; + } + /* if found */ + if (l != &myrpt->links) + { + /* if already in this mode, just ignore */ + if (!l->mode) { + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,REMALREADY,NULL); + return; } + ast_softhangup(l->chan,AST_SOFTHANGUP_DEV); + usleep(500000); + } + ast_mutex_unlock(&myrpt->lock); + /* establish call in monitor mode */ + l = malloc(sizeof(struct rpt_link)); + if (!l) + { + ast_log(LOG_WARNING, "Unable to malloc\n"); + pthread_exit(NULL); + } + /* zero the silly thing */ + memset((char *)l,0,sizeof(struct rpt_link)); + sprintf(deststr,"IAX2/%s",s1); + tele = strchr(deststr,'/'); + if (!tele) + { + fprintf(stderr,"rpt:Dial number (%s) must be in format tech/number\n",deststr); + pthread_exit(NULL); + } + *tele++ = 0; + l->isremote = (s && ast_true(s)); + strncpy(l->name,cmd + 1,MAXNODESTR - 1); + l->chan = ast_request(deststr,AST_FORMAT_SLINEAR,tele); + if (l->chan) + { + ast_set_read_format(l->chan,AST_FORMAT_SLINEAR); + ast_set_write_format(l->chan,AST_FORMAT_SLINEAR); + l->chan->whentohangup = 0; + l->chan->appl = "Apprpt"; + l->chan->data = "(Remote Rx)"; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "rpt (remote) initiating call to %s/%s on %s\n", + deststr,tele,l->chan->name); + l->chan->callerid = strdup(myrpt->name); + ast_call(l->chan,tele,0); + } + else + { + free(l); + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Unable to place call to %s/%s on %s\n", + deststr,tele,l->chan->name); + return; + } + /* allocate a pseudo-channel thru asterisk */ + l->pchan = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo"); + if (!l->pchan) + { + fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); + pthread_exit(NULL); + } + ast_set_read_format(l->pchan,AST_FORMAT_SLINEAR); + ast_set_write_format(l->pchan,AST_FORMAT_SLINEAR); + /* make a conference for the pseudo-one */ + ci.chan = 0; + ci.confno = myrpt->conf; + ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER; + /* first put the channel on the conference in proper mode */ + if (ioctl(l->pchan->fds[0],ZT_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + pthread_exit(NULL); } + ast_mutex_lock(&myrpt->lock); + /* insert at end of queue */ + insque((struct qelem *)l,(struct qelem *)myrpt->links.next); + ast_mutex_unlock(&myrpt->lock); break; case 3: /* remote base tranceieve */ - if (myrpt->rem_rxchannel == NULL) return; - myrpt->remotemode = REM_TX; - if (myrpt->remoterx && (!myrpt->remotetx)) - { - ci.chan = 0; - ci.confno = myrpt->pconf; - ci.confmode = ZT_CONF_CONF | ZT_CONF_TALKER; - /* Put on conf */ - if (ioctl(myrpt->rem_rxchannel->fds[0],ZT_SETCONF,&ci) == -1) + val = ast_variable_retrieve(cfg,NODES,cmd + 1); + if (!val) + { + rpt_telemetry(myrpt,REMNOTFOUND,NULL); + return; + } + strncpy(tmp,val,sizeof(tmp) - 1); + s = tmp; + s1 = strsep(&s,","); + ast_mutex_lock(&myrpt->lock); + l = myrpt->links.next; + /* try to find this one in queue */ + while(l != &myrpt->links) + { + /* if found matching string */ + if (!strcmp(l->name,cmd + 1)) break; + } + /* if found */ + if (l != &myrpt->links) + { + /* if already in this mode, just ignore */ + if (l->mode) { - ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); - pthread_exit(NULL); + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,REMALREADY,NULL); + return; } + ast_softhangup(l->chan,AST_SOFTHANGUP_DEV); + usleep(500000); } - break; - case 8: /* force ID */ - myrpt->idtimer = 0; - return; - default: - return; - } - /* send function complete */ - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&myrpt->rpt_call_thread,&attr,rpt_complete,(void *)myrpt); -} - -/* single thread with one file (request) to dial */ -static void *rpt(void *this) -{ -struct rpt *myrpt = (struct rpt *)this; -char *tele; -int ms = MSWAIT,lasttx,keyed,val,dtmfidx; -char dtmfbuf[MAXDTMF]; -struct ast_channel *who; + ast_mutex_unlock(&myrpt->lock); + /* establish call in tranceive mode */ + l = malloc(sizeof(struct rpt_link)); + if (!l) + { + ast_log(LOG_WARNING, "Unable to malloc\n"); + pthread_exit(NULL); + } + /* zero the silly thing */ + memset((char *)l,0,sizeof(struct rpt_link)); + l->mode = 1; + strncpy(l->name,cmd + 1,MAXNODESTR - 1); + l->isremote = (s && ast_true(s)); + sprintf(deststr,"IAX2/%s",s1); + tele = strchr(deststr,'/'); + if (!tele) + { + fprintf(stderr,"rpt:Dial number must be in format tech/number\n"); + pthread_exit(NULL); + } + *tele++ = 0; + l->chan = ast_request(deststr,AST_FORMAT_SLINEAR,tele); + if (l->chan) + { + ast_set_read_format(l->chan,AST_FORMAT_SLINEAR); + ast_set_write_format(l->chan,AST_FORMAT_SLINEAR); + l->chan->whentohangup = 0; + l->chan->appl = "Apprpt"; + l->chan->data = "(Remote Rx)"; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "rpt (remote) initiating call to %s/%s on %s\n", + deststr,tele,l->chan->name); + l->chan->callerid = strdup(myrpt->name); + ast_call(l->chan,tele,999); + } + else + { + free(l); + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Unable to place call to %s/%s on %s\n", + deststr,tele,l->chan->name); + return; + } + /* allocate a pseudo-channel thru asterisk */ + l->pchan = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo"); + if (!l->pchan) + { + fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); + pthread_exit(NULL); + } + ast_set_read_format(l->pchan,AST_FORMAT_SLINEAR); + ast_set_write_format(l->pchan,AST_FORMAT_SLINEAR); + /* make a conference for the tx */ + ci.chan = 0; + ci.confno = myrpt->conf; + ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER; + /* first put the channel on the conference in proper mode */ + if (ioctl(l->pchan->fds[0],ZT_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + pthread_exit(NULL); + } + ast_mutex_lock(&myrpt->lock); + /* insert at end of queue */ + insque((struct qelem *)l,(struct qelem *)myrpt->links.next); + ast_mutex_unlock(&myrpt->lock); + break; + case 4: /* remote cmd mode */ + /* if doesnt allow link cmd, return */ + if ((!allow_linkcmd) || (myrpt->links.next == &myrpt->links)) return; + /* if already in cmd mode, or selected self, forget it */ + if ((myrpt->cmdnode[0]) || (!strcmp(myrpt->name,cmd + 1))) + { + rpt_telemetry(myrpt,REMALREADY,NULL); + return; + } + /* node must at least exist in list */ + val = ast_variable_retrieve(cfg,NODES,cmd + 1); + if (!val) + { + rpt_telemetry(myrpt,REMNOTFOUND,NULL); + return; + } + ast_mutex_lock(&myrpt->lock); + myrpt->dtmfidx = -1; + myrpt->dtmfbuf[0] = 0; + myrpt->rem_dtmfidx = -1; + myrpt->rem_dtmfbuf[0] = 0; + strcpy(myrpt->cmdnode,cmd + 1); + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,REMGO,NULL); + return; + case 7: /* system status */ + rpt_telemetry(myrpt,STATUS,NULL); + return; + case 8: /* force ID */ + myrpt->idtimer = 0; + return; + default: + return; + } + rpt_telemetry(myrpt,COMPLETE,NULL); +} + +static void handle_link_data(struct rpt *myrpt, char *str) +{ +char tmp[300],cmd[300],dest[300],src[300],c; +int seq; +struct rpt_link *l; +struct ast_frame wf; + + /* if we are a remote, we dont want to do this */ + if (myrpt->remote) return; + wf.frametype = AST_FRAME_TEXT; + wf.subclass = 0; + wf.offset = 0; + wf.mallocd = 1; + wf.datalen = strlen(str) + 1; + wf.samples = 0; + l = myrpt->links.next; + /* put string in our buffer */ + strncpy(tmp,str,sizeof(tmp) - 1); + if (sscanf(tmp,"%s %s %s %d %c",cmd,dest,src,&seq,&c) != 5) + { + ast_log(LOG_WARNING, "Unable to parse link string %s\n",str); + return; + } + if (strcmp(cmd,"D")) + { + ast_log(LOG_WARNING, "Unable to parse link string %s\n",str); + return; + } + /* if not for me, redistribute to all links */ + if (strcmp(dest,myrpt->name)) + { + l = myrpt->links.next; + while(l != &myrpt->links) + { + if (!l->isremote) + { + wf.data = strdup(str); + ast_write(l->chan,&wf); + } + l = l->next; + } + return; + } + ast_mutex_lock(&myrpt->lock); + if (c == '*') + { + myrpt->rem_dtmfidx = 0; + myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0; + time(&myrpt->rem_dtmf_time); + ast_mutex_unlock(&myrpt->lock); + return; + } + else if ((c != '#') && (myrpt->rem_dtmfidx >= 0)) + { + time(&myrpt->rem_dtmf_time); + if (myrpt->rem_dtmfidx < MAXDTMF) + { + myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx++] = c; + myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0; + /* if to terminate function now */ + if ((myrpt->rem_dtmfidx == 1) && strchr(SHORTFUNCS,c)) + { + while(myrpt->rem_dtmfidx < FUNCTION_LEN) + myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx++] = '0'; + myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0; + } + } + if (myrpt->rem_dtmfidx == FUNCTION_LEN) + { + strcpy(cmd,myrpt->rem_dtmfbuf); + myrpt->rem_dtmfbuf[0] = 0; + myrpt->rem_dtmfidx = -1; + ast_mutex_unlock(&myrpt->lock); + process_dtmf(cmd,myrpt,0); + return; + } + } + ast_mutex_unlock(&myrpt->lock); + return; +} + +/* single thread with one file (request) to dial */ +static void *rpt(void *this) +{ +struct rpt *myrpt = (struct rpt *)this; +char *tele; +int ms = MSWAIT,lasttx,keyed,val,remrx; +struct ast_channel *who; ZT_CONFINFO ci; /* conference info */ time_t dtmf_time,t; +struct rpt_link *l,*m; pthread_attr_t attr; + ast_mutex_lock(&myrpt->lock); tele = strchr(myrpt->rxchanname,'/'); if (!tele) { fprintf(stderr,"rpt:Dial number must be in format tech/number\n"); + ast_mutex_unlock(&myrpt->lock); pthread_exit(NULL); } *tele++ = 0; @@ -616,6 +1125,7 @@ pthread_attr_t attr; else { fprintf(stderr,"rpt:Sorry unable to obtain Rx channel\n"); + ast_mutex_unlock(&myrpt->lock); pthread_exit(NULL); } if (myrpt->txchanname) @@ -624,6 +1134,7 @@ pthread_attr_t attr; if (!tele) { fprintf(stderr,"rpt:Dial number must be in format tech/number\n"); + ast_mutex_unlock(&myrpt->lock); pthread_exit(NULL); } *tele++ = 0; @@ -643,6 +1154,7 @@ pthread_attr_t attr; else { fprintf(stderr,"rpt:Sorry unable to obtain Tx channel\n"); + ast_mutex_unlock(&myrpt->lock); pthread_exit(NULL); } } @@ -650,85 +1162,23 @@ pthread_attr_t attr; { myrpt->txchannel = myrpt->rxchannel; } - myrpt->rem_rxchannel = NULL; - myrpt->rem_txchannel = NULL; - myrpt->remoterx = 0; - myrpt->remotemode = REM_OFF; - if (myrpt->rem_rxchanname) - { - tele = strchr(myrpt->rem_rxchanname,'/'); - if (!tele) - { - fprintf(stderr,"rpt:Dial number must be in format tech/number\n"); - pthread_exit(NULL); - } - *tele++ = 0; - myrpt->rem_rxchannel = ast_request(myrpt->rem_rxchanname,AST_FORMAT_SLINEAR,tele); - if (myrpt->rem_rxchannel) - { - ast_set_read_format(myrpt->rem_rxchannel,AST_FORMAT_SLINEAR); - ast_set_write_format(myrpt->rem_rxchannel,AST_FORMAT_SLINEAR); - myrpt->rem_rxchannel->whentohangup = 0; - myrpt->rem_rxchannel->appl = "Apprpt"; - myrpt->rem_rxchannel->data = "(Repeater/Remote Rx)"; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "rpt (RemoteRx) initiating call to %s/%s on %s\n", - myrpt->rem_rxchanname,tele,myrpt->rem_rxchannel->name); - ast_call(myrpt->rem_rxchannel,tele,999); - } - else - { - fprintf(stderr,"rpt:Sorry unable to obtain RemoteRx channel\n"); - pthread_exit(NULL); - } - if (myrpt->rem_txchanname) /* if in remote base mode */ - { - tele = strchr(myrpt->rem_txchanname,'/'); - if (!tele) - { - fprintf(stderr,"rpt:Dial number must be in format tech/number\n"); - pthread_exit(NULL); - } - *tele++ = 0; - myrpt->rem_txchannel = ast_request(myrpt->rem_txchanname,AST_FORMAT_SLINEAR,tele); - if (myrpt->rem_txchannel) - { - ast_set_read_format(myrpt->rem_txchannel,AST_FORMAT_SLINEAR); - ast_set_write_format(myrpt->rem_txchannel,AST_FORMAT_SLINEAR); - myrpt->rem_txchannel->whentohangup = 0; - myrpt->rem_txchannel->appl = "Apprpt"; - myrpt->rem_txchannel->data = "(Repeater/Remote Tx)"; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "rpt (RemoteTx) initiating call to %s/%s on %s\n", - myrpt->rem_txchanname,tele,myrpt->rem_txchannel->name); - ast_call(myrpt->rem_txchannel,tele,999); - } - else - { - fprintf(stderr,"rpt:Sorry unable to obtain Tx channel\n"); - pthread_exit(NULL); - } - } - else - { - myrpt->rem_txchannel = myrpt->rem_rxchannel; - } - } /* allocate a pseudo-channel thru asterisk */ myrpt->pchannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo"); if (!myrpt->pchannel) { fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); + ast_mutex_unlock(&myrpt->lock); pthread_exit(NULL); } /* make a conference for the tx */ ci.chan = 0; ci.confno = -1; /* make a new conf */ ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER; - /* first put the channel on the conference in announce mode */ + /* first put the channel on the conference in proper mode */ if (ioctl(myrpt->txchannel->fds[0],ZT_SETCONF,&ci) == -1) { ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + ast_mutex_unlock(&myrpt->lock); pthread_exit(NULL); } /* save tx conference number */ @@ -741,50 +1191,75 @@ pthread_attr_t attr; if (ioctl(myrpt->pchannel->fds[0],ZT_SETCONF,&ci) == -1) { ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + ast_mutex_unlock(&myrpt->lock); pthread_exit(NULL); } /* save pseudo channel conference number */ - myrpt->pconf = ci.confno; + myrpt->conf = ci.confno; + /* allocate a pseudo-channel thru asterisk */ + myrpt->txpchannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo"); + if (!myrpt->txpchannel) + { + fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); + ast_mutex_unlock(&myrpt->lock); + pthread_exit(NULL); + } + /* make a conference for the tx */ + ci.chan = 0; + ci.confno = myrpt->txconf; + ci.confmode = ZT_CONF_CONF | ZT_CONF_TALKER ; + /* first put the channel on the conference in proper mode */ + if (ioctl(myrpt->txpchannel->fds[0],ZT_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + ast_mutex_unlock(&myrpt->lock); + pthread_exit(NULL); + } /* Now, the idea here is to copy from the physical rx channel buffer into the pseudo tx buffer, and from the pseudo rx buffer into the tx channel buffer */ + myrpt->links.next = &myrpt->links; + myrpt->links.prev = &myrpt->links; myrpt->tailtimer = 0; myrpt->totimer = 0; myrpt->idtimer = 0; + myrpt->callmode = 0; + ast_mutex_unlock(&myrpt->lock); lasttx = 0; - myrpt->remotetx = 0; keyed = 0; - myrpt->callmode = 0; - dtmfidx = -1; - dtmfbuf[0] = 0; + myrpt->dtmfidx = -1; + myrpt->dtmfbuf[0] = 0; + myrpt->rem_dtmfidx = -1; + myrpt->rem_dtmfbuf[0] = 0; dtmf_time = 0; + myrpt->rem_dtmf_time = 0; val = 0; ast_channel_setoption(myrpt->rxchannel,AST_OPTION_TONE_VERIFY,&val,sizeof(char),0); val = 1; ast_channel_setoption(myrpt->rxchannel,AST_OPTION_RELAXDTMF,&val,sizeof(char),0); - if (myrpt->rem_rxchannel) - { - val = 0; - ast_channel_setoption(myrpt->rem_rxchannel,AST_OPTION_TONE_VERIFY,&val,sizeof(char),0); - val = 1; - ast_channel_setoption(myrpt->rem_rxchannel,AST_OPTION_RELAXDTMF,&val,sizeof(char),0); - } while (ms >= 0) { struct ast_frame *f; - struct ast_channel *cs[5]; - int totx,rem_totx,elap,n; + struct ast_channel *cs[300]; + int totx,elap,n,toexit; if (ast_check_hangup(myrpt->rxchannel)) break; if (ast_check_hangup(myrpt->txchannel)) break; - if (myrpt->rem_rxchannel) + if (ast_check_hangup(myrpt->pchannel)) break; + if (ast_check_hangup(myrpt->txpchannel)) break; + ast_mutex_lock(&myrpt->lock); + myrpt->localtx = keyed; + l = myrpt->links.next; + remrx = 0; + while(l != &myrpt->links) { - if (ast_check_hangup(myrpt->rem_rxchannel)) break; - if (ast_check_hangup(myrpt->rem_txchannel)) break; + if (l->lastrx) remrx = 1; + l = l->next; } - totx = (keyed || myrpt->callmode || myrpt->iding || myrpt->terming - || ((myrpt->remotemode != REM_OFF) && myrpt->remoterx) || - myrpt->teleing || myrpt->comping || myrpt->procing); + totx = (keyed || myrpt->callmode || + (myrpt->tele.next != &myrpt->tele)); + myrpt->exttx = totx; + totx |= remrx; if (!totx) myrpt->totimer = myrpt->totime; else myrpt->tailtimer = myrpt->hangtime; totx = (totx || myrpt->tailtimer) && myrpt->totimer; @@ -793,6 +1268,7 @@ pthread_attr_t attr; if ((!totx) && (!myrpt->totimer) && myrpt->callmode && keyed) { myrpt->totimer = myrpt->totime; + ast_mutex_unlock(&myrpt->lock); continue; } /* if timed-out and in circuit busy after call */ @@ -803,97 +1279,83 @@ pthread_attr_t attr; if (totx && (!myrpt->idtimer)) { myrpt->idtimer = myrpt->idtime; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&myrpt->rpt_id_thread,&attr,rpt_id,(void *) myrpt); + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,ID,NULL); + ast_mutex_lock(&myrpt->lock); } if (totx && (!lasttx)) { lasttx = 1; + ast_mutex_unlock(&myrpt->lock); ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_KEY); + ast_mutex_lock(&myrpt->lock); } if ((!totx) && lasttx) { lasttx = 0; + ast_mutex_unlock(&myrpt->lock); ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY); - } - rem_totx = ((keyed && (myrpt->remotemode == REM_TX)) && myrpt->totimer); - if (rem_totx && (!myrpt->remotetx)) - { - myrpt->remotetx = 1; - ci.chan = 0; - ci.confno = 0; - ci.confmode = 0; - /* Take off conf */ - if (ioctl(myrpt->rem_rxchannel->fds[0],ZT_SETCONF,&ci) == -1) - { - ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); - pthread_exit(NULL); - } - ast_indicate(myrpt->rem_txchannel,AST_CONTROL_RADIO_KEY); - ci.chan = 0; - ci.confno = myrpt->txconf; - ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER; - /* Put the channel on the conference in listener mode */ - if (ioctl(myrpt->rem_txchannel->fds[0],ZT_SETCONF,&ci) == -1) - { - ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); - pthread_exit(NULL); - } - } - if ((!rem_totx) && myrpt->remotetx) - { - myrpt->remotetx = 0; - ast_indicate(myrpt->rem_txchannel,AST_CONTROL_RADIO_UNKEY); - ci.chan = 0; - ci.confno = 0; - ci.confmode = 0; - /* Take off conf */ - if (ioctl(myrpt->rem_txchannel->fds[0],ZT_SETCONF,&ci) == -1) - { - ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); - pthread_exit(NULL); - } - if (myrpt->remotemode != REM_OFF) - { - ci.chan = 0; - ci.confno = myrpt->pconf; - ci.confmode = ZT_CONF_CONF | ZT_CONF_TALKER; - /* Put on conf */ - if (ioctl(myrpt->rem_rxchannel->fds[0],ZT_SETCONF,&ci) == -1) - { - ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); - pthread_exit(NULL); - } - } + ast_mutex_lock(&myrpt->lock); } time(&t); /* if DTMF timeout */ - if ((dtmfidx >= 0) && ((dtmf_time + DTMF_TIMEOUT) < t)) + if ((!myrpt->cmdnode[0]) && (myrpt->dtmfidx >= 0) && ((dtmf_time + DTMF_TIMEOUT) < t)) { - dtmfidx = -1; - dtmfbuf[0] = 0; + myrpt->dtmfidx = -1; + myrpt->dtmfbuf[0] = 0; + } + /* if remote DTMF timeout */ + if ((myrpt->rem_dtmfidx >= 0) && ((myrpt->rem_dtmf_time + DTMF_TIMEOUT) < t)) + { + myrpt->rem_dtmfidx = -1; + myrpt->rem_dtmfbuf[0] = 0; } n = 0; cs[n++] = myrpt->rxchannel; cs[n++] = myrpt->pchannel; + cs[n++] = myrpt->txpchannel; if (myrpt->txchannel != myrpt->rxchannel) cs[n++] = myrpt->txchannel; - if (myrpt->rem_rxchannel) + l = myrpt->links.next; + while(l != &myrpt->links) { - cs[n++] = myrpt->rem_rxchannel; - if (myrpt->rem_txchannel != myrpt->rem_rxchannel) - cs[n++] = myrpt->rem_txchannel; + cs[n++] = l->chan; + cs[n++] = l->pchan; + l = l->next; } + ast_mutex_unlock(&myrpt->lock); ms = MSWAIT; who = ast_waitfor_n(cs,n,&ms); if (who == NULL) ms = 0; elap = MSWAIT - ms; + ast_mutex_lock(&myrpt->lock); + l = myrpt->links.next; + while(l != &myrpt->links) + { + /* ignore non-timing channels */ + if (l->elaptime < 0) + { + l = l->next; + continue; + } + l->elaptime += elap; + /* if connection has taken too long */ + if ((l->elaptime > MAXCONNECTTIME) && + (l->chan->_state != AST_STATE_UP)) + { + ast_mutex_unlock(&myrpt->lock); + ast_softhangup(l->chan,AST_SOFTHANGUP_DEV); + rpt_telemetry(myrpt,CONNFAIL,l); + ast_mutex_lock(&myrpt->lock); + } + l = l->next; + } if (myrpt->tailtimer) myrpt->tailtimer -= elap; if (myrpt->tailtimer < 0) myrpt->tailtimer = 0; if (myrpt->totimer) myrpt->totimer -= elap; if (myrpt->totimer < 0) myrpt->totimer = 0; if (myrpt->idtimer) myrpt->idtimer -= elap; if (myrpt->idtimer < 0) myrpt->idtimer = 0; + ast_mutex_unlock(&myrpt->lock); if (!ms) continue; if (who == myrpt->rxchannel) /* if it was a read from rx */ { @@ -912,28 +1374,60 @@ pthread_attr_t attr; char c; c = (char) f->subclass; /* get DTMF char */ + if (c == '#') + { + /* if in simple mode, kill autopatch */ + if (myrpt->simple && myrpt->callmode) + { + myrpt->callmode = 0; + rpt_telemetry(myrpt,TERM,NULL); + continue; + } + ast_mutex_lock(&myrpt->lock); + if (myrpt->cmdnode[0]) + { + myrpt->cmdnode[0] = 0; + myrpt->dtmfidx = -1; + myrpt->dtmfbuf[0] = 0; + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,COMPLETE,NULL); + } else ast_mutex_unlock(&myrpt->lock); + continue; + } + if (myrpt->cmdnode[0]) + { + send_link_dtmf(myrpt,c); + continue; + } if (!myrpt->simple) { if (c == '*') { - dtmfidx = 0; - dtmfbuf[dtmfidx] = 0; + myrpt->dtmfidx = 0; + myrpt->dtmfbuf[myrpt->dtmfidx] = 0; time(&dtmf_time); continue; } - else if ((c != '#') && (dtmfidx >= 0)) + else if ((c != '#') && (myrpt->dtmfidx >= 0)) { time(&dtmf_time); - if (dtmfidx < MAXDTMF) + if (myrpt->dtmfidx < MAXDTMF) { - dtmfbuf[dtmfidx++] = c; - dtmfbuf[dtmfidx] = 0; + myrpt->dtmfbuf[myrpt->dtmfidx++] = c; + myrpt->dtmfbuf[myrpt->dtmfidx] = 0; + /* if to terminate function now */ + if ((myrpt->dtmfidx == 1) && strchr(SHORTFUNCS,c)) + { + while(myrpt->dtmfidx < FUNCTION_LEN) + myrpt->dtmfbuf[myrpt->dtmfidx++] = '0'; + myrpt->dtmfbuf[myrpt->dtmfidx] = 0; + } } - if (dtmfidx == FUNCTION_LEN) + if (myrpt->dtmfidx == FUNCTION_LEN) { - process_dtmf(dtmfbuf,myrpt); - dtmfbuf[0] = 0; - dtmfidx = -1; + process_dtmf(myrpt->dtmfbuf,myrpt,1); + myrpt->dtmfbuf[0] = 0; + myrpt->dtmfidx = -1; continue; } } @@ -951,14 +1445,6 @@ pthread_attr_t attr; continue; } } - if (myrpt->callmode && (c == '#')) - { - myrpt->callmode = 0; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&myrpt->rpt_term_thread,&attr,rpt_term,(void *) myrpt); - continue; - } if (myrpt->callmode == 1) { myrpt->exten[myrpt->cidx++] = c; @@ -967,9 +1453,7 @@ pthread_attr_t attr; if (ast_exists_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL)) { myrpt->callmode = 2; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&myrpt->rpt_proc_thread,&attr,rpt_proc,(void *) myrpt); + rpt_telemetry(myrpt,PROC,NULL); } /* if can continue, do so */ if (ast_canmatch_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL)) continue; @@ -1001,11 +1485,10 @@ pthread_attr_t attr; { if (debug) printf("@@@@ rx un-key\n"); keyed = 0; - if (myrpt->remotemode != REM_OFF) + /* if we have remotes, twiddle */ + if (myrpt->cmdnode[0] || (myrpt->links.next != &myrpt->links)) { - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&myrpt->rpt_proc_thread,&attr,rpt_remote_telemetry,(void *) myrpt); + rpt_telemetry(myrpt,UNKEY,NULL); } } } @@ -1022,9 +1505,7 @@ pthread_attr_t attr; } if (f->frametype == AST_FRAME_VOICE) { - ast_write(myrpt->txchannel,f); - if (myrpt->remotemode == REM_TX) - ast_write(myrpt->rem_txchannel,f); + ast_write(myrpt->txpchannel,f); } if (f->frametype == AST_FRAME_CONTROL) { @@ -1058,65 +1539,129 @@ pthread_attr_t attr; ast_frfree(f); continue; } - if (who == myrpt->rem_rxchannel) /* if it was a read from rx */ + toexit = 0; + l = myrpt->links.next; + while(l != &myrpt->links) { - f = ast_read(myrpt->rem_rxchannel); - if (!f) + if (who == l->chan) /* if it was a read from rx */ { - if (debug) printf("@@@@ rpt:Hung Up\n"); + ast_mutex_lock(&myrpt->lock); + remrx = 0; + /* see if any other links are receiving */ + m = myrpt->links.next; + while(m != &myrpt->links) + { + /* if not us, count it */ + if ((m != l) && (m->lastrx)) remrx = 1; + m = m->next; + } + ast_mutex_unlock(&myrpt->lock); + totx = (((l->isremote) ? myrpt->localtx : + myrpt->exttx) || remrx) && l->mode; + if (l->lasttx != totx) + { + if (totx) + { + ast_indicate(l->chan,AST_CONTROL_RADIO_KEY); + } + else + { + ast_indicate(l->chan,AST_CONTROL_RADIO_UNKEY); + } + } + l->lasttx = totx; + f = ast_read(l->chan); + if (!f) + { + ast_mutex_lock(&myrpt->lock); + /* remove from queue */ + remque((struct qelem *) l); + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,REMDISC,l); + /* hang-up on call to device */ + ast_hangup(l->chan); + ast_hangup(l->pchan); + free(l); + break; + } + if (f->frametype == AST_FRAME_VOICE) + { + ast_write(l->pchan,f); + } + if (f->frametype == AST_FRAME_TEXT) + { + handle_link_data(myrpt,f->data); + } + if (f->frametype == AST_FRAME_CONTROL) + { + if (f->subclass == AST_CONTROL_ANSWER) + { + l->connected = 1; + l->elaptime = -1; + rpt_telemetry(myrpt,CONNECTED,l); + } + /* if RX key */ + if (f->subclass == AST_CONTROL_RADIO_KEY) + { + if (debug) printf("@@@@ rx key\n"); + l->lastrx = 1; + } + /* if RX un-key */ + if (f->subclass == AST_CONTROL_RADIO_UNKEY) + { + if (debug) printf("@@@@ rx un-key\n"); + l->lastrx = 0; + } + if (f->subclass == AST_CONTROL_HANGUP) + { + ast_frfree(f); + ast_mutex_lock(&myrpt->lock); + /* remove from queue */ + remque((struct qelem *) l); + ast_mutex_unlock(&myrpt->lock); + rpt_telemetry(myrpt,REMDISC,l); + /* hang-up on call to device */ + ast_hangup(l->chan); + ast_hangup(l->pchan); + free(l); + break; + } + } + ast_frfree(f); break; } - if (f->frametype == AST_FRAME_CONTROL) + if (who == l->pchan) { - if (f->subclass == AST_CONTROL_HANGUP) + f = ast_read(l->pchan); + if (!f) { if (debug) printf("@@@@ rpt:Hung Up\n"); - ast_frfree(f); + toexit = 1; break; } - /* if RX key */ - if (f->subclass == AST_CONTROL_RADIO_KEY) + if (f->frametype == AST_FRAME_VOICE) { - if (debug) printf("@@@@ remote rx key\n"); - if (!myrpt->remotetx) - { - myrpt->remoterx = 1; - ci.chan = 0; - ci.confno = myrpt->pconf; - ci.confmode = ZT_CONF_CONF | ZT_CONF_TALKER; - /* Put on conf */ - if (ioctl(myrpt->rem_rxchannel->fds[0],ZT_SETCONF,&ci) == -1) - { - ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); - pthread_exit(NULL); - } - } + ast_write(l->chan,f); } - /* if RX un-key */ - if (f->subclass == AST_CONTROL_RADIO_UNKEY) + if (f->frametype == AST_FRAME_CONTROL) { - if (debug) printf("@@@@ remote rx un-key\n"); - if (!myrpt->remotetx) + if (f->subclass == AST_CONTROL_HANGUP) { - myrpt->remoterx = 0; - ci.chan = 0; - ci.confno = 0; - ci.confmode = 0; - /* Take off conf */ - if (ioctl(myrpt->rem_rxchannel->fds[0],ZT_SETCONF,&ci) == -1) - { - ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); - pthread_exit(NULL); - } + if (debug) printf("@@@@ rpt:Hung Up\n"); + ast_frfree(f); + toexit = 1; + break; } } + ast_frfree(f); + break; } - ast_frfree(f); - continue; + l = l->next; } - if (who == myrpt->rem_txchannel) /* if it was a read from remote tx */ + if (toexit) break; + if (who == myrpt->txpchannel) /* if it was a read from remote tx */ { - f = ast_read(myrpt->rem_txchannel); + f = ast_read(myrpt->txpchannel); if (!f) { if (debug) printf("@@@@ rpt:Hung Up\n"); @@ -1134,25 +1679,32 @@ pthread_attr_t attr; ast_frfree(f); continue; } - } + ast_mutex_lock(&myrpt->lock); ast_hangup(myrpt->pchannel); + ast_hangup(myrpt->txpchannel); ast_hangup(myrpt->rxchannel); if (myrpt->txchannel != myrpt->rxchannel) ast_hangup(myrpt->txchannel); - if (myrpt->rem_rxchannel) + l = myrpt->links.next; + while(l != &myrpt->links) { - ast_hangup(myrpt->rem_rxchannel); - if (myrpt->rem_txchannel != myrpt->rem_rxchannel) - ast_hangup(myrpt->rem_txchannel); + struct rpt_link *ll = l; + /* remove from queue */ + remque((struct qelem *) l); + /* hang-up on call to device */ + ast_hangup(l->chan); + ast_hangup(l->pchan); + l = l->next; + free(ll); } + ast_mutex_unlock(&myrpt->lock); if (debug) printf("@@@@ rpt:Hung up channel\n"); - pthread_exit(NULL); + pthread_exit(NULL); return NULL; } static void *rpt_master(void *ignore) { -struct ast_config *cfg; char *this,*val; int i,n; @@ -1170,17 +1722,19 @@ int i,n; n = 0; while((this = ast_category_browse(cfg,this)) != NULL) { + if (!strcmp(this,NODES)) continue; ast_log(LOG_DEBUG,"Loading config for repeater %s\n",this); + ast_mutex_init(&rpt_vars[n].lock); + rpt_vars[n].tele.next = &rpt_vars[n].tele; + rpt_vars[n].tele.prev = &rpt_vars[n].tele; rpt_vars[n].name = this; rpt_vars[n].rxchanname = ast_variable_retrieve(cfg,this,"rxchannel"); rpt_vars[n].txchanname = ast_variable_retrieve(cfg,this,"txchannel"); - rpt_vars[n].rem_rxchanname = ast_variable_retrieve(cfg,this,"remote_rxchannel"); - rpt_vars[n].rem_txchanname = ast_variable_retrieve(cfg,this,"remote_txchannel"); rpt_vars[n].ourcontext = ast_variable_retrieve(cfg,this,"context"); if (!rpt_vars[n].ourcontext) rpt_vars[n].ourcontext = this; rpt_vars[n].ourcallerid = ast_variable_retrieve(cfg,this,"callerid"); rpt_vars[n].acctcode = ast_variable_retrieve(cfg,this,"accountcode"); - rpt_vars[n].idrecording = ast_variable_retrieve(cfg,this,"idrecording"); + rpt_vars[n].ident = ast_variable_retrieve(cfg,this,"idrecording"); val = ast_variable_retrieve(cfg,this,"hangtime"); if (val) rpt_vars[n].hangtime = atoi(val); else rpt_vars[n].hangtime = HANGTIME; @@ -1193,40 +1747,381 @@ int i,n; val = ast_variable_retrieve(cfg,this,"simple"); if (val) rpt_vars[n].simple = ast_true(val); else rpt_vars[n].simple = 0; + val = ast_variable_retrieve(cfg,this,"remote"); + if (val) rpt_vars[n].remote = ast_true(val); + else rpt_vars[n].remote = 0; rpt_vars[n].tonezone = ast_variable_retrieve(cfg,this,"tonezone"); n++; } + nrpts = n; ast_log(LOG_DEBUG, "Total of %d repeaters configured.\n",n); /* start em all */ for(i = 0; i < n; i++) { if (!rpt_vars[i].rxchanname) { - ast_log(LOG_WARNING,"Did not specify rxchanname for repeater %s\n",rpt_vars[i].name); + ast_log(LOG_WARNING,"Did not specify rxchanname for node %s\n",rpt_vars[i].name); pthread_exit(NULL); } - if (!rpt_vars[i].idrecording) + /* if is a remote, dont start one for it */ + if (rpt_vars[i].remote) continue; + if (!rpt_vars[i].ident) { - ast_log(LOG_WARNING,"Did not specify idrecording for repeater %s\n",rpt_vars[i].name); + ast_log(LOG_WARNING,"Did not specify ident for node %s\n",rpt_vars[i].name); pthread_exit(NULL); } - pthread_create(&rpt_vars[i].rpt_id_thread,NULL,rpt,(void *) &rpt_vars[i]); + pthread_create(&rpt_vars[i].rpt_thread,NULL,rpt,(void *) &rpt_vars[i]); } /* wait for first one to die (should be never) */ - pthread_join(rpt_vars[0].rpt_id_thread,NULL); + pthread_join(rpt_vars[0].rpt_thread,NULL); pthread_exit(NULL); } +static int rpt_exec(struct ast_channel *chan, void *data) +{ + int res=-1,i,keyed = 0,rem_totx; + struct localuser *u; + char tmp[256]; + char *options,*stringp,*tele; + struct rpt *myrpt; + struct ast_frame *f; + struct ast_channel *who; + struct ast_channel *cs[20]; + struct rpt_link *l; + ZT_CONFINFO ci; /* conference info */ + int ms,elap; + + if (!data || ast_strlen_zero((char *)data)) { + ast_log(LOG_WARNING, "Rpt requires an argument (system node)\n"); + return -1; + } + strncpy(tmp, (char *)data, sizeof(tmp)-1); + stringp=tmp; + strsep(&stringp, "|"); + options = strsep(&stringp, "|"); + myrpt = NULL; + /* see if we can find our specified one */ + for(i = 0; i < nrpts; i++) + { + /* if name matches, assign it and exit loop */ + if (!strcmp(tmp,rpt_vars[i].name)) + { + myrpt = &rpt_vars[i]; + break; + } + } + if (myrpt == NULL) + { + ast_log(LOG_WARNING, "Cannot find specified system node %s\n",tmp); + return -1; + } + /* if is not a remote */ + if (!myrpt->remote) + { + char *b,*b1; + + /* look at callerid to see what node this comes from */ + if (!chan->callerid) /* if doesnt have callerid */ + { + ast_log(LOG_WARNING, "Trying to use busy link on %s\n",tmp); + return -1; + } + ast_callerid_parse(chan->callerid,&b,&b1); + ast_shrink_phone_number(b1); + if (!strcmp(myrpt->name,b1)) + { + ast_log(LOG_WARNING, "Trying to link to self!!\n"); + return -1; + } + ast_mutex_lock(&myrpt->lock); + l = myrpt->links.next; + /* try to find this one in queue */ + while(l != &myrpt->links) + { + /* if found matching string */ + if (!strcmp(l->name,b1)) break; + } + /* if found */ + if (l != &myrpt->links) + { + /* remove from queue */ + remque((struct qelem *) l); + ast_mutex_unlock(&myrpt->lock); + /* hang-up on call to device */ + ast_hangup(l->chan); + ast_hangup(l->pchan); + free(l); + usleep(500000); + } else + ast_mutex_unlock(&myrpt->lock); + /* establish call in tranceive mode */ + l = malloc(sizeof(struct rpt_link)); + if (!l) + { + ast_log(LOG_WARNING, "Unable to malloc\n"); + pthread_exit(NULL); + } + /* zero the silly thing */ + memset((char *)l,0,sizeof(struct rpt_link)); + l->mode = 1; + strncpy(l->name,b1,MAXNODESTR - 1); + l->isremote = 0; + l->chan = chan; + l->connected = 1; + ast_set_read_format(l->chan,AST_FORMAT_SLINEAR); + ast_set_write_format(l->chan,AST_FORMAT_SLINEAR); + /* allocate a pseudo-channel thru asterisk */ + l->pchan = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo"); + if (!l->pchan) + { + fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); + pthread_exit(NULL); + } + ast_set_read_format(l->pchan,AST_FORMAT_SLINEAR); + ast_set_write_format(l->pchan,AST_FORMAT_SLINEAR); + /* make a conference for the tx */ + ci.chan = 0; + ci.confno = myrpt->conf; + ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER; + /* first put the channel on the conference in proper mode */ + if (ioctl(l->pchan->fds[0],ZT_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + pthread_exit(NULL); + } + ast_mutex_lock(&myrpt->lock); + /* insert at end of queue */ + insque((struct qelem *)l,(struct qelem *)myrpt->links.next); + ast_mutex_unlock(&myrpt->lock); + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } + return AST_PBX_KEEPALIVE; + } + /* if remote, error if anyone else already linked */ + if (myrpt->remoteon) + { + ast_mutex_unlock(&myrpt->lock); + ast_log(LOG_WARNING, "Trying to use busy link on %s\n",tmp); + return -1; + } + LOCAL_USER_ADD(u); + tele = strchr(myrpt->rxchanname,'/'); + if (!tele) + { + fprintf(stderr,"rpt:Dial number must be in format tech/number\n"); + ast_mutex_unlock(&myrpt->lock); + pthread_exit(NULL); + } + *tele++ = 0; + ast_mutex_lock(&myrpt->lock); + myrpt->rxchannel = ast_request(myrpt->rxchanname,AST_FORMAT_SLINEAR,tele); + if (myrpt->rxchannel) + { + ast_set_read_format(myrpt->rxchannel,AST_FORMAT_SLINEAR); + ast_set_write_format(myrpt->rxchannel,AST_FORMAT_SLINEAR); + myrpt->rxchannel->whentohangup = 0; + myrpt->rxchannel->appl = "Apprpt"; + myrpt->rxchannel->data = "(Repeater Rx)"; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "rpt (Rx) initiating call to %s/%s on %s\n", + myrpt->rxchanname,tele,myrpt->rxchannel->name); + ast_mutex_unlock(&myrpt->lock); + ast_call(myrpt->rxchannel,tele,999); + ast_mutex_lock(&myrpt->lock); + } + else + { + fprintf(stderr,"rpt:Sorry unable to obtain Rx channel\n"); + ast_mutex_unlock(&myrpt->lock); + pthread_exit(NULL); + } + *--tele = '/'; + if (myrpt->txchanname) + { + tele = strchr(myrpt->txchanname,'/'); + if (!tele) + { + fprintf(stderr,"rpt:Dial number must be in format tech/number\n"); + ast_mutex_unlock(&myrpt->lock); + pthread_exit(NULL); + } + *tele++ = 0; + myrpt->txchannel = ast_request(myrpt->txchanname,AST_FORMAT_SLINEAR,tele); + if (myrpt->txchannel) + { + ast_set_read_format(myrpt->txchannel,AST_FORMAT_SLINEAR); + ast_set_write_format(myrpt->txchannel,AST_FORMAT_SLINEAR); + myrpt->txchannel->whentohangup = 0; + myrpt->txchannel->appl = "Apprpt"; + myrpt->txchannel->data = "(Repeater Rx)"; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "rpt (Tx) initiating call to %s/%s on %s\n", + myrpt->txchanname,tele,myrpt->txchannel->name); + ast_mutex_unlock(&myrpt->lock); + ast_call(myrpt->txchannel,tele,999); + ast_mutex_lock(&myrpt->lock); + } + else + { + fprintf(stderr,"rpt:Sorry unable to obtain Tx channel\n"); + ast_mutex_unlock(&myrpt->lock); + pthread_exit(NULL); + } + *--tele = '/'; + } + else + { + myrpt->txchannel = myrpt->rxchannel; + } + myrpt->remoterx = 0; + myrpt->remotetx = 0; + myrpt->remoteon = 1; + ast_mutex_unlock(&myrpt->lock); + ast_set_write_format(chan, ast_best_codec(chan->nativeformats)); + ast_set_read_format(chan, ast_best_codec(chan->nativeformats)); + /* if we are on 2w loop and are a remote, turn EC on */ + if (myrpt->remote && (myrpt->rxchannel == myrpt->txchannel)) + { + i = 128; + ioctl(myrpt->rxchannel->fds[0],ZT_ECHOCANCEL,&i); + } + if (chan->_state != AST_STATE_UP) { + ast_answer(chan); + } + cs[0] = chan; + cs[1] = myrpt->rxchannel; + for(;;) + { + if (ast_check_hangup(chan)) break; + if (ast_check_hangup(myrpt->rxchannel)) break; + ms = MSWAIT; + who = ast_waitfor_n(cs,2,&ms); + if (who == NULL) ms = 0; + elap = MSWAIT - ms; + if (!ms) continue; + rem_totx = keyed; + if (rem_totx && (!myrpt->remotetx)) + { + myrpt->remotetx = 1; + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_KEY); + } + if ((!rem_totx) && myrpt->remotetx) + { + myrpt->remotetx = 0; + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY); + } + if (who == chan) /* if it was a read from incomming */ + { + f = ast_read(chan); + if (!f) + { + if (debug) printf("@@@@ link:Hung Up\n"); + break; + } + if (f->frametype == AST_FRAME_VOICE) + { + /* if not transmitting, zero-out audio */ + if (!myrpt->remotetx) + memset(f->data,0,f->datalen); + ast_write(myrpt->txchannel,f); + } + else if (f->frametype == AST_FRAME_TEXT) + { + handle_link_data(myrpt,f->data); + } + else if (f->frametype == AST_FRAME_CONTROL) + { + if (f->subclass == AST_CONTROL_HANGUP) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + ast_frfree(f); + break; + } + /* if RX key */ + if (f->subclass == AST_CONTROL_RADIO_KEY) + { + if (debug) printf("@@@@ rx key\n"); + keyed = 1; + } + /* if RX un-key */ + if (f->subclass == AST_CONTROL_RADIO_UNKEY) + { + if (debug) printf("@@@@ rx un-key\n"); + keyed = 0; + } + } + ast_frfree(f); + continue; + } + if (who == myrpt->rxchannel) /* if it was a read from radio */ + { + f = ast_read(myrpt->rxchannel); + if (!f) + { + if (debug) printf("@@@@ link:Hung Up\n"); + break; + } + if (f->frametype == AST_FRAME_VOICE) + { + if ((myrpt->remote) && (myrpt->remotetx)) + memset(f->data,0,f->datalen); + ast_write(chan,f); + } + else if (f->frametype == AST_FRAME_CONTROL) + { + if (f->subclass == AST_CONTROL_HANGUP) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + ast_frfree(f); + break; + } + /* if RX key */ + if (f->subclass == AST_CONTROL_RADIO_KEY) + { + if (debug) printf("@@@@ remote rx key\n"); + if (!myrpt->remotetx) + { + ast_indicate(chan,AST_CONTROL_RADIO_KEY); + myrpt->remoterx = 1; + } + } + /* if RX un-key */ + if (f->subclass == AST_CONTROL_RADIO_UNKEY) + { + if (debug) printf("@@@@ remote rx un-key\n"); + if (!myrpt->remotetx) + { + ast_indicate(chan,AST_CONTROL_RADIO_UNKEY); + myrpt->remoterx = 0; + } + } + } + ast_frfree(f); + continue; + } + + } + ast_mutex_lock(&myrpt->lock); + if (myrpt->rxchannel != myrpt->txchannel) ast_hangup(myrpt->txchannel); + ast_hangup(myrpt->rxchannel); + myrpt->remoteon = 0; + ast_mutex_unlock(&myrpt->lock); + LOCAL_USER_REMOVE(u); + return res; +} + int unload_module(void) { STANDARD_HANGUP_LOCALUSERS; + return ast_unregister_application(app); return 0; } int load_module(void) { pthread_create(&rpt_master_thread,NULL,rpt_master,NULL); - return 0; + return ast_register_application(app, rpt_exec, synopsis, descrip); } char *description(void) diff --git a/apps/rpt_flow.pdf b/apps/rpt_flow.pdf new file mode 100755 index 0000000000000000000000000000000000000000..2085af0f20e151e1eca7a086a976532296a4ad03 GIT binary patch literal 51935 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�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	LGywQ87Ufs%9Lb20lPTeD$!MJzE-1P~r`KrOm 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�RzO*eJ2pG~-VT}pbD&vhxTedaz* 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 HzZ(|-QbK0J literal 0 HcmV?d00001 -- GitLab