3 * Asterisk -- A telephony toolkit for Linux.
5 * Radio Repeater program
7 * Copyright (C) 2002, Jim Dixon
9 * Jim Dixon <jim@lambdatel.com>
11 * This program is free software, distributed under the terms of
12 * the GNU General Public License
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>
30 #include <sys/types.h>
38 #include <sys/ioctl.h>
41 #include <linux/zaptel.h>
43 static char *tdesc = "Radio Repeater";
54 static pthread_t rpt_master_thread;
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];
76 static void *rpt_id(void *this)
78 ZT_CONFINFO ci; /* conference info */
80 struct rpt *myrpt = (struct rpt *)this;
81 struct ast_channel *mychannel;
83 /* allocate a pseudo-channel thru asterisk */
84 mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo");
87 fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
90 /* make a conference for the tx */
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)
97 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
101 ast_stopstream(mychannel);
102 res = ast_streamfile(mychannel, myrpt->idrecording, mychannel->language);
104 res = ast_waitstream(mychannel, "");
106 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
110 ast_stopstream(mychannel);
111 ast_hangup(mychannel);
115 static void *rpt_proc(void *this)
117 ZT_CONFINFO ci; /* conference info */
119 struct rpt *myrpt = (struct rpt *)this;
120 struct ast_channel *mychannel;
122 /* wait a little bit */
124 /* allocate a pseudo-channel thru asterisk */
125 mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo");
128 fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
131 /* make a conference for the tx */
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)
138 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
142 ast_stopstream(mychannel);
143 res = ast_streamfile(mychannel, "callproceeding", mychannel->language);
145 res = ast_waitstream(mychannel, "");
147 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
151 ast_stopstream(mychannel);
152 ast_hangup(mychannel);
156 static void *rpt_term(void *this)
158 ZT_CONFINFO ci; /* conference info */
160 struct rpt *myrpt = (struct rpt *)this;
161 struct ast_channel *mychannel;
163 /* wait a little bit */
165 /* allocate a pseudo-channel thru asterisk */
166 mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo");
169 fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
172 /* make a conference for the tx */
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)
179 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
183 ast_stopstream(mychannel);
184 res = ast_streamfile(mychannel, "callterminated", mychannel->language);
186 res = ast_waitstream(mychannel, "");
188 ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
192 ast_stopstream(mychannel);
193 ast_hangup(mychannel);
197 static void *rpt_call(void *this)
199 ZT_CONFINFO ci; /* conference info */
200 struct rpt *myrpt = (struct rpt *)this;
202 struct ast_frame *f,wf;
203 int stopped,congstarted;
204 struct ast_channel *mychannel,*genchannel;
207 /* allocate a pseudo-channel thru asterisk */
208 mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo");
211 fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
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)
221 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
222 ast_hangup(mychannel);
226 /* allocate a pseudo-channel thru asterisk */
227 genchannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo");
230 fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
231 ast_hangup(mychannel);
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)
241 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
242 ast_hangup(mychannel);
243 ast_hangup(genchannel);
248 if (tone_zone_play_tone(mychannel->fds[0],ZT_TONE_DIALTONE) < 0)
250 ast_log(LOG_WARNING, "Cannot start dialtone\n");
251 ast_hangup(mychannel);
252 ast_hangup(genchannel);
258 while ((myrpt->callmode == 1) || (myrpt->callmode == 4))
261 if ((myrpt->callmode == 1) && (myrpt->cidx > 0) && (!stopped))
265 tone_zone_play_tone(mychannel->fds[0],-1);
267 if ((myrpt->callmode == 4) && (!congstarted))
270 /* start congestion tone */
271 tone_zone_play_tone(mychannel->fds[0],ZT_TONE_CONGESTION);
273 res = ast_waitfor(mychannel, MSWAIT);
276 ast_hangup(mychannel);
277 ast_hangup(genchannel);
281 if (res == 0) continue;
282 f = ast_read(mychannel);
285 ast_hangup(mychannel);
286 ast_hangup(genchannel);
290 if ((f->frametype == AST_FRAME_CONTROL) &&
291 (f->subclass == AST_CONTROL_HANGUP))
294 ast_hangup(mychannel);
295 ast_hangup(genchannel);
301 /* stop any tone generation */
302 tone_zone_play_tone(mychannel->fds[0],-1);
304 if (!myrpt->callmode)
306 ast_hangup(mychannel);
307 ast_hangup(genchannel);
311 if (myrpt->ourcallerid && *myrpt->ourcallerid)
313 if (mychannel->callerid) free(mychannel->callerid);
314 mychannel->callerid = strdup(myrpt->ourcallerid);
316 strcpy(mychannel->exten,myrpt->exten);
317 strcpy(mychannel->context,myrpt->ourcontext);
319 strcpy(mychannel->accountcode,myrpt->acctcode);
320 mychannel->priority = 1;
321 ast_channel_undefer_dtmf(mychannel);
322 if (ast_pbx_start(mychannel) < 0)
324 ast_log(LOG_WARNING, "Unable to start PBX!!\n");
325 ast_hangup(mychannel);
326 ast_hangup(genchannel);
332 while(myrpt->callmode)
334 if ((!mychannel->pvt) && (myrpt->callmode != 4))
337 /* start congestion tone */
338 tone_zone_play_tone(genchannel->fds[0],ZT_TONE_CONGESTION);
342 wf.frametype = AST_FRAME_DTMF;
343 wf.subclass = myrpt->mydtmf;
349 ast_write(genchannel,&wf);
354 tone_zone_play_tone(genchannel->fds[0],-1);
355 if (mychannel->pvt) ast_softhangup(mychannel,AST_SOFTHANGUP_DEV);
356 ast_hangup(genchannel);
361 /* single thread with one file (request) to dial */
362 static void *rpt(void *this)
364 struct rpt *myrpt = (struct rpt *)this;
366 int ms = MSWAIT,lasttx,keyed,val;
367 struct ast_channel *who;
368 ZT_CONFINFO ci; /* conference info */
371 tele = strchr(myrpt->rxchanname,'/');
374 fprintf(stderr,"rpt:Dial number must be in format tech/number\n");
378 myrpt->rxchannel = ast_request(myrpt->rxchanname,AST_FORMAT_SLINEAR,tele);
379 if (myrpt->rxchannel)
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);
393 fprintf(stderr,"rpt:Sorry unable to obtain channel\n");
396 if (myrpt->txchanname)
398 tele = strchr(myrpt->txchanname,'/');
401 fprintf(stderr,"rpt:Dial number must be in format tech/number\n");
405 myrpt->txchannel = ast_request(myrpt->txchanname,AST_FORMAT_SLINEAR,tele);
406 if (myrpt->txchannel)
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);
420 fprintf(stderr,"rpt:Sorry unable to obtain channel\n");
426 myrpt->txchannel = myrpt->rxchannel;
428 /* allocate a pseudo-channel thru asterisk */
429 myrpt->pchannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo");
430 if (!myrpt->pchannel)
432 fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
435 /* make a conference for the tx */
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)
442 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
445 /* save tx conference number */
446 myrpt->txconf = ci.confno;
447 /* make a conference for the pseudo */
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)
454 ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
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
462 myrpt->tailtimer = 0;
469 ast_channel_setoption(myrpt->rxchannel,AST_OPTION_TONE_VERIFY,&val,sizeof(char),0);
471 ast_channel_setoption(myrpt->rxchannel,AST_OPTION_RELAXDTMF,&val,sizeof(char),0);
475 struct ast_channel *cs[3];
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)
486 myrpt->totimer = myrpt->totime;
489 if (totx && (!myrpt->idtimer))
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);
496 if (totx && (!lasttx))
499 ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_KEY);
501 if ((!totx) && lasttx)
504 ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY);
507 cs[0] = myrpt->rxchannel;
508 cs[1] = myrpt->pchannel;
509 cs[2] = myrpt->txchannel;
511 who = ast_waitfor_n(cs,3,&ms);
512 if (who == NULL) ms = 0;
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;
521 if (who == myrpt->rxchannel) /* if it was a read from rx */
523 f = ast_read(myrpt->rxchannel);
526 if (debug) printf("@@@@ rpt:Hung Up\n");
529 if (f->frametype == AST_FRAME_VOICE)
531 ast_write(myrpt->pchannel,f);
533 else if (f->frametype == AST_FRAME_DTMF)
537 c = (char) f->subclass; /* get DTMF char */
538 if ((!myrpt->callmode) && (c == '*'))
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);
548 if (myrpt->callmode && (c == '#'))
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);
556 if (myrpt->callmode == 1)
558 myrpt->exten[myrpt->cidx++] = c;
559 myrpt->exten[myrpt->cidx] = 0;
561 if (ast_exists_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL))
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);
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 */
574 if ((myrpt->callmode == 2) || (myrpt->callmode == 3))
576 myrpt->mydtmf = f->subclass;
579 else if (f->frametype == AST_FRAME_CONTROL)
581 if (f->subclass == AST_CONTROL_HANGUP)
583 if (debug) printf("@@@@ rpt:Hung Up\n");
588 if (f->subclass == AST_CONTROL_RADIO_KEY)
590 if (debug) printf("@@@@ rx key\n");
594 if (f->subclass == AST_CONTROL_RADIO_UNKEY)
596 if (debug) printf("@@@@ rx un-key\n");
602 if (who == myrpt->pchannel) /* if it was a read from pseudo */
604 f = ast_read(myrpt->pchannel);
607 if (debug) printf("@@@@ rpt:Hung Up\n");
610 if (f->frametype == AST_FRAME_VOICE)
612 ast_write(myrpt->txchannel,f);
614 if (f->frametype == AST_FRAME_CONTROL)
616 if (f->subclass == AST_CONTROL_HANGUP)
618 if (debug) printf("@@@@ rpt:Hung Up\n");
625 if (who == myrpt->txchannel) /* if it was a read from tx */
627 f = ast_read(myrpt->txchannel);
630 if (debug) printf("@@@@ rpt:Hung Up\n");
633 if (f->frametype == AST_FRAME_CONTROL)
635 if (f->subclass == AST_CONTROL_HANGUP)
637 if (debug) printf("@@@@ rpt:Hung Up\n");
646 ast_hangup(myrpt->pchannel);
647 ast_hangup(myrpt->rxchannel);
648 ast_hangup(myrpt->txchannel);
649 if (debug) printf("@@@@ rpt:Hung up channel\n");
654 static void *rpt_master(void *ignore)
656 struct ast_config *cfg;
660 /* start with blank config */
661 memset(&rpt_vars,0,sizeof(rpt_vars));
663 cfg = ast_load("rpt.conf");
665 ast_log(LOG_NOTICE, "Unable to open radio repeater configuration rpt.conf. Radio Repeater disabled.\n");
669 /* go thru all the specified repeaters */
672 while((this = ast_category_browse(cfg,this)) != NULL)
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;
694 ast_log(LOG_DEBUG, "Total of %d repeaters configured.\n",n);
696 for(i = 0; i < n; i++)
698 if (!rpt_vars[i].rxchanname)
700 ast_log(LOG_WARNING,"Did not specify rxchanname for repeater %s\n",rpt_vars[i].name);
703 if (!rpt_vars[i].idrecording)
705 ast_log(LOG_WARNING,"Did not specify idrecording for repeater %s\n",rpt_vars[i].name);
708 pthread_create(&rpt_vars[i].rpt_id_thread,NULL,rpt,(void *) &rpt_vars[i]);
710 /* wait for first one to die (should be never) */
711 pthread_join(rpt_vars[0].rpt_id_thread,NULL);
715 int unload_module(void)
717 STANDARD_HANGUP_LOCALUSERS;
721 int load_module(void)
723 pthread_create(&rpt_master_thread,NULL,rpt_master,NULL);
727 char *description(void)
735 STANDARD_USECOUNT(res);
741 return ASTERISK_GPL_KEY;