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