/* rc85prg: program an RC-85 compatible controller via the phone line, using a Hayes-compatible modem. USAGE: rc85prg [options] inputfile -or- rc85prg [options] < inputfile -or- (for example) grep pattern inputfile | rc85prg [options] Input file format: Usage S nn call number Program number into autodial slot nn CL Config Lock (after a CU) CU Config unlock (to permit msg programming) CS string... Send config string to RC85 CO string... Send control-op prefix followed by config string MP Msg program (same as CS *0) MR Msg readback (same as CS *2) MA Msg abort (same as CS *4) # comment text May appear anywhere on a line Written by James Dugal, N5KNX, Aug 1989. Ver 1.1 9/89: Improved abort handling. Added copyright greeting. Ver 1.2 10/89: Added inputfile as an argument, redid option parsing. Compile in Turbo C 1.5 + by: tcc -a -G -O rc85prg.c Requires AA4RE's MBBIOS com drivers be loaded before invocation. Copyright 1989 by James P. Dugal. */ #define VERSION "1.2" #include #include #include #include /* Int 14H (serial i/o) defines */ int baudtab[8] = { 110, 150, 300, 600, 1200, 2400, 4800, 9600 }; char partab[3][2] = { 'N', 0, 'E', 0x18, 'O', 0x8 }; #define SENDST 0 #define SENDCH 1 #define GOODWRITE 0x8000 #define READCH 2 #define GOODREAD 0x9E00 #define READST 3 #define STDAV 0x0100 /* data available */ #define MBENQ 4 #define MBDRTS 5 #define MBARTS 6 #define MBSBRK 7 #define MBNDRD 8 #define MBOPTS 9 #define SHAKEBF 0x0 /* No xmit buffering, no hardware handshake, low-speed opt */ #define MBWBUF 10 #define MBRBUF 11 /* ES:DI->BUF, CX={BUFSIZ,NREAD}, AX=stats */ #define COMMENT '#' /* in input file */ /* MODEM-related commands */ #define DEFPORT "COM1:1200,7E1" #define USRHEAD "ATDT" #define USRTAIL ";" #define HANGUP "ATH0" #define OK "OK" #define USRMAXSZ 42 /* 40 + AT (spaces and CR don't count) */ /* RC85 controller-related commands */ #define DIALRC85 "5551234" /* RC85 phone number */ #define RCUNLOCK "1234567890" /* owner's unlock seq. */ #define RCLOCK "#" /* owner's lock seq. */ #define CTRLOP "123" /* control operator prefix */ #define RCHANGUP "44" /* RC85 on-hook command */ #define RCAU "47" /* autodial prog unlock */ #define RCAL "48" /* autodial prog lock */ #define RC1SLOT "*520" /* then digit */ #define RC2DFLT "456" /* then *nnxxxyyyy or nn */ #define RC3DFLT "457" /* then *nnxxxyyyy or nn */ char *me; char baudetc; char *dialup_num = DIALRC85; char *owner_code = RCUNLOCK; char *ctrlop_code = CTRLOP; char *rc2slot = RC2DFLT; char *rc3slot = RC3DFLT; int comport; int unlock = 1; /* 1 => we must unlock user autodialer */ int aunlocked = 0; /* 1 => we have unlocked the user autodialer */ int cunlocked = 0; /* 1 => we have unlocked for config commands */ int verbose = 0; int waitsec = 25; int pausesec = 4; int test = 0; char progseq[100]; /* room for building programming sequences */ /* ANSI func defs */ int main(int, char**); void programslot(int, char *, char *); void dialup(void); void hangup(void); void enter_config(void); void exit_config(void); void send(char *); int writecom(char *); int waitfor(char *, int, char *, int); void parse_args(int, char**); int parse_comport(char *); int hndlbrk(void); int hat_break(void); main(argc,argv) int argc; char **argv; { int lineno,slot,i; char buffer[256],call[10],number[30],*cp; cprintf("Version %s. Copyright 1989 by James P. Dugal. All rights reserved.\r\n", VERSION); parse_args(argc, argv); if ((unsigned)bioscom(MBENQ, 0, comport) != 0xAA55) { fprintf(stderr,"%s: MBBIOS not enabled for COM%d.\n", me, comport+1); exit(2); } (void)bioscom(SENDST, baudetc, comport); /* set baud,parity,etc */ (void)bioscom(MBOPTS, SHAKEBF, comport); /* set handshaking etc */ /* Just to ge safe we flush all input */ while (bioscom(READST, 0, comport) & STDAV) (void)bioscom(READCH, 0, comport); dialup(); ctrlbrk(hndlbrk); /* we now take over if ^C typed */ lineno = 0; while (gets(buffer) != NULL) { lineno++; if ((cp=strchr(buffer,COMMENT)) != NULL) *cp=0; /* ignore trailing comments */ cp = strupr(buffer); /* map to uppercase */ switch (*cp) { case 0: /* full-line comment, ignore it */ break; case 'S': /* Slot: ss call phonenumber */ i = sscanf(++cp, "%d\t%s\t%s", &slot, call, number); if (i != 3) goto badfmt; else programslot(slot,call,number); break; case 'C': /* Config command */ if (*++cp == 'L') exit_config(); else if (*cp == 'U') enter_config(); else if (*cp == 'S' || *cp == 'O') { /* send all after CS/CO */ if (*cp == 'S') sprintf(progseq, "%s%s#%s", USRHEAD, ++cp, USRTAIL); else sprintf(progseq, "%s%s%s#%s", USRHEAD, ctrlop_code, ++cp, USRTAIL); send(progseq); /* Auto readback takes a while, so vary sleep time */ i = pausesec + strlen(progseq)/3; if (i > 50) i = 50; /* but not too long */ sleep(i); } else goto badfmt; break; case 'M': /* Voice message command */ if (*++cp == 'P') *cp = '0'; /* program */ else if (*cp == 'R') *cp = '2'; /* readback */ else if (*cp == 'A') *cp = '4'; /* abort */ else goto badfmt; sprintf(progseq, "%s*%c#%s", USRHEAD, *cp, USRTAIL); send(progseq); sleep(pausesec+5); if (*cp != '4') sleep(5); /* longer unless abort */ break; default: /* Unknown cmd */ badfmt: fprintf(stderr,"%s: line %d: unknown or malformed command %s (ignored)\n", me, lineno, cp); break; } /* end switch */ } /* end while */ hangup(); return(0); } void programslot(slot,call,number) int slot; char *call,*number; { char progprefix[10]; char secure[2]; /* * secure code, else null */ strcpy(secure, "*"); /* default is to NOT speak the programmed number */ if (slot < 0 ) { secure[0]=0; slot = -slot; } else if (slot > 199) { fprintf(stderr,"%s: illegal slot number %d for %s (ignored)\n", me, slot, call); return; } if (slot < 10) { /* emergency slot */ if (!cunlocked) enter_config(); sprintf(progseq, "%s%s%d%s#%s", USRHEAD, RC1SLOT, slot, number, USRTAIL); send(progseq); sleep(pausesec+5); } else { if (slot < 100) /* 2-digit slot */ strcpy(progprefix,rc2slot); else /* 3-digit slot */ strcpy(progprefix,rc3slot); /* First unlock as needed */ if (cunlocked) exit_config(); if (unlock && !aunlocked) { sprintf(progseq, "%s%s%s#%s", USRHEAD, ctrlop_code, RCAU, USRTAIL); send(progseq); sleep(pausesec); /* await AU */ aunlocked++; } sprintf(progseq, "%s%s%d#%s", USRHEAD, progprefix, slot, USRTAIL); send(progseq); sleep (pausesec); /* await finish of 'autodial clear' */ sprintf(progseq, "%s%s%s%d%s#%s", USRHEAD, progprefix, secure, slot, number, USRTAIL); send(progseq); sleep (pausesec); /* await finish of 'autodial program' */ } printf("%s: did %d\t%s\t%s\n", me, slot, call, number); } void dialup() { int errcode; char dialstr[64]; sprintf(dialstr, "%s%s%s", USRHEAD, dialup_num, USRTAIL); send(dialstr); if (!test) sleep (waitsec); /* await finish of greeting msg */ } void hangup() { if (cunlocked) exit_config(); /* exit owner config mode */ if (unlock && aunlocked) { /* relock user autodialer? */ sprintf(progseq, "%s%s%s#%s", USRHEAD, ctrlop_code, RCAL, USRTAIL); send(progseq); sleep(pausesec); /* await AL */ aunlocked = 0; } sprintf(progseq, "%s%s%s#%s", USRHEAD, ctrlop_code, RCHANGUP, USRTAIL); send(progseq); sleep(pausesec); /* await 'call complete' */ send(HANGUP); /* tell modem to go onhook */ } void enter_config (void) /* enter owner config mode */ { sprintf(progseq, "%s%s#%s", USRHEAD, owner_code, USRTAIL); send(progseq); sleep(pausesec); /* await UL */ cunlocked = 1; } void exit_config(void) /* exit owner config mode */ { sprintf(progseq, "%s%s#%s", USRHEAD, RCLOCK, USRTAIL); send(progseq); sleep(pausesec); /* await LOCK */ cunlocked = 0; } void send(s_str) /* Send s_str to the comport, then read the OK */ char *s_str; { int errcode; char logstr[128]; if (writecom(s_str)) { fprintf(stderr,"%s: Unable to write %s to COM%d (aborting)\n", me, s_str, comport+1); exit (1); } errcode = waitfor(OK,pausesec+strlen(s_str)/2,logstr,sizeof(logstr)); if (errcode) { fprintf(stderr,"%s: Expected %s, got %s (aborting)\n", me, OK, logstr); exit(1); } } int writecom(w_str) /* Write w_str to the comport, return 0 if OK */ char *w_str; { int retcode; char *sp; #define CR 0x0D #define SP 0x20 if (verbose) printf("Writing %s\n", w_str); for (retcode=0, sp=w_str; *sp; sp++) if (*sp != SP && *sp != '\t') retcode++; if (retcode > USRMAXSZ) fprintf(stderr,"%s: Warning: %s may exceed modem buffer capacity.\n", me, w_str); if (test) return(0); while (*w_str) { retcode = bioscom(SENDCH, *w_str++, comport); if (retcode & GOODWRITE) return (-1); } if (*--w_str != CR) { retcode = bioscom(SENDCH, CR, comport); if (retcode & GOODWRITE) return (-1); } return (0); } int waitfor(w_str, w_time, logstr, loglen) char *w_str; /* search string */ int w_time; /* time limit (seconds) */ char *logstr; /* log string */ int loglen; /* sizeof logstr */ /* wait a max of "w_time" seconds for "w_str" to appear on comport. append all characters read to "logstr". Return 0 if w_str read successfully, -1 if timeout, else errorcode. */ { #include int strindx, strln, retval; char ch; long done; if (test) return(0); done = w_time + time((long *) 0); logstr[0] = '\0'; strindx = 0; strln = strlen(w_str); while (done > time((long *) 0)) { if (bioscom(READST, 0, comport) & STDAV) { retval = bioscom(READCH, 0, comport); if (retval & GOODREAD) /* error */ return(retval); ch = retval & 0x7f; if (strlen(logstr) < loglen) strncat(logstr, &ch, 1); if (ch == w_str[strindx]) { strindx++; if (strindx >= strln) return(0); /* all done */ } else strindx=0; /* start over (ie, ignore leading chs */ } } return(-1); /* timeout */ } void parse_args(argc, argv) /* scan command-line */ int argc; char **argv; { char *cp; int i; /* Get our entry name for use in error msgs */ me = strrchr(*argv,'/'); if (me == NULL) me = strrchr(*argv,'\\'); if (me == NULL) me=*argv; else me++; if ((cp=strchr(me,'.')) != NULL) *cp=0; /* drop trailing .EXE */ argv++; if (parse_comport(DEFPORT)) { /* init baudetc and comport */ fprintf(stderr,"%s: Illegal default port spec: %s\n", me, DEFPORT); exit(1); } while (--argc) { if (**argv == '-' ) switch (*(++*argv)) { case 'm': /* -m modem_port_spec */ ++argv; --argc; if (!argc || parse_comport(*argv)) goto usage; ++argv; break; case 'w': /* -w Nsecs */ ++argv; --argc; if (!argc) goto usage; waitsec = atoi(*argv); ++argv; break; case 'p': /* -p Nsecs */ ++argv; --argc; if (!argc) goto usage; pausesec = atoi(*argv); ++argv; break; case 'd': /* -d phonenum */ ++argv; --argc; if (!argc) goto usage; dialup_num = *argv; ++argv; break; case 'o': /* -o owner_code */ ++argv; --argc; if (!argc) goto usage; owner_code = *argv; ++argv; if (strlen(owner_code) != 10) fprintf(stderr,"%s: Warning: 10 digits expected, got %s\n", me, owner_code); break; case 'c': /* -c ctrl_op_code */ ++argv; --argc; if (!argc) goto usage; ctrlop_code = *argv; ++argv; i=strlen(ctrlop_code); if (i<1 || i>7) fprintf(stderr, "%s: Warning: ctrl op code length is illegal: %s\n", me, ctrlop_code); break; case '2': /* -2 2digit_slot_code */ ++argv; --argc; if (!argc) goto usage; rc2slot = *argv; ++argv; break; case '3': /* -3 3digit_slot_code */ ++argv; --argc; if (!argc) goto usage; rc3slot = *argv; ++argv; break; case 'v': verbose++; argv++; break; case 'u': unlock = 0; /* already unlocked */ argv++; break; case 't': test++; /* enable test mode */ argv++; break; default: usage: fprintf(stderr,"USAGE: %s [options] infile\n", me); fprintf(stderr,"where infile contains lines of the form:\n"); fprintf(stderr,"S slotnum call phonenumber\nor commands CU,CL,CS,CO,MP,MA,MR\n\n"); fprintf(stderr,"options are:\t-v for verbose mode\n"); fprintf(stderr,"\t\t-t for test mode: simulate operations\n"); fprintf(stderr,"\t\t-u if autodialer is kept unlocked\n"); fprintf(stderr,"\t\t-w N to wait N secs after dialing the RC85\n"); fprintf(stderr,"\t\t-p N to pause N secs after each command\n"); fprintf(stderr,"\t\t-m COMn:baud,8N1 to select a modem port and parameters\n"); fprintf(stderr,"\t\t-d phonenumber to override the default phone number\n"); fprintf(stderr,"\t\t-o owner_code to override the default owner unlock code\n"); fprintf(stderr,"\t\t-c ctrlop_code to override the default control op code\n"); fprintf(stderr,"\t\t-2 code to override the default 2-digit slot programming code\n"); fprintf(stderr,"\t\t-3 code to override the default 3-digit slot programming code\n"); fprintf(stderr,"defaults are -w %d -p %d -m %s -d %s -o %s -c %s -2 %s -3 %s\n", waitsec, pausesec, DEFPORT, dialup_num, owner_code, ctrlop_code, rc2slot, rc3slot); exit (1); } /* end of if () switch */ else { /* not an option, so assume is an input file */ if (freopen(*argv, "r", stdin) == NULL) { fprintf(stderr,"%s: %s: %s\n", me, *argv, strerror(errno)); exit(2); } } } /* end while (--argc) */ } int parse_comport(ap) /* ap -> "COM1:9600,8N1" */ char *ap; /* Sets global variables comport, baudetc */ { char *p, *s; int i,j; (void)strupr(ap); s = strchr(ap,':'); if (s == NULL) { fprintf(stderr,"%s: Unknown port %s\n", me, ap); return (1); } *s++ = 0; /* terminate COMx, hop to baud */ if (strncmp(ap, "COM", 3)==0) comport=atoi(ap+3) - 1; /* COMn */ else { fprintf(stderr,"%s: Unknown port %s\n",me, ap); return (1); } p = strchr(s,','); /* Where is comma separating baud from par? */ if (p == NULL) return (1); *p++ = 0; /* replace comma by EOS, point p to 8N1 string */ j = atoi(s); /* baud */ for (i=0; i < (sizeof(baudtab)/sizeof(int)); i++) if (j == baudtab[i]) { baudetc = (i << 5); break; } if (i >= sizeof(baudtab)/sizeof(int)) { fprintf(stderr, "%s: Illegal baud %s\n", me, s); return (1); } if (*p < '5' || *p > '8') { fprintf(stderr,"%s: Illegal word length %c\n", me, *p); return (1); } baudetc |= (*p - '5'); p++; for (i=0; i < 3; i++) if (*p == partab[i][0]) { baudetc |= partab[i][1]; break; } if (i >= 3) { fprintf(stderr,"%s: Unknown parity %c\n",me, *p); return (1); } p++; if (*p < '1' || *p > '2') { fprintf(stderr,"%s: Illegal stop bit %c\n", *p); return (1); } baudetc |= (*p - '1') << 2; return (0); } int hndlbrk() { char abort[] = "\rAT\r"; /* \r to terminate possible cmd */ char i; /* cancel any modem cmd in progress */ writecom(abort); /* This MAY produce an OK, OR start a cmd going! */ for (i=0; i<15; i++) { /* try 15 times */ if (!waitfor(OK, 2, "", 0)) break; /* wait 2 secs for an OK */ writecom(abort); /* try to elicit an OK again */ } hangup(); /* undo what we did so far */ return(0); /* 0 => abort */ }