home *** CD-ROM | disk | FTP | other *** search
/ HAM Radio 1 / HamRadio.cdr / misc / rc85_prg / rc85prg.c < prev    next >
C/C++ Source or Header  |  1989-10-11  |  16KB  |  610 lines

  1. /* rc85prg: program an RC-85 compatible controller
  2.     via the phone line, using a Hayes-compatible modem.
  3.  
  4.    USAGE: rc85prg [options] inputfile
  5.         -or- rc85prg [options] < inputfile
  6.        -or- (for example) grep pattern inputfile | rc85prg [options]
  7.  
  8.    Input file format:        Usage
  9.    S nn  call  number           Program number into autodial slot nn
  10.    CL                Config Lock (after a CU)
  11.    CU                Config unlock (to permit msg programming)
  12.    CS string...            Send config string to RC85
  13.    CO string...            Send control-op prefix followed by config string
  14.    MP                Msg program (same as CS *0)
  15.    MR                Msg readback (same as CS *2)
  16.    MA                Msg abort (same as CS *4)
  17.    # comment text        May appear anywhere on a line
  18.  
  19.    Written by James Dugal, N5KNX, Aug 1989.
  20.    Ver 1.1 9/89: Improved abort handling. Added copyright greeting.
  21.    Ver 1.2 10/89: Added inputfile as an argument, redid option parsing.
  22.  
  23.    Compile in Turbo C 1.5 + by: tcc -a -G -O rc85prg.c
  24.    Requires AA4RE's MBBIOS com drivers be loaded before invocation.
  25.  
  26.    Copyright 1989 by James P. Dugal.
  27. */
  28.  
  29. #define VERSION "1.2"
  30.  
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <bios.h>
  34. #include <string.h>
  35.  
  36.  
  37. /* Int 14H (serial i/o) defines */
  38.  
  39. int baudtab[8] = { 110, 150, 300, 600, 1200, 2400, 4800, 9600 };
  40. char partab[3][2] = { 'N', 0, 'E', 0x18, 'O', 0x8 };
  41.  
  42. #define SENDST 0
  43. #define SENDCH 1
  44. #define GOODWRITE 0x8000
  45. #define READCH 2
  46. #define GOODREAD 0x9E00
  47. #define READST 3
  48. #define STDAV    0x0100    /* data available */
  49. #define MBENQ  4
  50. #define MBDRTS 5
  51. #define MBARTS 6
  52. #define MBSBRK 7
  53. #define MBNDRD 8
  54. #define MBOPTS 9
  55. #define SHAKEBF 0x0    /* No xmit buffering, no hardware handshake, low-speed opt */
  56.  
  57. #define MBWBUF 10
  58. #define MBRBUF 11    /* ES:DI->BUF, CX={BUFSIZ,NREAD}, AX=stats */
  59.  
  60.  
  61. #define COMMENT    '#'    /* in input file */
  62.  
  63.  
  64. /* MODEM-related commands */
  65. #define DEFPORT        "COM1:1200,7E1"
  66. #define USRHEAD        "ATDT"
  67. #define USRTAIL        ";"
  68. #define HANGUP        "ATH0"
  69. #define OK        "OK"
  70. #define USRMAXSZ    42    /* 40 + AT (spaces and CR don't count) */
  71.  
  72. /* RC85 controller-related commands */
  73. #define DIALRC85    "5551234"    /* RC85 phone number */
  74. #define RCUNLOCK    "1234567890"    /* owner's unlock seq. */
  75. #define RCLOCK        "#"        /* owner's lock seq. */
  76.  
  77. #define CTRLOP        "123"    /* control operator prefix */
  78. #define RCHANGUP    "44"    /* RC85 on-hook command */
  79. #define RCAU        "47"    /* autodial prog unlock */
  80. #define RCAL        "48"    /* autodial prog lock */
  81.  
  82. #define RC1SLOT        "*520"    /* then digit */
  83. #define RC2DFLT        "456"    /* then *nnxxxyyyy or nn */
  84. #define RC3DFLT        "457"    /* then *nnxxxyyyy or nn */
  85.  
  86. char    *me;
  87. char    baudetc;
  88. char    *dialup_num = DIALRC85;
  89. char    *owner_code = RCUNLOCK;
  90. char    *ctrlop_code = CTRLOP;
  91. char    *rc2slot = RC2DFLT;
  92. char    *rc3slot = RC3DFLT;
  93.  
  94. int    comport;
  95. int    unlock = 1;    /* 1 => we must unlock user autodialer */
  96. int    aunlocked = 0;    /* 1 => we have unlocked the user autodialer */
  97. int    cunlocked = 0;    /* 1 => we have unlocked for config commands */
  98. int    verbose = 0;
  99. int    waitsec = 25;
  100. int    pausesec = 4;
  101. int    test = 0;
  102.  
  103. char    progseq[100];    /* room for building programming sequences */
  104.  
  105. /* ANSI func defs */
  106. int main(int, char**);
  107. void programslot(int, char *, char *);
  108. void dialup(void);
  109. void hangup(void);
  110. void enter_config(void);
  111. void exit_config(void);
  112. void send(char *);
  113. int writecom(char *);
  114. int waitfor(char *, int, char *, int);
  115. void parse_args(int, char**);
  116. int parse_comport(char *);
  117. int hndlbrk(void);
  118. int hat_break(void);
  119.  
  120.  
  121. main(argc,argv)
  122. int argc;
  123. char **argv;
  124. {
  125.     int lineno,slot,i;
  126.     char buffer[256],call[10],number[30],*cp;
  127.  
  128.     cprintf("Version %s. Copyright 1989 by James P. Dugal.  All rights reserved.\r\n", VERSION);
  129.     parse_args(argc, argv);
  130.  
  131.     if ((unsigned)bioscom(MBENQ, 0, comport) != 0xAA55) {
  132.         fprintf(stderr,"%s: MBBIOS not enabled for COM%d.\n",
  133.             me, comport+1);
  134.         exit(2);
  135.     }
  136.  
  137.     (void)bioscom(SENDST, baudetc, comport);    /* set baud,parity,etc */
  138.     (void)bioscom(MBOPTS, SHAKEBF, comport);    /* set handshaking etc */
  139.  
  140.     /* Just to ge safe we flush all input */
  141.     while (bioscom(READST, 0, comport) & STDAV)
  142.         (void)bioscom(READCH, 0, comport);
  143.  
  144.  
  145.     dialup();
  146.     ctrlbrk(hndlbrk);    /* we now take over if ^C typed */
  147.  
  148.     lineno = 0;
  149.     while (gets(buffer) != NULL) {
  150.         lineno++;
  151.         if ((cp=strchr(buffer,COMMENT)) != NULL) *cp=0;    /* ignore trailing comments */
  152.         cp = strupr(buffer);    /* map to uppercase */
  153.  
  154.         switch (*cp) {
  155.  
  156.         case 0:        /* full-line comment, ignore it */
  157.             break;
  158.  
  159.         case 'S':    /* Slot:  ss  call  phonenumber */
  160.             i = sscanf(++cp, "%d\t%s\t%s", &slot, call, number);
  161.             if (i != 3) goto badfmt;
  162.             else programslot(slot,call,number);
  163.             break;
  164.  
  165.         case 'C':    /* Config command */
  166.             if (*++cp == 'L') exit_config();
  167.             else if (*cp == 'U') enter_config();
  168.             else if (*cp == 'S' || *cp == 'O') {    /* send all after CS/CO */
  169.                 if (*cp == 'S') sprintf(progseq, "%s%s#%s", USRHEAD, ++cp, USRTAIL);
  170.                 else sprintf(progseq, "%s%s%s#%s", USRHEAD, ctrlop_code, ++cp, USRTAIL);
  171.                 send(progseq);
  172.                 /* Auto readback takes a while, so vary sleep time */
  173.                 i = pausesec + strlen(progseq)/3;
  174.                 if (i > 50) i = 50;    /* but not too long */
  175.                 sleep(i);
  176.             }
  177.             else goto badfmt;
  178.             break;
  179.  
  180.         case 'M':    /* Voice message command */
  181.             if (*++cp == 'P') *cp = '0';    /* program */
  182.             else if (*cp == 'R') *cp = '2';    /* readback */
  183.             else if (*cp == 'A') *cp = '4';    /* abort */
  184.             else goto badfmt;
  185.  
  186.             sprintf(progseq, "%s*%c#%s", USRHEAD, *cp, USRTAIL);
  187.             send(progseq);
  188.             sleep(pausesec+5);
  189.             if (*cp != '4') sleep(5);    /* longer unless abort */
  190.             break;
  191.  
  192.         default:    /* Unknown cmd */
  193.         badfmt:
  194.             fprintf(stderr,"%s: line %d: unknown or malformed command %s (ignored)\n",
  195.                 me, lineno, cp);
  196.             break;
  197.         } /* end switch */
  198.     } /* end while */
  199.  
  200.  
  201.     hangup();
  202.  
  203.  
  204.     return(0);
  205. }
  206.  
  207. void programslot(slot,call,number)
  208. int slot;
  209. char *call,*number;
  210. {
  211.     char    progprefix[10];
  212.     char    secure[2];    /* * secure code, else null */
  213.  
  214.     strcpy(secure, "*");    /* default is to NOT speak the programmed number */
  215.     if (slot < 0 ) {
  216.         secure[0]=0;
  217.         slot = -slot;
  218.     }
  219.         
  220.     else if (slot > 199) {
  221.         fprintf(stderr,"%s: illegal slot number %d for %s (ignored)\n",
  222.             me, slot, call);
  223.         return;
  224.     }
  225.  
  226.     if (slot < 10) {        /* emergency slot */
  227.         if (!cunlocked) enter_config();
  228.  
  229.         sprintf(progseq, "%s%s%d%s#%s", USRHEAD, RC1SLOT, slot, number, USRTAIL);
  230.         send(progseq);
  231.         sleep(pausesec+5);
  232.     }
  233.     else {
  234.         if (slot < 100)     /* 2-digit slot */
  235.             strcpy(progprefix,rc2slot);
  236.         else     /* 3-digit slot */
  237.             strcpy(progprefix,rc3slot);
  238.  
  239.         /* First unlock as needed */
  240.         if (cunlocked) exit_config();
  241.         if (unlock && !aunlocked) {
  242.             sprintf(progseq, "%s%s%s#%s", USRHEAD, ctrlop_code, RCAU, USRTAIL);
  243.             send(progseq);
  244.             sleep(pausesec);    /* await AU */
  245.             aunlocked++;
  246.         }
  247.  
  248.         sprintf(progseq, "%s%s%d#%s",
  249.             USRHEAD, progprefix, slot, USRTAIL);
  250.         send(progseq);
  251.         sleep (pausesec);    /* await finish of 'autodial clear' */
  252.  
  253.         sprintf(progseq, "%s%s%s%d%s#%s",
  254.             USRHEAD, progprefix, secure, slot, number, USRTAIL);
  255.         send(progseq);
  256.         sleep (pausesec);    /* await finish of 'autodial program' */
  257.     }
  258.  
  259.     printf("%s: did %d\t%s\t%s\n", me, slot, call, number);
  260. }
  261.  
  262. void dialup()
  263. {
  264.     int    errcode;
  265.     char    dialstr[64];
  266.  
  267.     sprintf(dialstr, "%s%s%s", USRHEAD, dialup_num, USRTAIL);
  268.     send(dialstr);
  269.     if (!test) sleep (waitsec);    /* await finish of greeting msg */
  270. }
  271.  
  272. void hangup()
  273. {
  274.     if (cunlocked) exit_config();    /* exit owner config mode */
  275.  
  276.     if (unlock && aunlocked) {    /* relock user autodialer? */
  277.         sprintf(progseq, "%s%s%s#%s", USRHEAD, ctrlop_code, RCAL, USRTAIL);
  278.         send(progseq);
  279.         sleep(pausesec);    /* await AL */
  280.         aunlocked = 0;
  281.     }
  282.  
  283.     sprintf(progseq, "%s%s%s#%s", USRHEAD, ctrlop_code, RCHANGUP, USRTAIL);
  284.     send(progseq);
  285.     sleep(pausesec);    /* await 'call complete' */
  286.  
  287.     send(HANGUP);    /* tell modem to go onhook */
  288. }
  289.  
  290.  
  291. void enter_config (void)    /* enter owner config mode */
  292. {
  293.     sprintf(progseq, "%s%s#%s", USRHEAD, owner_code, USRTAIL);
  294.     send(progseq);
  295.     sleep(pausesec);    /* await UL */
  296.     cunlocked = 1;
  297. }
  298.  
  299.  
  300. void exit_config(void)    /* exit owner config mode */
  301. {
  302.     sprintf(progseq, "%s%s#%s", USRHEAD, RCLOCK, USRTAIL);
  303.     send(progseq);
  304.     sleep(pausesec);    /* await LOCK */
  305.     cunlocked = 0;
  306. }
  307.  
  308.  
  309. void send(s_str)    /* Send s_str to the comport, then read the OK */
  310. char    *s_str;
  311. {
  312.     int    errcode;
  313.     char    logstr[128];
  314.  
  315.     if (writecom(s_str)) {
  316.         fprintf(stderr,"%s: Unable to write %s to COM%d (aborting)\n",
  317.             me, s_str, comport+1);
  318.         exit (1);
  319.     }
  320.  
  321.     errcode = waitfor(OK,pausesec+strlen(s_str)/2,logstr,sizeof(logstr));
  322.     if (errcode) {
  323.         fprintf(stderr,"%s: Expected %s, got %s (aborting)\n",
  324.             me, OK, logstr);
  325.         exit(1);
  326.     }
  327. }
  328.  
  329.  
  330. int writecom(w_str)        /* Write w_str to the comport, return 0 if OK */
  331. char *w_str;
  332. {
  333.     int    retcode;
  334.     char    *sp;
  335.     #define CR    0x0D
  336.     #define SP    0x20
  337.  
  338.     if (verbose) printf("Writing %s\n", w_str);
  339.     for (retcode=0, sp=w_str; *sp; sp++)
  340.         if (*sp != SP && *sp != '\t') retcode++;
  341.     if (retcode > USRMAXSZ) fprintf(stderr,"%s: Warning: %s may exceed modem buffer capacity.\n",
  342.                     me, w_str);
  343.  
  344.     if (test) return(0);
  345.  
  346.     while (*w_str) {
  347.         retcode = bioscom(SENDCH, *w_str++, comport);
  348.         if (retcode & GOODWRITE) return (-1);
  349.     }
  350.     if (*--w_str != CR) {
  351.         retcode = bioscom(SENDCH, CR, comport);
  352.         if (retcode & GOODWRITE) return (-1);
  353.     }
  354.     return (0);
  355. }
  356.  
  357.  
  358. int waitfor(w_str, w_time, logstr, loglen)
  359. char    *w_str;        /* search string */
  360. int    w_time;        /* time limit (seconds) */
  361. char    *logstr;    /* log string */
  362. int    loglen;        /* sizeof logstr */
  363.  
  364. /* wait a max of "w_time" seconds for "w_str" to appear on comport.
  365.    append all characters read to "logstr".
  366.    Return 0 if w_str read successfully, -1 if timeout, else errorcode.
  367. */
  368.  
  369. {
  370. #include <time.h>
  371.     int strindx, strln, retval;
  372.     char ch;
  373.     long done;
  374.  
  375.     if (test) return(0);
  376.  
  377.     done = w_time + time((long *) 0);
  378.     logstr[0] = '\0';
  379.     strindx = 0;
  380.     strln = strlen(w_str);
  381.     while (done > time((long *) 0)) {
  382.         if (bioscom(READST, 0, comport) & STDAV) {
  383.             retval = bioscom(READCH, 0, comport);
  384.             if (retval & GOODREAD)    /* error */
  385.                 return(retval);
  386.             ch = retval & 0x7f;
  387.             if (strlen(logstr) < loglen)
  388.                 strncat(logstr, &ch, 1);
  389.             if (ch == w_str[strindx]) {
  390.                 strindx++;
  391.                 if (strindx >= strln) return(0);    /* all done */
  392.             }
  393.             else strindx=0;        /* start over (ie, ignore leading chs */
  394.         }
  395.     }
  396.  
  397.     return(-1);    /* timeout */
  398. }
  399.  
  400. void parse_args(argc, argv)    /* scan command-line */
  401. int argc;
  402. char **argv;
  403. {
  404.     char    *cp;
  405.     int    i;
  406.  
  407.     /* Get our entry name for use in error msgs */
  408.     me = strrchr(*argv,'/');
  409.     if (me == NULL) me = strrchr(*argv,'\\');
  410.     if (me == NULL) me=*argv;
  411.     else me++;
  412.     if ((cp=strchr(me,'.')) != NULL) *cp=0;    /* drop trailing .EXE */
  413.  
  414.     argv++;
  415.  
  416.     if (parse_comport(DEFPORT)) {    /* init baudetc and comport */
  417.         fprintf(stderr,"%s: Illegal default port spec: %s\n",
  418.             me, DEFPORT);
  419.         exit(1);
  420.     }
  421.  
  422.     while (--argc) {
  423.         if (**argv == '-' )
  424.             switch (*(++*argv)) {
  425.             case 'm':    /* -m modem_port_spec */
  426.                 ++argv; --argc;
  427.                 if (!argc || parse_comport(*argv)) goto usage;
  428.                 ++argv;
  429.                 break;
  430.  
  431.             case 'w':    /* -w Nsecs */
  432.                 ++argv; --argc;
  433.                 if (!argc) goto usage;
  434.                 waitsec = atoi(*argv);
  435.                 ++argv;
  436.                 break;
  437.  
  438.             case 'p':    /* -p Nsecs */
  439.                 ++argv; --argc;
  440.                 if (!argc) goto usage;
  441.                 pausesec = atoi(*argv);
  442.                 ++argv;
  443.                 break;
  444.  
  445.             case 'd':    /* -d phonenum */
  446.                 ++argv; --argc;
  447.                 if (!argc) goto usage;
  448.                 dialup_num = *argv;
  449.                 ++argv;
  450.                 break;
  451.  
  452.             case 'o':    /* -o owner_code */
  453.                 ++argv; --argc;
  454.                 if (!argc) goto usage;
  455.                 owner_code = *argv;
  456.                 ++argv;
  457.                 if (strlen(owner_code) != 10)
  458.                     fprintf(stderr,"%s: Warning: 10 digits expected, got %s\n",
  459.                         me, owner_code);
  460.                 break;
  461.  
  462.             case 'c':    /* -c ctrl_op_code */
  463.                 ++argv; --argc;
  464.                 if (!argc) goto usage;
  465.                 ctrlop_code = *argv;
  466.                 ++argv;
  467.                 i=strlen(ctrlop_code);
  468.                 if (i<1 || i>7) fprintf(stderr, "%s: Warning: ctrl op code length is illegal: %s\n",
  469.                             me, ctrlop_code);
  470.                 break;
  471.  
  472.             case '2':    /* -2 2digit_slot_code */
  473.                 ++argv; --argc;
  474.                 if (!argc) goto usage;
  475.                 rc2slot = *argv;
  476.                 ++argv;
  477.                 break;
  478.  
  479.             case '3':    /* -3 3digit_slot_code */
  480.                 ++argv; --argc;
  481.                 if (!argc) goto usage;
  482.                 rc3slot = *argv;
  483.                 ++argv;
  484.                 break;
  485.  
  486.             case 'v':
  487.                 verbose++;
  488.                 argv++;
  489.                 break;
  490.  
  491.             case 'u':
  492.                 unlock = 0;    /* already unlocked */
  493.                 argv++;
  494.                 break;
  495.  
  496.             case 't':
  497.                 test++;    /* enable test mode */
  498.                 argv++;
  499.                 break;
  500.  
  501.             default:
  502. usage:            fprintf(stderr,"USAGE: %s [options] infile\n", me);
  503.             fprintf(stderr,"where infile contains lines of the form:\n");
  504.             fprintf(stderr,"S slotnum  call  phonenumber\nor commands CU,CL,CS,CO,MP,MA,MR\n\n");
  505.             fprintf(stderr,"options are:\t-v for verbose mode\n");
  506.             fprintf(stderr,"\t\t-t for test mode: simulate operations\n");
  507.             fprintf(stderr,"\t\t-u if autodialer is kept unlocked\n");
  508.             fprintf(stderr,"\t\t-w N to wait N secs after dialing the RC85\n");
  509.             fprintf(stderr,"\t\t-p N to pause N secs after each command\n");
  510.             fprintf(stderr,"\t\t-m COMn:baud,8N1 to select a modem port and parameters\n");
  511.             fprintf(stderr,"\t\t-d phonenumber to override the default phone number\n");
  512.             fprintf(stderr,"\t\t-o owner_code to override the default owner unlock code\n");
  513.             fprintf(stderr,"\t\t-c ctrlop_code to override the default control op code\n");
  514.             fprintf(stderr,"\t\t-2 code to override the default 2-digit slot programming code\n");
  515.             fprintf(stderr,"\t\t-3 code to override the default 3-digit slot programming code\n");
  516.             fprintf(stderr,"defaults are -w %d -p %d -m %s -d %s -o %s -c %s -2 %s -3 %s\n",
  517.                     waitsec, pausesec, DEFPORT, dialup_num, owner_code,
  518.                     ctrlop_code, rc2slot, rc3slot);
  519.             exit (1);
  520.             } /* end of if () switch */
  521.         else {    /* not an option, so assume is an input file */
  522.             if (freopen(*argv, "r", stdin) == NULL) {
  523.                 fprintf(stderr,"%s: %s: %s\n", me, *argv, strerror(errno));
  524.                 exit(2);
  525.             }
  526.         }
  527.     } /* end while (--argc) */
  528. }
  529.  
  530.  
  531. int parse_comport(ap)    /* ap -> "COM1:9600,8N1" */
  532. char *ap;
  533. /* Sets global variables comport, baudetc */
  534.  
  535. {
  536.     char *p, *s;
  537.     int i,j;
  538.  
  539.     (void)strupr(ap);
  540.     s = strchr(ap,':');
  541.     if (s == NULL) {
  542.         fprintf(stderr,"%s: Unknown port %s\n", me, ap);
  543.         return (1);
  544.     }
  545.     *s++ = 0;        /* terminate COMx, hop to baud */
  546.  
  547.     if (strncmp(ap, "COM", 3)==0)
  548.         comport=atoi(ap+3) - 1;    /* COMn */
  549.     else {
  550.         fprintf(stderr,"%s: Unknown port %s\n",me, ap);
  551.         return (1);
  552.     }
  553.  
  554.     p = strchr(s,',');  /* Where is comma separating baud from par? */
  555.     if (p == NULL) return (1);
  556.     *p++ = 0;    /* replace comma by EOS, point p to 8N1 string */
  557.     j = atoi(s);  /* baud */
  558.  
  559.     for (i=0; i < (sizeof(baudtab)/sizeof(int)); i++)
  560.         if (j == baudtab[i]) {
  561.             baudetc = (i << 5);
  562.             break;
  563.         }
  564.  
  565.     if (i >= sizeof(baudtab)/sizeof(int)) {
  566.         fprintf(stderr, "%s: Illegal baud %s\n", me, s);
  567.         return (1);
  568.     }
  569.  
  570.     if (*p < '5' || *p > '8') {
  571.         fprintf(stderr,"%s: Illegal word length %c\n", me, *p);
  572.         return (1);
  573.     }
  574.     baudetc |= (*p - '5');
  575.     p++;
  576.     for (i=0; i < 3; i++)
  577.         if (*p == partab[i][0]) {
  578.             baudetc |= partab[i][1];
  579.             break;
  580.         }
  581.     if (i >= 3) {
  582.         fprintf(stderr,"%s: Unknown parity %c\n",me, *p);
  583.         return (1);
  584.     }
  585.  
  586.     p++;
  587.     if (*p < '1' || *p > '2') {
  588.         fprintf(stderr,"%s: Illegal stop bit %c\n", *p);
  589.         return (1);
  590.     }
  591.     baudetc |= (*p - '1') << 2;
  592.     return (0);
  593. }
  594.  
  595.  
  596. int hndlbrk()
  597. {
  598.     char abort[] = "\rAT\r";    /* \r to terminate possible cmd */
  599.     char i;
  600.  
  601.     /* cancel any modem cmd in progress */
  602.     writecom(abort);    /* This MAY produce an OK, OR start a cmd going! */
  603.     for (i=0; i<15; i++) {    /* try 15 times */
  604.         if (!waitfor(OK, 2, "", 0)) break;    /* wait 2 secs for an OK */
  605.         writecom(abort);    /* try to elicit an OK again */
  606.     }
  607.     hangup();    /* undo what we did so far */
  608.     return(0);    /* 0 => abort */
  609. }
  610.