539 lines
9 KiB
C
539 lines
9 KiB
C
#include "u.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "../port/error.h"
|
|
#include "io.h"
|
|
|
|
enum{
|
|
Linktarget = 0x13,
|
|
};
|
|
|
|
/*
|
|
* read and crack the card information structure enough to set
|
|
* important parameters like power
|
|
*/
|
|
/* cis memory walking */
|
|
typedef struct Cisdat {
|
|
uchar *cisbase;
|
|
int cispos;
|
|
int cisskip;
|
|
int cislen;
|
|
} Cisdat;
|
|
|
|
static void tcfig(PCMslot*, Cisdat*, int);
|
|
static void tentry(PCMslot*, Cisdat*, int);
|
|
static void tvers1(PCMslot*, Cisdat*, int);
|
|
static void tlonglnkmfc(PCMslot*, Cisdat*, int);
|
|
|
|
static int
|
|
readc(Cisdat *cis, uchar *x)
|
|
{
|
|
if(cis->cispos >= cis->cislen)
|
|
return 0;
|
|
*x = cis->cisbase[cis->cisskip*cis->cispos];
|
|
cis->cispos++;
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
xcistuple(int slotno, int tuple, int subtuple, void *v, int nv, int attr)
|
|
{
|
|
PCMmap *m;
|
|
Cisdat cis;
|
|
int i, l;
|
|
uchar *p;
|
|
uchar type, link, n, c;
|
|
int this, subtype;
|
|
|
|
m = pcmmap(slotno, 0, 0, attr);
|
|
if(m == 0)
|
|
return -1;
|
|
|
|
cis.cisbase = KADDR(m->isa);
|
|
cis.cispos = 0;
|
|
cis.cisskip = attr ? 2 : 1;
|
|
cis.cislen = m->len;
|
|
|
|
/* loop through all the tuples */
|
|
for(i = 0; i < 1000; i++){
|
|
this = cis.cispos;
|
|
if(readc(&cis, &type) != 1)
|
|
break;
|
|
if(type == 0xFF)
|
|
break;
|
|
if(readc(&cis, &link) != 1)
|
|
break;
|
|
if(link == 0xFF)
|
|
break;
|
|
|
|
n = link;
|
|
if(link > 1 && subtuple != -1){
|
|
if(readc(&cis, &c) != 1)
|
|
break;
|
|
subtype = c;
|
|
n--;
|
|
}else
|
|
subtype = -1;
|
|
|
|
if(type == tuple && subtype == subtuple){
|
|
p = v;
|
|
for(l=0; l<nv && l<n; l++)
|
|
if(readc(&cis, p++) != 1)
|
|
break;
|
|
pcmunmap(slotno, m);
|
|
return nv;
|
|
}
|
|
cis.cispos = this + (2+link);
|
|
}
|
|
pcmunmap(slotno, m);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
pcmcistuple(int slotno, int tuple, int subtuple, void *v, int nv)
|
|
{
|
|
int n;
|
|
|
|
/* try attribute space, then memory */
|
|
if((n = xcistuple(slotno, tuple, subtuple, v, nv, 1)) >= 0)
|
|
return n;
|
|
return xcistuple(slotno, tuple, subtuple, v, nv, 0);
|
|
}
|
|
|
|
void
|
|
pcmcisread(PCMslot *pp)
|
|
{
|
|
int this;
|
|
Cisdat cis;
|
|
PCMmap *m;
|
|
uchar type, link;
|
|
|
|
memset(pp->ctab, 0, sizeof(pp->ctab));
|
|
pp->ncfg = 0;
|
|
memset(pp->cfg, 0, sizeof(pp->cfg));
|
|
pp->configed = 0;
|
|
pp->nctab = 0;
|
|
pp->verstr[0] = 0;
|
|
|
|
/*
|
|
* Read all tuples in attribute space.
|
|
*/
|
|
m = pcmmap(pp->slotno, 0, 0, 1);
|
|
if(m == 0)
|
|
return;
|
|
|
|
cis.cisbase = KADDR(m->isa);
|
|
cis.cispos = 0;
|
|
cis.cisskip = 2;
|
|
cis.cislen = m->len;
|
|
|
|
/* loop through all the tuples */
|
|
for(;;){
|
|
this = cis.cispos;
|
|
if(readc(&cis, &type) != 1)
|
|
break;
|
|
if(type == 0xFF)
|
|
break;
|
|
if(readc(&cis, &link) != 1)
|
|
break;
|
|
|
|
switch(type){
|
|
default:
|
|
break;
|
|
case 6:
|
|
tlonglnkmfc(pp, &cis, type);
|
|
break;
|
|
case 0x15:
|
|
tvers1(pp, &cis, type);
|
|
break;
|
|
case 0x1A:
|
|
tcfig(pp, &cis, type);
|
|
break;
|
|
case 0x1B:
|
|
tentry(pp, &cis, type);
|
|
break;
|
|
}
|
|
|
|
if(link == 0xFF)
|
|
break;
|
|
cis.cispos = this + (2+link);
|
|
}
|
|
pcmunmap(pp->slotno, m);
|
|
}
|
|
|
|
static ulong
|
|
getlong(Cisdat *cis, int size)
|
|
{
|
|
uchar c;
|
|
int i;
|
|
ulong x;
|
|
|
|
x = 0;
|
|
for(i = 0; i < size; i++){
|
|
if(readc(cis, &c) != 1)
|
|
break;
|
|
x |= c<<(i*8);
|
|
}
|
|
return x;
|
|
}
|
|
|
|
static void
|
|
tcfig(PCMslot *pp, Cisdat *cis, int )
|
|
{
|
|
uchar size, rasize, rmsize;
|
|
uchar last;
|
|
|
|
if(readc(cis, &size) != 1)
|
|
return;
|
|
rasize = (size&0x3) + 1;
|
|
rmsize = ((size>>2)&0xf) + 1;
|
|
if(readc(cis, &last) != 1)
|
|
return;
|
|
|
|
if(pp->ncfg >= 8){
|
|
print("tcfig: too many configuration registers\n");
|
|
return;
|
|
}
|
|
|
|
pp->cfg[pp->ncfg].caddr = getlong(cis, rasize);
|
|
pp->cfg[pp->ncfg].cpresent = getlong(cis, rmsize);
|
|
pp->ncfg++;
|
|
}
|
|
|
|
static ulong vexp[8] =
|
|
{
|
|
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
|
|
};
|
|
static ulong vmant[16] =
|
|
{
|
|
10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90,
|
|
};
|
|
|
|
static ulong
|
|
microvolt(Cisdat *cis)
|
|
{
|
|
uchar c;
|
|
ulong microvolts;
|
|
ulong exp;
|
|
|
|
if(readc(cis, &c) != 1)
|
|
return 0;
|
|
exp = vexp[c&0x7];
|
|
microvolts = vmant[(c>>3)&0xf]*exp;
|
|
while(c & 0x80){
|
|
if(readc(cis, &c) != 1)
|
|
return 0;
|
|
switch(c){
|
|
case 0x7d:
|
|
break; /* high impedence when sleeping */
|
|
case 0x7e:
|
|
case 0x7f:
|
|
microvolts = 0; /* no connection */
|
|
break;
|
|
default:
|
|
exp /= 10;
|
|
microvolts += exp*(c&0x7f);
|
|
}
|
|
}
|
|
return microvolts;
|
|
}
|
|
|
|
static ulong
|
|
nanoamps(Cisdat *cis)
|
|
{
|
|
uchar c;
|
|
ulong nanoamps;
|
|
|
|
if(readc(cis, &c) != 1)
|
|
return 0;
|
|
nanoamps = vexp[c&0x7]*vmant[(c>>3)&0xf];
|
|
while(c & 0x80){
|
|
if(readc(cis, &c) != 1)
|
|
return 0;
|
|
if(c == 0x7d || c == 0x7e || c == 0x7f)
|
|
nanoamps = 0;
|
|
}
|
|
return nanoamps;
|
|
}
|
|
|
|
/*
|
|
* only nominal voltage (feature 1) is important for config,
|
|
* other features must read card to stay in sync.
|
|
*/
|
|
static ulong
|
|
power(Cisdat *cis)
|
|
{
|
|
uchar feature;
|
|
ulong mv;
|
|
|
|
mv = 0;
|
|
if(readc(cis, &feature) != 1)
|
|
return 0;
|
|
if(feature & 1)
|
|
mv = microvolt(cis);
|
|
if(feature & 2)
|
|
microvolt(cis);
|
|
if(feature & 4)
|
|
microvolt(cis);
|
|
if(feature & 8)
|
|
nanoamps(cis);
|
|
if(feature & 0x10)
|
|
nanoamps(cis);
|
|
if(feature & 0x20)
|
|
nanoamps(cis);
|
|
if(feature & 0x40)
|
|
nanoamps(cis);
|
|
return mv/1000000;
|
|
}
|
|
|
|
static ulong mantissa[16] =
|
|
{ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, };
|
|
|
|
static ulong exponent[8] =
|
|
{ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, };
|
|
|
|
static ulong
|
|
ttiming(Cisdat *cis, int scale)
|
|
{
|
|
uchar unscaled;
|
|
ulong nanosecs;
|
|
|
|
if(readc(cis, &unscaled) != 1)
|
|
return 0;
|
|
nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10;
|
|
nanosecs = nanosecs * vexp[scale];
|
|
return nanosecs;
|
|
}
|
|
|
|
static void
|
|
timing(Cisdat *cis, PCMconftab *ct)
|
|
{
|
|
uchar c, i;
|
|
|
|
if(readc(cis, &c) != 1)
|
|
return;
|
|
i = c&0x3;
|
|
if(i != 3)
|
|
ct->maxwait = ttiming(cis, i); /* max wait */
|
|
i = (c>>2)&0x7;
|
|
if(i != 7)
|
|
ct->readywait = ttiming(cis, i); /* max ready/busy wait */
|
|
i = (c>>5)&0x7;
|
|
if(i != 7)
|
|
ct->otherwait = ttiming(cis, i); /* reserved wait */
|
|
}
|
|
|
|
static void
|
|
iospaces(Cisdat *cis, PCMconftab *ct)
|
|
{
|
|
uchar c;
|
|
int i, nio;
|
|
|
|
ct->nio = 0;
|
|
if(readc(cis, &c) != 1)
|
|
return;
|
|
|
|
ct->bit16 = ((c>>5)&3) >= 2;
|
|
if(!(c & 0x80)){
|
|
ct->io[0].start = 0;
|
|
ct->io[0].len = 1<<(c&0x1f);
|
|
ct->nio = 1;
|
|
return;
|
|
}
|
|
|
|
if(readc(cis, &c) != 1)
|
|
return;
|
|
|
|
/*
|
|
* For each of the range descriptions read the
|
|
* start address and the length (value is length-1).
|
|
*/
|
|
nio = (c&0xf)+1;
|
|
for(i = 0; i < nio; i++){
|
|
ct->io[i].start = getlong(cis, (c>>4)&0x3);
|
|
ct->io[i].len = getlong(cis, (c>>6)&0x3)+1;
|
|
}
|
|
ct->nio = nio;
|
|
}
|
|
|
|
static void
|
|
irq(Cisdat *cis, PCMconftab *ct)
|
|
{
|
|
uchar c;
|
|
|
|
if(readc(cis, &c) != 1)
|
|
return;
|
|
ct->irqtype = c & 0xe0;
|
|
if(c & 0x10)
|
|
ct->irqs = getlong(cis, 2);
|
|
else
|
|
ct->irqs = 1<<(c&0xf);
|
|
ct->irqs &= 0xDEB8; /* levels available to card */
|
|
}
|
|
|
|
static void
|
|
memspace(Cisdat *cis, int asize, int lsize, int host)
|
|
{
|
|
ulong haddress, address, len;
|
|
|
|
len = getlong(cis, lsize)*256;
|
|
address = getlong(cis, asize)*256;
|
|
USED(len, address);
|
|
if(host){
|
|
haddress = getlong(cis, asize)*256;
|
|
USED(haddress);
|
|
}
|
|
}
|
|
|
|
static void
|
|
tentry(PCMslot *pp, Cisdat *cis, int )
|
|
{
|
|
uchar c, i, feature;
|
|
PCMconftab *ct;
|
|
|
|
if(pp->nctab >= nelem(pp->ctab))
|
|
return;
|
|
if(readc(cis, &c) != 1)
|
|
return;
|
|
ct = &pp->ctab[pp->nctab++];
|
|
|
|
/* copy from last default config */
|
|
if(pp->def)
|
|
*ct = *pp->def;
|
|
|
|
ct->index = c & 0x3f;
|
|
|
|
/* is this the new default? */
|
|
if(c & 0x40)
|
|
pp->def = ct;
|
|
|
|
/* memory wait specified? */
|
|
if(c & 0x80){
|
|
if(readc(cis, &i) != 1)
|
|
return;
|
|
if(i&0x80)
|
|
ct->memwait = 1;
|
|
}
|
|
|
|
if(readc(cis, &feature) != 1)
|
|
return;
|
|
switch(feature&0x3){
|
|
case 1:
|
|
ct->vpp1 = ct->vpp2 = power(cis);
|
|
break;
|
|
case 2:
|
|
power(cis);
|
|
ct->vpp1 = ct->vpp2 = power(cis);
|
|
break;
|
|
case 3:
|
|
power(cis);
|
|
ct->vpp1 = power(cis);
|
|
ct->vpp2 = power(cis);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if(feature&0x4)
|
|
timing(cis, ct);
|
|
if(feature&0x8)
|
|
iospaces(cis, ct);
|
|
if(feature&0x10)
|
|
irq(cis, ct);
|
|
switch((feature>>5)&0x3){
|
|
case 1:
|
|
memspace(cis, 0, 2, 0);
|
|
break;
|
|
case 2:
|
|
memspace(cis, 2, 2, 0);
|
|
break;
|
|
case 3:
|
|
if(readc(cis, &c) != 1)
|
|
return;
|
|
for(i = 0; i <= (c&0x7); i++)
|
|
memspace(cis, (c>>5)&0x3, (c>>3)&0x3, c&0x80);
|
|
break;
|
|
}
|
|
pp->configed++;
|
|
}
|
|
|
|
static void
|
|
tvers1(PCMslot *pp, Cisdat *cis, int )
|
|
{
|
|
uchar c, major, minor, last;
|
|
int i;
|
|
|
|
if(readc(cis, &major) != 1)
|
|
return;
|
|
if(readc(cis, &minor) != 1)
|
|
return;
|
|
last = 0;
|
|
for(i = 0; i < sizeof(pp->verstr)-1; i++){
|
|
if(readc(cis, &c) != 1)
|
|
return;
|
|
if(c == 0)
|
|
c = ';';
|
|
if(c == '\n')
|
|
c = ';';
|
|
if(c == 0xff)
|
|
break;
|
|
if(c == ';' && last == ';')
|
|
continue;
|
|
pp->verstr[i] = c;
|
|
last = c;
|
|
}
|
|
pp->verstr[i] = 0;
|
|
}
|
|
|
|
static void
|
|
tlonglnkmfc(PCMslot *pp, Cisdat *cis, int)
|
|
{
|
|
int i, npos, opos;
|
|
uchar nfn, space, expect, type, this, link;
|
|
|
|
readc(cis, &nfn);
|
|
for(i = 0; i < nfn; i++){
|
|
readc(cis, &space);
|
|
npos = getlong(cis, 4);
|
|
opos = cis->cispos;
|
|
cis->cispos = npos;
|
|
expect = Linktarget;
|
|
|
|
while(1){
|
|
this = cis->cispos;
|
|
if(readc(cis, &type) != 1)
|
|
break;
|
|
if(type == 0xFF)
|
|
break;
|
|
if(readc(cis, &link) != 1)
|
|
break;
|
|
|
|
if(expect && expect != type){
|
|
print("tlonglnkmfc: expected %X found %X\n",
|
|
expect, type);
|
|
break;
|
|
}
|
|
expect = 0;
|
|
|
|
switch(type){
|
|
default:
|
|
break;
|
|
case 0x15:
|
|
tvers1(pp, cis, type);
|
|
break;
|
|
case 0x1A:
|
|
tcfig(pp, cis, type);
|
|
break;
|
|
case 0x1B:
|
|
tentry(pp, cis, type);
|
|
break;
|
|
}
|
|
|
|
if(link == 0xFF)
|
|
break;
|
|
cis->cispos = this + (2+link);
|
|
}
|
|
cis->cispos = opos;
|
|
}
|
|
}
|