/*
 * stream driver for DK via KMC-11/KDI board
 * delicately intertwined with kmc.c
 */

#include "sys/param.h"
#include "sys/stream.h"
#include "sys/ttyio.h"
#include "sys/dkio.h"
#include "sys/conf.h"
#include "sys/dkstat.h"
#include "sys/dkmod.h"
#include "sys/ubaddr.h"
#include "sys/kmc.h"
#include "sys/kdi.h"
#include "sys/buf.h"
#include "sys/dkwindow.h"

#define	KDIPRI	28

/*
 * flags
 */
#define	KMBUSY	0x01
#define	KMSTOP	0x02
#define	KMINC	0x04
#define	KMBIG	0x3c		/* rcvd lots last time, use big rcv buffer */
#define	KMXCL	0x40		/* exclusive use */
#define	KMXBIG	0x80		/* tell KMC to use large messages */
#define BIGSET(c) ((((c)<<1)|KMINC)&KMBIG | ((c)&~KMBIG))
#define BIGCLR(c) (((c)<<1)&KMBIG | ((c)&~KMBIG))

/*
 * device structure
 */
struct device {
	char	sts;
	char	x1;
	union {
		struct {
			u_short	lo;
			u_char	hi;
		} a;
		struct {
			u_char	x2;
			u_char	x3;
			u_char	ch;
			u_char	ct;
			u_char	sh;
			u_char	st;
		} q;
	} u;
};

/*
 * KMC commands
 */
#define	KC_INIT		1
#define	KC_SEND		2
#define	KC_RCVB		3
#define	KC_CLOSE	4
#define	KC_XINIT	5
#define	KC_CMD		6
#define	KC_FLAG		7
#define	KC_SOI		8

/*
 * subcommands of KC_CMD or KC_SEND
 */
#define	OFLUSH		02	/* flush output */
#define	OSPND		04	/* suspend output */
#define	ORSME		010	/* resume output */
#define	OBOTM		0200	/* send BOTM trailer, not BOT */

/*
 * KMC reports
 */
#define	KS_SEND		024
#define	KS_RDB		025
#define	KS_EOI		026
#define	KS_CNTL		027
#define	KS_ERR		030

/*
 * KC_RCV modes
 */
#define	CBLOCK		0040	/* return on block boundary */
#define	CTIME		0100	/* return when time expires */

/*
 * KS_RDB mode
 */
#define	SFULL		0001	/* buffer full */
#define	SCNTL		0002	/* cntl char recv */
#define	SABORT		0010	/* rcv aborted */
#define	SBLOCK		0040	/* block boundary */
#define	STIME		0100	/* time limit expired */

/*
 * URP control characters
 */
#define	D_DELAY	0100
#define	D_BREAK	0110

/*
 *  KMC errors
 */
#define	E_NOQB	4		/* internal buffer runout */
#define	E_DUP	5		/* duplicate send (shouldn't but does happen) */
#define	E_UMETA	7		/* unknown control character */

/*
 * tracing
 */
#define DEBUG
#ifdef	DEBUG
struct	kin ktrbuf[256];
struct	kin *ktrp = ktrbuf;
#define	TRACE(x) *ktrp++ = x; if (ktrp >= &ktrbuf[256]) ktrp = ktrbuf;
#else
#define	TRACE(x)	;
#endif

int	kdiclose(), kdiput(), kdiisrv();
long	kdiopen();
struct	qinit kdirinit = { noput, kdiisrv, kdiopen, kdiclose, 0, 0 };
struct	qinit kdiwinit = { kdiput, NULL, kdiopen, kdiclose, 512, 128 };
struct	streamtab kdiinfo = { &kdirinit, &kdiwinit };
struct cdevsw kdicdev = cstrinit(&kdiinfo);

struct	kmaddr kmcubaddr();
extern	struct kdikmc kdikmc[];
extern	struct kmcdk k[];
extern	int kdicnt;
extern	struct kmc kmc[];
extern	struct ubaddr kmcaddr[];
extern	int kmccnt;
struct	dkstat	dkstat;

#define	KDICHAN	96		/* channels per kdi (== per kmc) */
#define	KMC(d)	((d)/KDICHAN)
#define	CHAN(d)	((d)%KDICHAN)

/*
 * open channel
 */
long
kdiopen(q, dev)
register struct queue *q;
dev_t dev;
{
	register struct kdi *kp;
	register struct block *bp;
	register struct kdikmc *xp;
	register int kno, chan;

	kno = KMC(minor(dev));
	chan = CHAN(minor(dev));
	if (kno >= kdicnt || kno >= kmccnt || chan >= KDICHAN)
		return (0);
	xp = &kdikmc[kno];
	if (xp->bad)
		return (0);
	if (xp->init == 0)
		if (kdiinit(dev) == 0)
			return (0);
	kp = &xp->kdi[chan];
	if (xp->state[chan] != DKCLOSED) {
		if (kp->ostate&KMXCL)	/* exclusive use until reset */
			return(0);
		if (xp->state[chan] != DKOPEN)
			return(0);	/* closing channels cannot reopen */
		return(1);
	}
	kp->dkrq = q;
	q->ptr = (caddr_t)kp;
	WR(q)->flag |= QDELIM|QBIGB;
	q->flag |= QDELIM;
	WR(q)->ptr = (caddr_t)kp;
	xp->state[chan] = DKOPEN;
	kp->ostate = KMXCL;
	kcmd(kno, KC_INIT, kp->chan, 0, 0, 0, (uaddr_t)NULL);
	if ((bp = allocb(64)) == NULL) {
		xp->state[chan] = 0;
		return(0);
	}
	kp->lastctl = 0;
	kp->ibp = bp;
	kp->imap = ubmblk(xp->ubno, bp, 0);
	kcmd(kno, KC_RCVB, chan, bp->lim-bp->wptr, 50,
	     CBLOCK|CTIME, ubadwptr(xp->ubno, bp, kp->imap));
	return(1);
}

kdiclose(q)
register struct queue *q;
{
	register struct kdi *kp = (struct kdi *)q->ptr;
	register s, i;
	register struct kdikmc *xp;

	xp = &kdikmc[kp->kno];
	if (xp->modp == NULL || xp->modp->listnrq == NULL
	||  xp->state[kp->chan]==DKRCLOSE)
		xp->state[kp->chan] = DKCLOSED;
	else if (xp->state[kp->chan] == DKOPEN) {
		xp->state[kp->chan] = DKLCLOSE;
		for (i=0; i<30 && (WR(q)->count || kp->obp); i++)
			tsleep((caddr_t)kp, KDIPRI, 1);
	}
	kp->dkrq = NULL;
	if (xp->modp && xp->modp->listnrq)
		putctl2(RD(xp->modp->listnrq), M_PRICTL, DKMCLOSE, kp->chan);
	s = spl6();
	if (kp->obp) {
		freeb(kp->obp);
		kp->obp = NULL;
	}
	splx(s);
	kp->ostate &= ~KMXCL;
	kcmd(kp->kno, KC_CLOSE, kp->chan, 0, 0, 0, (uaddr_t)NULL);
}

kdiput(q, bp)
register struct queue *q;
register struct block *bp;
{
	register struct kdi *kdp = (struct kdi *)q->ptr;
	register char *sp;
	register s;
	register struct kdikmc *xp;
	struct block *xbp;
	int t;

	xp = &kdikmc[kdp->kno];
	switch(bp->type) {

	case M_CTL:
	case M_DATA:
	case M_DELAY:
	case M_BREAK:
		if (xp->state[kdp->chan] < DKLCLOSE) {
			freeb(bp);
			return;
		}
		putq(q, bp);
		if ((kdp->ostate&KMBUSY)==0)
			kmstart(kdp);
		return;

	case M_PRICTL:
		switch (*bp->rptr) {
		case DKMCLOSE:
			s = bp->rptr[1];
			if (s < KDICHAN) {
				flushq(q, 1);
				if (xp->state[s] == DKOPEN) {
					xp->state[s] = DKRCLOSE;
					putctl(xp->kdi[s].dkrq->next, M_HANGUP);
				} else if (xp->state[s] == DKLCLOSE)
					xp->state[s] = DKCLOSED;
				kcmd(kdp->kno, KC_CLOSE, s, 0, 0, 0, (uaddr_t)NULL);
			}
			break;

		case DKMXINIT:
			/* retrieve channel number */
			bp->rptr++;
			s = *bp->rptr++;
	
			/* forget it if this isn't a valid channel */
			if (s >= KDICHAN || xp->state[s] != DKOPEN)
				break;
			
			/* next two bytes (if there) are the traffic type */
			if(bp->wptr-bp->rptr>=2){
				t = (*bp->rptr++<<8) & 0xff00;
				t |= *bp->rptr++ & 0xff;
	
				/* check window sizes */
				if (W_VALID(t)) {
	
					/* kmc has only two possible window sizes */
					t = W_VALUE(W_ORIG(t));
					if(t<1024)
						xp->kdi[s].ostate &= ~KMXBIG;
					else
						xp->kdi[s].ostate |= KMXBIG;
				}
			}

			/* restart urp on the channel, with the correct window */
			kdixinit(&xp->kdi[s]);
			break;

		default:
			break;
		}
		break;		/* and freeb */

	case M_IOCTL:
		sp = stiodata(bp);
		bp->type = M_IOCACK;
		switch (stiocom(bp)) {

		case TIOCSDEV:
			if (((struct ttydevb *)sp)->ispeed == 0)
				putctl2(q, M_PRICTL, DKMCLOSE, kdp->chan);
		case KIOCISURP:
			bp->wptr = bp->rptr;
			break;

		case DIOCSCTL:
			bp->wptr = bp->rptr;
			if (*sp == 0)
				break;
			if ((xbp = allocb(1)) == NULL)
				bp->type = M_IOCNAK;
			else {
				xbp->type = M_CTL;
				*xbp->wptr++ = *sp;
				putq(q, xbp);
				if ((kdp->ostate&KMBUSY) == 0)
					kmstart(kdp);
			}
			break;

		case DIOCRCTL:
			*sp = kdp->lastctl;
			kdp->lastctl = 0;
			bp->wptr = (unsigned char *)sp + 1;
			break;

		case DIOCNXCL:
			kdp->ostate &= ~KMXCL;
			bp->wptr = bp->rptr;
			break;

		case TIOCGDEV:
			((struct ttydevb *)sp)->ispeed =
			  ((struct ttydevb *)sp)->ospeed = B9600;
			break;

		case KIOCINIT:
			kdixinit(kdp);
			bp->rptr = bp->wptr;
			break;

		case KIOCSHUT:
			if (kdp->chan > 2) {	/* hackish security attempt */
				bp->type = M_IOCNAK;
				break;
			}
			kdireset(kdp->kno);
			bp->rptr = bp->wptr;
			break;

		case DIOCSTREAM:
			RD(q)->flag &= ~QDELIM;
			bp->rptr = bp->wptr;
			break;

		case DIOCRECORD:
			RD(q)->flag |= QDELIM;
			bp->rptr = bp->wptr;
			break;

		case DIOCXWIN: {
			unsigned int wins;
			/* KMC does big (756) or small (84) windows currently */
			wins = (sp[0] + (sp[1]<<8))*sp[4];
			kdp->ostate &= ~KMXBIG;
			if (wins>=756)
				kdp->ostate |= KMXBIG;
			/* will take effect next XINIT */
			bp->rptr = bp->wptr;
			break;
		    }

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

	case M_STOP:
		s = OSPND;
		kdp->ostate |= KMSTOP;
		goto dontcmd;

	case M_FLUSH:
		flushq(q, 0);
		s = spl6();
		if (kdp->obp) {
			freeb(kdp->obp);	/* this cannot be right -- too early */
			kdp->obp = 0;
		}
		kdp->ostate &= ~(KMSTOP|KMBUSY);
		splx(s);
		s = OFLUSH|ORSME;
		goto docmd;

	case M_START:
	case M_HANGUP:
		s = ORSME;
		kdp->ostate &= ~KMSTOP;
		goto dontcmd;
	docmd:
		kcmd(kdp->kno, KC_CMD, kdp->chan, 0, s, 0, (uaddr_t)NULL);
	dontcmd:
		kmstart(kdp);
		break;

	default:		/* not handled; just toss */
		break;
	}
	freeb(bp);
}

/*
 * restart URP on some channel:
 * send XINIT to KMC;
 * discard any pending transmit block
 *
 * should pending block really be discarded, or requeued?
 * former seems to match real cases
 */

kdixinit(kp)
register struct kdi *kp;
{
	register int s;

	s = spl6();
	kcmd(kp->kno, KC_XINIT, kp->chan, 0, 0, (kp->ostate&KMXBIG)!=0, (uaddr_t)NULL);
	if (kp->obp) {
		freeb(kp->obp);
		kp->obp = NULL;
	}
	if (kp->omap) {
		ubmfree(kdikmc[kp->kno].ubno, kp->omap);
		kp->omap = 0;
	}
	kp->ostate &=~ (KMSTOP|KMBUSY);
	splx(s);
}

kdiisrv(q)
register struct queue *q;
{
	register struct kdi *kp = (struct kdi *)q->ptr;
	register int ubno;
	register struct block *bp;

	if ((q->next->flag&QFULL)==0 && kp->ibp==NULL) {
		bp = allocb(kp->ostate&KMBIG? 1024:64);
		if (bp == NULL)
			panic("kdi: can't alloc");
		kp->ibp = bp;
		ubno = kdikmc[kp->kno].ubno;
		kp->imap = ubmblk(ubno, bp, 0);
		kcmd(kp->kno, KC_RCVB, kp->chan, bp->lim - bp->wptr,
		     50, CBLOCK|CTIME, ubadwptr(ubno, bp, kp->imap));
	}
}

kmstart(kdp)
register struct kdi *kdp;
{
	register s = spl5();
	register struct block *bp;
	register struct queue *qp;
	register c;
	register struct kdikmc *xp;

	if (kdp->ostate&(KMBUSY|KMSTOP)) {
		splx(s);
		return;
	}
	if ((qp = kdp->dkrq)==NULL || (qp = WR(qp))==NULL) {
		splx(s);
		return;
	}
	xp = &kdikmc[kdp->kno];
	if (bp = getq(qp)) switch (bp->type) {

	case M_DATA:
		c = OBOTM;
		if (bp->class&S_DELIM)
			c = 0;
		kdp->omap = ubmblk(xp->ubno, bp, 0);
		kcmd(kdp->kno, KC_SEND, kdp->chan, bp->wptr-bp->rptr,
		     0, c, ubadrptr(xp->ubno, bp, kdp->omap));
		kdp->ostate |= KMBUSY;
		dkstat.output += bp->wptr - bp->rptr;
		kdp->obp = bp;
		break;

	case M_DELAY:
		c = D_DELAY;
		while (*bp->rptr>0) {
			c++;
			*bp->rptr >>= 1;
		}
		kdp->omap = ubmblk(xp->ubno, bp, 0);
		kcmd(kdp->kno, KC_SEND, kdp->chan, 1, c, 0, ubadrptr(xp->ubno, bp, kdp->omap));
		kdp->ostate |= KMBUSY;
		kdp->obp = bp;
		break;

	case M_CTL:
		kdp->omap = ubmblk(xp->ubno, bp, 0);
		kcmd(kdp->kno, KC_SEND, kdp->chan, 0, *bp->rptr, 0, ubadrptr(xp->ubno, bp, kdp->omap));
		kdp->ostate |= KMBUSY;
		kdp->obp = bp;
		break;

	case M_BREAK:
		kdp->omap = ubmblk(xp->ubno, bp, 0);
		kcmd(kdp->kno, KC_SEND, kdp->chan, 0, D_BREAK, 0, ubadrptr(xp->ubno, bp, kdp->omap));
		kdp->ostate |= KMBUSY;
		kdp->obp = bp;
		break;

	default:
		printf("mesg %o in kdi\n", bp->type);
		freeb(bp);
		break;
	}
	splx(s);
}

int kcmdovern[10];
long kcmdovert[10];

kcmd(kno, type, chan, len, ctl, mode, addr)
uaddr_t addr;
{
	register i;
	register struct device *dp = kmc[kno].k_addr;
	register struct kin *kp;
	register s;
	static serno;
	register long loop;

	/*
	 *  if the kmc is dead, don't ask it to do things
	 */
	if(dp->sts ==3)
		return;

	if (dp->u.q.x3)
		dp->u.q.x3 = 0;
busywait:
	/*
	 *  wait at low priority till there's a free command.  No
	 *  sense slowing all processes down.
	 */
	if(((dp->u.q.ch+1)%NKMB)==dp->u.q.ct) {
		loop = 0;
		while (((dp->u.q.ch+1)%NKMB)==dp->u.q.ct) {
			if (loop == 1000)
				printf("waiting for kdi%d\n", kno);
			if(loop++>1000000) {
				printf("kmc%d died, restart\n", kno);
				kdireset(kno);
				return;
			}
		}
		kcmdovern[kno]++;
		if(kcmdovert[kno]<loop)
			kcmdovert[kno] = loop;
	}
	s = spl5();
	/*
	 *  someone else might have gotten in at low priority.  check
	 *  again.
	 */
	if(((dp->u.q.ch+1)%NKMB)==dp->u.q.ct) {
		splx(s);
		goto busywait;
	}
	i = dp->u.q.ch;
	if (i >= NKMB)
		panic("kcmd");
	kp = &k[kno].cmd[i];
	kp->type = type;
	kp->serno = ++serno;
	kp->chan = chan;
	kp->len = len;
	kp->ctl = ctl;
	kp->mode = mode;
	kp->addr.hi = addr>>16;
	kp->addr.lo = addr;
	i++;
	if (i >= NKMB)
		i = 0;
	dp->u.q.ch = i;
	TRACE(*kp);
	splx(s);
}

u_char kdiseqno[10];

/*
 * Interrupt routine-- unload status buffer:
 * release write blocks, collect input
 */
kdiintr(kno)
{
	register struct device *dp = kmc[kno].k_addr;
	register struct kin *sp;
	register struct kdi *kp;
	register struct block *bp;
	register c;
	register struct kdikmc *xp;

	xp = &kdikmc[kno];
	if (dp->u.q.x3)
		dp->u.q.x3 = 0;
	for ( ; (c = dp->u.q.st) != dp->u.q.sh; dp->u.q.st = c) {
		if (c >= NKMB) {
			printf("kdi stat buf is %d\n", c);
			panic("kdiintr");
		}
		sp = &k[kno].stat[c];
		TRACE(*sp);
		c++;
		if (c >= NKMB)
			c = 0;
		if (sp->chan >= KDICHAN) {
			printf("kdi%d stat chan is 0%o\n", kno, sp->chan);
			printf("type: %o len: 0%o mode: 0%o\n");
			continue;
		}
		if(sp->serno!=kdiseqno[kno]){
			printf("kdi%d stat chan is 0%o\n", kno, sp->chan);
			printf("seqno is %d, should be %d\n",sp->serno,
				kdiseqno[kno]);
		}
		kdiseqno[kno]=(sp->serno+1)%256;
		kp = &xp->kdi[sp->chan];
		switch (sp->type) {

		case KS_SEND:
			if (kp->obp) {
				freeb(kp->obp);
				kp->obp = NULL;
			}
			kp->ostate &= ~KMBUSY;
			if (kp->omap) {
				ubmfree(xp->ubno, kp->omap);
				kp->omap = 0;
			}
			if (kp->dkrq && WR(kp->dkrq)->first)
				kmstart(kp);
			break;

		case KS_RDB:
			if (kp->imap) {
				ubmfree(xp->ubno, kp->imap);
				kp->imap = 0;
			}
			bp = kp->ibp;
			kp->ibp = NULL;
			if (kp->dkrq==NULL || xp->state[kp->chan]!=DKOPEN) {
				if (bp)
					freeb(bp);
				break;
			}
			if (sp->mode & SABORT) {
				printf("kdi%d rcv abort chan %d mode %o bp %x\n",
				  kno, kp->chan, sp->mode, bp);
				if (bp)
					kp->ibp = bp;
				kp->rsize = 0;
				kp->ostate &= ~KMBIG;
				break;
			}
			if (bp == NULL) {
				printf("kdi%d: no ibp\n", kno);
				break;
			}
			/* special hacks */
			if (sp->mode&020
			||  sp->mode==0100 && bp->wptr+sp->len==bp->lim) {
				/*
				 *  Something no one understands.  Print out
				 *  some info so that we might figure out
				 *  why this freeb is in the code.
				 */
				printf("kdi%d rcv chan %d mode %o bp %x len %d\n",
				  kno, kp->chan, sp->mode, bp, sp->len);
				freeb(bp);
			} else {
				bp->wptr = bp->lim - sp->len;
				kp->rsize += bp->wptr - bp->rptr;
				dkstat.input += bp->wptr - bp->rptr;
				if(sp->mode&(SBLOCK|STIME)) {
					bp->class |= S_DELIM;
					if (kp->rsize >= 512)
						kp->ostate = BIGSET(kp->ostate);
					else
						kp->ostate = BIGCLR(kp->ostate);
					kp->rsize = 0;
				}
				(*kp->dkrq->next->qinfo->putp)
				       (kp->dkrq->next, bp);
				kp->ibp = NULL;
				if (sp->mode&SCNTL) {
					switch (sp->ctl) {

					case D_BREAK:
						putctl(kp->dkrq->next, M_BREAK);
						break;

					default:
						kp->lastctl = sp->ctl;
						putctl1(kp->dkrq->next, M_CTL, sp->ctl);
						break;

					}
				}
			}
			if ((kp->dkrq->next->flag&QFULL) == 0) {
				bp = allocb(kp->ostate&KMBIG? 1024:64);
				if (bp == NULL)
					panic("kdi: can't alloc");
				kp->ibp = bp;
				kp->imap = ubmblk(xp->ubno, bp, 0);
				kcmd(kno, KC_RCVB, kp->chan, bp->lim - bp->wptr,
				     50, CBLOCK|CTIME, ubadwptr(xp->ubno, bp, kp->imap));
			}
			break;
		
		case KS_EOI:
			printf("kdi%d chan %d rcv EOI %x\n", kno, sp->chan, sp->len);
			break;

		case KS_CNTL:
			/*
			printf("kdi%d chan %d rcv ctl %x\n", kno, sp->chan, sp->len);
			*/
			break;

		case KS_ERR:
			printf("kdi%d chan %d error %d\n", kno, sp->chan, sp->len);
			if (sp->len==E_NOQB || sp->len==E_UMETA
			 || sp->len==E_DUP) {
				printf("ignored\n");
				break;
			}
			kdireset(kno);
			break;

		}
	}
}

/*
 * init things, including data structures
 * -- assume KDICHAN struct kdis and KDICHAN chars for state
 * will fit in a buffer.  when BSIZE is 4096, it's true.
 */
int kdireload();

kdiinit(dev)
int dev;
{
	register struct device *kp;
	register i;
	register struct kdikmc *xp;
	register int kno;
	struct kmaddr ka;
	extern time_t time;

	kno = KMC(minor(dev));
	/* close all open channels? */
	if ((kp = kmc[kno].k_addr) == NULL)
		return (0);
	xp = &kdikmc[kno];
	if (xp->badtime+30 > time)	/* UGH */
		return(0);
	kp->sts = 0;			/* initialize KMC */
	if (xp->buf == NULL) {
		if ((xp->buf = geteblk()) == NULL)
			return (0);
		clrbuf(xp->buf);
		xp->kdi = (struct kdi *)xp->buf->b_un.b_addr;
		xp->state = (char *)&xp->kdi[KDICHAN];
		for (i = 0; i < KDICHAN; i++) {
			xp->kdi[i].chan = i;
			xp->kdi[i].kno = kno;
		}
	}
	if ((xp->modp = dkmodall(dev, kno*KDICHAN, (kno+1)*KDICHAN)) == 0)
		return (0);
	xp->modp->dkstate = xp->state;
	xp->ubno = kmcaddr[kno].ubno;
	if (xp->umap == 0) {
		if ((xp->umap = ubmalloc(xp->ubno, sizeof(struct kmcdk), 0)) == 0) {
			printf("kdi%d: no map\n", kno);
			return (0);
		}
	}
	xp->ubad = ubmaddr(xp->ubno, (caddr_t)&k[kno], sizeof(struct kmcdk), xp->umap);
	k[kno].kinit.cmdaddr = kmcubaddr((caddr_t)k[kno].cmd, (caddr_t)&k[kno], xp->ubad);
	k[kno].kinit.stataddr = kmcubaddr((caddr_t)k[kno].stat, (caddr_t)&k[kno], xp->ubad);
	k[kno].kinit.bufaddr = kmcubaddr((caddr_t)k[kno].kmcbuf, (caddr_t)&k[kno], xp->ubad);
	ka = kmcubaddr((caddr_t)&k[kno].kinit, (caddr_t)&k[kno], xp->ubad);
	kp->u.a.lo = ka.lo;
	kp->u.a.hi = ka.hi;
	xp->badtime = time;
	kp->sts = 1;			/* start handshake */
	for (i = 0; i<100000; i++) {
		if (kp->sts == 2) {
			kmc[kno].k_rint = kdiintr;
			kmc[kno].k_xint = kdiintr;
			kmc[kno].k_reset = kdireload;
			xp->init = 1;
			return(1);			/* KMC is running OK */
		}
		if (kp->sts == 3) {
			printf("kdi%d: entered state 3\n", kno);
			return(0);
		}
	}
	printf("kdi%d: kmc not ready\n", kno);
	return(0);
}

struct kmaddr
kmcubaddr(memaddr, membase, ubbase)
caddr_t memaddr, membase;
uaddr_t ubbase;
{
	struct kmaddr r;
	register long a;

	a = (long)memaddr - (long)membase + (long)ubbase;
	r.hi = a >> 16;
	r.lo = (u_short)a;
	return(r);
}

/*
 * kmc reload
 */
kdireload(kno)
{
	register struct kdikmc *xp;

	xp = &kdikmc[kno];
	xp->init = 0;
	xp->modp = 0;
	xp->bad = 0;
}

/*
 * zap all open channels
 */
kdireset(kno)
{
	register struct kdi *kp;
	register struct kdikmc *xp;
	register struct device *dp;

	xp = &kdikmc[kno];
	if (xp->modp==0)
		return;
	if ((dp = kmc[kno].k_addr) != NULL)
		dp->sts = 3;	/* disable kdi kmc code */
	for (kp = &xp->kdi[KDICHAN-1]; kp >= xp->kdi; --kp) {
		register state = xp->state[kp->chan];
		if ((state==DKOPEN || state==DKLCLOSE) && kp->dkrq) {
			flushq(WR(kp->dkrq), 1);
			xp->state[kp->chan] = state==DKOPEN? DKRCLOSE: DKCLOSED;
			putctl(kp->dkrq->next, M_HANGUP);
		}
		if (kp->imap)
			ubmfree(xp->ubno, kp->imap);
		if (kp->omap)
			ubmfree(xp->ubno, kp->omap);
		kp->imap = kp->omap = 0;
	}
	xp->bad = 1;
}
