#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" static char Etoomany[] = "too many ports opened"; static char Enoclient[] = "no such pci device..."; static void *pcidevbase = 0; enum { Qtopdir = 0, Qbase, Qpcidir, Qinb, /* reading */ Qinw, Qinl, Qoutb, /* writing */ Qoutw, Qoutl, }; typedef struct Mypci Mypci; typedef struct Devpci Devpci; struct Mypci { QLock; ulong base; }; enum { Maxpcidev = 128, /* looks like a good number */ }; struct Devpci { Mypci *pci[Maxpcidev]; uint npci; }; static Devpci devpci; static Dirtab pcidevdir[] = { "inb", {Qinb}, 0, 0440, "inw", {Qinw}, 0, 0440, "inl", {Qinl}, 0, 0440, "outb", {Qoutb}, 0, 0220, "outw", {Qoutw}, 0, 0220, "outl", {Qoutl}, 0, 0220, }; static Dirtab pcitopdir[] = { "base", {Qbase}, 0, 0220, }; #define QSHIFT 4 /* location in qid of client # */ #define QID(q) (((q).path&0x0000000F)>>0) #define CLIENTPATH(q) ((q&0x07FFFFFF0)>>QSHIFT) #define CLIENT(q) CLIENTPATH((q).path) Mypci* pcislotpath(ulong path) { Mypci *cl; int slot; slot = CLIENTPATH(path); if(slot == 0) return nil; cl = devpci.pci[slot-1]; if(cl==0 || cl->base==0) return nil; return cl; } Mypci* pcislot(Chan *c) { Mypci *client; client = pcislotpath(c->qid.path); if(client == nil) error(Enoclient); return client; } static int pcidevgen(Chan *c, Dirtab *tab, int x, int s, Dir *dp) { int t; Qid q; ulong path; Mypci *cl; char buf[NAMELEN]; USED(tab, x); q.vers = 0; if(s == DEVDOTDOT){ switch(QID(c->qid)){ case Qpcidir: cl = pcislot(c); sprint(buf, "0x%lux", cl->base); devdir(c, (Qid){CHDIR|Qtopdir, 0}, buf, 0, eve, 0500, dp); break; default: panic("pcidevwalk %lux", c->qid.path); } return 1; } t = QID(c->qid); if(t == Qtopdir){ if(s == 0){ q = (Qid){Qbase, 0}; devdir(c, q, "base", 0, eve, 0600, dp); } else if(s <= devpci.npci){ cl = devpci.pci[s-1]; if(cl == 0) return 0; sprint(buf, "0x%lux", cl->base); q = (Qid){CHDIR|(s<qid.path&~(CHDIR|((1<qid.vers; switch(s){ case 0: q = (Qid){path|Qinb, c->qid.vers}; devdir(c, q, "inb", 0, eve, 0200, dp); break; case 1: q = (Qid){path|Qinw, c->qid.vers}; devdir(c, q, "inw", 0, eve, 0200, dp); break; case 2: q = (Qid){path|Qinl, c->qid.vers}; devdir(c, q, "inl", 0, eve, 0200, dp); break; case 3: q = (Qid){path|Qoutb, c->qid.vers}; devdir(c, q, "outb", 0, eve, 0400, dp); break; case 4: q = (Qid){path|Qoutw, c->qid.vers}; devdir(c, q, "outw", 0, eve, 0400, dp); break; case 5: q = (Qid){path|Qoutl, c->qid.vers}; devdir(c, q, "outl", 0, eve, 0400, dp); break; default: return -1; } return 1; } static void pcidevreset(void) { } void pcidevinit(void) { devinit(); } static Chan* pcidevattach(char* spec) { return devattach('Z', spec); } int pcidevwalk(Chan* c, char* name) { return devwalk(c, name, 0,0 , pcidevgen); } static void pcidevstat(Chan* c, char* dp) { devstat(c, dp, pcitopdir, nelem(pcitopdir), devgen); } static Chan* pcidevopen(Chan* c, int omode) { return devopen(c, omode, pcitopdir, nelem(pcitopdir), devgen); } static long pcidevread(Chan* c, void* a, long n, vlong) { char str[16]; int size = 0; ulong o; Mypci *cl; if(c->qid.path & CHDIR) return devdirread(c, a, n, 0, 0, pcidevgen); cl = pcislot(c); qlock(cl); if(waserror()){ qunlock(cl); nexterror(); } /* assume some things about 'a' that we probably shouldn't */ switch(QID(c->qid)){ case Qinb: size = sprint(str, "0x%2.2ux", inb(cl->base) & 0xFF); break; case Qinw: size = sprint(str, "0x%4.4ux", ins(cl->base) & 0xFFFF); break; case Qinl: size = sprint(str, "0x%8.8lux", inl(cl->base) & 0xFFFFFFFF); break; } qunlock(cl); poperror(); o = c->offset; if(o >= size) return 0; if(o+n > size) n = size-c->offset; memmove(a, str+o, n); return n; } static long pcidevwrite(Chan* c, void* a, long n, vlong off) { Mypci *cl; ulong offset = off; USED(offset, n); if(c->qid.path & CHDIR) error(Eisdir); if(QID(c->qid) == Qbase) { if(devpci.npci < Maxpcidev) { devpci.pci[devpci.npci] = (Mypci *)malloc(sizeof(Mypci)); devpci.pci[devpci.npci]->base = strtol(a, nil, 0); devpci.npci++; return 1; } else { error(Etoomany); return 0; } } cl = pcislot(c); qlock(cl); switch(QID(c->qid)){ case Qoutb: outb(PADDR(cl->base), atol(a) & 0xff); break; case Qoutw: outs(PADDR(cl->base), atol(a) & 0xffff); break; case Qoutl: outl(PADDR(cl->base), atol(a)); break; } qunlock(cl); return 1; } static void pcidevcreate(Chan *, char*, int, ulong) { } static Chan * pcidevclone(Chan *c1, Chan *c2) { return devclone(c1, c2); } static void pcidevremove(Chan *c) { int slot; Mypci *cl; slot = CLIENTPATH(c->qid.path); if(slot == 0) return; slot--; /* align with pci[] */ cl = devpci.pci[slot]; free(cl); devpci.npci--; while (slot < devpci.npci) { devpci.pci[slot] = devpci.pci[slot+1]; slot++; } } static void pcidevclose(Chan *c) { USED(c); } Dev pcidevdevtab = { 'Z', "pcidev", pcidevreset, devinit, pcidevattach, pcidevclone, pcidevwalk, pcidevstat, pcidevopen, pcidevcreate, pcidevclose, pcidevread, devbread, pcidevwrite, devbwrite, pcidevremove, devwstat, };