2 * Miniphone: A simple, command line telephone
4 * IAX Support for talking to Asterisk and other Gnophone clients
6 * Copyright (C) 1999, Linux Support Services, Inc.
8 * Mark Spencer <markster@linux-support.net>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
21 #include <sys/ioctl.h>
22 #include <iax-client.h>
23 #include <linux/soundcard.h>
26 #include <sys/signal.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
32 #include <miniphone.h>
42 #define FRAME_SIZE 160
44 static char callerid[80];
51 struct iax_session *session;
55 static char *audiodev = "/dev/dsp";
56 static int audiofd = -1;
57 static struct peer *peers;
58 static int answered_call = 0;
60 static struct peer *find_peer(struct iax_session *);
61 static int audio_setup(char *);
62 static void sighandler(int);
63 static void parse_args(FILE *, unsigned char *);
64 void do_iax_event(FILE *);
65 void call(FILE *, char *);
66 void answer_call(void);
67 void reject_call(void);
68 static void handle_event(FILE *, struct iax_event *e, struct peer *p);
69 void parse_cmd(FILE *, int, char **);
70 void issue_prompt(FILE *);
71 void dump_array(FILE *, char **);
81 static int cursound = -1;
83 static int sampsent = 0;
84 static int offset = 0;
85 static int silencelen = 0;
86 static int nosound = 0;
88 static int offhook = 0;
89 static int ringing = 0;
91 static int writeonly = 0;
93 static struct iax_session *registry = NULL;
94 static struct timeval regtime;
97 #define TONE_RINGTONE 0
99 #define TONE_CONGEST 2
100 #define TONE_RINGER 3
101 #define TONE_ANSWER 4
102 #define TONE_DIALTONE 5
104 #define OUTPUT_NONE 0
105 #define OUTPUT_SPEAKER 1
106 #define OUTPUT_HANDSET 2
107 #define OUTPUT_BOTH 3
109 static struct sound sounds[] = {
110 { ringtone, sizeof(ringtone)/2, 16000, 32000, 1 },
111 { busy, sizeof(busy)/2, 4000, 4000, 1 },
112 { busy, sizeof(busy)/2, 2000, 2000, 1 },
113 { ring10, sizeof(ring10)/2, 16000, 32000, 1 },
114 { answer, sizeof(answer)/2, 2200, 0, 0 },
115 { dialtone, sizeof(dialtone)/2, 8000, 0, 1 },
118 static char *help[] = {
119 "Welcome to the miniphone telephony client, the commands are as follows:\n",
120 "Help\t\t-\tDisplays this screen.",
121 "Help <Command>\t-\tInqueries specific information on a command.",
122 "Dial <Number>\t-\tDials the number supplied in the first arguement",
123 "Status\t\t-\tLists the current sessions and their current status.",
124 "Quit\t\t-\tShuts down the client.",
129 static short silence[FRAME_SIZE];
131 static struct peer *most_recent_answer;
132 static struct iax_session *newcall = 0;
134 static struct peer *find_peer(struct iax_session *session)
136 struct peer *cur = peers;
138 if (cur->session == session)
145 static int audio_setup(char *dev)
148 int fmt = AFMT_S16_LE;
151 int fragsize = (40 << 16) | 6;
152 if ( (fd = open(dev, O_RDWR | O_NONBLOCK)) < 0) {
153 fprintf(stderr, "Unable to open audio device %s: %s\n", dev, strerror(errno));
156 if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) || (fmt != AFMT_S16_LE)) {
157 fprintf(stderr, "Unable to set in signed linear format.\n");
160 if (ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0)) {
161 fprintf(stderr, "Unable to set full duplex operation.\n");
165 if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) || (channels != 1)) {
166 fprintf(stderr, "Unable to set to mono\n");
169 if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) || (speed != 8000)) {
170 fprintf(stderr, "Unable to set speed to 8000 hz\n");
173 if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &fragsize)) {
174 fprintf(stderr, "Unable to set fragment size...\n");
181 static int send_sound(int soundfd)
183 /* Send FRAME_SIZE samples of whatever */
184 short myframe[FRAME_SIZE];
186 int total = FRAME_SIZE;
192 res = ioctl(soundfd, SNDCTL_DSP_GETOSPACE ,&abi);
194 fprintf(stderr,"Unable to read output space\n");
197 /* Calculate how many samples we can send, max */
198 if (total > (abi.fragments * abi.fragsize / 2))
199 total = abi.fragments * abi.fragsize / 2;
201 if (sampsent < sounds[cursound].samplen) {
205 if (amt > (sounds[cursound].datalen - offset))
206 amt = sounds[cursound].datalen - offset;
207 memcpy(myframe + myoff, sounds[cursound].data + offset, amt * 2);
212 if (offset >= sounds[cursound].datalen)
215 /* Set it up for silence */
216 if (sampsent >= sounds[cursound].samplen)
217 silencelen = sounds[cursound].silencelen;
220 if (silencelen > 0) {
224 if (sounds[cursound].repeat) {
236 printf("res is %d, frame[0] is %d\n", res, frame[0]);
238 res = write(soundfd, frame, res * 2);
246 static int iax_regtimeout(int timeout)
249 gettimeofday(®time, NULL);
250 regtime.tv_sec += timeout;
258 static int check_iax_register(void)
261 if (strlen(regpeer) && strlen(server)) {
262 registry = iax_session_new();
264 res = iax_register(registry, server,regpeer,regsecret, refresh);
267 fprintf(stderr, "Failed registration: %s\n", iax_errstr);
270 iax_regtimeout(5 * refresh / 6);
278 static int check_iax_timeout(void)
282 if (!regtime.tv_usec || !regtime.tv_sec)
284 gettimeofday(&tv, NULL);
285 if ((tv.tv_usec >= regtime.tv_usec) && (tv.tv_sec >= regtime.tv_sec)) {
286 check_iax_register();
287 /* Have it check again soon */
290 ms = (regtime.tv_sec - tv.tv_sec) * 1000 + (regtime.tv_usec - tv.tv_usec) / 1000;
294 static int gentone(int sound, int uninterruptible)
300 nosound = uninterruptible;
301 printf("Sending tone %d\n", sound);
310 } else if(sig == SIGINT) {
314 if ( (cur = time(0))-prev <= 5) {
315 printf("Terminating!\n");
319 printf("Press interrupt key again in the next %d seconds to really terminate\n", 5-(cur-prev));
325 parse_args(FILE *f, unsigned char *cmd)
327 static char *argv[MAXARGS];
328 unsigned char *parse = cmd;
331 // Don't mess with anything that doesn't exist...
335 bzero(argv, sizeof(argv));
337 if(*parse < 33 || *parse > 128) {
340 fprintf(f, "Warning: Argument exceeds maximum argument size, command ignored!\n");
343 } else if(t || !argc) {
344 if(argc == MAXARGS) {
345 fprintf(f, "Warning: Command ignored, too many arguments\n");
348 argv[argc++] = parse;
356 parse_cmd(f, argc, argv);
360 main(int argc, char *argv[])
366 int fd = STDIN_FILENO;
370 struct timeval timer;
371 struct timeval *timerptr = NULL;
376 if (!strlen(callerid))
377 gethostname(callerid, sizeof(callerid));
379 signal(SIGHUP, sighandler);
380 signal(SIGINT, sighandler);
382 if ( !(f = fdopen(fd, "w+"))) {
383 fprintf(stderr, "Unable to create file on fd %d\n", fd);
387 if ( (audiofd = audio_setup(audiodev)) == -1) {
388 fprintf(stderr, "Fatal error: failed to open sound device");
392 if ( (port = iax_init(0) < 0)) {
393 fprintf(stderr, "Fatal error: failed to initialize iax with port %d\n", port);
397 iax_set_formats(AST_FORMAT_GSM);
398 netfd = iax_get_fd();
400 check_iax_register();
402 fprintf(f, "Text Based Telephony Client.\n\n");
414 if(answered_call && !writeonly) {
415 FD_SET(audiofd, &readfd);
420 FD_SET(audiofd, &writefd);
424 FD_SET(netfd, &readfd);
428 if ( (c = select(h+1, &readfd, &writefd, 0, timerptr)) >= 0) {
429 if(FD_ISSET(fd, &readfd)) {
430 if ( ( fgets(&*rcmd, 256, f))) {
431 rcmd[strlen(rcmd)-1] = 0;
432 parse_args(f, &*rcmd);
433 } else fprintf(f, "Fatal error: failed to read data!\n");
438 if(FD_ISSET(audiofd, &readfd)) {
439 static int ret, rlen = 0;
440 static short rbuf[FRAME_SIZE];
442 if ( (ret = read(audiofd, rbuf + rlen, 2 * (FRAME_SIZE-rlen))) == -1) {
443 puts("Failed to read audio.");
447 if(rlen == FRAME_SIZE) {
450 if(!most_recent_answer->gsmout)
451 most_recent_answer->gsmout = gsm_create();
453 gsm_encode(most_recent_answer->gsmout, rbuf, fo);
454 if(iax_send_voice(most_recent_answer->session,
455 AST_FORMAT_GSM, (char *)fo, sizeof(fo)) == -1)
456 puts("Failed to send voice!");
461 m = iax_time_to_next_event();
464 timer.tv_sec = m /1000;
465 timer.tv_usec = (m % 1000) * 1000;
468 regm = check_iax_timeout();
469 if (!timerptr || (m > regm)) {
471 timer.tv_sec = regm /1000;
472 timer.tv_usec = (regm % 1000) * 1000;
474 if (FD_ISSET(audiofd, &writefd)) {
480 fprintf(stderr, "Fatal error in select(): %s\n", strerror(errno));
488 do_iax_event(FILE *f) {
490 struct iax_event *e = 0;
493 while ( (e = iax_get_event(0))) {
494 peer = find_peer(e->session);
496 handle_event(f, e, peer);
497 } else if (e->session == registry) {
498 fprintf(stderr, "Registration complete: %s (%d)\n",
499 (e->event.regreply.status == IAX_REG_SUCCESS) ? "Success" : "Failed",
500 e->event.regreply.status);
503 if(e->etype != IAX_EVENT_CONNECT) {
504 fprintf(stderr, "Huh? This is an event for a non-existant session?\n");
509 if(sessions >= MAX_SESSIONS) {
510 fprintf(f, "Missed a call... too many sessions open.\n");
514 if(e->event.connect.callerid && e->event.connect.dnid)
515 fprintf(f, "Call from '%s' for '%s'", e->event.connect.callerid,
516 e->event.connect.dnid);
517 else if(e->event.connect.dnid) {
518 fprintf(f, "Call from '%s'", e->event.connect.dnid);
519 } else if(e->event.connect.callerid) {
520 fprintf(f, "Call from '%s'", e->event.connect.callerid);
521 } else printf("Call from");
522 fprintf(f, " (%s)\n", inet_ntoa(iax_get_peer_addr(e->session).sin_addr));
524 if(most_recent_answer) {
525 fprintf(f, "Incoming call ignored, there's already a call waiting for answer... \
526 please accept or reject first\n");
527 iax_reject(e->session, "Too many calls, we're busy!");
529 if ( !(peer = malloc(sizeof(struct peer)))) {
530 fprintf(f, "Warning: Unable to allocate memory!\n");
534 peer->time = time(0);
535 peer->session = e->session;
546 iax_accept(peer->session);
547 iax_ring_announce(peer->session);
548 most_recent_answer = peer;
550 gentone(TONE_RINGER, 0);
551 fprintf(f, "Incoming call!\n");
560 call(FILE *f, char *num)
565 newcall = iax_session_new();
567 fprintf(f, "Already attempting to call somewhere, please cancel first!\n");
571 if ( !(peer = malloc(sizeof(struct peer)))) {
572 fprintf(f, "Warning: Unable to allocate memory!\n");
576 peer->time = time(0);
577 peer->session = newcall;
584 most_recent_answer = peer;
588 iax_call(peer->session, callerid, num, NULL, 10);
594 if(most_recent_answer)
595 iax_answer(most_recent_answer->session);
596 printf("Answering call!\n");
600 gentone(TONE_ANSWER, 1);
606 iax_reject(most_recent_answer->session, "Call rejected manually.");
607 most_recent_answer = 0;
609 gentone(TONE_NONE, 1);
613 handle_event(FILE *f, struct iax_event *e, struct peer *p)
615 short fr[FRAME_SIZE];
619 case IAX_EVENT_HANGUP:
620 iax_hangup(most_recent_answer->session, "Byeee!");
621 fprintf(f, "Call disconnected by peer\n");
622 free(most_recent_answer);
623 most_recent_answer = 0;
628 gentone(TONE_CONGEST, 0);
631 case IAX_EVENT_REJECT:
632 fprintf(f, "Authentication was rejected\n");
634 case IAX_EVENT_ACCEPT:
635 fprintf(f, "Accepted...\n");
638 case IAX_EVENT_RINGA:
639 fprintf(f, "Ringing...\n");
641 gentone(TONE_RINGTONE, 0);
643 case IAX_EVENT_ANSWER:
645 gentone(TONE_ANSWER, 1);
647 case IAX_EVENT_VOICE:
648 switch(e->event.voice.format) {
650 if(e->event.voice.datalen % 33) {
651 fprintf(stderr, "Weird gsm frame, not a multiple of 33.\n");
656 p->gsmin = gsm_create();
659 while(len < e->event.voice.datalen) {
660 if(gsm_decode(p->gsmin, e->event.voice.data + len, fr)) {
661 fprintf(stderr, "Bad GSM data\n");
666 res = write(audiofd, fr, sizeof(fr));
668 fprintf(f, "Write failed: %s\n", strerror(errno));
674 fprintf(f, "Don't know how to handle that format %d\n", e->event.voice.format);
678 fprintf(f, "Unknown event: %d\n", e->etype);
685 if(most_recent_answer)
687 printf("Dumping call!\n");
688 iax_hangup(most_recent_answer->session,"");
689 free(most_recent_answer);
692 most_recent_answer = 0;
698 gentone(TONE_NONE, 0);
702 parse_cmd(FILE *f, int argc, char **argv)
704 if(!strcasecmp(argv[0], "HELP")) {
708 if(!strcasecmp(argv[1], "HELP"))
709 fprintf(f, "Help <Command>\t-\tDisplays general help or specific help on command if supplied an arguement\n");
710 else if(!strcasecmp(argv[1], "QUIT"))
711 fprintf(f, "Quit\t\t-\tShuts down the miniphone\n");
712 else fprintf(f, "No help available on %s\n", argv[1]);
714 fprintf(f, "Too many arguements for command help.\n");
716 } else if(!strcasecmp(argv[0], "STATUS")) {
719 struct peer *peerptr = peers;
722 fprintf(f, "No session matches found.\n");
723 else while(peerptr) {
724 fprintf(f, "Listing sessions:\n\n");
725 fprintf(f, "Session %d\n", ++c);
726 fprintf(f, "Session existed for %d seconds\n", (int)time(0)-peerptr->time);
728 fprintf(f, "Call answered.\n");
729 else fprintf(f, "Call ringing.\n");
731 peerptr = peerptr->next;
733 } else fprintf(f, "Too many arguments for command status.\n");
734 } else if(!strcasecmp(argv[0], "ANSWER")) {
736 fprintf(f, "Too many arguements for command answer\n");
738 } else if(!strcasecmp(argv[0], "REJECT")) {
740 fprintf(f, "Too many arguements for command reject\n");
742 fprintf(f, "Rejecting current phone call.\n");
745 } else if (!strcasecmp(argv[0], "DUMP")) {
747 } else if (!strcasecmp(argv[0], "HANGUP")) {
749 } else if(!strcasecmp(argv[0], "CALL")) {
751 fprintf(f, "Too many arguements for command call\n");
755 } else if(!strcasecmp(argv[0], "QUIT")) {
757 fprintf(f, "Too many arguements for command quit\n");
759 fprintf(f, "Good bye!\n");
762 } else fprintf(f, "Unknown command of %s\n", argv[0]);
766 issue_prompt(FILE *f)
768 fprintf(f, "TeleClient> ");
773 dump_array(FILE *f, char **array) {
775 fprintf(f, "%s\n", *array++);