FreeBSD/Linux Kernel Cross Reference
sys/port/devpnp.c
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 %#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 };
Cache object: c391207021d9ec35a42e1b7930825b22
|