This is am.c in view mode; [Download] [Up]
/* TEXT WAS MADE WITH 4*SPACE = 1*TAB program : am.c version : see the version.h file date : Tue May 3 12:11:51 MET DST 1994 purpose : simulate an answering machine with fax- and datacall capabilities author : by jolly ( who else ) ADPCM3 support included by kiwi@belly, April '94 */ int modem; // fildes of modem int timerstatus; // sigalarm timerstatus for recording and playing int realtime; // sigalarm timer for recording and playing int connected; int amstatus; #import <libc.h> #import <mach/cthreads.h> #import "precomp.h" //extern int init_amserver(); int main(void) { int tty; signal(SIGALRM ,(void *)sig_alrm); signal(SIGINT ,(void *)sig_fatal); signal(SIGTERM ,(void *)sig_fatal); if(chdir(PATH)==-1) fatal("Can't change working directory to %s\n",PATH); /* forking server thread for status inquiries, kiwi@belly */ //cthread_detach(cthread_fork((cthread_fn_t)init_amserver, (any_t)NULL)); //tty=open("/dev/tty",O_RDWR); //ioctl(tty, TIOCNOTTY, 0); close(STD_IN); close(STD_OUT); close(STD_ERR); #if NORMALMODE tty=open("/dev/tty",O_RDWR); ioctl(tty, TIOCNOTTY, 0); #endif #if DEBUG log("*** Answering Machine started ***"); #endif amstatus = 1; #if NORMALMODE while(1) wait_for_call(); #else wait_for_call(); #endif } void wait_for_call(void) { int nfds=1; fd_set fdset; do { do { #ifndef NORMALMODE close(STD_IN); #endif open_line(DIAL_OUT); reset_modem(); unlock_line(DIAL_OUT); /* set EDR to recognize CNG (FAX) and DTMF 0 (DATACALLS) */ /*ask("ATS40=%d",OK,8+16+32+64);*/ wait_for("XXX",200); FD_ZERO(&fdset); FD_SET(modem,&fdset); #if NORMALMODE select(nfds,&fdset,NULL,NULL,NULL); #endif } while( tst_lock() && close_line(DIAL_OUT)); #if NORMALMODE if(wait_for("NO CARRIER",100)) #else if(1) #endif { #if DEBUG > 2 log("answering call"); #endif ask("AT+FCLASS=8","OK"); say("ATA"); } else { ask("AT+FCLASS=8","OK"); ask("ATS0=%d","OK",RING); } } #if NORMALMODE /* Jedes Klingeln dauert 6 Sekunden? kiwi@belly, oh Mann, diese 20000 hat mir den letzten Nerv geraubt! */ while( !wait_for("VCON",RING*6000) && close_line(DIAL_OUT) ); #else while(0); #endif main_loop(); reset_modem(); close_line(DIAL_OUT); } /*************************************************************************************nifty fncts */ /* bug fixes by kiwi@belly, Fri May 6 15:26:27 MET DST 1994 */ int rec_message(char *name) { static char *buf=NULL; FILE *feil; static struct zyxel header; static struct itimerval alrm,oalrm; int pass=0, dle_recv=0; char *i,*bend, *prev; char dle = DLE; amstatus = 2; if(buf==NULL) { buf=(char *)malloc(sizeof(char)*BSIZE); strcpy(header.title,"ZyXEL"); header.mode=ZYX_VOC; switch(SPEECH) { case ADPCM_3 :header.voice=ZYX_AD3;break; case ADPCM_2 :header.voice=ZYX_AD2;break; case CELP :header.voice=ZYX_CLP;break; default :header.voice=ZYX_AD2;break; } } if((feil=fopen(name,"w"))==NULL) fatal("Can't open File :%s",name); fwrite(&header,sizeof(struct zyxel),1,feil); ask("at+vsd=%d,%d","OK",SIL_TRES,SIL_TIME*10); /*ask("at+vvt=%d","OK",40); ask("at+vgt=%d","OK",100); seems to work only on internal speaker*/ ask(ATSP,"OK"); ask("at+vrx","CONNECT\r\n"); #if DEBUG log("recording message : %s",name); #endif siginterrupt(SIGALRM,1); alrm.it_interval.tv_sec=0; alrm.it_interval.tv_usec=1000000/RPS; alrm.it_value.tv_sec=0; alrm.it_value.tv_usec=1000000/RPS; if(setitimer(ITIMER_REAL,&alrm,&oalrm)==-1) fatal("error creating timer\n"); /* who is going to remove this timer when exit on silence or similar circumstances? kiwi@belly */ timerstatus =T_REC; realtime =0; do { do { sigpause(SIGALRM); if(timerstatus==T_STP) { fclose(feil); ask("\x10\x03","VCON"); alrm.it_interval.tv_usec=0; alrm.it_value.tv_usec=0; if(setitimer(ITIMER_REAL,&alrm,&oalrm)==-1) fatal("error creating timer\n"); return MAXTIME; } bend=buf+read(modem,buf,BSIZE); } while(bend<=buf); prev = buf; for(i=buf;i<bend;i++) { /* we have to handle a dle shielded code? */ if(dle_recv) { dle_recv = 0; /* we got it */ /* write the buffer without the last DLE */ if((i-prev-1)>0) fwrite(prev,1,i-prev-1,feil); /* previous marker gets set after <DLE>x */ prev = i+1; #if DEBUG if(*i!=DLE) log("REC time=%d %c=%x",realtime,(char)*i,(char)*i); #endif switch(*i) { case 'e' :{fclose(feil); ask("\x10\x03","VCON");return DATACALL;} /* kiwi, ROM6.12 */ case 's' :{fclose(feil); ask("\x10\x03","VCON");return SILENCE;} case 'q' :{fclose(feil); ask("\x10\x03","VCON");return QUIET;} case '*' :{fclose(feil); ask("\x10\x03","VCON");return (pass)?pass:STAR;} case '#' :pass=0;break; case '1' :pass=pass*10+1;break; case '2' :pass=pass*10+2;break; case '3' :pass=pass*10+3;break; case '4' :pass=pass*10+4;break; case '5' :pass=pass*10+5;break; case '6' :pass=pass*10+6;break; case '7' :pass=pass*10+7;break; case '8' :pass=pass*10+8;break; case '9' :pass=pass*10+9;break; case '0' :pass=pass*10;break; case 'c' :{fclose(feil); ask("\x10\x03","VCON");return FAX;} case 'b' :{fclose(feil); ask("\x10\x03","VCON");return BUSY;} case 'h' : case 'd' :{fclose(feil); ask("\x10\x03","VCON");return HANG_UP;} case ETX :{fclose(feil); ask("\x10\x03","VCON");return EOS;} case DLE :fwrite(&dle,1,1,feil); fwrite(&dle,1,1,feil);break; /* write <DLE><DLE> verbatim */ /* 'h' is not documented?!?! 'd' is new since 6.11a and means Dialtone detected */ default :log("unknown <DLE> code, time=%d %c=%x",realtime,(char)*i,(char)*i); break; } } else if(*i == DLE) dle_recv = 1; } /* closed for loop */ /* there is really something to write? if there is and no dle is received at the moment - write to end of buffer. else don't write the <DLE> which is the last char in buffer */ if(bend>prev) fwrite(prev,1,bend-prev - (dle_recv?1:0),feil); prev = buf; } while(1); return 0; // never reached } int ply_message(char *name) { char *snd; FILE *feil; int mfl; int pwd; static char a_name[200]; static struct zyxel header; strcpy(a_name,name); strcat(a_name,Z_EXT); amstatus = 3; if((feil=fopen(a_name,"r"))==NULL) { log("Can't play Message :%s",a_name); return Z_ERROR; } #if DEBUG log("Playing Message :%s",a_name); #endif fseek(feil,0L,SEEK_END); mfl=ftell(feil); rewind(feil); snd=(char *)malloc(sizeof(char)*mfl-sizeof(struct zyxel)); fread(&header,sizeof(struct zyxel),1,feil); fread(snd,1,mfl,feil); fclose(feil); switch(isZyXEL(&header)) { case ZYX_AD3 :ask("at+vsm=3","OK");break; case ZYX_AD2 :ask("at+vsm=2","OK");break; case ZYX_CLP :ask("at+vsm=1","OK");break; case ZYX_NOSNDHEADER:return log("File:%s is not a voice file",name); default :return log("File:%s voice format not supported",name); } /* set modem to send voice data */ ask("AT+VTX","CONNECT\r\n"); pwd=block_write(snd,mfl); if(pwd==FAX) { free(snd); ask("\x10\x03","VCON"); #if DEBUG log("Detected Fax while playing a message"); #endif try_fax_connection(NULL); return FAX; } ask("\x10\x03","VCON"); free(snd); return pwd; } int try_data_connection(char *rec_name) { amstatus = 4; ask("at+fclass=0","OK"); ask("at&n0&d0&c0s2=32","OK"); close_line(DIAL_OUT); open_line(DIAL_CONNECT); ask("AT&D3","OK"); say("ATA"); if( (realtime<(SIL_TIME+5)*RPS) && wait_for("CONNECT",MAX_CD*1000)) { char c=32; #if DEBUG log("Got Data Connection"); #endif remove(rec_name); write(modem,&c,1);usleep(500); write(modem,&c,1);usleep(500); write(modem,&c,1);usleep(500); wait_for("OK",2000); ask("at&c1","OK"); ask("ats2=255","OK"); say("ato"); execle("/usr/etc/getty","/usr/etc/getty",GETY,NULL,NULL); exit(1); } else { #if DEBUG log("Got no Data Connection"); #endif close_line(DIAL_CONNECT); open_line(DIAL_OUT); return shorten_file(rec_name); } return EOC; } /* shortens the recorded message file by the given silence interval value. I would like very much to add something to cut off the hangup noise at the end of a recording but I fear that a fair amount of signal processing has to be done to achieve this. This procedure was moved from inside try_data_connection, because I needed it for correct handling of <DLE>q which works since I upgraded to ROM 6.12 (it didn't work in 5.01 so _every_ recording was trying a data connection in the end and the recording got truncated appropriately. Since <DLE>q works, we have to truncate also on normal termination.) kiwi@belly */ int shorten_file(char *rec_name) { FILE *feil; char *tmp; long len; feil=fopen(rec_name,"r"); if(feil==NULL) { log("failed fopen can't open messagefile %s for reading",rec_name); return Z_ERROR; } fseek(feil,0L,SEEK_END); len=ftell(feil)-( (SIL_TIME)*BPS+(BPS/2) ); if(len<0) { fclose(feil); remove(rec_name); // never again - this fuckin people that hangup after the welcome message ! } else { rewind(feil); tmp=(char *)malloc(sizeof(char)*len+1); if(!tmp) { log("Cant alloc enough memory"); return Z_ERROR; } fread(tmp,1,len,feil); fclose(feil); remove(rec_name); feil=fopen(rec_name,"w"); if(feil==NULL) { log("failed fopen can't open messagefile %s for writing",rec_name); return Z_ERROR; } fwrite(tmp,1,len,feil); fclose(feil); free(tmp); } return EOC; } /*************************************************************************************low level fncts */ /* changed to log any ERROR message even ifndef DEBUG, kiwi@belly */ int wait_for(char *s,long t) { static char tst[100]; static char err[] = "ERROR"; static struct itimerval alrm,oalrm; int b=0,a=0,ok,flg=0,len,errok; siginterrupt(SIGALRM,1); alrm.it_interval.tv_sec=t/1000; /* changed interval timer from 0, because it might happen */ alrm.it_interval.tv_usec=(t%1000)*1000; /* (in rare circumstances) that the timer expires during */ alrm.it_value.tv_sec=t/1000; /* the handling of the input (not during the read) which */ alrm.it_value.tv_usec=(t%1000)*1000; /* would cause this function to hang forever. kiwi@belly */ if(setitimer(ITIMER_REAL,&alrm,&oalrm)==-1) fatal("error creating timer"); flg=0; len=strlen(s); tst[len]=0; ok=0; errok=0; do { a=read(modem,tst+((++b)%len),1); if(a<1) /* changed from a==-1 because a==0 means EOF (DTR dropped?). There might be more reasons why the read failed than SIGALRM, kiwi@belly. It really got stuck here!!! */ { flg=1; } else { /*log("wait for recv: %c=%x",tst[b%len],tst[b%len]);*/ if(tst[b%len]==s[ok]) ok++; else ok=0; if(tst[b%len]==err[errok]) errok++; else errok=0; if(errok == 5) { log("Waited for (%s), got ERROR time=%d",s,oalrm.it_value.tv_usec); flg = 1; } } } while(ok!=len && !flg); alrm.it_interval.tv_sec=0; alrm.it_interval.tv_usec=0; alrm.it_value.tv_sec=0; alrm.it_value.tv_usec=0; if(setitimer(ITIMER_REAL,&alrm,&oalrm)==-1) fatal("error removing timer"); #if DEBUG > 1 log("Waited for (%s) got (%s) == %s time=%d",s,tst,ok?"YES":"NO",oalrm.it_value.tv_usec); #endif return ok; } /* some variables we need for the communication between the sigalrm routine and block_write */ int modrecv_start = 0; int modrecv_end = 0; unsigned char modems_opinion[8]; /* ringbuffer for XON/XOFF requests from modem */ /* almost completely rewritten by kiwi@belly. added XON/XOFF support. Why don't we get XON/XOFF support by the tty driver when using RAW|TANDEM? */ int block_write(char * snd,int size) { int i=0,a,pass=0; static struct itimerval alrm,oalrm; char c; int en_tx = 1, dle_recv = 0; modrecv_start = modrecv_end = 0; siginterrupt(SIGALRM,1); alrm.it_interval.tv_sec=0; alrm.it_interval.tv_usec=1000000/(PPS); alrm.it_value.tv_sec=0; alrm.it_value.tv_usec=1000000/(PPS); if(setitimer(ITIMER_REAL,&alrm,&oalrm)==-1) fatal("error creating timer"); realtime =PPS; timerstatus =T_PLY; do { /* if transmission enabled */ if(en_tx) { if((size-i)>=BPP) a=write(modem,snd+i,BPP); else a=write(modem,snd+i,size-i); if(a>0) i+=a; #if DEBUG > 4 else log("block write probably interrupted by sigalrm - subject to retry"); #endif } /* got something from modem */ if(modrecv_start != modrecv_end) { c = modems_opinion[modrecv_start]; modrecv_start = (++modrecv_start) & 7; #if DEBUG > 2 log("checking modem input"); log("received from modem : %c = %x - realtime(s)=%f",c,c,1.0*realtime/PPS); #endif if(c=='\x13') { en_tx = 0; /* disable transmission */ #if DEBUG > 2 log("XOFF from modem - stopped"); #endif } else if(c=='\x11') { en_tx = 1; /* enable transmission */ #if DEBUG > 2 log("XON from modem - started"); #endif } else /* got something important? */ if(dle_recv) { #if DEBUG log("PLY time=%d %c=%x",realtime,c,c); #endif dle_recv = 0; switch(c) { case '*' :{return (pass)?pass:STAR;} case '#' :pass=0;break; case '1' :pass=pass*10+1;break; case '2' :pass=pass*10+2;break; case '3' :pass=pass*10+3;break; case '4' :pass=pass*10+4;break; case '5' :pass=pass*10+5;break; case '6' :pass=pass*10+6;break; case '7' :pass=pass*10+7;break; case '8' :pass=pass*10+8;break; case '9' :pass=pass*10+9;break; case '0' :pass=pass*10;break; case 'c' :return FAX; case 'b' :return BUSY; case 'h' : case 'd' :return HANG_UP; /* 'd' = Dialtone (6.11a) */ case 'e' :return DATACALL; /* 'e' = Data Calling Tone (6.11a) */ case ETX :return EOS; } } else if(c==DLE) dle_recv=1; } } while(i<size); /*wenn einkommentiert bleibt am gelegentlich an dieser Stelle haengen (startrek.zyxel) while(realtime*BPP<i) sigpause(SIGALRM);*/ alrm.it_interval.tv_sec=0; alrm.it_interval.tv_usec=0; alrm.it_value.tv_sec=0; alrm.it_value.tv_usec=0; if(setitimer(ITIMER_REAL,&alrm,&oalrm)==-1) fatal("error removing timer"); return EOC; } /*************************************************************************************signals */ int sig_alrm() { int val; switch(timerstatus) { case T_REC :if(realtime++>MAXRECTIME) timerstatus=T_STP;break; case T_PLY : /* In case of playing a message, the feedback from the modem gets regularily checked if modem wants a pause (XOFF). The fcntl calls make input non blocking for a moment (non blocking writes are not what we want usually) If we get something, modrecv_end gets incremented to reflect the number of characters received. modrecv_start gets incremented after the processing in block_write. kiwi@belly */ realtime++; val = fcntl(modem, F_GETFL, 0); fcntl(modem, F_SETFL, val|O_NDELAY); while (read(modem,&modems_opinion[modrecv_end],1)==1) modrecv_end = (++modrecv_end) & 7; fcntl(modem, F_SETFL, val); break; case T_FAX :realtime--;break; } return 0; } /* modified to reset modem even if signal occured during voice transmission this makes up a better testing environment where we can simply kill the job without hanging the modem kiwi@belly */ int sig_fatal() { log("sig_fatal"); ask("\x10\x03", "VCON"); ask("AT+VLS=0", "OK"); ask("AT+FCLASS=0", "OK"); fatal("Signal occured"); return 0; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.