Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#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;
        }
}