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
cmd(int reg,int val)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
initiation(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
readbit(int rddata)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
isolate(int rddata,ulong * id1,ulong * id2)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
getresbyte(int rddata)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 *
serial(ulong id1,ulong id2)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 *
findcsn(int csn,int create,int dolock)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 if(nc == nil) {
198 if(dolock)
199 qunlock(&pnp);
200 error(Enomem);
201 }
202 nc->next = c;
203 nc->csn = csn;
204 c = nc;
205 }
206 done:
207 if(dolock)
208 qunlock(&pnp);
209 return c;
210 }
211
212 static int
newcsn(void)213 newcsn(void)
214 {
215 int csn;
216 Card *c;
217
218 csn = 1;
219 for(c = pnp.cards; c != nil; c = c->next) {
220 if(c->csn > csn)
221 break;
222 csn = c->csn+1;
223 }
224 return csn;
225 }
226
227 static int
pnpncfg(int rddata)228 pnpncfg(int rddata)
229 {
230 int i, n, x, ncfg, n1, n2;
231
232 ncfg = 0;
233 for (;;) {
234 x = getresbyte(rddata);
235 if((x & 0x80) == 0) {
236 n = (x&7)+1;
237 for(i = 1; i < n; i++)
238 getresbyte(rddata);
239 }
240 else {
241 n1 = getresbyte(rddata);
242 n2 = getresbyte(rddata);
243 n = (n2<<8)|n1 + 3;
244 for (i = 3; i < n; i++)
245 getresbyte(rddata);
246 }
247 ncfg += n;
248 if((x>>3) == 0x0f)
249 break;
250 }
251 return ncfg;
252 }
253
254 /* look for cards, and assign them CSNs */
255 static int
pnpscan(int rddata,int dawn)256 pnpscan(int rddata, int dawn)
257 {
258 Card *c;
259 int csn;
260 ulong id1, id2;
261
262 initiation(); /* upsilon sigma */
263 cmd(0x02, 0x04+0x01); /* reset CSN on all cards and reset logical devices */
264 delay(1); /* delay after resetting cards */
265
266 cmd(0x03, 0); /* Wake all cards with a CSN of 0 */
267 cmd(0x00, rddata>>2); /* Set the READ_DATA port on all cards */
268 while(isolate(rddata, &id1, &id2)) {
269 for(c = pnp.cards; c != nil; c = c->next)
270 if(c->id1 == id1 && c->id2 == id2)
271 break;
272 if(c == nil) {
273 csn = newcsn();
274 c = findcsn(csn, 1, 0);
275 c->id1 = id1;
276 c->id2 = id2;
277 }
278 else if(c->cfgstr != nil) {
279 if(!wrconfig(c, c->cfgstr))
280 print("pnp%d: bad cfg: %s\n", c->csn, c->cfgstr);
281 c->cfgstr = nil;
282 }
283 cmd(0x06, c->csn); /* set the card's csn */
284 if(dawn)
285 print("pnp%d: %s\n", c->csn, serial(id1, id2));
286 c->ncfg = pnpncfg(rddata);
287 cmd(0x03, 0); /* Wake all cards with a CSN of 0, putting this card to sleep */
288 }
289 cmd(0x02, 0x02); /* return cards to Wait for Key state */
290 if(pnp.cards != 0) {
291 pnp.rddata = rddata;
292 return 1;
293 }
294 return 0;
295 }
296
297 static void
pnpreset(void)298 pnpreset(void)
299 {
300 Card *c;
301 ulong id1, id2;
302 int csn, i1, i2, i3, x;
303 char *s, *p, buf[20];
304 ISAConf isa;
305
306 memset(&isa, 0, sizeof(ISAConf));
307 pnp.rddata = -1;
308 if (isaconfig("pnp", 0, &isa) == 0)
309 return;
310 if(isa.port < 0x203 || isa.port > 0x3ff)
311 return;
312 for(csn = 1; csn < 256; csn++) {
313 snprint(buf, sizeof buf, "pnp%d", csn);
314 s = getconf(buf);
315 if(s == 0)
316 continue;
317 if(strlen(s) < 8 || s[7] != '.' || s[0] < 'A' || s[0] > 'Z' || s[1] < 'A' || s[1] > 'Z' || s[2] < 'A' || s[2] > 'Z') {
318 bad:
319 print("pnp%d: bad conf string %s\n", csn, s);
320 continue;
321 }
322 i1 = s[0]-'A'+1;
323 i2 = s[1]-'A'+1;
324 i3 = s[2]-'A'+1;
325 x = strtoul(&s[3], 0, 16);
326 id1 = (i1<<2)|((i2>>3)&3)|((i2&7)<<13)|(i3<<8)|((x&0xff)<<24)|((x&0xff00)<<8);
327 id2 = strtoul(&s[8], &p, 16);
328 if(*p == ' ')
329 p++;
330 else if(*p == '\0')
331 p = nil;
332 else
333 goto bad;
334 c = findcsn(csn, 1, 0);
335 c->id1 = id1;
336 c->id2 = id2;
337 c->cfgstr = p;
338 }
339 pnpscan(isa.port, 1);
340 }
341
342 static int
csngen(Chan * c,int t,int csn,Card * cp,Dir * dp)343 csngen(Chan *c, int t, int csn, Card *cp, Dir *dp)
344 {
345 Qid q;
346
347 switch(t) {
348 case Qcsnctl:
349 q = (Qid){QID(csn, Qcsnctl), 0, 0};
350 snprint(up->genbuf, sizeof up->genbuf, "csn%dctl", csn);
351 devdir(c, q, up->genbuf, 0, eve, 0664, dp);
352 return 1;
353 case Qcsnraw:
354 q = (Qid){QID(csn, Qcsnraw), 0, 0};
355 snprint(up->genbuf, sizeof up->genbuf, "csn%draw", csn);
356 devdir(c, q, up->genbuf, cp->ncfg, eve, 0444, dp);
357 return 1;
358 }
359 return -1;
360 }
361
362 static int
pcigen(Chan * c,int t,int tbdf,Dir * dp)363 pcigen(Chan *c, int t, int tbdf, Dir *dp)
364 {
365 Qid q;
366
367 q = (Qid){BUSBDF(tbdf)|t, 0, 0};
368 switch(t) {
369 case Qpcictl:
370 snprint(up->genbuf, sizeof up->genbuf, "%d.%d.%dctl",
371 BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
372 devdir(c, q, up->genbuf, 0, eve, 0444, dp);
373 return 1;
374 case Qpciraw:
375 snprint(up->genbuf, sizeof up->genbuf, "%d.%d.%draw",
376 BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
377 devdir(c, q, up->genbuf, 128, eve, 0660, dp);
378 return 1;
379 }
380 return -1;
381 }
382
383 static int
pnpgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)384 pnpgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
385 {
386 Qid q;
387 Card *cp;
388 Pcidev *p;
389 int csn, tbdf;
390
391 switch(TYPE(c->qid)){
392 case Qtopdir:
393 if(s == DEVDOTDOT){
394 q = (Qid){QID(0, Qtopdir), 0, QTDIR};
395 snprint(up->genbuf, sizeof up->genbuf, "#%C", pnpdevtab.dc);
396 devdir(c, q, up->genbuf, 0, eve, 0555, dp);
397 return 1;
398 }
399 return devgen(c, nil, topdir, nelem(topdir), s, dp);
400 case Qpnpdir:
401 if(s == DEVDOTDOT){
402 q = (Qid){QID(0, Qtopdir), 0, QTDIR};
403 snprint(up->genbuf, sizeof up->genbuf, "#%C", pnpdevtab.dc);
404 devdir(c, q, up->genbuf, 0, eve, 0555, dp);
405 return 1;
406 }
407 if(s < nelem(pnpdir)-1)
408 return devgen(c, nil, pnpdir, nelem(pnpdir), s, dp);
409 s -= nelem(pnpdir)-1;
410 qlock(&pnp);
411 cp = pnp.cards;
412 while(s >= 2 && cp != nil) {
413 s -= 2;
414 cp = cp->next;
415 }
416 qunlock(&pnp);
417 if(cp == nil)
418 return -1;
419 return csngen(c, s+Qcsnctl, cp->csn, cp, dp);
420 case Qpnpctl:
421 return devgen(c, nil, pnpdir, nelem(pnpdir), s, dp);
422 case Qcsnctl:
423 case Qcsnraw:
424 csn = CSN(c->qid);
425 cp = findcsn(csn, 0, 1);
426 if(cp == nil)
427 return -1;
428 return csngen(c, TYPE(c->qid), csn, cp, dp);
429 case Qpcidir:
430 if(s == DEVDOTDOT){
431 q = (Qid){QID(0, Qtopdir), 0, QTDIR};
432 snprint(up->genbuf, sizeof up->genbuf, "#%C", pnpdevtab.dc);
433 devdir(c, q, up->genbuf, 0, eve, 0555, dp);
434 return 1;
435 }
436 p = pcimatch(nil, 0, 0);
437 while(s >= 2 && p != nil) {
438 p = pcimatch(p, 0, 0);
439 s -= 2;
440 }
441 if(p == nil)
442 return -1;
443 return pcigen(c, s+Qpcictl, p->tbdf, dp);
444 case Qpcictl:
445 case Qpciraw:
446 tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
447 p = pcimatchtbdf(tbdf);
448 if(p == nil)
449 return -1;
450 return pcigen(c, TYPE(c->qid), tbdf, dp);
451 default:
452 break;
453 }
454 return -1;
455 }
456
457 static Chan*
pnpattach(char * spec)458 pnpattach(char *spec)
459 {
460 return devattach(pnpdevtab.dc, spec);
461 }
462
463 Walkqid*
pnpwalk(Chan * c,Chan * nc,char ** name,int nname)464 pnpwalk(Chan* c, Chan *nc, char** name, int nname)
465 {
466 return devwalk(c, nc, name, nname, (Dirtab *)0, 0, pnpgen);
467 }
468
469 static int
pnpstat(Chan * c,uchar * dp,int n)470 pnpstat(Chan* c, uchar* dp, int n)
471 {
472 return devstat(c, dp, n, (Dirtab *)0, 0L, pnpgen);
473 }
474
475 static Chan*
pnpopen(Chan * c,int omode)476 pnpopen(Chan *c, int omode)
477 {
478 c = devopen(c, omode, (Dirtab*)0, 0, pnpgen);
479 switch(TYPE(c->qid)){
480 default:
481 break;
482 }
483 return c;
484 }
485
486 static void
pnpclose(Chan *)487 pnpclose(Chan*)
488 {
489 }
490
491 static long
pnpread(Chan * c,void * va,long n,vlong offset)492 pnpread(Chan *c, void *va, long n, vlong offset)
493 {
494 ulong x;
495 Card *cp;
496 Pcidev *p;
497 char buf[256], *ebuf, *w;
498 char *a = va;
499 int csn, i, tbdf, r;
500
501 switch(TYPE(c->qid)){
502 case Qtopdir:
503 case Qpnpdir:
504 case Qpcidir:
505 return devdirread(c, a, n, (Dirtab *)0, 0L, pnpgen);
506 case Qpnpctl:
507 if(pnp.rddata > 0)
508 snprint(up->genbuf, sizeof up->genbuf, "enabled %#x\n",
509 pnp.rddata);
510 else
511 snprint(up->genbuf, sizeof up->genbuf, "disabled\n");
512 return readstr(offset, a, n, up->genbuf);
513 case Qcsnraw:
514 csn = CSN(c->qid);
515 cp = findcsn(csn, 0, 1);
516 if(cp == nil)
517 error(Egreg);
518 if(offset+n > cp->ncfg)
519 n = cp->ncfg - offset;
520 qlock(&pnp);
521 initiation();
522 cmd(0x03, csn); /* Wake up the card */
523 for(i = 0; i < offset+9; i++) /* 9 == skip serial + csum */
524 getresbyte(pnp.rddata);
525 for(i = 0; i < n; i++)
526 a[i] = getresbyte(pnp.rddata);
527 cmd(0x03, 0); /* Wake all cards with a CSN of 0, putting this card to sleep */
528 cmd(0x02, 0x02); /* return cards to Wait for Key state */
529 qunlock(&pnp);
530 break;
531 case Qcsnctl:
532 csn = CSN(c->qid);
533 cp = findcsn(csn, 0, 1);
534 if(cp == nil)
535 error(Egreg);
536 snprint(up->genbuf, sizeof up->genbuf, "%s\n",
537 serial(cp->id1, cp->id2));
538 return readstr(offset, a, n, up->genbuf);
539 case Qpcictl:
540 tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
541 p = pcimatchtbdf(tbdf);
542 if(p == nil)
543 error(Egreg);
544 ebuf = buf+sizeof buf-1; /* -1 for newline */
545 w = seprint(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d",
546 p->ccrb, p->ccru, p->ccrp, p->vid, p->did, p->intl);
547 for(i=0; i<nelem(p->mem); i++){
548 if(p->mem[i].size == 0)
549 continue;
550 w = seprint(w, ebuf, " %d:%.8lux %d", i, p->mem[i].bar, p->mem[i].size);
551 }
552 *w++ = '\n';
553 *w = '\0';
554 return readstr(offset, a, n, buf);
555 case Qpciraw:
556 tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
557 p = pcimatchtbdf(tbdf);
558 if(p == nil)
559 error(Egreg);
560 if(offset > 256)
561 return 0;
562 if(n+offset > 256)
563 n = 256-offset;
564 r = offset;
565 if(!(r & 3) && n == 4){
566 x = pcicfgr32(p, r);
567 PBIT32(a, x);
568 return 4;
569 }
570 if(!(r & 1) && n == 2){
571 x = pcicfgr16(p, r);
572 PBIT16(a, x);
573 return 2;
574 }
575 for(i = 0; i < n; i++){
576 x = pcicfgr8(p, r);
577 PBIT8(a, x);
578 a++;
579 r++;
580 }
581 return i;
582 default:
583 error(Egreg);
584 }
585 return n;
586 }
587
588 static long
pnpwrite(Chan * c,void * va,long n,vlong offset)589 pnpwrite(Chan *c, void *va, long n, vlong offset)
590 {
591 Card *cp;
592 Pcidev *p;
593 ulong port, x;
594 char buf[256];
595 uchar *a;
596 int csn, i, r, tbdf;
597
598 if(n >= sizeof(buf))
599 n = sizeof(buf)-1;
600 a = va;
601 strncpy(buf, va, n);
602 buf[n] = 0;
603
604 switch(TYPE(c->qid)){
605 case Qpnpctl:
606 if(strncmp(buf, "port ", 5) == 0) {
607 port = strtoul(buf+5, 0, 0);
608 if(port < 0x203 || port > 0x3ff)
609 error("bad value for rddata port");
610 qlock(&pnp);
611 if(waserror()) {
612 qunlock(&pnp);
613 nexterror();
614 }
615 if(pnp.rddata > 0)
616 error("pnp port already set");
617 if(!pnpscan(port, 0))
618 error("no cards found");
619 qunlock(&pnp);
620 poperror();
621 }
622 else if(strncmp(buf, "debug ", 6) == 0)
623 pnp.debug = strtoul(buf+6, 0, 0);
624 else
625 error(Ebadctl);
626 break;
627 case Qcsnctl:
628 csn = CSN(c->qid);
629 cp = findcsn(csn, 0, 1);
630 if(cp == nil)
631 error(Egreg);
632 if(!wrconfig(cp, buf))
633 error(Ebadctl);
634 break;
635 case Qpciraw:
636 tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
637 p = pcimatchtbdf(tbdf);
638 if(p == nil)
639 error(Egreg);
640 if(offset > 256)
641 return 0;
642 if(n+offset > 256)
643 n = 256-offset;
644 r = offset;
645 if(!(r & 3) && n == 4){
646 x = GBIT32(a);
647 pcicfgw32(p, r, x);
648 return 4;
649 }
650 if(!(r & 1) && n == 2){
651 x = GBIT16(a);
652 pcicfgw16(p, r, x);
653 return 2;
654 }
655 for(i = 0; i < n; i++){
656 x = GBIT8(a);
657 pcicfgw8(p, r, x);
658 a++;
659 r++;
660 }
661 return i;
662 default:
663 error(Egreg);
664 }
665 return n;
666 }
667
668 static int
wrconfig(Card * c,char * cmd)669 wrconfig(Card *c, char *cmd)
670 {
671 /* This should implement setting of I/O bases, etc */
672 USED(c, cmd);
673 return 1;
674 }
675
676
677 Dev pnpdevtab = {
678 '$',
679 "pnp",
680
681 pnpreset,
682 devinit,
683 devshutdown,
684 pnpattach,
685 pnpwalk,
686 pnpstat,
687 pnpopen,
688 devcreate,
689 pnpclose,
690 pnpread,
691 devbread,
692 pnpwrite,
693 devbwrite,
694 devremove,
695 devwstat,
696 };
697