~kris/9p

9hist

5d02de29ba95047c4e7cf238585df1accaf56eda — David du Colombier 32 years ago 14fdc3c
Plan 9 from Bell Labs 1993-11-13
18 files changed, 752 insertions(+), 3312 deletions(-)

D pc/adaptec.c
M pc/bbmalloc.c
M pc/clock.c
M pc/devether.c
D pc/devincon.c
D pc/ether.h
D pc/ether503.c
M pc/ether509.c
A pc/etherif.h
M pc/fault386.c
M pc/fns.h
M pc/kbd.c
M pc/l.s
M pc/main.c
M pc/mmu.c
A pc/screen.c
M pc/trap.c
D pc/ultrastor.c
D pc/adaptec.c => pc/adaptec.c +0 -505
@@ 1,505 0,0 @@
/*
 * Adaptec AHA-154[02][BC] Intelligent Host Adapter.
 * Needs work:
 *	handle multiple controllers;
 *	set options from user-level;
 *	split out to handle ultrastor too;
 */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"

#include "ureg.h"

enum {
	NCtlr		= 1,
	NTarget		= 8,		/* targets per controller */

	Port		= 0x330,	/* factory defaults: I/O port */
	CtlrID		= 7,		/*	adapter SCSI id */
	Irq		= 11,		/*	interrupt request level */
};

enum {					/* registers */
	Rc		= 0,		/* WO: control */
	Rs		= 0,		/* RO: status */
	Rcdo		= 1,		/* WO: command/data out */
	Rdi		= 1,		/* RO: data in */
	Rif		= 2,		/* RO: interrupt flags */
};

enum {					/* Rc */
	Scrst		= 0x10,		/* SCSI bus reset */
	Irst		= 0x20,		/* interrupt reset */
	Srst		= 0x40,		/* soft reset */
	Hrst		= 0x80,		/* hard reset */
};

enum {					/* Rs */
	Invdcmd		= 0x01,		/* invalid host adapter command */
	Df		= 0x04,		/* data in port full */
	Cdf		= 0x08,		/* command/data port full */
	Idle		= 0x10,		/* SCSI host adapter idle */
	Init		= 0x20,		/* mailbox initialisation required */
	Diagf		= 0x40,		/* internal diagnostic failure */
	Stst		= 0x80,		/* self testing in progress */
};

enum {					/* Rcdo */
	Cnop		= 0x00,		/* no operation */
	Cmbinit		= 0x01,		/* mailbox initialisation */
	Cstart		= 0x02,		/* start SCSI command */
	Cbios		= 0x03,		/* start PC/AT BIOS command */
	Cinquiry	= 0x04,		/* adapter inquiry */
	Cmboie		= 0x05,		/* enable mailbox out available interrupt */
	Cselection	= 0x06,		/* set selection timeout */
	Cbuson		= 0x07,		/* set bus-on time */
	Cbusoff		= 0x08,		/* set bus-off time */
	Ctransfer	= 0x09,		/* set transfer speed */
	Cdevices	= 0x0A,		/* return installed devices */
	Cconfiguration	= 0x0B,		/* return configuration data */
	Ctenable	= 0x0C,		/* enable target mode */
	Csetup		= 0x0D,		/* return setup data */
	Cwbuff		= 0x1A,		/* write adapter channel 2 buffer */
	Crbuff		= 0x1B,		/* read adapter channel 2 buffer */
	Cwfifo		= 0x1C,		/* write adapter FIFO buffer */
	Crfifo		= 0x1D,		/* read adapter FIFO buffer */
	Cecho		= 0x1F,		/* ECHO command data */
	Cdiag		= 0x20,		/* adapter diagnostic */
	Coptions	= 0x21,		/* set host adapter options */
};

enum {					/* Rif */
	Mbif		= 0x01,		/* mailbox in full */
	Mboa		= 0x02,		/* mailbox out available */
	Hacc		= 0x04,		/* host adapter command complete */
	Scrd		= 0x08,		/* SCSI reset detected */
	Ai		= 0x80,		/* any interrupt */
};

typedef struct {
	uchar	cmd;			/* command */
	uchar	msb;			/* CCB pointer MSB */
	uchar	mid;			/* CCB pointer MID */
	uchar	lsb;			/* CCB pointer LSB */
} Mbox;

enum {					/* mailbox commands */
	Mbfree		= 0x00,		/* mailbox is free */

	Mbostart	= 0x01,		/* start SCSI or adapter command */
	Mboabort	= 0x02,		/* abort SCSI or adapter command */

	Mbiok		= 0x01,		/* CCB completed without error */
	Mbiabort	= 0x02,		/* CCB aborted by host */
	Mbinx		= 0x03,		/* aborted CCB not found */
	Mbierror	= 0x04,		/* CCB completed with error */
};

typedef struct {
	uchar	op;			/* command control block operation code */
	uchar	ctl;			/* address and direction control */
	uchar	cmdlen;			/* SCSI command length */
	uchar	reqlen;			/* request sense allocation length */
	uchar	datalen[3];		/* data length (MSB, MID, LSB) */
	uchar	dataptr[3];		/* data pointer (MSB, MID, LSB) */
	uchar	linkptr[3];		/* link pointer (MSB, MID, LSB) */
	uchar	linkid;			/* command linking identifier */
	uchar	hastat;			/* host adapter status */
	uchar	tarstat;		/* target device status */
	uchar	reserved[2];
	uchar	cs[12+0xFF];		/* SCSI command and sense bytes */
} Ccb;

enum {					/* op */
	OInitiator	= 0x00,		/* initiator CCB */
	OTarget		= 0x01,		/* target CCB */
	Osg		= 0x02,		/* initiator CCB with scatter/gather */
	Ordl		= 0x03,		/* initiator CCB, residual data length returned */
	Osgrdl		= 0x04,		/* initiator CCB, both of the above */
	Obdr		= 0x81,		/* bus device reset */
};

enum {					/* ctl */
	CCBdatain	= 0x08,		/* inbound data transfer, length is checked */
	CCBdataout	= 0x10,		/* outbound data transfer, length is checked */
};

enum {					/* hastat */
	Eok		= 0x00,		/* no host adapter detected error */
	Etimeout	= 0x11,		/* selection timeout */
	Elength		= 0x12,		/* data over/under run */
	Ebusfree	= 0x13,		/* unexpected bus free */
	Ephase		= 0x14,		/* target bus phase sequence error */
	Eopcode		= 0x16,		/* invalid CCB opcode */
	Elink		= 0x17,		/* linked CCB does not have same LUN */
	Edirection	= 0x18,		/* invalid target direction received from host */
	Eduplicate	= 0x19,		/* duplicate CCB received in target mode */
	Esegment	= 0x1A,		/* invalid CCB or segment list parameter */
};

enum {					/* tarstat */
	Sgood		= 0x00,		/* good status */
	Scheck		= 0x02,		/* check status */
	Sbusy		= 0x08,		/* LUN busy */
};

typedef struct Target Target;
typedef struct Ctlr Ctlr;

struct Target {
	QLock;
	Rendez;

	Ccb	ccb;
	ulong	paddr;			/* physical address of ccb */
	uchar	*sense;			/* address of returned sense data */

	int	done;
	Target	*active;		/* link on active list */
};

struct Ctlr {
	Lock;

	ulong	port;			/* I/O port */
	ulong	id;			/* adapter SCSI id */

	uchar	cmd[5];			/* adapter command out */
	uchar	cmdlen;			/* adapter command out length */
	uchar	data[256];		/* adapter command data in */
	uchar	datalen;		/* adapter command data in length */

	Mbox	mb[NTarget+NTarget];	/* mailbox out + mailbox in */

	Mbox	*mbox;			/* current mailbox out index into mb */
	Mbox	*mbix;			/* current mailbox in index into mb */

	Target	target[NTarget];
	Target	*active;		/* link on active list */
};

static Ctlr softctlr[NCtlr];

/*
 * Issue a command to the controller. The command and its length is
 * contained in ctlr->cmd and ctlr->cmdlen. If any data is to be
 * returned, ctlr->datalen should be non-0, and the returned data will
 * be placed in ctlr->data.
 * If we see Hacc set, bail out, we'll process
 * the invalid command at interrupt time.
 */
static void
issue(Ctlr *ctlr)
{
	int len;

	len = 0;
	while(len < ctlr->cmdlen){
		if((inb(ctlr->port+Rs) & Cdf) == 0){
			outb(ctlr->port+Rcdo, ctlr->cmd[len]);
			len++;
		}

		if(inb(ctlr->port+Rif) & Hacc)
			return;
	}

	if(ctlr->datalen){
		len = 0;
		while(len < ctlr->datalen){
			if(inb(ctlr->port+Rs) & Df){
				ctlr->data[len] = inb(ctlr->port+Rdi);
				len++;
			}

			if(inb(ctlr->port+Rif) & Hacc)
				return;
		}
	}
}

static int
done(void *arg)
{
	return ((Target*)arg)->done;
}

static int
scsiio(int bus, Scsi *p, int rw)
{
	Ctlr *ctlr;
	Target *tp;
	ushort status;
	ulong len, s;

	/*
	 * Wait for the target to become free,
	 * then set it up. The Adaptec will allow us to
	 * queue multiple transactions per target, but
	 * gives no guarantee about ordering, so we just
	 * allow one per target.
	 */
	ctlr = &softctlr[bus];
	tp = &ctlr->target[p->target];

	qlock(tp);
	if(waserror()){
		qunlock(tp);
		nexterror();
	}

	/*
	 * If this is a request-sense and we have valid sense data
	 * from the last command, return it immediately.
	 * A pox on these weird enum names and the WD33C93A status
	 * codes.
	 */
	if(p->cmd.base[0] == ScsiExtsens && tp->sense){
		len = 8+tp->sense[7];
		memmove(p->data.ptr, tp->sense, len);
		p->data.ptr += len;
		tp->sense = 0;

		qunlock(tp);
		poperror();
		return 0x6000;
	}
	tp->sense = 0;

	/*
	 * Fill in the ccb. 
	 */
	tp->ccb.op = Ordl;
	tp->ccb.ctl = (p->target<<5)|p->lun;

	len = p->cmd.lim - p->cmd.base;
	tp->ccb.cmdlen = len;
	memmove(tp->ccb.cs, p->cmd.base, len);

	tp->ccb.reqlen = 0xFF;

	len = p->data.lim - p->data.base;
	tp->ccb.datalen[0] = (len>>16) & 0xFF;
	tp->ccb.datalen[1] = (len>>8) & 0xFF;
	tp->ccb.datalen[2] = len;
	if(len == 0)
		tp->ccb.ctl |= CCBdataout|CCBdatain;
	else if(rw == ScsiIn)
		tp->ccb.ctl |= CCBdatain;
	else
		tp->ccb.ctl |= CCBdataout;

	len = PADDR(p->data.base);
	tp->ccb.dataptr[0] = (len>>16) & 0xFF;
	tp->ccb.dataptr[1] = (len>>8) & 0xFF;
	tp->ccb.dataptr[2] = len;

	tp->ccb.linkptr[0] = tp->ccb.linkptr[1] = tp->ccb.linkptr[2] = 0;
	tp->ccb.linkid = 0;

	tp->ccb.hastat = tp->ccb.tarstat = 0;
	tp->ccb.reserved[0] = tp->ccb.reserved[1] = 0;

	/*
	 * Link the target onto the beginning of the
	 * ctlr active list and start the request.
	 * The interrupt routine has to be able to take
	 * requests off the queue in any order.
	 */
	s = splhi();
	if(active.machs > 1)
		lock(ctlr);

	tp->done = 0;
	tp->active = ctlr->active;
	ctlr->active = tp;

	ctlr->mbox->msb = (tp->paddr>>16) & 0xFF;
	ctlr->mbox->mid = (tp->paddr>>8) & 0xFF;
	ctlr->mbox->lsb = tp->paddr & 0xFF;
	ctlr->mbox->cmd = Mbostart;

	ctlr->cmd[0] = Cstart;
	ctlr->cmdlen = 1;
	ctlr->datalen = 0;
	issue(ctlr);

	ctlr->mbox++;
	if(ctlr->mbox >= &ctlr->mb[NTarget])
		ctlr->mbox = ctlr->mb;

	if(active.machs > 1)
		unlock(ctlr);
	splx(s);

	/*
	 * Wait for the request to complete
	 * and return the status.
	 */
	tsleep(tp, done, tp, 60*5*1000);
	if(done(tp) == 0)
		print("adaptec%d: timeout cmd=#%2.2ux\n", p->target, tp->ccb.cs[0]);

	if((status = (tp->ccb.hastat<<8)) == 0)
		status = 0x6000;
	status |= tp->ccb.tarstat;
	len = (tp->ccb.datalen[0]<<16)|(tp->ccb.datalen[1]<<8)|tp->ccb.datalen[2];
	p->data.ptr = p->data.lim - len;

	/*
	 * If the command returned sense data, keep a note
	 * of where it is for a subsequent request-sense command.
	 */
	if(tp->ccb.tarstat == Scheck && tp->ccb.hastat == Eok)
		tp->sense = &tp->ccb.cs[tp->ccb.cmdlen];

	qunlock(tp);
	poperror();

	return status;
}

static int
aha1542exec(Scsi *p, int rw)
{
	Ctlr *ctlr = &softctlr[0];

	if(ctlr->port == 0)
		error(Enodev);
	if(p->target == CtlrID)
		return 0x6002;

	return p->status = scsiio(0, p, rw);
}

static void
interrupt(Ureg *ur)
{
	Ctlr *ctlr = &softctlr[0];
	uchar rif, rs;
	Target *tp, **l;
	ulong paddr;

	USED(ur);

	if(active.machs > 1)
		lock(ctlr);

	/*
	 * Save and clear the interrupt(s). The only
	 * interrupts expected are Hacc, which we ignore,
	 * and Mbif which means something completed.
	 */
	rif = inb(ctlr->port+Rif);
	rs = inb(ctlr->port+Rs);
	outb(ctlr->port+Rc, Irst);
	if(rif & ~(Ai|Hacc|Mbif))
		print("adaptec%d: interrupt #%2.2ux\n", 0, rif);
	if((rif & Hacc) && (rs & Invdcmd))
		print("adaptec%d: invdcmd #%2.2ux, len %d\n", 0, ctlr->cmd[0], ctlr->cmdlen);

	/*
	 * Look for something in the mail.
	 * If there is, try to find the recipient from the
	 * ccb address, take it off the active list and
	 * wakeup whoever.
	 */
	while(ctlr->mbix->cmd){
		paddr = (ctlr->mbix->msb<<16)|(ctlr->mbix->mid<<8)|ctlr->mbix->lsb;
		l = &ctlr->active;
		for(tp = *l; tp; tp = tp->active){
			if(tp->paddr == paddr)
				break;
			l = &tp->active;
		}
		if(tp == 0)
			panic("adaptec%d: no target for ccb #%lux\n", 0, paddr);
		*l = tp->active;

		ctlr->mbix->cmd = 0;

		tp->done = 1;
		wakeup(tp);

		ctlr->mbix++;
		if(ctlr->mbix >= &ctlr->mb[NTarget+NTarget])
			ctlr->mbix = &ctlr->mb[NTarget];
	}

	if(active.machs > 1)
		unlock(ctlr);
}

static void
reset(Ctlr *ctlr, ulong port, ulong id)
{
	ulong paddr;
	int i;

	/*
	 * Initialise the software controller and set the board
	 * scanning the mailboxes.
	 * Need code here to tidy things up if we're
	 * resetting after being active.
	 */
	memset(ctlr, 0, sizeof(Ctlr));
	ctlr->port = port;
	ctlr->id = id;
	for(i = 0; i < NTarget; i++){
		ctlr->target[i].paddr = PADDR(&ctlr->target[i].ccb);
		ctlr->mbox = ctlr->mb;
		ctlr->mbix = &ctlr->mb[NTarget];
	}

	ctlr->cmd[0] = Cmbinit;
	paddr = PADDR(ctlr->mb);
	ctlr->cmd[1] = NTarget;
	ctlr->cmd[2] = (paddr>>16) & 0xFF;
	ctlr->cmd[3] = (paddr>>8) & 0xFF;
	ctlr->cmd[4] = paddr & 0xFF;
	ctlr->cmdlen = 5;
	ctlr->datalen = 0;
	issue(ctlr);
}

static ISAConf aha1542 = {
	"aha1542",
	Port,
	Irq,
	0,
	0,
};

int (*
aha1542reset(void))(Scsi*, int)
{
	ISAConf *isa = &aha1542;

	/*
	 * See if we have any configuration info.
	 * Only one controller for now.
	 */
	if(isaconfig("scsi", 0, &aha1542) == 0)
		return 0;

	/*
	 * Attempt to hard-reset the board and reset
	 * the SCSI bus. If the board state doesn't settle to
	 * idle with mailbox initialisation required, either
	 * it isn't an Adaptec or it's broken.
	 */
	outb(isa->port+Rc, Hrst|Scrst);
	delay(500);
	if(inb(isa->port+Rs) != (Init|Idle))
		return 0;

	setvec(Int0vec+isa->irq, interrupt);
	reset(&softctlr[0], isa->port, CtlrID);

	return aha1542exec;
}

M pc/bbmalloc.c => pc/bbmalloc.c +30 -32
@@ 5,24 5,31 @@
#include	"fns.h"
#include	"io.h"

#define LINEWORDS	(0)

/*
 * Allocate memory for use in kernel bitblts.
 * The allocated memory must have a flushed instruction
 * cache, and the data cache must be flushed by bbdflush().
 * To avoid the need for frequent cache flushes, the memory
 * is allocated out of an arena, and the i-cache is only
 * flushed when it has to be reused
 *
 * This code will have to be interlocked if we ever get
 * a multiprocessor with a bitmapped display.
 */

/* a 0->3 bitblt can take 800 longs */
/* a 0->3 bitblt can take 900 words */
enum {
	narena=2,	/* put interrupt time stuff in separate arena */
	nbbarena=4096	/* number of words in an arena */
};

static ulong	*bbarena[narena];
static ulong	*bbcur[narena];
static ulong	*bblast[narena];
static ulong	bbarena[narena][nbbarena+LINEWORDS];
static ulong	*bbcur[narena] = {&bbarena[0][0], &bbarena[1][0]};
static ulong	*bblast[narena] = {0, 0};

#define INTENABLED(v)	((v)&(1<<9))
#define INTLEVEL(v)	(((v)&(1<<9)) == 0)
void *
bbmalloc(int nbytes)
{


@@ 32,52 39,43 @@ bbmalloc(int nbytes)

	nw = nbytes/sizeof(long);
	s = splhi();
	a = INTENABLED(s) ? 0 : 1;
	if(bbcur[a] + nw > bbarena[a] + nbbarena)
	a = INTLEVEL(s)? 1 : 0;
	if(bbcur[a] + nw > &bbarena[a][nbbarena])
		ans = bbarena[a];
	else
		ans = bbcur[a];
	bbcur[a] = ans + nw;
	bblast[a] = ans;
	bbcur[a] = ans + nw + (LINEWORDS);
	splx(s);
	return ans;
}

void
bbinit(void)
{
	int i;

	if(bbarena[0])
		return;
	for(i = 0; i < narena; i++){
		bbarena[i] = xalloc(nbbarena * sizeof(long));
		bbcur[i] = bbarena[i];
		bblast[i] = 0;
	}
	bblast[a] = ans;
	return ans;
}


void
bbfree(void *p, int n)
{
	int a, s;

	s = splhi();
	a = INTENABLED(s) ? 0 : 1;
	a = INTLEVEL(s)? 1 : 0;
	if(p == bblast[a])
		bbcur[a] = (ulong *)(((char *)bblast[a]) + n);
		bbcur[a] = (ulong *)(((char *)bblast[a]) + n + LINEWORDS*sizeof(long));
	splx(s);
}

void
bbdflush(void *p, int n)
{
	USED(p, n);
}

int
bbonstack(void)
{
	/*if(u)
		return 1;*/
	return 0;
}

void
bbexec(void (*memstart)(void), int len, int onstack)
{
	memstart();
	if(onstack)
		return;
	bbfree(memstart, len);
}

M pc/clock.c => pc/clock.c +0 -1
@@ 88,7 88,6 @@ clock(Ureg *ur)
	m->ticks++;

	checkalarms();
	uartclock();
	mouseclock();

	/*

M pc/devether.c => pc/devether.c +111 -481
@@ 3,46 3,42 @@
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
#include "devtab.h"
#include "ureg.h"
#include "../port/error.h"
#include "../port/netif.h"

#include "ether.h"
#include "etherif.h"

/*
 * Half-arsed attempt at a general top-level
 * ethernet driver. Needs work:
 *	handle multiple controllers
 *	much tidying
 *	set ethernet address
 *	need a ctl file passed down to card drivers
 *	  so we can set options.
 */
extern Card ether8003, ether503, ether2000;
extern Card ether509;
static Ether *ether[MaxEther];

/*
 * The ordering here is important for those cards
 * using the DP8390 (WD8003, 3Com503 and NE2000) as
 * attempting to determine if a card is a NE2000
 * cannot be done passively, so it must be last to
 * prevent scrogging one of the others.
 */
static Card *cards[] = {
	&ether8003,
	&ether503,
	&ether2000,
void
etherinit(void)
{
}

	&ether509,
	0
};
Chan*
etherattach(char *spec)
{
	ulong ctlrno;
	char *p;
	Chan *c;

enum {
	NCtlr		= 1,
};
	ctlrno = 0;
	if(spec && *spec){
		ctlrno = strtoul(spec, &p, 0);
		if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther))
			error(Ebadarg);
	}
	if(ether[ctlrno] == 0)
		error(Enodev);

/*static */struct Ctlr *softctlr[NCtlr];
static int nctlr;
	c = devattach('l', spec);
	c->dev = ctlrno;
	if(ether[ctlrno]->attach)
		(*ether[ctlrno]->attach)(ether[ctlrno]);
	return c;
}

Chan*
etherclone(Chan *c, Chan *nc)


@@ 53,530 49,164 @@ etherclone(Chan *c, Chan *nc)
int
etherwalk(Chan *c, char *name)
{
	return netwalk(c, name, &softctlr[0]->net);
	return netifwalk(ether[c->dev], c, name);
}

void
etherstat(Chan *c, char *dp)
{
	netstat(c, dp, &softctlr[0]->net);
	netifstat(ether[c->dev], c, dp);
}

Chan*
etheropen(Chan *c, int omode)
{
	return netopen(c, omode, &softctlr[0]->net);
	return netifopen(ether[c->dev], c, omode);
}

void
ethercreate(Chan *c, char *name, int omode, ulong perm)
{
	USED(c, name, omode, perm);
	error(Eperm);
}

void
etherclose(Chan *c)
{
	if(c->stream)
		streamclose(c);
	netifclose(ether[c->dev], c);
}

long
etherread(Chan *c, void *a, long n, ulong offset)
etherread(Chan *c, void *buf, long n, ulong offset)
{
	return netread(c, a, n, offset, &softctlr[0]->net);
}

long
etherwrite(Chan *c, char *a, long n, ulong offset)
{
	USED(offset);
	return streamwrite(c, a, n, 0);
	return netifread(ether[c->dev], c, buf, n, offset);
}

void
etherremove(Chan *c)
{
	USED(c);
	error(Eperm);
}

void
etherwstat(Chan *c, char *dp)
{
	netwstat(c, dp, &softctlr[0]->net);
	netifwstat(ether[c->dev], c, dp);
}

static int
isobuf(void *arg)
{
	Ctlr *ctlr = arg;

	return ctlr->tb[ctlr->th].owner == Host;
}

static void
etheroput(Queue *q, Block *bp)
long
etherwrite(Chan *c, void *buf, long n, ulong offset)
{
	Ctlr *ctlr;
	Type *type;
	Etherpkt *pkt;
	RingBuf *ring;
	int len, n, s;
	Block *nbp;
	Ether *ctlr;

	type = q->ptr;
	ctlr = type->ctlr;
	if(bp->type == M_CTL){
		qlock(ctlr);
		if(streamparse("connect", bp)){
			if(type->type == -1)
				ctlr->all--;
			type->type = strtol((char*)bp->rptr, 0, 0);
			if(type->type == -1)
				ctlr->all++;
		}
		else if(streamparse("promiscuous", bp)) {
			type->prom = 1;
			ctlr->prom++;
			if(ctlr->prom == 1)
				(*ctlr->card.mode)(ctlr, 1);
		}
		qunlock(ctlr);
		freeb(bp);
		return;
	}
	USED(offset);
	if(n > ETHERMAXTU)
		error(Ebadarg);
	ctlr = ether[c->dev];

	/*
	 * Give packet a local address, return upstream if destined for
	 * this machine.
	 */
	if(BLEN(bp) < ETHERHDRSIZE && (bp = pullup(bp, ETHERHDRSIZE)) == 0)
		return;
	pkt = (Etherpkt*)bp->rptr;
	memmove(pkt->s, ctlr->ea, sizeof(ctlr->ea));
	if(memcmp(ctlr->ea, pkt->d, sizeof(ctlr->ea)) == 0){
		len = blen(bp);
		if(bp = expandb(bp, len >= ETHERMINTU ? len: ETHERMINTU)){
			putq(&ctlr->lbq, bp);
			wakeup(&ctlr->rr);
		}
		return;
	}
	if(memcmp(ctlr->ba, pkt->d, sizeof(ctlr->ba)) == 0 || ctlr->prom || ctlr->all){
		len = blen(bp);
		nbp = copyb(bp, len);
		if(nbp = expandb(nbp, len >= ETHERMINTU ? len: ETHERMINTU)){
			nbp->wptr = nbp->rptr+len;
			putq(&ctlr->lbq, nbp);
			wakeup(&ctlr->rr);
		}
	}
	if(NETTYPE(c->qid.path) != Ndataqid)
		return netifwrite(ctlr, c, buf, n);

	/*
	 * Only one transmitter at a time.
	 */
	qlock(&ctlr->tlock);
	if(waserror()){
		qunlock(&ctlr->tlock);
		freeb(bp);
		nexterror();
	}

	/*
	 * Wait till we get an output buffer.
	 * should try to restart.
	 */
	if(isobuf(ctlr) == 0){
		tsleep(&ctlr->tr, isobuf, ctlr, 3*1000);
		if(isobuf(ctlr) == 0){
			qunlock(&ctlr->tlock);
			freeb(bp);
			poperror();
			return;
		}
	}

	ring = &ctlr->tb[ctlr->th];

	/*
	 * Copy message into buffer.
	 */
	len = 0;
	for(nbp = bp; nbp; nbp = nbp->next){
		if(sizeof(Etherpkt) - len >= (n = BLEN(nbp))){
			memmove(ring->pkt+len, nbp->rptr, n);
			len += n;
		}
		if(bp->flags & S_DELIM)
			break;
	}

	/*
	 * Pad the packet (zero the pad).
	 */
	if(len < ETHERMINTU){
		memset(ring->pkt+len, 0, ETHERMINTU-len);
		len = ETHERMINTU;
	}

	/*
	 * Set up the transmit buffer and 
	 * start the transmission.
	 */
	s = splhi();
	ring->len = len;
	ring->owner = Interface;
	ctlr->th = NEXT(ctlr->th, ctlr->ntb);
	(*ctlr->card.transmit)(ctlr);
	splx(s);

	qunlock(&ctlr->tlock);
	freeb(bp);
	n = (*ctlr->write)(ctlr, buf, n);
	poperror();
}

/*
 * Open an ether line discipline.
 */
static void
etherstopen(Queue *q, Stream *s)
{
	Ctlr *ctlr = softctlr[0];
	Type *type;

	type = &ctlr->type[s->id];
	RD(q)->ptr = WR(q)->ptr = type;
	type->type = 0;
	type->q = RD(q);
	type->inuse = 1;
	type->ctlr = ctlr;
}
	qunlock(&ctlr->tlock);

/*
 * Close ether line discipline.
 *
 * The locking is to synchronize changing the ethertype with
 * sending packets up the stream on interrupts.
 */
static int
isclosed(void *arg)
{
	return ((Type*)arg)->q == 0;
	return n;
}

static void
etherstclose(Queue *q)
etherintr(Ureg *ur)
{
	Type *type = (Type*)(q->ptr);
	Ctlr *ctlr = type->ctlr;

	if(type->prom){
		qlock(ctlr);
		ctlr->prom--;
		if(ctlr->prom == 0)
			(*ctlr->card.mode)(ctlr, 0);
		qunlock(ctlr);
	}
	if(type->type == -1){
		qlock(ctlr);
		ctlr->all--;
		qunlock(ctlr);
	}
	int i, irq;

	/*
	 * Mark as closing and wait for kproc
	 * to close us.
	 * Call all ethernet interrupt routines on this IRQ.
	 * Might be better if setvec() took an argument which
	 * was passed down to the interrupt routine when
	 * called. This would let us easily distinguish multiple
	 * controllers. A hack by any other name...
	 */
	lock(&ctlr->clock);
	type->clist = ctlr->clist;
	ctlr->clist = type;
	unlock(&ctlr->clock);
	wakeup(&ctlr->rr);
	sleep(&type->cr, isclosed, type);

	type->type = 0;
	type->prom = 0;
	type->inuse = 0;
	netdisown(type);
	type->ctlr = 0;
}

static Qinfo info = {
	nullput,
	etheroput,
	etherstopen,
	etherstclose,
	"ether"
};

static int
clonecon(Chan *c)
{
	Ctlr *ctlr = softctlr[0];
	Type *type;

	USED(c);
	for(type = ctlr->type; type < &ctlr->type[NType]; type++){
		qlock(type);
		if(type->inuse || type->q){
			qunlock(type);
			continue;
		}
		type->inuse = 1;
		netown(type, u->p->user, 0);
		qunlock(type);
		return type - ctlr->type;
	irq = ur->trap-Int0vec;
	for(i = 0; i < MaxEther; i++){
		if(ether[i] && ether[i]->irq == irq)
			(*ether[i]->interrupt)(ether[i]);
	}
	exhausted("ether channels");
	return 0;
}

static void
statsfill(Chan *c, char *p, int n)
{
	Ctlr *ctlr = softctlr[0];
	char buf[256];

	USED(c);
	sprint(buf, "in: %d\nout: %d\ncrc errs %d\noverflows: %d\nframe errs %d\nbuff errs: %d\noerrs %d\naddr: %.02x:%.02x:%.02x:%.02x:%.02x:%.02x\n",
		ctlr->inpackets, ctlr->outpackets, ctlr->crcs,
		ctlr->overflows, ctlr->frames, ctlr->buffs, ctlr->oerrs,
		ctlr->ea[0], ctlr->ea[1], ctlr->ea[2],
		ctlr->ea[3], ctlr->ea[4], ctlr->ea[5]);
	strncpy(p, buf, n);
}

static void
typefill(Chan *c, char *p, int n)
{
	char buf[16];
	Type *type;

	type = &softctlr[0]->type[STREAMID(c->qid.path)];
	sprint(buf, "%d", type->type);
	strncpy(p, buf, n);
}

static void
etherup(Ctlr *ctlr, Etherpkt *pkt, int len)
{
	int t;
	Type *type;
	Block *bp;

	t = (pkt->type[0]<<8)|pkt->type[1];
	for(type = &ctlr->type[0]; type < &ctlr->type[NType]; type++){

		/*
		 * Check for open, the right type, and flow control.
		 */
		if(type->q == 0)
			continue;
		if(t != type->type && type->type != -1)
			continue;
		if(type->q->next->len > Streamhi)
			continue;

		/*
		 * Only a trace channel gets packets destined for other machines.
		 */
		if(type->type != -1 && pkt->d[0] != 0xFF
		  && (*pkt->d != *ctlr->ea || memcmp(pkt->d, ctlr->ea, sizeof(pkt->d))))
			continue;

		if(waserror() == 0){
			bp = allocb(len);
			memmove(bp->rptr, pkt, len);
			bp->wptr += len;
			bp->flags |= S_DELIM;
			PUTNEXT(type->q, bp);
			poperror();
		}
	}
}

static int
isinput(void *arg)
{
	Ctlr *ctlr = arg;

	return ctlr->lbq.first || ctlr->rb[ctlr->rh].owner == Host || ctlr->clist;
}

static void
etherkproc(void *arg)
{
	Ctlr *ctlr = arg;
	RingBuf *ring;
	Block *bp;
	Type *type;

	if(waserror()){
		print("%s noted\n", ctlr->name);
		/* fix
		if(ctlr->card.reset)
			(*ctlr->card.reset)(ctlr);
		 */
		ctlr->kproc = 0;
		nexterror();
	}

	for(;;){
		tsleep(&ctlr->rr, isinput, ctlr, 500);
		if(ctlr->card.watch)
			(*ctlr->card.watch)(ctlr);

		/*
		 * Process any internal loopback packets.
		 */
		while(bp = getq(&ctlr->lbq)){
			ctlr->inpackets++;
			etherup(ctlr, (Etherpkt*)bp->rptr, BLEN(bp));
			freeb(bp);
		}

		/*
		 * Process any received packets.
		 */
		while(ctlr->rb[ctlr->rh].owner == Host){
			ctlr->inpackets++;
			ring = &ctlr->rb[ctlr->rh];
			etherup(ctlr, (Etherpkt*)ring->pkt, ring->len);
			ring->owner = Interface;
			ctlr->rh = NEXT(ctlr->rh, ctlr->nrb);
		}

		/*
		 * Close Types requesting it.
		 */
		if(ctlr->clist){
			lock(&ctlr->clock);
			for(type = ctlr->clist; type; type = type->clist){
				type->q = 0;
				wakeup(&type->cr);
			}
			ctlr->clist = 0;
			unlock(&ctlr->clock);
		}
	}
}
#define NCARD 32
static struct {
	char	*type;
	int	(*reset)(Ether*);
} cards[NCARD+1];

static void
etherintr(Ureg *ur)
void
addethercard(char *t, int (*r)(Ether*))
{
	Ctlr *ctlr = softctlr[0];
	static int ncard;

	USED(ur);
	(*ctlr->card.intr)(ctlr);
	if(ncard == NCARD)
		panic("too many ether cards");
	cards[ncard].type = t;
	cards[ncard].reset = r;
	ncard++;
}

void
etherreset(void)
{
	Ctlr *ctlr;
	Card **card;
	int i;
	Ether *ctlr;
	int i, n, ctlrno;
	ulong irqmask;

	if(nctlr >= NCtlr)
		return;
	if(softctlr[nctlr] == 0)
		softctlr[nctlr] = xalloc(sizeof(Ctlr));
	ctlr = softctlr[nctlr];
	for(card = cards; *card; card++){
		memset(ctlr, 0, sizeof(Ctlr));
		ctlr->card = **card;
		if((*ctlr->card.reset)(ctlr) == 0){
			ctlr->present = 1;
	irqmask = 0;
	for(ctlr = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){
		if(ctlr == 0)
			ctlr = malloc(sizeof(Ether));
		memset(ctlr, 0, sizeof(Ether));
		if(isaconfig("ether", ctlrno, ctlr) == 0)
			continue;
		for(n = 0; cards[n].type; n++){
			if(strcmp(cards[n].type, ctlr->type))
				continue;
			if((*cards[n].reset)(ctlr))
				break;

			/*
			 * IRQ2 doesn't really exist, it's used to gang the interrupt
			 * controllers together. A device set to IRQ2 will appear on
			 * the second interrupt controller as IRQ9.
			 * If there are multiple controllers on the same IRQ, only
			 * call setvec() for one of them, etherintr() will scan through
			 * all the controllers looking for those at the IRQ it was
			 * called with. This is a hack, see the comments in etherintr().
			 */
			if(ctlr->card.irq == 2)
				ctlr->card.irq = 9;
			setvec(Int0vec + ctlr->card.irq, etherintr);
			break;
		}
	}
	if(ctlr->present == 0)
		return;

	print("ether%d: %s: I/O addr %lux width %d addr %lux size %d irq %d:",
		nctlr, ctlr->card.id,
		ctlr->card.io, ctlr->card.bit16 ? 16: 8, ctlr->card.ramstart,
		ctlr->card.ramstop-ctlr->card.ramstart, ctlr->card.irq);
	for(i = 0; i < sizeof(ctlr->ea); i++)
		print(" %2.2ux", ctlr->ea[i]);
	print("\n");

	nctlr++;

	if(ctlr->nrb == 0)
		ctlr->nrb = Nrb;
	ctlr->rb = xalloc(sizeof(RingBuf)*ctlr->nrb);
	if(ctlr->ntb == 0)
		ctlr->ntb = Ntb;
	ctlr->tb = xalloc(sizeof(RingBuf)*ctlr->ntb);

	memset(ctlr->ba, 0xFF, sizeof(ctlr->ba));

	ctlr->net.name = "ether";
	ctlr->net.nconv = NType;
	ctlr->net.devp = &info;
	ctlr->net.protop = 0;
	ctlr->net.listen = 0;
	ctlr->net.clone = clonecon;
	ctlr->net.ninfo = 2;
	ctlr->net.info[0].name = "stats";
	ctlr->net.info[0].fill = statsfill;
	ctlr->net.info[1].name = "type";
	ctlr->net.info[1].fill = typefill;
	for(i = 0; i < NType; i++)
		netadd(&ctlr->net, &ctlr->type[i], i);
}

void
etherinit(void)
{
	int ctlrno = 0;
	Ctlr *ctlr = softctlr[ctlrno];
	int i;

	if(ctlr->present == 0)
		return;

	ctlr->rh = 0;
	ctlr->ri = 0;
	for(i = 0; i < ctlr->nrb; i++)
		ctlr->rb[i].owner = Interface;
			if(ctlr->irq == 2)
				ctlr->irq = 9;
			if((irqmask & (1<<ctlr->irq)) == 0){
				setvec(Int0vec+ctlr->irq, etherintr);
				irqmask |= 1<<ctlr->irq;
			}

	ctlr->th = 0;
	ctlr->ti = 0;
	for(i = 0; i < ctlr->ntb; i++)
		ctlr->tb[i].owner = Host;
}
			print("ether%d: %s: port %lux irq %d addr %lux size %d:",
				ctlrno, ctlr->type, ctlr->port, ctlr->irq, ctlr->mem, ctlr->size);
			for(i = 0; i < sizeof(ctlr->ea); i++)
				print(" %2.2ux", ctlr->ea[i]);
			print("\n");

Chan*
etherattach(char *spec)
{
	int ctlrno = 0;
	Ctlr *ctlr = softctlr[ctlrno];
			netifinit(ctlr, "ether", Ntypes, 32*1024);
			ctlr->alen = Eaddrlen;
			memmove(ctlr->addr, ctlr->ea, sizeof(ctlr->ea));
			memmove(ctlr->bcast, etherbcast, sizeof(etherbcast));

	if(ctlr->present == 0)
		error(Enodev);

	/*
	 * Enable the interface
	 * and start the kproc.
	 */	
	(*ctlr->card.attach)(ctlr);
	if(ctlr->kproc == 0){
		sprint(ctlr->name, "ether%dkproc", ctlrno);
		ctlr->kproc = 1;
		kproc(ctlr->name, etherkproc, ctlr);
			ether[ctlrno] = ctlr;
			ctlr = 0;
		}
	}
	return devattach('l', spec);
	if(ctlr)
		free(ctlr);
}

D pc/devincon.c => pc/devincon.c +0 -1140
@@ 1,1140 0,0 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"
#include	"devtab.h"

#include	"io.h"

/* Centronix parallel (printer) port */

typedef struct Lptinfo	Lptinfo;

struct Lptinfo
{
	int	base;		/* port number */
	int	ivec;		/* interrupt number */
};

static Lptinfo lptinfo[] = {
	0x3bc,	Parallelvec,	/* lpt1 (safari) */
	0x378,	Parallelvec,	/* lpt1 (`official') */
	0x278,	Int0vec+5,	/* lpt2 (`official') */
};
#define NDEV	(sizeof lptinfo/sizeof lptinfo[0])

extern int	incondev;	/* from config */

/* offsets, and bits in the registers */
enum
{
	/* data latch register */
	Qdlr=		0x0,
	/* printer status register */
	Qpsr=		0x1,
	Fbusy=		0x80,
	Fintbar=	0x40,
	/* printer control register */
	Qpcr=		0x2,
	Fie=		0x10,
	Fsi=		0x08,
	Finitbar=	0x04,
	Faf=		0x02,
	Fstrobe=	0x01,

	/* other `registers' */
	Qstats=		0x03,
	Qdebug=		0x04
};

/*
 *	af  si  dlr<7:0>
 *	 0   x  xxxxxxxx	wr ctl/data char (si is dcto)
 *	 1   0  xxxxxxxx	wr cmd
 *	 1   1	xxxxxx00	rd ctl/data char
 *	 1   1	xxxxxx01	rd status
 *	 1   1	xxxxxx11	nop (multiplexer to normal state)
 *
 *	 psr<7:3> multiplexed as follows:
 *
 *	 		bc7  bc6  bc5  bc4  bc3
 *	 rd, st- low	bsy   d8   d7   d6   d5
 *	 rd, st- high	 d4   d3   d2   d1   d0
 *	 other		bsy  int-
 */

typedef struct Incon	Incon;

#define NOW (MACHP(0)->ticks*MS2HZ)

#define DPRINT if(incondebug)kprint

static void	inconintr(Ureg *ur);

enum {
	Minstation=	2,	/* lowest station # to poll */
	Maxstation=	15,	/* highest station # to poll */
	Nincon=		1,	/* number of incons */
	Nin=		32,	/* Blocks in the input ring */
	Bsize=		128,	/* size of an input ring block */
	Mfifo=		0xff	/* a mask, must be 2^n-1, must be > Nin */
};

struct Incon {
	QLock;

	QLock	xmit;		/* transmit lock */
	QLock	reslock;	/* reset lock */
	int	dev;		/* base address of port */
	int	station;	/* station number */
	int	state;		/* chip state */
	Rendez	r;		/* output process */
	Rendez	kr;		/* input kernel process */
	ushort	chan;		/* current input channel */
	Queue	*rq;		/* read queue */
	int	kstarted;	/* true if kernel process started */

	/*  input blocks */

	Block	*inb[Nin];
	ushort	wi;		
	ushort	ri;

	/* statistics */

	ulong	overflow;	/* overflow errors */
	ulong	pack0;		/* channel 0 */
	ulong	crc;		/* crc errors */
	ulong	in;		/* bytes in */
	ulong	out;		/* bytes out */
	ulong	wait;		/* wait time in milliseconds */
};

Incon incon[Nincon];

/*
 *  chip state
 */
enum {
	Selecting,
	Selected,
	Notliving,
};

/*
 *  internal chip registers
 */
#define	sel_polln	0
#define	sel_station	1
#define	sel_poll0	2
#define sel_rcv_cnt	3
#define sel_rcv_tim	4
#define sel_tx_cnt	5

/*
 *  CSR bits
 */
#define INCON_RUN	0x80
#define INCON_STOP	0x00
#define ENABLE_IRQ	0x40
#define ENABLE_TX_IRQ	0x20
#define INCON_ALIVE	0x80
#define TX_FULL		0x10
#define TX_EMPTY	0x08
#define RCV_EMPTY	0x04
#define OVERFLOW	0x02
#define CRC_ERROR	0x01

/*
 *  polling constants
 */
#define HT_GNOT	0x30
#define HT_UNIX	0x31
#define ST_UNIX 0x04
#define NCHAN 64

static void inconkproc(void*);

/*
 *  incon stream module definition
 */
static void inconoput(Queue*, Block*);
static void inconstopen(Queue*, Stream*);
static void inconstclose(Queue*);
Qinfo inconinfo =
{
	nullput,
	inconoput,
	inconstopen,
	inconstclose,
	"incon"
};

int incondebug = 0;

Dirtab incondir[]={
	"dlr",		{Qdlr},		1,		0666,
	"psr",		{Qpsr},		5,		0444,
	"pcr",		{Qpcr},		0,		0666,
	"stats",	{Qstats},	0,		0444,
	"debug",	{Qdebug},	0,		0666,
};
#define NINCON	(sizeof incondir/sizeof incondir[0])

static void
nop(void)
{
}

static int
slowinb(int x)
{
	nop();
	nop();
	return inb(x);
}

static void
reset(int dev)				/* hardware reset */
{
	outb(dev+Qpcr, Finitbar);
	outb(dev+Qpcr, 0);
	outb(dev+Qpcr, Finitbar);
}

static void
wrctl(int dev, int data)		/* write control character */
{
	outb(dev+Qpcr, Finitbar);	/* no interrupts, please */
	outb(dev+Qdlr, data);
	outb(dev+Qpcr, Finitbar|Fstrobe);
	outb(dev+Qpcr, Finitbar|Fie);
}

static void
wrdata(int dev, int data)		/* write data character */
{
	outb(dev+Qpcr, Finitbar|Fsi);
	outb(dev+Qdlr, data);
	outb(dev+Qpcr, Finitbar|Fsi|Fstrobe);
	outb(dev+Qpcr, Finitbar|Fsi|Fie);
}

static void
wrcmd(int dev, int data)		/* write command */
{
	outb(dev+Qpcr, Finitbar|Faf);
	outb(dev+Qdlr, data);
	outb(dev+Qpcr, Finitbar|Faf|Fstrobe);
	outb(dev+Qpcr, Finitbar|Faf|Fie);
}

static int
rdstatus(Incon *ip)			/* read status */
{
	int dev, data;

	dev = ip->dev;

	outb(dev+Qpcr, Finitbar|Faf|Fsi);
	outb(dev+Qdlr, 0x01);
	outb(dev+Qpcr, Finitbar|Faf|Fsi|Fstrobe);
	data = (inb(dev+Qpsr)&0xf8)<<2;
	outb(dev+Qpcr, Finitbar|Faf|Fsi);
	data |= inb(dev+Qpsr)>>3;
	if(data&(OVERFLOW|CRC_ERROR)){
		if(data&OVERFLOW)
			ip->overflow++;
		if(data&CRC_ERROR)
			ip->crc++;
	}
	outb(dev+Qdlr, 0x03);
	outb(dev+Qpcr, Finitbar|Faf|Fsi|Fstrobe);
	outb(dev+Qpcr, Finitbar|Faf|Fsi|Fie);

	return data;
}

static void
iwrcmd(int dev, int data)		/* write command at interrupt level */
{
	outb(dev+Qdlr, data);
	outb(dev+Qpcr, Finitbar|Faf|Fstrobe);
	outb(dev+Qpcr, Finitbar|Faf);
}

static int
irdstatus(Incon *ip)			/* read status at interrupt level */
{
	int dev, data;

	dev = ip->dev;

	outb(dev+Qdlr, 0x01);
	outb(dev+Qpcr, Finitbar|Faf|Fsi|Fstrobe);
	data = (slowinb(dev+Qpsr)&0xf8)<<2;
	outb(dev+Qpcr, Finitbar|Faf|Fsi);
	data |= slowinb(dev+Qpsr)>>3;
	if(data&(OVERFLOW|CRC_ERROR)){
		if(data&OVERFLOW)
			ip->overflow++;
		if(data&CRC_ERROR)
			ip->crc++;
	}

	return data;
}

static int
irddata(int dev)			/* read data at interrupt level */
{
	int data;

	outb(dev+Qdlr, 0x00);
	outb(dev+Qpcr, Finitbar|Faf|Fsi|Fstrobe);
	data = (slowinb(dev+Qpsr)&0xf8)<<2;
	outb(dev+Qpcr, Finitbar|Faf|Fsi);
	data |= slowinb(dev+Qpsr)>>3;

	return data;
}

static int
irdnext(int dev)			/* read next data at interrupt level */
{
	int data;

	outb(dev+Qpcr, Finitbar|Faf|Fsi|Fstrobe);
	data = (inb(dev+Qpsr)&0xf8)<<2;
	outb(dev+Qpcr, Finitbar|Faf|Fsi);
	data |= inb(dev+Qpsr)>>3;

	return data;
}

static void
irdnop(int dev, int pcr)		/* read nop (mux reset) */
{
	outb(dev+Qdlr, 0x03);
	outb(dev+Qpcr, Finitbar|Faf|Fsi|Fstrobe);
	outb(dev+Qpcr, pcr);
}

int		Irdnext(int);
#define	irdnext	Irdnext
/**/

/*
 *  set the incon parameters
 */
void
inconset(Incon *ip, int cnt, int del)
{
	int dev;

	if (cnt<1 || cnt>14 || del<1 || del>15)
		error(Ebadarg);

	dev = ip->dev;
	wrcmd(dev, sel_rcv_cnt | INCON_RUN);
	wrdata(dev, cnt);
	wrcmd(dev, sel_rcv_tim | INCON_RUN);
	wrdata(dev, del);
	wrcmd(dev, INCON_RUN | ENABLE_IRQ);
}

/*
 *  parse a set request
 */
void
inconsetctl(Incon *ip, Block *bp)
{
	char *field[3];
	int n;
	int del;
	int cnt;

	del = 15;
	n = getfields((char *)bp->rptr, field, 3, ' ');
	switch(n){
	default:
		freeb(bp);
		error(Ebadarg);
	case 2:
		del = strtol(field[1], 0, 0);
		if(del<0 || del>15){
			freeb(bp);
			error(Ebadarg);
		}
		/* fall through */
	case 1:
		cnt = strtol(field[0], 0, 0);
		if(cnt<0 || cnt>15){
			freeb(bp);
			error(Ebadarg);
		}
	}
	inconset(ip, cnt, del);
	freeb(bp);
}

/*
 *  poll for a station number
 */
void 
inconpoll(Incon *ip, int station)
{
	ulong timer;
	int dev;
	char *p;

	dev = ip->dev;

	/*
	 *  get us to a known state
	 */
	ip->state = Notliving;
	wrcmd(dev, INCON_STOP);

	/*
	 * try a station number
	 */
	wrcmd(dev, sel_station);
	wrdata(dev, station);
	wrcmd(dev, sel_poll0);
	wrdata(dev, HT_GNOT);
	wrcmd(dev, sel_rcv_cnt);
	wrdata(dev, 3);
	wrcmd(dev, sel_rcv_tim);
	wrdata(dev, 15);
	wrcmd(dev, sel_tx_cnt);
	wrdata(dev, 1);
	wrcmd(dev, sel_polln);
	wrdata(dev, 0x00);
	wrdata(dev, ST_UNIX);
	wrdata(dev, NCHAN);
	for(p = "gnot"; *p; p++)
		wrdata(dev, *p);
	wrctl(dev, 0);

	/*
	 *  poll and wait for ready (or 1/4 second)
	 */
	ip->state = Selecting;
	wrcmd(dev, INCON_RUN | ENABLE_IRQ);
	timer = NOW + 250;
	while (NOW < timer) {
		nop();
		if(rdstatus(ip) & INCON_ALIVE){
			ip->station = station;
			ip->state = Selected;
			break;
		}
	}
}

/*
 *  reset the chip and find a new station number
 */
void
inconrestart(Incon *ip)
{
	int i;

	if(!canqlock(&ip->reslock))
		return;

	/*
	 *  poll for incon station numbers
	 */
	DPRINT("inconrestart\n");
	for(i = Minstation; i <= Maxstation; i++){
		inconpoll(ip, i);
		if(ip->state == Selected)
			break;
	}
	switch(ip->state) {
	case Selecting:
		DPRINT("incon[%d] not polled\n", ip-incon);
		print("incon[%d] not polled\n", ip-incon);
		break;
	case Selected:
		DPRINT("incon[%d] station %d\n", ip-incon, ip->station);
		print("incon[%d] station %d\n", ip-incon, ip->station);
		inconset(ip, 3, 15);
		break;
	default:
		DPRINT("incon[%d] bollixed\n", ip-incon);
		print("incon[%d] bollixed\n", ip-incon);
		break;
	}
	qunlock(&ip->reslock);
}

/*
 *  reset all incon chips.
 */

void
inconreset(void)
{
	int i;
	char *p;

	/*
	 *  get incondev from p9rc
 	 */
	p = getconf("incondev");
	if(p)
		incondev = atoi(p);

	/*
	 * state is Selected if we used incon as the boot device
	 * i.e. we've already got a station number.
	 * state is Selecting if this is a first-time open.
	 */
/*	inconset(&incon[0], 3, 15); /**/
	for(i=0; i<Nincon; i++){
		incon[i].dev = lptinfo[incondev].base;
		incon[i].state = Notliving;
		reset(incon[i].dev);
		incon[i].ri = incon[i].wi = 0;
	}
	incon[0].state = Selecting;
	wrcmd(incon[0].dev, INCON_STOP);
}

void
inconinit(void)
{
	wrcmd(incon[0].dev, INCON_STOP);
}

/*
 *  enable the device for interrupts, spec is the device number
 */
Chan*
inconattach(char *spec)
{
	Incon *ip;
	int i;
	Chan *c;

	setvec(lptinfo[incondev].ivec, inconintr);
	i = strtoul(spec, 0, 0);
	if(i >= Nincon)
		error(Ebadarg);
	ip = &incon[i];
	rdstatus(ip);
	if(ip->state != Selected)
		inconrestart(ip);

	c = devattach('i', spec);
	c->dev = i;
	c->qid.path = CHDIR;
	c->qid.vers = 0;
	return c;
}

Chan*
inconclone(Chan *c, Chan *nc)
{
	return devclone(c, nc);
}

int	 
inconwalk(Chan *c, char *name)
{
	return devwalk(c, name, incondir, NINCON, streamgen);
}

void	 
inconstat(Chan *c, char *dp)
{
	devstat(c, dp, incondir, NINCON, streamgen);
}

Chan*
inconopen(Chan *c, int omode)
{
	if(c->qid.path != CHDIR && c->qid.path >= Slowqid)
		streamopen(c, &inconinfo);
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

void	 
inconcreate(Chan *c, char *name, int omode, ulong perm)
{
	USED(c, name, omode, perm);
	error(Eperm);
}

void	 
inconclose(Chan *c)
{
	if(c->qid.path != CHDIR && c->qid.path >= Slowqid)
		streamclose(c);
}

long	 
inconread(Chan *c, void *buf, long n, ulong offset)
{
	char b[256];
	Incon *ip;

	if(c->qid.path == CHDIR)
		return devdirread(c, buf, n, incondir, NINCON, streamgen);
	ip = &incon[c->dev];
	switch(c->qid.path){
	default:
		return streamread(c, buf, n);
	case Qdlr:
	case Qpsr:
	case Qpcr:
		sprint(b, "0x%2.2ux\n", inb(ip->dev + c->qid.path));
		break;
	case Qstats:
		sprint(b, "state: %d\nstation: %d\nin: %d\nout: %d\noverflow: %d\ncrc: %d\nwait: %d\n",
			ip->state, ip->station, ip->in, ip->out, ip->overflow, ip->crc, ip->wait);
		break;
	case Qdebug:
		sprint(b, "%d\n", incondebug);
		break;
	}
	return readstr(offset, buf, n, b);
}

long	 
inconwrite(Chan *c, void *buf, long n, ulong offset)
{
	char b[8];
	Incon *ip;
	int data;

	USED(offset);
	switch(c->qid.path){
	default:
		return streamwrite(c, buf, n, 0);
	case Qdlr:
	case Qpcr:
	case Qdebug:
		break;
	}
	if(n > sizeof b-1)
		n = sizeof b-1;
	memmove(b, buf, n);
	b[n] = 0;
	data = strtoul(b, 0, 0);
	switch(c->qid.path){
	default:
		ip = &incon[c->dev];
		outb(ip->dev + c->qid.path, data);
		break;
	case Qdebug:
		incondebug = data;
		break;
	}
	return n;
}

void	 
inconremove(Chan *c)
{
	USED(c);
	error(Eperm);
}

void	 
inconwstat(Chan *c, char *dp)
{
	USED(c, dp);
	error(Eperm);
}

/*
 *	the stream routines
 */

/*
 *  kill off the kernel process
 */
static int
kNotliving(void *arg)
{
	Incon *ip;

	ip = (Incon *)arg;
	return ip->kstarted == 0;
}

static void
inconstclose(Queue * q)
{
	Incon *ip;

	ip = (Incon *)q->ptr;
	qlock(ip);
	ip->rq = 0;
	qunlock(ip);
	wakeup(&ip->kr);
	sleep(&ip->r, kNotliving, ip);
}

/*
 *  create the kernel process for input
 *  first wait for any old ones to die
 */
static void
inconstopen(Queue *q, Stream *s)
{
	Incon *ip;
	char name[32];

	ip = &incon[s->dev];
	sprint(name, "incon%d", s->dev);
	q->ptr = q->other->ptr = ip;
	wakeup(&ip->kr);
	sleep(&ip->r, kNotliving, ip);
	ip->rq = q;
	kproc(name, inconkproc, ip);
}

/*
 *  free all blocks of a message in `q', `bp' is the first block
 *  of the message
 */
static void
freemsg(Queue *q, Block *bp)
{
	for(; bp; bp = getq(q)){
		if(bp->flags & S_DELIM){
			freeb(bp);
			return;
		}
		freeb(bp);
	}
}

static void
showpkt(char *str, int chan, int ctl, Block *bp, int i)
{
	int n;

	n = bp->wptr - bp->rptr;
	kprint("%s(%d)%uo %d", str, chan, ctl, n);
	if(n > 8)
		n = 8;
	for(; i<n; i++)
		kprint(" %2.2ux", bp->rptr[i]);
	kprint("\n");
}

/*
 *  output a block
 *
 *  the first 2 bytes of every message are the channel number,
 *  low order byte first.  the third is a possible trailing control
 *  character.
 */
void
inconoput(Queue *q, Block *bp)
{
	int dev;
	Incon *ip;
	ulong end;
	int status, chan, ctl;
	int n, size;

	ip = (Incon *)q->ptr;

	if(bp->type != M_DATA){
		if(streamparse("inconset", bp))
			inconsetctl(ip, bp);
		else
			freeb(bp);
		return;
	}
	if(BLEN(bp) < 3){
		bp = pullup(bp, 3);
		if(bp == 0){
			print("inconoput pullup failed\n");
			return;
		}
	}

	/*
	 *  get a whole message before handing bytes to the device
	 */
	if(!putq(q, bp))
		return;

	/*
	 *  one transmitter at a time
	 */
	qlock(&ip->xmit);
	dev = ip->dev;

	/*
	 *  parse message
	 */
	bp = getq(q);
	chan = bp->rptr[0] | (bp->rptr[1]<<8);
	ctl = bp->rptr[2];
	bp->rptr += 3;
	if(chan<=0)
		print("bad channel %d\n", chan);

	if(incondebug){
		kprint("0x%.2ux ", rdstatus(ip));
		showpkt("->", chan, ctl, bp, 0);
	}

	/*
	 *  make sure there's an incon out there
	 */
	if(!(rdstatus(ip)&INCON_ALIVE) || ip->state==Notliving){
		DPRINT("inconoput: not ready");
		inconrestart(ip);
		freemsg(q, bp);
		qunlock(&ip->xmit);
		return;
	}

	/*
	 *  send the 8 bit data
	 */
	for(;;){
		/*
		 *  spin till there is room
		 */
		for(n=0, end = NOW+1000; (status=rdstatus(ip)) & TX_FULL; n++){
			nop();	/* make sure we don't optimize too much */
			if(NOW > end){
				print("incon output stuck 0 %.2ux\n", status);
				freemsg(q, bp);
				qunlock(&ip->xmit);
				return;
			}
		}
		ip->wait = (n + ip->wait)>>1;

		/*
		 *  put in next packet
		 */
		n = bp->wptr - bp->rptr;
		if(n > 16)
			n = 16;
		size = n;
		wrctl(dev, chan);
		ip->out += n;
		while(n--){
			wrdata(dev, *bp->rptr++);
		}

		/*
		 *  get next block 
		 */
		if(bp->rptr >= bp->wptr){
			if(bp->flags & S_DELIM){
				freeb(bp);
				break;
			}
			freeb(bp);
			bp = getq(q);
			if(bp==0)
				break;
		}

		/*
		 *  end packet
		 */
		wrctl(dev, 0);
	}

	/*
	 *  send the control byte if there is one
	 */
	if(ctl){
		if(size >= 16){
			wrctl(dev, 0);
			for(end = NOW+1000; rdstatus(ip) & TX_FULL;){
				nop();	/* make sure we don't optimize too much */
				if(NOW > end){
					print("incon output stuck 1\n");
					freemsg(q, bp);
					qunlock(&ip->xmit);
					return;
				}
			}
			wrctl(dev, chan);
		}
		if(rdstatus(ip) & TX_FULL)
			print("inconfull\n");
		ip->out += 1;
		wrctl(dev, ctl);
	}
	wrctl(dev, 0);

	qunlock(&ip->xmit);
	return;
}

/*
 *  return true if the raw fifo is non-empty
 */
static int
notempty(void *arg)
{
	Incon *ip;

	ip = (Incon *)arg;
	return ip->ri!=ip->wi;
}

/*
 *  Read bytes from the raw input circular buffer.
 */
static void
inconkproc(void *arg)
{
	Incon *ip;
	Block *bp;
	int i;
	int locked;

	ip = (Incon *)arg;
	ip->kstarted = 1;

	/*
	 *  create a number of blocks for input
	 */
	for(i = 0; i < Nin; i++){
		bp = ip->inb[i] = allocb(Bsize);
		bp->wptr += 3;
	}

	locked = 0;
	if(waserror()){
		if(locked)
			qunlock(ip);
		ip->kstarted = 0;
		wakeup(&ip->r);
		return;
	}

	for(;;){
		/*
		 *  sleep if input fifo empty
		 */
		sleep(&ip->kr, notempty, ip);

		/*
		 *  die if the device is closed
		 */
		USED(locked);
		qlock(ip);
		locked = 1;
		if(ip->rq == 0){
			qunlock(ip);
			ip->kstarted = 0;
			wakeup(&ip->r);
			poperror();
			return;
		}

		/*
		 *  send blocks upstream and stage new blocks.
		 */
		while(ip->ri != ip->wi){
			bp = ip->inb[ip->ri];
			bp->flags |= S_DELIM;
			ip->in += BLEN(bp);
			PUTNEXT(ip->rq, bp);
			bp = ip->inb[ip->ri] = allocb(Bsize);
			bp->wptr += 3;
			ip->ri = (ip->ri+1)%Nin;
		}
		USED(locked);
		qunlock(ip);
		locked = 0;
	}
}

/*
 *  drop a single packet
 */
static void
droppacket(int dev)
{
	if(irddata(dev) == 0)
		return;
	while(irdnext(dev))
		;
}

/*
 *  flush the input fifo
 */
static void
flushfifo(Incon *ip)
{
	while(!(irdstatus(ip) & RCV_EMPTY))
		droppacket(ip->dev);
}

/*
 *  advance the queue. if we've run out of staged input blocks,
 *  drop the packet and return 0.  otherwise return the next input
 *  block to fill.
 */
static Block *
nextin(Incon *ip, unsigned int c)
{
	Block *bp;
	int next;

	bp = ip->inb[ip->wi];
	bp->rptr[0] = ip->chan;
	bp->rptr[1] = ip->chan>>8;
	bp->rptr[2] = c;
	if(incondebug)
		showpkt("<-", ip->chan, c, bp, 3);

	next = (ip->wi+1)%Nin;
	if(next == ip->ri){
		bp->wptr = bp->rptr+3;
		return bp;
	}
	ip->wi = next;

	return ip->inb[ip->wi];
}

/*
 *  read the packets from the device into the staged input blocks.
 *  we have to do this at interrupt tevel to turn off the interrupts.
 */
static void
rdpackets(Incon *ip)
{
	Block *bp;
	unsigned int c;
	int dev;
	uchar *p;
	int first = ip->wi;

	dev = ip->dev;
	bp = ip->inb[ip->wi];
	if(bp==0){
		flushfifo(ip);
		return;
	}
	p = bp->wptr;
	while(!(irdstatus(ip) & RCV_EMPTY)){
		/*
		 *  get channel number
		 */
		c = irddata(dev);
		if(c == 0){
			droppacket(dev);
			continue;
		}
		if(ip->chan != c){
			if(p - bp->rptr > 3){
				bp->wptr = p;
				bp = nextin(ip, 0);
				p = bp->wptr;
			}
			ip->chan = c;
		}

		/*
		 *  null byte marks end of packet
		 */
		while(c = irdnext(dev)){	/* assign = */
			if((c & 0x100) == 0){
				/*
				 *  control byte ends block
				 */
				bp->wptr = p;
				bp = nextin(ip, c);
				p = bp->wptr;
			}else{
				/*
				 *  data byte, put in local buffer
				 */
				*p++ = c;
			}
		}

		/*
		 *  pass a block on if it doesn't have room for one more
		 *  packet.  this way we don't have to check per byte.
		 */
		if(p + 16 > bp->lim){
			bp->wptr = p;
			bp = nextin(ip, 0);
			p = bp->wptr;
		}
	}	
	bp->wptr = p;
	if(bp->wptr != bp->rptr+3)
		nextin(ip, 0);

	if(first != ip->wi)/**/
		wakeup(&ip->kr);
}

/*
 *  Receive an incon interrupt.  One entry point
 *  for all types of interrupt.  Until we figure out
 *  how to use more than one incon, this routine only
 *  is for incon[0].
 */
static void
inconintr(Ureg *ur)
{
	uchar status;
	int pcr;
	Incon *ip;

	USED(ur);
	ip = &incon[0];
	pcr = inb(ip->dev+Qpcr);
	if(!(pcr & Fie))
		return;
	status = irdstatus(ip);
	if(incondebug){
		kprint("pcr=%2.2x status=%2.2x\n", pcr, status);
		if(pcr & Fstrobe)
			kprint("YIKES!!\n");
	}
	if(!(status & RCV_EMPTY))
		rdpackets(ip);

	/* see if it died underneath us */
	if(!(status&INCON_ALIVE)){
		switch(ip->state){
		case Selected:
			iwrcmd(ip->dev, INCON_STOP);
			DPRINT("Incon died\n");
			print("Incon died\n");
			break;
		case Selecting:
			DPRINT("rejected\n");
			print("rejected\n");
			break;
		default:
			iwrcmd(ip->dev, INCON_STOP);
			DPRINT("state was %d\n", ip->state);
			break;
		}
		ip->state = Notliving;
	}
	irdnop(ip->dev, pcr);
}

D pc/ether.h => pc/ether.h +0 -169
@@ 1,169 0,0 @@
/*
 * All the goo for PC ethernet cards.
 */
typedef struct Card Card;
typedef struct RingBuf RingBuf;
typedef struct Type Type;
typedef struct Ctlr Ctlr;

/*
 * Hardware interface.
 */
struct Card {
	ISAConf;

	int	(*reset)(Ctlr*);
	void	(*init)(Ctlr*);
	void	(*attach)(Ctlr*);
	void	(*mode)(Ctlr*, int);

	void	*(*read)(Ctlr*, void*, ulong, ulong);
	void	*(*write)(Ctlr*, ulong, void*, ulong);

	void	(*receive)(Ctlr*);
	void	(*transmit)(Ctlr*);
	void	(*intr)(Ctlr*);
	void	(*watch)(Ctlr*);
	void	(*overflow)(Ctlr*);

	uchar	bit16;			/* true if a 16 bit interface */
	uchar	ram;			/* true if card has shared memory */

	ulong	dp8390;			/* I/O address of 8390 (if any) */
	ulong	data;			/* I/O data port if no shared memory */
	uchar	nxtpkt;			/* software bndry */
	uchar	tstart;			/* 8390 ring addresses */
	uchar	pstart;
	uchar	pstop;
};

/*
 * Software ring buffer.
 */
struct RingBuf {
	uchar	owner;
	uchar	busy;			/* unused */
	ushort	len;
	uchar	pkt[sizeof(Etherpkt)];
};

enum {
	Host		= 0,		/* buffer owned by host */
	Interface	= 1,		/* buffer owned by card */

	Nrb		= 16,		/* default number of receive buffers */
	Ntb		= 4,		/* default number of transmit buffers */
};

/*
 * One per ethernet packet type.
 */
struct Type {
	QLock;
	Netprot;			/* stat info */
	int	type;			/* ethernet type */
	int	prom;			/* promiscuous mode */
	Queue	*q;
	int	inuse;
	Ctlr	*ctlr;

	Rendez	cr;			/* rendezvous for close */
	Type	*clist;			/* close list */
};

enum {
	NType		= 9,		/* types/card */
};

/*
 * Software controller.
 */
struct Ctlr {
	QLock;
	int	ctlrno;
	Ctlr	*next;

	Card	card;			/* hardware info */
	int	present;
	int	debug;

	ushort	nrb;			/* number of software receive buffers */
	ushort	ntb;			/* number of software transmit buffers */
	RingBuf *rb;			/* software receive buffers */
	RingBuf *tb;			/* software transmit buffers */

	uchar	ea[6];			/* ethernet address */
	uchar	ba[6];			/* broadcast address */

	Rendez	rr;			/* rendezvous for a receive buffer */
	ushort	rh;			/* first receive buffer belonging to host */
	ushort	ri;			/* first receive buffer belonging to card */	

	Rendez	tr;			/* rendezvous for a transmit buffer */
	QLock	tlock;			/* semaphore on th */
	ushort	th;			/* first transmit buffer belonging to host */	
	ushort	ti;			/* first transmit buffer belonging to card */
	int	tbusy;			/* transmitter is busy */	

	Type	type[NType];
	int	all;			/* number of channels listening to all packets */
	Type	*clist;			/* channels waiting to close */
	Lock	clock;			/* lock for clist */
	int	prom;			/* number of promiscuous channels */
	int	kproc;			/* true if kproc started */
	char	name[NAMELEN];		/* name of kproc */
	Network	net;

	Queue	lbq;			/* software loopback packet queue */

	int	inpackets;
	int	outpackets;
	int	crcs;			/* input crc errors */
	int	oerrs;			/* output errors */
	int	frames;			/* framing errors */
	int	overflows;		/* packet overflows */
	int	buffs;			/* buffering errors */
};

#define NEXT(x, l)	(((x)+1)%(l))
#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))

/*
 * The Western Digital 8003 and 8013 series, the NE2000
 * and the 3Com 503 all use the DP8390 Network Interface
 * Chip.
 */
extern void dp8390reset(Ctlr*);
extern void dp8390attach(Ctlr*);
extern void dp8390mode(Ctlr*, int);
extern void dp8390getea(Ctlr*);
extern void dp8390setea(Ctlr*);
extern void *dp8390read(Ctlr*, void*, ulong, ulong);
extern void *dp8390write(Ctlr*, ulong, void*, ulong);
extern void dp8390receive(Ctlr*);
extern void dp8390transmit(Ctlr*);
extern void dp8390intr(Ctlr*);
extern void dp8390watch(Ctlr*);
extern void dp8390overflow(Ctlr*);

extern void dp8390debug(Ctlr*);

/*
 * The DP8390 needs some time between successive 
 * chip selects, so we need special I/O routines.
 * See l.s.
 * These routines only need to be used for accessing
 * the chip registers. Data I/O, either through
 * remote DMA or shared memory, can use the normal
 * routines.
 */
extern int dp8390inb(ulong);
extern void dp8390outb(ulong, uchar);

enum {
	Dp8390BufSz	= 256,		/* hardware ring buffer size */
};


extern void	addethercard(char*, int	(*)(Ctlr*));

D pc/ether503.c => pc/ether503.c +0 -346
@@ 1,346 0,0 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
#include "devtab.h"

#include "ether.h"

/*
 * 3Com 503 (EtherLink II).
 * This driver doesn't work under load, the card gets blasted by
 * overwrite-warning interrupts and eventually just hangs. Always clearing
 * the Ovw bit in the DP8390 Isr along with the Rxe and Prx bits stops
 * the overwrite-warning interrupts, but the card eventually hangs anyway.
 * I really don't understand why this card doesn't work,
 * there is plenty of evidence that other systems manage to use it.
 * The only good thing about this driver is that both shared
 * memory and polled I/O work (given the above restraints), although
 * the polled I/O should be tuned.
 */
enum {					/* Gate Array Registers */
	Pstr		= 0x400,	/* Page Start Register */
	Pspr		= 0x401,	/* Page Stop Register */
	Dqtr		= 0x402,	/* Drq Timer Register */
	Bcfr		= 0x403,	/* Base Configuration Register */
	Pcfr		= 0x404,	/* EPROM Configuration Register */
	Gacfr		= 0x405,	/* GA Configuration Register */
	Ctrl		= 0x406,	/* Control Register */
	Streg		= 0x407,	/* Status Register */
	Idcfr		= 0x408,	/* Interrupt/DMA Configuration Register */
	Damsb		= 0x409,	/* DMA Address Register MSB */
	Dalsb		= 0x40A,	/* DMA Address Register LSB */
	Vptr2		= 0x40B,	/* Vector Pointer Register 2 */
	Vptr1		= 0x40C,	/* Vector Pointer Register 1 */
	Vptr0		= 0x40D,	/* Vector Pointer Register 0 */
	Rfmsb		= 0x40E,	/* Register File Access MSB */
	Rflsb		= 0x40F,	/* Register File Access LSB */
};

enum {					/* Pcfr */
	C8xxx		= 0x10,		/* memory address 0xC8000 */
	CCxxx		= 0x20,		/* memory address 0xCC000 */
	D8xxx		= 0x40,		/* memory address 0xD8000 */
	DCxxx		= 0x80,		/* memory address 0xDC000 */
};

enum {					/* Gacfr */
	Mbs0		= 0x01,		/* memory bank select 0 */
	Mbs1		= 0x02,		/* memory bank select 1 */
	Mbs2		= 0x04,		/* memory bank select 2 */
	Rsel		= 0x08,		/* RAM select */
	Test		= 0x10,		/* test */
	Ows		= 0x20,		/* 0 wait state */
	Tcm		= 0x40,		/* terminal count mask */
	Nim		= 0x80,		/* MIC interrupt mask */
};

enum {					/* Ctrl */
	Rst		= 0x01,		/* software reset */
	Xsel		= 0x02,		/* transceiver select (BNC = 1, AUI = 0) */
	Ealo		= 0x04,		/* ethernet address low */
	Eahi		= 0x08,		/* ethernet address high */
	Share		= 0x10,		/* interrupt share */
	Dbsel		= 0x20,		/* double buffer select */
	Ddir		= 0x40,		/* DMA direction */
	Start		= 0x80,		/* DMA start */
};

enum {					/* Streg */
	Rev0		= 0x01,		/* gate array revision bit 0 */
	Rev1		= 0x02,		/* gate array revision bit 1 */
	Rev2		= 0x04,		/* gate array revision bit 2 */
	Dip		= 0x08,		/* DMA in progress */
	Dtc		= 0x10,		/* DMA terminal count */
	Oflw		= 0x20,		/* overflow */
	Uflw		= 0x40,		/* underflow */
	Dprdy		= 0x80,		/* data port ready */
};

enum {					/* Idcfr */
	Drq1		= 0x01,		/* DMA request 1 */
	Drq2		= 0x02,		/* DMA request 2 */
	Drq3		= 0x04,		/* DMA request 3 */
	Irq2		= 0x10,		/* interrupt request 2 */
	Irq3		= 0x20,		/* interrupt request 3 */
	Irq4		= 0x40,		/* interrupt request 4 */
	Irq5		= 0x80,		/* interrupt request 5 */
};

enum {
	RamOffset	= 0x2000,	/* where the card sees the shared memory */
};

typedef struct {
	ulong	io;			/* I/O base address */
	uchar	irq;			/* interrupt level */
	uchar	bnc;			/* use BNC connector */
} Table;

/*
 * The only configuration info we can get from the card is the
 * I/O base address and the shared-memory address. So let's make
 * a little hard-wired configuration table.
 */
static Table table[] = {
	{ 0x300, Irq2, 0, },		/* external transceiver */
	{ 0x310, Irq3, 0, },
	{ 0x330, Irq4, 0, },
	{ 0x350, Irq5, 0, },

	{ 0x250, Irq2, Xsel, },		/* BNC */
	{ 0x280, Irq3, Xsel, },
	{ 0x2A0, Irq4, Xsel, },
	{ 0x2E0, Irq5, Xsel, },

	{ 0 },
};


static int
reset(Ctlr *ctlr)
{
	Table *tp;
	int i;
	uchar cfr;

	/*
	 * The Gate Array Ctrl register bits <3,2> determine what is
	 * mapped at the I/O base address. The options are the DP8390,
	 * PROM bytes 0-15 or PROM bytes 16-31.
	 * At power-on, the I/O base address contains PROM bytes 16-31.
	 */
	for(tp = table; ctlr->card.io = tp->io; tp++){
		/*
		 * Toggle the software reset bit, this should initialise the
		 * Ctrl register to 0x0A (Eahi|Xsel).
		 * Hopefully there is no other card at the gate-array address.
		 */
		outb(ctlr->card.io+Ctrl, Rst);
		delay(1);
		outb(ctlr->card.io+Ctrl, 0);
		if(inb(ctlr->card.io+Ctrl) == (Eahi|Xsel))
			break;
	}
	if(ctlr->card.io == 0)
		return -1;

	/*
	 * Map PROM bytes 0-15 to the I/O base address
	 * and read the ethernet address.
	 * When done, map the DP8390 at the I/O base address
	 * and set the transceiver type and irq.
	 */
	outb(ctlr->card.io+Ctrl, Ealo|Xsel);
	for(i = 0; i < sizeof(ctlr->ea); i++)
		ctlr->ea[i] = inb(ctlr->card.io+i);

	outb(ctlr->card.io+Gacfr, Nim|Tcm);

	outb(ctlr->card.io+Ctrl, 0|tp->bnc);
	outb(ctlr->card.io+Idcfr, tp->irq);

	if(tp->irq == Irq2)
		ctlr->card.irq = 2;
	else if(tp->irq == Irq3)
		ctlr->card.irq = 3;
	else if(tp->irq == Irq4)
		ctlr->card.irq = 4;
	else if(tp->irq == Irq5)
		ctlr->card.irq = 5;
	else
		return -1;

	/*
	 * Try to configure the shared memory.
	 * Assume this is an 8K card.
	 */
	ctlr->card.ram = 1;
	cfr = inb(ctlr->card.io+Pcfr);
	if(cfr & C8xxx)
		ctlr->card.ramstart = KZERO|0xC8000;
	else if(cfr & CCxxx)
		ctlr->card.ramstart = KZERO|0xCC000;
	else if(cfr & D8xxx)
		ctlr->card.ramstart = KZERO|0xD8000;
	else if(cfr & DCxxx)
		ctlr->card.ramstart = KZERO|0xDC000;
	else
		ctlr->card.ram = 0;

	ctlr->card.ramstop = ctlr->card.ramstart + 8*1024;

	/*
	 * Finish initialising the gate-array.
	 * The Pstr and Pspr registers must be the same as those
	 * given to the DP8390.
	 * Gacfr is set to enable shared memory and block DMA
	 * interrupts.
	 */
	outb(ctlr->card.io+Pstr, ctlr->card.pstart);
	outb(ctlr->card.io+Pspr, ctlr->card.pstop);

	outb(ctlr->card.io+Dqtr, 8);
	outb(ctlr->card.io+Damsb, ctlr->card.tstart);
	outb(ctlr->card.io+Dalsb, 0);

	outb(ctlr->card.io+Vptr2, 0xFF);
	outb(ctlr->card.io+Vptr1, 0xFF);
	outb(ctlr->card.io+Vptr0, 0x00);

	/*
	 * Finally, init the 8390, set the ethernet address
	 * and turn on the shared memory if any.
	 */
	ctlr->card.dp8390 = ctlr->card.io;
	dp8390reset(ctlr);
	dp8390setea(ctlr);

	if(ctlr->card.ram)
		outb(ctlr->card.io+Gacfr, Tcm|Rsel|Mbs0);
	else
		outb(ctlr->card.io+Gacfr, Tcm);

	return 0;
}

static void*
read(Ctlr *ctlr, void *to, ulong from, ulong len)
{
	uchar *addr, ctrl;

	/*
	 * If the interface has shared memory, just do a memmove.
	 * In this case, 'from' is an index into the shared memory.
	 * Due to 'hardware considerations', the memory is seen by
	 * the adapter at RamOffset, so we have to adjust.
	 */
	if(ctlr->card.ram)
		return memmove(to, (void*)(ctlr->card.ramstart+from-RamOffset), len);

	/*
	 * Polled I/O. This should be tuned.
	 * Set up the dma registers and pump the fifo.
	 */
	outb(ctlr->card.io+Dalsb, from & 0xFF);
	outb(ctlr->card.io+Damsb, (from>>8) & 0xFF);
	ctrl = inb(ctlr->card.io+Ctrl);
	outb(ctlr->card.io+Ctrl, Start|ctrl);
	addr = to;
	while(len){
		/*
		 * The fifo is 8 bytes deep, we have to wait
		 * until it's ready before stuffing it.
		 * The fifo is in its default configuration,
		 * so we can do word accesses.
		 */
		while((inb(ctlr->card.io+Streg) & Dprdy) == 0)
			;
		if(len >= 8){
			inss(ctlr->card.io+Rfmsb, addr, 4);
			addr += 8;
			len -= 8;
		}
		else{
			insb(ctlr->card.io+Rfmsb, addr, len);
			break;
		}
	}
	outb(ctlr->card.io+Ctrl, ctrl);
	return to;
}

static void*
write(Ctlr *ctlr, ulong to, void *from, ulong len)
{
	uchar *addr, ctrl;

	/*
	 * See the comments in 'read()' above.
	 */
	if(ctlr->card.ram)
		return memmove((void*)(ctlr->card.ramstart+to-RamOffset), from, len);

	outb(ctlr->card.io+Dalsb, to & 0xFF);
	outb(ctlr->card.io+Damsb, (to>>8) & 0xFF);
	ctrl = inb(ctlr->card.io+Ctrl);
	outb(ctlr->card.io+Ctrl, Start|Ddir|ctrl);
	addr = from;
	while(len){
		while((inb(ctlr->card.io+Streg) & Dprdy) == 0)
			;
		if(len >= 8){
			outss(ctlr->card.io+Rfmsb, addr, 4);
			addr += 8;
			len -= 8;
		}
		else{
			outsb(ctlr->card.io+Rfmsb, addr, len);
			break;
		}
	}
	outb(ctlr->card.io+Ctrl, ctrl);
	return (void*)to;
}

Card ether503 = {
	"3Com503",			/* ident */

	reset,				/* reset */
	0,				/* init */
	dp8390attach,			/* attach */
	dp8390mode,			/* mode */

	read,				/* read */
	write,				/* write */

	dp8390receive,			/* receive */
	dp8390transmit,			/* transmit */
	dp8390intr,			/* interrupt */
	dp8390watch,			/* watch */
	dp8390overflow,			/* overflow */

	0,				/* io */
	0,				/* irq */
	0,				/* bit16 */

	0,				/* ram */
	0,				/* ramstart */
	0,				/* ramstop */

	0,				/* dp8390 */
	0,				/* data */
	0,				/* software bndry */
	RamOffset/Dp8390BufSz,		/* tstart */
	(RamOffset+0x600)/Dp8390BufSz,	/* pstart */
	(RamOffset+8*1024)/Dp8390BufSz,	/* pstop */
};

void
ether503link(void)
{
	addethercard("3C503",  ccc503reset);
}

M pc/ether509.c => pc/ether509.c +112 -96
@@ 3,11 3,11 @@
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
#include "devtab.h"
#include "../port/error.h"
#include "../port/netif.h"

#include "ether.h"
#include "etherif.h"

enum {
	IDport		= 0x0100,	/* anywhere between 0x0100 and 0x01F0 */


@@ 87,11 87,11 @@ enum {
#define COMMAND(port, cmd, a)	outs(port+Command, ((cmd)<<11)|(a))

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

	port = ctlr->card.port;
	port = ether->port;
	/*
	 * Set the receiver packet filter for our own and
	 * and broadcast addresses, set the interrupt masks


@@ 108,11 108,11 @@ attach(Ctlr *ctlr)
}

static void
mode(Ctlr *ctlr, int on)
promiscuous(void *arg, int on)
{
	ulong port;

	port = ctlr->card.port;
	port = ((Ether*)arg)->port;
	if(on)
		COMMAND(port, SetRxFilter, Promiscuous|Broadcast|MyEtherAddr);
	else


@@ 120,14 120,14 @@ mode(Ctlr *ctlr, int on)
}

static void
receive(Ctlr *ctlr)
receive(Ether *ether)
{
	ushort status;
	RingBuf *rb;
	ushort status, type;
	int len;
	ulong port;
	Netfile *f, **fp, **ep;

	port = ctlr->card.port;
	port = ether->port;
	while(((status = ins(port+RxStatus)) & RxEmpty) == 0){
		/*
		 * If we had an error, log it and continue


@@ 137,47 137,44 @@ receive(Ctlr *ctlr)
			switch(status & RxErrMask){

			case RxErrOverrun:	/* Overrrun */
				ctlr->overflows++;
				ether->overflows++;
				break;

			case RxErrOversize:	/* Oversize Packet (>1514) */
			case RxErrRunt:		/* Runt Packet */
				ctlr->buffs++;
				ether->buffs++;
				break;
			case RxErrFraming:	/* Alignment (Framing) */
				ctlr->frames++;
				ether->frames++;
				break;

			case RxErrCRC:		/* CRC */
				ctlr->crcs++;
				ether->crcs++;
				break;
			}
		}
		else {
			/*
			 * We have a packet. Read it into the next
			 * free ring buffer, if any.
			 * The CRC is already stripped off.
			 * We have a packet. Read it into the
			 * buffer. The CRC is already stripped off.
			 * Must read len bytes padded to a
			 * doubleword. We can pick them out 16-bits
			 * at a time (can try 32-bits at a time
			 * later).
			insl(port+Fifo, ether->rpkt, HOWMANY(len, 4));
			 */
			len = (status & RxByteMask);
			inss(port+Fifo, &ether->rpkt, HOWMANY(len, 2));

			/*
			 * Copy the packet to whoever wants it.
			 */
			rb = &ctlr->rb[ctlr->ri];
			if(rb->owner == Interface){
				len = (status & RxByteMask);
				rb->len = len;
	
				/*
				 * Must read len bytes padded to a
				 * doubleword. We can pick them out 16-bits
				 * at a time (can try 32-bits at a time
				 * later).
				insl(port+Fifo, rb->pkt, HOWMANY(len, 4));
				 */
				inss(port+Fifo, rb->pkt, HOWMANY(len, 2));

				/*
				 * Update the ring.
				 */
				rb->owner = Host;
				ctlr->ri = NEXT(ctlr->ri, ctlr->nrb);
			type = (ether->rpkt.type[0]<<8)|ether->rpkt.type[1];
			ep = &ether->f[Ntypes];
			for(fp = ether->f; fp < ep; fp++) {
				f = *fp;
				if(f && (f->type == type || f->type < 0))
					qproduce(f->in, &ether->rpkt, len);
			}
		}



@@ 191,46 188,69 @@ receive(Ctlr *ctlr)
	}
}

static void
transmit(Ctlr *ctlr)
static int
istxfifo(void *arg)
{
	RingBuf *tb;
	Ether *ether;
	ushort len;
	ulong port;

	port = ctlr->card.port;
	for(tb = &ctlr->tb[ctlr->ti]; tb->owner == Interface; tb = &ctlr->tb[ctlr->ti]){
		/*
		 * If there's no room in the FIFO for this packet,
		 * set up an interrupt for when space becomes available.
		 * Output packet must be a multiple of 4 in length and
		 * we need 4 bytes for the preamble.
		 */
		len = ROUNDUP(tb->len, 4);
		if(len+4 > ins(port+TxFreeBytes)){
			COMMAND(port, SetTxAvailable, len);
			break;
		}
	ether = arg;
	port = ether->port;
	/*
	 * If there's no room in the FIFO for this packet,
	 * set up an interrupt for when space becomes available.
	 * Output packet must be a multiple of 4 in length and
	 * we need 4 bytes for the preamble.
	 * Assume here that when we are called (via tsleep) that
	 * we are safe from interrupts.
	 */
	len = ROUNDUP(ether->tlen, 4);
	if(len+4 > ins(port+TxFreeBytes)){
		COMMAND(port, SetTxAvailable, len);
		return 0;
	}
	return 1;
}

		/*
		 * There's room, copy the packet to the FIFO and free
		 * the buffer back to the host.
		 */
		outs(port+Fifo, tb->len);
		outs(port+Fifo, 0);
		outss(port+Fifo, tb->pkt, len/2);
		tb->owner = Host;
		ctlr->ti = NEXT(ctlr->ti, ctlr->ntb);
static long
write(Ether *ether, void *buf, long n)
{
	ushort len;
	ulong port;

print("W|");
	port = ether->port;
	ether->tlen = n;
	len = ROUNDUP(ether->tlen, 4);
	tsleep(&ether->tr, istxfifo, ether, 10000);
	if(len+4 > ins(port+TxFreeBytes)){
		print("ether509: transmitter jammed\n");
		return 0;
	}
	/*
	 * We know there's room, copy the packet to the FIFO.
	 * To save copying the packet into a local buffer just
	 * so we can set the source address, stuff the packet
	 * into the FIFO in 3 pieces.
	 * Transmission won't start until the entire packet is
	 * in the FIFO, so it's OK to fault here.
	 */
	outs(port+Fifo, ether->tlen);
	outs(port+Fifo, 0);
	outss(port+Fifo, buf, Eaddrlen/2);
	outss(port+Fifo, ether->ea, Eaddrlen/2);
	outss(port+Fifo, (uchar*)buf+2*Eaddrlen, (len-2*Eaddrlen)/2);
	return n;
}

static ushort
getdiag(Ctlr *ctlr)
getdiag(Ether *ether)
{
	ushort bytes;
	ulong port;

	port = ctlr->card.port;
	port = ether->port;
	COMMAND(port, SelectWindow, 4);
	bytes = ins(port+FIFOdiag);
	COMMAND(port, SelectWindow, 1);


@@ 238,15 258,16 @@ getdiag(Ctlr *ctlr)
}

static void
interrupt(Ctlr *ctlr)
interrupt(Ether *ether)
{
	ushort status, diag;
	uchar txstatus, x;
	ulong port;

	port = ctlr->card.port;
	port = ether->port;
	status = ins(port+Status);

print("I%2.2ux|", status);
	if(status & Failure){
		/*
		 * Adapter failure, try to find out why.


@@ 255,28 276,25 @@ interrupt(Ctlr *ctlr)
		 * need to retransmit?
		 * This probably isn't right.
		 */
		diag = getdiag(ctlr);
		diag = getdiag(ether);
		print("ether509: status #%ux, diag #%ux\n", status, diag);

		if(diag & TxOverrun){
			COMMAND(port, TxReset, 0);
			COMMAND(port, TxEnable, 0);
			wakeup(&ether->tr);
		}

		if(diag & RxUnderrun){
			COMMAND(port, RxReset, 0);
			attach(ctlr);
			attach(ether);
		}

		if(diag & TxOverrun)
			transmit(ctlr);

		return;
	}

	if(status & RxComplete){
		receive(ctlr);
		wakeup(&ctlr->rr);
		receive(ether);
		status &= ~RxComplete;
	}



@@ 297,17 315,17 @@ interrupt(Ctlr *ctlr)
		if(txstatus & (TxJabber|TxUnderrun))
			COMMAND(port, TxReset, 0);
		COMMAND(port, TxEnable, 0);
		ctlr->oerrs++;
		ether->oerrs++;
	}

	if(status & (TxAvailable|TxComplete)){
		/*
		 * Reset the Tx FIFO threshold.
		 */
		if(status & TxAvailable)
		if(status & TxAvailable){
			COMMAND(port, AckIntr, TxAvailable);
		transmit(ctlr);
		wakeup(&ctlr->tr);
			wakeup(&ether->tr);
		}
		status &= ~(TxAvailable|TxComplete);
	}



@@ 317,7 335,7 @@ interrupt(Ctlr *ctlr)
	 * Otherwise, acknowledge the interrupt.
	 */
	if(status & AllIntr)
		panic("ether509 interrupt: #%lux, #%ux\n", status, getdiag(ctlr));
		panic("ether509 interrupt: #%lux, #%ux\n", status, getdiag(ether));

	COMMAND(port, AckIntr, Latch);
}


@@ 349,17 367,15 @@ idseq(void)
/*
 * Get configuration parameters.
 */
int
ccc509reset(Ctlr *ctlr)
static int
reset(Ether *ether)
{
	int i, ea;
	ushort x, acr;
	ulong port;

	/*
	 * Do the little configuration dance. We only look
	 * at the first card that responds, if we ever have more
	 * than one we'll need to modify this sequence.
	 * Do the little configuration dance:
	 *
	 * 2. get to command state, reset, then return to command state
	 */


@@ 408,8 424,8 @@ ccc509reset(Ctlr *ctlr)
	 *
	 *    Enable the adapter. 
	 */
	ctlr->card.port = (acr & 0x1F)*0x10 + 0x200;
	port = ctlr->card.port;
	ether->port = (acr & 0x1F)*0x10 + 0x200;
	port = ether->port;
	outb(port+ConfigControl, 0x01);

	/*


@@ 418,7 434,7 @@ ccc509reset(Ctlr *ctlr)
	 * The EEPROM command is 8bits, the lower 6 bits being
	 * the address offset.
	 */
	ctlr->card.irq = (ins(port+ResourceConfig)>>12) & 0x0F;
	ether->irq = (ins(port+ResourceConfig)>>12) & 0x0F;
	for(ea = 0, i = 0; i < 3; i++, ea += 2){
		while(ins(port+EEPROMcmd) & 0x8000)
			;


@@ 426,8 442,8 @@ ccc509reset(Ctlr *ctlr)
		while(ins(port+EEPROMcmd) & 0x8000)
			;
		x = ins(port+EEPROMdata);
		ctlr->ea[ea] = (x>>8) & 0xFF;
		ctlr->ea[ea+1] = x & 0xFF;
		ether->ea[ea] = (x>>8) & 0xFF;
		ether->ea[ea+1] = x & 0xFF;
	}

	/*


@@ 438,7 454,7 @@ ccc509reset(Ctlr *ctlr)
	 */
	COMMAND(port, SelectWindow, 2);
	for(i = 0; i < 6; i++)
		outb(port+i, ctlr->ea[i]);
		outb(port+i, ether->ea[i]);

	/*
	 * Finished with window 2.


@@ 458,12 474,12 @@ ccc509reset(Ctlr *ctlr)
	/*
	 * Set up the software configuration.
	 */
	ctlr->card.reset = ccc509reset;
	ctlr->card.attach = attach;
	ctlr->card.mode = mode;
	ctlr->card.transmit = transmit;
	ctlr->card.intr = interrupt;
	ctlr->card.bit16 = 1;
	ether->attach = attach;
	ether->write = write;
	ether->interrupt = interrupt;

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

	return 0;
}


@@ 471,5 487,5 @@ ccc509reset(Ctlr *ctlr)
void
ether509link(void)
{
	addethercard("3C509",  ccc509reset);
	addethercard("3C509",  reset);
}

A pc/etherif.h => pc/etherif.h +29 -0
@@ 0,0 1,29 @@
enum {
	MaxEther	= 4,
	Ntypes		= 8,
};

typedef struct Ether Ether;
struct Ether {
	ISAConf;			/* hardware info */

	void	(*attach)(Ether*);	/* filled in by reset routine */
	long	(*write)(Ether*, void*, long);
	void	(*interrupt)(Ether*);
	void	*private;

	Etherpkt tpkt;			/* transmit buffer */
	Etherpkt rpkt;			/* receive buffer */

	QLock	tlock;			/* lock for grabbing transmitter queue */
	Rendez	tr;			/* wait here for free xmit buffer */
	long	tlen;			/* length of data in tb for txfifo management */

	Netif;
};

#define NEXT(x, l)	(((x)+1)%(l))
#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))

extern void addethercard(char*, int(*)(Ether*));

M pc/fault386.c => pc/fault386.c +2 -1
@@ 24,7 24,7 @@ fault386(Ureg *ur)
	read = !(ur->ecode & 2);
	user = (ur->cs&0xffff) == UESEL;
	spllo();
/* print("F%d:A#%lux:U%d:R%d|", up->pid, addr, user, read);/**/
/*print("F%d:A#%lux:U%d:R%d|", up->pid, addr, user, read);/**/
	n = fault(addr, read);
	if(n < 0){
		if(user){


@@ 33,6 33,7 @@ fault386(Ureg *ur)
			postnote(up, 1, buf, NDebug);
			return;
		}
print("fault: 0x%lux", addr);
		dumpregs(ur);
		panic("fault: 0x%lux", addr);
	}

M pc/fns.h => pc/fns.h +3 -0
@@ 63,6 63,7 @@ long	pcmread(int, int, void*, long, ulong);
int	pcmspecial(int);
void	pcmspecialclose(int);
long	pcmwrite(int, int, void*, long, ulong);
void	prflush(void);
void	prhex(ulong);
void	procrestore(Proc*);
void	procsave(Proc*);


@@ 91,3 92,5 @@ void	vgainit(void);
#define getcallerpc(x)	(*(ulong*)(x))
#define KADDR(a)	((void*)((ulong)(a)|KZERO))
#define PADDR(a)	((ulong)(a)&~KZERO)

void	NS16552special(int, int, Queue**, Queue**, int (*)(Queue*, int));

M pc/kbd.c => pc/kbd.c +16 -16
@@ 115,11 115,6 @@ uchar kbtabesc1[] =
[0x58]	No,	No,	No,	No,	No,	No,	No,	No,
};

/*
 *  keyboard input q
 */
KIOQ	kbdq;

static int keybuttons;
static uchar ccc;
static int shift;


@@ 138,7 133,8 @@ enum

static void	kbdintr(Ureg*);
static void	ctps2intr(Ureg*);
static int	ps2mouseputc(IOQ*, int);

extern int m3mouseputc(IOQ*, int);

/*
 *  wait for output no longer busy


@@ 246,6 242,8 @@ kbdinit(void)
{
	int c;

	kbdq = qopen(4*1024, 0, 0, 0);

	setvec(Kbdvec, kbdintr);
	bigcursor();



@@ 287,9 285,12 @@ serialmouse(int port, char *type, int setspeed)
		error(Ebadarg);

	/* set up /dev/eia0 as the mouse */
	uartspecial(port, 0, &mouseq, setspeed ? 1200 : 0);
	if(setspeed)
		setspeed = 1200;
	if(type && *type == 'M')
		mouseq.putc = m3mouseputc;
		NS16552special(port, setspeed, 0, 0, m3mouseputc);
	else
		NS16552special(port, setspeed, 0, 0, 0);
	mousetype = Mouseserial;
}



@@ 390,14 391,13 @@ ps2mouse(void)
 *  shift & left button is the same as middle button
 */
static int
ps2mouseputc(IOQ *q, int c)
ps2mouseputc(int c)
{
	static short msg[3];
	static int nb;
	static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 5, 2, 3, 6, 7 };
	int buttons, dx, dy;

	USED(q);		/* not */
	/* 
	 *  check byte 0 for consistency
	 */


@@ 508,7 508,7 @@ kbdintr0(void)
	 *  if it's the mouse...
	 */
	if(s & Minready){
		ps2mouseputc(&mouseq, c);
		ps2mouseputc(c);
		return 0;
	}



@@ 579,9 579,9 @@ kbdintr0(void)
		putit:
			lstate = 0;
			if(c != -1)
				kbdputc(&kbdq, c);
				kbdputc(kbdq, c);
			else for(i=0; i<nk; i++)
				kbdputc(&kbdq, kc[i]);
				kbdputc(kbdq, kc[i]);
			break;
		case 3:
		case 4:


@@ 595,7 595,7 @@ kbdintr0(void)
			nk = 5;
			goto putit;
		default:
			kbdputc(&kbdq, c);
			kbdputc(kbdq, c);
			break;
		}
		return 0;


@@ 618,7 618,7 @@ kbdintr0(void)
			return 0;
		}
	}
	kbdputc(&kbdq, c);
	kbdputc(kbdq, c);
	return 0;
}



@@ 641,5 641,5 @@ ctps2intr(Ureg *ur)
	if((c & Rready) == 0)
		return;
	c = inb(ctport + CTdata);
	ps2mouseputc(&mouseq, c);
	ps2mouseputc(c);
}

M pc/l.s => pc/l.s +14 -0
@@ 498,6 498,18 @@ intrcommon:
	ADDL	$8,SP	/* error code and trap type */
	IRETL

TEXT forkret(SB), $0
	POPL	AX
	POPAL
	NOP
	POPL	GS
	POPL	FS
	POPL	ES
	POPL	DS
	NOP
	ADDL	$8,SP	/* error code and trap type */
	IRETL

intrscommon:
	PUSHL	DS
	PUSHL	ES


@@ 596,6 608,7 @@ TEXT	config(SB),$0
	OUTB
	RET

#ifdef notdef
/*
 *  copy bitmap changes to screen memory for ldepth 0 screen.
 *  reverse the bits since the screen is big-endian


@@ 706,6 719,7 @@ l20:
	MOVB	BX,-1(DI)(CX*1)
	LOOP	l20
	RET
#endif /* notdef */

/*
 * The DP8390 ethernet chip needs some time between

M pc/main.c => pc/main.c +2 -4
@@ 92,8 92,6 @@ machinit(void)
	m->mmask = 1<<m->machno;
}

ulong garbage;

void
ksetterm(char *f)
{


@@ 292,7 290,7 @@ confinit(void)
	pcnt = 0;

	/*
	 *  parse configuration args from dos file p9rc
	 *  parse configuration args from dos file plan9.ini
	 */
	cp = BOOTARGS;	/* where b.com leaves its config */
	cp[BOOTARGSLEN-1] = 0;


@@ 542,7 540,7 @@ exit(int ispanic)
	up = 0;
	print("exiting\n");
	if(ispanic){
		if(cpuflag)
		if(cpuserver)
			delay(10000);
		else
			for(;;);

M pc/mmu.c => pc/mmu.c +31 -4
@@ 104,10 104,10 @@ struct
static void
taskswitch(ulong pagetbl, ulong stack)
{
	putcr3(pagetbl);
	tss.ss0 = KDSEL;
	tss.sp0 = stack;
	tss.cr3 = pagetbl;
	putcr3(pagetbl);
}

/*


@@ 166,11 166,11 @@ mmuinit(void)
	 */
	memset(&tss, 0, sizeof(tss));
	taskswitch(ktoppg.pa, BY2PG + (ulong)m);
	puttr(TSSSEL);
	puttr(TSSSEL);/**/
}

/*
 *  Mark the mmu and tlb as inconsistent and call mapstack to fix it up.
 *  Mark the mmu and tlb as inconsistent and call mmuswitch to fix it up.
 */
void
flushmmu(void)


@@ 223,6 223,32 @@ mmuswitch(Proc *p)
		taskswitch(ktoppg.pa, (ulong)(p->kstack+KSTACK));
}

void
simpleputpage(Page *pg)
{
	Rendez *r;

	if(pg->ref != 1)
		panic("simpleputpage");

	pg->ref = 0;
	if(palloc.head){
		pg->next = palloc.head;
		palloc.head->prev = pg;
	}
	else {
		palloc.tail = pg;
		pg->next = 0;
	}
	palloc.head = pg;
	pg->prev = 0;

	palloc.freecol[pg->color]++;
	r = &palloc.r[pg->color];
	if(r->p != 0)
		wakeup(r);
}

/*
 *  give all page table pages back to the free pool.  This is called in sched()
 *  with palloc locked.


@@ 305,7 331,8 @@ putmmu(ulong va, ulong pa, Page *pg)
	pt[BTMOFF(va)] = pa | PTEUSER;

	/* flush cached mmu entries */
	taskswitch(up->mmutop->pa, (ulong)up->kstack);
	/*taskswitch(up->mmutop->pa, (ulong)(up->kstack+KSTACK));/**/
	putcr3(up->mmutop->pa);/**/
	splx(s);
}


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

#include <libg.h>
#include <gnot.h>
#include "screen.h"
#include "vga.h"

/*
 * CGA-only hack.
 */
/* imported */
extern	GSubfont defont0;
extern	Cursor arrow;
extern	GBitmap cursorback;

/* exported */
GSubfont *defont;
int islittle = 1;		/* little endian bit ordering in bytes */
GBitmap	gscreen;

/* local */
static	Lock vgalock;
static	ulong colormap[256][3];
static	Rectangle mbb;

/*
 *  reverse pixels into little endian order
 */
uchar revtab0[] = {
 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
};
uchar revtab1[] = {
 0x00, 0x40, 0x80, 0xc0, 0x10, 0x50, 0x90, 0xd0,
 0x20, 0x60, 0xa0, 0xe0, 0x30, 0x70, 0xb0, 0xf0,
 0x04, 0x44, 0x84, 0xc4, 0x14, 0x54, 0x94, 0xd4,
 0x24, 0x64, 0xa4, 0xe4, 0x34, 0x74, 0xb4, 0xf4,
 0x08, 0x48, 0x88, 0xc8, 0x18, 0x58, 0x98, 0xd8,
 0x28, 0x68, 0xa8, 0xe8, 0x38, 0x78, 0xb8, 0xf8,
 0x0c, 0x4c, 0x8c, 0xcc, 0x1c, 0x5c, 0x9c, 0xdc,
 0x2c, 0x6c, 0xac, 0xec, 0x3c, 0x7c, 0xbc, 0xfc,
 0x01, 0x41, 0x81, 0xc1, 0x11, 0x51, 0x91, 0xd1,
 0x21, 0x61, 0xa1, 0xe1, 0x31, 0x71, 0xb1, 0xf1,
 0x05, 0x45, 0x85, 0xc5, 0x15, 0x55, 0x95, 0xd5,
 0x25, 0x65, 0xa5, 0xe5, 0x35, 0x75, 0xb5, 0xf5,
 0x09, 0x49, 0x89, 0xc9, 0x19, 0x59, 0x99, 0xd9,
 0x29, 0x69, 0xa9, 0xe9, 0x39, 0x79, 0xb9, 0xf9,
 0x0d, 0x4d, 0x8d, 0xcd, 0x1d, 0x5d, 0x9d, 0xdd,
 0x2d, 0x6d, 0xad, 0xed, 0x3d, 0x7d, 0xbd, 0xfd,
 0x02, 0x42, 0x82, 0xc2, 0x12, 0x52, 0x92, 0xd2,
 0x22, 0x62, 0xa2, 0xe2, 0x32, 0x72, 0xb2, 0xf2,
 0x06, 0x46, 0x86, 0xc6, 0x16, 0x56, 0x96, 0xd6,
 0x26, 0x66, 0xa6, 0xe6, 0x36, 0x76, 0xb6, 0xf6,
 0x0a, 0x4a, 0x8a, 0xca, 0x1a, 0x5a, 0x9a, 0xda,
 0x2a, 0x6a, 0xaa, 0xea, 0x3a, 0x7a, 0xba, 0xfa,
 0x0e, 0x4e, 0x8e, 0xce, 0x1e, 0x5e, 0x9e, 0xde,
 0x2e, 0x6e, 0xae, 0xee, 0x3e, 0x7e, 0xbe, 0xfe,
 0x03, 0x43, 0x83, 0xc3, 0x13, 0x53, 0x93, 0xd3,
 0x23, 0x63, 0xa3, 0xe3, 0x33, 0x73, 0xb3, 0xf3,
 0x07, 0x47, 0x87, 0xc7, 0x17, 0x57, 0x97, 0xd7,
 0x27, 0x67, 0xa7, 0xe7, 0x37, 0x77, 0xb7, 0xf7,
 0x0b, 0x4b, 0x8b, 0xcb, 0x1b, 0x5b, 0x9b, 0xdb,
 0x2b, 0x6b, 0xab, 0xeb, 0x3b, 0x7b, 0xbb, 0xfb,
 0x0f, 0x4f, 0x8f, 0xcf, 0x1f, 0x5f, 0x9f, 0xdf,
 0x2f, 0x6f, 0xaf, 0xef, 0x3f, 0x7f, 0xbf, 0xff,
};
uchar revtab2[] = {
 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0,
 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71,
 0x81, 0x91, 0xa1, 0xb1, 0xc1, 0xd1, 0xe1, 0xf1,
 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72,
 0x82, 0x92, 0xa2, 0xb2, 0xc2, 0xd2, 0xe2, 0xf2,
 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73,
 0x83, 0x93, 0xa3, 0xb3, 0xc3, 0xd3, 0xe3, 0xf3,
 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74,
 0x84, 0x94, 0xa4, 0xb4, 0xc4, 0xd4, 0xe4, 0xf4,
 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75,
 0x85, 0x95, 0xa5, 0xb5, 0xc5, 0xd5, 0xe5, 0xf5,
 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76,
 0x86, 0x96, 0xa6, 0xb6, 0xc6, 0xd6, 0xe6, 0xf6,
 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77,
 0x87, 0x97, 0xa7, 0xb7, 0xc7, 0xd7, 0xe7, 0xf7,
 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78,
 0x88, 0x98, 0xa8, 0xb8, 0xc8, 0xd8, 0xe8, 0xf8,
 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79,
 0x89, 0x99, 0xa9, 0xb9, 0xc9, 0xd9, 0xe9, 0xf9,
 0x0a, 0x1a, 0x2a, 0x3a, 0x4a, 0x5a, 0x6a, 0x7a,
 0x8a, 0x9a, 0xaa, 0xba, 0xca, 0xda, 0xea, 0xfa,
 0x0b, 0x1b, 0x2b, 0x3b, 0x4b, 0x5b, 0x6b, 0x7b,
 0x8b, 0x9b, 0xab, 0xbb, 0xcb, 0xdb, 0xeb, 0xfb,
 0x0c, 0x1c, 0x2c, 0x3c, 0x4c, 0x5c, 0x6c, 0x7c,
 0x8c, 0x9c, 0xac, 0xbc, 0xcc, 0xdc, 0xec, 0xfc,
 0x0d, 0x1d, 0x2d, 0x3d, 0x4d, 0x5d, 0x6d, 0x7d,
 0x8d, 0x9d, 0xad, 0xbd, 0xcd, 0xdd, 0xed, 0xfd,
 0x0e, 0x1e, 0x2e, 0x3e, 0x4e, 0x5e, 0x6e, 0x7e,
 0x8e, 0x9e, 0xae, 0xbe, 0xce, 0xde, 0xee, 0xfe,
 0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f,
 0x8f, 0x9f, 0xaf, 0xbf, 0xcf, 0xdf, 0xef, 0xff,
};

void
pixreverse(uchar *p, int len, int ldepth)
{
	uchar *e;
	uchar *tab;

	switch(ldepth){
	case 0:
		tab = revtab0;
		break;
	case 1:
		tab = revtab1;
		break;
	case 2:
		tab = revtab2;
		break;
	default:
		return;
	}

	for(e = p + len; p < e; p++)
		*p = tab[*p];
}

static void
crout(int reg, int val)
{
	outb(CRX, reg);
	outb(CR, val);
}

void
screeninit(void)
{
	int i;
	ulong *l;

	/*
	 *  arrow is defined as a big endian
	 */
	pixreverse(arrow.set, 2*16, 0);
	pixreverse(arrow.clr, 2*16, 0);

	/*
	 *  swizzle the font longs.  we do both byte and bit swizzling
	 *  since the font is initialized with big endian longs.
	 */
	defont = &defont0;
	l = defont->bits->base;
	for(i = defont->bits->width*Dy(defont->bits->r); i > 0; i--, l++)
		*l = (*l<<24) | ((*l>>8)&0x0000ff00) | ((*l<<8)&0x00ff0000) | (*l>>24);
	pixreverse((uchar*)defont->bits->base,
		defont->bits->width*BY2WD*Dy(defont->bits->r), 0);

	crout(0x0a, 0xff);		/* turn off cursor */
	memset(CGASCREEN, 0, CGAWIDTH*CGAHEIGHT);
}

static void
cgascreenputc(int c)
{
	int i;
	static int color;
	static int pos;

	if(c == '\n'){
		pos = pos/CGAWIDTH;
		pos = (pos+1)*CGAWIDTH;
	} else if(c == '\t'){
		i = 8 - ((pos/2)&7);
		while(i-->0)
			cgascreenputc(' ');
	} else if(c == '\b'){
		if(pos >= 2)
			pos -= 2;
		cgascreenputc(' ');
		pos -= 2;
	} else {
		CGASCREEN[pos++] = c;
		CGASCREEN[pos++] = 2;	/* green on black */
	}
	if(pos >= CGAWIDTH*CGAHEIGHT){
		memmove(CGASCREEN, &CGASCREEN[CGAWIDTH], CGAWIDTH*(CGAHEIGHT-1));
		memset(&CGASCREEN[CGAWIDTH*(CGAHEIGHT-1)], 0, CGAWIDTH);
		pos = CGAWIDTH*(CGAHEIGHT-1);
	}
}

void
screenputs(char *s, int n)
{
	while(n-- > 0)
		cgascreenputc(*s++);
}

/*
 *  collect changes to the 'soft' screen
 */
void
mbbrect(Rectangle r)
{
	if (r.min.x < mbb.min.x)
		mbb.min.x = r.min.x;
	if (r.min.y < mbb.min.y)
		mbb.min.y = r.min.y;
	if (r.max.x > mbb.max.x)
		mbb.max.x = r.max.x;
	if (r.max.y > mbb.max.y)
		mbb.max.y = r.max.y;
	mousescreenupdate();
}

void
hwcursset(ulong *s, ulong *c, int ox, int oy)
{
	USED(s, c, ox, oy);
}

void
hwcursmove(int x, int y)
{
	USED(x, y);
}

void
screenload(Rectangle r, uchar *data, int tl, int l)
{
	USED(r, data, tl, l);
}

void
getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb)
{
	p &= (1<<(1<<gscreen.ldepth))-1;
	lock(&vgalock);
	*pr = colormap[p][0];
	*pg = colormap[p][1];
	*pb = colormap[p][2];
	unlock(&vgalock);
}

void
mousescreenupdate(void)
{
}

/*
 *  a fatter than usual cursor for the safari
 */
Cursor fatarrow = {
	{ -1, -1 },
	{
		0xff, 0xff, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0c, 
		0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, 
		0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8c, 0x04, 
		0x92, 0x08, 0x91, 0x10, 0xa0, 0xa0, 0xc0, 0x40, 
	},
	{
		0x00, 0x00, 0x7f, 0xfe, 0x7f, 0xfc, 0x7f, 0xf0, 
		0x7f, 0xe0, 0x7f, 0xe0, 0x7f, 0xf0, 0x7f, 0xf8, 
		0x7f, 0xfc, 0x7f, 0xfe, 0x7f, 0xfc, 0x73, 0xf8, 
		0x61, 0xf0, 0x60, 0xe0, 0x40, 0x40, 0x00, 0x00, 
	},
};

void
bigcursor(void)
{
	memmove(&arrow, &fatarrow, sizeof(fatarrow));
	pixreverse(arrow.set, 2*16, 0);
	pixreverse(arrow.clr, 2*16, 0);
}

M pc/trap.c => pc/trap.c +93 -33
@@ 219,6 219,7 @@ char *excname[] =
};

Ureg lasttrap, *lastur;
Proc *lastup;


/*


@@ 234,10 235,17 @@ trap(Ureg *ur)

	v = ur->trap;

	user = ((ur->cs)&0xffff)!=KESEL && v!=Syscallvec;
	user = (ur->cs&0xffff) == UESEL;
	if(user)
		up->dbgreg = ur;

ur->cs &= 0xffff;
ur->ds &= 0xffff;
ur->es &= 0xffff;
ur->fs &= 0xffff;
ur->gs &= 0xffff;
if(user) ur->ss &= 0xffff;

	/*
	 *  tell the 8259 that we're done with the
	 *  highest level interrupt (interrupts are still


@@ 253,6 261,7 @@ trap(Ureg *ur)
	if(v>=256 || (h = halloc.ivec[v]) == 0){
lasttrap = *ur;
lastur = ur;
lastup = up;
		/* an old 386 generates these fairly often, no idea why */
		if(v == 13)
			return;


@@ 292,6 301,8 @@ lastur = ur;
	splhi();
	if(user && (up->procctl || up->nnote))
		notify(ur);

lastup = up;
lasttrap = *ur;
lastur = ur;
}


@@ 307,24 318,31 @@ dumpregs2(Ureg *ur)
	else
		print("registers for kernel\n");
	print("FLAGS=%lux TRAP=%lux ECODE=%lux CS=%lux PC=%lux", ur->flags, ur->trap,
		ur->ecode, ur->cs&0xff, ur->pc);
	print(" SS=%lux USP=%lux\n", ur->ss&0xff, ur->usp);
		ur->ecode, ur->cs, ur->pc);
	print(" SS=%lux USP=%lux\n", ur->ss, ur->usp);
	print("  AX %8.8lux  BX %8.8lux  CX %8.8lux  DX %8.8lux\n",
		ur->ax, ur->bx, ur->cx, ur->dx);
	print("  SI %8.8lux  DI %8.8lux  BP %8.8lux\n",
		ur->si, ur->di, ur->bp);
	print("  DS %4.4ux  ES %4.4ux  FS %4.4ux  GS %4.4ux\n",
		ur->ds&0xffff, ur->es&0xffff, ur->fs&0xffff, ur->gs&0xffff);
	print("  DS %4.4lux  ES %4.4lux  FS %4.4lux  GS %4.4lux\n",
		ur->ds, ur->es, ur->fs, ur->gs);

}

void
dumpregs(Ureg *ur)
{
	ulong *x;

	x = (ulong*)(ur+1);
	dumpregs2(ur);
	print("  ur %lux\n", ur);
	print("  magic %lux %lux %lux\n", x[0], x[1], x[2]);
	print("  ur %lux up %lux\n", ur, up);
	dumpregs2(&lasttrap);
	print("  lastur %lux\n", lastur);
	print("  lastur %lux lastup %lux\n", lastur);
splhi();
dumpstack();
for(;;);
}

void


@@ 332,49 350,28 @@ dumpstack(void)
{
	ulong l, v, i;
	extern ulong etext;
	int lim;

	if(up == 0)
		return;

	i = 0;
	for(l=(ulong)&l; l<(ulong)(up->kstack+BY2PG); l+=4){
	lim = 3;
	for(l=(ulong)&l; l<(ulong)(up->kstack+BY2PG) && lim; l+=4){
		v = *(ulong*)l;
		if(KTZERO < v && v < (ulong)&etext){
			print("%lux ", v);
			i++;
		}
		if(i == 4){
		if(i == 8){
			i = 0;
			print("\n");
			lim--;
		}
	}

}

long
execregs(ulong entry, ulong ssize, ulong nargs)
{
	ulong *sp;
	Ureg *ur;

	sp = (ulong*)(USTKTOP - ssize);
	*--sp = nargs;

	ur = up->dbgreg;
	ur->usp = (ulong)sp;
	ur->pc = entry;
	return USTKTOP-BY2WD;			/* address of user-level clock */
}

ulong
userpc(void)
{
	Ureg *ur;

	ur = (Ureg*)up->dbgreg;
	return ur->pc;
}

/*
 *  system calls
 */


@@ 573,6 570,30 @@ noted(Ureg *ur, ulong arg0)
	}
}

long
execregs(ulong entry, ulong ssize, ulong nargs)
{
	ulong *sp;
	Ureg *ur;

	sp = (ulong*)(USTKTOP - ssize);
	*--sp = nargs;

	ur = up->dbgreg;
	ur->usp = (ulong)sp;
	ur->pc = entry;
	return USTKTOP-BY2WD;			/* address of user-level clock */
}

ulong
userpc(void)
{
	Ureg *ur;

	ur = (Ureg*)up->dbgreg;
	return ur->pc;
}

/* This routine must save the values of registers the user is not permitted to write
 * from devproc and the restore the saved values before returning
 */


@@ 592,6 613,45 @@ setregisters(Ureg *xp, char *pureg, char *uva, int n)
	xp->ss = ss;
}

static void
linkproc(void)
{
	spllo();
	(*up->kpfun)(up->kparg);
}

void
kprocchild(Proc *p, void (*func)(void*), void *arg)
{
	p->sched.pc = (ulong)linkproc;
	p->sched.sp = (ulong)p->kstack+KSTACK;

	p->kpfun = func;
	p->kparg = arg;
}

void
forkchild(Proc *p, Ureg *ur)
{
	Ureg *cur;

	/*
	 * We add 2*BY2Wd to the stack because we have to account for
	 *  - the return PC
	 *  - trap's argument (ur)
	 */
	p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Ureg)+2*BY2WD);
	p->sched.pc = (ulong)forkret;

	cur = (Ureg*)(p->sched.sp+2*BY2WD);
	memmove(cur, ur, sizeof(Ureg));
	cur->ax = 0;				/* return value of syscall in child */

	/* Things from bottom of syscall we never got to execute */
	p->psstate = 0;
	p->insyscall = 0;
}

/* Give enough context in the ureg to produce a kernel stack for
 * a sleeping process
 */

D pc/ultrastor.c => pc/ultrastor.c +0 -484
@@ 1,484 0,0 @@
/*
 * Ultrastor [13]4f SCSI Host Adapter.
 * Originally written by Charles Forsyth, forsyth@minster.york.ac.uk.
 * Needs work:
 *	handle multiple controllers;
 *	set options from user-level;
 *	split out to handle adaptec too;
 */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"

#include "ureg.h"

typedef struct Ctlr Ctlr;
typedef struct Mailbox Mailbox;
typedef struct Scatter Scatter;

enum {
	Nctl		= 1,		/* only support one */
	Nmbox		= 8,		/* number of Mailboxes; up to 16? */
	Nscatter 	= 33,		/* s/g list limit fixed by Ultrastor controller */

	Port		= 0x330,	/* factory defaults: I/O port */
	CtlrID		= 7,		/*	adapter SCSI id */
	Irq		= 14,		/*	interrupt request level */
};

struct Scatter {
	ulong	start;	/* physical address; Intel byte order */
	ulong	len;	/* length in bytes; Intel byte order */
};

/*
 * Ultrastor mailbox pointers point to one of these structures
 */
struct Mailbox {
	uchar	op;		/* Adapter operation */
	uchar	addr;		/* lun, chan, scsi ID */
	uchar	datap[4];	/* transfer data phys. pointer */
	uchar	datalen[4];	/* transfer data length */
	uchar	cmdlink[4];	/* command link phys. pointer */
	uchar	linkid;		/* SCSI command link ID */
	uchar	nscatter;	/* scatter/gather length (8 bytes per entry) */
	uchar	senselen;	/* length of sense data */
	uchar	cmdlen;		/* length of CDB */
	uchar	cmd[12];	/* SCSI command */
	uchar	adapterstatus;
	uchar	targetstatus;
	uchar	sensep[4];	/* sense data phys. pointer */

	/* the remainder is used by software, not the device */
	uchar	sensedata[64];	/* enough for AdapterInquiry data too */
	Scatter	sglist[Nscatter];
	Rendez	iodone;
	int	busy;		/* interrupt pending */
	ulong	physaddr;	/* physical address of this Mailbox */
	int	p9status;	/* p9 status value */
	Mailbox	*next;		/* next Mailbox in free list */
};

/* pack pointer as Intel order bytes (can simply assign) */
#define	PL(buf,p)	(*(ulong*)(buf)=(ulong)(p))

enum {
					/* bits in Mailbox.op[0] */
					/* 3 bit opcode */
	OpAdapter	= 0x01,		/* adapter command, not SCSI command */
	OpTarget	= 0x02,		/* SCSI command for device */
	OpReset		= 0x04,		/* device reset */
					/* 2 bit transfer direction */
	CmdDirection	= 0<<3,		/* transfer direction determined by SCSI command */
	DataIn		= 1<<3,		/* SCSI Data In */
	DataOut		= 2<<3,		/* SCSI Data Out */
	NoTransfer	= 3<<3,
	Nodiscon=	1<<5,		/* disable disconnect */
	Usecache=	1<<6,		/* use adapter cache (if available) */
	Scattered=	1<<7,		/* transfer pointer refers to S/G list */

					/* OpAdapter commands in Mailbox.cmd[0] */
	AdapterInquiry	= 0x01,
	AdapterSelftest	= 0x02,
	AdapterRead	= 0x03,		/* read adapter's buffer */
	AdapterWrite	= 0x04,		/* write to adapter's buffer */

					/* IO port offsets and bits */
	AdapterMask	= 0x00, 	/* local doorbell mask register */
	  OGMIntEnable	= 0x01,
	  SoftResetEnable = 0x40,
	  AdapterReady	= 0xe1,		/* adapter ready for work when all these set */
	AdapterIntr	= 0x01,		/* adapter interrupt/status; set by host, reset by adapter */
	  SignalAdapter	= 0x01,		/* tell adapter to read OGMpointer */
	  BusReset	= 0x20,		/* SCSI Bus Reset */
	  SoftReset	= 0x40,		/* Adapter Soft Reset */
	HostMask	= 0x02, 	/* host doorbell mask register */
	  ICMIntEnable	= 0x01,		/* incoming mail enable */
	  SIntEnable	= 0x80, 	/* enable interrupt from HostIntr reg */
	HostIntr	= 0x03, 	/* host doorbell interrupt/status; set by adapter, reset by host */
	  IntPending	= 0x80,		/* interrupt pending for host */
	  SignalHost	= 0x01,		/* Incoming Mail Interrupt */
	ProductID	= 0x04,		/* two byte product ID */
	Config1		= 0x06,
	Config2		= 0x07,
	OGMpointer	= 0x08,		/* pointer to Outgoing Mail */
	ICMpointer	= 0x0c,		/* pointer to Incoming Mail */
};

#define	OUT(c,v) outb(ctlr->io+(c), (v))
#define	IN(c)	inb(ctlr->io+(c))

struct Ctlr {
	Lock;
	QLock;
	int	io;		/* io port */
	int	irq;		/* interrupt vector */
	int	dma;		/* DMA channel */
	int	ownid;		/* adapter's SCSI ID */
	Mailbox	mbox[Nmbox];
	Mailbox	*free;		/* next free Mailbox */
	Rendez	mboxes;		/* wait for free mbox */
	QLock	mboxq;		/* mutex for mboxes */
	Rendez	ogm;		/* wait for free OGM slot with Ctlr qlocked */
};

static	Ctlr	ultra[Nctl];

int	scsidebugs[8] = {0};
int	scsiownid = 7;

static void
prmbox(Mailbox *m)
{
	int i;

	print("scsi %lx: op=%2.2x dir=%x cmd=%d [", m->physaddr, m->op&07, (m->op>>3)&3, m->cmdlen);
	for(i=0; i<m->cmdlen; i++)
		print(" %2.2x", m->cmd[i]);
	print("] id=%d lun=%d chan=%d", m->addr&7, (m->addr>>5)&7, (m->addr>>3)&3);
	print(" dlen=%lx dptr=%lx\n", *(long*)m->datalen, *(long*)m->datap);
	print(" astat=%2.2x dstat=%2.2x sc=%d [", m->adapterstatus, m->targetstatus, m->nscatter);
	for(i=0; i<m->nscatter; i++)
		print(" %lx,%ld", m->sglist[i].start, m->sglist[i].len);
	print("]\n");
	/*for(i=0; i<10; i++)
		print(" %2.2x", m->sensedata[i]);*/
	print("\n");
}

static void
resetadapter(int busreset)
{
	Ctlr *ctlr = &ultra[0];
	int i;

	if(IN(AdapterMask) & SoftResetEnable){
		OUT(AdapterIntr, SoftReset|busreset);
		for(i=50000; IN(AdapterIntr) & (SoftReset|busreset);)
			if(--i<0) {
				print("Ultrastor reset timed out\n");
				break;
			}
	}
}

static void
freebox(Ctlr *ctlr, Mailbox *m)
{
	lock(ctlr);
	if((m->next = ctlr->free) == 0)
		wakeup(&ctlr->mboxes);
	ctlr->free = m;
	unlock(ctlr);
}

static int
boxavail(void *a)
{
	return ((Ctlr*)a)->free != 0;
}

static int
scatter(Scatter *list, void *va, ulong nb)
{
	char *p = (char *)va;
	Scatter *sp = list;
	int limit = Nscatter;

	for(; nb != 0; sp++){
		if(--limit < 0)
			panic("Ultrastor I/O too scattered");
		sp->start = PADDR(p);
		sp->len = BY2PG - (sp->start&(BY2PG-1));	/* the rest of that page */
		if(sp->len > nb)
			sp->len = nb;
		nb -= sp->len;
		p += sp->len;
	}
	return sp - list;
}

static int
isready(void *a)
{
	Ctlr *ctlr = (Ctlr*)a;

	return (IN(AdapterIntr) & SignalAdapter) == 0;
}

static int
isdone(void *a)
{
	return ((Mailbox*)a)->busy == 0;
}

static int
ultra14fexec(Scsi *p, int rflag)
{
	Ctlr *ctlr = &ultra[0];
	Mailbox *m;
	long n;
	uchar *cp;

	if (p == 0 || ctlr->io == 0)
		return 0x0200;	/* device not available */

	/*
	 * get a free Mailbox and build an Ultrastor command
	 */
	while(lock(ctlr), (m = ctlr->free) == 0){
		unlock(ctlr);
		qlock(&ctlr->mboxq);
		if(waserror()){
			qunlock(&ctlr->mboxq);
			nexterror();
		}
		sleep(&ctlr->mboxes, boxavail, ctlr);
		poperror();
		qunlock(&ctlr->mboxq);
	}
	ctlr->free = m->next;
	unlock(ctlr);
	p->rflag = rflag;
	m->p9status = 0;
	m->op = OpTarget | CmdDirection | Usecache;	/* BUG? is it safe always to Usecache? */
	m->addr = p->target | (p->lun<<5);
	if(p->cmd.lim - p->cmd.ptr > sizeof(m->cmd))
		panic("scsiexec");
	m->cmdlen = p->cmd.lim - p->cmd.ptr;
	for(cp = m->cmd; p->cmd.ptr < p->cmd.lim;)
		*cp++ = *p->cmd.ptr++;
	n = p->data.lim - p->data.ptr;
	if(n){
		PL(m->datap, PADDR(m->sglist));
		m->op |= Scattered;
		m->nscatter = scatter(m->sglist, p->data.ptr, n);
	} else {
		m->nscatter = 0; /* controller will not allow s/g when n==0 */
		PL(m->datap, 0);
	}
	PL(m->datalen, n);
	m->adapterstatus = 0;
	m->targetstatus = 0;

	/*
	 * send the command to the host adapter
	 */
	while(lock(ctlr), IN(AdapterIntr) & SignalAdapter) {	/* adapter busy: infrequent? */
		static int fred;
		if(fred == 0){
			print("ultrastor busy\n");
			fred = 1;
		}
		unlock(ctlr);
		qlock(ctlr);
		if(waserror()) {
			freebox(ctlr, m);
			qunlock(ctlr);
			nexterror();
		}
		sleep(&ctlr->ogm, isready, ctlr);
		poperror();
		qunlock(ctlr);
	}
	m->busy = 1;
	outl(ctlr->io+OGMpointer, m->physaddr);
	OUT(AdapterIntr, SignalAdapter);
	unlock(ctlr);

	/*
	 * wait for reply
	 */
	while(waserror())
		;	/* could send MSCP abort request to adapter */
	while(m->busy)
		sleep(&m->iodone, isdone, m);
	poperror();
	p->status = m->p9status;
	if(scsidebugs[2])
		prmbox(m);
	if(p->status == 0x6000)
		p->data.ptr = p->data.lim;	/* can only assume no residue */
	freebox(ctlr, m);
	return p->status;
}

static void
scsimoan(char *msg, Mailbox *m)
{
	/*int i;*/

	print("SCSI error: %s:", msg);
	print("id=%d cmd=%2.2x status=%2.2x adapter=%2.2x", m->addr&7, m->cmd[0], m->targetstatus, m->adapterstatus);
	/*print(" sense:");
	for(i=0; i<10; i++)
		print(" %2.2x", m->sensedata[i]);*/
	print("\n");
}

static void
interrupt(Ureg *ur)
{
	Ctlr *ctlr = &ultra[0];
	Mailbox *m;
	ulong pa;

	USED(ur);
	if(ctlr->ogm.p && (IN(AdapterIntr) & SignalAdapter) == 0)
		wakeup(&ctlr->ogm);
	if((IN(HostIntr) & SignalHost) == 0)
		return;	/* no incoming mail */
	pa = inl(ctlr->io+ICMpointer);
	OUT(HostIntr, SignalHost);	/* release ICM slot */
	for(m = &ctlr->mbox[0]; m->physaddr != pa;)
		if(++m >= &ctlr->mbox[Nmbox]) {
			/* these sometimes happen in response to a scsi bus reset; ignore them */
			print("ultrastor: invalid ICM pointer #%lux\n", pa);
			return;
		}
	if(scsidebugs[1])
		prmbox(m);

	m->p9status = 0x1000 | m->adapterstatus;
	switch(m->adapterstatus){
	default:
		scsimoan("adapter", m);
		if(m->adapterstatus >= 0x92) /* ie, scsi command protocol error */
			resetadapter(BusReset);
		/* might adapter reset be required in other cases? */
		break;

	case 0x91:	/* selection timed out */
		m->p9status = 0x0200;
		if(scsidebugs[0])
			scsimoan("timeout", m);
		break;

	case 0xA3:
		scsimoan("SCSI bus reset error", m);
		break;

	case 0x00:
		m->p9status = 0x6000 | m->targetstatus;
		if(m->targetstatus && scsidebugs[0])
			scsimoan("device", m);
		break;
	}
	m->busy = 0;
	wakeup(&m->iodone);
}

static ISAConf ultra14f = {
	"ultra14f",
	Port,
	Irq,
	0,
	0,
};

static int irq[4] = {
	15, 14, 11, 10,
};

int (*
ultra14freset(void))(Scsi*, int)
{
	ISAConf *isa = &ultra14f;
	Ctlr *ctlr = &ultra[0];
	Mailbox *m;
	int i, model;

	/*
	 * See if we have any configuration info
	 * and check it is an Ultrastor [13]4f.
	 * Only one controller for now.
	 */
	if(isaconfig("scsi", 0, &ultra14f) == 0)
		return 0;
	memset(ctlr, 0, sizeof(Ctlr));
	ctlr->io = isa->port;

	if(IN(ProductID) != 0x56)
		return 0;
	model = IN(ProductID+1);
	switch(model){

	case 0x40:
		model = 14;
		break;

	case 0x41:
		model = 34;
		break;

	default:
		return 0;
	}

	i = IN(Config1);
	ctlr->irq = irq[((i>>4)&03)];
	if(model == 14)
		ctlr->dma = 5 + ((i>>6)&03);
	ctlr->ownid = IN(Config2)&07;
	/*print("Ultrastor %dF id %d io 0x%x irq %d dma %d\n",
		model, ctlr->ownid, ctlr->io, ctlr->irq, ctlr->dma);*/

	/*
	 * set the DMA controller to cascade mode for bus master
	 */
	switch(ctlr->dma){
	case 5:
		outb(0xd6, 0xc1); outb(0xd4, 1); break;
	case 6:
		outb(0xd6, 0xc2); outb(0xd4, 2); break;
	case 7:
		outb(0xd6, 0xc3); outb(0xd4, 3); break;
	}

	resetadapter(0);
	ctlr->free = 0;
	for(i=0; i<Nmbox; i++){
		m = &ctlr->mbox[i];
		memset(m, 0, sizeof(*m));
		m->physaddr = PADDR(m);
		PL(m->sensep, 0);	/* DON'T collect sense data automatically: messes up scuzz? */
		m->senselen = 0;
		m->next = ctlr->free;
		ctlr->free = m;
	}

	setvec(Int0vec+ctlr->irq, interrupt);

	/*
	 * issue AdapterInquiry command to check it's alive
	 * -- could check enquiry data
	 */
	OUT(HostMask, 0);	/* mask interrupts */
	m = &ctlr->mbox[0];
	m->op = OpAdapter | CmdDirection;
	m->addr = ctlr->ownid;
	m->cmdlen = 1;
	m->cmd[0] = AdapterInquiry;
	PL(m->datap, PADDR(m->sensedata));
	PL(m->datalen, 38);
	while(IN(AdapterIntr) & SignalAdapter)
		;
	outl(ctlr->io+OGMpointer, m->physaddr);
	OUT(AdapterIntr, SignalAdapter);
	for(i=100000; (IN(HostIntr)&SignalHost)==0;)
		if(--i < 0){
			print("No response to UltraStor Inquiry\n");
			ctlr->io = 0;
			return 0;
		}
	OUT(HostIntr, SignalHost);

	OUT(HostMask, ICMIntEnable|SIntEnable);
	scsiownid = ctlr->ownid;

	return ultra14fexec;
}