Ausam/sys/dmr/dz.c-elec
/*
* 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];
}