/********************************************************************** * Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. * * All Rights Reserved. * * Reference "/usr/src/COPYRIGHT" for applicable restrictions. * **********************************************************************/ static char Sccsid[] = "@(#)cmx2.c 3.0 4/22/86"; /* * Ultrix-11 communications exerciser (cmx). * * DH11/DHU11/DHV11/DZ11/DZV11/DZQ11/DL11 * * PART 2 - (cmx2.c) * * Part 2 is the actual exerciser * Called from cmx, data passed in cmx_?#.arg file, * ? = dh, dhu, dhv, dz, dzv, dzq, dl # = unit number. * * Fred Canter 11/2/82 * Bill Burns 4/84 * * This program exercises one DH11, DHU11/DHV11, DZ11/DZV11/DZQ11, * or DL11 unit, (a DL11 unit is actually 16 DL11's) * either in maintenance loop back mode or with line * turnaroud connectors on the mux panel. * * Maintenance loopback mode: * * 1. Maintenance loopback mode for DH11 and DZ11/DZV11/DZQ11 * automatically loops back all lines. This means that all * terminal lines on DH11 and DZ11/DZV11/DZQ11 devices must be * disabled in the `/etc/ttys' file. * * 2. The DHU11/DHV11 controllers allow loopback on individual lines. * When testing DHU11/DHV11 devices only the lines that are * being tested need to be disabled in the `/etc/ttys' file. * * Turnaround connectors: * * With turnaround connectors, only the line(s) to be * exercised must be disabled in `/etc/ttys'. * **************************************************************** * WARNING WARNING WARNING WARNING WARNING WARNING * * * * In maintenance loop back mode, character output on * * the transmit lines in NOT disabled ! * * Prior to running this exerciser the customers * * equipment, other than terminals, must be disconnected * * from the lines to be exercised or disabled, if it * * would be affected by the test data on the lines. * * Also (cmx) will not function properly unless the * * currently running ULTRIX-11 is named `unix'. * * * **************************************************************** * * USAGE: * * cmxr ?# # * * ? Device type, dh, dhu, dhv, dz, dzv, dzq, dl * # Device unit number * (for DL, unit 0 is dl 0 - 15, & unit 1 is dl 16 - 31) * # Event flag number * * Below is gone due to event flags usage: * kfn Run/stop control file name * */ #include <stdio.h> #include <sgtty.h> #include <signal.h> #include <time.h> #include <a.out.h> #include <sys/param.h> /* Don't matter which one */ #define R 0 #define W 1 #define RW 2 #define OFF 0 /* tstate flags */ #define SETUP 1 #define WRDY 2 #define RRDY 4 #define TRDY 8 char afn[12]; /* argument file name */ char *dname; /* device name DH11, DHU11/DHV11, DZ11/DZV11/DZQ11, or DL11 */ char *dn; /* DH/DHU/DHV/DZ/DZV/DZQ/DL device name */ char tdn[12]; /* tty node name `/dev/tty??' */ char *obuf; /* pointer to tty line output buffers */ char *ibuf; /* pointer to tty line input buffers */ char tfc[16]; /* first character of test data pattern */ char tstate[16]; /* state of each line, OFF, WRT, READ, TEST */ int tnc[16]; /* # of characters sent on a line, 132 max */ int twc[16]; /* write character count for a line */ int tcc[16]; /* received character count for each line */ int tfd[16]; /* file descriptor for each line */ int toc[16]; /* timeout count on each line */ int tdec[16]; /* data error count on each line */ time_t tstime[16]; /* time output started on a line, for timeout */ /* stuff for end of pass and stats */ unsigned wrtcnt[16]; /* count of write operations per line */ unsigned rdcnt[16]; /* count of "good" read operations per line */ unsigned errcnt[16]; /* cout of read errors per line */ /* NOTE: end of pass not implemented */ unsigned passcnt[16]; /* end of pass count for each line */ int unit; /* DH11/DHU11/DHV11/DZ11/DZV11/DZQ11/DL11 unit number */ int nline; /* number of lines per unit */ int brate[] = /* bit rates (sgtty) */ { 3, /* 110 baud */ 7, /* 300 baud */ 9, /* 1200 baud */ 11, /* 2400 baud */ 12, /* 4800 baud */ 13, /* 9600 baud */ 13, 13 }; /* * Line activity table, * -1 = line not selected * 0 = line deselected via -u * 1 = line selected via -l (without bit rate) * >1 = line selected via -l (with bit rate) * for active lines, set to bit rate (sgtty) number. */ int lnact[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; int bdrsav[16]; /* baud rate save */ struct sgttyb tsgtty = { 0, 0, '#', '@', (RAW+ANYP) }; struct sgttyb t_etq; /* used by ETQ ioctl to get char count */ int dzflag; int dzvflag; int dzqflag; int dhuflag; int dhvflag; int dlflag; int nmflag, brflag, lsflag; int ndep; int ndel; long randx; time_t btbuf; time_t etbuf; time_t timbuf; time_t ptbuf; /* time buf for stat printouts */ #ifdef EFLG #include <sys/eflg.h> char *efpis; char *efids; int efbit; int efid; long evntflg(); int zflag; #else char *killfn; #endif int stopsig; int errno; int sys_nerr; char *sys_errlist[]; int istime; main(argc, argv) char *argv[]; int argc; { int stop(), intr(); FILE *argf; register int i, j; register char *p; char *n; int *ap; int fi, ln, opnerr; int first, cc, sum; int dcerr, cserr; int wcc, dec, fde; int burst, maxcc, bufsiz; int maj; int sflag; int oldrate; signal(SIGINT, SIG_IGN); signal(SIGTERM, intr); signal(SIGQUIT, stop); if((argc < 2) || (argc > 4)) exit(1); dn = argv[1]; /* device name, dz#, dh# or uh# */ sprintf(&afn, "cmx_%s.arg", dn); argf = fopen(afn, "r"); if(argf == NULL) exit(1); maj = getw(argf); unit = getw(argf); dzflag = getw(argf); dzvflag = getw(argf); dzqflag = getw(argf); dhuflag = getw(argf); dhvflag = getw(argf); dlflag = getw(argf); sflag = getw(argf); istime = getw(argf); ndep = getw(argf); ndel = getw(argf); bufsiz = getw(argf); burst = getw(argf); maxcc = getw(argf); brflag = getw(argf); nmflag = getw(argf); #ifdef EFLG zflag = getw(argf); #endif for(i=0; i<16; i++) lnact[i] = getw(argf); fclose(argf); unlink(afn); #ifdef EFLG if(zflag) { efpis = argv[2]; efids = argv[3]; } #else killfn = argv[2]; /* run/stop control file name */ #endif if(dzflag) { dname = "DZ11"; nline = 8; } else if(dzvflag) { dname = "DZV11"; nline = 4; } else if(dzqflag) { dname = "DZQ11"; nline = 4; } else if(dlflag) { dname = "DL11"; nline = 16; } else if(dhvflag) { dname = "DHV11"; nline = 8; } else if(dhuflag) { dname = "DHU11"; nline = 16; } else { dname = "DH11"; nline = 16; } /* * Allocate memory for 4 input & output * buffers if DZV11 or DZQ11, * 8 if DHV11 or DZ11, or 16 if DH11, DHU11, or DL11. */ ibuf = calloc((bufsiz*nline), sizeof(char)); if(ibuf == NULL) { fprintf(stderr, "\ncmx: Can't calloc input buffers\n"); exit(1); } obuf = calloc((bufsiz*nline), sizeof(char)); if(obuf == NULL) { fprintf(stderr, "\ncmx: Can't calloc output buffers\n"); exit(1); } time(&btbuf); ptbuf = btbuf; randx = btbuf & 0777; /* initialize random number generator */ printf("\n\n%s exerciser started - %s",dname, ctime(&btbuf)); fflush(stdout); #ifdef EFLG if(zflag) { efbit = atoi(efpis); efid = atoi(efids); evntflg(EFCLR, efid, (long)efbit); } #else unlink(killfn); /* tell SYSX cmx started */ #endif /* * Open all selected lines and set their * state to write ready. */ for(i=0; i<nline; i++) { toc[i] = 0; /* zero timeout counter */ if(lnact[i] < 0) { /* line not selected */ tfd[i] = -1; tstate[i] = OFF; } else { sprintf(&tdn, "/dev/%s%02d", dn, i); ttlocl((maj|(i+(unit*nline))),1); /* set local mode */ tfd[i] = open(tdn, RW); if(tfd[i] < 0) { perror("cmx"); fprintf(stderr, "\ncmx: Can't open %s\n", tdn); exit(1); } tstate[i] = SETUP; twc[i] = 0; } } /* * Write, Read, compare data loop as follows: * * 1. Do a stty() to set RAW mode and bit rate on the tty line. * The bit rate is randomly set to one of the following: * 110, 300, 1200, 2400, 4800, 9600, * or is fixed at the rate specified by [-b] or [-l]. * DL11 must use fixed bit rate, checked by cmx1. * * 2. On DH11, and DZ11/DZV11/DZQ11 set the controller * to maintenance loop back mode, unless [-m] was specified. * On DHU11/DHV11 and DL11 set maintenance loopback * on individual lines, unless [-m] was specified. * * 3. Generate a test data packet for the tty line * and load it into the output buffer. * The data packet contains a sequential binary count * pattern starting with a random character and is a * random number of characters in length. * Save the first character of the test data pattern * for use by the data compare test. * Write the output buffer to the tty line. * * CHAR CONTENTS * * 0 DH/DHU/DHV/DZ/DZV/DZQ/DL unit number * 1 DH/DHU/DHV/DZ/DZV/DZQ/DL line number * 2 - N test data pattern * ( N = random number of sequential characters) * N 16 bit checksum over entire packet * * 4. Scan all selected lines for input characters pending. * For any that have some, read all waiting characters * into the input buffer for that line. * When a line has read an entire packet, * do the data compare test and print any errors. * If the entire packet has not been received within * 2 minutes after the output was started on the line * a timeout error message is printed. * * 5. After the data has been compared, mark the line * ready for output and go back to step 1. */ first = 0; loop: /* intr(); /* poll for stop cmx (signals unreliable) */ if(sflag) goto nostat; time(&timbuf); if(((timbuf - ptbuf) / 60) < istime) goto nostat; else { ptbuf = timbuf; printf("\n\n CMX I/O Statistics at %s \n\n", ctime(&ptbuf)); printf("line\t\t write\t\t read\t\t error\n"); printf("\t\t count\t\t count\t\t count\n"); for(i=0; i<16; i++) if(tstate[i] != OFF) printf("%2.d\t\t%6.u\t\t%6.u\t\t%6.u\n", i, wrtcnt[i], rdcnt[i], errcnt[i]); } nostat: for(i=0; i<nline; i++) { if(tstate[i] & SETUP) { ioctl(tfd[i], TIOCFLUSH, &tsgtty); if(tdec[i] >= ndel) { /* data error limit exceeded */ printf("\n\n****** DATA ERROR LIMIT EXCEEDED ******\n"); printf("\n %s unit %d line %02d deselected\n", dname, unit, i); tstate[i] = OFF; continue; } if(brflag) { tsgtty.sg_ispeed = brflag; tsgtty.sg_ospeed = brflag; } else if(lnact[i] > 1) { tsgtty.sg_ispeed = lnact[i]; tsgtty.sg_ospeed = lnact[i]; } else { tsgtty.sg_ispeed = brate[rng()&7]; tsgtty.sg_ospeed = tsgtty.sg_ispeed; } oldrate = bdrsav[i]; /* note old rate for dzv11 */ bdrsav[i] = tsgtty.sg_ispeed; /* save bit rate */ stty(tfd[i], &tsgtty); if((dlflag || dhuflag || dhvflag) && !nmflag) { ioctl(tfd[i], TIOCSMLB, &tsgtty); } else if(!first && !nmflag) { first++; ioctl(tfd[i], TIOCSMLB, &tsgtty); } tnc[i] = (rng() & maxcc) + 1; p = obuf + (i*bufsiz); sum = 0; tfc[i] = rng() & 0377; *p = unit; sum += *p++; *p = i; sum += *p++; for(j=0; j<tnc[i]; j++) { *p = (tfc[i] + j) & 0377; sum += *p++; } sum = -sum; *p++ = sum & 0377; *p++ = (sum >> 8) & 0377; tnc[i] += 4; wrtcnt[i]++; /* 110 baud special case for dzv11 */ if((oldrate == 3) && (dzvflag || dzqflag)) sleep(1); ioctl(tfd[i], TIOCFLUSH, &tsgtty); tstate[i] = WRDY; continue; } if((tstate[i] & WRDY) && (twc[i] < tnc[i])) { if((tnc[i] - twc[i]) >= burst) wcc = burst; else wcc = tnc[i] - twc[i]; time(&tstime[i]); p = obuf + (i*bufsiz); p += twc[i]; if(write(tfd[i], (char *)p, wcc) != wcc) { /* In case QUIT signal received during system call */ /* NOTE - access will mess up errno! */ #ifdef EFLG if(zflag) { if(stopsig || checkflg()) stop(); } else { if(stopsig) stop(); } #else if(stopsig || (access(killfn, 0) == 0)) stop(); #endif printf("\n\n****** WRITE ERROR on %s unit %d line %02d ******\n",dname,unit,i); perror("cmxr"); tdec[i]++; tstate[i] = SETUP; twc[i] = 0; continue; } twc[i] += wcc; /* first write only */ if(twc[i] && (*(p+2) == tfc[i]) && (*(p+1) == i) && (*p == unit)) { tstate[i] |= RRDY; tcc[i] = 0; } } } /* intr() */ for(i=0; i<nline; i++) if(tstate[i] & RRDY) { t_etq.sg_flags = 0; ioctl(tfd[i], TIOCETQ, &t_etq); if(cc = t_etq.sg_flags) { /* char count */ if(tcc[i] == 0) { /* first read only */ p = ibuf + (i*bufsiz); for(j=0; j<bufsiz; j++) *p++ = 0; } for(j=0; j<cc; j++) if(read(tfd[i], (char *)(ibuf+(i*bufsiz)+tcc[i]+j),1) != 1) { #ifdef EFLG if(zflag) { if(stopsig || checkflg()) stop(); } else { if(stopsig) stop(); } #else if(stopsig || (access(killfn, 0) == 0)) stop(); #endif printf("\n\n****** READ ERROR on %s unit %d line %02d ******\n",dname,unit,i); tstate[i] = TRDY; goto nxtrd; } tcc[i] += cc; if(tcc[i] == tnc[i]) { tstate[i] = TRDY; continue; } } time(&timbuf); if((timbuf - tstime[i]) > 120) { printf("\n\nRead data timeout on "); printf("%s unit %d line %02d", dname, unit, i); if(tcc[i]) printf("\nComplete data packet not received within 2 minutes"); else printf("\nNo data packet received within 2 minutes"); toc[i]++; if(toc[i] >= 5) { printf("\n\n****** TIMEOUT LIMIT EXCEEDED ******"); printf("\n %s unit %d line %02d deselected\n",dname, unit, i); ioctl(tfd[i], TIOCFLUSH, &tsgtty); tstate[i] = OFF; } else { if(tcc[i] > 0) { tstate[i] = TRDY; continue; } tstate[i] = SETUP; twc[i] = 0; } } nxtrd: continue; } /* intr(); */ for(i=0; i<nline; i++) if(tstate[i] == TRDY) { cserr = 0; dcerr = 0; p = ibuf + (i*bufsiz); /* checksum */ for(sum=0, j=0; j<(tcc[i] - 2); j++) sum += *p++; if((sum == 0) && (tcc[i] != tnc[i])) cserr++; /* all zeroes in ibuf */ if((sum += ((*p++ & 0377) | (*p++ << 8))) != 0) cserr++; if(tcc[i] != tnc[i]) dcerr++; p = ibuf + (i*bufsiz); n = obuf + (i*bufsiz); for(j=0; j<(tnc[i] - 2); j++) /* packet data */ if((*p++ & 0377) != (*n++ & 0377)) { dcerr++; break; } if(!cserr && !dcerr) { tstate[i] = SETUP; twc[i] = 0; rdcnt[i]++; continue; } /* Print data error message */ errcnt[i]++; fde = 0; tdec[i]++; p = ibuf + (i*bufsiz); n = obuf + (i*bufsiz); time(&timbuf); printf("\n\n************ READ DATA ERROR *************\n"); printf("\t%s", ctime(&timbuf)); printf("\nData transmitted on %s unit %3d line %3d", dname, *n, *(n+1)); if(tcc[i]) /* only print if some data received */ printf("\nData received on %s unit %3d line %3d", dname, *p, *(p+1)); switch(bdrsav[i]) { case 3: j = 110; break; case 7: j = 300; break; case 9: j = 1200; break; case 11: j = 2400; break; case 12: j = 4800; break; case 13: j = 9600; break; default: j = 0; break; } printf("\nData transmitted at %d bits/second", j); printf("\nData packet checksum was "); if(cserr) printf("BAD"); else printf("GOOD"); printf("\n%03d characters transmitted", tnc[i]); printf("\n%03d characters received", tcc[i]); p = ibuf + (i*bufsiz); n = obuf + (i*bufsiz); dec = 0; for(j=0; j<(tnc[i] - 2); j++) { if((*p & 0377) != (*n & 0377)) { if(dec++ >= ndep) { printf("\n\n[ data error print limit exceeded ]"); break; } if(fde==0) { fde++; printf("\n\nCHAR #\tGOOD\tBAD\n"); } printf("\n%6d\t%03o\t%03o", j, (*n & 0377), (*p & 0377)); } p++; n++; } printf("\n\n******************************************\n"); tstate[i] = SETUP; twc[i] = 0; } fflush(stdout); goto loop; } /* * Random number generator */ rng() { return(((randx = randx * 1103515245 + 12345) >> 16) & 077777); } intr() { signal(SIGTERM, intr); #ifdef EFLG if(zflag) { if(!checkflg()) return; } else return; #else if(access(killfn, 0) != 0) return; #endif stop(); } stop() { register int i; stopsig++; signal(SIGTERM, SIG_IGN); signal(SIGQUIT, SIG_IGN); printf("\n\n CMX Final I/O Statistics \n\n"); printf("line\t\t write\t\t read\t\t error\n"); printf("\t\t count\t\t count\t\t count\n"); for(i=0; i<16; i++) if(tstate[i] != OFF) { ioctl(tfd[i], TIOCFLUSH, &tsgtty); ioctl(tfd[i], TIOCCMLB, &tsgtty); /* clear loop back */ ttlocl((i+(unit*nline)), 0); /* clear line local mode */ printf("%2.d\t\t%6.u\t\t%6.u\t\t%6.u\n", i, wrtcnt[i], rdcnt[i], errcnt[i]); close(tfd[i]); sprintf(&tdn, "/dev/%s%02d", dn, i); unlink(tdn); } time(&etbuf); if(dzflag) printf("\n\nDZ11"); else if(dzvflag) printf("\n\nDZV11"); else if(dzqflag) printf("\n\nDZQ11"); else if(dlflag) printf("\n\nDL11"); else if(dhuflag) printf("\n\nDHU11"); else if(dhvflag) printf("\n\nDHV11"); else printf("\n\nDH11"); printf(" exerciser stopped - %s\n", ctime(&etbuf)); fflush(stdout); #ifdef EFLG if(zflag) evntflg(EFCLR, efid, (long)efbit); #else unlink(killfn); /* tell SYSX cmx stopped */ #endif exit(0); } /* * Check eventflags to stop * return 0 for continuation * return 1 to stop */ extern int errno; checkflg() { union efrt { long efret; struct { int a; int b; } retval } ef; errno = 0; ef.efret = evntflg(EFRD, efid, (long)0); if(errno && ef.retval.a == -1) { zflag = 0; return(0); } if(ef.efret & (1L << efbit)) return(1); return(0); }