ref: 53fe82ee861967f0c02dbec7901660470da79098
dir: /sys/src/9/sgi/etherseeq.c/
#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/netif.h" #include "../port/etherif.h" typedef struct Hio Hio; typedef struct Desc Desc; typedef struct Ring Ring; typedef struct Ctlr Ctlr; /* * SEEQ 8003 interfaced to HPC3 (very different from IP20) */ struct Hio { ulong unused0[20480]; ulong crbp; /* current receive buf desc ptr */ ulong nrbdp; /* next receive buf desc ptr */ ulong unused1[1022]; ulong rbc; /* receive byte count */ ulong rstat; /* receiver status */ ulong rgio; /* receive gio fifo ptr */ ulong rdev; /* receive device fifo ptr */ ulong unused2; ulong ctl; /* interrupt, channel reset, buf oflow */ ulong dmacfg; /* dma configuration */ ulong piocfg; /* pio configuration */ ulong unused3[1016]; ulong cxbdp; /* current xmit buf desc ptr */ ulong nxbdp; /* next xmit buffer desc. pointer */ ulong unused4[1022]; ulong xbc; /* xmit byte count */ ulong xstat; ulong xgio; /* xmit gio fifo ptr */ ulong xdev; /* xmit device fifo ptr */ ulong unused5[1020]; ulong crbdp; /* current receive descriptor ptr */ ulong unused6[2047]; ulong cpfxbdp; /* current/previous packet 1st xmit */ ulong ppfxbdp; /* desc ptr */ ulong unused7[59390]; ulong eaddr[6]; /* seeq station address wo */ ulong csr; /* seeq receiver cmd/status reg */ ulong csx; /* seeq transmitter cmd/status reg */ }; enum { /* ctl */ Cover= 0x08, /* receive buffer overflow */ Cnormal=0x00, /* 1=normal, 0=loopback */ Cint= 0x02, /* interrupt (write 1 to clear) */ Creset= 0x01, /* ethernet channel reset */ /* xstat */ Xdma= 0x200, /* dma active */ Xold= 0x080, /* register has been read */ Xok= 0x008, /* transmission was successful */ Xmaxtry=0x004, /* transmission failed after 16 attempts */ Xcoll= 0x002, /* transmission collided */ Xunder= 0x001, /* transmitter underflowed */ /* csx */ Xreg0= 0x00, /* access reg bank 0 incl station addr */ XIok= 0x08, XImaxtry=0x04, XIcoll= 0x02, XIunder=0x01, /* rstat */ Rlshort=0x800, /* [small len in received frame] */ Rdma= 0x200, /* dma active */ Rold= 0x80, /* register has been read */ Rok= 0x20, /* received good frame */ Rend= 0x10, /* received end of frame */ Rshort= 0x08, /* received short frame */ Rdrbl= 0x04, /* dribble error */ Rcrc= 0x02, /* CRC error */ Rover= 0x01, /* overflow error */ /* csr */ Rsmb= 0xc0, /* receive station/broadcast/multicast frames */ Rsb= 0x80, /* receive station/broadcast frames */ Rprom= 0x40, /* receive all frames */ RIok= 0x20, /* interrupt on good frame */ RIend= 0x10, /* interrupt on end of frame */ RIshort=0x08, /* interrupt on short frame */ RIdrbl= 0x04, /* interrupt on dribble error */ RIcrc= 0x02, /* interrupt on CRC error */ RIover= 0x01, /* interrupt on overflow error */ HPC_MODNORM= 0x0, /* mode: 0=normal, 1=loopback */ HPC_FIX_INTR= 0x8000, /* start timeout counter after */ HPC_FIX_EOP= 0x4000, /* rcv_eop_intr/eop_in_chip is set */ HPC_FIX_RXDC= 0x2000, /* clear eop status upon rxdc */ }; struct Desc { ulong addr; /* addr */ ulong count; /* eox / eop / busy / xie / count:13 */ ulong next; uchar* base; }; struct Ring { Rendez; int size; int free; uchar* base; Desc* head; Desc* tail; }; enum { Eor= 1<<31, /* end of ring */ Eop= 1<<30, Ioc= 1<<29, /* interrupt on completion */ Busy= 1<<24, Empty= 1<<14, /* no data here */ Done= 1<<15, /* transmit done */ }; enum { Rbsize = ETHERMAXTU+3, }; struct Ctlr { int attach; char* txerr; ulong txwdog; Hio* io; Ring rx; Ring tx; }; static void startup(Ether*); static void shutdown(Ether*); static void txintr(Ctlr *ctlr) { Hio *io; ulong s; Desc *p; io = ctlr->io; s = io->xstat; if((s & Xdma) != 0) return; if((s & Xmaxtry) != 0) ctlr->txerr = "transmission failed"; if((s & Xunder) != 0) ctlr->txerr = "transmitter underflowed"; for(p = IO(Desc, ctlr->tx.head->next); (p->count & Busy) != 0; p = IO(Desc, p->next)){ if((p->count & Done) == 0){ io->nxbdp = PADDR(p); io->xstat = Xdma; ctlr->txwdog = MACHP(0)->ticks; break; } p->count = Eor|Eop; ctlr->tx.head = p; ctlr->tx.free++; ctlr->txwdog = 0; } wakeup(&ctlr->tx); } static void interrupt(Ureg *, void *arg) { Ether *edev; Ctlr *ctlr; Hio *io; uint s; edev = arg; ctlr = edev->ctlr; io = ctlr->io; s = io->ctl; if(s & Cover){ io->ctl = Cnormal | Cover; edev->overflows++; } if(s & Cint) { io->ctl = Cnormal | Cint; txintr(ctlr); wakeup(&ctlr->rx); } } static int notempty(void *arg) { Ctlr *ctlr = arg; return (IO(Desc, ctlr->rx.head->next)->count & Empty) == 0; } static char* checkerr(Ctlr *ctlr) { ulong t; if(ctlr->txerr != nil) return ctlr->txerr; t = ctlr->txwdog; if(t != 0 && TK2MS(MACHP(0)->ticks - t) > 1000) return "transmitter dma timeout"; if((ctlr->io->rstat & Rdma) == 0) return "recevier dma stopped"; return nil; } static void rxproc(void *arg) { Ether *edev = arg; Ctlr *ctlr; char *err; Block *b; Desc *p; int n; while(waserror()) ; ctlr = edev->ctlr; for(p = IO(Desc, ctlr->rx.head->next);; p = IO(Desc, p->next)){ while((p->count & Empty) != 0){ err = checkerr(ctlr); if(err != nil){ print("%s: %s; reseting\n", up->text, err); splhi(); shutdown(edev); startup(edev); spllo(); } tsleep(&ctlr->rx, notempty, ctlr, 500); } n = Rbsize - (p->count & 0x3fff)-3; if(n >= ETHERMINTU && (p->base[n+2] & Rok) != 0){ b = allocb(n); b->wp += n; memmove(b->rp, p->base+2, n); etheriq(edev, b); } p->addr = PADDR(p->base); p->count = Ioc|Empty|Rbsize; ctlr->rx.head = p; } } static int notbusy(void *arg) { Ctlr *ctlr = arg; return ctlr->tx.free > 0; } static void txproc(void *arg) { Ether *edev = arg; Ctlr *ctlr; Block *b; Desc *p; int n; while(waserror()) ; ctlr = edev->ctlr; for(p = IO(Desc, ctlr->tx.tail->next); (b = qbread(edev->oq, 1000000)) != nil; p = IO(Desc, p->next)){ while(ctlr->tx.free == 0) sleep(&ctlr->tx, notbusy, ctlr); n = BLEN(b); if(n > ETHERMAXTU) n = ETHERMAXTU; memmove(p->base, b->rp, n); p->addr = PADDR(p->base); p->count = Ioc|Eor|Eop|Busy|n; ctlr->tx.tail->count &= ~(Ioc|Eor); ctlr->tx.tail = p; splhi(); ctlr->tx.free--; txintr(ctlr); spllo(); freeb(b); } } static void allocring(Ring *r, int n) { uchar *b; Desc *p; int m; r->size = n; r->free = n; m = n*BY2PG/2; b = xspanalloc(m, BY2PG, 0); dcflush(b, m); b = IO(uchar, b); memset(b, 0, m); r->base = b; m = n*sizeof(Desc); p = xspanalloc(m, BY2PG, 0); dcflush(p, m); p = IO(Desc, p); memset(p, 0, m); r->head = r->tail = p; for(m=0; m<n; m++, p++, b += (BY2PG/2)){ p->base = b; p->next = PADDR(p+1); } p[-1].next = PADDR(r->head); } static void startup(Ether *edev) { Ctlr *ctlr; Desc *p; Hio *io; int i; ctlr = edev->ctlr; io = ctlr->io; io->dmacfg |= HPC_FIX_INTR | HPC_FIX_EOP | HPC_FIX_RXDC; p = ctlr->rx.head; do { p->addr = PADDR(p->base); p->count = Ioc|Empty|Rbsize; p = IO(Desc, p->next); } while(p != ctlr->rx.head); io->crbdp = PADDR(p); io->nrbdp = p->next; ctlr->rx.tail = p; ctlr->rx.free = ctlr->rx.size; p = ctlr->tx.tail; do { p->addr = PADDR(p->base); p->count = Eor|Eop; p = IO(Desc, p->next); } while(p != ctlr->tx.tail); io->cxbdp = PADDR(p); io->nxbdp = p->next; ctlr->tx.head = p; ctlr->tx.free = ctlr->tx.size; for(i=0; i<6; i++) io->eaddr[i] = edev->ea[i]; io->csx = XIok | XImaxtry | XIcoll | XIunder; io->csr = Rprom | RIok|RIend|RIshort|RIdrbl|RIcrc; io->rstat = Rdma; wakeup(&ctlr->rx); wakeup(&ctlr->tx); } static void shutdown(Ether *edev) { Ctlr *ctlr = edev->ctlr; Hio *io = ctlr->io; ctlr->txerr = nil; ctlr->txwdog = 0; io->csx = Xreg0; io->rstat = 0; io->xstat = 0; io->ctl = Cnormal | Creset | Cint; delay(10); io->ctl = Cnormal; io->csx = 0; io->csr = 0; } /* * do nothing for promiscuous() and multicast() as we * are always in promisc mode. */ static void promiscuous(void*, int) { } static void multicast(void*, uchar*, int) { } static void attach(Ether *edev) { Ctlr *ctlr; ctlr = edev->ctlr; if(ctlr->attach) return; ctlr->attach = 1; splhi(); startup(edev); spllo(); kproc("#l0rx", rxproc, edev); kproc("#l0tx", txproc, edev); } static int pnp(Ether *edev) { static Ctlr ctlr0; Ctlr *ctlr; char *s; /* only one controller */ if(edev->ctlrno != 0) return -1; /* get mac address from nvram */ if((s = getconf("eaddr")) != nil) parseether(edev->ea, s); ctlr = &ctlr0; edev->ctlr = ctlr; edev->port = HPC3_ETHER; edev->irq = IRQENET; edev->ctlr = ctlr; edev->promiscuous = promiscuous; edev->multicast = multicast; edev->attach = attach; edev->shutdown = shutdown; edev->arg = edev; edev->mbps = 10; edev->link = 1; ctlr->io = IO(Hio, edev->port); allocring(&ctlr->rx, 256); allocring(&ctlr->tx, 64); shutdown(edev); intrenable(hpc3irqlevel(edev->irq), interrupt, edev); return 0; } void etherseeqlink(void) { addethercard("seeq", pnp); }