/* * DZ-11 driver * ------------ * * OPTIMIZED dz driver to handle multiple dzs as efficiently * as possible. The efficiency is gained by disabling all * dz transmitter interrupts and using a KW11-P to generate * suitable interrupts. Carrier is supported but not Ring. * * Ian Johnstone UNSW * May 1979 */ #include "../defines.h" #include "../param.h" #include "../conf.h" #include "../user.h" #include "../tty.h" #include "../proc.h" #define NDZ 7 /* no. of dz-11s */ #define NDZLIN 8 /* no. of lines per dz DO NOT ALTER */ #define NLINES (NDZLIN*NDZ) /* total no. of lines available */ #define SSPEED 11 /* standard speed 2400 bd */ #define CLOCK 0172540 /* kw11-p lives here */ struct { int csr; /* control and status */ #define GO 0101 /* down, single, 100K, run */ unsigned counter; /* counter */ }; struct tty dz11[NLINES]; /* tty structures for this dz */ struct dz /* one for each dz-11 */ { int *dzaddr; /* control registers for this dz */ char nocarr; /* set for lines WITHOUT carrier */ char sopen; /* set for lines with exclusive use */ struct tty *ttys[NDZLIN]; /* address of tty structs this dz */ /* that is only ONE `open' allowed */ char openl; /* flags for open lines */ char closl; /* flags to indicate closing lines */ char xmit; /* set for lines to transmit on */ unsigned pyerrors; /* number of parity errors on input */ unsigned overrors; /* number of overrun errors on input */ int closet[NDZLIN]; /* handle closing via this field */ } dz[NDZ] { { 0160100, 0377, 0000, &dz11[000],&dz11[001],&dz11[002],&dz11[003], &dz11[004],&dz11[005],&dz11[006],&dz11[007] }, { 0160110, 0007, 0000, &dz11[010],&dz11[011],&dz11[012],&dz11[013], &dz11[014],&dz11[015],&dz11[016],&dz11[017] }, { 0160120, 0320, 0020, &dz11[020],&dz11[021],&dz11[022],&dz11[023], &dz11[024],&dz11[025],&dz11[026],&dz11[027] }, { 0160130, 0000, 0000, &dz11[030],&dz11[031],&dz11[032],&dz11[033], &dz11[034],&dz11[035],&dz11[036],&dz11[037] }, { 0160140, 0000, 0000, &dz11[040],&dz11[041],&dz11[042],&dz11[043], &dz11[044],&dz11[045],&dz11[046],&dz11[047] }, { 0160150, 0360, 0100, &dz11[050],&dz11[051],&dz11[052],&dz11[053], &dz11[054],&dz11[055],&dz11[056],&dz11[057] }, { 0160160, 0110, 0013, &dz11[060],&dz11[061],&dz11[062],&dz11[063], &dz11[064],&dz11[065],&dz11[066],&dz11[067] }, /* { 0160170, 0377, 0000, &dz11[070],&dz11[071],&dz11[072],&dz11[073], &dz11[074],&dz11[075],&dz11[076],&dz11[077] } */ }; int dzopenc; /* equal to total number of 'open' lines */ int dzrcvscan; /* when <= 0 scan receiver silos */ char dzbitab[NDZLIN] /* convert line numbers to bit pattern */ { 0001, 0002, 0004, 0010, 0020, 0040, 0100, 0200 }; #define SPLDZ spl5 /* dz interrupts at this priority */ /* * DZ11 register layout */ struct dzr_read { int dzcsr; /* r/w */ int dzrbuf; /* no bit, byte, or tst ops */ char dztcr; /* r/w */ char dzdtr; /* r/w */ char dzring; char dzcarr; }; struct dzr_write { int dzcsr; int dzlpr; /* no bit or byte ops */ char dztcr; char dzdtr; char dztbuf; /* no bit ops */ char dzbrk; /* no bit ops */ }; /* * register control bits */ #define SAE 010000 /* dzcsr */ #define RIE 0100 #define MSE 040 #define RCVR_ON 010000 /* dzlpr */ #define ODD_PAR 0300 #define EVN_PAR 0100 #define TWOSBIT 040 #define C8BIT 030 #define C7BIT 020 #define RERROR 070000 /* dzrbuf */ #define OVR_RUN 040000 #define FRAME 020000 #define PARITY 010000 /* * Table to map UNIX standard speeds to DZ11 speeds. * Illegal speeds are ignored, and are indicated by 0200 bit. */ char dzspeedmap[16] { 0200 /* 0 - zero */ , 0220 /* 1 - 50 */ , 0221 /* 2 - 75 */ , 0222 /* 3 - 110 */ , 0223 /* 4 - 134.5 */ , 0224 /* 5 - 150 */ , 0200 /* 6 - ILLEGAL */ #define LOWSPEED 7 /* lowest speed allowed on dz */ , 025 /* 7 - 300 */ , 026 /* 8 - 600 */ , 027 /* 9 - 1200 */ , 0230 /* 10 - 1800 */ , 032 /* 11 - 2400 */ , 034 /* 12 - 4800 */ , 036 /* 13 - 9600 */ , 0231 /* 14 - ext A - maps to 2000 */ , 0237 /* 15 - ext B - maps to 19200 */ }; /* * Table to map UNIX standard speeds to time between interrupts for * a line running at that speed. The value in the table is multiplied * by 10 to get a value in microseconds. A nominal 20 microseconds * is subtracted to make up for interrupt overhead. */ unsigned dzmicmap[16] { 0 /* 0 - zero */ , 19998 /* 1 - 50 */ , 13331 /* 2 - 75 */ , 9088 /* 3 - 110 */ , 7433 /* 4 - 134.5 */ , 6665 /* 5 - 150 */ , 0 /* 6 - ILLEGAL */ , 3331 /* 7 - 300 */ , 1665 /* 8 - 600 */ , 831 /* 9 - 1200 */ , 554 /* 10 - 1800 */ , 415 /* 11 - 2400 */ , 206 /* 12 - 4800 */ , 102 /* 13 - 9600 */ , 498 /* 14 - ext A - maps to 2000 */ , 50 /* 15 - ext B - maps to 19200 */ }; /* * open a DZ11 line */ dzopen(dev, flag) { extern dzstart(); register struct tty *tp; register struct dz *dzp; register lino; lino = dev.d_minor; if(lino >= NLINES) { u.u_error = ENXIO; return; } dzp = &dz[lino>>3]; if(!fkword(dzp->dzaddr)) /* fix036 */ { u.u_error = ENXIO; return; } /* fix036 */ tp = &dz11[lino]; lino =& 07; if( (dzp->sopen&dzbitab[lino]) && (dzp->openl&dzbitab[lino]) ) { u.u_error = EOPENFAIL; return; } if(u.u_procp->p_ttyp == 0) u.u_procp->p_ttyp = tp; SPLDZ(); if( (dzp->openl&dzbitab[lino]) == 0 ) { tp->t_dev = dev; tp->t_state = (ISOPEN|CARR_ON|SSTART); tp->t_addr = &dzstart; tp->t_speeds = SSPEED|(SSPEED<<8); tp->t_flags = ODDP|EVENP|XTABS|RAW; tp->t_erase = CERASE; tp->t_kill = CKILL; dzparam(tp); if(dzp->openl == 0) dzp->dzaddr->dzcsr =| (RIE|SAE|MSE); /* init */ dzp->openl =| dzbitab[lino]; if(dzopenc++ == 0) dzxint(); /* start transmitting */ } else dzp->closl =& ~dzbitab[lino]; spl0(); } /* * close a DZ11 line */ dzclose(dev) { register struct tty *tp; register struct dz *dzp; register lino; lino = dev.d_minor; dzp = &dz[lino>>3]; tp = &dz11[lino]; lino =& 07; dzp->closet[lino] = tp->t_outq.c_cc << 1; /* time for close */ dzp->closl =| dzbitab[lino]; dzp->xmit =| dzbitab[lino]; /* start transmitting */ dzp->dzaddr->dztcr =| dzbitab[lino]; /* start transmitting */ } /* * read from a DZ11 line */ dzread(dev) { ttread( &dz11[dev.d_minor] ); } /* * write on a DZ11 line */ dzwrite(dev) { ttwrite( &dz11[dev.d_minor] ); } /* * stty/gtty for DZ11 */ dzsgtty(dev, av) { register struct tty *tp; if((av == 0) && (dzspeedmap[u.u_arg[0]&017] < 0)) { u.u_error = ENXIO; /* illegal speed */ return; } tp = &dz11[dev.d_minor]; if(ttystty(tp, av)) return; dzparam(tp); } /* * set parameters from open or stty into DZ hardware registers */ dzparam(tp) register struct tty *tp; { register lpr, x; extern wakeup(); lpr = dzspeedmap[tp->t_speeds&017]<<8; if((x = tp->t_flags)&EVENP) if((x&ODDP) == 0) lpr =| (EVN_PAR|C7BIT); else lpr =| C8BIT; else if(x&ODDP) lpr =| (ODD_PAR|C7BIT); else lpr =| C8BIT; /* set new speed, char currently in uart may be screwed */ dz[tp->t_dev.d_minor>>3].dzaddr->dzlpr = lpr|(tp->t_dev.d_minor&07); } /* * dz start routine */ dzstart(tp) /* at SPLDZ */ struct tty *tp; { register lino = tp->t_dev.d_minor; register struct dz *dzp; dzp = &dz[lino>>3]; lino =& 07; dzp->xmit =| dzbitab[lino]; /* start transmitting */ dzp->dzaddr->dztcr =| dzbitab[lino]; /* start transmitting */ } /* * DZ11 transmitter interrupt. * * Scan every line on each dz. Internal dz limitations * force this scan to take an unusual form. One line * from each dz is serviced each scan until no dz requires * service. This is less efficient than servicing * entirely a dz prior to scanning the next dz but it * it is necessary. * * dzxint is not actually invoked by a dz interrupt * rather it is invoked by a clock interrupt. * to drive multiple dz's efficiently utilizing dz * transmitter interrupts is just NOT possible. */ int dzxc; dzxint() /* at SPLDZ */ { extern ttrstrt(); register struct dz *dzp; int hspeed = LOWSPEED; /* to determine clock speed */ int flag; /* control dz scanning */ dzxc++; /* count */ if( dzopenc == 0 ) return; /* stop if inactive */ /* scan every dz for characters to transmit */ do { for(dzp = &dz[0], flag=0; dzp < &dz[NDZ]; dzp++ ) { register struct tty *tp; register struct dzr_read *dza = dzp->dzaddr; int lino, t_bit; if((lino = dza->dzcsr.hibyte) < 0) /* xmit ?? */ { lino =& 07; /* isolate line number */ tp = dzp->ttys[lino]; t_bit = dzbitab[lino]; /* bit mask, not line number */ flag++; /* note service */ if( (dzp->closl & t_bit) && ((--(dzp->closet[lino]) <= 0) || (tp->t_outq.c_cc == 0)) ) { /* line closed, no time or chars left */ flushtty(tp); tp->t_state = SSTART; dzp->closl =& ~t_bit; dzp->openl =& ~t_bit; dzp->xmit =& ~t_bit; dza->dztcr =& ~t_bit; dza->dzdtr =& ~t_bit; if( (dzp->closl==0) && (dzp->openl==0) ) dza->dzcsr = 0; if( --dzopenc == 0 ) return; } else if(tp->t_outq.c_cc == 0) { dzp->xmit =& ~t_bit; dza->dztcr =& ~t_bit; } else if((dzp->nocarr&t_bit)||(dza->dzcarr&t_bit)) { int c = getc(&tp->t_outq); if( c <= 0177 || tp->t_flags == RAW ) { /* transmit the char for this line */ dza->dztbuf = c; if( tp->t_speeds.lobyte > hspeed ) hspeed = tp->t_speeds.lobyte; } else { dzp->xmit =& ~t_bit; dza->dztcr =& ~t_bit; timeout( &ttrstrt, tp, c&0177 ); tp->t_state =| TIMEOUT; } /* if low water mark then want more */ if( tp->t_state&ASLEEP && tp->t_outq.c_cc <= TTLOWAT ) { tp->t_state =& ~ASLEEP; wakeup(&tp->t_outq); } } else { dza->dztcr =& ~t_bit; } } } } while( flag ); /* finalize state of DZs prior to exitting */ for(dzp = &dz[0]; dzp < &dz[NDZ]; dzp++ ) { register struct dzr_read *dza = dzp->dzaddr; /* dtr to reflect state of carrier, for carrier lines */ dza->dzdtr = (dza->dzcarr | dzp->nocarr) & dzp->openl; /* Enable all lines still with characters to send */ dza->dztcr = dzp->xmit; } /* setup for next interrupt */ CLOCK->counter = dzmicmap[hspeed]; /* count in 10microseconds */ CLOCK->csr = GO; /* call dzrint if needed */ if( dzrcvscan <= 0 ) dzrint(0); dzrcvscan =- dzmicmap[hspeed]; } /* * DZ11 receiver interrupt * * Scan each dz commencing with the particular device that caused this call * Scan at least every dzmicmap[LOWSPEED] microseconds. */ dzrint(dev) { register struct tty *tp; register struct dz *dzp; register int lino; int i, c; for(dzp = &dz[dev], i = 0; i < NDZ; i++) { while((c = dzp->dzaddr->dzrbuf) < 0) /* char present in silo */ { lino = c.hibyte; lino =& 07; if( ((dzp->nocarr&dzbitab[lino]) == 0 ) && ((dzp->dzaddr->dzcarr&dzbitab[lino]) == 0 )) continue; if( (dzp->openl&dzbitab[lino]) == 0 ) continue; tp = dzp->ttys[lino]; if(c&RERROR) { if( (c & FRAME) && (tp->t_flags & RAW ) ) ttyinput(0, tp); /* break for getty */ else if(c & OVR_RUN) dzp->overrors++; else if(c & PARITY) dzp->pyerrors++; } else { ttyinput(c, tp); } } if( ++dzp >= &dz[NDZ] ) dzp = &dz[0]; } dzrcvscan = dzmicmap[LOWSPEED]; }