summaryrefslogtreecommitdiff
path: root/os/manga
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 21:39:35 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 21:39:35 +0000
commit74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a (patch)
treec6e220ba61db3a6ea4052e6841296d829654e664 /os/manga
parent46439007cf417cbd9ac8049bb4122c890097a0fa (diff)
20060303
Diffstat (limited to 'os/manga')
-rwxr-xr-xos/manga/Mk8
-rw-r--r--os/manga/archmanga.c164
-rw-r--r--os/manga/clock.c166
-rw-r--r--os/manga/dat.h128
-rw-r--r--os/manga/devether.c765
-rw-r--r--os/manga/devusb.c931
-rw-r--r--os/manga/eswnotes41
-rw-r--r--os/manga/ether8139.c744
-rw-r--r--os/manga/etherif.h44
-rw-r--r--os/manga/etherks8695.c1169
-rw-r--r--os/manga/flashif.h82
-rw-r--r--os/manga/fns.h163
-rw-r--r--os/manga/fpi.h61
-rw-r--r--os/manga/fpiarm.c483
-rw-r--r--os/manga/gpio.c75
-rw-r--r--os/manga/inb.c85
-rw-r--r--os/manga/io.h320
-rw-r--r--os/manga/ioring.c72
-rw-r--r--os/manga/l.s404
-rw-r--r--os/manga/main.c317
-rw-r--r--os/manga/manga137
-rw-r--r--os/manga/mem.h133
-rw-r--r--os/manga/mkfile89
-rw-r--r--os/manga/mmu.c244
-rw-r--r--os/manga/pci.c1007
-rw-r--r--os/manga/pinflatebin0 -> 20480 bytes
-rw-r--r--os/manga/trap.c498
-rw-r--r--os/manga/uartks8695.c629
-rw-r--r--os/manga/usb.h160
-rw-r--r--os/manga/usbuhci.c1556
30 files changed, 10675 insertions, 0 deletions
diff --git a/os/manga/Mk b/os/manga/Mk
new file mode 100755
index 00000000..51d69a44
--- /dev/null
+++ b/os/manga/Mk
@@ -0,0 +1,8 @@
+#!/bin/rc
+rfork ne
+ROOT=/usr/inferno
+fn cd
+NPROC=3
+path=(/usr/inferno/Plan9/$cputype/bin $path)
+#bind /usr/inferno/mkconfig.dist /usr/inferno/mkconfig
+exec mk $*
diff --git a/os/manga/archmanga.c b/os/manga/archmanga.c
new file mode 100644
index 00000000..45b0f68c
--- /dev/null
+++ b/os/manga/archmanga.c
@@ -0,0 +1,164 @@
+/*
+ * Manga Balance Plus
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+#include "../port/netif.h"
+#include "etherif.h"
+#include "../port/flashif.h"
+
+enum {
+ /* GPIO assignment ... */
+
+ Maxmac= 4, /* number of MAC addresses taken from EEPROM */
+};
+
+static uchar macaddrs[Maxmac][Eaddrlen] = {
+[0] {0x00, 0x10, 0xa1, 0x00, 0x10, 0x01},
+[1] {0x00, 0x11, 0x6E, 0x00, 0x4A, 0xD4},
+[2] {0x00, 0x10, 0xa1, 0x00, 0x20, 0x01}, /* TO DO */
+};
+
+void
+archreset(void)
+{
+ /* TO DO: set GPIO and other key registers? */
+ GPIOREG->iopm |= (1<<GPIO_status_orange_o)|(1<<GPIO_status_green_o);
+ GPIOREG->iopm &= ~(1<<GPIO_button_i);
+ GPIOREG->iopd &= ~(1<<GPIO_status_orange_o);
+ GPIOREG->iopd &= ~(1<<GPIO_status_green_o);
+ GPIOREG->iopc |= 0x8888;
+ m->cpuhz = 166000000; /* system clock is 125 = 5*CLOCKFREQ */
+ m->delayloop = m->cpuhz/1000;
+/*
+ uartdebuginit();
+*/
+}
+
+void
+ledset(int n)
+{
+ int s;
+
+ s = splhi();
+ if(n)
+ GPIOREG->iopd |= 1<<GPIO_status_green_o;
+ else
+ GPIOREG->iopd &= ~(1<<GPIO_status_green_o);
+ splx(s);
+}
+
+void
+archconfinit(void)
+{
+ ulong *p;
+
+ p = KADDR(PHYSMEMCR+0x30);
+ conf.topofmem = (((p[0]>>22)<<16)|0xFFFF)+1;
+// w = PMGRREG->ppcr & 0x1f;
+// m->cpuhz = CLOCKFREQ*(27*2*2);
+}
+
+void
+archuartpower(int, int)
+{
+}
+
+void
+kbdinit(void)
+{
+}
+
+void
+archreboot(void)
+{
+ dcflushall();
+ GPIOREG->iopd |= 1<<GPIO_status_green_o;
+ GPIOREG->iopd &= ~(1<<GPIO_status_orange_o);
+// mmuputctl(mmugetctl() & ~CpCaltivec); /* restore bootstrap's vectors */
+// RESETREG->rsrr = 1; /* software reset */
+ for(;;)
+ //spllo();
+ splhi();
+}
+
+void
+archflashwp(Flash*, int)
+{
+}
+
+/*
+ * for devflash.c:/^flashreset
+ * retrieve flash type, virtual base and length and return 0;
+ * return -1 on error (no flash)
+ */
+int
+archflashreset(int bank, Flash *f)
+{
+ ulong *p;
+ int w;
+
+ p = KADDR(PHYSMEMCR+0x10);
+iprint("Flash %8.8lux %8.8lux %8.8lux\n", p[0], p[1], p[4]);
+ w = p[4]&3;
+ if(bank > 0 || w == 0)
+ return -1;
+ if(w == 3)
+ w = 4;
+ f->type = "cfi8";
+ f->addr = (void*)FLASHMEM;
+ f->size = 0;
+ f->width = w;
+ f->interleave = 0;
+ return 0;
+}
+
+/*
+ * set ether parameters: the contents should be derived from EEPROM or NVRAM
+ */
+int
+archether(int ctlno, Ether *ether)
+{
+ ether->nopt = 0;
+ ether->itype = IRQ;
+ switch(ctlno){
+ case 0:
+ sprint(ether->type, "ks8695");
+ ether->mem = PHYSWANDMA;
+ ether->port = 0;
+ ether->irq = IRQwmrps;
+ break;
+ case 1:
+ sprint(ether->type, "ks8695");
+ ether->mem = PHYSLANDMA;
+ ether->port = 1;
+ ether->irq = IRQlmrps;
+ ether->maxmtu = ETHERMAXTU+4; /* 802.1[pQ] tags */
+ break;
+ case 2:
+ sprint(ether->type, "rtl8139");
+ ether->mem = 0;
+ ether->port = 0;
+ ether->irq = -1;
+ break;
+ default:
+ return -1;
+ }
+ memmove(ether->ea, macaddrs[ctlno], Eaddrlen);
+ return 1;
+}
+
+/*
+ * TO DO: extract some boot data from user area of flash
+ */
+
+void
+eepromscan(void)
+{
+}
diff --git a/os/manga/clock.c b/os/manga/clock.c
new file mode 100644
index 00000000..282f32d6
--- /dev/null
+++ b/os/manga/clock.c
@@ -0,0 +1,166 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ureg.h"
+
+enum {
+ Mclk= 25000000
+};
+
+typedef struct Clock0link Clock0link;
+typedef struct Clock0link {
+ void (*clock)(void);
+ Clock0link* link;
+} Clock0link;
+
+static Clock0link *clock0link;
+static Lock clock0lock;
+static void (*prof_fcn)(Ureg *, int);
+
+Timer*
+addclock0link(void (*clock)(void), int)
+{
+ Clock0link *lp;
+
+ if((lp = malloc(sizeof(Clock0link))) == 0){
+ print("addclock0link: too many links\n");
+ return nil;
+ }
+ ilock(&clock0lock);
+ lp->clock = clock;
+ lp->link = clock0link;
+ clock0link = lp;
+ iunlock(&clock0lock);
+ return nil;
+}
+
+static void
+profintr(Ureg *, void*)
+{
+ /* TO DO: watchdog, profile on Timer 0 */
+}
+
+static void
+clockintr(Ureg*, void*)
+{
+ Clock0link *lp;
+ static int blip, led;
+
+ if(++blip >= HZ){
+ blip = 0;
+ ledset(led ^= 1);
+ }
+ m->ticks++;
+
+ checkalarms();
+
+ if(canlock(&clock0lock)){
+ for(lp = clock0link; lp; lp = lp->link)
+ if(lp->clock)
+ lp->clock();
+ unlock(&clock0lock);
+ }
+
+ /* round robin time slice is done by trap.c and proc.c */
+}
+
+void
+installprof(void (*pf)(Ureg *, int))
+{
+ USED(pf);
+}
+
+void
+clockinit(void)
+{
+ TimerReg *tr;
+ IntrReg *ir;
+ ulong l, u;
+
+ m->ticks = 0;
+ tr = TIMERREG;
+ tr->enable = 0;
+ tr->pulse1 = 1;
+
+ /* first tune the delay loop parameter (using a search because the counter doesn't decrement) */
+ ir = INTRREG;
+ tr->count1 = Mclk/1000 - tr->pulse1; /* millisecond */
+ u = m->cpuhz/(2*1000); /* over-large estimate for a millisecond */
+ l = 10000;
+ while(l+1 < u){
+ m->delayloop = l + (u-l)/2;
+ ir->st = 1<<IRQtm1; /* reset edge */
+ tr->enable = 1<<1;
+ delay(1);
+ tr->enable = 0;
+ if(ir->st & (1<<IRQtm1))
+ u = m->delayloop;
+ else
+ l = m->delayloop;
+ }
+
+ intrenable(IRQ, IRQtm1, clockintr, nil, "timer.1");
+ tr->count1 = Mclk/HZ - tr->pulse1;
+ tr->enable = 1<<1; /* enable only Timer 1 */
+}
+
+void
+clockpoll(void)
+{
+}
+
+void
+clockcheck(void)
+{
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+ if(hz)
+ *hz = HZ;
+ return m->ticks;
+}
+
+void
+microdelay(int l)
+{
+ int i;
+
+ l *= m->delayloop;
+ l /= 1000;
+ if(l <= 0)
+ l = 1;
+ for(i = 0; i < l; i++)
+ ;
+}
+
+void
+delay(int l)
+{
+ ulong i, j;
+
+ j = m->delayloop;
+ while(l-- > 0)
+ for(i=0; i < j; i++)
+ ;
+}
+
+/*
+ * for devkprof.c
+ */
+long
+archkprofmicrosecondspertick(void)
+{
+ return MS2HZ*1000;
+}
+
+void
+archkprofenable(int)
+{
+ /* TO DO */
+}
diff --git a/os/manga/dat.h b/os/manga/dat.h
new file mode 100644
index 00000000..b53c25e5
--- /dev/null
+++ b/os/manga/dat.h
@@ -0,0 +1,128 @@
+typedef struct Conf Conf;
+typedef struct Dma Dma;
+typedef struct FPU FPU;
+typedef struct FPenv FPenv;
+typedef struct Label Label;
+typedef struct Lock Lock;
+typedef struct Mach Mach;
+typedef struct Ureg Ureg;
+typedef struct ISAConf ISAConf;
+typedef struct Pcidev Pcidev;
+
+typedef ulong Instr;
+
+struct Conf
+{
+ ulong nmach; /* processors */
+ ulong nproc; /* processes */
+ ulong npage0; /* total physical pages of memory */
+ ulong npage1; /* total physical pages of memory */
+ ulong topofmem; /* highest physical address + 1 */
+ ulong npage; /* total physical pages of memory */
+ ulong base0; /* base of bank 0 */
+ ulong base1; /* base of bank 1 */
+ ulong ialloc; /* max interrupt time allocation in bytes */
+
+ int useminicache; /* use mini cache: screen.c/lcd.c */
+ int textwrite; /* writeable text segment, for debug */
+ int portrait; /* display orientation */
+};
+
+#define NISAOPT 8
+struct ISAConf {
+ char type[KNAMELEN];
+ ulong port;
+ ulong irq;
+ int itype;
+ ulong dma;
+ ulong mem;
+ ulong size;
+ ulong freq;
+
+ int nopt;
+ char *opt[NISAOPT];
+};
+
+/*
+ * FPenv.status
+ */
+enum
+{
+ FPINIT,
+ FPACTIVE,
+ FPINACTIVE,
+};
+
+struct FPenv
+{
+ ulong status;
+ ulong control;
+ ushort fpistate; /* emulated fp */
+ ulong regs[8][3]; /* emulated fp */
+};
+
+/*
+ * This structure must agree with fpsave and fprestore asm routines
+ */
+struct FPU
+{
+ FPenv env;
+};
+
+struct Label
+{
+ ulong sp;
+ ulong pc;
+};
+
+struct Lock
+{
+ ulong key;
+ ulong sr;
+ ulong pc;
+ int pri;
+};
+
+#include "../port/portdat.h"
+
+/*
+ * machine dependent definitions not used by ../port/portdat.h
+ */
+struct Mach
+{
+ /* OFFSETS OF THE FOLLOWING KNOWN BY l.s */
+ ulong splpc; /* pc of last caller to splhi */
+
+ /* ordering from here on irrelevant */
+
+ int machno; /* physical id of processor */
+ ulong ticks; /* of the clock since boot time */
+ Proc *proc; /* current process on this processor */
+ Label sched; /* scheduler wakeup */
+ Lock alarmlock; /* access to alarm list */
+ void *alarm; /* alarms bound to this clock */
+ ulong cpuhz;
+ ulong delayloop;
+
+ /* stacks for exceptions */
+ ulong fiqstack[4];
+ ulong irqstack[4];
+ ulong abtstack[4];
+ ulong undstack[4];
+
+ int stack[1];
+};
+
+#define MACHP(n) (n == 0 ? (Mach*)(MACHADDR) : (Mach*)0)
+
+extern Mach *m;
+extern Proc *up;
+
+/*
+ * Layout at virtual address 0.
+ */
+typedef struct Vectorpage {
+ void (*vectors[8])(void);
+ uint vtable[8];
+} Vectorpage;
+extern Vectorpage *page0;
diff --git a/os/manga/devether.c b/os/manga/devether.c
new file mode 100644
index 00000000..18f786e4
--- /dev/null
+++ b/os/manga/devether.c
@@ -0,0 +1,765 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+enum {
+ Type8021Q= 0x8100, /* value of type field for 802.1[pQ] tags */
+};
+
+static Ether *etherxx[MaxEther]; /* real controllers */
+static Ether* vlanalloc(Ether*, int);
+static void vlanoq(Ether*, Block*);
+
+Chan*
+etherattach(char* spec)
+{
+ ulong ctlrno;
+ char *p;
+ Chan *chan;
+ Ether *ether, *vlan;
+ int vlanid;
+
+ ctlrno = 0;
+ vlanid = 0;
+ if(spec && *spec){
+ ctlrno = strtoul(spec, &p, 0);
+ if(ctlrno == 0 && p == spec || ctlrno >= MaxEther || *p && *p != '.')
+ error(Ebadarg);
+ if(*p == '.'){ /* vlan */
+ vlanid = strtoul(p+1, &p, 0);
+ if(vlanid <= 0 || vlanid > 0xFFF || *p)
+ error(Ebadarg);
+ }
+ }
+ if((ether = etherxx[ctlrno]) == 0)
+ error(Enodev);
+ rlock(ether);
+ if(waserror()){
+ runlock(ether);
+ nexterror();
+ }
+ if(vlanid){
+ if(ether->maxmtu < ETHERMAXTU+4)
+ error("interface cannot support 802.1 tags");
+ vlan = vlanalloc(ether, vlanid);
+ chan = devattach('l', spec);
+ chan->dev = ctlrno + (vlanid<<8);
+ chan->aux = vlan;
+ poperror();
+ runlock(ether);
+ return chan;
+ }
+ chan = devattach('l', spec);
+ chan->dev = ctlrno;
+ chan->aux = ether;
+ if(ether->attach)
+ ether->attach(ether);
+ poperror();
+ runlock(ether);
+ return chan;
+}
+
+static void
+ethershutdown(void)
+{
+ Ether *ether;
+ int i;
+
+ for(i=0; i<MaxEther; i++){
+ ether = etherxx[i];
+ if(ether != nil && ether->detach != nil)
+ ether->detach(ether);
+ }
+}
+
+static Walkqid*
+etherwalk(Chan* chan, Chan *nchan, char **name, int nname)
+{
+ Walkqid *wq;
+ Ether *ether;
+
+ ether = chan->aux;
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ wq = netifwalk(ether, chan, nchan, name, nname);
+ if(wq && wq->clone != nil && wq->clone != chan)
+ wq->clone->aux = ether;
+ poperror();
+ runlock(ether);
+ return wq;
+}
+
+static int
+etherstat(Chan* chan, uchar* dp, int n)
+{
+ int s;
+ Ether *ether;
+
+ ether = chan->aux;
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ s = netifstat(ether, chan, dp, n);
+ poperror();
+ runlock(ether);
+ return s;
+}
+
+static Chan*
+etheropen(Chan* chan, int omode)
+{
+ Chan *c;
+ Ether *ether;
+
+ ether = chan->aux;
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ c = netifopen(ether, chan, omode);
+ poperror();
+ runlock(ether);
+ return c;
+}
+
+static void
+etherclose(Chan* chan)
+{
+ Ether *ether;
+
+ ether = chan->aux;
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ netifclose(ether, chan);
+ poperror();
+ runlock(ether);
+}
+
+static long
+etherread(Chan* chan, void* buf, long n, vlong off)
+{
+ Ether *ether;
+ ulong offset = off;
+ long r;
+
+ ether = chan->aux;
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ if((chan->qid.type & QTDIR) == 0 && ether->ifstat){
+ /*
+ * With some controllers it is necessary to reach
+ * into the chip to extract statistics.
+ */
+ if(NETTYPE(chan->qid.path) == Nifstatqid){
+ r = ether->ifstat(ether, buf, n, offset);
+ goto out;
+ }
+ if(NETTYPE(chan->qid.path) == Nstatqid)
+ ether->ifstat(ether, buf, 0, offset);
+ }
+ r = netifread(ether, chan, buf, n, offset);
+out:
+ poperror();
+ runlock(ether);
+ return r;
+}
+
+static Block*
+etherbread(Chan* chan, long n, ulong offset)
+{
+ Block *b;
+ Ether *ether;
+
+ ether = chan->aux;
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ b = netifbread(ether, chan, n, offset);
+ poperror();
+ runlock(ether);
+ return b;
+}
+
+static int
+etherwstat(Chan* chan, uchar* dp, int n)
+{
+ Ether *ether;
+ int r;
+
+ ether = chan->aux;
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ r = netifwstat(ether, chan, dp, n);
+ poperror();
+ runlock(ether);
+ return r;
+}
+
+static void
+etherrtrace(Netfile* f, Etherpkt* pkt, int len)
+{
+ int i, n;
+ Block *bp;
+
+ if(qwindow(f->in) <= 0)
+ return;
+ if(len > 58)
+ n = 58;
+ else
+ n = len;
+ bp = iallocb(64);
+ if(bp == nil)
+ return;
+ memmove(bp->wp, pkt->d, n);
+ i = TK2MS(MACHP(0)->ticks);
+ bp->wp[58] = len>>8;
+ bp->wp[59] = len;
+ bp->wp[60] = i>>24;
+ bp->wp[61] = i>>16;
+ bp->wp[62] = i>>8;
+ bp->wp[63] = i;
+ bp->wp += 64;
+ qpass(f->in, bp);
+}
+
+Block*
+etheriq(Ether* ether, Block* bp, int fromwire)
+{
+ Etherpkt *pkt;
+ ushort type;
+ int len, multi, tome, fromme, vlanid, i;
+ Netfile **ep, *f, **fp, *fx;
+ Block *xbp;
+ Ether *vlan;
+
+ ether->inpackets++;
+
+ pkt = (Etherpkt*)bp->rp;
+ len = BLEN(bp);
+ type = (pkt->type[0]<<8)|pkt->type[1];
+ if(type == Type8021Q && ether->nvlan){
+ vlanid = nhgets(bp->rp+2*Eaddrlen+2) & 0xFFF;
+ if(vlanid){
+ for(i = 0; i < nelem(ether->vlans); i++){
+ vlan = ether->vlans[i];
+ if(vlan != nil && vlan->vlanid == vlanid){
+ memmove(bp->rp+4, bp->rp, 2*Eaddrlen);
+ bp->rp += 4;
+ return etheriq(vlan, bp, fromwire);
+ }
+ }
+ /* allow normal type handling to accept or discard it */
+ }
+ }
+
+ fx = 0;
+ ep = &ether->f[Ntypes];
+
+ multi = pkt->d[0] & 1;
+ /* check for valid multcast addresses */
+ if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){
+ if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
+ if(fromwire){
+ freeb(bp);
+ bp = 0;
+ }
+ return bp;
+ }
+ }
+
+ /* is it for me? */
+ tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+ fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
+
+ /*
+ * Multiplex the packet to all the connections which want it.
+ * If the packet is not to be used subsequently (fromwire != 0),
+ * attempt to simply pass it into one of the connections, thereby
+ * saving a copy of the data (usual case hopefully).
+ */
+ for(fp = ether->f; fp < ep; fp++){
+ if((f = *fp) && (f->type == type || f->type < 0))
+ if(tome || multi || f->prom){
+ /* Don't want to hear bridged packets */
+ if(f->bridge && !fromwire && !fromme)
+ continue;
+ if(!f->headersonly){
+ if(fromwire && fx == 0)
+ fx = f;
+ else if(xbp = iallocb(len)){
+ memmove(xbp->wp, pkt, len);
+ xbp->wp += len;
+ if(qpass(f->in, xbp) < 0)
+ ether->soverflows++;
+ }
+ else
+ ether->soverflows++;
+ }
+ else
+ etherrtrace(f, pkt, len);
+ }
+ }
+
+ if(fx){
+ if(qpass(fx->in, bp) < 0)
+ ether->soverflows++;
+ return 0;
+ }
+ if(fromwire){
+ freeb(bp);
+ return 0;
+ }
+
+ return bp;
+}
+
+static int
+etheroq(Ether* ether, Block* bp)
+{
+ int len, loopback, s;
+ Etherpkt *pkt;
+
+ ether->outpackets++;
+
+ /*
+ * Check if the packet has to be placed back onto the input queue,
+ * i.e. if it's a loopback or broadcast packet or the interface is
+ * in promiscuous mode.
+ * If it's a loopback packet indicate to etheriq that the data isn't
+ * needed and return, etheriq will pass-on or free the block.
+ * To enable bridging to work, only packets that were originated
+ * by this interface are fed back.
+ */
+ pkt = (Etherpkt*)bp->rp;
+ len = BLEN(bp);
+ loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+ if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){
+ s = splhi();
+ etheriq(ether, bp, 0);
+ splx(s);
+ }
+
+ if(!loopback){
+ if(ether->vlanid){
+ /* add tag */
+ bp = padblock(bp, 2+2);
+ memmove(bp->rp, bp->rp+4, 2*Eaddrlen);
+ hnputs(bp->rp+2*Eaddrlen, Type8021Q);
+ hnputs(bp->rp+2*Eaddrlen+2, ether->vlanid & 0xFFF); /* prio:3 0:1 vid:12 */
+ ether = ether->ctlr;
+ }
+ qbwrite(ether->oq, bp);
+ if(ether->transmit != nil)
+ ether->transmit(ether);
+ }else
+ freeb(bp);
+
+ return len;
+}
+
+static long
+etherwrite(Chan* chan, void* buf, long n, vlong)
+{
+ Ether *ether;
+ Block *bp;
+ int onoff;
+ Cmdbuf *cb;
+ long l;
+
+ ether = chan->aux;
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ if(NETTYPE(chan->qid.path) != Ndataqid) {
+ l = netifwrite(ether, chan, buf, n);
+ if(l >= 0)
+ goto out;
+ cb = parsecmd(buf, n);
+ if(strcmp(cb->f[0], "nonblocking") == 0){
+ if(cb->nf <= 1)
+ onoff = 1;
+ else
+ onoff = atoi(cb->f[1]);
+ if(ether->oq != nil)
+ qnoblock(ether->oq, onoff);
+ free(cb);
+ goto out;
+ }
+ free(cb);
+ if(ether->ctl!=nil){
+ l = ether->ctl(ether,buf,n);
+ goto out;
+ }
+ error(Ebadctl);
+ }
+
+ if(n > ether->maxmtu)
+ error(Etoobig);
+ if(n < ether->minmtu)
+ error(Etoosmall);
+ bp = allocb(n);
+ if(waserror()){
+ freeb(bp);
+ nexterror();
+ }
+ memmove(bp->rp, buf, n);
+ memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
+ bp->wp += n;
+ poperror();
+
+ l = etheroq(ether, bp);
+out:
+ poperror();
+ runlock(ether);
+ return l;
+}
+
+static long
+etherbwrite(Chan* chan, Block* bp, ulong)
+{
+ Ether *ether;
+ long n;
+
+ n = BLEN(bp);
+ if(NETTYPE(chan->qid.path) != Ndataqid){
+ if(waserror()) {
+ freeb(bp);
+ nexterror();
+ }
+ n = etherwrite(chan, bp->rp, n, 0);
+ poperror();
+ freeb(bp);
+ return n;
+ }
+ ether = chan->aux;
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ if(n > ether->maxmtu){
+ freeb(bp);
+ error(Etoobig);
+ }
+ if(n < ether->minmtu){
+ freeb(bp);
+ error(Etoosmall);
+ }
+ n = etheroq(ether, bp);
+ poperror();
+ runlock(ether);
+ return n;
+}
+
+static void
+nop(Ether*)
+{
+}
+
+static long
+vlanctl(Ether *ether, void *buf, long n)
+{
+ uchar ea[Eaddrlen];
+ Ether *master;
+ Cmdbuf *cb;
+ int i;
+
+ cb = parsecmd(buf, n);
+ if(cb->nf >= 2
+ && strcmp(cb->f[0], "ea")==0
+ && parseether(ea, cb->f[1]) == 0){
+ free(cb);
+ memmove(ether->ea, ea, Eaddrlen);
+ memmove(ether->addr, ether->ea, Eaddrlen);
+ return 0;
+ }
+ if(cb->nf == 1 && strcmp(cb->f[0], "disable") == 0){
+ master = ether->ctlr;
+ qlock(&master->vlq);
+ for(i = 0; i < nelem(master->vlans); i++)
+ if(master->vlans[i] == ether){
+ ether->vlanid = 0;
+ master->nvlan--;
+ break;
+ }
+ qunlock(&master->vlq);
+ free(cb);
+ return 0;
+ }
+ free(cb);
+ error(Ebadctl);
+ return -1; /* not reached */
+}
+
+static Ether*
+vlanalloc(Ether *ether, int id)
+{
+ Ether *vlan;
+ int i, fid;
+ char name[KNAMELEN];
+
+ qlock(&ether->vlq);
+ if(waserror()){
+ qunlock(&ether->vlq);
+ nexterror();
+ }
+ fid = -1;
+ for(i = 0; i < nelem(ether->vlans); i++){
+ vlan = ether->vlans[i];
+ if(vlan != nil && vlan->vlanid == id){
+ poperror();
+ qunlock(&ether->vlq);
+ return vlan;
+ }
+ if(fid < 0 && (vlan == nil || vlan->vlanid == 0))
+ fid = i;
+ }
+ if(fid < 0)
+ error(Enoifc);
+ snprint(name, sizeof(name), "ether%d.%d", ether->ctlrno, id);
+ vlan = ether->vlans[fid];
+ if(vlan == nil){
+ vlan = mallocz(sizeof(Ether), 1);
+ if(vlan == nil)
+ error(Enovmem);
+ netifinit(vlan, name, Ntypes, ether->limit);
+ ether->vlans[fid] = vlan; /* id is still zero, can't be matched */
+ ether->nvlan++;
+ }else
+ memmove(vlan->name, name, KNAMELEN-1);
+ vlan->attach = nop;
+ vlan->transmit = nil;
+ vlan->ctl = vlanctl;
+ vlan->irq = -1;
+// vlan->promiscuous = ether->promiscuous;
+// vlan->multicast = ether->multicast;
+ vlan->arg = vlan;
+ vlan->mbps = ether->mbps;
+ vlan->fullduplex = ether->fullduplex;
+ vlan->encry = ether->encry;
+ vlan->minmtu = ether->minmtu;
+ vlan->maxmtu = ether->maxmtu;
+ vlan->ctlrno = ether->ctlrno;
+ vlan->vlanid = id;
+ vlan->alen = Eaddrlen;
+ memmove(vlan->addr, ether->addr, sizeof(vlan->addr));
+ memmove(vlan->bcast, ether->bcast, sizeof(ether->bcast));
+ vlan->oq = nil;
+ vlan->ctlr = ether;
+ vlan->vlanid = id;
+ poperror();
+ qunlock(&ether->vlq);
+ return vlan;
+}
+
+static struct {
+ char* type;
+ int (*reset)(Ether*);
+} cards[MaxEther+1];
+
+void
+addethercard(char* t, int (*r)(Ether*))
+{
+ static int ncard;
+
+ if(ncard == MaxEther)
+ panic("too many ether cards");
+ cards[ncard].type = t;
+ cards[ncard].reset = r;
+ ncard++;
+}
+
+int
+parseether(uchar *to, char *from)
+{
+ char nip[4];
+ char *p;
+ int i;
+
+ p = from;
+ for(i = 0; i < Eaddrlen; i++){
+ if(*p == 0)
+ return -1;
+ nip[0] = *p++;
+ if(*p == 0)
+ return -1;
+ nip[1] = *p++;
+ nip[2] = 0;
+ to[i] = strtoul(nip, 0, 16);
+ if(*p == ':')
+ p++;
+ }
+ return 0;
+}
+
+static void
+etherreset(void)
+{
+ Ether *ether;
+ int i, n, ctlrno;
+ char name[KNAMELEN], buf[128];
+
+ for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+ if(ether == 0)
+ ether = malloc(sizeof(Ether));
+ memset(ether, 0, sizeof(Ether));
+ ether->ctlrno = ctlrno;
+ ether->mbps = 10;
+ ether->minmtu = ETHERMINTU;
+ ether->maxmtu = ETHERMAXTU;
+ ether->itype = -1;
+
+ if(archether(ctlrno, ether) <= 0)
+ continue;
+
+ for(n = 0; cards[n].type; n++){
+ if(cistrcmp(cards[n].type, ether->type))
+ continue;
+ for(i = 0; i < ether->nopt; i++){
+ if(cistrncmp(ether->opt[i], "ea=", 3) == 0){
+ if(parseether(ether->ea, &ether->opt[i][3]) == -1)
+ memset(ether->ea, 0, Eaddrlen);
+ }else if(cistrcmp(ether->opt[i], "fullduplex") == 0 ||
+ cistrcmp(ether->opt[i], "10BASE-TFD") == 0)
+ ether->fullduplex = 1;
+ else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
+ ether->mbps = 100;
+ }
+ if(cards[n].reset(ether))
+ break;
+ snprint(name, sizeof(name), "ether%d", ctlrno);
+
+ if(ether->interrupt != nil)
+ intrenable(ether->itype, ether->irq, ether->interrupt, ether, name);
+
+ i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud",
+ ctlrno, ether->type, ether->mbps, ether->port, ether->irq);
+ if(ether->mem)
+ i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem));
+ if(ether->size)
+ i += sprint(buf+i, " size 0x%luX", ether->size);
+ i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX",
+ ether->ea[0], ether->ea[1], ether->ea[2],
+ ether->ea[3], ether->ea[4], ether->ea[5]);
+ sprint(buf+i, "\n");
+ iprint(buf);
+
+ if(ether->mbps == 100){
+ netifinit(ether, name, Ntypes, 256*1024);
+ if(ether->oq == 0)
+ ether->oq = qopen(256*1024, Qmsg, 0, 0);
+ }
+ else{
+ netifinit(ether, name, Ntypes, 64*1024);
+ if(ether->oq == 0)
+ ether->oq = qopen(64*1024, Qmsg, 0, 0);
+ }
+ if(ether->oq == 0)
+ panic("etherreset %s", name);
+ ether->alen = Eaddrlen;
+ memmove(ether->addr, ether->ea, Eaddrlen);
+ memset(ether->bcast, 0xFF, Eaddrlen);
+
+ etherxx[ctlrno] = ether;
+ ether = 0;
+ break;
+ }
+ }
+ if(ether)
+ free(ether);
+}
+
+static void
+etherpower(int on)
+{
+ int i;
+ Ether *ether;
+
+ for(i = 0; i < MaxEther; i++){
+ if((ether = etherxx[i]) == nil || ether->power == nil)
+ continue;
+ if(on){
+ if(canrlock(ether))
+ continue;
+ if(ether->power != nil)
+ ether->power(ether, on);
+ wunlock(ether);
+ }else{
+ if(ether->readers)
+ continue;
+ wlock(ether);
+ if(ether->power != nil)
+ ether->power(ether, on);
+ /* Keep locked until power goes back on */
+ }
+ }
+}
+
+#define POLY 0xedb88320
+
+/* really slow 32 bit crc for ethers */
+ulong
+ethercrc(uchar *p, int len)
+{
+ int i, j;
+ ulong crc, b;
+
+ crc = 0xffffffff;
+ for(i = 0; i < len; i++){
+ b = *p++;
+ for(j = 0; j < 8; j++){
+ crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
+ b >>= 1;
+ }
+ }
+ return crc;
+}
+
+Dev etherdevtab = {
+ 'l',
+ "ether",
+
+ etherreset,
+ devinit,
+ ethershutdown,
+ etherattach,
+ etherwalk,
+ etherstat,
+ etheropen,
+ devcreate,
+ etherclose,
+ etherread,
+ etherbread,
+ etherwrite,
+ etherbwrite,
+ devremove,
+ etherwstat,
+ etherpower,
+};
diff --git a/os/manga/devusb.c b/os/manga/devusb.c
new file mode 100644
index 00000000..ead04b9a
--- /dev/null
+++ b/os/manga/devusb.c
@@ -0,0 +1,931 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#include "usb.h"
+
+static int debug = 0;
+
+#define Chatty 1
+#define DPRINT if(Chatty)print
+#define XPRINT if(debug)iprint
+
+Usbhost* usbhost[MaxUsb];
+
+static char *devstates[] = {
+ [Disabled] "Disabled",
+ [Attached] "Attached",
+ [Enabled] "Enabled",
+ [Assigned] "Assigned",
+ [Configured] "Configured",
+};
+
+static char Ebadusbmsg[] = "invalid parameters to USB ctl message";
+
+enum
+{
+ Qtopdir = 0,
+ Q2nd,
+ Qnew,
+ Qport,
+ Q3rd,
+ Qctl,
+ Qstatus,
+ Qep0,
+ /* other endpoint files */
+};
+
+/*
+ * Qid path is:
+ * 8 bits of file type (qids above)
+ * 8 bits of slot number; default address 0 used for per-controller files
+ * 4 bits of controller number
+ */
+enum {
+ TYPEBITS = 8,
+ SLOTBITS = 8,
+ CTLRBITS = 4,
+
+ SLOTSHIFT = TYPEBITS,
+ CTLRSHIFT = SLOTSHIFT+SLOTBITS,
+
+ TYPEMASK = (1<<TYPEBITS)-1,
+ SLOTMASK = (1<<SLOTBITS)-1,
+ CTLRMASK = (1<<CTLRBITS)-1,
+};
+
+#define TYPE(q) (((ulong)(q).path)&TYPEMASK)
+#define SLOT(q) ((((ulong)(q).path)>>SLOTSHIFT)&SLOTMASK)
+#define CTLR(q) ((((ulong)(q).path)>>CTLRSHIFT)&CTLRMASK)
+#define PATH(t, s, c) ((t)|((s)<<SLOTSHIFT)|((c)<<CTLRSHIFT))
+
+static Dirtab usbdir2[] = {
+ "new", {Qnew}, 0, 0666,
+ "port", {Qport}, 0, 0666,
+};
+
+static Dirtab usbdir3[]={
+ "ctl", {Qctl}, 0, 0666,
+ "status", {Qstatus}, 0, 0444,
+ "setup", {Qep0}, 0, 0666,
+ /* epNdata names are generated on demand */
+};
+
+enum
+{
+ PMdisable,
+ PMenable,
+ PMreset,
+};
+
+enum
+{
+ CMclass,
+ CMdata,
+ CMdebug,
+ CMep,
+ CMmaxpkt,
+ CMadjust,
+ CMspeed,
+ CMunstall,
+};
+
+static Cmdtab usbportmsg[] =
+{
+ PMdisable, "disable", 2,
+ PMenable, "enable", 2,
+ PMreset, "reset", 2,
+};
+
+static Cmdtab usbctlmsg[] =
+{
+ CMclass, "class", 0,
+ CMdata, "data", 3,
+ CMdebug, "debug", 3,
+ CMep, "ep", 6,
+ CMmaxpkt, "maxpkt", 3,
+ CMadjust, "adjust", 3,
+ CMspeed, "speed", 2,
+ CMunstall, "unstall", 2,
+};
+
+static struct
+{
+ char* type;
+ int (*reset)(Usbhost*);
+} usbtypes[MaxUsb+1];
+
+void
+addusbtype(char* t, int (*r)(Usbhost*))
+{
+ static int ntype;
+
+ if(ntype == MaxUsb)
+ panic("too many USB host interface types");
+ usbtypes[ntype].type = t;
+ usbtypes[ntype].reset = r;
+ ntype++;
+}
+
+static Udev*
+usbdeviceofslot(Usbhost *uh, int s)
+{
+ if(s < 0 || s > nelem(uh->dev))
+ return nil;
+ return uh->dev[s];
+}
+
+static Udev*
+usbdevice(Chan *c)
+{
+ int bus;
+ Udev *d;
+ Usbhost *uh;
+
+ bus = CTLR(c->qid);
+ if(bus > nelem(usbhost) || (uh = usbhost[bus]) == nil) {
+ error(Egreg);
+ return nil; /* for compiler */
+ }
+ d = usbdeviceofslot(uh, SLOT(c->qid));
+ if(d == nil || d->id != c->qid.vers || d->state == Disabled)
+ error(Ehungup);
+ return d;
+}
+
+static Endpt *
+devendpt(Udev *d, int id, int add)
+{
+ Usbhost *uh;
+ Endpt *e, **p;
+
+ p = &d->ep[id&0xF];
+ lock(d);
+ e = *p;
+ if(e != nil){
+ incref(e);
+ XPRINT("incref(0x%p) in devendpt, new value %ld\n", e, e->ref);
+ unlock(d);
+ return e;
+ }
+ unlock(d);
+ if(!add)
+ return nil;
+
+ e = mallocz(sizeof(*e), 1);
+ e->ref = 1;
+ e->x = id&0xF;
+ e->id = id;
+ e->sched = -1;
+ e->maxpkt = 8;
+ e->nbuf = 1;
+ e->dev = d;
+ e->active = 0;
+
+ uh = d->uh;
+ uh->epalloc(uh, e);
+
+ lock(d);
+ if(*p != nil){
+ incref(*p);
+ XPRINT("incref(0x%p) in devendpt, new value %ld\n", *p, (*p)->ref);
+ unlock(d);
+ uh->epfree(uh, e);
+ free(e);
+ return *p;
+ }
+ *p = e;
+ unlock(d);
+ e->rq = qopen(8*1024, 0, nil, e);
+ e->wq = qopen(8*1024, 0, nil, e);
+ return e;
+}
+
+static void
+freept(Endpt *e)
+{
+ Usbhost *uh;
+
+ if(e != nil && decref(e) == 0){
+ XPRINT("freept(%d,%d)\n", e->dev->x, e->x);
+ uh = e->dev->uh;
+ uh->epclose(uh, e);
+ e->dev->ep[e->x] = nil;
+ uh->epfree(uh, e);
+ free(e);
+ }
+}
+
+static Udev*
+usbnewdevice(Usbhost *uh)
+{
+ int i;
+ Udev *d;
+ Endpt *e;
+
+ d = nil;
+ qlock(uh);
+ if(waserror()){
+ qunlock(uh);
+ nexterror();
+ }
+ for(i=0; i<nelem(uh->dev); i++)
+ if(uh->dev[i] == nil){
+ uh->idgen++;
+ d = mallocz(sizeof(*d), 1);
+ d->uh = uh;
+ d->ref = 1;
+ d->x = i;
+ d->id = (uh->idgen << 8) | i;
+ d->state = Enabled;
+ XPRINT("calling devendpt in usbnewdevice\n");
+ e = devendpt(d, 0, 1); /* always provide control endpoint 0 */
+ e->mode = ORDWR;
+ e->iso = 0;
+ e->sched = -1;
+ uh->dev[i] = d;
+ break;
+ }
+ poperror();
+ qunlock(uh);
+ return d;
+}
+
+static void
+freedev(Udev *d, int ept)
+{
+ int i;
+ Endpt *e;
+ Usbhost *uh;
+
+ uh = d->uh;
+ if(decref(d) == 0){
+ XPRINT("freedev 0x%p, 0\n", d);
+ for(i=0; i<nelem(d->ep); i++)
+ freept(d->ep[i]);
+ if(d->x >= 0)
+ uh->dev[d->x] = nil;
+ free(d);
+ } else {
+ if(ept >= 0 && ept < nelem(d->ep)){
+ e = d->ep[ept];
+ XPRINT("freedev, freept 0x%p\n", e);
+ if(e != nil)
+ uh->epclose(uh, e);
+ }
+ }
+}
+
+static int
+usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
+{
+ Qid q;
+ Udev *d;
+ Endpt *e;
+ Dirtab *tab;
+ Usbhost *uh;
+ int t, bus, slot, perm;
+
+ /*
+ * Top level directory contains the controller names.
+ */
+ if(c->qid.path == Qtopdir){
+ if(s == DEVDOTDOT){
+ mkqid(&q, Qtopdir, 0, QTDIR);
+ devdir(c, q, "#U", 0, eve, 0555, dp);
+ return 1;
+ }
+ if(s >= nelem(usbhost) || usbhost[s] == nil)
+ return -1;
+ mkqid(&q, PATH(Q2nd, 0, s), 0, QTDIR);
+ snprint(up->genbuf, sizeof up->genbuf, "usb%d", s);
+ devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+ return 1;
+ }
+ bus = CTLR(c->qid);
+ if(bus >= nelem(usbhost) || (uh = usbhost[bus]) == nil)
+ return -1;
+
+ /*
+ * Second level contains "new", "port", and a numbered
+ * directory for each enumerated device on the bus.
+ */
+ t = TYPE(c->qid);
+ if(t < Q3rd){
+ if(s == DEVDOTDOT){
+ mkqid(&q, Qtopdir, 0, QTDIR);
+ devdir(c, q, "#U", 0, eve, 0555, dp);
+ return 1;
+ }
+ if(s < nelem(usbdir2)){
+ d = uh->dev[0];
+ if(d == nil)
+ return -1;
+ tab = &usbdir2[s];
+ mkqid(&q, PATH(tab->qid.path, 0, bus), d->id, QTFILE);
+ devdir(c, q, tab->name, tab->length, eve, tab->perm, dp);
+ return 1;
+ }
+ s -= nelem(usbdir2);
+ if(s >= 0 && s < nelem(uh->dev)) {
+ d = uh->dev[s];
+ if(d == nil)
+ return 0;
+ sprint(up->genbuf, "%d", s);
+ mkqid(&q, PATH(Q3rd, s, bus), d->id, QTDIR);
+ devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+ return 1;
+ }
+ return -1;
+ }
+
+ /*
+ * Third level.
+ */
+ slot = SLOT(c->qid);
+ if(s == DEVDOTDOT) {
+ mkqid(&q, PATH(Q2nd, 0, bus), c->qid.vers, QTDIR);
+ snprint(up->genbuf, sizeof up->genbuf, "usb%d", bus);
+ devdir(c, q, up->genbuf, 0, eve, 0555, dp);
+ return 1;
+ }
+ if(s < nelem(usbdir3)) {
+ tab = &usbdir3[s];
+ mkqid(&q, PATH(tab->qid.path, slot, bus), c->qid.vers, QTFILE);
+ devdir(c, q, tab->name, tab->length, eve, tab->perm, dp);
+ return 1;
+ }
+ s -= nelem(usbdir3);
+
+ /* active endpoints */
+ d = usbdeviceofslot(uh, slot);
+ if(d == nil || s >= nelem(d->ep))
+ return -1;
+ if(s == 0 || (e = d->ep[s]) == nil) /* ep0data is called "setup" */
+ return 0;
+ sprint(up->genbuf, "ep%ddata", s);
+ mkqid(&q, PATH(Qep0+s, slot, bus), c->qid.vers, QTFILE);
+ switch(e->mode) {
+ case OREAD:
+ perm = 0444;
+ break;
+ case OWRITE:
+ perm = 0222;
+ break;
+ default:
+ perm = 0666;
+ break;
+ }
+ devdir(c, q, up->genbuf, e->buffered, eve, perm, dp);
+ return 1;
+}
+
+static Usbhost*
+usbprobe(int cardno, int ctlrno)
+{
+ Usbhost *uh;
+ char buf[128], *ebuf, name[64], *p, *type;
+
+ if(cardno < 0)
+ return nil;
+ uh = malloc(sizeof(Usbhost));
+ memset(uh, 0, sizeof(Usbhost));
+ uh->tbdf = BUSUNKNOWN;
+
+ if(cardno >= MaxUsb || usbtypes[cardno].type == nil){
+ free(uh);
+ return nil;
+ }
+ if(usbtypes[cardno].reset(uh) < 0){
+ free(uh);
+ return nil;
+ }
+
+ snprint(name, sizeof(name), "usb%d", ctlrno);
+ intrenable(IRQ, uh->irq, uh->interrupt, uh, name);
+
+ ebuf = buf + sizeof buf;
+ p = seprint(buf, ebuf, "#U/usb%d: %s: port 0x%luX irq %ld", ctlrno, usbtypes[cardno].type, uh->port, uh->irq);
+ if(uh->mem)
+ p = seprint(p, ebuf, " addr 0x%luX", PADDR(uh->mem));
+ if(uh->size)
+ seprint(p, ebuf, " size 0x%luX", uh->size);
+ print("%s\n", buf);
+
+ return uh;
+}
+
+static void
+usbreset(void)
+{
+ int cardno, ctlrno;
+ Usbhost *uh;
+
+ for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){
+ if((uh = usbprobe(-1, ctlrno)) == nil)
+ continue;
+ usbhost[ctlrno] = uh;
+ }
+
+ if(getconf("*nousbprobe"))
+ return;
+
+ cardno = ctlrno = 0;
+ while(usbtypes[cardno].type != nil && ctlrno < MaxUsb){
+ if(usbhost[ctlrno] != nil){
+ ctlrno++;
+ continue;
+ }
+ if((uh = usbprobe(cardno, ctlrno)) == nil){
+ cardno++;
+ continue;
+ }
+ usbhost[ctlrno] = uh;
+ ctlrno++;
+ }
+}
+
+void
+usbinit(void)
+{
+ Udev *d;
+ int ctlrno;
+ Usbhost *uh;
+
+ for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){
+ uh = usbhost[ctlrno];
+ if(uh == nil)
+ continue;
+ if(uh->init != 0)
+ uh->init(uh);
+
+ /* reserve device for configuration */
+ d = usbnewdevice(uh);
+ incref(d);
+ d->state = Attached;
+ }
+}
+
+Chan *
+usbattach(char *spec)
+{
+ return devattach('U', spec);
+}
+
+static Walkqid*
+usbwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, nil, 0, usbgen);
+}
+
+static int
+usbstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, nil, 0, usbgen);
+}
+
+Chan*
+usbopen(Chan *c, int omode)
+{
+ Udev *d;
+ Endpt *e;
+ int f, s, type;
+ Usbhost *uh;
+
+ if(c->qid.type == QTDIR)
+ return devopen(c, omode, nil, 0, usbgen);
+
+ f = 0;
+ type = TYPE(c->qid);
+ if(type == Qnew){
+ d = usbdevice(c);
+ d = usbnewdevice(d->uh);
+ XPRINT("usbopen, new dev 0x%p\n", d);
+ if(d == nil) {
+ XPRINT("usbopen failed (usbnewdevice)\n");
+ error(Enodev);
+ }
+ type = Qctl;
+ mkqid(&c->qid, PATH(type, d->x, CTLR(c->qid)), d->id, QTFILE);
+ f = 1;
+ }
+
+ if(type < Q3rd){
+ XPRINT("usbopen, devopen < Q3rd\n");
+ return devopen(c, omode, nil, 0, usbgen);
+ }
+
+ d = usbdevice(c);
+ uh = d->uh;
+ qlock(uh);
+ if(waserror()){
+ qunlock(uh);
+ nexterror();
+ }
+
+ switch(type){
+ case Qctl:
+ if(0&&d->busy)
+ error(Einuse);
+ d->busy = 1;
+ if(!f)
+ incref(d);
+ XPRINT("usbopen, Qctl 0x%p\n", d);
+ break;
+
+ default:
+ s = type - Qep0;
+ XPRINT("usbopen, default 0x%p, %d\n", d, s);
+ if(s >= 0 && s < nelem(d->ep)){
+ if((e = d->ep[s]) == nil) {
+ XPRINT("usbopen failed (endpoint)\n");
+ error(Enodev);
+ }
+ XPRINT("usbopen: dev 0x%p, ept 0x%p\n", d, e);
+ uh->epopen(uh, e);
+ e->foffset = 0;
+ e->toffset = 0;
+ e->poffset = 0;
+ e->buffered = 0;
+ }
+ incref(d);
+ break;
+ }
+ poperror();
+ qunlock(uh);
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+}
+
+void
+usbclose(Chan *c)
+{
+ Udev *d;
+ int ept, type;
+ Usbhost *uh;
+
+ type = TYPE(c->qid);
+ if(c->qid.type == QTDIR || type < Q3rd)
+ return;
+ d = usbdevice(c);
+ uh = d->uh;
+ qlock(uh);
+ if(waserror()){
+ qunlock(uh);
+ nexterror();
+ }
+ if(type == Qctl)
+ d->busy = 0;
+ XPRINT("usbclose: dev 0x%p\n", d);
+ if(c->flag & COPEN){
+ ept = (type != Qctl) ? type - Qep0 : -1;
+ XPRINT("usbclose: freedev 0x%p\n", d);
+ freedev(d, ept);
+ }
+ poperror();
+ qunlock(uh);
+}
+
+static char *
+epstatus(char *s, char *se, Endpt *e, int i)
+{
+ char *p;
+
+ p = seprint(s, se, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks);
+ if(e->iso){
+ p = seprint(p, se, "bufsize %6d buffered %6d", e->maxpkt, e->buffered);
+ if(e->toffset)
+ p = seprint(p, se, " offset %10lud time %19lld\n", e->toffset, e->time);
+ p = seprint(p, se, "\n");
+ }
+ return p;
+}
+
+long
+usbread(Chan *c, void *a, long n, vlong offset)
+{
+ int t, i;
+ Udev *d;
+ Endpt *e;
+ Usbhost *uh;
+ char *s, *se, *p;
+
+ if(c->qid.type == QTDIR)
+ return devdirread(c, a, n, nil, 0, usbgen);
+
+ d = usbdevice(c);
+ uh = d->uh;
+ t = TYPE(c->qid);
+
+ if(t >= Qep0) {
+ t -= Qep0;
+ if(t >= nelem(d->ep))
+ error(Eio);
+ e = d->ep[t];
+ if(e == nil || e->mode == OWRITE)
+ error(Egreg);
+ if(t == 0) {
+ if(e->iso)
+ error(Egreg);
+ e->data01 = 1;
+ n = uh->read(uh, e, a, n, 0LL);
+ if(e->setin){
+ e->setin = 0;
+ e->data01 = 1;
+ uh->write(uh, e, "", 0, 0LL, TokOUT);
+ }
+ return n;
+ }
+ return uh->read(uh, e, a, n, offset);
+ }
+
+ s = smalloc(READSTR);
+ se = s+READSTR;
+ if(waserror()){
+ free(s);
+ nexterror();
+ }
+ switch(t){
+ case Qport:
+ uh->portinfo(uh, s, se);
+ break;
+
+ case Qctl:
+ seprint(s, se, "%11d %11d\n", d->x, d->id);
+ break;
+
+ case Qstatus:
+ if (d->did || d->vid)
+ p = seprint(s, se, "%s %#6.6lux %#4.4ux %#4.4ux\n", devstates[d->state], d->csp, d->vid, d->did);
+ else
+ p = seprint(s, se, "%s %#6.6lux\n", devstates[d->state], d->csp);
+ for(i=0; i<nelem(d->ep); i++) {
+ e = d->ep[i];
+ if(e == nil)
+ continue;
+ /* TO DO: freeze e */
+ p = epstatus(p, se, e, i);
+ }
+ }
+ n = readstr(offset, a, n, s);
+ poperror();
+ free(s);
+ return n;
+}
+
+long
+usbwrite(Chan *c, void *a, long n, vlong offset)
+{
+ Udev *d;
+ Endpt *e;
+ Cmdtab *ct;
+ Cmdbuf *cb;
+ Usbhost *uh;
+ int id, nw, t, i;
+ char cmd[50];
+
+ if(c->qid.type == QTDIR)
+ error(Egreg);
+ d = usbdevice(c);
+ uh = d->uh;
+ t = TYPE(c->qid);
+ switch(t){
+ case Qport:
+ cb = parsecmd(a, n);
+ if(waserror()){
+ free(cb);
+ nexterror();
+ }
+
+ ct = lookupcmd(cb, usbportmsg, nelem(usbportmsg));
+ id = strtol(cb->f[1], nil, 0);
+ switch(ct->index){
+ case PMdisable:
+ uh->portenable(uh, id, 0);
+ break;
+ case PMenable:
+ uh->portenable(uh, id, 1);
+ break;
+ case PMreset:
+ uh->portreset(uh, id);
+ break;
+ }
+
+ poperror();
+ free(cb);
+ return n;
+ case Qctl:
+ cb = parsecmd(a, n);
+ if(waserror()){
+ free(cb);
+ nexterror();
+ }
+
+ ct = lookupcmd(cb, usbctlmsg, nelem(usbctlmsg));
+ switch(ct->index){
+ case CMspeed:
+ d->ls = strtoul(cb->f[1], nil, 0) == 0;
+ break;
+ case CMclass:
+ if (cb->nf != 4 && cb->nf != 6)
+ cmderror(cb, Ebadusbmsg);
+ /* class #ifc ept csp ( == class subclass proto) [vendor product] */
+ d->npt = strtoul(cb->f[1], nil, 0); /* # of interfaces */
+ i = strtoul(cb->f[2], nil, 0); /* endpoint */
+ if (i < 0 || i >= nelem(d->ep)
+ || d->npt > nelem(d->ep) || i >= d->npt)
+ cmderror(cb, Ebadusbmsg);
+ if (cb->nf == 6) {
+ d->vid = strtoul(cb->f[4], nil, 0);
+ d->did = strtoul(cb->f[5], nil, 0);
+ }
+ if (i == 0)
+ d->csp = strtoul(cb->f[3], nil, 0);
+ if(d->ep[i] == nil){
+ XPRINT("calling devendpt in usbwrite (CMclass)\n");
+ d->ep[i] = devendpt(d, i, 1);
+ }
+ d->ep[i]->csp = strtoul(cb->f[3], nil, 0);
+ break;
+ case CMdata:
+ i = strtoul(cb->f[1], nil, 0);
+ if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
+ error(Ebadusbmsg);
+ e = d->ep[i];
+ e->data01 = strtoul(cb->f[2], nil, 0) != 0;
+ break;
+ case CMmaxpkt:
+ i = strtoul(cb->f[1], nil, 0);
+ if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
+ error(Ebadusbmsg);
+ e = d->ep[i];
+ e->maxpkt = strtoul(cb->f[2], nil, 0);
+ if(e->maxpkt > 1500)
+ e->maxpkt = 1500;
+ break;
+ case CMadjust:
+ i = strtoul(cb->f[1], nil, 0);
+ if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
+ error(Ebadusbmsg);
+ e = d->ep[i];
+ if (e->iso == 0)
+ error(Eperm);
+ i = strtoul(cb->f[2], nil, 0);
+ /* speed may not result in change of maxpkt */
+ if (i < (e->maxpkt-1)/e->samplesz * 1000/e->pollms
+ || i > e->maxpkt/e->samplesz * 1000/e->pollms){
+ snprint(cmd, sizeof(cmd), "%d < %d < %d?",
+ (e->maxpkt-1)/e->samplesz * 1000/e->pollms,
+ i,
+ e->maxpkt/e->samplesz * 1000/e->pollms);
+ error(cmd);
+ }
+ e->hz = i;
+ break;
+ case CMdebug:
+ i = strtoul(cb->f[1], nil, 0);
+ if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil)
+ error(Ebadusbmsg);
+ if (i == -1)
+ debug = 0;
+ else {
+ debug = 1;
+ e = d->ep[i];
+ e->debug = strtoul(cb->f[2], nil, 0);
+ }
+ break;
+ case CMunstall:
+ i = strtoul(cb->f[1], nil, 0);
+ if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
+ error(Ebadusbmsg);
+ e = d->ep[i];
+ e->err = nil;
+ break;
+ case CMep:
+ /* ep n `bulk' mode maxpkt nbuf OR
+ * ep n period mode samplesize Hz
+ */
+ i = strtoul(cb->f[1], nil, 0);
+ if(i < 0 || i >= nelem(d->ep)) {
+ XPRINT("field 1: 0 <= %d < %d\n", i, nelem(d->ep));
+ error(Ebadarg);
+ }
+ if((e = d->ep[i]) == nil){
+ XPRINT("calling devendpt in usbwrite (CMep)\n");
+ e = devendpt(d, i, 1);
+ }
+ qlock(uh);
+ if(waserror()){
+ freept(e);
+ qunlock(uh);
+ nexterror();
+ }
+ if(e->active)
+ error(Eperm);
+ if(strcmp(cb->f[2], "bulk") == 0){
+ /* ep n `bulk' mode maxpkt nbuf */
+ e->iso = 0;
+ i = strtoul(cb->f[4], nil, 0);
+ if(i < 8 || i > 1023)
+ i = 8;
+ e->maxpkt = i;
+ i = strtoul(cb->f[5], nil, 0);
+ if(i >= 1 && i <= 32)
+ e->nbuf = i;
+ } else {
+ /* ep n period mode samplesize Hz */
+ i = strtoul(cb->f[2], nil, 0);
+ if(i > 0 && i <= 1000){
+ e->pollms = i;
+ }else {
+ XPRINT("field 4: 0 <= %d <= 1000\n", i);
+ error(Ebadarg);
+ }
+ i = strtoul(cb->f[4], nil, 0);
+ if(i >= 1 && i <= 8){
+ e->samplesz = i;
+ }else {
+ XPRINT("field 4: 0 < %d <= 8\n", i);
+ error(Ebadarg);
+ }
+ i = strtoul(cb->f[5], nil, 0);
+ if(i >= 1 && i*e->samplesz <= 12*1000*1000){
+ /* Hz */
+ e->hz = i;
+ e->remain = 0;
+ }else {
+ XPRINT("field 5: 1 < %d <= 100000 Hz\n", i);
+ error(Ebadarg);
+ }
+ e->maxpkt = (e->hz * e->pollms + 999)/1000 * e->samplesz;
+ e->iso = 1;
+ }
+ e->mode = strcmp(cb->f[3],"r") == 0? OREAD :
+ strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR;
+ uh->epmode(uh, e);
+ poperror();
+ qunlock(uh);
+ }
+
+ poperror();
+ free(cb);
+ return n;
+
+ case Qep0: /* SETUP endpoint 0 */
+ /* should canqlock etc */
+ e = d->ep[0];
+ if(e == nil || e->iso)
+ error(Egreg);
+ if(n < 8)
+ error(Eio);
+ nw = *(uchar*)a & RD2H;
+ e->data01 = 0;
+ n = uh->write(uh, e, a, n, 0LL, TokSETUP);
+ if(nw == 0) { /* host to device: use IN[DATA1] to ack */
+ e->data01 = 1;
+ nw = uh->read(uh, e, cmd, 0LL, 8);
+ if(nw != 0)
+ error(Eio); /* could provide more status */
+ }else
+ e->setin = 1; /* two-phase */
+ break;
+
+ default: /* sends DATA[01] */
+ t -= Qep0;
+ if(t < 0 || t >= nelem(d->ep))
+ error(Egreg);
+ e = d->ep[t];
+ if(e == nil || e->mode == OREAD)
+ error(Egreg);
+ n = uh->write(uh, e, a, n, offset, TokOUT);
+ break;
+ }
+ return n;
+}
+
+Dev usbdevtab = {
+ 'U',
+ "usb",
+
+ usbreset,
+ usbinit,
+ devshutdown,
+ usbattach,
+ usbwalk,
+ usbstat,
+ usbopen,
+ devcreate,
+ usbclose,
+ usbread,
+ devbread,
+ usbwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
diff --git a/os/manga/eswnotes b/os/manga/eswnotes
new file mode 100644
index 00000000..4a4b892a
--- /dev/null
+++ b/os/manga/eswnotes
@@ -0,0 +1,41 @@
+- switch level
+ - VlanEn vlan enable
+ - disable tx/rx flow control
+ - buffer sharing control
+ - unicast port-VLAN mismatch discard
+ - fair flow control on/off
+ - priority buffer reserved
+ - high prio first, 10:1, 5:1 2:1
+ - tag mask enabled
+ - enable/disable switch?
+ - TPID mode for direct forwarding from port 5
+ - replace null VID with default port VID
+ - 802.1p base priority
+
+- port level
+ - auto negotiation
+ - spanning tree tx/rx/learn on/off
+ - priority classification on/off
+ - diffserve priority classification on/off
+ - 802.1p classification on/off
+ - some of those are possibly mutually exclusive
+ - priority function enabled
+ - default tag: userprio (3 bits), CFI (mbz), 12-bit VID
+ - VLAN related
+ - ingress filter (discard packets from port not in VLAN)
+ - discard non pvid (discard tagged packets not matching port's default VID)
+ - receive rate control
+ - high priority 8-bits, low priority 8-bits
+ - enable rate and/or flow control, high and low priority, tx/rx
+
+- FID management
+
+- static MAC management
+- VLAN table (16 entries)
+ - port membership
+ - FID
+ - 802.1Q 12-bit VID
+
+- stats
+
+- 8100 or 810x
diff --git a/os/manga/ether8139.c b/os/manga/ether8139.c
new file mode 100644
index 00000000..d64e0b20
--- /dev/null
+++ b/os/manga/ether8139.c
@@ -0,0 +1,744 @@
+/*
+ * Realtek 8139 (but not the 8129).
+ * Error recovery for the various over/under -flow conditions
+ * may need work.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+enum { /* registers */
+ Idr0 = 0x0000, /* MAC address */
+ Mar0 = 0x0008, /* Multicast address */
+ Tsd0 = 0x0010, /* Transmit Status Descriptor0 */
+ Tsad0 = 0x0020, /* Transmit Start Address Descriptor0 */
+ Rbstart = 0x0030, /* Receive Buffer Start Address */
+ Erbcr = 0x0034, /* Early Receive Byte Count */
+ Ersr = 0x0036, /* Early Receive Status */
+ Cr = 0x0037, /* Command Register */
+ Capr = 0x0038, /* Current Address of Packet Read */
+ Cbr = 0x003A, /* Current Buffer Address */
+ Imr = 0x003C, /* Interrupt Mask */
+ Isr = 0x003E, /* Interrupt Status */
+ Tcr = 0x0040, /* Transmit Configuration */
+ Rcr = 0x0044, /* Receive Configuration */
+ Tctr = 0x0048, /* Timer Count */
+ Mpc = 0x004C, /* Missed Packet Counter */
+ Cr9346 = 0x0050, /* 9346 Command Register */
+ Config0 = 0x0051, /* Configuration Register 0 */
+ Config1 = 0x0052, /* Configuration Register 1 */
+ TimerInt = 0x0054, /* Timer Interrupt */
+ Msr = 0x0058, /* Media Status */
+ Config3 = 0x0059, /* Configuration Register 3 */
+ Config4 = 0x005A, /* Configuration Register 4 */
+ Mulint = 0x005C, /* Multiple Interrupt Select */
+ RerID = 0x005E, /* PCI Revision ID */
+ Tsad = 0x0060, /* Transmit Status of all Descriptors */
+
+ Bmcr = 0x0062, /* Basic Mode Control */
+ Bmsr = 0x0064, /* Basic Mode Status */
+ Anar = 0x0066, /* Auto-Negotiation Advertisment */
+ Anlpar = 0x0068, /* Auto-Negotiation Link Partner */
+ Aner = 0x006A, /* Auto-Negotiation Expansion */
+ Dis = 0x006C, /* Disconnect Counter */
+ Fcsc = 0x006E, /* False Carrier Sense Counter */
+ Nwaytr = 0x0070, /* N-way Test */
+ Rec = 0x0072, /* RX_ER Counter */
+ Cscr = 0x0074, /* CS Configuration */
+ Phy1parm = 0x0078, /* PHY Parameter 1 */
+ Twparm = 0x007C, /* Twister Parameter */
+ Phy2parm = 0x0080, /* PHY Parameter 2 */
+};
+
+enum { /* Cr */
+ Bufe = 0x01, /* Rx Buffer Empty */
+ Te = 0x04, /* Transmitter Enable */
+ Re = 0x08, /* Receiver Enable */
+ Rst = 0x10, /* Software Reset */
+};
+
+enum { /* Imr/Isr */
+ Rok = 0x0001, /* Receive OK */
+ Rer = 0x0002, /* Receive Error */
+ Tok = 0x0004, /* Transmit OK */
+ Ter = 0x0008, /* Transmit Error */
+ Rxovw = 0x0010, /* Receive Buffer Overflow */
+ PunLc = 0x0020, /* Packet Underrun or Link Change */
+ Fovw = 0x0040, /* Receive FIFO Overflow */
+ Clc = 0x2000, /* Cable Length Change */
+ Timerbit = 0x4000, /* Timer */
+ Serr = 0x8000, /* System Error */
+};
+
+enum { /* Tcr */
+ Clrabt = 0x00000001, /* Clear Abort */
+ TxrrSHIFT = 4, /* Transmit Retry Count */
+ TxrrMASK = 0x000000F0,
+ MtxdmaSHIFT = 8, /* Max. DMA Burst Size */
+ MtxdmaMASK = 0x00000700,
+ Mtxdma2048 = 0x00000700,
+ Acrc = 0x00010000, /* Append CRC (not) */
+ LbkSHIFT = 17, /* Loopback Test */
+ LbkMASK = 0x00060000,
+ Rtl8139ArevG = 0x00800000, /* RTL8139A Rev. G ID */
+ IfgSHIFT = 24, /* Interframe Gap */
+ IfgMASK = 0x03000000,
+ HwveridSHIFT = 26, /* Hardware Version ID */
+ HwveridMASK = 0x7C000000,
+};
+
+enum { /* Rcr */
+ Aap = 0x00000001, /* Accept All Packets */
+ Apm = 0x00000002, /* Accept Physical Match */
+ Am = 0x00000004, /* Accept Multicast */
+ Ab = 0x00000008, /* Accept Broadcast */
+ Ar = 0x00000010, /* Accept Runt */
+ Aer = 0x00000020, /* Accept Error */
+ Sel9356 = 0x00000040, /* 9356 EEPROM used */
+ Wrap = 0x00000080, /* Rx Buffer Wrap Control */
+ MrxdmaSHIFT = 8, /* Max. DMA Burst Size */
+ MrxdmaMASK = 0x00000700,
+ Mrxdmaunlimited = 0x00000700,
+ RblenSHIFT = 11, /* Receive Buffer Length */
+ RblenMASK = 0x00001800,
+ Rblen8K = 0x00000000, /* 8KB+16 */
+ Rblen16K = 0x00000800, /* 16KB+16 */
+ Rblen32K = 0x00001000, /* 32KB+16 */
+ Rblen64K = 0x00001800, /* 64KB+16 */
+ RxfthSHIFT = 13, /* Receive Buffer Length */
+ RxfthMASK = 0x0000E000,
+ Rxfth256 = 0x00008000,
+ Rxfthnone = 0x0000E000,
+ Rer8 = 0x00010000, /* Accept Error Packets > 8 bytes */
+ MulERINT = 0x00020000, /* Multiple Early Interrupt Select */
+ ErxthSHIFT = 24, /* Early Rx Threshold */
+ ErxthMASK = 0x0F000000,
+ Erxthnone = 0x00000000,
+};
+
+enum { /* Received Packet Status */
+ Rcok = 0x0001, /* Receive Completed OK */
+ Fae = 0x0002, /* Frame Alignment Error */
+ Crc = 0x0004, /* CRC Error */
+ Long = 0x0008, /* Long Packet */
+ Runt = 0x0010, /* Runt Packet Received */
+ Ise = 0x0020, /* Invalid Symbol Error */
+ Bar = 0x2000, /* Broadcast Address Received */
+ Pam = 0x4000, /* Physical Address Matched */
+ Mar = 0x8000, /* Multicast Address Received */
+};
+
+enum { /* Media Status Register */
+ Rxpf = 0x01, /* Pause Flag */
+ Txpf = 0x02, /* Pause Flag */
+ Linkb = 0x04, /* Inverse of Link Status */
+ Speed10 = 0x08, /* 10Mbps */
+ Auxstatus = 0x10, /* Aux. Power Present Status */
+ Rxfce = 0x40, /* Receive Flow Control Enable */
+ Txfce = 0x80, /* Transmit Flow Control Enable */
+};
+
+typedef struct Td Td;
+struct Td { /* Soft Transmit Descriptor */
+ int tsd;
+ int tsad;
+ uchar* data;
+ Block* bp;
+};
+
+enum { /* Tsd0 */
+ SizeSHIFT = 0, /* Descriptor Size */
+ SizeMASK = 0x00001FFF,
+ Own = 0x00002000,
+ Tun = 0x00004000, /* Transmit FIFO Underrun */
+ Tcok = 0x00008000, /* Transmit COmpleted OK */
+ EtxthSHIFT = 16, /* Early Tx Threshold */
+ EtxthMASK = 0x001F0000,
+ NccSHIFT = 24, /* Number of Collisions Count */
+ NccMASK = 0x0F000000,
+ Cdh = 0x10000000, /* CD Heartbeat */
+ Owc = 0x20000000, /* Out of Window Collision */
+ Tabt = 0x40000000, /* Transmit Abort */
+ Crs = 0x80000000, /* Carrier Sense Lost */
+};
+
+enum {
+ Rblen = Rblen64K, /* Receive Buffer Length */
+ Ntd = 4, /* Number of Transmit Descriptors */
+ Tdbsz = ROUNDUP(sizeof(Etherpkt), 4),
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+ int port;
+ Pcidev* pcidev;
+ Ctlr* next;
+ int active;
+ int id;
+
+ QLock alock; /* attach */
+ Lock ilock; /* init */
+ void* alloc; /* base of per-Ctlr allocated data */
+
+ int rcr; /* receive configuration register */
+ uchar* rbstart; /* receive buffer */
+ int rblen; /* receive buffer length */
+ int ierrs; /* receive errors */
+
+ Lock tlock; /* transmit */
+ Td td[Ntd];
+ int ntd; /* descriptors active */
+ int tdh; /* host index into td */
+ int tdi; /* interface index into td */
+ int etxth; /* early transmit threshold */
+ int taligned; /* packet required no alignment */
+ int tunaligned; /* packet required alignment */
+
+ int dis; /* disconnect counter */
+ int fcsc; /* false carrier sense counter */
+ int rec; /* RX_ER counter */
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+#define csr8r(c, r) (inb((c)->port+(r)))
+#define csr16r(c, r) (ins((c)->port+(r)))
+#define csr32r(c, r) (inl((c)->port+(r)))
+#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b)))
+#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w)))
+#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l)))
+
+static void
+rtl8139promiscuous(void* arg, int on)
+{
+ Ether *edev;
+ Ctlr * ctlr;
+
+ edev = arg;
+ ctlr = edev->ctlr;
+ ilock(&ctlr->ilock);
+
+ if(on)
+ ctlr->rcr |= Aap;
+ else
+ ctlr->rcr &= ~Aap;
+ csr32w(ctlr, Rcr, ctlr->rcr);
+ iunlock(&ctlr->ilock);
+}
+
+static long
+rtl8139ifstat(Ether* edev, void* a, long n, ulong offset)
+{
+ int l;
+ char *p;
+ Ctlr *ctlr;
+
+ ctlr = edev->ctlr;
+ p = malloc(READSTR);
+ l = snprint(p, READSTR, "rcr %8.8uX\n", ctlr->rcr);
+ l += snprint(p+l, READSTR-l, "ierrs %d\n", ctlr->ierrs);
+ l += snprint(p+l, READSTR-l, "etxth %d\n", ctlr->etxth);
+ l += snprint(p+l, READSTR-l, "taligned %d\n", ctlr->taligned);
+ l += snprint(p+l, READSTR-l, "tunaligned %d\n", ctlr->tunaligned);
+ ctlr->dis += csr16r(ctlr, Dis);
+ l += snprint(p+l, READSTR-l, "dis %d\n", ctlr->dis);
+ ctlr->fcsc += csr16r(ctlr, Fcsc);
+ l += snprint(p+l, READSTR-l, "fcscnt %d\n", ctlr->fcsc);
+ ctlr->rec += csr16r(ctlr, Rec);
+ l += snprint(p+l, READSTR-l, "rec %d\n", ctlr->rec);
+
+ l += snprint(p+l, READSTR-l, "Tcr %8.8luX\n", csr32r(ctlr, Tcr));
+ l += snprint(p+l, READSTR-l, "Config0 %2.2uX\n", csr8r(ctlr, Config0));
+ l += snprint(p+l, READSTR-l, "Config1 %2.2uX\n", csr8r(ctlr, Config1));
+ l += snprint(p+l, READSTR-l, "Msr %2.2uX\n", csr8r(ctlr, Msr));
+ l += snprint(p+l, READSTR-l, "Config3 %2.2uX\n", csr8r(ctlr, Config3));
+ l += snprint(p+l, READSTR-l, "Config4 %2.2uX\n", csr8r(ctlr, Config4));
+
+ l += snprint(p+l, READSTR-l, "Bmcr %4.4uX\n", csr16r(ctlr, Bmcr));
+ l += snprint(p+l, READSTR-l, "Bmsr %4.4uX\n", csr16r(ctlr, Bmsr));
+ l += snprint(p+l, READSTR-l, "Anar %4.4uX\n", csr16r(ctlr, Anar));
+ l += snprint(p+l, READSTR-l, "Anlpar %4.4uX\n", csr16r(ctlr, Anlpar));
+ l += snprint(p+l, READSTR-l, "Aner %4.4uX\n", csr16r(ctlr, Aner));
+ l += snprint(p+l, READSTR-l, "Nwaytr %4.4uX\n", csr16r(ctlr, Nwaytr));
+ snprint(p+l, READSTR-l, "Cscr %4.4uX\n", csr16r(ctlr, Cscr));
+ n = readstr(offset, a, n, p);
+ free(p);
+
+ return n;
+}
+
+static int
+rtl8139reset(Ctlr* ctlr)
+{
+ int timeo;
+
+ /*
+ * Soft reset the controller.
+ */
+ csr8w(ctlr, Cr, Rst);
+ for(timeo = 0; timeo < 1000; timeo++){
+ if(!(csr8r(ctlr, Cr) & Rst))
+ return 0;
+ delay(1);
+ }
+
+ return -1;
+}
+
+static void
+rtl8139halt(Ctlr* ctlr)
+{
+ int i;
+
+ csr8w(ctlr, Cr, 0);
+ csr16w(ctlr, Imr, 0);
+ csr16w(ctlr, Isr, ~0);
+
+ for(i = 0; i < Ntd; i++){
+ if(ctlr->td[i].bp == nil)
+ continue;
+ freeb(ctlr->td[i].bp);
+ ctlr->td[i].bp = nil;
+ }
+}
+
+static void
+rtl8139init(Ether* edev)
+{
+ int i;
+ ulong r;
+ Ctlr *ctlr;
+ uchar *alloc;
+
+ ctlr = edev->ctlr;
+ ilock(&ctlr->ilock);
+
+ rtl8139halt(ctlr);
+
+ /*
+ * MAC Address.
+ */
+ r = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0];
+ csr32w(ctlr, Idr0, r);
+ r = (edev->ea[5]<<8)|edev->ea[4];
+ csr32w(ctlr, Idr0+4, r);
+
+ /*
+ * Receiver
+ */
+ alloc = mmucacheinhib((char*)ROUNDUP((ulong)ctlr->alloc, CACHELINESZ), ctlr->rblen+16 + Ntd*Tdbsz);
+ ctlr->rbstart = alloc;
+ alloc += ctlr->rblen+16;
+ memset(ctlr->rbstart, 0, ctlr->rblen+16);
+ csr32w(ctlr, Rbstart, PCIWADDR(ctlr->rbstart));
+ ctlr->rcr = Rxfth256|Rblen|Mrxdmaunlimited|Ab|Apm;
+
+ /*
+ * Transmitter.
+ */
+ for(i = 0; i < Ntd; i++){
+ ctlr->td[i].tsd = Tsd0+i*4;
+ ctlr->td[i].tsad = Tsad0+i*4;
+ ctlr->td[i].data = alloc;
+ alloc += Tdbsz;
+ ctlr->td[i].bp = nil;
+ }
+ ctlr->ntd = ctlr->tdh = ctlr->tdi = 0;
+ ctlr->etxth = 128/32;
+
+ /*
+ * Interrupts.
+ */
+ csr32w(ctlr, TimerInt, 0);
+ csr16w(ctlr, Imr, Serr|Timerbit|Fovw|PunLc|Rxovw|Ter|Tok|Rer|Rok);
+ csr32w(ctlr, Mpc, 0);
+
+ /*
+ * Enable receiver/transmitter.
+ * Need to enable before writing the Rcr or it won't take.
+ */
+ csr8w(ctlr, Cr, Te|Re);
+ csr32w(ctlr, Tcr, Mtxdma2048);
+ csr32w(ctlr, Rcr, ctlr->rcr);
+
+ iunlock(&ctlr->ilock);
+}
+
+static void
+rtl8139attach(Ether* edev)
+{
+ Ctlr *ctlr;
+
+ ctlr = edev->ctlr;
+ qlock(&ctlr->alock);
+ if(ctlr->alloc == nil){
+ ctlr->rblen = 1<<((Rblen>>RblenSHIFT)+13);
+ ctlr->alloc = mallocz(ctlr->rblen+16 + Ntd*Tdbsz + CACHELINESZ, 0);
+ rtl8139init(edev);
+ }
+ qunlock(&ctlr->alock);
+}
+
+static void
+rtl8139txstart(Ether* edev)
+{
+ Td *td;
+ int size;
+ Block *bp;
+ Ctlr *ctlr;
+
+ ctlr = edev->ctlr;
+ while(ctlr->ntd < Ntd){
+ bp = qget(edev->oq);
+ if(bp == nil)
+ break;
+ size = BLEN(bp);
+
+ td = &ctlr->td[ctlr->tdh];
+ if(((int)bp->rp) & 0x03){
+ memmove(td->data, bp->rp, size);
+ dcflush(td->data, size);
+ freeb(bp);
+ csr32w(ctlr, td->tsad, PCIWADDR(td->data));
+ ctlr->tunaligned++;
+ }
+ else{
+ td->bp = bp;
+ csr32w(ctlr, td->tsad, PCIWADDR(bp->rp));
+ dcflush(bp->rp, size);
+ ctlr->taligned++;
+ }
+ csr32w(ctlr, td->tsd, (ctlr->etxth<<EtxthSHIFT)|size);
+
+ ctlr->ntd++;
+ ctlr->tdh = NEXT(ctlr->tdh, Ntd);
+ }
+}
+
+static void
+rtl8139transmit(Ether* edev)
+{
+ Ctlr *ctlr;
+
+ ctlr = edev->ctlr;
+ ilock(&ctlr->tlock);
+ rtl8139txstart(edev);
+ iunlock(&ctlr->tlock);
+}
+
+static void
+rtl8139receive(Ether* edev)
+{
+ Block *bp;
+ Ctlr *ctlr;
+ ushort capr;
+ uchar cr, *p;
+ int l, length, status;
+
+ ctlr = edev->ctlr;
+
+ /*
+ * Capr is where the host is reading from,
+ * Cbr is where the NIC is currently writing.
+ */
+ capr = (csr16r(ctlr, Capr)+16) % ctlr->rblen;
+ while(!(csr8r(ctlr, Cr) & Bufe)){
+ p = ctlr->rbstart+capr;
+
+ /*
+ * Apparently the packet length may be 0xFFF0 if
+ * the NIC is still copying the packet into memory.
+ */
+ length = (*(p+3)<<8)|*(p+2);
+ if(length == 0xFFF0)
+ break;
+ status = (*(p+1)<<8)|*p;
+ if(!(status & Rcok)){
+ if(status & (Ise|Fae))
+ edev->frames++;
+ if(status & Crc)
+ edev->crcs++;
+ if(status & (Runt|Long))
+ edev->buffs++;
+
+ /*
+ * Reset the receiver.
+ * Also may have to restore the multicast list
+ * here too if it ever gets used.
+ */
+ cr = csr8r(ctlr, Cr);
+ csr8w(ctlr, Cr, cr & ~Re);
+ csr32w(ctlr, Rbstart, PCIWADDR(ctlr->rbstart));
+ csr8w(ctlr, Cr, cr);
+ csr32w(ctlr, Rcr, ctlr->rcr);
+
+ continue;
+ }
+
+ /*
+ * Receive Completed OK.
+ * Very simplistic; there are ways this could be done
+ * without copying, but the juice probably isn't worth
+ * the squeeze.
+ * The packet length includes a 4 byte CRC on the end.
+ */
+ capr = (capr+4) % ctlr->rblen;
+ p = ctlr->rbstart+capr;
+ capr = (capr+length) % ctlr->rblen;
+
+ if((bp = iallocb(length)) != nil){
+ if(p+length >= ctlr->rbstart+ctlr->rblen){
+ l = ctlr->rbstart+ctlr->rblen - p;
+ memmove(bp->wp, p, l);
+ bp->wp += l;
+ length -= l;
+ p = ctlr->rbstart;
+ }
+ if(length > 0){
+ memmove(bp->wp, p, length);
+ bp->wp += length;
+ }
+ bp->wp -= 4;
+ etheriq(edev, bp, 1);
+ }
+
+ capr = ROUNDUP(capr, 4);
+ csr16w(ctlr, Capr, capr-16);
+ }
+}
+
+static void
+rtl8139interrupt(Ureg*, void* arg)
+{
+ Td *td;
+ Ctlr *ctlr;
+ Ether *edev;
+ int isr, msr, tsd;
+
+ edev = arg;
+ ctlr = edev->ctlr;
+
+ while((isr = csr16r(ctlr, Isr)) != 0){
+ csr16w(ctlr, Isr, isr);
+ if(isr & (Fovw|PunLc|Rxovw|Rer|Rok)){
+ rtl8139receive(edev);
+ if(!(isr & Rok))
+ ctlr->ierrs++;
+ isr &= ~(Fovw|Rxovw|Rer|Rok);
+ }
+
+ if(isr & (Ter|Tok)){
+ ilock(&ctlr->tlock);
+ while(ctlr->ntd){
+ td = &ctlr->td[ctlr->tdi];
+ tsd = csr32r(ctlr, td->tsd);
+ if(!(tsd & (Tabt|Tun|Tcok)))
+ break;
+
+ if(!(tsd & Tcok)){
+ if(tsd & Tun){
+ if(ctlr->etxth < ETHERMAXTU/32)
+ ctlr->etxth++;
+ }
+ edev->oerrs++;
+ }
+
+ if(td->bp != nil){
+ freeb(td->bp);
+ td->bp = nil;
+ }
+
+ ctlr->ntd--;
+ ctlr->tdi = NEXT(ctlr->tdi, Ntd);
+ }
+ rtl8139txstart(edev);
+ iunlock(&ctlr->tlock);
+ isr &= ~(Ter|Tok);
+ }
+
+ if(isr & PunLc){
+ /*
+ * Maybe the link changed - do we care very much?
+ */
+ msr = csr8r(ctlr, Msr);
+ if(!(msr & Linkb)){
+ if(!(msr & Speed10) && edev->mbps != 100){
+ edev->mbps = 100;
+ qsetlimit(edev->oq, 256*1024);
+ }
+ else if((msr & Speed10) && edev->mbps != 10){
+ edev->mbps = 10;
+ qsetlimit(edev->oq, 65*1024);
+ }
+ }
+ isr &= ~(Clc|PunLc);
+ }
+
+ /*
+ * Only Serr|Timer should be left by now.
+ * Should anything be done to tidy up? TimerInt isn't
+ * used so that can be cleared. A PCI bus error is indicated
+ * by Serr, that's pretty serious; is there anyhing to do
+ * other than try to reinitialise the chip?
+ */
+ if(isr != 0){
+ iprint("rtl8139interrupt: imr %4.4uX isr %4.4uX\n",
+ csr16r(ctlr, Imr), isr);
+ if(isr & Timerbit)
+ csr32w(ctlr, TimerInt, 0);
+ if(isr & Serr)
+ rtl8139init(edev);
+ }
+ }
+}
+
+static Ctlr*
+rtl8139match(Ether* edev, int id)
+{
+ int port;
+ Pcidev *p;
+ Ctlr *ctlr;
+
+ /*
+ * Any adapter matches if no edev->port is supplied,
+ * otherwise the ports must match.
+ */
+ for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+ if(ctlr->active)
+ continue;
+ p = ctlr->pcidev;
+ if(((p->did<<16)|p->vid) != id)
+ continue;
+ port = p->mem[0].bar & ~0x01;
+ if(edev->port != 0 && edev->port != port)
+ continue;
+
+ if(ioalloc(port, p->mem[0].size, 0, "rtl8139") < 0){
+ print("rtl8139: port 0x%uX in use\n", port);
+ continue;
+ }
+
+ ctlr->port = port;
+ if(rtl8139reset(ctlr))
+ continue;
+ pcisetbme(p);
+
+ ctlr->active = 1;
+ return ctlr;
+ }
+ return nil;
+}
+
+static struct {
+ char* name;
+ int id;
+} rtl8139pci[] = {
+ { "rtl8139", (0x8139<<16)|0x10EC, }, /* generic */
+ { "smc1211", (0x1211<<16)|0x1113, }, /* SMC EZ-Card */
+ { "dfe-538tx", (0x1300<<16)|0x1186, }, /* D-Link DFE-538TX */
+ { "dfe-560txd", (0x1340<<16)|0x1186, }, /* D-Link DFE-560TXD */
+ { nil },
+};
+
+static int
+rtl8139pnp(Ether* edev)
+{
+ int i, id;
+ Pcidev *p;
+ Ctlr *ctlr;
+ uchar ea[Eaddrlen];
+
+ /*
+ * Make a list of all ethernet controllers
+ * if not already done.
+ */
+ if(ctlrhead == nil){
+ p = nil;
+ while(p = pcimatch(p, 0, 0)){
+ if(p->ccrb != 0x02 || p->ccru != 0)
+ continue;
+ ctlr = malloc(sizeof(Ctlr));
+ ctlr->pcidev = p;
+ ctlr->id = (p->did<<16)|p->vid;
+
+ if(ctlrhead != nil)
+ ctlrtail->next = ctlr;
+ else
+ ctlrhead = ctlr;
+ ctlrtail = ctlr;
+ }
+ }
+
+ /*
+ * Is it an RTL8139 under a different name?
+ * Normally a search is made through all the found controllers
+ * for one which matches any of the known vid+did pairs.
+ * If a vid+did pair is specified a search is made for that
+ * specific controller only.
+ */
+ id = 0;
+ for(i = 0; i < edev->nopt; i++){
+ if(cistrncmp(edev->opt[i], "id=", 3) == 0)
+ id = strtol(&edev->opt[i][3], nil, 0);
+ }
+
+ ctlr = nil;
+ if(id != 0)
+ ctlr = rtl8139match(edev, id);
+ else for(i = 0; rtl8139pci[i].name; i++){
+ if((ctlr = rtl8139match(edev, rtl8139pci[i].id)) != nil)
+ break;
+ }
+ if(ctlr == nil)
+ return -1;
+
+ edev->ctlr = ctlr;
+ edev->port = ctlr->port;
+ edev->irq = ctlr->pcidev->intl;
+ edev->tbdf = ctlr->pcidev->tbdf;
+
+ /*
+ * Check if the adapter's station address is to be overridden.
+ * If not, read it from the device and set in edev->ea.
+ */
+ memset(ea, 0, Eaddrlen);
+ if(memcmp(ea, edev->ea, Eaddrlen) == 0){
+ i = csr32r(ctlr, Idr0);
+ edev->ea[0] = i;
+ edev->ea[1] = i>>8;
+ edev->ea[2] = i>>16;
+ edev->ea[3] = i>>24;
+ i = csr32r(ctlr, Idr0+4);
+ edev->ea[4] = i;
+ edev->ea[5] = i>>8;
+ }
+ edev->attach = rtl8139attach;
+ edev->transmit = rtl8139transmit;
+ edev->interrupt = rtl8139interrupt;
+ edev->ifstat = rtl8139ifstat;
+
+ edev->arg = edev;
+ edev->promiscuous = rtl8139promiscuous;
+
+ /*
+ * This should be much more dynamic but will do for now.
+ */
+ if((csr8r(ctlr, Msr) & (Speed10|Linkb)) == 0)
+ edev->mbps = 100;
+
+ return 0;
+}
+
+void
+ether8139link(void)
+{
+ addethercard("rtl8139", rtl8139pnp);
+}
diff --git a/os/manga/etherif.h b/os/manga/etherif.h
new file mode 100644
index 00000000..61be9ea0
--- /dev/null
+++ b/os/manga/etherif.h
@@ -0,0 +1,44 @@
+enum {
+ MaxEther = 4,
+ MaxFID= 16,
+ Ntypes = 8,
+};
+
+typedef struct Ether Ether;
+
+struct Ether {
+RWlock; /* TO DO */
+ ISAConf; /* hardware info */
+ int ctlrno;
+ int tbdf; /* type+busno+devno+funcno */
+ int minmtu;
+ int maxmtu;
+ uchar ea[Eaddrlen];
+ int encry;
+
+ void (*attach)(Ether*); /* filled in by reset routine */
+ void (*closed)(Ether*);
+ void (*detach)(Ether*);
+ void (*transmit)(Ether*);
+ void (*interrupt)(Ureg*, void*);
+ long (*ifstat)(Ether*, void*, long, ulong);
+ long (*ctl)(Ether*, void*, long); /* custom ctl messages */
+ void (*power)(Ether*, int); /* power on/off */
+ void (*shutdown)(Ether*); /* shutdown hardware before reboot */
+ void *ctlr;
+ int pcmslot; /* PCMCIA */
+ int fullduplex; /* non-zero if full duplex */
+ int vlanid; /* non-zero if vlan */
+
+ Queue* oq;
+
+ QLock vlq; /* array change */
+ int nvlan;
+ Ether* vlans[MaxFID];
+
+ Netif;
+};
+
+extern Block* etheriq(Ether*, Block*, int);
+extern void addethercard(char*, int(*)(Ether*));
+extern int archether(int, Ether*);
diff --git a/os/manga/etherks8695.c b/os/manga/etherks8695.c
new file mode 100644
index 00000000..06e7de9e
--- /dev/null
+++ b/os/manga/etherks8695.c
@@ -0,0 +1,1169 @@
+/*
+ * KS8695P ethernet
+ * WAN port, LAN port to 4-port switch
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+#include "ureg.h"
+
+#define DBG if(0)iprint
+#define MIIDBG if(0)iprint
+
+enum {
+ Nrdre = 64, /* receive descriptor ring entries */
+ Ntdre = 32, /* transmit descriptor ring entries */
+
+ Rbsize = ROUNDUP(ETHERMAXTU+4, 4), /* ring buffer size (+4 for CRC), must be multiple of 4 */
+ Bufsize = ROUNDUP(Rbsize, CACHELINESZ), /* keep start and end at cache lines */
+};
+
+typedef struct DmaReg DmaReg;
+struct DmaReg {
+ ulong dtxc; /* transmit control register */
+ ulong drxc; /* receive control register */
+ ulong dtsc; /* transmit start command register */
+ ulong drsc; /* receive start command register */
+ ulong tdlb; /* transmit descriptor list base address */
+ ulong rdlb; /* receive descriptor list base address */
+ ulong mal; /* mac address low (4 bytes) */
+ ulong mah; /* mac address high (2 bytes) */
+ ulong pad[0x80-0x20];
+
+ /* pad to 0x80 for */
+ ulong maal[16][2]; /* additional mac addresses */
+};
+
+enum {
+ /* dtxc */
+ TxSoftReset= 1<<31,
+ /* 29:24 is burst size in words; 0, 1, 2, 4, 8, 16, 32; 0=unlimited */
+ TxUDPck= 1<<18, /* generate UDP, TCP, IP check sum */
+ TxTCPck= 1<<17,
+ TxIPck= 1<<16,
+ TxFCE= 1<<9, /* transmit flow control enable */
+ TxLB= 1<<8, /* loop back */
+ TxEP= 1<<2, /* enable padding */
+ TxCrc= 1<<1, /* add CRC */
+ TxEnable= 1<<0, /* enable Tx block */
+
+ /* drxc */
+ /* 29:24 is burst size in words */
+ RxUDPck= 1<<18, /* check UDP, TCP, IP check sum */
+ RxTCPck= 1<<17,
+ RxIPck= 1<<16,
+ RxFCE= 1<<9, /* flow control enable */
+ RxRB= 1<<6, /* receive broadcast */
+ RxRM= 1<<5, /* receive multicast (including broadcast) */
+ RxRU= 1<<4, /* receive unicast */
+ RxAE= 1<<3, /* receive error frames */
+ RxRA= 1<<2, /* receive all */
+ RxEnable= 1<<0, /* enable Rx block */
+
+};
+
+typedef struct WanPhy WanPhy;
+struct WanPhy {
+ ulong did; /* device ID */
+ ulong rid; /* revision ID */
+ ulong pad0; /* miscellaneous control in plain 8695 (not P or X) */
+ ulong wmc; /* WAN miscellaneous control */
+ ulong wppm; /* phy power management */
+ ulong wpc; /* phys ctl */
+ ulong wps; /* phys status */
+ ulong pps; /* phy power save */
+};
+
+enum {
+ /* wmc */
+ WAnc= 1<<30, /* auto neg complete */
+ WAnr= 1<<29, /* auto neg restart */
+ WAnaP= 1<<28, /* advertise pause */
+ WAna100FD= 1<<27, /* advertise 100BASE-TX FD */
+ WAna100HD= 1<<26, /* advertise 100BASE-TX */
+ WAna10FD= 1<<25, /* advertise 10BASE-TX FD */
+ WAna10HD= 1<<24, /* advertise 10BASE-TX */
+ WLs= 1<<23, /* link status */
+ WDs= 1<<22, /* duplex status (resolved) */
+ WSs= 1<<21, /* speed status (resolved) */
+ WLparP= 1<<20, /* link partner pause */
+ WLpar100FD= 1<<19, /* link partner 100BASE-TX FD */
+ WLpar100HD= 1<<18,
+ WLpar10FD= 1<<17,
+ WLpar10HD= 1<<16,
+ WAnDis= 1<<15, /* auto negotiation disable */
+ WForce100= 1<<14,
+ WForceFD= 1<<13,
+ /* 6:4 LED1 select */
+ /* 2:0 LED0 select */
+
+ /* LED select */
+ LedSpeed= 0,
+ LedLink,
+ LedFD, /* full duplex */
+ LedColl, /* collision */
+ LedTxRx, /* activity */
+ LedFDColl, /* FD/collision */
+ LedLinkTxRx, /* link and activity */
+
+ /* ppm */
+ WLpbk= 1<<14, /* local (MAC) loopback */
+ WRlpblk= 1<<13, /* remote (PHY) loopback */
+ WPhyIso= 1<<12, /* isolate PHY from MII and Tx+/Tx- */
+ WPhyLink= 1<<10, /* force link in PHY */
+ WMdix= 1<<9, /* =1, MDIX, =0, MDX */
+ WFef= 1<<8, /* far end fault */
+ WAmdixp= 1<<7, /* disable IEEE spec for auto-neg MDIX */
+ WTxdis= 1<<6, /* disable port's transmitter */
+ WDfef= 1<<5, /* disable far end fault detection */
+ Wpd= 1<<4, /* power down */
+ WDmdx= 1<<3, /* disable auto MDI/MDIX */
+ WFmdx= 1<<2, /* if auto disabled, force MDIX */
+ WMlpbk= 1<<1, /* local loopback */
+
+ /* pps */
+ Ppsm= 1<<0, /* enable PHY power save mode */
+};
+
+#define DMABURST(n) ((n)<<24)
+
+typedef struct {
+ Lock;
+ int port;
+ int init;
+ int active;
+ int reading; /* device read process is active */
+ ulong anap; /* auto negotiate result */
+ DmaReg* regs;
+ WanPhy* wphy;
+
+ Ring;
+
+ ulong interrupts; /* statistics */
+ ulong deferred;
+ ulong heartbeat;
+ ulong latecoll;
+ ulong retrylim;
+ ulong underrun;
+ ulong overrun;
+ ulong carrierlost;
+ ulong retrycount;
+} Ctlr;
+
+static void switchinit(uchar*);
+static void switchdump(void);
+
+static void
+attach(Ether *ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ ilock(ctlr);
+ if(!ctlr->active){
+ /* TO DO: rx/tx enable */
+ ctlr->regs->dtxc |= TxEnable;
+ ctlr->regs->drxc |= RxEnable;
+ microdelay(10);
+ ctlr->regs->drsc = 1; /* start read process */
+ microdelay(10);
+ ctlr->reading = (INTRREG->st & (1<<IRQwmrps)) == 0;
+ ctlr->active = 1;
+ }
+ iunlock(ctlr);
+}
+
+static void
+closed(Ether *ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ if(ctlr->active){
+ ilock(ctlr);
+iprint("ether closed\n");
+ ctlr->regs->dtxc &= ~TxEnable;
+ ctlr->regs->drxc &= ~RxEnable;
+ /* TO DO: reset ring? */
+ /* TO DO: could wait? */
+ ctlr->active = 0;
+ iunlock(ctlr);
+ }
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+ Ether *ether;
+ Ctlr *ctlr;
+ ulong w;
+
+ ether = (Ether*)arg;
+ ctlr = ether->ctlr;
+
+ ilock(ctlr);
+ /* TO DO: must disable reader */
+ w = ctlr->regs->drxc;
+ if(on != ((w&RxRA)!=0)){
+ /* TO DO: must disable reader */
+ ctlr->regs->drxc = w ^ RxRA;
+ /* TO DO: restart reader */
+ }
+ iunlock(ctlr);
+}
+
+static void
+multicast(void* arg, uchar *addr, int on)
+{
+ Ether *ether;
+ Ctlr *ctlr;
+
+ USED(addr, on); /* if on, could SetGroupAddress; if !on, it's hard */
+
+ ether = (Ether*)arg;
+ ctlr = ether->ctlr;
+
+ ilock(ctlr);
+ /* TO DO: must disable reader */
+ /* TO DO: use internal multicast tables? (probably needs LRU or some such) */
+ if(ether->nmaddr)
+ ctlr->regs->drxc |= RxRM;
+ else
+ ctlr->regs->drxc &= ~RxRM;
+ iunlock(ctlr);
+}
+
+static void
+txstart(Ether *ether)
+{
+ int len;
+ Ctlr *ctlr;
+ Block *b;
+ BD *dre;
+
+ ctlr = ether->ctlr;
+ while(ctlr->ntq < ctlr->ntdre-1){
+ b = qget(ether->oq);
+ if(b == 0)
+ break;
+
+ dre = &ctlr->tdr[ctlr->tdrh];
+ if(dre->ctrl & BdBusy)
+ panic("ether: txstart");
+
+ /*
+ * Give ownership of the descriptor to the chip, increment the
+ * software ring descriptor pointer and tell the chip to poll.
+ */
+ len = BLEN(b);
+ if(ctlr->txb[ctlr->tdrh] != nil)
+ panic("etherks8695: txstart");
+ ctlr->txb[ctlr->tdrh] = b;
+ dcflush(b->rp, len);
+ dre->addr = PADDR(b->rp);
+ dre->size = TxIC|TxFS|TxLS | len;
+ dre->ctrl = BdBusy;
+ ctlr->regs->dtsc = 1; /* go for it */
+ ctlr->ntq++;
+ ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdre);
+ }
+}
+
+static void
+transmit(Ether* ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ ilock(ctlr);
+ txstart(ether);
+ iunlock(ctlr);
+}
+
+/*
+ * allocate receive buffer space on cache-line boundaries
+ */
+static Block*
+clallocb(void)
+{
+ Block *b;
+
+ b = iallocb(Bufsize+CACHELINESZ-1);
+ if(b == nil)
+ return b;
+ dcflush(b->base, BALLOC(b));
+ b->wp = b->rp = (uchar*)(((ulong)b->base + CACHELINESZ - 1) & ~(CACHELINESZ-1));
+ return b;
+}
+
+
+static void
+rxring(Ureg*, void *arg)
+{
+ Ether *ether;
+ ulong status;
+ Ctlr *ctlr;
+ BD *dre;
+ Block *b, *rb;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+ ctlr->interrupts++;
+
+ /*
+ * Receiver interrupt: run round the descriptor ring logging
+ * errors and passing valid receive data up to the higher levels
+ * until we encounter a descriptor still owned by the chip.
+ * We rely on the descriptor accesses being uncached.
+ */
+ dre = &ctlr->rdr[ctlr->rdrx];
+ while(((status = dre->ctrl) & BdBusy) == 0){
+ if(status & RxES || (status & (RxFS|RxLS)) != (RxFS|RxLS)){
+ if(status & (RxRF|RxTL))
+ ether->buffs++;
+ if(status & RxRE)
+ ether->frames++;
+ if(status & RxCE)
+ ether->crcs++;
+ //if(status & RxOverrun)
+ // ether->overflows++;
+ iprint("eth rx: %lux\n", status);
+ }else{
+ /*
+ * We have a packet. Read it in.
+ */
+ b = clallocb();
+ if(b != nil){
+ rb = ctlr->rxb[ctlr->rdrx];
+ rb->wp += (dre->ctrl & RxFL)-4;
+ etheriq(ether, rb, 1);
+ ctlr->rxb[ctlr->rdrx] = b;
+ dre->addr = PADDR(b->wp);
+ }else
+ ether->soverflows++;
+ }
+
+ /*
+ * Finished with this descriptor,
+ * give it back to the chip, then on to the next...
+ */
+ dre->ctrl = BdBusy;
+
+ ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdre);
+ dre = &ctlr->rdr[ctlr->rdrx];
+ }
+}
+
+static void
+txring(Ureg*, void *arg)
+{
+ Ether *ether;
+ Ctlr *ctlr;
+ BD *dre;
+ Block *b;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+ ctlr->interrupts++;
+
+ /*
+ * Transmitter interrupt: handle anything queued for a free descriptor.
+ */
+ lock(ctlr);
+ while(ctlr->ntq){
+ dre = &ctlr->tdr[ctlr->tdri];
+ if(dre->ctrl & BdBusy)
+ break;
+ /* statistics are kept inside the device, but only for LAN */
+ /* there seems to be no per-packet error status for transmission */
+ b = ctlr->txb[ctlr->tdri];
+ if(b == nil)
+ panic("etherks8695: bufp");
+ ctlr->txb[ctlr->tdri] = nil;
+ freeb(b);
+ ctlr->ntq--;
+ ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdre);
+ }
+ txstart(ether);
+ unlock(ctlr);
+}
+
+/*
+ * receive buffer unavailable (overrun)
+ */
+static void
+rbuintr(Ureg*, void *arg)
+{
+ Ether *ether;
+ Ctlr *ctlr;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+
+ ctlr->interrupts++;
+ if(ctlr->active)
+ ctlr->overrun++;
+ ctlr->reading = 0;
+}
+
+/*
+ * read process (in device) stopped
+ */
+static void
+rxstopintr(Ureg*, void *arg)
+{
+ Ether *ether;
+ Ctlr *ctlr;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+
+ ctlr->interrupts++;
+ if(!ctlr->active)
+ return;
+
+iprint("rxstopintr\n");
+ ctlr->regs->drsc = 1;
+ /* just restart it? need to fiddle with ring? */
+}
+
+static void
+txstopintr(Ureg*, void *arg)
+{
+ Ether *ether;
+ Ctlr *ctlr;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+
+ ctlr->interrupts++;
+ if(!ctlr->active)
+ return;
+
+iprint("txstopintr\n");
+ ctlr->regs->dtsc = 1;
+ /* just restart it? need to fiddle with ring? */
+}
+
+
+static void
+linkchangeintr(Ureg*, void*)
+{
+ iprint("link change\n");
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+ char *p;
+ int len;
+ Ctlr *ctlr;
+
+ if(n == 0)
+ return 0;
+
+ ctlr = ether->ctlr;
+
+ p = malloc(READSTR);
+ len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts);
+ len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->carrierlost);
+ len += snprint(p+len, READSTR-len, "heartbeat: %lud\n", ctlr->heartbeat);
+ len += snprint(p+len, READSTR-len, "retrylimit: %lud\n", ctlr->retrylim);
+ len += snprint(p+len, READSTR-len, "retrycount: %lud\n", ctlr->retrycount);
+ len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->latecoll);
+ len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->overrun);
+ len += snprint(p+len, READSTR-len, "txunderruns: %lud\n", ctlr->underrun);
+{DmaReg *d = ctlr->regs; len += snprint(p+len, READSTR-len, "dtxc=%8.8lux drxc=%8.8lux\n", d->dtxc, d->drxc);}
+ snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->deferred);
+ n = readstr(offset, a, n, p);
+ free(p);
+
+ if(ctlr->port == 1)
+ switchdump();
+ return n;
+}
+
+static void
+physinit(Ether *ether, int force)
+{
+ Ctlr *ctlr;
+ WanPhy *p;
+ ulong anap;
+ int i;
+
+ ctlr = ether->ctlr;
+ p = ctlr->wphy;
+ if(p == nil){
+ if(ctlr->port){
+ ether->mbps = 100;
+ ether->fullduplex = 1;
+ switchinit(nil);
+ }
+ return;
+ }
+ iprint("phy%d: wmc=%8.8lux wpm=%8.8lux wpc=%8.8lux wps=%8.8lux pps=%8.8lux\n", ctlr->port, p->wmc, p->wppm, p->wpc, p->wps, p->pps);
+
+ p->wppm = 0; /* enable power, other defaults seem fine */
+ if(p->rid & 7)
+ p->wpc = 0x0200b000; /* magic */
+ else
+ p->wpc = 0xb000;
+ if(p->wppm & WFef)
+ iprint("ether%d: far end fault\n", ctlr->port);
+
+ if((p->wmc & WLs) == 0){
+ iprint("ether%d: no link\n", ctlr->port);
+ ether->mbps = 100; /* could use 10, but this is 2005 */
+ ether->fullduplex = 0;
+ return;
+ }
+
+ if((p->wmc & WAnc) == 0 || force){
+ p->wmc = WAnr | WAnaP | WAna100FD | WAna100HD | WAna10FD | WAna10HD | (p->wmc & 0x7F);
+ microdelay(10);
+ if(p->wmc & WLs){
+ for(i=0;; i++){
+ if(i > 600){
+ iprint("ether%d: auto negotiation failed\n", ctlr->port);
+ ether->mbps = 10; /* we'll assume it's stupid */
+ ether->fullduplex = 0;
+ return;
+ }
+ if(p->wmc & WAnc){
+ microdelay(10);
+ break;
+ }
+ delay(1);
+ }
+ }
+ }
+ anap = p->wmc;
+ ether->mbps = anap & WSs? 100: 10;
+ if(anap & (WLpar100FD|WLpar10FD) && anap & WDs)
+ ether->fullduplex = 1;
+ else
+ ether->fullduplex = 0;
+ ctlr->anap = anap;
+
+ iprint("ks8695%d mii: fd=%d speed=%d wmc=%8.8lux\n", ctlr->port, ether->fullduplex, ether->mbps, anap);
+}
+
+static void
+ctlrinit(Ctlr *ctlr, Ether *ether)
+{
+ int i;
+ DmaReg *em;
+ ulong mode;
+
+ em = ctlr->regs;
+
+ /* soft reset */
+ em->dtxc = TxSoftReset;
+ microdelay(10);
+ for(i=0; em->dtxc & TxSoftReset; i++){
+ if(i > 20){
+ iprint("etherks8695.%d: soft reset failed\n", ctlr->port);
+ i=0;
+ }
+ microdelay(100);
+ }
+iprint("%d: rx=%8.8lux tx=%8.8lux\n", ctlr->port, PADDR(ctlr->rdr), PADDR(ctlr->tdr));
+
+ physinit(ether, 0);
+
+ /* set ether address */
+ em->mah = (ether->ea[0]<<8) | ether->ea[1];
+ em->mal = (ether->ea[2]<<24) | (ether->ea[3]<<16) | (ether->ea[4]<<8) | ether->ea[5];
+ if(ctlr->port == 0){
+ /* clear other addresses for now */
+ for(i=0; i<nelem(em->maal); i++){
+ em->maal[i][0] = 0;
+ em->maal[i][1] = 0;
+ }
+ }
+
+ /* transmitter, enabled later by attach */
+ em->tdlb = PADDR(ctlr->tdr);
+ em->dtxc = DMABURST(8) | TxFCE | TxCrc; /* don't set TxEP: there is a h/w bug and it's anyway done by higher levels */
+
+ /* receiver, enabled later by attach */
+ em->rdlb = PADDR(ctlr->rdr);
+ mode = DMABURST(8) | RxRB | RxRU | RxAE; /* RxAE just there for testing */
+ if(ether->fullduplex)
+ mode |= RxFCE;
+ em->drxc = mode;
+
+ /* tx/rx enable is deferred until attach */
+}
+
+static int
+reset(Ether* ether)
+{
+ uchar ea[Eaddrlen];
+ char name[KNAMELEN];
+ Ctlr *ctlr;
+ int i, irqdelta;
+
+ snprint(name, sizeof(name), "ether%d", ether->ctlrno);
+
+ /*
+ * Insist that the platform-specific code provide the Ethernet address
+ */
+ memset(ea, 0, Eaddrlen);
+ if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+ print("%s (%s %ld): no ether address", name, ether->type, ether->port);
+ return -1;
+ }
+
+ ctlr = malloc(sizeof(*ctlr));
+ ctlr->port = ether->port;
+
+ switch(ether->port){
+ case 0:
+ ctlr->regs = KADDR(PHYSWANDMA);
+ ctlr->wphy = KADDR(PHYSMISC);
+ ctlr->wphy->wmc = (ctlr->wphy->wmc & ~0x7F) | (LedLinkTxRx<<0) | (LedSpeed<<4);
+ break;
+ case 1:
+ ctlr->regs = KADDR(PHYSLANDMA);
+ ctlr->wphy = nil;
+ break;
+ default:
+ print("%s: %s ether: no port %lud\n", name, ether->type, ether->port);
+ free(ctlr);
+ return -1;
+ }
+
+ ether->ctlr = ctlr;
+ irqdelta = ether->irq - IRQwmrps;
+
+ physinit(ether, 0);
+
+ if(ioringinit(ctlr, Nrdre, Ntdre) < 0)
+ panic("etherks8695 initring");
+
+ for(i = 0; i < ctlr->nrdre; i++){
+ if(ctlr->rxb[i] == nil)
+ ctlr->rxb[i] = clallocb();
+ ctlr->rdr[i].addr = PADDR(ctlr->rxb[i]->wp);
+ ctlr->rdr[i].size = Rbsize;
+ ctlr->rdr[i].ctrl = BdBusy;
+ }
+
+ ctlrinit(ctlr, ether);
+
+ ether->attach = attach;
+ ether->closed = closed;
+ ether->transmit = transmit;
+ ether->ifstat = ifstat;
+
+ /* there is more than one interrupt: we must enable some ourselves */
+ ether->irq = irqdelta + IRQwmrs; /* set main IRQ to receive status */
+ ether->interrupt = rxring;
+ intrenable(IRQ, irqdelta+IRQwmts, txring, ether, "ethertx");
+// intrenable(IRQ, irqdelta+IRQwmtbu, tbuintr, ether, "ethertbu"); /* don't care? */
+ intrenable(IRQ, irqdelta+IRQwmrbu, rbuintr, ether, "etherrbu");
+ intrenable(IRQ, irqdelta+IRQwmrps, rxstopintr, ether, "etherrps");
+ intrenable(IRQ, irqdelta+IRQwmtps, txstopintr, ether, "ethertps");
+ if(ether->port == 0)
+ intrenable(IRQ, IRQwmlc, linkchangeintr, ether, "etherwanlink");
+
+ ether->arg = ether;
+ ether->promiscuous = promiscuous;
+ ether->multicast = multicast;
+
+ return 0;
+}
+
+/*
+ * switch engine registers
+ * a 10 microsecond delay is required after each (write?) access
+ */
+typedef struct Switch Switch;
+struct Switch {
+ ulong sec0; /* control register 0 */
+ ulong sec1; /* control register 1 */
+ ulong sec2; /* control register 2, factory default, do not change */
+ ulong cfg[5][3]; /* port configuration registers */
+ ulong an[2]; /* ports 1 to 4 auto negotiation [1,2][3,4] */
+ ulong seiac; /* indirect access control register */
+ ulong seiadh2; /* indirect access data register 2 (4:0 is 68-64 of data) */
+ ulong seiadh1; /* indirect access data register 1 (63-32 of data) */
+ ulong seiadl; /* indirect access data register low */
+ ulong seafc; /* advanced feature control */
+ ulong scph; /* services code priority high (ie, TOS priority) */
+ ulong scpl; /* services code priority low */
+ ulong mah; /* switch MAC address high */
+ ulong mal; /* switch MAC address low */
+ ulong ppm[2]; /* ports 1 to 4 PHY power management */
+};
+
+enum {
+ /* Sec0 */
+ Nbe= 1<<31, /* new backoff (designed for UNH) enable */
+ /* 30:28 802.1p base priority */
+ /* 27:25 LAN LED1 select */
+ /* 24:22 LAN LED0 select */
+ Unh= 1<<21, /* =1, drop packets with type 8808 or DA=0180c2000001; =0, drop flow control */
+ Lca= 1<<20, /* link change age: faster aging for link->no link transition */
+ Paf= 1<<19, /* pass all frames, including bad ones */
+ Sfce= 1<<18, /* switch MII full-duplex flow control enable */
+ Flfc= 1<<17, /* frame length field check in IEEE (drop invalid ones) */
+ Bsm= 1<<16, /* =1, share all buffers; =0, use only 1/5 of pool */
+ Age= 1<<15, /* enable age function */
+ Agef= 1<<14, /* enable fast ageing */
+ Aboe= 1<<13, /* aggressive backoff enable */
+ Uvmd= 1<<12, /* unicast port-VLAN mismatch discard */
+ Mspd= 1<<11, /* multicast storm protection disable */
+ Bpm= 1<<10, /* =1, carrier sense backpressure; =0, collision backpressure */
+ Fair= 1<<9, /* fair flow control and back pressure */
+ Ncd= 1<<8, /* no excessive collision drop */
+ Lmpsd= 1<<7, /* 1=, drop packet sizes over 1536 bytes; =0, 1522 for tagged, 1518 untagged */
+ Pbr= 1<<6, /* priority buffer reserved */
+ Sbpe= 1<<5, /* switch back pressure enable */
+ Shdm= 1<<4, /* switch half duplex mode */
+ PrioHi= 0<<2, /* always deliver high priority first */
+ Prio10_1= 1<<2, /* high/low at 10:1 */
+ Prio5_1= 2<<2, /* high/low at 5:1 */
+ Prio2_1= 3<<2, /* high/low at 2:1 */
+ Etm= 1<<1, /* enable tag mask */
+ Esf= 1<<0, /* enable switch function */
+
+ /* sec1 */
+ /* 31:21 */ /* broadcast storm protection, byte count */
+ IEEEneg= 1<<11, /* follow IEEE spec for auto neg */
+ Tpid= 1<<10, /* special TPID mode used for direct forwarding from port 5 */
+ PhyEn= 1<<8, /* enable PHY MII */
+ TfcDis= 1<<7, /* disable IEEE transmit flow control */
+ RfcDis= 1<<6, /* disable IEEE receive flow control */
+ Hps= 1<<5, /* huge packet support: allow packets up to 1916 bytes */
+ VlanEn= 1<<4, /* 802.1Q VLAN enable; recommended when priority queue on */
+ Sw10BT= 1<<1, /* switch in 10 Mbps mode not 100 Mbps */
+ VIDrep= 1<<0, /* replace null VID with port VID (otherwise no replacement) */
+
+};
+#define BASEPRIO(n) (((n)&7)<<28)
+
+
+enum {
+ /* cfg[n][0] (SEP1C1-SEP4C1) p. 89 */
+ /* 31:16 default tag: 31:29=userprio, 28=CFI bit, 27:16=VID[11:0] */
+ AnegDis= 1<<15, /* disable auto negotiation */
+ Force100= 1<<14, /* force 100BT when auto neg is disabled */
+ ForceFD= 1<<13, /* force full duplex when auto neg is disabled */
+ /* 12:8 port VLAN membership: bit 8 is port 1, bit 12 is port 5, 1=member */
+ STTxEn= 1<<7, /* spanning tree transmit enable */
+ STRxEn= 1<<6, /* spanning tree receive enable */
+ STLnDis= 1<<5, /* spanning tree learn disnable */
+ Bsp= 1<<4, /* enable broadcast storm protection */
+ Pce= 1<<3, /* priority classification enable */
+ Dpce= 1<<2, /* diffserv priority classification enable */
+ IEEEpce= 1<<1, /* IEEE (802.1p) classification enable */
+ PrioEn= 1<<0, /* enable priority function on port */
+
+ /* cfg[n][1] (SEP1C2-SEP4C2) p. 91*/
+ IngressFilter= 1<<28, /* discard packets from ingress port not in VLAN */
+ DiscardNonPVID= 1<<27, /* discard packets whose VID does not match port default VID */
+ ForcePortFC= 1<<26, /* force flow control */
+ EnablePortBP= 1<<25, /* enable back pressure */
+ /* 23:12 transmit high priority rate control */
+ /* 11:0 transmit low priority rate control */
+
+ /* cfg[n][2] */
+ /* 13:20 receive high priority rate control */
+ /* 19:8 receive low priority rate control */
+ Rdprc= 1<<7, /* receive differential priority rate control */
+ Lprrc= 1<<6, /* low priority receive rate control */
+ Hprrc= 1<<5, /* high priority receive rate control */
+ Lprfce= 1<<4, /* low priority receive flow control enable */
+ Hprfce= 1<<3, /* high priority ... */
+ Tdprc= 1<<2, /* transmit differential priority rate control */
+ Lptrc= 1<<1, /* low priority transmit rate control */
+ Hptrc= 1<<0, /* high priority transmit rate control */
+
+ /* seiac */
+ Cread= 1<<12,
+ Cwrite= 0<<12,
+ StaticMacs= 0<<10, /* static mac address table used */
+ VLANs= 1<<10, /* VLAN table */
+ DynMacs= 2<<10, /* dynamic address table */
+ MibCounter= 3<<10, /* MIB counter selected */
+ /* 0:9, table index */
+
+ /* seafc */
+ /* 26:22 1<<(n+22-1) = removal for port 0 to 4 */
+};
+
+/*
+ * indirect access to
+ * static MAC address table (3.10.23, p. 107)
+ * VLAN table (3.10.24, p. 108)
+ * dynamic MAC address table (3.10.25, p. 109)
+ * MIB counters (3.10.26, p. 110)
+ */
+enum {
+ /* VLAN table */
+ VlanValid= 1<<21, /* entry is valid */
+ /* 20:16 are bits for VLAN membership */
+ /* 15:12 are bits for FID (filter id) for up to 16 active VLANs */
+ /* 11:0 has 802.1Q 12 bit VLAN ID */
+
+ /* Dynamic MAC table (1024 entries) */
+ MACempty= 1<<(68-2*32),
+ /* 67:58 is number of valid entries-1 */
+ /* 57:56 ageing time stamp */
+ NotReady= 1<<(55-32),
+ /* 54:52 source port 0 to 5 */
+ /* 51:48 FID */
+ /* 47:0 MAC */
+
+ NVlans= 16,
+ NSMacs= 8,
+};
+
+/*
+ * per-port counters, table 3, 3.10.26, p. 110
+ * cleared when read
+ * port counters at n*0x20 [n=0-3]
+ */
+static char* portmibnames[] = {
+ "RxLoPriorityByte",
+ "RxHiPriorityByte",
+ "RxUndersizePkt",
+ "RxFragments",
+ "RxOversize",
+ "RxJabbers",
+ "RxSymbolError",
+ "RxCRCerror",
+ "RxAlignmentError",
+ "RxControl8808Pkts",
+ "RxPausePkts",
+ "RxBroadcast",
+ "RxMulticast",
+ "RxUnicast",
+ "Rx64Octets",
+ "Rx65to127Octets",
+ "Rx128to255Octets",
+ "Rx256to511Octets",
+ "Rx512to1023Octets",
+ "Rx1024to1522Octets",
+ "TxLoPriorityByte",
+ "TxHiPriorityByte",
+ "TxLateCollision",
+ "TxPausePkts",
+ "TxBroadcastPkts",
+ "TxMulticastPkts",
+ "TxUnicastPkts",
+ "TxDeferred",
+ "TxTotalCollision", /* like, totally */
+ "TxExcessiveCollision",
+ "TxSingleCollision",
+ "TxMultipleCollision",
+};
+enum {
+ /* per-port MIB counter format */
+ MibOverflow= 1<<31,
+ MibValid= 1<<30,
+ /* 29:0 counter value */
+};
+
+/*
+ * 16 bit `all port' counters, not automatically cleared
+ * offset 0x100 and up
+ */
+
+static char* allportnames[] = {
+ "Port1TxDropPackets",
+ "Port2TxDropPackets",
+ "Port3TxDropPackets",
+ "Port4TxDropPackets",
+ "LanTxDropPackets", /* ie, internal port 5 */
+ "Port1RxDropPackets",
+ "Port2RxDropPackets",
+ "Port3RxDropPackets",
+ "Port4RxDropPackets",
+ "LanRxDropPackets",
+};
+
+static void
+switchinit(uchar *ea)
+{
+ Switch *sw;
+ int i;
+ ulong an;
+
+ /* TO DO: LED gpio setting */
+
+ GPIOREG->iopm |= 0xF0; /* bits 4-7 are LAN(?) */
+iprint("switch init...\n");
+ sw = KADDR(PHYSSWITCH);
+ if(sw->sec0 & Esf){
+ iprint("already inited\n");
+ return;
+ }
+ sw->seafc = 0;
+ microdelay(10);
+ sw->scph = 0;
+ microdelay(10);
+ sw->scpl = 0;
+ microdelay(10);
+ if(ea != nil){
+ sw->mah = (ea[0]<<8) | ea[1];
+ microdelay(10);
+ sw->mal = (ea[2]<<24) | (ea[3]<<16) | (ea[4]<<8) | ea[5];
+ microdelay(10);
+ }
+ for(i = 0; i < 5; i++){
+ sw->cfg[i][0] = (0x1F<<8) | STTxEn | STRxEn | Bsp; /* port is member of all vlans */
+ microdelay(10);
+ sw->cfg[i][1] = 0;
+ microdelay(10);
+ sw->cfg[i][2] = 0;
+ microdelay(10);
+ }
+ sw->ppm[0] = 0; /* perhaps soft reset? */
+ microdelay(10);
+ sw->ppm[1] = 0;
+ microdelay(10);
+ an = WAnr | WAnaP | WAna100FD | WAna100HD | WAna10FD | WAna10HD;
+ sw->an[0] = an | (an >> 16);
+ microdelay(10);
+ sw->an[1] = an | (an >> 16);
+ microdelay(10);
+ sw->sec1 = (0x4A<<21) | PhyEn;
+ microdelay(10);
+ sw->sec0 = Nbe | (0<<28) | (LedSpeed<<25) | (LedLinkTxRx<<22) | Sfce | Bsm | Age | Aboe | Bpm | Fair | Sbpe | Shdm | Esf;
+ microdelay(10);
+
+ /* off we go */
+}
+
+typedef struct Vidmap Vidmap;
+struct Vidmap {
+ uchar ports; /* bit mask for ports 0 to 4 */
+ uchar fid; /* switch's filter id */
+ ushort vid; /* 802.1Q vlan id; 0=not valid */
+};
+
+static Vidmap
+getvidmap(Switch *sw, int i)
+{
+ ulong w;
+ Vidmap v;
+
+ v.ports = 0;
+ v.fid = 0;
+ v.vid = 0;
+ if(i < 0 || i >= NVlans)
+ return v;
+ sw->seiac = Cread | VLANs | i;
+ microdelay(10);
+ w = sw->seiadl;
+ if((w & VlanValid) == 0)
+ return v;
+ v.vid = w & 0xFFFF;
+ v.fid = (w>>12) & 0xF;
+ v.ports = (w>>16) & 0x1F;
+ return v;
+}
+
+static void
+putvidmap(Switch *sw, int i, Vidmap v)
+{
+ ulong w;
+
+ w = ((v.ports & 0x1F)<<16) | ((v.fid & 0xF)<<12) | (v.vid & 0xFFFF);
+ if(v.vid != 0)
+ w |= VlanValid;
+ sw->seiadl = w;
+ microdelay(10);
+ sw->seiac = Cwrite | VLANs | i;
+ microdelay(10);
+}
+
+typedef struct StaticMac StaticMac;
+struct StaticMac {
+ uchar valid;
+ uchar fid;
+ uchar usefid;
+ uchar override; /* override spanning tree tx/rx disable */
+ uchar ports; /* forward to this set of ports */
+ uchar mac[Eaddrlen];
+};
+
+static StaticMac
+getstaticmac(Switch *sw, int i)
+{
+ StaticMac s;
+ ulong w;
+
+ memset(&s, 0, sizeof(s));
+ if(i < 0 || i >= NSMacs)
+ return s;
+ sw->seiac = Cread | StaticMacs | i;
+ microdelay(10);
+ w = sw->seiadh1;
+ if((w & (1<<(53-32))) == 0)
+ return s; /* entry not valid */
+ s.valid = 1;
+ s.fid= (w>>(57-32)) & 0xF;
+ s.usefid = (w & (1<<(56-32))) != 0;
+ s.override = (w & (1<<(54-32))) != 0;
+ s.ports = (w>>(48-32)) & 0x1F;
+ s.mac[5] = w >> 8;
+ s.mac[4] = w;
+ w = sw->seiadl;
+ s.mac[3] = w>>24;
+ s.mac[2] = w>>16;
+ s.mac[1] = w>>8;
+ s.mac[0] = w;
+ return s;
+}
+
+static void
+putstaticmac(Switch *sw, int i, StaticMac s)
+{
+ ulong w;
+
+ if(s.valid){
+ w = 1<<(53-32); /* entry valid */
+ if(s.usefid)
+ w |= 1<<(55-32);
+ if(s.override)
+ w |= 1<<(54-32);
+ w |= (s.fid & 0xF) << (56-32);
+ w |= (s.ports & 0x1F) << (48-32);
+ w |= (s.mac[5] << 8) | s.mac[4];
+ sw->seiadh1 = w;
+ microdelay(10);
+ w = (s.mac[3]<<24) | (s.mac[2]<<16) | (s.mac[1]<<8) | s.mac[0];
+ sw->seiadl = w;
+ microdelay(10);
+ }else{
+ sw->seiadh1 = 0; /* valid bit is 0; rest doesn't matter */
+ microdelay(10);
+ }
+ sw->seiac = Cwrite | StaticMacs | i;
+ microdelay(10);
+}
+
+typedef struct DynMac DynMac;
+struct DynMac {
+ ushort nentry;
+ uchar valid;
+ uchar age;
+ uchar port; /* source port (0 origin) */
+ uchar fid; /* filter id */
+ uchar mac[Eaddrlen];
+};
+
+static DynMac
+getdynmac(Switch *sw, int i)
+{
+ DynMac d;
+ ulong w;
+ int n, l;
+
+ memset(&d, 0, sizeof d);
+ l = 0;
+ do{
+ if(++l > 100)
+ return d;
+ sw->seiac = Cread | DynMacs | i;
+ microdelay(10);
+ w = sw->seiadh2;
+ /* peculiar encoding of table size */
+ if(w & MACempty)
+ return d;
+ n = w & 0xF;
+ w = sw->seiadh1;
+ }while(w & NotReady); /* TO DO: how long might it delay? */
+ d.nentry = ((n<<6) | (w>>(58-32))) + 1;
+ if(i < 0 || i >= d.nentry)
+ return d;
+ d.valid = 1;
+ d.age = (w>>(56-32)) & 3;
+ d.port = (w>>(52-32)) & 7;
+ d.fid = (w>>(48-32)) & 0xF;
+ d.mac[5] = w>>8;
+ d.mac[4] = w;
+ w = sw->seiadl;
+ d.mac[3] = w>>24;
+ d.mac[2] = w>>16;
+ d.mac[1] = w>>8;
+ d.mac[0] = w;
+ return d;
+}
+
+static void
+switchdump(void)
+{
+ Switch *sw;
+ int i, j;
+ ulong w;
+
+ sw = KADDR(PHYSSWITCH);
+ iprint("sec0 %8.8lux\n", sw->sec0);
+ iprint("sec1 %8.8lux\n", sw->sec1);
+ for(i = 0; i < 5; i++){
+ iprint("cfg%d", i);
+ for(j = 0; j < 3; j++){
+ w = sw->cfg[i][j];
+ iprint(" %8.8lux", w);
+ }
+ iprint("\n");
+ if(i < 2){
+ w = sw->an[i];
+ iprint(" an=%8.8lux pm=%8.8lux\n", w, sw->ppm[i]);
+ }
+ }
+ for(i = 0; i < 8; i++){
+ sw->seiac = Cread | DynMacs | i;
+ microdelay(10);
+ w = sw->seiadh2;
+ microdelay(10);
+ iprint("dyn%d: %8.8lux", i, w);
+ w = sw->seiadh1;
+ microdelay(10);
+ iprint(" %8.8lux", w);
+ w = sw->seiadl;
+ microdelay(10);
+ iprint(" %8.8lux\n", w);
+ }
+ for(i=0; i<0x20; i++){
+ sw->seiac = Cread | MibCounter | i;
+ microdelay(10);
+ w = sw->seiadl;
+ microdelay(10);
+ if(w & (1<<30))
+ iprint("%.2ux: %s: %lud\n", i, portmibnames[i], w & ~(3<<30));
+ }
+}
+
+static void
+switchstatproc(void*)
+{
+ for(;;){
+ tsleep(&up->sleep, return0, nil, 30*1000);
+ }
+}
+
+void
+etherks8695link(void)
+{
+ addethercard("ks8695", reset);
+}
+
+/*
+ * notes:
+ * switch control
+ * read stats every 30 seconds or so
+ */
diff --git a/os/manga/flashif.h b/os/manga/flashif.h
new file mode 100644
index 00000000..14b6a9e1
--- /dev/null
+++ b/os/manga/flashif.h
@@ -0,0 +1,82 @@
+typedef struct Flash Flash;
+
+/*
+ * structure defining a flash memory card
+ */
+struct Flash {
+ QLock; /* interlock on flash operations */
+ Flash* next;
+
+ /* the following are filled in by devflash before Flash.reset called */
+ char* name;
+ void* addr;
+ ulong size;
+ void * archdata;
+ int (*reset)(Flash*);
+
+ /* the following are filled in by the reset routine */
+ int (*eraseall)(Flash*);
+ int (*erasezone)(Flash*, int);
+ int (*read)(Flash*, ulong, void*, long); /* reads of correct width and alignment */
+ int (*write)(Flash*, ulong, void*, long); /* writes of correct width and alignment */
+ int (*suspend)(Flash*);
+ int (*resume)(Flash*);
+ int (*attach)(Flash*);
+
+ uchar id; /* flash manufacturer ID */
+ uchar devid; /* flash device ID */
+ int width; /* bytes per flash line */
+ int erasesize; /* size of erasable unit (accounting for width) */
+ void* data; /* flash type routines' private storage, or nil */
+ ulong unusable; /* bit mask of unusable sections */
+};
+
+/*
+ * called by link routine of driver for specific flash type: arguments are
+ * conventional name for card type/model, and card driver's reset routine.
+ */
+void addflashcard(char*, int (*)(Flash*));
+
+/*
+ * called by devflash.c:/^flashreset; if flash exists,
+ * sets type, address, and size in bytes of flash
+ * and returns 0; returns -1 if flash doesn't exist
+ */
+int archflashreset(int instance, char*, int, void**, long*, void **archdata);
+
+int archflash12v(int);
+void archflashwp(void *archdata, int);
+
+/*
+ * Architecture specific routines for managing nand devices
+ */
+
+/*
+ * do any device spcific initialisation
+ */
+void archnand_init(void *archdata);
+
+/*
+ * if claim is 1, claim device exclusively, and enable it (power it up)
+ * if claim is 0, release, and disable it (power it down)
+ * claiming may be as simple as a qlock per device
+ */
+void archnand_claim(void *archdata, int claim);
+
+/*
+ * set command latch enable (CLE) and address latch enable (ALE)
+ * appropriately
+ */
+void archnand_setCLEandALE(void *archdata, int cle, int ale);
+
+/*
+ * write a sequence of bytes to the device
+ */
+void archnand_write(void *archdata, void *buf, int len);
+
+/*
+ * read a sequence of bytes from the device
+ * if buf is 0, throw away the data
+ */
+void archnand_read(void *archdata, void *buf, int len);
+
diff --git a/os/manga/fns.h b/os/manga/fns.h
new file mode 100644
index 00000000..6eb28e26
--- /dev/null
+++ b/os/manga/fns.h
@@ -0,0 +1,163 @@
+#include "../port/portfns.h"
+
+ulong aifinit(uchar *aifarr);
+int archaudiopower(int);
+void archaudiomute(int);
+void archaudioamp(int);
+int archaudiospeed(int, int);
+void archconfinit(void);
+void archconsole(void);
+int archflash12v(int);
+long archkprofmicrosecondspertick(void);
+void archkprofenable(int);
+void archpowerdown(void);
+void archpowerup(void);
+void archreboot(void);
+void archreset(void);
+vlong archrdtsc(void);
+ulong archrdtsc32(void);
+void archuartpower(int, int);
+void blankscreen(int);
+void clockcheck(void);
+void clockinit(void);
+void clockpoll(void);
+#define coherence() /* nothing to do for cache coherence for uniprocessor */
+void cursorhide(void);
+void cursorunhide(void);
+void dcflush(void*, ulong);
+void dcflushall(void);
+void dcinval(void);
+int dmaidle(Dma*);
+Dma* dmasetup(int device, void(*)(void*,ulong), void*, ulong);
+int dmastart(Dma*, void*, void*, int);
+int dmacontinue(Dma*, void*, int);
+void dmastop(Dma*);
+int dmaerror(Dma*);
+void dmafree(Dma*);
+void dmareset(void);
+void dmawait(Dma*);
+void dumplongs(char *, ulong *, int);
+void dumpregs(Ureg* ureg);
+void dumpstack(void);
+int fpiarm(Ureg*);
+void fpinit(void);
+ulong getcallerpc(void*);
+ulong getcclkcfg(void);
+char* getconf(char*);
+ulong getcpsr(void);
+ulong getcpuid(void);
+ulong getspsr(void);
+void gotopc(ulong);
+
+void icflush(void*, ulong);
+void icflushall(void);
+void idle(void);
+void idlehands(void);
+int inb(ulong);
+int ins(ulong);
+ulong inl(ulong);
+void outb(ulong, int);
+void outs(ulong, int);
+void outl(ulong, ulong);
+void inss(ulong, void*, int);
+void outss(ulong, void*, int);
+void insb(ulong, void*, int);
+void outsb(ulong, void*, int);
+void intrdisable(int, int, void (*)(Ureg*, void*), void*, char*);
+void intrenable(int, int, void (*)(Ureg*, void*), void*, char*);
+void iofree(int);
+#define iofree(x)
+void ioinit(void);
+int iounused(int, int);
+int ioalloc(int, int, int, char*);
+#define ioalloc(a,b,c,d) 0
+int iprint(char*, ...);
+void installprof(void (*)(Ureg *, int));
+int isvalid_va(void*);
+void kbdinit(void);
+void ledset(int);
+void links(void);
+void mmuenable(ulong);
+void* mmucacheinhib(void*, ulong);
+ulong mmugetctl(void);
+ulong mmugetdac(void);
+ulong mmugetfar(void);
+ulong mmugetfsr(void);
+void mmuinit(void);
+void* mmukaddr(ulong);
+void* mmuphysmap(void*, ulong, ulong);
+void mmuputctl(ulong);
+void mmuputdac(ulong);
+void mmuputfsr(ulong);
+void mmuputttb(ulong);
+void mmureset(void);
+void mouseinit(void);
+void* pa2va(ulong);
+void pcimapinit(void);
+int pciscan(int, Pcidev **);
+ulong pcibarsize(Pcidev *, int);
+int pcicfgr8(Pcidev*, int);
+int pcicfgr16(Pcidev*, int);
+int pcicfgr32(Pcidev*, int);
+void pcicfgw8(Pcidev*, int, int);
+void pcicfgw16(Pcidev*, int, int);
+void pcicfgw32(Pcidev*, int, int);
+void pciclrbme(Pcidev*);
+void pcihinv(Pcidev*);
+uchar pciipin(Pcidev *, uchar);
+Pcidev* pcimatch(Pcidev*, int, int);
+Pcidev* pcimatchtbdf(int);
+void pcireset(void);
+void pcisetbme(Pcidev*);
+void powerenable(void (*)(int));
+void powerdisable(void (*)(int));
+void powerdown(void);
+void powerinit(void);
+void powersuspend(void);
+#define procsave(p)
+#define procrestore(p)
+void putcclkcfg(ulong);
+long rtctime(void);
+void screeninit(void);
+void (*screenputs)(char*, int);
+int segflush(void*, ulong);
+void setpanic(void);
+void setr13(int, void*);
+int splfhi(void);
+int splflo(void);
+void _suspendcode(void);
+void tlbinvalidateall(void);
+void tlbinvalidateaddr(void*);
+void trapinit(void);
+void trapstacks(void);
+void trapspecial(int (*)(Ureg *, uint));
+void uartconsole(void);
+void uartinstall(void);
+int uartprint(char*, ...);
+ulong va2pa(void*);
+void vectors(void);
+void vtable(void);
+#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+int wasbusy(int);
+
+#define KADDR(p) mmukaddr((ulong)(p))
+#define PADDR(v) va2pa((void*)(v))
+
+ulong timer_start(void);
+ulong timer_ticks(ulong);
+int timer_devwait(ulong *adr, ulong mask, ulong val, int ost);
+void timer_setwatchdog(int ost);
+void timer_delay(int ost);
+ulong ms2tmr(int ms);
+int tmr2ms(ulong t);
+void delay(int ms);
+ulong us2tmr(int us);
+int tmr2us(ulong t);
+void microdelay(int us);
+
+#define archuartclock(p,rate) 14745600
+
+/* debugging */
+extern void serialputs(char*, int);
+extern void serialputc(int);
+extern void xdelay(int);
diff --git a/os/manga/fpi.h b/os/manga/fpi.h
new file mode 100644
index 00000000..dfb9b1df
--- /dev/null
+++ b/os/manga/fpi.h
@@ -0,0 +1,61 @@
+typedef long Word;
+typedef unsigned long Single;
+typedef struct {
+ unsigned long h;
+ unsigned long l;
+} Double;
+
+enum {
+ FractBits = 28,
+ CarryBit = 0x10000000,
+ HiddenBit = 0x08000000,
+ MsBit = HiddenBit,
+ NGuardBits = 3,
+ GuardMask = 0x07,
+ LsBit = (1<<NGuardBits),
+
+ SingleExpBias = 127,
+ SingleExpMax = 255,
+ DoubleExpBias = 1023,
+ DoubleExpMax = 2047,
+
+ ExpBias = DoubleExpBias,
+ ExpInfinity = DoubleExpMax,
+};
+
+typedef struct {
+ unsigned char s;
+ short e;
+ long l; /* 0000FFFFFFFFFFFFFFFFFFFFFFFFFGGG */
+ long h; /* 0000HFFFFFFFFFFFFFFFFFFFFFFFFFFF */
+} Internal;
+
+#define IsWeird(n) ((n)->e >= ExpInfinity)
+#define IsInfinity(n) (IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0)
+#define SetInfinity(n) ((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0)
+#define IsNaN(n) (IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l))
+#define SetQNaN(n) ((n)->s = 0, (n)->e = ExpInfinity, \
+ (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0)
+#define IsZero(n) ((n)->e == 1 && (n)->h == 0 && (n)->l == 0)
+#define SetZero(n) ((n)->e = 1, (n)->h = 0, (n)->l = 0)
+
+/*
+ * fpi.c
+ */
+extern void fpiround(Internal *);
+extern void fpiadd(Internal *, Internal *, Internal *);
+extern void fpisub(Internal *, Internal *, Internal *);
+extern void fpimul(Internal *, Internal *, Internal *);
+extern void fpidiv(Internal *, Internal *, Internal *);
+extern int fpicmp(Internal *, Internal *);
+extern void fpinormalise(Internal*);
+
+/*
+ * fpimem.c
+ */
+extern void fpis2i(Internal *, void *);
+extern void fpid2i(Internal *, void *);
+extern void fpiw2i(Internal *, void *);
+extern void fpii2s(void *, Internal *);
+extern void fpii2d(void *, Internal *);
+extern void fpii2w(Word *, Internal *);
diff --git a/os/manga/fpiarm.c b/os/manga/fpiarm.c
new file mode 100644
index 00000000..4acfcd1d
--- /dev/null
+++ b/os/manga/fpiarm.c
@@ -0,0 +1,483 @@
+/*
+ * this doesn't attempt to implement ARM floating-point properties
+ * that aren't visible in the Inferno environment.
+ * all arithmetic is done in double precision.
+ * the FP trap status isn't updated.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ureg.h"
+
+#include "fpi.h"
+
+// #define R13OK undef this if correct kernel r13 isn't in Ureg; check calculation in fpiarm below
+
+#define REG(x) (*(long*)(((char*)(ur))+roff[(x)]))
+#define FPENV (*(ufp))
+#define FR(x) (*(Internal*)(ufp)->regs[(x)&7])
+
+/* BUG: check fetch (not worthwhile in Inferno) */
+#define getubyte(a) (*(uchar*)(a))
+#define getuword(a) (*(ushort*)(a))
+#define getulong(a) (*(ulong*)(a))
+
+typedef struct FP2 FP2;
+typedef struct FP1 FP1;
+
+struct FP2 {
+ char* name;
+ void (*f)(Internal, Internal, Internal*);
+};
+
+struct FP1 {
+ char* name;
+ void (*f)(Internal*, Internal*);
+};
+
+enum {
+ N = 1<<31,
+ Z = 1<<30,
+ C = 1<<29,
+ V = 1<<28,
+ REGPC = 15,
+};
+
+int fpemudebug = 0;
+
+#undef OFR
+#define OFR(X) ((ulong)&((Ureg*)0)->X)
+
+static int roff[] = {
+ OFR(r0), OFR(r1), OFR(r2), OFR(r3),
+ OFR(r4), OFR(r5), OFR(r6), OFR(r7),
+ OFR(r8), OFR(r9), OFR(r10), OFR(r11),
+#ifdef R13OK
+ OFR(r12), OFR(r13), OFR(r14), OFR(pc),
+#else
+ OFR(r12), OFR(type), OFR(r14), OFR(pc),
+#endif
+};
+
+static Internal fpconst[8] = { /* indexed by op&7 */
+ /* s, e, l, h */
+ {0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */
+ {0, 0x3FF, 0x00000000, 0x08000000}, /* 1.0 */
+ {0, 0x400, 0x00000000, 0x08000000}, /* 2.0 */
+ {0, 0x400, 0x00000000, 0x0C000000}, /* 3.0 */
+ {0, 0x401, 0x00000000, 0x08000000}, /* 4.0 */
+ {0, 0x401, 0x00000000, 0x0A000000}, /* 5.0 */
+ {0, 0x3FE, 0x00000000, 0x08000000}, /* 0.5 */
+ {0, 0x402, 0x00000000, 0x0A000000}, /* 10.0 */
+};
+
+/*
+ * arm binary operations
+ */
+
+static void
+fadd(Internal m, Internal n, Internal *d)
+{
+ (m.s == n.s? fpiadd: fpisub)(&m, &n, d);
+}
+
+static void
+fsub(Internal m, Internal n, Internal *d)
+{
+ m.s ^= 1;
+ (m.s == n.s? fpiadd: fpisub)(&m, &n, d);
+}
+
+static void
+fsubr(Internal m, Internal n, Internal *d)
+{
+ n.s ^= 1;
+ (n.s == m.s? fpiadd: fpisub)(&n, &m, d);
+}
+
+static void
+fmul(Internal m, Internal n, Internal *d)
+{
+ fpimul(&m, &n, d);
+}
+
+static void
+fdiv(Internal m, Internal n, Internal *d)
+{
+ fpidiv(&m, &n, d);
+}
+
+static void
+fdivr(Internal m, Internal n, Internal *d)
+{
+ fpidiv(&n, &m, d);
+}
+
+/*
+ * arm unary operations
+ */
+
+static void
+fmov(Internal *m, Internal *d)
+{
+ *d = *m;
+}
+
+static void
+fmovn(Internal *m, Internal *d)
+{
+ *d = *m;
+ d->s ^= 1;
+}
+
+static void
+fabsf(Internal *m, Internal *d)
+{
+ *d = *m;
+ d->s = 0;
+}
+
+static void
+frnd(Internal *m, Internal *d)
+{
+ short e;
+
+ (m->s? fsub: fadd)(fpconst[6], *m, d);
+ if(IsWeird(d))
+ return;
+ fpiround(d);
+ e = (d->e - ExpBias) + 1;
+ if(e <= 0)
+ SetZero(d);
+ else if(e > FractBits){
+ if(e < 2*FractBits)
+ d->l &= ~((1<<(2*FractBits - e))-1);
+ }else{
+ d->l = 0;
+ if(e < FractBits)
+ d->h &= ~((1<<(FractBits-e))-1);
+ }
+}
+
+static FP1 optab1[16] = { /* Fd := OP Fm */
+[0] {"MOVF", fmov},
+[1] {"NEGF", fmovn},
+[2] {"ABSF", fabsf},
+[3] {"RNDF", frnd},
+[4] {"SQTF", /*fsqt*/0},
+/* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */
+/* URD and NRM aren't implemented */
+};
+
+static FP2 optab2[16] = { /* Fd := Fn OP Fm */
+[0] {"ADDF", fadd},
+[1] {"MULF", fmul},
+[2] {"SUBF", fsub},
+[3] {"RSUBF", fsubr},
+[4] {"DIVF", fdiv},
+[5] {"RDIVF", fdivr},
+/* POW, RPW deprecated */
+[8] {"REMF", /*frem*/0},
+[9] {"FMF", fmul}, /* fast multiply */
+[10] {"FDV", fdiv}, /* fast divide */
+[11] {"FRD", fdivr}, /* fast reverse divide */
+/* POL deprecated */
+};
+
+static ulong
+fcmp(Internal *n, Internal *m)
+{
+ int i;
+
+ if(IsWeird(m) || IsWeird(n)){
+ /* BUG: should trap if not masked */
+ return V|C;
+ }
+ i = fpicmp(n, m);
+ if(i > 0)
+ return C;
+ else if(i == 0)
+ return C|Z;
+ else
+ return N;
+}
+
+static void
+fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPenv *ufp)
+{
+ void *mem;
+
+ mem = (void*)ea;
+ (*f)(&FR(d), mem);
+ if(fpemudebug)
+ print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
+}
+
+static void
+fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPenv *ufp)
+{
+ Internal tmp;
+ void *mem;
+
+ mem = (void*)ea;
+ tmp = FR(s);
+ if(fpemudebug)
+ print("MOV%c F%d,#%lux\n", n==8? 'D': 'F', s, ea);
+ (*f)(mem, &tmp);
+}
+
+static int
+condok(int cc, int c)
+{
+ switch(c){
+ case 0: /* Z set */
+ return cc&Z;
+ case 1: /* Z clear */
+ return (cc&Z) == 0;
+ case 2: /* C set */
+ return cc&C;
+ case 3: /* C clear */
+ return (cc&C) == 0;
+ case 4: /* N set */
+ return cc&N;
+ case 5: /* N clear */
+ return (cc&N) == 0;
+ case 6: /* V set */
+ return cc&V;
+ case 7: /* V clear */
+ return (cc&V) == 0;
+ case 8: /* C set and Z clear */
+ return cc&C && (cc&Z) == 0;
+ case 9: /* C clear or Z set */
+ return (cc&C) == 0 || cc&Z;
+ case 10: /* N set and V set, or N clear and V clear */
+ return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
+ case 11: /* N set and V clear, or N clear and V set */
+ return (cc&(N|V))==N || (cc&(N|V))==V;
+ case 12: /* Z clear, and either N set and V set or N clear and V clear */
+ return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
+ case 13: /* Z set, or N set and V clear or N clear and V set */
+ return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
+ case 14: /* always */
+ return 1;
+ case 15: /* never (reserved) */
+ return 0;
+ }
+ return 0; /* not reached */
+}
+
+static void
+unimp(ulong pc, ulong op)
+{
+ char buf[60];
+
+ snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op);
+ if(fpemudebug)
+ print("FPE: %s\n", buf);
+ error(buf);
+ /* no return */
+}
+
+static void
+fpemu(ulong pc, ulong op, Ureg *ur, FPenv *ufp)
+{
+ int rn, rd, tag, o;
+ long off;
+ ulong ea;
+ Internal tmp, *fm, *fn;
+
+ /* note: would update fault status here if we noted numeric exceptions */
+
+ /*
+ * LDF, STF; 10.1.1
+ */
+ if(((op>>25)&7) == 6){
+ if(op & (1<<22))
+ unimp(pc, op); /* packed or extended */
+ rn = (op>>16)&0xF;
+ off = (op&0xFF)<<2;
+ if((op & (1<<23)) == 0)
+ off = -off;
+ ea = REG(rn);
+ if(rn == REGPC)
+ ea += 8;
+ if(op & (1<<24))
+ ea += off;
+ rd = (op>>12)&7;
+ if(op & (1<<20)){
+ if(op & (1<<15))
+ fld(fpid2i, rd, ea, 8, ufp);
+ else
+ fld(fpis2i, rd, ea, 4, ufp);
+ }else{
+ if(op & (1<<15))
+ fst(fpii2d, ea, rd, 8, ufp);
+ else
+ fst(fpii2s, ea, rd, 4, ufp);
+ }
+ if((op & (1<<24)) == 0)
+ ea += off;
+ if(op & (1<<21))
+ REG(rn) = ea;
+ return;
+ }
+
+ /*
+ * CPRT/transfer, 10.3
+ */
+ if(op & (1<<4)){
+ rd = (op>>12) & 0xF;
+
+ /*
+ * compare, 10.3.1
+ */
+ if(rd == 15 && op & (1<<20)){
+ rn = (op>>16)&7;
+ fn = &FR(rn);
+ if(op & (1<<3)){
+ fm = &fpconst[op&7];
+ tag = 'C';
+ }else{
+ fm = &FR(op&7);
+ tag = 'F';
+ }
+ switch((op>>21)&7){
+ default:
+ unimp(pc, op);
+ case 4: /* CMF: Fn :: Fm */
+ case 6: /* CMFE: Fn :: Fm (with exception) */
+ ur->psr &= ~(N|C|Z|V);
+ ur->psr |= fcmp(fn, fm);
+ break;
+ case 5: /* CNF: Fn :: -Fm */
+ case 7: /* CNFE: Fn :: -Fm (with exception) */
+ tmp = *fm;
+ tmp.s ^= 1;
+ ur->psr &= ~(N|C|Z|V);
+ ur->psr |= fcmp(fn, &tmp);
+ break;
+ }
+ if(fpemudebug)
+ print("CMPF %c%d,F%ld =%x\n", tag, rn, op&7, ur->psr>>28);
+ return;
+ }
+
+ /*
+ * other transfer, 10.3
+ */
+ switch((op>>20)&0xF){
+ default:
+ unimp(pc, op);
+ case 0: /* FLT */
+ rn = (op>>16) & 7;
+ fpiw2i(&FR(rn), &REG(rd));
+ if(fpemudebug)
+ print("MOVW[FD] R%d, F%d\n", rd, rn);
+ break;
+ case 1: /* FIX */
+ if(op & (1<<3))
+ unimp(pc, op);
+ rn = op & 7;
+ tmp = FR(rn);
+ fpii2w(&REG(rd), &tmp);
+ if(fpemudebug)
+ print("MOV[FD]W F%d, R%d =%ld\n", rn, rd, REG(rd));
+ break;
+ case 2: /* FPSR := Rd */
+ FPENV.status = REG(rd);
+ if(fpemudebug)
+ print("MOVW R%d, FPSR\n", rd);
+ break;
+ case 3: /* Rd := FPSR */
+ REG(rd) = FPENV.status;
+ if(fpemudebug)
+ print("MOVW FPSR, R%d\n", rd);
+ break;
+ case 4: /* FPCR := Rd */
+ FPENV.control = REG(rd);
+ if(fpemudebug)
+ print("MOVW R%d, FPCR\n", rd);
+ break;
+ case 5: /* Rd := FPCR */
+ REG(rd) = FPENV.control;
+ if(fpemudebug)
+ print("MOVW FPCR, R%d\n", rd);
+ break;
+ }
+ return;
+ }
+
+ /*
+ * arithmetic
+ */
+
+ if(op & (1<<3)){ /* constant */
+ fm = &fpconst[op&7];
+ tag = 'C';
+ }else{
+ fm = &FR(op&7);
+ tag = 'F';
+ }
+ rd = (op>>12)&7;
+ o = (op>>20)&0xF;
+ if(op & (1<<15)){ /* monadic */
+ FP1 *fp;
+ fp = &optab1[o];
+ if(fp->f == nil)
+ unimp(pc, op);
+ if(fpemudebug)
+ print("%s %c%ld,F%d\n", fp->name, tag, op&7, rd);
+ (*fp->f)(fm, &FR(rd));
+ } else {
+ FP2 *fp;
+ fp = &optab2[o];
+ if(fp->f == nil)
+ unimp(pc, op);
+ rn = (op>>16)&7;
+ if(fpemudebug)
+ print("%s %c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd);
+ (*fp->f)(*fm, FR(rn), &FR(rd));
+ }
+}
+
+/*
+ * returns the number of FP instructions emulated
+ */
+int
+fpiarm(Ureg *ur)
+{
+ ulong op, o;
+ FPenv *ufp;
+ int n;
+
+#ifndef R13OK
+/* ur->type = &ur->pc+1; /* calculate kernel sp/R13 and put it here for roff[13] */
+ ur->type = (ulong)(ur + 1);
+#endif
+ if (up == nil)
+ panic("fpiarm not in a process");
+ ufp = &up->env->fpu; /* because all the state is in Osenv, it need not be saved/restored */
+ if(ufp->fpistate != FPACTIVE) {
+ ufp->fpistate = FPACTIVE;
+ ufp->control = 0;
+ ufp->status = (0x01<<28)|(1<<12); /* software emulation, alternative C flag */
+ for(n = 0; n < 8; n++)
+ FR(n) = fpconst[0];
+ }
+ for(n=0;;n++){
+ op = getulong(ur->pc);
+ o = (op>>24) & 0xF;
+ if(((op>>8) & 0xF) != 1 || o != 0xE && (o&~1) != 0xC)
+ break;
+ if(condok(ur->psr, op>>28))
+ fpemu(ur->pc, op, ur, ufp);
+ ur->pc += 4;
+ if(anyhigher())
+ sched();
+ }
+ return n;
+}
diff --git a/os/manga/gpio.c b/os/manga/gpio.c
new file mode 100644
index 00000000..c6d0179d
--- /dev/null
+++ b/os/manga/gpio.c
@@ -0,0 +1,75 @@
+#include "u.h"
+#include "mem.h"
+#include "../port/lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+static ulong gpioreserved;
+static Lock gpiolock;
+
+void
+gpioreserve(int n)
+{
+ ulong mask;
+
+ mask = 1<<n;
+ ilock(&gpiolock);
+ if(gpioreserved & mask)
+ panic("gpioreserve: duplicate use of GPIO %d", n);
+ gpioreserved |= mask;
+ iunlock(&gpiolock);
+}
+
+/*
+ * set direction and alternative function bits in the GPIO control register,
+ * following the configuration bits in cfg.
+ */
+void
+gpioconfig(int n, ulong cfg)
+{
+ GpioReg *g;
+
+ ilock(&gpiolock);
+ g = GPIOREG;
+ if(cfg & Gpio_out)
+ g->iopm |= 1<<n;
+ else
+ g->iopm &= ~(1<<n);
+ iunlock(&gpiolock);
+}
+
+ulong
+gpioget(int n)
+{
+ return GPIOREG->iopd & (1<<n);
+}
+
+void
+gpioset(int n, int v)
+{
+ GpioReg *g;
+ ulong mask;
+
+ mask = 1<<n;
+ ilock(&gpiolock);
+ g = GPIOREG;
+ if(v)
+ g->iopd |= mask;
+ else
+ g->iopd &= ~mask;
+ iunlock(&gpiolock);
+}
+
+void
+gpiorelease(int n)
+{
+ ulong mask;
+
+ mask = 1<<n;
+ ilock(&gpiolock);
+ if((gpioreserved & mask) != mask)
+ panic("gpiorelease: unexpected release of GPIO %d", n);
+ gpioreserved &= ~mask;
+ iunlock(&gpiolock);
+}
diff --git a/os/manga/inb.c b/os/manga/inb.c
new file mode 100644
index 00000000..26c45825
--- /dev/null
+++ b/os/manga/inb.c
@@ -0,0 +1,85 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define KIOP(port) KADDR(PHYSPCIIO+(port))
+
+int
+inb(ulong p)
+{
+ return *(uchar*)KIOP(p);
+}
+
+int
+ins(ulong p)
+{
+ return *(ushort*)KIOP(p);
+}
+
+ulong
+inl(ulong p)
+{
+ return *(ulong*)KIOP(p);
+}
+
+void
+outb(ulong p, int v)
+{
+ *(uchar*)KIOP(p) = v;
+}
+
+void
+outs(ulong p, int v)
+{
+ *(ushort*)KIOP(p) = v;
+}
+
+void
+outl(ulong p, ulong v)
+{
+ *(ulong*)KIOP(p) = v;
+}
+
+void
+inss(ulong p, void* buf, int ns)
+{
+ ushort *addr;
+
+ addr = (ushort*)buf;
+ for(;ns > 0; ns--)
+ *addr++ = *(ushort*)KIOP(p);
+}
+
+void
+outss(ulong p, void* buf, int ns)
+{
+ ushort *addr;
+
+ addr = (ushort*)buf;
+ for(;ns > 0; ns--)
+ *(ushort*)KIOP(p) = *addr++;
+}
+
+void
+insb(ulong p, void* buf, int ns)
+{
+ uchar *addr;
+
+ addr = (uchar*)buf;
+ for(;ns > 0; ns--)
+ *addr++ = *(uchar*)KIOP(p);
+}
+
+void
+outsb(ulong p, void* buf, int ns)
+{
+ uchar *addr;
+
+ addr = (uchar*)buf;
+ for(;ns > 0; ns--)
+ *(uchar*)KIOP(p) = *addr++;
+}
diff --git a/os/manga/io.h b/os/manga/io.h
new file mode 100644
index 00000000..6e53acc1
--- /dev/null
+++ b/os/manga/io.h
@@ -0,0 +1,320 @@
+typedef struct BD BD;
+typedef struct Ring Ring;
+
+/*
+ * types of interrupts
+ */
+enum
+{
+ /* some flags to change polarity and sensitivity */
+ IRQmask= 0xFF, /* actual vector address */
+ IRQactivelow= 0<<8,
+ IRQactivehigh= 1<<8,
+ IRQrising= 2<<8,
+ IRQfalling= 4<<8,
+ IRQmode= IRQactivelow | IRQactivehigh | IRQrising | IRQfalling,
+ IRQsoft= 1<<11, /* configure ext0 to ext3 as GPIO output */
+ IRQ= 0, /* notional bus */
+};
+
+enum {
+ IRQwmlc= 31, /* WAN link changed (edge) */
+ IRQwmts= 30, /* WAN MAC transmit status (edge) */
+ IRQwmrs= 29, /* WAN MAC receive status (edge) */
+ IRQwmtbu= 28, /* WAN MAC transmit buffer unavailable (edge) */
+ IRQwmrbu= 27, /* WAN MAC receive buffer unavailable (edge) */
+ IRQwmtps= 26, /* WAN MAC transmit process stopped (edge) */
+ IRQwmrps= 25, /* WAN MAC receive process stopped (edge) */
+ IRQaber= 24, /* AMBA bus error (level) */
+ IRQlmts= 17, /* LAN MAC transmit status (edge) */
+ IRQlmrs= 16, /* LAN MAC receive status (edge) */
+ IRQlmtbu= 15, /* LAN AMC transmit buffer unavailable (edge) */
+ IRQlmrbu= 14, /* LAN MAC receive buffer unavailable (edge) */
+ IRQlmtps= 13, /* LAN MAC transmit process stopped (edge) */
+ IRQlmrps= 12, /* LAN MAC receive process stopped (edge) */
+ IRQums= 11, /* UART modem status (level) */
+ IRQule= 10, /* UART line status (level) */
+ IRQurs= 9, /* UART receive status (level) */
+ IRQuts= 8, /* UART transmit status (level) */
+ IRQtm1= 7, /* timer 1 (edge) */
+ IRQtm0= 6, /* timer 0 (edge) */
+ IRQext3= 5, /* external interrupts (gpio control selects edge or level) */
+ IRQext2= 4,
+ IRQext1= 3,
+ IRQext0= 2,
+ IRQccts= 1, /* comms channel transmit status (level) */
+ IRQccrs= 0, /* comms channel receive status (level) */
+};
+
+/*
+ * these are defined to keep the interface compatible with other
+ * architectures, but only BUSUNKNOWN is currently used
+ */
+#define MKBUS(t,b,d,f) (((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8))
+#define BUSFNO(tbdf) (((tbdf)>>8)&0x07)
+#define BUSDNO(tbdf) (((tbdf)>>11)&0x1F)
+#define BUSBNO(tbdf) (((tbdf)>>16)&0xFF)
+#define BUSTYPE(tbdf) ((tbdf)>>24)
+#define BUSBDF(tbdf) ((tbdf)&0x00FFFF00)
+#define BUSUNKNOWN (-1)
+
+enum {
+ BusIRQ = IRQ,
+ BusPCI,
+ MaxBus
+};
+
+#define INTRREG ((IntrReg*)PHYSINTR)
+typedef struct IntrReg IntrReg;
+struct IntrReg {
+ ulong mc; /* mode control */
+ ulong en; /* enable */
+ ulong st; /* status */
+ ulong pw; /* priority for WAN */
+ ulong pad0;
+ ulong pl; /* priority for LAN */
+ ulong pt; /* priority for timer */
+ ulong pu; /* priority for UART */
+ ulong pe; /* priority for external */
+ ulong pc; /* priority for comms channel */
+ ulong pbe; /* priority for bus error response */
+ ulong ms; /* mask status */
+ ulong hpf; /* highest priority for FIQ */
+ ulong hpi; /* highest priority for IRQ */
+};
+
+#define TIMERREG ((TimerReg*)PHYSTIMER)
+typedef struct TimerReg TimerReg;
+struct TimerReg {
+ ulong enable; /* 1<<n to enable timer n */
+ ulong count1;
+ ulong count0; /* 0 becomes watchdog if byte 0 is 0xFF */
+ ulong pulse1;
+ ulong pulse0;
+};
+
+#define GPIOREG ((GpioReg*)PHYSGPIO)
+typedef struct GpioReg GpioReg;
+struct GpioReg {
+ ulong iopm; /* mode (1=output) */
+ ulong iopc; /* control */
+ ulong iopd; /* data */
+};
+
+enum {
+ /* WLAN and BT values are probably wrong */
+ GPIO_WLAN_act_o= 7,
+ GPIO_WLAN_100_o= 8,
+ GPIO_BT_act_o= 9,
+ GPIO_BT_100_o= 10,
+ GPIO_status_orange_o= 11,
+ GPIO_status_green_o= 12,
+ GPIO_button_i= 15, /* reset button, active low */
+ GPIO_misc_mask_o= (1<<13)|(1<<14)|(1<<15)|(1<<4)|(1<<5)|(1<<6), /* no idea */
+};
+
+void gpioreserve(int);
+void gpioconfig(int, ulong);
+ulong gpioget(int);
+void gpioset(int, int);
+void gpiorelease(int);
+
+enum {
+ /* software configuration bits for gpioconfig */
+ Gpio_in= 0<<4,
+ Gpio_out= 1<<4,
+};
+
+/*
+ * Host Communication buffer descriptors
+ */
+
+struct BD {
+ ulong ctrl; /* BdBusy and rx flags */
+ ulong size; /* buffer size, also BdLast and tx flags */
+ ulong addr;
+ ulong next; /* next descriptor address */
+};
+
+enum {
+ /* ctrl */
+ BdBusy= 1<<31, /* device owns it */
+
+ RxFS= 1<<30, /* first buffer of frame */
+ RxLS= 1<<29, /* last buffer of frame */
+ RxIPE= 1<<28, /* IP checksum error */
+ RxTCPE= 1<<27, /* TCP checksum error */
+ RxUDPE= 1<<26, /* UDP checksum error */
+ RxES= 1<<25, /* error summary */
+ RxMF= 1<<24, /* multicast */
+ RxRE= 1<<19, /* physical level reported error */
+ RxTL= 1<<18, /* frame too long */
+ RxRF= 1<<17, /* runt */
+ RxCE= 1<<16, /* CRC error */
+ RxFT= 1<<15, /* =0, Ether; =1, 802.3 */
+ RxFL= 0x7FF, /* frame length */
+
+ /* size and tx flags */
+ BdWrap= 1<<25, /* wrap to base of ring */
+ TxIC= 1<<31, /* interrupt on completion */
+ TxFS= 1<<30, /* first segment */
+ TxLS= 1<<29, /* last segment */
+ TxIPG= 1<<28, /* generate IP checksum */
+ TxTCPG= 1<<27, /* generate tcp/ip checksum */
+ TxUDPG= 1<<26, /* generate udp/ip checksum */
+};
+
+BD* bdalloc(ulong);
+void bdfree(BD*, int);
+void dumpbd(char*, BD*, int);
+
+struct Ring {
+ BD* rdr; /* receive descriptor ring */
+ Block** rxb; /* receive ring buffers */
+ int rdrx; /* index into rdr */
+ int nrdre; /* length of rdr */
+
+ BD* tdr; /* transmit descriptor ring */
+ Block** txb; /* transmit ring buffers */
+ int tdrh; /* host index into tdr */
+ int tdri; /* interface index into tdr */
+ int ntdre; /* length of tdr */
+ int ntq; /* pending transmit requests */
+};
+
+#define NEXT(x, l) (((x)+1)%(l))
+#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1)
+#define HOWMANY(x, y) (((x)+((y)-1))/(y))
+#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y))
+
+int ioringinit(Ring*, int, int);
+
+enum {
+ /* DMA configuration parameters */
+
+ /* DMA Direction */
+ DmaOut= 0,
+ DmaIn= 1,
+};
+
+/*
+ * PCI support code.
+ */
+enum { /* type 0 and type 1 pre-defined header */
+ PciVID = 0x00, /* vendor ID */
+ PciDID = 0x02, /* device ID */
+ PciPCR = 0x04, /* command */
+ PciPSR = 0x06, /* status */
+ PciRID = 0x08, /* revision ID */
+ PciCCRp = 0x09, /* programming interface class code */
+ PciCCRu = 0x0A, /* sub-class code */
+ PciCCRb = 0x0B, /* base class code */
+ PciCLS = 0x0C, /* cache line size */
+ PciLTR = 0x0D, /* latency timer */
+ PciHDT = 0x0E, /* header type */
+ PciBST = 0x0F, /* BIST */
+
+ PciBAR0 = 0x10, /* base address */
+ PciBAR1 = 0x14,
+
+ PciINTL = 0x3C, /* interrupt line */
+ PciINTP = 0x3D, /* interrupt pin */
+};
+
+enum { /* type 0 pre-defined header */
+ PciBAR2 = 0x18,
+ PciBAR3 = 0x1C,
+ PciBAR4 = 0x20,
+ PciBAR5 = 0x24,
+ PciCIS = 0x28, /* cardbus CIS pointer */
+ PciSVID = 0x2C, /* subsystem vendor ID */
+ PciSID = 0x2E, /* cardbus CIS pointer */
+ PciEBAR0 = 0x30, /* expansion ROM base address */
+ PciMGNT = 0x3E, /* burst period length */
+ PciMLT = 0x3F, /* maximum latency between bursts */
+};
+
+enum { /* type 1 pre-defined header */
+ PciPBN = 0x18, /* primary bus number */
+ PciSBN = 0x19, /* secondary bus number */
+ PciUBN = 0x1A, /* subordinate bus number */
+ PciSLTR = 0x1B, /* secondary latency timer */
+ PciIBR = 0x1C, /* I/O base */
+ PciILR = 0x1D, /* I/O limit */
+ PciSPSR = 0x1E, /* secondary status */
+ PciMBR = 0x20, /* memory base */
+ PciMLR = 0x22, /* memory limit */
+ PciPMBR = 0x24, /* prefetchable memory base */
+ PciPMLR = 0x26, /* prefetchable memory limit */
+ PciPUBR = 0x28, /* prefetchable base upper 32 bits */
+ PciPULR = 0x2C, /* prefetchable limit upper 32 bits */
+ PciIUBR = 0x30, /* I/O base upper 16 bits */
+ PciIULR = 0x32, /* I/O limit upper 16 bits */
+ PciEBAR1 = 0x28, /* expansion ROM base address */
+ PciBCR = 0x3E, /* bridge control register */
+};
+
+enum { /* type 2 pre-defined header */
+ PciCBExCA = 0x10,
+ PciCBSPSR = 0x16,
+ PciCBPBN = 0x18, /* primary bus number */
+ PciCBSBN = 0x19, /* secondary bus number */
+ PciCBUBN = 0x1A, /* subordinate bus number */
+ PciCBSLTR = 0x1B, /* secondary latency timer */
+ PciCBMBR0 = 0x1C,
+ PciCBMLR0 = 0x20,
+ PciCBMBR1 = 0x24,
+ PciCBMLR1 = 0x28,
+ PciCBIBR0 = 0x2C, /* I/O base */
+ PciCBILR0 = 0x30, /* I/O limit */
+ PciCBIBR1 = 0x34, /* I/O base */
+ PciCBILR1 = 0x38, /* I/O limit */
+ PciCBSVID = 0x40, /* subsystem vendor ID */
+ PciCBSID = 0x42, /* subsystem ID */
+ PciCBLMBAR = 0x44, /* legacy mode base address */
+};
+
+typedef struct Pcisiz Pcisiz;
+struct Pcisiz
+{
+ Pcidev* dev;
+ int siz;
+ int bar;
+};
+
+typedef struct Pcidev Pcidev;
+struct Pcidev
+{
+ int tbdf; /* type+bus+device+function */
+ ushort vid; /* vendor ID */
+ ushort did; /* device ID */
+
+ uchar rid;
+ uchar ccrp;
+ uchar ccru;
+ uchar ccrb;
+
+ struct {
+ ulong bar; /* base address */
+ int size;
+ } mem[6];
+
+ struct {
+ ulong bar;
+ int size;
+ } rom;
+ uchar intl; /* interrupt line */
+
+ Pcidev* list;
+ Pcidev* link; /* next device on this bno */
+
+ Pcidev* bridge; /* down a bus */
+ struct {
+ ulong bar;
+ int size;
+ } ioa, mema;
+ ulong pcr;
+};
+
+#define PCIWINDOW 0x80000000
+#define PCIWADDR(va) (PADDR(va)+PCIWINDOW)
diff --git a/os/manga/ioring.c b/os/manga/ioring.c
new file mode 100644
index 00000000..fa3dc209
--- /dev/null
+++ b/os/manga/ioring.c
@@ -0,0 +1,72 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+/*
+ * Host Communication buffer rings
+ */
+
+/*
+ * initialise receive and transmit buffer rings
+ *
+ * the ring entries must be uncached
+ */
+
+BD*
+bdalloc(ulong nd)
+{
+ BD *b;
+
+ b = xspanalloc(nd*sizeof(*b), CACHELINESZ, 0);
+ if(b == nil)
+ panic("bdalloc");
+ return mmucacheinhib(b, nd*sizeof(*b));
+}
+
+int
+ioringinit(Ring* r, int nrdre, int ntdre)
+{
+ int i;
+
+ r->nrdre = nrdre;
+ if(r->rdr == nil)
+ r->rdr = bdalloc(nrdre);
+ if(r->rxb == nil)
+ r->rxb = malloc(nrdre*sizeof(Block*));
+ if(r->rdr == nil || r->rxb == nil)
+ return -1;
+ for(i = 0; i < nrdre; i++){
+ r->rxb[i] = nil;
+ r->rdr[i].ctrl = 0;
+ r->rdr[i].size = 0;
+ r->rdr[i].addr = 0;
+ if(i)
+ r->rdr[i-1].next = PADDR(&r->rdr[i]);
+ }
+ r->rdr[i-1].next = PADDR(&r->rdr[0]);
+ r->rdrx = 0;
+
+ r->ntdre = ntdre;
+ if(r->tdr == nil)
+ r->tdr = bdalloc(ntdre);
+ if(r->txb == nil)
+ r->txb = malloc(ntdre*sizeof(Block*));
+ if(r->tdr == nil || r->txb == nil)
+ return -1;
+ for(i = 0; i < ntdre; i++){
+ r->txb[i] = nil;
+ r->tdr[i].ctrl = 0;
+ r->tdr[i].size = 0;
+ r->tdr[i].addr = 0;
+ if(i)
+ r->tdr[i-1].next = PADDR(&r->tdr[i]);
+ }
+ r->tdr[i-1].next = PADDR(&r->tdr[0]);
+ r->tdrh = 0;
+ r->tdri = 0;
+ r->ntq = 0;
+ return 0;
+}
diff --git a/os/manga/l.s b/os/manga/l.s
new file mode 100644
index 00000000..c9fa24ec
--- /dev/null
+++ b/os/manga/l.s
@@ -0,0 +1,404 @@
+#include "mem.h"
+
+#define CPWAIT
+
+/*
+ * Entered here from the boot loader with
+ * supervisor mode, interrupts disabled;
+ * MMU and caches disabled
+ */
+
+#define LED \
+ MOVW $(PHYSGPIO+8), R6;\
+ MOVW (R6), R7;\
+ EOR $(1<<12), R7;\
+ MOVW R7, (R6)
+
+TEXT _startup(SB), $-4
+ MOVW $setR12(SB), R12 /* static base (SB) */
+ MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R1 /* ensure SVC mode with interrupts disabled */
+ MOVW R1, CPSR
+
+ /* build a temporary translation table at 4MB */
+ MOVW $0x400000, R0
+ MCR CpMMU, 0, R0, C(CpTTB), C(0), 0 /* set TTB */
+ MOVW $4096, R1
+ MOVW $0, R3
+ ORR $(1<<4), R3 /* must be one */
+ ORR $(3<<10), R3 /* supervisor rw */
+ ORR $(2<<0), R3 /* section */
+startup0:
+ BIC $0xFC000000, R3 /* wraps round, at least for 0xC00... */
+ MOVW R3, (R0)
+ ADD $4, R0
+ ADD $(1<<20), R3
+ SUB $1, R1
+ CMP $0, R1
+ BNE startup0
+ MRC CpMMU, 0, R0, C(CpControl), C(0), 0
+ ORR $CpCmmu, R0
+
+ MOVW $3, R1
+ MCR CpMMU, 0, R1, C(CpDAC), C(0) /* set domain 0 to manager */
+ BL mmuenable(SB)
+
+ MOVW $(MACHADDR+BY2PG-4), R13 /* stack; 4 bytes for link */
+ BL _relocate(SB)
+ BL main(SB)
+dead:
+ B dead
+ BL _div(SB) /* hack to get _div etc loaded */
+
+TEXT _relocate(SB), $-4
+ ORR $KZERO, R14
+ RET
+
+TEXT getcpuid(SB), $-4
+ MRC CpMMU, 0, R0, C(CpCPUID), C(0)
+ RET
+
+TEXT getcacheid(SB), $-4
+ MRC CpMMU, 0, R0, C(CpCacheID), C(1)
+ RET
+
+TEXT mmugetctl(SB), $-4
+ MRC CpMMU, 0, R0, C(CpControl), C(0)
+ RET
+
+TEXT mmugetdac(SB), $-4
+ MRC CpMMU, 0, R0, C(CpDAC), C(0)
+ RET
+
+TEXT mmugetfar(SB), $-4
+ MRC CpMMU, 0, R0, C(CpFAR), C(0)
+ RET
+
+TEXT mmugetfsr(SB), $-4
+ MRC CpMMU, 0, R0, C(CpFSR), C(0)
+ RET
+
+TEXT mmuputdac(SB), $-4
+ MCR CpMMU, 0, R0, C(CpDAC), C(0)
+ CPWAIT
+ RET
+
+TEXT mmuputfsr(SB), $-4
+ MCR CpMMU, 0, R0, C(CpFSR), C(0)
+ CPWAIT
+ RET
+
+TEXT mmuputttb(SB), $-4
+ MCR CpMMU, 0, R0, C(CpTTB), C(0)
+ CPWAIT
+ RET
+
+TEXT mmuputctl(SB), $-4
+ MCR CpMMU, 0, R0, C(CpControl), C(0)
+
+ /* drain prefetch */
+ MOVW R0,R0
+ MOVW R0,R0
+ RET
+
+TEXT tlbinvalidateall(SB), $-4
+ MCR CpMMU, 0, R0, C(CpTLBops), C(7)
+ CPWAIT
+ RET
+
+TEXT itlbinvalidate(SB), $-4
+ MCR CpMMU, 0, R0, C(CpTLBops), C(5), 1
+ CPWAIT
+ RET
+
+TEXT dtlbinvalidate(SB), $-4
+ MCR CpMMU, 0, R0, C(CpTLBops), C(6), 1
+ CPWAIT
+ RET
+
+TEXT mmuenable(SB), $-4
+
+ /* disable and invalidate all caches and TLB's before enabling MMU */
+ MCR CpMMU, 0, R1, C(CpControl), C(0)
+ BIC $(CpCDcache | CpCIcache), R1
+ MRC CpMMU, 0, R1, C(CpControl), C(0)
+ CPWAIT
+
+ MOVW $0, R1 /* disable everything */
+ MCR CpMMU, 0, R1, C(CpCacheCtl), C(7), 0 /* invalidate I&D Caches and BTB */
+ MCR CpMMU, 0, R1, C(CpCacheCtl), C(10), 4 /* drain write buffer */
+ MCR CpMMU, 0, R1, C(CpTLBops), C(7), 0 /* invalidate I&D TLB */
+
+ /* enable desired mmu mode (R0) */
+ MCR CpMMU, 0, R0, C(CpControl), C(0)
+
+ /* drain prefetch */
+ MOVW R0,R0
+ MOVW R0,R0
+ RET /* start running in remapped area */
+
+TEXT setr13(SB), $-4
+ MOVW 4(FP), R1
+
+ MOVW CPSR, R2
+ BIC $PsrMask, R2, R3
+ ORR R0, R3
+ MOVW R3, CPSR
+
+ MOVW R13, R0
+ MOVW R1, R13
+
+ MOVW R2, CPSR
+ RET
+
+TEXT vectors(SB), $-4
+ MOVW 0x18(R15), R15 /* reset */
+ MOVW 0x18(R15), R15 /* undefined */
+ MOVW 0x18(R15), R15 /* SWI */
+ MOVW 0x18(R15), R15 /* prefetch abort */
+ MOVW 0x18(R15), R15 /* data abort */
+ MOVW 0x18(R15), R15 /* reserved */
+ MOVW 0x18(R15), R15 /* IRQ */
+ MOVW 0x18(R15), R15 /* FIQ */
+
+TEXT vtable(SB), $-4
+ WORD $_vsvc(SB) /* reset, in svc mode already */
+ WORD $_vund(SB) /* undefined, switch to svc mode */
+ WORD $_vsvc(SB) /* swi, in svc mode already */
+ WORD $_vpab(SB) /* prefetch abort, switch to svc mode */
+ WORD $_vdab(SB) /* data abort, switch to svc mode */
+ WORD $_vsvc(SB) /* reserved */
+ WORD $_virq(SB) /* IRQ, switch to svc mode */
+ WORD $_vfiq(SB) /* FIQ, switch to svc mode */
+
+TEXT _vund(SB), $-4
+ MOVM.DB [R0-R3], (R13)
+ MOVW $PsrMund, R0
+ B _vswitch
+
+TEXT _vsvc(SB), $-4
+ MOVW.W R14, -4(R13)
+ MOVW CPSR, R14
+ MOVW.W R14, -4(R13)
+ BIC $PsrMask, R14
+ ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14
+ MOVW R14, CPSR
+ MOVW $PsrMsvc, R14
+ MOVW.W R14, -4(R13)
+ B _vsaveu
+
+TEXT _vpab(SB), $-4
+ MOVM.DB [R0-R3], (R13)
+ MOVW $PsrMabt, R0
+ B _vswitch
+
+TEXT _vdab(SB), $-4
+ MOVM.DB [R0-R3], (R13)
+ MOVW $(PsrMabt+1), R0
+ B _vswitch
+
+TEXT _vfiq(SB), $-4 /* FIQ */
+ MOVM.DB [R0-R3], (R13)
+ MOVW $PsrMfiq, R0
+ B _vswitch
+
+TEXT _virq(SB), $-4 /* IRQ */
+ MOVM.DB [R0-R3], (R13)
+ MOVW $PsrMirq, R0
+
+_vswitch: /* switch to svc mode */
+ MOVW SPSR, R1
+ MOVW R14, R2
+ MOVW R13, R3
+
+ MOVW CPSR, R14
+ BIC $PsrMask, R14
+ ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14
+ MOVW R14, CPSR
+
+ MOVM.DB.W [R0-R2], (R13)
+ MOVM.DB (R3), [R0-R3]
+
+_vsaveu: /* Save Registers */
+ MOVW.W R14, -4(R13) /* save link */
+ MCR CpMMU, 0, R0, C(0), C(0), 0
+
+ SUB $8, R13
+ MOVM.DB.W [R0-R12], (R13)
+
+ MOVW R0, R0 /* gratuitous noop */
+
+ MOVW $setR12(SB), R12 /* static base (SB) */
+ MOVW R13, R0 /* argument is ureg */
+ SUB $8, R13 /* space for arg+lnk*/
+ BL trap(SB)
+
+_vrfe: /* Restore Regs */
+ MOVW CPSR, R0 /* splhi on return */
+ ORR $(PsrDirq|PsrDfiq), R0, R1
+ MOVW R1, CPSR
+ ADD $(8+4*15), R13 /* [r0-R14]+argument+link */
+ MOVW (R13), R14 /* restore link */
+ MOVW 8(R13), R0
+ MOVW R0, SPSR
+ MOVM.DB.S (R13), [R0-R14] /* restore user registers */
+ MOVW R0, R0 /* gratuitous nop */
+ ADD $12, R13 /* skip saved link+type+SPSR*/
+ RFE /* MOVM.IA.S.W (R13), [R15] */
+
+TEXT splhi(SB), $-4
+ MOVW CPSR, R0
+ ORR $(PsrDirq), R0, R1
+ MOVW R1, CPSR
+ MOVW $(MACHADDR), R6
+ MOVW R14, (R6) /* m->splpc */
+ RET
+
+TEXT spllo(SB), $-4
+ MOVW CPSR, R0
+ BIC $(PsrDirq|PsrDfiq), R0, R1
+ MOVW R1, CPSR
+ RET
+
+TEXT splx(SB), $-4
+ MOVW $(MACHADDR), R6
+ MOVW R14, (R6) /* m->splpc */
+
+TEXT splxpc(SB), $-4
+ MOVW R0, R1
+ MOVW CPSR, R0
+ MOVW R1, CPSR
+ RET
+
+TEXT spldone(SB), $-4
+ RET
+
+TEXT islo(SB), $-4
+ MOVW CPSR, R0
+ AND $(PsrDirq), R0
+ EOR $(PsrDirq), R0
+ RET
+
+TEXT splfhi(SB), $-4
+ MOVW CPSR, R0
+ ORR $(PsrDfiq|PsrDirq), R0, R1
+ MOVW R1, CPSR
+ RET
+
+TEXT splflo(SB), $-4
+ MOVW CPSR, R0
+ BIC $(PsrDfiq), R0, R1
+ MOVW R1, CPSR
+ RET
+
+TEXT getcpsr(SB), $-4
+ MOVW CPSR, R0
+ RET
+
+TEXT getspsr(SB), $-4
+ MOVW SPSR, R0
+ RET
+
+TEXT getcallerpc(SB), $-4
+ MOVW 0(R13), R0
+ RET
+
+TEXT _tas(SB), $-4
+ MOVW R0, R1
+ MOVW $0xDEADDEAD, R2
+ SWPW R2, (R1), R0
+ RET
+
+TEXT setlabel(SB), $-4
+ MOVW R13, 0(R0) /* sp */
+ MOVW R14, 4(R0) /* pc */
+ MOVW $0, R0
+ RET
+
+TEXT gotolabel(SB), $-4
+ MOVW 0(R0), R13 /* sp */
+ MOVW 4(R0), R14 /* pc */
+ MOVW $1, R0
+ RET
+
+/*
+ * flush (invalidate) the whole icache
+ */
+TEXT icflushall(SB), $-4
+_icflushall:
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(5), 0 /* invalidate i-cache */
+ CPWAIT
+ RET
+
+/*
+ * invalidate part of i-cache
+ */
+TEXT icflush(SB), $-4
+ MOVW 4(FP), R1
+ CMP $(CACHESIZE/2), R1
+ BGE _icflushall /* might as well do the lot */
+ ADD R0, R1
+ BIC $(CACHELINESZ-1), R0
+icflush1:
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(5), 1 /* invalidate entry by address */
+ ADD $CACHELINESZ, R0
+ CMP R1, R0
+ BLO icflush1
+ RET
+
+/*
+ * write back whole data cache, invalidate, and drain write buffer
+ */
+TEXT dcflushall(SB), $-4
+_dcflushall:
+ MOVW $(63<<26), R1 /* index, segment 0 */
+dcflushall0:
+ MCR CpMMU, 0, R1, C(CpCacheCtl), C(14), 2 /* clean and invalidate, using index */
+ ADD $(1<<5), R1 /* segment 1 */
+ MCR CpMMU, 0, R1, C(CpCacheCtl), C(14), 2
+ ADD $(1<<5), R1 /* segment 2 */
+ MCR CpMMU, 0, R1, C(CpCacheCtl), C(14), 2
+ ADD $(1<<5), R1 /* segment 3 */
+ MCR CpMMU, 0, R1, C(CpCacheCtl), C(14), 2
+ EOR $(3<<5), R1 /* back to 0 */
+ SUB.S $(1<<26), R1
+ BCS dcflushall0
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 /* drain write buffer */
+ CPWAIT
+ RET
+
+/*
+ * write back a given region, inavlidate it, and drain write buffer
+ */
+TEXT dcflush(SB), $-4
+ MOVW 4(FP), R1
+ CMP $(CACHESIZE/2), R1
+ BGE _dcflushall
+ ADD R0, R1
+ BIC $(CACHELINESZ-1), R0
+dcflush1:
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(14), 1 /* clean and invalidate entry by address */
+ ADD $CACHELINESZ, R0
+ CMP R1, R0
+ BLO dcflush1
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 /* drain write buffer */
+ CPWAIT
+ RET
+
+/*
+ * invalidate data cache
+ */
+TEXT dcinval(SB), $-4
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(6), 0
+ CPWAIT
+ RET
+
+/* for devboot */
+TEXT gotopc(SB), $-4
+ MOVW R0, R1
+ MOVW $0, R0
+ MOVW R1, PC
+ RET
+
+TEXT idle(SB), $-4
+ MCR CpMMU, 0, R0, C(7), C(0), 4
+ RET
diff --git a/os/manga/main.c b/os/manga/main.c
new file mode 100644
index 00000000..3f1adae0
--- /dev/null
+++ b/os/manga/main.c
@@ -0,0 +1,317 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+#include "version.h"
+
+#define MAXCONF 32
+
+Mach *m = (Mach*)MACHADDR;
+Proc *up = 0;
+Vectorpage *page0 = (Vectorpage*)KZERO; /* doubly-mapped to AIVECADDR */
+Conf conf;
+
+extern ulong kerndate;
+extern int cflag;
+extern int main_pool_pcnt;
+extern int heap_pool_pcnt;
+extern int image_pool_pcnt;
+ulong cpuidlecount;
+
+char *confname[MAXCONF];
+char *confval[MAXCONF];
+int nconf;
+
+void addconf(char *, char *);
+void eepromscan(void);
+char* getconf(char*);
+
+void
+doc(char *m)
+{
+ USED(m);
+ print("%s...\n", m);
+}
+
+void
+idoc(char *m)
+{
+ serialputs(m, strlen(m)); //xdelay(1);
+}
+
+static void
+poolsizeinit(void)
+{
+ ulong nb;
+
+ nb = conf.npage*BY2PG;
+ poolsize(mainmem, (nb*main_pool_pcnt)/100, 0);
+ poolsize(heapmem, (nb*heap_pool_pcnt)/100, 0);
+ poolsize(imagmem, (nb*image_pool_pcnt)/100, 1);
+}
+
+static char *hello = "Inferno\n";
+
+void
+main(void)
+{
+ memset(edata, 0, end-edata); /* clear the BSS */
+ memset(m, 0, sizeof(Mach)); /* clear the mach struct */
+ conf.nmach = 1;
+ archreset();
+ idoc(hello);
+ /* TO DO: clock speed */
+ quotefmtinstall();
+ idoc("confinit...\n");
+ confinit();
+ idoc("xinit...\n");
+ xinit();
+ idoc("mmuinit...\n");
+ mmuinit();
+ poolsizeinit();
+ poolinit();
+ idoc("trapinit...\n");
+ trapinit();
+// dmareset();
+ idoc("printinit...\n");
+ printinit();
+ idoc("uartconsole...\n");
+ uartconsole();
+ eepromscan();
+ pcimapinit();
+ doc("clockinit");
+ clockinit();
+ doc("procinit");
+ procinit();
+// cpuidprint();
+ doc("links");
+ links();
+ doc("chandevreset");
+ chandevreset();
+iprint("delayloop = %lud\n", m->delayloop);
+
+ eve = strdup("inferno");
+
+ kbdinit();
+
+ print("%ld MHz id %8.8lux\n", (m->cpuhz+500000)/1000000, getcpuid());
+ print("\nInferno %s\n", VERSION);
+ print("Vita Nuova\n");
+ print("conf %s (%lud) jit %d\n\n",conffile, kerndate, cflag);
+
+ userinit();
+ schedinit();
+}
+
+void
+reboot(void)
+{
+ exit(0);
+}
+
+void
+halt(void)
+{
+ spllo();
+ print("cpu halted\n");
+ for(;;){
+ /* nothing to do */
+ }
+}
+
+Conf conf;
+
+void
+addconf(char *name, char *val)
+{
+ if(nconf >= MAXCONF)
+ return;
+ confname[nconf] = name;
+ confval[nconf] = val;
+ nconf++;
+}
+
+char*
+getconf(char *name)
+{
+ int i;
+
+ for(i = 0; i < nconf; i++)
+ if(cistrcmp(confname[i], name) == 0)
+ return confval[i];
+ return 0;
+}
+
+void
+confinit(void)
+{
+ ulong base;
+
+ archconfinit();
+
+ base = PGROUND((ulong)end);
+ conf.base0 = base;
+
+ conf.base1 = 0;
+ conf.npage1 = 0;
+
+ conf.npage0 = (conf.topofmem - base)/BY2PG;
+
+ conf.npage = conf.npage0 + conf.npage1;
+ conf.ialloc = (((conf.npage*(main_pool_pcnt))/100)/2)*BY2PG;
+
+ conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+ conf.nmach = 1;
+
+}
+
+void
+init0(void)
+{
+ Osenv *o;
+ char buf[2*KNAMELEN];
+
+ up->nerrlab = 0;
+
+ spllo();
+
+ if(waserror())
+ panic("init0 %r");
+ /*
+ * These are o.k. because rootinit is null.
+ * Then early kproc's will have a root and dot.
+ */
+ o = up->env;
+ o->pgrp->slash = namec("#/", Atodir, 0, 0);
+ cnameclose(o->pgrp->slash->name);
+ o->pgrp->slash->name = newcname("/");
+ o->pgrp->dot = cclone(o->pgrp->slash);
+
+ chandevinit();
+
+ if(!waserror()){
+ ksetenv("cputype", "arm", 0);
+ snprint(buf, sizeof(buf), "arm %s", conffile);
+ ksetenv("terminal", buf, 0);
+ poperror();
+ }
+
+ poperror();
+
+ disinit("/osinit.dis");
+}
+
+void
+userinit(void)
+{
+ Proc *p;
+ Osenv *o;
+
+ p = newproc();
+ o = p->env;
+
+ o->fgrp = newfgrp(nil);
+ o->pgrp = newpgrp();
+ o->egrp = newegrp();
+ kstrdup(&o->user, eve);
+
+ strcpy(p->text, "interp");
+
+ p->fpstate = FPINIT;
+
+ /*
+ * Kernel Stack
+ *
+ * N.B. The -12 for the stack pointer is important.
+ * 4 bytes for gotolabel's return PC
+ */
+ p->sched.pc = (ulong)init0;
+ p->sched.sp = (ulong)p->kstack+KSTACK-8;
+
+ ready(p);
+}
+
+void
+exit(int inpanic)
+{
+ up = 0;
+
+ /* Shutdown running devices */
+ chandevshutdown();
+
+ if(inpanic && 0){
+ print("Hit the reset button\n");
+ for(;;)
+ clockpoll();
+ }
+ archreboot();
+}
+
+static void
+linkproc(void)
+{
+ spllo();
+ if (waserror())
+ print("error() underflow: %r\n");
+ else
+ (*up->kpfun)(up->arg);
+ pexit("end proc", 1);
+}
+
+void
+kprocchild(Proc *p, void (*func)(void*), void *arg)
+{
+ p->sched.pc = (ulong)linkproc;
+ p->sched.sp = (ulong)p->kstack+KSTACK-8;
+
+ p->kpfun = func;
+ p->arg = arg;
+}
+
+void
+idlehands(void)
+{
+ cpuidlecount++;
+ idle();
+}
+
+/* stubs */
+void
+setfsr(ulong)
+{
+}
+
+ulong
+getfsr()
+{
+ return 0;
+}
+
+void
+setfcr(ulong)
+{
+}
+
+ulong
+getfcr()
+{
+ return 0;
+}
+
+void
+fpinit(void)
+{
+}
+
+void
+FPsave(void*)
+{
+}
+
+void
+FPrestore(void*)
+{
+}
diff --git a/os/manga/manga b/os/manga/manga
new file mode 100644
index 00000000..725e275e
--- /dev/null
+++ b/os/manga/manga
@@ -0,0 +1,137 @@
+dev
+ root
+ cons archmanga
+# gpio
+ mnt
+ pipe
+ prog
+ srv
+ dup
+ ssl
+# cap
+# sign
+ uart
+ ip ip ipv6 ipaux iproute arp netlog ptclbsum iprouter plan9 nullmedium pktmedium netaux
+ flash
+ ether netif netaux
+ env
+ pci pci inb
+ usb pci
+
+ip
+ il
+ tcp
+ udp
+# rudp
+# igmp
+ ipifc
+ icmp
+ icmp6
+ ipmux
+
+lib
+ interp
+ keyring
+ sec
+ mp
+ math
+ kern
+
+mod
+ math
+ sys
+ keyring
+
+port
+ alarm
+ alloc
+ allocb
+ chan
+ dev
+ dial
+ dis
+ discall
+ exception
+ exportfs
+ inferno
+ latin1
+ nocache
+ nodynld
+ parse
+ pgrp
+ print
+ proc
+ qio
+ qlock
+ random
+ sysfile
+ taslock
+ xalloc
+
+misc
+ uartks8695
+
+link
+ flashcfi8
+ ether8139
+ etherks8695
+ ethermedium
+ usbuhci
+
+code
+ int main_pool_pcnt = 50;
+ int heap_pool_pcnt = 50;
+ int image_pool_pcnt = 0;
+ int cflag = 0; /* for JIT */
+
+ int consoleprint = 1;
+ int panicreset = 0;
+
+init
+ cerf405
+
+root
+ /chan /
+ /dev /
+ /boot /
+ /env /
+ /fd /
+ /net /
+ /prog /
+ /root /
+ /nvfs /
+ /osinit.dis
+ /tmp /
+
+# files used by osinit.dis during bootstrap
+ /boot/n /
+ /boot/n/local /
+ /boot/n/remote /
+
+# authentication
+ /boot/nvfs/default /usr/inferno/keyring/default
+ /boot/dis/lib/auth.dis /dis/lib/auth.dis
+ /boot/dis/lib/ssl.dis /dis/lib/ssl.dis
+# dhcp
+ /boot/dis/lib/dhcpclient.dis /dis/lib/dhcpclient.dis
+ /boot/dis/lib/ip.dis /dis/lib/ip.dis
+
+# and other files used to poke round during development
+ /boot/dis/cat.dis /dis/cat.dis
+ /boot/dis/echo.dis /dis/echo.dis
+ /boot/dis/lib/arg.dis /dis/lib/arg.dis
+
+ /boot/dis/sh.dis /dis/sh.dis
+ /boot/dis/lib/bufio.dis /dis/lib/bufio.dis
+ /boot/dis/lib/filepat.dis /dis/lib/filepat.dis
+ /boot/dis/lib/readdir.dis /dis/lib/readdir.dis
+ /boot/dis/lib/string.dis /dis/lib/string.dis
+
+ /boot/dis/cd.dis /dis/cd.dis
+ /boot/dis/bind.dis /dis/bind.dis
+ /boot/dis/dd.dis /dis/dd.dis
+ /boot/dis/p.dis /dis/p.dis
+ /boot/dis/ls.dis /dis/ls.dis
+ /boot/dis/lib/daytime.dis /dis/lib/daytime.dis
+ /boot/dis/time.dis /dis/time.dis
+ /boot/dis/xd.dis /dis/xd.dis
diff --git a/os/manga/mem.h b/os/manga/mem.h
new file mode 100644
index 00000000..61296039
--- /dev/null
+++ b/os/manga/mem.h
@@ -0,0 +1,133 @@
+/*
+ * Memory and machine-specific definitions. Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+#define BI2BY 8 /* bits per byte */
+#define BI2WD 32 /* bits per word */
+#define BY2WD 4 /* bytes per word */
+#define BY2V 8 /* bytes per double word */
+#define BY2PG 4096 /* bytes per page */
+#define WD2PG (BY2PG/BY2WD) /* words per page */
+#define PGSHIFT 12 /* log(BY2PG) */
+#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1))
+#define PGROUND(s) ROUND(s, BY2PG)
+#define BIT(n) (1<<n)
+#define BITS(a,b) ((1<<(b+1))-(1<<a))
+
+#define MAXMACH 1 /* max # cpus system can run */
+
+/*
+ * Time
+ */
+#define HZ (100) /* clock frequency */
+#define MS2HZ (1000/HZ) /* millisec per clock tick */
+#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */
+#define MS2TK(t) ((t)/MS2HZ) /* milliseconds to ticks */
+
+/*
+ * More accurate time
+ */
+#define CLOCKFREQ 25000000
+#define MS2TMR(t) ((ulong)(((uvlong)(t)*CLOCKFREQ)/1000))
+#define US2TMR(t) ((ulong)(((uvlong)(t)*CLOCKFREQ)/1000000))
+
+/*
+ * Address spaces
+ * nearly everything maps 1-1 with physical addresses
+ * 0 to 1Mb is not mapped
+ * cache strategy varies as needed (see mmu.c)
+ */
+
+#define KZERO 0xC0000000
+#define MACHADDR (KZERO+0x00001000)
+#define KTTB (KZERO+0x00004000)
+#define KTZERO (KZERO+0x00008010)
+#define KSTACK 8192 /* Size of kernel stack */
+#define FLASHMEM 0x50000000 /* map flash to otherwise unused virtual space */
+#define UCDRAMZERO (KZERO+0x08000000) /* base of memory doubly-mapped as uncached */
+#define AIVECADDR 0xFFFF0000 /* alternative interrupt vector address (other is 0) */
+
+/*
+ * Physical addresses
+ */
+#define PHYSDRAM0 0x00000000 /* where firmware puts it */
+#define PHYSFLASH0 0x02800000 /* where firmware puts it */
+#define PHYSSCRINIT 0x03FF0000 /* address at reset */
+#define PHYSSCR PHYSSCRINIT /* where it ends up after manga firmware */
+#define PHYSBRIDGE (PHYSSCR+0x2000) /* PCI-AHB bridge configuration */
+#define PHYSMEMCR (PHYSSCR+0x4000) /* memory controller interface */
+#define PHYSWANDMA (PHYSSCR+0x6000) /* WAN DMA registers */
+#define PHYSLANDMA (PHYSSCR+0x8000) /* LAN DMA registers */
+#define PHYSUART (PHYSSCR+0xE000)
+#define PHYSINTR (PHYSSCR+0xE200) /* interrupt controller */
+#define PHYSTIMER (PHYSSCR+0xE400) /* timer registers */
+#define PHYSGPIO (PHYSSCR+0xE600)
+#define PHYSSWITCH (PHYSSCR+0xE800) /* switch engine configuration */
+#define PHYSMISC (PHYSSCR+0xEA00) /* ``miscellaneous'' registers */
+
+#define PHYSPCIBRIDGE 0x80000000 /* physical address that maps to PCI 0 */
+#define PHYSPCIIO 0x10000000 /* physical address that maps to PCI I/O space */
+
+#define CACHELINELOG 5
+#define CACHELINESZ (1<<CACHELINELOG)
+#define CACHESIZE (8*1024) /* I & D caches are the same size, 4 segment, 64-way associative */
+
+/*
+ * PSR
+ */
+#define PsrMusr 0x10 /* mode */
+#define PsrMfiq 0x11
+#define PsrMirq 0x12
+#define PsrMsvc 0x13
+#define PsrMabt 0x17
+#define PsrMund 0x1B
+#define PsrMsys 0x1F
+#define PsrMask 0x1F
+
+#define PsrDfiq 0x00000040 /* disable FIQ interrupts */
+#define PsrDirq 0x00000080 /* disable IRQ interrupts */
+
+#define PsrV 0x10000000 /* overflow */
+#define PsrC 0x20000000 /* carry/borrow/extend */
+#define PsrZ 0x40000000 /* zero */
+#define PsrN 0x80000000 /* negative/less than */
+
+/*
+ * Internal MMU coprocessor registers (ARM 922)
+ */
+#define CpCPUID 0 /* R: opcode_2 is 0*/
+#define CpCacheID 0 /* R: opcode_2 is 1 */
+#define CpControl 1 /* R/W: control (opcode_2 is 0) */
+#define CpTTB 2 /* R/W: translation table base */
+#define CpDAC 3 /* R/W: domain access control */
+#define CpFSR 5 /* R/W: fault status */
+#define CpFAR 6 /* R/W: fault address */
+#define CpCacheCtl 7 /* W: */
+#define CpTLBops 8 /* W: TLB operations */
+#define CpCacheLk 9 /* W: cache lock down */
+#define CpPID 13 /* R/W: Process ID Virtual Mapping */
+#define CpTest 15 /* R/W: test configuration */
+
+/*
+ * Coprocessors
+ */
+#define CpMMU 15
+
+/*
+ * CpControl bits
+ */
+#define CpCmmu (1<<0) /* M: MMU enable */
+#define CpCalign (1<<1) /* A: alignment fault enable */
+#define CpCDcache (1<<2) /* C: data cache on */
+#define CpCwpd (15<<3) /* W, P, D, must be one */
+#define CpCbe (1<<7) /* B: big-endian operation */
+#define CpCsystem (1<<8) /* S: system permission */
+#define CpCrom (1<<9) /* R: ROM permission */
+#define CpCIcache (1<<12) /* I: Instruction Cache on */
+#define CpCaltivec (1<<13) /* X: exception vector relocation */
+#define CpCrrobin (1<<14) /* RR: round robin replacement */
+#define CpCnotFast (1<<30) /* nF: notFastBus select */
+#define CpCasync (1<<31) /* iA: asynchronous clock select */
diff --git a/os/manga/mkfile b/os/manga/mkfile
new file mode 100644
index 00000000..a6f7a99a
--- /dev/null
+++ b/os/manga/mkfile
@@ -0,0 +1,89 @@
+<../../mkconfig
+
+#Configurable parameters
+
+CONF=manga #default configuration
+CONFLIST=manga boot
+
+SYSTARG=$OSTARG
+OBJTYPE=arm
+INSTALLDIR=$ROOT/Inferno/$OBJTYPE #path of directory where kernel is installed
+#end configurable parameters
+
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system
+
+<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $ETHERS, $VGAS, $PORT, $MISC, $LIBS, $OTHERS
+
+KTZERO=0xC0008010
+
+OBJ=\
+ l.$O\
+ clock.$O\
+ fpi.$O\
+ fpiarm.$O\
+ fpimem.$O\
+ gpio.$O\
+ ioring.$O\
+ main.$O\
+ mmu.$O\
+ trap.$O\
+ $CONF.root.$O\
+ $IP\
+ $DEVS\
+ $ETHERS\
+ $LINKS\
+ $PORT\
+ $MISC\
+ $OTHERS\
+
+LIBNAMES=${LIBS:%=lib%.a}
+LIBDIRS=$LIBS
+
+HFILES=\
+ mem.h\
+ dat.h\
+ io.h\
+
+CFLAGS=-wFV -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp
+KERNDATE=`{$NDATE}
+
+default:V: i$CONF i$CONF.p9 k.gz
+
+install:V: $INSTALLDIR/i$CONF $INSTALLDIR/i$CONF.gz $INSTALLDIR/i$CONF.p9.gz
+
+i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD -s -o $target -H5 -T$KTZERO -R4 -l $OBJ $CONF.$O $LIBFILES
+
+i$CONF.p9: $OBJ $CONF.c $CONF.root.h $LIBNAMES
+ $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c
+ $LD -o $target -T$KTZERO -R4 -l $OBJ $CONF.$O $LIBFILES
+
+i$CONF.gz: i$CONF
+ rm -f i$CONF.gz
+ gzip -9 <i$CONF >i$CONF.gz
+
+<../port/portmkfile
+CLEANEXTRA=k.gz
+
+../init/$INIT.dis: ../init/$INIT.b
+ cd ../init; mk $INIT.dis
+
+clock.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h
+devether.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h
+main.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h
+trap.$O: $ROOT/Inferno/$OBJTYPE/include/ureg.h
+
+$IP devip.$O: ../ip/ip.h
+
+
+devuart.$O: ../port/devuart.c ../port/uart.h
+ $CC $CFLAGS ../port/devuart.c
+
+k.gz: i$CONF.gz pinflate
+ cat pinflate i$CONF.gz >k.gz
+ echo burble burble >>k.gz
+
+# pepinflate: /sys/src/boot/pep/pinflate
+
+dummy:V:
diff --git a/os/manga/mmu.c b/os/manga/mmu.c
new file mode 100644
index 00000000..433289d1
--- /dev/null
+++ b/os/manga/mmu.c
@@ -0,0 +1,244 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+/*
+ * Small pages:
+ * L1: 12-bit index -> 4096 descriptors -> 16Kb
+ * L2: 8-bit index -> 256 descriptors -> 1Kb
+ * Each L2 descriptor has access permissions for 4 1Kb sub-pages.
+ *
+ * TTB + L1Tx gives address of L1 descriptor
+ * L1 descriptor gives PTBA
+ * PTBA + L2Tx gives address of L2 descriptor
+ * L2 descriptor gives PBA
+ *
+ * C & B are interpreted as follows:
+ * C=0 B=0 uncached, unbuffered, stall until data access complete
+ * C=0 B=1 uncached, buffered
+ * C=1 B=0 write-through cachable
+ * C=1 B=1 write-back cachable
+ * and the i-cache uses only the C bit (cached if non-zero).
+ */
+#define TTB(pa) ((pa) & ~0x3FFF) /* translation table base */
+#define L1x(pa) (((pa)>>20) & 0xFFF) /* L1 table index */
+#define PTBA(pa) ((pa) & ~0x3FF) /* page table base address */
+#define L2x(pa) (((pa)>>12) & 0xFF) /* L2 table index */
+#define PBA(pa) ((pa) & ~0xFFF) /* page base address */
+#define SBA(pa) ((pa) & ~0xFFFFF) /* section base address */
+
+enum {
+ /* sizes */
+ Section= 1<<20,
+ LargePage= 1<<16,
+ SmallPage= 1<<12,
+ EsmallPage= 1<<10,
+ SectionPages = Section/SmallPage,
+ PtAlign = 1<<10,
+
+ /* L1 descriptor format */
+ L1type= 3<<0, /* mask for type */
+ L1page= 1<<0, /* descriptor is for L2 pages */
+ L1section= 2<<0, /* descriptor is for section */
+ L1fpage= 3<<0, /* descriptor is for fine (1k) L2 pages */
+ L1buffered= 1<<2,
+ L1cached= 1<<3,
+ L1mbo= 1<<4, /* must be one */
+
+ /* L2 descriptor format for coarse page table */
+ L2type= 3<<0, /* mask for type */
+ L2invalid= 0<<0,
+ L2large= 1<<0, /* large page */
+ L2small= 2<<0, /* small page */
+ L2esmall= 3<<0, /* extended small page */
+ L2buffered= 1<<2,
+ L2cached= 1<<3,
+ /* then access permissions */
+ L2smallX= 1<<6,
+ L2largeX= 1<<12,
+
+ /* domains */
+ Dnone= 0,
+ Dclient= 1,
+ Dmanager= 3,
+
+ /* access permissions */
+ APsro= 0, /* supervisor ro if S|R */
+ APsrw= 1, /* supervisor rw */
+ APuro= 2, /* supervisor rw + user ro */
+ APurw= 3, /* supervisor rw + user rw */
+};
+
+#define L1dom(d) (((d) & 0xF)<<5) /* L1 domain */
+#define AP(i, v) ((v)<<(((i)*2)+4)) /* access permissions */
+#define L1AP(v) AP(3, (v))
+#define L2AP(v) AP(3, (v))|AP(2, (v))|AP(1, (v))|AP(0, (v))
+
+#define L1krw (L1AP(APsrw) | L1dom(0))
+
+/*
+ * return physical address corresponding to a given virtual address,
+ * or 0 if there is no such address
+ */
+ulong
+va2pa(void *v)
+{
+ int idx;
+ ulong pte, ste, *ttb;
+
+ idx = L1x((ulong)v);
+ ttb = (ulong*)KTTB;
+ ste = ttb[idx];
+ switch(ste & L1type) {
+ case L1section:
+ return SBA(ste)|((ulong)v & 0x000fffff);
+ case L1page:
+ pte = ((ulong *)PTBA(ste))[L2x((ulong)v)];
+ switch(pte & 3) {
+ case L2large:
+ return (pte & 0xffff0000)|((ulong)v & 0x0000ffff);
+ case L2small:
+ return (pte & 0xfffff000)|((ulong)v & 0x00000fff);
+ }
+ }
+ return 0;
+}
+
+/* for debugging */
+void
+prs(char *s)
+{
+ serialputs(s, strlen(s));
+}
+
+void
+pr16(ulong n)
+{
+ int i, c;
+
+ for(i=28; i>=0; i-=4){
+ c = (n>>i) & 0xF;
+ if(c >= 0 && c <= 9)
+ c += '0';
+ else
+ c += 'A'-10;
+ serialputc(c);
+ }
+}
+
+void
+xdelay(int n)
+{
+ int j;
+
+ for(j=0; j<1000000/4; j++)
+ n++;
+ USED(n);
+}
+
+void*
+mmuphysmap(void *va, ulong pa, ulong nbytes)
+{
+ ulong *ttb;
+ ulong p, o;
+
+ if(va == nil)
+ va = KADDR(pa);
+ p = (ulong)va;
+ if((pa|p) & (Section-1))
+ panic("kmapphys");
+ ttb = (ulong*)KTTB;
+ nbytes = (nbytes+Section-1)&~(Section-1);
+ for(o = 0; o < nbytes; o += Section)
+ ttb[L1x(p+o)] = (pa+o) | (1<<4) | L1krw | L1section;
+ return va;
+}
+
+void*
+mmukaddr(ulong pa)
+{
+ if(pa >= PHYSDRAM0 && pa < conf.topofmem)
+ return (void*)(KZERO+(pa-PHYSDRAM0));
+ return (void*)pa;
+}
+
+/*
+ * Set a 1-1 map of virtual to physical memory, except:
+ * kernel is mapped to KZERO
+ * doubly-map page0 at the alternative interrupt vector address,
+ * doubly-map physical memory at KZERO+256*MB as uncached but buffered,
+ * map flash to virtual space away from 0,
+ * disable access to 0 (nil pointers).
+ *
+ * Other section maps are added later as required by mmuphysmap.
+ */
+void
+mmuinit(void)
+{
+ int i;
+ ulong *ttb, *ptable;
+
+ ttb = (ulong*)KTTB;
+ memset(ttb, 0, 16384);
+
+ /* assume flash is first in special physical space */
+ for(i = L1x(PHYSFLASH0); i < 0x1000; i++)
+ ttb[i] = (i<<20) | L1krw | (1<<4) | L1section;
+
+ /* cached dram at normal kernel addresses */
+ for(i = 0; i < 32*MB; i += MB)
+ ttb[L1x(KZERO+i)] = (PHYSDRAM0+i) | (1<<4) | L1krw | L1section | L1cached | L1buffered;
+
+ /* aliases for uncached dram */
+ for(i = 0; i < 64*MB; i += MB)
+ ttb[L1x(UCDRAMZERO+i)] = (PHYSDRAM0+i) | L1krw | (1<<4) | L1section;
+
+ /* TO DO: make the text read only */
+
+ /* remap flash */
+ for(i=0; i<8*MB; i+=MB)
+ ttb[L1x(FLASHMEM+i)] = (PHYSFLASH0+i) | L1krw | (1<<4) | L1section; /* we'll make flash uncached for now */
+
+ /*
+ * build page table for alternative vector page, mapping trap vectors in *page0
+ */
+ ptable = xspanalloc(SectionPages*sizeof(*ptable), PtAlign, 0);
+ ptable[L2x(AIVECADDR)] = PADDR(page0) | L2AP(APsrw) | L2cached | L2buffered | L2small;
+ ttb[L1x(AIVECADDR)] = PADDR(ptable) | (1<<4) | L1page;
+
+ mmuputttb(KTTB & ~KZERO);
+ mmuputdac(Dclient);
+ mmuputctl(mmugetctl() | CpCaltivec | CpCIcache | CpCsystem | CpCwpd | CpCDcache | CpCmmu);
+ tlbinvalidateall();
+}
+
+/*
+ * flush data in a given address range to memory
+ * and invalidate the region in the instruction cache.
+ */
+int
+segflush(void *a, ulong n)
+{
+ dcflush(a, n);
+ icflush(a, n);
+ return 0;
+}
+
+/*
+ * return an uncached alias for the memory at a
+ */
+void*
+mmucacheinhib(void *a, ulong nb)
+{
+ ulong p;
+
+ if(a == nil)
+ return nil;
+ p = PADDR(a);
+ if(p & (CACHELINESZ-1))
+ panic("mmucacheinhib");
+ dcflush(a, nb);
+ return (void*)(UCDRAMZERO|PADDR(a));
+}
diff --git a/os/manga/pci.c b/os/manga/pci.c
new file mode 100644
index 00000000..7bebf05b
--- /dev/null
+++ b/os/manga/pci.c
@@ -0,0 +1,1007 @@
+/*
+ * PCI support code.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define DBG if(0) pcilog
+#undef DBG
+#define DBG if(1) iprint
+
+typedef struct Pcicfg Pcicfg;
+struct Pcicfg {
+ ulong addr;
+ ulong data;
+};
+
+static Pcicfg* pcicfg;
+static ulong* pciack;
+static ulong* pcimem;
+
+struct
+{
+ char output[16384];
+ int ptr;
+}PCICONS;
+
+int
+pcilog(char *fmt, ...)
+{
+ int n;
+ va_list arg;
+ char buf[PRINTSIZE];
+
+ va_start(arg, fmt);
+ n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+ va_end(arg);
+
+ memmove(PCICONS.output+PCICONS.ptr, buf, n);
+ PCICONS.ptr += n;
+ return n;
+}
+
+enum
+{ /* configuration mechanism #1 */
+ MaxFNO = 7,
+ MaxUBN = 255,
+};
+
+enum
+{ /* command register */
+ IOen = (1<<0),
+ MEMen = (1<<1),
+ MASen = (1<<2),
+ MemWrInv = (1<<4),
+ PErrEn = (1<<6),
+ SErrEn = (1<<8),
+};
+
+static Lock pcicfglock;
+static QLock pcicfginitlock;
+static int pcicfgmode = -1;
+static int pcimaxbno = 7;
+static int pcimaxdno;
+static Pcidev* pciroot;
+static Pcidev* pcilist;
+static Pcidev* pcitail;
+
+static int pcicfgrw32(int, int, int, int);
+static int pcicfgrw8(int, int, int, int);
+static void pcirouting(void);
+static void pcirootmap(Pcidev*);
+static void pcidumpdev(ulong);
+
+static char* bustypes[] = {
+[BusIRQ] "IRQ",
+[BusPCI] "PCI",
+};
+
+#pragma varargck type "Y" int
+
+static int
+tbdffmt(Fmt* fmt)
+{
+ char *p;
+ int l, r, type, tbdf;
+
+ if((p = malloc(READSTR)) == nil)
+ return fmtstrcpy(fmt, "(tbdfconv)");
+
+ switch(fmt->r){
+ case 'T':
+ case 'Y':
+ tbdf = va_arg(fmt->args, int);
+ type = BUSTYPE(tbdf);
+ if(type < nelem(bustypes))
+ l = snprint(p, READSTR, bustypes[type]);
+ else
+ l = snprint(p, READSTR, "%d", type);
+ snprint(p+l, READSTR-l, ".%d.%d.%d",
+ BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
+ break;
+
+ default:
+ snprint(p, READSTR, "(tbdfconv)");
+ break;
+ }
+ r = fmtstrcpy(fmt, p);
+ free(p);
+
+ return r;
+}
+
+ulong
+pcibarsize(Pcidev *p, int rno)
+{
+ ulong v, size;
+
+ v = pcicfgrw32(p->tbdf, rno, 0, 1);
+ pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0);
+ size = pcicfgrw32(p->tbdf, rno, 0, 1);
+ if(v & 1)
+ size |= 0xFFFF0000;
+ pcicfgrw32(p->tbdf, rno, v, 0);
+
+ return -(size & ~0x0F);
+}
+
+static int
+pcisizcmp(void *a, void *b)
+{
+ Pcisiz *aa, *bb;
+
+ aa = a;
+ bb = b;
+ return aa->siz - bb->siz;
+}
+
+static ulong
+pcimask(ulong v)
+{
+ ulong m;
+
+ m = BI2BY*sizeof(v);
+ for(m = 1<<(m-1); m != 0; m >>= 1) {
+ if(m & v)
+ break;
+ }
+
+ m--;
+ if((v & m) == 0)
+ return v;
+
+ v |= m;
+ return v+1;
+}
+
+static void
+pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg)
+{
+ Pcidev *p;
+ int ntb, i, size, rno, hole;
+ ulong v, mema, ioa, sioa, smema, base, limit;
+ Pcisiz *table, *tptr, *mtb, *itb;
+ extern void qsort(void*, long, long, int (*)(void*, void*));
+
+ ioa = *pioa;
+ mema = *pmema;
+
+ DBG("pcibusmap wr=%d %Y mem=%luX io=%luX\n",
+ wrreg, root->tbdf, mema, ioa);
+
+ ntb = 0;
+ for(p = root; p != nil; p = p->link)
+ ntb++;
+
+ ntb *= (PciCIS-PciBAR0)/4;
+ table = malloc(2*ntb*sizeof(Pcisiz));
+ itb = table;
+ mtb = table+ntb;
+
+ /*
+ * Build a table of sizes
+ */
+ for(p = root; p != nil; p = p->link) {
+ if(p->ccrb == 0x06) {
+ if(p->ccru == 0x04 && p->bridge != nil) {
+ sioa = ioa;
+ smema = mema;
+ pcibusmap(p->bridge, &smema, &sioa, 0);
+
+ hole = pcimask(smema-mema);
+ if(hole < (1<<20))
+ hole = 1<<20;
+ p->mema.size = hole;
+
+ hole = pcimask(sioa-ioa);
+ if(hole < (1<<12))
+ hole = 1<<12;
+
+ p->ioa.size = hole;
+
+ itb->dev = p;
+ itb->bar = -1;
+ itb->siz = p->ioa.size;
+ itb++;
+
+ mtb->dev = p;
+ mtb->bar = -1;
+ mtb->siz = p->mema.size;
+ mtb++;
+ }
+ if((pcicfgr8(p, PciHDT)&0x7f) != 0)
+ continue;
+ }
+
+ for(i = 0; i <= 5; i++) {
+ rno = PciBAR0 + i*4;
+ v = pcicfgrw32(p->tbdf, rno, 0, 1);
+ size = pcibarsize(p, rno);
+ if(size == 0)
+ continue;
+
+ if(v & 1) {
+ itb->dev = p;
+ itb->bar = i;
+ itb->siz = size;
+ itb++;
+ }
+ else {
+ mtb->dev = p;
+ mtb->bar = i;
+ mtb->siz = size;
+ mtb++;
+ }
+
+ p->mem[i].size = size;
+ }
+ }
+
+ /*
+ * Sort both tables IO smallest first, Memory largest
+ */
+ qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp);
+ tptr = table+ntb;
+ qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp);
+
+ /*
+ * Allocate IO address space on this bus
+ */
+ for(tptr = table; tptr < itb; tptr++) {
+ hole = tptr->siz;
+ if(tptr->bar == -1)
+ hole = 1<<12;
+ ioa = (ioa+hole-1) & ~(hole-1);
+
+ p = tptr->dev;
+ if(tptr->bar == -1)
+ p->ioa.bar = ioa;
+ else {
+ p->pcr |= IOen;
+ p->mem[tptr->bar].bar = ioa|1;
+ if(wrreg)
+ pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), ioa|1, 0);
+ }
+
+ ioa += tptr->siz;
+ }
+
+ /*
+ * Allocate Memory address space on this bus
+ */
+ for(tptr = table+ntb; tptr < mtb; tptr++) {
+ hole = tptr->siz;
+ if(tptr->bar == -1)
+ hole = 1<<20;
+ mema = (mema+hole-1) & ~(hole-1);
+
+ p = tptr->dev;
+ if(tptr->bar == -1)
+ p->mema.bar = mema;
+ else {
+ p->pcr |= MEMen;
+ p->mem[tptr->bar].bar = mema;
+ if(wrreg)
+ pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), mema, 0);
+ }
+ mema += tptr->siz;
+ }
+
+ *pmema = mema;
+ *pioa = ioa;
+ free(table);
+
+ if(wrreg == 0)
+ return;
+
+ /*
+ * Finally set all the bridge addresses & registers
+ */
+ for(p = root; p != nil; p = p->link) {
+ if(p->bridge == nil) {
+ pcicfgrw8(p->tbdf, PciLTR, 64, 0);
+
+ p->pcr |= MASen;
+ pcicfgrw32(p->tbdf, PciPCR, p->pcr, 0);
+ continue;
+ }
+
+ base = p->ioa.bar;
+ limit = base+p->ioa.size-1;
+ v = pcicfgrw32(p->tbdf, PciBAR3, 0, 1);
+ v = (v&0xFFFF0000)|(limit & 0xF000)|((base & 0xF000)>>8);
+ pcicfgrw32(p->tbdf, PciBAR3, v, 0);
+ v = (limit & 0xFFFF0000)|(base>>16);
+ pcicfgrw32(p->tbdf, 0x30, v, 0);
+
+ base = p->mema.bar;
+ limit = base+p->mema.size-1;
+ v = (limit & 0xFFF00000)|((base & 0xFFF00000)>>16);
+ pcicfgrw32(p->tbdf, PciBAR4, v, 0);
+
+ /*
+ * Disable memory prefetch
+ */
+ pcicfgrw32(p->tbdf, PciBAR5, 0x0000FFFF, 0);
+ pcicfgrw8(p->tbdf, PciLTR, 64, 0);
+
+ /*
+ * Enable the bridge
+ */
+ v = 0xFFFF0000 | IOen | MEMen | MASen;
+ pcicfgrw32(p->tbdf, PciPCR, v, 0);
+
+ sioa = p->ioa.bar;
+ smema = p->mema.bar;
+ pcibusmap(p->bridge, &smema, &sioa, 1);
+ }
+}
+
+static int
+pcilscan(int bno, Pcidev** list)
+{
+ Pcidev *p, *head, *tail;
+ int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
+
+ maxubn = bno;
+ head = nil;
+ tail = nil;
+ for(dno = 0; dno <= pcimaxdno; dno++){
+ maxfno = 0;
+ for(fno = 0; fno <= maxfno; fno++){
+ /*
+ * For this possible device, form the
+ * bus+device+function triplet needed to address it
+ * and try to read the vendor and device ID.
+ * If successful, allocate a device struct and
+ * start to fill it in with some useful information
+ * from the device's configuration space.
+ */
+ tbdf = MKBUS(BusPCI, bno, dno, fno);
+ l = pcicfgrw32(tbdf, PciVID, 0, 1);
+ if(l == 0xFFFFFFFF || l == 0)
+ continue;
+ p = malloc(sizeof(*p));
+ p->tbdf = tbdf;
+ p->vid = l;
+ p->did = l>>16;
+
+ if(pcilist != nil)
+ pcitail->list = p;
+ else
+ pcilist = p;
+ pcitail = p;
+
+ p->rid = pcicfgr8(p, PciRID);
+ p->ccrp = pcicfgr8(p, PciCCRp);
+ p->ccru = pcicfgr8(p, PciCCRu);
+ p->ccrb = pcicfgr8(p, PciCCRb);
+ p->pcr = pcicfgr32(p, PciPCR);
+
+ p->intl = pcicfgr8(p, PciINTL);
+
+ /*
+ * If the device is a multi-function device adjust the
+ * loop count so all possible functions are checked.
+ */
+ hdt = pcicfgr8(p, PciHDT);
+ if(hdt & 0x80)
+ maxfno = MaxFNO;
+
+ /*
+ * If appropriate, read the base address registers
+ * and work out the sizes.
+ */
+ switch(p->ccrb) {
+ case 0x01: /* mass storage controller */
+ case 0x02: /* network controller */
+ case 0x03: /* display controller */
+ case 0x04: /* multimedia device */
+ case 0x06: /* bridge device */
+ case 0x07: /* simple comm. controllers */
+ case 0x08: /* base system peripherals */
+ case 0x09: /* input devices */
+ case 0x0A: /* docking stations */
+ case 0x0B: /* processors */
+ case 0x0C: /* serial bus controllers */
+ if((hdt & 0x7F) != 0)
+ break;
+ rno = PciBAR0 - 4;
+ for(i = 0; i < nelem(p->mem); i++) {
+ rno += 4;
+ p->mem[i].bar = pcicfgr32(p, rno);
+ p->mem[i].size = pcibarsize(p, rno);
+ }
+ break;
+
+ case 0x00:
+ case 0x05: /* memory controller */
+ default:
+ break;
+ }
+
+ if(head != nil)
+ tail->link = p;
+ else
+ head = p;
+ tail = p;
+ }
+ }
+
+ *list = head;
+ for(p = head; p != nil; p = p->link){
+ /*
+ * Find PCI-PCI bridges and recursively descend the tree.
+ */
+ if(p->ccrb != 0x06 || p->ccru != 0x04)
+ continue;
+
+ /*
+ * If the secondary or subordinate bus number is not
+ * initialised try to do what the PCI BIOS should have
+ * done and fill in the numbers as the tree is descended.
+ * On the way down the subordinate bus number is set to
+ * the maximum as it's not known how many buses are behind
+ * this one; the final value is set on the way back up.
+ */
+ sbn = pcicfgr8(p, PciSBN);
+ ubn = pcicfgr8(p, PciUBN);
+
+ if(sbn == 0 || ubn == 0) {
+ sbn = maxubn+1;
+ /*
+ * Make sure memory, I/O and master enables are
+ * off, set the primary, secondary and subordinate
+ * bus numbers and clear the secondary status before
+ * attempting to scan the secondary bus.
+ *
+ * Initialisation of the bridge should be done here.
+ */
+ pcicfgw32(p, PciPCR, 0xFFFF0000);
+ l = (MaxUBN<<16)|(sbn<<8)|bno;
+ pcicfgw32(p, PciPBN, l);
+ pcicfgw16(p, PciSPSR, 0xFFFF);
+ maxubn = pcilscan(sbn, &p->bridge);
+ l = (maxubn<<16)|(sbn<<8)|bno;
+
+ pcicfgw32(p, PciPBN, l);
+ }
+ else {
+ maxubn = ubn;
+ pcilscan(sbn, &p->bridge);
+ }
+ }
+
+ return maxubn;
+}
+
+int
+pciscan(int bno, Pcidev **list)
+{
+ int ubn;
+
+ qlock(&pcicfginitlock);
+ ubn = pcilscan(bno, list);
+ qunlock(&pcicfginitlock);
+ return ubn;
+}
+
+static void
+pcicfginit(void)
+{
+ char *p;
+ int bno;
+ Pcidev **list;
+ ulong mema, ioa;
+
+ qlock(&pcicfginitlock);
+ if(pcicfgmode != -1)
+ goto out;
+
+ //pcimmap();
+
+ pcicfgmode = 1;
+ pcimaxdno = 31;
+
+ fmtinstall('Y', tbdffmt);
+
+ if(p = getconf("*pcimaxbno"))
+ pcimaxbno = strtoul(p, 0, 0);
+ if(p = getconf("*pcimaxdno"))
+ pcimaxdno = strtoul(p, 0, 0);
+
+
+ list = &pciroot;
+ for(bno = 0; bno <= pcimaxbno; bno++) {
+ int sbno = bno;
+ bno = pcilscan(bno, list);
+
+ while(*list)
+ list = &(*list)->link;
+
+ if (sbno == 0) {
+ Pcidev *pci;
+
+ /*
+ * If we have found a PCI-to-Cardbus bridge, make sure
+ * it has no valid mappings anymore.
+ */
+ pci = pciroot;
+ while (pci) {
+ if (pci->ccrb == 6 && pci->ccru == 7) {
+ ushort bcr;
+
+ /* reset the cardbus */
+ bcr = pcicfgr16(pci, PciBCR);
+ pcicfgw16(pci, PciBCR, 0x40 | bcr);
+ delay(50);
+ }
+ pci = pci->link;
+ }
+ }
+ }
+
+ if(pciroot == nil)
+ goto out;
+
+ /*
+ * Work out how big the top bus is
+ */
+ mema = 0;
+ ioa = 0;
+ pcibusmap(pciroot, &mema, &ioa, 0);
+
+ DBG("Sizes: mem=%8.8lux size=%8.8lux io=%8.8lux\n",
+ mema, pcimask(mema), ioa);
+
+ /*
+ * Align the windows and map it
+ */
+ ioa = 0x1000;
+ mema = 0;
+
+ pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa);
+
+ pcibusmap(pciroot, &mema, &ioa, 1);
+ DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa);
+
+ pcirootmap(pciroot);
+
+ pcirouting();
+
+ if(1){
+ iprint("pci bridge':\n");
+ pcidumpdev(pciroot->tbdf);
+ }
+ if(1){
+ /* see we've left */
+ ulong *p;
+ int i;
+
+ p = KADDR(PHYSBRIDGE+0x200);
+ iprint("PCI:\n");
+ for(i=0; i<10; i++)
+ iprint("%8.8lux: %8.8lux\n", p+i, p[i]);
+ }
+
+out:
+ qunlock(&pcicfginitlock);
+}
+
+static int
+pcicfgrw8(int tbdf, int rno, int data, int read)
+{
+ int o, x;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ x = -1;
+ if(BUSDNO(tbdf) > pcimaxdno)
+ return x;
+
+ lock(&pcicfglock);
+ o = (rno & 0x03)<<3;
+ rno &= ~0x03;
+ pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno;
+ if(read)
+ x = (pcicfg->data>>o) & 0xFF;
+ else
+ pcicfg->data = (pcicfg->data & ~(0xFF<<o)) | ((data & 0xFF) << o);
+ pcicfg->addr = 0;
+ unlock(&pcicfglock);
+
+ return x;
+}
+
+int
+pcicfgr8(Pcidev* pcidev, int rno)
+{
+ return pcicfgrw8(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw8(Pcidev* pcidev, int rno, int data)
+{
+ pcicfgrw8(pcidev->tbdf, rno, data, 0);
+}
+
+static int
+pcicfgrw16(int tbdf, int rno, int data, int read)
+{
+ int o, x;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ x = -1;
+ if(BUSDNO(tbdf) > pcimaxdno)
+ return x;
+
+ lock(&pcicfglock);
+ o = ((rno >> 1) & 1)<<4;
+ rno &= ~0x03;
+ pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno;
+ if(read)
+ x = (pcicfg->data>>o) & 0xFFFF;
+ else
+ pcicfg->data = (pcicfg->data & ~(0xFFFF<<o)) | ((data&0xFFFF)<<o);
+ pcicfg->addr = 0;
+ unlock(&pcicfglock);
+
+ return x;
+}
+
+int
+pcicfgr16(Pcidev* pcidev, int rno)
+{
+ return pcicfgrw16(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw16(Pcidev* pcidev, int rno, int data)
+{
+ pcicfgrw16(pcidev->tbdf, rno, data, 0);
+}
+
+static int
+pcicfgrw32(int tbdf, int rno, int data, int read)
+{
+ int x;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ x = -1;
+ if(BUSDNO(tbdf) > pcimaxdno)
+ return x;
+
+ lock(&pcicfglock);
+ rno &= ~0x03;
+ pcicfg->addr = 0x80000000|BUSBDF(tbdf)|rno;
+ if(read)
+ x = pcicfg->data;
+ else
+ pcicfg->data = data;
+ pcicfg->addr = 0;
+ unlock(&pcicfglock);
+
+ return x;
+}
+
+int
+pcicfgr32(Pcidev* pcidev, int rno)
+{
+ return pcicfgrw32(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw32(Pcidev* pcidev, int rno, int data)
+{
+ pcicfgrw32(pcidev->tbdf, rno, data, 0);
+}
+
+Pcidev*
+pcimatch(Pcidev* prev, int vid, int did)
+{
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ if(prev == nil)
+ prev = pcilist;
+ else
+ prev = prev->list;
+
+ while(prev != nil){
+ if((vid == 0 || prev->vid == vid)
+ && (did == 0 || prev->did == did))
+ break;
+ prev = prev->list;
+ }
+ return prev;
+}
+
+Pcidev*
+pcimatchtbdf(int tbdf)
+{
+ Pcidev *pcidev;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) {
+ if(pcidev->tbdf == tbdf)
+ break;
+ }
+ return pcidev;
+}
+
+uchar
+pciipin(Pcidev *pci, uchar pin)
+{
+ if (pci == nil)
+ pci = pcilist;
+
+ while (pci) {
+ uchar intl;
+
+ if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff)
+ return pci->intl;
+
+ if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0)
+ return intl;
+
+ pci = pci->list;
+ }
+ return 0;
+}
+
+static void
+pcilhinv(Pcidev* p)
+{
+ int i;
+ Pcidev *t;
+
+ if(p == nil) {
+ putstrn(PCICONS.output, PCICONS.ptr);
+ p = pciroot;
+ print("bus dev type vid did intl memory\n");
+ }
+ for(t = p; t != nil; t = t->link) {
+ print("%d %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d ",
+ BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf),
+ t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl);
+
+ for(i = 0; i < nelem(p->mem); i++) {
+ if(t->mem[i].size == 0)
+ continue;
+ print("%d:%.8lux %d ", i,
+ t->mem[i].bar, t->mem[i].size);
+ }
+ if(t->ioa.bar || t->ioa.size)
+ print("ioa:%.8lux %d ", t->ioa.bar, t->ioa.size);
+ if(t->mema.bar || t->mema.size)
+ print("mema:%.8lux %d ", t->mema.bar, t->mema.size);
+ if(t->bridge)
+ print("->%d", BUSBNO(t->bridge->tbdf));
+ print("\n");
+ }
+ while(p != nil) {
+ if(p->bridge != nil)
+ pcilhinv(p->bridge);
+ p = p->link;
+ }
+}
+
+void
+pcihinv(Pcidev* p)
+{
+ if(pcicfgmode == -1)
+ pcicfginit();
+ qlock(&pcicfginitlock);
+ pcilhinv(p);
+ qunlock(&pcicfginitlock);
+}
+
+void
+pcishutdown(void)
+{
+ Pcidev *p;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ for(p = pcilist; p != nil; p = p->list){
+ /* don't mess with the bridges */
+ if(p->ccrb == 0x06)
+ continue;
+ pciclrbme(p);
+ }
+}
+
+void
+pcisetbme(Pcidev* p)
+{
+ int pcr;
+
+ pcr = pcicfgr16(p, PciPCR);
+ pcr |= MASen;
+ pcicfgw16(p, PciPCR, pcr);
+}
+
+void
+pciclrbme(Pcidev* p)
+{
+ int pcr;
+
+ pcr = pcicfgr16(p, PciPCR);
+ pcr &= ~MASen;
+ pcicfgw16(p, PciPCR, pcr);
+}
+
+/*
+ * KS8695P specific
+ */
+
+typedef struct Pciahb Pciahb;
+struct Pciahb {
+ ulong pbm; /* bridge mode */
+ ulong pbcs; /* control and status */
+ ulong pmba; /* memory base address */
+ ulong pmbac; /* memory base address control */
+ ulong pmbam; /* memory base address mask */
+ ulong pmbat; /* memory base address translation */
+ ulong pioba; /* i/o base address */
+ ulong piobac; /* i/o base address control */
+ ulong piobam; /* i/o base address mask */
+ ulong piobat; /* i/o base address translation */
+};
+
+enum {
+ /* pbm */
+ PciHost= 1<<31, /* host bridge mode */
+ PciModePCI= 0<<29,
+ PciModeMini= 1<<29,
+ PciModeCbus= 2<<29,
+
+ /* pbcs */
+ PciReset= 1<<31,
+ PciPF4= 0<<29, /* prefetch 4, 8 or 16 words */
+ PciPF8= 1<<29,
+ PciPF16= 2<<29,
+
+ /* pmbac, piobac */
+ PciTranslate= 1<<31, /* enable downstream address translation */
+};
+
+static void
+pcidumpdev(ulong tbdf)
+{
+ int i;
+
+ for(i=0; i<0x40; i+=4)
+ iprint("[%.2x]=%.8ux\n", i, pcicfgrw32(tbdf, i, 0, 1));
+}
+
+void
+pcimapinit(void)
+{
+ Pciahb *pm;
+ int i;
+
+ pm = KADDR(PHYSBRIDGE+0x200);
+ if(1){
+ /* see what the bootstrap left */
+ ulong *p;
+
+ p = (ulong*)pm;
+ iprint("PCI:\n");
+ for(i=0; i<10; i++)
+ iprint("%8.8lux: %8.8lux\n", p+i, p[i]);
+ }
+#ifdef NOT
+ /* TO DO: soft reset */
+ putdcr(Cpc0Srr, Rpci);
+ delay(1);
+ putdcr(Cpc0Srr, 0);
+#endif
+ pm->pbcs = 0x30000000; /* prefetch limit 8 words; pci config access disable */
+
+ pcicfg = KADDR(PHYSBRIDGE+0x100);
+
+ /*
+ * AHB addresses between PHYSPCIBRIDGE and PHYSPCIBRIDGE+64Mb
+ * are mapped to PCI memory at 0.
+ */
+ pcimem = mmuphysmap(KADDR(PHYSPCIBRIDGE), PHYSPCIBRIDGE, 0x4000000);
+ pm->pmbac = 0; /* disable during update */
+ pm->pmba = PHYSPCIBRIDGE;
+ pm->pmbam = 0xFC000000;
+ pm->pmbat = 0; /* TO DO: check */
+ pm->pmbac = PciTranslate; /* enable */
+
+ /*
+ * AHB addresses between PHYSPCIIO and PHYSPCIIO+64Mb
+ * are mapped to physical memory.
+ */
+ mmuphysmap(KADDR(PHYSPCIIO), PHYSPCIIO, 64*1024);
+ pm->piobac = 0; /* disable during update */
+ pm->pioba = PHYSPCIIO;
+ pm->piobam = ~(64*1024-1);
+ pm->piobat = 0; /* TO DO: check */
+ pm->piobac = PciTranslate; /* enable */
+
+ pcicfgmode = -1;
+}
+
+static void
+pcirootmap(Pcidev *bridge)
+{
+ ulong pcsr;
+
+ /*
+ * addresses presented by a PCI device between PCIWINDOW and PCIWINDOW+64Mb
+ * are mapped to physical memory.
+ */
+ pcsr = pcicfgr32(bridge, PciPSR);
+iprint("pcsr0=%8.8lux\n", pcsr);
+ pcicfgw32(bridge, PciPSR, pcsr); /* reset error status */
+ pcsr = pcicfgr32(bridge, PciPSR);
+iprint("pcsr1=%8.8lux\n", pcsr);
+ pcicfgw32(bridge, PciBAR0, PCIWINDOW);
+}
+
+typedef struct Pciroute Pciroute;
+struct Pciroute {
+ int slot;
+ int pin;
+ int irq;
+};
+
+static Pciroute pciroutes[] = {
+ {0, 1, IRQext0}, /* bridge */
+ {5, 1, IRQext3}, /* USB */
+ {5, 2, IRQext1},
+ {5, 3, IRQext2},
+// {6, 0, IRQext3}, /* miniPCI0 (or RTL8139 for extra WAN?) */
+ {6, 1, IRQext0}, /* RTL8139 for extra WAN */
+ {7, 0, IRQext3}, /* miniPCI1 */
+ {-1, 0, IRQext0},
+};
+
+static void
+pcirouting(void)
+{
+ int i, pin, irq;
+ Pcidev *pci;
+ Pciroute *r;
+
+ for(pci = pcilist; pci != nil; pci = pci->list){
+ pin = pcicfgr8(pci, PciINTP);
+ if(pin == 0 || pin == 0xff)
+ continue;
+ irq = -1;
+ for(i=0; i<nelem(pciroutes); i++){
+ r = &pciroutes[i];
+ if(r->slot < 0 || r->slot == BUSDNO(pci->tbdf) && (r->pin == 0 || r->pin == pin)){
+ irq = r->irq;
+ break;
+ }
+ }
+ if(irq < 0)
+ continue;
+ irq |= IRQactivelow;
+ iprint("pcirouting: %Y at pin %d ", pci->tbdf, pin);
+ if(pci->intl != 0 && pci->intl != 0xFF && pci->intl != irq)
+ iprint("irq %d -> %d\n", pci->intl, irq);
+ else
+ iprint("irq %d\n", irq);
+ pcicfgw8(pci, PciINTL, irq);
+ pci->intl = irq;
+ }
+}
diff --git a/os/manga/pinflate b/os/manga/pinflate
new file mode 100644
index 00000000..c6e8428e
--- /dev/null
+++ b/os/manga/pinflate
Binary files differ
diff --git a/os/manga/trap.c b/os/manga/trap.c
new file mode 100644
index 00000000..1ac808ba
--- /dev/null
+++ b/os/manga/trap.c
@@ -0,0 +1,498 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+#define waslo(sr) (!((sr) & (PsrDirq|PsrDfiq)))
+
+enum
+{
+ MaxVector= 32, /* determined by bits per word */
+ Maxhandler= MaxVector+5 /* max number of interrupt handlers, assuming a few shared */
+};
+
+typedef struct Handler Handler;
+struct Handler {
+ void (*r)(Ureg*, void*);
+ void* a;
+ char name[KNAMELEN];
+ Handler* next;
+ int edge;
+ ulong nintr;
+ ulong ticks;
+ int maxtick;
+};
+
+static Lock veclock;
+
+static struct
+{
+ Handler *ivec[MaxVector];
+ Handler h[Maxhandler];
+ int free;
+ Handler* freelist;
+} halloc;
+
+Instr BREAK = 0xE6BAD010;
+
+int (*breakhandler)(Ureg*, Proc*);
+int (*catchdbg)(Ureg *, uint);
+
+extern void (*serwrite)(char *, int);
+
+void
+intrenable(int sort, int v, void (*r)(Ureg*, void*), void* a, char *name)
+{
+ int o, x;
+ ulong f;
+ GpioReg *g;
+ Handler *h;
+
+ USED(sort);
+ f = v;
+ v &= IRQmask;
+ if(v >= nelem(halloc.ivec))
+ panic("intrenable(%d)", v);
+ ilock(&veclock);
+ if(v >= IRQext0 && v <= IRQtm1){
+ /* need to switch GPIO pins, set mode */
+ g = GPIOREG;
+ if(v <= IRQext3){ /* choice of interrupt type */
+ o = (v-IRQext0)*4; /* b mmm */
+ g->iopc = (g->iopc & ~(7<<o)) | (1<<(o+3)) | ((f>>8)&7)<<o;
+ o = v - IRQext0;
+ if(f & IRQsoft)
+ g->iopm |= 1<<o; /* soft interrupt uses GPIO as output */
+ else
+ g->iopm &= ~(1<<o);
+ }else
+ g->iopc |= 1<<(16+(v-IRQtm0));
+ if(0)
+ iprint("v=%d iopc=%8.8lux iopm=%8.8lux\n", v, g->iopc, g->iopm);
+ }
+ if((h = halloc.freelist) == nil){
+ if(halloc.free >= Maxhandler){
+ iunlock(&veclock);
+ panic("out of interrupt handlers"); /* can't happen */
+ }
+ h = &halloc.h[halloc.free++];
+ }else
+ halloc.freelist = h->next;
+ h->r = r;
+ h->a = a;
+ strncpy(h->name, name, KNAMELEN-1);
+ h->name[KNAMELEN-1] = 0;
+ h->next = halloc.ivec[v];
+ halloc.ivec[v] = h;
+
+ /* enable the corresponding interrupt in the controller */
+ x = splfhi();
+ INTRREG->st = 1<<v;
+ INTRREG->en |= 1<<v;
+ splx(x);
+ iunlock(&veclock);
+}
+
+void
+intrdisable(int sort, int v, void (*r)(Ureg*, void*), void* a, char *name)
+{
+ int x, o;
+ GpioReg *g;
+ Handler *h, **hp;
+
+ USED(sort);
+ v &= IRQmask;
+ if(v >= nelem(halloc.ivec))
+ panic("intrdisable(%d)", v);
+ ilock(&veclock);
+ for(hp = &halloc.ivec[v]; (h = *hp) != nil; hp = &h->next)
+ if(h->r == r && h->a == a && strcmp(h->name, name) == 0){
+ *hp = h->next;
+ h->r = nil;
+ h->next = halloc.freelist;
+ halloc.freelist = h;
+ break;
+ }
+ if(halloc.ivec[v] == nil){
+ if(v >= IRQext0 && v <= IRQtm1){
+ /* need to reset GPIO pins */
+ g = GPIOREG;
+ if(v <= IRQext3){ /* choice of interrupt type */
+ o = (v-IRQext0)*4; /* b mmm */
+ g->iopc &= ~(0xF<<o);
+ g->iopm &= ~(v-IRQext0); /* force to input */
+ }else
+ g->iopc &= ~(1<<(16+(v-IRQtm0)));
+ }
+ x = splfhi();
+ INTRREG->en &= ~(1<<v);
+ splx(x);
+ }
+ iunlock(&veclock);
+}
+
+static void
+intrs(Ureg *ur, ulong ibits)
+{
+ Handler *h;
+ int i, s;
+
+ for(i=0; i<nelem(halloc.ivec) && ibits; i++)
+ if(ibits & (1<<i)){
+ h = halloc.ivec[i];
+ for(; h != nil; h = h->next){
+ INTRREG->st = 1<<i; /* reset edge; has no effect on level interrupts */
+ h->r(ur, h->a);
+ ibits &= ~(1<<i);
+ }
+ }
+ if(ibits != 0){
+ iprint("spurious irq interrupt: %8.8lux\n", ibits);
+ s = splfhi();
+ INTRREG->en &= ~ibits;
+ splx(s);
+ }
+}
+
+/*
+ * initialise R13 in each trap mode, at the start and after suspend reset.
+ */
+void
+trapstacks(void)
+{
+ setr13(PsrMfiq, m->fiqstack+nelem(m->fiqstack));
+ setr13(PsrMirq, m->irqstack+nelem(m->irqstack));
+ setr13(PsrMabt, m->abtstack+nelem(m->abtstack));
+ setr13(PsrMund, m->undstack+nelem(m->undstack));
+}
+
+void
+trapinit(void)
+{
+ IntrReg *intr;
+
+ intr = INTRREG;
+ intr->mc = 0; /* all IRQ not FIQ */
+ intr->en = 0; /* disable everything */
+ intr->st = intr->st; /* reset edges */
+
+ trapstacks();
+
+ memmove(page0->vectors, vectors, sizeof(page0->vectors));
+ memmove(page0->vtable, vtable, sizeof(page0->vtable));
+ dcflush(page0, sizeof(*page0));
+
+ icflushall();
+}
+
+static char *trapnames[PsrMask+1] = {
+ [ PsrMfiq ] "Fiq interrupt",
+ [ PsrMirq ] "Mirq interrupt",
+ [ PsrMsvc ] "SVC/SWI Exception",
+ [ PsrMabt ] "Prefetch Abort/Data Abort",
+ [ PsrMabt+1 ] "Data Abort",
+ [ PsrMund ] "Undefined instruction",
+ [ PsrMsys ] "Sys trap"
+};
+
+static char *
+trapname(int psr)
+{
+ char *s;
+
+ s = trapnames[psr & PsrMask];
+ if(s == nil)
+ s = "Undefined trap";
+ return s;
+}
+
+static void
+sys_trap_error(int type)
+{
+ char errbuf[ERRMAX];
+ sprint(errbuf, "sys: trap: %s\n", trapname(type));
+ error(errbuf);
+}
+
+static void
+faultarm(Ureg *ureg, ulong far)
+{
+ char buf[ERRMAX];
+
+ sprint(buf, "sys: trap: fault pc=%8.8lux addr=0x%lux", (ulong)ureg->pc, far);
+ if(1){
+ iprint("%s\n", buf);
+ dumpregs(ureg);
+ }
+ if(far == ~0)
+ disfault(ureg, "dereference of nil");
+ disfault(ureg, buf);
+}
+
+/*
+ * All traps come here. It might be slightly slower to have all traps call trap
+ * rather than directly vectoring the handler.
+ * However, this avoids a lot of code duplication and possible bugs.
+ * trap is called splfhi().
+ */
+void
+trap(Ureg* ureg)
+{
+ ulong far, fsr;
+ int t, itype;
+ Proc *oup;
+
+ /*
+ * All interrupts/exceptions should be resumed at ureg->pc-4,
+ * except for Data Abort which resumes at ureg->pc-8.
+ */
+ itype = ureg->type;
+ if(itype == PsrMabt+1)
+ ureg->pc -= 8;
+ else
+ ureg->pc -= 4;
+ ureg->sp = (ulong)(ureg+1);
+ if(itype == PsrMfiq){ /* fast interrupt (eg, profiler) */
+ oup = up;
+ up = nil;
+ intrs(ureg, INTRREG->ms & INTRREG->mc); /* just FIQ ones */
+ up = oup;
+ return;
+ }
+
+ /* All other traps */
+
+ if(up){
+ up->pc = ureg->pc;
+ up->dbgreg = ureg;
+ }
+ switch(itype) {
+ case PsrMirq:
+ t = m->ticks; /* CPU time per proc */
+ up = nil; /* no process at interrupt level */
+ splflo(); /* allow fast interrupts */
+ intrs(ureg, INTRREG->ms & ~INTRREG->mc); /* just IRQ */
+ up = m->proc;
+ preemption(m->ticks - t);
+ break;
+
+ case PsrMund: /* Undefined instruction */
+ if(*(ulong*)ureg->pc == BREAK && breakhandler) {
+ int s;
+ Proc *p;
+
+ p = up;
+ /* if(!waslo(ureg->psr) || ureg->pc >= (ulong)splhi && ureg->pc < (ulong)islo)
+ p = 0; */
+ s = breakhandler(ureg, p);
+ if(s == BrkSched) {
+ p->preempted = 0;
+ sched();
+ } else if(s == BrkNoSched) {
+ p->preempted = 1; /* stop it being preempted until next instruction */
+ if(up)
+ up->dbgreg = 0;
+ return;
+ }
+ break;
+ }
+ if(up == nil)
+ goto faultpanic;
+ spllo();
+ if(waserror()) {
+ if(waslo(ureg->psr) && up->type == Interp)
+ disfault(ureg, up->env->errstr);
+ setpanic();
+ dumpregs(ureg);
+ panic("%s", up->env->errstr);
+ }
+ if(!fpiarm(ureg)) {
+ dumpregs(ureg);
+ sys_trap_error(ureg->type);
+ }
+ poperror();
+ break;
+
+ case PsrMsvc: /* Jump through 0 or SWI */
+ if(waslo(ureg->psr) && up && up->type == Interp) {
+ spllo();
+ dumpregs(ureg);
+ sys_trap_error(ureg->type);
+ }
+ setpanic();
+ dumpregs(ureg);
+ panic("SVC/SWI exception");
+ break;
+
+ case PsrMabt: /* Prefetch abort */
+ if(catchdbg && catchdbg(ureg, 0))
+ break;
+ /* FALL THROUGH */
+ case PsrMabt+1: /* Data abort */
+ fsr = mmugetfsr();
+ far = mmugetfar();
+ if(fsr & (1<<9)) {
+ mmuputfsr(fsr & ~(1<<9));
+ if(catchdbg && catchdbg(ureg, fsr))
+ break;
+ print("Debug/");
+ }
+ if(waslo(ureg->psr) && up && up->type == Interp) {
+ spllo();
+ faultarm(ureg, far);
+ }
+ iprint("Data Abort: FSR %8.8luX FAR %8.8luX\n", fsr, far); xdelay(500);serialputs("\n", 1);
+ /* FALL THROUGH */
+
+ default: /* ??? */
+faultpanic:
+ setpanic();
+ dumpregs(ureg);
+ panic("exception %uX %s\n", ureg->type, trapname(ureg->type));
+ break;
+ }
+
+ splhi();
+ if(up)
+ up->dbgreg = 0; /* becomes invalid after return from trap */
+}
+
+void
+setpanic(void)
+{
+ if(breakhandler != 0) /* don't mess up debugger */
+ return;
+/*
+ INTRREG->en = 0;
+ spllo();
+*/
+ splhi();
+ GPIOREG->iopd &= ~(1<<GPIO_status_orange_o);
+ consoleprint = 1;
+ serwrite = serialputs;
+}
+
+int
+isvalid_va(void *v)
+{
+ return (ulong)v >= KZERO && (ulong)v <= (ulong)KADDR(conf.topofmem-1);
+}
+
+void
+dumplongs(char *msg, ulong *v, int n)
+{
+ int i, l;
+
+ l = 0;
+ iprint("%s at %.8p: ", msg, v);
+ for(i=0; i<n; i++){
+ if(l >= 4){
+ iprint("\n %.8p: ", v);
+ l = 0;
+ }
+ if(isvalid_va(v)){
+ iprint(" %.8lux", *v++);
+ l++;
+ }else{
+ iprint(" invalid");
+ break;
+ }
+ }
+ iprint("\n");
+}
+
+static void
+_dumpstack(Ureg *ureg)
+{
+ ulong v, *l, *estack;
+ int i;
+
+ l = (ulong*)(ureg+1);
+ if((ulong)l & 3){
+ iprint("invalid ureg/stack: %.8p\n", l);
+ return;
+ }
+ iprint("dumpstack\n");
+ print("ktrace /kernel/path %.8ux %.8ux %.8ux\n", ureg->pc, ureg->sp, ureg->r14);
+ if(up != nil && l >= (ulong*)up->kstack && l <= (ulong*)(up->kstack+KSTACK-4))
+ estack = (ulong*)(up->kstack+KSTACK);
+ else if(l >= (ulong*)m->stack && l <= (ulong*)((ulong)m+BY2PG-4))
+ estack = (ulong*)((ulong)m+BY2PG-4);
+ else{
+ iprint("unknown stack %8.8p\n", l);
+ return;
+ }
+ iprint("estackx %8.8p\n", estack);
+ i = 0;
+ for(; l<estack; l++) {
+ v = *l;
+ if(KTZERO < v && v < (ulong)etext){
+ iprint("%8.8p=%8.8lux ", l, v);
+ if(i++ == 4){
+ iprint("\n");
+ i = 0;
+ }
+ }
+ }
+ if(i)
+ print("\n");
+}
+
+void
+dumpregs(Ureg* ureg)
+{
+ print("TRAP: %s", trapname(ureg->type));
+ if((ureg->psr & PsrMask) != PsrMsvc)
+ print(" in %s", trapname(ureg->psr));
+ print("\n");
+ print("PSR %8.8uX type %2.2uX PC %8.8uX LINK %8.8uX\n",
+ ureg->psr, ureg->type, ureg->pc, ureg->link);
+ print("R14 %8.8uX R13 %8.8uX R12 %8.8uX R11 %8.8uX R10 %8.8uX\n",
+ ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
+ print("R9 %8.8uX R8 %8.8uX R7 %8.8uX R6 %8.8uX R5 %8.8uX\n",
+ ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
+ print("R4 %8.8uX R3 %8.8uX R2 %8.8uX R1 %8.8uX R0 %8.8uX\n",
+ ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
+ print("Stack is at: %8.8luX\n", ureg);
+ print("PC %8.8lux LINK %8.8lux\n", (ulong)ureg->pc, (ulong)ureg->link);
+
+ if(up)
+ print("Process stack: %8.8lux-%8.8lux\n",
+ up->kstack, up->kstack+KSTACK-4);
+ else
+ print("System stack: %8.8lux-%8.8lux\n",
+ (ulong)(m+1), (ulong)m+BY2PG-4);
+ dumplongs("stack", (ulong *)(ureg + 1), 16);
+ _dumpstack(ureg);
+}
+
+/*
+ * Fill in enough of Ureg to get a stack trace, and call a function.
+ * Used by debugging interface rdb.
+ */
+void
+callwithureg(void (*fn)(Ureg*))
+{
+ Ureg ureg;
+ ureg.pc = getcallerpc(&fn);
+ ureg.sp = (ulong)&fn;
+ ureg.r14 = 0;
+ fn(&ureg);
+}
+
+void
+dumpstack(void)
+{
+return;
+ callwithureg(_dumpstack);
+}
+
+void
+trapspecial(int (*f)(Ureg *, uint))
+{
+ catchdbg = f;
+}
diff --git a/os/manga/uartks8695.c b/os/manga/uartks8695.c
new file mode 100644
index 00000000..dd04f02d
--- /dev/null
+++ b/os/manga/uartks8695.c
@@ -0,0 +1,629 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#include "../port/uart.h"
+
+/*
+ * KS8695 uart; similar to 8250 etc but registers are slightly different,
+ * and interrupt control is quite different
+ */
+enum {
+ UartFREQ = CLOCKFREQ,
+};
+
+/*
+ * similar to i8250/16450/16550 (slight differences)
+ */
+
+enum { /* I/O ports */
+ Rbr = 0, /* Receiver Buffer (RO) */
+ Thr = 1, /* Transmitter Holding (WO) */
+ Fcr = 2, /* FIFO Control */
+ Lcr = 3, /* Line Control */
+ Mcr = 4, /* Modem Control */
+ Lsr = 5, /* Line Status */
+ Msr = 6, /* Modem Status */
+ Div = 7, /* Divisor */
+ Usr = 8, /* Status */
+};
+
+enum { /* Fcr */
+ FIFOena = 0x01, /* FIFO enable */
+ FIFOrclr = 0x02, /* clear Rx FIFO */
+ FIFOtclr = 0x04, /* clear Tx FIFO */
+ FIFO1 = 0x00, /* Rx FIFO trigger level 1 byte */
+ FIFO4 = 0x40, /* 4 bytes */
+ FIFO8 = 0x80, /* 8 bytes */
+ FIFO14 = 0xC0, /* 14 bytes */
+};
+
+enum { /* Lcr */
+ Wls5 = 0x00, /* Word Length Select 5 bits/byte */
+ Wls6 = 0x01, /* 6 bits/byte */
+ Wls7 = 0x02, /* 7 bits/byte */
+ Wls8 = 0x03, /* 8 bits/byte */
+ WlsMASK = 0x03,
+ Stb = 0x04, /* 2 stop bits */
+ Pen = 0x08, /* Parity Enable */
+ Eps = 0x10, /* Even Parity Select */
+ Stp = 0x20, /* Stick Parity */
+ Brk = 0x40, /* Break */
+ Dlab = 0x80, /* Divisor Latch Access Bit */
+};
+
+enum { /* Mcr */
+ Dtr = 0x01, /* Data Terminal Ready */
+ Rts = 0x02, /* Ready To Send */
+ Out1 = 0x04, /* UART OUT1 asserted */
+ Out2 = 0x08, /* UART OUT2 asserted */
+ Dm = 0x10, /* Diagnostic Mode loopback */
+};
+
+enum { /* Lsr */
+ Dr = 0x01, /* Data Ready */
+ Oe = 0x02, /* Overrun Error */
+ Pe = 0x04, /* Parity Error */
+ Fe = 0x08, /* Framing Error */
+ Bi = 0x10, /* Break Interrupt */
+ Thre = 0x20, /* Thr Empty */
+ Temt = 0x40, /* Tramsmitter Empty */
+ FIFOerr = 0x80, /* error in receiver FIFO */
+ LsrInput = FIFOerr|Oe|Pe|Fe|Dr|Bi, /* input status only */
+};
+
+enum { /* Msr */
+ Dcts = 0x01, /* Delta Cts */
+ Ddsr = 0x02, /* Delta Dsr */
+ Teri = 0x04, /* Trailing Edge of Ri */
+ Ddcd = 0x08, /* Delta Dcd */
+ Cts = 0x10, /* Clear To Send */
+ Dsr = 0x20, /* Data Set Ready */
+ Ri = 0x40, /* Ring Indicator */
+ Dcd = 0x80, /* Data Set Ready */
+};
+
+enum { /* Usr */
+ Uti = 0x01, /* INTST[9]=1=> =1, interrupt is timeout; =0, receive FIFO trigger */
+};
+
+typedef struct Ctlr {
+ ulong* regs;
+ int irq;
+ int iena;
+
+ Lock;
+ int fena;
+} Ctlr;
+
+extern PhysUart ks8695physuart;
+
+
+static Ctlr ks8695_ctlr[1] = {
+{ .regs = (ulong*)PHYSUART,
+ .irq = IRQuts, /* base: ts then rs, ls, ms */
+},
+};
+
+static Uart ks8695_uart[1] = {
+{ .regs = &ks8695_ctlr[0],
+ .name = "eia0",
+ .freq = UartFREQ,
+ .phys = &ks8695physuart,
+ .special= 0,
+ .next = nil, },
+};
+
+#define csr8r(c, r) ((c)->regs[(r)])
+#define csr8w(c, r, v) ((c)->regs[(r)] = (v))
+
+static long
+ks8695_status(Uart* uart, void* buf, long n, long offset)
+{
+ char *p;
+ Ctlr *ctlr;
+ uchar ier, lcr, mcr, msr;
+
+ ctlr = uart->regs;
+ p = malloc(READSTR);
+ mcr = csr8r(ctlr, Mcr);
+ msr = csr8r(ctlr, Msr);
+ ier = INTRREG->en;
+ lcr = csr8r(ctlr, Lcr);
+ snprint(p, READSTR,
+ "b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d ier=%ux\n"
+ "dev(%d) type(%d) framing(%d) overruns(%d)%s%s%s%s\n",
+
+ uart->baud,
+ uart->hup_dcd,
+ (msr & Dsr) != 0,
+ uart->hup_dsr,
+ (lcr & WlsMASK) + 5,
+ (ier & (1<<IRQums)) != 0,
+ (lcr & Pen) ? ((lcr & Eps) ? 'e': 'o'): 'n',
+ (mcr & Rts) != 0,
+ (lcr & Stb) ? 2: 1,
+ ctlr->fena,
+ ier,
+
+ uart->dev,
+ uart->type,
+ uart->ferr,
+ uart->oerr,
+ (msr & Cts) ? " cts": "",
+ (msr & Dsr) ? " dsr": "",
+ (msr & Dcd) ? " dcd": "",
+ (msr & Ri) ? " ring": ""
+ );
+ n = readstr(offset, buf, n, p);
+ free(p);
+
+ return n;
+}
+
+static void
+ks8695_fifo(Uart* uart, int level)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+
+ /*
+ * Changing the FIFOena bit in Fcr flushes data
+ * from both receive and transmit FIFOs; there's
+ * no easy way to guarantee not losing data on
+ * the receive side, but it's possible to wait until
+ * the transmitter is really empty.
+ */
+ ilock(ctlr);
+ while(!(csr8r(ctlr, Lsr) & Temt))
+ ;
+
+ /*
+ * Set the trigger level, default is the max.
+ * value.
+ */
+ ctlr->fena = level;
+ switch(level){
+ case 0:
+ break;
+ case 1:
+ level = FIFO1|FIFOena;
+ break;
+ case 4:
+ level = FIFO4|FIFOena;
+ break;
+ case 8:
+ level = FIFO8|FIFOena;
+ break;
+ default:
+ level = FIFO14|FIFOena;
+ break;
+ }
+ csr8w(ctlr, Fcr, level);
+ iunlock(ctlr);
+}
+
+static void
+ks8695_dtr(Uart* uart, int on)
+{
+ Ctlr *ctlr;
+ int r;
+
+ /*
+ * Toggle DTR.
+ */
+ ctlr = uart->regs;
+ r = csr8r(ctlr, Mcr);
+ if(on)
+ r |= Dtr;
+ else
+ r &= ~Dtr;
+ csr8w(ctlr, Mcr, r);
+}
+
+static void
+ks8695_rts(Uart* uart, int on)
+{
+ Ctlr *ctlr;
+ int r;
+
+ /*
+ * Toggle RTS.
+ */
+ ctlr = uart->regs;
+ r = csr8r(ctlr, Mcr);
+ if(on)
+ r |= Rts;
+ else
+ r &= ~Rts;
+ csr8w(ctlr, Mcr, r);
+}
+
+static void
+ks8695_modemctl(Uart* uart, int on)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ ilock(&uart->tlock);
+ if(on){
+ INTRREG->en |= 1<<IRQums; /* TO DO */
+ uart->modem = 1;
+ uart->cts = csr8r(ctlr, Msr) & Cts;
+ }
+ else{
+ INTRREG->en &= ~(1<<IRQums);
+ uart->modem = 0;
+ uart->cts = 1;
+ }
+ iunlock(&uart->tlock);
+
+ /* modem needs fifo */
+ (*uart->phys->fifo)(uart, on);
+}
+
+static int
+ks8695_parity(Uart* uart, int parity)
+{
+ int lcr;
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ lcr = csr8r(ctlr, Lcr) & ~(Eps|Pen);
+
+ switch(parity){
+ case 'e':
+ lcr |= Eps|Pen;
+ break;
+ case 'o':
+ lcr |= Pen;
+ break;
+ case 'n':
+ default:
+ break;
+ }
+ csr8w(ctlr, Lcr, lcr);
+
+ uart->parity = parity;
+
+ return 0;
+}
+
+static int
+ks8695_stop(Uart* uart, int stop)
+{
+ int lcr;
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ lcr = csr8r(ctlr, Lcr);
+ switch(stop){
+ case 1:
+ lcr &= ~Stb;
+ break;
+ case 2:
+ lcr |= Stb;
+ break;
+ default:
+ return -1;
+ }
+ csr8w(ctlr, Lcr, lcr);
+ uart->stop = stop;
+ return 0;
+}
+
+static int
+ks8695_bits(Uart* uart, int bits)
+{
+ int lcr;
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ lcr = csr8r(ctlr, Lcr) & ~WlsMASK;
+
+ switch(bits){
+ case 5:
+ lcr |= Wls5;
+ break;
+ case 6:
+ lcr |= Wls6;
+ break;
+ case 7:
+ lcr |= Wls7;
+ break;
+ case 8:
+ lcr |= Wls8;
+ break;
+ default:
+ return -1;
+ }
+ csr8w(ctlr, Lcr, lcr);
+
+ uart->bits = bits;
+
+ return 0;
+}
+
+static int
+ks8695_baud(Uart* uart, int baud)
+{
+ ulong bgc;
+ Ctlr *ctlr;
+
+ if(uart->freq == 0 || baud <= 0)
+ return -1;
+ ctlr = uart->regs;
+ bgc = (uart->freq+baud-1)/baud;
+ csr8w(ctlr, Div, bgc);
+ uart->baud = baud;
+ return 0;
+}
+
+static void
+ks8695_break(Uart* uart, int ms)
+{
+ Ctlr *ctlr;
+ int lcr;
+
+ /*
+ * Send a break.
+ */
+ if(ms == 0)
+ ms = 200;
+
+ ctlr = uart->regs;
+ lcr = csr8r(ctlr, Lcr);
+ csr8w(ctlr, Lcr, lcr|Brk);
+ tsleep(&up->sleep, return0, 0, ms);
+ csr8w(ctlr, Lcr, lcr);
+}
+
+static void
+ks8695_kick(Uart* uart)
+{
+ int i;
+ Ctlr *ctlr;
+
+ if(uart->cts == 0 || uart->blocked)
+ return;
+
+ ctlr = uart->regs;
+ for(i = 0; i < 16; i++){
+ if(!(csr8r(ctlr, Lsr) & Thre))
+ break;
+ if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
+ break;
+ csr8w(ctlr, Thr, *uart->op++);
+ }
+}
+
+static void
+ks8695_modemintr(Ureg*, void *arg)
+{
+ Ctlr *ctlr;
+ Uart *uart;
+ int old, r;
+
+ uart = arg;
+ ctlr = uart->regs;
+ r = csr8r(ctlr, Msr);
+ if(r & Dcts){
+ ilock(&uart->tlock);
+ old = uart->cts;
+ uart->cts = r & Cts;
+ if(old == 0 && uart->cts)
+ uart->ctsbackoff = 2;
+ iunlock(&uart->tlock);
+ }
+ if(r & Ddsr){
+ old = r & Dsr;
+ if(uart->hup_dsr && uart->dsr && !old)
+ uart->dohup = 1;
+ uart->dsr = old;
+ }
+ if(r & Ddcd){
+ old = r & Dcd;
+ if(uart->hup_dcd && uart->dcd && !old)
+ uart->dohup = 1;
+ uart->dcd = old;
+ }
+}
+
+static void
+ks8695_rxintr(Ureg*, void* arg)
+{
+ Ctlr *ctlr;
+ Uart *uart;
+ int lsr, r;
+
+ /* handle line error status here as well */
+ uart = arg;
+ ctlr = uart->regs;
+ while((lsr = csr8r(ctlr, Lsr) & LsrInput) != 0){
+ /*
+ * Consume any received data.
+ * If the received byte came in with a break,
+ * parity or framing error, throw it away;
+ * overrun is an indication that something has
+ * already been tossed.
+ */
+ if(lsr & (FIFOerr|Oe))
+ uart->oerr++;
+ if(lsr & Pe)
+ uart->perr++;
+ if(lsr & Fe)
+ uart->ferr++;
+ if(lsr & Dr){
+ r = csr8r(ctlr, Rbr);
+ if(!(lsr & (Bi|Fe|Pe)))
+ uartrecv(uart, r);
+ }
+ }
+}
+
+static void
+ks8695_txintr(Ureg*, void* arg)
+{
+ uartkick(arg);
+}
+
+static void
+ks8695_disable(Uart* uart)
+{
+ Ctlr *ctlr;
+
+ /*
+ * Turn off DTR and RTS, disable interrupts and fifos.
+ */
+ (*uart->phys->dtr)(uart, 0);
+ (*uart->phys->rts)(uart, 0);
+ (*uart->phys->fifo)(uart, 0);
+
+ ctlr = uart->regs;
+
+ if(ctlr->iena != 0){
+ intrdisable(IRQ, ctlr->irq, ks8695_txintr, uart, uart->name);
+ intrdisable(IRQ, ctlr->irq+1, ks8695_rxintr, uart, uart->name);
+ intrdisable(IRQ, ctlr->irq+2, ks8695_rxintr, uart, uart->name);
+ intrdisable(IRQ, ctlr->irq+3, ks8695_modemintr, uart, uart->name);
+ ctlr->iena = 0;
+ }
+}
+
+static void
+ks8695_enable(Uart* uart, int ie)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+
+ /*
+ * Enable interrupts and turn on DTR and RTS.
+ * Be careful if this is called to set up a polled serial line
+ * early on not to try to enable interrupts as interrupt-
+ * -enabling mechanisms might not be set up yet.
+ */
+ if(ctlr->iena == 0 && ie){
+ intrenable(IRQ, ctlr->irq, ks8695_txintr, uart, uart->name);
+ intrenable(IRQ, ctlr->irq+1, ks8695_rxintr, uart, uart->name);
+ intrenable(IRQ, ctlr->irq+2, ks8695_rxintr, uart, uart->name);
+ intrenable(IRQ, ctlr->irq+3, ks8695_modemintr, uart, uart->name);
+ ctlr->iena = 1;
+ }
+
+ (*uart->phys->dtr)(uart, 1);
+ (*uart->phys->rts)(uart, 1);
+}
+
+static Uart*
+ks8695_pnp(void)
+{
+ return ks8695_uart;
+}
+
+static int
+ks8695_getc(Uart *uart)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ while(!(csr8r(ctlr, Lsr)&Dr))
+ delay(1);
+ return csr8r(ctlr, Rbr);
+}
+
+static void
+ks8695_putc(Uart *uart, int c)
+{
+ serialputc(c);
+#ifdef ROT
+ int i;
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 256; i++)
+ delay(1);
+ csr8w(ctlr, Thr, c);
+ if(c == '\n')
+ while((csr8r(ctlr, Lsr) & Temt) == 0){ /* let fifo drain */
+ /* skip */
+ }
+#endif
+}
+
+PhysUart ks8695physuart = {
+ .name = "ks8695",
+ .pnp = ks8695_pnp,
+ .enable = ks8695_enable,
+ .disable = ks8695_disable,
+ .kick = ks8695_kick,
+ .dobreak = ks8695_break,
+ .baud = ks8695_baud,
+ .bits = ks8695_bits,
+ .stop = ks8695_stop,
+ .parity = ks8695_parity,
+ .modemctl = ks8695_modemctl,
+ .rts = ks8695_rts,
+ .dtr = ks8695_dtr,
+ .status = ks8695_status,
+ .fifo = ks8695_fifo,
+ .getc = ks8695_getc,
+ .putc = ks8695_putc,
+};
+
+void
+uartconsole(void)
+{
+ Uart *uart;
+
+ uart = &ks8695_uart[0];
+ (*uart->phys->enable)(uart, 0);
+ uartctl(uart, "b38400 l8 pn s1");
+ consuart = uart;
+ uart->console = 1;
+}
+
+#define UR(p,r) ((ulong*)(p))[r]
+
+void
+serialputc(int c)
+{
+ ulong *p;
+
+ if(c == 0)
+ return;
+ p = (ulong*)PHYSUART;
+ while((UR(p,Lsr) & Thre) == 0){
+ /* skip */
+ }
+ UR(p,Thr) = c;
+ if(c == '\n')
+ while((UR(p,Lsr) & Temt) == 0){ /* let fifo drain */
+ /* skip */
+ }
+}
+
+/*
+ * for iprint, just write it
+ */
+void
+serialputs(char *data, int len)
+{
+ ulong *p;
+
+ p = (ulong*)PHYSUART;
+ while(--len >= 0){
+ if(*data == '\n')
+ serialputc('\r');
+ serialputc(*data++);
+ }
+ while((UR(p,Lsr) & Temt) == 0){ /* let fifo drain */
+ /* skip */
+ }
+}
+void (*serwrite)(char*, int) = serialputs;
diff --git a/os/manga/usb.h b/os/manga/usb.h
new file mode 100644
index 00000000..0aad42c5
--- /dev/null
+++ b/os/manga/usb.h
@@ -0,0 +1,160 @@
+typedef struct Ctlr Ctlr;
+typedef struct Endpt Endpt;
+typedef struct Udev Udev;
+typedef struct Usbhost Usbhost;
+
+enum
+{
+ MaxUsb = 4, /* max number of USB Host Controller Interfaces (Usbhost*) */
+ MaxUsbDev = 32, /* max number of attached USB devices, including root hub (Udev*) */
+
+ /*
+ * USB packet definitions...
+ */
+ TokIN = 0x69,
+ TokOUT = 0xE1,
+ TokSETUP = 0x2D,
+
+ /* request type */
+ RH2D = 0<<7,
+ RD2H = 1<<7,
+ Rstandard = 0<<5,
+ Rclass = 1<<5,
+ Rvendor = 2<<5,
+ Rdevice = 0,
+ Rinterface = 1,
+ Rendpt = 2,
+ Rother = 3,
+};
+
+#define Class(csp) ((csp)&0xff)
+#define Subclass(csp) (((csp)>>8)&0xff)
+#define Proto(csp) (((csp)>>16)&0xff)
+#define CSP(c, s, p) ((c) | ((s)<<8) | ((p)<<16))
+
+/*
+ * device endpoint
+ */
+struct Endpt
+{
+ Ref;
+ Lock;
+ int x; /* index in Udev.ep */
+ int id; /* hardware endpoint address */
+ int maxpkt; /* maximum packet size (from endpoint descriptor) */
+ int data01; /* 0=DATA0, 1=DATA1 */
+ uchar eof;
+ ulong csp;
+ uchar mode; /* OREAD, OWRITE, ORDWR */
+ uchar nbuf; /* number of buffers allowed */
+ uchar iso;
+ uchar debug;
+ uchar active; /* listed for examination by interrupts */
+ int setin;
+ /* ISO related: */
+ int hz;
+ int remain; /* for packet size calculations */
+ int samplesz;
+ int sched; /* schedule index; -1 if undefined or aperiodic */
+ int pollms; /* polling interval in msec */
+ int psize; /* (remaining) size of this packet */
+ int off; /* offset into packet */
+ /* Real-time iso stuff */
+ ulong foffset; /* file offset (to detect seeks) */
+ ulong poffset; /* offset of next packet to be queued */
+ ulong toffset; /* offset associated with time */
+ vlong time; /* timeassociated with offset */
+ int buffered; /* bytes captured but unread, or written but unsent */
+ /* end ISO stuff */
+
+ Udev* dev; /* owning device */
+
+ ulong nbytes;
+ ulong nblocks;
+
+ void *private;
+
+ // all the rest could (should?) move to the driver private structure; except perhaps err
+ QLock rlock;
+ Rendez rr;
+ Queue* rq;
+ QLock wlock;
+ Rendez wr;
+ Queue* wq;
+
+ int ntd;
+ char* err; // needs to be global for unstall; fix?
+
+ Endpt* activef; /* active endpoint list */
+};
+
+/* device parameters */
+enum
+{
+ /* Udev.state */
+ Disabled = 0,
+ Attached,
+ Enabled,
+ Assigned,
+ Configured,
+
+ /* Udev.class */
+ Noclass = 0,
+ Hubclass = 9,
+};
+
+/*
+ * active USB device
+ */
+struct Udev
+{
+ Ref;
+ Lock;
+ Usbhost *uh;
+ int x; /* index in usbdev[] */
+ int busy;
+ int state;
+ int id;
+ uchar port; /* port number on connecting hub */
+ ulong csp;
+ ushort vid; /* vendor id */
+ ushort did; /* product id */
+ int ls;
+ int npt;
+ Endpt* ep[16]; /* active end points */
+ Udev* ports; /* active ports, if hub */
+ Udev* next; /* next device on this hub */
+};
+
+/*
+ * One of these per active Host Controller Interface (HCI)
+ */
+struct Usbhost
+{
+ ISAConf; /* hardware info */
+ int tbdf; /* type+busno+devno+funcno */
+
+ QLock; /* protects namespace state */
+ int idgen; /* version number to distinguish new connections */
+ Udev* dev[MaxUsbDev]; /* device endpoints managed by this HCI */
+
+ void (*init)(Usbhost*);
+ void (*interrupt)(Ureg*, void*);
+
+ void (*portinfo)(Usbhost*, char*, char*);
+ void (*portreset)(Usbhost*, int);
+ void (*portenable)(Usbhost*, int, int);
+
+ void (*epalloc)(Usbhost*, Endpt*);
+ void (*epfree)(Usbhost*, Endpt*);
+ void (*epopen)(Usbhost*, Endpt*);
+ void (*epclose)(Usbhost*, Endpt*);
+ void (*epmode)(Usbhost*, Endpt*);
+
+ long (*read)(Usbhost*, Endpt*, void*, long, vlong);
+ long (*write)(Usbhost*, Endpt*, void*, long, vlong, int);
+
+ void *ctlr;
+};
+
+extern void addusbtype(char*, int(*)(Usbhost*));
diff --git a/os/manga/usbuhci.c b/os/manga/usbuhci.c
new file mode 100644
index 00000000..12140d04
--- /dev/null
+++ b/os/manga/usbuhci.c
@@ -0,0 +1,1556 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#include "usb.h"
+
+#define todget(x) 0 /* TO DO */
+#define XPRINT if(debug)iprint
+
+static int Chatty = 0;
+static int debug = 0;
+
+static char Estalled[] = "usb endpoint stalled";
+
+/*
+ * UHCI interface registers and bits
+ */
+enum
+{
+ /* i/o space */
+ Cmd = 0,
+ Status = 2,
+ Usbintr = 4,
+ Frnum = 6,
+ Flbaseadd = 8,
+ SOFMod = 0xC,
+ Portsc0 = 0x10,
+ Portsc1 = 0x12,
+
+ /* port status */
+ Suspend = 1<<12,
+ PortReset = 1<<9,
+ SlowDevice = 1<<8,
+ ResumeDetect = 1<<6,
+ PortChange = 1<<3, /* write 1 to clear */
+ PortEnable = 1<<2,
+ StatusChange = 1<<1, /* write 1 to clear */
+ DevicePresent = 1<<0,
+
+ NFRAME = 1024,
+ FRAMESIZE= NFRAME*sizeof(ulong), /* fixed by hardware; aligned to same */
+
+ Vf = 1<<2, /* TD only */
+ IsQH = 1<<1,
+ Terminate = 1<<0,
+
+ /* TD.status */
+ SPD = 1<<29,
+ ErrLimit0 = 0<<27,
+ ErrLimit1 = 1<<27,
+ ErrLimit2 = 2<<27,
+ ErrLimit3 = 3<<27,
+ LowSpeed = 1<<26,
+ IsoSelect = 1<<25,
+ IOC = 1<<24,
+ Active = 1<<23,
+ Stalled = 1<<22,
+ DataBufferErr = 1<<21,
+ Babbling = 1<<20,
+ NAKed = 1<<19,
+ CRCorTimeout = 1<<18,
+ BitstuffErr = 1<<17,
+ AnyError = (Stalled | DataBufferErr | Babbling | NAKed | CRCorTimeout | BitstuffErr),
+
+ /* TD.dev */
+ IsDATA1 = 1<<19,
+
+ /* TD.flags (software) */
+ CancelTD= 1<<0,
+ IsoClean= 1<<2,
+};
+
+static struct
+{
+ int bit;
+ char *name;
+}
+portstatus[] =
+{
+ { Suspend, "suspend", },
+ { PortReset, "reset", },
+ { SlowDevice, "lowspeed", },
+ { ResumeDetect, "resume", },
+ { PortChange, "portchange", },
+ { PortEnable, "enable", },
+ { StatusChange, "statuschange", },
+ { DevicePresent, "present", },
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Endptx Endptx;
+typedef struct QH QH;
+typedef struct TD TD;
+
+/*
+ * software structures
+ */
+struct Ctlr
+{
+ Lock; /* protects state shared with interrupt (eg, free list) */
+ Ctlr* next;
+ Pcidev* pcidev;
+ int active;
+
+ int io;
+ ulong* frames; /* frame list */
+ ulong* frameld; /* real time load on each of the frame list entries */
+ QLock resetl; /* lock controller during USB reset */
+
+ TD* tdpool;
+ TD* freetd;
+ QH* qhpool;
+ QH* freeqh;
+
+ QH* ctlq; /* queue for control i/o */
+ QH* bwsop; /* empty bandwidth sop (to PIIX4 errata specifications) */
+ QH* bulkq; /* queue for bulk i/o (points back to bandwidth sop) */
+ QH* recvq; /* receive queues for bulk i/o */
+
+ Udev* ports[2];
+
+ struct {
+ Lock;
+ Endpt* f;
+ } activends;
+
+ long usbints; /* debugging */
+ long framenumber;
+ long frameptr;
+ long usbbogus;
+};
+
+#define IN(x) ins(ctlr->io+(x))
+#define OUT(x, v) outs(ctlr->io+(x), (v))
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+struct Endptx
+{
+ QH* epq; /* queue of TDs for this endpoint */
+
+ /* ISO related: */
+ void* tdalloc;
+ void* bpalloc;
+ uchar* bp0; /* first block in array */
+ TD * td0; /* first td in array */
+ TD * etd; /* pointer into circular list of TDs for isochronous ept */
+ TD * xtd; /* next td to be cleaned */
+};
+
+/*
+ * UHCI hardware structures, aligned on 16-byte boundary
+ */
+struct TD
+{
+ ulong link;
+ ulong status; /* controller r/w */
+ ulong dev;
+ ulong buffer;
+
+ /* software */
+ ulong flags;
+ union{
+ Block* bp; /* non-iso */
+ ulong offset; /* iso */
+ };
+ Endpt* ep;
+ TD* next;
+};
+#define TFOL(p) ((TD*)KADDR((ulong)(p) & ~(0xF|PCIWINDOW)))
+
+struct QH
+{
+ ulong head;
+ ulong entries; /* address of next TD or QH to process (updated by controller) */
+
+ /* software */
+ QH* hlink;
+ TD* first;
+ QH* next; /* free list */
+ TD* last;
+ ulong _d1; /* fillers */
+ ulong _d2;
+};
+#define QFOL(p) ((QH*)KADDR((ulong)(p) & ~(0xF|PCIWINDOW)))
+
+static TD *
+alloctd(Ctlr *ctlr)
+{
+ TD *t;
+
+ ilock(ctlr);
+ t = ctlr->freetd;
+ if(t == nil)
+ panic("alloctd"); /* TO DO */
+ ctlr->freetd = t->next;
+ t->next = nil;
+ iunlock(ctlr);
+ t->ep = nil;
+ t->bp = nil;
+ t->status = 0;
+ t->link = Terminate;
+ t->buffer = 0;
+ t->flags = 0;
+ return t;
+}
+
+static void
+freetd(Ctlr *ctlr, TD *t)
+{
+ t->ep = nil;
+ if(t->bp)
+ freeb(t->bp);
+ t->bp = nil;
+ ilock(ctlr);
+ t->buffer = 0xdeadbeef;
+ t->next = ctlr->freetd;
+ ctlr->freetd = t;
+ iunlock(ctlr);
+}
+
+static void
+dumpdata(Block *b, int n)
+{
+ int i;
+
+ XPRINT("\tb %8.8lux[%d]: ", (ulong)b->rp, n);
+ if(n > 16)
+ n = 16;
+ for(i=0; i<n; i++)
+ XPRINT(" %2.2ux", b->rp[i]);
+ XPRINT("\n");
+}
+
+static void
+dumptd(TD *t, int follow)
+{
+ int i, n;
+ char buf[20], *s;
+ TD *t0;
+
+ t0 = t;
+ while(t){
+ i = t->dev & 0xFF;
+ if(i == TokOUT || i == TokSETUP)
+ n = ((t->dev>>21) + 1) & 0x7FF;
+ else if((t->status & Active) == 0)
+ n = (t->status + 1) & 0x7FF;
+ else
+ n = 0;
+ s = buf;
+ if(t->status & Active)
+ *s++ = 'A';
+ if(t->status & Stalled)
+ *s++ = 'S';
+ if(t->status & DataBufferErr)
+ *s++ = 'D';
+ if(t->status & Babbling)
+ *s++ = 'B';
+ if(t->status & NAKed)
+ *s++ = 'N';
+ if(t->status & CRCorTimeout)
+ *s++ = 'T';
+ if(t->status & BitstuffErr)
+ *s++ = 'b';
+ if(t->status & LowSpeed)
+ *s++ = 'L';
+ *s = 0;
+ XPRINT("td %8.8lux: ", t);
+ XPRINT("l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n",
+ t->link, t->status, t->dev, t->buffer, t->bp?(ulong)t->bp->rp:0, t->flags);
+ XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n",
+ buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1);
+ if(debug && t->bp && (t->flags & CancelTD) == 0)
+ dumpdata(t->bp, n);
+ if(!follow || t->link & Terminate || t->link & IsQH)
+ break;
+ t = TFOL(t->link);
+ if(t == t0)
+ break; /* looped */
+ }
+}
+
+static TD *
+alloctde(Ctlr *ctlr, Endpt *e, int pid, int n)
+{
+ TD *t;
+ int tog, id;
+
+ t = alloctd(ctlr);
+ id = (e->x<<7)|(e->dev->x&0x7F);
+ tog = 0;
+ if(e->data01 && pid != TokSETUP)
+ tog = IsDATA1;
+ t->ep = e;
+ t->status = ErrLimit3 | Active | IOC; /* or put IOC only on last? */
+ if(e->dev->ls)
+ t->status |= LowSpeed;
+ t->dev = ((n-1)<<21) | ((id&0x7FF)<<8) | pid | tog;
+ return t;
+}
+
+static QH *
+allocqh(Ctlr *ctlr)
+{
+ QH *qh;
+
+ ilock(ctlr);
+ qh = ctlr->freeqh;
+ if(qh == nil)
+ panic("allocqh"); /* TO DO */
+ ctlr->freeqh = qh->next;
+ qh->next = nil;
+ iunlock(ctlr);
+ qh->head = Terminate;
+ qh->entries = Terminate;
+ qh->hlink = nil;
+ qh->first = nil;
+ qh->last = nil;
+ return qh;
+}
+
+static void
+freeqh(Ctlr *ctlr, QH *qh)
+{
+ ilock(ctlr);
+ qh->next = ctlr->freeqh;
+ ctlr->freeqh = qh;
+ iunlock(ctlr);
+}
+
+static void
+dumpqh(QH *q)
+{
+ int i;
+ QH *q0;
+
+ q0 = q;
+ for(i = 0; q != nil && i < 10; i++){
+ XPRINT("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries);
+ if((q->entries & Terminate) == 0)
+ dumptd(TFOL(q->entries), 1);
+ if(q->head & Terminate)
+ break;
+ if((q->head & IsQH) == 0){
+ XPRINT("head:");
+ dumptd(TFOL(q->head), 1);
+ break;
+ }
+ q = QFOL(q->head);
+ if(q == q0)
+ break; /* looped */
+ }
+}
+
+static void
+queuetd(Ctlr *ctlr, QH *q, TD *t, int vf, char *why)
+{
+ TD *lt;
+
+ for(lt = t; lt->next != nil; lt = lt->next)
+ lt->link = PCIWADDR(lt->next) | vf;
+ lt->link = Terminate;
+ ilock(ctlr);
+ XPRINT("queuetd %s: t=%p lt=%p q=%p first=%p last=%p entries=%.8lux\n",
+ why, t, lt, q, q->first, q->last, q->entries);
+ if(q->first != nil){
+ q->last->link = PCIWADDR(t) | vf;
+ q->last->next = t;
+ }else{
+ q->first = t;
+ q->entries = PCIWADDR(t);
+ }
+ q->last = lt;
+ XPRINT(" t=%p q=%p first=%p last=%p entries=%.8lux\n",
+ t, q, q->first, q->last, q->entries);
+ dumpqh(q);
+ iunlock(ctlr);
+}
+
+static void
+cleantd(Ctlr *ctlr, TD *t, int discard)
+{
+ Block *b;
+ int n, err;
+
+ XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
+ if(t->ep != nil && t->ep->debug)
+ dumptd(t, 0);
+ if(t->status & Active)
+ panic("cleantd Active");
+ err = t->status & (AnyError&~NAKed);
+ /* TO DO: on t->status&AnyError, q->entries will not have advanced */
+ if (err) {
+ XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
+// print("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
+ }
+ switch(t->dev&0xFF){
+ case TokIN:
+ if(discard || (t->flags & CancelTD) || t->ep == nil || t->ep->x!=0&&err){
+ if(t->ep != nil){
+ if(err != 0)
+ t->ep->err = err==Stalled? Estalled: Eio;
+ wakeup(&t->ep->rr); /* in case anyone cares */
+ }
+ break;
+ }
+ b = t->bp;
+ n = (t->status + 1) & 0x7FF;
+ if(n > b->lim - b->wp)
+ n = 0;
+ b->wp += n;
+ if(Chatty)
+ dumpdata(b, n);
+ t->bp = nil;
+ t->ep->nbytes += n;
+ t->ep->nblocks++;
+ qpass(t->ep->rq, b); /* TO DO: flow control */
+ wakeup(&t->ep->rr); /* TO DO */
+ break;
+ case TokSETUP:
+ XPRINT("cleanTD: TokSETUP %lux\n", &t->ep);
+ /* don't really need to wakeup: subsequent IN or OUT gives status */
+ if(t->ep != nil) {
+ wakeup(&t->ep->wr); /* TO DO */
+ XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr);
+ }
+ break;
+ case TokOUT:
+ /* TO DO: mark it done somewhere */
+ XPRINT("cleanTD: TokOut %lux\n", &t->ep);
+ if(t->ep != nil){
+ if(t->bp){
+ n = BLEN(t->bp);
+ t->ep->nbytes += n;
+ t->ep->nblocks++;
+ }
+ if(t->ep->x!=0 && err != 0)
+ t->ep->err = err==Stalled? Estalled: Eio;
+ if(--t->ep->ntd < 0)
+ panic("cleantd ntd");
+ wakeup(&t->ep->wr); /* TO DO */
+ XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr);
+ }
+ break;
+ }
+ freetd(ctlr, t);
+}
+
+static void
+cleanq(Ctlr *ctlr, QH *q, int discard, int vf)
+{
+ TD *t, *tp;
+
+ ilock(ctlr);
+ tp = nil;
+ for(t = q->first; t != nil;){
+ XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer, t->flags, t->next);
+ if(t->status & Active){
+ if(t->status & NAKed){
+ t->status = (t->status & ~NAKed) | IOC; /* ensure interrupt next frame */
+ tp = t;
+ t = t->next;
+ continue;
+ }
+ if(t->flags & CancelTD){
+ XPRINT("cancelTD: %8.8lux\n", (ulong)t);
+ t->status = (t->status & ~Active) | IOC; /* ensure interrupt next frame */
+ tp = t;
+ t = t->next;
+ continue;
+ }
+ tp = t;
+ t = t->next;
+ continue;
+ }
+ t->status &= ~IOC;
+ if (tp == nil) {
+ q->first = t->next;
+ if(q->first != nil)
+ q->entries = PCIWADDR(q->first);
+ else
+ q->entries = Terminate;
+ } else {
+ tp->next = t->next;
+ if (t->next != nil)
+ tp->link = PCIWADDR(t->next) | vf;
+ else
+ tp->link = Terminate;
+ }
+ if (q->last == t)
+ q->last = tp;
+ iunlock(ctlr);
+ cleantd(ctlr, t, discard);
+ ilock(ctlr);
+ if (tp)
+ t = tp->next;
+ else
+ t = q->first;
+ XPRINT("t = %8.8lux\n", t);
+ dumpqh(q);
+ }
+ if(q->first && q->entries != PCIWADDR(q->first)){
+ ctlr->usbbogus++;
+ q->entries = PCIWADDR(q->first);
+ }
+ iunlock(ctlr);
+}
+
+static void
+canceltds(Ctlr *ctlr, QH *q, Endpt *e)
+{
+ TD *t;
+
+ if(q != nil){
+ ilock(ctlr);
+ for(t = q->first; t != nil; t = t->next)
+ if(t->ep == e)
+ t->flags |= CancelTD;
+ iunlock(ctlr);
+ XPRINT("cancel:\n");
+ dumpqh(q);
+ }
+}
+
+static void
+eptcancel(Ctlr *ctlr, Endpt *e)
+{
+ Endptx *x;
+
+ if(e == nil)
+ return;
+ x = e->private;
+ canceltds(ctlr, x->epq, e);
+ canceltds(ctlr, ctlr->ctlq, e);
+ canceltds(ctlr, ctlr->bulkq, e);
+}
+
+static void
+eptactivate(Ctlr *ctlr, Endpt *e)
+{
+ ilock(&ctlr->activends);
+ if(e->active == 0){
+ XPRINT("activate 0x%p\n", e);
+ e->active = 1;
+ e->activef = ctlr->activends.f;
+ ctlr->activends.f = e;
+ }
+ iunlock(&ctlr->activends);
+}
+
+static void
+eptdeactivate(Ctlr *ctlr, Endpt *e)
+{
+ Endpt **l;
+
+ /* could be O(1) but not worth it yet */
+ ilock(&ctlr->activends);
+ if(e->active){
+ e->active = 0;
+ XPRINT("deactivate 0x%p\n", e);
+ for(l = &ctlr->activends.f; *l != e; l = &(*l)->activef)
+ if(*l == nil){
+ iunlock(&ctlr->activends);
+ panic("usb eptdeactivate");
+ }
+ *l = e->activef;
+ }
+ iunlock(&ctlr->activends);
+}
+
+static void
+queueqh(Ctlr *ctlr, QH *qh)
+{
+ QH *q;
+
+ // See if it's already queued
+ for (q = ctlr->recvq->next; q; q = q->hlink)
+ if (q == qh)
+ return;
+ if ((qh->hlink = ctlr->recvq->next) == nil)
+ qh->head = Terminate;
+ else
+ qh->head = PCIWADDR(ctlr->recvq->next) | IsQH;
+ ctlr->recvq->next = qh;
+ ctlr->recvq->entries = PCIWADDR(qh) | IsQH;
+}
+
+static QH*
+qxmit(Ctlr *ctlr, Endpt *e, Block *b, int pid)
+{
+ TD *t;
+ int n, vf;
+ QH *qh;
+ Endptx *x;
+
+ x = e->private;
+ if(b != nil){
+ n = BLEN(b);
+ dcflush(b->rp, n);
+ t = alloctde(ctlr, e, pid, n);
+ t->bp = b;
+ t->buffer = PCIWADDR(b->rp);
+ }else
+ t = alloctde(ctlr, e, pid, 0);
+ ilock(ctlr);
+ e->ntd++;
+ iunlock(ctlr);
+ if(e->debug) pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0);
+ vf = 0;
+ if(e->x == 0){
+ qh = ctlr->ctlq;
+ vf = 0;
+ }else if((qh = x->epq) == nil || e->mode != OWRITE){
+ qh = ctlr->bulkq;
+ vf = Vf;
+ }
+ queuetd(ctlr, qh, t, vf, "qxmit");
+ return qh;
+}
+
+/*
+ * allocate receive buffer space on cache-line boundaries
+ */
+static Block*
+clallocb(long n)
+{
+ Block *b;
+
+ b = allocb(n+CACHELINESZ-1);
+ if(b == nil)
+ return b;
+ dcflush(b->base, BALLOC(b));
+ b->wp = b->rp = (uchar*)(((ulong)b->base + CACHELINESZ - 1) & ~(CACHELINESZ-1));
+ return b;
+}
+
+static QH*
+qrcv(Ctlr *ctlr, Endpt *e)
+{
+ TD *t;
+ Block *b;
+ QH *qh;
+ int vf;
+ Endptx *x;
+
+ x = e->private;
+ t = alloctde(ctlr, e, TokIN, e->maxpkt);
+ b = clallocb(e->maxpkt);
+ t->bp = b;
+ t->buffer = PCIWADDR(b->wp);
+ vf = 0;
+ if(e->x == 0){
+ qh = ctlr->ctlq;
+ }else if((qh = x->epq) == nil || e->mode != OREAD){
+ qh = ctlr->bulkq;
+ vf = Vf;
+ }
+ queuetd(ctlr, qh, t, vf, "qrcv");
+ return qh;
+}
+
+static int
+usbsched(Ctlr *ctlr, int pollms, ulong load)
+{
+ int i, d, q;
+ ulong best, worst;
+
+ best = 1000000;
+ q = -1;
+ for (d = 0; d < pollms; d++){
+ worst = 0;
+ for (i = d; i < NFRAME; i++){
+ if (ctlr->frameld[i] + load > worst)
+ worst = ctlr->frameld[i] + load;
+ }
+ if (worst < best){
+ best = worst;
+ q = d;
+ }
+ }
+ return q;
+}
+
+static int
+schedendpt(Ctlr *ctlr, Endpt *e)
+{
+ TD *td;
+ Endptx *x;
+ uchar *bp;
+ int i, id, ix, size, frnum;
+
+ if(!e->iso || e->sched >= 0)
+ return 0;
+
+ if (e->active){
+ return -1;
+ }
+ e->off = 0;
+ e->sched = usbsched(ctlr, e->pollms, e->maxpkt);
+ if(e->sched < 0)
+ return -1;
+
+ x = e->private;
+ if (x->tdalloc || x->bpalloc)
+ panic("usb: tdalloc/bpalloc");
+ x->tdalloc = mallocz(0x10 + NFRAME*sizeof(TD) + CACHELINESZ, 1);
+ x->bpalloc = mallocz(0x10 + e->maxpkt*NFRAME/e->pollms + CACHELINESZ, 1);
+ x->td0 = mmucacheinhib((TD*)(((ulong)x->tdalloc + CACHELINESZ-1) & ~(CACHELINESZ-1)), NFRAME*sizeof(TD) + 0x10);
+ x->bp0 = mmucacheinhib((uchar *)(((ulong)x->bpalloc + CACHELINESZ-1) & ~(CACHELINESZ-1)), e->maxpkt*NFRAME/e->pollms + 0x10);
+ frnum = (IN(Frnum) + 1) & 0x3ff;
+ frnum = (frnum & ~(e->pollms - 1)) + e->sched;
+ x->xtd = &x->td0[(frnum+8)&0x3ff]; /* Next td to finish */
+ x->etd = nil;
+ e->remain = 0;
+ e->nbytes = 0;
+ td = x->td0;
+ for(i = e->sched; i < NFRAME; i += e->pollms){
+ bp = x->bp0 + e->maxpkt*i/e->pollms;
+ td->buffer = PCIWADDR(bp);
+ td->ep = e;
+ td->next = &td[1];
+ ctlr->frameld[i] += e->maxpkt;
+ td++;
+ }
+ td[-1].next = x->td0;
+ for(i = e->sched; i < NFRAME; i += e->pollms){
+ ix = (frnum+i) & 0x3ff;
+ td = &x->td0[ix];
+
+ id = (e->x<<7)|(e->dev->x&0x7F);
+ if (e->mode == OREAD)
+ /* enable receive on this entry */
+ td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | TokIN;
+ else{
+ size = (e->hz + e->remain)*e->pollms/1000;
+ e->remain = (e->hz + e->remain)*e->pollms%1000;
+ size *= e->samplesz;
+ td->dev = ((size-1)<<21) | ((id&0x7FF)<<8) | TokOUT;
+ }
+ td->status = ErrLimit1 | Active | IsoSelect | IOC;
+ td->link = ctlr->frames[ix];
+ td->flags |= IsoClean;
+ ctlr->frames[ix] = PCIWADDR(td);
+ }
+ return 0;
+}
+
+static void
+unschedendpt(Ctlr *ctlr, Endpt *e)
+{
+ int q;
+ TD *td;
+ Endptx *x;
+ ulong *addr;
+
+ if(!e->iso || e->sched < 0)
+ return;
+
+ x = e->private;
+ if (x->tdalloc == nil)
+ panic("tdalloc");
+ for (q = e->sched; q < NFRAME; q += e->pollms){
+ td = x->td0++;
+ addr = &ctlr->frames[q];
+ while(*addr != PADDR(td)) {
+ if(*addr & IsQH)
+ panic("usb: TD expected");
+ addr = &TFOL(*addr)->link;
+ }
+ *addr = td->link;
+ ctlr->frameld[q] -= e->maxpkt;
+ }
+ free(x->tdalloc);
+ free(x->bpalloc);
+ x->tdalloc = nil;
+ x->bpalloc = nil;
+ x->etd = nil;
+ x->td0 = nil;
+ e->sched = -1;
+}
+
+static void
+epalloc(Usbhost *uh, Endpt *e)
+{
+ Endptx *x;
+
+ x = malloc(sizeof(Endptx));
+ e->private = x;
+ x->epq = allocqh(uh->ctlr);
+ if(x->epq == nil)
+ panic("devendptx");
+}
+
+static void
+epfree(Usbhost *uh, Endpt *e)
+{
+ Ctlr *ctlr;
+ Endptx *x;
+
+ ctlr = uh->ctlr;
+ x = e->private;
+ if(x->epq != nil)
+ freeqh(ctlr, x->epq);
+}
+
+static void
+epopen(Usbhost *uh, Endpt *e)
+{
+ Ctlr *ctlr;
+
+ ctlr = uh->ctlr;
+ if(e->iso && e->active)
+ error("already open");
+ if(schedendpt(ctlr, e) < 0){
+ if(e->active)
+ error("cannot schedule USB endpoint, active");
+ else
+ error("cannot schedule USB endpoint");
+ }
+ eptactivate(ctlr, e);
+}
+
+static void
+epclose(Usbhost *uh, Endpt *e)
+{
+ Ctlr *ctlr;
+
+ ctlr = uh->ctlr;
+ eptdeactivate(ctlr, e);
+ unschedendpt(ctlr, e);
+}
+
+static void
+epmode(Usbhost *uh, Endpt *e)
+{
+ Ctlr *ctlr;
+ Endptx *x;
+
+ ctlr = uh->ctlr;
+ x = e->private;
+ if(e->iso) {
+ if(x->epq != nil) {
+ freeqh(ctlr, x->epq);
+ x->epq = nil;
+ }
+ }
+ else {
+ /* Each bulk device gets a queue head hanging off the
+ * bulk queue head
+ */
+ if(x->epq == nil) {
+ x->epq = allocqh(ctlr);
+ if(x->epq == nil)
+ panic("epbulk: allocqh");
+ }
+ queueqh(ctlr, x->epq);
+ }
+}
+
+static int ioport[] = {-1, Portsc0, Portsc1};
+
+static void
+portreset(Usbhost *uh, int port)
+{
+ int i, p;
+ Ctlr *ctlr;
+
+ ctlr = uh->ctlr;
+ if(port != 1 && port != 2)
+ error(Ebadarg);
+
+ /* should check that device not being configured on other port? */
+ p = ioport[port];
+ qlock(&ctlr->resetl);
+ if(waserror()){
+ qunlock(&ctlr->resetl);
+ nexterror();
+ }
+ XPRINT("r: %x\n", IN(p));
+ ilock(ctlr);
+ OUT(p, PortReset);
+ delay(12); /* BUG */
+ XPRINT("r2: %x\n", IN(p));
+ OUT(p, IN(p) & ~PortReset);
+ XPRINT("r3: %x\n", IN(p));
+ OUT(p, IN(p) | PortEnable);
+ microdelay(64);
+ for(i=0; i<1000 && (IN(p) & PortEnable) == 0; i++)
+ ;
+ XPRINT("r': %x %d\n", IN(p), i);
+ OUT(p, (IN(p) & ~PortReset)|PortEnable);
+ iunlock(ctlr);
+ poperror();
+ qunlock(&ctlr->resetl);
+}
+
+static void
+portenable(Usbhost *uh, int port, int on)
+{
+ int w, p;
+ Ctlr *ctlr;
+
+ ctlr = uh->ctlr;
+ if(port != 1 && port != 2)
+ error(Ebadarg);
+
+ /* should check that device not being configured on other port? */
+ p = ioport[port];
+ qlock(&ctlr->resetl);
+ if(waserror()){
+ qunlock(&ctlr->resetl);
+ nexterror();
+ }
+ ilock(ctlr);
+ w = IN(p);
+ if(on)
+ w |= PortEnable;
+ else
+ w &= ~PortEnable;
+ OUT(p, w);
+ microdelay(64);
+ iunlock(ctlr);
+ XPRINT("e: %x\n", IN(p));
+ poperror();
+ qunlock(&ctlr->resetl);
+}
+
+static void
+portinfo(Usbhost *uh, char *s, char *se)
+{
+ int x, i, j;
+ Ctlr *ctlr;
+
+ ctlr = uh->ctlr;
+ for(i = 1; i <= 2; i++) {
+ ilock(ctlr);
+ x = IN(ioport[i]);
+ if((x & (PortChange|StatusChange)) != 0)
+ OUT(ioport[i], x);
+ iunlock(ctlr);
+ s = seprint(s, se, "%d %ux", i, x);
+ for(j = 0; j < nelem(portstatus); j++) {
+ if((x & portstatus[j].bit) != 0)
+ s = seprint(s, se, " %s", portstatus[j].name);
+ }
+ s = seprint(s, se, "\n");
+ }
+}
+
+static void
+cleaniso(Endpt *e, int frnum)
+{
+ TD *td;
+ int id, n, i;
+ Endptx *x;
+ uchar *bp;
+
+ x = e->private;
+ td = x->xtd;
+ if (td->status & Active)
+ return;
+ id = (e->x<<7)|(e->dev->x&0x7F);
+ do {
+ if (td->status & AnyError)
+ XPRINT("usbisoerror 0x%lux\n", td->status);
+ n = (td->status + 1) & 0x3ff;
+ e->nbytes += n;
+ if ((td->flags & IsoClean) == 0)
+ e->nblocks++;
+ if (e->mode == OREAD){
+ e->buffered += n;
+ e->poffset += (td->status + 1) & 0x3ff;
+ td->offset = e->poffset;
+ td->dev = ((e->maxpkt -1)<<21) | ((id&0x7FF)<<8) | TokIN;
+ e->toffset = td->offset;
+ }else{
+ if ((td->flags & IsoClean) == 0){
+ e->buffered -= n;
+ if (e->buffered < 0){
+// print("e->buffered %d?\n", e->buffered);
+ e->buffered = 0;
+ }
+ }
+ e->toffset = td->offset;
+ n = (e->hz + e->remain)*e->pollms/1000;
+ e->remain = (e->hz + e->remain)*e->pollms%1000;
+ n *= e->samplesz;
+ td->dev = ((n -1)<<21) | ((id&0x7FF)<<8) | TokOUT;
+ td->offset = e->poffset;
+ e->poffset += n;
+ }
+ td = td->next;
+ if (x->xtd == td){
+ XPRINT("@");
+ break;
+ }
+ } while ((td->status & Active) == 0);
+ e->time = todget(nil);
+ x->xtd = td;
+ for (n = 2; n < 4; n++){
+ i = ((frnum + n)&0x3ff);
+ td = x->td0 + i;
+ bp = x->bp0 + e->maxpkt*i/e->pollms;
+ if (td->status & Active)
+ continue;
+
+ if (e->mode == OWRITE){
+ if (td == x->etd) {
+ XPRINT("*");
+ memset(bp+e->off, 0, e->maxpkt-e->off);
+ if (e->off == 0)
+ td->flags |= IsoClean;
+ else
+ e->buffered += (((td->dev>>21) +1) & 0x3ff) - e->off;
+ x->etd = nil;
+ }else if ((td->flags & IsoClean) == 0){
+ XPRINT("-");
+ memset(bp, 0, e->maxpkt);
+ td->flags |= IsoClean;
+ }
+ } else {
+ /* Unread bytes are now lost */
+ e->buffered -= (td->status + 1) & 0x3ff;
+ }
+ td->status = ErrLimit1 | Active | IsoSelect | IOC;
+ }
+ wakeup(&e->wr);
+}
+
+static void
+interrupt(Ureg*, void *a)
+{
+ QH *q;
+ Ctlr *ctlr;
+ Endpt *e;
+ Endptx *x;
+ int s, frnum;
+ Usbhost *uh;
+
+ uh = a;
+ ctlr = uh->ctlr;
+ s = IN(Status);
+ ctlr->frameptr = inl(ctlr->io+Flbaseadd);
+ ctlr->framenumber = IN(Frnum) & 0x3ff;
+ OUT(Status, s);
+ if ((s & 0x1f) == 0)
+ return;
+ ctlr->usbints++;
+ frnum = IN(Frnum) & 0x3ff;
+ if (s & 0x1a) {
+ XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ctlr->io+SOFMod));
+ XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1));
+ }
+
+ ilock(&ctlr->activends);
+ for(e = ctlr->activends.f; e != nil; e = e->activef) {
+ x = e->private;
+ if(!e->iso && x->epq != nil) {
+ XPRINT("cleanq(ctlr, x->epq, 0, 0)\n");
+ cleanq(ctlr, x->epq, 0, 0);
+ }
+ if(e->iso) {
+ XPRINT("cleaniso(e)\n");
+ cleaniso(e, frnum);
+ }
+ }
+ iunlock(&ctlr->activends);
+ XPRINT("cleanq(ctlr, ctlr->ctlq, 0, 0)\n");
+ cleanq(ctlr, ctlr->ctlq, 0, 0);
+ XPRINT("cleanq(ctlr, ctlr->bulkq, 0, Vf)\n");
+ cleanq(ctlr, ctlr->bulkq, 0, Vf);
+ XPRINT("clean recvq\n");
+ for (q = ctlr->recvq->next; q; q = q->hlink) {
+ XPRINT("cleanq(ctlr, q, 0, Vf)\n");
+ cleanq(ctlr, q, 0, Vf);
+ }
+}
+
+static int
+eptinput(void *arg)
+{
+ Endpt *e;
+
+ e = arg;
+ return e->eof || e->err || qcanread(e->rq);
+}
+
+static int
+isoreadyx(Endptx *x)
+{
+ return x->etd == nil || (x->etd != x->xtd && (x->etd->status & Active) == 0);
+}
+
+static int
+isoready(void *arg)
+{
+ int ret;
+ Ctlr *ctlr;
+ Endpt *e;
+ Endptx *x;
+
+ e = arg;
+ ctlr = e->dev->uh->ctlr;
+ x = e->private;
+ ilock(&ctlr->activends);
+ ret = isoreadyx(x);
+ iunlock(&ctlr->activends);
+ return ret;
+}
+
+static long
+isoio(Ctlr *ctlr, Endpt *e, void *a, long n, ulong offset, int w)
+{
+ TD *td;
+ Endptx *x;
+ int i, frnum;
+ uchar *p, *q, *bp;
+ volatile int isolock;
+
+ x = e->private;
+ qlock(&e->rlock);
+ isolock = 0;
+ if(waserror()){
+ if (isolock){
+ isolock = 0;
+ iunlock(&ctlr->activends);
+ }
+ qunlock(&e->rlock);
+ eptcancel(ctlr, e);
+ nexterror();
+ }
+ p = a;
+ if (offset != 0 && offset != e->foffset){
+ iprint("offset %lud, foffset %lud\n", offset, e->foffset);
+ /* Seek to a specific position */
+ frnum = (IN(Frnum) + 8) & 0x3ff;
+ td = x->td0 +frnum;
+ if (offset < td->offset)
+ error("ancient history");
+ while (offset > e->toffset){
+ tsleep(&e->wr, return0, 0, 500);
+ }
+ while (offset >= td->offset + ((w?(td->dev >> 21):td->status) + 1) & 0x7ff){
+ td = td->next;
+ if (td == x->xtd)
+ iprint("trouble\n");
+ }
+ ilock(&ctlr->activends);
+ isolock = 1;
+ e->off = td->offset - offset;
+ if (e->off >= e->maxpkt){
+ iprint("I can't program: %d\n", e->off);
+ e->off = 0;
+ }
+ x->etd = td;
+ e->foffset = offset;
+ }
+ do {
+ if (isolock == 0){
+ ilock(&ctlr->activends);
+ isolock = 1;
+ }
+ td = x->etd;
+ if (td == nil || e->off == 0){
+ if (td == nil){
+ XPRINT("0");
+ if (w){
+ frnum = (IN(Frnum) + 1) & 0x3ff;
+ td = x->td0 + frnum;
+ while(td->status & Active)
+ td = td->next;
+ }else{
+ frnum = (IN(Frnum) - 4) & 0x3ff;
+ td = x->td0 + frnum;
+ while(td->next != x->xtd)
+ td = td->next;
+ }
+ x->etd = td;
+ e->off = 0;
+ }else{
+ /* New td, make sure it's ready */
+ while (isoreadyx(x) == 0){
+ isolock = 0;
+ iunlock(&ctlr->activends);
+ sleep(&e->wr, isoready, e);
+ ilock(&ctlr->activends);
+ isolock = 1;
+ }
+ if (x->etd == nil){
+ XPRINT("!");
+ continue;
+ }
+ }
+ if (w)
+ e->psize = ((td->dev >> 21) + 1) & 0x7ff;
+ else
+ e->psize = (x->etd->status + 1) & 0x7ff;
+ if(e->psize > e->maxpkt)
+ panic("packet size > maximum");
+ }
+ if((i = n) >= e->psize)
+ i = e->psize;
+ if (w)
+ e->buffered += i;
+ else{
+ e->buffered -= i;
+ if (e->buffered < 0)
+ e->buffered = 0;
+ }
+ isolock = 0;
+ iunlock(&ctlr->activends);
+ td->flags &= ~IsoClean;
+ bp = x->bp0 + (td - x->td0) * e->maxpkt / e->pollms;
+ q = bp + e->off;
+ if (w){
+ memmove(q, p, i);
+ }else{
+ memmove(p, q, i);
+ }
+ p += i;
+ n -= i;
+ e->off += i;
+ e->psize -= i;
+ if (e->psize){
+ if (n != 0)
+ panic("usb iso: can't happen");
+ break;
+ }
+ if(w)
+ td->offset = offset + (p-(uchar*)a) - (((td->dev >> 21) + 1) & 0x7ff);
+ td->status = ErrLimit3 | Active | IsoSelect | IOC;
+ x->etd = td->next;
+ e->off = 0;
+ } while(n > 0);
+ n = p-(uchar*)a;
+ e->foffset += n;
+ poperror();
+ if (isolock)
+ iunlock(&ctlr->activends);
+ qunlock(&e->rlock);
+ return n;
+}
+
+static long
+read(Usbhost *uh, Endpt *e, void *a, long n, vlong offset)
+{
+ long l, i;
+ Block *b;
+ Ctlr *ctlr;
+ uchar *p;
+
+ ctlr = uh->ctlr;
+ if(e->iso)
+ return isoio(ctlr, e, a, n, (ulong)offset, 0);
+
+ XPRINT("qlock(%p)\n", &e->rlock);
+ qlock(&e->rlock);
+ XPRINT("got qlock(%p)\n", &e->rlock);
+ if(waserror()){
+ qunlock(&e->rlock);
+ eptcancel(ctlr, e);
+ nexterror();
+ }
+ p = a;
+ do {
+ if(e->eof) {
+ XPRINT("e->eof\n");
+ break;
+ }
+ if(e->err)
+ error(e->err);
+ qrcv(ctlr, e);
+ if(!e->iso)
+ e->data01 ^= 1;
+ sleep(&e->rr, eptinput, e);
+ if(e->err)
+ error(e->err);
+ b = qget(e->rq); /* TO DO */
+ if(b == nil) {
+ XPRINT("b == nil\n");
+ break;
+ }
+ if(waserror()){
+ freeb(b);
+ nexterror();
+ }
+ l = BLEN(b);
+ if((i = l) > n)
+ i = n;
+ if(i > 0){
+ memmove(p, b->rp, i);
+ p += i;
+ }
+ poperror();
+ freeb(b);
+ n -= i;
+ if (l != e->maxpkt)
+ break;
+ } while (n > 0);
+ poperror();
+ qunlock(&e->rlock);
+ return p-(uchar*)a;
+}
+
+static int
+qisempty(void *arg)
+{
+ return ((QH*)arg)->entries & Terminate;
+}
+
+static long
+write(Usbhost *uh, Endpt *e, void *a, long n, vlong offset, int tok)
+{
+ int i, j;
+ QH *qh;
+ Block *b;
+ Ctlr *ctlr;
+ uchar *p;
+
+ ctlr = uh->ctlr;
+ if(e->iso)
+ return isoio(ctlr, e, a, n, (ulong)offset, 1);
+
+ p = a;
+ qlock(&e->wlock);
+ if(waserror()){
+ qunlock(&e->wlock);
+ eptcancel(ctlr, e);
+ nexterror();
+ }
+ do {
+ if(e->err)
+ error(e->err);
+ if((i = n) >= e->maxpkt)
+ i = e->maxpkt;
+ b = allocb(i);
+ if(waserror()){
+ freeb(b);
+ nexterror();
+ }
+ XPRINT("out [%d]", i);
+ for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]);
+ XPRINT("\n");
+ memmove(b->wp, p, i);
+ b->wp += i;
+ p += i;
+ n -= i;
+ poperror();
+ qh = qxmit(ctlr, e, b, tok);
+ tok = TokOUT;
+ e->data01 ^= 1;
+ if(e->ntd >= e->nbuf) {
+XPRINT("qh %s: q=%p first=%p last=%p entries=%.8lux\n",
+ "writeusb sleep", qh, qh->first, qh->last, qh->entries);
+ XPRINT("write: sleep %lux\n", &e->wr);
+ sleep(&e->wr, qisempty, qh);
+ XPRINT("write: awake\n");
+ }
+ } while(n > 0);
+ poperror();
+ qunlock(&e->wlock);
+ return p-(uchar*)a;
+}
+
+static void
+init(Usbhost* uh)
+{
+ Ctlr *ctlr;
+
+ ctlr = uh->ctlr;
+ ilock(ctlr);
+ outl(ctlr->io+Flbaseadd, PCIWADDR(ctlr->frames));
+ OUT(Frnum, 0);
+ OUT(Usbintr, 0xF); /* enable all interrupts */
+ XPRINT("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(ctlr->io+SOFMod));
+ XPRINT("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1));
+ if((IN(Cmd)&1)==0)
+ OUT(Cmd, 1); /* run */
+// pprint("at: c=%x s=%x c0=%x\n", IN(Cmd), IN(Status), IN(Portsc0));
+ iunlock(ctlr);
+}
+
+static void
+scanpci(void)
+{
+ int io;
+ Ctlr *ctlr;
+ Pcidev *p;
+ static int already = 0;
+
+ if(already)
+ return;
+ already = 1;
+ p = nil;
+ while(p = pcimatch(p, 0, 0)) {
+ /*
+ * Find UHCI controllers. Class = 12 (serial controller),
+ * Sub-class = 3 (USB) and Programming Interface = 0.
+ */
+ if(p->ccrb != 0x0C || p->ccru != 0x03 || p->ccrp != 0x00)
+ continue;
+ io = p->mem[4].bar & ~0x0F;
+ if(io == 0) {
+ print("usbuhci: failed to map registers\n");
+ continue;
+ }
+ if(ioalloc(io, p->mem[4].size, 0, "usbuhci") < 0){
+ print("usbuhci: port %d in use\n", io);
+ continue;
+ }
+ if(p->intl == 0xFF || p->intl == 0) {
+ print("usbuhci: no irq assigned for port %d\n", io);
+ continue;
+ }
+
+ XPRINT("usbuhci: %x/%x port 0x%ux size 0x%x irq %d\n",
+ p->vid, p->did, io, p->mem[4].size, p->intl);
+
+ ctlr = malloc(sizeof(Ctlr));
+ ctlr->pcidev = p;
+ ctlr->io = io;
+ if(ctlrhead != nil)
+ ctlrtail->next = ctlr;
+ else
+ ctlrhead = ctlr;
+ ctlrtail = ctlr;
+ }
+}
+
+static int
+reset(Usbhost *uh)
+{
+ int i;
+ TD *t;
+ ulong io;
+ Ctlr *ctlr;
+ Pcidev *p;
+
+ scanpci();
+
+ /*
+ * Any adapter matches if no uh->port is supplied,
+ * otherwise the ports must match.
+ */
+ for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+ if(ctlr->active)
+ continue;
+ if(uh->port == 0 || uh->port == ctlr->io){
+ ctlr->active = 1;
+ break;
+ }
+ }
+ if(ctlr == nil)
+ return -1;
+
+ io = ctlr->io;
+ p = ctlr->pcidev;
+
+ uh->ctlr = ctlr;
+ uh->port = io;
+ uh->irq = p->intl;
+ uh->tbdf = p->tbdf;
+
+ XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n",
+ IN(Cmd), IN(Status), IN(Usbintr), inb(io+Frnum));
+ XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n",
+ IN(Flbaseadd), inb(io+SOFMod), IN(Portsc0), IN(Portsc1));
+
+ OUT(Cmd, 0); /* stop */
+ while((IN(Status) & (1<<5)) == 0) /* wait for halt */
+ ;
+ OUT(Status, 0xFF); /* clear pending interrupts */
+ pcicfgw16(p, 0xc0, 0x2000); /* legacy support register: turn off lunacy mode */
+
+ if(0){
+ i = inb(io+SOFMod);
+ OUT(Cmd, 4); /* global reset */
+ delay(15);
+ OUT(Cmd, 0); /* end reset */
+ delay(4);
+ outb(io+SOFMod, i);
+ }
+
+ ctlr->tdpool = mmucacheinhib(xspanalloc(128*sizeof(TD), /*16*/CACHELINESZ, 0), 128*sizeof(TD));
+ for(i=128; --i>=0;){
+ ctlr->tdpool[i].next = ctlr->freetd;
+ ctlr->freetd = &ctlr->tdpool[i];
+ }
+ ctlr->qhpool = mmucacheinhib(xspanalloc(64*sizeof(QH), /*16*/CACHELINESZ, 0), 64*sizeof(QH));
+ for(i=64; --i>=0;){
+ ctlr->qhpool[i].next = ctlr->freeqh;
+ ctlr->freeqh = &ctlr->qhpool[i];
+ }
+
+ /*
+ * the last entries of the periodic (interrupt & isochronous) scheduling TD entries
+ * points to the control queue and the bandwidth sop for bulk traffic.
+ * this is looped following the instructions in PIIX4 errata 29773804.pdf:
+ * a QH links to a looped but inactive TD as its sole entry,
+ * with its head entry leading on to the bulk traffic, the last QH of which
+ * links back to the empty QH.
+ */
+ ctlr->ctlq = allocqh(ctlr);
+ ctlr->bwsop = allocqh(ctlr);
+ ctlr->bulkq = allocqh(ctlr);
+ ctlr->recvq = allocqh(ctlr);
+ t = alloctd(ctlr); /* inactive TD, looped */
+ t->link = PCIWADDR(t);
+ ctlr->bwsop->entries = PCIWADDR(t);
+
+ ctlr->ctlq->head = PCIWADDR(ctlr->bulkq) | IsQH;
+ ctlr->bulkq->head = PCIWADDR(ctlr->recvq) | IsQH;
+ ctlr->recvq->head = PCIWADDR(ctlr->bwsop) | IsQH;
+ if (1) /* don't use loop back */
+ ctlr->bwsop->head = Terminate;
+ else /* set up loop back */
+ ctlr->bwsop->head = PCIWADDR(ctlr->bwsop) | IsQH;
+
+ ctlr->frames = mmucacheinhib(xspanalloc(FRAMESIZE, FRAMESIZE, 0), FRAMESIZE);
+ ctlr->frameld = xallocz(FRAMESIZE, 1);
+ for (i = 0; i < NFRAME; i++)
+ ctlr->frames[i] = PCIWADDR(ctlr->ctlq) | IsQH;
+
+ /*
+ * Linkage to the generic USB driver.
+ */
+ uh->init = init;
+ uh->interrupt = interrupt;
+
+ uh->portinfo = portinfo;
+ uh->portreset = portreset;
+ uh->portenable = portenable;
+
+ uh->epalloc = epalloc;
+ uh->epfree = epfree;
+ uh->epopen = epopen;
+ uh->epclose = epclose;
+ uh->epmode = epmode;
+
+ uh->read = read;
+ uh->write = write;
+
+ return 0;
+}
+
+void
+usbuhcilink(void)
+{
+ addusbtype("uhci", reset);
+}