Version 0.3.0 from FTP
[asterisk/asterisk.git] / apps / app_rpt.c
1 /** @file app_rpt.c 
2  *
3  * Asterisk -- A telephony toolkit for Linux.
4  *
5  * Radio Repeater program
6  * 
7  * Copyright (C) 2002, Jim Dixon
8  *
9  * Jim Dixon <jim@lambdatel.com>
10  *
11  * This program is free software, distributed under the terms of
12  * the GNU General Public License
13  *
14  */
15  
16 #include <asterisk/lock.h>
17 #include <asterisk/file.h>
18 #include <asterisk/logger.h>
19 #include <asterisk/channel.h>
20 #include <asterisk/pbx.h>
21 #include <asterisk/module.h>
22 #include <asterisk/translate.h>
23 #include <asterisk/options.h>
24 #include <asterisk/config.h>
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <pthread.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <errno.h>
33 #include <dirent.h>
34 #include <ctype.h>
35 #include <sys/stat.h>
36 #include <sys/time.h>
37 #include <sys/file.h>
38 #include <sys/ioctl.h>
39 #include <math.h>
40 #include <tonezone.h>
41 #include <linux/zaptel.h>
42
43 static  char *tdesc = "Radio Repeater";
44 static int debug = 0;
45 STANDARD_LOCAL_USER;
46 LOCAL_USER_DECL;
47
48 #define MSWAIT 200
49 #define HANGTIME 5000
50 #define TOTIME 180000
51 #define IDTIME 300000
52 #define MAXRPTS 20
53
54 static  pthread_t rpt_master_thread;
55
56 static struct rpt
57 {
58         char *name;
59         char *rxchanname;
60         char *txchanname;
61         char *ourcontext;
62         char *ourcallerid;
63         char *acctcode;
64         char *idrecording;
65         int hangtime;
66         int totime;
67         int idtime;
68         struct ast_channel *rxchannel,*txchannel,*pchannel;
69         int tailtimer,totimer,idtimer,txconf,pconf,callmode,cidx;
70         pthread_t rpt_id_thread,rpt_term_thread,rpt_proc_thread,rpt_call_thread;
71         char mydtmf,iding,terming;
72         char exten[AST_MAX_EXTENSION];
73 } rpt_vars[MAXRPTS];            
74
75
76 static void *rpt_id(void *this)
77 {
78 ZT_CONFINFO ci;  /* conference info */
79 int     res;
80 struct  rpt *myrpt = (struct rpt *)this;
81 struct ast_channel *mychannel;
82
83         /* allocate a pseudo-channel thru asterisk */
84         mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo");
85         if (!mychannel)
86         {
87                 fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
88                 pthread_exit(NULL);
89         }
90         /* make a conference for the tx */
91         ci.chan = 0;
92         ci.confno = myrpt->txconf; /* use the tx conference */
93         ci.confmode = ZT_CONF_CONFANN;
94         /* first put the channel on the conference in announce mode */
95         if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1)
96         {
97                 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
98                 pthread_exit(NULL);
99         }
100         myrpt->iding = 1;
101         ast_stopstream(mychannel);
102         res = ast_streamfile(mychannel, myrpt->idrecording, mychannel->language);
103         if (!res) 
104                 res = ast_waitstream(mychannel, "");
105         else {
106                 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
107                 res = 0;
108         }
109         myrpt->iding = 0;
110         ast_stopstream(mychannel);
111         ast_hangup(mychannel);
112         pthread_exit(NULL);
113 }
114
115 static void *rpt_proc(void *this)
116 {
117 ZT_CONFINFO ci;  /* conference info */
118 int     res;
119 struct  rpt *myrpt = (struct rpt *)this;
120 struct ast_channel *mychannel;
121
122         /* wait a little bit */
123         usleep(1500000);
124         /* allocate a pseudo-channel thru asterisk */
125         mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo");
126         if (!mychannel)
127         {
128                 fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
129                 pthread_exit(NULL);
130         }
131         /* make a conference for the tx */
132         ci.chan = 0;
133         ci.confno = myrpt->pconf; /* use the tx conference */
134         ci.confmode = ZT_CONF_CONFANN;
135         /* first put the channel on the conference in announce mode */
136         if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1)
137         {
138                 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
139                 pthread_exit(NULL);
140         }
141         myrpt->terming = 1;
142         ast_stopstream(mychannel);
143         res = ast_streamfile(mychannel, "callproceeding", mychannel->language);
144         if (!res) 
145                 res = ast_waitstream(mychannel, "");
146         else {
147                 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
148                 res = 0;
149         }
150         myrpt->terming = 0;
151         ast_stopstream(mychannel);
152         ast_hangup(mychannel);
153         pthread_exit(NULL);
154 }
155
156 static void *rpt_term(void *this)
157 {
158 ZT_CONFINFO ci;  /* conference info */
159 int     res;
160 struct  rpt *myrpt = (struct rpt *)this;
161 struct ast_channel *mychannel;
162
163         /* wait a little bit */
164         usleep(1500000);
165         /* allocate a pseudo-channel thru asterisk */
166         mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo");
167         if (!mychannel)
168         {
169                 fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
170                 pthread_exit(NULL);
171         }
172         /* make a conference for the tx */
173         ci.chan = 0;
174         ci.confno = myrpt->pconf; /* use the tx conference */
175         ci.confmode = ZT_CONF_CONFANN;
176         /* first put the channel on the conference in announce mode */
177         if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1)
178         {
179                 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
180                 pthread_exit(NULL);
181         }
182         myrpt->terming = 1;
183         ast_stopstream(mychannel);
184         res = ast_streamfile(mychannel, "callterminated", mychannel->language);
185         if (!res) 
186                 res = ast_waitstream(mychannel, "");
187         else {
188                 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
189                 res = 0;
190         }
191         myrpt->terming = 0;
192         ast_stopstream(mychannel);
193         ast_hangup(mychannel);
194         pthread_exit(NULL);
195 }
196
197 static void *rpt_call(void *this)
198 {
199 ZT_CONFINFO ci;  /* conference info */
200 struct  rpt *myrpt = (struct rpt *)this;
201 int     res;
202 struct  ast_frame *f,wf;
203 int stopped,congstarted;
204 struct ast_channel *mychannel,*genchannel;
205
206         myrpt->mydtmf = 0;
207         /* allocate a pseudo-channel thru asterisk */
208         mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo");
209         if (!mychannel)
210         {
211                 fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
212                 pthread_exit(NULL);
213         }
214         ci.chan = 0;
215         ci.confno = myrpt->pconf; /* use the pseudo conference */
216         ci.confmode = ZT_CONF_REALANDPSEUDO | ZT_CONF_TALKER | ZT_CONF_LISTENER
217                 | ZT_CONF_PSEUDO_TALKER | ZT_CONF_PSEUDO_LISTENER; 
218         /* first put the channel on the conference */
219         if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1)
220         {
221                 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
222                 ast_hangup(mychannel);
223                 myrpt->callmode = 0;
224                 pthread_exit(NULL);
225         }
226         /* allocate a pseudo-channel thru asterisk */
227         genchannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo");
228         if (!genchannel)
229         {
230                 fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
231                 ast_hangup(mychannel);
232                 pthread_exit(NULL);
233         }
234         ci.chan = 0;
235         ci.confno = myrpt->pconf;
236         ci.confmode = ZT_CONF_REALANDPSEUDO | ZT_CONF_TALKER | ZT_CONF_LISTENER
237                 | ZT_CONF_PSEUDO_TALKER | ZT_CONF_PSEUDO_LISTENER; 
238         /* first put the channel on the conference */
239         if (ioctl(genchannel->fds[0],ZT_SETCONF,&ci) == -1)
240         {
241                 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
242                 ast_hangup(mychannel);
243                 ast_hangup(genchannel);
244                 myrpt->callmode = 0;
245                 pthread_exit(NULL);
246         }
247         /* start dialtone */
248         if (tone_zone_play_tone(mychannel->fds[0],ZT_TONE_DIALTONE) < 0)
249         {
250                 ast_log(LOG_WARNING, "Cannot start dialtone\n");
251                 ast_hangup(mychannel);
252                 ast_hangup(genchannel);
253                 myrpt->callmode = 0;
254                 pthread_exit(NULL);
255         }
256         stopped = 0;
257         congstarted = 0;
258         while ((myrpt->callmode == 1) || (myrpt->callmode == 4))
259         {
260
261                 if ((myrpt->callmode == 1) && (myrpt->cidx > 0) && (!stopped))
262                 {
263                         stopped = 1;
264                         /* stop dial tone */
265                         tone_zone_play_tone(mychannel->fds[0],-1);
266                 }
267                 if ((myrpt->callmode == 4) && (!congstarted))
268                 {
269                         congstarted = 1;
270                         /* start congestion tone */
271                         tone_zone_play_tone(mychannel->fds[0],ZT_TONE_CONGESTION);
272                 }
273                 res = ast_waitfor(mychannel, MSWAIT);
274                 if (res < 0)
275                 {
276                         ast_hangup(mychannel);
277                         ast_hangup(genchannel);
278                         myrpt->callmode = 0;
279                         pthread_exit(NULL);
280                 }
281                 if (res == 0) continue;
282                 f = ast_read(mychannel);
283                 if (f == NULL) 
284                 {
285                         ast_hangup(mychannel);
286                         ast_hangup(genchannel);
287                         myrpt->callmode = 0;
288                         pthread_exit(NULL);                     
289                 }
290                 if ((f->frametype == AST_FRAME_CONTROL) &&
291                     (f->subclass == AST_CONTROL_HANGUP))
292                 {
293                         ast_frfree(f);
294                         ast_hangup(mychannel);
295                         ast_hangup(genchannel);
296                         myrpt->callmode = 0;
297                         pthread_exit(NULL);                     
298                 }
299                 ast_frfree(f);
300         }
301         /* stop any tone generation */
302         tone_zone_play_tone(mychannel->fds[0],-1);
303         /* end if done */
304         if (!myrpt->callmode)
305         {
306                 ast_hangup(mychannel);
307                 ast_hangup(genchannel);
308                 myrpt->callmode = 0;
309                 pthread_exit(NULL);                     
310         }
311         if (myrpt->ourcallerid && *myrpt->ourcallerid)
312         {
313                 if (mychannel->callerid) free(mychannel->callerid);
314                 mychannel->callerid = strdup(myrpt->ourcallerid);
315         }
316         strcpy(mychannel->exten,myrpt->exten);
317         strcpy(mychannel->context,myrpt->ourcontext);
318         if (myrpt->acctcode)
319                 strcpy(mychannel->accountcode,myrpt->acctcode);
320         mychannel->priority = 1;
321         ast_channel_undefer_dtmf(mychannel);
322         if (ast_pbx_start(mychannel) < 0)
323         {
324                 ast_log(LOG_WARNING, "Unable to start PBX!!\n");
325                 ast_hangup(mychannel);
326                 ast_hangup(genchannel);
327                 myrpt->callmode = 0;
328                 pthread_exit(NULL);
329         }
330         myrpt->callmode = 3;
331
332         while(myrpt->callmode)
333         {
334                 if ((!mychannel->pvt) && (myrpt->callmode != 4))
335                 {
336                         myrpt->callmode = 4;
337                         /* start congestion tone */
338                         tone_zone_play_tone(genchannel->fds[0],ZT_TONE_CONGESTION);
339                 }
340                 if (myrpt->mydtmf)
341                 {
342                         wf.frametype = AST_FRAME_DTMF;
343                         wf.subclass = myrpt->mydtmf;
344                         wf.offset = 0;
345                         wf.mallocd = 0;
346                         wf.data = NULL;
347                         wf.datalen = 0;
348                         wf.samples = 0;
349                         ast_write(genchannel,&wf); 
350                         myrpt->mydtmf = 0;
351                 }
352                 usleep(25000);
353         }
354         tone_zone_play_tone(genchannel->fds[0],-1);
355         if (mychannel->pvt) ast_softhangup(mychannel,AST_SOFTHANGUP_DEV);
356         ast_hangup(genchannel);
357         myrpt->callmode = 0;
358         pthread_exit(NULL);
359 }
360
361 /* single thread with one file (request) to dial */
362 static void *rpt(void *this)
363 {
364 struct  rpt *myrpt = (struct rpt *)this;
365 char *tele;
366 int ms = MSWAIT,lasttx,keyed,val;
367 struct ast_channel *who;
368 ZT_CONFINFO ci;  /* conference info */
369 pthread_attr_t attr;
370
371         tele = strchr(myrpt->rxchanname,'/');
372         if (!tele)
373         {
374                 fprintf(stderr,"rpt:Dial number must be in format tech/number\n");
375                 pthread_exit(NULL);
376         }
377         *tele++ = 0;
378         myrpt->rxchannel = ast_request(myrpt->rxchanname,AST_FORMAT_SLINEAR,tele);
379         if (myrpt->rxchannel)
380         {
381                 ast_set_read_format(myrpt->rxchannel,AST_FORMAT_SLINEAR);
382                 ast_set_write_format(myrpt->rxchannel,AST_FORMAT_SLINEAR);
383                 myrpt->rxchannel->whentohangup = 0;
384                 myrpt->rxchannel->appl = "Apprpt";
385                 myrpt->rxchannel->data = "(Repeater Rx)";
386                 if (option_verbose > 2)
387                         ast_verbose(VERBOSE_PREFIX_3 "rpt (Rx) initiating call to %s/%s on %s\n",
388                                 myrpt->rxchanname,tele,myrpt->rxchannel->name);
389                 ast_call(myrpt->rxchannel,tele,999);
390         }
391         else
392         {
393                 fprintf(stderr,"rpt:Sorry unable to obtain channel\n");
394                 pthread_exit(NULL);
395         }
396         if (myrpt->txchanname)
397         {
398                 tele = strchr(myrpt->txchanname,'/');
399                 if (!tele)
400                 {
401                         fprintf(stderr,"rpt:Dial number must be in format tech/number\n");
402                         pthread_exit(NULL);
403                 }
404                 *tele++ = 0;
405                 myrpt->txchannel = ast_request(myrpt->txchanname,AST_FORMAT_SLINEAR,tele);
406                 if (myrpt->txchannel)
407                 {
408                         ast_set_read_format(myrpt->txchannel,AST_FORMAT_SLINEAR);
409                         ast_set_write_format(myrpt->txchannel,AST_FORMAT_SLINEAR);
410                         myrpt->txchannel->whentohangup = 0;
411                         myrpt->txchannel->appl = "Apprpt";
412                         myrpt->txchannel->data = "(Repeater Rx)";
413                         if (option_verbose > 2)
414                                 ast_verbose(VERBOSE_PREFIX_3 "rpt (Tx) initiating call to %s/%s on %s\n",
415                                         myrpt->txchanname,tele,myrpt->txchannel->name);
416                         ast_call(myrpt->txchannel,tele,999);
417                 }
418                 else
419                 {
420                         fprintf(stderr,"rpt:Sorry unable to obtain channel\n");
421                         pthread_exit(NULL);
422                 }
423         }
424         else
425         {
426                 myrpt->txchannel = myrpt->rxchannel;
427         }
428         /* allocate a pseudo-channel thru asterisk */
429         myrpt->pchannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo");
430         if (!myrpt->pchannel)
431         {
432                 fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
433                 pthread_exit(NULL);
434         }
435         /* make a conference for the tx */
436         ci.chan = 0;
437         ci.confno = -1; /* make a new conf */
438         ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
439         /* first put the channel on the conference in announce mode */
440         if (ioctl(myrpt->txchannel->fds[0],ZT_SETCONF,&ci) == -1)
441         {
442                 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
443                 pthread_exit(NULL);
444         }
445         /* save tx conference number */
446         myrpt->txconf = ci.confno;
447         /* make a conference for the pseudo */
448         ci.chan = 0;
449         ci.confno = -1; /* make a new conf */
450         ci.confmode = ZT_CONF_CONFANNMON; 
451         /* first put the channel on the conference in announce mode */
452         if (ioctl(myrpt->pchannel->fds[0],ZT_SETCONF,&ci) == -1)
453         {
454                 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
455                 pthread_exit(NULL);
456         }
457         /* save pseudo channel conference number */
458         myrpt->pconf = ci.confno;
459         /* Now, the idea here is to copy from the physical rx channel buffer
460            into the pseudo tx buffer, and from the pseudo rx buffer into the 
461            tx channel buffer */
462         myrpt->tailtimer = 0;
463         myrpt->totimer = 0;
464         myrpt->idtimer = 0;
465         lasttx = 0;
466         keyed = 0;
467         myrpt->callmode = 0;
468         val = 0;
469         ast_channel_setoption(myrpt->rxchannel,AST_OPTION_TONE_VERIFY,&val,sizeof(char),0);
470         val = 1;
471         ast_channel_setoption(myrpt->rxchannel,AST_OPTION_RELAXDTMF,&val,sizeof(char),0);
472         while (ms >= 0)
473         {
474                 struct ast_frame *f;
475                 struct ast_channel *cs[3];
476                 int totx,elap;
477
478                 totx = (keyed || myrpt->callmode || myrpt->iding || myrpt->terming);
479                 if (!totx) myrpt->totimer = myrpt->totime;
480                 else myrpt->tailtimer = myrpt->hangtime;
481                 totx = (totx || myrpt->tailtimer) && myrpt->totimer;
482                 /* if wants to transmit and in phone call, but timed out, 
483                         reset time-out timer if keyed */
484                 if ((!totx) && (!myrpt->totimer) && myrpt->callmode && keyed)
485                 {
486                         myrpt->totimer = myrpt->totime;
487                         continue;
488                 }
489                 if (totx && (!myrpt->idtimer))
490                 {
491                         myrpt->idtimer = myrpt->idtime;
492                         pthread_attr_init(&attr);
493                         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
494                         pthread_create(&myrpt->rpt_id_thread,&attr,rpt_id,(void *) myrpt);
495                 }
496                 if (totx && (!lasttx))
497                 {
498                         lasttx = 1;
499                         ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_KEY);
500                 }
501                 if ((!totx) && lasttx)
502                 {
503                         lasttx = 0;
504                         ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY);
505                 }
506
507                 cs[0] = myrpt->rxchannel;
508                 cs[1] = myrpt->pchannel;
509                 cs[2] = myrpt->txchannel;
510                 ms = MSWAIT;
511                 who = ast_waitfor_n(cs,3,&ms);
512                 if (who == NULL) ms = 0;
513                 elap = MSWAIT - ms;
514                 if (myrpt->tailtimer) myrpt->tailtimer -= elap;
515                 if (myrpt->tailtimer < 0) myrpt->tailtimer = 0;
516                 if (myrpt->totimer) myrpt->totimer -= elap;
517                 if (myrpt->totimer < 0) myrpt->totimer = 0;
518                 if (myrpt->idtimer) myrpt->idtimer -= elap;
519                 if (myrpt->idtimer < 0) myrpt->idtimer = 0;
520                 if (!ms) continue;
521                 if (who == myrpt->rxchannel) /* if it was a read from rx */
522                 {
523                         f = ast_read(myrpt->rxchannel);
524                         if (!f)
525                         {
526                                 if (debug) printf("@@@@ rpt:Hung Up\n");
527                                 break;
528                         }
529                         if (f->frametype == AST_FRAME_VOICE)
530                         {
531                                 ast_write(myrpt->pchannel,f);
532                         }
533                         else if (f->frametype == AST_FRAME_DTMF)
534                         {
535                                 char c;
536
537                                 c = (char) f->subclass; /* get DTMF char */
538                                 if ((!myrpt->callmode) && (c == '*'))
539                                 {
540                                         myrpt->callmode = 1;
541                                         myrpt->cidx = 0;
542                                         myrpt->exten[myrpt->cidx] = 0;
543                                         pthread_attr_init(&attr);
544                                         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
545                                         pthread_create(&myrpt->rpt_call_thread,&attr,rpt_call,(void *)myrpt);
546                                         continue;
547                                 }
548                                 if (myrpt->callmode && (c == '#'))
549                                 {
550                                         myrpt->callmode = 0;
551                                         pthread_attr_init(&attr);
552                                         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
553                                         pthread_create(&myrpt->rpt_term_thread,&attr,rpt_term,(void *) myrpt);
554                                         continue;
555                                 }
556                                 if (myrpt->callmode == 1)
557                                 {
558                                         myrpt->exten[myrpt->cidx++] = c;
559                                         myrpt->exten[myrpt->cidx] = 0;
560                                         /* if this exists */
561                                         if (ast_exists_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL))
562                                         {
563                                                 myrpt->callmode = 2;
564                                                 pthread_attr_init(&attr);
565                                                 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
566                                                 pthread_create(&myrpt->rpt_proc_thread,&attr,rpt_proc,(void *) myrpt);
567                                         }
568                                         /* if can continue, do so */
569                                         if (ast_canmatch_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL)) continue;
570                                         /* call has failed, inform user */
571                                         myrpt->callmode = 4;
572                                         continue;
573                                 }
574                                 if ((myrpt->callmode == 2) || (myrpt->callmode == 3))
575                                 {
576                                         myrpt->mydtmf = f->subclass;
577                                 }
578                         }                                               
579                         else if (f->frametype == AST_FRAME_CONTROL)
580                         {
581                                 if (f->subclass == AST_CONTROL_HANGUP)
582                                 {
583                                         if (debug) printf("@@@@ rpt:Hung Up\n");
584                                         ast_frfree(f);
585                                         break;
586                                 }
587                                 /* if RX key */
588                                 if (f->subclass == AST_CONTROL_RADIO_KEY)
589                                 {
590                                         if (debug) printf("@@@@ rx key\n");
591                                         keyed = 1;
592                                 }
593                                 /* if RX un-key */
594                                 if (f->subclass == AST_CONTROL_RADIO_UNKEY)
595                                 {
596                                         if (debug) printf("@@@@ rx un-key\n");
597                                         keyed = 0;
598                                 }
599                         }
600                         ast_frfree(f);
601                 }
602                 if (who == myrpt->pchannel) /* if it was a read from pseudo */
603                 {
604                         f = ast_read(myrpt->pchannel);
605                         if (!f)
606                         {
607                                 if (debug) printf("@@@@ rpt:Hung Up\n");
608                                 break;
609                         }
610                         if (f->frametype == AST_FRAME_VOICE)
611                         {
612                                 ast_write(myrpt->txchannel,f);
613                         }
614                         if (f->frametype == AST_FRAME_CONTROL)
615                         {
616                                 if (f->subclass == AST_CONTROL_HANGUP)
617                                 {
618                                         if (debug) printf("@@@@ rpt:Hung Up\n");
619                                         ast_frfree(f);
620                                         break;
621                                 }
622                         }
623                         ast_frfree(f);
624                 }
625                 if (who == myrpt->txchannel) /* if it was a read from tx */
626                 {
627                         f = ast_read(myrpt->txchannel);
628                         if (!f)
629                         {
630                                 if (debug) printf("@@@@ rpt:Hung Up\n");
631                                 break;
632                         }
633                         if (f->frametype == AST_FRAME_CONTROL)
634                         {
635                                 if (f->subclass == AST_CONTROL_HANGUP)
636                                 {
637                                         if (debug) printf("@@@@ rpt:Hung Up\n");
638                                         ast_frfree(f);
639                                         break;
640                                 }
641                         }
642                         ast_frfree(f);
643                 }
644
645         }
646         ast_hangup(myrpt->pchannel);
647         ast_hangup(myrpt->rxchannel);
648         ast_hangup(myrpt->txchannel);
649         if (debug) printf("@@@@ rpt:Hung up channel\n");
650         pthread_exit(NULL);
651         return NULL;
652 }
653
654 static void *rpt_master(void *ignore)
655 {
656 struct  ast_config *cfg;
657 char *this,*val;
658 int     i,n;
659
660         /* start with blank config */
661         memset(&rpt_vars,0,sizeof(rpt_vars));
662
663         cfg = ast_load("rpt.conf");
664         if (!cfg) {
665                 ast_log(LOG_NOTICE, "Unable to open radio repeater configuration rpt.conf.  Radio Repeater disabled.\n");
666                 pthread_exit(NULL);
667         }
668
669         /* go thru all the specified repeaters */
670         this = NULL;
671         n = 0;
672         while((this = ast_category_browse(cfg,this)) != NULL)
673         {
674                 ast_log(LOG_DEBUG,"Loading config for repeater %s\n",this);
675                 rpt_vars[n].name = this;
676                 rpt_vars[n].rxchanname = ast_variable_retrieve(cfg,this,"rxchannel");
677                 rpt_vars[n].txchanname = ast_variable_retrieve(cfg,this,"txchannel");
678                 rpt_vars[n].ourcontext = ast_variable_retrieve(cfg,this,"context");
679                 if (!rpt_vars[n].ourcontext) rpt_vars[n].ourcontext = this;
680                 rpt_vars[n].ourcallerid = ast_variable_retrieve(cfg,this,"callerid");
681                 rpt_vars[n].acctcode = ast_variable_retrieve(cfg,this,"accountcode");
682                 rpt_vars[n].idrecording = ast_variable_retrieve(cfg,this,"idrecording");
683                 val = ast_variable_retrieve(cfg,this,"hangtime");
684                 if (val) rpt_vars[n].hangtime = atoi(val);
685                         else rpt_vars[n].hangtime = HANGTIME;
686                 val = ast_variable_retrieve(cfg,this,"totime");
687                 if (val) rpt_vars[n].totime = atoi(val);
688                         else rpt_vars[n].totime = TOTIME;
689                 val = ast_variable_retrieve(cfg,this,"idtime");
690                 if (val) rpt_vars[n].idtime = atoi(val);
691                         else rpt_vars[n].idtime = IDTIME;
692                 n++;
693         }
694         ast_log(LOG_DEBUG, "Total of %d repeaters configured.\n",n);
695         /* start em all */
696         for(i = 0; i < n; i++)
697         {
698                 if (!rpt_vars[i].rxchanname)
699                 {
700                         ast_log(LOG_WARNING,"Did not specify rxchanname for repeater %s\n",rpt_vars[i].name);
701                         pthread_exit(NULL);
702                 }
703                 if (!rpt_vars[i].idrecording)
704                 {
705                         ast_log(LOG_WARNING,"Did not specify idrecording for repeater %s\n",rpt_vars[i].name);
706                         pthread_exit(NULL);
707                 }
708                 pthread_create(&rpt_vars[i].rpt_id_thread,NULL,rpt,(void *) &rpt_vars[i]);
709         }
710         /* wait for first one to die (should be never) */
711         pthread_join(rpt_vars[0].rpt_id_thread,NULL);
712         pthread_exit(NULL);
713 }
714
715 int unload_module(void)
716 {
717         STANDARD_HANGUP_LOCALUSERS;
718         return 0;
719 }
720
721 int load_module(void)
722 {
723         pthread_create(&rpt_master_thread,NULL,rpt_master,NULL);
724         return 0;
725 }
726
727 char *description(void)
728 {
729         return tdesc;
730 }
731
732 int usecount(void)
733 {
734         int res;
735         STANDARD_USECOUNT(res);
736         return res;
737 }
738
739 char *key()
740 {
741         return ASTERISK_GPL_KEY;
742 }