xref: /inferno-os/os/pc/devpnp.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
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, 0444, 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 		if(offset%4)
556 			error(Ebadarg);
557 		r = offset;
558 		for(i = 0; i+4 <= n; i+=4) {
559 			x = pcicfgr32(p, r);
560 			a[0] = x;
561 			a[1] = (x>>8);
562 			a[2] = (x>>16);
563 			a[3] = (x>>24);
564 			a += 4;
565 			r += 4;
566 		}
567 		return i;
568 	default:
569 		error(Egreg);
570 	}
571 	return n;
572 }
573 
574 static long
575 pnpwrite(Chan *c, void *a, long n, vlong)
576 {
577 	int csn;
578 	Card *cp;
579 	ulong port;
580 	char buf[256];
581 
582 	if(n >= sizeof(buf))
583 		n = sizeof(buf)-1;
584 	strncpy(buf, a, n);
585 	buf[n] = 0;
586 
587 	switch(TYPE(c->qid)){
588 	case Qpnpctl:
589 		if(strncmp(buf, "port ", 5) == 0) {
590 			port = strtoul(buf+5, 0, 0);
591 			if(port < 0x203 || port > 0x3ff)
592 				error("bad value for rddata port");
593 			qlock(&pnp);
594 			if(waserror()) {
595 				qunlock(&pnp);
596 				nexterror();
597 			}
598 			if(pnp.rddata > 0)
599 				error("pnp port already set");
600 			if(!pnpscan(port, 0))
601 				error("no cards found");
602 			qunlock(&pnp);
603 			poperror();
604 		}
605 		else if(strncmp(buf, "debug ", 6) == 0)
606 			pnp.debug = strtoul(buf+6, 0, 0);
607 		else
608 			error(Ebadctl);
609 		break;
610 	case Qcsnctl:
611 		csn = CSN(c->qid);
612 		cp = findcsn(csn, 0, 1);
613 		if(cp == nil)
614 			error(Egreg);
615 		if(!wrconfig(cp, buf))
616 			error(Ebadctl);
617 		break;
618 	default:
619 		error(Egreg);
620 	}
621 	return n;
622 }
623 
624 static int
625 wrconfig(Card *c, char *cmd)
626 {
627 	/* This should implement setting of I/O bases, etc */
628 	USED(c, cmd);
629 	return 1;
630 }
631 
632 
633 Dev pnpdevtab = {
634 	'$',
635 	"pnp",
636 
637 	pnpreset,
638 	devinit,
639 	devshutdown,
640 	pnpattach,
641 	pnpwalk,
642 	pnpstat,
643 	pnpopen,
644 	devcreate,
645 	pnpclose,
646 	pnpread,
647 	devbread,
648 	pnpwrite,
649 	devbwrite,
650 	devremove,
651 	devwstat,
652 };
653