V10/sys/io/cure.c

Compare this file to the similar file:
Show the results in this format:

/*
 * simple CURE datakit driver
 * -- assumes it is the first CURE, and only one allowed for now
 *	(the latter not severe, as 256 channels are permitted)
 */
#include "sys/param.h"
#include "sys/stream.h"
#include "sys/dkio.h"
#include "sys/ubaddr.h"
#include "sys/conf.h"
#include "sys/kc.h"
#include "sys/dkstat.h"
#include "sys/dkmod.h"
#include "sys/buf.h"

/*
 * hardware stuff
 */
struct device {
	unsigned short	buf;
	unsigned char	csr;
	char	fill1;
	short	fill2;
	char	reset;
};

/*
 * status bits
 */
#define	SSEQ	01	/* 1-bit command sequence # */
#define	SRRDY	02	/* receive ready */
#define	SXRDY	04	/* transmit ready */
#define	SUBERR	010	/* unibus error */
#define	SERR	020	/* protocol error */
#define	STATE	0300	/* booting state mask... */
#define	SOK	0200	 /* ready for real IO */
#define	SBOOT	0100	 /* ready for download (just been reset) */
#define	SBOOTING 0	 /* in middle of download */
#define	SRESET	0300	 /* finished download, must reset UB base, vector */

/*
 * command bits
 */
#define	CSEND	02		/* send */
#define	CSETB	03		/* announce location of control buffers */
#define	CRCV	04		/* receive */
#define	CBA	06		/* set bus address */
#define	CBC	010		/* set buffer count */
#define	CINTR	012		/* set vector address */
#define	CRESET	014		/* reset interface */
#define	CIACK	016		/* ack interrupt or reset */

#define	CXA	0140		/* high address bits */
#define	XASHIFT	11		/* shift high address bits this much */

typedef short Env;		/* datakit envelope in our buffers */

/*
 * bits in transmit buffer
 */
#define	XDATA	0400		/* data, not control */

/*
 * bits in receive buffer
 */
#define	RMARK	01000		/* channel mark */
#define	RDATA	0400		/* this is data */
#define	RSPCL	0100000		/* special (sign bit): mark or control */
#define	REOT	(RSPCL|RMARK)	/* end of receive buffer, if short */

/*
 * per-channel stuff
 */
#define	NCHAN	256
struct	kc kc[NCHAN];
int	kccnt = NCHAN;
char	kcstate[NCHAN];

/*
 * kc flags
 */
#define	DKXCL	01		/* exclusive open */
#define	DKXWANT	02		/* output pending this channel */

/*
 * per-controller stuff;
 * always just one for now
 */

extern	struct ubaddr cureaddr[];
struct kccure kccure[1];
#define	KBNO	0	/* always this kc for now */
int	kcxfirst;
int	kcxnext;
int	kcrfirst;
int	kcrnext;

/*
 * kccure flags
 */
#define	XBUSY	01	/* transmit busy */
#define	XWANT	02	/* output needed */
#define	INIT	04

/*
 * illicit linkage to other datakit code
 */

struct	dkstat	dkstat;

#define	KNO	0

#define	DKISML	16	/* smallest interesting allocation */
#define	DKITHRES 200

long kcopen();
int kcclose(), kcput(), kcosrv();

static	struct qinit kcrinit = { noput, NULL, kcopen, kcclose, 0, 0 };
	struct qinit kcwinit = { kcput, kcosrv, kcopen, kcclose, 1500, 600 };
struct	streamtab kcinfo = { &kcrinit, &kcwinit };
struct	cdevsw curecdev = cstrinit(&kcinfo);

static	kclastseq;

/*
 * open DK channel
 */
long
kcopen(q, dev)
register struct queue *q;
register dev_t dev;
{
	register struct kc *dkp;
	register struct kccure *kk;
	register chan;
	extern struct dkmodule *dkmodall();
	int kcclock();

	chan = minor(dev);
	if (chan<=0 || chan>=kccnt)
		return(0);
	kk = &kccure[KBNO];
	if ((kk->flags & INIT) == 0) {
		if ((kk->modp = dkmodall(dev, 0, kccnt)) == NULL)
			return (0);
		kk->modp->dkstate = kcstate;
		if (kcinit(kk, 1) == 0)
			return (0);
		kk->flags |= INIT;
		timeout(kcclock, (caddr_t)minor(dev), 10*HZ);
	}
	dkp = &kc[chan];
	if (kcstate[chan] != DKCLOSED) {	/* already open */
		if (dkp->flag & DKXCL)
			return(0);
		if (kcstate[chan] != DKOPEN)
			return(0);	/* closing channels can't reopen */
		return(1);
	}
	dkp->dkrq = q;
	q->ptr = (caddr_t)dkp;
	WR(q)->ptr = (caddr_t)dkp;
	WR(q)->flag |= QNOENB|QBIGB;
	dkp->flag = DKXCL;
	kcstate[chan] = DKOPEN;
	return(1);
}

/*
 * make sure cure is alive;
 * init data structures once
 * it might be better to deal with BDPs dynamically somehow,
 * e.g. on the comet where there are very few
 */
kcinit(kk, firsttime)
register struct kccure *kk;
{
	register struct device *reg;
	register int i;
	register struct kc *dkp;
	static ubm_t map;
	uaddr_t uaddr;
	extern cure0int(), kcreset();

	kk->kno = KNO;
	if ((reg = (struct device *)ubaddr(&cureaddr[0])) == NULL
	||  badaddr(reg, 2)) {
		printf("cure0 absent\n");
		return (0);
	}
	for (i = kccnt - 1, dkp = &kc[i]; i >= 0; --dkp, --i)
		dkp->chan = i;
	kk->addr = reg;
	if ((reg->csr&STATE) != SRESET && (reg->csr&STATE) != SOK)
		return(0);		/* check downloaded OK */
	kclastseq = (reg->csr^SSEQ) & SSEQ;
	if (kcwcmd(kk, CINTR, cureaddr[0].vec) == 0)
		return (0);
	/* allocate and announce command buffers */
	if (firsttime)
		map = ubmalloc(kk->ubno, (2*NCBUF+1)*sizeof(struct cmd), 0);
	else {		/* resetting; free blocks in use */
		for (i=0; i<NCBUF; i++) {
			if (kk->ibp[i])
				freeb(kk->ibp[i]);
			if (kk->obp[i])
				freeb(kk->obp[i]);
			kk->ibp[i] = NULL;
			kk->obp[i] = NULL;
		}
		kcxfirst = kcrfirst = kcxnext = kcrnext = 0;
	}
	uaddr = ubmaddr(kk->ubno, kk->xcbuf, (2*NCBUF+1)*sizeof(struct cmd), map);
	printf("cure reset, uaddr %x map %x\n", uaddr, map);
	kcwcmd(kk, CBA, uaddr);
	kcwcmd(kk, CBC, NCBUF);
	kcwcmd(kk, CSETB|((uaddr>>XASHIFT)&CXA), 0);
	kcwcmd(kk, CIACK, 0);	/* to delay until CSETB processed */
	if ((reg->csr&STATE)!=SOK) {
		printf("cure not ready, state %o\n", reg->csr);
		return(0);
	}
	kcistart(kk);
	return (1);
}

/*
 * close DK channel
 */
kcclose(q)
register struct queue *q;
{
	register struct kc *dkp;
	register struct kccure *kk;

	kk = &kccure[KBNO];
	dkp = (struct kc *)q->ptr;
	if (dkp == NULL)
		panic("kcclose");
	dkp->dkrq = NULL;
	dkp->flag = 0;
	if (kcstate[dkp->chan] == DKRCLOSE || kk->modp->listnrq==NULL)
		kcstate[dkp->chan] = DKCLOSED;
	else if (kcstate[dkp->chan] == DKOPEN)
		kcstate[dkp->chan] = DKLCLOSE;
	if (kk->modp->listnrq)
		putctl2(RD(kk->modp->listnrq), M_PRICTL, DKMCLOSE, dkp->chan);
}

/*
 * interrupt
 */
cure1int(dev) {}
cure0int(dev)
{
	register struct kccure *kk;
	register struct device *reg;
	register doneb, i, csr;


	kk = &kccure[KBNO];
	if ((reg = kk->addr) == NULL)
		return;
	csr = reg->csr;
	i = csr&STATE;
	if (i!=SOK && i!=SRESET)
		return;
	if (csr & (SERR|SUBERR))
		printf("cure status %o\n", reg->csr);
	doneb = reg->buf;
	i = doneb&0377;			/* first rcv buf not done */
	while (i != kcrfirst) {
		kcrecv(kk, kcrfirst);
		kcrfirst = (kcrfirst+1)%NCBUF;
	}
	kcistart(kk);
	i = (doneb>>8)&0377;		/* first xmit buf not done */
	while (i != kcxfirst) {
		kk->flags &=~ XBUSY;
		if (kk->obp[kcxfirst]) {
			freeb(kk->obp[kcxfirst]);
			ubmfree(kk->ubno, kk->omap[kcxfirst]);
		}
		kk->obp[kcxfirst] = NULL;
		kcxfirst = (kcxfirst+1)%NCBUF;
	}
	if (kk->xfirst)
		kcosrv((struct queue *)NULL);
}

/*
 * receive a buffer
 */
kcrecv(kk, i)
register struct kccure *kk;
{
	register struct block *bp, *nbp;
	register struct queue *q;
	register c;
	struct cmd rcbuf;

	rcbuf = kk->rcbuf[i];
	bp = kk->ibp[i];
	if (bp==NULL) {
		printf("null bp in kcrecv\n");
		return;
	}
	kk->ibp[i] = NULL;
	c = rcbuf.chan;
	if (c==0 || c>=kccnt || (q = kc[c].dkrq)==NULL) {
		if (c==0)
			dkstat.pack0++;
		else if (c>=kccnt)
			dkstat.packstrange++;
		else
			dkstat.closepack++;
		freeb(bp);
		return;
	}
	if (q->next->flag&QFULL) {	/* channel overflow */
		freeb(bp);
		return;
	}
	if (rcbuf.count==0)
		freeb(bp);
	else {			/* use small buffer if not much data */
		 if (rcbuf.count<=64 && (nbp=allocb(rcbuf.count))) {
			bcopy(bp->rptr, nbp->rptr, rcbuf.count);
			freeb(bp);
			bp = nbp;
		}
		bp->wptr += rcbuf.count;
		dkstat.input += rcbuf.count;
		(*q->next->qinfo->putp)(q->next, bp);
	}
	for (c=0; c<NCTL; c++) {
		if (rcbuf.ctl[c]==0)
			continue;
		bp = allocb(1);
		*bp->wptr++ = rcbuf.ctl[c];
		if ((rcbuf.ctl[c]&RDATA)==0)
			bp->type = M_CTL;
		(*q->next->qinfo->putp)(q->next, bp);
	}
}

/*
 * start new input buffers
 */
kcistart(kk)
register struct kccure *kk;
{
	register struct device *reg;
	register struct block *bp;
	register i;

	if ((reg = kk->addr) == NULL)
		return;
	while((kcrnext+1)%NCBUF != kcrfirst) {
		i = kcrnext;
		kk->ibp[i] = bp = allocb(1024);
		kk->imap[i] = ubmblk(kk->ubno, bp, 0);
		kk->rcbuf[i].ubaddr = ubadrptr(kk->ubno, bp, kk->imap[i]);
		kk->rcbuf[i].count = bp->lim - bp->base;
		kcwcmd(kk, CRCV, i);
		kcrnext = (i+1)%NCBUF;
	}
}

/*
 * put procedure for output
 */
kcput(q, bp)
register struct queue *q;
register struct block *bp;
{
	register struct kc *dkp;
	register struct kccure *kk;
	register int n;

	dkp = (struct kc *)q->ptr;
	kk = &kccure[KBNO];
	switch (bp->type) {

	case M_IOCTL:
		switch (stiocom(bp)) {
		case DIOCNXCL:
			dkp->flag &=~ DKXCL;
			bp->wptr = bp->rptr;
			bp->type = M_IOCACK;
			break;

		case KIOCINIT:
			/* eventually, reset things here */

		default:
			bp->type = M_IOCNAK;
			break;
		}
		qreply(q, bp);
		return;

	case M_CTL:
	case M_DATA:
		dkstat.output += bp->wptr - bp->rptr;
		putq(q, bp);
		n = spl5();
		if ((dkp->flag & DKXWANT) == 0) {
			if (kk->xfirst == NULL)
				kk->xfirst = dkp;
			else
				kk->xlast->link = dkp;
			dkp->link = NULL;
			kk->xlast = dkp;
			dkp->flag |= DKXWANT;
		}
		if ((kk->flags & XBUSY) == 0)
			qenable(q);
		splx(n);
		return;

	case M_PRICTL:
		switch (*bp->rptr) {
		case DKMCLOSE:
			n = bp->rptr[1];
			if (n < kccnt) {
				if (kcstate[n] == DKOPEN) {
					kcstate[n] = DKRCLOSE;
					putctl(kc[n].dkrq->next, M_HANGUP);
				} else if (kcstate[n] == DKLCLOSE)
					kcstate[n] = DKCLOSED;
			}
			freeb(bp);
			return;

		case DKMXINIT:
			n = bp->rptr[1];
			if (n < kccnt && kcstate[n] == DKOPEN)
				(*kc[n].dkrq->next->qinfo->putp)(kc[n].dkrq->next, bp);
			else
				freeb(bp);
			return;				

		default:
			freeb(bp);
			return;
		}

	default:
		freeb(bp);
		return;
	}
}

kcosrv(junk)
struct queue *junk;
{
	register struct kccure *kk = &kccure[KBNO];
	register s;

	s = spl5();
	while (kk->xfirst) {
		if ((kcxnext+1)%NCBUF == kcxfirst) {
			kk->flags |= XBUSY;
			return;
		}
		if (kcosrvbuf(kcxnext))
			kcxnext = (kcxnext+1)%NCBUF;
	}
	splx(s);
}

/*
 * fill up one buffer and send it
 */

kcosrvbuf(i)
register i;
{

	register struct kccure *kk;
	register struct block *bp;
	register struct queue *q;
	register struct device *reg;
	register struct kc *dkp;
	int c;

	kk = &kccure[KBNO];
	reg = kk->addr;
	dkp = kk->xfirst;
more:
	if (dkp==NULL) {
		printf("null dkp in kcosrvbuf\n");
		return 0;
	}
	if (dkp->dkrq==NULL) {
		dkp->flag &=~ DKXWANT;
		dkp = dkp->link;
		if ((kk->xfirst = dkp) == NULL) {
			kk->xlast = NULL;
			return 0;
		}
		goto more;
	}
	q = WR(dkp->dkrq);
	kk->xcbuf[i].chan = dkp->chan;
	c = 0;
	kk->xcbuf[i].count = 0;
	while ((bp = getq(q)) != NULL) {
		if (bp->type == M_CTL || bp->rptr+NCTL >= bp->wptr) {
			while (bp->rptr < bp->wptr && c < NCTL) {
				kk->xcbuf[i].ctl[c++] = *bp->rptr++ |
				   (bp->type==M_CTL?0:XDATA);
				bp->type = M_DATA;
			}
			if (bp->rptr >= bp->wptr) {
				freeb(bp);
				continue;
			}
		} else if (c==0 && kk->xcbuf[i].count==0) {
			kk->omap[i] = ubmblk(kk->ubno, bp, 0);
			kk->xcbuf[i].ubaddr = ubadrptr(kk->ubno, bp, kk->omap[i]);
			kk->xcbuf[i].count = bp->wptr - bp->rptr;
			kk->obp[i] = bp;
			continue;
		}
		putbq(q, bp);
		break;
	}
	if (bp==NULL) {
		dkp->flag &=~ DKXWANT;
		dkp = dkp->link;
		if ((kk->xfirst = dkp) == NULL)
			kk->xlast = NULL;
	}
	while (c<NCTL)
		kk->xcbuf[i].ctl[c++] = 0;
	kcwcmd(kk, CSEND, i);
	return 1;
}

kcwcmd(kk, cmd, buf)
register struct kccure *kk;
{
	register ocsr;
	register count = 0;
	register state;

	ocsr = kk->addr->csr;
	state = ocsr&STATE;
	if (state != SOK) {
		if (state!=SRESET)
			return(0);
		switch(cmd&037) {
		case CSETB:
		case CBA:
		case CBC:
		case CINTR:
		case CIACK:
			break;
		default:
			if (kcinit(kk, 0)==0)
				return(0);
			break;
		}
	}
	/* wait for last command to be accepted */
	while ((kk->addr->csr&SSEQ) == kclastseq) {
		if (++count > 100000) {
			printf("cure not responding: csr %o\n", kk->addr->csr);
			return(0);
		}
	}
	if (kk->addr->csr & (SERR|SUBERR))
		printf("cure csr %o\n", kk->addr->csr);
	kclastseq = kk->addr->csr&SSEQ;
	kk->addr->buf = buf;
	kk->addr->csr = cmd;
	return(1);
}

kcclock(dev)
caddr_t dev;
{
	cure0int(minor((int)dev));
	timeout(kcclock, dev, 10*HZ);
}

/*
 * raw cure device for downloading
 */
long rcureopen();
int rcureclose(), rcureoput();

static struct qinit rcurerinit = {
	noput, NULL, rcureopen, nulldev, 0, 0
};
static struct qinit rcurewinit = {
	rcureoput, NULL, rcureopen, nulldev, 200, 100
};
struct streamtab rcureinfo = {
	&rcurerinit, &rcurewinit
};

/*
 * config glue
 */
extern struct ubaddr rcureaddr[];		/* one per device */
extern int rcurecnt;			/* one per device or what? */
struct cdevsw rcurecdev = cstrinit(&rcureinfo);

long
rcureopen(q, d)
register struct queue *q;
{
	register dev;
	register struct device *mp;

	if((dev = minor(d)) >= rcurecnt)
		return 0;
	if((mp = (struct device *)ubaddr(&rcureaddr[dev])) == 0
	  || badaddr(mp, sizeof(u_char))) {
		printf("cure %d absent\n", d);
		return 0;
	}
	WR(q)->ptr = q->ptr = (caddr_t) dev;
	return 1;
}

rcureoput(q, bp)
register struct queue *q;
register struct block *bp;
{
#define	DV	017		/* actual device number */
#define	BOOTOK	020		/* initialization flag */

	register struct device *mp =
	     (struct device *)ubaddr(&rcureaddr[(int)q->ptr & DV]);
	register int csr, n;

	switch(bp->type) {

	case M_IOCTL:
		bp->type = M_IOCNAK;
		bp->wptr = bp->rptr;
		switch(stiocom(bp)) {
		case KIOCINIT:			/* reboot cure */
			mp->reset = 1;
			delay(10);
			mp->reset = 0;
			bp->type = M_IOCACK;
			break;
		}
		qreply(q, bp);
		return;

	case M_DATA:
		if (((int)q->ptr&BOOTOK) == 0) {		/* first write */
			if ((mp->csr&STATE) != SBOOT) {
				printf("cure not ready to load, %o\n", mp->csr);
				goto err;
			}
			mp->buf = 070707;	/* magic number starts bootload */
			delay(10);
			if ((mp->csr&STATE) != SBOOTING) {
				printf("cure load err0 %o\n", mp->csr);
				goto err;
			}
			q->ptr = (caddr_t)((int)q->ptr | BOOTOK);
		}
		while (bp->rptr < bp->wptr) {
			if ((mp->csr&STATE) != SBOOTING) {
				printf("cure load err1\n");
				goto err;
			}
			csr = mp->csr;
			mp->buf = *bp->rptr++;
			for (n=0; ((csr ^ mp->csr)&SSEQ)==0 && n<100000; ++n) {
				if (mp->csr&SERR || (mp->csr&STATE)!=SBOOTING) {
					printf("cure load err2 %o\n", mp->csr);
					goto err;
				}
			}
			if (n==100000) {
				printf("cure load err3\n");
				goto err;
			}
		}
		break;
	}
	freeb(bp);
	return;
  err:
	bp->type = M_HANGUP;
	qreply(q, bp);
}