]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devpnp.c
devshr: fixed crash
[plan9front.git] / sys / src / 9 / 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                 snprint(buf, sizeof 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                 snprint(up->genbuf, sizeof 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                 snprint(up->genbuf, sizeof 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                 snprint(up->genbuf, sizeof up->genbuf, "%d.%d.%dctl",
366                         BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
367                 devdir(c, q, up->genbuf, 0, eve, 0444, dp);
368                 return 1;
369         case Qpciraw:
370                 snprint(up->genbuf, sizeof up->genbuf, "%d.%d.%draw",
371                         BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
372                 devdir(c, q, up->genbuf, 128, eve, 0660, dp);
373                 return 1;
374         }
375         return -1;
376 }
377
378 static int
379 pnpgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
380 {
381         Qid q;
382         Card *cp;
383         Pcidev *p;
384         int csn, tbdf;
385
386         switch(TYPE(c->qid)){
387         case Qtopdir:
388                 if(s == DEVDOTDOT){
389                         q = (Qid){QID(0, Qtopdir), 0, QTDIR};
390                         snprint(up->genbuf, sizeof up->genbuf, "#%C", pnpdevtab.dc);
391                         devdir(c, q, up->genbuf, 0, eve, 0555, dp);
392                         return 1;
393                 }
394                 return devgen(c, nil, topdir, nelem(topdir), s, dp);
395         case Qpnpdir:
396                 if(s == DEVDOTDOT){
397                         q = (Qid){QID(0, Qtopdir), 0, QTDIR};
398                         snprint(up->genbuf, sizeof up->genbuf, "#%C", pnpdevtab.dc);
399                         devdir(c, q, up->genbuf, 0, eve, 0555, dp);
400                         return 1;
401                 }
402                 if(s < nelem(pnpdir)-1)
403                         return devgen(c, nil, pnpdir, nelem(pnpdir), s, dp);
404                 s -= nelem(pnpdir)-1;
405                 qlock(&pnp);
406                 cp = pnp.cards;
407                 while(s >= 2 && cp != nil) {
408                         s -= 2;
409                         cp = cp->next;
410                 }
411                 qunlock(&pnp);
412                 if(cp == nil)
413                         return -1;
414                 return csngen(c, s+Qcsnctl, cp->csn, cp, dp);
415         case Qpnpctl:
416                 return devgen(c, nil, pnpdir, nelem(pnpdir), s, dp);
417         case Qcsnctl:
418         case Qcsnraw:
419                 csn = CSN(c->qid);
420                 cp = findcsn(csn, 0, 1);
421                 if(cp == nil)
422                         return -1;
423                 return csngen(c, TYPE(c->qid), csn, cp, dp);
424         case Qpcidir:
425                 if(s == DEVDOTDOT){
426                         q = (Qid){QID(0, Qtopdir), 0, QTDIR};
427                         snprint(up->genbuf, sizeof up->genbuf, "#%C", pnpdevtab.dc);
428                         devdir(c, q, up->genbuf, 0, eve, 0555, dp);
429                         return 1;
430                 }
431                 p = pcimatch(nil, 0, 0);
432                 while(s >= 2 && p != nil) {
433                         p = pcimatch(p, 0, 0);
434                         s -= 2;
435                 }
436                 if(p == nil)
437                         return -1;
438                 return pcigen(c, s+Qpcictl, p->tbdf, dp);
439         case Qpcictl:
440         case Qpciraw:
441                 tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
442                 p = pcimatchtbdf(tbdf);
443                 if(p == nil)
444                         return -1;
445                 return pcigen(c, TYPE(c->qid), tbdf, dp);
446         default:
447                 break;
448         }
449         return -1;
450 }
451
452 static Chan*
453 pnpattach(char *spec)
454 {
455         return devattach(pnpdevtab.dc, spec);
456 }
457
458 Walkqid*
459 pnpwalk(Chan* c, Chan *nc, char** name, int nname)
460 {
461         return devwalk(c, nc, name, nname, (Dirtab *)0, 0, pnpgen);
462 }
463
464 static int
465 pnpstat(Chan* c, uchar* dp, int n)
466 {
467         return devstat(c, dp, n, (Dirtab *)0, 0L, pnpgen);
468 }
469
470 static Chan*
471 pnpopen(Chan *c, int omode)
472 {
473         c = devopen(c, omode, (Dirtab*)0, 0, pnpgen);
474         switch(TYPE(c->qid)){
475         default:
476                 break;
477         }
478         return c;
479 }
480
481 static void
482 pnpclose(Chan*)
483 {
484 }
485
486 static long
487 pnpread(Chan *c, void *va, long n, vlong offset)
488 {
489         ulong x;
490         Card *cp;
491         Pcidev *p;
492         char buf[256], *ebuf, *w;
493         char *a = va;
494         int csn, i, tbdf, r;
495
496         switch(TYPE(c->qid)){
497         case Qtopdir:
498         case Qpnpdir:
499         case Qpcidir:
500                 return devdirread(c, a, n, (Dirtab *)0, 0L, pnpgen);
501         case Qpnpctl:
502                 if(pnp.rddata > 0)
503                         snprint(up->genbuf, sizeof up->genbuf, "enabled %#x\n",
504                                 pnp.rddata);
505                 else
506                         snprint(up->genbuf, sizeof up->genbuf, "disabled\n");
507                 return readstr(offset, a, n, up->genbuf);
508         case Qcsnraw:
509                 csn = CSN(c->qid);
510                 cp = findcsn(csn, 0, 1);
511                 if(cp == nil)
512                         error(Egreg);
513                 if(offset+n > cp->ncfg)
514                         n = cp->ncfg - offset;
515                 qlock(&pnp);
516                 initiation();
517                 cmd(0x03, csn);                         /* Wake up the card */
518                 for(i = 0; i < offset+9; i++)           /* 9 == skip serial + csum */
519                         getresbyte(pnp.rddata);
520                 for(i = 0; i < n; i++)
521                         a[i] = getresbyte(pnp.rddata);
522                 cmd(0x03, 0);                                   /* Wake all cards with a CSN of 0, putting this card to sleep */
523                 cmd(0x02, 0x02);                                /* return cards to Wait for Key state */
524                 qunlock(&pnp);
525                 break;
526         case Qcsnctl:
527                 csn = CSN(c->qid);
528                 cp = findcsn(csn, 0, 1);
529                 if(cp == nil)
530                         error(Egreg);
531                 snprint(up->genbuf, sizeof up->genbuf, "%s\n",
532                         serial(cp->id1, cp->id2));
533                 return readstr(offset, a, n, up->genbuf);
534         case Qpcictl:
535                 tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
536                 p = pcimatchtbdf(tbdf);
537                 if(p == nil)
538                         error(Egreg);
539                 ebuf = buf+sizeof buf-1;        /* -1 for newline */
540                 w = seprint(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d",
541                         p->ccrb, p->ccru, p->ccrp, p->vid, p->did, p->intl);
542                 for(i=0; i<nelem(p->mem); i++){
543                         if(p->mem[i].size == 0)
544                                 continue;
545                         w = seprint(w, ebuf, " %d:%.8lux %d", i, p->mem[i].bar, p->mem[i].size);
546                 }
547                 *w++ = '\n';
548                 *w = '\0';
549                 return readstr(offset, a, n, buf);
550         case Qpciraw:
551                 tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
552                 p = pcimatchtbdf(tbdf);
553                 if(p == nil)
554                         error(Egreg);
555                 if(offset > 256)
556                         return 0;
557                 if(n+offset > 256)
558                         n = 256-offset;
559                 r = offset;
560                 if(!(r & 3) && n == 4){
561                         x = pcicfgr32(p, r);
562                         PBIT32(a, x);
563                         return 4;
564                 }
565                 if(!(r & 1) && n == 2){
566                         x = pcicfgr16(p, r);
567                         PBIT16(a, x);
568                         return 2;
569                 }
570                 for(i = 0; i <  n; i++){
571                         x = pcicfgr8(p, r);
572                         PBIT8(a, x);
573                         a++;
574                         r++;
575                 }
576                 return i;
577         default:
578                 error(Egreg);
579         }
580         return n;
581 }
582
583 static long
584 pnpwrite(Chan *c, void *va, long n, vlong offset)
585 {
586         Card *cp;
587         Pcidev *p;
588         ulong port, x;
589         char buf[256];
590         uchar *a;
591         int csn, i, r, tbdf;
592
593         if(n >= sizeof(buf))
594                 n = sizeof(buf)-1;
595         a = va;
596         strncpy(buf, va, n);
597         buf[n] = 0;
598
599         switch(TYPE(c->qid)){
600         case Qpnpctl:
601                 if(strncmp(buf, "port ", 5) == 0) {
602                         port = strtoul(buf+5, 0, 0);
603                         if(port < 0x203 || port > 0x3ff)
604                                 error("bad value for rddata port");
605                         qlock(&pnp);
606                         if(waserror()) {
607                                 qunlock(&pnp);
608                                 nexterror();
609                         }
610                         if(pnp.rddata > 0)
611                                 error("pnp port already set");
612                         if(!pnpscan(port, 0))
613                                 error("no cards found");
614                         qunlock(&pnp);
615                         poperror();
616                 }
617                 else if(strncmp(buf, "debug ", 6) == 0)
618                         pnp.debug = strtoul(buf+6, 0, 0);
619                 else
620                         error(Ebadctl);
621                 break;
622         case Qcsnctl:
623                 csn = CSN(c->qid);
624                 cp = findcsn(csn, 0, 1);
625                 if(cp == nil)
626                         error(Egreg);
627                 if(!wrconfig(cp, buf))
628                         error(Ebadctl);
629                 break;
630         case Qpciraw:
631                 tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
632                 p = pcimatchtbdf(tbdf);
633                 if(p == nil)
634                         error(Egreg);
635                 if(offset > 256)
636                         return 0;
637                 if(n+offset > 256)
638                         n = 256-offset;
639                 r = offset;
640                 if(!(r & 3) && n == 4){
641                         x = GBIT32(a);
642                         pcicfgw32(p, r, x);
643                         return 4;
644                 }
645                 if(!(r & 1) && n == 2){
646                         x = GBIT16(a);
647                         pcicfgw16(p, r, x);
648                         return 2;
649                 }
650                 for(i = 0; i <  n; i++){
651                         x = GBIT8(a);
652                         pcicfgw8(p, r, x);
653                         a++;
654                         r++;
655                 }
656                 return i;
657         default:
658                 error(Egreg);
659         }
660         return n;
661 }
662
663 static int
664 wrconfig(Card *c, char *cmd)
665 {
666         /* This should implement setting of I/O bases, etc */
667         USED(c, cmd);
668         return 1;
669 }
670
671
672 Dev pnpdevtab = {
673         '$',
674         "pnp",
675
676         pnpreset,
677         devinit,
678         devshutdown,
679         pnpattach,
680         pnpwalk,
681         pnpstat,
682         pnpopen,
683         devcreate,
684         pnpclose,
685         pnpread,
686         devbread,
687         pnpwrite,
688         devbwrite,
689         devremove,
690         devwstat,
691 };