~kris/9p

9hist

f96852eceb879408150badd9d08bf8ccf56cad9e — David du Colombier 24 years ago 4a8b0a3
Plan 9 from Bell Labs 2001-12-11
M alphapc/devfloppy.c => alphapc/devfloppy.c +1 -1076
@@ 1,1076 1,1 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"../port/error.h"

#include	"floppy.h"

/* Intel 82077A (8272A compatible) floppy controller */

/* This module expects the following functions to be defined
 * elsewhere: 
 * 
 * inb()
 * outb()
 * floppyexec()
 * floppyeject() 
 * floppysetup0()
 * floppysetup1()
 * dmainit()
 * dmasetup()
 * dmaend()
 * 
 * On DMA systems, floppyexec() should be an empty function; 
 * on non-DMA systems, dmaend() should be an empty function; 
 * dmasetup() may enforce maximum transfer sizes. 
 */

enum {
	/* file types */
	Qdir=		0, 
	Qdata=		(1<<2),
	Qctl=		(2<<2),
	Qmask=		(3<<2),

	DMAchan=	2,	/* floppy dma channel */
};

#define DPRINT if(floppydebug)print
int floppydebug = 0;

/*
 *  types of drive (from PC equipment byte)
 */
enum
{
	Tnone=		0,
	T360kb=		1,
	T1200kb=	2,
	T720kb=		3,
	T1440kb=	4,
};

FType floppytype[] =
{
 { "3½HD",	T1440kb, 512, 18, 2, 1, 80, 0x1B, 0x54,	0, },
 { "3½DD",	T1440kb, 512,  9, 2, 1, 80, 0x1B, 0x54, 2, },
 { "3½DD",	T720kb,  512,  9, 2, 1, 80, 0x1B, 0x54, 2, },
 { "5¼HD",	T1200kb, 512, 15, 2, 1, 80, 0x2A, 0x50, 0, },
 { "5¼DD",	T1200kb, 512,  9, 2, 2, 40, 0x2A, 0x50, 1, },
 { "ATT3B1",	T1200kb, 512,  8, 2, 2, 48, 0x2A, 0x50, 1, },
 { "5¼DD",	T360kb,  512,  9, 2, 1, 40, 0x2A, 0x50, 2, },
};

/*
 *  bytes per sector encoding for the controller.
 *  - index for b2c is is (bytes per sector/128).
 *  - index for c2b is code from b2c
 */
static int b2c[] =
{
[1]	0,
[2]	1,
[4]	2,
[8]	3,
};
static int c2b[] =
{
	128,
	256,
	512,
	1024,
};

FController	fl;

#define MOTORBIT(i)	(1<<((i)+4))

/*
 *  predeclared
 */
static int	cmddone(void*);
static void	floppyformat(FDrive*, Cmdbuf*);
static void	floppykproc(void*);
static void	floppypos(FDrive*,long);
static int	floppyrecal(FDrive*);
static int	floppyresult(void);
static void	floppyrevive(void);
static long	floppyseek(FDrive*, long);
static int	floppysense(void);
static void	floppywait(int);
static long	floppyxfer(FDrive*, int, void*, long, long);

Dirtab floppydir[]={
	".",		{Qdir, 0, QTDIR},	0,	0550,
	"fd0disk",		{Qdata + 0},	0,	0660,
	"fd0ctl",		{Qctl + 0},	0,	0660,
	"fd1disk",		{Qdata + 1},	0,	0660,
	"fd1ctl",		{Qctl + 1},	0,	0660,
	"fd2disk",		{Qdata + 2},	0,	0660,
	"fd2ctl",		{Qctl + 2},	0,	0660,
	"fd3disk",		{Qdata + 3},	0,	0660,
	"fd3ctl",		{Qctl + 3},	0,	0660,
};
#define NFDIR	2	/* directory entries/drive */

enum
{
	CMdebug,
	CMeject,
	CMformat,
	CMreset,
};

static Cmdtab floppyctlmsg[] =
{
	CMdebug,	"debug",	1,
	CMeject,	"eject",	1,
	CMformat,	"format",	0,
	CMreset,	"reset",	1,
};

static void
fldump(void)
{
	DPRINT("sra %ux srb %ux dor %ux msr %ux dir %ux\n", inb(Psra), inb(Psrb),
		inb(Pdor), inb(Pmsr), inb(Pdir));
}

/*
 *  set floppy drive to its default type
 */
static void
floppysetdef(FDrive *dp)
{
	FType *t;

	for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++)
		if(dp->dt == t->dt){
			dp->t = t;
			floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
			break;
		}
}

static void
floppyreset(void)
{
	FDrive *dp;
	FType *t;
	ulong maxtsize;
	
	floppysetup0(&fl);
	if(fl.ndrive == 0)
		return;

	/*
	 *  init dependent parameters
	 */
	maxtsize = 0;
	for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){
		t->cap = t->bytes * t->heads * t->sectors * t->tracks;
		t->bcode = b2c[t->bytes/128];
		t->tsize = t->bytes * t->sectors;
		if(maxtsize < t->tsize)
			maxtsize = t->tsize;
	}

	dmainit(DMAchan, maxtsize);

	/*
	 *  allocate the drive storage
	 */
	fl.d = xalloc(fl.ndrive*sizeof(FDrive));
	fl.selected = fl.d;

	/*
	 *  stop the motors
	 */
	fl.motor = 0;
	delay(10);
	outb(Pdor, fl.motor | Fintena | Fena);
	delay(10);

	/*
	 *  init drives
	 */
	for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){
		dp->dev = dp - fl.d;
		dp->dt = T1440kb;
		floppysetdef(dp);
		dp->cyl = -1;			/* because we don't know */
		dp->cache = (uchar*)xspanalloc(maxtsize, BY2PG, 64*1024);
		dp->ccyl = -1;
		dp->vers = 0;
	}

	/*
	 *  first operation will recalibrate
	 */
	fl.confused = 1;

	floppysetup1(&fl);
}

static Chan*
floppyattach(char *spec)
{
	static int kstarted;

	if(fl.ndrive == 0)
		error(Enodev);

	if(kstarted == 0){
		/*
		 *  watchdog to turn off the motors
		 */
		kstarted = 1;
		kproc("floppy", floppykproc, 0);
	}
	return devattach('f', spec);
}

static Walkqid*
floppywalk(Chan *c, Chan *nc, char **name, int nname)
{
	return devwalk(c, nc, name, nname, floppydir, 1+fl.ndrive*NFDIR, devgen);
}

static int
floppystat(Chan *c, uchar *dp, int n)
{
	return devstat(c, dp, n, floppydir, 1+fl.ndrive*NFDIR, devgen);
}

static Chan*
floppyopen(Chan *c, int omode)
{
	return devopen(c, omode, floppydir, 1+fl.ndrive*NFDIR, devgen);
}

static void
floppyclose(Chan *)
{
}

static void
islegal(ulong offset, long n, FDrive *dp)
{
	if(offset % dp->t->bytes)
		error(Ebadarg);
	if(n % dp->t->bytes)
		error(Ebadarg);
}

/*
 *  check if the floppy has been replaced under foot.  cause
 *  an error if it has.
 *
 *  a seek and a read clears the condition.  this was determined
 *  experimentally, there has to be a better way.
 *
 *  if the read fails, cycle through the possible floppy
 *  density till one works or we've cycled through all
 *  possibilities for this drive.
 */
static void
changed(Chan *c, FDrive *dp)
{
	ulong old;
	FType *start;

	/*
	 *  if floppy has changed or first time through
	 */
	if((inb(Pdir)&Fchange) || dp->vers == 0){
		DPRINT("changed\n");
		fldump();
		dp->vers++;
		start = dp->t;
		dp->maxtries = 3;	/* limit it when we're probing */

		/* floppyon will fail if there's a controller but no drive */
		dp->confused = 1;	/* make floppyon recal */
		if(floppyon(dp) < 0)
			error(Eio);

		/* seek to the first track */
		floppyseek(dp, dp->t->heads*dp->t->tsize);
		while(waserror()){
			/*
			 *  if first attempt doesn't reset changed bit, there's
			 *  no floppy there
			 */
			if(inb(Pdir)&Fchange)
				nexterror();

			while(++dp->t){
				if(dp->t == &floppytype[nelem(floppytype)])
					dp->t = floppytype;
				if(dp->dt == dp->t->dt)
					break;
			}
			floppydir[1+NFDIR*dp->dev].length = dp->t->cap;

			/* floppyon will fail if there's a controller but no drive */
			if(floppyon(dp) < 0)
				error(Eio);

			DPRINT("changed: trying %s\n", dp->t->name);
			fldump();
			if(dp->t == start)
				nexterror();
		}

		/* if the read succeeds, we've got the density right */
		floppyxfer(dp, Fread, dp->cache, 0, dp->t->tsize);
		poperror();
		dp->maxtries = 20;
	}

	old = c->qid.vers;
	c->qid.vers = dp->vers;
	if(old && old != dp->vers)
		error(Eio);
}

static int
readtrack(FDrive *dp, int cyl, int head)
{
	int i, nn, sofar;
	ulong pos;

	nn = dp->t->tsize;
	if(dp->ccyl==cyl && dp->chead==head)
		return nn;
	pos = (cyl*dp->t->heads+head) * nn;
	for(sofar = 0; sofar < nn; sofar += i){
		dp->ccyl = -1;
		i = floppyxfer(dp, Fread, dp->cache + sofar, pos + sofar, nn - sofar);
		if(i <= 0)
			return -1;
	}
	dp->ccyl = cyl;
	dp->chead = head;
	return nn;
}

static long
floppyread(Chan *c, void *a, long n, vlong off)
{
	FDrive *dp;
	long rv;
	int sec, head, cyl;
	long len;
	uchar *aa;
	ulong offset = off;

	if(c->qid.type & QTDIR)
		return devdirread(c, a, n, floppydir, 1+fl.ndrive*NFDIR, devgen);

	rv = 0;
	dp = &fl.d[c->qid.path & ~Qmask];
	switch ((int)(c->qid.path & Qmask)) {
	case Qdata:
		islegal(offset, n, dp);
		aa = a;

		qlock(&fl);
		if(waserror()){
			qunlock(&fl);
			nexterror();
		}
		floppyon(dp);
		changed(c, dp);
		for(rv = 0; rv < n; rv += len){
			/*
			 *  all xfers come out of the track cache
			 */
			dp->len = n - rv;
			floppypos(dp, offset+rv);
			cyl = dp->tcyl;
			head = dp->thead;
			len = dp->len;
			sec = dp->tsec;
			if(readtrack(dp, cyl, head) < 0)
				break;
			memmove(aa+rv, dp->cache + (sec-1)*dp->t->bytes, len);
		}
		qunlock(&fl);
		poperror();

		break;
	case Qctl:
		return readstr(offset, a, n, dp->t->name);
	default:
		panic("floppyread: bad qid");
	}

	return rv;
}

static long
floppywrite(Chan *c, void *a, long n, vlong off)
{
	FDrive *dp;
	long rv, i;
	char *aa = a;
	Cmdbuf *cb;
	Cmdtab *ct;
	ulong offset = off;

	rv = 0;
	dp = &fl.d[c->qid.path & ~Qmask];
	switch ((int)(c->qid.path & Qmask)) {
	case Qdata:
		islegal(offset, n, dp);
		qlock(&fl);
		if(waserror()){
			qunlock(&fl);
			nexterror();
		}
		floppyon(dp);
		changed(c, dp);
		for(rv = 0; rv < n; rv += i){
			floppypos(dp, offset+rv);
			if(dp->tcyl == dp->ccyl)
				dp->ccyl = -1;
			i = floppyxfer(dp, Fwrite, aa+rv, offset+rv, n-rv);
			if(i < 0)
				break;
			if(i == 0)
				error(Eio);
		}
		qunlock(&fl);
		poperror();
		break;
	case Qctl:
		rv = n;
		cb = parsecmd(a, n);
		if(waserror()){
			free(cb);
			nexterror();
		}
		qlock(&fl);
		if(waserror()){
			qunlock(&fl);
			nexterror();
		}
		ct = lookupcmd(cb, floppyctlmsg, nelem(floppyctlmsg));
		switch(ct->index){
		case CMeject:
			floppyeject(dp);
			break;
		case CMformat:
			floppyformat(dp, cb);
			break;
		case CMreset:
			fl.confused = 1;
			floppyon(dp);
			break;
		case CMdebug:
			floppydebug = 1;
			break;
		}
		poperror();
		qunlock(&fl);
		poperror();
		free(cb);
		break;
	default:
		panic("floppywrite: bad qid");
	}

	return rv;
}

static void
floppykproc(void *)
{
	FDrive *dp;

	while(waserror())
		;
	for(;;){
		for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){
			if((fl.motor&MOTORBIT(dp->dev))
			&& TK2SEC(m->ticks - dp->lasttouched) > 5
			&& canqlock(&fl)){
				if(TK2SEC(m->ticks - dp->lasttouched) > 5)
					floppyoff(dp);
				qunlock(&fl);
			}
		}
		tsleep(&fl.kr, return0, 0, 1000);
	}
}

/*
 *  start a floppy drive's motor.
 */
static int
floppyon(FDrive *dp)
{
	int alreadyon;
	int tries;

	if(fl.confused)
		floppyrevive();

	/* start motor and select drive */
	alreadyon = fl.motor & MOTORBIT(dp->dev);
	fl.motor |= MOTORBIT(dp->dev);
	outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
	if(!alreadyon){
		/* wait for drive to spin up */
		tsleep(&dp->r, return0, 0, 750);

		/* clear any pending interrupts */
		floppysense();
	}

	/* set transfer rate */
	if(fl.rate != dp->t->rate){
		fl.rate = dp->t->rate;
		outb(Pdsr, fl.rate);
	}

	/* get drive to a known cylinder */
	if(dp->confused)
		for(tries = 0; tries < 4; tries++)
			if(floppyrecal(dp) >= 0)
				break;
	dp->lasttouched = m->ticks;
	fl.selected = dp;

	/* return -1 if this didn't work */
	if(dp->confused)
		return -1;
	return 0;
}

/*
 *  stop the floppy if it hasn't been used in 5 seconds
 */
static void
floppyoff(FDrive *dp)
{
	fl.motor &= ~MOTORBIT(dp->dev);
	outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
}

/*
 *  send a command to the floppy
 */
static int
floppycmd(void)
{
	int i;
	int tries;

	fl.nstat = 0;
	for(i = 0; i < fl.ncmd; i++){
		for(tries = 0; ; tries++){
			if((inb(Pmsr)&(Ffrom|Fready)) == Fready)
				break;
			if(tries > 1000){
				DPRINT("cmd %ux can't be sent (%d)\n", fl.cmd[0], i);
				fldump();

				/* empty fifo, might have been a bad command */
				floppyresult();
				return -1;
			}
			microdelay(8);	/* for machine independence */
		}
		outb(Pfdata, fl.cmd[i]);
	}
	return 0;
}

/*
 *  get a command result from the floppy
 *
 *  when the controller goes ready waiting for a command
 *  (instead of sending results), we're done
 * 
 */
static int
floppyresult(void)
{
	int i, s;
	int tries;

	/* get the result of the operation */
	for(i = 0; i < sizeof(fl.stat); i++){
		/* wait for status byte */
		for(tries = 0; ; tries++){
			s = inb(Pmsr)&(Ffrom|Fready);
			if(s == Fready){
				fl.nstat = i;
				return fl.nstat;
			}
			if(s == (Ffrom|Fready))
				break;
			if(tries > 1000){
				DPRINT("floppyresult: %d stats\n", i);
				fldump();
				fl.confused = 1;
				return -1;
			}
			microdelay(8);	/* for machine independence */
		}
		fl.stat[i] = inb(Pfdata);
	}
	fl.nstat = sizeof(fl.stat);
	return fl.nstat;
}

/*
 *  calculate physical address of a logical byte offset into the disk
 *
 *  truncate dp->length if it crosses a track boundary
 */
static void
floppypos(FDrive *dp, long off)
{
	int lsec;
	int ltrack;
	int end;

	lsec = off/dp->t->bytes;
	ltrack = lsec/dp->t->sectors;
	dp->tcyl = ltrack/dp->t->heads;
	dp->tsec = (lsec % dp->t->sectors) + 1;
	dp->thead = (lsec/dp->t->sectors) % dp->t->heads;

	/*
	 *  can't read across track boundaries.
	 *  if so, decrement the bytes to be read.
	 */
	end = (ltrack+1)*dp->t->sectors*dp->t->bytes;
	if(off+dp->len > end)
		dp->len = end - off;
}

/*
 *  get the interrupt cause from the floppy.
 */
static int
floppysense(void)
{
	fl.ncmd = 0;
	fl.cmd[fl.ncmd++] = Fsense;
	if(floppycmd() < 0)
		return -1;
	if(floppyresult() < 2){
		DPRINT("can't read sense response\n");
		fldump();
		fl.confused = 1;
		return -1;
	}
	return 0;
}

static int
cmddone(void *)
{
	return fl.ncmd == 0;
}

/*
 *  Wait for a floppy interrupt.  If none occurs in 5 seconds, we
 *  may have missed one.  This only happens on some portables which
 *  do power management behind our backs.  Call the interrupt
 *  routine to try to clear any conditions.
 */
static void
floppywait(int slow)
{
	tsleep(&fl.r, cmddone, 0, slow ? 5000 : 1000);
	if(!cmddone(0)){
		floppyintr(0);
		fl.confused = 1;
	}
}

/*
 *  we've lost the floppy position, go to cylinder 0.
 */
static int
floppyrecal(FDrive *dp)
{
	dp->ccyl = -1;
	dp->cyl = -1;

	fl.ncmd = 0;
	fl.cmd[fl.ncmd++] = Frecal;
	fl.cmd[fl.ncmd++] = dp->dev;
	if(floppycmd() < 0)
		return -1;
	floppywait(1);
	if(fl.nstat < 2){
		DPRINT("recalibrate: confused %ux\n", inb(Pmsr));
		fl.confused = 1;
		return -1;
	}
	if((fl.stat[0] & (Codemask|Seekend)) != Seekend){
		DPRINT("recalibrate: failed\n");
		dp->confused = 1;
		return -1;
	}
	dp->cyl = fl.stat[1];
	if(dp->cyl != 0){
		DPRINT("recalibrate: wrong cylinder %d\n", dp->cyl);
		dp->cyl = -1;
		dp->confused = 1;
		return -1;
	}

	dp->confused = 0;
	return 0;
}

/*
 *  if the controller or a specific drive is in a confused state,
 *  reset it and get back to a known state
 */
static void
floppyrevive(void)
{
	FDrive *dp;

	/*
	 *  reset the controller if it's confused
	 */
	if(fl.confused){
		DPRINT("floppyrevive in\n");
		fldump();

		/* reset controller and turn all motors off */
		splhi();
		fl.ncmd = 1;
		fl.cmd[0] = 0;
		outb(Pdor, 0);
		delay(10);
		outb(Pdor, Fintena|Fena);
		delay(10);
		spllo();
		fl.motor = 0;
		fl.confused = 0;
		floppywait(0);

		/* mark all drives in an unknown state */
		for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++)
			dp->confused = 1;

		/* set rate to a known value */
		outb(Pdsr, 0);
		fl.rate = 0;

		DPRINT("floppyrevive out\n");
		fldump();
	}
}

/*
 *  seek to the target cylinder
 *
 *	interrupt, no results
 */
static long
floppyseek(FDrive *dp, long off)
{
	floppypos(dp, off);
	if(dp->cyl == dp->tcyl)
		return dp->tcyl;
	dp->cyl = -1;

	fl.ncmd = 0;
	fl.cmd[fl.ncmd++] = Fseek;
	fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev;
	fl.cmd[fl.ncmd++] = dp->tcyl * dp->t->steps;
	if(floppycmd() < 0)
		return -1;
	floppywait(1);
	if(fl.nstat < 2){
		DPRINT("seek: confused\n");
		fl.confused = 1;
		return -1;
	}
	if((fl.stat[0] & (Codemask|Seekend)) != Seekend){
		DPRINT("seek: failed\n");
		dp->confused = 1;
		return -1;
	}

	dp->cyl = dp->tcyl;
	return dp->tcyl;
}

/*
 *  read or write to floppy.  try up to three times.
 */
static long
floppyxfer(FDrive *dp, int cmd, void *a, long off, long n)
{
	long offset;
	int tries;

	if(off >= dp->t->cap)
		return 0;
	if(off + n > dp->t->cap)
		n = dp->t->cap - off;

	/* retry on error (until it gets ridiculous) */
	tries = 0;
	while(waserror()){
		if(tries++ >= dp->maxtries)
			nexterror();
		DPRINT("floppyxfer: retrying\n");
	}

	dp->len = n;
	if(floppyseek(dp, off) < 0){
		DPRINT("xfer: seek failed\n");
		dp->confused = 1;
		error(Eio);
	}

	/*
	 *  set up the dma (dp->len may be trimmed)
	 */
	if(waserror()){
		dmaend(DMAchan);
		nexterror();
	}
	dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread);
	if(dp->len < 0)
		error(Eio);

	/*
	 *  start operation
	 */
	fl.ncmd = 0;
	fl.cmd[fl.ncmd++] = cmd | (dp->t->heads > 1 ? Fmulti : 0);
	fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev;
	fl.cmd[fl.ncmd++] = dp->tcyl;
	fl.cmd[fl.ncmd++] = dp->thead;
	fl.cmd[fl.ncmd++] = dp->tsec;
	fl.cmd[fl.ncmd++] = dp->t->bcode;
	fl.cmd[fl.ncmd++] = dp->t->sectors;
	fl.cmd[fl.ncmd++] = dp->t->gpl;
	fl.cmd[fl.ncmd++] = 0xFF;
	if(floppycmd() < 0)
		error(Eio);

	/* Poll ready bits and transfer data */
	floppyexec((char*)a, dp->len, cmd==Fread);

	/*
	 *  give bus to DMA, floppyintr() will read result
	 */
	floppywait(0);
	dmaend(DMAchan);
	poperror();

	/*
	 *  check for errors
	 */
	if(fl.nstat < 7){
		DPRINT("xfer: confused\n");
		fl.confused = 1;
		error(Eio);
	}
	if((fl.stat[0] & Codemask)!=0 || fl.stat[1] || fl.stat[2]){
		DPRINT("xfer: failed %ux %ux %ux\n", fl.stat[0],
			fl.stat[1], fl.stat[2]);
		DPRINT("offset %lud len %ld\n", off, dp->len);
		if((fl.stat[0]&Codemask)==Cmdexec && fl.stat[1]==Overrun){
			DPRINT("DMA overrun: retry\n");
		} else
			dp->confused = 1;
		error(Eio);
	}

	/*
	 *  check for correct cylinder
	 */
	offset = fl.stat[3] * dp->t->heads + fl.stat[4];
	offset = offset*dp->t->sectors + fl.stat[5] - 1;
	offset = offset * c2b[fl.stat[6]];
	if(offset != off+dp->len){
		DPRINT("xfer: ends on wrong cyl\n");
		dp->confused = 1;
		error(Eio);
	}
	poperror();

	dp->lasttouched = m->ticks;
	return dp->len;
}

/*
 *  format a track
 */
static void
floppyformat(FDrive *dp, Cmdbuf *cb)
{
 	int cyl, h, sec;
	ulong track;
	uchar *buf, *bp;
	FType *t;

	/*
	 *  set the type
	 */
	if(cb->nf == 2){
		for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){
			if(strcmp(cb->f[1], t->name)==0 && t->dt==dp->dt){
				dp->t = t;
				floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
				break;
			}
		}
		if(t >= &floppytype[nelem(floppytype)])
			error(Ebadarg);
	} else if(cb->nf == 1){
		floppysetdef(dp);
		t = dp->t;
	} else {
		cmderror(cb, "invalid floppy format command");
		SET(t);
	}

	/*
	 *  buffer for per track info
	 */
	buf = smalloc(t->sectors*4);
	if(waserror()){
		free(buf);
		nexterror();
	}

	/* force a recalibrate to cylinder 0 */
	dp->confused = 1;
	if(!waserror()){
		floppyon(dp);
		poperror();
	}

	/*
	 *  format a track at time
	 */
	for(track = 0; track < t->tracks*t->heads; track++){
		cyl = track/t->heads;
		h = track % t->heads;

		/*
		 *  seek to track, ignore errors
		 */
		floppyseek(dp, track*t->tsize);
		dp->cyl = cyl;
		dp->confused = 0;

		/*
		 *  set up the dma (dp->len may be trimmed)
		 */
		bp = buf;
		for(sec = 1; sec <= t->sectors; sec++){
			*bp++ = cyl;
			*bp++ = h;
			*bp++ = sec;
			*bp++ = t->bcode;
		}
		if(waserror()){
			dmaend(DMAchan);
			nexterror();
		}
		if(dmasetup(DMAchan, buf, bp-buf, 0) < 0)
			error(Eio);

		/*
		 *  start operation
		 */
		fl.ncmd = 0;
		fl.cmd[fl.ncmd++] = Fformat;
		fl.cmd[fl.ncmd++] = (h<<2) | dp->dev;
		fl.cmd[fl.ncmd++] = t->bcode;
		fl.cmd[fl.ncmd++] = t->sectors;
		fl.cmd[fl.ncmd++] = t->fgpl;
		fl.cmd[fl.ncmd++] = 0x5a;
		if(floppycmd() < 0)
			error(Eio);

		/* Poll ready bits and transfer data */
		floppyexec((char *)buf, bp-buf, 0);

		/*
		 *  give bus to DMA, floppyintr() will read result
		 */
		floppywait(1);
		dmaend(DMAchan);
		poperror();

		/*
		 *  check for errors
		 */
		if(fl.nstat < 7){
			DPRINT("format: confused\n");
			fl.confused = 1;
			error(Eio);
		}
		if((fl.stat[0]&Codemask)!=0 || fl.stat[1]|| fl.stat[2]){
			DPRINT("format: failed %ux %ux %ux\n",
				fl.stat[0], fl.stat[1], fl.stat[2]);
			dp->confused = 1;
			error(Eio);
		}
	}
	free(buf);
	dp->confused = 1;
	poperror();
}

static void
floppyintr(Ureg *)
{
	switch(fl.cmd[0]&~Fmulti){
	case Fread:
	case Fwrite:
	case Fformat:
	case Fdumpreg: 
		floppyresult();
		break;
	case Fseek:
	case Frecal:
	default:
		floppysense();	/* to clear interrupt */
		break;
	}
	fl.ncmd = 0;
	wakeup(&fl.r);
}

Dev floppydevtab = {
	'f',
	"floppy",

	floppyreset,
	devinit,
	floppyattach,
	floppywalk,
	floppystat,
	floppyopen,
	devcreate,
	floppyclose,
	floppyread,
	devbread,
	floppywrite,
	devbwrite,
	devremove,
	devwstat,
};
#include "../pc/devfloppy.c"

M alphapc/ether2114x.c => alphapc/ether2114x.c +1 -1581
@@ 1,1581 1,1 @@
/*
 * Digital Semiconductor DECchip 2114x PCI Fast Ethernet LAN Controller.
 * To do:
 *	thresholds;
 *	ring sizing;
 *	handle more error conditions;
 *	tidy setup packet mess;
 *	push initialisation back to attach;
 *	full SROM decoding.
 */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "../port/netif.h"

#include "etherif.h"

#define DEBUG		(0)
#define debug		if(DEBUG)print

enum {
	Nrde		= 64,
	Ntde		= 64,
};

#define Rbsz		ROUNDUP(sizeof(Etherpkt)+4, 4)

enum {					/* CRS0 - Bus Mode */
	Swr		= 0x00000001,	/* Software Reset */
	Bar		= 0x00000002,	/* Bus Arbitration */
	Dsl		= 0x0000007C,	/* Descriptor Skip Length (field) */
	Ble		= 0x00000080,	/* Big/Little Endian */
	Pbl		= 0x00003F00,	/* Programmable Burst Length (field) */
	Cal		= 0x0000C000,	/* Cache Alignment (field) */
	Cal8		= 0x00004000,	/* 8 longword boundary alignment */
	Cal16		= 0x00008000,	/* 16 longword boundary alignment */
	Cal32		= 0x0000C000,	/* 32 longword boundary alignment */
	Tap		= 0x000E0000,	/* Transmit Automatic Polling (field) */
	Dbo		= 0x00100000,	/* Descriptor Byte Ordering Mode */
	Rml		= 0x00200000,	/* Read Multiple */
}; 

enum {					/* CSR[57] - Status and Interrupt Enable */
	Ti		= 0x00000001,	/* Transmit Interrupt */
	Tps		= 0x00000002,	/* Transmit Process Stopped */
	Tu		= 0x00000004,	/* Transmit buffer Unavailable */
	Tjt		= 0x00000008,	/* Transmit Jabber Timeout */
	Unf		= 0x00000020,	/* transmit UNderFlow */
	Ri		= 0x00000040,	/* Receive Interrupt */
	Ru		= 0x00000080,	/* Receive buffer Unavailable */
	Rps		= 0x00000100,	/* Receive Process Stopped */
	Rwt		= 0x00000200,	/* Receive Watchdog Timeout */
	Eti		= 0x00000400,	/* Early Transmit Interrupt */
	Gte		= 0x00000800,	/* General purpose Timer Expired */
	Fbe		= 0x00002000,	/* Fatal Bit Error */
	Ais		= 0x00008000,	/* Abnormal Interrupt Summary */
	Nis		= 0x00010000,	/* Normal Interrupt Summary */
	Rs		= 0x000E0000,	/* Receive process State (field) */
	Ts		= 0x00700000,	/* Transmit process State (field) */
	Eb		= 0x03800000,	/* Error bits */
};

enum {					/* CSR6 - Operating Mode */
	Hp		= 0x00000001,	/* Hash/Perfect receive filtering mode */
	Sr		= 0x00000002,	/* Start/stop Receive */
	Ho		= 0x00000004,	/* Hash-Only filtering mode */
	Pb		= 0x00000008,	/* Pass Bad frames */
	If		= 0x00000010,	/* Inverse Filtering */
	Sb		= 0x00000020,	/* Start/stop Backoff counter */
	Pr		= 0x00000040,	/* Promiscuous Mode */
	Pm		= 0x00000080,	/* Pass all Multicast */
	Fd		= 0x00000200,	/* Full Duplex mode */
	Om		= 0x00000C00,	/* Operating Mode (field) */
	Fc		= 0x00001000,	/* Force Collision */
	St		= 0x00002000,	/* Start/stop Transmission Command */
	Tr		= 0x0000C000,	/* ThReshold control bits (field) */
	Tr128		= 0x00000000,
	Tr256		= 0x00004000,
	Tr512		= 0x00008000,
	Tr1024		= 0x0000C000,
	Ca		= 0x00020000,	/* CApture effect enable */
	Ps		= 0x00040000,	/* Port Select */
	Hbd		= 0x00080000,	/* HeartBeat Disable */
	Imm		= 0x00100000,	/* IMMediate mode */
	Sf		= 0x00200000,	/* Store and Forward */
	Ttm		= 0x00400000,	/* Transmit Threshold Mode */
	Pcs		= 0x00800000,	/* PCS function */
	Scr		= 0x01000000,	/* SCRambler mode */
	Mbo		= 0x02000000,	/* Must Be One */
	Ra		= 0x40000000,	/* Receive All */
	Sc		= 0x80000000,	/* Special Capture effect enable */

	TrMODE		= Tr512,	/* default transmission threshold */
};

enum {					/* CSR9 - ROM and MII Management */
	Scs		= 0x00000001,	/* serial ROM chip select */
	Sclk		= 0x00000002,	/* serial ROM clock */
	Sdi		= 0x00000004,	/* serial ROM data in */
	Sdo		= 0x00000008,	/* serial ROM data out */
	Ss		= 0x00000800,	/* serial ROM select */
	Wr		= 0x00002000,	/* write */
	Rd		= 0x00004000,	/* read */

	Mdc		= 0x00010000,	/* MII management clock */
	Mdo		= 0x00020000,	/* MII management write data */
	Mii		= 0x00040000,	/* MII management operation mode (W) */
	Mdi		= 0x00080000,	/* MII management data in */
};

enum {					/* CSR12 - General-Purpose Port */
	Gpc		= 0x00000100,	/* General Purpose Control */
};

typedef struct Des {
	int	status;
	int	control;
	ulong	addr;
	Block*	bp;
} Des;

enum {					/* status */
	Of		= 0x00000001,	/* Rx: OverFlow */
	Ce		= 0x00000002,	/* Rx: CRC Error */
	Db		= 0x00000004,	/* Rx: Dribbling Bit */
	Re		= 0x00000008,	/* Rx: Report on MII Error */
	Rw		= 0x00000010,	/* Rx: Receive Watchdog */
	Ft		= 0x00000020,	/* Rx: Frame Type */
	Cs		= 0x00000040,	/* Rx: Collision Seen */
	Tl		= 0x00000080,	/* Rx: Frame too Long */
	Ls		= 0x00000100,	/* Rx: Last deScriptor */
	Fs		= 0x00000200,	/* Rx: First deScriptor */
	Mf		= 0x00000400,	/* Rx: Multicast Frame */
	Rf		= 0x00000800,	/* Rx: Runt Frame */
	Dt		= 0x00003000,	/* Rx: Data Type (field) */
	De		= 0x00004000,	/* Rx: Descriptor Error */
	Fl		= 0x3FFF0000,	/* Rx: Frame Length (field) */
	Ff		= 0x40000000,	/* Rx: Filtering Fail */

	Def		= 0x00000001,	/* Tx: DEFerred */
	Uf		= 0x00000002,	/* Tx: UnderFlow error */
	Lf		= 0x00000004,	/* Tx: Link Fail report */
	Cc		= 0x00000078,	/* Tx: Collision Count (field) */
	Hf		= 0x00000080,	/* Tx: Heartbeat Fail */
	Ec		= 0x00000100,	/* Tx: Excessive Collisions */
	Lc		= 0x00000200,	/* Tx: Late Collision */
	Nc		= 0x00000400,	/* Tx: No Carrier */
	Lo		= 0x00000800,	/* Tx: LOss of carrier */
	To		= 0x00004000,	/* Tx: Transmission jabber timeOut */

	Es		= 0x00008000,	/* [RT]x: Error Summary */
	Own		= 0x80000000,	/* [RT]x: OWN bit */
};

enum {					/* control */
	Bs1		= 0x000007FF,	/* [RT]x: Buffer 1 Size */
	Bs2		= 0x003FF800,	/* [RT]x: Buffer 2 Size */

	Ch		= 0x01000000,	/* [RT]x: second address CHained */
	Er		= 0x02000000,	/* [RT]x: End of Ring */

	Ft0		= 0x00400000,	/* Tx: Filtering Type 0 */
	Dpd		= 0x00800000,	/* Tx: Disabled PaDding */
	Ac		= 0x04000000,	/* Tx: Add CRC disable */
	Set		= 0x08000000,	/* Tx: SETup packet */
	Ft1		= 0x10000000,	/* Tx: Filtering Type 1 */
	Fseg		= 0x20000000,	/* Tx: First SEGment */
	Lseg		= 0x40000000,	/* Tx: Last SEGment */
	Ic		= 0x80000000,	/* Tx: Interrupt on Completion */
};

enum {					/* PHY registers */
	Bmcr		= 0,		/* Basic Mode Control */
	Bmsr		= 1,		/* Basic Mode Status */
	Phyidr1		= 2,		/* PHY Identifier #1 */
	Phyidr2		= 3,		/* PHY Identifier #2 */
	Anar		= 4,		/* Auto-Negotiation Advertisment */
	Anlpar		= 5,		/* Auto-Negotiation Link Partner Ability */
	Aner		= 6,		/* Auto-Negotiation Expansion */
};

enum {					/* Variants */
	Tulip0		= (0x0009<<16)|0x1011,
	Tulip3		= (0x0019<<16)|0x1011,
	Pnic		= (0x0002<<16)|0x11AD,
	Pnic2		= (0xC115<<16)|0x11AD,
};

typedef struct Ctlr Ctlr;
typedef struct Ctlr {
	int	port;
	Pcidev*	pcidev;
	Ctlr*	next;
	int	active;
	int	id;			/* (pcidev->did<<16)|pcidev->vid */

	uchar*	srom;
	int	sromsz;			/* address size in bits */
	uchar*	sromea;			/* MAC address */
	uchar*	leaf;
	int	sct;			/* selected connection type */
	int	k;			/* info block count */
	uchar*	infoblock[16];
	int	sctk;			/* sct block index */
	int	curk;			/* current block index */
	uchar*	type5block;

	int	phy[32];		/* logical to physical map */
	int	phyreset;		/* reset bitmap */
	int	curphyad;
	int	fdx;
	int	ttm;

	uchar	fd;			/* option */
	int	medium;			/* option */

	int	csr6;			/* CSR6 - operating mode */
	int	mask;			/* CSR[57] - interrupt mask */
	int	mbps;

	Lock	lock;

	Des*	rdr;			/* receive descriptor ring */
	int	nrdr;			/* size of rdr */
	int	rdrx;			/* index into rdr */

	Lock	tlock;
	Des*	tdr;			/* transmit descriptor ring */
	int	ntdr;			/* size of tdr */
	int	tdrh;			/* host index into tdr */
	int	tdri;			/* interface index into tdr */
	int	ntq;			/* descriptors active */
	int	ntqmax;
	Block*	setupbp;

	ulong	of;			/* receive statistics */
	ulong	ce;
	ulong	cs;
	ulong	tl;
	ulong	rf;
	ulong	de;

	ulong	ru;
	ulong	rps;
	ulong	rwt;

	ulong	uf;			/* transmit statistics */
	ulong	ec;
	ulong	lc;
	ulong	nc;
	ulong	lo;
	ulong	to;

	ulong	tps;
	ulong	tu;
	ulong	tjt;
	ulong	unf;
} Ctlr;

static Ctlr* ctlrhead;
static Ctlr* ctlrtail;

#define csr32r(c, r)	(inl((c)->port+((r)*8)))
#define csr32w(c, r, l)	(outl((c)->port+((r)*8), (ulong)(l)))

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

	ctlr = ((Ether*)arg)->ctlr;
	ilock(&ctlr->lock);
	if(on)
		ctlr->csr6 |= Pr;
	else
		ctlr->csr6 &= ~Pr;
	csr32w(ctlr, 6, ctlr->csr6);
	iunlock(&ctlr->lock);
}

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

	ctlr = ether->ctlr;
	ilock(&ctlr->lock);
	if(!(ctlr->csr6 & Sr)){
		ctlr->csr6 |= Sr;
		csr32w(ctlr, 6, ctlr->csr6);
	}
	iunlock(&ctlr->lock);
}

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

	ctlr = ether->ctlr;

	ether->crcs = ctlr->ce;
	ether->frames = ctlr->rf+ctlr->cs;
	ether->buffs = ctlr->de+ctlr->tl;
	ether->overflows = ctlr->of;

	if(n == 0)
		return 0;

	p = malloc(READSTR);
	l = snprint(p, READSTR, "Overflow: %lud\n", ctlr->of);
	l += snprint(p+l, READSTR-l, "Ru: %lud\n", ctlr->ru);
	l += snprint(p+l, READSTR-l, "Rps: %lud\n", ctlr->rps);
	l += snprint(p+l, READSTR-l, "Rwt: %lud\n", ctlr->rwt);
	l += snprint(p+l, READSTR-l, "Tps: %lud\n", ctlr->tps);
	l += snprint(p+l, READSTR-l, "Tu: %lud\n", ctlr->tu);
	l += snprint(p+l, READSTR-l, "Tjt: %lud\n", ctlr->tjt);
	l += snprint(p+l, READSTR-l, "Unf: %lud\n", ctlr->unf);
	l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->ce);
	l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->cs);
	l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->tl);
	l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->rf);
	l += snprint(p+l, READSTR-l, "Descriptor Error: %lud\n", ctlr->de);
	l += snprint(p+l, READSTR-l, "Underflow Error: %lud\n", ctlr->uf);
	l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec);
	l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->lc);
	l += snprint(p+l, READSTR-l, "No Carrier: %lud\n", ctlr->nc);
	l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->lo);
	l += snprint(p+l, READSTR-l, "Transmit Jabber Timeout: %lud\n",
		ctlr->to);
	l += snprint(p+l, READSTR-l, "csr6: %luX %uX\n", csr32r(ctlr, 6),
		ctlr->csr6);
	snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax);
	ctlr->ntqmax = 0;
	buf = a;
	len = readstr(offset, buf, n, p);
	if(offset > l)
		offset -= l;
	else
		offset = 0;
	buf += len;
	n -= len;

	l = snprint(p, READSTR, "srom:");
	for(i = 0; i < (1<<ctlr->sromsz); i++){
		if(i && ((i & 0x0F) == 0))
			l += snprint(p+l, READSTR-l, "\n     ");
		l += snprint(p+l, READSTR-l, " %2.2uX", ctlr->srom[i]);
	}

	snprint(p+l, READSTR-l, "\n");
	len += readstr(offset, buf, n, p);
	free(p);

	return len;
}

static void
txstart(Ether* ether)
{
	Ctlr *ctlr;
	Block *bp;
	Des *des;
	int control;

	ctlr = ether->ctlr;
	while(ctlr->ntq < (ctlr->ntdr-1)){
		if(ctlr->setupbp){
			bp = ctlr->setupbp;
			ctlr->setupbp = 0;
			control = Ic|Set|BLEN(bp);
		}
		else{
			bp = qget(ether->oq);
			if(bp == nil)
				break;
			control = Ic|Lseg|Fseg|BLEN(bp);
		}

		ctlr->tdr[PREV(ctlr->tdrh, ctlr->ntdr)].control &= ~Ic;
		des = &ctlr->tdr[ctlr->tdrh];
		des->bp = bp;
		des->addr = PCIWADDR(bp->rp);
		des->control |= control;
		ctlr->ntq++;
		coherence();
		des->status = Own;
		csr32w(ctlr, 1, 0);
		ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr);
	}

	if(ctlr->ntq > ctlr->ntqmax)
		ctlr->ntqmax = ctlr->ntq;
}

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

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

static void
interrupt(Ureg*, void* arg)
{
	Ctlr *ctlr;
	Ether *ether;
	int len, status;
	Des *des;
	Block *bp;

	ether = arg;
	ctlr = ether->ctlr;

	while((status = csr32r(ctlr, 5)) & (Nis|Ais)){
		/*
		 * Acknowledge the interrupts and mask-out
		 * the ones that are implicitly handled.
		 */
		csr32w(ctlr, 5, status);
		status &= (ctlr->mask & ~(Nis|Ti));

		if(status & Ais){
			if(status & Tps)
				ctlr->tps++;
			if(status & Tu)
				ctlr->tu++;
			if(status & Tjt)
				ctlr->tjt++;
			if(status & Ru)
				ctlr->ru++;
			if(status & Rps)
				ctlr->rps++;
			if(status & Rwt)
				ctlr->rwt++;
			status &= ~(Ais|Rwt|Rps|Ru|Tjt|Tu|Tps);
		}

		/*
		 * Received packets.
		 */
		if(status & Ri){
			des = &ctlr->rdr[ctlr->rdrx];
			while(!(des->status & Own)){
				if(des->status & Es){
					if(des->status & Of)
						ctlr->of++;
					if(des->status & Ce)
						ctlr->ce++;
					if(des->status & Cs)
						ctlr->cs++;
					if(des->status & Tl)
						ctlr->tl++;
					if(des->status & Rf)
						ctlr->rf++;
					if(des->status & De)
						ctlr->de++;
				}
				else if(bp = iallocb(Rbsz)){
					len = ((des->status & Fl)>>16)-4;
					des->bp->wp = des->bp->rp+len;
					etheriq(ether, des->bp, 1);
					des->bp = bp;
					des->addr = PCIWADDR(bp->rp);
				}

				des->control &= Er;
				des->control |= Rbsz;
				coherence();
				des->status = Own;

				ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr);
				des = &ctlr->rdr[ctlr->rdrx];
			}
			status &= ~Ri;
		}

		/*
		 * Check the transmit side:
		 *	check for Transmit Underflow and Adjust
		 *	the threshold upwards;
		 *	free any transmitted buffers and try to
		 *	top-up the ring.
		 */
		if(status & Unf){
			ctlr->unf++;
			ilock(&ctlr->lock);
			csr32w(ctlr, 6, ctlr->csr6 & ~St);
			switch(ctlr->csr6 & Tr){
			case Tr128:
				len = Tr256;
				break;
			case Tr256:
				len = Tr512;
				break;
			case Tr512:
				len = Tr1024;
				break;
			default:
			case Tr1024:
				len = Sf;
				break;
			}
			ctlr->csr6 = (ctlr->csr6 & ~Tr)|len;
			csr32w(ctlr, 6, ctlr->csr6);
			iunlock(&ctlr->lock);
			csr32w(ctlr, 5, Tps);
			status &= ~(Unf|Tps);
		}

		ilock(&ctlr->tlock);
		while(ctlr->ntq){
			des = &ctlr->tdr[ctlr->tdri];
			if(des->status & Own)
				break;

			if(des->status & Es){
				if(des->status & Uf)
					ctlr->uf++;
				if(des->status & Ec)
					ctlr->ec++;
				if(des->status & Lc)
					ctlr->lc++;
				if(des->status & Nc)
					ctlr->nc++;
				if(des->status & Lo)
					ctlr->lo++;
				if(des->status & To)
					ctlr->to++;
				ether->oerrs++;
			}

			freeb(des->bp);
			des->control &= Er;

			ctlr->ntq--;
			ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr);
		}
		txstart(ether);
		iunlock(&ctlr->tlock);

		/*
		 * Anything left not catered for?
		 */
		if(status)
			panic("#l%d: status %8.8uX\n", ether->ctlrno, status);
	}
}

static void
ctlrinit(Ether* ether)
{
	Ctlr *ctlr;
	Des *des;
	Block *bp;
	int i;
	uchar bi[Eaddrlen*2];

	ctlr = ether->ctlr;

	/*
	 * Allocate and initialise the receive ring;
	 * allocate and initialise the transmit ring;
	 * unmask interrupts and start the transmit side;
	 * create and post a setup packet to initialise
	 * the physical ethernet address.
	 */
	ctlr->rdr = xspanalloc(ctlr->nrdr*sizeof(Des), 8*sizeof(ulong), 0);
	for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){
		des->bp = iallocb(Rbsz);
		if(des->bp == nil)
			panic("can't allocate ethernet receive ring\n");
		des->status = Own;
		des->control = Rbsz;
		des->addr = PCIWADDR(des->bp->rp);
	}
	ctlr->rdr[ctlr->nrdr-1].control |= Er;
	ctlr->rdrx = 0;
	csr32w(ctlr, 3, PCIWADDR(ctlr->rdr));

	ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0);
	ctlr->tdr[ctlr->ntdr-1].control |= Er;
	ctlr->tdrh = 0;
	ctlr->tdri = 0;
	csr32w(ctlr, 4, PCIWADDR(ctlr->tdr));

	/*
	 * Clear any bits in the Status Register (CSR5) as
	 * the PNIC has a different reset value from a true 2114x.
	 */
	ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti;
	csr32w(ctlr, 5, ctlr->mask);
	csr32w(ctlr, 7, ctlr->mask);
	ctlr->csr6 |= St;
	csr32w(ctlr, 6, ctlr->csr6);

	for(i = 0; i < Eaddrlen/2; i++){
		bi[i*4] = ether->ea[i*2];
		bi[i*4+1] = ether->ea[i*2+1];
		bi[i*4+2] = ether->ea[i*2+1];
		bi[i*4+3] = ether->ea[i*2];
	}
	bp = iallocb(Eaddrlen*2*16);
	if(bp == nil)
		panic("can't allocate ethernet setup buffer\n");
	memset(bp->rp, 0xFF, sizeof(bi));
	for(i = sizeof(bi); i < sizeof(bi)*16; i += sizeof(bi))
		memmove(bp->rp+i, bi, sizeof(bi));
	bp->wp += sizeof(bi)*16;

	ctlr->setupbp = bp;
	ether->oq = qopen(256*1024, 1, 0, 0);
	transmit(ether);
}

static void
csr9w(Ctlr* ctlr, int data)
{
	csr32w(ctlr, 9, data);
	microdelay(1);
}

static int
miimdi(Ctlr* ctlr, int n)
{
	int data, i;

	/*
	 * Read n bits from the MII Management Register.
	 */
	data = 0;
	for(i = n-1; i >= 0; i--){
		if(csr32r(ctlr, 9) & Mdi)
			data |= (1<<i);
		csr9w(ctlr, Mii|Mdc);
		csr9w(ctlr, Mii);
	}
	csr9w(ctlr, 0);

	return data;
}

static void
miimdo(Ctlr* ctlr, int bits, int n)
{
	int i, mdo;

	/*
	 * Write n bits to the MII Management Register.
	 */
	for(i = n-1; i >= 0; i--){
		if(bits & (1<<i))
			mdo = Mdo;
		else
			mdo = 0;
		csr9w(ctlr, mdo);
		csr9w(ctlr, mdo|Mdc);
		csr9w(ctlr, mdo);
	}
}

static int
miir(Ctlr* ctlr, int phyad, int regad)
{
	int data, i;

	if(ctlr->id == Pnic){
		i = 1000;
		csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18));
		do{
			microdelay(1);
			data = csr32r(ctlr, 20);
		}while((data & 0x80000000) && --i);

		if(i == 0)
			return -1;
		return data & 0xFFFF;
	}

	/*
	 * Preamble;
	 * ST+OP+PHYAD+REGAD;
	 * TA + 16 data bits.
	 */
	miimdo(ctlr, 0xFFFFFFFF, 32);
	miimdo(ctlr, 0x1800|(phyad<<5)|regad, 14);
	data = miimdi(ctlr, 18);

	if(data & 0x10000)
		return -1;

	return data & 0xFFFF;
}

static void
miiw(Ctlr* ctlr, int phyad, int regad, int data)
{
	/*
	 * Preamble;
	 * ST+OP+PHYAD+REGAD+TA + 16 data bits;
	 * Z.
	 */
	miimdo(ctlr, 0xFFFFFFFF, 32);
	data &= 0xFFFF;
	data |= (0x05<<(5+5+2+16))|(phyad<<(5+2+16))|(regad<<(2+16))|(0x02<<16);
	miimdo(ctlr, data, 32);
	csr9w(ctlr, Mdc);
	csr9w(ctlr, 0);
}

static int
sromr(Ctlr* ctlr, int r)
{
	int i, op, data, size;

	if(ctlr->id == Pnic){
		i = 1000;
		csr32w(ctlr, 19, 0x600|r);
		do{
			microdelay(1);
			data = csr32r(ctlr, 19);
		}while((data & 0x80000000) && --i);

		if(ctlr->sromsz == 0)
			ctlr->sromsz = 6;

		return csr32r(ctlr, 9) & 0xFFFF;
	}

	/*
	 * This sequence for reading a 16-bit register 'r'
	 * in the EEPROM is taken straight from Section
	 * 7.4 of the 21140 Hardware Reference Manual.
	 */
reread:
	csr9w(ctlr, Rd|Ss);
	csr9w(ctlr, Rd|Ss|Scs);
	csr9w(ctlr, Rd|Ss|Sclk|Scs);
	csr9w(ctlr, Rd|Ss);

	op = 0x06;
	for(i = 3-1; i >= 0; i--){
		data = Rd|Ss|(((op>>i) & 0x01)<<2)|Scs;
		csr9w(ctlr, data);
		csr9w(ctlr, data|Sclk);
		csr9w(ctlr, data);
	}

	/*
	 * First time through must work out the EEPROM size.
	 */
	if((size = ctlr->sromsz) == 0)
		size = 8;

	for(size = size-1; size >= 0; size--){
		data = Rd|Ss|(((r>>size) & 0x01)<<2)|Scs;
		csr9w(ctlr, data);
		csr9w(ctlr, data|Sclk);
		csr9w(ctlr, data);
		microdelay(1);
		if(!(csr32r(ctlr, 9) & Sdo))
			break;
	}

	data = 0;
	for(i = 16-1; i >= 0; i--){
		csr9w(ctlr, Rd|Ss|Sclk|Scs);
		if(csr32r(ctlr, 9) & Sdo)
			data |= (1<<i);
		csr9w(ctlr, Rd|Ss|Scs);
	}

	csr9w(ctlr, 0);

	if(ctlr->sromsz == 0){
		ctlr->sromsz = 8-size;
		goto reread;
	}

	return data & 0xFFFF;
}

static void
softreset(Ctlr* ctlr)
{
	/*
	 * Soft-reset the controller and initialise bus mode.
	 * Delay should be >= 50 PCI cycles (2×S @ 25MHz).
	 */
	csr32w(ctlr, 0, Swr);
	microdelay(10);
	csr32w(ctlr, 0, Rml|Cal16);
	delay(1);
}

static int
type5block(Ctlr* ctlr, uchar* block)
{
	int csr15, i, len;

	/*
	 * Reset or GPR sequence. Reset should be once only,
	 * before the GPR sequence.
	 * Note 'block' is not a pointer to the block head but
	 * a pointer to the data in the block starting at the
	 * reset length value so type5block can be used for the
	 * sequences contained in type 1 and type 3 blocks.
	 * The SROM docs state the 21140 type 5 block is the
	 * same as that for the 21143, but the two controllers
	 * use different registers and sequence-element lengths
	 * so the 21140 code here is a guess for a real type 5
	 * sequence.
	 */
	len = *block++;
	if(ctlr->id != Tulip3){
		for(i = 0; i < len; i++){
			csr32w(ctlr, 12, *block);
			block++;
		}
		return len;
	}

	for(i = 0; i < len; i++){
		csr15 = *block++<<16;
		csr15 |= *block++<<24;
		csr32w(ctlr, 15, csr15);
		debug("%8.8uX ", csr15);
	}
	return 2*len;
}

static int
typephylink(Ctlr* ctlr, uchar*)
{
	int an, bmcr, bmsr, csr6, x;

	/*
	 * Fail if
	 *	auto-negotiataion enabled but not complete;
	 *	no valid link established.
	 */
	bmcr = miir(ctlr, ctlr->curphyad, Bmcr);
	miir(ctlr, ctlr->curphyad, Bmsr);
	bmsr = miir(ctlr, ctlr->curphyad, Bmsr);
	debug("bmcr 0x%2.2uX bmsr 0x%2.2uX\n", bmcr, bmsr);
	if(((bmcr & 0x1000) && !(bmsr & 0x0020)) || !(bmsr & 0x0004))
		return 0;

	if(bmcr & 0x1000){
		an = miir(ctlr, ctlr->curphyad, Anar);
		an &= miir(ctlr, ctlr->curphyad, Anlpar) & 0x3E0;
		debug("an 0x%2.uX 0x%2.2uX 0x%2.2uX\n",
	    		miir(ctlr, ctlr->curphyad, Anar),
			miir(ctlr, ctlr->curphyad, Anlpar),
			an);
	
		if(an & 0x0100)
			x = 0x4000;
		else if(an & 0x0080)
			x = 0x2000;
		else if(an & 0x0040)
			x = 0x1000;
		else if(an & 0x0020)
			x = 0x0800;
		else
			x = 0;
	}
	else if((bmcr & 0x2100) == 0x2100)
		x = 0x4000;
	else if(bmcr & 0x2000){
		/*
		 * If FD capable, force it if necessary.
		 */
		if((bmsr & 0x4000) && ctlr->fd){
			miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100);
			x = 0x4000;
		}
		else
			x = 0x2000;
	}
	else if(bmcr & 0x0100)
		x = 0x1000;
	else
		x = 0x0800;

	csr6 = Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE;
	if(ctlr->fdx & x)
		csr6 |= Fd;
	if(ctlr->ttm & x)
		csr6 |= Ttm;
	debug("csr6 0x%8.8uX 0x%8.8uX 0x%8.8luX\n",
		csr6, ctlr->csr6, csr32r(ctlr, 6));
	if(csr6 != ctlr->csr6){
		ctlr->csr6 = csr6;
		csr32w(ctlr, 6, csr6);
	}

	return 1;
}

static int
typephymode(Ctlr* ctlr, uchar* block, int wait)
{
	uchar *p;
	int len, mc, nway, phyx, timeo;

	if(DEBUG){
		int i;

		len = (block[0] & ~0x80)+1;
		for(i = 0; i < len; i++)
			debug("%2.2uX ", block[i]);
		debug("\n");
	}

	if(block[1] == 1)
		len = 1;
	else if(block[1] == 3)
		len = 2;
	else
		return -1;

	/*
	 * Snarf the media capabilities, nway advertisment,
	 * FDX and TTM bitmaps.
	 */
	p = &block[5+len*block[3]+len*block[4+len*block[3]]];
	mc = *p++;
	mc |= *p++<<8;
	nway = *p++;
	nway |= *p++<<8;
	ctlr->fdx = *p++;
	ctlr->fdx |= *p++<<8;
	ctlr->ttm = *p++;
	ctlr->ttm |= *p<<8;
	debug("mc %4.4uX nway %4.4uX fdx %4.4uX ttm %4.4uX\n",
		mc, nway, ctlr->fdx, ctlr->ttm);
	USED(mc);

	phyx = block[2];
	ctlr->curphyad = ctlr->phy[phyx];

	ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE;
	//csr32w(ctlr, 6, ctlr->csr6);
	if(typephylink(ctlr, block))
		return 0;

	if(!(ctlr->phyreset & (1<<phyx))){
		debug("reset seq: len %d: ", block[3]);
		if(ctlr->type5block)
			type5block(ctlr, &ctlr->type5block[2]);
		else
			type5block(ctlr, &block[4+len*block[3]]);
		debug("\n");
		ctlr->phyreset |= (1<<phyx);
	}

	/*
	 * GPR sequence.
	 */
	debug("gpr seq: len %d: ", block[3]);
	type5block(ctlr, &block[3]);
	debug("\n");

	ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE;
	//csr32w(ctlr, 6, ctlr->csr6);
	if(typephylink(ctlr, block))
		return 0;

	/*
	 * Turn off auto-negotiation, set the auto-negotiation
	 * advertisment register then start the auto-negotiation
	 * process again.
	 */
	miiw(ctlr, ctlr->curphyad, Bmcr, 0);
	miiw(ctlr, ctlr->curphyad, Anar, nway|1);
	miiw(ctlr, ctlr->curphyad, Bmcr, 0x1000);

	if(!wait)
		return 0;

	for(timeo = 0; timeo < 30; timeo++){
		if(typephylink(ctlr, block))
			return 0;
		delay(100);
	}

	return -1;
}

static int
typesymmode(Ctlr *ctlr, uchar *block, int wait)
{
	uint gpmode, gpdata, command;

	USED(wait);
	gpmode = block[3] | ((uint) block[4] << 8);
	gpdata = block[5] | ((uint) block[6] << 8);
	command = (block[7] | ((uint) block[8] << 8)) & 0x71;
	if (command & 0x8000) {
		print("ether2114x.c: FIXME: handle type 4 mode blocks where cmd.active_invalid != 0\n");
		return -1;
	}
	csr32w(ctlr, 15, gpmode);
	csr32w(ctlr, 15, gpdata);
	ctlr->csr6 = (command & 0x71) << 18;
	csr32w(ctlr, 6, ctlr->csr6);
	return 0;
}

static int
type0link(Ctlr* ctlr, uchar* block)
{
	int m, polarity, sense;

	m = (block[3]<<8)|block[2];
	sense = 1<<((m & 0x000E)>>1);
	if(m & 0x0080)
		polarity = sense;
	else
		polarity = 0;

	return (csr32r(ctlr, 12) & sense)^polarity;
}

static int
type0mode(Ctlr* ctlr, uchar* block, int wait)
{
	int csr6, m, timeo;

	csr6 = Sc|Mbo|Hbd|Ca|Sb|TrMODE;
debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
    ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]); 
	switch(block[0]){
	default:
		break;

	case 0x04:			/* 10BASE-TFD */
	case 0x05:			/* 100BASE-TXFD */
	case 0x08:			/* 100BASE-FXFD */
		/*
		 * Don't attempt full-duplex
		 * unless explicitly requested.
		 */
		if(!ctlr->fd)
			return -1;
		csr6 |= Fd;
		break;
	}

	m = (block[3]<<8)|block[2];
	if(m & 0x0001)
		csr6 |= Ps;
	if(m & 0x0010)
		csr6 |= Ttm;
	if(m & 0x0020)
		csr6 |= Pcs;
	if(m & 0x0040)
		csr6 |= Scr;

	csr32w(ctlr, 12, block[1]);
	microdelay(10);
	csr32w(ctlr, 6, csr6);
	ctlr->csr6 = csr6;

	if(!wait)
		return 0;

	for(timeo = 0; timeo < 30; timeo++){
		if(type0link(ctlr, block))
			return 0;
		delay(100);
	}

	return -1;
}

static int
mediaxx(Ether* ether, int wait)
{
	Ctlr* ctlr;
	uchar *block;

	ctlr = ether->ctlr;
	block = ctlr->infoblock[ctlr->curk];
	if(block[0] & 0x80){
		switch(block[1]){
		default:
			return -1;
		case 0:
			if(ctlr->medium >= 0 && block[2] != ctlr->medium)
				return 0;
/* need this test? */	if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[2])
				return 0;
			if(type0mode(ctlr, block+2, wait))
				return 0;
			break;
		case 1:
			if(typephymode(ctlr, block, wait))
				return 0;
			break;
		case 3:
			if(typephymode(ctlr, block, wait))
				return 0;
			break;
		case 4:
			if(typesymmode(ctlr, block, wait))
				return 0;
			break;
		}
	}
	else{
		if(ctlr->medium >= 0 && block[0] != ctlr->medium)
			return 0;
/* need this test? */if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[0])
			return 0;
		if(type0mode(ctlr, block, wait))
			return 0;
	}

	if(ctlr->csr6){
		if(!(ctlr->csr6 & Ps) || (ctlr->csr6 & Ttm))
			return 10;
		return 100;
	}

	return 0;
}

static int
media(Ether* ether, int wait)
{
	Ctlr* ctlr;
	int k, mbps;

	ctlr = ether->ctlr;
	for(k = 0; k < ctlr->k; k++){
		mbps = mediaxx(ether, wait);
		if(mbps > 0)
			return mbps;
		if(ctlr->curk == 0)
			ctlr->curk = ctlr->k-1;
		else
			ctlr->curk--;
	}

	return 0;
}

static char* mediatable[9] = {
	"10BASE-T",				/* TP */
	"10BASE-2",				/* BNC */
	"10BASE-5",				/* AUI */
	"100BASE-TX",
	"10BASE-TFD",
	"100BASE-TXFD",
	"100BASE-T4",
	"100BASE-FX",
	"100BASE-FXFD",
};

static uchar en1207[] = {		/* Accton EN1207-COMBO */
	0x00, 0x00, 0xE8,		/* [0]  vendor ethernet code */
	0x00,				/* [3]  spare */

	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
	0x1F,				/* [6]  general purpose control */
	2,				/* [7]  block count */

	0x00,				/* [8]  media code (10BASE-TX) */
	0x0B,				/* [9]  general purpose port data */
	0x9E, 0x00,			/* [10] command (LSB+MSB = 0x009E) */

	0x03,				/* [8]  media code (100BASE-TX) */
	0x1B,				/* [9]  general purpose port data */
	0x6D, 0x00,			/* [10] command (LSB+MSB = 0x006D) */

					/* There is 10BASE-2 as well, but... */
};

static uchar ana6910fx[] = {		/* Adaptec (Cogent) ANA-6910FX */
	0x00, 0x00, 0x92,		/* [0]  vendor ethernet code */
	0x00,				/* [3]  spare */

	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
	0x3F,				/* [6]  general purpose control */
	1,				/* [7]  block count */

	0x07,				/* [8]  media code (100BASE-FX) */
	0x03,				/* [9]  general purpose port data */
	0x2D, 0x00			/* [10] command (LSB+MSB = 0x000D) */
};

static uchar smc9332[] = {		/* SMC 9332 */
	0x00, 0x00, 0xC0,		/* [0]  vendor ethernet code */
	0x00,				/* [3]  spare */

	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
	0x1F,				/* [6]  general purpose control */
	2,				/* [7]  block count */

	0x00,				/* [8]  media code (10BASE-TX) */
	0x00,				/* [9]  general purpose port data */
	0x9E, 0x00,			/* [10] command (LSB+MSB = 0x009E) */

	0x03,				/* [8]  media code (100BASE-TX) */
	0x09,				/* [9]  general purpose port data */
	0x6D, 0x00,			/* [10] command (LSB+MSB = 0x006D) */
};

static uchar* leaf21140[] = {
	en1207,				/* Accton EN1207-COMBO */
	ana6910fx,			/* Adaptec (Cogent) ANA-6910FX */
	smc9332,			/* SMC 9332 */
	nil,
};

/*
 * Copied to ctlr->srom at offset 20.
 */
static uchar leafpnic[] = {
	0x00, 0x00, 0x00, 0x00,		/* MAC address */
	0x00, 0x00,
	0x00,				/* controller 0 device number */
	0x1E, 0x00,			/* controller 0 info leaf offset */
	0x00,				/* reserved */
	0x00, 0x08,			/* selected connection type */
	0x00,				/* general purpose control */
	0x01,				/* block count */

	0x8C,				/* format indicator and count */
	0x01,				/* block type */
	0x00,				/* PHY number */
	0x00,				/* GPR sequence length */
	0x00,				/* reset sequence length */
	0x00, 0x78,			/* media capabilities */
	0xE0, 0x01,			/* Nway advertisment */
	0x00, 0x50,			/* FDX bitmap */
	0x00, 0x18,			/* TTM bitmap */
};

static int
srom(Ctlr* ctlr)
{
	int i, k, oui, phy, x;
	uchar *p;

	/*
	 * This is a partial decoding of the SROM format described in
	 * 'Digital Semiconductor 21X4 Serial ROM Format, Version 4.05,
	 * 2-Mar-98'. Only the 2114[03] are handled, support for other
	 * controllers can be added as needed.
	 * Do a dummy read first to get the size and allocate ctlr->srom.
	 */
	sromr(ctlr, 0);
	if(ctlr->srom == nil)
		ctlr->srom = malloc((1<<ctlr->sromsz)*sizeof(ushort));
	for(i = 0; i < (1<<ctlr->sromsz); i++){
		x = sromr(ctlr, i);
		ctlr->srom[2*i] = x;
		ctlr->srom[2*i+1] = x>>8;
	}

	/*
	 * There are 2 SROM layouts:
	 *	e.g. Digital EtherWORKS	station address at offset 20;
	 *				this complies with the 21140A SROM
	 *				application note from Digital;
	 * 	e.g. SMC9332		station address at offset 0 followed by
	 *				2 additional bytes, repeated at offset
	 *				6; the 8 bytes are also repeated in
	 *				reverse order at offset 8.
	 * To check which it is, read the SROM and check for the repeating
	 * patterns of the non-compliant cards; if that fails use the one at
	 * offset 20.
	 */
	ctlr->sromea = ctlr->srom;
	for(i = 0; i < 8; i++){
		x = ctlr->srom[i];
		if(x != ctlr->srom[15-i] || x != ctlr->srom[16+i]){
			ctlr->sromea = &ctlr->srom[20];
			break;
		}
	}

	/*
	 * Fake up the SROM for the PNIC.
	 * It looks like a 21140 with a PHY.
	 * The MAC address is byte-swapped in the orginal SROM data.
	 */
	if(ctlr->id == Pnic){
		memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic));
		for(i = 0; i < Eaddrlen; i += 2){
			ctlr->srom[20+i] = ctlr->srom[i+1];
			ctlr->srom[20+i+1] = ctlr->srom[i];
		}
	}

	/*
	 * Next, try to find the info leaf in the SROM for media detection.
	 * If it's a non-conforming card try to match the vendor ethernet code
	 * and point p at a fake info leaf with compact 21140 entries.
	 */
	if(ctlr->sromea == ctlr->srom){
		p = nil;
		for(i = 0; leaf21140[i] != nil; i++){
			if(memcmp(leaf21140[i], ctlr->sromea, 3) == 0){
				p = &leaf21140[i][4];
				break;
			}
		}
		if(p == nil)
			return -1;
	}
	else
		p = &ctlr->srom[(ctlr->srom[28]<<8)|ctlr->srom[27]];

	/*
	 * Set up the info needed for later media detection.
	 * For the 21140, set the general-purpose mask in CSR12.
	 * The info block entries are stored in order of increasing
	 * precedence, so detection will work backwards through the
	 * stored indexes into ctlr->srom.
	 * If an entry is found which matches the selected connection
	 * type, save the index. Otherwise, start at the last entry.
	 * If any MII entries are found (type 1 and 3 blocks), scan
	 * for PHYs.
	 */
	ctlr->leaf = p;
	ctlr->sct = *p++;
	ctlr->sct |= *p++<<8;
	if(ctlr->id != Tulip3){
		csr32w(ctlr, 12, Gpc|*p++);
		delay(200);
	}
	ctlr->k = *p++;
	if(ctlr->k >= nelem(ctlr->infoblock))
		ctlr->k = nelem(ctlr->infoblock)-1;
	ctlr->sctk = ctlr->k-1;
	phy = 0;
	for(k = 0; k < ctlr->k; k++){
		ctlr->infoblock[k] = p;
		/*
		 * The RAMIX PMC665 has a badly-coded SROM,
		 * hence the test for 21143 and type 3.
		 */
		if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){
			*p |= 0x80;
			if(*(p+1) == 1 || *(p+1) == 3)
				phy = 1;
			if(*(p+1) == 5)
				ctlr->type5block = p;
			p += (*p & ~0x80)+1;
		}
		else{
			debug("type0: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
				p[0], p[1], p[2], p[3]); 
			if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF))
				ctlr->sctk = k;
			p += 4;
		}
	}
	ctlr->curk = ctlr->sctk;
	debug("sct 0x%uX medium 0x%uX k %d curk %d phy %d\n",
		ctlr->sct, ctlr->medium, ctlr->k, ctlr->curk, phy);

	if(phy){
		x = 0;
		for(k = 0; k < nelem(ctlr->phy); k++){
			if((oui = miir(ctlr, k, 2)) == -1 || oui == 0)
				continue;
			if(DEBUG){
				oui = (oui & 0x3FF)<<6;
				oui |= miir(ctlr, k, 3)>>10;
				miir(ctlr, k, 1);
				debug("phy%d: index %d oui %uX reg1 %uX\n",
					x, k, oui, miir(ctlr, k, 1));
				USED(oui);
			}
			ctlr->phy[x] = k;
		}
	}

	ctlr->fd = 0;
	ctlr->medium = -1;

	return 0;
}

static void
dec2114xpci(void)
{
	Ctlr *ctlr;
	Pcidev *p;
	int x;

	p = nil;
	while(p = pcimatch(p, 0, 0)){
		if(p->ccrb != 0x02 || p->ccru != 0)
			continue;
		switch((p->did<<16)|p->vid){
		default:
			continue;

		case Tulip3:			/* 21143 */
			/*
			 * Exit sleep mode.
			 */
			x = pcicfgr32(p, 0x40);
			x &= ~0xc0000000;
			pcicfgw32(p, 0x40, x);
			/*FALLTHROUGH*/

		case Pnic:			/* PNIC */
		case Pnic2:			/* PNIC-II */
		case Tulip0:			/* 21140 */
			break;
		}

		/*
		 * bar[0] is the I/O port register address and
		 * bar[1] is the memory-mapped register address.
		 */
		ctlr = malloc(sizeof(Ctlr));
		ctlr->port = p->mem[0].bar & ~0x01;
		ctlr->pcidev = p;
		ctlr->id = (p->did<<16)|p->vid;

		if(ioalloc(ctlr->port, p->mem[0].size, 0, "dec2114x") < 0){
			print("dec2114x: port 0x%uX in use\n", ctlr->port);
			free(ctlr);
			continue;
		}

		/*
		 * Some cards (e.g. ANA-6910FX) seem to need the Ps bit
		 * set or they don't always work right after a hardware
		 * reset.
		 */
		csr32w(ctlr, 6, Mbo|Ps);
		softreset(ctlr);

		if(srom(ctlr)){
			iofree(ctlr->port);
			free(ctlr);
			continue;
		}

		switch(ctlr->id){
		default:
			break;

		case Pnic:			/* PNIC */
			/*
			 * Turn off the jabber timer.
			 */
			csr32w(ctlr, 15, 0x00000001);
			break;
		}

		if(ctlrhead != nil)
			ctlrtail->next = ctlr;
		else
			ctlrhead = ctlr;
		ctlrtail = ctlr;
	}
}

static int
reset(Ether* ether)
{
	Ctlr *ctlr;
	int i, x;
	uchar ea[Eaddrlen];
	static int scandone;

	if(scandone == 0){
		dec2114xpci();
		scandone = 1;
	}

	/*
	 * Any adapter matches if no ether->port is supplied,
	 * otherwise the ports must match.
	 */
	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
		if(ctlr->active)
			continue;
		if(ether->port == 0 || ether->port == ctlr->port){
			ctlr->active = 1;
			break;
		}
	}
	if(ctlr == nil)
		return -1;

	ether->ctlr = ctlr;
	ether->port = ctlr->port;
	ether->irq = ctlr->pcidev->intl;
	ether->tbdf = ctlr->pcidev->tbdf;

	/*
	 * Check if the adapter's station address is to be overridden.
	 * If not, read it from the EEPROM and set in ether->ea prior to
	 * loading the station address in the hardware.
	 */
	memset(ea, 0, Eaddrlen);
	if(memcmp(ea, ether->ea, Eaddrlen) == 0)
		memmove(ether->ea, ctlr->sromea, Eaddrlen);

	/*
	 * Look for a medium override in case there's no autonegotiation
	 * (no MII) or the autonegotiation fails.
	 */
	for(i = 0; i < ether->nopt; i++){
		if(cistrcmp(ether->opt[i], "FD") == 0){
			ctlr->fd = 1;
			continue;
		}
		for(x = 0; x < nelem(mediatable); x++){
			debug("compare <%s> <%s>\n", mediatable[x],
				ether->opt[i]);
			if(cistrcmp(mediatable[x], ether->opt[i]))
				continue;
			ctlr->medium = x;
	
			switch(ctlr->medium){
			default:
				ctlr->fd = 0;
				break;
	
			case 0x04:		/* 10BASE-TFD */
			case 0x05:		/* 100BASE-TXFD */
			case 0x08:		/* 100BASE-FXFD */
				ctlr->fd = 1;
				break;
			}
			break;
		}
	}

	ether->mbps = media(ether, 1);

	/*
	 * Initialise descriptor rings, ethernet address.
	 */
	ctlr->nrdr = Nrde;
	ctlr->ntdr = Ntde;
	pcisetbme(ctlr->pcidev);
	ctlrinit(ether);

	/*
	 * Linkage to the generic ethernet driver.
	 */
	ether->attach = attach;
	ether->transmit = transmit;
	ether->interrupt = interrupt;
	ether->ifstat = ifstat;

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

	return 0;
}

void
ether2114xlink(void)
{
	addethercard("21140",  reset);
	addethercard("2114x",  reset);
}
#include "../pc/ether2114x.c"

M alphapc/etherif.h => alphapc/etherif.h +0 -1
@@ 13,7 13,6 @@ struct Ether {
	int	minmtu;
	int 	maxmtu;
	uchar	ea[Eaddrlen];
	int	encry;

	void	(*attach)(Ether*);	/* filled in by reset routine */
	void	(*transmit)(Ether*);

M alphapc/mouse.c => alphapc/mouse.c +1 -311
@@ 1,311 1,1 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"

#define	Image	IMAGE
#include <draw.h>
#include <memdraw.h>
#include <cursor.h>
#include "screen.h"

/*
 *  mouse types
 */
enum
{
	Mouseother=	0,
	Mouseserial=	1,
	MousePS2=	2,
};
static int mousetype;
static int intellimouse;
static int packetsize;
static int resolution;
static int accelerated;

enum
{
	CMaccelerated,
	CMintellimouse,
	CMlinear,
	CMps2,
	CMps2intellimouse,
	CMres,
	CMreset,
	CMserial,
};

static Cmdtab mousectlmsg[] =
{
	CMaccelerated,		"accelerated",		0,
	CMintellimouse,		"intellimouse",		1,
	CMlinear,		"linear",		1,
	CMps2,			"ps2",			1,
	CMps2intellimouse,	"ps2intellimouse",	1,
	CMres,			"res",			0,
	CMreset,		"reset",		1,
	CMserial,		"serial",		0,
};

/*
 *  setup a serial mouse
 */
static void
serialmouse(int port, char *type, int setspeed)
{
#ifdef notdef
	if(mousetype == Mouseserial)
		error(Emouseset);

	if(port >= 3 || port < 0)
		error(Ebadarg);

	/* set up /dev/eia? as the mouse */
	if(setspeed)
		setspeed = 1200;
	if(type && *type == 'M')
		uartspecial(port, setspeed, 0, 0, m3mouseputc);
	else
		uartspecial(port, setspeed, 0, 0, mouseputc);
	mousetype = Mouseserial;
	packetsize = 3;
#else
	error("serial mouse not supported yet");
	USED(port, type, setspeed);
#endif /* notdef */
}

/*
 *  ps/2 mouse message is three bytes
 *
 *	byte 0 -	0 0 SDY SDX 1 M R L
 *	byte 1 -	DX
 *	byte 2 -	DY
 *
 *  shift & right button is the same as middle button
 *
 * Intellimouse and AccuPoint with extra buttons deliver
 *	byte 3 -	00 or 01 or FF according to extra button state.
 * extra buttons are mapped in this code to buttons 4 and 5.
 * AccuPoint generates repeated events for these buttons;
*  it and Intellimouse generate 'down' events only, so
 * user-level code is required to generate button 'up' events
 * if they are needed by the application.
 * Also on laptops with AccuPoint AND external mouse, the
 * controller may deliver 3 or 4 bytes according to the type
 * of the external mouse; code must adapt.
 *
 * On the NEC Versa series (and perhaps others?) we seem to
 * lose a byte from the packet every once in a while, which
 * means we lose where we are in the instruction stream.
 * To resynchronize, if we get a byte more than two seconds
 * after the previous byte, we assume it's the first in a packet.
 */
static void
ps2mouseputc(int c, int shift)
{
	static short msg[4];
	static int nb;
	static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 3, 2, 3, 6, 7 };
	static ulong lasttick;
	ulong m;
	int buttons, dx, dy;

	/*
	 * Resynchronize in stream with timing; see comment above.
	 */
	m = MACHP(0)->ticks;
	if(TK2SEC(m - lasttick) > 2)
		nb = 0;
	lasttick = m;

	/* 
	 *  check byte 0 for consistency
	 */
	if(nb==0 && (c&0xc8)!=0x08)
		if(intellimouse && (c==0x00 || c==0x01 || c==0xFF)){
			/* last byte of 4-byte packet */
			packetsize = 4;
			return;
		}

	msg[nb] = c;
	if(++nb == packetsize){
		nb = 0;
		if(msg[0] & 0x10)
			msg[1] |= 0xFF00;
		if(msg[0] & 0x20)
			msg[2] |= 0xFF00;

		buttons = b[(msg[0]&7) | (shift ? 8 : 0)];
		if(intellimouse && packetsize==4){
			if((msg[3]&0xc8) == 0x08){
				/* first byte of 3-byte packet */
				packetsize = 3;
				msg[0] = msg[3];
				nb = 1;
				/* fall through to emit previous packet */
			}else{
				/* the AccuPoint on the Toshiba 34[48]0CT encodes extra buttons as 4 and 5 */
				/* they repeat and don't release, however, so user-level timing code is required */
				if(msg[3] == 0xFF) 
					buttons |= 1<<3;
				if(msg[3] == 0x01) 
					buttons |= 1<<4;
			}
		}
		dx = msg[1];
		dy = -msg[2];
		mousetrack(dx, dy, buttons, TK2MS(MACHP(0)->ticks));
	}
	return;
}

/*
 *  set up a ps2 mouse
 */
static void
ps2mouse(void)
{
	if(mousetype == MousePS2)
		return;

	i8042auxenable(ps2mouseputc);
	/* make mouse streaming, enabled */
	i8042auxcmd(0xEA);
	i8042auxcmd(0xF4);

	mousetype = MousePS2;
	packetsize = 3;
}

static void
setaccelerated(int x)
{
	accelerated = x;
	switch(mousetype){
	case MousePS2:
		i8042auxcmd(0xE7);
		break;
	default:
		mouseaccelerate(x);
		break;
	}
}

static void
setlinear(void)
{
	accelerated = 0;
	switch(mousetype){
	case MousePS2:
		i8042auxcmd(0xE6);
		break;
	default:
		mouseaccelerate(0);
		break;
	}
}

static void
setres(int n)
{
	resolution = n;
	switch(mousetype){
	case MousePS2:
		i8042auxcmd(0xE8);
		i8042auxcmd(n);
		break;
	}
}

static void
setintellimouse(void)
{
	intellimouse = 1;
	packetsize = 4;
	switch(mousetype){
	case MousePS2:
		i8042auxcmd(0xF3);	/* set sample */
		i8042auxcmd(0xC8);
		i8042auxcmd(0xF3);	/* set sample */
		i8042auxcmd(0x64);
		i8042auxcmd(0xF3);	/* set sample */
		i8042auxcmd(0x50);
		break;
	}
}

static void
resetmouse(void)
{
	packetsize = 3;
	switch(mousetype){
	case MousePS2:
		i8042auxcmd(0xF6);
		i8042auxcmd(0xEA);	/* streaming */
		i8042auxcmd(0xE8);	/* set resolution */
		i8042auxcmd(3);
		i8042auxcmd(0xF4);	/* enabled */
		break;
	}
}

void
mousectl(Cmdbuf *cb)
{
	Cmdtab *ct;

	ct = lookupcmd(cb, mousectlmsg, nelem(mousectlmsg));
	switch(ct->index){
	case CMaccelerated:
		setaccelerated(cb->nf == 1 ? 1 : atoi(cb->f[1]));
		break;
	case CMintellimouse:
		setintellimouse();
		break;
	case CMlinear:
		setlinear();
		break;
	case CMps2:
		ps2mouse();
		break;
	case CMps2intellimouse:
		ps2mouse();
		setintellimouse();
		break;
	case CMres:
		if(cb->nf >= 2)
			setres(atoi(cb->f[1]));
		else
			setres(1);
		break;
	case CMreset:
		resetmouse();
		if(accelerated)
			setaccelerated(accelerated);
		if(resolution)
			setres(resolution);
		if(intellimouse)
			setintellimouse();
		break;
	case CMserial:
		switch(cb->nf){
		case 1:
			serialmouse(atoi(cb->f[0]+6), 0, 1);
			break;
		case 2:
			serialmouse(atoi(cb->f[1]), 0, 0);
			break;
		case 3:
		default:
			serialmouse(atoi(cb->f[1]), cb->f[2], 0);
			break;
		}
		break;
	}
}
#include "../pc/mouse.c"

M alphapc/screen.h => alphapc/screen.h +5 -4
@@ 125,9 125,10 @@ enum {
extern void mousectl(Cmdbuf*);

/* screen.c */
extern int		hwaccel;	/* use hw acceleration; default on */
extern int		hwblank;	/* use hw blanking; default on */
extern uchar* attachscreen(Rectangle*, ulong*, int*, int*, int*);
extern int	hwaccel;	/* use hw acceleration; default on */
extern int	hwblank;	/* use hw blanking; default on */
extern void	addvgaseg(char*, ulong, ulong);
extern uchar*	attachscreen(Rectangle*, ulong*, int*, int*, int*);
extern void	flushmemscreen(Rectangle);
extern int	cursoron(int);
extern void	cursoroff(int);


@@ 139,7 140,7 @@ extern void	blankscreen(int);

/* devdraw.c */
extern void	deletescreenimage(void);
extern int		drawhasclients(void);
extern int	drawhasclients(void);
extern ulong	blanktime;
/* vga.c */
extern void	vgascreenwin(VGAscr*);

M alphapc/uarti8250.c => alphapc/uarti8250.c +1 -648
@@ 1,648 1,1 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"

/*
 * 8250 UART and compatibles.
 */
enum {
	Uart0		= 0x3F8,	/* COM1 */
	Uart0IRQ	= 4,
	Uart1		= 0x2F8,	/* COM2 */
	Uart1IRQ	= 3,

	UartFREQ	= 1843200,
};

enum {					/* I/O ports */
	Rbr		= 0,		/* Receiver Buffer (RO) */
	Thr		= 0,		/* Transmitter Holding (WO) */
	Ier		= 1,		/* Interrupt Enable */
	Iir		= 2,		/* Interrupt Identification (RO) */
	Fcr		= 2,		/* FIFO Control (WO) */
	Lcr		= 3,		/* Line Control */
	Mcr		= 4,		/* Modem Control */
	Lsr		= 5,		/* Line Status */
	Msr		= 6,		/* Modem Status */
	Scr		= 7,		/* Scratch Pad */
	Dll		= 0,		/* Divisor Latch LSB */
	Dlm		= 1,		/* Divisor Latch MSB */
};

enum {					/* Ier */
	Erda		= 0x01,		/* Enable Received Data Available */
	Ethre		= 0x02,		/* Enable Thr Empty */
	Erls		= 0x04,		/* Enable Receiver Line Status */
	Ems		= 0x08,		/* Enable Modem Status */
};

enum {					/* Iir */
	Ims		= 0x00,		/* Ms interrupt */
	Ip		= 0x01,		/* Interrupt Pending (not) */
	Ithre		= 0x02,		/* Thr Empty */
	Irda		= 0x04,		/* Received Data Available */
	Irls		= 0x06,		/* Receiver Line Status */
	Ictoi		= 0x0C,		/* Character Time-out Indication */
	IirMASK		= 0x3F,
	Ife		= 0xC0,		/* FIFOs enabled */
};

enum {					/* Fcr */
	FIFOena		= 0x01,		/* FIFO enable */
	FIFOrclr	= 0x02,		/* clear Rx FIFO */
	FIFOtclr	= 0x04,		/* clear Tx FIFO */
	FIFO1		= 0x00,		/* Rx FIFO trigger level 1 byte */
	FIFO4		= 0x40,		/*	4 bytes */
	FIFO8		= 0x80,		/*	8 bytes */
	FIFO14		= 0xC0,		/*	14 bytes */
};

enum {					/* Lcr */
	Wls5		= 0x00,		/* Word Length Select 5 bits/byte */
	Wls6		= 0x01,		/*	6 bits/byte */
	Wls7		= 0x02,		/*	7 bits/byte */
	Wls8		= 0x03,		/*	8 bits/byte */
	WlsMASK		= 0x03,
	Stb		= 0x04,		/* 2 stop bits */
	Pen		= 0x08,		/* Parity Enable */
	Eps		= 0x10,		/* Even Parity Select */
	Stp		= 0x20,		/* Stick Parity */
	Brk		= 0x40,		/* Break */
	Dlab		= 0x80,		/* Divisor Latch Access Bit */
};

enum {					/* Mcr */
	Dtr		= 0x01,		/* Data Terminal Ready */
	Rts		= 0x02,		/* Ready To Send */
	Out1		= 0x04,		/* no longer in use */
	Ie		= 0x08,		/* IRQ Enable */
	Dm		= 0x10,		/* Diagnostic Mode loopback */
};

enum {					/* Lsr */
	Dr		= 0x01,		/* Data Ready */
	Oe		= 0x02,		/* Overrun Error */
	Pe		= 0x04,		/* Parity Error */
	Fe		= 0x08,		/* Framing Error */
	Bi		= 0x10,		/* Break Interrupt */
	Thre		= 0x20,		/* Thr Empty */
	Temt		= 0x40,		/* Tramsmitter Empty */
	FIFOerr		= 0x80,		/* error in receiver FIFO */
};

enum {					/* Msr */
	Dcts		= 0x01,		/* Delta Cts */
	Ddsr		= 0x02,		/* Delta Dsr */
	Teri		= 0x04,		/* Trailing Edge of Ri */
	Ddcd		= 0x08,		/* Delta Dcd */
	Cts		= 0x10,		/* Clear To Send */
	Dsr		= 0x20,		/* Data Set Ready */
	Ri		= 0x40,		/* Ring Indicator */
	Dcd		= 0x80,		/* Data Set Ready */
};

typedef struct Ctlr {
	int	io;
	int	irq;
	int	tbdf;
	int	iena;

	uchar	sticky[8];

	Lock;
	int	fifo;
	int	fena;
} Ctlr;

extern PhysUart i8250physuart;

static Ctlr i8250ctlr[2] = {
{	.io	= Uart0,
	.irq	= Uart0IRQ,
	.tbdf	= BUSUNKNOWN, },

{	.io	= Uart1,
	.irq	= Uart1IRQ,
	.tbdf	= BUSUNKNOWN, },
};

static Uart i8250uart[2] = {
{	.regs	= &i8250ctlr[0],
	.name	= "COM1",
	.freq	= UartFREQ,
	.phys	= &i8250physuart,
	.special=0,
	.next	= &i8250uart[1], },

{	.regs	= &i8250ctlr[1],
	.name	= "COM2",
	.freq	= UartFREQ,
	.phys	= &i8250physuart,
	.special=0,
	.next	= nil, },
};

static Uart* consuart;

#define csr8r(c, r)	inb((c)->io+(r))
#define csr8w(c, r, v)	outb((c)->io+(r), (c)->sticky[(r)]|(v))

static long
i8250status(Uart* uart, void* buf, long n, long offset)
{
	char *p;
	Ctlr *ctlr;
	uchar ier, lcr, mcr, msr;

	ctlr = uart->regs;
	p = malloc(READSTR);
	mcr = ctlr->sticky[Mcr];
	msr = csr8r(ctlr, Msr);
	ier = ctlr->sticky[Ier];
	lcr = ctlr->sticky[Lcr];
	snprint(p, READSTR,
		"b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n"
		"dev(%d) type(%d) framing(%d) overruns(%d)%s%s%s%s\n",

		uart->baud,
		uart->hup_dcd, 
		(msr & Dsr) != 0,
		uart->hup_dsr,
		(lcr & WlsMASK) + 5,
		(ier & Ems) != 0, 
		(lcr & Pen) ? ((lcr & Eps) ? 'e': 'o'): 'n',
		(mcr & Rts) != 0,
		(lcr & Stb) ? 2: 1,
		ctlr->fena,

		uart->dev,
		uart->type,
		uart->ferr,
		uart->oerr, 
		(msr & Cts) ? " cts": "",
		(msr & Dsr) ? " dsr": "",
		(msr & Dcd) ? " dcd": "",
		(msr & Ri) ? " ring": ""
	);
	n = readstr(offset, buf, n, p);
	free(p);

	return n;
}

static void
i8250fifo(Uart* uart, int on)
{
	int i;
	Ctlr *ctlr;

	/*
	 * Toggle FIFOs:
	 * if none, do nothing;
	 * reset the Rx and Tx FIFOs;
	 * empty the Rx buffer and clear any interrupt conditions;
	 * if enabling, try to turn them on.
	 */
	ctlr = uart->regs;

	ilock(ctlr);
	if(!ctlr->fifo){
		csr8w(ctlr, Fcr, FIFOtclr|FIFOrclr);
		for(i = 0; i < 16; i++){
			csr8r(ctlr, Iir);
			csr8r(ctlr, Rbr);
		}
  
		ctlr->fena = 0;
		if(on){
			csr8w(ctlr, Fcr, FIFO4|FIFOena);
			if(!(csr8r(ctlr, Iir) & Ife))
				ctlr->fifo = 1;
			ctlr->fena = 1;
		}
	}
	iunlock(ctlr);
}

static void
i8250dtr(Uart* uart, int on)
{
	Ctlr *ctlr;

	/*
	 * Toggle DTR.
	 */
	ctlr = uart->regs;
	if(on)
		ctlr->sticky[Mcr] |= Dtr;
	else
		ctlr->sticky[Mcr] &= ~Dtr;
	csr8w(ctlr, Mcr, 0);
}

static void
i8250rts(Uart* uart, int on)
{
	Ctlr *ctlr;

	/*
	 * Toggle RTS.
	 */
	ctlr = uart->regs;
	if(on)
		ctlr->sticky[Mcr] |= Rts;
	else
		ctlr->sticky[Mcr] &= ~Rts;
	csr8w(ctlr, Mcr, 0);
}

static void
i8250modemctl(Uart* uart, int on)
{
	Ctlr *ctlr;

	ctlr = uart->regs;
	ilock(&uart->tlock);
	if(on){
		ctlr->sticky[Ier] |= Ems;
		csr8w(ctlr, Ier, 0);
		uart->modem = 1;
		uart->cts = csr8r(ctlr, Msr) & Cts;
	}
	else{
		ctlr->sticky[Ier] &= ~Ems;
		csr8w(ctlr, Ier, 0);
		uart->modem = 0;
		uart->cts = 1;
	}
	iunlock(&uart->tlock);

	/* modem needs fifo */
	(*uart->phys->fifo)(uart, on);
}

static int
i8250parity(Uart* uart, int parity)
{
	int lcr;
	Ctlr *ctlr;

	ctlr = uart->regs;
	lcr = ctlr->sticky[Lcr] & ~(Eps|Pen);

	switch(parity){
	case 'e':
		lcr |= Eps|Pen;
		break;
	case 'o':
		lcr |= Pen;
		break;
	case 'n':
	default:
		break;
	}
	ctlr->sticky[Lcr] = lcr;
	csr8w(ctlr, Lcr, 0);

	uart->parity = parity;

	return 0;
}

static int
i8250stop(Uart* uart, int stop)
{
	int lcr;
	Ctlr *ctlr;

	ctlr = uart->regs;
	lcr = ctlr->sticky[Lcr] & ~Stb;

	switch(stop){
	case 1:
		break;
	case 2:
		lcr |= Stb;
		break;
	default:
		return -1;
	}
	ctlr->sticky[Lcr] = lcr;
	csr8w(ctlr, Lcr, 0);

	uart->stop = stop;

	return 0;
}

static int
i8250bits(Uart* uart, int bits)
{
	int lcr;
	Ctlr *ctlr;

	ctlr = uart->regs;
	lcr = ctlr->sticky[Lcr] & ~WlsMASK;

	switch(bits){
	case 5:
		lcr |= Wls5;
		break;
	case 6:
		lcr |= Wls6;
		break;
	case 7:
		lcr |= Wls7;
		break;
	case 8:
		lcr |= Wls8;
		break;
	default:
		return -1;
	}
	ctlr->sticky[Lcr] = lcr;
	csr8w(ctlr, Lcr, 0);

	uart->bits = bits;

	return 0;
}

static int
i8250baud(Uart* uart, int baud)
{
	ulong bgc;
	Ctlr *ctlr;

	/*
	 * Set the Baud rate by calculating and setting the Baud rate
	 * Generator Constant. This will work with fairly non-standard
	 * Baud rates.
	 */
	if(uart->freq == 0 || baud <= 0)
		return -1;
	bgc = (uart->freq+8*baud-1)/(16*baud);

	ctlr = uart->regs;
	csr8w(ctlr, Lcr, Dlab);
	outb(ctlr->io+Dlm, bgc>>8);
	outb(ctlr->io+Dll, bgc);
	csr8w(ctlr, Lcr, 0);

	uart->baud = baud;

	return 0;
}

static void
i8250break(Uart* uart, int ms)
{
	Ctlr *ctlr;

	/*
	 * Send a break.
	 */
	if(ms == 0)
		ms = 200;

	ctlr = uart->regs;
	csr8w(ctlr, Lcr, Brk);
	tsleep(&up->sleep, return0, 0, ms);
	csr8w(ctlr, Lcr, 0);
}

static void
i8250kick(Uart* uart)
{
	int i;
	Ctlr *ctlr;

	if(uart->cts == 0 || uart->blocked)
		return;

	/*
	 *  128 here is an arbitrary limit to make sure
	 *  we don't stay in this loop too long.  If the
	 *  chip's output queue is longer than 128, too
	 *  bad -- presotto
	 */
	ctlr = uart->regs;
	for(i = 0; i < 128; i++){
		if(!(csr8r(ctlr, Lsr) & Thre))
			break;
		if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
			break;
		outb(ctlr->io+Thr, *(uart->op++));
	}
}

static void
i8250interrupt(Ureg*, void* arg)
{
	Ctlr *ctlr;
	Uart *uart;
	int iir, lsr, old, r;

	uart = arg;

	ctlr = uart->regs;
	for(iir = csr8r(ctlr, Iir); !(iir & Ip); iir = csr8r(ctlr, Iir)){
		switch(iir & IirMASK){
		case Ims:		/* Ms interrupt */
			r = csr8r(ctlr, Msr);
			if(r & Dcts){
				ilock(&uart->tlock);
				old = uart->cts;
				uart->cts = r & Cts;
				if(old == 0 && uart->cts)
					uart->ctsbackoff = 2;
				iunlock(&uart->tlock);
			}
		 	if(r & Ddsr){
				old = r & Dsr;
				if(uart->hup_dsr && uart->dsr && !old)
					uart->dohup = 1;
				uart->dsr = old;
			}
		 	if(r & Ddcd){
				old = r & Dcd;
				if(uart->hup_dcd && uart->dcd && !old)
					uart->dohup = 1;
				uart->dcd = old;
			}
			break;
		case Ithre:		/* Thr Empty */
			uartkick(uart);
			break;
		case Irda:		/* Received Data Available */
		case Ictoi:		/* Character Time-out Indication */
			/*
			 * Consume any received data.
			 * If the received byte came in with a break,
			 * parity or framing error, throw it away;
			 * overrun is an indication that something has
			 * already been tossed.
			 */
			while((lsr = csr8r(ctlr, Lsr)) & Dr){
				if(lsr & Oe)
					uart->oerr++;
				if(lsr & Pe)
					uart->perr++;
				if(lsr & Fe)
					uart->ferr++;
				r = csr8r(ctlr, Rbr);
				if(!(lsr & (Bi|Fe|Pe)))
					uartrecv(uart, r);
			}
			break;
		default:
			iprint("weird uart interrupt 0x%2.2uX\n", iir);
			break;
		}
	}
}

static void
i8250disable(Uart* uart)
{
	Ctlr *ctlr;

	/*
 	 * Turn off DTR and RTS, disable interrupts and fifos.
	 */
	(*uart->phys->dtr)(uart, 0);
	(*uart->phys->rts)(uart, 0);
	(*uart->phys->fifo)(uart, 0);

	ctlr = uart->regs;
	ctlr->sticky[Ier] = 0;
	csr8w(ctlr, Ier, 0);
}

static void
i8250enable(Uart* uart, int ie)
{
	Ctlr *ctlr;

	/*
 	 * Enable interrupts and turn on DTR and RTS.
	 * Be careful if this is called to set up a polled serial line
	 * early on not to try to enable interrupts as interrupt-
	 * -enabling mechanisms might not be set up yet.
	 */
	ctlr = uart->regs;
	if(ie){
		if(ctlr->iena == 0){
			intrenable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name);
			ctlr->iena = 1;
		}
		ctlr->sticky[Ier] = Ethre|Erda;
		ctlr->sticky[Mcr] |= Ie;
	}
	else{
		ctlr->sticky[Ier] = 0;
		ctlr->sticky[Mcr] = 0;
	}
	csr8w(ctlr, Ier, ctlr->sticky[Ier]);
	csr8w(ctlr, Mcr, ctlr->sticky[Mcr]);

	(*uart->phys->dtr)(uart, 1);
	(*uart->phys->rts)(uart, 1);
}

static Uart*
i8250pnp(void)
{
	return i8250uart;
}

PhysUart i8250physuart = {
	.name		= "i8250",
	.pnp		= i8250pnp,
	.enable		= i8250enable,
	.disable	= i8250disable,
	.kick		= i8250kick,
	.dobreak	= i8250break,
	.baud		= i8250baud,
	.bits		= i8250bits,
	.stop		= i8250stop,
	.parity		= i8250parity,
	.modemctl	= i8250modemctl,
	.rts		= i8250rts,
	.dtr		= i8250dtr,
	.status		= i8250status,
	.fifo		= i8250fifo,
};

int
serialgetc(void)
{
	return -1;
}

static void
i8250putc(Ctlr* ctlr, int c)
{
	int i;

	for(i = 0; i < 128; i++){
		if(csr8r(ctlr, Lsr) & Thre)
			break;
		delay(1);
	}
	outb(ctlr->io+Thr, c);
}

static void
i8250puts(char* s, int n)
{
	Ctlr *ctlr;
	Uart *uart;

	if((uart = consuart) == nil)
		return;

	ctlr = uart->regs;
	while(n--){
		if(*s == '\n')
			i8250putc(ctlr, '\r');
		i8250putc(ctlr, *s++);
	}
}

void
i8250console(void)
{
	Uart *uart;
	int n;
	char *cmd, *p;

	if((p = getconf("console")) == nil)
		return;
	n = strtoul(p, &cmd, 0);
	if(p == cmd)
		return;
	switch(n){
	default:
		return;
	case 0:
		uart = &i8250uart[0];
		break;
	case 1:
		uart = &i8250uart[1];
		break;	
	}

	if(*cmd == 0)
		cmd = "b9600 l8 pn s1";
	uartctl(uart, cmd);
	(*uart->phys->enable)(uart, 0);

	consuart = uart;
	serialputs = i8250puts;
	uart->console = 1;
} 
#include "../pc/uarti8250.c"

M alphapc/vga.c => alphapc/vga.c +1 -225
@@ 1,225 1,1 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"

#define	Image	IMAGE
#include <draw.h>
#include <memdraw.h>
#include <cursor.h>
#include "screen.h"

static Memimage* back;
static Memimage *conscol;

static Point curpos;
static Rectangle window;
static int *xp;
static int xbuf[256];
static Lock vgascreenlock;
int drawdebug;

void
vgaimageinit(ulong chan)
{
	if(back == nil){
		back = allocmemimage(Rect(0,0,1,1), chan);	/* RSC BUG */
		if(back == nil)
			panic("back alloc");		/* RSC BUG */
		back->flags |= Frepl;
		back->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
		memfillcolor(back, DBlack);
	}

	if(conscol == nil){
		conscol = allocmemimage(Rect(0,0,1,1), chan);	/* RSC BUG */
		if(conscol == nil)
			panic("conscol alloc");	/* RSC BUG */
		conscol->flags |= Frepl;
		conscol->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
		memfillcolor(conscol, DWhite);
	}
}

static void
vgascroll(VGAscr* scr)
{
	int h, o;
	Point p;
	Rectangle r;

	h = scr->memdefont->height;
	o = 8*h;
	r = Rpt(window.min, Pt(window.max.x, window.max.y-o));
	p = Pt(window.min.x, window.min.y+o);
	memimagedraw(scr->gscreen, r, scr->gscreen, p, nil, p);
	r = Rpt(Pt(window.min.x, window.max.y-o), window.max);
	memimagedraw(scr->gscreen, r, back, ZP, nil, ZP);

	curpos.y -= o;
}

static void
vgascreenputc(VGAscr* scr, char* buf, Rectangle *flushr)
{
	Point p;
	int h, w, pos;
	Rectangle r;

//	drawdebug = 1;
	if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)])
		xp = xbuf;

	h = scr->memdefont->height;
	switch(buf[0]){

	case '\n':
		if(curpos.y+h >= window.max.y){
			vgascroll(scr);
			*flushr = window;
		}
		curpos.y += h;
		vgascreenputc(scr, "\r", flushr);
		break;

	case '\r':
		xp = xbuf;
		curpos.x = window.min.x;
		break;

	case '\t':
		p = memsubfontwidth(scr->memdefont, " ");
		w = p.x;
		if(curpos.x >= window.max.x-4*w)
			vgascreenputc(scr, "\n", flushr);

		pos = (curpos.x-window.min.x)/w;
		pos = 4-(pos%4);
		*xp++ = curpos.x;
		r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y+h);
		memimagedraw(scr->gscreen, r, back, back->r.min, nil, ZP);
		combinerect(flushr, r);
		curpos.x += pos*w;
		break;

	case '\b':
		if(xp <= xbuf)
			break;
		xp--;
		r = Rect(*xp, curpos.y, curpos.x, curpos.y+h);
		memimagedraw(scr->gscreen, r, back, back->r.min, nil, ZP);
		combinerect(flushr, r);
		curpos.x = *xp;
		break;

	case '\0':
		break;

	default:
		p = memsubfontwidth(scr->memdefont, buf);
		w = p.x;

		if(curpos.x >= window.max.x-w)
			vgascreenputc(scr, "\n", flushr);

		*xp++ = curpos.x;
		r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h);
		memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min);
		memimagestring(scr->gscreen, curpos, conscol, ZP, scr->memdefont, buf);
		combinerect(flushr, r);
		curpos.x += w;
	}
//	drawdebug = 0;
}

static void
vgascreenputs(char* s, int n)
{
	int i;
	Rune r;
	char buf[4];
	VGAscr *scr;
	Rectangle flushr;

	scr = &vgascreen[0];

	if(!islo()){
		/*
		 * Don't deadlock trying to
		 * print in an interrupt.
		 */
		if(!canlock(&vgascreenlock))
			return;
	}
	else
		lock(&vgascreenlock);

	flushr = Rect(10000, 10000, -10000, -10000);

	while(n > 0){
		i = chartorune(&r, s);
		if(i == 0){
			s++;
			--n;
			continue;
		}
		memmove(buf, s, i);
		buf[i] = 0;
		n -= i;
		s += i;
		vgascreenputc(scr, buf, &flushr);
	}
	flushmemscreen(flushr);

	unlock(&vgascreenlock);
}

void
vgascreenwin(VGAscr* scr)
{
	int h, w;

	h = scr->memdefont->height;
	w = scr->memdefont->info[' '].width;

	window.min = Pt(48, 48);
	window.max = addpt(window.min, Pt(10+w*80, 10+h*50));
	if(window.max.y >= scr->gscreen->r.max.y)
		window.max.y = scr->gscreen->r.max.y-1;
	if(window.max.x >= scr->gscreen->r.max.x)
		window.max.x = scr->gscreen->r.max.x-1;
	window.max.y = window.min.y+((window.max.y-window.min.y)/h)*h;
	curpos = window.min;

	screenputs = vgascreenputs;
}

/*
 * Supposedly this is the way to turn DPMS
 * monitors off using just the VGA registers.
 * Unfortunately, it seems to mess up the video mode
 * on the cards I've tried.
 */
void
vgablank(VGAscr*, int blank)
{
	uchar seq1, crtc17;

	if(blank) {
		seq1 = 0x00;
		crtc17 = 0x80;
	} else {
		seq1 = 0x20;
		crtc17 = 0x00;
	}

	outs(Seqx, 0x0100);			/* synchronous reset */
	seq1 |= vgaxi(Seqx, 1) & ~0x20;
	vgaxo(Seqx, 1, seq1);
	crtc17 |= vgaxi(Crtx, 0x17) & ~0x80;
	delay(10);
	vgaxo(Crtx, 0x17, crtc17);
	outs(Crtx, 0x0300);				/* end synchronous reset */
}
#include "../pc/vga.c"

M alphapc/vgargb524.c => alphapc/vgargb524.c +1 -237
@@ 1,237 1,1 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"

#define	Image	IMAGE
#include <draw.h>
#include <memdraw.h>
#include <cursor.h>
#include "screen.h"

/*
 * IBM RGB524.
 * 170/220MHz High Performance Palette DAC.
 *
 * Assumes hooked up to an S3 Vision96[48].
 */
enum {
	IndexLo		= 0x00,
	IndexHi		= 0x01,
	Data		= 0x02,
	IndexCtl	= 0x03,
};

enum {						/* index registers */
	CursorCtl	= 0x30,
	CursorXLo	= 0x31,
	CursorXHi	= 0x32,
	CursorYLo	= 0x33,
	CursorYHi	= 0x34,
	CursorHotX	= 0x35,
	CursorHotY	= 0x36,

	CursorR1	= 0x40,
	CursorG1	= 0x41,
	CursorB1	= 0x42,
	CursorR2	= 0x43,
	CursorG2	= 0x44,
	CursorB2	= 0x45,
	CursorR3	= 0x46,
	CursorG3	= 0x47,
	CursorB3	= 0x48,

	CursorArray	= 0x100,
};

/*
 * Lower 2-bits of indirect DAC register
 * addressing.
 */
static ushort dacxreg[4] = {
	PaddrW, Pdata, Pixmask, PaddrR
};

static uchar
rgb524setrs2(void)
{
	uchar rs2;

	rs2 = vgaxi(Crtx, 0x55);
	vgaxo(Crtx, 0x55, (rs2 & 0xFC)|0x01);

	return rs2;
}

static void
rgb524xo(int index, uchar data)
{
	vgao(dacxreg[IndexLo], index & 0xFF);
	vgao(dacxreg[IndexHi], (index>>8) & 0xFF);
	vgao(dacxreg[Data], data);
}

static void
rgb524disable(VGAscr*)
{
	uchar rs2;

	rs2 = rgb524setrs2();
	rgb524xo(CursorCtl, 0x00);
	vgaxo(Crtx, 0x55, rs2);
}

static void
rgb524enable(VGAscr*)
{
	uchar rs2;

	rs2 = rgb524setrs2();

	/*
	 * Make sure cursor is off by initialising the cursor
	 * control to defaults.
	 */
	rgb524xo(CursorCtl, 0x00);

	/*
	 * Cursor colour 1 (white),
	 * cursor colour 2 (black).
	 */
	rgb524xo(CursorR1, Pwhite); rgb524xo(CursorG1, Pwhite); rgb524xo(CursorB1, Pwhite);
	rgb524xo(CursorR2, Pblack); rgb524xo(CursorG2, Pblack); rgb524xo(CursorB2, Pblack);

	/*
	 * Enable the cursor, 32x32, mode 2.
	 */
	rgb524xo(CursorCtl, 0x23);

	vgaxo(Crtx, 0x55, rs2);
}

static void
rgb524load(VGAscr*, Cursor* curs)
{
	uchar p, p0, p1, rs2;
	int x, y;

	rs2 = rgb524setrs2();

	/*
	 * Make sure cursor is off by initialising the cursor
	 * control to defaults.
	 */
	rgb524xo(CursorCtl, 0x00);

	/*
	 * Set auto-increment mode for index-register addressing
	 * and initialise the cursor array index.
	 */
	vgao(dacxreg[IndexCtl], 0x01);
	vgao(dacxreg[IndexLo], CursorArray & 0xFF);
	vgao(dacxreg[IndexHi], (CursorArray>>8) & 0xFF);

	/*
	 * Initialise the 32x32 cursor RAM array. There are 2 planes,
	 * p0 and p1. Data is written 4 pixels per byte, with p1 the
	 * MS bit of each pixel.
	 * The cursor is set in X-Windows mode which gives the following
	 * truth table:
	 *	p1 p0	colour
	 *	 0  0	underlying pixel colour
	 *	 0  1	underlying pixel colour
	 *	 1  0	cursor colour 1
	 *	 1  1	cursor colour 2
	 * Put the cursor into the top-left of the 32x32 array.
	 */
	for(y = 0; y < 32; y++){
		for(x = 0; x < 32/8; x++){
			if(x < 16/8 && y < 16){
				p0 = curs->clr[x+y*2];
				p1 = curs->set[x+y*2];

				p = 0x00;
				if(p1 & 0x80)
					p |= 0xC0;
				else if(p0 & 0x80)
					p |= 0x80;
				if(p1 & 0x40)
					p |= 0x30;
				else if(p0 & 0x40)
					p |= 0x20;
				if(p1 & 0x20)
					p |= 0x0C;
				else if(p0 & 0x20)
					p |= 0x08;
				if(p1 & 0x10)
					p |= 0x03;
				else if(p0 & 0x10)
					p |= 0x02;
				vgao(dacxreg[Data], p);

				p = 0x00;
				if(p1 & 0x08)
					p |= 0xC0;
				else if(p0 & 0x08)
					p |= 0x80;
				if(p1 & 0x04)
					p |= 0x30;
				else if(p0 & 0x04)
					p |= 0x20;
				if(p1 & 0x02)
					p |= 0x0C;
				else if(p0 & 0x02)
					p |= 0x08;
				if(p1 & 0x01)
					p |= 0x03;
				else if(p0 & 0x01)
					p |= 0x02;
				vgao(dacxreg[Data], p);
			}
			else{
				vgao(dacxreg[Data], 0x00);
				vgao(dacxreg[Data], 0x00);
			}
		}
	}

	/*
	 * Initialise the cursor hotpoint,
	 * enable the cursor and restore state.
	 */
	rgb524xo(CursorHotX, -curs->offset.x);
	rgb524xo(CursorHotY, -curs->offset.y);

	rgb524xo(CursorCtl, 0x23);

	vgaxo(Crtx, 0x55, rs2);
}

static int
rgb524move(VGAscr*, Point p)
{
	uchar rs2;

	rs2 = rgb524setrs2();

	rgb524xo(CursorXLo, p.x & 0xFF);
	rgb524xo(CursorXHi, (p.x>>8) & 0x0F);
	rgb524xo(CursorYLo, p.y & 0xFF);
	rgb524xo(CursorYHi, (p.y>>8) & 0x0F);

	vgaxo(Crtx, 0x55, rs2);

	return 0;
}

VGAcur vgargb524cur = {
	"rgb524hwgc",

	rgb524enable,
	rgb524disable,
	rgb524load,
	rgb524move,
};
#include "../pc/vgargb524.c"

M alphapc/vgas3.c => alphapc/vgas3.c +1 -506
@@ 1,506 1,1 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"

#define	Image	IMAGE
#include <draw.h>
#include <memdraw.h>
#include <cursor.h>
#include "screen.h"

static int
s3pageset(VGAscr* scr, int page)
{
	uchar crt35, crt51;
	int opage;

	crt35 = vgaxi(Crtx, 0x35);
	if(scr->gscreen->depth >= 8){
		/*
		 * The S3 registers need to be unlocked for this.
		 * Let's hope they are already:
		 *	vgaxo(Crtx, 0x38, 0x48);
		 *	vgaxo(Crtx, 0x39, 0xA0);
		 *
		 * The page is 6 bits, the lower 4 bits in Crt35<3:0>,
		 * the upper 2 in Crt51<3:2>.
		 */
		vgaxo(Crtx, 0x35, page & 0x0F);
		crt51 = vgaxi(Crtx, 0x51);
		vgaxo(Crtx, 0x51, (crt51 & ~0x0C)|((page & 0x30)>>2));
		opage = ((crt51 & 0x0C)<<2)|(crt35 & 0x0F);
	}
	else{
		vgaxo(Crtx, 0x35, (page<<2) & 0x0C);
		opage = (crt35>>2) & 0x03;
	}

	return opage;
}

static void
s3page(VGAscr* scr, int page)
{
	int id;

	id = (vgaxi(Crtx, 0x30)<<8)|vgaxi(Crtx, 0x2E);
	switch(id){

	case 0xE110:				/* ViRGE/GX2 */
		break;

	default:
		lock(&scr->devlock);
		s3pageset(scr, page);
		unlock(&scr->devlock);
		break;
	}
}

static ulong
s3linear(VGAscr* scr, int* size, int* align)
{
	ulong aperture, oaperture, opciaddr;
	int oapsize, wasupamem;
	Pcidev *p;

	oaperture = scr->aperture;
	oapsize = scr->apsize;
	opciaddr = scr->pciaddr;
	wasupamem = scr->isupamem;
	if(wasupamem)
		upafree(oaperture, oapsize);
	scr->isupamem = 0;
	scr->pciaddr = 0;

	if(p = pcimatch(nil, 0x5333, 0)){
		aperture = p->mem[0].bar & ~0x0F;
		*size = p->mem[0].size;
	}
	else
		aperture = 0;

	aperture = upamalloc(aperture, *size, *align);
	if(aperture == 0){
		if(wasupamem && upamalloc(oaperture, oapsize, 0)){
			scr->isupamem = 1;
			scr->pciaddr = opciaddr;
		}
	}
	else{
		scr->pciaddr = p->mem[0].bar & ~0x0F;
		scr->isupamem = 1;
	}

	return aperture;
}

static void
s3vsyncactive(void)
{
	/*
	 * Hardware cursor information is fetched from display memory
	 * during the horizontal blank active time. The 80x chips may hang
	 * if the cursor is turned on or off during this period.
	 */
	while((vgai(Status1) & 0x08) == 0)
		;
}

static void
s3disable(VGAscr*)
{
	uchar crt45;

	/*
	 * Turn cursor off.
	 */
	crt45 = vgaxi(Crtx, 0x45) & 0xFE;
	s3vsyncactive();
	vgaxo(Crtx, 0x45, crt45);
}

static void
s3enable(VGAscr* scr)
{
	int i;
	ulong storage;

	s3disable(scr);

	/*
	 * Cursor colours. Set both the CR0[EF] and the colour
	 * stack in case we are using a 16-bit RAMDAC.
	 */
	vgaxo(Crtx, 0x0E, Pwhite);
	vgaxo(Crtx, 0x0F, Pblack);
	vgaxi(Crtx, 0x45);

	for(i = 0; i < 3; i++)
		vgaxo(Crtx, 0x4A, Pblack);
	vgaxi(Crtx, 0x45);
	for(i = 0; i < 3; i++)
		vgaxo(Crtx, 0x4B, Pwhite);

	/*
	 * Find a place for the cursor data in display memory.
	 * Must be on a 1024-byte boundary.
	 */
	storage = (scr->gscreen->width*BY2WD*scr->gscreen->r.max.y+1023)/1024;
	vgaxo(Crtx, 0x4C, (storage>>8) & 0x0F);
	vgaxo(Crtx, 0x4D, storage & 0xFF);
	storage *= 1024;
	scr->storage = storage;

	/*
	 * Enable the cursor in Microsoft Windows format.
	 */
	vgaxo(Crtx, 0x55, vgaxi(Crtx, 0x55) & ~0x10);
	s3vsyncactive();
	vgaxo(Crtx, 0x45, 0x01);
}

static void
s3load(VGAscr* scr, Cursor* curs)
{
	uchar *p;
	int id, opage, x, y;

	/*
	 * Disable the cursor and
	 * set the pointer to the two planes.
	 */
	s3disable(scr);

	opage = 0;
	p = KADDR(scr->aperture);
	id = (vgaxi(Crtx, 0x30)<<8)|vgaxi(Crtx, 0x2E);
	switch(id){

	case 0xE131:				/* ViRGE */
	case 0xE18A:				/* ViRGE/[DG]X */
	case 0xE110:				/* ViRGE/GX2 */
	case 0xE13D:				/* ViRGE/VX */
		p += scr->storage;
		break;

	default:
		lock(&scr->devlock);
		opage = s3pageset(scr, scr->storage>>16);
		p += (scr->storage & 0xFFFF);
		break;
	}

	/*
	 * The cursor is set in Microsoft Windows format (the ViRGE/GX2 no
	 * longer supports the X11 format) which gives the following truth table:
	 *	and xor	colour
	 *	 0   0	background colour
	 *	 0   1	foreground colour
	 *	 1   0	current screen pixel
	 *	 1   1	NOT current screen pixel
	 * Put the cursor into the top-left of the 64x64 array.
	 *
	 * The cursor pattern in memory is interleaved words of
	 * AND and XOR patterns.
	 */
	for(y = 0; y < 64; y++){
		for(x = 0; x < 64/8; x += 2){
			if(x < 16/8 && y < 16){
				*p++ = ~(curs->clr[2*y + x]|curs->set[2*y + x]);
				*p++ = ~(curs->clr[2*y + x+1]|curs->set[2*y + x+1]);
				*p++ = curs->set[2*y + x];
				*p++ = curs->set[2*y + x+1];
			}
			else {
				*p++ = 0xFF;
				*p++ = 0xFF;
				*p++ = 0x00;
				*p++ = 0x00;
			}
		}
	}

	switch(id){

	case 0xE131:				/* ViRGE */
	case 0xE18A:				/* ViRGE/[DG]X */
	case 0xE110:				/* ViRGE/GX2 */
	case 0xE13D:				/* ViRGE/VX */
		break;

	default:
		s3pageset(scr, opage);
		unlock(&scr->devlock);
		break;
	}

	/*
	 * Save the cursor hotpoint and enable the cursor.
	 */
	scr->offset = curs->offset;
	s3vsyncactive();
	vgaxo(Crtx, 0x45, 0x01);
}

static int
s3move(VGAscr* scr, Point p)
{
	int x, xo, y, yo;

	/*
	 * Mustn't position the cursor offscreen even partially,
	 * or it disappears. Therefore, if x or y is -ve, adjust the
	 * cursor offset instead.
	 * There seems to be a bug in that if the offset is 1, the
	 * cursor doesn't disappear off the left edge properly, so
	 * round it up to be even.
	 */
	if((x = p.x+scr->offset.x) < 0){
		xo = -x;
		xo = ((xo+1)/2)*2;
		x = 0;
	}
	else
		xo = 0;
	if((y = p.y+scr->offset.y) < 0){
		yo = -y;
		y = 0;
	}
	else
		yo = 0;

	vgaxo(Crtx, 0x46, (x>>8) & 0x07);
	vgaxo(Crtx, 0x47, x & 0xFF);
	vgaxo(Crtx, 0x49, y & 0xFF);
	vgaxo(Crtx, 0x4E, xo);
	vgaxo(Crtx, 0x4F, yo);
	vgaxo(Crtx, 0x48, (y>>8) & 0x07);

	return 0;
}

/*
 * The manual gives byte offsets, but we want ulong offsets, hence /4.
 */
enum {
	SrcBase = 0xA4D4/4,
	DstBase = 0xA4D8/4,
	Stride = 0xA4E4/4,
	FgrdData = 0xA4F4/4,
	WidthHeight = 0xA504/4,
	SrcXY = 0xA508/4,
	DestXY = 0xA50C/4,
	Command = 0xA500/4,
	SubStat = 0x8504/4,
	FifoStat = 0x850C/4,
};

/*
 * Wait for writes to VGA memory via linear aperture to flush.
 */
enum {Maxloop = 1<<24};
struct {
	ulong linear;
	ulong fifo;
	ulong idle;
} waitcount;

static void
waitforlinearfifo(VGAscr *scr)
{
	ulong *mmio;
	long x;
	static ulong nwaitforlinearfifo;
	ulong mask, val;

	switch(scr->id){
	default:
		panic("unknown scr->id in s3 waitforlinearfifo");
	case 0xE131:	/* ViRGE */
	case 0xE13D:	/* ViRGE/VX */
		mask = 0x0F<<6;
		val = 0x08<<6;
		break;
	case 0xE110:	/* ViRGE/GX2 */
		mask = 0x1F<<6;
		val = 0x10<<6;
		break;
	}
	mmio = scr->mmio;
	x = 0;
	while((mmio[FifoStat]&mask) != val && x++ < Maxloop)
		waitcount.linear++;
}

static void
waitforfifo(VGAscr *scr, int entries)
{
	ulong *mmio;
	long x;
	static ulong nwaitforfifo;

	mmio = scr->mmio;
	x = 0;
	while((mmio[SubStat]&0x1F00) < ((entries+2)<<8) && x++ < Maxloop)
		waitcount.fifo++;
}

static void
waitforidle(VGAscr *scr)
{
	ulong *mmio;
	long x;

	mmio = scr->mmio;
	x = 0;
	while((mmio[SubStat]&0x3F00) != 0x3000 && x++ < Maxloop)
		waitcount.idle++;
}

static int
hwscroll(VGAscr *scr, Rectangle r, Rectangle sr)
{
	enum { Bitbltop = 0xCC };	/* copy source */
	ulong *mmio;
	ulong cmd, stride;
	Point dp, sp;
	int did, d;

	d = scr->gscreen->depth;
	did = (d-8)/8;
	cmd = 0x00000020|(Bitbltop<<17)|(did<<2);
	stride = Dx(scr->gscreen->r)*d/8;

	if(r.min.x <= sr.min.x){
		cmd |= 1<<25;
		dp.x = r.min.x;
		sp.x = sr.min.x;
	}else{
		dp.x = r.max.x-1;
		sp.x = sr.max.x-1;
	}

	if(r.min.y <= sr.min.y){
		cmd |= 1<<26;
		dp.y = r.min.y;
		sp.y = sr.min.y;
	}else{
		dp.y = r.max.y-1;
		sp.y = sr.max.y-1;
	}

	mmio = scr->mmio;
	waitforlinearfifo(scr);
	waitforfifo(scr, 7);
	mmio[SrcBase] = scr->aperture;
	mmio[DstBase] = scr->aperture;
	mmio[Stride] = (stride<<16)|stride;
	mmio[WidthHeight] = ((Dx(r)-1)<<16)|Dy(r);
	mmio[SrcXY] = (sp.x<<16)|sp.y;
	mmio[DestXY] = (dp.x<<16)|dp.y;
	mmio[Command] = cmd;
	waitforidle(scr);
	return 1;
}

static int
hwfill(VGAscr *scr, Rectangle r, ulong sval)
{
	enum { Bitbltop = 0xCC };	/* copy source */
	ulong *mmio;
	ulong cmd, stride;
	int did, d;

	d = scr->gscreen->depth;
	did = (d-8)/8;
	cmd = 0x16000120|(Bitbltop<<17)|(did<<2);
	stride = Dx(scr->gscreen->r)*d/8;
	mmio = scr->mmio;
	waitforlinearfifo(scr);
	waitforfifo(scr, 8);
	mmio[SrcBase] = scr->aperture;
	mmio[DstBase] = scr->aperture;
	mmio[DstBase] = scr->aperture;
	mmio[Stride] = (stride<<16)|stride;
	mmio[FgrdData] = sval;
	mmio[WidthHeight] = ((Dx(r)-1)<<16)|Dy(r);
	mmio[DestXY] = (r.min.x<<16)|r.min.y;
	mmio[Command] = cmd;
	waitforidle(scr);
	return 1;
}

enum {
	CursorSyncCtl = 0x0D,	/* in Seqx */
	VsyncHi = 0x80,
	VsyncLo = 0x40,
	HsyncHi = 0x20,
	HsyncLo = 0x10,
};

static void
s3blank(int blank)
{
	uchar x;

	x = vgaxi(Seqx, CursorSyncCtl);
	x &= ~0xF0;
	if(blank)
		x |= VsyncLo | HsyncLo;
	vgaxo(Seqx, CursorSyncCtl, x);
}

static void
s3drawinit(VGAscr *scr)
{
	ulong id;

	id = (vgaxi(Crtx, 0x30)<<8)|vgaxi(Crtx, 0x2E);
	scr->id = id;

	/*
	 * It's highly likely that other ViRGEs will work without
	 * change to the driver, with the exception of the size of
	 * the linear aperture memory write FIFO.  Since we don't
	 * know that size, I'm not turning them on.  See waitforlinearfifo
	 * above.
	 */
	switch(id){
	case 0xE131:				/* ViRGE */
	case 0xE13D:				/* ViRGE/VX */
	case 0xE110:				/* ViRGE/GX2 */
		scr->mmio = (ulong*)(scr->pciaddr+0x1000000);
/*
 * Untested on the Alpha.
 */
/*
		scr->fill = hwfill;
		scr->scroll = hwscroll;
*/
		/* scr->blank = hwblank; */
	}
}

VGAdev vgas3dev = {
	"s3",

	0,
	0,
	s3page,
	s3linear,
	s3drawinit,
};

VGAcur vgas3cur = {
	"s3hwgc",

	s3enable,
	s3disable,
	s3load,
	s3move,
};
#include "../pc/vgas3.c"

A alphapc/vgasavage.c => alphapc/vgasavage.c +1 -0
@@ 0,0 1,1 @@
#include "../pc/vgasavage.c"

M alphapc/vgatvp3026.c => alphapc/vgatvp3026.c +1 -189
@@ 1,189 1,1 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"

#define	Image	IMAGE
#include <draw.h>
#include <memdraw.h>
#include <cursor.h>
#include "screen.h"

/*
 * TVP3026 Viewpoint Video Interface Pallette.
 * Assumes hooked up to an S3 Vision968.
 */
enum {
	Index		= 0x00,		/* Index */
	Data		= 0x0A,		/* Data */

	CaddrW		= 0x04,		/* Colour Write Address */
	Cdata		= 0x05,		/* Colour Data */

	Cctl		= 0x09,		/* Direct Cursor Control */
	Cram		= 0x0B,		/* Cursor Ram Data */
	Cxlsb		= 0x0C,		/* Cursor X LSB */
	Cxmsb		= 0x0D,		/* Cursor X MSB */
	Cylsb		= 0x0E,		/* Cursor Y LSB */
	Cymsb		= 0x0F,		/* Cursor Y MSB */

	Icctl		= 0x06,		/* Indirect Cursor Control */
};

/*
 * Lower 2-bits of indirect DAC register
 * addressing.
 */
static ushort dacxreg[4] = {
	PaddrW, Pdata, Pixmask, PaddrR
};

static uchar
tvp3026io(uchar reg, uchar data)
{
	uchar crt55;

	crt55 = vgaxi(Crtx, 0x55) & 0xFC;
	vgaxo(Crtx, 0x55, crt55|((reg>>2) & 0x03));
	vgao(dacxreg[reg & 0x03], data);

	return crt55;
}

static void
tvp3026o(uchar reg, uchar data)
{
	uchar crt55;

	crt55 = tvp3026io(reg, data);
	vgaxo(Crtx, 0x55, crt55);
}

void
tvp3026xo(uchar index, uchar data)
{
	uchar crt55;

	crt55 = tvp3026io(Index, index);
	vgaxo(Crtx, 0x55, crt55|((Data>>2) & 0x03));
	vgao(dacxreg[Data & 0x03], data);
	vgaxo(Crtx, 0x55, crt55);
}

static void
tvp3026disable(VGAscr*)
{
	tvp3026xo(Icctl, 0x90);
	tvp3026o(Cctl, 0x00);
}

static void
tvp3026enable(VGAscr*)
{
	/*
	 * Make sure cursor is off and direct control enabled.
	 */
	tvp3026xo(Icctl, 0x90);
	tvp3026o(Cctl, 0x00);

	/*
	 * Overscan colour,
	 * cursor colour 1 (white),
	 * cursor colour 2, 3 (black).
	 */
	tvp3026o(CaddrW, 0x00);
	tvp3026o(Cdata, Pwhite); tvp3026o(Cdata, Pwhite); tvp3026o(Cdata, Pwhite);
	tvp3026o(Cdata, Pwhite); tvp3026o(Cdata, Pwhite); tvp3026o(Cdata, Pwhite);
	tvp3026o(Cdata, Pblack); tvp3026o(Cdata, Pblack); tvp3026o(Cdata, Pblack);
	tvp3026o(Cdata, Pblack); tvp3026o(Cdata, Pblack); tvp3026o(Cdata, Pblack);

	/*
	 * Enable the cursor in 3-colour mode.
	 */
	tvp3026o(Cctl, 0x01);
}

static void
tvp3026load(VGAscr* scr, Cursor* curs)
{
	int x, y;

	/*
	 * Make sure cursor is off by initialising the cursor
	 * control to defaults.
	 * Write to the indirect control register to make sure
	 * direct register is enabled and upper 2 bits of cursor
	 * RAM address are 0.
	 * The LSBs of the cursor RAM address are in PaddrW.
	 */
	tvp3026xo(Icctl, 0x90);
	tvp3026o(Cctl, 0x00);
	vgao(PaddrW, 0x00);

	/*
	 * Initialise the 64x64 cursor RAM array. There are 2 planes,
	 * p0 and p1. Data is written 8 pixels per byte, with p0 in the
	 * first 512 bytes of the array and p1 in the second.
	 * The cursor is set in 3-colour mode which gives the following
	 * truth table:
	 *	p1 p0	colour
	 *	 0  0	transparent
	 *	 0  1	cursor colour 0
	 *	 1  0	cursor colour 1
	 *	 1  1	cursor colour 2
	 * Put the cursor into the top-left of the 64x64 array.
	 * The 0,0 cursor point is bottom-right, so positioning will
	 * have to take that into account.
	 */
	for(y = 0; y < 64; y++){
		for(x = 0; x < 64/8; x++){
			if(x < 16/8 && y < 16)
				tvp3026o(Cram, curs->clr[x+y*2]);
			else
				tvp3026o(Cram, 0x00);
		}
	}
	for(y = 0; y < 64; y++){
		for(x = 0; x < 64/8; x++){
			if(x < 16/8 && y < 16)
				tvp3026o(Cram, curs->set[x+y*2]);
			else
				tvp3026o(Cram, 0x00);
		}
	}

	/*
	 * Initialise the cursor hotpoint
	 * and enable the cursor in 3-colour mode.
	 */
	scr->offset.x = 64+curs->offset.x;
	scr->offset.y = 64+curs->offset.y;
	tvp3026o(Cctl, 0x01);
}

static int
tvp3026move(VGAscr* scr, Point p)
{
	int x, y;

	x = p.x+scr->offset.x;
	y = p.y+scr->offset.y;

	tvp3026o(Cxlsb, x & 0xFF);
	tvp3026o(Cxmsb, (x>>8) & 0x0F);
	tvp3026o(Cylsb, y & 0xFF);
	tvp3026o(Cymsb, (y>>8) & 0x0F);

	return 0;
}

VGAcur vgatvp3026cur = {
	"tvp3026hwgc",

	tvp3026enable,
	tvp3026disable,
	tvp3026load,
	tvp3026move,
};
#include "../pc/vgatvp3026.c"

M alphapc/vgax.c => alphapc/vgax.c +1 -103
@@ 1,103 1,1 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"

#define	Image	IMAGE
#include <draw.h>
#include <memdraw.h>
#include <cursor.h>
#include "screen.h"

static Lock vgaxlock;			/* access to index registers */

int
vgaxi(long port, uchar index)
{
	uchar data;

	ilock(&vgaxlock);
	switch(port){

	case Seqx:
	case Crtx:
	case Grx:
		outb(port, index);
		data = inb(port+1);
		break;

	case Attrx:
		/*
		 * Allow processor access to the colour
		 * palette registers. Writes to Attrx must
		 * be preceded by a read from Status1 to
		 * initialise the register to point to the
		 * index register and not the data register.
		 * Processor access is allowed by turning
		 * off bit 0x20.
		 */
		inb(Status1);
		if(index < 0x10){
			outb(Attrx, index);
			data = inb(Attrx+1);
			inb(Status1);
			outb(Attrx, 0x20|index);
		}
		else{
			outb(Attrx, 0x20|index);
			data = inb(Attrx+1);
		}
		break;

	default:
		iunlock(&vgaxlock);
		return -1;
	}
	iunlock(&vgaxlock);

	return data & 0xFF;
}

int
vgaxo(long port, uchar index, uchar data)
{
	ilock(&vgaxlock);
	switch(port){

	case Seqx:
	case Crtx:
	case Grx:
		/*
		 * We could use an outport here, but some chips
		 * (e.g. 86C928) have trouble with that for some
		 * registers.
		 */
		outb(port, index);
		outb(port+1, data);
		break;

	case Attrx:
		inb(Status1);
		if(index < 0x10){
			outb(Attrx, index);
			outb(Attrx, data);
			inb(Status1);
			outb(Attrx, 0x20|index);
		}
		else{
			outb(Attrx, 0x20|index);
			outb(Attrx, data);
		}
		break;

	default:
		iunlock(&vgaxlock);
		return -1;
	}
	iunlock(&vgaxlock);

	return 0;
}
#include "../pc/vgax.c"