xref: /plan9/sys/src/9/port/devpnp.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
1 /*
2  *	ISA PNP 1.0 support + access to PCI configuration space
3  *
4  *	TODO
5  *		- implement PNP card configuration (setting io bases etc)
6  *		- write user program to drive PNP configuration...
7  *		- extend PCI raw access to configuration space (writes, byte/short access?)
8  *		- implement PCI access to memory/io space/BIOS ROM
9  *		- use c->aux instead of performing lookup on each read/write?
10  */
11 #include	"u.h"
12 #include	"../port/lib.h"
13 #include	"mem.h"
14 #include	"dat.h"
15 #include	"fns.h"
16 #include	"io.h"
17 #include	"../port/error.h"
18 
19 typedef struct Pnp Pnp;
20 typedef struct Card Card;
21 
22 struct Pnp
23 {
24 	QLock;
25 	int		rddata;
26 	int		debug;
27 	Card		*cards;
28 };
29 
30 struct Card
31 {
32 	int		csn;
33 	ulong	id1;
34 	ulong	id2;
35 	char		*cfgstr;
36 	int		ncfg;
37 	Card*	next;
38 };
39 
40 static Pnp	pnp;
41 
42 #define	DPRINT	if(pnp.debug) print
43 #define	XPRINT	if(1) print
44 
45 enum {
46 	Address = 0x279,
47 	WriteData = 0xa79,
48 
49 	Qtopdir = 0,
50 
51 	Qpnpdir,
52 	Qpnpctl,
53 	Qcsnctl,
54 	Qcsnraw,
55 
56 	Qpcidir,
57 	Qpcictl,
58 	Qpciraw,
59 };
60 
61 #define TYPE(q)		((ulong)(q).path & 0x0F)
62 #define CSN(q)		(((ulong)(q).path>>4) & 0xFF)
63 #define QID(c, t)	(((c)<<4)|(t))
64 
65 static Dirtab topdir[] = {
66 	".",	{ Qtopdir, 0, QTDIR },	0,	0555,
67 	"pnp",	{ Qpnpdir, 0, QTDIR },	0,	0555,
68 	"pci",	{ Qpcidir, 0, QTDIR },	0,	0555,
69 };
70 
71 static Dirtab pnpdir[] = {
72 	".",	{ Qpnpdir, 0, QTDIR },	0,	0555,
73 	"ctl",	{ Qpnpctl, 0, 0 },	0,	0666,
74 };
75 
76 extern Dev pnpdevtab;
77 static int wrconfig(Card*, char*);
78 
79 static char key[32] =
80 {
81 	0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB, 0x7D, 0xBE,
82 	0xDF, 0x6F, 0x37, 0x1B, 0x0D, 0x86, 0xC3, 0x61,
83 	0xB0, 0x58, 0x2C, 0x16, 0x8B, 0x45, 0xA2, 0xD1,
84 	0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x39,
85 };
86 
87 static void
88 cmd(int reg, int val)
89 {
90 	outb(Address, reg);
91 	outb(WriteData, val);
92 }
93 
94 /* Send initiation key, putting each card in Sleep state */
95 static void
96 initiation(void)
97 {
98 	int i;
99 
100 	/* ensure each card's LFSR is reset */
101 	outb(Address, 0x00);
102 	outb(Address, 0x00);
103 
104 	/* send initiation key */
105 	for (i = 0; i < 32; i++)
106 		outb(Address, key[i]);
107 }
108 
109 /* isolation protocol... */
110 static int
111 readbit(int rddata)
112 {
113 	int r1, r2;
114 
115 	r1 = inb(rddata);
116 	r2 = inb(rddata);
117 	microdelay(250);
118 	return (r1 == 0x55) && (r2 == 0xaa);
119 }
120 
121 static int
122 isolate(int rddata, ulong *id1, ulong *id2)
123 {
124 	int i, csum, bit;
125 	uchar *p, id[9];
126 
127 	outb(Address, 0x01);	/* point to serial isolation register */
128 	delay(1);
129 	csum = 0x6a;
130 	for(i = 0; i < 64; i++){
131 		bit = readbit(rddata);
132 		csum = (csum>>1) | (((csum&1) ^ ((csum>>1)&1) ^ bit)<<7);
133 		p = &id[i>>3];
134 		*p = (*p>>1) | (bit<<7);
135 	}
136 	for(; i < 72; i++){
137 		p = &id[i>>3];
138 		*p = (*p>>1) | (readbit(rddata)<<7);
139 	}
140 	*id1 = (id[3]<<24)|(id[2]<<16)|(id[1]<<8)|id[0];
141 	*id2 = (id[7]<<24)|(id[6]<<16)|(id[5]<<8)|id[4];
142 	if(*id1 == 0)
143 		return 0;
144 	if(id[8] != csum)
145 		DPRINT("pnp: bad checksum id1 %lux id2 %lux csum %x != %x\n", *id1, *id2, csum, id[8]); /**/
146 	return id[8] == csum;
147 }
148 
149 static int
150 getresbyte(int rddata)
151 {
152 	int tries = 0;
153 
154 	outb(Address, 0x05);
155 	while ((inb(rddata) & 1) == 0)
156 		if (tries++ > 1000000)
157 			error("pnp: timeout waiting for resource data\n");
158 	outb(Address, 0x04);
159 	return inb(rddata);
160 }
161 
162 static char *
163 serial(ulong id1, ulong id2)
164 {
165 	int i1, i2, i3;
166 	ulong x;
167 	static char buf[20];
168 
169 	i1 = (id1>>2)&31;
170 	i2 = ((id1<<3)&24)+((id1>>13)&7);
171 	i3 = (id1>>8)&31;
172 	x = (id1>>8)&0xff00|(id1>>24)&0x00ff;
173 	if (i1 > 0 && i1 < 27 && i2 > 0 && i2 < 27 && i3 > 0 && i3 < 27 && (id1 & (1<<7)) == 0)
174 		snprint(buf, sizeof(buf), "%c%c%c%.4lux.%lux", 'A'+i1-1, 'A'+i2-1, 'A'+i3-1, x, id2);
175 	else
176 		snprint(buf, sizeof(buf), "%.4lux%.4lux.%lux", (id1<<8)&0xff00|(id1>>8)&0x00ff, x, id2);
177 	return buf;
178 }
179 
180 static Card *
181 findcsn(int csn, int create, int dolock)
182 {
183 	Card *c, *nc, **l;
184 
185 	if(dolock)
186 		qlock(&pnp);
187 	l = &pnp.cards;
188 	for(c = *l; c != nil; c = *l) {
189 		if(c->csn == csn)
190 			goto done;
191 		if(c->csn > csn)
192 			break;
193 		l = &c->next;
194 	}
195 	if(create) {
196 		*l = nc = malloc(sizeof(Card));
197 		nc->next = c;
198 		nc->csn = csn;
199 		c = nc;
200 	}
201 done:
202 	if(dolock)
203 		qunlock(&pnp);
204 	return c;
205 }
206 
207 static int
208 newcsn(void)
209 {
210 	int csn;
211 	Card *c;
212 
213 	csn = 1;
214 	for(c = pnp.cards; c != nil; c = c->next) {
215 		if(c->csn > csn)
216 			break;
217 		csn = c->csn+1;
218 	}
219 	return csn;
220 }
221 
222 static int
223 pnpncfg(int rddata)
224 {
225 	int i, n, x, ncfg, n1, n2;
226 
227 	ncfg = 0;
228 	for (;;) {
229 		x = getresbyte(rddata);
230 		if((x & 0x80) == 0) {
231 			n = (x&7)+1;
232 			for(i = 1; i < n; i++)
233 				getresbyte(rddata);
234 		}
235 		else {
236 			n1 = getresbyte(rddata);
237 			n2 = getresbyte(rddata);
238 			n = (n2<<8)|n1 + 3;
239 			for (i = 3; i < n; i++)
240 				getresbyte(rddata);
241 		}
242 		ncfg += n;
243 		if((x>>3) == 0x0f)
244 			break;
245 	}
246 	return ncfg;
247 }
248 
249 /* look for cards, and assign them CSNs */
250 static int
251 pnpscan(int rddata, int dawn)
252 {
253 	Card *c;
254 	int csn;
255 	ulong id1, id2;
256 
257 	initiation();				/* upsilon sigma */
258 	cmd(0x02, 0x04+0x01);		/* reset CSN on all cards and reset logical devices */
259 	delay(1);					/* delay after resetting cards */
260 
261 	cmd(0x03, 0);				/* Wake all cards with a CSN of 0 */
262 	cmd(0x00, rddata>>2);		/* Set the READ_DATA port on all cards */
263 	while(isolate(rddata, &id1, &id2)) {
264 		for(c = pnp.cards; c != nil; c = c->next)
265 			if(c->id1 == id1 && c->id2 == id2)
266 				break;
267 		if(c == nil) {
268 			csn = newcsn();
269 			c = findcsn(csn, 1, 0);
270 			c->id1 = id1;
271 			c->id2 = id2;
272 		}
273 		else if(c->cfgstr != nil) {
274 			if(!wrconfig(c, c->cfgstr))
275 				print("pnp%d: bad cfg: %s\n", c->csn, c->cfgstr);
276 			c->cfgstr = nil;
277 		}
278 		cmd(0x06, c->csn);		/* set the card's csn */
279 		if(dawn)
280 			print("pnp%d: %s\n", c->csn, serial(id1, id2));
281 		c->ncfg = pnpncfg(rddata);
282 		cmd(0x03, 0);		/* Wake all cards with a CSN of 0, putting this card to sleep */
283 	}
284 	cmd(0x02, 0x02);			/* return cards to Wait for Key state */
285 	if(pnp.cards != 0) {
286 		pnp.rddata = rddata;
287 		return 1;
288 	}
289 	return 0;
290 }
291 
292 static void
293 pnpreset(void)
294 {
295 	Card *c;
296 	ulong id1, id2;
297 	int csn, i1, i2, i3, x;
298 	char *s, *p, buf[20];
299 	ISAConf isa;
300 
301 	memset(&isa, 0, sizeof(ISAConf));
302 	pnp.rddata = -1;
303 	if (isaconfig("pnp", 0, &isa) == 0)
304 		return;
305 	if(isa.port < 0x203 || isa.port > 0x3ff)
306 		return;
307 	for(csn = 1; csn < 256; csn++) {
308 		sprint(buf, "pnp%d", csn);
309 		s = getconf(buf);
310 		if(s == 0)
311 			continue;
312 		if(strlen(s) < 8 || s[7] != '.' || s[0] < 'A' || s[0] > 'Z' || s[1] < 'A' || s[1] > 'Z' || s[2] < 'A' || s[2] > 'Z') {
313 bad:
314 			print("pnp%d: bad conf string %s\n", csn, s);
315 			continue;
316 		}
317 		i1 = s[0]-'A'+1;
318 		i2 = s[1]-'A'+1;
319 		i3 = s[2]-'A'+1;
320 		x = strtoul(&s[3], 0, 16);
321 		id1 = (i1<<2)|((i2>>3)&3)|((i2&7)<<13)|(i3<<8)|((x&0xff)<<24)|((x&0xff00)<<8);
322 		id2 = strtoul(&s[8], &p, 16);
323 		if(*p == ' ')
324 			p++;
325 		else if(*p == '\0')
326 			p = nil;
327 		else
328 			goto bad;
329 		c = findcsn(csn, 1, 0);
330 		c->id1 = id1;
331 		c->id2 = id2;
332 		c->cfgstr = p;
333 	}
334 	pnpscan(isa.port, 1);
335 }
336 
337 static int
338 csngen(Chan *c, int t, int csn, Card *cp, Dir *dp)
339 {
340 	Qid q;
341 
342 	switch(t) {
343 	case Qcsnctl:
344 		q = (Qid){QID(csn, Qcsnctl), 0, 0};
345 		sprint(up->genbuf, "csn%dctl", csn);
346 		devdir(c, q, up->genbuf, 0, eve, 0664, dp);
347 		return 1;
348 	case Qcsnraw:
349 		q = (Qid){QID(csn, Qcsnraw), 0, 0};
350 		sprint(up->genbuf, "csn%draw", csn);
351 		devdir(c, q, up->genbuf, cp->ncfg, eve, 0444, dp);
352 		return 1;
353 	}
354 	return -1;
355 }
356 
357 static int
358 pcigen(Chan *c, int t, int tbdf, Dir *dp)
359 {
360 	Qid q;
361 
362 	q = (Qid){BUSBDF(tbdf)|t, 0, 0};
363 	switch(t) {
364 	case Qpcictl:
365 		sprint(up->genbuf, "%d.%d.%dctl", BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
366 		devdir(c, q, up->genbuf, 0, eve, 0444, dp);
367 		return 1;
368 	case Qpciraw:
369 		sprint(up->genbuf, "%d.%d.%draw", BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
370 		devdir(c, q, up->genbuf, 128, eve, 0660, dp);
371 		return 1;
372 	}
373 	return -1;
374 }
375 
376 static int
377 pnpgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
378 {
379 	Qid q;
380 	Card *cp;
381 	Pcidev *p;
382 	int csn, tbdf;
383 
384 	switch(TYPE(c->qid)){
385 	case Qtopdir:
386 		if(s == DEVDOTDOT){
387 			q = (Qid){QID(0, Qtopdir), 0, QTDIR};
388 			sprint(up->genbuf, "#%C", pnpdevtab.dc);
389 			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
390 			return 1;
391 		}
392 		return devgen(c, nil, topdir, nelem(topdir), s, dp);
393 	case Qpnpdir:
394 		if(s == DEVDOTDOT){
395 			q = (Qid){QID(0, Qtopdir), 0, QTDIR};
396 			sprint(up->genbuf, "#%C", pnpdevtab.dc);
397 			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
398 			return 1;
399 		}
400 		if(s < nelem(pnpdir)-1)
401 			return devgen(c, nil, pnpdir, nelem(pnpdir), s, dp);
402 		s -= nelem(pnpdir)-1;
403 		qlock(&pnp);
404 		cp = pnp.cards;
405 		while(s >= 2 && cp != nil) {
406 			s -= 2;
407 			cp = cp->next;
408 		}
409 		qunlock(&pnp);
410 		if(cp == nil)
411 			return -1;
412 		return csngen(c, s+Qcsnctl, cp->csn, cp, dp);
413 	case Qpnpctl:
414 		return devgen(c, nil, pnpdir, nelem(pnpdir), s, dp);
415 	case Qcsnctl:
416 	case Qcsnraw:
417 		csn = CSN(c->qid);
418 		cp = findcsn(csn, 0, 1);
419 		if(cp == nil)
420 			return -1;
421 		return csngen(c, TYPE(c->qid), csn, cp, dp);
422 	case Qpcidir:
423 		if(s == DEVDOTDOT){
424 			q = (Qid){QID(0, Qtopdir), 0, QTDIR};
425 			sprint(up->genbuf, "#%C", pnpdevtab.dc);
426 			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
427 			return 1;
428 		}
429 		p = pcimatch(nil, 0, 0);
430 		while(s >= 2 && p != nil) {
431 			p = pcimatch(p, 0, 0);
432 			s -= 2;
433 		}
434 		if(p == nil)
435 			return -1;
436 		return pcigen(c, s+Qpcictl, p->tbdf, dp);
437 	case Qpcictl:
438 	case Qpciraw:
439 		tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
440 		p = pcimatchtbdf(tbdf);
441 		if(p == nil)
442 			return -1;
443 		return pcigen(c, TYPE(c->qid), tbdf, dp);
444 	default:
445 		break;
446 	}
447 	return -1;
448 }
449 
450 static Chan*
451 pnpattach(char *spec)
452 {
453 	return devattach(pnpdevtab.dc, spec);
454 }
455 
456 Walkqid*
457 pnpwalk(Chan* c, Chan *nc, char** name, int nname)
458 {
459 	return devwalk(c, nc, name, nname, (Dirtab *)0, 0, pnpgen);
460 }
461 
462 static int
463 pnpstat(Chan* c, uchar* dp, int n)
464 {
465 	return devstat(c, dp, n, (Dirtab *)0, 0L, pnpgen);
466 }
467 
468 static Chan*
469 pnpopen(Chan *c, int omode)
470 {
471 	c = devopen(c, omode, (Dirtab*)0, 0, pnpgen);
472 	switch(TYPE(c->qid)){
473 	default:
474 		break;
475 	}
476 	return c;
477 }
478 
479 static void
480 pnpclose(Chan*)
481 {
482 }
483 
484 static long
485 pnpread(Chan *c, void *va, long n, vlong offset)
486 {
487 	ulong x;
488 	Card *cp;
489 	Pcidev *p;
490 	char buf[256], *ebuf, *w;
491 	char *a = va;
492 	int csn, i, tbdf, r;
493 
494 	switch(TYPE(c->qid)){
495 	case Qtopdir:
496 	case Qpnpdir:
497 	case Qpcidir:
498 		return devdirread(c, a, n, (Dirtab *)0, 0L, pnpgen);
499 	case Qpnpctl:
500 		if(pnp.rddata > 0)
501 			sprint(up->genbuf, "enabled 0x%x\n", pnp.rddata);
502 		else
503 			sprint(up->genbuf, "disabled\n");
504 		return readstr(offset, a, n, up->genbuf);
505 	case Qcsnraw:
506 		csn = CSN(c->qid);
507 		cp = findcsn(csn, 0, 1);
508 		if(cp == nil)
509 			error(Egreg);
510 		if(offset+n > cp->ncfg)
511 			n = cp->ncfg - offset;
512 		qlock(&pnp);
513 		initiation();
514 		cmd(0x03, csn);				/* Wake up the card */
515 		for(i = 0; i < offset+9; i++)		/* 9 == skip serial + csum */
516 			getresbyte(pnp.rddata);
517 		for(i = 0; i < n; i++)
518 			a[i] = getresbyte(pnp.rddata);
519 		cmd(0x03, 0);					/* Wake all cards with a CSN of 0, putting this card to sleep */
520 		cmd(0x02, 0x02);				/* return cards to Wait for Key state */
521 		qunlock(&pnp);
522 		break;
523 	case Qcsnctl:
524 		csn = CSN(c->qid);
525 		cp = findcsn(csn, 0, 1);
526 		if(cp == nil)
527 			error(Egreg);
528 		sprint(up->genbuf, "%s\n", serial(cp->id1, cp->id2));
529 		return readstr(offset, a, n, up->genbuf);
530 	case Qpcictl:
531 		tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
532 		p = pcimatchtbdf(tbdf);
533 		if(p == nil)
534 			error(Egreg);
535 		ebuf = buf+sizeof buf-1;	/* -1 for newline */
536 		w = seprint(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d",
537 			p->ccrb, p->ccru, p->ccrp, p->vid, p->did, p->intl);
538 		for(i=0; i<nelem(p->mem); i++){
539 			if(p->mem[i].size == 0)
540 				continue;
541 			w = seprint(w, ebuf, " %d:%.8lux %d", i, p->mem[i].bar, p->mem[i].size);
542 		}
543 		*w++ = '\n';
544 		*w = '\0';
545 		return readstr(offset, a, n, buf);
546 	case Qpciraw:
547 		tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
548 		p = pcimatchtbdf(tbdf);
549 		if(p == nil)
550 			error(Egreg);
551 		if(offset > 256)
552 			return 0;
553 		if(n+offset > 256)
554 			n = 256-offset;
555 		r = offset;
556 		if(!(r & 3) && n == 4){
557 			x = pcicfgr32(p, r);
558 			PBIT32(a, x);
559 			return 4;
560 		}
561 		if(!(r & 1) && n == 2){
562 			x = pcicfgr16(p, r);
563 			PBIT16(a, x);
564 			return 2;
565 		}
566 		for(i = 0; i <  n; i++){
567 			x = pcicfgr8(p, r);
568 			PBIT8(a, x);
569 			a++;
570 			r++;
571 		}
572 		return i;
573 	default:
574 		error(Egreg);
575 	}
576 	return n;
577 }
578 
579 static long
580 pnpwrite(Chan *c, void *va, long n, vlong offset)
581 {
582 	Card *cp;
583 	Pcidev *p;
584 	ulong port, x;
585 	char *a, buf[256];
586 	int csn, i, r, tbdf;
587 
588 	if(n >= sizeof(buf))
589 		n = sizeof(buf)-1;
590 	a = va;
591 	strncpy(buf, a, n);
592 	buf[n] = 0;
593 
594 	switch(TYPE(c->qid)){
595 	case Qpnpctl:
596 		if(strncmp(buf, "port ", 5) == 0) {
597 			port = strtoul(buf+5, 0, 0);
598 			if(port < 0x203 || port > 0x3ff)
599 				error("bad value for rddata port");
600 			qlock(&pnp);
601 			if(waserror()) {
602 				qunlock(&pnp);
603 				nexterror();
604 			}
605 			if(pnp.rddata > 0)
606 				error("pnp port already set");
607 			if(!pnpscan(port, 0))
608 				error("no cards found");
609 			qunlock(&pnp);
610 			poperror();
611 		}
612 		else if(strncmp(buf, "debug ", 6) == 0)
613 			pnp.debug = strtoul(buf+6, 0, 0);
614 		else
615 			error(Ebadctl);
616 		break;
617 	case Qcsnctl:
618 		csn = CSN(c->qid);
619 		cp = findcsn(csn, 0, 1);
620 		if(cp == nil)
621 			error(Egreg);
622 		if(!wrconfig(cp, buf))
623 			error(Ebadctl);
624 		break;
625 	case Qpciraw:
626 		tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
627 		p = pcimatchtbdf(tbdf);
628 		if(p == nil)
629 			error(Egreg);
630 		if(offset > 256)
631 			return 0;
632 		if(n+offset > 256)
633 			n = 256-offset;
634 		r = offset;
635 		if(!(r & 3) && n == 4){
636 			x = GBIT32(a);
637 			pcicfgw32(p, r, x);
638 			return 4;
639 		}
640 		if(!(r & 1) && n == 2){
641 			x = GBIT16(a);
642 			pcicfgw16(p, r, x);
643 			return 2;
644 		}
645 		for(i = 0; i <  n; i++){
646 			x = GBIT8(a);
647 			pcicfgw8(p, r, x);
648 			a++;
649 			r++;
650 		}
651 		return i;
652 	default:
653 		error(Egreg);
654 	}
655 	return n;
656 }
657 
658 static int
659 wrconfig(Card *c, char *cmd)
660 {
661 	/* This should implement setting of I/O bases, etc */
662 	USED(c, cmd);
663 	return 1;
664 }
665 
666 
667 Dev pnpdevtab = {
668 	'$',
669 	"pnp",
670 
671 	pnpreset,
672 	devinit,
673 	devshutdown,
674 	pnpattach,
675 	pnpwalk,
676 	pnpstat,
677 	pnpopen,
678 	devcreate,
679 	pnpclose,
680 	pnpread,
681 	devbread,
682 	pnpwrite,
683 	devbwrite,
684 	devremove,
685 	devwstat,
686 };
687