|
|
Plan 9 NeXT
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
#include "devtab.h"
#include "ether.h"
/*
* Half-arsed attempt at a general top-level
* ethernet driver. Needs work:
* handle multiple controllers
* much tidying
* set ethernet address
* need a ctl file passed down to card drivers
* so we can set options.
*/
struct Ctlr *softctlr;
Chan*
etherclone(Chan *c, Chan *nc)
{
return devclone(c, nc);
}
int
etherwalk(Chan *c, char *name)
{
return netwalk(c, name, &softctlr->net);
}
void
etherstat(Chan *c, char *dp)
{
netstat(c, dp, &softctlr->net);
}
Chan*
etheropen(Chan *c, int omode)
{
return netopen(c, omode, &softctlr->net);
}
void
ethercreate(Chan *c, char *name, int omode, ulong perm)
{
USED(c, name, omode, perm);
error(Eperm);
}
void
etherclose(Chan *c)
{
if(c->stream)
streamclose(c);
}
long
etherread(Chan *c, void *a, long n, ulong offset)
{
return netread(c, a, n, offset, &softctlr->net);
}
long
etherwrite(Chan *c, char *a, long n, ulong offset)
{
USED(offset);
return streamwrite(c, a, n, 0);
}
void
etherremove(Chan *c)
{
USED(c);
error(Eperm);
}
void
etherwstat(Chan *c, char *dp)
{
netwstat(c, dp, &softctlr->net);
}
static int
isobuf(void *arg)
{
Ctlr *ctlr = arg;
return ctlr->tb[ctlr->th].owner == Host;
}
static void
etheroput(Queue *q, Block *bp)
{
Ctlr *ctlr;
Type *type;
Etherpkt *pkt;
RingBuf *ring;
int len, n, s;
Block *nbp;
uchar ea[6];
char *err;
type = q->ptr;
ctlr = type->ctlr;
if(bp->type == M_CTL){
err = 0;
qlock(ctlr);
if(streamparse("connect", bp)){
if(type->type == -1)
ctlr->all--;
type->type = strtol((char*)bp->rptr, 0, 0);
if(type->type == -1)
ctlr->all++;
}
else if(streamparse("promiscuous", bp)) {
if(type->prom)
goto ctlout;
if(type->filter){
err = "address already set";
goto ctlout;
}
type->prom = 1;
ctlr->prom++;
if(ctlr->prom+ctlr->filter == 1)
(*ctlr->card.mode)(ctlr, 1);
}
else if(streamparse("address", bp)) {
if(type->prom){
err = "already promiscuous";
goto ctlout;
}
if(parseether(ea, bp->rptr) < 0){
err = "bad ether address";
goto ctlout;
}
memmove(type->ea, ea, sizeof(ea));
if(!type->filter){
type->filter = 1;
ctlr->filter++;
if(ctlr->filter == 1)
(*ctlr->card.mode)(ctlr, 3);
}
}
ctlout:
qunlock(ctlr);
freeb(bp);
if(err)
error(err);
return;
}
/*
* Give packet a local address, return upstream if destined for
* this machine.
*/
if(BLEN(bp) < ETHERHDRSIZE && (bp = pullup(bp, ETHERHDRSIZE)) == 0)
return;
pkt = (Etherpkt*)bp->rptr;
memmove(pkt->s, type->ea, sizeof(type->ea));
if(memcmp(ctlr->ea, pkt->d, sizeof(ctlr->ea)) == 0){
len = blen(bp);
if(bp = expandb(bp, len >= ETHERMINTU ? len: ETHERMINTU)){
putq(&ctlr->lbq, bp);
wakeup(&ctlr->rr);
}
return;
}
if(memcmp(ctlr->ba, pkt->d, sizeof(ctlr->ba)) == 0 || ctlr->prom || ctlr->all){
len = blen(bp);
nbp = copyb(bp, len);
if(nbp = expandb(nbp, len >= ETHERMINTU ? len: ETHERMINTU)){
nbp->wptr = nbp->rptr+len;
putq(&ctlr->lbq, nbp);
wakeup(&ctlr->rr);
}
}
/*
* Only one transmitter at a time.
*/
qlock(&ctlr->tlock);
if(waserror()){
qunlock(&ctlr->tlock);
freeb(bp);
nexterror();
}
/*
* Wait till we get an output buffer.
* should try to restart.
*/
if(isobuf(ctlr) == 0){
tsleep(&ctlr->tr, isobuf, ctlr, 3*1000);
if(isobuf(ctlr) == 0){
qunlock(&ctlr->tlock);
freeb(bp);
poperror();
return;
}
}
ring = &ctlr->tb[ctlr->th];
/*
* Copy message into buffer.
*/
len = 0;
for(nbp = bp; nbp; nbp = nbp->next){
if(sizeof(Etherpkt) - len >= (n = BLEN(nbp))){
memmove(ring->pkt+len, nbp->rptr, n);
len += n;
}
if(bp->flags & S_DELIM)
break;
}
/*
* Pad the packet (zero the pad).
*/
if(len < ETHERMINTU){
memset(ring->pkt+len, 0, ETHERMINTU-len);
len = ETHERMINTU;
}
/*
* Set up the transmit buffer and
* start the transmission.
*/
s = splhi();
ring->len = len;
ring->owner = Interface;
ctlr->th = NEXT(ctlr->th, ctlr->ntb);
(*ctlr->card.transmit)(ctlr);
ctlr->outpackets++;
splx(s);
qunlock(&ctlr->tlock);
freeb(bp);
poperror();
}
/*
* Open an ether line discipline.
*/
static void
etherstopen(Queue *q, Stream *s)
{
Ctlr *ctlr = softctlr;
Type *type;
type = &ctlr->type[s->id];
RD(q)->ptr = WR(q)->ptr = type;
type->type = 0;
type->q = RD(q);
type->inuse = 1;
memmove(type->ea, ctlr->ea, sizeof(type->ea));
type->ctlr = ctlr;
}
/*
* Close ether line discipline.
*
* The locking is to synchronize changing the ethertype with
* sending packets up the stream on interrupts.
*/
static int
isclosed(void *arg)
{
return ((Type*)arg)->q == 0;
}
static void
etherstclose(Queue *q)
{
Type *type = (Type*)(q->ptr);
Ctlr *ctlr = type->ctlr;
if(type->prom){
qlock(ctlr);
ctlr->prom--;
if(ctlr->prom+ctlr->filter == 0)
(*ctlr->card.mode)(ctlr, 0);
qunlock(ctlr);
}
if(type->filter){
qlock(ctlr);
ctlr->filter--;
if(ctlr->filter == 0)
(*ctlr->card.mode)(ctlr, ctlr->prom ? 1 : 0);
qunlock(ctlr);
}
if(type->type == -1){
qlock(ctlr);
ctlr->all--;
qunlock(ctlr);
}
/*
* Mark as closing and wait for kproc
* to close us.
*/
lock(&ctlr->clock);
type->clist = ctlr->clist;
ctlr->clist = type;
unlock(&ctlr->clock);
wakeup(&ctlr->rr);
sleep(&type->cr, isclosed, type);
type->type = 0;
type->prom = 0;
type->filter = 0;
type->inuse = 0;
netdisown(type);
type->ctlr = 0;
}
static Qinfo info = {
nullput,
etheroput,
etherstopen,
etherstclose,
"ether"
};
static int
clonecon(Chan *c)
{
Ctlr *ctlr = softctlr;
Type *type;
USED(c);
for(type = ctlr->type; type < &ctlr->type[NType]; type++){
qlock(type);
if(type->inuse || type->q){
qunlock(type);
continue;
}
type->inuse = 1;
memmove(type->ea, ctlr->ea, sizeof(type->ea));
netown(type, u->p->user, 0);
qunlock(type);
return type - ctlr->type;
}
exhausted("ether channels");
return 0;
}
static void
statsfill(Chan *c, char *p, int n)
{
Ctlr *ctlr = softctlr;
char buf[256];
USED(c);
sprint(buf, "in: %d\nout: %d\ncrc errs %d\noverflows: %d\nframe errs %d\nbuff errs: %d\noerrs %d\naddr: %.02x:%.02x:%.02x:%.02x:%.02x:%.02x\n",
ctlr->inpackets, ctlr->outpackets, ctlr->crcs,
ctlr->overflows, ctlr->frames, ctlr->buffs, ctlr->oerrs,
ctlr->ea[0], ctlr->ea[1], ctlr->ea[2],
ctlr->ea[3], ctlr->ea[4], ctlr->ea[5]);
strncpy(p, buf, n);
}
static void
typefill(Chan *c, char *p, int n)
{
char buf[16];
Type *type;
type = &softctlr->type[STREAMID(c->qid.path)];
sprint(buf, "%d", type->type);
strncpy(p, buf, n);
}
int
eaddrmatch(Ctlr *ctlr, uchar *ea)
{
Type *type;
for(type = &ctlr->type[0]; type < &ctlr->type[NType]; type++){
if(type->q == 0 || ea[0] != type->ea[0])
continue;
if(memcmp(ea, type->ea, sizeof(type->ea)) == 0)
return 1;
}
return 0;
}
static void
etherup(Ctlr *ctlr, Etherpkt *pkt, int len)
{
int t;
Type *type;
Block *bp;
t = (pkt->type[0]<<8)|pkt->type[1];
for(type = &ctlr->type[0]; type < &ctlr->type[NType]; type++){
/*
* Check for open, the right type, and flow control.
*/
if(type->q == 0)
continue;
if(t != type->type && type->type >= 0)
continue;
if(type->q->next->len > Streamhi)
continue;
/*
* Only a trace channel gets packets destined for other machines.
*/
if(type->type != -1 && pkt->d[0] != 0xFF
&& (*pkt->d != *type->ea || memcmp(pkt->d, type->ea, sizeof(pkt->d))))
continue;
if(waserror() == 0){
bp = allocb(len);
memmove(bp->rptr, pkt, len);
bp->wptr += len;
bp->flags |= S_DELIM;
PUTNEXT(type->q, bp);
poperror();
}
}
}
static int
isinput(void *arg)
{
Ctlr *ctlr = arg;
return ctlr->lbq.first || ctlr->rb[ctlr->rh].owner == Host || ctlr->clist;
}
static void
etherkproc(void *arg)
{
Ctlr *ctlr = arg;
RingBuf *ring;
Block *bp;
Type *type;
if(waserror()){
print("%s noted\n", ctlr->name);
/* fix
if(ctlr->card.reset)
(*ctlr->card.reset)(ctlr);
*/
ctlr->kproc = 0;
nexterror();
}
for(;;){
tsleep(&ctlr->rr, isinput, ctlr, 500);
if(ctlr->card.watch)
(*ctlr->card.watch)(ctlr);
/*
* Process any internal loopback packets.
*/
while(bp = getq(&ctlr->lbq)){
ctlr->inpackets++;
etherup(ctlr, (Etherpkt*)bp->rptr, BLEN(bp));
freeb(bp);
}
/*
* Process any received packets.
*/
while(ctlr->rb[ctlr->rh].owner == Host){
ctlr->inpackets++;
ring = &ctlr->rb[ctlr->rh];
etherup(ctlr, (Etherpkt*)ring->pkt, ring->len);
ring->owner = Interface;
ctlr->rh = NEXT(ctlr->rh, ctlr->nrb);
}
/*
* Close Types requesting it.
*/
if(ctlr->clist){
lock(&ctlr->clock);
for(type = ctlr->clist; type; type = type->clist){
type->q = 0;
wakeup(&type->cr);
}
ctlr->clist = 0;
unlock(&ctlr->clock);
}
}
}
static void
etherintr(Ureg *ur, void *a)
{
Ctlr *ctlr = softctlr;
USED(ur, a);
(*ctlr->card.intr)(ctlr);
}
static void
reset(Ctlr *ctlr)
{
int i;
if(ctlr->nrb == 0)
ctlr->nrb = Nrb;
ctlr->rb = xalloc(sizeof(RingBuf)*ctlr->nrb);
if(ctlr->ntb == 0)
ctlr->ntb = Ntb;
ctlr->tb = xalloc(sizeof(RingBuf)*ctlr->ntb);
memset(ctlr->ba, 0xFF, sizeof(ctlr->ba));
ctlr->net.name = "ether";
ctlr->net.nconv = NType;
ctlr->net.devp = &info;
ctlr->net.protop = 0;
ctlr->net.listen = 0;
ctlr->net.clone = clonecon;
ctlr->net.ninfo = 2;
ctlr->net.info[0].name = "stats";
ctlr->net.info[0].fill = statsfill;
ctlr->net.info[1].name = "type";
ctlr->net.info[1].fill = typefill;
for(i = 0; i < NType; i++)
netadd(&ctlr->net, &ctlr->type[i], i);
}
extern int wd8003reset(Ctlr*);
extern int ne2000reset(Ctlr*);
extern int ccc509reset(Ctlr*);
extern int nsciareset(Ctlr*);
extern int ne2000PCMreset(Ctlr*);
#define NCARD 32
struct {
char *type;
int (*reset)(Ctlr*);
} cards[NCARD+1];
void
addethercard(char *t, int (*r)(Ctlr*))
{
static int ncard;
if(ncard == NCARD)
panic("too many ether cards");
cards[ncard].type = t;
cards[ncard].reset = r;
ncard++;
}
void
etherreset(void)
{
Ctlr *ctlr;
int i, n, ctlrno;
if(softctlr == 0)
softctlr = xalloc(sizeof(Ctlr));
else
memset(softctlr, 0, sizeof(Ctlr));
ctlr = softctlr;
for(ctlrno = i = 0; isaconfig("ether", i, &ctlr->card); i++){
for(n = 0; cards[n].type; n++){
if(strcmp(cards[n].type, ctlr->card.type))
continue;
ctlr->ctlrno = ctlrno;
memmove(ctlr->ea, ctlr->card.ea, sizeof(ctlr->ea));
if((*cards[n].reset)(ctlr))
break;
/*ctlrno++;*/
ctlr->present = 1;
/*
* 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(ctlr->card.irq == 2)
ctlr->card.irq = 9;
setvec(Int0vec + ctlr->card.irq, etherintr, 0);
print("ether%d:%s: port %lux irq %d addr %lux size %d width %d:",
ctlr->ctlrno, ctlr->card.type, ctlr->card.port, ctlr->card.irq,
ctlr->card.mem, ctlr->card.size, ctlr->card.bit16 ? 16: 8);
for(i = 0; i < sizeof(ctlr->ea); i++)
print("%2.2ux", ctlr->ea[i]);
print("\n");
reset(ctlr);
return;
}
memset(softctlr, 0, sizeof(Ctlr));
}
}
void
etherinit(void)
{
Ctlr *ctlr = softctlr;
int i;
if(ctlr->present == 0)
return;
ctlr->rh = 0;
ctlr->ri = 0;
for(i = 0; i < ctlr->nrb; i++)
ctlr->rb[i].owner = Interface;
ctlr->th = 0;
ctlr->ti = 0;
for(i = 0; i < ctlr->ntb; i++)
ctlr->tb[i].owner = Host;
}
Chan*
etherattach(char *spec)
{
Ctlr *ctlr = softctlr;
if(ctlr->present == 0)
error(Enodev);
/*
* Enable the interface
* and start the kproc.
*/
(*ctlr->card.attach)(ctlr);
if(ctlr->kproc == 0){
sprint(ctlr->name, "ether%dkproc", 0);
ctlr->kproc = 1;
kproc(ctlr->name, etherkproc, ctlr);
}
return devattach('l', spec);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.