summaryrefslogtreecommitdiff
path: root/sys/src/9/port/devpnp.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/port/devpnp.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/port/devpnp.c')
-rwxr-xr-xsys/src/9/port/devpnp.c691
1 files changed, 691 insertions, 0 deletions
diff --git a/sys/src/9/port/devpnp.c b/sys/src/9/port/devpnp.c
new file mode 100755
index 000000000..805a21303
--- /dev/null
+++ b/sys/src/9/port/devpnp.c
@@ -0,0 +1,691 @@
+/*
+ * ISA PNP 1.0 support + access to PCI configuration space
+ *
+ * TODO
+ * - implement PNP card configuration (setting io bases etc)
+ * - write user program to drive PNP configuration...
+ * - extend PCI raw access to configuration space (writes, byte/short access?)
+ * - implement PCI access to memory/io space/BIOS ROM
+ * - use c->aux instead of performing lookup on each read/write?
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+typedef struct Pnp Pnp;
+typedef struct Card Card;
+
+struct Pnp
+{
+ QLock;
+ int rddata;
+ int debug;
+ Card *cards;
+};
+
+struct Card
+{
+ int csn;
+ ulong id1;
+ ulong id2;
+ char *cfgstr;
+ int ncfg;
+ Card* next;
+};
+
+static Pnp pnp;
+
+#define DPRINT if(pnp.debug) print
+#define XPRINT if(1) print
+
+enum {
+ Address = 0x279,
+ WriteData = 0xa79,
+
+ Qtopdir = 0,
+
+ Qpnpdir,
+ Qpnpctl,
+ Qcsnctl,
+ Qcsnraw,
+
+ Qpcidir,
+ Qpcictl,
+ Qpciraw,
+};
+
+#define TYPE(q) ((ulong)(q).path & 0x0F)
+#define CSN(q) (((ulong)(q).path>>4) & 0xFF)
+#define QID(c, t) (((c)<<4)|(t))
+
+static Dirtab topdir[] = {
+ ".", { Qtopdir, 0, QTDIR }, 0, 0555,
+ "pnp", { Qpnpdir, 0, QTDIR }, 0, 0555,
+ "pci", { Qpcidir, 0, QTDIR }, 0, 0555,
+};
+
+static Dirtab pnpdir[] = {
+ ".", { Qpnpdir, 0, QTDIR }, 0, 0555,
+ "ctl", { Qpnpctl, 0, 0 }, 0, 0666,
+};
+
+extern Dev pnpdevtab;
+static int wrconfig(Card*, char*);
+
+static char key[32] =
+{
+ 0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB, 0x7D, 0xBE,
+ 0xDF, 0x6F, 0x37, 0x1B, 0x0D, 0x86, 0xC3, 0x61,
+ 0xB0, 0x58, 0x2C, 0x16, 0x8B, 0x45, 0xA2, 0xD1,
+ 0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x39,
+};
+
+static void
+cmd(int reg, int val)
+{
+ outb(Address, reg);
+ outb(WriteData, val);
+}
+
+/* Send initiation key, putting each card in Sleep state */
+static void
+initiation(void)
+{
+ int i;
+
+ /* ensure each card's LFSR is reset */
+ outb(Address, 0x00);
+ outb(Address, 0x00);
+
+ /* send initiation key */
+ for (i = 0; i < 32; i++)
+ outb(Address, key[i]);
+}
+
+/* isolation protocol... */
+static int
+readbit(int rddata)
+{
+ int r1, r2;
+
+ r1 = inb(rddata);
+ r2 = inb(rddata);
+ microdelay(250);
+ return (r1 == 0x55) && (r2 == 0xaa);
+}
+
+static int
+isolate(int rddata, ulong *id1, ulong *id2)
+{
+ int i, csum, bit;
+ uchar *p, id[9];
+
+ outb(Address, 0x01); /* point to serial isolation register */
+ delay(1);
+ csum = 0x6a;
+ for(i = 0; i < 64; i++){
+ bit = readbit(rddata);
+ csum = (csum>>1) | (((csum&1) ^ ((csum>>1)&1) ^ bit)<<7);
+ p = &id[i>>3];
+ *p = (*p>>1) | (bit<<7);
+ }
+ for(; i < 72; i++){
+ p = &id[i>>3];
+ *p = (*p>>1) | (readbit(rddata)<<7);
+ }
+ *id1 = (id[3]<<24)|(id[2]<<16)|(id[1]<<8)|id[0];
+ *id2 = (id[7]<<24)|(id[6]<<16)|(id[5]<<8)|id[4];
+ if(*id1 == 0)
+ return 0;
+ if(id[8] != csum)
+ DPRINT("pnp: bad checksum id1 %lux id2 %lux csum %x != %x\n", *id1, *id2, csum, id[8]); /**/
+ return id[8] == csum;
+}
+
+static int
+getresbyte(int rddata)
+{
+ int tries = 0;
+
+ outb(Address, 0x05);
+ while ((inb(rddata) & 1) == 0)
+ if (tries++ > 1000000)
+ error("pnp: timeout waiting for resource data\n");
+ outb(Address, 0x04);
+ return inb(rddata);
+}
+
+static char *
+serial(ulong id1, ulong id2)
+{
+ int i1, i2, i3;
+ ulong x;
+ static char buf[20];
+
+ i1 = (id1>>2)&31;
+ i2 = ((id1<<3)&24)+((id1>>13)&7);
+ i3 = (id1>>8)&31;
+ x = (id1>>8)&0xff00|(id1>>24)&0x00ff;
+ if (i1 > 0 && i1 < 27 && i2 > 0 && i2 < 27 && i3 > 0 && i3 < 27 && (id1 & (1<<7)) == 0)
+ snprint(buf, sizeof(buf), "%c%c%c%.4lux.%lux", 'A'+i1-1, 'A'+i2-1, 'A'+i3-1, x, id2);
+ else
+ snprint(buf, sizeof(buf), "%.4lux%.4lux.%lux", (id1<<8)&0xff00|(id1>>8)&0x00ff, x, id2);
+ return buf;
+}
+
+static Card *
+findcsn(int csn, int create, int dolock)
+{
+ Card *c, *nc, **l;
+
+ if(dolock)
+ qlock(&pnp);
+ l = &pnp.cards;
+ for(c = *l; c != nil; c = *l) {
+ if(c->csn == csn)
+ goto done;
+ if(c->csn > csn)
+ break;
+ l = &c->next;
+ }
+ if(create) {
+ *l = nc = malloc(sizeof(Card));
+ nc->next = c;
+ nc->csn = csn;
+ c = nc;
+ }
+done:
+ if(dolock)
+ qunlock(&pnp);
+ return c;
+}
+
+static int
+newcsn(void)
+{
+ int csn;
+ Card *c;
+
+ csn = 1;
+ for(c = pnp.cards; c != nil; c = c->next) {
+ if(c->csn > csn)
+ break;
+ csn = c->csn+1;
+ }
+ return csn;
+}
+
+static int
+pnpncfg(int rddata)
+{
+ int i, n, x, ncfg, n1, n2;
+
+ ncfg = 0;
+ for (;;) {
+ x = getresbyte(rddata);
+ if((x & 0x80) == 0) {
+ n = (x&7)+1;
+ for(i = 1; i < n; i++)
+ getresbyte(rddata);
+ }
+ else {
+ n1 = getresbyte(rddata);
+ n2 = getresbyte(rddata);
+ n = (n2<<8)|n1 + 3;
+ for (i = 3; i < n; i++)
+ getresbyte(rddata);
+ }
+ ncfg += n;
+ if((x>>3) == 0x0f)
+ break;
+ }
+ return ncfg;
+}
+
+/* look for cards, and assign them CSNs */
+static int
+pnpscan(int rddata, int dawn)
+{
+ Card *c;
+ int csn;
+ ulong id1, id2;
+
+ initiation(); /* upsilon sigma */
+ cmd(0x02, 0x04+0x01); /* reset CSN on all cards and reset logical devices */
+ delay(1); /* delay after resetting cards */
+
+ cmd(0x03, 0); /* Wake all cards with a CSN of 0 */
+ cmd(0x00, rddata>>2); /* Set the READ_DATA port on all cards */
+ while(isolate(rddata, &id1, &id2)) {
+ for(c = pnp.cards; c != nil; c = c->next)
+ if(c->id1 == id1 && c->id2 == id2)
+ break;
+ if(c == nil) {
+ csn = newcsn();
+ c = findcsn(csn, 1, 0);
+ c->id1 = id1;
+ c->id2 = id2;
+ }
+ else if(c->cfgstr != nil) {
+ if(!wrconfig(c, c->cfgstr))
+ print("pnp%d: bad cfg: %s\n", c->csn, c->cfgstr);
+ c->cfgstr = nil;
+ }
+ cmd(0x06, c->csn); /* set the card's csn */
+ if(dawn)
+ print("pnp%d: %s\n", c->csn, serial(id1, id2));
+ c->ncfg = pnpncfg(rddata);
+ cmd(0x03, 0); /* Wake all cards with a CSN of 0, putting this card to sleep */
+ }
+ cmd(0x02, 0x02); /* return cards to Wait for Key state */
+ if(pnp.cards != 0) {
+ pnp.rddata = rddata;
+ return 1;
+ }
+ return 0;
+}
+
+static void
+pnpreset(void)
+{
+ Card *c;
+ ulong id1, id2;
+ int csn, i1, i2, i3, x;
+ char *s, *p, buf[20];
+ ISAConf isa;
+
+ memset(&isa, 0, sizeof(ISAConf));
+ pnp.rddata = -1;
+ if (isaconfig("pnp", 0, &isa) == 0)
+ return;
+ if(isa.port < 0x203 || isa.port > 0x3ff)
+ return;
+ for(csn = 1; csn < 256; csn++) {
+ snprint(buf, sizeof buf, "pnp%d", csn);
+ s = getconf(buf);
+ if(s == 0)
+ continue;
+ if(strlen(s) < 8 || s[7] != '.' || s[0] < 'A' || s[0] > 'Z' || s[1] < 'A' || s[1] > 'Z' || s[2] < 'A' || s[2] > 'Z') {
+bad:
+ print("pnp%d: bad conf string %s\n", csn, s);
+ continue;
+ }
+ i1 = s[0]-'A'+1;
+ i2 = s[1]-'A'+1;
+ i3 = s[2]-'A'+1;
+ x = strtoul(&s[3], 0, 16);
+ id1 = (i1<<2)|((i2>>3)&3)|((i2&7)<<13)|(i3<<8)|((x&0xff)<<24)|((x&0xff00)<<8);
+ id2 = strtoul(&s[8], &p, 16);
+ if(*p == ' ')
+ p++;
+ else if(*p == '\0')
+ p = nil;
+ else
+ goto bad;
+ c = findcsn(csn, 1, 0);
+ c->id1 = id1;
+ c->id2 = id2;
+ c->cfgstr = p;
+ }
+ pnpscan(isa.port, 1);
+}
+
+static int
+csngen(Chan *c, int t, int csn, Card *cp, Dir *dp)
+{
+ Qid q;
+
+ switch(t) {
+ case Qcsnctl:
+ q = (Qid){QID(csn, Qcsnctl), 0, 0};
+ snprint(up->genbuf, sizeof up->genbuf, "csn%dctl", csn);
+ devdir(c, q, up->genbuf, 0, eve, 0664, dp);
+ return 1;
+ case Qcsnraw:
+ q = (Qid){QID(csn, Qcsnraw), 0, 0};
+ snprint(up->genbuf, sizeof up->genbuf, "csn%draw", csn);
+ devdir(c, q, up->genbuf, cp->ncfg, eve, 0444, dp);
+ return 1;
+ }
+ return -1;
+}
+
+static int
+pcigen(Chan *c, int t, int tbdf, Dir *dp)
+{
+ Qid q;
+
+ q = (Qid){BUSBDF(tbdf)|t, 0, 0};
+ switch(t) {
+ case Qpcictl:
+ snprint(up->genbuf, sizeof up->genbuf, "%d.%d.%dctl",
+ BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
+ devdir(c, q, up->genbuf, 0, eve, 0444, dp);
+ return 1;
+ case Qpciraw:
+ snprint(up->genbuf, sizeof up->genbuf, "%d.%d.%draw",
+ BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
+ devdir(c, q, up->genbuf, 128, eve, 0660, dp);
+ return 1;
+ }
+ return -1;
+}
+
+static int
+pnpgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
+{
+ Qid q;
+ Card *cp;
+ Pcidev *p;
+ int csn, tbdf;
+
+ switch(TYPE(c->qid)){
+ case Qtopdir:
+ if(s == DEVDOTDOT){
+ q = (Qid){QID(0, Qtopdir), 0, QTDIR};
+ snprint(up->genbuf, sizeof up->genbuf, "#%C", pnpdevtab.dc);
+ devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+ return 1;
+ }
+ return devgen(c, nil, topdir, nelem(topdir), s, dp);
+ case Qpnpdir:
+ if(s == DEVDOTDOT){
+ q = (Qid){QID(0, Qtopdir), 0, QTDIR};
+ snprint(up->genbuf, sizeof up->genbuf, "#%C", pnpdevtab.dc);
+ devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+ return 1;
+ }
+ if(s < nelem(pnpdir)-1)
+ return devgen(c, nil, pnpdir, nelem(pnpdir), s, dp);
+ s -= nelem(pnpdir)-1;
+ qlock(&pnp);
+ cp = pnp.cards;
+ while(s >= 2 && cp != nil) {
+ s -= 2;
+ cp = cp->next;
+ }
+ qunlock(&pnp);
+ if(cp == nil)
+ return -1;
+ return csngen(c, s+Qcsnctl, cp->csn, cp, dp);
+ case Qpnpctl:
+ return devgen(c, nil, pnpdir, nelem(pnpdir), s, dp);
+ case Qcsnctl:
+ case Qcsnraw:
+ csn = CSN(c->qid);
+ cp = findcsn(csn, 0, 1);
+ if(cp == nil)
+ return -1;
+ return csngen(c, TYPE(c->qid), csn, cp, dp);
+ case Qpcidir:
+ if(s == DEVDOTDOT){
+ q = (Qid){QID(0, Qtopdir), 0, QTDIR};
+ snprint(up->genbuf, sizeof up->genbuf, "#%C", pnpdevtab.dc);
+ devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+ return 1;
+ }
+ p = pcimatch(nil, 0, 0);
+ while(s >= 2 && p != nil) {
+ p = pcimatch(p, 0, 0);
+ s -= 2;
+ }
+ if(p == nil)
+ return -1;
+ return pcigen(c, s+Qpcictl, p->tbdf, dp);
+ case Qpcictl:
+ case Qpciraw:
+ tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
+ p = pcimatchtbdf(tbdf);
+ if(p == nil)
+ return -1;
+ return pcigen(c, TYPE(c->qid), tbdf, dp);
+ default:
+ break;
+ }
+ return -1;
+}
+
+static Chan*
+pnpattach(char *spec)
+{
+ return devattach(pnpdevtab.dc, spec);
+}
+
+Walkqid*
+pnpwalk(Chan* c, Chan *nc, char** name, int nname)
+{
+ return devwalk(c, nc, name, nname, (Dirtab *)0, 0, pnpgen);
+}
+
+static int
+pnpstat(Chan* c, uchar* dp, int n)
+{
+ return devstat(c, dp, n, (Dirtab *)0, 0L, pnpgen);
+}
+
+static Chan*
+pnpopen(Chan *c, int omode)
+{
+ c = devopen(c, omode, (Dirtab*)0, 0, pnpgen);
+ switch(TYPE(c->qid)){
+ default:
+ break;
+ }
+ return c;
+}
+
+static void
+pnpclose(Chan*)
+{
+}
+
+static long
+pnpread(Chan *c, void *va, long n, vlong offset)
+{
+ ulong x;
+ Card *cp;
+ Pcidev *p;
+ char buf[256], *ebuf, *w;
+ char *a = va;
+ int csn, i, tbdf, r;
+
+ switch(TYPE(c->qid)){
+ case Qtopdir:
+ case Qpnpdir:
+ case Qpcidir:
+ return devdirread(c, a, n, (Dirtab *)0, 0L, pnpgen);
+ case Qpnpctl:
+ if(pnp.rddata > 0)
+ snprint(up->genbuf, sizeof up->genbuf, "enabled %#x\n",
+ pnp.rddata);
+ else
+ snprint(up->genbuf, sizeof up->genbuf, "disabled\n");
+ return readstr(offset, a, n, up->genbuf);
+ case Qcsnraw:
+ csn = CSN(c->qid);
+ cp = findcsn(csn, 0, 1);
+ if(cp == nil)
+ error(Egreg);
+ if(offset+n > cp->ncfg)
+ n = cp->ncfg - offset;
+ qlock(&pnp);
+ initiation();
+ cmd(0x03, csn); /* Wake up the card */
+ for(i = 0; i < offset+9; i++) /* 9 == skip serial + csum */
+ getresbyte(pnp.rddata);
+ for(i = 0; i < n; i++)
+ a[i] = getresbyte(pnp.rddata);
+ cmd(0x03, 0); /* Wake all cards with a CSN of 0, putting this card to sleep */
+ cmd(0x02, 0x02); /* return cards to Wait for Key state */
+ qunlock(&pnp);
+ break;
+ case Qcsnctl:
+ csn = CSN(c->qid);
+ cp = findcsn(csn, 0, 1);
+ if(cp == nil)
+ error(Egreg);
+ snprint(up->genbuf, sizeof up->genbuf, "%s\n",
+ serial(cp->id1, cp->id2));
+ return readstr(offset, a, n, up->genbuf);
+ case Qpcictl:
+ tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
+ p = pcimatchtbdf(tbdf);
+ if(p == nil)
+ error(Egreg);
+ ebuf = buf+sizeof buf-1; /* -1 for newline */
+ w = seprint(buf, ebuf, "%.2x.%.2x.%.2x %.4x/%.4x %3d",
+ p->ccrb, p->ccru, p->ccrp, p->vid, p->did, p->intl);
+ for(i=0; i<nelem(p->mem); i++){
+ if(p->mem[i].size == 0)
+ continue;
+ w = seprint(w, ebuf, " %d:%.8lux %d", i, p->mem[i].bar, p->mem[i].size);
+ }
+ *w++ = '\n';
+ *w = '\0';
+ return readstr(offset, a, n, buf);
+ case Qpciraw:
+ tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
+ p = pcimatchtbdf(tbdf);
+ if(p == nil)
+ error(Egreg);
+ if(offset > 256)
+ return 0;
+ if(n+offset > 256)
+ n = 256-offset;
+ r = offset;
+ if(!(r & 3) && n == 4){
+ x = pcicfgr32(p, r);
+ PBIT32(a, x);
+ return 4;
+ }
+ if(!(r & 1) && n == 2){
+ x = pcicfgr16(p, r);
+ PBIT16(a, x);
+ return 2;
+ }
+ for(i = 0; i < n; i++){
+ x = pcicfgr8(p, r);
+ PBIT8(a, x);
+ a++;
+ r++;
+ }
+ return i;
+ default:
+ error(Egreg);
+ }
+ return n;
+}
+
+static long
+pnpwrite(Chan *c, void *va, long n, vlong offset)
+{
+ Card *cp;
+ Pcidev *p;
+ ulong port, x;
+ char buf[256];
+ uchar *a;
+ int csn, i, r, tbdf;
+
+ if(n >= sizeof(buf))
+ n = sizeof(buf)-1;
+ a = va;
+ strncpy(buf, va, n);
+ buf[n] = 0;
+
+ switch(TYPE(c->qid)){
+ case Qpnpctl:
+ if(strncmp(buf, "port ", 5) == 0) {
+ port = strtoul(buf+5, 0, 0);
+ if(port < 0x203 || port > 0x3ff)
+ error("bad value for rddata port");
+ qlock(&pnp);
+ if(waserror()) {
+ qunlock(&pnp);
+ nexterror();
+ }
+ if(pnp.rddata > 0)
+ error("pnp port already set");
+ if(!pnpscan(port, 0))
+ error("no cards found");
+ qunlock(&pnp);
+ poperror();
+ }
+ else if(strncmp(buf, "debug ", 6) == 0)
+ pnp.debug = strtoul(buf+6, 0, 0);
+ else
+ error(Ebadctl);
+ break;
+ case Qcsnctl:
+ csn = CSN(c->qid);
+ cp = findcsn(csn, 0, 1);
+ if(cp == nil)
+ error(Egreg);
+ if(!wrconfig(cp, buf))
+ error(Ebadctl);
+ break;
+ case Qpciraw:
+ tbdf = MKBUS(BusPCI, 0, 0, 0)|BUSBDF((ulong)c->qid.path);
+ p = pcimatchtbdf(tbdf);
+ if(p == nil)
+ error(Egreg);
+ if(offset > 256)
+ return 0;
+ if(n+offset > 256)
+ n = 256-offset;
+ r = offset;
+ if(!(r & 3) && n == 4){
+ x = GBIT32(a);
+ pcicfgw32(p, r, x);
+ return 4;
+ }
+ if(!(r & 1) && n == 2){
+ x = GBIT16(a);
+ pcicfgw16(p, r, x);
+ return 2;
+ }
+ for(i = 0; i < n; i++){
+ x = GBIT8(a);
+ pcicfgw8(p, r, x);
+ a++;
+ r++;
+ }
+ return i;
+ default:
+ error(Egreg);
+ }
+ return n;
+}
+
+static int
+wrconfig(Card *c, char *cmd)
+{
+ /* This should implement setting of I/O bases, etc */
+ USED(c, cmd);
+ return 1;
+}
+
+
+Dev pnpdevtab = {
+ '$',
+ "pnp",
+
+ pnpreset,
+ devinit,
+ devshutdown,
+ pnpattach,
+ pnpwalk,
+ pnpstat,
+ pnpopen,
+ devcreate,
+ pnpclose,
+ pnpread,
+ devbread,
+ pnpwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};