|
|
researchv9-SUN3
/*
* ZS driver
*/
#include "zs.h"
#define TRC(c)
#include "../h/param.h"
#include "../h/stream.h"
#include "../h/ttyio.h"
#include "../h/ttyld.h"
#include "../machine/pte.h"
#include "../h/map.h"
#include "../h/buf.h"
#include "../h/systm.h"
#include "../h/conf.h"
#include "../sundev/mbvar.h"
#include "../sundev/zsreg.h"
#include "../sundev/zscom.h"
#include "../sundev/zsvar.h"
#include "../sun/fbio.h"
#define ZSWR1_INIT (ZSWR1_SIE|ZSWR1_TIE|ZSWR1_RIE)
#define ZS_ON (ZSWR5_DTR|ZSWR5_RTS)
#define ZS_OFF 0
#define NZSLINE (NZS*2)
#define SSPEED B9600 /* std speed = 9600 baud */
#define EXISTS 01
#define ISOPEN 02
#define WOPEN 04
#define TIMEOUT 010
#define CARR_ON 020
#define ZSSTOP 040
#define HPCL 0100
#define BRKING 0200
#define DIALOUT 0400 /* set when used as dialout with smart modems */
#define ZSPRI 30
/*
* Modem control commands.
*/
#define DMSET 0
#define DMBIS 1
#define DMBIC 2
#define DMGET 3
/*
* One structure per line.
* Includes communication between H/W level 6 interrupts
* and software interrupts
*/
#define ZSIBUFSZ 256
struct zsaline {
/* from v9 dz.c */
short state;
short flags;
struct block *oblock;
struct queue *rdq;
char speed;
/* from SUN zsasync.c */
short za_needsoft; /* need for software interrupt */
short za_break; /* break occurred */
short za_overrun; /* overrun (either hw or sw) */
short za_ext; /* modem status change */
short za_work; /* work to do */
u_char za_rr0; /* for break detection */
u_char za_ibuf[ZSIBUFSZ]; /* circular input buffer */
short za_iptr; /* producing ptr for input */
short za_sptr; /* consuming ptr for input */
/* additions */
dev_t za_dev;
struct zscom *za_addr;
} zsaline[NZSLINE];
extern int zssoftflags[NZSLINE];
int zsopen(), zsclose(), zsoput(), nodev();
static struct qinit zsrinit = { nodev, NULL, zsopen, zsclose, 0, 0 };
struct qinit zswinit = { zsoput, NULL, zsopen, zsclose, 200, 100 };
struct streamtab zsinfo = { &zsrinit, &zswinit };
#define PCLK (19660800/4) /* basic clock rate for UARTs */
#define ZSPEED(n) ZSTimeConst(PCLK, n)
#define NSPEED 16 /* max # of speeds */
u_short zs_speeds[NSPEED] = {
0,
ZSPEED(50),
ZSPEED(75),
ZSPEED(110),
#ifdef lint
ZSPEED(134),
#else
ZSPEED(134.5),
#endif
ZSPEED(150),
ZSPEED(200),
ZSPEED(300),
ZSPEED(600),
ZSPEED(1200),
ZSPEED(1800),
ZSPEED(2400),
ZSPEED(4800),
ZSPEED(9600),
ZSPEED(19200),
ZSPEED(38400),
};
int zsticks = 3; /* polling frequency */
/*
* The async zs protocol
*/
int zsa_attach(), zsa_txint(), zsa_xsint(), zsa_rxint(), zsa_srint(),
zsa_softint();
struct zsops zsops_async = {
zsa_attach,
zsa_txint,
zsa_xsint,
zsa_rxint,
zsa_srint,
zsa_softint,
};
zsa_attach(zs)
register struct zscom *zs;
{
register struct zsaline *za = &zsaline[zs->zs_unit];
za->za_addr = zs;
za->za_dev = zs->zs_unit;
za->state = EXISTS;
}
/*
* Get the current speed of the console and turn it into something
* UNIX knows about - used to preserve console speed when UNIX comes up
*/
zsgetspeed(dev)
dev_t dev;
{
struct zscom *zs;
register struct zsaline *za;
int uspeed, zspeed;
dev = minor(dev);
if (dev >= NZSLINE)
return(SSPEED);
za = &zsaline[dev];
zs = za->za_addr;
zspeed = ZREAD(12);
zspeed |= ZREAD(13) << 8;
for (uspeed = 0; uspeed < NSPEED; uspeed++)
if (zs_speeds[uspeed] == zspeed)
return (uspeed);
return (SSPEED);
}
/*ARGSUSED*/
zsopen(q, d, flag)
register struct queue *q;
{
register dev;
register struct zsaline *za;
struct zscom *zs;
int s;
static int first = 1;
int zspoll();
if (q->ptr) /* already attached */
return(1);
dev = minor(d);
za = &zsaline[dev];
if (dev >= NZSLINE || (za->state&EXISTS)==0)
return(0);
zs = za->za_addr;
q->ptr = (caddr_t)za;
WR(q)->ptr = (caddr_t)za;
s = splzs();
zs->zs_priv = (caddr_t)za;
zsopinit(zs, &zsops_async);
(void) splx(s);
if (first) {
first = 0;
timeout(zspoll, (caddr_t)0, zsticks);
}
if ((za->state&ISOPEN)==0 || (za->state&CARR_ON)==0) {
register s = spl5();
/* clear any stale input */
za->za_iptr = 0;
za->za_sptr = 0;
za->za_overrun = 0;
zsmctl(zs, ZS_ON, DMSET);
for (;;) {
za->flags = F8BIT|ODDP|EVENP;
if (zssoftflags[dev] & ZS_KBDMS)
za->speed = zsgetspeed(dev);
else
za->speed = SSPEED;
zsparam(za);
if (za->state & CARR_ON)
break;
/* ignore carrier? */
if (zssoftflags[dev] & 1) {
za->state |= (DIALOUT | CARR_ON);
break;
}
if (tsleep((caddr_t)za, ZSPRI, 0) != TS_OK) {
wakeup((caddr_t)za);
za->speed = 0;
zsparam(za);
splx(s);
return(0);
}
}
za->rdq = q;
za->state |= ISOPEN;
splx(s);
}
TRC('o');
return(1);
}
zsclose(q, d)
register struct queue *q;
{
register struct zsaline *za;
register s;
int s1;
za = (struct zsaline *)q->ptr;
s = spl5();
s1 = splzs();
if (za->oblock) {
register struct block *bp = za->oblock;
za->oblock = NULL;
splx(s1);
freeb(bp);
}
flushq(WR(q), 1);
za->rdq = NULL;
if (za->state&HPCL || (za->state&CARR_ON)==0) {
za->speed = 0;
zsparam(za);
}
za->state &= EXISTS;
splx(s);
TRC('c');
}
/*
* ZS write put routine
*/
zsoput(q, bp)
register struct queue *q;
register struct block *bp;
{
register struct zsaline *za = (struct zsaline *)q->ptr;
register union stmsg *sp;
register s;
int s1;
int delaytime;
TRC('p');
switch(bp->type) {
case M_IOCTL:
TRC('i');
sp = (union stmsg *)bp->rptr;
switch (sp->ioc0.com) {
case TIOCSDEV:
delaytime = 0;
if (za->speed != sp->ioc3.sb.ispeed)
delaytime = 20;
za->speed = sp->ioc3.sb.ispeed;
za->flags = sp->ioc3.sb.flags;
bp->type = M_IOCACK;
bp->wptr = bp->rptr;
qreply(q, bp);
qpctl1(q, M_DELAY, delaytime); /* wait a bit */
qpctl(q, M_CTL); /* means do zsparam */
zsstart(za);
return;
case TIOCGDEV:
sp->ioc3.sb.ispeed =
sp->ioc3.sb.ospeed = za->speed;
sp->ioc3.sb.flags = za->flags;
bp->type = M_IOCACK;
qreply(q, bp);
return;
case TIOCHPCL:
za->state |= HPCL;
bp->type = M_IOCACK;
bp->wptr = bp->rptr;
qreply(q, bp);
return;
case KBIOISKBD:
s = za - zsaline;
if ((zssoftflags[s] & ZS_KBDMS) && !(s & 1))
bp->type = M_IOCACK;
else
bp->type = M_IOCNAK;
bp->wptr = bp->rptr;
qreply(q, bp);
return;
default:
bp->wptr = bp->rptr;
bp->type = M_IOCNAK;
qreply(q, bp);
return;
}
case M_STOP:
s = spl5();
za->state |= ZSSTOP;
freeb(bp);
s1 = splzs();
if (bp = za->oblock) {
za->oblock = NULL;
splx(s1);
putbq(q, bp);
}
splx(s);
return;
case M_START:
za->state &= ~ZSSTOP;
zsstart(za);
break;
case M_FLUSH:
flushq(q, 1);
freeb(bp);
s = spl5();
s1 = splzs();
if (bp = za->oblock) {
za->oblock = NULL;
splx(s1);
freeb(bp);
}
splx(s);
return;
case M_BREAK:
qpctl1(q, M_DELAY, 10);
putq(q, bp);
qpctl1(q, M_DELAY, 10);
zsstart(za);
return;
case M_HANGUP:
za->state &= ~ZSSTOP;
case M_DELAY:
case M_DATA:
putq(q, bp);
TRC('d');
zsstart(za);
return;
default: /* not handled; just toss */
break;
}
freeb(bp);
}
/*
* set device parameters
*/
zsparam(za)
register struct zsaline *za;
{
register struct zscom *zs = za->za_addr;
register int wr1, wr3, wr4, wr5, speed;
int loops;
int s;
char c;
if (za->speed == 0) {
(void) zsmctl(zs, ZS_OFF, DMSET); /* hang up line */
return;
}
wr1 = ZSWR1_INIT;
wr3 = ZSWR3_RX_ENABLE;
wr4 = ZSWR4_X16_CLK;
wr5 = (zs->zs_wreg[5] & (ZSWR5_RTS|ZSWR5_DTR)) | ZSWR5_TX_ENABLE;
if (za->speed == B134) { /* what a joke! */
wr1 |= ZSWR1_PARITY_SPECIAL;
wr3 |= ZSWR3_RX_6;
wr4 |= ZSWR4_PARITY_ENABLE | ZSWR4_PARITY_EVEN;
wr5 |= ZSWR5_TX_6;
} else if (za->flags&(F8BIT)) {
wr3 |= ZSWR3_RX_8;
wr5 |= ZSWR5_TX_8;
} else switch (za->flags & (EVENP|ODDP)) {
case 0:
wr3 |= ZSWR3_RX_8;
wr5 |= ZSWR5_TX_8;
break;
case EVENP:
wr1 |= ZSWR1_PARITY_SPECIAL;
wr3 |= ZSWR3_RX_7;
wr4 |= ZSWR4_PARITY_ENABLE + ZSWR4_PARITY_EVEN;
wr5 |= ZSWR5_TX_7;
break;
case ODDP:
wr1 |= ZSWR1_PARITY_SPECIAL;
wr3 |= ZSWR3_RX_7;
wr4 |= ZSWR4_PARITY_ENABLE;
wr5 |= ZSWR5_TX_7;
break;
case EVENP|ODDP:
wr3 |= ZSWR3_RX_7;
wr4 |= ZSWR4_PARITY_ENABLE + ZSWR4_PARITY_EVEN;
wr5 |= ZSWR5_TX_7;
break;
}
if (za->speed == B110)
wr4 |= ZSWR4_2_STOP;
else if (za->speed == B134)
wr4 |= ZSWR4_1_5_STOP;
else
wr4 |= ZSWR4_1_STOP;
speed = zs->zs_wreg[12] + (zs->zs_wreg[13] << 8);
if (wr1 != zs->zs_wreg[1] || wr3 != zs->zs_wreg[3] ||
wr4 != zs->zs_wreg[4] || wr5 != zs->zs_wreg[5] ||
speed != zs_speeds[za->speed&017]) {
/*
* Wait for that last damn character to get out the
* door. At most 1000 loops of 100 usec each is worst
* case of 110 baud. If something appears on the output
* queue then somebody higher up isn't synchronized
* and we give up.
*/
s = splzs();
loops = 1000;
while ((ZREAD(1) & ZSRR1_ALL_SENT) == 0 && --loops > 0) {
(void) splx(s);
DELAY(100);
s = splzs();
}
ZWRITE(3, 0); /* disable receiver while setting parameters */
zs->zs_addr->zscc_control = ZSWR0_RESET_STATUS;
zs->zs_addr->zscc_control = ZSWR0_RESET_ERRORS;
c = zs->zs_addr->zscc_data; /* swallow junk */
c = zs->zs_addr->zscc_data; /* swallow junk */
c = zs->zs_addr->zscc_data; /* swallow junk */
ZWRITE(1, wr1);
ZWRITE(4, wr4);
ZWRITE(3, wr3);
ZWRITE(5, wr5);
speed = zs_speeds[za->speed&017];
ZWRITE(11, ZSWR11_TXCLK_BAUD + ZSWR11_RXCLK_BAUD);
ZWRITE(14, ZSWR14_BAUD_FROM_PCLK);
ZWRITE(12, speed);
ZWRITE(13, speed >> 8);
ZWRITE(14, ZSWR14_BAUD_ENA + ZSWR14_BAUD_FROM_PCLK);
(void) splx(s);
}
}
zstimo(za)
register struct zsaline *za;
{
register struct zscom *zs = za->za_addr;
int s;
if (za->state&BRKING) {
s = splzs();
ZBIC(5, ZSWR5_BREAK);
splx(s);
}
za->state &= ~(TIMEOUT|BRKING);
zsstart(za);
}
zsstart(za)
register struct zsaline *za;
{
register struct zscom *zs = za->za_addr;
register s = spl5();
register struct block *bp;
int s1;
TRC('s');
again:
if (za->state & (TIMEOUT|ZSSTOP|BRKING) || za->oblock) {
TRC('t');
goto out;
}
if (za->rdq == NULL)
goto out;
if ((bp = getq(WR(za->rdq))) == NULL) {
TRC('n');
goto out;
}
switch (bp->type) {
case M_DATA:
za->oblock = bp;
if (zs->zs_addr->zscc_control & ZSRR0_TX_READY)
zs->zs_addr->zscc_data = *bp->rptr++;
break;
case M_BREAK:
s1 = splzs();
ZBIS(5, ZSWR5_BREAK);
splx(s1);
za->state |= BRKING|TIMEOUT;
timeout(zstimo, (caddr_t)za, 15); /* about 250 ms */
freeb(bp);
break;
case M_DELAY:
za->state |= TIMEOUT;
timeout(zstimo, (caddr_t)za, (int)*bp->rptr + 6);
freeb(bp);
break;
case M_HANGUP:
za->speed = 0;
case M_CTL:
freeb(bp);
zsparam(za);
goto again;
}
out:
splx(s);
}
zsmctl(zs, bits, how)
register struct zscom *zs;
int bits, how;
{
register int mbits, s;
s = splzs();
mbits = zs->zs_wreg[5] & (ZSWR5_RTS|ZSWR5_DTR);
zs->zs_addr->zscc_control = ZSWR0_RESET_STATUS;
DELAY(2);
mbits |= zs->zs_addr->zscc_control & (ZSRR0_CD|ZSRR0_CTS);
switch (how) {
case DMSET:
mbits = bits;
break;
case DMBIS:
mbits |= bits;
break;
case DMBIC:
mbits &= ~bits;
break;
case DMGET:
(void) splx(s);
return (mbits);
}
zs->zs_wreg[5] &= ~(ZSWR5_RTS|ZSWR5_DTR);
ZBIS(5, mbits & (ZSWR5_RTS|ZSWR5_DTR));
(void) splx(s);
return (mbits);
}
zsa_txint(zs)
register struct zscom *zs;
{
register struct zsaline *za = (struct zsaline *)zs->zs_priv;
register struct zscc_device *zsaddr = zs->zs_addr;
register struct block *bp;
if ((bp = za->oblock) && bp->rptr < bp->wptr &&
(zsaddr->zscc_control & ZSRR0_TX_READY)) {
zsaddr->zscc_data = *bp->rptr++;
} else {
za->za_work++;
zsaddr->zscc_control = ZSWR0_RESET_TXINT;
ZSSETSOFT(zs);
}
}
zsa_xsint(zs)
register struct zscom *zs;
{
register struct zsaline *za = (struct zsaline *)zs->zs_priv;
register struct zscc_device *zsaddr = zs->zs_addr;
register u_char s0, x0, c;
s0 = zsaddr->zscc_control;
x0 = s0 ^ za->za_rr0;
za->za_rr0 = s0;
zsaddr->zscc_control = ZSWR0_RESET_STATUS;
if ((x0 & ZSRR0_BREAK) && (s0 & ZSRR0_BREAK) == 0) {
za->za_break++;
c = zsaddr->zscc_data; /* swallow null */
#ifdef lint
c = c;
#endif
zsaddr->zscc_control = ZSWR0_RESET_ERRORS;
/*
if (za->za_dev == kbddev)
montrap(*romp->v_abortent);
*/
}
za->za_work++;
za->za_ext++;
ZSSETSOFT(zs);
}
zsa_rxint(zs, d0)
register struct zscom *zs;
{
struct lregs {
int r[4]; /* d0, d1, a0, a1 */
u_short sr;
int pc;
} *lp;
extern struct zscom zscom[];
register struct zsaline *za = (struct zsaline *)zs->zs_priv;
register struct zscc_device *zsaddr = zs->zs_addr;
register u_char c;
c = zsaddr->zscc_data;
if (zs == &zscom[2] && c == 1) { /* A no nonsense abort */
lp = (struct lregs *)&d0;
printf("kernel aborted\n");
printf("pc = 0x%x, sr= 0x%x, &pc = 0x%x\n", lp->pc, lp->sr,
(int)&lp->pc);
panic("abort");
}
if (c == 0 && (za->za_rr0 & ZSRR0_BREAK))
return;
za->za_ibuf[za->za_iptr++] = c;
if (za->za_iptr >= ZSIBUFSZ)
za->za_iptr = 0;
if (za->za_iptr == za->za_sptr)
za->za_overrun++;
za->za_work++;
if (++za->za_needsoft > 20) {
za->za_needsoft = 0;
ZSSETSOFT(zs);
}
}
zsa_srint(zs)
register struct zscom *zs;
{
register struct zsaline *za = (struct zsaline *)zs->zs_priv;
register struct zscc_device *zsaddr = zs->zs_addr;
register short s1;
register u_char c;
s1 = ZREAD(1);
c = zsaddr->zscc_data; /* swallow bad char */
#ifdef lint
c = c;
#endif
zsaddr->zscc_control = ZSWR0_RESET_ERRORS;
if (s1 & ZSRR1_DO) {
za->za_overrun++;
za->za_work++;
ZSSETSOFT(zs);
}
}
/*
* Handle a software interrupt
*/
zsa_softint(zs)
register struct zscom *zs;
{
register struct zsaline *za = (struct zsaline *)zs->zs_priv;
if (zsa_process(za)) /* true if too much work at once */
zspoll(1);
return (0);
}
/*
* Poll for events in the zscom structures
* This routine is called at level 1, we jack up to 3 to lock
* out zsa_softint.
*/
zspoll(direct)
{
register struct zsaline *za;
register short more;
register int s;
do {
more = 0;
for (za = &zsaline[0]; za < &zsaline[NZSLINE]; za++)
if (za->za_work) {
za->za_work = 0;
s = spl3();
if (zsa_process(za)) {
za->za_work++;
more++;
}
(void) splx(s);
}
} while (more);
if (!direct)
timeout(zspoll, (caddr_t)0, zsticks);
}
/*
* Process software interrupts (or poll)
* Crucial points:
* 1. Inner loop gives equal priority to input and output so that
* in TANDEM mode the stop character has a chance of being sent
* before enough input arrives to exceed TTYHOG. This has happened
* in very busy systems.
* 2. The inner loop is executed at most 20 times before the next line
* is serviced -- this "schedules" more fairly among lines.
* 3. BUG - breaks are handled "out-of-band" - their relative position
* among input events is lost, as well as multiple breaks together.
* This is probably not a problem in practice.
*/
zsa_process(za)
register struct zsaline *za;
{
register struct zscc_device *zsaddr = za->za_addr->zs_addr;
register struct block *bp;
register short i;
register u_char c;
if (za->za_ext) {
za->za_ext = 0;
/* carrier up? */
if (zsaddr->zscc_control & ZSRR0_CD) {
/* carrier present */
if ((za->state & CARR_ON)==0)
wakeup((caddr_t)za);
za->state |= CARR_ON;
} else if ((za->state & CARR_ON) && !(za->state & DIALOUT)) {
/* carrier lost */
if (za->state & ISOPEN) {
(void) zsmctl(za->za_addr, ZSWR5_DTR, DMBIC);
if (za->rdq)
putctl(za->rdq->next,M_HANGUP);
}
za->state &= ~CARR_ON;
}
}
if (za->za_overrun) {
za->za_overrun = 0;
za->za_iptr = 0;
za->za_sptr = 0;
if (za->state & ISOPEN)
printf("zs%d: silo overflow\n", minor(za->za_dev));
}
if (za->za_break && (zsaddr->zscc_control & ZSRR0_BREAK) == 0) {
za->za_break = 0;
if (za->rdq != NULL && !(za->rdq->next->flag&QFULL) &&
(bp = allocb(16)) != NULL) {
bp->type = M_BREAK;
(*za->rdq->next->qinfo->putp)(za->rdq->next, bp);
}
}
/* need to handle I & O in same loop to make TANDEM mode work */
i = 0;
do {
if (za->za_sptr != za->za_iptr) {
c = za->za_ibuf[za->za_sptr++];
if (za->za_sptr >= ZSIBUFSZ)
za->za_sptr = 0;
if (za->rdq != NULL && !(za->rdq->next->flag&QFULL) &&
(bp = allocb(16)) != NULL) {
*bp->wptr++ = c;
(*za->rdq->next->qinfo->putp)(za->rdq->next, bp);
}
}
if ((bp = za->oblock) && bp->rptr >= bp->wptr) {
freeb(bp);
za->oblock = NULL;
zsstart(za);
}
} while (za->za_sptr != za->za_iptr && ++i < 20);
return (i >= 20);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.