V8/usr/sys/dev/sn.c

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

#define BSD41
#define	VAXONE
#define TIMEOUT

#ifdef MARVIN
#define TXDRIVER
#endif
#ifdef CANTOR
#define TXDRIVER
#endif
#ifdef HILBERT
#define TXDRIVER
#endif
#ifdef DAISY
#define TXDRIVER
#endif

#ifdef USG5
#define NSN 1
#else
#include "sn.h"
#endif
#if NSN > 0

#ifdef BSD41
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/buf.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/pte.h"
#include "../h/map.h"
#include "../h/vm.h"
#include "../h/dk.h"
#include "../h/cmap.h"
#include "../h/ubareg.h"
#include "../h/ubavar.h"
#include "../h/cpu.h"
#include "../h/proc.h"
#include "../h/snet.h"
#include "../h/packet.h"
#include "../h/channel.h"
#include "../h/status.h"
#endif

#ifdef SNDEBUG
int sndebug = 1;
#else
int sndebug = 0;
#endif

/* flags for status */
#define INPUT	1
#define OUTPUT	2

#define min(x,y) ((x) < (y) ? (x) : (y))
#define swab(x) (unsigned short)((((x)>>8)&0xff) | ((x) << 8))

#define SNPRI	28

#define CHKINIT 0xbf37

struct device {
	short drcs;
	short drout;
	short drin;
};

/* Command and Status word */
#define CSR0 0x1
#define CSR1 0x2
#define RIE 0x20
#define TIE 0x40
#define TEMPTY 0x80
#define RFULL 0x8000

/*
 *	CSR1 CSR0
 *	 0    0 	Write Data
 *	 0    1 	Write EOP
 *	 1    0 	Write Command
 *	 1    1 	Read Status
 */
#define DATA_MODE	(RIE)
#define EOP_MODE	(CSR0 | RIE)
#define CMD_MODE	(CSR1 | RIE)
#define STATUS_MODE	(CSR0 | CSR1 | RIE)

/* Command word format
 *
 *	+-----------------------------------------------+
 *	|  |B |R |A |           |              |        |
 *	|  |C |S |B |    DID    |              |  FCN   |
 *	|  |T |T |T |           |              |        |
 *	+-----------------------------------------------+
 *	 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
 */
/* Function codes */
#define NOP  	0x0
#define BOARD_RESET	0x5
#define MASTER_CLEAR	0x6

#define ABORT(did)	(0x1000 | (did))
#define RESET(did)	(0x2000 | (did))
#define BROAD_CAST(did)	(0x4000 | (did))


/* SNET status word bits
 *
 *	+-----------------------------------------------+
 *	|                             |E |S |I |O |  |  |
 *	|                             |O |N |B |B |  |  |
 *	|                             |P |K |E |E |  |  |
 *	+-----------------------------------------------+
 *	 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
 */	
#define OUTBUF_E	0x4
#define INBUF_E	0x8
#define SNACK	0x10	/* transmitter error */
#define EOP	0x20	/* we have read and EOP frame */

static int sn_multiplex;

struct openwait sn_openwait[NUMDEV];
struct openwait *sn_owhead, *sn_owtail;

#define SNFILE(dev)  (minor(dev) & 0x3f)
#define SNUNIT(dev) ((minor(dev) & 0xc0) >> 6)

struct chdat sn_chans[NUMLCH];
#define MAXMSGLEN (512+8)
struct sn_buffers {
	short inbuf[MAXMSGLEN];
	short outbuf[MAXMSGLEN];
} sn_buffers[NUMLCH];

#define NOPDID -1

#ifdef TIMEOUT
#define CKFUZZ 	4	/* number of timeouts we wait for a response */
#ifdef USG5
#define CKTICKS	(HZ/4)	/* number of ticks per timeout */
#else
#define CKTICKS	(hz/4)	/* number of ticks per timeout */
#endif

int sn_state;
#define	OPEN 1		/* set if at least one sn? is open */
#define	TIMER 2		/* set if at timer is running */

int cktimchk();
int sn_opncnt;
#endif TIMEOUT

struct snmach snmach[NUMDEV] = {
	{ 1, 255 },
	{ 2, 254 },
	{ 3, 253 },
	{ 4, 252 },
	{ 5, 251 },
	{ 6, 250 },
	{ 7, 249 },
	{ 8, 248 } };

int sn_once;

#ifdef BSD42
#define RETURN(x)	{return(x);}
#define ARGS(x)		args->x
#else
#define RETURN(x)	{u.u_error = x; return;}
#define ARGS(x)		args.x
#endif

#ifdef USG5
extern struct sninfo sn_sninfo[];
extern int sn_cnt;
extern struct device *sn_addr[];
#else
struct sninfo sn_sninfo[NSN];
int	snprobe(), snattach();
struct	uba_device *sndinfo[NSN];
u_short	snstd[] = {0};
struct uba_driver sndriver =
	{ snprobe, 0, snattach, 0, snstd, "sn", sndinfo};

snprobe(reg)
caddr_t reg;
{
	register int br, cvec;	/* value result */
	register struct device *snaddr = (struct device *)reg;

	br = 0x15;
#ifdef VAXONE
	cvec = 0510;
#else
#ifdef CANTOR
	if (((int)snaddr & 017777) == 007770)
		cvec = 0504;
	if (((int)snaddr & 017777) == 007750)
		cvec = 0464;
#else
	cvec = 0504;
#endif
#endif
#ifdef BSD41
	return(1);
#endif
#ifdef BSD42
 	return(sizeof(struct device));
#endif
}

/*ARGSUSED*/
snattach(ui)
register struct uba_device *ui;
{
}
#endif

snopen(dev, rw)
dev_t dev;
int rw;
{
	register struct device *draddr;
#ifndef USG5
	struct uba_device *ui;
#endif
	register struct sninfo *sninfop;
	int mdev = SNFILE(dev);
	int unit = SNUNIT(dev);

	if( sndebug )
		printf("snopen(%x,%x)\n", dev, rw);

#ifdef USG5
	if ( mdev > NUMDEV || unit >= sn_cnt) {
		RETURN ( ENXIO );
	}
	draddr = sn_addr[unit];
#else
	if (mdev > NUMDEV || unit >= NSN ||
	    (ui = sndinfo[unit]) == 0 || ui->ui_alive == 0) {
		RETURN ( ENXIO );
	}
	draddr = (struct device *)ui->ui_addr;
#endif

	if ( sn_once == 0 ) {
		struct chdat *cp = sn_chans;

		sn_once++;
		for( ; cp<&sn_chans[NUMLCH]; cp++) {
			cp->lchnum = -1;
			cp->dlchnum = -1;
		}
		sn_owhead = sn_owtail = sn_openwait;
	}

	sninfop = &sn_sninfo[unit];
	spl6();
	if (sninfop->sndevlock[mdev]++ != 0) {
		spl0();
		RETURN ( EBUSY );
	}
#ifdef TIMEOUT
	sn_opncnt++;
	sn_state |= OPEN;
	if ((sn_state & TIMER) == 0) {
		sn_state |= TIMER;
		timeout(cktimchk, (caddr_t)0, CKTICKS);
	}
#endif TIMEOUT
	if( sninfop->snlock++ == 0 ) {
#ifdef TXDRIVER
		{
		extern struct txinfo tx_sninfo[];

		if (tx_sninfo[unit].snlock != 0) {
			spl0();
			return;
		}
		}
#endif
		draddr->drcs = CMD_MODE;
		draddr->drout = 0;
		draddr->drout = BOARD_RESET;
		draddr->drout = 0;
		draddr->drcs = DATA_MODE;
	}

	spl0();
	return(0);
}

snclose(dev)
{
	register struct chdat *cp;
#ifndef USG5
	struct uba_device *ui;
#endif
	register struct sninfo *sninfop;
	register struct device *draddr;
	int mdev = SNFILE(dev);
	int unit = SNUNIT(dev);
	int resetmachno;
	struct openwait *curtail;
	
	if( sndebug )
		printf("snclose(%x)\n", dev);
#ifdef USG5
	if ( unit >= sn_cnt) {
		RETURN ( ENXIO );
	}
	draddr = sn_addr[unit];
#else
	if ( unit >= NSN) {
		RETURN ( ENXIO );
	}
	ui = sndinfo[unit];
	draddr = (struct device *)ui->ui_addr;
#endif

	sninfop = &sn_sninfo[unit];
	spl6();
	if( sninfop->snlock == 0 || mdev > NUMDEV 
			|| sninfop->sndevlock[mdev] == 0 ) {
		spl0();
		RETURN ( ENXIO );
	}

	resetmachno = -1;
	for(cp=sninfop->snlink[mdev]; cp; cp=cp->link) {
		if (cp->pdid == NOPDID)
			resetmachno = cp->machno;
		chclose(cp);
	}
	if (resetmachno != -1) {
		for(cp=sn_chans; cp < &sn_chans[NUMLCH]; cp++) {
			if (cp->pdid == NOPDID && cp->machno == resetmachno)
				goto noreset;
		}
		draddr->drcs = CMD_MODE;
		draddr->drout =  RESET(resetmachno << 8);
		draddr->drcs = EOP_MODE;
		draddr->drout = 0;
		draddr->drcs = DATA_MODE;
	}

noreset:
	for (curtail = sn_owtail; curtail != sn_owhead; 
			curtail = ownext(curtail)) {
		if (curtail->pid == u.u_procp->p_pid) {
			curtail->pid = 0;
			goto nowakeup;
		}
	}
	while (sn_owtail != sn_owhead) {
		curtail = sn_owtail;
		sn_owtail = ownext(sn_owtail);
		if (curtail->pid) {
			wakeup(curtail);
			break;
		}
	}

nowakeup:
	sninfop->snlink[mdev] = 0;
	sninfop->sndevlock[mdev] = 0;
#ifdef TIMEOUT
	if (--sn_opncnt <= 0) {
		sn_state &= ~OPEN;
		sn_opncnt = 0;
	}
#endif TIMEOUT
	if (--sninfop->snlock == 0 ) {
#ifdef TXDRIVER
		{
		if (tx_sninfo[unit].snlock != 0)
			return;
		}
#endif
		draddr->drcs &= ~RIE;
	}
	spl0();
	return(0);
}

static short ratshole[MAXMSGLEN];
static struct chdat ratschan;

#ifdef USG5
snintr(unit)
#else
snrint(unit)
#endif
int unit;
{
	register struct chdat *cp;
	register short *sp, *dp;
	register int i;
	register struct sninfo *sninfop;
	register struct device *draddr;
	unsigned short chksum, xchk;
	short len;
	unsigned short chan;
	short *osp;
#ifndef USG5
	struct uba_device *ui;
#endif

#ifdef USG5
	draddr = sn_addr[unit];
#else
	ui = sndinfo[unit];
	draddr = (struct device *)ui->ui_addr;
#endif
	dp = &draddr->drin;

	sninfop = &sn_sninfo[unit];
	sninfop->snrcnt++;

loop:
	chksum = CHKINIT;

	chan = *dp;
#ifdef TXDRIVER
	/* unusual process numbers go to tx driver */
	if ((chan & 0xff) <= 200 || ((unsigned int)chan) >= 0x2000 ) {
		txintrmt(unit, chan);
		goto getstatus;
	}
#endif
	chksum ^= chan;
	chan = swab(chan);
	len = *dp;
	chksum ^= len;
	len = swab(len);
	if( len > MAXMSGLEN ) {
		sninfop->snnbadlen++;
		goto getstatus;
	}
	sninfop->sninbytes += len+4;

	for(cp=sn_chans; cp < &sn_chans[NUMLCH]; cp++)
		if( cp->xchan == chan && cp->snunit == unit ) {
			if( cp->input.done == 0 && len <= cp->input.len )
				osp = sp = cp->input.buf;
			else
				osp = sp = ratshole;
			goto found;
		}
	osp = sp = ratshole;
	cp = &ratschan;
	sninfop->snnnolc++;
found:
			
	for(i=len; i>0; i-=8) {
		switch( i ) {
		default:
			*sp = *dp;
			chksum ^= *sp++;
		case 7:
			*sp = *dp;
			chksum ^= *sp++;
		case 6:
			*sp = *dp;
			chksum ^= *sp++;
		case 5:
			*sp = *dp;
			chksum ^= *sp++;
		case 4:
			*sp = *dp;
			chksum ^= *sp++;
		case 3:
			*sp = *dp;
			chksum ^= *sp++;
		case 2:
			*sp = *dp;
			chksum ^= *sp++;
		case 1:
			*sp = *dp;
			chksum ^= *sp++;
		}
	}
	if( (i = ((len < 0 ? 0 : len) + 3)&0xf) != 0 ) {
		for(i^=0xf; i-->=0; )
			chksum ^= *dp;
	}
	if( chksum != (xchk = *dp) ) {
		sninfop->snnchksum++;
		if( sndebug ) {
			short *usp = osp;
			int x = len;

			printf("snchksum (%x, %x):", chan, len);
			for(; --x>=0; )
				printf(" %x", *usp++);
			printf(" %x (%x)\n", xchk, chksum);
		}
	} else if( len > 0 ) {
		if( osp == ratshole ) {
			cp->flags |= SENTRNR;
			snxstart(unit, cp, RNR);
			sninfop->snsrnr++;
		} else {
			sninfop->snndata++;
			cp->input.done = 1;
			cp->input.len = len;
			snxstart(unit, cp, ACK);
			sninfop->snsack++;
			wakeup((caddr_t)cp->input.buf);
			if( cp->flags&MULTIPLEX )
				wakeup((caddr_t)&sn_multiplex);
		}
	} else {
		switch( len ) {
		case ACK:
			sninfop->snnack++;
			cp->flags &= ~SENTDATA;
			cp->output.done = 1;
			wakeup((caddr_t)cp->output.buf);
			break;

		case RDY:
			sninfop->snnrdy++;
			snxstart(unit, cp, RACK);
			if( cp->output.done == 0 && cp->output.len > 0 ) {
				snxstart(unit, cp, DATA);
				sninfop->snrout++;
			}
			break;

		case RNR:
			sninfop->snnrnr++;
			cp->flags &= ~SENTDATA;
			break;

		case RACK:
			sninfop->snnrack++;
			cp->flags &= ~SENTRDY;
			break;

		default:
			printf("snrint: unit %d, bad type %d\n", unit, len);
			break;
		}
	}

getstatus:
	draddr->drcs = STATUS_MODE;
	i = *dp;
	draddr->drcs = DATA_MODE;
	if ((i&INBUF_E) == 0) {
		sninfop->sninloop++;
		goto loop;
	}
}

snxstart(unit, cp, type)
int unit;
register struct chdat *cp;
int type;
{
	register short *sp, *dp;
	register struct sninfo *sninfop;
	register struct device *draddr;
	register int i;
	unsigned short chksum;
#ifndef USG5
	struct uba_device *ui;
#endif
	int len, numretry;

#ifdef USG5
	draddr = sn_addr[unit];
#else
	ui = sndinfo[unit];
	draddr = (struct device *)ui->ui_addr;
#endif
	dp = &draddr->drout;

	sninfop = &sn_sninfo[unit];

	if( type == DATA )  {
		len = cp->output.len;
		sninfop->snoutbytes += len+4;
	} else {
		len = type;
		sninfop->snoutbytes += 4;
	}
	draddr->drcs = STATUS_MODE;

	i = draddr->drin;
	if ( (i&OUTBUF_E) == 0 ) {
		sninfop->snoutfull++;
		do {
			i = draddr->drin;
			sninfop->sncntoutfull++;
		} while( (i&OUTBUF_E) == 0 );
	}
	
	numretry = 0;
	sninfop->snxcnt++;
retry:
	sp = cp->output.buf;
	chksum = CHKINIT;
	if (cp->pdid == NOPDID) {
		draddr->drcs = CMD_MODE;
		*dp = cp->machno<<8;
		draddr->drcs = DATA_MODE;
		*dp = i = swab(cp->dlchnum);
	} else {
		draddr->drcs = CMD_MODE;
		*dp = cp->pdid<<8;
		draddr->drcs = DATA_MODE;
		*dp = i = (cp->dlchnum<<8) | cp->machno;
	}
	chksum ^= (unsigned short)i;
	*dp = i = swab(len);
	chksum ^= (unsigned short)i;
	for( i=len; i>0; i-=8 ) {
		switch( i ) {
		default:
			chksum ^= *sp;
			*dp = *sp++;
		case 7:
			chksum ^= *sp;
			*dp = *sp++;
		case 6:
			chksum ^= *sp;
			*dp = *sp++;
		case 5:
			chksum ^= *sp;
			*dp = *sp++;
		case 4:
			chksum ^= *sp;
			*dp = *sp++;
		case 3:
			chksum ^= *sp;
			*dp = *sp++;
		case 2:
			chksum ^= *sp;
			*dp = *sp++;
		case 1:
			chksum ^= *sp;
			*dp = *sp++;
		}
	}
	if( (i = ((len < 0 ? 0 : len) + 3)&0xf) != 0 ) {
		for(i^=0xf; i-->=0; )
			*dp = 0;
	}
	draddr->drcs = EOP_MODE;
	*dp = chksum;
	draddr->drcs = STATUS_MODE;
	i = draddr->drin;
	draddr->drcs = DATA_MODE;
	if( i&SNACK ) {
		sninfop->snsnack++;
		if( ++numretry < 100 )
			goto retry;
		sninfop->snlost++;
	}
	cp->ckticks = CKFUZZ;
	if( type == DATA )  {
		cp->flags |= SENTDATA;
	}
}

snioctl(dev, cmd, addr, flag)
caddr_t addr;
dev_t dev;
{
	register struct chdat *cp;
	register struct reqinfo *rp;
	register struct sninfo *sninfop;
	register struct bufinfo *bp;
	register struct chdat *dp;
	struct status status;
	int i;
	struct status *ustatus;
	char *usaddr;
	int ulen;
	int error;
	int mdev = SNFILE(dev);
	int unit = SNUNIT(dev);

#ifdef USG5
	if ( unit >= sn_cnt) {
		RETURN ( ENXIO );
	}
#else
	if ( unit >= NSN) {
		RETURN ( ENXIO );
	}
#endif
	sninfop = &sn_sninfo[unit];

	switch( cmd ) {
	case NIOOPEN: {
#ifdef BSD42
 		struct oargs *args = (struct oargs *)addr;
#else
		struct oargs args;

		if (copyin(addr, (caddr_t)&args, sizeof(args))) {
			RETURN ( EFAULT );
		}
#endif
		spl6();
		for(cp=sn_chans; cp<&sn_chans[NUMLCH]; cp++)
			if( cp->lchnum == -1 && cp->dlchnum == -1 )
				break;
		if( cp >= &sn_chans[NUMLCH] ) {
			spl0();
			RETURN(  ENFILE );
		}
		for(dp=sn_chans; dp<&sn_chans[NUMLCH]; dp++)
			if( dp->lchnum != -1 && dp->lmachno == ARGS(lmachno)
			   && dp->lchnum == ARGS(lchno)
			   && dp->snunit == unit ) {
				spl0();
				RETURN(  EEXIST );
			}
		cp->lchnum = ARGS(lchno);
		cp->dlchnum = ARGS(dlchno);
		spl0();
		cp->lmachno = ARGS(lmachno);
		cp->machno = ARGS(dmachno);
		cp->xchan = (cp->lmachno<<8)|cp->lchnum;
		cp->snunit = unit;
		cp->pdid = NOPDID;
		bp = &cp->input;
		i = cp-sn_chans;
		bp->buf = sn_buffers[i].inbuf;
		bp->len = MAXMSGLEN;
		bp->done = 0;
		bp = &cp->output;
		bp->buf = sn_buffers[i].outbuf;
		bp->len = bp->done = 0;
		cp->flags = 0;
		cp->link = sninfop->snlink[mdev];
		sninfop->snlink[mdev] = cp;
		return(0);
	    }

	case NIOXOPEN: {
		struct snmach *snmachp;
		struct openwait *nexthead, *thishead;
#ifdef BSD42
 		struct oargs *args = (struct oargs *)addr;
#else
		struct oargs args;

		if (copyin(addr, (caddr_t)&args, sizeof(args))) {
			RETURN ( EFAULT );
		}
#endif
		for (;;) {
			for (snmachp = &snmach[0];
					snmachp < &snmach[NUMDEV] &&
						snmachp->machno;
					snmachp++) {
				spl6();
				for (cp=sn_chans; cp<&sn_chans[NUMLCH]; cp++)
					if( cp->lchnum == -1 && 
							cp->dlchnum == -1 )
						break;
				if( cp >= &sn_chans[NUMLCH] ) {
					goto xopensleep;
				}
				for(dp=sn_chans; dp<&sn_chans[NUMLCH]; dp++) {
					if( dp->lchnum != -1
					   && dp->lmachno == snmachp->lmachno
					   && dp->lchnum == ARGS(lchno)
					   && dp->snunit == unit ) {
						/* Channel already in use */
						break;
					}
				}
				if( dp >= &sn_chans[NUMLCH] ) 
					goto okmachp;
				spl0();
			}
xopensleep:
			nexthead = ownext(sn_owhead);
			if (nexthead == sn_owtail) {
				RETURN ( ENXIO );
			}
			thishead = sn_owhead;
			thishead->pid = u.u_procp->p_pid;
			sn_owhead = nexthead;
			sleep((caddr_t)thishead, SNPRI);
		}
okmachp:
		cp->lchnum = ARGS(lchno);
		cp->lmachno = snmachp->lmachno;
		spl0();
		cp->dlchnum = ARGS(dlchno);
		cp->machno = snmachp->machno;
		cp->xchan = (cp->lmachno<<8)|cp->lchnum;
		cp->snunit = unit;
		cp->pdid = NOPDID;
		bp = &cp->input;
		i = cp-sn_chans;
		bp->buf = sn_buffers[i].inbuf;
		bp->len = MAXMSGLEN;
		bp->done = 0;
		bp = &cp->output;
		bp->buf = sn_buffers[i].outbuf;
		bp->len = bp->done = 0;
		cp->flags = 0;
		cp->link = sninfop->snlink[mdev];
		sninfop->snlink[mdev] = cp;

		ARGS(lmachno) = snmachp->lmachno;
		ARGS(dmachno) = snmachp->machno;
#ifndef BSD42
		if (copyout((caddr_t)&args, addr, sizeof(args))) {
			RETURN ( EFAULT );
		}
#endif
		return(0);
	    }

	case NIOPOPEN: {
#ifdef BSD42
 		struct voargs *args = (struct voargs *)addr;
#else
		struct voargs args;

		if (copyin(addr, (caddr_t)&args, sizeof(args))) {
			RETURN ( EFAULT );
		}
#endif
		spl6();
		for(cp=sn_chans; cp<&sn_chans[NUMLCH]; cp++)
			if( cp->lchnum == -1 && cp->dlchnum == -1 )
				break;
		if( cp >= &sn_chans[NUMLCH] ) {
			spl0();
			RETURN(  ENFILE );
		}
		for(dp=sn_chans; dp<&sn_chans[NUMLCH]; dp++)
			if( dp->lchnum != -1 && dp->lmachno == ARGS(lmachno)
			   && dp->lchnum == ARGS(lchno)
			   && dp->snunit == unit ) {
				spl0();
				RETURN(  EEXIST );
			}
		cp->lchnum = ARGS(lchno);
		cp->lmachno = ARGS(lmachno);
		spl0();
		cp->dlchnum = ARGS(dlchno);
		cp->machno = ARGS(dmachno);
		cp->xchan = (cp->lmachno<<8)|cp->lchnum;
		cp->snunit = unit;
		cp->pdid = ARGS(pdid);
		bp = &cp->input;
		i = cp-sn_chans;
		bp->buf = sn_buffers[i].inbuf;
		bp->len = MAXMSGLEN;
		bp->done = 0;
		bp = &cp->output;
		bp->buf = sn_buffers[i].outbuf;
		bp->len = bp->done = 0;
		cp->flags = 0;
		cp->link = sninfop->snlink[mdev];
		sninfop->snlink[mdev] = cp;
		return(0);
	    }

	case NIOCLOSE: {
#ifdef BSD42
 		struct cargs *args = (struct cargs *)addr;
#else
		struct cargs args;

		if (copyin(addr, (caddr_t)&args, sizeof(args))) {
			RETURN ( EFAULT );
		}
#endif
		for(cp=sninfop->snlink[mdev], dp=0; cp; dp=cp, cp=cp->link)
			if( cp->lmachno == ARGS(lmachno)
			    && cp->lchnum == ARGS(lchno) ) {
				/* TIMING !!! */
				if( dp )
					dp->link = cp->link;
				else
					sninfop->snlink[mdev] = cp->link;
				chclose(cp);
				return(0);
			}
		RETURN( EBADF );
		}

	case NIOGET:
	case NIOPUT:
	{
#ifdef BSD42
 		struct gpargs *args = (struct gpargs *)addr;
#else
		struct gpargs args;

		if (copyin(addr, (caddr_t)&args, sizeof(args))) {
			RETURN ( EFAULT );
		}
#endif
		status.lchdir = (cmd == NIOGET ? INPUT : OUTPUT);
		status.lchnum = ARGS(lch);
		ustatus = ARGS(status);
		usaddr = ARGS(addr);
		ulen = ARGS(len);
		for(cp=sninfop->snlink[mdev]; cp; cp=cp->link) {
			if( cp->lchnum == ARGS(lch)
			    && cp->lmachno == ARGS(lmachno)) {
do_getput:
				bp = (cmd == NIOGET ? &cp->input : &cp->output);
				if( cmd == NIOPUT ) {
					copyin(usaddr, bp->buf, ulen);
					spl5();
					bp->len = (ulen+1)/2;
					snxstart(unit, cp, DATA);
					sninfop->snsdata++;
				} else {
					spl5();
					/*
					 * The following may be
					 * unnecessary in light of
					 * the later check.
					 */
					if( bp->done == 0 && cp->flags&SENTRNR ) {
						cp->flags &= ~SENTRNR;
						cp->flags |= SENTRDY;
						snxstart(unit, cp, RDY);
						sninfop->sns1rdy++;
					}
				}
				for (;;) {
					spl5();
					if (bp->done) break;
					sleep((caddr_t)bp->buf, SNPRI);
#ifdef TIMEOUT
					spl6();
					if (cp->flags & RETRY) {
						cp->flags &= ~RETRY;
						sninfop->sntimeout++;
						snxstart(unit, cp, (cp->flags&SENTDATA) ? DATA : RDY);
					}
#endif TIMEOUT
				}
				status.code = ST_OK;
				status.len = 2*bp->len;
				if( cmd == NIOGET ) {
					spl0();
					copyout(bp->buf, usaddr, 2*bp->len);
					spl5();
					bp->len = MAXMSGLEN;
					/*
					 * Believe it or not, we must check
					 * this here.  This is because
					 * we lowered the ipl to do the
					 * copyout().  During that time
					 * another message may have tried
					 * to sneak in.
					 */
					if( cp->flags&SENTRNR ) {
						cp->flags &= ~SENTRNR;
						snxstart(unit, cp, RDY);
						sninfop->snsrdy++;
					}
				} else
					bp->len = 0;
				bp->done = 0;
				spl0();
				error = 0;
				goto cpyout;
			}
		}
		status.code = ST_IARGS;
		error = EIO;
cpyout:
		copyout((caddr_t)&status, (caddr_t)ustatus, sizeof(status));
		RETURN ( error );
	}

	case NIOGETM:
	{
		struct pair pair;
		struct chdat *lchns[MAXMUX+1];
		register struct chdat **chp = lchns, **chpe = lchns;
#ifdef BSD42
 		struct gmargs *args = (struct gmargs *)addr;
#else
		struct gmargs args;

		if (copyin(addr, (caddr_t)&args, sizeof(args))) {
			RETURN ( EFAULT );
		}
#endif
		status.lchdir = INPUT;
		ustatus = ARGS(status);
		usaddr = ARGS(addr);
		ulen = ARGS(len);
		for(i=0; i<MAXMUX; i++) {
			if (copyin((caddr_t)ARGS(pairp++), (caddr_t)&pair,
					sizeof(struct pair))) {
				RETURN ( EFAULT );
			}
			if( pair.lchno == -1 )
				break;
			if (pair.lchno < 0 || pair.lchno >= 512
			    || pair.lmachno < 0 || pair.lmachno >= 512) {
				RETURN ( EBADF );
			}
			for(cp=sninfop->snlink[mdev]; cp; cp=cp->link) {
				if( cp->lchnum == pair.lchno
				    && cp->lmachno == pair.lmachno ) {
					*chpe++ = cp;
					break;
				}
			}
			if( cp >= &sn_chans[NUMLCH] ) {
				RETURN ( EBADF );
			}
		}
		spl5();
		for(chp=lchns; chp<chpe; chp++) {
			if( (*chp)->flags&MULTIPLEX ) {
				spl0();
				RETURN ( EBUSY );
			}
		}
		for(chp=lchns; chp<chpe; chp++)
			(*chp)->flags |= MULTIPLEX;

loop:
		spl5();
		for(chp=lchns; chp<chpe; chp++) {
			if( (*chp)->input.done ) {
				goto gotone;
			}
		}
		sleep((caddr_t)&sn_multiplex, SNPRI);
		goto loop;

gotone:
		cp = *chp;
		for(chp=lchns; chp<chpe; chp++)
			(*chp)->flags &= ~MULTIPLEX;
		spl0();
		status.lchnum = cp->lchnum;
		cmd = NIOGET;
		goto do_getput;
	}

	case NIOABORT:
	case NIORESET: {
		struct device *draddr;
#ifndef USG5
		struct uba_device *ui;
#endif
#ifdef BSD42
 		struct raargs *args = (struct raargs *)addr;
#else
		struct raargs args;

		if (copyin(addr, (caddr_t)&args, sizeof(args))) {
			RETURN ( EFAULT );
		}
#endif

#ifdef USG5
		draddr = sn_addr[unit];
#else
		ui = sndinfo[unit];
		draddr = (struct device *)ui->ui_addr;
#endif
		
		spl5();
		i = ARGS(machno) << 8;
		draddr->drcs = CMD_MODE;
		draddr->drout = (cmd == NIOABORT ? ABORT(i) : RESET(i));
		draddr->drcs = EOP_MODE;
		draddr->drout = 0;
		draddr->drcs = DATA_MODE;
		spl0();
		return(0);
	}

	case NIOREADSTATUS:
#ifdef BSD42
		copyout((caddr_t)sninfop, *(caddr_t *)addr, 
			sizeof sn_sninfo[0]);
#else
		copyout((caddr_t)sninfop, (caddr_t)addr, 
			sizeof sn_sninfo[0]);
#endif
		return(0);

	case NIOCHSTATUS: {

		struct chinfo chinfo;

		chinfo.numlch = NUMLCH;
		chinfo.chanaddr = &sn_chans[0];
#ifdef BSD42
		copyout((caddr_t)&chinfo, *(caddr_t *)addr, 
			sizeof chinfo);
		copyout((caddr_t)chinfo.chanaddr,
			(*(caddr_t *)addr)+sizeof chinfo,
			NUMLCH*sizeof sn_chans[0]);
#else
		copyout((caddr_t)&chinfo, (caddr_t)addr, 
			sizeof chinfo);
		copyout((caddr_t)chinfo.chanaddr, 
			(caddr_t)(addr+sizeof chinfo), 
			NUMLCH*sizeof sn_chans[0]);
#endif
		return(0);
	}

	case NIOSETMACH:
#ifdef BSD42
		copyin(*(caddr_t *)addr, (caddr_t)snmach, sizeof snmach);
#else
		copyin((caddr_t)addr, (caddr_t)snmach, sizeof snmach);
#endif
		return(0);


	case NIOGETMACH:
#ifdef BSD42
		copyout((caddr_t)snmach, *(caddr_t *)addr, sizeof snmach);
#else
		copyout((caddr_t)snmach, (caddr_t)addr, sizeof snmach);
#endif
		return(0);


	case NIOQSTATUS: {
		struct openinfo openinfo;

		openinfo.owaddr = sn_openwait;
		openinfo.head = sn_owhead;
		openinfo.tail = sn_owtail;

#ifdef BSD42
		copyout((caddr_t)&openinfo, *(caddr_t *)addr, sizeof openinfo);
		copyout((caddr_t)sn_openwait, 
			(*(caddr_t *)addr)+sizeof openinfo,
			NUMDEV*sizeof sn_openwait[0]);
#else
		copyout((caddr_t)&openinfo, (caddr_t)addr, sizeof openinfo);
		copyout((caddr_t)sn_openwait, (caddr_t)(addr+sizeof openinfo),
			NUMDEV*sizeof sn_openwait[0]);
#endif
		return(0);
	}

	case NIOZEROSTAT:
	{

		register char *p, *low, *high;

		low = (char *) &sn_sninfo[0];
#ifdef USG5
		high = low + sn_cnt*sizeof (struct sninfo);
#else
		high = low + NSN*sizeof (struct sninfo);
#endif

		for (p=low; p<high; p++)
			*p = '\0';
		return(0);
	}
	case NIOCHECK:
	case NIOWAIT:
	case NIOPURGE:
	case NIOSETVEC:
		;
	}
}

static
chclose(cp)
register struct chdat *cp;
{
	cp->lchnum = cp->dlchnum = cp->machno = -1;
	cp->flags = cp->xchan = cp->input.len = 0;
}

#ifdef TIMEOUT
cktimchk()
{
	register struct chdat *cp;
	register struct sninfo *sninfop;
	int unit;
	int s;

	for(cp=sn_chans; cp < &sn_chans[NUMLCH]; cp++) {
		if( cp->flags&(SENTDATA|SENTRDY) && --cp->ckticks == 0 ) {
			unit = cp->snunit;
			sninfop = &sn_sninfo[unit];
			sninfop->snsched++;
			sninfop->snenqlost[cp->machno]++;
			cp->flags |= RETRY;
			if (cp->flags & SENTDATA) {
				wakeup((caddr_t)cp->output.buf);
			} else {
				wakeup((caddr_t)cp->input.buf);
			}
		}
	}
	s = spl6();
	if (sn_state & OPEN) {
		timeout(cktimchk, (caddr_t)0, CKTICKS);
	} else {
		sn_state &= ~TIMER;
	}
	splx(s);
}
#endif TIMEOUT

#endif