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 "io.h"
#include "../port/error.h"

typedef struct IOMap IOMap;
struct IOMap
{
        IOMap   *next;
        char    tag[13];
        ulong   start;
        ulong   end;
};

static struct
{
        Lock;
        IOMap   *m;
        IOMap   *free;
        IOMap   maps[32];               // some initial free maps

        QLock   ql;                     // lock for reading map
} iomap;

enum {
        Qdir = 0,
        Qioalloc = 1,
        Qiob,
        Qiow,
        Qiol,
        Qbase,

        Qmax = 16,
};

typedef long Rdwrfn(Chan*, void*, long, vlong);

static Rdwrfn *readfn[Qmax];
static Rdwrfn *writefn[Qmax];

static Dirtab archdir[] = {
        ".",    { Qdir, 0, QTDIR },     0,      0555,
        "ioalloc",      { Qioalloc, 0 },        0,      0444,
        "iob",          { Qiob, 0 },            0,      0660,
        "iow",          { Qiow, 0 },            0,      0660,
        "iol",          { Qiol, 0 },            0,      0660,
};
Lock archwlock; /* the lock is only for changing archdir */
int narchdir = Qbase;
int (*_pcmspecial)(char *, ISAConf *);
void (*_pcmspecialclose)(int);

/*
 * Add a file to the #P listing.  Once added, you can't delete it.
 * You can't add a file with the same name as one already there,
 * and you get a pointer to the Dirtab entry so you can do things
 * like change the Qid version.  Changing the Qid path is disallowed.
 */
Dirtab*
addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn)
{
        int i;
        Dirtab d;
        Dirtab *dp;

        memset(&d, 0, sizeof d);
        strcpy(d.name, name);
        d.perm = perm;

        lock(&archwlock);
        if(narchdir >= Qmax){
                unlock(&archwlock);
                return nil;
        }

        for(i=0; i<narchdir; i++)
                if(strcmp(archdir[i].name, name) == 0){
                        unlock(&archwlock);
                        return nil;
                }

        d.qid.path = narchdir;
        archdir[narchdir] = d;
        readfn[narchdir] = rdfn;
        writefn[narchdir] = wrfn;
        dp = &archdir[narchdir++];
        unlock(&archwlock);

        return dp;
}

void
ioinit(void)
{
        int i;

        for(i = 0; i < nelem(iomap.maps)-1; i++)
                iomap.maps[i].next = &iomap.maps[i+1];
        iomap.maps[i].next = nil;
        iomap.free = iomap.maps;

        // a dummy entry at 2^17
        ioalloc(0x20000, 1, 0, "dummy");
}

//
//      alloc some io port space and remember who it was
//      alloced to.  if port < 0, find a free region.
//
int
ioalloc(int port, int size, int align, char *tag)
{
        IOMap *m, **l;
        int i;

        lock(&iomap);
        if(port < 0){
                // find a free port above 0x400 and below 0x1000
                port = 0x400;
                for(l = &iomap.m; *l; l = &(*l)->next){
                        m = *l;
                        i = m->start - port;
                        if(i > size)
                                break;
                        if(align > 0)
                                port = ((port+align-1)/align)*align;
                        else
                                port = m->end;
                }
                if(*l == nil){
                        unlock(&iomap);
                        return -1;
                }
        } else {
                // see if the space clashes with previously allocated ports
                for(l = &iomap.m; *l; l = &(*l)->next){
                        m = *l;
                        if(m->end <= port)
                                continue;
                        if(m->start >= port+size)
                                break;
                        unlock(&iomap);
                        return -1;
                }
        }
        m = iomap.free;
        if(m == nil){
                print("ioalloc: out of maps");
                unlock(&iomap);
                return port;
        }
        iomap.free = m->next;
        m->next = *l;
        m->start = port;
        m->end = port + size;
        strncpy(m->tag, tag, sizeof(m->tag));
        m->tag[sizeof(m->tag)-1] = 0;
        *l = m;

        archdir[0].qid.vers++;

        unlock(&iomap);
        return m->start;
}

void
iofree(int port)
{
        IOMap *m, **l;

        lock(&iomap);
        for(l = &iomap.m; *l; l = &(*l)->next){
                if((*l)->start == port){
                        m = *l;
                        *l = m->next;
                        m->next = iomap.free;
                        iomap.free = m;
                        break;
                }
                if((*l)->start > port)
                        break;
        }
        archdir[0].qid.vers++;
        unlock(&iomap);
}

int
iounused(int start, int end)
{
        IOMap *m;

        for(m = iomap.m; m; m = m->next){
                if(start >= m->start && start < m->end
                || start <= m->start && end > m->start)
                        return 0; 
        }
        return 1;
}

static void
checkport(int start, int end)
{
        /* standard vga regs are OK */
        if(start >= 0x2b0 && end <= 0x2df+1)
                return;
        if(start >= 0x3c0 && end <= 0x3da+1)
                return;

        if(iounused(start, end))
                return;
        error(Eperm);
}

static Chan*
archattach(char* spec)
{
        return devattach('P', spec);
}

Walkqid*
archwalk(Chan* c, Chan *nc, char** name, int nname)
{
        return devwalk(c, nc, name, nname, archdir, narchdir, devgen);
}

static int
archstat(Chan* c, uchar* dp, int n)
{
        return devstat(c, dp, n, archdir, narchdir, devgen);
}

static Chan*
archopen(Chan* c, int omode)
{
        return devopen(c, omode, archdir, nelem(archdir), devgen);
}

static void
archclose(Chan*)
{
}

enum
{
        Linelen= 31,
};

static long
archread(Chan *c, void *a, long n, vlong offset)
{
        char buf[Linelen+1], *p;
        int port;
        ushort *sp;
        ulong *lp;
        IOMap *m;
        Rdwrfn *fn;

        switch((ulong)c->qid.path){

        case Qdir:
                return devdirread(c, a, n, archdir, nelem(archdir), devgen);

        case Qiob:
                port = offset;
                checkport(offset, offset+n);
                for(p = a; port < offset+n; port++)
                        *p++ = inb(port);
                return n;

        case Qiow:
                if((n & 0x01) || (offset & 0x01))
                        error(Ebadarg);
                checkport(offset, offset+n+1);
                n /= 2;
                sp = a;
                for(port = offset; port < offset+n; port += 2)
                        *sp++ = ins(port);
                return n*2;

        case Qiol:
                if((n & 0x03) || (offset & 0x03))
                        error(Ebadarg);
                checkport(offset, offset+n+3);
                n /= 4;
                lp = a;
                for(port = offset; port < offset+n; port += 4)
                        *lp++ = inl(port);
                return n*4;

        case Qioalloc:
                break;

        default:
                if(c->qid.path < narchdir && (fn = readfn[c->qid.path]))
                        return fn(c, a, n, offset);
                error(Eperm);
                break;
        }

        offset = offset/Linelen;
        n = n/Linelen;
        p = a;
        lock(&iomap);
        for(m = iomap.m; n > 0 && m != nil; m = m->next){
                if(offset-- > 0)
                        continue;
                if(strcmp(m->tag, "dummy") == 0)
                        break;
                sprint(buf, "%8lux %8lux %-12.12s\n", m->start, m->end-1, m->tag);
                memmove(p, buf, Linelen);
                p += Linelen;
                n--;
        }
        unlock(&iomap);

        return p - (char*)a;
}

static long
archwrite(Chan *c, void *a, long n, vlong offset)
{
        char *p;
        int port;
        ushort *sp;
        ulong *lp;
        Rdwrfn *fn;

        switch((ulong)c->qid.path){

        case Qiob:
                p = a;
                checkport(offset, offset+n);
                for(port = offset; port < offset+n; port++)
                        outb(port, *p++);
                return n;

        case Qiow:
                if((n & 01) || (offset & 01))
                        error(Ebadarg);
                checkport(offset, offset+n+1);
                n /= 2;
                sp = a;
                for(port = offset; port < offset+n; port += 2)
                        outs(port, *sp++);
                return n*2;

        case Qiol:
                if((n & 0x03) || (offset & 0x03))
                        error(Ebadarg);
                checkport(offset, offset+n+3);
                n /= 4;
                lp = a;
                for(port = offset; port < offset+n; port += 4)
                        outl(port, *lp++);
                return n*4;

        default:
                if(c->qid.path < narchdir && (fn = writefn[c->qid.path]))
                        return fn(c, a, n, offset);
                error(Eperm);
                break;
        }
        return 0;
}

Dev archdevtab = {
        'P',
        "arch",

        devreset,
        devinit,
        devshutdown,
        archattach,
        archwalk,
        archstat,
        archopen,
        devcreate,
        archclose,
        archread,
        devbread,
        archwrite,
        devbwrite,
        devremove,
        devwstat,
};

int
pcmspecial(char *idstr, ISAConf *isa)
{
        return (_pcmspecial  != nil)? _pcmspecial(idstr, isa): -1;
}

void
pcmspecialclose(int a)
{
        if (_pcmspecialclose != nil)
                _pcmspecialclose(a);
}