summaryrefslogtreecommitdiff
path: root/sys/src/9/mtx/devarch.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/mtx/devarch.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/mtx/devarch.c')
-rwxr-xr-xsys/src/9/mtx/devarch.c401
1 files changed, 401 insertions, 0 deletions
diff --git a/sys/src/9/mtx/devarch.c b/sys/src/9/mtx/devarch.c
new file mode 100755
index 000000000..cd5eea88f
--- /dev/null
+++ b/sys/src/9/mtx/devarch.c
@@ -0,0 +1,401 @@
+#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);
+}