blob: b4830116066c6504f3b5f01b5d1202e24439e002 [file] [log] [blame]
#include <vfs.h>
#include <kfs.h>
#include <slab.h>
#include <kmalloc.h>
#include <kref.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <error.h>
#include <cpio.h>
#include <pmap.h>
#include <smp.h>
#include <etherif.h>
static struct ether *etherxx[Maxether];
struct chan *etherattach(char *spec)
{
ERRSTACK(2);
uint32_t ctlrno;
char *p;
struct chan *chan;
ctlrno = 0;
/* 'spec' is the special, i.e. l0, l1, etc. */
if (spec && *spec) {
ctlrno = strtoul(spec, &p, 0);
if ((ctlrno == 0 && p == spec) || *p || (ctlrno >= Maxether))
error(Ebadarg);
}
if (etherxx[ctlrno] == 0)
error(Enodev);
chan = devattach('l', spec);
if (waserror()) {
chanfree(chan);
nexterror();
}
chan->devno = ctlrno;
if (etherxx[ctlrno]->attach)
etherxx[ctlrno]->attach(etherxx[ctlrno]);
return chan;
}
static struct walkqid *etherwalk(struct chan *chan, struct chan *nchan,
char **name, int nname)
{
return netifwalk(&etherxx[chan->devno]->netif, chan, nchan, name, nname);
}
static long
etherstat(struct chan *chan, uint8_t * dp, long n)
{
return netifstat(&etherxx[chan->devno]->netif, chan, dp, n);
}
static struct chan *etheropen(struct chan *chan, int omode)
{
return netifopen(&etherxx[chan->devno]->netif, chan, omode);
}
static void
ethercreate(struct chan *u1, char *u2, int u3, int u4)
{
}
static void etherclose(struct chan *chan)
{
netifclose(&etherxx[chan->devno]->netif, chan);
}
static long
etherread(struct chan *chan, void *buf, long n, int64_t off)
{
struct ether *ether;
uint32_t offset = off;
ether = etherxx[chan->devno];
if ((chan->qid.type & QTDIR) == 0 && ether->ifstat) {
/*
* With some controllers it is necessary to reach
* into the chip to extract statistics.
*/
if (NETTYPE(chan->qid.path) == Nifstatqid)
return ether->ifstat(ether, buf, n, offset);
else if (NETTYPE(chan->qid.path) == Nstatqid)
ether->ifstat(ether, buf, 0, offset);
}
return netifread(&ether->netif, chan, buf, n, offset);
}
static struct block *etherbread(struct chan *chan, long n, int64_t offset)
{
return netifbread(&etherxx[chan->devno]->netif, chan, n, offset);
}
static long
etherwstat(struct chan *chan, uint8_t * dp, long n)
{
return netifwstat(&etherxx[chan->devno]->netif, chan, dp, n);
}
#if 0
part of plan 9 packet dump infrastructure.wrap output packets back to input
static void etherrtrace(Netfile * f, struct etherpkt *pkt, int len)
{
int i, n;
struct block *bp;
if (qwindow(f->iq) <= 0)
return;
if (len > 58)
n = 58;
else
n = len;
bp = iallocb(64);
if (bp == NULL)
return;
memmove(bp->wp, pkt->d, n);
i = TK2MS(sys->ticks);
bp->wp[58] = len >> 8;
bp->wp[59] = len;
bp->wp[60] = i >> 24;
bp->wp[61] = i >> 16;
bp->wp[62] = i >> 8;
bp->wp[63] = i;
bp->wp += 64;
qpass(f->iq, bp);
}
#endif
struct block *etheriq(struct ether *ether, struct block *bp, int fromwire)
{
struct etherpkt *pkt;
uint16_t type;
int len, multi, tome, fromme;
struct netfile **ep, *f, **fp, *fx;
struct block *xbp;
ether->netif.inpackets++;
pkt = (struct etherpkt *)bp->rp;
len = BLEN(bp);
type = (pkt->type[0] << 8) | pkt->type[1];
fx = 0;
ep = &ether->netif.f[Ntypes];
multi = pkt->d[0] & 1;
/* check for valid multicast addresses */
if (multi && memcmp(pkt->d, ether->netif.bcast, sizeof(pkt->d)) != 0
&& ether->netif.prom == 0) {
if (!activemulti(&ether->netif, pkt->d, sizeof(pkt->d))) {
if (fromwire) {
freeb(bp);
bp = 0;
}
return bp;
}
}
/* is it for me? */
tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
/*
* Multiplex the packet to all the connections which want it.
* If the packet is not to be used subsequently (fromwire != 0),
* attempt to simply pass it into one of the connections, thereby
* saving a copy of the data (usual case hopefully).
*/
for (fp = ether->netif.f; fp < ep; fp++) {
if ((f = *fp))
if (f->type == type || f->type < 0)
if (tome || multi || f->prom || f->bridge & 2) {
/* Don't want to hear bridged packets */
if (f->bridge && !fromwire && !fromme)
continue;
if (!f->headersonly) {
if (fromwire && fx == 0)
fx = f;
else if ((xbp = iallocb(len))) {
memmove(xbp->wp, pkt, len);
xbp->wp += len;
if (qpass(f->iq, xbp) < 0)
ether->netif.soverflows++;
} else
ether->netif.soverflows++;
} else; //etherrtrace(f, pkt, len);
}
}
if (fx) {
if (qpass(fx->iq, bp) < 0)
ether->netif.soverflows++;
return 0;
}
if (fromwire) {
freeb(bp);
return 0;
}
return bp;
}
static int
etheroq(struct ether *ether, struct block *bp)
{
int len, loopback;
struct etherpkt *pkt;
int8_t irq_state = 0;
ether->netif.outpackets++;
/*
* Check if the packet has to be placed back onto the input queue,
* i.e. if it's a loopback or broadcast packet or the interface is
* in promiscuous mode.
* If it's a loopback packet indicate to etheriq that the data isn't
* needed and return, etheriq will pass-on or free the block.
* To enable bridging to work, only packets that were originated
* by this interface are fed back.
*/
pkt = (struct etherpkt *)bp->rp;
len = BLEN(bp);
loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
if (loopback || memcmp(pkt->d, ether->netif.bcast, sizeof(pkt->d)) == 0
|| ether->netif.prom) {
disable_irqsave(&irq_state);
etheriq(ether, bp, 0);
enable_irqsave(&irq_state);
}
if (!loopback) {
qbwrite(ether->netif.oq, bp);
if (ether->transmit != NULL)
ether->transmit(ether);
} else
freeb(bp);
return len;
}
static long
etherwrite(struct chan *chan, void *buf, long n, int64_t unused)
{
ERRSTACK(2);
struct ether *ether;
struct block *bp;
int nn, onoff;
struct cmdbuf *cb;
ether = etherxx[chan->devno];
if (NETTYPE(chan->qid.path) != Ndataqid) {
nn = netifwrite(&ether->netif, chan, buf, n);
if (nn >= 0)
return nn;
cb = parsecmd(buf, n);
if (cb->f[0] && strcmp(cb->f[0], "nonblocking") == 0) {
if (cb->nf <= 1)
onoff = 1;
else
onoff = atoi(cb->f[1]);
qnoblock(ether->netif.oq, onoff);
kfree(cb);
return n;
}
kfree(cb);
if (ether->ctl != NULL)
return ether->ctl(ether, buf, n);
error(Ebadctl);
}
if (n > ether->netif.mtu)
error(Etoobig);
if (n < ether->netif.minmtu)
error(Etoosmall);
bp = allocb(n);
if (waserror()) {
freeb(bp);
nexterror();
}
memmove(bp->rp, buf, n);
if ((ether->netif.f[NETID(chan->qid.path)]->bridge & 2) == 0)
memmove(bp->rp + Eaddrlen, ether->ea, Eaddrlen);
bp->wp += n;
return etheroq(ether, bp);
}
static long
etherbwrite(struct chan *chan, struct block *bp, int64_t unused)
{
ERRSTACK(1);
struct ether *ether;
long n;
n = BLEN(bp);
if (NETTYPE(chan->qid.path) != Ndataqid) {
if (waserror()) {
freeb(bp);
nexterror();
}
n = etherwrite(chan, bp->rp, n, 0);
freeb(bp);
return n;
}
ether = etherxx[chan->devno];
if (n > ether->netif.mtu) {
freeb(bp);
error(Etoobig);
}
if (n < ether->netif.minmtu) {
freeb(bp);
error(Etoosmall);
}
return etheroq(ether, bp);
}
static struct {
char *type;
int (*reset) (struct ether *);
} cards[Maxether + 1];
void addethercard(char *t, int (*r) (struct ether *))
{
static int ncard;
if (ncard == Maxether)
panic("too many ether cards");
cards[ncard].type = t;
cards[ncard].reset = r;
ncard++;
}
static struct ether *etherprobe(int cardno, int ctlrno)
{
int i, j;
struct ether *ether;
char buf[128], name[32];
ether = kzmalloc(sizeof(struct ether), 0);
memset(ether, 0, sizeof(struct ether));
ether->ctlrno = ctlrno;
ether->netif.mbps = 10;
ether->netif.minmtu = ETHERMINTU;
ether->netif.mtu = ETHERMAXTU;
ether->netif.maxmtu = ETHERMAXTU;
qlock_init(&ether->netif.qlock);
if (cardno >= Maxether || cards[cardno].type == NULL) {
kfree(ether);
return NULL;
}
if (cards[cardno].reset(ether) < 0) {
kfree(ether);
return NULL;
}
/*
* 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;
snprintf(name, sizeof(name), "ether%d", ctlrno);
/*
* If ether->irq is <0, it is a hack to indicate no interrupt
* used by ethersink.
*/
if (ether->irq >= 0)
register_dev_irq(ether->irq, ether->interrupt, ether);
i = snprintf(buf, sizeof(buf), "#l%d: %s: %dMbps port %#p irq %d tu %d",
ctlrno, cards[cardno].type, ether->netif.mbps, ether->port,
ether->irq, ether->netif.mtu);
#if 0
if (ether->mem)
i += sprint(buf + i, " addr %#p", ether->mem);
if (ether->size)
i += sprint(buf + i, " size 0x%luX", ether->size);
#endif
i += snprintf(buf + i, sizeof(buf) - i,
": %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux", ether->ea[0],
ether->ea[1], ether->ea[2], ether->ea[3], ether->ea[4],
ether->ea[5]);
snprintf(buf + i, sizeof(buf) - i, "\n");
printd(buf);
j = ether->netif.mbps;
if (j > 1000)
j *= 10;
for (i = 0; j >= 100; i++)
j /= 10;
i = (128 << i) * 1024;
netifinit(&ether->netif, name, Ntypes, i);
if (ether->netif.oq == 0)
ether->netif.oq = qopen(i, Qmsg, 0, 0);
if (ether->netif.oq == 0)
panic("etherreset %s", name);
ether->netif.alen = Eaddrlen;
memmove(ether->netif.addr, ether->ea, Eaddrlen);
memset(ether->netif.bcast, 0xFF, Eaddrlen);
return ether;
}
static
void etherreset(void)
{
struct ether *ether;
int cardno, ctlrno;
cardno = ctlrno = 0;
while (cards[cardno].type != NULL && ctlrno < Maxether) {
if (etherxx[ctlrno] != NULL) {
ctlrno++;
continue;
}
if ((ether = etherprobe(cardno, ctlrno)) == NULL) {
cardno++;
continue;
}
etherxx[ctlrno] = ether;
ctlrno++;
}
}
static void ethershutdown(void)
{
char name[32];
int i;
struct ether *ether;
for (i = 0; i < Maxether; i++) {
ether = etherxx[i];
if (ether == NULL)
continue;
if (ether->shutdown == NULL) {
printd("#l%d: no shutdown function\n", i);
continue;
}
snprintf(name, sizeof(name), "ether%d", i);
if (ether->irq >= 0) {
// intrdisable(ether->irq, ether->interrupt, ether, ether->tbdf, name);
}
(*ether->shutdown) (ether);
}
}
#define ROLYPOLY 0xedb88320
/* really slow 32 bit crc for ethers */
uint32_t ethercrc(uint8_t * p, int len)
{
int i, j;
uint32_t crc, b;
crc = 0xffffffff;
for (i = 0; i < len; i++) {
b = *p++;
for (j = 0; j < 8; j++) {
crc = (crc >> 1) ^ (((crc ^ b) & 1) ? ROLYPOLY : 0);
b >>= 1;
}
}
return crc;
}
struct dev etherdevtab = {
'l',
"ether",
etherreset,
devinit,
ethershutdown,
etherattach,
etherwalk,
etherstat,
etheropen,
ethercreate,
etherclose,
etherread,
etherbread,
etherwrite,
etherbwrite,
devremove,
etherwstat,
devpower,
devconfig,
devchaninfo,
};