+/*
+ * 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 */
+};
+\f
+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 */
+\f
+/*
+ * 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
+\f
+/*
+ * 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 */
+};
+\f
+/*
+ * 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 */
+};
+\f
+/*
+ * 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 */
+}
+\f
+/*
+ * 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);
+}
+\f
+/*
+ * 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 */
+}
+\f
+/*
+ * 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];
+}
+\f
+/*
+ * 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];
+}