~kris/9p

9hist

50425f67e5cf896910bb3479c1b34617a8fef5a8 — David du Colombier 25 years ago c7b71a7
Plan 9 from Bell Labs 2001-07-28
M alphapc/devarch.c => alphapc/devarch.c +1 -1
@@ 452,7 452,7 @@ cputyperead(Chan*, void *a, long n, vlong offset)
	Hwcpu *cpu;

	mhz = (m->cpuhz+999999)/1000000;
	cpu = (Hwcpu*) ((ulong)hwrpb + hwrpb->cpuoff);
	cpu = (Hwcpu*) ((ulong)hwrpb + hwrpb->cpuoff);	/* NB CPU 0 */
	cputype = "unknown";
	maj = (ulong)cpu->cputype;
	if (maj < nelem(cpunames))

M alphapc/faultalpha.c => alphapc/faultalpha.c +2 -1
@@ 45,7 45,8 @@ faultalpha(Ureg *ur)
	if(0)
		mmudump();
	dumpregs(ur);
	panic("fault");
	_dumpstack(ur);
	exit(1);
}

/*

M alphapc/fns.h => alphapc/fns.h +1 -0
@@ 17,6 17,7 @@ int	dmadone(int);
void	dmaend(int);
int	dmainit(int, int);
long	dmasetup(int, void*, long, int);
void _dumpstack(Ureg *);
void		evenaddr(ulong);
void		fataltrap(Ureg *, char *);
void		fault0(void);

M alphapc/main.c => alphapc/main.c +7 -12
@@ 29,9 29,9 @@ options(void)
	long i, n;
	char *cp, *line[MAXCONF], *p, *q;

	cp = bootconf->bootargs;
	cp = bootargs;
	strncpy(cp, bootconf->bootargs, BOOTARGSLEN);
	cp[BOOTARGSLEN-1] = 0;
	strcpy(bootargs, cp);

	/*
	 * Strip out '\r', change '\t' -> ' '.


@@ 88,16 88,6 @@ main(void)
	if(arch->corehello)
		arch->corehello();

#ifdef	NEVER
	percpu = hwrpb + (hwrpb[40]>>2);
//	percpu[32] |= 2;		/* restart capable */
	percpu[32] &= ~1;		/* boot in progress - not */
//	percpu[32] |= (3<<16);		/* warm boot requested */
//	percpu[32] |= (2<<16);		/* cold boot requested */
//	percpu[32] |= (4<<16);		/* stay halted */
	percpu[32] |= (0<<16);		/* default action */
#endif

	procinit0();
	initseg();
	links();


@@ 118,6 108,7 @@ void
machinit(void)
{
	int n;
	Hwcpu *cpu;

	icflush();
	n = m->machno;


@@ 126,6 117,10 @@ machinit(void)

	active.exiting = 0;
	active.machs = 1;

	cpu = (Hwcpu*) ((ulong)hwrpb + hwrpb->cpuoff + n*hwrpb->cpulen);
	cpu->state &= ~1;			/* boot in progress - not */
/*	cpu->state |= (4<<16);		/* stay halted */
}

void

M alphapc/mmu.c => alphapc/mmu.c +43 -56
@@ 38,42 38,38 @@ mmuinit(void)
	nextio = (uchar*) (KZERO|bootconf->maxphys);
}

/*
 * Called splhi, not in Running state
 */
void
mmuswitch(Proc *p)
static void
mmuptefree(Proc* proc)
{
	Page *pg;
	uvlong *lvl2;

	if(p->newtlb){
		/*
		 *  newtlb set means that they are inconsistent
		 *  with the segment.c data structures.
		 *
		 *  bin the current 3rd level page tables and
		 *  the pointers to them in the 2nd level page.
		 *  pg->daddr is used by putmmu to save the offset into
		 *  the 2nd level page.
		 */
		if(p->mmutop && p->mmuused){
			lvl2 = (uvlong*)p->mmulvl2->va;
			for(pg = p->mmuused; pg->next; pg = pg->next)
				lvl2[pg->daddr] = 0;
			lvl2[pg->daddr] = 0;
			pg->next = p->mmufree;
			p->mmufree = p->mmuused;
			p->mmuused = 0;
	Page **last, *page;

	if(proc->mmutop && proc->mmuused){
		lvl2 = (uvlong*)proc->mmulvl2->va;
		last = &proc->mmuused;
		for(page = *last; page; page = page->next){
			lvl2[page->daddr] = 0;
			last = &page->next;
		}
		p->newtlb = 0;
		*last = proc->mmufree;
		proc->mmufree = proc->mmuused;
		proc->mmuused = 0;
	}
}

void
mmuswitch(Proc *proc)
{
	if(proc->newtlb){
		mmuptefree(proc);
		proc->newtlb = 0;
	}

	/* tell processor about new page table and flush cached entries */
	if(p->mmutop == 0)
	if(proc->mmutop == 0)
		setptb(origlvl1);
	else
		setptb(p->mmutop->pa);
		setptb(proc->mmutop->pa);
	tlbflush(-1, 0);
	icflush();
}


@@ 83,44 79,35 @@ mmuswitch(Proc *p)
 *  with palloc locked.
 */
void
mmurelease(Proc *p)
mmurelease(Proc *proc)
{
	Page *pg;
	Page *next;

	if(canlock(&palloc))
		panic("mmurelease");
	Page *page, *next;

	/* point to protoype page map */
	setptb(origlvl1);
	icflush();

	/* give away page table pages */
	for(pg = p->mmuused; pg; pg = next){
		next = pg->next;
		pg->next = p->mmufree;
		p->mmufree = pg;
	}
	p->mmuused = 0;
	if(p->mmutop) {
		p->mmutop->next = p->mmufree;
		p->mmufree = p->mmutop;
		p->mmutop = 0;
	mmuptefree(proc);
	proc->mmuused = 0;
	if(proc->mmutop) {
		proc->mmutop->next = proc->mmufree;
		proc->mmufree = proc->mmutop;
		proc->mmutop = 0;
	}
	if(p->mmulvl2) {
		p->mmulvl2->next = p->mmufree;
		p->mmufree = p->mmulvl2;
		p->mmulvl2 = 0;
	if(proc->mmulvl2) {
		proc->mmulvl2->next = proc->mmufree;
		proc->mmufree = proc->mmulvl2;
		proc->mmulvl2 = 0;
	}
	for(pg = p->mmufree; pg; pg = next){
		next = pg->next;
		if(--pg->ref)
			panic("mmurelease: pg->ref %d\n", pg->ref);
		pagechainhead(pg);
	for(page = proc->mmufree; page; page = next){
		next = page->next;
		if(--page->ref)
			panic("mmurelease: page->ref %d\n", page->ref);
		pagechainhead(page);
	}
	if(p->mmufree && palloc.r.p)
	if(proc->mmufree && palloc.r.p)
		wakeup(&palloc.r);
	p->mmufree = 0;
	proc->mmufree = 0;
}

void

M alphapc/sd53c8xx.c => alphapc/sd53c8xx.c +18 -20
@@ 200,20 200,12 @@ typedef enum State {
} State;

typedef struct Dsa {
	union {
		uchar state[4];
		struct {
			uchar stateb;
			uchar result;
			uchar dmablks;
			uchar flag;	/* setbyte(state,3,...) */
		};
	};

	union {
		ulong dmancr;		/* For block transfer: NCR order (little-endian) */
		uchar dmaaddr[4];
	};
	uchar stateb;
	uchar result;
	uchar dmablks;
	uchar flag;	/* setbyte(state,3,...) */

	uchar dmaaddr[4];

	uchar target;			/* Target */
	uchar pad0[3];


@@ 397,7 389,7 @@ dsaalloc(Controller *c, int target, int lun)
		if (DEBUG(1))
			KPRINT(PRINTPREFIX "%d/%d: allocated new dsa %lux\n", target, lun, (ulong)d);
		lesetl(d->next, 0);
		lesetl(d->state, A_STATE_ALLOCATED);
		lesetl(&d->stateb, A_STATE_ALLOCATED);
		if (legetl(c->dsalist.head) == 0)
			lesetl(c->dsalist.head, DMASEG(d));	/* ATOMIC?!? */
		else


@@ 408,7 400,7 @@ dsaalloc(Controller *c, int target, int lun)
		if (DEBUG(1))
			KPRINT(PRINTPREFIX "%d/%d: reused dsa %lux\n", target, lun, (ulong)d);
		c->dsalist.freechain = d->freechain;
		lesetl(d->state, A_STATE_ALLOCATED);
		lesetl(&d->stateb, A_STATE_ALLOCATED);
	}
	iunlock(&c->dsalist);
	d->target = target;


@@ 422,7 414,7 @@ dsafree(Controller *c, Dsa *d)
	ilock(&c->dsalist);
	d->freechain = c->dsalist.freechain;
	c->dsalist.freechain = d;
	lesetl(d->state, A_STATE_FREE);
	lesetl(&d->stateb, A_STATE_FREE);
	iunlock(&c->dsalist);
}



@@ 1204,7 1196,7 @@ interrupt(Ureg *ur, void *a)
				    dsa->dmablks, legetl(dsa->dmaaddr),
				    legetl(dsa->data_buf.pa), legetl(dsa->data_buf.dbc));
				n->scratcha[2] = dsa->dmablks;
				lesetl(n->scratchb, dsa->dmancr);
				lesetl(n->scratchb, *(ulong*)dsa->dmaaddr);
				cont = E_data_block_mismatch_recover;
			}
			else if (sa == E_data_out_mismatch) {


@@ 1231,7 1223,7 @@ interrupt(Ureg *ur, void *a)
				    dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc));
				/* copy changes into scratch registers */
				n->scratcha[2] = dsa->dmablks;
				lesetl(n->scratchb, dsa->dmancr);
				lesetl(n->scratchb, *(ulong*)dsa->dmaaddr);
				cont = E_data_block_mismatch_recover;
			}
			else if (sa == E_id_out_mismatch) {


@@ 1442,6 1434,12 @@ interrupt(Ureg *ur, void *a)
				    dsa->target, dsa->lun);
				cont = -2;
				break;
			case A_error_reselected:		/* dsa isn't valid here */
				print(PRINTPREFIX "reselection error\n");
				dumpncrregs(c, 1);
				for (dsa = KPTR(legetl(c->dsalist.head)); dsa; dsa = KPTR(legetl(dsa->next)))
					IPRINT(PRINTPREFIX "dsa target %d lun %d state %d\n", dsa->target, dsa->lun, dsa->stateb);
				break;
			default:
				IPRINT(PRINTPREFIX "%d/%d: script error %ld\n",
					dsa->target, dsa->lun, legetl(n->dsps));


@@ 1752,7 1750,7 @@ docheck:
		 * Clear out the microcode state
		 * so the Dsa can be re-used.
		 */
		lesetl(d->state, A_STATE_ALLOCATED);
		lesetl(&d->stateb, A_STATE_ALLOCATED);
		goto docheck;
	}
	qunlock(&c->q[target]);

M alphapc/trap.c => alphapc/trap.c +54 -39
@@ 245,7 245,7 @@ intr(Ureg *ur)
	}
	else{
		dumpregs(ur);
		panic("unknown intr: %d\n", vno); /* */
		print("unknown intr: %d\n", vno); /* */
	}
}



@@ 375,7 375,50 @@ kernfault(Ureg *ur, int code)
}

void
dumpstack(void)
dumpregs(Ureg *ur)
{
	int i, col;
	uvlong *l;

	if(up)
		print("registers for %s %ld\n", up->text, up->pid);
	else
		print("registers for kernel\n");

	l = &ur->type;
	col = 0;
	for (i = 0; i < sizeof regname/sizeof(char*); i++, l++) {
		print("%-7s%.16llux%s", regname[i], *l, col == 2 ? "\n" : "     ");
		if (col++ == 2)
			col = 0;
	}
	print("\n");
}


/*
 * Fill in enough of Ureg to get a stack trace, and call a function.
 * Used by debugging interface rdb.
 */
static void
getpcsp(ulong *pc, ulong *sp)
{
	*pc = getcallerpc(&pc);
	*sp = (ulong)&pc-8;
}

void
callwithureg(void (*fn)(Ureg*))
{
	Ureg ureg;

	getpcsp((ulong*)&ureg.pc, (ulong*)&ureg.sp);
	ureg.r26 = getcallerpc(&fn);
	fn(&ureg);
}

void
_dumpstack(Ureg *ureg)
{
	ulong l, sl, el, v, i, instr, op;
	extern ulong etext;


@@ 392,15 435,12 @@ dumpstack(void)
		el = sl + KSTACK;
	}
	if(l > el || l < sl){
		iprint("dumpstack: l %lux sl %lux el %lux m %lux up %lux\n",
			l, sl, el, m, up);
		el = (ulong)m+BY2PG;
		sl = el-KSTACK;
	}
	iprint("dumpstack: l %lux sl %lux el %lux m %lux\n", l, sl, el, m);
	if(l > el || l < sl){
	if(l > el || l < sl)
		return;
	}
	print("ktrace /kernel/path %.8lux %.8lux %.8lux\n", (ulong)ureg->pc, (ulong)ureg->sp, (ulong)ureg->r26);

	i = 0;
	for(; l<el; l+=8){


@@ 412,48 452,23 @@ dumpstack(void)
			instr = *(ulong*)v;
			op = (instr>>26);
			if(op == 26 || op == 52){
				iprint("%lux ", v);
				print("%lux=%lux ", l, v);
				i++;
			}
		}
		if(i == 8){
		if(i == 4){
			i = 0;
			iprint("\n");
			print("\n");
		}
	}
	if(i)
		print("\n");
}

void
callwithureg(void (*fn)(Ureg*))
{
	Ureg ureg;
	ureg.pc = getcallerpc(&fn);
	ureg.sp = (ulong)&fn;
	fn(&ureg);
}

void
dumpregs(Ureg *ur)
dumpstack(void)
{
	int i, col;
	uvlong *l;

	spllo();
	prflush(); 
	if(up)
		iprint("registers for %s %d\n", up->text, up->pid);
	else
		iprint("registers for kernel\n");

	l = &ur->type;
	col = 0;
	for (i = 0; i < sizeof regname/sizeof(char*); i++, l++) {
		iprint("%-7s%.16llux%s", regname[i], *l, col == 2 ? "\n" : "     ");
		if (col++ == 2)
			col = 0;
	}
	iprint("\n");
//	prflush();
	callwithureg(_dumpstack);
}

int

A port/devpnp.c => port/devpnp.c +523 -0
@@ 0,0 1,523 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"

typedef struct Pnp Pnp;
typedef struct Card Card;

struct Pnp
{
	Lock;
	int		rddata;
	int		debug;
	Card		*cards;
};

struct Card
{
	int		csn;
	ulong	id1;
	ulong	id2;
	int		ncfg;
	Card*	next;
};

static Pnp	pnp;

#define	DPRINT	if(pnp.debug) print
#define	XPRINT	if(1) print

enum {
	Address = 0x279,
	WriteData = 0xa79,

	Qtopdir = 0,

	Qpnpdir,
	Qpnpctl,
	Qcsnctl,
	Qcsnraw,

	Qpcidir,
	Qpcictl,
	Qpciraw,
};

#define TYPE(q)		((ulong)(q).path & 0x0F)
#define CSN(q)		(((ulong)(q).path>>4) & 0xFF)
#define QID(c, t)	(((c)<<4)|(t))

static Dirtab topdir[] = {
	".",	{ Qtopdir, 0, QTDIR },	0,	0555,
	"pnp",	{ Qpnpdir, 0, QTDIR },	0,	0555,
	"pci",	{ Qpcidir, 0, QTDIR },	0,	0555,
};

static Dirtab pnpdir[] = {
	".",	{ Qpnpdir, 0, QTDIR },	0,	0555,
	"ctl",	{ Qpnpctl, 0, 0 },	0,	0666,
};

extern Dev pnpdevtab;

static char key[32] =
{
	0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB, 0x7D, 0xBE,
	0xDF, 0x6F, 0x37, 0x1B, 0x0D, 0x86, 0xC3, 0x61,
	0xB0, 0x58, 0x2C, 0x16, 0x8B, 0x45, 0xA2, 0xD1,
	0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x39,
};

static void
cmd(int reg, int val)
{
	outb(Address, reg);
	outb(WriteData, val);
}

/* Send initiation key, putting each card in Sleep state */
static void
initiation(void)
{
	int i;

	/* ensure each card's LFSR is reset */
	outb(Address, 0x00);
	outb(Address, 0x00);

	/* send initiation key */
	for (i = 0; i < 32; i++)
		outb(Address, key[i]);
}

/* isolation protocol... */
static int
readbit(int rddata)
{
	int r1, r2;

	r1 = inb(rddata);
	r2 = inb(rddata);
	microdelay(250);
	return (r1 == 0x55) && (r2 == 0xaa);
}

static int
isolate(int rddata, ulong *id1, ulong *id2)
{
	int i, csum, bit;
	uchar *p, id[9];

	outb(Address, 0x01);	/* point to serial isolation register */
	delay(1);
	csum = 0x6a;
	for (i = 0; i < 64; i++) {
		bit = readbit(rddata);
		csum = (csum>>1) | (((csum&1) ^ ((csum>>1)&1) ^ bit)<<7);
		p = &id[i>>3];
		*p = (*p>>1) | (bit<<7);
	}
	for (; i < 72; i++) {
		p = &id[i>>3];
		*p = (*p>>1) | (readbit(rddata)<<7);
	}
	*id1 = (id[3]<<24)|(id[2]<<16)|(id[1]<<8)|id[0];
	*id2 = (id[7]<<24)|(id[6]<<16)|(id[5]<<8)|id[4];
	if (*id1 == 0)
		return 0;
	if (id[8] != csum)
		DPRINT("pnp: bad checksum id1 %lux id2 %lux csum %x != %x\n", *id1, *id2, csum, id[8]); /**/
	return id[8] == csum;
}

static int
getresbyte(int rddata)
{
	int tries = 0;

	outb(Address, 0x05);
	while ((inb(rddata) & 1) == 0)
		if (tries++ > 1000000)
			error("pnp: timeout waiting for resource data\n");
	outb(Address, 0x04);
	return inb(rddata);
}

static char *
serial(ulong id1, ulong id2)
{
	int i1, i2, i3;
	ulong x;
	static char buf[20];

	i1 = (id1>>2)&31;
	i2 = ((id1<<3)&24)+((id1>>13)&7);
	i3 = (id1>>8)&31;
	x = (id1>>8)&0xff00|(id1>>24)&0x00ff;
	if (i1 > 0 && i1 < 27 && i2 > 0 && i2 < 27 && i3 > 0 && i3 < 27 && (id1 & (1<<7)) == 0)
		snprint(buf, sizeof(buf), "%c%c%c%.4lux.%lux", 'A'+i1-1, 'A'+i2-1, 'A'+i3-1, x, id2);
	else
		snprint(buf, sizeof(buf), "%.4lux%.4lux.%lux", (id1<<8)&0xff00|(id1>>8)&0x00ff, x, id2);
	return buf;
}

/* called with pnp locked */
static Card *
findcsn(int csn, int create)
{
	Card *c, *nc, **l;

	l = &pnp.cards;
	for(c = *l; c != nil; c = *l) {
		if(c->csn == csn)
			return c;
		if(c->csn > csn)
			break;
		l = &c->next;
	}
	if(!create)
		return nil;
	*l = nc = malloc(sizeof(Card));
	nc->next = c;
	nc->csn = csn;
	return nc;
}

static int
newcsn(void)
{
	int csn;
	Card *c;

	csn = 1;
	for(c = pnp.cards; c != nil; c = c->next) {
		if(c->csn > csn)
			break;
		csn = c->csn+1;
	}
	return csn;
}

static int
pnpncfg(int rddata)
{
	int i, n, x, ncfg, n1, n2;

	ncfg = 0;
	for (;;) {
		x = getresbyte(rddata);
		if((x & 0x80) == 0) {
			n = (x&7)+1;
			for(i = 1; i < n; i++)
				getresbyte(rddata);
		}
		else {
			n1 = getresbyte(rddata);
			n2 = getresbyte(rddata);
			n = (n2<<8)|n1 + 3;
			for (i = 3; i < n; i++)
				getresbyte(rddata);
		}
		ncfg += n;
		if((x>>3) == 0x0f)
			break;
	}
	return ncfg;
}

/* look for cards, and assign them CSNs */
static int
pnpscan(int rddata)
{
	Card *c;
	int csn, ok;
	ulong id1, id2;

	ilock(&pnp);
	pnp.rddata = rddata;
	initiation();
	cmd(0x02, 0x04+0x01);		/* reset CSN on all cards and reset logical devices */
	delay(1);					/* delay after resetting cards */

	cmd(0x03, 0);				/* Wake all cards with a CSN of 0 */
	cmd(0x00, rddata>>2);		/* Set the READ_DATA port on all cards */
	while(isolate(rddata, &id1, &id2)) {
		for(c = pnp.cards; c != nil; c = c->next)
			if(c->id1 == id1 && c->id2 == id2)
				break;
		if(c == nil) {
			csn = newcsn();
			c = findcsn(csn, 1);
			c->id1 = id1;
			c->id2 = id2;
		}
		cmd(0x06, c->csn);		/* set the card's csn */
		print("pnp%d: %s\n", c->csn, serial(id1, id2));
		c->ncfg = pnpncfg(rddata);
		cmd(0x03, 0);		/* Wake all cards with a CSN of 0, putting this card to sleep */
	}
	cmd(0x02, 0x02);			/* return cards to Wait for Key state */
	ok = (pnp.cards != 0);
	iunlock(&pnp);
	return ok;
}

static void
pnpreset(void)
{
	Card *c;
	ulong id1, id2;
	int csn, i1, i2, i3, x;
	char *s, *p, buf[20];
	ISAConf isa;

	memset(&isa, 0, sizeof(ISAConf));
	pnp.rddata = -1;
	if (isaconfig("pnp", 0, &isa) == 0)
		return;
	if(isa.port < 0x203 || isa.port > 0x3ff)
		return;
	for(csn = 1; csn < 256; csn++) {
		sprint(buf, "pnp%d", csn);
		s = getconf(buf);
		if(s == 0)
			continue;
		if(strlen(s) < 8 || s[7] != '.' || s[0] < 'A' || s[0] > 'Z' || s[1] < 'A' || s[1] > 'Z' || s[2] < 'A' || s[2] > 'Z') {
bad:
			print("pnp%d: bad conf string %s\n", csn, s);
			continue;	
		}
		i1 = s[0]-'A'+1;
		i2 = s[1]-'A'+1;
		i3 = s[2]-'A'+1;
		x = strtoul(&s[3], 0, 16);
		id1 = (i1<<2)|((i2>>3)&3)|((i2&7)<<13)|(i3<<8)|((x&0xff)<<24)|((x&0xff00)<<8);
		id2 = strtoul(&s[8], &p, 16);
		if(*p != ' ' && *p != '\0')
			goto bad;
		c = findcsn(csn, 1);
		c->id1 = id1;
		c->id2 = id2;
	}
	pnpscan(isa.port);
}

static int
pnpgen1(Chan *c, int t, int csn, Card *cp, Dir *dp)
{
	Qid q;
	char buf[20];

	switch(t) {
	case Qcsnctl:
		q = (Qid){QID(csn, Qcsnctl), 0, 0};
		sprint(buf, "csn%dctl", csn);
		devdir(c, q, buf, 0, eve, 0664, dp);
		return 1;
	case Qcsnraw:
		q = (Qid){QID(csn, Qcsnraw), 0, 0};
		sprint(buf, "csn%draw", csn);
		devdir(c, q, buf, cp->ncfg, eve, 0444, dp);
		return 1;
	}
	return -1;
}

static int
pnpgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
{
	Qid q;
	int csn;
	Card *cp;
	char name[KNAMELEN];

DPRINT("pnpgen s %d offset %uld qid %lux\n", s, (ulong)c->offset, (ulong)c->qid.path);
	switch(TYPE(c->qid)){
	case Qtopdir:
		if(s == DEVDOTDOT){
			q = (Qid){QID(0, Qtopdir), 0, QTDIR};
			snprint(name, KNAMELEN, "#%C", pnpdevtab.dc);
			devdir(c, q, name, 0, eve, 0555, dp);
			return 1;
		}
		return devgen(c, nil, topdir, nelem(topdir), s, dp);
	case Qpnpdir:
		if(s == DEVDOTDOT){
			q = (Qid){QID(0, Qtopdir), 0, QTDIR};
			snprint(name, KNAMELEN, "#%C", pnpdevtab.dc);
			devdir(c, q, name, 0, eve, 0555, dp);
			return 1;
		}
DPRINT("Qpnpdir s %d\n", s);
		if(s < nelem(pnpdir))
			return devgen(c, nil, pnpdir, nelem(pnpdir), s, dp);
		s -= nelem(pnpdir);
DPRINT("Qpnpdir s now %d\n", s);
		ilock(&pnp);
		cp = pnp.cards;
		while(s >= 2 && cp != nil) {
			s -= 2;
			cp = cp->next;
		}
		iunlock(&pnp);
DPRINT("Qpnpdir s now %d, cp %p\n", s, cp);
		if(cp == nil)
			return -1;
		return pnpgen1(c, s+Qcsnctl, cp->csn, cp, dp);
	case Qpnpctl:
		return devgen(c, nil, pnpdir, nelem(pnpdir), s, dp);
	case Qcsnctl:
	case Qcsnraw:
		csn = CSN(c->qid);
		ilock(&pnp);
		cp = findcsn(csn, 0);
		iunlock(&pnp);
		if(cp == nil)
			return -1;
		return pnpgen1(c, TYPE(c->qid), csn, cp, dp);
	default:
		break;
	}
	return -1;
}

static Chan*
pnpattach(char *spec)
{
	return devattach(pnpdevtab.dc, spec);
}

Walkqid*
pnpwalk(Chan* c, Chan *nc, char** name, int nname)
{
	return devwalk(c, nc, name, nname, (Dirtab *)0, 0, pnpgen);
}

static int
pnpstat(Chan* c, uchar* dp, int n)
{
	return devstat(c, dp, n, (Dirtab *)0, 0L, pnpgen);
}

static Chan*
pnpopen(Chan *c, int omode)
{
	c = devopen(c, omode, (Dirtab*)0, 0, pnpgen);
	switch(TYPE(c->qid)){
	default:
		break;
	}
	return c;
}

static void
pnpclose(Chan*)
{
}

static long
pnpread(Chan *c, void *va, long n, vlong offset)
{
	int csn, i;
	Card *cp;
	char *a = va, buf[20];

	switch(TYPE(c->qid)){
	case Qtopdir:
	case Qpnpdir:
	case Qpcidir:
		return devdirread(c, a, n, (Dirtab *)0, 0L, pnpgen);
	case Qpnpctl:
		if(pnp.rddata > 0)
			sprint(buf, "enabled 0x%x\n", pnp.rddata);
		else
			sprint(buf, "disabled\n");
		return readstr(offset, a, n, buf);
	case Qcsnraw:
		csn = CSN(c->qid);
		ilock(&pnp);
		cp = findcsn(csn, 0);
		iunlock(&pnp);
		if(cp == nil)
			error(Egreg);
		if(offset+n > cp->ncfg)
			n = cp->ncfg - offset;
		ilock(&pnp);
		initiation();
		cmd(0x03, csn);				/* Wake up the card */
		for (i = 0; i < offset+9; i++)		/* 9 == skip serial + csum */
			getresbyte(pnp.rddata);
		for (i = 0; i < n; i++)
			a[i] = getresbyte(pnp.rddata);
		cmd(0x03, 0);					/* Wake all cards with a CSN of 0, putting this card to sleep */
		cmd(0x02, 0x02);				/* return cards to Wait for Key state */
		iunlock(&pnp);
		break;
	case Qcsnctl:
		csn = CSN(c->qid);
		ilock(&pnp);
		cp = findcsn(csn, 0);
		iunlock(&pnp);
		if(cp == nil)
			error(Egreg);
		sprint(buf, "%s\n", serial(cp->id1, cp->id2));
		return readstr(offset, a, n, buf);
	default:
		error(Egreg);
	}
	return n;
}

static long
pnpwrite(Chan *c, void *a, long n, vlong offset)
{
	ulong port;
	char buf[256];

	switch(TYPE(c->qid)){
	case Qpnpctl:
		if(n >= sizeof(buf))
			n = sizeof(buf)-1;
		strncpy(buf, a, n);
		buf[n] = 0;
		if(strncmp(buf, "rddata ", 7) == 0) {
			port = strtoul(buf+7, 0, 0);
			if(port < 0x203 || port > 0x3ff)
				error("bad value for rddata port");
			if(!pnpscan(port))
				error("no cards found");
		}
		else if(strncmp(buf, "debug ", 6) == 0)
			pnp.debug = strtoul(buf+6, 0, 0);
		else
			error(Ebadctl);
		break;
	default:
USED(offset);
		error(Egreg);
	}
	return n;
}

Dev pnpdevtab = {
	'$',
	"pnp",

	pnpreset,
	devinit,
	pnpattach,
	pnpwalk,
	pnpstat,
	pnpopen,
	devcreate,
	pnpclose,
	pnpread,
	devbread,
	pnpwrite,
	devbwrite,
	devremove,
	devwstat,
};

M port/qio.c => port/qio.c +2 -0
@@ 817,6 817,8 @@ qwait(Queue *q)
		if(q->state & Qclosed){
			if(++q->eof > 3)
				return -1;
			if(*q->err && strcmp(q->err, Ehungup) != 0)
				return -1;
			return 0;
		}


M port/sysfile.c => port/sysfile.c +2 -0
@@ 417,6 417,7 @@ syspread(ulong *arg)
	uvlong v;
	va_list list;

	/* use varargs to guarantee alignment of vlong */
	va_start(list, arg[2]);
	v = va_arg(list, uvlong);
	va_end(list);


@@ 489,6 490,7 @@ syspwrite(ulong *arg)
	uvlong v;
	va_list list;

	/* use varargs to guarantee alignment of vlong */
	va_start(list, arg[2]);
	v = va_arg(list, uvlong);
	va_end(list);