From 6522b1465f1334f90b2961e5a8581819012dc690 Mon Sep 17 00:00:00 2001 From: markster Date: Sun, 8 Dec 2002 17:30:24 +0000 Subject: Version 0.3.0 from FTP git-svn-id: http://svn.digium.com/svn/asterisk/trunk@565 f38db490-d61c-443f-a65b-d21fe96a405b --- apps/app_rpt.c | 742 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 742 insertions(+) create mode 100755 apps/app_rpt.c diff --git a/apps/app_rpt.c b/apps/app_rpt.c new file mode 100755 index 000000000..c99a06ae6 --- /dev/null +++ b/apps/app_rpt.c @@ -0,0 +1,742 @@ +/** @file app_rpt.c + * + * Asterisk -- A telephony toolkit for Linux. + * + * Radio Repeater program + * + * Copyright (C) 2002, Jim Dixon + * + * Jim Dixon + * + * This program is free software, distributed under the terms of + * the GNU General Public License + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *tdesc = "Radio Repeater"; +static int debug = 0; +STANDARD_LOCAL_USER; +LOCAL_USER_DECL; + +#define MSWAIT 200 +#define HANGTIME 5000 +#define TOTIME 180000 +#define IDTIME 300000 +#define MAXRPTS 20 + +static pthread_t rpt_master_thread; + +static struct rpt +{ + char *name; + char *rxchanname; + char *txchanname; + char *ourcontext; + char *ourcallerid; + char *acctcode; + char *idrecording; + int hangtime; + int totime; + int idtime; + struct ast_channel *rxchannel,*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; + 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->pconf; /* 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->terming = 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->terming = 0; + ast_stopstream(mychannel); + ast_hangup(mychannel); + pthread_exit(NULL); +} + +static void *rpt_term(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->pconf; /* 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->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_call(void *this) +{ +ZT_CONFINFO ci; /* conference info */ +struct rpt *myrpt = (struct rpt *)this; +int res; +struct ast_frame *f,wf; +int stopped,congstarted; +struct ast_channel *mychannel,*genchannel; + + myrpt->mydtmf = 0; + /* 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); + } + ci.chan = 0; + ci.confno = myrpt->pconf; /* 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 */ + if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + ast_hangup(mychannel); + myrpt->callmode = 0; + pthread_exit(NULL); + } + /* allocate a pseudo-channel thru asterisk */ + genchannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo"); + if (!genchannel) + { + fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n"); + ast_hangup(mychannel); + pthread_exit(NULL); + } + ci.chan = 0; + ci.confno = myrpt->pconf; + 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 */ + if (ioctl(genchannel->fds[0],ZT_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + ast_hangup(mychannel); + ast_hangup(genchannel); + myrpt->callmode = 0; + pthread_exit(NULL); + } + /* start dialtone */ + if (tone_zone_play_tone(mychannel->fds[0],ZT_TONE_DIALTONE) < 0) + { + ast_log(LOG_WARNING, "Cannot start dialtone\n"); + ast_hangup(mychannel); + ast_hangup(genchannel); + myrpt->callmode = 0; + pthread_exit(NULL); + } + stopped = 0; + congstarted = 0; + while ((myrpt->callmode == 1) || (myrpt->callmode == 4)) + { + + if ((myrpt->callmode == 1) && (myrpt->cidx > 0) && (!stopped)) + { + stopped = 1; + /* stop dial tone */ + tone_zone_play_tone(mychannel->fds[0],-1); + } + if ((myrpt->callmode == 4) && (!congstarted)) + { + congstarted = 1; + /* start congestion tone */ + tone_zone_play_tone(mychannel->fds[0],ZT_TONE_CONGESTION); + } + res = ast_waitfor(mychannel, MSWAIT); + if (res < 0) + { + ast_hangup(mychannel); + ast_hangup(genchannel); + myrpt->callmode = 0; + pthread_exit(NULL); + } + if (res == 0) continue; + f = ast_read(mychannel); + if (f == NULL) + { + ast_hangup(mychannel); + ast_hangup(genchannel); + myrpt->callmode = 0; + pthread_exit(NULL); + } + if ((f->frametype == AST_FRAME_CONTROL) && + (f->subclass == AST_CONTROL_HANGUP)) + { + ast_frfree(f); + ast_hangup(mychannel); + ast_hangup(genchannel); + myrpt->callmode = 0; + pthread_exit(NULL); + } + ast_frfree(f); + } + /* stop any tone generation */ + tone_zone_play_tone(mychannel->fds[0],-1); + /* end if done */ + if (!myrpt->callmode) + { + ast_hangup(mychannel); + ast_hangup(genchannel); + myrpt->callmode = 0; + pthread_exit(NULL); + } + if (myrpt->ourcallerid && *myrpt->ourcallerid) + { + if (mychannel->callerid) free(mychannel->callerid); + mychannel->callerid = strdup(myrpt->ourcallerid); + } + strcpy(mychannel->exten,myrpt->exten); + strcpy(mychannel->context,myrpt->ourcontext); + if (myrpt->acctcode) + strcpy(mychannel->accountcode,myrpt->acctcode); + mychannel->priority = 1; + ast_channel_undefer_dtmf(mychannel); + if (ast_pbx_start(mychannel) < 0) + { + ast_log(LOG_WARNING, "Unable to start PBX!!\n"); + ast_hangup(mychannel); + ast_hangup(genchannel); + myrpt->callmode = 0; + pthread_exit(NULL); + } + myrpt->callmode = 3; + + while(myrpt->callmode) + { + if ((!mychannel->pvt) && (myrpt->callmode != 4)) + { + myrpt->callmode = 4; + /* start congestion tone */ + tone_zone_play_tone(genchannel->fds[0],ZT_TONE_CONGESTION); + } + if (myrpt->mydtmf) + { + wf.frametype = AST_FRAME_DTMF; + wf.subclass = myrpt->mydtmf; + wf.offset = 0; + wf.mallocd = 0; + wf.data = NULL; + wf.datalen = 0; + wf.samples = 0; + ast_write(genchannel,&wf); + myrpt->mydtmf = 0; + } + usleep(25000); + } + tone_zone_play_tone(genchannel->fds[0],-1); + if (mychannel->pvt) ast_softhangup(mychannel,AST_SOFTHANGUP_DEV); + ast_hangup(genchannel); + myrpt->callmode = 0; + pthread_exit(NULL); +} + +/* 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; +struct ast_channel *who; +ZT_CONFINFO ci; /* conference info */ +pthread_attr_t attr; + + tele = strchr(myrpt->rxchanname,'/'); + if (!tele) + { + fprintf(stderr,"rpt:Dial number must be in format tech/number\n"); + pthread_exit(NULL); + } + *tele++ = 0; + 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_call(myrpt->rxchannel,tele,999); + } + else + { + fprintf(stderr,"rpt:Sorry unable to obtain channel\n"); + pthread_exit(NULL); + } + if (myrpt->txchanname) + { + tele = strchr(myrpt->txchanname,'/'); + if (!tele) + { + fprintf(stderr,"rpt:Dial number must be in format tech/number\n"); + 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_call(myrpt->txchannel,tele,999); + } + else + { + fprintf(stderr,"rpt:Sorry unable to obtain channel\n"); + pthread_exit(NULL); + } + } + else + { + myrpt->txchannel = myrpt->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"); + 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 */ + if (ioctl(myrpt->txchannel->fds[0],ZT_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + pthread_exit(NULL); + } + /* save tx conference number */ + myrpt->txconf = ci.confno; + /* make a conference for the pseudo */ + ci.chan = 0; + ci.confno = -1; /* make a new conf */ + ci.confmode = ZT_CONF_CONFANNMON; + /* first put the channel on the conference in announce mode */ + if (ioctl(myrpt->pchannel->fds[0],ZT_SETCONF,&ci) == -1) + { + ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n"); + pthread_exit(NULL); + } + /* save pseudo channel conference number */ + myrpt->pconf = ci.confno; + /* 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->tailtimer = 0; + myrpt->totimer = 0; + myrpt->idtimer = 0; + lasttx = 0; + keyed = 0; + myrpt->callmode = 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); + while (ms >= 0) + { + struct ast_frame *f; + struct ast_channel *cs[3]; + int totx,elap; + + totx = (keyed || myrpt->callmode || myrpt->iding || myrpt->terming); + if (!totx) myrpt->totimer = myrpt->totime; + else myrpt->tailtimer = myrpt->hangtime; + totx = (totx || myrpt->tailtimer) && myrpt->totimer; + /* if wants to transmit and in phone call, but timed out, + reset time-out timer if keyed */ + if ((!totx) && (!myrpt->totimer) && myrpt->callmode && keyed) + { + myrpt->totimer = myrpt->totime; + continue; + } + 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); + } + if (totx && (!lasttx)) + { + lasttx = 1; + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_KEY); + } + if ((!totx) && lasttx) + { + lasttx = 0; + ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY); + } + + cs[0] = myrpt->rxchannel; + cs[1] = myrpt->pchannel; + cs[2] = myrpt->txchannel; + ms = MSWAIT; + who = ast_waitfor_n(cs,3,&ms); + if (who == NULL) ms = 0; + elap = MSWAIT - ms; + 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; + if (!ms) continue; + if (who == myrpt->rxchannel) /* if it was a read from rx */ + { + f = ast_read(myrpt->rxchannel); + if (!f) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + break; + } + if (f->frametype == AST_FRAME_VOICE) + { + ast_write(myrpt->pchannel,f); + } + else if (f->frametype == AST_FRAME_DTMF) + { + char c; + + c = (char) f->subclass; /* get DTMF char */ + if ((!myrpt->callmode) && (c == '*')) + { + myrpt->callmode = 1; + myrpt->cidx = 0; + myrpt->exten[myrpt->cidx] = 0; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&myrpt->rpt_call_thread,&attr,rpt_call,(void *)myrpt); + 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; + myrpt->exten[myrpt->cidx] = 0; + /* if this exists */ + 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); + } + /* if can continue, do so */ + if (ast_canmatch_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL)) continue; + /* call has failed, inform user */ + myrpt->callmode = 4; + continue; + } + if ((myrpt->callmode == 2) || (myrpt->callmode == 3)) + { + myrpt->mydtmf = f->subclass; + } + } + 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); + } + if (who == myrpt->pchannel) /* if it was a read from pseudo */ + { + f = ast_read(myrpt->pchannel); + if (!f) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + break; + } + if (f->frametype == AST_FRAME_VOICE) + { + ast_write(myrpt->txchannel,f); + } + if (f->frametype == AST_FRAME_CONTROL) + { + if (f->subclass == AST_CONTROL_HANGUP) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + ast_frfree(f); + break; + } + } + ast_frfree(f); + } + if (who == myrpt->txchannel) /* if it was a read from tx */ + { + f = ast_read(myrpt->txchannel); + if (!f) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + break; + } + if (f->frametype == AST_FRAME_CONTROL) + { + if (f->subclass == AST_CONTROL_HANGUP) + { + if (debug) printf("@@@@ rpt:Hung Up\n"); + ast_frfree(f); + break; + } + } + ast_frfree(f); + } + + } + ast_hangup(myrpt->pchannel); + ast_hangup(myrpt->rxchannel); + ast_hangup(myrpt->txchannel); + if (debug) printf("@@@@ rpt:Hung up channel\n"); + pthread_exit(NULL); + return NULL; +} + +static void *rpt_master(void *ignore) +{ +struct ast_config *cfg; +char *this,*val; +int i,n; + + /* start with blank config */ + memset(&rpt_vars,0,sizeof(rpt_vars)); + + cfg = ast_load("rpt.conf"); + if (!cfg) { + ast_log(LOG_NOTICE, "Unable to open radio repeater configuration rpt.conf. Radio Repeater disabled.\n"); + pthread_exit(NULL); + } + + /* go thru all the specified repeaters */ + this = NULL; + n = 0; + while((this = ast_category_browse(cfg,this)) != NULL) + { + ast_log(LOG_DEBUG,"Loading config for repeater %s\n",this); + 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].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"); + val = ast_variable_retrieve(cfg,this,"hangtime"); + if (val) rpt_vars[n].hangtime = atoi(val); + else rpt_vars[n].hangtime = HANGTIME; + val = ast_variable_retrieve(cfg,this,"totime"); + if (val) rpt_vars[n].totime = atoi(val); + else rpt_vars[n].totime = TOTIME; + val = ast_variable_retrieve(cfg,this,"idtime"); + if (val) rpt_vars[n].idtime = atoi(val); + else rpt_vars[n].idtime = IDTIME; + 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); + pthread_exit(NULL); + } + if (!rpt_vars[i].idrecording) + { + ast_log(LOG_WARNING,"Did not specify idrecording for repeater %s\n",rpt_vars[i].name); + pthread_exit(NULL); + } + pthread_create(&rpt_vars[i].rpt_id_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_exit(NULL); +} + +int unload_module(void) +{ + STANDARD_HANGUP_LOCALUSERS; + return 0; +} + +int load_module(void) +{ + pthread_create(&rpt_master_thread,NULL,rpt_master,NULL); + return 0; +} + +char *description(void) +{ + return tdesc; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} -- cgit v1.2.3