![cinap_lenrek](/assets/img/avatar_default.png)
This makes vmap()/vunmap() take a vlong size argument, and change the type of Pci.mem[].size to vlong as well. Even if vmap() wont support large mappings, it is nice to get the original unruncated value for error checking. pc64 needs a bigger VMAP window, as system76 pangolin puts the framebuffer at a physical address > 512GB.
696 lines
14 KiB
C
696 lines
14 KiB
C
/*
|
|
* 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/pci.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) {
|
|
if((nc = malloc(sizeof(Card))) == nil)
|
|
panic("pnp: no memory for Card");
|
|
*l = nc;
|
|
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:%.8llux %lld", 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,
|
|
};
|