From eeee59b2bccf7e2ebdadc5cfc9280cbe408240eb Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Tue, 8 Jun 1999 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1999-06-08 --- carrera/fns.h | 2 +- carrera/ns16552.h | 2 +- ip/esp.c | 1 + mpc/_inb.s | 110 +++ mpc/clock.c | 27 +- mpc/devether.c | 73 +- mpc/devpcmcia.c | 1118 +++++++++++++++++++++++++++ mpc/devrtc.c | 48 +- mpc/devsac.c | 647 ++++++++++++++++ mpc/devuart.c | 38 +- mpc/ether589.c | 156 ++++ mpc/etherelnk3.c | 1854 +++++++++++++++++++++++++++++++++++++++++++++ mpc/etherscc.c | 2 - mpc/fns.h | 21 +- mpc/inb.s | 119 +++ mpc/inbc.c | 134 ++++ mpc/io.h | 26 +- mpc/l.s | 26 +- mpc/main.c | 26 +- mpc/mem.h | 5 +- mpc/mmu.c | 23 +- mpc/nocache.c | 39 + mpc/trap.c | 6 +- mpc/unsac.c | 401 ++++++++++ 24 files changed, 4798 insertions(+), 106 deletions(-) create mode 100644 mpc/_inb.s create mode 100644 mpc/devpcmcia.c create mode 100644 mpc/devsac.c create mode 100644 mpc/ether589.c create mode 100644 mpc/etherelnk3.c create mode 100644 mpc/inb.s create mode 100644 mpc/inbc.c create mode 100644 mpc/nocache.c create mode 100644 mpc/unsac.c diff --git a/carrera/fns.h b/carrera/fns.h index d42ea4ff89d183557bdc5dc2c200fbb453d6e251..52193611f982cba9113a411df9f6da9f91c28f62 100644 --- a/carrera/fns.h +++ b/carrera/fns.h @@ -72,7 +72,7 @@ void wrcompare(ulong); void serialinit(void); void ns16552special(int, int, Queue**, Queue**, int (*)(Queue*, int)); -void ns16552setup(ulong, ulong, char*); +void ns16552setup(ulong, ulong, char*, int); void ns16552intr(int); void etherintr(void); void iomapinit(void); diff --git a/carrera/ns16552.h b/carrera/ns16552.h index f196f4024a95b0b4b4d9636415fbe8f247a1dd39..95fa90f343c457cebc07d4f7332969a781b76117 100644 --- a/carrera/ns16552.h +++ b/carrera/ns16552.h @@ -13,7 +13,7 @@ ns16552install(void) return; already = 1; - ns16552setup(Uart1, UartFREQ, "eia0"); + ns16552setup(Uart1, UartFREQ, "eia0", Ns550); } #define RD(r) (*(uchar*)((Uart1+r)^7)) diff --git a/ip/esp.c b/ip/esp.c index 1d86773cafeba716120880b1ace9ef709f2261fd..846466333883745877ab40d65deb7c79c2c6b014 100644 --- a/ip/esp.c +++ b/ip/esp.c @@ -484,6 +484,7 @@ convlookup(Proto *esp, ulong spi) if(ecb->incoming && ecb->spi == spi) return c; } + return nil; } static char * diff --git a/mpc/_inb.s b/mpc/_inb.s new file mode 100644 index 0000000000000000000000000000000000000000..3a13b76dd8977ee13be54c00dc1baea3e6a816e3 --- /dev/null +++ b/mpc/_inb.s @@ -0,0 +1,110 @@ +#include "mem.h" + +#define BDNZ BC 16,0, +#define BDNE BC 0,2, + +TEXT inb(SB), $0 + OR $ISAMEM, R3 + MOVBZ (R3), R3 + RETURN + +TEXT insb(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAMEM, R3 + SUB $1, R4 +insb1: + MOVBZ (R3), R7 + MOVBU R7, 1(R4) + BDNZ insb1 + RETURN + +TEXT outb(SB), $0 + MOVW v+4(FP), R4 + OR $ISAMEM, R3 + MOVB R4, (R3) + RETURN + +TEXT outsb(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAMEM, R3 + SUB $1, R4 +outsb1: + MOVBZU 1(R4), R7 + MOVB R7, (R3) + BDNZ outsb1 + RETURN + +TEXT ins(SB), $0 + OR $ISAMEM, R3 + EIEIO + MOVHBR (R3), R3 + RETURN + +TEXT inss(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAMEM, R3 + SUB $2, R4 +inss1: + MOVHZ (R3), R7 + MOVHU R7, 2(R4) + BDNZ inss1 + RETURN + +TEXT outs(SB), $0 + MOVW v+4(FP), R4 + OR $ISAMEM, R3 + MOVHBR R4, (R3) + RETURN + +TEXT outss(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAMEM, R3 + SUB $2, R4 +outss1: + MOVHZU 2(R4), R7 + MOVH R7, (R3) + BDNZ outss1 + RETURN + +TEXT inl(SB), $0 + OR $ISAMEM, R3 + MOVWBR (R3), R3 + RETURN + +TEXT insl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAMEM, R3 + SUB $4, R4 +insl1: + MOVW (R3), R7 + MOVWU R7, 4(R4) + BDNZ insl1 + RETURN + +TEXT outl(SB), $0 + MOVW v+4(FP), R4 + OR $ISAMEM, R3 + MOVWBR R4, (R3) + RETURN + +TEXT outsl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAMEM, R3 + SUB $4, R4 +outsl1: + MOVWU 4(R4), R7 + MOVW R7, (R3) + BDNZ outsl1 + RETURN diff --git a/mpc/clock.c b/mpc/clock.c index e17723f655adbb51a0d384c92b31be51c70588d9..8dc254f0c37079edbcf9260e57d38585940b0ebc 100644 --- a/mpc/clock.c +++ b/mpc/clock.c @@ -112,26 +112,33 @@ clockintr(Ureg *ur) m->iomem->swsr = 0xaa39; m->ticks++; + if(m->proc) + m->proc->pc = ur->pc; + + accounttime(); + if(kproftimer != nil) + kproftimer(ur->pc); - if(up) - up->pc = ur->pc; checkalarms(); if(m->machno == 0) { - if(kproftick != nil) - (*kproftick)(ur->pc); lock(&clock0lock); for(lp = clock0link; lp; lp = lp->link) lp->clock(); unlock(&clock0lock); } - if(up && up->state == Running){ -// if(up->type == Interp && tready()) -// ur->cr |= 1<<(31-31); - if(nrdy > 0) - sched(); - } + if(up == 0 || up->state != Running) + return; + +// user profiling clock +// if(ur->status & KUSER) { +// (*(ulong*)(USTKTOP-BY2WD)) += TK2MS(1); +// segclock(ur->pc); +// } + + if(anyready()) + sched(); } void diff --git a/mpc/devether.c b/mpc/devether.c index 09e5bd649e1b7bcc720e18560c4cd41d29260451..e26b3a933b4c3681d9fe3e9443effcd6ced7c1b8 100644 --- a/mpc/devether.c +++ b/mpc/devether.c @@ -12,8 +12,6 @@ static Ether *etherxx[MaxEther]; -void ethermediumlink(void); - Chan* etherattach(char* spec) { @@ -37,19 +35,6 @@ etherattach(char* spec) return chan; } -static void -etherdetach(void) -{ - Ether **ether; - - ether = etherxx; - while(*ether){ - if((*ether)->detach) - (*ether)->detach(*ether); - ether++; - } -} - static int etherwalk(Chan* chan, char* name) { @@ -76,18 +61,14 @@ ethercreate(Chan*, char*, int, ulong) static void etherclose(Chan* chan) { - Ether *ether; - - ether = etherxx[chan->dev]; - netifclose(ether, chan); - if(ether->closed) - ether->closed(ether); + netifclose(etherxx[chan->dev], chan); } static long -etherread(Chan* chan, void* buf, long n, vlong offset) +etherread(Chan* chan, void* buf, long n, vlong off) { Ether *ether; + ulong offset = off; ether = etherxx[chan->dev]; if((chan->qid.path & CHDIR) == 0 && ether->ifstat){ @@ -153,7 +134,7 @@ etheriq(Ether* ether, Block* bp, int freebp) { Etherpkt *pkt; ushort type; - int len; + int len, multi, forme; Netfile **ep, *f, **fp, *fx; Block *xbp; @@ -165,8 +146,9 @@ etheriq(Ether* ether, Block* bp, int freebp) fx = 0; ep = ðer->f[Ntypes]; + multi = pkt->d[0] & 1; /* check for valid multcast addresses */ - if((pkt->d[0] & 1) && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){ + if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) && ether->prom == 0){ if(!activemulti(ether, pkt->d, sizeof(pkt->d))){ if(freebp){ freeb(bp); @@ -176,6 +158,9 @@ etheriq(Ether* ether, Block* bp, int freebp) } } + // is it for me? + forme = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0; + /* * Multiplex the packet to all the connections which want it. * If the packet is not to be used subsequently (freebp != 0), @@ -183,7 +168,7 @@ etheriq(Ether* ether, Block* bp, int freebp) * 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((f = *fp) && (f->type == type || f->type < 0) && (forme || multi || f->prom)){ if(f->type > -2){ if(freebp && fx == 0) fx = f; @@ -201,7 +186,8 @@ etheriq(Ether* ether, Block* bp, int freebp) } if(fx){ - qpass(fx->in, bp); + if(qpass(fx->in, bp) < 0) + ether->soverflows++; return 0; } if(freebp){ @@ -215,7 +201,7 @@ etheriq(Ether* ether, Block* bp, int freebp) static int etheroq(Ether* ether, Block* bp) { - int len, loopback, s; + int len, loopback, s, mine; Etherpkt *pkt; ether->outpackets++; @@ -226,10 +212,14 @@ etheroq(Ether* ether, Block* bp) * 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); + mine = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0; + if(mine) if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){ s = splhi(); etheriq(ether, bp, loopback); @@ -256,8 +246,8 @@ etherwrite(Chan* chan, void* buf, long n, vlong) if(n > ETHERMAXTU) error(Etoobig); -// if(n < ETHERMINTU) -// error(Etoosmall); + if(n < ETHERMINTU) + error(Etoosmall); bp = allocb(n); if(waserror()){ @@ -279,7 +269,6 @@ etherbwrite(Chan* chan, Block* bp, ulong) long n; n = BLEN(bp); - ether = etherxx[chan->dev]; if(NETTYPE(chan->qid.path) != Ndataqid){ n = netifwrite(ether, chan, bp->rp, n); @@ -291,6 +280,10 @@ etherbwrite(Chan* chan, Block* bp, ulong) freeb(bp); error(Ebadarg); } + if(n < ETHERMINTU){ + freeb(bp); + error(Etoosmall); + } return etheroq(ether, bp); } @@ -304,7 +297,7 @@ void addethercard(char* t, int (*r)(Ether*)) { static int ncard; -print("addethercard\n"); + if(ncard == MaxEther) panic("too many ether cards"); cards[ncard].type = t; @@ -342,8 +335,6 @@ etherreset(void) int i, n, ctlrno; char name[NAMELEN], buf[128]; -print("etherreset\n"); - //ethermediumlink(); for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){ if(ether == 0) ether = malloc(sizeof(Ether)); @@ -353,9 +344,7 @@ print("etherreset\n"); ether->mbps = 10; if(isaconfig("ether", ctlrno, ether) == 0) continue; -print("after isaconfig %d\n", n); for(n = 0; cards[n].type; n++){ -print("card %d\n", n); if(cistrcmp(cards[n].type, ether->type)) continue; for(i = 0; i < ether->nopt; i++){ @@ -367,9 +356,16 @@ print("card %d\n", n); if(cards[n].reset(ether)) break; + /* + * IRQ2 doesn't really exist, it's used to gang the interrupt + * controllers together. A device set to IRQ2 will appear on + * the second interrupt controller as IRQ9. + */ + if(ether->irq == 2) + ether->irq = 9; intrenable(ether->irq, ether->interrupt, ether, ether->tbdf); - i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %d", + 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)); @@ -388,9 +384,9 @@ print("card %d\n", n); ether->oq = qopen(256*1024, 1, 0, 0); } else{ - netifinit(ether, name, Ntypes, 32*1024); + netifinit(ether, name, Ntypes, 65*1024); if(ether->oq == 0) - ether->oq = qopen(64*1024, 1, 0, 0); + ether->oq = qopen(65*1024, 1, 0, 0); } if(ether->oq == 0) panic("etherreset %s", name); @@ -434,7 +430,6 @@ Dev etherdevtab = { etherreset, devinit, etherattach, - /*etherdetach,*/ devclone, etherwalk, etherstat, diff --git a/mpc/devpcmcia.c b/mpc/devpcmcia.c new file mode 100644 index 0000000000000000000000000000000000000000..0d5555ed701d434ac7065aabf51a9d537dd62fb5 --- /dev/null +++ b/mpc/devpcmcia.c @@ -0,0 +1,1118 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * MPC8xx PCMCIA driver + * + */ + +typedef struct PCMmap PCMmap; +typedef struct Slot Slot; +typedef struct Conftab Conftab; + +enum +{ + Maxctlr= 1, + + /* pipr */ + Cvs1= 1<<15, // voltage sense 1 + Cvs2= 1<<14, // voltage sense 2 + Cwp= 1<<13, // write protect + Ccd2= 1<<12, // card detect 2 + Ccd1= 1<<11, // card detect 1 + Cbvd2= 1<<10, // battery voltage 2 + Cbvd1= 1<<9, // battery voltage 1 + Crdy= 1<<8, // ready + + /* pscr */ + Cvs1_c= 1<<15, // voltage sense 1 changed + Cvs2_c= 1<<14, // voltage sense 2 changed + Cwp_c= 1<<13, // write protect changed + Ccd2_c= 1<<12, // card detect 2 changed + Ccd1_c= 1<<11, // card detect 1 changed + Cbvd2_c= 1<<10, // battery voltage 2 changed + Cbvd1_c= 1<<9, // battery voltage 1 changed + Crdy_l= 1<<7, // ready is low + Crdy_h= 1<<6, // ready is hi + Crdy_r= 1<<5, // ready raising edge + Crdy_f= 1<<4, // ready falling edge + + /* pgcrx */ + Creset = IBIT(25), // card reset + Coe = IBIT(24), // output enable + + /* porN */ + Rport8= 0<<6, + Rport16= 1<<6, + Rmem= 0<<3, /* common memory space */ + Rattrib= 2<<3, /* attribute space */ + Rio= 3<<3, + Rdma= 4<<3, /* normal DMA */ + Rdmalx= 5<<3, /* DMA, last transaction */ + RA22_23= 6<<3, /* ``drive A22 and A23 signals on CE2 and CE1'' */ + RslotB= 1<<2, /* select slot B (always, on MPC823) */ + Rwp= 1<<1, /* write protect */ + Rvalid= 1<<0, /* region valid */ + + /* + * configuration registers - they start at an offset in attribute + * memory found in the CIS. + */ + Rconfig= 0, + Clevel= (1<<6), /* level sensitive interrupt line */ + + Maxctab= 8, /* maximum configuration table entries */ + Nmap= 4, // map entries per slot + Mshift= 12, + Mgran= (1<qid.path>>8)&0xff) +#define TYPE(c) (c->qid.path&0xff) +#define QID(s,t) (((s)<<8)|(t)) + +static void pcmciaintr(Ureg *, void *); +static void increfp(Slot *pp); +static void decrefp(Slot *pp); +static PCMmap* pcmmap(Slot*, ulong, int, int); +static void pcmunmap(Slot*, PCMmap*); + +static Slot *slot; +static nslot; + +static int +pcmgen(Chan *c, Dirtab *tab, int ntab, int i, Dir *dp) +{ + int slotno; + Qid qid; + long len; + Slot *pp; + char name[NAMELEN]; + + USED(tab, ntab); + if(i>=3*nslot) + return -1; + slotno = i/3; + pp = slot + slotno; + len = 0; + switch(i%3){ + case 0: + qid.path = QID(slotno, Qmem); + sprint(name, "pcm%dmem", slotno); + len = pp->memlen; + break; + case 1: + qid.path = QID(slotno, Qattr); + sprint(name, "pcm%dattr", slotno); + len = pp->memlen; + break; + case 2: + qid.path = QID(slotno, Qctl); + sprint(name, "pcm%dctl", slotno); + break; + } + qid.vers = 0; + devdir(c, qid, name, len, eve, 0660, dp); + return 1; +} + +/* + * set up for slot cards + */ +static void +pcmciareset(void) +{ + static int already; + int i; + IMM *io; + Slot *pp; + + if(already) + return; + already = 1; + + nslot = 2; + slot = xalloc(nslot * sizeof(Slot)); + + io = m->iomem; + + for(i=0; i<8; i++){ + io->pcmr[i].option = 0; + io->pcmr[i].base = 0; + } + + for(i=0; i<2; i++) + io->pgcr[i] = (1<<(31-PCMCIAlevel)) | (1<<(23-PCMCIAlevel)); + + for(i = 0; i < nslot; i++){ + pp = slot + i; + pp->slotno = i; + pp->memlen = 512*1024; + } + + /* 512K - slow - attr */ + io->pcmr[0].base = ISAMEM + 512*1024; + io->pcmr[0].option = (0x1a<<27) | (8<<16) | (6<<12) | (24<<7) + | Rport16 | Rattrib | Rvalid; + // 64K io + io->pcmr[1].base = ISAMEM+0x300; + io->pcmr[1].option = (0x6<<27) | (2<<16) | (4<<12) | (8<<7) + | Rport16 | Rio | Rvalid; + +// intrenable(VectorPIC+PCMCIAlevel, pcmciaintr, 0, BUSUNKNOWN); + io->pscr = 0xFEF0FEF0; /* reset status */ + io->per = 0x00800000; /* enable interrupts */ + + print("pcmcia reset\n"); +} + +static Chan* +pcmciaattach(char *spec) +{ + return devattach('y', spec); +} + +static int +pcmciawalk(Chan *c, char *name) +{ + return devwalk(c, name, 0, 0, pcmgen); +} + +static void +pcmciastat(Chan *c, char *db) +{ + devstat(c, db, 0, 0, pcmgen); +} + +static Chan* +pcmciaopen(Chan *c, int omode) +{ + if(c->qid.path == CHDIR){ + if(omode != OREAD) + error(Eperm); + } else + increfp(slot + SLOTNO(c)); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +pcmciaclose(Chan *c) +{ + if(c->flag & COPEN) + if(c->qid.path != CHDIR) + decrefp(slot+SLOTNO(c)); +} + +/* a memmove using only bytes */ +static void +memmoveb(uchar *to, uchar *from, int n) +{ + while(n-- > 0) + *to++ = *from++; +} + +static long +pcmread(int slotno, int attr, void *a, long n, vlong offset) +{ + int i, len; + PCMmap *m; + ulong ka; + uchar *ac; + Slot *pp; + + pp = slot + slotno; + if(pp->memlen < offset) + return 0; + if(pp->memlen < offset + n) + n = pp->memlen - offset; + + m = 0; + if(waserror()){ + if(m) + pcmunmap(pp, m); + nexterror(); + } + + ac = a; + for(len = n; len > 0; len -= i){ + m = pcmmap(pp, offset, 0, attr); + if(m == 0) + error("can't map PCMCIA card"); + if(offset + len > m->cea) + i = m->cea - offset; + else + i = len; + ka = KZERO|(m->isa + offset - m->ca); + memmoveb(ac, (void*)ka, i); + pcmunmap(pp, m); + offset += i; + ac += i; + } + poperror(); + return n; +} + +static long +pcmciaread(Chan *c, void *a, long n, vlong offset) +{ + char *cp, *buf; + ulong p; + Slot *pp; + + p = TYPE(c); + switch(p){ + case Qdir: + return devdirread(c, a, n, 0, 0, pcmgen); + case Qmem: + case Qattr: + return pcmread(SLOTNO(c), p==Qattr, a, n, offset); + case Qctl: + buf = malloc(2048); + if(buf == nil) + error(Enomem); + if(waserror()){ + free(buf); + nexterror(); + } + cp = buf; + pp = slot + SLOTNO(c); + if(pp->occupied) + cp += sprint(cp, "occupied\n"); + if(pp->enabled) + cp += sprint(cp, "enabled\n"); + if(pp->powered) + cp += sprint(cp, "powered\n"); + if(pp->configed) + cp += sprint(cp, "configed\n"); + if(pp->wrprot) + cp += sprint(cp, "write protected\n"); + if(pp->busy) + cp += sprint(cp, "busy\n"); + cp += sprint(cp, "battery lvl %d\n", pp->battery); + cp += sprint(cp, "voltage select %d\n", pp->voltage); + cp += sprint(cp, "pipr %ux\n", m->iomem->pipr); + cp += sprint(cp, "pcsr %ux\n", m->iomem->pscr); + *cp = 0; + n = readstr(offset, a, n, buf); + poperror(); + free(buf); + break; + default: + n=0; + break; + } + return n; +} + +static long +pcmwrite(int dev, int attr, void *a, long n, vlong offset) +{ + int i, len; + PCMmap *m; + ulong ka; + uchar *ac; + Slot *pp; + + pp = slot + dev; + if(pp->memlen < offset) + return 0; + if(pp->memlen < offset + n) + n = pp->memlen - offset; + +#ifdef xxx + m = 0; + if(waserror()){ + if(m) + pcmunmap(pp->slotno, m); + nexterror(); + } + + ac = a; + for(len = n; len > 0; len -= i){ + m = pcmmap(pp, offset, 0, attr); + if(m == 0) + error("can't map PCMCIA card"); + if(offset + len > m->cea) + i = m->cea - offset; + else + i = len; + ka = KZERO|(m->isa + offset - m->ca); + memmoveb((void*)ka, ac, i); + pcmunmap(pp->slotno, m); + offset += i; + ac += i; + } + + poperror(); +#endif + + return n; +} + +static long +pcmciawrite(Chan *c, void *a, long n, vlong offset) +{ + ulong p; + Slot *pp; + char buf[32]; + + p = TYPE(c); + switch(p){ + case Qctl: + if(n >= sizeof(buf)) + n = sizeof(buf) - 1; + strncpy(buf, a, n); + buf[n] = 0; + pp = slot + SLOTNO(c); + if(!pp->occupied) + error(Eio); + + break; + case Qmem: + case Qattr: + pp = slot + SLOTNO(c); + if(pp->occupied == 0 || pp->enabled == 0) + error(Eio); + n = pcmwrite(pp->slotno, p == Qattr, a, n, offset); + if(n < 0) + error(Eio); + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev pcmciadevtab = { + 'y', + "pcmcia", + + pcmciareset, + devinit, + pcmciaattach, + devclone, + pcmciawalk, + pcmciastat, + pcmciaopen, + devcreate, + pcmciaclose, + pcmciaread, + devbread, + pcmciawrite, + devbwrite, + devremove, + devwstat, +}; + +static void +pcmciaintr(Ureg *, void *) +{ + print("pcmciaintr\n"); +} + +/* + * get a map for pc card region, return corrected len + */ +static PCMmap* +pcmmap(Slot *pp, ulong offset, int len, int attr) +{ + PCMmap *m; + ulong e; + + if(attr == 0) + panic("pcmmap"); + + /* convert offset to granularity */ + if(len <= 0) + len = 1; + e = ROUND(offset+len, Mgran); + offset &= Mmask; + len = e - offset; + + m = pp->mmap; + + m->ca = 0; + m->cea = 512*1024; + m->isa = ISAMEM + 512*1024; + m->len = 512*1024; + m->attr = attr; + m->ref = 1; + + return m; +} + +static void +pcmunmap(Slot *pp, PCMmap *m) +{ +} + +static int +pcmio(int slotno, ISAConf *isa) +{ + uchar we, x, *p; + Slot *pp; + Conftab *ct, *et, *t; + PCMmap *m; + int i, index, irq; + char *cp; + + isa->irq = VectorPIC+PCMCIAlevel; + irq = isa->irq; + + if(slotno > nslot) + return -1; + pp = slot + slotno; + + if(!pp->occupied) + return -1; + + et = &pp->ctab[pp->nctab]; + + ct = 0; + for(i = 0; i < isa->nopt; i++){ + if(strncmp(isa->opt[i], "index=", 6)) + continue; + index = strtol(&isa->opt[i][6], &cp, 0); + if(cp == &isa->opt[i][6] || index >= pp->nctab) + return -1; + ct = &pp->ctab[index]; + } + + if(ct == 0){ + + /* assume default is right */ + if(pp->def) + ct = pp->def; + else + ct = pp->ctab; + + /* try for best match */ + if(ct->nio == 0 + || ct->io[0].start != isa->port || ((1<irqs) == 0){ + for(t = pp->ctab; t < et; t++) + if(t->nio + && t->io[0].start == isa->port + && ((1<irqs)){ + ct = t; + break; + } + } + if(ct->nio == 0 || ((1<irqs) == 0){ + for(t = pp->ctab; t < et; t++) + if(t->nio && ((1<irqs)){ + ct = t; + break; + } + } + if(ct->nio == 0){ + for(t = pp->ctab; t < et; t++) + if(t->nio){ + ct = t; + break; + } + } + } + + if(ct == et || ct->nio == 0) + return -1; + + /* enable io port map 0 */ + if(isa->port == 0) + isa->port = ct->io[0].start; + if(isa->port == 0 && ct->io[0].start == 0) + return -1; + + if(pp->cpresent & (1<caddr + Rconfig, 1, 1); + p = KADDR(m->isa + pp->caddr + Rconfig - m->ca); + + /* set configuration and interrupt type */ + x = 1; + x |= ct->index<<1; + if((ct->irqtype & 0x20) && ((ct->irqtype & 0x40)==0 || isa->irq>7)) { + x |= Clevel; + } + *p = x; + delay(5); + pcmunmap(pp, m); + } + return 0; +} + +/* + * look for a card whose version contains 'idstr' + */ +int +pcmspecial(char *idstr, ISAConf *isa) +{ + Slot *pp; + extern char *strstr(char*, char*); + + for(pp = slot; pp < slot+nslot; pp++){ + if(pp->special) + continue; /* already taken */ + increfp(pp); + + if(pp->occupied) + if(strstr(pp->verstr, idstr)) + if(isa == 0 || pcmio(pp->slotno, isa) == 0){ + pp->special = 1; + return pp->slotno; + } + + decrefp(pp); + } + return -1; +} + +void +pcmspecialclose(int slotno) +{ + Slot *pp; + + if(slotno >= nslot) + panic("pcmspecialclose"); + pp = slot + slotno; + pp->special = 0; + decrefp(pp); +} + +static void +slotinfo(Slot *pp) +{ + ulong pipr; + + pipr = m->iomem->pipr; + if(pp->slotno == 0) + pipr >>= 16; + pp->occupied = (pipr&(Ccd1|Ccd2))==0; + pp->battery = (pipr & (Cbvd1|Cbvd2)) != 0; + pp->voltage = ((pipr & Cvs1) != 0) | (((pipr & Cvs2) != 0)<<1); + pp->wrprot = (pipr&Cwp)==0; + pp->busy = (pipr&Crdy)==0; + pp->powered = 1; // assume power is always on for the momment +} + +/* + * read and crack the card information structure enough to set + * important parameters like power + */ +static void tcfig(Slot*, int); +static void tentry(Slot*, int); +static void tvers1(Slot*, int); + +static void (*parse[256])(Slot*, int) = +{ +[0x15] tvers1, +[0x1A] tcfig, +[0x1B] tentry, +}; + +static int +readc(Slot *pp, uchar *x) +{ + if(pp->cispos >= Mgran) + return 0; + *x = pp->cisbase[2*pp->cispos]; + pp->cispos++; + return 1; +} + +static void +cisread(Slot *pp) +{ + uchar link; + uchar type; + int this, i; + PCMmap *m; + + memset(pp->ctab, 0, sizeof(pp->ctab)); + pp->caddr = 0; + pp->cpresent = 0; + pp->configed = 0; + pp->nctab = 0; + + m = pcmmap(pp, 0, 0, 1); + if(m == 0) + return; + pp->cisbase = KADDR(m->isa); + pp->cispos = 0; + + /* loop through all the tuples */ + for(i = 0; i < 1000; i++){ + this = pp->cispos; + if(readc(pp, &type) != 1) + break; + if(type == 0xFF) + break; + if(readc(pp, &link) != 1) + break; + if(parse[type]) + (*parse[type])(pp, type); + if(link == 0xff) + break; + pp->cispos = this + (2+link); + } + pcmunmap(pp, m); +} + +static ulong +getlong(Slot *pp, int size) +{ + uchar c; + int i; + ulong x; + + x = 0; + for(i = 0; i < size; i++){ + if(readc(pp, &c) != 1) + break; + x |= c<<(i*8); + } + return x; +} + +static void +tcfig(Slot *pp, int ) +{ + uchar size, rasize, rmsize; + uchar last; + + if(readc(pp, &size) != 1) + return; + rasize = (size&0x3) + 1; + rmsize = ((size>>2)&0xf) + 1; + if(readc(pp, &last) != 1) + return; + pp->caddr = getlong(pp, rasize); + pp->cpresent = getlong(pp, rmsize); +} + +/* + * enable the slot card + */ +static void +slotena(Slot *pp) +{ + IMM *io; + int shift; + + if(pp->enabled) + return; + shift = (pp->slotno == 0)?16:0; + + io = m->iomem; +// io->pgcr[pp->slotno] |= Coe; + io->pgcr[pp->slotno] &= ~Coe; // active low + delay(300); + io->pgcr[pp->slotno] |= Creset; + delay(100); + io->pgcr[pp->slotno] &= ~Creset; + delay(500); +#ifdef XXX + eieio(); + delay(300); + io->pgcrb |= Cbreset; /* TO DO: active high? active low? */ + eieio(); + delay(100); + io->pgcrb &= ~Cbreset; + eieio(); + delay(500); +#endif + /* get configuration */ + slotinfo(pp); + if(pp->occupied){ + cisread(pp); + pp->enabled = 1; + } +} + +/* + * disable the slot card + */ +static void +slotdis(Slot *pp) +{ + pp->enabled = 0; +} + +static void +increfp(Slot *pp) +{ + lock(pp); + if(pp->ref++ == 0) + slotena(pp); + unlock(pp); +} + +static void +decrefp(Slot *pp) +{ + lock(pp); + if(pp->ref-- == 1) + slotdis(pp); + unlock(pp); +} + +static ulong vexp[8] = +{ + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 +}; +static ulong vmant[16] = +{ + 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90, +}; + +static ulong +microvolt(Slot *pp) +{ + uchar c; + ulong microvolts; + ulong exp; + + if(readc(pp, &c) != 1) + return 0; + exp = vexp[c&0x7]; + microvolts = vmant[(c>>3)&0xf]*exp; + while(c & 0x80){ + if(readc(pp, &c) != 1) + return 0; + switch(c){ + case 0x7d: + break; /* high impedence when sleeping */ + case 0x7e: + case 0x7f: + microvolts = 0; /* no connection */ + break; + default: + exp /= 10; + microvolts += exp*(c&0x7f); + } + } + return microvolts; +} + +static ulong +nanoamps(Slot *pp) +{ + uchar c; + ulong nanoamps; + + if(readc(pp, &c) != 1) + return 0; + nanoamps = vexp[c&0x7]*vmant[(c>>3)&0xf]; + while(c & 0x80){ + if(readc(pp, &c) != 1) + return 0; + if(c == 0x7d || c == 0x7e || c == 0x7f) + nanoamps = 0; + } + return nanoamps; +} + +/* + * only nominal voltage is important for config + */ +static ulong +power(Slot *pp) +{ + uchar feature; + ulong mv; + + mv = 0; + if(readc(pp, &feature) != 1) + return 0; + if(feature & 1) + mv = microvolt(pp); + if(feature & 2) + microvolt(pp); + if(feature & 4) + microvolt(pp); + if(feature & 8) + nanoamps(pp); + if(feature & 0x10) + nanoamps(pp); + if(feature & 0x20) + nanoamps(pp); + if(feature & 0x40) + nanoamps(pp); + return mv/1000000; +} + +static ulong mantissa[16] = +{ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, }; + +static ulong exponent[8] = +{ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, }; + +static ulong +ttiming(Slot *pp, int scale) +{ + uchar unscaled; + ulong nanosecs; + + if(readc(pp, &unscaled) != 1) + return 0; + nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10; + nanosecs = nanosecs * vexp[scale]; + return nanosecs; +} + +static void +timing(Slot *pp, Conftab *ct) +{ + uchar c, i; + + if(readc(pp, &c) != 1) + return; + i = c&0x3; + if(i != 3) + ct->maxwait = ttiming(pp, i); /* max wait */ + i = (c>>2)&0x7; + if(i != 7) + ct->readywait = ttiming(pp, i); /* max ready/busy wait */ + i = (c>>5)&0x7; + if(i != 7) + ct->otherwait = ttiming(pp, i); /* reserved wait */ +} + +static void +iospaces(Slot *pp, Conftab *ct) +{ + uchar c; + int i, nio; + + ct->nio = 0; + if(readc(pp, &c) != 1) + return; + + ct->bit16 = ((c>>5)&3) >= 2; + if(!(c & 0x80)){ + ct->io[0].start = 0; + ct->io[0].len = 1<<(c&0x1f); + ct->nio = 1; + return; + } + + if(readc(pp, &c) != 1) + return; + + nio = (c&0xf)+1; + for(i = 0; i < nio; i++){ + ct->io[i].start = getlong(pp, (c>>4)&0x3); + ct->io[0].len = getlong(pp, (c>>6)&0x3); + } + ct->nio = nio; +} + +static void +irq(Slot *pp, Conftab *ct) +{ + uchar c; + + if(readc(pp, &c) != 1) + return; + ct->irqtype = c & 0xe0; + if(c & 0x10) + ct->irqs = getlong(pp, 2); + else + ct->irqs = 1<<(c&0xf); + ct->irqs &= 0xDEB8; /* levels available to card */ +} + +static void +memspace(Slot *pp, int asize, int lsize, int host) +{ + ulong haddress, address, len; + + len = getlong(pp, lsize)*256; + address = getlong(pp, asize)*256; + USED(len, address); + if(host){ + haddress = getlong(pp, asize)*256; + USED(haddress); + } +} + +static void +tentry(Slot *pp, int ) +{ + uchar c, i, feature; + Conftab *ct; + + if(pp->nctab >= Maxctab) + return; + if(readc(pp, &c) != 1) + return; + ct = &pp->ctab[pp->nctab++]; + + /* copy from last default config */ + if(pp->def) + *ct = *pp->def; + + ct->index = c & 0x3f; + + /* is this the new default? */ + if(c & 0x40) + pp->def = ct; + + /* memory wait specified? */ + if(c & 0x80){ + if(readc(pp, &i) != 1) + return; + if(i&0x80) + ct->memwait = 1; + } + + if(readc(pp, &feature) != 1) + return; + switch(feature&0x3){ + case 1: + ct->vpp1 = ct->vpp2 = power(pp); + break; + case 2: + power(pp); + ct->vpp1 = ct->vpp2 = power(pp); + break; + case 3: + power(pp); + ct->vpp1 = power(pp); + ct->vpp2 = power(pp); + break; + default: + break; + } + if(feature&0x4) + timing(pp, ct); + if(feature&0x8) + iospaces(pp, ct); + if(feature&0x10) + irq(pp, ct); + switch((feature>>5)&0x3){ + case 1: + memspace(pp, 0, 2, 0); + break; + case 2: + memspace(pp, 2, 2, 0); + break; + case 3: + if(readc(pp, &c) != 1) + return; + for(i = 0; i <= (c&0x7); i++) + memspace(pp, (c>>5)&0x3, (c>>3)&0x3, c&0x80); + break; + } + pp->configed++; +} + +static void +tvers1(Slot *pp, int ) +{ + uchar c, major, minor; + int i; + + if(readc(pp, &major) != 1) + return; + if(readc(pp, &minor) != 1) + return; + for(i = 0; i < sizeof(pp->verstr)-1; i++){ + if(readc(pp, &c) != 1) + return; + if(c == 0) + c = '\n'; + if(c == 0xff) + break; + pp->verstr[i] = c; + } + pp->verstr[i] = 0; +} diff --git a/mpc/devrtc.c b/mpc/devrtc.c index f03d026bcfbb9e5f54d3448151d11f8a38ff67a0..2a95acc82618a31a6321a83bfa72c075e82b5c96 100644 --- a/mpc/devrtc.c +++ b/mpc/devrtc.c @@ -7,16 +7,9 @@ #include "io.h" -/* - * MPC8xx real time clock - * FADS board option switch - * interrupt statistics - */ - enum{ Qrtc = 1, - Qswitch, - Qintstat, + Qnvram, /* sccr */ RTDIV= 1<<24, @@ -25,14 +18,18 @@ enum{ /* rtcsc */ RTE= 1<<0, R38K= 1<<4, + + Nvoff= 4*1024, /* where usable nvram lives */ + Nvsize= 4*1024, }; static QLock rtclock; /* mutex on clock operations */ +static Lock nvrtlock; + static Dirtab rtcdir[]={ "rtc", {Qrtc, 0}, 12, 0666, - "switch", {Qswitch, 0}, 0, 0444, - "intstat", {Qintstat, 0}, 0, 0444, + "nvram", {Qnvram, 0}, Nvsize, 0664, }; #define NRTC (sizeof(rtcdir)/sizeof(rtcdir[0])) @@ -79,9 +76,8 @@ rtcopen(Chan *c, int omode) if(strcmp(up->user, eve)!=0 && omode!=OREAD) error(Eperm); break; - case Qswitch: - case Qintstat: - if(omode!=OREAD) + case Qnvram: + if(strcmp(up->user, eve)!=0) error(Eperm); break; } @@ -97,7 +93,6 @@ static long rtcread(Chan *c, void *buf, long n, vlong offset) { ulong t; -// char *b; if(c->qid.path & CHDIR) return devdirread(c, buf, n, rtcdir, NRTC, devgen); @@ -107,8 +102,16 @@ rtcread(Chan *c, void *buf, long n, vlong offset) t = m->iomem->rtc; n = readnum(offset, buf, n, t, 12); return n; - case Qswitch: - return readnum(offset, buf, n, 0xf/*(m->bcsr[2]>>19)&0xF*/, 12); + case Qnvram: + if(offset >= Nvsize) + return 0; + t = offset; + if(t + n > Nvsize) + n = Nvsize - t; + ilock(&nvrtlock); + memmove(buf, (uchar*)(NVRAMMEM + Nvoff + t), n); + iunlock(&nvrtlock); + return n; } error(Egreg); return 0; /* not reached */ @@ -120,6 +123,7 @@ rtcwrite(Chan *c, void *buf, long n, vlong offset) ulong secs; char *cp, *ep; IMM *io; + ulong t; switch(c->qid.path){ case Qrtc: @@ -145,8 +149,16 @@ rtcwrite(Chan *c, void *buf, long n, vlong offset) io->rtck = ~KEEP_ALIVE_KEY; iopunlock(); return n; - case Qswitch: - return 0; + case Qnvram: + if(offset >= Nvsize) + return 0; + t = offset; + if(t + n > Nvsize) + n = Nvsize - t; + ilock(&nvrtlock); + memmove((uchar*)(NVRAMMEM + Nvoff + offset), buf, n); + iunlock(&nvrtlock); + return n; } error(Egreg); return 0; /* not reached */ diff --git a/mpc/devsac.c b/mpc/devsac.c new file mode 100644 index 0000000000000000000000000000000000000000..989034826714f5dc7f8f416b4d2a48402a3182ce --- /dev/null +++ b/mpc/devsac.c @@ -0,0 +1,647 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +/* + * Rather than reading /adm/users, which is a lot of work for + * a toy program, we assume all groups have the form + * NNN:user:user: + * meaning that each user is the leader of his own group. + */ + +enum +{ + OPERM = 0x3, /* mask of all permission types in open mode */ + Nram = 512, +}; + +typedef struct SacPath SacPath; +typedef struct Sac Sac; +typedef struct SacHeader SacHeader; +typedef struct SacDir SacDir; + +enum { + Magic = 0x5acf5, +}; + +struct SacDir +{ + char name[NAMELEN]; + char uid[NAMELEN]; + char gid[NAMELEN]; + uchar qid[4]; + uchar mode[4]; + uchar atime[4]; + uchar mtime[4]; + uchar length[8]; + uchar blocks[8]; +}; + +struct SacHeader +{ + uchar magic[4]; + uchar length[8]; + uchar blocksize[4]; + uchar md5[16]; +}; + + +struct Sac +{ + SacDir; + SacPath *path; +}; + +struct SacPath +{ + Ref; + SacPath *up; + vlong blocks; + int entry; +}; + +enum +{ + Pexec = 1, + Pwrite = 2, + Pread = 4, + Pother = 1, + Pgroup = 8, + Powner = 64, +}; + +uchar *data = SACMEM; +int blocksize; +Sac root; + +void sacstat(SacDir*, char*); +void io(void); +void usage(void); +ulong getl(void *p); +vlong getv(void *p); +void init(char*); +Sac *saccpy(Sac *s); +Sac *saclookup(Sac *s, char *name); +int sacdirread(Sac *s, char *p, long off, long cnt); +void loadblock(void *buf, uchar *offset, int blocksize); +void sacfree(Sac*); + +void +devinit(void) +{ + SacHeader *hdr; + hdr = (SacHeader*)data; + if(getl(hdr->magic) != Magic) { +print("devsac: bad magic"); + return; + } + blocksize = getl(hdr->blocksize); + root.SacDir = *(SacDir*)(data + sizeof(SacHeader)); +} + +static Chan* +sacattach(char* spec) +{ + Chan *c; + int dev; + + dev = atoi(spec); + if(dev != 0) + error("bad specification"); + + // check if init found sac file system in memory + if(blocksize == 0) + error("devsac: bad magic"); + + c = devattach('C', spec); + c->qid = (Qid){getl(root.qid), 0}; + c->dev = dev; + c->aux = saccpy(&root); + return c; +} + +Chan* +sacclone(Chan *c, Chan *nc) +{ + nc = devclone(c, nc); + nc->aux = saccpy(c->aux); + return nc; +} + +int +devwalk(Chan *c, char *name, Dirtab *tab, int ntab, Devgen *gen) +{ + Sac *sac; + + isdir(c); + sac = c->aux; + if(strcmp(name, ".") == 0) + return 1; + sac = saclookup(sac, name); + if(sac == nil) { + strncpy(up->error, Enonexist, NAMELEN); + return 0; + } + c->aux = sac; + c->qid = (Qid){getl(sac->qid), 0}; + op = c->path; + c->path = ptenter(&syspt, op, name); + decref(op); + return 1; +} + +char * +ropen(Fid *f) +{ + int mode, trunc; + + if(f->open) + return Eisopen; + if(f->busy == 0) + return Enotexist; + mode = rhdr.mode; + if(f->qid.path & CHDIR){ + if(mode != OREAD) + return Eperm; + thdr.qid = f->qid; + return 0; + } + if(mode & ORCLOSE) + return Erdonly; + trunc = mode & OTRUNC; + mode &= OPERM; + if(mode==OWRITE || mode==ORDWR || trunc) + return Erdonly; + if(mode==OREAD) + if(!perm(f, f->sac, Pread)) + return Eperm; + if(mode==OEXEC) + if(!perm(f, f->sac, Pexec)) + return Eperm; + thdr.qid = f->qid; + f->open = 1; + return 0; +} + +char * +rcreate(Fid *f) +{ + if(f->open) + return Eisopen; + if(f->busy == 0) + return Enotexist; + return Erdonly; +} + +char* +rread(Fid *f) +{ + Sac *sac; + char *buf, *buf2; + long off; + int n, cnt, i, j; + uchar *blocks; + vlong length; + + if(f->busy == 0) + return Enotexist; + sac = f->sac; + thdr.count = 0; + off = rhdr.offset; + buf = thdr.data; + cnt = rhdr.count; + if(f->qid.path & CHDIR){ + cnt = (rhdr.count/DIRLEN)*DIRLEN; + if(off%DIRLEN) + return "i/o error"; + thdr.count = sacdirread(sac, buf, off, cnt); + return 0; + } + length = getv(sac->length); + if(off >= length) { + rhdr.count = 0; + return 0; + } + if(cnt > length-off) + cnt = length-off; + thdr.count = cnt; + if(cnt == 0) + return 0; + blocks = data + getv(sac->blocks); + buf2 = malloc(blocksize); + while(cnt > 0) { + i = off/blocksize; + loadblock(buf2, blocks+i*8, blocksize); + j = off-i*blocksize; + n = blocksize-j; + if(n > cnt) + n = cnt; + memmove(buf, buf2+j, n); + cnt -= n; + off += n; + } + free(buf2); + return 0; +} + +char* +sacwrite(Fid *f) +{ + if(f->busy == 0) + return Enotexist; + return Erdonly; +} + +char * +rclunk(Fid *f) +{ + f->busy = 0; + f->open = 0; + free(f->user); + sacfree(f->sac); + return 0; +} + +char * +rremove(Fid *f) +{ + f->busy = 0; + f->open = 0; + free(f->user); + sacfree(f->sac); + return Erdonly; +} + +char * +rstat(Fid *f) +{ + if(f->busy == 0) + return Enotexist; + sacstat(f->sac, thdr.stat); + return 0; +} + +char * +rwstat(Fid *f) +{ + if(f->busy == 0) + return Enotexist; + return Erdonly; +} + +Sac* +saccpy(Sac *s) +{ + Sac *ss; + + ss = malloc(sizeof(Sac)); + *ss = *s; + if(ss->path) + incref(ss->path); + return ss; +} + +SacPath * +sacpathalloc(SacPath *p, vlong blocks, int entry) +{ + SacPath *pp = malloc(sizeof(SacPath)); + pp->ref = 1; + pp->blocks = blocks; + pp->entry = entry; + pp->up = p; + return pp; +} + +static void +sacpathfree(SacPath *p) +{ + if(p == nil) + return; + if(decref(p) > 0) + return; + sacpathfree(p->up); + free(p); +} + + +void +sacfree(Sac *s) +{ + sacpathfree(s->path); + free(s); +} + +void +sacstat(SacDir *s, char *buf) +{ + Dir dir; + + memmove(dir.name, s->name, NAMELEN); + dir.qid = (Qid){getl(s->qid), 0}; + dir.mode = getl(s->mode); + dir.length = getv(s->length); + if(dir.mode &CHDIR) + dir.length *= DIRLEN; + strcpy(dir.uid, s->uid); + strcpy(dir.gid, s->gid); + dir.atime = getl(s->atime); + dir.mtime = getl(s->mtime); + convD2M(&dir, buf); +} + +void +loadblock(void *buf, uchar *offset, int blocksize) +{ + vlong block, n; + + block = getv(offset); + if(block < 0) { + block = -block; + n = getv(offset+8); + if(n < 0) + n = -n; + n -= block; +//fprint(2, "blocksize = %d, block = %lld n = %lld\n", blocksize, block, n); + if(unsac(buf, data+block, blocksize, n)<0) + panic("unsac failed!"); + } else { + memmove(buf, data+block, blocksize); + } +} + +Sac* +sacparent(Sac *s) +{ + uchar *blocks; + SacDir *buf; + int per, i; + SacPath *p; + + p = s->path; + if(p == nil || p->up == nil) { + pathfree(p); + *s = root; + return s; + } + p = p->up; + +//fprint(2, "sacparent = %lld %d\n", p->blocks, p->entry); + blocks = data + p->blocks; + per = blocksize/sizeof(SacDir); + i = p->entry/per; + buf = malloc(per*sizeof(SacDir)); + loadblock(buf, blocks + i*8, per*sizeof(SacDir)); + s->SacDir = buf[p->entry-i*per]; +//fprint(2, "sacparent = %s\n", s->name); + free(buf); + incref(p); + pathfree(s->path); + s->path = p; + return s; +} + +int +sacdirread(Sac *s, char *p, long off, long cnt) +{ + uchar *blocks; + SacDir *buf; + int iblock, per, i, j, ndir; + + blocks = data + getv(s->blocks); + per = blocksize/sizeof(SacDir); + ndir = getv(s->length); + off /= DIRLEN; + cnt /= DIRLEN; + if(off >= ndir) + return 0; + if(cnt > ndir-off) + cnt = ndir-off; + iblock = -1; + buf = malloc(per*sizeof(SacDir)); + for(i=off; iblocks); + per = blocksize/sizeof(SacDir); + ndir = getv(s->length); + buf = malloc(per*sizeof(SacDir)); + iblock = -1; + + if(1) { + // linear search + for(i=0; iname); + if(k == 0) { +//print("walk %s %lld %d\n", name, getv(s->blocks), i); + s->path = sacpathalloc(s->path, getv(s->blocks), i); + s->SacDir = *sd; + free(buf); + return s; + } + } + free(buf); + return 0; + } + + // binary search + top = ndir; + bot = 0; + while(bot != top){ +if(bot>top) +sysfatal("binary serach failed: %d %d\n", bot, top); + i = (bot+top)>>1; + j = i/per; + if(j != iblock) { + loadblock(buf, blocks + j*8, per*sizeof(SacDir)); + iblock = j; + } + j *= per; + sd = buf+i-j; + k = strcmp(name, sd->name); + if(k == 0) { + s->path = sacpathalloc(s->path, getv(s->blocks), i); + s->SacDir = *sd; + free(buf); + } + if(k < 0) { + top = i; + sd = buf; + if(strcmp(name, sd->name) < 0) + top = j; + } else { + bot = i+1; + if(ndir-j < per) + i = ndir-j; + else + i = per; + sd = buf+i-1; + if(strcmp(name, sd->name) > 0) + bot = j+i; + } + } + return 0; +} + +Fid * +newfid(int fid) +{ + Fid *f, *ff; + + ff = 0; + for(f = fids; f; f = f->next) + if(f->fid == fid) + return f; + else if(!ff && !f->busy) + ff = f; + if(ff){ + ff->fid = fid; + return ff; + } + f = malloc(sizeof *f); +//fprint(2, "newfid\n"); + memset(f, 0 , sizeof(Fid)); + f->fid = fid; + f->next = fids; + fids = f; + return f; +} + +void +io(void) +{ + char *err; + int n; + + for(;;){ + /* + * reading from a pipe or a network device + * will give an error after a few eof reads + * however, we cannot tell the difference + * between a zero-length read and an interrupt + * on the processes writing to us, + * so we wait for the error + */ + n = read(mfd[0], mdata, sizeof mdata); + if(n == 0) + continue; + if(n < 0) + error("mount read"); + if(convM2S(mdata, &rhdr, n) == 0) + continue; + + if(debug) + fprint(2, "sacfs:<-%F\n", &rhdr); + + thdr.data = mdata + MAXMSG; + if(!fcalls[rhdr.type]) + err = "bad fcall type"; + else + err = (*fcalls[rhdr.type])(newfid(rhdr.fid)); + if(err){ + thdr.type = Rerror; + strncpy(thdr.ename, err, ERRLEN); + }else{ + thdr.type = rhdr.type + 1; + thdr.fid = rhdr.fid; + } + thdr.tag = rhdr.tag; + if(debug) + fprint(2, "ramfs:->%F\n", &thdr);/**/ + n = convS2M(&thdr, mdata); + if(write(mfd[1], mdata, n) != n) + error("mount write"); + } +} + +int +perm(Fid *f, Sac *s, int p) +{ + ulong perm = getl(s->mode); + if((p*Pother) & perm) + return 1; + if(strcmp(f->user, s->gid)==0 && ((p*Pgroup) & perm)) + return 1; + if(strcmp(f->user, s->uid)==0 && ((p*Powner) & perm)) + return 1; + return 0; +} + + +ulong +getl(void *p) +{ + uchar *a = p; + + return (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3]; +} + +vlong +getv(void *p) +{ + uchar *a = p; + ulong l0, l1; + vlong v; + + l0 = (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3]; + a += 4; + l1 = (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3]; + + v = l0; + v <<= 32; + v |= l1; + return v; +} + +Dev sacdevtab = { + 'C', + "sac", + + devreset, + sacinit, + sacattach, + sacclone, + sacwalk, + sacstat, + sacopen, + devcreate, + sacclose, + sacread, + devbread, + sacwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/mpc/devuart.c b/mpc/devuart.c index 4a1ab092daeb0b243d6b356fc96c94ef5168ca1b..e0bcd8a3fae2a481a272911c28d93d7dd63d6b64 100644 --- a/mpc/devuart.c +++ b/mpc/devuart.c @@ -11,7 +11,7 @@ enum { FADS823 = 1, - Rbufsize= 512, + Rbufsize= 32, // read buffer size Nuart= 3, /* max per machine (SMC1, SMC2, SCC2) */ CTLS= 's'&037, CTLQ= 'q'&037, @@ -188,8 +188,6 @@ struct Uart Queue *iq; Queue *oq; - /* staging areas to avoid some of the per character costs */ - uchar istage[Rbufsize][2]; /* double buffered */ int rdrx; /* last buffer read */ Lock plock; /* for output variables */ @@ -230,12 +228,22 @@ uartsetbuf(Uart *up) { IOCparam *p; BD *bd; + int n; + uchar *buf; p = up->param; p->rfcr = 0x18; p->tfcr = 0x18; p->mrblr = Rbufsize; + // allocate buffer for receive + // the buffers need to be CACHELINESZ aligned and + // a multiple of CACHELINESZ long in order for + // the manul cache coherence to work + n = 2*ROUND(Rbufsize, CACHELINESZ)+CACHELINESZ-1; + buf = malloc(n); + buf = (uchar*)ROUND((long)buf, CACHELINESZ); + if((bd = up->rxb) == nil){ bd = bdalloc(2); up->rxb = bd; @@ -243,12 +251,12 @@ uartsetbuf(Uart *up) p->rbase = (ushort)bd; bd->status = BDEmpty|BDInt; bd->length = 0; - bd->addr = PADDR(up->istage[0]); + bd->addr = PADDR(buf); bd++; bd->status = BDEmpty|BDInt|BDWrap; bd->length = 0; - bd->addr = PADDR(up->istage[1]); - dcflush(up->istage, sizeof(up->istage)); + bd->addr = PADDR(buf + ROUND(Rbufsize, CACHELINESZ)); + dcflush(buf, 2*ROUND(Rbufsize, CACHELINESZ)); up->rdrx = 0; if((bd = up->txb) == nil){ @@ -288,7 +296,7 @@ smcsetup(Uart *up) /* SMC protocol parameters */ p = (Uartsmc*)up->param; up->brkcr = &p->brkcr; - p->maxidl = 1; + p->maxidl = 10; p->brkln = 0; p->brkec = 0; p->brkcr = 1; @@ -892,6 +900,8 @@ uartspecial(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int p->opens++; } +int lastc = 'x'; + static int uartinput(Uart *p, BD *bd) { @@ -922,8 +932,10 @@ uartinput(Uart *p, BD *bd) } /* BUG? should discard on/off char? */ } - if(p->putc) + if(p->putc) { (*p->putc)(p->iq, ch); + lastc = ch; + } } } if(p->putc == nil && l > 0) @@ -1021,6 +1033,7 @@ uartintr(Uart *p, int events) iunlock(&p->plock); } } +#ifdef XXX eieio(); /* TO DO: modem status isn't available on 82xFADS */ if(dokick && p->cts && !p->blocked){ @@ -1030,7 +1043,10 @@ uartintr(Uart *p, int events) iunlock(&p->plock); } cpmop(RestartTx, p->cpmid, 0); - } else if (events & TXE) + } else +#endif + + if (events & TXE) cpmop(RestartTx, p->cpmid, 0); } @@ -1215,8 +1231,8 @@ uartstatus(Chan*, Uart *p, void *buf, long n, long offset) IMM *io; char str[256]; - sprint(str, "opens %d ferr %lud oerr %lud crcerr %lud baud %lud perr %lud intr %lud", p->opens, - p->frame, p->overrun, p->crcerr, p->baud, p->perror, p->interrupts); + sprint(str, "opens %d ferr %lud oerr %lud crcerr %lud baud %lud perr %lud intr %lud lastc = %c", p->opens, + p->frame, p->overrun, p->crcerr, p->baud, p->perror, p->interrupts, lastc); /* TO DO: cts, dsr, ring, dcd, dtr, rts aren't all available on 82xFADS */ io = m->iomem; if(p->scc){ diff --git a/mpc/ether589.c b/mpc/ether589.c new file mode 100644 index 0000000000000000000000000000000000000000..d8931e2ddcc712827f6f61b23e1604100f822889 --- /dev/null +++ b/mpc/ether589.c @@ -0,0 +1,156 @@ +/* + * 3C589 and 3C562. + * To do: + * check xcvr10Base2 still works (is GlobalReset necessary?). + * pull the station address out of the card space for the 3C562, + * it has no EEPROM. + */ +#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 { /* all windows */ + CommandR = 0x000E, + IntStatusR = 0x000E, +}; + +enum { /* Commands */ + GlobalReset = 0x0000, + SelectRegisterWindow = 0x0001, + RxReset = 0x0005, + TxReset = 0x000B, + AcknowledgeInterrupt = 0x000D, +}; + +enum { /* IntStatus bits */ + commandInProgress = 0x1000, +}; + +#define COMMAND(port, cmd, a) outs((port)+CommandR, ((cmd)<<11)|(a)) +#define STATUS(port) ins((port)+IntStatusR) + +enum { /* Window 0 - setup */ + Wsetup = 0x0000, + /* registers */ + ManufacturerID = 0x0000, /* 3C5[08]*, 3C59[27] */ + ProductID = 0x0002, /* 3C5[08]*, 3C59[27] */ + ConfigControl = 0x0004, /* 3C5[08]*, 3C59[27] */ + AddressConfig = 0x0006, /* 3C5[08]*, 3C59[27] */ + ResourceConfig = 0x0008, /* 3C5[08]*, 3C59[27] */ + EepromCommand = 0x000A, + EepromData = 0x000C, + /* AddressConfig Bits */ + autoSelect9 = 0x0080, + xcvrMask9 = 0xC000, + /* ConfigControl bits */ + Ena = 0x0001, + base10TAvailable9 = 0x0200, + coaxAvailable9 = 0x1000, + auiAvailable9 = 0x2000, + /* EepromCommand bits */ + EepromReadRegister = 0x0080, + EepromBusy = 0x8000, +}; + +enum { /* Window 1 - operating set */ + Wop = 0x0001, +}; + +enum { /* Window 3 - FIFO management */ + Wfifo = 0x0003, + /* registers */ + InternalConfig = 0x0000, /* 3C509B, 3C589, 3C59[0257] */ + /* InternalConfig bits */ + xcvr10BaseT = 0x00000000, + xcvr10Base2 = 0x00300000, +}; + +enum { /* Window 4 - diagnostic */ + Wdiagnostic = 0x0004, + /* registers */ + MediaStatus = 0x000A, + /* MediaStatus bits */ + linkBeatDetect = 0x0800, +}; + +extern int etherelnk3reset(Ether*); + +static int +configASIC(Ether* ether, int port, int xcvr) +{ + int x; + + /* set Window 0 configuration registers */ + COMMAND(port, SelectRegisterWindow, Wsetup); + outs(port+ConfigControl, Ena); + + /* IRQ must be 3 on 3C589/3C562 */ + outs(port + ResourceConfig, 0x3F00); + + x = ins(port+AddressConfig) & ~xcvrMask9; + x |= (xcvr>>20)<<14; + outs(port+AddressConfig, x); + + COMMAND(port, TxReset, 0); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, RxReset, 0); + while(STATUS(port) & commandInProgress) + ; + + return etherelnk3reset(ether); +} + +static int +reset(Ether* ether) +{ + int slot; + int port; + + if(ether->irq == 0) + ether->irq = 10; + if(ether->port == 0) + ether->port = 0x240; + port = ether->port; + + if((slot = pcmspecial(ether->type, ether)) < 0) + return -1; + + /* try configuring as a 10BaseT */ + if(configASIC(ether, port, xcvr10BaseT) < 0){ + pcmspecialclose(slot); + return -1; + } + delay(100); + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + if(ins(port+MediaStatus) & linkBeatDetect){ + COMMAND(port, SelectRegisterWindow, Wop); + print("#l%d: xcvr10BaseT %s\n", ether->ctlrno, ether->type); + return 0; + } + + /* try configuring as a 10base2 */ + COMMAND(port, GlobalReset, 0); + if(configASIC(ether, port, xcvr10Base2) < 0){ + pcmspecialclose(slot); + return -1; + } + print("#l%d: xcvr10Base2 %s\n", ether->ctlrno, ether->type); + + return 0; +} + +void +ether589link(void) +{ + addethercard("3C589", reset); + addethercard("3C562", reset); + addethercard("589E", reset); +} diff --git a/mpc/etherelnk3.c b/mpc/etherelnk3.c new file mode 100644 index 0000000000000000000000000000000000000000..944b0ba9b93aa7afffcc30cf2d6174a2bf0c9315 --- /dev/null +++ b/mpc/etherelnk3.c @@ -0,0 +1,1854 @@ +/* + * Etherlink III, Fast EtherLink and Fast EtherLink XL adapters. + * To do: + * check robustness in the face of errors (e.g. busmaster & rxUnderrun); + * RxEarly and busmaster; + * autoSelect; + * PCI latency timer and master enable; + * errata list; + * rewrite all initialisation; + * handle the cyclone adapter. + * + * Product ID: + * 9150 ISA 3C509[B] + * 9050 ISA 3C509[B]-TP + * 9450 ISA 3C509[B]-COMBO + * 9550 ISA 3C509[B]-TPO + * + * 9350 EISA 3C579 + * 9250 EISA 3C579-TP + * + * 5920 EISA 3C592-[TP|COMBO|TPO] + * 5970 EISA 3C597-TX Fast Etherlink 10BASE-T/100BASE-TX + * 5971 EISA 3C597-T4 Fast Etherlink 10BASE-T/100BASE-T4 + * 5972 EISA 3C597-MII Fast Etherlink 10BASE-T/MII + * + * 5900 PCI 3C590-[TP|COMBO|TPO] + * 5950 PCI 3C595-TX Fast Etherlink Shared 10BASE-T/100BASE-TX + * 5951 PCI 3C595-T4 Fast Etherlink Shared 10BASE-T/100BASE-T4 + * 5952 PCI 3C595-MII Fast Etherlink 10BASE-T/MII + * + * 9000 PCI 3C900-TPO Etherlink III XL PCI 10BASE-T + * 9001 PCI 3C900-COMBO Etherlink III XL PCI 10BASE-T/10BASE-2/AUI + * 9005 PCI 3C900B-COMBO Etherlink III XL PCI 10BASE-T/10BASE-2/AUI + * 9050 PCI 3C905-TX Fast Etherlink XL Shared 10BASE-T/100BASE-TX + * 9051 PCI 3C905-T4 Fast Etherlink Shared 10BASE-T/100BASE-T4 + * 9055 PCI 3C905B-TX Fast Etherlink Shared 10BASE-T/100BASE-TX + * + * 9058 PCMCIA 3C589[B]-[TP|COMBO] + * + * 627C MCA 3C529 + * 627D MCA 3C529-TP + */ +#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" + +#define XCVRDEBUG if(0)print + +enum { + IDport = 0x0110, /* anywhere between 0x0100 and 0x01F0 */ +}; + +enum { /* all windows */ + CommandR = 0x000E, + IntStatusR = 0x000E, +}; + +enum { /* Commands */ + GlobalReset = 0x0000, + SelectRegisterWindow = 0x0001, + EnableDcConverter = 0x0002, + RxDisable = 0x0003, + RxEnable = 0x0004, + RxReset = 0x0005, + Stall = 0x0006, /* 3C90x */ + TxDone = 0x0007, + RxDiscard = 0x0008, + TxEnable = 0x0009, + TxDisable = 0x000A, + TxReset = 0x000B, + RequestInterrupt = 0x000C, + AcknowledgeInterrupt = 0x000D, + SetInterruptEnable = 0x000E, + SetIndicationEnable = 0x000F, /* SetReadZeroMask */ + SetRxFilter = 0x0010, + SetRxEarlyThresh = 0x0011, + SetTxAvailableThresh = 0x0012, + SetTxStartThresh = 0x0013, + StartDma = 0x0014, /* initiate busmaster operation */ + StatisticsEnable = 0x0015, + StatisticsDisable = 0x0016, + DisableDcConverter = 0x0017, + SetTxReclaimThresh = 0x0018, /* PIO-only adapters */ + PowerUp = 0x001B, /* not all adapters */ + PowerDownFull = 0x001C, /* not all adapters */ + PowerAuto = 0x001D, /* not all adapters */ +}; + +enum { /* (Global|Rx|Tx)Reset command bits */ + tpAuiReset = 0x0001, /* 10BaseT and AUI transceivers */ + endecReset = 0x0002, /* internal Ethernet encoder/decoder */ + networkReset = 0x0004, /* network interface logic */ + fifoReset = 0x0008, /* FIFO control logic */ + aismReset = 0x0010, /* autoinitialise state-machine logic */ + hostReset = 0x0020, /* bus interface logic */ + dmaReset = 0x0040, /* bus master logic */ + vcoReset = 0x0080, /* on-board 10Mbps VCO */ + updnReset = 0x0100, /* upload/download (Rx/TX) logic */ + + resetMask = 0x01FF, +}; + +enum { /* Stall command bits */ + upStall = 0x0000, + upUnStall = 0x0001, + dnStall = 0x0002, + dnUnStall = 0x0003, +}; + +enum { /* SetRxFilter command bits */ + receiveIndividual = 0x0001, /* match station address */ + receiveMulticast = 0x0002, + receiveBroadcast = 0x0004, + receiveAllFrames = 0x0008, /* promiscuous */ +}; + +enum { /* StartDma command bits */ + Upload = 0x0000, /* transfer data from adapter to memory */ + Download = 0x0001, /* transfer data from memory to adapter */ +}; + +enum { /* IntStatus bits */ + interruptLatch = 0x0001, + hostError = 0x0002, /* Adapter Failure */ + txComplete = 0x0004, + txAvailable = 0x0008, + rxComplete = 0x0010, + rxEarly = 0x0020, + intRequested = 0x0040, + updateStats = 0x0080, + transferInt = 0x0100, /* Bus Master Transfer Complete */ + dnComplete = 0x0200, + upComplete = 0x0400, + busMasterInProgress = 0x0800, + commandInProgress = 0x1000, + + interruptMask = 0x07FE, +}; + +#define COMMAND(port, cmd, a) outs((port)+CommandR, ((cmd)<<11)|(a)) +#define STATUS(port) ins((port)+IntStatusR) + +enum { /* Window 0 - setup */ + Wsetup = 0x0000, + /* registers */ + ManufacturerID = 0x0000, /* 3C5[08]*, 3C59[27] */ + ProductID = 0x0002, /* 3C5[08]*, 3C59[27] */ + ConfigControl = 0x0004, /* 3C5[08]*, 3C59[27] */ + AddressConfig = 0x0006, /* 3C5[08]*, 3C59[27] */ + ResourceConfig = 0x0008, /* 3C5[08]*, 3C59[27] */ + EepromCommand = 0x000A, + EepromData = 0x000C, + /* AddressConfig Bits */ + autoSelect9 = 0x0080, + xcvrMask9 = 0xC000, + /* ConfigControl bits */ + Ena = 0x0001, + base10TAvailable9 = 0x0200, + coaxAvailable9 = 0x1000, + auiAvailable9 = 0x2000, + /* EepromCommand bits */ + EepromReadRegister = 0x0080, + EepromBusy = 0x8000, +}; + +#define EEPROMCMD(port, cmd, a) outs((port)+EepromCommand, (cmd)|(a)) +#define EEPROMBUSY(port) (ins((port)+EepromCommand) & EepromBusy) +#define EEPROMDATA(port) ins((port)+EepromData) + +enum { /* Window 1 - operating set */ + Wop = 0x0001, + /* registers */ + Fifo = 0x0000, + RxError = 0x0004, /* 3C59[0257] only */ + RxStatus = 0x0008, + Timer = 0x000A, + TxStatus = 0x000B, + TxFree = 0x000C, + /* RxError bits */ + rxOverrun = 0x0001, + runtFrame = 0x0002, + alignmentError = 0x0004, /* Framing */ + crcError = 0x0008, + oversizedFrame = 0x0010, + dribbleBits = 0x0080, + /* RxStatus bits */ + rxBytes = 0x1FFF, /* 3C59[0257] mask */ + rxBytes9 = 0x07FF, /* 3C5[078]9 mask */ + rxError9 = 0x3800, /* 3C5[078]9 error mask */ + rxOverrun9 = 0x0000, + oversizedFrame9 = 0x0800, + dribbleBits9 = 0x1000, + runtFrame9 = 0x1800, + alignmentError9 = 0x2000, /* Framing */ + crcError9 = 0x2800, + rxError = 0x4000, + rxIncomplete = 0x8000, + /* TxStatus Bits */ + txStatusOverflow = 0x0004, + maxCollisions = 0x0008, + txUnderrun = 0x0010, + txJabber = 0x0020, + interruptRequested = 0x0040, + txStatusComplete = 0x0080, +}; + +enum { /* Window 2 - station address */ + Wstation = 0x0002, + + ResetOp905B = 0x000C, +}; + +enum { /* Window 3 - FIFO management */ + Wfifo = 0x0003, + /* registers */ + InternalConfig = 0x0000, /* 3C509B, 3C589, 3C59[0257] */ + OtherInt = 0x0004, /* 3C59[0257] */ + RomControl = 0x0006, /* 3C509B, 3C59[27] */ + MacControl = 0x0006, /* 3C59[0257] */ + ResetOptions = 0x0008, /* 3C59[0257] */ + MediaOptions = 0x0008, /* 3C905B */ + RxFree = 0x000A, + /* InternalConfig bits */ + disableBadSsdDetect = 0x00000100, + ramLocation = 0x00000200, /* 0 external, 1 internal */ + ramPartition5to3 = 0x00000000, + ramPartition3to1 = 0x00010000, + ramPartition1to1 = 0x00020000, + ramPartition3to5 = 0x00030000, + ramPartitionMask = 0x00030000, + xcvr10BaseT = 0x00000000, + xcvrAui = 0x00100000, /* 10BASE5 */ + xcvr10Base2 = 0x00300000, + xcvr100BaseTX = 0x00400000, + xcvr100BaseFX = 0x00500000, + xcvrMii = 0x00600000, + xcvrMask = 0x00700000, + autoSelect = 0x01000000, + /* MacControl bits */ + deferExtendEnable = 0x0001, + deferTimerSelect = 0x001E, /* mask */ + fullDuplexEnable = 0x0020, + allowLargePackets = 0x0040, + extendAfterCollision = 0x0080, /* 3C90xB */ + flowControlEnable = 0x0100, /* 3C90xB */ + vltEnable = 0x0200, /* 3C90xB */ + /* ResetOptions bits */ + baseT4Available = 0x0001, + baseTXAvailable = 0x0002, + baseFXAvailable = 0x0004, + base10TAvailable = 0x0008, + coaxAvailable = 0x0010, + auiAvailable = 0x0020, + miiConnector = 0x0040, +}; + +enum { /* Window 4 - diagnostic */ + Wdiagnostic = 0x0004, + /* registers */ + VcoDiagnostic = 0x0002, + FifoDiagnostic = 0x0004, + NetworkDiagnostic = 0x0006, + PhysicalMgmt = 0x0008, + MediaStatus = 0x000A, + BadSSD = 0x000C, + UpperBytesOk = 0x000D, + /* FifoDiagnostic bits */ + txOverrun = 0x0400, + rxUnderrun = 0x2000, + receiving = 0x8000, + /* PhysicalMgmt bits */ + mgmtClk = 0x0001, + mgmtData = 0x0002, + mgmtDir = 0x0004, + cat5LinkTestDefeat = 0x8000, + /* MediaStatus bits */ + dataRate100 = 0x0002, + crcStripDisable = 0x0004, + enableSqeStats = 0x0008, + collisionDetect = 0x0010, + carrierSense = 0x0020, + jabberGuardEnable = 0x0040, + linkBeatEnable = 0x0080, + jabberDetect = 0x0200, + polarityReversed = 0x0400, + linkBeatDetect = 0x0800, + txInProg = 0x1000, + dcConverterEnabled = 0x4000, + auiDisable = 0x8000, /* 10BaseT transceiver selected */ +}; + +enum { /* Window 5 - internal state */ + Wstate = 0x0005, + /* registers */ + TxStartThresh = 0x0000, + TxAvailableThresh = 0x0002, + RxEarlyThresh = 0x0006, + RxFilter = 0x0008, + InterruptEnable = 0x000A, + IndicationEnable = 0x000C, +}; + +enum { /* Window 6 - statistics */ + Wstatistics = 0x0006, + /* registers */ + CarrierLost = 0x0000, + SqeErrors = 0x0001, + MultipleColls = 0x0002, + SingleCollFrames = 0x0003, + LateCollisions = 0x0004, + RxOverruns = 0x0005, + FramesXmittedOk = 0x0006, + FramesRcvdOk = 0x0007, + FramesDeferred = 0x0008, + UpperFramesOk = 0x0009, + BytesRcvdOk = 0x000A, + BytesXmittedOk = 0x000C, +}; + +enum { /* Window 7 - bus master operations */ + Wmaster = 0x0007, + /* registers */ + MasterAddress = 0x0000, + MasterLen = 0x0006, + MasterStatus = 0x000C, + /* MasterStatus bits */ + masterAbort = 0x0001, + targetAbort = 0x0002, + targetRetry = 0x0004, + targetDisc = 0x0008, + masterDownload = 0x1000, + masterUpload = 0x4000, + masterInProgress = 0x8000, + + masterMask = 0xD00F, +}; + +enum { /* 3C90x extended register set */ + PktStatus = 0x0020, /* 32-bits */ + DnListPtr = 0x0024, /* 32-bits, 8-byte aligned */ + FragAddr = 0x0028, /* 32-bits */ + FragLen = 0x002C, /* 16-bits */ + ListOffset = 0x002E, /* 8-bits */ + TxFreeThresh = 0x002F, /* 8-bits */ + UpPktStatus = 0x0030, /* 32-bits */ + FreeTimer = 0x0034, /* 16-bits */ + UpListPtr = 0x0038, /* 32-bits, 8-byte aligned */ + + /* PktStatus bits */ + fragLast = 0x00000001, + dnCmplReq = 0x00000002, + dnStalled = 0x00000004, + upCompleteX = 0x00000008, + dnCompleteX = 0x00000010, + upRxEarlyEnable = 0x00000020, + armCountdown = 0x00000040, + dnInProg = 0x00000080, + counterSpeed = 0x00000010, /* 0 3.2uS, 1 320nS */ + countdownMode = 0x00000020, + /* UpPktStatus bits (dpd->control) */ + upPktLenMask = 0x00001FFF, + upStalled = 0x00002000, + upError = 0x00004000, + upPktComplete = 0x00008000, + upOverrun = 0x00010000, /* RxError<<16 */ + upRuntFrame = 0x00020000, + upAlignmentError = 0x00040000, + upCRCError = 0x00080000, + upOversizedFrame = 0x00100000, + upDribbleBits = 0x00800000, + upOverflow = 0x01000000, + + dnIndicate = 0x80000000, /* FrameStartHeader (dpd->control) */ + + updnLastFrag = 0x80000000, /* (dpd->len) */ + + Nup = 32, + Ndn = 64, +}; + +/* + * Up/Dn Packet Descriptors. + * The hardware info (np, control, addr, len) must be 8-byte aligned + * and this structure size must be a multiple of 8. + */ +typedef struct Pd Pd; +typedef struct Pd { + ulong np; /* next pointer */ + ulong control; /* FSH or UpPktStatus */ + ulong addr; + ulong len; + + Pd* next; + Block* bp; +} Pd; + +typedef struct { + Lock wlock; /* window access */ + + int attached; + int busmaster; + Block* rbp; /* receive buffer */ + + Block* txbp; /* FIFO -based transmission */ + int txthreshold; + int txbusy; + + int nup; /* full-busmaster -based reception */ + void* upbase; + Pd* upr; + Pd* uphead; + + int ndn; /* full-busmaster -based transmission */ + void* dnbase; + Pd* dnr; + Pd* dnhead; + Pd* dntail; + int dnq; + + long interrupts; /* statistics */ + long timer; + long stats[BytesRcvdOk+3]; + + int upqmax; + int upqmaxhw; + ulong upinterrupts; + ulong upqueued; + ulong upstalls; + int dnqmax; + int dnqmaxhw; + ulong dninterrupts; + ulong dnqueued; + + int xcvr; /* transceiver type */ + int rxstatus9; /* old-style RxStatus register */ + int rxearly; /* RxEarlyThreshold */ + int ts; /* threshold shift */ + int upenabled; + int dnenabled; +} Ctlr; + +static void +init905(Ctlr* ctlr) +{ + Block *bp; + Pd *pd, *prev; + + /* + * Create rings for the receive and transmit sides. + * Take care with alignment: + * make sure ring base is 8-byte aligned; + * make sure each entry is 8-byte aligned. + */ + ctlr->upbase = malloc((ctlr->nup+1)*sizeof(Pd)); + ctlr->upr = (Pd*)ROUNDUP((ulong)ctlr->upbase, 8); + + prev = ctlr->upr; + for(pd = &ctlr->upr[ctlr->nup-1]; pd >= ctlr->upr; pd--){ + pd->np = PADDR(&prev->np); + pd->control = 0; + bp = allocb(sizeof(Etherpkt)); + pd->addr = PADDR(bp->rp); + pd->len = updnLastFrag|sizeof(Etherpkt); + + pd->next = prev; + prev = pd; + pd->bp = bp; + } + ctlr->uphead = ctlr->upr; + + ctlr->dnbase = malloc((ctlr->ndn+1)*sizeof(Pd)); + ctlr->dnr = (Pd*)ROUNDUP((ulong)ctlr->dnbase, 8); + + prev = ctlr->dnr; + for(pd = &ctlr->dnr[ctlr->ndn-1]; pd >= ctlr->dnr; pd--){ + pd->next = prev; + prev = pd; + } + ctlr->dnhead = ctlr->dnr; + ctlr->dntail = ctlr->dnr; + ctlr->dnq = 0; +} + +static Block* +rbpalloc(Block* (*f)(int)) +{ + Block *bp; + ulong addr; + + /* + * The receive buffers must be on a 32-byte + * boundary for EISA busmastering. + */ + if(bp = f(ROUNDUP(sizeof(Etherpkt), 4) + 31)){ + addr = (ulong)bp->base; + addr = ROUNDUP(addr, 32); + bp->rp = (uchar*)addr; + } + + return bp; +} + +static uchar* +startdma(Ether* ether, ulong address) +{ + int port, status, w; + uchar *wp; + + port = ether->port; + + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wmaster); + + wp = KADDR(inl(port+MasterAddress)); + status = ins(port+MasterStatus); + if(status & (masterInProgress|targetAbort|masterAbort)) + print("#l%d: BM status 0x%uX\n", ether->ctlrno, status); + outs(port+MasterStatus, masterMask); + outl(port+MasterAddress, address); + outs(port+MasterLen, sizeof(Etherpkt)); + COMMAND(port, StartDma, Upload); + + COMMAND(port, SelectRegisterWindow, w); + return wp; +} + +static void +promiscuous(void* arg, int on) +{ + int filter, port; + Ether *ether; + + ether = (Ether*)arg; + port = ether->port; + + filter = receiveBroadcast|receiveIndividual; + if(ether->nmaddr) + filter |= receiveMulticast; + if(on) + filter |= receiveAllFrames; + COMMAND(port, SetRxFilter, filter); +} + +static void +multicast(void* arg, uchar *addr, int on) +{ + int filter, port; + Ether *ether; + + USED(addr, on); + + ether = (Ether*)arg; + port = ether->port; + + filter = receiveBroadcast|receiveIndividual; + if(ether->nmaddr) + filter |= receiveMulticast; + if(ether->prom) + filter |= receiveAllFrames; + COMMAND(port, SetRxFilter, filter); +} + +static void +attach(Ether* ether) +{ + int port, x; + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->wlock); + if(ctlr->attached){ + iunlock(&ctlr->wlock); + return; + } + + port = ether->port; + + /* + * Set the receiver packet filter for this and broadcast addresses, + * set the interrupt masks for all interrupts, enable the receiver + * and transmitter. + */ + promiscuous(ether, ether->prom); + + x = interruptMask; + if(ctlr->busmaster == 1) + x &= ~(rxEarly|rxComplete); + else{ + if(ctlr->dnenabled) + x &= ~transferInt; + if(ctlr->upenabled) + x &= ~(rxEarly|rxComplete); + } + COMMAND(port, SetIndicationEnable, x); + COMMAND(port, SetInterruptEnable, x); + + COMMAND(port, RxEnable, 0); + COMMAND(port, TxEnable, 0); + + /* + * Prime the busmaster channel for receiving directly into a + * receive packet buffer if necessary. + */ + if(ctlr->busmaster == 1) + startdma(ether, PADDR(ctlr->rbp->rp)); + else{ + if(ctlr->upenabled) + outl(port+UpListPtr, PADDR(&ctlr->uphead->np)); + } + + ctlr->attached = 1; + iunlock(&ctlr->wlock); +} + +static void +statistics(Ether* ether) +{ + int port, i, u, w; + Ctlr *ctlr; + + port = ether->port; + ctlr = ether->ctlr; + + /* + * 3C59[27] require a read between a PIO write and + * reading a statistics register. + */ + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wstatistics); + STATUS(port); + + for(i = 0; i < UpperFramesOk; i++) + ctlr->stats[i] += inb(port+i) & 0xFF; + u = inb(port+UpperFramesOk) & 0xFF; + ctlr->stats[FramesXmittedOk] += (u & 0x30)<<4; + ctlr->stats[FramesRcvdOk] += (u & 0x03)<<8; + ctlr->stats[BytesRcvdOk] += ins(port+BytesRcvdOk) & 0xFFFF; + ctlr->stats[BytesRcvdOk+1] += ins(port+BytesXmittedOk) & 0xFFFF; + + switch(ctlr->xcvr){ + + case xcvrMii: + case xcvr100BaseTX: + case xcvr100BaseFX: + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + STATUS(port); + ctlr->stats[BytesRcvdOk+2] += inb(port+BadSSD); + break; + } + + COMMAND(port, SelectRegisterWindow, w); +} + +static void +txstart(Ether* ether) +{ + int port, len; + Ctlr *ctlr; + Block *bp; + + port = ether->port; + ctlr = ether->ctlr; + + /* + * Attempt to top-up the transmit FIFO. If there's room simply + * stuff in the packet length (unpadded to a dword boundary), the + * packet data (padded) and remove the packet from the queue. + * If there's no room post an interrupt for when there is. + * This routine is called both from the top level and from interrupt + * level and expects to be called with ctlr->wlock already locked + * and the correct register window (Wop) in place. + */ + for(;;){ + if(ctlr->txbp){ + bp = ctlr->txbp; + ctlr->txbp = 0; + } + else{ + bp = qget(ether->oq); + if(bp == nil) + break; + } + + len = ROUNDUP(BLEN(bp), 4); + if(len+4 <= ins(port+TxFree)){ + outl(port+Fifo, BLEN(bp)); + outsl(port+Fifo, bp->rp, len/4); + + freeb(bp); + + ether->outpackets++; + } + else{ + ctlr->txbp = bp; + if(ctlr->txbusy == 0){ + ctlr->txbusy = 1; + COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts); + } + break; + } + } +} + +static void +txstart905(Ether* ether) +{ + Ctlr *ctlr; + int port, stalled, timeo; + Block *bp; + Pd *pd; + + ctlr = ether->ctlr; + port = ether->port; + /* + * Free any completed packets. + */ + pd = ctlr->dntail; + while(ctlr->dnq){ + if(PADDR(&pd->np) == inl(port+DnListPtr)) + break; + if(pd->bp){ + freeb(pd->bp); + pd->bp = nil; + } + ctlr->dnq--; + pd = pd->next; + } + ctlr->dntail = pd; + + stalled = 0; + while(ctlr->dnq < (ctlr->ndn-1)){ + bp = qget(ether->oq); + if(bp == nil) + break; + + pd = ctlr->dnhead->next; + pd->np = 0; + pd->control = dnIndicate|BLEN(bp); + pd->addr = PADDR(bp->rp); + pd->len = updnLastFrag|BLEN(bp); + pd->bp = bp; + + if(stalled == 0 && ctlr->dnq && inl(port+DnListPtr)){ + COMMAND(port, Stall, dnStall); + for(timeo = 100; (STATUS(port) & commandInProgress) && timeo; timeo--) + ; + if(timeo == 0) + print("#l%d: dnstall %d\n", ether->ctlrno, timeo); + stalled = 1; + } + + coherence(); + ctlr->dnhead->np = PADDR(&pd->np); + ctlr->dnhead->control &= ~dnIndicate; + ctlr->dnhead = pd; + if(ctlr->dnq == 0) + ctlr->dntail = pd; + ctlr->dnq++; + + ctlr->dnqueued++; + } + + if(ctlr->dnq > ctlr->dnqmax) + ctlr->dnqmax = ctlr->dnq; + + /* + * If the adapter is not currently processing anything + * and there is something on the queue, start it processing. + */ + if(inl(port+DnListPtr) == 0 && ctlr->dnq) + outl(port+DnListPtr, PADDR(&ctlr->dnhead->np)); + if(stalled) + COMMAND(port, Stall, dnUnStall); +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + int port, w; + + port = ether->port; + ctlr = ether->ctlr; + + ilock(&ctlr->wlock); + if(ctlr->dnenabled) + txstart905(ether); + else{ + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wop); + txstart(ether); + COMMAND(port, SelectRegisterWindow, w); + } + iunlock(&ctlr->wlock); +} + +static void +receive905(Ether* ether) +{ + Ctlr *ctlr; + int len, port, q; + Pd *pd; + Block *bp; + + ctlr = ether->ctlr; + port = ether->port; + + if(inl(port+UpPktStatus) & upStalled) + ctlr->upstalls++; + q = 0; + for(pd = ctlr->uphead; pd->control & upPktComplete; pd = pd->next){ + if(pd->control & upError){ + if(pd->control & upOverrun) + ether->overflows++; + if(pd->control & (upOversizedFrame|upRuntFrame)) + ether->buffs++; + if(pd->control & upAlignmentError) + ether->frames++; + if(pd->control & upCRCError) + ether->crcs++; + } + else if(bp = iallocb(sizeof(Etherpkt)+4)){ + len = pd->control & rxBytes; + pd->bp->wp = pd->bp->rp+len; + etheriq(ether, pd->bp, 1); + pd->bp = bp; + pd->addr = PADDR(bp->rp); + coherence(); + } + + pd->control = 0; + COMMAND(port, Stall, upUnStall); + + q++; + } + ctlr->uphead = pd; + + ctlr->upqueued += q; + if(q > ctlr->upqmax) + ctlr->upqmax = q; +} + +static void +receive(Ether* ether) +{ + int len, port, rxerror, rxstatus; + Ctlr *ctlr; + Block *bp; + + port = ether->port; + ctlr = ether->ctlr; + + while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){ + if(ctlr->busmaster == 1 && (STATUS(port) & busMasterInProgress)) + break; + + /* + * If there was an error, log it and continue. + * Unfortunately the 3C5[078]9 has the error info in the status register + * and the 3C59[0257] implement a separate RxError register. + */ + if(rxstatus & rxError){ + if(ctlr->rxstatus9){ + switch(rxstatus & rxError9){ + + case rxOverrun9: + ether->overflows++; + break; + + case oversizedFrame9: + case runtFrame9: + ether->buffs++; + break; + + case alignmentError9: + ether->frames++; + break; + + case crcError9: + ether->crcs++; + break; + + } + } + else{ + rxerror = inb(port+RxError); + if(rxerror & rxOverrun) + ether->overflows++; + if(rxerror & (oversizedFrame|runtFrame)) + ether->buffs++; + if(rxerror & alignmentError) + ether->frames++; + if(rxerror & crcError) + ether->crcs++; + } + } + + /* + * If there was an error or a new receive buffer can't be + * allocated, discard the packet and go on to the next. + */ + if((rxstatus & rxError) || (bp = rbpalloc(iallocb)) == 0){ + COMMAND(port, RxDiscard, 0); + while(STATUS(port) & commandInProgress) + ; + + if(ctlr->busmaster == 1) + startdma(ether, PADDR(ctlr->rbp->rp)); + + continue; + } + + /* + * A valid receive packet awaits: + * if using PIO, read it into the buffer; + * discard the packet from the FIFO; + * if using busmastering, start a new transfer for + * the next packet and as a side-effect get the + * end-pointer of the one just received; + * pass the packet on to whoever wants it. + */ + if(ctlr->busmaster == 0 || ctlr->busmaster == 2){ + len = (rxstatus & rxBytes9); + ctlr->rbp->wp = ctlr->rbp->rp + len; + insl(port+Fifo, ctlr->rbp->rp, HOWMANY(len, 4)); + } + + COMMAND(port, RxDiscard, 0); + while(STATUS(port) & commandInProgress) + ; + + if(ctlr->busmaster == 1) + ctlr->rbp->wp = startdma(ether, PADDR(bp->rp)); + + etheriq(ether, ctlr->rbp, 1); + ctlr->rbp = bp; + } +} + +static void +interrupt(Ureg*, void* arg) +{ + Ether *ether; + int port, status, s, w, x; + Ctlr *ctlr; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + ilock(&ctlr->wlock); + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wop); + + ctlr->interrupts++; + ctlr->timer += inb(port+Timer) & 0xFF; + while((status = STATUS(port)) & (interruptMask|interruptLatch)){ + if(status & hostError){ + /* + * Adapter failure, try to find out why, reset if + * necessary. What happens if Tx is active and a reset + * occurs, need to retransmit? This probably isn't right. + */ + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+FifoDiagnostic); + COMMAND(port, SelectRegisterWindow, Wop); + print("#l%d: status 0x%uX, diag 0x%uX\n", + ether->ctlrno, status, x); + + if(x & txOverrun){ + if(ctlr->busmaster == 0) + COMMAND(port, TxReset, 0); + else + COMMAND(port, TxReset, (updnReset|dmaReset)); + COMMAND(port, TxEnable, 0); + } + + if(x & rxUnderrun){ + /* + * This shouldn't happen... + * Reset the receiver and restore the filter and RxEarly + * threshold before re-enabling. + * Need to restart any busmastering? + */ + COMMAND(port, SelectRegisterWindow, Wstate); + s = (port+RxFilter) & 0x000F; + COMMAND(port, SelectRegisterWindow, Wop); + COMMAND(port, RxReset, 0); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, SetRxFilter, s); + COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts); + COMMAND(port, RxEnable, 0); + } + + status &= ~hostError; + } + + if(status & (transferInt|rxComplete)){ + receive(ether); + status &= ~(transferInt|rxComplete); + } + + if(status & (upComplete)){ + COMMAND(port, AcknowledgeInterrupt, upComplete); + receive905(ether); + status &= ~upComplete; + ctlr->upinterrupts++; + } + + if(status & txComplete){ + /* + * Pop the TxStatus stack, accumulating errors. + * Adjust the TX start threshold if there was an underrun. + * If there was a Jabber or Underrun error, reset + * the transmitter, taking care not to reset the dma logic + * as a busmaster receive may be in progress. + * For all conditions enable the transmitter. + */ + s = 0; + do{ + if(x = inb(port+TxStatus)) + outb(port+TxStatus, 0); + s |= x; + }while(STATUS(port) & txComplete); + + if(s & txUnderrun){ + if(ctlr->dnenabled){ + while(inl(port+PktStatus) & dnInProg) + ; + } + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + while(ins(port+MediaStatus) & txInProg) + ; + COMMAND(port, SelectRegisterWindow, Wop); + if(ctlr->txthreshold < ETHERMAXTU) + ctlr->txthreshold += ETHERMINTU; + } + + /* + * According to the manual, maxCollisions does not require + * a TxReset, merely a TxEnable. However, evidence points to + * it being necessary on the 3C905. The jury is still out. + * On busy or badly configured networks maxCollisions can + * happen frequently enough for messages to be annoying so + * keep quiet about them by popular request. + */ + if(s & (txJabber|txUnderrun|maxCollisions)){ + if(ctlr->busmaster == 0) + COMMAND(port, TxReset, 0); + else + COMMAND(port, TxReset, (updnReset|dmaReset)); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts); + if(ctlr->busmaster == 2) + outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256)); + if(ctlr->dnenabled) + status |= dnComplete; + } + + if(s & ~(txStatusComplete|maxCollisions)) + print("#l%d: txstatus 0x%uX, threshold %d\n", + ether->ctlrno, s, ctlr->txthreshold); + COMMAND(port, TxEnable, 0); + ether->oerrs++; + status &= ~txComplete; + status |= txAvailable; + } + + if(status & txAvailable){ + COMMAND(port, AcknowledgeInterrupt, txAvailable); + ctlr->txbusy = 0; + txstart(ether); + status &= ~txAvailable; + } + + if(status & dnComplete){ + COMMAND(port, AcknowledgeInterrupt, dnComplete); + txstart905(ether); + status &= ~dnComplete; + ctlr->dninterrupts++; + } + + if(status & updateStats){ + statistics(ether); + status &= ~updateStats; + } + + /* + * Currently, this shouldn't happen. + */ + if(status & rxEarly){ + COMMAND(port, AcknowledgeInterrupt, rxEarly); + status &= ~rxEarly; + } + + /* + * Panic if there are any interrupts not dealt with. + */ + if(status & interruptMask) + panic("#l%d: interrupt mask 0x%uX\n", ether->ctlrno, status); + + COMMAND(port, AcknowledgeInterrupt, interruptLatch); + } + + COMMAND(port, SelectRegisterWindow, w); + iunlock(&ctlr->wlock); +} + +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; + + ilock(&ctlr->wlock); + statistics(ether); + iunlock(&ctlr->wlock); + + p = malloc(READSTR); + len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts); + len += snprint(p+len, READSTR-len, "timer: %lud\n", ctlr->timer); + len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->stats[CarrierLost]); + len += snprint(p+len, READSTR-len, "sqeerrors: %lud\n", ctlr->stats[SqeErrors]); + len += snprint(p+len, READSTR-len, "multiplecolls: %lud\n", ctlr->stats[MultipleColls]); + len += snprint(p+len, READSTR-len, "singlecollframes: %lud\n", ctlr->stats[SingleCollFrames]); + len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->stats[LateCollisions]); + len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->stats[RxOverruns]); + len += snprint(p+len, READSTR-len, "framesxmittedok: %lud\n", ctlr->stats[FramesXmittedOk]); + len += snprint(p+len, READSTR-len, "framesrcvdok: %lud\n", ctlr->stats[FramesRcvdOk]); + len += snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->stats[FramesDeferred]); + len += snprint(p+len, READSTR-len, "bytesrcvdok: %lud\n", ctlr->stats[BytesRcvdOk]); + len += snprint(p+len, READSTR-len, "bytesxmittedok: %lud\n", ctlr->stats[BytesRcvdOk+1]); + + if(ctlr->upenabled){ + if(ctlr->upqmax > ctlr->upqmaxhw) + ctlr->upqmaxhw = ctlr->upqmax; + len += snprint(p+len, READSTR-len, "up: q %lud i %lud m %d h %d s %lud\n", + ctlr->upqueued, ctlr->upinterrupts, + ctlr->upqmax, ctlr->upqmaxhw, ctlr->upstalls); + ctlr->upqmax = 0; + } + if(ctlr->dnenabled){ + if(ctlr->dnqmax > ctlr->dnqmaxhw) + ctlr->dnqmaxhw = ctlr->dnqmax; + len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d h %d\n", + ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax, ctlr->dnqmaxhw); + ctlr->dnqmax = 0; + } + + snprint(p+len, READSTR-len, "badssd: %lud\n", ctlr->stats[BytesRcvdOk+2]); + + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static void +txrxreset(int port) +{ + COMMAND(port, TxReset, 0); + while(STATUS(port) & commandInProgress) + ; + COMMAND(port, RxReset, 0); + while(STATUS(port) & commandInProgress) + ; +} + +typedef struct Adapter { + int port; + int irq; + int tbdf; +} Adapter; +static Block* adapter; + +static void +tcmadapter(int port, int irq, int tbdf) +{ + Block *bp; + Adapter *ap; + + bp = allocb(sizeof(Adapter)); + ap = (Adapter*)bp->rp; + ap->port = port; + ap->irq = irq; + ap->tbdf = tbdf; + + bp->next = adapter; + adapter = bp; +} + +/* + * Write two 0 bytes to identify the IDport and then reset the + * ID sequence. Then send the ID sequence to the card to get + * the card into command state. + */ +static void +idseq(void) +{ + int i; + uchar al; + static int reset, untag; + + /* + * One time only: + * reset any adapters listening + */ + if(reset == 0){ + outb(IDport, 0); + outb(IDport, 0); + outb(IDport, 0xC0); + delay(20); + reset = 1; + } + + outb(IDport, 0); + outb(IDport, 0); + for(al = 0xFF, i = 0; i < 255; i++){ + outb(IDport, al); + if(al & 0x80){ + al <<= 1; + al ^= 0xCF; + } + else + al <<= 1; + } + + /* + * One time only: + * write ID sequence to get the attention of all adapters; + * untag all adapters. + * If a global reset is done here on all adapters it will confuse + * any ISA cards configured for EISA mode. + */ + if(untag == 0){ + outb(IDport, 0xD0); + untag = 1; + } +} + +static ulong +activate(void) +{ + int i; + ushort x, acr; + + /* + * Do the little configuration dance: + * + * 2. write the ID sequence to get to command state. + */ + idseq(); + + /* + * 3. Read the Manufacturer ID from the EEPROM. + * This is done by writing the IDPort with 0x87 (0x80 + * is the 'read EEPROM' command, 0x07 is the offset of + * the Manufacturer ID field in the EEPROM). + * The data comes back 1 bit at a time. + * A delay seems necessary between reading the bits. + * + * If the ID doesn't match, there are no more adapters. + */ + outb(IDport, 0x87); + delay(20); + for(x = 0, i = 0; i < 16; i++){ + delay(20); + x <<= 1; + x |= inb(IDport) & 0x01; + } + if(x != 0x6D50) + return 0; + + /* + * 3. Read the Address Configuration from the EEPROM. + * The Address Configuration field is at offset 0x08 in the EEPROM). + */ + outb(IDport, 0x88); + for(acr = 0, i = 0; i < 16; i++){ + delay(20); + acr <<= 1; + acr |= inb(IDport) & 0x01; + } + + return (acr & 0x1F)*0x10 + 0x200; +} + +static char* tcmpcmcia[] = { + "3C589", /* 3COM 589[ABCD] */ + "3C562", /* 3COM 562 */ + "589E", /* 3COM Megahertz 589E */ + nil, +}; + +static int +tcm5XXpcmcia(Ether* ether) +{ + int i; + + for(i = 0; tcmpcmcia[i] != nil; i++){ + if(!cistrcmp(ether->type, tcmpcmcia[i])) + return ether->port; + } + + return 0; +} + +static void +setxcvr(int port, int xcvr, int is9) +{ + int x; + + if(is9){ + COMMAND(port, SelectRegisterWindow, Wsetup); + x = ins(port+AddressConfig) & ~xcvrMask9; + x |= (xcvr>>20)<<14; + outs(port+AddressConfig, x); + } + else{ + COMMAND(port, SelectRegisterWindow, Wfifo); + x = inl(port+InternalConfig) & ~xcvrMask; + x |= xcvr; + outl(port+InternalConfig, x); + } + + txrxreset(port); +} + +static void +setfullduplex(int port) +{ + int x; + + COMMAND(port, SelectRegisterWindow, Wfifo); + x = ins(port+MacControl); + outs(port+MacControl, fullDuplexEnable|x); + + txrxreset(port); +} + +static int +miimdi(int port, int n) +{ + int data, i; + + /* + * Read n bits from the MII Management Register. + */ + data = 0; + for(i = n-1; i >= 0; i--){ + if(ins(port) & mgmtData) + data |= (1<= 0; i--){ + if(bits & (1<>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + port += PhysicalMgmt; + + /* + * Preamble; + * ST+OP+PHYAD+REGAD; + * TA + 16 data bits. + */ + miimdo(port, 0xFFFFFFFF, 32); + miimdo(port, 0x1800|(phyad<<5)|regad, 14); + data = miimdi(port, 18); + + port -= PhysicalMgmt; + COMMAND(port, SelectRegisterWindow, w); + + if(data & 0x10000) + return -1; + + return data & 0xFFFF; +} + +static void +scanphy(int port) +{ + int i, x; + + for(i = 0; i < 32; i++){ + if((x = miir(port, i, 2)) == -1 || x == 0) + continue; + x <<= 6; + x |= miir(port, i, 3)>>10; + XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(port, i, 1)); + USED(x); + } +} + +#ifdef notdef +static struct xxx { + int available; + int next; +} xxx[8] = { + { base10TAvailable, 1, }, /* xcvr10BaseT -> xcvrAui */ + { auiAvailable, 3, }, /* xcvrAui -> xcvr10Base2 */ + { 0, -1, }, + { coaxAvailable, -1, }, /* xcvr10Base2 -> nowhere */ + { baseTXAvailable, 5, }, /* xcvr100BaseTX-> xcvr100BaseFX */ + { baseFXAvailable, -1, }, /* xcvr100BaseFX-> nowhere */ + { miiConnector, -1, }, /* xcvrMii -> nowhere */ + { 0, -1, }, +}; +#endif /* notdef */ + +static int +autoselect(int port, int xcvr, int is9) +{ + int media, x; + + USED(xcvr); + + /* + * Pathetic attempt at automatic media selection. + * Really just to get the Fast Etherlink 10BASE-T/100BASE-TX + * cards operational. + * It's a bonus if it works for anything else. + */ + if(is9){ + COMMAND(port, SelectRegisterWindow, Wsetup); + x = ins(port+ConfigControl); + media = 0; + if(x & base10TAvailable9) + media |= base10TAvailable; + if(x & coaxAvailable9) + media |= coaxAvailable; + if(x & auiAvailable9) + media |= auiAvailable; + } + else{ + COMMAND(port, SelectRegisterWindow, Wfifo); + media = ins(port+ResetOptions); + } + XCVRDEBUG("autoselect: media %uX\n", media); + + if(media & miiConnector) + return xcvrMii; + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + XCVRDEBUG("autoselect: media status %uX\n", ins(port+MediaStatus)); + + if(media & baseTXAvailable){ + /* + * Must have InternalConfig register. + */ + setxcvr(port, xcvr100BaseTX, is9); + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable); + outs(port+MediaStatus, linkBeatEnable|x); + delay(10); + + if(ins(port+MediaStatus) & linkBeatDetect) + return xcvr100BaseTX; + outs(port+MediaStatus, x); + } + + if(media & base10TAvailable){ + setxcvr(port, xcvr10BaseT, is9); + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~dcConverterEnabled; + outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x); + delay(100); + + XCVRDEBUG("autoselect: 10BaseT media status %uX\n", ins(port+MediaStatus)); + if(ins(port+MediaStatus) & linkBeatDetect) + return xcvr10BaseT; + outs(port+MediaStatus, x); + } + + /* + * Botch. + */ + return autoSelect; +} + +static int +eepromdata(int port, int offset) +{ + COMMAND(port, SelectRegisterWindow, Wsetup); + while(EEPROMBUSY(port)) + ; + EEPROMCMD(port, EepromReadRegister, offset); + while(EEPROMBUSY(port)) + ; + return EEPROMDATA(port); +} + +int +etherelnk3reset(Ether* ether) +{ + int anar, anlpar, busmaster, did, i, phyaddr, port, rxearly, rxstatus9, x, xcvr; + Block *bp, **bpp; + Adapter *ap; + uchar ea[Eaddrlen]; + Ctlr *ctlr; + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + port = 0; + bpp = &adapter; + for(bp = *bpp; bp; bp = bp->next){ + ap = (Adapter*)bp->rp; + if(ether->port == 0 || ether->port == ap->port){ + port = ap->port; + ether->irq = ap->irq; + ether->tbdf = ap->tbdf; + *bpp = bp->next; + freeb(bp); + break; + } + bpp = &bp->next; + } + if(port == 0 && (port = tcm5XXpcmcia(ether)) == 0) + return -1; + + /* + * Read the DeviceID from the EEPROM, it's at offset 0x03, + * and do something depending on capabilities. + */ + switch(did = eepromdata(port, 0x03)){ + + case 0x9000: + case 0x9001: + case 0x9005: + case 0x9050: + case 0x9051: + case 0x9055: + if(BUSTYPE(ether->tbdf) != BusPCI) + goto buggery; + busmaster = 2; + goto vortex; + + case 0x5900: + case 0x5920: + case 0x5950: + case 0x5951: + case 0x5952: + case 0x5970: + case 0x5971: + case 0x5972: + busmaster = 1; + vortex: + COMMAND(port, SelectRegisterWindow, Wfifo); + xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask); + rxearly = 8188; + rxstatus9 = 0; + break; + + buggery: + default: + busmaster = 0; + COMMAND(port, SelectRegisterWindow, Wsetup); + x = ins(port+AddressConfig); + xcvr = ((x & xcvrMask9)>>14)<<20; + if(x & autoSelect9) + xcvr |= autoSelect; + rxearly = 2044; + rxstatus9 = 1; + break; + } + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to loading the + * station address in Wstation. The EEPROM returns 16-bits at a time. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + for(i = 0; i < Eaddrlen/2; i++){ + x = eepromdata(port, i); + ether->ea[2*i] = x>>8; + ether->ea[2*i+1] = x; + } + } + + COMMAND(port, SelectRegisterWindow, Wstation); + for(i = 0; i < Eaddrlen; i++) + outb(port+i, ether->ea[i]); + + /* + * Enable the transceiver if necessary and determine whether + * busmastering can be used. Due to bugs in the first revision + * of the 3C59[05], don't use busmastering at 10Mbps. + */ + XCVRDEBUG("reset: xcvr %uX\n", xcvr); +/* + * forgive me, but i am weak + */ +if(did == 0x9055){ + xcvr = xcvrMii; + XCVRDEBUG("9055 reset ops 0x%uX\n", + ins(port+ResetOp905B)); +} +else + if(xcvr & autoSelect) + xcvr = autoselect(port, xcvr, rxstatus9); + XCVRDEBUG("autoselect returns: xcvr %uX, did 0x%uX\n", xcvr, did); + switch(xcvr){ + + case xcvrMii: + /* + * Quick hack. + scanphy(port); + */ + phyaddr = 24; +for(i = 0; i < 7; i++) + XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i)); +XCVRDEBUG("\n"); + +{ int phystat, timeo; + for(timeo = 0; timeo < 30; timeo++){ + phystat = miir(port, phyaddr, 0x01); + if(phystat & 0x20) + break; + XCVRDEBUG(" %2.2uX", phystat); + delay(100); + } + XCVRDEBUG(" %2.2uX", miir(port, phyaddr, 0x01)); + XCVRDEBUG("\n"); +} + + anar = miir(port, phyaddr, 0x04); + anlpar = miir(port, phyaddr, 0x05) & 0x03E0; + anar &= anlpar; + miir(port, phyaddr, 0x00); + XCVRDEBUG("mii an: %uX anlp: %uX r0:%uX r1:%uX\n", + anar, anlpar, miir(port, phyaddr, 0x00), + miir(port, phyaddr, 0x01)); + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "fullduplex") == 0) + anar |= 0x0100; + else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0) + anar |= 0x0100; + else if(cistrcmp(ether->opt[i], "force100") == 0) + anar |= 0x0080; + } + XCVRDEBUG("mii anar: %uX\n", anar); + if(anar & 0x0100){ /* 100BASE-TXFD */ + ether->mbps = 100; + setfullduplex(port); + } + else if(anar & 0x0200) /* 100BASE-T4 */ + ; + else if(anar & 0x0080) /* 100BASE-TX */ + ether->mbps = 100; + else if(anar & 0x0040) /* 10BASE-TFD */ + setfullduplex(port); + else /* 10BASE-T */ + ; + break; + + case xcvr100BaseTX: + case xcvr100BaseFX: + COMMAND(port, SelectRegisterWindow, Wfifo); + x = inl(port+InternalConfig) & ~ramPartitionMask; + outl(port+InternalConfig, x|ramPartition1to1); + + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable); + x |= linkBeatEnable; + outs(port+MediaStatus, x); + + if(x & dataRate100) + ether->mbps = 100; + break; + + case xcvr10BaseT: + /* + * Enable Link Beat and Jabber to start the + * transceiver. + */ + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~dcConverterEnabled; + x |= linkBeatEnable|jabberGuardEnable; + outs(port+MediaStatus, x); + + if((did & 0xFF00) == 0x5900) + busmaster = 0; + break; + + case xcvr10Base2: + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable); + outs(port+MediaStatus, x); + + /* + * Start the DC-DC converter. + * Wait > 800 microseconds. + */ + COMMAND(port, EnableDcConverter, 0); + delay(1); + break; + } + + /* + * Wop is the normal operating register set. + * The 3C59[0257] adapters allow access to more than one register window + * at a time, but there are situations where switching still needs to be + * done, so just do it. + * Clear out any lingering Tx status. + */ + COMMAND(port, SelectRegisterWindow, Wop); + while(inb(port+TxStatus)) + outb(port+TxStatus, 0); + + /* + * Allocate a controller structure, clear out the + * adapter statistics, clear the statistics logged into ctlr + * and enable statistics collection. Xcvr is needed in order + * to collect the BadSSD statistics. + */ + ether->ctlr = malloc(sizeof(Ctlr)); + ctlr = ether->ctlr; + + ilock(&ctlr->wlock); + ctlr->xcvr = xcvr; + statistics(ether); + memset(ctlr->stats, 0, sizeof(ctlr->stats)); + + ctlr->busmaster = busmaster; + ctlr->xcvr = xcvr; + ctlr->rxstatus9 = rxstatus9; + ctlr->rxearly = rxearly; + if(rxearly >= 2048) + ctlr->ts = 2; + + COMMAND(port, StatisticsEnable, 0); + + /* + * Allocate any receive buffers. + */ + switch(ctlr->busmaster){ + + case 2: + ctlr->dnenabled = 1; + + /* + * 10MUpldBug. + * Disabling is too severe, can use receive busmastering at + * 100Mbps OK, but how to tell which rate is actually being used - + * the 3c905 always seems to have dataRate100 set? + * Believe the bug doesn't apply if upRxEarlyEnable is set + * and the threshold is set such that uploads won't start + * until the whole packet has been received. + */ + ctlr->upenabled = 1; + x = eepromdata(port, 0x0F); + if(!(x & 0x01)) + outl(port+PktStatus, upRxEarlyEnable); + + if(ctlr->upenabled || ctlr->dnenabled){ + ctlr->nup = Nup; + ctlr->ndn = Ndn; + init905(ctlr); + } + else + ctlr->rbp = rbpalloc(allocb); + outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256)); + break; + + default: + ctlr->rbp = rbpalloc(allocb); + break; + } + + /* + * Set a base TxStartThresh which will be incremented + * if any txUnderrun errors occur and ensure no RxEarly + * interrupts happen. + */ + ctlr->txthreshold = ETHERMAXTU/2; + COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts); + COMMAND(port, SetRxEarlyThresh, rxearly>>ctlr->ts); + + iunlock(&ctlr->wlock); + + /* + * Linkage to the generic ethernet driver. + */ + ether->port = port; + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + + ether->promiscuous = promiscuous; + ether->multicast = multicast; + ether->arg = ether; + + return 0; +} + +void +etherelnk3link(void) +{ + addethercard("elnk3", etherelnk3reset); + addethercard("3C509", etherelnk3reset); +} diff --git a/mpc/etherscc.c b/mpc/etherscc.c index 860e16baae7c6d6a559a8843a9530523a0766357..e4137bec6c37c06c5cfcf8d22494ef2cc5b7ef7b 100644 --- a/mpc/etherscc.c +++ b/mpc/etherscc.c @@ -394,8 +394,6 @@ sccsetup(Ctlr *ctlr, SCC *scc, uchar *ea) Etherparam *p; IMM *io; -print("sccsetup\n"); - io = ioplock(); p = (Etherparam*)KADDR(SCC1P); diff --git a/mpc/fns.h b/mpc/fns.h index ae6fc1c7fd84109ebf99b4c8d72573fd37e1e1c9..62989eff38400f963b1837095e400de4fb8bbab5 100644 --- a/mpc/fns.h +++ b/mpc/fns.h @@ -19,8 +19,8 @@ void delay(int); ulong draminit(ulong*); void dtlbmiss(void); void dumpregs(Ureg*); -//void eieio(void); -#define eieio() +void eieio(void); +//#define eieio() void evenaddr(ulong); void faultpower(Ureg*, ulong addr, int read); void firmware(int); @@ -42,11 +42,17 @@ void gotopc(ulong); void icflush(void*, ulong); void idle(void); #define idlehands() /* nothing to do in the runproc */ -int iprint(char*, ...); +int inb(int); +void insb(int, void*, int); +ushort ins(int); +void inss(int, void*, int); +ulong inl(int); +void insl(int, void*, int); void intr(Ureg*); void intrenable(int, void (*)(Ureg*, void*), void*, int); int intrstats(char*, int); void intrvec(void); +int iprint(char*, ...); void itlbmiss(void); int isaconfig(char*, int, ISAConf*); void kbdinit(void); @@ -58,6 +64,14 @@ void mapinit(RMap*, Map*, int); void mathinit(void); void mmuinit(void); ulong* mmuwalk(ulong*, ulong, int); +void outb(int, int); +void outsb(int, void*, int); +void outs(int, ushort); +void outss(int, void*, int); +void outl(int, ulong); +void outsl(int, void*, int); +int pcmspecial(char*, ISAConf*); +void pcmspecialclose(int); #define procrestore(p) void procsave(Proc*); void procsetup(Proc*); @@ -78,6 +92,7 @@ void trapinit(void); void trapvec(void); void uartinstall(void); void uartwait(void); /* debugging */ +int unsac(uchar *dst, uchar *src, int n, int nsrc); void wbflush(void); #define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) diff --git a/mpc/inb.s b/mpc/inb.s new file mode 100644 index 0000000000000000000000000000000000000000..82a57f0d5bd8256f04b729cddcdc7a37b39a756a --- /dev/null +++ b/mpc/inb.s @@ -0,0 +1,119 @@ +#include "mem.h" + +#define BDNZ BC 16,0, +#define BDNE BC 0,2, + +TEXT inb(SB), $0 + OR $ISAMEM, R3 + MOVBZ (R3), R3 + RETURN + +TEXT insb(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAMEM, R3 + SUB $1, R4 +insb1: + MOVBZ (R3), R7 + MOVBU R7, 1(R4) + BDNZ insb1 + RETURN + +TEXT outb(SB), $0 + MOVW v+4(FP), R4 + OR $ISAMEM, R3 + EIEIO + MOVB R4, (R3) + RETURN + +TEXT outsb(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAMEM, R3 + SUB $1, R4 +outsb1: + EIEIO + MOVBZU 1(R4), R7 + MOVB R7, (R3) + BDNZ outsb1 + RETURN + +TEXT ins(SB), $0 + OR $ISAMEM, R3 + EIEIO + MOVHBR (R3), R3 + RETURN + +TEXT inss(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAMEM, R3 + SUB $2, R4 +inss1: + EIEIO + MOVHZ (R3), R7 + MOVHU R7, 2(R4) + BDNZ inss1 + RETURN + +TEXT outs(SB), $0 + MOVW v+4(FP), R4 + OR $ISAMEM, R3 + EIEIO + MOVHBR R4, (R3) + RETURN + +TEXT outss(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAMEM, R3 + SUB $2, R4 +outss1: + EIEIO + MOVHZU 2(R4), R7 + MOVH R7, (R3) + BDNZ outss1 + RETURN + +TEXT inl(SB), $0 + OR $ISAMEM, R3 + EIEIO + MOVWBR (R3), R3 + RETURN + +TEXT insl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAMEM, R3 + SUB $4, R4 +insl1: + EIEIO + MOVW (R3), R7 + MOVWU R7, 4(R4) + BDNZ insl1 + RETURN + +TEXT outl(SB), $0 + MOVW v+4(FP), R4 + OR $ISAMEM, R3 + EIEIO + MOVWBR R4, (R3) + RETURN + +TEXT outsl(SB), $0 + MOVW v+4(FP), R4 + MOVW n+8(FP), R5 + MOVW R5, CTR + OR $ISAMEM, R3 + SUB $4, R4 +outsl1: + EIEIO + MOVWU 4(R4), R7 + MOVW R7, (R3) + BDNZ outsl1 + RETURN diff --git a/mpc/inbc.c b/mpc/inbc.c new file mode 100644 index 0000000000000000000000000000000000000000..e842a51114f8b08c315277da0e8a2557f83f29f2 --- /dev/null +++ b/mpc/inbc.c @@ -0,0 +1,134 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +int +inb(int port) +{ + return *(uchar*)(port + ISAMEM); +} + +ushort +ins(int port) +{ + int x = *(ushort*)(port + ISAMEM); + return ((x&0xff) << 8) | (x>>8); +} + +ulong +inl(int port) +{ + ulong x = *(ulong*)(port + ISAMEM); +//print("inl %d\n", port); + return (x << 24) | ((x&0xff00)<<8) | ((x>>8)&0xff00) | (x>>24); +} + +void +outb(int port, int val) +{ +//print("outb %d %ux\n", port, val); + *(uchar*)(port + ISAMEM) = val; +} + +void +outs(int port, ushort val) +{ +//print("outs %d %ux\n", port, val); + int x = (val<<8) | (val>>8); + *(ushort*)(port + ISAMEM) = x; +} + +void +outl(int port, ulong val) +{ +//print("outl %d %ulx\n", port, val); + ulong x = (val << 24) | ((val&0xff00)<<8) | ((val>>8)&0xff00) | (val>>24); + *(ulong*)(port + ISAMEM) = x; +} + +void +insb(int port, void *buf, int len) +{ + int i; + uchar *p, *q; + +//print("insb %d %d\n", port, len); + p = (uchar*)(port + ISAMEM); + q = buf; + for(i = 0; i < len; i++){ + *q++ = *p; + } +} + +void +inss(int port, void *buf, int len) +{ + int i; + ushort *p, *q; + +//print("inss %d %d\n", port, len); + p = (ushort*)(port + ISAMEM); + q = buf; + for(i = 0; i < len; i++){ + *q++ = *p; + } +} + +void +insl(int port, void *buf, int len) +{ + int i; + ulong *p, *q; + +//print("insl %d %d\n", port, len); + p = (ulong*)(port + ISAMEM); + q = buf; + for(i = 0; i < len; i++){ + *q++ = *p; + } +} + +void +outsb(int port, void *buf, int len) +{ + int i; + uchar *p, *q; + +//print("outsb %d %d\n", port, len); + p = (uchar*)(port + ISAMEM); + q = buf; + for(i = 0; i < len; i++){ + *p = *q++; + } +} + +void +outss(int port, void *buf, int len) +{ + int i; + ushort *p, *q; + +//print("outss %d %d\n", port, len); + p = (ushort*)(port + ISAMEM); + q = buf; + for(i = 0; i < len; i++){ + *p = *q++; + } +} + +void +outsl(int port, void *buf, int len) +{ + int i; + ulong *p, *q; + +//print("outsl %d %d\n", port, len); + p = (ulong*)(port + ISAMEM); + q = buf; + for(i = 0; i < len; i++){ + *p = *q++; + } +} diff --git a/mpc/io.h b/mpc/io.h index ae6f187816a16028ebba93378563c735619b9484..5c8f961c4b96f68b28ce8ccd14cc9843310cafe1 100644 --- a/mpc/io.h +++ b/mpc/io.h @@ -324,8 +324,7 @@ struct IMM { ulong option; } pcmr[8]; uchar rsv3[0xe0-0xc0]; - ulong pgcra; - ulong pgcrb; + ulong pgcr[2]; ulong pscr; uchar rsv4[0xf0-0xec]; ulong pipr; @@ -554,3 +553,26 @@ struct IMM { uchar siram[512]; ushort lcdmap[256]; }; + + +enum { + BusCBUS = 0, /* Corollary CBUS */ + BusCBUSII, /* Corollary CBUS II */ + BusEISA, /* Extended ISA */ + BusFUTURE, /* IEEE Futurebus */ + BusINTERN, /* Internal bus */ + BusISA, /* Industry Standard Architecture */ + BusMBI, /* Multibus I */ + BusMBII, /* Multibus II */ + BusMCA, /* Micro Channel Architecture */ + BusMPI, /* MPI */ + BusMPSA, /* MPSA */ + BusNUBUS, /* Apple Macintosh NuBus */ + BusPCI, /* Peripheral Component Interconnect */ + BusPCMCIA, /* PC Memory Card International Association */ + BusTC, /* DEC TurboChannel */ + BusVL, /* VESA Local bus */ + BusVME, /* VMEbus */ + BusXPRESS, /* Express System Bus */ +}; + diff --git a/mpc/l.s b/mpc/l.s index bdbdb8e067b09e56a3e475a1a8655b9689f977ee..b8b067656fe8fad949ace3d4b38ce5613347b993 100644 --- a/mpc/l.s +++ b/mpc/l.s @@ -163,16 +163,16 @@ TEXT kernelmmu(SB), $0 * set: * PowerPC mode * Page protection mode - no 1K pages - * CI when MMU is disbaled - this will change + * ~CI when MMU is disbaled * WT when DMMU is disbaled - this will change * disable protected TLB for the momment * ignore user/supervisor state when looking for TLB * set first tlb entry to 28 - first lockable entry */ - MOVW $(MMUCIDEF|(31<<8)), R4 + MOVW $((31<<8)), R4 MOVW R4, SPR(MI_CTR) /* i-mmu control */ ISYNC - MOVW $(MMUCIDEF|MMUWTDEF|(31<<8)), R4 + MOVW $((31<<8)), R4 MOVW R4, SPR(MD_CTR) /* d-mmu control */ ISYNC @@ -197,14 +197,24 @@ ltlb: BDNZ ltlb /* lock kernel entries in tlb - also reset tlb index*/ - MOVW $(MMUCIDEF|MMURSV4), R4 + MOVW $(MMURSV4), R4 MOVW R4, SPR(MI_CTR) /* i-mmu control */ ISYNC - MOVW $(MMUCIDEF|MMUWTDEF|MMURSV4), R4 + MOVW $(MMURSV4), R4 MOVW R4, SPR(MD_CTR) /* d-mmu control */ ISYNC - /* enable MMU */ + /* enable i-cache */ + MOVW $(1<<25), R4 + MOVW R4, SPR(IC_CST) + ISYNC + + /* enable d-cache */ + MOVW $(1<<25), R4 + MOVW R4, SPR(DC_CST) + ISYNC + + /* enable MMU */ MOVW MSR, R4 OR $(MSR_IR|MSR_DR), R4 MOVW R4, MSR @@ -684,8 +694,8 @@ GLOBL isavetbl+0(SB), $4 TEXT tlbtab(SB), $-4 /* epn, rpn, twc */ - TLBE(DRAMMEM|MMUEV, MMUPS8M|MMUWT|MMUV, DRAMMEM|MMUPP|MMUSPS|MMUSH|MMUCI|MMUV) /* DRAM, 8M */ - TLBE((DRAMMEM+8*(1<<20))|MMUEV, MMUPS8M|MMUWT|MMUV, (DRAMMEM+8*(1<<20))|MMUPP|MMUSPS|MMUSH|MMUCI|MMUV) /* DRAM, second 8M */ + TLBE(DRAMMEM|MMUEV, MMUPS8M|MMUWT|MMUV, DRAMMEM|MMUPP|MMUSPS|MMUSH|MMUV) /* DRAM, 8M */ + TLBE((DRAMMEM+8*(1<<20))|MMUEV, MMUPS8M|MMUWT|MMUV, (DRAMMEM+8*(1<<20))|MMUPP|MMUSPS|MMUSH|MMUV) /* DRAM, second 8M */ TLBE(INTMEM|MMUEV, MMUPS8M|MMUWT|MMUV, INTMEM|MMUPP|MMUSPS|MMUSH|MMUCI|MMUV) /* IO space 8M */ TEXT tlbtabe(SB), $-4 RETURN diff --git a/mpc/main.c b/mpc/main.c index 2229c5476f79474d0898fc137ee7633c96568839..54e08245695462372e32f6511ac7b022095c2735 100644 --- a/mpc/main.c +++ b/mpc/main.c @@ -39,6 +39,11 @@ if(0) { delay(1000); } } +// turn on pcmcia +*(uchar*)(NVRAMMEM+0x100001) |= 0x60; +//print("sr1=%ux sr2=%ux\n", *(uchar*)(NVRAMMEM+0x100000), *(uchar*)(NVRAMMEM+0x100001)); + +//print("sccr=%ulx\n", m->iomem->sccr); pageinit(); procinit0(); initseg(); @@ -46,7 +51,6 @@ if(0) { links(); chandevreset(); swapinit(); -print("usrinit\n"); userinit(); predawn = 0; schedinit(); @@ -70,6 +74,8 @@ machinit(void) m->speed = osc*(mf+1); m->cpuhz = m->speed*MHz; /* general system clock (cycles) */ m->clockgen = osc*MHz; /* clock generator frequency (cycles) */ + + *(ushort*)&(io->memc[4].base) = 0x8060; } void @@ -202,6 +208,8 @@ exit(int ispanic) else delay(1000); + for(;;) + ; // arch->reset(); } @@ -248,7 +256,7 @@ confinit(void) ulong pa; conf.nmach = 1; /* processors */ - conf.nproc = 200; /* processes */ + conf.nproc = 30; /* processes */ // hard wire for now pa = 0xffd00000; // leave 1 Meg for kernel @@ -257,8 +265,11 @@ confinit(void) conf.npage0 = nbytes/BY2PG; conf.base0 = pa; - conf.npage1 = 0; - conf.base1 = 0; + pa = 0xfff04000; + nbytes = 1024*1024 - 0x4000; + + conf.npage1 = nbytes/BY2PG; + conf.base1 = pa; conf.npage = conf.npage0 + conf.npage1; @@ -266,9 +277,9 @@ confinit(void) conf.ialloc = ((conf.npage-conf.upages)/2)*BY2PG; /* set up other configuration parameters */ - conf.nswap = conf.npage*3; - conf.nswppo = 4096; - conf.nimage = 200; + conf.nswap = 0; // conf.npage*3; + conf.nswppo = 0; // 4096; + conf.nimage = 20; conf.copymode = 0; /* copy on write */ } @@ -296,6 +307,7 @@ getcfields(char* lp, char** fields, int n, char* sep) static char BOOTARGS[] = "ether0=type=SCC port=1 ea=08003e27df94\r\n" + "ether1=type=589E port=0x300\r\n" "vgasize=640x480x8\r\n" "kernelpercent=40\r\n" "console=0 lcd\r\nbaud=9600\r\n"; diff --git a/mpc/mem.h b/mpc/mem.h index bd14fbcce092e7671cf0304d8efc74d298c69342..0fe6ab979721cb9b926da2d473b7bd0c8a6cdea2 100644 --- a/mpc/mem.h +++ b/mpc/mem.h @@ -123,7 +123,7 @@ /* * portable MMU bits for fault.c - though still machine specific */ -#define PTEVALID (MMUPP|MMUSH|MMUCI|MMUV) +#define PTEVALID (MMUPP|MMUSH|MMUV) #define PTEWRITE (2<<10) #define PTERONLY (3<<10) #define PTEUNCACHED (1<<4) @@ -177,9 +177,10 @@ * atlas board registers */ #define INTMEM 0x80000000 +#define ISAMEM 0x80100000 #define FLASH0MEM 0x80200000 #define FLASH1MEM 0x80400000 -#define SDRAMMEM 0x03000000 +#define NVRAMMEM 0x80600000 #define DRAMMEM 0xff000000 /* to 0xffffffff: 16 Meg */ #define SIRAM (INTMEM+0xC00) diff --git a/mpc/mmu.c b/mpc/mmu.c index 24576e7aff6cfc5d226f3c1f931972b1388b55a0..c35eddf77aca7b745c1a66fe2d576e1620516f95 100644 --- a/mpc/mmu.c +++ b/mpc/mmu.c @@ -34,11 +34,32 @@ mmurelease(Proc* proc) void -putmmu(ulong va, ulong pa, Page*) +putmmu(ulong va, ulong pa, Page *pg) { int x, r; + char *ctl; + +//if((va&0x8000000) == 0) //print("putmmu va=%ux pa=%ux\n", va, pa); x = splhi(); r = _putmmu(va, pa); + + ctl = &pg->cachectl[m->machno]; + switch(*ctl) { + default: + panic("putmmu: %d\n", *ctl); + break; + case PG_NOFLUSH: + break; + case PG_TXTFLUSH: + icflush((void*)pg->va, BY2PG); + *ctl = PG_NOFLUSH; + break; + case PG_NEWCOL: + dcflush((void*)pg->va, BY2PG); + *ctl = PG_NOFLUSH; + break; + } + splx(x); } diff --git a/mpc/nocache.c b/mpc/nocache.c new file mode 100644 index 0000000000000000000000000000000000000000..5b6f7c84d71608f7a73c1109e14e6101fbcd1f09 --- /dev/null +++ b/mpc/nocache.c @@ -0,0 +1,39 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +// No file caching + +void +cinit(void) +{ +} + +void +copen(Chan *c) +{ +print("copen called!\n"); + USED(c); +} + +int +cread(Chan *c, uchar *buf, int len, vlong off) +{ + USED(c, buf, len, off); + return 0; +} + +void +cupdate(Chan *c, uchar *buf, int len, vlong off) +{ + USED(c, buf, len, off); +} + +void +cwrite(Chan* c, uchar *buf, int len, vlong off) +{ + USED(c, buf, len, off); +} diff --git a/mpc/trap.c b/mpc/trap.c index 7d2838922283b0a50d961a67b17a68e4337e4967..39a0644ac22d3ddca11c2bae6acba1a9b5e0486d 100644 --- a/mpc/trap.c +++ b/mpc/trap.c @@ -212,7 +212,7 @@ faultpower(Ureg *ureg, ulong addr, int read) char buf[ERRLEN]; user = (ureg->srr1 & MSR_PR) != 0; -//print("fault: pc = %ux, addr = %ux read = %d user = %d stack=%ux\n", ureg->pc, addr, read, user, &ureg); +//print("fault: pid=%d pc = %ux, addr = %ux read = %d user = %d stack=%ux\n", up->pid, ureg->pc, addr, read, user, &ureg); insyscall = up->insyscall; up->insyscall = 1; spllo(); @@ -222,6 +222,7 @@ faultpower(Ureg *ureg, ulong addr, int read) dumpregs(ureg); panic("fault: 0x%lux\n", addr); } +print("sys: trap: fault %s addr=0x%lux\n", read? "read" : "write", addr); sprint(buf, "sys: trap: fault %s addr=0x%lux", read? "read" : "write", addr); postnote(up, 1, buf, NDebug); @@ -674,6 +675,8 @@ notify(Ureg* ur) ulong s, sp; Note *n; +print("***notify\n"); + if(up->procctl) procctl(up); if(up->nnote == 0) @@ -749,6 +752,7 @@ noted(Ureg* ureg, ulong arg0) Ureg *nureg; ulong oureg, sp; +print("***noted\n"); qlock(&up->debug); if(arg0!=NRSTR && !up->notified) { qunlock(&up->debug); diff --git a/mpc/unsac.c b/mpc/unsac.c new file mode 100644 index 0000000000000000000000000000000000000000..91aea216258375c3471035a9461d02f869abc835 --- /dev/null +++ b/mpc/unsac.c @@ -0,0 +1,401 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +typedef struct Huff Huff; + +typedef struct Decode Decode; + +enum +{ + ZBase = 2, /* base of code to encode 0 runs */ + LitBase = ZBase-1, /* base of literal values */ + MaxLit = 256, + + MaxLeaf = MaxLit+LitBase, + MaxHuffBits = 15, /* max bits in a huffman code */ + MaxSelHuffBits = 15, /* max bits for a table selector huffman code */ + + Nhuffblock = 16*1024, /* symbols encoded before output */ + Nhuffslop = 64, /* slop for stuffing zeros, etc. */ +}; + +struct Huff +{ + int minbits; + int maxbits; + int flatbits; + ulong flat[1<<8]; + ulong maxcode[MaxHuffBits+1]; + ulong last[MaxHuffBits+1]; + ulong decode[MaxLeaf]; +}; + +struct Decode{ + Huff tab; + int ndec; + int nbits; + ulong bits; + int nzero; + int base; + ulong maxblocksym; + + jmp_buf errjmp; + + uchar *src; /* input buffer */ + uchar *smax; /* limit */ +}; + +static void fatal(Decode *dec, char*, ...); + +static int hdec(Decode*); +static void hflush(Decode*); +static void hbflush(Decode*); +static ulong bitget(Decode*, int); +static int mtf(uchar*, int); + +int +unsac(uchar *dst, uchar *src, int n, int nsrc) +{ + Decode dec; + uchar *buf, front[256]; + ulong *suflink, sums[256]; + ulong sum; + int i, m, I, j, c; + + buf = malloc(n+2); + suflink = malloc((n+2) * sizeof *suflink); + + if(waserror()) { + free(buf); + free(suflink); + nexterror(); + } + + dec.src = src; + dec.smax = src + nsrc; + + dec.nbits = 0; + dec.bits = 0; + dec.nzero = 0; + for(i = 0; i < 256; i++) + front[i] = i; + + n++; + I = bitget(&dec, 16); + if(I >= n) + fatal(&dec, "corrupted input file: n=%d I=%d", n, I); + + /* + * decode the character usage map + */ + for(i = 0; i < 256; i++) + sums[i] = 0; + c = bitget(&dec, 1); + for(i = 0; i < 256; ){ + m = bitget(&dec, 8) + 1; + while(m--){ + if(i >= 256) + fatal(&dec, "bad format encoding char map %d", m); + front[i++] = c; + } + c = c ^ 1; + } + + /* + * initialize mtf state + */ + c = 0; + for(i = 0; i < 256; i++) + if(front[i]) + front[c++] = i; + dec.maxblocksym = c + LitBase; + + /* + * huffman decoding, move to front decoding, + * along with character counting + */ + hbflush(&dec); + for(i = 0; i < n; i++){ + if(i == I) + continue; + m = hdec(&dec); + + /* + * move to front + */ + c = front[m]; + for(; m > 0; m--) + front[m] = front[m-1]; + front[0] = c; + + buf[i] = c; + sums[c]++; + } + + sum = 1; + for(i = 0; i < 256; i++){ + c = sums[i]; + sums[i] = sum; + sum += c; + } + + /* + * calculate the row step for column step array + * by calculating it for backwards moves and inverting it + */ + suflink[0] = I; + for(j = 0; j < I; j++) + suflink[sums[buf[j]]++] = j; + for(j++; j < n; j++) + suflink[sums[buf[j]]++] = j; + + /* + * to recover the suffix array, aka suffix array for input + * j = 0; + * for(i = I; i != 0; i = suflink[i]) + * sarray[i] = j++; + * sarray[i] = j++; + * note that suflink[i] = sarrayinv[sarray[i] + 1] + */ + + /* + * produce the decoded data forwards + */ + n--; + i = I; + for(j = 0; j < n; j++){ + i = suflink[i]; + dst[j] = buf[i]; + } + + poperror(); + free(buf); + free(suflink); + return n; +} + +static ulong +bitget(Decode *dec, int nb) +{ + int c; + + while(dec->nbits < nb){ + if(dec->src >= dec->smax) + fatal(dec, "premature eof 1"); + c = *dec->src++; + dec->bits <<= 8; + dec->bits |= c; + dec->nbits += 8; + } + dec->nbits -= nb; + return (dec->bits >> dec->nbits) & ((1 << nb) - 1); +} + +static void +fillbits(Decode *dec) +{ + int c; + + while(dec->nbits < 24){ + if(dec->src >= dec->smax) + fatal(dec, "premature eof 2: nbits %d", dec->nbits); + c = *dec->src++; + dec->bits <<= 8; + dec->bits |= c; + dec->nbits += 8; + } +} + +/* + * decode one symbol + */ +static int +hdecsym(Decode *dec, Huff *h) +{ + long c; + int b; + + dec->bits &= (1 << dec->nbits) - 1; + for(b = h->flatbits; (c = dec->bits >> (dec->nbits - b)) > h->maxcode[b]; b++) + ; + if(b > h->maxbits) + fatal(dec, "too many bits consumed: b=%d minbits=%d maxbits=%d", b, h->minbits, h->maxbits); + dec->nbits -= b; + c = h->decode[h->last[b] - c]; + + return c; +} + +static int +hdec(Decode *dec) +{ + ulong c; + + dec->ndec++; + if(dec->nzero){ + dec->nzero--; + return 0; + } + + if(dec->nbits < dec->tab.maxbits) + fillbits(dec); + dec->bits &= (1 << dec->nbits) - 1; + c = dec->tab.flat[dec->bits >> (dec->nbits - dec->tab.flatbits)]; + if(c == ~0) + c = hdecsym(dec, &dec->tab); + else{ + dec->nbits -= c & 0xff; + c >>= 8; + } + + /* + * reverse funny run-length coding + */ + if(c < ZBase){ + dec->nzero = dec->base << c; + dec->base <<= 1; + dec->nzero--; + return 0; + } + + dec->base = 1; + c -= LitBase; + return c; +} + +static void +hbflush(Decode *dec) +{ + dec->base = 1; + dec->ndec = 0; + hflush(dec); +} + +static void +hufftab(Decode *dec, Huff *h, ulong *hb, ulong *bitcount, int maxleaf, int maxbits, int flatbits) +{ + ulong c, code, nc[MaxHuffBits+1]; + int i, b, ec; + + code = 0; + c = 0; + h->minbits = maxbits; + for(b = 1; b <= maxbits; b++){ + h->last[b] = c; + if(c == 0) + h->minbits = b; + c += bitcount[b]; + nc[b] = code << 1; + code = (code << 1) + bitcount[b]; + if(code > (1 << b)) + fatal(dec, "corrupted huffman table"); + h->maxcode[b] = code - 1; + h->last[b] += code - 1; + } + if(code != (1 << maxbits)) + fatal(dec, "huffman table not full %d %d", code, 1<maxbits = b; + if(flatbits > b) + flatbits = b; + h->flatbits = flatbits; + + b = 1 << flatbits; + for(i = 0; i < b; i++) + h->flat[i] = ~0; + + for(i = 0; i < maxleaf; i++){ + b = hb[i]; + if(b == 0) + continue; + c = nc[b]++; + if(b <= flatbits){ +if(c > (1<<(b+1)))fatal(dec, "xx1"); + code = (i << 8) | b; + ec = (c + 1) << (flatbits - b); + if(ec > (1<flat[c] = code; + }else{ + c = h->last[b] - c; + if(c >= maxleaf) + fatal(dec, "corrupted huffman table: c=%d maxleaf=%d i=%d b=%d maxbits=%d last=%d, nc=%d", + c, maxleaf, i, b, maxbits, h->last[b], nc[b]); + h->decode[c] = i; + } + } +} + +static void +hflush(Decode *dec) +{ + Huff codetab; + ulong bitcount[MaxHuffBits+1], hb[MaxLeaf]; + uchar tmtf[MaxHuffBits+1]; + int i, b, m, maxbits; + + /* + * read the tables for the tables + */ + for(i = 0; i <= MaxHuffBits; i++) + bitcount[i] = 0; + maxbits = 0; + for(i = 0; i <= MaxHuffBits; i++){ + b = bitget(dec, 4); + hb[i] = b; + bitcount[b]++; + if(b > maxbits) + maxbits = b; + } + hufftab(dec, &codetab, hb, bitcount, MaxHuffBits+1, maxbits, 8); + for(i = 0; i <= MaxHuffBits; i++) + tmtf[i] = i; + for(i = 0; i <= MaxHuffBits; i++) + bitcount[i] = 0; + maxbits = 0; + for(i = 0; i < dec->maxblocksym; i++){ + if(dec->nbits <= codetab.maxbits) + fillbits(dec); + dec->bits &= (1 << dec->nbits) - 1; + m = codetab.flat[dec->bits >> (dec->nbits - codetab.flatbits)]; + if(m == ~0) + m = hdecsym(dec, &codetab); + else{ + dec->nbits -= m & 0xff; + m >>= 8; + } + b = tmtf[m]; + for(; m > 0; m--) + tmtf[m] = tmtf[m-1]; + tmtf[0] = b; + + if(b > MaxHuffBits) + fatal(dec, "bit length %d too large, max %d i %d maxval %d", b, MaxHuffBits, i, dec->maxblocksym); + hb[i] = b; + bitcount[b]++; + if(b > maxbits) + maxbits = b; + } + for(; i < MaxLeaf; i++) + hb[i] = 0; + + hufftab(dec, &dec->tab, hb, bitcount, MaxLeaf, maxbits, 8); +} + +static void +fatal(Decode *dec, char *fmt, ...) +{ + char buf[128]; + va_list arg; + + va_start(arg, fmt); + doprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + + print("devsac: %s\n", buf); + error(buf); +}