477 lines
8.8 KiB
C
477 lines
8.8 KiB
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 int reset(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();
|
|
reset(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 int
|
|
reset(Ether *edev)
|
|
{
|
|
Ctlr *ctlr;
|
|
Desc *p;
|
|
Hio *io;
|
|
int i;
|
|
|
|
ctlr = edev->ctlr;
|
|
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;
|
|
|
|
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);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int
|
|
init(Ether *edev)
|
|
{
|
|
Ctlr *ctlr;
|
|
|
|
ctlr = edev->ctlr;
|
|
ctlr->io = IO(Hio, edev->port);
|
|
allocring(&ctlr->rx, 256);
|
|
allocring(&ctlr->tx, 64);
|
|
|
|
return reset(edev);
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
kproc("#l0rx", rxproc, edev);
|
|
kproc("#l0tx", txproc, edev);
|
|
}
|
|
|
|
static int
|
|
pnp(Ether *edev)
|
|
{
|
|
static Ctlr ct;
|
|
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);
|
|
|
|
edev->ctlr = &ct;
|
|
edev->port = HPC3_ETHER;
|
|
edev->irq = IRQENET;
|
|
edev->ctlr = &ct;
|
|
edev->promiscuous = promiscuous;
|
|
edev->multicast = multicast;
|
|
edev->attach = attach;
|
|
edev->arg = edev;
|
|
edev->mbps = 10;
|
|
edev->link = 1;
|
|
|
|
if(init(edev) < 0){
|
|
edev->ctlr = nil;
|
|
return -1;
|
|
}
|
|
|
|
intrenable(hpc3irqlevel(edev->irq), interrupt, edev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
etherseeqlink(void)
|
|
{
|
|
addethercard("seeq", pnp);
|
|
}
|