xref: /plan9/sys/src/9/port/devpnp.c (revision aa72973a2891ccbd3fb042462446761159389e19)
19a747e4fSDavid du Colombier /*
29a747e4fSDavid du Colombier  *	ISA PNP 1.0 support + access to PCI configuration space
39a747e4fSDavid du Colombier  *
49a747e4fSDavid du Colombier  *	TODO
59a747e4fSDavid du Colombier  *		- implement PNP card configuration (setting io bases etc)
69a747e4fSDavid du Colombier  *		- write user program to drive PNP configuration...
79a747e4fSDavid du Colombier  *		- extend PCI raw access to configuration space (writes, byte/short access?)
89a747e4fSDavid du Colombier  *		- implement PCI access to memory/io space/BIOS ROM
99a747e4fSDavid du Colombier  *		- use c->aux instead of performing lookup on each read/write?
109a747e4fSDavid du Colombier  */
119a747e4fSDavid du Colombier #include	"u.h"
129a747e4fSDavid du Colombier #include	"../port/lib.h"
139a747e4fSDavid du Colombier #include	"mem.h"
149a747e4fSDavid du Colombier #include	"dat.h"
159a747e4fSDavid du Colombier #include	"fns.h"
169a747e4fSDavid du Colombier #include	"io.h"
179a747e4fSDavid du Colombier #include	"../port/error.h"
189a747e4fSDavid du Colombier 
199a747e4fSDavid du Colombier typedef struct Pnp Pnp;
209a747e4fSDavid du Colombier typedef struct Card Card;
219a747e4fSDavid du Colombier 
229a747e4fSDavid du Colombier struct Pnp
239a747e4fSDavid du Colombier {
249a747e4fSDavid du Colombier 	QLock;
259a747e4fSDavid du Colombier 	int		rddata;
269a747e4fSDavid du Colombier 	int		debug;
279a747e4fSDavid du Colombier 	Card		*cards;
289a747e4fSDavid du Colombier };
299a747e4fSDavid du Colombier 
309a747e4fSDavid du Colombier struct Card
319a747e4fSDavid du Colombier {
329a747e4fSDavid du Colombier 	int		csn;
339a747e4fSDavid du Colombier 	ulong	id1;
349a747e4fSDavid du Colombier 	ulong	id2;
359a747e4fSDavid du Colombier 	char		*cfgstr;
369a747e4fSDavid du Colombier 	int		ncfg;
379a747e4fSDavid du Colombier 	Card*	next;
389a747e4fSDavid du Colombier };
399a747e4fSDavid du Colombier 
409a747e4fSDavid du Colombier static Pnp	pnp;
419a747e4fSDavid du Colombier 
429a747e4fSDavid du Colombier #define	DPRINT	if(pnp.debug) print
439a747e4fSDavid du Colombier #define	XPRINT	if(1) print
449a747e4fSDavid du Colombier 
459a747e4fSDavid du Colombier enum {
469a747e4fSDavid du Colombier 	Address = 0x279,
479a747e4fSDavid du Colombier 	WriteData = 0xa79,
489a747e4fSDavid du Colombier 
499a747e4fSDavid du Colombier 	Qtopdir = 0,
509a747e4fSDavid du Colombier 
519a747e4fSDavid du Colombier 	Qpnpdir,
529a747e4fSDavid du Colombier 	Qpnpctl,
539a747e4fSDavid du Colombier 	Qcsnctl,
549a747e4fSDavid du Colombier 	Qcsnraw,
559a747e4fSDavid du Colombier 
569a747e4fSDavid du Colombier 	Qpcidir,
579a747e4fSDavid du Colombier 	Qpcictl,
589a747e4fSDavid du Colombier 	Qpciraw,
599a747e4fSDavid du Colombier };
609a747e4fSDavid du Colombier 
619a747e4fSDavid du Colombier #define TYPE(q)		((ulong)(q).path & 0x0F)
629a747e4fSDavid du Colombier #define CSN(q)		(((ulong)(q).path>>4) & 0xFF)
639a747e4fSDavid du Colombier #define QID(c, t)	(((c)<<4)|(t))
649a747e4fSDavid du Colombier 
659a747e4fSDavid du Colombier static Dirtab topdir[] = {
669a747e4fSDavid du Colombier 	".",	{ Qtopdir, 0, QTDIR },	0,	0555,
679a747e4fSDavid du Colombier 	"pnp",	{ Qpnpdir, 0, QTDIR },	0,	0555,
689a747e4fSDavid du Colombier 	"pci",	{ Qpcidir, 0, QTDIR },	0,	0555,
699a747e4fSDavid du Colombier };
709a747e4fSDavid du Colombier 
719a747e4fSDavid du Colombier static Dirtab pnpdir[] = {
729a747e4fSDavid du Colombier 	".",	{ Qpnpdir, 0, QTDIR },	0,	0555,
739a747e4fSDavid du Colombier 	"ctl",	{ Qpnpctl, 0, 0 },	0,	0666,
749a747e4fSDavid du Colombier };
759a747e4fSDavid du Colombier 
769a747e4fSDavid du Colombier extern Dev pnpdevtab;
779a747e4fSDavid du Colombier static int wrconfig(Card*, char*);
789a747e4fSDavid du Colombier 
799a747e4fSDavid du Colombier static char key[32] =
809a747e4fSDavid du Colombier {
819a747e4fSDavid du Colombier 	0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB, 0x7D, 0xBE,
829a747e4fSDavid du Colombier 	0xDF, 0x6F, 0x37, 0x1B, 0x0D, 0x86, 0xC3, 0x61,
839a747e4fSDavid du Colombier 	0xB0, 0x58, 0x2C, 0x16, 0x8B, 0x45, 0xA2, 0xD1,
849a747e4fSDavid du Colombier 	0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x39,
859a747e4fSDavid du Colombier };
869a747e4fSDavid du Colombier 
879a747e4fSDavid du Colombier static void
cmd(int reg,int val)889a747e4fSDavid du Colombier cmd(int reg, int val)
899a747e4fSDavid du Colombier {
909a747e4fSDavid du Colombier 	outb(Address, reg);
919a747e4fSDavid du Colombier 	outb(WriteData, val);
929a747e4fSDavid du Colombier }
939a747e4fSDavid du Colombier 
949a747e4fSDavid du Colombier /* Send initiation key, putting each card in Sleep state */
959a747e4fSDavid du Colombier static void
initiation(void)969a747e4fSDavid du Colombier initiation(void)
979a747e4fSDavid du Colombier {
989a747e4fSDavid du Colombier 	int i;
999a747e4fSDavid du Colombier 
1009a747e4fSDavid du Colombier 	/* ensure each card's LFSR is reset */
1019a747e4fSDavid du Colombier 	outb(Address, 0x00);
1029a747e4fSDavid du Colombier 	outb(Address, 0x00);
1039a747e4fSDavid du Colombier 
1049a747e4fSDavid du Colombier 	/* send initiation key */
1059a747e4fSDavid du Colombier 	for (i = 0; i < 32; i++)
1069a747e4fSDavid du Colombier 		outb(Address, key[i]);
1079a747e4fSDavid du Colombier }
1089a747e4fSDavid du Colombier 
1099a747e4fSDavid du Colombier /* isolation protocol... */
1109a747e4fSDavid du Colombier static int
readbit(int rddata)1119a747e4fSDavid du Colombier readbit(int rddata)
1129a747e4fSDavid du Colombier {
1139a747e4fSDavid du Colombier 	int r1, r2;
1149a747e4fSDavid du Colombier 
1159a747e4fSDavid du Colombier 	r1 = inb(rddata);
1169a747e4fSDavid du Colombier 	r2 = inb(rddata);
1179a747e4fSDavid du Colombier 	microdelay(250);
1189a747e4fSDavid du Colombier 	return (r1 == 0x55) && (r2 == 0xaa);
1199a747e4fSDavid du Colombier }
1209a747e4fSDavid du Colombier 
1219a747e4fSDavid du Colombier static int
isolate(int rddata,ulong * id1,ulong * id2)1229a747e4fSDavid du Colombier isolate(int rddata, ulong *id1, ulong *id2)
1239a747e4fSDavid du Colombier {
1249a747e4fSDavid du Colombier 	int i, csum, bit;
1259a747e4fSDavid du Colombier 	uchar *p, id[9];
1269a747e4fSDavid du Colombier 
1279a747e4fSDavid du Colombier 	outb(Address, 0x01);	/* point to serial isolation register */
1289a747e4fSDavid du Colombier 	delay(1);
1299a747e4fSDavid du Colombier 	csum = 0x6a;
1309a747e4fSDavid du Colombier 	for(i = 0; i < 64; i++){
1319a747e4fSDavid du Colombier 		bit = readbit(rddata);
1329a747e4fSDavid du Colombier 		csum = (csum>>1) | (((csum&1) ^ ((csum>>1)&1) ^ bit)<<7);
1339a747e4fSDavid du Colombier 		p = &id[i>>3];
1349a747e4fSDavid du Colombier 		*p = (*p>>1) | (bit<<7);
1359a747e4fSDavid du Colombier 	}
1369a747e4fSDavid du Colombier 	for(; i < 72; i++){
1379a747e4fSDavid du Colombier 		p = &id[i>>3];
1389a747e4fSDavid du Colombier 		*p = (*p>>1) | (readbit(rddata)<<7);
1399a747e4fSDavid du Colombier 	}
1409a747e4fSDavid du Colombier 	*id1 = (id[3]<<24)|(id[2]<<16)|(id[1]<<8)|id[0];
1419a747e4fSDavid du Colombier 	*id2 = (id[7]<<24)|(id[6]<<16)|(id[5]<<8)|id[4];
1429a747e4fSDavid du Colombier 	if(*id1 == 0)
1439a747e4fSDavid du Colombier 		return 0;
1449a747e4fSDavid du Colombier 	if(id[8] != csum)
1459a747e4fSDavid du Colombier 		DPRINT("pnp: bad checksum id1 %lux id2 %lux csum %x != %x\n", *id1, *id2, csum, id[8]); /**/
1469a747e4fSDavid du Colombier 	return id[8] == csum;
1479a747e4fSDavid du Colombier }
1489a747e4fSDavid du Colombier 
1499a747e4fSDavid du Colombier static int
getresbyte(int rddata)1509a747e4fSDavid du Colombier getresbyte(int rddata)
1519a747e4fSDavid du Colombier {
1529a747e4fSDavid du Colombier 	int tries = 0;
1539a747e4fSDavid du Colombier 
1549a747e4fSDavid du Colombier 	outb(Address, 0x05);
1559a747e4fSDavid du Colombier 	while ((inb(rddata) & 1) == 0)
1569a747e4fSDavid du Colombier 		if (tries++ > 1000000)
1579a747e4fSDavid du Colombier 			error("pnp: timeout waiting for resource data\n");
1589a747e4fSDavid du Colombier 	outb(Address, 0x04);
1599a747e4fSDavid du Colombier 	return inb(rddata);
1609a747e4fSDavid du Colombier }
1619a747e4fSDavid du Colombier 
1629a747e4fSDavid du Colombier static char *
serial(ulong id1,ulong id2)1639a747e4fSDavid du Colombier serial(ulong id1, ulong id2)
1649a747e4fSDavid du Colombier {
1659a747e4fSDavid du Colombier 	int i1, i2, i3;
1669a747e4fSDavid du Colombier 	ulong x;
1679a747e4fSDavid du Colombier 	static char buf[20];
1689a747e4fSDavid du Colombier 
1699a747e4fSDavid du Colombier 	i1 = (id1>>2)&31;
1709a747e4fSDavid du Colombier 	i2 = ((id1<<3)&24)+((id1>>13)&7);
1719a747e4fSDavid du Colombier 	i3 = (id1>>8)&31;
1729a747e4fSDavid du Colombier 	x = (id1>>8)&0xff00|(id1>>24)&0x00ff;
1739a747e4fSDavid du Colombier 	if (i1 > 0 && i1 < 27 && i2 > 0 && i2 < 27 && i3 > 0 && i3 < 27 && (id1 & (1<<7)) == 0)
1749a747e4fSDavid du Colombier 		snprint(buf, sizeof(buf), "%c%c%c%.4lux.%lux", 'A'+i1-1, 'A'+i2-1, 'A'+i3-1, x, id2);
1759a747e4fSDavid du Colombier 	else
1769a747e4fSDavid du Colombier 		snprint(buf, sizeof(buf), "%.4lux%.4lux.%lux", (id1<<8)&0xff00|(id1>>8)&0x00ff, x, id2);
1779a747e4fSDavid du Colombier 	return buf;
1789a747e4fSDavid du Colombier }
1799a747e4fSDavid du Colombier 
1809a747e4fSDavid du Colombier static Card *
findcsn(int csn,int create,int dolock)1819a747e4fSDavid du Colombier findcsn(int csn, int create, int dolock)
1829a747e4fSDavid du Colombier {
1839a747e4fSDavid du Colombier 	Card *c, *nc, **l;
1849a747e4fSDavid du Colombier 
1859a747e4fSDavid du Colombier 	if(dolock)
1869a747e4fSDavid du Colombier 		qlock(&pnp);
1879a747e4fSDavid du Colombier 	l = &pnp.cards;
1889a747e4fSDavid du Colombier 	for(c = *l; c != nil; c = *l) {
1899a747e4fSDavid du Colombier 		if(c->csn == csn)
1909a747e4fSDavid du Colombier 			goto done;
1919a747e4fSDavid du Colombier 		if(c->csn > csn)
1929a747e4fSDavid du Colombier 			break;
1939a747e4fSDavid du Colombier 		l = &c->next;
1949a747e4fSDavid du Colombier 	}
1959a747e4fSDavid du Colombier 	if(create) {
1969a747e4fSDavid du Colombier 		*l = nc = malloc(sizeof(Card));
197*aa72973aSDavid du Colombier 		if(nc == nil) {
198*aa72973aSDavid du Colombier 			if(dolock)
199*aa72973aSDavid du Colombier 				qunlock(&pnp);
200*aa72973aSDavid du Colombier 			error(Enomem);
201*aa72973aSDavid du Colombier 		}
2029a747e4fSDavid du Colombier 		nc->next = c;
2039a747e4fSDavid du Colombier 		nc->csn = csn;
2049a747e4fSDavid du Colombier 		c = nc;
2059a747e4fSDavid du Colombier 	}
2069a747e4fSDavid du Colombier done:
2079a747e4fSDavid du Colombier 	if(dolock)
2089a747e4fSDavid du Colombier 		qunlock(&pnp);
2099a747e4fSDavid du Colombier 	return c;
2109a747e4fSDavid du Colombier }
2119a747e4fSDavid du Colombier 
2129a747e4fSDavid du Colombier static int
newcsn(void)2139a747e4fSDavid du Colombier newcsn(void)
2149a747e4fSDavid du Colombier {
2159a747e4fSDavid du Colombier 	int csn;
2169a747e4fSDavid du Colombier 	Card *c;
2179a747e4fSDavid du Colombier 
2189a747e4fSDavid du Colombier 	csn = 1;
2199a747e4fSDavid du Colombier 	for(c = pnp.cards; c != nil; c = c->next) {
2209a747e4fSDavid du Colombier 		if(c->csn > csn)
2219a747e4fSDavid du Colombier 			break;
2229a747e4fSDavid du Colombier 		csn = c->csn+1;
2239a747e4fSDavid du Colombier 	}
2249a747e4fSDavid du Colombier 	return csn;
2259a747e4fSDavid du Colombier }
2269a747e4fSDavid du Colombier 
2279a747e4fSDavid du Colombier static int
pnpncfg(int rddata)2289a747e4fSDavid du Colombier pnpncfg(int rddata)
2299a747e4fSDavid du Colombier {
2309a747e4fSDavid du Colombier 	int i, n, x, ncfg, n1, n2;
2319a747e4fSDavid du Colombier 
2329a747e4fSDavid du Colombier 	ncfg = 0;
2339a747e4fSDavid du Colombier 	for (;;) {
2349a747e4fSDavid du Colombier 		x = getresbyte(rddata);
2359a747e4fSDavid du Colombier 		if((x & 0x80) == 0) {
2369a747e4fSDavid du Colombier 			n = (x&7)+1;
2379a747e4fSDavid du Colombier 			for(i = 1; i < n; i++)
2389a747e4fSDavid du Colombier 				getresbyte(rddata);
2399a747e4fSDavid du Colombier 		}
2409a747e4fSDavid du Colombier 		else {
2419a747e4fSDavid du Colombier 			n1 = getresbyte(rddata);
2429a747e4fSDavid du Colombier 			n2 = getresbyte(rddata);
2439a747e4fSDavid du Colombier 			n = (n2<<8)|n1 + 3;
2449a747e4fSDavid du Colombier 			for (i = 3; i < n; i++)
2459a747e4fSDavid du Colombier 				getresbyte(rddata);
2469a747e4fSDavid du Colombier 		}
2479a747e4fSDavid du Colombier 		ncfg += n;
2489a747e4fSDavid du Colombier 		if((x>>3) == 0x0f)
2499a747e4fSDavid du Colombier 			break;
2509a747e4fSDavid du Colombier 	}
2519a747e4fSDavid du Colombier 	return ncfg;
2529a747e4fSDavid du Colombier }
2539a747e4fSDavid du Colombier 
2549a747e4fSDavid du Colombier /* look for cards, and assign them CSNs */
2559a747e4fSDavid du Colombier static int
pnpscan(int rddata,int dawn)2569a747e4fSDavid du Colombier pnpscan(int rddata, int dawn)
2579a747e4fSDavid du Colombier {
2589a747e4fSDavid du Colombier 	Card *c;
2599a747e4fSDavid du Colombier 	int csn;
2609a747e4fSDavid du Colombier 	ulong id1, id2;
2619a747e4fSDavid du Colombier 
2629a747e4fSDavid du Colombier 	initiation();				/* upsilon sigma */
2639a747e4fSDavid du Colombier 	cmd(0x02, 0x04+0x01);		/* reset CSN on all cards and reset logical devices */
2649a747e4fSDavid du Colombier 	delay(1);					/* delay after resetting cards */
2659a747e4fSDavid du Colombier 
2669a747e4fSDavid du Colombier 	cmd(0x03, 0);				/* Wake all cards with a CSN of 0 */
2679a747e4fSDavid du Colombier 	cmd(0x00, rddata>>2);		/* Set the READ_DATA port on all cards */
2689a747e4fSDavid du Colombier 	while(isolate(rddata, &id1, &id2)) {
2699a747e4fSDavid du Colombier 		for(c = pnp.cards; c != nil; c = c->next)
2709a747e4fSDavid du Colombier 			if(c->id1 == id1 && c->id2 == id2)
2719a747e4fSDavid du Colombier 				break;
2729a747e4fSDavid du Colombier 		if(c == nil) {
2739a747e4fSDavid du Colombier 			csn = newcsn();
2749a747e4fSDavid du Colombier 			c = findcsn(csn, 1, 0);
2759a747e4fSDavid du Colombier 			c->id1 = id1;
2769a747e4fSDavid du Colombier 			c->id2 = id2;
2779a747e4fSDavid du Colombier 		}
2789a747e4fSDavid du Colombier 		else if(c->cfgstr != nil) {
2799a747e4fSDavid du Colombier 			if(!wrconfig(c, c->cfgstr))
2809a747e4fSDavid du Colombier 				print("pnp%d: bad cfg: %s\n", c->csn, c->cfgstr);
2819a747e4fSDavid du Colombier 			c->cfgstr = nil;
2829a747e4fSDavid du Colombier 		}
2839a747e4fSDavid du Colombier 		cmd(0x06, c->csn);		/* set the card's csn */
2849a747e4fSDavid du Colombier 		if(dawn)
2859a747e4fSDavid du Colombier 			print("pnp%d: %s\n", c->csn, serial(id1, id2));
2869a747e4fSDavid du Colombier 		c->ncfg = pnpncfg(rddata);
2879a747e4fSDavid du Colombier 		cmd(0x03, 0);		/* Wake all cards with a CSN of 0, putting this card to sleep */
2889a747e4fSDavid du Colombier 	}
2899a747e4fSDavid du Colombier 	cmd(0x02, 0x02);			/* return cards to Wait for Key state */
2909a747e4fSDavid du Colombier 	if(pnp.cards != 0) {
2919a747e4fSDavid du Colombier 		pnp.rddata = rddata;
2929a747e4fSDavid du Colombier 		return 1;
2939a747e4fSDavid du Colombier 	}
2949a747e4fSDavid du Colombier 	return 0;
2959a747e4fSDavid du Colombier }
2969a747e4fSDavid du Colombier 
2979a747e4fSDavid du Colombier static void
pnpreset(void)2989a747e4fSDavid du Colombier pnpreset(void)
2999a747e4fSDavid du Colombier {
3009a747e4fSDavid du Colombier 	Card *c;
3019a747e4fSDavid du Colombier 	ulong id1, id2;
3029a747e4fSDavid du Colombier 	int csn, i1, i2, i3, x;
3039a747e4fSDavid du Colombier 	char *s, *p, buf[20];
3049a747e4fSDavid du Colombier 	ISAConf isa;
3059a747e4fSDavid du Colombier 
3069a747e4fSDavid du Colombier 	memset(&isa, 0, sizeof(ISAConf));
3079a747e4fSDavid du Colombier 	pnp.rddata = -1;
3089a747e4fSDavid du Colombier 	if (isaconfig("pnp", 0, &isa) == 0)
3099a747e4fSDavid du Colombier 		return;
3109a747e4fSDavid du Colombier 	if(isa.port < 0x203 || isa.port > 0x3ff)
3119a747e4fSDavid du Colombier 		return;
3129a747e4fSDavid du Colombier 	for(csn = 1; csn < 256; csn++) {
31357d98441SDavid du Colombier 		snprint(buf, sizeof buf, "pnp%d", csn);
3149a747e4fSDavid du Colombier 		s = getconf(buf);
3159a747e4fSDavid du Colombier 		if(s == 0)
3169a747e4fSDavid du Colombier 			continue;
3179a747e4fSDavid du Colombier 		if(strlen(s) < 8 || s[7] != '.' || s[0] < 'A' || s[0] > 'Z' || s[1] < 'A' || s[1] > 'Z' || s[2] < 'A' || s[2] > 'Z') {
3189a747e4fSDavid du Colombier bad:
3199a747e4fSDavid du Colombier 			print("pnp%d: bad conf string %s\n", csn, s);
3209a747e4fSDavid du Colombier 			continue;
3219a747e4fSDavid du Colombier 		}
3229a747e4fSDavid du Colombier 		i1 = s[0]-'A'+1;
3239a747e4fSDavid du Colombier 		i2 = s[1]-'A'+1;
3249a747e4fSDavid du Colombier 		i3 = s[2]-'A'+1;
3259a747e4fSDavid du Colombier 		x = strtoul(&s[3], 0, 16);
3269a747e4fSDavid du Colombier 		id1 = (i1<<2)|((i2>>3)&3)|((i2&7)<<13)|(i3<<8)|((x&0xff)<<24)|((x&0xff00)<<8);
3279a747e4fSDavid du Colombier 		id2 = strtoul(&s[8], &p, 16);
3289a747e4fSDavid du Colombier 		if(*p == ' ')
3299a747e4fSDavid du Colombier 			p++;
3309a747e4fSDavid du Colombier 		else if(*p == '\0')
3319a747e4fSDavid du Colombier 			p = nil;
3329a747e4fSDavid du Colombier 		else
3339a747e4fSDavid du Colombier 			goto bad;
3349a747e4fSDavid du Colombier 		c = findcsn(csn, 1, 0);
3359a747e4fSDavid du Colombier 		c->id1 = id1;
3369a747e4fSDavid du Colombier 		c->id2 = id2;
3379a747e4fSDavid du Colombier 		c->cfgstr = p;
3389a747e4fSDavid du Colombier 	}
3399a747e4fSDavid du Colombier 	pnpscan(isa.port, 1);
3409a747e4fSDavid du Colombier }
3419a747e4fSDavid du Colombier 
3429a747e4fSDavid du Colombier static int
csngen(Chan * c,int t,int csn,Card * cp,Dir * dp)3439a747e4fSDavid du Colombier csngen(Chan *c, int t, int csn, Card *cp, Dir *dp)
3449a747e4fSDavid du Colombier {
3459a747e4fSDavid du Colombier 	Qid q;
3469a747e4fSDavid du Colombier 
3479a747e4fSDavid du Colombier 	switch(t) {
3489a747e4fSDavid du Colombier 	case Qcsnctl:
3499a747e4fSDavid du Colombier 		q = (Qid){QID(csn, Qcsnctl), 0, 0};
35057d98441SDavid du Colombier 		snprint(up->genbuf, sizeof up->genbuf, "csn%dctl", csn);
3519a747e4fSDavid du Colombier 		devdir(c, q, up->genbuf, 0, eve, 0664, dp);
3529a747e4fSDavid du Colombier 		return 1;
3539a747e4fSDavid du Colombier 	case Qcsnraw:
3549a747e4fSDavid du Colombier 		q = (Qid){QID(csn, Qcsnraw), 0, 0};
35557d98441SDavid du Colombier 		snprint(up->genbuf, sizeof up->genbuf, "csn%draw", csn);
3569a747e4fSDavid du Colombier 		devdir(c, q, up->genbuf, cp->ncfg, eve, 0444, dp);
3579a747e4fSDavid du Colombier 		return 1;
3589a747e4fSDavid du Colombier 	}
3599a747e4fSDavid du Colombier 	return -1;
3609a747e4fSDavid du Colombier }
3619a747e4fSDavid du Colombier 
3629a747e4fSDavid du Colombier static int
pcigen(Chan * c,int t,int tbdf,Dir * dp)3639a747e4fSDavid du Colombier pcigen(Chan *c, int t, int tbdf, Dir *dp)
3649a747e4fSDavid du Colombier {
3659a747e4fSDavid du Colombier 	Qid q;
3669a747e4fSDavid du Colombier 
3679a747e4fSDavid du Colombier 	q = (Qid){BUSBDF(tbdf)|t, 0, 0};
3689a747e4fSDavid du Colombier 	switch(t) {
3699a747e4fSDavid du Colombier 	case Qpcictl:
37057d98441SDavid du Colombier 		snprint(up->genbuf, sizeof up->genbuf, "%d.%d.%dctl",
37157d98441SDavid du Colombier 			BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
3729a747e4fSDavid du Colombier 		devdir(c, q, up->genbuf, 0, eve, 0444, dp);
3739a747e4fSDavid du Colombier 		return 1;
3749a747e4fSDavid du Colombier 	case Qpciraw:
37557d98441SDavid du Colombier 		snprint(up->genbuf, sizeof up->genbuf, "%d.%d.%draw",
37657d98441SDavid du Colombier 			BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
37751af9883SDavid du Colombier 		devdir(c, q, up->genbuf, 128, eve, 0660, dp);
3789a747e4fSDavid du Colombier 		return 1;
3799a747e4fSDavid du Colombier 	}
3809a747e4fSDavid du Colombier 	return -1;
3819a747e4fSDavid du Colombier }
3829a747e4fSDavid du Colombier 
3839a747e4fSDavid du Colombier static int
pnpgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)3849a747e4fSDavid du Colombier pnpgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
3859a747e4fSDavid du Colombier {
3869a747e4fSDavid du Colombier 	Qid q;
3879a747e4fSDavid du Colombier 	Card *cp;
3889a747e4fSDavid du Colombier 	Pcidev *p;
3899a747e4fSDavid du Colombier 	int csn, tbdf;
3909a747e4fSDavid du Colombier 
3919a747e4fSDavid du Colombier 	switch(TYPE(c->qid)){
3929a747e4fSDavid du Colombier 	case Qtopdir:
3939a747e4fSDavid du Colombier 		if(s == DEVDOTDOT){
3949a747e4fSDavid du Colombier 			q = (Qid){QID(0, Qtopdir), 0, QTDIR};
39557d98441SDavid du Colombier 			snprint(up->genbuf, sizeof up->genbuf, "#%C", pnpdevtab.dc);
3969a747e4fSDavid du Colombier 			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
3979a747e4fSDavid du Colombier 			return 1;
3989a747e4fSDavid du Colombier 		}
3999a747e4fSDavid du Colombier 		return devgen(c, nil, topdir, nelem(topdir), s, dp);
4009a747e4fSDavid du Colombier 	case Qpnpdir:
4019a747e4fSDavid du Colombier 		if(s == DEVDOTDOT){
4029a747e4fSDavid du Colombier 			q = (Qid){QID(0, Qtopdir), 0, QTDIR};
40357d98441SDavid du Colombier 			snprint(up->genbuf, sizeof up->genbuf, "#%C", pnpdevtab.dc);
4049a747e4fSDavid du Colombier 			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
4059a747e4fSDavid du Colombier 			return 1;
4069a747e4fSDavid du Colombier 		}
4079a747e4fSDavid du Colombier 		if(s < nelem(pnpdir)-1)
4089a747e4fSDavid du Colombier 			return devgen(c, nil, pnpdir, nelem(pnpdir), s, dp);
4099a747e4fSDavid du Colombier 		s -= nelem(pnpdir)-1;
4109a747e4fSDavid du Colombier 		qlock(&pnp);
4119a747e4fSDavid du Colombier 		cp = pnp.cards;
4129a747e4fSDavid du Colombier 		while(s >= 2 && cp != nil) {
4139a747e4fSDavid du Colombier 			s -= 2;
4149a747e4fSDavid du Colombier 			cp = cp->next;
4159a747e4fSDavid du Colombier 		}
4169a747e4fSDavid du Colombier 		qunlock(&pnp);
4179a747e4fSDavid du Colombier 		if(cp == nil)
4189a747e4fSDavid du Colombier 			return -1;
4199a747e4fSDavid du Colombier 		return csngen(c, s+Qcsnctl, cp->csn, cp, dp);
4209a747e4fSDavid du Colombier 	case Qpnpctl:
4219a747e4fSDavid du Colombier 		return devgen(c, nil, pnpdir, nelem(pnpdir), s, dp);
4229a747e4fSDavid du Colombier 	case Qcsnctl:
4239a747e4fSDavid du Colombier 	case Qcsnraw:
4249a747e4fSDavid du Colombier 		csn = CSN(c->qid);
4259a747e4fSDavid du Colombier 		cp = findcsn(csn, 0, 1);
4269a747e4fSDavid du Colombier 		if(cp == nil)
4279a747e4fSDavid du Colombier 			return -1;
4289a747e4fSDavid du Colombier 		return csngen(c, TYPE(c->qid), csn, cp, dp);
4299a747e4fSDavid du Colombier 	case Qpcidir:
4309a747e4fSDavid du Colombier 		if(s == DEVDOTDOT){
4319a747e4fSDavid du Colombier 			q = (Qid){QID(0, Qtopdir), 0, QTDIR};
43257d98441SDavid du Colombier 			snprint(up->genbuf, sizeof up->genbuf, "#%C", pnpdevtab.dc);
4339a747e4fSDavid du Colombier 			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
4349a747e4fSDavid du Colombier 			return 1;
4359a747e4fSDavid du Colombier 		}
4369a747e4fSDavid du Colombier 		p = pcimatch(nil, 0, 0);
4379a747e4fSDavid du Colombier 		while(s >= 2 && p != nil) {
4389a747e4fSDavid du Colombier 			p = pcimatch(p, 0, 0);
4399a747e4fSDavid du Colombier 			s -= 2;
4409a747e4fSDavid du Colombier 		}
4419a747e4fSDavid du Colombier 		if(p == nil)
4429a747e4fSDavid du Colombier 			return -1;
4439a747e4fSDavid du Colombier 		return pcigen(c, s+Qpcictl, p->tbdf, dp);
4449a747e4fSDavid du Colombier 	case Qpcictl:
4459a747e4fSDavid du Colombier 	case Qpciraw:
4469a747e4fSDavid du Colombier 		tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
4479a747e4fSDavid du Colombier 		p = pcimatchtbdf(tbdf);
4489a747e4fSDavid du Colombier 		if(p == nil)
4499a747e4fSDavid du Colombier 			return -1;
4509a747e4fSDavid du Colombier 		return pcigen(c, TYPE(c->qid), tbdf, dp);
4519a747e4fSDavid du Colombier 	default:
4529a747e4fSDavid du Colombier 		break;
4539a747e4fSDavid du Colombier 	}
4549a747e4fSDavid du Colombier 	return -1;
4559a747e4fSDavid du Colombier }
4569a747e4fSDavid du Colombier 
4579a747e4fSDavid du Colombier static Chan*
pnpattach(char * spec)4589a747e4fSDavid du Colombier pnpattach(char *spec)
4599a747e4fSDavid du Colombier {
4609a747e4fSDavid du Colombier 	return devattach(pnpdevtab.dc, spec);
4619a747e4fSDavid du Colombier }
4629a747e4fSDavid du Colombier 
4639a747e4fSDavid du Colombier Walkqid*
pnpwalk(Chan * c,Chan * nc,char ** name,int nname)4649a747e4fSDavid du Colombier pnpwalk(Chan* c, Chan *nc, char** name, int nname)
4659a747e4fSDavid du Colombier {
4669a747e4fSDavid du Colombier 	return devwalk(c, nc, name, nname, (Dirtab *)0, 0, pnpgen);
4679a747e4fSDavid du Colombier }
4689a747e4fSDavid du Colombier 
4699a747e4fSDavid du Colombier static int
pnpstat(Chan * c,uchar * dp,int n)4709a747e4fSDavid du Colombier pnpstat(Chan* c, uchar* dp, int n)
4719a747e4fSDavid du Colombier {
4729a747e4fSDavid du Colombier 	return devstat(c, dp, n, (Dirtab *)0, 0L, pnpgen);
4739a747e4fSDavid du Colombier }
4749a747e4fSDavid du Colombier 
4759a747e4fSDavid du Colombier static Chan*
pnpopen(Chan * c,int omode)4769a747e4fSDavid du Colombier pnpopen(Chan *c, int omode)
4779a747e4fSDavid du Colombier {
4789a747e4fSDavid du Colombier 	c = devopen(c, omode, (Dirtab*)0, 0, pnpgen);
4799a747e4fSDavid du Colombier 	switch(TYPE(c->qid)){
4809a747e4fSDavid du Colombier 	default:
4819a747e4fSDavid du Colombier 		break;
4829a747e4fSDavid du Colombier 	}
4839a747e4fSDavid du Colombier 	return c;
4849a747e4fSDavid du Colombier }
4859a747e4fSDavid du Colombier 
4869a747e4fSDavid du Colombier static void
pnpclose(Chan *)4879a747e4fSDavid du Colombier pnpclose(Chan*)
4889a747e4fSDavid du Colombier {
4899a747e4fSDavid du Colombier }
4909a747e4fSDavid du Colombier 
4919a747e4fSDavid du Colombier static long
pnpread(Chan * c,void * va,long n,vlong offset)4929a747e4fSDavid du Colombier pnpread(Chan *c, void *va, long n, vlong offset)
4939a747e4fSDavid du Colombier {
4949a747e4fSDavid du Colombier 	ulong x;
4959a747e4fSDavid du Colombier 	Card *cp;
4969a747e4fSDavid du Colombier 	Pcidev *p;
4979a747e4fSDavid du Colombier 	char buf[256], *ebuf, *w;
4989a747e4fSDavid du Colombier 	char *a = va;
4999a747e4fSDavid du Colombier 	int csn, i, tbdf, r;
5009a747e4fSDavid du Colombier 
5019a747e4fSDavid du Colombier 	switch(TYPE(c->qid)){
5029a747e4fSDavid du Colombier 	case Qtopdir:
5039a747e4fSDavid du Colombier 	case Qpnpdir:
5049a747e4fSDavid du Colombier 	case Qpcidir:
5059a747e4fSDavid du Colombier 		return devdirread(c, a, n, (Dirtab *)0, 0L, pnpgen);
5069a747e4fSDavid du Colombier 	case Qpnpctl:
5079a747e4fSDavid du Colombier 		if(pnp.rddata > 0)
50857d98441SDavid du Colombier 			snprint(up->genbuf, sizeof up->genbuf, "enabled %#x\n",
50957d98441SDavid du Colombier 				pnp.rddata);
5109a747e4fSDavid du Colombier 		else
51157d98441SDavid du Colombier 			snprint(up->genbuf, sizeof up->genbuf, "disabled\n");
5129a747e4fSDavid du Colombier 		return readstr(offset, a, n, up->genbuf);
5139a747e4fSDavid du Colombier 	case Qcsnraw:
5149a747e4fSDavid du Colombier 		csn = CSN(c->qid);
5159a747e4fSDavid du Colombier 		cp = findcsn(csn, 0, 1);
5169a747e4fSDavid du Colombier 		if(cp == nil)
5179a747e4fSDavid du Colombier 			error(Egreg);
5189a747e4fSDavid du Colombier 		if(offset+n > cp->ncfg)
5199a747e4fSDavid du Colombier 			n = cp->ncfg - offset;
5209a747e4fSDavid du Colombier 		qlock(&pnp);
5219a747e4fSDavid du Colombier 		initiation();
5229a747e4fSDavid du Colombier 		cmd(0x03, csn);				/* Wake up the card */
5239a747e4fSDavid du Colombier 		for(i = 0; i < offset+9; i++)		/* 9 == skip serial + csum */
5249a747e4fSDavid du Colombier 			getresbyte(pnp.rddata);
5259a747e4fSDavid du Colombier 		for(i = 0; i < n; i++)
5269a747e4fSDavid du Colombier 			a[i] = getresbyte(pnp.rddata);
5279a747e4fSDavid du Colombier 		cmd(0x03, 0);					/* Wake all cards with a CSN of 0, putting this card to sleep */
5289a747e4fSDavid du Colombier 		cmd(0x02, 0x02);				/* return cards to Wait for Key state */
5299a747e4fSDavid du Colombier 		qunlock(&pnp);
5309a747e4fSDavid du Colombier 		break;
5319a747e4fSDavid du Colombier 	case Qcsnctl:
5329a747e4fSDavid du Colombier 		csn = CSN(c->qid);
5339a747e4fSDavid du Colombier 		cp = findcsn(csn, 0, 1);
5349a747e4fSDavid du Colombier 		if(cp == nil)
5359a747e4fSDavid du Colombier 			error(Egreg);
53657d98441SDavid du Colombier 		snprint(up->genbuf, sizeof up->genbuf, "%s\n",
53757d98441SDavid du Colombier 			serial(cp->id1, cp->id2));
5389a747e4fSDavid du Colombier 		return readstr(offset, a, n, up->genbuf);
5399a747e4fSDavid du Colombier 	case Qpcictl:
5409a747e4fSDavid du Colombier 		tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
5419a747e4fSDavid du Colombier 		p = pcimatchtbdf(tbdf);
5429a747e4fSDavid du Colombier 		if(p == nil)
5439a747e4fSDavid du Colombier 			error(Egreg);
5449a747e4fSDavid du Colombier 		ebuf = buf+sizeof buf-1;	/* -1 for newline */
5459a747e4fSDavid du Colombier 		w = seprint(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d",
5469a747e4fSDavid du Colombier 			p->ccrb, p->ccru, p->ccrp, p->vid, p->did, p->intl);
5479a747e4fSDavid du Colombier 		for(i=0; i<nelem(p->mem); i++){
5489a747e4fSDavid du Colombier 			if(p->mem[i].size == 0)
5499a747e4fSDavid du Colombier 				continue;
5509a747e4fSDavid du Colombier 			w = seprint(w, ebuf, " %d:%.8lux %d", i, p->mem[i].bar, p->mem[i].size);
5519a747e4fSDavid du Colombier 		}
5529a747e4fSDavid du Colombier 		*w++ = '\n';
5539a747e4fSDavid du Colombier 		*w = '\0';
5549a747e4fSDavid du Colombier 		return readstr(offset, a, n, buf);
5559a747e4fSDavid du Colombier 	case Qpciraw:
5569a747e4fSDavid du Colombier 		tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
5579a747e4fSDavid du Colombier 		p = pcimatchtbdf(tbdf);
5589a747e4fSDavid du Colombier 		if(p == nil)
5599a747e4fSDavid du Colombier 			error(Egreg);
5609a747e4fSDavid du Colombier 		if(offset > 256)
5619a747e4fSDavid du Colombier 			return 0;
5629a747e4fSDavid du Colombier 		if(n+offset > 256)
5639a747e4fSDavid du Colombier 			n = 256-offset;
5649a747e4fSDavid du Colombier 		r = offset;
56551af9883SDavid du Colombier 		if(!(r & 3) && n == 4){
5669a747e4fSDavid du Colombier 			x = pcicfgr32(p, r);
56751af9883SDavid du Colombier 			PBIT32(a, x);
56851af9883SDavid du Colombier 			return 4;
56951af9883SDavid du Colombier 		}
57051af9883SDavid du Colombier 		if(!(r & 1) && n == 2){
57151af9883SDavid du Colombier 			x = pcicfgr16(p, r);
57251af9883SDavid du Colombier 			PBIT16(a, x);
57351af9883SDavid du Colombier 			return 2;
57451af9883SDavid du Colombier 		}
57551af9883SDavid du Colombier 		for(i = 0; i <  n; i++){
57651af9883SDavid du Colombier 			x = pcicfgr8(p, r);
57751af9883SDavid du Colombier 			PBIT8(a, x);
57851af9883SDavid du Colombier 			a++;
57951af9883SDavid du Colombier 			r++;
5809a747e4fSDavid du Colombier 		}
5819a747e4fSDavid du Colombier 		return i;
5829a747e4fSDavid du Colombier 	default:
5839a747e4fSDavid du Colombier 		error(Egreg);
5849a747e4fSDavid du Colombier 	}
5859a747e4fSDavid du Colombier 	return n;
5869a747e4fSDavid du Colombier }
5879a747e4fSDavid du Colombier 
5889a747e4fSDavid du Colombier static long
pnpwrite(Chan * c,void * va,long n,vlong offset)58951af9883SDavid du Colombier pnpwrite(Chan *c, void *va, long n, vlong offset)
5909a747e4fSDavid du Colombier {
5919a747e4fSDavid du Colombier 	Card *cp;
59251af9883SDavid du Colombier 	Pcidev *p;
59351af9883SDavid du Colombier 	ulong port, x;
594274d35baSDavid du Colombier 	char buf[256];
595274d35baSDavid du Colombier 	uchar *a;
59651af9883SDavid du Colombier 	int csn, i, r, tbdf;
5979a747e4fSDavid du Colombier 
5989a747e4fSDavid du Colombier 	if(n >= sizeof(buf))
5999a747e4fSDavid du Colombier 		n = sizeof(buf)-1;
60051af9883SDavid du Colombier 	a = va;
601274d35baSDavid du Colombier 	strncpy(buf, va, n);
6029a747e4fSDavid du Colombier 	buf[n] = 0;
6039a747e4fSDavid du Colombier 
6049a747e4fSDavid du Colombier 	switch(TYPE(c->qid)){
6059a747e4fSDavid du Colombier 	case Qpnpctl:
6069a747e4fSDavid du Colombier 		if(strncmp(buf, "port ", 5) == 0) {
6079a747e4fSDavid du Colombier 			port = strtoul(buf+5, 0, 0);
6089a747e4fSDavid du Colombier 			if(port < 0x203 || port > 0x3ff)
6099a747e4fSDavid du Colombier 				error("bad value for rddata port");
6109a747e4fSDavid du Colombier 			qlock(&pnp);
6119a747e4fSDavid du Colombier 			if(waserror()) {
6129a747e4fSDavid du Colombier 				qunlock(&pnp);
6139a747e4fSDavid du Colombier 				nexterror();
6149a747e4fSDavid du Colombier 			}
6159a747e4fSDavid du Colombier 			if(pnp.rddata > 0)
6169a747e4fSDavid du Colombier 				error("pnp port already set");
6179a747e4fSDavid du Colombier 			if(!pnpscan(port, 0))
6189a747e4fSDavid du Colombier 				error("no cards found");
6199a747e4fSDavid du Colombier 			qunlock(&pnp);
6209a747e4fSDavid du Colombier 			poperror();
6219a747e4fSDavid du Colombier 		}
6229a747e4fSDavid du Colombier 		else if(strncmp(buf, "debug ", 6) == 0)
6239a747e4fSDavid du Colombier 			pnp.debug = strtoul(buf+6, 0, 0);
6249a747e4fSDavid du Colombier 		else
6259a747e4fSDavid du Colombier 			error(Ebadctl);
6269a747e4fSDavid du Colombier 		break;
6279a747e4fSDavid du Colombier 	case Qcsnctl:
6289a747e4fSDavid du Colombier 		csn = CSN(c->qid);
6299a747e4fSDavid du Colombier 		cp = findcsn(csn, 0, 1);
6309a747e4fSDavid du Colombier 		if(cp == nil)
6319a747e4fSDavid du Colombier 			error(Egreg);
6329a747e4fSDavid du Colombier 		if(!wrconfig(cp, buf))
6339a747e4fSDavid du Colombier 			error(Ebadctl);
6349a747e4fSDavid du Colombier 		break;
63551af9883SDavid du Colombier 	case Qpciraw:
63651af9883SDavid du Colombier 		tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
63751af9883SDavid du Colombier 		p = pcimatchtbdf(tbdf);
63851af9883SDavid du Colombier 		if(p == nil)
63951af9883SDavid du Colombier 			error(Egreg);
64051af9883SDavid du Colombier 		if(offset > 256)
64151af9883SDavid du Colombier 			return 0;
64251af9883SDavid du Colombier 		if(n+offset > 256)
64351af9883SDavid du Colombier 			n = 256-offset;
64451af9883SDavid du Colombier 		r = offset;
64551af9883SDavid du Colombier 		if(!(r & 3) && n == 4){
64651af9883SDavid du Colombier 			x = GBIT32(a);
64751af9883SDavid du Colombier 			pcicfgw32(p, r, x);
64851af9883SDavid du Colombier 			return 4;
64951af9883SDavid du Colombier 		}
65051af9883SDavid du Colombier 		if(!(r & 1) && n == 2){
65151af9883SDavid du Colombier 			x = GBIT16(a);
65251af9883SDavid du Colombier 			pcicfgw16(p, r, x);
65351af9883SDavid du Colombier 			return 2;
65451af9883SDavid du Colombier 		}
65551af9883SDavid du Colombier 		for(i = 0; i <  n; i++){
65651af9883SDavid du Colombier 			x = GBIT8(a);
65751af9883SDavid du Colombier 			pcicfgw8(p, r, x);
65851af9883SDavid du Colombier 			a++;
65951af9883SDavid du Colombier 			r++;
66051af9883SDavid du Colombier 		}
66151af9883SDavid du Colombier 		return i;
6629a747e4fSDavid du Colombier 	default:
6639a747e4fSDavid du Colombier 		error(Egreg);
6649a747e4fSDavid du Colombier 	}
6659a747e4fSDavid du Colombier 	return n;
6669a747e4fSDavid du Colombier }
6679a747e4fSDavid du Colombier 
6689a747e4fSDavid du Colombier static int
wrconfig(Card * c,char * cmd)6699a747e4fSDavid du Colombier wrconfig(Card *c, char *cmd)
6709a747e4fSDavid du Colombier {
6719a747e4fSDavid du Colombier 	/* This should implement setting of I/O bases, etc */
6729a747e4fSDavid du Colombier 	USED(c, cmd);
6739a747e4fSDavid du Colombier 	return 1;
6749a747e4fSDavid du Colombier }
6759a747e4fSDavid du Colombier 
6769a747e4fSDavid du Colombier 
6779a747e4fSDavid du Colombier Dev pnpdevtab = {
6789a747e4fSDavid du Colombier 	'$',
6799a747e4fSDavid du Colombier 	"pnp",
6809a747e4fSDavid du Colombier 
6819a747e4fSDavid du Colombier 	pnpreset,
6829a747e4fSDavid du Colombier 	devinit,
6839a747e4fSDavid du Colombier 	devshutdown,
6849a747e4fSDavid du Colombier 	pnpattach,
6859a747e4fSDavid du Colombier 	pnpwalk,
6869a747e4fSDavid du Colombier 	pnpstat,
6879a747e4fSDavid du Colombier 	pnpopen,
6889a747e4fSDavid du Colombier 	devcreate,
6899a747e4fSDavid du Colombier 	pnpclose,
6909a747e4fSDavid du Colombier 	pnpread,
6919a747e4fSDavid du Colombier 	devbread,
6929a747e4fSDavid du Colombier 	pnpwrite,
6939a747e4fSDavid du Colombier 	devbwrite,
6949a747e4fSDavid du Colombier 	devremove,
6959a747e4fSDavid du Colombier 	devwstat,
6969a747e4fSDavid du Colombier };
697