/*
* 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));
if(nc == nil) {
if(dolock)
qunlock(&pnp);
error(Enomem);
}
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,
};
|