~kris/9p

9hist

a49cf8bda50e1d0720798bcaf0606fb2141c4e05 — David du Colombier 27 years ago 551d878
Plan 9 from Bell Labs 1999-01-22
M mpc/clock.c => mpc/clock.c +2 -2
@@ 106,8 106,8 @@ clockintr(Ureg *ur)
	putdec(clkreload-v);

	m->ticks++;
	if(m->ticks%MS2TK(1000) == 0)
		m->bcsr[4] ^= DisableLamp;
//	if(m->ticks%MS2TK(1000) == 0)
//		m->bcsr[4] ^= DisableLamp;

	if(up)
		up->pc = ur->pc;

M mpc/cpm.c => mpc/cpm.c +6 -6
@@ 45,7 45,7 @@ cpminit(void)
	io = m->iomem;
	io->rccr = 0;
	io->rmds = 0;
	io->lccr &= ~1;	/* disable LCD */
//	io->lccr &= ~1;	/* disable LCD */
	io->sdcr = 1;
	io->pcint = 0;	/* disable all port C interrupts */
	io->pcso = 0;


@@ 145,7 145,7 @@ scc2claim(int force, ulong enable, void (*stop)(void*), void *arg)

	clash = scc2owner.clash & ~enable;
	ilock(&scc2owner);
	if(~m->bcsr[1] & clash){
	if(/* ~m->bcsr[1] & clash */ 0){
		if(!force){
			iunlock(&scc2owner);
			return 0;


@@ 153,12 153,12 @@ scc2claim(int force, ulong enable, void (*stop)(void*), void *arg)
		/* notify current owner */
		if(scc2owner.stop)
			scc2owner.stop(scc2owner.arg);
		m->bcsr[1] |= clash;
//		m->bcsr[1] |= clash;
	}
	scc2owner.stop = stop;
	scc2owner.arg = arg;
	scc2owner.active = enable;
	m->bcsr[1] &= ~enable;	/* the bits are active low */
//	m->bcsr[1] &= ~enable;	/* the bits are active low */
	iunlock(&scc2owner);
	/* beware: someone else can claim it during the `successful' return */
	return 1;


@@ 178,7 178,7 @@ scc2stop(void *a)
		cpmop(CloseRxBD, SCC2ID, 0);
		delay(1);
		scc->gsmrl &= ~(3<<4);	/* disable current use */
		m->bcsr[1] |= scc2owner.clash;
//		m->bcsr[1] |= scc2owner.clash;
	}
}



@@ 189,7 189,7 @@ void
scc2free(void)
{
	ilock(&scc2owner);
	m->bcsr[1] |= scc2owner.active;
//	m->bcsr[1] |= scc2owner.active;
	scc2owner.active = 0;
	iunlock(&scc2owner);
}

M mpc/dat.h => mpc/dat.h +1 -1
@@ 126,7 126,7 @@ struct Mach
	long	clockgen;	/* clock generator frequency (cycles) */
	int	cputype;
	ulong	delayloop;
	ulong*	bcsr;
//	ulong*	bcsr;
	IMM*	iomem;	/* MPC8xx internal i/o control memory */

	ulong	fairness;		/* for runproc */

M mpc/devrtc.c => mpc/devrtc.c +2 -2
@@ 97,7 97,7 @@ static long
rtcread(Chan *c, void *buf, long n, vlong offset)
{
	ulong t;
	char *b;
//	char *b;

	if(c->qid.path & CHDIR)
		return devdirread(c, buf, n, rtcdir, NRTC, devgen);


@@ 108,7 108,7 @@ rtcread(Chan *c, void *buf, long n, vlong offset)
		n = readnum(offset, buf, n, t, 12);
		return n;
	case Qswitch:
		return readnum(offset, buf, n, (m->bcsr[2]>>19)&0xF, 12);
		return readnum(offset, buf, n, 0xf/*(m->bcsr[2]>>19)&0xF*/, 12);
	}
	error(Egreg);
	return 0;		/* not reached */

A mpc/devuart.c => mpc/devuart.c +1438 -0
@@ 0,0 1,1438 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"../port/error.h"

#include	"../port/netif.h"

enum {
	FADS823 = 1,

	Rbufsize=	512,
	Nuart=	3,	/* max per machine (SMC1, SMC2, SCC2) */
	CTLS=	's'&037,
	CTLQ=	'q'&037,
};

enum {
	/* status bits in SCC receive buffer descriptors */
	RxBRK=	1<<7,	/* break ended frame (async hdlc) */
	RxDE=	1<<7,	/* DPLL error (hdlc) */
	RxBOF=	1<<6,	/* BOF ended frame (async hdlc) */
	RxLG=	1<<5,	/* frame too large (hdlc) */
	RxNO=	1<<4,	/* bad bit alignment (hdlc) */
	RxBR=	1<<5,	/* break received during frame (uart) */
	RxFR=	1<<4,	/* framing error (uart) */
	RxPR=	1<<3,	/* parity error (uart) */
	RxAB=	1<<3,	/* frame aborted (hdlc, async hdlc) */
	RxCR=	1<<2,	/* bad CRC (hdlc, async hdlc) */
	RxOV=	1<<1,	/* receiver overrun (all) */
	RxCD=	1<<0,	/* CD lost (all) */

	/* hdlc-specific Rx/Tx BDs */
	TxTC=	1<<10,
};

/*
 *  SMC in UART mode
 */
enum {
	USESMC2 = 0
};

typedef struct Uartsmc Uartsmc;
struct Uartsmc {
	IOCparam;
	ushort	maxidl;
	ushort	idlc;
	ushort	brkln;
	ushort	brkec;
	ushort	brkcr;
	ushort	rmask;
};

/*
 * SCC2 UART parameters
 */
enum {
	USESCC2 = 0,

	/* special mode bits */
	SccAHDLC = 1<<0,
	SccHDLC = 1<<1,
	SccIR = 1<<2,
	SccPPP = 1<<3,
};

typedef struct Uartscc Uartscc;
struct Uartscc {
	SCCparam;
	uchar	rsvd[8];
	ushort	max_idl;
	ushort	idlc;
	ushort	brkcr;
	ushort	parec;
	ushort	frmec;
	ushort	nosec;
	ushort	brkec;
	ushort	brkln;
	ushort	uaddr1;
	ushort	uaddr2;
	ushort	rtemp;
	ushort	toseq;
	ushort	character[8];
	ushort	rccm;
	ushort	rccrp;
	ushort	rlbc;
};

typedef struct UartAHDLC UartAHDLC;
struct UartAHDLC {
	SCCparam;
	ulong	rsvd1;
	ulong	c_mask;
	ulong	c_pres;
	ushort	bof;
	ushort	eof;
	ushort	esc;
	ushort	rsvd2[2];
	ushort	zero;
	ushort	rsvd3;
	ushort	rfthr;
	ushort	resvd4[2];
	ulong	txctl_tbl;
	ulong	rxctl_tbl;
	ushort	nof;
	ushort	rsvd5;
};

typedef struct UartHDLC UartHDLC;
struct UartHDLC {
	SCCparam;
	ulong	rsvd1;
	ulong	c_mask;
	ulong	c_pres;
	ushort	disfc;
	ushort	crcec;
	ushort	abtsc;
	ushort	nmarc;
	ushort	retrc;
	ushort	mflr;
	ushort	max_cnt;
	ushort	rfthr;
	ushort	rfcnt;
	ushort	hmask;
	ushort	haddr[4];
	ushort	tmp;
	ushort	tmp_mb;
};

enum {
	/* SCC events of possible interest here eventually */
	AB=	1<<9,	/* autobaud detected */
	GRA= 1<<7,	/* graceful stop completed */
	CCR= 1<<3,	/* control character detected */

	/* SCC, SMC common interrupt events */
	BSY=	1<<2,	/* receive buffer was busy (overrun) */
	TXB= 1<<1,	/* block sent */
	RXB= 1<<0,	/* block received */

	/* SCC events */
	TXE = 1<<4,	/* transmission error */
	RXF = 1<<3,	/* final block received */

	/* gsmr_l */
	ENR = 2<<4,	/* enable receiver */
	ENT = 1<<4,	/* enable transmitter */
};

typedef struct Uart Uart;
struct Uart
{
	QLock;

	Uart	*elist;		/* next enabled interface */
	char	name[NAMELEN];

	int	x;		/* index: x in SMCx or SCCx */
	int	cpmid;		/* eg, SCC1ID, SMC1ID */
	int	opens;
	uchar	bpc;	/* bits/char */
	uchar	parity;
	uchar	stopb;
	uchar	setup;
	uchar	enabled;
	int	dev;

	ulong	frame;		/* framing errors */
	ulong	perror;
	ulong	overrun;	/* rcvr overruns */
	ulong	crcerr;
	ulong	interrupts;
	int	baud;		/* baud rate */

	/* flow control */
	int	xonoff;		/* software flow control on */
	int	blocked;
	int	modem;		/* hardware flow control on */
	int	cts;		/* ... cts state */
	int	rts;		/* ... rts state */
	Rendez	r;

	/* buffers */
	int	(*putc)(Queue*, int);
	Queue	*iq;
	Queue	*oq;

	/* staging areas to avoid some of the per character costs */
	uchar	istage[Rbufsize][2];	/* double buffered */
	int	rdrx;	/* last buffer read */

	Lock	plock;		/* for output variables */
	Block*	outb;	/* currently transmitting Block */

	BD*	rxb;
	BD*	txb;

	SMC*	smc;
	SCC*	scc;
	IOCparam*	param;
	ushort*	brkcr;	/* brkcr location in appropriate block */
	int	brgc;
	int	mode;
	Block*	partial;
	int	loopback;
};

static Uart *uart[Nuart];
static int nuart;

struct Uartalloc {
	Lock;
	Uart *elist;	/* list of enabled interfaces */
} uartalloc;

static void uartintr(Uart*, int);
static void smcuintr(Ureg*, void*);
static void sccuintr(Ureg*, void*);

static	int	sccirq[] = {-1, 0x1E, 0x1D, 0x1C, 0x1B};
static	int	sccid[] = {-1, SCC1ID, SCC2ID, SCC3ID, SCC4ID};
static	int	sccbase[] = {-1, 0xA00, 0xA20, 0xA40, 0xA60};
static	int	sccparam[] = {-1, SCC1P, SCC2P, SCC3P, SCC4P};

static void
uartsetbuf(Uart *up)
{
	IOCparam *p;
	BD *bd;

	p = up->param;
	p->rfcr = 0x18;
	p->tfcr = 0x18;
	p->mrblr = Rbufsize;

	if((bd = up->rxb) == nil){
		bd = bdalloc(2);
		up->rxb = bd;
	}
	p->rbase = (ushort)bd;
	bd->status = BDEmpty|BDInt;
	bd->length = 0;
	bd->addr = PADDR(up->istage[0]);
	bd++;
	bd->status = BDEmpty|BDInt|BDWrap;
	bd->length = 0;
	bd->addr = PADDR(up->istage[1]);
	dcflush(up->istage, sizeof(up->istage));
	up->rdrx = 0;

	if((bd = up->txb) == nil){
		bd = bdalloc(1);
		up->txb = bd;
	}
	p->tbase = (ushort)bd;
	bd->status = BDWrap|BDInt;
	bd->length = 0;
	bd->addr = 0;
}

static void
smcsetup(Uart *up)
{
	IMM *io;
	Uartsmc *p;
	SMC *smc;

	up->brgc = brgalloc();
	if(up->brgc < 0)
		error(Eio);
//	m->bcsr[1] &= ~DisableRS232a;	/* enable rs232-1 */

	io = ioplock();
	io->pbpar |= IBIT(23)|IBIT(24)|IBIT(25);	/* enable SMC1 TX/RX/SYN */
	io->pbdir &= ~(IBIT(23)|IBIT(24)|IBIT(25));
	io->brgc[up->brgc] = baudgen(up->baud, 16) | BaudEnable;
	io->simode = (io->simode & ~0xF000) | (up->brgc<<12);	/* SMC1 to NMSI mode, set Tx/Rx clock */
	iopunlock();

	up->param = KADDR(SMC1P);
	uartsetbuf(up);

	cpmop(InitRxTx, SMC1ID, 0);

	/* SMC protocol parameters */
	p = (Uartsmc*)up->param;
	up->brkcr = &p->brkcr;
	p->maxidl = 1;
	p->brkln = 0;
	p->brkec = 0;
	p->brkcr = 1;
	smc = IOREGS(0xA80, SMC);
	smc->smce = 0xff;	/* clear events */
	smc->smcm = BSY|RXB|TXB;	/* enable all possible interrupts */
	up->smc = smc;
	smc->smcmr = ((1+8+1-1)<<11)|(2<<4);	/* 8-bit, 1 stop, no parity; UART mode */
//	intrenable(VectorCPIC+4, smcuintr, up, BUSUNKNOWN);
	smc->smcmr |= 3;	/* enable rx/tx */
}

static void
smcuintr(Ureg*, void *a)
{
	Uart *up;
	int events;

	up = a;
	events = up->smc->smce;
	eieio();
	up->smc->smce = events;
	uartintr(up, events&(BSY|RXB|TXB));
}

static void
sccustop(void *a)
{
	Uart *up;

	up = a;
	if(up->setup){
		scc2stop(up->scc);
		brgfree(up->brgc);
		up->brgc = -1;
		up->setup = 0;
	}
}
		
static void
sccsetup(Uart *up)
{
	IMM *io;
	SCC *scc;
	ulong bg;
	int i;

	scc = IOREGS(sccbase[up->x], SCC);
	if(up->x == 2){	/* BUG: 823FADS-specific */
		if(scc->gsmrl & (ENT|ENR))
			scc2stop(scc);
		scc->gsmrl &= ~(ENT|ENR);
		scc2claim(1, up->mode&SccIR? DisableIR: DisableRS232b, sccustop, scc);
	}
	up->scc = scc;
	if(up->brgc < 0){
		up->brgc = brgalloc();
		if(up->brgc < 0)
			error(Eio);
	}
	bg = baudgen(up->baud, 16) | BaudEnable;

	io = ioplock();
	io->papar |= SIBIT(12)|SIBIT(13);	/* enable TXD2 and RXD2 pins */
	io->padir &= ~(SIBIT(12)|SIBIT(13));
	if((up->mode & SccIR) == 0)
		io->paodr |= SIBIT(12);	/* TO DO: is this right? */
	else
		io->paodr &= ~(SIBIT(12)|SIBIT(13));	/* TO DO: is this right? */

	io->pbpar &= ~IBIT(18);	/* disable RTS2 */
	io->pbdir &= ~IBIT(18);
	if((up->mode&SccIR) == 0)
		io->pcpar |= SIBIT(14);	/* RTS2~ */
	else
		io->pcpar &= ~SIBIT(14);
	io->pcpar &= ~(SIBIT(9)|SIBIT(8));	/* CTS2~ and CD2~ */
	io->pcdir &= ~(SIBIT(14)|SIBIT(9)|SIBIT(8));
	if(FADS823 || up->mode)
		io->pcso &= ~(SIBIT(9)|SIBIT(8));	/* 82xFADS: force CTS and CD on */
	else
		io->pcso |= SIBIT(9)|SIBIT(8);
	eieio();

	io->brgc[up->brgc] = bg;
	iopunlock();

	sccnmsi(2, up->brgc, up->brgc);

	up->param = KADDR(sccparam[up->x]);
	uartsetbuf(up);

	cpmop(InitRxTx, up->cpmid, 0);

	/* SCC protocol parameters */
	if((up->mode & (SccAHDLC|SccHDLC)) == 0){
		Uartscc *sp;
		sp = (Uartscc*)up->param;
		sp->max_idl = 1;
		sp->brkcr = 1;
		sp->parec = 0;
		sp->frmec = 0;
		sp->nosec = 0;
		sp->brkec = 0;
		sp->brkln = 0;
		sp->brkec = 0;
		sp->uaddr1 = 0;
		sp->uaddr2 = 0;
		sp->toseq = 0;
		for(i=0; i<8; i++)
			sp->character[i] = 0x8000;
		sp->rccm = 0xC0FF;
		up->brkcr = &sp->brkcr;
		scc->irmode = 0;
		scc->dsr = ~0;
		scc->gsmrh = 1<<5;	/* 8-bit oriented receive fifo */
		scc->gsmrl = 0x28004;	/* UART mode */
	}else{
		UartAHDLC *hp;
		hp = (UartAHDLC*)up->param;
		hp->c_mask = 0x0000F0B8;
		hp->c_pres = 0x0000FFFF;
		if(up->mode & SccIR){
			hp->bof = 0xC0;
			hp->eof = 0xC1;
			//scc->dsr = 0xC0C0;
			scc->dsr = 0x7E7E;
		}else{
			hp->bof = 0x7E;
			hp->eof = 0x7E;
			scc->dsr = 0x7E7E;
		}
		hp->esc = 0x7D;
		hp->zero = 0;
		if(up->mode & SccHDLC)
			hp->rfthr = 1;
		else
			hp->rfthr = 0;	/* receive threshold of 1 doesn't work properly for Async HDLC */
		hp->txctl_tbl = 0;
		hp->rxctl_tbl = 0;
		if(up->mode & SccIR){
			/* low-speed infrared */
			hp->nof = 12-1;	/* 12 flags */
			scc->irsip = 0;
			scc->irmode = (2<<8) | 1;
//			m->bcsr[1] |= DisableIR;
//			microdelay(2);
//			m->bcsr[1] &= ~DisableIR;	/* put TFDS6000 xcvr in low-speed mode (see 4.9.2.1 in FADS manual) */
			if(up->loopback)
				scc->irmode = (3<<4)|1;	/* loopback */
		}else{
			scc->irmode = 0;
			hp->txctl_tbl = ~0;
			hp->rxctl_tbl = ~0;
			hp->nof = 1-1;	/* one opening flag */
		}
		up->brkcr = nil;
		scc->gsmrh = 1<<5;	/* 8-bit oriented receive fifo */
		if(up->mode & SccHDLC)
			scc->gsmrl = 0x28000;	/* HDLC */
		else
			scc->gsmrl = 0x28006;	/* async HDLC/IrDA */
	}
	scc->scce = ~0;	/* clear events */
	scc->sccm = TXE|BSY|RXF|TXB|RXB;	/* enable all interesting interrupts */
	intrenable(VectorCPIC+sccirq[up->x], sccuintr, up, BUSUNKNOWN);
	scc->psmr = 3<<12;	/* 8-bit, 1 stop, no parity; UART mode */
	if(up->loopback && (up->mode & SccIR) == 0)
		scc->gsmrl |= 1<<6;	/* internal loop back */
	scc->gsmrl |= ENT|ENR;	/* enable rx/tx */
	if(0){
		print("gsmrl=%8.8lux gsmrh=%8.8lux dsr=%4.4ux irmode=%4.4ux\n", scc->gsmrl, scc->gsmrh, scc->dsr, scc->irmode);
		for(i=0; i<sizeof(Uartscc); i+=4)
			print("%2.2lux %8.8lux\n", i, *(ulong*)((uchar*)up->param+i));
	}
}

static void
sccuintr(Ureg*, void *a)
{
	Uart *up;
	int events;

	up = a;
	if(/*(m->bcsr[1] & (DisableRS232b|DisableIR)) == (DisableRS232b|DisableIR) ||*/ up->scc == nil)
		return;
	events = up->scc->scce;
	eieio();
	up->scc->scce = events;
	if(up->enabled){
		if(0)
			print("#%ux|", events);
		uartintr(up, events);
	}
}

static void
uartsetbaud(Uart *p, int rate)
{
	if(rate <= 0 || p->brgc < 0)
		return;
	p->baud = rate;
	m->iomem->brgc[p->brgc] = baudgen(rate, 16) | BaudEnable;
}

static void
uartsetmode(Uart *p)
{
	int r, clen;

	ilock(&p->plock);
	clen = p->bpc;
	if(p->parity == 'e' || p->parity == 'o')
		clen++;
	clen++;	/* stop bit */
	if(p->stopb == 2)
		clen++;
	if(p->smc){
		r = p->smc->smcmr & 0x3F;	/* keep mode, enable bits */
		r |= (clen<<11);
		if(p->parity == 'e')
			r |= 3<<8;
		else if(p->parity == 'o')
			r |= 2<<8;
		if(p->stopb == 2)
			r |= 1<<10;
		eieio();
		p->smc->smcmr = r;
	}else if(p->scc && p->mode == 0){
		r = p->scc->psmr & 0x8FE0;	/* keep mode bits */
		r |= ((p->bpc-5)&3)<<12;
		if(p->parity == 'e')
			r |= (6<<2)|2;
		else if(p->parity == 'o')
			r |= (4<<2)|0;
		if(p->stopb == 2)
			r |= 1<<14;
		eieio();
		p->scc->psmr = r;
	}
	iunlock(&p->plock);
}

static void
uartparity(Uart *p, char type)
{
	ilock(&p->plock);
	p->parity = type;
	iunlock(&p->plock);
	uartsetmode(p);
}

/*
 *  set bits/character
 */
static void
uartbits(Uart *p, int bits)
{
	if(bits < 5 || bits > 14 || bits > 8 && p->scc)
		error(Ebadarg);

	ilock(&p->plock);
	p->bpc = bits;
	iunlock(&p->plock);
	uartsetmode(p);
}


/*
 *  toggle DTR
 */
static void
uartdtr(Uart *p, int n)
{
	if(p->scc == nil)
		return;	/* not possible */
	USED(n);	/* not possible on FADS */
}

/*
 *  toggle RTS
 */
static void
uartrts(Uart *p, int n)
{
	p->rts = n;
	if(p->scc == nil)
		return;	/* not possible */
	USED(n);	/* not possible on FADS */
}

/*
 *  send break
 */
static void
uartbreak(Uart *p, int ms)
{
	if(p->brkcr == nil)
		return;

	if(ms <= 0)
		ms = 200;

	if(waserror()){
		ilock(&p->plock);
		*p->brkcr = 1;
		cpmop(RestartTx, p->cpmid, 0);
		iunlock(&p->plock);
		nexterror();
	}
	ilock(&p->plock);
	*p->brkcr = ((p->baud/(p->bpc+2))*ms+500)/1000;
	cpmop(StopTx, p->cpmid, 0);
	iunlock(&p->plock);

	tsleep(&up->sleep, return0, 0, ms);

	poperror();
	ilock(&p->plock);
	*p->brkcr = 1;
	cpmop(RestartTx, p->cpmid, 0);
	iunlock(&p->plock);
}

/*
 *  modem flow control on/off (rts/cts)
 */
static void
uartmflow(Uart *p, int n)
{
	if(p->scc == nil)
		return;	/* not possible */
	if(n){
		p->modem = 1;
		/* enable status interrupts ... */
		p->scc->psmr |= 1<<15;	/* enable async flow control */
		p->cts = 1;
		/* could change maxidl */
	}else{
		p->modem = 0;
		/* stop status interrupts ... */
		p->scc->psmr &= ~(1<<15);
		p->cts = 1;
	}
}

/*
 *  turn on a port's interrupts.  set DTR and RTS
 */
void
uartenable(Uart *p)
{
	Uart **l;

	if(p->enabled)
		return;

	if(p->setup == 0){
		if(p->cpmid == SMC1ID || p->cpmid == SMC2ID)
			smcsetup(p);
		else
			sccsetup(p);
		p->setup = 1;
	}

	/*
 	 *  turn on interrupts
	 */
	if(p->smc){
		cpmop(RestartTx, p->cpmid, 0);
		p->smc->smcmr |= 3;
		p->smc->smcm = BSY|TXB|RXB;
		eieio();
	}else if(p->scc){
		cpmop(RestartTx, p->cpmid, 0);
		p->scc->gsmrl |= ENT|ENR;
		p->scc->sccm = BSY|TXB|RXB;
		eieio();
	}

	/*
	 *  turn on DTR and RTS
	 */
	uartdtr(p, 1);
	uartrts(p, 1);

	/*
	 *  assume we can send
	 */
	p->cts = 1;
	p->blocked = 0;

	/*
	 *  set baud rate to the last used
	 */
	uartsetbaud(p, p->baud);

	lock(&uartalloc);
	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
		if(*l == p)
			break;
	}
	if(*l == 0){
		p->elist = uartalloc.elist;
		uartalloc.elist = p;
	}
	p->enabled = 1;
	unlock(&uartalloc);
	p->cts = 1;
	p->blocked = 0;
	p->xonoff = 0;
	p->enabled = 1;
}

/*
 *  turn off a port's interrupts.  reset DTR and RTS
 */
void
uartdisable(Uart *p)
{
	Uart **l;

	/*
 	 *  turn off interrpts
	 */
	if(p->smc){
		cpmop(StopTx, p->cpmid, 0);
		cpmop(CloseRxBD, p->cpmid, 0);
		delay(1);
		p->smc->smcmr &= ~3;
		eieio();
		p->smc->smcm = 0;
	}else if(p->scc){
		cpmop(GracefulStopTx, p->cpmid, 0);
		delay(1);
		cpmop(CloseRxBD, p->cpmid, 0);
		p->scc->gsmrl &= ~(ENT|ENR);
		eieio();
		p->scc->sccm = 0;
	}

	/*
	 *  revert to default settings
	 */
	p->bpc = 8;
	p->parity = 0;
	p->stopb = 0;

	/*
	 *  turn off DTR, RTS, hardware flow control & fifo's
	 */
	uartdtr(p, 0);
	uartrts(p, 0);
	uartmflow(p, 0);
	p->xonoff = p->blocked = 0;

	lock(&uartalloc);
	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
		if(*l == p){
			*l = p->elist;
			break;
		}
	}
	p->enabled = 0;
	unlock(&uartalloc);
}

/*
 *  set the next output buffer going
 */
static void
txstart(Uart *p)
{
	Block *b;
	int n, flags;

	if(!p->cts || p->blocked || p->txb->status & BDReady)
		return;
	if((b = p->outb) == nil){
		if((b = qget(p->oq)) == nil)
			return;
		if(p->mode & SccPPP &&
		   p->mode & SccAHDLC &&
		   BLEN(b) >= 8){	/* strip framing data */
			UartAHDLC *hp;
			hp = (UartAHDLC*)p->param;
			if(hp != nil && (p->mode & SccIR) == 0){
				hp->txctl_tbl = nhgetl(b->rp);
				hp->rxctl_tbl = nhgetl(b->rp+4);
			}
			b->rp += 8;
			if(0)
				print("tx #%lux rx #%lux\n", hp->txctl_tbl, hp->rxctl_tbl);
		}
	}
	n = BLEN(b);
	if(n <= 0)
		print("txstart: 0\n");
	if(p->bpc > 8){
		/* half-word alignment and length if chars are long */
		if(PADDR(b->rp)&1){	/* must be even if chars are long */
			memmove(b->base, b->rp, n);
			b->rp = b->base;
			b->wp = b->rp+n;
		}
		if(n & 1)
			n++;
	}
	dcflush(b->rp, n);
	p->outb = b;
	if(n > 0xFFFF)
		n = 0xFFFE;
	if(p->mode & SccHDLC)
		flags = BDLast | TxTC;
	else if(p->mode)
		flags = BDLast;
	else
		flags = 0;
	p->txb->addr = PADDR(b->rp);
	p->txb->length = n;
	eieio();
	p->txb->status = (p->txb->status & BDWrap) | flags | BDReady/*|BDInt */;
	eieio();

	while(p->txb->status & BDReady)
		flash();
}

/*
 *  (re)start output
 */
static void
uartkick(Uart *p)
{
	ilock(&p->plock);
	if(p->outb == nil)
		txstart(p);
	iunlock(&p->plock);
}

/*
 *  restart input if its off
 */
static void
uartflow(Uart *p)
{
	if(p->modem)
		uartrts(p, 1);
}

static void
uartsetup(int x, int cpmid, char *name)
{
	Uart *p;

	if(nuart >= Nuart)
		return;

	p = xalloc(sizeof(Uart));
	uart[nuart] = p;
	strcpy(p->name, name);
	p->dev = nuart;
	nuart++;
	p->x = x;
	p->cpmid = cpmid;
	p->brgc = -1;
	p->mode = 0;

	/*
	 *  set rate to 9600 baud.
	 *  8 bits/character.
	 *  1 stop bit.
	 *  interrupts enabled.
	 */
	p->bpc = 8;
	p->parity = 0;
	p->baud = 9600;

	p->iq = qopen(4*1024, 0, uartflow, p);
	p->oq = qopen(4*1024, 0, uartkick, p);
}

/*
 *  called by main() to configure a duart port as a console or a mouse
 */
void
uartspecial(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int))
{
	Uart *p = uart[port];

	uartenable(p);
	if(baud)
		uartsetbaud(p, baud);
	p->putc = putc;
	if(in)
		*in = p->iq;
	if(out)
		*out = p->oq;
	p->opens++;
}

static int
uartinput(Uart *p, BD *bd)
{
	int ch, dokick, i, l;
	uchar *bp;

	dokick = 0;
	if(bd->status & RxFR)
		p->frame++;
	if(bd->status & RxOV)
		p->overrun++;
	l = bd->length;
	if(bd->status & RxPR){
		p->perror++;
		l--;	/* it's the last character */
	}
	bp = KADDR(bd->addr);
	if(p->xonoff || p->putc){
		for(i=0; i<l; i++){
			ch = bp[i];
			if(p->xonoff){
				if(ch == CTLS){
					p->blocked = 1;
					cpmop(StopTx, p->cpmid, 0);
				}else if (ch == CTLQ){
					p->blocked = 0;
					dokick = 1;
				}
				/* BUG? should discard on/off char? */
			}
			if(p->putc)
				(*p->putc)(p->iq, ch);
		}
	}
	if(p->putc == nil && l > 0)
		qproduce(p->iq, bp, l);
	return dokick;
}

static void
framedinput(Uart *p, BD *bd)
{
	Block *pkt;
	int l;

	pkt = p->partial;
	p->partial = nil;
	if(bd->status & RxOV){
		p->overrun++;
		goto Discard;
	}
	if(bd->status & (RxAB|RxCR|RxCD|RxLG|RxNO|RxDE|RxBOF|RxBRK)){
		if(bd->status & RxCR)
			p->crcerr++;
		else
			p->frame++;
		goto Discard;
	}
	if(pkt == nil){
		pkt = iallocb(1500);	/* TO DO: allocate less if possible */
		if(pkt == nil)
			return;
	}
	l = bd->length;
	if(bd->status & BDLast)
		l -= BLEN(pkt);	/* last one gives size of entire frame */
	if(l > 0){
		if(pkt->wp+l > pkt->lim)
			goto Discard;
		memmove(pkt->wp, KADDR(bd->addr), l);
		pkt->wp += l;
	}
	if(0)
		print("#%ux|", bd->status);
	if(bd->status & BDLast){
		if(p->mode & (SccHDLC|SccAHDLC)){
			if(BLEN(pkt) <= 2){
				p->frame++;
				goto Discard;
			}
			pkt->wp -= 2;	/* strip CRC */
		}
		qpass(p->iq, pkt);
	}else
		p->partial = pkt;
	return;

Discard:
	if(pkt != nil)
		freeb(pkt);
}

/*
 *  handle an interrupt to a single uart
 */
static void
uartintr(Uart *p, int events)
{
	int dokick;
	BD *bd;
	Block *b;

	if(events & BSY)
		p->overrun++;
	p->interrupts++;
	dokick = 0;
	while(p->rxb != nil && ((bd = &p->rxb[p->rdrx])->status & BDEmpty) == 0){
		if(p->mode)
			framedinput(p, bd);
		else if(uartinput(p, bd))
			dokick = 1;
		dcflush(KADDR(bd->addr), bd->length);
		eieio();
		bd->status = (bd->status & BDWrap) | BDEmpty|BDInt;
		p->rdrx ^= 1;
	}
	if((bd = p->txb) != nil){
		if((bd->status & BDReady) == 0){
			ilock(&p->plock);
			if((b = p->outb) != nil){
				b->rp += bd->length;
				if(b->rp >= b->wp){
					p->outb = nil;
					freeb(b);
				}
			}
			txstart(p);
			iunlock(&p->plock);
		}
	}
	eieio();
	/* TO DO: modem status isn't available on 82xFADS */
	if(dokick && p->cts && !p->blocked){
		if(p->outb == nil){
			ilock(&p->plock);
			txstart(p);
			iunlock(&p->plock);
		}
		cpmop(RestartTx, p->cpmid, 0);
	} else if (events & TXE)
		cpmop(RestartTx, p->cpmid, 0);
}

/*
 * used to ensure uart console output when debugging
 */
void
uartwait(void)
{
	Uart *p = uart[0];
	int s;

	while(p && p->outb){
		s = splhi();
		if((p->txb->status & BDReady) == 0){
			p->blocked = 0;
			p->cts = 1;
			if(p->cpmid == SMC1ID || p->cpmid == SMC2ID)
				smcuintr(nil, p);
			else
				sccuintr(nil, p);
		}
		splx(s);
	}
}

static Dirtab *uartdir;
static int ndir;

static void
setlength(int i)
{
	Uart *p;

	if(i > 0){
		p = uart[i];
		if(p && p->opens && p->iq)
			uartdir[3*i].length = qlen(p->iq);
	} else for(i = 0; i < nuart; i++){
		p = uart[i];
		if(p && p->opens && p->iq)
			uartdir[3*i].length = qlen(p->iq);
	}
		
}

void
uartinstall(void)
{
	int port;
	char *p, *q;
	static int already;

	if(already)
		return;
	already = 1;
	uartsetup(1, SMC1ID, "eia0");
/*
	if((p = getconf("console")) || (p = "0")){
		if(USESMC2)
			port = strtol(p, &q, 0);
		else
			port = 0;
		if(p != q && (port == 0 || port == 1))
			uartspecial(port, 9600, &kbdq, &printq, kbdcr2nl);
	}
*/
	uartspecial(0, 19200, &kbdq, &printq, kbdcr2nl);

//	if(USESMC2)
//		uartsetup(2, SMC2ID, "eia1");
//	if(USESCC2)
//		uartsetup(2, SCC2ID, "eia2");
}

/*
 *  all uarts must be uartsetup() by this point or inside of uartinstall()
 */
static void
uartreset(void)
{
	int i;
	Dirtab *dp;

	uartinstall();	/* architecture specific */

	ndir = 4*nuart;
	uartdir = xalloc(ndir * sizeof(Dirtab));
	dp = uartdir;
	for(i = 0; i < nuart; i++){
		/* 3 directory entries per port */
		strcpy(dp->name, uart[i]->name);
		dp->qid.path = NETQID(i, Ndataqid);
		dp->perm = 0660;
		dp++;
		sprint(dp->name, "%sctl", uart[i]->name);
		dp->qid.path = NETQID(i, Nctlqid);
		dp->perm = 0660;
		dp++;
		sprint(dp->name, "%sstat", uart[i]->name);
		dp->qid.path = NETQID(i, Nstatqid);
		dp->perm = 0444;
		dp++;
		sprint(dp->name, "%smode", uart[i]->name);
		dp->qid.path = NETQID(i, Ntypeqid);
		dp->perm = 0660;
		dp++;
	}
}

static Chan*
uartattach(char *spec)
{
	return devattach('t', spec);
}

static int
uartwalk(Chan *c, char *name)
{
	return devwalk(c, name, uartdir, ndir, devgen);
}

static void
uartstat(Chan *c, char *dp)
{
	if(NETTYPE(c->qid.path) == Ndataqid)
		setlength(NETID(c->qid.path));
	devstat(c, dp, uartdir, ndir, devgen);
}

static Chan*
uartopen(Chan *c, int omode)
{
	Uart *p;

	c = devopen(c, omode, uartdir, ndir, devgen);

	switch(NETTYPE(c->qid.path)){
	case Nctlqid:
	case Ndataqid:
		p = uart[NETID(c->qid.path)];
		qlock(p);
		if(p->opens++ == 0){
			uartenable(p);
			qreopen(p->iq);
			qreopen(p->oq);
		}
		qunlock(p);
		break;
	}

	return c;
}

static void
uartclose(Chan *c)
{
	Uart *p;

	if(c->qid.path & CHDIR)
		return;
	if((c->flag & COPEN) == 0)
		return;
	switch(NETTYPE(c->qid.path)){
	case Ndataqid:
	case Nctlqid:
		p = uart[NETID(c->qid.path)];
		qlock(p);
		if(--(p->opens) == 0){
			uartdisable(p);
			qclose(p->iq);
			qclose(p->oq);
		}
		qunlock(p);
		break;
	}
}

static long
uartstatus(Chan*, Uart *p, void *buf, long n, long offset)
{
	IMM *io;
	char str[256];

	sprint(str, "opens %d ferr %lud oerr %lud crcerr %lud baud %lud perr %lud intr %lud", p->opens,
		p->frame, p->overrun, p->crcerr, p->baud, p->perror, p->interrupts);
	/* TO DO: cts, dsr, ring, dcd, dtr, rts aren't all available on 82xFADS */
	io = m->iomem;
	if(p->scc){
		if((io->pcdat & SIBIT(9)) == 0)
			strcat(str, " cts");
		if((io->pcdat & SIBIT(8)) == 0)
			strcat(str, " dcd");
		if((io->pbdat & IBIT(22)) == 0)
			strcat(str, " dtr");
	}else if(p->smc){
		if((io->pbdat & IBIT(23)) == 0)
			strcat(str, " dtr");
	}
	strcat(str, "\n");
	return readstr(offset, buf, n, str);
}

static long
uartread(Chan *c, void *buf, long n, vlong offset)
{
	Uart *p;

	if(c->qid.path & CHDIR){
		setlength(-1);
		return devdirread(c, buf, n, uartdir, ndir, devgen);
	}

	p = uart[NETID(c->qid.path)];
	switch(NETTYPE(c->qid.path)){
	case Ndataqid:
		return qread(p->iq, buf, n);
	case Nctlqid:
		return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
	case Nstatqid:
		return uartstatus(c, p, buf, n, offset);
	case Ntypeqid:
		return readnum(offset, buf, n, p->mode, NUMSIZE);
	}

	return 0;
}

static Block*
uartbread(Chan *c, long n, ulong offset)
{
	if(c->qid.path & CHDIR || NETTYPE(c->qid.path) != Ndataqid)
		return devbread(c, n, offset);
	return qbread(uart[NETID(c->qid.path)]->iq, n);
}

static void
uartctl(Uart *p, char *cmd)
{
	int i, n;

	/* let output drain for a while */
	for(i = 0; i < 16 && qlen(p->oq); i++)
		tsleep(&p->r, qlen, p->oq, 125);

	if(strncmp(cmd, "break", 5) == 0){
		uartbreak(p, 0);
		return;
	}
		
	n = atoi(cmd+1);
	switch(*cmd){
	case 'B':
	case 'b':
		uartsetbaud(p, n);
		break;
	case 'D':
	case 'd':
		uartdtr(p, n);
		break;
	case 'f':
	case 'F':
		qflush(p->oq);
		break;
	case 'H':
	case 'h':
		qhangup(p->iq, 0);
		qhangup(p->oq, 0);
		break;
	case 'L':
	case 'l':
		uartbits(p, n);
		break;
	case 'm':
	case 'M':
		uartmflow(p, n);
		break;
	case 'n':
	case 'N':
		qnoblock(p->oq, n);
		break;
	case 'P':
	case 'p':
		uartparity(p, *(cmd+1));
		break;
	case 'K':
	case 'k':
		uartbreak(p, n);
		break;
	case 'R':
	case 'r':
		uartrts(p, n);
		break;
	case 'Q':
	case 'q':
		qsetlimit(p->iq, n);
		qsetlimit(p->oq, n);
		break;
	case 'W':
	case 'w':
		/* obsolete */
		break;
	case 'X':
	case 'x':
		p->xonoff = n;
		break;
	case 'Z':
	case 'z':
		p->loopback = n;
		break;
	}
}

static long
uartwrite(Chan *c, void *buf, long n, vlong offset)
{
	Uart *p;
	char cmd[32];
	int m, inuse;

	USED(offset);

	if(c->qid.path & CHDIR)
		error(Eperm);

	p = uart[NETID(c->qid.path)];

	switch(NETTYPE(c->qid.path)){
	case Ndataqid:
		return qwrite(p->oq, buf, n);
	case Nctlqid:
		if(n >= sizeof(cmd))
			n = sizeof(cmd)-1;
		memmove(cmd, buf, n);
		cmd[n] = 0;
		uartctl(p, cmd);
		return n;
	case Ntypeqid:
		if(p->smc || p->putc)
			error(Ebadarg);
		if(n >= sizeof(cmd))
			n = sizeof(cmd)-1;
		memmove(cmd, buf, n);
		cmd[n] = 0;
		m = strtoul(cmd, nil, 0);
		inuse = 0;
		qlock(p);
		if(p->opens == 0){
			p->mode = m & 0x7F;
			p->loopback = (m&0x80)!=0;
			p->setup = 0;
		}else
			inuse = 1;
		qunlock(p);
		if(inuse)
			error(Einuse);
		return n;
	}
}

static long
uartbwrite(Chan *c, Block *bp, ulong offset)
{
	/* TO DO: better */
	return devbwrite(c, bp, offset);
}

static void
uartwstat(Chan *c, char *dp)
{
	Dir d;
	Dirtab *dt;

	if(!iseve())
		error(Eperm);
	if(CHDIR & c->qid.path)
		error(Eperm);
	if(NETTYPE(c->qid.path) == Nstatqid)
		error(Eperm);

	dt = &uartdir[3 * NETID(c->qid.path)];
	convM2D(dp, &d);
	d.mode &= 0666;
	dt[0].perm = dt[1].perm = d.mode;
}

Dev uartdevtab = {
	't',
	"uart",

	uartreset,
	devinit,
	uartattach,
	devclone,
	uartwalk,
	uartstat,
	uartopen,
	devcreate,
	uartclose,
	uartread,
	uartbread,
	uartwrite,
	uartbwrite,
	devremove,
	uartwstat,
};

A mpc/etherscc.c => mpc/etherscc.c +552 -0
@@ 0,0 1,552 @@
/*
 * SCCn ethernet
 */

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "../port/netif.h"

#include "etherif.h"

enum {
	Nrdre		= 16,	/* receive descriptor ring entries */
	Ntdre		= 16,	/* transmit descriptor ring entries */

	Rbsize		= ETHERMAXTU+4,		/* ring buffer size (+4 for CRC) */
	Bufsize		= (Rbsize+7)&~7,	/* aligned */
};

enum {
	/* ether-specific Rx BD bits */
	RxMiss=		1<<8,
	RxeLG=		1<<5,
	RxeNO=		1<<4,
	RxeSH=		1<<3,
	RxeCR=		1<<2,
	RxeOV=		1<<1,
	RxeCL=		1<<0,
	RxError=		(RxeLG|RxeNO|RxeSH|RxeCR|RxeOV|RxeCL),	/* various error flags */

	/* ether-specific Tx BD bits */
	TxPad=		1<<14,	/* pad short frames */
	TxTC=		1<<10,	/* transmit CRC */
	TxeDEF=		1<<9,
	TxeHB=		1<<8,
	TxeLC=		1<<7,
	TxeRL=		1<<6,
	TxeUN=		1<<1,
	TxeCSL=		1<<0,

	/* scce */
	RXB=	1<<0,
	TXB=	1<<1,
	BSY=		1<<2,
	RXF=		1<<3,
	TXE=		1<<4,

	/* psmr */
	PRO=	1<<9,	/* promiscuous mode */

	/* gsmrl */
	ENR=	1<<5,
	ENT=	1<<4,
};

typedef struct Etherparam Etherparam;
struct Etherparam {
	SCCparam;
	ulong	c_pres;		/* preset CRC */
	ulong	c_mask;		/* constant mask for CRC */
	ulong	crcec;		/* CRC error counter */
	ulong	alec;		/* alighnment error counter */
	ulong	disfc;		/* discard frame counter */
	ushort	pads;		/* short frame PAD characters */
	ushort	ret_lim;	/* retry limit threshold */
	ushort	ret_cnt;	/* retry limit counter */
	ushort	mflr;		/* maximum frame length reg */
	ushort	minflr;		/* minimum frame length reg */
	ushort	maxd1;		/* maximum DMA1 length reg */
	ushort	maxd2;		/* maximum DMA2 length reg */
	ushort	maxd;		/* rx max DMA */
	ushort	dma_cnt;	/* rx dma counter */
	ushort	max_b;		/* max bd byte count */
	ushort	gaddr[4];		/* group address filter */
	ulong	tbuf0_data0;	/* save area 0 - current frm */
	ulong	tbuf0_data1;	/* save area 1 - current frm */
	ulong	tbuf0_rba0;
	ulong	tbuf0_crc;
	ushort	tbuf0_bcnt;
	ushort	paddr[3];	/* physical address LSB to MSB increasing */
	ushort	p_per;		/* persistence */
	ushort	rfbd_ptr;	/* rx first bd pointer */
	ushort	tfbd_ptr;	/* tx first bd pointer */
	ushort	tlbd_ptr;	/* tx last bd pointer */
	ulong	tbuf1_data0;	/* save area 0 - next frame */
	ulong	tbuf1_data1;	/* save area 1 - next frame */
	ulong	tbuf1_rba0;
	ulong	tbuf1_crc;
	ushort	tbuf1_bcnt;
	ushort	tx_len;		/* tx frame length counter */
	ushort	iaddr[4];		/* individual address filter*/
	ushort	boff_cnt;	/* back-off counter */
	ushort	taddr[3];	/* temp address */
};

typedef struct {
	Lock;
	int	sccid;
	int	port;
	int	init;
	int	active;
	SCC*	scc;

	Ring;

	ulong	interrupts;			/* statistics */
	ulong	deferred;
	ulong	heartbeat;
	ulong	latecoll;
	ulong	retrylim;
	ulong	underrun;
	ulong	overrun;
	ulong	carrierlost;
	ulong	retrycount;
} Ctlr;

static	int	sccirq[] = {-1, 0x1E, 0x1D, 0x1C, 0x1B};
static	int	sccid[] = {-1, SCC1ID, SCC2ID, SCC3ID, SCC4ID};
static	int	sccbase[] = {-1, 0xA00, 0xA20, 0xA40, 0xA60};
static	int	sccparam[] = {-1, SCC1P, SCC2P, SCC3P, SCC4P};

static void
attach(Ether *ether)
{
	Ctlr *ctlr;

	ctlr = ether->ctlr;
	ctlr->active = 1;
	ctlr->scc->gsmrl |= ENR|ENT;
	eieio();
//	m->bcsr[1] &= ~DisableEther;
}

static void
closed(Ether *ether)
{
	Ctlr *ctlr;

	ctlr = ether->ctlr;
	if(0){
		if(ether->port == 2)
			scc2stop(ctlr->scc);
		ilock(ctlr);
		ctlr->active = 0;
		iunlock(ctlr);
		print("Ether closed\n");
	}
}

static void
promiscuous(void* arg, int on)
{
	Ether *ether;
	Ctlr *ctlr;

	ether = (Ether*)arg;
	ctlr = ether->ctlr;

	ilock(ctlr);
	if(on || ether->nmaddr)
		ctlr->scc->psmr |= PRO;
	else
		ctlr->scc->psmr &= ~PRO;
	iunlock(ctlr);
}

static void
multicast(void* arg, uchar *addr, int on)
{
	Ether *ether;
	Ctlr *ctlr;

	USED(addr, on);	/* if on, could SetGroupAddress; if !on, it's hard */

	ether = (Ether*)arg;
	ctlr = ether->ctlr;

	ilock(ctlr);
	if(ether->prom || ether->nmaddr)
		ctlr->scc->psmr |= PRO;
	else
		ctlr->scc->psmr &= ~PRO;
	iunlock(ctlr);
}

static void
txstart(Ether *ether)
{
	int len;
	Ctlr *ctlr;
	Block *b;
	BD *dre;

	ctlr = ether->ctlr;
	if(ctlr->init)
		return;
	while(ctlr->ntq < Ntdre-1){
		b = qget(ether->oq);
		if(b == 0)
			break;

		dre = &ctlr->tdr[ctlr->tdrh];
		if(dre->status & BDReady)
			panic("ether: txstart");
	
		/*
		 * Give ownership of the descriptor to the chip, increment the
		 * software ring descriptor pointer and tell the chip to poll.
		 */
		len = BLEN(b);
		dcflush(b->rp, len);
		if(ctlr->txb[ctlr->tdrh] != nil)
			panic("scc/ether: txstart");
		ctlr->txb[ctlr->tdrh] = b;
		if((ulong)b->rp&1)
			panic("scc/ether: txstart align");	/* TO DO: ensure alignment */
		dre->addr = PADDR(b->rp);
		dre->length = len;
		eieio();
		dre->status = (dre->status & BDWrap) | BDReady|TxPad|BDInt|BDLast|TxTC;
		eieio();
		ctlr->scc->todr = 1<<15;	/* transmit now */
		eieio();
		ctlr->ntq++;
		ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre);
	}
}

static void
transmit(Ether* ether)
{
	Ctlr *ctlr;

	ctlr = ether->ctlr;
	ilock(ctlr);
	txstart(ether);
	iunlock(ctlr);
}

static void
interrupt(Ureg*, Ether *ether)
{
	int len, events, status;
	Ctlr *ctlr;
	BD *dre;
	Block *b;

	ctlr = ether->ctlr;
	if(!ctlr->active)
		return;	/* not ours */

	/*
	 * Acknowledge all interrupts and whine about those that shouldn't
	 * happen.
	 */
	events = ctlr->scc->scce;
	eieio();
	ctlr->scc->scce = events;
	eieio();
	ctlr->interrupts++;

	if(events & (TXE|BSY|RXB)){
		if(events & RXB)
			ctlr->overrun++;
		if(events & TXE)
			ether->oerrs++;
		if(0 || events & TXE)
			print("ETHER.SCC#%d: scce = 0x%uX\n", ether->ctlrno, events);
	}
	/*
	 * Receiver interrupt: run round the descriptor ring logging
	 * errors and passing valid receive data up to the higher levels
	 * until we encounter a descriptor still owned by the chip.
	 */
	if(events & (RXF|RXB) || 1){
		dre = &ctlr->rdr[ctlr->rdrx];
		while(((status = dre->status) & BDEmpty) == 0){
			if(status & RxError || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){
				if(status & (RxeLG|RxeSH))
					ether->buffs++;
				if(status & RxeNO)
					ether->frames++;
				if(status & RxeCR)
					ether->crcs++;
				if(status & RxeOV)
					ether->overflows++;
				//print("eth rx: %ux\n", status);
			}
			else{
				/*
				 * We have a packet. Read it in.
				 */
				len = dre->length-4;
				if((b = iallocb(len)) != 0){
					memmove(b->wp, KADDR(dre->addr), len);
					b->wp += len;
					etheriq(ether, b, 1);
					dcflush(KADDR(dre->addr), len);
				}else
					ether->soverflows++;
			}

			/*
			 * Finished with this descriptor, reinitialise it,
			 * give it back to the chip, then on to the next...
			 */
			dre->length = 0;
			dre->status = (status & BDWrap) | BDEmpty | BDInt;
			eieio();

			ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre);
			dre = &ctlr->rdr[ctlr->rdrx];
		}
	}

	/*
	 * Transmitter interrupt: handle anything queued for a free descriptor.
	 */
	if(events & TXB){
		lock(ctlr);
		while(ctlr->ntq){
			dre = &ctlr->tdr[ctlr->tdri];
			status = dre->status;
			if(status & BDReady)
				break;
			if(status & TxeDEF)
				ctlr->deferred++;
			if(status & TxeHB)
				ctlr->heartbeat++;
			if(status & TxeLC)
				ctlr->latecoll++;
			if(status & TxeRL)
				ctlr->retrylim++;
			if(status & TxeUN)
				ctlr->underrun++;
			if(status & TxeCSL)
				ctlr->carrierlost++;
			ctlr->retrycount += (status>>2)&0xF;
			b = ctlr->txb[ctlr->tdri];
			if(b == nil)
				panic("scce/interrupt: bufp");
			ctlr->txb[ctlr->tdri] = nil;
			freeb(b);
			ctlr->ntq--;
			ctlr->tdri = NEXT(ctlr->tdri, Ntdre);
		}
		txstart(ether);
		unlock(ctlr);
	}
	if(events & TXE)
		cpmop(RestartTx, ctlr->sccid, 0);
}

static long
ifstat(Ether* ether, void* a, long n, ulong offset)
{
	char *p;
	int len;
	Ctlr *ctlr;

	if(n == 0)
		return 0;

	ctlr = ether->ctlr;

	p = malloc(READSTR);
	len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts);
	len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->carrierlost);
	len += snprint(p+len, READSTR-len, "heartbeat: %lud\n", ctlr->heartbeat);
	len += snprint(p+len, READSTR-len, "retrylimit: %lud\n", ctlr->retrylim);
	len += snprint(p+len, READSTR-len, "retrycount: %lud\n", ctlr->retrycount);
	len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->latecoll);
	len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->overrun);
	len += snprint(p+len, READSTR-len, "txunderruns: %lud\n", ctlr->underrun);
	snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->deferred);
	n = readstr(offset, a, n, p);
	free(p);

	return n;
}

/*
 * This follows the MPC823 user guide: section16.9.23.7's initialisation sequence,
 * except that it sets the right bits for the MPC823ADS board when SCC2 is used,
 * and those for the 860/821 development board for SCC1.
 */
static void
sccsetup(Ctlr *ctlr, SCC *scc, uchar *ea)
{
	int i, rcs, tcs;
	Etherparam *p;
	IMM *io;

	io = ioplock();
	if(ctlr->port == 2){
		io->papar |= SIBIT(12)|SIBIT(13);	/* enable TXD2 and RXD2 pins */
		io->padir &= ~(SIBIT(12)|SIBIT(13));
		io->paodr &= ~SIBIT(12);

		io->pcpar &= ~(SIBIT(9)|SIBIT(8));	/* enable CLSN (CTS2) and RENA (CD2) */
		io->pcdir &= ~(SIBIT(9)|SIBIT(8));
		io->pcso |= SIBIT(9)|SIBIT(8);

		io->papar |= SIBIT(6)|SIBIT(5);	/* enable CLK2 and CLK3 */
		io->padir &= ~(SIBIT(6)|SIBIT(5));
		eieio();

		rcs = CLK2;
		tcs = CLK3;
	}else{
		io->papar |= SIBIT(14)|SIBIT(15);	/* enable TXD1 and RXD1 pins */
		io->padir &= ~(SIBIT(14)|SIBIT(15));
		io->paodr &= ~SIBIT(14);

		io->pcpar &= ~(SIBIT(10)|SIBIT(11));	/* enable CLSN (CTS1) and RENA (CD1) */
		io->pcdir &= ~(SIBIT(10)|SIBIT(11));
		io->pcso |= SIBIT(10)|SIBIT(11);
		eieio();

		io->papar |= SIBIT(6)|SIBIT(7);	/* enable CLK2 and CLK1 */
		io->padir &= ~(SIBIT(6)|SIBIT(7));
		eieio();

		io->pcpar &= ~(SIBIT(4)|SIBIT(5)|SIBIT(6));	/* ETHLOOP, TPFULDL~, TPSQEL~ */
		io->pcdir |= SIBIT(4)|SIBIT(5)|SIBIT(6);
		io->pcdat &= ~SIBIT(4);
		io->pcdat |= SIBIT(5)|SIBIT(6);
		eieio();

		rcs = CLK2;
		tcs = CLK1;
	}
	iopunlock();

	sccnmsi(ctlr->port, rcs, tcs);	/* connect the clocks */

	p = (Etherparam*)KADDR(ctlr->sccid==SCC1ID? SCC1P: SCC2P);
	memset(p, 0, sizeof(*p));
	p->rfcr = 0x18;
	p->tfcr = 0x18;
	p->mrblr = Bufsize;
	p->rbase = PADDR(ctlr->rdr);
	p->tbase = PADDR(ctlr->tdr);

	cpmop(InitRxTx, ctlr->sccid, 0);

	p->c_pres = ~0;
	p->c_mask = 0xDEBB20E3;
	p->crcec = 0;
	p->alec = 0;
	p->disfc = 0;
	p->pads = 0x8888;
	p->ret_lim = 0xF;
	p->mflr = Rbsize;
	p->minflr = ETHERMINTU+4;
	p->maxd1 = Bufsize;
	p->maxd2 = Bufsize;
	p->p_per = 0;	/* only moderate aggression */

	for(i=0; i<Eaddrlen; i+=2)
		p->paddr[2-i/2] = (ea[i+1]<<8)|ea[i];	/* it's not the obvious byte order */

	scc->psmr = (2<<10)|(5<<1);	/* 32-bit CRC, ignore 22 bits before SFD */
	scc->dsr = 0xd555;
	scc->gsmrh = 0;	/* normal operation */
	scc->gsmrl = (1<<28)|(4<<21)|(1<<19)|0xC;	/* transmit clock invert, 48 bit preamble, repetitive 10 preamble, ethernet */
	eieio();
	scc->scce = ~0;	/* clear all events */
	eieio();
	scc->sccm = TXE | RXF | TXB;	/* enable interrupts */
	eieio();

	if(ctlr->sccid == SCC2ID){
		io->pbpar |= IBIT(18);	/* enable TENA pin (RTS2) */
		io->pbdir |= IBIT(18);
	}else{
		io->pbpar |= IBIT(19);	/* enable TENA pin (RTS1) */
		io->pbdir |= IBIT(19);
	}
	eieio();

	/* enable is deferred until attach */
}

static int
reset(Ether* ether)
{
	uchar ea[Eaddrlen];
	Ctlr *ctlr;
	SCC *scc;

	if(m->speed < 24){
		print("%s ether: system speed must be >= 24MHz for ether use\n", ether->type);
		return -1;
	}

	if(!(ether->port >= 1 && ether->port <= 4)){
		print("%s ether: no SCC port %d\n", ether->type, ether->port);
		return -1;
	}

	ether->irq = VectorCPIC + sccirq[ether->port];
	scc = IOREGS(sccbase[ether->port], SCC);
	if(ether->port == 2)
		scc2claim(1, DisableEther, scc2stop, scc);

	ctlr = malloc(sizeof(*ctlr));
	ether->ctlr = ctlr;
	memset(ctlr, 0, sizeof(*ctlr));
	ctlr->scc = scc;
	ctlr->port = ether->port;
	ctlr->sccid = sccid[ether->port];

	if(ioringinit(ctlr, Nrdre, Ntdre, Bufsize) < 0)
		panic("etherscc init");

	sccsetup(ctlr, scc, ether->ea);

	ether->mbps = 10;	/* TO DO: could be 100mbps on 860T */
	ether->attach = attach;
	ether->closed = closed;
	ether->transmit = transmit;
	ether->interrupt = interrupt;
	ether->ifstat = ifstat;

	ether->arg = ether;
	ether->promiscuous = promiscuous;
	ether->multicast = multicast;

	/*
	 * Until we know where to find it, insist that the plan9.ini
	 * entry holds the Ethernet address.
	 */
	memset(ea, 0, Eaddrlen);
	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
		print("no ether address");
		return -1;
	}

	return 0;
}

void
etherscclink(void)
{
	addethercard("SCC", reset);
	addethercard("SCC2", reset);
}

M mpc/fns.h => mpc/fns.h +2 -0
@@ 89,3 89,5 @@ ulong	getcallerpc(void*);

/* compatibility with inf2.1 */
#define	bpenumenv(n)	((char*)0)

#define IOREGS(x, T)	((T*)((char*)m->iomem+(x)))

M mpc/l.s => mpc/l.s +2 -11
@@ 122,17 122,15 @@
	MOVW	R4, SPR(IMMR)		/* set internal memory base */

	MOVW	$mach0(SB), R(MACH)
	ADD	$(MACHSIZE-8), R(MACH), R1
	ADD	$(MACHSIZE-8), R(MACH), R1	/* set stack */
	SUB	$4, R(MACH), R3
	ADD	$4, R1, R4

clrmach:
	MOVWU	R0, 4(R3)
	CMP	R3, R4
	BNE	clrmach

	MOVW	R0, R(USER)
	MOVW	R0, 0(R(MACH))

	MOVW	$edata(SB), R3
	MOVW	$end(SB), R4
	ADD	$4, R4


@@ 630,13 628,6 @@ TEXT	powerupled(SB), $0
	MOVW	R7,0(R11)
	RETURN

TEXT	reset(SB), $-4
	MOVW	$0,R4
	MOVW	0(R4), R5
loop:
	BR	loop


GLOBL	mach0+0(SB), $MACHSIZE
GLOBL	spltbl+0(SB), $4
GLOBL	intrtbl+0(SB), $4

M mpc/main.c => mpc/main.c +59 -0
@@ 16,9 16,35 @@ int nconf;
Conf	conf;

void
flash(void)
{
	int i;
	*(uchar*)(NIMMEM+0x2200) = 0;
	for(i=0; i<1000000; i++)
		;
	*(uchar*)(NIMMEM+0x2200) = 0x2;
	for(i=0; i<1000000; i++)
		;
}

void
main(void)
{
	machinit();
	confinit();
	xinit();
	printinit();
	cpminit();
	uartinstall();
	print("hello world\n");

	delay(100);

	reset();

	*(uchar*)0 = 0;
	for(;;)
		;
}

void


@@ 37,6 63,8 @@ machinit(void)
	mf = io->plprcr >> 20;
	m->oscclk = osc;
	m->speed = osc*(mf+1);
	m->cpuhz = m->speed*MHz;	/* general system clock (cycles) */
	m->clockgen = osc*MHz;		/* clock generator frequency (cycles) */
}

void


@@ 110,6 138,37 @@ iprint(char *fmt, ...)
	return n;
}

void
confinit(void)
{
	int nbytes;
	ulong pa;

	conf.nmach = 1;		/* processors */
	conf.nproc = 200;	/* processes */

	// hard wire for now
	pa = 0xff200000;		// leave 2 Meg for kernel
	nbytes = 10*1024*1024;	// leave room at the top as well
	
	conf.npage0 = nbytes/BY2PG;
	conf.base0 = pa;
	
	conf.npage1 = 0;
	conf.base1 = 0;

	conf.npage = conf.npage0 + conf.npage1;

	conf.upages = (conf.npage*50)/100;
	conf.ialloc = ((conf.npage-conf.upages)/2)*BY2PG;

	/* set up other configuration parameters */
	conf.nswap = conf.npage*3;
	conf.nswppo = 4096;
	conf.nimage = 200;

	conf.copymode = 0;		/* copy on write */
}

int
isaconfig(char *class, int ctlrno, ISAConf *isa)

M mpc/mem.h => mpc/mem.h +5 -0
@@ 18,6 18,8 @@
#define	CACHELINELOG	4
#define CACHELINESZ	(1<<CACHELINELOG)

#define	MHz	1000000

#define	MAXMACH		1			/* max # cpus system can run */
#define	MACHSIZE	BY2PG
#define KSTACK		4096			/* Size of kernel stack */


@@ 146,8 148,11 @@
#define	SCC2P	(INTMEM+0x3D00)
#define	SPIP	(INTMEM+0x3D80)
#define	TIMERP	(INTMEM+0x3DB0)
#define	IDMA2P	(INTMEM+0x3DC0)
#define	SCC3P	(INTMEM+0x3E00)
#define	SMC1P	(INTMEM+0x3E80)
#define	DSP1P	(INTMEM+0x3EC0)
#define	SCC4P	(INTMEM+0x3F00)
#define	SMC2P	(INTMEM+0x3F80)
#define	DSP2P	(INTMEM+0x3FC0)


M mpc/trap.c => mpc/trap.c +13 -0
@@ 131,3 131,16 @@ intrenable(int v, void (*r)(Ureg*, void*), void *arg, int)
{
	USED(v, r, arg);
}

void
reset(void)
{
	IMM *io;

	io = m->iomem;
	io->plprcrk = KEEP_ALIVE_KEY;	// unlock
	io->plprcr |= IBIT(24);		// enable checkstop reset
	putmsr(getmsr() & ~MSR_ME);
	// cause checkstop -> causes reset
	*(ulong*)(0) = 0;
}

M port/chan.c => port/chan.c +2 -2
@@ 688,7 688,7 @@ namec(char *name, int amode, int omode, ulong perm)
		if(omode == OEXEC)
			c->flag &= ~CCACHE;

		c = devtab[c->type]->open(c, omode);
		c = devtab[c->type]->open(c, omode&~OCEXEC);

		if(omode & OCEXEC)
			c->flag |= CCEXEC;


@@ 749,7 749,7 @@ namec(char *name, int amode, int omode, ulong perm)
			omode |= OTRUNC;
			goto Open;
		}
		devtab[c->type]->create(c, elem, omode, perm);
		devtab[c->type]->create(c, elem, omode&~OCEXEC, perm);
		if(omode & OCEXEC)
			c->flag |= CCEXEC;
		if(omode & ORCLOSE)

M port/dev.c => port/dev.c +2 -2
@@ 159,7 159,7 @@ devstat(Chan *c, char *db, Dirtab *tab, int ntab, Devgen *gen)
		case 0:
			break;
		case 1:
			if(eqqid(c->qid, dir.qid)) {
			if(c->qid.path == dir.qid.path) {
				if(c->flag&CMSG)
					dir.mode |= CHMOUNT;
				convD2M(&dir, db);


@@ 211,7 211,7 @@ devopen(Chan *c, int omode, Dirtab *tab, int ntab, Devgen *gen)
		case 0:
			break;
		case 1:
			if(eqqid(c->qid, dir.qid)) {
			if(c->qid.path == dir.qid.path) {
				if(strcmp(up->user, dir.uid) == 0)
					mode = dir.mode;
				else

M port/devenv.c => port/devenv.c +37 -23
@@ 28,11 28,21 @@ envgen(Chan *c, Dirtab*, int, int s, Dir *dp)
		return -1;
	}

	devdir(c, (Qid){e->path, 0}, e->name, e->len, eve, 0666, dp);
	devdir(c, e->qid, e->name, e->len, eve, 0666, dp);
	qunlock(eg);
	return 1;
}

static Evalue*
envlookup(Egrp *eg, char *name, ulong qidpath)
{
	Evalue *e;
	for(e = eg->entries; e; e = e->link)
		if(e->qid.path == qidpath || (name && strcmp(e->name, name) == 0))
			return e;
	return nil;
}

static Chan*
envattach(char *spec)
{


@@ 48,6 58,8 @@ envwalk(Chan *c, char *name)
static void
envstat(Chan *c, char *db)
{
	if(c->qid.path & CHDIR)
		c->qid.vers = up->egrp->vers;
	devstat(c, db, 0, 0, envgen);
}



@@ 64,15 76,13 @@ envopen(Chan *c, int omode)
	}
	else {
		qlock(eg);
		for(e = eg->entries; e; e = e->link)
			if(e->path == c->qid.path)
				break;

		e = envlookup(eg, nil, c->qid.path);
		if(e == 0) {
			qunlock(eg);
			error(Enonexist);
		}
		if(omode == (OWRITE|OTRUNC) && e->value) {
		if((omode & OTRUNC) && e->value) {
			e->qid.vers++;
			free(e->value);
			e->value = 0;
			e->len = 0;


@@ 103,18 113,19 @@ envcreate(Chan *c, char *name, int omode, ulong)
		nexterror();
	}

	for(e = eg->entries; e; e = e->link)
		if(strcmp(e->name, name) == 0)
			error(Einuse);
	if(envlookup(eg, name, -1))
		error(Eexist);

	e = smalloc(sizeof(Evalue));
	e->name = smalloc(strlen(name)+1);
	strcpy(e->name, name);

	e->path = ++eg->path;
	e->qid.path = ++eg->path;
	e->qid.vers = 0;
	eg->vers++;
	e->link = eg->entries;
	eg->entries = e;
	c->qid = (Qid){e->path, 0};
	c->qid = e->qid;

	qunlock(eg);
	poperror();


@@ 135,10 146,9 @@ envremove(Chan *c)

	eg = up->egrp;
	qlock(eg);

	l = &eg->entries;
	for(e = *l; e; e = e->link) {
		if(e->path == c->qid.path)
		if(e->qid.path == c->qid.path)
			break;
		l = &e->link;
	}


@@ 149,6 159,7 @@ envremove(Chan *c)
	}

	*l = e->link;
	eg->vers++;
	qunlock(eg);
	free(e->name);
	if(e->value)


@@ 157,8 168,15 @@ envremove(Chan *c)
}

static void
envclose(Chan*)
envclose(Chan *c)
{
	/*
	 * close can't fail, so errors from remove will be ignored anyway.
	 * since permissions aren't checked,
	 * envremove can't not remove it if its there.
	 */
	if(c->flag & CRCLOSE)
		envremove(c);
}

static long


@@ 173,10 191,7 @@ envread(Chan *c, void *a, long n, vlong off)

	eg = up->egrp;
	qlock(eg);
	for(e = eg->entries; e; e = e->link)
		if(e->path == c->qid.path)
			break;

	e = envlookup(eg, nil, c->qid.path);
	if(e == 0) {
		qunlock(eg);
		error(Enonexist);


@@ 210,10 225,7 @@ envwrite(Chan *c, void *a, long n, vlong off)

	eg = up->egrp;
	qlock(eg);
	for(e = eg->entries; e; e = e->link)
		if(e->path == c->qid.path)
			break;

	e = envlookup(eg, nil, c->qid.path);
	if(e == 0) {
		qunlock(eg);
		error(Enonexist);


@@ 228,6 240,8 @@ envwrite(Chan *c, void *a, long n, vlong off)
		e->len = vend;
	}
	memmove(e->value+offset, a, n);
	e->qid.vers++;
	eg->vers++;
	qunlock(eg);
	return n;
}


@@ 269,7 283,7 @@ envcpy(Egrp *to, Egrp *from)
			memmove(ne->value, e->value, e->len);
			ne->len = e->len;
		}
		ne->path = ++to->path;
		ne->qid.path = ++to->path;
		*l = ne;
		l = &ne->link;
	}

M port/devsrv.c => port/devsrv.c +40 -20
@@ 63,12 63,24 @@ srvstat(Chan *c, char *db)
	devstat(c, db, 0, 0, srvgen);
}

static Srv*
srvlookup(char *name, ulong qidpath)
{
	Srv *sp;
	for(sp = srv; sp; sp = sp->link)
		if(sp->path == qidpath || (name && strcmp(sp->name, name) == 0))
			return sp;
	return nil;
}

static Chan*
srvopen(Chan *c, int omode)
{
	Srv *sp;

	if(c->qid.path == CHDIR){
		if(omode & ORCLOSE)
			error(Eperm);
		if(omode != OREAD)
			error(Eisdir);
		c->mode = omode;


@@ 82,16 94,13 @@ srvopen(Chan *c, int omode)
		nexterror();
	}

	for(sp = srv; sp; sp = sp->link)
		if(sp->path == c->qid.path)
			break;

	sp = srvlookup(nil, c->qid.path);
	if(sp == 0 || sp->chan == 0)
		error(Eshutdown);

	if(omode&OTRUNC)
		error(Eperm);
	if(omode!=sp->chan->mode && sp->chan->mode!=ORDWR)
	if(openmode(omode)!=sp->chan->mode && sp->chan->mode!=ORDWR)
		error(Eperm);

	cclose(c);


@@ 106,9 115,12 @@ srvcreate(Chan *c, char *name, int omode, ulong perm)
{
	Srv *sp;

	if(omode != OWRITE)
	if(openmode(omode) != OWRITE)
		error(Eperm);

	if(omode & OCEXEC)	/* can't happen */
		panic("someone broke namec");

	sp = malloc(sizeof(Srv));
	if(sp == 0)
		error(Enomem);


@@ 118,6 130,9 @@ srvcreate(Chan *c, char *name, int omode, ulong perm)
		qunlock(&srvlk);
		nexterror();
	}
	if(srvlookup(name, -1))
		error(Eexist);

	sp->path = qidpath++;
	sp->link = srv;
	c->qid.path = sp->path;


@@ 174,9 189,7 @@ srvwstat(Chan *c, char *dp)
	Dir d;
	Srv *sp;

	if(!iseve())
		error(Eperm);
	if(CHDIR & c->qid.path)
	if(c->qid.path & CHDIR)
		error(Eperm);

	qlock(&srvlk);


@@ 185,11 198,13 @@ srvwstat(Chan *c, char *dp)
		nexterror();
	}

	for(sp = srv; sp; sp = sp->link)
		if(sp->path == c->qid.path)
			break;
	if(sp == 0 || sp->chan == 0)
		error(Eshutdown);
	sp = srvlookup(nil, c->qid.path);
	if(sp == 0)
		error(Enonexist);

	if(strcmp(sp->owner, up->user) && !iseve())
		error(Eperm);

	convM2D(dp, &d);
	d.mode &= 0777;
	sp->perm = d.mode;


@@ 199,8 214,16 @@ srvwstat(Chan *c, char *dp)
}

static void
srvclose(Chan*)
srvclose(Chan *c)
{
	/*
	 * errors from srvremove will be caught by cclose and ignored.
	 * in theory we need to override any changes in removability
	 * since open, but since all that's checked is the owner,
	 * which is immutable, all is well.
	 */
	if(c->flag & CRCLOSE)
		srvremove(c);
}

static long


@@ 232,15 255,12 @@ srvwrite(Chan *c, void *va, long n, vlong)
		cclose(c1);
		nexterror();
	}
	for(sp = srv; sp; sp = sp->link)
		if(sp->path == c->qid.path)
			break;

	sp = srvlookup(nil, c->qid.path);
	if(sp == 0)
		error(Enonexist);

	if(sp->chan)
		panic("srvwrite");
		error(Ebadusefd);

	sp->chan = c1;
	qunlock(&srvlk);

M port/error.h => port/error.h +1 -0
@@ 6,6 6,7 @@ extern char Emountrpc[];	/* mount rpc error */
extern char Eshutdown[];	/* mounted device shut down */
extern char Enocreate[];	/* mounted directory forbids creation */
extern char Enonexist[];	/* file does not exist */
extern char Eexist[];		/* file already exists */
extern char Ebadsharp[];	/* unknown device in # filename */
extern char Enotdir[];		/* not a directory */
extern char Eisdir[];		/* file is a directory */

M port/portdat.h => port/portdat.h +3 -2
@@ 422,7 422,8 @@ struct Egrp
	Ref;
	QLock;
	Evalue	*entries;
	ulong	path;
	ulong	path;	/* qid.path of next Evalue to be allocated */
	ulong	vers;	/* of Egrp */
};

struct Evalue


@@ 430,8 431,8 @@ struct Evalue
	char	*name;
	char	*value;
	int	len;
	ulong	path;
	Evalue	*link;
	Qid	qid;
};

struct Fgrp

M port/portfns.h => port/portfns.h +0 -3
@@ 258,9 258,6 @@ long		rtctime(void);
void		runlock(RWlock*);
Proc*		runproc(void);
void		savefpregs(FPsave*);
void		sccclock(void);
int		sccintr(void);
void		sccsetup(void*, ulong, int);
void		sched(void);
void		scheddump(void);
void		schedinit(void);

M port/sysfile.c => port/sysfile.c +0 -2
@@ 96,8 96,6 @@ fdtochan(int fd, int mode, int chkmnt, int iref)
int
openmode(ulong o)
{
	if(o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC))
		error(Ebadarg);
	o &= ~(OTRUNC|OCEXEC|ORCLOSE);
	if(o > OEXEC)
		error(Ebadarg);