# /* * driver for BURROUGHS TD800 POLL/SELECT communications protocol * * by * piers lauder * DEPT OF COMPUTER SCIENCE * UNIVERSITY OF SYDNEY * november 1977 * * This driver will simulate any number of "stations" on a BURROUGHS * asynchronous multi-drop TD800 type communications link attached to * a DL11. Minor device selects station, two major devices select * read or write. Any number of bytes may be written, but any bytes * not taken from a read message are lost. EOF is signalled by a * null block ( in either direction ) which is read as a zero. */ #include "../defines.h" #include "../param.h" #include "../user.h" #include "../buf.h" #include "dl11.h" #include "../conf.h" /* (not ERROR_MESSAGE) #define ERROR_MESSAGE /* if errors to be printed on close */ /* * tunable constants */ #define NSTATIONS 2 /* number of stations */ #define READPRI 3 /* priority for sleep when reading */ #define WRITEPRI 2 /* priority for sleep when writing */ #define T_DELAY 2 /* b1726 turn around delay in 'tics' */ #define NOECHO 01 /* reset "flags" bit 1 if echoes expected */ #define ERRORS 02 /* set "flags" bit 2 if errors are to be reported */ #define splbx spl6 /* priority of controller */ /* following bytes should be even parity (programming glitch) */ #define AD1 '0' /* protocol address 1 ('0' = UNIX) */ #define WMN '0' /* first message number for write */ struct stn { char s_flags; /* user synchronisation */ char ad2; /* this station address */ char lmn; /* last read message number */ char rmn; /* read message number */ char wmn; /* write message number */ char wlpc; /* write lpc */ struct buf *rbp; /* read buffer pointer */ struct buf *wbp; /* write buffer pointer */ } bx_stns[NSTATIONS] { { 0,060 }, /* station address '00' */ { 0,0261 }, /* station address '01' */ /* { 0,0262 } /* station address '02' */ }; struct { char flags; /* to control driver characteristics */ char echoes; /* hardware echoes expected */ char rstate; /* receive state */ char wstate; /* write state */ char rlpc; /* recieve lpc */ char *posn; /* position of data in buffer */ int count; /* data count */ int blks_sent; /* blocks sent - statistics */ int blks_rcvd; /* blocks recieved - statistics */ struct stn *station; /* current station on line */ } bx; /* * s_flags bits */ #define OPENR 1 /* open for reading */ #define OPENW 2 /* open for writing */ #define RD_DATA_RDY 4 /* data ready for reading */ #define GET_RD_BLK 010 /* block needed for input */ #define WTG_FOR_WRT 020 /* waiting to transfer data to output block */ #define WRT_DATA_RDY 040 /* data ready for output */ #define WCLOSE 0100 /* write null block on close */ /* * significant bytes */ #define SOH 1 #define STX 2 #define ETX 3 #define EOT 4 #define ENQ 5 #define ACK 6 #define NAK 025 #define POL 'p' #define SEL 'q' /* * significant bytes + parity */ #define SOHp 0201 #define STXp 0202 #define ETXp 3 #define EOTp 0204 #define ENQp 5 #define ACKp 6 #define NAKp 0225 /* * decision table selectors */ #define Y_EOT 0 #define Y_default 1 #define Y_error 2 /* * define structure to log errors */ #define NERR 8 struct error { #ifdef ERROR_MESSAGE char *e_mess; #endif ERROR_MESSAGE int e_count; } bxerr[NERR] #ifdef ERROR_MESSAGE { { "resets", 0 }, { "xmit errors",0 }, { "xmit parity",0 }, { "recv bad", 0 }, { "recv lpc", 0 }, { "breaks", 0 }, { "overuns", 0 }, { "recv parity", 0}, 0 } #endif ERROR_MESSAGE ; #define PARITY 7 #define OVERUN 6 #define BREAK 5 #define RECVLPC 4 #define RECVBAD 3 #define WPAR 2 #define WERR 1 #define RESET 0 char partab[]; /* must use version fixed with bit 7 set for illegal data bytes */ /* see "bxwparity" below */ /* * general open: * - check exclusive use & device free, * - set vectors. * - enable read interrupts */ bxopen( flagp , FLAG ) register char *flagp; register FLAG; { register char *port = &dl11[BP]; extern bxrint(), bxwint(); if ( (flagp >= &bx_stns[NSTATIONS]) || *flagp & FLAG || (*port && *port != COM) ) { u.u_error = ENXIO; return( 0 ); } *flagp =| FLAG; if ( !*port ) { dl11v[BP].rintad = bxrint; dl11v[BP].wintad = bxwint; BPADDR->dlrs = IENABLE; *port = COM; } return( 1 ); } /********* * write * *********/ /* * open for writing and set up write block * N.B. this block is attached for the duration of the write. */ bxopenw( dev ) { register struct stn *sp = &bx_stns[dev.d_minor]; splbx(); if ( bxopen( &sp->s_flags , OPENW ) ) { if ( !sp->wbp ) sp->wbp = getblk( NODEV ); if ( !sp->wmn ) sp->wmn = WMN; } spl0(); } /* * close off write */ bxwclose( dev ) { register struct stn *sp = &bx_stns[dev.d_minor]; splbx(); sp->s_flags =& ~WTG_FOR_WRT; sp->s_flags =| WCLOSE; # ifdef ERROR_MESSAGE bxerrors(); # endif ERROR_MESSAGE spl0(); } /* * write data to line */ bxwrite( dev ) { struct stn *sp = &bx_stns[dev.d_minor]; register struct buf *bp = sp->wbp; register char *flagp = &sp->s_flags; register count; do { splbx(); while ( *flagp & WRT_DATA_RDY ) { *flagp =| WTG_FOR_WRT; sleep( bp , WRITEPRI ); *flagp =& ~WTG_FOR_WRT; } spl0(); if ( bp->b_wcount = count = min( 512 , u.u_count ) ) { if ( count =& 01776 ) iomove( bp , 0 , count , B_WRITE ); if ( bp->b_wcount & 1 ) bp->b_addr[count++] = cpass(); bxwparity( count ); *flagp =| WRT_DATA_RDY; } } while ( u.u_count > 0 ); } /* * set parity bits on data in block and calculate lpc */ bxwparity( count , sp ) struct stn *sp; { register char *cp = sp->wbp->b_addr; register c, lpc; lpc = 0; do { c = *cp & 0177; if ( partab[c] & 0100 ) /* illegal char */ c = '?'; c =| (partab[c] & 0200); lpc =^ c; *cp++ = c; } while ( --count ); sp->wlpc = lpc^sp->wmn^sp->ad2^(AD1^STXp^ETXp); } /******** * read * ********/ /* * open for reading */ bxopenr( dev ) { splbx(); bxopen( &bx_stns[dev.d_minor].s_flags , OPENR ); spl0(); } /* * close off read */ bxrclose( dev ) { register struct stn *sp = &bx_stns[dev.d_minor]; splbx(); sp->s_flags =& ~OPENR ; sp->lmn = 0; # ifdef ERROR_MESSAGE bxerrors(); # endif ERROR_MESSAGE spl0(); } /* * read data from line */ bxread( dev ) { struct stn *sp = &bx_stns[dev.d_minor]; register struct buf *bp = sp->rbp; register char *flagp = &sp->s_flags; register count; int n; splbx(); for (;;) { if ( *flagp & RD_DATA_RDY ) { spl0(); if ( count = (n = min( bp->b_wcount , u.u_count )) & 01776 ) iomove( bp , 0 , count , B_READ ); if ( n & 1 ) passc( bp->b_addr[count] ); /* geterror( bp ); */ splbx(); *flagp =& ~RD_DATA_RDY; if ( *flagp & GET_RD_BLK ) *flagp =& ~GET_RD_BLK; else { sp->rbp = 0; brelse( bp ); } break; } if ( *flagp & GET_RD_BLK ) { spl0(); sp->rbp = bp = getblk( NODEV ); splbx(); *flagp =& ~GET_RD_BLK; } sleep( flagp , READPRI ); } spl0(); } /* * report any errors */ #ifdef ERROR_MESSAGE bxerrors() { register struct error *ep = bxerr; register errs = 0; if ( bx.flags & ERRORS ) { do if ( ep->e_count ) { if ( errs++ == 0 ) printf( "\nb1726 err:" ); printf( " %s:%d" , ep->e_mess , ep->e_count ); ep->e_count = 0; } while ( (++ep)->e_mess ); if ( errs ) putchar( '\n' ); } } #endif ERROR_MESSAGE /************** * interrupts * **************/ /* * deal with read interrupts */ bxrint() { register char *state = &bx.rstate; register union { struct stn *sp; struct buf *bp; char *cp; int i; } c, x; extern bxstart(), bxone(); x.i = BPADDR->dlrb; if ( bx.echoes > 0 ) { if ( bx.flags & NOECHO ) bx.echoes = 0; else { bx.echoes--; return; } } if ( x.i < 0 ) { if ( x.i & 020000 ) bxerr[BREAK].e_count++; else bxerr[OVERUN].e_count++; x.i = Y_error; }else if ( ( x.i ^ partab[(c.i = x.i & 0177)] ) & 0200 ) { bxerr[PARITY].e_count++; x.i = Y_error; } else if ( c.i == EOT ) x.i = Y_EOT; else { x.i = Y_default; bx.rlpc =^ c.i; } /* ** Here should be "switch ( states[state][x.i] )" ** but instead, for efficiency:- */ goto in; /** State **/ /** 0 **/ next: (*state)++; null: return; rset: bxerr[RESET].e_count++; reset1: *state = 1; return; /** 1/8 **/ ad1_c: if ( c.i != AD1 ) goto idl; goto next; /** 2 **/ ad2_c: for ( x.sp = bx_stns ; x.sp < &bx_stns[NSTATIONS] ; x.sp++ ) if ( c.i == ((x.sp)->ad2 & 0177) ) { bx.station = x.sp; goto next; } goto idl; /** 3 **/ polsel: if ( c.i == POL ) goto next; if ( c.i != SEL ) goto idl; select: *state = 6; return; /** 4 **/ testx: if ( c.i != ENQ ) { idl: *state = 0; return; } if ( (x.i = bx.station->s_flags) & WRT_DATA_RDY ) { send: bx.wstate = 0; timeout( bxstart , SOHp , T_DELAY ); goto next; }else if ( x.i & WCLOSE ) { x.sp = bx.station; x.sp->wbp->b_wcount = 0; x.sp->wlpc = x.sp->ad2^x.sp->wmn^(AD1^STXp^ETXp); goto send; }else { if ( !(x.i & OPENW) ) { if ( c.bp = bx.station->wbp ) { brelse( c.bp ); bx.station->wbp = 0; } if ( !(x.i & OPENR) ) { for ( x.sp=bx_stns ; x.sp<&bx_stns[NSTATIONS] ; x.sp++ ) if ( x.sp->s_flags & (OPENR|OPENW) ) goto w_finish; dl11[BP] = 0; BPADDR->dlrs = 0; } } w_finish: c.i = EOTp; w_idle: *state = 0; } w_out: timeout( bxone , c.i , T_DELAY ); return; /** 5 **/ w_err: bxerr[WERR].e_count++; goto reset1; acknak: if ( c.i == ACK ) goto sendok; if ( c.i != NAK ) goto w_err; resend: bxerr[WPAR].e_count++; *state = 4; goto send; sendok: bx.blks_sent++; c.sp = bx.station; c.sp->wmn =^ 0201; /* flip seqn. no. ( & parity ) */ if ( (x.i = c.sp->s_flags) & WTG_FOR_WRT ) wakeup( c.sp->wbp ); if ( x.i & WRT_DATA_RDY ) c.sp->s_flags =& ~WRT_DATA_RDY; else if ( x.i & WCLOSE ) c.sp->s_flags =& ~(WCLOSE|OPENW); goto w_finish; /** 6 **/ testr: if ( c.i != ENQ ) goto idl; if ( *(c.cp = &(x.sp = bx.station)->s_flags) & OPENR ) { if ( x.sp->rbp && !(*(c.cp) & RD_DATA_RDY) ) { c.i = ACKp; bx.posn = x.sp->rbp->b_addr; bx.count = 0; (*state)++; goto w_out; }else { *(c.cp) =| GET_RD_BLK; wakeup( c.cp ); } } c.i = NAKp; goto w_idle; /** 7 **/ soh: if ( c.i != SOH ) goto idl; bx.rlpc = 0; goto next; /** 9 **/ ad2_t: if ( c.i != (bx.station->ad2 & 0177) ) goto idl; goto next; /** 10 **/ xmn: if ( c.i != '0' && c.i != '1' ) goto idl; bx.station->rmn = c.i; goto next; /** 11 **/ stx: if ( c.i != STX ) goto idl; goto next; /** 12 **/ recv: if ( c.i == ETX ) goto next; if ( bx.count++ < 512 ) *bx.posn++ = c.i; return; rerr: bxerr[RECVBAD].e_count++; return; /** 13 **/ lpc: if ( !bx.rlpc ) { x.sp = bx.station; if ( x.sp->rmn != x.sp->lmn ) { bx.blks_rcvd++; x.sp->lmn = x.sp->rmn; x.sp->rbp->b_wcount = bx.count; x.sp->s_flags =| RD_DATA_RDY; wakeup( &x.sp->s_flags ); } c.i = ACKp; goto w_idle; } bxerr[RECVLPC].e_count++; c.i = NAKp; goto w_idle; in: { static *bxrm_c[][3] { /* EOT ? BAD */ /* 0 */ next,null,null, /* 1 */ rset,ad1_c,idl, /* 2 */ rset,ad2_c,idl, /* 3 */ rset,polsel,idl, /* 4 */ rset,testx,idl, /* 5 */ w_err,acknak,idl, /* 6 */ rset,testr,idl, /* 7 */ rset, soh, idl, /* 8 */ rset,ad1_c,idl, /* 9 */ rset,ad2_t,idl, /* 10 */ rset, xmn, idl, /* 11 */ rset, stx, idl, /* 12 */ rset,recv,rerr, /* 13 */ lpc, lpc, lpc }; goto bxrm_c[*state][x.i]; /* yes folks - a computed goto */ } } /* * transmit 1 char & start write interrupts */ bxstart( c ) { BPADDR->dlrs = 0; BPADDR->dlwb = c; BPADDR->dlws = IENABLE; } /* * transmit 1 char */ bxone( c ) { register x; BPADDR->dlwb = c; x = BPADDR->dlrb; bx.echoes = 1; } /* * deal with write interrupts */ bxwint() { register c, x; switch ( bx.wstate ) { case 0: c = AD1; break; case 1: c = bx.station->ad2; break; case 2: c = bx.station->wmn; break; case 3: c = STXp; bx.count = bx.station->wbp->b_wcount; bx.posn = bx.station->wbp->b_addr; break; case 4: if ( bx.count-- == 0 ) { c = ETXp; break; } BPADDR->dlwb = *bx.posn++; return; case 5: c = bx.station->wlpc; bx.echoes = 2; x = BPADDR->dlrb; BPADDR->dlws = 0; BPADDR->dlrs = IENABLE; } bx.wstate++; BPADDR->dlwb = c; } #ifdef POWER_FAIL /* * restart after a power restore */ bxpowerf() { register struct stn *sp; for ( sp=bx_stns ; sp < &bx_stns[NSTATIONS] ; sp++ ) if ( sp->s_flags & (OPENR|OPENW) ) { bx.echoes = 0; BPADDR->dlrb = IENABLE; return; } } #endif