V10/sys/io/kb.c

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

/*
 * simple two-stream kmc datakit driver
 * -- assumes it is the first kmc, 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/kb.h"
#include "sys/kmc.h"
#include "sys/dkstat.h"
#include "sys/dkmod.h"

/*
 * hardware stuff
 */

struct device {
	short	sts;	/* written by KMC */
	short	cmd;	/* command and transmit chan -- written by host */
	short	ba;	/* low bus address -- written by host */
	short	bc;	/* negative buffer size -- written by host */
};

/*
 * status info
 */
#define	SCOUNT	0xfff	/* receive buffer resid count */
#define	SRRDY	(020<<8)	/* receive ready */
#define	SOK	(040<<8)	/* init ok */
#define	SERROR	(0100<<8)	/* transfer or other error */
#define	SXRDY	(0200<<8)	/* transmit ready */

/*
 * commands
 */
#define	CGO	(01<<8)		/* turned off when command read */
#define	CRCV	(01<<8)		/* here is a receive buffer */
#define	CSEND	(03<<8)		/* send this buffer of data */
#define	CSCTL	(043<<8)	/* send this data, first char is ctl */
#define	CIE	(020<<8)	/* interrupt after this command */
#define	CXA	(014<<8)	/* high address bits */
#define	XASHIFT	10		/* shift high address bits this much */

#define	STALL	10000		/* max time to stall waiting for ready */
/*
 * per-channel stuff
 */
extern struct kb kb[];
extern int kbcnt;
extern char kbstate[];

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

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

struct kbkmc kbkmc[1];
#define	KBNO	0	/* always this kb for now */

/*
 * kbkmc flags
 */
#define	INIT	01

/*
 * illicit linkage to other datakit code
 */

struct	dkstat	dkstat;

/*
 * illicit linkage to kmc driver
 */

extern struct kmc kmc[];
extern struct ubaddr kmcaddr[];
extern int kmccnt;
#define	KNO	0		/* kmc0 is ours, by fiat */

#define	DKISIZE	1024		/* preferred input buffer size */
#define	MAXOBUF	4096		/* max allowed output buffer -- for unibus map */

long kbopen();
int kbclose(), kbput(), kbosrv();

static	struct qinit kbrinit = { nodev, NULL, kbopen, kbclose, 0, 0 };
	struct qinit kbwinit = { kbput, NULL, kbopen, kbclose, 1500, 600 };
struct	streamtab kbinfo = { &kbrinit, &kbwinit };
struct	cdevsw kbcdev = cstrinit(&kbinfo);

/*
 * open DK channel
 */
long
kbopen(q, dev)
register struct queue *q;
register dev_t dev;
{
	register struct kb *dkp;
	register struct kbkmc *kk;
	register chan;

	chan = minor(dev);
	if (chan<=0 || chan>=kbcnt)
		return(0);
	kk = &kbkmc[KBNO];
	if ((kk->flags & INIT) == 0) {
		if ((kk->modp = dkmodall(dev, 0, kbcnt)) == NULL)
			return (0);
		kk->modp->dkstate = kbstate;
		if (kbinit(kk) == 0)
			return (0);
		kk->flags |= INIT;
	}
	dkp = &kb[chan];
	if (kbstate[chan] != DKCLOSED) {	/* already open */
		if (dkp->flag & DKXCL)
			return(0);
		if (kbstate[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;
	dkp->chan = chan;
	kbstate[chan] = DKOPEN;
	return(1);
}

/*
 * make sure kmc is alive;
 * init data structures once
 */
kbinit(kk)
register struct kbkmc *kk;
{
	register struct kmc *kp;
	register struct device *reg;
	register int i;
	extern kbintr(), kbreset();

	kk->kno = KNO;
	kk->ubno = kmcaddr[KNO].ubno;	/* cheat */
	kp = &kmc[kk->kno];
	if ((reg = kp->k_addr) == 0)
		return (0);
	if ((reg->sts & SOK) == 0)
		return (0);
	if ((kk->omap = ubmalloc(kk->ubno, MAXOBUF, UBDP)) == 0)
		return (0);
	kp->k_rint = kbintr;
	kp->k_xint = kbintr;
	kp->k_reset = kbreset;
	kk->addr = reg;
	i = spl5();
	kbistart(kk);
	splx(i);
	return (1);
}

/*
 * called when, e.g., kmc is reloaded
 */
kbreset(kno)
int kno;
{
	register struct kbkmc *kk;
	register int s;

	kk = &kbkmc[KNO];
	if (kk->obuf) {
		freeb(kk->obuf);
		kk->obuf = NULL;
	}
	if (kk->imap) {
		ubmfree(kk->ubno, kk->imap);
		kk->imap = 0;
	}
	if (kk->ibuf) {
		freeb(kk->ibuf);
		kk->ibuf = NULL;
	}
	s = spl5();
	kbistart(kk);
	splx(s);
}

/*
 * close DK channel
 */
kbclose(q)
register struct queue *q;
{
	register struct kb *dkp;
	register struct kbkmc *kk;

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

/*
 * interrupt -- receiver only
 * try to short-circuit interrupts;
 * data often comes in a large piece, followed by two small ones (URP trailer)
 */

kbintr(dev)
{
	register struct kbkmc *kk;
	register struct block *bp;
	register struct queue *q;
	register int i;

	kk = &kbkmc[KBNO];
	if (kk->addr == NULL)
		return;
	i = kk->addr->sts;
	if ((bp = kk->ibuf) == NULL) {	/* shouldn't happen */
		kbistart(kk);
		return;
	}
	ubmfree(kk->ubno, kk->imap);
	kk->ibuf = NULL;
	kbistart(kk);
	i |= ~SCOUNT;	/* crypto-sign-extend */
	if ((i & SCOUNT) == 0)
		i = 0;	/* overflow just means buffer full */
	bp->wptr = bp->lim + i;		/* lim - residue */
	i = *bp->rptr;			/* channel */
	bp->rptr++;
	if (*bp->rptr)			/* ctl flag */
		bp->type = M_CTL;
	bp->rptr++;
	if (i == 0 || i >= kbcnt || (q = kb[i].dkrq) == NULL) {
		if (i == 0)
			dkstat.pack0++;
		else if (i >= kbcnt)
			dkstat.packstrange++;
		else
			dkstat.closepack++;
		freeb(bp);
		return;
	}
	dkstat.input += bp->wptr - bp->rptr;
	if ((q->next->flag & QFULL) == 0)
		(*q->next->qinfo->putp)(q->next, bp);
	else
		freeb(bp);
}

/*
 * start the next input buffer
 */
kbistart(kk)
register struct kbkmc *kk;
{
	register struct device *reg;
	register int sts;
	register struct block *bp;
	register int n;
	register uaddr_t ua;

	if ((reg = kk->addr) == NULL)
		return;
	if (kk->ibuf)
		return;
	for (sts = 0, n = 0; n < STALL; n++) {
		if ((reg->cmd & CGO) == 0
		&&  (sts = reg->sts) & SRRDY)
			break;
		/* delay to free up unibus? */
	}
	if ((sts & SRRDY) == 0) {
		printf("kb rcv not ready, sts %o cmd %o\n", reg->sts, reg->cmd);
		panic("kbistart");
		return;
	}
	if (sts & SERROR)
		printf("kb err, sts %o\n", sts&0177777);
	if ((bp = allocb(DKISIZE)) == NULL
	||  (kk->imap = ubmblk(kk->ubno, bp, 0)) == 0) {
		panic("kbistart can't alloc\n");	/* later printf */
		if (bp)
			freeb(bp);
		return;
	}
	kk->ibuf = bp;
	if ((int)bp->rptr & 01)
		bp->rptr++;
	kk->iaddr = ua = ubadrptr(kk->ubno, bp, kk->imap);
	reg->bc = bp->rptr - bp->lim;	/* sic - negative count */
	reg->ba = ua;
	n = (ua >> 16) << XASHIFT;
	n &= CXA;
	n |= CIE|CRCV;
	reg->cmd = n;
}

/*
 * put procedure for output
 */
kbput(q, bp)
struct queue *q;
register struct block *bp;
{
	register struct kb *dkp;
	register struct kbkmc *kk;
	register int n;
	register struct device *reg;
	register int sts;
	int s;

	dkp = (struct kb *)q->ptr;
	kk = &kbkmc[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:
		if (bp->rptr == bp->wptr) {
			freeb(bp);
			return;
		}
		dkstat.output += bp->wptr - bp->rptr;
		s = spl5();
		reg = kk->addr;
		for (sts = 0, n = 0; n < STALL; n++) {
			if ((reg->cmd & CGO) == 0
			&&  (sts = reg->sts) & SXRDY)
				break;
			DELAY(40);	/* get off the UNIBUS */
		}
		if ((sts & SXRDY) == 0) {	/* n >= STALL */
			printf("kb xmit not ready, sts %o, cmd %o\n", sts, reg->cmd);
			splx(s);
			freeb(bp);
			return;
		}
		if (sts & SERROR)
			printf("kb err, sts %o\n", sts&0177777);
		ubmflush(kk->ubno, ubmpath(kk->omap));
		if (kk->obuf) {
			freeb(kk->obuf);
			kk->obuf = NULL;
		}
		n = bp->wptr - bp->rptr;
		kk->oaddr = ubmaddr(kk->ubno, bp->rptr, n, kk->omap);
		reg->bc = -n;
		reg->ba = kk->oaddr;
		n = (kk->oaddr >> 16)<<XASHIFT;
		n &= CXA;
		n |= dkp->chan;
		if (bp->type == M_DATA)
			n |= CSEND;
		else
			n |= CSCTL;
		reg->cmd = n;
		kk->obuf = bp;
		splx(s);
		return;

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

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

		default:
			freeb(bp);
			return;
		}

	default:
		freeb(bp);
		return;
	}
}